diff -Nru qemu-kvm-0.12.5+noroms/alpha-dis.c qemu-kvm-0.14.1/alpha-dis.c --- qemu-kvm-0.12.5+noroms/alpha-dis.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/alpha-dis.c 2011-05-11 13:29:46.000000000 +0000 @@ -22,6 +22,9 @@ #include #include "dis-asm.h" +/* MAX is redefined below, so remove any previous definition. */ +#undef MAX + /* The opcode table is an array of struct alpha_opcode. */ struct alpha_opcode diff -Nru qemu-kvm-0.12.5+noroms/arch_init.c qemu-kvm-0.14.1/arch_init.c --- qemu-kvm-0.12.5+noroms/arch_init.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/arch_init.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,726 @@ +/* + * 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 +#include +#include +#ifndef _WIN32 +#include +#include +#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 +#define RAM_SAVE_FLAG_CONTINUE 0x20 + +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 RAMBlock *last_block; +static ram_addr_t last_offset; + +static int ram_save_block(QEMUFile *f) +{ + RAMBlock *block = last_block; + ram_addr_t offset = last_offset; + ram_addr_t current_addr; + int bytes_sent = 0; + + if (!block) + block = QLIST_FIRST(&ram_list.blocks); + + current_addr = block->offset + offset; + + do { + if (cpu_physical_memory_get_dirty(current_addr, MIGRATION_DIRTY_FLAG)) { + uint8_t *p; + int cont = (block == last_block) ? RAM_SAVE_FLAG_CONTINUE : 0; + + cpu_physical_memory_reset_dirty(current_addr, + current_addr + TARGET_PAGE_SIZE, + MIGRATION_DIRTY_FLAG); + + p = block->host + offset; + + if (is_dup_page(p, *p)) { + qemu_put_be64(f, offset | cont | RAM_SAVE_FLAG_COMPRESS); + if (!cont) { + qemu_put_byte(f, strlen(block->idstr)); + qemu_put_buffer(f, (uint8_t *)block->idstr, + strlen(block->idstr)); + } + qemu_put_byte(f, *p); + bytes_sent = 1; + } else { + qemu_put_be64(f, offset | cont | RAM_SAVE_FLAG_PAGE); + if (!cont) { + qemu_put_byte(f, strlen(block->idstr)); + qemu_put_buffer(f, (uint8_t *)block->idstr, + strlen(block->idstr)); + } + qemu_put_buffer(f, p, TARGET_PAGE_SIZE); + bytes_sent = TARGET_PAGE_SIZE; + } + + break; + } + + offset += TARGET_PAGE_SIZE; + if (offset >= block->length) { + offset = 0; + block = QLIST_NEXT(block, next); + if (!block) + block = QLIST_FIRST(&ram_list.blocks); + } + + current_addr = block->offset + offset; + + } while (current_addr != last_block->offset + last_offset); + + last_block = block; + last_offset = offset; + + return bytes_sent; +} + +static uint64_t bytes_transferred; + +static ram_addr_t ram_save_remaining(void) +{ + RAMBlock *block; + ram_addr_t count = 0; + + QLIST_FOREACH(block, &ram_list.blocks, next) { + ram_addr_t addr; + for (addr = block->offset; addr < block->offset + block->length; + 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) +{ + RAMBlock *block; + uint64_t total = 0; + + QLIST_FOREACH(block, &ram_list.blocks, next) + total += block->length; + + return total; +} + +static int block_compar(const void *a, const void *b) +{ + RAMBlock * const *ablock = a; + RAMBlock * const *bblock = b; + if ((*ablock)->offset < (*bblock)->offset) { + return -1; + } else if ((*ablock)->offset > (*bblock)->offset) { + return 1; + } + return 0; +} + +static void sort_ram_list(void) +{ + RAMBlock *block, *nblock, **blocks; + int n; + n = 0; + QLIST_FOREACH(block, &ram_list.blocks, next) { + ++n; + } + blocks = qemu_malloc(n * sizeof *blocks); + n = 0; + QLIST_FOREACH_SAFE(block, &ram_list.blocks, next, nblock) { + blocks[n++] = block; + QLIST_REMOVE(block, next); + } + qsort(blocks, n, sizeof *blocks, block_compar); + while (--n >= 0) { + QLIST_INSERT_HEAD(&ram_list.blocks, blocks[n], next); + } + qemu_free(blocks); +} + +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) { + RAMBlock *block; + bytes_transferred = 0; + last_block = NULL; + last_offset = 0; + sort_ram_list(); + + /* Make sure all dirty bits are set */ + QLIST_FOREACH(block, &ram_list.blocks, next) { + for (addr = block->offset; addr < block->offset + block->length; + 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, ram_bytes_total() | RAM_SAVE_FLAG_MEM_SIZE); + + QLIST_FOREACH(block, &ram_list.blocks, next) { + qemu_put_byte(f, strlen(block->idstr)); + qemu_put_buffer(f, (uint8_t *)block->idstr, strlen(block->idstr)); + qemu_put_be64(f, block->length); + } + } + + bytes_transferred_last = bytes_transferred; + bwidth = qemu_get_clock_ns(rt_clock); + + while (!qemu_file_rate_limit(f)) { + int bytes_sent; + + bytes_sent = ram_save_block(f); + bytes_transferred += bytes_sent; + if (bytes_sent == 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) { + int bytes_sent; + + /* flush all remaining blocks regardless of rate limiting */ + while ((bytes_sent = ram_save_block(f)) != 0) { + bytes_transferred += bytes_sent; + } + 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()); +} + +static inline void *host_from_stream_offset(QEMUFile *f, + ram_addr_t offset, + int flags) +{ + static RAMBlock *block = NULL; + char id[256]; + uint8_t len; + + if (flags & RAM_SAVE_FLAG_CONTINUE) { + if (!block) { + fprintf(stderr, "Ack, bad migration stream!\n"); + return NULL; + } + + return block->host + offset; + } + + len = qemu_get_byte(f); + qemu_get_buffer(f, (uint8_t *)id, len); + id[len] = 0; + + QLIST_FOREACH(block, &ram_list.blocks, next) { + if (!strncmp(id, block->idstr, sizeof(id))) + return block->host + offset; + } + + fprintf(stderr, "Can't find block %s!\n", id); + return NULL; +} + +int ram_load(QEMUFile *f, void *opaque, int version_id) +{ + ram_addr_t addr; + int flags; + + if (version_id < 3 || version_id > 4) { + 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 (version_id == 3) { + if (addr != ram_bytes_total()) { + return -EINVAL; + } + } else { + /* Synchronize RAM block list */ + char id[256]; + ram_addr_t length; + ram_addr_t total_ram_bytes = addr; + + while (total_ram_bytes) { + RAMBlock *block; + uint8_t len; + + len = qemu_get_byte(f); + qemu_get_buffer(f, (uint8_t *)id, len); + id[len] = 0; + length = qemu_get_be64(f); + + QLIST_FOREACH(block, &ram_list.blocks, next) { + if (!strncmp(id, block->idstr, sizeof(id))) { + if (block->length != length) + return -EINVAL; + break; + } + } + + if (!block) { + fprintf(stderr, "Unknown ramblock \"%s\", cannot " + "accept migration\n", id); + return -EINVAL; + } + + total_ram_bytes -= length; + } + } + } + + if (flags & RAM_SAVE_FLAG_COMPRESS) { + void *host; + uint8_t ch; + + if (version_id == 3) + host = qemu_get_ram_ptr(addr); + else + host = host_from_stream_offset(f, addr, flags); + if (!host) { + return -EINVAL; + } + + ch = qemu_get_byte(f); + memset(host, ch, TARGET_PAGE_SIZE); +#ifndef _WIN32 + if (ch == 0 && + (!kvm_enabled() || kvm_has_sync_mmu())) { + qemu_madvise(host, TARGET_PAGE_SIZE, QEMU_MADV_DONTNEED); + } +#endif + } else if (flags & RAM_SAVE_FLAG_PAGE) { + void *host; + + if (version_id == 3) + host = qemu_get_ram_ptr(addr); + else + host = host_from_stream_offset(f, addr, flags); + + qemu_get_buffer(f, host, 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 { + const char *name; + const char *descr; + int enabled; + int isa; + union { + int (*init_isa) (qemu_irq *pic); + int (*init_pci) (PCIBus *bus); + } init; +}; + +static 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 + +#ifdef CONFIG_HDA + { + "hda", + "Intel HD Audio", + 0, + 0, + { .init_pci = intel_hda_and_codec_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; + } + } +} + +void audio_init(qemu_irq *isa_pic, PCIBus *pci_bus) +{ + struct soundhw *c; + + for (c = soundhw; c->name; ++c) { + if (c->enabled) { + if (c->isa) { + if (isa_pic) { + c->init.init_isa(isa_pic); + } + } else { + if (pci_bus) { + c->init.init_pci(pci_bus); + } + } + } + } +} +#else +void select_soundhw(const char *optarg) +{ +} +void audio_init(qemu_irq *isa_pic, PCIBus *pci_bus) +{ +} +#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 +} diff -Nru qemu-kvm-0.12.5+noroms/arch_init.h qemu-kvm-0.14.1/arch_init.h --- qemu-kvm-0.12.5+noroms/arch_init.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/arch_init.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,34 @@ +#ifndef QEMU_ARCH_INIT_H +#define QEMU_ARCH_INIT_H + +extern const char arch_config_name[]; + +enum { + QEMU_ARCH_ALL = -1, + QEMU_ARCH_ALPHA = 1, + QEMU_ARCH_ARM = 2, + QEMU_ARCH_CRIS = 4, + QEMU_ARCH_I386 = 8, + QEMU_ARCH_M68K = 16, + QEMU_ARCH_MICROBLAZE = 32, + QEMU_ARCH_MIPS = 64, + QEMU_ARCH_PPC = 128, + QEMU_ARCH_S390X = 256, + QEMU_ARCH_SH4 = 512, + QEMU_ARCH_SPARC = 1024, +}; + +extern const uint32_t arch_type; + +void select_soundhw(const char *optarg); +int ram_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque); +int ram_load(QEMUFile *f, void *opaque, int version_id); +void do_acpitable_option(const char *optarg); +void do_smbios_option(const char *optarg); +void cpudef_init(void); +int audio_available(void); +void audio_init(qemu_irq *isa_pic, PCIBus *pci_bus); +int kvm_available(void); +int xen_available(void); + +#endif diff -Nru qemu-kvm-0.12.5+noroms/arm-dis.c qemu-kvm-0.14.1/arm-dis.c --- qemu-kvm-0.12.5+noroms/arm-dis.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/arm-dis.c 2011-05-11 13:29:46.000000000 +0000 @@ -60,10 +60,8 @@ #define FPU_VFP_EXT_V3 0 #define FPU_NEON_EXT_V1 0 -int floatformat_ieee_single_little; /* Assume host uses ieee float. */ -static void floatformat_to_double (int *ignored, unsigned char *data, - double *dest) +static void floatformat_to_double (unsigned char *data, double *dest) { union { uint32_t i; @@ -1589,7 +1587,7 @@ } static void -arm_decode_shift (long given, fprintf_ftype func, void *stream, +arm_decode_shift (long given, fprintf_function func, void *stream, int print_shift) { func (stream, "%s", arm_regnames[given & 0xf]); @@ -1635,7 +1633,7 @@ { const struct opcode32 *insn; void *stream = info->stream; - fprintf_ftype func = info->fprintf_func; + fprintf_function func = info->fprintf_func; unsigned long mask; unsigned long value; int cond; @@ -2129,7 +2127,7 @@ print_arm_address (bfd_vma pc, struct disassemble_info *info, long given) { void *stream = info->stream; - fprintf_ftype func = info->fprintf_func; + fprintf_function func = info->fprintf_func; if (((given & 0x000f0000) == 0x000f0000) && ((given & 0x02000000) == 0)) @@ -2224,7 +2222,7 @@ { const struct opcode32 *insn; void *stream = info->stream; - fprintf_ftype func = info->fprintf_func; + fprintf_function func = info->fprintf_func; if (thumb) { @@ -2517,7 +2515,6 @@ { func (stream, "", bits, cmode, op); - size = 32; break; } switch (size) @@ -2543,9 +2540,7 @@ valbytes[2] = (value >> 16) & 0xff; valbytes[3] = (value >> 24) & 0xff; - floatformat_to_double - (&floatformat_ieee_single_little, valbytes, - &fvalue); + floatformat_to_double (valbytes, &fvalue); func (stream, "#%.7g\t; 0x%.8lx", fvalue, value); @@ -2681,7 +2676,7 @@ { const struct opcode32 *insn; void *stream = info->stream; - fprintf_ftype func = info->fprintf_func; + fprintf_function func = info->fprintf_func; if (print_insn_coprocessor (pc, info, given, false)) return; @@ -3041,7 +3036,7 @@ { const struct opcode16 *insn; void *stream = info->stream; - fprintf_ftype func = info->fprintf_func; + fprintf_function func = info->fprintf_func; for (insn = thumb_opcodes; insn->assembler; insn++) if ((given & insn->mask) == insn->value) @@ -3153,14 +3148,14 @@ if (started) func (stream, ", "); started = 1; - func (stream, arm_regnames[14] /* "lr" */); + func (stream, "%s", arm_regnames[14] /* "lr" */); } if (domaskpc) { if (started) func (stream, ", "); - func (stream, arm_regnames[15] /* "pc" */); + func (stream, "%s", arm_regnames[15] /* "pc" */); } func (stream, "}"); @@ -3317,7 +3312,7 @@ { const struct opcode32 *insn; void *stream = info->stream; - fprintf_ftype func = info->fprintf_func; + fprintf_function func = info->fprintf_func; if (print_insn_coprocessor (pc, info, given, true)) return; @@ -3703,7 +3698,7 @@ } else { - func (stream, psr_name (given & 0xff)); + func (stream, "%s", psr_name (given & 0xff)); } break; @@ -3711,7 +3706,7 @@ if ((given & 0xff) == 0) func (stream, "%cPSR", (given & 0x100000) ? 'S' : 'C'); else - func (stream, psr_name (given & 0xff)); + func (stream, "%s", psr_name (given & 0xff)); break; case '0': case '1': case '2': case '3': case '4': @@ -4106,6 +4101,30 @@ addresses, since the addend is not currently pc-relative. */ pc = 0; + /* We include the hexdump of the instruction. The format here + matches that used by objdump and the ARM ARM (in particular, + 32 bit Thumb instructions are displayed as pairs of halfwords, + not as a single word.) */ + if (is_thumb) + { + if (size == 2) + { + info->fprintf_func(info->stream, "%04lx ", + ((unsigned long)given) & 0xffff); + } + else + { + info->fprintf_func(info->stream, "%04lx %04lx ", + (((unsigned long)given) >> 16) & 0xffff, + ((unsigned long)given) & 0xffff); + } + } + else + { + info->fprintf_func(info->stream, "%08lx ", + ((unsigned long)given) & 0xffffffff); + } + printer (pc, info, given); if (is_thumb) diff -Nru qemu-kvm-0.12.5+noroms/arm-semi.c qemu-kvm-0.14.1/arm-semi.c --- qemu-kvm-0.12.5+noroms/arm-semi.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/arm-semi.c 2011-05-11 13:29:46.000000000 +0000 @@ -373,45 +373,64 @@ #ifdef CONFIG_USER_ONLY /* Build a commandline from the original argv. */ { - char **arg = ts->info->host_argv; - int len = ARG(1); - /* lock the buffer on the ARM side */ - char *cmdline_buffer = (char*)lock_user(VERIFY_WRITE, ARG(0), len, 0); + char *arm_cmdline_buffer; + const char *host_cmdline_buffer; - if (!cmdline_buffer) - /* FIXME - should this error code be -TARGET_EFAULT ? */ - return (uint32_t)-1; + unsigned int i; + unsigned int arm_cmdline_len = ARG(1); + unsigned int host_cmdline_len = + ts->info->arg_end-ts->info->arg_start; - s = cmdline_buffer; - while (*arg && len > 2) { - int n = strlen(*arg); - - if (s != cmdline_buffer) { - *(s++) = ' '; - len--; - } - if (n >= len) - n = len - 1; - memcpy(s, *arg, n); - s += n; - len -= n; - arg++; + if (!arm_cmdline_len || host_cmdline_len > arm_cmdline_len) { + return -1; /* not enough space to store command line */ + } + + if (!host_cmdline_len) { + /* We special-case the "empty command line" case (argc==0). + Just provide the terminating 0. */ + arm_cmdline_buffer = lock_user(VERIFY_WRITE, ARG(0), 1, 0); + arm_cmdline_buffer[0] = 0; + unlock_user(arm_cmdline_buffer, ARG(0), 1); + + /* Adjust the commandline length argument. */ + SET_ARG(1, 0); + return 0; } - /* Null terminate the string. */ - *s = 0; - len = s - cmdline_buffer; - /* Unlock the buffer on the ARM side. */ - unlock_user(cmdline_buffer, ARG(0), len); + /* lock the buffers on the ARM side */ + arm_cmdline_buffer = + lock_user(VERIFY_WRITE, ARG(0), host_cmdline_len, 0); + host_cmdline_buffer = + lock_user(VERIFY_READ, ts->info->arg_start, + host_cmdline_len, 1); + + if (arm_cmdline_buffer && host_cmdline_buffer) + { + /* the last argument is zero-terminated; + no need for additional termination */ + memcpy(arm_cmdline_buffer, host_cmdline_buffer, + host_cmdline_len); + + /* separate arguments by white spaces */ + for (i = 0; i < host_cmdline_len-1; i++) { + if (arm_cmdline_buffer[i] == 0) { + arm_cmdline_buffer[i] = ' '; + } + } + + /* Adjust the commandline length argument. */ + SET_ARG(1, host_cmdline_len-1); + } - /* Adjust the commandline length argument. */ - SET_ARG(1, len); + /* Unlock the buffers on the ARM side. */ + unlock_user(arm_cmdline_buffer, ARG(0), host_cmdline_len); + unlock_user((void*)host_cmdline_buffer, ts->info->arg_start, 0); - /* Return success if commandline fit into buffer. */ - return *arg ? -1 : 0; + /* Return success if we could return a commandline. */ + return (arm_cmdline_buffer && host_cmdline_buffer) ? 0 : -1; } #else - return -1; + return -1; #endif case SYS_HEAPINFO: { @@ -459,6 +478,7 @@ return 0; } case SYS_EXIT: + gdb_exit(env, 0); exit(0); default: fprintf(stderr, "qemu: Unsupported SemiHosting SWI 0x%02x\n", nr); diff -Nru qemu-kvm-0.12.5+noroms/audio/alsaaudio.c qemu-kvm-0.14.1/audio/alsaaudio.c --- qemu-kvm-0.12.5+noroms/audio/alsaaudio.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/audio/alsaaudio.c 2011-05-11 13:29:46.000000000 +0000 @@ -318,7 +318,7 @@ return audio_pcm_sw_write (sw, buf, len); } -static snd_pcm_format_t aud_to_alsafmt (audfmt_e fmt) +static snd_pcm_format_t aud_to_alsafmt (audfmt_e fmt, int endianness) { switch (fmt) { case AUD_FMT_S8: @@ -328,16 +328,36 @@ return SND_PCM_FORMAT_U8; case AUD_FMT_S16: - return SND_PCM_FORMAT_S16_LE; + if (endianness) { + return SND_PCM_FORMAT_S16_BE; + } + else { + return SND_PCM_FORMAT_S16_LE; + } case AUD_FMT_U16: - return SND_PCM_FORMAT_U16_LE; + if (endianness) { + return SND_PCM_FORMAT_U16_BE; + } + else { + return SND_PCM_FORMAT_U16_LE; + } case AUD_FMT_S32: - return SND_PCM_FORMAT_S32_LE; + if (endianness) { + return SND_PCM_FORMAT_S32_BE; + } + else { + return SND_PCM_FORMAT_S32_LE; + } case AUD_FMT_U32: - return SND_PCM_FORMAT_U32_LE; + if (endianness) { + return SND_PCM_FORMAT_U32_BE; + } + else { + return SND_PCM_FORMAT_U32_LE; + } default: dolog ("Internal logic error: Bad audio format %d\n", fmt); @@ -411,10 +431,11 @@ } static void alsa_dump_info (struct alsa_params_req *req, - struct alsa_params_obt *obt) + struct alsa_params_obt *obt, + snd_pcm_format_t obtfmt) { dolog ("parameter | requested value | obtained value\n"); - dolog ("format | %10d | %10d\n", req->fmt, obt->fmt); + dolog ("format | %10d | %10d\n", req->fmt, obtfmt); dolog ("channels | %10d | %10d\n", req->nchannels, obt->nchannels); dolog ("frequency | %10d | %10d\n", req->freq, obt->freq); @@ -666,15 +687,15 @@ *handlep = handle; if (conf.verbose && - (obt->fmt != req->fmt || + (obtfmt != req->fmt || obt->nchannels != req->nchannels || obt->freq != req->freq)) { dolog ("Audio parameters for %s\n", typ); - alsa_dump_info (req, obt); + alsa_dump_info (req, obt, obtfmt); } #ifdef DEBUG - alsa_dump_info (req, obt); + alsa_dump_info (req, obt, obtfmt); #endif return 0; @@ -808,7 +829,7 @@ snd_pcm_t *handle; struct audsettings obt_as; - req.fmt = aud_to_alsafmt (as->fmt); + req.fmt = aud_to_alsafmt (as->fmt, as->endianness); req.freq = as->freq; req.nchannels = as->nchannels; req.period_size = conf.period_size_out; @@ -842,11 +863,15 @@ return 0; } -static int alsa_voice_ctl (snd_pcm_t *handle, const char *typ, int pause) +#define VOICE_CTL_PAUSE 0 +#define VOICE_CTL_PREPARE 1 +#define VOICE_CTL_START 2 + +static int alsa_voice_ctl (snd_pcm_t *handle, const char *typ, int ctl) { int err; - if (pause) { + if (ctl == VOICE_CTL_PAUSE) { err = snd_pcm_drop (handle); if (err < 0) { alsa_logerr (err, "Could not stop %s\n", typ); @@ -859,6 +884,13 @@ alsa_logerr (err, "Could not prepare handle for %s\n", typ); return -1; } + if (ctl == VOICE_CTL_START) { + err = snd_pcm_start(handle); + if (err < 0) { + alsa_logerr (err, "Could not start handle for %s\n", typ); + return -1; + } + } } return 0; @@ -883,12 +915,16 @@ poll_mode = 0; } hw->poll_mode = poll_mode; - return alsa_voice_ctl (alsa->handle, "playback", 0); + return alsa_voice_ctl (alsa->handle, "playback", VOICE_CTL_PREPARE); } case VOICE_DISABLE: ldebug ("disabling voice\n"); - return alsa_voice_ctl (alsa->handle, "playback", 1); + if (hw->poll_mode) { + hw->poll_mode = 0; + alsa_fini_poll (&alsa->pollhlp); + } + return alsa_voice_ctl (alsa->handle, "playback", VOICE_CTL_PAUSE); } return -1; @@ -902,7 +938,7 @@ snd_pcm_t *handle; struct audsettings obt_as; - req.fmt = aud_to_alsafmt (as->fmt); + req.fmt = aud_to_alsafmt (as->fmt, as->endianness); req.freq = as->freq; req.nchannels = as->nchannels; req.period_size = conf.period_size_in; @@ -1061,7 +1097,7 @@ } } - hw->conv (dst, src, nread, &nominal_volume); + hw->conv (dst, src, nread); src = advance (src, nread << hwshift); dst += nread; @@ -1101,7 +1137,7 @@ } hw->poll_mode = poll_mode; - return alsa_voice_ctl (alsa->handle, "capture", 0); + return alsa_voice_ctl (alsa->handle, "capture", VOICE_CTL_START); } case VOICE_DISABLE: @@ -1110,7 +1146,7 @@ hw->poll_mode = 0; alsa_fini_poll (&alsa->pollhlp); } - return alsa_voice_ctl (alsa->handle, "capture", 1); + return alsa_voice_ctl (alsa->handle, "capture", VOICE_CTL_PAUSE); } return -1; diff -Nru qemu-kvm-0.12.5+noroms/audio/audio.c qemu-kvm-0.14.1/audio/audio.c --- qemu-kvm-0.12.5+noroms/audio/audio.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/audio/audio.c 2011-05-11 13:29:46.000000000 +0000 @@ -44,6 +44,9 @@ that we generate the list. */ static struct audio_driver *drvtab[] = { +#ifdef CONFIG_SPICE + &spice_audio_driver, +#endif CONFIG_AUDIO_DRIVERS &no_audio_driver, &wav_audio_driver @@ -101,7 +104,7 @@ static AudioState glob_audio_state; -struct mixeng_volume nominal_volume = { +const struct mixeng_volume nominal_volume = { .mute = 0, #ifdef FLOAT_MIXENG .r = 1.0, @@ -115,6 +118,9 @@ #ifdef AUDIO_IS_FLAWLESS_AND_NO_CHECKS_ARE_REQURIED #error No its not #else +static void audio_print_options (const char *prefix, + struct audio_option *opt); + int audio_bug (const char *funcname, int cond) { if (cond) { @@ -122,10 +128,16 @@ AUD_log (NULL, "A bug was just triggered in %s\n", funcname); if (!shown) { + struct audio_driver *d; + shown = 1; AUD_log (NULL, "Save all your work and restart without audio\n"); AUD_log (NULL, "Please send bug report to av1474@comtv.ru\n"); AUD_log (NULL, "I am sorry\n"); + d = glob_audio_state.drv; + if (d) { + audio_print_options (d->name, d->options); + } } AUD_log (NULL, "Context:\n"); @@ -321,10 +333,10 @@ { if (conf.log_to_monitor) { if (cap) { - monitor_printf(cur_mon, "%s: ", cap); + monitor_printf(default_mon, "%s: ", cap); } - monitor_vprintf(cur_mon, fmt, ap); + monitor_vprintf(default_mon, fmt, ap); } else { if (cap) { @@ -690,13 +702,11 @@ /* * Capture */ -static void noop_conv (struct st_sample *dst, const void *src, - int samples, struct mixeng_volume *vol) +static void noop_conv (struct st_sample *dst, const void *src, int samples) { (void) src; (void) dst; (void) samples; - (void) vol; } static CaptureVoiceOut *audio_pcm_capture_find_specific ( @@ -944,6 +954,8 @@ total += isamp; } + mixeng_volume (sw->buf, ret, &sw->vol); + sw->clip (buf, sw->buf, ret); sw->total_hw_samples_acquired += total; return ret << sw->info.shift; @@ -1025,7 +1037,8 @@ swlim = ((int64_t) dead << 32) / sw->ratio; swlim = audio_MIN (swlim, samples); if (swlim) { - sw->conv (sw->buf, buf, swlim, &sw->vol); + sw->conv (sw->buf, buf, swlim); + mixeng_volume (sw->buf, swlim, &sw->vol); } while (swlim) { @@ -1084,15 +1097,6 @@ /* * Timer */ -static void audio_timer (void *opaque) -{ - AudioState *s = opaque; - - audio_run ("timer"); - qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + conf.period.ticks); -} - - static int audio_is_timer_needed (void) { HWVoiceIn *hwi = NULL; @@ -1107,10 +1111,8 @@ return 0; } -static void audio_reset_timer (void) +static void audio_reset_timer (AudioState *s) { - AudioState *s = &glob_audio_state; - if (audio_is_timer_needed ()) { qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + 1); } @@ -1119,6 +1121,12 @@ } } +static void audio_timer (void *opaque) +{ + audio_run ("timer"); + audio_reset_timer (opaque); +} + /* * Public API */ @@ -1183,7 +1191,7 @@ hw->enabled = 1; if (s->vm_running) { hw->pcm_ops->ctl_out (hw, VOICE_ENABLE, conf.try_poll_out); - audio_reset_timer (); + audio_reset_timer (s); } } } @@ -1228,6 +1236,7 @@ hw->enabled = 1; if (s->vm_running) { hw->pcm_ops->ctl_in (hw, VOICE_ENABLE, conf.try_poll_in); + audio_reset_timer (s); } } sw->total_hw_samples_acquired = hw->total_samples_captured; @@ -1749,7 +1758,7 @@ while ((hwi = audio_pcm_hw_find_any_enabled_in (hwi))) { hwi->pcm_ops->ctl_in (hwi, op, conf.try_poll_in); } - audio_reset_timer (); + audio_reset_timer (s); } static void audio_atexit (void) @@ -1892,7 +1901,7 @@ } QLIST_INIT (&s->card_head); - vmstate_register (0, &vmstate_audio, s); + vmstate_register (NULL, 0, &vmstate_audio, s); } void AUD_register_card (const char *name, QEMUSoundCard *card) diff -Nru qemu-kvm-0.12.5+noroms/audio/audio.h qemu-kvm-0.14.1/audio/audio.h --- qemu-kvm-0.12.5+noroms/audio/audio.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/audio/audio.h 2011-05-11 13:29:46.000000000 +0000 @@ -86,12 +86,8 @@ uint64_t old_ts; } QEMUAudioTimeStamp; -void AUD_vlog (const char *cap, const char *fmt, va_list ap); -void AUD_log (const char *cap, const char *fmt, ...) -#ifdef __GNUC__ - __attribute__ ((__format__ (__printf__, 2, 3))) -#endif - ; +void AUD_vlog (const char *cap, const char *fmt, va_list ap) GCC_FMT_ATTR(2, 0); +void AUD_log (const char *cap, const char *fmt, ...) GCC_FMT_ATTR(2, 3); void AUD_help (void); void AUD_register_card (const char *name, QEMUSoundCard *card); diff -Nru qemu-kvm-0.12.5+noroms/audio/audio_int.h qemu-kvm-0.14.1/audio/audio_int.h --- qemu-kvm-0.12.5+noroms/audio/audio_int.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/audio/audio_int.h 2011-05-11 13:29:46.000000000 +0000 @@ -209,8 +209,9 @@ extern struct audio_driver dsound_audio_driver; extern struct audio_driver esd_audio_driver; extern struct audio_driver pa_audio_driver; +extern struct audio_driver spice_audio_driver; extern struct audio_driver winwave_audio_driver; -extern struct mixeng_volume nominal_volume; +extern const struct mixeng_volume nominal_volume; void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as); void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len); @@ -236,14 +237,6 @@ return (dst >= src) ? (dst - src) : (len - src + dst); } -#if defined __GNUC__ -#define GCC_ATTR __attribute__ ((__unused__, __format__ (__printf__, 1, 2))) -#define GCC_FMT_ATTR(n, m) __attribute__ ((__format__ (__printf__, n, m))) -#else -#define GCC_ATTR /**/ -#define GCC_FMT_ATTR(n, m) -#endif - static void GCC_ATTR dolog (const char *fmt, ...) { va_list ap; diff -Nru qemu-kvm-0.12.5+noroms/audio/audio_pt_int.c qemu-kvm-0.14.1/audio/audio_pt_int.c --- qemu-kvm-0.12.5+noroms/audio/audio_pt_int.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/audio/audio_pt_int.c 2011-05-11 13:29:46.000000000 +0000 @@ -6,7 +6,10 @@ #include "audio_int.h" #include "audio_pt_int.h" -static void logerr (struct audio_pt *pt, int err, const char *fmt, ...) +#include + +static void GCC_FMT_ATTR(3, 4) logerr (struct audio_pt *pt, int err, + const char *fmt, ...) { va_list ap; @@ -23,9 +26,16 @@ { int err, err2; const char *efunc; + sigset_t set, old_set; p->drv = drv; + err = sigfillset (&set); + if (err) { + logerr (p, errno, "%s(%s): sigfillset failed", cap, AUDIO_FUNC); + return -1; + } + err = pthread_mutex_init (&p->mutex, NULL); if (err) { efunc = "pthread_mutex_init"; @@ -38,7 +48,23 @@ goto err1; } + err = pthread_sigmask (SIG_BLOCK, &set, &old_set); + if (err) { + efunc = "pthread_sigmask"; + goto err2; + } + err = pthread_create (&p->thread, NULL, func, opaque); + + err2 = pthread_sigmask (SIG_SETMASK, &old_set, NULL); + if (err2) { + logerr (p, err2, "%s(%s): pthread_sigmask (restore) failed", + cap, AUDIO_FUNC); + /* We have failed to restore original signal mask, all bets are off, + so terminate the process */ + exit (EXIT_FAILURE); + } + if (err) { efunc = "pthread_create"; goto err2; diff -Nru qemu-kvm-0.12.5+noroms/audio/audio_template.h qemu-kvm-0.14.1/audio/audio_template.h --- qemu-kvm-0.12.5+noroms/audio/audio_template.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/audio/audio_template.h 2011-05-11 13:29:46.000000000 +0000 @@ -108,11 +108,7 @@ { int samples; -#ifdef DAC - samples = sw->hw->samples; -#else samples = ((int64_t) sw->hw->samples << 32) / sw->ratio; -#endif sw->buf = audio_calloc (AUDIO_FUNC, samples, sizeof (struct st_sample)); if (!sw->buf) { @@ -541,7 +537,7 @@ cur_ts = sw->hw->ts_helper; old_ts = ts->old_ts; - /* dolog ("cur %lld old %lld\n", cur_ts, old_ts); */ + /* dolog ("cur %" PRId64 " old %" PRId64 "\n", cur_ts, old_ts); */ if (cur_ts >= old_ts) { delta = cur_ts - old_ts; diff -Nru qemu-kvm-0.12.5+noroms/audio/dsoundaudio.c qemu-kvm-0.14.1/audio/dsoundaudio.c --- qemu-kvm-0.12.5+noroms/audio/dsoundaudio.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/audio/dsoundaudio.c 2011-05-11 13:29:46.000000000 +0000 @@ -831,11 +831,11 @@ decr = len1 + len2; if (p1 && len1) { - hw->conv (hw->conv_buf + hw->wpos, p1, len1, &nominal_volume); + hw->conv (hw->conv_buf + hw->wpos, p1, len1); } if (p2 && len2) { - hw->conv (hw->conv_buf, p2, len2, &nominal_volume); + hw->conv (hw->conv_buf, p2, len2); } dsound_unlock_in (dscb, p1, p2, blen1, blen2); diff -Nru qemu-kvm-0.12.5+noroms/audio/esdaudio.c qemu-kvm-0.14.1/audio/esdaudio.c --- qemu-kvm-0.12.5+noroms/audio/esdaudio.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/audio/esdaudio.c 2011-05-11 13:29:46.000000000 +0000 @@ -24,7 +24,6 @@ #include #include "qemu-common.h" #include "audio.h" -#include #define AUDIO_CAP "esd" #include "audio_int.h" @@ -190,10 +189,6 @@ ESDVoiceOut *esd = (ESDVoiceOut *) hw; struct audsettings obt_as = *as; int esdfmt = ESD_STREAM | ESD_PLAY; - int err; - sigset_t set, old_set; - - sigfillset (&set); esdfmt |= (as->nchannels == 2) ? ESD_STEREO : ESD_MONO; switch (as->fmt) { @@ -231,43 +226,25 @@ return -1; } - esd->fd = -1; - err = pthread_sigmask (SIG_BLOCK, &set, &old_set); - if (err) { - qesd_logerr (err, "pthread_sigmask failed\n"); - goto fail1; - } - esd->fd = esd_play_stream (esdfmt, as->freq, conf.dac_host, NULL); if (esd->fd < 0) { qesd_logerr (errno, "esd_play_stream failed\n"); - goto fail2; + goto fail1; } if (audio_pt_init (&esd->pt, qesd_thread_out, esd, AUDIO_CAP, AUDIO_FUNC)) { - goto fail3; - } - - err = pthread_sigmask (SIG_SETMASK, &old_set, NULL); - if (err) { - qesd_logerr (err, "pthread_sigmask(restore) failed\n"); + goto fail2; } return 0; - fail3: + fail2: if (close (esd->fd)) { qesd_logerr (errno, "%s: close on esd socket(%d) failed\n", AUDIO_FUNC, esd->fd); } esd->fd = -1; - fail2: - err = pthread_sigmask (SIG_SETMASK, &old_set, NULL); - if (err) { - qesd_logerr (err, "pthread_sigmask(restore) failed\n"); - } - fail1: qemu_free (esd->pcm_buf); esd->pcm_buf = NULL; @@ -369,8 +346,7 @@ break; } - hw->conv (hw->conv_buf + wpos, buf, nread >> hw->info.shift, - &nominal_volume); + hw->conv (hw->conv_buf + wpos, buf, nread >> hw->info.shift); wpos = (wpos + chunk) % hw->samples; to_grab -= chunk; } @@ -423,10 +399,6 @@ ESDVoiceIn *esd = (ESDVoiceIn *) hw; struct audsettings obt_as = *as; int esdfmt = ESD_STREAM | ESD_RECORD; - int err; - sigset_t set, old_set; - - sigfillset (&set); esdfmt |= (as->nchannels == 2) ? ESD_STEREO : ESD_MONO; switch (as->fmt) { @@ -461,44 +433,25 @@ return -1; } - esd->fd = -1; - - err = pthread_sigmask (SIG_BLOCK, &set, &old_set); - if (err) { - qesd_logerr (err, "pthread_sigmask failed\n"); - goto fail1; - } - esd->fd = esd_record_stream (esdfmt, as->freq, conf.adc_host, NULL); if (esd->fd < 0) { qesd_logerr (errno, "esd_record_stream failed\n"); - goto fail2; + goto fail1; } if (audio_pt_init (&esd->pt, qesd_thread_in, esd, AUDIO_CAP, AUDIO_FUNC)) { - goto fail3; - } - - err = pthread_sigmask (SIG_SETMASK, &old_set, NULL); - if (err) { - qesd_logerr (err, "pthread_sigmask(restore) failed\n"); + goto fail2; } return 0; - fail3: + fail2: if (close (esd->fd)) { qesd_logerr (errno, "%s: close on esd socket(%d) failed\n", AUDIO_FUNC, esd->fd); } esd->fd = -1; - fail2: - err = pthread_sigmask (SIG_SETMASK, &old_set, NULL); - if (err) { - qesd_logerr (err, "pthread_sigmask(restore) failed\n"); - } - fail1: qemu_free (esd->pcm_buf); esd->pcm_buf = NULL; diff -Nru qemu-kvm-0.12.5+noroms/audio/fmodaudio.c qemu-kvm-0.14.1/audio/fmodaudio.c --- qemu-kvm-0.12.5+noroms/audio/fmodaudio.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/audio/fmodaudio.c 2011-05-11 13:29:46.000000000 +0000 @@ -488,10 +488,10 @@ decr = len1 + len2; if (p1 && blen1) { - hw->conv (hw->conv_buf + hw->wpos, p1, len1, &nominal_volume); + hw->conv (hw->conv_buf + hw->wpos, p1, len1); } if (p2 && len2) { - hw->conv (hw->conv_buf, p2, len2, &nominal_volume); + hw->conv (hw->conv_buf, p2, len2); } fmod_unlock_sample (fmd->fmod_sample, p1, p2, blen1, blen2); diff -Nru qemu-kvm-0.12.5+noroms/audio/mixeng.c qemu-kvm-0.14.1/audio/mixeng.c --- qemu-kvm-0.12.5+noroms/audio/mixeng.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/audio/mixeng.c 2011-05-11 13:29:46.000000000 +0000 @@ -123,7 +123,7 @@ #undef IN_T #undef SHIFT -/* Unsigned 16 bit */ +/* Unsigned 32 bit */ #define IN_T uint32_t #define IN_MIN 0 #define IN_MAX UINT32_MAX @@ -333,3 +333,28 @@ { memset (buf, 0, len * sizeof (struct st_sample)); } + +void mixeng_volume (struct st_sample *buf, int len, struct mixeng_volume *vol) +{ +#ifdef CONFIG_MIXEMU + if (vol->mute) { + mixeng_clear (buf, len); + return; + } + + while (len--) { +#ifdef FLOAT_MIXENG + buf->l = buf->l * vol->l; + buf->r = buf->r * vol->r; +#else + buf->l = (buf->l * vol->l) >> 32; + buf->r = (buf->r * vol->r) >> 32; +#endif + buf += 1; + } +#else + (void) buf; + (void) len; + (void) vol; +#endif +} diff -Nru qemu-kvm-0.12.5+noroms/audio/mixeng.h qemu-kvm-0.14.1/audio/mixeng.h --- qemu-kvm-0.12.5+noroms/audio/mixeng.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/audio/mixeng.h 2011-05-11 13:29:46.000000000 +0000 @@ -33,8 +33,7 @@ struct st_sample { int64_t l; int64_t r; }; #endif -typedef void (t_sample) (struct st_sample *dst, const void *src, - int samples, struct mixeng_volume *vol); +typedef void (t_sample) (struct st_sample *dst, const void *src, int samples); typedef void (f_sample) (void *dst, const struct st_sample *src, int samples); extern t_sample *mixeng_conv[2][2][2][3]; @@ -47,5 +46,6 @@ int *isamp, int *osamp); void st_rate_stop (void *opaque); void mixeng_clear (struct st_sample *buf, int len); +void mixeng_volume (struct st_sample *buf, int len, struct mixeng_volume *vol); #endif /* mixeng.h */ diff -Nru qemu-kvm-0.12.5+noroms/audio/mixeng_template.h qemu-kvm-0.14.1/audio/mixeng_template.h --- qemu-kvm-0.12.5+noroms/audio/mixeng_template.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/audio/mixeng_template.h 2011-05-11 13:29:46.000000000 +0000 @@ -31,16 +31,6 @@ #define HALF (IN_MAX >> 1) #endif -#ifdef CONFIG_MIXEMU -#ifdef FLOAT_MIXENG -#define VOL(a, b) ((a) * (b)) -#else -#define VOL(a, b) ((a) * (b)) >> 32 -#endif -#else -#define VOL(a, b) a -#endif - #define ET glue (ENDIAN_CONVERSION, glue (_, IN_T)) #ifdef FLOAT_MIXENG @@ -109,40 +99,26 @@ #endif static void glue (glue (conv_, ET), _to_stereo) - (struct st_sample *dst, const void *src, int samples, struct mixeng_volume *vol) + (struct st_sample *dst, const void *src, int samples) { struct st_sample *out = dst; IN_T *in = (IN_T *) src; -#ifdef CONFIG_MIXEMU - if (vol->mute) { - mixeng_clear (dst, samples); - return; - } -#else - (void) vol; -#endif + while (samples--) { - out->l = VOL (glue (conv_, ET) (*in++), vol->l); - out->r = VOL (glue (conv_, ET) (*in++), vol->r); + out->l = glue (conv_, ET) (*in++); + out->r = glue (conv_, ET) (*in++); out += 1; } } static void glue (glue (conv_, ET), _to_mono) - (struct st_sample *dst, const void *src, int samples, struct mixeng_volume *vol) + (struct st_sample *dst, const void *src, int samples) { struct st_sample *out = dst; IN_T *in = (IN_T *) src; -#ifdef CONFIG_MIXEMU - if (vol->mute) { - mixeng_clear (dst, samples); - return; - } -#else - (void) vol; -#endif + while (samples--) { - out->l = VOL (glue (conv_, ET) (in[0]), vol->l); + out->l = glue (conv_, ET) (in[0]); out->r = out->l; out += 1; in += 1; @@ -174,4 +150,3 @@ #undef ET #undef HALF -#undef VOL diff -Nru qemu-kvm-0.12.5+noroms/audio/noaudio.c qemu-kvm-0.14.1/audio/noaudio.c --- qemu-kvm-0.12.5+noroms/audio/noaudio.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/audio/noaudio.c 2011-05-11 13:29:46.000000000 +0000 @@ -117,11 +117,14 @@ static int no_read (SWVoiceIn *sw, void *buf, int size) { + /* use custom code here instead of audio_pcm_sw_read() to avoid + * useless resampling/mixing */ int samples = size >> sw->info.shift; int total = sw->hw->total_samples_captured - sw->total_hw_samples_acquired; int to_clear = audio_MIN (samples, total); + sw->total_hw_samples_acquired += total; audio_pcm_info_clear_buf (&sw->info, buf, to_clear); - return to_clear; + return to_clear << sw->info.shift; } static int no_ctl_in (HWVoiceIn *hw, int cmd, ...) diff -Nru qemu-kvm-0.12.5+noroms/audio/ossaudio.c qemu-kvm-0.14.1/audio/ossaudio.c --- qemu-kvm-0.12.5+noroms/audio/ossaudio.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/audio/ossaudio.c 2011-05-11 13:29:46.000000000 +0000 @@ -161,7 +161,7 @@ return audio_pcm_sw_write (sw, buf, len); } -static int aud_to_ossfmt (audfmt_e fmt) +static int aud_to_ossfmt (audfmt_e fmt, int endianness) { switch (fmt) { case AUD_FMT_S8: @@ -171,10 +171,20 @@ return AFMT_U8; case AUD_FMT_S16: - return AFMT_S16_LE; + if (endianness) { + return AFMT_S16_BE; + } + else { + return AFMT_S16_LE; + } case AUD_FMT_U16: - return AFMT_U16_LE; + if (endianness) { + return AFMT_U16_BE; + } + else { + return AFMT_U16_LE; + } default: dolog ("Internal logic error: Bad audio format %d\n", fmt); @@ -516,7 +526,7 @@ oss->fd = -1; - req.fmt = aud_to_ossfmt (as->fmt); + req.fmt = aud_to_ossfmt (as->fmt, as->endianness); req.freq = as->freq; req.nchannels = as->nchannels; req.fragsize = conf.fragsize; @@ -682,7 +692,7 @@ oss->fd = -1; - req.fmt = aud_to_ossfmt (as->fmt); + req.fmt = aud_to_ossfmt (as->fmt, as->endianness); req.freq = as->freq; req.nchannels = as->nchannels; req.fragsize = conf.fragsize; @@ -778,8 +788,7 @@ hw->info.align + 1); } read_samples += nread >> hwshift; - hw->conv (hw->conv_buf + bufs[i].add, p, nread >> hwshift, - &nominal_volume); + hw->conv (hw->conv_buf + bufs[i].add, p, nread >> hwshift); } if (bufs[i].len - nread) { diff -Nru qemu-kvm-0.12.5+noroms/audio/paaudio.c qemu-kvm-0.14.1/audio/paaudio.c --- qemu-kvm-0.12.5+noroms/audio/paaudio.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/audio/paaudio.c 2011-05-11 13:29:46.000000000 +0000 @@ -33,13 +33,11 @@ static struct { int samples; - int divisor; char *server; char *sink; char *source; } conf = { - .samples = 1024, - .divisor = 2, + .samples = 4096, }; static void GCC_FMT_ATTR (2, 3) qpa_logerr (int err, const char *fmt, ...) @@ -57,9 +55,6 @@ { PAVoiceOut *pa = arg; HWVoiceOut *hw = &pa->hw; - int threshold; - - threshold = conf.divisor ? hw->samples / conf.divisor : 0; if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { return NULL; @@ -73,7 +68,7 @@ goto exit; } - if (pa->live > threshold) { + if (pa->live > 0) { break; } @@ -82,8 +77,8 @@ } } - decr = to_mix = pa->live; - rpos = hw->rpos; + decr = to_mix = audio_MIN (pa->live, conf.samples >> 2); + rpos = pa->rpos; if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) { return NULL; @@ -152,9 +147,6 @@ { PAVoiceIn *pa = arg; HWVoiceIn *hw = &pa->hw; - int threshold; - - threshold = conf.divisor ? hw->samples / conf.divisor : 0; if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { return NULL; @@ -168,7 +160,7 @@ goto exit; } - if (pa->dead > threshold) { + if (pa->dead > 0) { break; } @@ -177,8 +169,8 @@ } } - incr = to_grab = pa->dead; - wpos = hw->wpos; + incr = to_grab = audio_MIN (pa->dead, conf.samples >> 2); + wpos = pa->wpos; if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) { return NULL; @@ -195,7 +187,7 @@ return NULL; } - hw->conv (hw->conv_buf + wpos, buf, chunk, &nominal_volume); + hw->conv (hw->conv_buf + wpos, buf, chunk); wpos = (wpos + chunk) % hw->samples; to_grab -= chunk; } @@ -295,6 +287,7 @@ { int error; static pa_sample_spec ss; + static pa_buffer_attr ba; struct audsettings obt_as = *as; PAVoiceOut *pa = (PAVoiceOut *) hw; @@ -302,6 +295,15 @@ ss.channels = as->nchannels; ss.rate = as->freq; + /* + * qemu audio tick runs at 250 Hz (by default), so processing + * data chunks worth 4 ms of sound should be a good fit. + */ + ba.tlength = pa_usec_to_bytes (4 * 1000, &ss); + ba.minreq = pa_usec_to_bytes (2 * 1000, &ss); + ba.maxlength = -1; + ba.prebuf = -1; + obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness); pa->s = pa_simple_new ( @@ -312,7 +314,7 @@ "pcm.playback", &ss, NULL, /* channel map */ - NULL, /* buffering attributes */ + &ba, /* buffering attributes */ &error ); if (!pa->s) { @@ -323,6 +325,7 @@ audio_pcm_init_info (&hw->info, &obt_as); hw->samples = conf.samples; pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift); + pa->rpos = hw->rpos; if (!pa->pcm_buf) { dolog ("Could not allocate buffer (%d bytes)\n", hw->samples << hw->info.shift); @@ -377,6 +380,7 @@ audio_pcm_init_info (&hw->info, &obt_as); hw->samples = conf.samples; pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift); + pa->wpos = hw->wpos; if (!pa->pcm_buf) { dolog ("Could not allocate buffer (%d bytes)\n", hw->samples << hw->info.shift); @@ -472,12 +476,6 @@ .descr = "buffer size in samples" }, { - .name = "DIVISOR", - .tag = AUD_OPT_INT, - .valp = &conf.divisor, - .descr = "threshold divisor" - }, - { .name = "SERVER", .tag = AUD_OPT_STR, .valp = &conf.server, diff -Nru qemu-kvm-0.12.5+noroms/audio/sdlaudio.c qemu-kvm-0.14.1/audio/sdlaudio.c --- qemu-kvm-0.12.5+noroms/audio/sdlaudio.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/audio/sdlaudio.c 2011-05-11 13:29:46.000000000 +0000 @@ -41,8 +41,8 @@ typedef struct SDLVoiceOut { HWVoiceOut hw; int live; + int rpos; int decr; - int pending; } SDLVoiceOut; static struct { @@ -115,23 +115,19 @@ return sdl_post (s, forfn); } -static int aud_to_sdlfmt (audfmt_e fmt, int *shift) +static int aud_to_sdlfmt (audfmt_e fmt) { switch (fmt) { case AUD_FMT_S8: - *shift = 0; return AUDIO_S8; case AUD_FMT_U8: - *shift = 0; return AUDIO_U8; case AUD_FMT_S16: - *shift = 1; return AUDIO_S16LSB; case AUD_FMT_U16: - *shift = 1; return AUDIO_U16LSB; default: @@ -188,11 +184,20 @@ { int status; #ifndef _WIN32 + int err; sigset_t new, old; /* Make sure potential threads created by SDL don't hog signals. */ - sigfillset (&new); - pthread_sigmask (SIG_BLOCK, &new, &old); + err = sigfillset (&new); + if (err) { + dolog ("sdl_open: sigfillset failed: %s\n", strerror (errno)); + return -1; + } + err = pthread_sigmask (SIG_BLOCK, &new, &old); + if (err) { + dolog ("sdl_open: pthread_sigmask failed: %s\n", strerror (err)); + return -1; + } #endif status = SDL_OpenAudio (req, obt); @@ -201,7 +206,14 @@ } #ifndef _WIN32 - pthread_sigmask (SIG_SETMASK, &old, NULL); + err = pthread_sigmask (SIG_SETMASK, &old, NULL); + if (err) { + dolog ("sdl_open: pthread_sigmask (restore) failed: %s\n", + strerror (errno)); + /* We have failed to restore original signal mask, all bets are off, + so exit the process */ + exit (EXIT_FAILURE); + } #endif return status; } @@ -225,10 +237,6 @@ HWVoiceOut *hw = &sdl->hw; int samples = len >> hw->info.shift; - if (sdl_lock (s, "sdl_callback")) { - return; - } - if (s->exit) { return; } @@ -236,34 +244,49 @@ while (samples) { int to_mix, decr; - while (!sdl->pending) { - if (sdl_unlock (s, "sdl_callback")) { - return; - } - - sdl_wait (s, "sdl_callback"); - if (s->exit) { - return; - } - - if (sdl_lock (s, "sdl_callback")) { - return; - } - sdl->pending += sdl->live; - sdl->live = 0; + /* dolog ("in callback samples=%d\n", samples); */ + sdl_wait (s, "sdl_callback"); + if (s->exit) { + return; + } + + if (sdl_lock (s, "sdl_callback")) { + return; + } + + if (audio_bug (AUDIO_FUNC, sdl->live < 0 || sdl->live > hw->samples)) { + dolog ("sdl->live=%d hw->samples=%d\n", + sdl->live, hw->samples); + return; } - to_mix = audio_MIN (samples, sdl->pending); - decr = audio_pcm_hw_clip_out (hw, buf, to_mix, 0); - buf += decr << hw->info.shift; + if (!sdl->live) { + goto again; + } + + /* dolog ("in callback live=%d\n", live); */ + to_mix = audio_MIN (samples, sdl->live); + decr = to_mix; + while (to_mix) { + int chunk = audio_MIN (to_mix, hw->samples - hw->rpos); + struct st_sample *src = hw->mix_buf + hw->rpos; + + /* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */ + hw->clip (buf, src, chunk); + sdl->rpos = (sdl->rpos + chunk) % hw->samples; + to_mix -= chunk; + buf += chunk << hw->info.shift; + } samples -= decr; + sdl->live -= decr; sdl->decr += decr; - sdl->pending -= decr; - } - if (sdl_unlock (s, "sdl_callback")) { - return; + again: + if (sdl_unlock (s, "sdl_callback")) { + return; + } } + /* dolog ("done len=%d\n", len); */ } static int sdl_write_out (SWVoiceOut *sw, void *buf, int len) @@ -281,9 +304,18 @@ return 0; } - sdl->live = live; - decr = sdl->decr; - sdl->decr = 0; + if (sdl->decr > live) { + ldebug ("sdl->decr %d live %d sdl->live %d\n", + sdl->decr, + live, + sdl->live); + } + + decr = audio_MIN (sdl->decr, live); + sdl->decr -= decr; + + sdl->live = live - decr; + hw->rpos = sdl->rpos; if (sdl->live > 0) { sdl_unlock_and_post (s, "sdl_run_out"); @@ -306,16 +338,13 @@ SDLVoiceOut *sdl = (SDLVoiceOut *) hw; SDLAudioState *s = &glob_sdl; SDL_AudioSpec req, obt; - int shift; int endianess; int err; audfmt_e effective_fmt; struct audsettings obt_as; - shift <<= as->nchannels == 2; - req.freq = as->freq; - req.format = aud_to_sdlfmt (as->fmt, &shift); + req.format = aud_to_sdlfmt (as->fmt); req.channels = as->nchannels; req.samples = conf.nb_samples; req.callback = sdl_callback; diff -Nru qemu-kvm-0.12.5+noroms/audio/spiceaudio.c qemu-kvm-0.14.1/audio/spiceaudio.c --- qemu-kvm-0.12.5+noroms/audio/spiceaudio.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/audio/spiceaudio.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,345 @@ +/* + * Copyright (C) 2010 Red Hat, Inc. + * + * maintained by Gerd Hoffmann + * + * 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 or + * (at your option) version 3 of the License. + * + * 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, see . + */ + +#include "hw/hw.h" +#include "qemu-timer.h" +#include "ui/qemu-spice.h" + +#define AUDIO_CAP "spice" +#include "audio.h" +#include "audio_int.h" + +#define LINE_IN_SAMPLES 1024 +#define LINE_OUT_SAMPLES 1024 + +typedef struct SpiceRateCtl { + int64_t start_ticks; + int64_t bytes_sent; +} SpiceRateCtl; + +typedef struct SpiceVoiceOut { + HWVoiceOut hw; + SpicePlaybackInstance sin; + SpiceRateCtl rate; + int active; + uint32_t *frame; + uint32_t *fpos; + uint32_t fsize; +} SpiceVoiceOut; + +typedef struct SpiceVoiceIn { + HWVoiceIn hw; + SpiceRecordInstance sin; + SpiceRateCtl rate; + int active; + uint32_t samples[LINE_IN_SAMPLES]; +} SpiceVoiceIn; + +static const SpicePlaybackInterface playback_sif = { + .base.type = SPICE_INTERFACE_PLAYBACK, + .base.description = "playback", + .base.major_version = SPICE_INTERFACE_PLAYBACK_MAJOR, + .base.minor_version = SPICE_INTERFACE_PLAYBACK_MINOR, +}; + +static const SpiceRecordInterface record_sif = { + .base.type = SPICE_INTERFACE_RECORD, + .base.description = "record", + .base.major_version = SPICE_INTERFACE_RECORD_MAJOR, + .base.minor_version = SPICE_INTERFACE_RECORD_MINOR, +}; + +static void *spice_audio_init (void) +{ + if (!using_spice) { + return NULL; + } + return &spice_audio_init; +} + +static void spice_audio_fini (void *opaque) +{ + /* nothing */ +} + +static void rate_start (SpiceRateCtl *rate) +{ + memset (rate, 0, sizeof (*rate)); + rate->start_ticks = qemu_get_clock (vm_clock); +} + +static int rate_get_samples (struct audio_pcm_info *info, SpiceRateCtl *rate) +{ + int64_t now; + int64_t ticks; + int64_t bytes; + int64_t samples; + + now = qemu_get_clock (vm_clock); + ticks = now - rate->start_ticks; + bytes = muldiv64 (ticks, info->bytes_per_second, get_ticks_per_sec ()); + samples = (bytes - rate->bytes_sent) >> info->shift; + if (samples < 0 || samples > 65536) { + fprintf (stderr, "Resetting rate control (%" PRId64 " samples)\n", samples); + rate_start (rate); + samples = 0; + } + rate->bytes_sent += samples << info->shift; + return samples; +} + +/* playback */ + +static int line_out_init (HWVoiceOut *hw, struct audsettings *as) +{ + SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw); + struct audsettings settings; + + settings.freq = SPICE_INTERFACE_PLAYBACK_FREQ; + settings.nchannels = SPICE_INTERFACE_PLAYBACK_CHAN; + settings.fmt = AUD_FMT_S16; + settings.endianness = AUDIO_HOST_ENDIANNESS; + + audio_pcm_init_info (&hw->info, &settings); + hw->samples = LINE_OUT_SAMPLES; + out->active = 0; + + out->sin.base.sif = &playback_sif.base; + qemu_spice_add_interface (&out->sin.base); + return 0; +} + +static void line_out_fini (HWVoiceOut *hw) +{ + SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw); + + spice_server_remove_interface (&out->sin.base); +} + +static int line_out_run (HWVoiceOut *hw, int live) +{ + SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw); + int rpos, decr; + int samples; + + if (!live) { + return 0; + } + + decr = rate_get_samples (&hw->info, &out->rate); + decr = audio_MIN (live, decr); + + samples = decr; + rpos = hw->rpos; + while (samples) { + int left_till_end_samples = hw->samples - rpos; + int len = audio_MIN (samples, left_till_end_samples); + + if (!out->frame) { + spice_server_playback_get_buffer (&out->sin, &out->frame, &out->fsize); + out->fpos = out->frame; + } + if (out->frame) { + len = audio_MIN (len, out->fsize); + hw->clip (out->fpos, hw->mix_buf + rpos, len); + out->fsize -= len; + out->fpos += len; + if (out->fsize == 0) { + spice_server_playback_put_samples (&out->sin, out->frame); + out->frame = out->fpos = NULL; + } + } + rpos = (rpos + len) % hw->samples; + samples -= len; + } + hw->rpos = rpos; + return decr; +} + +static int line_out_write (SWVoiceOut *sw, void *buf, int len) +{ + return audio_pcm_sw_write (sw, buf, len); +} + +static int line_out_ctl (HWVoiceOut *hw, int cmd, ...) +{ + SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw); + + switch (cmd) { + case VOICE_ENABLE: + if (out->active) { + break; + } + out->active = 1; + rate_start (&out->rate); + spice_server_playback_start (&out->sin); + break; + case VOICE_DISABLE: + if (!out->active) { + break; + } + out->active = 0; + if (out->frame) { + memset (out->fpos, 0, out->fsize << 2); + spice_server_playback_put_samples (&out->sin, out->frame); + out->frame = out->fpos = NULL; + } + spice_server_playback_stop (&out->sin); + break; + } + return 0; +} + +/* record */ + +static int line_in_init (HWVoiceIn *hw, struct audsettings *as) +{ + SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw); + struct audsettings settings; + + settings.freq = SPICE_INTERFACE_RECORD_FREQ; + settings.nchannels = SPICE_INTERFACE_RECORD_CHAN; + settings.fmt = AUD_FMT_S16; + settings.endianness = AUDIO_HOST_ENDIANNESS; + + audio_pcm_init_info (&hw->info, &settings); + hw->samples = LINE_IN_SAMPLES; + in->active = 0; + + in->sin.base.sif = &record_sif.base; + qemu_spice_add_interface (&in->sin.base); + return 0; +} + +static void line_in_fini (HWVoiceIn *hw) +{ + SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw); + + spice_server_remove_interface (&in->sin.base); +} + +static int line_in_run (HWVoiceIn *hw) +{ + SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw); + int num_samples; + int ready; + int len[2]; + uint64_t delta_samp; + const uint32_t *samples; + + if (!(num_samples = hw->samples - audio_pcm_hw_get_live_in (hw))) { + return 0; + } + + delta_samp = rate_get_samples (&hw->info, &in->rate); + num_samples = audio_MIN (num_samples, delta_samp); + + ready = spice_server_record_get_samples (&in->sin, in->samples, num_samples); + samples = in->samples; + if (ready == 0) { + static const uint32_t silence[LINE_IN_SAMPLES]; + samples = silence; + ready = LINE_IN_SAMPLES; + } + + num_samples = audio_MIN (ready, num_samples); + + if (hw->wpos + num_samples > hw->samples) { + len[0] = hw->samples - hw->wpos; + len[1] = num_samples - len[0]; + } else { + len[0] = num_samples; + len[1] = 0; + } + + hw->conv (hw->conv_buf + hw->wpos, samples, len[0]); + + if (len[1]) { + hw->conv (hw->conv_buf, samples + len[0], len[1]); + } + + hw->wpos = (hw->wpos + num_samples) % hw->samples; + + return num_samples; +} + +static int line_in_read (SWVoiceIn *sw, void *buf, int size) +{ + return audio_pcm_sw_read (sw, buf, size); +} + +static int line_in_ctl (HWVoiceIn *hw, int cmd, ...) +{ + SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw); + + switch (cmd) { + case VOICE_ENABLE: + if (in->active) { + break; + } + in->active = 1; + rate_start (&in->rate); + spice_server_record_start (&in->sin); + break; + case VOICE_DISABLE: + if (!in->active) { + break; + } + in->active = 0; + spice_server_record_stop (&in->sin); + break; + } + return 0; +} + +static struct audio_option audio_options[] = { + { /* end of list */ }, +}; + +static struct audio_pcm_ops audio_callbacks = { + .init_out = line_out_init, + .fini_out = line_out_fini, + .run_out = line_out_run, + .write = line_out_write, + .ctl_out = line_out_ctl, + + .init_in = line_in_init, + .fini_in = line_in_fini, + .run_in = line_in_run, + .read = line_in_read, + .ctl_in = line_in_ctl, +}; + +struct audio_driver spice_audio_driver = { + .name = "spice", + .descr = "spice audio driver", + .options = audio_options, + .init = spice_audio_init, + .fini = spice_audio_fini, + .pcm_ops = &audio_callbacks, + .max_voices_out = 1, + .max_voices_in = 1, + .voice_size_out = sizeof (SpiceVoiceOut), + .voice_size_in = sizeof (SpiceVoiceIn), +}; + +void qemu_spice_audio_init (void) +{ + spice_audio_driver.can_be_default = 1; +} diff -Nru qemu-kvm-0.12.5+noroms/audio/winwaveaudio.c qemu-kvm-0.14.1/audio/winwaveaudio.c --- qemu-kvm-0.12.5+noroms/audio/winwaveaudio.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/audio/winwaveaudio.c 2011-05-11 13:29:46.000000000 +0000 @@ -581,8 +581,7 @@ int conv = audio_MIN (left, decr); hw->conv (hw->conv_buf + hw->wpos, advance (wave->pcm_buf, wave->rpos << hw->info.shift), - conv, - &nominal_volume); + conv); wave->rpos = (wave->rpos + conv) % hw->samples; hw->wpos = (hw->wpos + conv) % hw->samples; diff -Nru qemu-kvm-0.12.5+noroms/balloon.c qemu-kvm-0.14.1/balloon.c --- qemu-kvm-0.12.5+noroms/balloon.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/balloon.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,148 @@ +/* + * 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 "sysemu.h" +#include "monitor.h" +#include "qjson.h" +#include "qint.h" +#include "cpu-common.h" +#include "kvm.h" +#include "balloon.h" +#include "trace.h" + + +static QEMUBalloonEvent *qemu_balloon_event; +void *qemu_balloon_event_opaque; + +void qemu_add_balloon_handler(QEMUBalloonEvent *func, void *opaque) +{ + qemu_balloon_event = func; + qemu_balloon_event_opaque = opaque; +} + +int qemu_balloon(ram_addr_t target, MonitorCompletion cb, void *opaque) +{ + if (qemu_balloon_event) { + trace_balloon_event(qemu_balloon_event_opaque, target); + qemu_balloon_event(qemu_balloon_event_opaque, target, cb, opaque); + return 1; + } else { + return 0; + } +} + +int qemu_balloon_status(MonitorCompletion cb, void *opaque) +{ + if (qemu_balloon_event) { + qemu_balloon_event(qemu_balloon_event_opaque, 0, cb, opaque); + return 1; + } else { + return 0; + } +} + +static void print_balloon_stat(const char *key, QObject *obj, void *opaque) +{ + Monitor *mon = opaque; + + if (strcmp(key, "actual")) + monitor_printf(mon, ",%s=%" PRId64, key, + qint_get_int(qobject_to_qint(obj))); +} + +void monitor_print_balloon(Monitor *mon, const QObject *data) +{ + QDict *qdict; + + qdict = qobject_to_qdict(data); + if (!qdict_haskey(qdict, "actual")) + return; + + monitor_printf(mon, "balloon: actual=%" PRId64, + qdict_get_int(qdict, "actual") >> 20); + qdict_iter(qdict, print_balloon_stat, mon); + monitor_printf(mon, "\n"); +} + +/** + * do_info_balloon(): Balloon information + * + * Make an asynchronous request for balloon info. When the request completes + * a QDict will be returned according to the following specification: + * + * - "actual": current balloon value in bytes + * The following fields may or may not be present: + * - "mem_swapped_in": Amount of memory swapped in (bytes) + * - "mem_swapped_out": Amount of memory swapped out (bytes) + * - "major_page_faults": Number of major faults + * - "minor_page_faults": Number of minor faults + * - "free_mem": Total amount of free and unused memory (bytes) + * - "total_mem": Total amount of available memory (bytes) + * + * Example: + * + * { "actual": 1073741824, "mem_swapped_in": 0, "mem_swapped_out": 0, + * "major_page_faults": 142, "minor_page_faults": 239245, + * "free_mem": 1014185984, "total_mem": 1044668416 } + */ +int do_info_balloon(Monitor *mon, MonitorCompletion cb, void *opaque) +{ + int ret; + + if (kvm_enabled() && !kvm_has_sync_mmu()) { + qerror_report(QERR_KVM_MISSING_CAP, "synchronous MMU", "balloon"); + return -1; + } + + ret = qemu_balloon_status(cb, opaque); + if (!ret) { + qerror_report(QERR_DEVICE_NOT_ACTIVE, "balloon"); + return -1; + } + + return 0; +} + +/** + * do_balloon(): Request VM to change its memory allocation + */ +int do_balloon(Monitor *mon, const QDict *params, + MonitorCompletion cb, void *opaque) +{ + int ret; + + if (kvm_enabled() && !kvm_has_sync_mmu()) { + qerror_report(QERR_KVM_MISSING_CAP, "synchronous MMU", "balloon"); + return -1; + } + + ret = qemu_balloon(qdict_get_int(params, "value"), cb, opaque); + if (ret == 0) { + qerror_report(QERR_DEVICE_NOT_ACTIVE, "balloon"); + return -1; + } + + cb(opaque, NULL); + return 0; +} diff -Nru qemu-kvm-0.12.5+noroms/balloon.h qemu-kvm-0.14.1/balloon.h --- qemu-kvm-0.12.5+noroms/balloon.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/balloon.h 2011-05-11 13:29:46.000000000 +0000 @@ -14,14 +14,20 @@ #ifndef _QEMU_BALLOON_H #define _QEMU_BALLOON_H -#include "cpu-defs.h" +#include "monitor.h" -typedef ram_addr_t (QEMUBalloonEvent)(void *opaque, ram_addr_t target); +typedef void (QEMUBalloonEvent)(void *opaque, ram_addr_t target, + MonitorCompletion cb, void *cb_data); void qemu_add_balloon_handler(QEMUBalloonEvent *func, void *opaque); -void qemu_balloon(ram_addr_t target); +int qemu_balloon(ram_addr_t target, MonitorCompletion cb, void *opaque); -ram_addr_t qemu_balloon_status(void); +int qemu_balloon_status(MonitorCompletion cb, void *opaque); + +void monitor_print_balloon(Monitor *mon, const QObject *data); +int do_info_balloon(Monitor *mon, MonitorCompletion cb, void *opaque); +int do_balloon(Monitor *mon, const QDict *params, + MonitorCompletion cb, void *opaque); #endif diff -Nru qemu-kvm-0.12.5+noroms/block/blkdebug.c qemu-kvm-0.14.1/block/blkdebug.c --- qemu-kvm-0.12.5+noroms/block/blkdebug.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/block/blkdebug.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,471 @@ +/* + * Block protocol for I/O error injection + * + * Copyright (c) 2010 Kevin Wolf + * + * 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 "qemu-common.h" +#include "block_int.h" +#include "module.h" + +typedef struct BlkdebugVars { + int state; + + /* If inject_errno != 0, an error is injected for requests */ + int inject_errno; + + /* Decides if all future requests fail (false) or only the next one and + * after the next request inject_errno is reset to 0 (true) */ + bool inject_once; + + /* Decides if aio_readv/writev fails right away (true) or returns an error + * return value only in the callback (false) */ + bool inject_immediately; +} BlkdebugVars; + +typedef struct BDRVBlkdebugState { + BlkdebugVars vars; + QLIST_HEAD(list, BlkdebugRule) rules[BLKDBG_EVENT_MAX]; +} BDRVBlkdebugState; + +typedef struct BlkdebugAIOCB { + BlockDriverAIOCB common; + QEMUBH *bh; + int ret; +} BlkdebugAIOCB; + +static void blkdebug_aio_cancel(BlockDriverAIOCB *blockacb); + +static AIOPool blkdebug_aio_pool = { + .aiocb_size = sizeof(BlkdebugAIOCB), + .cancel = blkdebug_aio_cancel, +}; + +enum { + ACTION_INJECT_ERROR, + ACTION_SET_STATE, +}; + +typedef struct BlkdebugRule { + BlkDebugEvent event; + int action; + int state; + union { + struct { + int error; + int immediately; + int once; + } inject; + struct { + int new_state; + } set_state; + } options; + QLIST_ENTRY(BlkdebugRule) next; +} BlkdebugRule; + +static QemuOptsList inject_error_opts = { + .name = "inject-error", + .head = QTAILQ_HEAD_INITIALIZER(inject_error_opts.head), + .desc = { + { + .name = "event", + .type = QEMU_OPT_STRING, + }, + { + .name = "state", + .type = QEMU_OPT_NUMBER, + }, + { + .name = "errno", + .type = QEMU_OPT_NUMBER, + }, + { + .name = "once", + .type = QEMU_OPT_BOOL, + }, + { + .name = "immediately", + .type = QEMU_OPT_BOOL, + }, + { /* end of list */ } + }, +}; + +static QemuOptsList set_state_opts = { + .name = "set-state", + .head = QTAILQ_HEAD_INITIALIZER(set_state_opts.head), + .desc = { + { + .name = "event", + .type = QEMU_OPT_STRING, + }, + { + .name = "state", + .type = QEMU_OPT_NUMBER, + }, + { + .name = "new_state", + .type = QEMU_OPT_NUMBER, + }, + { /* end of list */ } + }, +}; + +static QemuOptsList *config_groups[] = { + &inject_error_opts, + &set_state_opts, + NULL +}; + +static const char *event_names[BLKDBG_EVENT_MAX] = { + [BLKDBG_L1_UPDATE] = "l1_update", + [BLKDBG_L1_GROW_ALLOC_TABLE] = "l1_grow.alloc_table", + [BLKDBG_L1_GROW_WRITE_TABLE] = "l1_grow.write_table", + [BLKDBG_L1_GROW_ACTIVATE_TABLE] = "l1_grow.activate_table", + + [BLKDBG_L2_LOAD] = "l2_load", + [BLKDBG_L2_UPDATE] = "l2_update", + [BLKDBG_L2_UPDATE_COMPRESSED] = "l2_update_compressed", + [BLKDBG_L2_ALLOC_COW_READ] = "l2_alloc.cow_read", + [BLKDBG_L2_ALLOC_WRITE] = "l2_alloc.write", + + [BLKDBG_READ] = "read", + [BLKDBG_READ_AIO] = "read_aio", + [BLKDBG_READ_BACKING] = "read_backing", + [BLKDBG_READ_BACKING_AIO] = "read_backing_aio", + [BLKDBG_READ_COMPRESSED] = "read_compressed", + + [BLKDBG_WRITE_AIO] = "write_aio", + [BLKDBG_WRITE_COMPRESSED] = "write_compressed", + + [BLKDBG_VMSTATE_LOAD] = "vmstate_load", + [BLKDBG_VMSTATE_SAVE] = "vmstate_save", + + [BLKDBG_COW_READ] = "cow_read", + [BLKDBG_COW_WRITE] = "cow_write", + + [BLKDBG_REFTABLE_LOAD] = "reftable_load", + [BLKDBG_REFTABLE_GROW] = "reftable_grow", + + [BLKDBG_REFBLOCK_LOAD] = "refblock_load", + [BLKDBG_REFBLOCK_UPDATE] = "refblock_update", + [BLKDBG_REFBLOCK_UPDATE_PART] = "refblock_update_part", + [BLKDBG_REFBLOCK_ALLOC] = "refblock_alloc", + [BLKDBG_REFBLOCK_ALLOC_HOOKUP] = "refblock_alloc.hookup", + [BLKDBG_REFBLOCK_ALLOC_WRITE] = "refblock_alloc.write", + [BLKDBG_REFBLOCK_ALLOC_WRITE_BLOCKS] = "refblock_alloc.write_blocks", + [BLKDBG_REFBLOCK_ALLOC_WRITE_TABLE] = "refblock_alloc.write_table", + [BLKDBG_REFBLOCK_ALLOC_SWITCH_TABLE] = "refblock_alloc.switch_table", + + [BLKDBG_CLUSTER_ALLOC] = "cluster_alloc", + [BLKDBG_CLUSTER_ALLOC_BYTES] = "cluster_alloc_bytes", + [BLKDBG_CLUSTER_FREE] = "cluster_free", +}; + +static int get_event_by_name(const char *name, BlkDebugEvent *event) +{ + int i; + + for (i = 0; i < BLKDBG_EVENT_MAX; i++) { + if (!strcmp(event_names[i], name)) { + *event = i; + return 0; + } + } + + return -1; +} + +struct add_rule_data { + BDRVBlkdebugState *s; + int action; +}; + +static int add_rule(QemuOpts *opts, void *opaque) +{ + struct add_rule_data *d = opaque; + BDRVBlkdebugState *s = d->s; + const char* event_name; + BlkDebugEvent event; + struct BlkdebugRule *rule; + + /* Find the right event for the rule */ + event_name = qemu_opt_get(opts, "event"); + if (!event_name || get_event_by_name(event_name, &event) < 0) { + return -1; + } + + /* Set attributes common for all actions */ + rule = qemu_mallocz(sizeof(*rule)); + *rule = (struct BlkdebugRule) { + .event = event, + .action = d->action, + .state = qemu_opt_get_number(opts, "state", 0), + }; + + /* Parse action-specific options */ + switch (d->action) { + case ACTION_INJECT_ERROR: + rule->options.inject.error = qemu_opt_get_number(opts, "errno", EIO); + rule->options.inject.once = qemu_opt_get_bool(opts, "once", 0); + rule->options.inject.immediately = + qemu_opt_get_bool(opts, "immediately", 0); + break; + + case ACTION_SET_STATE: + rule->options.set_state.new_state = + qemu_opt_get_number(opts, "new_state", 0); + break; + }; + + /* Add the rule */ + QLIST_INSERT_HEAD(&s->rules[event], rule, next); + + return 0; +} + +static int read_config(BDRVBlkdebugState *s, const char *filename) +{ + FILE *f; + int ret; + struct add_rule_data d; + + f = fopen(filename, "r"); + if (f == NULL) { + return -errno; + } + + ret = qemu_config_parse(f, config_groups, filename); + if (ret < 0) { + goto fail; + } + + d.s = s; + d.action = ACTION_INJECT_ERROR; + qemu_opts_foreach(&inject_error_opts, add_rule, &d, 0); + + d.action = ACTION_SET_STATE; + qemu_opts_foreach(&set_state_opts, add_rule, &d, 0); + + ret = 0; +fail: + qemu_opts_reset(&inject_error_opts); + qemu_opts_reset(&set_state_opts); + fclose(f); + return ret; +} + +/* Valid blkdebug filenames look like blkdebug:path/to/config:path/to/image */ +static int blkdebug_open(BlockDriverState *bs, const char *filename, int flags) +{ + BDRVBlkdebugState *s = bs->opaque; + int ret; + char *config, *c; + + /* Parse the blkdebug: prefix */ + if (strncmp(filename, "blkdebug:", strlen("blkdebug:"))) { + return -EINVAL; + } + filename += strlen("blkdebug:"); + + /* Read rules from config file */ + c = strchr(filename, ':'); + if (c == NULL) { + return -EINVAL; + } + + config = strdup(filename); + config[c - filename] = '\0'; + ret = read_config(s, config); + free(config); + if (ret < 0) { + return ret; + } + filename = c + 1; + + /* Set initial state */ + s->vars.state = 1; + + /* Open the backing file */ + ret = bdrv_file_open(&bs->file, filename, flags); + if (ret < 0) { + return ret; + } + + return 0; +} + +static void error_callback_bh(void *opaque) +{ + struct BlkdebugAIOCB *acb = opaque; + qemu_bh_delete(acb->bh); + acb->common.cb(acb->common.opaque, acb->ret); + qemu_aio_release(acb); +} + +static void blkdebug_aio_cancel(BlockDriverAIOCB *blockacb) +{ + BlkdebugAIOCB *acb = container_of(blockacb, BlkdebugAIOCB, common); + qemu_aio_release(acb); +} + +static BlockDriverAIOCB *inject_error(BlockDriverState *bs, + BlockDriverCompletionFunc *cb, void *opaque) +{ + BDRVBlkdebugState *s = bs->opaque; + int error = s->vars.inject_errno; + struct BlkdebugAIOCB *acb; + QEMUBH *bh; + + if (s->vars.inject_once) { + s->vars.inject_errno = 0; + } + + if (s->vars.inject_immediately) { + return NULL; + } + + acb = qemu_aio_get(&blkdebug_aio_pool, bs, cb, opaque); + acb->ret = -error; + + bh = qemu_bh_new(error_callback_bh, acb); + acb->bh = bh; + qemu_bh_schedule(bh); + + return &acb->common; +} + +static BlockDriverAIOCB *blkdebug_aio_readv(BlockDriverState *bs, + int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, + BlockDriverCompletionFunc *cb, void *opaque) +{ + BDRVBlkdebugState *s = bs->opaque; + + if (s->vars.inject_errno) { + return inject_error(bs, cb, opaque); + } + + BlockDriverAIOCB *acb = + bdrv_aio_readv(bs->file, sector_num, qiov, nb_sectors, cb, opaque); + return acb; +} + +static BlockDriverAIOCB *blkdebug_aio_writev(BlockDriverState *bs, + int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, + BlockDriverCompletionFunc *cb, void *opaque) +{ + BDRVBlkdebugState *s = bs->opaque; + + if (s->vars.inject_errno) { + return inject_error(bs, cb, opaque); + } + + BlockDriverAIOCB *acb = + bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors, cb, opaque); + return acb; +} + +static void blkdebug_close(BlockDriverState *bs) +{ + BDRVBlkdebugState *s = bs->opaque; + BlkdebugRule *rule, *next; + int i; + + for (i = 0; i < BLKDBG_EVENT_MAX; i++) { + QLIST_FOREACH_SAFE(rule, &s->rules[i], next, next) { + QLIST_REMOVE(rule, next); + qemu_free(rule); + } + } +} + +static int blkdebug_flush(BlockDriverState *bs) +{ + return bdrv_flush(bs->file); +} + +static BlockDriverAIOCB *blkdebug_aio_flush(BlockDriverState *bs, + BlockDriverCompletionFunc *cb, void *opaque) +{ + return bdrv_aio_flush(bs->file, cb, opaque); +} + +static void process_rule(BlockDriverState *bs, struct BlkdebugRule *rule, + BlkdebugVars *old_vars) +{ + BDRVBlkdebugState *s = bs->opaque; + BlkdebugVars *vars = &s->vars; + + /* Only process rules for the current state */ + if (rule->state && rule->state != old_vars->state) { + return; + } + + /* Take the action */ + switch (rule->action) { + case ACTION_INJECT_ERROR: + vars->inject_errno = rule->options.inject.error; + vars->inject_once = rule->options.inject.once; + vars->inject_immediately = rule->options.inject.immediately; + break; + + case ACTION_SET_STATE: + vars->state = rule->options.set_state.new_state; + break; + } +} + +static void blkdebug_debug_event(BlockDriverState *bs, BlkDebugEvent event) +{ + BDRVBlkdebugState *s = bs->opaque; + struct BlkdebugRule *rule; + BlkdebugVars old_vars = s->vars; + + assert((int)event >= 0 && event < BLKDBG_EVENT_MAX); + + QLIST_FOREACH(rule, &s->rules[event], next) { + process_rule(bs, rule, &old_vars); + } +} + +static BlockDriver bdrv_blkdebug = { + .format_name = "blkdebug", + .protocol_name = "blkdebug", + + .instance_size = sizeof(BDRVBlkdebugState), + + .bdrv_file_open = blkdebug_open, + .bdrv_close = blkdebug_close, + .bdrv_flush = blkdebug_flush, + + .bdrv_aio_readv = blkdebug_aio_readv, + .bdrv_aio_writev = blkdebug_aio_writev, + .bdrv_aio_flush = blkdebug_aio_flush, + + .bdrv_debug_event = blkdebug_debug_event, +}; + +static void bdrv_blkdebug_init(void) +{ + bdrv_register(&bdrv_blkdebug); +} + +block_init(bdrv_blkdebug_init); diff -Nru qemu-kvm-0.12.5+noroms/block/blkverify.c qemu-kvm-0.14.1/block/blkverify.c --- qemu-kvm-0.12.5+noroms/block/blkverify.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/block/blkverify.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,383 @@ +/* + * Block protocol for block driver correctness testing + * + * Copyright (C) 2010 IBM, Corp. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include +#include "qemu_socket.h" /* for EINPROGRESS on Windows */ +#include "block_int.h" + +typedef struct { + BlockDriverState *test_file; +} BDRVBlkverifyState; + +typedef struct BlkverifyAIOCB BlkverifyAIOCB; +struct BlkverifyAIOCB { + BlockDriverAIOCB common; + QEMUBH *bh; + + /* Request metadata */ + bool is_write; + int64_t sector_num; + int nb_sectors; + + int ret; /* first completed request's result */ + unsigned int done; /* completion counter */ + bool *finished; /* completion signal for cancel */ + + QEMUIOVector *qiov; /* user I/O vector */ + QEMUIOVector raw_qiov; /* cloned I/O vector for raw file */ + void *buf; /* buffer for raw file I/O */ + + void (*verify)(BlkverifyAIOCB *acb); +}; + +static void blkverify_aio_cancel(BlockDriverAIOCB *blockacb) +{ + BlkverifyAIOCB *acb = (BlkverifyAIOCB *)blockacb; + bool finished = false; + + /* Wait until request completes, invokes its callback, and frees itself */ + acb->finished = &finished; + while (!finished) { + qemu_aio_wait(); + } +} + +static AIOPool blkverify_aio_pool = { + .aiocb_size = sizeof(BlkverifyAIOCB), + .cancel = blkverify_aio_cancel, +}; + +static void GCC_FMT_ATTR(2, 3) blkverify_err(BlkverifyAIOCB *acb, + const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + fprintf(stderr, "blkverify: %s sector_num=%" PRId64 " nb_sectors=%d ", + acb->is_write ? "write" : "read", acb->sector_num, + acb->nb_sectors); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); + exit(1); +} + +/* Valid blkverify filenames look like blkverify:path/to/raw_image:path/to/image */ +static int blkverify_open(BlockDriverState *bs, const char *filename, int flags) +{ + BDRVBlkverifyState *s = bs->opaque; + int ret; + char *raw, *c; + + /* Parse the blkverify: prefix */ + if (strncmp(filename, "blkverify:", strlen("blkverify:"))) { + return -EINVAL; + } + filename += strlen("blkverify:"); + + /* Parse the raw image filename */ + c = strchr(filename, ':'); + if (c == NULL) { + return -EINVAL; + } + + raw = strdup(filename); + raw[c - filename] = '\0'; + ret = bdrv_file_open(&bs->file, raw, flags); + free(raw); + if (ret < 0) { + return ret; + } + filename = c + 1; + + /* Open the test file */ + s->test_file = bdrv_new(""); + ret = bdrv_open(s->test_file, filename, flags, NULL); + if (ret < 0) { + bdrv_delete(s->test_file); + s->test_file = NULL; + return ret; + } + + return 0; +} + +static void blkverify_close(BlockDriverState *bs) +{ + BDRVBlkverifyState *s = bs->opaque; + + bdrv_delete(s->test_file); + s->test_file = NULL; +} + +static int blkverify_flush(BlockDriverState *bs) +{ + BDRVBlkverifyState *s = bs->opaque; + + /* Only flush test file, the raw file is not important */ + return bdrv_flush(s->test_file); +} + +static int64_t blkverify_getlength(BlockDriverState *bs) +{ + BDRVBlkverifyState *s = bs->opaque; + + return bdrv_getlength(s->test_file); +} + +/** + * Check that I/O vector contents are identical + * + * @a: I/O vector + * @b: I/O vector + * @ret: Offset to first mismatching byte or -1 if match + */ +static ssize_t blkverify_iovec_compare(QEMUIOVector *a, QEMUIOVector *b) +{ + int i; + ssize_t offset = 0; + + assert(a->niov == b->niov); + for (i = 0; i < a->niov; i++) { + size_t len = 0; + uint8_t *p = (uint8_t *)a->iov[i].iov_base; + uint8_t *q = (uint8_t *)b->iov[i].iov_base; + + assert(a->iov[i].iov_len == b->iov[i].iov_len); + while (len < a->iov[i].iov_len && *p++ == *q++) { + len++; + } + + offset += len; + + if (len != a->iov[i].iov_len) { + return offset; + } + } + return -1; +} + +typedef struct { + int src_index; + struct iovec *src_iov; + void *dest_base; +} IOVectorSortElem; + +static int sortelem_cmp_src_base(const void *a, const void *b) +{ + const IOVectorSortElem *elem_a = a; + const IOVectorSortElem *elem_b = b; + + /* Don't overflow */ + if (elem_a->src_iov->iov_base < elem_b->src_iov->iov_base) { + return -1; + } else if (elem_a->src_iov->iov_base > elem_b->src_iov->iov_base) { + return 1; + } else { + return 0; + } +} + +static int sortelem_cmp_src_index(const void *a, const void *b) +{ + const IOVectorSortElem *elem_a = a; + const IOVectorSortElem *elem_b = b; + + return elem_a->src_index - elem_b->src_index; +} + +/** + * Copy contents of I/O vector + * + * The relative relationships of overlapping iovecs are preserved. This is + * necessary to ensure identical semantics in the cloned I/O vector. + */ +static void blkverify_iovec_clone(QEMUIOVector *dest, const QEMUIOVector *src, + void *buf) +{ + IOVectorSortElem sortelems[src->niov]; + void *last_end; + int i; + + /* Sort by source iovecs by base address */ + for (i = 0; i < src->niov; i++) { + sortelems[i].src_index = i; + sortelems[i].src_iov = &src->iov[i]; + } + qsort(sortelems, src->niov, sizeof(sortelems[0]), sortelem_cmp_src_base); + + /* Allocate buffer space taking into account overlapping iovecs */ + last_end = NULL; + for (i = 0; i < src->niov; i++) { + struct iovec *cur = sortelems[i].src_iov; + ptrdiff_t rewind = 0; + + /* Detect overlap */ + if (last_end && last_end > cur->iov_base) { + rewind = last_end - cur->iov_base; + } + + sortelems[i].dest_base = buf - rewind; + buf += cur->iov_len - MIN(rewind, cur->iov_len); + last_end = MAX(cur->iov_base + cur->iov_len, last_end); + } + + /* Sort by source iovec index and build destination iovec */ + qsort(sortelems, src->niov, sizeof(sortelems[0]), sortelem_cmp_src_index); + for (i = 0; i < src->niov; i++) { + qemu_iovec_add(dest, sortelems[i].dest_base, src->iov[i].iov_len); + } +} + +static BlkverifyAIOCB *blkverify_aio_get(BlockDriverState *bs, bool is_write, + int64_t sector_num, QEMUIOVector *qiov, + int nb_sectors, + BlockDriverCompletionFunc *cb, + void *opaque) +{ + BlkverifyAIOCB *acb = qemu_aio_get(&blkverify_aio_pool, bs, cb, opaque); + + acb->bh = NULL; + acb->is_write = is_write; + acb->sector_num = sector_num; + acb->nb_sectors = nb_sectors; + acb->ret = -EINPROGRESS; + acb->done = 0; + acb->qiov = qiov; + acb->buf = NULL; + acb->verify = NULL; + acb->finished = NULL; + return acb; +} + +static void blkverify_aio_bh(void *opaque) +{ + BlkverifyAIOCB *acb = opaque; + + qemu_bh_delete(acb->bh); + if (acb->buf) { + qemu_iovec_destroy(&acb->raw_qiov); + qemu_vfree(acb->buf); + } + acb->common.cb(acb->common.opaque, acb->ret); + if (acb->finished) { + *acb->finished = true; + } + qemu_aio_release(acb); +} + +static void blkverify_aio_cb(void *opaque, int ret) +{ + BlkverifyAIOCB *acb = opaque; + + switch (++acb->done) { + case 1: + acb->ret = ret; + break; + + case 2: + if (acb->ret != ret) { + blkverify_err(acb, "return value mismatch %d != %d", acb->ret, ret); + } + + if (acb->verify) { + acb->verify(acb); + } + + acb->bh = qemu_bh_new(blkverify_aio_bh, acb); + qemu_bh_schedule(acb->bh); + break; + } +} + +static void blkverify_verify_readv(BlkverifyAIOCB *acb) +{ + ssize_t offset = blkverify_iovec_compare(acb->qiov, &acb->raw_qiov); + if (offset != -1) { + blkverify_err(acb, "contents mismatch in sector %" PRId64, + acb->sector_num + (int64_t)(offset / BDRV_SECTOR_SIZE)); + } +} + +static BlockDriverAIOCB *blkverify_aio_readv(BlockDriverState *bs, + int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, + BlockDriverCompletionFunc *cb, void *opaque) +{ + BDRVBlkverifyState *s = bs->opaque; + BlkverifyAIOCB *acb = blkverify_aio_get(bs, false, sector_num, qiov, + nb_sectors, cb, opaque); + + acb->verify = blkverify_verify_readv; + acb->buf = qemu_blockalign(bs->file, qiov->size); + qemu_iovec_init(&acb->raw_qiov, acb->qiov->niov); + blkverify_iovec_clone(&acb->raw_qiov, qiov, acb->buf); + + if (!bdrv_aio_readv(s->test_file, sector_num, qiov, nb_sectors, + blkverify_aio_cb, acb)) { + blkverify_aio_cb(acb, -EIO); + } + if (!bdrv_aio_readv(bs->file, sector_num, &acb->raw_qiov, nb_sectors, + blkverify_aio_cb, acb)) { + blkverify_aio_cb(acb, -EIO); + } + return &acb->common; +} + +static BlockDriverAIOCB *blkverify_aio_writev(BlockDriverState *bs, + int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, + BlockDriverCompletionFunc *cb, void *opaque) +{ + BDRVBlkverifyState *s = bs->opaque; + BlkverifyAIOCB *acb = blkverify_aio_get(bs, true, sector_num, qiov, + nb_sectors, cb, opaque); + + if (!bdrv_aio_writev(s->test_file, sector_num, qiov, nb_sectors, + blkverify_aio_cb, acb)) { + blkverify_aio_cb(acb, -EIO); + } + if (!bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors, + blkverify_aio_cb, acb)) { + blkverify_aio_cb(acb, -EIO); + } + return &acb->common; +} + +static BlockDriverAIOCB *blkverify_aio_flush(BlockDriverState *bs, + BlockDriverCompletionFunc *cb, + void *opaque) +{ + BDRVBlkverifyState *s = bs->opaque; + + /* Only flush test file, the raw file is not important */ + return bdrv_aio_flush(s->test_file, cb, opaque); +} + +static BlockDriver bdrv_blkverify = { + .format_name = "blkverify", + .protocol_name = "blkverify", + + .instance_size = sizeof(BDRVBlkverifyState), + + .bdrv_getlength = blkverify_getlength, + + .bdrv_file_open = blkverify_open, + .bdrv_close = blkverify_close, + .bdrv_flush = blkverify_flush, + + .bdrv_aio_readv = blkverify_aio_readv, + .bdrv_aio_writev = blkverify_aio_writev, + .bdrv_aio_flush = blkverify_aio_flush, +}; + +static void bdrv_blkverify_init(void) +{ + bdrv_register(&bdrv_blkverify); +} + +block_init(bdrv_blkverify_init); diff -Nru qemu-kvm-0.12.5+noroms/block/bochs.c qemu-kvm-0.14.1/block/bochs.c --- qemu-kvm-0.12.5+noroms/block/bochs.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/block/bochs.c 2011-05-11 13:29:46.000000000 +0000 @@ -80,8 +80,6 @@ }; typedef struct BDRVBochsState { - int fd; - uint32_t *catalog_bitmap; int catalog_size; @@ -109,25 +107,16 @@ return 0; } -static int bochs_open(BlockDriverState *bs, const char *filename, int flags) +static int bochs_open(BlockDriverState *bs, int flags) { BDRVBochsState *s = bs->opaque; - int fd, i; + int i; struct bochs_header bochs; struct bochs_header_v1 header_v1; - fd = open(filename, O_RDWR | O_BINARY); - if (fd < 0) { - fd = open(filename, O_RDONLY | O_BINARY); - if (fd < 0) - return -1; - } - bs->read_only = 1; // no write support yet - s->fd = fd; - - if (read(fd, &bochs, sizeof(bochs)) != sizeof(bochs)) { + if (bdrv_pread(bs->file, 0, &bochs, sizeof(bochs)) != sizeof(bochs)) { goto fail; } @@ -146,12 +135,10 @@ bs->total_sectors = le64_to_cpu(bochs.extra.redolog.disk) / 512; } - lseek(s->fd, le32_to_cpu(bochs.header), SEEK_SET); - s->catalog_size = le32_to_cpu(bochs.extra.redolog.catalog); s->catalog_bitmap = qemu_malloc(s->catalog_size * 4); - if (read(s->fd, s->catalog_bitmap, s->catalog_size * 4) != - s->catalog_size * 4) + if (bdrv_pread(bs->file, le32_to_cpu(bochs.header), s->catalog_bitmap, + s->catalog_size * 4) != s->catalog_size * 4) goto fail; for (i = 0; i < s->catalog_size; i++) le32_to_cpus(&s->catalog_bitmap[i]); @@ -165,68 +152,53 @@ return 0; fail: - close(fd); return -1; } -static inline int seek_to_sector(BlockDriverState *bs, int64_t sector_num) +static int64_t seek_to_sector(BlockDriverState *bs, int64_t sector_num) { BDRVBochsState *s = bs->opaque; int64_t offset = sector_num * 512; - int64_t extent_index, extent_offset, bitmap_offset, block_offset; + int64_t extent_index, extent_offset, bitmap_offset; char bitmap_entry; // seek to sector extent_index = offset / s->extent_size; extent_offset = (offset % s->extent_size) / 512; - if (s->catalog_bitmap[extent_index] == 0xffffffff) - { -// fprintf(stderr, "page not allocated [%x - %x:%x]\n", -// sector_num, extent_index, extent_offset); - return -1; // not allocated + if (s->catalog_bitmap[extent_index] == 0xffffffff) { + return -1; /* not allocated */ } bitmap_offset = s->data_offset + (512 * s->catalog_bitmap[extent_index] * (s->extent_blocks + s->bitmap_blocks)); - block_offset = bitmap_offset + (512 * (s->bitmap_blocks + extent_offset)); - -// fprintf(stderr, "sect: %x [ext i: %x o: %x] -> %x bitmap: %x block: %x\n", -// sector_num, extent_index, extent_offset, -// le32_to_cpu(s->catalog_bitmap[extent_index]), -// bitmap_offset, block_offset); - // read in bitmap for current extent - lseek(s->fd, bitmap_offset + (extent_offset / 8), SEEK_SET); - - read(s->fd, &bitmap_entry, 1); - - if (!((bitmap_entry >> (extent_offset % 8)) & 1)) - { -// fprintf(stderr, "sector (%x) in bitmap not allocated\n", -// sector_num); - return -1; // not allocated + /* read in bitmap for current extent */ + if (bdrv_pread(bs->file, bitmap_offset + (extent_offset / 8), + &bitmap_entry, 1) != 1) { + return -1; } - lseek(s->fd, block_offset, SEEK_SET); + if (!((bitmap_entry >> (extent_offset % 8)) & 1)) { + return -1; /* not allocated */ + } - return 0; + return bitmap_offset + (512 * (s->bitmap_blocks + extent_offset)); } static int bochs_read(BlockDriverState *bs, int64_t sector_num, uint8_t *buf, int nb_sectors) { - BDRVBochsState *s = bs->opaque; int ret; while (nb_sectors > 0) { - if (!seek_to_sector(bs, sector_num)) - { - ret = read(s->fd, buf, 512); - if (ret != 512) - return -1; - } - else + int64_t block_offset = seek_to_sector(bs, sector_num); + if (block_offset >= 0) { + ret = bdrv_pread(bs->file, block_offset, buf, 512); + if (ret != 512) { + return -1; + } + } else memset(buf, 0, 512); nb_sectors--; sector_num++; @@ -239,7 +211,6 @@ { BDRVBochsState *s = bs->opaque; qemu_free(s->catalog_bitmap); - close(s->fd); } static BlockDriver bdrv_bochs = { diff -Nru qemu-kvm-0.12.5+noroms/block/cloop.c qemu-kvm-0.14.1/block/cloop.c --- qemu-kvm-0.12.5+noroms/block/cloop.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/block/cloop.c 2011-05-11 13:29:46.000000000 +0000 @@ -27,7 +27,6 @@ #include typedef struct BDRVCloopState { - int fd; uint32_t block_size; uint32_t n_blocks; uint64_t* offsets; @@ -51,34 +50,31 @@ return 0; } -static int cloop_open(BlockDriverState *bs, const char *filename, int flags) +static int cloop_open(BlockDriverState *bs, int flags) { BDRVCloopState *s = bs->opaque; uint32_t offsets_size,max_compressed_block_size=1,i; - s->fd = open(filename, O_RDONLY | O_BINARY); - if (s->fd < 0) - return -errno; bs->read_only = 1; /* read header */ - if(lseek(s->fd,128,SEEK_SET)<0) { -cloop_close: - close(s->fd); - return -1; + if (bdrv_pread(bs->file, 128, &s->block_size, 4) < 4) { + goto cloop_close; } - if(read(s->fd,&s->block_size,4)<4) - goto cloop_close; - s->block_size=be32_to_cpu(s->block_size); - if(read(s->fd,&s->n_blocks,4)<4) - goto cloop_close; - s->n_blocks=be32_to_cpu(s->n_blocks); + s->block_size = be32_to_cpu(s->block_size); + + if (bdrv_pread(bs->file, 128 + 4, &s->n_blocks, 4) < 4) { + goto cloop_close; + } + s->n_blocks = be32_to_cpu(s->n_blocks); /* read offsets */ - offsets_size=s->n_blocks*sizeof(uint64_t); - s->offsets=(uint64_t*)qemu_malloc(offsets_size); - if(read(s->fd,s->offsets,offsets_size)n_blocks * sizeof(uint64_t); + s->offsets = qemu_malloc(offsets_size); + if (bdrv_pread(bs->file, 128 + 4 + 4, s->offsets, offsets_size) < + offsets_size) { goto cloop_close; + } for(i=0;in_blocks;i++) { s->offsets[i]=be64_to_cpu(s->offsets[i]); if(i>0) { @@ -98,16 +94,21 @@ s->sectors_per_block = s->block_size/512; bs->total_sectors = s->n_blocks*s->sectors_per_block; return 0; + +cloop_close: + return -1; } -static inline int cloop_read_block(BDRVCloopState *s,int block_num) +static inline int cloop_read_block(BlockDriverState *bs, int block_num) { + BDRVCloopState *s = bs->opaque; + if(s->current_block != block_num) { int ret; uint32_t bytes = s->offsets[block_num+1]-s->offsets[block_num]; - lseek(s->fd, s->offsets[block_num], SEEK_SET); - ret = read(s->fd, s->compressed_block, bytes); + ret = bdrv_pread(bs->file, s->offsets[block_num], s->compressed_block, + bytes); if (ret != bytes) return -1; @@ -136,7 +137,7 @@ for(i=0;isectors_per_block), block_num=(sector_num+i)/s->sectors_per_block; - if(cloop_read_block(s, block_num) != 0) + if(cloop_read_block(bs, block_num) != 0) return -1; memcpy(buf+i*512,s->uncompressed_block+sector_offset_in_block*512,512); } @@ -146,7 +147,6 @@ static void cloop_close(BlockDriverState *bs) { BDRVCloopState *s = bs->opaque; - close(s->fd); if(s->n_blocks>0) free(s->offsets); free(s->compressed_block); diff -Nru qemu-kvm-0.12.5+noroms/block/cow.c qemu-kvm-0.14.1/block/cow.c --- qemu-kvm-0.12.5+noroms/block/cow.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/block/cow.c 2011-05-11 13:29:46.000000000 +0000 @@ -21,11 +21,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#ifndef _WIN32 #include "qemu-common.h" #include "block_int.h" #include "module.h" -#include /**************************************************************/ /* COW block driver using file system holes */ @@ -44,10 +42,6 @@ }; typedef struct BDRVCowState { - int fd; - uint8_t *cow_bitmap; /* if non NULL, COW mappings are used first */ - uint8_t *cow_bitmap_addr; /* mmap address of cow_bitmap */ - int cow_bitmap_size; int64_t cow_sectors_offset; } BDRVCowState; @@ -63,22 +57,16 @@ return 0; } -static int cow_open(BlockDriverState *bs, const char *filename, int flags) +static int cow_open(BlockDriverState *bs, int flags) { BDRVCowState *s = bs->opaque; - int fd; struct cow_header_v2 cow_header; + int bitmap_size; int64_t size; - fd = open(filename, O_RDWR | O_BINARY | O_LARGEFILE); - if (fd < 0) { - fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE); - if (fd < 0) - return -1; - } - s->fd = fd; /* see if it is a cow image */ - if (read(fd, &cow_header, sizeof(cow_header)) != sizeof(cow_header)) { + if (bdrv_pread(bs->file, 0, &cow_header, sizeof(cow_header)) != + sizeof(cow_header)) { goto fail; } @@ -94,61 +82,91 @@ pstrcpy(bs->backing_file, sizeof(bs->backing_file), cow_header.backing_file); - /* mmap the bitmap */ - s->cow_bitmap_size = ((bs->total_sectors + 7) >> 3) + sizeof(cow_header); - s->cow_bitmap_addr = (void *)mmap(get_mmap_addr(s->cow_bitmap_size), - s->cow_bitmap_size, - PROT_READ | PROT_WRITE, - MAP_SHARED, s->fd, 0); - if (s->cow_bitmap_addr == MAP_FAILED) - goto fail; - s->cow_bitmap = s->cow_bitmap_addr + sizeof(cow_header); - s->cow_sectors_offset = (s->cow_bitmap_size + 511) & ~511; + bitmap_size = ((bs->total_sectors + 7) >> 3) + sizeof(cow_header); + s->cow_sectors_offset = (bitmap_size + 511) & ~511; return 0; fail: - close(fd); return -1; } -static inline void cow_set_bit(uint8_t *bitmap, int64_t bitnum) +/* + * XXX(hch): right now these functions are extremly ineffcient. + * We should just read the whole bitmap we'll need in one go instead. + */ +static inline int cow_set_bit(BlockDriverState *bs, int64_t bitnum) { - bitmap[bitnum / 8] |= (1 << (bitnum%8)); + uint64_t offset = sizeof(struct cow_header_v2) + bitnum / 8; + uint8_t bitmap; + int ret; + + ret = bdrv_pread(bs->file, offset, &bitmap, sizeof(bitmap)); + if (ret < 0) { + return ret; + } + + bitmap |= (1 << (bitnum % 8)); + + ret = bdrv_pwrite_sync(bs->file, offset, &bitmap, sizeof(bitmap)); + if (ret < 0) { + return ret; + } + return 0; } -static inline int is_bit_set(const uint8_t *bitmap, int64_t bitnum) +static inline int is_bit_set(BlockDriverState *bs, int64_t bitnum) { - return !!(bitmap[bitnum / 8] & (1 << (bitnum%8))); -} + uint64_t offset = sizeof(struct cow_header_v2) + bitnum / 8; + uint8_t bitmap; + int ret; + + ret = bdrv_pread(bs->file, offset, &bitmap, sizeof(bitmap)); + if (ret < 0) { + return ret; + } + return !!(bitmap & (1 << (bitnum % 8))); +} /* Return true if first block has been changed (ie. current version is * in COW file). Set the number of continuous blocks for which that * is true. */ -static inline int is_changed(uint8_t *bitmap, - int64_t sector_num, int nb_sectors, - int *num_same) +static int cow_is_allocated(BlockDriverState *bs, int64_t sector_num, + int nb_sectors, int *num_same) { int changed; - if (!bitmap || nb_sectors == 0) { + if (nb_sectors == 0) { *num_same = nb_sectors; return 0; } - changed = is_bit_set(bitmap, sector_num); + changed = is_bit_set(bs, sector_num); + if (changed < 0) { + return 0; /* XXX: how to return I/O errors? */ + } + for (*num_same = 1; *num_same < nb_sectors; (*num_same)++) { - if (is_bit_set(bitmap, sector_num + *num_same) != changed) + if (is_bit_set(bs, sector_num + *num_same) != changed) break; } return changed; } -static int cow_is_allocated(BlockDriverState *bs, int64_t sector_num, - int nb_sectors, int *pnum) +static int cow_update_bitmap(BlockDriverState *bs, int64_t sector_num, + int nb_sectors) { - BDRVCowState *s = bs->opaque; - return is_changed(s->cow_bitmap, sector_num, nb_sectors, pnum); + int error = 0; + int i; + + for (i = 0; i < nb_sectors; i++) { + error = cow_set_bit(bs, sector_num + i); + if (error) { + break; + } + } + + return error; } static int cow_read(BlockDriverState *bs, int64_t sector_num, @@ -158,9 +176,10 @@ int ret, n; while (nb_sectors > 0) { - if (is_changed(s->cow_bitmap, sector_num, nb_sectors, &n)) { - lseek(s->fd, s->cow_sectors_offset + sector_num * 512, SEEK_SET); - ret = read(s->fd, buf, n * 512); + if (cow_is_allocated(bs, sector_num, nb_sectors, &n)) { + ret = bdrv_pread(bs->file, + s->cow_sectors_offset + sector_num * 512, + buf, n * 512); if (ret != n * 512) return -1; } else { @@ -184,22 +203,18 @@ const uint8_t *buf, int nb_sectors) { BDRVCowState *s = bs->opaque; - int ret, i; + int ret; - lseek(s->fd, s->cow_sectors_offset + sector_num * 512, SEEK_SET); - ret = write(s->fd, buf, nb_sectors * 512); + ret = bdrv_pwrite(bs->file, s->cow_sectors_offset + sector_num * 512, + buf, nb_sectors * 512); if (ret != nb_sectors * 512) return -1; - for (i = 0; i < nb_sectors; i++) - cow_set_bit(s->cow_bitmap, sector_num + i); - return 0; + + return cow_update_bitmap(bs, sector_num, nb_sectors); } static void cow_close(BlockDriverState *bs) { - BDRVCowState *s = bs->opaque; - munmap((void *)s->cow_bitmap_addr, s->cow_bitmap_size); - close(s->fd); } static int cow_create(const char *filename, QEMUOptionParameter *options) @@ -209,6 +224,7 @@ struct stat st; int64_t image_sectors = 0; const char *image_filename = NULL; + int ret; /* Read out options */ while (options && options->name) { @@ -223,7 +239,7 @@ cow_fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644); if (cow_fd < 0) - return -1; + return -errno; memset(&cow_header, 0, sizeof(cow_header)); cow_header.magic = cpu_to_be32(COW_MAGIC); cow_header.version = cpu_to_be32(COW_VERSION); @@ -248,17 +264,27 @@ } cow_header.sectorsize = cpu_to_be32(512); cow_header.size = cpu_to_be64(image_sectors * 512); - write(cow_fd, &cow_header, sizeof(cow_header)); + ret = qemu_write_full(cow_fd, &cow_header, sizeof(cow_header)); + if (ret != sizeof(cow_header)) { + ret = -errno; + goto exit; + } + /* resize to include at least all the bitmap */ - ftruncate(cow_fd, sizeof(cow_header) + ((image_sectors + 7) >> 3)); + ret = ftruncate(cow_fd, sizeof(cow_header) + ((image_sectors + 7) >> 3)); + if (ret) { + ret = -errno; + goto exit; + } + +exit: close(cow_fd); - return 0; + return ret; } -static void cow_flush(BlockDriverState *bs) +static int cow_flush(BlockDriverState *bs) { - BDRVCowState *s = bs->opaque; - qemu_fdatasync(s->fd); + return bdrv_flush(bs->file); } static QEMUOptionParameter cow_create_options[] = { @@ -296,4 +322,3 @@ } block_init(bdrv_cow_init); -#endif diff -Nru qemu-kvm-0.12.5+noroms/block/curl.c qemu-kvm-0.14.1/block/curl.c --- qemu-kvm-0.12.5+noroms/block/curl.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/block/curl.c 2011-05-11 13:29:46.000000000 +0000 @@ -29,9 +29,9 @@ // #define DEBUG_VERBOSE #ifdef DEBUG_CURL -#define dprintf(fmt, ...) do { printf(fmt, ## __VA_ARGS__); } while (0) +#define DPRINTF(fmt, ...) do { printf(fmt, ## __VA_ARGS__); } while (0) #else -#define dprintf(fmt, ...) do { } while (0) +#define DPRINTF(fmt, ...) do { } while (0) #endif #define CURL_NUM_STATES 8 @@ -80,7 +80,7 @@ static int curl_sock_cb(CURL *curl, curl_socket_t fd, int action, void *s, void *sp) { - dprintf("CURL (AIO): Sock action %d on fd %d\n", action, fd); + DPRINTF("CURL (AIO): Sock action %d on fd %d\n", action, fd); switch (action) { case CURL_POLL_IN: qemu_aio_set_fd_handler(fd, curl_multi_do, NULL, NULL, NULL, s); @@ -104,10 +104,11 @@ { CURLState *s = ((CURLState*)opaque); size_t realsize = size * nmemb; - long long fsize; + size_t fsize; - if(sscanf(ptr, "Content-Length: %lld", &fsize) == 1) + if(sscanf(ptr, "Content-Length: %zd", &fsize) == 1) { s->s->len = fsize; + } return realsize; } @@ -118,7 +119,7 @@ size_t realsize = size * nmemb; int i; - dprintf("CURL: Just reading %lld bytes\n", (unsigned long long)realsize); + DPRINTF("CURL: Just reading %zd bytes\n", realsize); if (!s || !s->orig_buf) goto read_end; @@ -339,7 +340,7 @@ } if ((s->readahead_size & 0x1ff) != 0) { - fprintf(stderr, "HTTP_READAHEAD_SIZE %Zd is not a multiple of 512\n", + fprintf(stderr, "HTTP_READAHEAD_SIZE %zd is not a multiple of 512\n", s->readahead_size); goto out_noclean; } @@ -349,7 +350,7 @@ inited = 1; } - dprintf("CURL: Opening %s\n", file); + DPRINTF("CURL: Opening %s\n", file); s->url = file; state = curl_init_state(s); if (!state) @@ -368,7 +369,7 @@ s->len = (size_t)d; else if(!s->len) goto out; - dprintf("CURL: Size = %lld\n", (long long)s->len); + DPRINTF("CURL: Size = %zd\n", s->len); curl_clean_state(state); curl_easy_cleanup(state->curl); @@ -450,8 +451,9 @@ state->orig_buf = qemu_malloc(state->buf_len); state->acb[0] = acb; - snprintf(state->range, 127, "%lld-%lld", (long long)start, (long long)end); - dprintf("CURL (AIO): Reading %d at %lld (%s)\n", (nb_sectors * SECTOR_SIZE), start, state->range); + snprintf(state->range, 127, "%zd-%zd", start, end); + DPRINTF("CURL (AIO): Reading %d at %zd (%s)\n", + (nb_sectors * SECTOR_SIZE), start, state->range); curl_easy_setopt(state->curl, CURLOPT_RANGE, state->range); curl_multi_add_handle(s->multi, state->curl); @@ -465,7 +467,7 @@ BDRVCURLState *s = bs->opaque; int i; - dprintf("CURL: Close\n"); + DPRINTF("CURL: Close\n"); for (i=0; istates[i].in_use) curl_clean_state(&s->states[i]); @@ -495,7 +497,7 @@ .protocol_name = "http", .instance_size = sizeof(BDRVCURLState), - .bdrv_open = curl_open, + .bdrv_file_open = curl_open, .bdrv_close = curl_close, .bdrv_getlength = curl_getlength, @@ -507,7 +509,7 @@ .protocol_name = "https", .instance_size = sizeof(BDRVCURLState), - .bdrv_open = curl_open, + .bdrv_file_open = curl_open, .bdrv_close = curl_close, .bdrv_getlength = curl_getlength, @@ -519,7 +521,7 @@ .protocol_name = "ftp", .instance_size = sizeof(BDRVCURLState), - .bdrv_open = curl_open, + .bdrv_file_open = curl_open, .bdrv_close = curl_close, .bdrv_getlength = curl_getlength, @@ -531,7 +533,7 @@ .protocol_name = "ftps", .instance_size = sizeof(BDRVCURLState), - .bdrv_open = curl_open, + .bdrv_file_open = curl_open, .bdrv_close = curl_close, .bdrv_getlength = curl_getlength, @@ -543,7 +545,7 @@ .protocol_name = "tftp", .instance_size = sizeof(BDRVCURLState), - .bdrv_open = curl_open, + .bdrv_file_open = curl_open, .bdrv_close = curl_close, .bdrv_getlength = curl_getlength, diff -Nru qemu-kvm-0.12.5+noroms/block/dmg.c qemu-kvm-0.14.1/block/dmg.c --- qemu-kvm-0.12.5+noroms/block/dmg.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/block/dmg.c 2011-05-11 13:29:46.000000000 +0000 @@ -28,8 +28,6 @@ #include typedef struct BDRVDMGState { - int fd; - /* each chunk contains a certain number of sectors, * offsets[i] is the offset in the .dmg file, * lengths[i] is the length of the compressed chunk, @@ -58,69 +56,75 @@ return 0; } -static off_t read_off(int fd) +static off_t read_off(BlockDriverState *bs, int64_t offset) { uint64_t buffer; - if(read(fd,&buffer,8)<8) + if (bdrv_pread(bs->file, offset, &buffer, 8) < 8) return 0; return be64_to_cpu(buffer); } -static off_t read_uint32(int fd) +static off_t read_uint32(BlockDriverState *bs, int64_t offset) { uint32_t buffer; - if(read(fd,&buffer,4)<4) + if (bdrv_pread(bs->file, offset, &buffer, 4) < 4) return 0; return be32_to_cpu(buffer); } -static int dmg_open(BlockDriverState *bs, const char *filename, int flags) +static int dmg_open(BlockDriverState *bs, int flags) { BDRVDMGState *s = bs->opaque; off_t info_begin,info_end,last_in_offset,last_out_offset; uint32_t count; uint32_t max_compressed_size=1,max_sectors_per_chunk=1,i; + int64_t offset; - s->fd = open(filename, O_RDONLY | O_BINARY); - if (s->fd < 0) - return -errno; bs->read_only = 1; s->n_chunks = 0; s->offsets = s->lengths = s->sectors = s->sectorcounts = NULL; /* read offset of info blocks */ - if(lseek(s->fd,-0x1d8,SEEK_END)<0) { + offset = bdrv_getlength(bs->file); + if (offset < 0) { goto fail; } + offset -= 0x1d8; - info_begin=read_off(s->fd); - if(info_begin==0) - goto fail; - if(lseek(s->fd,info_begin,SEEK_SET)<0) - goto fail; - if(read_uint32(s->fd)!=0x100) - goto fail; - if((count = read_uint32(s->fd))==0) - goto fail; - info_end = info_begin+count; - if(lseek(s->fd,0xf8,SEEK_CUR)<0) + info_begin = read_off(bs, offset); + if (info_begin == 0) { goto fail; + } + + if (read_uint32(bs, info_begin) != 0x100) { + goto fail; + } + + count = read_uint32(bs, info_begin + 4); + if (count == 0) { + goto fail; + } + info_end = info_begin + count; + + offset = info_begin + 0x100; /* read offsets */ last_in_offset = last_out_offset = 0; - while(lseek(s->fd,0,SEEK_CUR)fd); + count = read_uint32(bs, offset); if(count==0) goto fail; - type = read_uint32(s->fd); - if(type!=0x6d697368 || count<244) - lseek(s->fd,count-4,SEEK_CUR); - else { + offset += 4; + + type = read_uint32(bs, offset); + if (type == 0x6d697368 && count >= 244) { int new_size, chunk_count; - if(lseek(s->fd,200,SEEK_CUR)<0) - goto fail; + + offset += 4; + offset += 200; + chunk_count = (count-204)/40; new_size = sizeof(uint64_t) * (s->n_chunks + chunk_count); s->types = qemu_realloc(s->types, new_size/2); @@ -130,7 +134,8 @@ s->sectorcounts = qemu_realloc(s->sectorcounts, new_size); for(i=s->n_chunks;in_chunks+chunk_count;i++) { - s->types[i] = read_uint32(s->fd); + s->types[i] = read_uint32(bs, offset); + offset += 4; if(s->types[i]!=0x80000005 && s->types[i]!=1 && s->types[i]!=2) { if(s->types[i]==0xffffffff) { last_in_offset = s->offsets[i-1]+s->lengths[i-1]; @@ -138,15 +143,23 @@ } chunk_count--; i--; - if(lseek(s->fd,36,SEEK_CUR)<0) - goto fail; + offset += 36; continue; } - read_uint32(s->fd); - s->sectors[i] = last_out_offset+read_off(s->fd); - s->sectorcounts[i] = read_off(s->fd); - s->offsets[i] = last_in_offset+read_off(s->fd); - s->lengths[i] = read_off(s->fd); + offset += 4; + + s->sectors[i] = last_out_offset+read_off(bs, offset); + offset += 8; + + s->sectorcounts[i] = read_off(bs, offset); + offset += 8; + + s->offsets[i] = last_in_offset+read_off(bs, offset); + offset += 8; + + s->lengths[i] = read_off(bs, offset); + offset += 8; + if(s->lengths[i]>max_compressed_size) max_compressed_size = s->lengths[i]; if(s->sectorcounts[i]>max_sectors_per_chunk) @@ -166,7 +179,6 @@ return 0; fail: - close(s->fd); return -1; } @@ -196,8 +208,10 @@ return s->n_chunks; /* error */ } -static inline int dmg_read_chunk(BDRVDMGState *s,int sector_num) +static inline int dmg_read_chunk(BlockDriverState *bs, int sector_num) { + BDRVDMGState *s = bs->opaque; + if(!is_sector_in_chunk(s,s->current_chunk,sector_num)) { int ret; uint32_t chunk = search_chunk(s,sector_num); @@ -210,15 +224,12 @@ case 0x80000005: { /* zlib compressed */ int i; - ret = lseek(s->fd, s->offsets[chunk], SEEK_SET); - if(ret<0) - return -1; - /* we need to buffer, because only the chunk as whole can be * inflated. */ i=0; do { - ret = read(s->fd, s->compressed_chunk+i, s->lengths[chunk]-i); + ret = bdrv_pread(bs->file, s->offsets[chunk] + i, + s->compressed_chunk+i, s->lengths[chunk]-i); if(ret<0 && errno==EINTR) ret=0; i+=ret; @@ -239,7 +250,8 @@ return -1; break; } case 1: /* copy */ - ret = read(s->fd, s->uncompressed_chunk, s->lengths[chunk]); + ret = bdrv_pread(bs->file, s->offsets[chunk], + s->uncompressed_chunk, s->lengths[chunk]); if (ret != s->lengths[chunk]) return -1; break; @@ -260,7 +272,7 @@ for(i=0;isectors[s->current_chunk]; memcpy(buf+i*512,s->uncompressed_chunk+sector_offset_in_chunk*512,512); @@ -271,7 +283,6 @@ static void dmg_close(BlockDriverState *bs) { BDRVDMGState *s = bs->opaque; - close(s->fd); if(s->n_chunks>0) { free(s->types); free(s->offsets); diff -Nru qemu-kvm-0.12.5+noroms/block/nbd.c qemu-kvm-0.14.1/block/nbd.c --- qemu-kvm-0.12.5+noroms/block/nbd.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/block/nbd.c 2011-05-11 13:29:46.000000000 +0000 @@ -33,6 +33,8 @@ #include #include +#define EN_OPTSTR ":exportname=" + typedef struct BDRVNBDState { int sock; off_t size; @@ -42,58 +44,81 @@ static int nbd_open(BlockDriverState *bs, const char* filename, int flags) { BDRVNBDState *s = bs->opaque; + uint32_t nbdflags; + + char *file; + char *name; const char *host; const char *unixpath; int sock; off_t size; size_t blocksize; int ret; + int err = -EINVAL; + + file = qemu_strdup(filename); - if ((flags & BDRV_O_CREAT)) - return -EINVAL; + name = strstr(file, EN_OPTSTR); + if (name) { + if (name[strlen(EN_OPTSTR)] == 0) { + goto out; + } + name[0] = 0; + name += strlen(EN_OPTSTR); + } - if (!strstart(filename, "nbd:", &host)) - return -EINVAL; + if (!strstart(file, "nbd:", &host)) { + goto out; + } if (strstart(host, "unix:", &unixpath)) { - if (unixpath[0] != '/') - return -EINVAL; + if (unixpath[0] != '/') { + goto out; + } sock = unix_socket_outgoing(unixpath); } else { - uint16_t port; + uint16_t port = NBD_DEFAULT_PORT; char *p, *r; char hostname[128]; pstrcpy(hostname, 128, host); p = strchr(hostname, ':'); - if (p == NULL) - return -EINVAL; - - *p = '\0'; - p++; + if (p != NULL) { + *p = '\0'; + p++; + + port = strtol(p, &r, 0); + if (r == p) { + goto out; + } + } - port = strtol(p, &r, 0); - if (r == p) - return -EINVAL; sock = tcp_socket_outgoing(hostname, port); } - if (sock == -1) - return -errno; + if (sock == -1) { + err = -errno; + goto out; + } - ret = nbd_receive_negotiate(sock, &size, &blocksize); - if (ret == -1) - return -errno; + ret = nbd_receive_negotiate(sock, name, &nbdflags, &size, &blocksize); + if (ret == -1) { + err = -errno; + goto out; + } s->sock = sock; s->size = size; s->blocksize = blocksize; + err = 0; - return 0; +out: + qemu_free(file); + return err; } static int nbd_read(BlockDriverState *bs, int64_t sector_num, @@ -180,7 +205,7 @@ static BlockDriver bdrv_nbd = { .format_name = "nbd", .instance_size = sizeof(BDRVNBDState), - .bdrv_open = nbd_open, + .bdrv_file_open = nbd_open, .bdrv_read = nbd_read, .bdrv_write = nbd_write, .bdrv_close = nbd_close, diff -Nru qemu-kvm-0.12.5+noroms/block/parallels.c qemu-kvm-0.14.1/block/parallels.c --- qemu-kvm-0.12.5+noroms/block/parallels.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/block/parallels.c 2011-05-11 13:29:46.000000000 +0000 @@ -46,7 +46,6 @@ } __attribute__((packed)); typedef struct BDRVParallelsState { - int fd; uint32_t *catalog_bitmap; int catalog_size; @@ -68,24 +67,15 @@ return 0; } -static int parallels_open(BlockDriverState *bs, const char *filename, int flags) +static int parallels_open(BlockDriverState *bs, int flags) { BDRVParallelsState *s = bs->opaque; - int fd, i; + int i; struct parallels_header ph; - fd = open(filename, O_RDWR | O_BINARY | O_LARGEFILE); - if (fd < 0) { - fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE); - if (fd < 0) - return -1; - } - bs->read_only = 1; // no write support yet - s->fd = fd; - - if (read(fd, &ph, sizeof(ph)) != sizeof(ph)) + if (bdrv_pread(bs->file, 0, &ph, sizeof(ph)) != sizeof(ph)) goto fail; if (memcmp(ph.magic, HEADER_MAGIC, 16) || @@ -95,14 +85,11 @@ bs->total_sectors = le32_to_cpu(ph.nb_sectors); - if (lseek(s->fd, 64, SEEK_SET) != 64) - goto fail; - s->tracks = le32_to_cpu(ph.tracks); s->catalog_size = le32_to_cpu(ph.catalog_entries); s->catalog_bitmap = qemu_malloc(s->catalog_size * 4); - if (read(s->fd, s->catalog_bitmap, s->catalog_size * 4) != + if (bdrv_pread(bs->file, 64, s->catalog_bitmap, s->catalog_size * 4) != s->catalog_size * 4) goto fail; for (i = 0; i < s->catalog_size; i++) @@ -112,45 +99,34 @@ fail: if (s->catalog_bitmap) qemu_free(s->catalog_bitmap); - close(fd); return -1; } -static inline int seek_to_sector(BlockDriverState *bs, int64_t sector_num) +static int64_t seek_to_sector(BlockDriverState *bs, int64_t sector_num) { BDRVParallelsState *s = bs->opaque; uint32_t index, offset; - uint64_t position; index = sector_num / s->tracks; offset = sector_num % s->tracks; - // not allocated + /* not allocated */ if ((index > s->catalog_size) || (s->catalog_bitmap[index] == 0)) return -1; - - position = (uint64_t)(s->catalog_bitmap[index] + offset) * 512; - -// fprintf(stderr, "sector: %llx index=%x offset=%x pointer=%x position=%x\n", -// sector_num, index, offset, s->catalog_bitmap[index], position); - - if (lseek(s->fd, position, SEEK_SET) != position) - return -1; - - return 0; + return (uint64_t)(s->catalog_bitmap[index] + offset) * 512; } static int parallels_read(BlockDriverState *bs, int64_t sector_num, uint8_t *buf, int nb_sectors) { - BDRVParallelsState *s = bs->opaque; - while (nb_sectors > 0) { - if (!seek_to_sector(bs, sector_num)) { - if (read(s->fd, buf, 512) != 512) - return -1; - } else + int64_t position = seek_to_sector(bs, sector_num); + if (position >= 0) { + if (bdrv_pread(bs->file, position, buf, 512) != 512) + return -1; + } else { memset(buf, 0, 512); + } nb_sectors--; sector_num++; buf += 512; @@ -162,7 +138,6 @@ { BDRVParallelsState *s = bs->opaque; qemu_free(s->catalog_bitmap); - close(s->fd); } static BlockDriver bdrv_parallels = { diff -Nru qemu-kvm-0.12.5+noroms/block/qcow2.c qemu-kvm-0.14.1/block/qcow2.c --- qemu-kvm-0.12.5+noroms/block/qcow2.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/block/qcow2.c 2011-05-11 13:29:46.000000000 +0000 @@ -27,6 +27,8 @@ #include #include "aes.h" #include "block/qcow2.h" +#include "qemu-error.h" +#include "qerror.h" /* Differences with QCOW: @@ -49,18 +51,16 @@ uint32_t magic; uint32_t len; } QCowExtension; -#define QCOW_EXT_MAGIC_END 0 -#define QCOW_EXT_MAGIC_BACKING_FORMAT 0xE2792ACA +#define QCOW2_EXT_MAGIC_END 0 +#define QCOW2_EXT_MAGIC_BACKING_FORMAT 0xE2792ACA - - -static int qcow_probe(const uint8_t *buf, int buf_size, const char *filename) +static int qcow2_probe(const uint8_t *buf, int buf_size, const char *filename) { const QCowHeader *cow_header = (const void *)buf; if (buf_size >= sizeof(QCowHeader) && be32_to_cpu(cow_header->magic) == QCOW_MAGIC && - be32_to_cpu(cow_header->version) == QCOW_VERSION) + be32_to_cpu(cow_header->version) >= QCOW_VERSION) return 100; else return 0; @@ -74,15 +74,14 @@ * unknown magic is skipped (future extension this version knows nothing about) * return 0 upon success, non-0 otherwise */ -static int qcow_read_extensions(BlockDriverState *bs, uint64_t start_offset, - uint64_t end_offset) +static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, + uint64_t end_offset) { - BDRVQcowState *s = bs->opaque; QCowExtension ext; uint64_t offset; #ifdef DEBUG_EXT - printf("qcow_read_extensions: start=%ld end=%ld\n", start_offset, end_offset); + printf("qcow2_read_extensions: start=%ld end=%ld\n", start_offset, end_offset); #endif offset = start_offset; while (offset < end_offset) { @@ -90,14 +89,15 @@ #ifdef DEBUG_EXT /* Sanity check */ if (offset > s->cluster_size) - printf("qcow_handle_extension: suspicious offset %lu\n", offset); + printf("qcow2_read_extension: suspicious offset %lu\n", offset); printf("attemting to read extended header in offset %lu\n", offset); #endif - if (bdrv_pread(s->hd, offset, &ext, sizeof(ext)) != sizeof(ext)) { - fprintf(stderr, "qcow_handle_extension: ERROR: pread fail from offset %llu\n", - (unsigned long long)offset); + if (bdrv_pread(bs->file, offset, &ext, sizeof(ext)) != sizeof(ext)) { + fprintf(stderr, "qcow2_read_extension: ERROR: " + "pread fail from offset %" PRIu64 "\n", + offset); return 1; } be32_to_cpus(&ext.magic); @@ -107,17 +107,17 @@ printf("ext.magic = 0x%x\n", ext.magic); #endif switch (ext.magic) { - case QCOW_EXT_MAGIC_END: + case QCOW2_EXT_MAGIC_END: return 0; - case QCOW_EXT_MAGIC_BACKING_FORMAT: + case QCOW2_EXT_MAGIC_BACKING_FORMAT: if (ext.len >= sizeof(bs->backing_format)) { fprintf(stderr, "ERROR: ext_backing_format: len=%u too large" " (>=%zu)\n", ext.len, sizeof(bs->backing_format)); return 2; } - if (bdrv_pread(s->hd, offset , bs->backing_format, + if (bdrv_pread(bs->file, offset , bs->backing_format, ext.len) != ext.len) return 3; bs->backing_format[ext.len] = '\0'; @@ -138,18 +138,18 @@ } -static int qcow_open(BlockDriverState *bs, const char *filename, int flags) +static int qcow2_open(BlockDriverState *bs, int flags) { BDRVQcowState *s = bs->opaque; - int len, i, shift, ret; + int len, i, ret = 0; QCowHeader header; uint64_t ext_end; + bool writethrough; - ret = bdrv_file_open(&s->hd, filename, flags); - if (ret < 0) - return ret; - if (bdrv_pread(s->hd, 0, &header, sizeof(header)) != sizeof(header)) + ret = bdrv_pread(bs->file, 0, &header, sizeof(header)); + if (ret < 0) { goto fail; + } be32_to_cpus(&header.magic); be32_to_cpus(&header.version); be64_to_cpus(&header.backing_file_offset); @@ -164,16 +164,31 @@ be64_to_cpus(&header.snapshots_offset); be32_to_cpus(&header.nb_snapshots); - if (header.magic != QCOW_MAGIC || header.version != QCOW_VERSION) + if (header.magic != QCOW_MAGIC) { + ret = -EINVAL; goto fail; + } + if (header.version != QCOW_VERSION) { + char version[64]; + snprintf(version, sizeof(version), "QCOW version %d", header.version); + qerror_report(QERR_UNKNOWN_BLOCK_FORMAT_FEATURE, + bs->device_name, "qcow2", version); + ret = -ENOTSUP; + goto fail; + } if (header.cluster_bits < MIN_CLUSTER_BITS || - header.cluster_bits > MAX_CLUSTER_BITS) + header.cluster_bits > MAX_CLUSTER_BITS) { + ret = -EINVAL; goto fail; - if (header.crypt_method > QCOW_CRYPT_AES) + } + if (header.crypt_method > QCOW_CRYPT_AES) { + ret = -EINVAL; goto fail; + } s->crypt_method_header = header.crypt_method; - if (s->crypt_method_header) + if (s->crypt_method_header) { bs->encrypted = 1; + } s->cluster_bits = header.cluster_bits; s->cluster_size = 1 << s->cluster_bits; s->cluster_sectors = 1 << (s->cluster_bits - 9); @@ -192,73 +207,93 @@ /* read the level 1 table */ s->l1_size = header.l1_size; - shift = s->cluster_bits + s->l2_bits; - s->l1_vm_state_index = (header.size + (1LL << shift) - 1) >> shift; + s->l1_vm_state_index = size_to_l1(s, header.size); /* the L1 table must contain at least enough entries to put header.size bytes */ - if (s->l1_size < s->l1_vm_state_index) + if (s->l1_size < s->l1_vm_state_index) { + ret = -EINVAL; goto fail; + } s->l1_table_offset = header.l1_table_offset; if (s->l1_size > 0) { s->l1_table = qemu_mallocz( align_offset(s->l1_size * sizeof(uint64_t), 512)); - if (bdrv_pread(s->hd, s->l1_table_offset, s->l1_table, s->l1_size * sizeof(uint64_t)) != - s->l1_size * sizeof(uint64_t)) + ret = bdrv_pread(bs->file, s->l1_table_offset, s->l1_table, + s->l1_size * sizeof(uint64_t)); + if (ret < 0) { goto fail; + } for(i = 0;i < s->l1_size; i++) { be64_to_cpus(&s->l1_table[i]); } } - /* alloc L2 cache */ - s->l2_cache = qemu_malloc(s->l2_size * L2_CACHE_SIZE * sizeof(uint64_t)); + + /* alloc L2 table/refcount block cache */ + writethrough = ((flags & BDRV_O_CACHE_MASK) == 0); + s->l2_table_cache = qcow2_cache_create(bs, L2_CACHE_SIZE, writethrough); + s->refcount_block_cache = qcow2_cache_create(bs, REFCOUNT_CACHE_SIZE, + writethrough); + s->cluster_cache = qemu_malloc(s->cluster_size); /* one more sector for decompressed data alignment */ s->cluster_data = qemu_malloc(QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size + 512); s->cluster_cache_offset = -1; - if (qcow2_refcount_init(bs) < 0) + ret = qcow2_refcount_init(bs); + if (ret != 0) { goto fail; + } QLIST_INIT(&s->cluster_allocs); /* read qcow2 extensions */ - if (header.backing_file_offset) + if (header.backing_file_offset) { ext_end = header.backing_file_offset; - else + } else { ext_end = s->cluster_size; - if (qcow_read_extensions(bs, sizeof(header), ext_end)) + } + if (qcow2_read_extensions(bs, sizeof(header), ext_end)) { + ret = -EINVAL; goto fail; + } /* read the backing file name */ if (header.backing_file_offset != 0) { len = header.backing_file_size; - if (len > 1023) + if (len > 1023) { len = 1023; - if (bdrv_pread(s->hd, header.backing_file_offset, bs->backing_file, len) != len) + } + ret = bdrv_pread(bs->file, header.backing_file_offset, + bs->backing_file, len); + if (ret < 0) { goto fail; + } bs->backing_file[len] = '\0'; } - if (qcow2_read_snapshots(bs) < 0) + if (qcow2_read_snapshots(bs) < 0) { + ret = -EINVAL; goto fail; + } #ifdef DEBUG_ALLOC qcow2_check_refcounts(bs); #endif - return 0; + return ret; fail: qcow2_free_snapshots(bs); qcow2_refcount_close(bs); qemu_free(s->l1_table); - qemu_free(s->l2_cache); + if (s->l2_table_cache) { + qcow2_cache_destroy(bs, s->l2_table_cache); + } qemu_free(s->cluster_cache); qemu_free(s->cluster_data); - bdrv_delete(s->hd); - return -1; + return ret; } -static int qcow_set_key(BlockDriverState *bs, const char *key) +static int qcow2_set_key(BlockDriverState *bs, const char *key) { BDRVQcowState *s = bs->opaque; uint8_t keybuf[16]; @@ -300,20 +335,26 @@ return 0; } -static int qcow_is_allocated(BlockDriverState *bs, int64_t sector_num, - int nb_sectors, int *pnum) +static int qcow2_is_allocated(BlockDriverState *bs, int64_t sector_num, + int nb_sectors, int *pnum) { uint64_t cluster_offset; + int ret; *pnum = nb_sectors; - cluster_offset = qcow2_get_cluster_offset(bs, sector_num << 9, pnum); + /* FIXME We can get errors here, but the bdrv_is_allocated interface can't + * pass them on today */ + ret = qcow2_get_cluster_offset(bs, sector_num << 9, pnum, &cluster_offset); + if (ret < 0) { + *pnum = 0; + } return (cluster_offset != 0); } /* handle reading after the end of the backing file */ -int qcow2_backing_read1(BlockDriverState *bs, - int64_t sector_num, uint8_t *buf, int nb_sectors) +int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov, + int64_t sector_num, int nb_sectors) { int n1; if ((sector_num + nb_sectors) <= bs->total_sectors) @@ -322,7 +363,9 @@ n1 = 0; else n1 = bs->total_sectors - sector_num; - memset(buf + n1 * 512, 0, 512 * (nb_sectors - n1)); + + qemu_iovec_memset_skip(qiov, 0, 512 * (nb_sectors - n1), 512 * n1); + return n1; } @@ -330,43 +373,41 @@ BlockDriverAIOCB common; int64_t sector_num; QEMUIOVector *qiov; - uint8_t *buf; - void *orig_buf; - int nb_sectors; - int n; + int remaining_sectors; + int cur_nr_sectors; /* number of sectors in current iteration */ + uint64_t bytes_done; uint64_t cluster_offset; uint8_t *cluster_data; BlockDriverAIOCB *hd_aiocb; - struct iovec hd_iov; QEMUIOVector hd_qiov; QEMUBH *bh; QCowL2Meta l2meta; QLIST_ENTRY(QCowAIOCB) next_depend; } QCowAIOCB; -static void qcow_aio_cancel(BlockDriverAIOCB *blockacb) +static void qcow2_aio_cancel(BlockDriverAIOCB *blockacb) { - QCowAIOCB *acb = (QCowAIOCB *)blockacb; + QCowAIOCB *acb = container_of(blockacb, QCowAIOCB, common); if (acb->hd_aiocb) bdrv_aio_cancel(acb->hd_aiocb); qemu_aio_release(acb); } -static AIOPool qcow_aio_pool = { +static AIOPool qcow2_aio_pool = { .aiocb_size = sizeof(QCowAIOCB), - .cancel = qcow_aio_cancel, + .cancel = qcow2_aio_cancel, }; -static void qcow_aio_read_cb(void *opaque, int ret); -static void qcow_aio_read_bh(void *opaque) +static void qcow2_aio_read_cb(void *opaque, int ret); +static void qcow2_aio_read_bh(void *opaque) { QCowAIOCB *acb = opaque; qemu_bh_delete(acb->bh); acb->bh = NULL; - qcow_aio_read_cb(opaque, 0); + qcow2_aio_read_cb(opaque, 0); } -static int qcow_schedule_bh(QEMUBHFunc *cb, QCowAIOCB *acb) +static int qcow2_schedule_bh(QEMUBHFunc *cb, QCowAIOCB *acb) { if (acb->bh) return -EIO; @@ -380,7 +421,7 @@ return 0; } -static void qcow_aio_read_cb(void *opaque, int ret) +static void qcow2_aio_read_cb(void *opaque, int ret) { QCowAIOCB *acb = opaque; BlockDriverState *bs = acb->common.bs; @@ -398,61 +439,83 @@ /* nothing to do */ } else { if (s->crypt_method) { - qcow2_encrypt_sectors(s, acb->sector_num, acb->buf, acb->buf, - acb->n, 0, - &s->aes_decrypt_key); + qcow2_encrypt_sectors(s, acb->sector_num, acb->cluster_data, + acb->cluster_data, acb->cur_nr_sectors, 0, &s->aes_decrypt_key); + qemu_iovec_reset(&acb->hd_qiov); + qemu_iovec_copy(&acb->hd_qiov, acb->qiov, acb->bytes_done, + acb->cur_nr_sectors * 512); + qemu_iovec_from_buffer(&acb->hd_qiov, acb->cluster_data, + 512 * acb->cur_nr_sectors); } } - acb->nb_sectors -= acb->n; - acb->sector_num += acb->n; - acb->buf += acb->n * 512; + acb->remaining_sectors -= acb->cur_nr_sectors; + acb->sector_num += acb->cur_nr_sectors; + acb->bytes_done += acb->cur_nr_sectors * 512; - if (acb->nb_sectors == 0) { + if (acb->remaining_sectors == 0) { /* request completed */ ret = 0; goto done; } /* prepare next AIO request */ - acb->n = acb->nb_sectors; - acb->cluster_offset = - qcow2_get_cluster_offset(bs, acb->sector_num << 9, &acb->n); + acb->cur_nr_sectors = acb->remaining_sectors; + if (s->crypt_method) { + acb->cur_nr_sectors = MIN(acb->cur_nr_sectors, + QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors); + } + + ret = qcow2_get_cluster_offset(bs, acb->sector_num << 9, + &acb->cur_nr_sectors, &acb->cluster_offset); + if (ret < 0) { + goto done; + } + index_in_cluster = acb->sector_num & (s->cluster_sectors - 1); + qemu_iovec_reset(&acb->hd_qiov); + qemu_iovec_copy(&acb->hd_qiov, acb->qiov, acb->bytes_done, + acb->cur_nr_sectors * 512); + if (!acb->cluster_offset) { + if (bs->backing_hd) { /* read from the base image */ - n1 = qcow2_backing_read1(bs->backing_hd, acb->sector_num, - acb->buf, acb->n); + n1 = qcow2_backing_read1(bs->backing_hd, &acb->hd_qiov, + acb->sector_num, acb->cur_nr_sectors); if (n1 > 0) { - acb->hd_iov.iov_base = (void *)acb->buf; - acb->hd_iov.iov_len = acb->n * 512; - qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1); + BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO); acb->hd_aiocb = bdrv_aio_readv(bs->backing_hd, acb->sector_num, - &acb->hd_qiov, acb->n, - qcow_aio_read_cb, acb); - if (acb->hd_aiocb == NULL) + &acb->hd_qiov, n1, qcow2_aio_read_cb, acb); + if (acb->hd_aiocb == NULL) { + ret = -EIO; goto done; + } } else { - ret = qcow_schedule_bh(qcow_aio_read_bh, acb); + ret = qcow2_schedule_bh(qcow2_aio_read_bh, acb); if (ret < 0) goto done; } } else { /* Note: in this case, no need to wait */ - memset(acb->buf, 0, 512 * acb->n); - ret = qcow_schedule_bh(qcow_aio_read_bh, acb); + qemu_iovec_memset(&acb->hd_qiov, 0, 512 * acb->cur_nr_sectors); + ret = qcow2_schedule_bh(qcow2_aio_read_bh, acb); if (ret < 0) goto done; } } else if (acb->cluster_offset & QCOW_OFLAG_COMPRESSED) { /* add AIO support for compressed blocks ? */ - if (qcow2_decompress_cluster(s, acb->cluster_offset) < 0) + ret = qcow2_decompress_cluster(bs, acb->cluster_offset); + if (ret < 0) { goto done; - memcpy(acb->buf, - s->cluster_cache + index_in_cluster * 512, 512 * acb->n); - ret = qcow_schedule_bh(qcow_aio_read_bh, acb); + } + + qemu_iovec_from_buffer(&acb->hd_qiov, + s->cluster_cache + index_in_cluster * 512, + 512 * acb->cur_nr_sectors); + + ret = qcow2_schedule_bh(qcow2_aio_read_bh, acb); if (ret < 0) goto done; } else { @@ -461,12 +524,28 @@ goto done; } - acb->hd_iov.iov_base = (void *)acb->buf; - acb->hd_iov.iov_len = acb->n * 512; - qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1); - acb->hd_aiocb = bdrv_aio_readv(s->hd, + if (s->crypt_method) { + /* + * For encrypted images, read everything into a temporary + * contiguous buffer on which the AES functions can work. + */ + if (!acb->cluster_data) { + acb->cluster_data = + qemu_mallocz(QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size); + } + + assert(acb->cur_nr_sectors <= + QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors); + qemu_iovec_reset(&acb->hd_qiov); + qemu_iovec_add(&acb->hd_qiov, acb->cluster_data, + 512 * acb->cur_nr_sectors); + } + + BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO); + acb->hd_aiocb = bdrv_aio_readv(bs->file, (acb->cluster_offset >> 9) + index_in_cluster, - &acb->hd_qiov, acb->n, qcow_aio_read_cb, acb); + &acb->hd_qiov, acb->cur_nr_sectors, + qcow2_aio_read_cb, acb); if (acb->hd_aiocb == NULL) { ret = -EIO; goto done; @@ -475,56 +554,53 @@ return; done: - if (acb->qiov->niov > 1) { - qemu_iovec_from_buffer(acb->qiov, acb->orig_buf, acb->qiov->size); - qemu_vfree(acb->orig_buf); - } acb->common.cb(acb->common.opaque, ret); + qemu_iovec_destroy(&acb->hd_qiov); qemu_aio_release(acb); } -static QCowAIOCB *qcow_aio_setup(BlockDriverState *bs, - int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, - BlockDriverCompletionFunc *cb, void *opaque, int is_write) +static QCowAIOCB *qcow2_aio_setup(BlockDriverState *bs, int64_t sector_num, + QEMUIOVector *qiov, int nb_sectors, + BlockDriverCompletionFunc *cb, + void *opaque, int is_write) { QCowAIOCB *acb; - acb = qemu_aio_get(&qcow_aio_pool, bs, cb, opaque); + acb = qemu_aio_get(&qcow2_aio_pool, bs, cb, opaque); if (!acb) return NULL; acb->hd_aiocb = NULL; acb->sector_num = sector_num; acb->qiov = qiov; - if (qiov->niov > 1) { - acb->buf = acb->orig_buf = qemu_blockalign(bs, qiov->size); - if (is_write) - qemu_iovec_to_buffer(qiov, acb->buf); - } else { - acb->buf = (uint8_t *)qiov->iov->iov_base; - } - acb->nb_sectors = nb_sectors; - acb->n = 0; + + qemu_iovec_init(&acb->hd_qiov, qiov->niov); + + acb->bytes_done = 0; + acb->remaining_sectors = nb_sectors; + acb->cur_nr_sectors = 0; acb->cluster_offset = 0; acb->l2meta.nb_clusters = 0; QLIST_INIT(&acb->l2meta.dependent_requests); return acb; } -static BlockDriverAIOCB *qcow_aio_readv(BlockDriverState *bs, - int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, - BlockDriverCompletionFunc *cb, void *opaque) +static BlockDriverAIOCB *qcow2_aio_readv(BlockDriverState *bs, + int64_t sector_num, + QEMUIOVector *qiov, int nb_sectors, + BlockDriverCompletionFunc *cb, + void *opaque) { QCowAIOCB *acb; - acb = qcow_aio_setup(bs, sector_num, qiov, nb_sectors, cb, opaque, 0); + acb = qcow2_aio_setup(bs, sector_num, qiov, nb_sectors, cb, opaque, 0); if (!acb) return NULL; - qcow_aio_read_cb(acb, 0); + qcow2_aio_read_cb(acb, 0); return &acb->common; } -static void qcow_aio_write_cb(void *opaque, int ret); +static void qcow2_aio_write_cb(void *opaque, int ret); static void run_dependent_requests(QCowL2Meta *m) { @@ -536,28 +612,21 @@ QLIST_REMOVE(m, next_in_flight); } - /* - * Restart all dependent requests. - * Can't use QLIST_FOREACH here - the next link might not be the same - * any more after the callback (request could depend on a different - * request now) - */ - for (req = m->dependent_requests.lh_first; req != NULL; req = next) { - next = req->next_depend.le_next; - qcow_aio_write_cb(req, 0); + /* Restart all dependent requests */ + QLIST_FOREACH_SAFE(req, &m->dependent_requests, next_depend, next) { + qcow2_aio_write_cb(req, 0); } /* Empty the list for the next part of the request */ QLIST_INIT(&m->dependent_requests); } -static void qcow_aio_write_cb(void *opaque, int ret) +static void qcow2_aio_write_cb(void *opaque, int ret) { QCowAIOCB *acb = opaque; BlockDriverState *bs = acb->common.bs; BDRVQcowState *s = bs->opaque; int index_in_cluster; - const uint8_t *src_buf; int n_end; acb->hd_aiocb = NULL; @@ -571,24 +640,24 @@ if (ret < 0) goto done; - acb->nb_sectors -= acb->n; - acb->sector_num += acb->n; - acb->buf += acb->n * 512; + acb->remaining_sectors -= acb->cur_nr_sectors; + acb->sector_num += acb->cur_nr_sectors; + acb->bytes_done += acb->cur_nr_sectors * 512; - if (acb->nb_sectors == 0) { + if (acb->remaining_sectors == 0) { /* request completed */ ret = 0; goto done; } index_in_cluster = acb->sector_num & (s->cluster_sectors - 1); - n_end = index_in_cluster + acb->nb_sectors; + n_end = index_in_cluster + acb->remaining_sectors; if (s->crypt_method && n_end > QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors) n_end = QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors; ret = qcow2_alloc_cluster_offset(bs, acb->sector_num << 9, - index_in_cluster, n_end, &acb->n, &acb->l2meta); + index_in_cluster, n_end, &acb->cur_nr_sectors, &acb->l2meta); if (ret < 0) { goto done; } @@ -604,24 +673,32 @@ assert((acb->cluster_offset & 511) == 0); + qemu_iovec_reset(&acb->hd_qiov); + qemu_iovec_copy(&acb->hd_qiov, acb->qiov, acb->bytes_done, + acb->cur_nr_sectors * 512); + if (s->crypt_method) { if (!acb->cluster_data) { acb->cluster_data = qemu_mallocz(QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size); } - qcow2_encrypt_sectors(s, acb->sector_num, acb->cluster_data, acb->buf, - acb->n, 1, &s->aes_encrypt_key); - src_buf = acb->cluster_data; - } else { - src_buf = acb->buf; + + assert(acb->hd_qiov.size <= QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size); + qemu_iovec_to_buffer(&acb->hd_qiov, acb->cluster_data); + + qcow2_encrypt_sectors(s, acb->sector_num, acb->cluster_data, + acb->cluster_data, acb->cur_nr_sectors, 1, &s->aes_encrypt_key); + + qemu_iovec_reset(&acb->hd_qiov); + qemu_iovec_add(&acb->hd_qiov, acb->cluster_data, + acb->cur_nr_sectors * 512); } - acb->hd_iov.iov_base = (void *)src_buf; - acb->hd_iov.iov_len = acb->n * 512; - qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1); - acb->hd_aiocb = bdrv_aio_writev(s->hd, + + BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO); + acb->hd_aiocb = bdrv_aio_writev(bs->file, (acb->cluster_offset >> 9) + index_in_cluster, - &acb->hd_qiov, acb->n, - qcow_aio_write_cb, acb); + &acb->hd_qiov, acb->cur_nr_sectors, + qcow2_aio_write_cb, acb); if (acb->hd_aiocb == NULL) { ret = -EIO; goto fail; @@ -634,65 +711,147 @@ QLIST_REMOVE(&acb->l2meta, next_in_flight); } done: - if (acb->qiov->niov > 1) - qemu_vfree(acb->orig_buf); acb->common.cb(acb->common.opaque, ret); + qemu_iovec_destroy(&acb->hd_qiov); qemu_aio_release(acb); } -static BlockDriverAIOCB *qcow_aio_writev(BlockDriverState *bs, - int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, - BlockDriverCompletionFunc *cb, void *opaque) +static BlockDriverAIOCB *qcow2_aio_writev(BlockDriverState *bs, + int64_t sector_num, + QEMUIOVector *qiov, int nb_sectors, + BlockDriverCompletionFunc *cb, + void *opaque) { BDRVQcowState *s = bs->opaque; QCowAIOCB *acb; s->cluster_cache_offset = -1; /* disable compressed cache */ - acb = qcow_aio_setup(bs, sector_num, qiov, nb_sectors, cb, opaque, 1); + acb = qcow2_aio_setup(bs, sector_num, qiov, nb_sectors, cb, opaque, 1); if (!acb) return NULL; - qcow_aio_write_cb(acb, 0); + qcow2_aio_write_cb(acb, 0); return &acb->common; } -static void qcow_close(BlockDriverState *bs) +static void qcow2_close(BlockDriverState *bs) { BDRVQcowState *s = bs->opaque; qemu_free(s->l1_table); - qemu_free(s->l2_cache); + + qcow2_cache_flush(bs, s->l2_table_cache); + qcow2_cache_flush(bs, s->refcount_block_cache); + + qcow2_cache_destroy(bs, s->l2_table_cache); + qcow2_cache_destroy(bs, s->refcount_block_cache); + qemu_free(s->cluster_cache); qemu_free(s->cluster_data); qcow2_refcount_close(bs); - bdrv_delete(s->hd); } -static int get_bits_from_size(size_t size) +/* + * Updates the variable length parts of the qcow2 header, i.e. the backing file + * name and all extensions. qcow2 was not designed to allow such changes, so if + * we run out of space (we can only use the first cluster) this function may + * fail. + * + * Returns 0 on success, -errno in error cases. + */ +static int qcow2_update_ext_header(BlockDriverState *bs, + const char *backing_file, const char *backing_fmt) { - int res = 0; + size_t backing_file_len = 0; + size_t backing_fmt_len = 0; + BDRVQcowState *s = bs->opaque; + QCowExtension ext_backing_fmt = {0, 0}; + int ret; - if (size == 0) { - return -1; + /* Backing file format doesn't make sense without a backing file */ + if (backing_fmt && !backing_file) { + return -EINVAL; } - while (size != 1) { - /* Not a power of two */ - if (size & 1) { - return -1; + /* Prepare the backing file format extension if needed */ + if (backing_fmt) { + ext_backing_fmt.len = cpu_to_be32(strlen(backing_fmt)); + ext_backing_fmt.magic = cpu_to_be32(QCOW2_EXT_MAGIC_BACKING_FORMAT); + backing_fmt_len = ((sizeof(ext_backing_fmt) + + strlen(backing_fmt) + 7) & ~7); + } + + /* Check if we can fit the new header into the first cluster */ + if (backing_file) { + backing_file_len = strlen(backing_file); + } + + size_t header_size = sizeof(QCowHeader) + backing_file_len + + backing_fmt_len; + + if (header_size > s->cluster_size) { + return -ENOSPC; + } + + /* Rewrite backing file name and qcow2 extensions */ + size_t ext_size = header_size - sizeof(QCowHeader); + uint8_t buf[ext_size]; + size_t offset = 0; + size_t backing_file_offset = 0; + + if (backing_file) { + if (backing_fmt) { + int padding = backing_fmt_len - + (sizeof(ext_backing_fmt) + strlen(backing_fmt)); + + memcpy(buf + offset, &ext_backing_fmt, sizeof(ext_backing_fmt)); + offset += sizeof(ext_backing_fmt); + + memcpy(buf + offset, backing_fmt, strlen(backing_fmt)); + offset += strlen(backing_fmt); + + memset(buf + offset, 0, padding); + offset += padding; } - size >>= 1; - res++; + memcpy(buf + offset, backing_file, backing_file_len); + backing_file_offset = sizeof(QCowHeader) + offset; + } + + ret = bdrv_pwrite_sync(bs->file, sizeof(QCowHeader), buf, ext_size); + if (ret < 0) { + goto fail; + } + + /* Update header fields */ + uint64_t be_backing_file_offset = cpu_to_be64(backing_file_offset); + uint32_t be_backing_file_size = cpu_to_be32(backing_file_len); + + ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, backing_file_offset), + &be_backing_file_offset, sizeof(uint64_t)); + if (ret < 0) { + goto fail; + } + + ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, backing_file_size), + &be_backing_file_size, sizeof(uint32_t)); + if (ret < 0) { + goto fail; } - return res; + ret = 0; +fail: + return ret; } +static int qcow2_change_backing_file(BlockDriverState *bs, + const char *backing_file, const char *backing_fmt) +{ + return qcow2_update_ext_header(bs, backing_file, backing_fmt); +} static int preallocate(BlockDriverState *bs) { - BDRVQcowState *s = bs->opaque; uint64_t nb_sectors; uint64_t offset; int num; @@ -707,14 +866,14 @@ while (nb_sectors) { num = MIN(nb_sectors, INT_MAX >> 9); ret = qcow2_alloc_cluster_offset(bs, offset, 0, num, &num, &meta); - if (ret < 0) { - return -1; + return ret; } - if (qcow2_alloc_cluster_link_l2(bs, &meta) < 0) { + ret = qcow2_alloc_cluster_link_l2(bs, &meta); + if (ret < 0) { qcow2_free_any_clusters(bs, meta.cluster_offset, meta.nb_clusters); - return -1; + return ret; } /* There are no dependent requests, but we need to remove our request @@ -735,175 +894,143 @@ if (meta.cluster_offset != 0) { uint8_t buf[512]; memset(buf, 0, 512); - bdrv_write(s->hd, (meta.cluster_offset >> 9) + num - 1, buf, 1); + ret = bdrv_write(bs->file, (meta.cluster_offset >> 9) + num - 1, buf, 1); + if (ret < 0) { + return ret; + } } return 0; } -static int qcow_create2(const char *filename, int64_t total_size, - const char *backing_file, const char *backing_format, - int flags, size_t cluster_size, int prealloc) -{ +static int qcow2_create2(const char *filename, int64_t total_size, + const char *backing_file, const char *backing_format, + int flags, size_t cluster_size, int prealloc, + QEMUOptionParameter *options) +{ + /* Calulate cluster_bits */ + int cluster_bits; + cluster_bits = ffs(cluster_size) - 1; + if (cluster_bits < MIN_CLUSTER_BITS || cluster_bits > MAX_CLUSTER_BITS || + (1 << cluster_bits) != cluster_size) + { + error_report( + "Cluster size must be a power of two between %d and %dk\n", + 1 << MIN_CLUSTER_BITS, 1 << (MAX_CLUSTER_BITS - 10)); + return -EINVAL; + } - int fd, header_size, backing_filename_len, l1_size, i, shift, l2_bits; - int ref_clusters, reftable_clusters, backing_format_len = 0; - int rounded_ext_bf_len = 0; + /* + * Open the image file and write a minimal qcow2 header. + * + * We keep things simple and start with a zero-sized image. We also + * do without refcount blocks or a L1 table for now. We'll fix the + * inconsistency later. + * + * We do need a refcount table because growing the refcount table means + * allocating two new refcount blocks - the seconds of which would be at + * 2 GB for 64k clusters, and we don't want to have a 2 GB initial file + * size for any qcow2 image. + */ + BlockDriverState* bs; QCowHeader header; - uint64_t tmp, offset; - uint64_t old_ref_clusters; - QCowCreateState s1, *s = &s1; - QCowExtension ext_bf = {0, 0}; + uint8_t* refcount_table; + int ret; + ret = bdrv_create_file(filename, options); + if (ret < 0) { + return ret; + } - memset(s, 0, sizeof(*s)); + ret = bdrv_file_open(&bs, filename, BDRV_O_RDWR); + if (ret < 0) { + return ret; + } - fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644); - if (fd < 0) - return -1; + /* Write the header */ memset(&header, 0, sizeof(header)); header.magic = cpu_to_be32(QCOW_MAGIC); header.version = cpu_to_be32(QCOW_VERSION); - header.size = cpu_to_be64(total_size * 512); - header_size = sizeof(header); - backing_filename_len = 0; - if (backing_file) { - if (backing_format) { - ext_bf.magic = QCOW_EXT_MAGIC_BACKING_FORMAT; - backing_format_len = strlen(backing_format); - ext_bf.len = backing_format_len; - rounded_ext_bf_len = (sizeof(ext_bf) + ext_bf.len + 7) & ~7; - header_size += rounded_ext_bf_len; - } - header.backing_file_offset = cpu_to_be64(header_size); - backing_filename_len = strlen(backing_file); - header.backing_file_size = cpu_to_be32(backing_filename_len); - header_size += backing_filename_len; - } - - /* Cluster size */ - s->cluster_bits = get_bits_from_size(cluster_size); - if (s->cluster_bits < MIN_CLUSTER_BITS || - s->cluster_bits > MAX_CLUSTER_BITS) - { - fprintf(stderr, "Cluster size must be a power of two between " - "%d and %dk\n", - 1 << MIN_CLUSTER_BITS, - 1 << (MAX_CLUSTER_BITS - 10)); - return -EINVAL; - } - s->cluster_size = 1 << s->cluster_bits; + header.cluster_bits = cpu_to_be32(cluster_bits); + header.size = cpu_to_be64(0); + header.l1_table_offset = cpu_to_be64(0); + header.l1_size = cpu_to_be32(0); + header.refcount_table_offset = cpu_to_be64(cluster_size); + header.refcount_table_clusters = cpu_to_be32(1); - header.cluster_bits = cpu_to_be32(s->cluster_bits); - header_size = (header_size + 7) & ~7; if (flags & BLOCK_FLAG_ENCRYPT) { header.crypt_method = cpu_to_be32(QCOW_CRYPT_AES); } else { header.crypt_method = cpu_to_be32(QCOW_CRYPT_NONE); } - l2_bits = s->cluster_bits - 3; - shift = s->cluster_bits + l2_bits; - l1_size = (((total_size * 512) + (1LL << shift) - 1) >> shift); - offset = align_offset(header_size, s->cluster_size); - s->l1_table_offset = offset; - header.l1_table_offset = cpu_to_be64(s->l1_table_offset); - header.l1_size = cpu_to_be32(l1_size); - offset += align_offset(l1_size * sizeof(uint64_t), s->cluster_size); - - /* count how many refcount blocks needed */ - -#define NUM_CLUSTERS(bytes) \ - (((bytes) + (s->cluster_size) - 1) / (s->cluster_size)) - - ref_clusters = NUM_CLUSTERS(NUM_CLUSTERS(offset) * sizeof(uint16_t)); - do { - uint64_t image_clusters; - old_ref_clusters = ref_clusters; - - /* Number of clusters used for the refcount table */ - reftable_clusters = NUM_CLUSTERS(ref_clusters * sizeof(uint64_t)); + ret = bdrv_pwrite(bs, 0, &header, sizeof(header)); + if (ret < 0) { + goto out; + } - /* Number of clusters that the whole image will have */ - image_clusters = NUM_CLUSTERS(offset) + ref_clusters - + reftable_clusters; + /* Write an empty refcount table */ + refcount_table = qemu_mallocz(cluster_size); + ret = bdrv_pwrite(bs, cluster_size, refcount_table, cluster_size); + qemu_free(refcount_table); - /* Number of refcount blocks needed for the image */ - ref_clusters = NUM_CLUSTERS(image_clusters * sizeof(uint16_t)); + if (ret < 0) { + goto out; + } - } while (ref_clusters != old_ref_clusters); + bdrv_close(bs); - s->refcount_table = qemu_mallocz(reftable_clusters * s->cluster_size); + /* + * And now open the image and make it consistent first (i.e. increase the + * refcount of the cluster that is occupied by the header and the refcount + * table) + */ + BlockDriver* drv = bdrv_find_format("qcow2"); + assert(drv != NULL); + ret = bdrv_open(bs, filename, + BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH, drv); + if (ret < 0) { + goto out; + } - s->refcount_table_offset = offset; - header.refcount_table_offset = cpu_to_be64(offset); - header.refcount_table_clusters = cpu_to_be32(reftable_clusters); - offset += (reftable_clusters * s->cluster_size); - s->refcount_block_offset = offset; + ret = qcow2_alloc_clusters(bs, 2 * cluster_size); + if (ret < 0) { + goto out; - for (i=0; i < ref_clusters; i++) { - s->refcount_table[i] = cpu_to_be64(offset); - offset += s->cluster_size; + } else if (ret != 0) { + error_report("Huh, first cluster in empty image is already in use?"); + abort(); } - s->refcount_block = qemu_mallocz(ref_clusters * s->cluster_size); - - /* update refcounts */ - qcow2_create_refcount_update(s, 0, header_size); - qcow2_create_refcount_update(s, s->l1_table_offset, - l1_size * sizeof(uint64_t)); - qcow2_create_refcount_update(s, s->refcount_table_offset, - reftable_clusters * s->cluster_size); - qcow2_create_refcount_update(s, s->refcount_block_offset, - ref_clusters * s->cluster_size); + /* Okay, now that we have a valid image, let's give it the right size */ + ret = bdrv_truncate(bs, total_size * BDRV_SECTOR_SIZE); + if (ret < 0) { + goto out; + } - /* write all the data */ - write(fd, &header, sizeof(header)); + /* Want a backing file? There you go.*/ if (backing_file) { - if (backing_format_len) { - char zero[16]; - int padding = rounded_ext_bf_len - (ext_bf.len + sizeof(ext_bf)); - - memset(zero, 0, sizeof(zero)); - cpu_to_be32s(&ext_bf.magic); - cpu_to_be32s(&ext_bf.len); - write(fd, &ext_bf, sizeof(ext_bf)); - write(fd, backing_format, backing_format_len); - if (padding > 0) { - write(fd, zero, padding); - } + ret = bdrv_change_backing_file(bs, backing_file, backing_format); + if (ret < 0) { + goto out; } - write(fd, backing_file, backing_filename_len); - } - lseek(fd, s->l1_table_offset, SEEK_SET); - tmp = 0; - for(i = 0;i < l1_size; i++) { - write(fd, &tmp, sizeof(tmp)); } - lseek(fd, s->refcount_table_offset, SEEK_SET); - write(fd, s->refcount_table, - reftable_clusters * s->cluster_size); - lseek(fd, s->refcount_block_offset, SEEK_SET); - write(fd, s->refcount_block, ref_clusters * s->cluster_size); - - qemu_free(s->refcount_table); - qemu_free(s->refcount_block); - close(fd); - - /* Preallocate metadata */ + /* And if we're supposed to preallocate metadata, do that now */ if (prealloc) { - BlockDriverState *bs; - bs = bdrv_new(""); - bdrv_open(bs, filename, BDRV_O_CACHE_WB); - preallocate(bs); - bdrv_close(bs); + ret = preallocate(bs); + if (ret < 0) { + goto out; + } } - return 0; + ret = 0; +out: + bdrv_delete(bs); + return ret; } -static int qcow_create(const char *filename, QEMUOptionParameter *options) +static int qcow2_create(const char *filename, QEMUOptionParameter *options) { const char *backing_file = NULL; const char *backing_fmt = NULL; @@ -946,11 +1073,11 @@ return -EINVAL; } - return qcow_create2(filename, sectors, backing_file, backing_fmt, flags, - cluster_size, prealloc); + return qcow2_create2(filename, sectors, backing_file, backing_fmt, flags, + cluster_size, prealloc, options); } -static int qcow_make_empty(BlockDriverState *bs) +static int qcow2_make_empty(BlockDriverState *bs) { #if 0 /* XXX: not correct */ @@ -959,9 +1086,9 @@ int ret; memset(s->l1_table, 0, l1_length); - if (bdrv_pwrite(s->hd, s->l1_table_offset, s->l1_table, l1_length) < 0) + if (bdrv_pwrite(bs->file, s->l1_table_offset, s->l1_table, l1_length) < 0) return -1; - ret = bdrv_truncate(s->hd, s->l1_table_offset + l1_length); + ret = bdrv_truncate(bs->file, s->l1_table_offset + l1_length); if (ret < 0) return ret; @@ -970,10 +1097,54 @@ return 0; } +static int qcow2_discard(BlockDriverState *bs, int64_t sector_num, + int nb_sectors) +{ + return qcow2_discard_clusters(bs, sector_num << BDRV_SECTOR_BITS, + nb_sectors); +} + +static int qcow2_truncate(BlockDriverState *bs, int64_t offset) +{ + BDRVQcowState *s = bs->opaque; + int ret, new_l1_size; + + if (offset & 511) { + return -EINVAL; + } + + /* cannot proceed if image has snapshots */ + if (s->nb_snapshots) { + return -ENOTSUP; + } + + /* shrinking is currently not supported */ + if (offset < bs->total_sectors * 512) { + return -ENOTSUP; + } + + new_l1_size = size_to_l1(s, offset); + ret = qcow2_grow_l1_table(bs, new_l1_size, true); + if (ret < 0) { + return ret; + } + + /* write updated header.size */ + offset = cpu_to_be64(offset); + ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, size), + &offset, sizeof(uint64_t)); + if (ret < 0) { + return ret; + } + + s->l1_vm_state_index = new_l1_size; + return 0; +} + /* XXX: put compressed sectors first, then all the cluster aligned tables to avoid losing bytes in alignment */ -static int qcow_write_compressed(BlockDriverState *bs, int64_t sector_num, - const uint8_t *buf, int nb_sectors) +static int qcow2_write_compressed(BlockDriverState *bs, int64_t sector_num, + const uint8_t *buf, int nb_sectors) { BDRVQcowState *s = bs->opaque; z_stream strm; @@ -984,9 +1155,9 @@ if (nb_sectors == 0) { /* align end of file to a sector boundary to ease reading with sector based I/Os */ - cluster_offset = bdrv_getlength(s->hd); + cluster_offset = bdrv_getlength(bs->file); cluster_offset = (cluster_offset + 511) & ~511; - bdrv_truncate(s->hd, cluster_offset); + bdrv_truncate(bs->file, cluster_offset); return 0; } @@ -1029,7 +1200,8 @@ if (!cluster_offset) return -1; cluster_offset &= s->cluster_offset_mask; - if (bdrv_pwrite(s->hd, cluster_offset, out_buf, out_len) != out_len) { + BLKDBG_EVENT(bs->file, BLKDBG_WRITE_COMPRESSED); + if (bdrv_pwrite(bs->file, cluster_offset, out_buf, out_len) != out_len) { qemu_free(out_buf); return -1; } @@ -1039,29 +1211,61 @@ return 0; } -static void qcow_flush(BlockDriverState *bs) +static int qcow2_flush(BlockDriverState *bs) +{ + BDRVQcowState *s = bs->opaque; + int ret; + + ret = qcow2_cache_flush(bs, s->l2_table_cache); + if (ret < 0) { + return ret; + } + + ret = qcow2_cache_flush(bs, s->refcount_block_cache); + if (ret < 0) { + return ret; + } + + return bdrv_flush(bs->file); +} + +static BlockDriverAIOCB *qcow2_aio_flush(BlockDriverState *bs, + BlockDriverCompletionFunc *cb, + void *opaque) { BDRVQcowState *s = bs->opaque; - bdrv_flush(s->hd); + int ret; + + ret = qcow2_cache_flush(bs, s->l2_table_cache); + if (ret < 0) { + return NULL; + } + + ret = qcow2_cache_flush(bs, s->refcount_block_cache); + if (ret < 0) { + return NULL; + } + + return bdrv_aio_flush(bs->file, cb, opaque); } -static int64_t qcow_vm_state_offset(BDRVQcowState *s) +static int64_t qcow2_vm_state_offset(BDRVQcowState *s) { return (int64_t)s->l1_vm_state_index << (s->cluster_bits + s->l2_bits); } -static int qcow_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) +static int qcow2_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) { BDRVQcowState *s = bs->opaque; bdi->cluster_size = s->cluster_size; - bdi->vm_state_offset = qcow_vm_state_offset(s); + bdi->vm_state_offset = qcow2_vm_state_offset(s); return 0; } -static int qcow_check(BlockDriverState *bs) +static int qcow2_check(BlockDriverState *bs, BdrvCheckResult *result) { - return qcow2_check_refcounts(bs); + return qcow2_check_refcounts(bs, result); } #if 0 @@ -1071,7 +1275,7 @@ int64_t nb_clusters, k, k1, size; int refcount; - size = bdrv_getlength(s->hd); + size = bdrv_getlength(bs->file); nb_clusters = size_to_clusters(s, size); for(k = 0; k < nb_clusters;) { k1 = k; @@ -1079,40 +1283,43 @@ k++; while (k < nb_clusters && get_refcount(bs, k) == refcount) k++; - printf("%lld: refcount=%d nb=%lld\n", k, refcount, k - k1); + printf("%" PRId64 ": refcount=%d nb=%" PRId64 "\n", k, refcount, + k - k1); } } #endif -static int qcow_save_vmstate(BlockDriverState *bs, const uint8_t *buf, - int64_t pos, int size) +static int qcow2_save_vmstate(BlockDriverState *bs, const uint8_t *buf, + int64_t pos, int size) { BDRVQcowState *s = bs->opaque; int growable = bs->growable; int ret; + BLKDBG_EVENT(bs->file, BLKDBG_VMSTATE_SAVE); bs->growable = 1; - ret = bdrv_pwrite(bs, qcow_vm_state_offset(s) + pos, buf, size); + ret = bdrv_pwrite(bs, qcow2_vm_state_offset(s) + pos, buf, size); bs->growable = growable; return ret; } -static int qcow_load_vmstate(BlockDriverState *bs, uint8_t *buf, - int64_t pos, int size) +static int qcow2_load_vmstate(BlockDriverState *bs, uint8_t *buf, + int64_t pos, int size) { BDRVQcowState *s = bs->opaque; int growable = bs->growable; int ret; + BLKDBG_EVENT(bs->file, BLKDBG_VMSTATE_LOAD); bs->growable = 1; - ret = bdrv_pread(bs, qcow_vm_state_offset(s) + pos, buf, size); + ret = bdrv_pread(bs, qcow2_vm_state_offset(s) + pos, buf, size); bs->growable = growable; return ret; } -static QEMUOptionParameter qcow_create_options[] = { +static QEMUOptionParameter qcow2_create_options[] = { { .name = BLOCK_OPT_SIZE, .type = OPT_SIZE, @@ -1147,32 +1354,39 @@ }; static BlockDriver bdrv_qcow2 = { - .format_name = "qcow2", - .instance_size = sizeof(BDRVQcowState), - .bdrv_probe = qcow_probe, - .bdrv_open = qcow_open, - .bdrv_close = qcow_close, - .bdrv_create = qcow_create, - .bdrv_flush = qcow_flush, - .bdrv_is_allocated = qcow_is_allocated, - .bdrv_set_key = qcow_set_key, - .bdrv_make_empty = qcow_make_empty, - - .bdrv_aio_readv = qcow_aio_readv, - .bdrv_aio_writev = qcow_aio_writev, - .bdrv_write_compressed = qcow_write_compressed, + .format_name = "qcow2", + .instance_size = sizeof(BDRVQcowState), + .bdrv_probe = qcow2_probe, + .bdrv_open = qcow2_open, + .bdrv_close = qcow2_close, + .bdrv_create = qcow2_create, + .bdrv_flush = qcow2_flush, + .bdrv_is_allocated = qcow2_is_allocated, + .bdrv_set_key = qcow2_set_key, + .bdrv_make_empty = qcow2_make_empty, + + .bdrv_aio_readv = qcow2_aio_readv, + .bdrv_aio_writev = qcow2_aio_writev, + .bdrv_aio_flush = qcow2_aio_flush, + + .bdrv_discard = qcow2_discard, + .bdrv_truncate = qcow2_truncate, + .bdrv_write_compressed = qcow2_write_compressed, .bdrv_snapshot_create = qcow2_snapshot_create, .bdrv_snapshot_goto = qcow2_snapshot_goto, .bdrv_snapshot_delete = qcow2_snapshot_delete, .bdrv_snapshot_list = qcow2_snapshot_list, - .bdrv_get_info = qcow_get_info, + .bdrv_snapshot_load_tmp = qcow2_snapshot_load_tmp, + .bdrv_get_info = qcow2_get_info, + + .bdrv_save_vmstate = qcow2_save_vmstate, + .bdrv_load_vmstate = qcow2_load_vmstate, - .bdrv_save_vmstate = qcow_save_vmstate, - .bdrv_load_vmstate = qcow_load_vmstate, + .bdrv_change_backing_file = qcow2_change_backing_file, - .create_options = qcow_create_options, - .bdrv_check = qcow_check, + .create_options = qcow2_create_options, + .bdrv_check = qcow2_check, }; static void bdrv_qcow2_init(void) diff -Nru qemu-kvm-0.12.5+noroms/block/qcow2-cache.c qemu-kvm-0.14.1/block/qcow2-cache.c --- qemu-kvm-0.12.5+noroms/block/qcow2-cache.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/block/qcow2-cache.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,314 @@ +/* + * L2/refcount table cache for the QCOW2 format + * + * Copyright (c) 2010 Kevin Wolf + * + * 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 "block_int.h" +#include "qemu-common.h" +#include "qcow2.h" + +typedef struct Qcow2CachedTable { + void* table; + int64_t offset; + bool dirty; + int cache_hits; + int ref; +} Qcow2CachedTable; + +struct Qcow2Cache { + Qcow2CachedTable* entries; + struct Qcow2Cache* depends; + int size; + bool depends_on_flush; + bool writethrough; +}; + +Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables, + bool writethrough) +{ + BDRVQcowState *s = bs->opaque; + Qcow2Cache *c; + int i; + + c = qemu_mallocz(sizeof(*c)); + c->size = num_tables; + c->entries = qemu_mallocz(sizeof(*c->entries) * num_tables); + c->writethrough = writethrough; + + for (i = 0; i < c->size; i++) { + c->entries[i].table = qemu_blockalign(bs, s->cluster_size); + } + + return c; +} + +int qcow2_cache_destroy(BlockDriverState* bs, Qcow2Cache *c) +{ + int i; + + for (i = 0; i < c->size; i++) { + assert(c->entries[i].ref == 0); + qemu_vfree(c->entries[i].table); + } + + qemu_free(c->entries); + qemu_free(c); + + return 0; +} + +static int qcow2_cache_flush_dependency(BlockDriverState *bs, Qcow2Cache *c) +{ + int ret; + + ret = qcow2_cache_flush(bs, c->depends); + if (ret < 0) { + return ret; + } + + c->depends = NULL; + c->depends_on_flush = false; + + return 0; +} + +static int qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i) +{ + BDRVQcowState *s = bs->opaque; + int ret = 0; + + if (!c->entries[i].dirty || !c->entries[i].offset) { + return 0; + } + + if (c->depends) { + ret = qcow2_cache_flush_dependency(bs, c); + } else if (c->depends_on_flush) { + ret = bdrv_flush(bs->file); + if (ret >= 0) { + c->depends_on_flush = false; + } + } + + if (ret < 0) { + return ret; + } + + if (c == s->refcount_block_cache) { + BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_UPDATE_PART); + } else if (c == s->l2_table_cache) { + BLKDBG_EVENT(bs->file, BLKDBG_L2_UPDATE); + } + + ret = bdrv_pwrite(bs->file, c->entries[i].offset, c->entries[i].table, + s->cluster_size); + if (ret < 0) { + return ret; + } + + c->entries[i].dirty = false; + + return 0; +} + +int qcow2_cache_flush(BlockDriverState *bs, Qcow2Cache *c) +{ + int result = 0; + int ret; + int i; + + for (i = 0; i < c->size; i++) { + ret = qcow2_cache_entry_flush(bs, c, i); + if (ret < 0 && result != -ENOSPC) { + result = ret; + } + } + + if (result == 0) { + ret = bdrv_flush(bs->file); + if (ret < 0) { + result = ret; + } + } + + return result; +} + +int qcow2_cache_set_dependency(BlockDriverState *bs, Qcow2Cache *c, + Qcow2Cache *dependency) +{ + int ret; + + if (dependency->depends) { + ret = qcow2_cache_flush_dependency(bs, dependency); + if (ret < 0) { + return ret; + } + } + + if (c->depends && (c->depends != dependency)) { + ret = qcow2_cache_flush_dependency(bs, c); + if (ret < 0) { + return ret; + } + } + + c->depends = dependency; + return 0; +} + +void qcow2_cache_depends_on_flush(Qcow2Cache *c) +{ + c->depends_on_flush = true; +} + +static int qcow2_cache_find_entry_to_replace(Qcow2Cache *c) +{ + int i; + int min_count = INT_MAX; + int min_index = -1; + + + for (i = 0; i < c->size; i++) { + if (c->entries[i].ref) { + continue; + } + + if (c->entries[i].cache_hits < min_count) { + min_index = i; + min_count = c->entries[i].cache_hits; + } + + /* Give newer hits priority */ + /* TODO Check how to optimize the replacement strategy */ + c->entries[i].cache_hits /= 2; + } + + if (min_index == -1) { + /* This can't happen in current synchronous code, but leave the check + * here as a reminder for whoever starts using AIO with the cache */ + abort(); + } + return min_index; +} + +static int qcow2_cache_do_get(BlockDriverState *bs, Qcow2Cache *c, + uint64_t offset, void **table, bool read_from_disk) +{ + BDRVQcowState *s = bs->opaque; + int i; + int ret; + + /* Check if the table is already cached */ + for (i = 0; i < c->size; i++) { + if (c->entries[i].offset == offset) { + goto found; + } + } + + /* If not, write a table back and replace it */ + i = qcow2_cache_find_entry_to_replace(c); + if (i < 0) { + return i; + } + + ret = qcow2_cache_entry_flush(bs, c, i); + if (ret < 0) { + return ret; + } + + c->entries[i].offset = 0; + if (read_from_disk) { + if (c == s->l2_table_cache) { + BLKDBG_EVENT(bs->file, BLKDBG_L2_LOAD); + } + + ret = bdrv_pread(bs->file, offset, c->entries[i].table, s->cluster_size); + if (ret < 0) { + return ret; + } + } + + /* Give the table some hits for the start so that it won't be replaced + * immediately. The number 32 is completely arbitrary. */ + c->entries[i].cache_hits = 32; + c->entries[i].offset = offset; + + /* And return the right table */ +found: + c->entries[i].cache_hits++; + c->entries[i].ref++; + *table = c->entries[i].table; + return 0; +} + +int qcow2_cache_get(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, + void **table) +{ + return qcow2_cache_do_get(bs, c, offset, table, true); +} + +int qcow2_cache_get_empty(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, + void **table) +{ + return qcow2_cache_do_get(bs, c, offset, table, false); +} + +int qcow2_cache_put(BlockDriverState *bs, Qcow2Cache *c, void **table) +{ + int i; + + for (i = 0; i < c->size; i++) { + if (c->entries[i].table == *table) { + goto found; + } + } + return -ENOENT; + +found: + c->entries[i].ref--; + *table = NULL; + + assert(c->entries[i].ref >= 0); + + if (c->writethrough) { + return qcow2_cache_entry_flush(bs, c, i); + } else { + return 0; + } +} + +void qcow2_cache_entry_mark_dirty(Qcow2Cache *c, void *table) +{ + int i; + + for (i = 0; i < c->size; i++) { + if (c->entries[i].table == table) { + goto found; + } + } + abort(); + +found: + c->entries[i].dirty = true; +} + diff -Nru qemu-kvm-0.12.5+noroms/block/qcow2-cluster.c qemu-kvm-0.14.1/block/qcow2-cluster.c --- qemu-kvm-0.12.5+noroms/block/qcow2-cluster.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/block/qcow2-cluster.c 2011-05-11 13:29:46.000000000 +0000 @@ -28,7 +28,7 @@ #include "block_int.h" #include "block/qcow2.h" -int qcow2_grow_l1_table(BlockDriverState *bs, int min_size) +int qcow2_grow_l1_table(BlockDriverState *bs, int min_size, bool exact_size) { BDRVQcowState *s = bs->opaque; int new_l1_size, new_l1_size2, ret, i; @@ -36,15 +36,22 @@ int64_t new_l1_table_offset; uint8_t data[12]; - new_l1_size = s->l1_size; - if (min_size <= new_l1_size) + if (min_size <= s->l1_size) return 0; - if (new_l1_size == 0) { - new_l1_size = 1; - } - while (min_size > new_l1_size) { - new_l1_size = (new_l1_size * 3 + 1) / 2; + + if (exact_size) { + new_l1_size = min_size; + } else { + /* Bump size up to reduce the number of times we have to grow */ + new_l1_size = s->l1_size; + if (new_l1_size == 0) { + new_l1_size = 1; + } + while (min_size > new_l1_size) { + new_l1_size = (new_l1_size * 3 + 1) / 2; + } } + #ifdef DEBUG_ALLOC2 printf("grow l1_table from %d to %d\n", s->l1_size, new_l1_size); #endif @@ -54,24 +61,32 @@ memcpy(new_l1_table, s->l1_table, s->l1_size * sizeof(uint64_t)); /* write new table (align to cluster) */ + BLKDBG_EVENT(bs->file, BLKDBG_L1_GROW_ALLOC_TABLE); new_l1_table_offset = qcow2_alloc_clusters(bs, new_l1_size2); if (new_l1_table_offset < 0) { qemu_free(new_l1_table); return new_l1_table_offset; } + ret = qcow2_cache_flush(bs, s->refcount_block_cache); + if (ret < 0) { + return ret; + } + + BLKDBG_EVENT(bs->file, BLKDBG_L1_GROW_WRITE_TABLE); for(i = 0; i < s->l1_size; i++) new_l1_table[i] = cpu_to_be64(new_l1_table[i]); - ret = bdrv_pwrite_sync(s->hd, new_l1_table_offset, new_l1_table, new_l1_size2); + ret = bdrv_pwrite_sync(bs->file, new_l1_table_offset, new_l1_table, new_l1_size2); if (ret < 0) goto fail; for(i = 0; i < s->l1_size; i++) new_l1_table[i] = be64_to_cpu(new_l1_table[i]); /* set new table */ + BLKDBG_EVENT(bs->file, BLKDBG_L1_GROW_ACTIVATE_TABLE); cpu_to_be32w((uint32_t*)data, new_l1_size); - cpu_to_be64w((uint64_t*)(data + 4), new_l1_table_offset); - ret = bdrv_pwrite_sync(s->hd, offsetof(QCowHeader, l1_size), data,sizeof(data)); + cpu_to_be64wu((uint64_t*)(data + 4), new_l1_table_offset); + ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, l1_size), data,sizeof(data)); if (ret < 0) { goto fail; } @@ -87,63 +102,6 @@ return ret; } -void qcow2_l2_cache_reset(BlockDriverState *bs) -{ - BDRVQcowState *s = bs->opaque; - - memset(s->l2_cache, 0, s->l2_size * L2_CACHE_SIZE * sizeof(uint64_t)); - memset(s->l2_cache_offsets, 0, L2_CACHE_SIZE * sizeof(uint64_t)); - memset(s->l2_cache_counts, 0, L2_CACHE_SIZE * sizeof(uint32_t)); -} - -static inline int l2_cache_new_entry(BlockDriverState *bs) -{ - BDRVQcowState *s = bs->opaque; - uint32_t min_count; - int min_index, i; - - /* find a new entry in the least used one */ - min_index = 0; - min_count = 0xffffffff; - for(i = 0; i < L2_CACHE_SIZE; i++) { - if (s->l2_cache_counts[i] < min_count) { - min_count = s->l2_cache_counts[i]; - min_index = i; - } - } - return min_index; -} - -/* - * seek_l2_table - * - * seek l2_offset in the l2_cache table - * if not found, return NULL, - * if found, - * increments the l2 cache hit count of the entry, - * if counter overflow, divide by two all counters - * return the pointer to the l2 cache entry - * - */ - -static uint64_t *seek_l2_table(BDRVQcowState *s, uint64_t l2_offset) -{ - int i, j; - - for(i = 0; i < L2_CACHE_SIZE; i++) { - if (l2_offset == s->l2_cache_offsets[i]) { - /* increment the hit count */ - if (++s->l2_cache_counts[i] == 0xffffffff) { - for(j = 0; j < L2_CACHE_SIZE; j++) { - s->l2_cache_counts[j] >>= 1; - } - } - return s->l2_cache + (i << s->l2_bits); - } - } - return NULL; -} - /* * l2_load * @@ -154,29 +112,15 @@ * the image file failed. */ -static uint64_t *l2_load(BlockDriverState *bs, uint64_t l2_offset) +static int l2_load(BlockDriverState *bs, uint64_t l2_offset, + uint64_t **l2_table) { BDRVQcowState *s = bs->opaque; - int min_index; - uint64_t *l2_table; - - /* seek if the table for the given offset is in the cache */ + int ret; - l2_table = seek_l2_table(s, l2_offset); - if (l2_table != NULL) - return l2_table; - - /* not found: load a new entry in the least used one */ - - min_index = l2_cache_new_entry(bs); - l2_table = s->l2_cache + (min_index << s->l2_bits); - if (bdrv_pread(s->hd, l2_offset, l2_table, s->l2_size * sizeof(uint64_t)) != - s->l2_size * sizeof(uint64_t)) - return NULL; - s->l2_cache_offsets[min_index] = l2_offset; - s->l2_cache_counts[min_index] = 1; + ret = qcow2_cache_get(bs, s->l2_table_cache, l2_offset, (void**) l2_table); - return l2_table; + return ret; } /* @@ -184,8 +128,9 @@ * and we really don't want bdrv_pread to perform a read-modify-write) */ #define L1_ENTRIES_PER_SECTOR (512 / 8) -static int write_l1_entry(BDRVQcowState *s, int l1_index) +static int write_l1_entry(BlockDriverState *bs, int l1_index) { + BDRVQcowState *s = bs->opaque; uint64_t buf[L1_ENTRIES_PER_SECTOR]; int l1_start_index; int i, ret; @@ -195,7 +140,8 @@ buf[i] = cpu_to_be64(s->l1_table[l1_start_index + i]); } - ret = bdrv_pwrite_sync(s->hd, s->l1_table_offset + 8 * l1_start_index, + BLKDBG_EVENT(bs->file, BLKDBG_L1_UPDATE); + ret = bdrv_pwrite_sync(bs->file, s->l1_table_offset + 8 * l1_start_index, buf, sizeof(buf)); if (ret < 0) { return ret; @@ -214,10 +160,9 @@ * */ -static uint64_t *l2_allocate(BlockDriverState *bs, int l1_index) +static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table) { BDRVQcowState *s = bs->opaque; - int min_index; uint64_t old_l2_offset; uint64_t *l2_table; int64_t l2_offset; @@ -229,48 +174,68 @@ l2_offset = qcow2_alloc_clusters(bs, s->l2_size * sizeof(uint64_t)); if (l2_offset < 0) { - return NULL; + return l2_offset; + } + + ret = qcow2_cache_flush(bs, s->refcount_block_cache); + if (ret < 0) { + goto fail; } /* allocate a new entry in the l2 cache */ - min_index = l2_cache_new_entry(bs); - l2_table = s->l2_cache + (min_index << s->l2_bits); + ret = qcow2_cache_get_empty(bs, s->l2_table_cache, l2_offset, (void**) table); + if (ret < 0) { + return ret; + } + + l2_table = *table; if (old_l2_offset == 0) { /* if there was no old l2 table, clear the new table */ memset(l2_table, 0, s->l2_size * sizeof(uint64_t)); } else { + uint64_t* old_table; + /* if there was an old l2 table, read it from the disk */ - if (bdrv_pread(s->hd, old_l2_offset, - l2_table, s->l2_size * sizeof(uint64_t)) != - s->l2_size * sizeof(uint64_t)) + BLKDBG_EVENT(bs->file, BLKDBG_L2_ALLOC_COW_READ); + ret = qcow2_cache_get(bs, s->l2_table_cache, old_l2_offset, + (void**) &old_table); + if (ret < 0) { + goto fail; + } + + memcpy(l2_table, old_table, s->cluster_size); + + ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &old_table); + if (ret < 0) { goto fail; + } } + /* write the l2 table to the file */ - ret = bdrv_pwrite_sync(s->hd, l2_offset, l2_table, - s->l2_size * sizeof(uint64_t)); + BLKDBG_EVENT(bs->file, BLKDBG_L2_ALLOC_WRITE); + + qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table); + ret = qcow2_cache_flush(bs, s->l2_table_cache); if (ret < 0) { goto fail; } /* update the L1 entry */ s->l1_table[l1_index] = l2_offset | QCOW_OFLAG_COPIED; - if (write_l1_entry(s, l1_index) < 0) { + ret = write_l1_entry(bs, l1_index); + if (ret < 0) { goto fail; } - /* update the l2 cache entry */ - - s->l2_cache_offsets[min_index] = l2_offset; - s->l2_cache_counts[min_index] = 1; - - return l2_table; + *table = l2_table; + return 0; fail: + qcow2_cache_put(bs, s->l2_table_cache, (void**) table); s->l1_table[l1_index] = old_l2_offset; - qcow2_l2_cache_reset(bs); - return NULL; + return ret; } static int count_contiguous_clusters(uint64_t nb_clusters, int cluster_size, @@ -325,22 +290,35 @@ } -static int qcow_read(BlockDriverState *bs, int64_t sector_num, - uint8_t *buf, int nb_sectors) +static int qcow2_read(BlockDriverState *bs, int64_t sector_num, + uint8_t *buf, int nb_sectors) { BDRVQcowState *s = bs->opaque; int ret, index_in_cluster, n, n1; uint64_t cluster_offset; + struct iovec iov; + QEMUIOVector qiov; while (nb_sectors > 0) { n = nb_sectors; - cluster_offset = qcow2_get_cluster_offset(bs, sector_num << 9, &n); + + ret = qcow2_get_cluster_offset(bs, sector_num << 9, &n, + &cluster_offset); + if (ret < 0) { + return ret; + } + index_in_cluster = sector_num & (s->cluster_sectors - 1); if (!cluster_offset) { if (bs->backing_hd) { /* read from the base image */ - n1 = qcow2_backing_read1(bs->backing_hd, sector_num, buf, n); + iov.iov_base = buf; + iov.iov_len = n * 512; + qemu_iovec_init_external(&qiov, &iov, 1); + + n1 = qcow2_backing_read1(bs->backing_hd, &qiov, sector_num, n); if (n1 > 0) { + BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING); ret = bdrv_read(bs->backing_hd, sector_num, buf, n1); if (ret < 0) return -1; @@ -349,11 +327,12 @@ memset(buf, 0, 512 * n); } } else if (cluster_offset & QCOW_OFLAG_COMPRESSED) { - if (qcow2_decompress_cluster(s, cluster_offset) < 0) + if (qcow2_decompress_cluster(bs, cluster_offset) < 0) return -1; memcpy(buf, s->cluster_cache + index_in_cluster * 512, 512 * n); } else { - ret = bdrv_pread(s->hd, cluster_offset + index_in_cluster * 512, buf, n * 512); + BLKDBG_EVENT(bs->file, BLKDBG_READ); + ret = bdrv_pread(bs->file, cluster_offset + index_in_cluster * 512, buf, n * 512); if (ret != n * 512) return -1; if (s->crypt_method) { @@ -377,7 +356,8 @@ n = n_end - n_start; if (n <= 0) return 0; - ret = qcow_read(bs, start_sect + n_start, s->cluster_data, n); + BLKDBG_EVENT(bs->file, BLKDBG_COW_READ); + ret = qcow2_read(bs, start_sect + n_start, s->cluster_data, n); if (ret < 0) return ret; if (s->crypt_method) { @@ -386,7 +366,8 @@ s->cluster_data, n, 1, &s->aes_encrypt_key); } - ret = bdrv_write_sync(s->hd, (cluster_offset >> 9) + n_start, + BLKDBG_EVENT(bs->file, BLKDBG_COW_WRITE); + ret = bdrv_write(bs->file, (cluster_offset >> 9) + n_start, s->cluster_data, n); if (ret < 0) return ret; @@ -397,28 +378,29 @@ /* * get_cluster_offset * - * For a given offset of the disk image, return cluster offset in - * qcow2 file. + * For a given offset of the disk image, find the cluster offset in + * qcow2 file. The offset is stored in *cluster_offset. * * on entry, *num is the number of contiguous clusters we'd like to * access following offset. * * on exit, *num is the number of contiguous clusters we can read. * - * Return 1, if the offset is found - * Return 0, otherwise. + * Return 0, if the offset is found + * Return -errno, otherwise. * */ -uint64_t qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, - int *num) +int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, + int *num, uint64_t *cluster_offset) { BDRVQcowState *s = bs->opaque; unsigned int l1_index, l2_index; - uint64_t l2_offset, *l2_table, cluster_offset; + uint64_t l2_offset, *l2_table; int l1_bits, c; unsigned int index_in_cluster, nb_clusters; uint64_t nb_available, nb_needed; + int ret; index_in_cluster = (offset >> 9) & (s->cluster_sectors - 1); nb_needed = *num + index_in_cluster; @@ -439,7 +421,7 @@ nb_needed = nb_available; } - cluster_offset = 0; + *cluster_offset = 0; /* seek the the l2 offset in the l1 table */ @@ -457,17 +439,18 @@ /* load the l2 table in memory */ l2_offset &= ~QCOW_OFLAG_COPIED; - l2_table = l2_load(bs, l2_offset); - if (l2_table == NULL) - return 0; + ret = l2_load(bs, l2_offset, &l2_table); + if (ret < 0) { + return ret; + } /* find the cluster offset for the given disk offset */ l2_index = (offset >> s->cluster_bits) & (s->l2_size - 1); - cluster_offset = be64_to_cpu(l2_table[l2_index]); + *cluster_offset = be64_to_cpu(l2_table[l2_index]); nb_clusters = size_to_clusters(s, nb_needed << 9); - if (!cluster_offset) { + if (!*cluster_offset) { /* how many empty clusters ? */ c = count_contiguous_free_clusters(nb_clusters, &l2_table[l2_index]); } else { @@ -476,6 +459,8 @@ &l2_table[l2_index], 0, QCOW_OFLAG_COPIED); } + qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); + nb_available = (c * s->cluster_sectors); out: if (nb_available > nb_needed) @@ -483,7 +468,8 @@ *num = nb_available - index_in_cluster; - return cluster_offset & ~QCOW_OFLAG_COPIED; + *cluster_offset &=~QCOW_OFLAG_COPIED; + return 0; } /* @@ -504,14 +490,15 @@ { BDRVQcowState *s = bs->opaque; unsigned int l1_index, l2_index; - uint64_t l2_offset, *l2_table; + uint64_t l2_offset; + uint64_t *l2_table = NULL; int ret; /* seek the the l2 offset in the l1 table */ l1_index = offset >> (s->l2_bits + s->cluster_bits); if (l1_index >= s->l1_size) { - ret = qcow2_grow_l1_table(bs, l1_index + 1); + ret = qcow2_grow_l1_table(bs, l1_index + 1, false); if (ret < 0) { return ret; } @@ -523,16 +510,20 @@ if (l2_offset & QCOW_OFLAG_COPIED) { /* load the l2 table in memory */ l2_offset &= ~QCOW_OFLAG_COPIED; - l2_table = l2_load(bs, l2_offset); - if (l2_table == NULL) { - return -EIO; + ret = l2_load(bs, l2_offset, &l2_table); + if (ret < 0) { + return ret; } } else { - if (l2_offset) + /* First allocate a new L2 table (and do COW if needed) */ + ret = l2_allocate(bs, l1_index, &l2_table); + if (ret < 0) { + return ret; + } + + /* Then decrease the refcount of the old table */ + if (l2_offset) { qcow2_free_clusters(bs, l2_offset, s->l2_size * sizeof(uint64_t)); - l2_table = l2_allocate(bs, l1_index); - if (l2_table == NULL) { - return -EIO; } l2_offset = s->l1_table[l1_index] & ~QCOW_OFLAG_COPIED; } @@ -585,6 +576,7 @@ cluster_offset = qcow2_alloc_bytes(bs, compressed_size); if (cluster_offset < 0) { + qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); return 0; } @@ -598,37 +590,15 @@ /* compressed clusters never have the copied flag */ + BLKDBG_EVENT(bs->file, BLKDBG_L2_UPDATE_COMPRESSED); + qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table); l2_table[l2_index] = cpu_to_be64(cluster_offset); - if (bdrv_pwrite_sync(s->hd, - l2_offset + l2_index * sizeof(uint64_t), - l2_table + l2_index, - sizeof(uint64_t)) < 0) - return 0; - - return cluster_offset; -} - -/* - * Write L2 table updates to disk, writing whole sectors to avoid a - * read-modify-write in bdrv_pwrite - */ -#define L2_ENTRIES_PER_SECTOR (512 / 8) -static int write_l2_entries(BDRVQcowState *s, uint64_t *l2_table, - uint64_t l2_offset, int l2_index, int num) -{ - int l2_start_index = l2_index & ~(L1_ENTRIES_PER_SECTOR - 1); - int start_offset = (8 * l2_index) & ~511; - int end_offset = (8 * (l2_index + num) + 511) & ~511; - size_t len = end_offset - start_offset; - int ret; - - ret = bdrv_pwrite_sync(s->hd, l2_offset + start_offset, - &l2_table[l2_start_index], len); + ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); if (ret < 0) { - return ret; + return 0; } - return 0; + return cluster_offset; } int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m) @@ -637,6 +607,7 @@ int i, j = 0, l2_index, ret; uint64_t *old_cluster, start_sect, l2_offset, *l2_table; uint64_t cluster_offset = m->cluster_offset; + bool cow = false; if (m->nb_clusters == 0) return 0; @@ -646,6 +617,7 @@ /* copy content of unmodified sectors */ start_sect = (m->offset & ~(s->cluster_size - 1)) >> 9; if (m->n_start) { + cow = true; ret = copy_sectors(bs, start_sect, cluster_offset, 0, m->n_start); if (ret < 0) goto err; @@ -653,17 +625,30 @@ if (m->nb_available & (s->cluster_sectors - 1)) { uint64_t end = m->nb_available & ~(uint64_t)(s->cluster_sectors - 1); + cow = true; ret = copy_sectors(bs, start_sect + end, cluster_offset + (end << 9), m->nb_available - end, s->cluster_sectors); if (ret < 0) goto err; } - /* update L2 table */ + /* + * Update L2 table. + * + * Before we update the L2 table to actually point to the new cluster, we + * need to be sure that the refcounts have been increased and COW was + * handled. + */ + if (cow) { + qcow2_cache_depends_on_flush(s->l2_table_cache); + } + + qcow2_cache_set_dependency(bs, s->l2_table_cache, s->refcount_block_cache); ret = get_cluster_table(bs, m->offset, &l2_table, &l2_offset, &l2_index); if (ret < 0) { goto err; } + qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table); for (i = 0; i < m->nb_clusters; i++) { /* if two concurrent writes happen to the same unallocated cluster @@ -679,15 +664,22 @@ (i << s->cluster_bits)) | QCOW_OFLAG_COPIED); } - ret = write_l2_entries(s, l2_table, l2_offset, l2_index, m->nb_clusters); + + ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); if (ret < 0) { - qcow2_l2_cache_reset(bs); goto err; } - for (i = 0; i < j; i++) - qcow2_free_any_clusters(bs, - be64_to_cpu(old_cluster[i]) & ~QCOW_OFLAG_COPIED, 1); + /* + * If this was a COW, we need to decrease the refcount of the old cluster. + * Also flush bs->file to get the right order for L2 and refcount update. + */ + if (j != 0) { + for (i = 0; i < j; i++) { + qcow2_free_any_clusters(bs, + be64_to_cpu(old_cluster[i]) & ~QCOW_OFLAG_COPIED, 1); + } + } ret = 0; err: @@ -804,7 +796,8 @@ m->depends_on = old_alloc; m->nb_clusters = 0; *num = 0; - return 0; + ret = 0; + goto fail; } } } @@ -820,7 +813,8 @@ cluster_offset = qcow2_alloc_clusters(bs, nb_clusters * s->cluster_size); if (cluster_offset < 0) { QLIST_REMOVE(m, next_in_flight); - return cluster_offset; + ret = cluster_offset; + goto fail; } /* save info needed for meta data update */ @@ -829,12 +823,21 @@ m->nb_clusters = nb_clusters; out: + ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); + if (ret < 0) { + return ret; + } + m->nb_available = MIN(nb_clusters << (s->cluster_bits - 9), n_end); m->cluster_offset = cluster_offset; *num = m->nb_available - n_start; return 0; + +fail: + qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); + return ret; } static int decompress_buffer(uint8_t *out_buf, int out_buf_size, @@ -864,8 +867,9 @@ return 0; } -int qcow2_decompress_cluster(BDRVQcowState *s, uint64_t cluster_offset) +int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset) { + BDRVQcowState *s = bs->opaque; int ret, csize, nb_csectors, sector_offset; uint64_t coffset; @@ -874,15 +878,98 @@ nb_csectors = ((cluster_offset >> s->csize_shift) & s->csize_mask) + 1; sector_offset = coffset & 511; csize = nb_csectors * 512 - sector_offset; - ret = bdrv_read(s->hd, coffset >> 9, s->cluster_data, nb_csectors); + BLKDBG_EVENT(bs->file, BLKDBG_READ_COMPRESSED); + ret = bdrv_read(bs->file, coffset >> 9, s->cluster_data, nb_csectors); if (ret < 0) { - return -1; + return ret; } if (decompress_buffer(s->cluster_cache, s->cluster_size, s->cluster_data + sector_offset, csize) < 0) { - return -1; + return -EIO; } s->cluster_cache_offset = coffset; } return 0; } + +/* + * This discards as many clusters of nb_clusters as possible at once (i.e. + * all clusters in the same L2 table) and returns the number of discarded + * clusters. + */ +static int discard_single_l2(BlockDriverState *bs, uint64_t offset, + unsigned int nb_clusters) +{ + BDRVQcowState *s = bs->opaque; + uint64_t l2_offset, *l2_table; + int l2_index; + int ret; + int i; + + ret = get_cluster_table(bs, offset, &l2_table, &l2_offset, &l2_index); + if (ret < 0) { + return ret; + } + + /* Limit nb_clusters to one L2 table */ + nb_clusters = MIN(nb_clusters, s->l2_size - l2_index); + + for (i = 0; i < nb_clusters; i++) { + uint64_t old_offset; + + old_offset = be64_to_cpu(l2_table[l2_index + i]); + old_offset &= ~QCOW_OFLAG_COPIED; + + if (old_offset == 0) { + continue; + } + + /* First remove L2 entries */ + qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table); + l2_table[l2_index + i] = cpu_to_be64(0); + + /* Then decrease the refcount */ + qcow2_free_any_clusters(bs, old_offset, 1); + } + + ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); + if (ret < 0) { + return ret; + } + + return nb_clusters; +} + +int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset, + int nb_sectors) +{ + BDRVQcowState *s = bs->opaque; + uint64_t end_offset; + unsigned int nb_clusters; + int ret; + + end_offset = offset + (nb_sectors << BDRV_SECTOR_BITS); + + /* Round start up and end down */ + offset = align_offset(offset, s->cluster_size); + end_offset &= ~(s->cluster_size - 1); + + if (offset > end_offset) { + return 0; + } + + nb_clusters = size_to_clusters(s, end_offset - offset); + + /* Each L2 table is handled by its own loop iteration */ + while (nb_clusters > 0) { + ret = discard_single_l2(bs, offset, nb_clusters); + if (ret < 0) { + return ret; + } + + nb_clusters -= ret; + offset += (ret * s->cluster_size); + } + + return 0; +} diff -Nru qemu-kvm-0.12.5+noroms/block/qcow2.h qemu-kvm-0.14.1/block/qcow2.h --- qemu-kvm-0.12.5+noroms/block/qcow2.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/block/qcow2.h 2011-05-11 13:29:46.000000000 +0000 @@ -51,6 +51,9 @@ #define L2_CACHE_SIZE 16 +/* Must be at least 4 to cover all cases of refcount table growth */ +#define REFCOUNT_CACHE_SIZE 4 + typedef struct QCowHeader { uint32_t magic; uint32_t version; @@ -78,8 +81,10 @@ uint64_t vm_clock_nsec; } QCowSnapshot; +struct Qcow2Cache; +typedef struct Qcow2Cache Qcow2Cache; + typedef struct BDRVQcowState { - BlockDriverState *hd; int cluster_bits; int cluster_size; int cluster_sectors; @@ -92,9 +97,10 @@ uint64_t cluster_offset_mask; uint64_t l1_table_offset; uint64_t *l1_table; - uint64_t *l2_cache; - uint64_t l2_cache_offsets[L2_CACHE_SIZE]; - uint32_t l2_cache_counts[L2_CACHE_SIZE]; + + Qcow2Cache* l2_table_cache; + Qcow2Cache* refcount_block_cache; + uint8_t *cluster_cache; uint8_t *cluster_data; uint64_t cluster_cache_offset; @@ -103,8 +109,6 @@ uint64_t *refcount_table; uint64_t refcount_table_offset; uint32_t refcount_table_size; - uint64_t refcount_block_cache_offset; - uint16_t *refcount_block_cache; int64_t free_cluster_index; int64_t free_byte_offset; @@ -150,6 +154,12 @@ return (size + (s->cluster_size - 1)) >> s->cluster_bits; } +static inline int size_to_l1(BDRVQcowState *s, int64_t size) +{ + int shift = s->cluster_bits + s->l2_bits; + return (size + (1ULL << shift) - 1) >> shift; +} + static inline int64_t align_offset(int64_t offset, int n) { offset = (offset + n - 1) & ~(n - 1); @@ -160,8 +170,8 @@ // FIXME Need qcow2_ prefix to global functions /* qcow2.c functions */ -int qcow2_backing_read1(BlockDriverState *bs, - int64_t sector_num, uint8_t *buf, int nb_sectors); +int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov, + int64_t sector_num, int nb_sectors); /* qcow2-refcount.c functions */ int qcow2_refcount_init(BlockDriverState *bs); @@ -179,19 +189,19 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, int64_t l1_table_offset, int l1_size, int addend); -int qcow2_check_refcounts(BlockDriverState *bs); +int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res); /* qcow2-cluster.c functions */ -int qcow2_grow_l1_table(BlockDriverState *bs, int min_size); +int qcow2_grow_l1_table(BlockDriverState *bs, int min_size, bool exact_size); void qcow2_l2_cache_reset(BlockDriverState *bs); -int qcow2_decompress_cluster(BDRVQcowState *s, uint64_t cluster_offset); +int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset); void qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num, uint8_t *out_buf, const uint8_t *in_buf, int nb_sectors, int enc, const AES_KEY *key); -uint64_t qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, - int *num); +int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, + int *num, uint64_t *cluster_offset); int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset, int n_start, int n_end, int *num, QCowL2Meta *m); uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, @@ -199,14 +209,34 @@ int compressed_size); int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m); +int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset, + int nb_sectors); /* qcow2-snapshot.c functions */ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info); int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id); int qcow2_snapshot_delete(BlockDriverState *bs, const char *snapshot_id); int qcow2_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab); +int qcow2_snapshot_load_tmp(BlockDriverState *bs, const char *snapshot_name); void qcow2_free_snapshots(BlockDriverState *bs); int qcow2_read_snapshots(BlockDriverState *bs); +/* qcow2-cache.c functions */ +Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables, + bool writethrough); +int qcow2_cache_destroy(BlockDriverState* bs, Qcow2Cache *c); + +void qcow2_cache_entry_mark_dirty(Qcow2Cache *c, void *table); +int qcow2_cache_flush(BlockDriverState *bs, Qcow2Cache *c); +int qcow2_cache_set_dependency(BlockDriverState *bs, Qcow2Cache *c, + Qcow2Cache *dependency); +void qcow2_cache_depends_on_flush(Qcow2Cache *c); + +int qcow2_cache_get(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, + void **table); +int qcow2_cache_get_empty(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset, + void **table); +int qcow2_cache_put(BlockDriverState *bs, Qcow2Cache *c, void **table); + #endif diff -Nru qemu-kvm-0.12.5+noroms/block/qcow2-refcount.c qemu-kvm-0.14.1/block/qcow2-refcount.c --- qemu-kvm-0.12.5+noroms/block/qcow2-refcount.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/block/qcow2-refcount.c 2011-05-11 13:29:46.000000000 +0000 @@ -32,25 +32,6 @@ int addend); -static int cache_refcount_updates = 0; - -static int write_refcount_block(BDRVQcowState *s) -{ - size_t size = s->cluster_size; - - if (s->refcount_block_cache_offset == 0) { - return 0; - } - - if (bdrv_pwrite_sync(s->hd, s->refcount_block_cache_offset, - s->refcount_block_cache, size) < 0) - { - return -EIO; - } - - return 0; -} - /*********************************************************/ /* refcount handling */ @@ -59,11 +40,11 @@ BDRVQcowState *s = bs->opaque; int ret, refcount_table_size2, i; - s->refcount_block_cache = qemu_malloc(s->cluster_size); refcount_table_size2 = s->refcount_table_size * sizeof(uint64_t); s->refcount_table = qemu_malloc(refcount_table_size2); if (s->refcount_table_size > 0) { - ret = bdrv_pread(s->hd, s->refcount_table_offset, + BLKDBG_EVENT(bs->file, BLKDBG_REFTABLE_LOAD); + ret = bdrv_pread(bs->file, s->refcount_table_offset, s->refcount_table, refcount_table_size2); if (ret != refcount_table_size2) goto fail; @@ -78,34 +59,37 @@ void qcow2_refcount_close(BlockDriverState *bs) { BDRVQcowState *s = bs->opaque; - qemu_free(s->refcount_block_cache); qemu_free(s->refcount_table); } static int load_refcount_block(BlockDriverState *bs, - int64_t refcount_block_offset) + int64_t refcount_block_offset, + void **refcount_block) { BDRVQcowState *s = bs->opaque; int ret; - if (cache_refcount_updates) { - write_refcount_block(s); - } + BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_LOAD); + ret = qcow2_cache_get(bs, s->refcount_block_cache, refcount_block_offset, + refcount_block); - ret = bdrv_pread(s->hd, refcount_block_offset, s->refcount_block_cache, - s->cluster_size); - if (ret != s->cluster_size) - return -EIO; - s->refcount_block_cache_offset = refcount_block_offset; - return 0; + return ret; } +/* + * Returns the refcount of the cluster given by its index. Any non-negative + * return value is the refcount of the cluster, negative values are -errno + * and indicate an error. + */ static int get_refcount(BlockDriverState *bs, int64_t cluster_index) { BDRVQcowState *s = bs->opaque; int refcount_table_index, block_index; int64_t refcount_block_offset; + int ret; + uint16_t *refcount_block; + uint16_t refcount; refcount_table_index = cluster_index >> (s->cluster_bits - REFCOUNT_SHIFT); if (refcount_table_index >= s->refcount_table_size) @@ -113,14 +97,24 @@ refcount_block_offset = s->refcount_table[refcount_table_index]; if (!refcount_block_offset) return 0; - if (refcount_block_offset != s->refcount_block_cache_offset) { - /* better than nothing: return allocated if read error */ - if (load_refcount_block(bs, refcount_block_offset) < 0) - return 1; + + ret = qcow2_cache_get(bs, s->refcount_block_cache, refcount_block_offset, + (void**) &refcount_block); + if (ret < 0) { + return ret; } + block_index = cluster_index & ((1 << (s->cluster_bits - REFCOUNT_SHIFT)) - 1); - return be16_to_cpu(s->refcount_block_cache[block_index]); + refcount = be16_to_cpu(refcount_block[block_index]); + + ret = qcow2_cache_put(bs, s->refcount_block_cache, + (void**) &refcount_block); + if (ret < 0) { + return ret; + } + + return refcount; } /* @@ -156,14 +150,17 @@ * Loads a refcount block. If it doesn't exist yet, it is allocated first * (including growing the refcount table if needed). * - * Returns the offset of the refcount block on success or -errno in error case + * Returns 0 on success or -errno in error case */ -static int64_t alloc_refcount_block(BlockDriverState *bs, int64_t cluster_index) +static int alloc_refcount_block(BlockDriverState *bs, + int64_t cluster_index, uint16_t **refcount_block) { BDRVQcowState *s = bs->opaque; unsigned int refcount_table_index; int ret; + BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC); + /* Find the refcount block for the given cluster */ refcount_table_index = cluster_index >> (s->cluster_bits - REFCOUNT_SHIFT); @@ -174,13 +171,8 @@ /* If it's already there, we're done */ if (refcount_block_offset) { - if (refcount_block_offset != s->refcount_block_cache_offset) { - ret = load_refcount_block(bs, refcount_block_offset); - if (ret < 0) { - return ret; - } - } - return refcount_block_offset; + return load_refcount_block(bs, refcount_block_offset, + (void**) refcount_block); } } @@ -206,15 +198,16 @@ * refcount block into the cache */ - if (cache_refcount_updates) { - ret = write_refcount_block(s); - if (ret < 0) { - return ret; - } - } + *refcount_block = NULL; + + /* We write to the refcount table, so we might depend on L2 tables */ + qcow2_cache_flush(bs, s->l2_table_cache); /* Allocate the refcount block itself and mark it as used */ - uint64_t new_block = alloc_clusters_noref(bs, s->cluster_size); + int64_t new_block = alloc_clusters_noref(bs, s->cluster_size); + if (new_block < 0) { + return new_block; + } #ifdef DEBUG_ALLOC2 fprintf(stderr, "qcow2: Allocate refcount block %d for %" PRIx64 @@ -224,13 +217,18 @@ if (in_same_refcount_block(s, new_block, cluster_index << s->cluster_bits)) { /* Zero the new refcount block before updating it */ - memset(s->refcount_block_cache, 0, s->cluster_size); - s->refcount_block_cache_offset = new_block; + ret = qcow2_cache_get_empty(bs, s->refcount_block_cache, new_block, + (void**) refcount_block); + if (ret < 0) { + goto fail_block; + } + + memset(*refcount_block, 0, s->cluster_size); /* The block describes itself, need to update the cache */ int block_index = (new_block >> s->cluster_bits) & ((1 << (s->cluster_bits - REFCOUNT_SHIFT)) - 1); - s->refcount_block_cache[block_index] = cpu_to_be16(1); + (*refcount_block)[block_index] = cpu_to_be16(1); } else { /* Described somewhere else. This can recurse at most twice before we * arrive at a block that describes itself. */ @@ -239,15 +237,23 @@ goto fail_block; } + bdrv_flush(bs->file); + /* Initialize the new refcount block only after updating its refcount, * update_refcount uses the refcount cache itself */ - memset(s->refcount_block_cache, 0, s->cluster_size); - s->refcount_block_cache_offset = new_block; + ret = qcow2_cache_get_empty(bs, s->refcount_block_cache, new_block, + (void**) refcount_block); + if (ret < 0) { + goto fail_block; + } + + memset(*refcount_block, 0, s->cluster_size); } /* Now the new refcount block needs to be written to disk */ - ret = bdrv_pwrite_sync(s->hd, new_block, s->refcount_block_cache, - s->cluster_size); + BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_WRITE); + qcow2_cache_entry_mark_dirty(s->refcount_block_cache, *refcount_block); + ret = qcow2_cache_flush(bs, s->refcount_block_cache); if (ret < 0) { goto fail_block; } @@ -255,7 +261,8 @@ /* If the refcount table is big enough, just hook the block up there */ if (refcount_table_index < s->refcount_table_size) { uint64_t data64 = cpu_to_be64(new_block); - ret = bdrv_pwrite_sync(s->hd, + BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_HOOKUP); + ret = bdrv_pwrite_sync(bs->file, s->refcount_table_offset + refcount_table_index * sizeof(uint64_t), &data64, sizeof(data64)); if (ret < 0) { @@ -263,7 +270,12 @@ } s->refcount_table[refcount_table_index] = new_block; - return new_block; + return 0; + } + + ret = qcow2_cache_put(bs, s->refcount_block_cache, (void**) refcount_block); + if (ret < 0) { + goto fail_block; } /* @@ -277,6 +289,8 @@ * refcount table at once without producing an inconsistent state in * between. */ + BLKDBG_EVENT(bs->file, BLKDBG_REFTABLE_GROW); + /* Calculate the number of refcount blocks needed so far */ uint64_t refcount_block_clusters = 1 << (s->cluster_bits - REFCOUNT_SHIFT); uint64_t blocks_used = (s->free_cluster_index + @@ -332,7 +346,8 @@ } /* Write refcount blocks to disk */ - ret = bdrv_pwrite_sync(s->hd, meta_offset, new_blocks, + BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_WRITE_BLOCKS); + ret = bdrv_pwrite_sync(bs->file, meta_offset, new_blocks, blocks_clusters * s->cluster_size); qemu_free(new_blocks); if (ret < 0) { @@ -344,7 +359,8 @@ cpu_to_be64s(&new_table[i]); } - ret = bdrv_pwrite_sync(s->hd, table_offset, new_table, + BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_WRITE_TABLE); + ret = bdrv_pwrite_sync(bs->file, table_offset, new_table, table_size * sizeof(uint64_t)); if (ret < 0) { goto fail_table; @@ -358,7 +374,8 @@ uint8_t data[12]; cpu_to_be64w((uint64_t*)data, table_offset); cpu_to_be32w((uint32_t*)(data + 8), table_clusters); - ret = bdrv_pwrite_sync(s->hd, offsetof(QCowHeader, refcount_table_offset), + BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_SWITCH_TABLE); + ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, refcount_table_offset), data, sizeof(data)); if (ret < 0) { goto fail_table; @@ -378,9 +395,9 @@ qcow2_free_clusters(bs, old_table_offset, old_table_size * sizeof(uint64_t)); s->free_cluster_index = old_free_cluster_index; - ret = load_refcount_block(bs, new_block); + ret = load_refcount_block(bs, new_block, (void**) refcount_block); if (ret < 0) { - goto fail_block; + return ret; } return new_block; @@ -388,38 +405,10 @@ fail_table: qemu_free(new_table); fail_block: - s->refcount_block_cache_offset = 0; - return ret; -} - -#define REFCOUNTS_PER_SECTOR (512 >> REFCOUNT_SHIFT) -static int write_refcount_block_entries(BDRVQcowState *s, - int64_t refcount_block_offset, int first_index, int last_index) -{ - size_t size; - int ret; - - if (cache_refcount_updates) { - return 0; - } - - if (first_index < 0) { - return 0; + if (*refcount_block != NULL) { + qcow2_cache_put(bs, s->refcount_block_cache, (void**) refcount_block); } - - first_index &= ~(REFCOUNTS_PER_SECTOR - 1); - last_index = (last_index + REFCOUNTS_PER_SECTOR) - & ~(REFCOUNTS_PER_SECTOR - 1); - - size = (last_index - first_index) << REFCOUNT_SHIFT; - ret = bdrv_pwrite_sync(s->hd, - refcount_block_offset + (first_index << REFCOUNT_SHIFT), - &s->refcount_block_cache[first_index], size); - if (ret < 0) { - return ret; - } - - return 0; + return ret; } /* XXX: cache several refcount block clusters ? */ @@ -428,9 +417,8 @@ { BDRVQcowState *s = bs->opaque; int64_t start, last, cluster_offset; - int64_t refcount_block_offset = 0; - int64_t table_index = -1, old_table_index; - int first_index = -1, last_index = -1; + uint16_t *refcount_block = NULL; + int64_t old_table_index = -1; int ret; #ifdef DEBUG_ALLOC2 @@ -443,6 +431,11 @@ return 0; } + if (addend < 0) { + qcow2_cache_set_dependency(bs, s->refcount_block_cache, + s->l2_table_cache); + } + start = offset & ~(s->cluster_size - 1); last = (offset + length - 1) & ~(s->cluster_size - 1); for(cluster_offset = start; cluster_offset <= last; @@ -450,42 +443,33 @@ { int block_index, refcount; int64_t cluster_index = cluster_offset >> s->cluster_bits; - int64_t new_block; + int64_t table_index = + cluster_index >> (s->cluster_bits - REFCOUNT_SHIFT); - /* Only write refcount block to disk when we are done with it */ - old_table_index = table_index; - table_index = cluster_index >> (s->cluster_bits - REFCOUNT_SHIFT); - if ((old_table_index >= 0) && (table_index != old_table_index)) { - - if (write_refcount_block_entries(s, refcount_block_offset, - first_index, last_index) < 0) - { - return -EIO; + /* Load the refcount block and allocate it if needed */ + if (table_index != old_table_index) { + if (refcount_block) { + ret = qcow2_cache_put(bs, s->refcount_block_cache, + (void**) &refcount_block); + if (ret < 0) { + goto fail; + } } - first_index = -1; - last_index = -1; + ret = alloc_refcount_block(bs, cluster_index, &refcount_block); + if (ret < 0) { + goto fail; + } } + old_table_index = table_index; - /* Load the refcount block and allocate it if needed */ - new_block = alloc_refcount_block(bs, cluster_index); - if (new_block < 0) { - ret = new_block; - goto fail; - } - refcount_block_offset = new_block; + qcow2_cache_entry_mark_dirty(s->refcount_block_cache, refcount_block); /* we can update the count and save it */ block_index = cluster_index & ((1 << (s->cluster_bits - REFCOUNT_SHIFT)) - 1); - if (first_index == -1 || block_index < first_index) { - first_index = block_index; - } - if (block_index > last_index) { - last_index = block_index; - } - refcount = be16_to_cpu(s->refcount_block_cache[block_index]); + refcount = be16_to_cpu(refcount_block[block_index]); refcount += addend; if (refcount < 0 || refcount > 0xffff) { ret = -EINVAL; @@ -494,18 +478,18 @@ if (refcount == 0 && cluster_index < s->free_cluster_index) { s->free_cluster_index = cluster_index; } - s->refcount_block_cache[block_index] = cpu_to_be16(refcount); + refcount_block[block_index] = cpu_to_be16(refcount); } ret = 0; fail: - /* Write last changed block to disk */ - if (refcount_block_offset != 0) { - if (write_refcount_block_entries(s, refcount_block_offset, - first_index, last_index) < 0) - { - return ret < 0 ? ret : -EIO; + if (refcount_block) { + int wret; + wret = qcow2_cache_put(bs, s->refcount_block_cache, + (void**) &refcount_block); + if (wret < 0) { + return ret < 0 ? ret : wret; } } @@ -516,12 +500,19 @@ if (ret < 0) { int dummy; dummy = update_refcount(bs, offset, cluster_offset - offset, -addend); + (void)dummy; } return ret; } -/* addend must be 1 or -1 */ +/* + * Increases or decreases the refcount of a given cluster by one. + * addend must be 1 or -1. + * + * If the return value is non-negative, it is the new refcount of the cluster. + * If it is negative, it is -errno and indicates an error. + */ static int update_cluster_refcount(BlockDriverState *bs, int64_t cluster_index, int addend) @@ -534,6 +525,8 @@ return ret; } + bdrv_flush(bs->file); + return get_refcount(bs, cluster_index); } @@ -548,14 +541,19 @@ static int64_t alloc_clusters_noref(BlockDriverState *bs, int64_t size) { BDRVQcowState *s = bs->opaque; - int i, nb_clusters; + int i, nb_clusters, refcount; nb_clusters = size_to_clusters(s, size); retry: for(i = 0; i < nb_clusters; i++) { - int64_t i = s->free_cluster_index++; - if (get_refcount(bs, i) != 0) + int64_t next_cluster_index = s->free_cluster_index++; + refcount = get_refcount(bs, next_cluster_index); + + if (refcount < 0) { + return refcount; + } else if (refcount != 0) { goto retry; + } } #ifdef DEBUG_ALLOC2 printf("alloc_clusters: size=%" PRId64 " -> %" PRId64 "\n", @@ -570,11 +568,17 @@ int64_t offset; int ret; + BLKDBG_EVENT(bs->file, BLKDBG_CLUSTER_ALLOC); offset = alloc_clusters_noref(bs, size); + if (offset < 0) { + return offset; + } + ret = update_refcount(bs, offset, size, 1); if (ret < 0) { return ret; } + return offset; } @@ -586,6 +590,7 @@ int64_t offset, cluster_offset; int free_in_cluster; + BLKDBG_EVENT(bs->file, BLKDBG_CLUSTER_ALLOC_BYTES); assert(size > 0 && size <= s->cluster_size); if (s->free_byte_offset == 0) { s->free_byte_offset = qcow2_alloc_clusters(bs, s->cluster_size); @@ -621,6 +626,8 @@ goto redo; } } + + bdrv_flush(bs->file); return offset; } @@ -629,6 +636,7 @@ { int ret; + BLKDBG_EVENT(bs->file, BLKDBG_CLUSTER_FREE); ret = update_refcount(bs, offset, size, -1); if (ret < 0) { fprintf(stderr, "qcow2_free_clusters failed: %s\n", strerror(-ret)); @@ -697,15 +705,12 @@ BDRVQcowState *s = bs->opaque; uint64_t *l1_table, *l2_table, l2_offset, offset, l1_size2, l1_allocated; int64_t old_offset, old_l2_offset; - int l2_size, i, j, l1_modified, l2_modified, nb_csectors, refcount; - - qcow2_l2_cache_reset(bs); - cache_refcount_updates = 1; + int i, j, l1_modified, nb_csectors, refcount; + int ret; l2_table = NULL; l1_table = NULL; l1_size2 = l1_size * sizeof(uint64_t); - l1_allocated = 0; if (l1_table_offset != s->l1_table_offset) { if (l1_size2 != 0) { l1_table = qemu_mallocz(align_offset(l1_size2, 512)); @@ -713,7 +718,7 @@ l1_table = NULL; } l1_allocated = 1; - if (bdrv_pread(s->hd, l1_table_offset, + if (bdrv_pread(bs->file, l1_table_offset, l1_table, l1_size2) != l1_size2) goto fail; for(i = 0;i < l1_size; i++) @@ -724,17 +729,19 @@ l1_allocated = 0; } - l2_size = s->l2_size * sizeof(uint64_t); - l2_table = qemu_malloc(l2_size); l1_modified = 0; for(i = 0; i < l1_size; i++) { l2_offset = l1_table[i]; if (l2_offset) { old_l2_offset = l2_offset; l2_offset &= ~QCOW_OFLAG_COPIED; - l2_modified = 0; - if (bdrv_pread(s->hd, l2_offset, l2_table, l2_size) != l2_size) + + ret = qcow2_cache_get(bs, s->l2_table_cache, l2_offset, + (void**) &l2_table); + if (ret < 0) { goto fail; + } + for(j = 0; j < s->l2_size; j++) { offset = be64_to_cpu(l2_table[j]); if (offset != 0) { @@ -751,6 +758,10 @@ if (ret < 0) { goto fail; } + + /* TODO Flushing once for the whole function should + * be enough */ + bdrv_flush(bs->file); } /* compressed clusters are never modified */ refcount = 2; @@ -760,29 +771,40 @@ } else { refcount = get_refcount(bs, offset >> s->cluster_bits); } + + if (refcount < 0) { + goto fail; + } } if (refcount == 1) { offset |= QCOW_OFLAG_COPIED; } if (offset != old_offset) { + if (addend > 0) { + qcow2_cache_set_dependency(bs, s->l2_table_cache, + s->refcount_block_cache); + } l2_table[j] = cpu_to_be64(offset); - l2_modified = 1; + qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table); } } } - if (l2_modified) { - if (bdrv_pwrite_sync(s->hd, - l2_offset, l2_table, l2_size) < 0) - goto fail; + + ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); + if (ret < 0) { + goto fail; } + if (addend != 0) { refcount = update_cluster_refcount(bs, l2_offset >> s->cluster_bits, addend); } else { refcount = get_refcount(bs, l2_offset >> s->cluster_bits); } - if (refcount == 1) { + if (refcount < 0) { + goto fail; + } else if (refcount == 1) { l2_offset |= QCOW_OFLAG_COPIED; } if (l2_offset != old_l2_offset) { @@ -794,7 +816,7 @@ if (l1_modified) { for(i = 0; i < l1_size; i++) cpu_to_be64s(&l1_table[i]); - if (bdrv_pwrite_sync(s->hd, l1_table_offset, l1_table, + if (bdrv_pwrite_sync(bs->file, l1_table_offset, l1_table, l1_size2) < 0) goto fail; for(i = 0; i < l1_size; i++) @@ -802,16 +824,14 @@ } if (l1_allocated) qemu_free(l1_table); - qemu_free(l2_table); - cache_refcount_updates = 0; - write_refcount_block(s); return 0; fail: + if (l2_table) { + qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); + } + if (l1_allocated) qemu_free(l1_table); - qemu_free(l2_table); - cache_refcount_updates = 0; - write_refcount_block(s); return -EIO; } @@ -828,9 +848,10 @@ * This is used to construct a temporary refcount table out of L1 and L2 tables * which can be compared the the refcount table saved in the image. * - * Returns the number of errors in the image that were found + * Modifies the number of errors in res. */ -static int inc_refcounts(BlockDriverState *bs, +static void inc_refcounts(BlockDriverState *bs, + BdrvCheckResult *res, uint16_t *refcount_table, int refcount_table_size, int64_t offset, int64_t size) @@ -838,30 +859,32 @@ BDRVQcowState *s = bs->opaque; int64_t start, last, cluster_offset; int k; - int errors = 0; if (size <= 0) - return 0; + return; start = offset & ~(s->cluster_size - 1); last = (offset + size - 1) & ~(s->cluster_size - 1); for(cluster_offset = start; cluster_offset <= last; cluster_offset += s->cluster_size) { k = cluster_offset >> s->cluster_bits; - if (k < 0 || k >= refcount_table_size) { + if (k < 0) { fprintf(stderr, "ERROR: invalid cluster offset=0x%" PRIx64 "\n", cluster_offset); - errors++; + res->corruptions++; + } else if (k >= refcount_table_size) { + fprintf(stderr, "Warning: cluster offset=0x%" PRIx64 " is after " + "the end of the image file, can't properly check refcounts.\n", + cluster_offset); + res->check_errors++; } else { if (++refcount_table[k] == 0) { fprintf(stderr, "ERROR: overflow cluster offset=0x%" PRIx64 "\n", cluster_offset); - errors++; + res->corruptions++; } } } - - return errors; } /* @@ -872,20 +895,19 @@ * Returns the number of errors found by the checks or -errno if an internal * error occurred. */ -static int check_refcounts_l2(BlockDriverState *bs, +static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res, uint16_t *refcount_table, int refcount_table_size, int64_t l2_offset, int check_copied) { BDRVQcowState *s = bs->opaque; uint64_t *l2_table, offset; int i, l2_size, nb_csectors, refcount; - int errors = 0; /* Read L2 table from disk */ l2_size = s->l2_size * sizeof(uint64_t); l2_table = qemu_malloc(l2_size); - if (bdrv_pread(s->hd, l2_offset, l2_table, l2_size) != l2_size) + if (bdrv_pread(bs->file, l2_offset, l2_table, l2_size) != l2_size) goto fail; /* Do the actual checks */ @@ -899,50 +921,53 @@ "copied flag must never be set for compressed " "clusters\n", offset >> s->cluster_bits); offset &= ~QCOW_OFLAG_COPIED; - errors++; + res->corruptions++; } /* Mark cluster as used */ nb_csectors = ((offset >> s->csize_shift) & s->csize_mask) + 1; offset &= s->cluster_offset_mask; - errors += inc_refcounts(bs, refcount_table, - refcount_table_size, - offset & ~511, nb_csectors * 512); + inc_refcounts(bs, res, refcount_table, refcount_table_size, + offset & ~511, nb_csectors * 512); } else { /* QCOW_OFLAG_COPIED must be set iff refcount == 1 */ if (check_copied) { uint64_t entry = offset; offset &= ~QCOW_OFLAG_COPIED; refcount = get_refcount(bs, offset >> s->cluster_bits); + if (refcount < 0) { + fprintf(stderr, "Can't get refcount for offset %" + PRIx64 ": %s\n", entry, strerror(-refcount)); + goto fail; + } if ((refcount == 1) != ((entry & QCOW_OFLAG_COPIED) != 0)) { fprintf(stderr, "ERROR OFLAG_COPIED: offset=%" PRIx64 " refcount=%d\n", entry, refcount); - errors++; + res->corruptions++; } } /* Mark cluster as used */ offset &= ~QCOW_OFLAG_COPIED; - errors += inc_refcounts(bs, refcount_table, - refcount_table_size, - offset, s->cluster_size); + inc_refcounts(bs, res, refcount_table,refcount_table_size, + offset, s->cluster_size); /* Correct offsets are cluster aligned */ if (offset & (s->cluster_size - 1)) { fprintf(stderr, "ERROR offset=%" PRIx64 ": Cluster is not " "properly aligned; L2 entry corrupted.\n", offset); - errors++; + res->corruptions++; } } } } qemu_free(l2_table); - return errors; + return 0; fail: - fprintf(stderr, "ERROR: I/O error in check_refcounts_l1\n"); + fprintf(stderr, "ERROR: I/O error in check_refcounts_l2\n"); qemu_free(l2_table); return -EIO; } @@ -956,6 +981,7 @@ * error occurred. */ static int check_refcounts_l1(BlockDriverState *bs, + BdrvCheckResult *res, uint16_t *refcount_table, int refcount_table_size, int64_t l1_table_offset, int l1_size, @@ -964,20 +990,19 @@ BDRVQcowState *s = bs->opaque; uint64_t *l1_table, l2_offset, l1_size2; int i, refcount, ret; - int errors = 0; l1_size2 = l1_size * sizeof(uint64_t); /* Mark L1 table as used */ - errors += inc_refcounts(bs, refcount_table, refcount_table_size, - l1_table_offset, l1_size2); + inc_refcounts(bs, res, refcount_table, refcount_table_size, + l1_table_offset, l1_size2); /* Read L1 table entries from disk */ if (l1_size2 == 0) { l1_table = NULL; } else { l1_table = qemu_malloc(l1_size2); - if (bdrv_pread(s->hd, l1_table_offset, + if (bdrv_pread(bs->file, l1_table_offset, l1_table, l1_size2) != l1_size2) goto fail; for(i = 0;i < l1_size; i++) @@ -992,41 +1017,44 @@ if (check_copied) { refcount = get_refcount(bs, (l2_offset & ~QCOW_OFLAG_COPIED) >> s->cluster_bits); + if (refcount < 0) { + fprintf(stderr, "Can't get refcount for l2_offset %" + PRIx64 ": %s\n", l2_offset, strerror(-refcount)); + goto fail; + } if ((refcount == 1) != ((l2_offset & QCOW_OFLAG_COPIED) != 0)) { fprintf(stderr, "ERROR OFLAG_COPIED: l2_offset=%" PRIx64 " refcount=%d\n", l2_offset, refcount); - errors++; + res->corruptions++; } } /* Mark L2 table as used */ l2_offset &= ~QCOW_OFLAG_COPIED; - errors += inc_refcounts(bs, refcount_table, - refcount_table_size, - l2_offset, - s->cluster_size); + inc_refcounts(bs, res, refcount_table, refcount_table_size, + l2_offset, s->cluster_size); /* L2 tables are cluster aligned */ if (l2_offset & (s->cluster_size - 1)) { fprintf(stderr, "ERROR l2_offset=%" PRIx64 ": Table is not " "cluster aligned; L1 entry corrupted\n", l2_offset); - errors++; + res->corruptions++; } /* Process and check L2 entries */ - ret = check_refcounts_l2(bs, refcount_table, refcount_table_size, - l2_offset, check_copied); + ret = check_refcounts_l2(bs, res, refcount_table, + refcount_table_size, l2_offset, check_copied); if (ret < 0) { goto fail; } - errors += ret; } } qemu_free(l1_table); - return errors; + return 0; fail: fprintf(stderr, "ERROR: I/O error in check_refcounts_l1\n"); + res->check_errors++; qemu_free(l1_table); return -EIO; } @@ -1037,66 +1065,102 @@ * Returns 0 if no errors are found, the number of errors in case the image is * detected as corrupted, and -errno when an internal error occured. */ -int qcow2_check_refcounts(BlockDriverState *bs) +int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res) { BDRVQcowState *s = bs->opaque; int64_t size; int nb_clusters, refcount1, refcount2, i; QCowSnapshot *sn; uint16_t *refcount_table; - int ret, errors = 0; + int ret; - size = bdrv_getlength(s->hd); + size = bdrv_getlength(bs->file); nb_clusters = size_to_clusters(s, size); refcount_table = qemu_mallocz(nb_clusters * sizeof(uint16_t)); /* header */ - errors += inc_refcounts(bs, refcount_table, nb_clusters, - 0, s->cluster_size); + inc_refcounts(bs, res, refcount_table, nb_clusters, + 0, s->cluster_size); /* current L1 table */ - ret = check_refcounts_l1(bs, refcount_table, nb_clusters, + ret = check_refcounts_l1(bs, res, refcount_table, nb_clusters, s->l1_table_offset, s->l1_size, 1); if (ret < 0) { return ret; } - errors += ret; /* snapshots */ for(i = 0; i < s->nb_snapshots; i++) { sn = s->snapshots + i; - check_refcounts_l1(bs, refcount_table, nb_clusters, - sn->l1_table_offset, sn->l1_size, 0); + ret = check_refcounts_l1(bs, res, refcount_table, nb_clusters, + sn->l1_table_offset, sn->l1_size, 0); + if (ret < 0) { + return ret; + } } - errors += inc_refcounts(bs, refcount_table, nb_clusters, - s->snapshots_offset, s->snapshots_size); + inc_refcounts(bs, res, refcount_table, nb_clusters, + s->snapshots_offset, s->snapshots_size); /* refcount data */ - errors += inc_refcounts(bs, refcount_table, nb_clusters, - s->refcount_table_offset, - s->refcount_table_size * sizeof(uint64_t)); + inc_refcounts(bs, res, refcount_table, nb_clusters, + s->refcount_table_offset, + s->refcount_table_size * sizeof(uint64_t)); + for(i = 0; i < s->refcount_table_size; i++) { - int64_t offset; + uint64_t offset, cluster; offset = s->refcount_table[i]; + cluster = offset >> s->cluster_bits; + + /* Refcount blocks are cluster aligned */ + if (offset & (s->cluster_size - 1)) { + fprintf(stderr, "ERROR refcount block %d is not " + "cluster aligned; refcount table entry corrupted\n", i); + res->corruptions++; + continue; + } + + if (cluster >= nb_clusters) { + fprintf(stderr, "ERROR refcount block %d is outside image\n", i); + res->corruptions++; + continue; + } + if (offset != 0) { - errors += inc_refcounts(bs, refcount_table, nb_clusters, - offset, s->cluster_size); + inc_refcounts(bs, res, refcount_table, nb_clusters, + offset, s->cluster_size); + if (refcount_table[cluster] != 1) { + fprintf(stderr, "ERROR refcount block %d refcount=%d\n", + i, refcount_table[cluster]); + res->corruptions++; + } } } /* compare ref counts */ for(i = 0; i < nb_clusters; i++) { refcount1 = get_refcount(bs, i); + if (refcount1 < 0) { + fprintf(stderr, "Can't get refcount for cluster %d: %s\n", + i, strerror(-refcount1)); + res->check_errors++; + continue; + } + refcount2 = refcount_table[i]; if (refcount1 != refcount2) { - fprintf(stderr, "ERROR cluster %d refcount=%d reference=%d\n", + fprintf(stderr, "%s cluster %d refcount=%d reference=%d\n", + refcount1 < refcount2 ? "ERROR" : "Leaked", i, refcount1, refcount2); - errors++; + if (refcount1 < refcount2) { + res->corruptions++; + } else { + res->leaks++; + } } } qemu_free(refcount_table); - return errors; + return 0; } diff -Nru qemu-kvm-0.12.5+noroms/block/qcow2-snapshot.c qemu-kvm-0.14.1/block/qcow2-snapshot.c --- qemu-kvm-0.12.5+noroms/block/qcow2-snapshot.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/block/qcow2-snapshot.c 2011-05-11 13:29:46.000000000 +0000 @@ -79,7 +79,7 @@ s->snapshots = qemu_mallocz(s->nb_snapshots * sizeof(QCowSnapshot)); for(i = 0; i < s->nb_snapshots; i++) { offset = align_offset(offset, 8); - if (bdrv_pread(s->hd, offset, &h, sizeof(h)) != sizeof(h)) + if (bdrv_pread(bs->file, offset, &h, sizeof(h)) != sizeof(h)) goto fail; offset += sizeof(h); sn = s->snapshots + i; @@ -97,13 +97,13 @@ offset += extra_data_size; sn->id_str = qemu_malloc(id_str_size + 1); - if (bdrv_pread(s->hd, offset, sn->id_str, id_str_size) != id_str_size) + if (bdrv_pread(bs->file, offset, sn->id_str, id_str_size) != id_str_size) goto fail; offset += id_str_size; sn->id_str[id_str_size] = '\0'; sn->name = qemu_malloc(name_size + 1); - if (bdrv_pread(s->hd, offset, sn->name, name_size) != name_size) + if (bdrv_pread(bs->file, offset, sn->name, name_size) != name_size) goto fail; offset += name_size; sn->name[name_size] = '\0'; @@ -116,7 +116,7 @@ } /* add at the end of the file a new list of snapshots */ -static int qcow_write_snapshots(BlockDriverState *bs) +static int qcow2_write_snapshots(BlockDriverState *bs) { BDRVQcowState *s = bs->opaque; QCowSnapshot *sn; @@ -138,6 +138,7 @@ snapshots_size = offset; snapshots_offset = qcow2_alloc_clusters(bs, snapshots_size); + bdrv_flush(bs->file); offset = snapshots_offset; if (offset < 0) { return offset; @@ -158,24 +159,24 @@ h.id_str_size = cpu_to_be16(id_str_size); h.name_size = cpu_to_be16(name_size); offset = align_offset(offset, 8); - if (bdrv_pwrite_sync(s->hd, offset, &h, sizeof(h)) < 0) + if (bdrv_pwrite_sync(bs->file, offset, &h, sizeof(h)) < 0) goto fail; offset += sizeof(h); - if (bdrv_pwrite_sync(s->hd, offset, sn->id_str, id_str_size) < 0) + if (bdrv_pwrite_sync(bs->file, offset, sn->id_str, id_str_size) < 0) goto fail; offset += id_str_size; - if (bdrv_pwrite_sync(s->hd, offset, sn->name, name_size) < 0) + if (bdrv_pwrite_sync(bs->file, offset, sn->name, name_size) < 0) goto fail; offset += name_size; } /* update the various header fields */ data64 = cpu_to_be64(snapshots_offset); - if (bdrv_pwrite_sync(s->hd, offsetof(QCowHeader, snapshots_offset), + if (bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, snapshots_offset), &data64, sizeof(data64)) < 0) goto fail; data32 = cpu_to_be32(s->nb_snapshots); - if (bdrv_pwrite_sync(s->hd, offsetof(QCowHeader, nb_snapshots), + if (bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, nb_snapshots), &data32, sizeof(data32)) < 0) goto fail; @@ -271,6 +272,7 @@ if (l1_table_offset < 0) { goto fail; } + bdrv_flush(bs->file); sn->l1_table_offset = l1_table_offset; sn->l1_size = s->l1_size; @@ -284,7 +286,7 @@ for(i = 0; i < s->l1_size; i++) { l1_table[i] = cpu_to_be64(s->l1_table[i]); } - if (bdrv_pwrite_sync(s->hd, sn->l1_table_offset, + if (bdrv_pwrite_sync(bs->file, sn->l1_table_offset, l1_table, s->l1_size * sizeof(uint64_t)) < 0) goto fail; qemu_free(l1_table); @@ -298,7 +300,7 @@ s->snapshots = snapshots1; s->snapshots[s->nb_snapshots++] = *sn; - if (qcow_write_snapshots(bs) < 0) + if (qcow2_write_snapshots(bs) < 0) goto fail; #ifdef DEBUG_ALLOC qcow2_check_refcounts(bs); @@ -325,16 +327,16 @@ if (qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, -1) < 0) goto fail; - if (qcow2_grow_l1_table(bs, sn->l1_size) < 0) + if (qcow2_grow_l1_table(bs, sn->l1_size, true) < 0) goto fail; s->l1_size = sn->l1_size; l1_size2 = s->l1_size * sizeof(uint64_t); /* copy the snapshot l1 table to the current l1 table */ - if (bdrv_pread(s->hd, sn->l1_table_offset, + if (bdrv_pread(bs->file, sn->l1_table_offset, s->l1_table, l1_size2) != l1_size2) goto fail; - if (bdrv_pwrite_sync(s->hd, s->l1_table_offset, + if (bdrv_pwrite_sync(bs->file, s->l1_table_offset, s->l1_table, l1_size2) < 0) goto fail; for(i = 0;i < s->l1_size; i++) { @@ -376,7 +378,7 @@ qemu_free(sn->name); memmove(sn, sn + 1, (s->nb_snapshots - snapshot_index - 1) * sizeof(*sn)); s->nb_snapshots--; - ret = qcow_write_snapshots(bs); + ret = qcow2_write_snapshots(bs); if (ret < 0) { /* XXX: restore snapshot if error ? */ return ret; @@ -416,3 +418,34 @@ return s->nb_snapshots; } +int qcow2_snapshot_load_tmp(BlockDriverState *bs, const char *snapshot_name) +{ + int i, snapshot_index, l1_size2; + BDRVQcowState *s = bs->opaque; + QCowSnapshot *sn; + + snapshot_index = find_snapshot_by_id_or_name(bs, snapshot_name); + if (snapshot_index < 0) { + return -ENOENT; + } + + sn = &s->snapshots[snapshot_index]; + s->l1_size = sn->l1_size; + l1_size2 = s->l1_size * sizeof(uint64_t); + if (s->l1_table != NULL) { + qemu_free(s->l1_table); + } + + s->l1_table_offset = sn->l1_table_offset; + s->l1_table = qemu_mallocz(align_offset(l1_size2, 512)); + + if (bdrv_pread(bs->file, sn->l1_table_offset, + s->l1_table, l1_size2) != l1_size2) { + return -1; + } + + for(i = 0;i < s->l1_size; i++) { + be64_to_cpus(&s->l1_table[i]); + } + return 0; +} diff -Nru qemu-kvm-0.12.5+noroms/block/qcow.c qemu-kvm-0.14.1/block/qcow.c --- qemu-kvm-0.12.5+noroms/block/qcow.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/block/qcow.c 2011-05-11 13:29:46.000000000 +0000 @@ -54,7 +54,6 @@ #define L2_CACHE_SIZE 16 typedef struct BDRVQcowState { - BlockDriverState *hd; int cluster_bits; int cluster_size; int cluster_sectors; @@ -76,7 +75,7 @@ AES_KEY aes_decrypt_key; } BDRVQcowState; -static int decompress_cluster(BDRVQcowState *s, uint64_t cluster_offset); +static int decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset); static int qcow_probe(const uint8_t *buf, int buf_size, const char *filename) { @@ -90,16 +89,13 @@ return 0; } -static int qcow_open(BlockDriverState *bs, const char *filename, int flags) +static int qcow_open(BlockDriverState *bs, int flags) { BDRVQcowState *s = bs->opaque; - int len, i, shift, ret; + int len, i, shift; QCowHeader header; - ret = bdrv_file_open(&s->hd, filename, flags); - if (ret < 0) - return ret; - if (bdrv_pread(s->hd, 0, &header, sizeof(header)) != sizeof(header)) + if (bdrv_pread(bs->file, 0, &header, sizeof(header)) != sizeof(header)) goto fail; be32_to_cpus(&header.magic); be32_to_cpus(&header.version); @@ -135,7 +131,7 @@ s->l1_table = qemu_malloc(s->l1_size * sizeof(uint64_t)); if (!s->l1_table) goto fail; - if (bdrv_pread(s->hd, s->l1_table_offset, s->l1_table, s->l1_size * sizeof(uint64_t)) != + if (bdrv_pread(bs->file, s->l1_table_offset, s->l1_table, s->l1_size * sizeof(uint64_t)) != s->l1_size * sizeof(uint64_t)) goto fail; for(i = 0;i < s->l1_size; i++) { @@ -158,7 +154,7 @@ len = header.backing_file_size; if (len > 1023) len = 1023; - if (bdrv_pread(s->hd, header.backing_file_offset, bs->backing_file, len) != len) + if (bdrv_pread(bs->file, header.backing_file_offset, bs->backing_file, len) != len) goto fail; bs->backing_file[len] = '\0'; } @@ -169,7 +165,6 @@ qemu_free(s->l2_cache); qemu_free(s->cluster_cache); qemu_free(s->cluster_data); - bdrv_delete(s->hd); return -1; } @@ -271,13 +266,13 @@ if (!allocate) return 0; /* allocate a new l2 entry */ - l2_offset = bdrv_getlength(s->hd); + l2_offset = bdrv_getlength(bs->file); /* round to cluster size */ l2_offset = (l2_offset + s->cluster_size - 1) & ~(s->cluster_size - 1); /* update the L1 entry */ s->l1_table[l1_index] = l2_offset; tmp = cpu_to_be64(l2_offset); - if (bdrv_pwrite_sync(s->hd, + if (bdrv_pwrite_sync(bs->file, s->l1_table_offset + l1_index * sizeof(tmp), &tmp, sizeof(tmp)) < 0) return 0; @@ -307,11 +302,11 @@ l2_table = s->l2_cache + (min_index << s->l2_bits); if (new_l2_table) { memset(l2_table, 0, s->l2_size * sizeof(uint64_t)); - if (bdrv_pwrite_sync(s->hd, l2_offset, l2_table, + if (bdrv_pwrite_sync(bs->file, l2_offset, l2_table, s->l2_size * sizeof(uint64_t)) < 0) return 0; } else { - if (bdrv_pread(s->hd, l2_offset, l2_table, s->l2_size * sizeof(uint64_t)) != + if (bdrv_pread(bs->file, l2_offset, l2_table, s->l2_size * sizeof(uint64_t)) != s->l2_size * sizeof(uint64_t)) return 0; } @@ -330,22 +325,22 @@ /* if the cluster is already compressed, we must decompress it in the case it is not completely overwritten */ - if (decompress_cluster(s, cluster_offset) < 0) + if (decompress_cluster(bs, cluster_offset) < 0) return 0; - cluster_offset = bdrv_getlength(s->hd); + cluster_offset = bdrv_getlength(bs->file); cluster_offset = (cluster_offset + s->cluster_size - 1) & ~(s->cluster_size - 1); /* write the cluster content */ - if (bdrv_pwrite(s->hd, cluster_offset, s->cluster_cache, s->cluster_size) != + if (bdrv_pwrite(bs->file, cluster_offset, s->cluster_cache, s->cluster_size) != s->cluster_size) return -1; } else { - cluster_offset = bdrv_getlength(s->hd); + cluster_offset = bdrv_getlength(bs->file); if (allocate == 1) { /* round to cluster size */ cluster_offset = (cluster_offset + s->cluster_size - 1) & ~(s->cluster_size - 1); - bdrv_truncate(s->hd, cluster_offset + s->cluster_size); + bdrv_truncate(bs->file, cluster_offset + s->cluster_size); /* if encrypted, we must initialize the cluster content which won't be written */ if (s->crypt_method && @@ -359,7 +354,7 @@ s->cluster_data, s->cluster_data + 512, 1, 1, &s->aes_encrypt_key); - if (bdrv_pwrite(s->hd, cluster_offset + i * 512, + if (bdrv_pwrite(bs->file, cluster_offset + i * 512, s->cluster_data, 512) != 512) return -1; } @@ -373,7 +368,7 @@ /* update L2 table */ tmp = cpu_to_be64(cluster_offset); l2_table[l2_index] = tmp; - if (bdrv_pwrite_sync(s->hd, l2_offset + l2_index * sizeof(tmp), + if (bdrv_pwrite_sync(bs->file, l2_offset + l2_index * sizeof(tmp), &tmp, sizeof(tmp)) < 0) return 0; } @@ -423,8 +418,9 @@ return 0; } -static int decompress_cluster(BDRVQcowState *s, uint64_t cluster_offset) +static int decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset) { + BDRVQcowState *s = bs->opaque; int ret, csize; uint64_t coffset; @@ -432,7 +428,7 @@ if (s->cluster_cache_offset != coffset) { csize = cluster_offset >> (63 - s->cluster_bits); csize &= (s->cluster_size - 1); - ret = bdrv_pread(s->hd, coffset, s->cluster_data, csize); + ret = bdrv_pread(bs->file, coffset, s->cluster_data, csize); if (ret != csize) return -1; if (decompress_buffer(s->cluster_cache, s->cluster_size, @@ -469,11 +465,11 @@ memset(buf, 0, 512 * n); } } else if (cluster_offset & QCOW_OFLAG_COMPRESSED) { - if (decompress_cluster(s, cluster_offset) < 0) + if (decompress_cluster(bs, cluster_offset) < 0) return -1; memcpy(buf, s->cluster_cache + index_in_cluster * 512, 512 * n); } else { - ret = bdrv_pread(s->hd, cluster_offset + index_in_cluster * 512, buf, n * 512); + ret = bdrv_pread(bs->file, cluster_offset + index_in_cluster * 512, buf, n * 512); if (ret != n * 512) return -1; if (s->crypt_method) { @@ -506,7 +502,7 @@ static void qcow_aio_cancel(BlockDriverAIOCB *blockacb) { - QCowAIOCB *acb = (QCowAIOCB *)blockacb; + QCowAIOCB *acb = container_of(blockacb, QCowAIOCB, common); if (acb->hd_aiocb) bdrv_aio_cancel(acb->hd_aiocb); qemu_aio_release(acb); @@ -602,7 +598,7 @@ } } else if (acb->cluster_offset & QCOW_OFLAG_COMPRESSED) { /* add AIO support for compressed blocks ? */ - if (decompress_cluster(s, acb->cluster_offset) < 0) + if (decompress_cluster(bs, acb->cluster_offset) < 0) goto done; memcpy(acb->buf, s->cluster_cache + index_in_cluster * 512, 512 * acb->n); @@ -615,7 +611,7 @@ acb->hd_iov.iov_base = (void *)acb->buf; acb->hd_iov.iov_len = acb->n * 512; qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1); - acb->hd_aiocb = bdrv_aio_readv(s->hd, + acb->hd_aiocb = bdrv_aio_readv(bs->file, (acb->cluster_offset >> 9) + index_in_cluster, &acb->hd_qiov, acb->n, qcow_aio_read_cb, acb); if (acb->hd_aiocb == NULL) @@ -700,7 +696,7 @@ acb->hd_iov.iov_base = (void *)src_buf; acb->hd_iov.iov_len = acb->n * 512; qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1); - acb->hd_aiocb = bdrv_aio_writev(s->hd, + acb->hd_aiocb = bdrv_aio_writev(bs->file, (cluster_offset >> 9) + index_in_cluster, &acb->hd_qiov, acb->n, qcow_aio_write_cb, acb); @@ -740,7 +736,6 @@ qemu_free(s->l2_cache); qemu_free(s->cluster_cache); qemu_free(s->cluster_data); - bdrv_delete(s->hd); } static int qcow_create(const char *filename, QEMUOptionParameter *options) @@ -751,6 +746,7 @@ int64_t total_size = 0; const char *backing_file = NULL; int flags = 0; + int ret; /* Read out options */ while (options && options->name) { @@ -766,7 +762,7 @@ fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644); if (fd < 0) - return -1; + return -errno; memset(&header, 0, sizeof(header)); header.magic = cpu_to_be32(QCOW_MAGIC); header.version = cpu_to_be32(QCOW_VERSION); @@ -802,17 +798,34 @@ } /* write all the data */ - write(fd, &header, sizeof(header)); + ret = qemu_write_full(fd, &header, sizeof(header)); + if (ret != sizeof(header)) { + ret = -errno; + goto exit; + } + if (backing_file) { - write(fd, backing_file, backing_filename_len); + ret = qemu_write_full(fd, backing_file, backing_filename_len); + if (ret != backing_filename_len) { + ret = -errno; + goto exit; + } + } lseek(fd, header_size, SEEK_SET); tmp = 0; for(i = 0;i < l1_size; i++) { - write(fd, &tmp, sizeof(tmp)); + ret = qemu_write_full(fd, &tmp, sizeof(tmp)); + if (ret != sizeof(tmp)) { + ret = -errno; + goto exit; + } } + + ret = 0; +exit: close(fd); - return 0; + return ret; } static int qcow_make_empty(BlockDriverState *bs) @@ -822,10 +835,10 @@ int ret; memset(s->l1_table, 0, l1_length); - if (bdrv_pwrite_sync(s->hd, s->l1_table_offset, s->l1_table, + if (bdrv_pwrite_sync(bs->file, s->l1_table_offset, s->l1_table, l1_length) < 0) return -1; - ret = bdrv_truncate(s->hd, s->l1_table_offset + l1_length); + ret = bdrv_truncate(bs->file, s->l1_table_offset + l1_length); if (ret < 0) return ret; @@ -886,7 +899,7 @@ cluster_offset = get_cluster_offset(bs, sector_num << 9, 2, out_len, 0, 0); cluster_offset &= s->cluster_offset_mask; - if (bdrv_pwrite(s->hd, cluster_offset, out_buf, out_len) != out_len) { + if (bdrv_pwrite(bs->file, cluster_offset, out_buf, out_len) != out_len) { qemu_free(out_buf); return -1; } @@ -896,10 +909,15 @@ return 0; } -static void qcow_flush(BlockDriverState *bs) +static int qcow_flush(BlockDriverState *bs) { - BDRVQcowState *s = bs->opaque; - bdrv_flush(s->hd); + return bdrv_flush(bs->file); +} + +static BlockDriverAIOCB *qcow_aio_flush(BlockDriverState *bs, + BlockDriverCompletionFunc *cb, void *opaque) +{ + return bdrv_aio_flush(bs->file, cb, opaque); } static int qcow_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) @@ -942,6 +960,7 @@ .bdrv_make_empty = qcow_make_empty, .bdrv_aio_readv = qcow_aio_readv, .bdrv_aio_writev = qcow_aio_writev, + .bdrv_aio_flush = qcow_aio_flush, .bdrv_write_compressed = qcow_write_compressed, .bdrv_get_info = qcow_get_info, diff -Nru qemu-kvm-0.12.5+noroms/block/qed.c qemu-kvm-0.14.1/block/qed.c --- qemu-kvm-0.12.5+noroms/block/qed.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/block/qed.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,1372 @@ +/* + * QEMU Enhanced Disk Format + * + * Copyright IBM, Corp. 2010 + * + * Authors: + * Stefan Hajnoczi + * Anthony Liguori + * + * This work is licensed under the terms of the GNU LGPL, version 2 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#include "trace.h" +#include "qed.h" +#include "qerror.h" + +static void qed_aio_cancel(BlockDriverAIOCB *blockacb) +{ + QEDAIOCB *acb = (QEDAIOCB *)blockacb; + bool finished = false; + + /* Wait for the request to finish */ + acb->finished = &finished; + while (!finished) { + qemu_aio_wait(); + } +} + +static AIOPool qed_aio_pool = { + .aiocb_size = sizeof(QEDAIOCB), + .cancel = qed_aio_cancel, +}; + +static int bdrv_qed_probe(const uint8_t *buf, int buf_size, + const char *filename) +{ + const QEDHeader *header = (const QEDHeader *)buf; + + if (buf_size < sizeof(*header)) { + return 0; + } + if (le32_to_cpu(header->magic) != QED_MAGIC) { + return 0; + } + return 100; +} + +/** + * Check whether an image format is raw + * + * @fmt: Backing file format, may be NULL + */ +static bool qed_fmt_is_raw(const char *fmt) +{ + return fmt && strcmp(fmt, "raw") == 0; +} + +static void qed_header_le_to_cpu(const QEDHeader *le, QEDHeader *cpu) +{ + cpu->magic = le32_to_cpu(le->magic); + cpu->cluster_size = le32_to_cpu(le->cluster_size); + cpu->table_size = le32_to_cpu(le->table_size); + cpu->header_size = le32_to_cpu(le->header_size); + cpu->features = le64_to_cpu(le->features); + cpu->compat_features = le64_to_cpu(le->compat_features); + cpu->autoclear_features = le64_to_cpu(le->autoclear_features); + cpu->l1_table_offset = le64_to_cpu(le->l1_table_offset); + cpu->image_size = le64_to_cpu(le->image_size); + cpu->backing_filename_offset = le32_to_cpu(le->backing_filename_offset); + cpu->backing_filename_size = le32_to_cpu(le->backing_filename_size); +} + +static void qed_header_cpu_to_le(const QEDHeader *cpu, QEDHeader *le) +{ + le->magic = cpu_to_le32(cpu->magic); + le->cluster_size = cpu_to_le32(cpu->cluster_size); + le->table_size = cpu_to_le32(cpu->table_size); + le->header_size = cpu_to_le32(cpu->header_size); + le->features = cpu_to_le64(cpu->features); + le->compat_features = cpu_to_le64(cpu->compat_features); + le->autoclear_features = cpu_to_le64(cpu->autoclear_features); + le->l1_table_offset = cpu_to_le64(cpu->l1_table_offset); + le->image_size = cpu_to_le64(cpu->image_size); + le->backing_filename_offset = cpu_to_le32(cpu->backing_filename_offset); + le->backing_filename_size = cpu_to_le32(cpu->backing_filename_size); +} + +static int qed_write_header_sync(BDRVQEDState *s) +{ + QEDHeader le; + int ret; + + qed_header_cpu_to_le(&s->header, &le); + ret = bdrv_pwrite(s->bs->file, 0, &le, sizeof(le)); + if (ret != sizeof(le)) { + return ret; + } + return 0; +} + +typedef struct { + GenericCB gencb; + BDRVQEDState *s; + struct iovec iov; + QEMUIOVector qiov; + int nsectors; + uint8_t *buf; +} QEDWriteHeaderCB; + +static void qed_write_header_cb(void *opaque, int ret) +{ + QEDWriteHeaderCB *write_header_cb = opaque; + + qemu_vfree(write_header_cb->buf); + gencb_complete(write_header_cb, ret); +} + +static void qed_write_header_read_cb(void *opaque, int ret) +{ + QEDWriteHeaderCB *write_header_cb = opaque; + BDRVQEDState *s = write_header_cb->s; + BlockDriverAIOCB *acb; + + if (ret) { + qed_write_header_cb(write_header_cb, ret); + return; + } + + /* Update header */ + qed_header_cpu_to_le(&s->header, (QEDHeader *)write_header_cb->buf); + + acb = bdrv_aio_writev(s->bs->file, 0, &write_header_cb->qiov, + write_header_cb->nsectors, qed_write_header_cb, + write_header_cb); + if (!acb) { + qed_write_header_cb(write_header_cb, -EIO); + } +} + +/** + * Update header in-place (does not rewrite backing filename or other strings) + * + * This function only updates known header fields in-place and does not affect + * extra data after the QED header. + */ +static void qed_write_header(BDRVQEDState *s, BlockDriverCompletionFunc cb, + void *opaque) +{ + /* We must write full sectors for O_DIRECT but cannot necessarily generate + * the data following the header if an unrecognized compat feature is + * active. Therefore, first read the sectors containing the header, update + * them, and write back. + */ + + BlockDriverAIOCB *acb; + int nsectors = (sizeof(QEDHeader) + BDRV_SECTOR_SIZE - 1) / + BDRV_SECTOR_SIZE; + size_t len = nsectors * BDRV_SECTOR_SIZE; + QEDWriteHeaderCB *write_header_cb = gencb_alloc(sizeof(*write_header_cb), + cb, opaque); + + write_header_cb->s = s; + write_header_cb->nsectors = nsectors; + write_header_cb->buf = qemu_blockalign(s->bs, len); + write_header_cb->iov.iov_base = write_header_cb->buf; + write_header_cb->iov.iov_len = len; + qemu_iovec_init_external(&write_header_cb->qiov, &write_header_cb->iov, 1); + + acb = bdrv_aio_readv(s->bs->file, 0, &write_header_cb->qiov, nsectors, + qed_write_header_read_cb, write_header_cb); + if (!acb) { + qed_write_header_cb(write_header_cb, -EIO); + } +} + +static uint64_t qed_max_image_size(uint32_t cluster_size, uint32_t table_size) +{ + uint64_t table_entries; + uint64_t l2_size; + + table_entries = (table_size * cluster_size) / sizeof(uint64_t); + l2_size = table_entries * cluster_size; + + return l2_size * table_entries; +} + +static bool qed_is_cluster_size_valid(uint32_t cluster_size) +{ + if (cluster_size < QED_MIN_CLUSTER_SIZE || + cluster_size > QED_MAX_CLUSTER_SIZE) { + return false; + } + if (cluster_size & (cluster_size - 1)) { + return false; /* not power of 2 */ + } + return true; +} + +static bool qed_is_table_size_valid(uint32_t table_size) +{ + if (table_size < QED_MIN_TABLE_SIZE || + table_size > QED_MAX_TABLE_SIZE) { + return false; + } + if (table_size & (table_size - 1)) { + return false; /* not power of 2 */ + } + return true; +} + +static bool qed_is_image_size_valid(uint64_t image_size, uint32_t cluster_size, + uint32_t table_size) +{ + if (image_size % BDRV_SECTOR_SIZE != 0) { + return false; /* not multiple of sector size */ + } + if (image_size > qed_max_image_size(cluster_size, table_size)) { + return false; /* image is too large */ + } + return true; +} + +/** + * Read a string of known length from the image file + * + * @file: Image file + * @offset: File offset to start of string, in bytes + * @n: String length in bytes + * @buf: Destination buffer + * @buflen: Destination buffer length in bytes + * @ret: 0 on success, -errno on failure + * + * The string is NUL-terminated. + */ +static int qed_read_string(BlockDriverState *file, uint64_t offset, size_t n, + char *buf, size_t buflen) +{ + int ret; + if (n >= buflen) { + return -EINVAL; + } + ret = bdrv_pread(file, offset, buf, n); + if (ret < 0) { + return ret; + } + buf[n] = '\0'; + return 0; +} + +/** + * Allocate new clusters + * + * @s: QED state + * @n: Number of contiguous clusters to allocate + * @ret: Offset of first allocated cluster + * + * This function only produces the offset where the new clusters should be + * written. It updates BDRVQEDState but does not make any changes to the image + * file. + */ +static uint64_t qed_alloc_clusters(BDRVQEDState *s, unsigned int n) +{ + uint64_t offset = s->file_size; + s->file_size += n * s->header.cluster_size; + return offset; +} + +QEDTable *qed_alloc_table(BDRVQEDState *s) +{ + /* Honor O_DIRECT memory alignment requirements */ + return qemu_blockalign(s->bs, + s->header.cluster_size * s->header.table_size); +} + +/** + * Allocate a new zeroed L2 table + */ +static CachedL2Table *qed_new_l2_table(BDRVQEDState *s) +{ + CachedL2Table *l2_table = qed_alloc_l2_cache_entry(&s->l2_cache); + + l2_table->table = qed_alloc_table(s); + l2_table->offset = qed_alloc_clusters(s, s->header.table_size); + + memset(l2_table->table->offsets, 0, + s->header.cluster_size * s->header.table_size); + return l2_table; +} + +static void qed_aio_next_io(void *opaque, int ret); + +static int bdrv_qed_open(BlockDriverState *bs, int flags) +{ + BDRVQEDState *s = bs->opaque; + QEDHeader le_header; + int64_t file_size; + int ret; + + s->bs = bs; + QSIMPLEQ_INIT(&s->allocating_write_reqs); + + ret = bdrv_pread(bs->file, 0, &le_header, sizeof(le_header)); + if (ret < 0) { + return ret; + } + ret = 0; /* ret should always be 0 or -errno */ + qed_header_le_to_cpu(&le_header, &s->header); + + if (s->header.magic != QED_MAGIC) { + return -EINVAL; + } + if (s->header.features & ~QED_FEATURE_MASK) { + /* image uses unsupported feature bits */ + char buf[64]; + snprintf(buf, sizeof(buf), "%" PRIx64, + s->header.features & ~QED_FEATURE_MASK); + qerror_report(QERR_UNKNOWN_BLOCK_FORMAT_FEATURE, + bs->device_name, "QED", buf); + return -ENOTSUP; + } + if (!qed_is_cluster_size_valid(s->header.cluster_size)) { + return -EINVAL; + } + + /* Round down file size to the last cluster */ + file_size = bdrv_getlength(bs->file); + if (file_size < 0) { + return file_size; + } + s->file_size = qed_start_of_cluster(s, file_size); + + if (!qed_is_table_size_valid(s->header.table_size)) { + return -EINVAL; + } + if (!qed_is_image_size_valid(s->header.image_size, + s->header.cluster_size, + s->header.table_size)) { + return -EINVAL; + } + if (!qed_check_table_offset(s, s->header.l1_table_offset)) { + return -EINVAL; + } + + s->table_nelems = (s->header.cluster_size * s->header.table_size) / + sizeof(uint64_t); + s->l2_shift = ffs(s->header.cluster_size) - 1; + s->l2_mask = s->table_nelems - 1; + s->l1_shift = s->l2_shift + ffs(s->table_nelems) - 1; + + if ((s->header.features & QED_F_BACKING_FILE)) { + if ((uint64_t)s->header.backing_filename_offset + + s->header.backing_filename_size > + s->header.cluster_size * s->header.header_size) { + return -EINVAL; + } + + ret = qed_read_string(bs->file, s->header.backing_filename_offset, + s->header.backing_filename_size, bs->backing_file, + sizeof(bs->backing_file)); + if (ret < 0) { + return ret; + } + + if (s->header.features & QED_F_BACKING_FORMAT_NO_PROBE) { + pstrcpy(bs->backing_format, sizeof(bs->backing_format), "raw"); + } + } + + /* Reset unknown autoclear feature bits. This is a backwards + * compatibility mechanism that allows images to be opened by older + * programs, which "knock out" unknown feature bits. When an image is + * opened by a newer program again it can detect that the autoclear + * feature is no longer valid. + */ + if ((s->header.autoclear_features & ~QED_AUTOCLEAR_FEATURE_MASK) != 0 && + !bdrv_is_read_only(bs->file)) { + s->header.autoclear_features &= QED_AUTOCLEAR_FEATURE_MASK; + + ret = qed_write_header_sync(s); + if (ret) { + return ret; + } + + /* From here on only known autoclear feature bits are valid */ + bdrv_flush(bs->file); + } + + s->l1_table = qed_alloc_table(s); + qed_init_l2_cache(&s->l2_cache); + + ret = qed_read_l1_table_sync(s); + if (ret) { + goto out; + } + + /* If image was not closed cleanly, check consistency */ + if (s->header.features & QED_F_NEED_CHECK) { + /* Read-only images cannot be fixed. There is no risk of corruption + * since write operations are not possible. Therefore, allow + * potentially inconsistent images to be opened read-only. This can + * aid data recovery from an otherwise inconsistent image. + */ + if (!bdrv_is_read_only(bs->file)) { + BdrvCheckResult result = {0}; + + ret = qed_check(s, &result, true); + if (!ret && !result.corruptions && !result.check_errors) { + /* Ensure fixes reach storage before clearing check bit */ + bdrv_flush(s->bs); + + s->header.features &= ~QED_F_NEED_CHECK; + qed_write_header_sync(s); + } + } + } + +out: + if (ret) { + qed_free_l2_cache(&s->l2_cache); + qemu_vfree(s->l1_table); + } + return ret; +} + +static void bdrv_qed_close(BlockDriverState *bs) +{ + BDRVQEDState *s = bs->opaque; + + /* Ensure writes reach stable storage */ + bdrv_flush(bs->file); + + /* Clean shutdown, no check required on next open */ + if (s->header.features & QED_F_NEED_CHECK) { + s->header.features &= ~QED_F_NEED_CHECK; + qed_write_header_sync(s); + } + + qed_free_l2_cache(&s->l2_cache); + qemu_vfree(s->l1_table); +} + +static int bdrv_qed_flush(BlockDriverState *bs) +{ + return bdrv_flush(bs->file); +} + +static int qed_create(const char *filename, uint32_t cluster_size, + uint64_t image_size, uint32_t table_size, + const char *backing_file, const char *backing_fmt) +{ + QEDHeader header = { + .magic = QED_MAGIC, + .cluster_size = cluster_size, + .table_size = table_size, + .header_size = 1, + .features = 0, + .compat_features = 0, + .l1_table_offset = cluster_size, + .image_size = image_size, + }; + QEDHeader le_header; + uint8_t *l1_table = NULL; + size_t l1_size = header.cluster_size * header.table_size; + int ret = 0; + BlockDriverState *bs = NULL; + + ret = bdrv_create_file(filename, NULL); + if (ret < 0) { + return ret; + } + + ret = bdrv_file_open(&bs, filename, BDRV_O_RDWR | BDRV_O_CACHE_WB); + if (ret < 0) { + return ret; + } + + /* File must start empty and grow, check truncate is supported */ + ret = bdrv_truncate(bs, 0); + if (ret < 0) { + goto out; + } + + if (backing_file) { + header.features |= QED_F_BACKING_FILE; + header.backing_filename_offset = sizeof(le_header); + header.backing_filename_size = strlen(backing_file); + + if (qed_fmt_is_raw(backing_fmt)) { + header.features |= QED_F_BACKING_FORMAT_NO_PROBE; + } + } + + qed_header_cpu_to_le(&header, &le_header); + ret = bdrv_pwrite(bs, 0, &le_header, sizeof(le_header)); + if (ret < 0) { + goto out; + } + ret = bdrv_pwrite(bs, sizeof(le_header), backing_file, + header.backing_filename_size); + if (ret < 0) { + goto out; + } + + l1_table = qemu_mallocz(l1_size); + ret = bdrv_pwrite(bs, header.l1_table_offset, l1_table, l1_size); + if (ret < 0) { + goto out; + } + + ret = 0; /* success */ +out: + qemu_free(l1_table); + bdrv_delete(bs); + return ret; +} + +static int bdrv_qed_create(const char *filename, QEMUOptionParameter *options) +{ + uint64_t image_size = 0; + uint32_t cluster_size = QED_DEFAULT_CLUSTER_SIZE; + uint32_t table_size = QED_DEFAULT_TABLE_SIZE; + const char *backing_file = NULL; + const char *backing_fmt = NULL; + + while (options && options->name) { + if (!strcmp(options->name, BLOCK_OPT_SIZE)) { + image_size = options->value.n; + } else if (!strcmp(options->name, BLOCK_OPT_BACKING_FILE)) { + backing_file = options->value.s; + } else if (!strcmp(options->name, BLOCK_OPT_BACKING_FMT)) { + backing_fmt = options->value.s; + } else if (!strcmp(options->name, BLOCK_OPT_CLUSTER_SIZE)) { + if (options->value.n) { + cluster_size = options->value.n; + } + } else if (!strcmp(options->name, BLOCK_OPT_TABLE_SIZE)) { + if (options->value.n) { + table_size = options->value.n; + } + } + options++; + } + + if (!qed_is_cluster_size_valid(cluster_size)) { + fprintf(stderr, "QED cluster size must be within range [%u, %u] and power of 2\n", + QED_MIN_CLUSTER_SIZE, QED_MAX_CLUSTER_SIZE); + return -EINVAL; + } + if (!qed_is_table_size_valid(table_size)) { + fprintf(stderr, "QED table size must be within range [%u, %u] and power of 2\n", + QED_MIN_TABLE_SIZE, QED_MAX_TABLE_SIZE); + return -EINVAL; + } + if (!qed_is_image_size_valid(image_size, cluster_size, table_size)) { + fprintf(stderr, "QED image size must be a non-zero multiple of " + "cluster size and less than %" PRIu64 " bytes\n", + qed_max_image_size(cluster_size, table_size)); + return -EINVAL; + } + + return qed_create(filename, cluster_size, image_size, table_size, + backing_file, backing_fmt); +} + +typedef struct { + int is_allocated; + int *pnum; +} QEDIsAllocatedCB; + +static void qed_is_allocated_cb(void *opaque, int ret, uint64_t offset, size_t len) +{ + QEDIsAllocatedCB *cb = opaque; + *cb->pnum = len / BDRV_SECTOR_SIZE; + cb->is_allocated = ret == QED_CLUSTER_FOUND; +} + +static int bdrv_qed_is_allocated(BlockDriverState *bs, int64_t sector_num, + int nb_sectors, int *pnum) +{ + BDRVQEDState *s = bs->opaque; + uint64_t pos = (uint64_t)sector_num * BDRV_SECTOR_SIZE; + size_t len = (size_t)nb_sectors * BDRV_SECTOR_SIZE; + QEDIsAllocatedCB cb = { + .is_allocated = -1, + .pnum = pnum, + }; + QEDRequest request = { .l2_table = NULL }; + + async_context_push(); + + qed_find_cluster(s, &request, pos, len, qed_is_allocated_cb, &cb); + + while (cb.is_allocated == -1) { + qemu_aio_wait(); + } + + async_context_pop(); + + qed_unref_l2_cache_entry(request.l2_table); + + return cb.is_allocated; +} + +static int bdrv_qed_make_empty(BlockDriverState *bs) +{ + return -ENOTSUP; +} + +static BDRVQEDState *acb_to_s(QEDAIOCB *acb) +{ + return acb->common.bs->opaque; +} + +/** + * Read from the backing file or zero-fill if no backing file + * + * @s: QED state + * @pos: Byte position in device + * @qiov: Destination I/O vector + * @cb: Completion function + * @opaque: User data for completion function + * + * This function reads qiov->size bytes starting at pos from the backing file. + * If there is no backing file then zeroes are read. + */ +static void qed_read_backing_file(BDRVQEDState *s, uint64_t pos, + QEMUIOVector *qiov, + BlockDriverCompletionFunc *cb, void *opaque) +{ + BlockDriverAIOCB *aiocb; + uint64_t backing_length = 0; + size_t size; + + /* If there is a backing file, get its length. Treat the absence of a + * backing file like a zero length backing file. + */ + if (s->bs->backing_hd) { + int64_t l = bdrv_getlength(s->bs->backing_hd); + if (l < 0) { + cb(opaque, l); + return; + } + backing_length = l; + } + + /* Zero all sectors if reading beyond the end of the backing file */ + if (pos >= backing_length || + pos + qiov->size > backing_length) { + qemu_iovec_memset(qiov, 0, qiov->size); + } + + /* Complete now if there are no backing file sectors to read */ + if (pos >= backing_length) { + cb(opaque, 0); + return; + } + + /* If the read straddles the end of the backing file, shorten it */ + size = MIN((uint64_t)backing_length - pos, qiov->size); + + BLKDBG_EVENT(s->bs->file, BLKDBG_READ_BACKING); + aiocb = bdrv_aio_readv(s->bs->backing_hd, pos / BDRV_SECTOR_SIZE, + qiov, size / BDRV_SECTOR_SIZE, cb, opaque); + if (!aiocb) { + cb(opaque, -EIO); + } +} + +typedef struct { + GenericCB gencb; + BDRVQEDState *s; + QEMUIOVector qiov; + struct iovec iov; + uint64_t offset; +} CopyFromBackingFileCB; + +static void qed_copy_from_backing_file_cb(void *opaque, int ret) +{ + CopyFromBackingFileCB *copy_cb = opaque; + qemu_vfree(copy_cb->iov.iov_base); + gencb_complete(©_cb->gencb, ret); +} + +static void qed_copy_from_backing_file_write(void *opaque, int ret) +{ + CopyFromBackingFileCB *copy_cb = opaque; + BDRVQEDState *s = copy_cb->s; + BlockDriverAIOCB *aiocb; + + if (ret) { + qed_copy_from_backing_file_cb(copy_cb, ret); + return; + } + + BLKDBG_EVENT(s->bs->file, BLKDBG_COW_WRITE); + aiocb = bdrv_aio_writev(s->bs->file, copy_cb->offset / BDRV_SECTOR_SIZE, + ©_cb->qiov, + copy_cb->qiov.size / BDRV_SECTOR_SIZE, + qed_copy_from_backing_file_cb, copy_cb); + if (!aiocb) { + qed_copy_from_backing_file_cb(copy_cb, -EIO); + } +} + +/** + * Copy data from backing file into the image + * + * @s: QED state + * @pos: Byte position in device + * @len: Number of bytes + * @offset: Byte offset in image file + * @cb: Completion function + * @opaque: User data for completion function + */ +static void qed_copy_from_backing_file(BDRVQEDState *s, uint64_t pos, + uint64_t len, uint64_t offset, + BlockDriverCompletionFunc *cb, + void *opaque) +{ + CopyFromBackingFileCB *copy_cb; + + /* Skip copy entirely if there is no work to do */ + if (len == 0) { + cb(opaque, 0); + return; + } + + copy_cb = gencb_alloc(sizeof(*copy_cb), cb, opaque); + copy_cb->s = s; + copy_cb->offset = offset; + copy_cb->iov.iov_base = qemu_blockalign(s->bs, len); + copy_cb->iov.iov_len = len; + qemu_iovec_init_external(©_cb->qiov, ©_cb->iov, 1); + + qed_read_backing_file(s, pos, ©_cb->qiov, + qed_copy_from_backing_file_write, copy_cb); +} + +/** + * Link one or more contiguous clusters into a table + * + * @s: QED state + * @table: L2 table + * @index: First cluster index + * @n: Number of contiguous clusters + * @cluster: First cluster byte offset in image file + */ +static void qed_update_l2_table(BDRVQEDState *s, QEDTable *table, int index, + unsigned int n, uint64_t cluster) +{ + int i; + for (i = index; i < index + n; i++) { + table->offsets[i] = cluster; + cluster += s->header.cluster_size; + } +} + +static void qed_aio_complete_bh(void *opaque) +{ + QEDAIOCB *acb = opaque; + BlockDriverCompletionFunc *cb = acb->common.cb; + void *user_opaque = acb->common.opaque; + int ret = acb->bh_ret; + bool *finished = acb->finished; + + qemu_bh_delete(acb->bh); + qemu_aio_release(acb); + + /* Invoke callback */ + cb(user_opaque, ret); + + /* Signal cancel completion */ + if (finished) { + *finished = true; + } +} + +static void qed_aio_complete(QEDAIOCB *acb, int ret) +{ + BDRVQEDState *s = acb_to_s(acb); + + trace_qed_aio_complete(s, acb, ret); + + /* Free resources */ + qemu_iovec_destroy(&acb->cur_qiov); + qed_unref_l2_cache_entry(acb->request.l2_table); + + /* Arrange for a bh to invoke the completion function */ + acb->bh_ret = ret; + acb->bh = qemu_bh_new(qed_aio_complete_bh, acb); + qemu_bh_schedule(acb->bh); + + /* Start next allocating write request waiting behind this one. Note that + * requests enqueue themselves when they first hit an unallocated cluster + * but they wait until the entire request is finished before waking up the + * next request in the queue. This ensures that we don't cycle through + * requests multiple times but rather finish one at a time completely. + */ + if (acb == QSIMPLEQ_FIRST(&s->allocating_write_reqs)) { + QSIMPLEQ_REMOVE_HEAD(&s->allocating_write_reqs, next); + acb = QSIMPLEQ_FIRST(&s->allocating_write_reqs); + if (acb) { + qed_aio_next_io(acb, 0); + } + } +} + +/** + * Commit the current L2 table to the cache + */ +static void qed_commit_l2_update(void *opaque, int ret) +{ + QEDAIOCB *acb = opaque; + BDRVQEDState *s = acb_to_s(acb); + CachedL2Table *l2_table = acb->request.l2_table; + + qed_commit_l2_cache_entry(&s->l2_cache, l2_table); + + /* This is guaranteed to succeed because we just committed the entry to the + * cache. + */ + acb->request.l2_table = qed_find_l2_cache_entry(&s->l2_cache, + l2_table->offset); + assert(acb->request.l2_table != NULL); + + qed_aio_next_io(opaque, ret); +} + +/** + * Update L1 table with new L2 table offset and write it out + */ +static void qed_aio_write_l1_update(void *opaque, int ret) +{ + QEDAIOCB *acb = opaque; + BDRVQEDState *s = acb_to_s(acb); + int index; + + if (ret) { + qed_aio_complete(acb, ret); + return; + } + + index = qed_l1_index(s, acb->cur_pos); + s->l1_table->offsets[index] = acb->request.l2_table->offset; + + qed_write_l1_table(s, index, 1, qed_commit_l2_update, acb); +} + +/** + * Update L2 table with new cluster offsets and write them out + */ +static void qed_aio_write_l2_update(void *opaque, int ret) +{ + QEDAIOCB *acb = opaque; + BDRVQEDState *s = acb_to_s(acb); + bool need_alloc = acb->find_cluster_ret == QED_CLUSTER_L1; + int index; + + if (ret) { + goto err; + } + + if (need_alloc) { + qed_unref_l2_cache_entry(acb->request.l2_table); + acb->request.l2_table = qed_new_l2_table(s); + } + + index = qed_l2_index(s, acb->cur_pos); + qed_update_l2_table(s, acb->request.l2_table->table, index, acb->cur_nclusters, + acb->cur_cluster); + + if (need_alloc) { + /* Write out the whole new L2 table */ + qed_write_l2_table(s, &acb->request, 0, s->table_nelems, true, + qed_aio_write_l1_update, acb); + } else { + /* Write out only the updated part of the L2 table */ + qed_write_l2_table(s, &acb->request, index, acb->cur_nclusters, false, + qed_aio_next_io, acb); + } + return; + +err: + qed_aio_complete(acb, ret); +} + +/** + * Flush new data clusters before updating the L2 table + * + * This flush is necessary when a backing file is in use. A crash during an + * allocating write could result in empty clusters in the image. If the write + * only touched a subregion of the cluster, then backing image sectors have + * been lost in the untouched region. The solution is to flush after writing a + * new data cluster and before updating the L2 table. + */ +static void qed_aio_write_flush_before_l2_update(void *opaque, int ret) +{ + QEDAIOCB *acb = opaque; + BDRVQEDState *s = acb_to_s(acb); + + if (!bdrv_aio_flush(s->bs->file, qed_aio_write_l2_update, opaque)) { + qed_aio_complete(acb, -EIO); + } +} + +/** + * Write data to the image file + */ +static void qed_aio_write_main(void *opaque, int ret) +{ + QEDAIOCB *acb = opaque; + BDRVQEDState *s = acb_to_s(acb); + uint64_t offset = acb->cur_cluster + + qed_offset_into_cluster(s, acb->cur_pos); + BlockDriverCompletionFunc *next_fn; + BlockDriverAIOCB *file_acb; + + trace_qed_aio_write_main(s, acb, ret, offset, acb->cur_qiov.size); + + if (ret) { + qed_aio_complete(acb, ret); + return; + } + + if (acb->find_cluster_ret == QED_CLUSTER_FOUND) { + next_fn = qed_aio_next_io; + } else { + if (s->bs->backing_hd) { + next_fn = qed_aio_write_flush_before_l2_update; + } else { + next_fn = qed_aio_write_l2_update; + } + } + + BLKDBG_EVENT(s->bs->file, BLKDBG_WRITE_AIO); + file_acb = bdrv_aio_writev(s->bs->file, offset / BDRV_SECTOR_SIZE, + &acb->cur_qiov, + acb->cur_qiov.size / BDRV_SECTOR_SIZE, + next_fn, acb); + if (!file_acb) { + qed_aio_complete(acb, -EIO); + } +} + +/** + * Populate back untouched region of new data cluster + */ +static void qed_aio_write_postfill(void *opaque, int ret) +{ + QEDAIOCB *acb = opaque; + BDRVQEDState *s = acb_to_s(acb); + uint64_t start = acb->cur_pos + acb->cur_qiov.size; + uint64_t len = + qed_start_of_cluster(s, start + s->header.cluster_size - 1) - start; + uint64_t offset = acb->cur_cluster + + qed_offset_into_cluster(s, acb->cur_pos) + + acb->cur_qiov.size; + + if (ret) { + qed_aio_complete(acb, ret); + return; + } + + trace_qed_aio_write_postfill(s, acb, start, len, offset); + qed_copy_from_backing_file(s, start, len, offset, + qed_aio_write_main, acb); +} + +/** + * Populate front untouched region of new data cluster + */ +static void qed_aio_write_prefill(void *opaque, int ret) +{ + QEDAIOCB *acb = opaque; + BDRVQEDState *s = acb_to_s(acb); + uint64_t start = qed_start_of_cluster(s, acb->cur_pos); + uint64_t len = qed_offset_into_cluster(s, acb->cur_pos); + + trace_qed_aio_write_prefill(s, acb, start, len, acb->cur_cluster); + qed_copy_from_backing_file(s, start, len, acb->cur_cluster, + qed_aio_write_postfill, acb); +} + +/** + * Check if the QED_F_NEED_CHECK bit should be set during allocating write + */ +static bool qed_should_set_need_check(BDRVQEDState *s) +{ + /* The flush before L2 update path ensures consistency */ + if (s->bs->backing_hd) { + return false; + } + + return !(s->header.features & QED_F_NEED_CHECK); +} + +/** + * Write new data cluster + * + * @acb: Write request + * @len: Length in bytes + * + * This path is taken when writing to previously unallocated clusters. + */ +static void qed_aio_write_alloc(QEDAIOCB *acb, size_t len) +{ + BDRVQEDState *s = acb_to_s(acb); + + /* Freeze this request if another allocating write is in progress */ + if (acb != QSIMPLEQ_FIRST(&s->allocating_write_reqs)) { + QSIMPLEQ_INSERT_TAIL(&s->allocating_write_reqs, acb, next); + } + if (acb != QSIMPLEQ_FIRST(&s->allocating_write_reqs)) { + return; /* wait for existing request to finish */ + } + + acb->cur_nclusters = qed_bytes_to_clusters(s, + qed_offset_into_cluster(s, acb->cur_pos) + len); + acb->cur_cluster = qed_alloc_clusters(s, acb->cur_nclusters); + qemu_iovec_copy(&acb->cur_qiov, acb->qiov, acb->qiov_offset, len); + + if (qed_should_set_need_check(s)) { + s->header.features |= QED_F_NEED_CHECK; + qed_write_header(s, qed_aio_write_prefill, acb); + } else { + qed_aio_write_prefill(acb, 0); + } +} + +/** + * Write data cluster in place + * + * @acb: Write request + * @offset: Cluster offset in bytes + * @len: Length in bytes + * + * This path is taken when writing to already allocated clusters. + */ +static void qed_aio_write_inplace(QEDAIOCB *acb, uint64_t offset, size_t len) +{ + /* Calculate the I/O vector */ + acb->cur_cluster = offset; + qemu_iovec_copy(&acb->cur_qiov, acb->qiov, acb->qiov_offset, len); + + /* Do the actual write */ + qed_aio_write_main(acb, 0); +} + +/** + * Write data cluster + * + * @opaque: Write request + * @ret: QED_CLUSTER_FOUND, QED_CLUSTER_L2, QED_CLUSTER_L1, + * or -errno + * @offset: Cluster offset in bytes + * @len: Length in bytes + * + * Callback from qed_find_cluster(). + */ +static void qed_aio_write_data(void *opaque, int ret, + uint64_t offset, size_t len) +{ + QEDAIOCB *acb = opaque; + + trace_qed_aio_write_data(acb_to_s(acb), acb, ret, offset, len); + + acb->find_cluster_ret = ret; + + switch (ret) { + case QED_CLUSTER_FOUND: + qed_aio_write_inplace(acb, offset, len); + break; + + case QED_CLUSTER_L2: + case QED_CLUSTER_L1: + qed_aio_write_alloc(acb, len); + break; + + default: + qed_aio_complete(acb, ret); + break; + } +} + +/** + * Read data cluster + * + * @opaque: Read request + * @ret: QED_CLUSTER_FOUND, QED_CLUSTER_L2, QED_CLUSTER_L1, + * or -errno + * @offset: Cluster offset in bytes + * @len: Length in bytes + * + * Callback from qed_find_cluster(). + */ +static void qed_aio_read_data(void *opaque, int ret, + uint64_t offset, size_t len) +{ + QEDAIOCB *acb = opaque; + BDRVQEDState *s = acb_to_s(acb); + BlockDriverState *bs = acb->common.bs; + BlockDriverAIOCB *file_acb; + + /* Adjust offset into cluster */ + offset += qed_offset_into_cluster(s, acb->cur_pos); + + trace_qed_aio_read_data(s, acb, ret, offset, len); + + if (ret < 0) { + goto err; + } + + qemu_iovec_copy(&acb->cur_qiov, acb->qiov, acb->qiov_offset, len); + + /* Handle backing file and unallocated sparse hole reads */ + if (ret != QED_CLUSTER_FOUND) { + qed_read_backing_file(s, acb->cur_pos, &acb->cur_qiov, + qed_aio_next_io, acb); + return; + } + + BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO); + file_acb = bdrv_aio_readv(bs->file, offset / BDRV_SECTOR_SIZE, + &acb->cur_qiov, + acb->cur_qiov.size / BDRV_SECTOR_SIZE, + qed_aio_next_io, acb); + if (!file_acb) { + ret = -EIO; + goto err; + } + return; + +err: + qed_aio_complete(acb, ret); +} + +/** + * Begin next I/O or complete the request + */ +static void qed_aio_next_io(void *opaque, int ret) +{ + QEDAIOCB *acb = opaque; + BDRVQEDState *s = acb_to_s(acb); + QEDFindClusterFunc *io_fn = + acb->is_write ? qed_aio_write_data : qed_aio_read_data; + + trace_qed_aio_next_io(s, acb, ret, acb->cur_pos + acb->cur_qiov.size); + + /* Handle I/O error */ + if (ret) { + qed_aio_complete(acb, ret); + return; + } + + acb->qiov_offset += acb->cur_qiov.size; + acb->cur_pos += acb->cur_qiov.size; + qemu_iovec_reset(&acb->cur_qiov); + + /* Complete request */ + if (acb->cur_pos >= acb->end_pos) { + qed_aio_complete(acb, 0); + return; + } + + /* Find next cluster and start I/O */ + qed_find_cluster(s, &acb->request, + acb->cur_pos, acb->end_pos - acb->cur_pos, + io_fn, acb); +} + +static BlockDriverAIOCB *qed_aio_setup(BlockDriverState *bs, + int64_t sector_num, + QEMUIOVector *qiov, int nb_sectors, + BlockDriverCompletionFunc *cb, + void *opaque, bool is_write) +{ + QEDAIOCB *acb = qemu_aio_get(&qed_aio_pool, bs, cb, opaque); + + trace_qed_aio_setup(bs->opaque, acb, sector_num, nb_sectors, + opaque, is_write); + + acb->is_write = is_write; + acb->finished = NULL; + acb->qiov = qiov; + acb->qiov_offset = 0; + acb->cur_pos = (uint64_t)sector_num * BDRV_SECTOR_SIZE; + acb->end_pos = acb->cur_pos + nb_sectors * BDRV_SECTOR_SIZE; + acb->request.l2_table = NULL; + qemu_iovec_init(&acb->cur_qiov, qiov->niov); + + /* Start request */ + qed_aio_next_io(acb, 0); + return &acb->common; +} + +static BlockDriverAIOCB *bdrv_qed_aio_readv(BlockDriverState *bs, + int64_t sector_num, + QEMUIOVector *qiov, int nb_sectors, + BlockDriverCompletionFunc *cb, + void *opaque) +{ + return qed_aio_setup(bs, sector_num, qiov, nb_sectors, cb, opaque, false); +} + +static BlockDriverAIOCB *bdrv_qed_aio_writev(BlockDriverState *bs, + int64_t sector_num, + QEMUIOVector *qiov, int nb_sectors, + BlockDriverCompletionFunc *cb, + void *opaque) +{ + return qed_aio_setup(bs, sector_num, qiov, nb_sectors, cb, opaque, true); +} + +static BlockDriverAIOCB *bdrv_qed_aio_flush(BlockDriverState *bs, + BlockDriverCompletionFunc *cb, + void *opaque) +{ + return bdrv_aio_flush(bs->file, cb, opaque); +} + +static int bdrv_qed_truncate(BlockDriverState *bs, int64_t offset) +{ + return -ENOTSUP; +} + +static int64_t bdrv_qed_getlength(BlockDriverState *bs) +{ + BDRVQEDState *s = bs->opaque; + return s->header.image_size; +} + +static int bdrv_qed_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) +{ + BDRVQEDState *s = bs->opaque; + + memset(bdi, 0, sizeof(*bdi)); + bdi->cluster_size = s->header.cluster_size; + return 0; +} + +static int bdrv_qed_change_backing_file(BlockDriverState *bs, + const char *backing_file, + const char *backing_fmt) +{ + BDRVQEDState *s = bs->opaque; + QEDHeader new_header, le_header; + void *buffer; + size_t buffer_len, backing_file_len; + int ret; + + /* Refuse to set backing filename if unknown compat feature bits are + * active. If the image uses an unknown compat feature then we may not + * know the layout of data following the header structure and cannot safely + * add a new string. + */ + if (backing_file && (s->header.compat_features & + ~QED_COMPAT_FEATURE_MASK)) { + return -ENOTSUP; + } + + memcpy(&new_header, &s->header, sizeof(new_header)); + + new_header.features &= ~(QED_F_BACKING_FILE | + QED_F_BACKING_FORMAT_NO_PROBE); + + /* Adjust feature flags */ + if (backing_file) { + new_header.features |= QED_F_BACKING_FILE; + + if (qed_fmt_is_raw(backing_fmt)) { + new_header.features |= QED_F_BACKING_FORMAT_NO_PROBE; + } + } + + /* Calculate new header size */ + backing_file_len = 0; + + if (backing_file) { + backing_file_len = strlen(backing_file); + } + + buffer_len = sizeof(new_header); + new_header.backing_filename_offset = buffer_len; + new_header.backing_filename_size = backing_file_len; + buffer_len += backing_file_len; + + /* Make sure we can rewrite header without failing */ + if (buffer_len > new_header.header_size * new_header.cluster_size) { + return -ENOSPC; + } + + /* Prepare new header */ + buffer = qemu_malloc(buffer_len); + + qed_header_cpu_to_le(&new_header, &le_header); + memcpy(buffer, &le_header, sizeof(le_header)); + buffer_len = sizeof(le_header); + + memcpy(buffer + buffer_len, backing_file, backing_file_len); + buffer_len += backing_file_len; + + /* Write new header */ + ret = bdrv_pwrite_sync(bs->file, 0, buffer, buffer_len); + qemu_free(buffer); + if (ret == 0) { + memcpy(&s->header, &new_header, sizeof(new_header)); + } + return ret; +} + +static int bdrv_qed_check(BlockDriverState *bs, BdrvCheckResult *result) +{ + BDRVQEDState *s = bs->opaque; + + return qed_check(s, result, false); +} + +static QEMUOptionParameter qed_create_options[] = { + { + .name = BLOCK_OPT_SIZE, + .type = OPT_SIZE, + .help = "Virtual disk size (in bytes)" + }, { + .name = BLOCK_OPT_BACKING_FILE, + .type = OPT_STRING, + .help = "File name of a base image" + }, { + .name = BLOCK_OPT_BACKING_FMT, + .type = OPT_STRING, + .help = "Image format of the base image" + }, { + .name = BLOCK_OPT_CLUSTER_SIZE, + .type = OPT_SIZE, + .help = "Cluster size (in bytes)" + }, { + .name = BLOCK_OPT_TABLE_SIZE, + .type = OPT_SIZE, + .help = "L1/L2 table size (in clusters)" + }, + { /* end of list */ } +}; + +static BlockDriver bdrv_qed = { + .format_name = "qed", + .instance_size = sizeof(BDRVQEDState), + .create_options = qed_create_options, + + .bdrv_probe = bdrv_qed_probe, + .bdrv_open = bdrv_qed_open, + .bdrv_close = bdrv_qed_close, + .bdrv_create = bdrv_qed_create, + .bdrv_flush = bdrv_qed_flush, + .bdrv_is_allocated = bdrv_qed_is_allocated, + .bdrv_make_empty = bdrv_qed_make_empty, + .bdrv_aio_readv = bdrv_qed_aio_readv, + .bdrv_aio_writev = bdrv_qed_aio_writev, + .bdrv_aio_flush = bdrv_qed_aio_flush, + .bdrv_truncate = bdrv_qed_truncate, + .bdrv_getlength = bdrv_qed_getlength, + .bdrv_get_info = bdrv_qed_get_info, + .bdrv_change_backing_file = bdrv_qed_change_backing_file, + .bdrv_check = bdrv_qed_check, +}; + +static void bdrv_qed_init(void) +{ + bdrv_register(&bdrv_qed); +} + +block_init(bdrv_qed_init); diff -Nru qemu-kvm-0.12.5+noroms/block/qed-check.c qemu-kvm-0.14.1/block/qed-check.c --- qemu-kvm-0.12.5+noroms/block/qed-check.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/block/qed-check.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,210 @@ +/* + * QEMU Enhanced Disk Format Consistency Check + * + * Copyright IBM, Corp. 2010 + * + * Authors: + * Stefan Hajnoczi + * + * This work is licensed under the terms of the GNU LGPL, version 2 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#include "qed.h" + +typedef struct { + BDRVQEDState *s; + BdrvCheckResult *result; + bool fix; /* whether to fix invalid offsets */ + + uint64_t nclusters; + uint32_t *used_clusters; /* referenced cluster bitmap */ + + QEDRequest request; +} QEDCheck; + +static bool qed_test_bit(uint32_t *bitmap, uint64_t n) { + return !!(bitmap[n / 32] & (1 << (n % 32))); +} + +static void qed_set_bit(uint32_t *bitmap, uint64_t n) { + bitmap[n / 32] |= 1 << (n % 32); +} + +/** + * Set bitmap bits for clusters + * + * @check: Check structure + * @offset: Starting offset in bytes + * @n: Number of clusters + */ +static bool qed_set_used_clusters(QEDCheck *check, uint64_t offset, + unsigned int n) +{ + uint64_t cluster = qed_bytes_to_clusters(check->s, offset); + unsigned int corruptions = 0; + + while (n-- != 0) { + /* Clusters should only be referenced once */ + if (qed_test_bit(check->used_clusters, cluster)) { + corruptions++; + } + + qed_set_bit(check->used_clusters, cluster); + cluster++; + } + + check->result->corruptions += corruptions; + return corruptions == 0; +} + +/** + * Check an L2 table + * + * @ret: Number of invalid cluster offsets + */ +static unsigned int qed_check_l2_table(QEDCheck *check, QEDTable *table) +{ + BDRVQEDState *s = check->s; + unsigned int i, num_invalid = 0; + + for (i = 0; i < s->table_nelems; i++) { + uint64_t offset = table->offsets[i]; + + if (!offset) { + continue; + } + + /* Detect invalid cluster offset */ + if (!qed_check_cluster_offset(s, offset)) { + if (check->fix) { + table->offsets[i] = 0; + } else { + check->result->corruptions++; + } + + num_invalid++; + continue; + } + + qed_set_used_clusters(check, offset, 1); + } + + return num_invalid; +} + +/** + * Descend tables and check each cluster is referenced once only + */ +static int qed_check_l1_table(QEDCheck *check, QEDTable *table) +{ + BDRVQEDState *s = check->s; + unsigned int i, num_invalid_l1 = 0; + int ret, last_error = 0; + + /* Mark L1 table clusters used */ + qed_set_used_clusters(check, s->header.l1_table_offset, + s->header.table_size); + + for (i = 0; i < s->table_nelems; i++) { + unsigned int num_invalid_l2; + uint64_t offset = table->offsets[i]; + + if (!offset) { + continue; + } + + /* Detect invalid L2 offset */ + if (!qed_check_table_offset(s, offset)) { + /* Clear invalid offset */ + if (check->fix) { + table->offsets[i] = 0; + } else { + check->result->corruptions++; + } + + num_invalid_l1++; + continue; + } + + if (!qed_set_used_clusters(check, offset, s->header.table_size)) { + continue; /* skip an invalid table */ + } + + ret = qed_read_l2_table_sync(s, &check->request, offset); + if (ret) { + check->result->check_errors++; + last_error = ret; + continue; + } + + num_invalid_l2 = qed_check_l2_table(check, + check->request.l2_table->table); + + /* Write out fixed L2 table */ + if (num_invalid_l2 > 0 && check->fix) { + ret = qed_write_l2_table_sync(s, &check->request, 0, + s->table_nelems, false); + if (ret) { + check->result->check_errors++; + last_error = ret; + continue; + } + } + } + + /* Drop reference to final table */ + qed_unref_l2_cache_entry(check->request.l2_table); + check->request.l2_table = NULL; + + /* Write out fixed L1 table */ + if (num_invalid_l1 > 0 && check->fix) { + ret = qed_write_l1_table_sync(s, 0, s->table_nelems); + if (ret) { + check->result->check_errors++; + last_error = ret; + } + } + + return last_error; +} + +/** + * Check for unreferenced (leaked) clusters + */ +static void qed_check_for_leaks(QEDCheck *check) +{ + BDRVQEDState *s = check->s; + uint64_t i; + + for (i = s->header.header_size; i < check->nclusters; i++) { + if (!qed_test_bit(check->used_clusters, i)) { + check->result->leaks++; + } + } +} + +int qed_check(BDRVQEDState *s, BdrvCheckResult *result, bool fix) +{ + QEDCheck check = { + .s = s, + .result = result, + .nclusters = qed_bytes_to_clusters(s, s->file_size), + .request = { .l2_table = NULL }, + .fix = fix, + }; + int ret; + + check.used_clusters = qemu_mallocz(((check.nclusters + 31) / 32) * + sizeof(check.used_clusters[0])); + + ret = qed_check_l1_table(&check, s->l1_table); + if (ret == 0) { + /* Only check for leaks if entire image was scanned successfully */ + qed_check_for_leaks(&check); + } + + qemu_free(check.used_clusters); + return ret; +} diff -Nru qemu-kvm-0.12.5+noroms/block/qed-cluster.c qemu-kvm-0.14.1/block/qed-cluster.c --- qemu-kvm-0.12.5+noroms/block/qed-cluster.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/block/qed-cluster.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,154 @@ +/* + * QEMU Enhanced Disk Format Cluster functions + * + * Copyright IBM, Corp. 2010 + * + * Authors: + * Stefan Hajnoczi + * Anthony Liguori + * + * This work is licensed under the terms of the GNU LGPL, version 2 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#include "qed.h" + +/** + * Count the number of contiguous data clusters + * + * @s: QED state + * @table: L2 table + * @index: First cluster index + * @n: Maximum number of clusters + * @offset: Set to first cluster offset + * + * This function scans tables for contiguous allocated or free clusters. + */ +static unsigned int qed_count_contiguous_clusters(BDRVQEDState *s, + QEDTable *table, + unsigned int index, + unsigned int n, + uint64_t *offset) +{ + unsigned int end = MIN(index + n, s->table_nelems); + uint64_t last = table->offsets[index]; + unsigned int i; + + *offset = last; + + for (i = index + 1; i < end; i++) { + if (last == 0) { + /* Counting free clusters */ + if (table->offsets[i] != 0) { + break; + } + } else { + /* Counting allocated clusters */ + if (table->offsets[i] != last + s->header.cluster_size) { + break; + } + last = table->offsets[i]; + } + } + return i - index; +} + +typedef struct { + BDRVQEDState *s; + uint64_t pos; + size_t len; + + QEDRequest *request; + + /* User callback */ + QEDFindClusterFunc *cb; + void *opaque; +} QEDFindClusterCB; + +static void qed_find_cluster_cb(void *opaque, int ret) +{ + QEDFindClusterCB *find_cluster_cb = opaque; + BDRVQEDState *s = find_cluster_cb->s; + QEDRequest *request = find_cluster_cb->request; + uint64_t offset = 0; + size_t len = 0; + unsigned int index; + unsigned int n; + + if (ret) { + goto out; + } + + index = qed_l2_index(s, find_cluster_cb->pos); + n = qed_bytes_to_clusters(s, + qed_offset_into_cluster(s, find_cluster_cb->pos) + + find_cluster_cb->len); + n = qed_count_contiguous_clusters(s, request->l2_table->table, + index, n, &offset); + + ret = offset ? QED_CLUSTER_FOUND : QED_CLUSTER_L2; + len = MIN(find_cluster_cb->len, n * s->header.cluster_size - + qed_offset_into_cluster(s, find_cluster_cb->pos)); + + if (offset && !qed_check_cluster_offset(s, offset)) { + ret = -EINVAL; + } + +out: + find_cluster_cb->cb(find_cluster_cb->opaque, ret, offset, len); + qemu_free(find_cluster_cb); +} + +/** + * Find the offset of a data cluster + * + * @s: QED state + * @request: L2 cache entry + * @pos: Byte position in device + * @len: Number of bytes + * @cb: Completion function + * @opaque: User data for completion function + * + * This function translates a position in the block device to an offset in the + * image file. It invokes the cb completion callback to report back the + * translated offset or unallocated range in the image file. + * + * If the L2 table exists, request->l2_table points to the L2 table cache entry + * and the caller must free the reference when they are finished. The cache + * entry is exposed in this way to avoid callers having to read the L2 table + * again later during request processing. If request->l2_table is non-NULL it + * will be unreferenced before taking on the new cache entry. + */ +void qed_find_cluster(BDRVQEDState *s, QEDRequest *request, uint64_t pos, + size_t len, QEDFindClusterFunc *cb, void *opaque) +{ + QEDFindClusterCB *find_cluster_cb; + uint64_t l2_offset; + + /* Limit length to L2 boundary. Requests are broken up at the L2 boundary + * so that a request acts on one L2 table at a time. + */ + len = MIN(len, (((pos >> s->l1_shift) + 1) << s->l1_shift) - pos); + + l2_offset = s->l1_table->offsets[qed_l1_index(s, pos)]; + if (!l2_offset) { + cb(opaque, QED_CLUSTER_L1, 0, len); + return; + } + if (!qed_check_table_offset(s, l2_offset)) { + cb(opaque, -EINVAL, 0, 0); + return; + } + + find_cluster_cb = qemu_malloc(sizeof(*find_cluster_cb)); + find_cluster_cb->s = s; + find_cluster_cb->pos = pos; + find_cluster_cb->len = len; + find_cluster_cb->cb = cb; + find_cluster_cb->opaque = opaque; + find_cluster_cb->request = request; + + qed_read_l2_table(s, request, l2_offset, + qed_find_cluster_cb, find_cluster_cb); +} diff -Nru qemu-kvm-0.12.5+noroms/block/qed-gencb.c qemu-kvm-0.14.1/block/qed-gencb.c --- qemu-kvm-0.12.5+noroms/block/qed-gencb.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/block/qed-gencb.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,32 @@ +/* + * QEMU Enhanced Disk Format + * + * Copyright IBM, Corp. 2010 + * + * Authors: + * Stefan Hajnoczi + * + * This work is licensed under the terms of the GNU LGPL, version 2 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#include "qed.h" + +void *gencb_alloc(size_t len, BlockDriverCompletionFunc *cb, void *opaque) +{ + GenericCB *gencb = qemu_malloc(len); + gencb->cb = cb; + gencb->opaque = opaque; + return gencb; +} + +void gencb_complete(void *opaque, int ret) +{ + GenericCB *gencb = opaque; + BlockDriverCompletionFunc *cb = gencb->cb; + void *user_opaque = gencb->opaque; + + qemu_free(gencb); + cb(user_opaque, ret); +} diff -Nru qemu-kvm-0.12.5+noroms/block/qed.h qemu-kvm-0.14.1/block/qed.h --- qemu-kvm-0.12.5+noroms/block/qed.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/block/qed.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,301 @@ +/* + * QEMU Enhanced Disk Format + * + * Copyright IBM, Corp. 2010 + * + * Authors: + * Stefan Hajnoczi + * Anthony Liguori + * + * This work is licensed under the terms of the GNU LGPL, version 2 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#ifndef BLOCK_QED_H +#define BLOCK_QED_H + +#include "block_int.h" + +/* The layout of a QED file is as follows: + * + * +--------+----------+----------+----------+-----+ + * | header | L1 table | cluster0 | cluster1 | ... | + * +--------+----------+----------+----------+-----+ + * + * There is a 2-level pagetable for cluster allocation: + * + * +----------+ + * | L1 table | + * +----------+ + * ,------' | '------. + * +----------+ | +----------+ + * | L2 table | ... | L2 table | + * +----------+ +----------+ + * ,------' | '------. + * +----------+ | +----------+ + * | Data | ... | Data | + * +----------+ +----------+ + * + * The L1 table is fixed size and always present. L2 tables are allocated on + * demand. The L1 table size determines the maximum possible image size; it + * can be influenced using the cluster_size and table_size values. + * + * All fields are little-endian on disk. + */ + +enum { + QED_MAGIC = 'Q' | 'E' << 8 | 'D' << 16 | '\0' << 24, + + /* The image supports a backing file */ + QED_F_BACKING_FILE = 0x01, + + /* The image needs a consistency check before use */ + QED_F_NEED_CHECK = 0x02, + + /* The backing file format must not be probed, treat as raw image */ + QED_F_BACKING_FORMAT_NO_PROBE = 0x04, + + /* Feature bits must be used when the on-disk format changes */ + QED_FEATURE_MASK = QED_F_BACKING_FILE | /* supported feature bits */ + QED_F_NEED_CHECK | + QED_F_BACKING_FORMAT_NO_PROBE, + QED_COMPAT_FEATURE_MASK = 0, /* supported compat feature bits */ + QED_AUTOCLEAR_FEATURE_MASK = 0, /* supported autoclear feature bits */ + + /* Data is stored in groups of sectors called clusters. Cluster size must + * be large to avoid keeping too much metadata. I/O requests that have + * sub-cluster size will require read-modify-write. + */ + QED_MIN_CLUSTER_SIZE = 4 * 1024, /* in bytes */ + QED_MAX_CLUSTER_SIZE = 64 * 1024 * 1024, + QED_DEFAULT_CLUSTER_SIZE = 64 * 1024, + + /* Allocated clusters are tracked using a 2-level pagetable. Table size is + * a multiple of clusters so large maximum image sizes can be supported + * without jacking up the cluster size too much. + */ + QED_MIN_TABLE_SIZE = 1, /* in clusters */ + QED_MAX_TABLE_SIZE = 16, + QED_DEFAULT_TABLE_SIZE = 4, +}; + +typedef struct { + uint32_t magic; /* QED\0 */ + + uint32_t cluster_size; /* in bytes */ + uint32_t table_size; /* for L1 and L2 tables, in clusters */ + uint32_t header_size; /* in clusters */ + + uint64_t features; /* format feature bits */ + uint64_t compat_features; /* compatible feature bits */ + uint64_t autoclear_features; /* self-resetting feature bits */ + + uint64_t l1_table_offset; /* in bytes */ + uint64_t image_size; /* total logical image size, in bytes */ + + /* if (features & QED_F_BACKING_FILE) */ + uint32_t backing_filename_offset; /* in bytes from start of header */ + uint32_t backing_filename_size; /* in bytes */ +} QEDHeader; + +typedef struct { + uint64_t offsets[0]; /* in bytes */ +} QEDTable; + +/* The L2 cache is a simple write-through cache for L2 structures */ +typedef struct CachedL2Table { + QEDTable *table; + uint64_t offset; /* offset=0 indicates an invalidate entry */ + QTAILQ_ENTRY(CachedL2Table) node; + int ref; +} CachedL2Table; + +typedef struct { + QTAILQ_HEAD(, CachedL2Table) entries; + unsigned int n_entries; +} L2TableCache; + +typedef struct QEDRequest { + CachedL2Table *l2_table; +} QEDRequest; + +typedef struct QEDAIOCB { + BlockDriverAIOCB common; + QEMUBH *bh; + int bh_ret; /* final return status for completion bh */ + QSIMPLEQ_ENTRY(QEDAIOCB) next; /* next request */ + bool is_write; /* false - read, true - write */ + bool *finished; /* signal for cancel completion */ + uint64_t end_pos; /* request end on block device, in bytes */ + + /* User scatter-gather list */ + QEMUIOVector *qiov; + size_t qiov_offset; /* byte count already processed */ + + /* Current cluster scatter-gather list */ + QEMUIOVector cur_qiov; + uint64_t cur_pos; /* position on block device, in bytes */ + uint64_t cur_cluster; /* cluster offset in image file */ + unsigned int cur_nclusters; /* number of clusters being accessed */ + int find_cluster_ret; /* used for L1/L2 update */ + + QEDRequest request; +} QEDAIOCB; + +typedef struct { + BlockDriverState *bs; /* device */ + uint64_t file_size; /* length of image file, in bytes */ + + QEDHeader header; /* always cpu-endian */ + QEDTable *l1_table; + L2TableCache l2_cache; /* l2 table cache */ + uint32_t table_nelems; + uint32_t l1_shift; + uint32_t l2_shift; + uint32_t l2_mask; + + /* Allocating write request queue */ + QSIMPLEQ_HEAD(, QEDAIOCB) allocating_write_reqs; +} BDRVQEDState; + +enum { + QED_CLUSTER_FOUND, /* cluster found */ + QED_CLUSTER_L2, /* cluster missing in L2 */ + QED_CLUSTER_L1, /* cluster missing in L1 */ +}; + +/** + * qed_find_cluster() completion callback + * + * @opaque: User data for completion callback + * @ret: QED_CLUSTER_FOUND Success + * QED_CLUSTER_L2 Data cluster unallocated in L2 + * QED_CLUSTER_L1 L2 unallocated in L1 + * -errno POSIX error occurred + * @offset: Data cluster offset + * @len: Contiguous bytes starting from cluster offset + * + * This function is invoked when qed_find_cluster() completes. + * + * On success ret is QED_CLUSTER_FOUND and offset/len are a contiguous range + * in the image file. + * + * On failure ret is QED_CLUSTER_L2 or QED_CLUSTER_L1 for missing L2 or L1 + * table offset, respectively. len is number of contiguous unallocated bytes. + */ +typedef void QEDFindClusterFunc(void *opaque, int ret, uint64_t offset, size_t len); + +/** + * Generic callback for chaining async callbacks + */ +typedef struct { + BlockDriverCompletionFunc *cb; + void *opaque; +} GenericCB; + +void *gencb_alloc(size_t len, BlockDriverCompletionFunc *cb, void *opaque); +void gencb_complete(void *opaque, int ret); + +/** + * L2 cache functions + */ +void qed_init_l2_cache(L2TableCache *l2_cache); +void qed_free_l2_cache(L2TableCache *l2_cache); +CachedL2Table *qed_alloc_l2_cache_entry(L2TableCache *l2_cache); +void qed_unref_l2_cache_entry(CachedL2Table *entry); +CachedL2Table *qed_find_l2_cache_entry(L2TableCache *l2_cache, uint64_t offset); +void qed_commit_l2_cache_entry(L2TableCache *l2_cache, CachedL2Table *l2_table); + +/** + * Table I/O functions + */ +int qed_read_l1_table_sync(BDRVQEDState *s); +void qed_write_l1_table(BDRVQEDState *s, unsigned int index, unsigned int n, + BlockDriverCompletionFunc *cb, void *opaque); +int qed_write_l1_table_sync(BDRVQEDState *s, unsigned int index, + unsigned int n); +int qed_read_l2_table_sync(BDRVQEDState *s, QEDRequest *request, + uint64_t offset); +void qed_read_l2_table(BDRVQEDState *s, QEDRequest *request, uint64_t offset, + BlockDriverCompletionFunc *cb, void *opaque); +void qed_write_l2_table(BDRVQEDState *s, QEDRequest *request, + unsigned int index, unsigned int n, bool flush, + BlockDriverCompletionFunc *cb, void *opaque); +int qed_write_l2_table_sync(BDRVQEDState *s, QEDRequest *request, + unsigned int index, unsigned int n, bool flush); + +/** + * Cluster functions + */ +void qed_find_cluster(BDRVQEDState *s, QEDRequest *request, uint64_t pos, + size_t len, QEDFindClusterFunc *cb, void *opaque); + +/** + * Consistency check + */ +int qed_check(BDRVQEDState *s, BdrvCheckResult *result, bool fix); + +QEDTable *qed_alloc_table(BDRVQEDState *s); + +/** + * Round down to the start of a cluster + */ +static inline uint64_t qed_start_of_cluster(BDRVQEDState *s, uint64_t offset) +{ + return offset & ~(uint64_t)(s->header.cluster_size - 1); +} + +static inline uint64_t qed_offset_into_cluster(BDRVQEDState *s, uint64_t offset) +{ + return offset & (s->header.cluster_size - 1); +} + +static inline uint64_t qed_bytes_to_clusters(BDRVQEDState *s, uint64_t bytes) +{ + return qed_start_of_cluster(s, bytes + (s->header.cluster_size - 1)) / + (s->header.cluster_size - 1); +} + +static inline unsigned int qed_l1_index(BDRVQEDState *s, uint64_t pos) +{ + return pos >> s->l1_shift; +} + +static inline unsigned int qed_l2_index(BDRVQEDState *s, uint64_t pos) +{ + return (pos >> s->l2_shift) & s->l2_mask; +} + +/** + * Test if a cluster offset is valid + */ +static inline bool qed_check_cluster_offset(BDRVQEDState *s, uint64_t offset) +{ + uint64_t header_size = (uint64_t)s->header.header_size * + s->header.cluster_size; + + if (offset & (s->header.cluster_size - 1)) { + return false; + } + return offset >= header_size && offset < s->file_size; +} + +/** + * Test if a table offset is valid + */ +static inline bool qed_check_table_offset(BDRVQEDState *s, uint64_t offset) +{ + uint64_t end_offset = offset + (s->header.table_size - 1) * + s->header.cluster_size; + + /* Overflow check */ + if (end_offset <= offset) { + return false; + } + + return qed_check_cluster_offset(s, offset) && + qed_check_cluster_offset(s, end_offset); +} + +#endif /* BLOCK_QED_H */ diff -Nru qemu-kvm-0.12.5+noroms/block/qed-l2-cache.c qemu-kvm-0.14.1/block/qed-l2-cache.c --- qemu-kvm-0.12.5+noroms/block/qed-l2-cache.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/block/qed-l2-cache.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,173 @@ +/* + * QEMU Enhanced Disk Format L2 Cache + * + * Copyright IBM, Corp. 2010 + * + * Authors: + * Anthony Liguori + * + * This work is licensed under the terms of the GNU LGPL, version 2 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +/* + * L2 table cache usage is as follows: + * + * An open image has one L2 table cache that is used to avoid accessing the + * image file for recently referenced L2 tables. + * + * Cluster offset lookup translates the logical offset within the block device + * to a cluster offset within the image file. This is done by indexing into + * the L1 and L2 tables which store cluster offsets. It is here where the L2 + * table cache serves up recently referenced L2 tables. + * + * If there is a cache miss, that L2 table is read from the image file and + * committed to the cache. Subsequent accesses to that L2 table will be served + * from the cache until the table is evicted from the cache. + * + * L2 tables are also committed to the cache when new L2 tables are allocated + * in the image file. Since the L2 table cache is write-through, the new L2 + * table is first written out to the image file and then committed to the + * cache. + * + * Multiple I/O requests may be using an L2 table cache entry at any given + * time. That means an entry may be in use across several requests and + * reference counting is needed to free the entry at the correct time. In + * particular, an entry evicted from the cache will only be freed once all + * references are dropped. + * + * An in-flight I/O request will hold a reference to a L2 table cache entry for + * the period during which it needs to access the L2 table. This includes + * cluster offset lookup, L2 table allocation, and L2 table update when a new + * data cluster has been allocated. + * + * An interesting case occurs when two requests need to access an L2 table that + * is not in the cache. Since the operation to read the table from the image + * file takes some time to complete, both requests may see a cache miss and + * start reading the L2 table from the image file. The first to finish will + * commit its L2 table into the cache. When the second tries to commit its + * table will be deleted in favor of the existing cache entry. + */ + +#include "trace.h" +#include "qed.h" + +/* Each L2 holds 2GB so this let's us fully cache a 100GB disk */ +#define MAX_L2_CACHE_SIZE 50 + +/** + * Initialize the L2 cache + */ +void qed_init_l2_cache(L2TableCache *l2_cache) +{ + QTAILQ_INIT(&l2_cache->entries); + l2_cache->n_entries = 0; +} + +/** + * Free the L2 cache + */ +void qed_free_l2_cache(L2TableCache *l2_cache) +{ + CachedL2Table *entry, *next_entry; + + QTAILQ_FOREACH_SAFE(entry, &l2_cache->entries, node, next_entry) { + qemu_vfree(entry->table); + qemu_free(entry); + } +} + +/** + * Allocate an uninitialized entry from the cache + * + * The returned entry has a reference count of 1 and is owned by the caller. + * The caller must allocate the actual table field for this entry and it must + * be freeable using qemu_vfree(). + */ +CachedL2Table *qed_alloc_l2_cache_entry(L2TableCache *l2_cache) +{ + CachedL2Table *entry; + + entry = qemu_mallocz(sizeof(*entry)); + entry->ref++; + + trace_qed_alloc_l2_cache_entry(l2_cache, entry); + + return entry; +} + +/** + * Decrease an entry's reference count and free if necessary when the reference + * count drops to zero. + */ +void qed_unref_l2_cache_entry(CachedL2Table *entry) +{ + if (!entry) { + return; + } + + entry->ref--; + trace_qed_unref_l2_cache_entry(entry, entry->ref); + if (entry->ref == 0) { + qemu_vfree(entry->table); + qemu_free(entry); + } +} + +/** + * Find an entry in the L2 cache. This may return NULL and it's up to the + * caller to satisfy the cache miss. + * + * For a cached entry, this function increases the reference count and returns + * the entry. + */ +CachedL2Table *qed_find_l2_cache_entry(L2TableCache *l2_cache, uint64_t offset) +{ + CachedL2Table *entry; + + QTAILQ_FOREACH(entry, &l2_cache->entries, node) { + if (entry->offset == offset) { + trace_qed_find_l2_cache_entry(l2_cache, entry, offset, entry->ref); + entry->ref++; + return entry; + } + } + return NULL; +} + +/** + * Commit an L2 cache entry into the cache. This is meant to be used as part of + * the process to satisfy a cache miss. A caller would allocate an entry which + * is not actually in the L2 cache and then once the entry was valid and + * present on disk, the entry can be committed into the cache. + * + * Since the cache is write-through, it's important that this function is not + * called until the entry is present on disk and the L1 has been updated to + * point to the entry. + * + * N.B. This function steals a reference to the l2_table from the caller so the + * caller must obtain a new reference by issuing a call to + * qed_find_l2_cache_entry(). + */ +void qed_commit_l2_cache_entry(L2TableCache *l2_cache, CachedL2Table *l2_table) +{ + CachedL2Table *entry; + + entry = qed_find_l2_cache_entry(l2_cache, l2_table->offset); + if (entry) { + qed_unref_l2_cache_entry(entry); + qed_unref_l2_cache_entry(l2_table); + return; + } + + if (l2_cache->n_entries >= MAX_L2_CACHE_SIZE) { + entry = QTAILQ_FIRST(&l2_cache->entries); + QTAILQ_REMOVE(&l2_cache->entries, entry, node); + l2_cache->n_entries--; + qed_unref_l2_cache_entry(entry); + } + + l2_cache->n_entries++; + QTAILQ_INSERT_TAIL(&l2_cache->entries, l2_table, node); +} diff -Nru qemu-kvm-0.12.5+noroms/block/qed-table.c qemu-kvm-0.14.1/block/qed-table.c --- qemu-kvm-0.12.5+noroms/block/qed-table.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/block/qed-table.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,319 @@ +/* + * QEMU Enhanced Disk Format Table I/O + * + * Copyright IBM, Corp. 2010 + * + * Authors: + * Stefan Hajnoczi + * Anthony Liguori + * + * This work is licensed under the terms of the GNU LGPL, version 2 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#include "trace.h" +#include "qemu_socket.h" /* for EINPROGRESS on Windows */ +#include "qed.h" + +typedef struct { + GenericCB gencb; + BDRVQEDState *s; + QEDTable *table; + + struct iovec iov; + QEMUIOVector qiov; +} QEDReadTableCB; + +static void qed_read_table_cb(void *opaque, int ret) +{ + QEDReadTableCB *read_table_cb = opaque; + QEDTable *table = read_table_cb->table; + int noffsets = read_table_cb->iov.iov_len / sizeof(uint64_t); + int i; + + /* Handle I/O error */ + if (ret) { + goto out; + } + + /* Byteswap offsets */ + for (i = 0; i < noffsets; i++) { + table->offsets[i] = le64_to_cpu(table->offsets[i]); + } + +out: + /* Completion */ + trace_qed_read_table_cb(read_table_cb->s, read_table_cb->table, ret); + gencb_complete(&read_table_cb->gencb, ret); +} + +static void qed_read_table(BDRVQEDState *s, uint64_t offset, QEDTable *table, + BlockDriverCompletionFunc *cb, void *opaque) +{ + QEDReadTableCB *read_table_cb = gencb_alloc(sizeof(*read_table_cb), + cb, opaque); + QEMUIOVector *qiov = &read_table_cb->qiov; + BlockDriverAIOCB *aiocb; + + trace_qed_read_table(s, offset, table); + + read_table_cb->s = s; + read_table_cb->table = table; + read_table_cb->iov.iov_base = table->offsets, + read_table_cb->iov.iov_len = s->header.cluster_size * s->header.table_size, + + qemu_iovec_init_external(qiov, &read_table_cb->iov, 1); + aiocb = bdrv_aio_readv(s->bs->file, offset / BDRV_SECTOR_SIZE, qiov, + read_table_cb->iov.iov_len / BDRV_SECTOR_SIZE, + qed_read_table_cb, read_table_cb); + if (!aiocb) { + qed_read_table_cb(read_table_cb, -EIO); + } +} + +typedef struct { + GenericCB gencb; + BDRVQEDState *s; + QEDTable *orig_table; + QEDTable *table; + bool flush; /* flush after write? */ + + struct iovec iov; + QEMUIOVector qiov; +} QEDWriteTableCB; + +static void qed_write_table_cb(void *opaque, int ret) +{ + QEDWriteTableCB *write_table_cb = opaque; + + trace_qed_write_table_cb(write_table_cb->s, + write_table_cb->orig_table, + write_table_cb->flush, + ret); + + if (ret) { + goto out; + } + + if (write_table_cb->flush) { + /* We still need to flush first */ + write_table_cb->flush = false; + bdrv_aio_flush(write_table_cb->s->bs, qed_write_table_cb, + write_table_cb); + return; + } + +out: + qemu_vfree(write_table_cb->table); + gencb_complete(&write_table_cb->gencb, ret); + return; +} + +/** + * Write out an updated part or all of a table + * + * @s: QED state + * @offset: Offset of table in image file, in bytes + * @table: Table + * @index: Index of first element + * @n: Number of elements + * @flush: Whether or not to sync to disk + * @cb: Completion function + * @opaque: Argument for completion function + */ +static void qed_write_table(BDRVQEDState *s, uint64_t offset, QEDTable *table, + unsigned int index, unsigned int n, bool flush, + BlockDriverCompletionFunc *cb, void *opaque) +{ + QEDWriteTableCB *write_table_cb; + BlockDriverAIOCB *aiocb; + unsigned int sector_mask = BDRV_SECTOR_SIZE / sizeof(uint64_t) - 1; + unsigned int start, end, i; + size_t len_bytes; + + trace_qed_write_table(s, offset, table, index, n); + + /* Calculate indices of the first and one after last elements */ + start = index & ~sector_mask; + end = (index + n + sector_mask) & ~sector_mask; + + len_bytes = (end - start) * sizeof(uint64_t); + + write_table_cb = gencb_alloc(sizeof(*write_table_cb), cb, opaque); + write_table_cb->s = s; + write_table_cb->orig_table = table; + write_table_cb->flush = flush; + write_table_cb->table = qemu_blockalign(s->bs, len_bytes); + write_table_cb->iov.iov_base = write_table_cb->table->offsets; + write_table_cb->iov.iov_len = len_bytes; + qemu_iovec_init_external(&write_table_cb->qiov, &write_table_cb->iov, 1); + + /* Byteswap table */ + for (i = start; i < end; i++) { + uint64_t le_offset = cpu_to_le64(table->offsets[i]); + write_table_cb->table->offsets[i - start] = le_offset; + } + + /* Adjust for offset into table */ + offset += start * sizeof(uint64_t); + + aiocb = bdrv_aio_writev(s->bs->file, offset / BDRV_SECTOR_SIZE, + &write_table_cb->qiov, + write_table_cb->iov.iov_len / BDRV_SECTOR_SIZE, + qed_write_table_cb, write_table_cb); + if (!aiocb) { + qed_write_table_cb(write_table_cb, -EIO); + } +} + +/** + * Propagate return value from async callback + */ +static void qed_sync_cb(void *opaque, int ret) +{ + *(int *)opaque = ret; +} + +int qed_read_l1_table_sync(BDRVQEDState *s) +{ + int ret = -EINPROGRESS; + + async_context_push(); + + qed_read_table(s, s->header.l1_table_offset, + s->l1_table, qed_sync_cb, &ret); + while (ret == -EINPROGRESS) { + qemu_aio_wait(); + } + + async_context_pop(); + + return ret; +} + +void qed_write_l1_table(BDRVQEDState *s, unsigned int index, unsigned int n, + BlockDriverCompletionFunc *cb, void *opaque) +{ + BLKDBG_EVENT(s->bs->file, BLKDBG_L1_UPDATE); + qed_write_table(s, s->header.l1_table_offset, + s->l1_table, index, n, false, cb, opaque); +} + +int qed_write_l1_table_sync(BDRVQEDState *s, unsigned int index, + unsigned int n) +{ + int ret = -EINPROGRESS; + + async_context_push(); + + qed_write_l1_table(s, index, n, qed_sync_cb, &ret); + while (ret == -EINPROGRESS) { + qemu_aio_wait(); + } + + async_context_pop(); + + return ret; +} + +typedef struct { + GenericCB gencb; + BDRVQEDState *s; + uint64_t l2_offset; + QEDRequest *request; +} QEDReadL2TableCB; + +static void qed_read_l2_table_cb(void *opaque, int ret) +{ + QEDReadL2TableCB *read_l2_table_cb = opaque; + QEDRequest *request = read_l2_table_cb->request; + BDRVQEDState *s = read_l2_table_cb->s; + CachedL2Table *l2_table = request->l2_table; + + if (ret) { + /* can't trust loaded L2 table anymore */ + qed_unref_l2_cache_entry(l2_table); + request->l2_table = NULL; + } else { + l2_table->offset = read_l2_table_cb->l2_offset; + + qed_commit_l2_cache_entry(&s->l2_cache, l2_table); + + /* This is guaranteed to succeed because we just committed the entry + * to the cache. + */ + request->l2_table = qed_find_l2_cache_entry(&s->l2_cache, + l2_table->offset); + assert(request->l2_table != NULL); + } + + gencb_complete(&read_l2_table_cb->gencb, ret); +} + +void qed_read_l2_table(BDRVQEDState *s, QEDRequest *request, uint64_t offset, + BlockDriverCompletionFunc *cb, void *opaque) +{ + QEDReadL2TableCB *read_l2_table_cb; + + qed_unref_l2_cache_entry(request->l2_table); + + /* Check for cached L2 entry */ + request->l2_table = qed_find_l2_cache_entry(&s->l2_cache, offset); + if (request->l2_table) { + cb(opaque, 0); + return; + } + + request->l2_table = qed_alloc_l2_cache_entry(&s->l2_cache); + request->l2_table->table = qed_alloc_table(s); + + read_l2_table_cb = gencb_alloc(sizeof(*read_l2_table_cb), cb, opaque); + read_l2_table_cb->s = s; + read_l2_table_cb->l2_offset = offset; + read_l2_table_cb->request = request; + + BLKDBG_EVENT(s->bs->file, BLKDBG_L2_LOAD); + qed_read_table(s, offset, request->l2_table->table, + qed_read_l2_table_cb, read_l2_table_cb); +} + +int qed_read_l2_table_sync(BDRVQEDState *s, QEDRequest *request, uint64_t offset) +{ + int ret = -EINPROGRESS; + + async_context_push(); + + qed_read_l2_table(s, request, offset, qed_sync_cb, &ret); + while (ret == -EINPROGRESS) { + qemu_aio_wait(); + } + + async_context_pop(); + return ret; +} + +void qed_write_l2_table(BDRVQEDState *s, QEDRequest *request, + unsigned int index, unsigned int n, bool flush, + BlockDriverCompletionFunc *cb, void *opaque) +{ + BLKDBG_EVENT(s->bs->file, BLKDBG_L2_UPDATE); + qed_write_table(s, request->l2_table->offset, + request->l2_table->table, index, n, flush, cb, opaque); +} + +int qed_write_l2_table_sync(BDRVQEDState *s, QEDRequest *request, + unsigned int index, unsigned int n, bool flush) +{ + int ret = -EINPROGRESS; + + async_context_push(); + + qed_write_l2_table(s, request, index, n, flush, qed_sync_cb, &ret); + while (ret == -EINPROGRESS) { + qemu_aio_wait(); + } + + async_context_pop(); + return ret; +} diff -Nru qemu-kvm-0.12.5+noroms/block/raw.c qemu-kvm-0.14.1/block/raw.c --- qemu-kvm-0.12.5+noroms/block/raw.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/block/raw.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,156 @@ + +#include "qemu-common.h" +#include "block_int.h" +#include "module.h" + +static int raw_open(BlockDriverState *bs, int flags) +{ + bs->sg = bs->file->sg; + return 0; +} + +static int raw_read(BlockDriverState *bs, int64_t sector_num, + uint8_t *buf, int nb_sectors) +{ + return bdrv_read(bs->file, sector_num, buf, nb_sectors); +} + +static int raw_write(BlockDriverState *bs, int64_t sector_num, + const uint8_t *buf, int nb_sectors) +{ + return bdrv_write(bs->file, sector_num, buf, nb_sectors); +} + +static BlockDriverAIOCB *raw_aio_readv(BlockDriverState *bs, + int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, + BlockDriverCompletionFunc *cb, void *opaque) +{ + return bdrv_aio_readv(bs->file, sector_num, qiov, nb_sectors, cb, opaque); +} + +static BlockDriverAIOCB *raw_aio_writev(BlockDriverState *bs, + int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, + BlockDriverCompletionFunc *cb, void *opaque) +{ + return bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors, cb, opaque); +} + +static void raw_close(BlockDriverState *bs) +{ +} + +static int raw_flush(BlockDriverState *bs) +{ + return bdrv_flush(bs->file); +} + +static BlockDriverAIOCB *raw_aio_flush(BlockDriverState *bs, + BlockDriverCompletionFunc *cb, void *opaque) +{ + return bdrv_aio_flush(bs->file, cb, opaque); +} + +static int64_t raw_getlength(BlockDriverState *bs) +{ + return bdrv_getlength(bs->file); +} + +static int raw_truncate(BlockDriverState *bs, int64_t offset) +{ + return bdrv_truncate(bs->file, offset); +} + +static int raw_probe(const uint8_t *buf, int buf_size, const char *filename) +{ + return 1; /* everything can be opened as raw image */ +} + +static int raw_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors) +{ + return bdrv_discard(bs->file, sector_num, nb_sectors); +} + +static int raw_is_inserted(BlockDriverState *bs) +{ + return bdrv_is_inserted(bs->file); +} + +static int raw_eject(BlockDriverState *bs, int eject_flag) +{ + return bdrv_eject(bs->file, eject_flag); +} + +static int raw_set_locked(BlockDriverState *bs, int locked) +{ + bdrv_set_locked(bs->file, locked); + return 0; +} + +static int raw_ioctl(BlockDriverState *bs, unsigned long int req, void *buf) +{ + return bdrv_ioctl(bs->file, req, buf); +} + +static BlockDriverAIOCB *raw_aio_ioctl(BlockDriverState *bs, + unsigned long int req, void *buf, + BlockDriverCompletionFunc *cb, void *opaque) +{ + return bdrv_aio_ioctl(bs->file, req, buf, cb, opaque); +} + +static int raw_create(const char *filename, QEMUOptionParameter *options) +{ + return bdrv_create_file(filename, options); +} + +static QEMUOptionParameter raw_create_options[] = { + { + .name = BLOCK_OPT_SIZE, + .type = OPT_SIZE, + .help = "Virtual disk size" + }, + { NULL } +}; + +static int raw_has_zero_init(BlockDriverState *bs) +{ + return bdrv_has_zero_init(bs->file); +} + +static BlockDriver bdrv_raw = { + .format_name = "raw", + + /* It's really 0, but we need to make qemu_malloc() happy */ + .instance_size = 1, + + .bdrv_open = raw_open, + .bdrv_close = raw_close, + .bdrv_read = raw_read, + .bdrv_write = raw_write, + .bdrv_flush = raw_flush, + .bdrv_probe = raw_probe, + .bdrv_getlength = raw_getlength, + .bdrv_truncate = raw_truncate, + + .bdrv_aio_readv = raw_aio_readv, + .bdrv_aio_writev = raw_aio_writev, + .bdrv_aio_flush = raw_aio_flush, + .bdrv_discard = raw_discard, + + .bdrv_is_inserted = raw_is_inserted, + .bdrv_eject = raw_eject, + .bdrv_set_locked = raw_set_locked, + .bdrv_ioctl = raw_ioctl, + .bdrv_aio_ioctl = raw_aio_ioctl, + + .bdrv_create = raw_create, + .create_options = raw_create_options, + .bdrv_has_zero_init = raw_has_zero_init, +}; + +static void bdrv_raw_init(void) +{ + bdrv_register(&bdrv_raw); +} + +block_init(bdrv_raw_init); diff -Nru qemu-kvm-0.12.5+noroms/block/raw-posix.c qemu-kvm-0.14.1/block/raw-posix.c --- qemu-kvm-0.12.5+noroms/block/raw-posix.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/block/raw-posix.c 2011-05-11 13:29:46.000000000 +0000 @@ -50,6 +50,7 @@ #endif #ifdef __linux__ #include +#include #include #include #endif @@ -70,6 +71,10 @@ #include #endif +#ifdef CONFIG_XFS +#include +#endif + //#define DEBUG_FLOPPY //#define DEBUG_BLOCK @@ -98,16 +103,15 @@ #define FTYPE_CD 1 #define FTYPE_FD 2 -#define ALIGNED_BUFFER_SIZE (32 * 512) - -/* if the FD is not accessed during that time (in ms), we try to +/* if the FD is not accessed during that time (in ns), we try to reopen it to see if the disk has been changed */ -#define FD_OPEN_TIMEOUT 1000 +#define FD_OPEN_TIMEOUT (1000000000) + +#define MAX_BLOCKSIZE 4096 typedef struct BDRVRawState { int fd; int type; - unsigned int lseek_err_cnt; int open_flags; #if defined(__linux__) /* linux floppy specific */ @@ -120,7 +124,11 @@ int use_aio; void *aio_ctx; #endif - uint8_t* aligned_buf; + uint8_t *aligned_buf; + unsigned aligned_buf_size; +#ifdef CONFIG_XFS + bool is_xfs : 1; +#endif } BDRVRawState; static int fd_open(BlockDriverState *bs); @@ -136,15 +144,12 @@ BDRVRawState *s = bs->opaque; int fd, ret; - s->lseek_err_cnt = 0; - s->open_flags = open_flags | O_BINARY; s->open_flags &= ~O_ACCMODE; - if ((bdrv_flags & BDRV_O_ACCESS) == BDRV_O_RDWR) { + if (bdrv_flags & BDRV_O_RDWR) { s->open_flags |= O_RDWR; } else { s->open_flags |= O_RDONLY; - bs->read_only = 1; } /* Use O_DSYNC for write-through caching, no flags for write-back caching, @@ -166,7 +171,12 @@ s->aligned_buf = NULL; if ((bdrv_flags & BDRV_O_NOCACHE)) { - s->aligned_buf = qemu_blockalign(bs, ALIGNED_BUFFER_SIZE); + /* + * Allocate a buffer for read/modify/write cycles. Chose the size + * pessimistically as we don't know the block size yet. + */ + s->aligned_buf_size = 32 * MAX_BLOCKSIZE; + s->aligned_buf = qemu_memalign(MAX_BLOCKSIZE, s->aligned_buf_size); if (s->aligned_buf == NULL) { goto out_close; } @@ -195,6 +205,12 @@ #endif } +#ifdef CONFIG_XFS + if (platform_test_xfs_fd(s->fd)) { + s->is_xfs = 1; + } +#endif + return 0; out_free_buf: @@ -207,13 +223,9 @@ static int raw_open(BlockDriverState *bs, const char *filename, int flags) { BDRVRawState *s = bs->opaque; - int open_flags = 0; s->type = FTYPE_FILE; - if (flags & BDRV_O_CREAT) - open_flags = O_CREAT | O_TRUNC; - - return raw_open_common(bs, filename, flags, open_flags); + return raw_open_common(bs, filename, flags, 0); } /* XXX: use host sector size if necessary with: @@ -226,7 +238,7 @@ } #endif #ifdef CONFIG_COCOA - u_int32_t blockSize = 512; + uint32_t blockSize = 512; if ( !ioctl( fd, DKIOCGETBLOCKSIZE, &blockSize ) && blockSize > bufsize) { bufsize = blockSize; } @@ -250,29 +262,16 @@ if (ret < 0) return ret; - if (offset >= 0 && lseek(s->fd, offset, SEEK_SET) == (off_t)-1) { - ++(s->lseek_err_cnt); - if(s->lseek_err_cnt <= 10) { - DEBUG_BLOCK_PRINT("raw_pread(%d:%s, %" PRId64 ", %p, %d) [%" PRId64 - "] lseek failed : %d = %s\n", - s->fd, bs->filename, offset, buf, count, - bs->total_sectors, errno, strerror(errno)); - } - return -1; - } - s->lseek_err_cnt=0; - - ret = read(s->fd, buf, count); + ret = pread(s->fd, buf, count, offset); if (ret == count) - goto label__raw_read__success; + return ret; /* Allow reads beyond the end (needed for pwrite) */ if ((ret == 0) && bs->growable) { int64_t size = raw_getlength(bs); if (offset >= size) { memset(buf, 0, count); - ret = count; - goto label__raw_read__success; + return count; } } @@ -282,15 +281,13 @@ bs->total_sectors, ret, errno, strerror(errno)); /* Try harder for CDrom. */ - if (bs->type == BDRV_TYPE_CDROM) { - lseek(s->fd, offset, SEEK_SET); - ret = read(s->fd, buf, count); + if (s->type != FTYPE_FILE) { + ret = pread(s->fd, buf, count, offset); if (ret == count) - goto label__raw_read__success; - lseek(s->fd, offset, SEEK_SET); - ret = read(s->fd, buf, count); + return ret; + ret = pread(s->fd, buf, count, offset); if (ret == count) - goto label__raw_read__success; + return ret; DEBUG_BLOCK_PRINT("raw_pread(%d:%s, %" PRId64 ", %p, %d) [%" PRId64 "] retry read failed %d : %d = %s\n", @@ -298,14 +295,13 @@ bs->total_sectors, ret, errno, strerror(errno)); } -label__raw_read__success: - return (ret < 0) ? -errno : ret; } /* - * offset and count are in bytes, but must be multiples of 512 for files - * opened with O_DIRECT. buf must be aligned to 512 bytes then. + * offset and count are in bytes, but must be multiples of the sector size + * for files opened with O_DIRECT. buf must be aligned to sector size bytes + * then. * * This function may be called without alignment if the caller ensures * that O_DIRECT is not in effect. @@ -320,29 +316,15 @@ if (ret < 0) return -errno; - if (offset >= 0 && lseek(s->fd, offset, SEEK_SET) == (off_t)-1) { - ++(s->lseek_err_cnt); - if(s->lseek_err_cnt) { - DEBUG_BLOCK_PRINT("raw_pwrite(%d:%s, %" PRId64 ", %p, %d) [%" - PRId64 "] lseek failed : %d = %s\n", - s->fd, bs->filename, offset, buf, count, - bs->total_sectors, errno, strerror(errno)); - } - return -EIO; - } - s->lseek_err_cnt = 0; - - ret = write(s->fd, buf, count); + ret = pwrite(s->fd, buf, count, offset); if (ret == count) - goto label__raw_write__success; + return ret; DEBUG_BLOCK_PRINT("raw_pwrite(%d:%s, %" PRId64 ", %p, %d) [%" PRId64 "] write failed %d : %d = %s\n", s->fd, bs->filename, offset, buf, count, bs->total_sectors, ret, errno, strerror(errno)); -label__raw_write__success: - return (ret < 0) ? -errno : ret; } @@ -356,24 +338,25 @@ uint8_t *buf, int count) { BDRVRawState *s = bs->opaque; + unsigned sector_mask = bs->buffer_alignment - 1; int size, ret, shift, sum; sum = 0; if (s->aligned_buf != NULL) { - if (offset & 0x1ff) { - /* align offset on a 512 bytes boundary */ + if (offset & sector_mask) { + /* align offset on a sector size bytes boundary */ - shift = offset & 0x1ff; - size = (shift + count + 0x1ff) & ~0x1ff; - if (size > ALIGNED_BUFFER_SIZE) - size = ALIGNED_BUFFER_SIZE; + shift = offset & sector_mask; + size = (shift + count + sector_mask) & ~sector_mask; + if (size > s->aligned_buf_size) + size = s->aligned_buf_size; ret = raw_pread_aligned(bs, offset - shift, s->aligned_buf, size); if (ret < 0) return ret; - size = 512 - shift; + size = bs->buffer_alignment - shift; if (size > count) size = count; memcpy(buf, s->aligned_buf + shift, size); @@ -386,19 +369,23 @@ if (count == 0) return sum; } - if (count & 0x1ff || (uintptr_t) buf & 0x1ff) { + if (count & sector_mask || (uintptr_t) buf & sector_mask) { /* read on aligned buffer */ while (count) { - size = (count + 0x1ff) & ~0x1ff; - if (size > ALIGNED_BUFFER_SIZE) - size = ALIGNED_BUFFER_SIZE; + size = (count + sector_mask) & ~sector_mask; + if (size > s->aligned_buf_size) + size = s->aligned_buf_size; ret = raw_pread_aligned(bs, offset, s->aligned_buf, size); - if (ret < 0) + if (ret < 0) { return ret; + } else if (ret == 0) { + fprintf(stderr, "raw_pread: read beyond end of file\n"); + abort(); + } size = ret; if (size > count) @@ -424,8 +411,9 @@ { int ret; - ret = raw_pread(bs, sector_num * 512, buf, nb_sectors * 512); - if (ret == (nb_sectors * 512)) + ret = raw_pread(bs, sector_num * BDRV_SECTOR_SIZE, buf, + nb_sectors * BDRV_SECTOR_SIZE); + if (ret == (nb_sectors * BDRV_SECTOR_SIZE)) ret = 0; return ret; } @@ -439,25 +427,28 @@ const uint8_t *buf, int count) { BDRVRawState *s = bs->opaque; + unsigned sector_mask = bs->buffer_alignment - 1; int size, ret, shift, sum; sum = 0; if (s->aligned_buf != NULL) { - if (offset & 0x1ff) { - /* align offset on a 512 bytes boundary */ - shift = offset & 0x1ff; - ret = raw_pread_aligned(bs, offset - shift, s->aligned_buf, 512); + if (offset & sector_mask) { + /* align offset on a sector size bytes boundary */ + shift = offset & sector_mask; + ret = raw_pread_aligned(bs, offset - shift, s->aligned_buf, + bs->buffer_alignment); if (ret < 0) return ret; - size = 512 - shift; + size = bs->buffer_alignment - shift; if (size > count) size = count; memcpy(s->aligned_buf + shift, buf, size); - ret = raw_pwrite_aligned(bs, offset - shift, s->aligned_buf, 512); + ret = raw_pwrite_aligned(bs, offset - shift, s->aligned_buf, + bs->buffer_alignment); if (ret < 0) return ret; @@ -469,12 +460,12 @@ if (count == 0) return sum; } - if (count & 0x1ff || (uintptr_t) buf & 0x1ff) { + if (count & sector_mask || (uintptr_t) buf & sector_mask) { - while ((size = (count & ~0x1ff)) != 0) { + while ((size = (count & ~sector_mask)) != 0) { - if (size > ALIGNED_BUFFER_SIZE) - size = ALIGNED_BUFFER_SIZE; + if (size > s->aligned_buf_size) + size = s->aligned_buf_size; memcpy(s->aligned_buf, buf, size); @@ -487,14 +478,16 @@ count -= ret; sum += ret; } - /* here, count < 512 because (count & ~0x1ff) == 0 */ + /* here, count < sector_size because (count & ~sector_mask) == 0 */ if (count) { - ret = raw_pread_aligned(bs, offset, s->aligned_buf, 512); + ret = raw_pread_aligned(bs, offset, s->aligned_buf, + bs->buffer_alignment); if (ret < 0) return ret; memcpy(s->aligned_buf, buf, count); - ret = raw_pwrite_aligned(bs, offset, s->aligned_buf, 512); + ret = raw_pwrite_aligned(bs, offset, s->aligned_buf, + bs->buffer_alignment); if (ret < 0) return ret; if (count < ret) @@ -512,8 +505,9 @@ const uint8_t *buf, int nb_sectors) { int ret; - ret = raw_pwrite(bs, sector_num * 512, buf, nb_sectors * 512); - if (ret == (nb_sectors * 512)) + ret = raw_pwrite(bs, sector_num * BDRV_SECTOR_SIZE, buf, + nb_sectors * BDRV_SECTOR_SIZE); + if (ret == (nb_sectors * BDRV_SECTOR_SIZE)) ret = 0; return ret; } @@ -521,12 +515,12 @@ /* * Check if all memory in this vector is sector aligned. */ -static int qiov_is_aligned(QEMUIOVector *qiov) +static int qiov_is_aligned(BlockDriverState *bs, QEMUIOVector *qiov) { int i; for (i = 0; i < qiov->niov; i++) { - if ((uintptr_t) qiov->iov[i].iov_base % 512) { + if ((uintptr_t) qiov->iov[i].iov_base % bs->buffer_alignment) { return 0; } } @@ -549,7 +543,7 @@ * driver that it needs to copy the buffer. */ if (s->aligned_buf) { - if (!qiov_is_aligned(qiov)) { + if (!qiov_is_aligned(bs, qiov)) { type |= QEMU_AIO_MISALIGNED; #ifdef CONFIG_LINUX_AIO } else if (s->use_aio) { @@ -630,29 +624,48 @@ } else return st.st_size; } -#else /* !__OpenBSD__ */ -static int64_t raw_getlength(BlockDriverState *bs) +#elif defined(__sun__) +static int64_t raw_getlength(BlockDriverState *bs) +{ + BDRVRawState *s = bs->opaque; + struct dk_minfo minfo; + int ret; + + ret = fd_open(bs); + if (ret < 0) { + return ret; + } + + /* + * Use the DKIOCGMEDIAINFO ioctl to read the size. + */ + ret = ioctl(s->fd, DKIOCGMEDIAINFO, &minfo); + if (ret != -1) { + return minfo.dki_lbsize * minfo.dki_capacity; + } + + /* + * There are reports that lseek on some devices fails, but + * irc discussion said that contingency on contingency was overkill. + */ + return lseek(s->fd, 0, SEEK_END); +} +#elif defined(CONFIG_BSD) +static int64_t raw_getlength(BlockDriverState *bs) { BDRVRawState *s = bs->opaque; int fd = s->fd; int64_t size; -#ifdef CONFIG_BSD struct stat sb; #if defined (__FreeBSD__) || defined(__FreeBSD_kernel__) int reopened = 0; #endif -#endif -#ifdef __sun__ - struct dk_minfo minfo; - int rv; -#endif int ret; ret = fd_open(bs); if (ret < 0) return ret; -#ifdef CONFIG_BSD #if defined (__FreeBSD__) || defined(__FreeBSD_kernel__) again: #endif @@ -687,24 +700,24 @@ } } #endif - } else -#endif -#ifdef __sun__ - /* - * use the DKIOCGMEDIAINFO ioctl to read the size. - */ - rv = ioctl ( fd, DKIOCGMEDIAINFO, &minfo ); - if ( rv != -1 ) { - size = minfo.dki_lbsize * minfo.dki_capacity; - } else /* there are reports that lseek on some devices - fails, but irc discussion said that contingency - on contingency was overkill */ -#endif - { + } else { size = lseek(fd, 0, SEEK_END); } return size; } +#else +static int64_t raw_getlength(BlockDriverState *bs) +{ + BDRVRawState *s = bs->opaque; + int ret; + + ret = fd_open(bs); + if (ret < 0) { + return ret; + } + + return lseek(s->fd, 0, SEEK_END); +} #endif static int raw_create(const char *filename, QEMUOptionParameter *options) @@ -716,7 +729,7 @@ /* Read out options */ while (options && options->name) { if (!strcmp(options->name, BLOCK_OPT_SIZE)) { - total_size = options->value.n / 512; + total_size = options->value.n / BDRV_SECTOR_SIZE; } options++; } @@ -726,7 +739,7 @@ if (fd < 0) { result = -errno; } else { - if (ftruncate(fd, total_size * 512) != 0) { + if (ftruncate(fd, total_size * BDRV_SECTOR_SIZE) != 0) { result = -errno; } if (close(fd) != 0) { @@ -736,12 +749,43 @@ return result; } -static void raw_flush(BlockDriverState *bs) +static int raw_flush(BlockDriverState *bs) { BDRVRawState *s = bs->opaque; - qemu_fdatasync(s->fd); + return qemu_fdatasync(s->fd); } +#ifdef CONFIG_XFS +static int xfs_discard(BDRVRawState *s, int64_t sector_num, int nb_sectors) +{ + struct xfs_flock64 fl; + + memset(&fl, 0, sizeof(fl)); + fl.l_whence = SEEK_SET; + fl.l_start = sector_num << 9; + fl.l_len = (int64_t)nb_sectors << 9; + + if (xfsctl(NULL, s->fd, XFS_IOC_UNRESVSP64, &fl) < 0) { + DEBUG_BLOCK_PRINT("cannot punch hole (%s)\n", strerror(errno)); + return -errno; + } + + return 0; +} +#endif + +static int raw_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors) +{ +#ifdef CONFIG_XFS + BDRVRawState *s = bs->opaque; + + if (s->is_xfs) { + return xfs_discard(s, sector_num, nb_sectors); + } +#endif + + return 0; +} static QEMUOptionParameter raw_create_options[] = { { @@ -752,16 +796,18 @@ { NULL } }; -static BlockDriver bdrv_raw = { - .format_name = "raw", +static BlockDriver bdrv_file = { + .format_name = "file", + .protocol_name = "file", .instance_size = sizeof(BDRVRawState), .bdrv_probe = NULL, /* no probe for protocols */ - .bdrv_open = raw_open, + .bdrv_file_open = raw_open, .bdrv_read = raw_read, .bdrv_write = raw_write, .bdrv_close = raw_close, .bdrv_create = raw_create, .bdrv_flush = raw_flush, + .bdrv_discard = raw_discard, .bdrv_aio_readv = raw_aio_readv, .bdrv_aio_writev = raw_aio_writev, @@ -883,8 +929,13 @@ s->type = FTYPE_FILE; #if defined(__linux__) - if (strstart(filename, "/dev/sg", NULL)) { - bs->sg = 1; + { + char resolved_path[ MAXPATHLEN ], *temp; + + temp = realpath(filename, resolved_path); + if (temp && strstart(temp, "/dev/sg", NULL)) { + bs->sg = 1; + } } #endif @@ -904,7 +955,7 @@ return 0; last_media_present = (s->fd >= 0); if (s->fd >= 0 && - (qemu_get_clock(rt_clock) - s->fd_open_time) >= FD_OPEN_TIMEOUT) { + (get_clock() - s->fd_open_time) >= FD_OPEN_TIMEOUT) { close(s->fd); s->fd = -1; #ifdef DEBUG_FLOPPY @@ -913,7 +964,7 @@ } if (s->fd < 0) { if (s->fd_got_error && - (qemu_get_clock(rt_clock) - s->fd_error_time) < FD_OPEN_TIMEOUT) { + (get_clock() - s->fd_error_time) < FD_OPEN_TIMEOUT) { #ifdef DEBUG_FLOPPY printf("No floppy (open delayed)\n"); #endif @@ -921,7 +972,7 @@ } s->fd = open(bs->filename, s->open_flags & ~O_NONBLOCK); if (s->fd < 0) { - s->fd_error_time = qemu_get_clock(rt_clock); + s->fd_error_time = get_clock(); s->fd_got_error = 1; if (last_media_present) s->fd_media_changed = 1; @@ -936,7 +987,7 @@ } if (!last_media_present) s->fd_media_changed = 1; - s->fd_open_time = qemu_get_clock(rt_clock); + s->fd_open_time = get_clock(); s->fd_got_error = 0; return 0; } @@ -988,35 +1039,41 @@ /* Read out options */ while (options && options->name) { if (!strcmp(options->name, "size")) { - total_size = options->value.n / 512; + total_size = options->value.n / BDRV_SECTOR_SIZE; } options++; } fd = open(filename, O_WRONLY | O_BINARY); if (fd < 0) - return -EIO; + return -errno; if (fstat(fd, &stat_buf) < 0) - ret = -EIO; + ret = -errno; else if (!S_ISBLK(stat_buf.st_mode) && !S_ISCHR(stat_buf.st_mode)) - ret = -EIO; - else if (lseek(fd, 0, SEEK_END) < total_size * 512) + ret = -ENODEV; + else if (lseek(fd, 0, SEEK_END) < total_size * BDRV_SECTOR_SIZE) ret = -ENOSPC; close(fd); return ret; } +static int hdev_has_zero_init(BlockDriverState *bs) +{ + return 0; +} + static BlockDriver bdrv_host_device = { .format_name = "host_device", + .protocol_name = "host_device", .instance_size = sizeof(BDRVRawState), .bdrv_probe_device = hdev_probe_device, - .bdrv_open = hdev_open, + .bdrv_file_open = hdev_open, .bdrv_close = raw_close, .bdrv_create = hdev_create, .create_options = raw_create_options, - .no_zero_init = 1, + .bdrv_has_zero_init = hdev_has_zero_init, .bdrv_flush = raw_flush, .bdrv_aio_readv = raw_aio_readv, @@ -1057,9 +1114,26 @@ static int floppy_probe_device(const char *filename) { + int fd, ret; + int prio = 0; + struct floppy_struct fdparam; + if (strstart(filename, "/dev/fd", NULL)) - return 100; - return 0; + prio = 50; + + fd = open(filename, O_RDONLY | O_NONBLOCK); + if (fd < 0) { + goto out; + } + + /* Attempt to detect via a floppy specific ioctl */ + ret = ioctl(fd, FDGETPRM, &fdparam); + if (ret >= 0) + prio = 100; + + close(fd); +out: + return prio; } @@ -1107,13 +1181,14 @@ static BlockDriver bdrv_host_floppy = { .format_name = "host_floppy", + .protocol_name = "host_floppy", .instance_size = sizeof(BDRVRawState), .bdrv_probe_device = floppy_probe_device, - .bdrv_open = floppy_open, + .bdrv_file_open = floppy_open, .bdrv_close = raw_close, .bdrv_create = hdev_create, .create_options = raw_create_options, - .no_zero_init = 1, + .bdrv_has_zero_init = hdev_has_zero_init, .bdrv_flush = raw_flush, .bdrv_aio_readv = raw_aio_readv, @@ -1142,9 +1217,22 @@ static int cdrom_probe_device(const char *filename) { - if (strstart(filename, "/dev/cd", NULL)) - return 100; - return 0; + int fd, ret; + int prio = 0; + + fd = open(filename, O_RDONLY | O_NONBLOCK); + if (fd < 0) { + goto out; + } + + /* Attempt to detect via a CDROM specific ioctl */ + ret = ioctl(fd, CDROM_DRIVE_STATUS, CDSL_CURRENT); + if (ret >= 0) + prio = 100; + + close(fd); +out: + return prio; } static int cdrom_is_inserted(BlockDriverState *bs) @@ -1190,13 +1278,14 @@ static BlockDriver bdrv_host_cdrom = { .format_name = "host_cdrom", + .protocol_name = "host_cdrom", .instance_size = sizeof(BDRVRawState), .bdrv_probe_device = cdrom_probe_device, - .bdrv_open = cdrom_open, + .bdrv_file_open = cdrom_open, .bdrv_close = raw_close, .bdrv_create = hdev_create, .create_options = raw_create_options, - .no_zero_init = 1, + .bdrv_has_zero_init = hdev_has_zero_init, .bdrv_flush = raw_flush, .bdrv_aio_readv = raw_aio_readv, @@ -1312,13 +1401,14 @@ static BlockDriver bdrv_host_cdrom = { .format_name = "host_cdrom", + .protocol_name = "host_cdrom", .instance_size = sizeof(BDRVRawState), .bdrv_probe_device = cdrom_probe_device, - .bdrv_open = cdrom_open, + .bdrv_file_open = cdrom_open, .bdrv_close = raw_close, .bdrv_create = hdev_create, .create_options = raw_create_options, - .no_zero_init = 1, + .bdrv_has_zero_init = hdev_has_zero_init, .bdrv_flush = raw_flush, .bdrv_aio_readv = raw_aio_readv, @@ -1336,13 +1426,13 @@ }; #endif /* __FreeBSD__ */ -static void bdrv_raw_init(void) +static void bdrv_file_init(void) { /* * Register all the drivers. Note that order is important, the driver * registered last will get probed first. */ - bdrv_register(&bdrv_raw); + bdrv_register(&bdrv_file); bdrv_register(&bdrv_host_device); #ifdef __linux__ bdrv_register(&bdrv_host_floppy); @@ -1353,4 +1443,4 @@ #endif } -block_init(bdrv_raw_init); +block_init(bdrv_file_init); diff -Nru qemu-kvm-0.12.5+noroms/block/raw-win32.c qemu-kvm-0.14.1/block/raw-win32.c --- qemu-kvm-0.12.5+noroms/block/raw-win32.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/block/raw-win32.c 2011-05-11 13:29:46.000000000 +0000 @@ -76,21 +76,17 @@ static int raw_open(BlockDriverState *bs, const char *filename, int flags) { BDRVRawState *s = bs->opaque; - int access_flags, create_flags; + int access_flags; DWORD overlapped; s->type = FTYPE_FILE; - if ((flags & BDRV_O_ACCESS) == O_RDWR) { + if (flags & BDRV_O_RDWR) { access_flags = GENERIC_READ | GENERIC_WRITE; } else { access_flags = GENERIC_READ; } - if (flags & BDRV_O_CREAT) { - create_flags = CREATE_ALWAYS; - } else { - create_flags = OPEN_EXISTING; - } + overlapped = FILE_ATTRIBUTE_NORMAL; if ((flags & BDRV_O_NOCACHE)) overlapped |= FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH; @@ -98,7 +94,7 @@ overlapped |= FILE_FLAG_WRITE_THROUGH; s->hfile = CreateFile(filename, access_flags, FILE_SHARE_READ, NULL, - create_flags, overlapped, NULL); + OPEN_EXISTING, overlapped, NULL); if (s->hfile == INVALID_HANDLE_VALUE) { int err = GetLastError(); @@ -151,10 +147,17 @@ return ret_count; } -static void raw_flush(BlockDriverState *bs) +static int raw_flush(BlockDriverState *bs) { BDRVRawState *s = bs->opaque; - FlushFileBuffers(s->hfile); + int ret; + + ret = FlushFileBuffers(s->hfile); + if (ret == 0) { + return -EIO; + } + + return 0; } static void raw_close(BlockDriverState *bs) @@ -242,10 +245,11 @@ { NULL } }; -static BlockDriver bdrv_raw = { - .format_name = "raw", +static BlockDriver bdrv_file = { + .format_name = "file", + .protocol_name = "file", .instance_size = sizeof(BDRVRawState), - .bdrv_open = raw_open, + .bdrv_file_open = raw_open, .bdrv_close = raw_close, .bdrv_create = raw_create, .bdrv_flush = raw_flush, @@ -337,7 +341,7 @@ } s->type = find_device_type(bs, filename); - if ((flags & BDRV_O_ACCESS) == O_RDWR) { + if (flags & BDRV_O_RDWR) { access_flags = GENERIC_READ | GENERIC_WRITE; } else { access_flags = GENERIC_READ; @@ -397,23 +401,30 @@ } #endif +static int hdev_has_zero_init(BlockDriverState *bs) +{ + return 0; +} + static BlockDriver bdrv_host_device = { .format_name = "host_device", + .protocol_name = "host_device", .instance_size = sizeof(BDRVRawState), .bdrv_probe_device = hdev_probe_device, - .bdrv_open = hdev_open, + .bdrv_file_open = hdev_open, .bdrv_close = raw_close, .bdrv_flush = raw_flush, + .bdrv_has_zero_init = hdev_has_zero_init, .bdrv_read = raw_read, .bdrv_write = raw_write, .bdrv_getlength = raw_getlength, }; -static void bdrv_raw_init(void) +static void bdrv_file_init(void) { - bdrv_register(&bdrv_raw); + bdrv_register(&bdrv_file); bdrv_register(&bdrv_host_device); } -block_init(bdrv_raw_init); +block_init(bdrv_file_init); diff -Nru qemu-kvm-0.12.5+noroms/block/rbd.c qemu-kvm-0.14.1/block/rbd.c --- qemu-kvm-0.12.5+noroms/block/rbd.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/block/rbd.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,1059 @@ +/* + * QEMU Block driver for RADOS (Ceph) + * + * Copyright (C) 2010 Christian Brunner + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ + +#include "qemu-common.h" +#include "qemu-error.h" + +#include "rbd_types.h" +#include "block_int.h" + +#include + + + +/* + * When specifying the image filename use: + * + * rbd:poolname/devicename + * + * poolname must be the name of an existing rados pool + * + * devicename is the basename for all objects used to + * emulate the raw device. + * + * Metadata information (image size, ...) is stored in an + * object with the name "devicename.rbd". + * + * The raw device is split into 4MB sized objects by default. + * The sequencenumber is encoded in a 12 byte long hex-string, + * and is attached to the devicename, separated by a dot. + * e.g. "devicename.1234567890ab" + * + */ + +#define OBJ_MAX_SIZE (1UL << OBJ_DEFAULT_OBJ_ORDER) + +typedef struct RBDAIOCB { + BlockDriverAIOCB common; + QEMUBH *bh; + int ret; + QEMUIOVector *qiov; + char *bounce; + int write; + int64_t sector_num; + int aiocnt; + int error; + struct BDRVRBDState *s; + int cancelled; +} RBDAIOCB; + +typedef struct RADOSCB { + int rcbid; + RBDAIOCB *acb; + struct BDRVRBDState *s; + int done; + int64_t segsize; + char *buf; + int ret; +} RADOSCB; + +#define RBD_FD_READ 0 +#define RBD_FD_WRITE 1 + +typedef struct BDRVRBDState { + int fds[2]; + rados_pool_t pool; + rados_pool_t header_pool; + char name[RBD_MAX_OBJ_NAME_SIZE]; + char block_name[RBD_MAX_BLOCK_NAME_SIZE]; + uint64_t size; + uint64_t objsize; + int qemu_aio_count; + int event_reader_pos; + RADOSCB *event_rcb; +} BDRVRBDState; + +typedef struct rbd_obj_header_ondisk RbdHeader1; + +static void rbd_aio_bh_cb(void *opaque); + +static int rbd_next_tok(char *dst, int dst_len, + char *src, char delim, + const char *name, + char **p) +{ + int l; + char *end; + + *p = NULL; + + if (delim != '\0') { + end = strchr(src, delim); + if (end) { + *p = end + 1; + *end = '\0'; + } + } + l = strlen(src); + if (l >= dst_len) { + error_report("%s too long", name); + return -EINVAL; + } else if (l == 0) { + error_report("%s too short", name); + return -EINVAL; + } + + pstrcpy(dst, dst_len, src); + + return 0; +} + +static int rbd_parsename(const char *filename, + char *pool, int pool_len, + char *snap, int snap_len, + char *name, int name_len) +{ + const char *start; + char *p, *buf; + int ret; + + if (!strstart(filename, "rbd:", &start)) { + return -EINVAL; + } + + buf = qemu_strdup(start); + p = buf; + + ret = rbd_next_tok(pool, pool_len, p, '/', "pool name", &p); + if (ret < 0 || !p) { + ret = -EINVAL; + goto done; + } + ret = rbd_next_tok(name, name_len, p, '@', "object name", &p); + if (ret < 0) { + goto done; + } + if (!p) { + *snap = '\0'; + goto done; + } + + ret = rbd_next_tok(snap, snap_len, p, '\0', "snap name", &p); + +done: + qemu_free(buf); + return ret; +} + +static int create_tmap_op(uint8_t op, const char *name, char **tmap_desc) +{ + uint32_t len = strlen(name); + uint32_t len_le = cpu_to_le32(len); + /* total_len = encoding op + name + empty buffer */ + uint32_t total_len = 1 + (sizeof(uint32_t) + len) + sizeof(uint32_t); + uint8_t *desc = NULL; + + desc = qemu_malloc(total_len); + + *tmap_desc = (char *)desc; + + *desc = op; + desc++; + memcpy(desc, &len_le, sizeof(len_le)); + desc += sizeof(len_le); + memcpy(desc, name, len); + desc += len; + len = 0; /* no need for endian conversion for 0 */ + memcpy(desc, &len, sizeof(len)); + desc += sizeof(len); + + return (char *)desc - *tmap_desc; +} + +static void free_tmap_op(char *tmap_desc) +{ + qemu_free(tmap_desc); +} + +static int rbd_register_image(rados_pool_t pool, const char *name) +{ + char *tmap_desc; + const char *dir = RBD_DIRECTORY; + int ret; + + ret = create_tmap_op(CEPH_OSD_TMAP_SET, name, &tmap_desc); + if (ret < 0) { + return ret; + } + + ret = rados_tmap_update(pool, dir, tmap_desc, ret); + free_tmap_op(tmap_desc); + + return ret; +} + +static int touch_rbd_info(rados_pool_t pool, const char *info_oid) +{ + int r = rados_write(pool, info_oid, 0, NULL, 0); + if (r < 0) { + return r; + } + return 0; +} + +static int rbd_assign_bid(rados_pool_t pool, uint64_t *id) +{ + uint64_t out[1]; + const char *info_oid = RBD_INFO; + + *id = 0; + + int r = touch_rbd_info(pool, info_oid); + if (r < 0) { + return r; + } + + r = rados_exec(pool, info_oid, "rbd", "assign_bid", NULL, + 0, (char *)out, sizeof(out)); + if (r < 0) { + return r; + } + + le64_to_cpus(out); + *id = out[0]; + + return 0; +} + +static int rbd_create(const char *filename, QEMUOptionParameter *options) +{ + int64_t bytes = 0; + int64_t objsize; + uint64_t size; + time_t mtime; + uint8_t obj_order = RBD_DEFAULT_OBJ_ORDER; + char pool[RBD_MAX_SEG_NAME_SIZE]; + char n[RBD_MAX_SEG_NAME_SIZE]; + char name[RBD_MAX_OBJ_NAME_SIZE]; + char snap_buf[RBD_MAX_SEG_NAME_SIZE]; + char *snap = NULL; + RbdHeader1 header; + rados_pool_t p; + uint64_t bid; + uint32_t hi, lo; + int ret; + + if (rbd_parsename(filename, + pool, sizeof(pool), + snap_buf, sizeof(snap_buf), + name, sizeof(name)) < 0) { + return -EINVAL; + } + if (snap_buf[0] != '\0') { + snap = snap_buf; + } + + snprintf(n, sizeof(n), "%s%s", name, RBD_SUFFIX); + + /* Read out options */ + while (options && options->name) { + if (!strcmp(options->name, BLOCK_OPT_SIZE)) { + bytes = options->value.n; + } else if (!strcmp(options->name, BLOCK_OPT_CLUSTER_SIZE)) { + if (options->value.n) { + objsize = options->value.n; + if ((objsize - 1) & objsize) { /* not a power of 2? */ + error_report("obj size needs to be power of 2"); + return -EINVAL; + } + if (objsize < 4096) { + error_report("obj size too small"); + return -EINVAL; + } + obj_order = ffs(objsize) - 1; + } + } + options++; + } + + memset(&header, 0, sizeof(header)); + pstrcpy(header.text, sizeof(header.text), RBD_HEADER_TEXT); + pstrcpy(header.signature, sizeof(header.signature), RBD_HEADER_SIGNATURE); + pstrcpy(header.version, sizeof(header.version), RBD_HEADER_VERSION); + header.image_size = cpu_to_le64(bytes); + header.options.order = obj_order; + header.options.crypt_type = RBD_CRYPT_NONE; + header.options.comp_type = RBD_COMP_NONE; + header.snap_seq = 0; + header.snap_count = 0; + + if (rados_initialize(0, NULL) < 0) { + error_report("error initializing"); + return -EIO; + } + + if (rados_open_pool(pool, &p)) { + error_report("error opening pool %s", pool); + rados_deinitialize(); + return -EIO; + } + + /* check for existing rbd header file */ + ret = rados_stat(p, n, &size, &mtime); + if (ret == 0) { + ret=-EEXIST; + goto done; + } + + ret = rbd_assign_bid(p, &bid); + if (ret < 0) { + error_report("failed assigning block id"); + rados_deinitialize(); + return -EIO; + } + hi = bid >> 32; + lo = bid & 0xFFFFFFFF; + snprintf(header.block_name, sizeof(header.block_name), "rb.%x.%x", hi, lo); + + /* create header file */ + ret = rados_write(p, n, 0, (const char *)&header, sizeof(header)); + if (ret < 0) { + goto done; + } + + ret = rbd_register_image(p, name); +done: + rados_close_pool(p); + rados_deinitialize(); + + return ret; +} + +/* + * This aio completion is being called from rbd_aio_event_reader() and + * runs in qemu context. It schedules a bh, but just in case the aio + * was not cancelled before. + */ +static void rbd_complete_aio(RADOSCB *rcb) +{ + RBDAIOCB *acb = rcb->acb; + int64_t r; + + acb->aiocnt--; + + if (acb->cancelled) { + if (!acb->aiocnt) { + qemu_vfree(acb->bounce); + qemu_aio_release(acb); + } + goto done; + } + + r = rcb->ret; + + if (acb->write) { + if (r < 0) { + acb->ret = r; + acb->error = 1; + } else if (!acb->error) { + acb->ret += rcb->segsize; + } + } else { + if (r == -ENOENT) { + memset(rcb->buf, 0, rcb->segsize); + if (!acb->error) { + acb->ret += rcb->segsize; + } + } else if (r < 0) { + memset(rcb->buf, 0, rcb->segsize); + acb->ret = r; + acb->error = 1; + } else if (r < rcb->segsize) { + memset(rcb->buf + r, 0, rcb->segsize - r); + if (!acb->error) { + acb->ret += rcb->segsize; + } + } else if (!acb->error) { + acb->ret += r; + } + } + /* Note that acb->bh can be NULL in case where the aio was cancelled */ + if (!acb->aiocnt) { + acb->bh = qemu_bh_new(rbd_aio_bh_cb, acb); + qemu_bh_schedule(acb->bh); + } +done: + qemu_free(rcb); +} + +/* + * aio fd read handler. It runs in the qemu context and calls the + * completion handling of completed rados aio operations. + */ +static void rbd_aio_event_reader(void *opaque) +{ + BDRVRBDState *s = opaque; + + ssize_t ret; + + do { + char *p = (char *)&s->event_rcb; + + /* now read the rcb pointer that was sent from a non qemu thread */ + if ((ret = read(s->fds[RBD_FD_READ], p + s->event_reader_pos, + sizeof(s->event_rcb) - s->event_reader_pos)) > 0) { + if (ret > 0) { + s->event_reader_pos += ret; + if (s->event_reader_pos == sizeof(s->event_rcb)) { + s->event_reader_pos = 0; + rbd_complete_aio(s->event_rcb); + s->qemu_aio_count --; + } + } + } + } while (ret < 0 && errno == EINTR); +} + +static int rbd_aio_flush_cb(void *opaque) +{ + BDRVRBDState *s = opaque; + + return (s->qemu_aio_count > 0); +} + + +static int rbd_set_snapc(rados_pool_t pool, const char *snap, RbdHeader1 *header) +{ + uint32_t snap_count = le32_to_cpu(header->snap_count); + rados_snap_t *snaps = NULL; + rados_snap_t seq; + uint32_t i; + uint64_t snap_names_len = le64_to_cpu(header->snap_names_len); + int r; + rados_snap_t snapid = 0; + + if (snap_count) { + const char *header_snap = (const char *)&header->snaps[snap_count]; + const char *end = header_snap + snap_names_len; + snaps = qemu_malloc(sizeof(rados_snap_t) * header->snap_count); + + for (i=0; i < snap_count; i++) { + snaps[i] = le64_to_cpu(header->snaps[i].id); + + if (snap && strcmp(snap, header_snap) == 0) { + snapid = snaps[i]; + } + + header_snap += strlen(header_snap) + 1; + if (header_snap > end) { + error_report("bad header, snapshot list broken"); + } + } + } + + if (snap && !snapid) { + error_report("snapshot not found"); + qemu_free(snaps); + return -ENOENT; + } + seq = le32_to_cpu(header->snap_seq); + + r = rados_set_snap_context(pool, seq, snaps, snap_count); + + rados_set_snap(pool, snapid); + + qemu_free(snaps); + + return r; +} + +#define BUF_READ_START_LEN 4096 + +static int rbd_read_header(BDRVRBDState *s, char **hbuf) +{ + char *buf = NULL; + char n[RBD_MAX_SEG_NAME_SIZE]; + uint64_t len = BUF_READ_START_LEN; + int r; + + snprintf(n, sizeof(n), "%s%s", s->name, RBD_SUFFIX); + + buf = qemu_malloc(len); + + r = rados_read(s->header_pool, n, 0, buf, len); + if (r < 0) { + goto failed; + } + + if (r < len) { + goto done; + } + + qemu_free(buf); + buf = qemu_malloc(len); + + r = rados_stat(s->header_pool, n, &len, NULL); + if (r < 0) { + goto failed; + } + + r = rados_read(s->header_pool, n, 0, buf, len); + if (r < 0) { + goto failed; + } + +done: + *hbuf = buf; + return 0; + +failed: + qemu_free(buf); + return r; +} + +static int rbd_open(BlockDriverState *bs, const char *filename, int flags) +{ + BDRVRBDState *s = bs->opaque; + RbdHeader1 *header; + char pool[RBD_MAX_SEG_NAME_SIZE]; + char snap_buf[RBD_MAX_SEG_NAME_SIZE]; + char *snap = NULL; + char *hbuf = NULL; + int r; + + if (rbd_parsename(filename, pool, sizeof(pool), + snap_buf, sizeof(snap_buf), + s->name, sizeof(s->name)) < 0) { + return -EINVAL; + } + if (snap_buf[0] != '\0') { + snap = snap_buf; + } + + if ((r = rados_initialize(0, NULL)) < 0) { + error_report("error initializing"); + return r; + } + + if ((r = rados_open_pool(pool, &s->pool))) { + error_report("error opening pool %s", pool); + rados_deinitialize(); + return r; + } + + if ((r = rados_open_pool(pool, &s->header_pool))) { + error_report("error opening pool %s", pool); + rados_deinitialize(); + return r; + } + + if ((r = rbd_read_header(s, &hbuf)) < 0) { + error_report("error reading header from %s", s->name); + goto failed; + } + + if (memcmp(hbuf + 64, RBD_HEADER_SIGNATURE, 4)) { + error_report("Invalid header signature"); + r = -EMEDIUMTYPE; + goto failed; + } + + if (memcmp(hbuf + 68, RBD_HEADER_VERSION, 8)) { + error_report("Unknown image version"); + r = -EMEDIUMTYPE; + goto failed; + } + + header = (RbdHeader1 *) hbuf; + s->size = le64_to_cpu(header->image_size); + s->objsize = 1ULL << header->options.order; + memcpy(s->block_name, header->block_name, sizeof(header->block_name)); + + r = rbd_set_snapc(s->pool, snap, header); + if (r < 0) { + error_report("failed setting snap context: %s", strerror(-r)); + goto failed; + } + + bs->read_only = (snap != NULL); + + s->event_reader_pos = 0; + r = qemu_pipe(s->fds); + if (r < 0) { + error_report("error opening eventfd"); + goto failed; + } + fcntl(s->fds[0], F_SETFL, O_NONBLOCK); + fcntl(s->fds[1], F_SETFL, O_NONBLOCK); + qemu_aio_set_fd_handler(s->fds[RBD_FD_READ], rbd_aio_event_reader, NULL, + rbd_aio_flush_cb, NULL, s); + + qemu_free(hbuf); + + return 0; + +failed: + qemu_free(hbuf); + + rados_close_pool(s->header_pool); + rados_close_pool(s->pool); + rados_deinitialize(); + return r; +} + +static void rbd_close(BlockDriverState *bs) +{ + BDRVRBDState *s = bs->opaque; + + close(s->fds[0]); + close(s->fds[1]); + qemu_aio_set_fd_handler(s->fds[RBD_FD_READ], NULL , NULL, NULL, NULL, + NULL); + + rados_close_pool(s->header_pool); + rados_close_pool(s->pool); + rados_deinitialize(); +} + +/* + * Cancel aio. Since we don't reference acb in a non qemu threads, + * it is safe to access it here. + */ +static void rbd_aio_cancel(BlockDriverAIOCB *blockacb) +{ + RBDAIOCB *acb = (RBDAIOCB *) blockacb; + acb->cancelled = 1; +} + +static AIOPool rbd_aio_pool = { + .aiocb_size = sizeof(RBDAIOCB), + .cancel = rbd_aio_cancel, +}; + +/* + * This is the callback function for rados_aio_read and _write + * + * Note: this function is being called from a non qemu thread so + * we need to be careful about what we do here. Generally we only + * write to the block notification pipe, and do the rest of the + * io completion handling from rbd_aio_event_reader() which + * runs in a qemu context. + */ +static void rbd_finish_aiocb(rados_completion_t c, RADOSCB *rcb) +{ + int ret; + rcb->ret = rados_aio_get_return_value(c); + rados_aio_release(c); + while (1) { + fd_set wfd; + int fd = rcb->s->fds[RBD_FD_WRITE]; + + /* send the rcb pointer to the qemu thread that is responsible + for the aio completion. Must do it in a qemu thread context */ + ret = write(fd, (void *)&rcb, sizeof(rcb)); + if (ret >= 0) { + break; + } + if (errno == EINTR) { + continue; + } + if (errno != EAGAIN) { + break; + } + + FD_ZERO(&wfd); + FD_SET(fd, &wfd); + do { + ret = select(fd + 1, NULL, &wfd, NULL, NULL); + } while (ret < 0 && errno == EINTR); + } + + if (ret < 0) { + error_report("failed writing to acb->s->fds\n"); + qemu_free(rcb); + } +} + +/* Callback when all queued rados_aio requests are complete */ + +static void rbd_aio_bh_cb(void *opaque) +{ + RBDAIOCB *acb = opaque; + + if (!acb->write) { + qemu_iovec_from_buffer(acb->qiov, acb->bounce, acb->qiov->size); + } + qemu_vfree(acb->bounce); + acb->common.cb(acb->common.opaque, (acb->ret > 0 ? 0 : acb->ret)); + qemu_bh_delete(acb->bh); + acb->bh = NULL; + + qemu_aio_release(acb); +} + +static BlockDriverAIOCB *rbd_aio_rw_vector(BlockDriverState *bs, + int64_t sector_num, + QEMUIOVector *qiov, + int nb_sectors, + BlockDriverCompletionFunc *cb, + void *opaque, int write) +{ + RBDAIOCB *acb; + RADOSCB *rcb; + rados_completion_t c; + char n[RBD_MAX_SEG_NAME_SIZE]; + int64_t segnr, segoffs, segsize, last_segnr; + int64_t off, size; + char *buf; + + BDRVRBDState *s = bs->opaque; + + acb = qemu_aio_get(&rbd_aio_pool, bs, cb, opaque); + acb->write = write; + acb->qiov = qiov; + acb->bounce = qemu_blockalign(bs, qiov->size); + acb->aiocnt = 0; + acb->ret = 0; + acb->error = 0; + acb->s = s; + acb->cancelled = 0; + acb->bh = NULL; + + if (write) { + qemu_iovec_to_buffer(acb->qiov, acb->bounce); + } + + buf = acb->bounce; + + off = sector_num * BDRV_SECTOR_SIZE; + size = nb_sectors * BDRV_SECTOR_SIZE; + segnr = off / s->objsize; + segoffs = off % s->objsize; + segsize = s->objsize - segoffs; + + last_segnr = ((off + size - 1) / s->objsize); + acb->aiocnt = (last_segnr - segnr) + 1; + + s->qemu_aio_count += acb->aiocnt; /* All the RADOSCB */ + + while (size > 0) { + if (size < segsize) { + segsize = size; + } + + snprintf(n, sizeof(n), "%s.%012" PRIx64, s->block_name, + segnr); + + rcb = qemu_malloc(sizeof(RADOSCB)); + rcb->done = 0; + rcb->acb = acb; + rcb->segsize = segsize; + rcb->buf = buf; + rcb->s = acb->s; + + if (write) { + rados_aio_create_completion(rcb, NULL, + (rados_callback_t) rbd_finish_aiocb, + &c); + rados_aio_write(s->pool, n, segoffs, buf, segsize, c); + } else { + rados_aio_create_completion(rcb, + (rados_callback_t) rbd_finish_aiocb, + NULL, &c); + rados_aio_read(s->pool, n, segoffs, buf, segsize, c); + } + + buf += segsize; + size -= segsize; + segoffs = 0; + segsize = s->objsize; + segnr++; + } + + return &acb->common; +} + +static BlockDriverAIOCB *rbd_aio_readv(BlockDriverState * bs, + int64_t sector_num, QEMUIOVector * qiov, + int nb_sectors, + BlockDriverCompletionFunc * cb, + void *opaque) +{ + return rbd_aio_rw_vector(bs, sector_num, qiov, nb_sectors, cb, opaque, 0); +} + +static BlockDriverAIOCB *rbd_aio_writev(BlockDriverState * bs, + int64_t sector_num, QEMUIOVector * qiov, + int nb_sectors, + BlockDriverCompletionFunc * cb, + void *opaque) +{ + return rbd_aio_rw_vector(bs, sector_num, qiov, nb_sectors, cb, opaque, 1); +} + +static int rbd_getinfo(BlockDriverState * bs, BlockDriverInfo * bdi) +{ + BDRVRBDState *s = bs->opaque; + bdi->cluster_size = s->objsize; + return 0; +} + +static int64_t rbd_getlength(BlockDriverState * bs) +{ + BDRVRBDState *s = bs->opaque; + + return s->size; +} + +static int rbd_snap_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) +{ + BDRVRBDState *s = bs->opaque; + char inbuf[512], outbuf[128]; + uint64_t snap_id; + int r; + char *p = inbuf; + char *end = inbuf + sizeof(inbuf); + char n[RBD_MAX_SEG_NAME_SIZE]; + char *hbuf = NULL; + RbdHeader1 *header; + + if (sn_info->name[0] == '\0') { + return -EINVAL; /* we need a name for rbd snapshots */ + } + + /* + * rbd snapshots are using the name as the user controlled unique identifier + * we can't use the rbd snapid for that purpose, as it can't be set + */ + if (sn_info->id_str[0] != '\0' && + strcmp(sn_info->id_str, sn_info->name) != 0) { + return -EINVAL; + } + + if (strlen(sn_info->name) >= sizeof(sn_info->id_str)) { + return -ERANGE; + } + + r = rados_selfmanaged_snap_create(s->header_pool, &snap_id); + if (r < 0) { + error_report("failed to create snap id: %s", strerror(-r)); + return r; + } + + *(uint32_t *)p = strlen(sn_info->name); + cpu_to_le32s((uint32_t *)p); + p += sizeof(uint32_t); + strncpy(p, sn_info->name, end - p); + p += strlen(p); + if (p + sizeof(snap_id) > end) { + error_report("invalid input parameter"); + return -EINVAL; + } + + *(uint64_t *)p = snap_id; + cpu_to_le64s((uint64_t *)p); + + snprintf(n, sizeof(n), "%s%s", s->name, RBD_SUFFIX); + + r = rados_exec(s->header_pool, n, "rbd", "snap_add", inbuf, + sizeof(inbuf), outbuf, sizeof(outbuf)); + if (r < 0) { + error_report("rbd.snap_add execution failed failed: %s", strerror(-r)); + return r; + } + + sprintf(sn_info->id_str, "%s", sn_info->name); + + r = rbd_read_header(s, &hbuf); + if (r < 0) { + error_report("failed reading header: %s", strerror(-r)); + return r; + } + + header = (RbdHeader1 *) hbuf; + r = rbd_set_snapc(s->pool, sn_info->name, header); + if (r < 0) { + error_report("failed setting snap context: %s", strerror(-r)); + goto failed; + } + + return 0; + +failed: + qemu_free(header); + return r; +} + +static int decode32(char **p, const char *end, uint32_t *v) +{ + if (*p + 4 > end) { + return -ERANGE; + } + + *v = *(uint32_t *)(*p); + le32_to_cpus(v); + *p += 4; + return 0; +} + +static int decode64(char **p, const char *end, uint64_t *v) +{ + if (*p + 8 > end) { + return -ERANGE; + } + + *v = *(uint64_t *)(*p); + le64_to_cpus(v); + *p += 8; + return 0; +} + +static int decode_str(char **p, const char *end, char **s) +{ + uint32_t len; + int r; + + if ((r = decode32(p, end, &len)) < 0) { + return r; + } + + *s = qemu_malloc(len + 1); + memcpy(*s, *p, len); + *p += len; + (*s)[len] = '\0'; + + return len; +} + +static int rbd_snap_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab) +{ + BDRVRBDState *s = bs->opaque; + char n[RBD_MAX_SEG_NAME_SIZE]; + QEMUSnapshotInfo *sn_info, *sn_tab = NULL; + RbdHeader1 *header; + char *hbuf = NULL; + char *outbuf = NULL, *end, *buf; + uint64_t len; + uint64_t snap_seq; + uint32_t snap_count; + int r, i; + + /* read header to estimate how much space we need to read the snap + * list */ + if ((r = rbd_read_header(s, &hbuf)) < 0) { + goto done_err; + } + header = (RbdHeader1 *)hbuf; + len = le64_to_cpu(header->snap_names_len); + len += 1024; /* should have already been enough, but new snapshots might + already been created since we read the header. just allocate + a bit more, so that in most cases it'll suffice anyway */ + qemu_free(hbuf); + + snprintf(n, sizeof(n), "%s%s", s->name, RBD_SUFFIX); + while (1) { + qemu_free(outbuf); + outbuf = qemu_malloc(len); + + r = rados_exec(s->header_pool, n, "rbd", "snap_list", NULL, 0, + outbuf, len); + if (r < 0) { + error_report("rbd.snap_list execution failed failed: %s", strerror(-r)); + goto done_err; + } + if (r != len) { + break; + } + + /* if we're here, we probably raced with some snaps creation */ + len *= 2; + } + buf = outbuf; + end = buf + len; + + if ((r = decode64(&buf, end, &snap_seq)) < 0) { + goto done_err; + } + if ((r = decode32(&buf, end, &snap_count)) < 0) { + goto done_err; + } + + sn_tab = qemu_mallocz(snap_count * sizeof(QEMUSnapshotInfo)); + for (i = 0; i < snap_count; i++) { + uint64_t id, image_size; + char *snap_name; + + if ((r = decode64(&buf, end, &id)) < 0) { + goto done_err; + } + if ((r = decode64(&buf, end, &image_size)) < 0) { + goto done_err; + } + if ((r = decode_str(&buf, end, &snap_name)) < 0) { + goto done_err; + } + + sn_info = sn_tab + i; + pstrcpy(sn_info->id_str, sizeof(sn_info->id_str), snap_name); + pstrcpy(sn_info->name, sizeof(sn_info->name), snap_name); + qemu_free(snap_name); + + sn_info->vm_state_size = image_size; + sn_info->date_sec = 0; + sn_info->date_nsec = 0; + sn_info->vm_clock_nsec = 0; + } + *psn_tab = sn_tab; + qemu_free(outbuf); + return snap_count; +done_err: + qemu_free(sn_tab); + qemu_free(outbuf); + return r; +} + +static QEMUOptionParameter rbd_create_options[] = { + { + .name = BLOCK_OPT_SIZE, + .type = OPT_SIZE, + .help = "Virtual disk size" + }, + { + .name = BLOCK_OPT_CLUSTER_SIZE, + .type = OPT_SIZE, + .help = "RBD object size" + }, + {NULL} +}; + +static BlockDriver bdrv_rbd = { + .format_name = "rbd", + .instance_size = sizeof(BDRVRBDState), + .bdrv_file_open = rbd_open, + .bdrv_close = rbd_close, + .bdrv_create = rbd_create, + .bdrv_get_info = rbd_getinfo, + .create_options = rbd_create_options, + .bdrv_getlength = rbd_getlength, + .protocol_name = "rbd", + + .bdrv_aio_readv = rbd_aio_readv, + .bdrv_aio_writev = rbd_aio_writev, + + .bdrv_snapshot_create = rbd_snap_create, + .bdrv_snapshot_list = rbd_snap_list, +}; + +static void bdrv_rbd_init(void) +{ + bdrv_register(&bdrv_rbd); +} + +block_init(bdrv_rbd_init); diff -Nru qemu-kvm-0.12.5+noroms/block/rbd_types.h qemu-kvm-0.14.1/block/rbd_types.h --- qemu-kvm-0.12.5+noroms/block/rbd_types.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/block/rbd_types.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,71 @@ +/* + * Ceph - scalable distributed file system + * + * Copyright (C) 2004-2010 Sage Weil + * + * This is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software + * Foundation. See file COPYING.LIB. + * + */ + +#ifndef CEPH_RBD_TYPES_H +#define CEPH_RBD_TYPES_H + + +/* + * rbd image 'foo' consists of objects + * foo.rbd - image metadata + * foo.00000000 + * foo.00000001 + * ... - data + */ + +#define RBD_SUFFIX ".rbd" +#define RBD_DIRECTORY "rbd_directory" +#define RBD_INFO "rbd_info" + +#define RBD_DEFAULT_OBJ_ORDER 22 /* 4MB */ + +#define RBD_MAX_OBJ_NAME_SIZE 96 +#define RBD_MAX_BLOCK_NAME_SIZE 24 +#define RBD_MAX_SEG_NAME_SIZE 128 + +#define RBD_COMP_NONE 0 +#define RBD_CRYPT_NONE 0 + +#define RBD_HEADER_TEXT "<<< Rados Block Device Image >>>\n" +#define RBD_HEADER_SIGNATURE "RBD" +#define RBD_HEADER_VERSION "001.005" + +struct rbd_info { + uint64_t max_id; +} __attribute__ ((packed)); + +struct rbd_obj_snap_ondisk { + uint64_t id; + uint64_t image_size; +} __attribute__((packed)); + +struct rbd_obj_header_ondisk { + char text[40]; + char block_name[RBD_MAX_BLOCK_NAME_SIZE]; + char signature[4]; + char version[8]; + struct { + uint8_t order; + uint8_t crypt_type; + uint8_t comp_type; + uint8_t unused; + } __attribute__((packed)) options; + uint64_t image_size; + uint64_t snap_seq; + uint32_t snap_count; + uint32_t reserved; + uint64_t snap_names_len; + struct rbd_obj_snap_ondisk snaps[0]; +} __attribute__((packed)); + + +#endif diff -Nru qemu-kvm-0.12.5+noroms/block/sheepdog.c qemu-kvm-0.14.1/block/sheepdog.c --- qemu-kvm-0.12.5+noroms/block/sheepdog.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/block/sheepdog.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,2037 @@ +/* + * Copyright (C) 2009-2010 Nippon Telegraph and Telephone Corporation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "qemu-common.h" +#include "qemu-error.h" +#include "qemu_socket.h" +#include "block_int.h" + +#define SD_PROTO_VER 0x01 + +#define SD_DEFAULT_ADDR "localhost" +#define SD_DEFAULT_PORT "7000" + +#define SD_OP_CREATE_AND_WRITE_OBJ 0x01 +#define SD_OP_READ_OBJ 0x02 +#define SD_OP_WRITE_OBJ 0x03 + +#define SD_OP_NEW_VDI 0x11 +#define SD_OP_LOCK_VDI 0x12 +#define SD_OP_RELEASE_VDI 0x13 +#define SD_OP_GET_VDI_INFO 0x14 +#define SD_OP_READ_VDIS 0x15 + +#define SD_FLAG_CMD_WRITE 0x01 +#define SD_FLAG_CMD_COW 0x02 + +#define SD_RES_SUCCESS 0x00 /* Success */ +#define SD_RES_UNKNOWN 0x01 /* Unknown error */ +#define SD_RES_NO_OBJ 0x02 /* No object found */ +#define SD_RES_EIO 0x03 /* I/O error */ +#define SD_RES_VDI_EXIST 0x04 /* Vdi exists already */ +#define SD_RES_INVALID_PARMS 0x05 /* Invalid parameters */ +#define SD_RES_SYSTEM_ERROR 0x06 /* System error */ +#define SD_RES_VDI_LOCKED 0x07 /* Vdi is locked */ +#define SD_RES_NO_VDI 0x08 /* No vdi found */ +#define SD_RES_NO_BASE_VDI 0x09 /* No base vdi found */ +#define SD_RES_VDI_READ 0x0A /* Cannot read requested vdi */ +#define SD_RES_VDI_WRITE 0x0B /* Cannot write requested vdi */ +#define SD_RES_BASE_VDI_READ 0x0C /* Cannot read base vdi */ +#define SD_RES_BASE_VDI_WRITE 0x0D /* Cannot write base vdi */ +#define SD_RES_NO_TAG 0x0E /* Requested tag is not found */ +#define SD_RES_STARTUP 0x0F /* Sheepdog is on starting up */ +#define SD_RES_VDI_NOT_LOCKED 0x10 /* Vdi is not locked */ +#define SD_RES_SHUTDOWN 0x11 /* Sheepdog is shutting down */ +#define SD_RES_NO_MEM 0x12 /* Cannot allocate memory */ +#define SD_RES_FULL_VDI 0x13 /* we already have the maximum vdis */ +#define SD_RES_VER_MISMATCH 0x14 /* Protocol version mismatch */ +#define SD_RES_NO_SPACE 0x15 /* Server has no room for new objects */ +#define SD_RES_WAIT_FOR_FORMAT 0x16 /* Waiting for a format operation */ +#define SD_RES_WAIT_FOR_JOIN 0x17 /* Waiting for other nodes joining */ +#define SD_RES_JOIN_FAILED 0x18 /* Target node had failed to join sheepdog */ + +/* + * Object ID rules + * + * 0 - 19 (20 bits): data object space + * 20 - 31 (12 bits): reserved data object space + * 32 - 55 (24 bits): vdi object space + * 56 - 59 ( 4 bits): reserved vdi object space + * 60 - 63 ( 4 bits): object type indentifier space + */ + +#define VDI_SPACE_SHIFT 32 +#define VDI_BIT (UINT64_C(1) << 63) +#define VMSTATE_BIT (UINT64_C(1) << 62) +#define MAX_DATA_OBJS (UINT64_C(1) << 20) +#define MAX_CHILDREN 1024 +#define SD_MAX_VDI_LEN 256 +#define SD_MAX_VDI_TAG_LEN 256 +#define SD_NR_VDIS (1U << 24) +#define SD_DATA_OBJ_SIZE (UINT64_C(1) << 22) +#define SD_MAX_VDI_SIZE (SD_DATA_OBJ_SIZE * MAX_DATA_OBJS) +#define SECTOR_SIZE 512 + +#define SD_INODE_SIZE (sizeof(SheepdogInode)) +#define CURRENT_VDI_ID 0 + +typedef struct SheepdogReq { + uint8_t proto_ver; + uint8_t opcode; + uint16_t flags; + uint32_t epoch; + uint32_t id; + uint32_t data_length; + uint32_t opcode_specific[8]; +} SheepdogReq; + +typedef struct SheepdogRsp { + uint8_t proto_ver; + uint8_t opcode; + uint16_t flags; + uint32_t epoch; + uint32_t id; + uint32_t data_length; + uint32_t result; + uint32_t opcode_specific[7]; +} SheepdogRsp; + +typedef struct SheepdogObjReq { + uint8_t proto_ver; + uint8_t opcode; + uint16_t flags; + uint32_t epoch; + uint32_t id; + uint32_t data_length; + uint64_t oid; + uint64_t cow_oid; + uint32_t copies; + uint32_t rsvd; + uint64_t offset; +} SheepdogObjReq; + +typedef struct SheepdogObjRsp { + uint8_t proto_ver; + uint8_t opcode; + uint16_t flags; + uint32_t epoch; + uint32_t id; + uint32_t data_length; + uint32_t result; + uint32_t copies; + uint32_t pad[6]; +} SheepdogObjRsp; + +typedef struct SheepdogVdiReq { + uint8_t proto_ver; + uint8_t opcode; + uint16_t flags; + uint32_t epoch; + uint32_t id; + uint32_t data_length; + uint64_t vdi_size; + uint32_t base_vdi_id; + uint32_t copies; + uint32_t snapid; + uint32_t pad[3]; +} SheepdogVdiReq; + +typedef struct SheepdogVdiRsp { + uint8_t proto_ver; + uint8_t opcode; + uint16_t flags; + uint32_t epoch; + uint32_t id; + uint32_t data_length; + uint32_t result; + uint32_t rsvd; + uint32_t vdi_id; + uint32_t pad[5]; +} SheepdogVdiRsp; + +typedef struct SheepdogInode { + char name[SD_MAX_VDI_LEN]; + char tag[SD_MAX_VDI_TAG_LEN]; + uint64_t ctime; + uint64_t snap_ctime; + uint64_t vm_clock_nsec; + uint64_t vdi_size; + uint64_t vm_state_size; + uint16_t copy_policy; + uint8_t nr_copies; + uint8_t block_size_shift; + uint32_t snap_id; + uint32_t vdi_id; + uint32_t parent_vdi_id; + uint32_t child_vdi_id[MAX_CHILDREN]; + uint32_t data_vdi_id[MAX_DATA_OBJS]; +} SheepdogInode; + +/* + * 64 bit FNV-1a non-zero initial basis + */ +#define FNV1A_64_INIT ((uint64_t)0xcbf29ce484222325ULL) + +/* + * 64 bit Fowler/Noll/Vo FNV-1a hash code + */ +static inline uint64_t fnv_64a_buf(void *buf, size_t len, uint64_t hval) +{ + unsigned char *bp = buf; + unsigned char *be = bp + len; + while (bp < be) { + hval ^= (uint64_t) *bp++; + hval += (hval << 1) + (hval << 4) + (hval << 5) + + (hval << 7) + (hval << 8) + (hval << 40); + } + return hval; +} + +static inline int is_data_obj_writeable(SheepdogInode *inode, unsigned int idx) +{ + return inode->vdi_id == inode->data_vdi_id[idx]; +} + +static inline int is_data_obj(uint64_t oid) +{ + return !(VDI_BIT & oid); +} + +static inline uint64_t data_oid_to_idx(uint64_t oid) +{ + return oid & (MAX_DATA_OBJS - 1); +} + +static inline uint64_t vid_to_vdi_oid(uint32_t vid) +{ + return VDI_BIT | ((uint64_t)vid << VDI_SPACE_SHIFT); +} + +static inline uint64_t vid_to_vmstate_oid(uint32_t vid, uint32_t idx) +{ + return VMSTATE_BIT | ((uint64_t)vid << VDI_SPACE_SHIFT) | idx; +} + +static inline uint64_t vid_to_data_oid(uint32_t vid, uint32_t idx) +{ + return ((uint64_t)vid << VDI_SPACE_SHIFT) | idx; +} + +static inline int is_snapshot(struct SheepdogInode *inode) +{ + return !!inode->snap_ctime; +} + +#undef dprintf +#ifdef DEBUG_SDOG +#define dprintf(fmt, args...) \ + do { \ + fprintf(stdout, "%s %d: " fmt, __func__, __LINE__, ##args); \ + } while (0) +#else +#define dprintf(fmt, args...) +#endif + +typedef struct SheepdogAIOCB SheepdogAIOCB; + +typedef struct AIOReq { + SheepdogAIOCB *aiocb; + unsigned int iov_offset; + + uint64_t oid; + uint64_t base_oid; + uint64_t offset; + unsigned int data_len; + uint8_t flags; + uint32_t id; + + QLIST_ENTRY(AIOReq) outstanding_aio_siblings; + QLIST_ENTRY(AIOReq) aioreq_siblings; +} AIOReq; + +enum AIOCBState { + AIOCB_WRITE_UDATA, + AIOCB_READ_UDATA, +}; + +struct SheepdogAIOCB { + BlockDriverAIOCB common; + + QEMUIOVector *qiov; + + int64_t sector_num; + int nb_sectors; + + int ret; + enum AIOCBState aiocb_type; + + QEMUBH *bh; + void (*aio_done_func)(SheepdogAIOCB *); + + int canceled; + + QLIST_HEAD(aioreq_head, AIOReq) aioreq_head; +}; + +typedef struct BDRVSheepdogState { + SheepdogInode inode; + + uint32_t min_dirty_data_idx; + uint32_t max_dirty_data_idx; + + char name[SD_MAX_VDI_LEN]; + int is_snapshot; + + char *addr; + char *port; + int fd; + + uint32_t aioreq_seq_num; + QLIST_HEAD(outstanding_aio_head, AIOReq) outstanding_aio_head; +} BDRVSheepdogState; + +static const char * sd_strerror(int err) +{ + int i; + + static const struct { + int err; + const char *desc; + } errors[] = { + {SD_RES_SUCCESS, "Success"}, + {SD_RES_UNKNOWN, "Unknown error"}, + {SD_RES_NO_OBJ, "No object found"}, + {SD_RES_EIO, "I/O error"}, + {SD_RES_VDI_EXIST, "VDI exists already"}, + {SD_RES_INVALID_PARMS, "Invalid parameters"}, + {SD_RES_SYSTEM_ERROR, "System error"}, + {SD_RES_VDI_LOCKED, "VDI is already locked"}, + {SD_RES_NO_VDI, "No vdi found"}, + {SD_RES_NO_BASE_VDI, "No base VDI found"}, + {SD_RES_VDI_READ, "Failed read the requested VDI"}, + {SD_RES_VDI_WRITE, "Failed to write the requested VDI"}, + {SD_RES_BASE_VDI_READ, "Failed to read the base VDI"}, + {SD_RES_BASE_VDI_WRITE, "Failed to write the base VDI"}, + {SD_RES_NO_TAG, "Failed to find the requested tag"}, + {SD_RES_STARTUP, "The system is still booting"}, + {SD_RES_VDI_NOT_LOCKED, "VDI isn't locked"}, + {SD_RES_SHUTDOWN, "The system is shutting down"}, + {SD_RES_NO_MEM, "Out of memory on the server"}, + {SD_RES_FULL_VDI, "We already have the maximum vdis"}, + {SD_RES_VER_MISMATCH, "Protocol version mismatch"}, + {SD_RES_NO_SPACE, "Server has no space for new objects"}, + {SD_RES_WAIT_FOR_FORMAT, "Sheepdog is waiting for a format operation"}, + {SD_RES_WAIT_FOR_JOIN, "Sheepdog is waiting for other nodes joining"}, + {SD_RES_JOIN_FAILED, "Target node had failed to join sheepdog"}, + }; + + for (i = 0; i < ARRAY_SIZE(errors); ++i) { + if (errors[i].err == err) { + return errors[i].desc; + } + } + + return "Invalid error code"; +} + +/* + * Sheepdog I/O handling: + * + * 1. In the sd_aio_readv/writev, read/write requests are added to the + * QEMU Bottom Halves. + * + * 2. In sd_readv_writev_bh_cb, the callbacks of BHs, we send the I/O + * requests to the server and link the requests to the + * outstanding_list in the BDRVSheepdogState. we exits the + * function without waiting for receiving the response. + * + * 3. We receive the response in aio_read_response, the fd handler to + * the sheepdog connection. If metadata update is needed, we send + * the write request to the vdi object in sd_write_done, the write + * completion function. The AIOCB callback is not called until all + * the requests belonging to the AIOCB are finished. + */ + +static inline AIOReq *alloc_aio_req(BDRVSheepdogState *s, SheepdogAIOCB *acb, + uint64_t oid, unsigned int data_len, + uint64_t offset, uint8_t flags, + uint64_t base_oid, unsigned int iov_offset) +{ + AIOReq *aio_req; + + aio_req = qemu_malloc(sizeof(*aio_req)); + aio_req->aiocb = acb; + aio_req->iov_offset = iov_offset; + aio_req->oid = oid; + aio_req->base_oid = base_oid; + aio_req->offset = offset; + aio_req->data_len = data_len; + aio_req->flags = flags; + aio_req->id = s->aioreq_seq_num++; + + QLIST_INSERT_HEAD(&s->outstanding_aio_head, aio_req, + outstanding_aio_siblings); + QLIST_INSERT_HEAD(&acb->aioreq_head, aio_req, aioreq_siblings); + + return aio_req; +} + +static inline int free_aio_req(BDRVSheepdogState *s, AIOReq *aio_req) +{ + SheepdogAIOCB *acb = aio_req->aiocb; + QLIST_REMOVE(aio_req, outstanding_aio_siblings); + QLIST_REMOVE(aio_req, aioreq_siblings); + qemu_free(aio_req); + + return !QLIST_EMPTY(&acb->aioreq_head); +} + +static void sd_finish_aiocb(SheepdogAIOCB *acb) +{ + if (!acb->canceled) { + acb->common.cb(acb->common.opaque, acb->ret); + } + qemu_aio_release(acb); +} + +static void sd_aio_cancel(BlockDriverAIOCB *blockacb) +{ + SheepdogAIOCB *acb = (SheepdogAIOCB *)blockacb; + + /* + * Sheepdog cannot cancel the requests which are already sent to + * the servers, so we just complete the request with -EIO here. + */ + acb->common.cb(acb->common.opaque, -EIO); + acb->canceled = 1; +} + +static AIOPool sd_aio_pool = { + .aiocb_size = sizeof(SheepdogAIOCB), + .cancel = sd_aio_cancel, +}; + +static SheepdogAIOCB *sd_aio_setup(BlockDriverState *bs, QEMUIOVector *qiov, + int64_t sector_num, int nb_sectors, + BlockDriverCompletionFunc *cb, void *opaque) +{ + SheepdogAIOCB *acb; + + acb = qemu_aio_get(&sd_aio_pool, bs, cb, opaque); + + acb->qiov = qiov; + + acb->sector_num = sector_num; + acb->nb_sectors = nb_sectors; + + acb->aio_done_func = NULL; + acb->canceled = 0; + acb->bh = NULL; + acb->ret = 0; + QLIST_INIT(&acb->aioreq_head); + return acb; +} + +static int sd_schedule_bh(QEMUBHFunc *cb, SheepdogAIOCB *acb) +{ + if (acb->bh) { + error_report("bug: %d %d\n", acb->aiocb_type, acb->aiocb_type); + return -EIO; + } + + acb->bh = qemu_bh_new(cb, acb); + if (!acb->bh) { + error_report("oom: %d %d\n", acb->aiocb_type, acb->aiocb_type); + return -EIO; + } + + qemu_bh_schedule(acb->bh); + + return 0; +} + +#ifdef _WIN32 + +struct msghdr { + struct iovec *msg_iov; + size_t msg_iovlen; +}; + +static ssize_t sendmsg(int s, const struct msghdr *msg, int flags) +{ + size_t size = 0; + char *buf, *p; + int i, ret; + + /* count the msg size */ + for (i = 0; i < msg->msg_iovlen; i++) { + size += msg->msg_iov[i].iov_len; + } + buf = qemu_malloc(size); + + p = buf; + for (i = 0; i < msg->msg_iovlen; i++) { + memcpy(p, msg->msg_iov[i].iov_base, msg->msg_iov[i].iov_len); + p += msg->msg_iov[i].iov_len; + } + + ret = send(s, buf, size, flags); + + qemu_free(buf); + return ret; +} + +static ssize_t recvmsg(int s, struct msghdr *msg, int flags) +{ + size_t size = 0; + char *buf, *p; + int i, ret; + + /* count the msg size */ + for (i = 0; i < msg->msg_iovlen; i++) { + size += msg->msg_iov[i].iov_len; + } + buf = qemu_malloc(size); + + ret = recv(s, buf, size, flags); + if (ret < 0) { + goto out; + } + + p = buf; + for (i = 0; i < msg->msg_iovlen; i++) { + memcpy(msg->msg_iov[i].iov_base, p, msg->msg_iov[i].iov_len); + p += msg->msg_iov[i].iov_len; + } +out: + qemu_free(buf); + return ret; +} + +#endif + +/* + * Send/recv data with iovec buffers + * + * This function send/recv data from/to the iovec buffer directly. + * The first `offset' bytes in the iovec buffer are skipped and next + * `len' bytes are used. + * + * For example, + * + * do_send_recv(sockfd, iov, len, offset, 1); + * + * is equals to + * + * char *buf = malloc(size); + * iov_to_buf(iov, iovcnt, buf, offset, size); + * send(sockfd, buf, size, 0); + * free(buf); + */ +static int do_send_recv(int sockfd, struct iovec *iov, int len, int offset, + int write) +{ + struct msghdr msg; + int ret, diff; + + memset(&msg, 0, sizeof(msg)); + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + len += offset; + + while (iov->iov_len < len) { + len -= iov->iov_len; + + iov++; + msg.msg_iovlen++; + } + + diff = iov->iov_len - len; + iov->iov_len -= diff; + + while (msg.msg_iov->iov_len <= offset) { + offset -= msg.msg_iov->iov_len; + + msg.msg_iov++; + msg.msg_iovlen--; + } + + msg.msg_iov->iov_base = (char *) msg.msg_iov->iov_base + offset; + msg.msg_iov->iov_len -= offset; + + if (write) { + ret = sendmsg(sockfd, &msg, 0); + } else { + ret = recvmsg(sockfd, &msg, 0); + } + + msg.msg_iov->iov_base = (char *) msg.msg_iov->iov_base - offset; + msg.msg_iov->iov_len += offset; + + iov->iov_len += diff; + return ret; +} + +static int connect_to_sdog(const char *addr, const char *port) +{ + char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; + int fd, ret; + struct addrinfo hints, *res, *res0; + + if (!addr) { + addr = SD_DEFAULT_ADDR; + port = SD_DEFAULT_PORT; + } + + memset(&hints, 0, sizeof(hints)); + hints.ai_socktype = SOCK_STREAM; + + ret = getaddrinfo(addr, port, &hints, &res0); + if (ret) { + error_report("unable to get address info %s, %s\n", + addr, strerror(errno)); + return -1; + } + + for (res = res0; res; res = res->ai_next) { + ret = getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, sizeof(hbuf), + sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV); + if (ret) { + continue; + } + + fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (fd < 0) { + continue; + } + + reconnect: + ret = connect(fd, res->ai_addr, res->ai_addrlen); + if (ret < 0) { + if (errno == EINTR) { + goto reconnect; + } + break; + } + + dprintf("connected to %s:%s\n", addr, port); + goto success; + } + fd = -1; + error_report("failed connect to %s:%s\n", addr, port); +success: + freeaddrinfo(res0); + return fd; +} + +static int do_readv_writev(int sockfd, struct iovec *iov, int len, + int iov_offset, int write) +{ + int ret; +again: + ret = do_send_recv(sockfd, iov, len, iov_offset, write); + if (ret < 0) { + if (errno == EINTR || errno == EAGAIN) { + goto again; + } + error_report("failed to recv a rsp, %s\n", strerror(errno)); + return 1; + } + + iov_offset += ret; + len -= ret; + if (len) { + goto again; + } + + return 0; +} + +static int do_readv(int sockfd, struct iovec *iov, int len, int iov_offset) +{ + return do_readv_writev(sockfd, iov, len, iov_offset, 0); +} + +static int do_writev(int sockfd, struct iovec *iov, int len, int iov_offset) +{ + return do_readv_writev(sockfd, iov, len, iov_offset, 1); +} + +static int do_read_write(int sockfd, void *buf, int len, int write) +{ + struct iovec iov; + + iov.iov_base = buf; + iov.iov_len = len; + + return do_readv_writev(sockfd, &iov, len, 0, write); +} + +static int do_read(int sockfd, void *buf, int len) +{ + return do_read_write(sockfd, buf, len, 0); +} + +static int do_write(int sockfd, void *buf, int len) +{ + return do_read_write(sockfd, buf, len, 1); +} + +static int send_req(int sockfd, SheepdogReq *hdr, void *data, + unsigned int *wlen) +{ + int ret; + struct iovec iov[2]; + + iov[0].iov_base = hdr; + iov[0].iov_len = sizeof(*hdr); + + if (*wlen) { + iov[1].iov_base = data; + iov[1].iov_len = *wlen; + } + + ret = do_writev(sockfd, iov, sizeof(*hdr) + *wlen, 0); + if (ret) { + error_report("failed to send a req, %s\n", strerror(errno)); + ret = -1; + } + + return ret; +} + +static int do_req(int sockfd, SheepdogReq *hdr, void *data, + unsigned int *wlen, unsigned int *rlen) +{ + int ret; + + ret = send_req(sockfd, hdr, data, wlen); + if (ret) { + ret = -1; + goto out; + } + + ret = do_read(sockfd, hdr, sizeof(*hdr)); + if (ret) { + error_report("failed to get a rsp, %s\n", strerror(errno)); + ret = -1; + goto out; + } + + if (*rlen > hdr->data_length) { + *rlen = hdr->data_length; + } + + if (*rlen) { + ret = do_read(sockfd, data, *rlen); + if (ret) { + error_report("failed to get the data, %s\n", strerror(errno)); + ret = -1; + goto out; + } + } + ret = 0; +out: + return ret; +} + +static int add_aio_request(BDRVSheepdogState *s, AIOReq *aio_req, + struct iovec *iov, int niov, int create, + enum AIOCBState aiocb_type); + +/* + * This function searchs pending requests to the object `oid', and + * sends them. + */ +static void send_pending_req(BDRVSheepdogState *s, uint64_t oid, uint32_t id) +{ + AIOReq *aio_req, *next; + SheepdogAIOCB *acb; + int ret; + + QLIST_FOREACH_SAFE(aio_req, &s->outstanding_aio_head, + outstanding_aio_siblings, next) { + if (id == aio_req->id) { + continue; + } + if (aio_req->oid != oid) { + continue; + } + + acb = aio_req->aiocb; + ret = add_aio_request(s, aio_req, acb->qiov->iov, + acb->qiov->niov, 0, acb->aiocb_type); + if (ret < 0) { + error_report("add_aio_request is failed\n"); + free_aio_req(s, aio_req); + if (QLIST_EMPTY(&acb->aioreq_head)) { + sd_finish_aiocb(acb); + } + } + } +} + +/* + * Receive responses of the I/O requests. + * + * This function is registered as a fd handler, and called from the + * main loop when s->fd is ready for reading responses. + */ +static void aio_read_response(void *opaque) +{ + SheepdogObjRsp rsp; + BDRVSheepdogState *s = opaque; + int fd = s->fd; + int ret; + AIOReq *aio_req = NULL; + SheepdogAIOCB *acb; + int rest; + unsigned long idx; + + if (QLIST_EMPTY(&s->outstanding_aio_head)) { + return; + } + + /* read a header */ + ret = do_read(fd, &rsp, sizeof(rsp)); + if (ret) { + error_report("failed to get the header, %s\n", strerror(errno)); + return; + } + + /* find the right aio_req from the outstanding_aio list */ + QLIST_FOREACH(aio_req, &s->outstanding_aio_head, outstanding_aio_siblings) { + if (aio_req->id == rsp.id) { + break; + } + } + if (!aio_req) { + error_report("cannot find aio_req %x\n", rsp.id); + return; + } + + acb = aio_req->aiocb; + + switch (acb->aiocb_type) { + case AIOCB_WRITE_UDATA: + if (!is_data_obj(aio_req->oid)) { + break; + } + idx = data_oid_to_idx(aio_req->oid); + + if (s->inode.data_vdi_id[idx] != s->inode.vdi_id) { + /* + * If the object is newly created one, we need to update + * the vdi object (metadata object). min_dirty_data_idx + * and max_dirty_data_idx are changed to include updated + * index between them. + */ + s->inode.data_vdi_id[idx] = s->inode.vdi_id; + s->max_dirty_data_idx = MAX(idx, s->max_dirty_data_idx); + s->min_dirty_data_idx = MIN(idx, s->min_dirty_data_idx); + + /* + * Some requests may be blocked because simultaneous + * create requests are not allowed, so we search the + * pending requests here. + */ + send_pending_req(s, vid_to_data_oid(s->inode.vdi_id, idx), rsp.id); + } + break; + case AIOCB_READ_UDATA: + ret = do_readv(fd, acb->qiov->iov, rsp.data_length, + aio_req->iov_offset); + if (ret) { + error_report("failed to get the data, %s\n", strerror(errno)); + return; + } + break; + } + + if (rsp.result != SD_RES_SUCCESS) { + acb->ret = -EIO; + error_report("%s\n", sd_strerror(rsp.result)); + } + + rest = free_aio_req(s, aio_req); + if (!rest) { + /* + * We've finished all requests which belong to the AIOCB, so + * we can call the callback now. + */ + acb->aio_done_func(acb); + } +} + +static int aio_flush_request(void *opaque) +{ + BDRVSheepdogState *s = opaque; + + return !QLIST_EMPTY(&s->outstanding_aio_head); +} + +#if !defined(SOL_TCP) || !defined(TCP_CORK) + +static int set_cork(int fd, int v) +{ + return 0; +} + +#else + +static int set_cork(int fd, int v) +{ + return setsockopt(fd, SOL_TCP, TCP_CORK, &v, sizeof(v)); +} + +#endif + +static int set_nodelay(int fd) +{ + int ret, opt; + + opt = 1; + ret = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&opt, sizeof(opt)); + return ret; +} + +/* + * Return a socket discriptor to read/write objects. + * + * We cannot use this discriptor for other operations because + * the block driver may be on waiting response from the server. + */ +static int get_sheep_fd(BDRVSheepdogState *s) +{ + int ret, fd; + + fd = connect_to_sdog(s->addr, s->port); + if (fd < 0) { + error_report("%s\n", strerror(errno)); + return -1; + } + + socket_set_nonblock(fd); + + ret = set_nodelay(fd); + if (ret) { + error_report("%s\n", strerror(errno)); + closesocket(fd); + return -1; + } + + qemu_aio_set_fd_handler(fd, aio_read_response, NULL, aio_flush_request, + NULL, s); + return fd; +} + +/* + * Parse a filename + * + * filename must be one of the following formats: + * 1. [vdiname] + * 2. [vdiname]:[snapid] + * 3. [vdiname]:[tag] + * 4. [hostname]:[port]:[vdiname] + * 5. [hostname]:[port]:[vdiname]:[snapid] + * 6. [hostname]:[port]:[vdiname]:[tag] + * + * You can boot from the snapshot images by specifying `snapid` or + * `tag'. + * + * You can run VMs outside the Sheepdog cluster by specifying + * `hostname' and `port' (experimental). + */ +static int parse_vdiname(BDRVSheepdogState *s, const char *filename, + char *vdi, uint32_t *snapid, char *tag) +{ + char *p, *q; + int nr_sep; + + p = q = qemu_strdup(filename); + + /* count the number of separators */ + nr_sep = 0; + while (*p) { + if (*p == ':') { + nr_sep++; + } + p++; + } + p = q; + + /* use the first two tokens as hostname and port number. */ + if (nr_sep >= 2) { + s->addr = p; + p = strchr(p, ':'); + *p++ = '\0'; + + s->port = p; + p = strchr(p, ':'); + *p++ = '\0'; + } else { + s->addr = NULL; + s->port = 0; + } + + strncpy(vdi, p, SD_MAX_VDI_LEN); + + p = strchr(vdi, ':'); + if (p) { + *p++ = '\0'; + *snapid = strtoul(p, NULL, 10); + if (*snapid == 0) { + strncpy(tag, p, SD_MAX_VDI_TAG_LEN); + } + } else { + *snapid = CURRENT_VDI_ID; /* search current vdi */ + } + + if (s->addr == NULL) { + qemu_free(q); + } + + return 0; +} + +static int find_vdi_name(BDRVSheepdogState *s, char *filename, uint32_t snapid, + char *tag, uint32_t *vid, int for_snapshot) +{ + int ret, fd; + SheepdogVdiReq hdr; + SheepdogVdiRsp *rsp = (SheepdogVdiRsp *)&hdr; + unsigned int wlen, rlen = 0; + char buf[SD_MAX_VDI_LEN + SD_MAX_VDI_TAG_LEN]; + + fd = connect_to_sdog(s->addr, s->port); + if (fd < 0) { + return -1; + } + + memset(buf, 0, sizeof(buf)); + strncpy(buf, filename, SD_MAX_VDI_LEN); + strncpy(buf + SD_MAX_VDI_LEN, tag, SD_MAX_VDI_TAG_LEN); + + memset(&hdr, 0, sizeof(hdr)); + if (for_snapshot) { + hdr.opcode = SD_OP_GET_VDI_INFO; + } else { + hdr.opcode = SD_OP_LOCK_VDI; + } + wlen = SD_MAX_VDI_LEN + SD_MAX_VDI_TAG_LEN; + hdr.proto_ver = SD_PROTO_VER; + hdr.data_length = wlen; + hdr.snapid = snapid; + hdr.flags = SD_FLAG_CMD_WRITE; + + ret = do_req(fd, (SheepdogReq *)&hdr, buf, &wlen, &rlen); + if (ret) { + ret = -1; + goto out; + } + + if (rsp->result != SD_RES_SUCCESS) { + error_report("cannot get vdi info, %s, %s %d %s\n", + sd_strerror(rsp->result), filename, snapid, tag); + ret = -1; + goto out; + } + *vid = rsp->vdi_id; + + ret = 0; +out: + closesocket(fd); + return ret; +} + +static int add_aio_request(BDRVSheepdogState *s, AIOReq *aio_req, + struct iovec *iov, int niov, int create, + enum AIOCBState aiocb_type) +{ + int nr_copies = s->inode.nr_copies; + SheepdogObjReq hdr; + unsigned int wlen; + int ret; + uint64_t oid = aio_req->oid; + unsigned int datalen = aio_req->data_len; + uint64_t offset = aio_req->offset; + uint8_t flags = aio_req->flags; + uint64_t old_oid = aio_req->base_oid; + + if (!nr_copies) { + error_report("bug\n"); + } + + memset(&hdr, 0, sizeof(hdr)); + + if (aiocb_type == AIOCB_READ_UDATA) { + wlen = 0; + hdr.opcode = SD_OP_READ_OBJ; + hdr.flags = flags; + } else if (create) { + wlen = datalen; + hdr.opcode = SD_OP_CREATE_AND_WRITE_OBJ; + hdr.flags = SD_FLAG_CMD_WRITE | flags; + } else { + wlen = datalen; + hdr.opcode = SD_OP_WRITE_OBJ; + hdr.flags = SD_FLAG_CMD_WRITE | flags; + } + + hdr.oid = oid; + hdr.cow_oid = old_oid; + hdr.copies = s->inode.nr_copies; + + hdr.data_length = datalen; + hdr.offset = offset; + + hdr.id = aio_req->id; + + set_cork(s->fd, 1); + + /* send a header */ + ret = do_write(s->fd, &hdr, sizeof(hdr)); + if (ret) { + error_report("failed to send a req, %s\n", strerror(errno)); + return -EIO; + } + + if (wlen) { + ret = do_writev(s->fd, iov, wlen, aio_req->iov_offset); + if (ret) { + error_report("failed to send a data, %s\n", strerror(errno)); + return -EIO; + } + } + + set_cork(s->fd, 0); + + return 0; +} + +static int read_write_object(int fd, char *buf, uint64_t oid, int copies, + unsigned int datalen, uint64_t offset, + int write, int create) +{ + SheepdogObjReq hdr; + SheepdogObjRsp *rsp = (SheepdogObjRsp *)&hdr; + unsigned int wlen, rlen; + int ret; + + memset(&hdr, 0, sizeof(hdr)); + + if (write) { + wlen = datalen; + rlen = 0; + hdr.flags = SD_FLAG_CMD_WRITE; + if (create) { + hdr.opcode = SD_OP_CREATE_AND_WRITE_OBJ; + } else { + hdr.opcode = SD_OP_WRITE_OBJ; + } + } else { + wlen = 0; + rlen = datalen; + hdr.opcode = SD_OP_READ_OBJ; + } + hdr.oid = oid; + hdr.data_length = datalen; + hdr.offset = offset; + hdr.copies = copies; + + ret = do_req(fd, (SheepdogReq *)&hdr, buf, &wlen, &rlen); + if (ret) { + error_report("failed to send a request to the sheep\n"); + return -1; + } + + switch (rsp->result) { + case SD_RES_SUCCESS: + return 0; + default: + error_report("%s\n", sd_strerror(rsp->result)); + return -1; + } +} + +static int read_object(int fd, char *buf, uint64_t oid, int copies, + unsigned int datalen, uint64_t offset) +{ + return read_write_object(fd, buf, oid, copies, datalen, offset, 0, 0); +} + +static int write_object(int fd, char *buf, uint64_t oid, int copies, + unsigned int datalen, uint64_t offset, int create) +{ + return read_write_object(fd, buf, oid, copies, datalen, offset, 1, create); +} + +static int sd_open(BlockDriverState *bs, const char *filename, int flags) +{ + int ret, fd; + uint32_t vid = 0; + BDRVSheepdogState *s = bs->opaque; + char vdi[SD_MAX_VDI_LEN], tag[SD_MAX_VDI_TAG_LEN]; + uint32_t snapid; + char *buf = NULL; + + strstart(filename, "sheepdog:", (const char **)&filename); + + QLIST_INIT(&s->outstanding_aio_head); + s->fd = -1; + + memset(vdi, 0, sizeof(vdi)); + memset(tag, 0, sizeof(tag)); + if (parse_vdiname(s, filename, vdi, &snapid, tag) < 0) { + goto out; + } + s->fd = get_sheep_fd(s); + if (s->fd < 0) { + goto out; + } + + ret = find_vdi_name(s, vdi, snapid, tag, &vid, 0); + if (ret) { + goto out; + } + + if (snapid) { + dprintf("%" PRIx32 " snapshot inode was open.\n", vid); + s->is_snapshot = 1; + } + + fd = connect_to_sdog(s->addr, s->port); + if (fd < 0) { + error_report("failed to connect\n"); + goto out; + } + + buf = qemu_malloc(SD_INODE_SIZE); + ret = read_object(fd, buf, vid_to_vdi_oid(vid), 0, SD_INODE_SIZE, 0); + + closesocket(fd); + + if (ret) { + goto out; + } + + memcpy(&s->inode, buf, sizeof(s->inode)); + s->min_dirty_data_idx = UINT32_MAX; + s->max_dirty_data_idx = 0; + + bs->total_sectors = s->inode.vdi_size / SECTOR_SIZE; + strncpy(s->name, vdi, sizeof(s->name)); + qemu_free(buf); + return 0; +out: + qemu_aio_set_fd_handler(s->fd, NULL, NULL, NULL, NULL, NULL); + if (s->fd >= 0) { + closesocket(s->fd); + } + qemu_free(buf); + return -1; +} + +static int do_sd_create(char *filename, int64_t vdi_size, + uint32_t base_vid, uint32_t *vdi_id, int snapshot, + const char *addr, const char *port) +{ + SheepdogVdiReq hdr; + SheepdogVdiRsp *rsp = (SheepdogVdiRsp *)&hdr; + int fd, ret; + unsigned int wlen, rlen = 0; + char buf[SD_MAX_VDI_LEN]; + + fd = connect_to_sdog(addr, port); + if (fd < 0) { + return -EIO; + } + + memset(buf, 0, sizeof(buf)); + strncpy(buf, filename, SD_MAX_VDI_LEN); + + memset(&hdr, 0, sizeof(hdr)); + hdr.opcode = SD_OP_NEW_VDI; + hdr.base_vdi_id = base_vid; + + wlen = SD_MAX_VDI_LEN; + + hdr.flags = SD_FLAG_CMD_WRITE; + hdr.snapid = snapshot; + + hdr.data_length = wlen; + hdr.vdi_size = vdi_size; + + ret = do_req(fd, (SheepdogReq *)&hdr, buf, &wlen, &rlen); + + closesocket(fd); + + if (ret) { + return -EIO; + } + + if (rsp->result != SD_RES_SUCCESS) { + error_report("%s, %s\n", sd_strerror(rsp->result), filename); + return -EIO; + } + + if (vdi_id) { + *vdi_id = rsp->vdi_id; + } + + return 0; +} + +static int sd_create(const char *filename, QEMUOptionParameter *options) +{ + int ret; + uint32_t vid = 0, base_vid = 0; + int64_t vdi_size = 0; + char *backing_file = NULL; + BDRVSheepdogState s; + char vdi[SD_MAX_VDI_LEN], tag[SD_MAX_VDI_TAG_LEN]; + uint32_t snapid; + + strstart(filename, "sheepdog:", (const char **)&filename); + + memset(&s, 0, sizeof(s)); + memset(vdi, 0, sizeof(vdi)); + memset(tag, 0, sizeof(tag)); + if (parse_vdiname(&s, filename, vdi, &snapid, tag) < 0) { + error_report("invalid filename\n"); + return -EINVAL; + } + + while (options && options->name) { + if (!strcmp(options->name, BLOCK_OPT_SIZE)) { + vdi_size = options->value.n; + } else if (!strcmp(options->name, BLOCK_OPT_BACKING_FILE)) { + backing_file = options->value.s; + } + options++; + } + + if (vdi_size > SD_MAX_VDI_SIZE) { + error_report("too big image size\n"); + return -EINVAL; + } + + if (backing_file) { + BlockDriverState *bs; + BDRVSheepdogState *s; + BlockDriver *drv; + + /* Currently, only Sheepdog backing image is supported. */ + drv = bdrv_find_protocol(backing_file); + if (!drv || strcmp(drv->protocol_name, "sheepdog") != 0) { + error_report("backing_file must be a sheepdog image\n"); + return -EINVAL; + } + + ret = bdrv_file_open(&bs, backing_file, 0); + if (ret < 0) + return -EIO; + + s = bs->opaque; + + if (!is_snapshot(&s->inode)) { + error_report("cannot clone from a non snapshot vdi\n"); + bdrv_delete(bs); + return -EINVAL; + } + + base_vid = s->inode.vdi_id; + bdrv_delete(bs); + } + + return do_sd_create((char *)vdi, vdi_size, base_vid, &vid, 0, s.addr, s.port); +} + +static void sd_close(BlockDriverState *bs) +{ + BDRVSheepdogState *s = bs->opaque; + SheepdogVdiReq hdr; + SheepdogVdiRsp *rsp = (SheepdogVdiRsp *)&hdr; + unsigned int wlen, rlen = 0; + int fd, ret; + + dprintf("%s\n", s->name); + + fd = connect_to_sdog(s->addr, s->port); + if (fd < 0) { + return; + } + + memset(&hdr, 0, sizeof(hdr)); + + hdr.opcode = SD_OP_RELEASE_VDI; + wlen = strlen(s->name) + 1; + hdr.data_length = wlen; + hdr.flags = SD_FLAG_CMD_WRITE; + + ret = do_req(fd, (SheepdogReq *)&hdr, s->name, &wlen, &rlen); + + closesocket(fd); + + if (!ret && rsp->result != SD_RES_SUCCESS && + rsp->result != SD_RES_VDI_NOT_LOCKED) { + error_report("%s, %s\n", sd_strerror(rsp->result), s->name); + } + + qemu_aio_set_fd_handler(s->fd, NULL, NULL, NULL, NULL, NULL); + closesocket(s->fd); + qemu_free(s->addr); +} + +static int64_t sd_getlength(BlockDriverState *bs) +{ + BDRVSheepdogState *s = bs->opaque; + + return s->inode.vdi_size; +} + +static int sd_truncate(BlockDriverState *bs, int64_t offset) +{ + BDRVSheepdogState *s = bs->opaque; + int ret, fd; + unsigned int datalen; + + if (offset < s->inode.vdi_size) { + error_report("shrinking is not supported\n"); + return -EINVAL; + } else if (offset > SD_MAX_VDI_SIZE) { + error_report("too big image size\n"); + return -EINVAL; + } + + fd = connect_to_sdog(s->addr, s->port); + if (fd < 0) { + return -EIO; + } + + /* we don't need to update entire object */ + datalen = SD_INODE_SIZE - sizeof(s->inode.data_vdi_id); + s->inode.vdi_size = offset; + ret = write_object(fd, (char *)&s->inode, vid_to_vdi_oid(s->inode.vdi_id), + s->inode.nr_copies, datalen, 0, 0); + close(fd); + + if (ret < 0) { + error_report("failed to update an inode.\n"); + return -EIO; + } + + return 0; +} + +/* + * This function is called after writing data objects. If we need to + * update metadata, this sends a write request to the vdi object. + * Otherwise, this calls the AIOCB callback. + */ +static void sd_write_done(SheepdogAIOCB *acb) +{ + int ret; + BDRVSheepdogState *s = acb->common.bs->opaque; + struct iovec iov; + AIOReq *aio_req; + uint32_t offset, data_len, mn, mx; + + mn = s->min_dirty_data_idx; + mx = s->max_dirty_data_idx; + if (mn <= mx) { + /* we need to update the vdi object. */ + offset = sizeof(s->inode) - sizeof(s->inode.data_vdi_id) + + mn * sizeof(s->inode.data_vdi_id[0]); + data_len = (mx - mn + 1) * sizeof(s->inode.data_vdi_id[0]); + + s->min_dirty_data_idx = UINT32_MAX; + s->max_dirty_data_idx = 0; + + iov.iov_base = &s->inode; + iov.iov_len = sizeof(s->inode); + aio_req = alloc_aio_req(s, acb, vid_to_vdi_oid(s->inode.vdi_id), + data_len, offset, 0, 0, offset); + ret = add_aio_request(s, aio_req, &iov, 1, 0, AIOCB_WRITE_UDATA); + if (ret) { + free_aio_req(s, aio_req); + acb->ret = -EIO; + goto out; + } + + acb->aio_done_func = sd_finish_aiocb; + acb->aiocb_type = AIOCB_WRITE_UDATA; + return; + } +out: + sd_finish_aiocb(acb); +} + +/* + * Create a writable VDI from a snapshot + */ +static int sd_create_branch(BDRVSheepdogState *s) +{ + int ret, fd; + uint32_t vid; + char *buf; + + dprintf("%" PRIx32 " is snapshot.\n", s->inode.vdi_id); + + buf = qemu_malloc(SD_INODE_SIZE); + + ret = do_sd_create(s->name, s->inode.vdi_size, s->inode.vdi_id, &vid, 1, + s->addr, s->port); + if (ret) { + goto out; + } + + dprintf("%" PRIx32 " is created.\n", vid); + + fd = connect_to_sdog(s->addr, s->port); + if (fd < 0) { + error_report("failed to connect\n"); + goto out; + } + + ret = read_object(fd, buf, vid_to_vdi_oid(vid), s->inode.nr_copies, + SD_INODE_SIZE, 0); + + closesocket(fd); + + if (ret < 0) { + goto out; + } + + memcpy(&s->inode, buf, sizeof(s->inode)); + + s->is_snapshot = 0; + ret = 0; + dprintf("%" PRIx32 " was newly created.\n", s->inode.vdi_id); + +out: + qemu_free(buf); + + return ret; +} + +/* + * Send I/O requests to the server. + * + * This function sends requests to the server, links the requests to + * the outstanding_list in BDRVSheepdogState, and exits without + * waiting the response. The responses are received in the + * `aio_read_response' function which is called from the main loop as + * a fd handler. + */ +static void sd_readv_writev_bh_cb(void *p) +{ + SheepdogAIOCB *acb = p; + int ret = 0; + unsigned long len, done = 0, total = acb->nb_sectors * SECTOR_SIZE; + unsigned long idx = acb->sector_num * SECTOR_SIZE / SD_DATA_OBJ_SIZE; + uint64_t oid; + uint64_t offset = (acb->sector_num * SECTOR_SIZE) % SD_DATA_OBJ_SIZE; + BDRVSheepdogState *s = acb->common.bs->opaque; + SheepdogInode *inode = &s->inode; + AIOReq *aio_req; + + qemu_bh_delete(acb->bh); + acb->bh = NULL; + + if (acb->aiocb_type == AIOCB_WRITE_UDATA && s->is_snapshot) { + /* + * In the case we open the snapshot VDI, Sheepdog creates the + * writable VDI when we do a write operation first. + */ + ret = sd_create_branch(s); + if (ret) { + acb->ret = -EIO; + goto out; + } + } + + while (done != total) { + uint8_t flags = 0; + uint64_t old_oid = 0; + int create = 0; + + oid = vid_to_data_oid(inode->data_vdi_id[idx], idx); + + len = MIN(total - done, SD_DATA_OBJ_SIZE - offset); + + if (!inode->data_vdi_id[idx]) { + if (acb->aiocb_type == AIOCB_READ_UDATA) { + goto done; + } + + create = 1; + } else if (acb->aiocb_type == AIOCB_WRITE_UDATA + && !is_data_obj_writeable(inode, idx)) { + /* Copy-On-Write */ + create = 1; + old_oid = oid; + flags = SD_FLAG_CMD_COW; + } + + if (create) { + dprintf("update ino (%" PRIu32") %" PRIu64 " %" PRIu64 + " %" PRIu64 "\n", inode->vdi_id, oid, + vid_to_data_oid(inode->data_vdi_id[idx], idx), idx); + oid = vid_to_data_oid(inode->vdi_id, idx); + dprintf("new oid %lx\n", oid); + } + + aio_req = alloc_aio_req(s, acb, oid, len, offset, flags, old_oid, done); + + if (create) { + AIOReq *areq; + QLIST_FOREACH(areq, &s->outstanding_aio_head, + outstanding_aio_siblings) { + if (areq == aio_req) { + continue; + } + if (areq->oid == oid) { + /* + * Sheepdog cannot handle simultaneous create + * requests to the same object. So we cannot send + * the request until the previous request + * finishes. + */ + aio_req->flags = 0; + aio_req->base_oid = 0; + goto done; + } + } + } + + ret = add_aio_request(s, aio_req, acb->qiov->iov, acb->qiov->niov, + create, acb->aiocb_type); + if (ret < 0) { + error_report("add_aio_request is failed\n"); + free_aio_req(s, aio_req); + acb->ret = -EIO; + goto out; + } + done: + offset = 0; + idx++; + done += len; + } +out: + if (QLIST_EMPTY(&acb->aioreq_head)) { + sd_finish_aiocb(acb); + } +} + +static BlockDriverAIOCB *sd_aio_writev(BlockDriverState *bs, int64_t sector_num, + QEMUIOVector *qiov, int nb_sectors, + BlockDriverCompletionFunc *cb, + void *opaque) +{ + SheepdogAIOCB *acb; + + if (bs->growable && sector_num + nb_sectors > bs->total_sectors) { + /* TODO: shouldn't block here */ + if (sd_truncate(bs, (sector_num + nb_sectors) * SECTOR_SIZE) < 0) { + return NULL; + } + bs->total_sectors = sector_num + nb_sectors; + } + + acb = sd_aio_setup(bs, qiov, sector_num, nb_sectors, cb, opaque); + acb->aio_done_func = sd_write_done; + acb->aiocb_type = AIOCB_WRITE_UDATA; + + sd_schedule_bh(sd_readv_writev_bh_cb, acb); + return &acb->common; +} + +static BlockDriverAIOCB *sd_aio_readv(BlockDriverState *bs, int64_t sector_num, + QEMUIOVector *qiov, int nb_sectors, + BlockDriverCompletionFunc *cb, + void *opaque) +{ + SheepdogAIOCB *acb; + int i; + + acb = sd_aio_setup(bs, qiov, sector_num, nb_sectors, cb, opaque); + acb->aiocb_type = AIOCB_READ_UDATA; + acb->aio_done_func = sd_finish_aiocb; + + /* + * TODO: we can do better; we don't need to initialize + * blindly. + */ + for (i = 0; i < qiov->niov; i++) { + memset(qiov->iov[i].iov_base, 0, qiov->iov[i].iov_len); + } + + sd_schedule_bh(sd_readv_writev_bh_cb, acb); + return &acb->common; +} + +static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) +{ + BDRVSheepdogState *s = bs->opaque; + int ret, fd; + uint32_t new_vid; + SheepdogInode *inode; + unsigned int datalen; + + dprintf("sn_info: name %s id_str %s s: name %s vm_state_size %d " + "is_snapshot %d\n", sn_info->name, sn_info->id_str, + s->name, sn_info->vm_state_size, s->is_snapshot); + + if (s->is_snapshot) { + error_report("You can't create a snapshot of a snapshot VDI, " + "%s (%" PRIu32 ").\n", s->name, s->inode.vdi_id); + + return -EINVAL; + } + + dprintf("%s %s\n", sn_info->name, sn_info->id_str); + + s->inode.vm_state_size = sn_info->vm_state_size; + s->inode.vm_clock_nsec = sn_info->vm_clock_nsec; + strncpy(s->inode.tag, sn_info->name, sizeof(s->inode.tag)); + /* we don't need to update entire object */ + datalen = SD_INODE_SIZE - sizeof(s->inode.data_vdi_id); + + /* refresh inode. */ + fd = connect_to_sdog(s->addr, s->port); + if (fd < 0) { + ret = -EIO; + goto cleanup; + } + + ret = write_object(fd, (char *)&s->inode, vid_to_vdi_oid(s->inode.vdi_id), + s->inode.nr_copies, datalen, 0, 0); + if (ret < 0) { + error_report("failed to write snapshot's inode.\n"); + ret = -EIO; + goto cleanup; + } + + ret = do_sd_create(s->name, s->inode.vdi_size, s->inode.vdi_id, &new_vid, 1, + s->addr, s->port); + if (ret < 0) { + error_report("failed to create inode for snapshot. %s\n", + strerror(errno)); + ret = -EIO; + goto cleanup; + } + + inode = (SheepdogInode *)qemu_malloc(datalen); + + ret = read_object(fd, (char *)inode, vid_to_vdi_oid(new_vid), + s->inode.nr_copies, datalen, 0); + + if (ret < 0) { + error_report("failed to read new inode info. %s\n", strerror(errno)); + ret = -EIO; + goto cleanup; + } + + memcpy(&s->inode, inode, datalen); + dprintf("s->inode: name %s snap_id %x oid %x\n", + s->inode.name, s->inode.snap_id, s->inode.vdi_id); + +cleanup: + closesocket(fd); + return ret; +} + +static int sd_snapshot_goto(BlockDriverState *bs, const char *snapshot_id) +{ + BDRVSheepdogState *s = bs->opaque; + BDRVSheepdogState *old_s; + char vdi[SD_MAX_VDI_LEN], tag[SD_MAX_VDI_TAG_LEN]; + char *buf = NULL; + uint32_t vid; + uint32_t snapid = 0; + int ret = -ENOENT, fd; + + old_s = qemu_malloc(sizeof(BDRVSheepdogState)); + + memcpy(old_s, s, sizeof(BDRVSheepdogState)); + + memset(vdi, 0, sizeof(vdi)); + strncpy(vdi, s->name, sizeof(vdi)); + + memset(tag, 0, sizeof(tag)); + snapid = strtoul(snapshot_id, NULL, 10); + if (!snapid) { + strncpy(tag, s->name, sizeof(tag)); + } + + ret = find_vdi_name(s, vdi, snapid, tag, &vid, 1); + if (ret) { + error_report("Failed to find_vdi_name\n"); + ret = -ENOENT; + goto out; + } + + fd = connect_to_sdog(s->addr, s->port); + if (fd < 0) { + error_report("failed to connect\n"); + goto out; + } + + buf = qemu_malloc(SD_INODE_SIZE); + ret = read_object(fd, buf, vid_to_vdi_oid(vid), s->inode.nr_copies, + SD_INODE_SIZE, 0); + + closesocket(fd); + + if (ret) { + ret = -ENOENT; + goto out; + } + + memcpy(&s->inode, buf, sizeof(s->inode)); + + if (!s->inode.vm_state_size) { + error_report("Invalid snapshot\n"); + ret = -ENOENT; + goto out; + } + + s->is_snapshot = 1; + + qemu_free(buf); + qemu_free(old_s); + + return 0; +out: + /* recover bdrv_sd_state */ + memcpy(s, old_s, sizeof(BDRVSheepdogState)); + qemu_free(buf); + qemu_free(old_s); + + error_report("failed to open. recover old bdrv_sd_state.\n"); + + return ret; +} + +static int sd_snapshot_delete(BlockDriverState *bs, const char *snapshot_id) +{ + /* FIXME: Delete specified snapshot id. */ + return 0; +} + +#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) +#define BITS_PER_BYTE 8 +#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long)) +#define DECLARE_BITMAP(name,bits) \ + unsigned long name[BITS_TO_LONGS(bits)] + +#define BITS_PER_LONG (BITS_PER_BYTE * sizeof(long)) + +static inline int test_bit(unsigned int nr, const unsigned long *addr) +{ + return ((1UL << (nr % BITS_PER_LONG)) & + (((unsigned long *)addr)[nr / BITS_PER_LONG])) != 0; +} + +static int sd_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab) +{ + BDRVSheepdogState *s = bs->opaque; + SheepdogReq req; + int fd, nr = 1024, ret, max = BITS_TO_LONGS(SD_NR_VDIS) * sizeof(long); + QEMUSnapshotInfo *sn_tab = NULL; + unsigned wlen, rlen; + int found = 0; + static SheepdogInode inode; + unsigned long *vdi_inuse; + unsigned int start_nr; + uint64_t hval; + uint32_t vid; + + vdi_inuse = qemu_malloc(max); + + fd = connect_to_sdog(s->addr, s->port); + if (fd < 0) { + goto out; + } + + rlen = max; + wlen = 0; + + memset(&req, 0, sizeof(req)); + + req.opcode = SD_OP_READ_VDIS; + req.data_length = max; + + ret = do_req(fd, (SheepdogReq *)&req, vdi_inuse, &wlen, &rlen); + + closesocket(fd); + if (ret) { + goto out; + } + + sn_tab = qemu_mallocz(nr * sizeof(*sn_tab)); + + /* calculate a vdi id with hash function */ + hval = fnv_64a_buf(s->name, strlen(s->name), FNV1A_64_INIT); + start_nr = hval & (SD_NR_VDIS - 1); + + fd = connect_to_sdog(s->addr, s->port); + if (fd < 0) { + error_report("failed to connect\n"); + goto out; + } + + for (vid = start_nr; found < nr; vid = (vid + 1) % SD_NR_VDIS) { + if (!test_bit(vid, vdi_inuse)) { + break; + } + + /* we don't need to read entire object */ + ret = read_object(fd, (char *)&inode, vid_to_vdi_oid(vid), + 0, SD_INODE_SIZE - sizeof(inode.data_vdi_id), 0); + + if (ret) { + continue; + } + + if (!strcmp(inode.name, s->name) && is_snapshot(&inode)) { + sn_tab[found].date_sec = inode.snap_ctime >> 32; + sn_tab[found].date_nsec = inode.snap_ctime & 0xffffffff; + sn_tab[found].vm_state_size = inode.vm_state_size; + sn_tab[found].vm_clock_nsec = inode.vm_clock_nsec; + + snprintf(sn_tab[found].id_str, sizeof(sn_tab[found].id_str), "%u", + inode.snap_id); + strncpy(sn_tab[found].name, inode.tag, + MIN(sizeof(sn_tab[found].name), sizeof(inode.tag))); + found++; + } + } + + closesocket(fd); +out: + *psn_tab = sn_tab; + + qemu_free(vdi_inuse); + + return found; +} + +static int do_load_save_vmstate(BDRVSheepdogState *s, uint8_t *data, + int64_t pos, int size, int load) +{ + int fd, create; + int ret = 0; + unsigned int data_len; + uint64_t vmstate_oid; + uint32_t vdi_index; + uint64_t offset; + + fd = connect_to_sdog(s->addr, s->port); + if (fd < 0) { + ret = -EIO; + goto cleanup; + } + + while (size) { + vdi_index = pos / SD_DATA_OBJ_SIZE; + offset = pos % SD_DATA_OBJ_SIZE; + + data_len = MIN(size, SD_DATA_OBJ_SIZE); + + vmstate_oid = vid_to_vmstate_oid(s->inode.vdi_id, vdi_index); + + create = (offset == 0); + if (load) { + ret = read_object(fd, (char *)data, vmstate_oid, + s->inode.nr_copies, data_len, offset); + } else { + ret = write_object(fd, (char *)data, vmstate_oid, + s->inode.nr_copies, data_len, offset, create); + } + + if (ret < 0) { + error_report("failed to save vmstate %s\n", strerror(errno)); + ret = -EIO; + goto cleanup; + } + + pos += data_len; + size -= data_len; + ret += data_len; + } +cleanup: + closesocket(fd); + return ret; +} + +static int sd_save_vmstate(BlockDriverState *bs, const uint8_t *data, + int64_t pos, int size) +{ + BDRVSheepdogState *s = bs->opaque; + + return do_load_save_vmstate(s, (uint8_t *)data, pos, size, 0); +} + +static int sd_load_vmstate(BlockDriverState *bs, uint8_t *data, + int64_t pos, int size) +{ + BDRVSheepdogState *s = bs->opaque; + + return do_load_save_vmstate(s, data, pos, size, 1); +} + + +static QEMUOptionParameter sd_create_options[] = { + { + .name = BLOCK_OPT_SIZE, + .type = OPT_SIZE, + .help = "Virtual disk size" + }, + { + .name = BLOCK_OPT_BACKING_FILE, + .type = OPT_STRING, + .help = "File name of a base image" + }, + { NULL } +}; + +BlockDriver bdrv_sheepdog = { + .format_name = "sheepdog", + .protocol_name = "sheepdog", + .instance_size = sizeof(BDRVSheepdogState), + .bdrv_file_open = sd_open, + .bdrv_close = sd_close, + .bdrv_create = sd_create, + .bdrv_getlength = sd_getlength, + .bdrv_truncate = sd_truncate, + + .bdrv_aio_readv = sd_aio_readv, + .bdrv_aio_writev = sd_aio_writev, + + .bdrv_snapshot_create = sd_snapshot_create, + .bdrv_snapshot_goto = sd_snapshot_goto, + .bdrv_snapshot_delete = sd_snapshot_delete, + .bdrv_snapshot_list = sd_snapshot_list, + + .bdrv_save_vmstate = sd_save_vmstate, + .bdrv_load_vmstate = sd_load_vmstate, + + .create_options = sd_create_options, +}; + +static void bdrv_sheepdog_init(void) +{ + bdrv_register(&bdrv_sheepdog); +} +block_init(bdrv_sheepdog_init); diff -Nru qemu-kvm-0.12.5+noroms/block/vdi.c qemu-kvm-0.14.1/block/vdi.c --- qemu-kvm-0.12.5+noroms/block/vdi.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/block/vdi.c 2011-05-11 13:29:46.000000000 +0000 @@ -119,13 +119,13 @@ #if !defined(CONFIG_UUID) void uuid_generate(uuid_t out) { - memset(out, 0, sizeof(out)); + memset(out, 0, sizeof(uuid_t)); } int uuid_is_null(const uuid_t uu) { uuid_t null_uuid = { 0 }; - return memcmp(uu, null_uuid, sizeof(uu)) == 0; + return memcmp(uu, null_uuid, sizeof(uuid_t)) == 0; } void uuid_unparse(const uuid_t uu, char *out) @@ -186,7 +186,6 @@ } VdiHeader; typedef struct { - BlockDriverState *hd; /* The block map entries are little endian (even in memory). */ uint32_t *bmap; /* Size of block (bytes). */ @@ -291,11 +290,10 @@ } #endif -static int vdi_check(BlockDriverState *bs) +static int vdi_check(BlockDriverState *bs, BdrvCheckResult *res) { /* TODO: additional checks possible. */ BDRVVdiState *s = (BDRVVdiState *)bs->opaque; - int n_errors = 0; uint32_t blocks_allocated = 0; uint32_t block; uint32_t *bmap; @@ -315,11 +313,12 @@ } else { fprintf(stderr, "ERROR: block index %" PRIu32 " also used by %" PRIu32 "\n", bmap[bmap_entry], bmap_entry); + res->corruptions++; } } else { fprintf(stderr, "ERROR: block index %" PRIu32 " too large, is %" PRIu32 "\n", block, bmap_entry); - n_errors++; + res->corruptions++; } } } @@ -327,12 +326,12 @@ fprintf(stderr, "ERROR: allocated blocks mismatch, is %" PRIu32 ", should be %" PRIu32 "\n", blocks_allocated, s->header.blocks_allocated); - n_errors++; + res->corruptions++; } qemu_free(bmap); - return n_errors; + return 0; } static int vdi_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) @@ -376,21 +375,15 @@ return result; } -static int vdi_open(BlockDriverState *bs, const char *filename, int flags) +static int vdi_open(BlockDriverState *bs, int flags) { BDRVVdiState *s = bs->opaque; VdiHeader header; size_t bmap_size; - int ret; logout("\n"); - ret = bdrv_file_open(&s->hd, filename, flags); - if (ret < 0) { - return ret; - } - - if (bdrv_read(s->hd, 0, (uint8_t *)&header, 1) < 0) { + if (bdrv_read(bs->file, 0, (uint8_t *)&header, 1) < 0) { goto fail; } @@ -447,8 +440,10 @@ bmap_size = header.blocks_in_image * sizeof(uint32_t); bmap_size = (bmap_size + SECTOR_SIZE - 1) / SECTOR_SIZE; - s->bmap = qemu_malloc(bmap_size * SECTOR_SIZE); - if (bdrv_read(s->hd, s->bmap_sector, (uint8_t *)s->bmap, bmap_size) < 0) { + if (bmap_size > 0) { + s->bmap = qemu_malloc(bmap_size * SECTOR_SIZE); + } + if (bdrv_read(bs->file, s->bmap_sector, (uint8_t *)s->bmap, bmap_size) < 0) { goto fail_free_bmap; } @@ -458,7 +453,6 @@ qemu_free(s->bmap); fail: - bdrv_delete(s->hd); return -1; } @@ -482,7 +476,7 @@ static void vdi_aio_cancel(BlockDriverAIOCB *blockacb) { /* TODO: This code is untested. How can I get it executed? */ - VdiAIOCB *acb = (VdiAIOCB *)blockacb; + VdiAIOCB *acb = container_of(blockacb, VdiAIOCB, common); logout("\n"); if (acb->hd_aiocb) { bdrv_aio_cancel(acb->hd_aiocb); @@ -613,7 +607,7 @@ acb->hd_iov.iov_base = (void *)acb->buf; acb->hd_iov.iov_len = n_sectors * SECTOR_SIZE; qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1); - acb->hd_aiocb = bdrv_aio_readv(s->hd, offset, &acb->hd_qiov, + acb->hd_aiocb = bdrv_aio_readv(bs->file, offset, &acb->hd_qiov, n_sectors, vdi_aio_read_cb, acb); if (acb->hd_aiocb == NULL) { goto done; @@ -676,7 +670,7 @@ acb->hd_iov.iov_base = acb->block_buffer; acb->hd_iov.iov_len = SECTOR_SIZE; qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1); - acb->hd_aiocb = bdrv_aio_writev(s->hd, 0, &acb->hd_qiov, 1, + acb->hd_aiocb = bdrv_aio_writev(bs->file, 0, &acb->hd_qiov, 1, vdi_aio_write_cb, acb); if (acb->hd_aiocb == NULL) { goto done; @@ -705,7 +699,7 @@ qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1); logout("will write %u block map sectors starting from entry %u\n", n_sectors, bmap_first); - acb->hd_aiocb = bdrv_aio_writev(s->hd, offset, &acb->hd_qiov, + acb->hd_aiocb = bdrv_aio_writev(bs->file, offset, &acb->hd_qiov, n_sectors, vdi_aio_write_cb, acb); if (acb->hd_aiocb == NULL) { goto done; @@ -754,7 +748,7 @@ acb->hd_iov.iov_base = (void *)block; acb->hd_iov.iov_len = s->block_size; qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1); - acb->hd_aiocb = bdrv_aio_writev(s->hd, offset, + acb->hd_aiocb = bdrv_aio_writev(bs->file, offset, &acb->hd_qiov, s->block_sectors, vdi_aio_write_cb, acb); if (acb->hd_aiocb == NULL) { @@ -767,7 +761,7 @@ acb->hd_iov.iov_base = (void *)acb->buf; acb->hd_iov.iov_len = n_sectors * SECTOR_SIZE; qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1); - acb->hd_aiocb = bdrv_aio_writev(s->hd, offset, &acb->hd_qiov, + acb->hd_aiocb = bdrv_aio_writev(bs->file, offset, &acb->hd_qiov, n_sectors, vdi_aio_write_cb, acb); if (acb->hd_aiocb == NULL) { goto done; @@ -873,7 +867,10 @@ result = -errno; } - bmap = (uint32_t *)qemu_mallocz(bmap_size); + bmap = NULL; + if (bmap_size > 0) { + bmap = (uint32_t *)qemu_mallocz(bmap_size); + } for (i = 0; i < blocks; i++) { if (image_type == VDI_TYPE_STATIC) { bmap[i] = i; @@ -900,16 +897,12 @@ static void vdi_close(BlockDriverState *bs) { - BDRVVdiState *s = bs->opaque; - logout("\n"); - bdrv_delete(s->hd); } -static void vdi_flush(BlockDriverState *bs) +static int vdi_flush(BlockDriverState *bs) { - BDRVVdiState *s = bs->opaque; logout("\n"); - bdrv_flush(s->hd); + return bdrv_flush(bs->file); } diff -Nru qemu-kvm-0.12.5+noroms/block/vmdk.c qemu-kvm-0.14.1/block/vmdk.c --- qemu-kvm-0.12.5+noroms/block/vmdk.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/block/vmdk.c 2011-05-11 13:29:46.000000000 +0000 @@ -61,7 +61,6 @@ #define L2_CACHE_SIZE 16 typedef struct BDRVVmdkState { - BlockDriverState *hd; int64_t l1_table_offset; int64_t l1_backup_table_offset; uint32_t *l1_table; @@ -76,7 +75,6 @@ unsigned int cluster_sectors; uint32_t parent_cid; - int is_parent; } BDRVVmdkState; typedef struct VmdkMetaData { @@ -109,14 +107,13 @@ static uint32_t vmdk_read_cid(BlockDriverState *bs, int parent) { - BDRVVmdkState *s = bs->opaque; char desc[DESC_SIZE]; uint32_t cid; const char *p_name, *cid_str; size_t cid_str_size; /* the descriptor offset = 0x200 */ - if (bdrv_pread(s->hd, 0x200, desc, DESC_SIZE) != DESC_SIZE) + if (bdrv_pread(bs->file, 0x200, desc, DESC_SIZE) != DESC_SIZE) return 0; if (parent) { @@ -137,12 +134,11 @@ static int vmdk_write_cid(BlockDriverState *bs, uint32_t cid) { - BDRVVmdkState *s = bs->opaque; char desc[DESC_SIZE], tmp_desc[DESC_SIZE]; char *p_name, *tmp_str; /* the descriptor offset = 0x200 */ - if (bdrv_pread(s->hd, 0x200, desc, DESC_SIZE) != DESC_SIZE) + if (bdrv_pread(bs->file, 0x200, desc, DESC_SIZE) != DESC_SIZE) return -1; tmp_str = strstr(desc,"parentCID"); @@ -153,7 +149,7 @@ pstrcat(desc, sizeof(desc), tmp_desc); } - if (bdrv_pwrite_sync(s->hd, 0x200, desc, DESC_SIZE) < 0) + if (bdrv_pwrite_sync(bs->file, 0x200, desc, DESC_SIZE) < 0) return -1; return 0; } @@ -179,6 +175,7 @@ static int vmdk_snapshot_create(const char *filename, const char *backing_file) { int snp_fd, p_fd; + int ret; uint32_t p_cid; char *p_name, *gd_buf, *rgd_buf; const char *real_filename, *temp_str; @@ -203,34 +200,49 @@ snp_fd = open(filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE, 0644); if (snp_fd < 0) - return -1; + return -errno; p_fd = open(backing_file, O_RDONLY | O_BINARY | O_LARGEFILE); if (p_fd < 0) { close(snp_fd); - return -1; + return -errno; } /* read the header */ - if (lseek(p_fd, 0x0, SEEK_SET) == -1) + if (lseek(p_fd, 0x0, SEEK_SET) == -1) { + ret = -errno; goto fail; - if (read(p_fd, hdr, HEADER_SIZE) != HEADER_SIZE) + } + if (read(p_fd, hdr, HEADER_SIZE) != HEADER_SIZE) { + ret = -errno; goto fail; + } /* write the header */ - if (lseek(snp_fd, 0x0, SEEK_SET) == -1) + if (lseek(snp_fd, 0x0, SEEK_SET) == -1) { + ret = -errno; goto fail; - if (write(snp_fd, hdr, HEADER_SIZE) == -1) + } + if (write(snp_fd, hdr, HEADER_SIZE) == -1) { + ret = -errno; goto fail; + } memset(&header, 0, sizeof(header)); memcpy(&header,&hdr[4], sizeof(header)); // skip the VMDK4_MAGIC - ftruncate(snp_fd, header.grain_offset << 9); + if (ftruncate(snp_fd, header.grain_offset << 9)) { + ret = -errno; + goto fail; + } /* the descriptor offset = 0x200 */ - if (lseek(p_fd, 0x200, SEEK_SET) == -1) + if (lseek(p_fd, 0x200, SEEK_SET) == -1) { + ret = -errno; goto fail; - if (read(p_fd, p_desc, DESC_SIZE) != DESC_SIZE) + } + if (read(p_fd, p_desc, DESC_SIZE) != DESC_SIZE) { + ret = -errno; goto fail; + } if ((p_name = strstr(p_desc,"CID")) != NULL) { p_name += sizeof("CID"); @@ -249,10 +261,14 @@ (uint32_t)header.capacity, real_filename); /* write the descriptor */ - if (lseek(snp_fd, 0x200, SEEK_SET) == -1) + if (lseek(snp_fd, 0x200, SEEK_SET) == -1) { + ret = -errno; goto fail; - if (write(snp_fd, s_desc, strlen(s_desc)) == -1) + } + if (write(snp_fd, s_desc, strlen(s_desc)) == -1) { + ret = -errno; goto fail; + } gd_offset = header.gd_offset * SECTOR_SIZE; // offset of GD table rgd_offset = header.rgd_offset * SECTOR_SIZE; // offset of RGD table @@ -262,70 +278,73 @@ * 512 GTE per GT, each GTE points to grain */ gt_size = (int64_t)header.num_gtes_per_gte * header.granularity * SECTOR_SIZE; - if (!gt_size) + if (!gt_size) { + ret = -EINVAL; goto fail; + } gde_entries = (uint32_t)(capacity / gt_size); // number of gde/rgde gd_size = gde_entries * sizeof(uint32_t); /* write RGD */ rgd_buf = qemu_malloc(gd_size); - if (lseek(p_fd, rgd_offset, SEEK_SET) == -1) + if (lseek(p_fd, rgd_offset, SEEK_SET) == -1) { + ret = -errno; goto fail_rgd; - if (read(p_fd, rgd_buf, gd_size) != gd_size) + } + if (read(p_fd, rgd_buf, gd_size) != gd_size) { + ret = -errno; goto fail_rgd; - if (lseek(snp_fd, rgd_offset, SEEK_SET) == -1) + } + if (lseek(snp_fd, rgd_offset, SEEK_SET) == -1) { + ret = -errno; goto fail_rgd; - if (write(snp_fd, rgd_buf, gd_size) == -1) + } + if (write(snp_fd, rgd_buf, gd_size) == -1) { + ret = -errno; goto fail_rgd; + } /* write GD */ gd_buf = qemu_malloc(gd_size); - if (lseek(p_fd, gd_offset, SEEK_SET) == -1) + if (lseek(p_fd, gd_offset, SEEK_SET) == -1) { + ret = -errno; goto fail_gd; - if (read(p_fd, gd_buf, gd_size) != gd_size) + } + if (read(p_fd, gd_buf, gd_size) != gd_size) { + ret = -errno; goto fail_gd; - if (lseek(snp_fd, gd_offset, SEEK_SET) == -1) + } + if (lseek(snp_fd, gd_offset, SEEK_SET) == -1) { + ret = -errno; goto fail_gd; - if (write(snp_fd, gd_buf, gd_size) == -1) + } + if (write(snp_fd, gd_buf, gd_size) == -1) { + ret = -errno; goto fail_gd; - qemu_free(gd_buf); - qemu_free(rgd_buf); - - close(p_fd); - close(snp_fd); - return 0; + } + ret = 0; - fail_gd: +fail_gd: qemu_free(gd_buf); - fail_rgd: +fail_rgd: qemu_free(rgd_buf); - fail: +fail: close(p_fd); close(snp_fd); - return -1; + return ret; } -static void vmdk_parent_close(BlockDriverState *bs) +static int vmdk_parent_open(BlockDriverState *bs) { - if (bs->backing_hd) - bdrv_close(bs->backing_hd); -} - -static int parent_open = 0; -static int vmdk_parent_open(BlockDriverState *bs, const char * filename) -{ - BDRVVmdkState *s = bs->opaque; char *p_name; char desc[DESC_SIZE]; - char parent_img_name[1024]; /* the descriptor offset = 0x200 */ - if (bdrv_pread(s->hd, 0x200, desc, DESC_SIZE) != DESC_SIZE) + if (bdrv_pread(bs->file, 0x200, desc, DESC_SIZE) != DESC_SIZE) return -1; if ((p_name = strstr(desc,"parentFileNameHint")) != NULL) { char *end_name; - struct stat file_buf; p_name += sizeof("parentFileNameHint") + 1; if ((end_name = strchr(p_name,'\"')) == NULL) @@ -334,50 +353,25 @@ return -1; pstrcpy(bs->backing_file, end_name - p_name + 1, p_name); - if (stat(bs->backing_file, &file_buf) != 0) { - path_combine(parent_img_name, sizeof(parent_img_name), - filename, bs->backing_file); - } else { - pstrcpy(parent_img_name, sizeof(parent_img_name), - bs->backing_file); - } - - bs->backing_hd = bdrv_new(""); - if (!bs->backing_hd) { - failure: - bdrv_close(s->hd); - return -1; - } - parent_open = 1; - if (bdrv_open(bs->backing_hd, parent_img_name, BDRV_O_RDONLY) < 0) - goto failure; - parent_open = 0; } return 0; } -static int vmdk_open(BlockDriverState *bs, const char *filename, int flags) +static int vmdk_open(BlockDriverState *bs, int flags) { BDRVVmdkState *s = bs->opaque; uint32_t magic; - int l1_size, i, ret; + int l1_size, i; - if (parent_open) - // Parent must be opened as RO. - flags = BDRV_O_RDONLY; - - ret = bdrv_file_open(&s->hd, filename, flags); - if (ret < 0) - return ret; - if (bdrv_pread(s->hd, 0, &magic, sizeof(magic)) != sizeof(magic)) + if (bdrv_pread(bs->file, 0, &magic, sizeof(magic)) != sizeof(magic)) goto fail; magic = be32_to_cpu(magic); if (magic == VMDK3_MAGIC) { VMDK3Header header; - if (bdrv_pread(s->hd, sizeof(magic), &header, sizeof(header)) != sizeof(header)) + if (bdrv_pread(bs->file, sizeof(magic), &header, sizeof(header)) != sizeof(header)) goto fail; s->cluster_sectors = le32_to_cpu(header.granularity); s->l2_size = 1 << 9; @@ -389,7 +383,7 @@ } else if (magic == VMDK4_MAGIC) { VMDK4Header header; - if (bdrv_pread(s->hd, sizeof(magic), &header, sizeof(header)) != sizeof(header)) + if (bdrv_pread(bs->file, sizeof(magic), &header, sizeof(header)) != sizeof(header)) goto fail; bs->total_sectors = le64_to_cpu(header.capacity); s->cluster_sectors = le64_to_cpu(header.granularity); @@ -402,13 +396,8 @@ s->l1_table_offset = le64_to_cpu(header.rgd_offset) << 9; s->l1_backup_table_offset = le64_to_cpu(header.gd_offset) << 9; - if (parent_open) - s->is_parent = 1; - else - s->is_parent = 0; - // try to open parent images, if exist - if (vmdk_parent_open(bs, filename) != 0) + if (vmdk_parent_open(bs) != 0) goto fail; // write the CID once after the image creation s->parent_cid = vmdk_read_cid(bs,1); @@ -419,7 +408,7 @@ /* read the L1 table */ l1_size = s->l1_size * sizeof(uint32_t); s->l1_table = qemu_malloc(l1_size); - if (bdrv_pread(s->hd, s->l1_table_offset, s->l1_table, l1_size) != l1_size) + if (bdrv_pread(bs->file, s->l1_table_offset, s->l1_table, l1_size) != l1_size) goto fail; for(i = 0; i < s->l1_size; i++) { le32_to_cpus(&s->l1_table[i]); @@ -427,7 +416,7 @@ if (s->l1_backup_table_offset) { s->l1_backup_table = qemu_malloc(l1_size); - if (bdrv_pread(s->hd, s->l1_backup_table_offset, s->l1_backup_table, l1_size) != l1_size) + if (bdrv_pread(bs->file, s->l1_backup_table_offset, s->l1_backup_table, l1_size) != l1_size) goto fail; for(i = 0; i < s->l1_size; i++) { le32_to_cpus(&s->l1_backup_table[i]); @@ -440,7 +429,6 @@ qemu_free(s->l1_backup_table); qemu_free(s->l1_table); qemu_free(s->l2_cache); - bdrv_delete(s->hd); return -1; } @@ -468,7 +456,7 @@ } //Write grain only into the active image - ret = bdrv_write(s->hd, cluster_offset, whole_grain, + ret = bdrv_write(bs->file, cluster_offset, whole_grain, s->cluster_sectors); if (ret < 0) { return -1; @@ -482,13 +470,13 @@ BDRVVmdkState *s = bs->opaque; /* update L2 table */ - if (bdrv_pwrite_sync(s->hd, ((int64_t)m_data->l2_offset * 512) + (m_data->l2_index * sizeof(m_data->offset)), + if (bdrv_pwrite_sync(bs->file, ((int64_t)m_data->l2_offset * 512) + (m_data->l2_index * sizeof(m_data->offset)), &(m_data->offset), sizeof(m_data->offset)) < 0) return -1; /* update backup L2 table */ if (s->l1_backup_table_offset != 0) { m_data->l2_offset = s->l1_backup_table[m_data->l1_index]; - if (bdrv_pwrite_sync(s->hd, ((int64_t)m_data->l2_offset * 512) + (m_data->l2_index * sizeof(m_data->offset)), + if (bdrv_pwrite_sync(bs->file, ((int64_t)m_data->l2_offset * 512) + (m_data->l2_index * sizeof(m_data->offset)), &(m_data->offset), sizeof(m_data->offset)) < 0) return -1; } @@ -536,7 +524,7 @@ } } l2_table = s->l2_cache + (min_index * s->l2_size); - if (bdrv_pread(s->hd, (int64_t)l2_offset * 512, l2_table, s->l2_size * sizeof(uint32_t)) != + if (bdrv_pread(bs->file, (int64_t)l2_offset * 512, l2_table, s->l2_size * sizeof(uint32_t)) != s->l2_size * sizeof(uint32_t)) return 0; @@ -549,15 +537,15 @@ if (!cluster_offset) { if (!allocate) return 0; + // Avoid the L2 tables update for the images that have snapshots. - if (!s->is_parent) { - cluster_offset = bdrv_getlength(s->hd); - bdrv_truncate(s->hd, cluster_offset + (s->cluster_sectors << 9)); - - cluster_offset >>= 9; - tmp = cpu_to_le32(cluster_offset); - l2_table[l2_index] = tmp; - } + cluster_offset = bdrv_getlength(bs->file); + bdrv_truncate(bs->file, cluster_offset + (s->cluster_sectors << 9)); + + cluster_offset >>= 9; + tmp = cpu_to_le32(cluster_offset); + l2_table[l2_index] = tmp; + /* First of all we write grain itself, to avoid race condition * that may to corrupt the image. * This problem may occur because of insufficient space on host disk @@ -619,7 +607,7 @@ memset(buf, 0, 512 * n); } } else { - if(bdrv_pread(s->hd, cluster_offset + index_in_cluster * 512, buf, n * 512) != n * 512) + if(bdrv_pread(bs->file, cluster_offset + index_in_cluster * 512, buf, n * 512) != n * 512) return -1; } nb_sectors -= n; @@ -655,7 +643,7 @@ if (!cluster_offset) return -1; - if (bdrv_pwrite(s->hd, cluster_offset + index_in_cluster * 512, buf, n * 512) != n * 512) + if (bdrv_pwrite(bs->file, cluster_offset + index_in_cluster * 512, buf, n * 512) != n * 512) return -1; if (m_data.valid) { /* update L2 tables */ @@ -703,6 +691,7 @@ int64_t total_size = 0; const char *backing_file = NULL; int flags = 0; + int ret; // Read out options while (options && options->name) { @@ -724,7 +713,7 @@ fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE, 0644); if (fd < 0) - return -1; + return -errno; magic = cpu_to_be32(VMDK4_MAGIC); memset(&header, 0, sizeof(header)); header.version = cpu_to_le32(1); @@ -759,22 +748,44 @@ header.check_bytes[3] = 0xa; /* write all the data */ - write(fd, &magic, sizeof(magic)); - write(fd, &header, sizeof(header)); + ret = qemu_write_full(fd, &magic, sizeof(magic)); + if (ret != sizeof(magic)) { + ret = -errno; + goto exit; + } + ret = qemu_write_full(fd, &header, sizeof(header)); + if (ret != sizeof(header)) { + ret = -errno; + goto exit; + } - ftruncate(fd, header.grain_offset << 9); + ret = ftruncate(fd, header.grain_offset << 9); + if (ret < 0) { + ret = -errno; + goto exit; + } /* write grain directory */ lseek(fd, le64_to_cpu(header.rgd_offset) << 9, SEEK_SET); for (i = 0, tmp = header.rgd_offset + gd_size; - i < gt_count; i++, tmp += gt_size) - write(fd, &tmp, sizeof(tmp)); + i < gt_count; i++, tmp += gt_size) { + ret = qemu_write_full(fd, &tmp, sizeof(tmp)); + if (ret != sizeof(tmp)) { + ret = -errno; + goto exit; + } + } /* write backup grain directory */ lseek(fd, le64_to_cpu(header.gd_offset) << 9, SEEK_SET); for (i = 0, tmp = header.gd_offset + gd_size; - i < gt_count; i++, tmp += gt_size) - write(fd, &tmp, sizeof(tmp)); + i < gt_count; i++, tmp += gt_size) { + ret = qemu_write_full(fd, &tmp, sizeof(tmp)); + if (ret != sizeof(tmp)) { + ret = -errno; + goto exit; + } + } /* compose the descriptor */ real_filename = filename; @@ -791,10 +802,16 @@ /* write the descriptor */ lseek(fd, le64_to_cpu(header.desc_offset) << 9, SEEK_SET); - write(fd, desc, strlen(desc)); + ret = qemu_write_full(fd, desc, strlen(desc)); + if (ret != strlen(desc)) { + ret = -errno; + goto exit; + } + ret = 0; +exit: close(fd); - return 0; + return ret; } static void vmdk_close(BlockDriverState *bs) @@ -803,15 +820,11 @@ qemu_free(s->l1_table); qemu_free(s->l2_cache); - // try to close parent image, if exist - vmdk_parent_close(s->hd); - bdrv_delete(s->hd); } -static void vmdk_flush(BlockDriverState *bs) +static int vmdk_flush(BlockDriverState *bs) { - BDRVVmdkState *s = bs->opaque; - bdrv_flush(s->hd); + return bdrv_flush(bs->file); } @@ -838,7 +851,7 @@ .format_name = "vmdk", .instance_size = sizeof(BDRVVmdkState), .bdrv_probe = vmdk_probe, - .bdrv_open = vmdk_open, + .bdrv_open = vmdk_open, .bdrv_read = vmdk_read, .bdrv_write = vmdk_write, .bdrv_close = vmdk_close, diff -Nru qemu-kvm-0.12.5+noroms/block/vpc.c qemu-kvm-0.14.1/block/vpc.c --- qemu-kvm-0.12.5+noroms/block/vpc.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/block/vpc.c 2011-05-11 13:29:46.000000000 +0000 @@ -110,8 +110,6 @@ }; typedef struct BDRVVPCState { - BlockDriverState *hd; - uint8_t footer_buf[HEADER_SIZE]; uint64_t free_data_block_offset; int max_table_entries; @@ -150,20 +148,16 @@ return 0; } -static int vpc_open(BlockDriverState *bs, const char *filename, int flags) +static int vpc_open(BlockDriverState *bs, int flags) { BDRVVPCState *s = bs->opaque; - int ret, i; + int i; struct vhd_footer* footer; struct vhd_dyndisk_header* dyndisk_header; uint8_t buf[HEADER_SIZE]; uint32_t checksum; - ret = bdrv_file_open(&s->hd, filename, flags); - if (ret < 0) - return ret; - - if (bdrv_pread(s->hd, 0, s->footer_buf, HEADER_SIZE) != HEADER_SIZE) + if (bdrv_pread(bs->file, 0, s->footer_buf, HEADER_SIZE) != HEADER_SIZE) goto fail; footer = (struct vhd_footer*) s->footer_buf; @@ -174,7 +168,7 @@ footer->checksum = 0; if (vpc_checksum(s->footer_buf, HEADER_SIZE) != checksum) fprintf(stderr, "block-vpc: The header checksum of '%s' is " - "incorrect.\n", filename); + "incorrect.\n", bs->filename); // The visible size of a image in Virtual PC depends on the geometry // rather than on the size stored in the footer (the size in the footer @@ -182,7 +176,7 @@ bs->total_sectors = (int64_t) be16_to_cpu(footer->cyls) * footer->heads * footer->secs_per_cyl; - if (bdrv_pread(s->hd, be64_to_cpu(footer->data_offset), buf, HEADER_SIZE) + if (bdrv_pread(bs->file, be64_to_cpu(footer->data_offset), buf, HEADER_SIZE) != HEADER_SIZE) goto fail; @@ -199,7 +193,7 @@ s->pagetable = qemu_malloc(s->max_table_entries * 4); s->bat_offset = be64_to_cpu(dyndisk_header->table_offset); - if (bdrv_pread(s->hd, s->bat_offset, s->pagetable, + if (bdrv_pread(bs->file, s->bat_offset, s->pagetable, s->max_table_entries * 4) != s->max_table_entries * 4) goto fail; @@ -228,7 +222,6 @@ return 0; fail: - bdrv_delete(s->hd); return -1; } @@ -266,7 +259,7 @@ s->last_bitmap_offset = bitmap_offset; memset(bitmap, 0xff, s->bitmap_size); - bdrv_pwrite_sync(s->hd, bitmap_offset, bitmap, s->bitmap_size); + bdrv_pwrite_sync(bs->file, bitmap_offset, bitmap, s->bitmap_size); } // printf("sector: %" PRIx64 ", index: %x, offset: %x, bioff: %" PRIx64 ", bloff: %" PRIx64 "\n", @@ -316,7 +309,7 @@ BDRVVPCState *s = bs->opaque; int64_t offset = s->free_data_block_offset; - ret = bdrv_pwrite_sync(s->hd, offset, s->footer_buf, HEADER_SIZE); + ret = bdrv_pwrite_sync(bs->file, offset, s->footer_buf, HEADER_SIZE); if (ret < 0) return ret; @@ -351,7 +344,7 @@ // Initialize the block's bitmap memset(bitmap, 0xff, s->bitmap_size); - bdrv_pwrite_sync(s->hd, s->free_data_block_offset, bitmap, + bdrv_pwrite_sync(bs->file, s->free_data_block_offset, bitmap, s->bitmap_size); // Write new footer (the old one will be overwritten) @@ -363,7 +356,7 @@ // Write BAT entry to disk bat_offset = s->bat_offset + (4 * index); bat_value = be32_to_cpu(s->pagetable[index]); - ret = bdrv_pwrite_sync(s->hd, bat_offset, &bat_value, 4); + ret = bdrv_pwrite_sync(bs->file, bat_offset, &bat_value, 4); if (ret < 0) goto fail; @@ -380,21 +373,30 @@ BDRVVPCState *s = bs->opaque; int ret; int64_t offset; + int64_t sectors, sectors_per_block; while (nb_sectors > 0) { offset = get_sector_offset(bs, sector_num, 0); + sectors_per_block = s->block_size >> BDRV_SECTOR_BITS; + sectors = sectors_per_block - (sector_num % sectors_per_block); + if (sectors > nb_sectors) { + sectors = nb_sectors; + } + if (offset == -1) { - memset(buf, 0, 512); + memset(buf, 0, sectors * BDRV_SECTOR_SIZE); } else { - ret = bdrv_pread(s->hd, offset, buf, 512); - if (ret != 512) + ret = bdrv_pread(bs->file, offset, buf, + sectors * BDRV_SECTOR_SIZE); + if (ret != sectors * BDRV_SECTOR_SIZE) { return -1; + } } - nb_sectors--; - sector_num++; - buf += 512; + nb_sectors -= sectors; + sector_num += sectors; + buf += sectors * BDRV_SECTOR_SIZE; } return 0; } @@ -404,29 +406,41 @@ { BDRVVPCState *s = bs->opaque; int64_t offset; + int64_t sectors, sectors_per_block; int ret; while (nb_sectors > 0) { offset = get_sector_offset(bs, sector_num, 1); + sectors_per_block = s->block_size >> BDRV_SECTOR_BITS; + sectors = sectors_per_block - (sector_num % sectors_per_block); + if (sectors > nb_sectors) { + sectors = nb_sectors; + } + if (offset == -1) { offset = alloc_block(bs, sector_num); if (offset < 0) return -1; } - ret = bdrv_pwrite(s->hd, offset, buf, 512); - if (ret != 512) + ret = bdrv_pwrite(bs->file, offset, buf, sectors * BDRV_SECTOR_SIZE); + if (ret != sectors * BDRV_SECTOR_SIZE) { return -1; + } - nb_sectors--; - sector_num++; - buf += 512; + nb_sectors -= sectors; + sector_num += sectors; + buf += sectors * BDRV_SECTOR_SIZE; } return 0; } +static int vpc_flush(BlockDriverState *bs) +{ + return bdrv_flush(bs->file); +} /* * Calculates the number of cylinders, heads and sectors per cylinder @@ -488,6 +502,7 @@ uint8_t secs_per_cyl = 0; size_t block_size, num_bat_entries; int64_t total_sectors = 0; + int ret = -EIO; // Read out options while (options && options->name) { @@ -507,7 +522,8 @@ for (i = 0; total_sectors > (int64_t)cyls * heads * secs_per_cyl; i++) { if (calculate_geometry(total_sectors + i, &cyls, &heads, &secs_per_cyl)) { - return -EFBIG; + ret = -EFBIG; + goto fail; } } total_sectors = (int64_t) cyls * heads * secs_per_cyl; @@ -546,22 +562,28 @@ block_size = 0x200000; num_bat_entries = (total_sectors + block_size / 512) / (block_size / 512); - if (write(fd, buf, HEADER_SIZE) != HEADER_SIZE) - return -EIO; + if (write(fd, buf, HEADER_SIZE) != HEADER_SIZE) { + goto fail; + } - if (lseek(fd, 1536 + ((num_bat_entries * 4 + 511) & ~511), SEEK_SET) < 0) - return -EIO; - if (write(fd, buf, HEADER_SIZE) != HEADER_SIZE) - return -EIO; + if (lseek(fd, 1536 + ((num_bat_entries * 4 + 511) & ~511), SEEK_SET) < 0) { + goto fail; + } + if (write(fd, buf, HEADER_SIZE) != HEADER_SIZE) { + goto fail; + } // Write the initial BAT - if (lseek(fd, 3 * 512, SEEK_SET) < 0) - return -EIO; + if (lseek(fd, 3 * 512, SEEK_SET) < 0) { + goto fail; + } memset(buf, 0xFF, 512); - for (i = 0; i < (num_bat_entries * 4 + 511) / 512; i++) - if (write(fd, buf, 512) != 512) - return -EIO; + for (i = 0; i < (num_bat_entries * 4 + 511) / 512; i++) { + if (write(fd, buf, 512) != 512) { + goto fail; + } + } // Prepare the Dynamic Disk Header @@ -578,13 +600,18 @@ dyndisk_header->checksum = be32_to_cpu(vpc_checksum(buf, 1024)); // Write the header - if (lseek(fd, 512, SEEK_SET) < 0) - return -EIO; - if (write(fd, buf, 1024) != 1024) - return -EIO; + if (lseek(fd, 512, SEEK_SET) < 0) { + goto fail; + } + + if (write(fd, buf, 1024) != 1024) { + goto fail; + } + ret = 0; + fail: close(fd); - return 0; + return ret; } static void vpc_close(BlockDriverState *bs) @@ -594,7 +621,6 @@ #ifdef CACHE qemu_free(s->pageentry_u8); #endif - bdrv_delete(s->hd); } static QEMUOptionParameter vpc_create_options[] = { @@ -607,14 +633,15 @@ }; static BlockDriver bdrv_vpc = { - .format_name = "vpc", - .instance_size = sizeof(BDRVVPCState), - .bdrv_probe = vpc_probe, - .bdrv_open = vpc_open, - .bdrv_read = vpc_read, - .bdrv_write = vpc_write, - .bdrv_close = vpc_close, - .bdrv_create = vpc_create, + .format_name = "vpc", + .instance_size = sizeof(BDRVVPCState), + .bdrv_probe = vpc_probe, + .bdrv_open = vpc_open, + .bdrv_read = vpc_read, + .bdrv_write = vpc_write, + .bdrv_flush = vpc_flush, + .bdrv_close = vpc_close, + .bdrv_create = vpc_create, .create_options = vpc_create_options, }; diff -Nru qemu-kvm-0.12.5+noroms/block/vvfat.c qemu-kvm-0.14.1/block/vvfat.c --- qemu-kvm-0.12.5+noroms/block/vvfat.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/block/vvfat.c 2011-05-11 13:29:46.000000000 +0000 @@ -512,7 +512,7 @@ for(i=0;i<11;i++) { unsigned char c; - c = (i <= 8) ? entry->name[i] : entry->extension[i-8]; + c = (i < 8) ? entry->name[i] : entry->extension[i-8]; chksum=(((chksum&0xfe)>>1)|((chksum&0x01)?0x80:0)) + c; } @@ -756,6 +756,7 @@ if (st.st_size > 0x7fffffff) { fprintf(stderr, "File %s is larger than 2GB\n", buffer); free(buffer); + closedir(dir); return -2; } direntry->size=cpu_to_le32(S_ISDIR(st.st_mode)?0:st.st_size); @@ -1099,8 +1100,8 @@ */ static inline int find_mapping_for_cluster_aux(BDRVVVFATState* s,int cluster_num,int index1,int index2) { - int index3=index1+1; while(1) { + int index3; mapping_t* mapping; index3=(index1+index2)/2; mapping=array_get(&(s->mapping),index3); @@ -1244,7 +1245,7 @@ int j = 0; char buffer[1024]; - fprintf(stderr, "direntry 0x%x: ", (int)direntry); + fprintf(stderr, "direntry %p: ", direntry); if(!direntry) return; if(is_long_name(direntry)) { @@ -1273,7 +1274,11 @@ static void print_mapping(const mapping_t* mapping) { - fprintf(stderr, "mapping (0x%x): begin, end = %d, %d, dir_index = %d, first_mapping_index = %d, name = %s, mode = 0x%x, " , (int)mapping, mapping->begin, mapping->end, mapping->dir_index, mapping->first_mapping_index, mapping->path, mapping->mode); + fprintf(stderr, "mapping (%p): begin, end = %d, %d, dir_index = %d, " + "first_mapping_index = %d, name = %s, mode = 0x%x, " , + mapping, mapping->begin, mapping->end, mapping->dir_index, + mapping->first_mapping_index, mapping->path, mapping->mode); + if (mapping->mode & MODE_DIRECTORY) fprintf(stderr, "parent_mapping_index = %d, first_dir_index = %d\n", mapping->info.dir.parent_mapping_index, mapping->info.dir.first_dir_index); else @@ -1638,7 +1643,7 @@ /* new file */ schedule_new_file(s, qemu_strdup(path), cluster_num); else { - assert(0); + abort(); return 0; } } @@ -1659,7 +1664,7 @@ if (offset != mapping->info.file.offset + s->cluster_size * (cluster_num - mapping->begin)) { /* offset of this cluster in file chain has changed */ - assert(0); + abort(); copy_it = 1; } else if (offset == 0) { const char* basename = get_basename(mapping->path); @@ -1671,7 +1676,7 @@ if (mapping->first_mapping_index != first_mapping_index && mapping->info.file.offset > 0) { - assert(0); + abort(); copy_it = 1; } @@ -1837,7 +1842,7 @@ goto fail; } } else - assert(0); /* cluster_count = 0; */ + abort(); /* cluster_count = 0; */ ret += cluster_count; } @@ -2278,7 +2283,6 @@ fprintf(stderr, "deleted\n"); continue; } - assert(mapping->dir_index >= 0); assert(mapping->dir_index < s->directory.next); direntry_t* direntry = array_get(&(s->directory), mapping->dir_index); assert(mapping->begin == begin_of_direntry(direntry) || mapping->first_mapping_index >= 0); @@ -2458,14 +2462,17 @@ commit_t* commit = array_get(&(s->commits), i); switch(commit->action) { case ACTION_RENAME: case ACTION_MKDIR: - assert(0); + abort(); fail = -2; break; case ACTION_WRITEOUT: { +#ifndef NDEBUG + /* these variables are only used by assert() below */ direntry_t* entry = array_get(&(s->directory), commit->param.writeout.dir_index); uint32_t begin = begin_of_direntry(entry); mapping_t* mapping = find_mapping_for_cluster(s, begin); +#endif assert(mapping); assert(mapping->begin == begin); @@ -2516,7 +2523,7 @@ break; } default: - assert(0); + abort(); } } if (i > 0 && array_remove_slice(&(s->commits), 0, i)) @@ -2604,7 +2611,7 @@ ret = handle_renames_and_mkdirs(s); if (ret) { fprintf(stderr, "Error handling renames (%d)\n", ret); - assert(0); + abort(); return ret; } @@ -2615,21 +2622,21 @@ ret = commit_direntries(s, 0, -1); if (ret) { fprintf(stderr, "Fatal: error while committing (%d)\n", ret); - assert(0); + abort(); return ret; } ret = handle_commits(s); if (ret) { fprintf(stderr, "Error handling commits (%d)\n", ret); - assert(0); + abort(); return ret; } ret = handle_deletes(s); if (ret) { fprintf(stderr, "Error deleting\n"); - assert(0); + abort(); return ret; } @@ -2658,6 +2665,11 @@ DLOG(checkpoint()); + /* Check if we're operating in read-only mode */ + if (s->qcow == NULL) { + return -EACCES; + } + vvfat_close_current_file(s); /* @@ -2756,12 +2768,12 @@ static int write_target_commit(BlockDriverState *bs, int64_t sector_num, const uint8_t* buffer, int nb_sectors) { - BDRVVVFATState* s = bs->opaque; + BDRVVVFATState* s = *((BDRVVVFATState**) bs->opaque); return try_commit(s); } static void write_target_close(BlockDriverState *bs) { - BDRVVVFATState* s = bs->opaque; + BDRVVVFATState* s = *((BDRVVVFATState**) bs->opaque); bdrv_delete(s->qcow); free(s->qcow_filename); } @@ -2776,6 +2788,7 @@ { BlockDriver *bdrv_qcow; QEMUOptionParameter *options; + int ret; int size = sector2cluster(s, s->sector_count); s->used_clusters = calloc(size, 1); @@ -2791,9 +2804,17 @@ if (bdrv_create(bdrv_qcow, s->qcow_filename, options) < 0) return -1; + s->qcow = bdrv_new(""); - if (s->qcow == NULL || bdrv_open(s->qcow, s->qcow_filename, 0) < 0) - return -1; + if (s->qcow == NULL) { + return -1; + } + + ret = bdrv_open(s->qcow, s->qcow_filename, + BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH, bdrv_qcow); + if (ret < 0) { + return ret; + } #ifndef _WIN32 unlink(s->qcow_filename); @@ -2801,7 +2822,8 @@ s->bs->backing_hd = calloc(sizeof(BlockDriverState), 1); s->bs->backing_hd->drv = &vvfat_write_target; - s->bs->backing_hd->opaque = s; + s->bs->backing_hd->opaque = qemu_malloc(sizeof(void*)); + *(void**)s->bs->backing_hd->opaque = s; return 0; } @@ -2821,7 +2843,7 @@ static BlockDriver bdrv_vvfat = { .format_name = "vvfat", .instance_size = sizeof(BDRVVVFATState), - .bdrv_open = vvfat_open, + .bdrv_file_open = vvfat_open, .bdrv_read = vvfat_read, .bdrv_write = vvfat_write, .bdrv_close = vvfat_close, @@ -2859,7 +2881,7 @@ return; /* avoid compiler warnings: */ hexdump(NULL, 100); - remove_mapping(vvv, NULL); + remove_mapping(vvv, 0); print_mapping(NULL); print_direntry(NULL); } diff -Nru qemu-kvm-0.12.5+noroms/block.c qemu-kvm-0.14.1/block.c --- qemu-kvm-0.12.5+noroms/block.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/block.c 2011-05-11 13:29:46.000000000 +0000 @@ -23,6 +23,7 @@ */ #include "config-host.h" #include "qemu-common.h" +#include "trace.h" #include "monitor.h" #include "block_int.h" #include "module.h" @@ -50,18 +51,58 @@ BlockDriverCompletionFunc *cb, void *opaque); static BlockDriverAIOCB *bdrv_aio_flush_em(BlockDriverState *bs, BlockDriverCompletionFunc *cb, void *opaque); +static BlockDriverAIOCB *bdrv_aio_noop_em(BlockDriverState *bs, + BlockDriverCompletionFunc *cb, void *opaque); static int bdrv_read_em(BlockDriverState *bs, int64_t sector_num, uint8_t *buf, int nb_sectors); static int bdrv_write_em(BlockDriverState *bs, int64_t sector_num, const uint8_t *buf, int nb_sectors); -BlockDriverState *bdrv_first; +static QTAILQ_HEAD(, BlockDriverState) bdrv_states = + QTAILQ_HEAD_INITIALIZER(bdrv_states); + +static QLIST_HEAD(, BlockDriver) bdrv_drivers = + QLIST_HEAD_INITIALIZER(bdrv_drivers); -static BlockDriver *first_drv; +/* The device to use for VM snapshots */ +static BlockDriverState *bs_snapshots; /* If non-zero, use only whitelisted block drivers */ static int use_bdrv_whitelist; +#ifdef _WIN32 +static int is_windows_drive_prefix(const char *filename) +{ + return (((filename[0] >= 'a' && filename[0] <= 'z') || + (filename[0] >= 'A' && filename[0] <= 'Z')) && + filename[1] == ':'); +} + +int is_windows_drive(const char *filename) +{ + if (is_windows_drive_prefix(filename) && + filename[2] == '\0') + return 1; + if (strstart(filename, "\\\\.\\", NULL) || + strstart(filename, "//./", NULL)) + return 1; + return 0; +} +#endif + +/* check if the path starts with ":" */ +static int path_has_protocol(const char *path) +{ +#ifdef _WIN32 + if (is_windows_drive(path) || + is_windows_drive_prefix(path)) { + return 0; + } +#endif + + return strchr(path, ':') != NULL; +} + int path_is_absolute(const char *path) { const char *p; @@ -141,23 +182,18 @@ if (!bdrv->bdrv_aio_flush) bdrv->bdrv_aio_flush = bdrv_aio_flush_em; - bdrv->next = first_drv; - first_drv = bdrv; + QLIST_INSERT_HEAD(&bdrv_drivers, bdrv, list); } /* create a new block device (by default it is empty) */ BlockDriverState *bdrv_new(const char *device_name) { - BlockDriverState **pbs, *bs; + BlockDriverState *bs; bs = qemu_mallocz(sizeof(BlockDriverState)); pstrcpy(bs->device_name, sizeof(bs->device_name), device_name); if (device_name[0] != '\0') { - /* insert at the end */ - pbs = &bdrv_first; - while (*pbs != NULL) - pbs = &(*pbs)->next; - *pbs = bs; + QTAILQ_INSERT_TAIL(&bdrv_states, bs, list); } return bs; } @@ -165,9 +201,10 @@ BlockDriver *bdrv_find_format(const char *format_name) { BlockDriver *drv1; - for(drv1 = first_drv; drv1 != NULL; drv1 = drv1->next) { - if (!strcmp(drv1->format_name, format_name)) + QLIST_FOREACH(drv1, &bdrv_drivers, list) { + if (!strcmp(drv1->format_name, format_name)) { return drv1; + } } return NULL; } @@ -205,6 +242,18 @@ return drv->bdrv_create(filename, options); } +int bdrv_create_file(const char* filename, QEMUOptionParameter *options) +{ + BlockDriver *drv; + + drv = bdrv_find_protocol(filename); + if (drv == NULL) { + return -ENOENT; + } + + return bdrv_create(drv, filename, options); +} + #ifdef _WIN32 void get_tmp_filename(char *filename, int size) { @@ -228,99 +277,102 @@ } #endif -#ifdef _WIN32 -static int is_windows_drive_prefix(const char *filename) +/* + * Detect host devices. By convention, /dev/cdrom[N] is always + * recognized as a host CDROM. + */ +static BlockDriver *find_hdev_driver(const char *filename) { - return (((filename[0] >= 'a' && filename[0] <= 'z') || - (filename[0] >= 'A' && filename[0] <= 'Z')) && - filename[1] == ':'); -} + int score_max = 0, score; + BlockDriver *drv = NULL, *d; -int is_windows_drive(const char *filename) -{ - if (is_windows_drive_prefix(filename) && - filename[2] == '\0') - return 1; - if (strstart(filename, "\\\\.\\", NULL) || - strstart(filename, "//./", NULL)) - return 1; - return 0; + QLIST_FOREACH(d, &bdrv_drivers, list) { + if (d->bdrv_probe_device) { + score = d->bdrv_probe_device(filename); + if (score > score_max) { + score_max = score; + drv = d; + } + } + } + + return drv; } -#endif -static BlockDriver *find_protocol(const char *filename) +BlockDriver *bdrv_find_protocol(const char *filename) { BlockDriver *drv1; char protocol[128]; int len; const char *p; -#ifdef _WIN32 - if (is_windows_drive(filename) || - is_windows_drive_prefix(filename)) - return bdrv_find_format("raw"); -#endif + /* TODO Drivers without bdrv_file_open must be specified explicitly */ + + /* + * XXX(hch): we really should not let host device detection + * override an explicit protocol specification, but moving this + * later breaks access to device names with colons in them. + * Thanks to the brain-dead persistent naming schemes on udev- + * based Linux systems those actually are quite common. + */ + drv1 = find_hdev_driver(filename); + if (drv1) { + return drv1; + } + + if (!path_has_protocol(filename)) { + return bdrv_find_format("file"); + } p = strchr(filename, ':'); - if (!p) - return bdrv_find_format("raw"); + assert(p != NULL); len = p - filename; if (len > sizeof(protocol) - 1) len = sizeof(protocol) - 1; memcpy(protocol, filename, len); protocol[len] = '\0'; - for(drv1 = first_drv; drv1 != NULL; drv1 = drv1->next) { + QLIST_FOREACH(drv1, &bdrv_drivers, list) { if (drv1->protocol_name && - !strcmp(drv1->protocol_name, protocol)) + !strcmp(drv1->protocol_name, protocol)) { return drv1; - } - return NULL; -} - -/* - * Detect host devices. By convention, /dev/cdrom[N] is always - * recognized as a host CDROM. - */ -static BlockDriver *find_hdev_driver(const char *filename) -{ - int score_max = 0, score; - BlockDriver *drv = NULL, *d; - - for (d = first_drv; d; d = d->next) { - if (d->bdrv_probe_device) { - score = d->bdrv_probe_device(filename); - if (score > score_max) { - score_max = score; - drv = d; - } } } - - return drv; + return NULL; } -static BlockDriver *find_image_format(const char *filename) +static int find_image_format(const char *filename, BlockDriver **pdrv) { int ret, score, score_max; BlockDriver *drv1, *drv; uint8_t buf[2048]; BlockDriverState *bs; - drv = find_protocol(filename); - /* no need to test disk image formats for vvfat */ - if (drv && strcmp(drv->format_name, "vvfat") == 0) - return drv; + ret = bdrv_file_open(&bs, filename, 0); + if (ret < 0) { + *pdrv = NULL; + return ret; + } + + /* Return the raw BlockDriver * to scsi-generic devices or empty drives */ + if (bs->sg || !bdrv_is_inserted(bs)) { + bdrv_delete(bs); + drv = bdrv_find_format("raw"); + if (!drv) { + ret = -ENOENT; + } + *pdrv = drv; + return ret; + } - ret = bdrv_file_open(&bs, filename, BDRV_O_RDONLY); - if (ret < 0) - return NULL; ret = bdrv_pread(bs, 0, buf, sizeof(buf)); bdrv_delete(bs); if (ret < 0) { - return NULL; + *pdrv = NULL; + return ret; } score_max = 0; - for(drv1 = first_drv; drv1 != NULL; drv1 = drv1->next) { + drv = NULL; + QLIST_FOREACH(drv1, &bdrv_drivers, list) { if (drv1->bdrv_probe) { score = drv1->bdrv_probe(buf, ret, filename); if (score > score_max) { @@ -329,16 +381,141 @@ } } } - return drv; + if (!drv) { + ret = -ENOENT; + } + *pdrv = drv; + return ret; +} + +/** + * Set the current 'total_sectors' value + */ +static int refresh_total_sectors(BlockDriverState *bs, int64_t hint) +{ + BlockDriver *drv = bs->drv; + + /* Do not attempt drv->bdrv_getlength() on scsi-generic devices */ + if (bs->sg) + return 0; + + /* query actual device if possible, otherwise just trust the hint */ + if (drv->bdrv_getlength) { + int64_t length = drv->bdrv_getlength(bs); + if (length < 0) { + return length; + } + hint = length >> BDRV_SECTOR_BITS; + } + + bs->total_sectors = hint; + return 0; +} + +/* + * Common part for opening disk images and files + */ +static int bdrv_open_common(BlockDriverState *bs, const char *filename, + int flags, BlockDriver *drv) +{ + int ret, open_flags; + + assert(drv != NULL); + + bs->file = NULL; + bs->total_sectors = 0; + bs->encrypted = 0; + bs->valid_key = 0; + bs->open_flags = flags; + /* buffer_alignment defaulted to 512, drivers can change this value */ + bs->buffer_alignment = 512; + + pstrcpy(bs->filename, sizeof(bs->filename), filename); + + if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv)) { + return -ENOTSUP; + } + + bs->drv = drv; + bs->opaque = qemu_mallocz(drv->instance_size); + + /* + * Yes, BDRV_O_NOCACHE aka O_DIRECT means we have to present a + * write cache to the guest. We do need the fdatasync to flush + * out transactions for block allocations, and we maybe have a + * volatile write cache in our backing device to deal with. + */ + if (flags & (BDRV_O_CACHE_WB|BDRV_O_NOCACHE)) + bs->enable_write_cache = 1; + + /* + * Clear flags that are internal to the block layer before opening the + * image. + */ + open_flags = flags & ~(BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING); + + /* + * Snapshots should be writeable. + */ + if (bs->is_temporary) { + open_flags |= BDRV_O_RDWR; + } + + /* Open the image, either directly or using a protocol */ + if (drv->bdrv_file_open) { + ret = drv->bdrv_file_open(bs, filename, open_flags); + } else { + ret = bdrv_file_open(&bs->file, filename, open_flags); + if (ret >= 0) { + ret = drv->bdrv_open(bs, open_flags); + } + } + + if (ret < 0) { + goto free_and_fail; + } + + bs->keep_read_only = bs->read_only = !(open_flags & BDRV_O_RDWR); + + ret = refresh_total_sectors(bs, bs->total_sectors); + if (ret < 0) { + goto free_and_fail; + } + +#ifndef _WIN32 + if (bs->is_temporary) { + unlink(filename); + } +#endif + return 0; + +free_and_fail: + if (bs->file) { + bdrv_delete(bs->file); + bs->file = NULL; + } + qemu_free(bs->opaque); + bs->opaque = NULL; + bs->drv = NULL; + return ret; } +/* + * Opens a file using a protocol (file, host_device, nbd, ...) + */ int bdrv_file_open(BlockDriverState **pbs, const char *filename, int flags) { BlockDriverState *bs; + BlockDriver *drv; int ret; + drv = bdrv_find_protocol(filename); + if (!drv) { + return -ENOENT; + } + bs = bdrv_new(""); - ret = bdrv_open2(bs, filename, flags | BDRV_O_FILE, NULL); + ret = bdrv_open_common(bs, filename, flags, drv); if (ret < 0) { bdrv_delete(bs); return ret; @@ -348,23 +525,13 @@ return 0; } -int bdrv_open(BlockDriverState *bs, const char *filename, int flags) -{ - return bdrv_open2(bs, filename, flags, NULL); -} - -int bdrv_open2(BlockDriverState *bs, const char *filename, int flags, - BlockDriver *drv) +/* + * Opens a disk image (raw, qcow2, vmdk, ...) + */ +int bdrv_open(BlockDriverState *bs, const char *filename, int flags, + BlockDriver *drv) { - int ret, open_flags, try_rw; - char tmp_filename[PATH_MAX]; - char backing_filename[PATH_MAX]; - - bs->is_temporary = 0; - bs->encrypted = 0; - bs->valid_key = 0; - /* buffer_alignment defaulted to 512, drivers can change this value */ - bs->buffer_alignment = 512; + int ret; if (flags & BDRV_O_SNAPSHOT) { BlockDriverState *bs1; @@ -372,18 +539,20 @@ int is_protocol = 0; BlockDriver *bdrv_qcow2; QEMUOptionParameter *options; + char tmp_filename[PATH_MAX]; + char backing_filename[PATH_MAX]; /* if snapshot, we create a temporary backing file and open it instead of opening 'filename' directly */ /* if there is a backing file, use it */ bs1 = bdrv_new(""); - ret = bdrv_open2(bs1, filename, 0, drv); + ret = bdrv_open(bs1, filename, 0, drv); if (ret < 0) { bdrv_delete(bs1); return ret; } - total_size = bdrv_getlength(bs1) >> BDRV_SECTOR_BITS; + total_size = bdrv_getlength(bs1) & BDRV_SECTOR_MASK; if (bs1->drv && bs1->drv->protocol_name) is_protocol = 1; @@ -396,13 +565,13 @@ if (is_protocol) snprintf(backing_filename, sizeof(backing_filename), "%s", filename); - else - realpath(filename, backing_filename); + else if (!realpath(filename, backing_filename)) + return -errno; bdrv_qcow2 = bdrv_find_format("qcow2"); options = parse_option_parameters("", bdrv_qcow2->create_options, NULL); - set_option_parameter_int(options, BLOCK_OPT_SIZE, total_size * 512); + set_option_parameter_int(options, BLOCK_OPT_SIZE, total_size); set_option_parameter(options, BLOCK_OPT_BACKING_FILE, backing_filename); if (drv) { set_option_parameter(options, BLOCK_OPT_BACKING_FMT, @@ -410,6 +579,7 @@ } ret = bdrv_create(bdrv_qcow2, tmp_filename, options); + free_option_parameters(options); if (ret < 0) { return ret; } @@ -419,98 +589,84 @@ bs->is_temporary = 1; } - pstrcpy(bs->filename, sizeof(bs->filename), filename); - if (flags & BDRV_O_FILE) { - drv = find_protocol(filename); - } else if (!drv) { - drv = find_hdev_driver(filename); - if (!drv) { - drv = find_image_format(filename); - } + /* Find the right image format driver */ + if (!drv) { + ret = find_image_format(filename, &drv); } + if (!drv) { - ret = -ENOENT; goto unlink_and_fail; } - bs->drv = drv; - bs->opaque = qemu_mallocz(drv->instance_size); - /* - * Yes, BDRV_O_NOCACHE aka O_DIRECT means we have to present a - * write cache to the guest. We do need the fdatasync to flush - * out transactions for block allocations, and we maybe have a - * volatile write cache in our backing device to deal with. - */ - if (flags & (BDRV_O_CACHE_WB|BDRV_O_NOCACHE)) - bs->enable_write_cache = 1; - - /* Note: for compatibility, we open disk image files as RDWR, and - RDONLY as fallback */ - try_rw = !bs->read_only || bs->is_temporary; - if (!(flags & BDRV_O_FILE)) - open_flags = (try_rw ? BDRV_O_RDWR : 0) | - (flags & (BDRV_O_CACHE_MASK|BDRV_O_NATIVE_AIO)); - else - open_flags = flags & ~(BDRV_O_FILE | BDRV_O_SNAPSHOT); - - bs->open_flags = open_flags; - if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv)) - ret = -ENOTSUP; - else - ret = drv->bdrv_open(bs, filename, open_flags); - if ((ret == -EACCES || ret == -EPERM) && !(flags & BDRV_O_FILE)) { - ret = drv->bdrv_open(bs, filename, open_flags & ~BDRV_O_RDWR); - bs->read_only = 1; - } + /* Open the image */ + ret = bdrv_open_common(bs, filename, flags, drv); if (ret < 0) { - qemu_free(bs->opaque); - bs->opaque = NULL; - bs->drv = NULL; - unlink_and_fail: - if (bs->is_temporary) - unlink(filename); - return ret; - } - if (drv->bdrv_getlength) { - bs->total_sectors = bdrv_getlength(bs) >> BDRV_SECTOR_BITS; - } -#ifndef _WIN32 - if (bs->is_temporary) { - unlink(filename); + goto unlink_and_fail; } -#endif - if (bs->backing_file[0] != '\0') { - /* if there is a backing file, use it */ + + /* If there is a backing file, use it */ + if ((flags & BDRV_O_NO_BACKING) == 0 && bs->backing_file[0] != '\0') { + char backing_filename[PATH_MAX]; + int back_flags; BlockDriver *back_drv = NULL; + bs->backing_hd = bdrv_new(""); - /* pass on read_only property to the backing_hd */ - bs->backing_hd->read_only = bs->read_only; - path_combine(backing_filename, sizeof(backing_filename), - filename, bs->backing_file); - if (bs->backing_format[0] != '\0') + + if (path_has_protocol(bs->backing_file)) { + pstrcpy(backing_filename, sizeof(backing_filename), + bs->backing_file); + } else { + path_combine(backing_filename, sizeof(backing_filename), + filename, bs->backing_file); + } + + if (bs->backing_format[0] != '\0') { back_drv = bdrv_find_format(bs->backing_format); - ret = bdrv_open2(bs->backing_hd, backing_filename, open_flags, - back_drv); + } + + /* backing files always opened read-only */ + back_flags = + flags & ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING); + + ret = bdrv_open(bs->backing_hd, backing_filename, back_flags, back_drv); if (ret < 0) { bdrv_close(bs); return ret; } + if (bs->is_temporary) { + bs->backing_hd->keep_read_only = !(flags & BDRV_O_RDWR); + } else { + /* base image inherits from "parent" */ + bs->backing_hd->keep_read_only = bs->keep_read_only; + } } if (!bdrv_key_required(bs)) { /* call the change callback */ bs->media_changed = 1; if (bs->change_cb) - bs->change_cb(bs->change_opaque); + bs->change_cb(bs->change_opaque, CHANGE_MEDIA); } + return 0; + +unlink_and_fail: + if (bs->is_temporary) { + unlink(filename); + } + return ret; } void bdrv_close(BlockDriverState *bs) { if (bs->drv) { - if (bs->backing_hd) + if (bs == bs_snapshots) { + bs_snapshots = NULL; + } + if (bs->backing_hd) { bdrv_delete(bs->backing_hd); + bs->backing_hd = NULL; + } bs->drv->bdrv_close(bs); qemu_free(bs->opaque); #ifdef _WIN32 @@ -521,82 +677,224 @@ bs->opaque = NULL; bs->drv = NULL; + if (bs->file != NULL) { + bdrv_close(bs->file); + } + /* call the change callback */ bs->media_changed = 1; if (bs->change_cb) - bs->change_cb(bs->change_opaque); + bs->change_cb(bs->change_opaque, CHANGE_MEDIA); } } +void bdrv_close_all(void) +{ + BlockDriverState *bs; + + QTAILQ_FOREACH(bs, &bdrv_states, list) { + bdrv_close(bs); + } +} + +/* make a BlockDriverState anonymous by removing from bdrv_state list. + Also, NULL terminate the device_name to prevent double remove */ +void bdrv_make_anon(BlockDriverState *bs) +{ + if (bs->device_name[0] != '\0') { + QTAILQ_REMOVE(&bdrv_states, bs, list); + } + bs->device_name[0] = '\0'; +} + void bdrv_delete(BlockDriverState *bs) { - BlockDriverState **pbs; + assert(!bs->peer); - pbs = &bdrv_first; - while (*pbs != bs && *pbs != NULL) - pbs = &(*pbs)->next; - if (*pbs == bs) - *pbs = bs->next; + /* remove from list, if necessary */ + bdrv_make_anon(bs); bdrv_close(bs); + if (bs->file != NULL) { + bdrv_delete(bs->file); + } + + assert(bs != bs_snapshots); qemu_free(bs); } +int bdrv_attach(BlockDriverState *bs, DeviceState *qdev) +{ + if (bs->peer) { + return -EBUSY; + } + bs->peer = qdev; + return 0; +} + +void bdrv_detach(BlockDriverState *bs, DeviceState *qdev) +{ + assert(bs->peer == qdev); + bs->peer = NULL; +} + +DeviceState *bdrv_get_attached(BlockDriverState *bs) +{ + return bs->peer; +} + /* * Run consistency checks on an image * - * Returns the number of errors or -errno when an internal error occurs + * Returns 0 if the check could be completed (it doesn't mean that the image is + * free of errors) or -errno when an internal error occured. The results of the + * check are stored in res. */ -int bdrv_check(BlockDriverState *bs) +int bdrv_check(BlockDriverState *bs, BdrvCheckResult *res) { if (bs->drv->bdrv_check == NULL) { return -ENOTSUP; } - return bs->drv->bdrv_check(bs); + memset(res, 0, sizeof(*res)); + return bs->drv->bdrv_check(bs, res); } +#define COMMIT_BUF_SECTORS 2048 + /* commit COW file into the raw image */ int bdrv_commit(BlockDriverState *bs) { BlockDriver *drv = bs->drv; - int64_t i, total_sectors; - int n, j; - unsigned char sector[512]; + BlockDriver *backing_drv; + int64_t sector, total_sectors; + int n, ro, open_flags; + int ret = 0, rw_ret = 0; + uint8_t *buf; + char filename[1024]; + BlockDriverState *bs_rw, *bs_ro; if (!drv) return -ENOMEDIUM; + + if (!bs->backing_hd) { + return -ENOTSUP; + } - if (bs->read_only) { - return -EACCES; + if (bs->backing_hd->keep_read_only) { + return -EACCES; } - if (!bs->backing_hd) { - return -ENOTSUP; + backing_drv = bs->backing_hd->drv; + ro = bs->backing_hd->read_only; + strncpy(filename, bs->backing_hd->filename, sizeof(filename)); + open_flags = bs->backing_hd->open_flags; + + if (ro) { + /* re-open as RW */ + bdrv_delete(bs->backing_hd); + bs->backing_hd = NULL; + bs_rw = bdrv_new(""); + rw_ret = bdrv_open(bs_rw, filename, open_flags | BDRV_O_RDWR, + backing_drv); + if (rw_ret < 0) { + bdrv_delete(bs_rw); + /* try to re-open read-only */ + bs_ro = bdrv_new(""); + ret = bdrv_open(bs_ro, filename, open_flags & ~BDRV_O_RDWR, + backing_drv); + if (ret < 0) { + bdrv_delete(bs_ro); + /* drive not functional anymore */ + bs->drv = NULL; + return ret; + } + bs->backing_hd = bs_ro; + return rw_ret; + } + bs->backing_hd = bs_rw; } total_sectors = bdrv_getlength(bs) >> BDRV_SECTOR_BITS; - for (i = 0; i < total_sectors;) { - if (drv->bdrv_is_allocated(bs, i, 65536, &n)) { - for(j = 0; j < n; j++) { - if (bdrv_read(bs, i, sector, 1) != 0) { - return -EIO; - } + buf = qemu_malloc(COMMIT_BUF_SECTORS * BDRV_SECTOR_SIZE); - if (bdrv_write(bs->backing_hd, i, sector, 1) != 0) { - return -EIO; - } - i++; - } - } else { - i += n; + for (sector = 0; sector < total_sectors; sector += n) { + if (drv->bdrv_is_allocated(bs, sector, COMMIT_BUF_SECTORS, &n)) { + + if (bdrv_read(bs, sector, buf, n) != 0) { + ret = -EIO; + goto ro_cleanup; + } + + if (bdrv_write(bs->backing_hd, sector, buf, n) != 0) { + ret = -EIO; + goto ro_cleanup; + } } } - if (drv->bdrv_make_empty) - return drv->bdrv_make_empty(bs); + if (drv->bdrv_make_empty) { + ret = drv->bdrv_make_empty(bs); + bdrv_flush(bs); + } + + /* + * Make sure all data we wrote to the backing device is actually + * stable on disk. + */ + if (bs->backing_hd) + bdrv_flush(bs->backing_hd); + +ro_cleanup: + qemu_free(buf); + + if (ro) { + /* re-open as RO */ + bdrv_delete(bs->backing_hd); + bs->backing_hd = NULL; + bs_ro = bdrv_new(""); + ret = bdrv_open(bs_ro, filename, open_flags & ~BDRV_O_RDWR, + backing_drv); + if (ret < 0) { + bdrv_delete(bs_ro); + /* drive not functional anymore */ + bs->drv = NULL; + return ret; + } + bs->backing_hd = bs_ro; + bs->backing_hd->keep_read_only = 0; + } + + return ret; +} + +void bdrv_commit_all(void) +{ + BlockDriverState *bs; + + QTAILQ_FOREACH(bs, &bdrv_states, list) { + bdrv_commit(bs); + } +} + +/* + * Return values: + * 0 - success + * -EINVAL - backing format specified, but no file + * -ENOSPC - can't update the backing file because no space is left in the + * image file header + * -ENOTSUP - format driver doesn't support changing the backing file + */ +int bdrv_change_backing_file(BlockDriverState *bs, + const char *backing_file, const char *backing_fmt) +{ + BlockDriver *drv = bs->drv; - return 0; + if (drv->bdrv_change_backing_file != NULL) { + return drv->bdrv_change_backing_file(bs, backing_file, backing_fmt); + } else { + return -ENOTSUP; + } } static int bdrv_check_byte_request(BlockDriverState *bs, int64_t offset, @@ -624,7 +922,8 @@ static int bdrv_check_request(BlockDriverState *bs, int64_t sector_num, int nb_sectors) { - return bdrv_check_byte_request(bs, sector_num * 512, nb_sectors * 512); + return bdrv_check_byte_request(bs, sector_num * BDRV_SECTOR_SIZE, + nb_sectors * BDRV_SECTOR_SIZE); } /* return < 0 if error. See bdrv_write() for the return codes */ @@ -655,9 +954,15 @@ bit = start % (sizeof(unsigned long) * 8); val = bs->dirty_bitmap[idx]; if (dirty) { - val |= 1 << bit; + if (!(val & (1UL << bit))) { + bs->dirty_count++; + val |= 1UL << bit; + } } else { - val &= ~(1 << bit); + if (val & (1UL << bit)) { + bs->dirty_count--; + val &= ~(1UL << bit); + } } bs->dirty_bitmap[idx] = val; } @@ -684,6 +989,10 @@ set_dirty_bitmap(bs, sector_num, nb_sectors, 1); } + if (bs->wr_highest_sector < sector_num + nb_sectors - 1) { + bs->wr_highest_sector = sector_num + nb_sectors - 1; + } + return drv->bdrv_write(bs, sector_num, buf, nb_sectors); } @@ -824,13 +1133,23 @@ int bdrv_truncate(BlockDriverState *bs, int64_t offset) { BlockDriver *drv = bs->drv; + int ret; if (!drv) return -ENOMEDIUM; if (!drv->bdrv_truncate) return -ENOTSUP; if (bs->read_only) return -EACCES; - return drv->bdrv_truncate(bs, offset); + if (bdrv_in_use(bs)) + return -EBUSY; + ret = drv->bdrv_truncate(bs, offset); + if (ret == 0) { + ret = refresh_total_sectors(bs, offset >> BDRV_SECTOR_BITS); + if (bs->change_cb) { + bs->change_cb(bs->change_opaque, CHANGE_SIZE); + } + } + return ret; } /** @@ -841,8 +1160,12 @@ BlockDriver *drv = bs->drv; if (!drv) return -ENOMEDIUM; - if (!drv->bdrv_getlength) { - /* legacy mode */ + + /* Fixed size devices use the total_sectors value for speed instead of + issuing a length query (like lseek) on each call. Also, legacy block + drivers don't provide a bdrv_getlength function and must use + total_sectors. */ + if (!bs->growable || !drv->bdrv_getlength) { return bs->total_sectors * BDRV_SECTOR_SIZE; } return drv->bdrv_getlength(bs); @@ -877,7 +1200,7 @@ static int guess_disk_lchs(BlockDriverState *bs, int *pcylinders, int *pheads, int *psectors) { - uint8_t buf[512]; + uint8_t buf[BDRV_SECTOR_SIZE]; int ret, i, heads, sectors, cylinders; struct partition *p; uint32_t nr_sects; @@ -1014,6 +1337,26 @@ return bs->translation; } +void bdrv_set_on_error(BlockDriverState *bs, BlockErrorAction on_read_error, + BlockErrorAction on_write_error) +{ + bs->on_read_error = on_read_error; + bs->on_write_error = on_write_error; +} + +BlockErrorAction bdrv_get_on_error(BlockDriverState *bs, int is_read) +{ + return is_read ? bs->on_read_error : bs->on_write_error; +} + +void bdrv_set_removable(BlockDriverState *bs, int removable) +{ + bs->removable = removable; + if (removable && bs == bs_snapshots) { + bs_snapshots = NULL; + } +} + int bdrv_is_removable(BlockDriverState *bs) { return bs->removable; @@ -1024,13 +1367,6 @@ return bs->read_only; } -int bdrv_set_read_only(BlockDriverState *bs, int read_only) -{ - int ret = bs->read_only; - bs->read_only = read_only; - return ret; -} - int bdrv_is_sg(BlockDriverState *bs) { return bs->sg; @@ -1043,7 +1379,8 @@ /* XXX: no longer used */ void bdrv_set_change_cb(BlockDriverState *bs, - void (*change_cb)(void *opaque), void *opaque) + void (*change_cb)(void *opaque, int reason), + void *opaque) { bs->change_cb = change_cb; bs->change_opaque = opaque; @@ -1075,8 +1412,11 @@ if (!bs->encrypted) return 0; } - if (!bs->encrypted || !bs->drv || !bs->drv->bdrv_set_key) - return -1; + if (!bs->encrypted) { + return -EINVAL; + } else if (!bs->drv || !bs->drv->bdrv_set_key) { + return -ENOMEDIUM; + } ret = bs->drv->bdrv_set_key(bs, key); if (ret < 0) { bs->valid_key = 0; @@ -1085,7 +1425,7 @@ /* call the change callback now, we skipped it on open */ bs->media_changed = 1; if (bs->change_cb) - bs->change_cb(bs->change_opaque); + bs->change_cb(bs->change_opaque, CHANGE_MEDIA); } return ret; } @@ -1104,7 +1444,7 @@ { BlockDriver *drv; - for (drv = first_drv; drv != NULL; drv = drv->next) { + QLIST_FOREACH(drv, &bdrv_drivers, list) { it(opaque, drv->format_name); } } @@ -1113,18 +1453,27 @@ { BlockDriverState *bs; - for (bs = bdrv_first; bs != NULL; bs = bs->next) { - if (!strcmp(name, bs->device_name)) + QTAILQ_FOREACH(bs, &bdrv_states, list) { + if (!strcmp(name, bs->device_name)) { return bs; + } } return NULL; } +BlockDriverState *bdrv_next(BlockDriverState *bs) +{ + if (!bs) { + return QTAILQ_FIRST(&bdrv_states); + } + return QTAILQ_NEXT(bs, list); +} + void bdrv_iterate(void (*it)(void *opaque, BlockDriverState *bs), void *opaque) { BlockDriverState *bs; - for (bs = bdrv_first; bs != NULL; bs = bs->next) { + QTAILQ_FOREACH(bs, &bdrv_states, list) { it(opaque, bs); } } @@ -1134,24 +1483,61 @@ return bs->device_name; } -void bdrv_flush(BlockDriverState *bs) +int bdrv_flush(BlockDriverState *bs) { - if (!bs->drv) - return; - if (bs->drv->bdrv_flush) - bs->drv->bdrv_flush(bs); - if (bs->backing_hd) - bdrv_flush(bs->backing_hd); + if (bs->open_flags & BDRV_O_NO_FLUSH) { + return 0; + } + + if (bs->drv && bs->drv->bdrv_flush) { + return bs->drv->bdrv_flush(bs); + } + + /* + * Some block drivers always operate in either writethrough or unsafe mode + * and don't support bdrv_flush therefore. Usually qemu doesn't know how + * the server works (because the behaviour is hardcoded or depends on + * server-side configuration), so we can't ensure that everything is safe + * on disk. Returning an error doesn't work because that would break guests + * even if the server operates in writethrough mode. + * + * Let's hope the user knows what he's doing. + */ + return 0; } void bdrv_flush_all(void) { BlockDriverState *bs; - for (bs = bdrv_first; bs != NULL; bs = bs->next) - if (bs->drv && !bdrv_is_read_only(bs) && - (!bdrv_is_removable(bs) || bdrv_is_inserted(bs))) + QTAILQ_FOREACH(bs, &bdrv_states, list) { + if (bs->drv && !bdrv_is_read_only(bs) && + (!bdrv_is_removable(bs) || bdrv_is_inserted(bs))) { bdrv_flush(bs); + } + } +} + +int bdrv_has_zero_init(BlockDriverState *bs) +{ + assert(bs->drv); + + if (bs->drv->bdrv_has_zero_init) { + return bs->drv->bdrv_has_zero_init(bs); + } + + return 1; +} + +int bdrv_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors) +{ + if (!bs->drv) { + return -ENOMEDIUM; + } + if (!bs->drv->bdrv_discard) { + return 0; + } + return bs->drv->bdrv_discard(bs, sector_num, nb_sectors); } /* @@ -1181,6 +1567,35 @@ return bs->drv->bdrv_is_allocated(bs, sector_num, nb_sectors, pnum); } +void bdrv_mon_event(const BlockDriverState *bdrv, + BlockMonEventAction action, int is_read) +{ + QObject *data; + const char *action_str; + + switch (action) { + case BDRV_ACTION_REPORT: + action_str = "report"; + break; + case BDRV_ACTION_IGNORE: + action_str = "ignore"; + break; + case BDRV_ACTION_STOP: + action_str = "stop"; + break; + default: + abort(); + } + + data = qobject_from_jsonf("{ 'device': %s, 'action': %s, 'operation': %s }", + bdrv->device_name, + action_str, + is_read ? "read" : "write"); + monitor_protocol_event(QEVENT_BLOCK_IO_ERROR, data); + + qobject_decref(data); +} + static void bdrv_print_dict(QObject *obj, void *opaque) { QDict *bs_dict; @@ -1222,33 +1637,6 @@ qlist_iter(qobject_to_qlist(data), bdrv_print_dict, mon); } -/** - * bdrv_info(): Block devices information - * - * Each block device information is stored in a QDict and the - * returned QObject is a QList of all devices. - * - * The QDict contains the following: - * - * - "device": device name - * - "type": device type - * - "removable": true if the device is removable, false otherwise - * - "locked": true if the device is locked, false otherwise - * - "inserted": only present if the device is inserted, it is a QDict - * containing the following: - * - "file": device file name - * - "ro": true if read-only, false otherwise - * - "drv": driver format name - * - "backing_file": backing file name if one is used - * - "encrypted": true if encrypted, false otherwise - * - * Example: - * - * [ { "device": "ide0-hd0", "type": "hd", "removable": false, "locked": false, - * "inserted": { "file": "/tmp/foobar", "ro": false, "drv": "qcow2" } }, - * { "device": "floppy0", "type": "floppy", "removable": true, - * "locked": false } ] - */ void bdrv_info(Monitor *mon, QObject **ret_data) { QList *bs_list; @@ -1256,7 +1644,7 @@ bs_list = qlist_new(); - for (bs = bdrv_first; bs != NULL; bs = bs->next) { + QTAILQ_FOREACH(bs, &bdrv_states, list) { QObject *bs_obj; const char *type = "unknown"; @@ -1276,7 +1664,6 @@ "'removable': %i, 'locked': %i }", bs->device_name, type, bs->removable, bs->locked); - assert(bs_obj != NULL); if (bs->drv) { QObject *obj; @@ -1287,7 +1674,6 @@ bs->filename, bs->read_only, bs->drv->format_name, bdrv_is_encrypted(bs)); - assert(obj != NULL); if (bs->backing_file[0] != '\0') { QDict *qdict = qobject_to_qdict(obj); qdict_put(qdict, "backing_file", @@ -1327,34 +1713,36 @@ qlist_iter(qobject_to_qlist(data), bdrv_stats_iter, mon); } -/** - * bdrv_info_stats(): show block device statistics - * - * Each device statistic information is stored in a QDict and - * the returned QObject is a QList of all devices. - * - * The QDict contains the following: - * - * - "device": device name - * - "stats": A QDict with the statistics information, it contains: - * - "rd_bytes": bytes read - * - "wr_bytes": bytes written - * - "rd_operations": read operations - * - "wr_operations": write operations - * - * Example: - * - * [ { "device": "ide0-hd0", - * "stats": { "rd_bytes": 512, - * "wr_bytes": 0, - * "rd_operations": 1, - * "wr_operations": 0 } }, - * { "device": "ide1-cd0", - * "stats": { "rd_bytes": 0, - * "wr_bytes": 0, - * "rd_operations": 0, - * "wr_operations": 0 } } ] - */ +static QObject* bdrv_info_stats_bs(BlockDriverState *bs) +{ + QObject *res; + QDict *dict; + + res = qobject_from_jsonf("{ 'stats': {" + "'rd_bytes': %" PRId64 "," + "'wr_bytes': %" PRId64 "," + "'rd_operations': %" PRId64 "," + "'wr_operations': %" PRId64 "," + "'wr_highest_offset': %" PRId64 + "} }", + bs->rd_bytes, bs->wr_bytes, + bs->rd_ops, bs->wr_ops, + bs->wr_highest_sector * + (uint64_t)BDRV_SECTOR_SIZE); + dict = qobject_to_qdict(res); + + if (*bs->device_name) { + qdict_put(dict, "device", qstring_from_str(bs->device_name)); + } + + if (bs->file) { + QObject *parent = bdrv_info_stats_bs(bs->file); + qdict_put_obj(dict, "parent", parent); + } + + return res; +} + void bdrv_info_stats(Monitor *mon, QObject **ret_data) { QObject *obj; @@ -1363,17 +1751,8 @@ devices = qlist_new(); - for (bs = bdrv_first; bs != NULL; bs = bs->next) { - obj = qobject_from_jsonf("{ 'device': %s, 'stats': {" - "'rd_bytes': %" PRId64 "," - "'wr_bytes': %" PRId64 "," - "'rd_operations': %" PRId64 "," - "'wr_operations': %" PRId64 - "} }", - bs->device_name, - bs->rd_bytes, bs->wr_bytes, - bs->rd_ops, bs->wr_ops); - assert(obj != NULL); + QTAILQ_FOREACH(bs, &bdrv_states, list) { + obj = bdrv_info_stats_bs(bs); qlist_append_obj(devices, obj); } @@ -1393,7 +1772,7 @@ void bdrv_get_backing_filename(BlockDriverState *bs, char *filename, int filename_size) { - if (!bs->backing_hd) { + if (!bs->backing_file) { pstrcpy(filename, filename_size, ""); } else { pstrcpy(filename, filename_size, bs->backing_file); @@ -1435,9 +1814,11 @@ BlockDriver *drv = bs->drv; if (!drv) return -ENOMEDIUM; - if (!drv->bdrv_save_vmstate) - return -ENOTSUP; - return drv->bdrv_save_vmstate(bs, buf, pos, size); + if (drv->bdrv_save_vmstate) + return drv->bdrv_save_vmstate(bs, buf, pos, size); + if (bs->file) + return bdrv_save_vmstate(bs->file, buf, pos, size); + return -ENOTSUP; } int bdrv_load_vmstate(BlockDriverState *bs, uint8_t *buf, @@ -1446,34 +1827,105 @@ BlockDriver *drv = bs->drv; if (!drv) return -ENOMEDIUM; - if (!drv->bdrv_load_vmstate) - return -ENOTSUP; - return drv->bdrv_load_vmstate(bs, buf, pos, size); + if (drv->bdrv_load_vmstate) + return drv->bdrv_load_vmstate(bs, buf, pos, size); + if (bs->file) + return bdrv_load_vmstate(bs->file, buf, pos, size); + return -ENOTSUP; +} + +void bdrv_debug_event(BlockDriverState *bs, BlkDebugEvent event) +{ + BlockDriver *drv = bs->drv; + + if (!drv || !drv->bdrv_debug_event) { + return; + } + + return drv->bdrv_debug_event(bs, event); + } /**************************************************************/ /* handling of snapshots */ +int bdrv_can_snapshot(BlockDriverState *bs) +{ + BlockDriver *drv = bs->drv; + if (!drv || bdrv_is_removable(bs) || bdrv_is_read_only(bs)) { + return 0; + } + + if (!drv->bdrv_snapshot_create) { + if (bs->file != NULL) { + return bdrv_can_snapshot(bs->file); + } + return 0; + } + + return 1; +} + +int bdrv_is_snapshot(BlockDriverState *bs) +{ + return !!(bs->open_flags & BDRV_O_SNAPSHOT); +} + +BlockDriverState *bdrv_snapshots(void) +{ + BlockDriverState *bs; + + if (bs_snapshots) { + return bs_snapshots; + } + + bs = NULL; + while ((bs = bdrv_next(bs))) { + if (bdrv_can_snapshot(bs)) { + bs_snapshots = bs; + return bs; + } + } + return NULL; +} + int bdrv_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) { BlockDriver *drv = bs->drv; if (!drv) return -ENOMEDIUM; - if (!drv->bdrv_snapshot_create) - return -ENOTSUP; - return drv->bdrv_snapshot_create(bs, sn_info); + if (drv->bdrv_snapshot_create) + return drv->bdrv_snapshot_create(bs, sn_info); + if (bs->file) + return bdrv_snapshot_create(bs->file, sn_info); + return -ENOTSUP; } int bdrv_snapshot_goto(BlockDriverState *bs, const char *snapshot_id) { BlockDriver *drv = bs->drv; + int ret, open_ret; + if (!drv) return -ENOMEDIUM; - if (!drv->bdrv_snapshot_goto) - return -ENOTSUP; - return drv->bdrv_snapshot_goto(bs, snapshot_id); + if (drv->bdrv_snapshot_goto) + return drv->bdrv_snapshot_goto(bs, snapshot_id); + + if (bs->file) { + drv->bdrv_close(bs); + ret = bdrv_snapshot_goto(bs->file, snapshot_id); + open_ret = drv->bdrv_open(bs, bs->open_flags); + if (open_ret < 0) { + bdrv_delete(bs->file); + bs->drv = NULL; + return open_ret; + } + return ret; + } + + return -ENOTSUP; } int bdrv_snapshot_delete(BlockDriverState *bs, const char *snapshot_id) @@ -1481,9 +1933,11 @@ BlockDriver *drv = bs->drv; if (!drv) return -ENOMEDIUM; - if (!drv->bdrv_snapshot_delete) - return -ENOTSUP; - return drv->bdrv_snapshot_delete(bs, snapshot_id); + if (drv->bdrv_snapshot_delete) + return drv->bdrv_snapshot_delete(bs, snapshot_id); + if (bs->file) + return bdrv_snapshot_delete(bs->file, snapshot_id); + return -ENOTSUP; } int bdrv_snapshot_list(BlockDriverState *bs, @@ -1492,9 +1946,27 @@ BlockDriver *drv = bs->drv; if (!drv) return -ENOMEDIUM; - if (!drv->bdrv_snapshot_list) - return -ENOTSUP; - return drv->bdrv_snapshot_list(bs, psn_info); + if (drv->bdrv_snapshot_list) + return drv->bdrv_snapshot_list(bs, psn_info); + if (bs->file) + return bdrv_snapshot_list(bs->file, psn_info); + return -ENOTSUP; +} + +int bdrv_snapshot_load_tmp(BlockDriverState *bs, + const char *snapshot_name) +{ + BlockDriver *drv = bs->drv; + if (!drv) { + return -ENOMEDIUM; + } + if (!bs->read_only) { + return -EINVAL; + } + if (drv->bdrv_snapshot_load_tmp) { + return drv->bdrv_snapshot_load_tmp(bs, snapshot_name); + } + return -ENOTSUP; } #define NB_SUFFIXES 4 @@ -1581,6 +2053,8 @@ BlockDriver *drv = bs->drv; BlockDriverAIOCB *ret; + trace_bdrv_aio_readv(bs, sector_num, nb_sectors, opaque); + if (!drv) return NULL; if (bdrv_check_request(bs, sector_num, nb_sectors)) @@ -1598,12 +2072,51 @@ return ret; } +typedef struct BlockCompleteData { + BlockDriverCompletionFunc *cb; + void *opaque; + BlockDriverState *bs; + int64_t sector_num; + int nb_sectors; +} BlockCompleteData; + +static void block_complete_cb(void *opaque, int ret) +{ + BlockCompleteData *b = opaque; + + if (b->bs->dirty_bitmap) { + set_dirty_bitmap(b->bs, b->sector_num, b->nb_sectors, 1); + } + b->cb(b->opaque, ret); + qemu_free(b); +} + +static BlockCompleteData *blk_dirty_cb_alloc(BlockDriverState *bs, + int64_t sector_num, + int nb_sectors, + BlockDriverCompletionFunc *cb, + void *opaque) +{ + BlockCompleteData *blkdata = qemu_mallocz(sizeof(BlockCompleteData)); + + blkdata->bs = bs; + blkdata->cb = cb; + blkdata->opaque = opaque; + blkdata->sector_num = sector_num; + blkdata->nb_sectors = nb_sectors; + + return blkdata; +} + BlockDriverAIOCB *bdrv_aio_writev(BlockDriverState *bs, int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, BlockDriverCompletionFunc *cb, void *opaque) { BlockDriver *drv = bs->drv; BlockDriverAIOCB *ret; + BlockCompleteData *blk_cb_data; + + trace_bdrv_aio_writev(bs, sector_num, nb_sectors, opaque); if (!drv) return NULL; @@ -1613,16 +2126,22 @@ return NULL; if (bs->dirty_bitmap) { - set_dirty_bitmap(bs, sector_num, nb_sectors, 1); + blk_cb_data = blk_dirty_cb_alloc(bs, sector_num, nb_sectors, cb, + opaque); + cb = &block_complete_cb; + opaque = blk_cb_data; } ret = drv->bdrv_aio_writev(bs, sector_num, qiov, nb_sectors, cb, opaque); if (ret) { - /* Update stats even though technically transfer has not happened. */ - bs->wr_bytes += (unsigned) nb_sectors * BDRV_SECTOR_SIZE; - bs->wr_ops ++; + /* Update stats even though technically transfer has not happened. */ + bs->wr_bytes += (unsigned) nb_sectors * BDRV_SECTOR_SIZE; + bs->wr_ops ++; + if (bs->wr_highest_sector < sector_num + nb_sectors - 1) { + bs->wr_highest_sector = sector_num + nb_sectors - 1; + } } return ret; @@ -1659,6 +2178,8 @@ { MultiwriteCB *mcb = opaque; + trace_multiwrite_cb(mcb, ret); + if (ret < 0 && !mcb->error) { mcb->error = ret; } @@ -1782,6 +2303,14 @@ MultiwriteCB *mcb; int i; + /* don't submit writes if we don't have a medium */ + if (bs->drv == NULL) { + for (i = 0; i < num_reqs; i++) { + reqs[i].error = -ENOMEDIUM; + } + return -1; + } + if (num_reqs == 0) { return 0; } @@ -1799,6 +2328,8 @@ // Check for mergable requests num_reqs = multiwrite_merge(bs, reqs, num_reqs, mcb); + trace_bdrv_aio_multiwrite(mcb, mcb->num_callbacks, num_reqs); + /* * Run the aio requests. As soon as one request can't be submitted * successfully, fail all requests that are not yet submitted (we must @@ -1820,6 +2351,7 @@ */ mcb->num_requests = 1; + // Run the aio requests for (i = 0; i < num_reqs; i++) { mcb->num_requests++; acb = bdrv_aio_writev(bs, reqs[i].sector, reqs[i].qiov, @@ -1830,8 +2362,10 @@ // submitted yet. Otherwise we'll wait for the submitted AIOs to // complete and report the error in the callback. if (i == 0) { + trace_bdrv_aio_multiwrite_earlyfail(mcb); goto fail; } else { + trace_bdrv_aio_multiwrite_latefail(mcb, i); multiwrite_cb(mcb, -EIO); break; } @@ -1856,13 +2390,12 @@ { BlockDriver *drv = bs->drv; + if (bs->open_flags & BDRV_O_NO_FLUSH) { + return bdrv_aio_noop_em(bs, cb, opaque); + } + if (!drv) return NULL; - - /* - * Note that unlike bdrv_flush the driver is reponsible for flushing a - * backing image if it exists. - */ return drv->bdrv_aio_flush(bs, cb, opaque); } @@ -1887,7 +2420,8 @@ static void bdrv_aio_cancel_em(BlockDriverAIOCB *blockacb) { - BlockDriverAIOCBSync *acb = (BlockDriverAIOCBSync *)blockacb; + BlockDriverAIOCBSync *acb = + container_of(blockacb, BlockDriverAIOCBSync, common); qemu_bh_delete(acb->bh); acb->bh = NULL; qemu_aio_release(acb); @@ -1975,6 +2509,25 @@ return &acb->common; } +static BlockDriverAIOCB *bdrv_aio_noop_em(BlockDriverState *bs, + BlockDriverCompletionFunc *cb, void *opaque) +{ + BlockDriverAIOCBSync *acb; + + acb = qemu_aio_get(&bdrv_em_aio_pool, bs, cb, opaque); + acb->is_write = 1; /* don't bounce in the completion handler */ + acb->qiov = NULL; + acb->bounce = NULL; + acb->ret = 0; + + if (!acb->bh) { + acb->bh = qemu_bh_new(bdrv_aio_bh_cb, acb); + } + + qemu_bh_schedule(acb->bh); + return &acb->common; +} + /**************************************************************/ /* sync block device emulation */ @@ -1997,7 +2550,7 @@ async_ret = NOT_DONE; iov.iov_base = (void *)buf; - iov.iov_len = nb_sectors * 512; + iov.iov_len = nb_sectors * BDRV_SECTOR_SIZE; qemu_iovec_init_external(&qiov, &iov, 1); acb = bdrv_aio_readv(bs, sector_num, &qiov, nb_sectors, bdrv_rw_em_cb, &async_ret); @@ -2028,7 +2581,7 @@ async_ret = NOT_DONE; iov.iov_base = (void *)buf; - iov.iov_len = nb_sectors * 512; + iov.iov_len = nb_sectors * BDRV_SECTOR_SIZE; qemu_iovec_init_external(&qiov, &iov, 1); acb = bdrv_aio_writev(bs, sector_num, &qiov, nb_sectors, bdrv_rw_em_cb, &async_ret); @@ -2095,7 +2648,7 @@ if (!drv) return 0; if (!drv->bdrv_is_inserted) - return 1; + return !bs->tray_open; ret = drv->bdrv_is_inserted(bs); return ret; } @@ -2137,10 +2690,11 @@ ret = drv->bdrv_eject(bs, eject_flag); } if (ret == -ENOTSUP) { - if (eject_flag) - bdrv_close(bs); ret = 0; } + if (ret >= 0) { + bs->tray_open = eject_flag; + } return ret; } @@ -2197,6 +2751,7 @@ { int64_t bitmap_size; + bs->dirty_count = 0; if (enable) { if (!bs->dirty_bitmap) { bitmap_size = (bdrv_getlength(bs) >> BDRV_SECTOR_BITS) + @@ -2219,8 +2774,8 @@ if (bs->dirty_bitmap && (sector << BDRV_SECTOR_BITS) < bdrv_getlength(bs)) { - return bs->dirty_bitmap[chunk / (sizeof(unsigned long) * 8)] & - (1 << (chunk % (sizeof(unsigned long) * 8))); + return !!(bs->dirty_bitmap[chunk / (sizeof(unsigned long) * 8)] & + (1UL << (chunk % (sizeof(unsigned long) * 8)))); } else { return 0; } @@ -2231,3 +2786,161 @@ { set_dirty_bitmap(bs, cur_sector, nr_sectors, 0); } + +int64_t bdrv_get_dirty_count(BlockDriverState *bs) +{ + return bs->dirty_count; +} + +void bdrv_set_in_use(BlockDriverState *bs, int in_use) +{ + assert(bs->in_use != in_use); + bs->in_use = in_use; +} + +int bdrv_in_use(BlockDriverState *bs) +{ + return bs->in_use; +} + +int bdrv_img_create(const char *filename, const char *fmt, + const char *base_filename, const char *base_fmt, + char *options, uint64_t img_size, int flags) +{ + QEMUOptionParameter *param = NULL, *create_options = NULL; + QEMUOptionParameter *backing_fmt, *backing_file; + BlockDriverState *bs = NULL; + BlockDriver *drv, *proto_drv; + BlockDriver *backing_drv = NULL; + int ret = 0; + + /* Find driver and parse its options */ + drv = bdrv_find_format(fmt); + if (!drv) { + error_report("Unknown file format '%s'", fmt); + ret = -EINVAL; + goto out; + } + + proto_drv = bdrv_find_protocol(filename); + if (!proto_drv) { + error_report("Unknown protocol '%s'", filename); + ret = -EINVAL; + goto out; + } + + create_options = append_option_parameters(create_options, + drv->create_options); + create_options = append_option_parameters(create_options, + proto_drv->create_options); + + /* Create parameter list with default values */ + param = parse_option_parameters("", create_options, param); + + set_option_parameter_int(param, BLOCK_OPT_SIZE, img_size); + + /* Parse -o options */ + if (options) { + param = parse_option_parameters(options, create_options, param); + if (param == NULL) { + error_report("Invalid options for file format '%s'.", fmt); + ret = -EINVAL; + goto out; + } + } + + if (base_filename) { + if (set_option_parameter(param, BLOCK_OPT_BACKING_FILE, + base_filename)) { + error_report("Backing file not supported for file format '%s'", + fmt); + ret = -EINVAL; + goto out; + } + } + + if (base_fmt) { + if (set_option_parameter(param, BLOCK_OPT_BACKING_FMT, base_fmt)) { + error_report("Backing file format not supported for file " + "format '%s'", fmt); + ret = -EINVAL; + goto out; + } + } + + backing_file = get_option_parameter(param, BLOCK_OPT_BACKING_FILE); + if (backing_file && backing_file->value.s) { + if (!strcmp(filename, backing_file->value.s)) { + error_report("Error: Trying to create an image with the " + "same filename as the backing file"); + ret = -EINVAL; + goto out; + } + } + + backing_fmt = get_option_parameter(param, BLOCK_OPT_BACKING_FMT); + if (backing_fmt && backing_fmt->value.s) { + backing_drv = bdrv_find_format(backing_fmt->value.s); + if (!backing_drv) { + error_report("Unknown backing file format '%s'", + backing_fmt->value.s); + ret = -EINVAL; + goto out; + } + } + + // The size for the image must always be specified, with one exception: + // If we are using a backing file, we can obtain the size from there + if (get_option_parameter(param, BLOCK_OPT_SIZE)->value.n == -1) { + if (backing_file && backing_file->value.s) { + uint64_t size; + char buf[32]; + + bs = bdrv_new(""); + + ret = bdrv_open(bs, backing_file->value.s, flags, backing_drv); + if (ret < 0) { + error_report("Could not open '%s'", backing_file->value.s); + goto out; + } + bdrv_get_geometry(bs, &size); + size *= 512; + + snprintf(buf, sizeof(buf), "%" PRId64, size); + set_option_parameter(param, BLOCK_OPT_SIZE, buf); + } else { + error_report("Image creation needs a size parameter"); + ret = -EINVAL; + goto out; + } + } + + printf("Formatting '%s', fmt=%s ", filename, fmt); + print_option_parameters(param); + puts(""); + + ret = bdrv_create(drv, filename, param); + + if (ret < 0) { + if (ret == -ENOTSUP) { + error_report("Formatting or formatting option not supported for " + "file format '%s'", fmt); + } else if (ret == -EFBIG) { + error_report("The image size is too large for file format '%s'", + fmt); + } else { + error_report("%s: error while creating %s: %s", filename, fmt, + strerror(-ret)); + } + } + +out: + free_option_parameters(create_options); + free_option_parameters(param); + + if (bs) { + bdrv_delete(bs); + } + + return ret; +} diff -Nru qemu-kvm-0.12.5+noroms/blockdev.c qemu-kvm-0.14.1/blockdev.c --- qemu-kvm-0.12.5+noroms/blockdev.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/blockdev.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,800 @@ +/* + * QEMU host block devices + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + */ + +#include "block.h" +#include "blockdev.h" +#include "monitor.h" +#include "qerror.h" +#include "qemu-option.h" +#include "qemu-config.h" +#include "sysemu.h" +#include "hw/qdev.h" +#include "block_int.h" + +DriveInfo *extboot_drive = NULL; + +static QTAILQ_HEAD(drivelist, DriveInfo) drives = QTAILQ_HEAD_INITIALIZER(drives); + +static const char *const if_name[IF_COUNT] = { + [IF_NONE] = "none", + [IF_IDE] = "ide", + [IF_SCSI] = "scsi", + [IF_FLOPPY] = "floppy", + [IF_PFLASH] = "pflash", + [IF_MTD] = "mtd", + [IF_SD] = "sd", + [IF_VIRTIO] = "virtio", + [IF_XEN] = "xen", +}; + +static const int if_max_devs[IF_COUNT] = { + /* + * Do not change these numbers! They govern how drive option + * index maps to unit and bus. That mapping is ABI. + * + * All controllers used to imlement if=T drives need to support + * if_max_devs[T] units, for any T with if_max_devs[T] != 0. + * Otherwise, some index values map to "impossible" bus, unit + * values. + * + * For instance, if you change [IF_SCSI] to 255, -drive + * if=scsi,index=12 no longer means bus=1,unit=5, but + * bus=0,unit=12. With an lsi53c895a controller (7 units max), + * the drive can't be set up. Regression. + */ + [IF_IDE] = 2, + [IF_SCSI] = 7, +}; + +/* + * We automatically delete the drive when a device using it gets + * unplugged. Questionable feature, but we can't just drop it. + * Device models call blockdev_mark_auto_del() to schedule the + * automatic deletion, and generic qdev code calls blockdev_auto_del() + * when deletion is actually safe. + */ +void blockdev_mark_auto_del(BlockDriverState *bs) +{ + DriveInfo *dinfo = drive_get_by_blockdev(bs); + + if (dinfo) { + dinfo->auto_del = 1; + } +} + +void blockdev_auto_del(BlockDriverState *bs) +{ + DriveInfo *dinfo = drive_get_by_blockdev(bs); + + if (dinfo && dinfo->auto_del) { + drive_put_ref(dinfo); + } +} + +static int drive_index_to_bus_id(BlockInterfaceType type, int index) +{ + int max_devs = if_max_devs[type]; + return max_devs ? index / max_devs : 0; +} + +static int drive_index_to_unit_id(BlockInterfaceType type, int index) +{ + int max_devs = if_max_devs[type]; + return max_devs ? index % max_devs : index; +} + +QemuOpts *drive_def(const char *optstr) +{ + return qemu_opts_parse(qemu_find_opts("drive"), optstr, 0); +} + +QemuOpts *drive_add(BlockInterfaceType type, int index, const char *file, + const char *optstr) +{ + QemuOpts *opts; + char buf[32]; + + opts = drive_def(optstr); + if (!opts) { + return NULL; + } + if (type != IF_DEFAULT) { + qemu_opt_set(opts, "if", if_name[type]); + } + if (index >= 0) { + snprintf(buf, sizeof(buf), "%d", index); + qemu_opt_set(opts, "index", buf); + } + if (file) + qemu_opt_set(opts, "file", file); + return opts; +} + +DriveInfo *drive_get(BlockInterfaceType type, int bus, int unit) +{ + DriveInfo *dinfo; + + /* seek interface, bus and unit */ + + QTAILQ_FOREACH(dinfo, &drives, next) { + if (dinfo->type == type && + dinfo->bus == bus && + dinfo->unit == unit) + return dinfo; + } + + return NULL; +} + +DriveInfo *drive_get_by_index(BlockInterfaceType type, int index) +{ + return drive_get(type, + drive_index_to_bus_id(type, index), + drive_index_to_unit_id(type, index)); +} + +int drive_get_max_bus(BlockInterfaceType type) +{ + int max_bus; + DriveInfo *dinfo; + + max_bus = -1; + QTAILQ_FOREACH(dinfo, &drives, next) { + if(dinfo->type == type && + dinfo->bus > max_bus) + max_bus = dinfo->bus; + } + return max_bus; +} + +/* Get a block device. This should only be used for single-drive devices + (e.g. SD/Floppy/MTD). Multi-disk devices (scsi/ide) should use the + appropriate bus. */ +DriveInfo *drive_get_next(BlockInterfaceType type) +{ + static int next_block_unit[IF_COUNT]; + + return drive_get(type, 0, next_block_unit[type]++); +} + +DriveInfo *drive_get_by_blockdev(BlockDriverState *bs) +{ + DriveInfo *dinfo; + + QTAILQ_FOREACH(dinfo, &drives, next) { + if (dinfo->bdrv == bs) { + return dinfo; + } + } + return NULL; +} + +static void bdrv_format_print(void *opaque, const char *name) +{ + error_printf(" %s", name); +} + +static void drive_uninit(DriveInfo *dinfo) +{ + qemu_opts_del(dinfo->opts); + bdrv_delete(dinfo->bdrv); + qemu_free(dinfo->id); + QTAILQ_REMOVE(&drives, dinfo, next); + qemu_free(dinfo); +} + +void drive_put_ref(DriveInfo *dinfo) +{ + assert(dinfo->refcount); + if (--dinfo->refcount == 0) { + drive_uninit(dinfo); + } +} + +void drive_get_ref(DriveInfo *dinfo) +{ + dinfo->refcount++; +} + +static int parse_block_error_action(const char *buf, int is_read) +{ + if (!strcmp(buf, "ignore")) { + return BLOCK_ERR_IGNORE; + } else if (!is_read && !strcmp(buf, "enospc")) { + return BLOCK_ERR_STOP_ENOSPC; + } else if (!strcmp(buf, "stop")) { + return BLOCK_ERR_STOP_ANY; + } else if (!strcmp(buf, "report")) { + return BLOCK_ERR_REPORT; + } else { + error_report("'%s' invalid %s error action", + buf, is_read ? "read" : "write"); + return -1; + } +} + +DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi) +{ + const char *buf; + const char *file = NULL; + char devname[128]; + const char *serial; + const char *mediastr = ""; + BlockInterfaceType type; + enum { MEDIA_DISK, MEDIA_CDROM } media; + int bus_id, unit_id; + int cyls, heads, secs, translation; + BlockDriver *drv = NULL; + int max_devs; + int index; + int ro = 0; + int bdrv_flags = 0; + int on_read_error, on_write_error; + const char *devaddr; + DriveInfo *dinfo; + int is_extboot = 0; + int snapshot = 0; + int ret; + + translation = BIOS_ATA_TRANSLATION_AUTO; + + if (default_to_scsi) { + type = IF_SCSI; + pstrcpy(devname, sizeof(devname), "scsi"); + } else { + type = IF_IDE; + pstrcpy(devname, sizeof(devname), "ide"); + } + media = MEDIA_DISK; + + /* extract parameters */ + bus_id = qemu_opt_get_number(opts, "bus", 0); + unit_id = qemu_opt_get_number(opts, "unit", -1); + index = qemu_opt_get_number(opts, "index", -1); + + cyls = qemu_opt_get_number(opts, "cyls", 0); + heads = qemu_opt_get_number(opts, "heads", 0); + secs = qemu_opt_get_number(opts, "secs", 0); + + snapshot = qemu_opt_get_bool(opts, "snapshot", 0); + ro = qemu_opt_get_bool(opts, "readonly", 0); + + file = qemu_opt_get(opts, "file"); + serial = qemu_opt_get(opts, "serial"); + + if ((buf = qemu_opt_get(opts, "if")) != NULL) { + pstrcpy(devname, sizeof(devname), buf); + for (type = 0; type < IF_COUNT && strcmp(buf, if_name[type]); type++) + ; + if (type == IF_COUNT) { + error_report("unsupported bus type '%s'", buf); + return NULL; + } + } + max_devs = if_max_devs[type]; + + if (cyls || heads || secs) { + if (cyls < 1 || (type == IF_IDE && cyls > 16383)) { + error_report("invalid physical cyls number"); + return NULL; + } + if (heads < 1 || (type == IF_IDE && heads > 16)) { + error_report("invalid physical heads number"); + return NULL; + } + if (secs < 1 || (type == IF_IDE && secs > 63)) { + error_report("invalid physical secs number"); + return NULL; + } + } + + if ((buf = qemu_opt_get(opts, "trans")) != NULL) { + if (!cyls) { + error_report("'%s' trans must be used with cyls,heads and secs", + buf); + return NULL; + } + if (!strcmp(buf, "none")) + translation = BIOS_ATA_TRANSLATION_NONE; + else if (!strcmp(buf, "lba")) + translation = BIOS_ATA_TRANSLATION_LBA; + else if (!strcmp(buf, "auto")) + translation = BIOS_ATA_TRANSLATION_AUTO; + else { + error_report("'%s' invalid translation type", buf); + return NULL; + } + } + + if ((buf = qemu_opt_get(opts, "media")) != NULL) { + if (!strcmp(buf, "disk")) { + media = MEDIA_DISK; + } else if (!strcmp(buf, "cdrom")) { + if (cyls || secs || heads) { + error_report("'%s' invalid physical CHS format", buf); + return NULL; + } + media = MEDIA_CDROM; + } else { + error_report("'%s' invalid media", buf); + return NULL; + } + } + + if ((buf = qemu_opt_get(opts, "cache")) != NULL) { + if (!strcmp(buf, "off") || !strcmp(buf, "none")) { + bdrv_flags |= BDRV_O_NOCACHE; + } else if (!strcmp(buf, "writeback")) { + bdrv_flags |= BDRV_O_CACHE_WB; + } else if (!strcmp(buf, "unsafe")) { + bdrv_flags |= BDRV_O_CACHE_WB; + bdrv_flags |= BDRV_O_NO_FLUSH; + } else if (!strcmp(buf, "writethrough")) { + /* this is the default */ + } else { + error_report("invalid cache option"); + return NULL; + } + } + +#ifdef CONFIG_LINUX_AIO + if ((buf = qemu_opt_get(opts, "aio")) != NULL) { + if (!strcmp(buf, "native")) { + bdrv_flags |= BDRV_O_NATIVE_AIO; + } else if (!strcmp(buf, "threads")) { + /* this is the default */ + } else { + error_report("invalid aio option"); + return NULL; + } + } +#endif + + if ((buf = qemu_opt_get(opts, "format")) != NULL) { + if (strcmp(buf, "?") == 0) { + error_printf("Supported formats:"); + bdrv_iterate_format(bdrv_format_print, NULL); + error_printf("\n"); + return NULL; + } + drv = bdrv_find_whitelisted_format(buf); + if (!drv) { + error_report("'%s' invalid format", buf); + return NULL; + } + } + + is_extboot = qemu_opt_get_bool(opts, "boot", 0); + if (is_extboot && extboot_drive) { + fprintf(stderr, "qemu: two bootable drives specified\n"); + return NULL; + } + + on_write_error = BLOCK_ERR_STOP_ENOSPC; + if ((buf = qemu_opt_get(opts, "werror")) != NULL) { + if (type != IF_IDE && type != IF_SCSI && type != IF_VIRTIO && type != IF_NONE) { + error_report("werror is not supported by this bus type"); + return NULL; + } + + on_write_error = parse_block_error_action(buf, 0); + if (on_write_error < 0) { + return NULL; + } + } + + on_read_error = BLOCK_ERR_REPORT; + if ((buf = qemu_opt_get(opts, "rerror")) != NULL) { + if (type != IF_IDE && type != IF_VIRTIO && type != IF_SCSI && type != IF_NONE) { + error_report("rerror is not supported by this bus type"); + return NULL; + } + + on_read_error = parse_block_error_action(buf, 1); + if (on_read_error < 0) { + return NULL; + } + } + + if ((devaddr = qemu_opt_get(opts, "addr")) != NULL) { + if (type != IF_VIRTIO) { + error_report("addr is not supported by this bus type"); + return NULL; + } + } + + /* compute bus and unit according index */ + + if (index != -1) { + if (bus_id != 0 || unit_id != -1) { + error_report("index cannot be used with bus and unit"); + return NULL; + } + bus_id = drive_index_to_bus_id(type, index); + unit_id = drive_index_to_unit_id(type, index); + } + + /* if user doesn't specify a unit_id, + * try to find the first free + */ + + if (unit_id == -1) { + unit_id = 0; + while (drive_get(type, bus_id, unit_id) != NULL) { + unit_id++; + if (max_devs && unit_id >= max_devs) { + unit_id -= max_devs; + bus_id++; + } + } + } + + /* check unit id */ + + if (max_devs && unit_id >= max_devs) { + error_report("unit %d too big (max is %d)", + unit_id, max_devs - 1); + return NULL; + } + + /* + * catch multiple definitions + */ + + if (drive_get(type, bus_id, unit_id) != NULL) { + error_report("drive with bus=%d, unit=%d (index=%d) exists", + bus_id, unit_id, index); + return NULL; + } + + /* init */ + + dinfo = qemu_mallocz(sizeof(*dinfo)); + if ((buf = qemu_opts_id(opts)) != NULL) { + dinfo->id = qemu_strdup(buf); + } else { + /* no id supplied -> create one */ + dinfo->id = qemu_mallocz(32); + if (type == IF_IDE || type == IF_SCSI) + mediastr = (media == MEDIA_CDROM) ? "-cd" : "-hd"; + if (max_devs) + snprintf(dinfo->id, 32, "%s%i%s%i", + devname, bus_id, mediastr, unit_id); + else + snprintf(dinfo->id, 32, "%s%s%i", + devname, mediastr, unit_id); + } + dinfo->bdrv = bdrv_new(dinfo->id); + dinfo->devaddr = devaddr; + dinfo->type = type; + dinfo->bus = bus_id; + dinfo->unit = unit_id; + dinfo->opts = opts; + dinfo->refcount = 1; + if (serial) + strncpy(dinfo->serial, serial, sizeof(dinfo->serial) - 1); + QTAILQ_INSERT_TAIL(&drives, dinfo, next); + + if (is_extboot) { + extboot_drive = dinfo; + } + + bdrv_set_on_error(dinfo->bdrv, on_read_error, on_write_error); + + switch(type) { + case IF_IDE: + case IF_SCSI: + case IF_XEN: + case IF_NONE: + switch(media) { + case MEDIA_DISK: + if (cyls != 0) { + bdrv_set_geometry_hint(dinfo->bdrv, cyls, heads, secs); + bdrv_set_translation_hint(dinfo->bdrv, translation); + } + break; + case MEDIA_CDROM: + bdrv_set_type_hint(dinfo->bdrv, BDRV_TYPE_CDROM); + break; + } + break; + case IF_SD: + /* FIXME: This isn't really a floppy, but it's a reasonable + approximation. */ + case IF_FLOPPY: + bdrv_set_type_hint(dinfo->bdrv, BDRV_TYPE_FLOPPY); + break; + case IF_PFLASH: + case IF_MTD: + break; + case IF_VIRTIO: + /* add virtio block device */ + opts = qemu_opts_create(qemu_find_opts("device"), NULL, 0); + qemu_opt_set(opts, "driver", "virtio-blk-pci"); + qemu_opt_set(opts, "drive", dinfo->id); + if (devaddr) + qemu_opt_set(opts, "addr", devaddr); + break; + default: + abort(); + } + if (!file || !*file) { + return dinfo; + } + if (snapshot) { + /* always use cache=unsafe with snapshot */ + bdrv_flags &= ~BDRV_O_CACHE_MASK; + bdrv_flags |= (BDRV_O_SNAPSHOT|BDRV_O_CACHE_WB|BDRV_O_NO_FLUSH); + } + + if (media == MEDIA_CDROM) { + /* CDROM is fine for any interface, don't check. */ + ro = 1; + } else if (ro == 1) { + if (type != IF_SCSI && type != IF_VIRTIO && type != IF_FLOPPY && type != IF_NONE) { + error_report("readonly not supported by this bus type"); + goto err; + } + } + + bdrv_flags |= ro ? 0 : BDRV_O_RDWR; + + ret = bdrv_open(dinfo->bdrv, file, bdrv_flags, drv); + if (ret < 0) { + error_report("could not open disk image %s: %s", + file, strerror(-ret)); + goto err; + } + + if (bdrv_key_required(dinfo->bdrv)) + autostart = 0; + return dinfo; + +err: + bdrv_delete(dinfo->bdrv); + qemu_free(dinfo->id); + QTAILQ_REMOVE(&drives, dinfo, next); + qemu_free(dinfo); + return NULL; +} + +void do_commit(Monitor *mon, const QDict *qdict) +{ + const char *device = qdict_get_str(qdict, "device"); + BlockDriverState *bs; + + if (!strcmp(device, "all")) { + bdrv_commit_all(); + } else { + bs = bdrv_find(device); + if (!bs) { + qerror_report(QERR_DEVICE_NOT_FOUND, device); + return; + } + bdrv_commit(bs); + } +} + +int do_snapshot_blkdev(Monitor *mon, const QDict *qdict, QObject **ret_data) +{ + const char *device = qdict_get_str(qdict, "device"); + const char *filename = qdict_get_try_str(qdict, "snapshot_file"); + const char *format = qdict_get_try_str(qdict, "format"); + BlockDriverState *bs; + BlockDriver *drv, *proto_drv; + int ret = 0; + int flags; + + if (!filename) { + qerror_report(QERR_MISSING_PARAMETER, "snapshot_file"); + ret = -1; + goto out; + } + + bs = bdrv_find(device); + if (!bs) { + qerror_report(QERR_DEVICE_NOT_FOUND, device); + ret = -1; + goto out; + } + + if (!format) { + format = "qcow2"; + } + + drv = bdrv_find_format(format); + if (!drv) { + qerror_report(QERR_INVALID_BLOCK_FORMAT, format); + ret = -1; + goto out; + } + + proto_drv = bdrv_find_protocol(filename); + if (!proto_drv) { + qerror_report(QERR_INVALID_BLOCK_FORMAT, format); + ret = -1; + goto out; + } + + ret = bdrv_img_create(filename, format, bs->filename, + bs->drv->format_name, NULL, -1, bs->open_flags); + if (ret) { + goto out; + } + + qemu_aio_flush(); + bdrv_flush(bs); + + flags = bs->open_flags; + bdrv_close(bs); + ret = bdrv_open(bs, filename, flags, drv); + /* + * If reopening the image file we just created fails, we really + * are in trouble :( + */ + if (ret != 0) { + abort(); + } +out: + if (ret) { + ret = -1; + } + + return ret; +} + +static int eject_device(Monitor *mon, BlockDriverState *bs, int force) +{ + if (!force) { + if (!bdrv_is_removable(bs)) { + qerror_report(QERR_DEVICE_NOT_REMOVABLE, + bdrv_get_device_name(bs)); + return -1; + } + if (bdrv_is_locked(bs)) { + qerror_report(QERR_DEVICE_LOCKED, bdrv_get_device_name(bs)); + return -1; + } + } + bdrv_close(bs); + return 0; +} + +int do_eject(Monitor *mon, const QDict *qdict, QObject **ret_data) +{ + BlockDriverState *bs; + int force = qdict_get_try_bool(qdict, "force", 0); + const char *filename = qdict_get_str(qdict, "device"); + + bs = bdrv_find(filename); + if (!bs) { + qerror_report(QERR_DEVICE_NOT_FOUND, filename); + return -1; + } + return eject_device(mon, bs, force); +} + +int do_block_set_passwd(Monitor *mon, const QDict *qdict, + QObject **ret_data) +{ + BlockDriverState *bs; + int err; + + bs = bdrv_find(qdict_get_str(qdict, "device")); + if (!bs) { + qerror_report(QERR_DEVICE_NOT_FOUND, qdict_get_str(qdict, "device")); + return -1; + } + + err = bdrv_set_key(bs, qdict_get_str(qdict, "password")); + if (err == -EINVAL) { + qerror_report(QERR_DEVICE_NOT_ENCRYPTED, bdrv_get_device_name(bs)); + return -1; + } else if (err < 0) { + qerror_report(QERR_INVALID_PASSWORD); + return -1; + } + + return 0; +} + +int do_change_block(Monitor *mon, const char *device, + const char *filename, const char *fmt) +{ + BlockDriverState *bs; + BlockDriver *drv = NULL; + int bdrv_flags; + + bs = bdrv_find(device); + if (!bs) { + qerror_report(QERR_DEVICE_NOT_FOUND, device); + return -1; + } + if (fmt) { + drv = bdrv_find_whitelisted_format(fmt); + if (!drv) { + qerror_report(QERR_INVALID_BLOCK_FORMAT, fmt); + return -1; + } + } + if (eject_device(mon, bs, 0) < 0) { + return -1; + } + bdrv_flags = bdrv_is_read_only(bs) ? 0 : BDRV_O_RDWR; + bdrv_flags |= bdrv_is_snapshot(bs) ? BDRV_O_SNAPSHOT : 0; + if (bdrv_open(bs, filename, bdrv_flags, drv) < 0) { + qerror_report(QERR_OPEN_FILE_FAILED, filename); + return -1; + } + return monitor_read_bdrv_key_start(mon, bs, NULL, NULL); +} + +int do_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data) +{ + const char *id = qdict_get_str(qdict, "id"); + BlockDriverState *bs; + + bs = bdrv_find(id); + if (!bs) { + qerror_report(QERR_DEVICE_NOT_FOUND, id); + return -1; + } + if (bdrv_in_use(bs)) { + qerror_report(QERR_DEVICE_IN_USE, id); + return -1; + } + + /* quiesce block driver; prevent further io */ + qemu_aio_flush(); + bdrv_flush(bs); + bdrv_close(bs); + + /* if we have a device associated with this BlockDriverState (bs->peer) + * then we need to make the drive anonymous until the device + * can be removed. If this is a drive with no device backing + * then we can just get rid of the block driver state right here. + */ + if (bs->peer) { + bdrv_make_anon(bs); + } else { + drive_uninit(drive_get_by_blockdev(bs)); + } + + return 0; +} + +/* + * XXX: replace the QERR_UNDEFINED_ERROR errors with real values once the + * existing QERR_ macro mess is cleaned up. A good example for better + * error reports can be found in the qemu-img resize code. + */ +int do_block_resize(Monitor *mon, const QDict *qdict, QObject **ret_data) +{ + const char *device = qdict_get_str(qdict, "device"); + int64_t size = qdict_get_int(qdict, "size"); + BlockDriverState *bs; + + bs = bdrv_find(device); + if (!bs) { + qerror_report(QERR_DEVICE_NOT_FOUND, device); + return -1; + } + + if (size < 0) { + qerror_report(QERR_UNDEFINED_ERROR); + return -1; + } + + if (bdrv_truncate(bs, size)) { + qerror_report(QERR_UNDEFINED_ERROR); + return -1; + } + + return 0; +} diff -Nru qemu-kvm-0.12.5+noroms/blockdev.h qemu-kvm-0.14.1/blockdev.h --- qemu-kvm-0.12.5+noroms/blockdev.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/blockdev.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,70 @@ +/* + * QEMU host block devices + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + */ + +#ifndef BLOCKDEV_H +#define BLOCKDEV_H + +#include "block.h" +#include "qemu-queue.h" + +void blockdev_mark_auto_del(BlockDriverState *bs); +void blockdev_auto_del(BlockDriverState *bs); + +#define BLOCK_SERIAL_STRLEN 20 + +typedef enum { + IF_DEFAULT = -1, /* for use with drive_add() only */ + IF_NONE, + IF_IDE, IF_SCSI, IF_FLOPPY, IF_PFLASH, IF_MTD, IF_SD, IF_VIRTIO, IF_XEN, + IF_COUNT +} BlockInterfaceType; + +struct DriveInfo { + BlockDriverState *bdrv; + char *id; + const char *devaddr; + BlockInterfaceType type; + int bus; + int unit; + int auto_del; /* see blockdev_mark_auto_del() */ + QemuOpts *opts; + char serial[BLOCK_SERIAL_STRLEN + 1]; + QTAILQ_ENTRY(DriveInfo) next; + int refcount; +}; + +DriveInfo *drive_get(BlockInterfaceType type, int bus, int unit); +DriveInfo *drive_get_by_index(BlockInterfaceType type, int index); +int drive_get_max_bus(BlockInterfaceType type); +DriveInfo *drive_get_next(BlockInterfaceType type); +void drive_get_ref(DriveInfo *dinfo); +void drive_put_ref(DriveInfo *dinfo); +DriveInfo *drive_get_by_blockdev(BlockDriverState *bs); + +QemuOpts *drive_def(const char *optstr); +QemuOpts *drive_add(BlockInterfaceType type, int index, const char *file, + const char *optstr); +DriveInfo *drive_init(QemuOpts *arg, int default_to_scsi); + +/* device-hotplug */ + +DriveInfo *add_init_drive(const char *opts); + +void do_commit(Monitor *mon, const QDict *qdict); +int do_eject(Monitor *mon, const QDict *qdict, QObject **ret_data); +int do_block_set_passwd(Monitor *mon, const QDict *qdict, QObject **ret_data); +int do_change_block(Monitor *mon, const char *device, + const char *filename, const char *fmt); +int do_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data); +int do_snapshot_blkdev(Monitor *mon, const QDict *qdict, QObject **ret_data); +int do_block_resize(Monitor *mon, const QDict *qdict, QObject **ret_data); + +extern DriveInfo *extboot_drive; + +#endif diff -Nru qemu-kvm-0.12.5+noroms/block.h qemu-kvm-0.14.1/block.h --- qemu-kvm-0.12.5+noroms/block.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/block.h 2011-05-11 13:29:46.000000000 +0000 @@ -27,25 +27,31 @@ uint64_t vm_clock_nsec; /* VM clock relative to boot */ } QEMUSnapshotInfo; -#define BDRV_O_RDONLY 0x0000 #define BDRV_O_RDWR 0x0002 -#define BDRV_O_ACCESS 0x0003 -#define BDRV_O_CREAT 0x0004 /* create an empty file */ #define BDRV_O_SNAPSHOT 0x0008 /* open the file read only and save writes in a snapshot */ -#define BDRV_O_FILE 0x0010 /* open as a raw file (do not try to - use a disk image format on top of - it (default for - bdrv_file_open()) */ #define BDRV_O_NOCACHE 0x0020 /* do not use the host page cache */ #define BDRV_O_CACHE_WB 0x0040 /* use write-back caching */ #define BDRV_O_NATIVE_AIO 0x0080 /* use native AIO instead of the thread pool */ +#define BDRV_O_NO_BACKING 0x0100 /* don't open the backing file */ +#define BDRV_O_NO_FLUSH 0x0200 /* disable flushing on this disk */ -#define BDRV_O_CACHE_MASK (BDRV_O_NOCACHE | BDRV_O_CACHE_WB) +#define BDRV_O_CACHE_MASK (BDRV_O_NOCACHE | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH) #define BDRV_SECTOR_BITS 9 -#define BDRV_SECTOR_SIZE (1 << BDRV_SECTOR_BITS) -#define BDRV_SECTOR_MASK ~(BDRV_SECTOR_SIZE - 1); +#define BDRV_SECTOR_SIZE (1ULL << BDRV_SECTOR_BITS) +#define BDRV_SECTOR_MASK ~(BDRV_SECTOR_SIZE - 1) +typedef enum { + BLOCK_ERR_REPORT, BLOCK_ERR_IGNORE, BLOCK_ERR_STOP_ENOSPC, + BLOCK_ERR_STOP_ANY +} BlockErrorAction; + +typedef enum { + BDRV_ACTION_REPORT, BDRV_ACTION_IGNORE, BDRV_ACTION_STOP +} BlockMonEventAction; + +void bdrv_mon_event(const BlockDriverState *bdrv, + BlockMonEventAction action, int is_read); void bdrv_info_print(Monitor *mon, const QObject *data); void bdrv_info(Monitor *mon, QObject **ret_data); void bdrv_stats_print(Monitor *mon, const QObject *data); @@ -53,22 +59,22 @@ void bdrv_init(void); void bdrv_init_with_whitelist(void); +BlockDriver *bdrv_find_protocol(const char *filename); BlockDriver *bdrv_find_format(const char *format_name); BlockDriver *bdrv_find_whitelisted_format(const char *format_name); int bdrv_create(BlockDriver *drv, const char* filename, QEMUOptionParameter *options); -int bdrv_create2(BlockDriver *drv, - const char *filename, int64_t size_in_sectors, - const char *backing_file, const char *backing_format, - int flags); +int bdrv_create_file(const char* filename, QEMUOptionParameter *options); BlockDriverState *bdrv_new(const char *device_name); +void bdrv_make_anon(BlockDriverState *bs); void bdrv_delete(BlockDriverState *bs); int bdrv_file_open(BlockDriverState **pbs, const char *filename, int flags); -int bdrv_open(BlockDriverState *bs, const char *filename, int flags); -int bdrv_open2(BlockDriverState *bs, const char *filename, int flags, - BlockDriver *drv); +int bdrv_open(BlockDriverState *bs, const char *filename, int flags, + BlockDriver *drv); void bdrv_close(BlockDriverState *bs); -int bdrv_check(BlockDriverState *bs); +int bdrv_attach(BlockDriverState *bs, DeviceState *qdev); +void bdrv_detach(BlockDriverState *bs, DeviceState *qdev); +DeviceState *bdrv_get_attached(BlockDriverState *bs); int bdrv_read(BlockDriverState *bs, int64_t sector_num, uint8_t *buf, int nb_sectors); int bdrv_write(BlockDriverState *bs, int64_t sector_num, @@ -86,8 +92,20 @@ void bdrv_get_geometry(BlockDriverState *bs, uint64_t *nb_sectors_ptr); void bdrv_guess_geometry(BlockDriverState *bs, int *pcyls, int *pheads, int *psecs); int bdrv_commit(BlockDriverState *bs); +void bdrv_commit_all(void); +int bdrv_change_backing_file(BlockDriverState *bs, + const char *backing_file, const char *backing_fmt); void bdrv_register(BlockDriver *bdrv); + +typedef struct BdrvCheckResult { + int corruptions; + int leaks; + int check_errors; +} BdrvCheckResult; + +int bdrv_check(BlockDriverState *bs, BdrvCheckResult *res); + /* async block I/O */ typedef struct BlockDriverAIOCB BlockDriverAIOCB; typedef void BlockDriverCompletionFunc(void *opaque, int ret); @@ -125,9 +143,12 @@ BlockDriverCompletionFunc *cb, void *opaque); /* Ensure contents are flushed to disk. */ -void bdrv_flush(BlockDriverState *bs); +int bdrv_flush(BlockDriverState *bs); void bdrv_flush_all(void); +void bdrv_close_all(void); +int bdrv_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors); +int bdrv_has_zero_init(BlockDriverState *bs); int bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num, int nb_sectors, int *pnum); @@ -148,9 +169,12 @@ int *pcyls, int *pheads, int *psecs); int bdrv_get_type_hint(BlockDriverState *bs); int bdrv_get_translation_hint(BlockDriverState *bs); +void bdrv_set_on_error(BlockDriverState *bs, BlockErrorAction on_read_error, + BlockErrorAction on_write_error); +BlockErrorAction bdrv_get_on_error(BlockDriverState *bs, int is_read); +void bdrv_set_removable(BlockDriverState *bs, int removable); int bdrv_is_removable(BlockDriverState *bs); int bdrv_is_read_only(BlockDriverState *bs); -int bdrv_set_read_only(BlockDriverState *bs, int read_only); int bdrv_is_sg(BlockDriverState *bs); int bdrv_enable_write_cache(BlockDriverState *bs); int bdrv_is_inserted(BlockDriverState *bs); @@ -159,9 +183,11 @@ void bdrv_set_locked(BlockDriverState *bs, int locked); int bdrv_eject(BlockDriverState *bs, int eject_flag); void bdrv_set_change_cb(BlockDriverState *bs, - void (*change_cb)(void *opaque), void *opaque); + void (*change_cb)(void *opaque, int reason), + void *opaque); void bdrv_get_format(BlockDriverState *bs, char *buf, int buf_size); BlockDriverState *bdrv_find(const char *name); +BlockDriverState *bdrv_next(BlockDriverState *bs); void bdrv_iterate(void (*it)(void *opaque, BlockDriverState *bs), void *opaque); int bdrv_is_encrypted(BlockDriverState *bs); @@ -178,6 +204,9 @@ const char *bdrv_get_encrypted_filename(BlockDriverState *bs); void bdrv_get_backing_filename(BlockDriverState *bs, char *filename, int filename_size); +int bdrv_can_snapshot(BlockDriverState *bs); +int bdrv_is_snapshot(BlockDriverState *bs); +BlockDriverState *bdrv_snapshots(void); int bdrv_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info); int bdrv_snapshot_goto(BlockDriverState *bs, @@ -185,6 +214,8 @@ int bdrv_snapshot_delete(BlockDriverState *bs, const char *snapshot_id); int bdrv_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_info); +int bdrv_snapshot_load_tmp(BlockDriverState *bs, + const char *snapshot_name); char *bdrv_snapshot_dump(char *buf, int buf_size, QEMUSnapshotInfo *sn); char *get_human_readable_size(char *buf, int buf_size, int64_t size); @@ -199,10 +230,70 @@ int bdrv_load_vmstate(BlockDriverState *bs, uint8_t *buf, int64_t pos, int size); +int bdrv_img_create(const char *filename, const char *fmt, + const char *base_filename, const char *base_fmt, + char *options, uint64_t img_size, int flags); + #define BDRV_SECTORS_PER_DIRTY_CHUNK 2048 void bdrv_set_dirty_tracking(BlockDriverState *bs, int enable); int bdrv_get_dirty(BlockDriverState *bs, int64_t sector); void bdrv_reset_dirty(BlockDriverState *bs, int64_t cur_sector, int nr_sectors); +int64_t bdrv_get_dirty_count(BlockDriverState *bs); + +void bdrv_set_in_use(BlockDriverState *bs, int in_use); +int bdrv_in_use(BlockDriverState *bs); + +typedef enum { + BLKDBG_L1_UPDATE, + + BLKDBG_L1_GROW_ALLOC_TABLE, + BLKDBG_L1_GROW_WRITE_TABLE, + BLKDBG_L1_GROW_ACTIVATE_TABLE, + + BLKDBG_L2_LOAD, + BLKDBG_L2_UPDATE, + BLKDBG_L2_UPDATE_COMPRESSED, + BLKDBG_L2_ALLOC_COW_READ, + BLKDBG_L2_ALLOC_WRITE, + + BLKDBG_READ, + BLKDBG_READ_AIO, + BLKDBG_READ_BACKING, + BLKDBG_READ_BACKING_AIO, + BLKDBG_READ_COMPRESSED, + + BLKDBG_WRITE_AIO, + BLKDBG_WRITE_COMPRESSED, + + BLKDBG_VMSTATE_LOAD, + BLKDBG_VMSTATE_SAVE, + + BLKDBG_COW_READ, + BLKDBG_COW_WRITE, + + BLKDBG_REFTABLE_LOAD, + BLKDBG_REFTABLE_GROW, + + BLKDBG_REFBLOCK_LOAD, + BLKDBG_REFBLOCK_UPDATE, + BLKDBG_REFBLOCK_UPDATE_PART, + BLKDBG_REFBLOCK_ALLOC, + BLKDBG_REFBLOCK_ALLOC_HOOKUP, + BLKDBG_REFBLOCK_ALLOC_WRITE, + BLKDBG_REFBLOCK_ALLOC_WRITE_BLOCKS, + BLKDBG_REFBLOCK_ALLOC_WRITE_TABLE, + BLKDBG_REFBLOCK_ALLOC_SWITCH_TABLE, + + BLKDBG_CLUSTER_ALLOC, + BLKDBG_CLUSTER_ALLOC_BYTES, + BLKDBG_CLUSTER_FREE, + + BLKDBG_EVENT_MAX, +} BlkDebugEvent; + +#define BLKDBG_EVENT(bs, evt) bdrv_debug_event(bs, evt) +void bdrv_debug_event(BlockDriverState *bs, BlkDebugEvent event); + #endif diff -Nru qemu-kvm-0.12.5+noroms/block_int.h qemu-kvm-0.14.1/block_int.h --- qemu-kvm-0.12.5+noroms/block_int.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/block_int.h 2011-05-11 13:29:46.000000000 +0000 @@ -26,9 +26,9 @@ #include "block.h" #include "qemu-option.h" +#include "qemu-queue.h" #define BLOCK_FLAG_ENCRYPT 1 -#define BLOCK_FLAG_COMPRESS 2 #define BLOCK_FLAG_COMPAT6 4 #define BLOCK_OPT_SIZE "size" @@ -37,6 +37,7 @@ #define BLOCK_OPT_BACKING_FILE "backing_file" #define BLOCK_OPT_BACKING_FMT "backing_fmt" #define BLOCK_OPT_CLUSTER_SIZE "cluster_size" +#define BLOCK_OPT_TABLE_SIZE "table_size" #define BLOCK_OPT_PREALLOC "preallocation" typedef struct AIOPool { @@ -50,14 +51,15 @@ int instance_size; int (*bdrv_probe)(const uint8_t *buf, int buf_size, const char *filename); int (*bdrv_probe_device)(const char *filename); - int (*bdrv_open)(BlockDriverState *bs, const char *filename, int flags); + int (*bdrv_open)(BlockDriverState *bs, int flags); + int (*bdrv_file_open)(BlockDriverState *bs, const char *filename, int flags); int (*bdrv_read)(BlockDriverState *bs, int64_t sector_num, uint8_t *buf, int nb_sectors); int (*bdrv_write)(BlockDriverState *bs, int64_t sector_num, const uint8_t *buf, int nb_sectors); void (*bdrv_close)(BlockDriverState *bs); int (*bdrv_create)(const char *filename, QEMUOptionParameter *options); - void (*bdrv_flush)(BlockDriverState *bs); + int (*bdrv_flush)(BlockDriverState *bs); int (*bdrv_is_allocated)(BlockDriverState *bs, int64_t sector_num, int nb_sectors, int *pnum); int (*bdrv_set_key)(BlockDriverState *bs, const char *key); @@ -71,6 +73,8 @@ BlockDriverCompletionFunc *cb, void *opaque); BlockDriverAIOCB *(*bdrv_aio_flush)(BlockDriverState *bs, BlockDriverCompletionFunc *cb, void *opaque); + int (*bdrv_discard)(BlockDriverState *bs, int64_t sector_num, + int nb_sectors); int (*bdrv_aio_multiwrite)(BlockDriverState *bs, BlockRequest *reqs, int num_reqs); @@ -91,6 +95,8 @@ int (*bdrv_snapshot_delete)(BlockDriverState *bs, const char *snapshot_id); int (*bdrv_snapshot_list)(BlockDriverState *bs, QEMUSnapshotInfo **psn_info); + int (*bdrv_snapshot_load_tmp)(BlockDriverState *bs, + const char *snapshot_name); int (*bdrv_get_info)(BlockDriverState *bs, BlockDriverInfo *bdi); int (*bdrv_save_vmstate)(BlockDriverState *bs, const uint8_t *buf, @@ -98,6 +104,9 @@ int (*bdrv_load_vmstate)(BlockDriverState *bs, uint8_t *buf, int64_t pos, int size); + int (*bdrv_change_backing_file)(BlockDriverState *bs, + const char *backing_file, const char *backing_fmt); + /* removable device specific */ int (*bdrv_is_inserted)(BlockDriverState *bs); int (*bdrv_media_changed)(BlockDriverState *bs); @@ -114,32 +123,44 @@ QEMUOptionParameter *create_options; - /* Returns number of errors in image, -errno for internal errors */ - int (*bdrv_check)(BlockDriverState* bs); - - /* Set if newly created images are not guaranteed to contain only zeros */ - int no_zero_init; + /* + * Returns 0 for completed check, -errno for internal errors. + * The check results are stored in result. + */ + int (*bdrv_check)(BlockDriverState* bs, BdrvCheckResult *result); + + void (*bdrv_debug_event)(BlockDriverState *bs, BlkDebugEvent event); + + /* + * Returns 1 if newly created images are guaranteed to contain only + * zeros, 0 otherwise. + */ + int (*bdrv_has_zero_init)(BlockDriverState *bs); - struct BlockDriver *next; + QLIST_ENTRY(BlockDriver) list; }; struct BlockDriverState { int64_t total_sectors; /* if we are reading a disk image, give its size in sectors */ int read_only; /* if true, the media is read only */ + int keep_read_only; /* if true, the media was requested to stay read only */ int open_flags; /* flags used to open the file, re-used for re-open */ int removable; /* if true, the media can be removed */ int locked; /* if true, the media cannot temporarily be ejected */ + int tray_open; /* if true, the virtual tray is open */ int encrypted; /* if true, the media is encrypted */ int valid_key; /* if true, a valid encryption key has been set */ int sg; /* if true, the device is a /dev/sg* */ /* event callback when inserting/removing */ - void (*change_cb)(void *opaque); + void (*change_cb)(void *opaque, int reason); void *change_opaque; BlockDriver *drv; /* NULL means no media */ void *opaque; + DeviceState *peer; + char filename[1024]; char backing_file[1024]; /* if non zero, the image is a diff of this file image */ @@ -148,6 +169,8 @@ int media_changed; BlockDriverState *backing_hd; + BlockDriverState *file; + /* async read/write emulation */ void *sync_aiocb; @@ -157,6 +180,7 @@ uint64_t wr_bytes; uint64_t rd_ops; uint64_t wr_ops; + uint64_t wr_highest_sector; /* Whether the disk can expand beyond total_sectors */ int growable; @@ -171,12 +195,18 @@ drivers. They are not used by the block driver */ int cyls, heads, secs, translation; int type; + BlockErrorAction on_read_error, on_write_error; char device_name[32]; unsigned long *dirty_bitmap; - BlockDriverState *next; + int64_t dirty_count; + int in_use; /* users other than guest access, eg. block migration */ + QTAILQ_ENTRY(BlockDriverState) list; void *private; }; +#define CHANGE_MEDIA 0x01 +#define CHANGE_SIZE 0x02 + struct BlockDriverAIOCB { AIOPool *pool; BlockDriverState *bs; @@ -193,10 +223,43 @@ void *qemu_blockalign(BlockDriverState *bs, size_t size); -extern BlockDriverState *bdrv_first; - #ifdef _WIN32 int is_windows_drive(const char *filename); #endif +typedef struct BlockConf { + BlockDriverState *bs; + uint16_t physical_block_size; + uint16_t logical_block_size; + uint16_t min_io_size; + uint32_t opt_io_size; + int32_t bootindex; + uint32_t discard_granularity; +} BlockConf; + +static inline unsigned int get_physical_block_exp(BlockConf *conf) +{ + unsigned int exp = 0, size; + + for (size = conf->physical_block_size; + size > conf->logical_block_size; + size >>= 1) { + exp++; + } + + return exp; +} + +#define DEFINE_BLOCK_PROPERTIES(_state, _conf) \ + DEFINE_PROP_DRIVE("drive", _state, _conf.bs), \ + DEFINE_PROP_UINT16("logical_block_size", _state, \ + _conf.logical_block_size, 512), \ + DEFINE_PROP_UINT16("physical_block_size", _state, \ + _conf.physical_block_size, 512), \ + DEFINE_PROP_UINT16("min_io_size", _state, _conf.min_io_size, 0), \ + DEFINE_PROP_UINT32("opt_io_size", _state, _conf.opt_io_size, 0), \ + DEFINE_PROP_INT32("bootindex", _state, _conf.bootindex, -1), \ + DEFINE_PROP_UINT32("discard_granularity", _state, \ + _conf.discard_granularity, 0) + #endif /* BLOCK_INT_H */ diff -Nru qemu-kvm-0.12.5+noroms/block-migration.c qemu-kvm-0.14.1/block-migration.c --- qemu-kvm-0.12.5+noroms/block-migration.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/block-migration.c 2011-05-11 13:29:46.000000000 +0000 @@ -15,8 +15,11 @@ #include "block_int.h" #include "hw/hw.h" #include "qemu-queue.h" +#include "qemu-timer.h" #include "monitor.h" #include "block-migration.h" +#include "migration.h" +#include "blockdev.h" #include #define BLOCK_SIZE (BDRV_SECTORS_PER_DIRTY_CHUNK << BDRV_SECTOR_BITS) @@ -26,17 +29,14 @@ #define BLK_MIG_FLAG_PROGRESS 0x04 #define MAX_IS_ALLOCATED_SEARCH 65536 -#define MAX_BLOCKS_READ 10000 -#define BLOCKS_READ_CHANGE 100 -#define INITIAL_BLOCKS_READ 100 //#define DEBUG_BLK_MIGRATION #ifdef DEBUG_BLK_MIGRATION -#define dprintf(fmt, ...) \ +#define DPRINTF(fmt, ...) \ do { printf("blk_migration: " fmt, ## __VA_ARGS__); } while (0) #else -#define dprintf(fmt, ...) \ +#define DPRINTF(fmt, ...) \ do { } while (0) #endif @@ -45,20 +45,24 @@ int bulk_completed; int shared_base; int64_t cur_sector; + int64_t cur_dirty; int64_t completed_sectors; int64_t total_sectors; int64_t dirty; QSIMPLEQ_ENTRY(BlkMigDevState) entry; + unsigned long *aio_bitmap; } BlkMigDevState; typedef struct BlkMigBlock { uint8_t *buf; BlkMigDevState *bmds; int64_t sector; + int nr_sectors; struct iovec iov; QEMUIOVector qiov; BlockDriverAIOCB *aiocb; int ret; + int64_t time; QSIMPLEQ_ENTRY(BlkMigBlock) entry; } BlkMigBlock; @@ -72,6 +76,9 @@ int transferred; int64_t total_sector_sum; int prev_progress; + int bulk_completed; + long double total_time; + int reads; } BlkMigState; static BlkMigState block_mig_state; @@ -124,13 +131,76 @@ return sum << BDRV_SECTOR_BITS; } +static inline void add_avg_read_time(int64_t time) +{ + block_mig_state.reads++; + block_mig_state.total_time += time; +} + +static inline long double compute_read_bwidth(void) +{ + assert(block_mig_state.total_time != 0); + return (block_mig_state.reads * BLOCK_SIZE)/ block_mig_state.total_time; +} + +static int bmds_aio_inflight(BlkMigDevState *bmds, int64_t sector) +{ + int64_t chunk = sector / (int64_t)BDRV_SECTORS_PER_DIRTY_CHUNK; + + if ((sector << BDRV_SECTOR_BITS) < bdrv_getlength(bmds->bs)) { + return !!(bmds->aio_bitmap[chunk / (sizeof(unsigned long) * 8)] & + (1UL << (chunk % (sizeof(unsigned long) * 8)))); + } else { + return 0; + } +} + +static void bmds_set_aio_inflight(BlkMigDevState *bmds, int64_t sector_num, + int nb_sectors, int set) +{ + int64_t start, end; + unsigned long val, idx, bit; + + start = sector_num / BDRV_SECTORS_PER_DIRTY_CHUNK; + end = (sector_num + nb_sectors - 1) / BDRV_SECTORS_PER_DIRTY_CHUNK; + + for (; start <= end; start++) { + idx = start / (sizeof(unsigned long) * 8); + bit = start % (sizeof(unsigned long) * 8); + val = bmds->aio_bitmap[idx]; + if (set) { + val |= 1UL << bit; + } else { + val &= ~(1UL << bit); + } + bmds->aio_bitmap[idx] = val; + } +} + +static void alloc_aio_bitmap(BlkMigDevState *bmds) +{ + BlockDriverState *bs = bmds->bs; + int64_t bitmap_size; + + bitmap_size = (bdrv_getlength(bs) >> BDRV_SECTOR_BITS) + + BDRV_SECTORS_PER_DIRTY_CHUNK * 8 - 1; + bitmap_size /= BDRV_SECTORS_PER_DIRTY_CHUNK * 8; + + bmds->aio_bitmap = qemu_mallocz(bitmap_size); +} + static void blk_mig_read_cb(void *opaque, int ret) { BlkMigBlock *blk = opaque; blk->ret = ret; + blk->time = qemu_get_clock_ns(rt_clock) - blk->time; + + add_avg_read_time(blk->time); + QSIMPLEQ_INSERT_TAIL(&block_mig_state.blk_list, blk, entry); + bmds_set_aio_inflight(blk->bmds, blk->sector, blk->nr_sectors, 0); block_mig_state.submitted--; block_mig_state.read_done++; @@ -138,7 +208,7 @@ } static int mig_save_device_bulk(Monitor *mon, QEMUFile *f, - BlkMigDevState *bmds, int is_async) + BlkMigDevState *bmds) { int64_t total_sectors = bmds->total_sectors; int64_t cur_sector = bmds->cur_sector; @@ -174,27 +244,20 @@ blk->buf = qemu_malloc(BLOCK_SIZE); blk->bmds = bmds; blk->sector = cur_sector; + blk->nr_sectors = nr_sectors; - if (is_async) { - blk->iov.iov_base = blk->buf; - blk->iov.iov_len = nr_sectors * BDRV_SECTOR_SIZE; - qemu_iovec_init_external(&blk->qiov, &blk->iov, 1); - - blk->aiocb = bdrv_aio_readv(bs, cur_sector, &blk->qiov, - nr_sectors, blk_mig_read_cb, blk); - if (!blk->aiocb) { - goto error; - } - block_mig_state.submitted++; - } else { - if (bdrv_read(bs, cur_sector, blk->buf, nr_sectors) < 0) { - goto error; - } - blk_send(f, blk); + blk->iov.iov_base = blk->buf; + blk->iov.iov_len = nr_sectors * BDRV_SECTOR_SIZE; + qemu_iovec_init_external(&blk->qiov, &blk->iov, 1); - qemu_free(blk->buf); - qemu_free(blk); + blk->time = qemu_get_clock_ns(rt_clock); + + blk->aiocb = bdrv_aio_readv(bs, cur_sector, &blk->qiov, + nr_sectors, blk_mig_read_cb, blk); + if (!blk->aiocb) { + goto error; } + block_mig_state.submitted++; bdrv_reset_dirty(bs, cur_sector, nr_sectors); bmds->cur_sector = cur_sector + nr_sectors; @@ -218,49 +281,58 @@ } } -static void init_blk_migration(Monitor *mon, QEMUFile *f) +static void init_blk_migration_it(void *opaque, BlockDriverState *bs) { + Monitor *mon = opaque; BlkMigDevState *bmds; - BlockDriverState *bs; int64_t sectors; + if (!bdrv_is_read_only(bs)) { + sectors = bdrv_getlength(bs) >> BDRV_SECTOR_BITS; + if (sectors <= 0) { + return; + } + + bmds = qemu_mallocz(sizeof(BlkMigDevState)); + bmds->bs = bs; + bmds->bulk_completed = 0; + bmds->total_sectors = sectors; + bmds->completed_sectors = 0; + bmds->shared_base = block_mig_state.shared_base; + alloc_aio_bitmap(bmds); + drive_get_ref(drive_get_by_blockdev(bs)); + bdrv_set_in_use(bs, 1); + + block_mig_state.total_sector_sum += sectors; + + if (bmds->shared_base) { + monitor_printf(mon, "Start migration for %s with shared base " + "image\n", + bs->device_name); + } else { + monitor_printf(mon, "Start full migration for %s\n", + bs->device_name); + } + + QSIMPLEQ_INSERT_TAIL(&block_mig_state.bmds_list, bmds, entry); + } +} + +static void init_blk_migration(Monitor *mon, QEMUFile *f) +{ block_mig_state.submitted = 0; block_mig_state.read_done = 0; block_mig_state.transferred = 0; block_mig_state.total_sector_sum = 0; block_mig_state.prev_progress = -1; + block_mig_state.bulk_completed = 0; + block_mig_state.total_time = 0; + block_mig_state.reads = 0; - for (bs = bdrv_first; bs != NULL; bs = bs->next) { - if (bs->type == BDRV_TYPE_HD) { - sectors = bdrv_getlength(bs) >> BDRV_SECTOR_BITS; - if (sectors == 0) { - continue; - } - - bmds = qemu_mallocz(sizeof(BlkMigDevState)); - bmds->bs = bs; - bmds->bulk_completed = 0; - bmds->total_sectors = sectors; - bmds->completed_sectors = 0; - bmds->shared_base = block_mig_state.shared_base; - - block_mig_state.total_sector_sum += sectors; - - if (bmds->shared_base) { - monitor_printf(mon, "Start migration for %s with shared base " - "image\n", - bs->device_name); - } else { - monitor_printf(mon, "Start full migration for %s\n", - bs->device_name); - } - - QSIMPLEQ_INSERT_TAIL(&block_mig_state.bmds_list, bmds, entry); - } - } + bdrv_iterate(init_blk_migration_it, mon); } -static int blk_mig_save_bulked_block(Monitor *mon, QEMUFile *f, int is_async) +static int blk_mig_save_bulked_block(Monitor *mon, QEMUFile *f) { int64_t completed_sector_sum = 0; BlkMigDevState *bmds; @@ -269,7 +341,7 @@ QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) { if (bmds->bulk_completed == 0) { - if (mig_save_device_bulk(mon, f, bmds, is_async) == 1) { + if (mig_save_device_bulk(mon, f, bmds) == 1) { /* completed bulk section for this device */ bmds->bulk_completed = 1; } @@ -281,7 +353,12 @@ } } - progress = completed_sector_sum * 100 / block_mig_state.total_sector_sum; + if (block_mig_state.total_sector_sum != 0) { + progress = completed_sector_sum * 100 / + block_mig_state.total_sector_sum; + } else { + progress = 100; + } if (progress != block_mig_state.prev_progress) { block_mig_state.prev_progress = progress; qemu_put_be64(f, (progress << BDRV_SECTOR_BITS) @@ -293,46 +370,102 @@ return ret; } -#define MAX_NUM_BLOCKS 4 - -static void blk_mig_save_dirty_blocks(Monitor *mon, QEMUFile *f) +static void blk_mig_reset_dirty_cursor(void) { BlkMigDevState *bmds; - BlkMigBlock blk; + + QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) { + bmds->cur_dirty = 0; + } +} + +static int mig_save_device_dirty(Monitor *mon, QEMUFile *f, + BlkMigDevState *bmds, int is_async) +{ + BlkMigBlock *blk; + int64_t total_sectors = bmds->total_sectors; int64_t sector; + int nr_sectors; - blk.buf = qemu_malloc(BLOCK_SIZE); + for (sector = bmds->cur_dirty; sector < bmds->total_sectors;) { + if (bmds_aio_inflight(bmds, sector)) { + qemu_aio_flush(); + } + if (bdrv_get_dirty(bmds->bs, sector)) { - QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) { - for (sector = 0; sector < bmds->cur_sector;) { - if (bdrv_get_dirty(bmds->bs, sector)) { - if (bdrv_read(bmds->bs, sector, blk.buf, - BDRV_SECTORS_PER_DIRTY_CHUNK) < 0) { - monitor_printf(mon, "Error reading sector %" PRId64 "\n", - sector); - qemu_file_set_error(f); - qemu_free(blk.buf); - return; + if (total_sectors - sector < BDRV_SECTORS_PER_DIRTY_CHUNK) { + nr_sectors = total_sectors - sector; + } else { + nr_sectors = BDRV_SECTORS_PER_DIRTY_CHUNK; + } + blk = qemu_malloc(sizeof(BlkMigBlock)); + blk->buf = qemu_malloc(BLOCK_SIZE); + blk->bmds = bmds; + blk->sector = sector; + blk->nr_sectors = nr_sectors; + + if (is_async) { + blk->iov.iov_base = blk->buf; + blk->iov.iov_len = nr_sectors * BDRV_SECTOR_SIZE; + qemu_iovec_init_external(&blk->qiov, &blk->iov, 1); + + blk->time = qemu_get_clock_ns(rt_clock); + + blk->aiocb = bdrv_aio_readv(bmds->bs, sector, &blk->qiov, + nr_sectors, blk_mig_read_cb, blk); + if (!blk->aiocb) { + goto error; + } + block_mig_state.submitted++; + bmds_set_aio_inflight(bmds, sector, nr_sectors, 1); + } else { + if (bdrv_read(bmds->bs, sector, blk->buf, + nr_sectors) < 0) { + goto error; } - blk.bmds = bmds; - blk.sector = sector; - blk_send(f, &blk); + blk_send(f, blk); - bdrv_reset_dirty(bmds->bs, sector, - BDRV_SECTORS_PER_DIRTY_CHUNK); + qemu_free(blk->buf); + qemu_free(blk); } - sector += BDRV_SECTORS_PER_DIRTY_CHUNK; + + bdrv_reset_dirty(bmds->bs, sector, nr_sectors); + break; } + sector += BDRV_SECTORS_PER_DIRTY_CHUNK; + bmds->cur_dirty = sector; } - qemu_free(blk.buf); + return (bmds->cur_dirty >= bmds->total_sectors); + +error: + monitor_printf(mon, "Error reading sector %" PRId64 "\n", sector); + qemu_file_set_error(f); + qemu_free(blk->buf); + qemu_free(blk); + return 0; +} + +static int blk_mig_save_dirty_block(Monitor *mon, QEMUFile *f, int is_async) +{ + BlkMigDevState *bmds; + int ret = 0; + + QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) { + if (mig_save_device_dirty(mon, f, bmds, is_async) == 0) { + ret = 1; + break; + } + } + + return ret; } static void flush_blks(QEMUFile* f) { BlkMigBlock *blk; - dprintf("%s Enter submitted %d read_done %d transferred %d\n", + DPRINTF("%s Enter submitted %d read_done %d transferred %d\n", __FUNCTION__, block_mig_state.submitted, block_mig_state.read_done, block_mig_state.transferred); @@ -355,26 +488,47 @@ assert(block_mig_state.read_done >= 0); } - dprintf("%s Exit submitted %d read_done %d transferred %d\n", __FUNCTION__, + DPRINTF("%s Exit submitted %d read_done %d transferred %d\n", __FUNCTION__, block_mig_state.submitted, block_mig_state.read_done, block_mig_state.transferred); } -static int is_stage2_completed(void) +static int64_t get_remaining_dirty(void) { BlkMigDevState *bmds; + int64_t dirty = 0; - if (block_mig_state.submitted > 0) { - return 0; + QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) { + dirty += bdrv_get_dirty_count(bmds->bs); } - QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) { - if (bmds->bulk_completed == 0) { - return 0; + return dirty * BLOCK_SIZE; +} + +static int is_stage2_completed(void) +{ + int64_t remaining_dirty; + long double bwidth; + + if (block_mig_state.bulk_completed == 1) { + + remaining_dirty = get_remaining_dirty(); + if (remaining_dirty == 0) { + return 1; + } + + bwidth = compute_read_bwidth(); + + if ((remaining_dirty / bwidth) <= + migrate_max_downtime()) { + /* finish stage2 because we think that we can finish remaing work + below max_downtime */ + + return 1; } } - return 1; + return 0; } static void blk_mig_cleanup(Monitor *mon) @@ -382,8 +536,13 @@ BlkMigDevState *bmds; BlkMigBlock *blk; + set_dirty_tracking(0); + while ((bmds = QSIMPLEQ_FIRST(&block_mig_state.bmds_list)) != NULL) { QSIMPLEQ_REMOVE_HEAD(&block_mig_state.bmds_list, entry); + bdrv_set_in_use(bmds->bs, 0); + drive_put_ref(drive_get_by_blockdev(bmds->bs)); + qemu_free(bmds->aio_bitmap); qemu_free(bmds); } @@ -393,14 +552,12 @@ qemu_free(blk); } - set_dirty_tracking(0); - monitor_printf(mon, "\n"); } static int block_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque) { - dprintf("Enter save live stage %d submitted %d transferred %d\n", + DPRINTF("Enter save live stage %d submitted %d transferred %d\n", stage, block_mig_state.submitted, block_mig_state.transferred); if (stage < 0) { @@ -428,29 +585,41 @@ return 0; } - /* control the rate of transfer */ - while ((block_mig_state.submitted + - block_mig_state.read_done) * BLOCK_SIZE < - qemu_file_get_rate_limit(f)) { - if (blk_mig_save_bulked_block(mon, f, 1) == 0) { - /* no more bulk blocks for now */ - break; + blk_mig_reset_dirty_cursor(); + + if (stage == 2) { + /* control the rate of transfer */ + while ((block_mig_state.submitted + + block_mig_state.read_done) * BLOCK_SIZE < + qemu_file_get_rate_limit(f)) { + if (block_mig_state.bulk_completed == 0) { + /* first finish the bulk phase */ + if (blk_mig_save_bulked_block(mon, f) == 0) { + /* finished saving bulk on all devices */ + block_mig_state.bulk_completed = 1; + } + } else { + if (blk_mig_save_dirty_block(mon, f, 1) == 0) { + /* no more dirty blocks */ + break; + } + } } - } - flush_blks(f); + flush_blks(f); - if (qemu_file_has_error(f)) { - blk_mig_cleanup(mon); - return 0; + if (qemu_file_has_error(f)) { + blk_mig_cleanup(mon); + return 0; + } } if (stage == 3) { - while (blk_mig_save_bulked_block(mon, f, 0) != 0) { - /* empty */ - } + /* we know for sure that save bulk is completed and + all async read completed */ + assert(block_mig_state.submitted == 0); - blk_mig_save_dirty_blocks(mon, f); + while (blk_mig_save_dirty_block(mon, f, 0) != 0); blk_mig_cleanup(mon); /* report completion */ @@ -474,8 +643,10 @@ int len, flags; char device_name[256]; int64_t addr; - BlockDriverState *bs; + BlockDriverState *bs, *bs_prev = NULL; uint8_t *buf; + int64_t total_sectors = 0; + int nr_sectors; do { addr = qemu_get_be64(f); @@ -484,6 +655,7 @@ addr >>= BDRV_SECTOR_BITS; if (flags & BLK_MIG_FLAG_DEVICE_BLOCK) { + int ret; /* get device name */ len = qemu_get_byte(f); qemu_get_buffer(f, (uint8_t *)device_name, len); @@ -496,12 +668,31 @@ return -EINVAL; } + if (bs != bs_prev) { + bs_prev = bs; + total_sectors = bdrv_getlength(bs) >> BDRV_SECTOR_BITS; + if (total_sectors <= 0) { + error_report("Error getting length of block device %s\n", + device_name); + return -EINVAL; + } + } + + if (total_sectors - addr < BDRV_SECTORS_PER_DIRTY_CHUNK) { + nr_sectors = total_sectors - addr; + } else { + nr_sectors = BDRV_SECTORS_PER_DIRTY_CHUNK; + } + buf = qemu_malloc(BLOCK_SIZE); qemu_get_buffer(f, buf, BLOCK_SIZE); - bdrv_write(bs, addr, buf, BDRV_SECTORS_PER_DIRTY_CHUNK); + ret = bdrv_write(bs, addr, buf, nr_sectors); qemu_free(buf); + if (ret < 0) { + return ret; + } } else if (flags & BLK_MIG_FLAG_PROGRESS) { if (!banner_printed) { printf("Receiving block device images\n"); @@ -536,6 +727,6 @@ QSIMPLEQ_INIT(&block_mig_state.bmds_list); QSIMPLEQ_INIT(&block_mig_state.blk_list); - register_savevm_live("block", 0, 1, block_set_params, block_save_live, - NULL, block_load, &block_mig_state); + register_savevm_live(NULL, "block", 0, 1, block_set_params, + block_save_live, NULL, block_load, &block_mig_state); } diff -Nru qemu-kvm-0.12.5+noroms/bsd-user/bsdload.c qemu-kvm-0.14.1/bsd-user/bsdload.c --- qemu-kvm-0.12.5+noroms/bsd-user/bsdload.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/bsd-user/bsdload.c 2011-05-11 13:29:46.000000000 +0000 @@ -176,8 +176,6 @@ retval = prepare_binprm(&bprm); - infop->host_argv = argv; - if(retval>=0) { if (bprm.buf[0] == 0x7f && bprm.buf[1] == 'E' diff -Nru qemu-kvm-0.12.5+noroms/bsd-user/elfload.c qemu-kvm-0.14.1/bsd-user/elfload.c --- qemu-kvm-0.12.5+noroms/bsd-user/elfload.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/bsd-user/elfload.c 2011-05-11 13:29:46.000000000 +0000 @@ -1044,7 +1044,7 @@ struct elf_shdr sechdr, symtab, strtab; char *strings; struct syminfo *s; - struct elf_sym *syms; + struct elf_sym *syms, *new_syms; lseek(fd, hdr->e_shoff, SEEK_SET); for (i = 0; i < hdr->e_shnum; i++) { @@ -1072,15 +1072,24 @@ /* Now know where the strtab and symtab are. Snarf them. */ s = malloc(sizeof(*s)); syms = malloc(symtab.sh_size); - if (!syms) + if (!syms) { + free(s); return; + } s->disas_strtab = strings = malloc(strtab.sh_size); - if (!s->disas_strtab) + if (!s->disas_strtab) { + free(s); + free(syms); return; + } lseek(fd, symtab.sh_offset, SEEK_SET); - if (read(fd, syms, symtab.sh_size) != symtab.sh_size) + if (read(fd, syms, symtab.sh_size) != symtab.sh_size) { + free(s); + free(syms); + free(strings); return; + } nsyms = symtab.sh_size / sizeof(struct elf_sym); @@ -1105,13 +1114,29 @@ #endif i++; } - syms = realloc(syms, nsyms * sizeof(*syms)); + + /* Attempt to free the storage associated with the local symbols + that we threw away. Whether or not this has any effect on the + memory allocation depends on the malloc implementation and how + many symbols we managed to discard. */ + new_syms = realloc(syms, nsyms * sizeof(*syms)); + if (new_syms == NULL) { + free(s); + free(syms); + free(strings); + return; + } + syms = new_syms; qsort(syms, nsyms, sizeof(*syms), symcmp); lseek(fd, strtab.sh_offset, SEEK_SET); - if (read(fd, strings, strtab.sh_size) != strtab.sh_size) + if (read(fd, strings, strtab.sh_size) != strtab.sh_size) { + free(s); + free(syms); + free(strings); return; + } s->disas_num_syms = nsyms; #if ELF_CLASS == ELFCLASS32 s->disas_symtab.elf32 = syms; diff -Nru qemu-kvm-0.12.5+noroms/bsd-user/main.c qemu-kvm-0.14.1/bsd-user/main.c --- qemu-kvm-0.12.5+noroms/bsd-user/main.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/bsd-user/main.c 2011-05-11 13:29:46.000000000 +0000 @@ -30,8 +30,8 @@ #include "qemu-common.h" /* For tb_lock */ #include "exec-all.h" - - +#include "tcg.h" +#include "qemu-timer.h" #include "envlist.h" #define DEBUG_LOGFILE "/tmp/qemu.log" @@ -43,7 +43,7 @@ int have_guest_base; #endif -static const char *interp_prefix = CONFIG_QEMU_PREFIX; +static const char *interp_prefix = CONFIG_QEMU_INTERP_PREFIX; const char *qemu_uname_release = CONFIG_UNAME_RELEASE; extern char **environ; enum BSDType bsd_type; @@ -759,6 +759,10 @@ } cpu_model = NULL; +#if defined(cpudef_setup) + cpudef_setup(); /* parse cpu definitions in target config file (TBD) */ +#endif + optind = 1; for(;;) { if (optind >= argc) @@ -791,6 +795,12 @@ r = argv[optind++]; if (envlist_setenv(envlist, r) != 0) usage(); + } else if (!strcmp(r, "ignore-environment")) { + envlist_free(envlist); + if ((envlist = envlist_create()) == NULL) { + (void) fprintf(stderr, "Unable to allocate envlist\n"); + exit(1); + } } else if (!strcmp(r, "U")) { r = argv[optind++]; if (envlist_unsetenv(envlist, r) != 0) @@ -966,6 +976,13 @@ syscall_init(); signal_init(); +#if defined(CONFIG_USE_GUEST_BASE) + /* Now that we've loaded the binary, GUEST_BASE is fixed. Delay + generating the prologue until now so that the prologue can take + the real value of GUEST_BASE into account. */ + tcg_prologue_init(&tcg_ctx); +#endif + /* build Task State */ memset(ts, 0, sizeof(TaskState)); init_task_state(ts); diff -Nru qemu-kvm-0.12.5+noroms/bsd-user/mmap.c qemu-kvm-0.14.1/bsd-user/mmap.c --- qemu-kvm-0.12.5+noroms/bsd-user/mmap.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/bsd-user/mmap.c 2011-05-11 13:29:46.000000000 +0000 @@ -77,16 +77,15 @@ void *qemu_vmalloc(size_t size) { void *p; - unsigned long addr; mmap_lock(); /* Use map and mark the pages as used. */ p = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); - addr = (unsigned long)p; - if (addr == (target_ulong) addr) { + if (h2g_valid(p)) { /* Allocated region overlaps guest address space. This may recurse. */ + abi_ulong addr = h2g(p); page_set_flags(addr & TARGET_PAGE_MASK, TARGET_PAGE_ALIGN(addr + size), PAGE_RESERVED); } @@ -240,7 +239,7 @@ possible while it is a shared mapping */ if ((flags & TARGET_BSD_MAP_FLAGMASK) == MAP_SHARED && (prot & PROT_WRITE)) - return -EINVAL; + return -1; /* adjust protection to be able to read */ if (!(prot1 & PROT_WRITE)) diff -Nru qemu-kvm-0.12.5+noroms/bsd-user/qemu.h qemu-kvm-0.14.1/bsd-user/qemu.h --- qemu-kvm-0.12.5+noroms/bsd-user/qemu.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/bsd-user/qemu.h 2011-05-11 13:29:46.000000000 +0000 @@ -50,7 +50,6 @@ abi_ulong entry; abi_ulong code_offset; abi_ulong data_offset; - char **host_argv; int personality; }; @@ -139,7 +138,7 @@ abi_long do_openbsd_syscall(void *cpu_env, int num, abi_long arg1, abi_long arg2, abi_long arg3, abi_long arg4, abi_long arg5, abi_long arg6); -void gemu_log(const char *fmt, ...) __attribute__((format(printf,1,2))); +void gemu_log(const char *fmt, ...) GCC_FMT_ATTR(1, 2); extern THREAD CPUState *thread_env; void cpu_loop(CPUState *env); char *target_strerror(int err); diff -Nru qemu-kvm-0.12.5+noroms/bswap.h qemu-kvm-0.14.1/bswap.h --- qemu-kvm-0.12.5+noroms/bswap.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/bswap.h 2011-05-11 13:29:46.000000000 +0000 @@ -144,6 +144,7 @@ #define cpu_to_be16wu(p, v) cpu_to_be16w(p, v) #define cpu_to_be32wu(p, v) cpu_to_be32w(p, v) +#define cpu_to_be64wu(p, v) cpu_to_be64w(p, v) #else @@ -201,12 +202,28 @@ p1[3] = v & 0xff; } +static inline void cpu_to_be64wu(uint64_t *p, uint64_t v) +{ + uint8_t *p1 = (uint8_t *)p; + + p1[0] = v >> 56; + p1[1] = v >> 48; + p1[2] = v >> 40; + p1[3] = v >> 32; + p1[4] = v >> 24; + p1[5] = v >> 16; + p1[6] = v >> 8; + p1[7] = v & 0xff; +} + #endif #ifdef HOST_WORDS_BIGENDIAN #define cpu_to_32wu cpu_to_be32wu +#define leul_to_cpu(v) glue(glue(le,HOST_LONG_BITS),_to_cpu)(v) #else #define cpu_to_32wu cpu_to_le32wu +#define leul_to_cpu(v) (v) #endif #undef le_bswap @@ -214,4 +231,10 @@ #undef le_bswaps #undef be_bswaps +/* len must be one of 1, 2, 4 */ +static inline uint32_t qemu_bswap_len(uint32_t value, int len) +{ + return bswap32(value) >> (32 - 8 * len); +} + #endif /* BSWAP_H */ diff -Nru qemu-kvm-0.12.5+noroms/bt-host.c qemu-kvm-0.14.1/bt-host.c --- qemu-kvm-0.12.5+noroms/bt-host.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/bt-host.c 2011-05-11 13:29:46.000000000 +0000 @@ -50,19 +50,19 @@ struct bt_host_hci_s *s = (struct bt_host_hci_s *) hci; uint8_t pkt = type; struct iovec iv[2]; - int ret; iv[0].iov_base = (void *)&pkt; iv[0].iov_len = 1; iv[1].iov_base = (void *) data; iv[1].iov_len = len; - while ((ret = writev(s->fd, iv, 2)) < 0) + while (writev(s->fd, iv, 2) < 0) { if (errno != EAGAIN && errno != EINTR) { fprintf(stderr, "qemu: error %i writing bluetooth packet.\n", errno); return; } + } } static void bt_host_cmd(struct HCIInfo *hci, const uint8_t *data, int len) @@ -80,13 +80,6 @@ bt_host_send(hci, HCI_SCODATA_PKT, data, len); } -static int bt_host_read_poll(void *opaque) -{ - struct bt_host_hci_s *s = (struct bt_host_hci_s *) opaque; - - return !!s->hci.evt_recv; -} - static void bt_host_read(void *opaque) { struct bt_host_hci_s *s = (struct bt_host_hci_s *) opaque; @@ -192,7 +185,7 @@ s->hci.acl_send = bt_host_acl; s->hci.bdaddr_set = bt_host_bdaddr_set; - qemu_set_fd_handler2(s->fd, bt_host_read_poll, bt_host_read, NULL, s); + qemu_set_fd_handler(s->fd, bt_host_read, NULL, s); return &s->hci; } diff -Nru qemu-kvm-0.12.5+noroms/buffered_file.c qemu-kvm-0.14.1/buffered_file.c --- qemu-kvm-0.12.5+noroms/buffered_file.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/buffered_file.c 2011-05-11 13:29:46.000000000 +0000 @@ -39,10 +39,10 @@ } QEMUFileBuffered; #ifdef DEBUG_BUFFERED_FILE -#define dprintf(fmt, ...) \ +#define DPRINTF(fmt, ...) \ do { printf("buffered-file: " fmt, ## __VA_ARGS__); } while (0) #else -#define dprintf(fmt, ...) \ +#define DPRINTF(fmt, ...) \ do { } while (0) #endif @@ -52,7 +52,7 @@ if (size > (s->buffer_capacity - s->buffer_size)) { void *tmp; - dprintf("increasing buffer capacity from %zu by %zu\n", + DPRINTF("increasing buffer capacity from %zu by %zu\n", s->buffer_capacity, size + 1024); s->buffer_capacity += size + 1024; @@ -75,11 +75,11 @@ size_t offset = 0; if (s->has_error) { - dprintf("flush when error, bailing\n"); + DPRINTF("flush when error, bailing\n"); return; } - dprintf("flushing %zu byte(s) of data\n", s->buffer_size); + DPRINTF("flushing %zu byte(s) of data\n", s->buffer_size); while (offset < s->buffer_size) { ssize_t ret; @@ -87,22 +87,22 @@ ret = s->put_buffer(s->opaque, s->buffer + offset, s->buffer_size - offset); if (ret == -EAGAIN) { - dprintf("backend not ready, freezing\n"); + DPRINTF("backend not ready, freezing\n"); s->freeze_output = 1; break; } if (ret <= 0) { - dprintf("error flushing data, %zd\n", ret); + DPRINTF("error flushing data, %zd\n", ret); s->has_error = 1; break; } else { - dprintf("flushed %zd byte(s)\n", ret); + DPRINTF("flushed %zd byte(s)\n", ret); offset += ret; } } - dprintf("flushed %zu of %zu byte(s)\n", offset, s->buffer_size); + DPRINTF("flushed %zu of %zu byte(s)\n", offset, s->buffer_size); memmove(s->buffer, s->buffer + offset, s->buffer_size - offset); s->buffer_size -= offset; } @@ -113,49 +113,57 @@ int offset = 0; ssize_t ret; - dprintf("putting %d bytes at %" PRId64 "\n", size, pos); + DPRINTF("putting %d bytes at %" PRId64 "\n", size, pos); if (s->has_error) { - dprintf("flush when error, bailing\n"); + DPRINTF("flush when error, bailing\n"); return -EINVAL; } - dprintf("unfreezing output\n"); + DPRINTF("unfreezing output\n"); s->freeze_output = 0; buffered_flush(s); while (!s->freeze_output && offset < size) { if (s->bytes_xfer > s->xfer_limit) { - dprintf("transfer limit exceeded when putting\n"); + DPRINTF("transfer limit exceeded when putting\n"); break; } ret = s->put_buffer(s->opaque, buf + offset, size - offset); if (ret == -EAGAIN) { - dprintf("backend not ready, freezing\n"); + DPRINTF("backend not ready, freezing\n"); s->freeze_output = 1; break; } if (ret <= 0) { - dprintf("error putting\n"); + DPRINTF("error putting\n"); s->has_error = 1; offset = -EINVAL; break; } - dprintf("put %zd byte(s)\n", ret); + DPRINTF("put %zd byte(s)\n", ret); offset += ret; s->bytes_xfer += ret; } if (offset >= 0) { - dprintf("buffering %d bytes\n", size - offset); + DPRINTF("buffering %d bytes\n", size - offset); buffered_append(s, buf + offset, size - offset); offset = size; } + if (pos == 0 && size == 0) { + DPRINTF("file is ready\n"); + if (s->bytes_xfer <= s->xfer_limit) { + DPRINTF("notifying client\n"); + s->put_ready(s->opaque); + } + } + return offset; } @@ -164,7 +172,7 @@ QEMUFileBuffered *s = opaque; int ret; - dprintf("closing\n"); + DPRINTF("closing\n"); while (!s->has_error && s->buffer_size) { buffered_flush(s); @@ -198,20 +206,23 @@ return 0; } -static size_t buffered_set_rate_limit(void *opaque, size_t new_rate) +static int64_t buffered_set_rate_limit(void *opaque, int64_t new_rate) { QEMUFileBuffered *s = opaque; - if (s->has_error) goto out; + if (new_rate > SIZE_MAX) { + new_rate = SIZE_MAX; + } + s->xfer_limit = new_rate / 10; out: return s->xfer_limit; } -static size_t buffered_get_rate_limit(void *opaque) +static int64_t buffered_get_rate_limit(void *opaque) { QEMUFileBuffered *s = opaque; @@ -222,8 +233,10 @@ { QEMUFileBuffered *s = opaque; - if (s->has_error) + if (s->has_error) { + buffered_close(s); return; + } qemu_mod_timer(s->timer, qemu_get_clock(rt_clock) + 100); diff -Nru qemu-kvm-0.12.5+noroms/cache-utils.c qemu-kvm-0.14.1/cache-utils.c --- qemu-kvm-0.12.5+noroms/cache-utils.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/cache-utils.c 2011-05-11 13:29:46.000000000 +0000 @@ -57,6 +57,30 @@ } #endif +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) +#include +#include +#include +#include +#include +#include + +static void ppc_init_cacheline_sizes(void) +{ + size_t len = 4; + unsigned cacheline; + + if (sysctlbyname ("machdep.cacheline_size", &cacheline, &len, NULL, 0)) { + fprintf(stderr, "sysctlbyname machdep.cacheline_size failed: %s\n", + strerror(errno)); + exit(1); + } + + qemu_cache_conf.dcache_bsize = cacheline; + qemu_cache_conf.icache_bsize = cacheline; +} +#endif + #ifdef __linux__ void qemu_cache_utils_init(char **envp) { diff -Nru qemu-kvm-0.12.5+noroms/cache-utils.h qemu-kvm-0.14.1/cache-utils.h --- qemu-kvm-0.12.5+noroms/cache-utils.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/cache-utils.h 2011-05-11 13:29:46.000000000 +0000 @@ -9,7 +9,7 @@ extern struct qemu_cache_conf qemu_cache_conf; -extern void qemu_cache_utils_init(char **envp); +void qemu_cache_utils_init(char **envp); /* mildly adjusted code from tcg-dyngen.c */ static inline void flush_icache_range(unsigned long start, unsigned long stop) diff -Nru qemu-kvm-0.12.5+noroms/Changelog qemu-kvm-0.14.1/Changelog --- qemu-kvm-0.12.5+noroms/Changelog 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/Changelog 2011-05-11 13:29:46.000000000 +0000 @@ -1,190 +1,3 @@ -version 0.12.5 - - audio/alsa: Handle SND_PCM_STATE_SETUP in alsa_poll_handler - - block: Handle multiwrite errors only when all requests have completed - - block: Fix early failure in multiwrite - - vpc: Use bdrv_(p)write_sync for metadata writes - - vmdk: Use bdrv_(p)write_sync for metadata writes - - qcow2: Use bdrv_(p)write_sync for metadata writes - - qcow: Use bdrv_(p)write_sync for metadata writes - - block: Add bdrv_(p)write_sync - - qcow2: Restore L1 entry on l2_allocate failure - - block/vdi: Fix image opening and creation for odd disk sizes - - block/vpc: Fix conversion from size to disk geometry - - qcow2: Remove abort on free_clusters failure - - vmdk: Fix COW - - qcow2: Fix creation of large images - - vmdk: fix double free - - qemu-options: add documentation for stdio signal=on|off - - target-arm : fix parallel saturated subtraction implementation - - target-arm : fix thumb2 parallel add/sub opcode decoding - - target-arm: fix addsub/subadd implementation - - target-i386: fix xchg rax,r8 - - block/vvfat.c: fix warnings with _FORTIFY_SOURCE - - audio/alsa: Spelling typo (paramters) - - target-mips: fix DINSU instruction - - Correct definitions for FD_CMD_SAVE and FD_CMD_RESTORE - - qcow2: Fix corruption after error in update_refcount - - qcow2: Fix corruption after refblock allocation - - block: Fix multiwrite with overlapping requests - - qcow2: Fix error handling in l2_allocate - - qcow2: Clear L2 table cache after write error - - ide: Fix ide_dma_cancel - - usb-bus: fix no params - - Avoid crash on '-usbdevice ' without parameters - - Fix -usbdevice crash - - Fix multiboot compilation - - Fix missing symbols in .rel/.rela.plt sections - - target-ppc: fix RFI by clearing some bits of MSR - - Fix typo in balloon help - - arm_timer: fix oneshot mode - - arm_timer: reload timer when enabled - - qemu-sockets: avoid strlen of NULL pointer - - block: fix aio_flush segfaults for read-only protocols (e.g. curl) - - virtio-blk: fix barrier support - - block: fix sector comparism in multiwrite_req_compare - - pci: irq_state vmstate breakage - - qemu-img: use the heap instead of the huge stack array for win32 - -version 0.12.4 - - Workaround for broken OSS_GETVERSION on FreeBSD, part two (Juergen Lock) - - oss: fix fragment setting (malc) - - oss: issue OSS_GETVERSION ioctl only when needed (malc) - - oss: refactor code around policy setting (malc) - - oss: workaround for cases when OSS_GETVERSION is not defined (malc) - - block: Free iovec arrays allocated by multiwrite_merge() (Stefan Hajnoczi) - - lsi: fix segfault in lsi_command_complete (Gerd Hoffmann) - - lsi: pass lsi_request to lsi_reselect (Gerd Hoffmann) - - lsi: move dma_len+dma_buf into lsi_request (Gerd Hoffmann) - - lsi: move current_dev into lsi_request (Gerd Hoffmann) - - lsi: have lsi_request for the whole life time of the request. (Gerd Hoffmann) - - lsi: use QTAILQ for lsi_queue (Gerd Hoffmann) - - tcp/mips: Change TCG_AREG0 (fp -> s0) (Stefan Weil) - - sh_pci: fix memory and I/O access (Aurelien Jarno) - - Fix incoming migration with iothread (Marcelo Tosatti) - - Fix SIGFPE for vnc display of width/height = 1 (Chris Webb) - - net: remove broken net_set_boot_mask() boot device validation (Eduardo Habkost) - - qcow2: Remove request from in-flight list after error (Kevin Wolf) - - qcow2: Don't ignore immediate read/write failures (Kevin Wolf) - - block: Fix multiwrite memory leak in error case (Kevin Wolf) - - block: Fix error code in multiwrite for immediate failures (Kevin Wolf) - - block: Fix multiwrite error handling (Kevin Wolf) - - scsi-disk: fix buffer overflow (Gerd Hoffmann) - - qcow2: Rewrite alloc_refcount_block/grow_refcount_table (Kevin Wolf) - - qcow2: Factor next_refcount_table_size out (Kevin Wolf) - - block: avoid creating too large iovecs in multiwrite_merge (Christoph Hellwig) - - json-parser: Fix segfault on malformed input (Kevin Wolf) - - linux-user: switch default ppc64 CPU to 970fx from 970 (Aurelien Jarno) - - target-sh4: MMU: fix store queue addresses (Aurelien Jarno) - - target-sh4: MMU: fix ITLB priviledge check (Aurelien Jarno) - - target-sh4: MMU: fix mem_idx computation (Aurelien Jarno) - - sh7750: handle MMUCR TI bit (Aurelien Jarno) - - UHCI spurious interrut fix (Paul Brook) - - tcg/mips: fix branch offset during retranslation (Aurelien Jarno) - - tcg/arm: correctly save/restore registers in prologue/epilogue (Aurelien Jarno) - - workaround for cmd646 bmdma register access while no dma is active (Igor V. Kovalenko) - - Fix corner case in chardev udp: parameter (Jan Kiszka) - - Don't set default monitor when there is a mux'ed one (Jan Kiszka) - - spelling typo (compatibilty) in hw/fw_cfg.c (Vagrant Cascadian) - - fdc: fix drive property handling. (Gerd Hoffmann) - - target-i386: fix commit c22549204a6edc431e8e4358e61bd56386ff6957 (TeLeMan) - - target-i386: fix SIB decoding with index = 4 (Aurelien Jarno) - - Fix segfault with ram_size > 4095M without kvm (Ryan Harper) - - target-i386: Fix long jumps/calls in long mode with REX.W set (malc) - - target-i386: fix lddqu SSE instruction (Aurelien Jarno) - - qemu-char.c: drop debug printfs from qemu_chr_parse_compat (Jan Kiszka) - - fix undefined shifts by >32 (Paolo Bonzini) - - Fix qemu -net user,hostfwd= example (Aurelien Jarno) - -version 0.12.3 - - kvm: Fix eflags corruption in kvm mode (Jan Kiszka) - - qcow2: Fix access after end of array (Kevin Wolf) - - ide save/restore pio/atapi cmd transfer fields and io buffer (Marcelo Tosatti) - - net: Monitor command set_link finds only VLAN clients, fix (Markus Armbruster) - - net: info network shows only VLAN clients, fix (Markus Armbruster) - - net: net_check_clients() checks only VLAN clients, fix (Markus Armbruster) - - net: Fix bogus "Warning: vlan 0 with no nics" with -device (Markus Armbruster) - - net: net_check_clients() runs too early to see -device, fix (Markus Armbruster) - - net: Remove unused net_client_uninit() (Markus Armbruster) - - don't dereference NULL after failed strdup (Jim Meyering) - - virtio-net: fix network stall under load (Tom Lendacky) - - json: fix PRId64 on Win32 (Roy Tam) - - fix inet_parse typo (Marcelo Tosatti) - - iothread: fix vcpu stop with smp tcg (Marcelo Tosatti) - - segfault due to buffer overrun in usb-serial (David S. Ahern) - - qcow2: Fix signedness bugs (Kevin Wolf) - - Do not ignore error, if open file failed (-serial /dev/tty) (Evgeniy Dushistov) - - pc-bios: update to newer version of (stable) seabios (Anthony Liguori) - - target-mips: fix ROTR and DROTR by zero (Aurelien Jarno) - - target-mips: fix CpU exception for coprocessor 0 (Nathan Froyd) - - tcg/mips: fix crash in tcg_out_qemu_ld() (Aurelien Jarno) - - target-mips: don't call cpu_loop_exit() from helper.c (Aurelien Jarno) - - virtio-blk: Fix error cases which ignored rerror/werror (Kevin Wolf) - - virtio-blk: Fix restart after read error (Kevin Wolf) - - virtio_blk: Factor virtio_blk_handle_request out (Kevin Wolf) - - cirrus: Properly re-register cirrus_linear_io_addr on vram unmap (Jan Kiszka) - - qcow2: Don't ignore qcow2_alloc_clusters return value (Kevin Wolf) - - qcow2: Don't ignore update_refcount return value (Kevin Wolf) - - qcow2: Allow updating no refcounts (Kevin Wolf) - - qcow2: Improve error handling in update_refcount (Kevin Wolf) - - qcow2: Fix error handling in grow_refcount_table (Kevin Wolf) - - block: Return original error codes in bdrv_pread/write (Kevin Wolf) - - qcow2: Return 0/-errno in qcow2_alloc_cluster_offset (Kevin Wolf) - - qcow2: Return 0/-errno in get_cluster_table (Kevin Wolf) - - qcow2: Fix error handling in qcow_save_vmstate (Kevin Wolf) - - qcow2: Fix error handling in qcow2_grow_l1_table (Kevin Wolf) - - win32/sdl: Fix toggle full screen (Herve Poussineau) - - win32: pair qemu_memalign() with qemu_vfree() (Herve Poussineau) - - vnc_refresh: calling vnc_update_client might free vs (Stefano Stabellini) - - Musicpal: Fix descriptor walk in eth_send (Jan Kiszka) - - Musicpal: Fix wm8750 I2C address (Jan Kiszka) - - fix savevm command without id or tag (Marcelo Tosatti) - - reduce number of reinjects on ACK (Gleb Natapov) - - QMP: Fix asynchronous events delivery (Luiz Capitulino) - - Documentation: Add missing documentation for qdev related command line options (Stefan Weil) - - pc: add driver version compat properties (Gerd Hoffmann) - - scsi: device version property (Gerd Hoffmann) - - ide: device version property (Gerd Hoffmann) - - QMP: Emit asynchronous events on all QMP monitors (Adam Litke) - - Fix QEMU_WARN_UNUSED_RESULT (Kevin Wolf) - -version 0.12.2: - - Qemu's internal TFTP server breaks lock-step-iness of TFTP (Milan Plzik) - - osdep.c: Fix accept4 fallback (Kevin Wolf) - - pc: add rombar to compat properties for pc-0.10 and pc-0.11 (Gerd Hoffmann) - - pci: allow loading roms via fw_cfg. (Gerd Hoffmann) - - roms: rework rom loading via fw (Gerd Hoffmann) - - fw_cfg: rom loader tweaks. (Gerd Hoffmann) - - roms: minor fixes and cleanups. (Gerd Hoffmann) - - pc: add machine type for 0.12 (Gerd Hoffmann) - - loader: more ignores for rom intended to be loaded by the bios (Aurelien Jarno) - - vnc_refresh: return if vd->timer is NULL (Stefano Stabellini) - - QMP: Don't free async event's 'data' (Luiz Capitulino) - - Handle TFTP ERROR from client (Thomas Horsten) - - dmg: fix ->open failure (Christoph Hellwig) - - virtio-pci: thinko fix (Michael S. Tsirkin) - - pc-bios: Update README (SeaBIOS) (Stefan Weil) - - vmware_vga: Check cursor dimensions passed from guest to avoid buffer overflow (Roland Dreier) - - remove pending exception on vcpu reset. (Gleb Natapov) - - Fix CPU topology initialization (Jiri Denemark) - - MCE: Fix bug of IA32_MCG_STATUS after system reset (Huang Ying) - - linuxboot: fix gdt address calculation (Avi Kivity) - - QMP: Drop wrong assert() (Luiz Capitulino) - - vnc: Fix artifacts in hextile decoding (Anthony Liguori) - - target-i386: Fix "call im" on x86_64 when executing 32-bit code (Aurelien Jarno) - - Add missing newline at the end of options list (Michael Tokarev) - - Don't load options roms intended to be loaded by the bios in qemu (Avi Kivity) - - USB: Improve usbdevice error messages (Scott Tsai) - - cpu-all.h: fix cpu_get_real_ticks() #ifdef (Aurelien Jarno) - - alpha: fix compile (Blue Swirl) - - user_only: compile everything with -fpie (Kirill A. Shutemov) - - fdc/sparc32: don't hang on detection under OBP (Artyom Tarasenko) - - scsi-disk: Inquiry with allocation length of CDB < 36 (v4) (Artyom Tarasenko) - - e1000: fix init values for command register (Michael S. Tsirkin) - -version 0.12.1: - - loader: fix rom loading at address 0 (fixes target-arm) (Aurelien Jarno) - - loader: fix rom_copy (fixes multiboot) (Kevin Wolf) - version 0.12.0: - Update to SeaBIOS 0.5.0 diff -Nru qemu-kvm-0.12.5+noroms/check-qdict.c qemu-kvm-0.14.1/check-qdict.c --- qemu-kvm-0.12.5+noroms/check-qdict.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/check-qdict.c 2011-05-11 13:29:46.000000000 +0000 @@ -5,6 +5,9 @@ * * Authors: * Luiz Capitulino + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. */ #include @@ -47,7 +50,7 @@ qdict_put_obj(qdict, "", QOBJECT(qint_from_int(num))); fail_unless(qdict_size(qdict) == 1); - ent = QLIST_FIRST(&qdict->table[12345 % QDICT_HASH_SIZE]); + ent = QLIST_FIRST(&qdict->table[12345 % QDICT_BUCKET_MAX]); qi = qobject_to_qint(ent->value); fail_unless(qint_get_int(qi) == num); @@ -191,6 +194,36 @@ } END_TEST +START_TEST(qdict_iterapi_test) +{ + int count; + const QDictEntry *ent; + + fail_unless(qdict_first(tests_dict) == NULL); + + qdict_put(tests_dict, "key1", qint_from_int(1)); + qdict_put(tests_dict, "key2", qint_from_int(2)); + qdict_put(tests_dict, "key3", qint_from_int(3)); + + count = 0; + for (ent = qdict_first(tests_dict); ent; ent = qdict_next(tests_dict, ent)){ + fail_unless(qdict_haskey(tests_dict, qdict_entry_key(ent)) == 1); + count++; + } + + fail_unless(count == qdict_size(tests_dict)); + + /* Do it again to test restarting */ + count = 0; + for (ent = qdict_first(tests_dict); ent; ent = qdict_next(tests_dict, ent)){ + fail_unless(qdict_haskey(tests_dict, qdict_entry_key(ent)) == 1); + count++; + } + + fail_unless(count == qdict_size(tests_dict)); +} +END_TEST + /* * Errors test-cases */ @@ -335,6 +368,7 @@ tcase_add_test(qdict_public2_tcase, qdict_haskey_test); tcase_add_test(qdict_public2_tcase, qdict_del_test); tcase_add_test(qdict_public2_tcase, qobject_to_qdict_test); + tcase_add_test(qdict_public2_tcase, qdict_iterapi_test); qdict_errors_tcase = tcase_create("Errors"); suite_add_tcase(s, qdict_errors_tcase); diff -Nru qemu-kvm-0.12.5+noroms/check-qfloat.c qemu-kvm-0.14.1/check-qfloat.c --- qemu-kvm-0.12.5+noroms/check-qfloat.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/check-qfloat.c 2011-05-11 13:29:46.000000000 +0000 @@ -1,11 +1,6 @@ /* * QFloat unit-tests. * - * Copyright (C) 2009 Red Hat Inc. - * - * Authors: - * Luiz Capitulino - * * Copyright IBM, Corp. 2009 * * Authors: diff -Nru qemu-kvm-0.12.5+noroms/check-qint.c qemu-kvm-0.14.1/check-qint.c --- qemu-kvm-0.12.5+noroms/check-qint.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/check-qint.c 2011-05-11 13:29:46.000000000 +0000 @@ -5,6 +5,9 @@ * * Authors: * Luiz Capitulino + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. */ #include diff -Nru qemu-kvm-0.12.5+noroms/check-qjson.c qemu-kvm-0.14.1/check-qjson.c --- qemu-kvm-0.12.5+noroms/check-qjson.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/check-qjson.c 2011-05-11 13:29:46.000000000 +0000 @@ -9,7 +9,6 @@ * */ #include -#include #include "qstring.h" #include "qint.h" @@ -29,6 +28,13 @@ const char *decoded; int skip; } test_cases[] = { + { "\"\\b\"", "\b" }, + { "\"\\f\"", "\f" }, + { "\"\\n\"", "\n" }, + { "\"\\r\"", "\r" }, + { "\"\\t\"", "\t" }, + { "\"\\/\"", "\\/" }, + { "\"\\\\\"", "\\" }, { "\"\\\"\"", "\"" }, { "\"hello world \\\"embedded string\\\"\"", "hello world \"embedded string\"" }, @@ -49,11 +55,14 @@ fail_unless(qobject_type(obj) == QTYPE_QSTRING); str = qobject_to_qstring(obj); - fail_unless(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0); + fail_unless(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0, + "%s != %s\n", qstring_get_str(str), test_cases[i].decoded); if (test_cases[i].skip == 0) { str = qobject_to_json(obj); - fail_unless(strcmp(qstring_get_str(str), test_cases[i].encoded) == 0); + fail_unless(strcmp(qstring_get_str(str),test_cases[i].encoded) == 0, + "%s != %s\n", qstring_get_str(str), + test_cases[i].encoded); qobject_decref(obj); } @@ -628,11 +637,92 @@ } END_TEST +START_TEST(empty_input) +{ + const char *empty = ""; + + QObject *obj = qobject_from_json(empty); + fail_unless(obj == NULL); +} +END_TEST + +START_TEST(unterminated_string) +{ + QObject *obj = qobject_from_json("\"abc"); + fail_unless(obj == NULL); +} +END_TEST + +START_TEST(unterminated_sq_string) +{ + QObject *obj = qobject_from_json("'abc"); + fail_unless(obj == NULL); +} +END_TEST + +START_TEST(unterminated_escape) +{ + QObject *obj = qobject_from_json("\"abc\\\""); + fail_unless(obj == NULL); +} +END_TEST + +START_TEST(unterminated_array) +{ + QObject *obj = qobject_from_json("[32"); + fail_unless(obj == NULL); +} +END_TEST + +START_TEST(unterminated_array_comma) +{ + QObject *obj = qobject_from_json("[32,"); + fail_unless(obj == NULL); +} +END_TEST + +START_TEST(invalid_array_comma) +{ + QObject *obj = qobject_from_json("[32,}"); + fail_unless(obj == NULL); +} +END_TEST + +START_TEST(unterminated_dict) +{ + QObject *obj = qobject_from_json("{'abc':32"); + fail_unless(obj == NULL); +} +END_TEST + +START_TEST(unterminated_dict_comma) +{ + QObject *obj = qobject_from_json("{'abc':32,"); + fail_unless(obj == NULL); +} +END_TEST + +#if 0 +START_TEST(invalid_dict_comma) +{ + QObject *obj = qobject_from_json("{'abc':32,}"); + fail_unless(obj == NULL); +} +END_TEST + +START_TEST(unterminated_literal) +{ + QObject *obj = qobject_from_json("nul"); + fail_unless(obj == NULL); +} +END_TEST +#endif + static Suite *qjson_suite(void) { Suite *suite; TCase *string_literals, *number_literals, *keyword_literals; - TCase *dicts, *lists, *whitespace, *varargs; + TCase *dicts, *lists, *whitespace, *varargs, *errors; string_literals = tcase_create("String Literals"); tcase_add_test(string_literals, simple_string); @@ -658,6 +748,22 @@ varargs = tcase_create("Varargs"); tcase_add_test(varargs, simple_varargs); + errors = tcase_create("Invalid JSON"); + tcase_add_test(errors, empty_input); + tcase_add_test(errors, unterminated_string); + tcase_add_test(errors, unterminated_escape); + tcase_add_test(errors, unterminated_sq_string); + tcase_add_test(errors, unterminated_array); + tcase_add_test(errors, unterminated_array_comma); + tcase_add_test(errors, invalid_array_comma); + tcase_add_test(errors, unterminated_dict); + tcase_add_test(errors, unterminated_dict_comma); +#if 0 + /* FIXME: this print parse error messages on stderr. */ + tcase_add_test(errors, invalid_dict_comma); + tcase_add_test(errors, unterminated_literal); +#endif + suite = suite_create("QJSON test-suite"); suite_add_tcase(suite, string_literals); suite_add_tcase(suite, number_literals); @@ -666,6 +772,7 @@ suite_add_tcase(suite, lists); suite_add_tcase(suite, whitespace); suite_add_tcase(suite, varargs); + suite_add_tcase(suite, errors); return suite; } diff -Nru qemu-kvm-0.12.5+noroms/check-qlist.c qemu-kvm-0.14.1/check-qlist.c --- qemu-kvm-0.12.5+noroms/check-qlist.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/check-qlist.c 2011-05-11 13:29:46.000000000 +0000 @@ -6,8 +6,8 @@ * Authors: * Luiz Capitulino * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. */ #include diff -Nru qemu-kvm-0.12.5+noroms/check-qstring.c qemu-kvm-0.14.1/check-qstring.c --- qemu-kvm-0.12.5+noroms/check-qstring.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/check-qstring.c 2011-05-11 13:29:46.000000000 +0000 @@ -5,6 +5,9 @@ * * Authors: * Luiz Capitulino + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. */ #include diff -Nru qemu-kvm-0.12.5+noroms/cmd.c qemu-kvm-0.14.1/cmd.c --- qemu-kvm-0.12.5+noroms/cmd.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/cmd.c 2011-05-11 13:29:46.000000000 +0000 @@ -24,6 +24,7 @@ #include #include "cmd.h" +#include "qemu-aio.h" #define _(x) x /* not gettext support yet */ @@ -149,10 +150,20 @@ args_func = af; } +static void prep_fetchline(void *opaque) +{ + int *fetchable = opaque; + + qemu_aio_set_fd_handler(STDIN_FILENO, NULL, NULL, NULL, NULL, NULL); + *fetchable= 1; +} + +static char *get_prompt(void); + void command_loop(void) { - int c, i, j = 0, done = 0; + int c, i, j = 0, done = 0, fetchable = 0, prompted = 0; char *input; char **v; const cmdinfo_t *ct; @@ -186,7 +197,21 @@ free(cmdline); return; } + while (!done) { + if (!prompted) { + printf("%s", get_prompt()); + fflush(stdout); + qemu_aio_set_fd_handler(STDIN_FILENO, prep_fetchline, NULL, NULL, + NULL, &fetchable); + prompted = 1; + } + + qemu_aio_wait(); + + if (!fetchable) { + continue; + } if ((input = fetchline()) == NULL) break; v = breakline(input, &c); @@ -199,7 +224,11 @@ v[0]); } doneline(input, v); + + prompted = 0; + fetchable = 0; } + qemu_aio_set_fd_handler(STDIN_FILENO, NULL, NULL, NULL, NULL, NULL); } /* from libxcmd/input.c */ @@ -270,8 +299,6 @@ if (!line) return NULL; - printf("%s", get_prompt()); - fflush(stdout); if (!fgets(line, MAXREADLINESZ, stdin)) { free(line); return NULL; @@ -287,7 +314,8 @@ { char *result = *input; if (result != NULL) { - char *p = result; + char *p; + for (p = result; *p != '\0'; p++) { if (strchr(delim, *p)) { break; diff -Nru qemu-kvm-0.12.5+noroms/cmd.h qemu-kvm-0.14.1/cmd.h --- qemu-kvm-0.12.5+noroms/cmd.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/cmd.h 2011-05-11 13:29:46.000000000 +0000 @@ -38,33 +38,33 @@ extern cmdinfo_t *cmdtab; extern int ncmds; -extern void help_init(void); -extern void quit_init(void); +void help_init(void); +void quit_init(void); typedef int (*argsfunc_t)(int index); typedef int (*checkfunc_t)(const cmdinfo_t *ci); -extern void add_command(const cmdinfo_t *ci); -extern void add_user_command(char *optarg); -extern void add_args_command(argsfunc_t af); -extern void add_check_command(checkfunc_t cf); - -extern const cmdinfo_t *find_command(const char *cmd); - -extern void command_loop(void); -extern int command_usage(const cmdinfo_t *ci); -extern int command(const cmdinfo_t *ci, int argc, char **argv); +void add_command(const cmdinfo_t *ci); +void add_user_command(char *optarg); +void add_args_command(argsfunc_t af); +void add_check_command(checkfunc_t cf); + +const cmdinfo_t *find_command(const char *cmd); + +void command_loop(void); +int command_usage(const cmdinfo_t *ci); +int command(const cmdinfo_t *ci, int argc, char **argv); /* from input.h */ -extern char **breakline(char *input, int *count); -extern void doneline(char *input, char **vec); -extern char *fetchline(void); +char **breakline(char *input, int *count); +void doneline(char *input, char **vec); +char *fetchline(void); -extern long long cvtnum(char *s); -extern void cvtstr(double value, char *str, size_t sz); +long long cvtnum(char *s); +void cvtstr(double value, char *str, size_t sz); -extern struct timeval tsub(struct timeval t1, struct timeval t2); -extern double tdiv(double value, struct timeval tv); +struct timeval tsub(struct timeval t1, struct timeval t2); +double tdiv(double value, struct timeval tv); enum { DEFAULT_TIME = 0x0, @@ -72,7 +72,7 @@ VERBOSE_FIXED_TIME = 0x2 }; -extern void timestr(struct timeval *tv, char *str, size_t sz, int flags); +void timestr(struct timeval *tv, char *str, size_t sz, int flags); extern char *progname; diff -Nru qemu-kvm-0.12.5+noroms/cocoa.m qemu-kvm-0.14.1/cocoa.m --- qemu-kvm-0.12.5+noroms/cocoa.m 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/cocoa.m 1970-01-01 00:00:00.000000000 +0000 @@ -1,982 +0,0 @@ -/* - * QEMU Cocoa CG display driver - * - * Copyright (c) 2008 Mike Kronenberg - * - * 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. - */ - -#import - -#include "qemu-common.h" -#include "console.h" -#include "sysemu.h" - - -//#define DEBUG - -#ifdef DEBUG -#define COCOA_DEBUG(...) { (void) fprintf (stdout, __VA_ARGS__); } -#else -#define COCOA_DEBUG(...) ((void) 0) -#endif - -#define cgrect(nsrect) (*(CGRect *)&(nsrect)) -#define COCOA_MOUSE_EVENT \ - if (isTabletEnabled) { \ - kbd_mouse_event((int)(p.x * 0x7FFF / (screen.width - 1)), (int)((screen.height - p.y) * 0x7FFF / (screen.height - 1)), 0, buttons); \ - } else if (isMouseGrabed) { \ - kbd_mouse_event((int)[event deltaX], (int)[event deltaY], 0, buttons); \ - } else { \ - [NSApp sendEvent:event]; \ - } - -typedef struct { - int width; - int height; - int bitsPerComponent; - int bitsPerPixel; -} QEMUScreen; - -int qemu_main(int argc, char **argv); // main defined in qemu/vl.c -NSWindow *normalWindow; -id cocoaView; -static DisplayChangeListener *dcl; - -int gArgc; -char **gArgv; - -// keymap conversion -int keymap[] = -{ -// SdlI macI macH SdlH 104xtH 104xtC sdl - 30, // 0 0x00 0x1e A QZ_a - 31, // 1 0x01 0x1f S QZ_s - 32, // 2 0x02 0x20 D QZ_d - 33, // 3 0x03 0x21 F QZ_f - 35, // 4 0x04 0x23 H QZ_h - 34, // 5 0x05 0x22 G QZ_g - 44, // 6 0x06 0x2c Z QZ_z - 45, // 7 0x07 0x2d X QZ_x - 46, // 8 0x08 0x2e C QZ_c - 47, // 9 0x09 0x2f V QZ_v - 0, // 10 0x0A Undefined - 48, // 11 0x0B 0x30 B QZ_b - 16, // 12 0x0C 0x10 Q QZ_q - 17, // 13 0x0D 0x11 W QZ_w - 18, // 14 0x0E 0x12 E QZ_e - 19, // 15 0x0F 0x13 R QZ_r - 21, // 16 0x10 0x15 Y QZ_y - 20, // 17 0x11 0x14 T QZ_t - 2, // 18 0x12 0x02 1 QZ_1 - 3, // 19 0x13 0x03 2 QZ_2 - 4, // 20 0x14 0x04 3 QZ_3 - 5, // 21 0x15 0x05 4 QZ_4 - 7, // 22 0x16 0x07 6 QZ_6 - 6, // 23 0x17 0x06 5 QZ_5 - 13, // 24 0x18 0x0d = QZ_EQUALS - 10, // 25 0x19 0x0a 9 QZ_9 - 8, // 26 0x1A 0x08 7 QZ_7 - 12, // 27 0x1B 0x0c - QZ_MINUS - 9, // 28 0x1C 0x09 8 QZ_8 - 11, // 29 0x1D 0x0b 0 QZ_0 - 27, // 30 0x1E 0x1b ] QZ_RIGHTBRACKET - 24, // 31 0x1F 0x18 O QZ_o - 22, // 32 0x20 0x16 U QZ_u - 26, // 33 0x21 0x1a [ QZ_LEFTBRACKET - 23, // 34 0x22 0x17 I QZ_i - 25, // 35 0x23 0x19 P QZ_p - 28, // 36 0x24 0x1c ENTER QZ_RETURN - 38, // 37 0x25 0x26 L QZ_l - 36, // 38 0x26 0x24 J QZ_j - 40, // 39 0x27 0x28 ' QZ_QUOTE - 37, // 40 0x28 0x25 K QZ_k - 39, // 41 0x29 0x27 ; QZ_SEMICOLON - 43, // 42 0x2A 0x2b \ QZ_BACKSLASH - 51, // 43 0x2B 0x33 , QZ_COMMA - 53, // 44 0x2C 0x35 / QZ_SLASH - 49, // 45 0x2D 0x31 N QZ_n - 50, // 46 0x2E 0x32 M QZ_m - 52, // 47 0x2F 0x34 . QZ_PERIOD - 15, // 48 0x30 0x0f TAB QZ_TAB - 57, // 49 0x31 0x39 SPACE QZ_SPACE - 41, // 50 0x32 0x29 ` QZ_BACKQUOTE - 14, // 51 0x33 0x0e BKSP QZ_BACKSPACE - 0, // 52 0x34 Undefined - 1, // 53 0x35 0x01 ESC QZ_ESCAPE - 0, // 54 0x36 QZ_RMETA - 0, // 55 0x37 QZ_LMETA - 42, // 56 0x38 0x2a L SHFT QZ_LSHIFT - 58, // 57 0x39 0x3a CAPS QZ_CAPSLOCK - 56, // 58 0x3A 0x38 L ALT QZ_LALT - 29, // 59 0x3B 0x1d L CTRL QZ_LCTRL - 54, // 60 0x3C 0x36 R SHFT QZ_RSHIFT - 184,// 61 0x3D 0xb8 E0,38 R ALT QZ_RALT - 157,// 62 0x3E 0x9d E0,1D R CTRL QZ_RCTRL - 0, // 63 0x3F Undefined - 0, // 64 0x40 Undefined - 0, // 65 0x41 Undefined - 0, // 66 0x42 Undefined - 55, // 67 0x43 0x37 KP * QZ_KP_MULTIPLY - 0, // 68 0x44 Undefined - 78, // 69 0x45 0x4e KP + QZ_KP_PLUS - 0, // 70 0x46 Undefined - 69, // 71 0x47 0x45 NUM QZ_NUMLOCK - 0, // 72 0x48 Undefined - 0, // 73 0x49 Undefined - 0, // 74 0x4A Undefined - 181,// 75 0x4B 0xb5 E0,35 KP / QZ_KP_DIVIDE - 152,// 76 0x4C 0x9c E0,1C KP EN QZ_KP_ENTER - 0, // 77 0x4D undefined - 74, // 78 0x4E 0x4a KP - QZ_KP_MINUS - 0, // 79 0x4F Undefined - 0, // 80 0x50 Undefined - 0, // 81 0x51 QZ_KP_EQUALS - 82, // 82 0x52 0x52 KP 0 QZ_KP0 - 79, // 83 0x53 0x4f KP 1 QZ_KP1 - 80, // 84 0x54 0x50 KP 2 QZ_KP2 - 81, // 85 0x55 0x51 KP 3 QZ_KP3 - 75, // 86 0x56 0x4b KP 4 QZ_KP4 - 76, // 87 0x57 0x4c KP 5 QZ_KP5 - 77, // 88 0x58 0x4d KP 6 QZ_KP6 - 71, // 89 0x59 0x47 KP 7 QZ_KP7 - 0, // 90 0x5A Undefined - 72, // 91 0x5B 0x48 KP 8 QZ_KP8 - 73, // 92 0x5C 0x49 KP 9 QZ_KP9 - 0, // 93 0x5D Undefined - 0, // 94 0x5E Undefined - 0, // 95 0x5F Undefined - 63, // 96 0x60 0x3f F5 QZ_F5 - 64, // 97 0x61 0x40 F6 QZ_F6 - 65, // 98 0x62 0x41 F7 QZ_F7 - 61, // 99 0x63 0x3d F3 QZ_F3 - 66, // 100 0x64 0x42 F8 QZ_F8 - 67, // 101 0x65 0x43 F9 QZ_F9 - 0, // 102 0x66 Undefined - 87, // 103 0x67 0x57 F11 QZ_F11 - 0, // 104 0x68 Undefined - 183,// 105 0x69 0xb7 QZ_PRINT - 0, // 106 0x6A Undefined - 70, // 107 0x6B 0x46 SCROLL QZ_SCROLLOCK - 0, // 108 0x6C Undefined - 68, // 109 0x6D 0x44 F10 QZ_F10 - 0, // 110 0x6E Undefined - 88, // 111 0x6F 0x58 F12 QZ_F12 - 0, // 112 0x70 Undefined - 110,// 113 0x71 0x0 QZ_PAUSE - 210,// 114 0x72 0xd2 E0,52 INSERT QZ_INSERT - 199,// 115 0x73 0xc7 E0,47 HOME QZ_HOME - 201,// 116 0x74 0xc9 E0,49 PG UP QZ_PAGEUP - 211,// 117 0x75 0xd3 E0,53 DELETE QZ_DELETE - 62, // 118 0x76 0x3e F4 QZ_F4 - 207,// 119 0x77 0xcf E0,4f END QZ_END - 60, // 120 0x78 0x3c F2 QZ_F2 - 209,// 121 0x79 0xd1 E0,51 PG DN QZ_PAGEDOWN - 59, // 122 0x7A 0x3b F1 QZ_F1 - 203,// 123 0x7B 0xcb e0,4B L ARROW QZ_LEFT - 205,// 124 0x7C 0xcd e0,4D R ARROW QZ_RIGHT - 208,// 125 0x7D 0xd0 E0,50 D ARROW QZ_DOWN - 200,// 126 0x7E 0xc8 E0,48 U ARROW QZ_UP -/* completed according to http://www.libsdl.org/cgi/cvsweb.cgi/SDL12/src/video/quartz/SDL_QuartzKeys.h?rev=1.6&content-type=text/x-cvsweb-markup */ - -/* Aditional 104 Key XP-Keyboard Scancodes from http://www.computer-engineering.org/ps2keyboard/scancodes1.html */ -/* - 219 // 0xdb e0,5b L GUI - 220 // 0xdc e0,5c R GUI - 221 // 0xdd e0,5d APPS - // E0,2A,E0,37 PRNT SCRN - // E1,1D,45,E1,9D,C5 PAUSE - 83 // 0x53 0x53 KP . -// ACPI Scan Codes - 222 // 0xde E0, 5E Power - 223 // 0xdf E0, 5F Sleep - 227 // 0xe3 E0, 63 Wake -// Windows Multimedia Scan Codes - 153 // 0x99 E0, 19 Next Track - 144 // 0x90 E0, 10 Previous Track - 164 // 0xa4 E0, 24 Stop - 162 // 0xa2 E0, 22 Play/Pause - 160 // 0xa0 E0, 20 Mute - 176 // 0xb0 E0, 30 Volume Up - 174 // 0xae E0, 2E Volume Down - 237 // 0xed E0, 6D Media Select - 236 // 0xec E0, 6C E-Mail - 161 // 0xa1 E0, 21 Calculator - 235 // 0xeb E0, 6B My Computer - 229 // 0xe5 E0, 65 WWW Search - 178 // 0xb2 E0, 32 WWW Home - 234 // 0xea E0, 6A WWW Back - 233 // 0xe9 E0, 69 WWW Forward - 232 // 0xe8 E0, 68 WWW Stop - 231 // 0xe7 E0, 67 WWW Refresh - 230 // 0xe6 E0, 66 WWW Favorites -*/ -}; - -int cocoa_keycode_to_qemu(int keycode) -{ - if((sizeof(keymap)/sizeof(int)) <= keycode) - { - printf("(cocoa) warning unknow keycode 0x%x\n", keycode); - return 0; - } - return keymap[keycode]; -} - - - -/* - ------------------------------------------------------ - QemuCocoaView - ------------------------------------------------------ -*/ -@interface QemuCocoaView : NSView -{ - QEMUScreen screen; - NSWindow *fullScreenWindow; - float cx,cy,cw,ch,cdx,cdy; - CGDataProviderRef dataProviderRef; - int modifiers_state[256]; - BOOL isMouseGrabed; - BOOL isFullscreen; - BOOL isAbsoluteEnabled; - BOOL isTabletEnabled; -} -- (void) resizeContentToWidth:(int)w height:(int)h displayState:(DisplayState *)ds; -- (void) grabMouse; -- (void) ungrabMouse; -- (void) toggleFullScreen:(id)sender; -- (void) handleEvent:(NSEvent *)event; -- (void) setAbsoluteEnabled:(BOOL)tIsAbsoluteEnabled; -- (BOOL) isMouseGrabed; -- (BOOL) isAbsoluteEnabled; -- (float) cdx; -- (float) cdy; -- (QEMUScreen) gscreen; -@end - -@implementation QemuCocoaView -- (id)initWithFrame:(NSRect)frameRect -{ - COCOA_DEBUG("QemuCocoaView: initWithFrame\n"); - - self = [super initWithFrame:frameRect]; - if (self) { - - screen.bitsPerComponent = 8; - screen.bitsPerPixel = 32; - screen.width = frameRect.size.width; - screen.height = frameRect.size.height; - - } - return self; -} - -- (void) dealloc -{ - COCOA_DEBUG("QemuCocoaView: dealloc\n"); - - if (dataProviderRef) - CGDataProviderRelease(dataProviderRef); - - [super dealloc]; -} - -- (void) drawRect:(NSRect) rect -{ - COCOA_DEBUG("QemuCocoaView: drawRect\n"); - - // get CoreGraphic context - CGContextRef viewContextRef = [[NSGraphicsContext currentContext] graphicsPort]; - CGContextSetInterpolationQuality (viewContextRef, kCGInterpolationNone); - CGContextSetShouldAntialias (viewContextRef, NO); - - // draw screen bitmap directly to Core Graphics context - if (dataProviderRef) { - CGImageRef imageRef = CGImageCreate( - screen.width, //width - screen.height, //height - screen.bitsPerComponent, //bitsPerComponent - screen.bitsPerPixel, //bitsPerPixel - (screen.width * (screen.bitsPerComponent/2)), //bytesPerRow -#if __LITTLE_ENDIAN__ - CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB), //colorspace for OS X >= 10.4 - kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipFirst, -#else - CGColorSpaceCreateDeviceRGB(), //colorspace for OS X < 10.4 (actually ppc) - kCGImageAlphaNoneSkipFirst, //bitmapInfo -#endif - dataProviderRef, //provider - NULL, //decode - 0, //interpolate - kCGRenderingIntentDefault //intent - ); -// test if host support "CGImageCreateWithImageInRect" at compiletime -#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4) - if (CGImageCreateWithImageInRect == NULL) { // test if "CGImageCreateWithImageInRect" is supported on host at runtime -#endif - // compatibility drawing code (draws everything) (OS X < 10.4) - CGContextDrawImage (viewContextRef, CGRectMake(0, 0, [self bounds].size.width, [self bounds].size.height), imageRef); -#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4) - } else { - // selective drawing code (draws only dirty rectangles) (OS X >= 10.4) - const NSRect *rectList; - int rectCount; - int i; - CGImageRef clipImageRef; - CGRect clipRect; - - [self getRectsBeingDrawn:&rectList count:&rectCount]; - for (i = 0; i < rectCount; i++) { - clipRect.origin.x = rectList[i].origin.x / cdx; - clipRect.origin.y = (float)screen.height - (rectList[i].origin.y + rectList[i].size.height) / cdy; - clipRect.size.width = rectList[i].size.width / cdx; - clipRect.size.height = rectList[i].size.height / cdy; - clipImageRef = CGImageCreateWithImageInRect( - imageRef, - clipRect - ); - CGContextDrawImage (viewContextRef, cgrect(rectList[i]), clipImageRef); - CGImageRelease (clipImageRef); - } - } -#endif - CGImageRelease (imageRef); - } -} - -- (void) setContentDimensions -{ - COCOA_DEBUG("QemuCocoaView: setContentDimensions\n"); - - if (isFullscreen) { - cdx = [[NSScreen mainScreen] frame].size.width / (float)screen.width; - cdy = [[NSScreen mainScreen] frame].size.height / (float)screen.height; - cw = screen.width * cdx; - ch = screen.height * cdy; - cx = ([[NSScreen mainScreen] frame].size.width - cw) / 2.0; - cy = ([[NSScreen mainScreen] frame].size.height - ch) / 2.0; - } else { - cx = 0; - cy = 0; - cw = screen.width; - ch = screen.height; - cdx = 1.0; - cdy = 1.0; - } -} - -- (void) resizeContentToWidth:(int)w height:(int)h displayState:(DisplayState *)ds -{ - COCOA_DEBUG("QemuCocoaView: resizeContent\n"); - - // update screenBuffer - if (dataProviderRef) - CGDataProviderRelease(dataProviderRef); - - //sync host window color space with guests - screen.bitsPerPixel = ds_get_bits_per_pixel(ds); - screen.bitsPerComponent = ds_get_bytes_per_pixel(ds) * 2; - - dataProviderRef = CGDataProviderCreateWithData(NULL, ds_get_data(ds), w * 4 * h, NULL); - - // update windows - if (isFullscreen) { - [[fullScreenWindow contentView] setFrame:[[NSScreen mainScreen] frame]]; - [normalWindow setFrame:NSMakeRect([normalWindow frame].origin.x, [normalWindow frame].origin.y - h + screen.height, w, h + [normalWindow frame].size.height - screen.height) display:NO animate:NO]; - } else { - if (qemu_name) - [normalWindow setTitle:[NSString stringWithFormat:@"QEMU %s", qemu_name]]; - [normalWindow setFrame:NSMakeRect([normalWindow frame].origin.x, [normalWindow frame].origin.y - h + screen.height, w, h + [normalWindow frame].size.height - screen.height) display:YES animate:YES]; - } - screen.width = w; - screen.height = h; - [normalWindow center]; - [self setContentDimensions]; - [self setFrame:NSMakeRect(cx, cy, cw, ch)]; -} - -- (void) toggleFullScreen:(id)sender -{ - COCOA_DEBUG("QemuCocoaView: toggleFullScreen\n"); - - if (isFullscreen) { // switch from fullscreen to desktop - isFullscreen = FALSE; - [self ungrabMouse]; - [self setContentDimensions]; -// test if host support "enterFullScreenMode:withOptions" at compiletime -#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4) - if ([NSView respondsToSelector:@selector(exitFullScreenModeWithOptions:)]) { // test if "exitFullScreenModeWithOptions" is supported on host at runtime - [self exitFullScreenModeWithOptions:nil]; - } else { -#endif - [fullScreenWindow close]; - [normalWindow setContentView: self]; - [normalWindow makeKeyAndOrderFront: self]; - [NSMenu setMenuBarVisible:YES]; -#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4) - } -#endif - } else { // switch from desktop to fullscreen - isFullscreen = TRUE; - [self grabMouse]; - [self setContentDimensions]; -// test if host support "enterFullScreenMode:withOptions" at compiletime -#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4) - if ([NSView respondsToSelector:@selector(enterFullScreenMode:withOptions:)]) { // test if "enterFullScreenMode:withOptions" is supported on host at runtime - [self enterFullScreenMode:[NSScreen mainScreen] withOptions:[NSDictionary dictionaryWithObjectsAndKeys: - [NSNumber numberWithBool:NO], NSFullScreenModeAllScreens, - [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:NO], kCGDisplayModeIsStretched, nil], NSFullScreenModeSetting, - nil]]; - } else { -#endif - [NSMenu setMenuBarVisible:NO]; - fullScreenWindow = [[NSWindow alloc] initWithContentRect:[[NSScreen mainScreen] frame] - styleMask:NSBorderlessWindowMask - backing:NSBackingStoreBuffered - defer:NO]; - [fullScreenWindow setHasShadow:NO]; - [fullScreenWindow setContentView:self]; - [fullScreenWindow makeKeyAndOrderFront:self]; -#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4) - } -#endif - } -} - -- (void) handleEvent:(NSEvent *)event -{ - COCOA_DEBUG("QemuCocoaView: handleEvent\n"); - - int buttons = 0; - int keycode; - NSPoint p = [event locationInWindow]; - - switch ([event type]) { - case NSFlagsChanged: - keycode = cocoa_keycode_to_qemu([event keyCode]); - if (keycode) { - if (keycode == 58 || keycode == 69) { // emulate caps lock and num lock keydown and keyup - kbd_put_keycode(keycode); - kbd_put_keycode(keycode | 0x80); - } else if (is_graphic_console()) { - if (keycode & 0x80) - kbd_put_keycode(0xe0); - if (modifiers_state[keycode] == 0) { // keydown - kbd_put_keycode(keycode & 0x7f); - modifiers_state[keycode] = 1; - } else { // keyup - kbd_put_keycode(keycode | 0x80); - modifiers_state[keycode] = 0; - } - } - } - - // release Mouse grab when pressing ctrl+alt - if (!isFullscreen && ([event modifierFlags] & NSControlKeyMask) && ([event modifierFlags] & NSAlternateKeyMask)) { - [self ungrabMouse]; - } - break; - case NSKeyDown: - - // forward command Key Combos - if ([event modifierFlags] & NSCommandKeyMask) { - [NSApp sendEvent:event]; - return; - } - - // default - keycode = cocoa_keycode_to_qemu([event keyCode]); - - // handle control + alt Key Combos (ctrl+alt is reserved for QEMU) - if (([event modifierFlags] & NSControlKeyMask) && ([event modifierFlags] & NSAlternateKeyMask)) { - switch (keycode) { - - // enable graphic console - case 0x02 ... 0x0a: // '1' to '9' keys - console_select(keycode - 0x02); - break; - } - - // handle keys for graphic console - } else if (is_graphic_console()) { - if (keycode & 0x80) //check bit for e0 in front - kbd_put_keycode(0xe0); - kbd_put_keycode(keycode & 0x7f); //remove e0 bit in front - - // handlekeys for Monitor - } else { - int keysym = 0; - switch([event keyCode]) { - case 115: - keysym = QEMU_KEY_HOME; - break; - case 117: - keysym = QEMU_KEY_DELETE; - break; - case 119: - keysym = QEMU_KEY_END; - break; - case 123: - keysym = QEMU_KEY_LEFT; - break; - case 124: - keysym = QEMU_KEY_RIGHT; - break; - case 125: - keysym = QEMU_KEY_DOWN; - break; - case 126: - keysym = QEMU_KEY_UP; - break; - default: - { - NSString *ks = [event characters]; - if ([ks length] > 0) - keysym = [ks characterAtIndex:0]; - } - } - if (keysym) - kbd_put_keysym(keysym); - } - break; - case NSKeyUp: - keycode = cocoa_keycode_to_qemu([event keyCode]); - if (is_graphic_console()) { - if (keycode & 0x80) - kbd_put_keycode(0xe0); - kbd_put_keycode(keycode | 0x80); //add 128 to signal release of key - } - break; - case NSMouseMoved: - if (isAbsoluteEnabled) { - if (p.x < 0 || p.x > screen.width || p.y < 0 || p.y > screen.height || ![[self window] isKeyWindow]) { - if (isTabletEnabled) { // if we leave the window, deactivate the tablet - [NSCursor unhide]; - isTabletEnabled = FALSE; - } - } else { - if (!isTabletEnabled) { // if we enter the window, activate the tablet - [NSCursor hide]; - isTabletEnabled = TRUE; - } - } - } - COCOA_MOUSE_EVENT - break; - case NSLeftMouseDown: - if ([event modifierFlags] & NSCommandKeyMask) { - buttons |= MOUSE_EVENT_RBUTTON; - } else { - buttons |= MOUSE_EVENT_LBUTTON; - } - COCOA_MOUSE_EVENT - break; - case NSRightMouseDown: - buttons |= MOUSE_EVENT_RBUTTON; - COCOA_MOUSE_EVENT - break; - case NSOtherMouseDown: - buttons |= MOUSE_EVENT_MBUTTON; - COCOA_MOUSE_EVENT - break; - case NSLeftMouseDragged: - if ([event modifierFlags] & NSCommandKeyMask) { - buttons |= MOUSE_EVENT_RBUTTON; - } else { - buttons |= MOUSE_EVENT_LBUTTON; - } - COCOA_MOUSE_EVENT - break; - case NSRightMouseDragged: - buttons |= MOUSE_EVENT_RBUTTON; - COCOA_MOUSE_EVENT - break; - case NSOtherMouseDragged: - buttons |= MOUSE_EVENT_MBUTTON; - COCOA_MOUSE_EVENT - break; - case NSLeftMouseUp: - if (isTabletEnabled) { - COCOA_MOUSE_EVENT - } else if (!isMouseGrabed) { - if (p.x > -1 && p.x < screen.width && p.y > -1 && p.y < screen.height) { - [self grabMouse]; - } else { - [NSApp sendEvent:event]; - } - } else { - COCOA_MOUSE_EVENT - } - break; - case NSRightMouseUp: - COCOA_MOUSE_EVENT - break; - case NSOtherMouseUp: - COCOA_MOUSE_EVENT - break; - case NSScrollWheel: - if (isTabletEnabled || isMouseGrabed) { - kbd_mouse_event(0, 0, -[event deltaY], 0); - } else { - [NSApp sendEvent:event]; - } - break; - default: - [NSApp sendEvent:event]; - } -} - -- (void) grabMouse -{ - COCOA_DEBUG("QemuCocoaView: grabMouse\n"); - - if (!isFullscreen) { - if (qemu_name) - [normalWindow setTitle:[NSString stringWithFormat:@"QEMU %s - (Press ctrl + alt to release Mouse)", qemu_name]]; - else - [normalWindow setTitle:@"QEMU - (Press ctrl + alt to release Mouse)"]; - } - [NSCursor hide]; - CGAssociateMouseAndMouseCursorPosition(FALSE); - isMouseGrabed = TRUE; // while isMouseGrabed = TRUE, QemuCocoaApp sends all events to [cocoaView handleEvent:] -} - -- (void) ungrabMouse -{ - COCOA_DEBUG("QemuCocoaView: ungrabMouse\n"); - - if (!isFullscreen) { - if (qemu_name) - [normalWindow setTitle:[NSString stringWithFormat:@"QEMU %s", qemu_name]]; - else - [normalWindow setTitle:@"QEMU"]; - } - [NSCursor unhide]; - CGAssociateMouseAndMouseCursorPosition(TRUE); - isMouseGrabed = FALSE; -} - -- (void) setAbsoluteEnabled:(BOOL)tIsAbsoluteEnabled {isAbsoluteEnabled = tIsAbsoluteEnabled;} -- (BOOL) isMouseGrabed {return isMouseGrabed;} -- (BOOL) isAbsoluteEnabled {return isAbsoluteEnabled;} -- (float) cdx {return cdx;} -- (float) cdy {return cdy;} -- (QEMUScreen) gscreen {return screen;} -@end - - - -/* - ------------------------------------------------------ - QemuCocoaAppController - ------------------------------------------------------ -*/ -@interface QemuCocoaAppController : NSObject -{ -} -- (void)startEmulationWithArgc:(int)argc argv:(char**)argv; -- (void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo; -- (void)toggleFullScreen:(id)sender; -- (void)showQEMUDoc:(id)sender; -- (void)showQEMUTec:(id)sender; -@end - -@implementation QemuCocoaAppController -- (id) init -{ - COCOA_DEBUG("QemuCocoaAppController: init\n"); - - self = [super init]; - if (self) { - - // create a view and add it to the window - cocoaView = [[QemuCocoaView alloc] initWithFrame:NSMakeRect(0.0, 0.0, 640.0, 480.0)]; - if(!cocoaView) { - fprintf(stderr, "(cocoa) can't create a view\n"); - exit(1); - } - - // create a window - normalWindow = [[NSWindow alloc] initWithContentRect:[cocoaView frame] - styleMask:NSTitledWindowMask|NSMiniaturizableWindowMask|NSClosableWindowMask - backing:NSBackingStoreBuffered defer:NO]; - if(!normalWindow) { - fprintf(stderr, "(cocoa) can't create window\n"); - exit(1); - } - [normalWindow setAcceptsMouseMovedEvents:YES]; - [normalWindow setTitle:[NSString stringWithFormat:@"QEMU"]]; - [normalWindow setContentView:cocoaView]; - [normalWindow makeKeyAndOrderFront:self]; - [normalWindow center]; - - } - return self; -} - -- (void) dealloc -{ - COCOA_DEBUG("QemuCocoaAppController: dealloc\n"); - - if (cocoaView) - [cocoaView release]; - [super dealloc]; -} - -- (void)applicationDidFinishLaunching: (NSNotification *) note -{ - COCOA_DEBUG("QemuCocoaAppController: applicationDidFinishLaunching\n"); - - // Display an open dialog box if no argument were passed or - // if qemu was launched from the finder ( the Finder passes "-psn" ) - if( gArgc <= 1 || strncmp ((char *)gArgv[1], "-psn", 4) == 0) { - NSOpenPanel *op = [[NSOpenPanel alloc] init]; - [op setPrompt:@"Boot image"]; - [op setMessage:@"Select the disk image you want to boot.\n\nHit the \"Cancel\" button to quit"]; - [op beginSheetForDirectory:nil file:nil types:[NSArray arrayWithObjects:@"img",@"iso",@"dmg",@"qcow",@"cow",@"cloop",@"vmdk",nil] - modalForWindow:normalWindow modalDelegate:self - didEndSelector:@selector(openPanelDidEnd:returnCode:contextInfo:) contextInfo:NULL]; - } else { - // or Launch Qemu, with the global args - [self startEmulationWithArgc:gArgc argv:(char **)gArgv]; - } -} - -- (void)applicationWillTerminate:(NSNotification *)aNotification -{ - COCOA_DEBUG("QemuCocoaAppController: applicationWillTerminate\n"); - - qemu_system_shutdown_request(); - exit(0); -} - -- (void)startEmulationWithArgc:(int)argc argv:(char**)argv -{ - COCOA_DEBUG("QemuCocoaAppController: startEmulationWithArgc\n"); - - int status; - status = qemu_main(argc, argv); - exit(status); -} - -- (void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo -{ - COCOA_DEBUG("QemuCocoaAppController: openPanelDidEnd\n"); - - if(returnCode == NSCancelButton) { - exit(0); - } else if(returnCode == NSOKButton) { - char *bin = "qemu"; - char *img = (char*)[ [ sheet filename ] cStringUsingEncoding:NSASCIIStringEncoding]; - - char **argv = (char**)malloc( sizeof(char*)*3 ); - - asprintf(&argv[0], "%s", bin); - asprintf(&argv[1], "-hda"); - asprintf(&argv[2], "%s", img); - - printf("Using argc %d argv %s -hda %s\n", 3, bin, img); - - [self startEmulationWithArgc:3 argv:(char**)argv]; - } -} -- (void)toggleFullScreen:(id)sender -{ - COCOA_DEBUG("QemuCocoaAppController: toggleFullScreen\n"); - - [cocoaView toggleFullScreen:sender]; -} - -- (void)showQEMUDoc:(id)sender -{ - COCOA_DEBUG("QemuCocoaAppController: showQEMUDoc\n"); - - [[NSWorkspace sharedWorkspace] openFile:[NSString stringWithFormat:@"%@/../doc/qemu/qemu-doc.html", - [[NSBundle mainBundle] resourcePath]] withApplication:@"Help Viewer"]; -} - -- (void)showQEMUTec:(id)sender -{ - COCOA_DEBUG("QemuCocoaAppController: showQEMUTec\n"); - - [[NSWorkspace sharedWorkspace] openFile:[NSString stringWithFormat:@"%@/../doc/qemu/qemu-tech.html", - [[NSBundle mainBundle] resourcePath]] withApplication:@"Help Viewer"]; -} -@end - - - -// Dock Connection -typedef struct CPSProcessSerNum -{ - UInt32 lo; - UInt32 hi; -} CPSProcessSerNum; - -extern OSErr CPSGetCurrentProcess( CPSProcessSerNum *psn); -extern OSErr CPSEnableForegroundOperation( CPSProcessSerNum *psn, UInt32 _arg2, UInt32 _arg3, UInt32 _arg4, UInt32 _arg5); -extern OSErr CPSSetFrontProcess( CPSProcessSerNum *psn); - -int main (int argc, const char * argv[]) { - - gArgc = argc; - gArgv = (char **)argv; - CPSProcessSerNum PSN; - - NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; - [NSApplication sharedApplication]; - - if (!CPSGetCurrentProcess(&PSN)) - if (!CPSEnableForegroundOperation(&PSN,0x03,0x3C,0x2C,0x1103)) - if (!CPSSetFrontProcess(&PSN)) - [NSApplication sharedApplication]; - - // Add menus - NSMenu *menu; - NSMenuItem *menuItem; - - [NSApp setMainMenu:[[NSMenu alloc] init]]; - - // Application menu - menu = [[NSMenu alloc] initWithTitle:@""]; - [menu addItemWithTitle:@"About QEMU" action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""]; // About QEMU - [menu addItem:[NSMenuItem separatorItem]]; //Separator - [menu addItemWithTitle:@"Hide QEMU" action:@selector(hide:) keyEquivalent:@"h"]; //Hide QEMU - menuItem = (NSMenuItem *)[menu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"]; // Hide Others - [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)]; - [menu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""]; // Show All - [menu addItem:[NSMenuItem separatorItem]]; //Separator - [menu addItemWithTitle:@"Quit QEMU" action:@selector(terminate:) keyEquivalent:@"q"]; - menuItem = [[NSMenuItem alloc] initWithTitle:@"Apple" action:nil keyEquivalent:@""]; - [menuItem setSubmenu:menu]; - [[NSApp mainMenu] addItem:menuItem]; - [NSApp performSelector:@selector(setAppleMenu:) withObject:menu]; // Workaround (this method is private since 10.4+) - - // View menu - menu = [[NSMenu alloc] initWithTitle:@"View"]; - [menu addItem: [[[NSMenuItem alloc] initWithTitle:@"Enter Fullscreen" action:@selector(toggleFullScreen:) keyEquivalent:@"f"] autorelease]]; // Fullscreen - menuItem = [[[NSMenuItem alloc] initWithTitle:@"View" action:nil keyEquivalent:@""] autorelease]; - [menuItem setSubmenu:menu]; - [[NSApp mainMenu] addItem:menuItem]; - - // Window menu - menu = [[NSMenu alloc] initWithTitle:@"Window"]; - [menu addItem: [[[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"] autorelease]]; // Miniaturize - menuItem = [[[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""] autorelease]; - [menuItem setSubmenu:menu]; - [[NSApp mainMenu] addItem:menuItem]; - [NSApp setWindowsMenu:menu]; - - // Help menu - menu = [[NSMenu alloc] initWithTitle:@"Help"]; - [menu addItem: [[[NSMenuItem alloc] initWithTitle:@"QEMU Documentation" action:@selector(showQEMUDoc:) keyEquivalent:@"?"] autorelease]]; // QEMU Help - [menu addItem: [[[NSMenuItem alloc] initWithTitle:@"QEMU Technology" action:@selector(showQEMUTec:) keyEquivalent:@""] autorelease]]; // QEMU Help - menuItem = [[[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""] autorelease]; - [menuItem setSubmenu:menu]; - [[NSApp mainMenu] addItem:menuItem]; - - // Create an Application controller - QemuCocoaAppController *appController = [[QemuCocoaAppController alloc] init]; - [NSApp setDelegate:appController]; - - // Start the main event loop - [NSApp run]; - - [appController release]; - [pool release]; - - return 0; -} - - - -#pragma mark qemu -static void cocoa_update(DisplayState *ds, int x, int y, int w, int h) -{ - COCOA_DEBUG("qemu_cocoa: cocoa_update\n"); - - NSRect rect; - if ([cocoaView cdx] == 1.0) { - rect = NSMakeRect(x, [cocoaView gscreen].height - y - h, w, h); - } else { - rect = NSMakeRect( - x * [cocoaView cdx], - ([cocoaView gscreen].height - y - h) * [cocoaView cdy], - w * [cocoaView cdx], - h * [cocoaView cdy]); - } - [cocoaView displayRect:rect]; -} - -static void cocoa_resize(DisplayState *ds) -{ - COCOA_DEBUG("qemu_cocoa: cocoa_resize\n"); - - [cocoaView resizeContentToWidth:(int)(ds_get_width(ds)) height:(int)(ds_get_height(ds)) displayState:ds]; -} - -static void cocoa_refresh(DisplayState *ds) -{ - COCOA_DEBUG("qemu_cocoa: cocoa_refresh\n"); - - if (kbd_mouse_is_absolute()) { - if (![cocoaView isAbsoluteEnabled]) { - if ([cocoaView isMouseGrabed]) { - [cocoaView ungrabMouse]; - } - } - [cocoaView setAbsoluteEnabled:YES]; - } - - NSDate *distantPast; - NSEvent *event; - distantPast = [NSDate distantPast]; - do { - event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:distantPast - inMode: NSDefaultRunLoopMode dequeue:YES]; - if (event != nil) { - [cocoaView handleEvent:event]; - } - } while(event != nil); - vga_hw_update(); -} - -static void cocoa_cleanup(void) -{ - COCOA_DEBUG("qemu_cocoa: cocoa_cleanup\n"); - qemu_free(dcl); -} - -void cocoa_display_init(DisplayState *ds, int full_screen) -{ - COCOA_DEBUG("qemu_cocoa: cocoa_display_init\n"); - - dcl = qemu_mallocz(sizeof(DisplayChangeListener)); - - // register vga output callbacks - dcl->dpy_update = cocoa_update; - dcl->dpy_resize = cocoa_resize; - dcl->dpy_refresh = cocoa_refresh; - - register_displaychangelistener(ds, dcl); - - // register cleanup function - atexit(cocoa_cleanup); -} diff -Nru qemu-kvm-0.12.5+noroms/CODING_STYLE qemu-kvm-0.14.1/CODING_STYLE --- qemu-kvm-0.12.5+noroms/CODING_STYLE 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/CODING_STYLE 2011-05-11 13:29:46.000000000 +0000 @@ -1,6 +1,9 @@ Qemu Coding Style ================= +Please use the script checkpatch.pl in the scripts directory to check +patches before submitting. + 1. Whitespace Of course, the most important aspect in any coding style is whitespace. @@ -46,8 +49,8 @@ uint64_t and family. Note that this last convention contradicts POSIX and is therefore likely to be changed. -Typedefs are used to eliminate the redundant 'struct' keyword. It is the -QEMU coding style. +When wrapping standard library functions, use the prefix qemu_ to alert +readers that they are seeing a wrapped version; otherwise avoid this prefix. 4. Block structure diff -Nru qemu-kvm-0.12.5+noroms/compatfd.c qemu-kvm-0.14.1/compatfd.c --- qemu-kvm-0.12.5+noroms/compatfd.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/compatfd.c 2011-05-11 13:29:46.000000000 +0000 @@ -115,29 +115,3 @@ return qemu_signalfd_compat(mask); } - -int qemu_eventfd(int *fds) -{ - int ret; - -#if defined(CONFIG_EVENTFD) - ret = syscall(SYS_eventfd, 0); - if (ret >= 0) { - fds[0] = ret; - qemu_set_cloexec(ret); - if ((fds[1] = dup(ret)) == -1) { - close(ret); - return -1; - } - qemu_set_cloexec(fds[1]); - return 0; - } -#endif - - ret = pipe(fds); - if (ret != -1) { - qemu_set_cloexec(fds[0]); - qemu_set_cloexec(fds[1]); - } - return ret; -} diff -Nru qemu-kvm-0.12.5+noroms/compatfd.h qemu-kvm-0.14.1/compatfd.h --- qemu-kvm-0.12.5+noroms/compatfd.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/compatfd.h 2011-05-11 13:29:46.000000000 +0000 @@ -40,6 +40,4 @@ int qemu_signalfd(const sigset_t *mask); -int qemu_eventfd(int *fds); - #endif diff -Nru qemu-kvm-0.12.5+noroms/configure qemu-kvm-0.14.1/configure --- qemu-kvm-0.12.5+noroms/configure 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/configure 2011-05-11 13:29:46.000000000 +0000 @@ -15,41 +15,178 @@ TMPO="${TMPDIR1}/qemu-conf-${RANDOM}-$$-${RANDOM}.o" TMPE="${TMPDIR1}/qemu-conf-${RANDOM}-$$-${RANDOM}.exe" -trap "rm -f $TMPC $TMPO $TMPE ; exit" 0 2 3 15 +# NB: do not call "exit" in the trap handler; this is buggy with some shells; +# see <1285349658-3122-1-git-send-email-loic.minier@linaro.org> +trap "rm -f $TMPC $TMPO $TMPE" EXIT INT QUIT TERM +rm -f config.log compile_object() { - $cc $QEMU_CFLAGS -c -o $TMPO $TMPC > /dev/null 2> /dev/null + echo $cc $QEMU_CFLAGS -c -o $TMPO $TMPC >> config.log + $cc $QEMU_CFLAGS -c -o $TMPO $TMPC >> config.log 2>&1 } compile_prog() { local_cflags="$1" local_ldflags="$2" - $cc $QEMU_CFLAGS $local_cflags -o $TMPE $TMPC $LDFLAGS $local_ldflags > /dev/null 2> /dev/null + echo $cc $QEMU_CFLAGS $local_cflags -o $TMPE $TMPC $LDFLAGS $local_ldflags >> config.log + $cc $QEMU_CFLAGS $local_cflags -o $TMPE $TMPC $LDFLAGS $local_ldflags >> config.log 2>&1 +} + +# symbolically link $1 to $2. Portable version of "ln -sf". +symlink() { + rm -f $2 + ln -s $1 $2 +} + +# check whether a command is available to this shell (may be either an +# executable or a builtin) +has() { + type "$1" >/dev/null 2>&1 +} + +# search for an executable in PATH +path_of() { + local_command="$1" + local_ifs="$IFS" + local_dir="" + + # pathname has a dir component? + if [ "${local_command#*/}" != "$local_command" ]; then + if [ -x "$local_command" ] && [ ! -d "$local_command" ]; then + echo "$local_command" + return 0 + fi + fi + if [ -z "$local_command" ]; then + return 1 + fi + + IFS=: + for local_dir in $PATH; do + if [ -x "$local_dir/$local_command" ] && [ ! -d "$local_dir/$local_command" ]; then + echo "$local_dir/$local_command" + IFS="${local_ifs:-$(printf ' \t\n')}" + return 0 + fi + done + # not found + IFS="${local_ifs:-$(printf ' \t\n')}" + return 1 } # default parameters +source_path=`dirname "$0"` cpu="" -prefix="" interp_prefix="/usr/gnemul/qemu-%M" static="no" sparc_cpu="" cross_prefix="" -cc="gcc" audio_drv_list="" -audio_card_list="ac97 es1370 sb16" -audio_possible_cards="ac97 es1370 sb16 cs4231a adlib gus" +audio_card_list="ac97 es1370 sb16 hda" +audio_possible_cards="ac97 es1370 sb16 cs4231a adlib gus hda" block_drv_whitelist="" host_cc="gcc" -ar="ar" -make="make" -install="install" -objcopy="objcopy" -ld="ld" helper_cflags="" libs_softmmu="" libs_tools="" audio_pt_int="" audio_win_int="" +cc_i386=i386-pc-linux-gnu-gcc + +target_list="x86_64-softmmu" + +kvm_version() { + local fname="$(dirname "$0")/KVM_VERSION" + + if test -f "$fname"; then + cat "$fname" + else + echo "qemu-kvm-devel" + fi +} + +# Default value for a variable defining feature "foo". +# * foo="no" feature will only be used if --enable-foo arg is given +# * foo="" feature will be searched for, and if found, will be used +# unless --disable-foo is given +# * foo="yes" this value will only be set by --enable-foo flag. +# feature will searched for, +# if not found, configure exits with error +# +# Always add --enable-foo and --disable-foo command line args. +# Distributions want to ensure that several features are compiled in, and it +# is impossible without a --enable-foo that exits if a feature is not found. + +bluez="" +brlapi="" +curl="" +curses="" +docs="" +fdt="" +kvm="" +kvm_para="" +nptl="" +sdl="" +sparse="no" +uuid="" +vde="" +vnc_tls="" +vnc_sasl="" +vnc_jpeg="" +vnc_png="" +vnc_thread="no" +xen="" +linux_aio="" +attr="" +vhost_net="" +xfs="" + +gprof="no" +debug_tcg="no" +debug_mon="no" +debug="no" +strip_opt="yes" +bigendian="no" +mingw32="no" +EXESUF="" +prefix="/usr/local" +mandir="\${prefix}/share/man" +datadir="\${prefix}/share/qemu" +docdir="\${prefix}/share/doc/qemu" +bindir="\${prefix}/bin" +sysconfdir="\${prefix}/etc" +confsuffix="/qemu" +slirp="yes" +fmod_lib="" +fmod_inc="" +oss_lib="" +bsd="no" +linux="no" +solaris="no" +profiler="no" +cocoa="no" +softmmu="yes" +linux_user="no" +darwin_user="no" +bsd_user="no" +guest_base="" +uname_release="" +io_thread="no" +mixemu="no" +kvm_cap_pit="" +kvm_cap_device_assignment="" +kerneldir="" +aix="no" +blobs="yes" +pkgversion=" ($(kvm_version))" +cpu_emulation="yes" +check_utests="no" +user_pie="no" +zero_malloc="" +trace_backend="nop" +trace_file="trace" +spice="" +rbd="" # parse CC options first for opt do @@ -57,7 +194,9 @@ case "$opt" in --cross-prefix=*) cross_prefix="$optarg" ;; - --cc=*) cc="$optarg" + --cc=*) CC="$optarg" + ;; + --source-path=*) source_path="$optarg" ;; --cpu=*) cpu="$optarg" ;; @@ -86,10 +225,14 @@ # Using uname is really, really broken. Once we have the right set of checks # we can eliminate it's usage altogether -cc="${cross_prefix}${cc}" -ar="${cross_prefix}${ar}" -objcopy="${cross_prefix}${objcopy}" -ld="${cross_prefix}${ld}" +cc="${cross_prefix}${CC-gcc}" +ar="${cross_prefix}${AR-ar}" +objcopy="${cross_prefix}${OBJCOPY-objcopy}" +ld="${cross_prefix}${LD-ld}" +strip="${cross_prefix}${STRIP-strip}" +windres="${cross_prefix}${WINDRES-windres}" +pkg_config="${cross_prefix}${PKG_CONFIG-pkg-config}" +sdl_config="${cross_prefix}${SDL_CONFIG-sdl-config}" # default flags for all hosts QEMU_CFLAGS="-fno-strict-aliasing $QEMU_CFLAGS" @@ -97,31 +240,12 @@ QEMU_CFLAGS="-Wall -Wundef -Wendif-labels -Wwrite-strings -Wmissing-prototypes $QEMU_CFLAGS" QEMU_CFLAGS="-Wstrict-prototypes -Wredundant-decls $QEMU_CFLAGS" QEMU_CFLAGS="-D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE $QEMU_CFLAGS" -QEMU_CFLAGS="-U_FORTIFY_SOURCE $QEMU_CFLAGS" -QEMU_CFLAGS="-I. -I\$(SRC_PATH) $QEMU_CFLAGS" +QEMU_CFLAGS="-D_FORTIFY_SOURCE=2 $QEMU_CFLAGS" +QEMU_INCLUDES="-I. -I\$(SRC_PATH)" LDFLAGS="-g $LDFLAGS" -gcc_flags="-Wold-style-declaration -Wold-style-definition" -cat > $TMPC << EOF -int main(void) { } -EOF -for flag in $gcc_flags; do - if compile_prog "$QEMU_CFLAGS" "$flag" ; then - QEMU_CFLAGS="$flag $QEMU_CFLAGS" - fi -done - -# check that the C compiler works. -cat > $TMPC < $TMPC <" +echo " --disable-spice disable spice" +echo " --enable-spice enable spice" +echo " --enable-rbd enable building the rados block device (rbd)" echo "" echo "NOTE: The object files are built at the place where configure is launched" exit 1 fi +# check that the C compiler works. +cat > $TMPC < $TMPC << EOF +int main(void) { return 0; } +EOF +for flag in $gcc_flags; do + if compile_prog "-Werror $QEMU_CFLAGS" "-Werror $flag" ; then + QEMU_CFLAGS="$QEMU_CFLAGS $flag" + fi +done + # # Solaris specific configure tool chain decisions # if test "$solaris" = "yes" ; then - solinst=`which $install 2> /dev/null | /usr/bin/grep -v "no $install in"` - if test -z "$solinst" ; then + if has $install; then + : + else echo "Solaris install program not found. Use --install=/usr/ucb/install or" echo "install fileutils from www.blastwave.org using pkg-get -i fileutils" echo "to get ginstall which is used by default (which lives in /opt/csw/bin)" exit 1 fi - if test "$solinst" = "/usr/sbin/install" ; then + if test "`path_of $install`" = "/usr/sbin/install" ; then echo "Error: Solaris /usr/sbin/install is not an appropriate install program." echo "try ginstall from the GNU fileutils available from www.blastwave.org" echo "using pkg-get -i fileutils, or use --install=/usr/ucb/install" exit 1 fi - sol_ar=`which ar 2> /dev/null | /usr/bin/grep -v "no ar in"` - if test -z "$sol_ar" ; then + if has ar; then + : + else echo "Error: No path includes ar" if test -f /usr/ccs/bin/ar ; then echo "Add /usr/ccs/bin to your path and rerun configure" @@ -892,6 +1075,13 @@ echo "No targets enabled" exit 1 fi +# see if system emulation was really requested +case " $target_list " in + *"-softmmu "*) softmmu=yes + ;; + *) softmmu=no + ;; +esac feature_not_found() { feature=$1 @@ -932,13 +1122,15 @@ fi -# host long bits test -hostlongbits="32" -case "$cpu" in - x86_64|alpha|ia64|sparc64|ppc64|s390x) - hostlongbits=64 - ;; -esac +# host long bits test, actually a pointer size test +cat > $TMPC << EOF +int sizeof_pointer_is_8[sizeof(void *) == 8 ? 1 : -1]; +EOF +if compile_object; then +hostlongbits=64 +else +hostlongbits=32 +fi ########################################## @@ -1005,9 +1197,17 @@ fi ########################################## +# pkg-config probe + +if ! has $pkg_config; then + echo warning: proceeding without "$pkg_config" >&2 + pkg_config=/bin/false +fi + +########################################## # Sparse probe if test "$sparse" != "no" ; then - if test -x "$(which cgcc 2>/dev/null)"; then + if has cgcc; then sparse=yes else if test "$sparse" = "yes" ; then @@ -1020,18 +1220,42 @@ ########################################## # SDL probe -sdl_too_old=no +# Look for sdl configuration program (pkg-config or sdl-config). Try +# sdl-config even without cross prefix, and favour pkg-config over sdl-config. +if test "`basename $sdl_config`" != sdl-config && ! has ${sdl_config}; then + sdl_config=sdl-config +fi + +if $pkg_config sdl --modversion >/dev/null 2>&1; then + sdlconfig="$pkg_config sdl" + _sdlversion=`$sdlconfig --modversion 2>/dev/null | sed 's/[^0-9]//g'` +elif has ${sdl_config}; then + sdlconfig="$sdl_config" + _sdlversion=`$sdlconfig --version | sed 's/[^0-9]//g'` +else + if test "$sdl" = "yes" ; then + feature_not_found "sdl" + fi + sdl=no +fi +if test -n "$cross_prefix" && test "`basename $sdlconfig`" = sdl-config; then + echo warning: using "\"$sdlconfig\"" to detect cross-compiled sdl >&2 +fi +sdl_too_old=no if test "$sdl" != "no" ; then cat > $TMPC << EOF #include #undef main /* We don't want SDL to override our main() */ int main( void ) { return SDL_Init (SDL_INIT_VIDEO); } EOF - sdl_cflags=`sdl-config --cflags 2> /dev/null` - sdl_libs=`sdl-config --libs 2> /dev/null` + sdl_cflags=`$sdlconfig --cflags 2> /dev/null` + if test "$static" = "yes" ; then + sdl_libs=`$sdlconfig --static-libs 2>/dev/null` + else + sdl_libs=`$sdlconfig --libs 2> /dev/null` + fi if compile_prog "$sdl_cflags" "$sdl_libs" ; then - _sdlversion=`sdl-config --version | sed 's/[^0-9]//g'` if test "$_sdlversion" -lt 121 ; then sdl_too_old=yes else @@ -1040,12 +1264,11 @@ fi fi - # static link with sdl ? + # static link with sdl ? (note: sdl.pc's --static --libs is broken) if test "$sdl" = "yes" -a "$static" = "yes" ; then - sdl_libs=`sdl-config --static-libs 2>/dev/null` - if test `sdl-config --static-libs 2>/dev/null | grep \\\-laa > /dev/null` ; then - sdl_libs="$sdl_libs `aalib-config --static-libs >2 /dev/null`" - sdl_cflags="$sd_cflags `aalib-config --cflags >2 /dev/null`" + if test $? = 0 && echo $sdl_libs | grep -- -laa > /dev/null; then + sdl_libs="$sdl_libs `aalib-config --static-libs 2>/dev/null`" + sdl_cflags="$sdl_cflags `aalib-config --cflags 2>/dev/null`" fi if compile_prog "$sdl_cflags" "$sdl_libs" ; then : @@ -1087,8 +1310,8 @@ #include int main(void) { gnutls_session_t s; gnutls_init(&s, GNUTLS_SERVER); return 0; } EOF - vnc_tls_cflags=`pkg-config --cflags gnutls 2> /dev/null` - vnc_tls_libs=`pkg-config --libs gnutls 2> /dev/null` + vnc_tls_cflags=`$pkg_config --cflags gnutls 2> /dev/null` + vnc_tls_libs=`$pkg_config --libs gnutls 2> /dev/null` if compile_prog "$vnc_tls_cflags" "$vnc_tls_libs" ; then vnc_tls=yes libs_softmmu="$vnc_tls_libs $libs_softmmu" @@ -1123,6 +1346,53 @@ fi ########################################## +# VNC JPEG detection +if test "$vnc_jpeg" != "no" ; then +cat > $TMPC < +#include +int main(void) { struct jpeg_compress_struct s; jpeg_create_compress(&s); return 0; } +EOF + vnc_jpeg_cflags="" + vnc_jpeg_libs="-ljpeg" + if compile_prog "$vnc_jpeg_cflags" "$vnc_jpeg_libs" ; then + vnc_jpeg=yes + libs_softmmu="$vnc_jpeg_libs $libs_softmmu" + else + if test "$vnc_jpeg" = "yes" ; then + feature_not_found "vnc-jpeg" + fi + vnc_jpeg=no + fi +fi + +########################################## +# VNC PNG detection +if test "$vnc_png" != "no" ; then +cat > $TMPC < +#include +#include +int main(void) { + png_structp png_ptr; + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + return 0; +} +EOF + vnc_png_cflags="" + vnc_png_libs="-lpng" + if compile_prog "$vnc_png_cflags" "$vnc_png_libs" ; then + vnc_png=yes + libs_softmmu="$vnc_png_libs $libs_softmmu" + else + if test "$vnc_png" = "yes" ; then + feature_not_found "vnc-png" + fi + vnc_png=no + fi +fi + +########################################## # fnmatch() probe, used for ACL routines fnmatch="no" cat > $TMPC << EOF @@ -1163,6 +1433,27 @@ fi ########################################## +# xfsctl() probe, used for raw-posix +if test "$xfs" != "no" ; then + cat > $TMPC << EOF +#include +int main(void) +{ + xfsctl(NULL, 0, 0, NULL); + return 0; +} +EOF + if compile_prog "" "" ; then + xfs="yes" + else + if test "$xfs" = "yes" ; then + feature_not_found "xfs" + fi + xfs=no + fi +fi + +########################################## # vde libraries probe if test "$vde" != "no" ; then vde_libs="-lvdeplug" @@ -1287,6 +1578,7 @@ brlapi_libs="-lbrlapi" cat > $TMPC << EOF #include +#include int main( void ) { return brlapi__openConnection (NULL, NULL, NULL); } EOF if compile_prog "" "$brlapi_libs" ; then @@ -1333,13 +1625,19 @@ ########################################## # curl probe +if $pkg_config libcurl --modversion >/dev/null 2>&1; then + curlconfig="$pkg_config libcurl" +else + curlconfig=curl-config +fi + if test "$curl" != "no" ; then cat > $TMPC << EOF #include int main(void) { return curl_easy_init(); } EOF - curl_cflags=`curl-config --cflags 2>/dev/null` - curl_libs=`curl-config --libs 2>/dev/null` + curl_cflags=`$curlconfig --cflags 2>/dev/null` + curl_libs=`$curlconfig --libs 2>/dev/null` if compile_prog "$curl_cflags" "$curl_libs" ; then curl=yes libs_tools="$curl_libs $libs_tools" @@ -1360,7 +1658,7 @@ #include int main(void) { suite_create("qemu test"); return 0; } EOF - check_libs=`pkg-config --libs check` + check_libs=`$pkg_config --libs check` if compile_prog "" $check_libs ; then check_utests=yes libs_tools="$check_libs $libs_tools" @@ -1379,8 +1677,8 @@ #include int main(void) { return bt_error(0); } EOF - bluez_cflags=`pkg-config --cflags bluez 2> /dev/null` - bluez_libs=`pkg-config --libs bluez 2> /dev/null` + bluez_cflags=`$pkg_config --cflags bluez 2> /dev/null` + bluez_libs=`$pkg_config --libs bluez 2> /dev/null` if compile_prog "$bluez_cflags" "$bluez_libs" ; then bluez=yes libs_softmmu="$bluez_libs $libs_softmmu" @@ -1400,15 +1698,31 @@ #if !defined(KVM_API_VERSION) || KVM_API_VERSION < 12 || KVM_API_VERSION > 12 #error Invalid KVM version #endif -#if !defined(KVM_CAP_USER_MEMORY) -#error Missing KVM capability KVM_CAP_USER_MEMORY -#endif -#if !defined(KVM_CAP_SET_TSS_ADDR) -#error Missing KVM capability KVM_CAP_SET_TSS_ADDR -#endif -#if !defined(KVM_CAP_DESTROY_MEMORY_REGION_WORKS) -#error Missing KVM capability KVM_CAP_DESTROY_MEMORY_REGION_WORKS +EOF + must_have_caps="KVM_CAP_USER_MEMORY \ + KVM_CAP_DESTROY_MEMORY_REGION_WORKS \ + KVM_CAP_COALESCED_MMIO \ + KVM_CAP_SYNC_MMU \ + " + if test \( "$cpu" = "i386" -o "$cpu" = "x86_64" \) ; then + must_have_caps="$caps \ + KVM_CAP_SET_TSS_ADDR \ + KVM_CAP_EXT_CPUID \ + KVM_CAP_CLOCKSOURCE \ + KVM_CAP_NOP_IO_DELAY \ + KVM_CAP_PV_MMU \ + KVM_CAP_MP_STATE \ + KVM_CAP_USER_NMI \ + " + fi + for c in $must_have_caps ; do + cat >> $TMPC <> $TMPC </dev/null` + if test "$kvm_cflags" = ""; then case "$cpu" in i386 | x86_64) kvm_arch="x86" @@ -1438,21 +1754,28 @@ kvm_cflags="-I$source_path/kvm/include" kvm_cflags="$kvm_cflags -include $source_path/kvm/include/linux/config.h" kvm_cflags="$kvm_cflags -I$source_path/kvm/include/$kvm_arch" + fi fi kvm_cflags="$kvm_cflags -idirafter $source_path/compat" if compile_prog "$kvm_cflags" "" ; then kvm=yes + cat > $TMPC < +int main(void) { return 0; } +EOF + if compile_prog "$kvm_cflags" "" ; then + kvm_para=yes + fi else if test "$kvm" = "yes" ; then - if [ -x "`which awk 2>/dev/null`" ] && \ - [ -x "`which grep 2>/dev/null`" ]; then + if has awk && has grep; then kvmerr=`LANG=C $cc $QEMU_CFLAGS -o $TMPE $kvm_cflags $TMPC 2>&1 \ | grep "error: " \ | awk -F "error: " '{if (NR>1) printf(", "); printf("%s",$2);}'` if test "$kvmerr" != "" ; then echo -e "${kvmerr}\n\ - NOTE: To enable KVM support, update your kernel to 2.6.29+ or install \ - recent kvm-kmod from http://sourceforge.net/projects/kvm." +NOTE: To enable KVM support, update your kernel to 2.6.29+ or install \ +recent kvm-kmod from http://sourceforge.net/projects/kvm." fi fi feature_not_found "kvm" @@ -1510,20 +1833,20 @@ fi ########################################## -# libpci probe for kvm_cap_device_assignment +# libpci header probe for kvm_cap_device_assignment if test $kvm_cap_device_assignment = "yes" ; then cat > $TMPC << EOF -#include +#include #ifndef PCI_VENDOR_ID -#error NO LIBPCI +#error NO LIBPCI HEADER #endif -int main(void) { struct pci_access a; pci_init(&a); return 0; } +int main(void) { return 0; } EOF - if compile_prog "" "-lpci -lz" ; then - libs_softmmu="-lpci -lz $libs_softmmu" + if compile_prog "" "" ; then + kvm_cap_device_assignment=yes else echo - echo "Error: libpci check failed" + echo "Error: libpci header check failed" echo "Disable KVM Device Assignment capability." echo kvm_cap_device_assignment=no @@ -1531,6 +1854,32 @@ fi ########################################## +# test for vhost net + +if test "$vhost_net" != "no"; then + if test "$kvm" != "no"; then + cat > $TMPC < + int main(void) { return 0; } +EOF + if compile_prog "$kvm_cflags" "" ; then + vhost_net=yes + else + if test "$vhost_net" = "yes" ; then + feature_not_found "vhost-net" + fi + vhost_net=no + fi + else + if test "$vhost_net" = "yes" ; then + echo "NOTE: vhost-net feature requires KVM (--enable-kvm)." + feature_not_found "vhost-net" + fi + vhost_net=no + fi +fi + +########################################## # pthread probe PTHREADLIBS_LIST="-lpthread -lpthreadGC2" @@ -1539,13 +1888,17 @@ #include int main(void) { pthread_create(0,0,0,0); return 0; } EOF -for pthread_lib in $PTHREADLIBS_LIST; do - if compile_prog "" "$pthread_lib" ; then - pthread=yes - LIBS="$pthread_lib $LIBS" - break - fi -done +if compile_prog "" "" ; then + pthread=yes +else + for pthread_lib in $PTHREADLIBS_LIST; do + if compile_prog "" "$pthread_lib" ; then + pthread=yes + LIBS="$pthread_lib $LIBS" + break + fi + done +fi if test "$mingw32" != yes -a "$pthread" = no; then echo @@ -1556,17 +1909,61 @@ fi ########################################## +# rbd probe +if test "$rbd" != "no" ; then + cat > $TMPC < +#include +int main(void) { rados_initialize(0, NULL); return 0; } +EOF + rbd_libs="-lrados" + if compile_prog "" "$rbd_libs" ; then + librados_too_old=no + cat > $TMPC < +#include +#ifndef CEPH_OSD_TMAP_SET +#error missing CEPH_OSD_TMAP_SET +#endif +int main(void) { + int (*func)(const rados_pool_t pool, uint64_t *snapid) = rados_selfmanaged_snap_create; + rados_initialize(0, NULL); + return 0; +} +EOF + if compile_prog "" "$rbd_libs" ; then + rbd=yes + libs_tools="$rbd_libs $libs_tools" + libs_softmmu="$rbd_libs $libs_softmmu" + else + rbd=no + librados_too_old=yes + fi + else + if test "$rbd" = "yes" ; then + feature_not_found "rados block device" + fi + rbd=no + fi + if test "$librados_too_old" = "yes" ; then + echo "-> Your librados version is too old - upgrade needed to have rbd support" + fi +fi + +########################################## # linux-aio probe if test "$linux_aio" != "no" ; then cat > $TMPC < #include +#include int main(void) { io_setup(0, NULL); io_set_eventfd(NULL, 0); eventfd(0, 0); return 0; } EOF if compile_prog "" "-laio" ; then linux_aio=yes - LIBS="$LIBS -laio" + libs_softmmu="$libs_softmmu -laio" + libs_tools="$libs_tools -laio" else if test "$linux_aio" = "yes" ; then feature_not_found "linux AIO" @@ -1576,6 +1973,27 @@ fi ########################################## +# attr probe + +if test "$attr" != "no" ; then + cat > $TMPC < +#include +#include +int main(void) { getxattr(NULL, NULL, NULL, 0); setxattr(NULL, NULL, NULL, 0, 0); return 0; } +EOF + if compile_prog "" "-lattr" ; then + attr=yes + LIBS="-lattr $LIBS" + else + if test "$attr" = "yes" ; then + feature_not_found "ATTR" + fi + attr=no + fi +fi + +########################################## # iovec probe cat > $TMPC < @@ -1662,11 +2080,25 @@ inotify=yes fi +inotify1=no +cat > $TMPC << EOF +#include + +int +main(void) +{ + /* try to start inotify */ + return inotify_init1(0); +} +EOF +if compile_prog "" "" ; then + inotify1=yes +fi + # check if utimensat and futimens are supported utimens=no cat > $TMPC << EOF #define _ATFILE_SOURCE -#define _GNU_SOURCE #include #include @@ -1684,7 +2116,6 @@ # check if pipe2 is there pipe2=no cat > $TMPC << EOF -#define _GNU_SOURCE #include #include @@ -1702,7 +2133,6 @@ # check if accept4 is there accept4=no cat > $TMPC << EOF -#define _GNU_SOURCE #include #include @@ -1719,7 +2149,6 @@ # check if tee/splice is there. vmsplice was added same time. splice=no cat > $TMPC << EOF -#define _GNU_SOURCE #include #include #include @@ -1747,7 +2176,7 @@ int main(void) { return syscall(SYS_signalfd, -1, NULL, _NSIG / 8); } EOF -if $cc $ARCH_CFLAGS -o $TMPE $TMPC 2> /dev/null ; then +if compile_prog "" "" ; then signalfd=yes fi @@ -1781,6 +2210,38 @@ fallocate=yes fi +# check for sync_file_range +sync_file_range=no +cat > $TMPC << EOF +#include + +int main(void) +{ + sync_file_range(0, 0, 0, 0); + return 0; +} +EOF +if compile_prog "$ARCH_CFLAGS" "" ; then + sync_file_range=yes +fi + +# check for linux/fiemap.h and FS_IOC_FIEMAP +fiemap=no +cat > $TMPC << EOF +#include +#include +#include + +int main(void) +{ + ioctl(0, FS_IOC_FIEMAP, 0); + return 0; +} +EOF +if compile_prog "$ARCH_CFLAGS" "" ; then + fiemap=yes +fi + # check for dup3 dup3=no cat > $TMPC << EOF @@ -1798,8 +2259,7 @@ # Check if tools are available to build documentation. if test "$docs" != "no" ; then - if test -x "`which texi2html 2>/dev/null`" -a \ - -x "`which pod2man 2>/dev/null`" ; then + if has makeinfo && has pod2man; then docs=yes else if test "$docs" = "yes" ; then @@ -1845,39 +2305,8 @@ LIBS="-lrt $LIBS" fi -# Determine what linker flags to use to force archive inclusion -check_linker_flags() -{ - w2= - if test "$2" ; then - w2=-Wl,$2 - fi - compile_prog "" "-Wl,$1 ${w2}" -} - -cat > $TMPC << EOF -int main(void) { } -EOF -if check_linker_flags --whole-archive --no-whole-archive ; then - # GNU ld - arlibs_begin="-Wl,--whole-archive" - arlibs_end="-Wl,--no-whole-archive" -elif check_linker_flags -z,allextract -z,defaultextract ; then - # Solaris ld - arlibs_begin="-Wl,-z,allextract" - arlibs_end="-Wl,-z,defaultextract" -elif check_linker_flags -all_load ; then - # Mac OS X - arlibs_begin="-all_load" - arlibs_end="" -else - echo "Error: your linker does not support --whole-archive or -z." - echo "Please report to qemu-devel@nongnu.org" - exit 1 -fi - if test "$darwin" != "yes" -a "$mingw32" != "yes" -a "$solaris" != yes -a \ - "$aix" != "yes" ; then + "$aix" != "yes" -a "$haiku" != "yes" ; then libs_softmmu="-lutil $libs_softmmu" fi @@ -1910,6 +2339,29 @@ gcc_attribute_warn_unused_result=yes fi +# spice probe +if test "$spice" != "no" ; then + cat > $TMPC << EOF +#include +int main(void) { spice_server_new(); return 0; } +EOF + spice_cflags=$($pkg_config --cflags spice-protocol spice-server 2>/dev/null) + spice_libs=$($pkg_config --libs spice-protocol spice-server 2>/dev/null) + if $pkg_config --atleast-version=0.5.3 spice-server >/dev/null 2>&1 && \ + compile_prog "$spice_cflags" "$spice_libs" ; then + spice="yes" + libs_softmmu="$libs_softmmu $spice_libs" + QEMU_CFLAGS="$QEMU_CFLAGS $spice_cflags" + else + if test "$spice" = "yes" ; then + feature_not_found "spice" + fi + spice="no" + fi +fi + +########################################## + ########################################## # check if we have fdatasync @@ -1922,6 +2374,79 @@ fdatasync=yes fi +########################################## +# check if we have madvise + +madvise=no +cat > $TMPC << EOF +#include +#include +#include +int main(void) { return madvise(NULL, 0, MADV_DONTNEED); } +EOF +if compile_prog "" "" ; then + madvise=yes +fi + +########################################## +# check if we have posix_madvise + +posix_madvise=no +cat > $TMPC << EOF +#include +#include +int main(void) { return posix_madvise(NULL, 0, POSIX_MADV_DONTNEED); } +EOF +if compile_prog "" "" ; then + posix_madvise=yes +fi + +########################################## +# check if trace backend exists + +sh "$source_path/scripts/tracetool" "--$trace_backend" --check-backend > /dev/null 2> /dev/null +if test "$?" -ne 0 ; then + echo + echo "Error: invalid trace backend" + echo "Please choose a supported trace backend." + echo + exit 1 +fi + +########################################## +# For 'ust' backend, test if ust headers are present +if test "$trace_backend" = "ust"; then + cat > $TMPC << EOF +#include +#include +int main(void) { return 0; } +EOF + if compile_prog "" "" ; then + LIBS="-lust $LIBS" + else + echo + echo "Error: Trace backend 'ust' missing libust header files" + echo + exit 1 + fi +fi + +########################################## +# For 'dtrace' backend, test if 'dtrace' command is present +if test "$trace_backend" = "dtrace"; then + if ! has 'dtrace' ; then + echo + echo "Error: dtrace command is not found in PATH $PATH" + echo + exit 1 + fi + trace_backend_stap="no" + if has 'stap' ; then + trace_backend_stap="yes" + fi +fi + +########################################## # End of CC checks # After here, no more $cc or $ld runs @@ -1962,43 +2487,44 @@ fi fi +# Use ASLR, no-SEH and DEP if available if test "$mingw32" = "yes" ; then - if test -z "$prefix" ; then - prefix="c:/Program Files/Qemu" - fi - mansuffix="" - datasuffix="" - docsuffix="" - binsuffix="" -else - if test -z "$prefix" ; then - prefix="/usr/local" + for flag in --dynamicbase --no-seh --nxcompat; do + if $ld --help 2>/dev/null | grep ".$flag" >/dev/null 2>/dev/null ; then + LDFLAGS="-Wl,$flag $LDFLAGS" + fi + done +fi + +confdir=$sysconfdir$confsuffix + +tools= +if test "$softmmu" = yes ; then + tools="qemu-img\$(EXESUF) qemu-io\$(EXESUF) $tools" + if [ "$linux" = "yes" -o "$bsd" = "yes" -o "$solaris" = "yes" ] ; then + tools="qemu-nbd\$(EXESUF) $tools" + if [ "$check_utests" = "yes" ]; then + tools="check-qint check-qstring check-qdict check-qlist $tools" + tools="check-qfloat check-qjson $tools" + fi fi - mansuffix="/share/man" - datasuffix="/share/qemu" - docsuffix="/share/doc/qemu" - binsuffix="/bin" -fi - -if test -f kvm/kernel/configure; then - kvm_kmod="yes" - kmod_args="" - if test -n "$kerneldir"; then - kmod_args="--kerneldir=$kerneldir" - fi - if test "$kvm_trace" = "yes"; then - kmod_args="$kmod_args --with-kvm-trace" - fi - # hope there are no spaces in kmod_args; can't use arrays because of - # dash. - (cd kvm/kernel; ./configure $kmod_args) fi +# Mac OS X ships with a broken assembler +roms= +if test \( "$cpu" = "i386" -o "$cpu" = "x86_64" \) -a \ + "$targetos" != "Darwin" -a "$targetos" != "SunOS" -a \ + "$softmmu" = yes ; then + roms="optionrom" +fi + + echo "Install prefix $prefix" -echo "BIOS directory $prefix$datasuffix" -echo "binary directory $prefix$binsuffix" +echo "BIOS directory `eval echo $datadir`" +echo "binary directory `eval echo $bindir`" +echo "config directory `eval echo $sysconfdir`" if test "$mingw32" = "no" ; then -echo "Manual directory $prefix$mansuffix" +echo "Manual directory `eval echo $mandir`" echo "ELF interp prefix $interp_prefix" fi echo "Source path $source_path" @@ -2013,6 +2539,7 @@ echo "host big endian $bigendian" echo "target list $target_list" echo "tcg debug enabled $debug_tcg" +echo "Mon debug enabled $debug_mon" echo "gprof enabled $gprof" echo "sparse enabled $sparse" echo "strip binaries $strip_opt" @@ -2033,6 +2560,9 @@ echo "Mixer emulation $mixemu" echo "VNC TLS support $vnc_tls" echo "VNC SASL support $vnc_sasl" +echo "VNC JPEG support $vnc_jpeg" +echo "VNC PNG support $vnc_png" +echo "VNC thread $vnc_thread" if test -n "$sparc_cpu"; then echo "Target Sparc Arch $sparc_cpu" fi @@ -2049,15 +2579,23 @@ echo "vde support $vde" echo "IO thread $io_thread" echo "Linux AIO support $linux_aio" +echo "ATTR/XATTR support $attr" echo "Install blobs $blobs" echo "KVM support $kvm" echo "KVM PIT support $kvm_cap_pit" echo "KVM device assig. $kvm_cap_device_assignment" -echo "KVM trace support $kvm_trace" echo "fdt support $fdt" echo "preadv support $preadv" echo "fdatasync $fdatasync" +echo "madvise $madvise" +echo "posix_madvise $posix_madvise" echo "uuid support $uuid" +echo "vhost-net support $vhost_net" +echo "Trace backend $trace_backend" +echo "Trace output file $trace_file-" +echo "spice support $spice" +echo "rbd support $rbd" +echo "xfsctl support $xfs" if test $sdl_too_old = "yes"; then echo "-> Your SDL version is too old - please upgrade to have SDL support" @@ -2071,7 +2609,14 @@ printf " '%s'" "$0" "$@" >> $config_host_mak echo >> $config_host_mak -echo "CONFIG_QEMU_SHAREDIR=\"$prefix$datasuffix\"" >> $config_host_mak +echo all: >> $config_host_mak +echo "prefix=$prefix" >> $config_host_mak +echo "bindir=$bindir" >> $config_host_mak +echo "mandir=$mandir" >> $config_host_mak +echo "datadir=$datadir" >> $config_host_mak +echo "sysconfdir=$sysconfdir" >> $config_host_mak +echo "docdir=$docdir" >> $config_host_mak +echo "confdir=$confdir" >> $config_host_mak case "$cpu" in i386|x86_64|alpha|cris|hppa|ia64|m68k|microblaze|mips|mips64|ppc|ppc64|s390|s390x|sparc|sparc64) @@ -2080,20 +2625,19 @@ armv4b|armv4l) ARCH=arm ;; - *) - echo "Unsupported CPU = $cpu" - exit 1 - ;; esac echo "ARCH=$ARCH" >> $config_host_mak if test "$debug_tcg" = "yes" ; then echo "CONFIG_DEBUG_TCG=y" >> $config_host_mak fi +if test "$debug_mon" = "yes" ; then + echo "CONFIG_DEBUG_MONITOR=y" >> $config_host_mak +fi if test "$debug" = "yes" ; then echo "CONFIG_DEBUG_EXEC=y" >> $config_host_mak fi if test "$strip_opt" = "yes" ; then - echo "STRIP_OPT=-s" >> $config_host_mak + echo "STRIP=${strip}" >> $config_host_mak fi if test "$bigendian" = "yes" ; then echo "HOST_WORDS_BIGENDIAN=y" >> $config_host_mak @@ -2101,6 +2645,15 @@ echo "HOST_LONG_BITS=$hostlongbits" >> $config_host_mak if test "$mingw32" = "yes" ; then echo "CONFIG_WIN32=y" >> $config_host_mak + rc_version=`cat $source_path/VERSION` + version_major=${rc_version%%.*} + rc_version=${rc_version#*.} + version_minor=${rc_version%%.*} + rc_version=${rc_version#*.} + version_subminor=${rc_version%%.*} + version_micro=0 + echo "CONFIG_FILEVERSION=$version_major,$version_minor,$version_subminor,$version_micro" >> $config_host_mak + echo "CONFIG_PRODUCTVERSION=$version_major,$version_minor,$version_subminor,$version_micro" >> $config_host_mak else echo "CONFIG_POSIX=y" >> $config_host_mak fi @@ -2124,16 +2677,18 @@ echo "CONFIG_NEEDS_LIBSUNMATH=y" >> $config_host_mak fi fi +if test "$haiku" = "yes" ; then + echo "CONFIG_HAIKU=y" >> $config_host_mak +fi if test "$static" = "yes" ; then echo "CONFIG_STATIC=y" >> $config_host_mak - LDFLAGS="-static $LDFLAGS" fi if test $profiler = "yes" ; then echo "CONFIG_PROFILER=y" >> $config_host_mak fi if test "$slirp" = "yes" ; then echo "CONFIG_SLIRP=y" >> $config_host_mak - QEMU_CFLAGS="-I\$(SRC_PATH)/slirp $QEMU_CFLAGS" + QEMU_INCLUDES="-I\$(SRC_PATH)/slirp $QEMU_INCLUDES" fi if test "$vde" = "yes" ; then echo "CONFIG_VDE=y" >> $config_host_mak @@ -2168,19 +2723,31 @@ echo "CONFIG_VNC_SASL=y" >> $config_host_mak echo "VNC_SASL_CFLAGS=$vnc_sasl_cflags" >> $config_host_mak fi +if test "$vnc_jpeg" != "no" ; then + echo "CONFIG_VNC_JPEG=y" >> $config_host_mak + echo "VNC_JPEG_CFLAGS=$vnc_jpeg_cflags" >> $config_host_mak +fi +if test "$vnc_png" != "no" ; then + echo "CONFIG_VNC_PNG=y" >> $config_host_mak + echo "VNC_PNG_CFLAGS=$vnc_png_cflags" >> $config_host_mak +fi +if test "$vnc_thread" != "no" ; then + echo "CONFIG_VNC_THREAD=y" >> $config_host_mak + echo "CONFIG_THREAD=y" >> $config_host_mak +fi if test "$fnmatch" = "yes" ; then echo "CONFIG_FNMATCH=y" >> $config_host_mak fi if test "$uuid" = "yes" ; then echo "CONFIG_UUID=y" >> $config_host_mak fi +if test "$xfs" = "yes" ; then + echo "CONFIG_XFS=y" >> $config_host_mak +fi qemu_version=`head $source_path/VERSION` echo "VERSION=$qemu_version" >>$config_host_mak echo "PKGVERSION=$pkgversion" >>$config_host_mak echo "SRC_PATH=$source_path" >> $config_host_mak -if [ "$source_path_used" = "yes" ]; then - echo "VPATH=$source_path" >> $config_host_mak -fi echo "TARGET_DIRS=$target_list" >> $config_host_mak if [ "$docs" = "yes" ] ; then echo "BUILD_DOCS=yes" >> $config_host_mak @@ -2216,12 +2783,21 @@ if test "$fallocate" = "yes" ; then echo "CONFIG_FALLOCATE=y" >> $config_host_mak fi +if test "$sync_file_range" = "yes" ; then + echo "CONFIG_SYNC_FILE_RANGE=y" >> $config_host_mak +fi +if test "$fiemap" = "yes" ; then + echo "CONFIG_FIEMAP=y" >> $config_host_mak +fi if test "$dup3" = "yes" ; then echo "CONFIG_DUP3=y" >> $config_host_mak fi if test "$inotify" = "yes" ; then echo "CONFIG_INOTIFY=y" >> $config_host_mak fi +if test "$inotify1" = "yes" ; then + echo "CONFIG_INOTIFY1=y" >> $config_host_mak +fi if test "$byteswap_h" = "yes" ; then echo "CONFIG_BYTESWAP_H=y" >> $config_host_mak fi @@ -2244,10 +2820,19 @@ fi if test "$io_thread" = "yes" ; then echo "CONFIG_IOTHREAD=y" >> $config_host_mak + echo "CONFIG_THREAD=y" >> $config_host_mak fi if test "$linux_aio" = "yes" ; then echo "CONFIG_LINUX_AIO=y" >> $config_host_mak fi +if test "$attr" = "yes" ; then + echo "CONFIG_ATTR=y" >> $config_host_mak +fi +if test "$linux" = "yes" ; then + if test "$attr" = "yes" ; then + echo "CONFIG_VIRTFS=y" >> $config_host_mak + fi +fi if test "$blobs" = "yes" ; then echo "INSTALL_BLOBS=yes" >> $config_host_mak fi @@ -2277,6 +2862,16 @@ else echo "CONFIG_NO_CPU_EMULATION=y" >> $config_host_mak fi +if test "$madvise" = "yes" ; then + echo "CONFIG_MADVISE=y" >> $config_host_mak +fi +if test "$posix_madvise" = "yes" ; then + echo "CONFIG_POSIX_MADVISE=y" >> $config_host_mak +fi + +if test "$spice" = "yes" ; then + echo "CONFIG_SPICE=y" >> $config_host_mak +fi # XXX: suppress that if [ "$bsd" = "yes" ] ; then @@ -2288,6 +2883,9 @@ if test "$zero_malloc" = "yes" ; then echo "CONFIG_ZERO_MALLOC=y" >> $config_host_mak fi +if test "$rbd" = "yes" ; then + echo "CONFIG_RBD=y" >> $config_host_mak +fi # USB host support case "$usb" in @@ -2302,52 +2900,41 @@ ;; esac -echo "KVM_KMOD=$kvm_kmod" >> $config_host_mak - -tools= -if test `expr "$target_list" : ".*softmmu.*"` != 0 ; then - tools="qemu-img\$(EXESUF) qemu-io\$(EXESUF) $tools" - if [ "$linux" = "yes" ] ; then - tools="qemu-nbd\$(EXESUF) $tools" - if [ "$check_utests" = "yes" ]; then - tools="check-qint check-qstring check-qdict check-qlist $tools" - tools="check-qfloat check-qjson $tools" - fi - fi +echo "TRACE_BACKEND=$trace_backend" >> $config_host_mak +if test "$trace_backend" = "simple"; then + echo "CONFIG_SIMPLE_TRACE=y" >> $config_host_mak +fi +# Set the appropriate trace file. +if test "$trace_backend" = "simple"; then + trace_file="\"$trace_file-%u\"" fi -echo "TOOLS=$tools" >> $config_host_mak - -# Mac OS X ships with a broken assembler -roms= -if test \( "$cpu" = "i386" -o "$cpu" = "x86_64" \) -a \ - "$targetos" != "Darwin" -a "$targetos" != "SunOS" -a \ - `expr "$target_list" : ".*softmmu.*"` != 0 ; then - roms="optionrom" +if test "$trace_backend" = "dtrace" -a "$trace_backend_stap" = "yes" ; then + echo "CONFIG_SYSTEMTAP_TRACE=y" >> $config_host_mak fi -echo "ROMS=$roms" >> $config_host_mak +echo "CONFIG_TRACE_FILE=$trace_file" >> $config_host_mak -echo "prefix=$prefix" >> $config_host_mak -echo "bindir=\${prefix}$binsuffix" >> $config_host_mak -echo "mandir=\${prefix}$mansuffix" >> $config_host_mak -echo "datadir=\${prefix}$datasuffix" >> $config_host_mak -echo "docdir=\${prefix}$docsuffix" >> $config_host_mak +echo "TOOLS=$tools" >> $config_host_mak +echo "ROMS=$roms" >> $config_host_mak echo "MAKE=$make" >> $config_host_mak echo "INSTALL=$install" >> $config_host_mak echo "INSTALL_DIR=$install -d -m0755 -p" >> $config_host_mak echo "INSTALL_DATA=$install -m0644 -p" >> $config_host_mak echo "INSTALL_PROG=$install -m0755 -p" >> $config_host_mak echo "CC=$cc" >> $config_host_mak +echo "CC_I386=$cc_i386" >> $config_host_mak echo "HOST_CC=$host_cc" >> $config_host_mak -if test "$sparse" = "yes" ; then - echo "CC := REAL_CC=\"\$(CC)\" cgcc" >> $config_host_mak - echo "HOST_CC := REAL_CC=\"\$(HOST_CC)\" cgcc" >> $config_host_mak - echo "QEMU_CFLAGS += -Wbitwise -Wno-transparent-union -Wno-old-initializer -Wno-non-pointer-null" >> $config_host_mak -fi echo "AR=$ar" >> $config_host_mak echo "OBJCOPY=$objcopy" >> $config_host_mak echo "LD=$ld" >> $config_host_mak +echo "WINDRES=$windres" >> $config_host_mak echo "CFLAGS=$CFLAGS" >> $config_host_mak echo "QEMU_CFLAGS=$QEMU_CFLAGS" >> $config_host_mak +echo "QEMU_INCLUDES=$QEMU_INCLUDES" >> $config_host_mak +if test "$sparse" = "yes" ; then + echo "CC := REAL_CC=\"\$(CC)\" cgcc" >> $config_host_mak + echo "HOST_CC := REAL_CC=\"\$(HOST_CC)\" cgcc" >> $config_host_mak + echo "QEMU_CFLAGS += -Wbitwise -Wno-transparent-union -Wno-old-initializer -Wno-non-pointer-null" >> $config_host_mak +fi echo "HELPER_CFLAGS=$helper_cflags" >> $config_host_mak echo "LDFLAGS=$LDFLAGS" >> $config_host_mak echo "ARLIBS_BEGIN=$arlibs_begin" >> $config_host_mak @@ -2368,6 +2955,15 @@ fi fi +for d in libdis libdis-user; do + mkdir -p $d + symlink $source_path/Makefile.dis $d/Makefile + echo > $d/config.mak +done +if test "$static" = "no" -a "$user_pie" = "yes" ; then + echo "QEMU_CFLAGS+=-fpie" > libdis-user/config.mak +fi + for target in $target_list; do target_dir="$target" config_target_mak=$target_dir/config-target.mak @@ -2425,21 +3021,15 @@ if test "$target" = "arm-linux-user" -o "$target" = "armeb-linux-user" -o "$target" = "arm-bsd-user" -o "$target" = "armeb-bsd-user" ; then mkdir -p $target_dir/nwfpe fi - -# -# don't use ln -sf as not all "ln -sf" over write the file/link -# -rm -f $target_dir/Makefile -ln -s $source_path/Makefile.target $target_dir/Makefile +symlink $source_path/Makefile.target $target_dir/Makefile echo "# Automatically generated by configure - do not modify" > $config_target_mak bflt="no" -elfload32="no" target_nptl="no" interp_prefix1=`echo "$interp_prefix" | sed "s/%M/$target_arch2/g"` -echo "CONFIG_QEMU_PREFIX=\"$interp_prefix1\"" >> $config_target_mak +echo "CONFIG_QEMU_INTERP_PREFIX=\"$interp_prefix1\"" >> $config_target_mak gdb_xml_files="" TARGET_ARCH="$target_arch2" @@ -2459,6 +3049,7 @@ ;; alpha) target_phys_bits=64 + target_nptl="yes" ;; arm|armeb) TARGET_ARCH=arm @@ -2536,7 +3127,6 @@ ;; sparc64) TARGET_BASE_ARCH=sparc - elfload32="yes" target_phys_bits=64 ;; sparc32plus) @@ -2567,9 +3157,6 @@ TARGET_ABI_DIR=$TARGET_ARCH fi echo "TARGET_ABI_DIR=$TARGET_ABI_DIR" >> $config_target_mak -if [ $target_phys_bits -lt $hostlongbits ] ; then - target_phys_bits=$hostlongbits -fi case "$target_arch2" in i386|x86_64) if test "$xen" = "yes" -a "$target_softmmu" = "yes" ; then @@ -2587,22 +3174,28 @@ \( "$target_arch2" = "i386" -a "$cpu" = "x86_64" \) \) ; then echo "CONFIG_KVM=y" >> $config_target_mak echo "KVM_CFLAGS=$kvm_cflags" >> $config_target_mak + if test "$kvm_para" = "yes"; then + echo "CONFIG_KVM_PARA=y" >> $config_target_mak + fi if test $kvm_cap_pit = "yes" ; then echo "CONFIG_KVM_PIT=y" >> $config_target_mak fi if test $kvm_cap_device_assignment = "yes" ; then echo "CONFIG_KVM_DEVICE_ASSIGNMENT=y" >> $config_target_mak fi + if test $vhost_net = "yes" ; then + echo "CONFIG_VHOST_NET=y" >> $config_target_mak + fi fi esac -echo "TARGET_PHYS_ADDR_BITS=$target_phys_bits" >> $config_target_mak if test "$target_bigendian" = "yes" ; then echo "TARGET_WORDS_BIGENDIAN=y" >> $config_target_mak fi if test "$target_softmmu" = "yes" ; then + echo "TARGET_PHYS_ADDR_BITS=$target_phys_bits" >> $config_target_mak echo "CONFIG_SOFTMMU=y" >> $config_target_mak echo "LIBS+=$libs_softmmu" >> $config_target_mak - echo "HWLIB=../libhw$target_phys_bits/libqemuhw$target_phys_bits.a" >> $config_target_mak + echo "HWDIR=../libhw$target_phys_bits" >> $config_target_mak echo "subdir-$target: subdir-libhw$target_phys_bits" >> $config_host_mak fi if test "$target_user_only" = "yes" ; then @@ -2623,11 +3216,11 @@ fi case "$target_arch2" in - arm|armeb|m68k|microblaze|mips|mipsel|mipsn32|mipsn32el|mips64|mips64el|ppc|ppc64|ppc64abi32|ppcemb|s390x|sparc|sparc64|sparc32plus) - echo "CONFIG_SOFTFLOAT=y" >> $config_target_mak + i386|x86_64) + echo "CONFIG_NOSOFTFLOAT=y" >> $config_target_mak ;; *) - echo "CONFIG_NOSOFTFLOAT=y" >> $config_target_mak + echo "CONFIG_SOFTFLOAT=y" >> $config_target_mak ;; esac @@ -2638,10 +3231,6 @@ -a "$nptl" = "yes" -a "$target_nptl" = "yes"; then echo "CONFIG_USE_NPTL=y" >> $config_target_mak fi -# 32 bit ELF loader in addition to native 64 bit loader? -if test "$target_user_only" = "yes" -a "$elfload32" = "yes"; then - echo "TARGET_HAS_ELFLOAD32=y" >> $config_target_mak -fi if test "$target_user_only" = "yes" -a "$guest_base" = "yes"; then echo "CONFIG_USE_GUEST_BASE=y" >> $config_target_mak fi @@ -2652,55 +3241,80 @@ # generate QEMU_CFLAGS/LDFLAGS for targets cflags="" +includes="" ldflags="" if test "$ARCH" = "sparc64" ; then - cflags="-I\$(SRC_PATH)/tcg/sparc $cflags" + includes="-I\$(SRC_PATH)/tcg/sparc $includes" elif test "$ARCH" = "s390x" ; then - cflags="-I\$(SRC_PATH)/tcg/s390 $cflags" + includes="-I\$(SRC_PATH)/tcg/s390 $includes" +elif test "$ARCH" = "x86_64" ; then + includes="-I\$(SRC_PATH)/tcg/i386 $includes" +else + includes="-I\$(SRC_PATH)/tcg/\$(ARCH) $includes" +fi +includes="-I\$(SRC_PATH)/tcg $includes" +includes="-I\$(SRC_PATH)/fpu $includes" + +if test "$target_user_only" = "yes" ; then + libdis_config_mak=libdis-user/config.mak else - cflags="-I\$(SRC_PATH)/tcg/\$(ARCH) $cflags" + libdis_config_mak=libdis/config.mak fi -cflags="-I\$(SRC_PATH)/tcg $cflags" -cflags="-I\$(SRC_PATH)/fpu $cflags" for i in $ARCH $TARGET_BASE_ARCH ; do case "$i" in alpha) echo "CONFIG_ALPHA_DIS=y" >> $config_target_mak + echo "CONFIG_ALPHA_DIS=y" >> $libdis_config_mak ;; arm) echo "CONFIG_ARM_DIS=y" >> $config_target_mak + echo "CONFIG_ARM_DIS=y" >> $libdis_config_mak ;; cris) echo "CONFIG_CRIS_DIS=y" >> $config_target_mak + echo "CONFIG_CRIS_DIS=y" >> $libdis_config_mak ;; hppa) echo "CONFIG_HPPA_DIS=y" >> $config_target_mak + echo "CONFIG_HPPA_DIS=y" >> $libdis_config_mak ;; i386|x86_64) echo "CONFIG_I386_DIS=y" >> $config_target_mak + echo "CONFIG_I386_DIS=y" >> $libdis_config_mak + ;; + ia64*) + echo "CONFIG_IA64_DIS=y" >> $config_target_mak + echo "CONFIG_IA64_DIS=y" >> $libdis_config_mak ;; m68k) echo "CONFIG_M68K_DIS=y" >> $config_target_mak + echo "CONFIG_M68K_DIS=y" >> $libdis_config_mak ;; microblaze) echo "CONFIG_MICROBLAZE_DIS=y" >> $config_target_mak + echo "CONFIG_MICROBLAZE_DIS=y" >> $libdis_config_mak ;; mips*) echo "CONFIG_MIPS_DIS=y" >> $config_target_mak + echo "CONFIG_MIPS_DIS=y" >> $libdis_config_mak ;; ppc*) echo "CONFIG_PPC_DIS=y" >> $config_target_mak + echo "CONFIG_PPC_DIS=y" >> $libdis_config_mak ;; s390*) echo "CONFIG_S390_DIS=y" >> $config_target_mak + echo "CONFIG_S390_DIS=y" >> $libdis_config_mak ;; sh4) echo "CONFIG_SH4_DIS=y" >> $config_target_mak + echo "CONFIG_SH4_DIS=y" >> $libdis_config_mak ;; sparc*) echo "CONFIG_SPARC_DIS=y" >> $config_target_mak + echo "CONFIG_SPARC_DIS=y" >> $libdis_config_mak ;; esac done @@ -2710,9 +3324,6 @@ # Ensure there's only a single GP cflags="-msmall-data $cflags" ;; -ia64) - cflags="-mno-sdata $cflags" -;; esac if test "$target_softmmu" = "yes" ; then @@ -2757,76 +3368,65 @@ # -static is used to avoid g1/g3 usage by the dynamic linker ldflags="$linker_script -static $ldflags" ;; - ia64) - ldflags="-Wl,-G0 $linker_script -static $ldflags" + alpha | s390x) + # The default placement of the application is fine. ;; - i386|x86_64|ppc|ppc64|s390|sparc64|alpha|arm|m68k|mips|mips64) + *) ldflags="$linker_script $ldflags" ;; esac fi -if test "$target_softmmu" = "yes" ; then - case "$ARCH" in - ia64) - ldflags="-Wl,-G0 $linker_script -static $ldflags" - ;; - esac -fi echo "LDFLAGS+=$ldflags" >> $config_target_mak echo "QEMU_CFLAGS+=$cflags" >> $config_target_mak +echo "QEMU_INCLUDES+=$includes" >> $config_target_mak done # for target in $targets -# build tree in object directory if source path is different from current one -if test "$source_path_used" = "yes" ; then - DIRS="tests tests/cris slirp audio block net pc-bios/optionrom" - DIRS="$DIRS roms/seabios roms/vgabios" - FILES="Makefile tests/Makefile" - FILES="$FILES tests/cris/Makefile tests/cris/.gdbinit" - FILES="$FILES tests/test-mmap.c" - FILES="$FILES pc-bios/optionrom/Makefile pc-bios/keymaps pc-bios/video.x" - FILES="$FILES roms/seabios/Makefile roms/vgabios/Makefile" - for bios_file in $source_path/pc-bios/*.bin $source_path/pc-bios/*.dtb $source_path/pc-bios/openbios-*; do - FILES="$FILES pc-bios/`basename $bios_file`" - done - for dir in $DIRS ; do - mkdir -p $dir - done - # remove the link and recreate it, as not all "ln -sf" overwrite the link - for f in $FILES ; do - rm -f $f - ln -s $source_path/$f $f - done -fi +# build tree in object directory in case the source is not in the current directory +DIRS="tests tests/cris slirp audio block net pc-bios/optionrom" +DIRS="$DIRS roms/seabios roms/vgabios" +DIRS="$DIRS fsdev ui" +FILES="Makefile tests/Makefile" +FILES="$FILES tests/cris/Makefile tests/cris/.gdbinit" +FILES="$FILES pc-bios/optionrom/Makefile pc-bios/keymaps" +FILES="$FILES roms/seabios/Makefile roms/vgabios/Makefile" +for bios_file in $source_path/pc-bios/*.bin $source_path/pc-bios/*.dtb $source_path/pc-bios/openbios-*; do + FILES="$FILES pc-bios/`basename $bios_file`" +done +mkdir -p $DIRS +for f in $FILES ; do + test -e $f || symlink $source_path/$f $f +done # temporary config to build submodules for rom in seabios vgabios; do config_mak=roms/$rom/config.mak - echo "# Automatically generated by configure - do not modify" >> $config_mak + echo "# Automatically generated by configure - do not modify" > $config_mak echo "SRC_PATH=$source_path/roms/$rom" >> $config_mak echo "CC=$cc" >> $config_mak echo "BCC=bcc" >> $config_mak echo "CPP=${cross_prefix}cpp" >> $config_mak echo "OBJCOPY=objcopy" >> $config_mak echo "IASL=iasl" >> $config_mak - echo "HOST_CC=$host_cc" >> $config_mak echo "LD=$ld" >> $config_mak done for hwlib in 32 64; do d=libhw$hwlib mkdir -p $d - rm -f $d/Makefile - ln -s $source_path/Makefile.hw $d/Makefile - echo "HWLIB=libqemuhw$hwlib.a" > $d/config.mak - echo "QEMU_CFLAGS+=-DTARGET_PHYS_ADDR_BITS=$hwlib" >> $d/config.mak + mkdir -p $d/ide + symlink $source_path/Makefile.hw $d/Makefile + echo "QEMU_CFLAGS+=-DTARGET_PHYS_ADDR_BITS=$hwlib" > $d/config.mak done d=libuser mkdir -p $d -rm -f $d/Makefile -ln -s $source_path/Makefile.user $d/Makefile +symlink $source_path/Makefile.user $d/Makefile if test "$static" = "no" -a "$user_pie" = "yes" ; then echo "QEMU_CFLAGS+=-fpie" > $d/config.mak fi + +if test "$docs" = "yes" ; then + mkdir -p QMP +fi diff -Nru qemu-kvm-0.12.5+noroms/console.c qemu-kvm-0.14.1/console.c --- qemu-kvm-0.12.5+noroms/console.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/console.c 2011-05-11 13:29:46.000000000 +0000 @@ -137,6 +137,7 @@ TextAttributes t_attrib; /* currently active text attributes */ TextCell *cells; int text_x[2], text_y[2], cursor_invalidate; + int echo; int update_x0; int update_y0; @@ -154,6 +155,7 @@ QEMUTimer *kbd_timer; }; +static DisplayState *display_state; static TextConsole *active_console; static TextConsole *consoles[MAX_CONSOLES]; static int nb_consoles = 0; @@ -166,7 +168,7 @@ void vga_hw_invalidate(void) { - if (active_console->hw_invalidate) + if (active_console && active_console->hw_invalidate) active_console->hw_invalidate(active_console->hw); } @@ -828,7 +830,6 @@ TextCell *c = &s->cells[y1 * s->width + x]; c->ch = ' '; c->t_attrib = s->t_attrib_default; - c++; update_xy(s, x, y); } @@ -1060,8 +1061,10 @@ if (index >= MAX_CONSOLES) return; - active_console->g_width = ds_get_width(active_console->ds); - active_console->g_height = ds_get_height(active_console->ds); + if (active_console) { + active_console->g_width = ds_get_width(active_console->ds); + active_console->g_height = ds_get_height(active_console->ds); + } s = consoles[index]; if (s) { DisplayState *ds = s->ds; @@ -1175,8 +1178,14 @@ *q++ = '\033'; *q++ = '['; *q++ = keysym & 0xff; + } else if (s->echo && (keysym == '\r' || keysym == '\n')) { + console_puts(s->chr, (const uint8_t *) "\r", 1); + *q++ = '\n'; } else { - *q++ = keysym; + *q++ = keysym; + } + if (s->echo) { + console_puts(s->chr, buf, q - buf); } if (s->chr->chr_read) { qemu_fifo_write(&s->out_fifo, buf, q - buf); @@ -1265,6 +1274,117 @@ return s; } +static DisplaySurface* defaultallocator_create_displaysurface(int width, int height) +{ + DisplaySurface *surface = (DisplaySurface*) qemu_mallocz(sizeof(DisplaySurface)); + + surface->width = width; + surface->height = height; + surface->linesize = width * 4; + surface->pf = qemu_default_pixelformat(32); +#ifdef HOST_WORDS_BIGENDIAN + surface->flags = QEMU_ALLOCATED_FLAG | QEMU_BIG_ENDIAN_FLAG; +#else + surface->flags = QEMU_ALLOCATED_FLAG; +#endif + surface->data = (uint8_t*) qemu_mallocz(surface->linesize * surface->height); + + return surface; +} + +static DisplaySurface* defaultallocator_resize_displaysurface(DisplaySurface *surface, + int width, int height) +{ + surface->width = width; + surface->height = height; + surface->linesize = width * 4; + surface->pf = qemu_default_pixelformat(32); + if (surface->flags & QEMU_ALLOCATED_FLAG) + surface->data = (uint8_t*) qemu_realloc(surface->data, surface->linesize * surface->height); + else + surface->data = (uint8_t*) qemu_malloc(surface->linesize * surface->height); +#ifdef HOST_WORDS_BIGENDIAN + surface->flags = QEMU_ALLOCATED_FLAG | QEMU_BIG_ENDIAN_FLAG; +#else + surface->flags = QEMU_ALLOCATED_FLAG; +#endif + + return surface; +} + +DisplaySurface* qemu_create_displaysurface_from(int width, int height, int bpp, + int linesize, uint8_t *data) +{ + DisplaySurface *surface = (DisplaySurface*) qemu_mallocz(sizeof(DisplaySurface)); + + surface->width = width; + surface->height = height; + surface->linesize = linesize; + surface->pf = qemu_default_pixelformat(bpp); +#ifdef HOST_WORDS_BIGENDIAN + surface->flags = QEMU_BIG_ENDIAN_FLAG; +#endif + surface->data = data; + + return surface; +} + +static void defaultallocator_free_displaysurface(DisplaySurface *surface) +{ + if (surface == NULL) + return; + if (surface->flags & QEMU_ALLOCATED_FLAG) + qemu_free(surface->data); + qemu_free(surface); +} + +static struct DisplayAllocator default_allocator = { + defaultallocator_create_displaysurface, + defaultallocator_resize_displaysurface, + defaultallocator_free_displaysurface +}; + +static void dumb_display_init(void) +{ + DisplayState *ds = qemu_mallocz(sizeof(DisplayState)); + ds->allocator = &default_allocator; + ds->surface = qemu_create_displaysurface(ds, 640, 480); + register_displaystate(ds); +} + +/***********************************************************/ +/* register display */ + +void register_displaystate(DisplayState *ds) +{ + DisplayState **s; + s = &display_state; + while (*s != NULL) + s = &(*s)->next; + ds->next = NULL; + *s = ds; +} + +DisplayState *get_displaystate(void) +{ + if (!display_state) { + dumb_display_init (); + } + return display_state; +} + +DisplayAllocator *register_displayallocator(DisplayState *ds, DisplayAllocator *da) +{ + if(ds->allocator == &default_allocator) { + DisplaySurface *surf; + surf = da->create_displaysurface(ds_get_width(ds), ds_get_height(ds)); + defaultallocator_free_displaysurface(ds->surface); + ds->surface = surf; + ds->allocator = da; + } + return ds->allocator; +} + DisplayState *graphic_console_init(vga_hw_update_ptr update, vga_hw_invalidate_ptr invalidate, vga_hw_screen_dump_ptr screen_dump, @@ -1317,40 +1437,24 @@ static int n_text_consoles; static CharDriverState *text_consoles[128]; -static QemuOpts *text_console_opts[128]; -static void text_console_do_init(CharDriverState *chr, DisplayState *ds, QemuOpts *opts) +static void text_console_set_echo(CharDriverState *chr, bool echo) { - TextConsole *s; - unsigned width; - unsigned height; - static int color_inited; + TextConsole *s = chr->opaque; - width = qemu_opt_get_number(opts, "width", 0); - if (width == 0) - width = qemu_opt_get_number(opts, "cols", 0) * FONT_WIDTH; + s->echo = echo; +} - height = qemu_opt_get_number(opts, "height", 0); - if (height == 0) - height = qemu_opt_get_number(opts, "rows", 0) * FONT_HEIGHT; +static void text_console_do_init(CharDriverState *chr, DisplayState *ds) +{ + TextConsole *s; + static int color_inited; - if (width == 0 || height == 0) { - s = new_console(ds, TEXT_CONSOLE); - width = ds_get_width(s->ds); - height = ds_get_height(s->ds); - } else { - s = new_console(ds, TEXT_CONSOLE_FIXED_SIZE); - } + s = chr->opaque; - if (!s) { - free(chr); - return; - } - chr->opaque = s; chr->chr_write = console_puts; chr->chr_send_event = console_send_event; - s->chr = chr; s->out_fifo.buf = s->out_fifo_buf; s->out_fifo.buf_size = sizeof(s->out_fifo_buf); s->kbd_timer = qemu_new_timer(rt_clock, kbd_send_chars, s); @@ -1365,8 +1469,10 @@ s->total_height = DEFAULT_BACKSCROLL; s->x = 0; s->y = 0; - s->g_width = width; - s->g_height = height; + if (s->console_type == TEXT_CONSOLE) { + s->g_width = ds_get_width(s->ds); + s->g_height = ds_get_height(s->ds); + } s->hw_invalidate = text_console_invalidate; s->hw_text_update = text_console_update; @@ -1402,6 +1508,9 @@ CharDriverState *text_console_init(QemuOpts *opts) { CharDriverState *chr; + TextConsole *s; + unsigned width; + unsigned height; chr = qemu_mallocz(sizeof(CharDriverState)); @@ -1410,9 +1519,32 @@ exit(1); } text_consoles[n_text_consoles] = chr; - text_console_opts[n_text_consoles] = opts; n_text_consoles++; + width = qemu_opt_get_number(opts, "width", 0); + if (width == 0) + width = qemu_opt_get_number(opts, "cols", 0) * FONT_WIDTH; + + height = qemu_opt_get_number(opts, "height", 0); + if (height == 0) + height = qemu_opt_get_number(opts, "rows", 0) * FONT_HEIGHT; + + if (width == 0 || height == 0) { + s = new_console(NULL, TEXT_CONSOLE); + } else { + s = new_console(NULL, TEXT_CONSOLE_FIXED_SIZE); + } + + if (!s) { + free(chr); + return NULL; + } + + s->chr = chr; + s->g_width = width; + s->g_height = height; + chr->opaque = s; + chr->chr_set_echo = text_console_set_echo; return chr; } @@ -1421,9 +1553,7 @@ int i; for (i = 0; i < n_text_consoles; i++) { - text_console_do_init(text_consoles[i], ds, text_console_opts[i]); - qemu_opts_del(text_console_opts[i]); - text_console_opts[i] = NULL; + text_console_do_init(text_consoles[i], ds); } n_text_consoles = 0; @@ -1510,6 +1640,22 @@ pf.depth = bpp == 32 ? 24 : bpp; switch (bpp) { + case 15: + pf.bits_per_pixel = 16; + pf.bytes_per_pixel = 2; + pf.rmask = 0x00007c00; + pf.gmask = 0x000003E0; + pf.bmask = 0x0000001F; + pf.rmax = 31; + pf.gmax = 31; + pf.bmax = 31; + pf.rshift = 10; + pf.gshift = 5; + pf.bshift = 0; + pf.rbits = 5; + pf.gbits = 5; + pf.bbits = 5; + break; case 16: pf.rmask = 0x0000F800; pf.gmask = 0x000007E0; @@ -1559,67 +1705,3 @@ } return pf; } - -DisplaySurface* defaultallocator_create_displaysurface(int width, int height) -{ - DisplaySurface *surface = (DisplaySurface*) qemu_mallocz(sizeof(DisplaySurface)); - - surface->width = width; - surface->height = height; - surface->linesize = width * 4; - surface->pf = qemu_default_pixelformat(32); -#ifdef HOST_WORDS_BIGENDIAN - surface->flags = QEMU_ALLOCATED_FLAG | QEMU_BIG_ENDIAN_FLAG; -#else - surface->flags = QEMU_ALLOCATED_FLAG; -#endif - surface->data = (uint8_t*) qemu_mallocz(surface->linesize * surface->height); - - return surface; -} - -DisplaySurface* defaultallocator_resize_displaysurface(DisplaySurface *surface, - int width, int height) -{ - surface->width = width; - surface->height = height; - surface->linesize = width * 4; - surface->pf = qemu_default_pixelformat(32); - if (surface->flags & QEMU_ALLOCATED_FLAG) - surface->data = (uint8_t*) qemu_realloc(surface->data, surface->linesize * surface->height); - else - surface->data = (uint8_t*) qemu_malloc(surface->linesize * surface->height); -#ifdef HOST_WORDS_BIGENDIAN - surface->flags = QEMU_ALLOCATED_FLAG | QEMU_BIG_ENDIAN_FLAG; -#else - surface->flags = QEMU_ALLOCATED_FLAG; -#endif - - return surface; -} - -DisplaySurface* qemu_create_displaysurface_from(int width, int height, int bpp, - int linesize, uint8_t *data) -{ - DisplaySurface *surface = (DisplaySurface*) qemu_mallocz(sizeof(DisplaySurface)); - - surface->width = width; - surface->height = height; - surface->linesize = linesize; - surface->pf = qemu_default_pixelformat(bpp); -#ifdef HOST_WORDS_BIGENDIAN - surface->flags = QEMU_BIG_ENDIAN_FLAG; -#endif - surface->data = data; - - return surface; -} - -void defaultallocator_free_displaysurface(DisplaySurface *surface) -{ - if (surface == NULL) - return; - if (surface->flags & QEMU_ALLOCATED_FLAG) - qemu_free(surface->data); - qemu_free(surface); -} diff -Nru qemu-kvm-0.12.5+noroms/console.h qemu-kvm-0.14.1/console.h --- qemu-kvm-0.12.5+noroms/console.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/console.h 2011-05-11 13:29:46.000000000 +0000 @@ -3,6 +3,7 @@ #include "qemu-char.h" #include "qdict.h" +#include "notify.h" /* keyboard/mouse support */ @@ -10,10 +11,16 @@ #define MOUSE_EVENT_RBUTTON 0x02 #define MOUSE_EVENT_MBUTTON 0x04 +/* identical to the ps/2 keyboard bits */ +#define QEMU_SCROLL_LOCK_LED (1 << 0) +#define QEMU_NUM_LOCK_LED (1 << 1) +#define QEMU_CAPS_LOCK_LED (1 << 2) + /* in ms */ #define GUI_REFRESH_INTERVAL 30 typedef void QEMUPutKBDEvent(void *opaque, int keycode); +typedef void QEMUPutLEDEvent(void *opaque, int ledstate); typedef void QEMUPutMouseEvent(void *opaque, int dx, int dy, int dz, int buttons_state); typedef struct QEMUPutMouseEntry { @@ -22,19 +29,40 @@ int qemu_put_mouse_event_absolute; char *qemu_put_mouse_event_name; + int index; + /* used internally by qemu for handling mice */ - struct QEMUPutMouseEntry *next; + QTAILQ_ENTRY(QEMUPutMouseEntry) node; } QEMUPutMouseEntry; +typedef struct QEMUPutLEDEntry { + QEMUPutLEDEvent *put_led; + void *opaque; + QTAILQ_ENTRY(QEMUPutLEDEntry) next; +} QEMUPutLEDEntry; + void qemu_add_kbd_event_handler(QEMUPutKBDEvent *func, void *opaque); +void qemu_remove_kbd_event_handler(void); QEMUPutMouseEntry *qemu_add_mouse_event_handler(QEMUPutMouseEvent *func, void *opaque, int absolute, const char *name); void qemu_remove_mouse_event_handler(QEMUPutMouseEntry *entry); +void qemu_activate_mouse_event_handler(QEMUPutMouseEntry *entry); + +QEMUPutLEDEntry *qemu_add_led_event_handler(QEMUPutLEDEvent *func, void *opaque); +void qemu_remove_led_event_handler(QEMUPutLEDEntry *entry); void kbd_put_keycode(int keycode); +void kbd_put_ledstate(int ledstate); void kbd_mouse_event(int dx, int dy, int dz, int buttons_state); + +/* Does the current mouse generate absolute events */ int kbd_mouse_is_absolute(void); +void qemu_add_mouse_mode_change_notifier(Notifier *notify); +void qemu_remove_mouse_mode_change_notifier(Notifier *notify); + +/* Of all the mice, is there one that generates absolute events */ +int kbd_mouse_has_absolute(void); struct MouseTransformInfo { /* Touchscreen resolution */ @@ -99,6 +127,27 @@ struct PixelFormat pf; }; +/* cursor data format is 32bit RGBA */ +typedef struct QEMUCursor { + int width, height; + int hot_x, hot_y; + int refcount; + uint32_t data[]; +} QEMUCursor; + +QEMUCursor *cursor_alloc(int width, int height); +void cursor_get(QEMUCursor *c); +void cursor_put(QEMUCursor *c); +QEMUCursor *cursor_builtin_hidden(void); +QEMUCursor *cursor_builtin_left_ptr(void); +void cursor_print_ascii_art(QEMUCursor *c, const char *prefix); +int cursor_get_mono_bpl(QEMUCursor *c); +void cursor_set_mono(QEMUCursor *c, + uint32_t foreground, uint32_t background, uint8_t *image, + int transparent, uint8_t *mask); +void cursor_get_mono_image(QEMUCursor *c, int foreground, uint8_t *mask); +void cursor_get_mono_mask(QEMUCursor *c, int transparent, uint8_t *mask); + struct DisplayChangeListener { int idle; uint64_t gui_timer_interval; @@ -131,8 +180,7 @@ struct DisplayChangeListener* listeners; void (*mouse_set)(int x, int y, int on); - void (*cursor_define)(int width, int height, int bpp, int hot_x, int hot_y, - uint8_t *image, uint8_t *mask); + void (*cursor_define)(QEMUCursor *cursor); struct DisplayState *next; }; @@ -144,11 +192,7 @@ PixelFormat qemu_different_endianness_pixelformat(int bpp); PixelFormat qemu_default_pixelformat(int bpp); -extern struct DisplayAllocator default_allocator; DisplayAllocator *register_displayallocator(DisplayState *ds, DisplayAllocator *da); -DisplaySurface* defaultallocator_create_displaysurface(int width, int height); -DisplaySurface* defaultallocator_resize_displaysurface(DisplaySurface *surface, int width, int height); -void defaultallocator_free_displaysurface(DisplaySurface *surface); static inline DisplaySurface* qemu_create_displaysurface(DisplayState *ds, int width, int height) { @@ -283,7 +327,9 @@ typedef unsigned long console_ch_t; static inline void console_write_ch(console_ch_t *dest, uint32_t ch) { - cpu_to_le32wu((uint32_t *) dest, ch); + if (!(ch & 0xff)) + ch |= ' '; + *dest = ch; } typedef void (*vga_hw_update_ptr)(void *); @@ -323,6 +369,8 @@ void vnc_display_close(DisplayState *ds); int vnc_display_open(DisplayState *ds, const char *display); int vnc_display_password(DisplayState *ds, const char *password); +int vnc_display_disable_login(DisplayState *ds); +int vnc_display_pw_expire(DisplayState *ds, time_t expires); void do_info_vnc_print(Monitor *mon, const QObject *data); void do_info_vnc(Monitor *mon, QObject **ret_data); char *vnc_display_local_addr(DisplayState *ds); diff -Nru qemu-kvm-0.12.5+noroms/cpu-all.h qemu-kvm-0.14.1/cpu-all.h --- qemu-kvm-0.12.5+noroms/cpu-all.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/cpu-all.h 2011-05-11 13:29:46.000000000 +0000 @@ -627,23 +627,32 @@ #if defined(CONFIG_USE_GUEST_BASE) extern unsigned long guest_base; extern int have_guest_base; +extern unsigned long reserved_va; #define GUEST_BASE guest_base +#define RESERVED_VA reserved_va #else #define GUEST_BASE 0ul +#define RESERVED_VA 0ul #endif /* All direct uses of g2h and h2g need to go away for usermode softmmu. */ #define g2h(x) ((void *)((unsigned long)(x) + GUEST_BASE)) + +#if HOST_LONG_BITS <= TARGET_VIRT_ADDR_SPACE_BITS +#define h2g_valid(x) 1 +#else +#define h2g_valid(x) ({ \ + unsigned long __guest = (unsigned long)(x) - GUEST_BASE; \ + __guest < (1ul << TARGET_VIRT_ADDR_SPACE_BITS); \ +}) +#endif + #define h2g(x) ({ \ unsigned long __ret = (unsigned long)(x) - GUEST_BASE; \ /* Check if given address fits target address space */ \ - assert(__ret == (abi_ulong)__ret); \ + assert(h2g_valid(x)); \ (abi_ulong)__ret; \ }) -#define h2g_valid(x) ({ \ - unsigned long __guest = (unsigned long)(x) - GUEST_BASE; \ - (__guest == (abi_ulong)__guest); \ -}) #define saddr(x) g2h(x) #define laddr(x) g2h(x) @@ -736,32 +745,37 @@ /* original state of the write flag (used when tracking self-modifying code */ #define PAGE_WRITE_ORG 0x0010 +#if defined(CONFIG_BSD) && defined(CONFIG_USER_ONLY) +/* FIXME: Code that sets/uses this is broken and needs to go away. */ #define PAGE_RESERVED 0x0020 +#endif +#if defined(CONFIG_USER_ONLY) void page_dump(FILE *f); -int walk_memory_regions(void *, - int (*fn)(void *, unsigned long, unsigned long, unsigned long)); + +typedef int (*walk_memory_regions_fn)(void *, abi_ulong, + abi_ulong, unsigned long); +int walk_memory_regions(void *, walk_memory_regions_fn); + int page_get_flags(target_ulong address); void page_set_flags(target_ulong start, target_ulong end, int flags); int page_check_range(target_ulong start, target_ulong len, int flags); +#endif -void cpu_exec_init_all(unsigned long tb_size); CPUState *cpu_copy(CPUState *env); CPUState *qemu_get_cpu(int cpu); -void cpu_dump_state(CPUState *env, FILE *f, - int (*cpu_fprintf)(FILE *f, const char *fmt, ...), +#define CPU_DUMP_CODE 0x00010000 + +void cpu_dump_state(CPUState *env, FILE *f, fprintf_function cpu_fprintf, int flags); -void cpu_dump_statistics (CPUState *env, FILE *f, - int (*cpu_fprintf)(FILE *f, const char *fmt, ...), - int flags); +void cpu_dump_statistics(CPUState *env, FILE *f, fprintf_function cpu_fprintf, + int flags); void QEMU_NORETURN cpu_abort(CPUState *env, const char *fmt, ...) - __attribute__ ((__format__ (__printf__, 2, 3))); + GCC_FMT_ATTR(2, 3); extern CPUState *first_cpu; extern CPUState *cpu_single_env; -extern int64_t qemu_icount; -extern int use_icount; #define CPU_INTERRUPT_HARD 0x02 /* hardware interrupt pending */ #define CPU_INTERRUPT_EXITTB 0x04 /* exit the current TB (use for x86 a20 case) */ @@ -810,11 +824,8 @@ void cpu_single_step(CPUState *env, int enabled); void cpu_reset(CPUState *s); - -/* Return the physical page corresponding to a virtual one. Use it - only for debugging because no protection checks are done. Return -1 - if no page found. */ -target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr); +int cpu_is_stopped(CPUState *env); +void run_on_cpu(CPUState *env, void (*func)(void *data), void *data); #define CPU_LOG_TB_OUT_ASM (1 << 0) #define CPU_LOG_TB_IN_ASM (1 << 1) @@ -840,16 +851,37 @@ void cpu_set_log_filename(const char *filename); int cpu_str_to_log_mask(const char *str); -/* IO ports API */ -#include "ioport.h" +#if !defined(CONFIG_USER_ONLY) + +/* Return the physical page corresponding to a virtual one. Use it + only for debugging because no protection checks are done. Return -1 + if no page found. */ +target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr); /* memory API */ extern int phys_ram_fd; -extern uint8_t *phys_ram_dirty; extern ram_addr_t ram_size; -extern ram_addr_t last_ram_offset; -extern uint8_t *bios_mem; + +typedef struct RAMBlock { + uint8_t *host; + ram_addr_t offset; + ram_addr_t length; + char idstr[256]; + QLIST_ENTRY(RAMBlock) next; +#if defined(__linux__) && !defined(TARGET_S390X) + int fd; +#endif +} RAMBlock; + +typedef struct RAMList { + uint8_t *phys_dirty; + QLIST_HEAD(ram, RAMBlock) blocks; +} RAMList; +extern RAMList ram_list; + +extern const char *mem_path; +extern int mem_prealloc; /* physical memory access */ @@ -869,9 +901,6 @@ /* Set if TLB entry is an IO callback. */ #define TLB_MMIO (1 << 5) -int cpu_memory_rw_debug(CPUState *env, target_ulong addr, - uint8_t *buf, int len, int is_write); - #define VGA_DIRTY_FLAG 0x01 #define CODE_DIRTY_FLAG 0x02 #define MIGRATION_DIRTY_FLAG 0x08 @@ -879,199 +908,65 @@ /* read dirty bit (return 0 or 1) */ static inline int cpu_physical_memory_is_dirty(ram_addr_t addr) { - return phys_ram_dirty[addr >> TARGET_PAGE_BITS] == 0xff; -} - -static inline int cpu_physical_memory_get_dirty(ram_addr_t addr, - int dirty_flags) -{ - return phys_ram_dirty[addr >> TARGET_PAGE_BITS] & dirty_flags; -} - -static inline void cpu_physical_memory_set_dirty(ram_addr_t addr) -{ - phys_ram_dirty[addr >> TARGET_PAGE_BITS] = 0xff; -} - -void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t end, - int dirty_flags); -void cpu_tlb_update_dirty(CPUState *env); - -int cpu_physical_memory_set_dirty_tracking(int enable); - -int cpu_physical_memory_get_dirty_tracking(void); - -int cpu_physical_sync_dirty_bitmap(target_phys_addr_t start_addr, - target_phys_addr_t end_addr); - -void dump_exec_info(FILE *f, - int (*cpu_fprintf)(FILE *f, const char *fmt, ...)); - -/* Coalesced MMIO regions are areas where write operations can be reordered. - * This usually implies that write operations are side-effect free. This allows - * batching which can make a major impact on performance when using - * virtualization. - */ -void qemu_register_coalesced_mmio(target_phys_addr_t addr, ram_addr_t size); - -void qemu_unregister_coalesced_mmio(target_phys_addr_t addr, ram_addr_t size); - -/*******************************************/ -/* host CPU ticks (if available) */ - -#if defined(_ARCH_PPC) - -static inline int64_t cpu_get_real_ticks(void) -{ - int64_t retval; -#ifdef _ARCH_PPC64 - /* This reads timebase in one 64bit go and includes Cell workaround from: - http://ozlabs.org/pipermail/linuxppc-dev/2006-October/027052.html - */ - __asm__ __volatile__ ( - "mftb %0\n\t" - "cmpwi %0,0\n\t" - "beq- $-8" - : "=r" (retval)); -#else - /* http://ozlabs.org/pipermail/linuxppc-dev/1999-October/003889.html */ - unsigned long junk; - __asm__ __volatile__ ( - "mftbu %1\n\t" - "mftb %L0\n\t" - "mftbu %0\n\t" - "cmpw %0,%1\n\t" - "bne $-16" - : "=r" (retval), "=r" (junk)); -#endif - return retval; -} - -#elif defined(__i386__) - -static inline int64_t cpu_get_real_ticks(void) -{ - int64_t val; - asm volatile ("rdtsc" : "=A" (val)); - return val; + return ram_list.phys_dirty[addr >> TARGET_PAGE_BITS] == 0xff; } -#elif defined(__x86_64__) - -static inline int64_t cpu_get_real_ticks(void) +static inline int cpu_physical_memory_get_dirty_flags(ram_addr_t addr) { - uint32_t low,high; - int64_t val; - asm volatile("rdtsc" : "=a" (low), "=d" (high)); - val = high; - val <<= 32; - val |= low; - return val; + return ram_list.phys_dirty[addr >> TARGET_PAGE_BITS]; } -#elif defined(__hppa__) - -static inline int64_t cpu_get_real_ticks(void) +static inline int cpu_physical_memory_get_dirty(ram_addr_t addr, + int dirty_flags) { - int val; - asm volatile ("mfctl %%cr16, %0" : "=r"(val)); - return val; + return ram_list.phys_dirty[addr >> TARGET_PAGE_BITS] & dirty_flags; } -#elif defined(__ia64) - -static inline int64_t cpu_get_real_ticks(void) +static inline void cpu_physical_memory_set_dirty(ram_addr_t addr) { - int64_t val; - asm volatile ("mov %0 = ar.itc" : "=r"(val) :: "memory"); - return val; + ram_list.phys_dirty[addr >> TARGET_PAGE_BITS] = 0xff; } -#elif defined(__s390__) - -static inline int64_t cpu_get_real_ticks(void) +static inline int cpu_physical_memory_set_dirty_flags(ram_addr_t addr, + int dirty_flags) { - int64_t val; - asm volatile("stck 0(%1)" : "=m" (val) : "a" (&val) : "cc"); - return val; + return ram_list.phys_dirty[addr >> TARGET_PAGE_BITS] |= dirty_flags; } -#elif defined(__sparc_v8plus__) || defined(__sparc_v8plusa__) || defined(__sparc_v9__) - -static inline int64_t cpu_get_real_ticks (void) +static inline void cpu_physical_memory_mask_dirty_range(ram_addr_t start, + int length, + int dirty_flags) { -#if defined(_LP64) - uint64_t rval; - asm volatile("rd %%tick,%0" : "=r"(rval)); - return rval; -#else - union { - uint64_t i64; - struct { - uint32_t high; - uint32_t low; - } i32; - } rval; - asm volatile("rd %%tick,%1; srlx %1,32,%0" - : "=r"(rval.i32.high), "=r"(rval.i32.low)); - return rval.i64; -#endif -} + int i, mask, len; + uint8_t *p; -#elif defined(__mips__) && \ - ((defined(__mips_isa_rev) && __mips_isa_rev >= 2) || defined(__linux__)) -/* - * binutils wants to use rdhwr only on mips32r2 - * but as linux kernel emulate it, it's fine - * to use it. - * - */ -#define MIPS_RDHWR(rd, value) { \ - __asm__ __volatile__ ( \ - ".set push\n\t" \ - ".set mips32r2\n\t" \ - "rdhwr %0, "rd"\n\t" \ - ".set pop" \ - : "=r" (value)); \ + len = length >> TARGET_PAGE_BITS; + mask = ~dirty_flags; + p = ram_list.phys_dirty + (start >> TARGET_PAGE_BITS); + for (i = 0; i < len; i++) { + p[i] &= mask; + } } -static inline int64_t cpu_get_real_ticks(void) -{ -/* On kernels >= 2.6.25 rdhwr , $2 and $3 are emulated */ - uint32_t count; - static uint32_t cyc_per_count = 0; +void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t end, + int dirty_flags); +void cpu_tlb_update_dirty(CPUState *env); - if (!cyc_per_count) - MIPS_RDHWR("$3", cyc_per_count); +int cpu_physical_memory_set_dirty_tracking(int enable); - MIPS_RDHWR("$2", count); - return (int64_t)(count * cyc_per_count); -} +int cpu_physical_memory_get_dirty_tracking(void); -#else -/* The host CPU doesn't have an easily accessible cycle counter. - Just return a monotonically increasing value. This will be - totally wrong, but hopefully better than nothing. */ -static inline int64_t cpu_get_real_ticks (void) -{ - static int64_t ticks = 0; - return ticks++; -} -#endif +int cpu_physical_sync_dirty_bitmap(target_phys_addr_t start_addr, + target_phys_addr_t end_addr); -/* profiling */ -#ifdef CONFIG_PROFILER -static inline int64_t profile_getclock(void) -{ - return cpu_get_real_ticks(); -} +void dump_exec_info(FILE *f, fprintf_function cpu_fprintf); +#endif /* !CONFIG_USER_ONLY */ -extern int64_t qemu_time, qemu_time_start; -extern int64_t tlb_flush_time; -extern int64_t dev_time; -#endif +int cpu_memory_rw_debug(CPUState *env, target_ulong addr, + uint8_t *buf, int len, int is_write); void cpu_inject_x86_mce(CPUState *cenv, int bank, uint64_t status, - uint64_t mcg_status, uint64_t addr, uint64_t misc); + uint64_t mcg_status, uint64_t addr, uint64_t misc, + int broadcast); #endif /* CPU_ALL_H */ diff -Nru qemu-kvm-0.12.5+noroms/cpu-common.h qemu-kvm-0.14.1/cpu-common.h --- qemu-kvm-0.12.5+noroms/cpu-common.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/cpu-common.h 2011-05-11 13:29:46.000000000 +0000 @@ -3,11 +3,28 @@ /* CPU interfaces that are target indpendent. */ -#if defined(__arm__) || defined(__sparc__) || defined(__mips__) || defined(__hppa__) +#if defined(__arm__) || defined(__sparc__) || defined(__mips__) || defined(__hppa__) || defined(__ia64__) #define WORDS_ALIGNED #endif +#ifdef TARGET_PHYS_ADDR_BITS +#include "targphys.h" +#endif + +#ifndef NEED_CPU_H +#include "poison.h" +#endif + #include "bswap.h" +#include "qemu-queue.h" + +#if !defined(CONFIG_USER_ONLY) + +enum device_endian { + DEVICE_NATIVE_ENDIAN, + DEVICE_BIG_ENDIAN, + DEVICE_LITTLE_ENDIAN, +}; /* address in the RAM (different from a physical address) */ typedef unsigned long ram_addr_t; @@ -29,17 +46,23 @@ } ram_addr_t cpu_get_physical_page_desc(target_phys_addr_t addr); -ram_addr_t qemu_ram_alloc(ram_addr_t); +ram_addr_t qemu_ram_alloc_from_ptr(DeviceState *dev, const char *name, + ram_addr_t size, void *host); +ram_addr_t qemu_ram_alloc(DeviceState *dev, const char *name, ram_addr_t size); +void qemu_ram_unmap(ram_addr_t addr); void qemu_ram_free(ram_addr_t addr); /* This should only be used for ram local to a device. */ void *qemu_get_ram_ptr(ram_addr_t addr); +/* Same but slower, to use for migration, where the order of + * RAMBlocks must not change. */ +void *qemu_safe_ram_ptr(ram_addr_t addr); /* This should not be used by devices. */ -int do_qemu_ram_addr_from_host(void *ptr, ram_addr_t *ram_addr); -ram_addr_t qemu_ram_addr_from_host(void *ptr); +int qemu_ram_addr_from_host(void *ptr, ram_addr_t *ram_addr); +ram_addr_t qemu_ram_addr_from_host_nofail(void *ptr); int cpu_register_io_memory(CPUReadMemoryFunc * const *mem_read, CPUWriteMemoryFunc * const *mem_write, - void *opaque); + void *opaque, enum device_endian endian); void cpu_unregister_io_memory(int table_address); void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf, @@ -62,6 +85,35 @@ void *cpu_register_map_client(void *opaque, void (*callback)(void *opaque)); void cpu_unregister_map_client(void *cookie); +struct CPUPhysMemoryClient; +typedef struct CPUPhysMemoryClient CPUPhysMemoryClient; +struct CPUPhysMemoryClient { + void (*set_memory)(struct CPUPhysMemoryClient *client, + target_phys_addr_t start_addr, + ram_addr_t size, + ram_addr_t phys_offset); + int (*sync_dirty_bitmap)(struct CPUPhysMemoryClient *client, + target_phys_addr_t start_addr, + target_phys_addr_t end_addr); + int (*migration_log)(struct CPUPhysMemoryClient *client, + int enable); + QLIST_ENTRY(CPUPhysMemoryClient) list; +}; + +void cpu_register_phys_memory_client(CPUPhysMemoryClient *); +void cpu_unregister_phys_memory_client(CPUPhysMemoryClient *); + +/* Coalesced MMIO regions are areas where write operations can be reordered. + * This usually implies that write operations are side-effect free. This allows + * batching which can make a major impact on performance when using + * virtualization. + */ +void qemu_register_coalesced_mmio(target_phys_addr_t addr, ram_addr_t size); + +void qemu_unregister_coalesced_mmio(target_phys_addr_t addr, ram_addr_t size); + +void qemu_flush_coalesced_mmio_buffer(void); + uint32_t ldub_phys(target_phys_addr_t addr); uint32_t lduw_phys(target_phys_addr_t addr); uint32_t ldl_phys(target_phys_addr_t addr); @@ -86,6 +138,7 @@ /* Acts like a ROM when read and like a device when written. */ #define IO_MEM_ROMD (1) #define IO_MEM_SUBPAGE (2) -#define IO_MEM_SUBWIDTH (4) + +#endif #endif /* !CPU_COMMON_H */ diff -Nru qemu-kvm-0.12.5+noroms/cpu-defs.h qemu-kvm-0.14.1/cpu-defs.h --- qemu-kvm-0.12.5+noroms/cpu-defs.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/cpu-defs.h 2011-05-11 13:29:46.000000000 +0000 @@ -73,10 +73,11 @@ #define TB_JMP_ADDR_MASK (TB_JMP_PAGE_SIZE - 1) #define TB_JMP_PAGE_MASK (TB_JMP_CACHE_SIZE - TB_JMP_PAGE_SIZE) +#if !defined(CONFIG_USER_ONLY) #define CPU_TLB_BITS 8 #define CPU_TLB_SIZE (1 << CPU_TLB_BITS) -#if TARGET_PHYS_ADDR_BITS == 32 && TARGET_LONG_BITS == 32 +#if HOST_LONG_BITS == 32 && TARGET_LONG_BITS == 32 #define CPU_TLB_ENTRY_BITS 4 #else #define CPU_TLB_ENTRY_BITS 5 @@ -92,21 +93,32 @@ target_ulong addr_read; target_ulong addr_write; target_ulong addr_code; - /* Addend to virtual address to get physical address. IO accesses + /* Addend to virtual address to get host address. IO accesses use the corresponding iotlb value. */ -#if TARGET_PHYS_ADDR_BITS == 64 - /* on i386 Linux make sure it is aligned */ - target_phys_addr_t addend __attribute__((aligned(8))); -#else - target_phys_addr_t addend; -#endif + unsigned long addend; /* padding to get a power of two size */ uint8_t dummy[(1 << CPU_TLB_ENTRY_BITS) - (sizeof(target_ulong) * 3 + - ((-sizeof(target_ulong) * 3) & (sizeof(target_phys_addr_t) - 1)) + - sizeof(target_phys_addr_t))]; + ((-sizeof(target_ulong) * 3) & (sizeof(unsigned long) - 1)) + + sizeof(unsigned long))]; } CPUTLBEntry; +extern int CPUTLBEntry_wrong_size[sizeof(CPUTLBEntry) == (1 << CPU_TLB_ENTRY_BITS) ? 1 : -1]; + +#define CPU_COMMON_TLB \ + /* The meaning of the MMU modes is defined in the target code. */ \ + CPUTLBEntry tlb_table[NB_MMU_MODES][CPU_TLB_SIZE]; \ + target_phys_addr_t iotlb[NB_MMU_MODES][CPU_TLB_SIZE]; \ + target_ulong tlb_flush_addr; \ + target_ulong tlb_flush_mask; + +#else + +#define CPU_COMMON_TLB + +#endif + + #ifdef HOST_WORDS_BIGENDIAN typedef struct icount_decr_u16 { uint16_t high; @@ -121,6 +133,7 @@ struct kvm_run; struct KVMState; +struct qemu_work_item; typedef struct CPUBreakpoint { target_ulong pc; @@ -142,7 +155,6 @@ pthread_t thread; int signalled; struct qemu_work_item *queued_work_first, *queued_work_last; - int regs_modified; }; #define CPU_TEMP_BUF_NLONGS 128 @@ -159,9 +171,7 @@ uint32_t halted; /* Nonzero if the CPU is in suspend state */ \ uint32_t interrupt_request; \ volatile sig_atomic_t exit_request; \ - /* The meaning of the MMU modes is defined in the target code. */ \ - CPUTLBEntry tlb_table[NB_MMU_MODES][CPU_TLB_SIZE]; \ - target_phys_addr_t iotlb[NB_MMU_MODES][CPU_TLB_SIZE]; \ + CPU_COMMON_TLB \ struct TranslationBlock *tb_jmp_cache[TB_JMP_CACHE_SIZE]; \ /* buffer for temporaries in the code generator */ \ long temp_buf[CPU_TEMP_BUF_NLONGS]; \ @@ -202,14 +212,16 @@ void *opaque; \ \ uint32_t created; \ + uint32_t stop; /* Stop request */ \ + uint32_t stopped; /* Artificially stopped */ \ struct QemuThread *thread; \ struct QemuCond *halt_cond; \ + struct qemu_work_item *queued_work_first, *queued_work_last; \ const char *cpu_model_str; \ struct KVMState *kvm_state; \ struct kvm_run *kvm_run; \ int kvm_fd; \ - uint32_t stop; /* Stop request */ \ - uint32_t stopped; /* Artificially stopped */ \ + int kvm_vcpu_dirty; \ struct KVMCPUState kvm_cpu_state; #endif diff -Nru qemu-kvm-0.12.5+noroms/cpu-exec.c qemu-kvm-0.14.1/cpu-exec.c --- qemu-kvm-0.12.5+noroms/cpu-exec.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/cpu-exec.c 2011-05-11 13:29:46.000000000 +0000 @@ -23,6 +23,7 @@ #include "tcg.h" #endif #include "kvm.h" +#include "qemu-barrier.h" #if !defined(CONFIG_SOFTMMU) #undef EAX @@ -60,9 +61,7 @@ void cpu_loop_exit(void) { - /* NOTE: the register at this point must be saved by hand because - longjmp restore them */ - regs_to_env(); + env->current_tb = NULL; longjmp(env->jmp_env, 1); } @@ -87,7 +86,11 @@ if (puc) { /* XXX: use siglongjmp ? */ #ifdef __linux__ +#ifdef __ia64 + sigprocmask(SIG_SETMASK, (sigset_t *)&uc->uc_sigmask, NULL); +#else sigprocmask(SIG_SETMASK, &uc->uc_sigmask, NULL); +#endif #elif defined(__OpenBSD__) sigprocmask(SIG_SETMASK, &uc->sc_mask, NULL); #endif @@ -114,6 +117,7 @@ env->current_tb = tb; /* execute the generated code */ next_tb = tcg_qemu_tb_exec(tb->tc_ptr); + env->current_tb = NULL; if ((next_tb & 3) == 2) { /* Restore PC. This may happen if async event occurs before @@ -130,14 +134,13 @@ { TranslationBlock *tb, **ptb1; unsigned int h; - target_ulong phys_pc, phys_page1, phys_page2, virt_page2; + tb_page_addr_t phys_pc, phys_page1, phys_page2; + target_ulong virt_page2; tb_invalidated_flag = 0; - regs_to_env(); /* XXX: do it just before cpu_gen_code() */ - /* find translated block using physical mappings */ - phys_pc = get_phys_addr_code(env, pc); + phys_pc = get_page_addr_code(env, pc); phys_page1 = phys_pc & TARGET_PAGE_MASK; phys_page2 = -1; h = tb_phys_hash_func(phys_pc); @@ -154,7 +157,7 @@ if (tb->page_addr[1] != -1) { virt_page2 = (pc & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; - phys_page2 = get_phys_addr_code(env, virt_page2); + phys_page2 = get_page_addr_code(env, virt_page2); if (tb->page_addr[1] == phys_page2) goto found; } else { @@ -168,6 +171,12 @@ tb = tb_gen_code(env, pc, cs_base, flags, 0); found: + /* Move the last found TB to the head of the list */ + if (likely(*ptb1)) { + *ptb1 = tb->phys_hash_next; + tb->phys_hash_next = tb_phys_hash[h]; + tb_phys_hash[h] = tb; + } /* we add the TB in the virtual pc hash table */ env->tb_jmp_cache[tb_jmp_cache_hash_func(pc)] = tb; return tb; @@ -215,10 +224,11 @@ /* main execution loop */ +volatile sig_atomic_t exit_request; + int cpu_exec(CPUState *env1) { -#define DECLARE_HOST_REGS 1 -#include "hostregs_helper.h" + volatile host_reg_t saved_env_reg; int ret, interrupt_request; TranslationBlock *tb; uint8_t *tc_ptr; @@ -229,12 +239,18 @@ cpu_single_env = env1; - /* first we save global registers */ -#define SAVE_HOST_REGS 1 -#include "hostregs_helper.h" + /* the access to env below is actually saving the global register's + value, so that files not including target-xyz/exec.h are free to + use it. */ + QEMU_BUILD_BUG_ON (sizeof (saved_env_reg) != sizeof (env)); + saved_env_reg = (host_reg_t) env; + barrier(); env = env1; - env_to_regs(); + if (unlikely(exit_request)) { + env->exit_request = 1; + } + #if defined(TARGET_I386) if (!kvm_enabled()) { /* put eflags in CPU temporary format */ @@ -271,7 +287,6 @@ env = cpu_single_env; #define env cpu_single_env #endif - env->current_tb = NULL; /* if an exception is pending, we execute it here */ if (env->exception_index >= 0) { if (env->exception_index >= EXCP_INTERRUPT) { @@ -327,9 +342,9 @@ #elif defined(TARGET_IA64) do_interrupt(env); #endif + env->exception_index = -1; #endif } - env->exception_index = -1; } if (kvm_enabled()) { @@ -446,11 +461,7 @@ } #elif defined(TARGET_MIPS) if ((interrupt_request & CPU_INTERRUPT_HARD) && - (env->CP0_Status & env->CP0_Cause & CP0Ca_IP_mask) && - (env->CP0_Status & (1 << CP0St_IE)) && - !(env->CP0_Status & (1 << CP0St_EXL)) && - !(env->CP0_Status & (1 << CP0St_ERL)) && - !(env->hflags & MIPS_HFLAG_DM)) { + cpu_mips_hw_interrupts_pending(env)) { /* Raise it */ env->exception_index = EXCP_EXT_INTERRUPT; env->error_code = 0; @@ -458,20 +469,20 @@ next_tb = 0; } #elif defined(TARGET_SPARC) - if ((interrupt_request & CPU_INTERRUPT_HARD) && - cpu_interrupts_enabled(env)) { - int pil = env->interrupt_index & 15; - int type = env->interrupt_index & 0xf0; - - if (((type == TT_EXTINT) && - (pil == 15 || pil > env->psrpil)) || - type != TT_EXTINT) { - env->interrupt_request &= ~CPU_INTERRUPT_HARD; - env->exception_index = env->interrupt_index; - do_interrupt(env); - env->interrupt_index = 0; - next_tb = 0; - } + if (interrupt_request & CPU_INTERRUPT_HARD) { + if (cpu_interrupts_enabled(env) && + env->interrupt_index > 0) { + int pil = env->interrupt_index & 0xf; + int type = env->interrupt_index & 0xf0; + + if (((type == TT_EXTINT) && + cpu_pil_allowed(env, pil)) || + type != TT_EXTINT) { + env->exception_index = env->interrupt_index; + do_interrupt(env); + next_tb = 0; + } + } } else if (interrupt_request & CPU_INTERRUPT_TIMER) { //do_interrupt(0, 0, 0, 0, 0); env->interrupt_request &= ~CPU_INTERRUPT_TIMER; @@ -511,7 +522,8 @@ } #elif defined(TARGET_CRIS) if (interrupt_request & CPU_INTERRUPT_HARD - && (env->pregs[PR_CCS] & I_FLAG)) { + && (env->pregs[PR_CCS] & I_FLAG) + && !env->locked_irq) { env->exception_index = EXCP_IRQ; do_interrupt(env); next_tb = 0; @@ -550,41 +562,24 @@ env->exception_index = EXCP_INTERRUPT; cpu_loop_exit(); } -#ifdef CONFIG_DEBUG_EXEC +#if defined(DEBUG_DISAS) || defined(CONFIG_DEBUG_EXEC) if (qemu_loglevel_mask(CPU_LOG_TB_CPU)) { /* restore flags in standard format */ - regs_to_env(); #if defined(TARGET_I386) env->eflags = env->eflags | helper_cc_compute_all(CC_OP) | (DF & DF_MASK); log_cpu_state(env, X86_DUMP_CCOP); env->eflags &= ~(DF_MASK | CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C); -#elif defined(TARGET_ARM) - log_cpu_state(env, 0); -#elif defined(TARGET_SPARC) - log_cpu_state(env, 0); -#elif defined(TARGET_PPC) - log_cpu_state(env, 0); #elif defined(TARGET_M68K) cpu_m68k_flush_flags(env, env->cc_op); env->cc_op = CC_OP_FLAGS; env->sr = (env->sr & 0xffe0) | env->cc_dest | (env->cc_x << 4); log_cpu_state(env, 0); -#elif defined(TARGET_MICROBLAZE) - log_cpu_state(env, 0); -#elif defined(TARGET_MIPS) - log_cpu_state(env, 0); -#elif defined(TARGET_SH4) - log_cpu_state(env, 0); -#elif defined(TARGET_ALPHA) - log_cpu_state(env, 0); -#elif defined(TARGET_CRIS) - log_cpu_state(env, 0); #else -#error unsupported target CPU + log_cpu_state(env, 0); #endif } -#endif +#endif /* DEBUG_DISAS || CONFIG_DEBUG_EXEC */ spin_lock(&tb_lock); tb = tb_find_fast(); /* Note: we do it here to avoid a gcc bug on Mac OS X when @@ -604,22 +599,18 @@ /* see if we can patch the calling TB. When the TB spans two pages, we cannot safely do a direct jump. */ - { - if (next_tb != 0 && tb->page_addr[1] == -1) { + if (next_tb != 0 && tb->page_addr[1] == -1) { tb_add_jump((TranslationBlock *)(next_tb & ~3), next_tb & 3, tb); } - } spin_unlock(&tb_lock); - env->current_tb = tb; /* cpu_interrupt might be called while translating the TB, but before it is linked into a potentially infinite loop and becomes env->current_tb. Avoid starting execution if there is a pending interrupt. */ - if (unlikely (env->exit_request)) - env->current_tb = NULL; - - while (env->current_tb) { + env->current_tb = tb; + barrier(); + if (likely(!env->exit_request)) { tc_ptr = tb->tc_ptr; /* execute the generated code */ #if defined(__sparc__) && !defined(CONFIG_SOLARIS) @@ -628,7 +619,6 @@ #define env cpu_single_env #endif next_tb = tcg_qemu_tb_exec(tc_ptr); - env->current_tb = NULL; if ((next_tb & 3) == 2) { /* Instruction counter expired. */ int insns_left; @@ -657,11 +647,10 @@ } } } + env->current_tb = NULL; /* reset soft MMU for next block (it can currently only be set by a memory fault) */ } /* for(;;) */ - } else { - env_to_regs(); } } /* for(;;) */ @@ -691,7 +680,8 @@ #endif /* restore global registers */ -#include "hostregs_helper.h" + barrier(); + env = (void *) saved_env_reg; /* fail safe : never use cpu_single_env outside cpu_exec() */ cpu_single_env = NULL; @@ -943,6 +933,20 @@ # define TRAP_sig(context) REG_sig(trap, context) #endif /* linux */ +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) +#include +# define IAR_sig(context) ((context)->uc_mcontext.mc_srr0) +# define MSR_sig(context) ((context)->uc_mcontext.mc_srr1) +# define CTR_sig(context) ((context)->uc_mcontext.mc_ctr) +# define XER_sig(context) ((context)->uc_mcontext.mc_xer) +# define LR_sig(context) ((context)->uc_mcontext.mc_lr) +# define CR_sig(context) ((context)->uc_mcontext.mc_cr) +/* Exception Registers access */ +# define DAR_sig(context) ((context)->uc_mcontext.mc_dar) +# define DSISR_sig(context) ((context)->uc_mcontext.mc_dsisr) +# define TRAP_sig(context) ((context)->uc_mcontext.mc_exc) +#endif /* __FreeBSD__|| __FreeBSD_kernel__ */ + #ifdef __APPLE__ # include typedef struct ucontext SIGCONTEXT; @@ -972,7 +976,11 @@ void *puc) { siginfo_t *info = pinfo; +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) + ucontext_t *uc = puc; +#else struct ucontext *uc = puc; +#endif unsigned long pc; int is_write; @@ -1148,7 +1156,7 @@ } return handle_cpu_signal(ip, (unsigned long)info->si_addr, is_write, - &uc->uc_sigmask, puc); + (sigset_t *)&uc->uc_sigmask, puc); } #elif defined(__s390__) @@ -1159,11 +1167,47 @@ siginfo_t *info = pinfo; struct ucontext *uc = puc; unsigned long pc; - int is_write; + uint16_t *pinsn; + int is_write = 0; pc = uc->uc_mcontext.psw.addr; - /* XXX: compute is_write */ - is_write = 0; + + /* ??? On linux, the non-rt signal handler has 4 (!) arguments instead + of the normal 2 arguments. The 3rd argument contains the "int_code" + from the hardware which does in fact contain the is_write value. + The rt signal handler, as far as I can tell, does not give this value + at all. Not that we could get to it from here even if it were. */ + /* ??? This is not even close to complete, since it ignores all + of the read-modify-write instructions. */ + pinsn = (uint16_t *)pc; + switch (pinsn[0] >> 8) { + case 0x50: /* ST */ + case 0x42: /* STC */ + case 0x40: /* STH */ + is_write = 1; + break; + case 0xc4: /* RIL format insns */ + switch (pinsn[0] & 0xf) { + case 0xf: /* STRL */ + case 0xb: /* STGRL */ + case 0x7: /* STHRL */ + is_write = 1; + } + break; + case 0xe3: /* RXY format insns */ + switch (pinsn[2] & 0xff) { + case 0x50: /* STY */ + case 0x24: /* STG */ + case 0x72: /* STCY */ + case 0x70: /* STHY */ + case 0x8e: /* STPQ */ + case 0x3f: /* STRVH */ + case 0x3e: /* STRV */ + case 0x2f: /* STRVG */ + is_write = 1; + } + break; + } return handle_cpu_signal(pc, (unsigned long)info->si_addr, is_write, &uc->uc_sigmask, puc); } @@ -1191,15 +1235,39 @@ { struct siginfo *info = pinfo; struct ucontext *uc = puc; - unsigned long pc; - int is_write; + unsigned long pc = uc->uc_mcontext.sc_iaoq[0]; + uint32_t insn = *(uint32_t *)pc; + int is_write = 0; + + /* XXX: need kernel patch to get write flag faster. */ + switch (insn >> 26) { + case 0x1a: /* STW */ + case 0x19: /* STH */ + case 0x18: /* STB */ + case 0x1b: /* STWM */ + is_write = 1; + break; + + case 0x09: /* CSTWX, FSTWX, FSTWS */ + case 0x0b: /* CSTDX, FSTDX, FSTDS */ + /* Distinguish from coprocessor load ... */ + is_write = (insn >> 9) & 1; + break; + + case 0x03: + switch ((insn >> 6) & 15) { + case 0xa: /* STWS */ + case 0x9: /* STHS */ + case 0x8: /* STBS */ + case 0xe: /* STWAS */ + case 0xc: /* STBYS */ + is_write = 1; + } + break; + } - pc = uc->uc_mcontext.sc_iaoq[0]; - /* FIXME: compute is_write */ - is_write = 0; return handle_cpu_signal(pc, (unsigned long)info->si_addr, - is_write, - &uc->uc_sigmask, puc); + is_write, &uc->uc_sigmask, puc); } #else diff -Nru qemu-kvm-0.12.5+noroms/cpus.c qemu-kvm-0.14.1/cpus.c --- qemu-kvm-0.12.5+noroms/cpus.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/cpus.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,991 @@ +/* + * 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. + */ + +/* Needed early for CONFIG_BSD etc. */ +#include "config-host.h" + +#include "monitor.h" +#include "sysemu.h" +#include "gdbstub.h" +#include "dma.h" +#include "kvm.h" +#include "exec-all.h" + +#include "cpus.h" +#include "compatfd.h" +#ifdef CONFIG_LINUX +#include +#endif + +#ifdef SIGRTMIN +#define SIG_IPI (SIGRTMIN+4) +#else +#define SIG_IPI SIGUSR1 +#endif + +#ifndef PR_MCE_KILL +#define PR_MCE_KILL 33 +#endif + +static CPUState *next_cpu; + +/***********************************************************/ +void hw_error(const char *fmt, ...) +{ + va_list ap; + CPUState *env; + + va_start(ap, fmt); + fprintf(stderr, "qemu: hardware error: "); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + for(env = first_cpu; env != NULL; env = env->next_cpu) { + fprintf(stderr, "CPU #%d:\n", env->cpu_index); +#ifdef TARGET_I386 + cpu_dump_state(env, stderr, fprintf, X86_DUMP_FPU); +#else + cpu_dump_state(env, stderr, fprintf, 0); +#endif + } + va_end(ap); + abort(); +} + +void cpu_synchronize_all_states(void) +{ + CPUState *cpu; + + for (cpu = first_cpu; cpu; cpu = cpu->next_cpu) { + cpu_synchronize_state(cpu); + } +} + +void cpu_synchronize_all_post_reset(void) +{ + CPUState *cpu; + + for (cpu = first_cpu; cpu; cpu = cpu->next_cpu) { + cpu_synchronize_post_reset(cpu); + } +} + +void cpu_synchronize_all_post_init(void) +{ + CPUState *cpu; + + for (cpu = first_cpu; cpu; cpu = cpu->next_cpu) { + cpu_synchronize_post_init(cpu); + } +} + +int cpu_is_stopped(CPUState *env) +{ + return !vm_running || env->stopped; +} + +static void do_vm_stop(int reason) +{ + if (vm_running) { + cpu_disable_ticks(); + vm_running = 0; + pause_all_vcpus(); + vm_state_notify(0, reason); + qemu_aio_flush(); + bdrv_flush_all(); + monitor_protocol_event(QEVENT_STOP, NULL); + } +} + +static int cpu_can_run(CPUState *env) +{ + if (env->stop) + return 0; + if (env->stopped || !vm_running) + return 0; + return 1; +} + +static int cpu_has_work(CPUState *env) +{ + if (env->stop) + return 1; + if (env->queued_work_first) + return 1; + if (env->stopped || !vm_running) + return 0; + if (!env->halted) + return 1; + if (qemu_cpu_has_work(env)) + return 1; + return 0; +} + +static int any_cpu_has_work(void) +{ + CPUState *env; + + for (env = first_cpu; env != NULL; env = env->next_cpu) + if (cpu_has_work(env)) + return 1; + return 0; +} + +static void cpu_debug_handler(CPUState *env) +{ + gdb_set_stop_cpu(env); + debug_requested = EXCP_DEBUG; + vm_stop(EXCP_DEBUG); +} + +#ifndef _WIN32 +static int io_thread_fd = -1; + +static void qemu_event_increment(void) +{ + /* Write 8 bytes to be compatible with eventfd. */ + static const uint64_t val = 1; + ssize_t ret; + + if (io_thread_fd == -1) + return; + + do { + ret = write(io_thread_fd, &val, sizeof(val)); + } while (ret < 0 && errno == EINTR); + + /* EAGAIN is fine, a read must be pending. */ + if (ret < 0 && errno != EAGAIN) { + fprintf(stderr, "qemu_event_increment: write() filed: %s\n", + strerror(errno)); + exit (1); + } +} + +static void qemu_event_read(void *opaque) +{ + int fd = (unsigned long)opaque; + ssize_t len; + char buffer[512]; + + /* Drain the notify pipe. For eventfd, only 8 bytes will be read. */ + do { + len = read(fd, buffer, sizeof(buffer)); + } while ((len == -1 && errno == EINTR) || len == sizeof(buffer)); +} + +static int qemu_event_init(void) +{ + int err; + int fds[2]; + + err = qemu_eventfd(fds); + if (err == -1) + return -errno; + + err = fcntl_setfl(fds[0], O_NONBLOCK); + if (err < 0) + goto fail; + + err = fcntl_setfl(fds[1], O_NONBLOCK); + if (err < 0) + goto fail; + + qemu_set_fd_handler2(fds[0], NULL, qemu_event_read, NULL, + (void *)(unsigned long)fds[0]); + + io_thread_fd = fds[1]; + return 0; + +fail: + close(fds[0]); + close(fds[1]); + return err; +} +#else +HANDLE qemu_event_handle; + +static void dummy_event_handler(void *opaque) +{ +} + +static int qemu_event_init(void) +{ + qemu_event_handle = CreateEvent(NULL, FALSE, FALSE, NULL); + if (!qemu_event_handle) { + fprintf(stderr, "Failed CreateEvent: %ld\n", GetLastError()); + return -1; + } + qemu_add_wait_object(qemu_event_handle, dummy_event_handler, NULL); + return 0; +} + +static void qemu_event_increment(void) +{ + if (!SetEvent(qemu_event_handle)) { + fprintf(stderr, "qemu_event_increment: SetEvent failed: %ld\n", + GetLastError()); + exit (1); + } +} +#endif + +#ifndef CONFIG_IOTHREAD +int qemu_init_main_loop(void) +{ + cpu_set_debug_excp_handler(cpu_debug_handler); + + return qemu_event_init(); +} + +void qemu_main_loop_start(void) +{ +} + +void qemu_init_vcpu(void *_env) +{ + CPUState *env = _env; + + env->nr_cores = smp_cores; + env->nr_threads = smp_threads; + if (kvm_enabled()) + kvm_init_vcpu(env); + return; +} + +int qemu_cpu_self(void *env) +{ + return 1; +} + +void run_on_cpu(CPUState *env, void (*func)(void *data), void *data) +{ + func(data); +} + +void resume_all_vcpus(void) +{ +} + +void pause_all_vcpus(void) +{ +} + +void qemu_cpu_kick(void *env) +{ + return; +} + +void qemu_notify_event(void) +{ + CPUState *env = cpu_single_env; + + qemu_event_increment (); + if (env) { + cpu_exit(env); + } + if (next_cpu && env != next_cpu) { + cpu_exit(next_cpu); + } +} + +#if defined(OBSOLETE_KVM_IMPL) || !defined(CONFIG_KVM) +void qemu_mutex_lock_iothread(void) {} +void qemu_mutex_unlock_iothread(void) {} +#endif + +void vm_stop(int reason) +{ + do_vm_stop(reason); +} + +#else /* CONFIG_IOTHREAD */ + +#include "qemu-thread.h" + +QemuMutex qemu_global_mutex; +static QemuMutex qemu_fair_mutex; + +static QemuThread io_thread; + +static QemuThread *tcg_cpu_thread; +static QemuCond *tcg_halt_cond; + +static int qemu_system_ready; +/* cpu creation */ +static QemuCond qemu_cpu_cond; +/* system init */ +static QemuCond qemu_system_cond; +static QemuCond qemu_pause_cond; +static QemuCond qemu_work_cond; + +static void tcg_init_ipi(void); +static void kvm_init_ipi(CPUState *env); +static sigset_t block_io_signals(void); + +/* If we have signalfd, we mask out the signals we want to handle and then + * use signalfd to listen for them. We rely on whatever the current signal + * handler is to dispatch the signals when we receive them. + */ +static void sigfd_handler(void *opaque) +{ + int fd = (unsigned long) opaque; + struct qemu_signalfd_siginfo info; + struct sigaction action; + ssize_t len; + + while (1) { + do { + len = read(fd, &info, sizeof(info)); + } while (len == -1 && errno == EINTR); + + if (len == -1 && errno == EAGAIN) { + break; + } + + if (len != sizeof(info)) { + printf("read from sigfd returned %zd: %m\n", len); + return; + } + + sigaction(info.ssi_signo, NULL, &action); + if ((action.sa_flags & SA_SIGINFO) && action.sa_sigaction) { + action.sa_sigaction(info.ssi_signo, + (siginfo_t *)&info, NULL); + } else if (action.sa_handler) { + action.sa_handler(info.ssi_signo); + } + } +} + +static int qemu_signalfd_init(sigset_t mask) +{ + int sigfd; + + sigfd = qemu_signalfd(&mask); + if (sigfd == -1) { + fprintf(stderr, "failed to create signalfd\n"); + return -errno; + } + + fcntl_setfl(sigfd, O_NONBLOCK); + + qemu_set_fd_handler2(sigfd, NULL, sigfd_handler, NULL, + (void *)(unsigned long) sigfd); + + return 0; +} + +int qemu_init_main_loop(void) +{ + int ret; + sigset_t blocked_signals; + + cpu_set_debug_excp_handler(cpu_debug_handler); + + blocked_signals = block_io_signals(); + + ret = qemu_signalfd_init(blocked_signals); + if (ret) + return ret; + + /* Note eventfd must be drained before signalfd handlers run */ + ret = qemu_event_init(); + if (ret) + return ret; + + qemu_cond_init(&qemu_pause_cond); + qemu_cond_init(&qemu_system_cond); + qemu_mutex_init(&qemu_fair_mutex); + qemu_mutex_init(&qemu_global_mutex); + qemu_mutex_lock(&qemu_global_mutex); + + qemu_thread_self(&io_thread); + + return 0; +} + +void qemu_main_loop_start(void) +{ + qemu_system_ready = 1; + qemu_cond_broadcast(&qemu_system_cond); +} + +void run_on_cpu(CPUState *env, void (*func)(void *data), void *data) +{ + struct qemu_work_item wi; + + if (qemu_cpu_self(env)) { + func(data); + return; + } + + wi.func = func; + wi.data = data; + if (!env->queued_work_first) + env->queued_work_first = &wi; + else + env->queued_work_last->next = &wi; + env->queued_work_last = &wi; + wi.next = NULL; + wi.done = false; + + qemu_cpu_kick(env); + while (!wi.done) { + CPUState *self_env = cpu_single_env; + + qemu_cond_wait(&qemu_work_cond, &qemu_global_mutex); + cpu_single_env = self_env; + } +} + +static void flush_queued_work(CPUState *env) +{ + struct qemu_work_item *wi; + + if (!env->queued_work_first) + return; + + while ((wi = env->queued_work_first)) { + env->queued_work_first = wi->next; + wi->func(wi->data); + wi->done = true; + } + env->queued_work_last = NULL; + qemu_cond_broadcast(&qemu_work_cond); +} + +static void qemu_wait_io_event_common(CPUState *env) +{ + if (env->stop) { + env->stop = 0; + env->stopped = 1; + qemu_cond_signal(&qemu_pause_cond); + } + flush_queued_work(env); +} + +static void qemu_tcg_wait_io_event(void) +{ + CPUState *env; + + while (!any_cpu_has_work()) + qemu_cond_timedwait(tcg_halt_cond, &qemu_global_mutex, 1000); + + qemu_mutex_unlock(&qemu_global_mutex); + + /* + * Users of qemu_global_mutex can be starved, having no chance + * to acquire it since this path will get to it first. + * So use another lock to provide fairness. + */ + qemu_mutex_lock(&qemu_fair_mutex); + qemu_mutex_unlock(&qemu_fair_mutex); + + qemu_mutex_lock(&qemu_global_mutex); + + for (env = first_cpu; env != NULL; env = env->next_cpu) { + qemu_wait_io_event_common(env); + } +} + +static void sigbus_reraise(void) +{ + sigset_t set; + struct sigaction action; + + memset(&action, 0, sizeof(action)); + action.sa_handler = SIG_DFL; + if (!sigaction(SIGBUS, &action, NULL)) { + raise(SIGBUS); + sigemptyset(&set); + sigaddset(&set, SIGBUS); + sigprocmask(SIG_UNBLOCK, &set, NULL); + } + perror("Failed to re-raise SIGBUS!\n"); + abort(); +} + +static void sigbus_handler(int n, struct qemu_signalfd_siginfo *siginfo, + void *ctx) +{ +#if defined(TARGET_I386) + if (kvm_on_sigbus(siginfo->ssi_code, (void *)(intptr_t)siginfo->ssi_addr)) +#endif + sigbus_reraise(); +} + +static void qemu_kvm_eat_signal(CPUState *env, int timeout) +{ + struct timespec ts; + int r, e; + siginfo_t siginfo; + sigset_t waitset; + sigset_t chkset; + + ts.tv_sec = timeout / 1000; + ts.tv_nsec = (timeout % 1000) * 1000000; + + sigemptyset(&waitset); + sigaddset(&waitset, SIG_IPI); + sigaddset(&waitset, SIGBUS); + + do { + qemu_mutex_unlock(&qemu_global_mutex); + + r = sigtimedwait(&waitset, &siginfo, &ts); + e = errno; + + qemu_mutex_lock(&qemu_global_mutex); + + if (r == -1 && !(e == EAGAIN || e == EINTR)) { + fprintf(stderr, "sigtimedwait: %s\n", strerror(e)); + exit(1); + } + + switch (r) { + case SIGBUS: +#ifdef TARGET_I386 + if (kvm_on_sigbus_vcpu(env, siginfo.si_code, siginfo.si_addr)) +#endif + sigbus_reraise(); + break; + default: + break; + } + + r = sigpending(&chkset); + if (r == -1) { + fprintf(stderr, "sigpending: %s\n", strerror(e)); + exit(1); + } + } while (sigismember(&chkset, SIG_IPI) || sigismember(&chkset, SIGBUS)); +} + +static void qemu_kvm_wait_io_event(CPUState *env) +{ + while (!cpu_has_work(env)) + qemu_cond_timedwait(env->halt_cond, &qemu_global_mutex, 1000); + + qemu_kvm_eat_signal(env, 0); + qemu_wait_io_event_common(env); +} + +static int qemu_cpu_exec(CPUState *env); + +static void *kvm_cpu_thread_fn(void *arg) +{ + CPUState *env = arg; + + qemu_mutex_lock(&qemu_global_mutex); + qemu_thread_self(env->thread); + if (kvm_enabled()) + kvm_init_vcpu(env); + + kvm_init_ipi(env); + + /* signal CPU creation */ + env->created = 1; + qemu_cond_signal(&qemu_cpu_cond); + + /* and wait for machine initialization */ + while (!qemu_system_ready) + qemu_cond_timedwait(&qemu_system_cond, &qemu_global_mutex, 100); + + while (1) { + if (cpu_can_run(env)) + qemu_cpu_exec(env); + qemu_kvm_wait_io_event(env); + } + + return NULL; +} + +static void *tcg_cpu_thread_fn(void *arg) +{ + CPUState *env = arg; + + tcg_init_ipi(); + qemu_thread_self(env->thread); + + /* signal CPU creation */ + qemu_mutex_lock(&qemu_global_mutex); + for (env = first_cpu; env != NULL; env = env->next_cpu) + env->created = 1; + qemu_cond_signal(&qemu_cpu_cond); + + /* and wait for machine initialization */ + while (!qemu_system_ready) + qemu_cond_timedwait(&qemu_system_cond, &qemu_global_mutex, 100); + + while (1) { + cpu_exec_all(); + qemu_tcg_wait_io_event(); + } + + return NULL; +} + +void qemu_cpu_kick(void *_env) +{ + CPUState *env = _env; + qemu_cond_broadcast(env->halt_cond); + qemu_thread_signal(env->thread, SIG_IPI); +} + +int qemu_cpu_self(void *_env) +{ + CPUState *env = _env; + QemuThread this; + + qemu_thread_self(&this); + + return qemu_thread_equal(&this, env->thread); +} + +static void cpu_signal(int sig) +{ + if (cpu_single_env) + cpu_exit(cpu_single_env); + exit_request = 1; +} + +static void tcg_init_ipi(void) +{ + sigset_t set; + struct sigaction sigact; + + memset(&sigact, 0, sizeof(sigact)); + sigact.sa_handler = cpu_signal; + sigaction(SIG_IPI, &sigact, NULL); + + sigemptyset(&set); + sigaddset(&set, SIG_IPI); + pthread_sigmask(SIG_UNBLOCK, &set, NULL); +} + +static void dummy_signal(int sig) +{ +} + +static void kvm_init_ipi(CPUState *env) +{ + int r; + sigset_t set; + struct sigaction sigact; + + memset(&sigact, 0, sizeof(sigact)); + sigact.sa_handler = dummy_signal; + sigaction(SIG_IPI, &sigact, NULL); + + pthread_sigmask(SIG_BLOCK, NULL, &set); + sigdelset(&set, SIG_IPI); + sigdelset(&set, SIGBUS); + r = kvm_set_signal_mask(env, &set); + if (r) { + fprintf(stderr, "kvm_set_signal_mask: %s\n", strerror(r)); + exit(1); + } +} + +static sigset_t block_io_signals(void) +{ + sigset_t set; + struct sigaction action; + + /* SIGUSR2 used by posix-aio-compat.c */ + sigemptyset(&set); + sigaddset(&set, SIGUSR2); + pthread_sigmask(SIG_UNBLOCK, &set, NULL); + + sigemptyset(&set); + sigaddset(&set, SIGIO); + sigaddset(&set, SIGALRM); + sigaddset(&set, SIG_IPI); + sigaddset(&set, SIGBUS); + pthread_sigmask(SIG_BLOCK, &set, NULL); + + memset(&action, 0, sizeof(action)); + action.sa_flags = SA_SIGINFO; + action.sa_sigaction = (void (*)(int, siginfo_t*, void*))sigbus_handler; + sigaction(SIGBUS, &action, NULL); + prctl(PR_MCE_KILL, 1, 1, 0, 0); + + return set; +} + +void qemu_mutex_lock_iothread(void) +{ + if (kvm_enabled()) { + qemu_mutex_lock(&qemu_global_mutex); + } else { + qemu_mutex_lock(&qemu_fair_mutex); + if (qemu_mutex_trylock(&qemu_global_mutex)) { + qemu_thread_signal(tcg_cpu_thread, SIG_IPI); + qemu_mutex_lock(&qemu_global_mutex); + } + qemu_mutex_unlock(&qemu_fair_mutex); + } +} + +void qemu_mutex_unlock_iothread(void) +{ + qemu_mutex_unlock(&qemu_global_mutex); +} + +static int all_vcpus_paused(void) +{ + CPUState *penv = first_cpu; + + while (penv) { + if (!penv->stopped) + return 0; + penv = (CPUState *)penv->next_cpu; + } + + return 1; +} + +void pause_all_vcpus(void) +{ + CPUState *penv = first_cpu; + + while (penv) { + penv->stop = 1; + qemu_cpu_kick(penv); + penv = (CPUState *)penv->next_cpu; + } + + while (!all_vcpus_paused()) { + qemu_cond_timedwait(&qemu_pause_cond, &qemu_global_mutex, 100); + penv = first_cpu; + while (penv) { + qemu_cpu_kick(penv); + penv = (CPUState *)penv->next_cpu; + } + } +} + +void resume_all_vcpus(void) +{ + CPUState *penv = first_cpu; + + while (penv) { + penv->stop = 0; + penv->stopped = 0; + qemu_cpu_kick(penv); + penv = (CPUState *)penv->next_cpu; + } +} + +static void tcg_init_vcpu(void *_env) +{ + CPUState *env = _env; + /* share a single thread for all cpus with TCG */ + if (!tcg_cpu_thread) { + env->thread = qemu_mallocz(sizeof(QemuThread)); + env->halt_cond = qemu_mallocz(sizeof(QemuCond)); + qemu_cond_init(env->halt_cond); + qemu_thread_create(env->thread, tcg_cpu_thread_fn, env); + while (env->created == 0) + qemu_cond_timedwait(&qemu_cpu_cond, &qemu_global_mutex, 100); + tcg_cpu_thread = env->thread; + tcg_halt_cond = env->halt_cond; + } else { + env->thread = tcg_cpu_thread; + env->halt_cond = tcg_halt_cond; + } +} + +static void kvm_start_vcpu(CPUState *env) +{ + env->thread = qemu_mallocz(sizeof(QemuThread)); + env->halt_cond = qemu_mallocz(sizeof(QemuCond)); + qemu_cond_init(env->halt_cond); + qemu_thread_create(env->thread, kvm_cpu_thread_fn, env); + while (env->created == 0) + qemu_cond_timedwait(&qemu_cpu_cond, &qemu_global_mutex, 100); +} + +void qemu_init_vcpu(void *_env) +{ + CPUState *env = _env; + + env->nr_cores = smp_cores; + env->nr_threads = smp_threads; + if (kvm_enabled()) + kvm_start_vcpu(env); + else + tcg_init_vcpu(env); +} + +void qemu_notify_event(void) +{ + qemu_event_increment(); +} + +static void qemu_system_vmstop_request(int reason) +{ + vmstop_requested = reason; + qemu_notify_event(); +} + +void vm_stop(int reason) +{ + QemuThread me; + qemu_thread_self(&me); + + if (!qemu_thread_equal(&me, &io_thread)) { + qemu_system_vmstop_request(reason); + /* + * FIXME: should not return to device code in case + * vm_stop() has been requested. + */ + if (cpu_single_env) { + cpu_exit(cpu_single_env); + cpu_single_env->stop = 1; + } + return; + } + do_vm_stop(reason); +} + +#endif + +static int qemu_cpu_exec(CPUState *env) +{ + int ret; +#ifdef CONFIG_PROFILER + int64_t ti; +#endif + +#ifdef CONFIG_PROFILER + ti = profile_getclock(); +#endif + if (use_icount) { + int64_t count; + int decr; + qemu_icount -= (env->icount_decr.u16.low + env->icount_extra); + env->icount_decr.u16.low = 0; + env->icount_extra = 0; + count = qemu_icount_round (qemu_next_deadline()); + qemu_icount += count; + decr = (count > 0xffff) ? 0xffff : count; + count -= decr; + env->icount_decr.u16.low = decr; + env->icount_extra = count; + } + ret = cpu_exec(env); +#ifdef CONFIG_PROFILER + qemu_time += profile_getclock() - ti; +#endif + if (use_icount) { + /* Fold pending instructions back into the + instruction counter, and clear the interrupt flag. */ + qemu_icount -= (env->icount_decr.u16.low + + env->icount_extra); + env->icount_decr.u32 = 0; + env->icount_extra = 0; + } + return ret; +} + +bool cpu_exec_all(void) +{ + if (next_cpu == NULL) + next_cpu = first_cpu; + for (; next_cpu != NULL && !exit_request; next_cpu = next_cpu->next_cpu) { + CPUState *env = next_cpu; + + qemu_clock_enable(vm_clock, + (env->singlestep_enabled & SSTEP_NOTIMER) == 0); + + if (qemu_alarm_pending()) + break; + if (cpu_can_run(env)) { + if (qemu_cpu_exec(env) == EXCP_DEBUG) { + break; + } + } else if (env->stop) { + break; + } + } + exit_request = 0; + return any_cpu_has_work(); +} + +void set_numa_modes(void) +{ + CPUState *env; + int i; + + for (env = first_cpu; env != NULL; env = env->next_cpu) { + for (i = 0; i < nb_numa_nodes; i++) { + if (node_cpumask[i] & (1 << env->cpu_index)) { + env->numa_node = i; + } + } + } +} + +void set_cpu_log(const char *optarg) +{ + int mask; + const CPULogItem *item; + + mask = cpu_str_to_log_mask(optarg); + if (!mask) { + printf("Log items (comma separated):\n"); + for (item = cpu_log_items; item->mask != 0; item++) { + printf("%-10s %s\n", item->name, item->help); + } + exit(1); + } + cpu_set_log(mask); +} + +/* Return the virtual CPU time, based on the instruction counter. */ +int64_t cpu_get_icount(void) +{ + int64_t icount; + CPUState *env = cpu_single_env;; + + icount = qemu_icount; + if (env) { + if (!can_do_io(env)) { + fprintf(stderr, "Bad clock read\n"); + } + icount -= (env->icount_decr.u16.low + env->icount_extra); + } + return qemu_icount_bias + (icount << icount_time_shift); +} + +void list_cpus(FILE *f, fprintf_function cpu_fprintf, const char *optarg) +{ + /* XXX: implement xxx_cpu_list for targets that still miss it */ +#if defined(cpu_list_id) + cpu_list_id(f, cpu_fprintf, optarg); +#elif defined(cpu_list) + cpu_list(f, cpu_fprintf); /* deprecated */ +#endif +} diff -Nru qemu-kvm-0.12.5+noroms/cpus.h qemu-kvm-0.14.1/cpus.h --- qemu-kvm-0.12.5+noroms/cpus.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/cpus.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,21 @@ +#ifndef QEMU_CPUS_H +#define QEMU_CPUS_H + +/* cpus.c */ +int qemu_init_main_loop(void); +void qemu_main_loop_start(void); +void resume_all_vcpus(void); +void pause_all_vcpus(void); + +/* vl.c */ +extern int smp_cores; +extern int smp_threads; +extern int debug_requested; +extern int vmstop_requested; +void vm_state_notify(int running, int reason); +bool cpu_exec_all(void); +void set_numa_modes(void); +void set_cpu_log(const char *optarg); +void list_cpus(FILE *f, fprintf_function cpu_fprintf, const char *optarg); + +#endif diff -Nru qemu-kvm-0.12.5+noroms/create_config qemu-kvm-0.14.1/create_config --- qemu-kvm-0.12.5+noroms/create_config 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/create_config 1970-01-01 00:00:00.000000000 +0000 @@ -1,99 +0,0 @@ -#!/bin/sh - -echo "/* Automatically generated by create_config - do not modify */" - -while read line; do - -case $line in - VERSION=*) # configuration - version=${line#*=} - echo "#define QEMU_VERSION \"$version\"" - ;; - PKGVERSION=*) # configuration - pkgversion=${line#*=} - echo "#define QEMU_PKGVERSION \"$pkgversion\"" - ;; - ARCH=*) # configuration - arch=${line#*=} - arch_name=`echo $arch | tr '[:lower:]' '[:upper:]'` - echo "#define HOST_$arch_name 1" - ;; - CONFIG_AUDIO_DRIVERS=*) - drivers=${line#*=} - echo "#define CONFIG_AUDIO_DRIVERS \\" - for drv in $drivers; do - echo " &${drv}_audio_driver,\\" - done - echo "" - ;; - CONFIG_BDRV_WHITELIST=*) - echo "#define CONFIG_BDRV_WHITELIST \\" - for drv in ${line#*=}; do - echo " \"${drv}\",\\" - done - echo " NULL" - ;; - CONFIG_*=y) # configuration - name=${line%=*} - echo "#define $name 1" - ;; - CONFIG_*=*) # configuration - name=${line%=*} - value=${line#*=} - echo "#define $name $value" - ;; - ARCH=*) # configuration - arch=${line#*=} - arch_name=`echo $arch | tr '[:lower:]' '[:upper:]'` - echo "#define HOST_$arch_name 1" - ;; - HOST_USB=*) - # do nothing - ;; - HOST_CC=*) - # do nothing - ;; - HOST_*=y) # configuration - name=${line%=*} - echo "#define $name 1" - ;; - HOST_*=*) # configuration - name=${line%=*} - value=${line#*=} - echo "#define $name $value" - ;; - TARGET_ARCH=*) # configuration - target_arch=${line#*=} - echo "#define TARGET_ARCH \"$target_arch\"" - ;; - TARGET_BASE_ARCH=*) # configuration - target_base_arch=${line#*=} - if [ "$target_base_arch" != "$target_arch" ]; then - base_arch_name=`echo $target_base_arch | tr '[:lower:]' '[:upper:]'` - echo "#define TARGET_$base_arch_name 1" - fi - ;; - TARGET_XML_FILES=*) - # do nothing - ;; - TARGET_ABI_DIR=*) - # do nothing - ;; - TARGET_ARCH2=*) - # do nothing - ;; - TARGET_DIRS=*) - # do nothing - ;; - TARGET_*=y) # configuration - name=${line%=*} - echo "#define $name 1" - ;; - TARGET_*=*) # configuration - name=${line%=*} - value=${line#*=} - echo "#define $name $value" - ;; -esac - -done # read diff -Nru qemu-kvm-0.12.5+noroms/cris-dis.c qemu-kvm-0.14.1/cris-dis.c --- qemu-kvm-0.12.5+noroms/cris-dis.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/cris-dis.c 2011-05-11 13:29:46.000000000 +0000 @@ -18,13 +18,11 @@ You should have received a copy of the GNU General Public License along with this program; if not, see . */ +#include "qemu-common.h" #include "dis-asm.h" //#include "sysdep.h" #include "target-cris/opcode-cris.h" //#include "libiberty.h" - - -void *qemu_malloc(size_t len); /* can't include qemu-common.h here */ #define CONST_STRNEQ(STR1,STR2) (strncmp ((STR1), (STR2), sizeof (STR2) - 1) == 0) @@ -2769,7 +2767,6 @@ } /* Disassemble, prefixing register names with `$'. CRIS v0..v10. */ -#if 0 static int print_insn_cris_with_register_prefix (bfd_vma vma, disassemble_info *info) @@ -2779,7 +2776,6 @@ return -1; return print_insn_cris_generic (vma, info, true); } -#endif /* Disassemble, prefixing register names with `$'. CRIS v32. */ static int @@ -2845,6 +2841,13 @@ #endif int +print_insn_crisv10 (bfd_vma vma, + disassemble_info *info) +{ + return print_insn_cris_with_register_prefix(vma, info); +} + +int print_insn_crisv32 (bfd_vma vma, disassemble_info *info) { diff -Nru qemu-kvm-0.12.5+noroms/curses.c qemu-kvm-0.14.1/curses.c --- qemu-kvm-0.12.5+noroms/curses.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/curses.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,371 +0,0 @@ -/* - * QEMU curses/ncurses display driver - * - * Copyright (c) 2005 Andrzej Zaborowski - * - * 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 - -#ifndef _WIN32 -#include -#include -#include -#endif - -#ifdef __OpenBSD__ -#define resize_term resizeterm -#endif - -#include "qemu-common.h" -#include "console.h" -#include "sysemu.h" - -#define FONT_HEIGHT 16 -#define FONT_WIDTH 8 - -static console_ch_t screen[160 * 100]; -static WINDOW *screenpad = NULL; -static int width, height, gwidth, gheight, invalidate; -static int px, py, sminx, sminy, smaxx, smaxy; - -static void curses_update(DisplayState *ds, int x, int y, int w, int h) -{ - chtype *line; - - line = ((chtype *) screen) + y * width; - for (h += y; y < h; y ++, line += width) - mvwaddchnstr(screenpad, y, 0, line, width); - - pnoutrefresh(screenpad, py, px, sminy, sminx, smaxy - 1, smaxx - 1); - refresh(); -} - -static void curses_calc_pad(void) -{ - if (is_fixedsize_console()) { - width = gwidth; - height = gheight; - } else { - width = COLS; - height = LINES; - } - - if (screenpad) - delwin(screenpad); - - clear(); - refresh(); - - screenpad = newpad(height, width); - - if (width > COLS) { - px = (width - COLS) / 2; - sminx = 0; - smaxx = COLS; - } else { - px = 0; - sminx = (COLS - width) / 2; - smaxx = sminx + width; - } - - if (height > LINES) { - py = (height - LINES) / 2; - sminy = 0; - smaxy = LINES; - } else { - py = 0; - sminy = (LINES - height) / 2; - smaxy = sminy + height; - } -} - -static void curses_resize(DisplayState *ds) -{ - if (ds_get_width(ds) == gwidth && ds_get_height(ds) == gheight) - return; - - gwidth = ds_get_width(ds); - gheight = ds_get_height(ds); - - curses_calc_pad(); - ds->surface->width = width * FONT_WIDTH; - ds->surface->height = height * FONT_HEIGHT; -} - -#ifndef _WIN32 -#if defined(SIGWINCH) && defined(KEY_RESIZE) -static void curses_winch_handler(int signum) -{ - struct winsize { - unsigned short ws_row; - unsigned short ws_col; - unsigned short ws_xpixel; /* unused */ - unsigned short ws_ypixel; /* unused */ - } ws; - - /* terminal size changed */ - if (ioctl(1, TIOCGWINSZ, &ws) == -1) - return; - - resize_term(ws.ws_row, ws.ws_col); - curses_calc_pad(); - invalidate = 1; - - /* some systems require this */ - signal(SIGWINCH, curses_winch_handler); -} -#endif -#endif - -static void curses_cursor_position(DisplayState *ds, int x, int y) -{ - if (x >= 0) { - x = sminx + x - px; - y = sminy + y - py; - - if (x >= 0 && y >= 0 && x < COLS && y < LINES) { - move(y, x); - curs_set(1); - /* it seems that curs_set(1) must always be called before - * curs_set(2) for the latter to have effect */ - if (!is_graphic_console()) - curs_set(2); - return; - } - } - - curs_set(0); -} - -/* generic keyboard conversion */ - -#include "curses_keys.h" - -static kbd_layout_t *kbd_layout = NULL; -static int keycode2keysym[CURSES_KEYS]; - -static void curses_refresh(DisplayState *ds) -{ - int chr, nextchr, keysym, keycode; - - if (invalidate) { - clear(); - refresh(); - curses_calc_pad(); - ds->surface->width = FONT_WIDTH * width; - ds->surface->height = FONT_HEIGHT * height; - vga_hw_invalidate(); - invalidate = 0; - } - - vga_hw_text_update(screen); - - nextchr = ERR; - while (1) { - /* while there are any pending key strokes to process */ - if (nextchr == ERR) - chr = getch(); - else { - chr = nextchr; - nextchr = ERR; - } - - if (chr == ERR) - break; - -#ifdef KEY_RESIZE - /* this shouldn't occur when we use a custom SIGWINCH handler */ - if (chr == KEY_RESIZE) { - clear(); - refresh(); - curses_calc_pad(); - curses_update(ds, 0, 0, width, height); - ds->surface->width = FONT_WIDTH * width; - ds->surface->height = FONT_HEIGHT * height; - continue; - } -#endif - - keycode = curses2keycode[chr]; - if (keycode == -1) - continue; - - /* alt key */ - if (keycode == 1) { - nextchr = getch(); - - if (nextchr != ERR) { - keycode = curses2keycode[nextchr]; - nextchr = ERR; - if (keycode == -1) - continue; - - keycode |= ALT; - - /* process keys reserved for qemu */ - if (keycode >= QEMU_KEY_CONSOLE0 && - keycode < QEMU_KEY_CONSOLE0 + 9) { - erase(); - wnoutrefresh(stdscr); - console_select(keycode - QEMU_KEY_CONSOLE0); - - invalidate = 1; - continue; - } - } - } - - if (kbd_layout && !(keycode & GREY)) { - keysym = keycode2keysym[keycode & KEY_MASK]; - if (keysym == -1) - keysym = chr; - - keycode &= ~KEY_MASK; - keycode |= keysym2scancode(kbd_layout, keysym); - } - - if (is_graphic_console()) { - /* since terminals don't know about key press and release - * events, we need to emit both for each key received */ - if (keycode & SHIFT) - kbd_put_keycode(SHIFT_CODE); - if (keycode & CNTRL) - kbd_put_keycode(CNTRL_CODE); - if (keycode & ALT) - kbd_put_keycode(ALT_CODE); - if (keycode & GREY) - kbd_put_keycode(GREY_CODE); - kbd_put_keycode(keycode & KEY_MASK); - if (keycode & GREY) - kbd_put_keycode(GREY_CODE); - kbd_put_keycode((keycode & KEY_MASK) | KEY_RELEASE); - if (keycode & ALT) - kbd_put_keycode(ALT_CODE | KEY_RELEASE); - if (keycode & CNTRL) - kbd_put_keycode(CNTRL_CODE | KEY_RELEASE); - if (keycode & SHIFT) - kbd_put_keycode(SHIFT_CODE | KEY_RELEASE); - } else { - keysym = curses2keysym[chr]; - if (keysym == -1) - keysym = chr; - - kbd_put_keysym(keysym); - } - } -} - -static void curses_cleanup(void *opaque) -{ - endwin(); -} - -static void curses_atexit(void) -{ - curses_cleanup(NULL); -} - -static void curses_setup(void) -{ - int i, colour_default[8] = { - COLOR_BLACK, COLOR_BLUE, COLOR_GREEN, COLOR_CYAN, - COLOR_RED, COLOR_MAGENTA, COLOR_YELLOW, COLOR_WHITE, - }; - - /* input as raw as possible, let everything be interpreted - * by the guest system */ - initscr(); noecho(); intrflush(stdscr, FALSE); - nodelay(stdscr, TRUE); nonl(); keypad(stdscr, TRUE); - start_color(); raw(); scrollok(stdscr, FALSE); - - for (i = 0; i < 64; i ++) - init_pair(i, colour_default[i & 7], colour_default[i >> 3]); -} - -static void curses_keyboard_setup(void) -{ - int i, keycode, keysym; - -#if defined(__APPLE__) - /* always use generic keymaps */ - if (!keyboard_layout) - keyboard_layout = "en-us"; -#endif - if(keyboard_layout) { - kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout); - if (!kbd_layout) - exit(1); - } - - for (i = 0; i < CURSES_KEYS; i ++) - keycode2keysym[i] = -1; - - for (i = 0; i < CURSES_KEYS; i ++) { - if (curses2keycode[i] == -1) - continue; - - keycode = curses2keycode[i] & KEY_MASK; - if (keycode2keysym[keycode] >= 0) - continue; - - for (keysym = 0; keysym < CURSES_KEYS; keysym ++) - if (curses2keycode[keysym] == keycode) { - keycode2keysym[keycode] = keysym; - break; - } - - if (keysym >= CURSES_KEYS) - keycode2keysym[keycode] = i; - } -} - -void curses_display_init(DisplayState *ds, int full_screen) -{ - DisplayChangeListener *dcl; -#ifndef _WIN32 - if (!isatty(1)) { - fprintf(stderr, "We need a terminal output\n"); - exit(1); - } -#endif - - curses_setup(); - curses_keyboard_setup(); - atexit(curses_atexit); - -#ifndef _WIN32 -#if defined(SIGWINCH) && defined(KEY_RESIZE) - /* some curses implementations provide a handler, but we - * want to be sure this is handled regardless of the library */ - signal(SIGWINCH, curses_winch_handler); -#endif -#endif - - dcl = (DisplayChangeListener *) qemu_mallocz(sizeof(DisplayChangeListener)); - dcl->dpy_update = curses_update; - dcl->dpy_resize = curses_resize; - dcl->dpy_refresh = curses_refresh; - dcl->dpy_text_cursor = curses_cursor_position; - register_displaychangelistener(ds, dcl); - qemu_free_displaysurface(ds); - ds->surface = qemu_create_displaysurface_from(640, 400, 0, 0, (uint8_t*) screen); - - invalidate = 1; -} diff -Nru qemu-kvm-0.12.5+noroms/curses_keys.h qemu-kvm-0.14.1/curses_keys.h --- qemu-kvm-0.12.5+noroms/curses_keys.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/curses_keys.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,483 +0,0 @@ -/* - * Keycode and keysyms conversion tables for curses - * - * Copyright (c) 2005 Andrzej Zaborowski - * - * 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 "keymaps.h" - - -#define KEY_RELEASE 0x80 -#define KEY_MASK 0x7f -#define SHIFT_CODE 0x2a -#define SHIFT 0x0080 -#define GREY_CODE 0xe0 -#define GREY 0x0100 -#define CNTRL_CODE 0x1d -#define CNTRL 0x0200 -#define ALT_CODE 0x38 -#define ALT 0x0400 - -/* curses won't detect a Control + Alt + 1, so use Alt + 1 */ -#define QEMU_KEY_CONSOLE0 (2 | ALT) /* (curses2keycode['1'] | ALT) */ - -#define CURSES_KEYS KEY_MAX /* KEY_MAX defined in */ - -static const int curses2keycode[CURSES_KEYS] = { - [0 ... (CURSES_KEYS - 1)] = -1, - - [0x01b] = 1, /* Escape */ - ['1'] = 2, - ['2'] = 3, - ['3'] = 4, - ['4'] = 5, - ['5'] = 6, - ['6'] = 7, - ['7'] = 8, - ['8'] = 9, - ['9'] = 10, - ['0'] = 11, - ['-'] = 12, - ['='] = 13, - [0x07f] = 14, /* Backspace */ - [0x107] = 14, /* Backspace */ - - ['\t'] = 15, /* Tab */ - ['q'] = 16, - ['w'] = 17, - ['e'] = 18, - ['r'] = 19, - ['t'] = 20, - ['y'] = 21, - ['u'] = 22, - ['i'] = 23, - ['o'] = 24, - ['p'] = 25, - ['['] = 26, - [']'] = 27, - ['\n'] = 28, /* Return */ - ['\r'] = 28, /* Return */ - [0x157] = 28, /* Return */ - - ['a'] = 30, - ['s'] = 31, - ['d'] = 32, - ['f'] = 33, - ['g'] = 34, - ['h'] = 35, - ['j'] = 36, - ['k'] = 37, - ['l'] = 38, - [';'] = 39, - ['\''] = 40, /* Single quote */ - ['`'] = 41, - ['\\'] = 43, /* Backslash */ - - ['z'] = 44, - ['x'] = 45, - ['c'] = 46, - ['v'] = 47, - ['b'] = 48, - ['n'] = 49, - ['m'] = 50, - [','] = 51, - ['.'] = 52, - ['/'] = 53, - - [' '] = 57, - - [0x109] = 59, /* Function Key 1 */ - [0x10a] = 60, /* Function Key 2 */ - [0x10b] = 61, /* Function Key 3 */ - [0x10c] = 62, /* Function Key 4 */ - [0x10d] = 63, /* Function Key 5 */ - [0x10e] = 64, /* Function Key 6 */ - [0x10f] = 65, /* Function Key 7 */ - [0x110] = 66, /* Function Key 8 */ - [0x111] = 67, /* Function Key 9 */ - [0x112] = 68, /* Function Key 10 */ - [0x113] = 87, /* Function Key 11 */ - [0x114] = 88, /* Function Key 12 */ - - [0x106] = 71 | GREY, /* Home */ - [0x103] = 72 | GREY, /* Up Arrow */ - [0x153] = 73 | GREY, /* Page Up */ - [0x104] = 75 | GREY, /* Left Arrow */ - [0x105] = 77 | GREY, /* Right Arrow */ - [0x168] = 79 | GREY, /* End */ - [0x102] = 80 | GREY, /* Down Arrow */ - [0x152] = 81 | GREY, /* Page Down */ - [0x14b] = 82 | GREY, /* Insert */ - [0x14a] = 83 | GREY, /* Delete */ - - ['!'] = 2 | SHIFT, - ['@'] = 3 | SHIFT, - ['#'] = 4 | SHIFT, - ['$'] = 5 | SHIFT, - ['%'] = 6 | SHIFT, - ['^'] = 7 | SHIFT, - ['&'] = 8 | SHIFT, - ['*'] = 9 | SHIFT, - ['('] = 10 | SHIFT, - [')'] = 11 | SHIFT, - ['_'] = 12 | SHIFT, - ['+'] = 13 | SHIFT, - - [0x161] = 15 | SHIFT, /* Shift + Tab */ - ['Q'] = 16 | SHIFT, - ['W'] = 17 | SHIFT, - ['E'] = 18 | SHIFT, - ['R'] = 19 | SHIFT, - ['T'] = 20 | SHIFT, - ['Y'] = 21 | SHIFT, - ['U'] = 22 | SHIFT, - ['I'] = 23 | SHIFT, - ['O'] = 24 | SHIFT, - ['P'] = 25 | SHIFT, - ['{'] = 26 | SHIFT, - ['}'] = 27 | SHIFT, - - ['A'] = 30 | SHIFT, - ['S'] = 31 | SHIFT, - ['D'] = 32 | SHIFT, - ['F'] = 33 | SHIFT, - ['G'] = 34 | SHIFT, - ['H'] = 35 | SHIFT, - ['J'] = 36 | SHIFT, - ['K'] = 37 | SHIFT, - ['L'] = 38 | SHIFT, - [':'] = 39 | SHIFT, - ['"'] = 40 | SHIFT, - ['~'] = 41 | SHIFT, - ['|'] = 43 | SHIFT, - - ['Z'] = 44 | SHIFT, - ['X'] = 45 | SHIFT, - ['C'] = 46 | SHIFT, - ['V'] = 47 | SHIFT, - ['B'] = 48 | SHIFT, - ['N'] = 49 | SHIFT, - ['M'] = 50 | SHIFT, - ['<'] = 51 | SHIFT, - ['>'] = 52 | SHIFT, - ['?'] = 53 | SHIFT, - - [0x115] = 59 | SHIFT, /* Shift + Function Key 1 */ - [0x116] = 60 | SHIFT, /* Shift + Function Key 2 */ - [0x117] = 61 | SHIFT, /* Shift + Function Key 3 */ - [0x118] = 62 | SHIFT, /* Shift + Function Key 4 */ - [0x119] = 63 | SHIFT, /* Shift + Function Key 5 */ - [0x11a] = 64 | SHIFT, /* Shift + Function Key 6 */ - [0x11b] = 65 | SHIFT, /* Shift + Function Key 7 */ - [0x11c] = 66 | SHIFT, /* Shift + Function Key 8 */ - - [0x011] = 16 | CNTRL, /* Control + q */ - [0x017] = 17 | CNTRL, /* Control + w */ - [0x005] = 18 | CNTRL, /* Control + e */ - [0x012] = 19 | CNTRL, /* Control + r */ - [0x014] = 20 | CNTRL, /* Control + t */ - [0x019] = 21 | CNTRL, /* Control + y */ - [0x015] = 22 | CNTRL, /* Control + u */ - /* Control + i collides with Tab */ - [0x00f] = 24 | CNTRL, /* Control + o */ - [0x010] = 25 | CNTRL, /* Control + p */ - - [0x001] = 30 | CNTRL, /* Control + a */ - [0x013] = 31 | CNTRL, /* Control + s */ - [0x004] = 32 | CNTRL, /* Control + d */ - [0x006] = 33 | CNTRL, /* Control + f */ - [0x007] = 34 | CNTRL, /* Control + g */ - [0x008] = 35 | CNTRL, /* Control + h */ - /* Control + j collides with Return */ - [0x00b] = 37 | CNTRL, /* Control + k */ - [0x00c] = 38 | CNTRL, /* Control + l */ - - [0x01a] = 44 | CNTRL, /* Control + z */ - [0x018] = 45 | CNTRL, /* Control + x */ - [0x003] = 46 | CNTRL, /* Control + c */ - [0x016] = 47 | CNTRL, /* Control + v */ - [0x002] = 48 | CNTRL, /* Control + b */ - [0x00e] = 49 | CNTRL, /* Control + n */ - /* Control + m collides with the keycode for Enter */ - -}; - -static const int curses2keysym[CURSES_KEYS] = { - [0 ... (CURSES_KEYS - 1)] = -1, - - ['\n'] = '\n', - ['\r'] = '\n', - - [0x07f] = QEMU_KEY_BACKSPACE, - - [0x102] = QEMU_KEY_DOWN, - [0x103] = QEMU_KEY_UP, - [0x104] = QEMU_KEY_LEFT, - [0x105] = QEMU_KEY_RIGHT, - [0x106] = QEMU_KEY_HOME, - [0x107] = QEMU_KEY_BACKSPACE, - - [0x14a] = QEMU_KEY_DELETE, - [0x152] = QEMU_KEY_PAGEDOWN, - [0x153] = QEMU_KEY_PAGEUP, - [0x157] = '\n', - [0x168] = QEMU_KEY_END, - -}; - -static const name2keysym_t name2keysym[] = { - /* Plain ASCII */ - { "space", 0x020 }, - { "exclam", 0x021 }, - { "quotedbl", 0x022 }, - { "numbersign", 0x023 }, - { "dollar", 0x024 }, - { "percent", 0x025 }, - { "ampersand", 0x026 }, - { "apostrophe", 0x027 }, - { "parenleft", 0x028 }, - { "parenright", 0x029 }, - { "asterisk", 0x02a }, - { "plus", 0x02b }, - { "comma", 0x02c }, - { "minus", 0x02d }, - { "period", 0x02e }, - { "slash", 0x02f }, - { "0", 0x030 }, - { "1", 0x031 }, - { "2", 0x032 }, - { "3", 0x033 }, - { "4", 0x034 }, - { "5", 0x035 }, - { "6", 0x036 }, - { "7", 0x037 }, - { "8", 0x038 }, - { "9", 0x039 }, - { "colon", 0x03a }, - { "semicolon", 0x03b }, - { "less", 0x03c }, - { "equal", 0x03d }, - { "greater", 0x03e }, - { "question", 0x03f }, - { "at", 0x040 }, - { "A", 0x041 }, - { "B", 0x042 }, - { "C", 0x043 }, - { "D", 0x044 }, - { "E", 0x045 }, - { "F", 0x046 }, - { "G", 0x047 }, - { "H", 0x048 }, - { "I", 0x049 }, - { "J", 0x04a }, - { "K", 0x04b }, - { "L", 0x04c }, - { "M", 0x04d }, - { "N", 0x04e }, - { "O", 0x04f }, - { "P", 0x050 }, - { "Q", 0x051 }, - { "R", 0x052 }, - { "S", 0x053 }, - { "T", 0x054 }, - { "U", 0x055 }, - { "V", 0x056 }, - { "W", 0x057 }, - { "X", 0x058 }, - { "Y", 0x059 }, - { "Z", 0x05a }, - { "bracketleft", 0x05b }, - { "backslash", 0x05c }, - { "bracketright", 0x05d }, - { "asciicircum", 0x05e }, - { "underscore", 0x05f }, - { "grave", 0x060 }, - { "a", 0x061 }, - { "b", 0x062 }, - { "c", 0x063 }, - { "d", 0x064 }, - { "e", 0x065 }, - { "f", 0x066 }, - { "g", 0x067 }, - { "h", 0x068 }, - { "i", 0x069 }, - { "j", 0x06a }, - { "k", 0x06b }, - { "l", 0x06c }, - { "m", 0x06d }, - { "n", 0x06e }, - { "o", 0x06f }, - { "p", 0x070 }, - { "q", 0x071 }, - { "r", 0x072 }, - { "s", 0x073 }, - { "t", 0x074 }, - { "u", 0x075 }, - { "v", 0x076 }, - { "w", 0x077 }, - { "x", 0x078 }, - { "y", 0x079 }, - { "z", 0x07a }, - { "braceleft", 0x07b }, - { "bar", 0x07c }, - { "braceright", 0x07d }, - { "asciitilde", 0x07e }, - - /* Latin-1 extensions */ - { "nobreakspace", 0x0a0 }, - { "exclamdown", 0x0a1 }, - { "cent", 0x0a2 }, - { "sterling", 0x0a3 }, - { "currency", 0x0a4 }, - { "yen", 0x0a5 }, - { "brokenbar", 0x0a6 }, - { "section", 0x0a7 }, - { "diaeresis", 0x0a8 }, - { "copyright", 0x0a9 }, - { "ordfeminine", 0x0aa }, - { "guillemotleft", 0x0ab }, - { "notsign", 0x0ac }, - { "hyphen", 0x0ad }, - { "registered", 0x0ae }, - { "macron", 0x0af }, - { "degree", 0x0b0 }, - { "plusminus", 0x0b1 }, - { "twosuperior", 0x0b2 }, - { "threesuperior", 0x0b3 }, - { "acute", 0x0b4 }, - { "mu", 0x0b5 }, - { "paragraph", 0x0b6 }, - { "periodcentered", 0x0b7 }, - { "cedilla", 0x0b8 }, - { "onesuperior", 0x0b9 }, - { "masculine", 0x0ba }, - { "guillemotright", 0x0bb }, - { "onequarter", 0x0bc }, - { "onehalf", 0x0bd }, - { "threequarters", 0x0be }, - { "questiondown", 0x0bf }, - { "Agrave", 0x0c0 }, - { "Aacute", 0x0c1 }, - { "Acircumflex", 0x0c2 }, - { "Atilde", 0x0c3 }, - { "Adiaeresis", 0x0c4 }, - { "Aring", 0x0c5 }, - { "AE", 0x0c6 }, - { "Ccedilla", 0x0c7 }, - { "Egrave", 0x0c8 }, - { "Eacute", 0x0c9 }, - { "Ecircumflex", 0x0ca }, - { "Ediaeresis", 0x0cb }, - { "Igrave", 0x0cc }, - { "Iacute", 0x0cd }, - { "Icircumflex", 0x0ce }, - { "Idiaeresis", 0x0cf }, - { "ETH", 0x0d0 }, - { "Eth", 0x0d0 }, - { "Ntilde", 0x0d1 }, - { "Ograve", 0x0d2 }, - { "Oacute", 0x0d3 }, - { "Ocircumflex", 0x0d4 }, - { "Otilde", 0x0d5 }, - { "Odiaeresis", 0x0d6 }, - { "multiply", 0x0d7 }, - { "Ooblique", 0x0d8 }, - { "Oslash", 0x0d8 }, - { "Ugrave", 0x0d9 }, - { "Uacute", 0x0da }, - { "Ucircumflex", 0x0db }, - { "Udiaeresis", 0x0dc }, - { "Yacute", 0x0dd }, - { "THORN", 0x0de }, - { "Thorn", 0x0de }, - { "ssharp", 0x0df }, - { "agrave", 0x0e0 }, - { "aacute", 0x0e1 }, - { "acircumflex", 0x0e2 }, - { "atilde", 0x0e3 }, - { "adiaeresis", 0x0e4 }, - { "aring", 0x0e5 }, - { "ae", 0x0e6 }, - { "ccedilla", 0x0e7 }, - { "egrave", 0x0e8 }, - { "eacute", 0x0e9 }, - { "ecircumflex", 0x0ea }, - { "ediaeresis", 0x0eb }, - { "igrave", 0x0ec }, - { "iacute", 0x0ed }, - { "icircumflex", 0x0ee }, - { "idiaeresis", 0x0ef }, - { "eth", 0x0f0 }, - { "ntilde", 0x0f1 }, - { "ograve", 0x0f2 }, - { "oacute", 0x0f3 }, - { "ocircumflex", 0x0f4 }, - { "otilde", 0x0f5 }, - { "odiaeresis", 0x0f6 }, - { "division", 0x0f7 }, - { "oslash", 0x0f8 }, - { "ooblique", 0x0f8 }, - { "ugrave", 0x0f9 }, - { "uacute", 0x0fa }, - { "ucircumflex", 0x0fb }, - { "udiaeresis", 0x0fc }, - { "yacute", 0x0fd }, - { "thorn", 0x0fe }, - { "ydiaeresis", 0x0ff }, - - /* Special keys */ - { "BackSpace", 0x07f }, - { "Tab", '\t' }, - { "Return", '\r' }, - { "Right", 0x105 }, - { "Left", 0x104 }, - { "Up", 0x103 }, - { "Down", 0x102 }, - { "Page_Down", 0x152 }, - { "Page_Up", 0x153 }, - { "Insert", 0x14b }, - { "Delete", 0x14a }, - { "Home", 0x106 }, - { "End", 0x168 }, - { "F1", 0x109 }, - { "F2", 0x10a }, - { "F3", 0x10b }, - { "F4", 0x10c }, - { "F5", 0x10d }, - { "F6", 0x10e }, - { "F7", 0x10f }, - { "F8", 0x110 }, - { "F9", 0x111 }, - { "F10", 0x112 }, - { "F11", 0x113 }, - { "F12", 0x114 }, - { "F13", 0x115 }, - { "F14", 0x116 }, - { "F15", 0x117 }, - { "F16", 0x118 }, - { "F17", 0x119 }, - { "F18", 0x11a }, - { "F19", 0x11b }, - { "F20", 0x11c }, - { "Escape", 27 }, - - { NULL, 0 }, -}; diff -Nru qemu-kvm-0.12.5+noroms/cursor.c qemu-kvm-0.14.1/cursor.c --- qemu-kvm-0.12.5+noroms/cursor.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/cursor.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,210 @@ +#include "qemu-common.h" +#include "console.h" + +#include "cursor_hidden.xpm" +#include "cursor_left_ptr.xpm" + +/* for creating built-in cursors */ +static QEMUCursor *cursor_parse_xpm(const char *xpm[]) +{ + QEMUCursor *c; + uint32_t ctab[128]; + unsigned int width, height, colors, chars; + unsigned int line = 0, i, r, g, b, x, y, pixel; + char name[16]; + uint8_t idx; + + /* parse header line: width, height, #colors, #chars */ + if (sscanf(xpm[line], "%d %d %d %d", &width, &height, &colors, &chars) != 4) { + fprintf(stderr, "%s: header parse error: \"%s\"\n", + __FUNCTION__, xpm[line]); + return NULL; + } + if (chars != 1) { + fprintf(stderr, "%s: chars != 1 not supported\n", __FUNCTION__); + return NULL; + } + line++; + + /* parse color table */ + for (i = 0; i < colors; i++, line++) { + if (sscanf(xpm[line], "%c c %15s", &idx, name) == 2) { + if (sscanf(name, "#%02x%02x%02x", &r, &g, &b) == 3) { + ctab[idx] = (0xff << 24) | (b << 16) | (g << 8) | r; + continue; + } + if (strcmp(name, "None") == 0) { + ctab[idx] = 0x00000000; + continue; + } + } + fprintf(stderr, "%s: color parse error: \"%s\"\n", + __FUNCTION__, xpm[line]); + return NULL; + } + + /* parse pixel data */ + c = cursor_alloc(width, height); + for (pixel = 0, y = 0; y < height; y++, line++) { + for (x = 0; x < height; x++, pixel++) { + idx = xpm[line][x]; + c->data[pixel] = ctab[idx]; + } + } + return c; +} + +/* nice for debugging */ +void cursor_print_ascii_art(QEMUCursor *c, const char *prefix) +{ + uint32_t *data = c->data; + int x,y; + + for (y = 0; y < c->height; y++) { + fprintf(stderr, "%s: %2d: |", prefix, y); + for (x = 0; x < c->width; x++, data++) { + if ((*data & 0xff000000) != 0xff000000) { + fprintf(stderr, " "); /* transparent */ + } else if ((*data & 0x00ffffff) == 0x00ffffff) { + fprintf(stderr, "."); /* white */ + } else if ((*data & 0x00ffffff) == 0x00000000) { + fprintf(stderr, "X"); /* black */ + } else { + fprintf(stderr, "o"); /* other */ + } + } + fprintf(stderr, "|\n"); + } +} + +QEMUCursor *cursor_builtin_hidden(void) +{ + QEMUCursor *c; + + c = cursor_parse_xpm(cursor_hidden_xpm); + return c; +} + +QEMUCursor *cursor_builtin_left_ptr(void) +{ + QEMUCursor *c; + + c = cursor_parse_xpm(cursor_left_ptr_xpm); + return c; +} + +QEMUCursor *cursor_alloc(int width, int height) +{ + QEMUCursor *c; + int datasize = width * height * sizeof(uint32_t); + + c = qemu_mallocz(sizeof(QEMUCursor) + datasize); + c->width = width; + c->height = height; + c->refcount = 1; + return c; +} + +void cursor_get(QEMUCursor *c) +{ + c->refcount++; +} + +void cursor_put(QEMUCursor *c) +{ + if (c == NULL) + return; + c->refcount--; + if (c->refcount) + return; + qemu_free(c); +} + +int cursor_get_mono_bpl(QEMUCursor *c) +{ + return (c->width + 7) / 8; +} + +void cursor_set_mono(QEMUCursor *c, + uint32_t foreground, uint32_t background, uint8_t *image, + int transparent, uint8_t *mask) +{ + uint32_t *data = c->data; + uint8_t bit; + int x,y,bpl; + + bpl = cursor_get_mono_bpl(c); + for (y = 0; y < c->height; y++) { + bit = 0x80; + for (x = 0; x < c->width; x++, data++) { + if (transparent && mask[x/8] & bit) { + *data = 0x00000000; + } else if (!transparent && !(mask[x/8] & bit)) { + *data = 0x00000000; + } else if (image[x/8] & bit) { + *data = 0xff000000 | foreground; + } else { + *data = 0xff000000 | background; + } + bit >>= 1; + if (bit == 0) { + bit = 0x80; + } + } + mask += bpl; + image += bpl; + } +} + +void cursor_get_mono_image(QEMUCursor *c, int foreground, uint8_t *image) +{ + uint32_t *data = c->data; + uint8_t bit; + int x,y,bpl; + + bpl = cursor_get_mono_bpl(c); + memset(image, 0, bpl * c->height); + for (y = 0; y < c->height; y++) { + bit = 0x80; + for (x = 0; x < c->width; x++, data++) { + if (((*data & 0xff000000) == 0xff000000) && + ((*data & 0x00ffffff) == foreground)) { + image[x/8] |= bit; + } + bit >>= 1; + if (bit == 0) { + bit = 0x80; + } + } + image += bpl; + } +} + +void cursor_get_mono_mask(QEMUCursor *c, int transparent, uint8_t *mask) +{ + uint32_t *data = c->data; + uint8_t bit; + int x,y,bpl; + + bpl = cursor_get_mono_bpl(c); + memset(mask, 0, bpl * c->height); + for (y = 0; y < c->height; y++) { + bit = 0x80; + for (x = 0; x < c->width; x++, data++) { + if ((*data & 0xff000000) != 0xff000000) { + if (transparent != 0) { + mask[x/8] |= bit; + } + } else { + if (transparent == 0) { + mask[x/8] |= bit; + } + } + bit >>= 1; + if (bit == 0) { + bit = 0x80; + } + } + mask += bpl; + } +} diff -Nru qemu-kvm-0.12.5+noroms/cursor_hidden.xpm qemu-kvm-0.14.1/cursor_hidden.xpm --- qemu-kvm-0.12.5+noroms/cursor_hidden.xpm 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/cursor_hidden.xpm 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,37 @@ +/* XPM */ +static const char *cursor_hidden_xpm[] = { + "32 32 1 1", + " c None", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", +}; diff -Nru qemu-kvm-0.12.5+noroms/cursor_left_ptr.xpm qemu-kvm-0.14.1/cursor_left_ptr.xpm --- qemu-kvm-0.12.5+noroms/cursor_left_ptr.xpm 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/cursor_left_ptr.xpm 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,39 @@ +/* XPM */ +static const char *cursor_left_ptr_xpm[] = { + "32 32 3 1", + "X c #000000", + ". c #ffffff", + " c None", + "X ", + "XX ", + "X.X ", + "X..X ", + "X...X ", + "X....X ", + "X.....X ", + "X......X ", + "X.......X ", + "X........X ", + "X.....XXXXX ", + "X..X..X ", + "X.X X..X ", + "XX X..X ", + "X X..X ", + " X..X ", + " X..X ", + " X..X ", + " XX ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", + " ", +}; diff -Nru qemu-kvm-0.12.5+noroms/cutils.c qemu-kvm-0.14.1/cutils.c --- qemu-kvm-0.12.5+noroms/cutils.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/cutils.c 2011-05-11 13:29:46.000000000 +0000 @@ -23,6 +23,7 @@ */ #include "qemu-common.h" #include "host-utils.h" +#include void pstrcpy(char *buf, int buf_size, const char *str) { @@ -168,30 +169,50 @@ } /* - * Copies iovecs from src to the end dst until src is completely copied or the - * total size of the copied iovec reaches size. The size of the last copied - * iovec is changed in order to fit the specified total size if it isn't a - * perfect fit already. + * Copies iovecs from src to the end of dst. It starts copying after skipping + * the given number of bytes in src and copies until src is completely copied + * or the total size of the copied iovec reaches size.The size of the last + * copied iovec is changed in order to fit the specified total size if it isn't + * a perfect fit already. */ -void qemu_iovec_concat(QEMUIOVector *dst, QEMUIOVector *src, size_t size) +void qemu_iovec_copy(QEMUIOVector *dst, QEMUIOVector *src, uint64_t skip, + size_t size) { int i; size_t done; + void *iov_base; + uint64_t iov_len; assert(dst->nalloc != -1); done = 0; for (i = 0; (i < src->niov) && (done != size); i++) { - if (done + src->iov[i].iov_len > size) { - qemu_iovec_add(dst, src->iov[i].iov_base, size - done); + if (skip >= src->iov[i].iov_len) { + /* Skip the whole iov */ + skip -= src->iov[i].iov_len; + continue; + } else { + /* Skip only part (or nothing) of the iov */ + iov_base = (uint8_t*) src->iov[i].iov_base + skip; + iov_len = src->iov[i].iov_len - skip; + skip = 0; + } + + if (done + iov_len > size) { + qemu_iovec_add(dst, iov_base, size - done); break; } else { - qemu_iovec_add(dst, src->iov[i].iov_base, src->iov[i].iov_len); + qemu_iovec_add(dst, iov_base, iov_len); } - done += src->iov[i].iov_len; + done += iov_len; } } +void qemu_iovec_concat(QEMUIOVector *dst, QEMUIOVector *src, size_t size) +{ + qemu_iovec_copy(dst, src, 0, size); +} + void qemu_iovec_destroy(QEMUIOVector *qiov) { assert(qiov->nalloc != -1); @@ -238,3 +259,158 @@ count -= copy; } } + +void qemu_iovec_memset(QEMUIOVector *qiov, int c, size_t count) +{ + size_t n; + int i; + + for (i = 0; i < qiov->niov && count; ++i) { + n = MIN(count, qiov->iov[i].iov_len); + memset(qiov->iov[i].iov_base, c, n); + count -= n; + } +} + +void qemu_iovec_memset_skip(QEMUIOVector *qiov, int c, size_t count, + size_t skip) +{ + int i; + size_t done; + void *iov_base; + uint64_t iov_len; + + done = 0; + for (i = 0; (i < qiov->niov) && (done != count); i++) { + if (skip >= qiov->iov[i].iov_len) { + /* Skip the whole iov */ + skip -= qiov->iov[i].iov_len; + continue; + } else { + /* Skip only part (or nothing) of the iov */ + iov_base = (uint8_t*) qiov->iov[i].iov_base + skip; + iov_len = qiov->iov[i].iov_len - skip; + skip = 0; + } + + if (done + iov_len > count) { + memset(iov_base, c, count - done); + break; + } else { + memset(iov_base, c, iov_len); + } + done += iov_len; + } +} + +#ifndef _WIN32 +/* Sets a specific flag */ +int fcntl_setfl(int fd, int flag) +{ + int flags; + + flags = fcntl(fd, F_GETFL); + if (flags == -1) + return -errno; + + if (fcntl(fd, F_SETFL, flags | flag) == -1) + return -errno; + + return 0; +} +#endif + +/* + * Convert string to bytes, allowing either B/b for bytes, K/k for KB, + * M/m for MB, G/g for GB or T/t for TB. Default without any postfix + * is MB. End pointer will be returned in *end, if not NULL. A valid + * value must be terminated by whitespace, ',' or '\0'. Return -1 on + * error. + */ +int64_t strtosz_suffix(const char *nptr, char **end, const char default_suffix) +{ + int64_t retval = -1; + char *endptr; + unsigned char c, d; + int mul_required = 0; + double val, mul, integral, fraction; + + errno = 0; + val = strtod(nptr, &endptr); + if (isnan(val) || endptr == nptr || errno != 0) { + goto fail; + } + fraction = modf(val, &integral); + if (fraction != 0) { + mul_required = 1; + } + /* + * Any whitespace character is fine for terminating the number, + * in addition we accept ',' to handle strings where the size is + * part of a multi token argument. + */ + c = *endptr; + d = c; + if (qemu_isspace(c) || c == '\0' || c == ',') { + c = 0; + if (default_suffix) { + d = default_suffix; + } else { + d = c; + } + } + switch (qemu_toupper(d)) { + case STRTOSZ_DEFSUFFIX_B: + mul = 1; + if (mul_required) { + goto fail; + } + break; + case STRTOSZ_DEFSUFFIX_KB: + mul = 1 << 10; + break; + case 0: + if (mul_required) { + goto fail; + } + case STRTOSZ_DEFSUFFIX_MB: + mul = 1ULL << 20; + break; + case STRTOSZ_DEFSUFFIX_GB: + mul = 1ULL << 30; + break; + case STRTOSZ_DEFSUFFIX_TB: + mul = 1ULL << 40; + break; + default: + goto fail; + } + /* + * If not terminated by whitespace, ',', or \0, increment endptr + * to point to next character, then check that we are terminated + * by an appropriate separating character, ie. whitespace, ',', or + * \0. If not, we are seeing trailing garbage, thus fail. + */ + if (c != 0) { + endptr++; + if (!qemu_isspace(*endptr) && *endptr != ',' && *endptr != 0) { + goto fail; + } + } + if ((val * mul >= INT64_MAX) || val < 0) { + goto fail; + } + retval = val * mul; + +fail: + if (end) { + *end = endptr; + } + + return retval; +} + +int64_t strtosz(const char *nptr, char **end) +{ + return strtosz_suffix(nptr, end, STRTOSZ_DEFSUFFIX_MB); +} diff -Nru qemu-kvm-0.12.5+noroms/d3des.c qemu-kvm-0.14.1/d3des.c --- qemu-kvm-0.12.5+noroms/d3des.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/d3des.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,424 +0,0 @@ -/* - * This is D3DES (V5.09) by Richard Outerbridge with the double and - * triple-length support removed for use in VNC. Also the bytebit[] array - * has been reversed so that the most significant bit in each byte of the - * key is ignored, not the least significant. - * - * These changes are: - * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. - * - * This software 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. - */ - -/* D3DES (V5.09) - - * - * A portable, public domain, version of the Data Encryption Standard. - * - * Written with Symantec's THINK (Lightspeed) C by Richard Outerbridge. - * Thanks to: Dan Hoey for his excellent Initial and Inverse permutation - * code; Jim Gillogly & Phil Karn for the DES key schedule code; Dennis - * Ferguson, Eric Young and Dana How for comparing notes; and Ray Lau, - * for humouring me on. - * - * Copyright (c) 1988,1989,1990,1991,1992 by Richard Outerbridge. - * (GEnie : OUTER; CIS : [71755,204]) Graven Imagery, 1992. - */ - -#include "d3des.h" - -static void scrunch(unsigned char *, unsigned long *); -static void unscrun(unsigned long *, unsigned char *); -static void desfunc(unsigned long *, unsigned long *); -static void cookey(unsigned long *); - -static unsigned long KnL[32] = { 0L }; - -static const unsigned short bytebit[8] = { - 01, 02, 04, 010, 020, 040, 0100, 0200 }; - -static const unsigned long bigbyte[24] = { - 0x800000L, 0x400000L, 0x200000L, 0x100000L, - 0x80000L, 0x40000L, 0x20000L, 0x10000L, - 0x8000L, 0x4000L, 0x2000L, 0x1000L, - 0x800L, 0x400L, 0x200L, 0x100L, - 0x80L, 0x40L, 0x20L, 0x10L, - 0x8L, 0x4L, 0x2L, 0x1L }; - -/* Use the key schedule specified in the Standard (ANSI X3.92-1981). */ - -static const unsigned char pc1[56] = { - 56, 48, 40, 32, 24, 16, 8, 0, 57, 49, 41, 33, 25, 17, - 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35, - 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, - 13, 5, 60, 52, 44, 36, 28, 20, 12, 4, 27, 19, 11, 3 }; - -static const unsigned char totrot[16] = { - 1,2,4,6,8,10,12,14,15,17,19,21,23,25,27,28 }; - -static const unsigned char pc2[48] = { - 13, 16, 10, 23, 0, 4, 2, 27, 14, 5, 20, 9, - 22, 18, 11, 3, 25, 7, 15, 6, 26, 19, 12, 1, - 40, 51, 30, 36, 46, 54, 29, 39, 50, 44, 32, 47, - 43, 48, 38, 55, 33, 52, 45, 41, 49, 35, 28, 31 }; - -/* Thanks to James Gillogly & Phil Karn! */ -void deskey(unsigned char *key, int edf) -{ - register int i, j, l, m, n; - unsigned char pc1m[56], pcr[56]; - unsigned long kn[32]; - - for ( j = 0; j < 56; j++ ) { - l = pc1[j]; - m = l & 07; - pc1m[j] = (key[l >> 3] & bytebit[m]) ? 1 : 0; - } - for( i = 0; i < 16; i++ ) { - if( edf == DE1 ) m = (15 - i) << 1; - else m = i << 1; - n = m + 1; - kn[m] = kn[n] = 0L; - for( j = 0; j < 28; j++ ) { - l = j + totrot[i]; - if( l < 28 ) pcr[j] = pc1m[l]; - else pcr[j] = pc1m[l - 28]; - } - for( j = 28; j < 56; j++ ) { - l = j + totrot[i]; - if( l < 56 ) pcr[j] = pc1m[l]; - else pcr[j] = pc1m[l - 28]; - } - for( j = 0; j < 24; j++ ) { - if( pcr[pc2[j]] ) kn[m] |= bigbyte[j]; - if( pcr[pc2[j+24]] ) kn[n] |= bigbyte[j]; - } - } - cookey(kn); - return; - } - -static void cookey(register unsigned long *raw1) -{ - register unsigned long *cook, *raw0; - unsigned long dough[32]; - register int i; - - cook = dough; - for( i = 0; i < 16; i++, raw1++ ) { - raw0 = raw1++; - *cook = (*raw0 & 0x00fc0000L) << 6; - *cook |= (*raw0 & 0x00000fc0L) << 10; - *cook |= (*raw1 & 0x00fc0000L) >> 10; - *cook++ |= (*raw1 & 0x00000fc0L) >> 6; - *cook = (*raw0 & 0x0003f000L) << 12; - *cook |= (*raw0 & 0x0000003fL) << 16; - *cook |= (*raw1 & 0x0003f000L) >> 4; - *cook++ |= (*raw1 & 0x0000003fL); - } - usekey(dough); - return; - } - -void cpkey(register unsigned long *into) -{ - register unsigned long *from, *endp; - - from = KnL, endp = &KnL[32]; - while( from < endp ) *into++ = *from++; - return; - } - -void usekey(register unsigned long *from) -{ - register unsigned long *to, *endp; - - to = KnL, endp = &KnL[32]; - while( to < endp ) *to++ = *from++; - return; - } - -void des(unsigned char *inblock, unsigned char *outblock) -{ - unsigned long work[2]; - - scrunch(inblock, work); - desfunc(work, KnL); - unscrun(work, outblock); - return; - } - -static void scrunch(register unsigned char *outof, register unsigned long *into) -{ - *into = (*outof++ & 0xffL) << 24; - *into |= (*outof++ & 0xffL) << 16; - *into |= (*outof++ & 0xffL) << 8; - *into++ |= (*outof++ & 0xffL); - *into = (*outof++ & 0xffL) << 24; - *into |= (*outof++ & 0xffL) << 16; - *into |= (*outof++ & 0xffL) << 8; - *into |= (*outof & 0xffL); - return; - } - -static void unscrun(register unsigned long *outof, register unsigned char *into) -{ - *into++ = (unsigned char)((*outof >> 24) & 0xffL); - *into++ = (unsigned char)((*outof >> 16) & 0xffL); - *into++ = (unsigned char)((*outof >> 8) & 0xffL); - *into++ = (unsigned char)(*outof++ & 0xffL); - *into++ = (unsigned char)((*outof >> 24) & 0xffL); - *into++ = (unsigned char)((*outof >> 16) & 0xffL); - *into++ = (unsigned char)((*outof >> 8) & 0xffL); - *into = (unsigned char)(*outof & 0xffL); - return; - } - -static const unsigned long SP1[64] = { - 0x01010400L, 0x00000000L, 0x00010000L, 0x01010404L, - 0x01010004L, 0x00010404L, 0x00000004L, 0x00010000L, - 0x00000400L, 0x01010400L, 0x01010404L, 0x00000400L, - 0x01000404L, 0x01010004L, 0x01000000L, 0x00000004L, - 0x00000404L, 0x01000400L, 0x01000400L, 0x00010400L, - 0x00010400L, 0x01010000L, 0x01010000L, 0x01000404L, - 0x00010004L, 0x01000004L, 0x01000004L, 0x00010004L, - 0x00000000L, 0x00000404L, 0x00010404L, 0x01000000L, - 0x00010000L, 0x01010404L, 0x00000004L, 0x01010000L, - 0x01010400L, 0x01000000L, 0x01000000L, 0x00000400L, - 0x01010004L, 0x00010000L, 0x00010400L, 0x01000004L, - 0x00000400L, 0x00000004L, 0x01000404L, 0x00010404L, - 0x01010404L, 0x00010004L, 0x01010000L, 0x01000404L, - 0x01000004L, 0x00000404L, 0x00010404L, 0x01010400L, - 0x00000404L, 0x01000400L, 0x01000400L, 0x00000000L, - 0x00010004L, 0x00010400L, 0x00000000L, 0x01010004L }; - -static const unsigned long SP2[64] = { - 0x80108020L, 0x80008000L, 0x00008000L, 0x00108020L, - 0x00100000L, 0x00000020L, 0x80100020L, 0x80008020L, - 0x80000020L, 0x80108020L, 0x80108000L, 0x80000000L, - 0x80008000L, 0x00100000L, 0x00000020L, 0x80100020L, - 0x00108000L, 0x00100020L, 0x80008020L, 0x00000000L, - 0x80000000L, 0x00008000L, 0x00108020L, 0x80100000L, - 0x00100020L, 0x80000020L, 0x00000000L, 0x00108000L, - 0x00008020L, 0x80108000L, 0x80100000L, 0x00008020L, - 0x00000000L, 0x00108020L, 0x80100020L, 0x00100000L, - 0x80008020L, 0x80100000L, 0x80108000L, 0x00008000L, - 0x80100000L, 0x80008000L, 0x00000020L, 0x80108020L, - 0x00108020L, 0x00000020L, 0x00008000L, 0x80000000L, - 0x00008020L, 0x80108000L, 0x00100000L, 0x80000020L, - 0x00100020L, 0x80008020L, 0x80000020L, 0x00100020L, - 0x00108000L, 0x00000000L, 0x80008000L, 0x00008020L, - 0x80000000L, 0x80100020L, 0x80108020L, 0x00108000L }; - -static const unsigned long SP3[64] = { - 0x00000208L, 0x08020200L, 0x00000000L, 0x08020008L, - 0x08000200L, 0x00000000L, 0x00020208L, 0x08000200L, - 0x00020008L, 0x08000008L, 0x08000008L, 0x00020000L, - 0x08020208L, 0x00020008L, 0x08020000L, 0x00000208L, - 0x08000000L, 0x00000008L, 0x08020200L, 0x00000200L, - 0x00020200L, 0x08020000L, 0x08020008L, 0x00020208L, - 0x08000208L, 0x00020200L, 0x00020000L, 0x08000208L, - 0x00000008L, 0x08020208L, 0x00000200L, 0x08000000L, - 0x08020200L, 0x08000000L, 0x00020008L, 0x00000208L, - 0x00020000L, 0x08020200L, 0x08000200L, 0x00000000L, - 0x00000200L, 0x00020008L, 0x08020208L, 0x08000200L, - 0x08000008L, 0x00000200L, 0x00000000L, 0x08020008L, - 0x08000208L, 0x00020000L, 0x08000000L, 0x08020208L, - 0x00000008L, 0x00020208L, 0x00020200L, 0x08000008L, - 0x08020000L, 0x08000208L, 0x00000208L, 0x08020000L, - 0x00020208L, 0x00000008L, 0x08020008L, 0x00020200L }; - -static const unsigned long SP4[64] = { - 0x00802001L, 0x00002081L, 0x00002081L, 0x00000080L, - 0x00802080L, 0x00800081L, 0x00800001L, 0x00002001L, - 0x00000000L, 0x00802000L, 0x00802000L, 0x00802081L, - 0x00000081L, 0x00000000L, 0x00800080L, 0x00800001L, - 0x00000001L, 0x00002000L, 0x00800000L, 0x00802001L, - 0x00000080L, 0x00800000L, 0x00002001L, 0x00002080L, - 0x00800081L, 0x00000001L, 0x00002080L, 0x00800080L, - 0x00002000L, 0x00802080L, 0x00802081L, 0x00000081L, - 0x00800080L, 0x00800001L, 0x00802000L, 0x00802081L, - 0x00000081L, 0x00000000L, 0x00000000L, 0x00802000L, - 0x00002080L, 0x00800080L, 0x00800081L, 0x00000001L, - 0x00802001L, 0x00002081L, 0x00002081L, 0x00000080L, - 0x00802081L, 0x00000081L, 0x00000001L, 0x00002000L, - 0x00800001L, 0x00002001L, 0x00802080L, 0x00800081L, - 0x00002001L, 0x00002080L, 0x00800000L, 0x00802001L, - 0x00000080L, 0x00800000L, 0x00002000L, 0x00802080L }; - -static const unsigned long SP5[64] = { - 0x00000100L, 0x02080100L, 0x02080000L, 0x42000100L, - 0x00080000L, 0x00000100L, 0x40000000L, 0x02080000L, - 0x40080100L, 0x00080000L, 0x02000100L, 0x40080100L, - 0x42000100L, 0x42080000L, 0x00080100L, 0x40000000L, - 0x02000000L, 0x40080000L, 0x40080000L, 0x00000000L, - 0x40000100L, 0x42080100L, 0x42080100L, 0x02000100L, - 0x42080000L, 0x40000100L, 0x00000000L, 0x42000000L, - 0x02080100L, 0x02000000L, 0x42000000L, 0x00080100L, - 0x00080000L, 0x42000100L, 0x00000100L, 0x02000000L, - 0x40000000L, 0x02080000L, 0x42000100L, 0x40080100L, - 0x02000100L, 0x40000000L, 0x42080000L, 0x02080100L, - 0x40080100L, 0x00000100L, 0x02000000L, 0x42080000L, - 0x42080100L, 0x00080100L, 0x42000000L, 0x42080100L, - 0x02080000L, 0x00000000L, 0x40080000L, 0x42000000L, - 0x00080100L, 0x02000100L, 0x40000100L, 0x00080000L, - 0x00000000L, 0x40080000L, 0x02080100L, 0x40000100L }; - -static const unsigned long SP6[64] = { - 0x20000010L, 0x20400000L, 0x00004000L, 0x20404010L, - 0x20400000L, 0x00000010L, 0x20404010L, 0x00400000L, - 0x20004000L, 0x00404010L, 0x00400000L, 0x20000010L, - 0x00400010L, 0x20004000L, 0x20000000L, 0x00004010L, - 0x00000000L, 0x00400010L, 0x20004010L, 0x00004000L, - 0x00404000L, 0x20004010L, 0x00000010L, 0x20400010L, - 0x20400010L, 0x00000000L, 0x00404010L, 0x20404000L, - 0x00004010L, 0x00404000L, 0x20404000L, 0x20000000L, - 0x20004000L, 0x00000010L, 0x20400010L, 0x00404000L, - 0x20404010L, 0x00400000L, 0x00004010L, 0x20000010L, - 0x00400000L, 0x20004000L, 0x20000000L, 0x00004010L, - 0x20000010L, 0x20404010L, 0x00404000L, 0x20400000L, - 0x00404010L, 0x20404000L, 0x00000000L, 0x20400010L, - 0x00000010L, 0x00004000L, 0x20400000L, 0x00404010L, - 0x00004000L, 0x00400010L, 0x20004010L, 0x00000000L, - 0x20404000L, 0x20000000L, 0x00400010L, 0x20004010L }; - -static const unsigned long SP7[64] = { - 0x00200000L, 0x04200002L, 0x04000802L, 0x00000000L, - 0x00000800L, 0x04000802L, 0x00200802L, 0x04200800L, - 0x04200802L, 0x00200000L, 0x00000000L, 0x04000002L, - 0x00000002L, 0x04000000L, 0x04200002L, 0x00000802L, - 0x04000800L, 0x00200802L, 0x00200002L, 0x04000800L, - 0x04000002L, 0x04200000L, 0x04200800L, 0x00200002L, - 0x04200000L, 0x00000800L, 0x00000802L, 0x04200802L, - 0x00200800L, 0x00000002L, 0x04000000L, 0x00200800L, - 0x04000000L, 0x00200800L, 0x00200000L, 0x04000802L, - 0x04000802L, 0x04200002L, 0x04200002L, 0x00000002L, - 0x00200002L, 0x04000000L, 0x04000800L, 0x00200000L, - 0x04200800L, 0x00000802L, 0x00200802L, 0x04200800L, - 0x00000802L, 0x04000002L, 0x04200802L, 0x04200000L, - 0x00200800L, 0x00000000L, 0x00000002L, 0x04200802L, - 0x00000000L, 0x00200802L, 0x04200000L, 0x00000800L, - 0x04000002L, 0x04000800L, 0x00000800L, 0x00200002L }; - -static const unsigned long SP8[64] = { - 0x10001040L, 0x00001000L, 0x00040000L, 0x10041040L, - 0x10000000L, 0x10001040L, 0x00000040L, 0x10000000L, - 0x00040040L, 0x10040000L, 0x10041040L, 0x00041000L, - 0x10041000L, 0x00041040L, 0x00001000L, 0x00000040L, - 0x10040000L, 0x10000040L, 0x10001000L, 0x00001040L, - 0x00041000L, 0x00040040L, 0x10040040L, 0x10041000L, - 0x00001040L, 0x00000000L, 0x00000000L, 0x10040040L, - 0x10000040L, 0x10001000L, 0x00041040L, 0x00040000L, - 0x00041040L, 0x00040000L, 0x10041000L, 0x00001000L, - 0x00000040L, 0x10040040L, 0x00001000L, 0x00041040L, - 0x10001000L, 0x00000040L, 0x10000040L, 0x10040000L, - 0x10040040L, 0x10000000L, 0x00040000L, 0x10001040L, - 0x00000000L, 0x10041040L, 0x00040040L, 0x10000040L, - 0x10040000L, 0x10001000L, 0x10001040L, 0x00000000L, - 0x10041040L, 0x00041000L, 0x00041000L, 0x00001040L, - 0x00001040L, 0x00040040L, 0x10000000L, 0x10041000L }; - -static void desfunc(register unsigned long *block, register unsigned long *keys) -{ - register unsigned long fval, work, right, leftt; - register int round; - - leftt = block[0]; - right = block[1]; - work = ((leftt >> 4) ^ right) & 0x0f0f0f0fL; - right ^= work; - leftt ^= (work << 4); - work = ((leftt >> 16) ^ right) & 0x0000ffffL; - right ^= work; - leftt ^= (work << 16); - work = ((right >> 2) ^ leftt) & 0x33333333L; - leftt ^= work; - right ^= (work << 2); - work = ((right >> 8) ^ leftt) & 0x00ff00ffL; - leftt ^= work; - right ^= (work << 8); - right = ((right << 1) | ((right >> 31) & 1L)) & 0xffffffffL; - work = (leftt ^ right) & 0xaaaaaaaaL; - leftt ^= work; - right ^= work; - leftt = ((leftt << 1) | ((leftt >> 31) & 1L)) & 0xffffffffL; - - for( round = 0; round < 8; round++ ) { - work = (right << 28) | (right >> 4); - work ^= *keys++; - fval = SP7[ work & 0x3fL]; - fval |= SP5[(work >> 8) & 0x3fL]; - fval |= SP3[(work >> 16) & 0x3fL]; - fval |= SP1[(work >> 24) & 0x3fL]; - work = right ^ *keys++; - fval |= SP8[ work & 0x3fL]; - fval |= SP6[(work >> 8) & 0x3fL]; - fval |= SP4[(work >> 16) & 0x3fL]; - fval |= SP2[(work >> 24) & 0x3fL]; - leftt ^= fval; - work = (leftt << 28) | (leftt >> 4); - work ^= *keys++; - fval = SP7[ work & 0x3fL]; - fval |= SP5[(work >> 8) & 0x3fL]; - fval |= SP3[(work >> 16) & 0x3fL]; - fval |= SP1[(work >> 24) & 0x3fL]; - work = leftt ^ *keys++; - fval |= SP8[ work & 0x3fL]; - fval |= SP6[(work >> 8) & 0x3fL]; - fval |= SP4[(work >> 16) & 0x3fL]; - fval |= SP2[(work >> 24) & 0x3fL]; - right ^= fval; - } - - right = (right << 31) | (right >> 1); - work = (leftt ^ right) & 0xaaaaaaaaL; - leftt ^= work; - right ^= work; - leftt = (leftt << 31) | (leftt >> 1); - work = ((leftt >> 8) ^ right) & 0x00ff00ffL; - right ^= work; - leftt ^= (work << 8); - work = ((leftt >> 2) ^ right) & 0x33333333L; - right ^= work; - leftt ^= (work << 2); - work = ((right >> 16) ^ leftt) & 0x0000ffffL; - leftt ^= work; - right ^= (work << 16); - work = ((right >> 4) ^ leftt) & 0x0f0f0f0fL; - leftt ^= work; - right ^= (work << 4); - *block++ = right; - *block = leftt; - return; - } - -/* Validation sets: - * - * Single-length key, single-length plaintext - - * Key : 0123 4567 89ab cdef - * Plain : 0123 4567 89ab cde7 - * Cipher : c957 4425 6a5e d31d - * - * Double-length key, single-length plaintext - - * Key : 0123 4567 89ab cdef fedc ba98 7654 3210 - * Plain : 0123 4567 89ab cde7 - * Cipher : 7f1d 0a77 826b 8aff - * - * Double-length key, double-length plaintext - - * Key : 0123 4567 89ab cdef fedc ba98 7654 3210 - * Plain : 0123 4567 89ab cdef 0123 4567 89ab cdff - * Cipher : 27a0 8440 406a df60 278f 47cf 42d6 15d7 - * - * Triple-length key, single-length plaintext - - * Key : 0123 4567 89ab cdef fedc ba98 7654 3210 89ab cdef 0123 4567 - * Plain : 0123 4567 89ab cde7 - * Cipher : de0b 7c06 ae5e 0ed5 - * - * Triple-length key, double-length plaintext - - * Key : 0123 4567 89ab cdef fedc ba98 7654 3210 89ab cdef 0123 4567 - * Plain : 0123 4567 89ab cdef 0123 4567 89ab cdff - * Cipher : ad0d 1b30 ac17 cf07 0ed1 1c63 81e4 4de5 - * - * d3des V5.0a rwo 9208.07 18:44 Graven Imagery - **********************************************************************/ diff -Nru qemu-kvm-0.12.5+noroms/d3des.h qemu-kvm-0.14.1/d3des.h --- qemu-kvm-0.12.5+noroms/d3des.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/d3des.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,51 +0,0 @@ -/* - * This is D3DES (V5.09) by Richard Outerbridge with the double and - * triple-length support removed for use in VNC. - * - * These changes are: - * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. - * - * This software 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. - */ - -/* d3des.h - - * - * Headers and defines for d3des.c - * Graven Imagery, 1992. - * - * Copyright (c) 1988,1989,1990,1991,1992 by Richard Outerbridge - * (GEnie : OUTER; CIS : [71755,204]) - */ - -#define EN0 0 /* MODE == encrypt */ -#define DE1 1 /* MODE == decrypt */ - -extern void deskey(unsigned char *, int); -/* hexkey[8] MODE - * Sets the internal key register according to the hexadecimal - * key contained in the 8 bytes of hexkey, according to the DES, - * for encryption or decryption according to MODE. - */ - -extern void usekey(unsigned long *); -/* cookedkey[32] - * Loads the internal key register with the data in cookedkey. - */ - -extern void cpkey(unsigned long *); -/* cookedkey[32] - * Copies the contents of the internal key register into the storage - * located at &cookedkey[0]. - */ - -extern void des(unsigned char *, unsigned char *); -/* from[8] to[8] - * Encrypts/Decrypts (according to the key currently loaded in the - * internal key register) one block of eight bytes at address 'from' - * into the block at address 'to'. They can be the same. - */ - -/* d3des.h V5.09 rwo 9208.04 15:06 Graven Imagery - ********************************************************************/ diff -Nru qemu-kvm-0.12.5+noroms/darwin-user/commpage.c qemu-kvm-0.14.1/darwin-user/commpage.c --- qemu-kvm-0.12.5+noroms/darwin-user/commpage.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/darwin-user/commpage.c 2011-05-11 13:29:46.000000000 +0000 @@ -237,7 +237,7 @@ uint64_t *value = (uint64_t*)((CPUX86State*)cpu_env)->regs[R_ESI]; old = (uint64_t)((uint64_t)((CPUX86State*)cpu_env)->regs[R_EDX]) << 32 | (uint64_t)((CPUX86State*)cpu_env)->regs[R_EAX]; - DPRINTF("commpage: compare_and_swap64(%llx,new,%p)\n", old, value); + DPRINTF("commpage: compare_and_swap64(%" PRIx64 ",new,%p)\n", old, value); swapped_val = tswap64(*value); if(old == swapped_val) diff -Nru qemu-kvm-0.12.5+noroms/darwin-user/machload.c qemu-kvm-0.14.1/darwin-user/machload.c --- qemu-kvm-0.12.5+noroms/darwin-user/machload.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/darwin-user/machload.c 2011-05-11 13:29:46.000000000 +0000 @@ -82,7 +82,7 @@ int macho_offset = 0; int load_object(const char *filename, struct target_pt_regs * regs, void ** mh); -void qerror(const char *format, ...); + #ifdef TARGET_I386 typedef struct mach_i386_thread_state { unsigned int eax; diff -Nru qemu-kvm-0.12.5+noroms/darwin-user/main.c qemu-kvm-0.14.1/darwin-user/main.c --- qemu-kvm-0.12.5+noroms/darwin-user/main.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/darwin-user/main.c 2011-05-11 13:29:46.000000000 +0000 @@ -82,9 +82,9 @@ return 0; } -uint32_t cpu_ppc_load_tbl (CPUState *env) +uint64_t cpu_ppc_load_tbl (CPUState *env) { - return cpu_ppc_get_tb(env) & 0xFFFFFFFF; + return cpu_ppc_get_tb(env); } uint32_t cpu_ppc_load_tbu (CPUState *env) @@ -92,9 +92,9 @@ return cpu_ppc_get_tb(env) >> 32; } -uint32_t cpu_ppc_load_atbl (CPUState *env) +uint64_t cpu_ppc_load_atbl (CPUState *env) { - return cpu_ppc_get_tb(env) & 0xFFFFFFFF; + return cpu_ppc_get_tb(env); } uint32_t cpu_ppc_load_atbu (CPUState *env) @@ -113,12 +113,12 @@ } /* XXX: to be fixed */ -int ppc_dcr_read (ppc_dcr_t *dcr_env, int dcrn, target_ulong *valp) +int ppc_dcr_read (ppc_dcr_t *dcr_env, int dcrn, uint32_t *valp) { return -1; } -int ppc_dcr_write (ppc_dcr_t *dcr_env, int dcrn, target_ulong val) +int ppc_dcr_write (ppc_dcr_t *dcr_env, int dcrn, uint32_t val) { return -1; } @@ -704,7 +704,7 @@ } #endif -void usage(void) +static void usage(void) { printf("qemu-" TARGET_ARCH " version " QEMU_VERSION ", Copyright (c) 2003-2004 Fabrice Bellard\n" "usage: qemu-" TARGET_ARCH " [-h] [-d opts] [-L path] [-s size] program [arguments...]\n" diff -Nru qemu-kvm-0.12.5+noroms/darwin-user/mmap.c qemu-kvm-0.14.1/darwin-user/mmap.c --- qemu-kvm-0.12.5+noroms/darwin-user/mmap.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/darwin-user/mmap.c 2011-05-11 13:29:46.000000000 +0000 @@ -129,7 +129,7 @@ if ((flags & MAP_SHARED) && #endif (prot & PROT_WRITE)) - return -EINVAL; + return -1; /* adjust protection to be able to read */ if (!(prot1 & PROT_WRITE)) diff -Nru qemu-kvm-0.12.5+noroms/darwin-user/qemu.h qemu-kvm-0.14.1/darwin-user/qemu.h --- qemu-kvm-0.12.5+noroms/darwin-user/qemu.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/darwin-user/qemu.h 2011-05-11 13:29:46.000000000 +0000 @@ -99,8 +99,8 @@ struct sigaction *oact); int do_sigaltstack(const struct sigaltstack *ss, struct sigaltstack *oss); -void gemu_log(const char *fmt, ...) __attribute__((format(printf,1,2))); -void qerror(const char *fmt, ...); +void gemu_log(const char *fmt, ...) GCC_FMT_ATTR(1, 2); +void qerror(const char *fmt, ...) GCC_FMT_ATTR(1, 2); void write_dt(void *ptr, unsigned long addr, unsigned long limit, int flags); diff -Nru qemu-kvm-0.12.5+noroms/darwin-user/syscall.c qemu-kvm-0.14.1/darwin-user/syscall.c --- qemu-kvm-0.12.5+noroms/darwin-user/syscall.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/darwin-user/syscall.c 2011-05-11 13:29:46.000000000 +0000 @@ -858,7 +858,7 @@ long do_pread(uint32_t arg1, void * arg2, size_t arg3, off_t arg4) { - DPRINTF("0x%x, %p, 0x%lx, 0x%llx\n", arg1, arg2, arg3, arg4); + DPRINTF("0x%x, %p, 0x%lx, 0x%" PRIx64 "\n", arg1, arg2, arg3, arg4); long ret = pread(arg1, arg2, arg3, arg4); return ret; } diff -Nru qemu-kvm-0.12.5+noroms/debian/changelog qemu-kvm-0.14.1/debian/changelog --- qemu-kvm-0.12.5+noroms/debian/changelog 2011-07-27 16:49:17.000000000 +0000 +++ qemu-kvm-0.14.1/debian/changelog 2011-07-27 16:49:18.000000000 +0000 @@ -1,17 +1,314 @@ -qemu-kvm (0.12.5+noroms-0ubuntu7.1~ppa1) lucid; urgency=low +qemu-kvm (0.14.1-0ubuntu1.10.04.1) lucid; urgency=low - * Backport from maverick to lucid. + * New upstream release + * Removed patch applied upstream: debian/patches/CVE-2011-1750.diff + + -- Serge Hallyn Tue, 26 Jul 2011 23:06:23 -0500 + +qemu-kvm (0.14.0+noroms-0ubuntu9) oneiric; urgency=low + + * SECURITY UPDATE: fix potential privilege escalation via improper group + handling + - debian/patches/CVE-2011-2527.patch: call initgroups() to drop + supplementary group privileges + - CVE-2011-2527 + + -- Jamie Strandboge Tue, 26 Jul 2011 07:51:28 -0500 + +qemu-kvm (0.14.0+noroms-0ubuntu8) oneiric; urgency=low + + * SECURITY UPDATE: fix to validate virtqueue in and out requests from the + guests + - debian/patches/CVE-2011-2212-virtqueue-indirect-overflow.patch: update + hw/virtio.c to verify the length of indirect descriptors in + virtqueue_pop() and virtqueue_avail_bytes() + - CVE-2011-2212 + - LP: #806167 + * SECURITY UPDATE: validate virtio_queue_notify() is non-negative + - virtio-guard-against-negative-vq-notifies-CVE-2011-2512.diff: update + to move comparison out to syborg_virtio_writel(), virtio_ioport_write() + and virtio_queue_notify_vq() and don't call common virtio code if + virtqueue number is invalid. Patch from Debian. + - CVE-2011-2512 + - LP: #806166 + + -- Jamie Strandboge Tue, 05 Jul 2011 13:24:52 -0500 + +qemu-kvm (0.14.0+noroms-0ubuntu7) oneiric; urgency=low + + * SECURITY UPDATE: fix heap buffer overflow from unaligned requests + - CVE-2011-1750 + * SECURITY UPDATE: verify no_hotplug attribute when handling hot-unplug + requests + - CVE-2011-1751 + + -- Jamie Strandboge Sun, 29 May 2011 09:22:55 -0500 + +qemu-kvm (0.14.0+noroms-0ubuntu6) oneiric; urgency=low + + * We need a versioned depend on vgabios to ensure the files we link to + exist. (LP: #783864) + + -- Serge Hallyn Tue, 24 May 2011 10:09:01 -0500 + +qemu-kvm (0.14.0+noroms-0ubuntu5) oneiric; urgency=low + + * Add libattr1-dev to build-depends to enable use of 9p virtfs (LP: #782973) + + -- Serge Hallyn Mon, 16 May 2011 09:53:15 -0500 + +qemu-kvm (0.14.0+noroms-0ubuntu4) natty; urgency=low + + *LP: #719174 + Typo corrected + + -- Bhaveek Desai Fri, 18 Mar 2011 21:06:54 +0530 - -- Serge Hallyn Thu, 16 Dec 2010 18:32:50 +0000 +qemu-kvm (0.14.0+noroms-0ubuntu3) natty; urgency=low -qemu-kvm (0.12.5+noroms-0ubuntu7.1) maverick-proposed; urgency=low + * debian/qemu-common.links: symlink all of the vgabios bin files into + the qemu expected paths, LP: #736351 + + -- Dustin Kirkland Wed, 16 Mar 2011 21:20:14 -0500 + +qemu-kvm (0.14.0+noroms-0ubuntu2) natty; urgency=low + + * debian/qemu-kvm.default: per popular request, reduce the ksm sleep + milliseconds to 200ms, LP: #578930 + + -- Dustin Kirkland Wed, 09 Mar 2011 11:21:40 +0000 + +qemu-kvm (0.14.0+noroms-0ubuntu1) natty; urgency=low + + * Merge qemu-kvm 0.14.0 + * debian/rules: get DEB_HOST_ARCH from dpkg-architecture. Otherwise + it is empty, resulting in kvm being not enabled + * debian/rules: re-enable parallel builds + + -- Serge Hallyn Tue, 08 Mar 2011 10:10:49 -0600 + +qemu-kvm (0.14.0~rc1+noroms-0ubuntu4) natty; urgency=low + + * Apply spice-qxl-locking-fix-for-qemu-kvm.patch to fix bug with -qxl. + (LP: #723871) + + -- Serge Hallyn Tue, 01 Mar 2011 11:12:44 -0600 + +qemu-kvm (0.14.0~rc1+noroms-0ubuntu3) natty; urgency=low + + * control: fix description of dummy qemu and kvm packages to mention + "qemu-kvm" and not "kvm-qemu". + * control: add armhf in the list of architectures along armel. + * rules: test whether DEB_HOST_ARCH_CPU is arm instead of testing whether + DEB_HOST_ARCH is arm or armel; this fixes support for armhf. + + -- Loïc Minier Wed, 16 Feb 2011 09:58:03 +0100 + +qemu-kvm (0.14.0~rc1+noroms-0ubuntu2) natty; urgency=low + + * debian/rules: place --fno-var-tracking back into CFLAGS for arm, so + as to prevent out of memory. (LP: #693341) + + -- Serge Hallyn Tue, 15 Feb 2011 20:37:56 -0600 + +qemu-kvm (0.14.0~rc1+noroms-0ubuntu1) natty; urgency=low + + [ Serge Hallyn ] + * Merge qemu-kvm 0.14.0-rc1 + * removed all rom's + * removed tests/pi_10.com as it's binary data + * removed 697197-fix-vnc-password-semantics.patch in favor of upstream fix + * removed caps-lock-key-up-event.patch - upstream commit + 9a121a2fbf88dd1bc869b1ac2449dc12c27cccfa is supposed to fix it + - bdrung to verify + * removed 1000-undo-earlier-static.patch + - re-add if build fails - but we no longer do static build + * removed 2000-vmmouse-adapt-to-mouse-handler-changes.patch, now upstream + * removed arm patches - qemu-kvm now only offers x86 and ppc emulation + - qemu-user provides armel + * kvmtrace_format is now shipped with different tree. + + [ Dustin Kirkland ] + * Re-roll tarball, adding ~rc1 to version, so that the official GA release + will supercede these rc's + * debian/control: bump standards versions, remove redundant depends on + adduser, update section to misc + * debian/rules: drop qemu-system-ppc64 from the build, as this is in + qemu-linaro now, LP: #717690 + * debian/copyright: embed the BSD license, per lintian + + [ Steve Langasek ] + * debian/rules: drop the binfmt-misc handling; we're not building any + static user binaries from this source, so this is just noise. + * debian/rules: $INSTALL_PROGRAM is never set, so modifying it is + pointless. Delete this as well. + * build qemu-kvm package on all archs; this is the authoritative package + for x86 system emulators, so we only have to deal with the bios + dependencies in one place. + * don't run 'make install' in kvm/libkvm directory, this is a no-op anyway. + + -- Serge Hallyn Mon, 14 Feb 2011 07:50:16 -0600 + +qemu-kvm (0.13.0+noroms-0ubuntu15) natty; urgency=low + + [ Dustin Kirkland ] + * debian/binfmts/qemu-alpha, debian/binfmts/qemu-arm, + debian/binfmts/qemu-armeb, debian/binfmts/qemu-cris, + debian/binfmts/qemu-i386, debian/binfmts/qemu-m68k, + debian/binfmts/qemu-microblaze, debian/binfmts/qemu-mips, + debian/binfmts/qemu-mipsel, debian/binfmts/qemu-ppc, + debian/binfmts/qemu-ppc64, debian/binfmts/qemu-ppc64abi32, + debian/binfmts/qemu-sh4, debian/binfmts/qemu-sh4eb, + debian/binfmts/qemu-sparc, debian/binfmts/qemu-sparc32plus, + debian/binfmts/qemu-sparc64, debian/binfmts/qemu-x86_64, + debian/control, debian/qemu-arm-static.postinst, debian/qemu-kvm- + extras.dirs, debian/qemu-kvm-extras.links, debian/qemu-kvm-extras- + static.dirs, debian/qemu-kvm-extras-static.postinst, debian/qemu- + kvm-extras-static.postrm, debian/qemu-kvm-extras-static.preinst, + debian/qemu-kvm-extras-static.prerm, debian/qemu-kvm-extras- + static.sysctl, debian/qemu-kvm-extras-static.sysctl.amd64, + debian/rules, === removed directory debian/binfmts: + - massive simplification of the qemu-kvm build, now that qemu-linaro + provides all of the system emulation for non-accelerated architectures, + ie, those other than [i386, amd64, powerpc] + - specify the i386, amd64, ppc64 targets + + [ Steve Langasek ] + * Rename qemu-user manpage to qemu-kvm so that qemu-linaro can take over + the more generic name. + + -- Dustin Kirkland Fri, 11 Feb 2011 15:07:04 -0600 + +qemu-kvm (0.13.0+noroms-0ubuntu14) natty; urgency=low + + * Pass -fno-var-tracking on armel to hopefully reduce memory consumption. + (LP: #693341) + + -- Serge Hallyn Tue, 08 Feb 2011 14:41:39 -0600 + +qemu-kvm (0.13.0+noroms-0ubuntu13) natty; urgency=low + + [ Neil Wilson ] + * SECURITY UPDATE: Setting VNC password to empty string silently + disables all authentication (LP: #697197) + - debian/patches/697197-fix-vnc-password-semantics.patch: Reverses the + change introduced in Qemu by git commit 52c18be9 + - CVE: 2011-0011 + + [ Dustin Kirkland ] + * Updated patch to reflect the move of vnc.c to ui/vnc.c + + -- Dustin Kirkland Fri, 11 Feb 2011 09:53:19 -0600 + +qemu-kvm (0.13.0+noroms-0ubuntu12) natty; urgency=low + + * Move creation of group kvm to qemu-kvm.preinst from .postinst, so that + /dev/kvm can be created owned by group kvm. I also removed '|| true' + from the addgroup line, because if that fails, package install should + fail. (LP: #705509) + + -- Serge Hallyn Thu, 20 Jan 2011 11:11:03 -0600 + +qemu-kvm (0.13.0+noroms-0ubuntu11) natty; urgency=low + + * Specify version for the kvm Conflicts. Otherwise installing 'kvm' + fails since it depends on qemu-kvm, then conflicts with itself. + (LP: #701288) + + -- Serge Hallyn Mon, 10 Jan 2011 17:34:28 -0600 + +qemu-kvm (0.13.0+noroms-0ubuntu10) natty; urgency=low + + [ Serge Hallyn ] + * specify in configuration that we want documentation and add the + needed build-depends, so we get qemu.1 manpage. (LP: #675753) + + -- Dustin Kirkland Mon, 06 Dec 2010 19:36:07 -0600 + +qemu-kvm (0.13.0+noroms-0ubuntu9) natty; urgency=low + + [ Serge Hallyn ] + * debian/qemu-kvm.upstart: don't load the kernel modules if the package + has been removed but the upstart jobs still exists. (LP: #292588) + (Originally by Felix Geyer ) + + -- Dustin Kirkland Mon, 29 Nov 2010 13:47:03 -0600 + +qemu-kvm (0.13.0+noroms-0ubuntu8) natty; urgency=low * Add caps-lock-key-up-event.patch to enable normal up/down events for Caps-Lock and Num-Lock keys by setting SDL_DISABLE_LOCK_KEYS (which requires SDL > 1.2.14). This fixes handling of capslock when capslock is mapped to something else in host system. (LP: #427612) - -- Benjamin Drung Wed, 24 Nov 2010 15:35:10 +0100 + -- Benjamin Drung Wed, 24 Nov 2010 21:46:44 +0100 + +qemu-kvm (0.13.0+noroms-0ubuntu7) natty; urgency=low + + [ Colin Watson ] + * Remove "flags: OC" from binfmt files; update-binfmts happens to ignore + it, but it's out of spec for the file format. + + -- Loïc Minier Mon, 22 Nov 2010 16:34:15 +0100 + +qemu-kvm (0.13.0+noroms-0ubuntu6) natty; urgency=low + + * 0.13.0+noroms-0ubuntu5 accidentally reverted changes from + 0.13.0+noroms-0ubuntu4 and 0.13.0+noroms-0ubuntu3; revert this revert. + * debian/rules: filter static versions of qemu-linux-user binaries with + qemu-[a-z0-9_]+ when renaming them as otherwise we install qemu-malloc.d + and qemu-malloc.o. + * Add a comment in rules for the changes in 0.13.0+noroms-0ubuntu5. + + -- Loïc Minier Sun, 21 Nov 2010 18:24:15 +0100 + +qemu-kvm (0.13.0+noroms-0ubuntu5) natty; urgency=low + + * Don't allow parallel build of kvm, as it is rumored to be the + cause of error compilations, including 'qemu-host.h not found' + + -- Serge Hallyn Tue, 16 Nov 2010 17:45:13 -0600 + +qemu-kvm (0.13.0+noroms-0ubuntu4) natty; urgency=low + + * debian/patches/2000-vmmouse-adapt-to-mouse-handler-changes.patch, + debian/patches/series: apply changes from upstream to make mouse + work again, LP: #675749 + + -- Serge Hallyn Mon, 15 Nov 2010 21:34:37 -0600 + +qemu-kvm (0.13.0+noroms-0ubuntu3) natty; urgency=low + + * qemu-debootstrap: + - Allow for empty script argument; thanks Cody Somerville and + Michael Hudson. + - Fix bogus usage of log() instead of warn(); thanks Cody Somerville. + + -- Loïc Minier Thu, 11 Nov 2010 16:26:14 +0100 + +qemu-kvm (0.13.0+noroms-0ubuntu2) natty; urgency=low + + * debian/control: fix broken install/upgrades of kvm, LP: #673559, + versioned conflicts/replaces are not necessary + + -- Dustin Kirkland Wed, 10 Nov 2010 10:05:02 -0600 + +qemu-kvm (0.13.0+noroms-0ubuntu1) natty; urgency=low + + * New upstream release + * Remove patches which have been applied upstream: + * 05_improve-qemu-img-errors.patch + * arm-host-fix-compiler-warning.patch + * check-for-invalid-initrd-file.patch + * fix-CMOS-info-for-drives-defined-with--device.patch + * linux-user-do-not-warn-for-missing-pselect6.patch + * scm-rights-fd.patch + * Added a patch to revert a commit which caused configure + to test compilations with -static. This caused configure + to fail bc of unresolved dependencies in libsasl. + * Remove binary roms + + -- Serge Hallyn Wed, 20 Oct 2010 15:31:32 -0500 qemu-kvm (0.12.5+noroms-0ubuntu7) maverick; urgency=low diff -Nru qemu-kvm-0.12.5+noroms/debian/control qemu-kvm-0.14.1/debian/control --- qemu-kvm-0.12.5+noroms/debian/control 2011-07-27 16:49:17.000000000 +0000 +++ qemu-kvm-0.14.1/debian/control 2011-07-27 16:49:18.000000000 +0000 @@ -2,25 +2,46 @@ Section: misc Priority: optional Maintainer: Ubuntu Developers -Build-Depends: debhelper (>= 7), pkg-config, quilt (>= 0.40), - bzip2, uuid-dev, zlib1g-dev, libsdl1.2-dev (>= 1.2.14), libasound2-dev, libcurl4-gnutls-dev, libgnutls-dev, - libncurses5-dev, libpci-dev, libpulse-dev, libaio-dev, nasm, texi2html, bcc, iasl, - device-tree-compiler [powerpc], sysv-rc (>= 2.86.ds1-14.1ubuntu2), libx11-dev, libsasl2-dev -Standards-Version: 3.8.3 +Build-Depends: bcc, + bzip2, + debhelper (>= 7), + device-tree-compiler [powerpc], + iasl, + libaio-dev, + libasound2-dev, + libattr1-dev, + libcurl4-gnutls-dev, + libgnutls-dev, + libncurses5-dev, + libpci-dev, + libpulse-dev, + libsasl2-dev, + libsdl1.2-dev (>= 1.2.14), + libx11-dev, + nasm, + perl, + pkg-config, + quilt (>= 0.40), + sysv-rc (>= 2.86.ds1-14.1ubuntu2), + texi2html, + texinfo, + uuid-dev, + zlib1g-dev +Standards-Version: 3.9.1 Homepage: http://www.linux-kvm.org Package: qemu-kvm -Architecture: i386 amd64 powerpc s390 lpia -Depends: ${shlibs:Depends}, - ${misc:Depends}, - adduser, - bridge-utils, - iproute, - python, - qemu-common (>= ${source:Version}) +Architecture: any +Depends: bridge-utils, + iproute, + python, + qemu-common (>= ${source:Version}), + ${misc:Depends}, + ${shlibs:Depends} +Pre-Depends: adduser Provides: kvm, qemu -Conflicts: qemu (<< 0.11.0-0ubuntu5), kvm (<< 1:84+dfsg-0ubuntu16+0.11.0), kvm-data -Replaces: qemu, kvm (<< 1:84+dfsg-0ubuntu16+0.11.0), kvm-data +Conflicts: kvm (<< 1:84+dfsg-0ubuntu16+0.11.0), kvm-data, qemu (<< 0.11.0-0ubuntu5) +Replaces: kvm (<< 1:84+dfsg-0ubuntu16+0.11.0), kvm-data, qemu Breaks: udev (<< 136-1) Description: Full virtualization on i386 and amd64 hardware Using KVM, one can run multiple virtual PCs, each running unmodified Linux or @@ -48,74 +69,38 @@ kvm-intel.ko) and a userspace component. This package contains the userspace component, and you can get the kernel modules from the standard kernel images. . - This package contains support for the x86 and x86-64 architectures only. - Support for other architectures is provided by the qemu-kvm-extras package. + This package contains support for running virtualized and emulated x86 and + x86-64 machines only. Support for other architectures is provided by the + qemu-linaro source package. Package: qemu-common Architecture: all -Depends: ${shlibs:Depends}, ${misc:Depends}, seabios, vgabios +Depends: seabios, vgabios (>= 0.6c-2ubuntu2), ${misc:Depends}, ${shlibs:Depends} Recommends: cpu-checker -Suggests: mol-drivers-macosx, openbios-sparc, kvm-pxe, ubuntu-vm-builder, uml-utilities +Suggests: kvm-pxe, + mol-drivers-macosx, + openbios-sparc, + ubuntu-vm-builder, + uml-utilities Replaces: qemu-kvm (<< 0.12.3+noroms-0ubuntu1) Description: qemu common functionality (bios, documentation, etc) This package pulls in the various binary bios rom blobs needed to boot the various emulated architectures, as well as the documentation. -Package: qemu-kvm-extras -Architecture: i386 amd64 powerpc s390 lpia armel -Depends: ${shlibs:Depends}, ${misc:Depends}, qemu-kvm -Description: fast processor emulator binaries for non-x86 architectures - QEMU is a fast processor emulator: currently the package supports - ARM, CRIS, i386, M68k (ColdFire), MIPS, PowerPC, SH4, SPARc and x86-64 - emulation. By using dynamic translation it achieves reasonable speed - while being easy to port on new host CPUs. QEMU has two operating modes: - . - * User mode emulation: QEMU can launch Linux processes compiled for - one CPU on another CPU. - * Full system emulation: QEMU emulates a full system, including a - processor and various peripherals. It enables easier testing and - debugging of system code. It can also be used to provide virtual - hosting of several virtual PC on a single server. - . - QEMU requires no host kernel patches or CPU extensions to run. - -Package: qemu-kvm-extras-static -Architecture: amd64 i386 lpia -Depends: ${misc:Depends}, binfmt-support (>= 1.2.17) -Recommends: debootstrap -Replaces: qemu-arm-static (<< 0.12.2-0ubuntu2~) -Provides: qemu-arm-static -Conflicts: qemu-arm-static (<< 0.12.2-0ubuntu2~) -Description: static QEMU user mode emulation binaries - This package provides a static version of the QEMU ARM user mode - emulation. This is particularly useful in combination with - binfmt-support: it permits running ARM binaries in an ARM chroot. - . - If you want full system emulation or if you don't need binfmt - support, you probably want the qemu-kvm-extras package instead. - -Package: qemu-arm-static -Architecture: amd64 i386 lpia -Depends: ${misc:Depends}, qemu-kvm-extras-static -Description: dummy transitional package for qemu-kvm-extras-static - This transitional package helps users transition to the - qemu-kvm-extras-static package. Once this package and its dependencies - are installed you can safely remove it. - Package: kvm -Architecture: amd64 armel i386 powerpc sparc -Depends: ${misc:Depends}, qemu-kvm -Section: metapackages -Description: dummy transitional pacakge from kvm to qemu-kvm +Architecture: amd64 armel armhf i386 powerpc sparc +Depends: qemu-kvm, ${misc:Depends} +Section: misc +Description: dummy transitional package from kvm to qemu-kvm This transitional package helps users transition from the kvm package to the - kvm-qemu package. Once this package and its dependencies are installed you + qemu-kvm package. Once this package and its dependencies are installed you can safely remove it. Package: qemu -Architecture: amd64 armel i386 powerpc sparc -Depends: ${misc:Depends}, qemu-kvm -Section: metapackages -Description: dummy transitional pacakge from qemu to qemu-kvm +Architecture: amd64 armel armhf i386 powerpc sparc +Depends: qemu-kvm, ${misc:Depends} +Section: misc +Description: dummy transitional package from qemu to qemu-kvm This transitional package helps users transition from the qemu package to the - kvm-qemu package. Once this package and its dependencies are installed you + qemu-kvm package. Once this package and its dependencies are installed you can safely remove it. diff -Nru qemu-kvm-0.12.5+noroms/debian/copyright qemu-kvm-0.14.1/debian/copyright --- qemu-kvm-0.12.5+noroms/debian/copyright 2011-07-27 16:49:17.000000000 +0000 +++ qemu-kvm-0.14.1/debian/copyright 2011-07-27 16:49:18.000000000 +0000 @@ -29,9 +29,6 @@ released under the BSD license, including: * aes, bsd-user, sd, slirp, sys-queue - On Debian systems, the complete text of the BSD license be found in the - file /usr/share/common-licenses/BSD. - Some hardware device emulation sources and other QEMU functionality are released under the MIT/X11 (BSD-like) license, including: * sdl, host-utils, vnc, keymaps, ioport, usb, hw/*, net, acl, block, @@ -51,6 +48,35 @@ 4) QEMU is a trademark of Fabrice Bellard. -- Fabrice Bellard. + The text of the BSD license: + + Copyright (c) The Regents of the University of California. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the University nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + The Ubuntu packaging: Copyright (C) 2009 Canonical Ltd. released under the GPL-2. diff -Nru qemu-kvm-0.12.5+noroms/debian/patches/05_improve-qemu-img-errors.patch qemu-kvm-0.14.1/debian/patches/05_improve-qemu-img-errors.patch --- qemu-kvm-0.12.5+noroms/debian/patches/05_improve-qemu-img-errors.patch 2011-07-27 16:49:17.000000000 +0000 +++ qemu-kvm-0.14.1/debian/patches/05_improve-qemu-img-errors.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,19 +0,0 @@ -qemu-img: improve error reporting - -Use strerror to provide a better error message when qemu-img fails. - -https://bugs.edge.launchpad.net/ubuntu/+source/qemu-kvm/+bug/418112 - -Signed-off-by: Dustin Kirkland - ---- a/qemu-img.c -+++ b/qemu-img.c -@@ -365,7 +365,7 @@ - } else if (ret == -EFBIG) { - error("The image size is too large for file format '%s'", fmt); - } else { -- error("Error while formatting"); -+ error("Error while formatting (%s)", strerror(-ret)); - } - } - return 0; diff -Nru qemu-kvm-0.12.5+noroms/debian/patches/arm-higher-initrd-load-address.patch qemu-kvm-0.14.1/debian/patches/arm-higher-initrd-load-address.patch --- qemu-kvm-0.12.5+noroms/debian/patches/arm-higher-initrd-load-address.patch 2011-07-27 16:49:17.000000000 +0000 +++ qemu-kvm-0.14.1/debian/patches/arm-higher-initrd-load-address.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -Index: qemu-kvm-0.12.3/hw/arm_boot.c -=================================================================== ---- qemu-kvm-0.12.3.orig/hw/arm_boot.c 2010-03-20 10:31:24.200423864 +0100 -+++ qemu-kvm-0.12.3/hw/arm_boot.c 2010-03-20 10:31:38.120443588 +0100 -@@ -15,7 +15,7 @@ - - #define KERNEL_ARGS_ADDR 0x100 - #define KERNEL_LOAD_ADDR 0x00010000 --#define INITRD_LOAD_ADDR 0x00800000 -+#define INITRD_LOAD_ADDR 0x00d00000 - - /* The worlds second smallest bootloader. Set r0-r2, then jump to kernel. */ - static uint32_t bootloader[] = { diff -Nru qemu-kvm-0.12.5+noroms/debian/patches/arm-host-fix-compiler-warning.patch qemu-kvm-0.14.1/debian/patches/arm-host-fix-compiler-warning.patch --- qemu-kvm-0.12.5+noroms/debian/patches/arm-host-fix-compiler-warning.patch 2011-07-27 16:49:17.000000000 +0000 +++ qemu-kvm-0.14.1/debian/patches/arm-host-fix-compiler-warning.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,87 +0,0 @@ -From bc4347b883e8175dadef77ed9e02ccaa5e8eba94 Mon Sep 17 00:00:00 2001 -From: Stefan Weil -Date: Wed, 20 Jan 2010 19:43:25 +0100 -Subject: [PATCH] arm host: fix compiler warning -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Compilation for arm (native or cross) results in this -warning: - -fpu/softfloat-native.c: In function ‘float64_round_to_int’: -fpu/softfloat-native.c:387: error: control reaches end of non-void function - -float64_round_to_int uses special assembler code for arm -and has no explicit return value. - -As there is no obvious reason why arm should need special -code, all fpu related conditionals were removed. -The remaining code is standard (C99) and compiles for arm, -too. - -Signed-off-by: Stefan Weil -Acked-by: Laurent Desnogues -Signed-off-by: Aurelien Jarno ---- - fpu/softfloat-native.c | 20 -------------------- - fpu/softfloat-native.h | 7 ------- - 2 files changed, 0 insertions(+), 27 deletions(-) - -Index: b/fpu/softfloat-native.c -=================================================================== ---- a/fpu/softfloat-native.c -+++ b/fpu/softfloat-native.c -@@ -13,8 +13,6 @@ - #if (defined(CONFIG_BSD) && !defined(__APPLE__) && !defined(__GLIBC__)) || \ - (defined(CONFIG_SOLARIS) && CONFIG_SOLARIS_VERSION < 10) - fpsetround(val); --#elif defined(__arm__) -- /* nothing to do */ - #else - fesetround(val); - #endif -@@ -366,25 +364,7 @@ - - float64 float64_round_to_int( float64 a STATUS_PARAM ) - { --#if defined(__arm__) -- switch(STATUS(float_rounding_mode)) { -- default: -- case float_round_nearest_even: -- asm("rndd %0, %1" : "=f" (a) : "f"(a)); -- break; -- case float_round_down: -- asm("rnddm %0, %1" : "=f" (a) : "f"(a)); -- break; -- case float_round_up: -- asm("rnddp %0, %1" : "=f" (a) : "f"(a)); -- break; -- case float_round_to_zero: -- asm("rnddz %0, %1" : "=f" (a) : "f"(a)); -- break; -- } --#else - return rint(a); --#endif - } - - float64 float64_rem( float64 a, float64 b STATUS_PARAM) -Index: b/fpu/softfloat-native.h -=================================================================== ---- a/fpu/softfloat-native.h -+++ b/fpu/softfloat-native.h -@@ -126,13 +126,6 @@ - float_round_up = FP_RP, - float_round_to_zero = FP_RZ - }; --#elif defined(__arm__) --enum { -- float_round_nearest_even = 0, -- float_round_down = 1, -- float_round_up = 2, -- float_round_to_zero = 3 --}; - #else - enum { - float_round_nearest_even = FE_TONEAREST, diff -Nru qemu-kvm-0.12.5+noroms/debian/patches/arm-ignore-writes-of-perf-reg-cp15-with-crm-12.patch qemu-kvm-0.14.1/debian/patches/arm-ignore-writes-of-perf-reg-cp15-with-crm-12.patch --- qemu-kvm-0.12.5+noroms/debian/patches/arm-ignore-writes-of-perf-reg-cp15-with-crm-12.patch 2011-07-27 16:49:17.000000000 +0000 +++ qemu-kvm-0.14.1/debian/patches/arm-ignore-writes-of-perf-reg-cp15-with-crm-12.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,22 +0,0 @@ -From 51f6215e44ed765b11d47ac00dabbc77ccb2b119 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Lo=C3=AFc=20Minier?= -Date: Sun, 25 Jul 2010 21:26:22 +0200 -Subject: [PATCH] Ignore writes of perf reg (cp15 with crm == 12) - -On ARMv7, ignore writes to cp15 with crm == 12; these are to setup perf -counters which we don't have. ---- - target-arm/helper.c | 2 ++ - 1 files changed, 2 insertions(+), 0 deletions(-) - ---- a/target-arm/helper.c -+++ b/target-arm/helper.c -@@ -1438,6 +1438,8 @@ - } - break; - case 9: -+ if (arm_feature(env, ARM_FEATURE_V7) && crm == 12) -+ break; /* Perf counters. */ - if (arm_feature(env, ARM_FEATURE_OMAPCP)) - break; - switch (crm) { diff -Nru qemu-kvm-0.12.5+noroms/debian/patches/caps-lock-key-up-event.patch qemu-kvm-0.14.1/debian/patches/caps-lock-key-up-event.patch --- qemu-kvm-0.12.5+noroms/debian/patches/caps-lock-key-up-event.patch 2011-07-27 16:49:17.000000000 +0000 +++ qemu-kvm-0.14.1/debian/patches/caps-lock-key-up-event.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,33 +0,0 @@ -Description: Enable normal up/down events for Caps-Lock and Num-Lock keys - by setting SDL_DISABLE_LOCK_KEYS (which requires SDL > 1.2.14). This fixes - handling of capslock when capslock is mapped to something else in host system. -Author: Benjamin Drung -Bug-Ubuntu: https://launchpad.net/bugs/427612 - ---- a/sdl.c 2010-02-26 16:26:00 +0000 -+++ b/sdl.c 2010-11-24 21:17:18 +0000 -@@ -388,12 +388,6 @@ - else - modifiers_state[keycode] = 1; - break; -- case 0x45: /* num lock */ -- case 0x3a: /* caps lock */ -- /* SDL does not send the key up event, so we generate it */ -- kbd_put_keycode(keycode); -- kbd_put_keycode(keycode | 0x80); -- return; - } - - /* now send the key code */ -@@ -848,6 +842,10 @@ - if (no_frame) - gui_noframe = 1; - -+ // Enable normal up/down events for Caps-Lock and Num-Lock keys. -+ // This requires SDL >= 1.2.14 -+ setenv("SDL_DISABLE_LOCK_KEYS", "1", 1); -+ - flags = SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE; - if (SDL_Init (flags)) { - fprintf(stderr, "Could not initialize SDL - exiting\n"); - diff -Nru qemu-kvm-0.12.5+noroms/debian/patches/check-for-invalid-initrd-file.patch qemu-kvm-0.14.1/debian/patches/check-for-invalid-initrd-file.patch --- qemu-kvm-0.12.5+noroms/debian/patches/check-for-invalid-initrd-file.patch 2011-07-27 16:49:17.000000000 +0000 +++ qemu-kvm-0.14.1/debian/patches/check-for-invalid-initrd-file.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,30 +0,0 @@ -commit d6fa4b77fb8f27ac84cf23fb1e15016673d98a47 -Author: M. Mohan Kumar -Date: Mon Apr 12 10:01:33 2010 +0530 - - Check for invalid initrd file - - When qemu is invoked with an invalid initrd file, it crashes. Following - patch prints a error message and exits if an invalid initrd is - specified. Includes changes suggested by JV. - - Signed-off-by: M. Mohan Kumar - Signed-off-by: Aurelien Jarno - -Index: qemu-kvm-0.12.3+noroms/hw/pc.c -=================================================================== ---- qemu-kvm-0.12.3+noroms.orig/hw/pc.c 2010-08-31 03:11:42.000000000 +1200 -+++ qemu-kvm-0.12.3+noroms/hw/pc.c 2010-08-31 03:13:59.000000000 +1200 -@@ -871,6 +871,12 @@ - } - - initrd_size = get_image_size(initrd_filename); -+ if (initrd_size < 0) { -+ fprintf(stderr, "qemu: error reading initrd %s\n", -+ initrd_filename); -+ exit(1); -+ } -+ - initrd_addr = (initrd_max-initrd_size) & ~4095; - - initrd_data = qemu_malloc(initrd_size); diff -Nru qemu-kvm-0.12.5+noroms/debian/patches/CVE-2011-1751.diff qemu-kvm-0.14.1/debian/patches/CVE-2011-1751.diff --- qemu-kvm-0.12.5+noroms/debian/patches/CVE-2011-1751.diff 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/debian/patches/CVE-2011-1751.diff 2011-07-27 16:49:18.000000000 +0000 @@ -0,0 +1,32 @@ +Origin: Gerd Hoffmann +X-Patchwork-Id: 96331 +Message-Id: <1305796393-22786-2-git-send-email-kraxel@redhat.com> +Description: Ignore pci unplug requests for unpluggable devices (CVE-2011-1751) + This patch makes qemu ignore unplug requests from the guest for pci + devices which are tagged as non-hotpluggable. Trouble spot is the + piix4 chipset with the ISA bridge. Requests to unplug that one will + make it go away together with all ISA bus devices, which are not + prepared to be unplugged and thus don't cleanup, leaving active + qemu timers behind in free'ed memory. + +Signed-off-by: Gerd Hoffmann + +Index: qemu-kvm-0.14.0+noroms/hw/acpi_piix4.c +=================================================================== +--- qemu-kvm-0.14.0+noroms.orig/hw/acpi_piix4.c 2011-05-29 09:13:56.000000000 -0500 ++++ qemu-kvm-0.14.0+noroms/hw/acpi_piix4.c 2011-05-29 09:14:00.000000000 -0500 +@@ -605,11 +605,13 @@ + BusState *bus = opaque; + DeviceState *qdev, *next; + PCIDevice *dev; ++ PCIDeviceInfo *info; + int slot = ffs(val) - 1; + + QLIST_FOREACH_SAFE(qdev, &bus->children, sibling, next) { + dev = DO_UPCAST(PCIDevice, qdev, qdev); +- if (PCI_SLOT(dev->devfn) == slot) { ++ info = container_of(qdev->info, PCIDeviceInfo, qdev); ++ if (PCI_SLOT(dev->devfn) == slot && !info->no_hotplug) { + qdev_free(qdev); + } + } diff -Nru qemu-kvm-0.12.5+noroms/debian/patches/CVE-2011-2212-virtqueue-indirect-overflow.patch qemu-kvm-0.14.1/debian/patches/CVE-2011-2212-virtqueue-indirect-overflow.patch --- qemu-kvm-0.12.5+noroms/debian/patches/CVE-2011-2212-virtqueue-indirect-overflow.patch 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/debian/patches/CVE-2011-2212-virtqueue-indirect-overflow.patch 2011-07-27 16:49:18.000000000 +0000 @@ -0,0 +1,35 @@ +From: Nelson Elhage +Date: Thu, 19 May 2011 13:23:17 -0400 +Subject: [PATCH] virtqueue: Sanity-check the length of indirect descriptors. + +We were previously allowing arbitrarily-long descriptors, which could lead to a +buffer overflow in the qemu-kvm process. + +Index: qemu-kvm-0.14.0+noroms/hw/virtio.c +=================================================================== +--- qemu-kvm-0.14.0+noroms.orig/hw/virtio.c 2011-07-05 14:28:39.000000000 -0500 ++++ qemu-kvm-0.14.0+noroms/hw/virtio.c 2011-07-05 14:29:19.000000000 -0500 +@@ -336,6 +336,11 @@ + max = vring_desc_len(desc_pa, i) / sizeof(VRingDesc); + num_bufs = i = 0; + desc_pa = vring_desc_addr(desc_pa, i); ++ ++ if (max > VIRTQUEUE_MAX_SIZE) { ++ error_report("Too-large indirect descriptor"); ++ exit(1); ++ } + } + + do { +@@ -406,6 +411,11 @@ + max = vring_desc_len(desc_pa, i) / sizeof(VRingDesc); + desc_pa = vring_desc_addr(desc_pa, i); + i = 0; ++ ++ if (max > VIRTQUEUE_MAX_SIZE) { ++ error_report("Too-large indirect descriptor"); ++ exit(1); ++ } + } + + /* Collect all the descriptors */ diff -Nru qemu-kvm-0.12.5+noroms/debian/patches/CVE-2011-2527.patch qemu-kvm-0.14.1/debian/patches/CVE-2011-2527.patch --- qemu-kvm-0.12.5+noroms/debian/patches/CVE-2011-2527.patch 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/debian/patches/CVE-2011-2527.patch 2011-07-27 16:49:18.000000000 +0000 @@ -0,0 +1,52 @@ +Refreshed for 0.14.1 + +Subject: os-posix: set groups properly for -runas +Date: Fri, 08 Jul 2011 23:22:07 -0000 +From: Stefan Hajnoczi +Message-Id: <1310203327-27069-1-git-send-email-stefanha@linux.vnet.ibm.com> +To: +Cc: Bug 807893 <807893@bugs.launchpad.net>, + Stefan Hajnoczi + +Andrew Griffiths reports that -runas does not set supplementary group +IDs. This means that gid 0 (root) is not dropped when switching to an +unprivileged user. + +Add an initgroups(3) call to use the -runas user's /etc/groups +membership to update the supplementary group IDs. + +Signed-off-by: Stefan Hajnoczi +Acked-by: Chris Wright + +--- +Note this needs compile testing on various POSIX host platforms. Tested on +Linux. Should work on BSD and Solaris. initgroups(3) is SVr4/BSD but not in +POSIX. + + os-posix.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +Index: qemu-kvm-0.14.0+noroms/os-posix.c +=================================================================== +--- qemu-kvm-0.14.0+noroms.orig/os-posix.c 2011-02-22 07:34:38.000000000 -0600 ++++ qemu-kvm-0.14.0+noroms/os-posix.c 2011-07-26 08:02:42.000000000 -0500 +@@ -31,6 +31,7 @@ + /*needed for MAP_POPULATE before including qemu-options.h */ + #include + #include ++#include + #include + + /* Needed early for CONFIG_BSD etc. */ +@@ -206,6 +207,11 @@ + fprintf(stderr, "Failed to setgid(%d)\n", user_pwd->pw_gid); + exit(1); + } ++ if (initgroups(user_pwd->pw_name, user_pwd->pw_gid) < 0) { ++ fprintf(stderr, "Failed to initgroups(\"%s\", %d)\n", ++ user_pwd->pw_name, user_pwd->pw_gid); ++ exit(1); ++ } + if (setuid(user_pwd->pw_uid) < 0) { + fprintf(stderr, "Failed to setuid(%d)\n", user_pwd->pw_uid); + exit(1); diff -Nru qemu-kvm-0.12.5+noroms/debian/patches/Detect-and-use-GCC-atomic-builtins-for-locking.patch qemu-kvm-0.14.1/debian/patches/Detect-and-use-GCC-atomic-builtins-for-locking.patch --- qemu-kvm-0.12.5+noroms/debian/patches/Detect-and-use-GCC-atomic-builtins-for-locking.patch 2011-07-27 16:49:17.000000000 +0000 +++ qemu-kvm-0.14.1/debian/patches/Detect-and-use-GCC-atomic-builtins-for-locking.patch 2011-07-27 16:49:18.000000000 +0000 @@ -8,9 +8,11 @@ qemu-lock.h | 13 +++++++++++++ 2 files changed, 30 insertions(+), 0 deletions(-) ---- a/configure -+++ b/configure -@@ -1911,6 +1911,20 @@ +Index: qemu-kvm-0.13.0-rc1/configure +=================================================================== +--- qemu-kvm-0.13.0-rc1.orig/configure 2010-09-07 20:22:49.000000000 -0500 ++++ qemu-kvm-0.13.0-rc1/configure 2010-10-20 13:20:03.425515003 -0500 +@@ -2182,6 +2182,20 @@ fi ########################################## @@ -31,7 +33,7 @@ # check if we have fdatasync fdatasync=no -@@ -2269,6 +2283,9 @@ +@@ -2562,6 +2576,9 @@ if test "$gcc_attribute_warn_unused_result" = "yes" ; then echo "CONFIG_GCC_ATTRIBUTE_WARN_UNUSED_RESULT=y" >> $config_host_mak fi @@ -41,8 +43,10 @@ if test "$fdatasync" = "yes" ; then echo "CONFIG_FDATASYNC=y" >> $config_host_mak fi ---- a/qemu-lock.h -+++ b/qemu-lock.h +Index: qemu-kvm-0.13.0-rc1/qemu-lock.h +=================================================================== +--- qemu-kvm-0.13.0-rc1.orig/qemu-lock.h 2010-09-07 20:22:49.000000000 -0500 ++++ qemu-kvm-0.13.0-rc1/qemu-lock.h 2010-10-20 13:20:03.425515003 -0500 @@ -33,6 +33,14 @@ #else diff -Nru qemu-kvm-0.12.5+noroms/debian/patches/fix-CMOS-info-for-drives-defined-with--device.patch qemu-kvm-0.14.1/debian/patches/fix-CMOS-info-for-drives-defined-with--device.patch --- qemu-kvm-0.12.5+noroms/debian/patches/fix-CMOS-info-for-drives-defined-with--device.patch 2011-07-27 16:49:17.000000000 +0000 +++ qemu-kvm-0.14.1/debian/patches/fix-CMOS-info-for-drives-defined-with--device.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,278 +0,0 @@ -Description: make sure the CMOS knows about the correct geometry so - Windows XP installs properly. -Origin: backport, http://git.qemu.org/qemu.git/commit/?id=c0897e0cb94e83ec1098867b81870e4f51f225b9 -Bug-Debian: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=579166 -Bug-Debian: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=588739 -Bug-Ubuntu: https://bugs.edge.launchpad.net/ubuntu/+source/qemu-kvm/+bug/586175 - -diff -Nur qemu-kvm-0.12.5+noroms/hw/ide/isa.c qemu-kvm-0.12.5+noroms.new/hw/ide/isa.c ---- qemu-kvm-0.12.5+noroms/hw/ide/isa.c 2010-07-26 20:43:53.000000000 -0400 -+++ qemu-kvm-0.12.5+noroms.new/hw/ide/isa.c 2010-09-15 20:22:37.000000000 -0400 -@@ -75,8 +75,8 @@ - return 0; - }; - --int isa_ide_init(int iobase, int iobase2, int isairq, -- DriveInfo *hd0, DriveInfo *hd1) -+ISADevice *isa_ide_init(int iobase, int iobase2, int isairq, -+ DriveInfo *hd0, DriveInfo *hd1) - { - ISADevice *dev; - ISAIDEState *s; -@@ -86,14 +86,14 @@ - qdev_prop_set_uint32(&dev->qdev, "iobase2", iobase2); - qdev_prop_set_uint32(&dev->qdev, "irq", isairq); - if (qdev_init(&dev->qdev) < 0) -- return -1; -+ return NULL; - - s = DO_UPCAST(ISAIDEState, dev, dev); - if (hd0) - ide_create_drive(&s->bus, 0, hd0); - if (hd1) - ide_create_drive(&s->bus, 1, hd1); -- return 0; -+ return dev; - } - - static ISADeviceInfo isa_ide_info = { -diff -Nur qemu-kvm-0.12.5+noroms/hw/ide/piix.c qemu-kvm-0.12.5+noroms.new/hw/ide/piix.c ---- qemu-kvm-0.12.5+noroms/hw/ide/piix.c 2010-07-26 20:43:53.000000000 -0400 -+++ qemu-kvm-0.12.5+noroms.new/hw/ide/piix.c 2010-09-15 20:22:37.000000000 -0400 -@@ -158,22 +158,24 @@ - - /* hd_table must contain 4 block drivers */ - /* NOTE: for the PIIX3, the IRQs and IOports are hardcoded */ --void pci_piix3_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn) -+PCIDevice *pci_piix3_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn) - { - PCIDevice *dev; - - dev = pci_create_simple(bus, devfn, "piix3-ide"); - pci_ide_create_devs(dev, hd_table); -+ return dev; - } - - /* hd_table must contain 4 block drivers */ - /* NOTE: for the PIIX4, the IRQs and IOports are hardcoded */ --void pci_piix4_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn) -+PCIDevice *pci_piix4_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn) - { - PCIDevice *dev; - - dev = pci_create_simple(bus, devfn, "piix4-ide"); - pci_ide_create_devs(dev, hd_table); -+ return dev; - } - - static PCIDeviceInfo piix_ide_info[] = { -diff -Nur qemu-kvm-0.12.5+noroms/hw/ide/qdev.c qemu-kvm-0.12.5+noroms.new/hw/ide/qdev.c ---- qemu-kvm-0.12.5+noroms/hw/ide/qdev.c 2010-07-26 20:43:53.000000000 -0400 -+++ qemu-kvm-0.12.5+noroms.new/hw/ide/qdev.c 2010-09-15 20:22:37.000000000 -0400 -@@ -90,6 +90,13 @@ - return DO_UPCAST(IDEDevice, qdev, dev); - } - -+void ide_get_bs(BlockDriverState *bs[], BusState *qbus) -+{ -+ IDEBus *bus = DO_UPCAST(IDEBus, qbus, qbus); -+ bs[0] = bus->master ? bus->master->dinfo->bdrv : NULL; -+ bs[1] = bus->slave ? bus->slave->dinfo->bdrv : NULL; -+} -+ - /* --------------------------------- */ - - typedef struct IDEDrive { -diff -Nur qemu-kvm-0.12.5+noroms/hw/ide.h qemu-kvm-0.12.5+noroms.new/hw/ide.h ---- qemu-kvm-0.12.5+noroms/hw/ide.h 2010-07-26 20:43:53.000000000 -0400 -+++ qemu-kvm-0.12.5+noroms.new/hw/ide.h 2010-09-15 20:22:37.000000000 -0400 -@@ -1,17 +1,18 @@ - #ifndef HW_IDE_H - #define HW_IDE_H - --#include "qdev.h" -+#include "isa.h" -+#include "pci.h" - - /* ide-isa.c */ --int isa_ide_init(int iobase, int iobase2, int isairq, -- DriveInfo *hd0, DriveInfo *hd1); -+ISADevice *isa_ide_init(int iobase, int iobase2, int isairq, -+ DriveInfo *hd0, DriveInfo *hd1); - - /* ide-pci.c */ - void pci_cmd646_ide_init(PCIBus *bus, DriveInfo **hd_table, - int secondary_ide_enabled); --void pci_piix3_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn); --void pci_piix4_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn); -+PCIDevice *pci_piix3_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn); -+PCIDevice *pci_piix4_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn); - - /* ide-macio.c */ - int pmac_ide_init (DriveInfo **hd_table, qemu_irq irq, -@@ -22,4 +23,6 @@ - qemu_irq irq, int shift, - DriveInfo *hd0, DriveInfo *hd1); - -+void ide_get_bs(BlockDriverState *bs[], BusState *qbus); -+ - #endif /* HW_IDE_H */ -diff -Nur qemu-kvm-0.12.5+noroms/hw/pc.c qemu-kvm-0.12.5+noroms.new/hw/pc.c ---- qemu-kvm-0.12.5+noroms/hw/pc.c 2010-09-15 20:22:28.000000000 -0400 -+++ qemu-kvm-0.12.5+noroms.new/hw/pc.c 2010-09-15 20:22:37.000000000 -0400 -@@ -24,6 +24,7 @@ - #include "hw.h" - #include "pc.h" - #include "fdc.h" -+#include "ide.h" - #include "pci.h" - #include "vmware_vga.h" - #include "usb-uhci.h" -@@ -251,15 +252,66 @@ - return(0); - } - --/* hd_table must contain 4 block drivers */ -+typedef struct pc_cmos_init_late_arg { -+ BusState *idebus0, *idebus1; -+} pc_cmos_init_late_arg; -+ -+static void pc_cmos_init_late(void *opaque) -+{ -+ pc_cmos_init_late_arg *arg = opaque; -+ RTCState *s = rtc_state; -+ int val; -+ BlockDriverState *hd_table[4]; -+ int i; -+ -+ ide_get_bs(hd_table, arg->idebus0); -+ ide_get_bs(hd_table + 2, arg->idebus1); -+ -+ rtc_set_memory(s, 0x12, (hd_table[0] ? 0xf0 : 0) | (hd_table[1] ? 0x0f : 0)); -+ if (hd_table[0]) -+ cmos_init_hd(0x19, 0x1b, hd_table[0]); -+ if (hd_table[1]) -+ cmos_init_hd(0x1a, 0x24, hd_table[1]); -+ -+ val = 0; -+ for (i = 0; i < 4; i++) { -+ if (hd_table[i]) { -+ int cylinders, heads, sectors, translation; -+ /* NOTE: bdrv_get_geometry_hint() returns the physical -+ geometry. It is always such that: 1 <= sects <= 63, 1 -+ <= heads <= 16, 1 <= cylinders <= 16383. The BIOS -+ geometry can be different if a translation is done. */ -+ translation = bdrv_get_translation_hint(hd_table[i]); -+ if (translation == BIOS_ATA_TRANSLATION_AUTO) { -+ bdrv_get_geometry_hint(hd_table[i], &cylinders, &heads, §ors); -+ if (cylinders <= 1024 && heads <= 16 && sectors <= 63) { -+ /* No translation. */ -+ translation = 0; -+ } else { -+ /* LBA translation. */ -+ translation = 1; -+ } -+ } else { -+ translation--; -+ } -+ val |= translation << (i * 2); -+ } -+ } -+ rtc_set_memory(s, 0x39, val); -+ -+ qemu_unregister_reset(pc_cmos_init_late, opaque); -+} -+ - static void cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size, -- const char *boot_device, DriveInfo **hd_table) -+ const char *boot_device, -+ BusState *idebus0, BusState *idebus1) - { - RTCState *s = rtc_state; - int nbds, bds[3] = { 0, }; - int val; - int fd0, fd1, nb; - int i; -+ static pc_cmos_init_late_arg arg; - - /* various important CMOS locations needed by PC/Bochs bios */ - -@@ -342,37 +394,9 @@ - - /* hard drives */ - -- rtc_set_memory(s, 0x12, (hd_table[0] ? 0xf0 : 0) | (hd_table[1] ? 0x0f : 0)); -- if (hd_table[0]) -- cmos_init_hd(0x19, 0x1b, hd_table[0]->bdrv); -- if (hd_table[1]) -- cmos_init_hd(0x1a, 0x24, hd_table[1]->bdrv); -- -- val = 0; -- for (i = 0; i < 4; i++) { -- if (hd_table[i]) { -- int cylinders, heads, sectors, translation; -- /* NOTE: bdrv_get_geometry_hint() returns the physical -- geometry. It is always such that: 1 <= sects <= 63, 1 -- <= heads <= 16, 1 <= cylinders <= 16383. The BIOS -- geometry can be different if a translation is done. */ -- translation = bdrv_get_translation_hint(hd_table[i]->bdrv); -- if (translation == BIOS_ATA_TRANSLATION_AUTO) { -- bdrv_get_geometry_hint(hd_table[i]->bdrv, &cylinders, &heads, §ors); -- if (cylinders <= 1024 && heads <= 16 && sectors <= 63) { -- /* No translation. */ -- translation = 0; -- } else { -- /* LBA translation. */ -- translation = 1; -- } -- } else { -- translation--; -- } -- val |= translation << (i * 2); -- } -- } -- rtc_set_memory(s, 0x39, val); -+ arg.idebus0 = idebus0; -+ arg.idebus1 = idebus1; -+ qemu_register_reset(pc_cmos_init_late, &arg); - } - - void ioport_set_a20(int enable) -@@ -1013,6 +1037,7 @@ - qemu_irq *i8259; - IsaIrqState *isa_irq_state; - DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; -+ BusState *idebus[MAX_IDE_BUS]; - DriveInfo *fd[MAX_FD]; - void *fw_cfg; - -@@ -1222,11 +1247,16 @@ - } - - if (pci_enabled) { -- pci_piix3_ide_init(pci_bus, hd, piix3_devfn + 1); -+ PCIDevice *dev; -+ dev = pci_piix3_ide_init(pci_bus, hd, piix3_devfn + 1); -+ idebus[0] = qdev_get_child_bus(&dev->qdev, "ide.0"); -+ idebus[1] = qdev_get_child_bus(&dev->qdev, "ide.1"); - } else { - for(i = 0; i < MAX_IDE_BUS; i++) { -- isa_ide_init(ide_iobase[i], ide_iobase2[i], ide_irq[i], -- hd[MAX_IDE_DEVS * i], hd[MAX_IDE_DEVS * i + 1]); -+ ISADevice *dev; -+ dev = isa_ide_init(ide_iobase[i], ide_iobase2[i], ide_irq[i], -+ hd[MAX_IDE_DEVS * i], hd[MAX_IDE_DEVS * i + 1]); -+ idebus[i] = qdev_get_child_bus(&dev->qdev, "ide.0"); - } - } - -@@ -1241,7 +1271,8 @@ - } - floppy_controller = fdctrl_init_isa(fd); - -- cmos_init(below_4g_mem_size, above_4g_mem_size, boot_device, hd); -+ cmos_init(below_4g_mem_size, above_4g_mem_size, boot_device, -+ idebus[0], idebus[1]); - - if (pci_enabled && usb_enabled) { - usb_uhci_piix3_init(pci_bus, piix3_devfn + 2); diff -Nru qemu-kvm-0.12.5+noroms/debian/patches/larger_default_ram_size.patch qemu-kvm-0.14.1/debian/patches/larger_default_ram_size.patch --- qemu-kvm-0.12.5+noroms/debian/patches/larger_default_ram_size.patch 2011-07-27 16:49:17.000000000 +0000 +++ qemu-kvm-0.14.1/debian/patches/larger_default_ram_size.patch 2011-07-27 16:49:18.000000000 +0000 @@ -1,11 +1,13 @@ ---- a/vl.c 2010-02-25 18:34:00.000000000 -0800 -+++ b/vl.c 2010-03-22 09:58:36.974869227 -0700 -@@ -173,7 +173,7 @@ int main(int argc, char **argv) +Index: qemu-kvm-0.13.0-rc1/vl.c +=================================================================== +--- qemu-kvm-0.13.0-rc1.orig/vl.c 2010-09-07 20:22:49.000000000 -0500 ++++ qemu-kvm-0.13.0-rc1/vl.c 2010-10-20 13:20:00.865515000 -0500 +@@ -165,7 +165,7 @@ //#define DEBUG_NET //#define DEBUG_SLIRP -#define DEFAULT_RAM_SIZE 128 +#define DEFAULT_RAM_SIZE 384 - static const char *data_dir; - const char *bios_name = NULL; + #define MAX_VIRTIO_CONSOLES 1 + diff -Nru qemu-kvm-0.12.5+noroms/debian/patches/linux-user-do-not-warn-for-missing-pselect6.patch qemu-kvm-0.14.1/debian/patches/linux-user-do-not-warn-for-missing-pselect6.patch --- qemu-kvm-0.12.5+noroms/debian/patches/linux-user-do-not-warn-for-missing-pselect6.patch 2011-07-27 16:49:17.000000000 +0000 +++ qemu-kvm-0.14.1/debian/patches/linux-user-do-not-warn-for-missing-pselect6.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,64 +0,0 @@ -From 9e42382fc14b999126f8cabb32761eb98153fd1b Mon Sep 17 00:00:00 2001 -From: Riku Voipio -Date: Fri, 7 May 2010 12:28:05 +0000 -Subject: [PATCH] linux-user: do not warn for missing pselect6 - -Libc will fallback gracefully if pselect6 is not available. Thus put -pselect6 to nowarn until the atomicity issues of the original pselect6 -patch are dealt with. - -Signed-off-by: Riku Voipio -Cc: Michael Casadevall -Signed-off-by: Aurelien Jarno ---- - linux-user/syscall.c | 4 ++++ - 1 files changed, 4 insertions(+), 0 deletions(-) - -diff --git a/linux-user/syscall.c b/linux-user/syscall.c -index bca8f70..8222cb9 100644 ---- a/linux-user/syscall.c -+++ b/linux-user/syscall.c -@@ -5268,6 +5268,10 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, - } - break; - #endif -+#ifdef TARGET_NR_pselect6 -+ case TARGET_NR_pselect6: -+ goto unimplemented_nowarn; -+#endif - case TARGET_NR_symlink: - { - void *p2; --- -1.7.1 - -From 7270547fe8d0d2c1d13fee592042208d4a82f73a Mon Sep 17 00:00:00 2001 -From: Michael Casadevall -Date: Fri, 26 Mar 2010 15:25:10 +0000 -Subject: [PATCH] linux-user: Add the syscall id for pselect6 on ARM - -As this is now supported in newer linux kernels. - -Signed-off-by: Michael Casadevall -Signed-off-by: Riku Voipio -Signed-off-by: Aurelien Jarno ---- - linux-user/arm/syscall_nr.h | 2 +- - 1 files changed, 1 insertions(+), 1 deletions(-) - -diff --git a/linux-user/arm/syscall_nr.h b/linux-user/arm/syscall_nr.h -index b1db341..79a216a 100644 ---- a/linux-user/arm/syscall_nr.h -+++ b/linux-user/arm/syscall_nr.h -@@ -338,7 +338,7 @@ - #define TARGET_NR_readlinkat (332) - #define TARGET_NR_fchmodat (333) - #define TARGET_NR_faccessat (334) -- /* 335 for pselect6 */ -+#define TARGET_NR_pselect6 (335) - /* 336 for ppoll */ - #define TARGET_NR_unshare (337) - #define TARGET_NR_set_robust_list (338) --- -1.7.1 - diff -Nru qemu-kvm-0.12.5+noroms/debian/patches/scm-rights-fd.patch qemu-kvm-0.14.1/debian/patches/scm-rights-fd.patch --- qemu-kvm-0.12.5+noroms/debian/patches/scm-rights-fd.patch 2011-07-27 16:49:17.000000000 +0000 +++ qemu-kvm-0.14.1/debian/patches/scm-rights-fd.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,57 +0,0 @@ -From e53f27b9d9df73461308618151fa6e6392aebd85 Mon Sep 17 00:00:00 2001 -From: Paolo Bonzini -Date: Fri, 16 Apr 2010 15:25:23 +0000 -Subject: stash away SCM_RIGHTS fd until a getfd command arrives - -If there is already a fd in s->msgfd before recvmsg it is -closed by parts that this patch does not touch. So, only -one descriptor can be "leaked" by attaching it to a command -other than getfd. - -Signed-off-by: Paolo Bonzini -Signed-off-by: Luiz Capitulino - -diff -Nur qemu-kvm-0.12.4+noroms/monitor.c qemu-kvm-0.12.4+noroms.new/monitor.c ---- qemu-kvm-0.12.4+noroms/monitor.c 2010-05-09 06:05:19.000000000 -0500 -+++ qemu-kvm-0.12.4+noroms.new/monitor.c 2010-06-16 11:01:57.000000000 -0500 -@@ -2294,15 +2294,6 @@ - return; - } - -- fd = dup(fd); -- if (fd == -1) { -- if (errno == EMFILE) -- qemu_error_new(QERR_TOO_MANY_FILES); -- else -- qemu_error_new(QERR_UNDEFINED_ERROR); -- return; -- } -- - QLIST_FOREACH(monfd, &mon->fds, next) { - if (strcmp(monfd->name, fdname) != 0) { - continue; -diff -Nur qemu-kvm-0.12.4+noroms/qemu-char.c qemu-kvm-0.12.4+noroms.new/qemu-char.c ---- qemu-kvm-0.12.4+noroms/qemu-char.c 2010-05-09 06:05:19.000000000 -0500 -+++ qemu-kvm-0.12.4+noroms.new/qemu-char.c 2010-06-16 11:03:40.000000000 -0500 -@@ -1955,7 +1955,9 @@ - { - TCPCharDriver *s = chr->opaque; - -- return s->msgfd; -+ int fd = s->msgfd; -+ s->msgfd = -1; -+ return fd; - } - - #ifndef _WIN32 -@@ -2043,10 +2045,6 @@ - tcp_chr_process_IAC_bytes(chr, s, buf, &size); - if (size > 0) - qemu_chr_read(chr, buf, size); -- if (s->msgfd != -1) { -- close(s->msgfd); -- s->msgfd = -1; -- } - } - } - diff -Nru qemu-kvm-0.12.5+noroms/debian/patches/series qemu-kvm-0.14.1/debian/patches/series --- qemu-kvm-0.12.5+noroms/debian/patches/series 2011-07-27 16:49:17.000000000 +0000 +++ qemu-kvm-0.14.1/debian/patches/series 2011-07-27 16:49:18.000000000 +0000 @@ -1,11 +1,7 @@ -arm-higher-initrd-load-address.patch -arm-ignore-writes-of-perf-reg-cp15-with-crm-12.patch larger_default_ram_size.patch -scm-rights-fd.patch Detect-and-use-GCC-atomic-builtins-for-locking.patch -linux-user-do-not-warn-for-missing-pselect6.patch -05_improve-qemu-img-errors.patch -check-for-invalid-initrd-file.patch -fix-CMOS-info-for-drives-defined-with--device.patch -arm-host-fix-compiler-warning.patch -caps-lock-key-up-event.patch +spice-qxl-locking-fix-for-qemu-kvm.patch +CVE-2011-1751.diff +CVE-2011-2212-virtqueue-indirect-overflow.patch +virtio-guard-against-negative-vq-notifies-CVE-2011-2512.diff +CVE-2011-2527.patch diff -Nru qemu-kvm-0.12.5+noroms/debian/patches/spice-qxl-locking-fix-for-qemu-kvm.patch qemu-kvm-0.14.1/debian/patches/spice-qxl-locking-fix-for-qemu-kvm.patch --- qemu-kvm-0.12.5+noroms/debian/patches/spice-qxl-locking-fix-for-qemu-kvm.patch 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/debian/patches/spice-qxl-locking-fix-for-qemu-kvm.patch 2011-07-27 16:49:18.000000000 +0000 @@ -0,0 +1,150 @@ +Description: spice/qxl: locking fix for qemu-kvm +Author: Gerd Hoffmann +Source: upstream, http://patchwork.ozlabs.org/patch/84704/ +Forwarding: not-needed + +Index: qemu-kvm-natty-qxl/hw/qxl.c +=================================================================== +--- qemu-kvm-natty-qxl.orig/hw/qxl.c 2011-03-01 11:08:54.787408000 -0600 ++++ qemu-kvm-natty-qxl/hw/qxl.c 2011-03-01 11:12:36.229760328 -0600 +@@ -125,6 +125,27 @@ + static void qxl_reset_surfaces(PCIQXLDevice *d); + static void qxl_ring_set_dirty(PCIQXLDevice *qxl); + ++/* qemu-kvm locking ... */ ++void qxl_unlock_iothread(SimpleSpiceDisplay *ssd) ++{ ++ if (cpu_single_env) { ++ assert(ssd->env == NULL); ++ ssd->env = cpu_single_env; ++ cpu_single_env = NULL; ++ } ++ qemu_mutex_unlock_iothread(); ++} ++ ++void qxl_lock_iothread(SimpleSpiceDisplay *ssd) ++{ ++ qemu_mutex_lock_iothread(); ++ if (ssd->env) { ++ assert(cpu_single_env == NULL); ++ cpu_single_env = ssd->env; ++ ssd->env = NULL; ++ } ++} ++ + static inline uint32_t msb_mask(uint32_t val) + { + uint32_t mask; +@@ -662,10 +683,10 @@ + dprint(d, 1, "%s: start%s\n", __FUNCTION__, + loadvm ? " (loadvm)" : ""); + +- qemu_mutex_unlock_iothread(); ++ qxl_unlock_iothread(&d->ssd); + d->ssd.worker->reset_cursor(d->ssd.worker); + d->ssd.worker->reset_image_cache(d->ssd.worker); +- qemu_mutex_lock_iothread(); ++ qxl_lock_iothread(&d->ssd); + qxl_reset_surfaces(d); + qxl_reset_memslots(d); + +@@ -795,9 +816,9 @@ + { + dprint(d, 1, "%s:\n", __FUNCTION__); + d->mode = QXL_MODE_UNDEFINED; +- qemu_mutex_unlock_iothread(); ++ qxl_unlock_iothread(&d->ssd); + d->ssd.worker->destroy_surfaces(d->ssd.worker); +- qemu_mutex_lock_iothread(); ++ qxl_lock_iothread(&d->ssd); + memset(&d->guest_surfaces.cmds, 0, sizeof(d->guest_surfaces.cmds)); + } + +@@ -866,9 +887,9 @@ + dprint(d, 1, "%s\n", __FUNCTION__); + + d->mode = QXL_MODE_UNDEFINED; +- qemu_mutex_unlock_iothread(); ++ qxl_unlock_iothread(&d->ssd); + d->ssd.worker->destroy_primary_surface(d->ssd.worker, 0); +- qemu_mutex_lock_iothread(); ++ qxl_lock_iothread(&d->ssd); + } + + static void qxl_set_mode(PCIQXLDevice *d, int modenr, int loadvm) +@@ -938,10 +959,10 @@ + case QXL_IO_UPDATE_AREA: + { + QXLRect update = d->ram->update_area; +- qemu_mutex_unlock_iothread(); ++ qxl_unlock_iothread(&d->ssd); + d->ssd.worker->update_area(d->ssd.worker, d->ram->update_surface, + &update, NULL, 0, 0); +- qemu_mutex_lock_iothread(); ++ qxl_lock_iothread(&d->ssd); + break; + } + case QXL_IO_NOTIFY_CMD: +Index: qemu-kvm-natty-qxl/ui/spice-display.c +=================================================================== +--- qemu-kvm-natty-qxl.orig/ui/spice-display.c 2011-03-01 11:08:54.787408000 -0600 ++++ qemu-kvm-natty-qxl/ui/spice-display.c 2011-03-01 11:12:36.233761675 -0600 +@@ -186,18 +186,18 @@ + surface.mem = (intptr_t)ssd->buf; + surface.group_id = MEMSLOT_GROUP_HOST; + +- qemu_mutex_unlock_iothread(); ++ qxl_unlock_iothread(ssd); + ssd->worker->create_primary_surface(ssd->worker, 0, &surface); +- qemu_mutex_lock_iothread(); ++ qxl_lock_iothread(ssd); + } + + void qemu_spice_destroy_host_primary(SimpleSpiceDisplay *ssd) + { + dprint(1, "%s:\n", __FUNCTION__); + +- qemu_mutex_unlock_iothread(); ++ qxl_unlock_iothread(ssd); + ssd->worker->destroy_primary_surface(ssd->worker, 0); +- qemu_mutex_lock_iothread(); ++ qxl_lock_iothread(ssd); + } + + void qemu_spice_vm_change_state_handler(void *opaque, int running, int reason) +@@ -207,9 +207,9 @@ + if (running) { + ssd->worker->start(ssd->worker); + } else { +- qemu_mutex_unlock_iothread(); ++ qxl_unlock_iothread(ssd); + ssd->worker->stop(ssd->worker); +- qemu_mutex_lock_iothread(); ++ qxl_lock_iothread(ssd); + } + ssd->running = running; + } +Index: qemu-kvm-natty-qxl/ui/spice-display.h +=================================================================== +--- qemu-kvm-natty-qxl.orig/ui/spice-display.h 2011-03-01 11:08:54.787408000 -0600 ++++ qemu-kvm-natty-qxl/ui/spice-display.h 2011-03-01 11:12:36.233761675 -0600 +@@ -43,6 +43,9 @@ + QXLRect dirty; + int notify; + int running; ++ ++ /* qemu-kvm locking ... */ ++ void *env; + } SimpleSpiceDisplay; + + typedef struct SimpleSpiceUpdate { +@@ -52,6 +55,9 @@ + uint8_t *bitmap; + } SimpleSpiceUpdate; + ++void qxl_unlock_iothread(SimpleSpiceDisplay *ssd); ++void qxl_lock_iothread(SimpleSpiceDisplay *ssd); ++ + int qemu_spice_rect_is_empty(const QXLRect* r); + void qemu_spice_rect_union(QXLRect *dest, const QXLRect *r); + diff -Nru qemu-kvm-0.12.5+noroms/debian/patches/virtio-guard-against-negative-vq-notifies-CVE-2011-2512.diff qemu-kvm-0.14.1/debian/patches/virtio-guard-against-negative-vq-notifies-CVE-2011-2512.diff --- qemu-kvm-0.12.5+noroms/debian/patches/virtio-guard-against-negative-vq-notifies-CVE-2011-2512.diff 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/debian/patches/virtio-guard-against-negative-vq-notifies-CVE-2011-2512.diff 2011-07-27 16:49:18.000000000 +0000 @@ -0,0 +1,70 @@ +upstream commit 7157e2e23e89adcd436caeab31fdd6b47eded377 +(refreshed for 0.14) +Author: Stefan Hajnoczi +Date: Sun May 8 22:29:07 2011 +0100 + + virtio: guard against negative vq notifies + + The virtio_queue_notify() function checks that the virtqueue number is + less than the maximum number of virtqueues. A signed comparison is used + but the virtqueue number could be negative if a buggy or malicious guest + is run. This results in memory accesses outside of the virtqueue array. + + It is risky doing input validation in common code instead of at the + guest<->host boundary. Note that virtio_queue_set_addr(), + virtio_queue_get_addr(), virtio_queue_get_num(), and many other virtio + functions do *not* validate the virtqueue number argument. + + Instead of fixing the comparison in virtio_queue_notify(), move the + comparison to the virtio bindings (just like VIRTIO_PCI_QUEUE_SEL) where + we have a uint32_t value and can avoid ever calling into common virtio + code if the virtqueue number is invalid. + + Signed-off-by: Stefan Hajnoczi + Signed-off-by: Michael S. Tsirkin + +Index: qemu-kvm-0.14.0+noroms/hw/syborg_virtio.c +=================================================================== +--- qemu-kvm-0.14.0+noroms.orig/hw/syborg_virtio.c 2011-02-22 07:34:38.000000000 -0600 ++++ qemu-kvm-0.14.0+noroms/hw/syborg_virtio.c 2011-07-05 14:31:57.000000000 -0500 +@@ -147,7 +147,9 @@ + vdev->queue_sel = value; + break; + case SYBORG_VIRTIO_QUEUE_NOTIFY: +- virtio_queue_notify(vdev, value); ++ if (value < VIRTIO_PCI_QUEUE_MAX) { ++ virtio_queue_notify(vdev, value); ++ } + break; + case SYBORG_VIRTIO_STATUS: + virtio_set_status(vdev, value & 0xFF); +Index: qemu-kvm-0.14.0+noroms/hw/virtio-pci.c +=================================================================== +--- qemu-kvm-0.14.0+noroms.orig/hw/virtio-pci.c 2011-02-22 07:34:38.000000000 -0600 ++++ qemu-kvm-0.14.0+noroms/hw/virtio-pci.c 2011-07-05 14:31:57.000000000 -0500 +@@ -355,7 +355,9 @@ + vdev->queue_sel = val; + break; + case VIRTIO_PCI_QUEUE_NOTIFY: +- virtio_queue_notify(vdev, val); ++ if (val < VIRTIO_PCI_QUEUE_MAX) { ++ virtio_queue_notify(vdev, val); ++ } + break; + case VIRTIO_PCI_STATUS: + if (!(val & VIRTIO_CONFIG_S_DRIVER_OK)) { +Index: qemu-kvm-0.14.0+noroms/hw/virtio.c +=================================================================== +--- qemu-kvm-0.14.0+noroms.orig/hw/virtio.c 2011-07-05 14:31:57.000000000 -0500 ++++ qemu-kvm-0.14.0+noroms/hw/virtio.c 2011-07-05 14:31:57.000000000 -0500 +@@ -596,9 +596,7 @@ + + void virtio_queue_notify(VirtIODevice *vdev, int n) + { +- if (n < VIRTIO_PCI_QUEUE_MAX) { +- virtio_queue_notify_vq(&vdev->vq[n]); +- } ++ virtio_queue_notify_vq(&vdev->vq[n]); + } + + uint16_t virtio_queue_vector(VirtIODevice *vdev, int n) diff -Nru qemu-kvm-0.12.5+noroms/debian/qemu-common.install qemu-kvm-0.14.1/debian/qemu-common.install --- qemu-kvm-0.12.5+noroms/debian/qemu-common.install 2011-07-27 16:49:17.000000000 +0000 +++ qemu-kvm-0.14.1/debian/qemu-common.install 2011-07-27 16:49:18.000000000 +0000 @@ -1,5 +1,5 @@ -pc-bios/keymaps usr/share/qemu -pc-bios/optionrom/*.bin usr/share/qemu -kvm/scripts/qemu-ifup usr/bin debian/qemu-ifdown usr/bin debian/source_qemu-kvm.py usr/share/apport/package-hooks/ +kvm/scripts/qemu-ifup usr/bin +pc-bios/keymaps usr/share/qemu +pc-bios/optionrom/*.bin usr/share/qemu diff -Nru qemu-kvm-0.12.5+noroms/debian/qemu-common.links qemu-kvm-0.14.1/debian/qemu-common.links --- qemu-kvm-0.12.5+noroms/debian/qemu-common.links 2011-07-27 16:49:17.000000000 +0000 +++ qemu-kvm-0.14.1/debian/qemu-common.links 2011-07-27 16:49:18.000000000 +0000 @@ -3,3 +3,6 @@ usr/share/seabios/bios.bin usr/share/qemu/bios.bin usr/share/vgabios/vgabios.bin usr/share/qemu/vgabios.bin usr/share/vgabios/vgabios.cirrus.bin usr/share/qemu/vgabios-cirrus.bin +usr/share/vgabios/vgabios.qxl.bin usr/share/qemu/vgabios-qxl.bin +usr/share/vgabios/vgabios.stdvga.bin usr/share/qemu/vgabios-stdvga.bin +usr/share/vgabios/vgabios.vmware.bin usr/share/qemu/vgabios-vmware.bin diff -Nru qemu-kvm-0.12.5+noroms/debian/qemu-debootstrap qemu-kvm-0.14.1/debian/qemu-debootstrap --- qemu-kvm-0.12.5+noroms/debian/qemu-debootstrap 2011-07-27 16:49:17.000000000 +0000 +++ qemu-kvm-0.14.1/debian/qemu-debootstrap 2011-07-27 16:49:18.000000000 +0000 @@ -71,7 +71,7 @@ if [ "`basename "$0"`" = "build-arm-chroot" ]; then deb_arch="armel" log "Setting Debian architecture to armel" - log "W:" "$0 is deprecated, please use qemu-debootstrap" + warn "$0 is deprecated, please use qemu-debootstrap" fi opts="" @@ -110,7 +110,9 @@ eval $stage=\"\$1\" args="$args $(escape "$1")" else - die "option %s may not be empty" "$stage" + if [ $stage != script ]; then + die "option %s may not be empty" "$stage" + fi fi shift ;; diff -Nru qemu-kvm-0.12.5+noroms/debian/qemu-kvm.1 qemu-kvm-0.14.1/debian/qemu-kvm.1 --- qemu-kvm-0.12.5+noroms/debian/qemu-kvm.1 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/debian/qemu-kvm.1 2011-07-27 16:49:18.000000000 +0000 @@ -0,0 +1,38 @@ +.\" $Id: qemu-kvm.1 376 2009-03-03 20:45:06Z aurel32 $ +.TH qemu\-kvm 1 2007-02-08 "0.9.0" Debian +.SH NAME +qemu\-kvm \- QEMU User Emulator +.SH SYNOPSIS +.B qemu\-kvm +.RI [ options ] +.I program +.RI [ program-arguments... ] +.SH DESCRIPTION +The +.B qemu\-kvm +emulator can run binaries for other architectures but with the same operating +system as the current one. +.SH OPTIONS +.TP +.BR \-h +Print this help. +.TP +.BR \-g +Wait gdb connection to port 1234. +.TP +.BR \-L " \fI\fP" +Set the elf interpreter prefix (default=\fI/usr/gnemul/qemu\-arm\fP). +.TP +.BR \-s " \fI\fP" +Set the stack size in bytes (default=\fI524288\fP). +.TP +.BR \-d " \fI\fP" +Activate log (logfile=\fI/tmp/qemu.log\fP) +.TP +.BR \-p " \fI\fP" +Set the host page size to 'pagesize'. +.SH SEE ALSO +.BR qemu (1), +.BR qemu\-img (1). +.SH AUTHOR +This manual page was written by Guillem Jover . diff -Nru qemu-kvm-0.12.5+noroms/debian/qemu-kvm.default qemu-kvm-0.14.1/debian/qemu-kvm.default --- qemu-kvm-0.12.5+noroms/debian/qemu-kvm.default 2011-07-27 16:49:17.000000000 +0000 +++ qemu-kvm-0.14.1/debian/qemu-kvm.default 2011-07-27 16:49:18.000000000 +0000 @@ -2,4 +2,4 @@ # sudo restart qemu-kvm KSM_ENABLED=1 -#SLEEP_MILLISECS=2000 +SLEEP_MILLISECS=200 diff -Nru qemu-kvm-0.12.5+noroms/debian/qemu-kvm.install qemu-kvm-0.14.1/debian/qemu-kvm.install --- qemu-kvm-0.12.5+noroms/debian/qemu-kvm.install 2011-07-27 16:49:17.000000000 +0000 +++ qemu-kvm-0.14.1/debian/qemu-kvm.install 2011-07-27 16:49:18.000000000 +0000 @@ -1,2 +1 @@ kvm/kvm_stat usr/bin -kvm/user/kvmtrace_format usr/bin diff -Nru qemu-kvm-0.12.5+noroms/debian/qemu-kvm.links qemu-kvm-0.14.1/debian/qemu-kvm.links --- qemu-kvm-0.12.5+noroms/debian/qemu-kvm.links 2011-07-27 16:49:17.000000000 +0000 +++ qemu-kvm-0.14.1/debian/qemu-kvm.links 2011-07-27 16:49:18.000000000 +0000 @@ -5,8 +5,8 @@ usr/share/qemu usr/share/qemu-kvm usr/share/qemu usr/share/kvm usr/share/doc/qemu usr/share/doc/kvm -usr/share/man/man1/qemu-user.1 usr/share/man/man1/qemu-i386.1 -usr/share/man/man1/qemu-user.1 usr/share/man/man1/qemu-x86_64.1 +usr/share/man/man1/qemu-kvm.1 usr/share/man/man1/qemu-i386.1 +usr/share/man/man1/qemu-kvm.1 usr/share/man/man1/qemu-x86_64.1 usr/share/man/man1/qemu.1 usr/share/man/man1/qemu-system-i386.1 usr/share/man/man1/qemu.1 usr/share/man/man1/qemu-system-x86_64.1 usr/share/man/man1/qemu.1 usr/share/man/man1/kvm.1 diff -Nru qemu-kvm-0.12.5+noroms/debian/qemu-kvm.manpages qemu-kvm-0.14.1/debian/qemu-kvm.manpages --- qemu-kvm-0.12.5+noroms/debian/qemu-kvm.manpages 2011-07-27 16:49:17.000000000 +0000 +++ qemu-kvm-0.14.1/debian/qemu-kvm.manpages 2011-07-27 16:49:18.000000000 +0000 @@ -1 +1 @@ -debian/qemu-user.1 +debian/qemu-kvm.1 diff -Nru qemu-kvm-0.12.5+noroms/debian/qemu-kvm.postinst qemu-kvm-0.14.1/debian/qemu-kvm.postinst --- qemu-kvm-0.12.5+noroms/debian/qemu-kvm.postinst 2011-07-27 16:49:17.000000000 +0000 +++ qemu-kvm-0.14.1/debian/qemu-kvm.postinst 2011-07-27 16:49:18.000000000 +0000 @@ -3,10 +3,6 @@ set -e if [ "$1" = configure ]; then - # Add the kvm group unless it's already there - if ! getent group kvm >/dev/null; then - addgroup --quiet --system kvm || true - fi # Clean up old kvm confiles from jaunty-era kvm if [ -n "$2" ] && dpkg --compare-versions "$2" lt 0.12.3-0ubuntu6; then for i in "/lib/udev/rules.d/45-kvm.rules" "/etc/init.d/kvm" "/etc/kvm/kvm-ifup"; do diff -Nru qemu-kvm-0.12.5+noroms/debian/qemu-kvm.preinst qemu-kvm-0.14.1/debian/qemu-kvm.preinst --- qemu-kvm-0.12.5+noroms/debian/qemu-kvm.preinst 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/debian/qemu-kvm.preinst 2011-07-27 16:49:18.000000000 +0000 @@ -0,0 +1,10 @@ +#!/bin/sh + +set -e + +# Add the kvm group unless it's already there +if ! getent group kvm >/dev/null; then +addgroup --quiet --system kvm +fi + +#DEBHELPER# diff -Nru qemu-kvm-0.12.5+noroms/debian/qemu-kvm.upstart qemu-kvm-0.14.1/debian/qemu-kvm.upstart --- qemu-kvm-0.12.5+noroms/debian/qemu-kvm.upstart 2011-07-27 16:49:17.000000000 +0000 +++ qemu-kvm-0.14.1/debian/qemu-kvm.upstart 2011-07-27 16:49:18.000000000 +0000 @@ -6,6 +6,10 @@ start on runlevel [2345] pre-start script + # Silently exit if the package isn't installed anymore + if [ ! -e /usr/bin/kvm ]; then + exit 0 + fi # Load the appropriate module, respecting blacklists if grep -qs "^flags.* vmx" /proc/cpuinfo; then modprobe -b kvm_intel diff -Nru qemu-kvm-0.12.5+noroms/debian/rules qemu-kvm-0.14.1/debian/rules --- qemu-kvm-0.12.5+noroms/debian/rules 2011-07-27 16:49:17.000000000 +0000 +++ qemu-kvm-0.14.1/debian/rules 2011-07-27 16:49:18.000000000 +0000 @@ -3,14 +3,12 @@ include /usr/share/quilt/quilt.make DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE) +DEB_HOST_ARCH_CPU ?= $(shell dpkg-architecture -qDEB_HOST_ARCH_CPU) DEB_HOST_ARCH_OS ?= $(shell dpkg-architecture -qDEB_HOST_ARCH_OS) +DEB_HOST_ARCH ?= $(shell dpkg-architecture -qDEB_HOST_ARCH) DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE) DEB_BUILD_ARCH ?= $(shell dpkg-architecture -qDEB_BUILD_ARCH) -ifneq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS))) - INSTALL_PROGRAM += -s -endif - # Handle DEB_BUILD_OPTIONS=parallel=N , := , ifneq (,$(filter parallel=%,$(subst $(,), ,$(DEB_BUILD_OPTIONS)))) @@ -21,101 +19,44 @@ ifeq ($(DEB_HOST_ARCH_OS),linux) # Note: We differ from Debian here by favoring the pulseaudio driver, since that's default in Ubuntu. # Users override this when calling qemu by exporting the environment variable QEMU_AUDIO_DRV. -conf_arch += --audio-drv-list="pa,alsa,sdl,oss" --enable-vnc-sasl +conf_arch += --audio-drv-list="pa,alsa,sdl,oss" --enable-vnc-sasl --enable-docs endif ifeq ($(DEB_HOST_ARCH_OS),kfreebsd) conf_arch += --audio-drv-list=oss,sdl,pa endif -ifeq ($(DEB_HOST_ARCH),powerpc) +ifeq ($(filter $(DEB_HOST_ARCH),i386 amd64 lpia),) conf_arch += --disable-kvm endif debsrc_ver := $(shell dpkg-parsechangelog | sed -n -e 's/^Version: //p' | perl -pe 's/-[^-]+$$//o') debian_rev := $(shell dpkg-parsechangelog | sed -n -e 's/^Version: //p' | perl -pe 's/.*-//o') -STATIC_BUILDDIR := static-build - CFLAGS += -Wall -g -O$(if $(findstring noopt,$(DEB_BUILD_OPTIONS)),0,2) -# binfmt support -ALL_BINFMT_TARGETS := alpha arm armeb cris i386 m68k microblaze mips mipsel ppc ppc64 ppc64abi32 sh4 sh4eb sparc sparc32plus sparc64 x86_64 -ifeq ($(DEB_HOST_ARCH),amd64) -BINFMT_TARGETS := $(filter-out i386 x86_64, $(ALL_BINFMT_TARGETS)) -endif -ifneq (,$(findstring $(DEB_HOST_ARCH), arm armel)) -BINFMT_TARGETS := $(filter-out arm, $(ALL_BINFMT_TARGETS)) -endif -ifeq ($(DEB_HOST_ARCH),i386) -BINFMT_TARGETS := $(filter-out i386, $(ALL_BINFMT_TARGETS)) -endif -ifeq ($(DEB_HOST_ARCH),powerpc) -BINFMT_TARGETS := $(filter-out ppc, $(ALL_BINFMT_TARGETS)) -endif -ifeq ($(DEB_HOST_ARCH),ppc64) -BINFMT_TARGETS := $(filter-out ppc ppc64 ppc64abi32 , $(ALL_BINFMT_TARGETS)) -endif -ifeq ($(DEB_HOST_ARCH),sparc) -BINFMT_TARGETS := $(filter-out sparc sparc32plus sparc64, $(ALL_BINFMT_TARGETS)) -endif -ifeq ($(DEB_HOST_ARCH),sparc64) -BINFMT_TARGETS := $(filter-out sparc sparc32plus sparc64, $(ALL_BINFMT_TARGETS)) -endif -BINFMT_TARGETS ?= $(ALL_BINFMT_TARGETS) - -ifeq ($(filter qemu-kvm-extras-static,$(shell dh_listpackages -s)),) -DO_STATIC := no -MAYBE_STATIC_CONFIG := -else -DO_STATIC := yes -MAYBE_STATIC_CONFIG := $(STATIC_BUILDDIR)/config-host.mak -endif - -ifeq ($(filter qemu-kvm,$(shell dh_listpackages -s)),) -DO_KVM := no -else -DO_KVM := yes +ifeq ($(DEB_HOST_ARCH_CPU),arm) +CFLAGS += -fno-var-tracking endif config-host.mak: $(QUILT_STAMPFN) dh_testdir ./configure \ - --target-list="" \ + --target-list="x86_64-softmmu i386-softmmu x86_64-linux-user i386-linux-user" \ --prefix=/usr \ --interp-prefix=/etc/qemu-binfmt/%M \ --disable-blobs \ --disable-strip \ $(conf_arch) -$(STATIC_BUILDDIR)/config-host.mak: $(QUILT_STAMPFN) - dh_testdir - rm -rf $(STATIC_BUILDDIR) - mkdir -p $(STATIC_BUILDDIR) - #cp -a $(filter-out config-host.mak debian $(STATIC_BUILDDIR),$(wildcard *)) $(STATIC_BUILDDIR) - cd $(STATIC_BUILDDIR) && \ - ../configure \ - --target-list="" \ - --prefix=/usr \ - --interp-prefix=/etc/qemu-binfmt/%M \ - --disable-blobs \ - --disable-strip \ - --disable-system \ - --static \ - $(conf_arch) - build: build-stamp -build-stamp: config-host.mak $(MAYBE_STATIC_CONFIG) +build-stamp: config-host.mak dh_testdir -ifeq ($(DO_STATIC),yes) - $(MAKE) -C $(STATIC_BUILDDIR) -endif touch $@ clean: dh_testdir rm -f install-stamp build-stamp -ifeq ($(DO_STATIC),yes) - rm -rf $(STATIC_BUILDDIR) -endif + # Clean up some upstream build cruft + rm -f pc-bios/\*.bin pc-bios/\*.dtb pc-bios/openbios-\* roms/seabios/Makefile roms/vgabios/Makefile [ ! -f config-host.mak ] || $(MAKE) distclean $(MAKE) -f debian/rules unpatch rm -f kvm/extboot/*.o kvm/extboot/extboot.img kvm/extboot/signrom config.mak kvm/user/config.mak kvm/user/test/lib/.*.d kvm/user/test/lib/*/.*.d kvm/bios/acpi-dsdt.aml kvm/bios/acpi-ssdt.aml qemu-monitor.texi @@ -128,26 +69,7 @@ dh_clean -k dh_installdirs # Build & install normally -ifeq ($(DO_KVM),yes) - $(MAKE) -C kvm/libkvm DESTDIR=$(CURDIR)/debian/qemu-kvm install -endif $(MAKE) DESTDIR=$(CURDIR)/debian/qemu-kvm install - # Move the extras binaries into place - for i in alpha arm armeb cris m68k microblaze mips mipsel ppc ppc64 ppc64abi32 sh4 sh4eb sparc sparc32plus sparc64 system-arm system-cris system-m68k system-microblaze system-mips system-mips64 system-mips64el system-mipsel system-ppc system-ppc64 system-ppcemb system-sh4 system-sh4eb system-sparc system-sparc64; do \ - mv debian/qemu-kvm/usr/bin/qemu-$$i debian/qemu-kvm-extras/usr/bin/qemu-$$i; \ - done -ifeq ($(DO_STATIC),yes) - for target in $(STATIC_BUILDDIR)/*-*-user/qemu-*; do \ - install -m 755 $$target debian/qemu-kvm-extras-static/usr/bin/$$(basename "$$target")-static; \ - done - for target in $(BINFMT_TARGETS); do \ - install -m 644 $(CURDIR)/debian/binfmts/qemu-$$target \ - $(CURDIR)/debian/qemu-kvm-extras-static/usr/share/binfmts; \ - done - cp debian/qemu-debootstrap debian/qemu-kvm-extras-static/usr/bin/ - ln -s qemu-debootstrap debian/qemu-kvm-extras-static/usr/bin/build-arm-chroot - f=debian/qemu-kvm-extras-static.sysctl; [ ! -f debian/qemu-kvm-extras-static.sysctl.$(DEB_BUILD_ARCH) ] || f=debian/qemu-kvm-extras-static.sysctl.$(DEB_BUILD_ARCH); cp $$f debian/qemu-kvm-extras-static/etc/sysctl.d/30-qemu-kvm-extras-static.conf -endif touch $@ binary-indep: @@ -184,9 +106,6 @@ dh_shlibdeps -s dh_gencontrol -s -Nkvm dh_gencontrol -pkvm -- -v1:84+dfsg-0ubuntu16+$(debsrc_ver)+$(debian_rev) -ifeq ($(DO_STATIC),yes) - sed -i 's/@BINFMT_TARGETS@/$(BINFMT_TARGETS)/g' debian/qemu-kvm-extras-static/DEBIAN/* -endif # Prune keymaps from qemu-kvm, as these are now in qemu-common rm -rf debian/qemu-kvm/usr/share/qemu/keymaps dh_md5sums -s diff -Nru qemu-kvm-0.12.5+noroms/default-configs/arm-softmmu.mak qemu-kvm-0.14.1/default-configs/arm-softmmu.mak --- qemu-kvm-0.12.5+noroms/default-configs/arm-softmmu.mak 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/default-configs/arm-softmmu.mak 2011-05-11 13:29:46.000000000 +0000 @@ -1,10 +1,11 @@ # Default configuration for arm-softmmu +include pci.mak CONFIG_GDBSTUB_XML=y -CONFIG_USB_OHCI=y CONFIG_ISA_MMIO=y CONFIG_NAND=y CONFIG_ECC=y +CONFIG_SERIAL=y CONFIG_PTIMER=y CONFIG_SD=y CONFIG_MAX7310=y @@ -14,8 +15,8 @@ CONFIG_LM832X=y CONFIG_TMP105=y CONFIG_STELLARIS_INPUT=y -CONFIG_SD0303=y -CONFIG_SD0323=y +CONFIG_SSD0303=y +CONFIG_SSD0323=y CONFIG_ADS7846=y CONFIG_MAX111X=y CONFIG_SSI=y @@ -23,3 +24,5 @@ CONFIG_LAN9118=y CONFIG_SMC91C111=y CONFIG_DS1338=y +CONFIG_PFLASH_CFI01=y +CONFIG_PFLASH_CFI02=y diff -Nru qemu-kvm-0.12.5+noroms/default-configs/cris-softmmu.mak qemu-kvm-0.14.1/default-configs/cris-softmmu.mak --- qemu-kvm-0.12.5+noroms/default-configs/cris-softmmu.mak 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/default-configs/cris-softmmu.mak 2011-05-11 13:29:46.000000000 +0000 @@ -2,3 +2,4 @@ CONFIG_NAND=y CONFIG_PTIMER=y +CONFIG_PFLASH_CFI02=y diff -Nru qemu-kvm-0.12.5+noroms/default-configs/i386-softmmu.mak qemu-kvm-0.14.1/default-configs/i386-softmmu.mak --- qemu-kvm-0.12.5+noroms/default-configs/i386-softmmu.mak 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/default-configs/i386-softmmu.mak 2011-05-11 13:29:46.000000000 +0000 @@ -1 +1,20 @@ # Default configuration for i386-softmmu + +include pci.mak +CONFIG_VGA_PCI=y +CONFIG_VGA_ISA=y +CONFIG_VMWARE_VGA=y +CONFIG_SERIAL=y +CONFIG_PARALLEL=y +CONFIG_I8254=y +CONFIG_PCSPK=y +CONFIG_PCKBD=y +CONFIG_FDC=y +CONFIG_ACPI=y +CONFIG_APM=y +CONFIG_DMA=y +CONFIG_IDE_ISA=y +CONFIG_IDE_PIIX=y +CONFIG_NE2000_ISA=y +CONFIG_PIIX_PCI=y +CONFIG_SOUND=y diff -Nru qemu-kvm-0.12.5+noroms/default-configs/m68k-softmmu.mak qemu-kvm-0.14.1/default-configs/m68k-softmmu.mak --- qemu-kvm-0.12.5+noroms/default-configs/m68k-softmmu.mak 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/default-configs/m68k-softmmu.mak 2011-05-11 13:29:46.000000000 +0000 @@ -1,4 +1,5 @@ # Default configuration for m68k-softmmu +include pci.mak CONFIG_GDBSTUB_XML=y CONFIG_PTIMER=y diff -Nru qemu-kvm-0.12.5+noroms/default-configs/microblaze-softmmu.mak qemu-kvm-0.14.1/default-configs/microblaze-softmmu.mak --- qemu-kvm-0.12.5+noroms/default-configs/microblaze-softmmu.mak 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/default-configs/microblaze-softmmu.mak 2011-05-11 13:29:46.000000000 +0000 @@ -1,3 +1,4 @@ # Default configuration for microblaze-softmmu CONFIG_PTIMER=y +CONFIG_PFLASH_CFI01=y diff -Nru qemu-kvm-0.12.5+noroms/default-configs/mips64el-softmmu.mak qemu-kvm-0.14.1/default-configs/mips64el-softmmu.mak --- qemu-kvm-0.12.5+noroms/default-configs/mips64el-softmmu.mak 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/default-configs/mips64el-softmmu.mak 2011-05-11 13:29:46.000000000 +0000 @@ -1,4 +1,30 @@ # Default configuration for mips64el-softmmu +include pci.mak CONFIG_ISA_MMIO=y CONFIG_ESP=y +CONFIG_VGA_PCI=y +CONFIG_VGA_ISA=y +CONFIG_VGA_ISA_MM=y +CONFIG_VMWARE_VGA=y +CONFIG_SERIAL=y +CONFIG_PARALLEL=y +CONFIG_I8254=y +CONFIG_PCSPK=y +CONFIG_PCKBD=y +CONFIG_FDC=y +CONFIG_ACPI=y +CONFIG_APM=y +CONFIG_DMA=y +CONFIG_PIIX4=y +CONFIG_IDE_ISA=y +CONFIG_IDE_PIIX=y +CONFIG_IDE_VIA=y +CONFIG_NE2000_ISA=y +CONFIG_SOUND=y +CONFIG_RC4030=y +CONFIG_DP8393X=y +CONFIG_DS1225Y=y +CONFIG_MIPSNET=y +CONFIG_PFLASH_CFI01=y +CONFIG_FULONG=y diff -Nru qemu-kvm-0.12.5+noroms/default-configs/mips64-softmmu.mak qemu-kvm-0.14.1/default-configs/mips64-softmmu.mak --- qemu-kvm-0.12.5+noroms/default-configs/mips64-softmmu.mak 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/default-configs/mips64-softmmu.mak 2011-05-11 13:29:46.000000000 +0000 @@ -1,4 +1,28 @@ # Default configuration for mips64-softmmu +include pci.mak CONFIG_ISA_MMIO=y CONFIG_ESP=y +CONFIG_VGA_PCI=y +CONFIG_VGA_ISA=y +CONFIG_VGA_ISA_MM=y +CONFIG_VMWARE_VGA=y +CONFIG_SERIAL=y +CONFIG_PARALLEL=y +CONFIG_I8254=y +CONFIG_PCSPK=y +CONFIG_PCKBD=y +CONFIG_FDC=y +CONFIG_ACPI=y +CONFIG_APM=y +CONFIG_DMA=y +CONFIG_PIIX4=y +CONFIG_IDE_ISA=y +CONFIG_IDE_PIIX=y +CONFIG_NE2000_ISA=y +CONFIG_SOUND=y +CONFIG_RC4030=y +CONFIG_DP8393X=y +CONFIG_DS1225Y=y +CONFIG_MIPSNET=y +CONFIG_PFLASH_CFI01=y diff -Nru qemu-kvm-0.12.5+noroms/default-configs/mipsel-softmmu.mak qemu-kvm-0.14.1/default-configs/mipsel-softmmu.mak --- qemu-kvm-0.12.5+noroms/default-configs/mipsel-softmmu.mak 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/default-configs/mipsel-softmmu.mak 2011-05-11 13:29:46.000000000 +0000 @@ -1,4 +1,28 @@ # Default configuration for mipsel-softmmu +include pci.mak CONFIG_ISA_MMIO=y CONFIG_ESP=y +CONFIG_VGA_PCI=y +CONFIG_VGA_ISA=y +CONFIG_VGA_ISA_MM=y +CONFIG_VMWARE_VGA=y +CONFIG_SERIAL=y +CONFIG_PARALLEL=y +CONFIG_I8254=y +CONFIG_PCSPK=y +CONFIG_PCKBD=y +CONFIG_FDC=y +CONFIG_ACPI=y +CONFIG_APM=y +CONFIG_DMA=y +CONFIG_PIIX4=y +CONFIG_IDE_ISA=y +CONFIG_IDE_PIIX=y +CONFIG_NE2000_ISA=y +CONFIG_SOUND=y +CONFIG_RC4030=y +CONFIG_DP8393X=y +CONFIG_DS1225Y=y +CONFIG_MIPSNET=y +CONFIG_PFLASH_CFI01=y diff -Nru qemu-kvm-0.12.5+noroms/default-configs/mips-softmmu.mak qemu-kvm-0.14.1/default-configs/mips-softmmu.mak --- qemu-kvm-0.12.5+noroms/default-configs/mips-softmmu.mak 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/default-configs/mips-softmmu.mak 2011-05-11 13:29:46.000000000 +0000 @@ -1,4 +1,28 @@ # Default configuration for mips-softmmu +include pci.mak CONFIG_ISA_MMIO=y CONFIG_ESP=y +CONFIG_VGA_PCI=y +CONFIG_VGA_ISA=y +CONFIG_VGA_ISA_MM=y +CONFIG_VMWARE_VGA=y +CONFIG_SERIAL=y +CONFIG_PARALLEL=y +CONFIG_I8254=y +CONFIG_PCSPK=y +CONFIG_PCKBD=y +CONFIG_FDC=y +CONFIG_ACPI=y +CONFIG_APM=y +CONFIG_DMA=y +CONFIG_PIIX4=y +CONFIG_IDE_ISA=y +CONFIG_IDE_PIIX=y +CONFIG_NE2000_ISA=y +CONFIG_SOUND=y +CONFIG_RC4030=y +CONFIG_DP8393X=y +CONFIG_DS1225Y=y +CONFIG_MIPSNET=y +CONFIG_PFLASH_CFI01=y diff -Nru qemu-kvm-0.12.5+noroms/default-configs/pci.mak qemu-kvm-0.14.1/default-configs/pci.mak --- qemu-kvm-0.12.5+noroms/default-configs/pci.mak 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/default-configs/pci.mak 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,16 @@ +CONFIG_PCI=y +CONFIG_VIRTIO_PCI=y +CONFIG_VIRTIO=y +CONFIG_USB_UHCI=y +CONFIG_USB_OHCI=y +CONFIG_NE2000_PCI=y +CONFIG_EEPRO100_PCI=y +CONFIG_PCNET_PCI=y +CONFIG_PCNET_COMMON=y +CONFIG_LSI_SCSI_PCI=y +CONFIG_RTL8139_PCI=y +CONFIG_E1000_PCI=y +CONFIG_IDE_CORE=y +CONFIG_IDE_QDEV=y +CONFIG_IDE_PCI=y +CONFIG_AHCI=y diff -Nru qemu-kvm-0.12.5+noroms/default-configs/ppc64-softmmu.mak qemu-kvm-0.14.1/default-configs/ppc64-softmmu.mak --- qemu-kvm-0.12.5+noroms/default-configs/ppc64-softmmu.mak 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/default-configs/ppc64-softmmu.mak 2011-05-11 13:29:46.000000000 +0000 @@ -1,7 +1,33 @@ # Default configuration for ppc64-softmmu +include pci.mak CONFIG_GDBSTUB_XML=y -CONFIG_USB_OHCI=y CONFIG_ISA_MMIO=y CONFIG_ESCC=y CONFIG_M48T59=y +CONFIG_VGA_PCI=y +CONFIG_SERIAL=y +CONFIG_I8254=y +CONFIG_PCKBD=y +CONFIG_FDC=y +CONFIG_DMA=y +CONFIG_OPENPIC=y +CONFIG_PREP_PCI=y +CONFIG_MACIO=y +CONFIG_CUDA=y +CONFIG_ADB=y +CONFIG_MAC_NVRAM=y +CONFIG_MAC_DBDMA=y +CONFIG_HEATHROW_PIC=y +CONFIG_GRACKLE_PCI=y +CONFIG_UNIN_PCI=y +CONFIG_DEC_PCI=y +CONFIG_PPCE500_PCI=y +CONFIG_IDE_ISA=y +CONFIG_IDE_CMD646=y +CONFIG_IDE_MACIO=y +CONFIG_NE2000_ISA=y +CONFIG_SOUND=y +CONFIG_PFLASH_CFI01=y +CONFIG_PFLASH_CFI02=y +CONFIG_PTIMER=y diff -Nru qemu-kvm-0.12.5+noroms/default-configs/ppcemb-softmmu.mak qemu-kvm-0.14.1/default-configs/ppcemb-softmmu.mak --- qemu-kvm-0.12.5+noroms/default-configs/ppcemb-softmmu.mak 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/default-configs/ppcemb-softmmu.mak 2011-05-11 13:29:46.000000000 +0000 @@ -1,7 +1,33 @@ # Default configuration for ppcemb-softmmu +include pci.mak CONFIG_GDBSTUB_XML=y -CONFIG_USB_OHCI=y CONFIG_ISA_MMIO=y CONFIG_ESCC=y CONFIG_M48T59=y +CONFIG_VGA_PCI=y +CONFIG_SERIAL=y +CONFIG_I8254=y +CONFIG_PCKBD=y +CONFIG_FDC=y +CONFIG_DMA=y +CONFIG_OPENPIC=y +CONFIG_PREP_PCI=y +CONFIG_MACIO=y +CONFIG_CUDA=y +CONFIG_ADB=y +CONFIG_MAC_NVRAM=y +CONFIG_MAC_DBDMA=y +CONFIG_HEATHROW_PIC=y +CONFIG_GRACKLE_PCI=y +CONFIG_UNIN_PCI=y +CONFIG_DEC_PCI=y +CONFIG_PPCE500_PCI=y +CONFIG_IDE_ISA=y +CONFIG_IDE_CMD646=y +CONFIG_IDE_MACIO=y +CONFIG_NE2000_ISA=y +CONFIG_SOUND=y +CONFIG_PFLASH_CFI01=y +CONFIG_PFLASH_CFI02=y +CONFIG_PTIMER=y diff -Nru qemu-kvm-0.12.5+noroms/default-configs/ppc-softmmu.mak qemu-kvm-0.14.1/default-configs/ppc-softmmu.mak --- qemu-kvm-0.12.5+noroms/default-configs/ppc-softmmu.mak 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/default-configs/ppc-softmmu.mak 2011-05-11 13:29:46.000000000 +0000 @@ -1,7 +1,33 @@ # Default configuration for ppc-softmmu +include pci.mak CONFIG_GDBSTUB_XML=y -CONFIG_USB_OHCI=y CONFIG_ISA_MMIO=y CONFIG_ESCC=y CONFIG_M48T59=y +CONFIG_VGA_PCI=y +CONFIG_SERIAL=y +CONFIG_I8254=y +CONFIG_PCKBD=y +CONFIG_FDC=y +CONFIG_DMA=y +CONFIG_OPENPIC=y +CONFIG_PREP_PCI=y +CONFIG_MACIO=y +CONFIG_CUDA=y +CONFIG_ADB=y +CONFIG_MAC_NVRAM=y +CONFIG_MAC_DBDMA=y +CONFIG_HEATHROW_PIC=y +CONFIG_GRACKLE_PCI=y +CONFIG_UNIN_PCI=y +CONFIG_DEC_PCI=y +CONFIG_PPCE500_PCI=y +CONFIG_IDE_ISA=y +CONFIG_IDE_CMD646=y +CONFIG_IDE_MACIO=y +CONFIG_NE2000_ISA=y +CONFIG_SOUND=y +CONFIG_PFLASH_CFI01=y +CONFIG_PFLASH_CFI02=y +CONFIG_PTIMER=y diff -Nru qemu-kvm-0.12.5+noroms/default-configs/s390x-softmmu.mak qemu-kvm-0.14.1/default-configs/s390x-softmmu.mak --- qemu-kvm-0.12.5+noroms/default-configs/s390x-softmmu.mak 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/default-configs/s390x-softmmu.mak 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1 @@ +CONFIG_VIRTIO=y diff -Nru qemu-kvm-0.12.5+noroms/default-configs/sh4eb-softmmu.mak qemu-kvm-0.14.1/default-configs/sh4eb-softmmu.mak --- qemu-kvm-0.12.5+noroms/default-configs/sh4eb-softmmu.mak 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/default-configs/sh4eb-softmmu.mak 2011-05-11 13:29:46.000000000 +0000 @@ -1,5 +1,7 @@ # Default configuration for sh4eb-softmmu -CONFIG_USB_OHCI=y +include pci.mak +CONFIG_SERIAL=y CONFIG_PTIMER=y +CONFIG_PFLASH_CFI02=y CONFIG_ISA_MMIO=y diff -Nru qemu-kvm-0.12.5+noroms/default-configs/sh4-softmmu.mak qemu-kvm-0.14.1/default-configs/sh4-softmmu.mak --- qemu-kvm-0.12.5+noroms/default-configs/sh4-softmmu.mak 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/default-configs/sh4-softmmu.mak 2011-05-11 13:29:46.000000000 +0000 @@ -1,5 +1,7 @@ # Default configuration for sh4-softmmu -CONFIG_USB_OHCI=y +include pci.mak +CONFIG_SERIAL=y CONFIG_PTIMER=y +CONFIG_PFLASH_CFI02=y CONFIG_ISA_MMIO=y diff -Nru qemu-kvm-0.12.5+noroms/default-configs/sparc64-softmmu.mak qemu-kvm-0.14.1/default-configs/sparc64-softmmu.mak --- qemu-kvm-0.12.5+noroms/default-configs/sparc64-softmmu.mak 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/default-configs/sparc64-softmmu.mak 2011-05-11 13:29:46.000000000 +0000 @@ -1,5 +1,13 @@ # Default configuration for sparc64-softmmu +include pci.mak CONFIG_ISA_MMIO=y CONFIG_M48T59=y CONFIG_PTIMER=y +CONFIG_VGA_PCI=y +CONFIG_SERIAL=y +CONFIG_PARALLEL=y +CONFIG_PCKBD=y +CONFIG_FDC=y +CONFIG_IDE_ISA=y +CONFIG_IDE_CMD646=y diff -Nru qemu-kvm-0.12.5+noroms/default-configs/sparc-softmmu.mak qemu-kvm-0.14.1/default-configs/sparc-softmmu.mak --- qemu-kvm-0.12.5+noroms/default-configs/sparc-softmmu.mak 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/default-configs/sparc-softmmu.mak 2011-05-11 13:29:46.000000000 +0000 @@ -1,8 +1,10 @@ # Default configuration for sparc-softmmu -CONFIG_QDEV_ADDR=y CONFIG_ECC=y CONFIG_ESP=y CONFIG_ESCC=y CONFIG_M48T59=y CONFIG_PTIMER=y +CONFIG_FDC=y +CONFIG_EMPTY_SLOT=y +CONFIG_PCNET_COMMON=y diff -Nru qemu-kvm-0.12.5+noroms/default-configs/x86_64-softmmu.mak qemu-kvm-0.14.1/default-configs/x86_64-softmmu.mak --- qemu-kvm-0.12.5+noroms/default-configs/x86_64-softmmu.mak 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/default-configs/x86_64-softmmu.mak 2011-05-11 13:29:46.000000000 +0000 @@ -1 +1,20 @@ # Default configuration for x86_64-softmmu + +include pci.mak +CONFIG_VGA_PCI=y +CONFIG_VGA_ISA=y +CONFIG_VMWARE_VGA=y +CONFIG_SERIAL=y +CONFIG_PARALLEL=y +CONFIG_I8254=y +CONFIG_PCSPK=y +CONFIG_PCKBD=y +CONFIG_FDC=y +CONFIG_ACPI=y +CONFIG_APM=y +CONFIG_DMA=y +CONFIG_IDE_ISA=y +CONFIG_IDE_PIIX=y +CONFIG_NE2000_ISA=y +CONFIG_PIIX_PCI=y +CONFIG_SOUND=y diff -Nru qemu-kvm-0.12.5+noroms/def-helper.h qemu-kvm-0.14.1/def-helper.h --- qemu-kvm-0.12.5+noroms/def-helper.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/def-helper.h 2011-05-11 13:29:46.000000000 +0000 @@ -81,9 +81,29 @@ #define dh_is_64bit_ptr (TCG_TARGET_REG_BITS == 64) #define dh_is_64bit(t) glue(dh_is_64bit_, dh_alias(t)) +#define dh_is_signed_void 0 +#define dh_is_signed_i32 0 +#define dh_is_signed_s32 1 +#define dh_is_signed_i64 0 +#define dh_is_signed_s64 1 +#define dh_is_signed_f32 0 +#define dh_is_signed_f64 0 +#define dh_is_signed_tl 0 +#define dh_is_signed_int 1 +/* ??? This is highly specific to the host cpu. There are even special + extension instructions that may be required, e.g. ia64's addp4. But + for now we don't support any 64-bit targets with 32-bit pointers. */ +#define dh_is_signed_ptr 0 +#define dh_is_signed_env dh_is_signed_ptr +#define dh_is_signed(t) dh_is_signed_##t + +#define dh_sizemask(t, n) \ + sizemask |= dh_is_64bit(t) << (n*2); \ + sizemask |= dh_is_signed(t) << (n*2+1) + #define dh_arg(t, n) \ args[n - 1] = glue(GET_TCGV_, dh_alias(t))(glue(arg, n)); \ - sizemask |= dh_is_64bit(t) << n + dh_sizemask(t, n) #define dh_arg_decl(t, n) glue(TCGv_, dh_alias(t)) glue(arg, n) @@ -138,8 +158,8 @@ static inline void glue(gen_helper_, name)(dh_retvar_decl(ret) dh_arg_decl(t1, 1)) \ { \ TCGArg args[1]; \ - int sizemask; \ - sizemask = dh_is_64bit(ret); \ + int sizemask = 0; \ + dh_sizemask(ret, 0); \ dh_arg(t1, 1); \ tcg_gen_helperN(HELPER(name), flags, sizemask, dh_retvar(ret), 1, args); \ } @@ -149,8 +169,8 @@ dh_arg_decl(t2, 2)) \ { \ TCGArg args[2]; \ - int sizemask; \ - sizemask = dh_is_64bit(ret); \ + int sizemask = 0; \ + dh_sizemask(ret, 0); \ dh_arg(t1, 1); \ dh_arg(t2, 2); \ tcg_gen_helperN(HELPER(name), flags, sizemask, dh_retvar(ret), 2, args); \ @@ -161,8 +181,8 @@ dh_arg_decl(t2, 2), dh_arg_decl(t3, 3)) \ { \ TCGArg args[3]; \ - int sizemask; \ - sizemask = dh_is_64bit(ret); \ + int sizemask = 0; \ + dh_sizemask(ret, 0); \ dh_arg(t1, 1); \ dh_arg(t2, 2); \ dh_arg(t3, 3); \ @@ -174,8 +194,8 @@ dh_arg_decl(t2, 2), dh_arg_decl(t3, 3), dh_arg_decl(t4, 4)) \ { \ TCGArg args[4]; \ - int sizemask; \ - sizemask = dh_is_64bit(ret); \ + int sizemask = 0; \ + dh_sizemask(ret, 0); \ dh_arg(t1, 1); \ dh_arg(t2, 2); \ dh_arg(t3, 3); \ diff -Nru qemu-kvm-0.12.5+noroms/disas.c qemu-kvm-0.14.1/disas.c --- qemu-kvm-0.12.5+noroms/disas.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/disas.c 2011-05-11 13:29:46.000000000 +0000 @@ -73,6 +73,21 @@ return 1; } +bfd_vma bfd_getl64 (const bfd_byte *addr) +{ + unsigned long long v; + + v = (unsigned long long) addr[0]; + v |= (unsigned long long) addr[1] << 8; + v |= (unsigned long long) addr[2] << 16; + v |= (unsigned long long) addr[3] << 24; + v |= (unsigned long long) addr[4] << 32; + v |= (unsigned long long) addr[5] << 40; + v |= (unsigned long long) addr[6] << 48; + v |= (unsigned long long) addr[7] << 56; + return (bfd_vma) v; +} + bfd_vma bfd_getl32 (const bfd_byte *addr) { unsigned long v; @@ -193,8 +208,13 @@ disasm_info.mach = bfd_mach_alpha; print_insn = print_insn_alpha; #elif defined(TARGET_CRIS) - disasm_info.mach = bfd_mach_cris_v32; - print_insn = print_insn_crisv32; + if (flags != 32) { + disasm_info.mach = bfd_mach_cris_v0_v10; + print_insn = print_insn_crisv10; + } else { + disasm_info.mach = bfd_mach_cris_v32; + print_insn = print_insn_crisv32; + } #elif defined(TARGET_MICROBLAZE) disasm_info.mach = bfd_arch_microblaze; print_insn = print_insn_microblaze; @@ -278,6 +298,8 @@ print_insn = print_insn_s390; #elif defined(__hppa__) print_insn = print_insn_hppa; +#elif defined(__ia64__) + print_insn = print_insn_ia64; #else fprintf(out, "0x%lx: Asm output not supported on this arch\n", (long) code); @@ -285,11 +307,6 @@ #endif for (pc = (unsigned long)code; size > 0; pc += count, size -= count) { fprintf(out, "0x%08lx: ", pc); -#ifdef __arm__ - /* since data is included in the code, it is better to - display code data too */ - fprintf(out, "%08x ", (int)bfd_getl32((const bfd_byte *)pc)); -#endif count = print_insn(pc, &disasm_info); fprintf(out, "\n"); if (count < 0) @@ -332,7 +349,8 @@ return 0; } -static int monitor_fprintf(FILE *stream, const char *fmt, ...) +static int GCC_FMT_ATTR(2, 3) +monitor_fprintf(FILE *stream, const char *fmt, ...) { va_list ap; va_start(ap, fmt); diff -Nru qemu-kvm-0.12.5+noroms/disas.h qemu-kvm-0.14.1/disas.h --- qemu-kvm-0.12.5+noroms/disas.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/disas.h 2011-05-11 13:29:46.000000000 +0000 @@ -8,11 +8,8 @@ void disas(FILE *out, void *code, unsigned long size); void target_disas(FILE *out, target_ulong code, target_ulong size, int flags); -/* The usual mess... FIXME: Remove this condition once dyngen-exec.h is gone */ -#ifndef __DYNGEN_EXEC_H__ void monitor_disas(Monitor *mon, CPUState *env, target_ulong pc, int nb_insn, int is_physical, int flags); -#endif /* Look up symbol for debugging purpose. Returns "" if unknown. */ const char *lookup_symbol(target_ulong orig_addr); @@ -22,7 +19,11 @@ struct elf32_sym; struct elf64_sym; +#if defined(CONFIG_USER_ONLY) +typedef const char *(*lookup_symbol_t)(struct syminfo *s, target_ulong orig_addr); +#else typedef const char *(*lookup_symbol_t)(struct syminfo *s, target_phys_addr_t orig_addr); +#endif struct syminfo { lookup_symbol_t lookup_symbol; diff -Nru qemu-kvm-0.12.5+noroms/dis-asm.h qemu-kvm-0.14.1/dis-asm.h --- qemu-kvm-0.12.5+noroms/dis-asm.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/dis-asm.h 2011-05-11 13:29:46.000000000 +0000 @@ -9,11 +9,7 @@ #ifndef DIS_ASM_H #define DIS_ASM_H -#include -#include -#include -#include -#include +#include "qemu-common.h" typedef void *PTR; typedef uint64_t bfd_vma; @@ -219,6 +215,9 @@ #define bfd_mach_cris_v32 32 #define bfd_mach_cris_v10_v32 1032 bfd_arch_microblaze, /* Xilinx MicroBlaze. */ + bfd_arch_ia64, /* HP/Intel ia64 */ +#define bfd_mach_ia64_elf64 64 +#define bfd_mach_ia64_elf32 32 bfd_arch_last }; #define bfd_mach_s390_31 31 @@ -234,8 +233,6 @@ } udata; } asymbol; -typedef int (*fprintf_ftype) (FILE*, const char*, ...); - enum dis_insn_type { dis_noninsn, /* Not a valid instruction */ dis_nonbranch, /* Not a branch instruction */ @@ -258,7 +255,7 @@ by hand, or using one of the initialization macros below. */ typedef struct disassemble_info { - fprintf_ftype fprintf_func; + fprintf_function fprintf_func; FILE *stream; PTR application_data; @@ -365,46 +362,48 @@ target address. Return number of bytes processed. */ typedef int (*disassembler_ftype) (bfd_vma, disassemble_info *); -extern int print_insn_big_mips (bfd_vma, disassemble_info*); -extern int print_insn_little_mips (bfd_vma, disassemble_info*); -extern int print_insn_i386 (bfd_vma, disassemble_info*); -extern int print_insn_m68k (bfd_vma, disassemble_info*); -extern int print_insn_z8001 (bfd_vma, disassemble_info*); -extern int print_insn_z8002 (bfd_vma, disassemble_info*); -extern int print_insn_h8300 (bfd_vma, disassemble_info*); -extern int print_insn_h8300h (bfd_vma, disassemble_info*); -extern int print_insn_h8300s (bfd_vma, disassemble_info*); -extern int print_insn_h8500 (bfd_vma, disassemble_info*); -extern int print_insn_alpha (bfd_vma, disassemble_info*); -extern disassembler_ftype arc_get_disassembler (int, int); -extern int print_insn_arm (bfd_vma, disassemble_info*); -extern int print_insn_sparc (bfd_vma, disassemble_info*); -extern int print_insn_big_a29k (bfd_vma, disassemble_info*); -extern int print_insn_little_a29k (bfd_vma, disassemble_info*); -extern int print_insn_i960 (bfd_vma, disassemble_info*); -extern int print_insn_sh (bfd_vma, disassemble_info*); -extern int print_insn_shl (bfd_vma, disassemble_info*); -extern int print_insn_hppa (bfd_vma, disassemble_info*); -extern int print_insn_m32r (bfd_vma, disassemble_info*); -extern int print_insn_m88k (bfd_vma, disassemble_info*); -extern int print_insn_mn10200 (bfd_vma, disassemble_info*); -extern int print_insn_mn10300 (bfd_vma, disassemble_info*); -extern int print_insn_ns32k (bfd_vma, disassemble_info*); -extern int print_insn_big_powerpc (bfd_vma, disassemble_info*); -extern int print_insn_little_powerpc (bfd_vma, disassemble_info*); -extern int print_insn_rs6000 (bfd_vma, disassemble_info*); -extern int print_insn_w65 (bfd_vma, disassemble_info*); -extern int print_insn_d10v (bfd_vma, disassemble_info*); -extern int print_insn_v850 (bfd_vma, disassemble_info*); -extern int print_insn_tic30 (bfd_vma, disassemble_info*); -extern int print_insn_ppc (bfd_vma, disassemble_info*); -extern int print_insn_s390 (bfd_vma, disassemble_info*); -extern int print_insn_crisv32 (bfd_vma, disassemble_info*); -extern int print_insn_microblaze (bfd_vma, disassemble_info*); +int print_insn_big_mips (bfd_vma, disassemble_info*); +int print_insn_little_mips (bfd_vma, disassemble_info*); +int print_insn_i386 (bfd_vma, disassemble_info*); +int print_insn_m68k (bfd_vma, disassemble_info*); +int print_insn_z8001 (bfd_vma, disassemble_info*); +int print_insn_z8002 (bfd_vma, disassemble_info*); +int print_insn_h8300 (bfd_vma, disassemble_info*); +int print_insn_h8300h (bfd_vma, disassemble_info*); +int print_insn_h8300s (bfd_vma, disassemble_info*); +int print_insn_h8500 (bfd_vma, disassemble_info*); +int print_insn_alpha (bfd_vma, disassemble_info*); +disassembler_ftype arc_get_disassembler (int, int); +int print_insn_arm (bfd_vma, disassemble_info*); +int print_insn_sparc (bfd_vma, disassemble_info*); +int print_insn_big_a29k (bfd_vma, disassemble_info*); +int print_insn_little_a29k (bfd_vma, disassemble_info*); +int print_insn_i960 (bfd_vma, disassemble_info*); +int print_insn_sh (bfd_vma, disassemble_info*); +int print_insn_shl (bfd_vma, disassemble_info*); +int print_insn_hppa (bfd_vma, disassemble_info*); +int print_insn_m32r (bfd_vma, disassemble_info*); +int print_insn_m88k (bfd_vma, disassemble_info*); +int print_insn_mn10200 (bfd_vma, disassemble_info*); +int print_insn_mn10300 (bfd_vma, disassemble_info*); +int print_insn_ns32k (bfd_vma, disassemble_info*); +int print_insn_big_powerpc (bfd_vma, disassemble_info*); +int print_insn_little_powerpc (bfd_vma, disassemble_info*); +int print_insn_rs6000 (bfd_vma, disassemble_info*); +int print_insn_w65 (bfd_vma, disassemble_info*); +int print_insn_d10v (bfd_vma, disassemble_info*); +int print_insn_v850 (bfd_vma, disassemble_info*); +int print_insn_tic30 (bfd_vma, disassemble_info*); +int print_insn_ppc (bfd_vma, disassemble_info*); +int print_insn_s390 (bfd_vma, disassemble_info*); +int print_insn_crisv32 (bfd_vma, disassemble_info*); +int print_insn_crisv10 (bfd_vma, disassemble_info*); +int print_insn_microblaze (bfd_vma, disassemble_info*); +int print_insn_ia64 (bfd_vma, disassemble_info*); #if 0 /* Fetch the disassembler for a given BFD, if that support is available. */ -extern disassembler_ftype disassembler (bfd *); +disassembler_ftype disassembler(bfd *); #endif @@ -413,21 +412,20 @@ /* Here is a function which callers may wish to use for read_memory_func. It gets bytes from a buffer. */ -extern int buffer_read_memory - (bfd_vma, bfd_byte *, int, struct disassemble_info *); +int buffer_read_memory(bfd_vma, bfd_byte *, int, struct disassemble_info *); /* This function goes with buffer_read_memory. It prints a message using info->fprintf_func and info->stream. */ -extern void perror_memory (int, bfd_vma, struct disassemble_info *); +void perror_memory(int, bfd_vma, struct disassemble_info *); /* Just print the address in hex. This is included for completeness even though both GDB and objdump provide their own (to print symbolic addresses). */ -extern void generic_print_address (bfd_vma, struct disassemble_info *); +void generic_print_address(bfd_vma, struct disassemble_info *); /* Always true. */ -extern int generic_symbol_at_address (bfd_vma, struct disassemble_info *); +int generic_symbol_at_address(bfd_vma, struct disassemble_info *); /* Macro to initialize a disassemble_info struct. This should be called by all applications creating such a struct. */ @@ -468,6 +466,7 @@ /* from libbfd */ +bfd_vma bfd_getl64 (const bfd_byte *addr); bfd_vma bfd_getl32 (const bfd_byte *addr); bfd_vma bfd_getb32 (const bfd_byte *addr); bfd_vma bfd_getl16 (const bfd_byte *addr); diff -Nru qemu-kvm-0.12.5+noroms/docs/blkverify.txt qemu-kvm-0.14.1/docs/blkverify.txt --- qemu-kvm-0.12.5+noroms/docs/blkverify.txt 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/docs/blkverify.txt 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,69 @@ += Block driver correctness testing with blkverify = + +== Introduction == + +This document describes how to use the blkverify protocol to test that a block +driver is operating correctly. + +It is difficult to test and debug block drivers against real guests. Often +processes inside the guest will crash because corrupt sectors were read as part +of the executable. Other times obscure errors are raised by a program inside +the guest. These issues are extremely hard to trace back to bugs in the block +driver. + +Blkverify solves this problem by catching data corruption inside QEMU the first +time bad data is read and reporting the disk sector that is corrupted. + +== How it works == + +The blkverify protocol has two child block devices, the "test" device and the +"raw" device. Read/write operations are mirrored to both devices so their +state should always be in sync. + +The "raw" device is a raw image, a flat file, that has identical starting +contents to the "test" image. The idea is that the "raw" device will handle +read/write operations correctly and not corrupt data. It can be used as a +reference for comparison against the "test" device. + +After a mirrored read operation completes, blkverify will compare the data and +raise an error if it is not identical. This makes it possible to catch the +first instance where corrupt data is read. + +== Example == + +Imagine raw.img has 0xcd repeated throughout its first sector: + + $ ./qemu-io -c 'read -v 0 512' raw.img + 00000000: cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd ................ + 00000010: cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd ................ + [...] + 000001e0: cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd ................ + 000001f0: cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd ................ + read 512/512 bytes at offset 0 + 512.000000 bytes, 1 ops; 0.0000 sec (97.656 MiB/sec and 200000.0000 ops/sec) + +And test.img is corrupt, its first sector is zeroed when it shouldn't be: + + $ ./qemu-io -c 'read -v 0 512' test.img + 00000000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + 00000010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + [...] + 000001e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + 000001f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ + read 512/512 bytes at offset 0 + 512.000000 bytes, 1 ops; 0.0000 sec (81.380 MiB/sec and 166666.6667 ops/sec) + +This error is caught by blkverify: + + $ ./qemu-io -c 'read 0 512' blkverify:a.img:b.img + blkverify: read sector_num=0 nb_sectors=4 contents mismatch in sector 0 + +A more realistic scenario is verifying the installation of a guest OS: + + $ ./qemu-img create raw.img 16G + $ ./qemu-img create -f qcow2 test.qcow2 16G + $ x86_64-softmmu/qemu-system-x86_64 -cdrom debian.iso \ + -drive file=blkverify:raw.img:test.qcow2 + +If the installation is aborted when blkverify detects corruption, use qemu-io +to explore the contents of the disk image at the sector in question. diff -Nru qemu-kvm-0.12.5+noroms/docs/bootindex.txt qemu-kvm-0.14.1/docs/bootindex.txt --- qemu-kvm-0.12.5+noroms/docs/bootindex.txt 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/docs/bootindex.txt 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,43 @@ += Bootindex propery = + +Block and net devices have bootindex property. This property is used to +determine the order in which firmware will consider devices for booting +the guest OS. If the bootindex property is not set for a device, it gets +lowest boot priority. There is no particular order in which devices with +unset bootindex property will be considered for booting, but they will +still be bootable. + +== Example == + +Lets assume we have QEMU machine with two NICs (virtio, e1000) and two +disks (IDE, virtio): + +qemu -drive file=disk1.img,if=none,id=disk1 + -device ide-drive,drive=disk1,bootindex=4 + -drive file=disk2.img,if=none,id=disk2 + -device virtio-blk-pci,drive=disk2,bootindex=3 + -netdev type=user,id=net0 -device virtio-net-pci,netdev=net0,bootindex=2 + -netdev type=user,id=net1 -device e1000,netdev=net1,bootindex=1 + +Given the command above, firmware should try to boot from the e1000 NIC +first. If this fails, it should try the virtio NIC next, if this fails +too, it should try the virtio disk, and then the IDE disk. + +== Limitations == + +1. Some firmware has limitations on which devices can be considered for +booting. For instance, the PC BIOS boot specification allows only one +disk to be bootable. If boot from disk fails for some reason, the BIOS +won't retry booting from other disk. It still can try to boot from +floppy or net, though. + +2. Sometimes, firmware cannot map the device path QEMU wants firmware to +boot from to a boot method. It doesn't happen for devices the firmware +can natively boot from, but if firmware relies on an option ROM for +booting, and the same option ROM is used for booting from more then one +device, the firmware may not be able to ask the option ROM to boot from +a particular device reliably. For instance with PC BIOS, if a SCSI HBA +has three bootable devices target1, target3, target5 connected to it, +the option ROM will have a boot method for each of them, but it is not +possible to map from boot method back to a specific target. This is a +shortcoming of PC BIOS boot specification. diff -Nru qemu-kvm-0.12.5+noroms/docs/migration.txt qemu-kvm-0.14.1/docs/migration.txt --- qemu-kvm-0.12.5+noroms/docs/migration.txt 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/docs/migration.txt 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,303 @@ += Migration = + +QEMU has code to load/save the state of the guest that it is running. +These are two complementary operations. Saving the state just does +that, saves the state for each device that the guest is running. +Restoring a guest is just the opposite operation: we need to load the +state of each device. + +For this to work, QEMU has to be launched with the same arguments the +two times. I.e. it can only restore the state in one guest that has +the same devices that the one it was saved (this last requirement can +be relaxed a bit, but for now we can consider that configuration has +to be exactly the same). + +Once that we are able to save/restore a guest, a new functionality is +requested: migration. This means that QEMU is able to start in one +machine and being "migrated" to another machine. I.e. being moved to +another machine. + +Next was the "live migration" functionality. This is important +because some guests run with a lot of state (specially RAM), and it +can take a while to move all state from one machine to another. Live +migration allows the guest to continue running while the state is +transferred. Only while the last part of the state is transferred has +the guest to be stopped. Typically the time that the guest is +unresponsive during live migration is the low hundred of milliseconds +(notice that this depends on a lot of things). + +=== Types of migration === + +Now that we have talked about live migration, there are several ways +to do migration: + +- tcp migration: do the migration using tcp sockets +- unix migration: do the migration using unix sockets +- exec migration: do the migration using the stdin/stdout through a process. +- fd migration: do the migration using an file descriptor that is + passed to QEMU. QEMU doesn't care how this file descriptor is opened. + +All these four migration protocols use the same infrastructure to +save/restore state devices. This infrastructure is shared with the +savevm/loadvm functionality. + +=== State Live Migration == + +This is used for RAM and block devices. It is not yet ported to vmstate. + + +=== What is the common infrastructure === + +QEMU uses a QEMUFile abstraction to be able to do migration. Any type +of migration that wants to use QEMU infrastructure has to create a +QEMUFile with: + +QEMUFile *qemu_fopen_ops(void *opaque, + QEMUFilePutBufferFunc *put_buffer, + QEMUFileGetBufferFunc *get_buffer, + QEMUFileCloseFunc *close, + QEMUFileRateLimit *rate_limit, + QEMUFileSetRateLimit *set_rate_limit, + QEMUFileGetRateLimit *get_rate_limit); + +The functions have the following functionality: + +This function writes a chunk of data to a file at the given position. +The pos argument can be ignored if the file is only used for +streaming. The handler should try to write all of the data it can. + +typedef int (QEMUFilePutBufferFunc)(void *opaque, const uint8_t *buf, + int64_t pos, int size); + +Read a chunk of data from a file at the given position. The pos argument +can be ignored if the file is only be used for streaming. The number of +bytes actually read should be returned. + +typedef int (QEMUFileGetBufferFunc)(void *opaque, uint8_t *buf, + int64_t pos, int size); + +Close a file and return an error code. + +typedef int (QEMUFileCloseFunc)(void *opaque); + +Called to determine if the file has exceeded its bandwidth allocation. The +bandwidth capping is a soft limit, not a hard limit. + +typedef int (QEMUFileRateLimit)(void *opaque); + +Called to change the current bandwidth allocation. This function must return +the new actual bandwidth. It should be new_rate if everything goes OK, and +the old rate otherwise. + +typedef size_t (QEMUFileSetRateLimit)(void *opaque, size_t new_rate); +typedef size_t (QEMUFileGetRateLimit)(void *opaque); + +You can use any internal state that you need using the opaque void * +pointer that is passed to all functions. + +The rate limiting functions are used to limit the bandwidth used by +QEMU migration. + +The important functions for us are put_buffer()/get_buffer() that +allow to write/read a buffer into the QEMUFile. + +=== How to save the state of one device == + +The state of a device is saved using intermediate buffers. There are +some helper functions to assist this saving. + +There is a new concept that we have to explain here: device state +version. When we migrate a device, we save/load the state as a series +of fields. Some times, due to bugs or new functionality, we need to +change the state to store more/different information. We use the +version to identify each time that we do a change. Each version is +associated with a series of fields saved. The save_state always saves +the state as the newer version. But load_state sometimes is able to +load state from an older version. + + === Legacy way === + +This way is going to disappear as soon as all current users are ported to VMSTATE. + +Each device has to register two functions, one to save the state and +another to load the state back. + +int register_savevm(DeviceState *dev, + const char *idstr, + int instance_id, + int version_id, + SaveStateHandler *save_state, + LoadStateHandler *load_state, + void *opaque); + +typedef void SaveStateHandler(QEMUFile *f, void *opaque); +typedef int LoadStateHandler(QEMUFile *f, void *opaque, int version_id); + +The important functions for the device state format are the save_state +and load_state. Notice that load_state receives a version_id +parameter to know what state format is receiving. save_state doesn't +have a version_id parameter because it always uses the latest version. + +=== VMState === + +The legacy way of saving/loading state of the device had the problem +that we have to maintain two functions in sync. If we did one change +in one of them and not in the other, we would get a failed migration. + +VMState changed the way that state is saved/loaded. Instead of using +a function to save the state and another to load it, it was changed to +a declarative way of what the state consisted of. Now VMState is able +to interpret that definition to be able to load/save the state. As +the state is declared only once, it can't go out of sync in the +save/load functions. + +An example (from hw/pckbd.c) + +static const VMStateDescription vmstate_kbd = { + .name = "pckbd", + .version_id = 3, + .minimum_version_id = 3, + .minimum_version_id_old = 3, + .fields = (VMStateField []) { + VMSTATE_UINT8(write_cmd, KBDState), + VMSTATE_UINT8(status, KBDState), + VMSTATE_UINT8(mode, KBDState), + VMSTATE_UINT8(pending, KBDState), + VMSTATE_END_OF_LIST() + } +}; + +We are declaring the state with name "pckbd". +The version_id is 3, and the fields are 4 uint8_t in a KBDState structure. +We registered this with: + + vmstate_register(NULL, 0, &vmstate_kbd, s); + +Note: talk about how vmstate <-> qdev interact, and what the instance ids mean. + +You can search for VMSTATE_* macros for lots of types used in QEMU in +hw/hw.h. + +=== More about versions == + +You can see that there are several version fields: + +- version_id: the maximum version_id supported by VMState for that device. +- minimum_version_id: the minimum version_id that VMState is able to understand + for that device. +- minimum_version_id_old: For devices that were not able to port to vmstate, we can + assign a function that knows how to read this old state. + +So, VMState is able to read versions from minimum_version_id to +version_id. And the function load_state_old() is able to load state +from minimum_version_id_old to minimum_version_id. This function is +deprecated and will be removed when no more users are left. + +=== Massaging functions === + +Sometimes, it is not enough to be able to save the state directly +from one structure, we need to fill the correct values there. One +example is when we are using kvm. Before saving the cpu state, we +need to ask kvm to copy to QEMU the state that it is using. And the +opposite when we are loading the state, we need a way to tell kvm to +load the state for the cpu that we have just loaded from the QEMUFile. + +The functions to do that are inside a vmstate definition, and are called: + +- int (*pre_load)(void *opaque); + + This function is called before we load the state of one device. + +- int (*post_load)(void *opaque, int version_id); + + This function is called after we load the state of one device. + +- void (*pre_save)(void *opaque); + + This function is called before we save the state of one device. + +Example: You can look at hpet.c, that uses the three function to + massage the state that is transferred. + +=== Subsections === + +The use of version_id allows to be able to migrate from older versions +to newer versions of a device. But not the other way around. This +makes very complicated to fix bugs in stable branches. If we need to +add anything to the state to fix a bug, we have to disable migration +to older versions that don't have that bug-fix (i.e. a new field). + +But sometimes, that bug-fix is only needed sometimes, not always. For +instance, if the device is in the middle of a DMA operation, it is +using a specific functionality, .... + +It is impossible to create a way to make migration from any version to +any other version to work. But we can do better than only allowing +migration from older versions no newer ones. For that fields that are +only needed sometimes, we add the idea of subsections. A subsection +is "like" a device vmstate, but with a particularity, it has a Boolean +function that tells if that values are needed to be sent or not. If +this functions returns false, the subsection is not sent. + +On the receiving side, if we found a subsection for a device that we +don't understand, we just fail the migration. If we understand all +the subsections, then we load the state with success. + +One important note is that the post_load() function is called "after" +loading all subsections, because a newer subsection could change same +value that it uses. + +Example: + +static bool ide_drive_pio_state_needed(void *opaque) +{ + IDEState *s = opaque; + + return (s->status & DRQ_STAT) != 0; +} + +const VMStateDescription vmstate_ide_drive_pio_state = { + .name = "ide_drive/pio_state", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .pre_save = ide_drive_pio_pre_save, + .post_load = ide_drive_pio_post_load, + .fields = (VMStateField []) { + VMSTATE_INT32(req_nb_sectors, IDEState), + VMSTATE_VARRAY_INT32(io_buffer, IDEState, io_buffer_total_len, 1, + vmstate_info_uint8, uint8_t), + VMSTATE_INT32(cur_io_buffer_offset, IDEState), + VMSTATE_INT32(cur_io_buffer_len, IDEState), + VMSTATE_UINT8(end_transfer_fn_idx, IDEState), + VMSTATE_INT32(elementary_transfer_size, IDEState), + VMSTATE_INT32(packet_transfer_size, IDEState), + VMSTATE_END_OF_LIST() + } +}; + +const VMStateDescription vmstate_ide_drive = { + .name = "ide_drive", + .version_id = 3, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .post_load = ide_drive_post_load, + .fields = (VMStateField []) { + .... several fields .... + VMSTATE_END_OF_LIST() + }, + .subsections = (VMStateSubsection []) { + { + .vmsd = &vmstate_ide_drive_pio_state, + .needed = ide_drive_pio_state_needed, + }, { + /* empty */ + } + } +}; + +Here we have a subsection for the pio state. We only need to +save/send this state when we are in the middle of a pio operation +(that is what ide_drive_pio_state_needed() checks). If DRQ_STAT is +not enabled, the values on that fields are garbage and don't need to +be sent. diff -Nru qemu-kvm-0.12.5+noroms/docs/qdev-device-use.txt qemu-kvm-0.14.1/docs/qdev-device-use.txt --- qemu-kvm-0.12.5+noroms/docs/qdev-device-use.txt 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/docs/qdev-device-use.txt 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,368 @@ += How to convert to -device & friends = + +=== Specifying Bus and Address on Bus === + +In qdev, each device has a parent bus. Some devices provide one or +more buses for children. You can specify a device's parent bus with +-device parameter bus. + +A device typically has a device address on its parent bus. For buses +where this address can be configured, devices provide a bus-specific +property. These are + + bus property name value format + PCI addr %x.%x (dev.fn, .fn optional) + I2C address %u + SCSI scsi-id %u + +Example: device i440FX-pcihost is on the root bus, and provides a PCI +bus named pci.0. To put a FOO device into its slot 4, use -device +FOO,bus=/i440FX-pcihost/pci.0,addr=4. The abbreviated form bus=pci.0 +also works as long as the bus name is unique. + +Note: the USB device address can't be controlled at this time. + +=== Block Devices === + +A QEMU block device (drive) has a host and a guest part. + +In the general case, the guest device is connected to a controller +device. For instance, the IDE controller provides two IDE buses, each +of which can have up to two ide-drive devices, and each ide-drive +device is a guest part, and is connected to a host part. + +Except we sometimes lump controller, bus(es) and drive device(s) all +together into a single device. For instance, the ISA floppy +controller is connected to up to two host drives. + +The old ways to define block devices define host and guest part +together. Sometimes, they can even define a controller device in +addition to the block device. + +The new way keeps the parts separate: you create the host part with +-drive, and guest device(s) with -device. + +The various old ways to define drives all boil down to the common form + + -drive if=TYPE,index=IDX,bus=BUS,unit=UNIT,HOST-OPTS... + +TYPE, BUS and UNIT identify the controller device, which of its buses +to use, and the drive's address on that bus. Details depend on TYPE. +IDX is an alternative way to specify BUS and UNIT. + +In the new way, this becomes something like + + -drive if=none,id=DRIVE-ID,HOST-OPTS... + -device DEVNAME,drive=DRIVE-ID,DEV-OPTS... + +The -device argument differs in detail for each kind of drive: + +* if=ide + + -device ide-drive,drive=DRIVE-ID,bus=IDE-BUS,unit=UNIT + + where IDE-BUS identifies an IDE bus, normally either ide.0 or ide.1, + and UNIT is either 0 or 1. + + Bug: new way does not work for ide.1 unit 0 (in old terms: index=2) + unless you disable the default CD-ROM with -nodefaults. + +* if=scsi + + The old way implicitly creates SCSI controllers as needed. The new + way makes that explicit: + + -device lsi53c895a,id=ID + + As for all PCI devices, you can add bus=PCI-BUS,addr=DEVFN to + control the PCI device address. + + This SCSI controller a single SCSI bus, named ID.0. Put a disk on + it: + + -device scsi-disk,drive=DRIVE-ID,bus=ID.0,scsi-id=SCSI-ID,removable=RMB + + The (optional) removable parameter lets you override the SCSI INQUIRY + removable (RMB) bit for non CD-ROM devices. It is ignored for CD-ROM devices + which are always removable. RMB is "on" or "off". + +* if=floppy + + -global isa-fdc,driveA=DRIVE-ID,driveB=DRIVE-ID + + This is -global instead of -device, because the floppy controller is + created automatically, and we want to configure that one, not create + a second one (which isn't possible anyway). + + Omitting a drive parameter makes that drive empty. + + Bug: driveA works only if you disable the default floppy drive with + -nodefaults. + +* if=virtio + + -device virtio-blk-pci,drive=DRIVE-ID,class=C,vectors=V,ioeventfd=IOEVENTFD + + This lets you control PCI device class and MSI-X vectors. + + IOEVENTFD controls whether or not ioeventfd is used for virtqueue notify. It + can be set to on (default) or off. + + As for all PCI devices, you can add bus=PCI-BUS,addr=DEVFN to + control the PCI device address. + +* if=pflash, if=mtd, if=sd, if=xen are not yet available with -device + +For USB devices, the old way is actually different: + + -usbdevice disk:format=FMT:FILENAME + +Provides much less control than -drive's HOST-OPTS... The new way +fixes that: + + -device usb-storage,drive=DRIVE-ID,removable=RMB + +The removable parameter gives control over the SCSI INQUIRY removable (RMB) +bit. USB thumbdrives usually set removable=on, while USB hard disks set +removable=off. See the if=scsi description above for details on the removable +parameter, which applies only to scsi-disk devices and not to scsi-generic. + +=== Character Devices === + +A QEMU character device has a host and a guest part. + +The old ways to define character devices define host and guest part +together. + +The new way keeps the parts separate: you create the host part with +-chardev, and the guest device with -device. + +The various old ways to define a character device are all of the +general form + + -FOO FOO-OPTS...,LEGACY-CHARDEV + +where FOO-OPTS... is specific to -FOO, and the host part +LEGACY-CHARDEV is the same everywhere. + +In the new way, this becomes + + -chardev HOST-OPTS...,id=CHR-ID + -device DEVNAME,chardev=CHR-ID,DEV-OPTS... + +The appropriate DEVNAME depends on the machine type. For type "pc": + +* -serial becomes -device isa-serial,iobase=IOADDR,irq=IRQ,index=IDX + + This lets you control I/O ports and IRQs. + +* -parallel becomes -device isa-parallel,iobase=IOADDR,irq=IRQ,index=IDX + + This lets you control I/O ports and IRQs. + +* -usbdevice serial:vendorid=VID,productid=PRID becomes + -device usb-serial,vendorid=VID,productid=PRID + +* -usbdevice braille doesn't support LEGACY-CHARDEV syntax. It always + uses "braille". With -device, this useful default is gone, so you + have to use something like + + -device usb-braille,chardev=braille,vendorid=VID,productid=PRID + -chardev braille,id=braille + +* -virtioconsole is still being worked on + +LEGACY-CHARDEV translates to -chardev HOST-OPTS... as follows: + +* null becomes -chardev null + +* pty, msmouse, braille, stdio likewise + +* vc:WIDTHxHEIGHT becomes -chardev vc,width=WIDTH,height=HEIGHT + +* vc:CxC becomes -chardev vc,cols=,rows= + +* con: becomes -chardev console + +* COM becomes -chardev serial,path= + +* file:FNAME becomes -chardev file,path=FNAME + +* pipe:FNAME becomes -chardev pipe,path=FNAME + +* tcp:HOST:PORT,OPTS... becomes -chardev socket,host=HOST,port=PORT,OPTS... + +* telnet:HOST:PORT,OPTS... becomes + -chardev socket,host=HOST,port=PORT,OPTS...,telnet=on + +* udp:HOST:PORT@LOCALADDR:LOCALPORT becomes + -chardev udp,host=HOST,port=PORT,localaddr=LOCALADDR,localport=LOCALPORT + +* unix:FNAME becomes -chardev socket,path=FNAME + +* /dev/parportN becomes -chardev parport,file=/dev/parportN + +* /dev/ppiN likewise + +* Any other /dev/FNAME becomes -chardev tty,path=/dev/FNAME + +* mon:LEGACY-CHARDEV is special: it multiplexes the monitor onto the + character device defined by LEGACY-CHARDEV. -chardev provides more + general multiplexing instead: you can connect up to four users to a + single host part. You need to pass mux=on to -chardev to enable + switching the input focus. + +QEMU uses LEGACY-CHARDEV syntax not just to set up guest devices, but +also in various other places such as -monitor or -net +user,guestfwd=... You can use chardev:CHR-ID in place of +LEGACY-CHARDEV to refer to a host part defined with -chardev. + +=== Network Devices === + +A QEMU network device (NIC) has a host and a guest part. + +The old ways to define NICs define host and guest part together. It +looks like this: + + -net nic,vlan=VLAN,macaddr=MACADDR,model=MODEL,name=ID,addr=STR,vectors=V + +Except for USB it looks like this: + + -usbdevice net:vlan=VLAN,macaddr=MACADDR,name=ID,addr=STR,vectors=V + +The new way keeps the parts separate: you create the host part with +-netdev, and the guest device with -device, like this: + + -netdev type=TYPE,id=NET-ID + -device DEVNAME,netdev=NET-ID,mac=MACADDR,DEV-OPTS... + +Unlike the old way, this creates just a network device, not a VLAN. +If you really want a VLAN, create it the usual way, then create the +guest device like this: + + -device DEVNAME,vlan=VLAN,mac=MACADDR,DEV-OPTS... + +DEVNAME equals MODEL, except for virtio you have to name the virtio +device appropriate for the bus (virtio-net-pci for PCI), and for USB +NIC you have to use usb-net. + +The old name=ID parameter becomes the usual id=ID with -device. + +For PCI devices, you can add bus=PCI-BUS,addr=DEVFN to control the PCI +device address, as usual. The old -net nic provides parameter addr +for that, it is silently ignored when the NIC is not a PCI device. + +For virtio-net-pci, you can control whether or not ioeventfd is used for +virtqueue notify by setting ioeventfd= to on or off (default). + +-net nic accepts vectors=V for all models, but it's silently ignored +except for virtio-net-pci (model=virtio). With -device, only devices +that support it accept it. + +Not all devices are available with -device at this time. All PCI +devices and ne2k_isa are. + +Some PCI devices aren't available with -net nic, e.g. i82558a. + +Bug: usb-net does not work, yet. Patch posted. + +=== Graphics Devices === + +Host and guest part of graphics devices have always been separate. + +The old way to define the guest graphics device is -vga VGA. + +The new way is -device. Map from -vga argument to -device: + + std -device VGA + cirrus -device cirrus-vga + vmware -device vmware-svga + xenfb not yet available with -device + +As for all PCI devices, you can add bus=PCI-BUS,addr=DEVFN to control +the PCI device address. + +-device VGA supports properties bios-offset and bios-size, but they +aren't used with machine type "pc". + +Bug: -device cirrus-vga and -device vmware-svga require -nodefaults. + +Bug: the new way requires PCI; ISA VGA is not yet available with +-device. + +Bug: the new way doesn't work for machine type "pc", because it +violates obscure device initialization ordering constraints. + +=== Audio Devices === + +Host and guest part of audio devices have always been separate. + +The old way to define guest audio devices is -soundhw C1,... + +The new way is to define each guest audio device separately with +-device. + +Map from -soundhw sound card name to -device: + + ac97 -device AC97 + cs4231a -device cs4231a,iobase=IOADDR,irq=IRQ,dma=DMA + es1370 -device ES1370 + gus -device gus,iobase=IOADDR,irq=IRQ,dma=DMA,freq=F + sb16 -device sb16,iobase=IOADDR,irq=IRQ,dma=DMA,dma16=DMA16,version=V + adlib not yet available with -device + pcspk not yet available with -device + +For PCI devices, you can add bus=PCI-BUS,addr=DEVFN to control the PCI +device address, as usual. + +=== USB Devices === + +The old way to define a virtual USB device is -usbdevice DRIVER:OPTS... + +The new way is -device DEVNAME,DEV-OPTS... Details depend on DRIVER: + +* mouse -device usb-mouse +* tablet -device usb-tablet +* keyboard -device usb-kdb +* wacom-tablet -device usb-wacom-tablet +* host:... See "Host Device Assignment" +* disk:... See "Block Devices" +* serial:... See "Character Devices" +* braille See "Character Devices" +* net:... See "Network Devices" +* bt:... not yet available with -device + +=== Watchdog Devices === + +Host and guest part of watchdog devices have always been separate. + +The old way to define a guest watchdog device is -watchdog DEVNAME. +The new way is -device DEVNAME. For PCI devices, you can add +bus=PCI-BUS,addr=DEVFN to control the PCI device address, as usual. + +=== Host Device Assignment === + +QEMU supports assigning host PCI devices (qemu-kvm only at this time) +and host USB devices. + +The old way to assign a host PCI device is + + -pcidevice host=ADDR,dma=none,id=ID + +The new way is + + -device pci-assign,host=ADDR,iommu=IOMMU,id=ID + +The old dma=none becomes iommu=0 with -device. + +The old way to assign a host USB device is + + -usbdevice host:auto:BUS.ADDR:VID:PRID + +where any of BUS, ADDR, VID, PRID can be the wildcard *. + +The new way is + + -device usb-host,hostbus=BUS,hostaddr=ADDR,vendorid=VID,productid=PRID + +where left out or zero BUS, ADDR, VID, PRID serve as wildcard. diff -Nru qemu-kvm-0.12.5+noroms/docs/specs/acpi_pci_hotplug.txt qemu-kvm-0.14.1/docs/specs/acpi_pci_hotplug.txt --- qemu-kvm-0.12.5+noroms/docs/specs/acpi_pci_hotplug.txt 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/docs/specs/acpi_pci_hotplug.txt 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,37 @@ +QEMU<->ACPI BIOS PCI hotplug interface +-------------------------------------- + +QEMU supports PCI hotplug via ACPI, for PCI bus 0. This document +describes the interface between QEMU and the ACPI BIOS. + +ACPI GPE block (IO ports 0xafe0-0xafe3, byte access): +----------------------------------------- + +Generic ACPI GPE block. Bit 1 (GPE.1) used to notify PCI hotplug/eject +event to ACPI BIOS, via SCI interrupt. + +PCI slot injection notification pending (IO port 0xae00-0xae03, 4-byte access): +--------------------------------------------------------------- +Slot injection notification pending. One bit per slot. + +Read by ACPI BIOS GPE.1 handler to notify OS of injection +events. + +PCI slot removal notification (IO port 0xae04-0xae07, 4-byte access): +----------------------------------------------------- +Slot removal notification pending. One bit per slot. + +Read by ACPI BIOS GPE.1 handler to notify OS of removal +events. + +PCI device eject (IO port 0xae08-0xae0b, 4-byte access): +---------------------------------------- + +Used by ACPI BIOS _EJ0 method to request device removal. One bit per slot. +Reads return 0. + +PCI removability status (IO port 0xae0c-0xae0f, 4-byte access): +----------------------------------------------- + +Used by ACPI BIOS _RMV method to indicate removability status to OS. One +bit per slot. diff -Nru qemu-kvm-0.12.5+noroms/docs/specs/ivshmem_device_spec.txt qemu-kvm-0.14.1/docs/specs/ivshmem_device_spec.txt --- qemu-kvm-0.12.5+noroms/docs/specs/ivshmem_device_spec.txt 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/docs/specs/ivshmem_device_spec.txt 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,96 @@ + +Device Specification for Inter-VM shared memory device +------------------------------------------------------ + +The Inter-VM shared memory device is designed to share a region of memory to +userspace in multiple virtual guests. The memory region does not belong to any +guest, but is a POSIX memory object on the host. Optionally, the device may +support sending interrupts to other guests sharing the same memory region. + + +The Inter-VM PCI device +----------------------- + +*BARs* + +The device supports three BARs. BAR0 is a 1 Kbyte MMIO region to support +registers. BAR1 is used for MSI-X when it is enabled in the device. BAR2 is +used to map the shared memory object from the host. The size of BAR2 is +specified when the guest is started and must be a power of 2 in size. + +*Registers* + +The device currently supports 4 registers of 32-bits each. Registers +are used for synchronization between guests sharing the same memory object when +interrupts are supported (this requires using the shared memory server). + +The server assigns each VM an ID number and sends this ID number to the Qemu +process when the guest starts. + +enum ivshmem_registers { + IntrMask = 0, + IntrStatus = 4, + IVPosition = 8, + Doorbell = 12 +}; + +The first two registers are the interrupt mask and status registers. Mask and +status are only used with pin-based interrupts. They are unused with MSI +interrupts. + +Status Register: The status register is set to 1 when an interrupt occurs. + +Mask Register: The mask register is bitwise ANDed with the interrupt status +and the result will raise an interrupt if it is non-zero. However, since 1 is +the only value the status will be set to, it is only the first bit of the mask +that has any effect. Therefore interrupts can be masked by setting the first +bit to 0 and unmasked by setting the first bit to 1. + +IVPosition Register: The IVPosition register is read-only and reports the +guest's ID number. The guest IDs are non-negative integers. When using the +server, since the server is a separate process, the VM ID will only be set when +the device is ready (shared memory is received from the server and accessible via +the device). If the device is not ready, the IVPosition will return -1. +Applications should ensure that they have a valid VM ID before accessing the +shared memory. + +Doorbell Register: To interrupt another guest, a guest must write to the +Doorbell register. The doorbell register is 32-bits, logically divided into +two 16-bit fields. The high 16-bits are the guest ID to interrupt and the low +16-bits are the interrupt vector to trigger. The semantics of the value +written to the doorbell depends on whether the device is using MSI or a regular +pin-based interrupt. In short, MSI uses vectors while regular interrupts set the +status register. + +Regular Interrupts + +If regular interrupts are used (due to either a guest not supporting MSI or the +user specifying not to use them on startup) then the value written to the lower +16-bits of the Doorbell register results is arbitrary and will trigger an +interrupt in the destination guest. + +Message Signalled Interrupts + +A ivshmem device may support multiple MSI vectors. If so, the lower 16-bits +written to the Doorbell register must be between 0 and the maximum number of +vectors the guest supports. The lower 16 bits written to the doorbell is the +MSI vector that will be raised in the destination guest. The number of MSI +vectors is configurable but it is set when the VM is started. + +The important thing to remember with MSI is that it is only a signal, no status +is set (since MSI interrupts are not shared). All information other than the +interrupt itself should be communicated via the shared memory region. Devices +supporting multiple MSI vectors can use different vectors to indicate different +events have occurred. The semantics of interrupt vectors are left to the +user's discretion. + + +Usage in the Guest +------------------ + +The shared memory device is intended to be used with the provided UIO driver. +Very little configuration is needed. The guest should map BAR0 to access the +registers (an array of 32-bit ints allows simple writing) and map BAR2 to +access the shared memory region itself. The size of the shared memory region +is specified when the guest (or shared memory server) is started. A guest may +map the whole shared memory region or only part of it. diff -Nru qemu-kvm-0.12.5+noroms/docs/specs/qed_spec.txt qemu-kvm-0.14.1/docs/specs/qed_spec.txt --- qemu-kvm-0.12.5+noroms/docs/specs/qed_spec.txt 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/docs/specs/qed_spec.txt 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,130 @@ +=Specification= + +The file format looks like this: + + +----------+----------+----------+-----+ + | cluster0 | cluster1 | cluster2 | ... | + +----------+----------+----------+-----+ + +The first cluster begins with the '''header'''. The header contains information about where regular clusters start; this allows the header to be extensible and store extra information about the image file. A regular cluster may be a '''data cluster''', an '''L2''', or an '''L1 table'''. L1 and L2 tables are composed of one or more contiguous clusters. + +Normally the file size will be a multiple of the cluster size. If the file size is not a multiple, extra information after the last cluster may not be preserved if data is written. Legitimate extra information should use space between the header and the first regular cluster. + +All fields are little-endian. + +==Header== + Header { + uint32_t magic; /* QED\0 */ + + uint32_t cluster_size; /* in bytes */ + uint32_t table_size; /* for L1 and L2 tables, in clusters */ + uint32_t header_size; /* in clusters */ + + uint64_t features; /* format feature bits */ + uint64_t compat_features; /* compat feature bits */ + uint64_t autoclear_features; /* self-resetting feature bits */ + + uint64_t l1_table_offset; /* in bytes */ + uint64_t image_size; /* total logical image size, in bytes */ + + /* if (features & QED_F_BACKING_FILE) */ + uint32_t backing_filename_offset; /* in bytes from start of header */ + uint32_t backing_filename_size; /* in bytes */ + } + +Field descriptions: +* ''cluster_size'' must be a power of 2 in range [2^12, 2^26]. +* ''table_size'' must be a power of 2 in range [1, 16]. +* ''header_size'' is the number of clusters used by the header and any additional information stored before regular clusters. +* ''features'', ''compat_features'', and ''autoclear_features'' are file format extension bitmaps. They work as follows: +** An image with unknown ''features'' bits enabled must not be opened. File format changes that are not backwards-compatible must use ''features'' bits. +** An image with unknown ''compat_features'' bits enabled can be opened safely. The unknown features are simply ignored and represent backwards-compatible changes to the file format. +** An image with unknown ''autoclear_features'' bits enable can be opened safely after clearing the unknown bits. This allows for backwards-compatible changes to the file format which degrade gracefully and can be re-enabled again by a new program later. +* ''l1_table_offset'' is the offset of the first byte of the L1 table in the image file and must be a multiple of ''cluster_size''. +* ''image_size'' is the block device size seen by the guest and must be a multiple of 512 bytes. +* ''backing_filename_offset'' and ''backing_filename_size'' describe a string in (byte offset, byte size) form. It is not NUL-terminated and has no alignment constraints. The string must be stored within the first ''header_size'' clusters. The backing filename may be an absolute path or relative to the image file. + +Feature bits: +* QED_F_BACKING_FILE = 0x01. The image uses a backing file. +* QED_F_NEED_CHECK = 0x02. The image needs a consistency check before use. +* QED_F_BACKING_FORMAT_NO_PROBE = 0x04. The backing file is a raw disk image and no file format autodetection should be attempted. This should be used to ensure that raw backing files are never detected as an image format if they happen to contain magic constants. + +There are currently no defined ''compat_features'' or ''autoclear_features'' bits. + +Fields predicated on a feature bit are only used when that feature is set. The fields always take up header space, regardless of whether or not the feature bit is set. + +==Tables== + +Tables provide the translation from logical offsets in the block device to cluster offsets in the file. + + #define TABLE_NOFFSETS (table_size * cluster_size / sizeof(uint64_t)) + + Table { + uint64_t offsets[TABLE_NOFFSETS]; + } + +The tables are organized as follows: + + +----------+ + | L1 table | + +----------+ + ,------' | '------. + +----------+ | +----------+ + | L2 table | ... | L2 table | + +----------+ +----------+ + ,------' | '------. + +----------+ | +----------+ + | Data | ... | Data | + +----------+ +----------+ + +A table is made up of one or more contiguous clusters. The table_size header field determines table size for an image file. For example, cluster_size=64 KB and table_size=4 results in 256 KB tables. + +The logical image size must be less than or equal to the maximum possible size of clusters rooted by the L1 table: + header.image_size <= TABLE_NOFFSETS * TABLE_NOFFSETS * header.cluster_size + +L1, L2, and data cluster offsets must be aligned to header.cluster_size. The following offsets have special meanings: + +===L2 table offsets=== +* 0 - unallocated. The L2 table is not yet allocated. + +===Data cluster offsets=== +* 0 - unallocated. The data cluster is not yet allocated. + +Future format extensions may wish to store per-offset information. The least significant 12 bits of an offset are reserved for this purpose and must be set to zero. Image files with cluster_size > 2^12 will have more unused bits which should also be zeroed. + +===Unallocated L2 tables and data clusters=== +Reads to an unallocated area of the image file access the backing file. If there is no backing file, then zeroes are produced. The backing file may be smaller than the image file and reads of unallocated areas beyond the end of the backing file produce zeroes. + +Writes to an unallocated area cause a new data clusters to be allocated, and a new L2 table if that is also unallocated. The new data cluster is populated with data from the backing file (or zeroes if no backing file) and the data being written. + +===Logical offset translation=== +Logical offsets are translated into cluster offsets as follows: + + table_bits table_bits cluster_bits + <--------> <--------> <---------------> + +----------+----------+-----------------+ + | L1 index | L2 index | byte offset | + +----------+----------+-----------------+ + + Structure of a logical offset + + offset_mask = ~(cluster_size - 1) # mask for the image file byte offset + + def logical_to_cluster_offset(l1_index, l2_index, byte_offset): + l2_offset = l1_table[l1_index] + l2_table = load_table(l2_offset) + cluster_offset = l2_table[l2_index] & offset_mask + return cluster_offset + byte_offset + +==Consistency checking== + +This section is informational and included to provide background on the use of the QED_F_NEED_CHECK ''features'' bit. + +The QED_F_NEED_CHECK bit is used to mark an image as dirty before starting an operation that could leave the image in an inconsistent state if interrupted by a crash or power failure. A dirty image must be checked on open because its metadata may not be consistent. + +Consistency check includes the following invariants: +# Each cluster is referenced once and only once. It is an inconsistency to have a cluster referenced more than once by L1 or L2 tables. A cluster has been leaked if it has no references. +# Offsets must be within the image file size and must be ''cluster_size'' aligned. +# Table offsets must at least ''table_size'' * ''cluster_size'' bytes from the end of the image file so that there is space for the entire table. + +The consistency check process starts by from ''l1_table_offset'' and scans all L2 tables. After the check completes with no other errors besides leaks, the QED_F_NEED_CHECK bit can be cleared and the image can be accessed. diff -Nru qemu-kvm-0.12.5+noroms/docs/tracing.txt qemu-kvm-0.14.1/docs/tracing.txt --- qemu-kvm-0.12.5+noroms/docs/tracing.txt 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/docs/tracing.txt 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,189 @@ += Tracing = + +== Introduction == + +This document describes the tracing infrastructure in QEMU and how to use it +for debugging, profiling, and observing execution. + +== Quickstart == + +1. Build with the 'simple' trace backend: + + ./configure --trace-backend=simple + make + +2. Enable trace events you are interested in: + + $EDITOR trace-events # remove "disable" from events you want + +3. Run the virtual machine to produce a trace file: + + qemu ... # your normal QEMU invocation + +4. Pretty-print the binary trace file: + + ./simpletrace.py trace-events trace-* + +== Trace events == + +There is a set of static trace events declared in the trace-events source +file. Each trace event declaration names the event, its arguments, and the +format string which can be used for pretty-printing: + + qemu_malloc(size_t size, void *ptr) "size %zu ptr %p" + qemu_free(void *ptr) "ptr %p" + +The trace-events file is processed by the tracetool script during build to +generate code for the trace events. Trace events are invoked directly from +source code like this: + + #include "trace.h" /* needed for trace event prototype */ + + void *qemu_malloc(size_t size) + { + void *ptr; + if (!size && !allow_zero_malloc()) { + abort(); + } + ptr = oom_check(malloc(size ? size : 1)); + trace_qemu_malloc(size, ptr); /* <-- trace event */ + return ptr; + } + +=== Declaring trace events === + +The tracetool script produces the trace.h header file which is included by +every source file that uses trace events. Since many source files include +trace.h, it uses a minimum of types and other header files included to keep +the namespace clean and compile times and dependencies down. + +Trace events should use types as follows: + + * Use stdint.h types for fixed-size types. Most offsets and guest memory + addresses are best represented with uint32_t or uint64_t. Use fixed-size + types over primitive types whose size may change depending on the host + (32-bit versus 64-bit) so trace events don't truncate values or break + the build. + + * Use void * for pointers to structs or for arrays. The trace.h header + cannot include all user-defined struct declarations and it is therefore + necessary to use void * for pointers to structs. + + * For everything else, use primitive scalar types (char, int, long) with the + appropriate signedness. + +Format strings should reflect the types defined in the trace event. Take +special care to use PRId64 and PRIu64 for int64_t and uint64_t types, +respectively. This ensures portability between 32- and 64-bit platforms. Note +that format strings must begin and end with double quotes. When using +portability macros, ensure they are preceded and followed by double quotes: +"value %"PRIx64"". + +=== Hints for adding new trace events === + +1. Trace state changes in the code. Interesting points in the code usually + involve a state change like starting, stopping, allocating, freeing. State + changes are good trace events because they can be used to understand the + execution of the system. + +2. Trace guest operations. Guest I/O accesses like reading device registers + are good trace events because they can be used to understand guest + interactions. + +3. Use correlator fields so the context of an individual line of trace output + can be understood. For example, trace the pointer returned by malloc and + used as an argument to free. This way mallocs and frees can be matched up. + Trace events with no context are not very useful. + +4. Name trace events after their function. If there are multiple trace events + in one function, append a unique distinguisher at the end of the name. + +5. Declare trace events with the "disable" keyword. Some trace events can + produce a lot of output and users are typically only interested in a subset + of trace events. Marking trace events disabled by default saves the user + from having to manually disable noisy trace events. + +== Trace backends == + +The tracetool script automates tedious trace event code generation and also +keeps the trace event declarations independent of the trace backend. The trace +events are not tightly coupled to a specific trace backend, such as LTTng or +SystemTap. Support for trace backends can be added by extending the tracetool +script. + +The trace backend is chosen at configure time and only one trace backend can +be built into the binary: + + ./configure --trace-backend=simple + +For a list of supported trace backends, try ./configure --help or see below. + +The following subsections describe the supported trace backends. + +=== Nop === + +The "nop" backend generates empty trace event functions so that the compiler +can optimize out trace events completely. This is the default and imposes no +performance penalty. + +=== Simpletrace === + +The "simple" backend supports common use cases and comes as part of the QEMU +source tree. It may not be as powerful as platform-specific or third-party +trace backends but it is portable. This is the recommended trace backend +unless you have specific needs for more advanced backends. + +=== Stderr === + +The "stderr" backend sends trace events directly to standard error output +during emulation. + +==== Monitor commands ==== + +* info trace + Display the contents of trace buffer. This command dumps the trace buffer + with simple formatting. For full pretty-printing, use the simpletrace.py + script on a binary trace file. + + The trace buffer is written into until full. The full trace buffer is + flushed and emptied. This means the 'info trace' will display few or no + entries if the buffer has just been flushed. + +* info trace-events + View available trace events and their state. State 1 means enabled, state 0 + means disabled. + +* trace-event NAME on|off + Enable/disable a given trace event. + +* trace-file on|off|flush|set + Enable/disable/flush the trace file or set the trace file name. + +==== Enabling/disabling trace events programmatically ==== + +The st_change_trace_event_state() function can be used to enable or disable trace +events at runtime inside QEMU: + + #include "trace.h" + + st_change_trace_event_state("virtio_irq", true); /* enable */ + [...] + st_change_trace_event_state("virtio_irq", false); /* disable */ + +==== Analyzing trace files ==== + +The "simple" backend produces binary trace files that can be formatted with the +simpletrace.py script. The script takes the trace-events file and the binary +trace: + + ./simpletrace.py trace-events trace-12345 + +You must ensure that the same trace-events file was used to build QEMU, +otherwise trace event declarations may have changed and output will not be +consistent. + +=== LTTng Userspace Tracer === + +The "ust" backend uses the LTTng Userspace Tracer library. There are no +monitor commands built into QEMU, instead UST utilities should be used to list, +enable/disable, and dump traces. diff -Nru qemu-kvm-0.12.5+noroms/dyngen-exec.h qemu-kvm-0.14.1/dyngen-exec.h --- qemu-kvm-0.12.5+noroms/dyngen-exec.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/dyngen-exec.h 2011-05-11 13:29:46.000000000 +0000 @@ -19,18 +19,7 @@ #if !defined(__DYNGEN_EXEC_H__) #define __DYNGEN_EXEC_H__ -/* prevent Solaris from trying to typedef FILE in gcc's - include/floatingpoint.h which will conflict with the - definition down below */ -#ifdef __sun__ -#define _FILEDEFED -#endif - -/* NOTE: standard headers should be used with special care at this - point because host CPU registers are used as global variables. Some - host headers do not allow that. */ -#include -#include +#include "qemu-common.h" #ifdef __OpenBSD__ #include @@ -39,73 +28,38 @@ /* XXX: This may be wrong for 64-bit ILP32 hosts. */ typedef void * host_reg_t; -#ifdef CONFIG_BSD -typedef struct __sFILE FILE; -#else -typedef struct FILE FILE; -#endif -extern int fprintf(FILE *, const char *, ...); -extern int fputs(const char *, FILE *); -extern int printf(const char *, ...); - #if defined(__i386__) #define AREG0 "ebp" -#define AREG1 "ebx" -#define AREG2 "esi" #elif defined(__x86_64__) #define AREG0 "r14" -#define AREG1 "r15" -#define AREG2 "r12" #elif defined(_ARCH_PPC) #define AREG0 "r27" -#define AREG1 "r24" -#define AREG2 "r25" #elif defined(__arm__) #define AREG0 "r7" -#define AREG1 "r4" -#define AREG2 "r5" #elif defined(__hppa__) #define AREG0 "r17" -#define AREG1 "r14" -#define AREG2 "r15" #elif defined(__mips__) #define AREG0 "s0" -#define AREG1 "s1" -#define AREG2 "fp" #elif defined(__sparc__) #ifdef CONFIG_SOLARIS #define AREG0 "g2" -#define AREG1 "g3" -#define AREG2 "g4" #else #ifdef __sparc_v9__ #define AREG0 "g5" -#define AREG1 "g6" -#define AREG2 "g7" #else #define AREG0 "g6" -#define AREG1 "g1" -#define AREG2 "g2" #endif #endif #elif defined(__s390__) #define AREG0 "r10" -#define AREG1 "r7" -#define AREG2 "r8" #elif defined(__alpha__) /* Note $15 is the frame pointer, so anything in op-i386.c that would require a frame pointer, like alloca, would probably loose. */ #define AREG0 "$15" -#define AREG1 "$9" -#define AREG2 "$10" #elif defined(__mc68000) #define AREG0 "%a5" -#define AREG1 "%a4" -#define AREG2 "%d7" #elif defined(__ia64__) #define AREG0 "r7" -#define AREG1 "r4" -#define AREG2 "r5" #else #error unsupported CPU #endif diff -Nru qemu-kvm-0.12.5+noroms/elf.h qemu-kvm-0.14.1/elf.h --- qemu-kvm-0.12.5+noroms/elf.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/elf.h 2011-05-11 13:29:46.000000000 +0000 @@ -119,7 +119,8 @@ */ #define EM_S390_OLD 0xA390 -#define EM_XILINX_MICROBLAZE 0xBAAB +#define EM_MICROBLAZE 189 +#define EM_MICROBLAZE_OLD 0xBAAB /* This is the info that is needed to parse the dynamic section of the file */ #define DT_NULL 0 @@ -146,8 +147,37 @@ #define DT_DEBUG 21 #define DT_TEXTREL 22 #define DT_JMPREL 23 +#define DT_BINDNOW 24 +#define DT_INIT_ARRAY 25 +#define DT_FINI_ARRAY 26 +#define DT_INIT_ARRAYSZ 27 +#define DT_FINI_ARRAYSZ 28 +#define DT_RUNPATH 29 +#define DT_FLAGS 30 +#define DT_LOOS 0x6000000d +#define DT_HIOS 0x6ffff000 #define DT_LOPROC 0x70000000 #define DT_HIPROC 0x7fffffff + +/* DT_ entries which fall between DT_VALRNGLO and DT_VALRNDHI use + the d_val field of the Elf*_Dyn structure. I.e. they contain scalars. */ +#define DT_VALRNGLO 0x6ffffd00 +#define DT_VALRNGHI 0x6ffffdff + +/* DT_ entries which fall between DT_ADDRRNGLO and DT_ADDRRNGHI use + the d_ptr field of the Elf*_Dyn structure. I.e. they contain pointers. */ +#define DT_ADDRRNGLO 0x6ffffe00 +#define DT_ADDRRNGHI 0x6ffffeff + +#define DT_VERSYM 0x6ffffff0 +#define DT_RELACOUNT 0x6ffffff9 +#define DT_RELCOUNT 0x6ffffffa +#define DT_FLAGS_1 0x6ffffffb +#define DT_VERDEF 0x6ffffffc +#define DT_VERDEFNUM 0x6ffffffd +#define DT_VERNEED 0x6ffffffe +#define DT_VERNEEDNUM 0x6fffffff + #define DT_MIPS_RLD_VERSION 0x70000001 #define DT_MIPS_TIME_STAMP 0x70000002 #define DT_MIPS_ICHECKSUM 0x70000003 @@ -206,6 +236,21 @@ #define AT_PLATFORM 15 /* string identifying CPU for optimizations */ #define AT_HWCAP 16 /* arch dependent hints at CPU capabilities */ #define AT_CLKTCK 17 /* frequency at which times() increments */ +#define AT_FPUCW 18 /* info about fpu initialization by kernel */ +#define AT_DCACHEBSIZE 19 /* data cache block size */ +#define AT_ICACHEBSIZE 20 /* instruction cache block size */ +#define AT_UCACHEBSIZE 21 /* unified cache block size */ +#define AT_IGNOREPPC 22 /* ppc only; entry should be ignored */ +#define AT_SECURE 23 /* boolean, was exec suid-like? */ +#define AT_BASE_PLATFORM 24 /* string identifying real platforms */ +#define AT_RANDOM 25 /* address of 16 random bytes */ +#define AT_EXECFN 31 /* filename of the executable */ +#define AT_SYSINFO 32 /* address of kernel entry point */ +#define AT_SYSINFO_EHDR 33 /* address of kernel vdso */ +#define AT_L1I_CACHESHAPE 34 /* shapes of the caches: */ +#define AT_L1D_CACHESHAPE 35 /* bits 0-3: cache associativity. */ +#define AT_L2_CACHESHAPE 36 /* bits 4-7: log2 of line size. */ +#define AT_L3_CACHESHAPE 37 /* val&~255: cache size. */ typedef struct dynamic{ Elf32_Sword d_tag; @@ -243,6 +288,8 @@ #define R_386_GOTOFF 9 #define R_386_GOTPC 10 #define R_386_NUM 11 +/* Not a dynamic reloc, so not included in R_386_NUM. Used in TCG. */ +#define R_386_PC8 23 #define R_MIPS_NONE 0 #define R_MIPS_16 1 diff -Nru qemu-kvm-0.12.5+noroms/envlist.h qemu-kvm-0.14.1/envlist.h --- qemu-kvm-0.12.5+noroms/envlist.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/envlist.h 2011-05-11 13:29:46.000000000 +0000 @@ -7,13 +7,13 @@ typedef struct envlist envlist_t; -extern envlist_t *envlist_create(void); -extern void envlist_free(envlist_t *); -extern int envlist_setenv(envlist_t *, const char *); -extern int envlist_unsetenv(envlist_t *, const char *); -extern int envlist_parse_set(envlist_t *, const char *); -extern int envlist_parse_unset(envlist_t *, const char *); -extern char **envlist_to_environ(const envlist_t *, size_t *); +envlist_t *envlist_create(void); +void envlist_free(envlist_t *); +int envlist_setenv(envlist_t *, const char *); +int envlist_unsetenv(envlist_t *, const char *); +int envlist_parse_set(envlist_t *, const char *); +int envlist_parse_unset(envlist_t *, const char *); +char **envlist_to_environ(const envlist_t *, size_t *); #ifdef __cplusplus } diff -Nru qemu-kvm-0.12.5+noroms/exec-all.h qemu-kvm-0.14.1/exec-all.h --- qemu-kvm-0.12.5+noroms/exec-all.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/exec-all.h 2011-05-11 13:29:46.000000000 +0000 @@ -25,6 +25,15 @@ /* allow to see translation results - the slowdown should be negligible, so we leave it */ #define DEBUG_DISAS +/* Page tracking code uses ram addresses in system mode, and virtual + addresses in userspace mode. Define tb_page_addr_t to be an appropriate + type. */ +#if defined(CONFIG_USER_ONLY) +typedef abi_ulong tb_page_addr_t; +#else +typedef ram_addr_t tb_page_addr_t; +#endif + /* is_jmp field values */ #define DISAS_NEXT 0 /* next instruction can be analyzed */ #define DISAS_JUMP 1 /* only pc was modified dynamically */ @@ -35,8 +44,20 @@ /* XXX: make safe guess about sizes */ #define MAX_OP_PER_INSTR 96 -/* A Call op needs up to 6 + 2N parameters (N = number of arguments). */ -#define MAX_OPC_PARAM 10 + +#if HOST_LONG_BITS == 32 +#define MAX_OPC_PARAM_PER_ARG 2 +#else +#define MAX_OPC_PARAM_PER_ARG 1 +#endif +#define MAX_OPC_PARAM_IARGS 4 +#define MAX_OPC_PARAM_OARGS 1 +#define MAX_OPC_PARAM_ARGS (MAX_OPC_PARAM_IARGS + MAX_OPC_PARAM_OARGS) + +/* A Call op needs up to 4 + 2N parameters on 32-bit archs, + * and up to 4 + N parameters on 64-bit archs + * (N = number of input arguments + output arguments). */ +#define MAX_OPC_PARAM (4 + (MAX_OPC_PARAM_PER_ARG * MAX_OPC_PARAM_ARGS)) #define OPC_BUF_SIZE 640 #define OPC_MAX_SIZE (OPC_BUF_SIZE - MAX_OP_PER_INSTR) @@ -49,12 +70,8 @@ #define OPPARAM_BUF_SIZE (OPC_BUF_SIZE * MAX_OPC_PARAM) extern target_ulong gen_opc_pc[OPC_BUF_SIZE]; -extern target_ulong gen_opc_npc[OPC_BUF_SIZE]; -extern uint8_t gen_opc_cc_op[OPC_BUF_SIZE]; extern uint8_t gen_opc_instr_start[OPC_BUF_SIZE]; extern uint16_t gen_opc_icount[OPC_BUF_SIZE]; -extern target_ulong gen_opc_jump_pc[2]; -extern uint32_t gen_opc_hflags[OPC_BUF_SIZE]; #include "qemu-log.h" @@ -63,16 +80,12 @@ void gen_pc_load(CPUState *env, struct TranslationBlock *tb, unsigned long searched_pc, int pc_pos, void *puc); -unsigned long code_gen_max_block_size(void); void cpu_gen_init(void); int cpu_gen_code(CPUState *env, struct TranslationBlock *tb, int *gen_code_size_ptr); int cpu_restore_state(struct TranslationBlock *tb, CPUState *env, unsigned long searched_pc, void *puc); -int cpu_restore_state_copy(struct TranslationBlock *tb, - CPUState *env, unsigned long searched_pc, - void *puc); void cpu_resume_from_signal(CPUState *env1, void *puc); void cpu_io_recompile(CPUState *env, void *retaddr); TranslationBlock *tb_gen_code(CPUState *env, @@ -81,22 +94,16 @@ void cpu_exec_init(CPUState *env); void QEMU_NORETURN cpu_loop_exit(void); int page_unprotect(target_ulong address, unsigned long pc, void *puc); -void tb_invalidate_phys_page_range(target_phys_addr_t start, target_phys_addr_t end, +void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end, int is_cpu_write_access); void tb_invalidate_page_range(target_ulong start, target_ulong end); void tlb_flush_page(CPUState *env, target_ulong addr); void tlb_flush(CPUState *env, int flush_global); -int tlb_set_page_exec(CPUState *env, target_ulong vaddr, - target_phys_addr_t paddr, int prot, - int mmu_idx, int is_softmmu); -static inline int tlb_set_page(CPUState *env1, target_ulong vaddr, - target_phys_addr_t paddr, int prot, - int mmu_idx, int is_softmmu) -{ - if (prot & PAGE_READ) - prot |= PAGE_EXEC; - return tlb_set_page_exec(env1, vaddr, paddr, prot, mmu_idx, is_softmmu); -} +#if !defined(CONFIG_USER_ONLY) +void tlb_set_page(CPUState *env, target_ulong vaddr, + target_phys_addr_t paddr, int prot, + int mmu_idx, target_ulong size); +#endif #define CODE_GEN_ALIGN 16 /* must be >= of the size of a icache line */ @@ -134,13 +141,13 @@ /* first and second physical page containing code. The lower bit of the pointer tells the index in page_next[] */ struct TranslationBlock *page_next[2]; - target_ulong page_addr[2]; + tb_page_addr_t page_addr[2]; /* the following data are used to directly call another TB from the code of this one. */ uint16_t tb_next_offset[2]; /* offset of original jump target */ #ifdef USE_DIRECT_JUMP - uint16_t tb_jmp_offset[4]; /* offset of jump instruction */ + uint16_t tb_jmp_offset[2]; /* offset of jump instruction */ #else unsigned long tb_next[2]; /* address of jump generated code */ #endif @@ -168,26 +175,24 @@ | (tmp & TB_JMP_ADDR_MASK)); } -static inline unsigned int tb_phys_hash_func(unsigned long pc) +static inline unsigned int tb_phys_hash_func(tb_page_addr_t pc) { - return pc & (CODE_GEN_PHYS_HASH_SIZE - 1); + return (pc >> 2) & (CODE_GEN_PHYS_HASH_SIZE - 1); } TranslationBlock *tb_alloc(target_ulong pc); void tb_free(TranslationBlock *tb); void tb_flush(CPUState *env); -void tb_link_phys(TranslationBlock *tb, - target_ulong phys_pc, target_ulong phys_page2); -void tb_phys_invalidate(TranslationBlock *tb, target_ulong page_addr); +void tb_link_page(TranslationBlock *tb, + tb_page_addr_t phys_pc, tb_page_addr_t phys_page2); +void tb_phys_invalidate(TranslationBlock *tb, tb_page_addr_t page_addr); extern TranslationBlock *tb_phys_hash[CODE_GEN_PHYS_HASH_SIZE]; -extern uint8_t *code_gen_ptr; -extern int code_gen_max_blocks; #if defined(USE_DIRECT_JUMP) #if defined(_ARCH_PPC) -extern void ppc_tb_set_jmp_target(unsigned long jmp_addr, unsigned long addr); +void ppc_tb_set_jmp_target(unsigned long jmp_addr, unsigned long addr); #define tb_set_jmp_target1 ppc_tb_set_jmp_target #elif defined(__i386__) || defined(__x86_64__) static inline void tb_set_jmp_target1(unsigned long jmp_addr, unsigned long addr) @@ -199,9 +204,7 @@ #elif defined(__arm__) static inline void tb_set_jmp_target1(unsigned long jmp_addr, unsigned long addr) { -#if QEMU_GNUC_PREREQ(4, 1) - void __clear_cache(char *beg, char *end); -#else +#if !QEMU_GNUC_PREREQ(4, 1) register unsigned long _beg __asm ("a1"); register unsigned long _end __asm ("a2"); register unsigned long _flg __asm ("a3"); @@ -213,7 +216,7 @@ | (((addr - (jmp_addr + 8)) >> 2) & 0xffffff); #if QEMU_GNUC_PREREQ(4, 1) - __clear_cache((char *) jmp_addr, (char *) jmp_addr + 4); + __builtin___clear_cache((char *) jmp_addr, (char *) jmp_addr + 4); #else /* flush icache */ _beg = jmp_addr; @@ -231,9 +234,6 @@ offset = tb->tb_jmp_offset[n]; tb_set_jmp_target1((unsigned long)(tb->tc_ptr + offset), addr); - offset = tb->tb_jmp_offset[n + 2]; - if (offset != 0xffff) - tb_set_jmp_target1((unsigned long)(tb->tc_ptr + offset), addr); } #else @@ -263,10 +263,6 @@ TranslationBlock *tb_find_pc(unsigned long pc_ptr); -extern CPUWriteMemoryFunc *io_mem_write[IO_MEM_NB_ENTRIES][4]; -extern CPUReadMemoryFunc *io_mem_read[IO_MEM_NB_ENTRIES][4]; -extern void *io_mem_opaque[IO_MEM_NB_ENTRIES]; - #include "qemu-lock.h" extern spinlock_t tb_lock; @@ -275,6 +271,10 @@ #if !defined(CONFIG_USER_ONLY) +extern CPUWriteMemoryFunc *io_mem_write[IO_MEM_NB_ENTRIES][4]; +extern CPUReadMemoryFunc *io_mem_read[IO_MEM_NB_ENTRIES][4]; +extern void *io_mem_opaque[IO_MEM_NB_ENTRIES]; + void tlb_fill(target_ulong addr, int is_write, int mmu_idx, void *retaddr); @@ -303,7 +303,7 @@ #endif #if defined(CONFIG_USER_ONLY) -static inline target_ulong get_phys_addr_code(CPUState *env1, target_ulong addr) +static inline tb_page_addr_t get_page_addr_code(CPUState *env1, target_ulong addr) { return addr; } @@ -311,7 +311,7 @@ /* NOTE: this function can trigger an exception */ /* NOTE2: the returned address is not exactly the physical address: it is the offset relative to phys_ram_base */ -static inline target_ulong get_phys_addr_code(CPUState *env1, target_ulong addr) +static inline tb_page_addr_t get_page_addr_code(CPUState *env1, target_ulong addr) { int mmu_idx, page_index, pd; void *p; @@ -332,21 +332,7 @@ } p = (void *)(unsigned long)addr + env1->tlb_table[mmu_idx][page_index].addend; - return qemu_ram_addr_from_host(p); -} - -/* Deterministic execution requires that IO only be performed on the last - instruction of a TB so that interrupts take effect immediately. */ -static inline int can_do_io(CPUState *env) -{ - if (!use_icount) - return 1; - - /* If not executing code then assume we are ok. */ - if (!env->current_tb) - return 1; - - return env->can_do_io != 0; + return qemu_ram_addr_from_host_nofail(p); } #endif @@ -357,4 +343,7 @@ /* vl.c */ extern int singlestep; +/* cpu-exec.c */ +extern volatile sig_atomic_t exit_request; + #endif diff -Nru qemu-kvm-0.12.5+noroms/exec.c qemu-kvm-0.14.1/exec.c --- qemu-kvm-0.12.5+noroms/exec.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/exec.c 2011-05-11 13:29:46.000000000 +0000 @@ -23,17 +23,10 @@ #include #include #endif -#include -#include -#include -#include -#include -#include -#include +#include "qemu-common.h" #include "cpu.h" #include "exec-all.h" -#include "qemu-common.h" #include "cache-utils.h" #if !defined(TARGET_IA64) @@ -42,10 +35,28 @@ #include "qemu-kvm.h" #include "hw/hw.h" +#include "hw/qdev.h" #include "osdep.h" #include "kvm.h" +#include "qemu-timer.h" #if defined(CONFIG_USER_ONLY) #include +#include +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) +#include +#if __FreeBSD_version >= 700104 +#define HAVE_KINFO_GETVMMAP +#define sigqueue sigqueue_freebsd /* avoid redefinition */ +#include +#include +#include +#define _KERNEL +#include +#undef _KERNEL +#undef sigqueue +#include +#endif +#endif #endif //#define DEBUG_TB_INVALIDATE @@ -67,27 +78,8 @@ #define SMC_BITMAP_USE_THRESHOLD 10 -#if defined(TARGET_SPARC64) -#define TARGET_PHYS_ADDR_SPACE_BITS 41 -#elif defined(TARGET_SPARC) -#define TARGET_PHYS_ADDR_SPACE_BITS 36 -#elif defined(TARGET_ALPHA) -#define TARGET_PHYS_ADDR_SPACE_BITS 42 -#define TARGET_VIRT_ADDR_SPACE_BITS 42 -#elif defined(TARGET_PPC64) -#define TARGET_PHYS_ADDR_SPACE_BITS 42 -#elif defined(TARGET_X86_64) -#define TARGET_PHYS_ADDR_SPACE_BITS 42 -#elif defined(TARGET_I386) -#define TARGET_PHYS_ADDR_SPACE_BITS 36 -#elif defined(TARGET_IA64) -#define TARGET_PHYS_ADDR_SPACE_BITS 36 -#else -#define TARGET_PHYS_ADDR_SPACE_BITS 32 -#endif - static TranslationBlock *tbs; -int code_gen_max_blocks; +static int code_gen_max_blocks; TranslationBlock *tb_phys_hash[CODE_GEN_PHYS_HASH_SIZE]; static int nb_tbs; /* any access to the tbs or the page table must use this lock */ @@ -114,26 +106,13 @@ static unsigned long code_gen_buffer_size; /* threshold to flush the translated code buffer */ static unsigned long code_gen_buffer_max_size; -uint8_t *code_gen_ptr; +static uint8_t *code_gen_ptr; #if !defined(CONFIG_USER_ONLY) int phys_ram_fd; -uint8_t *phys_ram_dirty; -uint8_t *bios_mem; static int in_migration; -typedef struct RAMBlock { - uint8_t *host; - ram_addr_t offset; - ram_addr_t length; - struct RAMBlock *next; -} RAMBlock; - -static RAMBlock *ram_blocks; -/* TODO: When we implement (and use) ram deallocation (e.g. for hotplug) - then we can no longer assume contiguous ram offsets, and external uses - of this variable will break. */ -ram_addr_t last_ram_offset; +RAMList ram_list = { .blocks = QLIST_HEAD_INITIALIZER(ram_list) }; #endif CPUState *first_cpu; @@ -160,36 +139,67 @@ #endif } PageDesc; -typedef struct PhysPageDesc { - /* offset in host memory of the page + io_index in the low bits */ - ram_addr_t phys_offset; - ram_addr_t region_offset; -} PhysPageDesc; +/* In system mode we want L1_MAP to be based on ram offsets, + while in user mode we want it to be based on virtual addresses. */ +#if !defined(CONFIG_USER_ONLY) +#if HOST_LONG_BITS < TARGET_PHYS_ADDR_SPACE_BITS +# define L1_MAP_ADDR_SPACE_BITS HOST_LONG_BITS +#else +# define L1_MAP_ADDR_SPACE_BITS TARGET_PHYS_ADDR_SPACE_BITS +#endif +#else +# define L1_MAP_ADDR_SPACE_BITS TARGET_VIRT_ADDR_SPACE_BITS +#endif +/* Size of the L2 (and L3, etc) page tables. */ #define L2_BITS 10 -#if defined(CONFIG_USER_ONLY) && defined(TARGET_VIRT_ADDR_SPACE_BITS) -/* XXX: this is a temporary hack for alpha target. - * In the future, this is to be replaced by a multi-level table - * to actually be able to handle the complete 64 bits address space. - */ -#define L1_BITS (TARGET_VIRT_ADDR_SPACE_BITS - L2_BITS - TARGET_PAGE_BITS) +#define L2_SIZE (1 << L2_BITS) + +/* The bits remaining after N lower levels of page tables. */ +#define P_L1_BITS_REM \ + ((TARGET_PHYS_ADDR_SPACE_BITS - TARGET_PAGE_BITS) % L2_BITS) +#define V_L1_BITS_REM \ + ((L1_MAP_ADDR_SPACE_BITS - TARGET_PAGE_BITS) % L2_BITS) + +/* Size of the L1 page table. Avoid silly small sizes. */ +#if P_L1_BITS_REM < 4 +#define P_L1_BITS (P_L1_BITS_REM + L2_BITS) #else -#define L1_BITS (32 - L2_BITS - TARGET_PAGE_BITS) +#define P_L1_BITS P_L1_BITS_REM #endif -#define L1_SIZE (1 << L1_BITS) -#define L2_SIZE (1 << L2_BITS) +#if V_L1_BITS_REM < 4 +#define V_L1_BITS (V_L1_BITS_REM + L2_BITS) +#else +#define V_L1_BITS V_L1_BITS_REM +#endif + +#define P_L1_SIZE ((target_phys_addr_t)1 << P_L1_BITS) +#define V_L1_SIZE ((target_ulong)1 << V_L1_BITS) + +#define P_L1_SHIFT (TARGET_PHYS_ADDR_SPACE_BITS - TARGET_PAGE_BITS - P_L1_BITS) +#define V_L1_SHIFT (L1_MAP_ADDR_SPACE_BITS - TARGET_PAGE_BITS - V_L1_BITS) unsigned long qemu_real_host_page_size; unsigned long qemu_host_page_bits; unsigned long qemu_host_page_size; unsigned long qemu_host_page_mask; -/* XXX: for system emulation, it could just be an array */ -static PageDesc *l1_map[L1_SIZE]; -static PhysPageDesc **l1_phys_map; +/* This is a multi-level map on the virtual address space. + The bottom level has pointers to PageDesc. */ +static void *l1_map[V_L1_SIZE]; #if !defined(CONFIG_USER_ONLY) +typedef struct PhysPageDesc { + /* offset in host memory of the page + io_index in the low bits */ + ram_addr_t phys_offset; + ram_addr_t region_offset; +} PhysPageDesc; + +/* This is a multi-level map on the physical address space. + The bottom level has pointers to PhysPageDesc. */ +static void *l1_phys_map[P_L1_SIZE]; + static void io_mem_init(void); /* io memory support */ @@ -201,25 +211,22 @@ #endif /* log support */ +#ifdef WIN32 +static const char *logfilename = "qemu.log"; +#else static const char *logfilename = "/tmp/qemu.log"; +#endif FILE *logfile; int loglevel; static int log_append = 0; /* statistics */ +#if !defined(CONFIG_USER_ONLY) static int tlb_flush_count; +#endif static int tb_flush_count; static int tb_phys_invalidate_count; -#define SUBPAGE_IDX(addr) ((addr) & ~TARGET_PAGE_MASK) -typedef struct subpage_t { - target_phys_addr_t base; - CPUReadMemoryFunc * const *mem_read[TARGET_PAGE_SIZE][4]; - CPUWriteMemoryFunc * const *mem_write[TARGET_PAGE_SIZE][4]; - void *opaque[TARGET_PAGE_SIZE][2][4]; - ram_addr_t region_offset[TARGET_PAGE_SIZE][2][4]; -} subpage_t; - #ifdef _WIN32 static void map_exec(void *addr, long size) { @@ -268,130 +275,167 @@ while ((1 << qemu_host_page_bits) < qemu_host_page_size) qemu_host_page_bits++; qemu_host_page_mask = ~(qemu_host_page_size - 1); - l1_phys_map = qemu_vmalloc(L1_SIZE * sizeof(void *)); - memset(l1_phys_map, 0, L1_SIZE * sizeof(void *)); -#if !defined(_WIN32) && defined(CONFIG_USER_ONLY) +#if defined(CONFIG_BSD) && defined(CONFIG_USER_ONLY) { - long long startaddr, endaddr; +#ifdef HAVE_KINFO_GETVMMAP + struct kinfo_vmentry *freep; + int i, cnt; + + freep = kinfo_getvmmap(getpid(), &cnt); + if (freep) { + mmap_lock(); + for (i = 0; i < cnt; i++) { + unsigned long startaddr, endaddr; + + startaddr = freep[i].kve_start; + endaddr = freep[i].kve_end; + if (h2g_valid(startaddr)) { + startaddr = h2g(startaddr) & TARGET_PAGE_MASK; + + if (h2g_valid(endaddr)) { + endaddr = h2g(endaddr); + page_set_flags(startaddr, endaddr, PAGE_RESERVED); + } else { +#if TARGET_ABI_BITS <= L1_MAP_ADDR_SPACE_BITS + endaddr = ~0ul; + page_set_flags(startaddr, endaddr, PAGE_RESERVED); +#endif + } + } + } + free(freep); + mmap_unlock(); + } +#else FILE *f; - int n; - mmap_lock(); last_brk = (unsigned long)sbrk(0); - f = fopen("/proc/self/maps", "r"); + + f = fopen("/compat/linux/proc/self/maps", "r"); if (f) { + mmap_lock(); + do { - n = fscanf (f, "%llx-%llx %*[^\n]\n", &startaddr, &endaddr); - if (n == 2) { - startaddr = MIN(startaddr, - (1ULL << TARGET_PHYS_ADDR_SPACE_BITS) - 1); - endaddr = MIN(endaddr, - (1ULL << TARGET_PHYS_ADDR_SPACE_BITS) - 1); - page_set_flags(startaddr & TARGET_PAGE_MASK, - TARGET_PAGE_ALIGN(endaddr), - PAGE_RESERVED); + unsigned long startaddr, endaddr; + int n; + + n = fscanf (f, "%lx-%lx %*[^\n]\n", &startaddr, &endaddr); + + if (n == 2 && h2g_valid(startaddr)) { + startaddr = h2g(startaddr) & TARGET_PAGE_MASK; + + if (h2g_valid(endaddr)) { + endaddr = h2g(endaddr); + } else { + endaddr = ~0ul; + } + page_set_flags(startaddr, endaddr, PAGE_RESERVED); } } while (!feof(f)); + fclose(f); + mmap_unlock(); } - mmap_unlock(); - } #endif -} - -static inline PageDesc **page_l1_map(target_ulong index) -{ -#if TARGET_LONG_BITS > 32 - /* Host memory outside guest VM. For 32-bit targets we have already - excluded high addresses. */ - if (index > ((target_ulong)L2_SIZE * L1_SIZE)) - return NULL; + } #endif - return &l1_map[index >> L2_BITS]; } -static inline PageDesc *page_find_alloc(target_ulong index) +static PageDesc *page_find_alloc(tb_page_addr_t index, int alloc) { - PageDesc **lp, *p; - lp = page_l1_map(index); - if (!lp) - return NULL; + PageDesc *pd; + void **lp; + int i; - p = *lp; - if (!p) { - /* allocate if not found */ #if defined(CONFIG_USER_ONLY) - size_t len = sizeof(PageDesc) * L2_SIZE; - /* Don't use qemu_malloc because it may recurse. */ - p = mmap(NULL, len, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - *lp = p; - if (h2g_valid(p)) { - unsigned long addr = h2g(p); - page_set_flags(addr & TARGET_PAGE_MASK, - TARGET_PAGE_ALIGN(addr + len), - PAGE_RESERVED); - } + /* We can't use qemu_malloc because it may recurse into a locked mutex. */ +# define ALLOC(P, SIZE) \ + do { \ + P = mmap(NULL, SIZE, PROT_READ | PROT_WRITE, \ + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); \ + } while (0) #else - p = qemu_mallocz(sizeof(PageDesc) * L2_SIZE); - *lp = p; +# define ALLOC(P, SIZE) \ + do { P = qemu_mallocz(SIZE); } while (0) #endif + + /* Level 1. Always allocated. */ + lp = l1_map + ((index >> V_L1_SHIFT) & (V_L1_SIZE - 1)); + + /* Level 2..N-1. */ + for (i = V_L1_SHIFT / L2_BITS - 1; i > 0; i--) { + void **p = *lp; + + if (p == NULL) { + if (!alloc) { + return NULL; + } + ALLOC(p, sizeof(void *) * L2_SIZE); + *lp = p; + } + + lp = p + ((index >> (i * L2_BITS)) & (L2_SIZE - 1)); } - return p + (index & (L2_SIZE - 1)); + + pd = *lp; + if (pd == NULL) { + if (!alloc) { + return NULL; + } + ALLOC(pd, sizeof(PageDesc) * L2_SIZE); + *lp = pd; + } + +#undef ALLOC + + return pd + (index & (L2_SIZE - 1)); } -static inline PageDesc *page_find(target_ulong index) +static inline PageDesc *page_find(tb_page_addr_t index) { - PageDesc **lp, *p; - lp = page_l1_map(index); - if (!lp) - return NULL; - - p = *lp; - if (!p) { - return NULL; - } - return p + (index & (L2_SIZE - 1)); + return page_find_alloc(index, 0); } +#if !defined(CONFIG_USER_ONLY) static PhysPageDesc *phys_page_find_alloc(target_phys_addr_t index, int alloc) { - void **lp, **p; PhysPageDesc *pd; + void **lp; + int i; - p = (void **)l1_phys_map; -#if TARGET_PHYS_ADDR_SPACE_BITS > 32 + /* Level 1. Always allocated. */ + lp = l1_phys_map + ((index >> P_L1_SHIFT) & (P_L1_SIZE - 1)); -#if TARGET_PHYS_ADDR_SPACE_BITS > (32 + L1_BITS) -#error unsupported TARGET_PHYS_ADDR_SPACE_BITS -#endif - lp = p + ((index >> (L1_BITS + L2_BITS)) & (L1_SIZE - 1)); - p = *lp; - if (!p) { - /* allocate if not found */ - if (!alloc) - return NULL; - p = qemu_vmalloc(sizeof(void *) * L1_SIZE); - memset(p, 0, sizeof(void *) * L1_SIZE); - *lp = p; + /* Level 2..N-1. */ + for (i = P_L1_SHIFT / L2_BITS - 1; i > 0; i--) { + void **p = *lp; + if (p == NULL) { + if (!alloc) { + return NULL; + } + *lp = p = qemu_mallocz(sizeof(void *) * L2_SIZE); + } + lp = p + ((index >> (i * L2_BITS)) & (L2_SIZE - 1)); } -#endif - lp = p + ((index >> L2_BITS) & (L1_SIZE - 1)); + pd = *lp; - if (!pd) { + if (pd == NULL) { int i; - /* allocate if not found */ - if (!alloc) + + if (!alloc) { return NULL; - pd = qemu_vmalloc(sizeof(PhysPageDesc) * L2_SIZE); - *lp = pd; + } + + *lp = pd = qemu_malloc(sizeof(PhysPageDesc) * L2_SIZE); + for (i = 0; i < L2_SIZE; i++) { - pd[i].phys_offset = IO_MEM_UNASSIGNED; - pd[i].region_offset = (index + i) << TARGET_PAGE_BITS; + pd[i].phys_offset = IO_MEM_UNASSIGNED; + pd[i].region_offset = (index + i) << TARGET_PAGE_BITS; } } - return ((PhysPageDesc *)pd) + (index & (L2_SIZE - 1)); + + return pd + (index & (L2_SIZE - 1)); } static inline PhysPageDesc *phys_page_find(target_phys_addr_t index) @@ -399,7 +443,6 @@ return phys_page_find_alloc(index, 0); } -#if !defined(CONFIG_USER_ONLY) static void tlb_protect_code(ram_addr_t ram_addr); static void tlb_unprotect_code_phys(CPUState *env, ram_addr_t ram_addr, target_ulong vaddr); @@ -416,7 +459,8 @@ #endif #ifdef USE_STATIC_CODE_GEN_BUFFER -static uint8_t static_code_gen_buffer[DEFAULT_CODE_GEN_BUFFER_SIZE]; +static uint8_t static_code_gen_buffer[DEFAULT_CODE_GEN_BUFFER_SIZE] + __attribute__((aligned (CODE_GEN_ALIGN))); #endif static void code_gen_alloc(unsigned long tb_size) @@ -466,6 +510,13 @@ start = (void *) 0x01000000UL; if (code_gen_buffer_size > 16 * 1024 * 1024) code_gen_buffer_size = 16 * 1024 * 1024; +#elif defined(__s390x__) + /* Map the buffer so that we can use direct calls and branches. */ + /* We have a +- 4GB range on the branches; leave some slop. */ + if (code_gen_buffer_size > (3ul * 1024 * 1024 * 1024)) { + code_gen_buffer_size = 3ul * 1024 * 1024 * 1024; + } + start = (void *)0x90000000UL; #endif code_gen_buffer = mmap(start, code_gen_buffer_size, PROT_WRITE | PROT_READ | PROT_EXEC, @@ -475,7 +526,8 @@ exit(1); } } -#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) +#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) \ + || defined(__DragonFly__) || defined(__OpenBSD__) { int flags; void *addr = NULL; @@ -488,6 +540,13 @@ /* Cannot map more than that */ if (code_gen_buffer_size > (800 * 1024 * 1024)) code_gen_buffer_size = (800 * 1024 * 1024); +#elif defined(__sparc_v9__) + // Map the buffer below 2G, so we can use direct calls and branches + flags |= MAP_FIXED; + addr = (void *) 0x60000000UL; + if (code_gen_buffer_size > (512 * 1024 * 1024)) { + code_gen_buffer_size = (512 * 1024 * 1024); + } #endif code_gen_buffer = mmap(addr, code_gen_buffer_size, PROT_WRITE | PROT_READ | PROT_EXEC, @@ -504,7 +563,7 @@ #endif /* !USE_STATIC_CODE_GEN_BUFFER */ map_exec(code_gen_prologue, sizeof(code_gen_prologue)); code_gen_buffer_max_size = code_gen_buffer_size - - code_gen_max_block_size(); + (TCG_MAX_OP_SIZE * OPC_MAX_SIZE); code_gen_max_blocks = code_gen_buffer_size / CODE_GEN_AVG_BLOCK_SIZE; tbs = qemu_malloc(code_gen_max_blocks * sizeof(TranslationBlock)); } @@ -521,25 +580,15 @@ #if !defined(CONFIG_USER_ONLY) io_mem_init(); #endif +#if !defined(CONFIG_USER_ONLY) || !defined(CONFIG_USE_GUEST_BASE) + /* There's no guest base to take into account, so go ahead and + initialize the prologue now. */ + tcg_prologue_init(&tcg_ctx); +#endif } #if defined(CPU_SAVE_VERSION) && !defined(CONFIG_USER_ONLY) -static void cpu_common_pre_save(void *opaque) -{ - CPUState *env = opaque; - - cpu_synchronize_state(env); -} - -static int cpu_common_pre_load(void *opaque) -{ - CPUState *env = opaque; - - cpu_synchronize_state(env); - return 0; -} - static int cpu_common_post_load(void *opaque, int version_id) { CPUState *env = opaque; @@ -557,8 +606,6 @@ .version_id = 1, .minimum_version_id = 1, .minimum_version_id_old = 1, - .pre_save = cpu_common_pre_save, - .pre_load = cpu_common_pre_load, .post_load = cpu_common_post_load, .fields = (VMStateField []) { VMSTATE_UINT32(halted, CPUState), @@ -610,8 +657,8 @@ cpu_list_unlock(); #endif #if defined(CPU_SAVE_VERSION) && !defined(CONFIG_USER_ONLY) - vmstate_register(cpu_index, &vmstate_cpu_common, env); - register_savevm("cpu", cpu_index, CPU_SAVE_VERSION, + vmstate_register(NULL, cpu_index, &vmstate_cpu_common, env); + register_savevm(NULL, "cpu", cpu_index, CPU_SAVE_VERSION, cpu_save, cpu_load, env); #endif } @@ -625,24 +672,37 @@ p->code_write_count = 0; } -/* set to NULL all the 'first_tb' fields in all PageDescs */ -static void page_flush_tb(void) +/* Set to NULL all the 'first_tb' fields in all PageDescs. */ + +static void page_flush_tb_1 (int level, void **lp) { - int i, j; - PageDesc *p; + int i; - for(i = 0; i < L1_SIZE; i++) { - p = l1_map[i]; - if (p) { - for(j = 0; j < L2_SIZE; j++) { - p->first_tb = NULL; - invalidate_page_bitmap(p); - p++; - } + if (*lp == NULL) { + return; + } + if (level == 0) { + PageDesc *pd = *lp; + for (i = 0; i < L2_SIZE; ++i) { + pd[i].first_tb = NULL; + invalidate_page_bitmap(pd + i); + } + } else { + void **pp = *lp; + for (i = 0; i < L2_SIZE; ++i) { + page_flush_tb_1 (level - 1, pp + i); } } } +static void page_flush_tb(void) +{ + int i; + for (i = 0; i < V_L1_SIZE; i++) { + page_flush_tb_1(V_L1_SHIFT / L2_BITS - 1, l1_map + i); + } +} + /* flush all the translation blocks */ /* XXX: tb_flush is currently not thread safe */ void tb_flush(CPUState *env1) @@ -778,12 +838,12 @@ tb_set_jmp_target(tb, n, (unsigned long)(tb->tc_ptr + tb->tb_next_offset[n])); } -void tb_phys_invalidate(TranslationBlock *tb, target_ulong page_addr) +void tb_phys_invalidate(TranslationBlock *tb, tb_page_addr_t page_addr) { CPUState *env; PageDesc *p; unsigned int h, n1; - target_phys_addr_t phys_pc; + tb_page_addr_t phys_pc; TranslationBlock *tb1, *tb2; /* remove the TB from the hash list */ @@ -895,10 +955,11 @@ { TranslationBlock *tb; uint8_t *tc_ptr; - target_ulong phys_pc, phys_page2, virt_page2; + tb_page_addr_t phys_pc, phys_page2; + target_ulong virt_page2; int code_gen_size; - phys_pc = get_phys_addr_code(env, pc); + phys_pc = get_page_addr_code(env, pc); tb = tb_alloc(pc); if (!tb) { /* flush must be done */ @@ -920,9 +981,9 @@ virt_page2 = (pc + tb->size - 1) & TARGET_PAGE_MASK; phys_page2 = -1; if ((pc & TARGET_PAGE_MASK) != virt_page2) { - phys_page2 = get_phys_addr_code(env, virt_page2); + phys_page2 = get_page_addr_code(env, virt_page2); } - tb_link_phys(tb, phys_pc, phys_page2); + tb_link_page(tb, phys_pc, phys_page2); return tb; } @@ -931,12 +992,12 @@ the same physical page. 'is_cpu_write_access' should be true if called from a real cpu write access: the virtual CPU will exit the current TB if code is modified inside this TB. */ -void tb_invalidate_phys_page_range(target_phys_addr_t start, target_phys_addr_t end, +void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end, int is_cpu_write_access) { TranslationBlock *tb, *tb_next, *saved_tb; CPUState *env = cpu_single_env; - target_ulong tb_start, tb_end; + tb_page_addr_t tb_start, tb_end; PageDesc *p; int n; #ifdef TARGET_HAS_PRECISE_SMC @@ -1038,7 +1099,7 @@ } /* len must be <= 8 and start must be a multiple of len */ -static inline void tb_invalidate_phys_page_fast(target_phys_addr_t start, int len) +static inline void tb_invalidate_phys_page_fast(tb_page_addr_t start, int len) { PageDesc *p; int offset, b; @@ -1065,7 +1126,7 @@ } #if !defined(CONFIG_SOFTMMU) -static void tb_invalidate_phys_page(target_phys_addr_t addr, +static void tb_invalidate_phys_page(tb_page_addr_t addr, unsigned long pc, void *puc) { TranslationBlock *tb; @@ -1127,13 +1188,13 @@ /* add the tb in the target page and protect it if necessary */ static inline void tb_alloc_page(TranslationBlock *tb, - unsigned int n, target_ulong page_addr) + unsigned int n, tb_page_addr_t page_addr) { PageDesc *p; TranslationBlock *last_first_tb; tb->page_addr[n] = page_addr; - p = page_find_alloc(page_addr >> TARGET_PAGE_BITS); + p = page_find_alloc(page_addr >> TARGET_PAGE_BITS, 1); tb->page_next[n] = p->first_tb; last_first_tb = p->first_tb; p->first_tb = (TranslationBlock *)((long)tb | n); @@ -1159,7 +1220,6 @@ continue; prot |= p2->flags; p2->flags &= ~PAGE_WRITE; - page_get_flags(addr); } mprotect(g2h(page_addr), qemu_host_page_size, (prot & PAGE_BITS) & ~PAGE_WRITE); @@ -1208,8 +1268,8 @@ /* add a new TB and link it to the physical page tables. phys_page2 is (-1) to indicate that only one page contains the TB. */ -void tb_link_phys(TranslationBlock *tb, - target_ulong phys_pc, target_ulong phys_page2) +void tb_link_page(TranslationBlock *tb, + tb_page_addr_t phys_pc, tb_page_addr_t phys_page2) { unsigned int h; TranslationBlock **ptb; @@ -1325,6 +1385,12 @@ } #if defined(TARGET_HAS_ICE) +#if defined(CONFIG_USER_ONLY) +static void breakpoint_invalidate(CPUState *env, target_ulong pc) +{ + tb_invalidate_phys_page_range(pc, pc + 1, 0); +} +#else static void breakpoint_invalidate(CPUState *env, target_ulong pc) { target_phys_addr_t addr; @@ -1343,7 +1409,20 @@ tb_invalidate_phys_page_range(ram_addr, ram_addr + 1, 0); } #endif +#endif /* TARGET_HAS_ICE */ + +#if defined(CONFIG_USER_ONLY) +void cpu_watchpoint_remove_all(CPUState *env, int mask) + +{ +} +int cpu_watchpoint_insert(CPUState *env, target_ulong addr, target_ulong len, + int flags, CPUWatchpoint **watchpoint) +{ + return -ENOSYS; +} +#else /* Add a watchpoint. */ int cpu_watchpoint_insert(CPUState *env, target_ulong addr, target_ulong len, int flags, CPUWatchpoint **watchpoint) @@ -1413,6 +1492,7 @@ cpu_watchpoint_remove_by_ref(env, wp); } } +#endif /* Add a breakpoint. */ int cpu_breakpoint_insert(CPUState *env, target_ulong pc, int flags, @@ -1543,24 +1623,22 @@ static void cpu_unlink_tb(CPUState *env) { -#if defined(CONFIG_USE_NPTL) /* FIXME: TB unchaining isn't SMP safe. For now just ignore the problem and hope the cpu will stop of its own accord. For userspace emulation this often isn't actually as bad as it sounds. Often signals are used primarily to interrupt blocking syscalls. */ -#else TranslationBlock *tb; static spinlock_t interrupt_lock = SPIN_LOCK_UNLOCKED; + spin_lock(&interrupt_lock); tb = env->current_tb; /* if the cpu is currently executing code, we must unlink it and all the potentially executing TB */ - if (tb && !testandset(&interrupt_lock)) { + if (tb) { env->current_tb = NULL; tb_reset_jump_recursive(tb); - resetlock(&interrupt_lock); } -#endif + spin_unlock(&interrupt_lock); } /* mask must never be zero, except for A20 change call */ @@ -1640,6 +1718,88 @@ { 0, NULL, NULL }, }; +#ifndef CONFIG_USER_ONLY +static QLIST_HEAD(memory_client_list, CPUPhysMemoryClient) memory_client_list + = QLIST_HEAD_INITIALIZER(memory_client_list); + +static void cpu_notify_set_memory(target_phys_addr_t start_addr, + ram_addr_t size, + ram_addr_t phys_offset) +{ + CPUPhysMemoryClient *client; + QLIST_FOREACH(client, &memory_client_list, list) { + client->set_memory(client, start_addr, size, phys_offset); + } +} + +static int cpu_notify_sync_dirty_bitmap(target_phys_addr_t start, + target_phys_addr_t end) +{ + CPUPhysMemoryClient *client; + QLIST_FOREACH(client, &memory_client_list, list) { + int r = client->sync_dirty_bitmap(client, start, end); + if (r < 0) + return r; + } + return 0; +} + +static int cpu_notify_migration_log(int enable) +{ + CPUPhysMemoryClient *client; + QLIST_FOREACH(client, &memory_client_list, list) { + int r = client->migration_log(client, enable); + if (r < 0) + return r; + } + return 0; +} + +static void phys_page_for_each_1(CPUPhysMemoryClient *client, + int level, void **lp) +{ + int i; + + if (*lp == NULL) { + return; + } + if (level == 0) { + PhysPageDesc *pd = *lp; + for (i = 0; i < L2_SIZE; ++i) { + if (pd[i].phys_offset != IO_MEM_UNASSIGNED) { + client->set_memory(client, pd[i].region_offset, + TARGET_PAGE_SIZE, pd[i].phys_offset); + } + } + } else { + void **pp = *lp; + for (i = 0; i < L2_SIZE; ++i) { + phys_page_for_each_1(client, level - 1, pp + i); + } + } +} + +static void phys_page_for_each(CPUPhysMemoryClient *client) +{ + int i; + for (i = 0; i < P_L1_SIZE; ++i) { + phys_page_for_each_1(client, P_L1_SHIFT / L2_BITS - 1, + l1_phys_map + 1); + } +} + +void cpu_register_phys_memory_client(CPUPhysMemoryClient *client) +{ + QLIST_INSERT_HEAD(&memory_client_list, client, list); + phys_page_for_each(client); +} + +void cpu_unregister_phys_memory_client(CPUPhysMemoryClient *client) +{ + QLIST_REMOVE(client, list); +} +#endif + static int cmp1(const char *s1, int n, const char *s2) { if (strlen(s2) != n) @@ -1660,17 +1820,17 @@ p1 = strchr(p, ','); if (!p1) p1 = p + strlen(p); - if(cmp1(p,p1-p,"all")) { - for(item = cpu_log_items; item->mask != 0; item++) { - mask |= item->mask; - } - } else { - for(item = cpu_log_items; item->mask != 0; item++) { - if (cmp1(p, p1 - p, item->name)) - goto found; + if(cmp1(p,p1-p,"all")) { + for(item = cpu_log_items; item->mask != 0; item++) { + mask |= item->mask; + } + } else { + for(item = cpu_log_items; item->mask != 0; item++) { + if (cmp1(p, p1 - p, item->name)) + goto found; + } + return 0; } - return 0; - } found: mask |= item->mask; if (*p1 != ',') @@ -1709,6 +1869,14 @@ } va_end(ap2); va_end(ap); +#if defined(CONFIG_USER_ONLY) + { + struct sigaction act; + sigfillset(&act.sa_mask); + act.sa_handler = SIG_DFL; + sigaction(SIGABRT, &act, NULL); + } +#endif abort(); } @@ -1756,11 +1924,11 @@ overlap the flushed page. */ i = tb_jmp_cache_hash_page(addr - TARGET_PAGE_SIZE); memset (&env->tb_jmp_cache[i], 0, - TB_JMP_PAGE_SIZE * sizeof(TranslationBlock *)); + TB_JMP_PAGE_SIZE * sizeof(TranslationBlock *)); i = tb_jmp_cache_hash_page(addr); memset (&env->tb_jmp_cache[i], 0, - TB_JMP_PAGE_SIZE * sizeof(TranslationBlock *)); + TB_JMP_PAGE_SIZE * sizeof(TranslationBlock *)); } static CPUTLBEntry s_cputlb_empty_entry = { @@ -1792,6 +1960,8 @@ memset (env->tb_jmp_cache, 0, TB_JMP_CACHE_SIZE * sizeof (void *)); + env->tlb_flush_addr = -1; + env->tlb_flush_mask = 0; tlb_flush_count++; } @@ -1815,6 +1985,16 @@ #if defined(DEBUG_TLB) printf("tlb_flush_page: " TARGET_FMT_lx "\n", addr); #endif + /* Check if we need to flush due to large pages. */ + if ((addr & env->tlb_flush_mask) == env->tlb_flush_addr) { +#if defined(DEBUG_TLB) + printf("tlb_flush_page: forced full flush (" + TARGET_FMT_lx "/" TARGET_FMT_lx ")\n", + env->tlb_flush_addr, env->tlb_flush_mask); +#endif + tlb_flush(env, 1); + return; + } /* must reset current TB so that interrupts cannot modify the links while we are modifying them */ env->current_tb = NULL; @@ -1841,7 +2021,7 @@ static void tlb_unprotect_code_phys(CPUState *env, ram_addr_t ram_addr, target_ulong vaddr) { - phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS] |= CODE_DIRTY_FLAG; + cpu_physical_memory_set_dirty_flags(ram_addr, CODE_DIRTY_FLAG); } static inline void tlb_reset_dirty_range(CPUTLBEntry *tlb_entry, @@ -1862,8 +2042,7 @@ { CPUState *env; unsigned long length, start1; - int i, mask, len; - uint8_t *p; + int i; start &= TARGET_PAGE_MASK; end = TARGET_PAGE_ALIGN(end); @@ -1871,18 +2050,14 @@ length = end - start; if (length == 0) return; - len = length >> TARGET_PAGE_BITS; - mask = ~dirty_flags; - p = phys_ram_dirty + (start >> TARGET_PAGE_BITS); - for(i = 0; i < len; i++) - p[i] &= mask; + cpu_physical_memory_mask_dirty_range(start, length, dirty_flags); /* we modify the TLB cache so that the dirty bit will be set again when accessing the range */ - start1 = (unsigned long)qemu_get_ram_ptr(start); + start1 = (unsigned long)qemu_safe_ram_ptr(start); /* Chek that we don't span multiple blocks - this breaks the address comparisons below. */ - if ((unsigned long)qemu_get_ram_ptr(end - 1) - start1 + if ((unsigned long)qemu_safe_ram_ptr(end - 1) - start1 != (end - 1) - start) { abort(); } @@ -1899,10 +2074,10 @@ int cpu_physical_memory_set_dirty_tracking(int enable) { - if (kvm_enabled()) { - return kvm_set_migration_log(enable); - } - return 0; + int ret = 0; + in_migration = enable; + ret = cpu_notify_migration_log(!!enable); + return ret; } int cpu_physical_memory_get_dirty_tracking(void) @@ -1913,10 +2088,9 @@ int cpu_physical_sync_dirty_bitmap(target_phys_addr_t start_addr, target_phys_addr_t end_addr) { - int ret = 0; + int ret; - if (kvm_enabled()) - ret = kvm_physical_sync_dirty_bitmap(start_addr, end_addr); + ret = cpu_notify_sync_dirty_bitmap(start_addr, end_addr); return ret; } @@ -1928,7 +2102,7 @@ if ((tlb_entry->addr_write & ~TARGET_PAGE_MASK) == IO_MEM_RAM) { p = (void *)(unsigned long)((tlb_entry->addr_write & TARGET_PAGE_MASK) + tlb_entry->addend); - ram_addr = qemu_ram_addr_from_host(p); + ram_addr = qemu_ram_addr_from_host_nofail(p); if (!cpu_physical_memory_is_dirty(ram_addr)) { tlb_entry->addr_write |= TLB_NOTDIRTY; } @@ -1965,25 +2139,50 @@ tlb_set_dirty1(&env->tlb_table[mmu_idx][i], vaddr); } -/* add a new TLB entry. At most one entry for a given virtual address - is permitted. Return 0 if OK or 2 if the page could not be mapped - (can only happen in non SOFTMMU mode for I/O pages or pages - conflicting with the host address space). */ -int tlb_set_page_exec(CPUState *env, target_ulong vaddr, - target_phys_addr_t paddr, int prot, - int mmu_idx, int is_softmmu) +/* Our TLB does not support large pages, so remember the area covered by + large pages and trigger a full TLB flush if these are invalidated. */ +static void tlb_add_large_page(CPUState *env, target_ulong vaddr, + target_ulong size) +{ + target_ulong mask = ~(size - 1); + + if (env->tlb_flush_addr == (target_ulong)-1) { + env->tlb_flush_addr = vaddr & mask; + env->tlb_flush_mask = mask; + return; + } + /* Extend the existing region to include the new page. + This is a compromise between unnecessary flushes and the cost + of maintaining a full variable size TLB. */ + mask &= env->tlb_flush_mask; + while (((env->tlb_flush_addr ^ vaddr) & mask) != 0) { + mask <<= 1; + } + env->tlb_flush_addr &= mask; + env->tlb_flush_mask = mask; +} + +/* Add a new TLB entry. At most one entry for a given virtual address + is permitted. Only a single TARGET_PAGE_SIZE region is mapped, the + supplied size is only used by tlb_flush_page. */ +void tlb_set_page(CPUState *env, target_ulong vaddr, + target_phys_addr_t paddr, int prot, + int mmu_idx, target_ulong size) { PhysPageDesc *p; unsigned long pd; unsigned int index; target_ulong address; target_ulong code_address; - target_phys_addr_t addend; - int ret; + unsigned long addend; CPUTLBEntry *te; CPUWatchpoint *wp; target_phys_addr_t iotlb; + assert(size >= TARGET_PAGE_SIZE); + if (size != TARGET_PAGE_SIZE) { + tlb_add_large_page(env, vaddr, size); + } p = phys_page_find(paddr >> TARGET_PAGE_BITS); if (!p) { pd = IO_MEM_UNASSIGNED; @@ -1991,11 +2190,11 @@ pd = p->phys_offset; } #if defined(DEBUG_TLB) - printf("tlb_set_page: vaddr=" TARGET_FMT_lx " paddr=0x%08x prot=%x idx=%d smmu=%d pd=0x%08lx\n", - vaddr, (int)paddr, prot, mmu_idx, is_softmmu, pd); + printf("tlb_set_page: vaddr=" TARGET_FMT_lx " paddr=0x" TARGET_FMT_plx + " prot=%x idx=%d pd=0x%08lx\n", + vaddr, paddr, prot, mmu_idx, pd); #endif - ret = 0; address = vaddr; if ((pd & ~TARGET_PAGE_MASK) > IO_MEM_ROM && !(pd & IO_MEM_ROMD)) { /* IO memory case (romd handled later) */ @@ -2029,10 +2228,12 @@ watchpoint trap routines. */ QTAILQ_FOREACH(wp, &env->watchpoints, entry) { if (vaddr == (wp->vaddr & TARGET_PAGE_MASK)) { - iotlb = io_mem_watch + paddr; - /* TODO: The memory case can be optimized by not trapping - reads of pages with a write breakpoint. */ - address |= TLB_MMIO; + /* Avoid trapping reads of pages with a write breakpoint. */ + if ((prot & PAGE_WRITE) || (wp->flags & BP_MEM_READ)) { + iotlb = io_mem_watch + paddr; + address |= TLB_MMIO; + break; + } } } @@ -2065,7 +2266,6 @@ } else { te->addr_write = -1; } - return ret; } #else @@ -2078,73 +2278,111 @@ { } -int tlb_set_page_exec(CPUState *env, target_ulong vaddr, - target_phys_addr_t paddr, int prot, - int mmu_idx, int is_softmmu) -{ - return 0; -} - /* * Walks guest process memory "regions" one by one * and calls callback function 'fn' for each region. */ -int walk_memory_regions(void *priv, - int (*fn)(void *, unsigned long, unsigned long, unsigned long)) + +struct walk_memory_regions_data { - unsigned long start, end; - PageDesc *p = NULL; - int i, j, prot, prot1; - int rc = 0; - - start = end = -1; - prot = 0; - - for (i = 0; i <= L1_SIZE; i++) { - p = (i < L1_SIZE) ? l1_map[i] : NULL; - for (j = 0; j < L2_SIZE; j++) { - prot1 = (p == NULL) ? 0 : p[j].flags; - /* - * "region" is one continuous chunk of memory - * that has same protection flags set. - */ - if (prot1 != prot) { - end = (i << (32 - L1_BITS)) | (j << TARGET_PAGE_BITS); - if (start != -1) { - rc = (*fn)(priv, start, end, prot); - /* callback can stop iteration by returning != 0 */ - if (rc != 0) - return (rc); - } - if (prot1 != 0) - start = end; - else - start = -1; - prot = prot1; - } - if (p == NULL) - break; + walk_memory_regions_fn fn; + void *priv; + unsigned long start; + int prot; +}; + +static int walk_memory_regions_end(struct walk_memory_regions_data *data, + abi_ulong end, int new_prot) +{ + if (data->start != -1ul) { + int rc = data->fn(data->priv, data->start, end, data->prot); + if (rc != 0) { + return rc; } } - return (rc); + + data->start = (new_prot ? end : -1ul); + data->prot = new_prot; + + return 0; } -static int dump_region(void *priv, unsigned long start, - unsigned long end, unsigned long prot) +static int walk_memory_regions_1(struct walk_memory_regions_data *data, + abi_ulong base, int level, void **lp) { - FILE *f = (FILE *)priv; + abi_ulong pa; + int i, rc; - (void) fprintf(f, "%08lx-%08lx %08lx %c%c%c\n", - start, end, end - start, - ((prot & PAGE_READ) ? 'r' : '-'), - ((prot & PAGE_WRITE) ? 'w' : '-'), - ((prot & PAGE_EXEC) ? 'x' : '-')); + if (*lp == NULL) { + return walk_memory_regions_end(data, base, 0); + } - return (0); + if (level == 0) { + PageDesc *pd = *lp; + for (i = 0; i < L2_SIZE; ++i) { + int prot = pd[i].flags; + + pa = base | (i << TARGET_PAGE_BITS); + if (prot != data->prot) { + rc = walk_memory_regions_end(data, pa, prot); + if (rc != 0) { + return rc; + } + } + } + } else { + void **pp = *lp; + for (i = 0; i < L2_SIZE; ++i) { + pa = base | ((abi_ulong)i << + (TARGET_PAGE_BITS + L2_BITS * level)); + rc = walk_memory_regions_1(data, pa, level - 1, pp + i); + if (rc != 0) { + return rc; + } + } + } + + return 0; } -/* dump memory mappings */ -void page_dump(FILE *f) +int walk_memory_regions(void *priv, walk_memory_regions_fn fn) +{ + struct walk_memory_regions_data data; + unsigned long i; + + data.fn = fn; + data.priv = priv; + data.start = -1ul; + data.prot = 0; + + for (i = 0; i < V_L1_SIZE; i++) { + int rc = walk_memory_regions_1(&data, (abi_ulong)i << V_L1_SHIFT, + V_L1_SHIFT / L2_BITS - 1, l1_map + i); + if (rc != 0) { + return rc; + } + } + + return walk_memory_regions_end(&data, 0, 0); +} + +static int dump_region(void *priv, abi_ulong start, + abi_ulong end, unsigned long prot) +{ + FILE *f = (FILE *)priv; + + (void) fprintf(f, TARGET_ABI_FMT_lx"-"TARGET_ABI_FMT_lx + " "TARGET_ABI_FMT_lx" %c%c%c\n", + start, end, end - start, + ((prot & PAGE_READ) ? 'r' : '-'), + ((prot & PAGE_WRITE) ? 'w' : '-'), + ((prot & PAGE_EXEC) ? 'x' : '-')); + + return (0); +} + +/* dump memory mappings */ +void page_dump(FILE *f) { (void) fprintf(f, "%-8s %-8s %-8s %s\n", "start", "end", "size", "prot"); @@ -2161,27 +2399,35 @@ return p->flags; } -/* modify the flags of a page and invalidate the code if - necessary. The flag PAGE_WRITE_ORG is positioned automatically - depending on PAGE_WRITE */ +/* Modify the flags of a page and invalidate the code if necessary. + The flag PAGE_WRITE_ORG is positioned automatically depending + on PAGE_WRITE. The mmap_lock should already be held. */ void page_set_flags(target_ulong start, target_ulong end, int flags) { - PageDesc *p; - target_ulong addr; + target_ulong addr, len; + + /* This function should never be called with addresses outside the + guest address space. If this assert fires, it probably indicates + a missing call to h2g_valid. */ +#if TARGET_ABI_BITS > L1_MAP_ADDR_SPACE_BITS + assert(end < ((abi_ulong)1 << L1_MAP_ADDR_SPACE_BITS)); +#endif + assert(start < end); - /* mmap_lock should already be held. */ start = start & TARGET_PAGE_MASK; end = TARGET_PAGE_ALIGN(end); - if (flags & PAGE_WRITE) + + if (flags & PAGE_WRITE) { flags |= PAGE_WRITE_ORG; - for(addr = start; addr < end; addr += TARGET_PAGE_SIZE) { - p = page_find_alloc(addr >> TARGET_PAGE_BITS); - /* We may be called for host regions that are outside guest - address space. */ - if (!p) - return; - /* if the write protection is set, then we invalidate the code - inside */ + } + + for (addr = start, len = end - start; + len != 0; + len -= TARGET_PAGE_SIZE, addr += TARGET_PAGE_SIZE) { + PageDesc *p = page_find_alloc(addr >> TARGET_PAGE_BITS, 1); + + /* If the write protection bit is set, then we invalidate + the code inside. */ if (!(p->flags & PAGE_WRITE) && (flags & PAGE_WRITE) && p->first_tb) { @@ -2197,14 +2443,27 @@ target_ulong end; target_ulong addr; - if (start + len < start) - /* we've wrapped around */ + /* This function should never be called with addresses outside the + guest address space. If this assert fires, it probably indicates + a missing call to h2g_valid. */ +#if TARGET_ABI_BITS > L1_MAP_ADDR_SPACE_BITS + assert(start < ((abi_ulong)1 << L1_MAP_ADDR_SPACE_BITS)); +#endif + + if (len == 0) { + return 0; + } + if (start + len - 1 < start) { + /* We've wrapped around. */ return -1; + } end = TARGET_PAGE_ALIGN(start+len); /* must do before we loose bits in the next step */ start = start & TARGET_PAGE_MASK; - for(addr = start; addr < end; addr += TARGET_PAGE_SIZE) { + for (addr = start, len = end - start; + len != 0; + len -= TARGET_PAGE_SIZE, addr += TARGET_PAGE_SIZE) { p = page_find(addr >> TARGET_PAGE_BITS); if( !p ) return -1; @@ -2232,8 +2491,8 @@ page. Return TRUE if the fault was successfully handled. */ int page_unprotect(target_ulong address, unsigned long pc, void *puc) { - unsigned int page_index, prot, pindex; - PageDesc *p, *p1; + unsigned int prot; + PageDesc *p; target_ulong host_start, host_end, addr; /* Technically this isn't safe inside a signal handler. However we @@ -2241,37 +2500,36 @@ practice it seems to be ok. */ mmap_lock(); - host_start = address & qemu_host_page_mask; - page_index = host_start >> TARGET_PAGE_BITS; - p1 = page_find(page_index); - if (!p1) { + p = page_find(address >> TARGET_PAGE_BITS); + if (!p) { mmap_unlock(); return 0; } - host_end = host_start + qemu_host_page_size; - p = p1; - prot = 0; - for(addr = host_start;addr < host_end; addr += TARGET_PAGE_SIZE) { - prot |= p->flags; - p++; - } + /* if the page was really writable, then we change its protection back to writable */ - if (prot & PAGE_WRITE_ORG) { - pindex = (address - host_start) >> TARGET_PAGE_BITS; - if (!(p1[pindex].flags & PAGE_WRITE)) { - mprotect((void *)g2h(host_start), qemu_host_page_size, - (prot & PAGE_BITS) | PAGE_WRITE); - p1[pindex].flags |= PAGE_WRITE; + if ((p->flags & PAGE_WRITE_ORG) && !(p->flags & PAGE_WRITE)) { + host_start = address & qemu_host_page_mask; + host_end = host_start + qemu_host_page_size; + + prot = 0; + for (addr = host_start ; addr < host_end ; addr += TARGET_PAGE_SIZE) { + p = page_find(addr >> TARGET_PAGE_BITS); + p->flags |= PAGE_WRITE; + prot |= p->flags; + /* and since the content will be modified, we must invalidate the corresponding translated code. */ - tb_invalidate_phys_page(address, pc, puc); + tb_invalidate_phys_page(addr, pc, puc); #ifdef DEBUG_TB_CHECK - tb_invalidate_check(address); + tb_invalidate_check(addr); #endif - mmap_unlock(); - return 1; } + mprotect((void *)g2h(host_start), qemu_host_page_size, + prot & PAGE_BITS); + + mmap_unlock(); + return 1; } mmap_unlock(); return 0; @@ -2285,10 +2543,18 @@ #if !defined(CONFIG_USER_ONLY) +#define SUBPAGE_IDX(addr) ((addr) & ~TARGET_PAGE_MASK) +typedef struct subpage_t { + target_phys_addr_t base; + ram_addr_t sub_io_index[TARGET_PAGE_SIZE]; + ram_addr_t region_offset[TARGET_PAGE_SIZE]; +} subpage_t; + static int subpage_register (subpage_t *mmio, uint32_t start, uint32_t end, ram_addr_t memory, ram_addr_t region_offset); -static void *subpage_init (target_phys_addr_t base, ram_addr_t *phys, - ram_addr_t orig_memory, ram_addr_t region_offset); +static subpage_t *subpage_init (target_phys_addr_t base, ram_addr_t *phys, + ram_addr_t orig_memory, + ram_addr_t region_offset); #define CHECK_SUBPAGE(addr, start_addr, start_addr2, end_addr, end_addr2, \ need_subpage) \ do { \ @@ -2326,10 +2592,9 @@ PhysPageDesc *p; CPUState *env; ram_addr_t orig_size = size; - void *subpage; + subpage_t *subpage; - if (kvm_enabled()) - kvm_set_phys_mem(start_addr, size, phys_offset); + cpu_notify_set_memory(start_addr, size, phys_offset); if (phys_offset == IO_MEM_UNASSIGNED) { region_offset = start_addr; @@ -2346,7 +2611,7 @@ CHECK_SUBPAGE(addr, start_addr, start_addr2, end_addr, end_addr2, need_subpage); - if (need_subpage || phys_offset & IO_MEM_SUBWIDTH) { + if (need_subpage) { if (!(orig_memory & IO_MEM_SUBPAGE)) { subpage = subpage_init((addr & TARGET_PAGE_MASK), &p->phys_offset, orig_memory, @@ -2378,7 +2643,7 @@ CHECK_SUBPAGE(addr, start_addr, start_addr2, end_addr, end_addr2, need_subpage); - if (need_subpage || phys_offset & IO_MEM_SUBWIDTH) { + if (need_subpage) { subpage = subpage_init((addr & TARGET_PAGE_MASK), &p->phys_offset, IO_MEM_UNASSIGNED, addr & TARGET_PAGE_MASK); @@ -2422,7 +2687,13 @@ kvm_uncoalesce_mmio_region(addr, size); } -#ifdef __linux__ +void qemu_flush_coalesced_mmio_buffer(void) +{ + if (kvm_enabled()) + kvm_flush_coalesced_mmio_buffer(); +} + +#if defined(__linux__) && !defined(TARGET_S390X) #include @@ -2434,21 +2705,23 @@ int ret; do { - ret = statfs(path, &fs); + ret = statfs(path, &fs); } while (ret != 0 && errno == EINTR); if (ret != 0) { - perror("statfs"); - return 0; + perror(path); + return 0; } if (fs.f_type != HUGETLBFS_MAGIC) - fprintf(stderr, "Warning: path not on HugeTLBFS: %s\n", path); + fprintf(stderr, "Warning: path not on HugeTLBFS: %s\n", path); return fs.f_bsize; } -static void *file_ram_alloc(ram_addr_t memory, const char *path) +static void *file_ram_alloc(RAMBlock *block, + ram_addr_t memory, + const char *path) { char *filename; void *area; @@ -2457,15 +2730,10 @@ int flags; #endif unsigned long hpagesize; - extern int mem_prealloc; - - if (!path) { - return NULL; - } hpagesize = gethugepagesize(path); if (!hpagesize) { - return NULL; + return NULL; } if (memory < hpagesize) { @@ -2473,19 +2741,19 @@ } if (kvm_enabled() && !kvm_has_sync_mmu()) { - fprintf(stderr, "host lacks mmu notifiers, disabling --mem-path\n"); + fprintf(stderr, "host lacks kvm mmu notifiers, -mem-path unsupported\n"); return NULL; } - if (asprintf(&filename, "%s/kvm.XXXXXX", path) == -1) { - return NULL; + if (asprintf(&filename, "%s/qemu_back_mem.XXXXXX", path) == -1) { + return NULL; } fd = mkstemp(filename); if (fd < 0) { - perror("mkstemp"); - free(filename); - return NULL; + perror("unable to create backing store for hugepages"); + free(filename); + return NULL; } unlink(filename); free(filename); @@ -2494,84 +2762,184 @@ /* * ftruncate is not supported by hugetlbfs in older - * hosts, so don't bother checking for errors. + * hosts, so don't bother bailing out on errors. * If anything goes wrong with it under other filesystems, * mmap will fail. */ - ftruncate(fd, memory); + if (ftruncate(fd, memory)) + perror("ftruncate"); #ifdef MAP_POPULATE /* NB: MAP_POPULATE won't exhaustively alloc all phys pages in the case * MAP_PRIVATE is requested. For mem_prealloc we mmap as MAP_SHARED * to sidestep this quirk. */ - flags = mem_prealloc ? MAP_POPULATE|MAP_SHARED : MAP_PRIVATE; - area = mmap(0, memory, PROT_READ|PROT_WRITE, flags, fd, 0); + flags = mem_prealloc ? MAP_POPULATE | MAP_SHARED : MAP_PRIVATE; + area = mmap(0, memory, PROT_READ | PROT_WRITE, flags, fd, 0); #else - area = mmap(0, memory, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0); + area = mmap(0, memory, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); #endif if (area == MAP_FAILED) { - perror("alloc_mem_area: can't mmap hugetlbfs pages"); - close(fd); - return (NULL); + perror("file_ram_alloc: can't mmap RAM pages"); + close(fd); + return (NULL); } + block->fd = fd; return area; } +#endif -#else - -static void *file_ram_alloc(ram_addr_t memory, const char *path) +static ram_addr_t find_ram_offset(ram_addr_t size) { - return NULL; + RAMBlock *block, *next_block; + ram_addr_t offset = 0, mingap = ULONG_MAX; + + if (QLIST_EMPTY(&ram_list.blocks)) + return 0; + + QLIST_FOREACH(block, &ram_list.blocks, next) { + ram_addr_t end, next = ULONG_MAX; + + end = block->offset + block->length; + + QLIST_FOREACH(next_block, &ram_list.blocks, next) { + if (next_block->offset >= end) { + next = MIN(next, next_block->offset); + } + } + if (next - end >= size && next - end < mingap) { + offset = end; + mingap = next - end; + } + } + return offset; } -#endif +static ram_addr_t last_ram_offset(void) +{ + RAMBlock *block; + ram_addr_t last = 0; + + QLIST_FOREACH(block, &ram_list.blocks, next) + last = MAX(last, block->offset + block->length); -extern const char *mem_path; + return last; +} -ram_addr_t qemu_ram_alloc(ram_addr_t size) +ram_addr_t qemu_ram_alloc_from_ptr(DeviceState *dev, const char *name, + ram_addr_t size, void *host) { - RAMBlock *new_block; + RAMBlock *new_block, *block; size = TARGET_PAGE_ALIGN(size); - new_block = qemu_malloc(sizeof(*new_block)); + new_block = qemu_mallocz(sizeof(*new_block)); - new_block->host = file_ram_alloc(size, mem_path); - if (!new_block->host) { -#if defined(TARGET_S390X) && defined(CONFIG_KVM) - /* XXX S390 KVM requires the topmost vma of the RAM to be < 256GB */ - new_block->host = mmap((void*)0x1000000, size, - PROT_EXEC|PROT_READ|PROT_WRITE, - MAP_SHARED | MAP_ANONYMOUS, -1, 0); + if (dev && dev->parent_bus && dev->parent_bus->info->get_dev_path) { + char *id = dev->parent_bus->info->get_dev_path(dev); + if (id) { + snprintf(new_block->idstr, sizeof(new_block->idstr), "%s/", id); + qemu_free(id); + } + } + pstrcat(new_block->idstr, sizeof(new_block->idstr), name); + + QLIST_FOREACH(block, &ram_list.blocks, next) { + if (!strcmp(block->idstr, new_block->idstr)) { + fprintf(stderr, "RAMBlock \"%s\" already registered, abort!\n", + new_block->idstr); + abort(); + } + } + + if (host) { + new_block->host = host; + } else { + if (mem_path) { +#if defined (__linux__) && !defined(TARGET_S390X) + new_block->host = file_ram_alloc(new_block, size, mem_path); + if (!new_block->host) { + new_block->host = qemu_vmalloc(size); + qemu_madvise(new_block->host, size, QEMU_MADV_MERGEABLE); + } #else - new_block->host = qemu_vmalloc(size); + fprintf(stderr, "-mem-path option unsupported\n"); + exit(1); #endif -#ifdef MADV_MERGEABLE - madvise(new_block->host, size, MADV_MERGEABLE); + } else { +#if defined(TARGET_S390X) && defined(CONFIG_KVM) + /* XXX S390 KVM requires the topmost vma of the RAM to be < 256GB */ + new_block->host = mmap((void*)0x1000000, size, + PROT_EXEC|PROT_READ|PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, -1, 0); +#else + new_block->host = qemu_vmalloc(size); #endif + qemu_madvise(new_block->host, size, QEMU_MADV_MERGEABLE); + } } - new_block->offset = last_ram_offset; + + new_block->offset = find_ram_offset(size); new_block->length = size; - new_block->next = ram_blocks; - ram_blocks = new_block; + QLIST_INSERT_HEAD(&ram_list.blocks, new_block, next); - phys_ram_dirty = qemu_realloc(phys_ram_dirty, - (last_ram_offset + size) >> TARGET_PAGE_BITS); - memset(phys_ram_dirty + (last_ram_offset >> TARGET_PAGE_BITS), + ram_list.phys_dirty = qemu_realloc(ram_list.phys_dirty, + last_ram_offset() >> TARGET_PAGE_BITS); + memset(ram_list.phys_dirty + (new_block->offset >> TARGET_PAGE_BITS), 0xff, size >> TARGET_PAGE_BITS); - last_ram_offset += size; - if (kvm_enabled()) kvm_setup_guest_memory(new_block->host, size); return new_block->offset; } +void qemu_ram_unmap(ram_addr_t addr) +{ + RAMBlock *block; + + QLIST_FOREACH(block, &ram_list.blocks, next) { + if (addr == block->offset) { + QLIST_REMOVE(block, next); + qemu_free(block); + return; + } + } +} + +ram_addr_t qemu_ram_alloc(DeviceState *dev, const char *name, ram_addr_t size) +{ + return qemu_ram_alloc_from_ptr(dev, name, size, NULL); +} + void qemu_ram_free(ram_addr_t addr) { - /* TODO: implement this. */ + RAMBlock *block; + + QLIST_FOREACH(block, &ram_list.blocks, next) { + if (addr == block->offset) { + QLIST_REMOVE(block, next); + if (mem_path) { +#if defined (__linux__) && !defined(TARGET_S390X) + if (block->fd) { + munmap(block->host, block->length); + close(block->fd); + } else { + qemu_vfree(block->host); + } +#endif + } else { +#if defined(TARGET_S390X) && defined(CONFIG_KVM) + munmap(block->host, block->length); +#else + qemu_vfree(block->host); +#endif + } + qemu_free(block); + return; + } + } + } /* Return a host pointer to ram allocated with qemu_ram_alloc. @@ -2584,63 +2952,62 @@ */ void *qemu_get_ram_ptr(ram_addr_t addr) { - RAMBlock *prev; - RAMBlock **prevp; RAMBlock *block; - prev = NULL; - prevp = &ram_blocks; - block = ram_blocks; - while (block && (block->offset > addr - || block->offset + block->length <= addr)) { - if (prev) - prevp = &prev->next; - prev = block; - block = block->next; - } - if (!block) { - fprintf(stderr, "Bad ram offset %" PRIx64 "\n", (uint64_t)addr); - abort(); + QLIST_FOREACH(block, &ram_list.blocks, next) { + if (addr - block->offset < block->length) { + QLIST_REMOVE(block, next); + QLIST_INSERT_HEAD(&ram_list.blocks, block, next); + return block->host + (addr - block->offset); + } } - /* Move this entry to to start of the list. */ - if (prev) { - prev->next = block->next; - block->next = *prevp; - *prevp = block; + + fprintf(stderr, "Bad ram offset %" PRIx64 "\n", (uint64_t)addr); + abort(); + + return NULL; +} + +/* Return a host pointer to ram allocated with qemu_ram_alloc. + * Same as qemu_get_ram_ptr but avoid reordering ramblocks. + */ +void *qemu_safe_ram_ptr(ram_addr_t addr) +{ + RAMBlock *block; + + QLIST_FOREACH(block, &ram_list.blocks, next) { + if (addr - block->offset < block->length) { + return block->host + (addr - block->offset); + } } - return block->host + (addr - block->offset); + + fprintf(stderr, "Bad ram offset %" PRIx64 "\n", (uint64_t)addr); + abort(); + + return NULL; } -int do_qemu_ram_addr_from_host(void *ptr, ram_addr_t *ram_addr) +int qemu_ram_addr_from_host(void *ptr, ram_addr_t *ram_addr) { - RAMBlock *prev; - RAMBlock **prevp; RAMBlock *block; uint8_t *host = ptr; - prev = NULL; - prevp = &ram_blocks; - block = ram_blocks; - while (block && (block->host > host - || block->host + block->length <= host)) { - if (prev) - prevp = &prev->next; - prev = block; - block = block->next; + QLIST_FOREACH(block, &ram_list.blocks, next) { + if (host - block->host < block->length) { + *ram_addr = block->offset + (host - block->host); + return 0; + } } - if (!block) - return -1; - *ram_addr = block->offset + (host - block->host); - return 0; + return -1; } /* Some of the softmmu routines need to translate from a host pointer (typically a TLB entry) back to a ram offset. */ -ram_addr_t qemu_ram_addr_from_host(void *ptr) +ram_addr_t qemu_ram_addr_from_host_nofail(void *ptr) { ram_addr_t ram_addr; - if (do_qemu_ram_addr_from_host(ptr, &ram_addr)) { + if (qemu_ram_addr_from_host(ptr, &ram_addr)) { fprintf(stderr, "Bad ram pointer %p\n", ptr); abort(); } @@ -2726,16 +3093,16 @@ uint32_t val) { int dirty_flags; - dirty_flags = phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS]; + dirty_flags = cpu_physical_memory_get_dirty_flags(ram_addr); if (!(dirty_flags & CODE_DIRTY_FLAG)) { #if !defined(CONFIG_USER_ONLY) tb_invalidate_phys_page_fast(ram_addr, 1); - dirty_flags = phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS]; + dirty_flags = cpu_physical_memory_get_dirty_flags(ram_addr); #endif } stb_p(qemu_get_ram_ptr(ram_addr), val); dirty_flags |= (0xff & ~CODE_DIRTY_FLAG); - phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS] = dirty_flags; + cpu_physical_memory_set_dirty_flags(ram_addr, dirty_flags); /* we remove the notdirty callback only if the code has been flushed */ if (dirty_flags == 0xff) @@ -2746,16 +3113,16 @@ uint32_t val) { int dirty_flags; - dirty_flags = phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS]; + dirty_flags = cpu_physical_memory_get_dirty_flags(ram_addr); if (!(dirty_flags & CODE_DIRTY_FLAG)) { #if !defined(CONFIG_USER_ONLY) tb_invalidate_phys_page_fast(ram_addr, 2); - dirty_flags = phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS]; + dirty_flags = cpu_physical_memory_get_dirty_flags(ram_addr); #endif } stw_p(qemu_get_ram_ptr(ram_addr), val); dirty_flags |= (0xff & ~CODE_DIRTY_FLAG); - phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS] = dirty_flags; + cpu_physical_memory_set_dirty_flags(ram_addr, dirty_flags); /* we remove the notdirty callback only if the code has been flushed */ if (dirty_flags == 0xff) @@ -2766,16 +3133,16 @@ uint32_t val) { int dirty_flags; - dirty_flags = phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS]; + dirty_flags = cpu_physical_memory_get_dirty_flags(ram_addr); if (!(dirty_flags & CODE_DIRTY_FLAG)) { #if !defined(CONFIG_USER_ONLY) tb_invalidate_phys_page_fast(ram_addr, 4); - dirty_flags = phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS]; + dirty_flags = cpu_physical_memory_get_dirty_flags(ram_addr); #endif } stl_p(qemu_get_ram_ptr(ram_addr), val); dirty_flags |= (0xff & ~CODE_DIRTY_FLAG); - phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS] = dirty_flags; + cpu_physical_memory_set_dirty_flags(ram_addr, dirty_flags); /* we remove the notdirty callback only if the code has been flushed */ if (dirty_flags == 0xff) @@ -2893,89 +3260,65 @@ watch_mem_writel, }; -static inline uint32_t subpage_readlen (subpage_t *mmio, target_phys_addr_t addr, - unsigned int len) +static inline uint32_t subpage_readlen (subpage_t *mmio, + target_phys_addr_t addr, + unsigned int len) { - uint32_t ret; - unsigned int idx; - - idx = SUBPAGE_IDX(addr); + unsigned int idx = SUBPAGE_IDX(addr); #if defined(DEBUG_SUBPAGE) printf("%s: subpage %p len %d addr " TARGET_FMT_plx " idx %d\n", __func__, mmio, len, addr, idx); #endif - ret = (**mmio->mem_read[idx][len])(mmio->opaque[idx][0][len], - addr + mmio->region_offset[idx][0][len]); - return ret; + addr += mmio->region_offset[idx]; + idx = mmio->sub_io_index[idx]; + return io_mem_read[idx][len](io_mem_opaque[idx], addr); } static inline void subpage_writelen (subpage_t *mmio, target_phys_addr_t addr, - uint32_t value, unsigned int len) + uint32_t value, unsigned int len) { - unsigned int idx; - - idx = SUBPAGE_IDX(addr); + unsigned int idx = SUBPAGE_IDX(addr); #if defined(DEBUG_SUBPAGE) - printf("%s: subpage %p len %d addr " TARGET_FMT_plx " idx %d value %08x\n", __func__, - mmio, len, addr, idx, value); + printf("%s: subpage %p len %d addr " TARGET_FMT_plx " idx %d value %08x\n", + __func__, mmio, len, addr, idx, value); #endif - (**mmio->mem_write[idx][len])(mmio->opaque[idx][1][len], - addr + mmio->region_offset[idx][1][len], - value); + + addr += mmio->region_offset[idx]; + idx = mmio->sub_io_index[idx]; + io_mem_write[idx][len](io_mem_opaque[idx], addr, value); } static uint32_t subpage_readb (void *opaque, target_phys_addr_t addr) { -#if defined(DEBUG_SUBPAGE) - printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr); -#endif - return subpage_readlen(opaque, addr, 0); } static void subpage_writeb (void *opaque, target_phys_addr_t addr, uint32_t value) { -#if defined(DEBUG_SUBPAGE) - printf("%s: addr " TARGET_FMT_plx " val %08x\n", __func__, addr, value); -#endif subpage_writelen(opaque, addr, value, 0); } static uint32_t subpage_readw (void *opaque, target_phys_addr_t addr) { -#if defined(DEBUG_SUBPAGE) - printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr); -#endif - return subpage_readlen(opaque, addr, 1); } static void subpage_writew (void *opaque, target_phys_addr_t addr, uint32_t value) { -#if defined(DEBUG_SUBPAGE) - printf("%s: addr " TARGET_FMT_plx " val %08x\n", __func__, addr, value); -#endif subpage_writelen(opaque, addr, value, 1); } static uint32_t subpage_readl (void *opaque, target_phys_addr_t addr) { -#if defined(DEBUG_SUBPAGE) - printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr); -#endif - return subpage_readlen(opaque, addr, 2); } -static void subpage_writel (void *opaque, - target_phys_addr_t addr, uint32_t value) +static void subpage_writel (void *opaque, target_phys_addr_t addr, + uint32_t value) { -#if defined(DEBUG_SUBPAGE) - printf("%s: addr " TARGET_FMT_plx " val %08x\n", __func__, addr, value); -#endif subpage_writelen(opaque, addr, value, 2); } @@ -2995,7 +3338,6 @@ ram_addr_t memory, ram_addr_t region_offset) { int idx, eidx; - unsigned int i; if (start >= TARGET_PAGE_SIZE || end >= TARGET_PAGE_SIZE) return -1; @@ -3005,27 +3347,20 @@ printf("%s: %p start %08x end %08x idx %08x eidx %08x mem %ld\n", __func__, mmio, start, end, idx, eidx, memory); #endif - memory >>= IO_MEM_SHIFT; + if ((memory & ~TARGET_PAGE_MASK) == IO_MEM_RAM) + memory = IO_MEM_UNASSIGNED; + memory = (memory >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1); for (; idx <= eidx; idx++) { - for (i = 0; i < 4; i++) { - if (io_mem_read[memory][i]) { - mmio->mem_read[idx][i] = &io_mem_read[memory][i]; - mmio->opaque[idx][0][i] = io_mem_opaque[memory]; - mmio->region_offset[idx][0][i] = region_offset; - } - if (io_mem_write[memory][i]) { - mmio->mem_write[idx][i] = &io_mem_write[memory][i]; - mmio->opaque[idx][1][i] = io_mem_opaque[memory]; - mmio->region_offset[idx][1][i] = region_offset; - } - } + mmio->sub_io_index[idx] = memory; + mmio->region_offset[idx] = region_offset; } return 0; } -static void *subpage_init (target_phys_addr_t base, ram_addr_t *phys, - ram_addr_t orig_memory, ram_addr_t region_offset) +static subpage_t *subpage_init (target_phys_addr_t base, ram_addr_t *phys, + ram_addr_t orig_memory, + ram_addr_t region_offset) { subpage_t *mmio; int subpage_memory; @@ -3033,14 +3368,14 @@ mmio = qemu_mallocz(sizeof(subpage_t)); mmio->base = base; - subpage_memory = cpu_register_io_memory(subpage_read, subpage_write, mmio); + subpage_memory = cpu_register_io_memory(subpage_read, subpage_write, mmio, + DEVICE_NATIVE_ENDIAN); #if defined(DEBUG_SUBPAGE) printf("%s: %p base " TARGET_FMT_plx " len %08x %d\n", __func__, mmio, base, TARGET_PAGE_SIZE, subpage_memory); #endif *phys = subpage_memory | IO_MEM_SUBPAGE; - subpage_register(mmio, 0, TARGET_PAGE_SIZE - 1, orig_memory, - region_offset); + subpage_register(mmio, 0, TARGET_PAGE_SIZE-1, orig_memory, region_offset); return mmio; } @@ -3054,10 +3389,110 @@ io_mem_used[i] = 1; return i; } - + fprintf(stderr, "RAN out out io_mem_idx, max %d !\n", IO_MEM_NB_ENTRIES); return -1; } +/* + * Usually, devices operate in little endian mode. There are devices out + * there that operate in big endian too. Each device gets byte swapped + * mmio if plugged onto a CPU that does the other endianness. + * + * CPU Device swap? + * + * little little no + * little big yes + * big little yes + * big big no + */ + +typedef struct SwapEndianContainer { + CPUReadMemoryFunc *read[3]; + CPUWriteMemoryFunc *write[3]; + void *opaque; +} SwapEndianContainer; + +static uint32_t swapendian_mem_readb (void *opaque, target_phys_addr_t addr) +{ + uint32_t val; + SwapEndianContainer *c = opaque; + val = c->read[0](c->opaque, addr); + return val; +} + +static uint32_t swapendian_mem_readw(void *opaque, target_phys_addr_t addr) +{ + uint32_t val; + SwapEndianContainer *c = opaque; + val = bswap16(c->read[1](c->opaque, addr)); + return val; +} + +static uint32_t swapendian_mem_readl(void *opaque, target_phys_addr_t addr) +{ + uint32_t val; + SwapEndianContainer *c = opaque; + val = bswap32(c->read[2](c->opaque, addr)); + return val; +} + +static CPUReadMemoryFunc * const swapendian_readfn[3]={ + swapendian_mem_readb, + swapendian_mem_readw, + swapendian_mem_readl +}; + +static void swapendian_mem_writeb(void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + SwapEndianContainer *c = opaque; + c->write[0](c->opaque, addr, val); +} + +static void swapendian_mem_writew(void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + SwapEndianContainer *c = opaque; + c->write[1](c->opaque, addr, bswap16(val)); +} + +static void swapendian_mem_writel(void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + SwapEndianContainer *c = opaque; + c->write[2](c->opaque, addr, bswap32(val)); +} + +static CPUWriteMemoryFunc * const swapendian_writefn[3]={ + swapendian_mem_writeb, + swapendian_mem_writew, + swapendian_mem_writel +}; + +static void swapendian_init(int io_index) +{ + SwapEndianContainer *c = qemu_malloc(sizeof(SwapEndianContainer)); + int i; + + /* Swap mmio for big endian targets */ + c->opaque = io_mem_opaque[io_index]; + for (i = 0; i < 3; i++) { + c->read[i] = io_mem_read[io_index][i]; + c->write[i] = io_mem_write[io_index][i]; + + io_mem_read[io_index][i] = swapendian_readfn[i]; + io_mem_write[io_index][i] = swapendian_writefn[i]; + } + io_mem_opaque[io_index] = c; +} + +static void swapendian_del(int io_index) +{ + if (io_mem_read[io_index][0] == swapendian_readfn[0]) { + qemu_free(io_mem_opaque[io_index]); + } +} + /* mem_read and mem_write are arrays of functions containing the function to access byte (index 0), word (index 1) and dword (index 2). Functions can be omitted with a NULL function pointer. @@ -3068,9 +3503,9 @@ static int cpu_register_io_memory_fixed(int io_index, CPUReadMemoryFunc * const *mem_read, CPUWriteMemoryFunc * const *mem_write, - void *opaque) + void *opaque, enum device_endian endian) { - int i, subwidth = 0; + int i; if (io_index <= 0) { io_index = get_free_io_mem_idx(); @@ -3082,21 +3517,40 @@ return -1; } - for(i = 0;i < 3; i++) { - if (!mem_read[i] || !mem_write[i]) - subwidth = IO_MEM_SUBWIDTH; - io_mem_read[io_index][i] = mem_read[i]; - io_mem_write[io_index][i] = mem_write[i]; + for (i = 0; i < 3; ++i) { + io_mem_read[io_index][i] + = (mem_read[i] ? mem_read[i] : unassigned_mem_read[i]); + } + for (i = 0; i < 3; ++i) { + io_mem_write[io_index][i] + = (mem_write[i] ? mem_write[i] : unassigned_mem_write[i]); } io_mem_opaque[io_index] = opaque; - return (io_index << IO_MEM_SHIFT) | subwidth; + + switch (endian) { + case DEVICE_BIG_ENDIAN: +#ifndef TARGET_WORDS_BIGENDIAN + swapendian_init(io_index); +#endif + break; + case DEVICE_LITTLE_ENDIAN: +#ifdef TARGET_WORDS_BIGENDIAN + swapendian_init(io_index); +#endif + break; + case DEVICE_NATIVE_ENDIAN: + default: + break; + } + + return (io_index << IO_MEM_SHIFT); } int cpu_register_io_memory(CPUReadMemoryFunc * const *mem_read, CPUWriteMemoryFunc * const *mem_write, - void *opaque) + void *opaque, enum device_endian endian) { - return cpu_register_io_memory_fixed(0, mem_read, mem_write, opaque); + return cpu_register_io_memory_fixed(0, mem_read, mem_write, opaque, endian); } void cpu_unregister_io_memory(int io_table_address) @@ -3104,6 +3558,8 @@ int i; int io_index = io_table_address >> IO_MEM_SHIFT; + swapendian_del(io_index); + for (i=0;i < 3; i++) { io_mem_read[io_index][i] = unassigned_mem_read[i]; io_mem_write[io_index][i] = unassigned_mem_write[i]; @@ -3116,22 +3572,29 @@ { int i; - cpu_register_io_memory_fixed(IO_MEM_ROM, error_mem_read, unassigned_mem_write, NULL); - cpu_register_io_memory_fixed(IO_MEM_UNASSIGNED, unassigned_mem_read, unassigned_mem_write, NULL); - cpu_register_io_memory_fixed(IO_MEM_NOTDIRTY, error_mem_read, notdirty_mem_write, NULL); + cpu_register_io_memory_fixed(IO_MEM_ROM, error_mem_read, + unassigned_mem_write, NULL, + DEVICE_NATIVE_ENDIAN); + cpu_register_io_memory_fixed(IO_MEM_UNASSIGNED, unassigned_mem_read, + unassigned_mem_write, NULL, + DEVICE_NATIVE_ENDIAN); + cpu_register_io_memory_fixed(IO_MEM_NOTDIRTY, error_mem_read, + notdirty_mem_write, NULL, + DEVICE_NATIVE_ENDIAN); for (i=0; i<5; i++) io_mem_used[i] = 1; io_mem_watch = cpu_register_io_memory(watch_mem_read, - watch_mem_write, NULL); + watch_mem_write, NULL, + DEVICE_NATIVE_ENDIAN); } #endif /* !defined(CONFIG_USER_ONLY) */ /* physical memory access (slow version, mainly for debug) */ #if defined(CONFIG_USER_ONLY) -void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf, - int len, int is_write) +int cpu_memory_rw_debug(CPUState *env, target_ulong addr, + uint8_t *buf, int len, int is_write) { int l, flags; target_ulong page; @@ -3144,23 +3607,21 @@ l = len; flags = page_get_flags(page); if (!(flags & PAGE_VALID)) - return; + return -1; if (is_write) { if (!(flags & PAGE_WRITE)) - return; + return -1; /* XXX: this code should not depend on lock_user */ if (!(p = lock_user(VERIFY_WRITE, addr, l, 0))) - /* FIXME - should this return an error rather than just fail? */ - return; + return -1; memcpy(p, buf, l); unlock_user(p, addr, l); } else { if (!(flags & PAGE_READ)) - return; + return -1; /* XXX: this code should not depend on lock_user */ if (!(p = lock_user(VERIFY_READ, addr, l, 1))) - /* FIXME - should this return an error rather than just fail? */ - return; + return -1; memcpy(buf, p, l); unlock_user(p, addr, 0); } @@ -3168,6 +3629,7 @@ buf += l; addr += l; } + return 0; } #else @@ -3227,8 +3689,8 @@ /* invalidate code */ tb_invalidate_phys_page_range(addr1, addr1 + l, 0); /* set dirty bit */ - phys_ram_dirty[addr1 >> TARGET_PAGE_BITS] |= - (0xff & ~CODE_DIRTY_FLAG); + cpu_physical_memory_set_dirty_flags( + addr1, (0xff & ~CODE_DIRTY_FLAG)); } /* qemu doesn't execute guest code directly, but kvm does therefore flush instruction caches */ @@ -3431,7 +3893,7 @@ if (buffer != bounce.buffer) { if (is_write) { - ram_addr_t addr1 = qemu_ram_addr_from_host(buffer); + ram_addr_t addr1 = qemu_ram_addr_from_host_nofail(buffer); while (access_len) { unsigned l; l = TARGET_PAGE_SIZE; @@ -3441,8 +3903,8 @@ /* invalidate code */ tb_invalidate_phys_page_range(addr1, addr1 + l, 0); /* set dirty bit */ - phys_ram_dirty[addr1 >> TARGET_PAGE_BITS] |= - (0xff & ~CODE_DIRTY_FLAG); + cpu_physical_memory_set_dirty_flags( + addr1, (0xff & ~CODE_DIRTY_FLAG)); } addr1 += l; access_len -= l; @@ -3538,12 +4000,36 @@ return val; } -/* XXX: optimize */ +/* warning: addr must be aligned */ uint32_t lduw_phys(target_phys_addr_t addr) { - uint16_t val; - cpu_physical_memory_read(addr, (uint8_t *)&val, 2); - return tswap16(val); + int io_index; + uint8_t *ptr; + uint64_t val; + unsigned long pd; + PhysPageDesc *p; + + p = phys_page_find(addr >> TARGET_PAGE_BITS); + if (!p) { + pd = IO_MEM_UNASSIGNED; + } else { + pd = p->phys_offset; + } + + if ((pd & ~TARGET_PAGE_MASK) > IO_MEM_ROM && + !(pd & IO_MEM_ROMD)) { + /* I/O case */ + io_index = (pd >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1); + if (p) + addr = (addr & ~TARGET_PAGE_MASK) + p->region_offset; + val = io_mem_read[io_index][1](io_mem_opaque[io_index], addr); + } else { + /* RAM case */ + ptr = qemu_get_ram_ptr(pd & TARGET_PAGE_MASK) + + (addr & ~TARGET_PAGE_MASK); + val = lduw_p(ptr); + } + return val; } /* warning: addr must be aligned. The ram page is not masked as dirty @@ -3578,8 +4064,8 @@ /* invalidate code */ tb_invalidate_phys_page_range(addr1, addr1 + 4, 0); /* set dirty bit */ - phys_ram_dirty[addr1 >> TARGET_PAGE_BITS] |= - (0xff & ~CODE_DIRTY_FLAG); + cpu_physical_memory_set_dirty_flags( + addr1, (0xff & ~CODE_DIRTY_FLAG)); } } } @@ -3647,8 +4133,8 @@ /* invalidate code */ tb_invalidate_phys_page_range(addr1, addr1 + 4, 0); /* set dirty bit */ - phys_ram_dirty[addr1 >> TARGET_PAGE_BITS] |= - (0xff & ~CODE_DIRTY_FLAG); + cpu_physical_memory_set_dirty_flags(addr1, + (0xff & ~CODE_DIRTY_FLAG)); } } } @@ -3660,11 +4146,40 @@ cpu_physical_memory_write(addr, &v, 1); } -/* XXX: optimize */ +/* warning: addr must be aligned */ void stw_phys(target_phys_addr_t addr, uint32_t val) { - uint16_t v = tswap16(val); - cpu_physical_memory_write(addr, (const uint8_t *)&v, 2); + int io_index; + uint8_t *ptr; + unsigned long pd; + PhysPageDesc *p; + + p = phys_page_find(addr >> TARGET_PAGE_BITS); + if (!p) { + pd = IO_MEM_UNASSIGNED; + } else { + pd = p->phys_offset; + } + + if ((pd & ~TARGET_PAGE_MASK) != IO_MEM_RAM) { + io_index = (pd >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1); + if (p) + addr = (addr & ~TARGET_PAGE_MASK) + p->region_offset; + io_mem_write[io_index][1](io_mem_opaque[io_index], addr, val); + } else { + unsigned long addr1; + addr1 = (pd & TARGET_PAGE_MASK) + (addr & ~TARGET_PAGE_MASK); + /* RAM case */ + ptr = qemu_get_ram_ptr(addr1); + stw_p(ptr, val); + if (!cpu_physical_memory_is_dirty(addr1)) { + /* invalidate code */ + tb_invalidate_phys_page_range(addr1, addr1 + 2, 0); + /* set dirty bit */ + cpu_physical_memory_set_dirty_flags(addr1, + (0xff & ~CODE_DIRTY_FLAG)); + } + } } /* XXX: optimize */ @@ -3674,8 +4189,6 @@ cpu_physical_memory_write(addr, (const uint8_t *)&val, 8); } -#endif - /* virtual memory access for debug (includes writing to ROM) */ int cpu_memory_rw_debug(CPUState *env, target_ulong addr, uint8_t *buf, int len, int is_write) @@ -3694,11 +4207,9 @@ if (l > len) l = len; phys_addr += (addr & ~TARGET_PAGE_MASK); -#if !defined(CONFIG_USER_ONLY) if (is_write) cpu_physical_memory_write_rom(phys_addr, buf, l); else -#endif cpu_physical_memory_rw(phys_addr, buf, l, is_write); len -= l; buf += l; @@ -3706,6 +4217,7 @@ } return 0; } +#endif /* in deterministic execution mode, instructions doing device I/Os must be at the end of the TB */ @@ -3766,8 +4278,9 @@ cpu_resume_from_signal(env, NULL); } -void dump_exec_info(FILE *f, - int (*cpu_fprintf)(FILE *f, const char *fmt, ...)) +#if !defined(CONFIG_USER_ONLY) + +void dump_exec_info(FILE *f, fprintf_function cpu_fprintf) { int i, target_code_size, max_target_code_size; int direct_jmp_count, direct_jmp2_count, cross_page; @@ -3794,14 +4307,14 @@ } /* XXX: avoid using doubles ? */ cpu_fprintf(f, "Translation buffer state:\n"); - cpu_fprintf(f, "gen code size %ld/%ld\n", + cpu_fprintf(f, "gen code size %td/%ld\n", code_gen_ptr - code_gen_buffer, code_gen_buffer_max_size); cpu_fprintf(f, "TB count %d/%d\n", nb_tbs, code_gen_max_blocks); cpu_fprintf(f, "TB avg target size %d max=%d bytes\n", nb_tbs ? target_code_size / nb_tbs : 0, max_target_code_size); - cpu_fprintf(f, "TB avg host size %d bytes (expansion ratio: %0.1f)\n", + cpu_fprintf(f, "TB avg host size %td bytes (expansion ratio: %0.1f)\n", nb_tbs ? (code_gen_ptr - code_gen_buffer) / nb_tbs : 0, target_code_size ? (double) (code_gen_ptr - code_gen_buffer) / target_code_size : 0); cpu_fprintf(f, "cross page TB count %d (%d%%)\n", @@ -3821,8 +4334,6 @@ #endif } -#if !defined(CONFIG_USER_ONLY) - #define MMUSUFFIX _cmmu #define GETPC() NULL #define env cpu_single_env diff -Nru qemu-kvm-0.12.5+noroms/EXTERNAL_DEPENDENCIES qemu-kvm-0.14.1/EXTERNAL_DEPENDENCIES --- qemu-kvm-0.12.5+noroms/EXTERNAL_DEPENDENCIES 2010-07-27 19:47:05.000000000 +0000 +++ qemu-kvm-0.14.1/EXTERNAL_DEPENDENCIES 2011-05-11 13:29:46.000000000 +0000 @@ -1,2 +1,2 @@ -seabios 9fb3f4d950744e97cc655b7d7b523d8bf101e4a0 -vgabios 6e62666cfc19e7fd45dd0d7c3ad62fd8d0b5f67a +seabios 06d0bdd9e2e20377b3180e4986b14c8549b393e4 +vgabios ca056d8e77a534f4f90548bc8cee166a378c1454 diff -Nru qemu-kvm-0.12.5+noroms/feature_to_c.sh qemu-kvm-0.14.1/feature_to_c.sh --- qemu-kvm-0.12.5+noroms/feature_to_c.sh 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/feature_to_c.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,76 +0,0 @@ -#!/bin/sh - -# Convert text files to compilable C arrays. -# -# Copyright (C) 2007 Free Software Foundation, Inc. -# -# This file is part of GDB. -# -# 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, see . - -output=$1 -shift - -if test -z "$output" || test -z "$1"; then - echo "Usage: $0 OUTPUTFILE INPUTFILE..." - exit 1 -fi - -if test -e "$output"; then - echo "Output file \"$output\" already exists; refusing to overwrite." - exit 1 -fi - -for input; do - arrayname=xml_feature_`echo $input | sed 's,.*/,,; s/[-.]/_/g'` - - ${AWK:-awk} 'BEGIN { n = 0 - print "static const char '$arrayname'[] = {" - for (i = 0; i < 255; i++) - _ord_[sprintf("%c", i)] = i - } { - split($0, line, ""); - printf " " - for (i = 1; i <= length($0); i++) { - c = line[i] - if (c == "'\''") { - printf "'\''\\'\'''\'', " - } else if (c == "\\") { - printf "'\''\\\\'\'', " - } else if (_ord_[c] >= 32 && _ord_[c] < 127) { - printf "'\''%s'\'', ", c - } else { - printf "'\''\\%03o'\'', ", _ord_[c] - } - if (i % 10 == 0) - printf "\n " - } - printf "'\''\\n'\'', \n" - } END { - print " 0 };" - }' < $input >> $output -done - -echo >> $output -echo "extern const char *const xml_builtin[][2];" >> $output -echo "const char *const xml_builtin[][2] = {" >> $output - -for input; do - basename=`echo $input | sed 's,.*/,,'` - arrayname=xml_feature_`echo $input | sed 's,.*/,,; s/[-.]/_/g'` - echo " { \"$basename\", $arrayname }," >> $output -done - -echo " { (char *)0, (char *)0 }" >> $output -echo "};" >> $output diff -Nru qemu-kvm-0.12.5+noroms/fpu/softfloat.c qemu-kvm-0.14.1/fpu/softfloat.c --- qemu-kvm-0.12.5+noroms/fpu/softfloat.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/fpu/softfloat.c 2011-05-11 13:29:46.000000000 +0000 @@ -30,8 +30,6 @@ =============================================================================*/ -/* FIXME: Flush-To-Zero only effects results. Denormal inputs should also - be flushed to zero. */ #include "softfloat.h" /*---------------------------------------------------------------------------- @@ -204,6 +202,21 @@ } /*---------------------------------------------------------------------------- +| If `a' is denormal and we are in flush-to-zero mode then set the +| input-denormal exception and return zero. Otherwise just return the value. +*----------------------------------------------------------------------------*/ +static float32 float32_squash_input_denormal(float32 a STATUS_PARAM) +{ + if (STATUS(flush_inputs_to_zero)) { + if (extractFloat32Exp(a) == 0 && extractFloat32Frac(a) != 0) { + float_raise(float_flag_input_denormal STATUS_VAR); + return make_float32(float32_val(a) & 0x80000000); + } + } + return a; +} + +/*---------------------------------------------------------------------------- | Normalizes the subnormal single-precision floating-point value represented | by the denormalized significand `aSig'. The normalized exponent and | significand are stored at the locations pointed to by `zExpPtr' and @@ -368,6 +381,21 @@ } /*---------------------------------------------------------------------------- +| If `a' is denormal and we are in flush-to-zero mode then set the +| input-denormal exception and return zero. Otherwise just return the value. +*----------------------------------------------------------------------------*/ +static float64 float64_squash_input_denormal(float64 a STATUS_PARAM) +{ + if (STATUS(flush_inputs_to_zero)) { + if (extractFloat64Exp(a) == 0 && extractFloat64Frac(a) != 0) { + float_raise(float_flag_input_denormal STATUS_VAR); + return make_float64(float64_val(a) & (1ULL << 63)); + } + } + return a; +} + +/*---------------------------------------------------------------------------- | Normalizes the subnormal double-precision floating-point value represented | by the denormalized significand `aSig'. The normalized exponent and | significand are stored at the locations pointed to by `zExpPtr' and @@ -1298,6 +1326,7 @@ bits32 aSig; bits64 aSig64; + a = float32_squash_input_denormal(a STATUS_VAR); aSig = extractFloat32Frac( a ); aExp = extractFloat32Exp( a ); aSign = extractFloat32Sign( a ); @@ -1327,6 +1356,7 @@ int16 aExp, shiftCount; bits32 aSig; int32 z; + a = float32_squash_input_denormal(a STATUS_VAR); aSig = extractFloat32Frac( a ); aExp = extractFloat32Exp( a ); @@ -1355,6 +1385,55 @@ /*---------------------------------------------------------------------------- | Returns the result of converting the single-precision floating-point value +| `a' to the 16-bit two's complement integer format. The conversion is +| performed according to the IEC/IEEE Standard for Binary Floating-Point +| Arithmetic, except that the conversion is always rounded toward zero. +| If `a' is a NaN, the largest positive integer is returned. Otherwise, if +| the conversion overflows, the largest integer with the same sign as `a' is +| returned. +*----------------------------------------------------------------------------*/ + +int16 float32_to_int16_round_to_zero( float32 a STATUS_PARAM ) +{ + flag aSign; + int16 aExp, shiftCount; + bits32 aSig; + int32 z; + + aSig = extractFloat32Frac( a ); + aExp = extractFloat32Exp( a ); + aSign = extractFloat32Sign( a ); + shiftCount = aExp - 0x8E; + if ( 0 <= shiftCount ) { + if ( float32_val(a) != 0xC7000000 ) { + float_raise( float_flag_invalid STATUS_VAR); + if ( ! aSign || ( ( aExp == 0xFF ) && aSig ) ) { + return 0x7FFF; + } + } + return (sbits32) 0xffff8000; + } + else if ( aExp <= 0x7E ) { + if ( aExp | aSig ) { + STATUS(float_exception_flags) |= float_flag_inexact; + } + return 0; + } + shiftCount -= 0x10; + aSig = ( aSig | 0x00800000 )<<8; + z = aSig>>( - shiftCount ); + if ( (bits32) ( aSig<<( shiftCount & 31 ) ) ) { + STATUS(float_exception_flags) |= float_flag_inexact; + } + if ( aSign ) { + z = - z; + } + return z; + +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the single-precision floating-point value | `a' to the 64-bit two's complement integer format. The conversion is | performed according to the IEC/IEEE Standard for Binary Floating-Point | Arithmetic---which means in particular that the conversion is rounded @@ -1369,6 +1448,7 @@ int16 aExp, shiftCount; bits32 aSig; bits64 aSig64, aSigExtra; + a = float32_squash_input_denormal(a STATUS_VAR); aSig = extractFloat32Frac( a ); aExp = extractFloat32Exp( a ); @@ -1406,6 +1486,7 @@ bits32 aSig; bits64 aSig64; int64 z; + a = float32_squash_input_denormal(a STATUS_VAR); aSig = extractFloat32Frac( a ); aExp = extractFloat32Exp( a ); @@ -1447,6 +1528,7 @@ flag aSign; int16 aExp; bits32 aSig; + a = float32_squash_input_denormal(a STATUS_VAR); aSig = extractFloat32Frac( a ); aExp = extractFloat32Exp( a ); @@ -1479,6 +1561,7 @@ int16 aExp; bits32 aSig; + a = float32_squash_input_denormal(a STATUS_VAR); aSig = extractFloat32Frac( a ); aExp = extractFloat32Exp( a ); aSign = extractFloat32Sign( a ); @@ -1512,6 +1595,7 @@ int16 aExp; bits32 aSig; + a = float32_squash_input_denormal(a STATUS_VAR); aSig = extractFloat32Frac( a ); aExp = extractFloat32Exp( a ); aSign = extractFloat32Sign( a ); @@ -1544,6 +1628,7 @@ bits32 lastBitMask, roundBitsMask; int8 roundingMode; bits32 z; + a = float32_squash_input_denormal(a STATUS_VAR); aExp = extractFloat32Exp( a ); if ( 0x96 <= aExp ) { @@ -1747,6 +1832,8 @@ float32 float32_add( float32 a, float32 b STATUS_PARAM ) { flag aSign, bSign; + a = float32_squash_input_denormal(a STATUS_VAR); + b = float32_squash_input_denormal(b STATUS_VAR); aSign = extractFloat32Sign( a ); bSign = extractFloat32Sign( b ); @@ -1768,6 +1855,8 @@ float32 float32_sub( float32 a, float32 b STATUS_PARAM ) { flag aSign, bSign; + a = float32_squash_input_denormal(a STATUS_VAR); + b = float32_squash_input_denormal(b STATUS_VAR); aSign = extractFloat32Sign( a ); bSign = extractFloat32Sign( b ); @@ -1794,6 +1883,9 @@ bits64 zSig64; bits32 zSig; + a = float32_squash_input_denormal(a STATUS_VAR); + b = float32_squash_input_denormal(b STATUS_VAR); + aSig = extractFloat32Frac( a ); aExp = extractFloat32Exp( a ); aSign = extractFloat32Sign( a ); @@ -1851,6 +1943,8 @@ flag aSign, bSign, zSign; int16 aExp, bExp, zExp; bits32 aSig, bSig, zSig; + a = float32_squash_input_denormal(a STATUS_VAR); + b = float32_squash_input_denormal(b STATUS_VAR); aSig = extractFloat32Frac( a ); aExp = extractFloat32Exp( a ); @@ -1910,20 +2004,21 @@ float32 float32_rem( float32 a, float32 b STATUS_PARAM ) { - flag aSign, bSign, zSign; + flag aSign, zSign; int16 aExp, bExp, expDiff; bits32 aSig, bSig; bits32 q; bits64 aSig64, bSig64, q64; bits32 alternateASig; sbits32 sigMean; + a = float32_squash_input_denormal(a STATUS_VAR); + b = float32_squash_input_denormal(b STATUS_VAR); aSig = extractFloat32Frac( a ); aExp = extractFloat32Exp( a ); aSign = extractFloat32Sign( a ); bSig = extractFloat32Frac( b ); bExp = extractFloat32Exp( b ); - bSign = extractFloat32Sign( b ); if ( aExp == 0xFF ) { if ( aSig || ( ( bExp == 0xFF ) && bSig ) ) { return propagateFloat32NaN( a, b STATUS_VAR ); @@ -2014,6 +2109,7 @@ int16 aExp, zExp; bits32 aSig, zSig; bits64 rem, term; + a = float32_squash_input_denormal(a STATUS_VAR); aSig = extractFloat32Frac( a ); aExp = extractFloat32Exp( a ); @@ -2057,6 +2153,86 @@ } /*---------------------------------------------------------------------------- +| Returns the binary exponential of the single-precision floating-point value +| `a'. The operation is performed according to the IEC/IEEE Standard for +| Binary Floating-Point Arithmetic. +| +| Uses the following identities: +| +| 1. ------------------------------------------------------------------------- +| x x*ln(2) +| 2 = e +| +| 2. ------------------------------------------------------------------------- +| 2 3 4 5 n +| x x x x x x x +| e = 1 + --- + --- + --- + --- + --- + ... + --- + ... +| 1! 2! 3! 4! 5! n! +*----------------------------------------------------------------------------*/ + +static const float64 float32_exp2_coefficients[15] = +{ + make_float64( 0x3ff0000000000000ll ), /* 1 */ + make_float64( 0x3fe0000000000000ll ), /* 2 */ + make_float64( 0x3fc5555555555555ll ), /* 3 */ + make_float64( 0x3fa5555555555555ll ), /* 4 */ + make_float64( 0x3f81111111111111ll ), /* 5 */ + make_float64( 0x3f56c16c16c16c17ll ), /* 6 */ + make_float64( 0x3f2a01a01a01a01all ), /* 7 */ + make_float64( 0x3efa01a01a01a01all ), /* 8 */ + make_float64( 0x3ec71de3a556c734ll ), /* 9 */ + make_float64( 0x3e927e4fb7789f5cll ), /* 10 */ + make_float64( 0x3e5ae64567f544e4ll ), /* 11 */ + make_float64( 0x3e21eed8eff8d898ll ), /* 12 */ + make_float64( 0x3de6124613a86d09ll ), /* 13 */ + make_float64( 0x3da93974a8c07c9dll ), /* 14 */ + make_float64( 0x3d6ae7f3e733b81fll ), /* 15 */ +}; + +float32 float32_exp2( float32 a STATUS_PARAM ) +{ + flag aSign; + int16 aExp; + bits32 aSig; + float64 r, x, xn; + int i; + a = float32_squash_input_denormal(a STATUS_VAR); + + aSig = extractFloat32Frac( a ); + aExp = extractFloat32Exp( a ); + aSign = extractFloat32Sign( a ); + + if ( aExp == 0xFF) { + if ( aSig ) return propagateFloat32NaN( a, float32_zero STATUS_VAR ); + return (aSign) ? float32_zero : a; + } + if (aExp == 0) { + if (aSig == 0) return float32_one; + } + + float_raise( float_flag_inexact STATUS_VAR); + + /* ******************************* */ + /* using float64 for approximation */ + /* ******************************* */ + x = float32_to_float64(a STATUS_VAR); + x = float64_mul(x, float64_ln2 STATUS_VAR); + + xn = x; + r = float64_one; + for (i = 0 ; i < 15 ; i++) { + float64 f; + + f = float64_mul(xn, float32_exp2_coefficients[i] STATUS_VAR); + r = float64_add(r, f STATUS_VAR); + + xn = float64_mul(xn, x STATUS_VAR); + } + + return float64_to_float32(r, status); +} + +/*---------------------------------------------------------------------------- | Returns the binary log of the single-precision floating-point value `a'. | The operation is performed according to the IEC/IEEE Standard for Binary | Floating-Point Arithmetic. @@ -2067,6 +2243,7 @@ int16 aExp; bits32 aSig, zSig, i; + a = float32_squash_input_denormal(a STATUS_VAR); aSig = extractFloat32Frac( a ); aExp = extractFloat32Exp( a ); aSign = extractFloat32Sign( a ); @@ -2111,6 +2288,8 @@ int float32_eq( float32 a, float32 b STATUS_PARAM ) { + a = float32_squash_input_denormal(a STATUS_VAR); + b = float32_squash_input_denormal(b STATUS_VAR); if ( ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) ) || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) ) @@ -2136,6 +2315,8 @@ { flag aSign, bSign; bits32 av, bv; + a = float32_squash_input_denormal(a STATUS_VAR); + b = float32_squash_input_denormal(b STATUS_VAR); if ( ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) ) || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) ) @@ -2162,6 +2343,8 @@ { flag aSign, bSign; bits32 av, bv; + a = float32_squash_input_denormal(a STATUS_VAR); + b = float32_squash_input_denormal(b STATUS_VAR); if ( ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) ) || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) ) @@ -2188,6 +2371,8 @@ int float32_eq_signaling( float32 a, float32 b STATUS_PARAM ) { bits32 av, bv; + a = float32_squash_input_denormal(a STATUS_VAR); + b = float32_squash_input_denormal(b STATUS_VAR); if ( ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) ) || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) ) @@ -2212,6 +2397,8 @@ { flag aSign, bSign; bits32 av, bv; + a = float32_squash_input_denormal(a STATUS_VAR); + b = float32_squash_input_denormal(b STATUS_VAR); if ( ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) ) || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) ) @@ -2241,6 +2428,8 @@ { flag aSign, bSign; bits32 av, bv; + a = float32_squash_input_denormal(a STATUS_VAR); + b = float32_squash_input_denormal(b STATUS_VAR); if ( ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) ) || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) ) @@ -2274,6 +2463,7 @@ flag aSign; int16 aExp, shiftCount; bits64 aSig; + a = float64_squash_input_denormal(a STATUS_VAR); aSig = extractFloat64Frac( a ); aExp = extractFloat64Exp( a ); @@ -2302,6 +2492,7 @@ int16 aExp, shiftCount; bits64 aSig, savedASig; int32 z; + a = float64_squash_input_denormal(a STATUS_VAR); aSig = extractFloat64Frac( a ); aExp = extractFloat64Exp( a ); @@ -2334,6 +2525,57 @@ /*---------------------------------------------------------------------------- | Returns the result of converting the double-precision floating-point value +| `a' to the 16-bit two's complement integer format. The conversion is +| performed according to the IEC/IEEE Standard for Binary Floating-Point +| Arithmetic, except that the conversion is always rounded toward zero. +| If `a' is a NaN, the largest positive integer is returned. Otherwise, if +| the conversion overflows, the largest integer with the same sign as `a' is +| returned. +*----------------------------------------------------------------------------*/ + +int16 float64_to_int16_round_to_zero( float64 a STATUS_PARAM ) +{ + flag aSign; + int16 aExp, shiftCount; + bits64 aSig, savedASig; + int32 z; + + aSig = extractFloat64Frac( a ); + aExp = extractFloat64Exp( a ); + aSign = extractFloat64Sign( a ); + if ( 0x40E < aExp ) { + if ( ( aExp == 0x7FF ) && aSig ) { + aSign = 0; + } + goto invalid; + } + else if ( aExp < 0x3FF ) { + if ( aExp || aSig ) { + STATUS(float_exception_flags) |= float_flag_inexact; + } + return 0; + } + aSig |= LIT64( 0x0010000000000000 ); + shiftCount = 0x433 - aExp; + savedASig = aSig; + aSig >>= shiftCount; + z = aSig; + if ( aSign ) { + z = - z; + } + if ( ( (int16_t)z < 0 ) ^ aSign ) { + invalid: + float_raise( float_flag_invalid STATUS_VAR); + return aSign ? (sbits32) 0xffff8000 : 0x7FFF; + } + if ( ( aSig< 0xffff) { + res = 0xffff; + float_raise( float_flag_invalid STATUS_VAR); + } else { + res = v; + } + return res; +} + unsigned int float64_to_uint32( float64 a STATUS_PARAM ) { int64_t v; @@ -5593,6 +5882,24 @@ return res; } +unsigned int float64_to_uint16_round_to_zero( float64 a STATUS_PARAM ) +{ + int64_t v; + unsigned int res; + + v = float64_to_int64_round_to_zero(a STATUS_VAR); + if (v < 0) { + res = 0; + float_raise( float_flag_invalid STATUS_VAR); + } else if (v > 0xffff) { + res = 0xffff; + float_raise( float_flag_invalid STATUS_VAR); + } else { + res = v; + } + return res; +} + /* FIXME: This looks broken. */ uint64_t float64_to_uint64 (float64 a STATUS_PARAM) { @@ -5622,6 +5929,8 @@ { \ flag aSign, bSign; \ bits ## s av, bv; \ + a = float ## s ## _squash_input_denormal(a STATUS_VAR); \ + b = float ## s ## _squash_input_denormal(b STATUS_VAR); \ \ if (( ( extractFloat ## s ## Exp( a ) == nan_exp ) && \ extractFloat ## s ## Frac( a ) ) || \ @@ -5718,6 +6027,7 @@ int16 aExp; bits32 aSig; + a = float32_squash_input_denormal(a STATUS_VAR); aSig = extractFloat32Frac( a ); aExp = extractFloat32Exp( a ); aSign = extractFloat32Sign( a ); @@ -5741,6 +6051,7 @@ int16 aExp; bits64 aSig; + a = float64_squash_input_denormal(a STATUS_VAR); aSig = extractFloat64Frac( a ); aExp = extractFloat64Exp( a ); aSign = extractFloat64Sign( a ); diff -Nru qemu-kvm-0.12.5+noroms/fpu/softfloat.h qemu-kvm-0.14.1/fpu/softfloat.h --- qemu-kvm-0.12.5+noroms/fpu/softfloat.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/fpu/softfloat.h 2011-05-11 13:29:46.000000000 +0000 @@ -180,7 +180,8 @@ float_flag_divbyzero = 4, float_flag_overflow = 8, float_flag_underflow = 16, - float_flag_inexact = 32 + float_flag_inexact = 32, + float_flag_input_denormal = 64 }; typedef struct float_status { @@ -190,7 +191,10 @@ #ifdef FLOATX80 signed char floatx80_rounding_precision; #endif + /* should denormalised results go to zero and set the inexact flag? */ flag flush_to_zero; + /* should denormalised inputs go to zero and set the input_denormal flag? */ + flag flush_inputs_to_zero; flag default_nan_mode; } float_status; @@ -200,6 +204,10 @@ { STATUS(flush_to_zero) = val; } +INLINE void set_flush_inputs_to_zero(flag val STATUS_PARAM) +{ + STATUS(flush_inputs_to_zero) = val; +} INLINE void set_default_nan_mode(flag val STATUS_PARAM) { STATUS(default_nan_mode) = val; @@ -251,6 +259,8 @@ /*---------------------------------------------------------------------------- | Software IEC/IEEE single-precision conversion routines. *----------------------------------------------------------------------------*/ +int float32_to_int16_round_to_zero( float32 STATUS_PARAM ); +unsigned int float32_to_uint16_round_to_zero( float32 STATUS_PARAM ); int float32_to_int32( float32 STATUS_PARAM ); int float32_to_int32_round_to_zero( float32 STATUS_PARAM ); unsigned int float32_to_uint32( float32 STATUS_PARAM ); @@ -275,6 +285,7 @@ float32 float32_div( float32, float32 STATUS_PARAM ); float32 float32_rem( float32, float32 STATUS_PARAM ); float32 float32_sqrt( float32 STATUS_PARAM ); +float32 float32_exp2( float32 STATUS_PARAM ); float32 float32_log2( float32 STATUS_PARAM ); int float32_eq( float32, float32 STATUS_PARAM ); int float32_le( float32, float32 STATUS_PARAM ); @@ -284,17 +295,24 @@ int float32_lt_quiet( float32, float32 STATUS_PARAM ); int float32_compare( float32, float32 STATUS_PARAM ); int float32_compare_quiet( float32, float32 STATUS_PARAM ); -int float32_is_nan( float32 ); +int float32_is_quiet_nan( float32 ); int float32_is_signaling_nan( float32 ); +float32 float32_maybe_silence_nan( float32 ); float32 float32_scalbn( float32, int STATUS_PARAM ); INLINE float32 float32_abs(float32 a) { + /* Note that abs does *not* handle NaN specially, nor does + * it flush denormal inputs to zero. + */ return make_float32(float32_val(a) & 0x7fffffff); } INLINE float32 float32_chs(float32 a) { + /* Note that chs does *not* handle NaN specially, nor does + * it flush denormal inputs to zero. + */ return make_float32(float32_val(a) ^ 0x80000000); } @@ -313,12 +331,25 @@ return (float32_val(a) & 0x7fffffff) == 0; } +INLINE int float32_is_any_nan(float32 a) +{ + return ((float32_val(a) & ~(1 << 31)) > 0x7f800000UL); +} + +INLINE int float32_is_zero_or_denormal(float32 a) +{ + return (float32_val(a) & 0x7f800000) == 0; +} + #define float32_zero make_float32(0) #define float32_one make_float32(0x3f800000) +#define float32_ln2 make_float32(0x3f317218) /*---------------------------------------------------------------------------- | Software IEC/IEEE double-precision conversion routines. *----------------------------------------------------------------------------*/ +int float64_to_int16_round_to_zero( float64 STATUS_PARAM ); +unsigned int float64_to_uint16_round_to_zero( float64 STATUS_PARAM ); int float64_to_int32( float64 STATUS_PARAM ); int float64_to_int32_round_to_zero( float64 STATUS_PARAM ); unsigned int float64_to_uint32( float64 STATUS_PARAM ); @@ -355,17 +386,24 @@ int float64_lt_quiet( float64, float64 STATUS_PARAM ); int float64_compare( float64, float64 STATUS_PARAM ); int float64_compare_quiet( float64, float64 STATUS_PARAM ); -int float64_is_nan( float64 a ); +int float64_is_quiet_nan( float64 a ); int float64_is_signaling_nan( float64 ); +float64 float64_maybe_silence_nan( float64 ); float64 float64_scalbn( float64, int STATUS_PARAM ); INLINE float64 float64_abs(float64 a) { + /* Note that abs does *not* handle NaN specially, nor does + * it flush denormal inputs to zero. + */ return make_float64(float64_val(a) & 0x7fffffffffffffffLL); } INLINE float64 float64_chs(float64 a) { + /* Note that chs does *not* handle NaN specially, nor does + * it flush denormal inputs to zero. + */ return make_float64(float64_val(a) ^ 0x8000000000000000LL); } @@ -384,8 +422,14 @@ return (float64_val(a) & 0x7fffffffffffffffLL) == 0; } +INLINE int float64_is_any_nan(float64 a) +{ + return ((float64_val(a) & ~(1ULL << 63)) > 0x7ff0000000000000ULL); +} + #define float64_zero make_float64(0) #define float64_one make_float64(0x3ff0000000000000LL) +#define float64_ln2 make_float64(0x3fe62e42fefa39efLL) #ifdef FLOATX80 @@ -418,8 +462,9 @@ int floatx80_eq_signaling( floatx80, floatx80 STATUS_PARAM ); int floatx80_le_quiet( floatx80, floatx80 STATUS_PARAM ); int floatx80_lt_quiet( floatx80, floatx80 STATUS_PARAM ); -int floatx80_is_nan( floatx80 ); +int floatx80_is_quiet_nan( floatx80 ); int floatx80_is_signaling_nan( floatx80 ); +floatx80 floatx80_maybe_silence_nan( floatx80 ); floatx80 floatx80_scalbn( floatx80, int STATUS_PARAM ); INLINE floatx80 floatx80_abs(floatx80 a) @@ -449,6 +494,11 @@ return (a.high & 0x7fff) == 0 && a.low == 0; } +INLINE int floatx80_is_any_nan(floatx80 a) +{ + return ((a.high & 0x7fff) == 0x7fff) && (a.low<<1); +} + #endif #ifdef FLOAT128 @@ -484,8 +534,9 @@ int float128_lt_quiet( float128, float128 STATUS_PARAM ); int float128_compare( float128, float128 STATUS_PARAM ); int float128_compare_quiet( float128, float128 STATUS_PARAM ); -int float128_is_nan( float128 ); +int float128_is_quiet_nan( float128 ); int float128_is_signaling_nan( float128 ); +float128 float128_maybe_silence_nan( float128 ); float128 float128_scalbn( float128, int STATUS_PARAM ); INLINE float128 float128_abs(float128 a) @@ -515,6 +566,12 @@ return (a.high & 0x7fffffffffffffffLL) == 0 && a.low == 0; } +INLINE int float128_is_any_nan(float128 a) +{ + return ((a.high >> 48) & 0x7fff) == 0x7fff && + ((a.low != 0) || ((a.high & 0xffffffffffffLL) != 0)); +} + #endif #else /* CONFIG_SOFTFLOAT */ diff -Nru qemu-kvm-0.12.5+noroms/fpu/softfloat-native.c qemu-kvm-0.14.1/fpu/softfloat-native.c --- qemu-kvm-0.12.5+noroms/fpu/softfloat-native.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/fpu/softfloat-native.c 2011-05-11 13:29:46.000000000 +0000 @@ -13,8 +13,6 @@ #if (defined(CONFIG_BSD) && !defined(__APPLE__) && !defined(__GLIBC__)) || \ (defined(CONFIG_SOLARIS) && CONFIG_SOLARIS_VERSION < 10) fpsetround(val); -#elif defined(__arm__) - /* nothing to do */ #else fesetround(val); #endif @@ -257,7 +255,7 @@ return ( ( ( a>>22 ) & 0x1FF ) == 0x1FE ) && ( a & 0x003FFFFF ); } -int float32_is_nan( float32 a1 ) +int float32_is_quiet_nan( float32 a1 ) { float32u u; uint64_t a; @@ -366,25 +364,7 @@ float64 float64_round_to_int( float64 a STATUS_PARAM ) { -#if defined(__arm__) - switch(STATUS(float_rounding_mode)) { - default: - case float_round_nearest_even: - asm("rndd %0, %1" : "=f" (a) : "f"(a)); - break; - case float_round_down: - asm("rnddm %0, %1" : "=f" (a) : "f"(a)); - break; - case float_round_up: - asm("rnddp %0, %1" : "=f" (a) : "f"(a)); - break; - case float_round_to_zero: - asm("rnddz %0, %1" : "=f" (a) : "f"(a)); - break; - } -#else return rint(a); -#endif } float64 float64_rem( float64 a, float64 b STATUS_PARAM) @@ -432,7 +412,7 @@ } -int float64_is_nan( float64 a1 ) +int float64_is_quiet_nan( float64 a1 ) { float64u u; uint64_t a; @@ -525,7 +505,7 @@ && ( u.i.low == aLow ); } -int floatx80_is_nan( floatx80 a1 ) +int floatx80_is_quiet_nan( floatx80 a1 ) { floatx80u u; u.f = a1; diff -Nru qemu-kvm-0.12.5+noroms/fpu/softfloat-native.h qemu-kvm-0.14.1/fpu/softfloat-native.h --- qemu-kvm-0.12.5+noroms/fpu/softfloat-native.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/fpu/softfloat-native.h 2011-05-11 13:29:46.000000000 +0000 @@ -22,7 +22,7 @@ */ #if defined(CONFIG_SOLARIS) && \ ((CONFIG_SOLARIS_VERSION <= 9 ) || \ - ((CONFIG_SOLARIS_VERSION >= 10) && (__GNUC__ < 4))) \ + ((CONFIG_SOLARIS_VERSION == 10) && (__GNUC__ < 4))) \ || (defined(__OpenBSD__) && (OpenBSD < 200811)) /* * C99 7.12.3 classification macros @@ -126,13 +126,6 @@ float_round_up = FP_RP, float_round_to_zero = FP_RZ }; -#elif defined(__arm__) -enum { - float_round_nearest_even = 0, - float_round_down = 1, - float_round_up = 2, - float_round_to_zero = 3 -}; #else enum { float_round_nearest_even = FE_TONEAREST, @@ -249,7 +242,7 @@ int float32_compare( float32, float32 STATUS_PARAM ); int float32_compare_quiet( float32, float32 STATUS_PARAM ); int float32_is_signaling_nan( float32 ); -int float32_is_nan( float32 ); +int float32_is_quiet_nan( float32 ); INLINE float32 float32_abs(float32 a) { @@ -358,7 +351,7 @@ int float64_compare( float64, float64 STATUS_PARAM ); int float64_compare_quiet( float64, float64 STATUS_PARAM ); int float64_is_signaling_nan( float64 ); -int float64_is_nan( float64 ); +int float64_is_quiet_nan( float64 ); INLINE float64 float64_abs(float64 a) { @@ -462,7 +455,7 @@ int floatx80_compare( floatx80, floatx80 STATUS_PARAM ); int floatx80_compare_quiet( floatx80, floatx80 STATUS_PARAM ); int floatx80_is_signaling_nan( floatx80 ); -int floatx80_is_nan( floatx80 ); +int floatx80_is_quiet_nan( floatx80 ); INLINE floatx80 floatx80_abs(floatx80 a) { diff -Nru qemu-kvm-0.12.5+noroms/fpu/softfloat-specialize.h qemu-kvm-0.14.1/fpu/softfloat-specialize.h --- qemu-kvm-0.12.5+noroms/fpu/softfloat-specialize.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/fpu/softfloat-specialize.h 2011-05-11 13:29:46.000000000 +0000 @@ -30,7 +30,7 @@ =============================================================================*/ -#if defined(TARGET_MIPS) || defined(TARGET_HPPA) +#if defined(TARGET_MIPS) || defined(TARGET_SH4) #define SNAN_BIT_IS_ONE 1 #else #define SNAN_BIT_IS_ONE 0 @@ -61,10 +61,8 @@ *----------------------------------------------------------------------------*/ #if defined(TARGET_SPARC) #define float32_default_nan make_float32(0x7FFFFFFF) -#elif defined(TARGET_POWERPC) || defined(TARGET_ARM) +#elif defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_ALPHA) #define float32_default_nan make_float32(0x7FC00000) -#elif defined(TARGET_HPPA) -#define float32_default_nan make_float32(0x7FA00000) #elif SNAN_BIT_IS_ONE #define float32_default_nan make_float32(0x7FBFFFFF) #else @@ -76,7 +74,7 @@ | NaN; otherwise returns 0. *----------------------------------------------------------------------------*/ -int float32_is_nan( float32 a_ ) +int float32_is_quiet_nan( float32 a_ ) { uint32_t a = float32_val(a_); #if SNAN_BIT_IS_ONE @@ -102,6 +100,29 @@ } /*---------------------------------------------------------------------------- +| Returns a quiet NaN if the single-precision floating point value `a' is a +| signaling NaN; otherwise returns `a'. +*----------------------------------------------------------------------------*/ + +float32 float32_maybe_silence_nan( float32 a_ ) +{ + if (float32_is_signaling_nan(a_)) { +#if SNAN_BIT_IS_ONE +# if defined(TARGET_MIPS) || defined(TARGET_SH4) + return float32_default_nan; +# else +# error Rules for silencing a signaling NaN are target-specific +# endif +#else + bits32 a = float32_val(a_); + a |= (1 << 22); + return make_float32(a); +#endif + } + return a_; +} + +/*---------------------------------------------------------------------------- | Returns the result of converting the single-precision floating-point NaN | `a' to the canonical NaN format. If `a' is a signaling NaN, the invalid | exception is raised. @@ -134,6 +155,118 @@ } /*---------------------------------------------------------------------------- +| Select which NaN to propagate for a two-input operation. +| IEEE754 doesn't specify all the details of this, so the +| algorithm is target-specific. +| The routine is passed various bits of information about the +| two NaNs and should return 0 to select NaN a and 1 for NaN b. +| Note that signalling NaNs are always squashed to quiet NaNs +| by the caller, by calling floatXX_maybe_silence_nan() before +| returning them. +| +| aIsLargerSignificand is only valid if both a and b are NaNs +| of some kind, and is true if a has the larger significand, +| or if both a and b have the same significand but a is +| positive but b is negative. It is only needed for the x87 +| tie-break rule. +*----------------------------------------------------------------------------*/ + +#if defined(TARGET_ARM) +static int pickNaN(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, + flag aIsLargerSignificand) +{ + /* ARM mandated NaN propagation rules: take the first of: + * 1. A if it is signaling + * 2. B if it is signaling + * 3. A (quiet) + * 4. B (quiet) + * A signaling NaN is always quietened before returning it. + */ + if (aIsSNaN) { + return 0; + } else if (bIsSNaN) { + return 1; + } else if (aIsQNaN) { + return 0; + } else { + return 1; + } +} +#elif defined(TARGET_MIPS) +static int pickNaN(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, + flag aIsLargerSignificand) +{ + /* According to MIPS specifications, if one of the two operands is + * a sNaN, a new qNaN has to be generated. This is done in + * floatXX_maybe_silence_nan(). For qNaN inputs the specifications + * says: "When possible, this QNaN result is one of the operand QNaN + * values." In practice it seems that most implementations choose + * the first operand if both operands are qNaN. In short this gives + * the following rules: + * 1. A if it is signaling + * 2. B if it is signaling + * 3. A (quiet) + * 4. B (quiet) + * A signaling NaN is always silenced before returning it. + */ + if (aIsSNaN) { + return 0; + } else if (bIsSNaN) { + return 1; + } else if (aIsQNaN) { + return 0; + } else { + return 1; + } +} +#elif defined(TARGET_PPC) +static int pickNaN(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, + flag aIsLargerSignificand) +{ + /* PowerPC propagation rules: + * 1. A if it sNaN or qNaN + * 2. B if it sNaN or qNaN + * A signaling NaN is always silenced before returning it. + */ + if (aIsSNaN || aIsQNaN) { + return 0; + } else { + return 1; + } +} +#else +static int pickNaN(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, + flag aIsLargerSignificand) +{ + /* This implements x87 NaN propagation rules: + * SNaN + QNaN => return the QNaN + * two SNaNs => return the one with the larger significand, silenced + * two QNaNs => return the one with the larger significand + * SNaN and a non-NaN => return the SNaN, silenced + * QNaN and a non-NaN => return the QNaN + * + * If we get down to comparing significands and they are the same, + * return the NaN with the positive sign bit (if any). + */ + if (aIsSNaN) { + if (bIsSNaN) { + return aIsLargerSignificand ? 0 : 1; + } + return bIsQNaN ? 1 : 0; + } + else if (aIsQNaN) { + if (bIsSNaN || !bIsQNaN) + return 0; + else { + return aIsLargerSignificand ? 0 : 1; + } + } else { + return 1; + } +} +#endif + +/*---------------------------------------------------------------------------- | Takes two single-precision floating-point values `a' and `b', one of which | is a NaN, and returns the appropriate NaN result. If either `a' or `b' is a | signaling NaN, the invalid exception is raised. @@ -141,47 +274,36 @@ static float32 propagateFloat32NaN( float32 a, float32 b STATUS_PARAM) { - flag aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN; - bits32 av, bv, res; - - if ( STATUS(default_nan_mode) ) - return float32_default_nan; + flag aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN; + flag aIsLargerSignificand; + bits32 av, bv; - aIsNaN = float32_is_nan( a ); + aIsQuietNaN = float32_is_quiet_nan( a ); aIsSignalingNaN = float32_is_signaling_nan( a ); - bIsNaN = float32_is_nan( b ); + bIsQuietNaN = float32_is_quiet_nan( b ); bIsSignalingNaN = float32_is_signaling_nan( b ); av = float32_val(a); bv = float32_val(b); -#if SNAN_BIT_IS_ONE - av &= ~0x00400000; - bv &= ~0x00400000; -#else - av |= 0x00400000; - bv |= 0x00400000; -#endif + if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid STATUS_VAR); - if ( aIsSignalingNaN ) { - if ( bIsSignalingNaN ) goto returnLargerSignificand; - res = bIsNaN ? bv : av; - } - else if ( aIsNaN ) { - if ( bIsSignalingNaN || ! bIsNaN ) - res = av; - else { - returnLargerSignificand: - if ( (bits32) ( av<<1 ) < (bits32) ( bv<<1 ) ) - res = bv; - else if ( (bits32) ( bv<<1 ) < (bits32) ( av<<1 ) ) - res = av; - else - res = ( av < bv ) ? av : bv; - } + + if ( STATUS(default_nan_mode) ) + return float32_default_nan; + + if ((bits32)(av<<1) < (bits32)(bv<<1)) { + aIsLargerSignificand = 0; + } else if ((bits32)(bv<<1) < (bits32)(av<<1)) { + aIsLargerSignificand = 1; + } else { + aIsLargerSignificand = (av < bv) ? 1 : 0; } - else { - res = bv; + + if (pickNaN(aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN, + aIsLargerSignificand)) { + return float32_maybe_silence_nan(b); + } else { + return float32_maybe_silence_nan(a); } - return make_float32(res); } /*---------------------------------------------------------------------------- @@ -189,10 +311,8 @@ *----------------------------------------------------------------------------*/ #if defined(TARGET_SPARC) #define float64_default_nan make_float64(LIT64( 0x7FFFFFFFFFFFFFFF )) -#elif defined(TARGET_POWERPC) || defined(TARGET_ARM) +#elif defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_ALPHA) #define float64_default_nan make_float64(LIT64( 0x7FF8000000000000 )) -#elif defined(TARGET_HPPA) -#define float64_default_nan make_float64(LIT64( 0x7FF4000000000000 )) #elif SNAN_BIT_IS_ONE #define float64_default_nan make_float64(LIT64( 0x7FF7FFFFFFFFFFFF )) #else @@ -204,7 +324,7 @@ | NaN; otherwise returns 0. *----------------------------------------------------------------------------*/ -int float64_is_nan( float64 a_ ) +int float64_is_quiet_nan( float64 a_ ) { bits64 a = float64_val(a_); #if SNAN_BIT_IS_ONE @@ -234,6 +354,29 @@ } /*---------------------------------------------------------------------------- +| Returns a quiet NaN if the double-precision floating point value `a' is a +| signaling NaN; otherwise returns `a'. +*----------------------------------------------------------------------------*/ + +float64 float64_maybe_silence_nan( float64 a_ ) +{ + if (float64_is_signaling_nan(a_)) { +#if SNAN_BIT_IS_ONE +# if defined(TARGET_MIPS) || defined(TARGET_SH4) + return float64_default_nan; +# else +# error Rules for silencing a signaling NaN are target-specific +# endif +#else + bits64 a = float64_val(a_); + a |= LIT64( 0x0008000000000000 ); + return make_float64(a); +#endif + } + return a_; +} + +/*---------------------------------------------------------------------------- | Returns the result of converting the double-precision floating-point NaN | `a' to the canonical NaN format. If `a' is a signaling NaN, the invalid | exception is raised. @@ -276,47 +419,36 @@ static float64 propagateFloat64NaN( float64 a, float64 b STATUS_PARAM) { - flag aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN; - bits64 av, bv, res; + flag aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN; + flag aIsLargerSignificand; + bits64 av, bv; - if ( STATUS(default_nan_mode) ) - return float64_default_nan; - - aIsNaN = float64_is_nan( a ); + aIsQuietNaN = float64_is_quiet_nan( a ); aIsSignalingNaN = float64_is_signaling_nan( a ); - bIsNaN = float64_is_nan( b ); + bIsQuietNaN = float64_is_quiet_nan( b ); bIsSignalingNaN = float64_is_signaling_nan( b ); av = float64_val(a); bv = float64_val(b); -#if SNAN_BIT_IS_ONE - av &= ~LIT64( 0x0008000000000000 ); - bv &= ~LIT64( 0x0008000000000000 ); -#else - av |= LIT64( 0x0008000000000000 ); - bv |= LIT64( 0x0008000000000000 ); -#endif + if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid STATUS_VAR); - if ( aIsSignalingNaN ) { - if ( bIsSignalingNaN ) goto returnLargerSignificand; - res = bIsNaN ? bv : av; - } - else if ( aIsNaN ) { - if ( bIsSignalingNaN || ! bIsNaN ) - res = av; - else { - returnLargerSignificand: - if ( (bits64) ( av<<1 ) < (bits64) ( bv<<1 ) ) - res = bv; - else if ( (bits64) ( bv<<1 ) < (bits64) ( av<<1 ) ) - res = av; - else - res = ( av < bv ) ? av : bv; - } + + if ( STATUS(default_nan_mode) ) + return float64_default_nan; + + if ((bits64)(av<<1) < (bits64)(bv<<1)) { + aIsLargerSignificand = 0; + } else if ((bits64)(bv<<1) < (bits64)(av<<1)) { + aIsLargerSignificand = 1; + } else { + aIsLargerSignificand = (av < bv) ? 1 : 0; } - else { - res = bv; + + if (pickNaN(aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN, + aIsLargerSignificand)) { + return float64_maybe_silence_nan(b); + } else { + return float64_maybe_silence_nan(a); } - return make_float64(res); } #ifdef FLOATX80 @@ -336,10 +468,11 @@ /*---------------------------------------------------------------------------- | Returns 1 if the extended double-precision floating-point value `a' is a -| quiet NaN; otherwise returns 0. +| quiet NaN; otherwise returns 0. This slightly differs from the same +| function for other types as floatx80 has an explicit bit. *----------------------------------------------------------------------------*/ -int floatx80_is_nan( floatx80 a ) +int floatx80_is_quiet_nan( floatx80 a ) { #if SNAN_BIT_IS_ONE bits64 aLow; @@ -350,19 +483,22 @@ && (bits64) ( aLow<<1 ) && ( a.low == aLow ); #else - return ( ( a.high & 0x7FFF ) == 0x7FFF ) && (bits64) ( a.low<<1 ); + return ( ( a.high & 0x7FFF ) == 0x7FFF ) + && (LIT64( 0x8000000000000000 ) <= ((bits64) ( a.low<<1 ))); #endif } /*---------------------------------------------------------------------------- | Returns 1 if the extended double-precision floating-point value `a' is a -| signaling NaN; otherwise returns 0. +| signaling NaN; otherwise returns 0. This slightly differs from the same +| function for other types as floatx80 has an explicit bit. *----------------------------------------------------------------------------*/ int floatx80_is_signaling_nan( floatx80 a ) { #if SNAN_BIT_IS_ONE - return ( ( a.high & 0x7FFF ) == 0x7FFF ) && (bits64) ( a.low<<1 ); + return ( ( a.high & 0x7FFF ) == 0x7FFF ) + && (LIT64( 0x8000000000000000 ) <= ((bits64) ( a.low<<1 ))); #else bits64 aLow; @@ -375,6 +511,29 @@ } /*---------------------------------------------------------------------------- +| Returns a quiet NaN if the extended double-precision floating point value +| `a' is a signaling NaN; otherwise returns `a'. +*----------------------------------------------------------------------------*/ + +floatx80 floatx80_maybe_silence_nan( floatx80 a ) +{ + if (floatx80_is_signaling_nan(a)) { +#if SNAN_BIT_IS_ONE +# if defined(TARGET_MIPS) || defined(TARGET_SH4) + a.low = floatx80_default_nan_low; + a.high = floatx80_default_nan_high; +# else +# error Rules for silencing a signaling NaN are target-specific +# endif +#else + a.low |= LIT64( 0xC000000000000000 ); + return a; +#endif + } + return a; +} + +/*---------------------------------------------------------------------------- | Returns the result of converting the extended double-precision floating- | point NaN `a' to the canonical NaN format. If `a' is a signaling NaN, the | invalid exception is raised. @@ -416,7 +575,15 @@ static floatx80 propagateFloatx80NaN( floatx80 a, floatx80 b STATUS_PARAM) { - flag aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN; + flag aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN; + flag aIsLargerSignificand; + + aIsQuietNaN = floatx80_is_quiet_nan( a ); + aIsSignalingNaN = floatx80_is_signaling_nan( a ); + bIsQuietNaN = floatx80_is_quiet_nan( b ); + bIsSignalingNaN = floatx80_is_signaling_nan( b ); + + if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid STATUS_VAR); if ( STATUS(default_nan_mode) ) { a.low = floatx80_default_nan_low; @@ -424,31 +591,19 @@ return a; } - aIsNaN = floatx80_is_nan( a ); - aIsSignalingNaN = floatx80_is_signaling_nan( a ); - bIsNaN = floatx80_is_nan( b ); - bIsSignalingNaN = floatx80_is_signaling_nan( b ); -#if SNAN_BIT_IS_ONE - a.low &= ~LIT64( 0xC000000000000000 ); - b.low &= ~LIT64( 0xC000000000000000 ); -#else - a.low |= LIT64( 0xC000000000000000 ); - b.low |= LIT64( 0xC000000000000000 ); -#endif - if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid STATUS_VAR); - if ( aIsSignalingNaN ) { - if ( bIsSignalingNaN ) goto returnLargerSignificand; - return bIsNaN ? b : a; - } - else if ( aIsNaN ) { - if ( bIsSignalingNaN || ! bIsNaN ) return a; - returnLargerSignificand: - if ( a.low < b.low ) return b; - if ( b.low < a.low ) return a; - return ( a.high < b.high ) ? a : b; + if (a.low < b.low) { + aIsLargerSignificand = 0; + } else if (b.low < a.low) { + aIsLargerSignificand = 1; + } else { + aIsLargerSignificand = (a.high < b.high) ? 1 : 0; } - else { - return b; + + if (pickNaN(aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN, + aIsLargerSignificand)) { + return floatx80_maybe_silence_nan(b); + } else { + return floatx80_maybe_silence_nan(a); } } @@ -473,7 +628,7 @@ | NaN; otherwise returns 0. *----------------------------------------------------------------------------*/ -int float128_is_nan( float128 a ) +int float128_is_quiet_nan( float128 a ) { #if SNAN_BIT_IS_ONE return @@ -505,6 +660,29 @@ } /*---------------------------------------------------------------------------- +| Returns a quiet NaN if the quadruple-precision floating point value `a' is +| a signaling NaN; otherwise returns `a'. +*----------------------------------------------------------------------------*/ + +float128 float128_maybe_silence_nan( float128 a ) +{ + if (float128_is_signaling_nan(a)) { +#if SNAN_BIT_IS_ONE +# if defined(TARGET_MIPS) || defined(TARGET_SH4) + a.low = float128_default_nan_low; + a.high = float128_default_nan_high; +# else +# error Rules for silencing a signaling NaN are target-specific +# endif +#else + a.high |= LIT64( 0x0000800000000000 ); + return a; +#endif + } + return a; +} + +/*---------------------------------------------------------------------------- | Returns the result of converting the quadruple-precision floating-point NaN | `a' to the canonical NaN format. If `a' is a signaling NaN, the invalid | exception is raised. @@ -542,7 +720,15 @@ static float128 propagateFloat128NaN( float128 a, float128 b STATUS_PARAM) { - flag aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN; + flag aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN; + flag aIsLargerSignificand; + + aIsQuietNaN = float128_is_quiet_nan( a ); + aIsSignalingNaN = float128_is_signaling_nan( a ); + bIsQuietNaN = float128_is_quiet_nan( b ); + bIsSignalingNaN = float128_is_signaling_nan( b ); + + if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid STATUS_VAR); if ( STATUS(default_nan_mode) ) { a.low = float128_default_nan_low; @@ -550,31 +736,19 @@ return a; } - aIsNaN = float128_is_nan( a ); - aIsSignalingNaN = float128_is_signaling_nan( a ); - bIsNaN = float128_is_nan( b ); - bIsSignalingNaN = float128_is_signaling_nan( b ); -#if SNAN_BIT_IS_ONE - a.high &= ~LIT64( 0x0000800000000000 ); - b.high &= ~LIT64( 0x0000800000000000 ); -#else - a.high |= LIT64( 0x0000800000000000 ); - b.high |= LIT64( 0x0000800000000000 ); -#endif - if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid STATUS_VAR); - if ( aIsSignalingNaN ) { - if ( bIsSignalingNaN ) goto returnLargerSignificand; - return bIsNaN ? b : a; - } - else if ( aIsNaN ) { - if ( bIsSignalingNaN || ! bIsNaN ) return a; - returnLargerSignificand: - if ( lt128( a.high<<1, a.low, b.high<<1, b.low ) ) return b; - if ( lt128( b.high<<1, b.low, a.high<<1, a.low ) ) return a; - return ( a.high < b.high ) ? a : b; + if (lt128(a.high<<1, a.low, b.high<<1, b.low)) { + aIsLargerSignificand = 0; + } else if (lt128(b.high<<1, b.low, a.high<<1, a.low)) { + aIsLargerSignificand = 1; + } else { + aIsLargerSignificand = (a.high < b.high) ? 1 : 0; } - else { - return b; + + if (pickNaN(aIsQuietNaN, aIsSignalingNaN, bIsQuietNaN, bIsSignalingNaN, + aIsLargerSignificand)) { + return float128_maybe_silence_nan(b); + } else { + return float128_maybe_silence_nan(a); } } diff -Nru qemu-kvm-0.12.5+noroms/fsdev/qemu-fsdev.c qemu-kvm-0.14.1/fsdev/qemu-fsdev.c --- qemu-kvm-0.12.5+noroms/fsdev/qemu-fsdev.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/fsdev/qemu-fsdev.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,100 @@ +/* + * Virtio 9p + * + * Copyright IBM, Corp. 2010 + * + * Authors: + * Gautham R Shenoy + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ +#include +#include +#include "qemu-fsdev.h" +#include "qemu-queue.h" +#include "osdep.h" +#include "qemu-common.h" +#include "qemu-config.h" + +static QTAILQ_HEAD(FsTypeEntry_head, FsTypeListEntry) fstype_entries = + QTAILQ_HEAD_INITIALIZER(fstype_entries); + +static FsTypeTable FsTypes[] = { + { .name = "local", .ops = &local_ops}, +}; + +int qemu_fsdev_add(QemuOpts *opts) +{ + struct FsTypeListEntry *fsle; + int i; + const char *fsdev_id = qemu_opts_id(opts); + const char *fstype = qemu_opt_get(opts, "fstype"); + const char *path = qemu_opt_get(opts, "path"); + const char *sec_model = qemu_opt_get(opts, "security_model"); + + if (!fsdev_id) { + fprintf(stderr, "fsdev: No id specified\n"); + return -1; + } + + if (fstype) { + for (i = 0; i < ARRAY_SIZE(FsTypes); i++) { + if (strcmp(FsTypes[i].name, fstype) == 0) { + break; + } + } + + if (i == ARRAY_SIZE(FsTypes)) { + fprintf(stderr, "fsdev: fstype %s not found\n", fstype); + return -1; + } + } else { + fprintf(stderr, "fsdev: No fstype specified\n"); + return -1; + } + + if (!sec_model) { + fprintf(stderr, "fsdev: No security_model specified.\n"); + return -1; + } + + if (!path) { + fprintf(stderr, "fsdev: No path specified.\n"); + return -1; + } + + fsle = qemu_malloc(sizeof(*fsle)); + + fsle->fse.fsdev_id = qemu_strdup(fsdev_id); + fsle->fse.path = qemu_strdup(path); + fsle->fse.security_model = qemu_strdup(sec_model); + fsle->fse.ops = FsTypes[i].ops; + + QTAILQ_INSERT_TAIL(&fstype_entries, fsle, next); + return 0; + +} + +FsTypeEntry *get_fsdev_fsentry(char *id) +{ + if (id) { + struct FsTypeListEntry *fsle; + + QTAILQ_FOREACH(fsle, &fstype_entries, next) { + if (strcmp(fsle->fse.fsdev_id, id) == 0) { + return &fsle->fse; + } + } + } + return NULL; +} + +static void fsdev_register_config(void) +{ + qemu_add_opts(&qemu_fsdev_opts); + qemu_add_opts(&qemu_virtfs_opts); +} +machine_init(fsdev_register_config); + diff -Nru qemu-kvm-0.12.5+noroms/fsdev/qemu-fsdev.h qemu-kvm-0.14.1/fsdev/qemu-fsdev.h --- qemu-kvm-0.12.5+noroms/fsdev/qemu-fsdev.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/fsdev/qemu-fsdev.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,55 @@ +/* + * Virtio 9p + * + * Copyright IBM, Corp. 2010 + * + * Authors: + * Gautham R Shenoy + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ +#ifndef QEMU_FSDEV_H +#define QEMU_FSDEV_H +#include "qemu-option.h" +#include "hw/file-op-9p.h" + + +/* + * A table to store the various file systems and their callback operations. + * ----------------- + * fstype | ops + * ----------------- + * local | local_ops + * . | + * . | + * . | + * . | + * ----------------- + * etc + */ +typedef struct FsTypeTable { + const char *name; + FileOperations *ops; +} FsTypeTable; + +/* + * Structure to store the various fsdev's passed through command line. + */ +typedef struct FsTypeEntry { + char *fsdev_id; + char *path; + char *security_model; + FileOperations *ops; +} FsTypeEntry; + +typedef struct FsTypeListEntry { + FsTypeEntry fse; + QTAILQ_ENTRY(FsTypeListEntry) next; +} FsTypeListEntry; + +int qemu_fsdev_add(QemuOpts *opts); +FsTypeEntry *get_fsdev_fsentry(char *id); +extern FileOperations local_ops; +#endif diff -Nru qemu-kvm-0.12.5+noroms/gdbstub.c qemu-kvm-0.14.1/gdbstub.c --- qemu-kvm-0.12.5+noroms/gdbstub.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/gdbstub.c 2011-05-11 13:29:46.000000000 +0000 @@ -38,6 +38,7 @@ #define MAX_PACKET_LENGTH 4096 +#include "exec-all.h" #include "qemu_socket.h" #include "kvm.h" @@ -805,7 +806,7 @@ /* Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR */ switch (n) { case 64: GET_REGA(env->y); - case 65: GET_REGA(GET_PSR(env)); + case 65: GET_REGA(cpu_get_psr(env)); case 66: GET_REGA(env->wim); case 67: GET_REGA(env->tbr); case 68: GET_REGA(env->pc); @@ -830,10 +831,10 @@ switch (n) { case 80: GET_REGL(env->pc); case 81: GET_REGL(env->npc); - case 82: GET_REGL(((uint64_t)GET_CCR(env) << 32) | - ((env->asi & 0xff) << 24) | - ((env->pstate & 0xfff) << 8) | - GET_CWP64(env)); + case 82: GET_REGL((cpu_get_ccr(env) << 32) | + ((env->asi & 0xff) << 24) | + ((env->pstate & 0xfff) << 8) | + cpu_get_cwp64(env)); case 83: GET_REGL(env->fsr); case 84: GET_REGL(env->fprs); case 85: GET_REGL(env->y); @@ -869,7 +870,7 @@ /* Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR */ switch (n) { case 64: env->y = tmp; break; - case 65: PUT_PSR(env, tmp); break; + case 65: cpu_put_psr(env, tmp); break; case 66: env->wim = tmp; break; case 67: env->tbr = tmp; break; case 68: env->pc = tmp; break; @@ -893,10 +894,10 @@ case 80: env->pc = tmp; break; case 81: env->npc = tmp; break; case 82: - PUT_CCR(env, tmp >> 32); + cpu_put_ccr(env, tmp >> 32); env->asi = (tmp >> 24) & 0xff; env->pstate = (tmp >> 8) & 0xfff; - PUT_CWP64(env, tmp & 0xff); + cpu_put_cwp64(env, tmp & 0xff); break; case 83: env->fsr = tmp; break; case 84: env->fprs = tmp; break; @@ -1015,7 +1016,7 @@ if (n < 8) { /* D0-D7 */ env->dregs[n] = tmp; - } else if (n < 8) { + } else if (n < 16) { /* A0-A7 */ env->aregs[n - 8] = tmp; } else { @@ -1054,7 +1055,7 @@ case 34: GET_REGL(env->active_tc.HI[0]); case 35: GET_REGL(env->CP0_BadVAddr); case 36: GET_REGL((int32_t)env->CP0_Cause); - case 37: GET_REGL(env->active_tc.PC); + case 37: GET_REGL(env->active_tc.PC | !!(env->hflags & MIPS_HFLAG_M16)); case 72: GET_REGL(0); /* fp */ case 89: GET_REGL((int32_t)env->CP0_PRid); } @@ -1115,7 +1116,14 @@ case 34: env->active_tc.HI[0] = tmp; break; case 35: env->CP0_BadVAddr = tmp; break; case 36: env->CP0_Cause = tmp; break; - case 37: env->active_tc.PC = tmp; break; + case 37: + env->active_tc.PC = tmp & ~(target_ulong)1; + if (tmp & 1) { + env->hflags |= MIPS_HFLAG_M16; + } else { + env->hflags &= ~(MIPS_HFLAG_M16); + } + break; case 72: /* fp, ignored */ break; default: if (n > 89) @@ -1142,7 +1150,7 @@ GET_REGL(env->gregs[n]); } } else if (n < 16) { - GET_REGL(env->gregs[n - 8]); + GET_REGL(env->gregs[n]); } else if (n >= 25 && n < 41) { GET_REGL(env->fregs[(n - 25) + ((env->fpscr & FPSCR_FR) ? 16 : 0)]); } else if (n >= 43 && n < 51) { @@ -1181,10 +1189,11 @@ } return 4; } else if (n < 16) { - env->gregs[n - 8] = tmp; + env->gregs[n] = tmp; return 4; } else if (n >= 25 && n < 41) { env->fregs[(n - 25) + ((env->fpscr & FPSCR_FR) ? 16 : 0)] = tmp; + return 4; } else if (n >= 43 && n < 51) { env->gregs[n - 43] = tmp; return 4; @@ -1193,17 +1202,17 @@ return 4; } switch (n) { - case 16: env->pc = tmp; - case 17: env->pr = tmp; - case 18: env->gbr = tmp; - case 19: env->vbr = tmp; - case 20: env->mach = tmp; - case 21: env->macl = tmp; - case 22: env->sr = tmp; - case 23: env->fpul = tmp; - case 24: env->fpscr = tmp; - case 41: env->ssr = tmp; - case 42: env->spc = tmp; + case 16: env->pc = tmp; break; + case 17: env->pr = tmp; break; + case 18: env->gbr = tmp; break; + case 19: env->vbr = tmp; break; + case 20: env->mach = tmp; break; + case 21: env->macl = tmp; break; + case 22: env->sr = tmp; break; + case 23: env->fpul = tmp; break; + case 24: env->fpscr = tmp; break; + case 41: env->ssr = tmp; break; + case 42: env->spc = tmp; break; default: return 0; } @@ -1243,10 +1252,46 @@ #define NUM_CORE_REGS 49 +static int +read_register_crisv10(CPUState *env, uint8_t *mem_buf, int n) +{ + if (n < 15) { + GET_REG32(env->regs[n]); + } + + if (n == 15) { + GET_REG32(env->pc); + } + + if (n < 32) { + switch (n) { + case 16: + GET_REG8(env->pregs[n - 16]); + break; + case 17: + GET_REG8(env->pregs[n - 16]); + break; + case 20: + case 21: + GET_REG16(env->pregs[n - 16]); + break; + default: + if (n >= 23) { + GET_REG32(env->pregs[n - 16]); + } + break; + } + } + return 0; +} + static int cpu_gdb_read_register(CPUState *env, uint8_t *mem_buf, int n) { uint8_t srs; + if (env->pregs[PR_VR] < 32) + return read_register_crisv10(env, mem_buf, n); + srs = env->pregs[PR_SRS]; if (n < 16) { GET_REG32(env->regs[n]); @@ -1301,52 +1346,72 @@ } #elif defined (TARGET_ALPHA) -#define NUM_CORE_REGS 65 +#define NUM_CORE_REGS 67 static int cpu_gdb_read_register(CPUState *env, uint8_t *mem_buf, int n) { - if (n < 31) { - GET_REGL(env->ir[n]); - } - else if (n == 31) { - GET_REGL(0); - } - else if (n<63) { - uint64_t val; + uint64_t val; + CPU_DoubleU d; - val = *((uint64_t *)&env->fir[n-32]); - GET_REGL(val); - } - else if (n==63) { - GET_REGL(env->fpcr); - } - else if (n==64) { - GET_REGL(env->pc); - } - else { - GET_REGL(0); + switch (n) { + case 0 ... 30: + val = env->ir[n]; + break; + case 32 ... 62: + d.d = env->fir[n - 32]; + val = d.ll; + break; + case 63: + val = cpu_alpha_load_fpcr(env); + break; + case 64: + val = env->pc; + break; + case 66: + val = env->unique; + break; + case 31: + case 65: + /* 31 really is the zero register; 65 is unassigned in the + gdb protocol, but is still required to occupy 8 bytes. */ + val = 0; + break; + default: + return 0; } - - return 0; + GET_REGL(val); } static int cpu_gdb_write_register(CPUState *env, uint8_t *mem_buf, int n) { - target_ulong tmp; - tmp = ldtul_p(mem_buf); + target_ulong tmp = ldtul_p(mem_buf); + CPU_DoubleU d; - if (n < 31) { + switch (n) { + case 0 ... 30: env->ir[n] = tmp; + break; + case 32 ... 62: + d.ll = tmp; + env->fir[n - 32] = d.d; + break; + case 63: + cpu_alpha_store_fpcr(env, tmp); + break; + case 64: + env->pc = tmp; + break; + case 66: + env->unique = tmp; + break; + case 31: + case 65: + /* 31 really is the zero register; 65 is unassigned in the + gdb protocol, but is still required to occupy 8 bytes. */ + break; + default: + return 0; } - - if (n > 31 && n < 63) { - env->fir[n - 32] = ldfl_p(mem_buf); - } - - if (n == 64 ) { - env->pc=tmp; - } - return 8; } #elif defined (TARGET_S390X) @@ -1440,7 +1505,6 @@ static const char *get_feature_xml(const char *p, const char **newp) { - extern const char *const xml_builtin[][2]; size_t len; int i; const char *name; @@ -1659,7 +1723,12 @@ #elif defined (TARGET_SH4) s->c_cpu->pc = pc; #elif defined (TARGET_MIPS) - s->c_cpu->active_tc.PC = pc; + s->c_cpu->active_tc.PC = pc & ~(target_ulong)1; + if (pc & 1) { + s->c_cpu->hflags |= MIPS_HFLAG_M16; + } else { + s->c_cpu->hflags &= ~(MIPS_HFLAG_M16); + } #elif defined (TARGET_MICROBLAZE) s->c_cpu->sregs[SR_PC] = pc; #elif defined (TARGET_CRIS) @@ -1801,6 +1870,7 @@ case 'D': /* Detach packet */ gdb_breakpoint_remove_all(); + gdb_syscall_mode = GDB_SYS_DISABLED; gdb_continue(s); put_packet(s, "OK"); break; @@ -2304,6 +2374,32 @@ } } +/* Tell the remote gdb that the process has exited. */ +void gdb_exit(CPUState *env, int code) +{ + GDBState *s; + char buf[4]; + + s = gdbserver_state; + if (!s) { + return; + } +#ifdef CONFIG_USER_ONLY + if (gdbserver_fd < 0 || s->fd < 0) { + return; + } +#endif + + snprintf(buf, sizeof(buf), "W%02x", (uint8_t)code); + put_packet(s, buf); + +#ifndef CONFIG_USER_ONLY + if (s->chr) { + qemu_chr_close(s->chr); + } +#endif +} + #ifdef CONFIG_USER_ONLY int gdb_queuesig (void) @@ -2367,20 +2463,6 @@ return sig; } -/* Tell the remote gdb that the process has exited. */ -void gdb_exit(CPUState *env, int code) -{ - GDBState *s; - char buf[4]; - - s = gdbserver_state; - if (gdbserver_fd < 0 || s->fd < 0) - return; - - snprintf(buf, sizeof(buf), "W%02x", code); - put_packet(s, buf); -} - /* Tell the remote gdb that the process has exited due to SIG. */ void gdb_signalled(CPUState *env, int sig) { diff -Nru qemu-kvm-0.12.5+noroms/gdbstub.h qemu-kvm-0.14.1/gdbstub.h --- qemu-kvm-0.12.5+noroms/gdbstub.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/gdbstub.h 2011-05-11 13:29:46.000000000 +0000 @@ -10,21 +10,19 @@ #define GDB_WATCHPOINT_READ 3 #define GDB_WATCHPOINT_ACCESS 4 +#ifdef NEED_CPU_H typedef void (*gdb_syscall_complete_cb)(CPUState *env, target_ulong ret, target_ulong err); void gdb_do_syscall(gdb_syscall_complete_cb cb, const char *fmt, ...); int use_gdb_syscalls(void); void gdb_set_stop_cpu(CPUState *env); +void gdb_exit(CPUState *, int); #ifdef CONFIG_USER_ONLY int gdb_queuesig (void); int gdb_handlesig (CPUState *, int); -void gdb_exit(CPUState *, int); void gdb_signalled(CPUState *, int); -int gdbserver_start(int); void gdbserver_fork(CPUState *); -#else -int gdbserver_start(const char *port); #endif /* Get or set a register. Returns the size of the register. */ typedef int (*gdb_reg_cb)(CPUState *env, uint8_t *buf, int reg); @@ -33,3 +31,14 @@ int num_regs, const char *xml, int g_pos); #endif + +#ifdef CONFIG_USER_ONLY +int gdbserver_start(int); +#else +int gdbserver_start(const char *port); +#endif + +/* in gdbstub-xml.c, generated by scripts/feature_to_c.sh */ +extern const char *const xml_builtin[][2]; + +#endif diff -Nru qemu-kvm-0.12.5+noroms/gen-icount.h qemu-kvm-0.14.1/gen-icount.h --- qemu-kvm-0.12.5+noroms/gen-icount.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/gen-icount.h 2011-05-11 13:29:46.000000000 +0000 @@ -1,3 +1,5 @@ +#include "qemu-timer.h" + /* Helpers for instruction counting code generation. */ static TCGArg *icount_arg; diff -Nru qemu-kvm-0.12.5+noroms/.gitignore qemu-kvm-0.14.1/.gitignore --- qemu-kvm-0.12.5+noroms/.gitignore 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/.gitignore 2011-05-11 13:29:46.000000000 +0000 @@ -2,11 +2,16 @@ config-all-devices.* config-host.* config-target.* -i386 +trace.h +trace.c +trace-dtrace.h +trace-dtrace.dtrace +*-timestamp *-softmmu *-darwin-user *-linux-user *-bsd-user +libdis* libhw32 libhw64 libuser @@ -22,11 +27,13 @@ qemu-nbd qemu-nbd.8 qemu-nbd.pod +qemu-options.def qemu-options.texi qemu-img-cmds.texi qemu-img-cmds.h qemu-io qemu-monitor.texi +QMP/qmp-commands.txt .gdbinit *.a *.aux @@ -36,7 +43,9 @@ *.fn *.ky *.log +*.pdf *.pg +*.pyc *.toc *.tp *.vr @@ -46,7 +55,10 @@ patches pc-bios/bios-pq/status pc-bios/vgabios-pq/status +pc-bios/optionrom/linuxboot.bin pc-bios/optionrom/multiboot.bin pc-bios/optionrom/multiboot.raw pc-bios/optionrom/extboot.bin +pc-bios/optionrom/vapic.bin .stgit-* +cscope.* diff -Nru qemu-kvm-0.12.5+noroms/.gitmodules qemu-kvm-0.14.1/.gitmodules --- qemu-kvm-0.12.5+noroms/.gitmodules 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/.gitmodules 2011-05-11 13:29:46.000000000 +0000 @@ -1,6 +1,6 @@ [submodule "roms/vgabios"] path = roms/vgabios - url = ../vgabios.git + url = git://git.kernel.org/pub/scm/virt/kvm/vgabios.git/ [submodule "roms/seabios"] path = roms/seabios - url = ../seabios.git + url = git://git.qemu.org/seabios.git/ diff -Nru qemu-kvm-0.12.5+noroms/HACKING qemu-kvm-0.14.1/HACKING --- qemu-kvm-0.12.5+noroms/HACKING 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/HACKING 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,125 @@ +1. Preprocessor + +For variadic macros, stick with this C99-like syntax: + +#define DPRINTF(fmt, ...) \ + do { printf("IRQ: " fmt, ## __VA_ARGS__); } while (0) + +2. C types + +It should be common sense to use the right type, but we have collected +a few useful guidelines here. + +2.1. Scalars + +If you're using "int" or "long", odds are good that there's a better type. +If a variable is counting something, it should be declared with an +unsigned type. + +If it's host memory-size related, size_t should be a good choice (use +ssize_t only if required). Guest RAM memory offsets must use ram_addr_t, +but only for RAM, it may not cover whole guest address space. + +If it's file-size related, use off_t. +If it's file-offset related (i.e., signed), use off_t. +If it's just counting small numbers use "unsigned int"; +(on all but oddball embedded systems, you can assume that that +type is at least four bytes wide). + +In the event that you require a specific width, use a standard type +like int32_t, uint32_t, uint64_t, etc. The specific types are +mandatory for VMState fields. + +Don't use Linux kernel internal types like u32, __u32 or __le32. + +Use target_phys_addr_t for guest physical addresses except pcibus_t +for PCI addresses. In addition, ram_addr_t is a QEMU internal address +space that maps guest RAM physical addresses into an intermediate +address space that can map to host virtual address spaces. Generally +speaking, the size of guest memory can always fit into ram_addr_t but +it would not be correct to store an actual guest physical address in a +ram_addr_t. + +Use target_ulong (or abi_ulong) for CPU virtual addresses, however +devices should not need to use target_ulong. + +Of course, take all of the above with a grain of salt. If you're about +to use some system interface that requires a type like size_t, pid_t or +off_t, use matching types for any corresponding variables. + +Also, if you try to use e.g., "unsigned int" as a type, and that +conflicts with the signedness of a related variable, sometimes +it's best just to use the *wrong* type, if "pulling the thread" +and fixing all related variables would be too invasive. + +Finally, while using descriptive types is important, be careful not to +go overboard. If whatever you're doing causes warnings, or requires +casts, then reconsider or ask for help. + +2.2. Pointers + +Ensure that all of your pointers are "const-correct". +Unless a pointer is used to modify the pointed-to storage, +give it the "const" attribute. That way, the reader knows +up-front that this is a read-only pointer. Perhaps more +importantly, if we're diligent about this, when you see a non-const +pointer, you're guaranteed that it is used to modify the storage +it points to, or it is aliased to another pointer that is. + +2.3. Typedefs +Typedefs are used to eliminate the redundant 'struct' keyword. + +2.4. Reserved namespaces in C and POSIX +Underscore capital, double underscore, and underscore 't' suffixes should be +avoided. + +3. Low level memory management + +Use of the malloc/free/realloc/calloc/valloc/memalign/posix_memalign +APIs is not allowed in the QEMU codebase. Instead of these routines, +use the replacement qemu_malloc/qemu_mallocz/qemu_realloc/qemu_free or +qemu_vmalloc/qemu_memalign/qemu_vfree APIs. + +Please note that NULL check for the qemu_malloc result is redundant and +that qemu_malloc() call with zero size is not allowed. + +Memory allocated by qemu_vmalloc or qemu_memalign must be freed with +qemu_vfree, since breaking this will cause problems on Win32 and user +emulators. + +4. String manipulation + +Do not use the strncpy function. According to the man page, it does +*not* guarantee a NULL-terminated buffer, which makes it extremely dangerous +to use. Instead, use functionally equivalent function: +void pstrcpy(char *buf, int buf_size, const char *str) + +Don't use strcat because it can't check for buffer overflows, but: +char *pstrcat(char *buf, int buf_size, const char *s) + +The same limitation exists with sprintf and vsprintf, so use snprintf and +vsnprintf. + +QEMU provides other useful string functions: +int strstart(const char *str, const char *val, const char **ptr) +int stristart(const char *str, const char *val, const char **ptr) +int qemu_strnlen(const char *s, int max_len) + +There are also replacement character processing macros for isxyz and toxyz, +so instead of e.g. isalnum you should use qemu_isalnum. + +Because of the memory management rules, you must use qemu_strdup/qemu_strndup +instead of plain strdup/strndup. + +5. Printf-style functions + +Whenever you add a new printf-style function, i.e., one with a format +string argument and following "..." in its prototype, be sure to use +gcc's printf attribute directive in the prototype. + +This makes it so gcc's -Wformat and -Wformat-security options can do +their jobs and cross-check format strings with the number and types +of arguments. + +Currently many functions in QEMU are not following this rule but +patches to add the attribute would be very much appreciated. diff -Nru qemu-kvm-0.12.5+noroms/hmp-commands.hx qemu-kvm-0.14.1/hmp-commands.hx --- qemu-kvm-0.12.5+noroms/hmp-commands.hx 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hmp-commands.hx 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,1382 @@ +HXCOMM Use DEFHEADING() to define headings in both help text and texi +HXCOMM Text between STEXI and ETEXI are copied to texi version and +HXCOMM discarded from C version +HXCOMM DEF(command, args, callback, arg_string, help) is used to construct +HXCOMM monitor commands +HXCOMM HXCOMM can be used for comments, discarded from both texi and C + +STEXI +@table @option +ETEXI + + { + .name = "help|?", + .args_type = "name:s?", + .params = "[cmd]", + .help = "show the help", + .mhandler.cmd = do_help_cmd, + }, + +STEXI +@item help or ? [@var{cmd}] +@findex help +Show the help for all commands or just for command @var{cmd}. +ETEXI + + { + .name = "commit", + .args_type = "device:B", + .params = "device|all", + .help = "commit changes to the disk images (if -snapshot is used) or backing files", + .mhandler.cmd = do_commit, + }, + +STEXI +@item commit +@findex commit +Commit changes to the disk images (if -snapshot is used) or backing files. +ETEXI + + { + .name = "q|quit", + .args_type = "", + .params = "", + .help = "quit the emulator", + .user_print = monitor_user_noop, + .mhandler.cmd_new = do_quit, + }, + +STEXI +@item q or quit +@findex quit +Quit the emulator. +ETEXI + + { + .name = "block_resize", + .args_type = "device:B,size:o", + .params = "device size", + .help = "resize a block image", + .user_print = monitor_user_noop, + .mhandler.cmd_new = do_block_resize, + }, + +STEXI +@item block_resize +@findex block_resize +Resize a block image while a guest is running. Usually requires guest +action to see the updated size. Resize to a lower size is supported, +but should be used with extreme caution. Note that this command only +resizes image files, it can not resize block devices like LVM volumes. +ETEXI + + + { + .name = "eject", + .args_type = "force:-f,device:B", + .params = "[-f] device", + .help = "eject a removable medium (use -f to force it)", + .user_print = monitor_user_noop, + .mhandler.cmd_new = do_eject, + }, + +STEXI +@item eject [-f] @var{device} +@findex eject +Eject a removable medium (use -f to force it). +ETEXI + + { + .name = "drive_del", + .args_type = "id:s", + .params = "device", + .help = "remove host block device", + .user_print = monitor_user_noop, + .mhandler.cmd_new = do_drive_del, + }, + +STEXI +@item drive_del @var{device} +@findex drive_del +Remove host block device. The result is that guest generated IO is no longer +submitted against the host device underlying the disk. Once a drive has +been deleted, the QEMU Block layer returns -EIO which results in IO +errors in the guest for applications that are reading/writing to the device. +ETEXI + + { + .name = "change", + .args_type = "device:B,target:F,arg:s?", + .params = "device filename [format]", + .help = "change a removable medium, optional format", + .user_print = monitor_user_noop, + .mhandler.cmd_new = do_change, + }, + +STEXI +@item change @var{device} @var{setting} +@findex change + +Change the configuration of a device. + +@table @option +@item change @var{diskdevice} @var{filename} [@var{format}] +Change the medium for a removable disk device to point to @var{filename}. eg + +@example +(qemu) change ide1-cd0 /path/to/some.iso +@end example + +@var{format} is optional. + +@item change vnc @var{display},@var{options} +Change the configuration of the VNC server. The valid syntax for @var{display} +and @var{options} are described at @ref{sec_invocation}. eg + +@example +(qemu) change vnc localhost:1 +@end example + +@item change vnc password [@var{password}] + +Change the password associated with the VNC server. If the new password is not +supplied, the monitor will prompt for it to be entered. VNC passwords are only +significant up to 8 letters. eg + +@example +(qemu) change vnc password +Password: ******** +@end example + +@end table +ETEXI + + { + .name = "screendump", + .args_type = "filename:F", + .params = "filename", + .help = "save screen into PPM image 'filename'", + .user_print = monitor_user_noop, + .mhandler.cmd_new = do_screen_dump, + }, + +STEXI +@item screendump @var{filename} +@findex screendump +Save screen into PPM image @var{filename}. +ETEXI + + { + .name = "logfile", + .args_type = "filename:F", + .params = "filename", + .help = "output logs to 'filename'", + .mhandler.cmd = do_logfile, + }, + +STEXI +@item logfile @var{filename} +@findex logfile +Output logs to @var{filename}. +ETEXI + +#ifdef CONFIG_SIMPLE_TRACE + { + .name = "trace-event", + .args_type = "name:s,option:b", + .params = "name on|off", + .help = "changes status of a specific trace event", + .mhandler.cmd = do_change_trace_event_state, + }, + +STEXI +@item trace-event +@findex trace-event +changes status of a trace event +ETEXI + + { + .name = "trace-file", + .args_type = "op:s?,arg:F?", + .params = "on|off|flush|set [arg]", + .help = "open, close, or flush trace file, or set a new file name", + .mhandler.cmd = do_trace_file, + }, + +STEXI +@item trace-file on|off|flush +@findex trace-file +Open, close, or flush the trace file. If no argument is given, the status of the trace file is displayed. +ETEXI +#endif + + { + .name = "log", + .args_type = "items:s", + .params = "item1[,...]", + .help = "activate logging of the specified items to '/tmp/qemu.log'", + .mhandler.cmd = do_log, + }, + +STEXI +@item log @var{item1}[,...] +@findex log +Activate logging of the specified items to @file{/tmp/qemu.log}. +ETEXI + + { + .name = "savevm", + .args_type = "name:s?", + .params = "[tag|id]", + .help = "save a VM snapshot. If no tag or id are provided, a new snapshot is created", + .mhandler.cmd = do_savevm, + }, + +STEXI +@item savevm [@var{tag}|@var{id}] +@findex savevm +Create a snapshot of the whole virtual machine. If @var{tag} is +provided, it is used as human readable identifier. If there is already +a snapshot with the same tag or ID, it is replaced. More info at +@ref{vm_snapshots}. +ETEXI + + { + .name = "loadvm", + .args_type = "name:s", + .params = "tag|id", + .help = "restore a VM snapshot from its tag or id", + .mhandler.cmd = do_loadvm, + }, + +STEXI +@item loadvm @var{tag}|@var{id} +@findex loadvm +Set the whole virtual machine to the snapshot identified by the tag +@var{tag} or the unique snapshot ID @var{id}. +ETEXI + + { + .name = "delvm", + .args_type = "name:s", + .params = "tag|id", + .help = "delete a VM snapshot from its tag or id", + .mhandler.cmd = do_delvm, + }, + +STEXI +@item delvm @var{tag}|@var{id} +@findex delvm +Delete the snapshot identified by @var{tag} or @var{id}. +ETEXI + + { + .name = "singlestep", + .args_type = "option:s?", + .params = "[on|off]", + .help = "run emulation in singlestep mode or switch to normal mode", + .mhandler.cmd = do_singlestep, + }, + +STEXI +@item singlestep [off] +@findex singlestep +Run the emulation in single step mode. +If called with option off, the emulation returns to normal mode. +ETEXI + + { + .name = "stop", + .args_type = "", + .params = "", + .help = "stop emulation", + .user_print = monitor_user_noop, + .mhandler.cmd_new = do_stop, + }, + +STEXI +@item stop +@findex stop +Stop emulation. +ETEXI + + { + .name = "c|cont", + .args_type = "", + .params = "", + .help = "resume emulation", + .user_print = monitor_user_noop, + .mhandler.cmd_new = do_cont, + }, + +STEXI +@item c or cont +@findex cont +Resume emulation. +ETEXI + + { + .name = "gdbserver", + .args_type = "device:s?", + .params = "[device]", + .help = "start gdbserver on given device (default 'tcp::1234'), stop with 'none'", + .mhandler.cmd = do_gdbserver, + }, + +STEXI +@item gdbserver [@var{port}] +@findex gdbserver +Start gdbserver session (default @var{port}=1234) +ETEXI + + { + .name = "x", + .args_type = "fmt:/,addr:l", + .params = "/fmt addr", + .help = "virtual memory dump starting at 'addr'", + .mhandler.cmd = do_memory_dump, + }, + +STEXI +@item x/fmt @var{addr} +@findex x +Virtual memory dump starting at @var{addr}. +ETEXI + + { + .name = "xp", + .args_type = "fmt:/,addr:l", + .params = "/fmt addr", + .help = "physical memory dump starting at 'addr'", + .mhandler.cmd = do_physical_memory_dump, + }, + +STEXI +@item xp /@var{fmt} @var{addr} +@findex xp +Physical memory dump starting at @var{addr}. + +@var{fmt} is a format which tells the command how to format the +data. Its syntax is: @option{/@{count@}@{format@}@{size@}} + +@table @var +@item count +is the number of items to be dumped. + +@item format +can be x (hex), d (signed decimal), u (unsigned decimal), o (octal), +c (char) or i (asm instruction). + +@item size +can be b (8 bits), h (16 bits), w (32 bits) or g (64 bits). On x86, +@code{h} or @code{w} can be specified with the @code{i} format to +respectively select 16 or 32 bit code instruction size. + +@end table + +Examples: +@itemize +@item +Dump 10 instructions at the current instruction pointer: +@example +(qemu) x/10i $eip +0x90107063: ret +0x90107064: sti +0x90107065: lea 0x0(%esi,1),%esi +0x90107069: lea 0x0(%edi,1),%edi +0x90107070: ret +0x90107071: jmp 0x90107080 +0x90107073: nop +0x90107074: nop +0x90107075: nop +0x90107076: nop +@end example + +@item +Dump 80 16 bit values at the start of the video memory. +@smallexample +(qemu) xp/80hx 0xb8000 +0x000b8000: 0x0b50 0x0b6c 0x0b65 0x0b78 0x0b38 0x0b36 0x0b2f 0x0b42 +0x000b8010: 0x0b6f 0x0b63 0x0b68 0x0b73 0x0b20 0x0b56 0x0b47 0x0b41 +0x000b8020: 0x0b42 0x0b69 0x0b6f 0x0b73 0x0b20 0x0b63 0x0b75 0x0b72 +0x000b8030: 0x0b72 0x0b65 0x0b6e 0x0b74 0x0b2d 0x0b63 0x0b76 0x0b73 +0x000b8040: 0x0b20 0x0b30 0x0b35 0x0b20 0x0b4e 0x0b6f 0x0b76 0x0b20 +0x000b8050: 0x0b32 0x0b30 0x0b30 0x0b33 0x0720 0x0720 0x0720 0x0720 +0x000b8060: 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 +0x000b8070: 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 +0x000b8080: 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 +0x000b8090: 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 +@end smallexample +@end itemize +ETEXI + + { + .name = "p|print", + .args_type = "fmt:/,val:l", + .params = "/fmt expr", + .help = "print expression value (use $reg for CPU register access)", + .mhandler.cmd = do_print, + }, + +STEXI +@item p or print/@var{fmt} @var{expr} +@findex print + +Print expression value. Only the @var{format} part of @var{fmt} is +used. +ETEXI + + { + .name = "i", + .args_type = "fmt:/,addr:i,index:i.", + .params = "/fmt addr", + .help = "I/O port read", + .mhandler.cmd = do_ioport_read, + }, + +STEXI +Read I/O port. +ETEXI + + { + .name = "o", + .args_type = "fmt:/,addr:i,val:i", + .params = "/fmt addr value", + .help = "I/O port write", + .mhandler.cmd = do_ioport_write, + }, + +STEXI +Write to I/O port. +ETEXI + + { + .name = "sendkey", + .args_type = "string:s,hold_time:i?", + .params = "keys [hold_ms]", + .help = "send keys to the VM (e.g. 'sendkey ctrl-alt-f1', default hold time=100 ms)", + .mhandler.cmd = do_sendkey, + }, + +STEXI +@item sendkey @var{keys} +@findex sendkey + +Send @var{keys} to the emulator. @var{keys} could be the name of the +key or @code{#} followed by the raw value in either decimal or hexadecimal +format. Use @code{-} to press several keys simultaneously. Example: +@example +sendkey ctrl-alt-f1 +@end example + +This command is useful to send keys that your graphical user interface +intercepts at low level, such as @code{ctrl-alt-f1} in X Window. +ETEXI + + { + .name = "system_reset", + .args_type = "", + .params = "", + .help = "reset the system", + .user_print = monitor_user_noop, + .mhandler.cmd_new = do_system_reset, + }, + +STEXI +@item system_reset +@findex system_reset + +Reset the system. +ETEXI + + { + .name = "system_powerdown", + .args_type = "", + .params = "", + .help = "send system power down event", + .user_print = monitor_user_noop, + .mhandler.cmd_new = do_system_powerdown, + }, + +STEXI +@item system_powerdown +@findex system_powerdown + +Power down the system (if supported). +ETEXI + + { + .name = "sum", + .args_type = "start:i,size:i", + .params = "addr size", + .help = "compute the checksum of a memory region", + .mhandler.cmd = do_sum, + }, + +STEXI +@item sum @var{addr} @var{size} +@findex sum + +Compute the checksum of a memory region. +ETEXI + + { + .name = "usb_add", + .args_type = "devname:s", + .params = "device", + .help = "add USB device (e.g. 'host:bus.addr' or 'host:vendor_id:product_id')", + .mhandler.cmd = do_usb_add, + }, + +STEXI +@item usb_add @var{devname} +@findex usb_add + +Add the USB device @var{devname}. For details of available devices see +@ref{usb_devices} +ETEXI + + { + .name = "usb_del", + .args_type = "devname:s", + .params = "device", + .help = "remove USB device 'bus.addr'", + .mhandler.cmd = do_usb_del, + }, + +STEXI +@item usb_del @var{devname} +@findex usb_del + +Remove the USB device @var{devname} from the QEMU virtual USB +hub. @var{devname} has the syntax @code{bus.addr}. Use the monitor +command @code{info usb} to see the devices you can remove. +ETEXI + + { + .name = "device_add", + .args_type = "device:O", + .params = "driver[,prop=value][,...]", + .help = "add device, like -device on the command line", + .user_print = monitor_user_noop, + .mhandler.cmd_new = do_device_add, + }, + +STEXI +@item device_add @var{config} +@findex device_add + +Add device. +ETEXI + + { + .name = "device_del", + .args_type = "id:s", + .params = "device", + .help = "remove device", + .user_print = monitor_user_noop, + .mhandler.cmd_new = do_device_del, + }, + +STEXI +@item device_del @var{id} +@findex device_del + +Remove device @var{id}. +ETEXI + + { + .name = "cpu", + .args_type = "index:i", + .params = "index", + .help = "set the default CPU", + .user_print = monitor_user_noop, + .mhandler.cmd_new = do_cpu_set, + }, + +STEXI +@item cpu @var{index} +@findex cpu +Set the default CPU. +ETEXI + + { + .name = "mouse_move", + .args_type = "dx_str:s,dy_str:s,dz_str:s?", + .params = "dx dy [dz]", + .help = "send mouse move events", + .mhandler.cmd = do_mouse_move, + }, + +STEXI +@item mouse_move @var{dx} @var{dy} [@var{dz}] +@findex mouse_move +Move the active mouse to the specified coordinates @var{dx} @var{dy} +with optional scroll axis @var{dz}. +ETEXI + + { + .name = "mouse_button", + .args_type = "button_state:i", + .params = "state", + .help = "change mouse button state (1=L, 2=M, 4=R)", + .mhandler.cmd = do_mouse_button, + }, + +STEXI +@item mouse_button @var{val} +@findex mouse_button +Change the active mouse button state @var{val} (1=L, 2=M, 4=R). +ETEXI + + { + .name = "mouse_set", + .args_type = "index:i", + .params = "index", + .help = "set which mouse device receives events", + .mhandler.cmd = do_mouse_set, + }, + +STEXI +@item mouse_set @var{index} +@findex mouse_set +Set which mouse device receives events at given @var{index}, index +can be obtained with +@example +info mice +@end example +ETEXI + +#ifdef HAS_AUDIO + { + .name = "wavcapture", + .args_type = "path:F,freq:i?,bits:i?,nchannels:i?", + .params = "path [frequency [bits [channels]]]", + .help = "capture audio to a wave file (default frequency=44100 bits=16 channels=2)", + .mhandler.cmd = do_wav_capture, + }, +#endif +STEXI +@item wavcapture @var{filename} [@var{frequency} [@var{bits} [@var{channels}]]] +@findex wavcapture +Capture audio into @var{filename}. Using sample rate @var{frequency} +bits per sample @var{bits} and number of channels @var{channels}. + +Defaults: +@itemize @minus +@item Sample rate = 44100 Hz - CD quality +@item Bits = 16 +@item Number of channels = 2 - Stereo +@end itemize +ETEXI + +#ifdef HAS_AUDIO + { + .name = "stopcapture", + .args_type = "n:i", + .params = "capture index", + .help = "stop capture", + .mhandler.cmd = do_stop_capture, + }, +#endif +STEXI +@item stopcapture @var{index} +@findex stopcapture +Stop capture with a given @var{index}, index can be obtained with +@example +info capture +@end example +ETEXI + + { + .name = "memsave", + .args_type = "val:l,size:i,filename:s", + .params = "addr size file", + .help = "save to disk virtual memory dump starting at 'addr' of size 'size'", + .user_print = monitor_user_noop, + .mhandler.cmd_new = do_memory_save, + }, + +STEXI +@item memsave @var{addr} @var{size} @var{file} +@findex memsave +save to disk virtual memory dump starting at @var{addr} of size @var{size}. +ETEXI + + { + .name = "pmemsave", + .args_type = "val:l,size:i,filename:s", + .params = "addr size file", + .help = "save to disk physical memory dump starting at 'addr' of size 'size'", + .user_print = monitor_user_noop, + .mhandler.cmd_new = do_physical_memory_save, + }, + +STEXI +@item pmemsave @var{addr} @var{size} @var{file} +@findex pmemsave +save to disk physical memory dump starting at @var{addr} of size @var{size}. +ETEXI + + { + .name = "boot_set", + .args_type = "bootdevice:s", + .params = "bootdevice", + .help = "define new values for the boot device list", + .mhandler.cmd = do_boot_set, + }, + +STEXI +@item boot_set @var{bootdevicelist} +@findex boot_set + +Define new values for the boot device list. Those values will override +the values specified on the command line through the @code{-boot} option. + +The values that can be specified here depend on the machine type, but are +the same that can be specified in the @code{-boot} command line option. +ETEXI + +#if defined(TARGET_I386) + { + .name = "nmi", + .args_type = "cpu_index:i", + .params = "cpu", + .help = "inject an NMI on the given CPU", + .mhandler.cmd = do_inject_nmi, + }, +#endif +STEXI +@item nmi @var{cpu} +@findex nmi +Inject an NMI on the given CPU (x86 only). +ETEXI + + { + .name = "migrate", + .args_type = "detach:-d,blk:-b,inc:-i,uri:s", + .params = "[-d] [-b] [-i] uri", + .help = "migrate to URI (using -d to not wait for completion)" + "\n\t\t\t -b for migration without shared storage with" + " full copy of disk\n\t\t\t -i for migration without " + "shared storage with incremental copy of disk " + "(base image shared between src and destination)", + .user_print = monitor_user_noop, + .mhandler.cmd_new = do_migrate, + }, + + +STEXI +@item migrate [-d] [-b] [-i] @var{uri} +@findex migrate +Migrate to @var{uri} (using -d to not wait for completion). + -b for migration with full copy of disk + -i for migration with incremental copy of disk (base image is shared) +ETEXI + + { + .name = "migrate_cancel", + .args_type = "", + .params = "", + .help = "cancel the current VM migration", + .user_print = monitor_user_noop, + .mhandler.cmd_new = do_migrate_cancel, + }, + +STEXI +@item migrate_cancel +@findex migrate_cancel +Cancel the current VM migration. +ETEXI + + { + .name = "migrate_set_speed", + .args_type = "value:o", + .params = "value", + .help = "set maximum speed (in bytes) for migrations. " + "Defaults to MB if no size suffix is specified, ie. B/K/M/G/T", + .user_print = monitor_user_noop, + .mhandler.cmd_new = do_migrate_set_speed, + }, + +STEXI +@item migrate_set_speed @var{value} +@findex migrate_set_speed +Set maximum speed to @var{value} (in bytes) for migrations. +ETEXI + + { + .name = "migrate_set_downtime", + .args_type = "value:T", + .params = "value", + .help = "set maximum tolerated downtime (in seconds) for migrations", + .user_print = monitor_user_noop, + .mhandler.cmd_new = do_migrate_set_downtime, + }, + +STEXI +@item migrate_set_downtime @var{second} +@findex migrate_set_downtime +Set maximum tolerated downtime (in seconds) for migration. +ETEXI + + { + .name = "snapshot_blkdev", + .args_type = "device:B,snapshot_file:s?,format:s?", + .params = "device [new-image-file] [format]", + .help = "initiates a live snapshot\n\t\t\t" + "of device. If a new image file is specified, the\n\t\t\t" + "new image file will become the new root image.\n\t\t\t" + "If format is specified, the snapshot file will\n\t\t\t" + "be created in that format. Otherwise the\n\t\t\t" + "snapshot will be internal! (currently unsupported)", + .mhandler.cmd_new = do_snapshot_blkdev, + }, + +STEXI +@item client_migrate_info @var{protocol} @var{hostname} @var{port} @var{tls-port} @var{cert-subject} +@findex client_migrate_info +Set the spice/vnc connection info for the migration target. The spice/vnc +server will ask the spice/vnc client to automatically reconnect using the +new parameters (if specified) once the vm migration finished successfully. +ETEXI + + { + .name = "client_migrate_info", + .args_type = "protocol:s,hostname:s,port:i?,tls-port:i?,cert-subject:s?", + .params = "protocol hostname port tls-port cert-subject", + .help = "send migration info to spice/vnc client", + .user_print = monitor_user_noop, + .mhandler.cmd_new = client_migrate_info, + }, + +STEXI +@item snapshot_blkdev +@findex snapshot_blkdev +Snapshot device, using snapshot file as target if provided +ETEXI + +#if defined(TARGET_I386) + { + .name = "drive_add", + .args_type = "pci_addr:s,opts:s", + .params = "[[:]:]\n" + "[file=file][,if=type][,bus=n]\n" + "[,unit=m][,media=d][index=i]\n" + "[,cyls=c,heads=h,secs=s[,trans=t]]\n" + "[snapshot=on|off][,cache=on|off]", + .help = "add drive to PCI storage controller", + .mhandler.cmd = drive_hot_add, + }, +#endif + +STEXI +@item drive_add +@findex drive_add +Add drive to PCI storage controller. +ETEXI + +#if defined(TARGET_I386) + { + .name = "pci_add", + .args_type = "pci_addr:s,type:s,opts:s?", + .params = "auto|[[:]:] nic|storage [[vlan=n][,macaddr=addr][,model=type]] [file=file][,if=type][,bus=nr]...", + .help = "hot-add PCI device", + .mhandler.cmd = pci_device_hot_add, + }, +#endif + +STEXI +@item pci_add +@findex pci_add +Hot-add PCI device. +ETEXI + +#if defined(TARGET_I386) + { + .name = "pci_del", + .args_type = "pci_addr:s", + .params = "[[:]:]", + .help = "hot remove PCI device", + .mhandler.cmd = do_pci_device_hot_remove, + }, +#endif + +STEXI +@item pci_del +@findex pci_del +Hot remove PCI device. +ETEXI + + { + .name = "pcie_aer_inject_error", + .args_type = "advisory_non_fatal:-a,correctable:-c," + "id:s,error_status:s," + "header0:i?,header1:i?,header2:i?,header3:i?," + "prefix0:i?,prefix1:i?,prefix2:i?,prefix3:i?", + .params = "[-a] [-c] id " + " [ []]", + .help = "inject pcie aer error\n\t\t\t" + " -a for advisory non fatal error\n\t\t\t" + " -c for correctable error\n\t\t\t" + " = qdev device id\n\t\t\t" + " = error string or 32bit\n\t\t\t" + " = 32bit x 4\n\t\t\t" + " = 32bit x 4", + .user_print = pcie_aer_inject_error_print, + .mhandler.cmd_new = do_pcie_aer_inejct_error, + }, + +STEXI +@item pcie_aer_inject_error +@findex pcie_aer_inject_error +Inject PCIe AER error +ETEXI + + { + .name = "host_net_add", + .args_type = "device:s,opts:s?", + .params = "tap|user|socket|vde|dump [options]", + .help = "add host VLAN client", + .mhandler.cmd = net_host_device_add, + }, + +STEXI +@item host_net_add +@findex host_net_add +Add host VLAN client. +ETEXI + + { + .name = "host_net_remove", + .args_type = "vlan_id:i,device:s", + .params = "vlan_id name", + .help = "remove host VLAN client", + .mhandler.cmd = net_host_device_remove, + }, + +STEXI +@item host_net_remove +@findex host_net_remove +Remove host VLAN client. +ETEXI + + { + .name = "netdev_add", + .args_type = "netdev:O", + .params = "[user|tap|socket],id=str[,prop=value][,...]", + .help = "add host network device", + .user_print = monitor_user_noop, + .mhandler.cmd_new = do_netdev_add, + }, + +STEXI +@item netdev_add +@findex netdev_add +Add host network device. +ETEXI + + { + .name = "netdev_del", + .args_type = "id:s", + .params = "id", + .help = "remove host network device", + .user_print = monitor_user_noop, + .mhandler.cmd_new = do_netdev_del, + }, + +STEXI +@item netdev_del +@findex netdev_del +Remove host network device. +ETEXI + +#ifdef CONFIG_SLIRP + { + .name = "hostfwd_add", + .args_type = "arg1:s,arg2:s?,arg3:s?", + .params = "[vlan_id name] [tcp|udp]:[hostaddr]:hostport-[guestaddr]:guestport", + .help = "redirect TCP or UDP connections from host to guest (requires -net user)", + .mhandler.cmd = net_slirp_hostfwd_add, + }, +#endif +STEXI +@item hostfwd_add +@findex hostfwd_add +Redirect TCP or UDP connections from host to guest (requires -net user). +ETEXI + +#ifdef CONFIG_SLIRP + { + .name = "hostfwd_remove", + .args_type = "arg1:s,arg2:s?,arg3:s?", + .params = "[vlan_id name] [tcp|udp]:[hostaddr]:hostport", + .help = "remove host-to-guest TCP or UDP redirection", + .mhandler.cmd = net_slirp_hostfwd_remove, + }, + +#endif +STEXI +@item hostfwd_remove +@findex hostfwd_remove +Remove host-to-guest TCP or UDP redirection. +ETEXI + + { + .name = "balloon", + .args_type = "value:M", + .params = "target", + .help = "request VM to change its memory allocation (in MB)", + .user_print = monitor_user_noop, + .mhandler.cmd_async = do_balloon, + .flags = MONITOR_CMD_ASYNC, + }, + +STEXI +@item balloon @var{value} +@findex balloon +Request VM to change its memory allocation to @var{value} (in MB). +ETEXI + + { + .name = "set_link", + .args_type = "name:s,up:b", + .params = "name on|off", + .help = "change the link status of a network adapter", + .user_print = monitor_user_noop, + .mhandler.cmd_new = do_set_link, + }, + +STEXI +@item set_link @var{name} [on|off] +@findex set_link +Switch link @var{name} on (i.e. up) or off (i.e. down). +ETEXI + + { + .name = "watchdog_action", + .args_type = "action:s", + .params = "[reset|shutdown|poweroff|pause|debug|none]", + .help = "change watchdog action", + .mhandler.cmd = do_watchdog_action, + }, + +STEXI +@item watchdog_action +@findex watchdog_action +Change watchdog action. +ETEXI + + { + .name = "acl_show", + .args_type = "aclname:s", + .params = "aclname", + .help = "list rules in the access control list", + .mhandler.cmd = do_acl_show, + }, + +STEXI +@item acl_show @var{aclname} +@findex acl_show +List all the matching rules in the access control list, and the default +policy. There are currently two named access control lists, +@var{vnc.x509dname} and @var{vnc.username} matching on the x509 client +certificate distinguished name, and SASL username respectively. +ETEXI + + { + .name = "acl_policy", + .args_type = "aclname:s,policy:s", + .params = "aclname allow|deny", + .help = "set default access control list policy", + .mhandler.cmd = do_acl_policy, + }, + +STEXI +@item acl_policy @var{aclname} @code{allow|deny} +@findex acl_policy +Set the default access control list policy, used in the event that +none of the explicit rules match. The default policy at startup is +always @code{deny}. +ETEXI + + { + .name = "acl_add", + .args_type = "aclname:s,match:s,policy:s,index:i?", + .params = "aclname match allow|deny [index]", + .help = "add a match rule to the access control list", + .mhandler.cmd = do_acl_add, + }, + +STEXI +@item acl_add @var{aclname} @var{match} @code{allow|deny} [@var{index}] +@findex acl_add +Add a match rule to the access control list, allowing or denying access. +The match will normally be an exact username or x509 distinguished name, +but can optionally include wildcard globs. eg @code{*@@EXAMPLE.COM} to +allow all users in the @code{EXAMPLE.COM} kerberos realm. The match will +normally be appended to the end of the ACL, but can be inserted +earlier in the list if the optional @var{index} parameter is supplied. +ETEXI + + { + .name = "acl_remove", + .args_type = "aclname:s,match:s", + .params = "aclname match", + .help = "remove a match rule from the access control list", + .mhandler.cmd = do_acl_remove, + }, + +STEXI +@item acl_remove @var{aclname} @var{match} +@findex acl_remove +Remove the specified match rule from the access control list. +ETEXI + + { + .name = "acl_reset", + .args_type = "aclname:s", + .params = "aclname", + .help = "reset the access control list", + .mhandler.cmd = do_acl_reset, + }, + +STEXI +@item acl_reset @var{aclname} +@findex acl_reset +Remove all matches from the access control list, and set the default +policy back to @code{deny}. +ETEXI + +#if defined(TARGET_I386) + + { + .name = "mce", + .args_type = "broadcast:-b,cpu_index:i,bank:i,status:l,mcg_status:l,addr:l,misc:l", + .params = "[-b] cpu bank status mcgstatus addr misc", + .help = "inject a MCE on the given CPU [and broadcast to other CPUs with -b option]", + .mhandler.cmd = do_inject_mce, + }, + +#endif +STEXI +@item mce @var{cpu} @var{bank} @var{status} @var{mcgstatus} @var{addr} @var{misc} +@findex mce (x86) +Inject an MCE on the given CPU (x86 only). +ETEXI + + { + .name = "getfd", + .args_type = "fdname:s", + .params = "getfd name", + .help = "receive a file descriptor via SCM rights and assign it a name", + .user_print = monitor_user_noop, + .mhandler.cmd_new = do_getfd, + }, + +STEXI +@item getfd @var{fdname} +@findex getfd +If a file descriptor is passed alongside this command using the SCM_RIGHTS +mechanism on unix sockets, it is stored using the name @var{fdname} for +later use by other monitor commands. +ETEXI + + { + .name = "closefd", + .args_type = "fdname:s", + .params = "closefd name", + .help = "close a file descriptor previously passed via SCM rights", + .user_print = monitor_user_noop, + .mhandler.cmd_new = do_closefd, + }, + +STEXI +@item closefd @var{fdname} +@findex closefd +Close the file descriptor previously assigned to @var{fdname} using the +@code{getfd} command. This is only needed if the file descriptor was never +used by another monitor command. +ETEXI + + { + .name = "block_passwd", + .args_type = "device:B,password:s", + .params = "block_passwd device password", + .help = "set the password of encrypted block devices", + .user_print = monitor_user_noop, + .mhandler.cmd_new = do_block_set_passwd, + }, + +STEXI +@item block_passwd @var{device} @var{password} +@findex block_passwd +Set the encrypted device @var{device} password to @var{password} +ETEXI + + { + .name = "cpu_set", + .args_type = "cpu:i,state:s", + .params = "cpu [online|offline]", + .help = "change cpu state", + .mhandler.cmd = do_cpu_set_nr, + }, + +STEXI +@item cpu_set @var{cpu} [online|offline] +Set CPU @var{cpu} online or offline. +ETEXI + + { + .name = "set_password", + .args_type = "protocol:s,password:s,connected:s?", + .params = "protocol password action-if-connected", + .help = "set spice/vnc password", + .user_print = monitor_user_noop, + .mhandler.cmd_new = set_password, + }, + +STEXI +@item set_password [ vnc | spice ] password [ action-if-connected ] +@findex set_password + +Change spice/vnc password. Use zero to make the password stay valid +forever. @var{action-if-connected} specifies what should happen in +case a connection is established: @var{fail} makes the password change +fail. @var{disconnect} changes the password and disconnects the +client. @var{keep} changes the password and keeps the connection up. +@var{keep} is the default. +ETEXI + + { + .name = "expire_password", + .args_type = "protocol:s,time:s", + .params = "protocol time", + .help = "set spice/vnc password expire-time", + .user_print = monitor_user_noop, + .mhandler.cmd_new = expire_password, + }, + +STEXI +@item expire_password [ vnc | spice ] expire-time +@findex expire_password + +Specify when a password for spice/vnc becomes +invalid. @var{expire-time} accepts: + +@table @var +@item now +Invalidate password instantly. + +@item never +Password stays valid forever. + +@item +nsec +Password stays valid for @var{nsec} seconds starting now. + +@item nsec +Password is invalidated at the given time. @var{nsec} are the seconds +passed since 1970, i.e. unix epoch. + +@end table +ETEXI + + { + .name = "info", + .args_type = "item:s?", + .params = "[subcommand]", + .help = "show various information about the system state", + .mhandler.cmd = do_info, + }, + +STEXI +@item info @var{subcommand} +@findex info +Show various information about the system state. + +@table @option +@item info version +show the version of QEMU +@item info network +show the various VLANs and the associated devices +@item info chardev +show the character devices +@item info block +show the block devices +@item info blockstats +show block device statistics +@item info registers +show the cpu registers +@item info cpus +show infos for each CPU +@item info history +show the command line history +@item info irq +show the interrupts statistics (if available) +@item info pic +show i8259 (PIC) state +@item info pci +show emulated PCI device info +@item info tlb +show virtual to physical memory mappings (i386, SH4 and SPARC only) +@item info mem +show the active virtual memory mappings (i386 only) +@item info jit +show dynamic compiler info +@item info kvm +show KVM information +@item info numa +show NUMA information +@item info kvm +show KVM information +@item info usb +show USB devices plugged on the virtual USB hub +@item info usbhost +show all USB host devices +@item info profile +show profiling information +@item info capture +show information about active capturing +@item info snapshots +show list of VM snapshots +@item info status +show the current VM status (running|paused) +@item info pcmcia +show guest PCMCIA status +@item info mice +show which guest mouse is receiving events +@item info vnc +show the vnc server status +@item info name +show the current VM name +@item info uuid +show the current VM UUID +@item info cpustats +show CPU statistics +@item info usernet +show user network stack connection states +@item info migrate +show migration status +@item info balloon +show balloon information +@item info qtree +show device tree +@item info qdm +show qdev device model list +@item info roms +show roms +@end table +ETEXI + +#ifdef CONFIG_SIMPLE_TRACE +STEXI +@item info trace +show contents of trace buffer +@item info trace-events +show available trace events and their state +ETEXI +#endif + +STEXI +@end table +ETEXI diff -Nru qemu-kvm-0.12.5+noroms/hostregs_helper.h qemu-kvm-0.14.1/hostregs_helper.h --- qemu-kvm-0.12.5+noroms/hostregs_helper.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hostregs_helper.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,61 +0,0 @@ -/* - * Save/restore host registers. - * - * Copyright (c) 2007 CodeSourcery - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - */ - -/* The GCC global register variable extension is used to reserve some - host registers for use by generated code. However only the core parts of - the translation engine are compiled with these settings. We must manually - save/restore these registers when called from regular code. - It is not sufficient to save/restore T0 et. al. as these may be declared - with a datatype smaller than the actual register. */ - -#if defined(DECLARE_HOST_REGS) - -#define DO_REG(REG) \ - register host_reg_t reg_AREG##REG asm(AREG##REG); \ - volatile host_reg_t saved_AREG##REG; - -#elif defined(SAVE_HOST_REGS) - -#define DO_REG(REG) \ - __asm__ __volatile__ ("" : "=r" (reg_AREG##REG)); \ - saved_AREG##REG = reg_AREG##REG; - -#else - -#define DO_REG(REG) \ - reg_AREG##REG = saved_AREG##REG; \ - __asm__ __volatile__ ("" : : "r" (reg_AREG##REG)); - -#endif - -#ifdef AREG0 -DO_REG(0) -#endif - -#ifdef AREG1 -DO_REG(1) -#endif - -#ifdef AREG2 -DO_REG(2) -#endif - -#undef SAVE_HOST_REGS -#undef DECLARE_HOST_REGS -#undef DO_REG diff -Nru qemu-kvm-0.12.5+noroms/hppa-dis.c qemu-kvm-0.14.1/hppa-dis.c --- qemu-kvm-0.12.5+noroms/hppa-dis.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hppa-dis.c 2011-05-11 13:29:46.000000000 +0000 @@ -576,7 +576,7 @@ const char *name; unsigned long int match; /* Bits that must be set... */ unsigned long int mask; /* ... in these bits. */ - char *args; + const char *args; enum pa_arch arch; char flags; }; @@ -2753,7 +2753,7 @@ int sf = GET_FIELD (insn, 19, 20); const char * const * source = float_format_names; const char * const * dest = float_format_names; - char *t = ""; + const char *t = ""; if (sub == 4) { diff -Nru qemu-kvm-0.12.5+noroms/hw/9p.h qemu-kvm-0.14.1/hw/9p.h --- qemu-kvm-0.12.5+noroms/hw/9p.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/9p.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,24 @@ +/* + * Virtio 9p + * + * Copyright IBM, Corp. 2010 + * + * Authors: + * Aneesh Kumar K.V + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ + +#ifndef QEMU_9P_H +#define QEMU_9P_H + +typedef struct V9fsConf +{ + /* tag name for the device */ + char *tag; + char *fsdev_id; +} V9fsConf; + +#endif diff -Nru qemu-kvm-0.12.5+noroms/hw/ac97.c qemu-kvm-0.14.1/hw/ac97.c --- qemu-kvm-0.12.5+noroms/hw/ac97.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/ac97.c 2011-05-11 13:29:46.000000000 +0000 @@ -1284,37 +1284,41 @@ pci_config_set_vendor_id (c, PCI_VENDOR_ID_INTEL); /* ro */ pci_config_set_device_id (c, PCI_DEVICE_ID_INTEL_82801AA_5); /* ro */ - c[0x04] = 0x00; /* pcicmd pci command rw, ro */ - c[0x05] = 0x00; + /* TODO: no need to override */ + c[PCI_COMMAND] = 0x00; /* pcicmd pci command rw, ro */ + c[PCI_COMMAND + 1] = 0x00; + + /* TODO: */ + c[PCI_STATUS] = PCI_STATUS_FAST_BACK; /* pcists pci status rwc, ro */ + c[PCI_STATUS + 1] = PCI_STATUS_DEVSEL_MEDIUM >> 8; - c[0x06] = 0x80; /* pcists pci status rwc, ro */ - c[0x07] = 0x02; - - c[0x08] = 0x01; /* rid revision ro */ - c[0x09] = 0x00; /* pi programming interface ro */ + c[PCI_REVISION_ID] = 0x01; /* rid revision ro */ + c[PCI_CLASS_PROG] = 0x00; /* pi programming interface ro */ pci_config_set_class (c, PCI_CLASS_MULTIMEDIA_AUDIO); /* ro */ - c[PCI_HEADER_TYPE] = PCI_HEADER_TYPE_NORMAL; /* headtyp header type ro */ - - c[0x10] = 0x01; /* nabmar native audio mixer base - address rw */ - c[0x11] = 0x00; - c[0x12] = 0x00; - c[0x13] = 0x00; - - c[0x14] = 0x01; /* nabmbar native audio bus mastering - base address rw */ - c[0x15] = 0x00; - c[0x16] = 0x00; - c[0x17] = 0x00; - - c[0x2c] = 0x86; /* svid subsystem vendor id rwo */ - c[0x2d] = 0x80; - - c[0x2e] = 0x00; /* sid subsystem id rwo */ - c[0x2f] = 0x00; - c[0x3c] = 0x00; /* intr_ln interrupt line rw */ - c[0x3d] = 0x01; /* intr_pn interrupt pin ro */ + /* TODO set when bar is registered. no need to override. */ + /* nabmar native audio mixer base address rw */ + c[PCI_BASE_ADDRESS_0] = PCI_BASE_ADDRESS_SPACE_IO; + c[PCI_BASE_ADDRESS_0 + 1] = 0x00; + c[PCI_BASE_ADDRESS_0 + 2] = 0x00; + c[PCI_BASE_ADDRESS_0 + 3] = 0x00; + + /* TODO set when bar is registered. no need to override. */ + /* nabmbar native audio bus mastering base address rw */ + c[PCI_BASE_ADDRESS_0 + 4] = PCI_BASE_ADDRESS_SPACE_IO; + c[PCI_BASE_ADDRESS_0 + 5] = 0x00; + c[PCI_BASE_ADDRESS_0 + 6] = 0x00; + c[PCI_BASE_ADDRESS_0 + 7] = 0x00; + + c[PCI_SUBSYSTEM_VENDOR_ID] = 0x86; /* svid subsystem vendor id rwo */ + c[PCI_SUBSYSTEM_VENDOR_ID + 1] = 0x80; + + c[PCI_SUBSYSTEM_ID] = 0x00; /* sid subsystem id rwo */ + c[PCI_SUBSYSTEM_ID + 1] = 0x00; + + c[PCI_INTERRUPT_LINE] = 0x00; /* intr_ln interrupt line rw */ + /* TODO: RST# value should be 0. */ + c[PCI_INTERRUPT_PIN] = 0x01; /* intr_pn interrupt pin ro */ pci_register_bar (&s->dev, 0, 256 * 4, PCI_BASE_ADDRESS_SPACE_IO, ac97_map); diff -Nru qemu-kvm-0.12.5+noroms/hw/acpi.c qemu-kvm-0.14.1/hw/acpi.c --- qemu-kvm-0.12.5+noroms/hw/acpi.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/acpi.c 2011-05-11 13:29:46.000000000 +0000 @@ -17,815 +17,11 @@ */ #include "hw.h" #include "pc.h" -#include "pci.h" -#include "qemu-timer.h" -#include "sysemu.h" -#include "i2c.h" -#include "smbus.h" +#include "acpi.h" #include "kvm.h" #include "qemu-kvm.h" #include "string.h" -//#define DEBUG - -/* i82731AB (PIIX4) compatible power management function */ -#define PM_FREQ 3579545 - -#define ACPI_DBG_IO_ADDR 0xb044 - -typedef struct PIIX4PMState { - PCIDevice dev; - uint16_t pmsts; - uint16_t pmen; - uint16_t pmcntrl; - uint8_t apmc; - uint8_t apms; - QEMUTimer *tmr_timer; - int64_t tmr_overflow_time; - i2c_bus *smbus; - uint8_t smb_stat; - uint8_t smb_ctl; - uint8_t smb_cmd; - uint8_t smb_addr; - uint8_t smb_data0; - uint8_t smb_data1; - uint8_t smb_data[32]; - uint8_t smb_index; - qemu_irq irq; -} PIIX4PMState; - -#define RSM_STS (1 << 15) -#define PWRBTN_STS (1 << 8) -#define RTC_EN (1 << 10) -#define PWRBTN_EN (1 << 8) -#define GBL_EN (1 << 5) -#define TMROF_EN (1 << 0) - -#define SCI_EN (1 << 0) - -#define SUS_EN (1 << 13) - -#define ACPI_ENABLE 0xf1 -#define ACPI_DISABLE 0xf0 - -#define SMBHSTSTS 0x00 -#define SMBHSTCNT 0x02 -#define SMBHSTCMD 0x03 -#define SMBHSTADD 0x04 -#define SMBHSTDAT0 0x05 -#define SMBHSTDAT1 0x06 -#define SMBBLKDAT 0x07 - -static PIIX4PMState *pm_state; - -static uint32_t get_pmtmr(PIIX4PMState *s) -{ - uint32_t d; - d = muldiv64(qemu_get_clock(vm_clock), PM_FREQ, get_ticks_per_sec()); - return d & 0xffffff; -} - -static int get_pmsts(PIIX4PMState *s) -{ - int64_t d; - int pmsts; - pmsts = s->pmsts; - d = muldiv64(qemu_get_clock(vm_clock), PM_FREQ, get_ticks_per_sec()); - if (d >= s->tmr_overflow_time) - s->pmsts |= TMROF_EN; - return s->pmsts; -} - -static void pm_update_sci(PIIX4PMState *s) -{ - int sci_level, pmsts; - int64_t expire_time; - - pmsts = get_pmsts(s); - sci_level = (((pmsts & s->pmen) & - (RTC_EN | PWRBTN_EN | GBL_EN | TMROF_EN)) != 0); - qemu_set_irq(s->irq, sci_level); - /* schedule a timer interruption if needed */ - if ((s->pmen & TMROF_EN) && !(pmsts & TMROF_EN)) { - expire_time = muldiv64(s->tmr_overflow_time, get_ticks_per_sec(), PM_FREQ); - qemu_mod_timer(s->tmr_timer, expire_time); - } else { - qemu_del_timer(s->tmr_timer); - } -} - -static void pm_tmr_timer(void *opaque) -{ - PIIX4PMState *s = opaque; - pm_update_sci(s); -} - -static void pm_ioport_writew(void *opaque, uint32_t addr, uint32_t val) -{ - PIIX4PMState *s = opaque; - addr &= 0x3f; - switch(addr) { - case 0x00: - { - int64_t d; - int pmsts; - pmsts = get_pmsts(s); - if (pmsts & val & TMROF_EN) { - /* if TMRSTS is reset, then compute the new overflow time */ - d = muldiv64(qemu_get_clock(vm_clock), PM_FREQ, - get_ticks_per_sec()); - s->tmr_overflow_time = (d + 0x800000LL) & ~0x7fffffLL; - } - s->pmsts &= ~val; - pm_update_sci(s); - } - break; - case 0x02: - s->pmen = val; - pm_update_sci(s); - break; - case 0x04: - { - int sus_typ; - s->pmcntrl = val & ~(SUS_EN); - if (val & SUS_EN) { - /* change suspend type */ - sus_typ = (val >> 10) & 7; - switch(sus_typ) { - case 0: /* soft power off */ - qemu_system_shutdown_request(); - break; - case 1: - /* RSM_STS should be set on resume. Pretend that resume - was caused by power button */ - s->pmsts |= (RSM_STS | PWRBTN_STS); - qemu_system_reset_request(); -#if defined(TARGET_I386) - cmos_set_s3_resume(); -#endif - default: - break; - } - } - } - break; - default: - break; - } -#ifdef DEBUG - printf("PM writew port=0x%04x val=0x%04x\n", addr, val); -#endif -} - -static uint32_t pm_ioport_readw(void *opaque, uint32_t addr) -{ - PIIX4PMState *s = opaque; - uint32_t val; - - addr &= 0x3f; - switch(addr) { - case 0x00: - val = get_pmsts(s); - break; - case 0x02: - val = s->pmen; - break; - case 0x04: - val = s->pmcntrl; - break; - default: - val = 0; - break; - } -#ifdef DEBUG - printf("PM readw port=0x%04x val=0x%04x\n", addr, val); -#endif - return val; -} - -static void pm_ioport_writel(void *opaque, uint32_t addr, uint32_t val) -{ - // PIIX4PMState *s = opaque; - addr &= 0x3f; -#ifdef DEBUG - printf("PM writel port=0x%04x val=0x%08x\n", addr, val); -#endif -} - -static uint32_t pm_ioport_readl(void *opaque, uint32_t addr) -{ - PIIX4PMState *s = opaque; - uint32_t val; - - addr &= 0x3f; - switch(addr) { - case 0x08: - val = get_pmtmr(s); - break; - default: - val = 0; - break; - } -#ifdef DEBUG - printf("PM readl port=0x%04x val=0x%08x\n", addr, val); -#endif - return val; -} - -static void pm_smi_writeb(void *opaque, uint32_t addr, uint32_t val) -{ - PIIX4PMState *s = opaque; - addr &= 1; -#ifdef DEBUG - printf("pm_smi_writeb addr=0x%x val=0x%02x\n", addr, val); -#endif - if (addr == 0) { - s->apmc = val; - - /* ACPI specs 3.0, 4.7.2.5 */ - if (val == ACPI_ENABLE) { - s->pmcntrl |= SCI_EN; - } else if (val == ACPI_DISABLE) { - s->pmcntrl &= ~SCI_EN; - } - - if (s->dev.config[0x5b] & (1 << 1)) { - cpu_interrupt(first_cpu, CPU_INTERRUPT_SMI); - } - } else { - s->apms = val; - } -} - -static uint32_t pm_smi_readb(void *opaque, uint32_t addr) -{ - PIIX4PMState *s = opaque; - uint32_t val; - - addr &= 1; - if (addr == 0) { - val = s->apmc; - } else { - val = s->apms; - } -#ifdef DEBUG - printf("pm_smi_readb addr=0x%x val=0x%02x\n", addr, val); -#endif - return val; -} - -static void acpi_dbg_writel(void *opaque, uint32_t addr, uint32_t val) -{ -#if defined(DEBUG) - printf("ACPI: DBG: 0x%08x\n", val); -#endif -} - -static void smb_transaction(PIIX4PMState *s) -{ - uint8_t prot = (s->smb_ctl >> 2) & 0x07; - uint8_t read = s->smb_addr & 0x01; - uint8_t cmd = s->smb_cmd; - uint8_t addr = s->smb_addr >> 1; - i2c_bus *bus = s->smbus; - -#ifdef DEBUG - printf("SMBus trans addr=0x%02x prot=0x%02x\n", addr, prot); -#endif - switch(prot) { - case 0x0: - smbus_quick_command(bus, addr, read); - break; - case 0x1: - if (read) { - s->smb_data0 = smbus_receive_byte(bus, addr); - } else { - smbus_send_byte(bus, addr, cmd); - } - break; - case 0x2: - if (read) { - s->smb_data0 = smbus_read_byte(bus, addr, cmd); - } else { - smbus_write_byte(bus, addr, cmd, s->smb_data0); - } - break; - case 0x3: - if (read) { - uint16_t val; - val = smbus_read_word(bus, addr, cmd); - s->smb_data0 = val; - s->smb_data1 = val >> 8; - } else { - smbus_write_word(bus, addr, cmd, (s->smb_data1 << 8) | s->smb_data0); - } - break; - case 0x5: - if (read) { - s->smb_data0 = smbus_read_block(bus, addr, cmd, s->smb_data); - } else { - smbus_write_block(bus, addr, cmd, s->smb_data, s->smb_data0); - } - break; - default: - goto error; - } - return; - - error: - s->smb_stat |= 0x04; -} - -static void smb_ioport_writeb(void *opaque, uint32_t addr, uint32_t val) -{ - PIIX4PMState *s = opaque; - addr &= 0x3f; -#ifdef DEBUG - printf("SMB writeb port=0x%04x val=0x%02x\n", addr, val); -#endif - switch(addr) { - case SMBHSTSTS: - s->smb_stat = 0; - s->smb_index = 0; - break; - case SMBHSTCNT: - s->smb_ctl = val; - if (val & 0x40) - smb_transaction(s); - break; - case SMBHSTCMD: - s->smb_cmd = val; - break; - case SMBHSTADD: - s->smb_addr = val; - break; - case SMBHSTDAT0: - s->smb_data0 = val; - break; - case SMBHSTDAT1: - s->smb_data1 = val; - break; - case SMBBLKDAT: - s->smb_data[s->smb_index++] = val; - if (s->smb_index > 31) - s->smb_index = 0; - break; - default: - break; - } -} - -static uint32_t smb_ioport_readb(void *opaque, uint32_t addr) -{ - PIIX4PMState *s = opaque; - uint32_t val; - - addr &= 0x3f; - switch(addr) { - case SMBHSTSTS: - val = s->smb_stat; - break; - case SMBHSTCNT: - s->smb_index = 0; - val = s->smb_ctl & 0x1f; - break; - case SMBHSTCMD: - val = s->smb_cmd; - break; - case SMBHSTADD: - val = s->smb_addr; - break; - case SMBHSTDAT0: - val = s->smb_data0; - break; - case SMBHSTDAT1: - val = s->smb_data1; - break; - case SMBBLKDAT: - val = s->smb_data[s->smb_index++]; - if (s->smb_index > 31) - s->smb_index = 0; - break; - default: - val = 0; - break; - } -#ifdef DEBUG - printf("SMB readb port=0x%04x val=0x%02x\n", addr, val); -#endif - return val; -} - -static void pm_io_space_update(PIIX4PMState *s) -{ - uint32_t pm_io_base; - - if (s->dev.config[0x80] & 1) { - pm_io_base = le32_to_cpu(*(uint32_t *)(s->dev.config + 0x40)); - pm_io_base &= 0xffc0; - - /* XXX: need to improve memory and ioport allocation */ -#if defined(DEBUG) - printf("PM: mapping to 0x%x\n", pm_io_base); -#endif - register_ioport_write(pm_io_base, 64, 2, pm_ioport_writew, s); - register_ioport_read(pm_io_base, 64, 2, pm_ioport_readw, s); - register_ioport_write(pm_io_base, 64, 4, pm_ioport_writel, s); - register_ioport_read(pm_io_base, 64, 4, pm_ioport_readl, s); - } -} - -static void pm_write_config(PCIDevice *d, - uint32_t address, uint32_t val, int len) -{ - pci_default_write_config(d, address, val, len); - if (address == 0x80) - pm_io_space_update((PIIX4PMState *)d); -} - -static int vmstate_acpi_post_load(void *opaque, int version_id) -{ - PIIX4PMState *s = opaque; - - pm_io_space_update(s); - return 0; -} - -static const VMStateDescription vmstate_acpi = { - .name = "piix4_pm", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, - .post_load = vmstate_acpi_post_load, - .fields = (VMStateField []) { - VMSTATE_PCI_DEVICE(dev, PIIX4PMState), - VMSTATE_UINT16(pmsts, PIIX4PMState), - VMSTATE_UINT16(pmen, PIIX4PMState), - VMSTATE_UINT16(pmcntrl, PIIX4PMState), - VMSTATE_UINT8(apmc, PIIX4PMState), - VMSTATE_UINT8(apms, PIIX4PMState), - VMSTATE_TIMER(tmr_timer, PIIX4PMState), - VMSTATE_INT64(tmr_overflow_time, PIIX4PMState), - VMSTATE_END_OF_LIST() - } -}; - -static void piix4_reset(void *opaque) -{ - PIIX4PMState *s = opaque; - uint8_t *pci_conf = s->dev.config; - - pci_conf[0x58] = 0; - pci_conf[0x59] = 0; - pci_conf[0x5a] = 0; - pci_conf[0x5b] = 0; - - if (kvm_enabled()) { - /* Mark SMM as already inited (until KVM supports SMM). */ - pci_conf[0x5B] = 0x02; - } -} - -static void piix4_powerdown(void *opaque, int irq, int power_failing) -{ -#if defined(TARGET_I386) - PIIX4PMState *s = opaque; - - if (!s) { - qemu_system_shutdown_request(); - } else if (s->pmen & PWRBTN_EN) { - s->pmsts |= PWRBTN_EN; - pm_update_sci(s); - } -#endif -} - -i2c_bus *piix4_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base, - qemu_irq sci_irq) -{ - PIIX4PMState *s; - uint8_t *pci_conf; - - s = (PIIX4PMState *)pci_register_device(bus, - "PM", sizeof(PIIX4PMState), - devfn, NULL, pm_write_config); - pm_state = s; - pci_conf = s->dev.config; - pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_INTEL); - pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_INTEL_82371AB_3); - pci_conf[0x06] = 0x80; - pci_conf[0x07] = 0x02; - pci_conf[0x08] = 0x03; // revision number - pci_conf[0x09] = 0x00; - pci_config_set_class(pci_conf, PCI_CLASS_BRIDGE_OTHER); - pci_conf[PCI_HEADER_TYPE] = PCI_HEADER_TYPE_NORMAL; // header_type - pci_conf[0x3d] = 0x01; // interrupt pin 1 - - pci_conf[0x40] = 0x01; /* PM io base read only bit */ - -#if defined(TARGET_IA64) - pci_conf[0x40] = 0x41; /* PM io base read only bit */ - pci_conf[0x41] = 0x1f; - pm_write_config(s, 0x80, 0x01, 1); /*Set default pm_io_base 0x1f40*/ - s->pmcntrl = SCI_EN; -#endif - - register_ioport_write(0xb2, 2, 1, pm_smi_writeb, s); - register_ioport_read(0xb2, 2, 1, pm_smi_readb, s); - - register_ioport_write(ACPI_DBG_IO_ADDR, 4, 4, acpi_dbg_writel, s); - - if (kvm_enabled()) { - /* Mark SMM as already inited to prevent SMM from running. KVM does not - * support SMM mode. */ - pci_conf[0x5B] = 0x02; - } - - /* XXX: which specification is used ? The i82731AB has different - mappings */ - pci_conf[0x5f] = (parallel_hds[0] != NULL ? 0x80 : 0) | 0x10; - pci_conf[0x63] = 0x60; - pci_conf[0x67] = (serial_hds[0] != NULL ? 0x08 : 0) | - (serial_hds[1] != NULL ? 0x90 : 0); - - pci_conf[0x90] = smb_io_base | 1; - pci_conf[0x91] = smb_io_base >> 8; - pci_conf[0xd2] = 0x09; - register_ioport_write(smb_io_base, 64, 1, smb_ioport_writeb, s); - register_ioport_read(smb_io_base, 64, 1, smb_ioport_readb, s); - - s->tmr_timer = qemu_new_timer(vm_clock, pm_tmr_timer, s); - - qemu_system_powerdown = *qemu_allocate_irqs(piix4_powerdown, s, 1); - - vmstate_register(0, &vmstate_acpi, s); - - s->smbus = i2c_init_bus(NULL, "i2c"); - s->irq = sci_irq; - qemu_register_reset(piix4_reset, s); - - return s->smbus; -} - -#define GPE_BASE 0xafe0 -#define PROC_BASE 0xaf00 -#define PCI_BASE 0xae00 -#define PCI_EJ_BASE 0xae08 - -struct gpe_regs { - uint16_t sts; /* status */ - uint16_t en; /* enabled */ - uint8_t cpus_sts[32]; -}; - -struct pci_status { - uint32_t up; - uint32_t down; -}; - -static struct gpe_regs gpe; -static struct pci_status pci0_status; - -static uint32_t gpe_read_val(uint16_t val, uint32_t addr) -{ - if (addr & 1) - return (val >> 8) & 0xff; - return val & 0xff; -} - -static uint32_t gpe_readb(void *opaque, uint32_t addr) -{ - uint32_t val = 0; - struct gpe_regs *g = opaque; - switch (addr) { - case PROC_BASE ... PROC_BASE+31: - val = g->cpus_sts[addr - PROC_BASE]; - break; - - case GPE_BASE: - case GPE_BASE + 1: - val = gpe_read_val(g->sts, addr); - break; - case GPE_BASE + 2: - case GPE_BASE + 3: - val = gpe_read_val(g->en, addr); - break; - default: - break; - } - -#if defined(DEBUG) - printf("gpe read %x == %x\n", addr, val); -#endif - return val; -} - -static void gpe_write_val(uint16_t *cur, int addr, uint32_t val) -{ - if (addr & 1) - *cur = (*cur & 0xff) | (val << 8); - else - *cur = (*cur & 0xff00) | (val & 0xff); -} - -static void gpe_reset_val(uint16_t *cur, int addr, uint32_t val) -{ - uint16_t x1, x0 = val & 0xff; - int shift = (addr & 1) ? 8 : 0; - - x1 = (*cur >> shift) & 0xff; - - x1 = x1 & ~x0; - - *cur = (*cur & (0xff << (8 - shift))) | (x1 << shift); -} - -static void gpe_writeb(void *opaque, uint32_t addr, uint32_t val) -{ - struct gpe_regs *g = opaque; - switch (addr) { - case PROC_BASE ... PROC_BASE + 31: - /* don't allow to change cpus_sts from inside a guest */ - break; - - case GPE_BASE: - case GPE_BASE + 1: - gpe_reset_val(&g->sts, addr, val); - break; - case GPE_BASE + 2: - case GPE_BASE + 3: - gpe_write_val(&g->en, addr, val); - break; - default: - break; - } - -#if defined(DEBUG) - printf("gpe write %x <== %d\n", addr, val); -#endif -} - -static uint32_t pcihotplug_read(void *opaque, uint32_t addr) -{ - uint32_t val = 0; - struct pci_status *g = opaque; - switch (addr) { - case PCI_BASE: - val = g->up; - break; - case PCI_BASE + 4: - val = g->down; - break; - default: - break; - } - -#if defined(DEBUG) - printf("pcihotplug read %x == %x\n", addr, val); -#endif - return val; -} - -static void pcihotplug_write(void *opaque, uint32_t addr, uint32_t val) -{ - struct pci_status *g = opaque; - switch (addr) { - case PCI_BASE: - g->up = val; - break; - case PCI_BASE + 4: - g->down = val; - break; - } - -#if defined(DEBUG) - printf("pcihotplug write %x <== %d\n", addr, val); -#endif -} - -static uint32_t pciej_read(void *opaque, uint32_t addr) -{ -#if defined(DEBUG) - printf("pciej read %x\n", addr); -#endif - return 0; -} - -static void pciej_write(void *opaque, uint32_t addr, uint32_t val) -{ - BusState *bus = opaque; - DeviceState *qdev, *next; - PCIDevice *dev; - int slot = ffs(val) - 1; - - QLIST_FOREACH_SAFE(qdev, &bus->children, sibling, next) { - dev = DO_UPCAST(PCIDevice, qdev, qdev); - if (PCI_SLOT(dev->devfn) == slot) { - qdev_free(qdev); - } - } - - -#if defined(DEBUG) - printf("pciej write %x <== %d\n", addr, val); -#endif -} - -static const char *model; - -static int piix4_device_hotplug(PCIDevice *dev, int state); - -void piix4_acpi_system_hot_add_init(PCIBus *bus, const char *cpu_model) -{ - int i = 0, cpus = smp_cpus; - - while (cpus > 0) { - gpe.cpus_sts[i++] = (cpus < 8) ? (1 << cpus) - 1 : 0xff; - cpus -= 8; - } - register_ioport_write(GPE_BASE, 4, 1, gpe_writeb, &gpe); - register_ioport_read(GPE_BASE, 4, 1, gpe_readb, &gpe); - - register_ioport_write(PROC_BASE, 32, 1, gpe_writeb, &gpe); - register_ioport_read(PROC_BASE, 32, 1, gpe_readb, &gpe); - - register_ioport_write(PCI_BASE, 8, 4, pcihotplug_write, &pci0_status); - register_ioport_read(PCI_BASE, 8, 4, pcihotplug_read, &pci0_status); - - register_ioport_write(PCI_EJ_BASE, 4, 4, pciej_write, bus); - register_ioport_read(PCI_EJ_BASE, 4, 4, pciej_read, bus); - - model = cpu_model; - - pci_bus_hotplug(bus, piix4_device_hotplug); -} - -#if defined(TARGET_I386) -static void enable_processor(struct gpe_regs *g, int cpu) -{ - g->sts |= 4; - g->cpus_sts[cpu/8] |= (1 << (cpu%8)); -} - -static void disable_processor(struct gpe_regs *g, int cpu) -{ - g->sts |= 4; - g->cpus_sts[cpu/8] &= ~(1 << (cpu%8)); -} - -void qemu_system_cpu_hot_add(int cpu, int state) -{ - CPUState *env; - - if (state && !qemu_get_cpu(cpu)) { - env = pc_new_cpu(model); - if (!env) { - fprintf(stderr, "cpu %d creation failed\n", cpu); - return; - } - env->cpuid_apic_id = cpu; - } - - if (state) - enable_processor(&gpe, cpu); - else - disable_processor(&gpe, cpu); - if (gpe.en & 4) { - qemu_set_irq(pm_state->irq, 1); - qemu_set_irq(pm_state->irq, 0); - } -} -#endif - -static void enable_device(struct pci_status *p, struct gpe_regs *g, int slot) -{ - g->sts |= 2; - p->up |= (1 << slot); -} - -static void disable_device(struct pci_status *p, struct gpe_regs *g, int slot) -{ - g->sts |= 2; - p->down |= (1 << slot); -} - -static int piix4_device_hotplug(PCIDevice *dev, int state) -{ - int slot = PCI_SLOT(dev->devfn); - - pci0_status.up = 0; - pci0_status.down = 0; - if (state) - enable_device(&pci0_status, &gpe, slot); - else - disable_device(&pci0_status, &gpe, slot); - if (gpe.en & 2) { - qemu_set_irq(pm_state->irq, 1); - qemu_set_irq(pm_state->irq, 0); - } - return 0; -} - struct acpi_table_header { char signature [4]; /* ACPI signature (4 ASCII characters) */ @@ -857,6 +53,8 @@ char buf[1024], *p, *f; struct acpi_table_header acpi_hdr; unsigned long val; + uint32_t length; + struct acpi_table_header *acpi_hdr_p; size_t off; memset(&acpi_hdr, 0, sizeof(acpi_hdr)); @@ -915,7 +113,7 @@ buf[0] = '\0'; } - acpi_hdr.length = sizeof(acpi_hdr); + length = sizeof(acpi_hdr); f = buf; while (buf[0]) { @@ -927,7 +125,7 @@ fprintf(stderr, "Can't stat file '%s': %s\n", f, strerror(errno)); goto out; } - acpi_hdr.length += s.st_size; + length += s.st_size; if (!n) break; *n = ':'; @@ -938,12 +136,12 @@ acpi_tables_len = sizeof(uint16_t); acpi_tables = qemu_mallocz(acpi_tables_len); } + acpi_tables = qemu_realloc(acpi_tables, + acpi_tables_len + sizeof(uint16_t) + length); p = acpi_tables + acpi_tables_len; - acpi_tables_len += sizeof(uint16_t) + acpi_hdr.length; - acpi_tables = qemu_realloc(acpi_tables, acpi_tables_len); + acpi_tables_len += sizeof(uint16_t) + length; - acpi_hdr.length = cpu_to_le32(acpi_hdr.length); - *(uint16_t*)p = acpi_hdr.length; + *(uint16_t*)p = cpu_to_le32(length); p += sizeof(uint16_t); memcpy(p, &acpi_hdr, sizeof(acpi_hdr)); off = sizeof(acpi_hdr); @@ -964,7 +162,9 @@ goto out; } - do { + /* off < length is necessary because file size can be changed + under our foot */ + while(s.st_size && off < length) { int r; r = read(fd, p + off, s.st_size); if (r > 0) { @@ -974,15 +174,21 @@ close(fd); goto out; } - } while(s.st_size); + } close(fd); if (!n) break; f = n + 1; } + if (off < length) { + /* don't pass random value in process to guest */ + memset(p + off, 0, length - off); + } - ((struct acpi_table_header*)p)->checksum = acpi_checksum((uint8_t*)p, off); + acpi_hdr_p = (struct acpi_table_header*)p; + acpi_hdr_p->length = cpu_to_le32(length); + acpi_hdr_p->checksum = acpi_checksum((uint8_t*)p, length); /* increase number of tables */ (*(uint16_t*)acpi_tables) = cpu_to_le32(le32_to_cpu(*(uint16_t*)acpi_tables) + 1); diff -Nru qemu-kvm-0.12.5+noroms/hw/acpi.h qemu-kvm-0.14.1/hw/acpi.h --- qemu-kvm-0.12.5+noroms/hw/acpi.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/acpi.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,78 @@ +#ifndef QEMU_HW_ACPI_H +#define QEMU_HW_ACPI_H +/* + * Copyright (c) 2009 Isaku Yamahata + * VA Linux Systems Japan K.K. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * . + */ + +/* from linux include/acpi/actype.h */ +/* Default ACPI register widths */ + +#define ACPI_GPE_REGISTER_WIDTH 8 +#define ACPI_PM1_REGISTER_WIDTH 16 +#define ACPI_PM2_REGISTER_WIDTH 8 +#define ACPI_PM_TIMER_WIDTH 32 + +/* PM Timer ticks per second (HZ) */ +#define PM_TIMER_FREQUENCY 3579545 + + +/* ACPI fixed hardware registers */ + +/* from linux/drivers/acpi/acpica/aclocal.h */ +/* Masks used to access the bit_registers */ + +/* PM1x_STS */ +#define ACPI_BITMASK_TIMER_STATUS 0x0001 +#define ACPI_BITMASK_BUS_MASTER_STATUS 0x0010 +#define ACPI_BITMASK_GLOBAL_LOCK_STATUS 0x0020 +#define ACPI_BITMASK_POWER_BUTTON_STATUS 0x0100 +#define ACPI_BITMASK_SLEEP_BUTTON_STATUS 0x0200 +#define ACPI_BITMASK_RT_CLOCK_STATUS 0x0400 +#define ACPI_BITMASK_PCIEXP_WAKE_STATUS 0x4000 /* ACPI 3.0 */ +#define ACPI_BITMASK_WAKE_STATUS 0x8000 + +#define ACPI_BITMASK_ALL_FIXED_STATUS (\ + ACPI_BITMASK_TIMER_STATUS | \ + ACPI_BITMASK_BUS_MASTER_STATUS | \ + ACPI_BITMASK_GLOBAL_LOCK_STATUS | \ + ACPI_BITMASK_POWER_BUTTON_STATUS | \ + ACPI_BITMASK_SLEEP_BUTTON_STATUS | \ + ACPI_BITMASK_RT_CLOCK_STATUS | \ + ACPI_BITMASK_WAKE_STATUS) + +/* PM1x_EN */ +#define ACPI_BITMASK_TIMER_ENABLE 0x0001 +#define ACPI_BITMASK_GLOBAL_LOCK_ENABLE 0x0020 +#define ACPI_BITMASK_POWER_BUTTON_ENABLE 0x0100 +#define ACPI_BITMASK_SLEEP_BUTTON_ENABLE 0x0200 +#define ACPI_BITMASK_RT_CLOCK_ENABLE 0x0400 +#define ACPI_BITMASK_PCIEXP_WAKE_DISABLE 0x4000 /* ACPI 3.0 */ + +/* PM1x_CNT */ +#define ACPI_BITMASK_SCI_ENABLE 0x0001 +#define ACPI_BITMASK_BUS_MASTER_RLD 0x0002 +#define ACPI_BITMASK_GLOBAL_LOCK_RELEASE 0x0004 +#define ACPI_BITMASK_SLEEP_TYPE 0x1C00 +#define ACPI_BITMASK_SLEEP_ENABLE 0x2000 + +/* PM2_CNT */ +#define ACPI_BITMASK_ARB_DISABLE 0x0001 + +/* PM_TMR */ + +#endif /* !QEMU_HW_ACPI_H */ diff -Nru qemu-kvm-0.12.5+noroms/hw/acpi_piix4.c qemu-kvm-0.14.1/hw/acpi_piix4.c --- qemu-kvm-0.12.5+noroms/hw/acpi_piix4.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/acpi_piix4.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,739 @@ +/* + * ACPI implementation + * + * Copyright (c) 2006 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2 as published by the Free Software Foundation. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + */ +#include "hw.h" +#include "pc.h" +#include "apm.h" +#include "pm_smbus.h" +#include "pci.h" +#include "acpi.h" +#include "sysemu.h" +#include "range.h" + +//#define DEBUG + +#ifdef DEBUG +# define PIIX4_DPRINTF(format, ...) printf(format, ## __VA_ARGS__) +#else +# define PIIX4_DPRINTF(format, ...) do { } while (0) +#endif + +#define ACPI_DBG_IO_ADDR 0xb044 + +#define GPE_BASE 0xafe0 +#define PROC_BASE 0xaf00 +#define PCI_BASE 0xae00 +#define PCI_EJ_BASE 0xae08 +#define PCI_RMV_BASE 0xae0c + +#define PIIX4_CPU_HOTPLUG_STATUS 4 +#define PIIX4_PCI_HOTPLUG_STATUS 2 + +struct gpe_regs { + uint16_t sts; /* status */ + uint16_t en; /* enabled */ + uint8_t cpus_sts[32]; +}; + +struct pci_status { + uint32_t up; + uint32_t down; +}; + +typedef struct PIIX4PMState { + PCIDevice dev; + IORange ioport; + uint16_t pmsts; + uint16_t pmen; + uint16_t pmcntrl; + + APMState apm; + + QEMUTimer *tmr_timer; + int64_t tmr_overflow_time; + + PMSMBus smb; + uint32_t smb_io_base; + + qemu_irq irq; + qemu_irq cmos_s3; + qemu_irq smi_irq; + int kvm_enabled; + + /* for pci hotplug */ + struct gpe_regs gpe; + struct pci_status pci0_status; + uint32_t pci0_hotplug_enable; +} PIIX4PMState; + +static void piix4_acpi_system_hot_add_init(PCIBus *bus, PIIX4PMState *s); + +#define ACPI_ENABLE 0xf1 +#define ACPI_DISABLE 0xf0 + +static uint32_t get_pmtmr(PIIX4PMState *s) +{ + uint32_t d; + d = muldiv64(qemu_get_clock(vm_clock), PM_TIMER_FREQUENCY, get_ticks_per_sec()); + return d & 0xffffff; +} + +static int get_pmsts(PIIX4PMState *s) +{ + int64_t d; + + d = muldiv64(qemu_get_clock(vm_clock), PM_TIMER_FREQUENCY, + get_ticks_per_sec()); + if (d >= s->tmr_overflow_time) + s->pmsts |= ACPI_BITMASK_TIMER_STATUS; + return s->pmsts; +} + +static void pm_update_sci(PIIX4PMState *s) +{ + int sci_level, pmsts; + int64_t expire_time; + + pmsts = get_pmsts(s); + sci_level = (((pmsts & s->pmen) & + (ACPI_BITMASK_RT_CLOCK_ENABLE | + ACPI_BITMASK_POWER_BUTTON_ENABLE | + ACPI_BITMASK_GLOBAL_LOCK_ENABLE | + ACPI_BITMASK_TIMER_ENABLE)) != 0) || + (((s->gpe.sts & s->gpe.en) & + (PIIX4_CPU_HOTPLUG_STATUS | PIIX4_PCI_HOTPLUG_STATUS)) != 0); + + qemu_set_irq(s->irq, sci_level); + /* schedule a timer interruption if needed */ + if ((s->pmen & ACPI_BITMASK_TIMER_ENABLE) && + !(pmsts & ACPI_BITMASK_TIMER_STATUS)) { + expire_time = muldiv64(s->tmr_overflow_time, get_ticks_per_sec(), + PM_TIMER_FREQUENCY); + qemu_mod_timer(s->tmr_timer, expire_time); + } else { + qemu_del_timer(s->tmr_timer); + } +} + +static void pm_tmr_timer(void *opaque) +{ + PIIX4PMState *s = opaque; + pm_update_sci(s); +} + +static void pm_ioport_write(IORange *ioport, uint64_t addr, unsigned width, + uint64_t val) +{ + PIIX4PMState *s = container_of(ioport, PIIX4PMState, ioport); + + if (width != 2) { + PIIX4_DPRINTF("PM write port=0x%04x width=%d val=0x%08x\n", + (unsigned)addr, width, (unsigned)val); + } + + switch(addr) { + case 0x00: + { + int64_t d; + int pmsts; + pmsts = get_pmsts(s); + if (pmsts & val & ACPI_BITMASK_TIMER_STATUS) { + /* if TMRSTS is reset, then compute the new overflow time */ + d = muldiv64(qemu_get_clock(vm_clock), PM_TIMER_FREQUENCY, + get_ticks_per_sec()); + s->tmr_overflow_time = (d + 0x800000LL) & ~0x7fffffLL; + } + s->pmsts &= ~val; + pm_update_sci(s); + } + break; + case 0x02: + s->pmen = val; + pm_update_sci(s); + break; + case 0x04: + { + int sus_typ; + s->pmcntrl = val & ~(ACPI_BITMASK_SLEEP_ENABLE); + if (val & ACPI_BITMASK_SLEEP_ENABLE) { + /* change suspend type */ + sus_typ = (val >> 10) & 7; + switch(sus_typ) { + case 0: /* soft power off */ + qemu_system_shutdown_request(); + break; + case 1: + /* ACPI_BITMASK_WAKE_STATUS should be set on resume. + Pretend that resume was caused by power button */ + s->pmsts |= (ACPI_BITMASK_WAKE_STATUS | + ACPI_BITMASK_POWER_BUTTON_STATUS); + qemu_system_reset_request(); + if (s->cmos_s3) { + qemu_irq_raise(s->cmos_s3); + } + default: + break; + } + } + } + break; + default: + break; + } + PIIX4_DPRINTF("PM writew port=0x%04x val=0x%04x\n", addr, val); +} + +static void pm_ioport_read(IORange *ioport, uint64_t addr, unsigned width, + uint64_t *data) +{ + PIIX4PMState *s = container_of(ioport, PIIX4PMState, ioport); + uint32_t val; + + switch(addr) { + case 0x00: + val = get_pmsts(s); + break; + case 0x02: + val = s->pmen; + break; + case 0x04: + val = s->pmcntrl; + break; + case 0x08: + val = get_pmtmr(s); + break; + default: + val = 0; + break; + } + PIIX4_DPRINTF("PM readw port=0x%04x val=0x%04x\n", addr, val); + *data = val; +} + +static const IORangeOps pm_iorange_ops = { + .read = pm_ioport_read, + .write = pm_ioport_write, +}; + +static void apm_ctrl_changed(uint32_t val, void *arg) +{ + PIIX4PMState *s = arg; + + /* ACPI specs 3.0, 4.7.2.5 */ + if (val == ACPI_ENABLE) { + s->pmcntrl |= ACPI_BITMASK_SCI_ENABLE; + } else if (val == ACPI_DISABLE) { + s->pmcntrl &= ~ACPI_BITMASK_SCI_ENABLE; + } + + if (s->dev.config[0x5b] & (1 << 1)) { + if (s->smi_irq) { + qemu_irq_raise(s->smi_irq); + } + } +} + +static void acpi_dbg_writel(void *opaque, uint32_t addr, uint32_t val) +{ + PIIX4_DPRINTF("ACPI: DBG: 0x%08x\n", val); +} + +static void pm_io_space_update(PIIX4PMState *s) +{ + uint32_t pm_io_base; + + if (s->dev.config[0x80] & 1) { + pm_io_base = le32_to_cpu(*(uint32_t *)(s->dev.config + 0x40)); + pm_io_base &= 0xffc0; + + /* XXX: need to improve memory and ioport allocation */ + PIIX4_DPRINTF("PM: mapping to 0x%x\n", pm_io_base); + iorange_init(&s->ioport, &pm_iorange_ops, pm_io_base, 64); + ioport_register(&s->ioport); + } +} + +static void pm_write_config(PCIDevice *d, + uint32_t address, uint32_t val, int len) +{ + pci_default_write_config(d, address, val, len); + if (range_covers_byte(address, len, 0x80)) + pm_io_space_update((PIIX4PMState *)d); +} + +static int vmstate_acpi_post_load(void *opaque, int version_id) +{ + PIIX4PMState *s = opaque; + + pm_io_space_update(s); + return 0; +} + +static const VMStateDescription vmstate_gpe = { + .name = "gpe", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField []) { + VMSTATE_UINT16(sts, struct gpe_regs), + VMSTATE_UINT16(en, struct gpe_regs), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_pci_status = { + .name = "pci_status", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField []) { + VMSTATE_UINT32(up, struct pci_status), + VMSTATE_UINT32(down, struct pci_status), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_acpi = { + .name = "piix4_pm", + .version_id = 2, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .post_load = vmstate_acpi_post_load, + .fields = (VMStateField []) { + VMSTATE_PCI_DEVICE(dev, PIIX4PMState), + VMSTATE_UINT16(pmsts, PIIX4PMState), + VMSTATE_UINT16(pmen, PIIX4PMState), + VMSTATE_UINT16(pmcntrl, PIIX4PMState), + VMSTATE_STRUCT(apm, PIIX4PMState, 0, vmstate_apm, APMState), + VMSTATE_TIMER(tmr_timer, PIIX4PMState), + VMSTATE_INT64(tmr_overflow_time, PIIX4PMState), + VMSTATE_STRUCT(gpe, PIIX4PMState, 2, vmstate_gpe, struct gpe_regs), + VMSTATE_STRUCT(pci0_status, PIIX4PMState, 2, vmstate_pci_status, + struct pci_status), + VMSTATE_END_OF_LIST() + } +}; + +static void piix4_update_hotplug(PIIX4PMState *s) +{ + PCIDevice *dev = &s->dev; + BusState *bus = qdev_get_parent_bus(&dev->qdev); + DeviceState *qdev, *next; + + s->pci0_hotplug_enable = ~0; + + QLIST_FOREACH_SAFE(qdev, &bus->children, sibling, next) { + PCIDeviceInfo *info = container_of(qdev->info, PCIDeviceInfo, qdev); + PCIDevice *pdev = DO_UPCAST(PCIDevice, qdev, qdev); + int slot = PCI_SLOT(pdev->devfn); + + if (info->no_hotplug) { + s->pci0_hotplug_enable &= ~(1 << slot); + } + } +} + +static void piix4_reset(void *opaque) +{ + PIIX4PMState *s = opaque; + uint8_t *pci_conf = s->dev.config; + + pci_conf[0x58] = 0; + pci_conf[0x59] = 0; + pci_conf[0x5a] = 0; + pci_conf[0x5b] = 0; + + if (s->kvm_enabled) { + /* Mark SMM as already inited (until KVM supports SMM). */ + pci_conf[0x5B] = 0x02; + } + piix4_update_hotplug(s); +} + +static void piix4_powerdown(void *opaque, int irq, int power_failing) +{ + PIIX4PMState *s = opaque; + + if (!s) { + qemu_system_shutdown_request(); + } else if (s->pmen & ACPI_BITMASK_POWER_BUTTON_ENABLE) { + s->pmsts |= ACPI_BITMASK_POWER_BUTTON_STATUS; + pm_update_sci(s); + } +} + +static PIIX4PMState *global_piix4_pm_state; /* cpu hotadd */ + +static int piix4_pm_initfn(PCIDevice *dev) +{ + PIIX4PMState *s = DO_UPCAST(PIIX4PMState, dev, dev); + uint8_t *pci_conf; + + /* for cpu hotadd */ + global_piix4_pm_state = s; + + pci_conf = s->dev.config; + pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_INTEL); + pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_INTEL_82371AB_3); + pci_conf[0x06] = 0x80; + pci_conf[0x07] = 0x02; + pci_conf[0x08] = 0x03; // revision number + pci_conf[0x09] = 0x00; + pci_config_set_class(pci_conf, PCI_CLASS_BRIDGE_OTHER); + pci_conf[0x3d] = 0x01; // interrupt pin 1 + + pci_conf[0x40] = 0x01; /* PM io base read only bit */ + +#if defined(TARGET_IA64) + pci_conf[0x40] = 0x41; /* PM io base read only bit */ + pci_conf[0x41] = 0x1f; + pm_write_config(s, 0x80, 0x01, 1); /*Set default pm_io_base 0x1f40*/ + s->pmcntrl = SCI_EN; +#endif + + /* APM */ + apm_init(&s->apm, apm_ctrl_changed, s); + + register_ioport_write(ACPI_DBG_IO_ADDR, 4, 4, acpi_dbg_writel, s); + + if (s->kvm_enabled) { + /* Mark SMM as already inited to prevent SMM from running. KVM does not + * support SMM mode. */ + pci_conf[0x5B] = 0x02; + } + + /* XXX: which specification is used ? The i82731AB has different + mappings */ + pci_conf[0x5f] = (parallel_hds[0] != NULL ? 0x80 : 0) | 0x10; + pci_conf[0x63] = 0x60; + pci_conf[0x67] = (serial_hds[0] != NULL ? 0x08 : 0) | + (serial_hds[1] != NULL ? 0x90 : 0); + + pci_conf[0x90] = s->smb_io_base | 1; + pci_conf[0x91] = s->smb_io_base >> 8; + pci_conf[0xd2] = 0x09; + register_ioport_write(s->smb_io_base, 64, 1, smb_ioport_writeb, &s->smb); + register_ioport_read(s->smb_io_base, 64, 1, smb_ioport_readb, &s->smb); + + s->tmr_timer = qemu_new_timer(vm_clock, pm_tmr_timer, s); + + qemu_system_powerdown = *qemu_allocate_irqs(piix4_powerdown, s, 1); + + pm_smbus_init(&s->dev.qdev, &s->smb); + qemu_register_reset(piix4_reset, s); + piix4_acpi_system_hot_add_init(dev->bus, s); + + return 0; +} + +i2c_bus *piix4_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base, + qemu_irq sci_irq, qemu_irq cmos_s3, qemu_irq smi_irq, + int kvm_enabled) +{ + PCIDevice *dev; + PIIX4PMState *s; + + dev = pci_create(bus, devfn, "PIIX4_PM"); + qdev_prop_set_uint32(&dev->qdev, "smb_io_base", smb_io_base); + + s = DO_UPCAST(PIIX4PMState, dev, dev); + s->irq = sci_irq; + s->cmos_s3 = cmos_s3; + s->smi_irq = smi_irq; + s->kvm_enabled = kvm_enabled; + + qdev_init_nofail(&dev->qdev); + + return s->smb.smbus; +} + +static PCIDeviceInfo piix4_pm_info = { + .qdev.name = "PIIX4_PM", + .qdev.desc = "PM", + .qdev.size = sizeof(PIIX4PMState), + .qdev.vmsd = &vmstate_acpi, + .qdev.no_user = 1, + .no_hotplug = 1, + .init = piix4_pm_initfn, + .config_write = pm_write_config, + .qdev.props = (Property[]) { + DEFINE_PROP_UINT32("smb_io_base", PIIX4PMState, smb_io_base, 0), + DEFINE_PROP_END_OF_LIST(), + } +}; + +static void piix4_pm_register(void) +{ + pci_qdev_register(&piix4_pm_info); +} + +device_init(piix4_pm_register); + +static uint32_t gpe_read_val(uint16_t val, uint32_t addr) +{ + if (addr & 1) + return (val >> 8) & 0xff; + return val & 0xff; +} + +static uint32_t gpe_readb(void *opaque, uint32_t addr) +{ + uint32_t val = 0; + PIIX4PMState *s = opaque; + struct gpe_regs *g = &s->gpe; + + switch (addr) { + case PROC_BASE ... PROC_BASE+31: + val = g->cpus_sts[addr - PROC_BASE]; + break; + + case GPE_BASE: + case GPE_BASE + 1: + val = gpe_read_val(g->sts, addr); + break; + case GPE_BASE + 2: + case GPE_BASE + 3: + val = gpe_read_val(g->en, addr); + break; + default: + break; + } + + PIIX4_DPRINTF("gpe read %x == %x\n", addr, val); + return val; +} + +static void gpe_write_val(uint16_t *cur, int addr, uint32_t val) +{ + if (addr & 1) + *cur = (*cur & 0xff) | (val << 8); + else + *cur = (*cur & 0xff00) | (val & 0xff); +} + +static void gpe_reset_val(uint16_t *cur, int addr, uint32_t val) +{ + uint16_t x1, x0 = val & 0xff; + int shift = (addr & 1) ? 8 : 0; + + x1 = (*cur >> shift) & 0xff; + + x1 = x1 & ~x0; + + *cur = (*cur & (0xff << (8 - shift))) | (x1 << shift); +} + +static void gpe_writeb(void *opaque, uint32_t addr, uint32_t val) +{ + PIIX4PMState *s = opaque; + struct gpe_regs *g = &s->gpe; + + switch (addr) { + case GPE_BASE: + case GPE_BASE + 1: + gpe_reset_val(&g->sts, addr, val); + break; + case GPE_BASE + 2: + case GPE_BASE + 3: + gpe_write_val(&g->en, addr, val); + break; + default: + break; + } + + pm_update_sci(s); + + PIIX4_DPRINTF("gpe write %x <== %d\n", addr, val); +} + +static uint32_t pcihotplug_read(void *opaque, uint32_t addr) +{ + uint32_t val = 0; + struct pci_status *g = opaque; + switch (addr) { + case PCI_BASE: + val = g->up; + break; + case PCI_BASE + 4: + val = g->down; + break; + default: + break; + } + + PIIX4_DPRINTF("pcihotplug read %x == %x\n", addr, val); + return val; +} + +static void pcihotplug_write(void *opaque, uint32_t addr, uint32_t val) +{ + struct pci_status *g = opaque; + switch (addr) { + case PCI_BASE: + g->up = val; + break; + case PCI_BASE + 4: + g->down = val; + break; + } + + PIIX4_DPRINTF("pcihotplug write %x <== %d\n", addr, val); +} + +static uint32_t pciej_read(void *opaque, uint32_t addr) +{ + PIIX4_DPRINTF("pciej read %x\n", addr); + return 0; +} + +static void pciej_write(void *opaque, uint32_t addr, uint32_t val) +{ + BusState *bus = opaque; + DeviceState *qdev, *next; + PCIDevice *dev; + int slot = ffs(val) - 1; + + QLIST_FOREACH_SAFE(qdev, &bus->children, sibling, next) { + dev = DO_UPCAST(PCIDevice, qdev, qdev); + if (PCI_SLOT(dev->devfn) == slot) { + qdev_free(qdev); + } + } + + + PIIX4_DPRINTF("pciej write %x <== %d\n", addr, val); +} + +static uint32_t pcirmv_read(void *opaque, uint32_t addr) +{ + PIIX4PMState *s = opaque; + + return s->pci0_hotplug_enable; +} + +static void pcirmv_write(void *opaque, uint32_t addr, uint32_t val) +{ + return; +} + +extern const char *global_cpu_model; + +static int piix4_device_hotplug(DeviceState *qdev, PCIDevice *dev, + PCIHotplugState state); + +static void piix4_acpi_system_hot_add_init(PCIBus *bus, PIIX4PMState *s) +{ + struct pci_status *pci0_status = &s->pci0_status; + int i = 0, cpus = smp_cpus; + + while (cpus > 0) { + s->gpe.cpus_sts[i++] = (cpus < 8) ? (1 << cpus) - 1 : 0xff; + cpus -= 8; + } + + register_ioport_write(GPE_BASE, 4, 1, gpe_writeb, s); + register_ioport_read(GPE_BASE, 4, 1, gpe_readb, s); + + register_ioport_write(PROC_BASE, 32, 1, gpe_writeb, s); + register_ioport_read(PROC_BASE, 32, 1, gpe_readb, s); + + register_ioport_write(PCI_BASE, 8, 4, pcihotplug_write, pci0_status); + register_ioport_read(PCI_BASE, 8, 4, pcihotplug_read, pci0_status); + + register_ioport_write(PCI_EJ_BASE, 4, 4, pciej_write, bus); + register_ioport_read(PCI_EJ_BASE, 4, 4, pciej_read, bus); + + register_ioport_write(PCI_RMV_BASE, 4, 4, pcirmv_write, s); + register_ioport_read(PCI_RMV_BASE, 4, 4, pcirmv_read, s); + + pci_bus_hotplug(bus, piix4_device_hotplug, &s->dev.qdev); +} + +#if defined(TARGET_I386) +static void enable_processor(struct gpe_regs *g, int cpu) +{ + g->sts |= PIIX4_CPU_HOTPLUG_STATUS; + g->cpus_sts[cpu/8] |= (1 << (cpu%8)); +} + +static void disable_processor(struct gpe_regs *g, int cpu) +{ + g->sts |= PIIX4_CPU_HOTPLUG_STATUS; + g->cpus_sts[cpu/8] &= ~(1 << (cpu%8)); +} + +void qemu_system_cpu_hot_add(int cpu, int state) +{ + CPUState *env; + PIIX4PMState *s = global_piix4_pm_state; + + if (state && !qemu_get_cpu(cpu)) { + env = pc_new_cpu(global_cpu_model); + if (!env) { + fprintf(stderr, "cpu %d creation failed\n", cpu); + return; + } + env->cpuid_apic_id = cpu; + } + + if (state) + enable_processor(&s->gpe, cpu); + else + disable_processor(&s->gpe, cpu); + + pm_update_sci(s); +} +#endif + +static void enable_device(PIIX4PMState *s, int slot) +{ + s->gpe.sts |= PIIX4_PCI_HOTPLUG_STATUS; + s->pci0_status.up |= (1 << slot); +} + +static void disable_device(PIIX4PMState *s, int slot) +{ + s->gpe.sts |= PIIX4_PCI_HOTPLUG_STATUS; + s->pci0_status.down |= (1 << slot); +} + +static int piix4_device_hotplug(DeviceState *qdev, PCIDevice *dev, + PCIHotplugState state) +{ + int slot = PCI_SLOT(dev->devfn); + PIIX4PMState *s = DO_UPCAST(PIIX4PMState, dev, + DO_UPCAST(PCIDevice, qdev, qdev)); + + /* Don't send event when device is enabled during qemu machine creation: + * it is present on boot, no hotplug event is necessary. We do send an + * event when the device is disabled later. */ + if (state == PCI_COLDPLUG_ENABLED) { + return 0; + } + + s->pci0_status.up = 0; + s->pci0_status.down = 0; + if (state == PCI_HOTPLUG_ENABLED) { + enable_device(s, slot); + } else { + disable_device(s, slot); + } + + pm_update_sci(s); + + return 0; +} diff -Nru qemu-kvm-0.12.5+noroms/hw/adb.c qemu-kvm-0.14.1/hw/adb.c --- qemu-kvm-0.12.5+noroms/hw/adb.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/adb.c 2011-05-11 13:29:46.000000000 +0000 @@ -305,7 +305,7 @@ d = adb_register_device(bus, ADB_KEYBOARD, adb_kbd_request, adb_kbd_reset, s); qemu_add_kbd_event_handler(adb_kbd_put_keycode, d); - register_savevm("adb_kbd", -1, 1, adb_kbd_save, + register_savevm(NULL, "adb_kbd", -1, 1, adb_kbd_save, adb_kbd_load, s); } @@ -475,6 +475,6 @@ d = adb_register_device(bus, ADB_MOUSE, adb_mouse_request, adb_mouse_reset, s); qemu_add_mouse_event_handler(adb_mouse_event, d, 0, "QEMU ADB Mouse"); - register_savevm("adb_mouse", -1, 1, adb_mouse_save, + register_savevm(NULL, "adb_mouse", -1, 1, adb_mouse_save, adb_mouse_load, s); } diff -Nru qemu-kvm-0.12.5+noroms/hw/ads7846.c qemu-kvm-0.14.1/hw/ads7846.c --- qemu-kvm-0.12.5+noroms/hw/ads7846.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/ads7846.c 2011-05-11 13:29:46.000000000 +0000 @@ -151,7 +151,7 @@ ads7846_int_update(s); - register_savevm("ads7846", -1, 0, ads7846_save, ads7846_load, s); + register_savevm(NULL, "ads7846", -1, 0, ads7846_save, ads7846_load, s); return 0; } diff -Nru qemu-kvm-0.12.5+noroms/hw/alpha_palcode.c qemu-kvm-0.14.1/hw/alpha_palcode.c --- qemu-kvm-0.12.5+noroms/hw/alpha_palcode.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/alpha_palcode.c 2011-05-11 13:29:46.000000000 +0000 @@ -21,11 +21,9 @@ #include #include -#include "qemu.h" #include "cpu.h" #include "exec-all.h" -#if !defined (CONFIG_USER_ONLY) /* Shared handlers */ static void pal_reset (CPUState *env); /* Console handlers */ @@ -81,9 +79,7 @@ static void do_swappal (CPUState *env, uint64_t palid) { pal_handler_t *pal_handler; - int status; - status = 0; switch (palid) { case 0 ... 2: pal_handler = &pal_handlers[palid]; @@ -997,22 +993,22 @@ uint64_t physical, page_size, end; int prot, zbits, ret; -#if defined(CONFIG_USER_ONLY) - ret = 2; -#else - ret = virtual_to_physical(env, &physical, &zbits, &prot, - address, mmu_idx, rw); -#endif + ret = virtual_to_physical(env, &physical, &zbits, &prot, + address, mmu_idx, rw); + switch (ret) { case 0: /* No fault */ page_size = 1ULL << zbits; address &= ~(page_size - 1); + /* FIXME: page_size should probably be passed to tlb_set_page, + and this loop removed. */ for (end = physical + page_size; physical < end; physical += 0x1000) { - ret = tlb_set_page(env, address, physical, prot, - mmu_idx, is_softmmu); + tlb_set_page(env, address, physical, prot, mmu_idx, + TARGET_PAGE_SIZE); address += 0x1000; } + ret = 0; break; #if 0 case 1: @@ -1050,46 +1046,3 @@ return ret; } #endif - -#else /* !defined (CONFIG_USER_ONLY) */ -void pal_init (CPUState *env) -{ -} - -void call_pal (CPUState *env, int palcode) -{ - target_long ret; - - qemu_log("%s: palcode %02x\n", __func__, palcode); - switch (palcode) { - case 0x83: - /* CALLSYS */ - qemu_log("CALLSYS n " TARGET_FMT_ld "\n", env->ir[0]); - ret = do_syscall(env, env->ir[IR_V0], env->ir[IR_A0], env->ir[IR_A1], - env->ir[IR_A2], env->ir[IR_A3], env->ir[IR_A4], - env->ir[IR_A5]); - if (ret >= 0) { - env->ir[IR_A3] = 0; - env->ir[IR_V0] = ret; - } else { - env->ir[IR_A3] = 1; - env->ir[IR_V0] = -ret; - } - break; - case 0x9E: - /* RDUNIQUE */ - env->ir[IR_V0] = env->unique; - qemu_log("RDUNIQUE: " TARGET_FMT_lx "\n", env->unique); - break; - case 0x9F: - /* WRUNIQUE */ - env->unique = env->ir[IR_A0]; - qemu_log("WRUNIQUE: " TARGET_FMT_lx "\n", env->unique); - break; - default: - qemu_log("%s: unhandled palcode %02x\n", - __func__, palcode); - exit(1); - } -} -#endif diff -Nru qemu-kvm-0.12.5+noroms/hw/an5206.c qemu-kvm-0.14.1/hw/an5206.c --- qemu-kvm-0.12.5+noroms/hw/an5206.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/an5206.c 2011-05-11 13:29:46.000000000 +0000 @@ -54,11 +54,11 @@ /* DRAM at address zero */ cpu_register_physical_memory(0, ram_size, - qemu_ram_alloc(ram_size) | IO_MEM_RAM); + qemu_ram_alloc(NULL, "an5206.ram", ram_size) | IO_MEM_RAM); /* Internal SRAM. */ cpu_register_physical_memory(AN5206_RAMBAR_ADDR, 512, - qemu_ram_alloc(512) | IO_MEM_RAM); + qemu_ram_alloc(NULL, "an5206.sram", 512) | IO_MEM_RAM); mcf5206_init(AN5206_MBAR_ADDR, env); @@ -68,8 +68,8 @@ exit(1); } - kernel_size = load_elf(kernel_filename, 0, &elf_entry, NULL, NULL, - 1, ELF_MACHINE, 0); + kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry, + NULL, NULL, 1, ELF_MACHINE, 0); entry = elf_entry; if (kernel_size < 0) { kernel_size = load_uimage(kernel_filename, &entry, NULL, NULL); diff -Nru qemu-kvm-0.12.5+noroms/hw/apb_pci.c qemu-kvm-0.14.1/hw/apb_pci.c --- qemu-kvm-0.12.5+noroms/hw/apb_pci.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/apb_pci.c 2011-05-11 13:29:46.000000000 +0000 @@ -29,7 +29,11 @@ #include "sysbus.h" #include "pci.h" #include "pci_host.h" +#include "pci_bridge.h" +#include "pci_internals.h" +#include "rwhandler.h" #include "apb_pci.h" +#include "sysemu.h" /* debug APB */ //#define DEBUG_APB @@ -50,23 +54,74 @@ * http://www.sun.com/processors/manuals/805-1251.pdf */ +#define PBM_PCI_IMR_MASK 0x7fffffff +#define PBM_PCI_IMR_ENABLED 0x80000000 + +#define POR (1 << 31) +#define SOFT_POR (1 << 30) +#define SOFT_XIR (1 << 29) +#define BTN_POR (1 << 28) +#define BTN_XIR (1 << 27) +#define RESET_MASK 0xf8000000 +#define RESET_WCMASK 0x98000000 +#define RESET_WMASK 0x60000000 + typedef struct APBState { SysBusDevice busdev; - PCIHostState host_state; + PCIBus *bus; + ReadWriteHandler pci_config_handler; + uint32_t iommu[4]; + uint32_t pci_control[16]; + uint32_t pci_irq_map[8]; + uint32_t obio_irq_map[32]; + qemu_irq pci_irqs[32]; + uint32_t reset_control; + unsigned int nr_resets; } APBState; static void apb_config_writel (void *opaque, target_phys_addr_t addr, uint32_t val) { - //PCIBus *s = opaque; + APBState *s = opaque; - switch (addr & 0x3f) { - case 0x00: // Control/Status - case 0x10: // AFSR - case 0x18: // AFAR - case 0x20: // Diagnostic - case 0x28: // Target address space - // XXX + APB_DPRINTF("%s: addr " TARGET_FMT_lx " val %x\n", __func__, addr, val); + + switch (addr & 0xffff) { + case 0x30 ... 0x4f: /* DMA error registers */ + /* XXX: not implemented yet */ + break; + case 0x200 ... 0x20b: /* IOMMU */ + s->iommu[(addr & 0xf) >> 2] = val; + break; + case 0x20c ... 0x3ff: /* IOMMU flush */ + break; + case 0xc00 ... 0xc3f: /* PCI interrupt control */ + if (addr & 4) { + s->pci_irq_map[(addr & 0x3f) >> 3] &= PBM_PCI_IMR_MASK; + s->pci_irq_map[(addr & 0x3f) >> 3] |= val & ~PBM_PCI_IMR_MASK; + } + break; + case 0x2000 ... 0x202f: /* PCI control */ + s->pci_control[(addr & 0x3f) >> 2] = val; + break; + case 0xf020 ... 0xf027: /* Reset control */ + if (addr & 4) { + val &= RESET_MASK; + s->reset_control &= ~(val & RESET_WCMASK); + s->reset_control |= val & RESET_WMASK; + if (val & SOFT_POR) { + s->nr_resets = 0; + qemu_system_reset_request(); + } else if (val & SOFT_XIR) { + qemu_system_reset_request(); + } + } + break; + case 0x5000 ... 0x51cf: /* PIO/DMA diagnostics */ + case 0xa400 ... 0xa67f: /* IOMMU diagnostics */ + case 0xa800 ... 0xa80f: /* Interrupt diagnostics */ + case 0xf000 ... 0xf01f: /* FFB config, memory control */ + /* we don't care */ default: break; } @@ -75,20 +130,48 @@ static uint32_t apb_config_readl (void *opaque, target_phys_addr_t addr) { - //PCIBus *s = opaque; + APBState *s = opaque; uint32_t val; - switch (addr & 0x3f) { - case 0x00: // Control/Status - case 0x10: // AFSR - case 0x18: // AFAR - case 0x20: // Diagnostic - case 0x28: // Target address space - // XXX + switch (addr & 0xffff) { + case 0x30 ... 0x4f: /* DMA error registers */ + val = 0; + /* XXX: not implemented yet */ + break; + case 0x200 ... 0x20b: /* IOMMU */ + val = s->iommu[(addr & 0xf) >> 2]; + break; + case 0x20c ... 0x3ff: /* IOMMU flush */ + val = 0; + break; + case 0xc00 ... 0xc3f: /* PCI interrupt control */ + if (addr & 4) { + val = s->pci_irq_map[(addr & 0x3f) >> 3]; + } else { + val = 0; + } + break; + case 0x2000 ... 0x202f: /* PCI control */ + val = s->pci_control[(addr & 0x3f) >> 2]; + break; + case 0xf020 ... 0xf027: /* Reset control */ + if (addr & 4) { + val = s->reset_control; + } else { + val = 0; + } + break; + case 0x5000 ... 0x51cf: /* PIO/DMA diagnostics */ + case 0xa400 ... 0xa67f: /* IOMMU diagnostics */ + case 0xa800 ... 0xa80f: /* Interrupt diagnostics */ + case 0xf000 ... 0xf01f: /* FFB config, memory control */ + /* we don't care */ default: val = 0; break; } + APB_DPRINTF("%s: addr " TARGET_FMT_lx " -> %x\n", __func__, addr, val); + return val; } @@ -104,6 +187,28 @@ &apb_config_readl, }; +static void apb_pci_config_write(ReadWriteHandler *h, pcibus_t addr, + uint32_t val, int size) +{ + APBState *s = container_of(h, APBState, pci_config_handler); + + val = qemu_bswap_len(val, size); + APB_DPRINTF("%s: addr " TARGET_FMT_lx " val %x\n", __func__, addr, val); + pci_data_write(s->bus, addr, val, size); +} + +static uint32_t apb_pci_config_read(ReadWriteHandler *h, pcibus_t addr, + int size) +{ + uint32_t ret; + APBState *s = container_of(h, APBState, pci_config_handler); + + ret = pci_data_read(s->bus, addr, size); + ret = qemu_bswap_len(ret, size); + APB_DPRINTF("%s: addr " TARGET_FMT_lx " -> %x\n", __func__, addr, ret); + return ret; +} + static void pci_apb_iowriteb (void *opaque, target_phys_addr_t addr, uint32_t val) { @@ -113,13 +218,13 @@ static void pci_apb_iowritew (void *opaque, target_phys_addr_t addr, uint32_t val) { - cpu_outw(addr & IOPORTS_MASK, val); + cpu_outw(addr & IOPORTS_MASK, bswap16(val)); } static void pci_apb_iowritel (void *opaque, target_phys_addr_t addr, uint32_t val) { - cpu_outl(addr & IOPORTS_MASK, val); + cpu_outl(addr & IOPORTS_MASK, bswap32(val)); } static uint32_t pci_apb_ioreadb (void *opaque, target_phys_addr_t addr) @@ -134,7 +239,7 @@ { uint32_t val; - val = cpu_inw(addr & IOPORTS_MASK); + val = bswap16(cpu_inw(addr & IOPORTS_MASK)); return val; } @@ -142,7 +247,7 @@ { uint32_t val; - val = cpu_inl(addr & IOPORTS_MASK); + val = bswap32(cpu_inl(addr & IOPORTS_MASK)); return val; } @@ -176,15 +281,31 @@ static void pci_apb_set_irq(void *opaque, int irq_num, int level) { - qemu_irq *pic = opaque; + APBState *s = opaque; /* PCI IRQ map onto the first 32 INO. */ - qemu_set_irq(pic[irq_num], level); + if (irq_num < 32) { + if (s->pci_irq_map[irq_num >> 2] & PBM_PCI_IMR_ENABLED) { + APB_DPRINTF("%s: set irq %d level %d\n", __func__, irq_num, level); + qemu_set_irq(s->pci_irqs[irq_num], level); + } else { + APB_DPRINTF("%s: not enabled: lower irq %d\n", __func__, irq_num); + qemu_irq_lower(s->pci_irqs[irq_num]); + } + } } -static void apb_pci_bridge_init(PCIBus *b) +static int apb_pci_bridge_initfn(PCIDevice *dev) { - PCIDevice *dev = pci_bridge_get_device(b); + int rc; + + rc = pci_bridge_initfn(dev); + if (rc < 0) { + return rc; + } + + pci_config_set_vendor_id(dev->config, PCI_VENDOR_ID_SUN); + pci_config_set_device_id(dev->config, PCI_DEVICE_ID_SUN_SIMBA); /* * command register: @@ -196,9 +317,12 @@ * (which is true) and thus it should be PCI_COMMAND_MEMORY. */ pci_set_word(dev->config + PCI_COMMAND, - PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); - dev->config[PCI_LATENCY_TIMER] = 0x10; - dev->config[PCI_HEADER_TYPE] |= PCI_HEADER_TYPE_MULTI_FUNCTION; + PCI_COMMAND_MEMORY); + pci_set_word(dev->config + PCI_STATUS, + PCI_STATUS_FAST_BACK | PCI_STATUS_66MHZ | + PCI_STATUS_DEVSEL_MEDIUM); + pci_set_byte(dev->config + PCI_REVISION_ID, 0x11); + return 0; } PCIBus *pci_apb_init(target_phys_addr_t special_base, @@ -208,61 +332,105 @@ DeviceState *dev; SysBusDevice *s; APBState *d; + unsigned int i; + PCIDevice *pci_dev; + PCIBridge *br; /* Ultrasparc PBM main bus */ dev = qdev_create(NULL, "pbm"); qdev_init_nofail(dev); s = sysbus_from_qdev(dev); /* apb_config */ - sysbus_mmio_map(s, 0, special_base + 0x2000ULL); + sysbus_mmio_map(s, 0, special_base); + /* PCI configuration space */ + sysbus_mmio_map(s, 1, special_base + 0x1000000ULL); /* pci_ioport */ - sysbus_mmio_map(s, 1, special_base + 0x2000000ULL); - /* mem_config: XXX size should be 4G-prom */ - sysbus_mmio_map(s, 2, special_base + 0x1000000ULL); - /* mem_data */ - sysbus_mmio_map(s, 3, mem_base); + sysbus_mmio_map(s, 2, special_base + 0x2000000ULL); d = FROM_SYSBUS(APBState, s); - d->host_state.bus = pci_register_bus(&d->busdev.qdev, "pci", - pci_apb_set_irq, pci_pbm_map_irq, pic, + + d->bus = pci_register_bus(&d->busdev.qdev, "pci", + pci_apb_set_irq, pci_pbm_map_irq, d, 0, 32); - pci_create_simple(d->host_state.bus, 0, "pbm"); + pci_bus_set_mem_base(d->bus, mem_base); + + for (i = 0; i < 32; i++) { + sysbus_connect_irq(s, i, pic[i]); + } + + pci_create_simple(d->bus, 0, "pbm"); + /* APB secondary busses */ - *bus2 = pci_bridge_init(d->host_state.bus, PCI_DEVFN(1, 0), - PCI_VENDOR_ID_SUN, PCI_DEVICE_ID_SUN_SIMBA, - pci_apb_map_irq, - "Advanced PCI Bus secondary bridge 1"); - apb_pci_bridge_init(*bus2); - - *bus3 = pci_bridge_init(d->host_state.bus, PCI_DEVFN(1, 1), - PCI_VENDOR_ID_SUN, PCI_DEVICE_ID_SUN_SIMBA, - pci_apb_map_irq, - "Advanced PCI Bus secondary bridge 2"); - apb_pci_bridge_init(*bus3); + pci_dev = pci_create_multifunction(d->bus, PCI_DEVFN(1, 0), true, + "pbm-bridge"); + br = DO_UPCAST(PCIBridge, dev, pci_dev); + pci_bridge_map_irq(br, "Advanced PCI Bus secondary bridge 1", + pci_apb_map_irq); + qdev_init_nofail(&pci_dev->qdev); + *bus2 = pci_bridge_get_sec_bus(br); + + pci_dev = pci_create_multifunction(d->bus, PCI_DEVFN(1, 1), true, + "pbm-bridge"); + br = DO_UPCAST(PCIBridge, dev, pci_dev); + pci_bridge_map_irq(br, "Advanced PCI Bus secondary bridge 2", + pci_apb_map_irq); + qdev_init_nofail(&pci_dev->qdev); + *bus3 = pci_bridge_get_sec_bus(br); + + return d->bus; +} + +static void pci_pbm_reset(DeviceState *d) +{ + unsigned int i; + APBState *s = container_of(d, APBState, busdev.qdev); + + for (i = 0; i < 8; i++) { + s->pci_irq_map[i] &= PBM_PCI_IMR_MASK; + } - return d->host_state.bus; + if (s->nr_resets++ == 0) { + /* Power on reset */ + s->reset_control = POR; + } } static int pci_pbm_init_device(SysBusDevice *dev) { - APBState *s; - int pci_mem_config, pci_mem_data, apb_config, pci_ioport; + int pci_config, apb_config, pci_ioport; + unsigned int i; s = FROM_SYSBUS(APBState, dev); + for (i = 0; i < 8; i++) { + s->pci_irq_map[i] = (0x1f << 6) | (i << 2); + } + for (i = 0; i < 32; i++) { + sysbus_init_irq(dev, &s->pci_irqs[i]); + } + /* apb_config */ apb_config = cpu_register_io_memory(apb_config_read, - apb_config_write, s); - sysbus_init_mmio(dev, 0x40ULL, apb_config); + apb_config_write, s, + DEVICE_NATIVE_ENDIAN); + /* at region 0 */ + sysbus_init_mmio(dev, 0x10000ULL, apb_config); + + /* PCI configuration space */ + s->pci_config_handler.read = apb_pci_config_read; + s->pci_config_handler.write = apb_pci_config_write; + pci_config = cpu_register_io_memory_simple(&s->pci_config_handler, + DEVICE_NATIVE_ENDIAN); + assert(pci_config >= 0); + /* at region 1 */ + sysbus_init_mmio(dev, 0x1000000ULL, pci_config); + /* pci_ioport */ pci_ioport = cpu_register_io_memory(pci_apb_ioread, - pci_apb_iowrite, s); + pci_apb_iowrite, s, + DEVICE_NATIVE_ENDIAN); + /* at region 2 */ sysbus_init_mmio(dev, 0x10000ULL, pci_ioport); - /* mem_config */ - pci_mem_config = pci_host_conf_register_mmio(&s->host_state); - sysbus_init_mmio(dev, 0x10ULL, pci_mem_config); - /* mem_data */ - pci_mem_data = pci_host_data_register_mmio(&s->host_state); - sysbus_init_mmio(dev, 0x10000000ULL, pci_mem_data); + return 0; } @@ -270,15 +438,12 @@ { pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_SUN); pci_config_set_device_id(d->config, PCI_DEVICE_ID_SUN_SABRE); - d->config[0x04] = 0x06; // command = bus master, pci mem - d->config[0x05] = 0x00; - d->config[0x06] = 0xa0; // status = fast back-to-back, 66MHz, no error - d->config[0x07] = 0x03; // status = medium devsel - d->config[0x08] = 0x00; // revision - d->config[0x09] = 0x00; // programming i/f + pci_set_word(d->config + PCI_COMMAND, + PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); + pci_set_word(d->config + PCI_STATUS, + PCI_STATUS_FAST_BACK | PCI_STATUS_66MHZ | + PCI_STATUS_DEVSEL_MEDIUM); pci_config_set_class(d->config, PCI_CLASS_BRIDGE_HOST); - d->config[0x0D] = 0x10; // latency_timer - d->config[PCI_HEADER_TYPE] = PCI_HEADER_TYPE_NORMAL; // header_type return 0; } @@ -286,12 +451,32 @@ .qdev.name = "pbm", .qdev.size = sizeof(PCIDevice), .init = pbm_pci_host_init, + .is_bridge = 1, +}; + +static SysBusDeviceInfo pbm_host_info = { + .qdev.name = "pbm", + .qdev.size = sizeof(APBState), + .qdev.reset = pci_pbm_reset, + .init = pci_pbm_init_device, +}; + +static PCIDeviceInfo pbm_pci_bridge_info = { + .qdev.name = "pbm-bridge", + .qdev.size = sizeof(PCIBridge), + .qdev.vmsd = &vmstate_pci_device, + .qdev.reset = pci_bridge_reset, + .init = apb_pci_bridge_initfn, + .exit = pci_bridge_exitfn, + .config_write = pci_bridge_write_config, + .is_bridge = 1, }; static void pbm_register_devices(void) { - sysbus_register_dev("pbm", sizeof(APBState), pci_pbm_init_device); + sysbus_register_withprop(&pbm_host_info); pci_qdev_register(&pbm_pci_host_info); + pci_qdev_register(&pbm_pci_bridge_info); } device_init(pbm_register_devices) diff -Nru qemu-kvm-0.12.5+noroms/hw/apic.c qemu-kvm-0.14.1/hw/apic.c --- qemu-kvm-0.12.5+noroms/hw/apic.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/apic.c 2011-05-11 13:29:46.000000000 +0000 @@ -17,17 +17,14 @@ * License along with this library; if not, see */ #include "hw.h" -#include "pc.h" -#include "pci.h" -#include "msix.h" +#include "apic.h" +#include "ioapic.h" #include "qemu-timer.h" #include "host-utils.h" +#include "sysbus.h" +#include "trace.h" #include "kvm.h" -#include "qemu-kvm.h" - -//#define DEBUG_APIC - /* APIC Local Vector Table */ #define APIC_LVT_TIMER 0 #define APIC_LVT_THERMAL 1 @@ -62,7 +59,8 @@ #define ESR_ILLEGAL_ADDRESS (1 << 7) -#define APIC_SV_ENABLE (1 << 8) +#define APIC_SV_DIRECTED_IO (1<<12) +#define APIC_SV_ENABLE (1<<8) #define MAX_APICS 255 #define MAX_APIC_WORDS 8 @@ -77,11 +75,13 @@ #define MSI_ADDR_DEST_ID_SHIFT 12 #define MSI_ADDR_DEST_ID_MASK 0x00ffff0 -#define MSI_ADDR_BASE 0xfee00000 #define MSI_ADDR_SIZE 0x100000 -typedef struct APICState { - CPUState *cpu_env; +typedef struct APICState APICState; + +struct APICState { + SysBusDevice busdev; + void *cpu_env; uint32_t apicbase; uint8_t id; uint8_t arb_id; @@ -104,14 +104,11 @@ QEMUTimer *timer; int sipi_vector; int wait_for_sipi; -} APICState; +}; -static int apic_io_memory; static APICState *local_apics[MAX_APICS + 1]; -static int last_apic_idx = 0; static int apic_irq_delivered; - static void apic_set_irq(APICState *s, int vector_num, int trigger_mode); static void apic_update_irq(APICState *s); static void apic_get_delivery_bitmask(uint32_t *deliver_bitmask, @@ -153,26 +150,27 @@ return !!(tab[i] & mask); } -static void apic_local_deliver(CPUState *env, int vector) +static void apic_local_deliver(APICState *s, int vector) { - APICState *s = env->apic_state; uint32_t lvt = s->lvt[vector]; int trigger_mode; + trace_apic_local_deliver(vector, (lvt >> 8) & 7); + if (lvt & APIC_LVT_MASKED) return; switch ((lvt >> 8) & 7) { case APIC_DM_SMI: - cpu_interrupt(env, CPU_INTERRUPT_SMI); + cpu_interrupt(s->cpu_env, CPU_INTERRUPT_SMI); break; case APIC_DM_NMI: - cpu_interrupt(env, CPU_INTERRUPT_NMI); + cpu_interrupt(s->cpu_env, CPU_INTERRUPT_NMI); break; case APIC_DM_EXTINT: - cpu_interrupt(env, CPU_INTERRUPT_HARD); + cpu_interrupt(s->cpu_env, CPU_INTERRUPT_HARD); break; case APIC_DM_FIXED: @@ -184,12 +182,13 @@ } } -void apic_deliver_pic_intr(CPUState *env, int level) +void apic_deliver_pic_intr(DeviceState *d, int level) { - if (level) - apic_local_deliver(env, APIC_LVT_LINT0); - else { - APICState *s = env->apic_state; + APICState *s = DO_UPCAST(APICState, busdev.qdev, d); + + if (level) { + apic_local_deliver(s, APIC_LVT_LINT0); + } else { uint32_t lvt = s->lvt[APIC_LVT_LINT0]; switch ((lvt >> 8) & 7) { @@ -199,7 +198,7 @@ reset_bit(s->irr, lvt & 0xff); /* fall through */ case APIC_DM_EXTINT: - cpu_reset_interrupt(env, CPU_INTERRUPT_HARD); + cpu_reset_interrupt(s->cpu_env, CPU_INTERRUPT_HARD); break; } } @@ -288,17 +287,20 @@ { uint32_t deliver_bitmask[MAX_APIC_WORDS]; + trace_apic_deliver_irq(dest, dest_mode, delivery_mode, vector_num, + polarity, trigger_mode); + apic_get_delivery_bitmask(deliver_bitmask, dest, dest_mode); apic_bus_deliver(deliver_bitmask, delivery_mode, vector_num, polarity, trigger_mode); } -void cpu_set_apic_base(CPUState *env, uint64_t val) +void cpu_set_apic_base(DeviceState *d, uint64_t val) { - APICState *s = env->apic_state; -#ifdef DEBUG_APIC - printf("cpu_set_apic_base: %016" PRIx64 "\n", val); -#endif + APICState *s = DO_UPCAST(APICState, busdev.qdev, d); + + trace_cpu_set_apic_base(val); + if (!s) return; if (kvm_enabled() && kvm_irqchip_in_kernel()) @@ -309,33 +311,34 @@ /* if disabled, cannot be enabled again */ if (!(val & MSR_IA32_APICBASE_ENABLE)) { s->apicbase &= ~MSR_IA32_APICBASE_ENABLE; - env->cpuid_features &= ~CPUID_APIC; + cpu_clear_apic_feature(s->cpu_env); s->spurious_vec &= ~APIC_SV_ENABLE; } } -uint64_t cpu_get_apic_base(CPUState *env) +uint64_t cpu_get_apic_base(DeviceState *d) { - APICState *s = env->apic_state; -#ifdef DEBUG_APIC - printf("cpu_get_apic_base: %016" PRIx64 "\n", - s ? (uint64_t)s->apicbase: 0); -#endif + APICState *s = DO_UPCAST(APICState, busdev.qdev, d); + + trace_cpu_get_apic_base(s ? (uint64_t)s->apicbase: 0); + return s ? s->apicbase : 0; } -void cpu_set_apic_tpr(CPUX86State *env, uint8_t val) +void cpu_set_apic_tpr(DeviceState *d, uint8_t val) { - APICState *s = env->apic_state; + APICState *s = DO_UPCAST(APICState, busdev.qdev, d); + if (!s) return; s->tpr = (val & 0x0f) << 4; apic_update_irq(s); } -uint8_t cpu_get_apic_tpr(CPUX86State *env) +uint8_t cpu_get_apic_tpr(DeviceState *d) { - APICState *s = env->apic_state; + APICState *s = DO_UPCAST(APICState, busdev.qdev, d); + return s ? s->tpr >> 4 : 0; } @@ -373,28 +376,49 @@ return 0; } -/* signal the CPU if an irq is pending */ -static void apic_update_irq(APICState *s) + +/* + * <0 - low prio interrupt, + * 0 - no interrupt, + * >0 - interrupt number + */ +static int apic_irq_pending(APICState *s) { int irrv, ppr; - if (!(s->spurious_vec & APIC_SV_ENABLE)) - return; irrv = get_highest_priority_int(s->irr); - if (irrv < 0) - return; + if (irrv < 0) { + return 0; + } ppr = apic_get_ppr(s); - if (ppr && (irrv & 0xf0) <= (ppr & 0xf0)) + if (ppr && (irrv & 0xf0) <= (ppr & 0xf0)) { + return -1; + } + + return irrv; +} + +/* signal the CPU if an irq is pending */ +static void apic_update_irq(APICState *s) +{ + if (!(s->spurious_vec & APIC_SV_ENABLE)) { return; - cpu_interrupt(s->cpu_env, CPU_INTERRUPT_HARD); + } + if (apic_irq_pending(s) > 0) { + cpu_interrupt(s->cpu_env, CPU_INTERRUPT_HARD); + } } void apic_reset_irq_delivered(void) { + trace_apic_reset_irq_delivered(apic_irq_delivered); + apic_irq_delivered = 0; } int apic_get_irq_delivered(void) { + trace_apic_get_irq_delivered(apic_irq_delivered); + return apic_irq_delivered; } @@ -407,6 +431,8 @@ { apic_irq_delivered += !get_bit(s->irr, vector_num); + trace_apic_set_irq(apic_irq_delivered); + set_bit(s->irr, vector_num); if (trigger_mode) set_bit(s->tmr, vector_num); @@ -422,8 +448,9 @@ if (isrv < 0) return; reset_bit(s->isr, isrv); - /* XXX: send the EOI packet to the APIC bus to allow the I/O APIC to - set the remote IRR bit for level triggered interrupts. */ + if (!(s->spurious_vec & APIC_SV_DIRECTED_IO) && get_bit(s->tmr, isrv)) { + ioapic_eoi_broadcast(isrv); + } apic_update_irq(s); } @@ -439,6 +466,8 @@ apic = local_apics[i]; if (apic && apic->id == dest) return i; + if (!apic) + break; } return -1; @@ -474,21 +503,21 @@ set_bit(deliver_bitmask, i); } } + } else { + break; } } } } - -void apic_init_reset(CPUState *env) +void apic_init_reset(DeviceState *d) { - APICState *s = env->apic_state; + APICState *s = DO_UPCAST(APICState, busdev.qdev, d); int i; if (!s) return; - cpu_synchronize_state(env); s->tpr = 0; s->spurious_vec = 0xff; s->log_dest = 0; @@ -506,15 +535,6 @@ s->initial_count_load_time = 0; s->next_time = 0; s->wait_for_sipi = 1; - - env->halted = !(s->apicbase & MSR_IA32_APICBASE_BSP); -#ifdef KVM_CAP_MP_STATE - if (kvm_enabled() && kvm_irqchip_in_kernel()) { - env->mp_state - = env->halted ? KVM_MP_STATE_UNINITIALIZED : KVM_MP_STATE_RUNNABLE; - kvm_load_mpstate(env); - } -#endif } static void apic_startup(APICState *s, int vector_num) @@ -523,26 +543,23 @@ cpu_interrupt(s->cpu_env, CPU_INTERRUPT_SIPI); } -void apic_sipi(CPUState *env) +void apic_sipi(DeviceState *d) { - APICState *s = env->apic_state; + APICState *s = DO_UPCAST(APICState, busdev.qdev, d); - cpu_reset_interrupt(env, CPU_INTERRUPT_SIPI); + cpu_reset_interrupt(s->cpu_env, CPU_INTERRUPT_SIPI); if (!s->wait_for_sipi) return; - - env->eip = 0; - cpu_x86_load_seg_cache(env, R_CS, s->sipi_vector << 8, s->sipi_vector << 12, - env->segs[R_CS].limit, env->segs[R_CS].flags); - env->halted = 0; + cpu_x86_load_seg_cache_sipi(s->cpu_env, s->sipi_vector); s->wait_for_sipi = 0; } -static void apic_deliver(APICState *s, uint8_t dest, uint8_t dest_mode, +static void apic_deliver(DeviceState *d, uint8_t dest, uint8_t dest_mode, uint8_t delivery_mode, uint8_t vector_num, uint8_t polarity, uint8_t trigger_mode) { + APICState *s = DO_UPCAST(APICState, busdev.qdev, d); uint32_t deliver_bitmask[MAX_APIC_WORDS]; int dest_shorthand = (s->icr[0] >> 18) & 3; APICState *apic_iter; @@ -587,9 +604,9 @@ trigger_mode); } -int apic_get_interrupt(CPUState *env) +int apic_get_interrupt(DeviceState *d) { - APICState *s = env->apic_state; + APICState *s = DO_UPCAST(APICState, busdev.qdev, d); int intno; /* if the APIC is installed or enabled, we let the 8259 handle the @@ -599,21 +616,22 @@ if (!(s->spurious_vec & APIC_SV_ENABLE)) return -1; - /* XXX: spurious IRQ handling */ - intno = get_highest_priority_int(s->irr); - if (intno < 0) + intno = apic_irq_pending(s); + + if (intno == 0) { return -1; - if (s->tpr && intno <= s->tpr) + } else if (intno < 0) { return s->spurious_vec & 0xff; + } reset_bit(s->irr, intno); set_bit(s->isr, intno); apic_update_irq(s); return intno; } -int apic_accept_pic_intr(CPUState *env) +int apic_accept_pic_intr(DeviceState *d) { - APICState *s = env->apic_state; + APICState *s = DO_UPCAST(APICState, busdev.qdev, d); uint32_t lvt0; if (!s) @@ -675,7 +693,7 @@ { APICState *s = opaque; - apic_local_deliver(s->cpu_env, APIC_LVT_TIMER); + apic_local_deliver(s, APIC_LVT_TIMER); apic_timer_update(s, s->next_time); } @@ -699,15 +717,16 @@ static uint32_t apic_mem_readl(void *opaque, target_phys_addr_t addr) { - CPUState *env; + DeviceState *d; APICState *s; uint32_t val; int index; - env = cpu_single_env; - if (!env) + d = cpu_get_current_apic(); + if (!d) { return 0; - s = env->apic_state; + } + s = DO_UPCAST(APICState, busdev.qdev, d); index = (addr >> 4) & 0xff; switch(index) { @@ -772,13 +791,11 @@ val = 0; break; } -#ifdef DEBUG_APIC - printf("APIC read: %08x = %08x\n", (uint32_t)addr, val); -#endif + trace_apic_mem_readl(addr, val); return val; } -static void apic_send_msi(target_phys_addr_t addr, uint32 data) +static void apic_send_msi(target_phys_addr_t addr, uint32_t data) { uint8_t dest = (addr & MSI_ADDR_DEST_ID_MASK) >> MSI_ADDR_DEST_ID_SHIFT; uint8_t vector = (data & MSI_DATA_VECTOR_MASK) >> MSI_DATA_VECTOR_SHIFT; @@ -791,7 +808,7 @@ static void apic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) { - CPUState *env; + DeviceState *d; APICState *s; int index = (addr >> 4) & 0xff; if (addr > 0xfff || !index) { @@ -804,14 +821,13 @@ return; } - env = cpu_single_env; - if (!env) + d = cpu_get_current_apic(); + if (!d) { return; - s = env->apic_state; + } + s = DO_UPCAST(APICState, busdev.qdev, d); -#ifdef DEBUG_APIC - printf("APIC write: %08x = %08x\n", (uint32_t)addr, val); -#endif + trace_apic_mem_writel(addr, val); switch(index) { case 0x02: @@ -846,7 +862,7 @@ break; case 0x30: s->icr[0] = val; - apic_deliver(s, (s->icr[1] >> 24) & 0xff, (s->icr[0] >> 11) & 1, + apic_deliver(d, (s->icr[1] >> 24) & 0xff, (s->icr[0] >> 11) & 1, (s->icr[0] >> 8) & 7, (s->icr[0] & 0xff), (s->icr[0] >> 14) & 1, (s->icr[0] >> 15) & 1); break; @@ -959,36 +975,34 @@ #endif -void qemu_kvm_load_lapic(CPUState *env) +void kvm_load_lapic(CPUState *env) { #ifdef KVM_CAP_IRQCHIP - if (kvm_enabled() && kvm_vcpu_inited(env) && kvm_irqchip_in_kernel()) { - kvm_kernel_lapic_load_from_user(env->apic_state); - } -#endif -} + APICState *s = DO_UPCAST(APICState, busdev.qdev, env->apic_state); -static void apic_pre_save(void *opaque) -{ -#ifdef KVM_CAP_IRQCHIP - APICState *s = (void *)opaque; + if (!s) { + return; + } if (kvm_enabled() && kvm_irqchip_in_kernel()) { - kvm_kernel_lapic_save_to_user(s); + kvm_kernel_lapic_load_from_user(s); } #endif } -static int apic_post_load(void *opaque, int version_id) +void kvm_save_lapic(CPUState *env) { #ifdef KVM_CAP_IRQCHIP - APICState *s = opaque; + APICState *s = DO_UPCAST(APICState, busdev.qdev, env->apic_state); + + if (!s) { + return; + } if (kvm_enabled() && kvm_irqchip_in_kernel()) { - kvm_kernel_lapic_load_from_user(s); + kvm_kernel_lapic_save_to_user(s); } #endif - return 0; } /* This function is only used for old state version 1 and 2 */ @@ -1027,9 +1041,6 @@ if (version_id >= 2) qemu_get_timer(f, s->timer); - - qemu_kvm_load_lapic(s->cpu_env); - return 0; } @@ -1060,24 +1071,19 @@ VMSTATE_INT64(next_time, APICState), VMSTATE_TIMER(timer, APICState), VMSTATE_END_OF_LIST() - }, - .pre_save = apic_pre_save, - .post_load = apic_post_load, + } }; -static void apic_reset(void *opaque) +static void apic_reset(DeviceState *d) { - APICState *s = opaque; + APICState *s = DO_UPCAST(APICState, busdev.qdev, d); int bsp; - cpu_synchronize_state(s->cpu_env); - bsp = cpu_is_bsp(s->cpu_env); s->apicbase = 0xfee00000 | (bsp ? MSR_IA32_APICBASE_BSP : 0) | MSR_IA32_APICBASE_ENABLE; - cpu_reset(s->cpu_env); - apic_init_reset(s->cpu_env); + apic_init_reset(d); if (bsp) { /* @@ -1087,7 +1093,6 @@ */ s->lvt[APIC_LVT_LINT0] = 0x700; } - qemu_kvm_load_lapic(s->cpu_env); } static CPUReadMemoryFunc * const apic_mem_read[3] = { @@ -1102,40 +1107,43 @@ apic_mem_writel, }; -int apic_init(CPUState *env) +static int apic_init1(SysBusDevice *dev) { - APICState *s; + APICState *s = FROM_SYSBUS(APICState, dev); + int apic_io_memory; + static int last_apic_idx; - if (last_apic_idx >= MAX_APICS) + if (last_apic_idx >= MAX_APICS) { return -1; - s = qemu_mallocz(sizeof(APICState)); - env->apic_state = s; - s->idx = last_apic_idx++; - s->id = env->cpuid_apic_id; - s->cpu_env = env; - - msix_supported = 1; - - /* XXX: mapping more APICs at the same memory location */ - if (apic_io_memory == 0) { - /* NOTE: the APIC is directly connected to the CPU - it is not - on the global memory bus. */ - apic_io_memory = cpu_register_io_memory(apic_mem_read, - apic_mem_write, NULL); - /* XXX: what if the base changes? */ - cpu_register_physical_memory(MSI_ADDR_BASE, MSI_ADDR_SIZE, - apic_io_memory); } - s->timer = qemu_new_timer(vm_clock, apic_timer, s); - - vmstate_register(s->idx, &vmstate_apic, s); - qemu_register_reset(apic_reset, s); - - /* apic_reset must be called before the vcpu threads are initialized and load - * registers, in qemu-kvm. - */ - apic_reset(s); + apic_io_memory = cpu_register_io_memory(apic_mem_read, + apic_mem_write, NULL, + DEVICE_NATIVE_ENDIAN); + sysbus_init_mmio(dev, MSI_ADDR_SIZE, apic_io_memory); + s->timer = qemu_new_timer(vm_clock, apic_timer, s); + s->idx = last_apic_idx++; local_apics[s->idx] = s; return 0; } + +static SysBusDeviceInfo apic_info = { + .init = apic_init1, + .qdev.name = "apic", + .qdev.size = sizeof(APICState), + .qdev.vmsd = &vmstate_apic, + .qdev.reset = apic_reset, + .qdev.no_user = 1, + .qdev.props = (Property[]) { + DEFINE_PROP_UINT8("id", APICState, id, -1), + DEFINE_PROP_PTR("cpu_env", APICState, cpu_env), + DEFINE_PROP_END_OF_LIST(), + } +}; + +static void apic_register_devices(void) +{ + sysbus_register_withprop(&apic_info); +} + +device_init(apic_register_devices) diff -Nru qemu-kvm-0.12.5+noroms/hw/apic.h qemu-kvm-0.14.1/hw/apic.h --- qemu-kvm-0.12.5+noroms/hw/apic.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/apic.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,28 @@ +#ifndef APIC_H +#define APIC_H + +#include "qemu-common.h" + +/* apic.c */ +void apic_deliver_irq(uint8_t dest, uint8_t dest_mode, + uint8_t delivery_mode, + uint8_t vector_num, uint8_t polarity, + uint8_t trigger_mode); +int apic_accept_pic_intr(DeviceState *s); +void apic_deliver_pic_intr(DeviceState *s, int level); +int apic_get_interrupt(DeviceState *s); +void apic_reset_irq_delivered(void); +int apic_get_irq_delivered(void); +void apic_set_irq_delivered(void); +void cpu_set_apic_base(DeviceState *s, uint64_t val); +uint64_t cpu_get_apic_base(DeviceState *s); +void cpu_set_apic_tpr(DeviceState *s, uint8_t val); +uint8_t cpu_get_apic_tpr(DeviceState *s); +void apic_init_reset(DeviceState *s); +void apic_sipi(DeviceState *s); + +/* pc.c */ +int cpu_is_bsp(CPUState *env); +DeviceState *cpu_get_current_apic(void); + +#endif diff -Nru qemu-kvm-0.12.5+noroms/hw/apm.c qemu-kvm-0.14.1/hw/apm.c --- qemu-kvm-0.12.5+noroms/hw/apm.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/apm.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,86 @@ +/* + * QEMU PC APM controller Emulation + * This is split out from acpi.c + * + * Copyright (c) 2006 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2 as published by the Free Software Foundation. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + */ + +#include "apm.h" +#include "hw.h" + +//#define DEBUG + +#ifdef DEBUG +# define APM_DPRINTF(format, ...) printf(format, ## __VA_ARGS__) +#else +# define APM_DPRINTF(format, ...) do { } while (0) +#endif + +/* fixed I/O location */ +#define APM_CNT_IOPORT 0xb2 +#define APM_STS_IOPORT 0xb3 + +static void apm_ioport_writeb(void *opaque, uint32_t addr, uint32_t val) +{ + APMState *apm = opaque; + addr &= 1; + APM_DPRINTF("apm_ioport_writeb addr=0x%x val=0x%02x\n", addr, val); + if (addr == 0) { + apm->apmc = val; + + if (apm->callback) { + (apm->callback)(val, apm->arg); + } + } else { + apm->apms = val; + } +} + +static uint32_t apm_ioport_readb(void *opaque, uint32_t addr) +{ + APMState *apm = opaque; + uint32_t val; + + addr &= 1; + if (addr == 0) { + val = apm->apmc; + } else { + val = apm->apms; + } + APM_DPRINTF("apm_ioport_readb addr=0x%x val=0x%02x\n", addr, val); + return val; +} + +const VMStateDescription vmstate_apm = { + .name = "APM State", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8(apmc, APMState), + VMSTATE_UINT8(apms, APMState), + VMSTATE_END_OF_LIST() + } +}; + +void apm_init(APMState *apm, apm_ctrl_changed_t callback, void *arg) +{ + apm->callback = callback; + apm->arg = arg; + + /* ioport 0xb2, 0xb3 */ + register_ioport_write(APM_CNT_IOPORT, 2, 1, apm_ioport_writeb, apm); + register_ioport_read(APM_CNT_IOPORT, 2, 1, apm_ioport_readb, apm); +} diff -Nru qemu-kvm-0.12.5+noroms/hw/apm.h qemu-kvm-0.14.1/hw/apm.h --- qemu-kvm-0.12.5+noroms/hw/apm.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/apm.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,22 @@ +#ifndef APM_H +#define APM_H + +#include +#include "qemu-common.h" +#include "hw.h" + +typedef void (*apm_ctrl_changed_t)(uint32_t val, void *arg); + +typedef struct APMState { + uint8_t apmc; + uint8_t apms; + + apm_ctrl_changed_t callback; + void *arg; +} APMState; + +void apm_init(APMState *s, apm_ctrl_changed_t callback, void *arg); + +extern const VMStateDescription vmstate_apm; + +#endif /* APM_H */ diff -Nru qemu-kvm-0.12.5+noroms/hw/applesmc.c qemu-kvm-0.14.1/hw/applesmc.c --- qemu-kvm-0.12.5+noroms/hw/applesmc.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/applesmc.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,241 @@ +/* + * Apple SMC controller + * + * Copyright (c) 2007 Alexander Graf + * + * Authors: Alexander Graf + * Susanne Graf + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + * + * ***************************************************************** + * + * In all Intel-based Apple hardware there is an SMC chip to control the + * backlight, fans and several other generic device parameters. It also + * contains the magic keys used to dongle Mac OS X to the device. + * + * This driver was mostly created by looking at the Linux AppleSMC driver + * implementation and does not support IRQ. + * + */ + +#include "hw.h" +#include "isa.h" +#include "console.h" +#include "qemu-timer.h" + +/* #define DEBUG_SMC */ + +#define APPLESMC_DEFAULT_IOBASE 0x300 +/* data port used by Apple SMC */ +#define APPLESMC_DATA_PORT 0x0 +/* command/status port used by Apple SMC */ +#define APPLESMC_CMD_PORT 0x4 +#define APPLESMC_NR_PORTS 32 +#define APPLESMC_MAX_DATA_LENGTH 32 + +#define APPLESMC_READ_CMD 0x10 +#define APPLESMC_WRITE_CMD 0x11 +#define APPLESMC_GET_KEY_BY_INDEX_CMD 0x12 +#define APPLESMC_GET_KEY_TYPE_CMD 0x13 + +#ifdef DEBUG_SMC +#define smc_debug(...) fprintf(stderr, "AppleSMC: " __VA_ARGS__) +#else +#define smc_debug(...) do { } while(0) +#endif + +static char default_osk[64] = "This is a dummy key. Enter the real key " + "using the -osk parameter"; + +struct AppleSMCData { + uint8_t len; + const char *key; + const char *data; + QLIST_ENTRY(AppleSMCData) node; +}; + +struct AppleSMCStatus { + ISADevice dev; + uint32_t iobase; + uint8_t cmd; + uint8_t status; + uint8_t key[4]; + uint8_t read_pos; + uint8_t data_len; + uint8_t data_pos; + uint8_t data[255]; + uint8_t charactic[4]; + char *osk; + QLIST_HEAD(, AppleSMCData) data_def; +}; + +static void applesmc_io_cmd_writeb(void *opaque, uint32_t addr, uint32_t val) +{ + struct AppleSMCStatus *s = opaque; + + smc_debug("CMD Write B: %#x = %#x\n", addr, val); + switch(val) { + case APPLESMC_READ_CMD: + s->status = 0x0c; + break; + } + s->cmd = val; + s->read_pos = 0; + s->data_pos = 0; +} + +static void applesmc_fill_data(struct AppleSMCStatus *s) +{ + struct AppleSMCData *d; + + QLIST_FOREACH(d, &s->data_def, node) { + if (!memcmp(d->key, s->key, 4)) { + smc_debug("Key matched (%s Len=%d Data=%s)\n", d->key, + d->len, d->data); + memcpy(s->data, d->data, d->len); + return; + } + } +} + +static void applesmc_io_data_writeb(void *opaque, uint32_t addr, uint32_t val) +{ + struct AppleSMCStatus *s = opaque; + + smc_debug("DATA Write B: %#x = %#x\n", addr, val); + switch(s->cmd) { + case APPLESMC_READ_CMD: + if(s->read_pos < 4) { + s->key[s->read_pos] = val; + s->status = 0x04; + } else if(s->read_pos == 4) { + s->data_len = val; + s->status = 0x05; + s->data_pos = 0; + smc_debug("Key = %c%c%c%c Len = %d\n", s->key[0], + s->key[1], s->key[2], s->key[3], val); + applesmc_fill_data(s); + } + s->read_pos++; + break; + } +} + +static uint32_t applesmc_io_data_readb(void *opaque, uint32_t addr1) +{ + struct AppleSMCStatus *s = opaque; + uint8_t retval = 0; + + switch(s->cmd) { + case APPLESMC_READ_CMD: + if(s->data_pos < s->data_len) { + retval = s->data[s->data_pos]; + smc_debug("READ_DATA[%d] = %#hhx\n", s->data_pos, + retval); + s->data_pos++; + if(s->data_pos == s->data_len) { + s->status = 0x00; + smc_debug("EOF\n"); + } else + s->status = 0x05; + } + } + smc_debug("DATA Read b: %#x = %#x\n", addr1, retval); + + return retval; +} + +static uint32_t applesmc_io_cmd_readb(void *opaque, uint32_t addr1) +{ + struct AppleSMCStatus *s = opaque; + + smc_debug("CMD Read B: %#x\n", addr1); + return s->status; +} + +static void applesmc_add_key(struct AppleSMCStatus *s, const char *key, + int len, const char *data) +{ + struct AppleSMCData *def; + + def = qemu_mallocz(sizeof(struct AppleSMCData)); + def->key = key; + def->len = len; + def->data = data; + + QLIST_INSERT_HEAD(&s->data_def, def, node); +} + +static void qdev_applesmc_isa_reset(DeviceState *dev) +{ + struct AppleSMCStatus *s = DO_UPCAST(struct AppleSMCStatus, dev.qdev, dev); + struct AppleSMCData *d, *next; + + /* Remove existing entries */ + QLIST_FOREACH_SAFE(d, &s->data_def, node, next) { + QLIST_REMOVE(d, node); + } + + applesmc_add_key(s, "REV ", 6, "\x01\x13\x0f\x00\x00\x03"); + applesmc_add_key(s, "OSK0", 32, s->osk); + applesmc_add_key(s, "OSK1", 32, s->osk + 32); + applesmc_add_key(s, "NATJ", 1, "\0"); + applesmc_add_key(s, "MSSP", 1, "\0"); + applesmc_add_key(s, "MSSD", 1, "\0x3"); +} + +static int applesmc_isa_init(ISADevice *dev) +{ + struct AppleSMCStatus *s = DO_UPCAST(struct AppleSMCStatus, dev, dev); + + register_ioport_read(s->iobase + APPLESMC_DATA_PORT, 4, 1, + applesmc_io_data_readb, s); + register_ioport_read(s->iobase + APPLESMC_CMD_PORT, 4, 1, + applesmc_io_cmd_readb, s); + register_ioport_write(s->iobase + APPLESMC_DATA_PORT, 4, 1, + applesmc_io_data_writeb, s); + register_ioport_write(s->iobase + APPLESMC_CMD_PORT, 4, 1, + applesmc_io_cmd_writeb, s); + + if (!s->osk || (strlen(s->osk) != 64)) { + fprintf(stderr, "WARNING: Using AppleSMC with invalid key\n"); + s->osk = default_osk; + } + + QLIST_INIT(&s->data_def); + qdev_applesmc_isa_reset(&dev->qdev); + + return 0; +} + +static ISADeviceInfo applesmc_isa_info = { + .qdev.name = "isa-applesmc", + .qdev.size = sizeof(struct AppleSMCStatus), + .qdev.reset = qdev_applesmc_isa_reset, + .init = applesmc_isa_init, + .qdev.props = (Property[]) { + DEFINE_PROP_HEX32("iobase", struct AppleSMCStatus, iobase, + APPLESMC_DEFAULT_IOBASE), + DEFINE_PROP_STRING("osk", struct AppleSMCStatus, osk), + DEFINE_PROP_END_OF_LIST(), + }, +}; + +static void applesmc_register_devices(void) +{ + isa_qdev_register(&applesmc_isa_info); +} + +device_init(applesmc_register_devices) diff -Nru qemu-kvm-0.12.5+noroms/hw/arm_boot.c qemu-kvm-0.14.1/hw/arm_boot.c --- qemu-kvm-0.12.5+noroms/hw/arm_boot.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/arm_boot.c 2011-05-11 13:29:46.000000000 +0000 @@ -187,6 +187,7 @@ env->regs[15] = info->entry & 0xfffffffe; env->thumb = info->entry & 1; } else { + env->regs[15] = info->loader_start; if (old_param) { set_kernel_args_old(info, info->initrd_size, info->loader_start); @@ -225,8 +226,8 @@ #endif /* Assume that raw images are linux kernels, and ELF images are not. */ - kernel_size = load_elf(info->kernel_filename, 0, &elf_entry, NULL, NULL, - big_endian, ELF_MACHINE, 1); + kernel_size = load_elf(info->kernel_filename, NULL, NULL, &elf_entry, + NULL, NULL, big_endian, ELF_MACHINE, 1); entry = elf_entry; if (kernel_size < 0) { kernel_size = load_uimage(info->kernel_filename, &entry, NULL, diff -Nru qemu-kvm-0.12.5+noroms/hw/arm_gic.c qemu-kvm-0.14.1/hw/arm_gic.c --- qemu-kvm-0.12.5+noroms/hw/arm_gic.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/arm_gic.c 2011-05-11 13:29:46.000000000 +0000 @@ -742,7 +742,8 @@ sysbus_init_irq(&s->busdev, &s->parent_irq[i]); } s->iomemtype = cpu_register_io_memory(gic_dist_readfn, - gic_dist_writefn, s); + gic_dist_writefn, s, + DEVICE_NATIVE_ENDIAN); gic_reset(s); - register_savevm("arm_gic", -1, 1, gic_save, gic_load, s); + register_savevm(NULL, "arm_gic", -1, 1, gic_save, gic_load, s); } diff -Nru qemu-kvm-0.12.5+noroms/hw/arm_sysctl.c qemu-kvm-0.14.1/hw/arm_sysctl.c --- qemu-kvm-0.12.5+noroms/hw/arm_sysctl.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/arm_sysctl.c 2011-05-11 13:29:46.000000000 +0000 @@ -28,6 +28,22 @@ uint32_t proc_id; } arm_sysctl_state; +static const VMStateDescription vmstate_arm_sysctl = { + .name = "realview_sysctl", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(leds, arm_sysctl_state), + VMSTATE_UINT16(lockval, arm_sysctl_state), + VMSTATE_UINT32(cfgdata1, arm_sysctl_state), + VMSTATE_UINT32(cfgdata2, arm_sysctl_state), + VMSTATE_UINT32(flags, arm_sysctl_state), + VMSTATE_UINT32(nvflags, arm_sysctl_state), + VMSTATE_UINT32(resetlevel, arm_sysctl_state), + VMSTATE_END_OF_LIST() + } +}; + static void arm_sysctl_reset(DeviceState *d) { arm_sysctl_state *s = FROM_SYSBUS(arm_sysctl_state, sysbus_from_qdev(d)); @@ -208,7 +224,8 @@ int iomemtype; iomemtype = cpu_register_io_memory(arm_sysctl_readfn, - arm_sysctl_writefn, s); + arm_sysctl_writefn, s, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, 0x1000, iomemtype); /* ??? Save/restore. */ return 0; @@ -230,6 +247,7 @@ .init = arm_sysctl_init1, .qdev.name = "realview_sysctl", .qdev.size = sizeof(arm_sysctl_state), + .qdev.vmsd = &vmstate_arm_sysctl, .qdev.reset = arm_sysctl_reset, .qdev.props = (Property[]) { DEFINE_PROP_UINT32("sys_id", arm_sysctl_state, sys_id, 0), diff -Nru qemu-kvm-0.12.5+noroms/hw/arm_timer.c qemu-kvm-0.14.1/hw/arm_timer.c --- qemu-kvm-0.12.5+noroms/hw/arm_timer.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/arm_timer.c 2011-05-11 13:29:46.000000000 +0000 @@ -174,7 +174,7 @@ bh = qemu_bh_new(arm_timer_tick, s); s->timer = ptimer_init(bh); - register_savevm("arm_timer", -1, 1, arm_timer_save, arm_timer_load, s); + register_savevm(NULL, "arm_timer", -1, 1, arm_timer_save, arm_timer_load, s); return s; } @@ -269,9 +269,9 @@ s->timer[0]->irq = qi[0]; s->timer[1]->irq = qi[1]; iomemtype = cpu_register_io_memory(sp804_readfn, - sp804_writefn, s); + sp804_writefn, s, DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, 0x1000, iomemtype); - register_savevm("sp804", -1, 1, sp804_save, sp804_load, s); + register_savevm(&dev->qdev, "sp804", -1, 1, sp804_save, sp804_load, s); return 0; } @@ -340,7 +340,8 @@ sysbus_init_irq(dev, &s->timer[2]->irq); iomemtype = cpu_register_io_memory(icp_pit_readfn, - icp_pit_writefn, s); + icp_pit_writefn, s, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, 0x1000, iomemtype); /* This device has no state to save/restore. The component timers will save themselves. */ diff -Nru qemu-kvm-0.12.5+noroms/hw/armv7m.c qemu-kvm-0.14.1/hw/armv7m.c --- qemu-kvm-0.12.5+noroms/hw/armv7m.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/armv7m.c 2011-05-11 13:29:46.000000000 +0000 @@ -130,7 +130,7 @@ int iomemtype; iomemtype = cpu_register_io_memory(bitband_readfn, bitband_writefn, - &s->base); + &s->base, DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, 0x02000000, iomemtype); return 0; } @@ -151,6 +151,12 @@ } /* Board init. */ + +static void armv7m_reset(void *opaque) +{ + cpu_reset((CPUState *)opaque); +} + /* Init CPU and memory for a v7-M based board. flash_size and sram_size are in kb. Returns the NVIC array. */ @@ -163,7 +169,6 @@ /* FIXME: make this local state. */ static qemu_irq pic[64]; qemu_irq *cpu_pic; - uint32_t pc; int image_size; uint64_t entry; uint64_t lowaddr; @@ -195,13 +200,15 @@ /* Flash programming is done via the SCU, so pretend it is ROM. */ cpu_register_physical_memory(0, flash_size, - qemu_ram_alloc(flash_size) | IO_MEM_ROM); + qemu_ram_alloc(NULL, "armv7m.flash", + flash_size) | IO_MEM_ROM); cpu_register_physical_memory(0x20000000, sram_size, - qemu_ram_alloc(sram_size) | IO_MEM_RAM); + qemu_ram_alloc(NULL, "armv7m.sram", + sram_size) | IO_MEM_RAM); armv7m_bitband_init(); nvic = qdev_create(NULL, "armv7m_nvic"); - env->v7m.nvic = nvic; + env->nvic = nvic; qdev_init_nofail(nvic); cpu_pic = arm_pic_init_cpu(env); sysbus_connect_irq(sysbus_from_qdev(nvic), 0, cpu_pic[ARM_PIC_CPU_IRQ]); @@ -215,8 +222,8 @@ big_endian = 0; #endif - image_size = load_elf(kernel_filename, 0, &entry, &lowaddr, NULL, - big_endian, ELF_MACHINE, 1); + image_size = load_elf(kernel_filename, NULL, NULL, &entry, &lowaddr, + NULL, big_endian, ELF_MACHINE, 1); if (image_size < 0) { image_size = load_image_targphys(kernel_filename, 0, flash_size); lowaddr = 0; @@ -227,24 +234,14 @@ exit(1); } - /* If the image was loaded at address zero then assume it is a - regular ROM image and perform the normal CPU reset sequence. - Otherwise jump directly to the entry point. */ - if (lowaddr == 0) { - env->regs[13] = ldl_phys(0); - pc = ldl_phys(4); - } else { - pc = entry; - } - env->thumb = pc & 1; - env->regs[15] = pc & ~1; - /* Hack to map an additional page of ram at the top of the address space. This stops qemu complaining about executing code outside RAM when returning from an exception. */ cpu_register_physical_memory(0xfffff000, 0x1000, - qemu_ram_alloc(0x1000) | IO_MEM_RAM); + qemu_ram_alloc(NULL, "armv7m.hack", + 0x1000) | IO_MEM_RAM); + qemu_register_reset(armv7m_reset, env); return pic; } diff -Nru qemu-kvm-0.12.5+noroms/hw/armv7m_nvic.c qemu-kvm-0.14.1/hw/armv7m_nvic.c --- qemu-kvm-0.12.5+noroms/hw/armv7m_nvic.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/armv7m_nvic.c 2011-05-11 13:29:46.000000000 +0000 @@ -197,10 +197,10 @@ case 0xd18: case 0xd1c: case 0xd20: /* System Handler Priority. */ irq = offset - 0xd14; val = 0; - val = s->gic.priority1[irq++][0]; - val = s->gic.priority1[irq++][0] << 8; - val = s->gic.priority1[irq++][0] << 16; - val = s->gic.priority1[irq][0] << 24; + val |= s->gic.priority1[irq++][0]; + val |= s->gic.priority1[irq++][0] << 8; + val |= s->gic.priority1[irq++][0] << 16; + val |= s->gic.priority1[irq][0] << 24; return val; case 0xd24: /* System Handler Status. */ val = 0; @@ -397,7 +397,7 @@ gic_init(&s->gic); cpu_register_physical_memory(0xe000e000, 0x1000, s->gic.iomemtype); s->systick.timer = qemu_new_timer(vm_clock, systick_timer_tick, s); - register_savevm("armv7m_nvic", -1, 1, nvic_save, nvic_load, s); + register_savevm(&dev->qdev, "armv7m_nvic", -1, 1, nvic_save, nvic_load, s); return 0; } diff -Nru qemu-kvm-0.12.5+noroms/hw/audiodev.h qemu-kvm-0.14.1/hw/audiodev.h --- qemu-kvm-0.12.5+noroms/hw/audiodev.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/audiodev.h 2011-05-11 13:29:46.000000000 +0000 @@ -15,3 +15,6 @@ /* cs4231a.c */ int cs4231a_init(qemu_irq *pic); + +/* intel-hda.c + hda-audio.c */ +int intel_hda_and_codec_init(PCIBus *bus); diff -Nru qemu-kvm-0.12.5+noroms/hw/axis_dev88.c qemu-kvm-0.14.1/hw/axis_dev88.c --- qemu-kvm-0.12.5+noroms/hw/axis_dev88.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/axis_dev88.c 2011-05-11 13:29:46.000000000 +0000 @@ -30,6 +30,7 @@ #include "etraxfs.h" #include "loader.h" #include "elf.h" +#include "cris-boot.h" #define D(x) #define DNAND(x) @@ -240,14 +241,7 @@ #define INTMEM_SIZE (128 * 1024) -static uint32_t bootstrap_pc; -static void main_cpu_reset(void *opaque) -{ - CPUState *env = opaque; - cpu_reset(env); - - env->pc = bootstrap_pc; -} +static struct cris_load_info li; static void axisdev88_init (ram_addr_t ram_size, @@ -261,7 +255,6 @@ qemu_irq irq[30], nmi[2], *cpu_irq; void *etraxfs_dmac; struct etraxfs_dma_client *eth[2] = {NULL, NULL}; - int kernel_size; int i; int nand_regs; int gpio_regs; @@ -273,26 +266,27 @@ cpu_model = "crisv32"; } env = cpu_init(cpu_model); - qemu_register_reset(main_cpu_reset, env); /* allocate RAM */ - phys_ram = qemu_ram_alloc(ram_size); + phys_ram = qemu_ram_alloc(NULL, "axisdev88.ram", ram_size); cpu_register_physical_memory(0x40000000, ram_size, phys_ram | IO_MEM_RAM); /* The ETRAX-FS has 128Kb on chip ram, the docs refer to it as the internal memory. */ - phys_intmem = qemu_ram_alloc(INTMEM_SIZE); + phys_intmem = qemu_ram_alloc(NULL, "axisdev88.chipram", INTMEM_SIZE); cpu_register_physical_memory(0x38000000, INTMEM_SIZE, phys_intmem | IO_MEM_RAM); /* Attach a NAND flash to CS1. */ nand_state.nand = nand_init(NAND_MFR_STMICRO, 0x39); - nand_regs = cpu_register_io_memory(nand_read, nand_write, &nand_state); + nand_regs = cpu_register_io_memory(nand_read, nand_write, &nand_state, + DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(0x10000000, 0x05000000, nand_regs); gpio_state.nand = &nand_state; - gpio_regs = cpu_register_io_memory(gpio_read, gpio_write, &gpio_state); + gpio_regs = cpu_register_io_memory(gpio_read, gpio_write, &gpio_state, + DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(0x3001a000, 0x5c, gpio_regs); @@ -339,39 +333,14 @@ irq[0x14 + i]); } - if (kernel_filename) { - uint64_t entry, high; - int kcmdline_len; - - /* Boots a kernel elf binary, os/linux-2.6/vmlinux from the axis - devboard SDK. */ - kernel_size = load_elf(kernel_filename, -0x80000000LL, - &entry, NULL, &high, 0, ELF_MACHINE, 0); - bootstrap_pc = entry; - if (kernel_size < 0) { - /* Takes a kimage from the axis devboard SDK. */ - kernel_size = load_image_targphys(kernel_filename, 0x40004000, - ram_size); - bootstrap_pc = 0x40004000; - env->regs[9] = 0x40004000 + kernel_size; - } - env->regs[8] = 0x56902387; /* RAM init magic. */ - - if (kernel_cmdline && (kcmdline_len = strlen(kernel_cmdline))) { - if (kcmdline_len > 256) { - fprintf(stderr, "Too long CRIS kernel cmdline (max 256)\n"); - exit(1); - } - /* Let the kernel know we are modifying the cmdline. */ - env->regs[10] = 0x87109563; - env->regs[11] = 0x40000000; - pstrcpy_targphys("cmdline", env->regs[11], 256, kernel_cmdline); - } + if (!kernel_filename) { + fprintf(stderr, "Kernel image must be specified\n"); + exit(1); } - env->pc = bootstrap_pc; - printf ("pc =%x\n", env->pc); - printf ("ram size =%ld\n", ram_size); + li.image_filename = kernel_filename; + li.cmdline = kernel_cmdline; + cris_load_image(env, &li); } static QEMUMachine axisdev88_machine = { diff -Nru qemu-kvm-0.12.5+noroms/hw/baum.c qemu-kvm-0.14.1/hw/baum.c --- qemu-kvm-0.12.5+noroms/hw/baum.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/baum.c 2011-05-11 13:29:46.000000000 +0000 @@ -564,6 +564,18 @@ } } +static void baum_close(struct CharDriverState *chr) +{ + BaumDriverState *baum = chr->opaque; + + qemu_free_timer(baum->cellCount_timer); + if (baum->brlapi) { + brlapi__closeConnection(baum->brlapi); + qemu_free(baum->brlapi); + } + qemu_free(baum); +} + CharDriverState *chr_baum_init(QemuOpts *opts) { BaumDriverState *baum; @@ -581,6 +593,7 @@ chr->chr_write = baum_write; chr->chr_send_event = baum_send_event; chr->chr_accept_input = baum_accept_input; + chr->chr_close = baum_close; handle = qemu_mallocz(brlapi_getHandleSize()); baum->brlapi = handle; diff -Nru qemu-kvm-0.12.5+noroms/hw/boards.h qemu-kvm-0.14.1/hw/boards.h --- qemu-kvm-0.12.5+noroms/hw/boards.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/boards.h 2011-05-11 13:29:46.000000000 +0000 @@ -19,7 +19,7 @@ QEMUMachineInitFunc *init; int use_scsi; int max_cpus; - int no_serial:1, + unsigned int no_serial:1, no_parallel:1, use_virtcon:1, no_vga:1, diff -Nru qemu-kvm-0.12.5+noroms/hw/bonito.c qemu-kvm-0.14.1/hw/bonito.c --- qemu-kvm-0.12.5+noroms/hw/bonito.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/bonito.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,813 @@ +/* + * bonito north bridge support + * + * Copyright (c) 2008 yajin (yajin@vm-kernel.org) + * Copyright (c) 2010 Huacai Chen (zltjiangshi@gmail.com) + * + * This code is licensed under the GNU GPL v2. + */ + +/* + * fulong 2e mini pc has a bonito north bridge. + */ + +/* what is the meaning of devfn in qemu and IDSEL in bonito northbridge? + * + * devfn pci_slot<<3 + funno + * one pci bus can have 32 devices and each device can have 8 functions. + * + * In bonito north bridge, pci slot = IDSEL bit - 12. + * For example, PCI_IDSEL_VIA686B = 17, + * pci slot = 17-12=5 + * + * so + * VT686B_FUN0's devfn = (5<<3)+0 + * VT686B_FUN1's devfn = (5<<3)+1 + * + * qemu also uses pci address for north bridge to access pci config register. + * bus_no [23:16] + * dev_no [15:11] + * fun_no [10:8] + * reg_no [7:2] + * + * so function bonito_sbridge_pciaddr for the translation from + * north bridge address to pci address. + */ + +#include + +#include "hw.h" +#include "pci.h" +#include "pc.h" +#include "mips.h" +#include "pci_host.h" +#include "sysemu.h" + +//#define DEBUG_BONITO + +#ifdef DEBUG_BONITO +#define DPRINTF(fmt, ...) fprintf(stderr, "%s: " fmt, __FUNCTION__, ##__VA_ARGS__) +#else +#define DPRINTF(fmt, ...) +#endif + +/* from linux soure code. include/asm-mips/mips-boards/bonito64.h*/ +#define BONITO_BOOT_BASE 0x1fc00000 +#define BONITO_BOOT_SIZE 0x00100000 +#define BONITO_BOOT_TOP (BONITO_BOOT_BASE+BONITO_BOOT_SIZE-1) +#define BONITO_FLASH_BASE 0x1c000000 +#define BONITO_FLASH_SIZE 0x03000000 +#define BONITO_FLASH_TOP (BONITO_FLASH_BASE+BONITO_FLASH_SIZE-1) +#define BONITO_SOCKET_BASE 0x1f800000 +#define BONITO_SOCKET_SIZE 0x00400000 +#define BONITO_SOCKET_TOP (BONITO_SOCKET_BASE+BONITO_SOCKET_SIZE-1) +#define BONITO_REG_BASE 0x1fe00000 +#define BONITO_REG_SIZE 0x00040000 +#define BONITO_REG_TOP (BONITO_REG_BASE+BONITO_REG_SIZE-1) +#define BONITO_DEV_BASE 0x1ff00000 +#define BONITO_DEV_SIZE 0x00100000 +#define BONITO_DEV_TOP (BONITO_DEV_BASE+BONITO_DEV_SIZE-1) +#define BONITO_PCILO_BASE 0x10000000 +#define BONITO_PCILO_BASE_VA 0xb0000000 +#define BONITO_PCILO_SIZE 0x0c000000 +#define BONITO_PCILO_TOP (BONITO_PCILO_BASE+BONITO_PCILO_SIZE-1) +#define BONITO_PCILO0_BASE 0x10000000 +#define BONITO_PCILO1_BASE 0x14000000 +#define BONITO_PCILO2_BASE 0x18000000 +#define BONITO_PCIHI_BASE 0x20000000 +#define BONITO_PCIHI_SIZE 0x20000000 +#define BONITO_PCIHI_TOP (BONITO_PCIHI_BASE+BONITO_PCIHI_SIZE-1) +#define BONITO_PCIIO_BASE 0x1fd00000 +#define BONITO_PCIIO_BASE_VA 0xbfd00000 +#define BONITO_PCIIO_SIZE 0x00010000 +#define BONITO_PCIIO_TOP (BONITO_PCIIO_BASE+BONITO_PCIIO_SIZE-1) +#define BONITO_PCICFG_BASE 0x1fe80000 +#define BONITO_PCICFG_SIZE 0x00080000 +#define BONITO_PCICFG_TOP (BONITO_PCICFG_BASE+BONITO_PCICFG_SIZE-1) + + +#define BONITO_PCICONFIGBASE 0x00 +#define BONITO_REGBASE 0x100 + +#define BONITO_PCICONFIG_BASE (BONITO_PCICONFIGBASE+BONITO_REG_BASE) +#define BONITO_PCICONFIG_SIZE (0x100) + +#define BONITO_INTERNAL_REG_BASE (BONITO_REGBASE+BONITO_REG_BASE) +#define BONITO_INTERNAL_REG_SIZE (0x70) + +#define BONITO_SPCICONFIG_BASE (BONITO_PCICFG_BASE) +#define BONITO_SPCICONFIG_SIZE (BONITO_PCICFG_SIZE) + + + +/* 1. Bonito h/w Configuration */ +/* Power on register */ + +#define BONITO_BONPONCFG (0x00 >> 2) /* 0x100 */ +#define BONITO_BONGENCFG_OFFSET 0x4 +#define BONITO_BONGENCFG (BONITO_BONGENCFG_OFFSET>>2) /*0x104 */ + +/* 2. IO & IDE configuration */ +#define BONITO_IODEVCFG (0x08 >> 2) /* 0x108 */ + +/* 3. IO & IDE configuration */ +#define BONITO_SDCFG (0x0c >> 2) /* 0x10c */ + +/* 4. PCI address map control */ +#define BONITO_PCIMAP (0x10 >> 2) /* 0x110 */ +#define BONITO_PCIMEMBASECFG (0x14 >> 2) /* 0x114 */ +#define BONITO_PCIMAP_CFG (0x18 >> 2) /* 0x118 */ + +/* 5. ICU & GPIO regs */ +/* GPIO Regs - r/w */ +#define BONITO_GPIODATA_OFFSET 0x1c +#define BONITO_GPIODATA (BONITO_GPIODATA_OFFSET >> 2) /* 0x11c */ +#define BONITO_GPIOIE (0x20 >> 2) /* 0x120 */ + +/* ICU Configuration Regs - r/w */ +#define BONITO_INTEDGE (0x24 >> 2) /* 0x124 */ +#define BONITO_INTSTEER (0x28 >> 2) /* 0x128 */ +#define BONITO_INTPOL (0x2c >> 2) /* 0x12c */ + +/* ICU Enable Regs - IntEn & IntISR are r/o. */ +#define BONITO_INTENSET (0x30 >> 2) /* 0x130 */ +#define BONITO_INTENCLR (0x34 >> 2) /* 0x134 */ +#define BONITO_INTEN (0x38 >> 2) /* 0x138 */ +#define BONITO_INTISR (0x3c >> 2) /* 0x13c */ + +/* PCI mail boxes */ +#define BONITO_PCIMAIL0_OFFSET 0x40 +#define BONITO_PCIMAIL1_OFFSET 0x44 +#define BONITO_PCIMAIL2_OFFSET 0x48 +#define BONITO_PCIMAIL3_OFFSET 0x4c +#define BONITO_PCIMAIL0 (0x40 >> 2) /* 0x140 */ +#define BONITO_PCIMAIL1 (0x44 >> 2) /* 0x144 */ +#define BONITO_PCIMAIL2 (0x48 >> 2) /* 0x148 */ +#define BONITO_PCIMAIL3 (0x4c >> 2) /* 0x14c */ + +/* 6. PCI cache */ +#define BONITO_PCICACHECTRL (0x50 >> 2) /* 0x150 */ +#define BONITO_PCICACHETAG (0x54 >> 2) /* 0x154 */ +#define BONITO_PCIBADADDR (0x58 >> 2) /* 0x158 */ +#define BONITO_PCIMSTAT (0x5c >> 2) /* 0x15c */ + +/* 7. other*/ +#define BONITO_TIMECFG (0x60 >> 2) /* 0x160 */ +#define BONITO_CPUCFG (0x64 >> 2) /* 0x164 */ +#define BONITO_DQCFG (0x68 >> 2) /* 0x168 */ +#define BONITO_MEMSIZE (0x6C >> 2) /* 0x16c */ + +#define BONITO_REGS (0x70 >> 2) + +/* PCI config for south bridge. type 0 */ +#define BONITO_PCICONF_IDSEL_MASK 0xfffff800 /* [31:11] */ +#define BONITO_PCICONF_IDSEL_OFFSET 11 +#define BONITO_PCICONF_FUN_MASK 0x700 /* [10:8] */ +#define BONITO_PCICONF_FUN_OFFSET 8 +#define BONITO_PCICONF_REG_MASK 0xFC +#define BONITO_PCICONF_REG_OFFSET 0 + + +/* idsel BIT = pci slot number +12 */ +#define PCI_SLOT_BASE 12 +#define PCI_IDSEL_VIA686B_BIT (17) +#define PCI_IDSEL_VIA686B (1<> 2; + + DPRINTF("bonito_writel "TARGET_FMT_plx" val %x saddr %x \n", addr, val, saddr); + switch (saddr) { + case BONITO_BONPONCFG: + case BONITO_IODEVCFG: + case BONITO_SDCFG: + case BONITO_PCIMAP: + case BONITO_PCIMEMBASECFG: + case BONITO_PCIMAP_CFG: + case BONITO_GPIODATA: + case BONITO_GPIOIE: + case BONITO_INTEDGE: + case BONITO_INTSTEER: + case BONITO_INTPOL: + case BONITO_PCIMAIL0: + case BONITO_PCIMAIL1: + case BONITO_PCIMAIL2: + case BONITO_PCIMAIL3: + case BONITO_PCICACHECTRL: + case BONITO_PCICACHETAG: + case BONITO_PCIBADADDR: + case BONITO_PCIMSTAT: + case BONITO_TIMECFG: + case BONITO_CPUCFG: + case BONITO_DQCFG: + case BONITO_MEMSIZE: + s->regs[saddr] = val; + break; + case BONITO_BONGENCFG: + if (!(s->regs[saddr] & 0x04) && (val & 0x04)) { + reset = 1; /* bit 2 jump from 0 to 1 cause reset */ + } + s->regs[saddr] = val; + if (reset) { + qemu_system_reset_request(); + } + break; + case BONITO_INTENSET: + s->regs[BONITO_INTENSET] = val; + s->regs[BONITO_INTEN] |= val; + break; + case BONITO_INTENCLR: + s->regs[BONITO_INTENCLR] = val; + s->regs[BONITO_INTEN] &= ~val; + break; + case BONITO_INTEN: + case BONITO_INTISR: + DPRINTF("write to readonly bonito register %x \n", saddr); + break; + default: + DPRINTF("write to unknown bonito register %x \n", saddr); + break; + } +} + +static uint32_t bonito_readl(void *opaque, target_phys_addr_t addr) +{ + PCIBonitoState *s = opaque; + uint32_t saddr; + + saddr = (addr - BONITO_REGBASE) >> 2; + + DPRINTF("bonito_readl "TARGET_FMT_plx" \n", addr); + switch (saddr) { + case BONITO_INTISR: + return s->regs[saddr]; + default: + return s->regs[saddr]; + } +} + +static CPUWriteMemoryFunc * const bonito_write[] = { + NULL, + NULL, + bonito_writel, +}; + +static CPUReadMemoryFunc * const bonito_read[] = { + NULL, + NULL, + bonito_readl, +}; + +static void bonito_pciconf_writel(void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + PCIBonitoState *s = opaque; + + DPRINTF("bonito_pciconf_writel "TARGET_FMT_plx" val %x \n", addr, val); + s->dev.config_write(&s->dev, addr, val, 4); +} + +static uint32_t bonito_pciconf_readl(void *opaque, target_phys_addr_t addr) +{ + + PCIBonitoState *s = opaque; + + DPRINTF("bonito_pciconf_readl "TARGET_FMT_plx"\n", addr); + return s->dev.config_read(&s->dev, addr, 4); +} + +/* north bridge PCI configure space. 0x1fe0 0000 - 0x1fe0 00ff */ +static CPUWriteMemoryFunc * const bonito_pciconf_write[] = { + NULL, + NULL, + bonito_pciconf_writel, +}; + +static CPUReadMemoryFunc * const bonito_pciconf_read[] = { + NULL, + NULL, + bonito_pciconf_readl, +}; + +static uint32_t bonito_ldma_readl(void *opaque, target_phys_addr_t addr) +{ + uint32_t val; + PCIBonitoState *s = opaque; + + val = ((uint32_t *)(&s->bonldma))[addr/sizeof(uint32_t)]; + + return val; +} + +static void bonito_ldma_writel(void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + PCIBonitoState *s = opaque; + + ((uint32_t *)(&s->bonldma))[addr/sizeof(uint32_t)] = val & 0xffffffff; +} + +static CPUWriteMemoryFunc * const bonito_ldma_write[] = { + NULL, + NULL, + bonito_ldma_writel, +}; + +static CPUReadMemoryFunc * const bonito_ldma_read[] = { + NULL, + NULL, + bonito_ldma_readl, +}; + +static uint32_t bonito_cop_readl(void *opaque, target_phys_addr_t addr) +{ + uint32_t val; + PCIBonitoState *s = opaque; + + val = ((uint32_t *)(&s->boncop))[addr/sizeof(uint32_t)]; + + return val; +} + +static void bonito_cop_writel(void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + PCIBonitoState *s = opaque; + + ((uint32_t *)(&s->boncop))[addr/sizeof(uint32_t)] = val & 0xffffffff; +} + +static CPUWriteMemoryFunc * const bonito_cop_write[] = { + NULL, + NULL, + bonito_cop_writel, +}; + +static CPUReadMemoryFunc * const bonito_cop_read[] = { + NULL, + NULL, + bonito_cop_readl, +}; + +static uint32_t bonito_sbridge_pciaddr(void *opaque, target_phys_addr_t addr) +{ + PCIBonitoState *s = opaque; + uint32_t cfgaddr; + uint32_t idsel; + uint32_t devno; + uint32_t funno; + uint32_t regno; + uint32_t pciaddr; + + /* support type0 pci config */ + if ((s->regs[BONITO_PCIMAP_CFG] & 0x10000) != 0x0) { + return 0xffffffff; + } + + cfgaddr = addr & 0xffff; + cfgaddr |= (s->regs[BONITO_PCIMAP_CFG] & 0xffff) << 16; + + idsel = (cfgaddr & BONITO_PCICONF_IDSEL_MASK) >> BONITO_PCICONF_IDSEL_OFFSET; + devno = ffs(idsel) - 1; + funno = (cfgaddr & BONITO_PCICONF_FUN_MASK) >> BONITO_PCICONF_FUN_OFFSET; + regno = (cfgaddr & BONITO_PCICONF_REG_MASK) >> BONITO_PCICONF_REG_OFFSET; + + if (idsel == 0) { + fprintf(stderr, "error in bonito pci config address" TARGET_FMT_plx + ",pcimap_cfg=%x\n", addr, s->regs[BONITO_PCIMAP_CFG]); + exit(1); + } + pciaddr = PCI_ADDR(pci_bus_num(s->pcihost->bus), devno, funno, regno); + DPRINTF("cfgaddr %x pciaddr %x busno %x devno %d funno %d regno %d \n", + cfgaddr, pciaddr, pci_bus_num(s->pcihost->bus), devno, funno, regno); + + return pciaddr; +} + +static void bonito_spciconf_writeb(void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + PCIBonitoState *s = opaque; + uint32_t pciaddr; + uint16_t status; + + DPRINTF("bonito_spciconf_writeb "TARGET_FMT_plx" val %x \n", addr, val); + pciaddr = bonito_sbridge_pciaddr(s, addr); + + if (pciaddr == 0xffffffff) { + return; + } + + /* set the pci address in s->config_reg */ + s->pcihost->config_reg = (pciaddr) | (1u << 31); + pci_data_write(s->pcihost->bus, s->pcihost->config_reg, val & 0xff, 1); + + /* clear PCI_STATUS_REC_MASTER_ABORT and PCI_STATUS_REC_TARGET_ABORT */ + status = pci_get_word(s->dev.config + PCI_STATUS); + status &= ~(PCI_STATUS_REC_MASTER_ABORT | PCI_STATUS_REC_TARGET_ABORT); + pci_set_word(s->dev.config + PCI_STATUS, status); +} + +static void bonito_spciconf_writew(void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + PCIBonitoState *s = opaque; + uint32_t pciaddr; + uint16_t status; + + DPRINTF("bonito_spciconf_writew "TARGET_FMT_plx" val %x \n", addr, val); + assert((addr&0x1)==0); + + pciaddr = bonito_sbridge_pciaddr(s, addr); + + if (pciaddr == 0xffffffff) { + return; + } + + /* set the pci address in s->config_reg */ + s->pcihost->config_reg = (pciaddr) | (1u << 31); + pci_data_write(s->pcihost->bus, s->pcihost->config_reg, val, 2); + + /* clear PCI_STATUS_REC_MASTER_ABORT and PCI_STATUS_REC_TARGET_ABORT */ + status = pci_get_word(s->dev.config + PCI_STATUS); + status &= ~(PCI_STATUS_REC_MASTER_ABORT | PCI_STATUS_REC_TARGET_ABORT); + pci_set_word(s->dev.config + PCI_STATUS, status); +} + +static void bonito_spciconf_writel(void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + PCIBonitoState *s = opaque; + uint32_t pciaddr; + uint16_t status; + + DPRINTF("bonito_spciconf_writel "TARGET_FMT_plx" val %x \n", addr, val); + assert((addr&0x3)==0); + + pciaddr = bonito_sbridge_pciaddr(s, addr); + + if (pciaddr == 0xffffffff) { + return; + } + + /* set the pci address in s->config_reg */ + s->pcihost->config_reg = (pciaddr) | (1u << 31); + pci_data_write(s->pcihost->bus, s->pcihost->config_reg, val, 4); + + /* clear PCI_STATUS_REC_MASTER_ABORT and PCI_STATUS_REC_TARGET_ABORT */ + status = pci_get_word(s->dev.config + PCI_STATUS); + status &= ~(PCI_STATUS_REC_MASTER_ABORT | PCI_STATUS_REC_TARGET_ABORT); + pci_set_word(s->dev.config + PCI_STATUS, status); +} + +static uint32_t bonito_spciconf_readb(void *opaque, target_phys_addr_t addr) +{ + PCIBonitoState *s = opaque; + uint32_t pciaddr; + uint16_t status; + + DPRINTF("bonito_spciconf_readb "TARGET_FMT_plx" \n", addr); + pciaddr = bonito_sbridge_pciaddr(s, addr); + + if (pciaddr == 0xffffffff) { + return 0xff; + } + + /* set the pci address in s->config_reg */ + s->pcihost->config_reg = (pciaddr) | (1u << 31); + + /* clear PCI_STATUS_REC_MASTER_ABORT and PCI_STATUS_REC_TARGET_ABORT */ + status = pci_get_word(s->dev.config + PCI_STATUS); + status &= ~(PCI_STATUS_REC_MASTER_ABORT | PCI_STATUS_REC_TARGET_ABORT); + pci_set_word(s->dev.config + PCI_STATUS, status); + + return pci_data_read(s->pcihost->bus, s->pcihost->config_reg, 1); +} + +static uint32_t bonito_spciconf_readw(void *opaque, target_phys_addr_t addr) +{ + PCIBonitoState *s = opaque; + uint32_t pciaddr; + uint16_t status; + + DPRINTF("bonito_spciconf_readw "TARGET_FMT_plx" \n", addr); + assert((addr&0x1)==0); + + pciaddr = bonito_sbridge_pciaddr(s, addr); + + if (pciaddr == 0xffffffff) { + return 0xffff; + } + + /* set the pci address in s->config_reg */ + s->pcihost->config_reg = (pciaddr) | (1u << 31); + + /* clear PCI_STATUS_REC_MASTER_ABORT and PCI_STATUS_REC_TARGET_ABORT */ + status = pci_get_word(s->dev.config + PCI_STATUS); + status &= ~(PCI_STATUS_REC_MASTER_ABORT | PCI_STATUS_REC_TARGET_ABORT); + pci_set_word(s->dev.config + PCI_STATUS, status); + + return pci_data_read(s->pcihost->bus, s->pcihost->config_reg, 2); +} + +static uint32_t bonito_spciconf_readl(void *opaque, target_phys_addr_t addr) +{ + PCIBonitoState *s = opaque; + uint32_t pciaddr; + uint16_t status; + + DPRINTF("bonito_spciconf_readl "TARGET_FMT_plx" \n", addr); + assert((addr&0x3) == 0); + + pciaddr = bonito_sbridge_pciaddr(s, addr); + + if (pciaddr == 0xffffffff) { + return 0xffffffff; + } + + /* set the pci address in s->config_reg */ + s->pcihost->config_reg = (pciaddr) | (1u << 31); + + /* clear PCI_STATUS_REC_MASTER_ABORT and PCI_STATUS_REC_TARGET_ABORT */ + status = pci_get_word(s->dev.config + PCI_STATUS); + status &= ~(PCI_STATUS_REC_MASTER_ABORT | PCI_STATUS_REC_TARGET_ABORT); + pci_set_word(s->dev.config + PCI_STATUS, status); + + return pci_data_read(s->pcihost->bus, s->pcihost->config_reg, 4); +} + +/* south bridge PCI configure space. 0x1fe8 0000 - 0x1fef ffff */ +static CPUWriteMemoryFunc * const bonito_spciconf_write[] = { + bonito_spciconf_writeb, + bonito_spciconf_writew, + bonito_spciconf_writel, +}; + +static CPUReadMemoryFunc * const bonito_spciconf_read[] = { + bonito_spciconf_readb, + bonito_spciconf_readw, + bonito_spciconf_readl, +}; + +#define BONITO_IRQ_BASE 32 + +static void pci_bonito_set_irq(void *opaque, int irq_num, int level) +{ + qemu_irq *pic = opaque; + int internal_irq = irq_num - BONITO_IRQ_BASE; + + if (bonito_state->regs[BONITO_INTEDGE] & (1<regs[BONITO_INTPOL] & (1<devfn >> 3); + + switch (slot) { + case 5: /* FULONG2E_VIA_SLOT, SouthBridge, IDE, USB, ACPI, AC97, MC97 */ + return irq_num % 4 + BONITO_IRQ_BASE; + case 6: /* FULONG2E_ATI_SLOT, VGA */ + return 4 + BONITO_IRQ_BASE; + case 7: /* FULONG2E_RTL_SLOT, RTL8139 */ + return 5 + BONITO_IRQ_BASE; + case 8 ... 12: /* PCI slot 1 to 4 */ + return (slot - 8 + irq_num) + 6 + BONITO_IRQ_BASE; + default: /* Unknown device, don't do any translation */ + return irq_num; + } +} + +static void bonito_reset(void *opaque) +{ + PCIBonitoState *s = opaque; + + /* set the default value of north bridge registers */ + + s->regs[BONITO_BONPONCFG] = 0xc40; + s->regs[BONITO_BONGENCFG] = 0x1384; + s->regs[BONITO_IODEVCFG] = 0x2bff8010; + s->regs[BONITO_SDCFG] = 0x255e0091; + + s->regs[BONITO_GPIODATA] = 0x1ff; + s->regs[BONITO_GPIOIE] = 0x1ff; + s->regs[BONITO_DQCFG] = 0x8; + s->regs[BONITO_MEMSIZE] = 0x10000000; + s->regs[BONITO_PCIMAP] = 0x6140; +} + +static const VMStateDescription vmstate_bonito = { + .name = "Bonito", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField []) { + VMSTATE_PCI_DEVICE(dev, PCIBonitoState), + VMSTATE_END_OF_LIST() + } +}; + +static int bonito_pcihost_initfn(SysBusDevice *dev) +{ + return 0; +} + +static int bonito_initfn(PCIDevice *dev) +{ + PCIBonitoState *s = DO_UPCAST(PCIBonitoState, dev, dev); + + /* Bonito North Bridge, built on FPGA, VENDOR_ID/DEVICE_ID are "undefined" */ + pci_config_set_vendor_id(dev->config, 0xdf53); + pci_config_set_device_id(dev->config, 0x00d5); + pci_config_set_class(dev->config, PCI_CLASS_BRIDGE_HOST); + pci_config_set_prog_interface(dev->config, 0x00); + pci_config_set_revision(dev->config, 0x01); + + /* set the north bridge register mapping */ + s->bonito_reg_handle = cpu_register_io_memory(bonito_read, bonito_write, s, + DEVICE_NATIVE_ENDIAN); + s->bonito_reg_start = BONITO_INTERNAL_REG_BASE; + s->bonito_reg_length = BONITO_INTERNAL_REG_SIZE; + cpu_register_physical_memory(s->bonito_reg_start, s->bonito_reg_length, + s->bonito_reg_handle); + + /* set the north bridge pci configure mapping */ + s->bonito_pciconf_handle = cpu_register_io_memory(bonito_pciconf_read, + bonito_pciconf_write, s, + DEVICE_NATIVE_ENDIAN); + s->bonito_pciconf_start = BONITO_PCICONFIG_BASE; + s->bonito_pciconf_length = BONITO_PCICONFIG_SIZE; + cpu_register_physical_memory(s->bonito_pciconf_start, s->bonito_pciconf_length, + s->bonito_pciconf_handle); + + /* set the south bridge pci configure mapping */ + s->bonito_spciconf_handle = cpu_register_io_memory(bonito_spciconf_read, + bonito_spciconf_write, s, + DEVICE_NATIVE_ENDIAN); + s->bonito_spciconf_start = BONITO_SPCICONFIG_BASE; + s->bonito_spciconf_length = BONITO_SPCICONFIG_SIZE; + cpu_register_physical_memory(s->bonito_spciconf_start, s->bonito_spciconf_length, + s->bonito_spciconf_handle); + + s->bonito_ldma_handle = cpu_register_io_memory(bonito_ldma_read, + bonito_ldma_write, s, + DEVICE_NATIVE_ENDIAN); + s->bonito_ldma_start = 0xbfe00200; + s->bonito_ldma_length = 0x100; + cpu_register_physical_memory(s->bonito_ldma_start, s->bonito_ldma_length, + s->bonito_ldma_handle); + + s->bonito_cop_handle = cpu_register_io_memory(bonito_cop_read, + bonito_cop_write, s, + DEVICE_NATIVE_ENDIAN); + s->bonito_cop_start = 0xbfe00300; + s->bonito_cop_length = 0x100; + cpu_register_physical_memory(s->bonito_cop_start, s->bonito_cop_length, + s->bonito_cop_handle); + + /* Map PCI IO Space 0x1fd0 0000 - 0x1fd1 0000 */ + s->bonito_pciio_start = BONITO_PCIIO_BASE; + s->bonito_pciio_length = BONITO_PCIIO_SIZE; + isa_mem_base = s->bonito_pciio_start; + isa_mmio_init(s->bonito_pciio_start, s->bonito_pciio_length); + + /* add pci local io mapping */ + s->bonito_localio_start = BONITO_DEV_BASE; + s->bonito_localio_length = BONITO_DEV_SIZE; + isa_mmio_init(s->bonito_localio_start, s->bonito_localio_length); + + /* set the default value of north bridge pci config */ + pci_set_word(dev->config + PCI_COMMAND, 0x0000); + pci_set_word(dev->config + PCI_STATUS, 0x0000); + pci_set_word(dev->config + PCI_SUBSYSTEM_VENDOR_ID, 0x0000); + pci_set_word(dev->config + PCI_SUBSYSTEM_ID, 0x0000); + + pci_set_byte(dev->config + PCI_INTERRUPT_LINE, 0x00); + pci_set_byte(dev->config + PCI_INTERRUPT_PIN, 0x01); + pci_set_byte(dev->config + PCI_MIN_GNT, 0x3c); + pci_set_byte(dev->config + PCI_MAX_LAT, 0x00); + + qemu_register_reset(bonito_reset, s); + + return 0; +} + +PCIBus *bonito_init(qemu_irq *pic) +{ + DeviceState *dev; + PCIBus *b; + BonitoState *pcihost; + PCIBonitoState *s; + PCIDevice *d; + + dev = qdev_create(NULL, "Bonito-pcihost"); + pcihost = FROM_SYSBUS(BonitoState, sysbus_from_qdev(dev)); + b = pci_register_bus(&pcihost->busdev.qdev, "pci", pci_bonito_set_irq, + pci_bonito_map_irq, pic, 0x28, 32); + pcihost->bus = b; + qdev_init_nofail(dev); + + d = pci_create_simple(b, PCI_DEVFN(0, 0), "Bonito"); + s = DO_UPCAST(PCIBonitoState, dev, d); + s->pcihost = pcihost; + bonito_state = s; + + return b; +} + +static PCIDeviceInfo bonito_info = { + .qdev.name = "Bonito", + .qdev.desc = "Host bridge", + .qdev.size = sizeof(PCIBonitoState), + .qdev.vmsd = &vmstate_bonito, + .qdev.no_user = 1, + .init = bonito_initfn, +}; + +static SysBusDeviceInfo bonito_pcihost_info = { + .init = bonito_pcihost_initfn, + .qdev.name = "Bonito-pcihost", + .qdev.size = sizeof(BonitoState), + .qdev.no_user = 1, +}; + +static void bonito_register(void) +{ + sysbus_register_withprop(&bonito_pcihost_info); + pci_qdev_register(&bonito_info); +} +device_init(bonito_register); diff -Nru qemu-kvm-0.12.5+noroms/hw/bt-hci.c qemu-kvm-0.14.1/hw/bt-hci.c --- qemu-kvm-0.12.5+noroms/hw/bt-hci.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/bt-hci.c 2011-05-11 13:29:46.000000000 +0000 @@ -994,13 +994,12 @@ static int bt_hci_version_req(struct bt_hci_s *hci, uint16_t handle) { - struct bt_device_s *slave; evt_read_remote_version_complete params; if (bt_hci_handle_bad(hci, handle)) return -ENODEV; - slave = bt_hci_remote_dev(hci, handle); + bt_hci_remote_dev(hci, handle); bt_hci_event_status(hci, HCI_SUCCESS); @@ -2080,7 +2079,6 @@ const uint8_t *data, int length) { struct bt_hci_s *hci = hci_from_info(info); - struct bt_link_s *link; uint16_t handle; int datalen; @@ -2089,7 +2087,6 @@ handle = acl_handle((data[1] << 8) | data[0]); datalen = data[2]; - data += 3; length -= 3; if (bt_hci_handle_bad(hci, handle)) { @@ -2097,7 +2094,6 @@ __FUNCTION__, handle); return; } - handle &= ~HCI_HANDLE_OFFSET; if (datalen > length) { fprintf(stderr, "%s: SCO packet too short (%iB < %iB)\n", @@ -2105,7 +2101,6 @@ return; } - link = hci->lm.handle[handle].link; /* TODO */ /* TODO: increase counter and send EVT_NUM_COMP_PKTS if synchronous diff -Nru qemu-kvm-0.12.5+noroms/hw/bt-hci-csr.c qemu-kvm-0.14.1/hw/bt-hci-csr.c --- qemu-kvm-0.12.5+noroms/hw/bt-hci-csr.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/bt-hci-csr.c 2011-05-11 13:29:46.000000000 +0000 @@ -226,7 +226,7 @@ *rpkt ++ = 0x20; /* Operational settings negotation Ok */ memcpy(rpkt, pkt, 7); rpkt += 7; *rpkt ++ = 0xff; - *rpkt ++ = 0xff; + *rpkt = 0xff; break; case H4_ALIVE_PKT: @@ -238,7 +238,7 @@ rpkt = csrhci_out_packet_csr(s, H4_ALIVE_PKT, 2); *rpkt ++ = 0xcc; - *rpkt ++ = 0x00; + *rpkt = 0x00; break; default: diff -Nru qemu-kvm-0.12.5+noroms/hw/bt-l2cap.c qemu-kvm-0.14.1/hw/bt-l2cap.c --- qemu-kvm-0.12.5+noroms/hw/bt-l2cap.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/bt-l2cap.c 2011-05-11 13:29:46.000000000 +0000 @@ -996,10 +996,10 @@ l2cap_rexmit_enable(ch, !(hdr->data[0] >> 7)); if (hdr->data[0] & 1) { - if (len != 4) - /* TODO: Signal an error? */; + if (len != 4) { + /* TODO: Signal an error? */ return; - + } return l2cap_sframe_in(ch, le16_to_cpup((void *) hdr->data)); } diff -Nru qemu-kvm-0.12.5+noroms/hw/bt-sdp.c qemu-kvm-0.14.1/hw/bt-sdp.c --- qemu-kvm-0.12.5+noroms/hw/bt-sdp.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/bt-sdp.c 2011-05-11 13:29:46.000000000 +0000 @@ -159,7 +159,7 @@ if (len < 3) return -SDP_INVALID_SYNTAX; - end = (req[0] << 8) | req[1]; + max = (req[0] << 8) | req[1]; req += 2; len -= 2; @@ -171,7 +171,7 @@ } else start = 0; - if (len > 1); + if (len > 1) return -SDP_INVALID_SYNTAX; /* Output the results */ @@ -786,11 +786,11 @@ .type = SDP_DTYPE_UUID | SDP_DSIZE_16, \ .value.uint = val, \ }, -#define TRUE { \ +#define SDP_TRUE { \ .type = SDP_DTYPE_BOOL | SDP_DSIZE_1, \ .value.uint = 1, \ }, -#define FALSE { \ +#define SDP_FALSE { \ .type = SDP_DTYPE_BOOL | SDP_DSIZE_1, \ .value.uint = 0, \ }, @@ -842,8 +842,8 @@ /* TODO: extract from l2cap_device->device.class[0] */ ATTRIBUTE(DEVICE_SUBCLASS, UINT8(0x40)) ATTRIBUTE(COUNTRY_CODE, UINT8(0x15)) - ATTRIBUTE(VIRTUAL_CABLE, TRUE) - ATTRIBUTE(RECONNECT_INITIATE, FALSE) + ATTRIBUTE(VIRTUAL_CABLE, SDP_TRUE) + ATTRIBUTE(RECONNECT_INITIATE, SDP_FALSE) /* TODO: extract from hid->usbdev->report_desc */ ATTRIBUTE(DESCRIPTOR_LIST, LIST( LIST(UINT8(0x22) ARRAY( @@ -883,12 +883,12 @@ ATTRIBUTE(LANG_ID_BASE_LIST, LIST( LIST(UINT16(0x0409) UINT16(0x0100)) )) - ATTRIBUTE(SDP_DISABLE, FALSE) - ATTRIBUTE(BATTERY_POWER, TRUE) - ATTRIBUTE(REMOTE_WAKEUP, TRUE) - ATTRIBUTE(BOOT_DEVICE, TRUE) /* XXX: untested */ + ATTRIBUTE(SDP_DISABLE, SDP_FALSE) + ATTRIBUTE(BATTERY_POWER, SDP_TRUE) + ATTRIBUTE(REMOTE_WAKEUP, SDP_TRUE) + ATTRIBUTE(BOOT_DEVICE, SDP_TRUE) /* XXX: untested */ ATTRIBUTE(SUPERVISION_TIMEOUT, UINT16(0x0c80)) - ATTRIBUTE(NORMALLY_CONNECTABLE, TRUE) + ATTRIBUTE(NORMALLY_CONNECTABLE, SDP_TRUE) ATTRIBUTE(PROFILE_VERSION, UINT16(0x0100)) ) @@ -936,7 +936,7 @@ /* Profile specific */ ATTRIBUTE(SPECIFICATION_ID, UINT16(0x0100)) ATTRIBUTE(VERSION, UINT16(0x0100)) - ATTRIBUTE(PRIMARY_RECORD, TRUE) + ATTRIBUTE(PRIMARY_RECORD, SDP_TRUE) ) static int bt_l2cap_sdp_new_ch(struct bt_l2cap_device_s *dev, diff -Nru qemu-kvm-0.12.5+noroms/hw/cirrus_vga.c qemu-kvm-0.14.1/hw/cirrus_vga.c --- qemu-kvm-0.12.5+noroms/hw/cirrus_vga.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/cirrus_vga.c 2011-05-11 13:29:46.000000000 +0000 @@ -281,63 +281,63 @@ } #define ROP_NAME 0 -#define ROP_OP(d, s) d = 0 +#define ROP_FN(d, s) 0 #include "cirrus_vga_rop.h" #define ROP_NAME src_and_dst -#define ROP_OP(d, s) d = (s) & (d) +#define ROP_FN(d, s) (s) & (d) #include "cirrus_vga_rop.h" #define ROP_NAME src_and_notdst -#define ROP_OP(d, s) d = (s) & (~(d)) +#define ROP_FN(d, s) (s) & (~(d)) #include "cirrus_vga_rop.h" #define ROP_NAME notdst -#define ROP_OP(d, s) d = ~(d) +#define ROP_FN(d, s) ~(d) #include "cirrus_vga_rop.h" #define ROP_NAME src -#define ROP_OP(d, s) d = s +#define ROP_FN(d, s) s #include "cirrus_vga_rop.h" #define ROP_NAME 1 -#define ROP_OP(d, s) d = ~0 +#define ROP_FN(d, s) ~0 #include "cirrus_vga_rop.h" #define ROP_NAME notsrc_and_dst -#define ROP_OP(d, s) d = (~(s)) & (d) +#define ROP_FN(d, s) (~(s)) & (d) #include "cirrus_vga_rop.h" #define ROP_NAME src_xor_dst -#define ROP_OP(d, s) d = (s) ^ (d) +#define ROP_FN(d, s) (s) ^ (d) #include "cirrus_vga_rop.h" #define ROP_NAME src_or_dst -#define ROP_OP(d, s) d = (s) | (d) +#define ROP_FN(d, s) (s) | (d) #include "cirrus_vga_rop.h" #define ROP_NAME notsrc_or_notdst -#define ROP_OP(d, s) d = (~(s)) | (~(d)) +#define ROP_FN(d, s) (~(s)) | (~(d)) #include "cirrus_vga_rop.h" #define ROP_NAME src_notxor_dst -#define ROP_OP(d, s) d = ~((s) ^ (d)) +#define ROP_FN(d, s) ~((s) ^ (d)) #include "cirrus_vga_rop.h" #define ROP_NAME src_or_notdst -#define ROP_OP(d, s) d = (s) | (~(d)) +#define ROP_FN(d, s) (s) | (~(d)) #include "cirrus_vga_rop.h" #define ROP_NAME notsrc -#define ROP_OP(d, s) d = (~(s)) +#define ROP_FN(d, s) (~(s)) #include "cirrus_vga_rop.h" #define ROP_NAME notsrc_or_dst -#define ROP_OP(d, s) d = (~(s)) | (d) +#define ROP_FN(d, s) (~(s)) | (d) #include "cirrus_vga_rop.h" #define ROP_NAME notsrc_and_notdst -#define ROP_OP(d, s) d = (~(s)) & (~(d)) +#define ROP_FN(d, s) (~(s)) & (~(d)) #include "cirrus_vga_rop.h" static const cirrus_bitblt_rop_t cirrus_fwd_rop[16] = { @@ -674,45 +674,46 @@ static void cirrus_do_copy(CirrusVGAState *s, int dst, int src, int w, int h) { - int sx, sy; - int dx, dy; - int width, height; - int depth; + int sx = 0, sy = 0; + int dx = 0, dy = 0; + int depth = 0; int notify = 0; - depth = s->vga.get_bpp(&s->vga) / 8; - s->vga.get_resolution(&s->vga, &width, &height); + /* make sure to only copy if it's a plain copy ROP */ + if (*s->cirrus_rop == cirrus_bitblt_rop_fwd_src || + *s->cirrus_rop == cirrus_bitblt_rop_bkwd_src) { + + int width, height; + + depth = s->vga.get_bpp(&s->vga) / 8; + s->vga.get_resolution(&s->vga, &width, &height); + + /* extra x, y */ + sx = (src % ABS(s->cirrus_blt_srcpitch)) / depth; + sy = (src / ABS(s->cirrus_blt_srcpitch)); + dx = (dst % ABS(s->cirrus_blt_dstpitch)) / depth; + dy = (dst / ABS(s->cirrus_blt_dstpitch)); + + /* normalize width */ + w /= depth; + + /* if we're doing a backward copy, we have to adjust + our x/y to be the upper left corner (instead of the lower + right corner) */ + if (s->cirrus_blt_dstpitch < 0) { + sx -= (s->cirrus_blt_width / depth) - 1; + dx -= (s->cirrus_blt_width / depth) - 1; + sy -= s->cirrus_blt_height - 1; + dy -= s->cirrus_blt_height - 1; + } - /* extra x, y */ - sx = (src % ABS(s->cirrus_blt_srcpitch)) / depth; - sy = (src / ABS(s->cirrus_blt_srcpitch)); - dx = (dst % ABS(s->cirrus_blt_dstpitch)) / depth; - dy = (dst / ABS(s->cirrus_blt_dstpitch)); - - /* normalize width */ - w /= depth; - - /* if we're doing a backward copy, we have to adjust - our x/y to be the upper left corner (instead of the lower - right corner) */ - if (s->cirrus_blt_dstpitch < 0) { - sx -= (s->cirrus_blt_width / depth) - 1; - dx -= (s->cirrus_blt_width / depth) - 1; - sy -= s->cirrus_blt_height - 1; - dy -= s->cirrus_blt_height - 1; - } - - /* are we in the visible portion of memory? */ - if (sx >= 0 && sy >= 0 && dx >= 0 && dy >= 0 && - (sx + w) <= width && (sy + h) <= height && - (dx + w) <= width && (dy + h) <= height) { - notify = 1; - } - - /* make to sure only copy if it's a plain copy ROP */ - if (*s->cirrus_rop != cirrus_bitblt_rop_fwd_src && - *s->cirrus_rop != cirrus_bitblt_rop_bkwd_src) - notify = 0; + /* are we in the visible portion of memory? */ + if (sx >= 0 && sy >= 0 && dx >= 0 && dy >= 0 && + (sx + w) <= width && (sy + h) <= height && + (dx + w) <= width && (dy + h) <= height) { + notify = 1; + } + } /* we have to flush all pending changes so that the copy is generated at the appropriate moment in time */ @@ -2004,30 +2005,20 @@ static uint32_t cirrus_vga_mem_readw(void *opaque, target_phys_addr_t addr) { uint32_t v; -#ifdef TARGET_WORDS_BIGENDIAN - v = cirrus_vga_mem_readb(opaque, addr) << 8; - v |= cirrus_vga_mem_readb(opaque, addr + 1); -#else + v = cirrus_vga_mem_readb(opaque, addr); v |= cirrus_vga_mem_readb(opaque, addr + 1) << 8; -#endif return v; } static uint32_t cirrus_vga_mem_readl(void *opaque, target_phys_addr_t addr) { uint32_t v; -#ifdef TARGET_WORDS_BIGENDIAN - v = cirrus_vga_mem_readb(opaque, addr) << 24; - v |= cirrus_vga_mem_readb(opaque, addr + 1) << 16; - v |= cirrus_vga_mem_readb(opaque, addr + 2) << 8; - v |= cirrus_vga_mem_readb(opaque, addr + 3); -#else + v = cirrus_vga_mem_readb(opaque, addr); v |= cirrus_vga_mem_readb(opaque, addr + 1) << 8; v |= cirrus_vga_mem_readb(opaque, addr + 2) << 16; v |= cirrus_vga_mem_readb(opaque, addr + 3) << 24; -#endif return v; } @@ -2098,28 +2089,16 @@ static void cirrus_vga_mem_writew(void *opaque, target_phys_addr_t addr, uint32_t val) { -#ifdef TARGET_WORDS_BIGENDIAN - cirrus_vga_mem_writeb(opaque, addr, (val >> 8) & 0xff); - cirrus_vga_mem_writeb(opaque, addr + 1, val & 0xff); -#else cirrus_vga_mem_writeb(opaque, addr, val & 0xff); cirrus_vga_mem_writeb(opaque, addr + 1, (val >> 8) & 0xff); -#endif } static void cirrus_vga_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) { -#ifdef TARGET_WORDS_BIGENDIAN - cirrus_vga_mem_writeb(opaque, addr, (val >> 24) & 0xff); - cirrus_vga_mem_writeb(opaque, addr + 1, (val >> 16) & 0xff); - cirrus_vga_mem_writeb(opaque, addr + 2, (val >> 8) & 0xff); - cirrus_vga_mem_writeb(opaque, addr + 3, val & 0xff); -#else cirrus_vga_mem_writeb(opaque, addr, val & 0xff); cirrus_vga_mem_writeb(opaque, addr + 1, (val >> 8) & 0xff); cirrus_vga_mem_writeb(opaque, addr + 2, (val >> 16) & 0xff); cirrus_vga_mem_writeb(opaque, addr + 3, (val >> 24) & 0xff); -#endif } static CPUReadMemoryFunc * const cirrus_vga_mem_read[3] = { @@ -2341,30 +2320,20 @@ static uint32_t cirrus_linear_readw(void *opaque, target_phys_addr_t addr) { uint32_t v; -#ifdef TARGET_WORDS_BIGENDIAN - v = cirrus_linear_readb(opaque, addr) << 8; - v |= cirrus_linear_readb(opaque, addr + 1); -#else + v = cirrus_linear_readb(opaque, addr); v |= cirrus_linear_readb(opaque, addr + 1) << 8; -#endif return v; } static uint32_t cirrus_linear_readl(void *opaque, target_phys_addr_t addr) { uint32_t v; -#ifdef TARGET_WORDS_BIGENDIAN - v = cirrus_linear_readb(opaque, addr) << 24; - v |= cirrus_linear_readb(opaque, addr + 1) << 16; - v |= cirrus_linear_readb(opaque, addr + 2) << 8; - v |= cirrus_linear_readb(opaque, addr + 3); -#else + v = cirrus_linear_readb(opaque, addr); v |= cirrus_linear_readb(opaque, addr + 1) << 8; v |= cirrus_linear_readb(opaque, addr + 2) << 16; v |= cirrus_linear_readb(opaque, addr + 3) << 24; -#endif return v; } @@ -2412,29 +2381,17 @@ static void cirrus_linear_writew(void *opaque, target_phys_addr_t addr, uint32_t val) { -#ifdef TARGET_WORDS_BIGENDIAN - cirrus_linear_writeb(opaque, addr, (val >> 8) & 0xff); - cirrus_linear_writeb(opaque, addr + 1, val & 0xff); -#else cirrus_linear_writeb(opaque, addr, val & 0xff); cirrus_linear_writeb(opaque, addr + 1, (val >> 8) & 0xff); -#endif } static void cirrus_linear_writel(void *opaque, target_phys_addr_t addr, uint32_t val) { -#ifdef TARGET_WORDS_BIGENDIAN - cirrus_linear_writeb(opaque, addr, (val >> 24) & 0xff); - cirrus_linear_writeb(opaque, addr + 1, (val >> 16) & 0xff); - cirrus_linear_writeb(opaque, addr + 2, (val >> 8) & 0xff); - cirrus_linear_writeb(opaque, addr + 3, val & 0xff); -#else cirrus_linear_writeb(opaque, addr, val & 0xff); cirrus_linear_writeb(opaque, addr + 1, (val >> 8) & 0xff); cirrus_linear_writeb(opaque, addr + 2, (val >> 16) & 0xff); cirrus_linear_writeb(opaque, addr + 3, (val >> 24) & 0xff); -#endif } @@ -2469,30 +2426,20 @@ static uint32_t cirrus_linear_bitblt_readw(void *opaque, target_phys_addr_t addr) { uint32_t v; -#ifdef TARGET_WORDS_BIGENDIAN - v = cirrus_linear_bitblt_readb(opaque, addr) << 8; - v |= cirrus_linear_bitblt_readb(opaque, addr + 1); -#else + v = cirrus_linear_bitblt_readb(opaque, addr); v |= cirrus_linear_bitblt_readb(opaque, addr + 1) << 8; -#endif return v; } static uint32_t cirrus_linear_bitblt_readl(void *opaque, target_phys_addr_t addr) { uint32_t v; -#ifdef TARGET_WORDS_BIGENDIAN - v = cirrus_linear_bitblt_readb(opaque, addr) << 24; - v |= cirrus_linear_bitblt_readb(opaque, addr + 1) << 16; - v |= cirrus_linear_bitblt_readb(opaque, addr + 2) << 8; - v |= cirrus_linear_bitblt_readb(opaque, addr + 3); -#else + v = cirrus_linear_bitblt_readb(opaque, addr); v |= cirrus_linear_bitblt_readb(opaque, addr + 1) << 8; v |= cirrus_linear_bitblt_readb(opaque, addr + 2) << 16; v |= cirrus_linear_bitblt_readb(opaque, addr + 3) << 24; -#endif return v; } @@ -2513,29 +2460,17 @@ static void cirrus_linear_bitblt_writew(void *opaque, target_phys_addr_t addr, uint32_t val) { -#ifdef TARGET_WORDS_BIGENDIAN - cirrus_linear_bitblt_writeb(opaque, addr, (val >> 8) & 0xff); - cirrus_linear_bitblt_writeb(opaque, addr + 1, val & 0xff); -#else cirrus_linear_bitblt_writeb(opaque, addr, val & 0xff); cirrus_linear_bitblt_writeb(opaque, addr + 1, (val >> 8) & 0xff); -#endif } static void cirrus_linear_bitblt_writel(void *opaque, target_phys_addr_t addr, uint32_t val) { -#ifdef TARGET_WORDS_BIGENDIAN - cirrus_linear_bitblt_writeb(opaque, addr, (val >> 24) & 0xff); - cirrus_linear_bitblt_writeb(opaque, addr + 1, (val >> 16) & 0xff); - cirrus_linear_bitblt_writeb(opaque, addr + 2, (val >> 8) & 0xff); - cirrus_linear_bitblt_writeb(opaque, addr + 3, val & 0xff); -#else cirrus_linear_bitblt_writeb(opaque, addr, val & 0xff); cirrus_linear_bitblt_writeb(opaque, addr + 1, (val >> 8) & 0xff); cirrus_linear_bitblt_writeb(opaque, addr + 2, (val >> 16) & 0xff); cirrus_linear_bitblt_writeb(opaque, addr + 3, (val >> 24) & 0xff); -#endif } @@ -2853,30 +2788,20 @@ static uint32_t cirrus_mmio_readw(void *opaque, target_phys_addr_t addr) { uint32_t v; -#ifdef TARGET_WORDS_BIGENDIAN - v = cirrus_mmio_readb(opaque, addr) << 8; - v |= cirrus_mmio_readb(opaque, addr + 1); -#else + v = cirrus_mmio_readb(opaque, addr); v |= cirrus_mmio_readb(opaque, addr + 1) << 8; -#endif return v; } static uint32_t cirrus_mmio_readl(void *opaque, target_phys_addr_t addr) { uint32_t v; -#ifdef TARGET_WORDS_BIGENDIAN - v = cirrus_mmio_readb(opaque, addr) << 24; - v |= cirrus_mmio_readb(opaque, addr + 1) << 16; - v |= cirrus_mmio_readb(opaque, addr + 2) << 8; - v |= cirrus_mmio_readb(opaque, addr + 3); -#else + v = cirrus_mmio_readb(opaque, addr); v |= cirrus_mmio_readb(opaque, addr + 1) << 8; v |= cirrus_mmio_readb(opaque, addr + 2) << 16; v |= cirrus_mmio_readb(opaque, addr + 3) << 24; -#endif return v; } @@ -2897,29 +2822,17 @@ static void cirrus_mmio_writew(void *opaque, target_phys_addr_t addr, uint32_t val) { -#ifdef TARGET_WORDS_BIGENDIAN - cirrus_mmio_writeb(opaque, addr, (val >> 8) & 0xff); - cirrus_mmio_writeb(opaque, addr + 1, val & 0xff); -#else cirrus_mmio_writeb(opaque, addr, val & 0xff); cirrus_mmio_writeb(opaque, addr + 1, (val >> 8) & 0xff); -#endif } static void cirrus_mmio_writel(void *opaque, target_phys_addr_t addr, uint32_t val) { -#ifdef TARGET_WORDS_BIGENDIAN - cirrus_mmio_writeb(opaque, addr, (val >> 24) & 0xff); - cirrus_mmio_writeb(opaque, addr + 1, (val >> 16) & 0xff); - cirrus_mmio_writeb(opaque, addr + 2, (val >> 8) & 0xff); - cirrus_mmio_writeb(opaque, addr + 3, val & 0xff); -#else cirrus_mmio_writeb(opaque, addr, val & 0xff); cirrus_mmio_writeb(opaque, addr + 1, (val >> 8) & 0xff); cirrus_mmio_writeb(opaque, addr + 2, (val >> 16) & 0xff); cirrus_mmio_writeb(opaque, addr + 3, (val >> 24) & 0xff); -#endif } @@ -2997,7 +2910,6 @@ .version_id = 2, .minimum_version_id = 2, .minimum_version_id_old = 2, - .post_load = cirrus_post_load, .fields = (VMStateField []) { VMSTATE_PCI_DEVICE(dev, PCICirrusVGAState), VMSTATE_STRUCT(cirrus_vga, PCICirrusVGAState, 0, @@ -3089,23 +3001,27 @@ register_ioport_read(0x3da, 1, 1, cirrus_vga_ioport_read, s); s->vga.vga_io_memory = cpu_register_io_memory(cirrus_vga_mem_read, - cirrus_vga_mem_write, s); + cirrus_vga_mem_write, s, + DEVICE_LITTLE_ENDIAN); cpu_register_physical_memory(isa_mem_base + 0x000a0000, 0x20000, s->vga.vga_io_memory); qemu_register_coalesced_mmio(isa_mem_base + 0x000a0000, 0x20000); /* I/O handler for LFB */ s->cirrus_linear_io_addr = - cpu_register_io_memory(cirrus_linear_read, cirrus_linear_write, s); + cpu_register_io_memory(cirrus_linear_read, cirrus_linear_write, s, + DEVICE_LITTLE_ENDIAN); /* I/O handler for LFB */ s->cirrus_linear_bitblt_io_addr = cpu_register_io_memory(cirrus_linear_bitblt_read, - cirrus_linear_bitblt_write, s); + cirrus_linear_bitblt_write, s, + DEVICE_LITTLE_ENDIAN); /* I/O handler for memory-mapped I/O */ s->cirrus_mmio_io_addr = - cpu_register_io_memory(cirrus_mmio_read, cirrus_mmio_write, s); + cpu_register_io_memory(cirrus_mmio_read, cirrus_mmio_write, s, + DEVICE_LITTLE_ENDIAN); s->real_vram_size = (s->device_id == CIRRUS_ID_CLGD5446) ? 4096 * 1024 : 2048 * 1024; @@ -3141,7 +3057,7 @@ s->vga.ds = graphic_console_init(s->vga.update, s->vga.invalidate, s->vga.screen_dump, s->vga.text_update, &s->vga); - vmstate_register(0, &vmstate_cirrus_vga, s); + vmstate_register(NULL, 0, &vmstate_cirrus_vga, s); rom_add_vga(VGABIOS_CIRRUS_FILENAME); /* XXX ISA-LFB support */ } @@ -3223,10 +3139,10 @@ /* memory #0 LFB */ /* memory #1 memory-mapped I/O */ /* XXX: s->vga.vram_size must be a power of two */ - pci_register_bar((PCIDevice *)d, 0, 0x2000000, + pci_register_bar(&d->dev, 0, 0x2000000, PCI_BASE_ADDRESS_MEM_PREFETCH, cirrus_pci_lfb_map); if (device_id == CIRRUS_ID_CLGD5446) { - pci_register_bar((PCIDevice *)d, 1, CIRRUS_PNPMMIO_SIZE, + pci_register_bar(&d->dev, 1, CIRRUS_PNPMMIO_SIZE, PCI_BASE_ADDRESS_SPACE_MEMORY, cirrus_pci_mmio_map); } return 0; @@ -3242,6 +3158,7 @@ .qdev.desc = "Cirrus CLGD 54xx VGA", .qdev.size = sizeof(PCICirrusVGAState), .qdev.vmsd = &vmstate_pci_cirrus_vga, + .no_hotplug = 1, .init = pci_cirrus_vga_initfn, .romfile = VGABIOS_CIRRUS_FILENAME, .config_write = pci_cirrus_write_config, diff -Nru qemu-kvm-0.12.5+noroms/hw/cirrus_vga_rop2.h qemu-kvm-0.14.1/hw/cirrus_vga_rop2.h --- qemu-kvm-0.12.5+noroms/hw/cirrus_vga_rop2.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/cirrus_vga_rop2.h 2011-05-11 13:29:46.000000000 +0000 @@ -23,15 +23,15 @@ */ #if DEPTH == 8 -#define PUTPIXEL() ROP_OP(d[0], col) +#define PUTPIXEL() ROP_OP(&d[0], col) #elif DEPTH == 16 -#define PUTPIXEL() ROP_OP(((uint16_t *)d)[0], col); +#define PUTPIXEL() ROP_OP_16((uint16_t *)&d[0], col) #elif DEPTH == 24 -#define PUTPIXEL() ROP_OP(d[0], col); \ - ROP_OP(d[1], (col >> 8)); \ - ROP_OP(d[2], (col >> 16)) +#define PUTPIXEL() ROP_OP(&d[0], col); \ + ROP_OP(&d[1], (col >> 8)); \ + ROP_OP(&d[2], (col >> 16)) #elif DEPTH == 32 -#define PUTPIXEL() ROP_OP(((uint32_t *)d)[0], col) +#define PUTPIXEL() ROP_OP_32(((uint32_t *)&d[0]), col) #else #error unsupported DEPTH #endif diff -Nru qemu-kvm-0.12.5+noroms/hw/cirrus_vga_rop.h qemu-kvm-0.14.1/hw/cirrus_vga_rop.h --- qemu-kvm-0.12.5+noroms/hw/cirrus_vga_rop.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/cirrus_vga_rop.h 2011-05-11 13:29:46.000000000 +0000 @@ -22,6 +22,26 @@ * THE SOFTWARE. */ +static inline void glue(rop_8_,ROP_NAME)(uint8_t *dst, uint8_t src) +{ + *dst = ROP_FN(*dst, src); +} + +static inline void glue(rop_16_,ROP_NAME)(uint16_t *dst, uint16_t src) +{ + *dst = ROP_FN(*dst, src); +} + +static inline void glue(rop_32_,ROP_NAME)(uint32_t *dst, uint32_t src) +{ + *dst = ROP_FN(*dst, src); +} + +#define ROP_OP(d, s) glue(rop_8_,ROP_NAME)(d, s) +#define ROP_OP_16(d, s) glue(rop_16_,ROP_NAME)(d, s) +#define ROP_OP_32(d, s) glue(rop_32_,ROP_NAME)(d, s) +#undef ROP_FN + static void glue(cirrus_bitblt_rop_fwd_, ROP_NAME)(CirrusVGAState *s, uint8_t *dst,const uint8_t *src, @@ -39,7 +59,7 @@ for (y = 0; y < bltheight; y++) { for (x = 0; x < bltwidth; x++) { - ROP_OP(*dst, *src); + ROP_OP(dst, *src); dst++; src++; } @@ -59,7 +79,7 @@ srcpitch += bltwidth; for (y = 0; y < bltheight; y++) { for (x = 0; x < bltwidth; x++) { - ROP_OP(*dst, *src); + ROP_OP(dst, *src); dst--; src--; } @@ -81,7 +101,7 @@ for (y = 0; y < bltheight; y++) { for (x = 0; x < bltwidth; x++) { p = *dst; - ROP_OP(p, *src); + ROP_OP(&p, *src); if (p != s->vga.gr[0x34]) *dst = p; dst++; src++; @@ -104,7 +124,7 @@ for (y = 0; y < bltheight; y++) { for (x = 0; x < bltwidth; x++) { p = *dst; - ROP_OP(p, *src); + ROP_OP(&p, *src); if (p != s->vga.gr[0x34]) *dst = p; dst--; src--; @@ -128,8 +148,8 @@ for (x = 0; x < bltwidth; x+=2) { p1 = *dst; p2 = *(dst+1); - ROP_OP(p1, *src); - ROP_OP(p2, *(src+1)); + ROP_OP(&p1, *src); + ROP_OP(&p2, *(src + 1)); if ((p1 != s->vga.gr[0x34]) || (p2 != s->vga.gr[0x35])) { *dst = p1; *(dst+1) = p2; @@ -156,8 +176,8 @@ for (x = 0; x < bltwidth; x+=2) { p1 = *(dst-1); p2 = *dst; - ROP_OP(p1, *(src-1)); - ROP_OP(p2, *src); + ROP_OP(&p1, *(src - 1)); + ROP_OP(&p2, *src); if ((p1 != s->vga.gr[0x34]) || (p2 != s->vga.gr[0x35])) { *(dst-1) = p1; *dst = p2; @@ -184,3 +204,5 @@ #undef ROP_NAME #undef ROP_OP +#undef ROP_OP_16 +#undef ROP_OP_32 diff -Nru qemu-kvm-0.12.5+noroms/hw/cris-boot.c qemu-kvm-0.14.1/hw/cris-boot.c --- qemu-kvm-0.12.5+noroms/hw/cris-boot.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/cris-boot.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,97 @@ +/* + * CRIS image loading. + * + * Copyright (c) 2010 Edgar E. Iglesias, Axis Communications AB. + * + * 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 "hw.h" +#include "sysemu.h" +#include "loader.h" +#include "elf.h" +#include "cris-boot.h" + +static void main_cpu_reset(void *opaque) +{ + CPUState *env = opaque; + struct cris_load_info *li; + + li = env->load_info; + + cpu_reset(env); + + if (!li) { + /* nothing more to do. */ + return; + } + + env->pc = li->entry; + + if (li->image_filename) { + env->regs[8] = 0x56902387; /* RAM boot magic. */ + env->regs[9] = 0x40004000 + li->image_size; + } + + if (li->cmdline) { + /* Let the kernel know we are modifying the cmdline. */ + env->regs[10] = 0x87109563; + env->regs[11] = 0x40000000; + } +} + +static uint64_t translate_kernel_address(void *opaque, uint64_t addr) +{ + return addr - 0x80000000LL; +} + +void cris_load_image(CPUState *env, struct cris_load_info *li) +{ + uint64_t entry, high; + int kcmdline_len; + int image_size; + + env->load_info = li; + /* Boots a kernel elf binary, os/linux-2.6/vmlinux from the axis + devboard SDK. */ + image_size = load_elf(li->image_filename, translate_kernel_address, NULL, + &entry, NULL, &high, 0, ELF_MACHINE, 0); + li->entry = entry; + if (image_size < 0) { + /* Takes a kimage from the axis devboard SDK. */ + image_size = load_image_targphys(li->image_filename, 0x40004000, + ram_size); + li->entry = 0x40004000; + } + + if (image_size < 0) { + fprintf(stderr, "qemu: could not load kernel '%s'\n", + li->image_filename); + exit(1); + } + + if (li->cmdline && (kcmdline_len = strlen(li->cmdline))) { + if (kcmdline_len > 256) { + fprintf(stderr, "Too long CRIS kernel cmdline (max 256)\n"); + exit(1); + } + pstrcpy_targphys("cmdline", 0x40000000, 256, li->cmdline); + } + qemu_register_reset(main_cpu_reset, env); +} diff -Nru qemu-kvm-0.12.5+noroms/hw/cris-boot.h qemu-kvm-0.14.1/hw/cris-boot.h --- qemu-kvm-0.12.5+noroms/hw/cris-boot.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/cris-boot.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,11 @@ + +struct cris_load_info +{ + const char *image_filename; + const char *cmdline; + int image_size; + + target_phys_addr_t entry; +}; + +void cris_load_image(CPUState *env, struct cris_load_info *li); diff -Nru qemu-kvm-0.12.5+noroms/hw/cs4231a.c qemu-kvm-0.14.1/hw/cs4231a.c --- qemu-kvm-0.12.5+noroms/hw/cs4231a.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/cs4231a.c 2011-05-11 13:29:46.000000000 +0000 @@ -645,6 +645,7 @@ isa_init_irq (dev, &s->pic, s->irq); for (i = 0; i < 4; i++) { + isa_init_ioport(dev, i); register_ioport_write (s->port + i, 1, 1, cs_write, s); register_ioport_read (s->port + i, 1, 1, cs_read, s); } diff -Nru qemu-kvm-0.12.5+noroms/hw/cs4231.c qemu-kvm-0.14.1/hw/cs4231.c --- qemu-kvm-0.12.5+noroms/hw/cs4231.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/cs4231.c 2011-05-11 13:29:46.000000000 +0000 @@ -23,9 +23,7 @@ */ #include "sysbus.h" - -/* debug CS4231 */ -//#define DEBUG_CS +#include "trace.h" /* * In addition to Crystal CS4231 there is a DMA controller on Sparc. @@ -46,13 +44,6 @@ #define CS_VER 0xa0 #define CS_CDC_VER 0x8a -#ifdef DEBUG_CS -#define DPRINTF(fmt, ...) \ - do { printf("CS: " fmt , ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) -#endif - static void cs_reset(DeviceState *d) { CSState *s = container_of(d, CSState, busdev.qdev); @@ -79,11 +70,11 @@ ret = s->dregs[CS_RAP(s)]; break; } - DPRINTF("read dreg[%d]: 0x%8.8x\n", CS_RAP(s), ret); + trace_cs4231_mem_readl_dreg(CS_RAP(s), ret); break; default: ret = s->regs[saddr]; - DPRINTF("read reg[%d]: 0x%8.8x\n", saddr, ret); + trace_cs4231_mem_readl_reg(saddr, ret); break; } return ret; @@ -95,11 +86,10 @@ uint32_t saddr; saddr = addr >> 2; - DPRINTF("write reg[%d]: 0x%8.8x -> 0x%8.8x\n", saddr, s->regs[saddr], val); + trace_cs4231_mem_writel_reg(saddr, s->regs[saddr], val); switch (saddr) { case 1: - DPRINTF("write dreg[%d]: 0x%2.2x -> 0x%2.2x\n", CS_RAP(s), - s->dregs[CS_RAP(s)], val); + trace_cs4231_mem_writel_dreg(CS_RAP(s), s->dregs[CS_RAP(s)], val); switch(CS_RAP(s)) { case 11: case 25: // Read only @@ -158,7 +148,8 @@ int io; CSState *s = FROM_SYSBUS(CSState, dev); - io = cpu_register_io_memory(cs_mem_read, cs_mem_write, s); + io = cpu_register_io_memory(cs_mem_read, cs_mem_write, s, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, CS_SIZE, io); sysbus_init_irq(dev, &s->irq); diff -Nru qemu-kvm-0.12.5+noroms/hw/cuda.c qemu-kvm-0.14.1/hw/cuda.c --- qemu-kvm-0.12.5+noroms/hw/cuda.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/cuda.c 2011-05-11 13:29:46.000000000 +0000 @@ -316,8 +316,10 @@ val = s->anh; break; } - if (addr != 13 || val != 0) + if (addr != 13 || val != 0) { CUDA_DPRINTF("read: reg=0x%x val=%02x\n", (int)addr, val); + } + return val; } @@ -760,7 +762,8 @@ s->tick_offset = (uint32_t)mktimegm(&tm) + RTC_OFFSET; s->adb_poll_timer = qemu_new_timer(vm_clock, cuda_adb_poll, s); - *cuda_mem_index = cpu_register_io_memory(cuda_read, cuda_write, s); - register_savevm("cuda", -1, 1, cuda_save, cuda_load, s); + *cuda_mem_index = cpu_register_io_memory(cuda_read, cuda_write, s, + DEVICE_NATIVE_ENDIAN); + register_savevm(NULL, "cuda", -1, 1, cuda_save, cuda_load, s); qemu_register_reset(cuda_reset, s); } diff -Nru qemu-kvm-0.12.5+noroms/hw/debugcon.c qemu-kvm-0.14.1/hw/debugcon.c --- qemu-kvm-0.12.5+noroms/hw/debugcon.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/debugcon.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,107 @@ +/* + * QEMU Bochs-style debug console ("port E9") emulation + * + * Copyright (c) 2003-2004 Fabrice Bellard + * Copyright (c) 2008 Citrix Systems, Inc. + * Copyright (c) Intel Corporation; author: H. Peter Anvin + * + * 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 "hw.h" +#include "qemu-char.h" +#include "isa.h" +#include "pc.h" + +//#define DEBUG_DEBUGCON + +typedef struct DebugconState { + CharDriverState *chr; + uint32_t readback; +} DebugconState; + +typedef struct ISADebugconState { + ISADevice dev; + uint32_t iobase; + DebugconState state; +} ISADebugconState; + +static void debugcon_ioport_write(void *opaque, uint32_t addr, uint32_t val) +{ + DebugconState *s = opaque; + unsigned char ch = val; + +#ifdef DEBUG_DEBUGCON + printf("debugcon: write addr=0x%04x val=0x%02x\n", addr, val); +#endif + + qemu_chr_write(s->chr, &ch, 1); +} + + +static uint32_t debugcon_ioport_read(void *opaque, uint32_t addr) +{ + DebugconState *s = opaque; + +#ifdef DEBUG_DEBUGCON + printf("debugcon: read addr=0x%04x\n", addr); +#endif + + return s->readback; +} + +static void debugcon_init_core(DebugconState *s) +{ + if (!s->chr) { + fprintf(stderr, "Can't create debugcon device, empty char device\n"); + exit(1); + } + + qemu_chr_add_handlers(s->chr, NULL, NULL, NULL, s); +} + +static int debugcon_isa_initfn(ISADevice *dev) +{ + ISADebugconState *isa = DO_UPCAST(ISADebugconState, dev, dev); + DebugconState *s = &isa->state; + + debugcon_init_core(s); + register_ioport_write(isa->iobase, 1, 1, debugcon_ioport_write, s); + register_ioport_read(isa->iobase, 1, 1, debugcon_ioport_read, s); + return 0; +} + +static ISADeviceInfo debugcon_isa_info = { + .qdev.name = "isa-debugcon", + .qdev.size = sizeof(ISADebugconState), + .init = debugcon_isa_initfn, + .qdev.props = (Property[]) { + DEFINE_PROP_HEX32("iobase", ISADebugconState, iobase, 0xe9), + DEFINE_PROP_CHR("chardev", ISADebugconState, state.chr), + DEFINE_PROP_HEX32("readback", ISADebugconState, state.readback, 0xe9), + DEFINE_PROP_END_OF_LIST(), + }, +}; + +static void debugcon_register_devices(void) +{ + isa_qdev_register(&debugcon_isa_info); +} + +device_init(debugcon_register_devices) diff -Nru qemu-kvm-0.12.5+noroms/hw/dec_pci.c qemu-kvm-0.14.1/hw/dec_pci.c --- qemu-kvm-0.12.5+noroms/hw/dec_pci.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/dec_pci.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,133 @@ +/* + * QEMU DEC 21154 PCI bridge + * + * Copyright (c) 2006-2007 Fabrice Bellard + * Copyright (c) 2007 Jocelyn Mayer + * + * 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 "dec_pci.h" +#include "sysbus.h" +#include "pci.h" +#include "pci_host.h" +#include "pci_bridge.h" +#include "pci_internals.h" + +/* debug DEC */ +//#define DEBUG_DEC + +#ifdef DEBUG_DEC +#define DEC_DPRINTF(fmt, ...) \ + do { printf("DEC: " fmt , ## __VA_ARGS__); } while (0) +#else +#define DEC_DPRINTF(fmt, ...) +#endif + +typedef struct DECState { + SysBusDevice busdev; + PCIHostState host_state; +} DECState; + +static int dec_map_irq(PCIDevice *pci_dev, int irq_num) +{ + return irq_num; +} + +static int dec_21154_initfn(PCIDevice *dev) +{ + int rc; + + rc = pci_bridge_initfn(dev); + if (rc < 0) { + return rc; + } + + pci_config_set_vendor_id(dev->config, PCI_VENDOR_ID_DEC); + pci_config_set_device_id(dev->config, PCI_DEVICE_ID_DEC_21154); + return 0; +} + +static PCIDeviceInfo dec_21154_pci_bridge_info = { + .qdev.name = "dec-21154-p2p-bridge", + .qdev.desc = "DEC 21154 PCI-PCI bridge", + .qdev.size = sizeof(PCIBridge), + .qdev.vmsd = &vmstate_pci_device, + .qdev.reset = pci_bridge_reset, + .init = dec_21154_initfn, + .exit = pci_bridge_exitfn, + .config_write = pci_bridge_write_config, + .is_bridge = 1, +}; + +PCIBus *pci_dec_21154_init(PCIBus *parent_bus, int devfn) +{ + PCIDevice *dev; + PCIBridge *br; + + dev = pci_create_multifunction(parent_bus, devfn, false, + "dec-21154-p2p-bridge"); + br = DO_UPCAST(PCIBridge, dev, dev); + pci_bridge_map_irq(br, "DEC 21154 PCI-PCI bridge", dec_map_irq); + qdev_init_nofail(&dev->qdev); + return pci_bridge_get_sec_bus(br); +} + +static int pci_dec_21154_init_device(SysBusDevice *dev) +{ + DECState *s; + int pci_mem_config, pci_mem_data; + + s = FROM_SYSBUS(DECState, dev); + + pci_mem_config = pci_host_conf_register_mmio(&s->host_state, + DEVICE_LITTLE_ENDIAN); + pci_mem_data = pci_host_data_register_mmio(&s->host_state, + DEVICE_LITTLE_ENDIAN); + sysbus_init_mmio(dev, 0x1000, pci_mem_config); + sysbus_init_mmio(dev, 0x1000, pci_mem_data); + return 0; +} + +static int dec_21154_pci_host_init(PCIDevice *d) +{ + /* PCI2PCI bridge same values as PearPC - check this */ + pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_DEC); + pci_config_set_device_id(d->config, PCI_DEVICE_ID_DEC_21154); + pci_set_byte(d->config + PCI_REVISION_ID, 0x02); + pci_config_set_class(d->config, PCI_CLASS_BRIDGE_PCI); + return 0; +} + +static PCIDeviceInfo dec_21154_pci_host_info = { + .qdev.name = "dec-21154", + .qdev.size = sizeof(PCIDevice), + .init = dec_21154_pci_host_init, + .is_bridge = 1, +}; + +static void dec_register_devices(void) +{ + sysbus_register_dev("dec-21154", sizeof(DECState), + pci_dec_21154_init_device); + pci_qdev_register(&dec_21154_pci_host_info); + pci_qdev_register(&dec_21154_pci_bridge_info); +} + +device_init(dec_register_devices) diff -Nru qemu-kvm-0.12.5+noroms/hw/dec_pci.h qemu-kvm-0.14.1/hw/dec_pci.h --- qemu-kvm-0.12.5+noroms/hw/dec_pci.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/dec_pci.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,8 @@ +#ifndef DEC_PCI_H +#define DEC_PCI_H + +#include "qemu-common.h" + +PCIBus *pci_dec_21154_init(PCIBus *parent_bus, int devfn); + +#endif diff -Nru qemu-kvm-0.12.5+noroms/hw/device-assignment.c qemu-kvm-0.14.1/hw/device-assignment.c --- qemu-kvm-0.12.5+noroms/hw/device-assignment.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/device-assignment.c 2011-05-11 13:29:46.000000000 +0000 @@ -33,11 +33,14 @@ #include "qemu-kvm.h" #include "hw.h" #include "pc.h" -#include "sysemu.h" +#include "qemu-error.h" #include "console.h" #include "device-assignment.h" #include "loader.h" -#include +#include "monitor.h" +#include "range.h" +#include +#include "sysemu.h" /* From linux/ioport.h */ #define IORESOURCE_IO 0x00000100 /* Resource type */ @@ -59,93 +62,209 @@ static void assigned_dev_load_option_rom(AssignedDevice *dev); -static uint32_t guest_to_host_ioport(AssignedDevRegion *region, uint32_t addr) -{ - return region->u.r_baseport + (addr - region->e_physbase); +static void assigned_dev_unregister_msix_mmio(AssignedDevice *dev); + +static void assigned_device_pci_cap_write_config(PCIDevice *pci_dev, + uint32_t address, + uint32_t val, int len); + +static uint32_t assigned_device_pci_cap_read_config(PCIDevice *pci_dev, + uint32_t address, int len); + +static uint32_t assigned_dev_ioport_rw(AssignedDevRegion *dev_region, + uint32_t addr, int len, uint32_t *val) +{ + uint32_t ret = 0; + uint32_t offset = addr - dev_region->e_physbase; + int fd = dev_region->region->resource_fd; + + if (fd >= 0) { + if (val) { + DEBUG("pwrite val=%x, len=%d, e_phys=%x, offset=%x\n", + *val, len, addr, offset); + if (pwrite(fd, val, len, offset) != len) { + fprintf(stderr, "%s - pwrite failed %s\n", + __func__, strerror(errno)); + } + } else { + if (pread(fd, &ret, len, offset) != len) { + fprintf(stderr, "%s - pread failed %s\n", + __func__, strerror(errno)); + ret = (1UL << (len * 8)) - 1; + } + DEBUG("pread ret=%x, len=%d, e_phys=%x, offset=%x\n", + ret, len, addr, offset); + } + } else { + uint32_t port = offset + dev_region->u.r_baseport; + + if (val) { + DEBUG("out val=%x, len=%d, e_phys=%x, host=%x\n", + *val, len, addr, port); + switch (len) { + case 1: + outb(*val, port); + break; + case 2: + outw(*val, port); + break; + case 4: + outl(*val, port); + break; + } + } else { + switch (len) { + case 1: + ret = inb(port); + break; + case 2: + ret = inw(port); + break; + case 4: + ret = inl(port); + break; + } + DEBUG("in val=%x, len=%d, e_phys=%x, host=%x\n", + ret, len, addr, port); + } + } + return ret; } static void assigned_dev_ioport_writeb(void *opaque, uint32_t addr, uint32_t value) { - AssignedDevRegion *r_access = opaque; - uint32_t r_pio = guest_to_host_ioport(r_access, addr); - - DEBUG("r_pio=%08x e_physbase=%08x r_baseport=%08lx value=%08x\n", - r_pio, (int)r_access->e_physbase, - (unsigned long)r_access->u.r_baseport, value); - - outb(value, r_pio); + assigned_dev_ioport_rw(opaque, addr, 1, &value); + return; } static void assigned_dev_ioport_writew(void *opaque, uint32_t addr, uint32_t value) { - AssignedDevRegion *r_access = opaque; - uint32_t r_pio = guest_to_host_ioport(r_access, addr); - - DEBUG("r_pio=%08x e_physbase=%08x r_baseport=%08lx value=%08x\n", - r_pio, (int)r_access->e_physbase, - (unsigned long)r_access->u.r_baseport, value); - - outw(value, r_pio); + assigned_dev_ioport_rw(opaque, addr, 2, &value); + return; } static void assigned_dev_ioport_writel(void *opaque, uint32_t addr, uint32_t value) { - AssignedDevRegion *r_access = opaque; - uint32_t r_pio = guest_to_host_ioport(r_access, addr); + assigned_dev_ioport_rw(opaque, addr, 4, &value); + return; +} + +static uint32_t assigned_dev_ioport_readb(void *opaque, uint32_t addr) +{ + return assigned_dev_ioport_rw(opaque, addr, 1, NULL); +} - DEBUG("r_pio=%08x e_physbase=%08x r_baseport=%08lx value=%08x\n", - r_pio, (int)r_access->e_physbase, - (unsigned long)r_access->u.r_baseport, value); +static uint32_t assigned_dev_ioport_readw(void *opaque, uint32_t addr) +{ + return assigned_dev_ioport_rw(opaque, addr, 2, NULL); +} - outl(value, r_pio); +static uint32_t assigned_dev_ioport_readl(void *opaque, uint32_t addr) +{ + return assigned_dev_ioport_rw(opaque, addr, 4, NULL); } -static uint32_t assigned_dev_ioport_readb(void *opaque, uint32_t addr) +static uint32_t slow_bar_readb(void *opaque, target_phys_addr_t addr) { - AssignedDevRegion *r_access = opaque; - uint32_t r_pio = guest_to_host_ioport(r_access, addr); - uint32_t value; + AssignedDevRegion *d = opaque; + uint8_t *in = d->u.r_virtbase + addr; + uint32_t r; - value = inb(r_pio); + r = *in; + DEBUG("slow_bar_readl addr=0x" TARGET_FMT_plx " val=0x%08x\n", addr, r); - DEBUG("r_pio=%08x e_physbase=%08x r_=%08lx value=%08x\n", - r_pio, (int)r_access->e_physbase, - (unsigned long)r_access->u.r_baseport, value); + return r; +} - return value; +static uint32_t slow_bar_readw(void *opaque, target_phys_addr_t addr) +{ + AssignedDevRegion *d = opaque; + uint16_t *in = d->u.r_virtbase + addr; + uint32_t r; + + r = *in; + DEBUG("slow_bar_readl addr=0x" TARGET_FMT_plx " val=0x%08x\n", addr, r); + + return r; } -static uint32_t assigned_dev_ioport_readw(void *opaque, uint32_t addr) +static uint32_t slow_bar_readl(void *opaque, target_phys_addr_t addr) { - AssignedDevRegion *r_access = opaque; - uint32_t r_pio = guest_to_host_ioport(r_access, addr); - uint32_t value; + AssignedDevRegion *d = opaque; + uint32_t *in = d->u.r_virtbase + addr; + uint32_t r; - value = inw(r_pio); + r = *in; + DEBUG("slow_bar_readl addr=0x" TARGET_FMT_plx " val=0x%08x\n", addr, r); - DEBUG("r_pio=%08x e_physbase=%08x r_baseport=%08lx value=%08x\n", - r_pio, (int)r_access->e_physbase, - (unsigned long)r_access->u.r_baseport, value); + return r; +} - return value; +static void slow_bar_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + AssignedDevRegion *d = opaque; + uint8_t *out = d->u.r_virtbase + addr; + + DEBUG("slow_bar_writeb addr=0x" TARGET_FMT_plx " val=0x%02x\n", addr, val); + *out = val; } -static uint32_t assigned_dev_ioport_readl(void *opaque, uint32_t addr) +static void slow_bar_writew(void *opaque, target_phys_addr_t addr, uint32_t val) { - AssignedDevRegion *r_access = opaque; - uint32_t r_pio = guest_to_host_ioport(r_access, addr); - uint32_t value; + AssignedDevRegion *d = opaque; + uint16_t *out = d->u.r_virtbase + addr; - value = inl(r_pio); + DEBUG("slow_bar_writew addr=0x" TARGET_FMT_plx " val=0x%04x\n", addr, val); + *out = val; +} - DEBUG("r_pio=%08x e_physbase=%08x r_baseport=%08lx value=%08x\n", - r_pio, (int)r_access->e_physbase, - (unsigned long)r_access->u.r_baseport, value); +static void slow_bar_writel(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + AssignedDevRegion *d = opaque; + uint32_t *out = d->u.r_virtbase + addr; - return value; + DEBUG("slow_bar_writel addr=0x" TARGET_FMT_plx " val=0x%08x\n", addr, val); + *out = val; +} + +static CPUWriteMemoryFunc * const slow_bar_write[] = { + &slow_bar_writeb, + &slow_bar_writew, + &slow_bar_writel +}; + +static CPUReadMemoryFunc * const slow_bar_read[] = { + &slow_bar_readb, + &slow_bar_readw, + &slow_bar_readl +}; + +static void assigned_dev_iomem_map_slow(PCIDevice *pci_dev, int region_num, + pcibus_t e_phys, pcibus_t e_size, + int type) +{ + AssignedDevice *r_dev = container_of(pci_dev, AssignedDevice, dev); + AssignedDevRegion *region = &r_dev->v_addrs[region_num]; + PCIRegion *real_region = &r_dev->real_device.regions[region_num]; + int m; + + DEBUG("%s", "slow map\n"); + m = cpu_register_io_memory(slow_bar_read, slow_bar_write, region, + DEVICE_NATIVE_ENDIAN); + cpu_register_physical_memory(e_phys, e_size, m); + + /* MSI-X MMIO page */ + if ((e_size > 0) && + real_region->base_addr <= r_dev->msix_table_addr && + real_region->base_addr + real_region->size >= r_dev->msix_table_addr) { + int offset = r_dev->msix_table_addr - real_region->base_addr; + + cpu_register_physical_memory(e_phys + offset, + TARGET_PAGE_SIZE, r_dev->mmio_index); + } } static void assigned_dev_iomem_map(PCIDevice *pci_dev, int region_num, @@ -154,41 +273,26 @@ AssignedDevice *r_dev = container_of(pci_dev, AssignedDevice, dev); AssignedDevRegion *region = &r_dev->v_addrs[region_num]; PCIRegion *real_region = &r_dev->real_device.regions[region_num]; - pcibus_t old_ephys = region->e_physbase; - pcibus_t old_esize = region->e_size; - int first_map = (region->e_size == 0); int ret = 0; - DEBUG("e_phys=%08x r_virt=%p type=%d len=%08x region_num=%d \n", + DEBUG("e_phys=%08" FMT_PCIBUS " r_virt=%p type=%d len=%08" FMT_PCIBUS " region_num=%d \n", e_phys, region->u.r_virtbase, type, e_size, region_num); region->e_physbase = e_phys; region->e_size = e_size; - if (!first_map) - kvm_destroy_phys_mem(kvm_context, old_ephys, - TARGET_PAGE_ALIGN(old_esize)); - if (e_size > 0) { + cpu_register_physical_memory(e_phys, e_size, region->memory_index); + /* deal with MSI-X MMIO page */ if (real_region->base_addr <= r_dev->msix_table_addr && real_region->base_addr + real_region->size >= r_dev->msix_table_addr) { int offset = r_dev->msix_table_addr - real_region->base_addr; - ret = munmap(region->u.r_virtbase + offset, TARGET_PAGE_SIZE); - if (ret == 0) - DEBUG("munmap done, virt_base 0x%p\n", - region->u.r_virtbase + offset); - else { - fprintf(stderr, "%s: fail munmap msix table!\n", __func__); - exit(1); - } + cpu_register_physical_memory(e_phys + offset, TARGET_PAGE_SIZE, r_dev->mmio_index); } - ret = kvm_register_phys_mem(kvm_context, e_phys, - region->u.r_virtbase, - TARGET_PAGE_ALIGN(e_size), 0); } if (ret != 0) { @@ -208,10 +312,10 @@ region->e_physbase = addr; region->e_size = size; - DEBUG("e_phys=0x%x r_baseport=%x type=0x%x len=%d region_num=%d \n", + DEBUG("e_phys=0x%" FMT_PCIBUS " r_baseport=%x type=0x%x len=%" FMT_PCIBUS " region_num=%d \n", addr, region->u.r_baseport, type, size, region_num); - if (first_map) { + if (first_map && region->region->resource_fd < 0) { struct ioperm_data *data; data = qemu_mallocz(sizeof(struct ioperm_data)); @@ -244,24 +348,72 @@ (r_dev->v_addrs + region_num)); } -static uint8_t pci_find_cap_offset(struct pci_dev *pci_dev, uint8_t cap) +static uint32_t assigned_dev_pci_read(PCIDevice *d, int pos, int len) +{ + AssignedDevice *pci_dev = container_of(d, AssignedDevice, dev); + uint32_t val; + ssize_t ret; + int fd = pci_dev->real_device.config_fd; + +again: + ret = pread(fd, &val, len, pos); + if (ret != len) { + if ((ret < 0) && (errno == EINTR || errno == EAGAIN)) + goto again; + + fprintf(stderr, "%s: pread failed, ret = %zd errno = %d\n", + __func__, ret, errno); + + exit(1); + } + + return val; +} + +static uint8_t assigned_dev_pci_read_byte(PCIDevice *d, int pos) +{ + return (uint8_t)assigned_dev_pci_read(d, pos, 1); +} + +static void assigned_dev_pci_write(PCIDevice *d, int pos, uint32_t val, int len) +{ + AssignedDevice *pci_dev = container_of(d, AssignedDevice, dev); + ssize_t ret; + int fd = pci_dev->real_device.config_fd; + +again: + ret = pwrite(fd, &val, len, pos); + if (ret != len) { + if ((ret < 0) && (errno == EINTR || errno == EAGAIN)) + goto again; + + fprintf(stderr, "%s: pwrite failed, ret = %zd errno = %d\n", + __func__, ret, errno); + + exit(1); + } + + return; +} + +static uint8_t pci_find_cap_offset(PCIDevice *d, uint8_t cap, uint8_t start) { int id; int max_cap = 48; - int pos = PCI_CAPABILITY_LIST; + int pos = start ? start : PCI_CAPABILITY_LIST; int status; - status = pci_read_byte(pci_dev, PCI_STATUS); + status = assigned_dev_pci_read_byte(d, PCI_STATUS); if ((status & PCI_STATUS_CAP_LIST) == 0) return 0; while (max_cap--) { - pos = pci_read_byte(pci_dev, pos); + pos = assigned_dev_pci_read_byte(d, pos); if (pos < 0x40) break; pos &= ~3; - id = pci_read_byte(pci_dev, pos + PCI_CAP_LIST_ID); + id = assigned_dev_pci_read_byte(d, pos + PCI_CAP_LIST_ID); if (id == 0xff) break; @@ -284,14 +436,17 @@ ((d->devfn >> 3) & 0x1F), (d->devfn & 0x7), (uint16_t) address, val, len); + if (address >= PCI_CONFIG_HEADER_SIZE && d->config_map[address]) { + return assigned_device_pci_cap_write_config(d, address, val, len); + } + if (address == 0x4) { pci_default_write_config(d, address, val, len); /* Continue to program the card */ } if ((address >= 0x10 && address <= 0x24) || address == 0x30 || - address == 0x34 || address == 0x3c || address == 0x3d || - pci_access_cap_config(d, address, len)) { + address == 0x34 || address == 0x3c || address == 0x3d) { /* used for update-mappings (BAR emulation) */ pci_default_write_config(d, address, val, len); return; @@ -324,10 +479,16 @@ ssize_t ret; AssignedDevice *pci_dev = container_of(d, AssignedDevice, dev); + if (address >= PCI_CONFIG_HEADER_SIZE && d->config_map[address]) { + val = assigned_device_pci_cap_read_config(d, address, len); + DEBUG("(%x.%x): address=%04x val=0x%08x len=%d\n", + (d->devfn >> 3) & 0x1F, (d->devfn & 0x7), address, val, len); + return val; + } + if (address < 0x4 || (pci_dev->need_emulate_cmd && address == 0x4) || (address >= 0x10 && address <= 0x24) || address == 0x30 || - address == 0x34 || address == 0x3c || address == 0x3d || - pci_access_cap_config(d, address, len)) { + address == 0x34 || address == 0x3c || address == 0x3d) { val = pci_default_read_config(d, address, len); DEBUG("(%x.%x): address=%04x val=0x%08x len=%d\n", (d->devfn >> 3) & 0x1F, (d->devfn & 0x7), address, val, len); @@ -381,34 +542,28 @@ /* handle memory io regions */ if (cur_region->type & IORESOURCE_MEM) { + int slow_map = 0; int t = cur_region->type & IORESOURCE_PREFETCH ? PCI_BASE_ADDRESS_MEM_PREFETCH : PCI_BASE_ADDRESS_SPACE_MEMORY; + if (cur_region->size & 0xFFF) { - fprintf(stderr, "Unable to assign device: PCI region %d " - "at address 0x%llx has size 0x%x, " - " which is not a multiple of 4K\n", + fprintf(stderr, "PCI region %d at address 0x%llx " + "has size 0x%x, which is not a multiple of 4K. " + "You might experience some performance hit " + "due to that.\n", i, (unsigned long long)cur_region->base_addr, cur_region->size); - return -1; + slow_map = 1; } /* map physical memory */ pci_dev->v_addrs[i].e_physbase = cur_region->base_addr; - if (i == PCI_ROM_SLOT) { - pci_dev->v_addrs[i].u.r_virtbase = - mmap(NULL, - (cur_region->size + 0xFFF) & 0xFFFFF000, - PROT_WRITE | PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, - 0, (off_t) 0); - - } else { - pci_dev->v_addrs[i].u.r_virtbase = - mmap(NULL, - (cur_region->size + 0xFFF) & 0xFFFFF000, - PROT_WRITE | PROT_READ, MAP_SHARED, - cur_region->resource_fd, (off_t) 0); - } + pci_dev->v_addrs[i].u.r_virtbase = mmap(NULL, cur_region->size, + PROT_WRITE | PROT_READ, + MAP_SHARED, + cur_region->resource_fd, + (off_t)0); if (pci_dev->v_addrs[i].u.r_virtbase == MAP_FAILED) { pci_dev->v_addrs[i].u.r_virtbase = NULL; @@ -418,13 +573,6 @@ return -1; } - if (i == PCI_ROM_SLOT) { - memset(pci_dev->v_addrs[i].u.r_virtbase, 0, - (cur_region->size + 0xFFF) & 0xFFFFF000); - mprotect(pci_dev->v_addrs[PCI_ROM_SLOT].u.r_virtbase, - (cur_region->size + 0xFFF) & 0xFFFFF000, PROT_READ); - } - pci_dev->v_addrs[i].r_size = cur_region->size; pci_dev->v_addrs[i].e_size = 0; @@ -432,62 +580,147 @@ pci_dev->v_addrs[i].u.r_virtbase += (cur_region->base_addr & 0xFFF); + + if (!slow_map) { + void *virtbase = pci_dev->v_addrs[i].u.r_virtbase; + char name[32]; + snprintf(name, sizeof(name), "%s.bar%d", + pci_dev->dev.qdev.info->name, i); + pci_dev->v_addrs[i].memory_index = + qemu_ram_alloc_from_ptr( + &pci_dev->dev.qdev, + name, cur_region->size, + virtbase); + } else + pci_dev->v_addrs[i].memory_index = 0; + pci_register_bar((PCIDevice *) pci_dev, i, cur_region->size, t, - assigned_dev_iomem_map); + slow_map ? assigned_dev_iomem_map_slow + : assigned_dev_iomem_map); continue; - } - /* handle port io regions */ - pci_dev->v_addrs[i].e_physbase = cur_region->base_addr; - pci_dev->v_addrs[i].u.r_baseport = cur_region->base_addr; - pci_dev->v_addrs[i].r_size = cur_region->size; - pci_dev->v_addrs[i].e_size = 0; - - pci_register_bar((PCIDevice *) pci_dev, i, - cur_region->size, PCI_BASE_ADDRESS_SPACE_IO, - assigned_dev_ioport_map); + } else { + /* handle port io regions */ + uint32_t val; + int ret; + + /* Test kernel support for ioport resource read/write. Old + * kernels return EIO. New kernels only allow 1/2/4 byte reads + * so should return EINVAL for a 3 byte read */ + ret = pread(pci_dev->v_addrs[i].region->resource_fd, &val, 3, 0); + if (ret == 3) { + fprintf(stderr, "I/O port resource supports 3 byte read?!\n"); + abort(); + } else if (errno != EINVAL) { + fprintf(stderr, "Using raw in/out ioport access (sysfs - %s)\n", + strerror(errno)); + close(pci_dev->v_addrs[i].region->resource_fd); + pci_dev->v_addrs[i].region->resource_fd = -1; + } + + pci_dev->v_addrs[i].e_physbase = cur_region->base_addr; + pci_dev->v_addrs[i].u.r_baseport = cur_region->base_addr; + pci_dev->v_addrs[i].r_size = cur_region->size; + pci_dev->v_addrs[i].e_size = 0; - /* not relevant for port io */ - pci_dev->v_addrs[i].memory_index = 0; + pci_register_bar((PCIDevice *) pci_dev, i, + cur_region->size, PCI_BASE_ADDRESS_SPACE_IO, + assigned_dev_ioport_map); + + /* not relevant for port io */ + pci_dev->v_addrs[i].memory_index = 0; + } } /* success */ return 0; } -static int get_real_device(AssignedDevice *pci_dev, uint8_t r_bus, - uint8_t r_dev, uint8_t r_func) +static int get_real_id(const char *devpath, const char *idname, uint16_t *val) +{ + FILE *f; + char name[128]; + long id; + + snprintf(name, sizeof(name), "%s%s", devpath, idname); + f = fopen(name, "r"); + if (f == NULL) { + fprintf(stderr, "%s: %s: %m\n", __func__, name); + return -1; + } + if (fscanf(f, "%li\n", &id) == 1) { + *val = id; + } else { + return -1; + } + fclose(f); + + return 0; +} + +static int get_real_vendor_id(const char *devpath, uint16_t *val) +{ + return get_real_id(devpath, "vendor", val); +} + +static int get_real_device_id(const char *devpath, uint16_t *val) +{ + return get_real_id(devpath, "device", val); +} + +static int get_real_device(AssignedDevice *pci_dev, uint16_t r_seg, + uint8_t r_bus, uint8_t r_dev, uint8_t r_func) { char dir[128], name[128]; - int fd, r = 0; + int fd, r = 0, v; FILE *f; unsigned long long start, end, size, flags; - unsigned long id; + uint16_t id; struct stat statbuf; PCIRegion *rp; PCIDevRegions *dev = &pci_dev->real_device; dev->region_number = 0; - snprintf(dir, sizeof(dir), "/sys/bus/pci/devices/0000:%02x:%02x.%x/", - r_bus, r_dev, r_func); + snprintf(dir, sizeof(dir), "/sys/bus/pci/devices/%04x:%02x:%02x.%x/", + r_seg, r_bus, r_dev, r_func); snprintf(name, sizeof(name), "%sconfig", dir); - fd = open(name, O_RDWR); - if (fd == -1) { - fprintf(stderr, "%s: %s: %m\n", __func__, name); - return 1; + if (pci_dev->configfd_name && *pci_dev->configfd_name) { + if (qemu_isdigit(pci_dev->configfd_name[0])) { + dev->config_fd = strtol(pci_dev->configfd_name, NULL, 0); + } else { + dev->config_fd = monitor_get_fd(cur_mon, pci_dev->configfd_name); + if (dev->config_fd < 0) { + fprintf(stderr, "%s: (%s) unkown\n", __func__, + pci_dev->configfd_name); + return 1; + } + } + } else { + dev->config_fd = open(name, O_RDWR); + + if (dev->config_fd == -1) { + fprintf(stderr, "%s: %s: %m\n", __func__, name); + return 1; + } } - dev->config_fd = fd; again: - r = read(fd, pci_dev->dev.config, pci_config_size(&pci_dev->dev)); + r = read(dev->config_fd, pci_dev->dev.config, + pci_config_size(&pci_dev->dev)); if (r < 0) { if (errno == EINTR || errno == EAGAIN) goto again; fprintf(stderr, "%s: read failed, errno = %d\n", __func__, errno); } + /* Clear host resource mapping info. If we choose not to register a + * BAR, such as might be the case with the option ROM, we can get + * confusing, unwritable, residual addresses from the host here. */ + memset(&pci_dev->dev.config[PCI_BASE_ADDRESS_0], 0, 24); + memset(&pci_dev->dev.config[PCI_ROM_ADDRESS], 0, 4); + snprintf(name, sizeof(name), "%sresource", dir); f = fopen(name, "r"); @@ -496,63 +729,54 @@ return 1; } - for (r = 0; r < PCI_NUM_REGIONS; r++) { + for (r = 0; r < PCI_ROM_SLOT; r++) { if (fscanf(f, "%lli %lli %lli\n", &start, &end, &flags) != 3) break; rp = dev->regions + r; rp->valid = 0; + rp->resource_fd = -1; size = end - start + 1; flags &= IORESOURCE_IO | IORESOURCE_MEM | IORESOURCE_PREFETCH; if (size == 0 || (flags & ~IORESOURCE_PREFETCH) == 0) continue; if (flags & IORESOURCE_MEM) { flags &= ~IORESOURCE_IO; - if (r != PCI_ROM_SLOT) { - snprintf(name, sizeof(name), "%sresource%d", dir, r); - fd = open(name, O_RDWR); - if (fd == -1) - continue; - rp->resource_fd = fd; - } - } else + } else { flags &= ~IORESOURCE_PREFETCH; + } + snprintf(name, sizeof(name), "%sresource%d", dir, r); + fd = open(name, O_RDWR); + if (fd == -1) + continue; + rp->resource_fd = fd; rp->type = flags; rp->valid = 1; rp->base_addr = start; rp->size = size; + pci_dev->v_addrs[r].region = rp; DEBUG("region %d size %d start 0x%llx type %d resource_fd %d\n", r, rp->size, start, rp->type, rp->resource_fd); } fclose(f); - /* read and fill device ID */ - snprintf(name, sizeof(name), "%svendor", dir); - f = fopen(name, "r"); - if (f == NULL) { - fprintf(stderr, "%s: %s: %m\n", __func__, name); + /* read and fill vendor ID */ + v = get_real_vendor_id(dir, &id); + if (v) { return 1; } - if (fscanf(f, "%li\n", &id) == 1) { - pci_dev->dev.config[0] = id & 0xff; - pci_dev->dev.config[1] = (id & 0xff00) >> 8; - } - fclose(f); + pci_dev->dev.config[0] = id & 0xff; + pci_dev->dev.config[1] = (id & 0xff00) >> 8; - /* read and fill vendor ID */ - snprintf(name, sizeof(name), "%sdevice", dir); - f = fopen(name, "r"); - if (f == NULL) { - fprintf(stderr, "%s: %s: %m\n", __func__, name); + /* read and fill device ID */ + v = get_real_device_id(dir, &id); + if (v) { return 1; } - if (fscanf(f, "%li\n", &id) == 1) { - pci_dev->dev.config[2] = id & 0xff; - pci_dev->dev.config[3] = (id & 0xff00) >> 8; - } - fclose(f); + pci_dev->dev.config[2] = id & 0xff; + pci_dev->dev.config[3] = (id & 0xff00) >> 8; /* dealing with virtual function device */ snprintf(name, sizeof(name), "%sphysfn/", dir); @@ -573,7 +797,7 @@ int i; for (i = 0; i < dev->irq_entries_nr; i++) - kvm_del_routing_entry(kvm_context, &dev->entry[i]); + kvm_del_routing_entry(&dev->entry[i]); free(dev->entry); dev->entry = NULL; dev->irq_entries_nr = 0; @@ -593,27 +817,35 @@ continue; if (pci_region->type & IORESOURCE_IO) { - kvm_remove_ioperm_data(region->u.r_baseport, region->r_size); - continue; + if (pci_region->resource_fd < 0) { + kvm_remove_ioperm_data(region->u.r_baseport, + region->r_size); + } } else if (pci_region->type & IORESOURCE_MEM) { - if (region->e_size > 0) - kvm_destroy_phys_mem(kvm_context, region->e_physbase, - TARGET_PAGE_ALIGN(region->e_size)); - if (region->u.r_virtbase) { - int ret = munmap(region->u.r_virtbase, - (pci_region->size + 0xFFF) & 0xFFFFF000); - if (ret != 0) + if (region->memory_index) { + cpu_register_physical_memory(region->e_physbase, + region->e_size, + IO_MEM_UNASSIGNED); + qemu_ram_unmap(region->memory_index); + } + if (munmap(region->u.r_virtbase, + (pci_region->size + 0xFFF) & 0xFFFFF000)) fprintf(stderr, "Failed to unmap assigned device region: %s\n", strerror(errno)); } - } + } + if (pci_region->resource_fd >= 0) { + close(pci_region->resource_fd); + } } - if (dev->real_device.config_fd) { + if (dev->cap.available & ASSIGNED_DEVICE_CAP_MSIX) + assigned_dev_unregister_msix_mmio(dev); + + if (dev->real_device.config_fd >= 0) { close(dev->real_device.config_fd); - dev->real_device.config_fd = 0; } #ifdef KVM_CAP_IRQ_ROUTING @@ -622,9 +854,56 @@ } } -static uint32_t calc_assigned_dev_id(uint8_t bus, uint8_t devfn) +static uint32_t calc_assigned_dev_id(uint16_t seg, uint8_t bus, uint8_t devfn) { - return (uint32_t)bus << 8 | (uint32_t)devfn; + return (uint32_t)seg << 16 | (uint32_t)bus << 8 | (uint32_t)devfn; +} + +static void assign_failed_examine(AssignedDevice *dev) +{ + char name[PATH_MAX], dir[PATH_MAX], driver[PATH_MAX] = {}, *ns; + uint16_t vendor_id, device_id; + int r; + + sprintf(dir, "/sys/bus/pci/devices/%04x:%02x:%02x.%01x/", + dev->host.seg, dev->host.bus, dev->host.dev, dev->host.func); + + sprintf(name, "%sdriver", dir); + + r = readlink(name, driver, sizeof(driver)); + if ((r <= 0) || r >= sizeof(driver) || !(ns = strrchr(driver, '/'))) { + goto fail; + } + + ns++; + + if (get_real_vendor_id(dir, &vendor_id) || + get_real_device_id(dir, &device_id)) { + goto fail; + } + + fprintf(stderr, "*** The driver '%s' is occupying your device " + "%04x:%02x:%02x.%x.\n", + ns, dev->host.seg, dev->host.bus, dev->host.dev, dev->host.func); + fprintf(stderr, "***\n"); + fprintf(stderr, "*** You can try the following commands to free it:\n"); + fprintf(stderr, "***\n"); + fprintf(stderr, "*** $ echo \"%04x %04x\" > /sys/bus/pci/drivers/pci-stub/" + "new_id\n", vendor_id, device_id); + fprintf(stderr, "*** $ echo \"%04x:%02x:%02x.%x\" > /sys/bus/pci/drivers/" + "%s/unbind\n", + dev->host.seg, dev->host.bus, dev->host.dev, dev->host.func, ns); + fprintf(stderr, "*** $ echo \"%04x:%02x:%02x.%x\" > /sys/bus/pci/drivers/" + "pci-stub/bind\n", + dev->host.seg, dev->host.bus, dev->host.dev, dev->host.func); + fprintf(stderr, "*** $ echo \"%04x %04x\" > /sys/bus/pci/drivers/pci-stub" + "/remove_id\n", vendor_id, device_id); + fprintf(stderr, "***\n"); + + return; + +fail: + fprintf(stderr, "Couldn't find out why.\n"); } static int assign_device(AssignedDevice *dev) @@ -632,15 +911,28 @@ struct kvm_assigned_pci_dev assigned_dev_data; int r; +#ifdef KVM_CAP_PCI_SEGMENT + /* Only pass non-zero PCI segment to capable module */ + if (!kvm_check_extension(kvm_state, KVM_CAP_PCI_SEGMENT) && + dev->h_segnr) { + fprintf(stderr, "Can't assign device inside non-zero PCI segment " + "as this KVM module doesn't support it.\n"); + return -ENODEV; + } +#endif + memset(&assigned_dev_data, 0, sizeof(assigned_dev_data)); assigned_dev_data.assigned_dev_id = - calc_assigned_dev_id(dev->h_busnr, dev->h_devfn); + calc_assigned_dev_id(dev->h_segnr, dev->h_busnr, dev->h_devfn); +#ifdef KVM_CAP_PCI_SEGMENT + assigned_dev_data.segnr = dev->h_segnr; +#endif assigned_dev_data.busnr = dev->h_busnr; assigned_dev_data.devfn = dev->h_devfn; #ifdef KVM_CAP_IOMMU /* We always enable the IOMMU unless disabled on the command line */ - if (dev->use_iommu) { + if (dev->features & ASSIGNED_DEVICE_USE_IOMMU_MASK) { if (!kvm_check_extension(kvm_state, KVM_CAP_IOMMU)) { fprintf(stderr, "No IOMMU found. Unable to assign device \"%s\"\n", dev->dev.qdev.id); @@ -649,13 +941,28 @@ assigned_dev_data.flags |= KVM_DEV_ASSIGN_ENABLE_IOMMU; } #else - dev->use_iommu = 0; + dev->features &= ~ASSIGNED_DEVICE_USE_IOMMU_MASK; #endif + if (!(dev->features & ASSIGNED_DEVICE_USE_IOMMU_MASK)) { + fprintf(stderr, + "WARNING: Assigning a device without IOMMU protection can " + "cause host memory corruption if the device issues DMA write " + "requests!\n"); + } r = kvm_assign_pci_device(kvm_context, &assigned_dev_data); - if (r < 0) - fprintf(stderr, "Failed to assign device \"%s\" : %s\n", + if (r < 0) { + fprintf(stderr, "Failed to assign device \"%s\" : %s\n", dev->dev.qdev.id, strerror(-r)); + + switch (r) { + case -EBUSY: + assign_failed_examine(dev); + break; + default: + break; + } + } return r; } @@ -665,7 +972,7 @@ int irq, r = 0; /* Interrupt PIN 0 means don't use INTx */ - if (pci_read_byte(dev->pdev, PCI_INTERRUPT_PIN) == 0) + if (assigned_dev_pci_read_byte(&dev->dev, PCI_INTERRUPT_PIN) == 0) return 0; irq = pci_map_irq(&dev->dev, dev->intpin); @@ -680,7 +987,7 @@ memset(&assigned_irq_data, 0, sizeof(assigned_irq_data)); assigned_irq_data.assigned_dev_id = - calc_assigned_dev_id(dev->h_busnr, dev->h_devfn); + calc_assigned_dev_id(dev->h_segnr, dev->h_busnr, dev->h_devfn); assigned_irq_data.guest_irq = irq; assigned_irq_data.host_irq = dev->real_device.irq; #ifdef KVM_CAP_ASSIGN_DEV_IRQ @@ -693,7 +1000,8 @@ } assigned_irq_data.flags = KVM_DEV_IRQ_GUEST_INTX; - if (dev->cap.available & ASSIGNED_DEVICE_CAP_MSI) + if (dev->features & ASSIGNED_DEVICE_PREFER_MSI_MASK && + dev->cap.available & ASSIGNED_DEVICE_CAP_MSI) assigned_irq_data.flags |= KVM_DEV_IRQ_HOST_MSI; else assigned_irq_data.flags |= KVM_DEV_IRQ_HOST_INTX; @@ -721,7 +1029,7 @@ memset(&assigned_dev_data, 0, sizeof(assigned_dev_data)); assigned_dev_data.assigned_dev_id = - calc_assigned_dev_id(dev->h_busnr, dev->h_devfn); + calc_assigned_dev_id(dev->h_segnr, dev->h_busnr, dev->h_devfn); r = kvm_deassign_pci_device(kvm_context, &assigned_dev_data); if (r < 0) @@ -777,40 +1085,47 @@ memset(&assigned_irq_data, 0, sizeof assigned_irq_data); assigned_irq_data.assigned_dev_id = - calc_assigned_dev_id(assigned_dev->h_busnr, + calc_assigned_dev_id(assigned_dev->h_segnr, assigned_dev->h_busnr, (uint8_t)assigned_dev->h_devfn); - if (assigned_dev->irq_requested_type) { - assigned_irq_data.flags = assigned_dev->irq_requested_type; - free_dev_irq_entries(assigned_dev); - r = kvm_deassign_irq(kvm_context, &assigned_irq_data); - /* -ENXIO means no assigned irq */ - if (r && r != -ENXIO) - perror("assigned_dev_update_msi: deassign irq"); + /* Some guests gratuitously disable MSI even if they're not using it, + * try to catch this by only deassigning irqs if the guest is using + * MSI or intends to start. */ + if ((assigned_dev->irq_requested_type & KVM_DEV_IRQ_GUEST_MSI) || + (ctrl_byte & PCI_MSI_FLAGS_ENABLE)) { + + assigned_irq_data.flags = assigned_dev->irq_requested_type; + free_dev_irq_entries(assigned_dev); + r = kvm_deassign_irq(kvm_context, &assigned_irq_data); + /* -ENXIO means no assigned irq */ + if (r && r != -ENXIO) + perror("assigned_dev_update_msi: deassign irq"); + + assigned_dev->irq_requested_type = 0; } if (ctrl_byte & PCI_MSI_FLAGS_ENABLE) { + int pos = ctrl_pos - PCI_MSI_FLAGS; assigned_dev->entry = calloc(1, sizeof(struct kvm_irq_routing_entry)); if (!assigned_dev->entry) { perror("assigned_dev_update_msi: "); return; } assigned_dev->entry->u.msi.address_lo = - *(uint32_t *)(pci_dev->config + pci_dev->cap.start + - PCI_MSI_ADDRESS_LO); + pci_get_long(pci_dev->config + pos + PCI_MSI_ADDRESS_LO); assigned_dev->entry->u.msi.address_hi = 0; - assigned_dev->entry->u.msi.data = *(uint16_t *)(pci_dev->config + - pci_dev->cap.start + PCI_MSI_DATA_32); + assigned_dev->entry->u.msi.data = + pci_get_word(pci_dev->config + pos + PCI_MSI_DATA_32); assigned_dev->entry->type = KVM_IRQ_ROUTING_MSI; - r = kvm_get_irq_route_gsi(kvm_context); + r = kvm_get_irq_route_gsi(); if (r < 0) { perror("assigned_dev_update_msi: kvm_get_irq_route_gsi"); return; } assigned_dev->entry->gsi = r; - kvm_add_routing_entry(kvm_context, assigned_dev->entry); - if (kvm_commit_irq_routes(kvm_context) < 0) { + kvm_add_routing_entry(assigned_dev->entry); + if (kvm_commit_irq_routes() < 0) { perror("assigned_dev_update_msi: kvm_commit_irq_routes"); assigned_dev->cap.state &= ~ASSIGNED_DEVICE_MSI_ENABLED; return; @@ -822,7 +1137,10 @@ if (kvm_assign_irq(kvm_context, &assigned_irq_data) < 0) perror("assigned_dev_enable_msi: assign irq"); + assigned_dev->girq = -1; assigned_dev->irq_requested_type = assigned_irq_data.flags; + } else { + assign_irq(assigned_dev); } } #endif @@ -831,19 +1149,16 @@ static int assigned_dev_update_msix_mmio(PCIDevice *pci_dev) { AssignedDevice *adev = container_of(pci_dev, AssignedDevice, dev); - u16 entries_nr = 0, entries_max_nr; + uint16_t entries_nr = 0, entries_max_nr; int pos = 0, i, r = 0; - u32 msg_addr, msg_upper_addr, msg_data, msg_ctrl; + uint32_t msg_addr, msg_upper_addr, msg_data, msg_ctrl; struct kvm_assigned_msix_nr msix_nr; struct kvm_assigned_msix_entry msix_entry; void *va = adev->msix_table_page; - if (adev->cap.available & ASSIGNED_DEVICE_CAP_MSI) - pos = pci_dev->cap.start + PCI_CAPABILITY_CONFIG_MSI_LENGTH; - else - pos = pci_dev->cap.start; + pos = pci_find_capability(pci_dev, PCI_CAP_ID_MSIX); - entries_max_nr = pci_dev->config[pos + 2]; + entries_max_nr = *(uint16_t *)(pci_dev->config + pos + 2); entries_max_nr &= PCI_MSIX_TABSIZE; entries_max_nr += 1; @@ -861,7 +1176,7 @@ fprintf(stderr, "MSI-X entry number is zero!\n"); return -EINVAL; } - msix_nr.assigned_dev_id = calc_assigned_dev_id(adev->h_busnr, + msix_nr.assigned_dev_id = calc_assigned_dev_id(adev->h_segnr, adev->h_busnr, (uint8_t)adev->h_devfn); msix_nr.entry_nr = entries_nr; r = kvm_assign_set_msix_nr(kvm_context, &msix_nr); @@ -892,7 +1207,7 @@ memcpy(&msg_addr, va + i * 16, 4); memcpy(&msg_upper_addr, va + i * 16 + 4, 4); - r = kvm_get_irq_route_gsi(kvm_context); + r = kvm_get_irq_route_gsi(); if (r < 0) return r; @@ -903,7 +1218,7 @@ adev->entry[entries_nr].u.msi.address_hi = msg_upper_addr; adev->entry[entries_nr].u.msi.data = msg_data; DEBUG("MSI-X data 0x%x, MSI-X addr_lo 0x%x\n!", msg_data, msg_addr); - kvm_add_routing_entry(kvm_context, &adev->entry[entries_nr]); + kvm_add_routing_entry(&adev->entry[entries_nr]); msix_entry.gsi = adev->entry[entries_nr].gsi; msix_entry.entry = i; @@ -917,7 +1232,7 @@ entries_nr ++; } - if (r == 0 && kvm_commit_irq_routes(kvm_context) < 0) { + if (r == 0 && kvm_commit_irq_routes() < 0) { perror("assigned_dev_update_msix_mmio: kvm_commit_irq_routes"); return -EINVAL; } @@ -934,20 +1249,29 @@ memset(&assigned_irq_data, 0, sizeof assigned_irq_data); assigned_irq_data.assigned_dev_id = - calc_assigned_dev_id(assigned_dev->h_busnr, + calc_assigned_dev_id(assigned_dev->h_segnr, assigned_dev->h_busnr, (uint8_t)assigned_dev->h_devfn); - if (assigned_dev->irq_requested_type) { + /* Some guests gratuitously disable MSIX even if they're not using it, + * try to catch this by only deassigning irqs if the guest is using + * MSIX or intends to start. */ + if ((assigned_dev->irq_requested_type & KVM_DEV_IRQ_GUEST_MSIX) || + (*ctrl_word & PCI_MSIX_ENABLE)) { + assigned_irq_data.flags = assigned_dev->irq_requested_type; free_dev_irq_entries(assigned_dev); r = kvm_deassign_irq(kvm_context, &assigned_irq_data); /* -ENXIO means no assigned irq */ if (r && r != -ENXIO) perror("assigned_dev_update_msix: deassign irq"); + + assigned_dev->irq_requested_type = 0; } - assigned_irq_data.flags = KVM_DEV_IRQ_HOST_MSIX | KVM_DEV_IRQ_GUEST_MSIX; if (*ctrl_word & PCI_MSIX_ENABLE) { + assigned_irq_data.flags = KVM_DEV_IRQ_HOST_MSIX | + KVM_DEV_IRQ_GUEST_MSIX; + if (assigned_dev_update_msix_mmio(pci_dev) < 0) { perror("assigned_dev_update_msix_mmio"); return; @@ -956,96 +1280,330 @@ perror("assigned_dev_enable_msix: assign irq"); return; } + assigned_dev->girq = -1; assigned_dev->irq_requested_type = assigned_irq_data.flags; + } else { + assign_irq(assigned_dev); } } #endif #endif -static void assigned_device_pci_cap_write_config(PCIDevice *pci_dev, uint32_t address, +/* There can be multiple VNDR capabilities per device, we need to find the + * one that starts closet to the given address without going over. */ +static uint8_t find_vndr_start(PCIDevice *pci_dev, uint32_t address) +{ + uint8_t cap, pos; + + for (cap = pos = 0; + (pos = pci_find_cap_offset(pci_dev, PCI_CAP_ID_VNDR, pos)); + pos += PCI_CAP_LIST_NEXT) { + if (pos <= address) { + cap = MAX(pos, cap); + } + } + return cap; +} + +/* Merge the bits set in mask from mval into val. Both val and mval are + * at the same addr offset, pos is the starting offset of the mask. */ +static uint32_t merge_bits(uint32_t val, uint32_t mval, uint8_t addr, + int len, uint8_t pos, uint32_t mask) +{ + if (!ranges_overlap(addr, len, pos, 4)) { + return val; + } + + if (addr >= pos) { + mask >>= (addr - pos) * 8; + } else { + mask <<= (pos - addr) * 8; + } + mask &= 0xffffffffU >> (4 - len) * 8; + + val &= ~mask; + val |= (mval & mask); + + return val; +} + +static uint32_t assigned_device_pci_cap_read_config(PCIDevice *pci_dev, + uint32_t address, int len) +{ + uint8_t cap, cap_id = pci_dev->config_map[address]; + uint32_t val; + + switch (cap_id) { + + case PCI_CAP_ID_VPD: + cap = pci_find_capability(pci_dev, cap_id); + val = assigned_dev_pci_read(pci_dev, address, len); + return merge_bits(val, pci_get_long(pci_dev->config + address), + address, len, cap + PCI_CAP_LIST_NEXT, 0xff); + + case PCI_CAP_ID_VNDR: + cap = find_vndr_start(pci_dev, address); + val = assigned_dev_pci_read(pci_dev, address, len); + return merge_bits(val, pci_get_long(pci_dev->config + address), + address, len, cap + PCI_CAP_LIST_NEXT, 0xff); + } + + return pci_default_read_config(pci_dev, address, len); +} + +static void assigned_device_pci_cap_write_config(PCIDevice *pci_dev, + uint32_t address, uint32_t val, int len) { - AssignedDevice *assigned_dev = container_of(pci_dev, AssignedDevice, dev); - unsigned int pos = pci_dev->cap.start, ctrl_pos; + uint8_t cap_id = pci_dev->config_map[address]; - pci_default_cap_write_config(pci_dev, address, val, len); + pci_default_write_config(pci_dev, address, val, len); + switch (cap_id) { #ifdef KVM_CAP_IRQ_ROUTING + case PCI_CAP_ID_MSI: #ifdef KVM_CAP_DEVICE_MSI - if (assigned_dev->cap.available & ASSIGNED_DEVICE_CAP_MSI) { - ctrl_pos = pos + PCI_MSI_FLAGS; - if (address <= ctrl_pos && address + len > ctrl_pos) - assigned_dev_update_msi(pci_dev, ctrl_pos); - pos += PCI_CAPABILITY_CONFIG_MSI_LENGTH; - } + { + uint8_t cap = pci_find_capability(pci_dev, cap_id); + if (ranges_overlap(address - cap, len, PCI_MSI_FLAGS, 1)) { + assigned_dev_update_msi(pci_dev, cap + PCI_MSI_FLAGS); + } + } #endif + break; + + case PCI_CAP_ID_MSIX: #ifdef KVM_CAP_DEVICE_MSIX - if (assigned_dev->cap.available & ASSIGNED_DEVICE_CAP_MSIX) { - ctrl_pos = pos + 3; - if (address <= ctrl_pos && address + len > ctrl_pos) { - ctrl_pos--; /* control is word long */ - assigned_dev_update_msix(pci_dev, ctrl_pos); - } - pos += PCI_CAPABILITY_CONFIG_MSIX_LENGTH; - } + { + uint8_t cap = pci_find_capability(pci_dev, cap_id); + if (ranges_overlap(address - cap, len, PCI_MSIX_FLAGS + 1, 1)) { + assigned_dev_update_msix(pci_dev, cap + PCI_MSIX_FLAGS); + } + } #endif + break; #endif - return; + + case PCI_CAP_ID_VPD: + case PCI_CAP_ID_VNDR: + assigned_dev_pci_write(pci_dev, address, val, len); + break; + } } static int assigned_device_pci_cap_init(PCIDevice *pci_dev) { AssignedDevice *dev = container_of(pci_dev, AssignedDevice, dev); PCIRegion *pci_region = dev->real_device.regions; - int next_cap_pt = 0; + int ret, pos; + + /* Clear initial capabilities pointer and status copied from hw */ + pci_set_byte(pci_dev->config + PCI_CAPABILITY_LIST, 0); + pci_set_word(pci_dev->config + PCI_STATUS, + pci_get_word(pci_dev->config + PCI_STATUS) & + ~PCI_STATUS_CAP_LIST); - pci_dev->cap.length = 0; #ifdef KVM_CAP_IRQ_ROUTING #ifdef KVM_CAP_DEVICE_MSI /* Expose MSI capability * MSI capability is the 1st capability in capability config */ - if (pci_find_cap_offset(dev->pdev, PCI_CAP_ID_MSI)) { + if ((pos = pci_find_cap_offset(pci_dev, PCI_CAP_ID_MSI, 0))) { dev->cap.available |= ASSIGNED_DEVICE_CAP_MSI; - memset(&pci_dev->config[pci_dev->cap.start + pci_dev->cap.length], - 0, PCI_CAPABILITY_CONFIG_MSI_LENGTH); - pci_dev->config[pci_dev->cap.start + pci_dev->cap.length] = - PCI_CAP_ID_MSI; - pci_dev->cap.length += PCI_CAPABILITY_CONFIG_MSI_LENGTH; - next_cap_pt = 1; + /* Only 32-bit/no-mask currently supported */ + if ((ret = pci_add_capability(pci_dev, PCI_CAP_ID_MSI, pos, 10)) < 0) { + return ret; + } + + pci_set_word(pci_dev->config + pos + PCI_MSI_FLAGS, + pci_get_word(pci_dev->config + pos + PCI_MSI_FLAGS) & + PCI_MSI_FLAGS_QMASK); + pci_set_long(pci_dev->config + pos + PCI_MSI_ADDRESS_LO, 0); + pci_set_word(pci_dev->config + pos + PCI_MSI_DATA_32, 0); + + /* Set writable fields */ + pci_set_word(pci_dev->wmask + pos + PCI_MSI_FLAGS, + PCI_MSI_FLAGS_QSIZE | PCI_MSI_FLAGS_ENABLE); + pci_set_long(pci_dev->wmask + pos + PCI_MSI_ADDRESS_LO, 0xfffffffc); + pci_set_word(pci_dev->wmask + pos + PCI_MSI_DATA_32, 0xffff); } #endif #ifdef KVM_CAP_DEVICE_MSIX /* Expose MSI-X capability */ - if (pci_find_cap_offset(dev->pdev, PCI_CAP_ID_MSIX)) { - int pos, entry_nr, bar_nr; - u32 msix_table_entry; + if ((pos = pci_find_cap_offset(pci_dev, PCI_CAP_ID_MSIX, 0))) { + int bar_nr; + uint32_t msix_table_entry; + dev->cap.available |= ASSIGNED_DEVICE_CAP_MSIX; - memset(&pci_dev->config[pci_dev->cap.start + pci_dev->cap.length], - 0, PCI_CAPABILITY_CONFIG_MSIX_LENGTH); - pos = pci_find_cap_offset(dev->pdev, PCI_CAP_ID_MSIX); - entry_nr = pci_read_word(dev->pdev, pos + 2) & PCI_MSIX_TABSIZE; - pci_dev->config[pci_dev->cap.start + pci_dev->cap.length] = 0x11; - pci_dev->config[pci_dev->cap.start + - pci_dev->cap.length + 2] = entry_nr; - msix_table_entry = pci_read_long(dev->pdev, pos + PCI_MSIX_TABLE); - *(uint32_t *)(pci_dev->config + pci_dev->cap.start + - pci_dev->cap.length + PCI_MSIX_TABLE) = msix_table_entry; - *(uint32_t *)(pci_dev->config + pci_dev->cap.start + - pci_dev->cap.length + PCI_MSIX_PBA) = - pci_read_long(dev->pdev, pos + PCI_MSIX_PBA); + if ((ret = pci_add_capability(pci_dev, PCI_CAP_ID_MSIX, pos, 12)) < 0) { + return ret; + } + + pci_set_word(pci_dev->config + pos + PCI_MSIX_FLAGS, + pci_get_word(pci_dev->config + pos + PCI_MSIX_FLAGS) & + PCI_MSIX_TABSIZE); + + /* Only enable and function mask bits are writable */ + pci_set_word(pci_dev->wmask + pos + PCI_MSIX_FLAGS, + PCI_MSIX_FLAGS_ENABLE | PCI_MSIX_FLAGS_MASKALL); + + msix_table_entry = pci_get_long(pci_dev->config + pos + PCI_MSIX_TABLE); bar_nr = msix_table_entry & PCI_MSIX_BIR; msix_table_entry &= ~PCI_MSIX_BIR; dev->msix_table_addr = pci_region[bar_nr].base_addr + msix_table_entry; - if (next_cap_pt != 0) { - pci_dev->config[pci_dev->cap.start + next_cap_pt] = - pci_dev->cap.start + pci_dev->cap.length; - next_cap_pt += PCI_CAPABILITY_CONFIG_MSI_LENGTH; - } else - next_cap_pt = 1; - pci_dev->cap.length += PCI_CAPABILITY_CONFIG_MSIX_LENGTH; } #endif #endif + /* Minimal PM support, nothing writable, device appears to NAK changes */ + if ((pos = pci_find_cap_offset(pci_dev, PCI_CAP_ID_PM, 0))) { + uint16_t pmc; + if ((ret = pci_add_capability(pci_dev, PCI_CAP_ID_PM, pos, + PCI_PM_SIZEOF)) < 0) { + return ret; + } + + pmc = pci_get_word(pci_dev->config + pos + PCI_CAP_FLAGS); + pmc &= (PCI_PM_CAP_VER_MASK | PCI_PM_CAP_DSI); + pci_set_word(pci_dev->config + pos + PCI_CAP_FLAGS, pmc); + + /* assign_device will bring the device up to D0, so we don't need + * to worry about doing that ourselves here. */ + pci_set_word(pci_dev->config + pos + PCI_PM_CTRL, + PCI_PM_CTRL_NO_SOFT_RESET); + + pci_set_byte(pci_dev->config + pos + PCI_PM_PPB_EXTENSIONS, 0); + pci_set_byte(pci_dev->config + pos + PCI_PM_DATA_REGISTER, 0); + } + + if ((pos = pci_find_cap_offset(pci_dev, PCI_CAP_ID_EXP, 0))) { + uint8_t version; + uint16_t type, devctl, lnkcap, lnksta; + uint32_t devcap; + int size = 0x3c; /* version 2 size */ + + version = pci_get_byte(pci_dev->config + pos + PCI_EXP_FLAGS); + version &= PCI_EXP_FLAGS_VERS; + if (version == 1) { + size = 0x14; + } else if (version > 2) { + fprintf(stderr, "Unsupported PCI express capability version %d\n", + version); + return -EINVAL; + } + + if ((ret = pci_add_capability(pci_dev, PCI_CAP_ID_EXP, + pos, size)) < 0) { + return ret; + } + + type = pci_get_word(pci_dev->config + pos + PCI_EXP_FLAGS); + type = (type & PCI_EXP_FLAGS_TYPE) >> 8; + if (type != PCI_EXP_TYPE_ENDPOINT && + type != PCI_EXP_TYPE_LEG_END && type != PCI_EXP_TYPE_RC_END) { + fprintf(stderr, + "Device assignment only supports endpoint assignment, " + "device type %d\n", type); + return -EINVAL; + } + + /* capabilities, pass existing read-only copy + * PCI_EXP_FLAGS_IRQ: updated by hardware, should be direct read */ + + /* device capabilities: hide FLR */ + devcap = pci_get_long(pci_dev->config + pos + PCI_EXP_DEVCAP); + devcap &= ~PCI_EXP_DEVCAP_FLR; + pci_set_long(pci_dev->config + pos + PCI_EXP_DEVCAP, devcap); + + /* device control: clear all error reporting enable bits, leaving + * leaving only a few host values. Note, these are + * all writable, but not passed to hw. + */ + devctl = pci_get_word(pci_dev->config + pos + PCI_EXP_DEVCTL); + devctl = (devctl & (PCI_EXP_DEVCTL_READRQ | PCI_EXP_DEVCTL_PAYLOAD)) | + PCI_EXP_DEVCTL_RELAX_EN | PCI_EXP_DEVCTL_NOSNOOP_EN; + pci_set_word(pci_dev->config + pos + PCI_EXP_DEVCTL, devctl); + devctl = PCI_EXP_DEVCTL_BCR_FLR | PCI_EXP_DEVCTL_AUX_PME; + pci_set_word(pci_dev->wmask + pos + PCI_EXP_DEVCTL, ~devctl); + + /* Clear device status */ + pci_set_word(pci_dev->config + pos + PCI_EXP_DEVSTA, 0); + + /* Link capabilities, expose links and latencues, clear reporting */ + lnkcap = pci_get_word(pci_dev->config + pos + PCI_EXP_LNKCAP); + lnkcap &= (PCI_EXP_LNKCAP_SLS | PCI_EXP_LNKCAP_MLW | + PCI_EXP_LNKCAP_ASPMS | PCI_EXP_LNKCAP_L0SEL | + PCI_EXP_LNKCAP_L1EL); + pci_set_word(pci_dev->config + pos + PCI_EXP_LNKCAP, lnkcap); + pci_set_word(pci_dev->wmask + pos + PCI_EXP_LNKCAP, + PCI_EXP_LNKCTL_ASPMC | PCI_EXP_LNKCTL_RCB | + PCI_EXP_LNKCTL_CCC | PCI_EXP_LNKCTL_ES | + PCI_EXP_LNKCTL_CLKREQ_EN | PCI_EXP_LNKCTL_HAWD); + + /* Link control, pass existing read-only copy. Should be writable? */ + + /* Link status, only expose current speed and width */ + lnksta = pci_get_word(pci_dev->config + pos + PCI_EXP_LNKSTA); + lnksta &= (PCI_EXP_LNKSTA_CLS | PCI_EXP_LNKSTA_NLW); + pci_set_word(pci_dev->config + pos + PCI_EXP_LNKSTA, lnksta); + + if (version >= 2) { + /* Slot capabilities, control, status - not needed for endpoints */ + pci_set_long(pci_dev->config + pos + PCI_EXP_SLTCAP, 0); + pci_set_word(pci_dev->config + pos + PCI_EXP_SLTCTL, 0); + pci_set_word(pci_dev->config + pos + PCI_EXP_SLTSTA, 0); + + /* Root control, capabilities, status - not needed for endpoints */ + pci_set_word(pci_dev->config + pos + PCI_EXP_RTCTL, 0); + pci_set_word(pci_dev->config + pos + PCI_EXP_RTCAP, 0); + pci_set_long(pci_dev->config + pos + PCI_EXP_RTSTA, 0); + + /* Device capabilities/control 2, pass existing read-only copy */ + /* Link control 2, pass existing read-only copy */ + } + } + + if ((pos = pci_find_cap_offset(pci_dev, PCI_CAP_ID_PCIX, 0))) { + uint16_t cmd; + uint32_t status; + + /* Only expose the minimum, 8 byte capability */ + if ((ret = pci_add_capability(pci_dev, PCI_CAP_ID_PCIX, pos, 8)) < 0) { + return ret; + } + + /* Command register, clear upper bits, including extended modes */ + cmd = pci_get_word(pci_dev->config + pos + PCI_X_CMD); + cmd &= (PCI_X_CMD_DPERR_E | PCI_X_CMD_ERO | PCI_X_CMD_MAX_READ | + PCI_X_CMD_MAX_SPLIT); + pci_set_word(pci_dev->config + pos + PCI_X_CMD, cmd); + + /* Status register, update with emulated PCI bus location, clear + * error bits, leave the rest. */ + status = pci_get_long(pci_dev->config + pos + PCI_X_STATUS); + status &= ~(PCI_X_STATUS_BUS | PCI_X_STATUS_DEVFN); + status |= (pci_bus_num(pci_dev->bus) << 8) | pci_dev->devfn; + status &= ~(PCI_X_STATUS_SPL_DISC | PCI_X_STATUS_UNX_SPL | + PCI_X_STATUS_SPL_ERR); + pci_set_long(pci_dev->config + pos + PCI_X_STATUS, status); + } + + if ((pos = pci_find_cap_offset(pci_dev, PCI_CAP_ID_VPD, 0))) { + /* Direct R/W passthrough */ + if ((ret = pci_add_capability(pci_dev, PCI_CAP_ID_VPD, pos, 8)) < 0) { + return ret; + } + } + + /* Devices can have multiple vendor capabilities, get them all */ + for (pos = 0; (pos = pci_find_cap_offset(pci_dev, PCI_CAP_ID_VNDR, pos)); + pos += PCI_CAP_LIST_NEXT) { + uint8_t len = pci_get_byte(pci_dev->config + pos + PCI_CAP_FLAGS); + /* Direct R/W passthrough */ + if ((ret = pci_add_capability(pci_dev, PCI_CAP_ID_VNDR, + pos, len)) < 0) { + return ret; + } + } + return 0; } @@ -1080,7 +1638,7 @@ unsigned int offset = addr & 0xfff; void *page = adev->msix_table_page; - DEBUG("write to MSI-X entry table mmio offset 0x%lx, val 0x%lx\n", + DEBUG("write to MSI-X entry table mmio offset 0x%lx, val 0x%x\n", addr, val); memcpy((void *)((char *)page + offset), &val, 4); } @@ -1119,25 +1677,64 @@ } memset(dev->msix_table_page, 0, 0x1000); dev->mmio_index = cpu_register_io_memory( - msix_mmio_read, msix_mmio_write, dev); + msix_mmio_read, msix_mmio_write, dev, + DEVICE_NATIVE_ENDIAN); return 0; } +static void assigned_dev_unregister_msix_mmio(AssignedDevice *dev) +{ + if (!dev->msix_table_page) + return; + + cpu_unregister_io_memory(dev->mmio_index); + dev->mmio_index = 0; + + if (munmap(dev->msix_table_page, 0x1000) == -1) { + fprintf(stderr, "error unmapping msix_table_page! %s\n", + strerror(errno)); + } + dev->msix_table_page = NULL; +} + +static const VMStateDescription vmstate_assigned_device = { + .name = "pci-assign", + .fields = (VMStateField []) { + VMSTATE_END_OF_LIST() + } +}; + +static void reset_assigned_device(DeviceState *dev) +{ + PCIDevice *d = DO_UPCAST(PCIDevice, qdev, dev); + + /* + * When a 0 is written to the command register, the device is logically + * disconnected from the PCI bus. This avoids further DMA transfers. + */ + assigned_dev_pci_write_config(d, PCI_COMMAND, 0, 2); +} + static int assigned_initfn(struct PCIDevice *pci_dev) { AssignedDevice *dev = DO_UPCAST(AssignedDevice, dev, pci_dev); - struct pci_access *pacc; uint8_t e_device, e_intx; int r; - if (!dev->host.bus && !dev->host.dev && !dev->host.func) { - qemu_error("pci-assign: error: no host device specified\n"); - goto out; + if (!kvm_enabled()) { + error_report("pci-assign: error: requires KVM support"); + return -1; } - if (get_real_device(dev, dev->host.bus, dev->host.dev, dev->host.func)) { - qemu_error("pci-assign: Error: Couldn't get real device (%s)!\n", - dev->dev.qdev.id); + if (!dev->host.seg && !dev->host.bus && !dev->host.dev && !dev->host.func) { + error_report("pci-assign: error: no host device specified"); + return -1; + } + + if (get_real_device(dev, dev->host.seg, dev->host.bus, + dev->host.dev, dev->host.func)) { + error_report("pci-assign: Error: Couldn't get real device (%s)!", + dev->dev.qdev.id); goto out; } @@ -1152,23 +1749,18 @@ e_intx = dev->dev.config[0x3d] - 1; dev->intpin = e_intx; dev->run = 0; - dev->girq = 0; + dev->girq = -1; + dev->h_segnr = dev->host.seg; dev->h_busnr = dev->host.bus; dev->h_devfn = PCI_DEVFN(dev->host.dev, dev->host.func); - pacc = pci_alloc(); - pci_init(pacc); - dev->pdev = pci_get_dev(pacc, 0, dev->host.bus, dev->host.dev, dev->host.func); - - if (pci_enable_capability_support(pci_dev, 0, NULL, - assigned_device_pci_cap_write_config, - assigned_device_pci_cap_init) < 0) - goto assigned_out; + if (assigned_device_pci_cap_init(pci_dev) < 0) + goto out; /* assign device to guest */ r = assign_device(dev); if (r < 0) - goto assigned_out; + goto out; /* assign irq for the device */ r = assign_irq(dev); @@ -1182,6 +1774,14 @@ assigned_dev_load_option_rom(dev); QLIST_INSERT_HEAD(&devs, dev, next); + + add_boot_device_path(dev->bootindex, &pci_dev->qdev, NULL); + + /* Register a vmsd so that we can mark it unmigratable. */ + vmstate_register(&dev->dev.qdev, 0, &vmstate_assigned_device, dev); + register_device_unmigratable(&dev->dev.qdev, + vmstate_assigned_device.name, dev); + return 0; assigned_out: @@ -1195,6 +1795,7 @@ { AssignedDevice *dev = DO_UPCAST(AssignedDevice, dev, pci_dev); + vmstate_unregister(&dev->dev.qdev, &vmstate_assigned_device, dev); QLIST_REMOVE(dev, next); deassign_device(dev); free_assigned_device(dev); @@ -1206,7 +1807,7 @@ PCIHostDevice *ptr = qdev_get_prop_ptr(dev, prop); int rc; - rc = pci_parse_host_devaddr(str, &ptr->bus, &ptr->dev, &ptr->func); + rc = pci_parse_host_devaddr(str, &ptr->seg, &ptr->bus, &ptr->dev, &ptr->func); if (rc != 0) return -1; return 0; @@ -1231,13 +1832,19 @@ .qdev.name = "pci-assign", .qdev.desc = "pass through host pci devices to the guest", .qdev.size = sizeof(AssignedDevice), + .qdev.reset = reset_assigned_device, .init = assigned_initfn, .exit = assigned_exitfn, .config_read = assigned_dev_pci_read_config, .config_write = assigned_dev_pci_write_config, .qdev.props = (Property[]) { DEFINE_PROP("host", AssignedDevice, host, qdev_prop_hostaddr, PCIHostDevice), - DEFINE_PROP_UINT32("iommu", AssignedDevice, use_iommu, 1), + DEFINE_PROP_BIT("iommu", AssignedDevice, features, + ASSIGNED_DEVICE_USE_IOMMU_BIT, true), + DEFINE_PROP_BIT("prefer_msi", AssignedDevice, features, + ASSIGNED_DEVICE_PREFER_MSI_BIT, true), + DEFINE_PROP_INT32("bootindex", AssignedDevice, bootindex, -1), + DEFINE_PROP_STRING("configfd", AssignedDevice, configfd_name), DEFINE_PROP_END_OF_LIST(), }, }; @@ -1249,69 +1856,6 @@ device_init(assign_register_devices) - -/* - * Syntax to assign device: - * - * -pcidevice host=bus:dev.func[,dma=none][,name=Foo] - * - * Example: - * -pcidevice host=00:13.0,dma=pvdma - * - * dma can currently only be 'none' to disable iommu support. - */ -QemuOpts *add_assigned_device(const char *arg) -{ - QemuOpts *opts = NULL; - char host[64], id[64], dma[8]; - int r; - - r = get_param_value(host, sizeof(host), "host", arg); - if (!r) - goto bad; - r = get_param_value(id, sizeof(id), "id", arg); - if (!r) - r = get_param_value(id, sizeof(id), "name", arg); - if (!r) - r = get_param_value(id, sizeof(id), "host", arg); - - opts = qemu_opts_create(&qemu_device_opts, id, 0); - if (!opts) - goto bad; - qemu_opt_set(opts, "driver", "pci-assign"); - qemu_opt_set(opts, "host", host); - -#ifdef KVM_CAP_IOMMU - r = get_param_value(dma, sizeof(dma), "dma", arg); - if (r && !strncmp(dma, "none", 4)) - qemu_opt_set(opts, "iommu", "0"); -#endif - qemu_opts_print(opts, NULL); - return opts; - -bad: - fprintf(stderr, "pcidevice argument parse error; " - "please check the help text for usage\n"); - if (opts) - qemu_opts_del(opts); - return NULL; -} - -void add_assigned_devices(PCIBus *bus, const char **devices, int n_devices) -{ - QemuOpts *opts; - int i; - - for (i = 0; i < n_devices; i++) { - opts = add_assigned_device(devices[i]); - if (opts == NULL) { - fprintf(stderr, "Could not add assigned device %s\n", devices[i]); - exit(1); - } - /* generic code will call qdev_device_add() for the device */ - } -} - /* * Scan the assigned devices for the devices that have an option ROM, and then * load the corresponding ROM data to RAM. If an error occurs while loading an @@ -1319,61 +1863,64 @@ */ static void assigned_dev_load_option_rom(AssignedDevice *dev) { - int size, len; - void *buf; + char name[32], rom_file[64]; FILE *fp; - uint8_t i = 1; - char rom_file[64]; + uint8_t val; + struct stat st; + void *ptr; - snprintf(rom_file, sizeof(rom_file), - "/sys/bus/pci/devices/0000:%02x:%02x.%01x/rom", - dev->host.bus, dev->host.dev, dev->host.func); - - if (access(rom_file, F_OK)) + /* If loading ROM from file, pci handles it */ + if (dev->dev.romfile || !dev->dev.rom_bar) return; - /* Write something to the ROM file to enable it */ - fp = fopen(rom_file, "wb"); - if (fp == NULL) - return; - len = fwrite(&i, 1, 1, fp); - fclose(fp); - if (len != 1) - return; + snprintf(rom_file, sizeof(rom_file), + "/sys/bus/pci/devices/%04x:%02x:%02x.%01x/rom", + dev->host.seg, dev->host.bus, dev->host.dev, dev->host.func); - /* The file has to be closed and reopened, otherwise it won't work */ - fp = fopen(rom_file, "rb"); - if (fp == NULL) + if (stat(rom_file, &st)) { return; + } - fseek(fp, 0, SEEK_END); - size = ftell(fp); - fseek(fp, 0, SEEK_SET); - - buf = malloc(size); - if (buf == NULL) { - fclose(fp); + if (access(rom_file, F_OK)) { + fprintf(stderr, "pci-assign: Insufficient privileges for %s\n", + rom_file); return; } - fread(buf, size, 1, fp); - if (!feof(fp) || ferror(fp)) { - free(buf); - fclose(fp); + /* Write "1" to the ROM file to enable it */ + fp = fopen(rom_file, "r+"); + if (fp == NULL) { return; } - fclose(fp); - - /* Copy ROM contents into the space backing the ROM BAR */ - if (dev->v_addrs[PCI_ROM_SLOT].r_size >= size && - dev->v_addrs[PCI_ROM_SLOT].u.r_virtbase) { - mprotect(dev->v_addrs[PCI_ROM_SLOT].u.r_virtbase, - size, PROT_READ | PROT_WRITE); - memcpy(dev->v_addrs[PCI_ROM_SLOT].u.r_virtbase, - buf, size); - mprotect(dev->v_addrs[PCI_ROM_SLOT].u.r_virtbase, - size, PROT_READ); + val = 1; + if (fwrite(&val, 1, 1, fp) != 1) { + goto close_rom; } + fseek(fp, 0, SEEK_SET); - free(buf); + snprintf(name, sizeof(name), "%s.rom", dev->dev.qdev.info->name); + dev->dev.rom_offset = qemu_ram_alloc(&dev->dev.qdev, name, st.st_size); + ptr = qemu_get_ram_ptr(dev->dev.rom_offset); + memset(ptr, 0xff, st.st_size); + + if (!fread(ptr, 1, st.st_size, fp)) { + fprintf(stderr, "pci-assign: Cannot read from host %s\n" + "\tDevice option ROM contents are probably invalid " + "(check dmesg).\n\tSkip option ROM probe with rombar=0, " + "or load from file with romfile=\n", rom_file); + qemu_ram_free(dev->dev.rom_offset); + dev->dev.rom_offset = 0; + goto close_rom; + } + + pci_register_bar(&dev->dev, PCI_ROM_SLOT, + st.st_size, 0, pci_map_option_rom); +close_rom: + /* Write "0" to disable ROM */ + fseek(fp, 0, SEEK_SET); + val = 0; + if (!fwrite(&val, 1, 1, fp)) { + DEBUG("%s\n", "Failed to disable pci-sysfs rom file"); + } + fclose(fp); } diff -Nru qemu-kvm-0.12.5+noroms/hw/device-assignment.h qemu-kvm-0.14.1/hw/device-assignment.h --- qemu-kvm-0.12.5+noroms/hw/device-assignment.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/device-assignment.h 2011-05-11 13:29:46.000000000 +0000 @@ -37,6 +37,7 @@ #define PCI_DEVFN(slot, func) ((((slot) & 0x1f) << 3) | ((func) & 0x07)) typedef struct PCIHostDevice { + int seg; int bus; int dev; int func; @@ -56,13 +57,13 @@ uint16_t region_number; /* number of active regions */ /* Port I/O or MMIO Regions */ - PCIRegion regions[PCI_NUM_REGIONS]; + PCIRegion regions[PCI_NUM_REGIONS - 1]; int config_fd; } PCIDevRegions; typedef struct { pcibus_t e_physbase; - uint32_t memory_index; + ram_addr_t memory_index; union { void *r_virtbase; /* mmapped access address for memory regions */ uint32_t r_baseport; /* the base guest port for I/O regions */ @@ -70,23 +71,30 @@ int num; /* our index within v_addrs[] */ pcibus_t e_size; /* emulated size of region in bytes */ pcibus_t r_size; /* real size of region in bytes */ + PCIRegion *region; } AssignedDevRegion; +#define ASSIGNED_DEVICE_USE_IOMMU_BIT 0 +#define ASSIGNED_DEVICE_PREFER_MSI_BIT 1 + +#define ASSIGNED_DEVICE_USE_IOMMU_MASK (1 << ASSIGNED_DEVICE_USE_IOMMU_BIT) +#define ASSIGNED_DEVICE_PREFER_MSI_MASK (1 << ASSIGNED_DEVICE_PREFER_MSI_BIT) + typedef struct AssignedDevice { PCIDevice dev; PCIHostDevice host; - uint32_t use_iommu; + uint32_t features; int intpin; uint8_t debug_flags; - AssignedDevRegion v_addrs[PCI_NUM_REGIONS]; + AssignedDevRegion v_addrs[PCI_NUM_REGIONS - 1]; PCIDevRegions real_device; int run; int girq; + unsigned int h_segnr; unsigned char h_busnr; unsigned int h_devfn; int irq_requested_type; int bound; - struct pci_dev *pdev; struct { #define ASSIGNED_DEVICE_CAP_MSI (1 << 0) #define ASSIGNED_DEVICE_CAP_MSIX (1 << 1) @@ -102,16 +110,11 @@ target_phys_addr_t msix_table_addr; int mmio_index; int need_emulate_cmd; + char *configfd_name; + int32_t bootindex; QLIST_ENTRY(AssignedDevice) next; } AssignedDevice; -QemuOpts *add_assigned_device(const char *arg); -void add_assigned_devices(PCIBus *bus, const char **devices, int n_devices); void assigned_dev_update_irqs(void); -#define MAX_DEV_ASSIGN_CMDLINE 8 - -extern const char *assigned_devices[MAX_DEV_ASSIGN_CMDLINE]; -extern int assigned_devices_index; - #endif /* __DEVICE_ASSIGNMENT_H__ */ diff -Nru qemu-kvm-0.12.5+noroms/hw/device-hotplug.c qemu-kvm-0.14.1/hw/device-hotplug.c --- qemu-kvm-0.12.5+noroms/hw/device-hotplug.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/device-hotplug.c 2011-05-11 13:29:46.000000000 +0000 @@ -25,20 +25,18 @@ #include "hw.h" #include "boards.h" #include "net.h" -#include "block_int.h" -#include "sysemu.h" +#include "blockdev.h" DriveInfo *add_init_drive(const char *optstr) { - int fatal_error; DriveInfo *dinfo; QemuOpts *opts; - opts = drive_add(NULL, "%s", optstr); + opts = drive_def(optstr); if (!opts) return NULL; - dinfo = drive_init(opts, current_machine, &fatal_error); + dinfo = drive_init(opts, current_machine->use_scsi); if (!dinfo) { qemu_opts_del(opts); return NULL; diff -Nru qemu-kvm-0.12.5+noroms/hw/devices.h qemu-kvm-0.14.1/hw/devices.h --- qemu-kvm-0.12.5+noroms/hw/devices.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/devices.h 2011-05-11 13:29:46.000000000 +0000 @@ -67,7 +67,4 @@ void sm501_init(uint32_t base, uint32_t local_mem_bytes, qemu_irq irq, CharDriverState *chr); -/* usb-ohci.c */ -void usb_ohci_init_sm501(uint32_t mmio_base, uint32_t localmem_base, - int num_ports, int devfn, qemu_irq irq); #endif diff -Nru qemu-kvm-0.12.5+noroms/hw/dma.c qemu-kvm-0.14.1/hw/dma.c --- qemu-kvm-0.12.5+noroms/hw/dma.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/dma.c 2011-05-11 13:29:46.000000000 +0000 @@ -57,6 +57,7 @@ uint8_t flip_flop; int dshift; struct dma_regs regs[4]; + qemu_irq *cpu_request_exit; } dma_controllers[2]; enum { @@ -344,7 +345,6 @@ } #endif - r = dma_controllers[ncont].regs + ichan; n = r->transfer_handler (r->opaque, ichan + (ncont << 2), r->now[COUNT], (r->base[COUNT] + 1) << ncont); r->now[COUNT] = n; @@ -445,9 +445,9 @@ /* request the emulator to transfer a new DMA memory block ASAP */ void DMA_schedule(int nchan) { - CPUState *env = cpu_single_env; - if (env) - cpu_exit(env); + struct dma_cont *d = &dma_controllers[nchan > 3]; + + qemu_irq_pulse(*d->cpu_request_exit); } static void dma_reset(void *opaque) @@ -465,12 +465,14 @@ /* dshift = 0: 8 bit DMA, 1 = 16 bit DMA */ static void dma_init2(struct dma_cont *d, int base, int dshift, - int page_base, int pageh_base) + int page_base, int pageh_base, + qemu_irq *cpu_request_exit) { static const int page_port_list[] = { 0x1, 0x2, 0x3, 0x7 }; int i; d->dshift = dshift; + d->cpu_request_exit = cpu_request_exit; for (i = 0; i < 8; i++) { register_ioport_write (base + (i << dshift), 1, 1, write_chan, d); register_ioport_read (base + (i << dshift), 1, 1, read_chan, d); @@ -540,14 +542,14 @@ } }; -void DMA_init (int high_page_enable) +void DMA_init(int high_page_enable, qemu_irq *cpu_request_exit) { dma_init2(&dma_controllers[0], 0x00, 0, 0x80, - high_page_enable ? 0x480 : -1); + high_page_enable ? 0x480 : -1, cpu_request_exit); dma_init2(&dma_controllers[1], 0xc0, 1, 0x88, - high_page_enable ? 0x488 : -1); - vmstate_register (0, &vmstate_dma, &dma_controllers[0]); - vmstate_register (1, &vmstate_dma, &dma_controllers[1]); + high_page_enable ? 0x488 : -1, cpu_request_exit); + vmstate_register (NULL, 0, &vmstate_dma, &dma_controllers[0]); + vmstate_register (NULL, 1, &vmstate_dma, &dma_controllers[1]); dma_bh = qemu_bh_new(DMA_run_bh, NULL); } diff -Nru qemu-kvm-0.12.5+noroms/hw/dp8393x.c qemu-kvm-0.14.1/hw/dp8393x.c --- qemu-kvm-0.12.5+noroms/hw/dp8393x.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/dp8393x.c 2011-05-11 13:29:46.000000000 +0000 @@ -908,6 +908,7 @@ qemu_register_reset(nic_reset, s); nic_reset(s); - s->mmio_index = cpu_register_io_memory(dp8393x_read, dp8393x_write, s); + s->mmio_index = cpu_register_io_memory(dp8393x_read, dp8393x_write, s, + DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base, 0x40 << it_shift, s->mmio_index); } diff -Nru qemu-kvm-0.12.5+noroms/hw/ds1225y.c qemu-kvm-0.14.1/hw/ds1225y.c --- qemu-kvm-0.12.5+noroms/hw/ds1225y.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/ds1225y.c 2011-05-11 13:29:46.000000000 +0000 @@ -171,10 +171,12 @@ } /* Read/write memory */ - mem_indexRW = cpu_register_io_memory(nvram_read, nvram_write, s); + mem_indexRW = cpu_register_io_memory(nvram_read, nvram_write, s, + DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(mem_base, s->chip_size, mem_indexRW); /* Read/write protected memory */ - mem_indexRP = cpu_register_io_memory(nvram_read, nvram_write_protected, s); + mem_indexRP = cpu_register_io_memory(nvram_read, nvram_write_protected, s, + DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(mem_base + s->chip_size, s->chip_size, mem_indexRP); return s; } diff -Nru qemu-kvm-0.12.5+noroms/hw/dummy_m68k.c qemu-kvm-0.14.1/hw/dummy_m68k.c --- qemu-kvm-0.12.5+noroms/hw/dummy_m68k.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/dummy_m68k.c 2011-05-11 13:29:46.000000000 +0000 @@ -39,12 +39,12 @@ /* RAM at address zero */ cpu_register_physical_memory(0, ram_size, - qemu_ram_alloc(ram_size) | IO_MEM_RAM); + qemu_ram_alloc(NULL, "dummy_m68k.ram", ram_size) | IO_MEM_RAM); /* Load kernel. */ if (kernel_filename) { - kernel_size = load_elf(kernel_filename, 0, &elf_entry, NULL, NULL, - 1, ELF_MACHINE, 0); + kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry, + NULL, NULL, 1, ELF_MACHINE, 0); entry = elf_entry; if (kernel_size < 0) { kernel_size = load_uimage(kernel_filename, &entry, NULL, NULL); diff -Nru qemu-kvm-0.12.5+noroms/hw/e1000.c qemu-kvm-0.14.1/hw/e1000.c --- qemu-kvm-0.12.5+noroms/hw/e1000.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/e1000.c 2011-05-11 13:29:46.000000000 +0000 @@ -1,6 +1,9 @@ /* * QEMU e1000 emulation * + * Software developer's manual: + * http://download.intel.com/design/network/manuals/8254x_GBe_SDM.pdf + * * Nir Peleg, Tutis Systems Ltd. for Qumranet Inc. * Copyright (c) 2008 Qumranet * Based on work done by: @@ -27,12 +30,13 @@ #include "net.h" #include "net/checksum.h" #include "loader.h" +#include "sysemu.h" #include "e1000_hw.h" -#define DEBUG +#define E1000_DEBUG -#ifdef DEBUG +#ifdef E1000_DEBUG enum { DEBUG_GENERAL, DEBUG_IO, DEBUG_MMIO, DEBUG_INTERRUPT, DEBUG_RX, DEBUG_TX, DEBUG_MDIC, DEBUG_EEPROM, @@ -52,6 +56,7 @@ #define IOPORT_SIZE 0x40 #define PNPMMIO_SIZE 0x20000 +#define MIN_BUF_SIZE 60 /* Min. octets in an ethernet frame sans FCS */ /* * HW models: @@ -259,21 +264,20 @@ s->eecd_state.old_eecd = val & (E1000_EECD_SK | E1000_EECD_CS | E1000_EECD_DI|E1000_EECD_FWE_MASK|E1000_EECD_REQ); + if (!(E1000_EECD_CS & val)) // CS inactive; nothing to do + return; + if (E1000_EECD_CS & (val ^ oldval)) { // CS rise edge; reset state + s->eecd_state.val_in = 0; + s->eecd_state.bitnum_in = 0; + s->eecd_state.bitnum_out = 0; + s->eecd_state.reading = 0; + } if (!(E1000_EECD_SK & (val ^ oldval))) // no clock edge return; if (!(E1000_EECD_SK & val)) { // falling edge s->eecd_state.bitnum_out++; return; } - if (!(val & E1000_EECD_CS)) { // rising, no CS (EEPROM reset) - memset(&s->eecd_state, 0, sizeof s->eecd_state); - /* - * restore old_eecd's E1000_EECD_SK (known to be on) - * to avoid false detection of a clock edge - */ - s->eecd_state.old_eecd = E1000_EECD_SK; - return; - } s->eecd_state.val_in <<= 1; if (val & E1000_EECD_DI) s->eecd_state.val_in |= 1; @@ -341,6 +345,15 @@ return ((txd_lower & E1000_TXD_CMD_VLE) != 0); } +/* FCS aka Ethernet CRC-32. We don't get it from backends and can't + * fill it in, just pad descriptor length by 4 bytes unless guest + * told us to strip it off the packet. */ +static inline int +fcs_len(E1000State *s) +{ + return (s->mac_reg[RCTL] & E1000_RCTL_SECRC) ? 0 : 4; +} + static void xmit_seg(E1000State *s) { @@ -372,9 +385,12 @@ } else // UDP cpu_to_be16wu((uint16_t *)(tp->data+css+4), len); if (tp->sum_needed & E1000_TXD_POPTS_TXSM) { + unsigned int phsum; // add pseudo-header length before checksum calculation sp = (uint16_t *)(tp->data + tp->tucso); - cpu_to_be16wu(sp, be16_to_cpup(sp) + len); + phsum = be16_to_cpup(sp) + len; + phsum = (phsum >> 16) + (phsum & 0xffff); + cpu_to_be16wu(sp, phsum); } tp->tso_frames++; } @@ -432,9 +448,10 @@ // data descriptor tp->sum_needed = le32_to_cpu(dp->upper.data) >> 8; tp->cptse = ( txd_lower & E1000_TXD_CMD_TSE ) ? 1 : 0; - } else + } else { // legacy descriptor tp->cptse = 0; + } if (vlan_enabled(s) && is_vlan_txd(txd_lower) && (tp->cptse || txd_lower & E1000_TXD_CMD_EOP)) { @@ -541,8 +558,8 @@ static int receive_filter(E1000State *s, const uint8_t *buf, int size) { - static uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; - static int mta_shift[] = {4, 3, 2, 0}; + static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + static const int mta_shift[] = {4, 3, 2, 0}; uint32_t f, rctl = s->mac_reg[RCTL], ra[2], *rp; if (is_vlan_packet(s, buf) && vlan_rx_filter_enabled(s)) { @@ -624,10 +641,19 @@ uint32_t rdh_start; uint16_t vlan_special = 0; uint8_t vlan_status = 0, vlan_offset = 0; + uint8_t min_buf[MIN_BUF_SIZE]; if (!(s->mac_reg[RCTL] & E1000_RCTL_EN)) return -1; + /* Pad to minimum Ethernet frame length */ + if (size < sizeof(min_buf)) { + memcpy(min_buf, buf, size); + memset(&min_buf[size], 0, sizeof(min_buf) - size); + buf = min_buf; + size = sizeof(min_buf); + } + if (size > s->rxbuf_size) { DBGOUT(RX, "packet too large for buffers (%lu > %d)\n", (unsigned long)size, s->rxbuf_size); @@ -639,14 +665,13 @@ if (vlan_enabled(s) && is_vlan_packet(s, buf)) { vlan_special = cpu_to_le16(be16_to_cpup((uint16_t *)(buf + 14))); - memmove((void *)(buf + 4), buf, 12); + memmove((uint8_t *)buf + 4, buf, 12); vlan_status = E1000_RXD_STAT_VP; vlan_offset = 4; size -= 4; } rdh_start = s->mac_reg[RDH]; - size += 4; // for the header do { if (s->mac_reg[RDH] == s->mac_reg[RDT] && s->check_rxov) { set_ics(s, 0, E1000_ICS_RXO); @@ -660,10 +685,11 @@ if (desc.buffer_addr) { cpu_physical_memory_write(le64_to_cpu(desc.buffer_addr), (void *)(buf + vlan_offset), size); - desc.length = cpu_to_le16(size); + desc.length = cpu_to_le16(size + fcs_len(s)); desc.status |= E1000_RXD_STAT_EOP|E1000_RXD_STAT_IXSM; - } else // as per intel docs; skip descriptors with null buf addr + } else { // as per intel docs; skip descriptors with null buf addr DBGOUT(RX, "Null RX descriptor!!\n"); + } cpu_physical_memory_write(base, (void *)&desc, sizeof(desc)); if (++s->mac_reg[RDH] * sizeof(desc) >= s->mac_reg[RDLEN]) @@ -680,9 +706,14 @@ s->mac_reg[GPRC]++; s->mac_reg[TPR]++; - n = s->mac_reg[TORL]; - if ((s->mac_reg[TORL] += size) < n) + /* TOR - Total Octets Received: + * This register includes bytes received in a packet from the field through the field, inclusively. + */ + n = s->mac_reg[TORL] + size + /* Always include FCS length. */ 4; + if (n < s->mac_reg[TORL]) s->mac_reg[TORH]++; + s->mac_reg[TORL] = n; n = E1000_ICS_RXT0; if ((rdt = s->mac_reg[RDT]) < s->mac_reg[RDH]) @@ -827,16 +858,14 @@ E1000State *s = opaque; unsigned int index = (addr & 0x1ffff) >> 2; -#ifdef TARGET_WORDS_BIGENDIAN - val = bswap32(val); -#endif - if (index < NWRITEOPS && macreg_writeops[index]) + if (index < NWRITEOPS && macreg_writeops[index]) { macreg_writeops[index](s, index, val); - else if (index < NREADOPS && macreg_readops[index]) + } else if (index < NREADOPS && macreg_readops[index]) { DBGOUT(MMIO, "e1000_mmio_writel RO %x: 0x%04x\n", index<<2, val); - else + } else { DBGOUT(UNKNOWN, "MMIO unknown write addr=0x%08x,val=0x%08x\n", index<<2, val); + } } static void @@ -863,11 +892,7 @@ if (index < NREADOPS && macreg_readops[index]) { - uint32_t val = macreg_readops[index](s, index); -#ifdef TARGET_WORDS_BIGENDIAN - val = bswap32(val); -#endif - return val; + return macreg_readops[index](s, index); } DBGOUT(UNKNOWN, "MMIO unknown read addr=0x%08x\n", index<<2); return 0; @@ -1089,20 +1114,23 @@ pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_INTEL); pci_config_set_device_id(pci_conf, E1000_DEVID); - *(uint16_t *)(pci_conf+0x06) = cpu_to_le16(0x0010); - pci_conf[0x08] = 0x03; + /* TODO: we have no capabilities, so why is this bit set? */ + pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_CAP_LIST); + pci_conf[PCI_REVISION_ID] = 0x03; pci_config_set_class(pci_conf, PCI_CLASS_NETWORK_ETHERNET); - pci_conf[0x0c] = 0x10; + /* TODO: RST# value should be 0, PCI spec 6.2.4 */ + pci_conf[PCI_CACHE_LINE_SIZE] = 0x10; - pci_conf[0x3d] = 1; // interrupt pin 0 + /* TODO: RST# value should be 0 if programmable, PCI spec 6.2.4 */ + pci_conf[PCI_INTERRUPT_PIN] = 1; // interrupt pin 0 d->mmio_index = cpu_register_io_memory(e1000_mmio_read, - e1000_mmio_write, d); + e1000_mmio_write, d, DEVICE_LITTLE_ENDIAN); - pci_register_bar((PCIDevice *)d, 0, PNPMMIO_SIZE, + pci_register_bar(&d->dev, 0, PNPMMIO_SIZE, PCI_BASE_ADDRESS_SPACE_MEMORY, e1000_mmio_map); - pci_register_bar((PCIDevice *)d, 1, IOPORT_SIZE, + pci_register_bar(&d->dev, 1, IOPORT_SIZE, PCI_BASE_ADDRESS_SPACE_IO, ioport_map); memmove(d->eeprom_data, e1000_eeprom_template, @@ -1120,6 +1148,9 @@ d->dev.qdev.info->name, d->dev.qdev.id, d); qemu_format_nic_info_str(&d->nic->nc, macaddr); + + add_boot_device_path(d->conf.bootindex, &pci_dev->qdev, "/ethernet-phy@0"); + return 0; } diff -Nru qemu-kvm-0.12.5+noroms/hw/ecc.c qemu-kvm-0.14.1/hw/ecc.c --- qemu-kvm-0.12.5+noroms/hw/ecc.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/ecc.c 2011-05-11 13:29:46.000000000 +0000 @@ -74,18 +74,15 @@ } /* Save/restore */ -void ecc_put(QEMUFile *f, ECCState *s) -{ - qemu_put_8s(f, &s->cp); - qemu_put_be16s(f, &s->lp[0]); - qemu_put_be16s(f, &s->lp[1]); - qemu_put_be16s(f, &s->count); -} - -void ecc_get(QEMUFile *f, ECCState *s) -{ - qemu_get_8s(f, &s->cp); - qemu_get_be16s(f, &s->lp[0]); - qemu_get_be16s(f, &s->lp[1]); - qemu_get_be16s(f, &s->count); -} +VMStateDescription vmstate_ecc_state = { + .name = "ecc-state", + .version_id = 0, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .fields = (VMStateField []) { + VMSTATE_UINT8(cp, ECCState), + VMSTATE_UINT16_ARRAY(lp, ECCState, 2), + VMSTATE_UINT16(count, ECCState), + VMSTATE_END_OF_LIST(), + }, +}; diff -Nru qemu-kvm-0.12.5+noroms/hw/eccmemctl.c qemu-kvm-0.14.1/hw/eccmemctl.c --- qemu-kvm-0.12.5+noroms/hw/eccmemctl.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/eccmemctl.c 2011-05-11 13:29:46.000000000 +0000 @@ -23,15 +23,7 @@ */ #include "sysbus.h" - -//#define DEBUG_ECC - -#ifdef DEBUG_ECC -#define DPRINTF(fmt, ...) \ - do { printf("ECC: " fmt , ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) -#endif +#include "trace.h" /* There are 3 versions of this chip used in SMP sun4m systems: * MCC (version 0, implementation 0) SS-600MP @@ -148,32 +140,32 @@ s->regs[ECC_MER] = s->version | (val & ECC_MER_MASK_1); else if (s->version == ECC_SMC) s->regs[ECC_MER] = s->version | (val & ECC_MER_MASK_2); - DPRINTF("Write memory enable %08x\n", val); + trace_ecc_mem_writel_mer(val); break; case ECC_MDR: s->regs[ECC_MDR] = val & ECC_MDR_MASK; - DPRINTF("Write memory delay %08x\n", val); + trace_ecc_mem_writel_mdr(val); break; case ECC_MFSR: s->regs[ECC_MFSR] = val; qemu_irq_lower(s->irq); - DPRINTF("Write memory fault status %08x\n", val); + trace_ecc_mem_writel_mfsr(val); break; case ECC_VCR: s->regs[ECC_VCR] = val; - DPRINTF("Write slot configuration %08x\n", val); + trace_ecc_mem_writel_vcr(val); break; case ECC_DR: s->regs[ECC_DR] = val; - DPRINTF("Write diagnostic %08x\n", val); + trace_ecc_mem_writel_dr(val); break; case ECC_ECR0: s->regs[ECC_ECR0] = val; - DPRINTF("Write event count 1 %08x\n", val); + trace_ecc_mem_writel_ecr0(val); break; case ECC_ECR1: s->regs[ECC_ECR0] = val; - DPRINTF("Write event count 2 %08x\n", val); + trace_ecc_mem_writel_ecr1(val); break; } } @@ -186,39 +178,39 @@ switch (addr >> 2) { case ECC_MER: ret = s->regs[ECC_MER]; - DPRINTF("Read memory enable %08x\n", ret); + trace_ecc_mem_readl_mer(ret); break; case ECC_MDR: ret = s->regs[ECC_MDR]; - DPRINTF("Read memory delay %08x\n", ret); + trace_ecc_mem_readl_mdr(ret); break; case ECC_MFSR: ret = s->regs[ECC_MFSR]; - DPRINTF("Read memory fault status %08x\n", ret); + trace_ecc_mem_readl_mfsr(ret); break; case ECC_VCR: ret = s->regs[ECC_VCR]; - DPRINTF("Read slot configuration %08x\n", ret); + trace_ecc_mem_readl_vcr(ret); break; case ECC_MFAR0: ret = s->regs[ECC_MFAR0]; - DPRINTF("Read memory fault address 0 %08x\n", ret); + trace_ecc_mem_readl_mfar0(ret); break; case ECC_MFAR1: ret = s->regs[ECC_MFAR1]; - DPRINTF("Read memory fault address 1 %08x\n", ret); + trace_ecc_mem_readl_mfar1(ret); break; case ECC_DR: ret = s->regs[ECC_DR]; - DPRINTF("Read diagnostic %08x\n", ret); + trace_ecc_mem_readl_dr(ret); break; case ECC_ECR0: ret = s->regs[ECC_ECR0]; - DPRINTF("Read event count 1 %08x\n", ret); + trace_ecc_mem_readl_ecr0(ret); break; case ECC_ECR1: ret = s->regs[ECC_ECR0]; - DPRINTF("Read event count 2 %08x\n", ret); + trace_ecc_mem_readl_ecr1(ret); break; } return ret; @@ -241,7 +233,7 @@ { ECCState *s = opaque; - DPRINTF("Write diagnostic[%d] = %02x\n", (int)addr, val); + trace_ecc_diag_mem_writeb(addr, val); s->diag[addr & ECC_DIAG_MASK] = val; } @@ -250,7 +242,7 @@ ECCState *s = opaque; uint32_t ret = s->diag[(int)addr]; - DPRINTF("Read diagnostic[%d] = %02x\n", (int)addr, ret); + trace_ecc_diag_mem_readb(addr, ret); return ret; } @@ -305,12 +297,14 @@ sysbus_init_irq(dev, &s->irq); s->regs[0] = s->version; - ecc_io_memory = cpu_register_io_memory(ecc_mem_read, ecc_mem_write, s); + ecc_io_memory = cpu_register_io_memory(ecc_mem_read, ecc_mem_write, s, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, ECC_SIZE, ecc_io_memory); if (s->version == ECC_MCC) { // SS-600MP only ecc_io_memory = cpu_register_io_memory(ecc_diag_mem_read, - ecc_diag_mem_write, s); + ecc_diag_mem_write, s, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, ECC_DIAG_SIZE, ecc_io_memory); } diff -Nru qemu-kvm-0.12.5+noroms/hw/eepro100.c qemu-kvm-0.14.1/hw/eepro100.c --- qemu-kvm-0.12.5+noroms/hw/eepro100.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/eepro100.c 2011-05-11 13:29:46.000000000 +0000 @@ -1,15 +1,15 @@ /* * QEMU i8255x (PRO100) emulation * - * Copyright (c) 2006-2007 Stefan Weil + * Copyright (C) 2006-2010 Stefan Weil * * Portions of the code are copies from grub / etherboot eepro100.c * and linux e100.c. * - * This program is free software; you can redistribute it and/or modify + * 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. + * the Free Software Foundation, either version 2 of the License, or + * (at your option) version 3 or 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 @@ -17,10 +17,10 @@ * 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, see . + * along with this program. If not, see . * * Tested features (i82559): - * PXE boot (i386) no valid link + * PXE boot (i386) ok * Linux networking (i386) ok * * Untested: @@ -31,33 +31,29 @@ * * Intel 8255x 10/100 Mbps Ethernet Controller Family * Open Source Software Developer Manual + * + * TODO: + * * PHY emulation should be separated from nic emulation. + * Most nic emulations could share the same phy code. + * * i82550 is untested. It is programmed like the i82559. + * * i82562 is untested. It is programmed like the i82559. + * * Power management (i82558 and later) is not implemented. + * * Wake-on-LAN is not implemented. */ -#if defined(TARGET_I386) -# warning "PXE boot still not working!" -#endif - #include /* offsetof */ -#include #include "hw.h" -#include "loader.h" /* rom_add_option */ #include "pci.h" #include "net.h" #include "eeprom93xx.h" - -/* Common declarations for all PCI devices. */ - -#define PCI_CONFIG_8(offset, value) \ - (pci_conf[offset] = (value)) -#define PCI_CONFIG_16(offset, value) \ - (*(uint16_t *)&pci_conf[offset] = cpu_to_le16(value)) -#define PCI_CONFIG_32(offset, value) \ - (*(uint32_t *)&pci_conf[offset] = cpu_to_le32(value)) +#include "sysemu.h" #define KiB 1024 /* Debug EEPRO100 card. */ -//~ #define DEBUG_EEPRO100 +#if 0 +# define DEBUG_EEPRO100 +#endif #ifdef DEBUG_EEPRO100 #define logout(fmt, ...) fprintf(stderr, "EE100\t%-24s" fmt, __func__, ## __VA_ARGS__) @@ -91,6 +87,7 @@ #define i82559C 0x82559c #define i82559ER 0x82559e #define i82562 0x82562 +#define i82801 0x82801 /* Use 64 word EEPROM. TODO: could be a runtime option. */ #define EEPROM_SIZE 64 @@ -115,25 +112,39 @@ #define RU_NOP 0x0000 #define RX_START 0x0001 #define RX_RESUME 0x0002 -#define RX_ABORT 0x0004 +#define RU_ABORT 0x0004 #define RX_ADDR_LOAD 0x0006 #define RX_RESUMENR 0x0007 #define INT_MASK 0x0100 #define DRVR_INT 0x0200 /* Driver generated interrupt. */ +typedef struct { + PCIDeviceInfo pci; + uint32_t device; + uint16_t device_id; + uint8_t revision; + uint8_t stats_size; + bool has_extended_tcb_support; + bool power_management; +} E100PCIDeviceInfo; + /* Offsets to the various registers. All accesses need not be longword aligned. */ enum speedo_offsets { - SCBStatus = 0, + SCBStatus = 0, /* Status Word. */ SCBAck = 1, SCBCmd = 2, /* Rx/Command Unit command and status. */ SCBIntmask = 3, SCBPointer = 4, /* General purpose pointer. */ SCBPort = 8, /* Misc. commands and operands. */ - SCBflash = 12, SCBeeprom = 14, /* EEPROM and flash memory control. */ + SCBflash = 12, /* Flash memory control. */ + SCBeeprom = 14, /* EEPROM control. */ SCBCtrlMDI = 16, /* MDI interface control. */ SCBEarlyRx = 20, /* Early receive byte count. */ - SCBFlow = 24, + SCBFlow = 24, /* Flow Control. */ + SCBpmdr = 27, /* Power Management Driver. */ + SCBgctrl = 28, /* General Control. */ + SCBgstat = 29, /* General Status. */ }; /* A speedo3 transmit buffer descriptor with two buffers... */ @@ -141,15 +152,17 @@ uint16_t status; uint16_t command; uint32_t link; /* void * */ - uint32_t tx_desc_addr; /* transmit buffer decsriptor array address. */ + uint32_t tbd_array_addr; /* transmit buffer descriptor array address. */ uint16_t tcb_bytes; /* transmit command block byte count (in lower 14 bits */ uint8_t tx_threshold; /* transmit threshold */ uint8_t tbd_count; /* TBD number */ - //~ /* This constitutes two "TBD" entries: hdr and data */ - //~ uint32_t tx_buf_addr0; /* void *, header of frame to be transmitted. */ - //~ int32_t tx_buf_size0; /* Length of Tx hdr. */ - //~ uint32_t tx_buf_addr1; /* void *, data to be transmitted. */ - //~ int32_t tx_buf_size1; /* Length of Tx data. */ +#if 0 + /* This constitutes two "TBD" entries: hdr and data */ + uint32_t tx_buf_addr0; /* void *, header of frame to be transmitted. */ + int32_t tx_buf_size0; /* Length of Tx hdr. */ + uint32_t tx_buf_addr1; /* void *, data to be transmitted. */ + int32_t tx_buf_size1; /* Length of Tx data. */ +#endif } eepro100_tx_t; /* Receive frame descriptor. */ @@ -163,13 +176,27 @@ char packet[MAX_ETH_FRAME_SIZE + 4]; } eepro100_rx_t; +typedef enum { + COMMAND_EL = BIT(15), + COMMAND_S = BIT(14), + COMMAND_I = BIT(13), + COMMAND_NC = BIT(4), + COMMAND_SF = BIT(3), + COMMAND_CMD = BITS(2, 0), +} scb_command_bit; + +typedef enum { + STATUS_C = BIT(15), + STATUS_OK = BIT(13), +} scb_status_bit; + typedef struct { uint32_t tx_good_frames, tx_max_collisions, tx_late_collisions, - tx_underruns, tx_lost_crs, tx_deferred, tx_single_collisions, - tx_multiple_collisions, tx_total_collisions; + tx_underruns, tx_lost_crs, tx_deferred, tx_single_collisions, + tx_multiple_collisions, tx_total_collisions; uint32_t rx_good_frames, rx_crc_errors, rx_alignment_errors, - rx_resource_errors, rx_overrun_errors, rx_cdt_errors, - rx_short_frame_errors; + rx_resource_errors, rx_overrun_errors, rx_cdt_errors, + rx_short_frame_errors; uint32_t fc_xmt_pause, fc_rcv_pause, fc_rcv_unsupported; uint16_t xmt_tco_frames, rcv_tco_frames; /* TODO: i82559 has six reserved statistics but a total of 24 dwords. */ @@ -193,7 +220,8 @@ typedef struct { PCIDevice dev; - uint8_t mult[8]; /* multicast mask array */ + /* Hash register (multicast mask array, multiple individual addresses). */ + uint8_t mult[8]; int mmio_index; NICState *nic; NICConf conf; @@ -213,13 +241,14 @@ uint32_t ru_offset; /* RU address offset */ uint32_t statsaddr; /* pointer to eepro100_stats_t */ + /* Temporary status information (no need to save these values), + * used while processing CU commands. */ + eepro100_tx_t tx; /* transmit buffer descriptor */ + uint32_t cb_address; /* = cu_base + cu_offset */ + /* Statistical counters. Also used for wake-up packet (i82559). */ eepro100_stats_t statistics; -#if 0 - uint16_t status; -#endif - /* Configuration bytes. */ uint8_t configuration[22]; @@ -233,6 +262,32 @@ bool has_extended_tcb_support; } EEPRO100State; +/* Word indices in EEPROM. */ +typedef enum { + EEPROM_CNFG_MDIX = 0x03, + EEPROM_ID = 0x05, + EEPROM_PHY_ID = 0x06, + EEPROM_VENDOR_ID = 0x0c, + EEPROM_CONFIG_ASF = 0x0d, + EEPROM_DEVICE_ID = 0x23, + EEPROM_SMBUS_ADDR = 0x90, +} EEPROMOffset; + +/* Bit values for EEPROM ID word. */ +typedef enum { + EEPROM_ID_MDM = BIT(0), /* Modem */ + EEPROM_ID_STB = BIT(1), /* Standby Enable */ + EEPROM_ID_WMR = BIT(2), /* ??? */ + EEPROM_ID_WOL = BIT(5), /* Wake on LAN */ + EEPROM_ID_DPD = BIT(6), /* Deep Power Down */ + EEPROM_ID_ALT = BIT(7), /* */ + /* BITS(10, 8) device revision */ + EEPROM_ID_BD = BIT(11), /* boot disable */ + EEPROM_ID_ID = BIT(13), /* id bit */ + /* BITS(15, 14) signature */ + EEPROM_ID_VALID = BIT(14), /* signature for valid eeprom */ +} eeprom_id_bit; + /* Default values for MDI (PHY) registers */ static const uint16_t eepro100_mdi_default[] = { /* MDI Registers 0 - 6, 7 */ @@ -263,7 +318,7 @@ /* From FreeBSD */ /* XXX: optimize */ -static int compute_mcast_idx(const uint8_t * ep) +static unsigned compute_mcast_idx(const uint8_t * ep) { uint32_t crc; int carry, i, j; @@ -281,7 +336,7 @@ } } } - return (crc >> 26); + return (crc & BITS(7, 2)) >> 2; } #if defined(DEBUG_EEPRO100) @@ -338,14 +393,16 @@ } } -static void eepro100_interrupt(EEPRO100State * s, uint8_t stat) +static void eepro100_interrupt(EEPRO100State * s, uint8_t status) { uint8_t mask = ~s->mem[SCBIntmask]; - s->mem[SCBAck] |= stat; - stat = s->scb_stat = s->mem[SCBAck]; - stat &= (mask | 0x0f); - //~ stat &= (~s->mem[SCBIntmask] | 0x0xf); - if (stat && (mask & 0x01)) { + s->mem[SCBAck] |= status; + status = s->scb_stat = s->mem[SCBAck]; + status &= (mask | 0x0f); +#if 0 + status &= (~s->mem[SCBIntmask] | 0x0xf); +#endif + if (status && (mask & 0x01)) { /* SCB mask and SCB Bit M do not disable interrupt. */ enable_interrupt(s); } else if (s->int_stat) { @@ -372,13 +429,11 @@ eepro100_interrupt(s, 0x40); } -#if 0 static void eepro100_rnr_interrupt(EEPRO100State * s) { /* RU is not ready. */ eepro100_interrupt(s, 0x10); } -#endif static void eepro100_mdi_interrupt(EEPRO100State * s) { @@ -400,160 +455,65 @@ } #endif -static void pci_reset(EEPRO100State * s) +static void e100_pci_reset(EEPRO100State * s, E100PCIDeviceInfo *e100_device) { uint32_t device = s->device; uint8_t *pci_conf = s->dev.config; - bool power_management = 1; TRACE(OTHER, logout("%p\n", s)); /* PCI Vendor ID */ pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_INTEL); - /* PCI Device ID depends on device and is set below. */ - /* PCI Command */ - PCI_CONFIG_16(PCI_COMMAND, 0x0000); + /* PCI Device ID */ + pci_config_set_device_id(pci_conf, e100_device->device_id); /* PCI Status */ - PCI_CONFIG_16(PCI_STATUS, 0x2800); + pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_DEVSEL_MEDIUM | + PCI_STATUS_FAST_BACK); /* PCI Revision ID */ - PCI_CONFIG_8(PCI_REVISION_ID, 0x08); - /* PCI Class Code */ - PCI_CONFIG_8(0x09, 0x00); + pci_config_set_revision(pci_conf, e100_device->revision); pci_config_set_class(pci_conf, PCI_CLASS_NETWORK_ETHERNET); - /* PCI Cache Line Size */ - /* check cache line size!!! */ - //~ PCI_CONFIG_8(0x0c, 0x00); /* PCI Latency Timer */ - PCI_CONFIG_8(0x0d, 0x20); // latency timer = 32 clocks - /* PCI Header Type */ - /* BIST (built-in self test) */ -#if defined(TARGET_I386) -// !!! workaround for buggy bios -//~ #define PCI_BASE_ADDRESS_MEM_PREFETCH 0 -#endif -#if 0 - /* PCI Base Address Registers */ - /* CSR Memory Mapped Base Address */ - PCI_CONFIG_32(PCI_BASE_ADDRESS_0, - PCI_BASE_ADDRESS_SPACE_MEMORY | - PCI_BASE_ADDRESS_MEM_PREFETCH); - /* CSR I/O Mapped Base Address */ - PCI_CONFIG_32(PCI_BASE_ADDRESS_1, PCI_BASE_ADDRESS_SPACE_IO); -#if 0 - /* Flash Memory Mapped Base Address */ - PCI_CONFIG_32(PCI_BASE_ADDRESS_2, - 0xfffe0000 | PCI_BASE_ADDRESS_SPACE_MEMORY); -#endif -#endif - /* Expansion ROM Base Address (depends on boot disable!!!) */ - PCI_CONFIG_32(0x30, 0x00000000); - /* Capability Pointer */ - PCI_CONFIG_8(0x34, 0xdc); + pci_set_byte(pci_conf + PCI_LATENCY_TIMER, 0x20); /* latency timer = 32 clocks */ + /* Capability Pointer is set by PCI framework. */ /* Interrupt Line */ /* Interrupt Pin */ - PCI_CONFIG_8(0x3d, 1); // interrupt pin 0 + pci_set_byte(pci_conf + PCI_INTERRUPT_PIN, 1); /* interrupt pin A */ /* Minimum Grant */ - PCI_CONFIG_8(0x3e, 0x08); + pci_set_byte(pci_conf + PCI_MIN_GNT, 0x08); /* Maximum Latency */ - PCI_CONFIG_8(0x3f, 0x18); + pci_set_byte(pci_conf + PCI_MAX_LAT, 0x18); + + s->stats_size = e100_device->stats_size; + s->has_extended_tcb_support = e100_device->has_extended_tcb_support; switch (device) { case i82550: - // TODO: check device id. - pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_INTEL_82551IT); - /* Revision ID: 0x0c, 0x0d, 0x0e. */ - PCI_CONFIG_8(PCI_REVISION_ID, 0x0e); - // TODO: check size of statistical counters. - s->stats_size = 80; - // TODO: check extended tcb support. - s->has_extended_tcb_support = 1; - break; case i82551: - pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_INTEL_82551IT); - /* Revision ID: 0x0f, 0x10. */ - PCI_CONFIG_8(PCI_REVISION_ID, 0x0f); - // TODO: check size of statistical counters. - s->stats_size = 80; - s->has_extended_tcb_support = 1; - break; case i82557A: - pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_INTEL_82557); - PCI_CONFIG_8(PCI_REVISION_ID, 0x01); - PCI_CONFIG_8(0x34, 0x00); - power_management = 0; - break; case i82557B: - pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_INTEL_82557); - PCI_CONFIG_8(PCI_REVISION_ID, 0x02); - PCI_CONFIG_8(0x34, 0x00); - power_management = 0; - break; case i82557C: - pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_INTEL_82557); - PCI_CONFIG_8(PCI_REVISION_ID, 0x03); - PCI_CONFIG_8(0x34, 0x00); - power_management = 0; - break; case i82558A: - pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_INTEL_82557); - PCI_CONFIG_16(PCI_STATUS, 0x0290); - PCI_CONFIG_8(PCI_REVISION_ID, 0x04); - s->stats_size = 76; - s->has_extended_tcb_support = 1; - break; case i82558B: - pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_INTEL_82557); - PCI_CONFIG_16(PCI_STATUS, 0x0290); - PCI_CONFIG_8(PCI_REVISION_ID, 0x05); - s->stats_size = 76; - s->has_extended_tcb_support = 1; - break; case i82559A: - pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_INTEL_82557); - PCI_CONFIG_16(PCI_STATUS, 0x0290); - PCI_CONFIG_8(PCI_REVISION_ID, 0x06); - s->stats_size = 80; - s->has_extended_tcb_support = 1; - break; case i82559B: - pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_INTEL_82557); - PCI_CONFIG_16(PCI_STATUS, 0x0290); - PCI_CONFIG_8(PCI_REVISION_ID, 0x07); - s->stats_size = 80; - s->has_extended_tcb_support = 1; + case i82559ER: + case i82562: + case i82801: break; case i82559C: - pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_INTEL_82557); - PCI_CONFIG_16(PCI_STATUS, 0x0290); - PCI_CONFIG_8(PCI_REVISION_ID, 0x08); - // TODO: Windows wants revision id 0x0c. - PCI_CONFIG_8(PCI_REVISION_ID, 0x0c); #if EEPROM_SIZE > 0 - PCI_CONFIG_16(PCI_SUBSYSTEM_VENDOR_ID, 0x8086); - PCI_CONFIG_16(PCI_SUBSYSTEM_ID, 0x0040); + pci_set_word(pci_conf + PCI_SUBSYSTEM_VENDOR_ID, PCI_VENDOR_ID_INTEL); + pci_set_word(pci_conf + PCI_SUBSYSTEM_ID, 0x0040); #endif - s->stats_size = 80; - s->has_extended_tcb_support = 1; - break; - case i82559ER: - pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_INTEL_82551IT); - PCI_CONFIG_16(PCI_STATUS, 0x0290); - PCI_CONFIG_8(PCI_REVISION_ID, 0x09); - s->stats_size = 80; - s->has_extended_tcb_support = 1; - break; - case i82562: - // TODO: check device id. - pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_INTEL_82551IT); - /* TODO: wrong revision id. */ - PCI_CONFIG_8(PCI_REVISION_ID, 0x0e); - s->stats_size = 80; - s->has_extended_tcb_support = 1; break; default: logout("Device %X is undefined!\n", device); } + /* Standard TxCB. */ + s->configuration[6] |= BIT(4); + + /* Standard statistical counters. */ s->configuration[6] |= BIT(5); if (s->stats_size == 80) { @@ -578,26 +538,33 @@ } assert(s->stats_size > 0 && s->stats_size <= sizeof(s->statistics)); - if (power_management) { + if (e100_device->power_management) { /* Power Management Capabilities */ - PCI_CONFIG_8(0xdc, 0x01); - /* Next Item Pointer */ - /* Capability ID */ - PCI_CONFIG_16(0xde, 0x7e21); + int cfg_offset = 0xdc; + int r = pci_add_capability(&s->dev, PCI_CAP_ID_PM, + cfg_offset, PCI_PM_SIZEOF); + assert(r >= 0); + pci_set_word(pci_conf + cfg_offset + PCI_PM_PMC, 0x7e21); +#if 0 /* TODO: replace dummy code for power management emulation. */ /* TODO: Power Management Control / Status. */ + pci_set_word(pci_conf + cfg_offset + PCI_PM_CTRL, 0x0000); /* TODO: Ethernet Power Consumption Registers (i82559 and later). */ + pci_set_byte(pci_conf + cfg_offset + PCI_PM_PPB_EXTENSIONS, 0x0000); +#endif } #if EEPROM_SIZE > 0 if (device == i82557C || device == i82558B || device == i82559C) { - // TODO: get vendor id from EEPROM for i82557C or later. - // TODO: get device id from EEPROM for i82557C or later. - // TODO: status bit 4 can be disabled by EEPROM for i82558, i82559. - // TODO: header type is determined by EEPROM for i82559. - // TODO: get subsystem id from EEPROM for i82557C or later. - // TODO: get subsystem vendor id from EEPROM for i82557C or later. - // TODO: exp. rom baddr depends on a bit in EEPROM for i82558 or later. - // TODO: capability pointer depends on EEPROM for i82558. + /* + TODO: get vendor id from EEPROM for i82557C or later. + TODO: get device id from EEPROM for i82557C or later. + TODO: status bit 4 can be disabled by EEPROM for i82558, i82559. + TODO: header type is determined by EEPROM for i82559. + TODO: get subsystem id from EEPROM for i82557C or later. + TODO: get subsystem vendor id from EEPROM for i82557C or later. + TODO: exp. rom baddr depends on a bit in EEPROM for i82558 or later. + TODO: capability pointer depends on EEPROM for i82558. + */ logout("Get device id and revision from EEPROM!!!\n"); } #endif /* EEPROM_SIZE > 0 */ @@ -607,11 +574,14 @@ { size_t i; uint16_t *eeprom_contents = eeprom93xx_data(s->eeprom); - //~ eeprom93xx_reset(s->eeprom); +#if 0 + eeprom93xx_reset(s->eeprom); +#endif memcpy(eeprom_contents, s->conf.macaddr.a, 6); - eeprom_contents[0xa] = 0x4000; + eeprom_contents[EEPROM_ID] = EEPROM_ID_VALID; if (s->device == i82557B || s->device == i82557C) eeprom_contents[5] = 0x0100; + eeprom_contents[EEPROM_PHY_ID] = 1; uint16_t sum = 0; for (i = 0; i < EEPROM_SIZE - 1; i++) { sum += eeprom_contents[i]; @@ -631,6 +601,8 @@ { EEPRO100State *s = opaque; TRACE(OTHER, logout("%p\n", s)); + /* TODO: Clearing of hash register for selective reset, too? */ + memset(&s->mult[0], 0, sizeof(s->mult)); nic_selective_reset(s); } @@ -663,21 +635,6 @@ } #endif /* DEBUG_EEPRO100 */ -#if 0 -static uint16_t eepro100_read_status(EEPRO100State * s) -{ - uint16_t val = s->status; - TRACE(OTHER, logout("val=0x%04x\n", val)); - return val; -} - -static void eepro100_write_status(EEPRO100State * s, uint16_t val) -{ - TRACE(OTHER, logout("val=0x%04x\n", val)); - s->status = val; -} -#endif - /***************************************************************************** * * Command emulation. @@ -688,7 +645,7 @@ static uint16_t eepro100_read_command(EEPRO100State * s) { uint16_t val = 0xffff; - //~ TRACE(OTHER, logout("val=0x%04x\n", val)); + TRACE(OTHER, logout("val=0x%04x\n", val)); return val; } #endif @@ -712,22 +669,22 @@ static cu_state_t get_cu_state(EEPRO100State * s) { - return ((s->mem[SCBStatus] >> 6) & 0x03); + return ((s->mem[SCBStatus] & BITS(7, 6)) >> 6); } static void set_cu_state(EEPRO100State * s, cu_state_t state) { - s->mem[SCBStatus] = (s->mem[SCBStatus] & 0x3f) + (state << 6); + s->mem[SCBStatus] = (s->mem[SCBStatus] & ~BITS(7, 6)) + (state << 6); } static ru_state_t get_ru_state(EEPRO100State * s) { - return ((s->mem[SCBStatus] >> 2) & 0x0f); + return ((s->mem[SCBStatus] & BITS(5, 2)) >> 2); } static void set_ru_state(EEPRO100State * s, ru_state_t state) { - s->mem[SCBStatus] = (s->mem[SCBStatus] & 0xc3) + (state << 2); + s->mem[SCBStatus] = (s->mem[SCBStatus] & ~BITS(5, 2)) + (state << 2); } static void dump_statistics(EEPRO100State * s) @@ -743,149 +700,196 @@ stl_le_phys(s->statsaddr + 36, s->statistics.rx_good_frames); stl_le_phys(s->statsaddr + 48, s->statistics.rx_resource_errors); stl_le_phys(s->statsaddr + 60, s->statistics.rx_short_frame_errors); - //~ stw_le_phys(s->statsaddr + 76, s->statistics.xmt_tco_frames); - //~ stw_le_phys(s->statsaddr + 78, s->statistics.rcv_tco_frames); - //~ missing("CU dump statistical counters"); +#if 0 + stw_le_phys(s->statsaddr + 76, s->statistics.xmt_tco_frames); + stw_le_phys(s->statsaddr + 78, s->statistics.rcv_tco_frames); + missing("CU dump statistical counters"); +#endif +} + +static void read_cb(EEPRO100State *s) +{ + cpu_physical_memory_read(s->cb_address, (uint8_t *) &s->tx, sizeof(s->tx)); + s->tx.status = le16_to_cpu(s->tx.status); + s->tx.command = le16_to_cpu(s->tx.command); + s->tx.link = le32_to_cpu(s->tx.link); + s->tx.tbd_array_addr = le32_to_cpu(s->tx.tbd_array_addr); + s->tx.tcb_bytes = le16_to_cpu(s->tx.tcb_bytes); +} + +static void tx_command(EEPRO100State *s) +{ + uint32_t tbd_array = le32_to_cpu(s->tx.tbd_array_addr); + uint16_t tcb_bytes = (le16_to_cpu(s->tx.tcb_bytes) & 0x3fff); + /* Sends larger than MAX_ETH_FRAME_SIZE are allowed, up to 2600 bytes. */ + uint8_t buf[2600]; + uint16_t size = 0; + uint32_t tbd_address = s->cb_address + 0x10; + TRACE(RXTX, logout + ("transmit, TBD array address 0x%08x, TCB byte count 0x%04x, TBD count %u\n", + tbd_array, tcb_bytes, s->tx.tbd_count)); + + if (tcb_bytes > 2600) { + logout("TCB byte count too large, using 2600\n"); + tcb_bytes = 2600; + } + if (!((tcb_bytes > 0) || (tbd_array != 0xffffffff))) { + logout + ("illegal values of TBD array address and TCB byte count!\n"); + } + assert(tcb_bytes <= sizeof(buf)); + while (size < tcb_bytes) { + uint32_t tx_buffer_address = ldl_phys(tbd_address); + uint16_t tx_buffer_size = lduw_phys(tbd_address + 4); +#if 0 + uint16_t tx_buffer_el = lduw_phys(tbd_address + 6); +#endif + tbd_address += 8; + TRACE(RXTX, logout + ("TBD (simplified mode): buffer address 0x%08x, size 0x%04x\n", + tx_buffer_address, tx_buffer_size)); + tx_buffer_size = MIN(tx_buffer_size, sizeof(buf) - size); + cpu_physical_memory_read(tx_buffer_address, &buf[size], + tx_buffer_size); + size += tx_buffer_size; + } + if (tbd_array == 0xffffffff) { + /* Simplified mode. Was already handled by code above. */ + } else { + /* Flexible mode. */ + uint8_t tbd_count = 0; + if (s->has_extended_tcb_support && !(s->configuration[6] & BIT(4))) { + /* Extended Flexible TCB. */ + for (; tbd_count < 2; tbd_count++) { + uint32_t tx_buffer_address = ldl_phys(tbd_address); + uint16_t tx_buffer_size = lduw_phys(tbd_address + 4); + uint16_t tx_buffer_el = lduw_phys(tbd_address + 6); + tbd_address += 8; + TRACE(RXTX, logout + ("TBD (extended flexible mode): buffer address 0x%08x, size 0x%04x\n", + tx_buffer_address, tx_buffer_size)); + tx_buffer_size = MIN(tx_buffer_size, sizeof(buf) - size); + cpu_physical_memory_read(tx_buffer_address, &buf[size], + tx_buffer_size); + size += tx_buffer_size; + if (tx_buffer_el & 1) { + break; + } + } + } + tbd_address = tbd_array; + for (; tbd_count < s->tx.tbd_count; tbd_count++) { + uint32_t tx_buffer_address = ldl_phys(tbd_address); + uint16_t tx_buffer_size = lduw_phys(tbd_address + 4); + uint16_t tx_buffer_el = lduw_phys(tbd_address + 6); + tbd_address += 8; + TRACE(RXTX, logout + ("TBD (flexible mode): buffer address 0x%08x, size 0x%04x\n", + tx_buffer_address, tx_buffer_size)); + tx_buffer_size = MIN(tx_buffer_size, sizeof(buf) - size); + cpu_physical_memory_read(tx_buffer_address, &buf[size], + tx_buffer_size); + size += tx_buffer_size; + if (tx_buffer_el & 1) { + break; + } + } + } + TRACE(RXTX, logout("%p sending frame, len=%d,%s\n", s, size, nic_dump(buf, size))); + qemu_send_packet(&s->nic->nc, buf, size); + s->statistics.tx_good_frames++; + /* Transmit with bad status would raise an CX/TNO interrupt. + * (82557 only). Emulation never has bad status. */ +#if 0 + eepro100_cx_interrupt(s); +#endif +} + +static void set_multicast_list(EEPRO100State *s) +{ + uint16_t multicast_count = s->tx.tbd_array_addr & BITS(13, 0); + uint16_t i; + memset(&s->mult[0], 0, sizeof(s->mult)); + TRACE(OTHER, logout("multicast list, multicast count = %u\n", multicast_count)); + for (i = 0; i < multicast_count; i += 6) { + uint8_t multicast_addr[6]; + cpu_physical_memory_read(s->cb_address + 10 + i, multicast_addr, 6); + TRACE(OTHER, logout("multicast entry %s\n", nic_dump(multicast_addr, 6))); + unsigned mcast_idx = compute_mcast_idx(multicast_addr); + assert(mcast_idx < 64); + s->mult[mcast_idx >> 3] |= (1 << (mcast_idx & 7)); + } } static void action_command(EEPRO100State *s) { for (;;) { - uint32_t cb_address = s->cu_base + s->cu_offset; - eepro100_tx_t tx; - cpu_physical_memory_read(cb_address, (uint8_t *) & tx, sizeof(tx)); - uint16_t status = le16_to_cpu(tx.status); - uint16_t command = le16_to_cpu(tx.command); - logout - ("val=0x%02x (cu start), status=0x%04x, command=0x%04x, link=0x%08x\n", - val, status, command, tx.link); - bool bit_el = ((command & 0x8000) != 0); - bool bit_s = ((command & 0x4000) != 0); - bool bit_i = ((command & 0x2000) != 0); - bool bit_nc = ((command & 0x0010) != 0); - bool success = true; - //~ bool bit_sf = ((command & 0x0008) != 0); - uint16_t cmd = command & 0x0007; - s->cu_offset = le32_to_cpu(tx.link); - switch (cmd) { + bool bit_el; + bool bit_s; + bool bit_i; + bool bit_nc; + uint16_t ok_status = STATUS_OK; + s->cb_address = s->cu_base + s->cu_offset; + read_cb(s); + bit_el = ((s->tx.command & COMMAND_EL) != 0); + bit_s = ((s->tx.command & COMMAND_S) != 0); + bit_i = ((s->tx.command & COMMAND_I) != 0); + bit_nc = ((s->tx.command & COMMAND_NC) != 0); +#if 0 + bool bit_sf = ((s->tx.command & COMMAND_SF) != 0); +#endif + s->cu_offset = s->tx.link; + TRACE(OTHER, + logout("val=(cu start), status=0x%04x, command=0x%04x, link=0x%08x\n", + s->tx.status, s->tx.command, s->tx.link)); + switch (s->tx.command & COMMAND_CMD) { case CmdNOp: /* Do nothing. */ break; case CmdIASetup: - cpu_physical_memory_read(cb_address + 8, &s->conf.macaddr.a[0], 6); - TRACE(OTHER, logout("macaddr: %s\n", nic_dump(&s->macaddr[0], 6))); + cpu_physical_memory_read(s->cb_address + 8, &s->conf.macaddr.a[0], 6); + TRACE(OTHER, logout("macaddr: %s\n", nic_dump(&s->conf.macaddr.a[0], 6))); break; case CmdConfigure: - cpu_physical_memory_read(cb_address + 8, &s->configuration[0], + cpu_physical_memory_read(s->cb_address + 8, &s->configuration[0], sizeof(s->configuration)); - TRACE(OTHER, logout("configuration: %s\n", nic_dump(&s->configuration[0], 16))); + TRACE(OTHER, logout("configuration: %s\n", + nic_dump(&s->configuration[0], 16))); + TRACE(OTHER, logout("configuration: %s\n", + nic_dump(&s->configuration[16], + ARRAY_SIZE(s->configuration) - 16))); + if (s->configuration[20] & BIT(6)) { + TRACE(OTHER, logout("Multiple IA bit\n")); + } break; case CmdMulticastList: - //~ missing("multicast list"); + set_multicast_list(s); break; case CmdTx: - (void)0; - uint32_t tbd_array = le32_to_cpu(tx.tx_desc_addr); - uint16_t tcb_bytes = (le16_to_cpu(tx.tcb_bytes) & 0x3fff); - TRACE(RXTX, logout - ("transmit, TBD array address 0x%08x, TCB byte count 0x%04x, TBD count %u\n", - tbd_array, tcb_bytes, tx.tbd_count)); - if (bit_nc) { missing("CmdTx: NC = 0"); - success = false; + ok_status = 0; break; } - //~ assert(!bit_sf); - if (tcb_bytes > 2600) { - logout("TCB byte count too large, using 2600\n"); - tcb_bytes = 2600; - } - /* Next assertion fails for local configuration. */ - //~ assert((tcb_bytes > 0) || (tbd_array != 0xffffffff)); - if (!((tcb_bytes > 0) || (tbd_array != 0xffffffff))) { - logout - ("illegal values of TBD array address and TCB byte count!\n"); - } - // sends larger than MAX_ETH_FRAME_SIZE are allowed, up to 2600 bytes - uint8_t buf[2600]; - uint16_t size = 0; - uint32_t tbd_address = cb_address + 0x10; - assert(tcb_bytes <= sizeof(buf)); - while (size < tcb_bytes) { - uint32_t tx_buffer_address = ldl_phys(tbd_address); - uint16_t tx_buffer_size = lduw_phys(tbd_address + 4); - //~ uint16_t tx_buffer_el = lduw_phys(tbd_address + 6); - tbd_address += 8; - TRACE(RXTX, logout - ("TBD (simplified mode): buffer address 0x%08x, size 0x%04x\n", - tx_buffer_address, tx_buffer_size)); - tx_buffer_size = MIN(tx_buffer_size, sizeof(buf) - size); - cpu_physical_memory_read(tx_buffer_address, &buf[size], - tx_buffer_size); - size += tx_buffer_size; - } - if (tbd_array == 0xffffffff) { - /* Simplified mode. Was already handled by code above. */ - } else { - /* Flexible mode. */ - uint8_t tbd_count = 0; - if (s->has_extended_tcb_support && !(s->configuration[6] & BIT(4))) { - /* Extended Flexible TCB. */ - for (; tbd_count < 2; tbd_count++) { - uint32_t tx_buffer_address = ldl_phys(tbd_address); - uint16_t tx_buffer_size = lduw_phys(tbd_address + 4); - uint16_t tx_buffer_el = lduw_phys(tbd_address + 6); - tbd_address += 8; - TRACE(RXTX, logout - ("TBD (extended flexible mode): buffer address 0x%08x, size 0x%04x\n", - tx_buffer_address, tx_buffer_size)); - tx_buffer_size = MIN(tx_buffer_size, sizeof(buf) - size); - cpu_physical_memory_read(tx_buffer_address, &buf[size], - tx_buffer_size); - size += tx_buffer_size; - if (tx_buffer_el & 1) { - break; - } - } - } - tbd_address = tbd_array; - for (; tbd_count < tx.tbd_count; tbd_count++) { - uint32_t tx_buffer_address = ldl_phys(tbd_address); - uint16_t tx_buffer_size = lduw_phys(tbd_address + 4); - uint16_t tx_buffer_el = lduw_phys(tbd_address + 6); - tbd_address += 8; - TRACE(RXTX, logout - ("TBD (flexible mode): buffer address 0x%08x, size 0x%04x\n", - tx_buffer_address, tx_buffer_size)); - tx_buffer_size = MIN(tx_buffer_size, sizeof(buf) - size); - cpu_physical_memory_read(tx_buffer_address, &buf[size], - tx_buffer_size); - size += tx_buffer_size; - if (tx_buffer_el & 1) { - break; - } - } - } - TRACE(RXTX, logout("%p sending frame, len=%d,%s\n", s, size, nic_dump(buf, size))); - qemu_send_packet(&s->nic->nc, buf, size); - s->statistics.tx_good_frames++; - /* Transmit with bad status would raise an CX/TNO interrupt. - * (82557 only). Emulation never has bad status. */ - //~ eepro100_cx_interrupt(s); + tx_command(s); break; case CmdTDR: TRACE(OTHER, logout("load microcode\n")); /* Starting with offset 8, the command contains * 64 dwords microcode which we just ignore here. */ break; + case CmdDiagnose: + TRACE(OTHER, logout("diagnose\n")); + /* Make sure error flag is not set. */ + s->tx.status = 0; + break; default: missing("undefined command"); - success = false; + ok_status = 0; break; } /* Write new status. */ - stw_phys(cb_address, status | 0x8000 | (success ? 0x2000 : 0)); + stw_phys(s->cb_address, s->tx.status | ok_status | STATUS_C); if (bit_i) { /* CU completed action. */ eepro100_cx_interrupt(s); @@ -911,17 +915,17 @@ static void eepro100_cu_command(EEPRO100State * s, uint8_t val) { + cu_state_t cu_state; switch (val) { case CU_NOP: /* No operation. */ break; case CU_START: - if (get_cu_state(s) != cu_idle) { - /* Intel documentation says that CU must be idle for the CU - * start command. Intel driver for Linux also starts the CU - * from suspended state. */ - logout("CU state is %u, should be %u\n", get_cu_state(s), cu_idle); - //~ assert(!"wrong CU state"); + cu_state = get_cu_state(s); + if (cu_state != cu_idle && cu_state != cu_suspended) { + /* Intel documentation says that CU must be idle or suspended + * for the CU start command. */ + logout("unexpected CU state is %u\n", cu_state); } set_cu_state(s, cu_active); s->cu_offset = s->pointer; @@ -932,7 +936,9 @@ logout("bad CU resume from CU state %u\n", get_cu_state(s)); /* Workaround for bad Linux eepro100 driver which resumes * from idle state. */ - //~ missing("cu resume"); +#if 0 + missing("cu resume"); +#endif set_cu_state(s, cu_suspended); } if (get_cu_state(s) == cu_suspended) { @@ -983,7 +989,9 @@ /* RU start. */ if (get_ru_state(s) != ru_idle) { logout("RU state is %u, should be %u\n", get_ru_state(s), ru_idle); - //~ assert(!"wrong RU state"); +#if 0 + assert(!"wrong RU state"); +#endif } set_ru_state(s, ru_ready); s->ru_offset = s->pointer; @@ -994,10 +1002,19 @@ if (get_ru_state(s) != ru_suspended) { logout("RU state is %u, should be %u\n", get_ru_state(s), ru_suspended); - //~ assert(!"wrong RU state"); +#if 0 + assert(!"wrong RU state"); +#endif } set_ru_state(s, ru_ready); break; + case RU_ABORT: + /* RU abort. */ + if (get_ru_state(s) == ru_ready) { + eepro100_rnr_interrupt(s); + } + set_ru_state(s, ru_idle); + break; case RX_ADDR_LOAD: /* Load RU base. */ TRACE(OTHER, logout("val=0x%02x (RU base address)\n", val)); @@ -1049,7 +1066,9 @@ TRACE(EEPROM, logout("val=0x%02x\n", val)); /* mask unwriteable bits */ - //~ val = SET_MASKED(val, 0x31, eeprom->value); +#if 0 + val = SET_MASKED(val, 0x31, eeprom->value); +#endif int eecs = ((val & EEPROM_CS) != 0); int eesk = ((val & EEPROM_SK) != 0); @@ -1131,7 +1150,9 @@ val, raiseint, mdi_op_name[opcode], phy, reg2name(reg), data)); if (phy != 1) { /* Unsupported PHY address. */ - //~ logout("phy must be 1 but is %u\n", phy); +#if 0 + logout("phy must be 1 but is %u\n", phy); +#endif data = 0; } else if (opcode != 1 && opcode != 2) { /* Unsupported opcode. */ @@ -1270,23 +1291,21 @@ static uint8_t eepro100_read1(EEPRO100State * s, uint32_t addr) { - uint8_t val; + uint8_t val = 0; if (addr <= sizeof(s->mem) - sizeof(val)) { memcpy(&val, &s->mem[addr], sizeof(val)); } switch (addr) { case SCBStatus: - //~ val = eepro100_read_status(s); - TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); - break; case SCBAck: - //~ val = eepro100_read_status(s); TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); break; case SCBCmd: TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); - //~ val = eepro100_read_command(s); +#if 0 + val = eepro100_read_command(s); +#endif break; case SCBIntmask: TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); @@ -1297,11 +1316,11 @@ case SCBeeprom: val = eepro100_read_eeprom(s); break; - case 0x1b: /* PMDR (power management driver register) */ + case SCBpmdr: /* Power Management Driver Register */ val = 0; TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); break; - case 0x1d: /* general status register */ + case SCBgstat: /* General Status Register */ /* 100 Mbps full duplex, valid link */ val = 0x07; TRACE(OTHER, logout("addr=General Status val=%02x\n", val)); @@ -1315,14 +1334,13 @@ static uint16_t eepro100_read2(EEPRO100State * s, uint32_t addr) { - uint16_t val; + uint16_t val = 0; if (addr <= sizeof(s->mem) - sizeof(val)) { memcpy(&val, &s->mem[addr], sizeof(val)); } switch (addr) { case SCBStatus: - //~ val = eepro100_read_status(s); case SCBCmd: TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val)); break; @@ -1339,18 +1357,19 @@ static uint32_t eepro100_read4(EEPRO100State * s, uint32_t addr) { - uint32_t val; + uint32_t val = 0; if (addr <= sizeof(s->mem) - sizeof(val)) { memcpy(&val, &s->mem[addr], sizeof(val)); } switch (addr) { case SCBStatus: - //~ val = eepro100_read_status(s); TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val)); break; case SCBPointer: - //~ val = eepro100_read_pointer(s); +#if 0 + val = eepro100_read_pointer(s); +#endif TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val)); break; case SCBPort: @@ -1369,7 +1388,8 @@ static void eepro100_write1(EEPRO100State * s, uint32_t addr, uint8_t val) { - if (addr <= sizeof(s->mem) - sizeof(val)) { + /* SCBStatus is readonly. */ + if (addr > SCBStatus && addr <= sizeof(s->mem) - sizeof(val)) { memcpy(&s->mem[addr], &val, sizeof(val)); } @@ -1377,7 +1397,6 @@ switch (addr) { case SCBStatus: - //~ eepro100_write_status(s, val); break; case SCBAck: eepro100_acknowledge(s); @@ -1395,7 +1414,7 @@ case SCBFlow: /* does not exist on 82557 */ case SCBFlow + 1: case SCBFlow + 2: - case SCBFlow + 3: + case SCBpmdr: /* does not exist on 82557 */ TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val)); break; case SCBeeprom: @@ -1409,7 +1428,8 @@ static void eepro100_write2(EEPRO100State * s, uint32_t addr, uint16_t val) { - if (addr <= sizeof(s->mem) - sizeof(val)) { + /* SCBStatus is readonly. */ + if (addr > SCBStatus && addr <= sizeof(s->mem) - sizeof(val)) { memcpy(&s->mem[addr], &val, sizeof(val)); } @@ -1417,7 +1437,7 @@ switch (addr) { case SCBStatus: - //~ eepro100_write_status(s, val); + s->mem[SCBAck] = (val >> 8); eepro100_acknowledge(s); break; case SCBCmd: @@ -1465,7 +1485,9 @@ static uint32_t ioport_read1(void *opaque, uint32_t addr) { EEPRO100State *s = opaque; - //~ logout("addr=%s\n", regname(addr)); +#if 0 + logout("addr=%s\n", regname(addr)); +#endif return eepro100_read1(s, addr - s->region[1]); } @@ -1484,7 +1506,9 @@ static void ioport_write1(void *opaque, uint32_t addr, uint32_t val) { EEPRO100State *s = opaque; - //~ logout("addr=%s val=0x%02x\n", regname(addr), val); +#if 0 + logout("addr=%s val=0x%02x\n", regname(addr), val); +#endif eepro100_write1(s, addr - s->region[1], val); } @@ -1532,42 +1556,54 @@ static void pci_mmio_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) { EEPRO100State *s = opaque; - //~ logout("addr=%s val=0x%02x\n", regname(addr), val); +#if 0 + logout("addr=%s val=0x%02x\n", regname(addr), val); +#endif eepro100_write1(s, addr, val); } static void pci_mmio_writew(void *opaque, target_phys_addr_t addr, uint32_t val) { EEPRO100State *s = opaque; - //~ logout("addr=%s val=0x%02x\n", regname(addr), val); +#if 0 + logout("addr=%s val=0x%02x\n", regname(addr), val); +#endif eepro100_write2(s, addr, val); } static void pci_mmio_writel(void *opaque, target_phys_addr_t addr, uint32_t val) { EEPRO100State *s = opaque; - //~ logout("addr=%s val=0x%02x\n", regname(addr), val); +#if 0 + logout("addr=%s val=0x%02x\n", regname(addr), val); +#endif eepro100_write4(s, addr, val); } static uint32_t pci_mmio_readb(void *opaque, target_phys_addr_t addr) { EEPRO100State *s = opaque; - //~ logout("addr=%s\n", regname(addr)); +#if 0 + logout("addr=%s\n", regname(addr)); +#endif return eepro100_read1(s, addr); } static uint32_t pci_mmio_readw(void *opaque, target_phys_addr_t addr) { EEPRO100State *s = opaque; - //~ logout("addr=%s\n", regname(addr)); +#if 0 + logout("addr=%s\n", regname(addr)); +#endif return eepro100_read2(s, addr); } static uint32_t pci_mmio_readl(void *opaque, target_phys_addr_t addr) { EEPRO100State *s = opaque; - //~ logout("addr=%s\n", regname(addr)); +#if 0 + logout("addr=%s\n", regname(addr)); +#endif return eepro100_read4(s, addr); } @@ -1592,11 +1628,11 @@ "size=0x%08"FMT_PCIBUS", type=%d\n", region_num, addr, size, type)); - if (region_num == 0) { - /* Map control / status registers. */ - cpu_register_physical_memory(addr, size, s->mmio_index); - s->region[region_num] = addr; - } + assert(region_num == 0 || region_num == 2); + + /* Map control / status registers and flash. */ + cpu_register_physical_memory(addr, size, s->mmio_index); + s->region[region_num] = addr; } static int nic_can_receive(VLANClientState *nc) @@ -1604,7 +1640,9 @@ EEPRO100State *s = DO_UPCAST(NICState, nc, nc)->opaque; TRACE(RXTX, logout("%p\n", s)); return get_ru_state(s) == ru_ready; - //~ return !eepro100_buffer_full(s); +#if 0 + return !eepro100_buffer_full(s); +#endif } static ssize_t nic_receive(VLANClientState *nc, const uint8_t * buf, size_t size) @@ -1618,28 +1656,24 @@ static const uint8_t broadcast_macaddr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; - /* TODO: check multiple IA bit. */ - if (s->configuration[20] & BIT(6)) { - missing("Multiple IA bit"); - return -1; - } - if (s->configuration[8] & 0x80) { /* CSMA is disabled. */ logout("%p received while CSMA is disabled\n", s); return -1; - } else if (size < 64 && (s->configuration[7] & 1)) { + } else if (size < 64 && (s->configuration[7] & BIT(0))) { /* Short frame and configuration byte 7/0 (discard short receive) set: * Short frame is discarded */ logout("%p received short frame (%zu byte)\n", s, size); s->statistics.rx_short_frame_errors++; - //~ return -1; - } else if ((size > MAX_ETH_FRAME_SIZE + 4) && !(s->configuration[18] & 8)) { +#if 0 + return -1; +#endif + } else if ((size > MAX_ETH_FRAME_SIZE + 4) && !(s->configuration[18] & BIT(3))) { /* Long frame and configuration byte 18/3 (long receive ok) not set: * Long frames are discarded. */ logout("%p received long frame (%zu byte), ignored\n", s, size); return -1; - } else if (memcmp(buf, s->conf.macaddr.a, 6) == 0) { // !!! + } else if (memcmp(buf, s->conf.macaddr.a, 6) == 0) { /* !!! */ /* Frame matches individual address. */ /* TODO: check configuration byte 15/4 (ignore U/L). */ TRACE(RXTX, logout("%p received frame for me, len=%zu\n", s, size)); @@ -1647,22 +1681,40 @@ /* Broadcast frame. */ TRACE(RXTX, logout("%p received broadcast, len=%zu\n", s, size)); rfd_status |= 0x0002; - } else if (buf[0] & 0x01) { // !!! + } else if (buf[0] & 0x01) { /* Multicast frame. */ - TRACE(RXTX, logout("%p received multicast, len=%zu\n", s, size)); - /* TODO: check multicast all bit. */ + TRACE(RXTX, logout("%p received multicast, len=%zu,%s\n", s, size, nic_dump(buf, size))); if (s->configuration[21] & BIT(3)) { - missing("Multicast All bit"); - } - int mcast_idx = compute_mcast_idx(buf); - if (!(s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7)))) { - return size; + /* Multicast all bit is set, receive all multicast frames. */ + } else { + unsigned mcast_idx = compute_mcast_idx(buf); + assert(mcast_idx < 64); + if (s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7))) { + /* Multicast frame is allowed in hash table. */ + } else if (s->configuration[15] & BIT(0)) { + /* Promiscuous: receive all. */ + rfd_status |= 0x0004; + } else { + TRACE(RXTX, logout("%p multicast ignored\n", s)); + return -1; + } } + /* TODO: Next not for promiscuous mode? */ rfd_status |= 0x0002; - } else if (s->configuration[15] & 1) { + } else if (s->configuration[15] & BIT(0)) { /* Promiscuous: receive all. */ TRACE(RXTX, logout("%p received frame in promiscuous mode, len=%zu\n", s, size)); rfd_status |= 0x0004; + } else if (s->configuration[20] & BIT(6)) { + /* Multiple IA bit set. */ + unsigned mcast_idx = compute_mcast_idx(buf); + assert(mcast_idx < 64); + if (s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7))) { + TRACE(RXTX, logout("%p accepted, multiple IA bit set\n", s)); + } else { + TRACE(RXTX, logout("%p frame ignored, multiple IA bit set\n", s)); + return -1; + } } else { TRACE(RXTX, logout("%p received frame, ignored, len=%zu,%s\n", s, size, nic_dump(buf, size))); @@ -1672,12 +1724,15 @@ if (get_ru_state(s) != ru_ready) { /* No resources available. */ logout("no resources, state=%u\n", get_ru_state(s)); + /* TODO: RNR interrupt only at first failed frame? */ + eepro100_rnr_interrupt(s); s->statistics.rx_resource_errors++; - //~ assert(!"no resources"); +#if 0 + assert(!"no resources"); +#endif return -1; } - //~ !!! -//~ $3 = {status = 0x0, command = 0xc000, link = 0x2d220, rx_buf_addr = 0x207dc, count = 0x0, size = 0x5f8, packet = {0x0 }} + /* !!! */ eepro100_rx_t rx; cpu_physical_memory_read(s->ru_base + s->ru_offset, (uint8_t *) & rx, offsetof(eepro100_rx_t, packet)); @@ -1698,25 +1753,29 @@ rfd_status); stw_phys(s->ru_base + s->ru_offset + offsetof(eepro100_rx_t, count), size); /* Early receive interrupt not supported. */ - //~ eepro100_er_interrupt(s); +#if 0 + eepro100_er_interrupt(s); +#endif /* Receive CRC Transfer not supported. */ - if (s->configuration[18] & 4) { + if (s->configuration[18] & BIT(2)) { missing("Receive CRC Transfer"); return -1; } /* TODO: check stripping enable bit. */ - //~ assert(!(s->configuration[17] & 1)); +#if 0 + assert(!(s->configuration[17] & BIT(0))); +#endif cpu_physical_memory_write(s->ru_base + s->ru_offset + offsetof(eepro100_rx_t, packet), buf, size); s->statistics.rx_good_frames++; eepro100_fr_interrupt(s); s->ru_offset = le32_to_cpu(rx.link); - if (rfd_command & 0x8000) { + if (rfd_command & COMMAND_EL) { /* EL bit is set, so this was the last frame. */ logout("receive: Running out of frames\n"); set_ru_state(s, ru_suspended); } - if (rfd_command & 0x4000) { + if (rfd_command & COMMAND_S) { /* S bit is set. */ set_ru_state(s, ru_suspended); } @@ -1770,9 +1829,6 @@ VMSTATE_UINT32(statistics.fc_rcv_unsupported, EEPRO100State), VMSTATE_UINT16(statistics.xmt_tco_frames, EEPRO100State), VMSTATE_UINT16(statistics.rcv_tco_frames, EEPRO100State), -#if 0 - VMSTATE_UINT16(status, EEPRO100State), -#endif /* Configuration bytes. */ VMSTATE_BUFFER(configuration, EEPRO100State), VMSTATE_END_OF_LIST() @@ -1791,8 +1847,8 @@ EEPRO100State *s = DO_UPCAST(EEPRO100State, dev, pci_dev); cpu_unregister_io_memory(s->mmio_index); - vmstate_unregister(s->vmstate, s); - eeprom93xx_free(s->eeprom); + vmstate_unregister(&pci_dev->qdev, s->vmstate, s); + eeprom93xx_free(&pci_dev->qdev, s->eeprom); qemu_del_vlan_client(&s->nic->nc); return 0; } @@ -1805,23 +1861,26 @@ .cleanup = nic_cleanup, }; -static int nic_init(PCIDevice *pci_dev, uint32_t device) +static int e100_nic_init(PCIDevice *pci_dev) { EEPRO100State *s = DO_UPCAST(EEPRO100State, dev, pci_dev); + E100PCIDeviceInfo *e100_device = DO_UPCAST(E100PCIDeviceInfo, pci.qdev, + pci_dev->qdev.info); TRACE(OTHER, logout("\n")); - s->device = device; + s->device = e100_device->device; - pci_reset(s); + e100_pci_reset(s, e100_device); /* Add 64 * 2 EEPROM. i82557 and i82558 support a 64 word EEPROM, * i82559 and later support 64 or 256 word EEPROM. */ - s->eeprom = eeprom93xx_new(EEPROM_SIZE); + s->eeprom = eeprom93xx_new(&pci_dev->qdev, EEPROM_SIZE); /* Handler for memory-mapped I/O */ s->mmio_index = - cpu_register_io_memory(pci_mmio_read, pci_mmio_write, s); + cpu_register_io_memory(pci_mmio_read, pci_mmio_write, s, + DEVICE_NATIVE_ENDIAN); pci_register_bar(&s->dev, 0, PCI_MEM_SIZE, PCI_BASE_ADDRESS_SPACE_MEMORY | @@ -1832,7 +1891,7 @@ pci_mmio_map); qemu_macaddr_default_if_unset(&s->conf.macaddr); - logout("macaddr: %s\n", nic_dump(&s->macaddr[0], 6)); + logout("macaddr: %s\n", nic_dump(&s->conf.macaddr.a[0], 6)); assert(s->region[1] == 0); nic_reset(s); @@ -1848,197 +1907,160 @@ s->vmstate = qemu_malloc(sizeof(vmstate_eepro100)); memcpy(s->vmstate, &vmstate_eepro100, sizeof(vmstate_eepro100)); s->vmstate->name = s->nic->nc.model; - vmstate_register(-1, s->vmstate, s); + vmstate_register(&pci_dev->qdev, -1, s->vmstate, s); - if (!pci_dev->qdev.hotplugged) { - static int loaded = 0; - if (!loaded) { - char fname[32]; - snprintf(fname, sizeof(fname), "pxe-%s.bin", s->nic->nc.model); - rom_add_option(fname); - loaded = 1; - } - } - return 0; -} + add_boot_device_path(s->conf.bootindex, &pci_dev->qdev, "/ethernet-phy@0"); -static int pci_i82550_init(PCIDevice *pci_dev) -{ - return nic_init(pci_dev, i82550); -} - -static int pci_i82551_init(PCIDevice *pci_dev) -{ - return nic_init(pci_dev, i82551); -} - -static int pci_i82557a_init(PCIDevice *pci_dev) -{ - return nic_init(pci_dev, i82557A); -} - -static int pci_i82557b_init(PCIDevice *pci_dev) -{ - return nic_init(pci_dev, i82557B); -} - -static int pci_i82557c_init(PCIDevice *pci_dev) -{ - return nic_init(pci_dev, i82557C); -} - -static int pci_i82558a_init(PCIDevice *pci_dev) -{ - return nic_init(pci_dev, i82558A); -} - -static int pci_i82558b_init(PCIDevice *pci_dev) -{ - return nic_init(pci_dev, i82558B); -} - -static int pci_i82559a_init(PCIDevice *pci_dev) -{ - return nic_init(pci_dev, i82559A); -} - -static int pci_i82559b_init(PCIDevice *pci_dev) -{ - return nic_init(pci_dev, i82559B); -} - -static int pci_i82559c_init(PCIDevice *pci_dev) -{ - return nic_init(pci_dev, i82559C); -} - -static int pci_i82559er_init(PCIDevice *pci_dev) -{ - return nic_init(pci_dev, i82559ER); -} - -static int pci_i82562_init(PCIDevice *pci_dev) -{ - return nic_init(pci_dev, i82562); + return 0; } -static PCIDeviceInfo eepro100_info[] = { +static E100PCIDeviceInfo e100_devices[] = { { - .qdev.name = "i82550", - .qdev.size = sizeof(EEPRO100State), - .init = pci_i82550_init, - .exit = pci_nic_uninit, - .qdev.props = (Property[]) { - DEFINE_NIC_PROPERTIES(EEPRO100State, conf), - DEFINE_PROP_END_OF_LIST(), - }, + .pci.qdev.name = "i82550", + .pci.qdev.desc = "Intel i82550 Ethernet", + .device = i82550, + /* TODO: check device id. */ + .device_id = PCI_DEVICE_ID_INTEL_82551IT, + /* Revision ID: 0x0c, 0x0d, 0x0e. */ + .revision = 0x0e, + /* TODO: check size of statistical counters. */ + .stats_size = 80, + /* TODO: check extended tcb support. */ + .has_extended_tcb_support = true, + .power_management = true, },{ - .qdev.name = "i82551", - .qdev.size = sizeof(EEPRO100State), - .init = pci_i82551_init, - .exit = pci_nic_uninit, - .qdev.props = (Property[]) { - DEFINE_NIC_PROPERTIES(EEPRO100State, conf), - DEFINE_PROP_END_OF_LIST(), - }, + .pci.qdev.name = "i82551", + .pci.qdev.desc = "Intel i82551 Ethernet", + .device = i82551, + .device_id = PCI_DEVICE_ID_INTEL_82551IT, + /* Revision ID: 0x0f, 0x10. */ + .revision = 0x0f, + /* TODO: check size of statistical counters. */ + .stats_size = 80, + .has_extended_tcb_support = true, + .power_management = true, },{ - .qdev.name = "i82557a", - .qdev.size = sizeof(EEPRO100State), - .init = pci_i82557a_init, - .exit = pci_nic_uninit, - .qdev.props = (Property[]) { - DEFINE_NIC_PROPERTIES(EEPRO100State, conf), - DEFINE_PROP_END_OF_LIST(), - }, + .pci.qdev.name = "i82557a", + .pci.qdev.desc = "Intel i82557A Ethernet", + .device = i82557A, + .device_id = PCI_DEVICE_ID_INTEL_82557, + .revision = 0x01, + .power_management = false, },{ - .qdev.name = "i82557b", - .qdev.size = sizeof(EEPRO100State), - .init = pci_i82557b_init, - .exit = pci_nic_uninit, - .qdev.props = (Property[]) { - DEFINE_NIC_PROPERTIES(EEPRO100State, conf), - DEFINE_PROP_END_OF_LIST(), - }, + .pci.qdev.name = "i82557b", + .pci.qdev.desc = "Intel i82557B Ethernet", + .device = i82557B, + .device_id = PCI_DEVICE_ID_INTEL_82557, + .revision = 0x02, + .power_management = false, },{ - .qdev.name = "i82557c", - .qdev.size = sizeof(EEPRO100State), - .init = pci_i82557c_init, - .exit = pci_nic_uninit, - .qdev.props = (Property[]) { - DEFINE_NIC_PROPERTIES(EEPRO100State, conf), - DEFINE_PROP_END_OF_LIST(), - }, + .pci.qdev.name = "i82557c", + .pci.qdev.desc = "Intel i82557C Ethernet", + .device = i82557C, + .device_id = PCI_DEVICE_ID_INTEL_82557, + .revision = 0x03, + .power_management = false, },{ - .qdev.name = "i82558a", - .qdev.size = sizeof(EEPRO100State), - .init = pci_i82558a_init, - .exit = pci_nic_uninit, - .qdev.props = (Property[]) { - DEFINE_NIC_PROPERTIES(EEPRO100State, conf), - DEFINE_PROP_END_OF_LIST(), - }, + .pci.qdev.name = "i82558a", + .pci.qdev.desc = "Intel i82558A Ethernet", + .device = i82558A, + .device_id = PCI_DEVICE_ID_INTEL_82557, + .revision = 0x04, + .stats_size = 76, + .has_extended_tcb_support = true, + .power_management = true, },{ - .qdev.name = "i82558b", - .qdev.size = sizeof(EEPRO100State), - .init = pci_i82558b_init, - .exit = pci_nic_uninit, - .qdev.props = (Property[]) { - DEFINE_NIC_PROPERTIES(EEPRO100State, conf), - DEFINE_PROP_END_OF_LIST(), - }, + .pci.qdev.name = "i82558b", + .pci.qdev.desc = "Intel i82558B Ethernet", + .device = i82558B, + .device_id = PCI_DEVICE_ID_INTEL_82557, + .revision = 0x05, + .stats_size = 76, + .has_extended_tcb_support = true, + .power_management = true, },{ - .qdev.name = "i82559a", - .qdev.size = sizeof(EEPRO100State), - .init = pci_i82559a_init, - .exit = pci_nic_uninit, - .qdev.props = (Property[]) { - DEFINE_NIC_PROPERTIES(EEPRO100State, conf), - DEFINE_PROP_END_OF_LIST(), - }, + .pci.qdev.name = "i82559a", + .pci.qdev.desc = "Intel i82559A Ethernet", + .device = i82559A, + .device_id = PCI_DEVICE_ID_INTEL_82557, + .revision = 0x06, + .stats_size = 80, + .has_extended_tcb_support = true, + .power_management = true, },{ - .qdev.name = "i82559b", - .qdev.size = sizeof(EEPRO100State), - .init = pci_i82559b_init, - .exit = pci_nic_uninit, - .qdev.props = (Property[]) { - DEFINE_NIC_PROPERTIES(EEPRO100State, conf), - DEFINE_PROP_END_OF_LIST(), - }, + .pci.qdev.name = "i82559b", + .pci.qdev.desc = "Intel i82559B Ethernet", + .device = i82559B, + .device_id = PCI_DEVICE_ID_INTEL_82557, + .revision = 0x07, + .stats_size = 80, + .has_extended_tcb_support = true, + .power_management = true, },{ - .qdev.name = "i82559c", - .qdev.size = sizeof(EEPRO100State), - .init = pci_i82559c_init, - .exit = pci_nic_uninit, - .qdev.props = (Property[]) { - DEFINE_NIC_PROPERTIES(EEPRO100State, conf), - DEFINE_PROP_END_OF_LIST(), - }, + .pci.qdev.name = "i82559c", + .pci.qdev.desc = "Intel i82559C Ethernet", + .device = i82559C, + .device_id = PCI_DEVICE_ID_INTEL_82557, +#if 0 + .revision = 0x08, +#endif + /* TODO: Windows wants revision id 0x0c. */ + .revision = 0x0c, + .stats_size = 80, + .has_extended_tcb_support = true, + .power_management = true, },{ - .qdev.name = "i82559er", - .qdev.size = sizeof(EEPRO100State), - .init = pci_i82559er_init, - .exit = pci_nic_uninit, - .qdev.props = (Property[]) { - DEFINE_NIC_PROPERTIES(EEPRO100State, conf), - DEFINE_PROP_END_OF_LIST(), - }, + .pci.qdev.name = "i82559er", + .pci.qdev.desc = "Intel i82559ER Ethernet", + .device = i82559ER, + .device_id = PCI_DEVICE_ID_INTEL_82551IT, + .revision = 0x09, + .stats_size = 80, + .has_extended_tcb_support = true, + .power_management = true, },{ - .qdev.name = "i82562", - .qdev.size = sizeof(EEPRO100State), - .init = pci_i82562_init, - .exit = pci_nic_uninit, - .qdev.props = (Property[]) { - DEFINE_NIC_PROPERTIES(EEPRO100State, conf), - DEFINE_PROP_END_OF_LIST(), - }, + .pci.qdev.name = "i82562", + .pci.qdev.desc = "Intel i82562 Ethernet", + .device = i82562, + /* TODO: check device id. */ + .device_id = PCI_DEVICE_ID_INTEL_82551IT, + /* TODO: wrong revision id. */ + .revision = 0x0e, + .stats_size = 80, + .has_extended_tcb_support = true, + .power_management = true, },{ - /* end of list */ + /* Toshiba Tecra 8200. */ + .pci.qdev.name = "i82801", + .pci.qdev.desc = "Intel i82801 Ethernet", + .device = i82801, + .device_id = 0x2449, + .revision = 0x03, + .stats_size = 80, + .has_extended_tcb_support = true, + .power_management = true, } }; +static Property e100_properties[] = { + DEFINE_NIC_PROPERTIES(EEPRO100State, conf), + DEFINE_PROP_END_OF_LIST(), +}; + static void eepro100_register_devices(void) { - pci_qdev_register_many(eepro100_info); + size_t i; + for (i = 0; i < ARRAY_SIZE(e100_devices); i++) { + PCIDeviceInfo *pci_dev = &e100_devices[i].pci; + /* We use the same rom file for all device ids. + QEMU fixes the device id during rom load. */ + pci_dev->romfile = "gpxe-eepro100-80861209.rom"; + pci_dev->init = e100_nic_init; + pci_dev->exit = pci_nic_uninit; + pci_dev->qdev.props = e100_properties; + pci_dev->qdev.size = sizeof(EEPRO100State); + pci_qdev_register(pci_dev); + } } device_init(eepro100_register_devices) diff -Nru qemu-kvm-0.12.5+noroms/hw/eeprom93xx.c qemu-kvm-0.14.1/hw/eeprom93xx.c --- qemu-kvm-0.12.5+noroms/hw/eeprom93xx.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/eeprom93xx.c 2011-05-11 13:29:46.000000000 +0000 @@ -289,7 +289,7 @@ } #endif -eeprom_t *eeprom93xx_new(uint16_t nwords) +eeprom_t *eeprom93xx_new(DeviceState *dev, uint16_t nwords) { /* Add a new EEPROM (with 16, 64 or 256 words). */ eeprom_t *eeprom; @@ -316,15 +316,15 @@ /* Output DO is tristate, read results in 1. */ eeprom->eedo = 1; logout("eeprom = 0x%p, nwords = %u\n", eeprom, nwords); - vmstate_register(0, &vmstate_eeprom, eeprom); + vmstate_register(dev, 0, &vmstate_eeprom, eeprom); return eeprom; } -void eeprom93xx_free(eeprom_t *eeprom) +void eeprom93xx_free(DeviceState *dev, eeprom_t *eeprom) { /* Destroy EEPROM. */ logout("eeprom = 0x%p\n", eeprom); - vmstate_unregister(&vmstate_eeprom, eeprom); + vmstate_unregister(dev, &vmstate_eeprom, eeprom); qemu_free(eeprom); } diff -Nru qemu-kvm-0.12.5+noroms/hw/eeprom93xx.h qemu-kvm-0.14.1/hw/eeprom93xx.h --- qemu-kvm-0.12.5+noroms/hw/eeprom93xx.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/eeprom93xx.h 2011-05-11 13:29:46.000000000 +0000 @@ -23,10 +23,10 @@ typedef struct _eeprom_t eeprom_t; /* Create a new EEPROM with (nwords * 2) bytes. */ -eeprom_t *eeprom93xx_new(uint16_t nwords); +eeprom_t *eeprom93xx_new(DeviceState *dev, uint16_t nwords); /* Destroy an existing EEPROM. */ -void eeprom93xx_free(eeprom_t *eeprom); +void eeprom93xx_free(DeviceState *dev, eeprom_t *eeprom); /* Read from the EEPROM. */ uint16_t eeprom93xx_read(eeprom_t *eeprom); diff -Nru qemu-kvm-0.12.5+noroms/hw/elf_ops.h qemu-kvm-0.14.1/hw/elf_ops.h --- qemu-kvm-0.12.5+noroms/hw/elf_ops.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/elf_ops.h 2011-05-11 13:29:46.000000000 +0000 @@ -149,9 +149,19 @@ } i++; } - syms = qemu_realloc(syms, nsyms * sizeof(*syms)); + if (nsyms) { + syms = qemu_realloc(syms, nsyms * sizeof(*syms)); - qsort(syms, nsyms, sizeof(*syms), glue(symcmp, SZ)); + qsort(syms, nsyms, sizeof(*syms), glue(symcmp, SZ)); + for (i = 0; i < nsyms - 1; i++) { + if (syms[i].st_size == 0) { + syms[i].st_size = syms[i + 1].st_value - syms[i].st_value; + } + } + } else { + qemu_free(syms); + syms = NULL; + } /* String table */ if (symtab->sh_link >= ehdr->e_shnum) @@ -179,7 +189,9 @@ return -1; } -static int glue(load_elf, SZ)(const char *name, int fd, int64_t address_offset, +static int glue(load_elf, SZ)(const char *name, int fd, + uint64_t (*translate_fn)(void *, uint64_t), + void *translate_opaque, int must_swab, uint64_t *pentry, uint64_t *lowaddr, uint64_t *highaddr, int elf_machine, int clear_lsb) @@ -209,6 +221,11 @@ if (EM_386 != ehdr.e_machine) goto fail; break; + case EM_MICROBLAZE: + if (EM_MICROBLAZE != ehdr.e_machine) + if (EM_MICROBLAZE_OLD != ehdr.e_machine) + goto fail; + break; default: if (elf_machine != ehdr.e_machine) goto fail; @@ -248,7 +265,11 @@ } /* address_offset is hack for kernel images that are linked at the wrong physical address. */ - addr = ph->p_paddr + address_offset; + if (translate_fn) { + addr = translate_fn(translate_opaque, ph->p_paddr); + } else { + addr = ph->p_paddr; + } snprintf(label, sizeof(label), "phdr #%d: %s", i, name); rom_add_blob_fixed(label, data, mem_size, addr); diff -Nru qemu-kvm-0.12.5+noroms/hw/empty_slot.c qemu-kvm-0.14.1/hw/empty_slot.c --- qemu-kvm-0.12.5+noroms/hw/empty_slot.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/empty_slot.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,93 @@ +/* + * QEMU Empty Slot + * + * The empty_slot device emulates known to a bus but not connected devices. + * + * Copyright (c) 2010 Artyom Tarasenko + * + * This code is licensed under the GNU GPL v2 or (at your option) any later + * version. + */ + +#include "hw.h" +#include "sysbus.h" +#include "empty_slot.h" + +//#define DEBUG_EMPTY_SLOT + +#ifdef DEBUG_EMPTY_SLOT +#define DPRINTF(fmt, ...) \ + do { printf("empty_slot: " fmt , ## __VA_ARGS__); } while (0) +#else +#define DPRINTF(fmt, ...) do {} while (0) +#endif + +typedef struct EmptySlot { + SysBusDevice busdev; + uint64_t size; +} EmptySlot; + +static uint32_t empty_slot_readl(void *opaque, target_phys_addr_t addr) +{ + DPRINTF("read from " TARGET_FMT_plx "\n", addr); + return 0; +} + +static void empty_slot_writel(void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + DPRINTF("write 0x%x to " TARGET_FMT_plx "\n", val, addr); +} + +CPUReadMemoryFunc * const empty_slot_read[3] = { + empty_slot_readl, + empty_slot_readl, + empty_slot_readl, +}; + +static CPUWriteMemoryFunc * const empty_slot_write[3] = { + empty_slot_writel, + empty_slot_writel, + empty_slot_writel, +}; + +void empty_slot_init(target_phys_addr_t addr, uint64_t slot_size) +{ + DeviceState *dev; + SysBusDevice *s; + EmptySlot *e; + + dev = qdev_create(NULL, "empty_slot"); + s = sysbus_from_qdev(dev); + e = FROM_SYSBUS(EmptySlot, s); + e->size = slot_size; + + qdev_init_nofail(dev); + + sysbus_mmio_map(s, 0, addr); +} + +static int empty_slot_init1(SysBusDevice *dev) +{ + EmptySlot *s = FROM_SYSBUS(EmptySlot, dev); + ram_addr_t empty_slot_offset; + + empty_slot_offset = cpu_register_io_memory(empty_slot_read, + empty_slot_write, s, + DEVICE_NATIVE_ENDIAN); + sysbus_init_mmio(dev, s->size, empty_slot_offset | IO_MEM_RAM); + return 0; +} + +static SysBusDeviceInfo empty_slot_info = { + .init = empty_slot_init1, + .qdev.name = "empty_slot", + .qdev.size = sizeof(EmptySlot), +}; + +static void empty_slot_register_devices(void) +{ + sysbus_register_withprop(&empty_slot_info); +} + +device_init(empty_slot_register_devices); diff -Nru qemu-kvm-0.12.5+noroms/hw/empty_slot.h qemu-kvm-0.14.1/hw/empty_slot.h --- qemu-kvm-0.12.5+noroms/hw/empty_slot.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/empty_slot.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,2 @@ +/* empty_slot.c */ +void empty_slot_init(target_phys_addr_t addr, uint64_t slot_size); diff -Nru qemu-kvm-0.12.5+noroms/hw/es1370.c qemu-kvm-0.14.1/hw/es1370.c --- qemu-kvm-0.12.5+noroms/hw/es1370.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/es1370.c 2011-05-11 13:29:46.000000000 +0000 @@ -1000,27 +1000,28 @@ pci_config_set_vendor_id (c, PCI_VENDOR_ID_ENSONIQ); pci_config_set_device_id (c, PCI_DEVICE_ID_ENSONIQ_ES1370); - c[0x07] = 2 << 1; + c[PCI_STATUS + 1] = PCI_STATUS_DEVSEL_SLOW >> 8; pci_config_set_class (c, PCI_CLASS_MULTIMEDIA_AUDIO); #if 1 - c[0x2c] = 0x42; - c[0x2d] = 0x49; - c[0x2e] = 0x4c; - c[0x2f] = 0x4c; + c[PCI_SUBSYSTEM_VENDOR_ID] = 0x42; + c[PCI_SUBSYSTEM_VENDOR_ID + 1] = 0x49; + c[PCI_SUBSYSTEM_ID] = 0x4c; + c[PCI_SUBSYSTEM_ID + 1] = 0x4c; #else - c[0x2c] = 0x74; - c[0x2d] = 0x12; - c[0x2e] = 0x71; - c[0x2f] = 0x13; - c[0x34] = 0xdc; - c[0x3c] = 10; + c[PCI_SUBSYSTEM_VENDOR_ID] = 0x74; + c[PCI_SUBSYSTEM_VENDOR_ID + 1] = 0x12; + c[PCI_SUBSYSTEM_ID] = 0x71; + c[PCI_SUBSYSTEM_ID + 1] = 0x13; + c[PCI_CAPABILITY_LIST] = 0xdc; + c[PCI_INTERRUPT_LINE] = 10; c[0xdc] = 0x00; #endif - c[0x3d] = 1; - c[0x3e] = 0x0c; - c[0x3f] = 0x80; + /* TODO: RST# value should be 0. */ + c[PCI_INTERRUPT_PIN] = 1; + c[PCI_MIN_GNT] = 0x0c; + c[PCI_MAX_LAT] = 0x80; pci_register_bar (&s->dev, 0, 256, PCI_BASE_ADDRESS_SPACE_IO, es1370_map); qemu_register_reset (es1370_on_reset, s); diff -Nru qemu-kvm-0.12.5+noroms/hw/escc.c qemu-kvm-0.14.1/hw/escc.c --- qemu-kvm-0.12.5+noroms/hw/escc.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/escc.c 2011-05-11 13:29:46.000000000 +0000 @@ -65,6 +65,8 @@ * 2006-Aug-10 Igor Kovalenko : Renamed KBDQueue to SERIOQueue, implemented * serial mouse queue. * Implemented serial mouse protocol. + * + * 2010-May-23 Artyom Tarasenko: Reworked IUS logic */ #ifdef DEBUG_SERIAL @@ -88,13 +90,13 @@ typedef enum { chn_a, chn_b, -} chn_id_t; +} ChnID; #define CHN_C(s) ((s)->chn == chn_b? 'b' : 'a') typedef enum { ser, kbd, mouse, -} chn_type_t; +} ChnType; #define SERIO_QUEUE_SIZE 256 @@ -108,8 +110,8 @@ qemu_irq irq; uint32_t reg; uint32_t rxint, txint, rxint_under_svc, txint_under_svc; - chn_id_t chn; // this channel, A (base+4) or B (base+0) - chn_type_t type; + ChnID chn; // this channel, A (base+4) or B (base+0) + ChnType type; struct ChannelState *otherchn; uint8_t rx, tx, wregs[SERIAL_REGS], rregs[SERIAL_REGS]; SERIOQueue queue; @@ -279,7 +281,7 @@ static int escc_update_irq_chn(ChannelState *s) { - if ((((s->wregs[W_INTR] & INTR_TXINT) && s->txint == 1) || + if ((((s->wregs[W_INTR] & INTR_TXINT) && (s->txint == 1)) || // tx ints enabled, pending ((((s->wregs[W_INTR] & INTR_RXMODEMSK) == INTR_RXINT1ST) || ((s->wregs[W_INTR] & INTR_RXMODEMSK) == INTR_RXINTALL)) && @@ -342,24 +344,22 @@ static inline void set_rxint(ChannelState *s) { s->rxint = 1; - if (!s->txint_under_svc) { - s->rxint_under_svc = 1; - if (s->chn == chn_a) { - if (s->wregs[W_MINTR] & MINTR_STATUSHI) - s->otherchn->rregs[R_IVEC] = IVEC_HIRXINTA; - else - s->otherchn->rregs[R_IVEC] = IVEC_LORXINTA; - } else { - if (s->wregs[W_MINTR] & MINTR_STATUSHI) - s->rregs[R_IVEC] = IVEC_HIRXINTB; - else - s->rregs[R_IVEC] = IVEC_LORXINTB; - } - } - if (s->chn == chn_a) + /* XXX: missing daisy chainnig: chn_b rx should have a lower priority + than chn_a rx/tx/special_condition service*/ + s->rxint_under_svc = 1; + if (s->chn == chn_a) { s->rregs[R_INTR] |= INTR_RXINTA; - else + if (s->wregs[W_MINTR] & MINTR_STATUSHI) + s->otherchn->rregs[R_IVEC] = IVEC_HIRXINTA; + else + s->otherchn->rregs[R_IVEC] = IVEC_LORXINTA; + } else { s->otherchn->rregs[R_INTR] |= INTR_RXINTB; + if (s->wregs[W_MINTR] & MINTR_STATUSHI) + s->rregs[R_IVEC] = IVEC_HIRXINTB; + else + s->rregs[R_IVEC] = IVEC_LORXINTB; + } escc_update_irq(s); } @@ -369,19 +369,21 @@ if (!s->rxint_under_svc) { s->txint_under_svc = 1; if (s->chn == chn_a) { + if (s->wregs[W_INTR] & INTR_TXINT) { + s->rregs[R_INTR] |= INTR_TXINTA; + } if (s->wregs[W_MINTR] & MINTR_STATUSHI) s->otherchn->rregs[R_IVEC] = IVEC_HITXINTA; else s->otherchn->rregs[R_IVEC] = IVEC_LOTXINTA; } else { s->rregs[R_IVEC] = IVEC_TXINTB; + if (s->wregs[W_INTR] & INTR_TXINT) { + s->otherchn->rregs[R_INTR] |= INTR_TXINTB; + } } - } - if (s->chn == chn_a) - s->rregs[R_INTR] |= INTR_TXINTA; - else - s->otherchn->rregs[R_INTR] |= INTR_TXINTB; escc_update_irq(s); + } } static inline void clr_rxint(ChannelState *s) @@ -417,6 +419,7 @@ s->otherchn->rregs[R_IVEC] = IVEC_LONOINT; s->rregs[R_INTR] &= ~INTR_TXINTA; } else { + s->otherchn->rregs[R_INTR] &= ~INTR_TXINTB; if (s->wregs[W_MINTR] & MINTR_STATUSHI) s->rregs[R_IVEC] = IVEC_HINOINT; else @@ -515,10 +518,15 @@ clr_txint(s); break; case CMD_CLR_IUS: - if (s->rxint_under_svc) - clr_rxint(s); - else if (s->txint_under_svc) - clr_txint(s); + if (s->rxint_under_svc) { + s->rxint_under_svc = 0; + if (s->txint) { + set_txint(s); + } + } else if (s->txint_under_svc) { + s->txint_under_svc = 0; + } + escc_update_irq(s); break; default: break; @@ -910,7 +918,8 @@ s->chn[0].otherchn = &s->chn[1]; s->chn[1].otherchn = &s->chn[0]; - io = cpu_register_io_memory(escc_mem_read, escc_mem_write, s); + io = cpu_register_io_memory(escc_mem_read, escc_mem_write, s, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, ESCC_SIZE << s->it_shift, io); s->mmio_index = io; diff -Nru qemu-kvm-0.12.5+noroms/hw/esp.c qemu-kvm-0.14.1/hw/esp.c --- qemu-kvm-0.12.5+noroms/hw/esp.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/esp.c 2011-05-11 13:29:46.000000000 +0000 @@ -77,9 +77,11 @@ uint8_t *async_buf; uint32_t async_len; - espdma_memory_read_write dma_memory_read; - espdma_memory_read_write dma_memory_write; + ESPDMAMemoryReadWriteFunc dma_memory_read; + ESPDMAMemoryReadWriteFunc dma_memory_write; void *dma_opaque; + int dma_enabled; + void (*dma_cb)(ESPState *s); }; #define ESP_TCLO 0x0 @@ -154,6 +156,7 @@ if (!(s->rregs[ESP_RSTAT] & STAT_INT)) { s->rregs[ESP_RSTAT] |= STAT_INT; qemu_irq_raise(s->irq); + DPRINTF("Raise IRQ\n"); } } @@ -162,6 +165,25 @@ if (s->rregs[ESP_RSTAT] & STAT_INT) { s->rregs[ESP_RSTAT] &= ~STAT_INT; qemu_irq_lower(s->irq); + DPRINTF("Lower IRQ\n"); + } +} + +static void esp_dma_enable(void *opaque, int irq, int level) +{ + DeviceState *d = opaque; + ESPState *s = container_of(d, ESPState, busdev.qdev); + + if (level) { + s->dma_enabled = 1; + DPRINTF("Raise enable\n"); + if (s->dma_cb) { + s->dma_cb(s); + s->dma_cb = NULL; + } + } else { + DPRINTF("Lower enable\n"); + s->dma_enabled = 0; } } @@ -241,6 +263,10 @@ uint8_t buf[32]; int len; + if (!s->dma_enabled) { + s->dma_cb = handle_satn; + return; + } len = get_cmd(s, buf); if (len) do_cmd(s, buf); @@ -251,6 +277,10 @@ uint8_t buf[32]; int len; + if (!s->dma_enabled) { + s->dma_cb = handle_s_without_atn; + return; + } len = get_cmd(s, buf); if (len) { do_busid_cmd(s, buf, 0); @@ -259,6 +289,10 @@ static void handle_satn_stop(ESPState *s) { + if (!s->dma_enabled) { + s->dma_cb = handle_satn_stop; + return; + } s->cmdlen = get_cmd(s, s->cmdbuf); if (s->cmdlen) { DPRINTF("Set ATN & Stop: cmdlen %d\n", s->cmdlen); @@ -417,7 +451,7 @@ } } -static void esp_reset(DeviceState *d) +static void esp_hard_reset(DeviceState *d) { ESPState *s = container_of(d, ESPState, busdev.qdev); @@ -429,14 +463,36 @@ s->ti_wptr = 0; s->dma = 0; s->do_cmd = 0; + s->dma_cb = NULL; s->rregs[ESP_CFG1] = 7; } +static void esp_soft_reset(DeviceState *d) +{ + ESPState *s = container_of(d, ESPState, busdev.qdev); + + qemu_irq_lower(s->irq); + esp_hard_reset(d); +} + static void parent_esp_reset(void *opaque, int irq, int level) { - if (level) - esp_reset(opaque); + if (level) { + esp_soft_reset(opaque); + } +} + +static void esp_gpio_demux(void *opaque, int irq, int level) +{ + switch (irq) { + case 0: + parent_esp_reset(opaque, irq, level); + break; + case 1: + esp_dma_enable(opaque, irq, level); + break; + } } static uint32_t esp_mem_readb(void *opaque, target_phys_addr_t addr) @@ -526,7 +582,7 @@ break; case CMD_RESET: DPRINTF("Chip reset (%2.2x)\n", val); - esp_reset(&s->busdev.qdev); + esp_soft_reset(&s->busdev.qdev); break; case CMD_BUSRESET: DPRINTF("Bus reset (%2.2x)\n", val); @@ -633,9 +689,10 @@ }; void esp_init(target_phys_addr_t espaddr, int it_shift, - espdma_memory_read_write dma_memory_read, - espdma_memory_read_write dma_memory_write, - void *dma_opaque, qemu_irq irq, qemu_irq *reset) + ESPDMAMemoryReadWriteFunc dma_memory_read, + ESPDMAMemoryReadWriteFunc dma_memory_write, + void *dma_opaque, qemu_irq irq, qemu_irq *reset, + qemu_irq *dma_enable) { DeviceState *dev; SysBusDevice *s; @@ -647,11 +704,14 @@ esp->dma_memory_write = dma_memory_write; esp->dma_opaque = dma_opaque; esp->it_shift = it_shift; + /* XXX for now until rc4030 has been changed to use DMA enable signal */ + esp->dma_enabled = 1; qdev_init_nofail(dev); s = sysbus_from_qdev(dev); sysbus_connect_irq(s, 0, irq); sysbus_mmio_map(s, 0, espaddr); *reset = qdev_get_gpio_in(dev, 0); + *dma_enable = qdev_get_gpio_in(dev, 1); } static int esp_init1(SysBusDevice *dev) @@ -662,14 +722,14 @@ sysbus_init_irq(dev, &s->irq); assert(s->it_shift != -1); - esp_io_memory = cpu_register_io_memory(esp_mem_read, esp_mem_write, s); + esp_io_memory = cpu_register_io_memory(esp_mem_read, esp_mem_write, s, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, ESP_REGS << s->it_shift, esp_io_memory); - qdev_init_gpio_in(&dev->qdev, parent_esp_reset, 1); + qdev_init_gpio_in(&dev->qdev, esp_gpio_demux, 2); scsi_bus_new(&s->bus, &dev->qdev, 0, ESP_MAX_DEVS, esp_command_complete); - scsi_bus_legacy_handle_cmdline(&s->bus); - return 0; + return scsi_bus_legacy_handle_cmdline(&s->bus); } static SysBusDeviceInfo esp_info = { @@ -677,7 +737,7 @@ .qdev.name = "esp", .qdev.size = sizeof(ESPState), .qdev.vmsd = &vmstate_esp, - .qdev.reset = esp_reset, + .qdev.reset = esp_hard_reset, .qdev.props = (Property[]) { {.name = NULL} } diff -Nru qemu-kvm-0.12.5+noroms/hw/esp.h qemu-kvm-0.14.1/hw/esp.h --- qemu-kvm-0.12.5+noroms/hw/esp.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/esp.h 2011-05-11 13:29:46.000000000 +0000 @@ -3,10 +3,11 @@ /* esp.c */ #define ESP_MAX_DEVS 7 -typedef void (*espdma_memory_read_write)(void *opaque, uint8_t *buf, int len); +typedef void (*ESPDMAMemoryReadWriteFunc)(void *opaque, uint8_t *buf, int len); void esp_init(target_phys_addr_t espaddr, int it_shift, - espdma_memory_read_write dma_memory_read, - espdma_memory_read_write dma_memory_write, - void *dma_opaque, qemu_irq irq, qemu_irq *reset); + ESPDMAMemoryReadWriteFunc dma_memory_read, + ESPDMAMemoryReadWriteFunc dma_memory_write, + void *dma_opaque, qemu_irq irq, qemu_irq *reset, + qemu_irq *dma_enable); #endif diff -Nru qemu-kvm-0.12.5+noroms/hw/etraxfs.c qemu-kvm-0.14.1/hw/etraxfs.c --- qemu-kvm-0.12.5+noroms/hw/etraxfs.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/etraxfs.c 2011-05-11 13:29:46.000000000 +0000 @@ -30,18 +30,18 @@ #include "etraxfs.h" #include "loader.h" #include "elf.h" +#include "cris-boot.h" +#include "blockdev.h" #define FLASH_SIZE 0x2000000 #define INTMEM_SIZE (128 * 1024) -static uint32_t bootstrap_pc; +static struct cris_load_info li; -static void main_cpu_reset(void *opaque) +static void flash_cpu_reset(void *opaque) { CPUState *env = opaque; cpu_reset(env); - - env->pc = bootstrap_pc; } static @@ -56,7 +56,6 @@ qemu_irq irq[30], nmi[2], *cpu_irq; void *etraxfs_dmac; struct etraxfs_dma_client *eth[2] = {NULL, NULL}; - int kernel_size; DriveInfo *dinfo; int i; ram_addr_t phys_ram; @@ -68,26 +67,25 @@ cpu_model = "crisv32"; } env = cpu_init(cpu_model); - qemu_register_reset(main_cpu_reset, env); /* allocate RAM */ - phys_ram = qemu_ram_alloc(ram_size); + phys_ram = qemu_ram_alloc(NULL, "etraxfs.ram", ram_size); cpu_register_physical_memory(0x40000000, ram_size, phys_ram | IO_MEM_RAM); /* The ETRAX-FS has 128Kb on chip ram, the docs refer to it as the internal memory. */ - phys_intmem = qemu_ram_alloc(INTMEM_SIZE); + phys_intmem = qemu_ram_alloc(NULL, "etraxfs.chipram", INTMEM_SIZE); cpu_register_physical_memory(0x38000000, INTMEM_SIZE, phys_intmem | IO_MEM_RAM); - phys_flash = qemu_ram_alloc(FLASH_SIZE); + phys_flash = qemu_ram_alloc(NULL, "etraxfs.flash", FLASH_SIZE); dinfo = drive_get(IF_PFLASH, 0, 0); pflash_cfi02_register(0x0, phys_flash, dinfo ? dinfo->bdrv : NULL, (64 * 1024), FLASH_SIZE >> 16, 1, 2, 0x0000, 0x0000, 0x0000, 0x0000, - 0x555, 0x2aa); + 0x555, 0x2aa, 0); cpu_irq = cris_pic_init_cpu(env); dev = qdev_create(NULL, "etraxfs,pic"); /* FIXME: Is there a proper way to signal vectors to the CPU core? */ @@ -132,38 +130,19 @@ } if (kernel_filename) { - uint64_t entry, high; - int kcmdline_len; - - /* Boots a kernel elf binary, os/linux-2.6/vmlinux from the axis - devboard SDK. */ - kernel_size = load_elf(kernel_filename, -0x80000000LL, - &entry, NULL, &high, 0, ELF_MACHINE, 0); - bootstrap_pc = entry; - if (kernel_size < 0) { - /* Takes a kimage from the axis devboard SDK. */ - kernel_size = load_image_targphys(kernel_filename, 0x40004000, - ram_size); - bootstrap_pc = 0x40004000; - env->regs[9] = 0x40004000 + kernel_size; + li.image_filename = kernel_filename; + li.cmdline = kernel_cmdline; + cris_load_image(env, &li); + } else { + if (!dinfo) { + fprintf(stderr, + "Provide a kernel image or a flash image to boot from.\n"); + exit(1); } - env->regs[8] = 0x56902387; /* RAM init magic. */ - if (kernel_cmdline && (kcmdline_len = strlen(kernel_cmdline))) { - if (kcmdline_len > 256) { - fprintf(stderr, "Too long CRIS kernel cmdline (max 256)\n"); - exit(1); - } - /* Let the kernel know we are modifying the cmdline. */ - env->regs[10] = 0x87109563; - env->regs[11] = 0x40000000; - pstrcpy_targphys("cmdline", env->regs[11], 256, kernel_cmdline); - } + /* Nothing more to do for flash images, those boot from addr 0. */ + qemu_register_reset(flash_cpu_reset, env); } - env->pc = bootstrap_pc; - - printf ("pc =%x\n", env->pc); - printf ("ram size =%ld\n", ram_size); } static QEMUMachine bareetraxfs_machine = { diff -Nru qemu-kvm-0.12.5+noroms/hw/etraxfs_dma.c qemu-kvm-0.14.1/hw/etraxfs_dma.c --- qemu-kvm-0.12.5+noroms/hw/etraxfs_dma.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/etraxfs_dma.c 2011-05-11 13:29:46.000000000 +0000 @@ -750,7 +750,7 @@ ctrl->nr_channels = nr_channels; ctrl->channels = qemu_mallocz(sizeof ctrl->channels[0] * nr_channels); - ctrl->map = cpu_register_io_memory(dma_read, dma_write, ctrl); + ctrl->map = cpu_register_io_memory(dma_read, dma_write, ctrl, DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base, nr_channels * 0x2000, ctrl->map); return ctrl; } diff -Nru qemu-kvm-0.12.5+noroms/hw/etraxfs_eth.c qemu-kvm-0.14.1/hw/etraxfs_eth.c --- qemu-kvm-0.12.5+noroms/hw/etraxfs_eth.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/etraxfs_eth.c 2011-05-11 13:29:46.000000000 +0000 @@ -401,7 +401,7 @@ eth->macaddr[ma][i++] = eth->regs[reg] >> 16; eth->macaddr[ma][i++] = eth->regs[reg] >> 24; eth->macaddr[ma][i++] = eth->regs[reg + 1]; - eth->macaddr[ma][i++] = eth->regs[reg + 1] >> 8; + eth->macaddr[ma][i] = eth->regs[reg + 1] >> 8; D(printf("set mac%d=%x.%x.%x.%x.%x.%x\n", ma, eth->macaddr[ma][0], eth->macaddr[ma][1], @@ -437,6 +437,7 @@ eth_validate_duplex(eth); } eth->mdio_bus.mdc = !!(value & 4); + eth->regs[addr] = value; break; case RW_REC_CTRL: @@ -463,7 +464,7 @@ /* First bit on the wire of a MAC address signals multicast or physical address. */ - if (!m_individual && !sa[0] & 1) + if (!m_individual && !(sa[0] & 1)) return 0; /* Calculate the hash index for the GA registers. */ @@ -597,7 +598,8 @@ tdk_init(ð->phy); mdio_attach(ð->mdio_bus, ð->phy, eth->phyaddr); - eth->ethregs = cpu_register_io_memory(eth_read, eth_write, eth); + eth->ethregs = cpu_register_io_memory(eth_read, eth_write, eth, + DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory (base, 0x5c, eth->ethregs); memcpy(eth->conf.macaddr.a, nd->macaddr, sizeof(nd->macaddr)); diff -Nru qemu-kvm-0.12.5+noroms/hw/etraxfs_pic.c qemu-kvm-0.14.1/hw/etraxfs_pic.c --- qemu-kvm-0.12.5+noroms/hw/etraxfs_pic.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/etraxfs_pic.c 2011-05-11 13:29:46.000000000 +0000 @@ -145,7 +145,8 @@ sysbus_init_irq(dev, &s->parent_irq); sysbus_init_irq(dev, &s->parent_nmi); - intr_vect_regs = cpu_register_io_memory(pic_read, pic_write, s); + intr_vect_regs = cpu_register_io_memory(pic_read, pic_write, s, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, R_MAX * 4, intr_vect_regs); return 0; } diff -Nru qemu-kvm-0.12.5+noroms/hw/etraxfs_ser.c qemu-kvm-0.14.1/hw/etraxfs_ser.c --- qemu-kvm-0.12.5+noroms/hw/etraxfs_ser.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/etraxfs_ser.c 2011-05-11 13:29:46.000000000 +0000 @@ -49,20 +49,27 @@ CharDriverState *chr; qemu_irq irq; - /* This pending thing is a hack. */ int pending_tx; + uint8_t rx_fifo[16]; + unsigned int rx_fifo_pos; + unsigned int rx_fifo_len; + /* Control registers. */ uint32_t regs[R_MAX]; }; static void ser_update_irq(struct etrax_serial *s) { - s->regs[R_INTR] &= ~(s->regs[RW_ACK_INTR]); - s->regs[R_MASKED_INTR] = s->regs[R_INTR] & s->regs[RW_INTR_MASK]; + if (s->rx_fifo_len) { + s->regs[R_INTR] |= 8; + } else { + s->regs[R_INTR] &= ~8; + } + + s->regs[R_MASKED_INTR] = s->regs[R_INTR] & s->regs[RW_INTR_MASK]; qemu_set_irq(s->irq, !!s->regs[R_MASKED_INTR]); - s->regs[RW_ACK_INTR] = 0; } static uint32_t ser_readl (void *opaque, target_phys_addr_t addr) @@ -75,16 +82,25 @@ switch (addr) { case R_STAT_DIN: - r = s->regs[RS_STAT_DIN]; + r = s->rx_fifo[(s->rx_fifo_pos - s->rx_fifo_len) & 15]; + if (s->rx_fifo_len) { + r |= 1 << STAT_DAV; + } + r |= 1 << STAT_TR_RDY; + r |= 1 << STAT_TR_IDLE; break; case RS_STAT_DIN: - r = s->regs[addr]; - /* Read side-effect: clear dav. */ - s->regs[addr] &= ~(1 << STAT_DAV); + r = s->rx_fifo[(s->rx_fifo_pos - s->rx_fifo_len) & 15]; + if (s->rx_fifo_len) { + r |= 1 << STAT_DAV; + s->rx_fifo_len--; + } + r |= 1 << STAT_TR_RDY; + r |= 1 << STAT_TR_IDLE; break; default: r = s->regs[addr]; - D(printf ("%s %x=%x\n", __func__, addr, r)); + D(printf ("%s " TARGET_FMT_plx "=%x\n", __func__, addr, r)); break; } return r; @@ -97,23 +113,25 @@ unsigned char ch = value; D(CPUState *env = s->env); - D(printf ("%s %x %x\n", __func__, addr, value)); + D(printf ("%s " TARGET_FMT_plx "=%x\n", __func__, addr, value)); addr >>= 2; switch (addr) { case RW_DOUT: qemu_chr_write(s->chr, &ch, 1); - s->regs[R_INTR] |= 1; + s->regs[R_INTR] |= 3; s->pending_tx = 1; s->regs[addr] = value; break; case RW_ACK_INTR: - s->regs[addr] = value; - if (s->pending_tx && (s->regs[addr] & 1)) { - s->regs[R_INTR] |= 1; + if (s->pending_tx) { + value &= ~1; s->pending_tx = 0; - s->regs[addr] &= ~1; + D(printf("fixedup value=%x r_intr=%x\n", value, s->regs[R_INTR])); } + s->regs[addr] = value; + s->regs[R_INTR] &= ~value; + D(printf("r_intr=%x\n", s->regs[R_INTR])); break; default: s->regs[addr] = value; @@ -135,11 +153,21 @@ static void serial_receive(void *opaque, const uint8_t *buf, int size) { struct etrax_serial *s = opaque; + int i; + + /* Got a byte. */ + if (s->rx_fifo_len >= 16) { + printf("WARNING: UART dropped char.\n"); + return; + } + + for (i = 0; i < size; i++) { + s->rx_fifo[s->rx_fifo_pos] = buf[i]; + s->rx_fifo_pos++; + s->rx_fifo_pos &= 15; + s->rx_fifo_len++; + } - s->regs[R_INTR] |= 8; - s->regs[RS_STAT_DIN] &= ~0xff; - s->regs[RS_STAT_DIN] |= (buf[0] & 0xff); - s->regs[RS_STAT_DIN] |= (1 << STAT_DAV); /* dav. */ ser_update_irq(s); } @@ -149,10 +177,11 @@ int r; /* Is the receiver enabled? */ - r = s->regs[RW_REC_CTRL] & 1; + if (!(s->regs[RW_REC_CTRL] & (1 << 3))) { + return 0; + } - /* Pending rx data? */ - r |= !(s->regs[R_INTR] & 8); + r = sizeof(s->rx_fifo) - s->rx_fifo_len; return r; } @@ -171,7 +200,8 @@ s->regs[RS_STAT_DIN] |= (1 << STAT_TR_IDLE); sysbus_init_irq(dev, &s->irq); - ser_regs = cpu_register_io_memory(ser_read, ser_write, s); + ser_regs = cpu_register_io_memory(ser_read, ser_write, s, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, R_MAX * 4, ser_regs); s->chr = qdev_init_chardev(&dev->qdev); if (s->chr) diff -Nru qemu-kvm-0.12.5+noroms/hw/etraxfs_timer.c qemu-kvm-0.14.1/hw/etraxfs_timer.c --- qemu-kvm-0.12.5+noroms/hw/etraxfs_timer.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/etraxfs_timer.c 2011-05-11 13:29:46.000000000 +0000 @@ -100,7 +100,6 @@ return r; } -#define TIMER_SLOWDOWN 1 static void update_ctrl(struct etrax_timer *t, int tnum) { unsigned int op; @@ -142,9 +141,6 @@ } D(printf ("freq_hz=%d div=%d\n", freq_hz, div)); - div = div * TIMER_SLOWDOWN; - div /= 1000; - freq_hz /= 1000; ptimer_set_freq(timer, freq_hz); ptimer_set_limit(timer, div, 0); @@ -323,7 +319,8 @@ sysbus_init_irq(dev, &t->irq); sysbus_init_irq(dev, &t->nmi); - timer_regs = cpu_register_io_memory(timer_read, timer_write, t); + timer_regs = cpu_register_io_memory(timer_read, timer_write, t, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, 0x5c, timer_regs); qemu_register_reset(etraxfs_timer_reset, t); diff -Nru qemu-kvm-0.12.5+noroms/hw/event_notifier.c qemu-kvm-0.14.1/hw/event_notifier.c --- qemu-kvm-0.12.5+noroms/hw/event_notifier.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/event_notifier.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,62 @@ +/* + * event notifier support + * + * Copyright Red Hat, Inc. 2010 + * + * Authors: + * Michael S. Tsirkin + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + */ + +#include "hw.h" +#include "event_notifier.h" +#ifdef CONFIG_EVENTFD +#include +#endif + +int event_notifier_init(EventNotifier *e, int active) +{ +#ifdef CONFIG_EVENTFD + int fd = eventfd(!!active, EFD_NONBLOCK | EFD_CLOEXEC); + if (fd < 0) + return -errno; + e->fd = fd; + return 0; +#else + return -ENOSYS; +#endif +} + +void event_notifier_cleanup(EventNotifier *e) +{ + close(e->fd); +} + +int event_notifier_get_fd(EventNotifier *e) +{ + return e->fd; +} + +int event_notifier_test_and_clear(EventNotifier *e) +{ + uint64_t value; + int r = read(e->fd, &value, sizeof(value)); + return r == sizeof(value); +} + +int event_notifier_test(EventNotifier *e) +{ + uint64_t value; + int r = read(e->fd, &value, sizeof(value)); + if (r == sizeof(value)) { + /* restore previous value. */ + int s = write(e->fd, &value, sizeof(value)); + /* never blocks because we use EFD_SEMAPHORE. + * If we didn't we'd get EAGAIN on overflow + * and we'd have to write code to ignore it. */ + assert(s == sizeof(value)); + } + return r == sizeof(value); +} diff -Nru qemu-kvm-0.12.5+noroms/hw/event_notifier.h qemu-kvm-0.14.1/hw/event_notifier.h --- qemu-kvm-0.12.5+noroms/hw/event_notifier.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/event_notifier.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,16 @@ +#ifndef QEMU_EVENT_NOTIFIER_H +#define QEMU_EVENT_NOTIFIER_H + +#include "qemu-common.h" + +struct EventNotifier { + int fd; +}; + +int event_notifier_init(EventNotifier *, int active); +void event_notifier_cleanup(EventNotifier *); +int event_notifier_get_fd(EventNotifier *); +int event_notifier_test_and_clear(EventNotifier *); +int event_notifier_test(EventNotifier *); + +#endif diff -Nru qemu-kvm-0.12.5+noroms/hw/extboot.c qemu-kvm-0.14.1/hw/extboot.c --- qemu-kvm-0.12.5+noroms/hw/extboot.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/extboot.c 2011-05-11 13:29:46.000000000 +0000 @@ -65,12 +65,6 @@ } } -static uint32_t extboot_read(void *opaque, uint32_t addr) -{ - int *pcmd = opaque; - return *pcmd; -} - static void extboot_write_cmd(void *opaque, uint32_t addr, uint32_t value) { union extboot_cmd cmd; @@ -123,13 +117,7 @@ qemu_free(buf); } -void extboot_init(BlockDriverState *bs, int cmd) +void extboot_init(BlockDriverState *bs) { - int *pcmd; - - pcmd = qemu_mallocz(sizeof(int)); - - *pcmd = cmd; - register_ioport_read(0x404, 1, 1, extboot_read, pcmd); register_ioport_write(0x405, 1, 2, extboot_write_cmd, bs); } diff -Nru qemu-kvm-0.12.5+noroms/hw/fdc.c qemu-kvm-0.14.1/hw/fdc.c --- qemu-kvm-0.12.5+noroms/hw/fdc.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/fdc.c 2011-05-11 13:29:46.000000000 +0000 @@ -29,11 +29,13 @@ #include "hw.h" #include "fdc.h" -#include "block.h" +#include "qemu-error.h" #include "qemu-timer.h" #include "isa.h" #include "sysbus.h" #include "qdev-addr.h" +#include "blockdev.h" +#include "sysemu.h" /********************************************************/ /* debug Floppy devices */ @@ -61,47 +63,45 @@ #define FD_RESET_SENSEI_COUNT 4 /* Number of sense interrupts on RESET */ /* Floppy disk drive emulation */ -typedef enum fdisk_type_t { +typedef enum FDiskType { FDRIVE_DISK_288 = 0x01, /* 2.88 MB disk */ FDRIVE_DISK_144 = 0x02, /* 1.44 MB disk */ FDRIVE_DISK_720 = 0x03, /* 720 kB disk */ FDRIVE_DISK_USER = 0x04, /* User defined geometry */ FDRIVE_DISK_NONE = 0x05, /* No disk */ -} fdisk_type_t; +} FDiskType; -typedef enum fdrive_type_t { +typedef enum FDriveType { FDRIVE_DRV_144 = 0x00, /* 1.44 MB 3"5 drive */ FDRIVE_DRV_288 = 0x01, /* 2.88 MB 3"5 drive */ FDRIVE_DRV_120 = 0x02, /* 1.2 MB 5"25 drive */ FDRIVE_DRV_NONE = 0x03, /* No drive connected */ -} fdrive_type_t; +} FDriveType; -typedef enum fdisk_flags_t { +typedef enum FDiskFlags { FDISK_DBL_SIDES = 0x01, -} fdisk_flags_t; +} FDiskFlags; -typedef struct fdrive_t { - DriveInfo *dinfo; +typedef struct FDrive { BlockDriverState *bs; /* Drive status */ - fdrive_type_t drive; + FDriveType drive; uint8_t perpendicular; /* 2.88 MB access mode */ /* Position */ uint8_t head; uint8_t track; uint8_t sect; /* Media */ - fdisk_flags_t flags; + FDiskFlags flags; uint8_t last_sect; /* Nb sector per track */ uint8_t max_track; /* Nb of tracks */ uint16_t bps; /* Bytes per sector */ uint8_t ro; /* Is read-only */ -} fdrive_t; +} FDrive; -static void fd_init (fdrive_t *drv) +static void fd_init(FDrive *drv) { /* Drive */ - drv->bs = drv->dinfo ? drv->dinfo->bdrv : NULL; drv->drive = FDRIVE_DRV_NONE; drv->perpendicular = 0; /* Disk */ @@ -109,16 +109,16 @@ drv->max_track = 0; } -static int _fd_sector (uint8_t head, uint8_t track, - uint8_t sect, uint8_t last_sect) +static int fd_sector_calc(uint8_t head, uint8_t track, uint8_t sect, + uint8_t last_sect) { return (((track * 2) + head) * last_sect) + sect - 1; } /* Returns current position, in sectors, for given drive */ -static int fd_sector (fdrive_t *drv) +static int fd_sector(FDrive *drv) { - return _fd_sector(drv->head, drv->track, drv->sect, drv->last_sect); + return fd_sector_calc(drv->head, drv->track, drv->sect, drv->last_sect); } /* Seek to a new position: @@ -128,8 +128,8 @@ * returns 3 if sector is invalid * returns 4 if seek is disabled */ -static int fd_seek (fdrive_t *drv, uint8_t head, uint8_t track, uint8_t sect, - int enable_seek) +static int fd_seek(FDrive *drv, uint8_t head, uint8_t track, uint8_t sect, + int enable_seek) { uint32_t sector; int ret; @@ -149,7 +149,7 @@ drv->max_track, drv->last_sect); return 3; } - sector = _fd_sector(head, track, sect, drv->last_sect); + sector = fd_sector_calc(head, track, sect, drv->last_sect); ret = 0; if (sector != fd_sector(drv)) { #if 0 @@ -170,7 +170,7 @@ } /* Set drive back to track 0 */ -static void fd_recalibrate (fdrive_t *drv) +static void fd_recalibrate(FDrive *drv) { FLOPPY_DPRINTF("recalibrate\n"); drv->head = 0; @@ -179,16 +179,16 @@ } /* Recognize floppy formats */ -typedef struct fd_format_t { - fdrive_type_t drive; - fdisk_type_t disk; +typedef struct FDFormat { + FDriveType drive; + FDiskType disk; uint8_t last_sect; uint8_t max_track; uint8_t max_head; const char *str; -} fd_format_t; +} FDFormat; -static const fd_format_t fd_formats[] = { +static const FDFormat fd_formats[] = { /* First entry is default format */ /* 1.44 MB 3"1/2 floppy disks */ { FDRIVE_DRV_144, FDRIVE_DISK_144, 18, 80, 1, "1.44 MB 3\"1/2", }, @@ -236,9 +236,9 @@ }; /* Revalidate a disk drive after a disk change */ -static void fd_revalidate (fdrive_t *drv) +static void fd_revalidate(FDrive *drv) { - const fd_format_t *parse; + const FDFormat *parse; uint64_t nb_sectors, size; int i, first_match, match; int nb_heads, max_track, last_sect, ro; @@ -303,23 +303,23 @@ /********************************************************/ /* Intel 82078 floppy disk controller emulation */ -static void fdctrl_reset (fdctrl_t *fdctrl, int do_irq); -static void fdctrl_reset_fifo (fdctrl_t *fdctrl); +static void fdctrl_reset(FDCtrl *fdctrl, int do_irq); +static void fdctrl_reset_fifo(FDCtrl *fdctrl); static int fdctrl_transfer_handler (void *opaque, int nchan, int dma_pos, int dma_len); -static void fdctrl_raise_irq (fdctrl_t *fdctrl, uint8_t status0); +static void fdctrl_raise_irq(FDCtrl *fdctrl, uint8_t status0); -static uint32_t fdctrl_read_statusA (fdctrl_t *fdctrl); -static uint32_t fdctrl_read_statusB (fdctrl_t *fdctrl); -static uint32_t fdctrl_read_dor (fdctrl_t *fdctrl); -static void fdctrl_write_dor (fdctrl_t *fdctrl, uint32_t value); -static uint32_t fdctrl_read_tape (fdctrl_t *fdctrl); -static void fdctrl_write_tape (fdctrl_t *fdctrl, uint32_t value); -static uint32_t fdctrl_read_main_status (fdctrl_t *fdctrl); -static void fdctrl_write_rate (fdctrl_t *fdctrl, uint32_t value); -static uint32_t fdctrl_read_data (fdctrl_t *fdctrl); -static void fdctrl_write_data (fdctrl_t *fdctrl, uint32_t value); -static uint32_t fdctrl_read_dir (fdctrl_t *fdctrl); +static uint32_t fdctrl_read_statusA(FDCtrl *fdctrl); +static uint32_t fdctrl_read_statusB(FDCtrl *fdctrl); +static uint32_t fdctrl_read_dor(FDCtrl *fdctrl); +static void fdctrl_write_dor(FDCtrl *fdctrl, uint32_t value); +static uint32_t fdctrl_read_tape(FDCtrl *fdctrl); +static void fdctrl_write_tape(FDCtrl *fdctrl, uint32_t value); +static uint32_t fdctrl_read_main_status(FDCtrl *fdctrl); +static void fdctrl_write_rate(FDCtrl *fdctrl, uint32_t value); +static uint32_t fdctrl_read_data(FDCtrl *fdctrl); +static void fdctrl_write_data(FDCtrl *fdctrl, uint32_t value); +static uint32_t fdctrl_read_dir(FDCtrl *fdctrl); enum { FD_DIR_WRITE = 0, @@ -471,7 +471,7 @@ #define FD_DID_SEEK(state) ((state) & FD_STATE_SEEK) #define FD_FORMAT_CMD(state) ((state) & FD_STATE_FORMAT) -struct fdctrl_t { +struct FDCtrl { /* Controller's identification */ uint8_t version; /* HW */ @@ -512,23 +512,25 @@ int sun4m; /* Floppy drives */ uint8_t num_floppies; - fdrive_t drives[MAX_FD]; + FDrive drives[MAX_FD]; int reset_sensei; }; -typedef struct fdctrl_sysbus_t { +typedef struct FDCtrlSysBus { SysBusDevice busdev; - struct fdctrl_t state; -} fdctrl_sysbus_t; + struct FDCtrl state; +} FDCtrlSysBus; -typedef struct fdctrl_isabus_t { +typedef struct FDCtrlISABus { ISADevice busdev; - struct fdctrl_t state; -} fdctrl_isabus_t; + struct FDCtrl state; + int32_t bootindexA; + int32_t bootindexB; +} FDCtrlISABus; static uint32_t fdctrl_read (void *opaque, uint32_t reg) { - fdctrl_t *fdctrl = opaque; + FDCtrl *fdctrl = opaque; uint32_t retval; switch (reg) { @@ -564,7 +566,7 @@ static void fdctrl_write (void *opaque, uint32_t reg, uint32_t value) { - fdctrl_t *fdctrl = opaque; + FDCtrl *fdctrl = opaque; FLOPPY_DPRINTF("write reg%d: 0x%02x\n", reg & 7, value); @@ -637,23 +639,23 @@ .minimum_version_id = 1, .minimum_version_id_old = 1, .fields = (VMStateField []) { - VMSTATE_UINT8(head, fdrive_t), - VMSTATE_UINT8(track, fdrive_t), - VMSTATE_UINT8(sect, fdrive_t), + VMSTATE_UINT8(head, FDrive), + VMSTATE_UINT8(track, FDrive), + VMSTATE_UINT8(sect, FDrive), VMSTATE_END_OF_LIST() } }; static void fdc_pre_save(void *opaque) { - fdctrl_t *s = opaque; + FDCtrl *s = opaque; s->dor_vmstate = s->dor | GET_CUR_DRV(s); } static int fdc_post_load(void *opaque, int version_id) { - fdctrl_t *s = opaque; + FDCtrl *s = opaque; SET_CUR_DRV(s, s->dor_vmstate & FD_DOR_SELMASK); s->dor = s->dor_vmstate & ~FD_DOR_SELMASK; @@ -669,55 +671,56 @@ .post_load = fdc_post_load, .fields = (VMStateField []) { /* Controller State */ - VMSTATE_UINT8(sra, fdctrl_t), - VMSTATE_UINT8(srb, fdctrl_t), - VMSTATE_UINT8(dor_vmstate, fdctrl_t), - VMSTATE_UINT8(tdr, fdctrl_t), - VMSTATE_UINT8(dsr, fdctrl_t), - VMSTATE_UINT8(msr, fdctrl_t), - VMSTATE_UINT8(status0, fdctrl_t), - VMSTATE_UINT8(status1, fdctrl_t), - VMSTATE_UINT8(status2, fdctrl_t), + VMSTATE_UINT8(sra, FDCtrl), + VMSTATE_UINT8(srb, FDCtrl), + VMSTATE_UINT8(dor_vmstate, FDCtrl), + VMSTATE_UINT8(tdr, FDCtrl), + VMSTATE_UINT8(dsr, FDCtrl), + VMSTATE_UINT8(msr, FDCtrl), + VMSTATE_UINT8(status0, FDCtrl), + VMSTATE_UINT8(status1, FDCtrl), + VMSTATE_UINT8(status2, FDCtrl), /* Command FIFO */ - VMSTATE_VARRAY_INT32(fifo, fdctrl_t, fifo_size, 0, vmstate_info_uint8, uint8), - VMSTATE_UINT32(data_pos, fdctrl_t), - VMSTATE_UINT32(data_len, fdctrl_t), - VMSTATE_UINT8(data_state, fdctrl_t), - VMSTATE_UINT8(data_dir, fdctrl_t), - VMSTATE_UINT8(eot, fdctrl_t), + VMSTATE_VARRAY_INT32(fifo, FDCtrl, fifo_size, 0, vmstate_info_uint8, + uint8_t), + VMSTATE_UINT32(data_pos, FDCtrl), + VMSTATE_UINT32(data_len, FDCtrl), + VMSTATE_UINT8(data_state, FDCtrl), + VMSTATE_UINT8(data_dir, FDCtrl), + VMSTATE_UINT8(eot, FDCtrl), /* States kept only to be returned back */ - VMSTATE_UINT8(timer0, fdctrl_t), - VMSTATE_UINT8(timer1, fdctrl_t), - VMSTATE_UINT8(precomp_trk, fdctrl_t), - VMSTATE_UINT8(config, fdctrl_t), - VMSTATE_UINT8(lock, fdctrl_t), - VMSTATE_UINT8(pwrd, fdctrl_t), - VMSTATE_UINT8_EQUAL(num_floppies, fdctrl_t), - VMSTATE_STRUCT_ARRAY(drives, fdctrl_t, MAX_FD, 1, - vmstate_fdrive, fdrive_t), + VMSTATE_UINT8(timer0, FDCtrl), + VMSTATE_UINT8(timer1, FDCtrl), + VMSTATE_UINT8(precomp_trk, FDCtrl), + VMSTATE_UINT8(config, FDCtrl), + VMSTATE_UINT8(lock, FDCtrl), + VMSTATE_UINT8(pwrd, FDCtrl), + VMSTATE_UINT8_EQUAL(num_floppies, FDCtrl), + VMSTATE_STRUCT_ARRAY(drives, FDCtrl, MAX_FD, 1, + vmstate_fdrive, FDrive), VMSTATE_END_OF_LIST() } }; static void fdctrl_external_reset_sysbus(DeviceState *d) { - fdctrl_sysbus_t *sys = container_of(d, fdctrl_sysbus_t, busdev.qdev); - fdctrl_t *s = &sys->state; + FDCtrlSysBus *sys = container_of(d, FDCtrlSysBus, busdev.qdev); + FDCtrl *s = &sys->state; fdctrl_reset(s, 0); } static void fdctrl_external_reset_isa(DeviceState *d) { - fdctrl_isabus_t *isa = container_of(d, fdctrl_isabus_t, busdev.qdev); - fdctrl_t *s = &isa->state; + FDCtrlISABus *isa = container_of(d, FDCtrlISABus, busdev.qdev); + FDCtrl *s = &isa->state; fdctrl_reset(s, 0); } static void fdctrl_handle_tc(void *opaque, int irq, int level) { - //fdctrl_t *s = opaque; + //FDCtrl *s = opaque; if (level) { // XXX @@ -726,13 +729,13 @@ } /* XXX: may change if moved to bdrv */ -int fdctrl_get_drive_type(fdctrl_t *fdctrl, int drive_num) +int fdctrl_get_drive_type(FDCtrl *fdctrl, int drive_num) { return fdctrl->drives[drive_num].drive; } /* Change IRQ state */ -static void fdctrl_reset_irq (fdctrl_t *fdctrl) +static void fdctrl_reset_irq(FDCtrl *fdctrl) { if (!(fdctrl->sra & FD_SRA_INTPEND)) return; @@ -741,7 +744,7 @@ fdctrl->sra &= ~FD_SRA_INTPEND; } -static void fdctrl_raise_irq (fdctrl_t *fdctrl, uint8_t status0) +static void fdctrl_raise_irq(FDCtrl *fdctrl, uint8_t status0) { /* Sparc mutation */ if (fdctrl->sun4m && (fdctrl->msr & FD_MSR_CMDBUSY)) { @@ -761,7 +764,7 @@ } /* Reset controller */ -static void fdctrl_reset (fdctrl_t *fdctrl, int do_irq) +static void fdctrl_reset(FDCtrl *fdctrl, int do_irq) { int i; @@ -790,12 +793,12 @@ } } -static inline fdrive_t *drv0 (fdctrl_t *fdctrl) +static inline FDrive *drv0(FDCtrl *fdctrl) { return &fdctrl->drives[(fdctrl->tdr & FD_TDR_BOOTSEL) >> 2]; } -static inline fdrive_t *drv1 (fdctrl_t *fdctrl) +static inline FDrive *drv1(FDCtrl *fdctrl) { if ((fdctrl->tdr & FD_TDR_BOOTSEL) < (1 << 2)) return &fdctrl->drives[1]; @@ -804,7 +807,7 @@ } #if MAX_FD == 4 -static inline fdrive_t *drv2 (fdctrl_t *fdctrl) +static inline FDrive *drv2(FDCtrl *fdctrl) { if ((fdctrl->tdr & FD_TDR_BOOTSEL) < (2 << 2)) return &fdctrl->drives[2]; @@ -812,7 +815,7 @@ return &fdctrl->drives[1]; } -static inline fdrive_t *drv3 (fdctrl_t *fdctrl) +static inline FDrive *drv3(FDCtrl *fdctrl) { if ((fdctrl->tdr & FD_TDR_BOOTSEL) < (3 << 2)) return &fdctrl->drives[3]; @@ -821,7 +824,7 @@ } #endif -static fdrive_t *get_cur_drv (fdctrl_t *fdctrl) +static FDrive *get_cur_drv(FDCtrl *fdctrl) { switch (fdctrl->cur_drv) { case 0: return drv0(fdctrl); @@ -835,7 +838,7 @@ } /* Status A register : 0x00 (read-only) */ -static uint32_t fdctrl_read_statusA (fdctrl_t *fdctrl) +static uint32_t fdctrl_read_statusA(FDCtrl *fdctrl) { uint32_t retval = fdctrl->sra; @@ -845,7 +848,7 @@ } /* Status B register : 0x01 (read-only) */ -static uint32_t fdctrl_read_statusB (fdctrl_t *fdctrl) +static uint32_t fdctrl_read_statusB(FDCtrl *fdctrl) { uint32_t retval = fdctrl->srb; @@ -855,7 +858,7 @@ } /* Digital output register : 0x02 */ -static uint32_t fdctrl_read_dor (fdctrl_t *fdctrl) +static uint32_t fdctrl_read_dor(FDCtrl *fdctrl) { uint32_t retval = fdctrl->dor; @@ -866,7 +869,7 @@ return retval; } -static void fdctrl_write_dor (fdctrl_t *fdctrl, uint32_t value) +static void fdctrl_write_dor(FDCtrl *fdctrl, uint32_t value) { FLOPPY_DPRINTF("digital output register set to 0x%02x\n", value); @@ -905,7 +908,7 @@ } /* Tape drive register : 0x03 */ -static uint32_t fdctrl_read_tape (fdctrl_t *fdctrl) +static uint32_t fdctrl_read_tape(FDCtrl *fdctrl) { uint32_t retval = fdctrl->tdr; @@ -914,7 +917,7 @@ return retval; } -static void fdctrl_write_tape (fdctrl_t *fdctrl, uint32_t value) +static void fdctrl_write_tape(FDCtrl *fdctrl, uint32_t value) { /* Reset mode */ if (!(fdctrl->dor & FD_DOR_nRESET)) { @@ -928,7 +931,7 @@ } /* Main status register : 0x04 (read) */ -static uint32_t fdctrl_read_main_status (fdctrl_t *fdctrl) +static uint32_t fdctrl_read_main_status(FDCtrl *fdctrl) { uint32_t retval = fdctrl->msr; @@ -947,7 +950,7 @@ } /* Data select rate register : 0x04 (write) */ -static void fdctrl_write_rate (fdctrl_t *fdctrl, uint32_t value) +static void fdctrl_write_rate(FDCtrl *fdctrl, uint32_t value) { /* Reset mode */ if (!(fdctrl->dor & FD_DOR_nRESET)) { @@ -967,7 +970,7 @@ fdctrl->dsr = value; } -static int fdctrl_media_changed(fdrive_t *drv) +static int fdctrl_media_changed(FDrive *drv) { int ret; @@ -981,7 +984,7 @@ } /* Digital input register : 0x07 (read-only) */ -static uint32_t fdctrl_read_dir (fdctrl_t *fdctrl) +static uint32_t fdctrl_read_dir(FDCtrl *fdctrl) { uint32_t retval = 0; @@ -993,14 +996,15 @@ #endif ) retval |= FD_DIR_DSKCHG; - if (retval != 0) + if (retval != 0) { FLOPPY_DPRINTF("Floppy digital input register: 0x%02x\n", retval); + } return retval; } /* FIFO state control */ -static void fdctrl_reset_fifo (fdctrl_t *fdctrl) +static void fdctrl_reset_fifo(FDCtrl *fdctrl) { fdctrl->data_dir = FD_DIR_WRITE; fdctrl->data_pos = 0; @@ -1008,7 +1012,7 @@ } /* Set FIFO status for the host to read */ -static void fdctrl_set_fifo (fdctrl_t *fdctrl, int fifo_len, int do_irq) +static void fdctrl_set_fifo(FDCtrl *fdctrl, int fifo_len, int do_irq) { fdctrl->data_dir = FD_DIR_READ; fdctrl->data_len = fifo_len; @@ -1019,7 +1023,7 @@ } /* Set an error: unimplemented/unknown command */ -static void fdctrl_unimplemented (fdctrl_t *fdctrl, int direction) +static void fdctrl_unimplemented(FDCtrl *fdctrl, int direction) { FLOPPY_ERROR("unimplemented command 0x%02x\n", fdctrl->fifo[0]); fdctrl->fifo[0] = FD_SR0_INVCMD; @@ -1027,7 +1031,7 @@ } /* Seek to next sector */ -static int fdctrl_seek_to_next_sect (fdctrl_t *fdctrl, fdrive_t *cur_drv) +static int fdctrl_seek_to_next_sect(FDCtrl *fdctrl, FDrive *cur_drv) { FLOPPY_DPRINTF("seek to next sector (%d %02x %02x => %d)\n", cur_drv->head, cur_drv->track, cur_drv->sect, @@ -1061,10 +1065,10 @@ } /* Callback for transfer end (stop or abort) */ -static void fdctrl_stop_transfer (fdctrl_t *fdctrl, uint8_t status0, - uint8_t status1, uint8_t status2) +static void fdctrl_stop_transfer(FDCtrl *fdctrl, uint8_t status0, + uint8_t status1, uint8_t status2) { - fdrive_t *cur_drv; + FDrive *cur_drv; cur_drv = get_cur_drv(fdctrl); FLOPPY_DPRINTF("transfer status: %02x %02x %02x (%02x)\n", @@ -1087,9 +1091,9 @@ } /* Prepare a data transfer (either DMA or FIFO) */ -static void fdctrl_start_transfer (fdctrl_t *fdctrl, int direction) +static void fdctrl_start_transfer(FDCtrl *fdctrl, int direction) { - fdrive_t *cur_drv; + FDrive *cur_drv; uint8_t kh, kt, ks; int did_seek = 0; @@ -1100,7 +1104,7 @@ ks = fdctrl->fifo[4]; FLOPPY_DPRINTF("Start transfer at %d %d %02x %02x (%d)\n", GET_CUR_DRV(fdctrl), kh, kt, ks, - _fd_sector(kh, kt, ks, cur_drv->last_sect)); + fd_sector_calc(kh, kt, ks, cur_drv->last_sect)); switch (fd_seek(cur_drv, kh, kt, ks, fdctrl->config & FD_CONFIG_EIS)) { case 2: /* sect too big */ @@ -1189,7 +1193,7 @@ } /* Prepare a transfer of deleted data */ -static void fdctrl_start_transfer_del (fdctrl_t *fdctrl, int direction) +static void fdctrl_start_transfer_del(FDCtrl *fdctrl, int direction) { FLOPPY_ERROR("fdctrl_start_transfer_del() unimplemented\n"); @@ -1203,8 +1207,8 @@ static int fdctrl_transfer_handler (void *opaque, int nchan, int dma_pos, int dma_len) { - fdctrl_t *fdctrl; - fdrive_t *cur_drv; + FDCtrl *fdctrl; + FDrive *cur_drv; int len, start_pos, rel_pos; uint8_t status0 = 0x00, status1 = 0x00, status2 = 0x00; @@ -1310,9 +1314,9 @@ } /* Data register : 0x05 */ -static uint32_t fdctrl_read_data (fdctrl_t *fdctrl) +static uint32_t fdctrl_read_data(FDCtrl *fdctrl) { - fdrive_t *cur_drv; + FDrive *cur_drv; uint32_t retval = 0; int pos; @@ -1358,9 +1362,9 @@ return retval; } -static void fdctrl_format_sector (fdctrl_t *fdctrl) +static void fdctrl_format_sector(FDCtrl *fdctrl) { - fdrive_t *cur_drv; + FDrive *cur_drv; uint8_t kh, kt, ks; SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK); @@ -1370,7 +1374,7 @@ ks = fdctrl->fifo[8]; FLOPPY_DPRINTF("format sector at %d %d %02x %02x (%d)\n", GET_CUR_DRV(fdctrl), kh, kt, ks, - _fd_sector(kh, kt, ks, cur_drv->last_sect)); + fd_sector_calc(kh, kt, ks, cur_drv->last_sect)); switch (fd_seek(cur_drv, kh, kt, ks, fdctrl->config & FD_CONFIG_EIS)) { case 2: /* sect too big */ @@ -1420,16 +1424,16 @@ } } -static void fdctrl_handle_lock (fdctrl_t *fdctrl, int direction) +static void fdctrl_handle_lock(FDCtrl *fdctrl, int direction) { fdctrl->lock = (fdctrl->fifo[0] & 0x80) ? 1 : 0; fdctrl->fifo[0] = fdctrl->lock << 4; fdctrl_set_fifo(fdctrl, 1, fdctrl->lock); } -static void fdctrl_handle_dumpreg (fdctrl_t *fdctrl, int direction) +static void fdctrl_handle_dumpreg(FDCtrl *fdctrl, int direction) { - fdrive_t *cur_drv = get_cur_drv(fdctrl); + FDrive *cur_drv = get_cur_drv(fdctrl); /* Drives position */ fdctrl->fifo[0] = drv0(fdctrl)->track; @@ -1452,22 +1456,22 @@ fdctrl_set_fifo(fdctrl, 10, 0); } -static void fdctrl_handle_version (fdctrl_t *fdctrl, int direction) +static void fdctrl_handle_version(FDCtrl *fdctrl, int direction) { /* Controller's version */ fdctrl->fifo[0] = fdctrl->version; fdctrl_set_fifo(fdctrl, 1, 1); } -static void fdctrl_handle_partid (fdctrl_t *fdctrl, int direction) +static void fdctrl_handle_partid(FDCtrl *fdctrl, int direction) { fdctrl->fifo[0] = 0x41; /* Stepping 1 */ fdctrl_set_fifo(fdctrl, 1, 0); } -static void fdctrl_handle_restore (fdctrl_t *fdctrl, int direction) +static void fdctrl_handle_restore(FDCtrl *fdctrl, int direction) { - fdrive_t *cur_drv = get_cur_drv(fdctrl); + FDrive *cur_drv = get_cur_drv(fdctrl); /* Drives position */ drv0(fdctrl)->track = fdctrl->fifo[3]; @@ -1488,9 +1492,9 @@ fdctrl_reset_fifo(fdctrl); } -static void fdctrl_handle_save (fdctrl_t *fdctrl, int direction) +static void fdctrl_handle_save(FDCtrl *fdctrl, int direction) { - fdrive_t *cur_drv = get_cur_drv(fdctrl); + FDrive *cur_drv = get_cur_drv(fdctrl); fdctrl->fifo[0] = 0; fdctrl->fifo[1] = 0; @@ -1518,9 +1522,9 @@ fdctrl_set_fifo(fdctrl, 15, 1); } -static void fdctrl_handle_readid (fdctrl_t *fdctrl, int direction) +static void fdctrl_handle_readid(FDCtrl *fdctrl, int direction) { - fdrive_t *cur_drv = get_cur_drv(fdctrl); + FDrive *cur_drv = get_cur_drv(fdctrl); /* XXX: should set main status register to busy */ cur_drv->head = (fdctrl->fifo[1] >> 2) & 1; @@ -1528,9 +1532,9 @@ qemu_get_clock(vm_clock) + (get_ticks_per_sec() / 50)); } -static void fdctrl_handle_format_track (fdctrl_t *fdctrl, int direction) +static void fdctrl_handle_format_track(FDCtrl *fdctrl, int direction) { - fdrive_t *cur_drv; + FDrive *cur_drv; SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK); cur_drv = get_cur_drv(fdctrl); @@ -1557,7 +1561,7 @@ fdctrl_stop_transfer(fdctrl, 0x00, 0x00, 0x00); } -static void fdctrl_handle_specify (fdctrl_t *fdctrl, int direction) +static void fdctrl_handle_specify(FDCtrl *fdctrl, int direction) { fdctrl->timer0 = (fdctrl->fifo[1] >> 4) & 0xF; fdctrl->timer1 = fdctrl->fifo[2] >> 1; @@ -1569,9 +1573,9 @@ fdctrl_reset_fifo(fdctrl); } -static void fdctrl_handle_sense_drive_status (fdctrl_t *fdctrl, int direction) +static void fdctrl_handle_sense_drive_status(FDCtrl *fdctrl, int direction) { - fdrive_t *cur_drv; + FDrive *cur_drv; SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK); cur_drv = get_cur_drv(fdctrl); @@ -1585,9 +1589,9 @@ fdctrl_set_fifo(fdctrl, 1, 0); } -static void fdctrl_handle_recalibrate (fdctrl_t *fdctrl, int direction) +static void fdctrl_handle_recalibrate(FDCtrl *fdctrl, int direction) { - fdrive_t *cur_drv; + FDrive *cur_drv; SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK); cur_drv = get_cur_drv(fdctrl); @@ -1597,9 +1601,9 @@ fdctrl_raise_irq(fdctrl, FD_SR0_SEEK); } -static void fdctrl_handle_sense_interrupt_status (fdctrl_t *fdctrl, int direction) +static void fdctrl_handle_sense_interrupt_status(FDCtrl *fdctrl, int direction) { - fdrive_t *cur_drv = get_cur_drv(fdctrl); + FDrive *cur_drv = get_cur_drv(fdctrl); if(fdctrl->reset_sensei > 0) { fdctrl->fifo[0] = @@ -1619,9 +1623,9 @@ fdctrl->status0 = FD_SR0_RDYCHG; } -static void fdctrl_handle_seek (fdctrl_t *fdctrl, int direction) +static void fdctrl_handle_seek(FDCtrl *fdctrl, int direction) { - fdrive_t *cur_drv; + FDrive *cur_drv; SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK); cur_drv = get_cur_drv(fdctrl); @@ -1635,9 +1639,9 @@ } } -static void fdctrl_handle_perpendicular_mode (fdctrl_t *fdctrl, int direction) +static void fdctrl_handle_perpendicular_mode(FDCtrl *fdctrl, int direction) { - fdrive_t *cur_drv = get_cur_drv(fdctrl); + FDrive *cur_drv = get_cur_drv(fdctrl); if (fdctrl->fifo[1] & 0x80) cur_drv->perpendicular = fdctrl->fifo[1] & 0x7; @@ -1645,7 +1649,7 @@ fdctrl_reset_fifo(fdctrl); } -static void fdctrl_handle_configure (fdctrl_t *fdctrl, int direction) +static void fdctrl_handle_configure(FDCtrl *fdctrl, int direction) { fdctrl->config = fdctrl->fifo[2]; fdctrl->precomp_trk = fdctrl->fifo[3]; @@ -1653,22 +1657,22 @@ fdctrl_reset_fifo(fdctrl); } -static void fdctrl_handle_powerdown_mode (fdctrl_t *fdctrl, int direction) +static void fdctrl_handle_powerdown_mode(FDCtrl *fdctrl, int direction) { fdctrl->pwrd = fdctrl->fifo[1]; fdctrl->fifo[0] = fdctrl->fifo[1]; fdctrl_set_fifo(fdctrl, 1, 1); } -static void fdctrl_handle_option (fdctrl_t *fdctrl, int direction) +static void fdctrl_handle_option(FDCtrl *fdctrl, int direction) { /* No result back */ fdctrl_reset_fifo(fdctrl); } -static void fdctrl_handle_drive_specification_command (fdctrl_t *fdctrl, int direction) +static void fdctrl_handle_drive_specification_command(FDCtrl *fdctrl, int direction) { - fdrive_t *cur_drv = get_cur_drv(fdctrl); + FDrive *cur_drv = get_cur_drv(fdctrl); if (fdctrl->fifo[fdctrl->data_pos - 1] & 0x80) { /* Command parameters done */ @@ -1688,9 +1692,9 @@ } } -static void fdctrl_handle_relative_seek_out (fdctrl_t *fdctrl, int direction) +static void fdctrl_handle_relative_seek_out(FDCtrl *fdctrl, int direction) { - fdrive_t *cur_drv; + FDrive *cur_drv; SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK); cur_drv = get_cur_drv(fdctrl); @@ -1704,9 +1708,9 @@ fdctrl_raise_irq(fdctrl, FD_SR0_SEEK); } -static void fdctrl_handle_relative_seek_in (fdctrl_t *fdctrl, int direction) +static void fdctrl_handle_relative_seek_in(FDCtrl *fdctrl, int direction) { - fdrive_t *cur_drv; + FDrive *cur_drv; SET_CUR_DRV(fdctrl, fdctrl->fifo[1] & FD_DOR_SELMASK); cur_drv = get_cur_drv(fdctrl); @@ -1725,7 +1729,7 @@ uint8_t mask; const char* name; int parameters; - void (*handler)(fdctrl_t *fdctrl, int direction); + void (*handler)(FDCtrl *fdctrl, int direction); int direction; } handlers[] = { { FD_CMD_READ, 0x1f, "READ", 8, fdctrl_start_transfer, FD_DIR_READ }, @@ -1764,9 +1768,9 @@ /* Associate command to an index in the 'handlers' array */ static uint8_t command_to_handler[256]; -static void fdctrl_write_data (fdctrl_t *fdctrl, uint32_t value) +static void fdctrl_write_data(FDCtrl *fdctrl, uint32_t value) { - fdrive_t *cur_drv; + FDrive *cur_drv; int pos; /* Reset mode */ @@ -1831,8 +1835,8 @@ static void fdctrl_result_timer(void *opaque) { - fdctrl_t *fdctrl = opaque; - fdrive_t *cur_drv = get_cur_drv(fdctrl); + FDCtrl *fdctrl = opaque; + FDrive *cur_drv = get_cur_drv(fdctrl); /* Pretend we are spinning. * This is needed for Coherent, which uses READ ID to check for @@ -1845,49 +1849,65 @@ } /* Init functions */ -static void fdctrl_connect_drives(fdctrl_t *fdctrl) +static int fdctrl_connect_drives(FDCtrl *fdctrl) { unsigned int i; + FDrive *drive; for (i = 0; i < MAX_FD; i++) { - fd_init(&fdctrl->drives[i]); - fd_revalidate(&fdctrl->drives[i]); + drive = &fdctrl->drives[i]; + + if (drive->bs) { + if (bdrv_get_on_error(drive->bs, 0) != BLOCK_ERR_STOP_ENOSPC) { + error_report("fdc doesn't support drive option werror"); + return -1; + } + if (bdrv_get_on_error(drive->bs, 1) != BLOCK_ERR_REPORT) { + error_report("fdc doesn't support drive option rerror"); + return -1; + } + } + + fd_init(drive); + fd_revalidate(drive); + if (drive->bs) { + bdrv_set_removable(drive->bs, 1); + } } + return 0; } -fdctrl_t *fdctrl_init_isa(DriveInfo **fds) +FDCtrl *fdctrl_init_isa(DriveInfo **fds) { ISADevice *dev; dev = isa_create("isa-fdc"); if (fds[0]) { - qdev_prop_set_drive(&dev->qdev, "driveA", fds[0]); + qdev_prop_set_drive_nofail(&dev->qdev, "driveA", fds[0]->bdrv); } if (fds[1]) { - qdev_prop_set_drive(&dev->qdev, "driveB", fds[1]); + qdev_prop_set_drive_nofail(&dev->qdev, "driveB", fds[1]->bdrv); } - if (qdev_init(&dev->qdev) < 0) - return NULL; - return &(DO_UPCAST(fdctrl_isabus_t, busdev, dev)->state); + qdev_init_nofail(&dev->qdev); + return &(DO_UPCAST(FDCtrlISABus, busdev, dev)->state); } -fdctrl_t *fdctrl_init_sysbus(qemu_irq irq, int dma_chann, - target_phys_addr_t mmio_base, - DriveInfo **fds) +FDCtrl *fdctrl_init_sysbus(qemu_irq irq, int dma_chann, + target_phys_addr_t mmio_base, DriveInfo **fds) { - fdctrl_t *fdctrl; + FDCtrl *fdctrl; DeviceState *dev; - fdctrl_sysbus_t *sys; + FDCtrlSysBus *sys; dev = qdev_create(NULL, "sysbus-fdc"); - sys = DO_UPCAST(fdctrl_sysbus_t, busdev.qdev, dev); + sys = DO_UPCAST(FDCtrlSysBus, busdev.qdev, dev); fdctrl = &sys->state; fdctrl->dma_chann = dma_chann; /* FIXME */ if (fds[0]) { - qdev_prop_set_drive(dev, "driveA", fds[0]); + qdev_prop_set_drive_nofail(dev, "driveA", fds[0]->bdrv); } if (fds[1]) { - qdev_prop_set_drive(dev, "driveB", fds[1]); + qdev_prop_set_drive_nofail(dev, "driveB", fds[1]->bdrv); } qdev_init_nofail(dev); sysbus_connect_irq(&sys->busdev, 0, irq); @@ -1896,19 +1916,19 @@ return fdctrl; } -fdctrl_t *sun4m_fdctrl_init (qemu_irq irq, target_phys_addr_t io_base, - DriveInfo **fds, qemu_irq *fdc_tc) +FDCtrl *sun4m_fdctrl_init(qemu_irq irq, target_phys_addr_t io_base, + DriveInfo **fds, qemu_irq *fdc_tc) { DeviceState *dev; - fdctrl_sysbus_t *sys; - fdctrl_t *fdctrl; + FDCtrlSysBus *sys; + FDCtrl *fdctrl; dev = qdev_create(NULL, "SUNW,fdtwo"); if (fds[0]) { - qdev_prop_set_drive(dev, "drive", fds[0]); + qdev_prop_set_drive_nofail(dev, "drive", fds[0]->bdrv); } qdev_init_nofail(dev); - sys = DO_UPCAST(fdctrl_sysbus_t, busdev.qdev, dev); + sys = DO_UPCAST(FDCtrlSysBus, busdev.qdev, dev); fdctrl = &sys->state; sysbus_connect_irq(&sys->busdev, 0, irq); sysbus_mmio_map(&sys->busdev, 0, io_base); @@ -1917,7 +1937,7 @@ return fdctrl; } -static int fdctrl_init_common(fdctrl_t *fdctrl, target_phys_addr_t io_base) +static int fdctrl_init_common(FDCtrl *fdctrl) { int i, j; static int command_tables_inited = 0; @@ -1946,16 +1966,13 @@ if (fdctrl->dma_chann != -1) DMA_register_channel(fdctrl->dma_chann, &fdctrl_transfer_handler, fdctrl); - fdctrl_connect_drives(fdctrl); - - vmstate_register(io_base, &vmstate_fdc, fdctrl); - return 0; + return fdctrl_connect_drives(fdctrl); } static int isabus_fdc_init1(ISADevice *dev) { - fdctrl_isabus_t *isa = DO_UPCAST(fdctrl_isabus_t, busdev, dev); - fdctrl_t *fdctrl = &isa->state; + FDCtrlISABus *isa = DO_UPCAST(FDCtrlISABus, busdev, dev); + FDCtrl *fdctrl = &isa->state; int iobase = 0x3f0; int isairq = 6; int dma_chann = 2; @@ -1969,68 +1986,104 @@ &fdctrl_write_port, fdctrl); register_ioport_write(iobase + 0x07, 1, 1, &fdctrl_write_port, fdctrl); + isa_init_ioport_range(dev, iobase, 6); + isa_init_ioport(dev, iobase + 7); + isa_init_irq(&isa->busdev, &fdctrl->irq, isairq); fdctrl->dma_chann = dma_chann; - ret = fdctrl_init_common(fdctrl, iobase); + qdev_set_legacy_instance_id(&dev->qdev, iobase, 2); + ret = fdctrl_init_common(fdctrl); + + add_boot_device_path(isa->bootindexA, &dev->qdev, "/floppy@0"); + add_boot_device_path(isa->bootindexB, &dev->qdev, "/floppy@1"); return ret; } static int sysbus_fdc_init1(SysBusDevice *dev) { - fdctrl_sysbus_t *sys = DO_UPCAST(fdctrl_sysbus_t, busdev, dev); - fdctrl_t *fdctrl = &sys->state; + FDCtrlSysBus *sys = DO_UPCAST(FDCtrlSysBus, busdev, dev); + FDCtrl *fdctrl = &sys->state; int io; int ret; - io = cpu_register_io_memory(fdctrl_mem_read, fdctrl_mem_write, fdctrl); + io = cpu_register_io_memory(fdctrl_mem_read, fdctrl_mem_write, fdctrl, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, 0x08, io); sysbus_init_irq(dev, &fdctrl->irq); qdev_init_gpio_in(&dev->qdev, fdctrl_handle_tc, 1); fdctrl->dma_chann = -1; - ret = fdctrl_init_common(fdctrl, io); + qdev_set_legacy_instance_id(&dev->qdev, io, 2); + ret = fdctrl_init_common(fdctrl); return ret; } static int sun4m_fdc_init1(SysBusDevice *dev) { - fdctrl_t *fdctrl = &(FROM_SYSBUS(fdctrl_sysbus_t, dev)->state); + FDCtrl *fdctrl = &(FROM_SYSBUS(FDCtrlSysBus, dev)->state); int io; io = cpu_register_io_memory(fdctrl_mem_read_strict, - fdctrl_mem_write_strict, fdctrl); + fdctrl_mem_write_strict, fdctrl, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, 0x08, io); sysbus_init_irq(dev, &fdctrl->irq); qdev_init_gpio_in(&dev->qdev, fdctrl_handle_tc, 1); fdctrl->sun4m = 1; - return fdctrl_init_common(fdctrl, io); + qdev_set_legacy_instance_id(&dev->qdev, io, 2); + return fdctrl_init_common(fdctrl); } +static const VMStateDescription vmstate_isa_fdc ={ + .name = "fdc", + .version_id = 2, + .minimum_version_id = 2, + .fields = (VMStateField []) { + VMSTATE_STRUCT(state, FDCtrlISABus, 0, vmstate_fdc, FDCtrl), + VMSTATE_END_OF_LIST() + } +}; + static ISADeviceInfo isa_fdc_info = { .init = isabus_fdc_init1, .qdev.name = "isa-fdc", - .qdev.size = sizeof(fdctrl_isabus_t), + .qdev.fw_name = "fdc", + .qdev.size = sizeof(FDCtrlISABus), .qdev.no_user = 1, + .qdev.vmsd = &vmstate_isa_fdc, .qdev.reset = fdctrl_external_reset_isa, .qdev.props = (Property[]) { - DEFINE_PROP_DRIVE("driveA", fdctrl_isabus_t, state.drives[0].dinfo), - DEFINE_PROP_DRIVE("driveB", fdctrl_isabus_t, state.drives[1].dinfo), + DEFINE_PROP_DRIVE("driveA", FDCtrlISABus, state.drives[0].bs), + DEFINE_PROP_DRIVE("driveB", FDCtrlISABus, state.drives[1].bs), + DEFINE_PROP_INT32("bootindexA", FDCtrlISABus, bootindexA, -1), + DEFINE_PROP_INT32("bootindexB", FDCtrlISABus, bootindexB, -1), DEFINE_PROP_END_OF_LIST(), }, }; +static const VMStateDescription vmstate_sysbus_fdc ={ + .name = "fdc", + .version_id = 2, + .minimum_version_id = 2, + .fields = (VMStateField []) { + VMSTATE_STRUCT(state, FDCtrlSysBus, 0, vmstate_fdc, FDCtrl), + VMSTATE_END_OF_LIST() + } +}; + static SysBusDeviceInfo sysbus_fdc_info = { .init = sysbus_fdc_init1, .qdev.name = "sysbus-fdc", - .qdev.size = sizeof(fdctrl_sysbus_t), + .qdev.size = sizeof(FDCtrlSysBus), + .qdev.vmsd = &vmstate_sysbus_fdc, .qdev.reset = fdctrl_external_reset_sysbus, .qdev.props = (Property[]) { - DEFINE_PROP_DRIVE("driveA", fdctrl_sysbus_t, state.drives[0].dinfo), - DEFINE_PROP_DRIVE("driveB", fdctrl_sysbus_t, state.drives[1].dinfo), + DEFINE_PROP_DRIVE("driveA", FDCtrlSysBus, state.drives[0].bs), + DEFINE_PROP_DRIVE("driveB", FDCtrlSysBus, state.drives[1].bs), DEFINE_PROP_END_OF_LIST(), }, }; @@ -2038,10 +2091,11 @@ static SysBusDeviceInfo sun4m_fdc_info = { .init = sun4m_fdc_init1, .qdev.name = "SUNW,fdtwo", - .qdev.size = sizeof(fdctrl_sysbus_t), + .qdev.size = sizeof(FDCtrlSysBus), + .qdev.vmsd = &vmstate_sysbus_fdc, .qdev.reset = fdctrl_external_reset_sysbus, .qdev.props = (Property[]) { - DEFINE_PROP_DRIVE("drive", fdctrl_sysbus_t, state.drives[0].dinfo), + DEFINE_PROP_DRIVE("drive", FDCtrlSysBus, state.drives[0].bs), DEFINE_PROP_END_OF_LIST(), }, }; diff -Nru qemu-kvm-0.12.5+noroms/hw/fdc.h qemu-kvm-0.14.1/hw/fdc.h --- qemu-kvm-0.12.5+noroms/hw/fdc.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/fdc.h 2011-05-11 13:29:46.000000000 +0000 @@ -1,13 +1,16 @@ +#ifndef HW_FDC_H +#define HW_FDC_H + /* fdc.c */ -#include "sysemu.h" #define MAX_FD 2 -typedef struct fdctrl_t fdctrl_t; +typedef struct FDCtrl FDCtrl; + +FDCtrl *fdctrl_init_isa(DriveInfo **fds); +FDCtrl *fdctrl_init_sysbus(qemu_irq irq, int dma_chann, + target_phys_addr_t mmio_base, DriveInfo **fds); +FDCtrl *sun4m_fdctrl_init(qemu_irq irq, target_phys_addr_t io_base, + DriveInfo **fds, qemu_irq *fdc_tc); +int fdctrl_get_drive_type(FDCtrl *fdctrl, int drive_num); -fdctrl_t *fdctrl_init_isa(DriveInfo **fds); -fdctrl_t *fdctrl_init_sysbus(qemu_irq irq, int dma_chann, - target_phys_addr_t mmio_base, - DriveInfo **fds); -fdctrl_t *sun4m_fdctrl_init (qemu_irq irq, target_phys_addr_t io_base, - DriveInfo **fds, qemu_irq *fdc_tc); -int fdctrl_get_drive_type(fdctrl_t *fdctrl, int drive_num); +#endif diff -Nru qemu-kvm-0.12.5+noroms/hw/file-op-9p.h qemu-kvm-0.14.1/hw/file-op-9p.h --- qemu-kvm-0.12.5+noroms/hw/file-op-9p.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/file-op-9p.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,107 @@ +/* + * Virtio 9p + * + * Copyright IBM, Corp. 2010 + * + * Authors: + * Aneesh Kumar K.V + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ +#ifndef _FILEOP_H +#define _FILEOP_H +#include +#include +#include +#include +#include +#include +#include +#define SM_LOCAL_MODE_BITS 0600 +#define SM_LOCAL_DIR_MODE_BITS 0700 + +typedef enum +{ + /* + * Server will try to set uid/gid. + * On failure ignore the error. + */ + SM_NONE = 0, + /* + * uid/gid set on fileserver files + */ + SM_PASSTHROUGH = 1, + /* + * uid/gid part of xattr + */ + SM_MAPPED, +} SecModel; + +typedef struct FsCred +{ + uid_t fc_uid; + gid_t fc_gid; + mode_t fc_mode; + dev_t fc_rdev; +} FsCred; + +struct xattr_operations; + +typedef struct FsContext +{ + char *fs_root; + SecModel fs_sm; + uid_t uid; + struct xattr_operations **xops; +} FsContext; + +void cred_init(FsCred *); + +typedef struct FileOperations +{ + int (*lstat)(FsContext *, const char *, struct stat *); + ssize_t (*readlink)(FsContext *, const char *, char *, size_t); + int (*chmod)(FsContext *, const char *, FsCred *); + int (*chown)(FsContext *, const char *, FsCred *); + int (*mknod)(FsContext *, const char *, FsCred *); + int (*utimensat)(FsContext *, const char *, const struct timespec *); + int (*remove)(FsContext *, const char *); + int (*symlink)(FsContext *, const char *, const char *, FsCred *); + int (*link)(FsContext *, const char *, const char *); + int (*setuid)(FsContext *, uid_t); + int (*close)(FsContext *, int); + int (*closedir)(FsContext *, DIR *); + DIR *(*opendir)(FsContext *, const char *); + int (*open)(FsContext *, const char *, int); + int (*open2)(FsContext *, const char *, int, FsCred *); + void (*rewinddir)(FsContext *, DIR *); + off_t (*telldir)(FsContext *, DIR *); + struct dirent *(*readdir)(FsContext *, DIR *); + void (*seekdir)(FsContext *, DIR *, off_t); + ssize_t (*preadv)(FsContext *, int, const struct iovec *, int, off_t); + ssize_t (*pwritev)(FsContext *, int, const struct iovec *, int, off_t); + int (*mkdir)(FsContext *, const char *, FsCred *); + int (*fstat)(FsContext *, int, struct stat *); + int (*rename)(FsContext *, const char *, const char *); + int (*truncate)(FsContext *, const char *, off_t); + int (*fsync)(FsContext *, int, int); + int (*statfs)(FsContext *s, const char *path, struct statfs *stbuf); + ssize_t (*lgetxattr)(FsContext *, const char *, + const char *, void *, size_t); + ssize_t (*llistxattr)(FsContext *, const char *, void *, size_t); + int (*lsetxattr)(FsContext *, const char *, + const char *, void *, size_t, int); + int (*lremovexattr)(FsContext *, const char *, const char *); + void *opaque; +} FileOperations; + +static inline const char *rpath(FsContext *ctx, const char *path) +{ + /* FIXME: so wrong... */ + static char buffer[4096]; + snprintf(buffer, sizeof(buffer), "%s/%s", ctx->fs_root, path); + return buffer; +} +#endif diff -Nru qemu-kvm-0.12.5+noroms/hw/flash.h qemu-kvm-0.14.1/hw/flash.h --- qemu-kvm-0.12.5+noroms/hw/flash.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/flash.h 2011-05-11 13:29:46.000000000 +0000 @@ -6,7 +6,7 @@ BlockDriverState *bs, uint32_t sector_len, int nb_blocs, int width, uint16_t id0, uint16_t id1, - uint16_t id2, uint16_t id3); + uint16_t id2, uint16_t id3, int be); /* pflash_cfi02.c */ pflash_t *pflash_cfi02_register(target_phys_addr_t base, ram_addr_t off, @@ -14,7 +14,8 @@ int nb_blocs, int nb_mappings, int width, uint16_t id0, uint16_t id1, uint16_t id2, uint16_t id3, - uint16_t unlock_addr0, uint16_t unlock_addr1); + uint16_t unlock_addr0, uint16_t unlock_addr1, + int be); /* nand.c */ typedef struct NANDFlashState NANDFlashState; @@ -50,5 +51,4 @@ uint8_t ecc_digest(ECCState *s, uint8_t sample); void ecc_reset(ECCState *s); -void ecc_put(QEMUFile *f, ECCState *s); -void ecc_get(QEMUFile *f, ECCState *s); +extern VMStateDescription vmstate_ecc_state; diff -Nru qemu-kvm-0.12.5+noroms/hw/fmopl.c qemu-kvm-0.14.1/hw/fmopl.c --- qemu-kvm-0.12.5+noroms/hw/fmopl.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/fmopl.c 2011-05-11 13:29:46.000000000 +0000 @@ -1342,8 +1342,9 @@ { if(OPL->keyboardhandler_r) return OPL->keyboardhandler_r(OPL->keyboard_param); - else + else { LOG(LOG_WAR,("OPL:read unmapped KEYBOARD port\n")); + } } return 0; #if 0 @@ -1355,8 +1356,9 @@ { if(OPL->porthandler_r) return OPL->porthandler_r(OPL->port_param); - else + else { LOG(LOG_WAR,("OPL:read unmapped I/O port\n")); + } } return 0; case 0x1a: /* PCM-DATA */ diff -Nru qemu-kvm-0.12.5+noroms/hw/fw_cfg.c qemu-kvm-0.14.1/hw/fw_cfg.c --- qemu-kvm-0.12.5+noroms/hw/fw_cfg.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/fw_cfg.c 2011-05-11 13:29:46.000000000 +0000 @@ -25,6 +25,7 @@ #include "sysemu.h" #include "isa.h" #include "fw_cfg.h" +#include "sysbus.h" /* debug firmware config */ //#define DEBUG_FW_CFG @@ -38,18 +39,21 @@ #define FW_CFG_SIZE 2 -typedef struct _FWCfgEntry { +typedef struct FWCfgEntry { uint32_t len; uint8_t *data; void *callback_opaque; FWCfgCallback callback; } FWCfgEntry; -struct _FWCfgState { +struct FWCfgState { + SysBusDevice busdev; + uint32_t ctl_iobase, data_iobase; FWCfgEntry entries[2][FW_CFG_MAX_ENTRY]; FWCfgFiles *files; uint16_t cur_entry; uint32_t cur_offset; + Notifier machine_ready; }; static void fw_cfg_write(FWCfgState *s, uint8_t value) @@ -158,9 +162,9 @@ NULL, }; -static void fw_cfg_reset(void *opaque) +static void fw_cfg_reset(DeviceState *d) { - FWCfgState *s = opaque; + FWCfgState *s = DO_UPCAST(FWCfgState, busdev.qdev, d); fw_cfg_select(s, 0); } @@ -274,10 +278,9 @@ return 1; } -int fw_cfg_add_file(FWCfgState *s, const char *dir, const char *filename, - uint8_t *data, uint32_t len) +int fw_cfg_add_file(FWCfgState *s, const char *filename, uint8_t *data, + uint32_t len) { - const char *basename; int i, index; if (!s->files) { @@ -294,15 +297,8 @@ fw_cfg_add_bytes(s, FW_CFG_FILE_FIRST + index, data, len); - basename = strrchr(filename, '/'); - if (basename) { - basename++; - } else { - basename = filename; - } - - snprintf(s->files->f[index].name, sizeof(s->files->f[index].name), - "%s/%s", dir, basename); + pstrcpy(s->files->f[index].name, sizeof(s->files->f[index].name), + filename); for (i = 0; i < index; i++) { if (strcmp(s->files->f[index].name, s->files->f[i].name) == 0) { FW_CFG_DPRINTF("%s: skip duplicate: %s\n", __FUNCTION__, @@ -320,30 +316,35 @@ return 1; } +static void fw_cfg_machine_ready(struct Notifier* n) +{ + uint32_t len; + FWCfgState *s = container_of(n, FWCfgState, machine_ready); + char *bootindex = get_boot_devices_list(&len); + + fw_cfg_add_file(s, "bootorder", (uint8_t*)bootindex, len); +} + FWCfgState *fw_cfg_init(uint32_t ctl_port, uint32_t data_port, target_phys_addr_t ctl_addr, target_phys_addr_t data_addr) { + DeviceState *dev; + SysBusDevice *d; FWCfgState *s; - int io_ctl_memory, io_data_memory; - s = qemu_mallocz(sizeof(FWCfgState)); + dev = qdev_create(NULL, "fw_cfg"); + qdev_prop_set_uint32(dev, "ctl_iobase", ctl_port); + qdev_prop_set_uint32(dev, "data_iobase", data_port); + qdev_init_nofail(dev); + d = sysbus_from_qdev(dev); + + s = DO_UPCAST(FWCfgState, busdev.qdev, dev); - if (ctl_port) { - register_ioport_write(ctl_port, 2, 2, fw_cfg_io_writew, s); - } - if (data_port) { - register_ioport_read(data_port, 1, 1, fw_cfg_io_readb, s); - register_ioport_write(data_port, 1, 1, fw_cfg_io_writeb, s); - } if (ctl_addr) { - io_ctl_memory = cpu_register_io_memory(fw_cfg_ctl_mem_read, - fw_cfg_ctl_mem_write, s); - cpu_register_physical_memory(ctl_addr, FW_CFG_SIZE, io_ctl_memory); + sysbus_mmio_map(d, 0, ctl_addr); } if (data_addr) { - io_data_memory = cpu_register_io_memory(fw_cfg_data_mem_read, - fw_cfg_data_mem_write, s); - cpu_register_physical_memory(data_addr, FW_CFG_SIZE, io_data_memory); + sysbus_mmio_map(d, 1, data_addr); } fw_cfg_add_bytes(s, FW_CFG_SIGNATURE, (uint8_t *)"QEMU", 4); fw_cfg_add_bytes(s, FW_CFG_UUID, qemu_uuid, 16); @@ -352,8 +353,55 @@ fw_cfg_add_i16(s, FW_CFG_MAX_CPUS, (uint16_t)max_cpus); fw_cfg_add_i16(s, FW_CFG_BOOT_MENU, (uint16_t)boot_menu); - vmstate_register(-1, &vmstate_fw_cfg, s); - qemu_register_reset(fw_cfg_reset, s); + + s->machine_ready.notify = fw_cfg_machine_ready; + qemu_add_machine_init_done_notifier(&s->machine_ready); return s; } + +static int fw_cfg_init1(SysBusDevice *dev) +{ + FWCfgState *s = FROM_SYSBUS(FWCfgState, dev); + int io_ctl_memory, io_data_memory; + + io_ctl_memory = cpu_register_io_memory(fw_cfg_ctl_mem_read, + fw_cfg_ctl_mem_write, s, + DEVICE_NATIVE_ENDIAN); + sysbus_init_mmio(dev, FW_CFG_SIZE, io_ctl_memory); + + io_data_memory = cpu_register_io_memory(fw_cfg_data_mem_read, + fw_cfg_data_mem_write, s, + DEVICE_NATIVE_ENDIAN); + sysbus_init_mmio(dev, FW_CFG_SIZE, io_data_memory); + + if (s->ctl_iobase) { + register_ioport_write(s->ctl_iobase, 2, 2, fw_cfg_io_writew, s); + } + if (s->data_iobase) { + register_ioport_read(s->data_iobase, 1, 1, fw_cfg_io_readb, s); + register_ioport_write(s->data_iobase, 1, 1, fw_cfg_io_writeb, s); + } + return 0; +} + +static SysBusDeviceInfo fw_cfg_info = { + .init = fw_cfg_init1, + .qdev.name = "fw_cfg", + .qdev.size = sizeof(FWCfgState), + .qdev.vmsd = &vmstate_fw_cfg, + .qdev.reset = fw_cfg_reset, + .qdev.no_user = 1, + .qdev.props = (Property[]) { + DEFINE_PROP_HEX32("ctl_iobase", FWCfgState, ctl_iobase, -1), + DEFINE_PROP_HEX32("data_iobase", FWCfgState, data_iobase, -1), + DEFINE_PROP_END_OF_LIST(), + }, +}; + +static void fw_cfg_register_devices(void) +{ + sysbus_register_withprop(&fw_cfg_info); +} + +device_init(fw_cfg_register_devices) diff -Nru qemu-kvm-0.12.5+noroms/hw/fw_cfg.h qemu-kvm-0.14.1/hw/fw_cfg.h --- qemu-kvm-0.12.5+noroms/hw/fw_cfg.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/fw_cfg.h 2011-05-11 13:29:46.000000000 +0000 @@ -53,15 +53,15 @@ typedef void (*FWCfgCallback)(void *opaque, uint8_t *data); -typedef struct _FWCfgState FWCfgState; +typedef struct FWCfgState FWCfgState; int fw_cfg_add_bytes(FWCfgState *s, uint16_t key, uint8_t *data, uint32_t len); int fw_cfg_add_i16(FWCfgState *s, uint16_t key, uint16_t value); int fw_cfg_add_i32(FWCfgState *s, uint16_t key, uint32_t value); int fw_cfg_add_i64(FWCfgState *s, uint16_t key, uint64_t value); int fw_cfg_add_callback(FWCfgState *s, uint16_t key, FWCfgCallback callback, void *callback_opaque, uint8_t *data, size_t len); -int fw_cfg_add_file(FWCfgState *s, const char *dir, const char *filename, - uint8_t *data, uint32_t len); +int fw_cfg_add_file(FWCfgState *s, const char *filename, uint8_t *data, + uint32_t len); FWCfgState *fw_cfg_init(uint32_t ctl_port, uint32_t data_port, target_phys_addr_t crl_addr, target_phys_addr_t data_addr); diff -Nru qemu-kvm-0.12.5+noroms/hw/g364fb.c qemu-kvm-0.14.1/hw/g364fb.c --- qemu-kvm-0.12.5+noroms/hw/g364fb.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/g364fb.c 2011-05-11 13:29:46.000000000 +0000 @@ -593,12 +593,12 @@ s = qemu_mallocz(sizeof(G364State)); s->vram_size = 8 * 1024 * 1024; - s->vram_offset = qemu_ram_alloc(s->vram_size); + s->vram_offset = qemu_ram_alloc(NULL, "g364fb.vram", s->vram_size); s->vram = qemu_get_ram_ptr(s->vram_offset); s->irq = irq; qemu_register_reset(g364fb_reset, s); - register_savevm("g364fb", 0, 1, g364fb_save, g364fb_load, s); + register_savevm(NULL, "g364fb", 0, 1, g364fb_save, g364fb_load, s); g364fb_reset(s); s->ds = graphic_console_init(g364fb_update_display, @@ -607,7 +607,8 @@ cpu_register_physical_memory(vram_base, s->vram_size, s->vram_offset); - io_ctrl = cpu_register_io_memory(g364fb_ctrl_read, g364fb_ctrl_write, s); + io_ctrl = cpu_register_io_memory(g364fb_ctrl_read, g364fb_ctrl_write, s, + DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(ctrl_base, 0x200000, io_ctrl); return 0; diff -Nru qemu-kvm-0.12.5+noroms/hw/grackle_pci.c qemu-kvm-0.14.1/hw/grackle_pci.c --- qemu-kvm-0.12.5+noroms/hw/grackle_pci.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/grackle_pci.c 2011-05-11 13:29:46.000000000 +0000 @@ -108,31 +108,19 @@ s = FROM_SYSBUS(GrackleState, dev); - pci_mem_config = pci_host_conf_register_mmio(&s->host_state); - pci_mem_data = pci_host_data_register_mmio(&s->host_state); + pci_mem_config = pci_host_conf_register_mmio(&s->host_state, + DEVICE_LITTLE_ENDIAN); + pci_mem_data = pci_host_data_register_mmio(&s->host_state, + DEVICE_LITTLE_ENDIAN); sysbus_init_mmio(dev, 0x1000, pci_mem_config); sysbus_init_mmio(dev, 0x1000, pci_mem_data); - register_savevm("grackle", 0, 1, pci_grackle_save, pci_grackle_load, - &s->host_state); + register_savevm(&dev->qdev, "grackle", 0, 1, pci_grackle_save, + pci_grackle_load, &s->host_state); qemu_register_reset(pci_grackle_reset, &s->host_state); return 0; } -static int pci_dec_21154_init_device(SysBusDevice *dev) -{ - GrackleState *s; - int pci_mem_config, pci_mem_data; - - s = FROM_SYSBUS(GrackleState, dev); - - pci_mem_config = pci_host_conf_register_mmio(&s->host_state); - pci_mem_data = pci_host_data_register_mmio(&s->host_state); - sysbus_init_mmio(dev, 0x1000, pci_mem_config); - sysbus_init_mmio(dev, 0x1000, pci_mem_data); - return 0; -} - static int grackle_pci_host_init(PCIDevice *d) { pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_MOTOROLA); @@ -140,34 +128,6 @@ d->config[0x08] = 0x00; // revision d->config[0x09] = 0x01; pci_config_set_class(d->config, PCI_CLASS_BRIDGE_HOST); - d->config[PCI_HEADER_TYPE] = PCI_HEADER_TYPE_NORMAL; // header_type - return 0; -} - -static int dec_21154_pci_host_init(PCIDevice *d) -{ - /* PCI2PCI bridge same values as PearPC - check this */ - pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_DEC); - pci_config_set_device_id(d->config, PCI_DEVICE_ID_DEC_21154); - d->config[0x08] = 0x02; // revision - pci_config_set_class(d->config, PCI_CLASS_BRIDGE_PCI); - d->config[PCI_HEADER_TYPE] = PCI_HEADER_TYPE_BRIDGE; // header_type - - d->config[0x18] = 0x0; // primary_bus - d->config[0x19] = 0x1; // secondary_bus - d->config[0x1a] = 0x1; // subordinate_bus - d->config[0x1c] = 0x10; // io_base - d->config[0x1d] = 0x20; // io_limit - - d->config[0x20] = 0x80; // memory_base - d->config[0x21] = 0x80; - d->config[0x22] = 0x90; // memory_limit - d->config[0x23] = 0x80; - - d->config[0x24] = 0x00; // prefetchable_memory_base - d->config[0x25] = 0x84; - d->config[0x26] = 0x00; // prefetchable_memory_limit - d->config[0x27] = 0x85; return 0; } @@ -177,20 +137,11 @@ .init = grackle_pci_host_init, }; -static PCIDeviceInfo dec_21154_pci_host_info = { - .qdev.name = "dec-21154", - .qdev.size = sizeof(PCIDevice), - .init = dec_21154_pci_host_init, -}; - static void grackle_register_devices(void) { sysbus_register_dev("grackle", sizeof(GrackleState), pci_grackle_init_device); pci_qdev_register(&grackle_pci_host_info); - sysbus_register_dev("dec-21154", sizeof(GrackleState), - pci_dec_21154_init_device); - pci_qdev_register(&dec_21154_pci_host_info); } device_init(grackle_register_devices) diff -Nru qemu-kvm-0.12.5+noroms/hw/grlib_apbuart.c qemu-kvm-0.14.1/hw/grlib_apbuart.c --- qemu-kvm-0.12.5+noroms/hw/grlib_apbuart.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/grlib_apbuart.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,187 @@ +/* + * QEMU GRLIB APB UART Emulator + * + * Copyright (c) 2010-2011 AdaCore + * + * 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 "sysbus.h" +#include "qemu-char.h" + +#include "trace.h" + +#define UART_REG_SIZE 20 /* Size of memory mapped registers */ + +/* UART status register fields */ +#define UART_DATA_READY (1 << 0) +#define UART_TRANSMIT_SHIFT_EMPTY (1 << 1) +#define UART_TRANSMIT_FIFO_EMPTY (1 << 2) +#define UART_BREAK_RECEIVED (1 << 3) +#define UART_OVERRUN (1 << 4) +#define UART_PARITY_ERROR (1 << 5) +#define UART_FRAMING_ERROR (1 << 6) +#define UART_TRANSMIT_FIFO_HALF (1 << 7) +#define UART_RECEIVE_FIFO_HALF (1 << 8) +#define UART_TRANSMIT_FIFO_FULL (1 << 9) +#define UART_RECEIVE_FIFO_FULL (1 << 10) + +/* UART control register fields */ +#define UART_RECEIVE_ENABLE (1 << 0) +#define UART_TRANSMIT_ENABLE (1 << 1) +#define UART_RECEIVE_INTERRUPT (1 << 2) +#define UART_TRANSMIT_INTERRUPT (1 << 3) +#define UART_PARITY_SELECT (1 << 4) +#define UART_PARITY_ENABLE (1 << 5) +#define UART_FLOW_CONTROL (1 << 6) +#define UART_LOOPBACK (1 << 7) +#define UART_EXTERNAL_CLOCK (1 << 8) +#define UART_RECEIVE_FIFO_INTERRUPT (1 << 9) +#define UART_TRANSMIT_FIFO_INTERRUPT (1 << 10) +#define UART_FIFO_DEBUG_MODE (1 << 11) +#define UART_OUTPUT_ENABLE (1 << 12) +#define UART_FIFO_AVAILABLE (1 << 31) + +/* Memory mapped register offsets */ +#define DATA_OFFSET 0x00 +#define STATUS_OFFSET 0x04 +#define CONTROL_OFFSET 0x08 +#define SCALER_OFFSET 0x0C /* not supported */ +#define FIFO_DEBUG_OFFSET 0x10 /* not supported */ + +typedef struct UART { + SysBusDevice busdev; + + qemu_irq irq; + + CharDriverState *chr; + + /* registers */ + uint32_t receive; + uint32_t status; + uint32_t control; +} UART; + +static int grlib_apbuart_can_receive(void *opaque) +{ + UART *uart = opaque; + + return !!(uart->status & UART_DATA_READY); +} + +static void grlib_apbuart_receive(void *opaque, const uint8_t *buf, int size) +{ + UART *uart = opaque; + + uart->receive = *buf; + uart->status |= UART_DATA_READY; + + if (uart->control & UART_RECEIVE_INTERRUPT) { + qemu_irq_pulse(uart->irq); + } +} + +static void grlib_apbuart_event(void *opaque, int event) +{ + trace_grlib_apbuart_event(event); +} + +static void +grlib_apbuart_writel(void *opaque, target_phys_addr_t addr, uint32_t value) +{ + UART *uart = opaque; + unsigned char c = 0; + + addr &= 0xff; + + /* Unit registers */ + switch (addr) { + case DATA_OFFSET: + c = value & 0xFF; + qemu_chr_write(uart->chr, &c, 1); + return; + + case STATUS_OFFSET: + /* Read Only */ + return; + + case CONTROL_OFFSET: + /* Not supported */ + return; + + case SCALER_OFFSET: + /* Not supported */ + return; + + default: + break; + } + + trace_grlib_apbuart_unknown_register("write", addr); +} + +static CPUReadMemoryFunc * const grlib_apbuart_read[] = { + NULL, NULL, NULL, +}; + +static CPUWriteMemoryFunc * const grlib_apbuart_write[] = { + NULL, NULL, grlib_apbuart_writel, +}; + +static int grlib_apbuart_init(SysBusDevice *dev) +{ + UART *uart = FROM_SYSBUS(typeof(*uart), dev); + int uart_regs = 0; + + qemu_chr_add_handlers(uart->chr, + grlib_apbuart_can_receive, + grlib_apbuart_receive, + grlib_apbuart_event, + uart); + + sysbus_init_irq(dev, &uart->irq); + + uart_regs = cpu_register_io_memory(grlib_apbuart_read, + grlib_apbuart_write, + uart, DEVICE_NATIVE_ENDIAN); + if (uart_regs < 0) { + return -1; + } + + sysbus_init_mmio(dev, UART_REG_SIZE, uart_regs); + + return 0; +} + +static SysBusDeviceInfo grlib_gptimer_info = { + .init = grlib_apbuart_init, + .qdev.name = "grlib,apbuart", + .qdev.size = sizeof(UART), + .qdev.props = (Property[]) { + DEFINE_PROP_CHR("chrdev", UART, chr), + DEFINE_PROP_END_OF_LIST() + } +}; + +static void grlib_gptimer_register(void) +{ + sysbus_register_withprop(&grlib_gptimer_info); +} + +device_init(grlib_gptimer_register) diff -Nru qemu-kvm-0.12.5+noroms/hw/grlib_gptimer.c qemu-kvm-0.14.1/hw/grlib_gptimer.c --- qemu-kvm-0.12.5+noroms/hw/grlib_gptimer.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/grlib_gptimer.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,395 @@ +/* + * QEMU GRLIB GPTimer Emulator + * + * Copyright (c) 2010-2011 AdaCore + * + * 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 "sysbus.h" +#include "qemu-timer.h" + +#include "trace.h" + +#define UNIT_REG_SIZE 16 /* Size of memory mapped regs for the unit */ +#define GPTIMER_REG_SIZE 16 /* Size of memory mapped regs for a GPTimer */ + +#define GPTIMER_MAX_TIMERS 8 + +/* GPTimer Config register fields */ +#define GPTIMER_ENABLE (1 << 0) +#define GPTIMER_RESTART (1 << 1) +#define GPTIMER_LOAD (1 << 2) +#define GPTIMER_INT_ENABLE (1 << 3) +#define GPTIMER_INT_PENDING (1 << 4) +#define GPTIMER_CHAIN (1 << 5) /* Not supported */ +#define GPTIMER_DEBUG_HALT (1 << 6) /* Not supported */ + +/* Memory mapped register offsets */ +#define SCALER_OFFSET 0x00 +#define SCALER_RELOAD_OFFSET 0x04 +#define CONFIG_OFFSET 0x08 +#define COUNTER_OFFSET 0x00 +#define COUNTER_RELOAD_OFFSET 0x04 +#define TIMER_BASE 0x10 + +typedef struct GPTimer GPTimer; +typedef struct GPTimerUnit GPTimerUnit; + +struct GPTimer { + QEMUBH *bh; + struct ptimer_state *ptimer; + + qemu_irq irq; + int id; + GPTimerUnit *unit; + + /* registers */ + uint32_t counter; + uint32_t reload; + uint32_t config; +}; + +struct GPTimerUnit { + SysBusDevice busdev; + + uint32_t nr_timers; /* Number of timers available */ + uint32_t freq_hz; /* System frequency */ + uint32_t irq_line; /* Base irq line */ + + GPTimer *timers; + + /* registers */ + uint32_t scaler; + uint32_t reload; + uint32_t config; +}; + +static void grlib_gptimer_enable(GPTimer *timer) +{ + assert(timer != NULL); + + + ptimer_stop(timer->ptimer); + + if (!(timer->config & GPTIMER_ENABLE)) { + /* Timer disabled */ + trace_grlib_gptimer_disabled(timer->id, timer->config); + return; + } + + /* ptimer is triggered when the counter reach 0 but GPTimer is triggered at + underflow. Set count + 1 to simulate the GPTimer behavior. */ + + trace_grlib_gptimer_enable(timer->id, timer->counter + 1); + + ptimer_set_count(timer->ptimer, timer->counter + 1); + ptimer_run(timer->ptimer, 1); +} + +static void grlib_gptimer_restart(GPTimer *timer) +{ + assert(timer != NULL); + + trace_grlib_gptimer_restart(timer->id, timer->reload); + + timer->counter = timer->reload; + grlib_gptimer_enable(timer); +} + +static void grlib_gptimer_set_scaler(GPTimerUnit *unit, uint32_t scaler) +{ + int i = 0; + uint32_t value = 0; + + assert(unit != NULL); + + if (scaler > 0) { + value = unit->freq_hz / (scaler + 1); + } else { + value = unit->freq_hz; + } + + trace_grlib_gptimer_set_scaler(scaler, value); + + for (i = 0; i < unit->nr_timers; i++) { + ptimer_set_freq(unit->timers[i].ptimer, value); + } +} + +static void grlib_gptimer_hit(void *opaque) +{ + GPTimer *timer = opaque; + assert(timer != NULL); + + trace_grlib_gptimer_hit(timer->id); + + /* Timer expired */ + + if (timer->config & GPTIMER_INT_ENABLE) { + /* Set the pending bit (only unset by write in the config register) */ + timer->config |= GPTIMER_INT_PENDING; + qemu_irq_pulse(timer->irq); + } + + if (timer->config & GPTIMER_RESTART) { + grlib_gptimer_restart(timer); + } +} + +static uint32_t grlib_gptimer_readl(void *opaque, target_phys_addr_t addr) +{ + GPTimerUnit *unit = opaque; + target_phys_addr_t timer_addr; + int id; + uint32_t value = 0; + + addr &= 0xff; + + /* Unit registers */ + switch (addr) { + case SCALER_OFFSET: + trace_grlib_gptimer_readl(-1, "scaler:", unit->scaler); + return unit->scaler; + + case SCALER_RELOAD_OFFSET: + trace_grlib_gptimer_readl(-1, "reload:", unit->reload); + return unit->reload; + + case CONFIG_OFFSET: + trace_grlib_gptimer_readl(-1, "config:", unit->config); + return unit->config; + + default: + break; + } + + timer_addr = (addr % TIMER_BASE); + id = (addr - TIMER_BASE) / TIMER_BASE; + + if (id >= 0 && id < unit->nr_timers) { + + /* GPTimer registers */ + switch (timer_addr) { + case COUNTER_OFFSET: + value = ptimer_get_count(unit->timers[id].ptimer); + trace_grlib_gptimer_readl(id, "counter value:", value); + return value; + + case COUNTER_RELOAD_OFFSET: + value = unit->timers[id].reload; + trace_grlib_gptimer_readl(id, "reload value:", value); + return value; + + case CONFIG_OFFSET: + trace_grlib_gptimer_readl(id, "scaler value:", + unit->timers[id].config); + return unit->timers[id].config; + + default: + break; + } + + } + + trace_grlib_gptimer_unknown_register("read", addr); + return 0; +} + +static void +grlib_gptimer_writel(void *opaque, target_phys_addr_t addr, uint32_t value) +{ + GPTimerUnit *unit = opaque; + target_phys_addr_t timer_addr; + int id; + + addr &= 0xff; + + /* Unit registers */ + switch (addr) { + case SCALER_OFFSET: + value &= 0xFFFF; /* clean up the value */ + unit->scaler = value; + trace_grlib_gptimer_writel(-1, "scaler:", unit->scaler); + return; + + case SCALER_RELOAD_OFFSET: + value &= 0xFFFF; /* clean up the value */ + unit->reload = value; + trace_grlib_gptimer_writel(-1, "reload:", unit->reload); + grlib_gptimer_set_scaler(unit, value); + return; + + case CONFIG_OFFSET: + /* Read Only (disable timer freeze not supported) */ + trace_grlib_gptimer_writel(-1, "config (Read Only):", 0); + return; + + default: + break; + } + + timer_addr = (addr % TIMER_BASE); + id = (addr - TIMER_BASE) / TIMER_BASE; + + if (id >= 0 && id < unit->nr_timers) { + + /* GPTimer registers */ + switch (timer_addr) { + case COUNTER_OFFSET: + trace_grlib_gptimer_writel(id, "counter:", value); + unit->timers[id].counter = value; + grlib_gptimer_enable(&unit->timers[id]); + return; + + case COUNTER_RELOAD_OFFSET: + trace_grlib_gptimer_writel(id, "reload:", value); + unit->timers[id].reload = value; + return; + + case CONFIG_OFFSET: + trace_grlib_gptimer_writel(id, "config:", value); + + if (value & GPTIMER_INT_PENDING) { + /* clear pending bit */ + value &= ~GPTIMER_INT_PENDING; + } else { + /* keep pending bit */ + value |= unit->timers[id].config & GPTIMER_INT_PENDING; + } + + unit->timers[id].config = value; + + /* gptimer_restart calls gptimer_enable, so if "enable" and "load" + bits are present, we just have to call restart. */ + + if (value & GPTIMER_LOAD) { + grlib_gptimer_restart(&unit->timers[id]); + } else if (value & GPTIMER_ENABLE) { + grlib_gptimer_enable(&unit->timers[id]); + } + + /* These fields must always be read as 0 */ + value &= ~(GPTIMER_LOAD & GPTIMER_DEBUG_HALT); + + unit->timers[id].config = value; + return; + + default: + break; + } + + } + + trace_grlib_gptimer_unknown_register("write", addr); +} + +static CPUReadMemoryFunc * const grlib_gptimer_read[] = { + NULL, NULL, grlib_gptimer_readl, +}; + +static CPUWriteMemoryFunc * const grlib_gptimer_write[] = { + NULL, NULL, grlib_gptimer_writel, +}; + +static void grlib_gptimer_reset(DeviceState *d) +{ + GPTimerUnit *unit = container_of(d, GPTimerUnit, busdev.qdev); + int i = 0; + + assert(unit != NULL); + + unit->scaler = 0; + unit->reload = 0; + unit->config = 0; + + unit->config = unit->nr_timers; + unit->config |= unit->irq_line << 3; + unit->config |= 1 << 8; /* separate interrupt */ + unit->config |= 1 << 9; /* Disable timer freeze */ + + + for (i = 0; i < unit->nr_timers; i++) { + GPTimer *timer = &unit->timers[i]; + + timer->counter = 0; + timer->reload = 0; + timer->config = 0; + ptimer_stop(timer->ptimer); + ptimer_set_count(timer->ptimer, 0); + ptimer_set_freq(timer->ptimer, unit->freq_hz); + } +} + +static int grlib_gptimer_init(SysBusDevice *dev) +{ + GPTimerUnit *unit = FROM_SYSBUS(typeof(*unit), dev); + unsigned int i; + int timer_regs; + + assert(unit->nr_timers > 0); + assert(unit->nr_timers <= GPTIMER_MAX_TIMERS); + + unit->timers = qemu_mallocz(sizeof unit->timers[0] * unit->nr_timers); + + for (i = 0; i < unit->nr_timers; i++) { + GPTimer *timer = &unit->timers[i]; + + timer->unit = unit; + timer->bh = qemu_bh_new(grlib_gptimer_hit, timer); + timer->ptimer = ptimer_init(timer->bh); + timer->id = i; + + /* One IRQ line for each timer */ + sysbus_init_irq(dev, &timer->irq); + + ptimer_set_freq(timer->ptimer, unit->freq_hz); + } + + timer_regs = cpu_register_io_memory(grlib_gptimer_read, + grlib_gptimer_write, + unit, DEVICE_NATIVE_ENDIAN); + if (timer_regs < 0) { + return -1; + } + + sysbus_init_mmio(dev, UNIT_REG_SIZE + GPTIMER_REG_SIZE * unit->nr_timers, + timer_regs); + return 0; +} + +static SysBusDeviceInfo grlib_gptimer_info = { + .init = grlib_gptimer_init, + .qdev.name = "grlib,gptimer", + .qdev.reset = grlib_gptimer_reset, + .qdev.size = sizeof(GPTimerUnit), + .qdev.props = (Property[]) { + DEFINE_PROP_UINT32("frequency", GPTimerUnit, freq_hz, 40000000), + DEFINE_PROP_UINT32("irq-line", GPTimerUnit, irq_line, 8), + DEFINE_PROP_UINT32("nr-timers", GPTimerUnit, nr_timers, 2), + DEFINE_PROP_END_OF_LIST() + } +}; + +static void grlib_gptimer_register(void) +{ + sysbus_register_withprop(&grlib_gptimer_info); +} + +device_init(grlib_gptimer_register) diff -Nru qemu-kvm-0.12.5+noroms/hw/grlib.h qemu-kvm-0.14.1/hw/grlib.h --- qemu-kvm-0.12.5+noroms/hw/grlib.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/grlib.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,126 @@ +/* + * QEMU GRLIB Components + * + * Copyright (c) 2010-2011 AdaCore + * + * 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. + */ + +#ifndef _GRLIB_H_ +#define _GRLIB_H_ + +#include "qdev.h" +#include "sysbus.h" + +/* Emulation of GrLib device is base on the GRLIB IP Core User's Manual: + * http://www.gaisler.com/products/grlib/grip.pdf + */ + +/* IRQMP */ + +typedef void (*set_pil_in_fn) (void *opaque, uint32_t pil_in); + +void grlib_irqmp_set_irq(void *opaque, int irq, int level); + +void grlib_irqmp_ack(DeviceState *dev, int intno); + +static inline +DeviceState *grlib_irqmp_create(target_phys_addr_t base, + CPUState *env, + qemu_irq **cpu_irqs, + uint32_t nr_irqs, + set_pil_in_fn set_pil_in) +{ + DeviceState *dev; + + assert(cpu_irqs != NULL); + + dev = qdev_create(NULL, "grlib,irqmp"); + qdev_prop_set_ptr(dev, "set_pil_in", set_pil_in); + qdev_prop_set_ptr(dev, "set_pil_in_opaque", env); + + if (qdev_init(dev)) { + return NULL; + } + + env->irq_manager = dev; + + sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); + + *cpu_irqs = qemu_allocate_irqs(grlib_irqmp_set_irq, + dev, + nr_irqs); + + return dev; +} + +/* GPTimer */ + +static inline +DeviceState *grlib_gptimer_create(target_phys_addr_t base, + uint32_t nr_timers, + uint32_t freq, + qemu_irq *cpu_irqs, + int base_irq) +{ + DeviceState *dev; + int i; + + dev = qdev_create(NULL, "grlib,gptimer"); + qdev_prop_set_uint32(dev, "nr-timers", nr_timers); + qdev_prop_set_uint32(dev, "frequency", freq); + qdev_prop_set_uint32(dev, "irq-line", base_irq); + + if (qdev_init(dev)) { + return NULL; + } + + sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); + + for (i = 0; i < nr_timers; i++) { + sysbus_connect_irq(sysbus_from_qdev(dev), i, cpu_irqs[base_irq + i]); + } + + return dev; +} + +/* APB UART */ + +static inline +DeviceState *grlib_apbuart_create(target_phys_addr_t base, + CharDriverState *serial, + qemu_irq irq) +{ + DeviceState *dev; + + dev = qdev_create(NULL, "grlib,apbuart"); + qdev_prop_set_chr(dev, "chrdev", serial); + + if (qdev_init(dev)) { + return NULL; + } + + sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); + + sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq); + + return dev; +} + +#endif /* ! _GRLIB_H_ */ diff -Nru qemu-kvm-0.12.5+noroms/hw/grlib_irqmp.c qemu-kvm-0.14.1/hw/grlib_irqmp.c --- qemu-kvm-0.12.5+noroms/hw/grlib_irqmp.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/grlib_irqmp.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,376 @@ +/* + * QEMU GRLIB IRQMP Emulator + * + * (Multiprocessor and extended interrupt not supported) + * + * Copyright (c) 2010-2011 AdaCore + * + * 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 "sysbus.h" +#include "cpu.h" + +#include "grlib.h" + +#include "trace.h" + +#define IRQMP_MAX_CPU 16 +#define IRQMP_REG_SIZE 256 /* Size of memory mapped registers */ + +/* Memory mapped register offsets */ +#define LEVEL_OFFSET 0x00 +#define PENDING_OFFSET 0x04 +#define FORCE0_OFFSET 0x08 +#define CLEAR_OFFSET 0x0C +#define MP_STATUS_OFFSET 0x10 +#define BROADCAST_OFFSET 0x14 +#define MASK_OFFSET 0x40 +#define FORCE_OFFSET 0x80 +#define EXTENDED_OFFSET 0xC0 + +typedef struct IRQMPState IRQMPState; + +typedef struct IRQMP { + SysBusDevice busdev; + + void *set_pil_in; + void *set_pil_in_opaque; + + IRQMPState *state; +} IRQMP; + +struct IRQMPState { + uint32_t level; + uint32_t pending; + uint32_t clear; + uint32_t broadcast; + + uint32_t mask[IRQMP_MAX_CPU]; + uint32_t force[IRQMP_MAX_CPU]; + uint32_t extended[IRQMP_MAX_CPU]; + + IRQMP *parent; +}; + +static void grlib_irqmp_check_irqs(IRQMPState *state) +{ + uint32_t pend = 0; + uint32_t level0 = 0; + uint32_t level1 = 0; + set_pil_in_fn set_pil_in; + + assert(state != NULL); + assert(state->parent != NULL); + + /* IRQ for CPU 0 (no SMP support) */ + pend = (state->pending | state->force[0]) + & state->mask[0]; + + level0 = pend & ~state->level; + level1 = pend & state->level; + + trace_grlib_irqmp_check_irqs(state->pending, state->force[0], + state->mask[0], level1, level0); + + set_pil_in = (set_pil_in_fn)state->parent->set_pil_in; + + /* Trigger level1 interrupt first and level0 if there is no level1 */ + if (level1 != 0) { + set_pil_in(state->parent->set_pil_in_opaque, level1); + } else { + set_pil_in(state->parent->set_pil_in_opaque, level0); + } +} + +void grlib_irqmp_ack(DeviceState *dev, int intno) +{ + SysBusDevice *sdev; + IRQMP *irqmp; + IRQMPState *state; + uint32_t mask; + + assert(dev != NULL); + + sdev = sysbus_from_qdev(dev); + assert(sdev != NULL); + + irqmp = FROM_SYSBUS(typeof(*irqmp), sdev); + assert(irqmp != NULL); + + state = irqmp->state; + assert(state != NULL); + + intno &= 15; + mask = 1 << intno; + + trace_grlib_irqmp_ack(intno); + + /* Clear registers */ + state->pending &= ~mask; + state->force[0] &= ~mask; /* Only CPU 0 (No SMP support) */ + + grlib_irqmp_check_irqs(state); +} + +void grlib_irqmp_set_irq(void *opaque, int irq, int level) +{ + IRQMP *irqmp; + IRQMPState *s; + int i = 0; + + assert(opaque != NULL); + + irqmp = FROM_SYSBUS(typeof(*irqmp), sysbus_from_qdev(opaque)); + assert(irqmp != NULL); + + s = irqmp->state; + assert(s != NULL); + assert(s->parent != NULL); + + + if (level) { + trace_grlib_irqmp_set_irq(irq); + + if (s->broadcast & 1 << irq) { + /* Broadcasted IRQ */ + for (i = 0; i < IRQMP_MAX_CPU; i++) { + s->force[i] |= 1 << irq; + } + } else { + s->pending |= 1 << irq; + } + grlib_irqmp_check_irqs(s); + + } +} + +static uint32_t grlib_irqmp_readl(void *opaque, target_phys_addr_t addr) +{ + IRQMP *irqmp = opaque; + IRQMPState *state; + + assert(irqmp != NULL); + state = irqmp->state; + assert(state != NULL); + + addr &= 0xff; + + /* global registers */ + switch (addr) { + case LEVEL_OFFSET: + return state->level; + + case PENDING_OFFSET: + return state->pending; + + case FORCE0_OFFSET: + /* This register is an "alias" for the force register of CPU 0 */ + return state->force[0]; + + case CLEAR_OFFSET: + case MP_STATUS_OFFSET: + /* Always read as 0 */ + return 0; + + case BROADCAST_OFFSET: + return state->broadcast; + + default: + break; + } + + /* mask registers */ + if (addr >= MASK_OFFSET && addr < FORCE_OFFSET) { + int cpu = (addr - MASK_OFFSET) / 4; + assert(cpu >= 0 && cpu < IRQMP_MAX_CPU); + + return state->mask[cpu]; + } + + /* force registers */ + if (addr >= FORCE_OFFSET && addr < EXTENDED_OFFSET) { + int cpu = (addr - FORCE_OFFSET) / 4; + assert(cpu >= 0 && cpu < IRQMP_MAX_CPU); + + return state->force[cpu]; + } + + /* extended (not supported) */ + if (addr >= EXTENDED_OFFSET && addr < IRQMP_REG_SIZE) { + int cpu = (addr - EXTENDED_OFFSET) / 4; + assert(cpu >= 0 && cpu < IRQMP_MAX_CPU); + + return state->extended[cpu]; + } + + trace_grlib_irqmp_unknown_register("read", addr); + return 0; +} + +static void +grlib_irqmp_writel(void *opaque, target_phys_addr_t addr, uint32_t value) +{ + IRQMP *irqmp = opaque; + IRQMPState *state; + + assert(irqmp != NULL); + state = irqmp->state; + assert(state != NULL); + + addr &= 0xff; + + /* global registers */ + switch (addr) { + case LEVEL_OFFSET: + value &= 0xFFFF << 1; /* clean up the value */ + state->level = value; + return; + + case PENDING_OFFSET: + /* Read Only */ + return; + + case FORCE0_OFFSET: + /* This register is an "alias" for the force register of CPU 0 */ + + value &= 0xFFFE; /* clean up the value */ + state->force[0] = value; + grlib_irqmp_check_irqs(irqmp->state); + return; + + case CLEAR_OFFSET: + value &= ~1; /* clean up the value */ + state->pending &= ~value; + return; + + case MP_STATUS_OFFSET: + /* Read Only (no SMP support) */ + return; + + case BROADCAST_OFFSET: + value &= 0xFFFE; /* clean up the value */ + state->broadcast = value; + return; + + default: + break; + } + + /* mask registers */ + if (addr >= MASK_OFFSET && addr < FORCE_OFFSET) { + int cpu = (addr - MASK_OFFSET) / 4; + assert(cpu >= 0 && cpu < IRQMP_MAX_CPU); + + value &= ~1; /* clean up the value */ + state->mask[cpu] = value; + grlib_irqmp_check_irqs(irqmp->state); + return; + } + + /* force registers */ + if (addr >= FORCE_OFFSET && addr < EXTENDED_OFFSET) { + int cpu = (addr - FORCE_OFFSET) / 4; + assert(cpu >= 0 && cpu < IRQMP_MAX_CPU); + + uint32_t force = value & 0xFFFE; + uint32_t clear = (value >> 16) & 0xFFFE; + uint32_t old = state->force[cpu]; + + state->force[cpu] = (old | force) & ~clear; + grlib_irqmp_check_irqs(irqmp->state); + return; + } + + /* extended (not supported) */ + if (addr >= EXTENDED_OFFSET && addr < IRQMP_REG_SIZE) { + int cpu = (addr - EXTENDED_OFFSET) / 4; + assert(cpu >= 0 && cpu < IRQMP_MAX_CPU); + + value &= 0xF; /* clean up the value */ + state->extended[cpu] = value; + return; + } + + trace_grlib_irqmp_unknown_register("write", addr); +} + +static CPUReadMemoryFunc * const grlib_irqmp_read[] = { + NULL, NULL, &grlib_irqmp_readl, +}; + +static CPUWriteMemoryFunc * const grlib_irqmp_write[] = { + NULL, NULL, &grlib_irqmp_writel, +}; + +static void grlib_irqmp_reset(DeviceState *d) +{ + IRQMP *irqmp = container_of(d, IRQMP, busdev.qdev); + assert(irqmp != NULL); + assert(irqmp->state != NULL); + + memset(irqmp->state, 0, sizeof *irqmp->state); + irqmp->state->parent = irqmp; +} + +static int grlib_irqmp_init(SysBusDevice *dev) +{ + IRQMP *irqmp = FROM_SYSBUS(typeof(*irqmp), dev); + int irqmp_regs; + + assert(irqmp != NULL); + + /* Check parameters */ + if (irqmp->set_pil_in == NULL) { + return -1; + } + + irqmp_regs = cpu_register_io_memory(grlib_irqmp_read, + grlib_irqmp_write, + irqmp, DEVICE_NATIVE_ENDIAN); + + irqmp->state = qemu_mallocz(sizeof *irqmp->state); + + if (irqmp_regs < 0) { + return -1; + } + + sysbus_init_mmio(dev, IRQMP_REG_SIZE, irqmp_regs); + + return 0; +} + +static SysBusDeviceInfo grlib_irqmp_info = { + .init = grlib_irqmp_init, + .qdev.name = "grlib,irqmp", + .qdev.reset = grlib_irqmp_reset, + .qdev.size = sizeof(IRQMP), + .qdev.props = (Property[]) { + DEFINE_PROP_PTR("set_pil_in", IRQMP, set_pil_in), + DEFINE_PROP_PTR("set_pil_in_opaque", IRQMP, set_pil_in_opaque), + DEFINE_PROP_END_OF_LIST(), + } +}; + +static void grlib_irqmp_register(void) +{ + sysbus_register_withprop(&grlib_irqmp_info); +} + +device_init(grlib_irqmp_register) diff -Nru qemu-kvm-0.12.5+noroms/hw/gt64xxx.c qemu-kvm-0.14.1/hw/gt64xxx.c --- qemu-kvm-0.12.5+noroms/hw/gt64xxx.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/gt64xxx.c 2011-05-11 13:29:46.000000000 +0000 @@ -31,9 +31,9 @@ //#define DEBUG #ifdef DEBUG -#define dprintf(fmt, ...) fprintf(stderr, "%s: " fmt, __FUNCTION__, ##__VA_ARGS__) +#define DPRINTF(fmt, ...) fprintf(stderr, "%s: " fmt, __FUNCTION__, ##__VA_ARGS__) #else -#define dprintf(fmt, ...) +#define DPRINTF(fmt, ...) #endif #define GT_REGS (0x1000 >> 2) @@ -223,16 +223,14 @@ #define GT_PCI0_HICMASK (0xca4 >> 2) #define GT_PCI1_SERR1MASK (0xca8 >> 2) - -typedef PCIHostState GT64120PCIState; - #define PCI_MAPPING_ENTRY(regname) \ target_phys_addr_t regname ##_start; \ target_phys_addr_t regname ##_length; \ int regname ##_handle typedef struct GT64120State { - GT64120PCIState *pci; + SysBusDevice busdev; + PCIHostState pci; uint32_t regs[GT_REGS]; PCI_MAPPING_ENTRY(PCI0IO); PCI_MAPPING_ENTRY(ISD); @@ -276,7 +274,7 @@ check_reserved_space(&start, &length); length = 0x1000; /* Map new address */ - dprintf("ISD: %x@%x -> %x@%x, %x\n", s->ISD_length, s->ISD_start, + DPRINTF("ISD: "TARGET_FMT_plx"@"TARGET_FMT_plx" -> "TARGET_FMT_plx"@"TARGET_FMT_plx", %x\n", s->ISD_length, s->ISD_start, length, start, s->ISD_handle); s->ISD_start = start; s->ISD_length = length; @@ -423,7 +421,7 @@ case GT_DEV_B3: case GT_DEV_BOOT: /* Not implemented */ - dprintf ("Unimplemented device register offset 0x%x\n", saddr << 2); + DPRINTF ("Unimplemented device register offset 0x%x\n", saddr << 2); break; /* ECC */ @@ -457,7 +455,7 @@ case GT_DMA2_CUR: case GT_DMA3_CUR: /* Not implemented */ - dprintf ("Unimplemented DMA register offset 0x%x\n", saddr << 2); + DPRINTF ("Unimplemented DMA register offset 0x%x\n", saddr << 2); break; /* DMA Channel Control */ @@ -466,13 +464,13 @@ case GT_DMA2_CTRL: case GT_DMA3_CTRL: /* Not implemented */ - dprintf ("Unimplemented DMA register offset 0x%x\n", saddr << 2); + DPRINTF ("Unimplemented DMA register offset 0x%x\n", saddr << 2); break; /* DMA Arbiter */ case GT_DMA_ARB: /* Not implemented */ - dprintf ("Unimplemented DMA register offset 0x%x\n", saddr << 2); + DPRINTF ("Unimplemented DMA register offset 0x%x\n", saddr << 2); break; /* Timer/Counter */ @@ -482,7 +480,7 @@ case GT_TC3: case GT_TC_CONTROL: /* Not implemented */ - dprintf ("Unimplemented timer register offset 0x%x\n", saddr << 2); + DPRINTF ("Unimplemented timer register offset 0x%x\n", saddr << 2); break; /* PCI Internal */ @@ -525,13 +523,13 @@ /* not implemented */ break; case GT_PCI0_CFGADDR: - s->pci->config_reg = val & 0x80fffffc; + s->pci.config_reg = val & 0x80fffffc; break; case GT_PCI0_CFGDATA: - if (!(s->regs[GT_PCI0_CMD] & 1) && (s->pci->config_reg & 0x00fff800)) + if (!(s->regs[GT_PCI0_CMD] & 1) && (s->pci.config_reg & 0x00fff800)) val = bswap32(val); - if (s->pci->config_reg & (1u << 31)) - pci_data_write(s->pci->bus, s->pci->config_reg, val, 4); + if (s->pci.config_reg & (1u << 31)) + pci_data_write(s->pci.bus, s->pci.config_reg, val, 4); break; /* Interrupts */ @@ -539,19 +537,19 @@ /* not really implemented */ s->regs[saddr] = ~(~(s->regs[saddr]) | ~(val & 0xfffffffe)); s->regs[saddr] |= !!(s->regs[saddr] & 0xfffffffe); - dprintf("INTRCAUSE %x\n", val); + DPRINTF("INTRCAUSE %x\n", val); break; case GT_INTRMASK: s->regs[saddr] = val & 0x3c3ffffe; - dprintf("INTRMASK %x\n", val); + DPRINTF("INTRMASK %x\n", val); break; case GT_PCI0_ICMASK: s->regs[saddr] = val & 0x03fffffe; - dprintf("ICMASK %x\n", val); + DPRINTF("ICMASK %x\n", val); break; case GT_PCI0_SERR0MASK: s->regs[saddr] = val & 0x0000003f; - dprintf("SERR0MASK %x\n", val); + DPRINTF("SERR0MASK %x\n", val); break; /* Reserved when only PCI_0 is configured. */ @@ -575,7 +573,7 @@ break; default: - dprintf ("Bad register offset 0x%x\n", (int)addr); + DPRINTF ("Bad register offset 0x%x\n", (int)addr); break; } } @@ -765,14 +763,14 @@ /* PCI Internal */ case GT_PCI0_CFGADDR: - val = s->pci->config_reg; + val = s->pci.config_reg; break; case GT_PCI0_CFGDATA: - if (!(s->pci->config_reg & (1 << 31))) + if (!(s->pci.config_reg & (1 << 31))) val = 0xffffffff; else - val = pci_data_read(s->pci->bus, s->pci->config_reg, 4); - if (!(s->regs[GT_PCI0_CMD] & 1) && (s->pci->config_reg & 0x00fff800)) + val = pci_data_read(s->pci.bus, s->pci.config_reg, 4); + if (!(s->regs[GT_PCI0_CMD] & 1) && (s->pci.config_reg & 0x00fff800)) val = bswap32(val); break; @@ -815,19 +813,19 @@ /* Interrupts */ case GT_INTRCAUSE: val = s->regs[saddr]; - dprintf("INTRCAUSE %x\n", val); + DPRINTF("INTRCAUSE %x\n", val); break; case GT_INTRMASK: val = s->regs[saddr]; - dprintf("INTRMASK %x\n", val); + DPRINTF("INTRMASK %x\n", val); break; case GT_PCI0_ICMASK: val = s->regs[saddr]; - dprintf("ICMASK %x\n", val); + DPRINTF("ICMASK %x\n", val); break; case GT_PCI0_SERR0MASK: val = s->regs[saddr]; - dprintf("SERR0MASK %x\n", val); + DPRINTF("SERR0MASK %x\n", val); break; /* Reserved when only PCI_0 is configured. */ @@ -842,7 +840,7 @@ default: val = s->regs[saddr]; - dprintf ("Bad register offset 0x%x\n", (int)addr); + DPRINTF ("Bad register offset 0x%x\n", (int)addr); break; } @@ -864,7 +862,7 @@ >64120_readl, }; -static int pci_gt64120_map_irq(PCIDevice *pci_dev, int irq_num) +static int gt64120_pci_map_irq(PCIDevice *pci_dev, int irq_num) { int slot; @@ -891,7 +889,7 @@ static int pci_irq_levels[4]; -static void pci_gt64120_set_irq(void *opaque, int irq_num, int level) +static void gt64120_pci_set_irq(void *opaque, int irq_num, int level) { int i, pic_irq, pic_level; qemu_irq *pic = opaque; @@ -1082,17 +1080,6 @@ gt64120_pci_mapping(s); } -static uint32_t gt64120_read_config(PCIDevice *d, uint32_t address, int len) -{ - return pci_default_read_config(d, address, len); -} - -static void gt64120_write_config(PCIDevice *d, uint32_t address, uint32_t val, - int len) -{ - pci_default_write_config(d, address, val, len); -} - static void gt64120_save(QEMUFile* f, void *opaque) { PCIDevice *d = opaque; @@ -1112,48 +1099,76 @@ return 0; } -PCIBus *pci_gt64120_init(qemu_irq *pic) +PCIBus *gt64120_register(qemu_irq *pic) +{ + SysBusDevice *s; + GT64120State *d; + DeviceState *dev; + + dev = qdev_create(NULL, "gt64120"); + qdev_init_nofail(dev); + s = sysbus_from_qdev(dev); + d = FROM_SYSBUS(GT64120State, s); + d->pci.bus = pci_register_bus(&d->busdev.qdev, "pci", + gt64120_pci_set_irq, gt64120_pci_map_irq, + pic, PCI_DEVFN(18, 0), 4); + d->ISD_handle = cpu_register_io_memory(gt64120_read, gt64120_write, d, + DEVICE_NATIVE_ENDIAN); + + pci_create_simple(d->pci.bus, PCI_DEVFN(0, 0), "gt64120_pci"); + return d->pci.bus; +} + +static int gt64120_init(SysBusDevice *dev) { GT64120State *s; - PCIDevice *d; - s = qemu_mallocz(sizeof(GT64120State)); - s->pci = qemu_mallocz(sizeof(GT64120PCIState)); + s = FROM_SYSBUS(GT64120State, dev); - s->pci->bus = pci_register_bus(NULL, "pci", - pci_gt64120_set_irq, pci_gt64120_map_irq, - pic, 144, 4); - s->ISD_handle = cpu_register_io_memory(gt64120_read, gt64120_write, s); - d = pci_register_device(s->pci->bus, "GT64120 PCI Bus", sizeof(PCIDevice), - 0, gt64120_read_config, gt64120_write_config); + /* FIXME: This value is computed from registers during reset, but some + devices (e.g. VGA card) need to know it when they are registered. + This also mean that changing the register to change the mapping + does not fully work. */ + isa_mem_base = 0x10000000; + qemu_register_reset(gt64120_reset, s); + register_savevm(&dev->qdev, "GT64120 PCI Bus", 0, 1, + gt64120_save, gt64120_load, &s->pci); + return 0; +} +static int gt64120_pci_init(PCIDevice *d) +{ /* FIXME: Malta specific hw assumptions ahead */ - pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_MARVELL); pci_config_set_device_id(d->config, PCI_DEVICE_ID_MARVELL_GT6412X); - - d->config[0x04] = 0x00; - d->config[0x05] = 0x00; - d->config[0x06] = 0x80; - d->config[0x07] = 0x02; - - d->config[0x08] = 0x10; - d->config[0x09] = 0x00; + pci_set_word(d->config + PCI_COMMAND, 0); + pci_set_word(d->config + PCI_STATUS, + PCI_STATUS_FAST_BACK | PCI_STATUS_DEVSEL_MEDIUM); + pci_set_byte(d->config + PCI_CLASS_REVISION, 0x10); + pci_config_set_prog_interface(d->config, 0); pci_config_set_class(d->config, PCI_CLASS_BRIDGE_HOST); + pci_set_long(d->config + PCI_BASE_ADDRESS_0, 0x00000008); + pci_set_long(d->config + PCI_BASE_ADDRESS_1, 0x01000008); + pci_set_long(d->config + PCI_BASE_ADDRESS_2, 0x1c000000); + pci_set_long(d->config + PCI_BASE_ADDRESS_3, 0x1f000000); + pci_set_long(d->config + PCI_BASE_ADDRESS_4, 0x14000000); + pci_set_long(d->config + PCI_BASE_ADDRESS_5, 0x14000001); + pci_set_byte(d->config + 0x3d, 0x01); - d->config[0x10] = 0x08; - d->config[0x14] = 0x08; - d->config[0x17] = 0x01; - d->config[0x1B] = 0x1c; - d->config[0x1F] = 0x1f; - d->config[0x23] = 0x14; - d->config[0x24] = 0x01; - d->config[0x27] = 0x14; - d->config[0x3D] = 0x01; - - gt64120_reset(s); + return 0; +} - register_savevm("GT64120 PCI Bus", 0, 1, gt64120_save, gt64120_load, d); +static PCIDeviceInfo gt64120_pci_info = { + .qdev.name = "gt64120_pci", + .qdev.size = sizeof(PCIDevice), + .init = gt64120_pci_init, +}; - return s->pci->bus; +static void gt64120_pci_register_devices(void) +{ + sysbus_register_dev("gt64120", sizeof(GT64120State), + gt64120_init); + pci_qdev_register(>64120_pci_info); } + +device_init(gt64120_pci_register_devices) diff -Nru qemu-kvm-0.12.5+noroms/hw/gumstix.c qemu-kvm-0.14.1/hw/gumstix.c --- qemu-kvm-0.12.5+noroms/hw/gumstix.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/gumstix.c 2011-05-11 13:29:46.000000000 +0000 @@ -38,6 +38,7 @@ #include "sysemu.h" #include "devices.h" #include "boards.h" +#include "blockdev.h" static const int sector_len = 128 * 1024; @@ -48,6 +49,7 @@ { PXA2xxState *cpu; DriveInfo *dinfo; + int be; uint32_t connex_rom = 0x01000000; uint32_t connex_ram = 0x04000000; @@ -61,18 +63,22 @@ exit(1); } - if (!pflash_cfi01_register(0x00000000, qemu_ram_alloc(connex_rom), - dinfo->bdrv, sector_len, connex_rom / sector_len, - 2, 0, 0, 0, 0)) { +#ifdef TARGET_WORDS_BIGENDIAN + be = 1; +#else + be = 0; +#endif + if (!pflash_cfi01_register(0x00000000, qemu_ram_alloc(NULL, "connext.rom", + connex_rom), + dinfo->bdrv, sector_len, connex_rom / sector_len, + 2, 0, 0, 0, 0, be)) { fprintf(stderr, "qemu: Error registering flash memory.\n"); exit(1); } - cpu->env->regs[15] = 0x00000000; - /* Interrupt line of NIC is connected to GPIO line 36 */ smc91c111_init(&nd_table[0], 0x04000300, - pxa2xx_gpio_in_get(cpu->gpio)[36]); + qdev_get_gpio_in(cpu->gpio, 36)); } static void verdex_init(ram_addr_t ram_size, @@ -82,6 +88,7 @@ { PXA2xxState *cpu; DriveInfo *dinfo; + int be; uint32_t verdex_rom = 0x02000000; uint32_t verdex_ram = 0x10000000; @@ -95,18 +102,22 @@ exit(1); } - if (!pflash_cfi01_register(0x00000000, qemu_ram_alloc(verdex_rom), - dinfo->bdrv, sector_len, verdex_rom / sector_len, - 2, 0, 0, 0, 0)) { +#ifdef TARGET_WORDS_BIGENDIAN + be = 1; +#else + be = 0; +#endif + if (!pflash_cfi01_register(0x00000000, qemu_ram_alloc(NULL, "verdex.rom", + verdex_rom), + dinfo->bdrv, sector_len, verdex_rom / sector_len, + 2, 0, 0, 0, 0, be)) { fprintf(stderr, "qemu: Error registering flash memory.\n"); exit(1); } - cpu->env->regs[15] = 0x00000000; - /* Interrupt line of NIC is connected to GPIO line 99 */ smc91c111_init(&nd_table[0], 0x04000300, - pxa2xx_gpio_in_get(cpu->gpio)[99]); + qdev_get_gpio_in(cpu->gpio, 99)); } static QEMUMachine connex_machine = { diff -Nru qemu-kvm-0.12.5+noroms/hw/gus.c qemu-kvm-0.14.1/hw/gus.c --- qemu-kvm-0.12.5+noroms/hw/gus.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/gus.c 2011-05-11 13:29:46.000000000 +0000 @@ -264,20 +264,24 @@ register_ioport_write (s->port, 1, 1, gus_writeb, s); register_ioport_write (s->port, 1, 2, gus_writew, s); + isa_init_ioport_range(dev, s->port, 2); register_ioport_read ((s->port + 0x100) & 0xf00, 1, 1, gus_readb, s); register_ioport_read ((s->port + 0x100) & 0xf00, 1, 2, gus_readw, s); + isa_init_ioport_range(dev, (s->port + 0x100) & 0xf00, 2); register_ioport_write (s->port + 6, 10, 1, gus_writeb, s); register_ioport_write (s->port + 6, 10, 2, gus_writew, s); register_ioport_read (s->port + 6, 10, 1, gus_readb, s); register_ioport_read (s->port + 6, 10, 2, gus_readw, s); + isa_init_ioport_range(dev, s->port + 6, 10); register_ioport_write (s->port + 0x100, 8, 1, gus_writeb, s); register_ioport_write (s->port + 0x100, 8, 2, gus_writew, s); register_ioport_read (s->port + 0x100, 8, 1, gus_readb, s); register_ioport_read (s->port + 0x100, 8, 2, gus_readw, s); + isa_init_ioport_range(dev, s->port + 0x100, 8); DMA_register_channel (s->emu.gusdma, GUS_read_DMA, s); s->emu.himemaddr = s->himem; diff -Nru qemu-kvm-0.12.5+noroms/hw/hda-audio.c qemu-kvm-0.14.1/hw/hda-audio.c --- qemu-kvm-0.12.5+noroms/hw/hda-audio.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/hda-audio.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,926 @@ +/* + * Copyright (C) 2010 Red Hat, Inc. + * + * written by Gerd Hoffmann + * + * 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 or + * (at your option) version 3 of the License. + * + * 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, see . + */ + +#include "hw.h" +#include "pci.h" +#include "intel-hda.h" +#include "intel-hda-defs.h" +#include "audio/audio.h" + +/* -------------------------------------------------------------------------- */ + +typedef struct desc_param { + uint32_t id; + uint32_t val; +} desc_param; + +typedef struct desc_node { + uint32_t nid; + const char *name; + const desc_param *params; + uint32_t nparams; + uint32_t config; + uint32_t pinctl; + uint32_t *conn; + uint32_t stindex; +} desc_node; + +typedef struct desc_codec { + const char *name; + uint32_t iid; + const desc_node *nodes; + uint32_t nnodes; +} desc_codec; + +static const desc_param* hda_codec_find_param(const desc_node *node, uint32_t id) +{ + int i; + + for (i = 0; i < node->nparams; i++) { + if (node->params[i].id == id) { + return &node->params[i]; + } + } + return NULL; +} + +static const desc_node* hda_codec_find_node(const desc_codec *codec, uint32_t nid) +{ + int i; + + for (i = 0; i < codec->nnodes; i++) { + if (codec->nodes[i].nid == nid) { + return &codec->nodes[i]; + } + } + return NULL; +} + +static void hda_codec_parse_fmt(uint32_t format, struct audsettings *as) +{ + if (format & AC_FMT_TYPE_NON_PCM) { + return; + } + + as->freq = (format & AC_FMT_BASE_44K) ? 44100 : 48000; + + switch ((format & AC_FMT_MULT_MASK) >> AC_FMT_MULT_SHIFT) { + case 1: as->freq *= 2; break; + case 2: as->freq *= 3; break; + case 3: as->freq *= 4; break; + } + + switch ((format & AC_FMT_DIV_MASK) >> AC_FMT_DIV_SHIFT) { + case 1: as->freq /= 2; break; + case 2: as->freq /= 3; break; + case 3: as->freq /= 4; break; + case 4: as->freq /= 5; break; + case 5: as->freq /= 6; break; + case 6: as->freq /= 7; break; + case 7: as->freq /= 8; break; + } + + switch (format & AC_FMT_BITS_MASK) { + case AC_FMT_BITS_8: as->fmt = AUD_FMT_S8; break; + case AC_FMT_BITS_16: as->fmt = AUD_FMT_S16; break; + case AC_FMT_BITS_32: as->fmt = AUD_FMT_S32; break; + } + + as->nchannels = ((format & AC_FMT_CHAN_MASK) >> AC_FMT_CHAN_SHIFT) + 1; +} + +/* -------------------------------------------------------------------------- */ +/* + * HDA codec descriptions + */ + +/* some defines */ + +#define QEMU_HDA_ID_VENDOR 0x1af4 +#define QEMU_HDA_ID_OUTPUT ((QEMU_HDA_ID_VENDOR << 16) | 0x10) +#define QEMU_HDA_ID_DUPLEX ((QEMU_HDA_ID_VENDOR << 16) | 0x20) + +#define QEMU_HDA_PCM_FORMATS (AC_SUPPCM_BITS_16 | \ + 0x1fc /* 16 -> 96 kHz */) +#define QEMU_HDA_AMP_NONE (0) +#define QEMU_HDA_AMP_STEPS 0x4a + +#ifdef CONFIG_MIXEMU +#define QEMU_HDA_AMP_CAPS \ + (AC_AMPCAP_MUTE | \ + (QEMU_HDA_AMP_STEPS << AC_AMPCAP_OFFSET_SHIFT) | \ + (QEMU_HDA_AMP_STEPS << AC_AMPCAP_NUM_STEPS_SHIFT) | \ + (3 << AC_AMPCAP_STEP_SIZE_SHIFT)) +#else +#define QEMU_HDA_AMP_CAPS QEMU_HDA_AMP_NONE +#endif + +/* common: audio output widget */ +static const desc_param common_params_audio_dac[] = { + { + .id = AC_PAR_AUDIO_WIDGET_CAP, + .val = ((AC_WID_AUD_OUT << AC_WCAP_TYPE_SHIFT) | + AC_WCAP_FORMAT_OVRD | + AC_WCAP_AMP_OVRD | + AC_WCAP_OUT_AMP | + AC_WCAP_STEREO), + },{ + .id = AC_PAR_PCM, + .val = QEMU_HDA_PCM_FORMATS, + },{ + .id = AC_PAR_STREAM, + .val = AC_SUPFMT_PCM, + },{ + .id = AC_PAR_AMP_IN_CAP, + .val = QEMU_HDA_AMP_NONE, + },{ + .id = AC_PAR_AMP_OUT_CAP, + .val = QEMU_HDA_AMP_CAPS, + }, +}; + +/* common: pin widget (line-out) */ +static const desc_param common_params_audio_lineout[] = { + { + .id = AC_PAR_AUDIO_WIDGET_CAP, + .val = ((AC_WID_PIN << AC_WCAP_TYPE_SHIFT) | + AC_WCAP_CONN_LIST | + AC_WCAP_STEREO), + },{ + .id = AC_PAR_PIN_CAP, + .val = AC_PINCAP_OUT, + },{ + .id = AC_PAR_CONNLIST_LEN, + .val = 1, + },{ + .id = AC_PAR_AMP_IN_CAP, + .val = QEMU_HDA_AMP_NONE, + },{ + .id = AC_PAR_AMP_OUT_CAP, + .val = QEMU_HDA_AMP_NONE, + }, +}; + +/* output: root node */ +static const desc_param output_params_root[] = { + { + .id = AC_PAR_VENDOR_ID, + .val = QEMU_HDA_ID_OUTPUT, + },{ + .id = AC_PAR_SUBSYSTEM_ID, + .val = QEMU_HDA_ID_OUTPUT, + },{ + .id = AC_PAR_REV_ID, + .val = 0x00100101, + },{ + .id = AC_PAR_NODE_COUNT, + .val = 0x00010001, + }, +}; + +/* output: audio function */ +static const desc_param output_params_audio_func[] = { + { + .id = AC_PAR_FUNCTION_TYPE, + .val = AC_GRP_AUDIO_FUNCTION, + },{ + .id = AC_PAR_SUBSYSTEM_ID, + .val = QEMU_HDA_ID_OUTPUT, + },{ + .id = AC_PAR_NODE_COUNT, + .val = 0x00020002, + },{ + .id = AC_PAR_PCM, + .val = QEMU_HDA_PCM_FORMATS, + },{ + .id = AC_PAR_STREAM, + .val = AC_SUPFMT_PCM, + },{ + .id = AC_PAR_AMP_IN_CAP, + .val = QEMU_HDA_AMP_NONE, + },{ + .id = AC_PAR_AMP_OUT_CAP, + .val = QEMU_HDA_AMP_NONE, + },{ + .id = AC_PAR_GPIO_CAP, + .val = 0, + },{ + .id = AC_PAR_AUDIO_FG_CAP, + .val = 0x00000808, + },{ + .id = AC_PAR_POWER_STATE, + .val = 0, + }, +}; + +/* output: nodes */ +static const desc_node output_nodes[] = { + { + .nid = AC_NODE_ROOT, + .name = "root", + .params = output_params_root, + .nparams = ARRAY_SIZE(output_params_root), + },{ + .nid = 1, + .name = "func", + .params = output_params_audio_func, + .nparams = ARRAY_SIZE(output_params_audio_func), + },{ + .nid = 2, + .name = "dac", + .params = common_params_audio_dac, + .nparams = ARRAY_SIZE(common_params_audio_dac), + .stindex = 0, + },{ + .nid = 3, + .name = "out", + .params = common_params_audio_lineout, + .nparams = ARRAY_SIZE(common_params_audio_lineout), + .config = ((AC_JACK_PORT_COMPLEX << AC_DEFCFG_PORT_CONN_SHIFT) | + (AC_JACK_LINE_OUT << AC_DEFCFG_DEVICE_SHIFT) | + (AC_JACK_CONN_UNKNOWN << AC_DEFCFG_CONN_TYPE_SHIFT) | + (AC_JACK_COLOR_GREEN << AC_DEFCFG_COLOR_SHIFT) | + 0x10), + .pinctl = AC_PINCTL_OUT_EN, + .conn = (uint32_t[]) { 2 }, + } +}; + +/* output: codec */ +static const desc_codec output = { + .name = "output", + .iid = QEMU_HDA_ID_OUTPUT, + .nodes = output_nodes, + .nnodes = ARRAY_SIZE(output_nodes), +}; + +/* duplex: root node */ +static const desc_param duplex_params_root[] = { + { + .id = AC_PAR_VENDOR_ID, + .val = QEMU_HDA_ID_DUPLEX, + },{ + .id = AC_PAR_SUBSYSTEM_ID, + .val = QEMU_HDA_ID_DUPLEX, + },{ + .id = AC_PAR_REV_ID, + .val = 0x00100101, + },{ + .id = AC_PAR_NODE_COUNT, + .val = 0x00010001, + }, +}; + +/* duplex: audio input widget */ +static const desc_param duplex_params_audio_adc[] = { + { + .id = AC_PAR_AUDIO_WIDGET_CAP, + .val = ((AC_WID_AUD_IN << AC_WCAP_TYPE_SHIFT) | + AC_WCAP_CONN_LIST | + AC_WCAP_FORMAT_OVRD | + AC_WCAP_AMP_OVRD | + AC_WCAP_IN_AMP | + AC_WCAP_STEREO), + },{ + .id = AC_PAR_CONNLIST_LEN, + .val = 1, + },{ + .id = AC_PAR_PCM, + .val = QEMU_HDA_PCM_FORMATS, + },{ + .id = AC_PAR_STREAM, + .val = AC_SUPFMT_PCM, + },{ + .id = AC_PAR_AMP_IN_CAP, + .val = QEMU_HDA_AMP_CAPS, + },{ + .id = AC_PAR_AMP_OUT_CAP, + .val = QEMU_HDA_AMP_NONE, + }, +}; + +/* duplex: pin widget (line-in) */ +static const desc_param duplex_params_audio_linein[] = { + { + .id = AC_PAR_AUDIO_WIDGET_CAP, + .val = ((AC_WID_PIN << AC_WCAP_TYPE_SHIFT) | + AC_WCAP_STEREO), + },{ + .id = AC_PAR_PIN_CAP, + .val = AC_PINCAP_IN, + },{ + .id = AC_PAR_AMP_IN_CAP, + .val = QEMU_HDA_AMP_NONE, + },{ + .id = AC_PAR_AMP_OUT_CAP, + .val = QEMU_HDA_AMP_NONE, + }, +}; + +/* duplex: audio function */ +static const desc_param duplex_params_audio_func[] = { + { + .id = AC_PAR_FUNCTION_TYPE, + .val = AC_GRP_AUDIO_FUNCTION, + },{ + .id = AC_PAR_SUBSYSTEM_ID, + .val = QEMU_HDA_ID_DUPLEX, + },{ + .id = AC_PAR_NODE_COUNT, + .val = 0x00020004, + },{ + .id = AC_PAR_PCM, + .val = QEMU_HDA_PCM_FORMATS, + },{ + .id = AC_PAR_STREAM, + .val = AC_SUPFMT_PCM, + },{ + .id = AC_PAR_AMP_IN_CAP, + .val = QEMU_HDA_AMP_NONE, + },{ + .id = AC_PAR_AMP_OUT_CAP, + .val = QEMU_HDA_AMP_NONE, + },{ + .id = AC_PAR_GPIO_CAP, + .val = 0, + },{ + .id = AC_PAR_AUDIO_FG_CAP, + .val = 0x00000808, + },{ + .id = AC_PAR_POWER_STATE, + .val = 0, + }, +}; + +/* duplex: nodes */ +static const desc_node duplex_nodes[] = { + { + .nid = AC_NODE_ROOT, + .name = "root", + .params = duplex_params_root, + .nparams = ARRAY_SIZE(duplex_params_root), + },{ + .nid = 1, + .name = "func", + .params = duplex_params_audio_func, + .nparams = ARRAY_SIZE(duplex_params_audio_func), + },{ + .nid = 2, + .name = "dac", + .params = common_params_audio_dac, + .nparams = ARRAY_SIZE(common_params_audio_dac), + .stindex = 0, + },{ + .nid = 3, + .name = "out", + .params = common_params_audio_lineout, + .nparams = ARRAY_SIZE(common_params_audio_lineout), + .config = ((AC_JACK_PORT_COMPLEX << AC_DEFCFG_PORT_CONN_SHIFT) | + (AC_JACK_LINE_OUT << AC_DEFCFG_DEVICE_SHIFT) | + (AC_JACK_CONN_UNKNOWN << AC_DEFCFG_CONN_TYPE_SHIFT) | + (AC_JACK_COLOR_GREEN << AC_DEFCFG_COLOR_SHIFT) | + 0x10), + .pinctl = AC_PINCTL_OUT_EN, + .conn = (uint32_t[]) { 2 }, + },{ + .nid = 4, + .name = "adc", + .params = duplex_params_audio_adc, + .nparams = ARRAY_SIZE(duplex_params_audio_adc), + .stindex = 1, + .conn = (uint32_t[]) { 5 }, + },{ + .nid = 5, + .name = "in", + .params = duplex_params_audio_linein, + .nparams = ARRAY_SIZE(duplex_params_audio_linein), + .config = ((AC_JACK_PORT_COMPLEX << AC_DEFCFG_PORT_CONN_SHIFT) | + (AC_JACK_LINE_IN << AC_DEFCFG_DEVICE_SHIFT) | + (AC_JACK_CONN_UNKNOWN << AC_DEFCFG_CONN_TYPE_SHIFT) | + (AC_JACK_COLOR_RED << AC_DEFCFG_COLOR_SHIFT) | + 0x20), + .pinctl = AC_PINCTL_IN_EN, + } +}; + +/* duplex: codec */ +static const desc_codec duplex = { + .name = "duplex", + .iid = QEMU_HDA_ID_DUPLEX, + .nodes = duplex_nodes, + .nnodes = ARRAY_SIZE(duplex_nodes), +}; + +/* -------------------------------------------------------------------------- */ + +static const char *fmt2name[] = { + [ AUD_FMT_U8 ] = "PCM-U8", + [ AUD_FMT_S8 ] = "PCM-S8", + [ AUD_FMT_U16 ] = "PCM-U16", + [ AUD_FMT_S16 ] = "PCM-S16", + [ AUD_FMT_U32 ] = "PCM-U32", + [ AUD_FMT_S32 ] = "PCM-S32", +}; + +typedef struct HDAAudioState HDAAudioState; +typedef struct HDAAudioStream HDAAudioStream; + +struct HDAAudioStream { + HDAAudioState *state; + const desc_node *node; + bool output, running; + uint32_t stream; + uint32_t channel; + uint32_t format; + uint32_t gain_left, gain_right; + bool mute_left, mute_right; + struct audsettings as; + union { + SWVoiceIn *in; + SWVoiceOut *out; + } voice; + uint8_t buf[HDA_BUFFER_SIZE]; + uint32_t bpos; +}; + +struct HDAAudioState { + HDACodecDevice hda; + const char *name; + + QEMUSoundCard card; + const desc_codec *desc; + HDAAudioStream st[4]; + bool running[16]; + + /* properties */ + uint32_t debug; +}; + +static void hda_audio_input_cb(void *opaque, int avail) +{ + HDAAudioStream *st = opaque; + int recv = 0; + int len; + bool rc; + + while (avail - recv >= sizeof(st->buf)) { + if (st->bpos != sizeof(st->buf)) { + len = AUD_read(st->voice.in, st->buf + st->bpos, + sizeof(st->buf) - st->bpos); + st->bpos += len; + recv += len; + if (st->bpos != sizeof(st->buf)) { + break; + } + } + rc = hda_codec_xfer(&st->state->hda, st->stream, false, + st->buf, sizeof(st->buf)); + if (!rc) { + break; + } + st->bpos = 0; + } +} + +static void hda_audio_output_cb(void *opaque, int avail) +{ + HDAAudioStream *st = opaque; + int sent = 0; + int len; + bool rc; + + while (avail - sent >= sizeof(st->buf)) { + if (st->bpos == sizeof(st->buf)) { + rc = hda_codec_xfer(&st->state->hda, st->stream, true, + st->buf, sizeof(st->buf)); + if (!rc) { + break; + } + st->bpos = 0; + } + len = AUD_write(st->voice.out, st->buf + st->bpos, + sizeof(st->buf) - st->bpos); + st->bpos += len; + sent += len; + if (st->bpos != sizeof(st->buf)) { + break; + } + } +} + +static void hda_audio_set_running(HDAAudioStream *st, bool running) +{ + if (st->node == NULL) { + return; + } + if (st->running == running) { + return; + } + st->running = running; + dprint(st->state, 1, "%s: %s (stream %d)\n", st->node->name, + st->running ? "on" : "off", st->stream); + if (st->output) { + AUD_set_active_out(st->voice.out, st->running); + } else { + AUD_set_active_in(st->voice.in, st->running); + } +} + +static void hda_audio_set_amp(HDAAudioStream *st) +{ + bool muted; + uint32_t left, right; + + if (st->node == NULL) { + return; + } + + muted = st->mute_left && st->mute_right; + left = st->mute_left ? 0 : st->gain_left; + right = st->mute_right ? 0 : st->gain_right; + + left = left * 255 / QEMU_HDA_AMP_STEPS; + right = right * 255 / QEMU_HDA_AMP_STEPS; + + if (st->output) { + AUD_set_volume_out(st->voice.out, muted, left, right); + } else { + AUD_set_volume_in(st->voice.in, muted, left, right); + } +} + +static void hda_audio_setup(HDAAudioStream *st) +{ + if (st->node == NULL) { + return; + } + + dprint(st->state, 1, "%s: format: %d x %s @ %d Hz\n", + st->node->name, st->as.nchannels, + fmt2name[st->as.fmt], st->as.freq); + + if (st->output) { + st->voice.out = AUD_open_out(&st->state->card, st->voice.out, + st->node->name, st, + hda_audio_output_cb, &st->as); + } else { + st->voice.in = AUD_open_in(&st->state->card, st->voice.in, + st->node->name, st, + hda_audio_input_cb, &st->as); + } +} + +static void hda_audio_command(HDACodecDevice *hda, uint32_t nid, uint32_t data) +{ + HDAAudioState *a = DO_UPCAST(HDAAudioState, hda, hda); + HDAAudioStream *st; + const desc_node *node = NULL; + const desc_param *param; + uint32_t verb, payload, response, count, shift; + + if ((data & 0x70000) == 0x70000) { + /* 12/8 id/payload */ + verb = (data >> 8) & 0xfff; + payload = data & 0x00ff; + } else { + /* 4/16 id/payload */ + verb = (data >> 8) & 0xf00; + payload = data & 0xffff; + } + + node = hda_codec_find_node(a->desc, nid); + if (node == NULL) { + goto fail; + } + dprint(a, 2, "%s: nid %d (%s), verb 0x%x, payload 0x%x\n", + __FUNCTION__, nid, node->name, verb, payload); + + switch (verb) { + /* all nodes */ + case AC_VERB_PARAMETERS: + param = hda_codec_find_param(node, payload); + if (param == NULL) { + goto fail; + } + hda_codec_response(hda, true, param->val); + break; + case AC_VERB_GET_SUBSYSTEM_ID: + hda_codec_response(hda, true, a->desc->iid); + break; + + /* all functions */ + case AC_VERB_GET_CONNECT_LIST: + param = hda_codec_find_param(node, AC_PAR_CONNLIST_LEN); + count = param ? param->val : 0; + response = 0; + shift = 0; + while (payload < count && shift < 32) { + response |= node->conn[payload] << shift; + payload++; + shift += 8; + } + hda_codec_response(hda, true, response); + break; + + /* pin widget */ + case AC_VERB_GET_CONFIG_DEFAULT: + hda_codec_response(hda, true, node->config); + break; + case AC_VERB_GET_PIN_WIDGET_CONTROL: + hda_codec_response(hda, true, node->pinctl); + break; + case AC_VERB_SET_PIN_WIDGET_CONTROL: + if (node->pinctl != payload) { + dprint(a, 1, "unhandled pin control bit\n"); + } + hda_codec_response(hda, true, 0); + break; + + /* audio in/out widget */ + case AC_VERB_SET_CHANNEL_STREAMID: + st = a->st + node->stindex; + if (st->node == NULL) { + goto fail; + } + hda_audio_set_running(st, false); + st->stream = (payload >> 4) & 0x0f; + st->channel = payload & 0x0f; + dprint(a, 2, "%s: stream %d, channel %d\n", + st->node->name, st->stream, st->channel); + hda_audio_set_running(st, a->running[st->stream]); + hda_codec_response(hda, true, 0); + break; + case AC_VERB_GET_CONV: + st = a->st + node->stindex; + if (st->node == NULL) { + goto fail; + } + response = st->stream << 4 | st->channel; + hda_codec_response(hda, true, response); + break; + case AC_VERB_SET_STREAM_FORMAT: + st = a->st + node->stindex; + if (st->node == NULL) { + goto fail; + } + st->format = payload; + hda_codec_parse_fmt(st->format, &st->as); + hda_audio_setup(st); + hda_codec_response(hda, true, 0); + break; + case AC_VERB_GET_STREAM_FORMAT: + st = a->st + node->stindex; + if (st->node == NULL) { + goto fail; + } + hda_codec_response(hda, true, st->format); + break; + case AC_VERB_GET_AMP_GAIN_MUTE: + st = a->st + node->stindex; + if (st->node == NULL) { + goto fail; + } + if (payload & AC_AMP_GET_LEFT) { + response = st->gain_left | (st->mute_left ? AC_AMP_MUTE : 0); + } else { + response = st->gain_right | (st->mute_right ? AC_AMP_MUTE : 0); + } + hda_codec_response(hda, true, response); + break; + case AC_VERB_SET_AMP_GAIN_MUTE: + st = a->st + node->stindex; + if (st->node == NULL) { + goto fail; + } + dprint(a, 1, "amp (%s): %s%s%s%s index %d gain %3d %s\n", + st->node->name, + (payload & AC_AMP_SET_OUTPUT) ? "o" : "-", + (payload & AC_AMP_SET_INPUT) ? "i" : "-", + (payload & AC_AMP_SET_LEFT) ? "l" : "-", + (payload & AC_AMP_SET_RIGHT) ? "r" : "-", + (payload & AC_AMP_SET_INDEX) >> AC_AMP_SET_INDEX_SHIFT, + (payload & AC_AMP_GAIN), + (payload & AC_AMP_MUTE) ? "muted" : ""); + if (payload & AC_AMP_SET_LEFT) { + st->gain_left = payload & AC_AMP_GAIN; + st->mute_left = payload & AC_AMP_MUTE; + } + if (payload & AC_AMP_SET_RIGHT) { + st->gain_right = payload & AC_AMP_GAIN; + st->mute_right = payload & AC_AMP_MUTE; + } + hda_audio_set_amp(st); + hda_codec_response(hda, true, 0); + break; + + /* not supported */ + case AC_VERB_SET_POWER_STATE: + case AC_VERB_GET_POWER_STATE: + case AC_VERB_GET_SDI_SELECT: + hda_codec_response(hda, true, 0); + break; + default: + goto fail; + } + return; + +fail: + dprint(a, 1, "%s: not handled: nid %d (%s), verb 0x%x, payload 0x%x\n", + __FUNCTION__, nid, node ? node->name : "?", verb, payload); + hda_codec_response(hda, true, 0); +} + +static void hda_audio_stream(HDACodecDevice *hda, uint32_t stnr, bool running) +{ + HDAAudioState *a = DO_UPCAST(HDAAudioState, hda, hda); + int s; + + a->running[stnr] = running; + for (s = 0; s < ARRAY_SIZE(a->st); s++) { + if (a->st[s].node == NULL) { + continue; + } + if (a->st[s].stream != stnr) { + continue; + } + hda_audio_set_running(&a->st[s], running); + } +} + +static int hda_audio_init(HDACodecDevice *hda, const struct desc_codec *desc) +{ + HDAAudioState *a = DO_UPCAST(HDAAudioState, hda, hda); + HDAAudioStream *st; + const desc_node *node; + const desc_param *param; + uint32_t i, type; + + a->desc = desc; + a->name = a->hda.qdev.info->name; + dprint(a, 1, "%s: cad %d\n", __FUNCTION__, a->hda.cad); + + AUD_register_card("hda", &a->card); + for (i = 0; i < a->desc->nnodes; i++) { + node = a->desc->nodes + i; + param = hda_codec_find_param(node, AC_PAR_AUDIO_WIDGET_CAP); + if (NULL == param) + continue; + type = (param->val & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; + switch (type) { + case AC_WID_AUD_OUT: + case AC_WID_AUD_IN: + assert(node->stindex < ARRAY_SIZE(a->st)); + st = a->st + node->stindex; + st->state = a; + st->node = node; + if (type == AC_WID_AUD_OUT) { + /* unmute output by default */ + st->gain_left = QEMU_HDA_AMP_STEPS; + st->gain_right = QEMU_HDA_AMP_STEPS; + st->bpos = sizeof(st->buf); + st->output = true; + } else { + st->output = false; + } + st->format = AC_FMT_TYPE_PCM | AC_FMT_BITS_16 | + (1 << AC_FMT_CHAN_SHIFT); + hda_codec_parse_fmt(st->format, &st->as); + hda_audio_setup(st); + break; + } + } + return 0; +} + +static int hda_audio_exit(HDACodecDevice *hda) +{ + HDAAudioState *a = DO_UPCAST(HDAAudioState, hda, hda); + HDAAudioStream *st; + int i; + + dprint(a, 1, "%s\n", __FUNCTION__); + for (i = 0; i < ARRAY_SIZE(a->st); i++) { + st = a->st + i; + if (st->node == NULL) { + continue; + } + if (st->output) { + AUD_close_out(&a->card, st->voice.out); + } else { + AUD_close_in(&a->card, st->voice.in); + } + } + AUD_remove_card(&a->card); + return 0; +} + +static int hda_audio_post_load(void *opaque, int version) +{ + HDAAudioState *a = opaque; + HDAAudioStream *st; + int i; + + dprint(a, 1, "%s\n", __FUNCTION__); + for (i = 0; i < ARRAY_SIZE(a->st); i++) { + st = a->st + i; + if (st->node == NULL) + continue; + hda_codec_parse_fmt(st->format, &st->as); + hda_audio_setup(st); + hda_audio_set_amp(st); + hda_audio_set_running(st, a->running[st->stream]); + } + return 0; +} + +static const VMStateDescription vmstate_hda_audio_stream = { + .name = "hda-audio-stream", + .version_id = 1, + .fields = (VMStateField []) { + VMSTATE_UINT32(stream, HDAAudioStream), + VMSTATE_UINT32(channel, HDAAudioStream), + VMSTATE_UINT32(format, HDAAudioStream), + VMSTATE_UINT32(gain_left, HDAAudioStream), + VMSTATE_UINT32(gain_right, HDAAudioStream), + VMSTATE_BOOL(mute_left, HDAAudioStream), + VMSTATE_BOOL(mute_right, HDAAudioStream), + VMSTATE_UINT32(bpos, HDAAudioStream), + VMSTATE_BUFFER(buf, HDAAudioStream), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_hda_audio = { + .name = "hda-audio", + .version_id = 1, + .post_load = hda_audio_post_load, + .fields = (VMStateField []) { + VMSTATE_STRUCT_ARRAY(st, HDAAudioState, 4, 0, + vmstate_hda_audio_stream, + HDAAudioStream), + VMSTATE_BOOL_ARRAY(running, HDAAudioState, 16), + VMSTATE_END_OF_LIST() + } +}; + +static Property hda_audio_properties[] = { + DEFINE_PROP_UINT32("debug", HDAAudioState, debug, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static int hda_audio_init_output(HDACodecDevice *hda) +{ + return hda_audio_init(hda, &output); +} + +static int hda_audio_init_duplex(HDACodecDevice *hda) +{ + return hda_audio_init(hda, &duplex); +} + +static HDACodecDeviceInfo hda_audio_info_output = { + .qdev.name = "hda-output", + .qdev.desc = "HDA Audio Codec, output-only", + .qdev.size = sizeof(HDAAudioState), + .qdev.vmsd = &vmstate_hda_audio, + .qdev.props = hda_audio_properties, + .init = hda_audio_init_output, + .exit = hda_audio_exit, + .command = hda_audio_command, + .stream = hda_audio_stream, +}; + +static HDACodecDeviceInfo hda_audio_info_duplex = { + .qdev.name = "hda-duplex", + .qdev.desc = "HDA Audio Codec, duplex", + .qdev.size = sizeof(HDAAudioState), + .qdev.vmsd = &vmstate_hda_audio, + .qdev.props = hda_audio_properties, + .init = hda_audio_init_duplex, + .exit = hda_audio_exit, + .command = hda_audio_command, + .stream = hda_audio_stream, +}; + +static void hda_audio_register(void) +{ + hda_codec_register(&hda_audio_info_output); + hda_codec_register(&hda_audio_info_duplex); +} +device_init(hda_audio_register); diff -Nru qemu-kvm-0.12.5+noroms/hw/heathrow_pic.c qemu-kvm-0.14.1/hw/heathrow_pic.c --- qemu-kvm-0.12.5+noroms/hw/heathrow_pic.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/heathrow_pic.c 2011-05-11 13:29:46.000000000 +0000 @@ -68,9 +68,6 @@ HeathrowPIC *pic; unsigned int n; -#ifdef TARGET_WORDS_BIGENDIAN - value = bswap32(value); -#endif n = ((addr & 0xfff) - 0x10) >> 4; PIC_DPRINTF("writel: " TARGET_FMT_plx " %u: %08x\n", addr, n, value); if (n >= 2) @@ -120,9 +117,6 @@ } } PIC_DPRINTF("readl: " TARGET_FMT_plx " %u: %08x\n", addr, n, value); -#ifdef TARGET_WORDS_BIGENDIAN - value = bswap32(value); -#endif return value; } @@ -226,9 +220,10 @@ s = qemu_mallocz(sizeof(HeathrowPICS)); /* only 1 CPU */ s->irqs = irqs[0]; - *pmem_index = cpu_register_io_memory(pic_read, pic_write, s); + *pmem_index = cpu_register_io_memory(pic_read, pic_write, s, + DEVICE_LITTLE_ENDIAN); - register_savevm("heathrow_pic", -1, 1, heathrow_pic_save, + register_savevm(NULL, "heathrow_pic", -1, 1, heathrow_pic_save, heathrow_pic_load, s); qemu_register_reset(heathrow_pic_reset, s); return qemu_allocate_irqs(heathrow_pic_set_irq, s, 64); diff -Nru qemu-kvm-0.12.5+noroms/hw/hpet.c qemu-kvm-0.14.1/hw/hpet.c --- qemu-kvm-0.12.5+noroms/hw/hpet.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/hpet.c 2011-05-11 13:29:46.000000000 +0000 @@ -29,34 +29,71 @@ #include "console.h" #include "qemu-timer.h" #include "hpet_emul.h" +#include "sysbus.h" +#include "mc146818rtc.h" //#define HPET_DEBUG #ifdef HPET_DEBUG -#define dprintf printf +#define DPRINTF printf #else -#define dprintf(...) +#define DPRINTF(...) #endif -static HPETState *hpet_statep; +#define HPET_MSI_SUPPORT 0 -uint32_t hpet_in_legacy_mode(void) +struct HPETState; +typedef struct HPETTimer { /* timers */ + uint8_t tn; /*timer number*/ + QEMUTimer *qemu_timer; + struct HPETState *state; + /* Memory-mapped, software visible timer registers */ + uint64_t config; /* configuration/cap */ + uint64_t cmp; /* comparator */ + uint64_t fsb; /* FSB route */ + /* Hidden register state */ + uint64_t period; /* Last value written to comparator */ + uint8_t wrap_flag; /* timer pop will indicate wrap for one-shot 32-bit + * mode. Next pop will be actual timer expiration. + */ +} HPETTimer; + +typedef struct HPETState { + SysBusDevice busdev; + uint64_t hpet_offset; + qemu_irq irqs[HPET_NUM_IRQ_ROUTES]; + uint32_t flags; + uint8_t rtc_irq_level; + uint8_t num_timers; + HPETTimer timer[HPET_MAX_TIMERS]; + + /* Memory-mapped, software visible registers */ + uint64_t capability; /* capabilities */ + uint64_t config; /* configuration */ + uint64_t isr; /* interrupt status reg */ + uint64_t hpet_counter; /* main counter */ + uint8_t hpet_id; /* instance id */ +} HPETState; + +struct hpet_fw_config hpet_cfg = {.count = UINT8_MAX}; + +static uint32_t hpet_in_legacy_mode(HPETState *s) { - if (hpet_statep) - return hpet_statep->config & HPET_CFG_LEGACY; - else - return 0; + return s->config & HPET_CFG_LEGACY; } static uint32_t timer_int_route(struct HPETTimer *timer) { - uint32_t route; - route = (timer->config & HPET_TN_INT_ROUTE_MASK) >> HPET_TN_INT_ROUTE_SHIFT; - return route; + return (timer->config & HPET_TN_INT_ROUTE_MASK) >> HPET_TN_INT_ROUTE_SHIFT; +} + +static uint32_t timer_fsb_route(HPETTimer *t) +{ + return t->config & HPET_TN_FSB_ENABLE; } -static uint32_t hpet_enabled(void) +static uint32_t hpet_enabled(HPETState *s) { - return hpet_statep->config & HPET_CFG_ENABLE; + return s->config & HPET_CFG_ENABLE; } static uint32_t timer_is_periodic(HPETTimer *t) @@ -106,11 +143,9 @@ return ((old & mask) && !(new & mask)); } -static uint64_t hpet_get_ticks(void) +static uint64_t hpet_get_ticks(HPETState *s) { - uint64_t ticks; - ticks = ns_to_ticks(qemu_get_clock(vm_clock) + hpet_statep->hpet_offset); - return ticks; + return ns_to_ticks(qemu_get_clock(vm_clock) + s->hpet_offset); } /* @@ -121,12 +156,14 @@ if (t->config & HPET_TN_32BIT) { uint32_t diff, cmp; + cmp = (uint32_t)t->cmp; diff = cmp - (uint32_t)current; diff = (int32_t)diff > 0 ? diff : (uint32_t)0; return (uint64_t)diff; } else { uint64_t diff, cmp; + cmp = t->cmp; diff = cmp - current; diff = (int64_t)diff > 0 ? diff : (uint64_t)0; @@ -134,34 +171,54 @@ } } -static void update_irq(struct HPETTimer *timer) +static void update_irq(struct HPETTimer *timer, int set) { - qemu_irq irq; + uint64_t mask; + HPETState *s; int route; - if (timer->tn <= 1 && hpet_in_legacy_mode()) { + if (timer->tn <= 1 && hpet_in_legacy_mode(timer->state)) { /* if LegacyReplacementRoute bit is set, HPET specification requires * timer0 be routed to IRQ0 in NON-APIC or IRQ2 in the I/O APIC, * timer1 be routed to IRQ8 in NON-APIC or IRQ8 in the I/O APIC. */ - if (timer->tn == 0) { - irq=timer->state->irqs[0]; - } else - irq=timer->state->irqs[8]; + route = (timer->tn == 0) ? 0 : RTC_ISA_IRQ; } else { - route=timer_int_route(timer); - irq=timer->state->irqs[route]; + route = timer_int_route(timer); } - if (timer_enabled(timer) && hpet_enabled()) { - qemu_irq_pulse(irq); + s = timer->state; + mask = 1 << timer->tn; + if (!set || !timer_enabled(timer) || !hpet_enabled(timer->state)) { + s->isr &= ~mask; + if (!timer_fsb_route(timer)) { + qemu_irq_lower(s->irqs[route]); + } + } else if (timer_fsb_route(timer)) { + stl_phys(timer->fsb >> 32, timer->fsb & 0xffffffff); + } else if (timer->config & HPET_TN_TYPE_LEVEL) { + s->isr |= mask; + qemu_irq_raise(s->irqs[route]); + } else { + s->isr &= ~mask; + qemu_irq_pulse(s->irqs[route]); } } static void hpet_pre_save(void *opaque) { HPETState *s = opaque; + /* save current counter value */ - s->hpet_counter = hpet_get_ticks(); + s->hpet_counter = hpet_get_ticks(s); +} + +static int hpet_pre_load(void *opaque) +{ + HPETState *s = opaque; + + /* version 1 only supports 3, later versions will load the actual value */ + s->num_timers = HPET_MIN_TIMERS; + return 0; } static int hpet_post_load(void *opaque, int version_id) @@ -171,8 +228,19 @@ /* Recalculate the offset between the main counter and guest time */ s->hpet_offset = ticks_to_ns(s->hpet_counter) - qemu_get_clock(vm_clock); - if (hpet_in_legacy_mode()) { - hpet_disable_pit(); + /* Push number of timers into capability returned via HPET_ID */ + s->capability &= ~HPET_ID_NUM_TIM_MASK; + s->capability |= (s->num_timers - 1) << HPET_ID_NUM_TIM_SHIFT; + hpet_cfg.hpet[s->hpet_id].event_timer_block_id = (uint32_t)s->capability; + + /* Derive HPET_MSI_SUPPORT from the capability of the first timer. */ + s->flags &= ~(1 << HPET_MSI_SUPPORT); + if (s->timer[0].config & HPET_TN_FSB_CAP) { + s->flags |= 1 << HPET_MSI_SUPPORT; + } + + if (hpet_in_legacy_mode(s)) { + hpet_pit_disable(); } return 0; @@ -197,17 +265,19 @@ static const VMStateDescription vmstate_hpet = { .name = "hpet", - .version_id = 1, + .version_id = 2, .minimum_version_id = 1, .minimum_version_id_old = 1, .pre_save = hpet_pre_save, + .pre_load = hpet_pre_load, .post_load = hpet_post_load, .fields = (VMStateField []) { VMSTATE_UINT64(config, HPETState), VMSTATE_UINT64(isr, HPETState), VMSTATE_UINT64(hpet_counter, HPETState), - VMSTATE_STRUCT_ARRAY(timer, HPETState, HPET_NUM_TIMERS, 0, - vmstate_hpet_timer, HPETTimer), + VMSTATE_UINT8_V(num_timers, HPETState, 2), + VMSTATE_STRUCT_VARRAY_UINT8(timer, HPETState, num_timers, 0, + vmstate_hpet_timer, HPETTimer), VMSTATE_END_OF_LIST() } }; @@ -217,39 +287,41 @@ */ static void hpet_timer(void *opaque) { - HPETTimer *t = (HPETTimer*)opaque; + HPETTimer *t = opaque; uint64_t diff; uint64_t period = t->period; - uint64_t cur_tick = hpet_get_ticks(); + uint64_t cur_tick = hpet_get_ticks(t->state); if (timer_is_periodic(t) && period != 0) { if (t->config & HPET_TN_32BIT) { - while (hpet_time_after(cur_tick, t->cmp)) + while (hpet_time_after(cur_tick, t->cmp)) { t->cmp = (uint32_t)(t->cmp + t->period); - } else - while (hpet_time_after64(cur_tick, t->cmp)) + } + } else { + while (hpet_time_after64(cur_tick, t->cmp)) { t->cmp += period; - + } + } diff = hpet_calculate_diff(t, cur_tick); - qemu_mod_timer(t->qemu_timer, qemu_get_clock(vm_clock) - + (int64_t)ticks_to_ns(diff)); + qemu_mod_timer(t->qemu_timer, + qemu_get_clock(vm_clock) + (int64_t)ticks_to_ns(diff)); } else if (t->config & HPET_TN_32BIT && !timer_is_periodic(t)) { if (t->wrap_flag) { diff = hpet_calculate_diff(t, cur_tick); - qemu_mod_timer(t->qemu_timer, qemu_get_clock(vm_clock) - + (int64_t)ticks_to_ns(diff)); + qemu_mod_timer(t->qemu_timer, qemu_get_clock(vm_clock) + + (int64_t)ticks_to_ns(diff)); t->wrap_flag = 0; } } - update_irq(t); + update_irq(t, 1); } static void hpet_set_timer(HPETTimer *t) { uint64_t diff; uint32_t wrap_diff; /* how many ticks until we wrap? */ - uint64_t cur_tick = hpet_get_ticks(); + uint64_t cur_tick = hpet_get_ticks(t->state); /* whenever new timer is being set up, make sure wrap_flag is 0 */ t->wrap_flag = 0; @@ -265,13 +337,14 @@ t->wrap_flag = 1; } } - qemu_mod_timer(t->qemu_timer, qemu_get_clock(vm_clock) - + (int64_t)ticks_to_ns(diff)); + qemu_mod_timer(t->qemu_timer, + qemu_get_clock(vm_clock) + (int64_t)ticks_to_ns(diff)); } static void hpet_del_timer(HPETTimer *t) { qemu_del_timer(t->qemu_timer); + update_irq(t, 0); } #ifdef HPET_DEBUG @@ -290,65 +363,70 @@ static uint32_t hpet_ram_readl(void *opaque, target_phys_addr_t addr) { - HPETState *s = (HPETState *)opaque; + HPETState *s = opaque; uint64_t cur_tick, index; - dprintf("qemu: Enter hpet_ram_readl at %" PRIx64 "\n", addr); + DPRINTF("qemu: Enter hpet_ram_readl at %" PRIx64 "\n", addr); index = addr; /*address range of all TN regs*/ if (index >= 0x100 && index <= 0x3ff) { uint8_t timer_id = (addr - 0x100) / 0x20; - if (timer_id > HPET_NUM_TIMERS - 1) { - printf("qemu: timer id out of range\n"); + HPETTimer *timer = &s->timer[timer_id]; + + if (timer_id > s->num_timers) { + DPRINTF("qemu: timer id out of range\n"); return 0; } - HPETTimer *timer = &s->timer[timer_id]; switch ((addr - 0x100) % 0x20) { - case HPET_TN_CFG: - return timer->config; - case HPET_TN_CFG + 4: // Interrupt capabilities - return timer->config >> 32; - case HPET_TN_CMP: // comparator register - return timer->cmp; - case HPET_TN_CMP + 4: - return timer->cmp >> 32; - case HPET_TN_ROUTE: - return timer->fsb >> 32; - default: - dprintf("qemu: invalid hpet_ram_readl\n"); - break; + case HPET_TN_CFG: + return timer->config; + case HPET_TN_CFG + 4: // Interrupt capabilities + return timer->config >> 32; + case HPET_TN_CMP: // comparator register + return timer->cmp; + case HPET_TN_CMP + 4: + return timer->cmp >> 32; + case HPET_TN_ROUTE: + return timer->fsb; + case HPET_TN_ROUTE + 4: + return timer->fsb >> 32; + default: + DPRINTF("qemu: invalid hpet_ram_readl\n"); + break; } } else { switch (index) { - case HPET_ID: - return s->capability; - case HPET_PERIOD: - return s->capability >> 32; - case HPET_CFG: - return s->config; - case HPET_CFG + 4: - dprintf("qemu: invalid HPET_CFG + 4 hpet_ram_readl \n"); - return 0; - case HPET_COUNTER: - if (hpet_enabled()) - cur_tick = hpet_get_ticks(); - else - cur_tick = s->hpet_counter; - dprintf("qemu: reading counter = %" PRIx64 "\n", cur_tick); - return cur_tick; - case HPET_COUNTER + 4: - if (hpet_enabled()) - cur_tick = hpet_get_ticks(); - else - cur_tick = s->hpet_counter; - dprintf("qemu: reading counter + 4 = %" PRIx64 "\n", cur_tick); - return cur_tick >> 32; - case HPET_STATUS: - return s->isr; - default: - dprintf("qemu: invalid hpet_ram_readl\n"); - break; + case HPET_ID: + return s->capability; + case HPET_PERIOD: + return s->capability >> 32; + case HPET_CFG: + return s->config; + case HPET_CFG + 4: + DPRINTF("qemu: invalid HPET_CFG + 4 hpet_ram_readl \n"); + return 0; + case HPET_COUNTER: + if (hpet_enabled(s)) { + cur_tick = hpet_get_ticks(s); + } else { + cur_tick = s->hpet_counter; + } + DPRINTF("qemu: reading counter = %" PRIx64 "\n", cur_tick); + return cur_tick; + case HPET_COUNTER + 4: + if (hpet_enabled(s)) { + cur_tick = hpet_get_ticks(s); + } else { + cur_tick = s->hpet_counter; + } + DPRINTF("qemu: reading counter + 4 = %" PRIx64 "\n", cur_tick); + return cur_tick >> 32; + case HPET_STATUS: + return s->isr; + default: + DPRINTF("qemu: invalid hpet_ram_readl\n"); + break; } } return 0; @@ -374,10 +452,10 @@ uint32_t value) { int i; - HPETState *s = (HPETState *)opaque; + HPETState *s = opaque; uint64_t old_val, new_val, val, index; - dprintf("qemu: Enter hpet_ram_writel at %" PRIx64 " = %#x\n", addr, value); + DPRINTF("qemu: Enter hpet_ram_writel at %" PRIx64 " = %#x\n", addr, value); index = addr; old_val = hpet_ram_readl(opaque, addr); new_val = value; @@ -385,131 +463,151 @@ /*address range of all TN regs*/ if (index >= 0x100 && index <= 0x3ff) { uint8_t timer_id = (addr - 0x100) / 0x20; - dprintf("qemu: hpet_ram_writel timer_id = %#x \n", timer_id); HPETTimer *timer = &s->timer[timer_id]; + DPRINTF("qemu: hpet_ram_writel timer_id = %#x \n", timer_id); + if (timer_id > s->num_timers) { + DPRINTF("qemu: timer id out of range\n"); + return; + } switch ((addr - 0x100) % 0x20) { - case HPET_TN_CFG: - dprintf("qemu: hpet_ram_writel HPET_TN_CFG\n"); - val = hpet_fixup_reg(new_val, old_val, HPET_TN_CFG_WRITE_MASK); - timer->config = (timer->config & 0xffffffff00000000ULL) | val; - if (new_val & HPET_TN_32BIT) { - timer->cmp = (uint32_t)timer->cmp; - timer->period = (uint32_t)timer->period; - } - if (new_val & HPET_TIMER_TYPE_LEVEL) { - printf("qemu: level-triggered hpet not supported\n"); - exit (-1); - } - - break; - case HPET_TN_CFG + 4: // Interrupt capabilities - dprintf("qemu: invalid HPET_TN_CFG+4 write\n"); - break; - case HPET_TN_CMP: // comparator register - dprintf("qemu: hpet_ram_writel HPET_TN_CMP \n"); - if (timer->config & HPET_TN_32BIT) - new_val = (uint32_t)new_val; - if (!timer_is_periodic(timer) || - (timer->config & HPET_TN_SETVAL)) - timer->cmp = (timer->cmp & 0xffffffff00000000ULL) - | new_val; - if (timer_is_periodic(timer)) { - /* - * FIXME: Clamp period to reasonable min value? - * Clamp period to reasonable max value - */ - new_val &= (timer->config & HPET_TN_32BIT ? ~0u : ~0ull) >> 1; - timer->period = (timer->period & 0xffffffff00000000ULL) - | new_val; + case HPET_TN_CFG: + DPRINTF("qemu: hpet_ram_writel HPET_TN_CFG\n"); + if (activating_bit(old_val, new_val, HPET_TN_FSB_ENABLE)) { + update_irq(timer, 0); + } + val = hpet_fixup_reg(new_val, old_val, HPET_TN_CFG_WRITE_MASK); + timer->config = (timer->config & 0xffffffff00000000ULL) | val; + if (new_val & HPET_TN_32BIT) { + timer->cmp = (uint32_t)timer->cmp; + timer->period = (uint32_t)timer->period; + } + if (activating_bit(old_val, new_val, HPET_TN_ENABLE)) { + hpet_set_timer(timer); + } else if (deactivating_bit(old_val, new_val, HPET_TN_ENABLE)) { + hpet_del_timer(timer); + } + break; + case HPET_TN_CFG + 4: // Interrupt capabilities + DPRINTF("qemu: invalid HPET_TN_CFG+4 write\n"); + break; + case HPET_TN_CMP: // comparator register + DPRINTF("qemu: hpet_ram_writel HPET_TN_CMP \n"); + if (timer->config & HPET_TN_32BIT) { + new_val = (uint32_t)new_val; + } + if (!timer_is_periodic(timer) + || (timer->config & HPET_TN_SETVAL)) { + timer->cmp = (timer->cmp & 0xffffffff00000000ULL) | new_val; + } + if (timer_is_periodic(timer)) { + /* + * FIXME: Clamp period to reasonable min value? + * Clamp period to reasonable max value + */ + new_val &= (timer->config & HPET_TN_32BIT ? ~0u : ~0ull) >> 1; + timer->period = + (timer->period & 0xffffffff00000000ULL) | new_val; + } + timer->config &= ~HPET_TN_SETVAL; + if (hpet_enabled(s)) { + hpet_set_timer(timer); + } + break; + case HPET_TN_CMP + 4: // comparator register high order + DPRINTF("qemu: hpet_ram_writel HPET_TN_CMP + 4\n"); + if (!timer_is_periodic(timer) + || (timer->config & HPET_TN_SETVAL)) { + timer->cmp = (timer->cmp & 0xffffffffULL) | new_val << 32; + } else { + /* + * FIXME: Clamp period to reasonable min value? + * Clamp period to reasonable max value + */ + new_val &= (timer->config & HPET_TN_32BIT ? ~0u : ~0ull) >> 1; + timer->period = + (timer->period & 0xffffffffULL) | new_val << 32; } timer->config &= ~HPET_TN_SETVAL; - if (hpet_enabled()) + if (hpet_enabled(s)) { hpet_set_timer(timer); - break; - case HPET_TN_CMP + 4: // comparator register high order - dprintf("qemu: hpet_ram_writel HPET_TN_CMP + 4\n"); - if (!timer_is_periodic(timer) || - (timer->config & HPET_TN_SETVAL)) - timer->cmp = (timer->cmp & 0xffffffffULL) - | new_val << 32; - else { - /* - * FIXME: Clamp period to reasonable min value? - * Clamp period to reasonable max value - */ - new_val &= (timer->config - & HPET_TN_32BIT ? ~0u : ~0ull) >> 1; - timer->period = (timer->period & 0xffffffffULL) - | new_val << 32; } - timer->config &= ~HPET_TN_SETVAL; - if (hpet_enabled()) - hpet_set_timer(timer); - break; - case HPET_TN_ROUTE + 4: - dprintf("qemu: hpet_ram_writel HPET_TN_ROUTE + 4\n"); - break; - default: - dprintf("qemu: invalid hpet_ram_writel\n"); break; + case HPET_TN_ROUTE: + timer->fsb = (timer->fsb & 0xffffffff00000000ULL) | new_val; + break; + case HPET_TN_ROUTE + 4: + timer->fsb = (new_val << 32) | (timer->fsb & 0xffffffff); + break; + default: + DPRINTF("qemu: invalid hpet_ram_writel\n"); + break; } return; } else { switch (index) { - case HPET_ID: - return; - case HPET_CFG: - val = hpet_fixup_reg(new_val, old_val, HPET_CFG_WRITE_MASK); - s->config = (s->config & 0xffffffff00000000ULL) | val; - if (activating_bit(old_val, new_val, HPET_CFG_ENABLE)) { - /* Enable main counter and interrupt generation. */ - s->hpet_offset = ticks_to_ns(s->hpet_counter) - - qemu_get_clock(vm_clock); - for (i = 0; i < HPET_NUM_TIMERS; i++) - if ((&s->timer[i])->cmp != ~0ULL) - hpet_set_timer(&s->timer[i]); + case HPET_ID: + return; + case HPET_CFG: + val = hpet_fixup_reg(new_val, old_val, HPET_CFG_WRITE_MASK); + s->config = (s->config & 0xffffffff00000000ULL) | val; + if (activating_bit(old_val, new_val, HPET_CFG_ENABLE)) { + /* Enable main counter and interrupt generation. */ + s->hpet_offset = + ticks_to_ns(s->hpet_counter) - qemu_get_clock(vm_clock); + for (i = 0; i < s->num_timers; i++) { + if ((&s->timer[i])->cmp != ~0ULL) { + hpet_set_timer(&s->timer[i]); + } } - else if (deactivating_bit(old_val, new_val, HPET_CFG_ENABLE)) { - /* Halt main counter and disable interrupt generation. */ - s->hpet_counter = hpet_get_ticks(); - for (i = 0; i < HPET_NUM_TIMERS; i++) - hpet_del_timer(&s->timer[i]); + } else if (deactivating_bit(old_val, new_val, HPET_CFG_ENABLE)) { + /* Halt main counter and disable interrupt generation. */ + s->hpet_counter = hpet_get_ticks(s); + for (i = 0; i < s->num_timers; i++) { + hpet_del_timer(&s->timer[i]); } - /* i8254 and RTC are disabled when HPET is in legacy mode */ - if (activating_bit(old_val, new_val, HPET_CFG_LEGACY)) { - hpet_disable_pit(); - dprintf("qemu: hpet disabled pit\n"); - } else if (deactivating_bit(old_val, new_val, HPET_CFG_LEGACY)) { - hpet_enable_pit(); - dprintf("qemu: hpet enabled pit\n"); + } + /* i8254 and RTC are disabled when HPET is in legacy mode */ + if (activating_bit(old_val, new_val, HPET_CFG_LEGACY)) { + hpet_pit_disable(); + qemu_irq_lower(s->irqs[RTC_ISA_IRQ]); + } else if (deactivating_bit(old_val, new_val, HPET_CFG_LEGACY)) { + hpet_pit_enable(); + qemu_set_irq(s->irqs[RTC_ISA_IRQ], s->rtc_irq_level); + } + break; + case HPET_CFG + 4: + DPRINTF("qemu: invalid HPET_CFG+4 write \n"); + break; + case HPET_STATUS: + val = new_val & s->isr; + for (i = 0; i < s->num_timers; i++) { + if (val & (1 << i)) { + update_irq(&s->timer[i], 0); } - break; - case HPET_CFG + 4: - dprintf("qemu: invalid HPET_CFG+4 write \n"); - break; - case HPET_STATUS: - /* FIXME: need to handle level-triggered interrupts */ - break; - case HPET_COUNTER: - if (hpet_enabled()) - printf("qemu: Writing counter while HPET enabled!\n"); - s->hpet_counter = (s->hpet_counter & 0xffffffff00000000ULL) - | value; - dprintf("qemu: HPET counter written. ctr = %#x -> %" PRIx64 "\n", - value, s->hpet_counter); - break; - case HPET_COUNTER + 4: - if (hpet_enabled()) - printf("qemu: Writing counter while HPET enabled!\n"); - s->hpet_counter = (s->hpet_counter & 0xffffffffULL) - | (((uint64_t)value) << 32); - dprintf("qemu: HPET counter + 4 written. ctr = %#x -> %" PRIx64 "\n", - value, s->hpet_counter); - break; - default: - dprintf("qemu: invalid hpet_ram_writel\n"); - break; + } + break; + case HPET_COUNTER: + if (hpet_enabled(s)) { + DPRINTF("qemu: Writing counter while HPET enabled!\n"); + } + s->hpet_counter = + (s->hpet_counter & 0xffffffff00000000ULL) | value; + DPRINTF("qemu: HPET counter written. ctr = %#x -> %" PRIx64 "\n", + value, s->hpet_counter); + break; + case HPET_COUNTER + 4: + if (hpet_enabled(s)) { + DPRINTF("qemu: Writing counter while HPET enabled!\n"); + } + s->hpet_counter = + (s->hpet_counter & 0xffffffffULL) | (((uint64_t)value) << 32); + DPRINTF("qemu: HPET counter + 4 written. ctr = %#x -> %" PRIx64 "\n", + value, s->hpet_counter); + break; + default: + DPRINTF("qemu: invalid hpet_ram_writel\n"); + break; } } } @@ -536,58 +634,119 @@ hpet_ram_writel, }; -static void hpet_reset(void *opaque) { - HPETState *s = opaque; +static void hpet_reset(DeviceState *d) +{ + HPETState *s = FROM_SYSBUS(HPETState, sysbus_from_qdev(d)); int i; static int count = 0; - for (i=0; inum_timers; i++) { HPETTimer *timer = &s->timer[i]; + hpet_del_timer(timer); - timer->tn = i; timer->cmp = ~0ULL; - timer->config = HPET_TN_PERIODIC_CAP | HPET_TN_SIZE_CAP; + timer->config = HPET_TN_PERIODIC_CAP | HPET_TN_SIZE_CAP; + if (s->flags & (1 << HPET_MSI_SUPPORT)) { + timer->config |= HPET_TN_FSB_CAP; + } /* advertise availability of ioapic inti2 */ timer->config |= 0x00000004ULL << 32; - timer->state = s; timer->period = 0ULL; timer->wrap_flag = 0; } s->hpet_counter = 0ULL; s->hpet_offset = 0ULL; - /* 64-bit main counter; 3 timers supported; LegacyReplacementRoute. */ - s->capability = 0x8086a201ULL; - s->capability |= ((HPET_CLK_PERIOD) << 32); s->config = 0ULL; - if (count > 0) + if (count > 0) { /* we don't enable pit when hpet_reset is first called (by hpet_init) * because hpet is taking over for pit here. On subsequent invocations, * hpet_reset is called due to system reset. At this point control must * be returned to pit until SW reenables hpet. */ - hpet_enable_pit(); + hpet_pit_enable(); + } + hpet_cfg.hpet[s->hpet_id].event_timer_block_id = (uint32_t)s->capability; + hpet_cfg.hpet[s->hpet_id].address = sysbus_from_qdev(d)->mmio[0].addr; count = 1; } +static void hpet_handle_rtc_irq(void *opaque, int n, int level) +{ + HPETState *s = FROM_SYSBUS(HPETState, opaque); + + s->rtc_irq_level = level; + if (!hpet_in_legacy_mode(s)) { + qemu_set_irq(s->irqs[RTC_ISA_IRQ], level); + } +} -void hpet_init(qemu_irq *irq) { +static int hpet_init(SysBusDevice *dev) +{ + HPETState *s = FROM_SYSBUS(HPETState, dev); int i, iomemtype; - HPETState *s; + HPETTimer *timer; - dprintf ("hpet_init\n"); + if (hpet_cfg.count == UINT8_MAX) { + /* first instance */ + hpet_cfg.count = 0; + } - s = qemu_mallocz(sizeof(HPETState)); - hpet_statep = s; - s->irqs = irq; - for (i=0; itimer[i]; + if (hpet_cfg.count == 8) { + fprintf(stderr, "Only 8 instances of HPET is allowed\n"); + return -1; + } + + s->hpet_id = hpet_cfg.count++; + + for (i = 0; i < HPET_NUM_IRQ_ROUTES; i++) { + sysbus_init_irq(dev, &s->irqs[i]); + } + + if (s->num_timers < HPET_MIN_TIMERS) { + s->num_timers = HPET_MIN_TIMERS; + } else if (s->num_timers > HPET_MAX_TIMERS) { + s->num_timers = HPET_MAX_TIMERS; + } + for (i = 0; i < HPET_MAX_TIMERS; i++) { + timer = &s->timer[i]; timer->qemu_timer = qemu_new_timer(vm_clock, hpet_timer, timer); + timer->tn = i; + timer->state = s; } - vmstate_register(-1, &vmstate_hpet, s); - qemu_register_reset(hpet_reset, s); + + /* 64-bit main counter; LegacyReplacementRoute. */ + s->capability = 0x8086a001ULL; + s->capability |= (s->num_timers - 1) << HPET_ID_NUM_TIM_SHIFT; + s->capability |= ((HPET_CLK_PERIOD) << 32); + + qdev_init_gpio_in(&dev->qdev, hpet_handle_rtc_irq, 1); + /* HPET Area */ iomemtype = cpu_register_io_memory(hpet_ram_read, - hpet_ram_write, s); - cpu_register_physical_memory(HPET_BASE, 0x400, iomemtype); + hpet_ram_write, s, + DEVICE_NATIVE_ENDIAN); + sysbus_init_mmio(dev, 0x400, iomemtype); + return 0; } + +static SysBusDeviceInfo hpet_device_info = { + .qdev.name = "hpet", + .qdev.size = sizeof(HPETState), + .qdev.no_user = 1, + .qdev.vmsd = &vmstate_hpet, + .qdev.reset = hpet_reset, + .init = hpet_init, + .qdev.props = (Property[]) { + DEFINE_PROP_UINT8("timers", HPETState, num_timers, HPET_MIN_TIMERS), + DEFINE_PROP_BIT("msi", HPETState, flags, HPET_MSI_SUPPORT, false), + DEFINE_PROP_END_OF_LIST(), + }, +}; + +static void hpet_register_device(void) +{ + sysbus_register_withprop(&hpet_device_info); +} + +device_init(hpet_register_device) diff -Nru qemu-kvm-0.12.5+noroms/hw/hpet_emul.h qemu-kvm-0.14.1/hw/hpet_emul.h --- qemu-kvm-0.12.5+noroms/hw/hpet_emul.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/hpet_emul.h 2011-05-11 13:29:46.000000000 +0000 @@ -17,8 +17,10 @@ #define HPET_CLK_PERIOD 10000000ULL /* 10000000 femtoseconds == 10ns*/ #define FS_PER_NS 1000000 -#define HPET_NUM_TIMERS 3 -#define HPET_TIMER_TYPE_LEVEL 0x002 +#define HPET_MIN_TIMERS 3 +#define HPET_MAX_TIMERS 32 + +#define HPET_NUM_IRQ_ROUTES 32 #define HPET_CFG_ENABLE 0x001 #define HPET_CFG_LEGACY 0x002 @@ -33,7 +35,10 @@ #define HPET_TN_ROUTE 0x010 #define HPET_CFG_WRITE_MASK 0x3 +#define HPET_ID_NUM_TIM_SHIFT 8 +#define HPET_ID_NUM_TIM_MASK 0x1f00 +#define HPET_TN_TYPE_LEVEL 0x002 #define HPET_TN_ENABLE 0x004 #define HPET_TN_PERIODIC 0x008 #define HPET_TN_PERIODIC_CAP 0x010 @@ -41,42 +46,26 @@ #define HPET_TN_SETVAL 0x040 #define HPET_TN_32BIT 0x100 #define HPET_TN_INT_ROUTE_MASK 0x3e00 -#define HPET_TN_CFG_WRITE_MASK 0x3f4e +#define HPET_TN_FSB_ENABLE 0x4000 +#define HPET_TN_FSB_CAP 0x8000 +#define HPET_TN_CFG_WRITE_MASK 0x7f4e #define HPET_TN_INT_ROUTE_SHIFT 9 #define HPET_TN_INT_ROUTE_CAP_SHIFT 32 #define HPET_TN_CFG_BITS_READONLY_OR_RESERVED 0xffff80b1U -struct HPETState; -typedef struct HPETTimer { /* timers */ - uint8_t tn; /*timer number*/ - QEMUTimer *qemu_timer; - struct HPETState *state; - /* Memory-mapped, software visible timer registers */ - uint64_t config; /* configuration/cap */ - uint64_t cmp; /* comparator */ - uint64_t fsb; /* FSB route, not supported now */ - /* Hidden register state */ - uint64_t period; /* Last value written to comparator */ - uint8_t wrap_flag; /* timer pop will indicate wrap for one-shot 32-bit - * mode. Next pop will be actual timer expiration. - */ -} HPETTimer; - -typedef struct HPETState { - uint64_t hpet_offset; - qemu_irq *irqs; - HPETTimer timer[HPET_NUM_TIMERS]; - - /* Memory-mapped, software visible registers */ - uint64_t capability; /* capabilities */ - uint64_t config; /* configuration */ - uint64_t isr; /* interrupt status reg */ - uint64_t hpet_counter; /* main counter */ -} HPETState; - -#if defined TARGET_I386 -extern uint32_t hpet_in_legacy_mode(void); -extern void hpet_init(qemu_irq *irq); -#endif +struct hpet_fw_entry +{ + uint32_t event_timer_block_id; + uint64_t address; + uint16_t min_tick; + uint8_t page_prot; +} __attribute__ ((packed)); + +struct hpet_fw_config +{ + uint8_t count; + struct hpet_fw_entry hpet[8]; +} __attribute__ ((packed)); +extern struct hpet_fw_config hpet_cfg; #endif diff -Nru qemu-kvm-0.12.5+noroms/hw/hw.h qemu-kvm-0.14.1/hw/hw.h --- qemu-kvm-0.12.5+noroms/hw/hw.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/hw.h 2011-05-11 13:29:46.000000000 +0000 @@ -5,12 +5,9 @@ #include "qemu-common.h" #if defined(TARGET_PHYS_ADDR_BITS) && !defined(NEED_CPU_H) -#include "targphys.h" -#include "poison.h" #include "cpu-common.h" #endif -#include #include "ioport.h" #include "irq.h" @@ -42,8 +39,8 @@ * the new actual bandwidth. It should be new_rate if everything goes ok, and * the old rate otherwise */ -typedef size_t (QEMUFileSetRateLimit)(void *opaque, size_t new_rate); -typedef size_t (QEMUFileGetRateLimit)(void *opaque); +typedef int64_t (QEMUFileSetRateLimit)(void *opaque, int64_t new_rate); +typedef int64_t (QEMUFileGetRateLimit)(void *opaque); QEMUFile *qemu_fopen_ops(void *opaque, QEMUFilePutBufferFunc *put_buffer, QEMUFileGetBufferFunc *get_buffer, @@ -86,8 +83,8 @@ unsigned int qemu_get_be32(QEMUFile *f); uint64_t qemu_get_be64(QEMUFile *f); int qemu_file_rate_limit(QEMUFile *f); -size_t qemu_file_set_rate_limit(QEMUFile *f, size_t new_rate); -size_t qemu_file_get_rate_limit(QEMUFile *f); +int64_t qemu_file_set_rate_limit(QEMUFile *f, int64_t new_rate); +int64_t qemu_file_get_rate_limit(QEMUFile *f); int qemu_file_has_error(QEMUFile *f); void qemu_file_set_error(QEMUFile *f); @@ -248,14 +245,16 @@ void *opaque); typedef int LoadStateHandler(QEMUFile *f, void *opaque, int version_id); -int register_savevm(const char *idstr, +int register_savevm(DeviceState *dev, + const char *idstr, int instance_id, int version_id, SaveStateHandler *save_state, LoadStateHandler *load_state, void *opaque); -int register_savevm_live(const char *idstr, +int register_savevm_live(DeviceState *dev, + const char *idstr, int instance_id, int version_id, SaveSetParamsHandler *set_params, @@ -264,7 +263,9 @@ LoadStateHandler *load_state, void *opaque); -void unregister_savevm(const char *idstr, void *opaque); +void unregister_savevm(DeviceState *dev, const char *idstr, void *opaque); +void register_device_unmigratable(DeviceState *dev, const char *idstr, + void *opaque); typedef void QEMUResetHandler(void *opaque); @@ -314,6 +315,11 @@ bool (*field_exists)(void *opaque, int version_id); } VMStateField; +typedef struct VMStateSubsection { + const VMStateDescription *vmsd; + bool (*needed)(void *opaque); +} VMStateSubsection; + struct VMStateDescription { const char *name; int version_id; @@ -323,10 +329,12 @@ int (*pre_load)(void *opaque); int (*post_load)(void *opaque, int version_id); void (*pre_save)(void *opaque); - void (*post_save)(void *opaque); VMStateField *fields; + const VMStateSubsection *subsections; }; +extern const VMStateInfo vmstate_info_bool; + extern const VMStateInfo vmstate_info_int8; extern const VMStateInfo vmstate_info_int16; extern const VMStateInfo vmstate_info_int32; @@ -481,6 +489,16 @@ .offset = vmstate_offset_array(_state, _field, _type, _num), \ } +#define VMSTATE_STRUCT_VARRAY_UINT8(_field, _state, _field_num, _version, _vmsd, _type) { \ + .name = (stringify(_field)), \ + .num_offset = vmstate_offset_value(_state, _field_num, uint8_t), \ + .version_id = (_version), \ + .vmsd = &(_vmsd), \ + .size = sizeof(_type), \ + .flags = VMS_STRUCT|VMS_VARRAY_INT32, \ + .offset = offsetof(_state, _field), \ +} + #define VMSTATE_STATIC_BUFFER(_field, _state, _version, _test, _start, _size) { \ .name = (stringify(_field)), \ .version_id = (_version), \ @@ -514,6 +532,17 @@ .start = (_start), \ } +#define VMSTATE_VBUFFER_UINT32(_field, _state, _version, _test, _start, _field_size) { \ + .name = (stringify(_field)), \ + .version_id = (_version), \ + .field_exists = (_test), \ + .size_offset = vmstate_offset_value(_state, _field_size, uint32_t),\ + .info = &vmstate_info_buffer, \ + .flags = VMS_VBUFFER|VMS_POINTER, \ + .offset = offsetof(_state, _field), \ + .start = (_start), \ +} + #define VMSTATE_BUFFER_UNSAFE_INFO(_field, _state, _version, _info, _size) { \ .name = (stringify(_field)), \ .version_id = (_version), \ @@ -562,6 +591,16 @@ .offset = vmstate_offset_value(_state, _field, i2c_slave), \ } +extern const VMStateDescription vmstate_usb_device; + +#define VMSTATE_USB_DEVICE(_field, _state) { \ + .name = (stringify(_field)), \ + .size = sizeof(USBDevice), \ + .vmsd = &vmstate_usb_device, \ + .flags = VMS_STRUCT, \ + .offset = vmstate_offset_value(_state, _field, USBDevice), \ +} + #define vmstate_offset_macaddr(_state, _field) \ vmstate_offset_array(_state, _field.a, uint8_t, \ sizeof(typeof_field(_state, _field))) @@ -590,6 +629,9 @@ #define VMSTATE_STRUCT_POINTER(_field, _state, _vmsd, _type) \ VMSTATE_STRUCT_POINTER_TEST(_field, _state, NULL, _vmsd, _type) +#define VMSTATE_BOOL_V(_f, _s, _v) \ + VMSTATE_SINGLE(_f, _s, _v, vmstate_info_bool, bool) + #define VMSTATE_INT8_V(_f, _s, _v) \ VMSTATE_SINGLE(_f, _s, _v, vmstate_info_int8, int8_t) #define VMSTATE_INT16_V(_f, _s, _v) \ @@ -608,6 +650,9 @@ #define VMSTATE_UINT64_V(_f, _s, _v) \ VMSTATE_SINGLE(_f, _s, _v, vmstate_info_uint64, uint64_t) +#define VMSTATE_BOOL(_f, _s) \ + VMSTATE_BOOL_V(_f, _s, 0) + #define VMSTATE_INT8(_f, _s) \ VMSTATE_INT8_V(_f, _s, 0) #define VMSTATE_INT16(_f, _s) \ @@ -671,6 +716,12 @@ #define VMSTATE_PTIMER(_f, _s) \ VMSTATE_PTIMER_V(_f, _s, 0) +#define VMSTATE_BOOL_ARRAY_V(_f, _s, _n, _v) \ + VMSTATE_ARRAY(_f, _s, _n, _v, vmstate_info_bool, bool) + +#define VMSTATE_BOOL_ARRAY(_f, _s, _n) \ + VMSTATE_BOOL_ARRAY_V(_f, _s, _n, 0) + #define VMSTATE_UINT16_ARRAY_V(_f, _s, _n, _v) \ VMSTATE_ARRAY(_f, _s, _n, _v, vmstate_info_uint16, uint16_t) @@ -728,6 +779,9 @@ #define VMSTATE_PARTIAL_VBUFFER(_f, _s, _size) \ VMSTATE_VBUFFER(_f, _s, 0, NULL, 0, _size) +#define VMSTATE_PARTIAL_VBUFFER_UINT32(_f, _s, _size) \ + VMSTATE_VBUFFER_UINT32(_f, _s, 0, NULL, 0, _size) + #define VMSTATE_SUB_VBUFFER(_f, _s, _start, _size) \ VMSTATE_VBUFFER(_f, _s, 0, NULL, _start, _size) @@ -768,11 +822,16 @@ #define VMSTATE_END_OF_LIST() \ {} -extern int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, - void *opaque, int version_id); -extern void vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd, - void *opaque); -extern int vmstate_register(int instance_id, const VMStateDescription *vmsd, - void *base); -void vmstate_unregister(const VMStateDescription *vmsd, void *opaque); +int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, + void *opaque, int version_id); +void vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd, + void *opaque); +int vmstate_register(DeviceState *dev, int instance_id, + const VMStateDescription *vmsd, void *base); +int vmstate_register_with_alias_id(DeviceState *dev, int instance_id, + const VMStateDescription *vmsd, + void *base, int alias_id, + int required_for_version); +void vmstate_unregister(DeviceState *dev, const VMStateDescription *vmsd, + void *opaque); #endif diff -Nru qemu-kvm-0.12.5+noroms/hw/i2c.c qemu-kvm-0.14.1/hw/i2c.c --- qemu-kvm-0.12.5+noroms/hw/i2c.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/i2c.c 2011-05-11 13:29:46.000000000 +0000 @@ -62,7 +62,7 @@ i2c_bus *bus; bus = FROM_QBUS(i2c_bus, qbus_create(&i2c_bus_info, parent, name)); - vmstate_register(-1, &vmstate_i2c_bus, bus); + vmstate_register(NULL, -1, &vmstate_i2c_bus, bus); return bus; } diff -Nru qemu-kvm-0.12.5+noroms/hw/i8254.c qemu-kvm-0.14.1/hw/i8254.c --- qemu-kvm-0.12.5+noroms/hw/i8254.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/i8254.c 2011-05-11 13:29:46.000000000 +0000 @@ -25,7 +25,7 @@ #include "pc.h" #include "isa.h" #include "qemu-timer.h" -#include "qemu-kvm.h" +#include "kvm.h" #include "i8254.h" //#define DEBUG_PIT @@ -487,11 +487,11 @@ #ifdef TARGET_I386 /* When HPET is operating in legacy mode, i8254 timer0 is disabled */ -void hpet_disable_pit(void) +void hpet_pit_disable(void) { PITChannelState *s = &pit_state.channels[0]; - if (kvm_enabled() && qemu_kvm_pit_in_kernel()) { + if (kvm_enabled() && kvm_pit_in_kernel()) { if (qemu_kvm_has_pit_state2()) { kvm_hpet_disable_kpit(); } else { @@ -510,12 +510,12 @@ * timer 0 */ -void hpet_enable_pit(void) +void hpet_pit_enable(void) { PITState *pit = &pit_state; PITChannelState *s = &pit->channels[0]; - if (kvm_enabled() && qemu_kvm_pit_in_kernel()) { + if (kvm_enabled() && kvm_pit_in_kernel()) { if (qemu_kvm_has_pit_state2()) { kvm_hpet_enable_kpit(); } else { @@ -539,7 +539,7 @@ s->irq_timer = qemu_new_timer(vm_clock, pit_irq_timer, s); s->irq = irq; - vmstate_register(base, &vmstate_pit, pit); + vmstate_register(NULL, base, &vmstate_pit, pit); qemu_register_reset(pit_reset, pit); register_ioport_write(base, 4, 1, pit_ioport_write, pit); register_ioport_read(base, 3, 1, pit_ioport_read, pit); diff -Nru qemu-kvm-0.12.5+noroms/hw/i8254-kvm.c qemu-kvm-0.14.1/hw/i8254-kvm.c --- qemu-kvm-0.12.5+noroms/hw/i8254-kvm.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/i8254-kvm.c 2011-05-11 13:29:46.000000000 +0000 @@ -114,7 +114,7 @@ s->irq_timer = qemu_new_timer(vm_clock, dummy_timer, s); vmstate_pit.pre_save = kvm_pit_pre_save; vmstate_pit.post_load = kvm_pit_post_load; - vmstate_register(base, &vmstate_pit, pit); + vmstate_register(NULL, base, &vmstate_pit, pit); qemu_register_reset(pit_reset, pit); pit_reset(pit); diff -Nru qemu-kvm-0.12.5+noroms/hw/i8259.c qemu-kvm-0.14.1/hw/i8259.c --- qemu-kvm-0.12.5+noroms/hw/i8259.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/i8259.c 2011-05-11 13:29:46.000000000 +0000 @@ -23,15 +23,23 @@ */ #include "hw.h" #include "pc.h" +#include "apic.h" #include "isa.h" #include "monitor.h" #include "qemu-timer.h" -#include "qemu-kvm.h" +#include "kvm.h" /* debug PIC */ //#define DEBUG_PIC +#ifdef DEBUG_PIC +#define DPRINTF(fmt, ...) \ + do { printf("pic: " fmt , ## __VA_ARGS__); } while (0) +#else +#define DPRINTF(fmt, ...) +#endif + //#define DEBUG_IRQ_LATENCY //#define DEBUG_IRQ_COUNT @@ -70,6 +78,7 @@ #ifdef DEBUG_IRQ_COUNT static uint64_t irq_count[16]; #endif +PicState2 *isa_pic; /* set irq level. If an edge is detected, then the IRR is set to 1 */ static inline void pic_set_irq1(PicState *s, int irq, int level) @@ -185,9 +194,7 @@ PicState2 *s = opaque; #if defined(DEBUG_PIC) || defined(DEBUG_IRQ_COUNT) if (level != irq_level[irq]) { -#if defined(DEBUG_PIC) - printf("i8259_set_irq: irq=%d level=%d\n", irq, level); -#endif + DPRINTF("i8259_set_irq: irq=%d level=%d\n", irq, level); irq_level[irq] = level; #ifdef DEBUG_IRQ_COUNT if (level == 1) @@ -221,6 +228,7 @@ } extern int time_drift_fix; +extern int64_t timer_acks, timer_ints_to_push; int pic_read_irq(PicState2 *s) { @@ -230,9 +238,8 @@ if (irq >= 0) { pic_intack(&s->pics[0], irq); -#ifndef TARGET_IA64 +#ifdef TARGET_I386 if (time_drift_fix && irq == 0) { - extern int64_t timer_acks, timer_ints_to_push; timer_acks++; if (timer_ints_to_push > 0) { timer_ints_to_push--; @@ -251,7 +258,9 @@ irq2 = 7; } intno = s->pics[1].irq_base + irq2; +#if defined(DEBUG_PIC) || defined(DEBUG_IRQ_LATENCY) irq = irq2 + 8; +#endif } else { intno = s->pics[0].irq_base + irq; } @@ -268,9 +277,7 @@ (double)(qemu_get_clock(vm_clock) - irq_time[irq]) * 1000000.0 / get_ticks_per_sec()); #endif -#if defined(DEBUG_PIC) - printf("pic_interrupt: irq=%d\n", irq); -#endif + DPRINTF("pic_interrupt: irq=%d\n", irq); return intno; } @@ -301,9 +308,7 @@ PicState *s = opaque; int priority, cmd, irq; -#ifdef DEBUG_PIC - printf("pic_write: addr=0x%02x val=0x%02x\n", addr, val); -#endif + DPRINTF("write: addr=0x%02x val=0x%02x\n", addr, val); addr &= 1; if (addr == 0) { if (val & 0x10) { @@ -431,9 +436,7 @@ ret = s->imr; } } -#ifdef DEBUG_PIC - printf("pic_read: addr=0x%02x val=0x%02x\n", addr1, ret); -#endif + DPRINTF("read: addr=0x%02x val=0x%02x\n", addr1, ret); return ret; } @@ -523,7 +526,7 @@ register_ioport_write(elcr_addr, 1, 1, elcr_ioport_write, s); register_ioport_read(elcr_addr, 1, 1, elcr_ioport_read, s); } - vmstate_register(io_addr, &vmstate_pic, s); + vmstate_register(NULL, io_addr, &vmstate_pic, s); qemu_register_reset(pic_reset, s); } @@ -655,7 +658,7 @@ static void kvm_pic_init1(int io_addr, PicState *s) { - vmstate_register(io_addr, &vmstate_pic, s); + vmstate_register(NULL, io_addr, &vmstate_pic, s); qemu_register_reset(pic_reset, s); } diff -Nru qemu-kvm-0.12.5+noroms/hw/ide/ahci.c qemu-kvm-0.14.1/hw/ide/ahci.c --- qemu-kvm-0.12.5+noroms/hw/ide/ahci.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/ide/ahci.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,1152 @@ +/* + * QEMU AHCI Emulation + * + * Copyright (c) 2010 qiaochong@loongson.cn + * Copyright (c) 2010 Roland Elek + * Copyright (c) 2010 Sebastian Herbszt + * Copyright (c) 2010 Alexander Graf + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + * + */ + +#include +#include +#include +#include + +#include "monitor.h" +#include "dma.h" +#include "cpu-common.h" +#include "internal.h" +#include +#include + +/* #define DEBUG_AHCI */ + +#ifdef DEBUG_AHCI +#define DPRINTF(port, fmt, ...) \ +do { fprintf(stderr, "ahci: %s: [%d] ", __FUNCTION__, port); \ + fprintf(stderr, fmt, ## __VA_ARGS__); } while (0) +#else +#define DPRINTF(port, fmt, ...) do {} while(0) +#endif + +static void check_cmd(AHCIState *s, int port); +static int handle_cmd(AHCIState *s,int port,int slot); +static void ahci_reset_port(AHCIState *s, int port); +static void ahci_write_fis_d2h(AHCIDevice *ad, uint8_t *cmd_fis); +static void ahci_init_d2h(AHCIDevice *ad); + +static uint32_t ahci_port_read(AHCIState *s, int port, int offset) +{ + uint32_t val; + AHCIPortRegs *pr; + pr = &s->dev[port].port_regs; + + switch (offset) { + case PORT_LST_ADDR: + val = pr->lst_addr; + break; + case PORT_LST_ADDR_HI: + val = pr->lst_addr_hi; + break; + case PORT_FIS_ADDR: + val = pr->fis_addr; + break; + case PORT_FIS_ADDR_HI: + val = pr->fis_addr_hi; + break; + case PORT_IRQ_STAT: + val = pr->irq_stat; + break; + case PORT_IRQ_MASK: + val = pr->irq_mask; + break; + case PORT_CMD: + val = pr->cmd; + break; + case PORT_TFDATA: + val = ((uint16_t)s->dev[port].port.ifs[0].error << 8) | + s->dev[port].port.ifs[0].status; + break; + case PORT_SIG: + val = pr->sig; + break; + case PORT_SCR_STAT: + if (s->dev[port].port.ifs[0].bs) { + val = SATA_SCR_SSTATUS_DET_DEV_PRESENT_PHY_UP | + SATA_SCR_SSTATUS_SPD_GEN1 | SATA_SCR_SSTATUS_IPM_ACTIVE; + } else { + val = SATA_SCR_SSTATUS_DET_NODEV; + } + break; + case PORT_SCR_CTL: + val = pr->scr_ctl; + break; + case PORT_SCR_ERR: + val = pr->scr_err; + break; + case PORT_SCR_ACT: + pr->scr_act &= ~s->dev[port].finished; + s->dev[port].finished = 0; + val = pr->scr_act; + break; + case PORT_CMD_ISSUE: + val = pr->cmd_issue; + break; + case PORT_RESERVED: + default: + val = 0; + } + DPRINTF(port, "offset: 0x%x val: 0x%x\n", offset, val); + return val; + +} + +static void ahci_irq_raise(AHCIState *s, AHCIDevice *dev) +{ + struct AHCIPCIState *d = container_of(s, AHCIPCIState, ahci); + + DPRINTF(0, "raise irq\n"); + + if (msi_enabled(&d->card)) { + msi_notify(&d->card, 0); + } else { + qemu_irq_raise(s->irq); + } +} + +static void ahci_irq_lower(AHCIState *s, AHCIDevice *dev) +{ + struct AHCIPCIState *d = container_of(s, AHCIPCIState, ahci); + + DPRINTF(0, "lower irq\n"); + + if (!msi_enabled(&d->card)) { + qemu_irq_lower(s->irq); + } +} + +static void ahci_check_irq(AHCIState *s) +{ + int i; + + DPRINTF(-1, "check irq %#x\n", s->control_regs.irqstatus); + + for (i = 0; i < s->ports; i++) { + AHCIPortRegs *pr = &s->dev[i].port_regs; + if (pr->irq_stat & pr->irq_mask) { + s->control_regs.irqstatus |= (1 << i); + } + } + + if (s->control_regs.irqstatus && + (s->control_regs.ghc & HOST_CTL_IRQ_EN)) { + ahci_irq_raise(s, NULL); + } else { + ahci_irq_lower(s, NULL); + } +} + +static void ahci_trigger_irq(AHCIState *s, AHCIDevice *d, + int irq_type) +{ + DPRINTF(d->port_no, "trigger irq %#x -> %x\n", + irq_type, d->port_regs.irq_mask & irq_type); + + d->port_regs.irq_stat |= irq_type; + ahci_check_irq(s); +} + +static void map_page(uint8_t **ptr, uint64_t addr, uint32_t wanted) +{ + target_phys_addr_t len = wanted; + + if (*ptr) { + cpu_physical_memory_unmap(*ptr, len, 1, len); + } + + *ptr = cpu_physical_memory_map(addr, &len, 1); + if (len < wanted) { + cpu_physical_memory_unmap(*ptr, len, 1, len); + *ptr = NULL; + } +} + +static void ahci_port_write(AHCIState *s, int port, int offset, uint32_t val) +{ + AHCIPortRegs *pr = &s->dev[port].port_regs; + + DPRINTF(port, "offset: 0x%x val: 0x%x\n", offset, val); + switch (offset) { + case PORT_LST_ADDR: + pr->lst_addr = val; + map_page(&s->dev[port].lst, + ((uint64_t)pr->lst_addr_hi << 32) | pr->lst_addr, 1024); + s->dev[port].cur_cmd = NULL; + break; + case PORT_LST_ADDR_HI: + pr->lst_addr_hi = val; + map_page(&s->dev[port].lst, + ((uint64_t)pr->lst_addr_hi << 32) | pr->lst_addr, 1024); + s->dev[port].cur_cmd = NULL; + break; + case PORT_FIS_ADDR: + pr->fis_addr = val; + map_page(&s->dev[port].res_fis, + ((uint64_t)pr->fis_addr_hi << 32) | pr->fis_addr, 256); + break; + case PORT_FIS_ADDR_HI: + pr->fis_addr_hi = val; + map_page(&s->dev[port].res_fis, + ((uint64_t)pr->fis_addr_hi << 32) | pr->fis_addr, 256); + break; + case PORT_IRQ_STAT: + pr->irq_stat &= ~val; + break; + case PORT_IRQ_MASK: + pr->irq_mask = val & 0xfdc000ff; + ahci_check_irq(s); + break; + case PORT_CMD: + pr->cmd = val & ~(PORT_CMD_LIST_ON | PORT_CMD_FIS_ON); + + if (pr->cmd & PORT_CMD_START) { + pr->cmd |= PORT_CMD_LIST_ON; + } + + if (pr->cmd & PORT_CMD_FIS_RX) { + pr->cmd |= PORT_CMD_FIS_ON; + } + + /* XXX usually the FIS would be pending on the bus here and + issuing deferred until the OS enables FIS receival. + Instead, we only submit it once - which works in most + cases, but is a hack. */ + if ((pr->cmd & PORT_CMD_FIS_ON) && + !s->dev[port].init_d2h_sent) { + ahci_init_d2h(&s->dev[port]); + s->dev[port].init_d2h_sent = 1; + } + + check_cmd(s, port); + break; + case PORT_TFDATA: + s->dev[port].port.ifs[0].error = (val >> 8) & 0xff; + s->dev[port].port.ifs[0].status = val & 0xff; + break; + case PORT_SIG: + pr->sig = val; + break; + case PORT_SCR_STAT: + pr->scr_stat = val; + break; + case PORT_SCR_CTL: + if (((pr->scr_ctl & AHCI_SCR_SCTL_DET) == 1) && + ((val & AHCI_SCR_SCTL_DET) == 0)) { + ahci_reset_port(s, port); + } + pr->scr_ctl = val; + break; + case PORT_SCR_ERR: + pr->scr_err &= ~val; + break; + case PORT_SCR_ACT: + /* RW1 */ + pr->scr_act |= val; + break; + case PORT_CMD_ISSUE: + pr->cmd_issue |= val; + check_cmd(s, port); + break; + default: + break; + } +} + +static uint32_t ahci_mem_readl(void *ptr, target_phys_addr_t addr) +{ + AHCIState *s = ptr; + uint32_t val = 0; + + addr = addr & 0xfff; + if (addr < AHCI_GENERIC_HOST_CONTROL_REGS_MAX_ADDR) { + switch (addr) { + case HOST_CAP: + val = s->control_regs.cap; + break; + case HOST_CTL: + val = s->control_regs.ghc; + break; + case HOST_IRQ_STAT: + val = s->control_regs.irqstatus; + break; + case HOST_PORTS_IMPL: + val = s->control_regs.impl; + break; + case HOST_VERSION: + val = s->control_regs.version; + break; + } + + DPRINTF(-1, "(addr 0x%08X), val 0x%08X\n", (unsigned) addr, val); + } else if ((addr >= AHCI_PORT_REGS_START_ADDR) && + (addr < (AHCI_PORT_REGS_START_ADDR + + (s->ports * AHCI_PORT_ADDR_OFFSET_LEN)))) { + val = ahci_port_read(s, (addr - AHCI_PORT_REGS_START_ADDR) >> 7, + addr & AHCI_PORT_ADDR_OFFSET_MASK); + } + + return val; +} + + + +static void ahci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val) +{ + AHCIState *s = ptr; + addr = addr & 0xfff; + + /* Only aligned reads are allowed on AHCI */ + if (addr & 3) { + fprintf(stderr, "ahci: Mis-aligned write to addr 0x" + TARGET_FMT_plx "\n", addr); + return; + } + + if (addr < AHCI_GENERIC_HOST_CONTROL_REGS_MAX_ADDR) { + DPRINTF(-1, "(addr 0x%08X), val 0x%08X\n", (unsigned) addr, val); + + switch (addr) { + case HOST_CAP: /* R/WO, RO */ + /* FIXME handle R/WO */ + break; + case HOST_CTL: /* R/W */ + if (val & HOST_CTL_RESET) { + DPRINTF(-1, "HBA Reset\n"); + ahci_reset(container_of(s, AHCIPCIState, ahci)); + } else { + s->control_regs.ghc = (val & 0x3) | HOST_CTL_AHCI_EN; + ahci_check_irq(s); + } + break; + case HOST_IRQ_STAT: /* R/WC, RO */ + s->control_regs.irqstatus &= ~val; + ahci_check_irq(s); + break; + case HOST_PORTS_IMPL: /* R/WO, RO */ + /* FIXME handle R/WO */ + break; + case HOST_VERSION: /* RO */ + /* FIXME report write? */ + break; + default: + DPRINTF(-1, "write to unknown register 0x%x\n", (unsigned)addr); + } + } else if ((addr >= AHCI_PORT_REGS_START_ADDR) && + (addr < (AHCI_PORT_REGS_START_ADDR + + (s->ports * AHCI_PORT_ADDR_OFFSET_LEN)))) { + ahci_port_write(s, (addr - AHCI_PORT_REGS_START_ADDR) >> 7, + addr & AHCI_PORT_ADDR_OFFSET_MASK, val); + } + +} + +static CPUReadMemoryFunc * const ahci_readfn[3]={ + ahci_mem_readl, + ahci_mem_readl, + ahci_mem_readl +}; + +static CPUWriteMemoryFunc * const ahci_writefn[3]={ + ahci_mem_writel, + ahci_mem_writel, + ahci_mem_writel +}; + +static void ahci_reg_init(AHCIState *s) +{ + int i; + + s->control_regs.cap = (s->ports - 1) | + (AHCI_NUM_COMMAND_SLOTS << 8) | + (AHCI_SUPPORTED_SPEED_GEN1 << AHCI_SUPPORTED_SPEED) | + HOST_CAP_NCQ | HOST_CAP_AHCI; + + s->control_regs.impl = (1 << s->ports) - 1; + + s->control_regs.version = AHCI_VERSION_1_0; + + for (i = 0; i < s->ports; i++) { + s->dev[i].port_state = STATE_RUN; + } +} + +static uint32_t read_from_sglist(uint8_t *buffer, uint32_t len, + QEMUSGList *sglist) +{ + uint32_t i = 0; + uint32_t total = 0, once; + ScatterGatherEntry *cur_prd; + uint32_t sgcount; + + cur_prd = sglist->sg; + sgcount = sglist->nsg; + for (i = 0; len && sgcount; i++) { + once = MIN(cur_prd->len, len); + cpu_physical_memory_read(cur_prd->base, buffer, once); + cur_prd++; + sgcount--; + len -= once; + buffer += once; + total += once; + } + + return total; +} + +static uint32_t write_to_sglist(uint8_t *buffer, uint32_t len, + QEMUSGList *sglist) +{ + uint32_t i = 0; + uint32_t total = 0, once; + ScatterGatherEntry *cur_prd; + uint32_t sgcount; + + DPRINTF(-1, "total: 0x%x bytes\n", len); + + cur_prd = sglist->sg; + sgcount = sglist->nsg; + for (i = 0; len && sgcount; i++) { + once = MIN(cur_prd->len, len); + DPRINTF(-1, "write 0x%x bytes to 0x%lx\n", once, (long)cur_prd->base); + cpu_physical_memory_write(cur_prd->base, buffer, once); + cur_prd++; + sgcount--; + len -= once; + buffer += once; + total += once; + } + + return total; +} + +static void check_cmd(AHCIState *s, int port) +{ + AHCIPortRegs *pr = &s->dev[port].port_regs; + int slot; + + if ((pr->cmd & PORT_CMD_START) && pr->cmd_issue) { + for (slot = 0; (slot < 32) && pr->cmd_issue; slot++) { + if ((pr->cmd_issue & (1 << slot)) && + !handle_cmd(s, port, slot)) { + pr->cmd_issue &= ~(1 << slot); + } + } + } +} + +static void ahci_check_cmd_bh(void *opaque) +{ + AHCIDevice *ad = opaque; + + qemu_bh_delete(ad->check_bh); + ad->check_bh = NULL; + + if ((ad->busy_slot != -1) && + !(ad->port.ifs[0].status & (BUSY_STAT|DRQ_STAT))) { + /* no longer busy */ + ad->port_regs.cmd_issue &= ~(1 << ad->busy_slot); + ad->busy_slot = -1; + } + + check_cmd(ad->hba, ad->port_no); +} + +static void ahci_init_d2h(AHCIDevice *ad) +{ + uint8_t init_fis[0x20]; + IDEState *ide_state = &ad->port.ifs[0]; + + memset(init_fis, 0, sizeof(init_fis)); + + init_fis[4] = 1; + init_fis[12] = 1; + + if (ide_state->drive_kind == IDE_CD) { + init_fis[5] = ide_state->lcyl; + init_fis[6] = ide_state->hcyl; + } + + ahci_write_fis_d2h(ad, init_fis); +} + +static void ahci_reset_port(AHCIState *s, int port) +{ + AHCIDevice *d = &s->dev[port]; + AHCIPortRegs *pr = &d->port_regs; + IDEState *ide_state = &d->port.ifs[0]; + int i; + + DPRINTF(port, "reset port\n"); + + ide_bus_reset(&d->port); + ide_state->ncq_queues = AHCI_MAX_CMDS; + + pr->irq_stat = 0; + pr->irq_mask = 0; + pr->scr_stat = 0; + pr->scr_ctl = 0; + pr->scr_err = 0; + pr->scr_act = 0; + d->busy_slot = -1; + d->init_d2h_sent = 0; + + ide_state = &s->dev[port].port.ifs[0]; + if (!ide_state->bs) { + return; + } + + /* reset ncq queue */ + for (i = 0; i < AHCI_MAX_CMDS; i++) { + NCQTransferState *ncq_tfs = &s->dev[port].ncq_tfs[i]; + if (!ncq_tfs->used) { + continue; + } + + if (ncq_tfs->aiocb) { + bdrv_aio_cancel(ncq_tfs->aiocb); + ncq_tfs->aiocb = NULL; + } + + qemu_sglist_destroy(&ncq_tfs->sglist); + ncq_tfs->used = 0; + } + + s->dev[port].port_state = STATE_RUN; + if (!ide_state->bs) { + s->dev[port].port_regs.sig = 0; + ide_state->status = SEEK_STAT | WRERR_STAT; + } else if (ide_state->drive_kind == IDE_CD) { + s->dev[port].port_regs.sig = SATA_SIGNATURE_CDROM; + ide_state->lcyl = 0x14; + ide_state->hcyl = 0xeb; + DPRINTF(port, "set lcyl = %d\n", ide_state->lcyl); + ide_state->status = SEEK_STAT | WRERR_STAT | READY_STAT; + } else { + s->dev[port].port_regs.sig = SATA_SIGNATURE_DISK; + ide_state->status = SEEK_STAT | WRERR_STAT; + } + + ide_state->error = 1; + ahci_init_d2h(d); +} + +static void debug_print_fis(uint8_t *fis, int cmd_len) +{ +#ifdef DEBUG_AHCI + int i; + + fprintf(stderr, "fis:"); + for (i = 0; i < cmd_len; i++) { + if ((i & 0xf) == 0) { + fprintf(stderr, "\n%02x:",i); + } + fprintf(stderr, "%02x ",fis[i]); + } + fprintf(stderr, "\n"); +#endif +} + +static void ahci_write_fis_sdb(AHCIState *s, int port, uint32_t finished) +{ + AHCIPortRegs *pr = &s->dev[port].port_regs; + IDEState *ide_state; + uint8_t *sdb_fis; + + if (!s->dev[port].res_fis || + !(pr->cmd & PORT_CMD_FIS_RX)) { + return; + } + + sdb_fis = &s->dev[port].res_fis[RES_FIS_SDBFIS]; + ide_state = &s->dev[port].port.ifs[0]; + + /* clear memory */ + *(uint32_t*)sdb_fis = 0; + + /* write values */ + sdb_fis[0] = ide_state->error; + sdb_fis[2] = ide_state->status & 0x77; + s->dev[port].finished |= finished; + *(uint32_t*)(sdb_fis + 4) = cpu_to_le32(s->dev[port].finished); + + ahci_trigger_irq(s, &s->dev[port], PORT_IRQ_STAT_SDBS); +} + +static void ahci_write_fis_d2h(AHCIDevice *ad, uint8_t *cmd_fis) +{ + AHCIPortRegs *pr = &ad->port_regs; + uint8_t *d2h_fis; + int i; + target_phys_addr_t cmd_len = 0x80; + int cmd_mapped = 0; + + if (!ad->res_fis || !(pr->cmd & PORT_CMD_FIS_RX)) { + return; + } + + if (!cmd_fis) { + /* map cmd_fis */ + uint64_t tbl_addr = le64_to_cpu(ad->cur_cmd->tbl_addr); + cmd_fis = cpu_physical_memory_map(tbl_addr, &cmd_len, 0); + cmd_mapped = 1; + } + + d2h_fis = &ad->res_fis[RES_FIS_RFIS]; + + d2h_fis[0] = 0x34; + d2h_fis[1] = (ad->hba->control_regs.irqstatus ? (1 << 6) : 0); + d2h_fis[2] = ad->port.ifs[0].status; + d2h_fis[3] = ad->port.ifs[0].error; + + d2h_fis[4] = cmd_fis[4]; + d2h_fis[5] = cmd_fis[5]; + d2h_fis[6] = cmd_fis[6]; + d2h_fis[7] = cmd_fis[7]; + d2h_fis[8] = cmd_fis[8]; + d2h_fis[9] = cmd_fis[9]; + d2h_fis[10] = cmd_fis[10]; + d2h_fis[11] = cmd_fis[11]; + d2h_fis[12] = cmd_fis[12]; + d2h_fis[13] = cmd_fis[13]; + for (i = 14; i < 0x20; i++) { + d2h_fis[i] = 0; + } + + if (d2h_fis[2] & ERR_STAT) { + ahci_trigger_irq(ad->hba, ad, PORT_IRQ_STAT_TFES); + } + + ahci_trigger_irq(ad->hba, ad, PORT_IRQ_D2H_REG_FIS); + + if (cmd_mapped) { + cpu_physical_memory_unmap(cmd_fis, cmd_len, 0, cmd_len); + } +} + +static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist) +{ + AHCICmdHdr *cmd = ad->cur_cmd; + uint32_t opts = le32_to_cpu(cmd->opts); + uint64_t prdt_addr = le64_to_cpu(cmd->tbl_addr) + 0x80; + int sglist_alloc_hint = opts >> AHCI_CMD_HDR_PRDT_LEN; + target_phys_addr_t prdt_len = (sglist_alloc_hint * sizeof(AHCI_SG)); + target_phys_addr_t real_prdt_len = prdt_len; + uint8_t *prdt; + int i; + int r = 0; + + if (!sglist_alloc_hint) { + DPRINTF(ad->port_no, "no sg list given by guest: 0x%08x\n", opts); + return -1; + } + + /* map PRDT */ + if (!(prdt = cpu_physical_memory_map(prdt_addr, &prdt_len, 0))){ + DPRINTF(ad->port_no, "map failed\n"); + return -1; + } + + if (prdt_len < real_prdt_len) { + DPRINTF(ad->port_no, "mapped less than expected\n"); + r = -1; + goto out; + } + + /* Get entries in the PRDT, init a qemu sglist accordingly */ + if (sglist_alloc_hint > 0) { + AHCI_SG *tbl = (AHCI_SG *)prdt; + + qemu_sglist_init(sglist, sglist_alloc_hint); + for (i = 0; i < sglist_alloc_hint; i++) { + /* flags_size is zero-based */ + qemu_sglist_add(sglist, le64_to_cpu(tbl[i].addr), + le32_to_cpu(tbl[i].flags_size) + 1); + } + } + +out: + cpu_physical_memory_unmap(prdt, prdt_len, 0, prdt_len); + return r; +} + +static void ncq_cb(void *opaque, int ret) +{ + NCQTransferState *ncq_tfs = (NCQTransferState *)opaque; + IDEState *ide_state = &ncq_tfs->drive->port.ifs[0]; + + /* Clear bit for this tag in SActive */ + ncq_tfs->drive->port_regs.scr_act &= ~(1 << ncq_tfs->tag); + + if (ret < 0) { + /* error */ + ide_state->error = ABRT_ERR; + ide_state->status = READY_STAT | ERR_STAT; + ncq_tfs->drive->port_regs.scr_err |= (1 << ncq_tfs->tag); + } else { + ide_state->status = READY_STAT | SEEK_STAT; + } + + ahci_write_fis_sdb(ncq_tfs->drive->hba, ncq_tfs->drive->port_no, + (1 << ncq_tfs->tag)); + + DPRINTF(ncq_tfs->drive->port_no, "NCQ transfer tag %d finished\n", + ncq_tfs->tag); + + qemu_sglist_destroy(&ncq_tfs->sglist); + ncq_tfs->used = 0; +} + +static void process_ncq_command(AHCIState *s, int port, uint8_t *cmd_fis, + int slot) +{ + NCQFrame *ncq_fis = (NCQFrame*)cmd_fis; + uint8_t tag = ncq_fis->tag >> 3; + NCQTransferState *ncq_tfs = &s->dev[port].ncq_tfs[tag]; + + if (ncq_tfs->used) { + /* error - already in use */ + fprintf(stderr, "%s: tag %d already used\n", __FUNCTION__, tag); + return; + } + + ncq_tfs->used = 1; + ncq_tfs->drive = &s->dev[port]; + ncq_tfs->slot = slot; + ncq_tfs->lba = ((uint64_t)ncq_fis->lba5 << 40) | + ((uint64_t)ncq_fis->lba4 << 32) | + ((uint64_t)ncq_fis->lba3 << 24) | + ((uint64_t)ncq_fis->lba2 << 16) | + ((uint64_t)ncq_fis->lba1 << 8) | + (uint64_t)ncq_fis->lba0; + + /* Note: We calculate the sector count, but don't currently rely on it. + * The total size of the DMA buffer tells us the transfer size instead. */ + ncq_tfs->sector_count = ((uint16_t)ncq_fis->sector_count_high << 8) | + ncq_fis->sector_count_low; + + DPRINTF(port, "NCQ transfer LBA from %ld to %ld, drive max %ld\n", + ncq_tfs->lba, ncq_tfs->lba + ncq_tfs->sector_count - 2, + s->dev[port].port.ifs[0].nb_sectors - 1); + + ahci_populate_sglist(&s->dev[port], &ncq_tfs->sglist); + ncq_tfs->tag = tag; + + switch(ncq_fis->command) { + case READ_FPDMA_QUEUED: + DPRINTF(port, "NCQ reading %d sectors from LBA %ld, tag %d\n", + ncq_tfs->sector_count-1, ncq_tfs->lba, ncq_tfs->tag); + ncq_tfs->is_read = 1; + + DPRINTF(port, "tag %d aio read %ld\n", ncq_tfs->tag, ncq_tfs->lba); + ncq_tfs->aiocb = dma_bdrv_read(ncq_tfs->drive->port.ifs[0].bs, + &ncq_tfs->sglist, ncq_tfs->lba, + ncq_cb, ncq_tfs); + break; + case WRITE_FPDMA_QUEUED: + DPRINTF(port, "NCQ writing %d sectors to LBA %ld, tag %d\n", + ncq_tfs->sector_count-1, ncq_tfs->lba, ncq_tfs->tag); + ncq_tfs->is_read = 0; + + DPRINTF(port, "tag %d aio write %ld\n", ncq_tfs->tag, ncq_tfs->lba); + ncq_tfs->aiocb = dma_bdrv_write(ncq_tfs->drive->port.ifs[0].bs, + &ncq_tfs->sglist, ncq_tfs->lba, + ncq_cb, ncq_tfs); + break; + default: + DPRINTF(port, "error: tried to process non-NCQ command as NCQ\n"); + qemu_sglist_destroy(&ncq_tfs->sglist); + break; + } +} + +static int handle_cmd(AHCIState *s, int port, int slot) +{ + IDEState *ide_state; + uint32_t opts; + uint64_t tbl_addr; + AHCICmdHdr *cmd; + uint8_t *cmd_fis; + target_phys_addr_t cmd_len; + + if (s->dev[port].port.ifs[0].status & (BUSY_STAT|DRQ_STAT)) { + /* Engine currently busy, try again later */ + DPRINTF(port, "engine busy\n"); + return -1; + } + + cmd = &((AHCICmdHdr *)s->dev[port].lst)[slot]; + + if (!s->dev[port].lst) { + DPRINTF(port, "error: lst not given but cmd handled"); + return -1; + } + + /* remember current slot handle for later */ + s->dev[port].cur_cmd = cmd; + + opts = le32_to_cpu(cmd->opts); + tbl_addr = le64_to_cpu(cmd->tbl_addr); + + cmd_len = 0x80; + cmd_fis = cpu_physical_memory_map(tbl_addr, &cmd_len, 1); + + if (!cmd_fis) { + DPRINTF(port, "error: guest passed us an invalid cmd fis\n"); + return -1; + } + + /* The device we are working for */ + ide_state = &s->dev[port].port.ifs[0]; + + if (!ide_state->bs) { + DPRINTF(port, "error: guest accessed unused port"); + goto out; + } + + debug_print_fis(cmd_fis, 0x90); + //debug_print_fis(cmd_fis, (opts & AHCI_CMD_HDR_CMD_FIS_LEN) * 4); + + switch (cmd_fis[0]) { + case SATA_FIS_TYPE_REGISTER_H2D: + break; + default: + DPRINTF(port, "unknown command cmd_fis[0]=%02x cmd_fis[1]=%02x " + "cmd_fis[2]=%02x\n", cmd_fis[0], cmd_fis[1], + cmd_fis[2]); + goto out; + break; + } + + switch (cmd_fis[1]) { + case SATA_FIS_REG_H2D_UPDATE_COMMAND_REGISTER: + break; + case 0: + break; + default: + DPRINTF(port, "unknown command cmd_fis[0]=%02x cmd_fis[1]=%02x " + "cmd_fis[2]=%02x\n", cmd_fis[0], cmd_fis[1], + cmd_fis[2]); + goto out; + break; + } + + switch (s->dev[port].port_state) { + case STATE_RUN: + if (cmd_fis[15] & ATA_SRST) { + s->dev[port].port_state = STATE_RESET; + } + break; + case STATE_RESET: + if (!(cmd_fis[15] & ATA_SRST)) { + ahci_reset_port(s, port); + } + break; + } + + if (cmd_fis[1] == SATA_FIS_REG_H2D_UPDATE_COMMAND_REGISTER) { + + /* Check for NCQ command */ + if ((cmd_fis[2] == READ_FPDMA_QUEUED) || + (cmd_fis[2] == WRITE_FPDMA_QUEUED)) { + process_ncq_command(s, port, cmd_fis, slot); + goto out; + } + + /* Decompose the FIS */ + ide_state->nsector = (int64_t)((cmd_fis[13] << 8) | cmd_fis[12]); + ide_state->feature = cmd_fis[3]; + if (!ide_state->nsector) { + ide_state->nsector = 256; + } + + if (ide_state->drive_kind != IDE_CD) { + ide_set_sector(ide_state, (cmd_fis[6] << 16) | (cmd_fis[5] << 8) | + cmd_fis[4]); + } + + /* Copy the ACMD field (ATAPI packet, if any) from the AHCI command + * table to ide_state->io_buffer + */ + if (opts & AHCI_CMD_ATAPI) { + memcpy(ide_state->io_buffer, &cmd_fis[AHCI_COMMAND_TABLE_ACMD], 0x10); + ide_state->lcyl = 0x14; + ide_state->hcyl = 0xeb; + debug_print_fis(ide_state->io_buffer, 0x10); + ide_state->feature = IDE_FEATURE_DMA; + s->dev[port].done_atapi_packet = 0; + /* XXX send PIO setup FIS */ + } + + ide_state->error = 0; + + /* Reset transferred byte counter */ + cmd->status = 0; + + /* We're ready to process the command in FIS byte 2. */ + ide_exec_cmd(&s->dev[port].port, cmd_fis[2]); + + if (s->dev[port].port.ifs[0].status & READY_STAT) { + ahci_write_fis_d2h(&s->dev[port], cmd_fis); + } + } + +out: + cpu_physical_memory_unmap(cmd_fis, cmd_len, 1, cmd_len); + + if (s->dev[port].port.ifs[0].status & (BUSY_STAT|DRQ_STAT)) { + /* async command, complete later */ + s->dev[port].busy_slot = slot; + return -1; + } + + /* done handling the command */ + return 0; +} + +/* DMA dev <-> ram */ +static int ahci_start_transfer(IDEDMA *dma) +{ + AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma); + IDEState *s = &ad->port.ifs[0]; + uint32_t size = (uint32_t)(s->data_end - s->data_ptr); + /* write == ram -> device */ + uint32_t opts = le32_to_cpu(ad->cur_cmd->opts); + int is_write = opts & AHCI_CMD_WRITE; + int is_atapi = opts & AHCI_CMD_ATAPI; + int has_sglist = 0; + + if (is_atapi && !ad->done_atapi_packet) { + /* already prepopulated iobuffer */ + ad->done_atapi_packet = 1; + goto out; + } + + if (!ahci_populate_sglist(ad, &s->sg)) { + has_sglist = 1; + } + + DPRINTF(ad->port_no, "%sing %d bytes on %s w/%s sglist\n", + is_write ? "writ" : "read", size, is_atapi ? "atapi" : "ata", + has_sglist ? "" : "o"); + + if (is_write && has_sglist && (s->data_ptr < s->data_end)) { + read_from_sglist(s->data_ptr, size, &s->sg); + } + + if (!is_write && has_sglist && (s->data_ptr < s->data_end)) { + write_to_sglist(s->data_ptr, size, &s->sg); + } + + /* update number of transferred bytes */ + ad->cur_cmd->status = cpu_to_le32(le32_to_cpu(ad->cur_cmd->status) + size); + +out: + /* declare that we processed everything */ + s->data_ptr = s->data_end; + + if (has_sglist) { + qemu_sglist_destroy(&s->sg); + } + + s->end_transfer_func(s); + + if (!(s->status & DRQ_STAT)) { + /* done with DMA */ + ahci_trigger_irq(ad->hba, ad, PORT_IRQ_STAT_DSS); + } + + return 0; +} + +static void ahci_start_dma(IDEDMA *dma, IDEState *s, + BlockDriverCompletionFunc *dma_cb) +{ + AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma); + + DPRINTF(ad->port_no, "\n"); + ad->dma_cb = dma_cb; + ad->dma_status |= BM_STATUS_DMAING; + dma_cb(s, 0); +} + +static int ahci_dma_prepare_buf(IDEDMA *dma, int is_write) +{ + AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma); + IDEState *s = &ad->port.ifs[0]; + int i; + + ahci_populate_sglist(ad, &s->sg); + + s->io_buffer_size = 0; + for (i = 0; i < s->sg.nsg; i++) { + s->io_buffer_size += s->sg.sg[i].len; + } + + DPRINTF(ad->port_no, "len=%#x\n", s->io_buffer_size); + return s->io_buffer_size != 0; +} + +static int ahci_dma_rw_buf(IDEDMA *dma, int is_write) +{ + AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma); + IDEState *s = &ad->port.ifs[0]; + uint8_t *p = s->io_buffer + s->io_buffer_index; + int l = s->io_buffer_size - s->io_buffer_index; + + if (ahci_populate_sglist(ad, &s->sg)) { + return 0; + } + + if (is_write) { + write_to_sglist(p, l, &s->sg); + } else { + read_from_sglist(p, l, &s->sg); + } + + /* update number of transferred bytes */ + ad->cur_cmd->status = cpu_to_le32(le32_to_cpu(ad->cur_cmd->status) + l); + s->io_buffer_index += l; + + DPRINTF(ad->port_no, "len=%#x\n", l); + + return 1; +} + +static int ahci_dma_set_unit(IDEDMA *dma, int unit) +{ + /* only a single unit per link */ + return 0; +} + +static int ahci_dma_add_status(IDEDMA *dma, int status) +{ + AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma); + ad->dma_status |= status; + DPRINTF(ad->port_no, "set status: %x\n", status); + + if (status & BM_STATUS_INT) { + ahci_trigger_irq(ad->hba, ad, PORT_IRQ_STAT_DSS); + } + + return 0; +} + +static int ahci_dma_set_inactive(IDEDMA *dma) +{ + AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma); + + DPRINTF(ad->port_no, "dma done\n"); + + /* update d2h status */ + ahci_write_fis_d2h(ad, NULL); + + ad->dma_cb = NULL; + + /* maybe we still have something to process, check later */ + ad->check_bh = qemu_bh_new(ahci_check_cmd_bh, ad); + qemu_bh_schedule(ad->check_bh); + + return 0; +} + +static void ahci_irq_set(void *opaque, int n, int level) +{ +} + +static void ahci_dma_restart_cb(void *opaque, int running, int reason) +{ +} + +static int ahci_dma_reset(IDEDMA *dma) +{ + return 0; +} + +static const IDEDMAOps ahci_dma_ops = { + .start_dma = ahci_start_dma, + .start_transfer = ahci_start_transfer, + .prepare_buf = ahci_dma_prepare_buf, + .rw_buf = ahci_dma_rw_buf, + .set_unit = ahci_dma_set_unit, + .add_status = ahci_dma_add_status, + .set_inactive = ahci_dma_set_inactive, + .restart_cb = ahci_dma_restart_cb, + .reset = ahci_dma_reset, +}; + +void ahci_init(AHCIState *s, DeviceState *qdev, int ports) +{ + qemu_irq *irqs; + int i; + + s->ports = ports; + s->dev = qemu_mallocz(sizeof(AHCIDevice) * ports); + ahci_reg_init(s); + s->mem = cpu_register_io_memory(ahci_readfn, ahci_writefn, s, + DEVICE_LITTLE_ENDIAN); + irqs = qemu_allocate_irqs(ahci_irq_set, s, s->ports); + + for (i = 0; i < s->ports; i++) { + AHCIDevice *ad = &s->dev[i]; + + ide_bus_new(&ad->port, qdev, i); + ide_init2(&ad->port, irqs[i]); + + ad->hba = s; + ad->port_no = i; + ad->port.dma = &ad->dma; + ad->port.dma->ops = &ahci_dma_ops; + ad->port_regs.cmd = PORT_CMD_SPIN_UP | PORT_CMD_POWER_ON; + } +} + +void ahci_uninit(AHCIState *s) +{ + qemu_free(s->dev); +} + +void ahci_pci_map(PCIDevice *pci_dev, int region_num, + pcibus_t addr, pcibus_t size, int type) +{ + struct AHCIPCIState *d = (struct AHCIPCIState *)pci_dev; + AHCIState *s = &d->ahci; + + cpu_register_physical_memory(addr, size, s->mem); +} + +void ahci_reset(void *opaque) +{ + struct AHCIPCIState *d = opaque; + int i; + + d->ahci.control_regs.irqstatus = 0; + d->ahci.control_regs.ghc = 0; + + for (i = 0; i < d->ahci.ports; i++) { + ahci_reset_port(&d->ahci, i); + } +} diff -Nru qemu-kvm-0.12.5+noroms/hw/ide/ahci.h qemu-kvm-0.14.1/hw/ide/ahci.h --- qemu-kvm-0.12.5+noroms/hw/ide/ahci.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/ide/ahci.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,333 @@ +/* + * QEMU AHCI Emulation + * + * Copyright (c) 2010 qiaochong@loongson.cn + * Copyright (c) 2010 Roland Elek + * Copyright (c) 2010 Sebastian Herbszt + * Copyright (c) 2010 Alexander Graf + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + * + */ + +#ifndef HW_IDE_AHCI_H +#define HW_IDE_AHCI_H + +#define AHCI_PCI_BAR 5 +#define AHCI_MAX_PORTS 32 +#define AHCI_MAX_SG 168 /* hardware max is 64K */ +#define AHCI_DMA_BOUNDARY 0xffffffff +#define AHCI_USE_CLUSTERING 0 +#define AHCI_MAX_CMDS 32 +#define AHCI_CMD_SZ 32 +#define AHCI_CMD_SLOT_SZ (AHCI_MAX_CMDS * AHCI_CMD_SZ) +#define AHCI_RX_FIS_SZ 256 +#define AHCI_CMD_TBL_CDB 0x40 +#define AHCI_CMD_TBL_HDR_SZ 0x80 +#define AHCI_CMD_TBL_SZ (AHCI_CMD_TBL_HDR_SZ + (AHCI_MAX_SG * 16)) +#define AHCI_CMD_TBL_AR_SZ (AHCI_CMD_TBL_SZ * AHCI_MAX_CMDS) +#define AHCI_PORT_PRIV_DMA_SZ (AHCI_CMD_SLOT_SZ + AHCI_CMD_TBL_AR_SZ + \ + AHCI_RX_FIS_SZ) + +#define AHCI_IRQ_ON_SG (1 << 31) +#define AHCI_CMD_ATAPI (1 << 5) +#define AHCI_CMD_WRITE (1 << 6) +#define AHCI_CMD_PREFETCH (1 << 7) +#define AHCI_CMD_RESET (1 << 8) +#define AHCI_CMD_CLR_BUSY (1 << 10) + +#define RX_FIS_D2H_REG 0x40 /* offset of D2H Register FIS data */ +#define RX_FIS_SDB 0x58 /* offset of SDB FIS data */ +#define RX_FIS_UNK 0x60 /* offset of Unknown FIS data */ + +/* global controller registers */ +#define HOST_CAP 0x00 /* host capabilities */ +#define HOST_CTL 0x04 /* global host control */ +#define HOST_IRQ_STAT 0x08 /* interrupt status */ +#define HOST_PORTS_IMPL 0x0c /* bitmap of implemented ports */ +#define HOST_VERSION 0x10 /* AHCI spec. version compliancy */ + +/* HOST_CTL bits */ +#define HOST_CTL_RESET (1 << 0) /* reset controller; self-clear */ +#define HOST_CTL_IRQ_EN (1 << 1) /* global IRQ enable */ +#define HOST_CTL_AHCI_EN (1 << 31) /* AHCI enabled */ + +/* HOST_CAP bits */ +#define HOST_CAP_SSC (1 << 14) /* Slumber capable */ +#define HOST_CAP_AHCI (1 << 18) /* AHCI only */ +#define HOST_CAP_CLO (1 << 24) /* Command List Override support */ +#define HOST_CAP_SSS (1 << 27) /* Staggered Spin-up */ +#define HOST_CAP_NCQ (1 << 30) /* Native Command Queueing */ +#define HOST_CAP_64 (1 << 31) /* PCI DAC (64-bit DMA) support */ + +/* registers for each SATA port */ +#define PORT_LST_ADDR 0x00 /* command list DMA addr */ +#define PORT_LST_ADDR_HI 0x04 /* command list DMA addr hi */ +#define PORT_FIS_ADDR 0x08 /* FIS rx buf addr */ +#define PORT_FIS_ADDR_HI 0x0c /* FIS rx buf addr hi */ +#define PORT_IRQ_STAT 0x10 /* interrupt status */ +#define PORT_IRQ_MASK 0x14 /* interrupt enable/disable mask */ +#define PORT_CMD 0x18 /* port command */ +#define PORT_TFDATA 0x20 /* taskfile data */ +#define PORT_SIG 0x24 /* device TF signature */ +#define PORT_SCR_STAT 0x28 /* SATA phy register: SStatus */ +#define PORT_SCR_CTL 0x2c /* SATA phy register: SControl */ +#define PORT_SCR_ERR 0x30 /* SATA phy register: SError */ +#define PORT_SCR_ACT 0x34 /* SATA phy register: SActive */ +#define PORT_CMD_ISSUE 0x38 /* command issue */ +#define PORT_RESERVED 0x3c /* reserved */ + +/* PORT_IRQ_{STAT,MASK} bits */ +#define PORT_IRQ_COLD_PRES (1 << 31) /* cold presence detect */ +#define PORT_IRQ_TF_ERR (1 << 30) /* task file error */ +#define PORT_IRQ_HBUS_ERR (1 << 29) /* host bus fatal error */ +#define PORT_IRQ_HBUS_DATA_ERR (1 << 28) /* host bus data error */ +#define PORT_IRQ_IF_ERR (1 << 27) /* interface fatal error */ +#define PORT_IRQ_IF_NONFATAL (1 << 26) /* interface non-fatal error */ +#define PORT_IRQ_OVERFLOW (1 << 24) /* xfer exhausted available S/G */ +#define PORT_IRQ_BAD_PMP (1 << 23) /* incorrect port multiplier */ + +#define PORT_IRQ_PHYRDY (1 << 22) /* PhyRdy changed */ +#define PORT_IRQ_DEV_ILCK (1 << 7) /* device interlock */ +#define PORT_IRQ_CONNECT (1 << 6) /* port connect change status */ +#define PORT_IRQ_SG_DONE (1 << 5) /* descriptor processed */ +#define PORT_IRQ_UNK_FIS (1 << 4) /* unknown FIS rx'd */ +#define PORT_IRQ_SDB_FIS (1 << 3) /* Set Device Bits FIS rx'd */ +#define PORT_IRQ_DMAS_FIS (1 << 2) /* DMA Setup FIS rx'd */ +#define PORT_IRQ_PIOS_FIS (1 << 1) /* PIO Setup FIS rx'd */ +#define PORT_IRQ_D2H_REG_FIS (1 << 0) /* D2H Register FIS rx'd */ + +#define PORT_IRQ_FREEZE (PORT_IRQ_HBUS_ERR | PORT_IRQ_IF_ERR | \ + PORT_IRQ_CONNECT | PORT_IRQ_PHYRDY | \ + PORT_IRQ_UNK_FIS) +#define PORT_IRQ_ERROR (PORT_IRQ_FREEZE | PORT_IRQ_TF_ERR | \ + PORT_IRQ_HBUS_DATA_ERR) +#define DEF_PORT_IRQ (PORT_IRQ_ERROR | PORT_IRQ_SG_DONE | \ + PORT_IRQ_SDB_FIS | PORT_IRQ_DMAS_FIS | \ + PORT_IRQ_PIOS_FIS | PORT_IRQ_D2H_REG_FIS) + +/* PORT_CMD bits */ +#define PORT_CMD_ATAPI (1 << 24) /* Device is ATAPI */ +#define PORT_CMD_LIST_ON (1 << 15) /* cmd list DMA engine running */ +#define PORT_CMD_FIS_ON (1 << 14) /* FIS DMA engine running */ +#define PORT_CMD_FIS_RX (1 << 4) /* Enable FIS receive DMA engine */ +#define PORT_CMD_CLO (1 << 3) /* Command list override */ +#define PORT_CMD_POWER_ON (1 << 2) /* Power up device */ +#define PORT_CMD_SPIN_UP (1 << 1) /* Spin up device */ +#define PORT_CMD_START (1 << 0) /* Enable port DMA engine */ + +#define PORT_CMD_ICC_MASK (0xf << 28) /* i/f ICC state mask */ +#define PORT_CMD_ICC_ACTIVE (0x1 << 28) /* Put i/f in active state */ +#define PORT_CMD_ICC_PARTIAL (0x2 << 28) /* Put i/f in partial state */ +#define PORT_CMD_ICC_SLUMBER (0x6 << 28) /* Put i/f in slumber state */ + +#define PORT_IRQ_STAT_DHRS (1 << 0) /* Device to Host Register FIS */ +#define PORT_IRQ_STAT_PSS (1 << 1) /* PIO Setup FIS */ +#define PORT_IRQ_STAT_DSS (1 << 2) /* DMA Setup FIS */ +#define PORT_IRQ_STAT_SDBS (1 << 3) /* Set Device Bits */ +#define PORT_IRQ_STAT_UFS (1 << 4) /* Unknown FIS */ +#define PORT_IRQ_STAT_DPS (1 << 5) /* Descriptor Processed */ +#define PORT_IRQ_STAT_PCS (1 << 6) /* Port Connect Change Status */ +#define PORT_IRQ_STAT_DMPS (1 << 7) /* Device Mechanical Presence + Status */ +#define PORT_IRQ_STAT_PRCS (1 << 22) /* File Ready Status */ +#define PORT_IRQ_STAT_IPMS (1 << 23) /* Incorrect Port Multiplier + Status */ +#define PORT_IRQ_STAT_OFS (1 << 24) /* Overflow Status */ +#define PORT_IRQ_STAT_INFS (1 << 26) /* Interface Non-Fatal Error + Status */ +#define PORT_IRQ_STAT_IFS (1 << 27) /* Interface Fatal Error */ +#define PORT_IRQ_STAT_HBDS (1 << 28) /* Host Bus Data Error Status */ +#define PORT_IRQ_STAT_HBFS (1 << 29) /* Host Bus Fatal Error Status */ +#define PORT_IRQ_STAT_TFES (1 << 30) /* Task File Error Status */ +#define PORT_IRQ_STAT_CPDS (1 << 31) /* Code Port Detect Status */ + +/* ap->flags bits */ +#define AHCI_FLAG_NO_NCQ (1 << 24) +#define AHCI_FLAG_IGN_IRQ_IF_ERR (1 << 25) /* ignore IRQ_IF_ERR */ +#define AHCI_FLAG_HONOR_PI (1 << 26) /* honor PORTS_IMPL */ +#define AHCI_FLAG_IGN_SERR_INTERNAL (1 << 27) /* ignore SERR_INTERNAL */ +#define AHCI_FLAG_32BIT_ONLY (1 << 28) /* force 32bit */ + +#define ATA_SRST (1 << 2) /* software reset */ + +#define STATE_RUN 0 +#define STATE_RESET 1 + +#define SATA_SCR_SSTATUS_DET_NODEV 0x0 +#define SATA_SCR_SSTATUS_DET_DEV_PRESENT_PHY_UP 0x3 + +#define SATA_SCR_SSTATUS_SPD_NODEV 0x00 +#define SATA_SCR_SSTATUS_SPD_GEN1 0x10 + +#define SATA_SCR_SSTATUS_IPM_NODEV 0x000 +#define SATA_SCR_SSTATUS_IPM_ACTIVE 0X100 + +#define AHCI_SCR_SCTL_DET 0xf + +#define SATA_FIS_TYPE_REGISTER_H2D 0x27 +#define SATA_FIS_REG_H2D_UPDATE_COMMAND_REGISTER 0x80 + +#define AHCI_CMD_HDR_CMD_FIS_LEN 0x1f +#define AHCI_CMD_HDR_PRDT_LEN 16 + +#define SATA_SIGNATURE_CDROM 0xeb140000 +#define SATA_SIGNATURE_DISK 0x00000101 + +#define AHCI_GENERIC_HOST_CONTROL_REGS_MAX_ADDR 0x20 + /* Shouldn't this be 0x2c? */ + +#define AHCI_PORT_REGS_START_ADDR 0x100 +#define AHCI_PORT_ADDR_OFFSET_MASK 0x7f +#define AHCI_PORT_ADDR_OFFSET_LEN 0x80 + +#define AHCI_NUM_COMMAND_SLOTS 31 +#define AHCI_SUPPORTED_SPEED 20 +#define AHCI_SUPPORTED_SPEED_GEN1 1 +#define AHCI_VERSION_1_0 0x10000 + +#define AHCI_PROGMODE_MAJOR_REV_1 1 + +#define AHCI_COMMAND_TABLE_ACMD 0x40 + +#define IDE_FEATURE_DMA 1 + +#define READ_FPDMA_QUEUED 0x60 +#define WRITE_FPDMA_QUEUED 0x61 + +#define RES_FIS_DSFIS 0x00 +#define RES_FIS_PSFIS 0x20 +#define RES_FIS_RFIS 0x40 +#define RES_FIS_SDBFIS 0x58 +#define RES_FIS_UFIS 0x60 + +typedef struct AHCIControlRegs { + uint32_t cap; + uint32_t ghc; + uint32_t irqstatus; + uint32_t impl; + uint32_t version; +} AHCIControlRegs; + +typedef struct AHCIPortRegs { + uint32_t lst_addr; + uint32_t lst_addr_hi; + uint32_t fis_addr; + uint32_t fis_addr_hi; + uint32_t irq_stat; + uint32_t irq_mask; + uint32_t cmd; + uint32_t unused0; + uint32_t tfdata; + uint32_t sig; + uint32_t scr_stat; + uint32_t scr_ctl; + uint32_t scr_err; + uint32_t scr_act; + uint32_t cmd_issue; + uint32_t reserved; +} AHCIPortRegs; + +typedef struct AHCICmdHdr { + uint32_t opts; + uint32_t status; + uint64_t tbl_addr; + uint32_t reserved[4]; +} __attribute__ ((packed)) AHCICmdHdr; + +typedef struct AHCI_SG { + uint64_t addr; + uint32_t reserved; + uint32_t flags_size; +} __attribute__ ((packed)) AHCI_SG; + +typedef struct AHCIDevice AHCIDevice; + +typedef struct NCQTransferState { + AHCIDevice *drive; + BlockDriverAIOCB *aiocb; + QEMUSGList sglist; + int is_read; + uint16_t sector_count; + uint64_t lba; + uint8_t tag; + int slot; + int used; +} NCQTransferState; + +struct AHCIDevice { + IDEDMA dma; + IDEBus port; + int port_no; + uint32_t port_state; + uint32_t finished; + AHCIPortRegs port_regs; + struct AHCIState *hba; + QEMUBH *check_bh; + uint8_t *lst; + uint8_t *res_fis; + int dma_status; + int done_atapi_packet; + int busy_slot; + int init_d2h_sent; + BlockDriverCompletionFunc *dma_cb; + AHCICmdHdr *cur_cmd; + NCQTransferState ncq_tfs[AHCI_MAX_CMDS]; +}; + +typedef struct AHCIState { + AHCIDevice *dev; + AHCIControlRegs control_regs; + int mem; + int ports; + qemu_irq irq; +} AHCIState; + +typedef struct AHCIPCIState { + PCIDevice card; + AHCIState ahci; +} AHCIPCIState; + +typedef struct NCQFrame { + uint8_t fis_type; + uint8_t c; + uint8_t command; + uint8_t sector_count_low; + uint8_t lba0; + uint8_t lba1; + uint8_t lba2; + uint8_t fua; + uint8_t lba3; + uint8_t lba4; + uint8_t lba5; + uint8_t sector_count_high; + uint8_t tag; + uint8_t reserved5; + uint8_t reserved6; + uint8_t control; + uint8_t reserved7; + uint8_t reserved8; + uint8_t reserved9; + uint8_t reserved10; +} __attribute__ ((packed)) NCQFrame; + +void ahci_init(AHCIState *s, DeviceState *qdev, int ports); +void ahci_uninit(AHCIState *s); + +void ahci_pci_map(PCIDevice *pci_dev, int region_num, + pcibus_t addr, pcibus_t size, int type); + +void ahci_reset(void *opaque); + +#endif /* HW_IDE_AHCI_H */ diff -Nru qemu-kvm-0.12.5+noroms/hw/ide/cmd646.c qemu-kvm-0.14.1/hw/ide/cmd646.c --- qemu-kvm-0.12.5+noroms/hw/ide/cmd646.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/ide/cmd646.c 2011-05-11 13:29:46.000000000 +0000 @@ -68,15 +68,9 @@ } } -static PCIIDEState *pci_from_bm(BMDMAState *bm) +static uint32_t bmdma_readb_common(PCIIDEState *pci_dev, BMDMAState *bm, + uint32_t addr) { - return bm->pci_dev; -} - -static uint32_t bmdma_readb(void *opaque, uint32_t addr) -{ - BMDMAState *bm = opaque; - PCIIDEState *pci_dev = pci_from_bm(bm); uint32_t val; switch(addr & 3) { @@ -90,7 +84,7 @@ val = bm->status; break; case 3: - if (bm->unit == 0) { + if (bm == &pci_dev->bmdma[0]) { val = pci_dev->dev.config[UDIDETCR0]; } else { val = pci_dev->dev.config[UDIDETCR1]; @@ -106,14 +100,32 @@ return val; } -static void bmdma_writeb(void *opaque, uint32_t addr, uint32_t val) +static uint32_t bmdma_readb_0(void *opaque, uint32_t addr) +{ + PCIIDEState *pci_dev = opaque; + BMDMAState *bm = &pci_dev->bmdma[0]; + + return bmdma_readb_common(pci_dev, bm, addr); +} + +static uint32_t bmdma_readb_1(void *opaque, uint32_t addr) +{ + PCIIDEState *pci_dev = opaque; + BMDMAState *bm = &pci_dev->bmdma[1]; + + return bmdma_readb_common(pci_dev, bm, addr); +} + +static void bmdma_writeb_common(PCIIDEState *pci_dev, BMDMAState *bm, + uint32_t addr, uint32_t val) { - BMDMAState *bm = opaque; - PCIIDEState *pci_dev = pci_from_bm(bm); #ifdef DEBUG_IDE printf("bmdma: writeb 0x%02x : 0x%02x\n", addr, val); #endif switch(addr & 3) { + case 0: + bmdma_cmd_writeb(bm, addr, val); + break; case 1: pci_dev->dev.config[MRDMODE] = (pci_dev->dev.config[MRDMODE] & ~0x30) | (val & 0x30); @@ -123,7 +135,7 @@ bm->status = (val & 0x60) | (bm->status & 1) | (bm->status & ~val & 0x06); break; case 3: - if (bm->unit == 0) + if (bm == &pci_dev->bmdma[0]) pci_dev->dev.config[UDIDETCR0] = val; else pci_dev->dev.config[UDIDETCR1] = val; @@ -131,6 +143,22 @@ } } +static void bmdma_writeb_0(void *opaque, uint32_t addr, uint32_t val) +{ + PCIIDEState *pci_dev = opaque; + BMDMAState *bm = &pci_dev->bmdma[0]; + + bmdma_writeb_common(pci_dev, bm, addr, val); +} + +static void bmdma_writeb_1(void *opaque, uint32_t addr, uint32_t val) +{ + PCIIDEState *pci_dev = opaque; + BMDMAState *bm = &pci_dev->bmdma[1]; + + bmdma_writeb_common(pci_dev, bm, addr, val); +} + static void bmdma_map(PCIDevice *pci_dev, int region_num, pcibus_t addr, pcibus_t size, int type) { @@ -139,22 +167,17 @@ for(i = 0;i < 2; i++) { BMDMAState *bm = &d->bmdma[i]; - d->bus[i].bmdma = bm; - bm->bus = d->bus+i; - bm->pci_dev = d; - qemu_add_vm_change_state_handler(ide_dma_restart_cb, bm); - - register_ioport_write(addr, 1, 1, bmdma_cmd_writeb, bm); - - register_ioport_write(addr + 1, 3, 1, bmdma_writeb, bm); - register_ioport_read(addr, 4, 1, bmdma_readb, bm); - - register_ioport_write(addr + 4, 4, 1, bmdma_addr_writeb, bm); - register_ioport_read(addr + 4, 4, 1, bmdma_addr_readb, bm); - register_ioport_write(addr + 4, 4, 2, bmdma_addr_writew, bm); - register_ioport_read(addr + 4, 4, 2, bmdma_addr_readw, bm); - register_ioport_write(addr + 4, 4, 4, bmdma_addr_writel, bm); - register_ioport_read(addr + 4, 4, 4, bmdma_addr_readl, bm); + + if (i == 0) { + register_ioport_write(addr, 4, 1, bmdma_writeb_0, d); + register_ioport_read(addr, 4, 1, bmdma_readb_0, d); + } else { + register_ioport_write(addr, 4, 1, bmdma_writeb_1, d); + register_ioport_read(addr, 4, 1, bmdma_readb_1, d); + } + + iorange_init(&bm->addr_ioport, &bmdma_addr_ioport_ops, addr + 4, 4); + ioport_register(&bm->addr_ioport); addr += 8; } } @@ -192,7 +215,6 @@ for (i = 0; i < 2; i++) { ide_bus_reset(&d->bus[i]); - ide_dma_reset(&d->bmdma[i]); } } @@ -202,15 +224,15 @@ PCIIDEState *d = DO_UPCAST(PCIIDEState, dev, dev); uint8_t *pci_conf = d->dev.config; qemu_irq *irq; + int i; pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_CMD); pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_CMD_646); - pci_conf[0x08] = 0x07; // IDE controller revision - pci_conf[0x09] = 0x8f; + pci_conf[PCI_REVISION_ID] = 0x07; // IDE controller revision + pci_conf[PCI_CLASS_PROG] = 0x8f; pci_config_set_class(pci_conf, PCI_CLASS_STORAGE_IDE); - pci_conf[PCI_HEADER_TYPE] = PCI_HEADER_TYPE_NORMAL; // header_type pci_conf[0x51] = 0x04; // enable IDE0 if (d->secondary) { @@ -224,15 +246,21 @@ pci_register_bar(dev, 3, 0x4, PCI_BASE_ADDRESS_SPACE_IO, ide_map); pci_register_bar(dev, 4, 0x10, PCI_BASE_ADDRESS_SPACE_IO, bmdma_map); - pci_conf[0x3d] = 0x01; // interrupt on pin 1 + /* TODO: RST# value should be 0 */ + pci_conf[PCI_INTERRUPT_PIN] = 0x01; // interrupt on pin 1 irq = qemu_allocate_irqs(cmd646_set_irq, d, 2); - ide_bus_new(&d->bus[0], &d->dev.qdev); - ide_bus_new(&d->bus[1], &d->dev.qdev); - ide_init2(&d->bus[0], NULL, NULL, irq[0]); - ide_init2(&d->bus[1], NULL, NULL, irq[1]); + for (i = 0; i < 2; i++) { + ide_bus_new(&d->bus[i], &d->dev.qdev, i); + ide_init2(&d->bus[i], irq[i]); + + bmdma_init(&d->bus[i], &d->bmdma[i]); + d->bmdma[i].bus = &d->bus[i]; + qemu_add_vm_change_state_handler(d->bus[i].dma->ops->restart_cb, + &d->bmdma[i].dma); + } - vmstate_register(0, &vmstate_ide_pci, d); + vmstate_register(&dev->qdev, 0, &vmstate_ide_pci, d); qemu_register_reset(cmd646_reset, d); return 0; } diff -Nru qemu-kvm-0.12.5+noroms/hw/ide/core.c qemu-kvm-0.14.1/hw/ide/core.c --- qemu-kvm-0.12.5+noroms/hw/ide/core.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/ide/core.c 2011-05-11 13:29:46.000000000 +0000 @@ -26,16 +26,15 @@ #include #include #include -#include -#include "block.h" -#include "block_int.h" +#include "qemu-error.h" #include "qemu-timer.h" #include "sysemu.h" #include "dma.h" +#include "blockdev.h" #include -static int smart_attributes[][5] = { +static const int smart_attributes[][5] = { /* id, flags, val, wrst, thrsh */ { 0x01, 0x03, 0x64, 0x64, 0x06}, /* raw read */ { 0x03, 0x03, 0x64, 0x64, 0x46}, /* spin up */ @@ -60,8 +59,6 @@ return (media_present(s) && s->nb_sectors <= CD_MAX_SECTORS); } -static void ide_dma_start(IDEState *s, BlockDriverCompletionFunc *dma_cb); -static void ide_dma_restart(IDEState *s, int is_read); static void ide_atapi_cmd_read_dma_cb(void *opaque, int ret); static int ide_handle_rw_error(IDEState *s, int error, int op); @@ -97,6 +94,7 @@ { uint16_t *p; unsigned int oldsize; + IDEDevice *dev; if (s->identify_set) { memcpy(s->io_buffer, s->identify_data, sizeof(s->identify_data)); @@ -137,14 +135,22 @@ put_le16(p + 61, s->nb_sectors >> 16); put_le16(p + 62, 0x07); /* single word dma0-2 supported */ put_le16(p + 63, 0x07); /* mdma0-2 supported */ + put_le16(p + 64, 0x03); /* pio3-4 supported */ put_le16(p + 65, 120); put_le16(p + 66, 120); put_le16(p + 67, 120); put_le16(p + 68, 120); + + if (s->ncq_queues) { + put_le16(p + 75, s->ncq_queues - 1); + /* NCQ supported */ + put_le16(p + 76, (1 << 8)); + } + put_le16(p + 80, 0xf0); /* ata3 -> ata6 supported */ put_le16(p + 81, 0x16); /* conforms to ata5 */ - /* 14=NOP supported, 0=SMART supported */ - put_le16(p + 82, (1 << 14) | 1); + /* 14=NOP supported, 5=WCACHE supported, 0=SMART supported */ + put_le16(p + 82, (1 << 14) | (1 << 5) | 1); /* 13=flush_cache_ext,12=flush_cache,10=lba48 */ put_le16(p + 83, (1 << 14) | (1 << 13) | (1 <<12) | (1 << 10)); /* 14=set to 1, 1=SMART self test, 0=SMART error logging */ @@ -164,6 +170,9 @@ put_le16(p + 101, s->nb_sectors >> 16); put_le16(p + 102, s->nb_sectors >> 32); put_le16(p + 103, s->nb_sectors >> 48); + dev = s->unit ? s->bus->slave : s->bus->master; + if (dev && dev->conf.physical_block_size) + put_le16(p + 106, 0x6000 | get_physical_block_exp(&dev->conf)); memcpy(s->identify_data, p, sizeof(s->identify_data)); s->identify_set = 1; @@ -194,13 +203,12 @@ put_le16(p + 53, 7); /* words 64-70, 54-58, 88 valid */ put_le16(p + 62, 7); /* single word dma0-2 supported */ put_le16(p + 63, 7); /* mdma0-2 supported */ - put_le16(p + 64, 0x3f); /* PIO modes supported */ #else put_le16(p + 49, 1 << 9); /* LBA supported, no DMA */ put_le16(p + 53, 3); /* words 64-70, 54-58 valid */ put_le16(p + 63, 0x103); /* DMA modes XXX: may be incorrect */ - put_le16(p + 64, 1); /* PIO modes */ #endif + put_le16(p + 64, 3); /* pio3-4 supported */ put_le16(p + 65, 0xb4); /* minimum DMA multiword tx cycle time */ put_le16(p + 66, 0xb4); /* recommended DMA multiword tx cycle time */ put_le16(p + 67, 0x12c); /* minimum PIO cycle time without flow control */ @@ -209,6 +217,12 @@ put_le16(p + 71, 30); /* in ns */ put_le16(p + 72, 30); /* in ns */ + if (s->ncq_queues) { + put_le16(p + 75, s->ncq_queues - 1); + /* NCQ supported */ + put_le16(p + 76, (1 << 8)); + } + put_le16(p + 80, 0x1e); /* support up to ATA/ATAPI-4 */ #ifdef USE_DMA_CDROM put_le16(p + 88, 0x3f | (1 << 13)); /* udma5 set and supported */ @@ -289,7 +303,7 @@ /* put signature */ s->nsector = 1; s->sector = 1; - if (s->is_cdrom) { + if (s->drive_kind == IDE_CD) { s->lcyl = 0x14; s->hcyl = 0xeb; } else if (s->bs) { @@ -307,14 +321,6 @@ s->error = ABRT_ERR; } -static inline void ide_dma_submit_check(IDEState *s, - BlockDriverCompletionFunc *dma_cb, BMDMAState *bm) -{ - if (bm->aiocb) - return; - dma_cb(bm, -1); -} - /* prepare data transfer and tell what to do after */ static void ide_transfer_start(IDEState *s, uint8_t *buf, int size, EndTransferFunc *end_transfer_func) @@ -322,8 +328,10 @@ s->end_transfer_func = end_transfer_func; s->data_ptr = buf; s->data_end = buf + size; - if (!(s->status & ERR_STAT)) + if (!(s->status & ERR_STAT)) { s->status |= DRQ_STAT; + } + s->bus->dma->ops->start_transfer(s->bus->dma); } static void ide_transfer_stop(IDEState *s) @@ -388,7 +396,7 @@ ide_set_irq(s->bus); } -static void ide_sector_read(IDEState *s) +void ide_sector_read(IDEState *s) { int64_t sector_num; int ret, n; @@ -421,50 +429,15 @@ } } - -/* return 0 if buffer completed */ -static int dma_buf_prepare(BMDMAState *bm, int is_write) +static void dma_buf_commit(IDEState *s, int is_write) { - IDEState *s = bmdma_active_if(bm); - struct { - uint32_t addr; - uint32_t size; - } prd; - int l, len; - - qemu_sglist_init(&s->sg, s->nsector / (TARGET_PAGE_SIZE/512) + 1); - s->io_buffer_size = 0; - for(;;) { - if (bm->cur_prd_len == 0) { - /* end of table (with a fail safe of one page) */ - if (bm->cur_prd_last || - (bm->cur_addr - bm->addr) >= 4096) - return s->io_buffer_size != 0; - cpu_physical_memory_read(bm->cur_addr, (uint8_t *)&prd, 8); - bm->cur_addr += 8; - prd.addr = le32_to_cpu(prd.addr); - prd.size = le32_to_cpu(prd.size); - len = prd.size & 0xfffe; - if (len == 0) - len = 0x10000; - bm->cur_prd_len = len; - bm->cur_prd_addr = prd.addr; - bm->cur_prd_last = (prd.size & 0x80000000); - } - l = bm->cur_prd_len; - if (l > 0) { - qemu_sglist_add(&s->sg, bm->cur_prd_addr, l); - bm->cur_prd_addr += l; - bm->cur_prd_len -= l; - s->io_buffer_size += l; - } - } - return 1; + qemu_sglist_destroy(&s->sg); } -static void dma_buf_commit(IDEState *s, int is_write) +static void ide_set_inactive(IDEState *s) { - qemu_sglist_destroy(&s->sg); + s->bus->dma->aiocb = NULL; + s->bus->dma->ops->set_inactive(s->bus->dma); } void ide_dma_error(IDEState *s) @@ -472,21 +445,26 @@ ide_transfer_stop(s); s->error = ABRT_ERR; s->status = READY_STAT | ERR_STAT; + ide_set_inactive(s); + s->bus->dma->ops->add_status(s->bus->dma, BM_STATUS_INT); ide_set_irq(s->bus); } static int ide_handle_rw_error(IDEState *s, int error, int op) { int is_read = (op & BM_STATUS_RETRY_READ); - BlockInterfaceErrorAction action = drive_get_on_error(s->bs, is_read); + BlockErrorAction action = bdrv_get_on_error(s->bs, is_read); - if (action == BLOCK_ERR_IGNORE) + if (action == BLOCK_ERR_IGNORE) { + bdrv_mon_event(s->bs, BDRV_ACTION_IGNORE, is_read); return 0; + } if ((error == ENOSPC && action == BLOCK_ERR_STOP_ENOSPC) || action == BLOCK_ERR_STOP_ANY) { - s->bus->bmdma->unit = s->unit; - s->bus->bmdma->status |= op; + s->bus->dma->ops->set_unit(s->bus->dma, s->unit); + s->bus->dma->ops->add_status(s->bus->dma, op); + bdrv_mon_event(s->bs, BDRV_ACTION_STOP, is_read); vm_stop(0); } else { if (op & BM_STATUS_DMA_RETRY) { @@ -495,70 +473,25 @@ } else { ide_rw_error(s); } + bdrv_mon_event(s->bs, BDRV_ACTION_REPORT, is_read); } return 1; } -/* return 0 if buffer completed */ -static int dma_buf_rw(BMDMAState *bm, int is_write) -{ - IDEState *s = bmdma_active_if(bm); - struct { - uint32_t addr; - uint32_t size; - } prd; - int l, len; - - for(;;) { - l = s->io_buffer_size - s->io_buffer_index; - if (l <= 0) - break; - if (bm->cur_prd_len == 0) { - /* end of table (with a fail safe of one page) */ - if (bm->cur_prd_last || - (bm->cur_addr - bm->addr) >= 4096) - return 0; - cpu_physical_memory_read(bm->cur_addr, (uint8_t *)&prd, 8); - bm->cur_addr += 8; - prd.addr = le32_to_cpu(prd.addr); - prd.size = le32_to_cpu(prd.size); - len = prd.size & 0xfffe; - if (len == 0) - len = 0x10000; - bm->cur_prd_len = len; - bm->cur_prd_addr = prd.addr; - bm->cur_prd_last = (prd.size & 0x80000000); - } - if (l > bm->cur_prd_len) - l = bm->cur_prd_len; - if (l > 0) { - if (is_write) { - cpu_physical_memory_write(bm->cur_prd_addr, - s->io_buffer + s->io_buffer_index, l); - } else { - cpu_physical_memory_read(bm->cur_prd_addr, - s->io_buffer + s->io_buffer_index, l); - } - bm->cur_prd_addr += l; - bm->cur_prd_len -= l; - s->io_buffer_index += l; - } - } - return 1; -} - -static void ide_read_dma_cb(void *opaque, int ret) +void ide_dma_cb(void *opaque, int ret) { - BMDMAState *bm = opaque; - IDEState *s = bmdma_active_if(bm); + IDEState *s = opaque; int n; int64_t sector_num; +handle_rw_error: if (ret < 0) { - if (ide_handle_rw_error(s, -ret, - BM_STATUS_DMA_RETRY | BM_STATUS_RETRY_READ)) - { + int op = BM_STATUS_DMA_RETRY; + + if (s->is_read) + op |= BM_STATUS_RETRY_READ; + if (ide_handle_rw_error(s, -ret, op)) { return; } } @@ -566,7 +499,7 @@ n = s->io_buffer_size >> 9; sector_num = ide_get_sector(s); if (n > 0) { - dma_buf_commit(s, 1); + dma_buf_commit(s, s->is_read); sector_num += n; ide_set_sector(s, sector_num); s->nsector -= n; @@ -576,35 +509,47 @@ if (s->nsector == 0) { s->status = READY_STAT | SEEK_STAT; ide_set_irq(s->bus); - eot: - bm->status &= ~BM_STATUS_DMAING; - bm->status |= BM_STATUS_INT; - bm->dma_cb = NULL; - bm->unit = -1; - bm->aiocb = NULL; - return; + goto eot; } /* launch next transfer */ n = s->nsector; s->io_buffer_index = 0; s->io_buffer_size = n * 512; - if (dma_buf_prepare(bm, 1) == 0) + if (s->bus->dma->ops->prepare_buf(s->bus->dma, s->is_read) == 0) goto eot; + #ifdef DEBUG_AIO - printf("aio_read: sector_num=%" PRId64 " n=%d\n", sector_num, n); + printf("ide_dma_cb: sector_num=%" PRId64 " n=%d, is_read=%d\n", + sector_num, n, s->is_read); #endif - bm->aiocb = dma_bdrv_read(s->bs, &s->sg, sector_num, ide_read_dma_cb, bm); - ide_dma_submit_check(s, ide_read_dma_cb, bm); + + if (s->is_read) { + s->bus->dma->aiocb = dma_bdrv_read(s->bs, &s->sg, sector_num, + ide_dma_cb, s); + } else { + s->bus->dma->aiocb = dma_bdrv_write(s->bs, &s->sg, sector_num, + ide_dma_cb, s); + } + + if (!s->bus->dma->aiocb) { + ret = -1; + goto handle_rw_error; + } + return; + +eot: + s->bus->dma->ops->add_status(s->bus->dma, BM_STATUS_INT); + ide_set_inactive(s); } -static void ide_sector_read_dma(IDEState *s) +static void ide_sector_start_dma(IDEState *s, int is_read) { s->status = READY_STAT | SEEK_STAT | DRQ_STAT | BUSY_STAT; s->io_buffer_index = 0; s->io_buffer_size = 0; - s->is_read = 1; - ide_dma_start(s, ide_read_dma_cb); + s->is_read = is_read; + s->bus->dma->ops->start_dma(s->bus->dma, s, ide_dma_cb); } static void ide_sector_write_timer_cb(void *opaque) @@ -613,7 +558,7 @@ ide_set_irq(s->bus); } -static void ide_sector_write(IDEState *s) +void ide_sector_write(IDEState *s) { int64_t sector_num; int ret, n, n1; @@ -645,7 +590,6 @@ } ide_set_sector(s, sector_num + n); -#ifdef TARGET_I386 if (win2k_install_hack && ((++s->irq_count % 16) == 0)) { /* It seems there is a bug in the Windows 2000 installer HDD IDE driver which fills the disk with empty logs when the @@ -653,104 +597,11 @@ that at the expense of slower write performances. Use this option _only_ to install Windows 2000. You must disable it for normal use. */ - qemu_mod_timer(s->sector_write_timer, + qemu_mod_timer(s->sector_write_timer, qemu_get_clock(vm_clock) + (get_ticks_per_sec() / 1000)); - } else -#endif - { - ide_set_irq(s->bus); - } -} - -static void ide_dma_restart_bh(void *opaque) -{ - BMDMAState *bm = opaque; - int is_read; - - qemu_bh_delete(bm->bh); - bm->bh = NULL; - - is_read = !!(bm->status & BM_STATUS_RETRY_READ); - - if (bm->status & BM_STATUS_DMA_RETRY) { - bm->status &= ~(BM_STATUS_DMA_RETRY | BM_STATUS_RETRY_READ); - ide_dma_restart(bmdma_active_if(bm), is_read); - } else if (bm->status & BM_STATUS_PIO_RETRY) { - bm->status &= ~(BM_STATUS_PIO_RETRY | BM_STATUS_RETRY_READ); - if (is_read) { - ide_sector_read(bmdma_active_if(bm)); - } else { - ide_sector_write(bmdma_active_if(bm)); - } - } -} - -void ide_dma_restart_cb(void *opaque, int running, int reason) -{ - BMDMAState *bm = opaque; - - if (!running) - return; - - if (!bm->bh) { - bm->bh = qemu_bh_new(ide_dma_restart_bh, bm); - qemu_bh_schedule(bm->bh); - } -} - -static void ide_write_dma_cb(void *opaque, int ret) -{ - BMDMAState *bm = opaque; - IDEState *s = bmdma_active_if(bm); - int n; - int64_t sector_num; - - if (ret < 0) { - if (ide_handle_rw_error(s, -ret, BM_STATUS_DMA_RETRY)) - return; - } - - n = s->io_buffer_size >> 9; - sector_num = ide_get_sector(s); - if (n > 0) { - dma_buf_commit(s, 0); - sector_num += n; - ide_set_sector(s, sector_num); - s->nsector -= n; - } - - /* end of transfer ? */ - if (s->nsector == 0) { - s->status = READY_STAT | SEEK_STAT; + } else { ide_set_irq(s->bus); - eot: - bm->status &= ~BM_STATUS_DMAING; - bm->status |= BM_STATUS_INT; - bm->dma_cb = NULL; - bm->unit = -1; - bm->aiocb = NULL; - return; } - - n = s->nsector; - s->io_buffer_size = n * 512; - /* launch next transfer */ - if (dma_buf_prepare(bm, 0) == 0) - goto eot; -#ifdef DEBUG_AIO - printf("aio_write: sector_num=%" PRId64 " n=%d\n", sector_num, n); -#endif - bm->aiocb = dma_bdrv_write(s->bs, &s->sg, sector_num, ide_write_dma_cb, bm); - ide_dma_submit_check(s, ide_write_dma_cb, bm); -} - -static void ide_sector_write_dma(IDEState *s) -{ - s->status = READY_STAT | SEEK_STAT | DRQ_STAT | BUSY_STAT; - s->io_buffer_index = 0; - s->io_buffer_size = 0; - s->is_read = 0; - ide_dma_start(s, ide_write_dma_cb); } void ide_atapi_cmd_ok(IDEState *s) @@ -789,12 +640,32 @@ { IDEState *s = opaque; - /* XXX: how do we signal I/O errors here? */ + if (ret < 0) { + /* XXX: What sector number to set here? */ + if (ide_handle_rw_error(s, -ret, BM_STATUS_RETRY_FLUSH)) { + return; + } + } s->status = READY_STAT | SEEK_STAT; ide_set_irq(s->bus); } +void ide_flush_cache(IDEState *s) +{ + BlockDriverAIOCB *acb; + + if (s->bs == NULL) { + ide_flush_cb(s, 0); + return; + } + + acb = bdrv_aio_flush(s->bs, ide_flush_cb, s); + if (acb == NULL) { + ide_flush_cb(s, -EIO); + } +} + static inline void cpu_to_ube16(uint8_t *buf, int val) { buf[0] = val >> 8; @@ -915,11 +786,11 @@ size = s->cd_sector_size - s->io_buffer_index; if (size > s->elementary_transfer_size) size = s->elementary_transfer_size; - ide_transfer_start(s, s->io_buffer + s->io_buffer_index, - size, ide_atapi_cmd_reply_end); s->packet_transfer_size -= size; s->elementary_transfer_size -= size; s->io_buffer_index += size; + ide_transfer_start(s, s->io_buffer + s->io_buffer_index - size, + size, ide_atapi_cmd_reply_end); } else { /* a new transfer is needed */ s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO; @@ -944,11 +815,11 @@ if (size > (s->cd_sector_size - s->io_buffer_index)) size = (s->cd_sector_size - s->io_buffer_index); } - ide_transfer_start(s, s->io_buffer + s->io_buffer_index, - size, ide_atapi_cmd_reply_end); s->packet_transfer_size -= size; s->elementary_transfer_size -= size; s->io_buffer_index += size; + ide_transfer_start(s, s->io_buffer + s->io_buffer_index - size, + size, ide_atapi_cmd_reply_end); ide_set_irq(s->bus); #ifdef DEBUG_IDE_ATAPI printf("status=0x%x\n", s->status); @@ -970,7 +841,8 @@ if (s->atapi_dma) { s->status = READY_STAT | SEEK_STAT | DRQ_STAT; - ide_dma_start(s, ide_atapi_cmd_read_dma_cb); + s->bus->dma->ops->start_dma(s->bus->dma, s, + ide_atapi_cmd_read_dma_cb); } else { s->status = READY_STAT | SEEK_STAT; ide_atapi_cmd_reply_end(s); @@ -996,8 +868,7 @@ /* XXX: handle read errors */ static void ide_atapi_cmd_read_dma_cb(void *opaque, int ret) { - BMDMAState *bm = opaque; - IDEState *s = bmdma_active_if(bm); + IDEState *s = opaque; int data_offset, n; if (ret < 0) { @@ -1023,7 +894,7 @@ s->lba += n; } s->packet_transfer_size -= s->io_buffer_size; - if (dma_buf_rw(bm, 1) == 0) + if (s->bus->dma->ops->rw_buf(s->bus->dma, 1) == 0) goto eot; } @@ -1032,11 +903,8 @@ s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO | ATAPI_INT_REASON_CD; ide_set_irq(s->bus); eot: - bm->status &= ~BM_STATUS_DMAING; - bm->status |= BM_STATUS_INT; - bm->dma_cb = NULL; - bm->unit = -1; - bm->aiocb = NULL; + s->bus->dma->ops->add_status(s->bus->dma, BM_STATUS_INT); + ide_set_inactive(s); return; } @@ -1055,12 +923,13 @@ #ifdef DEBUG_AIO printf("aio_read_cd: lba=%u n=%d\n", s->lba, n); #endif - bm->iov.iov_base = (void *)(s->io_buffer + data_offset); - bm->iov.iov_len = n * 4 * 512; - qemu_iovec_init_external(&bm->qiov, &bm->iov, 1); - bm->aiocb = bdrv_aio_readv(s->bs, (int64_t)s->lba << 2, &bm->qiov, - n * 4, ide_atapi_cmd_read_dma_cb, bm); - if (!bm->aiocb) { + s->bus->dma->iov.iov_base = (void *)(s->io_buffer + data_offset); + s->bus->dma->iov.iov_len = n * 4 * 512; + qemu_iovec_init_external(&s->bus->dma->qiov, &s->bus->dma->iov, 1); + s->bus->dma->aiocb = bdrv_aio_readv(s->bs, (int64_t)s->lba << 2, + &s->bus->dma->qiov, n * 4, + ide_atapi_cmd_read_dma_cb, s); + if (!s->bus->dma->aiocb) { /* Note: media not present is the most likely case */ ide_atapi_cmd_error(s, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT); @@ -1081,7 +950,8 @@ /* XXX: check if BUSY_STAT should be set */ s->status = READY_STAT | SEEK_STAT | DRQ_STAT | BUSY_STAT; - ide_dma_start(s, ide_atapi_cmd_read_dma_cb); + s->bus->dma->ops->start_dma(s->bus->dma, s, + ide_atapi_cmd_read_dma_cb); } static void ide_atapi_cmd_read(IDEState *s, int lba, int nb_sectors, @@ -1638,6 +1508,21 @@ ide_atapi_cmd_reply(s, len, max_len); break; } + case GPCMD_GET_EVENT_STATUS_NOTIFICATION: + max_len = ube16_to_cpu(packet + 7); + + if (packet[1] & 0x01) { /* polling */ + /* We don't support any event class (yet). */ + cpu_to_ube16(buf, 0x00); /* No event descriptor returned */ + buf[2] = 0x80; /* No Event Available (NEA) */ + buf[3] = 0x00; /* Empty supported event classes */ + ide_atapi_cmd_reply(s, 4, max_len); + } else { /* asynchronous mode */ + /* Only polling is supported, asynchronous mode is not. */ + ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, + ASC_INV_FIELD_IN_CMD_PACKET); + } + break; default: ide_atapi_cmd_error(s, SENSE_ILLEGAL_REQUEST, ASC_ILLEGAL_OPCODE); @@ -1699,11 +1584,15 @@ } /* called when the inserted state of the media has changed */ -static void cdrom_change_cb(void *opaque) +static void cdrom_change_cb(void *opaque, int reason) { IDEState *s = opaque; uint64_t nb_sectors; + if (!(reason & CHANGE_MEDIA)) { + return; + } + bdrv_get_geometry(s->bs, &nb_sectors); s->nb_sectors = nb_sectors; @@ -1746,9 +1635,6 @@ void ide_ioport_write(void *opaque, uint32_t addr, uint32_t val) { IDEBus *bus = opaque; - IDEState *s; - int n; - int lba48 = 0; #ifdef DEBUG_IDE printf("IDE: write addr=0x%x val=0x%02x\n", addr, val); @@ -1809,427 +1695,436 @@ default: case 7: /* command */ + ide_exec_cmd(bus, val); + break; + } +} + + +void ide_exec_cmd(IDEBus *bus, uint32_t val) +{ + IDEState *s; + int n; + int lba48 = 0; + #if defined(DEBUG_IDE) - printf("ide: CMD=%02x\n", val); + printf("ide: CMD=%02x\n", val); #endif - s = idebus_active_if(bus); - /* ignore commands to non existant slave */ - if (s != bus->ifs && !s->bs) - break; + s = idebus_active_if(bus); + /* ignore commands to non existant slave */ + if (s != bus->ifs && !s->bs) + return; - /* Only DEVICE RESET is allowed while BSY or/and DRQ are set */ - if ((s->status & (BUSY_STAT|DRQ_STAT)) && val != WIN_DEVICE_RESET) - break; + /* Only DEVICE RESET is allowed while BSY or/and DRQ are set */ + if ((s->status & (BUSY_STAT|DRQ_STAT)) && val != WIN_DEVICE_RESET) + return; - switch(val) { - case WIN_IDENTIFY: - if (s->bs && !s->is_cdrom) { - if (!s->is_cf) - ide_identify(s); - else - ide_cfata_identify(s); - s->status = READY_STAT | SEEK_STAT; - ide_transfer_start(s, s->io_buffer, 512, ide_transfer_stop); - } else { - if (s->is_cdrom) { - ide_set_signature(s); - } - ide_abort_command(s); - } - ide_set_irq(s->bus); - break; - case WIN_SPECIFY: - case WIN_RECAL: - s->error = 0; + switch(val) { + case WIN_IDENTIFY: + if (s->bs && s->drive_kind != IDE_CD) { + if (s->drive_kind != IDE_CFATA) + ide_identify(s); + else + ide_cfata_identify(s); s->status = READY_STAT | SEEK_STAT; - ide_set_irq(s->bus); - break; - case WIN_SETMULT: - if (s->is_cf && s->nsector == 0) { - /* Disable Read and Write Multiple */ - s->mult_sectors = 0; - s->status = READY_STAT | SEEK_STAT; - } else if ((s->nsector & 0xff) != 0 && - ((s->nsector & 0xff) > MAX_MULT_SECTORS || - (s->nsector & (s->nsector - 1)) != 0)) { - ide_abort_command(s); - } else { - s->mult_sectors = s->nsector & 0xff; - s->status = READY_STAT | SEEK_STAT; + ide_transfer_start(s, s->io_buffer, 512, ide_transfer_stop); + } else { + if (s->drive_kind == IDE_CD) { + ide_set_signature(s); } - ide_set_irq(s->bus); - break; - case WIN_VERIFY_EXT: - lba48 = 1; - case WIN_VERIFY: - case WIN_VERIFY_ONCE: - /* do sector number check ? */ - ide_cmd_lba48_transform(s, lba48); + ide_abort_command(s); + } + ide_set_irq(s->bus); + break; + case WIN_SPECIFY: + case WIN_RECAL: + s->error = 0; + s->status = READY_STAT | SEEK_STAT; + ide_set_irq(s->bus); + break; + case WIN_SETMULT: + if (s->drive_kind == IDE_CFATA && s->nsector == 0) { + /* Disable Read and Write Multiple */ + s->mult_sectors = 0; s->status = READY_STAT | SEEK_STAT; - ide_set_irq(s->bus); - break; + } else if ((s->nsector & 0xff) != 0 && + ((s->nsector & 0xff) > MAX_MULT_SECTORS || + (s->nsector & (s->nsector - 1)) != 0)) { + ide_abort_command(s); + } else { + s->mult_sectors = s->nsector & 0xff; + s->status = READY_STAT | SEEK_STAT; + } + ide_set_irq(s->bus); + break; + case WIN_VERIFY_EXT: + lba48 = 1; + case WIN_VERIFY: + case WIN_VERIFY_ONCE: + /* do sector number check ? */ + ide_cmd_lba48_transform(s, lba48); + s->status = READY_STAT | SEEK_STAT; + ide_set_irq(s->bus); + break; case WIN_READ_EXT: - lba48 = 1; - case WIN_READ: - case WIN_READ_ONCE: - if (!s->bs) - goto abort_cmd; - ide_cmd_lba48_transform(s, lba48); - s->req_nb_sectors = 1; - ide_sector_read(s); - break; + lba48 = 1; + case WIN_READ: + case WIN_READ_ONCE: + if (!s->bs) + goto abort_cmd; + ide_cmd_lba48_transform(s, lba48); + s->req_nb_sectors = 1; + ide_sector_read(s); + break; case WIN_WRITE_EXT: - lba48 = 1; - case WIN_WRITE: - case WIN_WRITE_ONCE: - case CFA_WRITE_SECT_WO_ERASE: - case WIN_WRITE_VERIFY: - ide_cmd_lba48_transform(s, lba48); - s->error = 0; - s->status = SEEK_STAT | READY_STAT; - s->req_nb_sectors = 1; - ide_transfer_start(s, s->io_buffer, 512, ide_sector_write); - s->media_changed = 1; - break; + lba48 = 1; + case WIN_WRITE: + case WIN_WRITE_ONCE: + case CFA_WRITE_SECT_WO_ERASE: + case WIN_WRITE_VERIFY: + ide_cmd_lba48_transform(s, lba48); + s->error = 0; + s->status = SEEK_STAT | READY_STAT; + s->req_nb_sectors = 1; + ide_transfer_start(s, s->io_buffer, 512, ide_sector_write); + s->media_changed = 1; + break; case WIN_MULTREAD_EXT: - lba48 = 1; - case WIN_MULTREAD: - if (!s->mult_sectors) - goto abort_cmd; - ide_cmd_lba48_transform(s, lba48); - s->req_nb_sectors = s->mult_sectors; - ide_sector_read(s); - break; - case WIN_MULTWRITE_EXT: - lba48 = 1; - case WIN_MULTWRITE: - case CFA_WRITE_MULTI_WO_ERASE: - if (!s->mult_sectors) - goto abort_cmd; - ide_cmd_lba48_transform(s, lba48); - s->error = 0; - s->status = SEEK_STAT | READY_STAT; - s->req_nb_sectors = s->mult_sectors; - n = s->nsector; - if (n > s->req_nb_sectors) - n = s->req_nb_sectors; - ide_transfer_start(s, s->io_buffer, 512 * n, ide_sector_write); - s->media_changed = 1; - break; + lba48 = 1; + case WIN_MULTREAD: + if (!s->mult_sectors) + goto abort_cmd; + ide_cmd_lba48_transform(s, lba48); + s->req_nb_sectors = s->mult_sectors; + ide_sector_read(s); + break; + case WIN_MULTWRITE_EXT: + lba48 = 1; + case WIN_MULTWRITE: + case CFA_WRITE_MULTI_WO_ERASE: + if (!s->mult_sectors) + goto abort_cmd; + ide_cmd_lba48_transform(s, lba48); + s->error = 0; + s->status = SEEK_STAT | READY_STAT; + s->req_nb_sectors = s->mult_sectors; + n = s->nsector; + if (n > s->req_nb_sectors) + n = s->req_nb_sectors; + ide_transfer_start(s, s->io_buffer, 512 * n, ide_sector_write); + s->media_changed = 1; + break; case WIN_READDMA_EXT: - lba48 = 1; - case WIN_READDMA: - case WIN_READDMA_ONCE: - if (!s->bs) - goto abort_cmd; - ide_cmd_lba48_transform(s, lba48); - ide_sector_read_dma(s); - break; + lba48 = 1; + case WIN_READDMA: + case WIN_READDMA_ONCE: + if (!s->bs) + goto abort_cmd; + ide_cmd_lba48_transform(s, lba48); + ide_sector_start_dma(s, 1); + break; case WIN_WRITEDMA_EXT: - lba48 = 1; - case WIN_WRITEDMA: - case WIN_WRITEDMA_ONCE: - if (!s->bs) - goto abort_cmd; - ide_cmd_lba48_transform(s, lba48); - ide_sector_write_dma(s); - s->media_changed = 1; - break; - case WIN_READ_NATIVE_MAX_EXT: - lba48 = 1; - case WIN_READ_NATIVE_MAX: - ide_cmd_lba48_transform(s, lba48); - ide_set_sector(s, s->nb_sectors - 1); - s->status = READY_STAT | SEEK_STAT; - ide_set_irq(s->bus); - break; - case WIN_CHECKPOWERMODE1: - case WIN_CHECKPOWERMODE2: - s->nsector = 0xff; /* device active or idle */ + lba48 = 1; + case WIN_WRITEDMA: + case WIN_WRITEDMA_ONCE: + if (!s->bs) + goto abort_cmd; + ide_cmd_lba48_transform(s, lba48); + ide_sector_start_dma(s, 0); + s->media_changed = 1; + break; + case WIN_READ_NATIVE_MAX_EXT: + lba48 = 1; + case WIN_READ_NATIVE_MAX: + ide_cmd_lba48_transform(s, lba48); + ide_set_sector(s, s->nb_sectors - 1); + s->status = READY_STAT | SEEK_STAT; + ide_set_irq(s->bus); + break; + case WIN_CHECKPOWERMODE1: + case WIN_CHECKPOWERMODE2: + s->nsector = 0xff; /* device active or idle */ + s->status = READY_STAT | SEEK_STAT; + ide_set_irq(s->bus); + break; + case WIN_SETFEATURES: + if (!s->bs) + goto abort_cmd; + /* XXX: valid for CDROM ? */ + switch(s->feature) { + case 0xcc: /* reverting to power-on defaults enable */ + case 0x66: /* reverting to power-on defaults disable */ + case 0x02: /* write cache enable */ + case 0x82: /* write cache disable */ + case 0xaa: /* read look-ahead enable */ + case 0x55: /* read look-ahead disable */ + case 0x05: /* set advanced power management mode */ + case 0x85: /* disable advanced power management mode */ + case 0x69: /* NOP */ + case 0x67: /* NOP */ + case 0x96: /* NOP */ + case 0x9a: /* NOP */ + case 0x42: /* enable Automatic Acoustic Mode */ + case 0xc2: /* disable Automatic Acoustic Mode */ s->status = READY_STAT | SEEK_STAT; ide_set_irq(s->bus); break; - case WIN_SETFEATURES: - if (!s->bs) - goto abort_cmd; - /* XXX: valid for CDROM ? */ - switch(s->feature) { - case 0xcc: /* reverting to power-on defaults enable */ - case 0x66: /* reverting to power-on defaults disable */ - case 0x02: /* write cache enable */ - case 0x82: /* write cache disable */ - case 0xaa: /* read look-ahead enable */ - case 0x55: /* read look-ahead disable */ - case 0x05: /* set advanced power management mode */ - case 0x85: /* disable advanced power management mode */ - case 0x69: /* NOP */ - case 0x67: /* NOP */ - case 0x96: /* NOP */ - case 0x9a: /* NOP */ - case 0x42: /* enable Automatic Acoustic Mode */ - case 0xc2: /* disable Automatic Acoustic Mode */ - s->status = READY_STAT | SEEK_STAT; - ide_set_irq(s->bus); - break; - case 0x03: { /* set transfer mode */ + case 0x03: { /* set transfer mode */ uint8_t val = s->nsector & 0x07; - uint16_t *identify_data = (uint16_t *)s->identify_data; + uint16_t *identify_data = (uint16_t *)s->identify_data; switch (s->nsector >> 3) { - case 0x00: /* pio default */ - case 0x01: /* pio mode */ + case 0x00: /* pio default */ + case 0x01: /* pio mode */ put_le16(identify_data + 62,0x07); put_le16(identify_data + 63,0x07); put_le16(identify_data + 88,0x3f); break; - case 0x02: /* sigle word dma mode*/ + case 0x02: /* sigle word dma mode*/ put_le16(identify_data + 62,0x07 | (1 << (val + 8))); put_le16(identify_data + 63,0x07); put_le16(identify_data + 88,0x3f); break; - case 0x04: /* mdma mode */ + case 0x04: /* mdma mode */ put_le16(identify_data + 62,0x07); put_le16(identify_data + 63,0x07 | (1 << (val + 8))); put_le16(identify_data + 88,0x3f); break; - case 0x08: /* udma mode */ + case 0x08: /* udma mode */ put_le16(identify_data + 62,0x07); put_le16(identify_data + 63,0x07); put_le16(identify_data + 88,0x3f | (1 << (val + 8))); break; - default: + default: goto abort_cmd; } - s->status = READY_STAT | SEEK_STAT; - ide_set_irq(s->bus); - break; - } - default: - goto abort_cmd; - } - break; - case WIN_FLUSH_CACHE: - case WIN_FLUSH_CACHE_EXT: - if (s->bs) - bdrv_aio_flush(s->bs, ide_flush_cb, s); - else - ide_flush_cb(s, 0); - break; - case WIN_STANDBY: - case WIN_STANDBY2: - case WIN_STANDBYNOW1: - case WIN_STANDBYNOW2: - case WIN_IDLEIMMEDIATE: - case CFA_IDLEIMMEDIATE: - case WIN_SETIDLE1: - case WIN_SETIDLE2: - case WIN_SLEEPNOW1: - case WIN_SLEEPNOW2: - s->status = READY_STAT; - ide_set_irq(s->bus); - break; - case WIN_SEEK: - if(s->is_cdrom) - goto abort_cmd; - /* XXX: Check that seek is within bounds */ s->status = READY_STAT | SEEK_STAT; ide_set_irq(s->bus); break; - /* ATAPI commands */ - case WIN_PIDENTIFY: - if (s->is_cdrom) { - ide_atapi_identify(s); - s->status = READY_STAT | SEEK_STAT; - ide_transfer_start(s, s->io_buffer, 512, ide_transfer_stop); - } else { - ide_abort_command(s); - } - ide_set_irq(s->bus); - break; - case WIN_DIAGNOSE: - ide_set_signature(s); - if (s->is_cdrom) - s->status = 0; /* ATAPI spec (v6) section 9.10 defines packet - * devices to return a clear status register - * with READY_STAT *not* set. */ - else - s->status = READY_STAT | SEEK_STAT; - s->error = 0x01; /* Device 0 passed, Device 1 passed or not - * present. - */ - ide_set_irq(s->bus); - break; - case WIN_SRST: - if (!s->is_cdrom) - goto abort_cmd; - ide_set_signature(s); - s->status = 0x00; /* NOTE: READY is _not_ set */ - s->error = 0x01; - break; - case WIN_PACKETCMD: - if (!s->is_cdrom) - goto abort_cmd; - /* overlapping commands not supported */ - if (s->feature & 0x02) - goto abort_cmd; - s->status = READY_STAT | SEEK_STAT; - s->atapi_dma = s->feature & 1; - s->nsector = 1; - ide_transfer_start(s, s->io_buffer, ATAPI_PACKET_SIZE, - ide_atapi_cmd); - break; - /* CF-ATA commands */ - case CFA_REQ_EXT_ERROR_CODE: - if (!s->is_cf) - goto abort_cmd; - s->error = 0x09; /* miscellaneous error */ + } + default: + goto abort_cmd; + } + break; + case WIN_FLUSH_CACHE: + case WIN_FLUSH_CACHE_EXT: + ide_flush_cache(s); + break; + case WIN_STANDBY: + case WIN_STANDBY2: + case WIN_STANDBYNOW1: + case WIN_STANDBYNOW2: + case WIN_IDLEIMMEDIATE: + case CFA_IDLEIMMEDIATE: + case WIN_SETIDLE1: + case WIN_SETIDLE2: + case WIN_SLEEPNOW1: + case WIN_SLEEPNOW2: + s->status = READY_STAT; + ide_set_irq(s->bus); + break; + case WIN_SEEK: + if(s->drive_kind == IDE_CD) + goto abort_cmd; + /* XXX: Check that seek is within bounds */ + s->status = READY_STAT | SEEK_STAT; + ide_set_irq(s->bus); + break; + /* ATAPI commands */ + case WIN_PIDENTIFY: + if (s->drive_kind == IDE_CD) { + ide_atapi_identify(s); s->status = READY_STAT | SEEK_STAT; - ide_set_irq(s->bus); - break; - case CFA_ERASE_SECTORS: - case CFA_WEAR_LEVEL: - if (!s->is_cf) - goto abort_cmd; - if (val == CFA_WEAR_LEVEL) - s->nsector = 0; - if (val == CFA_ERASE_SECTORS) - s->media_changed = 1; - s->error = 0x00; + ide_transfer_start(s, s->io_buffer, 512, ide_transfer_stop); + } else { + ide_abort_command(s); + } + ide_set_irq(s->bus); + break; + case WIN_DIAGNOSE: + ide_set_signature(s); + if (s->drive_kind == IDE_CD) + s->status = 0; /* ATAPI spec (v6) section 9.10 defines packet + * devices to return a clear status register + * with READY_STAT *not* set. */ + else s->status = READY_STAT | SEEK_STAT; - ide_set_irq(s->bus); + s->error = 0x01; /* Device 0 passed, Device 1 passed or not + * present. + */ + ide_set_irq(s->bus); + break; + case WIN_SRST: + if (s->drive_kind != IDE_CD) + goto abort_cmd; + ide_set_signature(s); + s->status = 0x00; /* NOTE: READY is _not_ set */ + s->error = 0x01; + break; + case WIN_PACKETCMD: + if (s->drive_kind != IDE_CD) + goto abort_cmd; + /* overlapping commands not supported */ + if (s->feature & 0x02) + goto abort_cmd; + s->status = READY_STAT | SEEK_STAT; + s->atapi_dma = s->feature & 1; + s->nsector = 1; + ide_transfer_start(s, s->io_buffer, ATAPI_PACKET_SIZE, + ide_atapi_cmd); + break; + /* CF-ATA commands */ + case CFA_REQ_EXT_ERROR_CODE: + if (s->drive_kind != IDE_CFATA) + goto abort_cmd; + s->error = 0x09; /* miscellaneous error */ + s->status = READY_STAT | SEEK_STAT; + ide_set_irq(s->bus); + break; + case CFA_ERASE_SECTORS: + case CFA_WEAR_LEVEL: + if (s->drive_kind != IDE_CFATA) + goto abort_cmd; + if (val == CFA_WEAR_LEVEL) + s->nsector = 0; + if (val == CFA_ERASE_SECTORS) + s->media_changed = 1; + s->error = 0x00; + s->status = READY_STAT | SEEK_STAT; + ide_set_irq(s->bus); + break; + case CFA_TRANSLATE_SECTOR: + if (s->drive_kind != IDE_CFATA) + goto abort_cmd; + s->error = 0x00; + s->status = READY_STAT | SEEK_STAT; + memset(s->io_buffer, 0, 0x200); + s->io_buffer[0x00] = s->hcyl; /* Cyl MSB */ + s->io_buffer[0x01] = s->lcyl; /* Cyl LSB */ + s->io_buffer[0x02] = s->select; /* Head */ + s->io_buffer[0x03] = s->sector; /* Sector */ + s->io_buffer[0x04] = ide_get_sector(s) >> 16; /* LBA MSB */ + s->io_buffer[0x05] = ide_get_sector(s) >> 8; /* LBA */ + s->io_buffer[0x06] = ide_get_sector(s) >> 0; /* LBA LSB */ + s->io_buffer[0x13] = 0x00; /* Erase flag */ + s->io_buffer[0x18] = 0x00; /* Hot count */ + s->io_buffer[0x19] = 0x00; /* Hot count */ + s->io_buffer[0x1a] = 0x01; /* Hot count */ + ide_transfer_start(s, s->io_buffer, 0x200, ide_transfer_stop); + ide_set_irq(s->bus); + break; + case CFA_ACCESS_METADATA_STORAGE: + if (s->drive_kind != IDE_CFATA) + goto abort_cmd; + switch (s->feature) { + case 0x02: /* Inquiry Metadata Storage */ + ide_cfata_metadata_inquiry(s); break; - case CFA_TRANSLATE_SECTOR: - if (!s->is_cf) - goto abort_cmd; - s->error = 0x00; - s->status = READY_STAT | SEEK_STAT; - memset(s->io_buffer, 0, 0x200); - s->io_buffer[0x00] = s->hcyl; /* Cyl MSB */ - s->io_buffer[0x01] = s->lcyl; /* Cyl LSB */ - s->io_buffer[0x02] = s->select; /* Head */ - s->io_buffer[0x03] = s->sector; /* Sector */ - s->io_buffer[0x04] = ide_get_sector(s) >> 16; /* LBA MSB */ - s->io_buffer[0x05] = ide_get_sector(s) >> 8; /* LBA */ - s->io_buffer[0x06] = ide_get_sector(s) >> 0; /* LBA LSB */ - s->io_buffer[0x13] = 0x00; /* Erase flag */ - s->io_buffer[0x18] = 0x00; /* Hot count */ - s->io_buffer[0x19] = 0x00; /* Hot count */ - s->io_buffer[0x1a] = 0x01; /* Hot count */ - ide_transfer_start(s, s->io_buffer, 0x200, ide_transfer_stop); - ide_set_irq(s->bus); + case 0x03: /* Read Metadata Storage */ + ide_cfata_metadata_read(s); break; - case CFA_ACCESS_METADATA_STORAGE: - if (!s->is_cf) - goto abort_cmd; - switch (s->feature) { - case 0x02: /* Inquiry Metadata Storage */ - ide_cfata_metadata_inquiry(s); - break; - case 0x03: /* Read Metadata Storage */ - ide_cfata_metadata_read(s); - break; - case 0x04: /* Write Metadata Storage */ - ide_cfata_metadata_write(s); - break; - default: - goto abort_cmd; - } - ide_transfer_start(s, s->io_buffer, 0x200, ide_transfer_stop); - s->status = 0x00; /* NOTE: READY is _not_ set */ - ide_set_irq(s->bus); + case 0x04: /* Write Metadata Storage */ + ide_cfata_metadata_write(s); break; - case IBM_SENSE_CONDITION: - if (!s->is_cf) - goto abort_cmd; - switch (s->feature) { - case 0x01: /* sense temperature in device */ - s->nsector = 0x50; /* +20 C */ - break; - default: - goto abort_cmd; - } - s->status = READY_STAT | SEEK_STAT; - ide_set_irq(s->bus); + default: + goto abort_cmd; + } + ide_transfer_start(s, s->io_buffer, 0x200, ide_transfer_stop); + s->status = 0x00; /* NOTE: READY is _not_ set */ + ide_set_irq(s->bus); + break; + case IBM_SENSE_CONDITION: + if (s->drive_kind != IDE_CFATA) + goto abort_cmd; + switch (s->feature) { + case 0x01: /* sense temperature in device */ + s->nsector = 0x50; /* +20 C */ break; + default: + goto abort_cmd; + } + s->status = READY_STAT | SEEK_STAT; + ide_set_irq(s->bus); + break; case WIN_SMART: - if (s->is_cdrom) + if (s->drive_kind == IDE_CD) goto abort_cmd; - if (s->hcyl != 0xc2 || s->lcyl != 0x4f) + if (s->hcyl != 0xc2 || s->lcyl != 0x4f) goto abort_cmd; - if (!s->smart_enabled && s->feature != SMART_ENABLE) + if (!s->smart_enabled && s->feature != SMART_ENABLE) goto abort_cmd; - switch (s->feature) { - case SMART_DISABLE: + switch (s->feature) { + case SMART_DISABLE: s->smart_enabled = 0; s->status = READY_STAT | SEEK_STAT; ide_set_irq(s->bus); break; - case SMART_ENABLE: + case SMART_ENABLE: s->smart_enabled = 1; s->status = READY_STAT | SEEK_STAT; ide_set_irq(s->bus); break; - case SMART_ATTR_AUTOSAVE: + case SMART_ATTR_AUTOSAVE: switch (s->sector) { case 0x00: - s->smart_autosave = 0; - break; + s->smart_autosave = 0; + break; case 0xf1: - s->smart_autosave = 1; - break; + s->smart_autosave = 1; + break; default: - goto abort_cmd; + goto abort_cmd; } s->status = READY_STAT | SEEK_STAT; ide_set_irq(s->bus); break; - case SMART_STATUS: + case SMART_STATUS: if (!s->smart_errors) { - s->hcyl = 0xc2; - s->lcyl = 0x4f; + s->hcyl = 0xc2; + s->lcyl = 0x4f; } else { - s->hcyl = 0x2c; - s->lcyl = 0xf4; + s->hcyl = 0x2c; + s->lcyl = 0xf4; } s->status = READY_STAT | SEEK_STAT; ide_set_irq(s->bus); break; - case SMART_READ_THRESH: + case SMART_READ_THRESH: memset(s->io_buffer, 0, 0x200); s->io_buffer[0] = 0x01; /* smart struct version */ for (n=0; n<30; n++) { - if (smart_attributes[n][0] == 0) + if (smart_attributes[n][0] == 0) break; - s->io_buffer[2+0+(n*12)] = smart_attributes[n][0]; - s->io_buffer[2+1+(n*12)] = smart_attributes[n][4]; + s->io_buffer[2+0+(n*12)] = smart_attributes[n][0]; + s->io_buffer[2+1+(n*12)] = smart_attributes[n][4]; } for (n=0; n<511; n++) /* checksum */ - s->io_buffer[511] += s->io_buffer[n]; + s->io_buffer[511] += s->io_buffer[n]; s->io_buffer[511] = 0x100 - s->io_buffer[511]; s->status = READY_STAT | SEEK_STAT; ide_transfer_start(s, s->io_buffer, 0x200, ide_transfer_stop); ide_set_irq(s->bus); break; - case SMART_READ_DATA: + case SMART_READ_DATA: memset(s->io_buffer, 0, 0x200); s->io_buffer[0] = 0x01; /* smart struct version */ for (n=0; n<30; n++) { - if (smart_attributes[n][0] == 0) + if (smart_attributes[n][0] == 0) break; - s->io_buffer[2+0+(n*12)] = smart_attributes[n][0]; - s->io_buffer[2+1+(n*12)] = smart_attributes[n][1]; - s->io_buffer[2+3+(n*12)] = smart_attributes[n][2]; - s->io_buffer[2+4+(n*12)] = smart_attributes[n][3]; + s->io_buffer[2+0+(n*12)] = smart_attributes[n][0]; + s->io_buffer[2+1+(n*12)] = smart_attributes[n][1]; + s->io_buffer[2+3+(n*12)] = smart_attributes[n][2]; + s->io_buffer[2+4+(n*12)] = smart_attributes[n][3]; } s->io_buffer[362] = 0x02 | (s->smart_autosave?0x80:0x00); if (s->smart_selftest_count == 0) { - s->io_buffer[363] = 0; + s->io_buffer[363] = 0; } else { - s->io_buffer[363] = + s->io_buffer[363] = s->smart_selftest_data[3 + - (s->smart_selftest_count - 1) * - 24]; + (s->smart_selftest_count - 1) * + 24]; } s->io_buffer[364] = 0x20; s->io_buffer[365] = 0x01; @@ -2243,76 +2138,75 @@ s->io_buffer[374] = 0x01; /* minutes for poll conveyance */ for (n=0; n<511; n++) - s->io_buffer[511] += s->io_buffer[n]; + s->io_buffer[511] += s->io_buffer[n]; s->io_buffer[511] = 0x100 - s->io_buffer[511]; s->status = READY_STAT | SEEK_STAT; ide_transfer_start(s, s->io_buffer, 0x200, ide_transfer_stop); ide_set_irq(s->bus); break; - case SMART_READ_LOG: + case SMART_READ_LOG: switch (s->sector) { case 0x01: /* summary smart error log */ - memset(s->io_buffer, 0, 0x200); - s->io_buffer[0] = 0x01; - s->io_buffer[1] = 0x00; /* no error entries */ - s->io_buffer[452] = s->smart_errors & 0xff; - s->io_buffer[453] = (s->smart_errors & 0xff00) >> 8; + memset(s->io_buffer, 0, 0x200); + s->io_buffer[0] = 0x01; + s->io_buffer[1] = 0x00; /* no error entries */ + s->io_buffer[452] = s->smart_errors & 0xff; + s->io_buffer[453] = (s->smart_errors & 0xff00) >> 8; - for (n=0; n<511; n++) + for (n=0; n<511; n++) s->io_buffer[511] += s->io_buffer[n]; - s->io_buffer[511] = 0x100 - s->io_buffer[511]; - break; + s->io_buffer[511] = 0x100 - s->io_buffer[511]; + break; case 0x06: /* smart self test log */ - memset(s->io_buffer, 0, 0x200); - s->io_buffer[0] = 0x01; - if (s->smart_selftest_count == 0) { + memset(s->io_buffer, 0, 0x200); + s->io_buffer[0] = 0x01; + if (s->smart_selftest_count == 0) { s->io_buffer[508] = 0; - } else { + } else { s->io_buffer[508] = s->smart_selftest_count; for (n=2; n<506; n++) - s->io_buffer[n] = s->smart_selftest_data[n]; - } - for (n=0; n<511; n++) + s->io_buffer[n] = s->smart_selftest_data[n]; + } + for (n=0; n<511; n++) s->io_buffer[511] += s->io_buffer[n]; - s->io_buffer[511] = 0x100 - s->io_buffer[511]; - break; + s->io_buffer[511] = 0x100 - s->io_buffer[511]; + break; default: - goto abort_cmd; + goto abort_cmd; } s->status = READY_STAT | SEEK_STAT; ide_transfer_start(s, s->io_buffer, 0x200, ide_transfer_stop); ide_set_irq(s->bus); break; - case SMART_EXECUTE_OFFLINE: + case SMART_EXECUTE_OFFLINE: switch (s->sector) { case 0: /* off-line routine */ case 1: /* short self test */ case 2: /* extended self test */ - s->smart_selftest_count++; - if(s->smart_selftest_count > 21) + s->smart_selftest_count++; + if(s->smart_selftest_count > 21) s->smart_selftest_count = 0; - n = 2 + (s->smart_selftest_count - 1) * 24; - s->smart_selftest_data[n] = s->sector; - s->smart_selftest_data[n+1] = 0x00; /* OK and finished */ - s->smart_selftest_data[n+2] = 0x34; /* hour count lsb */ - s->smart_selftest_data[n+3] = 0x12; /* hour count msb */ - s->status = READY_STAT | SEEK_STAT; - ide_set_irq(s->bus); - break; + n = 2 + (s->smart_selftest_count - 1) * 24; + s->smart_selftest_data[n] = s->sector; + s->smart_selftest_data[n+1] = 0x00; /* OK and finished */ + s->smart_selftest_data[n+2] = 0x34; /* hour count lsb */ + s->smart_selftest_data[n+3] = 0x12; /* hour count msb */ + s->status = READY_STAT | SEEK_STAT; + ide_set_irq(s->bus); + break; default: - goto abort_cmd; + goto abort_cmd; } break; - default: + default: goto abort_cmd; - } - break; - default: - abort_cmd: - ide_abort_command(s); - ide_set_irq(s->bus); - break; - } + } + break; + default: + abort_cmd: + ide_abort_command(s); + ide_set_irq(s->bus); + break; } } @@ -2434,7 +2328,7 @@ /* high to low */ for(i = 0;i < 2; i++) { s = &bus->ifs[i]; - if (s->is_cdrom) + if (s->drive_kind == IDE_CD) s->status = 0x00; /* NOTE: READY is _not_ set */ else s->status = READY_STAT | SEEK_STAT; @@ -2536,7 +2430,7 @@ #ifdef DEBUG_IDE printf("ide: reset\n"); #endif - if (s->is_cf) + if (s->drive_kind == IDE_CFATA) s->mult_sectors = 0; else s->mult_sectors = MAX_MULT_SECTORS; @@ -2588,68 +2482,168 @@ ide_reset(&bus->ifs[0]); ide_reset(&bus->ifs[1]); ide_clear_hob(bus); + + /* pending async DMA */ + if (bus->dma->aiocb) { +#ifdef DEBUG_AIO + printf("aio_cancel\n"); +#endif + bdrv_aio_cancel(bus->dma->aiocb); + bus->dma->aiocb = NULL; + } + + /* reset dma provider too */ + bus->dma->ops->reset(bus->dma); } -void ide_init_drive(IDEState *s, DriveInfo *dinfo, const char *version) +int ide_init_drive(IDEState *s, BlockDriverState *bs, + const char *version, const char *serial) { int cylinders, heads, secs; uint64_t nb_sectors; - if (dinfo && dinfo->bdrv) { - s->bs = dinfo->bdrv; - bdrv_get_geometry(s->bs, &nb_sectors); - bdrv_guess_geometry(s->bs, &cylinders, &heads, &secs); - s->cylinders = cylinders; - s->heads = heads; - s->sectors = secs; - s->nb_sectors = nb_sectors; - /* The SMART values should be preserved across power cycles - but they aren't. */ - s->smart_enabled = 1; - s->smart_autosave = 1; - s->smart_errors = 0; - s->smart_selftest_count = 0; - if (bdrv_get_type_hint(s->bs) == BDRV_TYPE_CDROM) { - s->is_cdrom = 1; - bdrv_set_change_cb(s->bs, cdrom_change_cb, s); + s->bs = bs; + bdrv_get_geometry(bs, &nb_sectors); + bdrv_guess_geometry(bs, &cylinders, &heads, &secs); + if (cylinders < 1 || cylinders > 16383) { + error_report("cyls must be between 1 and 16383"); + return -1; + } + if (heads < 1 || heads > 16) { + error_report("heads must be between 1 and 16"); + return -1; + } + if (secs < 1 || secs > 63) { + error_report("secs must be between 1 and 63"); + return -1; + } + s->cylinders = cylinders; + s->heads = heads; + s->sectors = secs; + s->nb_sectors = nb_sectors; + /* The SMART values should be preserved across power cycles + but they aren't. */ + s->smart_enabled = 1; + s->smart_autosave = 1; + s->smart_errors = 0; + s->smart_selftest_count = 0; + if (bdrv_get_type_hint(bs) == BDRV_TYPE_CDROM) { + s->drive_kind = IDE_CD; + bdrv_set_change_cb(bs, cdrom_change_cb, s); + bs->buffer_alignment = 2048; + } else { + if (!bdrv_is_inserted(s->bs)) { + error_report("Device needs media, but drive is empty"); + return -1; + } + if (bdrv_is_read_only(bs)) { + error_report("Can't use a read-only drive"); + return -1; } - strncpy(s->drive_serial_str, drive_get_serial(s->bs), - sizeof(s->drive_serial_str)); } - if (strlen(s->drive_serial_str) == 0) + if (serial) { + strncpy(s->drive_serial_str, serial, sizeof(s->drive_serial_str)); + } else { snprintf(s->drive_serial_str, sizeof(s->drive_serial_str), "QM%05d", s->drive_serial); + } if (version) { pstrcpy(s->version, sizeof(s->version), version); } else { pstrcpy(s->version, sizeof(s->version), QEMU_VERSION); } + ide_reset(s); + bdrv_set_removable(bs, s->drive_kind == IDE_CD); + return 0; } -void ide_init2(IDEBus *bus, DriveInfo *hd0, DriveInfo *hd1, - qemu_irq irq) +static void ide_init1(IDEBus *bus, int unit) { - IDEState *s; static int drive_serial = 1; + IDEState *s = &bus->ifs[unit]; + + s->bus = bus; + s->unit = unit; + s->drive_serial = drive_serial++; + /* we need at least 2k alignment for accessing CDROMs using O_DIRECT */ + s->io_buffer = qemu_memalign(2048, IDE_DMA_BUF_SECTORS*512 + 4); + s->io_buffer_total_len = IDE_DMA_BUF_SECTORS*512 + 4; + s->smart_selftest_data = qemu_blockalign(s->bs, 512); + s->sector_write_timer = qemu_new_timer(vm_clock, + ide_sector_write_timer_cb, s); +} + +static void ide_nop_start(IDEDMA *dma, IDEState *s, + BlockDriverCompletionFunc *cb) +{ +} + +static int ide_nop(IDEDMA *dma) +{ + return 0; +} + +static int ide_nop_int(IDEDMA *dma, int x) +{ + return 0; +} + +static void ide_nop_restart(void *opaque, int x, int y) +{ +} + +static const IDEDMAOps ide_dma_nop_ops = { + .start_dma = ide_nop_start, + .start_transfer = ide_nop, + .prepare_buf = ide_nop_int, + .rw_buf = ide_nop_int, + .set_unit = ide_nop_int, + .add_status = ide_nop_int, + .set_inactive = ide_nop, + .restart_cb = ide_nop_restart, + .reset = ide_nop, +}; + +static IDEDMA ide_dma_nop = { + .ops = &ide_dma_nop_ops, + .aiocb = NULL, +}; + +void ide_init2(IDEBus *bus, qemu_irq irq) +{ int i; for(i = 0; i < 2; i++) { - s = bus->ifs + i; - s->bus = bus; - s->unit = i; - s->drive_serial = drive_serial++; - s->io_buffer = qemu_blockalign(s->bs, IDE_DMA_BUF_SECTORS*512 + 4); - s->io_buffer_total_len = IDE_DMA_BUF_SECTORS*512 + 4; - s->smart_selftest_data = qemu_blockalign(s->bs, 512); - s->sector_write_timer = qemu_new_timer(vm_clock, - ide_sector_write_timer_cb, s); - if (i == 0) - ide_init_drive(s, hd0, NULL); - if (i == 1) - ide_init_drive(s, hd1, NULL); + ide_init1(bus, i); + ide_reset(&bus->ifs[i]); } bus->irq = irq; + bus->dma = &ide_dma_nop; +} + +/* TODO convert users to qdev and remove */ +void ide_init2_with_non_qdev_drives(IDEBus *bus, DriveInfo *hd0, + DriveInfo *hd1, qemu_irq irq) +{ + int i; + DriveInfo *dinfo; + + for(i = 0; i < 2; i++) { + dinfo = i == 0 ? hd0 : hd1; + ide_init1(bus, i); + if (dinfo) { + if (ide_init_drive(&bus->ifs[i], dinfo->bdrv, NULL, + *dinfo->serial ? dinfo->serial : NULL) < 0) { + error_report("Can't set up IDE drive %s", dinfo->id); + exit(1); + } + } else { + ide_reset(&bus->ifs[i]); + } + } + bus->irq = irq; + bus->dma = &ide_dma_nop; } void ide_init_ioport(IDEBus *bus, int iobase, int iobase2) @@ -2681,6 +2675,7 @@ ide_transfer_stop, ide_atapi_cmd_reply_end, ide_atapi_cmd, + ide_dummy_transfer_stop, }; static int transfer_end_table_idx(EndTransferFunc *fn) @@ -2704,42 +2699,73 @@ s->cdrom_changed = 1; } } - - if (s->cur_io_buffer_len) { - s->end_transfer_func = transfer_end_table[s->end_transfer_fn_idx]; - s->data_ptr = s->io_buffer + s->cur_io_buffer_offset; - s->data_end = s->data_ptr + s->cur_io_buffer_len; - } - return 0; } -static void ide_drive_pre_save(void *opaque) +static int ide_drive_pio_post_load(void *opaque, int version_id) { IDEState *s = opaque; - s->cur_io_buffer_len = 0; + if (s->end_transfer_fn_idx > ARRAY_SIZE(transfer_end_table)) { + return -EINVAL; + } + s->end_transfer_func = transfer_end_table[s->end_transfer_fn_idx]; + s->data_ptr = s->io_buffer + s->cur_io_buffer_offset; + s->data_end = s->data_ptr + s->cur_io_buffer_len; - if (!(s->status & DRQ_STAT)) - return; + return 0; +} + +static void ide_drive_pio_pre_save(void *opaque) +{ + IDEState *s = opaque; + int idx; s->cur_io_buffer_offset = s->data_ptr - s->io_buffer; s->cur_io_buffer_len = s->data_end - s->data_ptr; - s->end_transfer_fn_idx = transfer_end_table_idx(s->end_transfer_func); - if (s->end_transfer_fn_idx == -1) { + idx = transfer_end_table_idx(s->end_transfer_func); + if (idx == -1) { fprintf(stderr, "%s: invalid end_transfer_func for DRQ_STAT\n", __func__); s->end_transfer_fn_idx = 2; + } else { + s->end_transfer_fn_idx = idx; } } +static bool ide_drive_pio_state_needed(void *opaque) +{ + IDEState *s = opaque; + + return (s->status & DRQ_STAT) != 0; +} + +const VMStateDescription vmstate_ide_drive_pio_state = { + .name = "ide_drive/pio_state", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .pre_save = ide_drive_pio_pre_save, + .post_load = ide_drive_pio_post_load, + .fields = (VMStateField []) { + VMSTATE_INT32(req_nb_sectors, IDEState), + VMSTATE_VARRAY_INT32(io_buffer, IDEState, io_buffer_total_len, 1, + vmstate_info_uint8, uint8_t), + VMSTATE_INT32(cur_io_buffer_offset, IDEState), + VMSTATE_INT32(cur_io_buffer_len, IDEState), + VMSTATE_UINT8(end_transfer_fn_idx, IDEState), + VMSTATE_INT32(elementary_transfer_size, IDEState), + VMSTATE_INT32(packet_transfer_size, IDEState), + VMSTATE_END_OF_LIST() + } +}; + const VMStateDescription vmstate_ide_drive = { .name = "ide_drive", - .version_id = 4, + .version_id = 3, .minimum_version_id = 0, .minimum_version_id_old = 0, - .pre_save = ide_drive_pre_save, .post_load = ide_drive_post_load, .fields = (VMStateField []) { VMSTATE_INT32(mult_sectors, IDEState), @@ -2762,15 +2788,15 @@ VMSTATE_UINT8(sense_key, IDEState), VMSTATE_UINT8(asc, IDEState), VMSTATE_UINT8_V(cdrom_changed, IDEState, 3), - VMSTATE_INT32_V(req_nb_sectors, IDEState, 4), - VMSTATE_VARRAY_INT32(io_buffer, IDEState, io_buffer_total_len, 4, - vmstate_info_uint8, uint8_t), - VMSTATE_INT32_V(cur_io_buffer_offset, IDEState, 4), - VMSTATE_INT32_V(cur_io_buffer_len, IDEState, 4), - VMSTATE_UINT8_V(end_transfer_fn_idx, IDEState, 4), - VMSTATE_INT32_V(elementary_transfer_size, IDEState, 4), - VMSTATE_INT32_V(packet_transfer_size, IDEState, 4), VMSTATE_END_OF_LIST() + }, + .subsections = (VMStateSubsection []) { + { + .vmsd = &vmstate_ide_drive_pio_state, + .needed = ide_drive_pio_state_needed, + }, { + /* empty */ + } } }; @@ -2785,75 +2811,3 @@ VMSTATE_END_OF_LIST() } }; - -/***********************************************************/ -/* PCI IDE definitions */ - -static void ide_dma_start(IDEState *s, BlockDriverCompletionFunc *dma_cb) -{ - BMDMAState *bm = s->bus->bmdma; - if(!bm) - return; - bm->unit = s->unit; - bm->dma_cb = dma_cb; - bm->cur_prd_last = 0; - bm->cur_prd_addr = 0; - bm->cur_prd_len = 0; - bm->sector_num = ide_get_sector(s); - bm->nsector = s->nsector; - if (bm->status & BM_STATUS_DMAING) { - bm->dma_cb(bm, 0); - } -} - -static void ide_dma_restart(IDEState *s, int is_read) -{ - BMDMAState *bm = s->bus->bmdma; - ide_set_sector(s, bm->sector_num); - s->io_buffer_index = 0; - s->io_buffer_size = 0; - s->nsector = bm->nsector; - bm->cur_addr = bm->addr; - - if (is_read) { - bm->dma_cb = ide_read_dma_cb; - } else { - bm->dma_cb = ide_write_dma_cb; - } - - ide_dma_start(s, bm->dma_cb); -} - -void ide_dma_cancel(BMDMAState *bm) -{ - if (bm->status & BM_STATUS_DMAING) { - if (bm->aiocb) { -#ifdef DEBUG_AIO - printf("aio_cancel\n"); -#endif - bdrv_aio_cancel(bm->aiocb); - bm->aiocb = NULL; - } - bm->status &= ~BM_STATUS_DMAING; - /* cancel DMA request */ - bm->unit = -1; - bm->dma_cb = NULL; - } -} - -void ide_dma_reset(BMDMAState *bm) -{ -#ifdef DEBUG_IDE - printf("ide: dma_reset\n"); -#endif - ide_dma_cancel(bm); - bm->cmd = 0; - bm->status = 0; - bm->addr = 0; - bm->cur_addr = 0; - bm->cur_prd_last = 0; - bm->cur_prd_addr = 0; - bm->cur_prd_len = 0; - bm->sector_num = 0; - bm->nsector = 0; -} diff -Nru qemu-kvm-0.12.5+noroms/hw/ide/ich.c qemu-kvm-0.14.1/hw/ide/ich.c --- qemu-kvm-0.12.5+noroms/hw/ide/ich.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/ide/ich.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,148 @@ +/* + * QEMU ICH Emulation + * + * Copyright (c) 2010 Sebastian Herbszt + * Copyright (c) 2010 Alexander Graf + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + * + * + * lspci dump of a ICH-9 real device + * + * 00:1f.2 SATA controller [0106]: Intel Corporation 82801IR/IO/IH (ICH9R/DO/DH) 6 port SATA AHCI Controller [8086:2922] (rev 02) (prog-if 01 [AHCI 1.0]) + * Subsystem: Intel Corporation 82801IR/IO/IH (ICH9R/DO/DH) 6 port SATA AHCI Controller [8086:2922] + * Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B- DisINTx+ + * Status: Cap+ 66MHz+ UDF- FastB2B+ ParErr- DEVSEL=medium >TAbort- SERR- + * Capabilities: [b0] Vendor Specific Information + * Kernel driver in use: ahci + * Kernel modules: ahci + * 00: 86 80 22 29 07 04 b0 02 02 01 06 01 00 00 00 00 + * 10: 01 d0 00 00 01 cc 00 00 81 c8 00 00 01 c8 00 00 + * 20: 81 c4 00 00 00 90 bf fe 00 00 00 00 86 80 22 29 + * 30: 00 00 00 00 80 00 00 00 00 00 00 00 0f 02 00 00 + * 40: 00 80 00 80 00 00 00 00 00 00 00 00 00 00 00 00 + * 50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 70: 01 a8 03 40 08 00 00 00 00 00 00 00 00 00 00 00 + * 80: 05 70 09 00 0c f0 e0 fe d9 41 00 00 00 00 00 00 + * 90: 40 00 0f 82 93 01 00 00 00 00 00 00 00 00 00 00 + * a0: ac 00 00 00 0a 00 12 00 12 b0 10 00 48 00 00 00 + * b0: 09 00 06 20 00 00 00 00 00 00 00 00 00 00 00 00 + * c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * f0: 00 00 00 00 00 00 00 00 86 0f 02 00 00 00 00 00 + * + */ + +#include +#include +#include +#include +#include +#include "block.h" +#include "block_int.h" +#include "sysemu.h" +#include "dma.h" + +#include +#include + +static int pci_ich9_ahci_init(PCIDevice *dev) +{ + struct AHCIPCIState *d; + d = DO_UPCAST(struct AHCIPCIState, card, dev); + + pci_config_set_vendor_id(d->card.config, PCI_VENDOR_ID_INTEL); + pci_config_set_device_id(d->card.config, PCI_DEVICE_ID_INTEL_82801IR); + + pci_config_set_class(d->card.config, PCI_CLASS_STORAGE_SATA); + pci_config_set_revision(d->card.config, 0x02); + pci_config_set_prog_interface(d->card.config, AHCI_PROGMODE_MAJOR_REV_1); + + d->card.config[PCI_CACHE_LINE_SIZE] = 0x08; /* Cache line size */ + d->card.config[PCI_LATENCY_TIMER] = 0x00; /* Latency timer */ + pci_config_set_interrupt_pin(d->card.config, 1); + + /* XXX Software should program this register */ + d->card.config[0x90] = 1 << 6; /* Address Map Register - AHCI mode */ + + qemu_register_reset(ahci_reset, d); + + /* XXX BAR size should be 1k, but that breaks, so bump it to 4k for now */ + pci_register_bar(&d->card, 5, 0x1000, PCI_BASE_ADDRESS_SPACE_MEMORY, + ahci_pci_map); + + msi_init(dev, 0x50, 1, true, false); + + ahci_init(&d->ahci, &dev->qdev, 6); + d->ahci.irq = d->card.irq[0]; + + return 0; +} + +static int pci_ich9_uninit(PCIDevice *dev) +{ + struct AHCIPCIState *d; + d = DO_UPCAST(struct AHCIPCIState, card, dev); + + if (msi_enabled(dev)) { + msi_uninit(dev); + } + + qemu_unregister_reset(ahci_reset, d); + ahci_uninit(&d->ahci); + + return 0; +} + +static void pci_ich9_write_config(PCIDevice *pci, uint32_t addr, + uint32_t val, int len) +{ + pci_default_write_config(pci, addr, val, len); + msi_write_config(pci, addr, val, len); +} + +static PCIDeviceInfo ich_ahci_info[] = { + { + .qdev.name = "ich9-ahci", + .qdev.alias = "ahci", + .qdev.size = sizeof(AHCIPCIState), + .init = pci_ich9_ahci_init, + .exit = pci_ich9_uninit, + .config_write = pci_ich9_write_config, + },{ + /* end of list */ + } +}; + +static void ich_ahci_register(void) +{ + pci_qdev_register_many(ich_ahci_info); +} +device_init(ich_ahci_register); diff -Nru qemu-kvm-0.12.5+noroms/hw/ide/internal.h qemu-kvm-0.14.1/hw/ide/internal.h --- qemu-kvm-0.12.5+noroms/hw/ide/internal.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/ide/internal.h 2011-05-11 13:29:46.000000000 +0000 @@ -7,6 +7,8 @@ * non-internal declarations are in hw/ide.h */ #include +#include "block_int.h" +#include "iorange.h" /* debug IDE devices */ //#define DEBUG_IDE @@ -18,7 +20,8 @@ typedef struct IDEDevice IDEDevice; typedef struct IDEDeviceInfo IDEDeviceInfo; typedef struct IDEState IDEState; -typedef struct BMDMAState BMDMAState; +typedef struct IDEDMA IDEDMA; +typedef struct IDEDMAOps IDEDMAOps; /* Bits of HD_STATUS */ #define ERR_STAT 0x01 @@ -361,15 +364,21 @@ #define SMART_DISABLE 0xd9 #define SMART_STATUS 0xda +typedef enum { IDE_HD, IDE_CD, IDE_CFATA } IDEDriveKind; + typedef void EndTransferFunc(IDEState *); +typedef void DMAStartFunc(IDEDMA *, IDEState *, BlockDriverCompletionFunc *); +typedef int DMAFunc(IDEDMA *); +typedef int DMAIntFunc(IDEDMA *, int); +typedef void DMARestartFunc(void *, int, int); + /* NOTE: IDEState represents in fact one drive */ struct IDEState { IDEBus *bus; uint8_t unit; /* ide config */ - int is_cdrom; - int is_cf; + IDEDriveKind drive_kind; int cylinders, heads, sectors; int64_t nb_sectors; int mult_sectors; @@ -430,7 +439,6 @@ uint32_t mdata_size; uint8_t *mdata_storage; int media_changed; - /* for pmac */ int is_read; /* SMART */ uint8_t smart_enabled; @@ -438,14 +446,36 @@ int smart_errors; uint8_t smart_selftest_count; uint8_t *smart_selftest_data; + /* AHCI */ + int ncq_queues; +}; + +struct IDEDMAOps { + DMAStartFunc *start_dma; + DMAFunc *start_transfer; + DMAIntFunc *prepare_buf; + DMAIntFunc *rw_buf; + DMAIntFunc *set_unit; + DMAIntFunc *add_status; + DMAFunc *set_inactive; + DMARestartFunc *restart_cb; + DMAFunc *reset; +}; + +struct IDEDMA { + const struct IDEDMAOps *ops; + struct iovec iov; + QEMUIOVector qiov; + BlockDriverAIOCB *aiocb; }; struct IDEBus { BusState qbus; IDEDevice *master; IDEDevice *slave; - BMDMAState *bmdma; IDEState ifs[2]; + int bus_id; + IDEDMA *dma; uint8_t unit; uint8_t cmd; qemu_irq irq; @@ -454,16 +484,15 @@ struct IDEDevice { DeviceState qdev; uint32_t unit; - DriveInfo *dinfo; + BlockConf conf; char *version; + char *serial; }; typedef int (*ide_qdev_initfn)(IDEDevice *dev); struct IDEDeviceInfo { DeviceInfo qdev; ide_qdev_initfn init; - uint32_t unit; - DriveInfo *drive; }; #define BM_STATUS_DMAING 0x01 @@ -471,51 +500,20 @@ #define BM_STATUS_INT 0x04 #define BM_STATUS_DMA_RETRY 0x08 #define BM_STATUS_PIO_RETRY 0x10 -#define BM_STATUS_RETRY_READ 0x20 +#define BM_STATUS_RETRY_READ 0x20 +#define BM_STATUS_RETRY_FLUSH 0x40 #define BM_CMD_START 0x01 #define BM_CMD_READ 0x08 -struct BMDMAState { - uint8_t cmd; - uint8_t status; - uint32_t addr; - - struct PCIIDEState *pci_dev; - IDEBus *bus; - /* current transfer state */ - uint32_t cur_addr; - uint32_t cur_prd_last; - uint32_t cur_prd_addr; - uint32_t cur_prd_len; - uint8_t unit; - BlockDriverCompletionFunc *dma_cb; - BlockDriverAIOCB *aiocb; - struct iovec iov; - QEMUIOVector qiov; - int64_t sector_num; - uint32_t nsector; - QEMUBH *bh; -}; - static inline IDEState *idebus_active_if(IDEBus *bus) { return bus->ifs + bus->unit; } -static inline IDEState *bmdma_active_if(BMDMAState *bmdma) -{ - assert(bmdma->unit != (uint8_t)-1); - return bmdma->bus->ifs + bmdma->unit; -} - static inline void ide_set_irq(IDEBus *bus) { - BMDMAState *bm = bus->bmdma; if (!(bus->cmd & IDE_CMD_DISABLE_IRQ)) { - if (bm) { - bm->status |= BM_STATUS_INT; - } qemu_irq_raise(bus->irq); } } @@ -538,10 +536,7 @@ int64_t ide_get_sector(IDEState *s); void ide_set_sector(IDEState *s, int64_t sector_num); -void ide_dma_cancel(BMDMAState *bm); -void ide_dma_restart_cb(void *opaque, int running, int reason); void ide_dma_error(IDEState *s); -void ide_dma_reset(BMDMAState *bm); void ide_atapi_cmd_ok(IDEState *s); void ide_atapi_cmd_error(IDEState *s, int sense_key, int asc); @@ -556,13 +551,21 @@ void ide_data_writel(void *opaque, uint32_t addr, uint32_t val); uint32_t ide_data_readl(void *opaque, uint32_t addr); -void ide_init_drive(IDEState *s, DriveInfo *dinfo, const char *version); -void ide_init2(IDEBus *bus, DriveInfo *hd0, DriveInfo *hd1, - qemu_irq irq); +int ide_init_drive(IDEState *s, BlockDriverState *bs, + const char *version, const char *serial); +void ide_init2(IDEBus *bus, qemu_irq irq); +void ide_init2_with_non_qdev_drives(IDEBus *bus, DriveInfo *hd0, + DriveInfo *hd1, qemu_irq irq); void ide_init_ioport(IDEBus *bus, int iobase, int iobase2); +void ide_exec_cmd(IDEBus *bus, uint32_t val); +void ide_dma_cb(void *opaque, int ret); +void ide_sector_write(IDEState *s); +void ide_sector_read(IDEState *s); +void ide_flush_cache(IDEState *s); + /* hw/ide/qdev.c */ -void ide_bus_new(IDEBus *idebus, DeviceState *dev); +void ide_bus_new(IDEBus *idebus, DeviceState *dev, int bus_id); IDEDevice *ide_create_drive(IDEBus *bus, int unit, DriveInfo *drive); #endif /* HW_IDE_INTERNAL_H */ diff -Nru qemu-kvm-0.12.5+noroms/hw/ide/isa.c qemu-kvm-0.14.1/hw/ide/isa.c --- qemu-kvm-0.12.5+noroms/hw/ide/isa.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/ide/isa.c 2011-05-11 13:29:46.000000000 +0000 @@ -67,16 +67,18 @@ { ISAIDEState *s = DO_UPCAST(ISAIDEState, dev, dev); - ide_bus_new(&s->bus, &s->dev.qdev); + ide_bus_new(&s->bus, &s->dev.qdev, 0); ide_init_ioport(&s->bus, s->iobase, s->iobase2); isa_init_irq(dev, &s->irq, s->isairq); - ide_init2(&s->bus, NULL, NULL, s->irq); - vmstate_register(0, &vmstate_ide_isa, s); + isa_init_ioport_range(dev, s->iobase, 8); + isa_init_ioport(dev, s->iobase2); + ide_init2(&s->bus, s->irq); + vmstate_register(&dev->qdev, 0, &vmstate_ide_isa, s); return 0; }; -int isa_ide_init(int iobase, int iobase2, int isairq, - DriveInfo *hd0, DriveInfo *hd1) +ISADevice *isa_ide_init(int iobase, int iobase2, int isairq, + DriveInfo *hd0, DriveInfo *hd1) { ISADevice *dev; ISAIDEState *s; @@ -86,18 +88,19 @@ qdev_prop_set_uint32(&dev->qdev, "iobase2", iobase2); qdev_prop_set_uint32(&dev->qdev, "irq", isairq); if (qdev_init(&dev->qdev) < 0) - return -1; + return NULL; s = DO_UPCAST(ISAIDEState, dev, dev); if (hd0) ide_create_drive(&s->bus, 0, hd0); if (hd1) ide_create_drive(&s->bus, 1, hd1); - return 0; + return dev; } static ISADeviceInfo isa_ide_info = { .qdev.name = "isa-ide", + .qdev.fw_name = "ide", .qdev.size = sizeof(ISAIDEState), .init = isa_ide_initfn, .qdev.reset = isa_ide_reset, diff -Nru qemu-kvm-0.12.5+noroms/hw/ide/macio.c qemu-kvm-0.14.1/hw/ide/macio.c --- qemu-kvm-0.12.5+noroms/hw/ide/macio.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/ide/macio.c 2011-05-11 13:29:46.000000000 +0000 @@ -40,6 +40,8 @@ BlockDriverAIOCB *aiocb; } MACIOIDEState; +#define MACIO_PAGE_SIZE 4096 + static void pmac_ide_atapi_transfer_cb(void *opaque, int ret) { DBDMA_io *io = opaque; @@ -77,7 +79,7 @@ s->io_buffer_size = io->len; - qemu_sglist_init(&s->sg, io->len / TARGET_PAGE_SIZE + 1); + qemu_sglist_init(&s->sg, io->len / MACIO_PAGE_SIZE + 1); qemu_sglist_add(&s->sg, io->addr, io->len); io->addr += io->len; io->len = 0; @@ -139,7 +141,7 @@ s->io_buffer_index = 0; s->io_buffer_size = io->len; - qemu_sglist_init(&s->sg, io->len / TARGET_PAGE_SIZE + 1); + qemu_sglist_init(&s->sg, io->len / MACIO_PAGE_SIZE + 1); qemu_sglist_add(&s->sg, io->addr, io->len); io->addr += io->len; io->len = 0; @@ -160,7 +162,7 @@ IDEState *s = idebus_active_if(&m->bus); s->io_buffer_size = 0; - if (s->is_cdrom) { + if (s->drive_kind == IDE_CD) { pmac_ide_atapi_transfer_cb(io, 0); return; } @@ -223,9 +225,7 @@ MACIOIDEState *d = opaque; addr = (addr & 0xFFF) >> 4; -#ifdef TARGET_WORDS_BIGENDIAN val = bswap16(val); -#endif if (addr == 0) { ide_data_writew(&d->bus, 0, val); } @@ -242,9 +242,7 @@ } else { retval = 0xFFFF; } -#ifdef TARGET_WORDS_BIGENDIAN retval = bswap16(retval); -#endif return retval; } @@ -254,9 +252,7 @@ MACIOIDEState *d = opaque; addr = (addr & 0xFFF) >> 4; -#ifdef TARGET_WORDS_BIGENDIAN val = bswap32(val); -#endif if (addr == 0) { ide_data_writel(&d->bus, 0, val); } @@ -273,9 +269,7 @@ } else { retval = 0xFFFFFFFF; } -#ifdef TARGET_WORDS_BIGENDIAN retval = bswap32(retval); -#endif return retval; } @@ -320,14 +314,15 @@ int pmac_ide_memory; d = qemu_mallocz(sizeof(MACIOIDEState)); - ide_init2(&d->bus, hd_table[0], hd_table[1], irq); + ide_init2_with_non_qdev_drives(&d->bus, hd_table[0], hd_table[1], irq); if (dbdma) DBDMA_register_channel(dbdma, channel, dma_irq, pmac_ide_transfer, pmac_ide_flush, d); pmac_ide_memory = cpu_register_io_memory(pmac_ide_read, - pmac_ide_write, d); - vmstate_register(0, &vmstate_pmac, d); + pmac_ide_write, d, + DEVICE_NATIVE_ENDIAN); + vmstate_register(NULL, 0, &vmstate_pmac, d); qemu_register_reset(pmac_ide_reset, d); return pmac_ide_memory; diff -Nru qemu-kvm-0.12.5+noroms/hw/ide/microdrive.c qemu-kvm-0.14.1/hw/ide/microdrive.c --- qemu-kvm-0.12.5+noroms/hw/ide/microdrive.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/ide/microdrive.c 2011-05-11 13:29:46.000000000 +0000 @@ -539,12 +539,13 @@ md->card.cis = dscm1xxxx_cis; md->card.cis_len = sizeof(dscm1xxxx_cis); - ide_init2(&md->bus, bdrv, NULL, qemu_allocate_irqs(md_set_irq, md, 1)[0]); - md->bus.ifs[0].is_cf = 1; + ide_init2_with_non_qdev_drives(&md->bus, bdrv, NULL, + qemu_allocate_irqs(md_set_irq, md, 1)[0]); + md->bus.ifs[0].drive_kind = IDE_CFATA; md->bus.ifs[0].mdata_size = METADATA_SIZE; md->bus.ifs[0].mdata_storage = (uint8_t *) qemu_mallocz(METADATA_SIZE); - vmstate_register(-1, &vmstate_microdrive, md); + vmstate_register(NULL, -1, &vmstate_microdrive, md); return &md->card; } diff -Nru qemu-kvm-0.12.5+noroms/hw/ide/mmio.c qemu-kvm-0.14.1/hw/ide/mmio.c --- qemu-kvm-0.12.5+noroms/hw/ide/mmio.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/ide/mmio.c 2011-05-11 13:29:46.000000000 +0000 @@ -125,15 +125,17 @@ MMIOState *s = qemu_mallocz(sizeof(MMIOState)); int mem1, mem2; - ide_init2(&s->bus, hd0, hd1, irq); + ide_init2_with_non_qdev_drives(&s->bus, hd0, hd1, irq); s->shift = shift; - mem1 = cpu_register_io_memory(mmio_ide_reads, mmio_ide_writes, s); - mem2 = cpu_register_io_memory(mmio_ide_status, mmio_ide_cmd, s); + mem1 = cpu_register_io_memory(mmio_ide_reads, mmio_ide_writes, s, + DEVICE_NATIVE_ENDIAN); + mem2 = cpu_register_io_memory(mmio_ide_status, mmio_ide_cmd, s, + DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(membase, 16 << shift, mem1); cpu_register_physical_memory(membase2, 2 << shift, mem2); - vmstate_register(0, &vmstate_ide_mmio, s); + vmstate_register(NULL, 0, &vmstate_ide_mmio, s); qemu_register_reset(mmio_ide_reset, s); } diff -Nru qemu-kvm-0.12.5+noroms/hw/ide/pci.c qemu-kvm-0.14.1/hw/ide/pci.c --- qemu-kvm-0.12.5+noroms/hw/ide/pci.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/ide/pci.c 2011-05-11 13:29:46.000000000 +0000 @@ -33,94 +33,344 @@ #include -void bmdma_cmd_writeb(void *opaque, uint32_t addr, uint32_t val) +#define BMDMA_PAGE_SIZE 4096 + +static void bmdma_start_dma(IDEDMA *dma, IDEState *s, + BlockDriverCompletionFunc *dma_cb) { - BMDMAState *bm = opaque; -#ifdef DEBUG_IDE - printf("%s: 0x%08x\n", __func__, val); -#endif - if (!(val & BM_CMD_START)) { - /* XXX: do it better */ - ide_dma_cancel(bm); - bm->cmd = val & 0x09; - } else { - if (!(bm->status & BM_STATUS_DMAING)) { - bm->status |= BM_STATUS_DMAING; - /* start dma transfer if possible */ - if (bm->dma_cb) - bm->dma_cb(bm, 0); + BMDMAState *bm = DO_UPCAST(BMDMAState, dma, dma); + + bm->unit = s->unit; + bm->dma_cb = dma_cb; + bm->cur_prd_last = 0; + bm->cur_prd_addr = 0; + bm->cur_prd_len = 0; + bm->sector_num = ide_get_sector(s); + bm->nsector = s->nsector; + + if (bm->status & BM_STATUS_DMAING) { + bm->dma_cb(bmdma_active_if(bm), 0); + } +} + +/* return 0 if buffer completed */ +static int bmdma_prepare_buf(IDEDMA *dma, int is_write) +{ + BMDMAState *bm = DO_UPCAST(BMDMAState, dma, dma); + IDEState *s = bmdma_active_if(bm); + struct { + uint32_t addr; + uint32_t size; + } prd; + int l, len; + + qemu_sglist_init(&s->sg, s->nsector / (BMDMA_PAGE_SIZE / 512) + 1); + s->io_buffer_size = 0; + for(;;) { + if (bm->cur_prd_len == 0) { + /* end of table (with a fail safe of one page) */ + if (bm->cur_prd_last || + (bm->cur_addr - bm->addr) >= BMDMA_PAGE_SIZE) + return s->io_buffer_size != 0; + cpu_physical_memory_read(bm->cur_addr, (uint8_t *)&prd, 8); + bm->cur_addr += 8; + prd.addr = le32_to_cpu(prd.addr); + prd.size = le32_to_cpu(prd.size); + len = prd.size & 0xfffe; + if (len == 0) + len = 0x10000; + bm->cur_prd_len = len; + bm->cur_prd_addr = prd.addr; + bm->cur_prd_last = (prd.size & 0x80000000); + } + l = bm->cur_prd_len; + if (l > 0) { + qemu_sglist_add(&s->sg, bm->cur_prd_addr, l); + bm->cur_prd_addr += l; + bm->cur_prd_len -= l; + s->io_buffer_size += l; + } + } + return 1; +} + +/* return 0 if buffer completed */ +static int bmdma_rw_buf(IDEDMA *dma, int is_write) +{ + BMDMAState *bm = DO_UPCAST(BMDMAState, dma, dma); + IDEState *s = bmdma_active_if(bm); + struct { + uint32_t addr; + uint32_t size; + } prd; + int l, len; + + for(;;) { + l = s->io_buffer_size - s->io_buffer_index; + if (l <= 0) + break; + if (bm->cur_prd_len == 0) { + /* end of table (with a fail safe of one page) */ + if (bm->cur_prd_last || + (bm->cur_addr - bm->addr) >= BMDMA_PAGE_SIZE) + return 0; + cpu_physical_memory_read(bm->cur_addr, (uint8_t *)&prd, 8); + bm->cur_addr += 8; + prd.addr = le32_to_cpu(prd.addr); + prd.size = le32_to_cpu(prd.size); + len = prd.size & 0xfffe; + if (len == 0) + len = 0x10000; + bm->cur_prd_len = len; + bm->cur_prd_addr = prd.addr; + bm->cur_prd_last = (prd.size & 0x80000000); + } + if (l > bm->cur_prd_len) + l = bm->cur_prd_len; + if (l > 0) { + if (is_write) { + cpu_physical_memory_write(bm->cur_prd_addr, + s->io_buffer + s->io_buffer_index, l); + } else { + cpu_physical_memory_read(bm->cur_prd_addr, + s->io_buffer + s->io_buffer_index, l); + } + bm->cur_prd_addr += l; + bm->cur_prd_len -= l; + s->io_buffer_index += l; } - bm->cmd = val & 0x09; } + return 1; +} + +static int bmdma_set_unit(IDEDMA *dma, int unit) +{ + BMDMAState *bm = DO_UPCAST(BMDMAState, dma, dma); + bm->unit = unit; + + return 0; } -uint32_t bmdma_addr_readb(void *opaque, uint32_t addr) +static int bmdma_add_status(IDEDMA *dma, int status) +{ + BMDMAState *bm = DO_UPCAST(BMDMAState, dma, dma); + bm->status |= status; + + return 0; +} + +static int bmdma_set_inactive(IDEDMA *dma) +{ + BMDMAState *bm = DO_UPCAST(BMDMAState, dma, dma); + + bm->status &= ~BM_STATUS_DMAING; + bm->dma_cb = NULL; + bm->unit = -1; + + return 0; +} + +static void bmdma_restart_dma(BMDMAState *bm, int is_read) +{ + IDEState *s = bmdma_active_if(bm); + + ide_set_sector(s, bm->sector_num); + s->io_buffer_index = 0; + s->io_buffer_size = 0; + s->nsector = bm->nsector; + s->is_read = is_read; + bm->cur_addr = bm->addr; + bm->dma_cb = ide_dma_cb; + bmdma_start_dma(&bm->dma, s, bm->dma_cb); +} + +static void bmdma_restart_bh(void *opaque) { BMDMAState *bm = opaque; - uint32_t val; - val = (bm->addr >> ((addr & 3) * 8)) & 0xff; + int is_read; + + qemu_bh_delete(bm->bh); + bm->bh = NULL; + + is_read = !!(bm->status & BM_STATUS_RETRY_READ); + + if (bm->status & BM_STATUS_DMA_RETRY) { + bm->status &= ~(BM_STATUS_DMA_RETRY | BM_STATUS_RETRY_READ); + bmdma_restart_dma(bm, is_read); + } else if (bm->status & BM_STATUS_PIO_RETRY) { + bm->status &= ~(BM_STATUS_PIO_RETRY | BM_STATUS_RETRY_READ); + if (is_read) { + ide_sector_read(bmdma_active_if(bm)); + } else { + ide_sector_write(bmdma_active_if(bm)); + } + } else if (bm->status & BM_STATUS_RETRY_FLUSH) { + ide_flush_cache(bmdma_active_if(bm)); + } +} + +static void bmdma_restart_cb(void *opaque, int running, int reason) +{ + IDEDMA *dma = opaque; + BMDMAState *bm = DO_UPCAST(BMDMAState, dma, dma); + + if (!running) + return; + + if (!bm->bh) { + bm->bh = qemu_bh_new(bmdma_restart_bh, &bm->dma); + qemu_bh_schedule(bm->bh); + } +} + +static void bmdma_cancel(BMDMAState *bm) +{ + if (bm->status & BM_STATUS_DMAING) { + /* cancel DMA request */ + bmdma_set_inactive(&bm->dma); + } +} + +static int bmdma_reset(IDEDMA *dma) +{ + BMDMAState *bm = DO_UPCAST(BMDMAState, dma, dma); + #ifdef DEBUG_IDE - printf("%s: 0x%08x\n", __func__, val); + printf("ide: dma_reset\n"); #endif - return val; + bmdma_cancel(bm); + bm->cmd = 0; + bm->status = 0; + bm->addr = 0; + bm->cur_addr = 0; + bm->cur_prd_last = 0; + bm->cur_prd_addr = 0; + bm->cur_prd_len = 0; + bm->sector_num = 0; + bm->nsector = 0; + + return 0; } -void bmdma_addr_writeb(void *opaque, uint32_t addr, uint32_t val) +static int bmdma_start_transfer(IDEDMA *dma) +{ + return 0; +} + +static void bmdma_irq(void *opaque, int n, int level) { BMDMAState *bm = opaque; - int shift = (addr & 3) * 8; -#ifdef DEBUG_IDE - printf("%s: 0x%08x\n", __func__, val); -#endif - bm->addr &= ~(0xFF << shift); - bm->addr |= ((val & 0xFF) << shift) & ~3; - bm->cur_addr = bm->addr; + + if (!level) { + /* pass through lower */ + qemu_set_irq(bm->irq, level); + return; + } + + bm->status |= BM_STATUS_INT; + + /* trigger the real irq */ + qemu_set_irq(bm->irq, level); } -uint32_t bmdma_addr_readw(void *opaque, uint32_t addr) +void bmdma_cmd_writeb(void *opaque, uint32_t addr, uint32_t val) { BMDMAState *bm = opaque; - uint32_t val; - val = (bm->addr >> ((addr & 3) * 8)) & 0xffff; #ifdef DEBUG_IDE printf("%s: 0x%08x\n", __func__, val); #endif - return val; + + /* Ignore writes to SSBM if it keeps the old value */ + if ((val & BM_CMD_START) != (bm->cmd & BM_CMD_START)) { + if (!(val & BM_CMD_START)) { + /* + * We can't cancel Scatter Gather DMA in the middle of the + * operation or a partial (not full) DMA transfer would reach + * the storage so we wait for completion instead (we beahve + * like if the DMA was completed by the time the guest trying + * to cancel dma with bmdma_cmd_writeb with BM_CMD_START not + * set). + * + * In the future we'll be able to safely cancel the I/O if the + * whole DMA operation will be submitted to disk with a single + * aio operation with preadv/pwritev. + */ + if (bm->bus->dma->aiocb) { + qemu_aio_flush(); +#ifdef DEBUG_IDE + if (bm->bus->dma->aiocb) + printf("ide_dma_cancel: aiocb still pending"); + if (bm->status & BM_STATUS_DMAING) + printf("ide_dma_cancel: BM_STATUS_DMAING still pending"); +#endif + } + } else { + bm->cur_addr = bm->addr; + if (!(bm->status & BM_STATUS_DMAING)) { + bm->status |= BM_STATUS_DMAING; + /* start dma transfer if possible */ + if (bm->dma_cb) + bm->dma_cb(bmdma_active_if(bm), 0); + } + } + } + + bm->cmd = val & 0x09; } -void bmdma_addr_writew(void *opaque, uint32_t addr, uint32_t val) +static void bmdma_addr_read(IORange *ioport, uint64_t addr, + unsigned width, uint64_t *data) { - BMDMAState *bm = opaque; - int shift = (addr & 3) * 8; + BMDMAState *bm = container_of(ioport, BMDMAState, addr_ioport); + uint32_t mask = (1ULL << (width * 8)) - 1; + + *data = (bm->addr >> (addr * 8)) & mask; #ifdef DEBUG_IDE - printf("%s: 0x%08x\n", __func__, val); + printf("%s: 0x%08x\n", __func__, (unsigned)*data); #endif - bm->addr &= ~(0xFFFF << shift); - bm->addr |= ((val & 0xFFFF) << shift) & ~3; - bm->cur_addr = bm->addr; } -uint32_t bmdma_addr_readl(void *opaque, uint32_t addr) +static void bmdma_addr_write(IORange *ioport, uint64_t addr, + unsigned width, uint64_t data) { - BMDMAState *bm = opaque; - uint32_t val; - val = bm->addr; + BMDMAState *bm = container_of(ioport, BMDMAState, addr_ioport); + int shift = addr * 8; + uint32_t mask = (1ULL << (width * 8)) - 1; + #ifdef DEBUG_IDE - printf("%s: 0x%08x\n", __func__, val); + printf("%s: 0x%08x\n", __func__, (unsigned)data); #endif - return val; + bm->addr &= ~(mask << shift); + bm->addr |= ((data & mask) << shift) & ~3; } -void bmdma_addr_writel(void *opaque, uint32_t addr, uint32_t val) +const IORangeOps bmdma_addr_ioport_ops = { + .read = bmdma_addr_read, + .write = bmdma_addr_write, +}; + +static bool ide_bmdma_current_needed(void *opaque) { BMDMAState *bm = opaque; -#ifdef DEBUG_IDE - printf("%s: 0x%08x\n", __func__, val); -#endif - bm->addr = val & ~3; - bm->cur_addr = bm->addr; + + return (bm->cur_prd_len != 0); } +static const VMStateDescription vmstate_bmdma_current = { + .name = "ide bmdma_current", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField []) { + VMSTATE_UINT32(cur_addr, BMDMAState), + VMSTATE_UINT32(cur_prd_last, BMDMAState), + VMSTATE_UINT32(cur_prd_addr, BMDMAState), + VMSTATE_UINT32(cur_prd_len, BMDMAState), + VMSTATE_END_OF_LIST() + } +}; + + static const VMStateDescription vmstate_bmdma = { .name = "ide bmdma", .version_id = 3, @@ -134,6 +384,14 @@ VMSTATE_UINT32(nsector, BMDMAState), VMSTATE_UINT8(unit, BMDMAState), VMSTATE_END_OF_LIST() + }, + .subsections = (VMStateSubsection []) { + { + .vmsd = &vmstate_bmdma_current, + .needed = ide_bmdma_current_needed, + }, { + /* empty */ + } } }; @@ -180,3 +438,30 @@ ide_create_drive(d->bus+bus[i], unit[i], hd_table[i]); } } + +static const struct IDEDMAOps bmdma_ops = { + .start_dma = bmdma_start_dma, + .start_transfer = bmdma_start_transfer, + .prepare_buf = bmdma_prepare_buf, + .rw_buf = bmdma_rw_buf, + .set_unit = bmdma_set_unit, + .add_status = bmdma_add_status, + .set_inactive = bmdma_set_inactive, + .restart_cb = bmdma_restart_cb, + .reset = bmdma_reset, +}; + +void bmdma_init(IDEBus *bus, BMDMAState *bm) +{ + qemu_irq *irq; + + if (bus->dma == &bm->dma) { + return; + } + + bm->dma.ops = &bmdma_ops; + bus->dma = &bm->dma; + bm->irq = bus->irq; + irq = qemu_allocate_irqs(bmdma_irq, bm, 1); + bus->irq = *irq; +} diff -Nru qemu-kvm-0.12.5+noroms/hw/ide/pci.h qemu-kvm-0.14.1/hw/ide/pci.h --- qemu-kvm-0.12.5+noroms/hw/ide/pci.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/ide/pci.h 2011-05-11 13:29:46.000000000 +0000 @@ -3,6 +3,27 @@ #include +typedef struct BMDMAState { + IDEDMA dma; + uint8_t cmd; + uint8_t status; + uint32_t addr; + + IDEBus *bus; + /* current transfer state */ + uint32_t cur_addr; + uint32_t cur_prd_last; + uint32_t cur_prd_addr; + uint32_t cur_prd_len; + uint8_t unit; + BlockDriverCompletionFunc *dma_cb; + int64_t sector_num; + uint32_t nsector; + IORange addr_ioport; + QEMUBH *bh; + qemu_irq irq; +} BMDMAState; + typedef struct PCIIDEState { PCIDevice dev; IDEBus bus[2]; @@ -10,13 +31,17 @@ uint32_t secondary; /* used only for cmd646 */ } PCIIDEState; + +static inline IDEState *bmdma_active_if(BMDMAState *bmdma) +{ + assert(bmdma->unit != (uint8_t)-1); + return bmdma->bus->ifs + bmdma->unit; +} + + +void bmdma_init(IDEBus *bus, BMDMAState *bm); void bmdma_cmd_writeb(void *opaque, uint32_t addr, uint32_t val); -uint32_t bmdma_addr_readb(void *opaque, uint32_t addr); -void bmdma_addr_writeb(void *opaque, uint32_t addr, uint32_t val); -uint32_t bmdma_addr_readw(void *opaque, uint32_t addr); -void bmdma_addr_writew(void *opaque, uint32_t addr, uint32_t val); -uint32_t bmdma_addr_readl(void *opaque, uint32_t addr); -void bmdma_addr_writel(void *opaque, uint32_t addr, uint32_t val); +extern const IORangeOps bmdma_addr_ioport_ops; void pci_ide_create_devs(PCIDevice *dev, DriveInfo **hd_table); extern const VMStateDescription vmstate_ide_pci; diff -Nru qemu-kvm-0.12.5+noroms/hw/ide/piix.c qemu-kvm-0.14.1/hw/ide/piix.c --- qemu-kvm-0.12.5+noroms/hw/ide/piix.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/ide/piix.c 2011-05-11 13:29:46.000000000 +0000 @@ -76,22 +76,14 @@ for(i = 0;i < 2; i++) { BMDMAState *bm = &d->bmdma[i]; - d->bus[i].bmdma = bm; - bm->bus = d->bus+i; - bm->pci_dev = d; - qemu_add_vm_change_state_handler(ide_dma_restart_cb, bm); register_ioport_write(addr, 1, 1, bmdma_cmd_writeb, bm); register_ioport_write(addr + 1, 3, 1, bmdma_writeb, bm); register_ioport_read(addr, 4, 1, bmdma_readb, bm); - register_ioport_write(addr + 4, 4, 1, bmdma_addr_writeb, bm); - register_ioport_read(addr + 4, 4, 1, bmdma_addr_readb, bm); - register_ioport_write(addr + 4, 4, 2, bmdma_addr_writew, bm); - register_ioport_read(addr + 4, 4, 2, bmdma_addr_readw, bm); - register_ioport_write(addr + 4, 4, 4, bmdma_addr_writel, bm); - register_ioport_read(addr + 4, 4, 4, bmdma_addr_readl, bm); + iorange_init(&bm->addr_ioport, &bmdma_addr_ioport_ops, addr + 4, 4); + ioport_register(&bm->addr_ioport); addr += 8; } } @@ -104,37 +96,56 @@ for (i = 0; i < 2; i++) { ide_bus_reset(&d->bus[i]); - ide_dma_reset(&d->bmdma[i]); } - pci_conf[0x04] = 0x00; - pci_conf[0x05] = 0x00; - pci_conf[0x06] = 0x80; /* FBC */ - pci_conf[0x07] = 0x02; // PCI_status_devsel_medium + /* TODO: this is the default. do not override. */ + pci_conf[PCI_COMMAND] = 0x00; + /* TODO: this is the default. do not override. */ + pci_conf[PCI_COMMAND + 1] = 0x00; + /* TODO: use pci_set_word */ + pci_conf[PCI_STATUS] = PCI_STATUS_FAST_BACK; + pci_conf[PCI_STATUS + 1] = PCI_STATUS_DEVSEL_MEDIUM >> 8; pci_conf[0x20] = 0x01; /* BMIBA: 20-23h */ } +static void pci_piix_init_ports(PCIIDEState *d) { + int i; + struct { + int iobase; + int iobase2; + int isairq; + } port_info[] = { + {0x1f0, 0x3f6, 14}, + {0x170, 0x376, 15}, + }; + + for (i = 0; i < 2; i++) { + ide_bus_new(&d->bus[i], &d->dev.qdev, i); + ide_init_ioport(&d->bus[i], port_info[i].iobase, port_info[i].iobase2); + ide_init2(&d->bus[i], isa_get_irq(port_info[i].isairq)); + + bmdma_init(&d->bus[i], &d->bmdma[i]); + d->bmdma[i].bus = &d->bus[i]; + qemu_add_vm_change_state_handler(d->bus[i].dma->ops->restart_cb, + &d->bmdma[i].dma); + } +} + static int pci_piix_ide_initfn(PCIIDEState *d) { uint8_t *pci_conf = d->dev.config; - pci_conf[0x09] = 0x80; // legacy ATA mode + pci_conf[PCI_CLASS_PROG] = 0x80; // legacy ATA mode pci_config_set_class(pci_conf, PCI_CLASS_STORAGE_IDE); - pci_conf[PCI_HEADER_TYPE] = PCI_HEADER_TYPE_NORMAL; // header_type qemu_register_reset(piix3_reset, d); pci_register_bar(&d->dev, 4, 0x10, PCI_BASE_ADDRESS_SPACE_IO, bmdma_map); - vmstate_register(0, &vmstate_ide_pci, d); + vmstate_register(&d->dev.qdev, 0, &vmstate_ide_pci, d); - ide_bus_new(&d->bus[0], &d->dev.qdev); - ide_bus_new(&d->bus[1], &d->dev.qdev); - ide_init_ioport(&d->bus[0], 0x1f0, 0x3f6); - ide_init_ioport(&d->bus[1], 0x170, 0x376); + pci_piix_init_ports(d); - ide_init2(&d->bus[0], NULL, NULL, isa_reserve_irq(14)); - ide_init2(&d->bus[1], NULL, NULL, isa_reserve_irq(15)); return 0; } @@ -158,22 +169,24 @@ /* hd_table must contain 4 block drivers */ /* NOTE: for the PIIX3, the IRQs and IOports are hardcoded */ -void pci_piix3_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn) +PCIDevice *pci_piix3_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn) { PCIDevice *dev; dev = pci_create_simple(bus, devfn, "piix3-ide"); pci_ide_create_devs(dev, hd_table); + return dev; } /* hd_table must contain 4 block drivers */ /* NOTE: for the PIIX4, the IRQs and IOports are hardcoded */ -void pci_piix4_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn) +PCIDevice *pci_piix4_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn) { PCIDevice *dev; dev = pci_create_simple(bus, devfn, "piix4-ide"); pci_ide_create_devs(dev, hd_table); + return dev; } static PCIDeviceInfo piix_ide_info[] = { @@ -181,11 +194,13 @@ .qdev.name = "piix3-ide", .qdev.size = sizeof(PCIIDEState), .qdev.no_user = 1, + .no_hotplug = 1, .init = pci_piix3_ide_initfn, },{ .qdev.name = "piix4-ide", .qdev.size = sizeof(PCIIDEState), .qdev.no_user = 1, + .no_hotplug = 1, .init = pci_piix4_ide_initfn, },{ /* end of list */ diff -Nru qemu-kvm-0.12.5+noroms/hw/ide/qdev.c qemu-kvm-0.14.1/hw/ide/qdev.c --- qemu-kvm-0.12.5+noroms/hw/ide/qdev.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/ide/qdev.c 2011-05-11 13:29:46.000000000 +0000 @@ -17,21 +17,36 @@ * License along with this library; if not, see . */ #include -#include "sysemu.h" #include "dma.h" - +#include "qemu-error.h" #include +#include "blockdev.h" +#include "sysemu.h" /* --------------------------------- */ +static char *idebus_get_fw_dev_path(DeviceState *dev); + static struct BusInfo ide_bus_info = { .name = "IDE", .size = sizeof(IDEBus), + .get_fw_dev_path = idebus_get_fw_dev_path, }; -void ide_bus_new(IDEBus *idebus, DeviceState *dev) +void ide_bus_new(IDEBus *idebus, DeviceState *dev, int bus_id) { qbus_create_inplace(&idebus->qbus, &ide_bus_info, dev, NULL); + idebus->bus_id = bus_id; +} + +static char *idebus_get_fw_dev_path(DeviceState *dev) +{ + char path[30]; + + snprintf(path, sizeof(path), "%s@%d", qdev_fw_name(dev), + ((IDEBus*)dev->parent_bus)->bus_id); + + return strdup(path); } static int ide_qdev_init(DeviceState *qdev, DeviceInfo *base) @@ -40,8 +55,8 @@ IDEDeviceInfo *info = DO_UPCAST(IDEDeviceInfo, qdev, base); IDEBus *bus = DO_UPCAST(IDEBus, qbus, qdev->parent_bus); - if (!dev->dinfo) { - fprintf(stderr, "%s: no drive specified\n", qdev->info->name); + if (!dev->conf.bs) { + error_report("No drive specified"); goto err; } if (dev->unit == -1) { @@ -50,19 +65,20 @@ switch (dev->unit) { case 0: if (bus->master) { - fprintf(stderr, "ide: tried to assign master twice\n"); + error_report("IDE unit %d is in use", dev->unit); goto err; } bus->master = dev; break; case 1: if (bus->slave) { - fprintf(stderr, "ide: tried to assign slave twice\n"); + error_report("IDE unit %d is in use", dev->unit); goto err; } bus->slave = dev; break; default: + error_report("Invalid IDE unit %d", dev->unit); goto err; } return info->init(dev); @@ -84,12 +100,18 @@ dev = qdev_create(&bus->qbus, "ide-drive"); qdev_prop_set_uint32(dev, "unit", unit); - qdev_prop_set_drive(dev, "drive", drive); - if (qdev_init(dev) < 0) - return NULL; + qdev_prop_set_drive_nofail(dev, "drive", drive->bdrv); + qdev_init_nofail(dev); return DO_UPCAST(IDEDevice, qdev, dev); } +void ide_get_bs(BlockDriverState *bs[], BusState *qbus) +{ + IDEBus *bus = DO_UPCAST(IDEBus, qbus, qbus); + bs[0] = bus->master ? bus->master->conf.bs : NULL; + bs[1] = bus->slave ? bus->slave->conf.bs : NULL; +} + /* --------------------------------- */ typedef struct IDEDrive { @@ -99,18 +121,46 @@ static int ide_drive_initfn(IDEDevice *dev) { IDEBus *bus = DO_UPCAST(IDEBus, qbus, dev->qdev.parent_bus); - ide_init_drive(bus->ifs + dev->unit, dev->dinfo, dev->version); + IDEState *s = bus->ifs + dev->unit; + const char *serial; + DriveInfo *dinfo; + + serial = dev->serial; + if (!serial) { + /* try to fall back to value set with legacy -drive serial=... */ + dinfo = drive_get_by_blockdev(dev->conf.bs); + if (*dinfo->serial) { + serial = dinfo->serial; + } + } + + if (ide_init_drive(s, dev->conf.bs, dev->version, serial) < 0) { + return -1; + } + + if (!dev->version) { + dev->version = qemu_strdup(s->version); + } + if (!dev->serial) { + dev->serial = qemu_strdup(s->drive_serial_str); + } + + add_boot_device_path(dev->conf.bootindex, &dev->qdev, + dev->unit ? "/disk@1" : "/disk@0"); + return 0; } static IDEDeviceInfo ide_drive_info = { .qdev.name = "ide-drive", + .qdev.fw_name = "drive", .qdev.size = sizeof(IDEDrive), .init = ide_drive_initfn, .qdev.props = (Property[]) { DEFINE_PROP_UINT32("unit", IDEDrive, dev.unit, -1), - DEFINE_PROP_DRIVE("drive", IDEDrive, dev.dinfo), + DEFINE_BLOCK_PROPERTIES(IDEDrive, dev.conf), DEFINE_PROP_STRING("ver", IDEDrive, dev.version), + DEFINE_PROP_STRING("serial", IDEDrive, dev.serial), DEFINE_PROP_END_OF_LIST(), } }; diff -Nru qemu-kvm-0.12.5+noroms/hw/ide/via.c qemu-kvm-0.14.1/hw/ide/via.c --- qemu-kvm-0.12.5+noroms/hw/ide/via.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/ide/via.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,200 @@ +/* + * QEMU IDE Emulation: PCI VIA82C686B support. + * + * Copyright (c) 2003 Fabrice Bellard + * Copyright (c) 2006 Openedhand Ltd. + * Copyright (c) 2010 Huacai Chen + * + * 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 +#include +#include +#include +#include "block.h" +#include "block_int.h" +#include "sysemu.h" +#include "dma.h" + +#include + +static uint32_t bmdma_readb(void *opaque, uint32_t addr) +{ + BMDMAState *bm = opaque; + uint32_t val; + + switch (addr & 3) { + case 0: + val = bm->cmd; + break; + case 2: + val = bm->status; + break; + default: + val = 0xff; + break; + } +#ifdef DEBUG_IDE + printf("bmdma: readb 0x%02x : 0x%02x\n", addr, val); +#endif + return val; +} + +static void bmdma_writeb(void *opaque, uint32_t addr, uint32_t val) +{ + BMDMAState *bm = opaque; +#ifdef DEBUG_IDE + printf("bmdma: writeb 0x%02x : 0x%02x\n", addr, val); +#endif + switch (addr & 3) { + case 2: + bm->status = (val & 0x60) | (bm->status & 1) | (bm->status & ~val & 0x06); + break; + default:; + } +} + +static void bmdma_map(PCIDevice *pci_dev, int region_num, + pcibus_t addr, pcibus_t size, int type) +{ + PCIIDEState *d = DO_UPCAST(PCIIDEState, dev, pci_dev); + int i; + + for(i = 0;i < 2; i++) { + BMDMAState *bm = &d->bmdma[i]; + + register_ioport_write(addr, 1, 1, bmdma_cmd_writeb, bm); + + register_ioport_write(addr + 1, 3, 1, bmdma_writeb, bm); + register_ioport_read(addr, 4, 1, bmdma_readb, bm); + + iorange_init(&bm->addr_ioport, &bmdma_addr_ioport_ops, addr + 4, 4); + ioport_register(&bm->addr_ioport); + addr += 8; + } +} + +static void via_reset(void *opaque) +{ + PCIIDEState *d = opaque; + uint8_t *pci_conf = d->dev.config; + int i; + + for (i = 0; i < 2; i++) { + ide_bus_reset(&d->bus[i]); + } + + pci_set_word(pci_conf + PCI_COMMAND, PCI_COMMAND_WAIT); + pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_FAST_BACK | + PCI_STATUS_DEVSEL_MEDIUM); + + pci_set_long(pci_conf + PCI_BASE_ADDRESS_0, 0x000001f0); + pci_set_long(pci_conf + PCI_BASE_ADDRESS_1, 0x000003f4); + pci_set_long(pci_conf + PCI_BASE_ADDRESS_2, 0x00000170); + pci_set_long(pci_conf + PCI_BASE_ADDRESS_3, 0x00000374); + pci_set_long(pci_conf + PCI_BASE_ADDRESS_4, 0x0000cc01); /* BMIBA: 20-23h */ + pci_set_long(pci_conf + PCI_INTERRUPT_LINE, 0x0000010e); + + /* IDE chip enable, IDE configuration 1/2, IDE FIFO Configuration*/ + pci_set_long(pci_conf + 0x40, 0x0a090600); + /* IDE misc configuration 1/2/3 */ + pci_set_long(pci_conf + 0x44, 0x00c00068); + /* IDE Timing control */ + pci_set_long(pci_conf + 0x48, 0xa8a8a8a8); + /* IDE Address Setup Time */ + pci_set_long(pci_conf + 0x4c, 0x000000ff); + /* UltraDMA Extended Timing Control*/ + pci_set_long(pci_conf + 0x50, 0x07070707); + /* UltraDMA FIFO Control */ + pci_set_long(pci_conf + 0x54, 0x00000004); + /* IDE primary sector size */ + pci_set_long(pci_conf + 0x60, 0x00000200); + /* IDE secondary sector size */ + pci_set_long(pci_conf + 0x68, 0x00000200); + /* PCI PM Block */ + pci_set_long(pci_conf + 0xc0, 0x00020001); +} + +static void vt82c686b_init_ports(PCIIDEState *d) { + int i; + struct { + int iobase; + int iobase2; + int isairq; + } port_info[] = { + {0x1f0, 0x3f6, 14}, + {0x170, 0x376, 15}, + }; + + for (i = 0; i < 2; i++) { + ide_bus_new(&d->bus[i], &d->dev.qdev, i); + ide_init_ioport(&d->bus[i], port_info[i].iobase, port_info[i].iobase2); + ide_init2(&d->bus[i], isa_get_irq(port_info[i].isairq)); + + bmdma_init(&d->bus[i], &d->bmdma[i]); + d->bmdma[i].bus = &d->bus[i]; + qemu_add_vm_change_state_handler(d->bus[i].dma->ops->restart_cb, + &d->bmdma[i].dma); + } +} + +/* via ide func */ +static int vt82c686b_ide_initfn(PCIDevice *dev) +{ + PCIIDEState *d = DO_UPCAST(PCIIDEState, dev, dev);; + uint8_t *pci_conf = d->dev.config; + + pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_VIA); + pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_VIA_IDE); + pci_config_set_class(pci_conf, PCI_CLASS_STORAGE_IDE); + pci_config_set_prog_interface(pci_conf, 0x8a); /* legacy ATA mode */ + pci_config_set_revision(pci_conf,0x06); /* Revision 0.6 */ + pci_set_long(pci_conf + PCI_CAPABILITY_LIST, 0x000000c0); + + qemu_register_reset(via_reset, d); + pci_register_bar(&d->dev, 4, 0x10, + PCI_BASE_ADDRESS_SPACE_IO, bmdma_map); + + vmstate_register(&dev->qdev, 0, &vmstate_ide_pci, d); + + vt82c686b_init_ports(d); + + return 0; +} + +void vt82c686b_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn) +{ + PCIDevice *dev; + + dev = pci_create_simple(bus, devfn, "via-ide"); + pci_ide_create_devs(dev, hd_table); +} + +static PCIDeviceInfo via_ide_info = { + .qdev.name = "via-ide", + .qdev.size = sizeof(PCIIDEState), + .qdev.no_user = 1, + .init = vt82c686b_ide_initfn, +}; + +static void via_ide_register(void) +{ + pci_qdev_register(&via_ide_info); +} +device_init(via_ide_register); diff -Nru qemu-kvm-0.12.5+noroms/hw/ide.h qemu-kvm-0.14.1/hw/ide.h --- qemu-kvm-0.12.5+noroms/hw/ide.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/ide.h 2011-05-11 13:29:46.000000000 +0000 @@ -1,17 +1,21 @@ #ifndef HW_IDE_H #define HW_IDE_H -#include "qdev.h" +#include "isa.h" +#include "pci.h" + +#define MAX_IDE_DEVS 2 /* ide-isa.c */ -int isa_ide_init(int iobase, int iobase2, int isairq, - DriveInfo *hd0, DriveInfo *hd1); +ISADevice *isa_ide_init(int iobase, int iobase2, int isairq, + DriveInfo *hd0, DriveInfo *hd1); /* ide-pci.c */ void pci_cmd646_ide_init(PCIBus *bus, DriveInfo **hd_table, int secondary_ide_enabled); -void pci_piix3_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn); -void pci_piix4_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn); +PCIDevice *pci_piix3_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn); +PCIDevice *pci_piix4_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn); +void vt82c686b_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn); /* ide-macio.c */ int pmac_ide_init (DriveInfo **hd_table, qemu_irq irq, @@ -22,4 +26,6 @@ qemu_irq irq, int shift, DriveInfo *hd0, DriveInfo *hd1); +void ide_get_bs(BlockDriverState *bs[], BusState *qbus); + #endif /* HW_IDE_H */ diff -Nru qemu-kvm-0.12.5+noroms/hw/integratorcp.c qemu-kvm-0.14.1/hw/integratorcp.c --- qemu-kvm-0.12.5+noroms/hw/integratorcp.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/integratorcp.c 2011-05-11 13:29:46.000000000 +0000 @@ -253,10 +253,11 @@ } memcpy(integrator_spd + 73, "QEMU-MEMORY", 11); s->cm_init = 0x00000112; - s->flash_offset = qemu_ram_alloc(0x100000); + s->flash_offset = qemu_ram_alloc(NULL, "integrator.flash", 0x100000); iomemtype = cpu_register_io_memory(integratorcm_readfn, - integratorcm_writefn, s); + integratorcm_writefn, s, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, 0x00800000, iomemtype); integratorcm_do_remap(s, 1); /* ??? Save/restore. */ @@ -382,7 +383,8 @@ sysbus_init_irq(dev, &s->parent_irq); sysbus_init_irq(dev, &s->parent_fiq); iomemtype = cpu_register_io_memory(icp_pic_readfn, - icp_pic_writefn, s); + icp_pic_writefn, s, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, 0x00800000, iomemtype); return 0; } @@ -435,7 +437,8 @@ int iomemtype; iomemtype = cpu_register_io_memory(icp_control_readfn, - icp_control_writefn, NULL); + icp_control_writefn, NULL, + DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base, 0x00800000, iomemtype); /* ??? Save/restore. */ } @@ -467,7 +470,7 @@ fprintf(stderr, "Unable to find CPU definition\n"); exit(1); } - ram_offset = qemu_ram_alloc(ram_size); + ram_offset = qemu_ram_alloc(NULL, "integrator.ram", ram_size); /* ??? On a real system the first 1Mb is mapped as SSRAM or boot flash. */ /* ??? RAM should repeat to fill physical memory space. */ /* SDRAM at address zero*/ diff -Nru qemu-kvm-0.12.5+noroms/hw/intel-hda.c qemu-kvm-0.14.1/hw/intel-hda.c --- qemu-kvm-0.12.5+noroms/hw/intel-hda.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/intel-hda.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,1308 @@ +/* + * Copyright (C) 2010 Red Hat, Inc. + * + * written by Gerd Hoffmann + * + * 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 or + * (at your option) version 3 of the License. + * + * 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, see . + */ + +#include "hw.h" +#include "pci.h" +#include "msi.h" +#include "qemu-timer.h" +#include "audiodev.h" +#include "intel-hda.h" +#include "intel-hda-defs.h" + +/* --------------------------------------------------------------------- */ +/* hda bus */ + +static struct BusInfo hda_codec_bus_info = { + .name = "HDA", + .size = sizeof(HDACodecBus), + .props = (Property[]) { + DEFINE_PROP_UINT32("cad", HDACodecDevice, cad, -1), + DEFINE_PROP_END_OF_LIST() + } +}; + +void hda_codec_bus_init(DeviceState *dev, HDACodecBus *bus, + hda_codec_response_func response, + hda_codec_xfer_func xfer) +{ + qbus_create_inplace(&bus->qbus, &hda_codec_bus_info, dev, NULL); + bus->response = response; + bus->xfer = xfer; +} + +static int hda_codec_dev_init(DeviceState *qdev, DeviceInfo *base) +{ + HDACodecBus *bus = DO_UPCAST(HDACodecBus, qbus, qdev->parent_bus); + HDACodecDevice *dev = DO_UPCAST(HDACodecDevice, qdev, qdev); + HDACodecDeviceInfo *info = DO_UPCAST(HDACodecDeviceInfo, qdev, base); + + dev->info = info; + if (dev->cad == -1) { + dev->cad = bus->next_cad; + } + if (dev->cad >= 15) { + return -1; + } + bus->next_cad = dev->cad + 1; + return info->init(dev); +} + +static int hda_codec_dev_exit(DeviceState *qdev) +{ + HDACodecDevice *dev = DO_UPCAST(HDACodecDevice, qdev, qdev); + + if (dev->info->exit) { + dev->info->exit(dev); + } + return 0; +} + +void hda_codec_register(HDACodecDeviceInfo *info) +{ + info->qdev.init = hda_codec_dev_init; + info->qdev.exit = hda_codec_dev_exit; + info->qdev.bus_info = &hda_codec_bus_info; + qdev_register(&info->qdev); +} + +HDACodecDevice *hda_codec_find(HDACodecBus *bus, uint32_t cad) +{ + DeviceState *qdev; + HDACodecDevice *cdev; + + QLIST_FOREACH(qdev, &bus->qbus.children, sibling) { + cdev = DO_UPCAST(HDACodecDevice, qdev, qdev); + if (cdev->cad == cad) { + return cdev; + } + } + return NULL; +} + +void hda_codec_response(HDACodecDevice *dev, bool solicited, uint32_t response) +{ + HDACodecBus *bus = DO_UPCAST(HDACodecBus, qbus, dev->qdev.parent_bus); + bus->response(dev, solicited, response); +} + +bool hda_codec_xfer(HDACodecDevice *dev, uint32_t stnr, bool output, + uint8_t *buf, uint32_t len) +{ + HDACodecBus *bus = DO_UPCAST(HDACodecBus, qbus, dev->qdev.parent_bus); + return bus->xfer(dev, stnr, output, buf, len); +} + +/* --------------------------------------------------------------------- */ +/* intel hda emulation */ + +typedef struct IntelHDAStream IntelHDAStream; +typedef struct IntelHDAState IntelHDAState; +typedef struct IntelHDAReg IntelHDAReg; + +typedef struct bpl { + uint64_t addr; + uint32_t len; + uint32_t flags; +} bpl; + +struct IntelHDAStream { + /* registers */ + uint32_t ctl; + uint32_t lpib; + uint32_t cbl; + uint32_t lvi; + uint32_t fmt; + uint32_t bdlp_lbase; + uint32_t bdlp_ubase; + + /* state */ + bpl *bpl; + uint32_t bentries; + uint32_t bsize, be, bp; +}; + +struct IntelHDAState { + PCIDevice pci; + const char *name; + HDACodecBus codecs; + + /* registers */ + uint32_t g_ctl; + uint32_t wake_en; + uint32_t state_sts; + uint32_t int_ctl; + uint32_t int_sts; + uint32_t wall_clk; + + uint32_t corb_lbase; + uint32_t corb_ubase; + uint32_t corb_rp; + uint32_t corb_wp; + uint32_t corb_ctl; + uint32_t corb_sts; + uint32_t corb_size; + + uint32_t rirb_lbase; + uint32_t rirb_ubase; + uint32_t rirb_wp; + uint32_t rirb_cnt; + uint32_t rirb_ctl; + uint32_t rirb_sts; + uint32_t rirb_size; + + uint32_t dp_lbase; + uint32_t dp_ubase; + + uint32_t icw; + uint32_t irr; + uint32_t ics; + + /* streams */ + IntelHDAStream st[8]; + + /* state */ + int mmio_addr; + uint32_t rirb_count; + int64_t wall_base_ns; + + /* debug logging */ + const IntelHDAReg *last_reg; + uint32_t last_val; + uint32_t last_write; + uint32_t last_sec; + uint32_t repeat_count; + + /* properties */ + uint32_t debug; + uint32_t msi; +}; + +struct IntelHDAReg { + const char *name; /* register name */ + uint32_t size; /* size in bytes */ + uint32_t reset; /* reset value */ + uint32_t wmask; /* write mask */ + uint32_t wclear; /* write 1 to clear bits */ + uint32_t offset; /* location in IntelHDAState */ + uint32_t shift; /* byte access entries for dwords */ + uint32_t stream; + void (*whandler)(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old); + void (*rhandler)(IntelHDAState *d, const IntelHDAReg *reg); +}; + +static void intel_hda_reset(DeviceState *dev); + +/* --------------------------------------------------------------------- */ + +static target_phys_addr_t intel_hda_addr(uint32_t lbase, uint32_t ubase) +{ + target_phys_addr_t addr; + +#if TARGET_PHYS_ADDR_BITS == 32 + addr = lbase; +#else + addr = ubase; + addr <<= 32; + addr |= lbase; +#endif + return addr; +} + +static void stl_phys_le(target_phys_addr_t addr, uint32_t value) +{ + uint32_t value_le = cpu_to_le32(value); + cpu_physical_memory_write(addr, (uint8_t*)(&value_le), sizeof(value_le)); +} + +static uint32_t ldl_phys_le(target_phys_addr_t addr) +{ + uint32_t value_le; + cpu_physical_memory_read(addr, (uint8_t*)(&value_le), sizeof(value_le)); + return le32_to_cpu(value_le); +} + +static void intel_hda_update_int_sts(IntelHDAState *d) +{ + uint32_t sts = 0; + uint32_t i; + + /* update controller status */ + if (d->rirb_sts & ICH6_RBSTS_IRQ) { + sts |= (1 << 30); + } + if (d->rirb_sts & ICH6_RBSTS_OVERRUN) { + sts |= (1 << 30); + } + if (d->state_sts & d->wake_en) { + sts |= (1 << 30); + } + + /* update stream status */ + for (i = 0; i < 8; i++) { + /* buffer completion interrupt */ + if (d->st[i].ctl & (1 << 26)) { + sts |= (1 << i); + } + } + + /* update global status */ + if (sts & d->int_ctl) { + sts |= (1 << 31); + } + + d->int_sts = sts; +} + +static void intel_hda_update_irq(IntelHDAState *d) +{ + int msi = d->msi && msi_enabled(&d->pci); + int level; + + intel_hda_update_int_sts(d); + if (d->int_sts & (1 << 31) && d->int_ctl & (1 << 31)) { + level = 1; + } else { + level = 0; + } + dprint(d, 2, "%s: level %d [%s]\n", __FUNCTION__, + level, msi ? "msi" : "intx"); + if (msi) { + if (level) { + msi_notify(&d->pci, 0); + } + } else { + qemu_set_irq(d->pci.irq[0], level); + } +} + +static int intel_hda_send_command(IntelHDAState *d, uint32_t verb) +{ + uint32_t cad, nid, data; + HDACodecDevice *codec; + + cad = (verb >> 28) & 0x0f; + if (verb & (1 << 27)) { + /* indirect node addressing, not specified in HDA 1.0 */ + dprint(d, 1, "%s: indirect node addressing (guest bug?)\n", __FUNCTION__); + return -1; + } + nid = (verb >> 20) & 0x7f; + data = verb & 0xfffff; + + codec = hda_codec_find(&d->codecs, cad); + if (codec == NULL) { + dprint(d, 1, "%s: addressed non-existing codec\n", __FUNCTION__); + return -1; + } + codec->info->command(codec, nid, data); + return 0; +} + +static void intel_hda_corb_run(IntelHDAState *d) +{ + target_phys_addr_t addr; + uint32_t rp, verb; + + if (d->ics & ICH6_IRS_BUSY) { + dprint(d, 2, "%s: [icw] verb 0x%08x\n", __FUNCTION__, d->icw); + intel_hda_send_command(d, d->icw); + return; + } + + for (;;) { + if (!(d->corb_ctl & ICH6_CORBCTL_RUN)) { + dprint(d, 2, "%s: !run\n", __FUNCTION__); + return; + } + if ((d->corb_rp & 0xff) == d->corb_wp) { + dprint(d, 2, "%s: corb ring empty\n", __FUNCTION__); + return; + } + if (d->rirb_count == d->rirb_cnt) { + dprint(d, 2, "%s: rirb count reached\n", __FUNCTION__); + return; + } + + rp = (d->corb_rp + 1) & 0xff; + addr = intel_hda_addr(d->corb_lbase, d->corb_ubase); + verb = ldl_phys_le(addr + 4*rp); + d->corb_rp = rp; + + dprint(d, 2, "%s: [rp 0x%x] verb 0x%08x\n", __FUNCTION__, rp, verb); + intel_hda_send_command(d, verb); + } +} + +static void intel_hda_response(HDACodecDevice *dev, bool solicited, uint32_t response) +{ + HDACodecBus *bus = DO_UPCAST(HDACodecBus, qbus, dev->qdev.parent_bus); + IntelHDAState *d = container_of(bus, IntelHDAState, codecs); + target_phys_addr_t addr; + uint32_t wp, ex; + + if (d->ics & ICH6_IRS_BUSY) { + dprint(d, 2, "%s: [irr] response 0x%x, cad 0x%x\n", + __FUNCTION__, response, dev->cad); + d->irr = response; + d->ics &= ~(ICH6_IRS_BUSY | 0xf0); + d->ics |= (ICH6_IRS_VALID | (dev->cad << 4)); + return; + } + + if (!(d->rirb_ctl & ICH6_RBCTL_DMA_EN)) { + dprint(d, 1, "%s: rirb dma disabled, drop codec response\n", __FUNCTION__); + return; + } + + ex = (solicited ? 0 : (1 << 4)) | dev->cad; + wp = (d->rirb_wp + 1) & 0xff; + addr = intel_hda_addr(d->rirb_lbase, d->rirb_ubase); + stl_phys_le(addr + 8*wp, response); + stl_phys_le(addr + 8*wp + 4, ex); + d->rirb_wp = wp; + + dprint(d, 2, "%s: [wp 0x%x] response 0x%x, extra 0x%x\n", + __FUNCTION__, wp, response, ex); + + d->rirb_count++; + if (d->rirb_count == d->rirb_cnt) { + dprint(d, 2, "%s: rirb count reached (%d)\n", __FUNCTION__, d->rirb_count); + if (d->rirb_ctl & ICH6_RBCTL_IRQ_EN) { + d->rirb_sts |= ICH6_RBSTS_IRQ; + intel_hda_update_irq(d); + } + } else if ((d->corb_rp & 0xff) == d->corb_wp) { + dprint(d, 2, "%s: corb ring empty (%d/%d)\n", __FUNCTION__, + d->rirb_count, d->rirb_cnt); + if (d->rirb_ctl & ICH6_RBCTL_IRQ_EN) { + d->rirb_sts |= ICH6_RBSTS_IRQ; + intel_hda_update_irq(d); + } + } +} + +static bool intel_hda_xfer(HDACodecDevice *dev, uint32_t stnr, bool output, + uint8_t *buf, uint32_t len) +{ + HDACodecBus *bus = DO_UPCAST(HDACodecBus, qbus, dev->qdev.parent_bus); + IntelHDAState *d = container_of(bus, IntelHDAState, codecs); + IntelHDAStream *st = NULL; + target_phys_addr_t addr; + uint32_t s, copy, left; + bool irq = false; + + for (s = 0; s < ARRAY_SIZE(d->st); s++) { + if (stnr == ((d->st[s].ctl >> 20) & 0x0f)) { + st = d->st + s; + break; + } + } + if (st == NULL) { + return false; + } + if (st->bpl == NULL) { + return false; + } + if (st->ctl & (1 << 26)) { + /* + * Wait with the next DMA xfer until the guest + * has acked the buffer completion interrupt + */ + return false; + } + + left = len; + while (left > 0) { + copy = left; + if (copy > st->bsize - st->lpib) + copy = st->bsize - st->lpib; + if (copy > st->bpl[st->be].len - st->bp) + copy = st->bpl[st->be].len - st->bp; + + dprint(d, 3, "dma: entry %d, pos %d/%d, copy %d\n", + st->be, st->bp, st->bpl[st->be].len, copy); + + cpu_physical_memory_rw(st->bpl[st->be].addr + st->bp, + buf, copy, !output); + st->lpib += copy; + st->bp += copy; + buf += copy; + left -= copy; + + if (st->bpl[st->be].len == st->bp) { + /* bpl entry filled */ + if (st->bpl[st->be].flags & 0x01) { + irq = true; + } + st->bp = 0; + st->be++; + if (st->be == st->bentries) { + /* bpl wrap around */ + st->be = 0; + st->lpib = 0; + } + } + } + if (d->dp_lbase & 0x01) { + addr = intel_hda_addr(d->dp_lbase & ~0x01, d->dp_ubase); + stl_phys_le(addr + 8*s, st->lpib); + } + dprint(d, 3, "dma: --\n"); + + if (irq) { + st->ctl |= (1 << 26); /* buffer completion interrupt */ + intel_hda_update_irq(d); + } + return true; +} + +static void intel_hda_parse_bdl(IntelHDAState *d, IntelHDAStream *st) +{ + target_phys_addr_t addr; + uint8_t buf[16]; + uint32_t i; + + addr = intel_hda_addr(st->bdlp_lbase, st->bdlp_ubase); + st->bentries = st->lvi +1; + qemu_free(st->bpl); + st->bpl = qemu_malloc(sizeof(bpl) * st->bentries); + for (i = 0; i < st->bentries; i++, addr += 16) { + cpu_physical_memory_read(addr, buf, 16); + st->bpl[i].addr = le64_to_cpu(*(uint64_t *)buf); + st->bpl[i].len = le32_to_cpu(*(uint32_t *)(buf + 8)); + st->bpl[i].flags = le32_to_cpu(*(uint32_t *)(buf + 12)); + dprint(d, 1, "bdl/%d: 0x%" PRIx64 " +0x%x, 0x%x\n", + i, st->bpl[i].addr, st->bpl[i].len, st->bpl[i].flags); + } + + st->bsize = st->cbl; + st->lpib = 0; + st->be = 0; + st->bp = 0; +} + +static void intel_hda_notify_codecs(IntelHDAState *d, uint32_t stream, bool running) +{ + DeviceState *qdev; + HDACodecDevice *cdev; + + QLIST_FOREACH(qdev, &d->codecs.qbus.children, sibling) { + cdev = DO_UPCAST(HDACodecDevice, qdev, qdev); + if (cdev->info->stream) { + cdev->info->stream(cdev, stream, running); + } + } +} + +/* --------------------------------------------------------------------- */ + +static void intel_hda_set_g_ctl(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old) +{ + if ((d->g_ctl & ICH6_GCTL_RESET) == 0) { + intel_hda_reset(&d->pci.qdev); + } +} + +static void intel_hda_set_wake_en(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old) +{ + intel_hda_update_irq(d); +} + +static void intel_hda_set_state_sts(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old) +{ + intel_hda_update_irq(d); +} + +static void intel_hda_set_int_ctl(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old) +{ + intel_hda_update_irq(d); +} + +static void intel_hda_get_wall_clk(IntelHDAState *d, const IntelHDAReg *reg) +{ + int64_t ns; + + ns = qemu_get_clock_ns(vm_clock) - d->wall_base_ns; + d->wall_clk = (uint32_t)(ns * 24 / 1000); /* 24 MHz */ +} + +static void intel_hda_set_corb_wp(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old) +{ + intel_hda_corb_run(d); +} + +static void intel_hda_set_corb_ctl(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old) +{ + intel_hda_corb_run(d); +} + +static void intel_hda_set_rirb_wp(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old) +{ + if (d->rirb_wp & ICH6_RIRBWP_RST) { + d->rirb_wp = 0; + } +} + +static void intel_hda_set_rirb_sts(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old) +{ + intel_hda_update_irq(d); + + if ((old & ICH6_RBSTS_IRQ) && !(d->rirb_sts & ICH6_RBSTS_IRQ)) { + /* cleared ICH6_RBSTS_IRQ */ + d->rirb_count = 0; + intel_hda_corb_run(d); + } +} + +static void intel_hda_set_ics(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old) +{ + if (d->ics & ICH6_IRS_BUSY) { + intel_hda_corb_run(d); + } +} + +static void intel_hda_set_st_ctl(IntelHDAState *d, const IntelHDAReg *reg, uint32_t old) +{ + IntelHDAStream *st = d->st + reg->stream; + + if (st->ctl & 0x01) { + /* reset */ + dprint(d, 1, "st #%d: reset\n", reg->stream); + st->ctl = 0; + } + if ((st->ctl & 0x02) != (old & 0x02)) { + uint32_t stnr = (st->ctl >> 20) & 0x0f; + /* run bit flipped */ + if (st->ctl & 0x02) { + /* start */ + dprint(d, 1, "st #%d: start %d (ring buf %d bytes)\n", + reg->stream, stnr, st->cbl); + intel_hda_parse_bdl(d, st); + intel_hda_notify_codecs(d, stnr, true); + } else { + /* stop */ + dprint(d, 1, "st #%d: stop %d\n", reg->stream, stnr); + intel_hda_notify_codecs(d, stnr, false); + } + } + intel_hda_update_irq(d); +} + +/* --------------------------------------------------------------------- */ + +#define ST_REG(_n, _o) (0x80 + (_n) * 0x20 + (_o)) + +static const struct IntelHDAReg regtab[] = { + /* global */ + [ ICH6_REG_GCAP ] = { + .name = "GCAP", + .size = 2, + .reset = 0x4401, + }, + [ ICH6_REG_VMIN ] = { + .name = "VMIN", + .size = 1, + }, + [ ICH6_REG_VMAJ ] = { + .name = "VMAJ", + .size = 1, + .reset = 1, + }, + [ ICH6_REG_OUTPAY ] = { + .name = "OUTPAY", + .size = 2, + .reset = 0x3c, + }, + [ ICH6_REG_INPAY ] = { + .name = "INPAY", + .size = 2, + .reset = 0x1d, + }, + [ ICH6_REG_GCTL ] = { + .name = "GCTL", + .size = 4, + .wmask = 0x0103, + .offset = offsetof(IntelHDAState, g_ctl), + .whandler = intel_hda_set_g_ctl, + }, + [ ICH6_REG_WAKEEN ] = { + .name = "WAKEEN", + .size = 2, + .wmask = 0x7fff, + .offset = offsetof(IntelHDAState, wake_en), + .whandler = intel_hda_set_wake_en, + }, + [ ICH6_REG_STATESTS ] = { + .name = "STATESTS", + .size = 2, + .wmask = 0x7fff, + .wclear = 0x7fff, + .offset = offsetof(IntelHDAState, state_sts), + .whandler = intel_hda_set_state_sts, + }, + + /* interrupts */ + [ ICH6_REG_INTCTL ] = { + .name = "INTCTL", + .size = 4, + .wmask = 0xc00000ff, + .offset = offsetof(IntelHDAState, int_ctl), + .whandler = intel_hda_set_int_ctl, + }, + [ ICH6_REG_INTSTS ] = { + .name = "INTSTS", + .size = 4, + .wmask = 0xc00000ff, + .wclear = 0xc00000ff, + .offset = offsetof(IntelHDAState, int_sts), + }, + + /* misc */ + [ ICH6_REG_WALLCLK ] = { + .name = "WALLCLK", + .size = 4, + .offset = offsetof(IntelHDAState, wall_clk), + .rhandler = intel_hda_get_wall_clk, + }, + [ ICH6_REG_WALLCLK + 0x2000 ] = { + .name = "WALLCLK(alias)", + .size = 4, + .offset = offsetof(IntelHDAState, wall_clk), + .rhandler = intel_hda_get_wall_clk, + }, + + /* dma engine */ + [ ICH6_REG_CORBLBASE ] = { + .name = "CORBLBASE", + .size = 4, + .wmask = 0xffffff80, + .offset = offsetof(IntelHDAState, corb_lbase), + }, + [ ICH6_REG_CORBUBASE ] = { + .name = "CORBUBASE", + .size = 4, + .wmask = 0xffffffff, + .offset = offsetof(IntelHDAState, corb_ubase), + }, + [ ICH6_REG_CORBWP ] = { + .name = "CORBWP", + .size = 2, + .wmask = 0xff, + .offset = offsetof(IntelHDAState, corb_wp), + .whandler = intel_hda_set_corb_wp, + }, + [ ICH6_REG_CORBRP ] = { + .name = "CORBRP", + .size = 2, + .wmask = 0x80ff, + .offset = offsetof(IntelHDAState, corb_rp), + }, + [ ICH6_REG_CORBCTL ] = { + .name = "CORBCTL", + .size = 1, + .wmask = 0x03, + .offset = offsetof(IntelHDAState, corb_ctl), + .whandler = intel_hda_set_corb_ctl, + }, + [ ICH6_REG_CORBSTS ] = { + .name = "CORBSTS", + .size = 1, + .wmask = 0x01, + .wclear = 0x01, + .offset = offsetof(IntelHDAState, corb_sts), + }, + [ ICH6_REG_CORBSIZE ] = { + .name = "CORBSIZE", + .size = 1, + .reset = 0x42, + .offset = offsetof(IntelHDAState, corb_size), + }, + [ ICH6_REG_RIRBLBASE ] = { + .name = "RIRBLBASE", + .size = 4, + .wmask = 0xffffff80, + .offset = offsetof(IntelHDAState, rirb_lbase), + }, + [ ICH6_REG_RIRBUBASE ] = { + .name = "RIRBUBASE", + .size = 4, + .wmask = 0xffffffff, + .offset = offsetof(IntelHDAState, rirb_ubase), + }, + [ ICH6_REG_RIRBWP ] = { + .name = "RIRBWP", + .size = 2, + .wmask = 0x8000, + .offset = offsetof(IntelHDAState, rirb_wp), + .whandler = intel_hda_set_rirb_wp, + }, + [ ICH6_REG_RINTCNT ] = { + .name = "RINTCNT", + .size = 2, + .wmask = 0xff, + .offset = offsetof(IntelHDAState, rirb_cnt), + }, + [ ICH6_REG_RIRBCTL ] = { + .name = "RIRBCTL", + .size = 1, + .wmask = 0x07, + .offset = offsetof(IntelHDAState, rirb_ctl), + }, + [ ICH6_REG_RIRBSTS ] = { + .name = "RIRBSTS", + .size = 1, + .wmask = 0x05, + .wclear = 0x05, + .offset = offsetof(IntelHDAState, rirb_sts), + .whandler = intel_hda_set_rirb_sts, + }, + [ ICH6_REG_RIRBSIZE ] = { + .name = "RIRBSIZE", + .size = 1, + .reset = 0x42, + .offset = offsetof(IntelHDAState, rirb_size), + }, + + [ ICH6_REG_DPLBASE ] = { + .name = "DPLBASE", + .size = 4, + .wmask = 0xffffff81, + .offset = offsetof(IntelHDAState, dp_lbase), + }, + [ ICH6_REG_DPUBASE ] = { + .name = "DPUBASE", + .size = 4, + .wmask = 0xffffffff, + .offset = offsetof(IntelHDAState, dp_ubase), + }, + + [ ICH6_REG_IC ] = { + .name = "ICW", + .size = 4, + .wmask = 0xffffffff, + .offset = offsetof(IntelHDAState, icw), + }, + [ ICH6_REG_IR ] = { + .name = "IRR", + .size = 4, + .offset = offsetof(IntelHDAState, irr), + }, + [ ICH6_REG_IRS ] = { + .name = "ICS", + .size = 2, + .wmask = 0x0003, + .wclear = 0x0002, + .offset = offsetof(IntelHDAState, ics), + .whandler = intel_hda_set_ics, + }, + +#define HDA_STREAM(_t, _i) \ + [ ST_REG(_i, ICH6_REG_SD_CTL) ] = { \ + .stream = _i, \ + .name = _t stringify(_i) " CTL", \ + .size = 4, \ + .wmask = 0x1cff001f, \ + .offset = offsetof(IntelHDAState, st[_i].ctl), \ + .whandler = intel_hda_set_st_ctl, \ + }, \ + [ ST_REG(_i, ICH6_REG_SD_CTL) + 2] = { \ + .stream = _i, \ + .name = _t stringify(_i) " CTL(stnr)", \ + .size = 1, \ + .shift = 16, \ + .wmask = 0x00ff0000, \ + .offset = offsetof(IntelHDAState, st[_i].ctl), \ + .whandler = intel_hda_set_st_ctl, \ + }, \ + [ ST_REG(_i, ICH6_REG_SD_STS)] = { \ + .stream = _i, \ + .name = _t stringify(_i) " CTL(sts)", \ + .size = 1, \ + .shift = 24, \ + .wmask = 0x1c000000, \ + .wclear = 0x1c000000, \ + .offset = offsetof(IntelHDAState, st[_i].ctl), \ + .whandler = intel_hda_set_st_ctl, \ + }, \ + [ ST_REG(_i, ICH6_REG_SD_LPIB) ] = { \ + .stream = _i, \ + .name = _t stringify(_i) " LPIB", \ + .size = 4, \ + .offset = offsetof(IntelHDAState, st[_i].lpib), \ + }, \ + [ ST_REG(_i, ICH6_REG_SD_LPIB) + 0x2000 ] = { \ + .stream = _i, \ + .name = _t stringify(_i) " LPIB(alias)", \ + .size = 4, \ + .offset = offsetof(IntelHDAState, st[_i].lpib), \ + }, \ + [ ST_REG(_i, ICH6_REG_SD_CBL) ] = { \ + .stream = _i, \ + .name = _t stringify(_i) " CBL", \ + .size = 4, \ + .wmask = 0xffffffff, \ + .offset = offsetof(IntelHDAState, st[_i].cbl), \ + }, \ + [ ST_REG(_i, ICH6_REG_SD_LVI) ] = { \ + .stream = _i, \ + .name = _t stringify(_i) " LVI", \ + .size = 2, \ + .wmask = 0x00ff, \ + .offset = offsetof(IntelHDAState, st[_i].lvi), \ + }, \ + [ ST_REG(_i, ICH6_REG_SD_FIFOSIZE) ] = { \ + .stream = _i, \ + .name = _t stringify(_i) " FIFOS", \ + .size = 2, \ + .reset = HDA_BUFFER_SIZE, \ + }, \ + [ ST_REG(_i, ICH6_REG_SD_FORMAT) ] = { \ + .stream = _i, \ + .name = _t stringify(_i) " FMT", \ + .size = 2, \ + .wmask = 0x7f7f, \ + .offset = offsetof(IntelHDAState, st[_i].fmt), \ + }, \ + [ ST_REG(_i, ICH6_REG_SD_BDLPL) ] = { \ + .stream = _i, \ + .name = _t stringify(_i) " BDLPL", \ + .size = 4, \ + .wmask = 0xffffff80, \ + .offset = offsetof(IntelHDAState, st[_i].bdlp_lbase), \ + }, \ + [ ST_REG(_i, ICH6_REG_SD_BDLPU) ] = { \ + .stream = _i, \ + .name = _t stringify(_i) " BDLPU", \ + .size = 4, \ + .wmask = 0xffffffff, \ + .offset = offsetof(IntelHDAState, st[_i].bdlp_ubase), \ + }, \ + + HDA_STREAM("IN", 0) + HDA_STREAM("IN", 1) + HDA_STREAM("IN", 2) + HDA_STREAM("IN", 3) + + HDA_STREAM("OUT", 4) + HDA_STREAM("OUT", 5) + HDA_STREAM("OUT", 6) + HDA_STREAM("OUT", 7) + +}; + +static const IntelHDAReg *intel_hda_reg_find(IntelHDAState *d, target_phys_addr_t addr) +{ + const IntelHDAReg *reg; + + if (addr >= sizeof(regtab)/sizeof(regtab[0])) { + goto noreg; + } + reg = regtab+addr; + if (reg->name == NULL) { + goto noreg; + } + return reg; + +noreg: + dprint(d, 1, "unknown register, addr 0x%x\n", (int) addr); + return NULL; +} + +static uint32_t *intel_hda_reg_addr(IntelHDAState *d, const IntelHDAReg *reg) +{ + uint8_t *addr = (void*)d; + + addr += reg->offset; + return (uint32_t*)addr; +} + +static void intel_hda_reg_write(IntelHDAState *d, const IntelHDAReg *reg, uint32_t val, + uint32_t wmask) +{ + uint32_t *addr; + uint32_t old; + + if (!reg) { + return; + } + + if (d->debug) { + time_t now = time(NULL); + if (d->last_write && d->last_reg == reg && d->last_val == val) { + d->repeat_count++; + if (d->last_sec != now) { + dprint(d, 2, "previous register op repeated %d times\n", d->repeat_count); + d->last_sec = now; + d->repeat_count = 0; + } + } else { + if (d->repeat_count) { + dprint(d, 2, "previous register op repeated %d times\n", d->repeat_count); + } + dprint(d, 2, "write %-16s: 0x%x (%x)\n", reg->name, val, wmask); + d->last_write = 1; + d->last_reg = reg; + d->last_val = val; + d->last_sec = now; + d->repeat_count = 0; + } + } + assert(reg->offset != 0); + + addr = intel_hda_reg_addr(d, reg); + old = *addr; + + if (reg->shift) { + val <<= reg->shift; + wmask <<= reg->shift; + } + wmask &= reg->wmask; + *addr &= ~wmask; + *addr |= wmask & val; + *addr &= ~(val & reg->wclear); + + if (reg->whandler) { + reg->whandler(d, reg, old); + } +} + +static uint32_t intel_hda_reg_read(IntelHDAState *d, const IntelHDAReg *reg, + uint32_t rmask) +{ + uint32_t *addr, ret; + + if (!reg) { + return 0; + } + + if (reg->rhandler) { + reg->rhandler(d, reg); + } + + if (reg->offset == 0) { + /* constant read-only register */ + ret = reg->reset; + } else { + addr = intel_hda_reg_addr(d, reg); + ret = *addr; + if (reg->shift) { + ret >>= reg->shift; + } + ret &= rmask; + } + if (d->debug) { + time_t now = time(NULL); + if (!d->last_write && d->last_reg == reg && d->last_val == ret) { + d->repeat_count++; + if (d->last_sec != now) { + dprint(d, 2, "previous register op repeated %d times\n", d->repeat_count); + d->last_sec = now; + d->repeat_count = 0; + } + } else { + if (d->repeat_count) { + dprint(d, 2, "previous register op repeated %d times\n", d->repeat_count); + } + dprint(d, 2, "read %-16s: 0x%x (%x)\n", reg->name, ret, rmask); + d->last_write = 0; + d->last_reg = reg; + d->last_val = ret; + d->last_sec = now; + d->repeat_count = 0; + } + } + return ret; +} + +static void intel_hda_regs_reset(IntelHDAState *d) +{ + uint32_t *addr; + int i; + + for (i = 0; i < sizeof(regtab)/sizeof(regtab[0]); i++) { + if (regtab[i].name == NULL) { + continue; + } + if (regtab[i].offset == 0) { + continue; + } + addr = intel_hda_reg_addr(d, regtab + i); + *addr = regtab[i].reset; + } +} + +/* --------------------------------------------------------------------- */ + +static void intel_hda_mmio_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + IntelHDAState *d = opaque; + const IntelHDAReg *reg = intel_hda_reg_find(d, addr); + + intel_hda_reg_write(d, reg, val, 0xff); +} + +static void intel_hda_mmio_writew(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + IntelHDAState *d = opaque; + const IntelHDAReg *reg = intel_hda_reg_find(d, addr); + + intel_hda_reg_write(d, reg, val, 0xffff); +} + +static void intel_hda_mmio_writel(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + IntelHDAState *d = opaque; + const IntelHDAReg *reg = intel_hda_reg_find(d, addr); + + intel_hda_reg_write(d, reg, val, 0xffffffff); +} + +static uint32_t intel_hda_mmio_readb(void *opaque, target_phys_addr_t addr) +{ + IntelHDAState *d = opaque; + const IntelHDAReg *reg = intel_hda_reg_find(d, addr); + + return intel_hda_reg_read(d, reg, 0xff); +} + +static uint32_t intel_hda_mmio_readw(void *opaque, target_phys_addr_t addr) +{ + IntelHDAState *d = opaque; + const IntelHDAReg *reg = intel_hda_reg_find(d, addr); + + return intel_hda_reg_read(d, reg, 0xffff); +} + +static uint32_t intel_hda_mmio_readl(void *opaque, target_phys_addr_t addr) +{ + IntelHDAState *d = opaque; + const IntelHDAReg *reg = intel_hda_reg_find(d, addr); + + return intel_hda_reg_read(d, reg, 0xffffffff); +} + +static CPUReadMemoryFunc * const intel_hda_mmio_read[3] = { + intel_hda_mmio_readb, + intel_hda_mmio_readw, + intel_hda_mmio_readl, +}; + +static CPUWriteMemoryFunc * const intel_hda_mmio_write[3] = { + intel_hda_mmio_writeb, + intel_hda_mmio_writew, + intel_hda_mmio_writel, +}; + +static void intel_hda_map(PCIDevice *pci, int region_num, + pcibus_t addr, pcibus_t size, int type) +{ + IntelHDAState *d = DO_UPCAST(IntelHDAState, pci, pci); + + cpu_register_physical_memory(addr, 0x4000, d->mmio_addr); +} + +/* --------------------------------------------------------------------- */ + +static void intel_hda_reset(DeviceState *dev) +{ + IntelHDAState *d = DO_UPCAST(IntelHDAState, pci.qdev, dev); + DeviceState *qdev; + HDACodecDevice *cdev; + + intel_hda_regs_reset(d); + d->wall_base_ns = qemu_get_clock(vm_clock); + + /* reset codecs */ + QLIST_FOREACH(qdev, &d->codecs.qbus.children, sibling) { + cdev = DO_UPCAST(HDACodecDevice, qdev, qdev); + if (qdev->info->reset) { + qdev->info->reset(qdev); + } + d->state_sts |= (1 << cdev->cad); + } + intel_hda_update_irq(d); +} + +static int intel_hda_init(PCIDevice *pci) +{ + IntelHDAState *d = DO_UPCAST(IntelHDAState, pci, pci); + uint8_t *conf = d->pci.config; + + d->name = d->pci.qdev.info->name; + + pci_config_set_vendor_id(conf, PCI_VENDOR_ID_INTEL); + pci_config_set_device_id(conf, 0x2668); + pci_config_set_revision(conf, 1); + pci_config_set_class(conf, PCI_CLASS_MULTIMEDIA_HD_AUDIO); + pci_config_set_interrupt_pin(conf, 1); + + /* HDCTL off 0x40 bit 0 selects signaling mode (1-HDA, 0 - Ac97) 18.1.19 */ + conf[0x40] = 0x01; + + d->mmio_addr = cpu_register_io_memory(intel_hda_mmio_read, + intel_hda_mmio_write, d, + DEVICE_NATIVE_ENDIAN); + pci_register_bar(&d->pci, 0, 0x4000, PCI_BASE_ADDRESS_SPACE_MEMORY, + intel_hda_map); + if (d->msi) { + msi_init(&d->pci, 0x50, 1, true, false); + } + + hda_codec_bus_init(&d->pci.qdev, &d->codecs, + intel_hda_response, intel_hda_xfer); + + return 0; +} + +static int intel_hda_exit(PCIDevice *pci) +{ + IntelHDAState *d = DO_UPCAST(IntelHDAState, pci, pci); + + if (d->msi) { + msi_uninit(&d->pci); + } + cpu_unregister_io_memory(d->mmio_addr); + return 0; +} + +static void intel_hda_write_config(PCIDevice *pci, uint32_t addr, + uint32_t val, int len) +{ + IntelHDAState *d = DO_UPCAST(IntelHDAState, pci, pci); + + pci_default_write_config(pci, addr, val, len); + if (d->msi) { + msi_write_config(pci, addr, val, len); + } +} + +static int intel_hda_post_load(void *opaque, int version) +{ + IntelHDAState* d = opaque; + int i; + + dprint(d, 1, "%s\n", __FUNCTION__); + for (i = 0; i < ARRAY_SIZE(d->st); i++) { + if (d->st[i].ctl & 0x02) { + intel_hda_parse_bdl(d, &d->st[i]); + } + } + intel_hda_update_irq(d); + return 0; +} + +static const VMStateDescription vmstate_intel_hda_stream = { + .name = "intel-hda-stream", + .version_id = 1, + .fields = (VMStateField []) { + VMSTATE_UINT32(ctl, IntelHDAStream), + VMSTATE_UINT32(lpib, IntelHDAStream), + VMSTATE_UINT32(cbl, IntelHDAStream), + VMSTATE_UINT32(lvi, IntelHDAStream), + VMSTATE_UINT32(fmt, IntelHDAStream), + VMSTATE_UINT32(bdlp_lbase, IntelHDAStream), + VMSTATE_UINT32(bdlp_ubase, IntelHDAStream), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_intel_hda = { + .name = "intel-hda", + .version_id = 1, + .post_load = intel_hda_post_load, + .fields = (VMStateField []) { + VMSTATE_PCI_DEVICE(pci, IntelHDAState), + + /* registers */ + VMSTATE_UINT32(g_ctl, IntelHDAState), + VMSTATE_UINT32(wake_en, IntelHDAState), + VMSTATE_UINT32(state_sts, IntelHDAState), + VMSTATE_UINT32(int_ctl, IntelHDAState), + VMSTATE_UINT32(int_sts, IntelHDAState), + VMSTATE_UINT32(wall_clk, IntelHDAState), + VMSTATE_UINT32(corb_lbase, IntelHDAState), + VMSTATE_UINT32(corb_ubase, IntelHDAState), + VMSTATE_UINT32(corb_rp, IntelHDAState), + VMSTATE_UINT32(corb_wp, IntelHDAState), + VMSTATE_UINT32(corb_ctl, IntelHDAState), + VMSTATE_UINT32(corb_sts, IntelHDAState), + VMSTATE_UINT32(corb_size, IntelHDAState), + VMSTATE_UINT32(rirb_lbase, IntelHDAState), + VMSTATE_UINT32(rirb_ubase, IntelHDAState), + VMSTATE_UINT32(rirb_wp, IntelHDAState), + VMSTATE_UINT32(rirb_cnt, IntelHDAState), + VMSTATE_UINT32(rirb_ctl, IntelHDAState), + VMSTATE_UINT32(rirb_sts, IntelHDAState), + VMSTATE_UINT32(rirb_size, IntelHDAState), + VMSTATE_UINT32(dp_lbase, IntelHDAState), + VMSTATE_UINT32(dp_ubase, IntelHDAState), + VMSTATE_UINT32(icw, IntelHDAState), + VMSTATE_UINT32(irr, IntelHDAState), + VMSTATE_UINT32(ics, IntelHDAState), + VMSTATE_STRUCT_ARRAY(st, IntelHDAState, 8, 0, + vmstate_intel_hda_stream, + IntelHDAStream), + + /* additional state info */ + VMSTATE_UINT32(rirb_count, IntelHDAState), + VMSTATE_INT64(wall_base_ns, IntelHDAState), + + VMSTATE_END_OF_LIST() + } +}; + +static PCIDeviceInfo intel_hda_info = { + .qdev.name = "intel-hda", + .qdev.desc = "Intel HD Audio Controller", + .qdev.size = sizeof(IntelHDAState), + .qdev.vmsd = &vmstate_intel_hda, + .qdev.reset = intel_hda_reset, + .init = intel_hda_init, + .exit = intel_hda_exit, + .config_write = intel_hda_write_config, + .qdev.props = (Property[]) { + DEFINE_PROP_UINT32("debug", IntelHDAState, debug, 0), + DEFINE_PROP_UINT32("msi", IntelHDAState, msi, 1), + DEFINE_PROP_END_OF_LIST(), + } +}; + +static void intel_hda_register(void) +{ + pci_qdev_register(&intel_hda_info); +} +device_init(intel_hda_register); + +/* + * create intel hda controller with codec attached to it, + * so '-soundhw hda' works. + */ +int intel_hda_and_codec_init(PCIBus *bus) +{ + PCIDevice *controller; + BusState *hdabus; + DeviceState *codec; + + controller = pci_create_simple(bus, -1, "intel-hda"); + hdabus = QLIST_FIRST(&controller->qdev.child_bus); + codec = qdev_create(hdabus, "hda-duplex"); + qdev_init_nofail(codec); + return 0; +} + diff -Nru qemu-kvm-0.12.5+noroms/hw/intel-hda-defs.h qemu-kvm-0.14.1/hw/intel-hda-defs.h --- qemu-kvm-0.12.5+noroms/hw/intel-hda-defs.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/intel-hda-defs.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,717 @@ +#ifndef HW_INTEL_HDA_DEFS_H +#define HW_INTEL_HDA_DEFS_H + +/* qemu */ +#define HDA_BUFFER_SIZE 256 + +/* --------------------------------------------------------------------- */ +/* from linux/sound/pci/hda/hda_intel.c */ + +/* + * registers + */ +#define ICH6_REG_GCAP 0x00 +#define ICH6_GCAP_64OK (1 << 0) /* 64bit address support */ +#define ICH6_GCAP_NSDO (3 << 1) /* # of serial data out signals */ +#define ICH6_GCAP_BSS (31 << 3) /* # of bidirectional streams */ +#define ICH6_GCAP_ISS (15 << 8) /* # of input streams */ +#define ICH6_GCAP_OSS (15 << 12) /* # of output streams */ +#define ICH6_REG_VMIN 0x02 +#define ICH6_REG_VMAJ 0x03 +#define ICH6_REG_OUTPAY 0x04 +#define ICH6_REG_INPAY 0x06 +#define ICH6_REG_GCTL 0x08 +#define ICH6_GCTL_RESET (1 << 0) /* controller reset */ +#define ICH6_GCTL_FCNTRL (1 << 1) /* flush control */ +#define ICH6_GCTL_UNSOL (1 << 8) /* accept unsol. response enable */ +#define ICH6_REG_WAKEEN 0x0c +#define ICH6_REG_STATESTS 0x0e +#define ICH6_REG_GSTS 0x10 +#define ICH6_GSTS_FSTS (1 << 1) /* flush status */ +#define ICH6_REG_INTCTL 0x20 +#define ICH6_REG_INTSTS 0x24 +#define ICH6_REG_WALLCLK 0x30 /* 24Mhz source */ +#define ICH6_REG_SYNC 0x34 +#define ICH6_REG_CORBLBASE 0x40 +#define ICH6_REG_CORBUBASE 0x44 +#define ICH6_REG_CORBWP 0x48 +#define ICH6_REG_CORBRP 0x4a +#define ICH6_CORBRP_RST (1 << 15) /* read pointer reset */ +#define ICH6_REG_CORBCTL 0x4c +#define ICH6_CORBCTL_RUN (1 << 1) /* enable DMA */ +#define ICH6_CORBCTL_CMEIE (1 << 0) /* enable memory error irq */ +#define ICH6_REG_CORBSTS 0x4d +#define ICH6_CORBSTS_CMEI (1 << 0) /* memory error indication */ +#define ICH6_REG_CORBSIZE 0x4e + +#define ICH6_REG_RIRBLBASE 0x50 +#define ICH6_REG_RIRBUBASE 0x54 +#define ICH6_REG_RIRBWP 0x58 +#define ICH6_RIRBWP_RST (1 << 15) /* write pointer reset */ +#define ICH6_REG_RINTCNT 0x5a +#define ICH6_REG_RIRBCTL 0x5c +#define ICH6_RBCTL_IRQ_EN (1 << 0) /* enable IRQ */ +#define ICH6_RBCTL_DMA_EN (1 << 1) /* enable DMA */ +#define ICH6_RBCTL_OVERRUN_EN (1 << 2) /* enable overrun irq */ +#define ICH6_REG_RIRBSTS 0x5d +#define ICH6_RBSTS_IRQ (1 << 0) /* response irq */ +#define ICH6_RBSTS_OVERRUN (1 << 2) /* overrun irq */ +#define ICH6_REG_RIRBSIZE 0x5e + +#define ICH6_REG_IC 0x60 +#define ICH6_REG_IR 0x64 +#define ICH6_REG_IRS 0x68 +#define ICH6_IRS_VALID (1<<1) +#define ICH6_IRS_BUSY (1<<0) + +#define ICH6_REG_DPLBASE 0x70 +#define ICH6_REG_DPUBASE 0x74 +#define ICH6_DPLBASE_ENABLE 0x1 /* Enable position buffer */ + +/* SD offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */ +enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 }; + +/* stream register offsets from stream base */ +#define ICH6_REG_SD_CTL 0x00 +#define ICH6_REG_SD_STS 0x03 +#define ICH6_REG_SD_LPIB 0x04 +#define ICH6_REG_SD_CBL 0x08 +#define ICH6_REG_SD_LVI 0x0c +#define ICH6_REG_SD_FIFOW 0x0e +#define ICH6_REG_SD_FIFOSIZE 0x10 +#define ICH6_REG_SD_FORMAT 0x12 +#define ICH6_REG_SD_BDLPL 0x18 +#define ICH6_REG_SD_BDLPU 0x1c + +/* PCI space */ +#define ICH6_PCIREG_TCSEL 0x44 + +/* + * other constants + */ + +/* max number of SDs */ +/* ICH, ATI and VIA have 4 playback and 4 capture */ +#define ICH6_NUM_CAPTURE 4 +#define ICH6_NUM_PLAYBACK 4 + +/* ULI has 6 playback and 5 capture */ +#define ULI_NUM_CAPTURE 5 +#define ULI_NUM_PLAYBACK 6 + +/* ATI HDMI has 1 playback and 0 capture */ +#define ATIHDMI_NUM_CAPTURE 0 +#define ATIHDMI_NUM_PLAYBACK 1 + +/* TERA has 4 playback and 3 capture */ +#define TERA_NUM_CAPTURE 3 +#define TERA_NUM_PLAYBACK 4 + +/* this number is statically defined for simplicity */ +#define MAX_AZX_DEV 16 + +/* max number of fragments - we may use more if allocating more pages for BDL */ +#define BDL_SIZE 4096 +#define AZX_MAX_BDL_ENTRIES (BDL_SIZE / 16) +#define AZX_MAX_FRAG 32 +/* max buffer size - no h/w limit, you can increase as you like */ +#define AZX_MAX_BUF_SIZE (1024*1024*1024) + +/* RIRB int mask: overrun[2], response[0] */ +#define RIRB_INT_RESPONSE 0x01 +#define RIRB_INT_OVERRUN 0x04 +#define RIRB_INT_MASK 0x05 + +/* STATESTS int mask: S3,SD2,SD1,SD0 */ +#define AZX_MAX_CODECS 8 +#define AZX_DEFAULT_CODECS 4 +#define STATESTS_INT_MASK ((1 << AZX_MAX_CODECS) - 1) + +/* SD_CTL bits */ +#define SD_CTL_STREAM_RESET 0x01 /* stream reset bit */ +#define SD_CTL_DMA_START 0x02 /* stream DMA start bit */ +#define SD_CTL_STRIPE (3 << 16) /* stripe control */ +#define SD_CTL_TRAFFIC_PRIO (1 << 18) /* traffic priority */ +#define SD_CTL_DIR (1 << 19) /* bi-directional stream */ +#define SD_CTL_STREAM_TAG_MASK (0xf << 20) +#define SD_CTL_STREAM_TAG_SHIFT 20 + +/* SD_CTL and SD_STS */ +#define SD_INT_DESC_ERR 0x10 /* descriptor error interrupt */ +#define SD_INT_FIFO_ERR 0x08 /* FIFO error interrupt */ +#define SD_INT_COMPLETE 0x04 /* completion interrupt */ +#define SD_INT_MASK (SD_INT_DESC_ERR|SD_INT_FIFO_ERR|\ + SD_INT_COMPLETE) + +/* SD_STS */ +#define SD_STS_FIFO_READY 0x20 /* FIFO ready */ + +/* INTCTL and INTSTS */ +#define ICH6_INT_ALL_STREAM 0xff /* all stream interrupts */ +#define ICH6_INT_CTRL_EN 0x40000000 /* controller interrupt enable bit */ +#define ICH6_INT_GLOBAL_EN 0x80000000 /* global interrupt enable bit */ + +/* below are so far hardcoded - should read registers in future */ +#define ICH6_MAX_CORB_ENTRIES 256 +#define ICH6_MAX_RIRB_ENTRIES 256 + +/* position fix mode */ +enum { + POS_FIX_AUTO, + POS_FIX_LPIB, + POS_FIX_POSBUF, +}; + +/* Defines for ATI HD Audio support in SB450 south bridge */ +#define ATI_SB450_HDAUDIO_MISC_CNTR2_ADDR 0x42 +#define ATI_SB450_HDAUDIO_ENABLE_SNOOP 0x02 + +/* Defines for Nvidia HDA support */ +#define NVIDIA_HDA_TRANSREG_ADDR 0x4e +#define NVIDIA_HDA_ENABLE_COHBITS 0x0f +#define NVIDIA_HDA_ISTRM_COH 0x4d +#define NVIDIA_HDA_OSTRM_COH 0x4c +#define NVIDIA_HDA_ENABLE_COHBIT 0x01 + +/* Defines for Intel SCH HDA snoop control */ +#define INTEL_SCH_HDA_DEVC 0x78 +#define INTEL_SCH_HDA_DEVC_NOSNOOP (0x1<<11) + +/* Define IN stream 0 FIFO size offset in VIA controller */ +#define VIA_IN_STREAM0_FIFO_SIZE_OFFSET 0x90 +/* Define VIA HD Audio Device ID*/ +#define VIA_HDAC_DEVICE_ID 0x3288 + +/* HD Audio class code */ +#define PCI_CLASS_MULTIMEDIA_HD_AUDIO 0x0403 + +/* --------------------------------------------------------------------- */ +/* from linux/sound/pci/hda/hda_codec.h */ + +/* + * nodes + */ +#define AC_NODE_ROOT 0x00 + +/* + * function group types + */ +enum { + AC_GRP_AUDIO_FUNCTION = 0x01, + AC_GRP_MODEM_FUNCTION = 0x02, +}; + +/* + * widget types + */ +enum { + AC_WID_AUD_OUT, /* Audio Out */ + AC_WID_AUD_IN, /* Audio In */ + AC_WID_AUD_MIX, /* Audio Mixer */ + AC_WID_AUD_SEL, /* Audio Selector */ + AC_WID_PIN, /* Pin Complex */ + AC_WID_POWER, /* Power */ + AC_WID_VOL_KNB, /* Volume Knob */ + AC_WID_BEEP, /* Beep Generator */ + AC_WID_VENDOR = 0x0f /* Vendor specific */ +}; + +/* + * GET verbs + */ +#define AC_VERB_GET_STREAM_FORMAT 0x0a00 +#define AC_VERB_GET_AMP_GAIN_MUTE 0x0b00 +#define AC_VERB_GET_PROC_COEF 0x0c00 +#define AC_VERB_GET_COEF_INDEX 0x0d00 +#define AC_VERB_PARAMETERS 0x0f00 +#define AC_VERB_GET_CONNECT_SEL 0x0f01 +#define AC_VERB_GET_CONNECT_LIST 0x0f02 +#define AC_VERB_GET_PROC_STATE 0x0f03 +#define AC_VERB_GET_SDI_SELECT 0x0f04 +#define AC_VERB_GET_POWER_STATE 0x0f05 +#define AC_VERB_GET_CONV 0x0f06 +#define AC_VERB_GET_PIN_WIDGET_CONTROL 0x0f07 +#define AC_VERB_GET_UNSOLICITED_RESPONSE 0x0f08 +#define AC_VERB_GET_PIN_SENSE 0x0f09 +#define AC_VERB_GET_BEEP_CONTROL 0x0f0a +#define AC_VERB_GET_EAPD_BTLENABLE 0x0f0c +#define AC_VERB_GET_DIGI_CONVERT_1 0x0f0d +#define AC_VERB_GET_DIGI_CONVERT_2 0x0f0e /* unused */ +#define AC_VERB_GET_VOLUME_KNOB_CONTROL 0x0f0f +/* f10-f1a: GPIO */ +#define AC_VERB_GET_GPIO_DATA 0x0f15 +#define AC_VERB_GET_GPIO_MASK 0x0f16 +#define AC_VERB_GET_GPIO_DIRECTION 0x0f17 +#define AC_VERB_GET_GPIO_WAKE_MASK 0x0f18 +#define AC_VERB_GET_GPIO_UNSOLICITED_RSP_MASK 0x0f19 +#define AC_VERB_GET_GPIO_STICKY_MASK 0x0f1a +#define AC_VERB_GET_CONFIG_DEFAULT 0x0f1c +/* f20: AFG/MFG */ +#define AC_VERB_GET_SUBSYSTEM_ID 0x0f20 +#define AC_VERB_GET_CVT_CHAN_COUNT 0x0f2d +#define AC_VERB_GET_HDMI_DIP_SIZE 0x0f2e +#define AC_VERB_GET_HDMI_ELDD 0x0f2f +#define AC_VERB_GET_HDMI_DIP_INDEX 0x0f30 +#define AC_VERB_GET_HDMI_DIP_DATA 0x0f31 +#define AC_VERB_GET_HDMI_DIP_XMIT 0x0f32 +#define AC_VERB_GET_HDMI_CP_CTRL 0x0f33 +#define AC_VERB_GET_HDMI_CHAN_SLOT 0x0f34 + +/* + * SET verbs + */ +#define AC_VERB_SET_STREAM_FORMAT 0x200 +#define AC_VERB_SET_AMP_GAIN_MUTE 0x300 +#define AC_VERB_SET_PROC_COEF 0x400 +#define AC_VERB_SET_COEF_INDEX 0x500 +#define AC_VERB_SET_CONNECT_SEL 0x701 +#define AC_VERB_SET_PROC_STATE 0x703 +#define AC_VERB_SET_SDI_SELECT 0x704 +#define AC_VERB_SET_POWER_STATE 0x705 +#define AC_VERB_SET_CHANNEL_STREAMID 0x706 +#define AC_VERB_SET_PIN_WIDGET_CONTROL 0x707 +#define AC_VERB_SET_UNSOLICITED_ENABLE 0x708 +#define AC_VERB_SET_PIN_SENSE 0x709 +#define AC_VERB_SET_BEEP_CONTROL 0x70a +#define AC_VERB_SET_EAPD_BTLENABLE 0x70c +#define AC_VERB_SET_DIGI_CONVERT_1 0x70d +#define AC_VERB_SET_DIGI_CONVERT_2 0x70e +#define AC_VERB_SET_VOLUME_KNOB_CONTROL 0x70f +#define AC_VERB_SET_GPIO_DATA 0x715 +#define AC_VERB_SET_GPIO_MASK 0x716 +#define AC_VERB_SET_GPIO_DIRECTION 0x717 +#define AC_VERB_SET_GPIO_WAKE_MASK 0x718 +#define AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK 0x719 +#define AC_VERB_SET_GPIO_STICKY_MASK 0x71a +#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_0 0x71c +#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_1 0x71d +#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_2 0x71e +#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_3 0x71f +#define AC_VERB_SET_EAPD 0x788 +#define AC_VERB_SET_CODEC_RESET 0x7ff +#define AC_VERB_SET_CVT_CHAN_COUNT 0x72d +#define AC_VERB_SET_HDMI_DIP_INDEX 0x730 +#define AC_VERB_SET_HDMI_DIP_DATA 0x731 +#define AC_VERB_SET_HDMI_DIP_XMIT 0x732 +#define AC_VERB_SET_HDMI_CP_CTRL 0x733 +#define AC_VERB_SET_HDMI_CHAN_SLOT 0x734 + +/* + * Parameter IDs + */ +#define AC_PAR_VENDOR_ID 0x00 +#define AC_PAR_SUBSYSTEM_ID 0x01 +#define AC_PAR_REV_ID 0x02 +#define AC_PAR_NODE_COUNT 0x04 +#define AC_PAR_FUNCTION_TYPE 0x05 +#define AC_PAR_AUDIO_FG_CAP 0x08 +#define AC_PAR_AUDIO_WIDGET_CAP 0x09 +#define AC_PAR_PCM 0x0a +#define AC_PAR_STREAM 0x0b +#define AC_PAR_PIN_CAP 0x0c +#define AC_PAR_AMP_IN_CAP 0x0d +#define AC_PAR_CONNLIST_LEN 0x0e +#define AC_PAR_POWER_STATE 0x0f +#define AC_PAR_PROC_CAP 0x10 +#define AC_PAR_GPIO_CAP 0x11 +#define AC_PAR_AMP_OUT_CAP 0x12 +#define AC_PAR_VOL_KNB_CAP 0x13 +#define AC_PAR_HDMI_LPCM_CAP 0x20 + +/* + * AC_VERB_PARAMETERS results (32bit) + */ + +/* Function Group Type */ +#define AC_FGT_TYPE (0xff<<0) +#define AC_FGT_TYPE_SHIFT 0 +#define AC_FGT_UNSOL_CAP (1<<8) + +/* Audio Function Group Capabilities */ +#define AC_AFG_OUT_DELAY (0xf<<0) +#define AC_AFG_IN_DELAY (0xf<<8) +#define AC_AFG_BEEP_GEN (1<<16) + +/* Audio Widget Capabilities */ +#define AC_WCAP_STEREO (1<<0) /* stereo I/O */ +#define AC_WCAP_IN_AMP (1<<1) /* AMP-in present */ +#define AC_WCAP_OUT_AMP (1<<2) /* AMP-out present */ +#define AC_WCAP_AMP_OVRD (1<<3) /* AMP-parameter override */ +#define AC_WCAP_FORMAT_OVRD (1<<4) /* format override */ +#define AC_WCAP_STRIPE (1<<5) /* stripe */ +#define AC_WCAP_PROC_WID (1<<6) /* Proc Widget */ +#define AC_WCAP_UNSOL_CAP (1<<7) /* Unsol capable */ +#define AC_WCAP_CONN_LIST (1<<8) /* connection list */ +#define AC_WCAP_DIGITAL (1<<9) /* digital I/O */ +#define AC_WCAP_POWER (1<<10) /* power control */ +#define AC_WCAP_LR_SWAP (1<<11) /* L/R swap */ +#define AC_WCAP_CP_CAPS (1<<12) /* content protection */ +#define AC_WCAP_CHAN_CNT_EXT (7<<13) /* channel count ext */ +#define AC_WCAP_DELAY (0xf<<16) +#define AC_WCAP_DELAY_SHIFT 16 +#define AC_WCAP_TYPE (0xf<<20) +#define AC_WCAP_TYPE_SHIFT 20 + +/* supported PCM rates and bits */ +#define AC_SUPPCM_RATES (0xfff << 0) +#define AC_SUPPCM_BITS_8 (1<<16) +#define AC_SUPPCM_BITS_16 (1<<17) +#define AC_SUPPCM_BITS_20 (1<<18) +#define AC_SUPPCM_BITS_24 (1<<19) +#define AC_SUPPCM_BITS_32 (1<<20) + +/* supported PCM stream format */ +#define AC_SUPFMT_PCM (1<<0) +#define AC_SUPFMT_FLOAT32 (1<<1) +#define AC_SUPFMT_AC3 (1<<2) + +/* GP I/O count */ +#define AC_GPIO_IO_COUNT (0xff<<0) +#define AC_GPIO_O_COUNT (0xff<<8) +#define AC_GPIO_O_COUNT_SHIFT 8 +#define AC_GPIO_I_COUNT (0xff<<16) +#define AC_GPIO_I_COUNT_SHIFT 16 +#define AC_GPIO_UNSOLICITED (1<<30) +#define AC_GPIO_WAKE (1<<31) + +/* Converter stream, channel */ +#define AC_CONV_CHANNEL (0xf<<0) +#define AC_CONV_STREAM (0xf<<4) +#define AC_CONV_STREAM_SHIFT 4 + +/* Input converter SDI select */ +#define AC_SDI_SELECT (0xf<<0) + +/* stream format id */ +#define AC_FMT_CHAN_SHIFT 0 +#define AC_FMT_CHAN_MASK (0x0f << 0) +#define AC_FMT_BITS_SHIFT 4 +#define AC_FMT_BITS_MASK (7 << 4) +#define AC_FMT_BITS_8 (0 << 4) +#define AC_FMT_BITS_16 (1 << 4) +#define AC_FMT_BITS_20 (2 << 4) +#define AC_FMT_BITS_24 (3 << 4) +#define AC_FMT_BITS_32 (4 << 4) +#define AC_FMT_DIV_SHIFT 8 +#define AC_FMT_DIV_MASK (7 << 8) +#define AC_FMT_MULT_SHIFT 11 +#define AC_FMT_MULT_MASK (7 << 11) +#define AC_FMT_BASE_SHIFT 14 +#define AC_FMT_BASE_48K (0 << 14) +#define AC_FMT_BASE_44K (1 << 14) +#define AC_FMT_TYPE_SHIFT 15 +#define AC_FMT_TYPE_PCM (0 << 15) +#define AC_FMT_TYPE_NON_PCM (1 << 15) + +/* Unsolicited response control */ +#define AC_UNSOL_TAG (0x3f<<0) +#define AC_UNSOL_ENABLED (1<<7) +#define AC_USRSP_EN AC_UNSOL_ENABLED + +/* Unsolicited responses */ +#define AC_UNSOL_RES_TAG (0x3f<<26) +#define AC_UNSOL_RES_TAG_SHIFT 26 +#define AC_UNSOL_RES_SUBTAG (0x1f<<21) +#define AC_UNSOL_RES_SUBTAG_SHIFT 21 +#define AC_UNSOL_RES_ELDV (1<<1) /* ELD Data valid (for HDMI) */ +#define AC_UNSOL_RES_PD (1<<0) /* pinsense detect */ +#define AC_UNSOL_RES_CP_STATE (1<<1) /* content protection */ +#define AC_UNSOL_RES_CP_READY (1<<0) /* content protection */ + +/* Pin widget capabilies */ +#define AC_PINCAP_IMP_SENSE (1<<0) /* impedance sense capable */ +#define AC_PINCAP_TRIG_REQ (1<<1) /* trigger required */ +#define AC_PINCAP_PRES_DETECT (1<<2) /* presence detect capable */ +#define AC_PINCAP_HP_DRV (1<<3) /* headphone drive capable */ +#define AC_PINCAP_OUT (1<<4) /* output capable */ +#define AC_PINCAP_IN (1<<5) /* input capable */ +#define AC_PINCAP_BALANCE (1<<6) /* balanced I/O capable */ +/* Note: This LR_SWAP pincap is defined in the Realtek ALC883 specification, + * but is marked reserved in the Intel HDA specification. + */ +#define AC_PINCAP_LR_SWAP (1<<7) /* L/R swap */ +/* Note: The same bit as LR_SWAP is newly defined as HDMI capability + * in HD-audio specification + */ +#define AC_PINCAP_HDMI (1<<7) /* HDMI pin */ +#define AC_PINCAP_DP (1<<24) /* DisplayPort pin, can + * coexist with AC_PINCAP_HDMI + */ +#define AC_PINCAP_VREF (0x37<<8) +#define AC_PINCAP_VREF_SHIFT 8 +#define AC_PINCAP_EAPD (1<<16) /* EAPD capable */ +#define AC_PINCAP_HBR (1<<27) /* High Bit Rate */ +/* Vref status (used in pin cap) */ +#define AC_PINCAP_VREF_HIZ (1<<0) /* Hi-Z */ +#define AC_PINCAP_VREF_50 (1<<1) /* 50% */ +#define AC_PINCAP_VREF_GRD (1<<2) /* ground */ +#define AC_PINCAP_VREF_80 (1<<4) /* 80% */ +#define AC_PINCAP_VREF_100 (1<<5) /* 100% */ + +/* Amplifier capabilities */ +#define AC_AMPCAP_OFFSET (0x7f<<0) /* 0dB offset */ +#define AC_AMPCAP_OFFSET_SHIFT 0 +#define AC_AMPCAP_NUM_STEPS (0x7f<<8) /* number of steps */ +#define AC_AMPCAP_NUM_STEPS_SHIFT 8 +#define AC_AMPCAP_STEP_SIZE (0x7f<<16) /* step size 0-32dB + * in 0.25dB + */ +#define AC_AMPCAP_STEP_SIZE_SHIFT 16 +#define AC_AMPCAP_MUTE (1<<31) /* mute capable */ +#define AC_AMPCAP_MUTE_SHIFT 31 + +/* Connection list */ +#define AC_CLIST_LENGTH (0x7f<<0) +#define AC_CLIST_LONG (1<<7) + +/* Supported power status */ +#define AC_PWRST_D0SUP (1<<0) +#define AC_PWRST_D1SUP (1<<1) +#define AC_PWRST_D2SUP (1<<2) +#define AC_PWRST_D3SUP (1<<3) +#define AC_PWRST_D3COLDSUP (1<<4) +#define AC_PWRST_S3D3COLDSUP (1<<29) +#define AC_PWRST_CLKSTOP (1<<30) +#define AC_PWRST_EPSS (1U<<31) + +/* Power state values */ +#define AC_PWRST_SETTING (0xf<<0) +#define AC_PWRST_ACTUAL (0xf<<4) +#define AC_PWRST_ACTUAL_SHIFT 4 +#define AC_PWRST_D0 0x00 +#define AC_PWRST_D1 0x01 +#define AC_PWRST_D2 0x02 +#define AC_PWRST_D3 0x03 + +/* Processing capabilies */ +#define AC_PCAP_BENIGN (1<<0) +#define AC_PCAP_NUM_COEF (0xff<<8) +#define AC_PCAP_NUM_COEF_SHIFT 8 + +/* Volume knobs capabilities */ +#define AC_KNBCAP_NUM_STEPS (0x7f<<0) +#define AC_KNBCAP_DELTA (1<<7) + +/* HDMI LPCM capabilities */ +#define AC_LPCMCAP_48K_CP_CHNS (0x0f<<0) /* max channels w/ CP-on */ +#define AC_LPCMCAP_48K_NO_CHNS (0x0f<<4) /* max channels w/o CP-on */ +#define AC_LPCMCAP_48K_20BIT (1<<8) /* 20b bitrate supported */ +#define AC_LPCMCAP_48K_24BIT (1<<9) /* 24b bitrate supported */ +#define AC_LPCMCAP_96K_CP_CHNS (0x0f<<10) /* max channels w/ CP-on */ +#define AC_LPCMCAP_96K_NO_CHNS (0x0f<<14) /* max channels w/o CP-on */ +#define AC_LPCMCAP_96K_20BIT (1<<18) /* 20b bitrate supported */ +#define AC_LPCMCAP_96K_24BIT (1<<19) /* 24b bitrate supported */ +#define AC_LPCMCAP_192K_CP_CHNS (0x0f<<20) /* max channels w/ CP-on */ +#define AC_LPCMCAP_192K_NO_CHNS (0x0f<<24) /* max channels w/o CP-on */ +#define AC_LPCMCAP_192K_20BIT (1<<28) /* 20b bitrate supported */ +#define AC_LPCMCAP_192K_24BIT (1<<29) /* 24b bitrate supported */ +#define AC_LPCMCAP_44K (1<<30) /* 44.1kHz support */ +#define AC_LPCMCAP_44K_MS (1<<31) /* 44.1kHz-multiplies support */ + +/* + * Control Parameters + */ + +/* Amp gain/mute */ +#define AC_AMP_MUTE (1<<7) +#define AC_AMP_GAIN (0x7f) +#define AC_AMP_GET_INDEX (0xf<<0) + +#define AC_AMP_GET_LEFT (1<<13) +#define AC_AMP_GET_RIGHT (0<<13) +#define AC_AMP_GET_OUTPUT (1<<15) +#define AC_AMP_GET_INPUT (0<<15) + +#define AC_AMP_SET_INDEX (0xf<<8) +#define AC_AMP_SET_INDEX_SHIFT 8 +#define AC_AMP_SET_RIGHT (1<<12) +#define AC_AMP_SET_LEFT (1<<13) +#define AC_AMP_SET_INPUT (1<<14) +#define AC_AMP_SET_OUTPUT (1<<15) + +/* DIGITAL1 bits */ +#define AC_DIG1_ENABLE (1<<0) +#define AC_DIG1_V (1<<1) +#define AC_DIG1_VCFG (1<<2) +#define AC_DIG1_EMPHASIS (1<<3) +#define AC_DIG1_COPYRIGHT (1<<4) +#define AC_DIG1_NONAUDIO (1<<5) +#define AC_DIG1_PROFESSIONAL (1<<6) +#define AC_DIG1_LEVEL (1<<7) + +/* DIGITAL2 bits */ +#define AC_DIG2_CC (0x7f<<0) + +/* Pin widget control - 8bit */ +#define AC_PINCTL_EPT (0x3<<0) +#define AC_PINCTL_EPT_NATIVE 0 +#define AC_PINCTL_EPT_HBR 3 +#define AC_PINCTL_VREFEN (0x7<<0) +#define AC_PINCTL_VREF_HIZ 0 /* Hi-Z */ +#define AC_PINCTL_VREF_50 1 /* 50% */ +#define AC_PINCTL_VREF_GRD 2 /* ground */ +#define AC_PINCTL_VREF_80 4 /* 80% */ +#define AC_PINCTL_VREF_100 5 /* 100% */ +#define AC_PINCTL_IN_EN (1<<5) +#define AC_PINCTL_OUT_EN (1<<6) +#define AC_PINCTL_HP_EN (1<<7) + +/* Pin sense - 32bit */ +#define AC_PINSENSE_IMPEDANCE_MASK (0x7fffffff) +#define AC_PINSENSE_PRESENCE (1<<31) +#define AC_PINSENSE_ELDV (1<<30) /* ELD valid (HDMI) */ + +/* EAPD/BTL enable - 32bit */ +#define AC_EAPDBTL_BALANCED (1<<0) +#define AC_EAPDBTL_EAPD (1<<1) +#define AC_EAPDBTL_LR_SWAP (1<<2) + +/* HDMI ELD data */ +#define AC_ELDD_ELD_VALID (1<<31) +#define AC_ELDD_ELD_DATA 0xff + +/* HDMI DIP size */ +#define AC_DIPSIZE_ELD_BUF (1<<3) /* ELD buf size of packet size */ +#define AC_DIPSIZE_PACK_IDX (0x07<<0) /* packet index */ + +/* HDMI DIP index */ +#define AC_DIPIDX_PACK_IDX (0x07<<5) /* packet idnex */ +#define AC_DIPIDX_BYTE_IDX (0x1f<<0) /* byte index */ + +/* HDMI DIP xmit (transmit) control */ +#define AC_DIPXMIT_MASK (0x3<<6) +#define AC_DIPXMIT_DISABLE (0x0<<6) /* disable xmit */ +#define AC_DIPXMIT_ONCE (0x2<<6) /* xmit once then disable */ +#define AC_DIPXMIT_BEST (0x3<<6) /* best effort */ + +/* HDMI content protection (CP) control */ +#define AC_CPCTRL_CES (1<<9) /* current encryption state */ +#define AC_CPCTRL_READY (1<<8) /* ready bit */ +#define AC_CPCTRL_SUBTAG (0x1f<<3) /* subtag for unsol-resp */ +#define AC_CPCTRL_STATE (3<<0) /* current CP request state */ + +/* Converter channel <-> HDMI slot mapping */ +#define AC_CVTMAP_HDMI_SLOT (0xf<<0) /* HDMI slot number */ +#define AC_CVTMAP_CHAN (0xf<<4) /* converter channel number */ + +/* configuration default - 32bit */ +#define AC_DEFCFG_SEQUENCE (0xf<<0) +#define AC_DEFCFG_DEF_ASSOC (0xf<<4) +#define AC_DEFCFG_ASSOC_SHIFT 4 +#define AC_DEFCFG_MISC (0xf<<8) +#define AC_DEFCFG_MISC_SHIFT 8 +#define AC_DEFCFG_MISC_NO_PRESENCE (1<<0) +#define AC_DEFCFG_COLOR (0xf<<12) +#define AC_DEFCFG_COLOR_SHIFT 12 +#define AC_DEFCFG_CONN_TYPE (0xf<<16) +#define AC_DEFCFG_CONN_TYPE_SHIFT 16 +#define AC_DEFCFG_DEVICE (0xf<<20) +#define AC_DEFCFG_DEVICE_SHIFT 20 +#define AC_DEFCFG_LOCATION (0x3f<<24) +#define AC_DEFCFG_LOCATION_SHIFT 24 +#define AC_DEFCFG_PORT_CONN (0x3<<30) +#define AC_DEFCFG_PORT_CONN_SHIFT 30 + +/* device device types (0x0-0xf) */ +enum { + AC_JACK_LINE_OUT, + AC_JACK_SPEAKER, + AC_JACK_HP_OUT, + AC_JACK_CD, + AC_JACK_SPDIF_OUT, + AC_JACK_DIG_OTHER_OUT, + AC_JACK_MODEM_LINE_SIDE, + AC_JACK_MODEM_HAND_SIDE, + AC_JACK_LINE_IN, + AC_JACK_AUX, + AC_JACK_MIC_IN, + AC_JACK_TELEPHONY, + AC_JACK_SPDIF_IN, + AC_JACK_DIG_OTHER_IN, + AC_JACK_OTHER = 0xf, +}; + +/* jack connection types (0x0-0xf) */ +enum { + AC_JACK_CONN_UNKNOWN, + AC_JACK_CONN_1_8, + AC_JACK_CONN_1_4, + AC_JACK_CONN_ATAPI, + AC_JACK_CONN_RCA, + AC_JACK_CONN_OPTICAL, + AC_JACK_CONN_OTHER_DIGITAL, + AC_JACK_CONN_OTHER_ANALOG, + AC_JACK_CONN_DIN, + AC_JACK_CONN_XLR, + AC_JACK_CONN_RJ11, + AC_JACK_CONN_COMB, + AC_JACK_CONN_OTHER = 0xf, +}; + +/* jack colors (0x0-0xf) */ +enum { + AC_JACK_COLOR_UNKNOWN, + AC_JACK_COLOR_BLACK, + AC_JACK_COLOR_GREY, + AC_JACK_COLOR_BLUE, + AC_JACK_COLOR_GREEN, + AC_JACK_COLOR_RED, + AC_JACK_COLOR_ORANGE, + AC_JACK_COLOR_YELLOW, + AC_JACK_COLOR_PURPLE, + AC_JACK_COLOR_PINK, + AC_JACK_COLOR_WHITE = 0xe, + AC_JACK_COLOR_OTHER, +}; + +/* Jack location (0x0-0x3f) */ +/* common case */ +enum { + AC_JACK_LOC_NONE, + AC_JACK_LOC_REAR, + AC_JACK_LOC_FRONT, + AC_JACK_LOC_LEFT, + AC_JACK_LOC_RIGHT, + AC_JACK_LOC_TOP, + AC_JACK_LOC_BOTTOM, +}; +/* bits 4-5 */ +enum { + AC_JACK_LOC_EXTERNAL = 0x00, + AC_JACK_LOC_INTERNAL = 0x10, + AC_JACK_LOC_SEPARATE = 0x20, + AC_JACK_LOC_OTHER = 0x30, +}; +enum { + /* external on primary chasis */ + AC_JACK_LOC_REAR_PANEL = 0x07, + AC_JACK_LOC_DRIVE_BAY, + /* internal */ + AC_JACK_LOC_RISER = 0x17, + AC_JACK_LOC_HDMI, + AC_JACK_LOC_ATAPI, + /* others */ + AC_JACK_LOC_MOBILE_IN = 0x37, + AC_JACK_LOC_MOBILE_OUT, +}; + +/* Port connectivity (0-3) */ +enum { + AC_JACK_PORT_COMPLEX, + AC_JACK_PORT_NONE, + AC_JACK_PORT_FIXED, + AC_JACK_PORT_BOTH, +}; + +/* max. connections to a widget */ +#define HDA_MAX_CONNECTIONS 32 + +/* max. codec address */ +#define HDA_MAX_CODEC_ADDRESS 0x0f + +/* max number of PCM devics per card */ +#define HDA_MAX_PCMS 10 + +/* --------------------------------------------------------------------- */ + +#endif diff -Nru qemu-kvm-0.12.5+noroms/hw/intel-hda.h qemu-kvm-0.14.1/hw/intel-hda.h --- qemu-kvm-0.12.5+noroms/hw/intel-hda.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/intel-hda.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,62 @@ +#ifndef HW_INTEL_HDA_H +#define HW_INTEL_HDA_H + +#include "qdev.h" + +/* --------------------------------------------------------------------- */ +/* hda bus */ + +typedef struct HDACodecBus HDACodecBus; +typedef struct HDACodecDevice HDACodecDevice; +typedef struct HDACodecDeviceInfo HDACodecDeviceInfo; + +typedef void (*hda_codec_response_func)(HDACodecDevice *dev, + bool solicited, uint32_t response); +typedef bool (*hda_codec_xfer_func)(HDACodecDevice *dev, + uint32_t stnr, bool output, + uint8_t *buf, uint32_t len); + +struct HDACodecBus { + BusState qbus; + uint32_t next_cad; + hda_codec_response_func response; + hda_codec_xfer_func xfer; +}; + +struct HDACodecDevice { + DeviceState qdev; + HDACodecDeviceInfo *info; + uint32_t cad; /* codec address */ +}; + +struct HDACodecDeviceInfo { + DeviceInfo qdev; + int (*init)(HDACodecDevice *dev); + int (*exit)(HDACodecDevice *dev); + void (*command)(HDACodecDevice *dev, uint32_t nid, uint32_t data); + void (*stream)(HDACodecDevice *dev, uint32_t stnr, bool running); +}; + +void hda_codec_bus_init(DeviceState *dev, HDACodecBus *bus, + hda_codec_response_func response, + hda_codec_xfer_func xfer); +void hda_codec_register(HDACodecDeviceInfo *info); +HDACodecDevice *hda_codec_find(HDACodecBus *bus, uint32_t cad); + +void hda_codec_response(HDACodecDevice *dev, bool solicited, uint32_t response); +bool hda_codec_xfer(HDACodecDevice *dev, uint32_t stnr, bool output, + uint8_t *buf, uint32_t len); + +/* --------------------------------------------------------------------- */ + +#define dprint(_dev, _level, _fmt, ...) \ + do { \ + if (_dev->debug >= _level) { \ + fprintf(stderr, "%s: ", _dev->name); \ + fprintf(stderr, _fmt, ## __VA_ARGS__); \ + } \ + } while (0) + +/* --------------------------------------------------------------------- */ + +#endif diff -Nru qemu-kvm-0.12.5+noroms/hw/ioapic.c qemu-kvm-0.14.1/hw/ioapic.c --- qemu-kvm-0.12.5+noroms/hw/ioapic.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/ioapic.c 2011-05-11 13:29:46.000000000 +0000 @@ -22,39 +22,84 @@ #include "hw.h" #include "pc.h" +#include "apic.h" #include "sysemu.h" +#include "apic.h" +#include "ioapic.h" #include "qemu-timer.h" #include "host-utils.h" +#include "sysbus.h" -#include "qemu-kvm.h" +#include "kvm.h" //#define DEBUG_IOAPIC -#define IOAPIC_NUM_PINS 0x18 +#ifdef DEBUG_IOAPIC +#define DPRINTF(fmt, ...) \ + do { printf("ioapic: " fmt , ## __VA_ARGS__); } while (0) +#else +#define DPRINTF(fmt, ...) +#endif + #define IOAPIC_DEFAULT_BASE_ADDRESS 0xfec00000 -#define IOAPIC_LVT_MASKED (1<<16) +#define MAX_IOAPICS 1 + +#define IOAPIC_VERSION 0x11 -#define IOAPIC_TRIGGER_EDGE 0 -#define IOAPIC_TRIGGER_LEVEL 1 +#define IOAPIC_LVT_DEST_SHIFT 56 +#define IOAPIC_LVT_MASKED_SHIFT 16 +#define IOAPIC_LVT_TRIGGER_MODE_SHIFT 15 +#define IOAPIC_LVT_REMOTE_IRR_SHIFT 14 +#define IOAPIC_LVT_POLARITY_SHIFT 13 +#define IOAPIC_LVT_DELIV_STATUS_SHIFT 12 +#define IOAPIC_LVT_DEST_MODE_SHIFT 11 +#define IOAPIC_LVT_DELIV_MODE_SHIFT 8 + +#define IOAPIC_LVT_MASKED (1 << IOAPIC_LVT_MASKED_SHIFT) +#define IOAPIC_LVT_REMOTE_IRR (1 << IOAPIC_LVT_REMOTE_IRR_SHIFT) + +#define IOAPIC_TRIGGER_EDGE 0 +#define IOAPIC_TRIGGER_LEVEL 1 /*io{apic,sapic} delivery mode*/ -#define IOAPIC_DM_FIXED 0x0 -#define IOAPIC_DM_LOWEST_PRIORITY 0x1 -#define IOAPIC_DM_PMI 0x2 -#define IOAPIC_DM_NMI 0x4 -#define IOAPIC_DM_INIT 0x5 -#define IOAPIC_DM_SIPI 0x5 -#define IOAPIC_DM_EXTINT 0x7 +#define IOAPIC_DM_FIXED 0x0 +#define IOAPIC_DM_LOWEST_PRIORITY 0x1 +#define IOAPIC_DM_PMI 0x2 +#define IOAPIC_DM_NMI 0x4 +#define IOAPIC_DM_INIT 0x5 +#define IOAPIC_DM_SIPI 0x6 +#define IOAPIC_DM_EXTINT 0x7 +#define IOAPIC_DM_MASK 0x7 + +#define IOAPIC_VECTOR_MASK 0xff + +#define IOAPIC_IOREGSEL 0x00 +#define IOAPIC_IOWIN 0x10 + +#define IOAPIC_REG_ID 0x00 +#define IOAPIC_REG_VER 0x01 +#define IOAPIC_REG_ARB 0x02 +#define IOAPIC_REG_REDTBL_BASE 0x10 +#define IOAPIC_ID 0x00 + +#define IOAPIC_ID_SHIFT 24 +#define IOAPIC_ID_MASK 0xf + +#define IOAPIC_VER_ENTRIES_SHIFT 16 + +typedef struct IOAPICState IOAPICState; struct IOAPICState { + SysBusDevice busdev; uint8_t id; uint8_t ioregsel; uint64_t base_address; - uint32_t irr; uint64_t ioredtbl[IOAPIC_NUM_PINS]; }; +static IOAPICState *ioapics[MAX_IOAPICS]; + static void ioapic_service(IOAPICState *s) { uint8_t i; @@ -72,18 +117,22 @@ if (s->irr & mask) { entry = s->ioredtbl[i]; if (!(entry & IOAPIC_LVT_MASKED)) { - trig_mode = ((entry >> 15) & 1); - dest = entry >> 56; - dest_mode = (entry >> 11) & 1; - delivery_mode = (entry >> 8) & 7; - polarity = (entry >> 13) & 1; - if (trig_mode == IOAPIC_TRIGGER_EDGE) + trig_mode = ((entry >> IOAPIC_LVT_TRIGGER_MODE_SHIFT) & 1); + dest = entry >> IOAPIC_LVT_DEST_SHIFT; + dest_mode = (entry >> IOAPIC_LVT_DEST_MODE_SHIFT) & 1; + delivery_mode = + (entry >> IOAPIC_LVT_DELIV_MODE_SHIFT) & IOAPIC_DM_MASK; + polarity = (entry >> IOAPIC_LVT_POLARITY_SHIFT) & 1; + if (trig_mode == IOAPIC_TRIGGER_EDGE) { s->irr &= ~mask; - if (delivery_mode == IOAPIC_DM_EXTINT) + } else { + s->ioredtbl[i] |= IOAPIC_LVT_REMOTE_IRR; + } + if (delivery_mode == IOAPIC_DM_EXTINT) { vector = pic_read_irq(isa_pic); - else - vector = entry & 0xff; - + } else { + vector = entry & IOAPIC_VECTOR_MASK; + } apic_deliver_irq(dest, dest_mode, delivery_mode, vector, polarity, trig_mode); } @@ -91,7 +140,7 @@ } } -void ioapic_set_irq(void *opaque, int vector, int level) +static void ioapic_set_irq(void *opaque, int vector, int level) { IOAPICState *s = opaque; @@ -99,15 +148,16 @@ * to GSI 2. GSI maps to ioapic 1-1. This is not * the cleanest way of doing it but it should work. */ + DPRINTF("%s: %s vec %x\n", __func__, level ? "raise" : "lower", vector); if (vector == 0 && irq0override) { vector = 2; } - if (vector >= 0 && vector < IOAPIC_NUM_PINS) { uint32_t mask = 1 << vector; uint64_t entry = s->ioredtbl[vector]; - if ((entry >> 15) & 1) { + if (((entry >> IOAPIC_LVT_TRIGGER_MODE_SHIFT) & 1) == + IOAPIC_TRIGGER_LEVEL) { /* level triggered */ if (level) { s->irr |= mask; @@ -125,75 +175,101 @@ } } +void ioapic_eoi_broadcast(int vector) +{ + IOAPICState *s; + uint64_t entry; + int i, n; + + for (i = 0; i < MAX_IOAPICS; i++) { + s = ioapics[i]; + if (!s) { + continue; + } + for (n = 0; n < IOAPIC_NUM_PINS; n++) { + entry = s->ioredtbl[n]; + if ((entry & IOAPIC_LVT_REMOTE_IRR) + && (entry & IOAPIC_VECTOR_MASK) == vector) { + s->ioredtbl[n] = entry & ~IOAPIC_LVT_REMOTE_IRR; + if (!(entry & IOAPIC_LVT_MASKED) && (s->irr & (1 << n))) { + ioapic_service(s); + } + } + } + } +} + static uint32_t ioapic_mem_readl(void *opaque, target_phys_addr_t addr) { IOAPICState *s = opaque; int index; uint32_t val = 0; - addr &= 0xff; - if (addr == 0x00) { + switch (addr & 0xff) { + case IOAPIC_IOREGSEL: val = s->ioregsel; - } else if (addr == 0x10) { + break; + case IOAPIC_IOWIN: switch (s->ioregsel) { - case 0x00: - val = s->id << 24; - break; - case 0x01: - val = 0x11 | ((IOAPIC_NUM_PINS - 1) << 16); /* version 0x11 */ - break; - case 0x02: - val = 0; - break; - default: - index = (s->ioregsel - 0x10) >> 1; - if (index >= 0 && index < IOAPIC_NUM_PINS) { - if (s->ioregsel & 1) - val = s->ioredtbl[index] >> 32; - else - val = s->ioredtbl[index] & 0xffffffff; + case IOAPIC_REG_ID: + val = s->id << IOAPIC_ID_SHIFT; + break; + case IOAPIC_REG_VER: + val = IOAPIC_VERSION | + ((IOAPIC_NUM_PINS - 1) << IOAPIC_VER_ENTRIES_SHIFT); + break; + case IOAPIC_REG_ARB: + val = 0; + break; + default: + index = (s->ioregsel - IOAPIC_REG_REDTBL_BASE) >> 1; + if (index >= 0 && index < IOAPIC_NUM_PINS) { + if (s->ioregsel & 1) { + val = s->ioredtbl[index] >> 32; + } else { + val = s->ioredtbl[index] & 0xffffffff; } + } } -#ifdef DEBUG_IOAPIC - printf("I/O APIC read: %08x = %08x\n", s->ioregsel, val); -#endif + DPRINTF("read: %08x = %08x\n", s->ioregsel, val); + break; } return val; } -static void ioapic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) +static void +ioapic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) { IOAPICState *s = opaque; int index; - addr &= 0xff; - if (addr == 0x00) { + switch (addr & 0xff) { + case IOAPIC_IOREGSEL: s->ioregsel = val; - return; - } else if (addr == 0x10) { -#ifdef DEBUG_IOAPIC - printf("I/O APIC write: %08x = %08x\n", s->ioregsel, val); -#endif + break; + case IOAPIC_IOWIN: + DPRINTF("write: %08x = %08x\n", s->ioregsel, val); switch (s->ioregsel) { - case 0x00: - s->id = (val >> 24) & 0xff; - return; - case 0x01: - case 0x02: - return; - default: - index = (s->ioregsel - 0x10) >> 1; - if (index >= 0 && index < IOAPIC_NUM_PINS) { - if (s->ioregsel & 1) { - s->ioredtbl[index] &= 0xffffffff; - s->ioredtbl[index] |= (uint64_t)val << 32; - } else { - s->ioredtbl[index] &= ~0xffffffffULL; - s->ioredtbl[index] |= val; - } - ioapic_service(s); + case IOAPIC_REG_ID: + s->id = (val >> IOAPIC_ID_SHIFT) & IOAPIC_ID_MASK; + break; + case IOAPIC_REG_VER: + case IOAPIC_REG_ARB: + break; + default: + index = (s->ioregsel - IOAPIC_REG_REDTBL_BASE) >> 1; + if (index >= 0 && index < IOAPIC_NUM_PINS) { + if (s->ioregsel & 1) { + s->ioredtbl[index] &= 0xffffffff; + s->ioredtbl[index] |= (uint64_t)val << 32; + } else { + s->ioredtbl[index] &= ~0xffffffffULL; + s->ioredtbl[index] |= val; } + ioapic_service(s); + } } + break; } } @@ -263,21 +339,27 @@ { IOAPICState *s = opaque; + if (version_id == 1) { + /* set sane value */ + s->irr = 0; + } + if (kvm_enabled() && kvm_irqchip_in_kernel()) { kvm_kernel_ioapic_load_from_user(s); } + return 0; } static const VMStateDescription vmstate_ioapic = { .name = "ioapic", - .version_id = 2, + .version_id = 3, + .post_load = ioapic_post_load, .minimum_version_id = 1, .minimum_version_id_old = 1, .pre_load = ioapic_pre_load, - .post_load = ioapic_post_load, .pre_save = ioapic_pre_save, - .fields = (VMStateField []) { + .fields = (VMStateField[]) { VMSTATE_UINT8(id, IOAPICState), VMSTATE_UINT8(ioregsel, IOAPICState), VMSTATE_UINT64_V(base_address, IOAPICState, 2), @@ -287,15 +369,18 @@ } }; -static void ioapic_reset(void *opaque) +static void ioapic_reset(DeviceState *d) { - IOAPICState *s = opaque; + IOAPICState *s = DO_UPCAST(IOAPICState, busdev.qdev, d); int i; - memset(s, 0, sizeof(*s)); s->base_address = IOAPIC_DEFAULT_BASE_ADDRESS; - for(i = 0; i < IOAPIC_NUM_PINS; i++) - s->ioredtbl[i] = 1 << 16; /* mask LVT */ + s->id = 0; + s->ioregsel = 0; + s->irr = 0; + for (i = 0; i < IOAPIC_NUM_PINS; i++) { + s->ioredtbl[i] = 1 << IOAPIC_LVT_MASKED_SHIFT; + } #ifdef KVM_CAP_IRQCHIP if (kvm_enabled() && kvm_irqchip_in_kernel()) { kvm_kernel_ioapic_load_from_user(s); @@ -315,22 +400,40 @@ ioapic_mem_writel, }; -qemu_irq *ioapic_init(void) +static int ioapic_init1(SysBusDevice *dev) { - IOAPICState *s; - qemu_irq *irq; + IOAPICState *s = FROM_SYSBUS(IOAPICState, dev); int io_memory; + static int ioapic_no; - s = qemu_mallocz(sizeof(IOAPICState)); - ioapic_reset(s); + if (ioapic_no >= MAX_IOAPICS) { + return -1; + } io_memory = cpu_register_io_memory(ioapic_mem_read, - ioapic_mem_write, s); - cpu_register_physical_memory(0xfec00000, 0x1000, io_memory); + ioapic_mem_write, s, + DEVICE_NATIVE_ENDIAN); + sysbus_init_mmio(dev, 0x1000, io_memory); + + qdev_init_gpio_in(&dev->qdev, ioapic_set_irq, IOAPIC_NUM_PINS); - vmstate_register(0, &vmstate_ioapic, s); - qemu_register_reset(ioapic_reset, s); - irq = qemu_allocate_irqs(ioapic_set_irq, s, IOAPIC_NUM_PINS); + ioapics[ioapic_no++] = s; - return irq; + return 0; } + +static SysBusDeviceInfo ioapic_info = { + .init = ioapic_init1, + .qdev.name = "ioapic", + .qdev.size = sizeof(IOAPICState), + .qdev.vmsd = &vmstate_ioapic, + .qdev.reset = ioapic_reset, + .qdev.no_user = 1, +}; + +static void ioapic_register_devices(void) +{ + sysbus_register_withprop(&ioapic_info); +} + +device_init(ioapic_register_devices) diff -Nru qemu-kvm-0.12.5+noroms/hw/ioapic.h qemu-kvm-0.14.1/hw/ioapic.h --- qemu-kvm-0.12.5+noroms/hw/ioapic.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/ioapic.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,20 @@ +/* + * ioapic.c IOAPIC emulation logic + * + * Copyright (c) 2011 Jan Kiszka, Siemens AG + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +void ioapic_eoi_broadcast(int vector); diff -Nru qemu-kvm-0.12.5+noroms/hw/ioh3420.c qemu-kvm-0.14.1/hw/ioh3420.c --- qemu-kvm-0.12.5+noroms/hw/ioh3420.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/ioh3420.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,246 @@ +/* + * ioh3420.c + * Intel X58 north bridge IOH + * PCI Express root port device id 3420 + * + * Copyright (c) 2010 Isaku Yamahata + * VA Linux Systems Japan K.K. + * + * 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, see . + */ + +#include "pci_ids.h" +#include "msi.h" +#include "pcie.h" +#include "ioh3420.h" + +#define PCI_DEVICE_ID_IOH_EPORT 0x3420 /* D0:F0 express mode */ +#define PCI_DEVICE_ID_IOH_REV 0x2 +#define IOH_EP_SSVID_OFFSET 0x40 +#define IOH_EP_SSVID_SVID PCI_VENDOR_ID_INTEL +#define IOH_EP_SSVID_SSID 0 +#define IOH_EP_MSI_OFFSET 0x60 +#define IOH_EP_MSI_SUPPORTED_FLAGS PCI_MSI_FLAGS_MASKBIT +#define IOH_EP_MSI_NR_VECTOR 2 +#define IOH_EP_EXP_OFFSET 0x90 +#define IOH_EP_AER_OFFSET 0x100 + +/* + * If two MSI vector are allocated, Advanced Error Interrupt Message Number + * is 1. otherwise 0. + * 17.12.5.10 RPERRSTS, 32:27 bit Advanced Error Interrupt Message Number. + */ +static uint8_t ioh3420_aer_vector(const PCIDevice *d) +{ + switch (msi_nr_vectors_allocated(d)) { + case 1: + return 0; + case 2: + return 1; + case 4: + case 8: + case 16: + case 32: + default: + break; + } + abort(); + return 0; +} + +static void ioh3420_aer_vector_update(PCIDevice *d) +{ + pcie_aer_root_set_vector(d, ioh3420_aer_vector(d)); +} + +static void ioh3420_write_config(PCIDevice *d, + uint32_t address, uint32_t val, int len) +{ + uint32_t root_cmd = + pci_get_long(d->config + d->exp.aer_cap + PCI_ERR_ROOT_COMMAND); + + pci_bridge_write_config(d, address, val, len); + msi_write_config(d, address, val, len); + ioh3420_aer_vector_update(d); + pcie_cap_slot_write_config(d, address, val, len); + pcie_aer_write_config(d, address, val, len); + pcie_aer_root_write_config(d, address, val, len, root_cmd); +} + +static void ioh3420_reset(DeviceState *qdev) +{ + PCIDevice *d = DO_UPCAST(PCIDevice, qdev, qdev); + msi_reset(d); + ioh3420_aer_vector_update(d); + pcie_cap_root_reset(d); + pcie_cap_deverr_reset(d); + pcie_cap_slot_reset(d); + pcie_aer_root_reset(d); + pci_bridge_reset(qdev); + pci_bridge_disable_base_limit(d); +} + +static int ioh3420_initfn(PCIDevice *d) +{ + PCIBridge* br = DO_UPCAST(PCIBridge, dev, d); + PCIEPort *p = DO_UPCAST(PCIEPort, br, br); + PCIESlot *s = DO_UPCAST(PCIESlot, port, p); + int rc; + int tmp; + + rc = pci_bridge_initfn(d); + if (rc < 0) { + return rc; + } + + d->config[PCI_REVISION_ID] = PCI_DEVICE_ID_IOH_REV; + pcie_port_init_reg(d); + + pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_INTEL); + pci_config_set_device_id(d->config, PCI_DEVICE_ID_IOH_EPORT); + + rc = pci_bridge_ssvid_init(d, IOH_EP_SSVID_OFFSET, + IOH_EP_SSVID_SVID, IOH_EP_SSVID_SSID); + if (rc < 0) { + goto err_bridge; + } + rc = msi_init(d, IOH_EP_MSI_OFFSET, IOH_EP_MSI_NR_VECTOR, + IOH_EP_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_64BIT, + IOH_EP_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT); + if (rc < 0) { + goto err_bridge; + } + rc = pcie_cap_init(d, IOH_EP_EXP_OFFSET, PCI_EXP_TYPE_ROOT_PORT, p->port); + if (rc < 0) { + goto err_msi; + } + pcie_cap_deverr_init(d); + pcie_cap_slot_init(d, s->slot); + pcie_chassis_create(s->chassis); + rc = pcie_chassis_add_slot(s); + if (rc < 0) { + goto err_pcie_cap; + return rc; + } + pcie_cap_root_init(d); + rc = pcie_aer_init(d, IOH_EP_AER_OFFSET); + if (rc < 0) { + goto err; + } + pcie_aer_root_init(d); + ioh3420_aer_vector_update(d); + return 0; + +err: + pcie_chassis_del_slot(s); +err_pcie_cap: + pcie_cap_exit(d); +err_msi: + msi_uninit(d); +err_bridge: + tmp = pci_bridge_exitfn(d); + assert(!tmp); + return rc; +} + +static int ioh3420_exitfn(PCIDevice *d) +{ + PCIBridge* br = DO_UPCAST(PCIBridge, dev, d); + PCIEPort *p = DO_UPCAST(PCIEPort, br, br); + PCIESlot *s = DO_UPCAST(PCIESlot, port, p); + + pcie_aer_exit(d); + pcie_chassis_del_slot(s); + pcie_cap_exit(d); + msi_uninit(d); + return pci_bridge_exitfn(d); +} + +PCIESlot *ioh3420_init(PCIBus *bus, int devfn, bool multifunction, + const char *bus_name, pci_map_irq_fn map_irq, + uint8_t port, uint8_t chassis, uint16_t slot) +{ + PCIDevice *d; + PCIBridge *br; + DeviceState *qdev; + + d = pci_create_multifunction(bus, devfn, multifunction, "ioh3420"); + if (!d) { + return NULL; + } + br = DO_UPCAST(PCIBridge, dev, d); + + qdev = &br->dev.qdev; + pci_bridge_map_irq(br, bus_name, map_irq); + qdev_prop_set_uint8(qdev, "port", port); + qdev_prop_set_uint8(qdev, "chassis", chassis); + qdev_prop_set_uint16(qdev, "slot", slot); + qdev_init_nofail(qdev); + + return DO_UPCAST(PCIESlot, port, DO_UPCAST(PCIEPort, br, br)); +} + +static const VMStateDescription vmstate_ioh3420 = { + .name = "ioh-3240-express-root-port", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .post_load = pcie_cap_slot_post_load, + .fields = (VMStateField[]) { + VMSTATE_PCIE_DEVICE(port.br.dev, PCIESlot), + VMSTATE_STRUCT(port.br.dev.exp.aer_log, PCIESlot, 0, + vmstate_pcie_aer_log, PCIEAERLog), + VMSTATE_END_OF_LIST() + } +}; + +static PCIDeviceInfo ioh3420_info = { + .qdev.name = "ioh3420", + .qdev.desc = "Intel IOH device id 3420 PCIE Root Port", + .qdev.size = sizeof(PCIESlot), + .qdev.reset = ioh3420_reset, + .qdev.vmsd = &vmstate_ioh3420, + + .is_express = 1, + .is_bridge = 1, + .config_write = ioh3420_write_config, + .init = ioh3420_initfn, + .exit = ioh3420_exitfn, + + .qdev.props = (Property[]) { + DEFINE_PROP_UINT8("port", PCIESlot, port.port, 0), + DEFINE_PROP_UINT8("chassis", PCIESlot, chassis, 0), + DEFINE_PROP_UINT16("slot", PCIESlot, slot, 0), + DEFINE_PROP_UINT16("aer_log_max", PCIESlot, + port.br.dev.exp.aer_log.log_max, + PCIE_AER_LOG_MAX_DEFAULT), + DEFINE_PROP_END_OF_LIST(), + } +}; + +static void ioh3420_register(void) +{ + pci_qdev_register(&ioh3420_info); +} + +device_init(ioh3420_register); + +/* + * Local variables: + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 8 + * indent-tab-mode: nil + * End: + */ diff -Nru qemu-kvm-0.12.5+noroms/hw/ioh3420.h qemu-kvm-0.14.1/hw/ioh3420.h --- qemu-kvm-0.12.5+noroms/hw/ioh3420.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/ioh3420.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,10 @@ +#ifndef QEMU_IOH3420_H +#define QEMU_IOH3420_H + +#include "pcie_port.h" + +PCIESlot *ioh3420_init(PCIBus *bus, int devfn, bool multifunction, + const char *bus_name, pci_map_irq_fn map_irq, + uint8_t port, uint8_t chassis, uint16_t slot); + +#endif /* QEMU_IOH3420_H */ diff -Nru qemu-kvm-0.12.5+noroms/hw/iommu.c qemu-kvm-0.14.1/hw/iommu.c --- qemu-kvm-0.12.5+noroms/hw/iommu.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/iommu.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,386 +0,0 @@ -/* - * QEMU SPARC iommu emulation - * - * Copyright (c) 2003-2005 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "sun4m.h" -#include "sysbus.h" - -/* debug iommu */ -//#define DEBUG_IOMMU - -#ifdef DEBUG_IOMMU -#define DPRINTF(fmt, ...) \ - do { printf("IOMMU: " fmt , ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) -#endif - -#define IOMMU_NREGS (4*4096/4) -#define IOMMU_CTRL (0x0000 >> 2) -#define IOMMU_CTRL_IMPL 0xf0000000 /* Implementation */ -#define IOMMU_CTRL_VERS 0x0f000000 /* Version */ -#define IOMMU_CTRL_RNGE 0x0000001c /* Mapping RANGE */ -#define IOMMU_RNGE_16MB 0x00000000 /* 0xff000000 -> 0xffffffff */ -#define IOMMU_RNGE_32MB 0x00000004 /* 0xfe000000 -> 0xffffffff */ -#define IOMMU_RNGE_64MB 0x00000008 /* 0xfc000000 -> 0xffffffff */ -#define IOMMU_RNGE_128MB 0x0000000c /* 0xf8000000 -> 0xffffffff */ -#define IOMMU_RNGE_256MB 0x00000010 /* 0xf0000000 -> 0xffffffff */ -#define IOMMU_RNGE_512MB 0x00000014 /* 0xe0000000 -> 0xffffffff */ -#define IOMMU_RNGE_1GB 0x00000018 /* 0xc0000000 -> 0xffffffff */ -#define IOMMU_RNGE_2GB 0x0000001c /* 0x80000000 -> 0xffffffff */ -#define IOMMU_CTRL_ENAB 0x00000001 /* IOMMU Enable */ -#define IOMMU_CTRL_MASK 0x0000001d - -#define IOMMU_BASE (0x0004 >> 2) -#define IOMMU_BASE_MASK 0x07fffc00 - -#define IOMMU_TLBFLUSH (0x0014 >> 2) -#define IOMMU_TLBFLUSH_MASK 0xffffffff - -#define IOMMU_PGFLUSH (0x0018 >> 2) -#define IOMMU_PGFLUSH_MASK 0xffffffff - -#define IOMMU_AFSR (0x1000 >> 2) -#define IOMMU_AFSR_ERR 0x80000000 /* LE, TO, or BE asserted */ -#define IOMMU_AFSR_LE 0x40000000 /* SBUS reports error after - transaction */ -#define IOMMU_AFSR_TO 0x20000000 /* Write access took more than - 12.8 us. */ -#define IOMMU_AFSR_BE 0x10000000 /* Write access received error - acknowledge */ -#define IOMMU_AFSR_SIZE 0x0e000000 /* Size of transaction causing error */ -#define IOMMU_AFSR_S 0x01000000 /* Sparc was in supervisor mode */ -#define IOMMU_AFSR_RESV 0x00800000 /* Reserved, forced to 0x8 by - hardware */ -#define IOMMU_AFSR_ME 0x00080000 /* Multiple errors occurred */ -#define IOMMU_AFSR_RD 0x00040000 /* A read operation was in progress */ -#define IOMMU_AFSR_FAV 0x00020000 /* IOMMU afar has valid contents */ -#define IOMMU_AFSR_MASK 0xff0fffff - -#define IOMMU_AFAR (0x1004 >> 2) - -#define IOMMU_AER (0x1008 >> 2) /* Arbiter Enable Register */ -#define IOMMU_AER_EN_P0_ARB 0x00000001 /* MBus master 0x8 (Always 1) */ -#define IOMMU_AER_EN_P1_ARB 0x00000002 /* MBus master 0x9 */ -#define IOMMU_AER_EN_P2_ARB 0x00000004 /* MBus master 0xa */ -#define IOMMU_AER_EN_P3_ARB 0x00000008 /* MBus master 0xb */ -#define IOMMU_AER_EN_0 0x00010000 /* SBus slot 0 */ -#define IOMMU_AER_EN_1 0x00020000 /* SBus slot 1 */ -#define IOMMU_AER_EN_2 0x00040000 /* SBus slot 2 */ -#define IOMMU_AER_EN_3 0x00080000 /* SBus slot 3 */ -#define IOMMU_AER_EN_F 0x00100000 /* SBus on-board */ -#define IOMMU_AER_SBW 0x80000000 /* S-to-M asynchronous writes */ -#define IOMMU_AER_MASK 0x801f000f - -#define IOMMU_SBCFG0 (0x1010 >> 2) /* SBUS configration per-slot */ -#define IOMMU_SBCFG1 (0x1014 >> 2) /* SBUS configration per-slot */ -#define IOMMU_SBCFG2 (0x1018 >> 2) /* SBUS configration per-slot */ -#define IOMMU_SBCFG3 (0x101c >> 2) /* SBUS configration per-slot */ -#define IOMMU_SBCFG_SAB30 0x00010000 /* Phys-address bit 30 when - bypass enabled */ -#define IOMMU_SBCFG_BA16 0x00000004 /* Slave supports 16 byte bursts */ -#define IOMMU_SBCFG_BA8 0x00000002 /* Slave supports 8 byte bursts */ -#define IOMMU_SBCFG_BYPASS 0x00000001 /* Bypass IOMMU, treat all addresses - produced by this device as pure - physical. */ -#define IOMMU_SBCFG_MASK 0x00010003 - -#define IOMMU_ARBEN (0x2000 >> 2) /* SBUS arbitration enable */ -#define IOMMU_ARBEN_MASK 0x001f0000 -#define IOMMU_MID 0x00000008 - -#define IOMMU_MASK_ID (0x3018 >> 2) /* Mask ID */ -#define IOMMU_MASK_ID_MASK 0x00ffffff - -#define IOMMU_MSII_MASK 0x26000000 /* microSPARC II mask number */ -#define IOMMU_TS_MASK 0x23000000 /* turboSPARC mask number */ - -/* The format of an iopte in the page tables */ -#define IOPTE_PAGE 0xffffff00 /* Physical page number (PA[35:12]) */ -#define IOPTE_CACHE 0x00000080 /* Cached (in vme IOCACHE or - Viking/MXCC) */ -#define IOPTE_WRITE 0x00000004 /* Writeable */ -#define IOPTE_VALID 0x00000002 /* IOPTE is valid */ -#define IOPTE_WAZ 0x00000001 /* Write as zeros */ - -#define IOMMU_PAGE_SHIFT 12 -#define IOMMU_PAGE_SIZE (1 << IOMMU_PAGE_SHIFT) -#define IOMMU_PAGE_MASK ~(IOMMU_PAGE_SIZE - 1) - -typedef struct IOMMUState { - SysBusDevice busdev; - uint32_t regs[IOMMU_NREGS]; - target_phys_addr_t iostart; - uint32_t version; - qemu_irq irq; -} IOMMUState; - -static uint32_t iommu_mem_readl(void *opaque, target_phys_addr_t addr) -{ - IOMMUState *s = opaque; - target_phys_addr_t saddr; - uint32_t ret; - - saddr = addr >> 2; - switch (saddr) { - default: - ret = s->regs[saddr]; - break; - case IOMMU_AFAR: - case IOMMU_AFSR: - ret = s->regs[saddr]; - qemu_irq_lower(s->irq); - break; - } - DPRINTF("read reg[%d] = %x\n", (int)saddr, ret); - return ret; -} - -static void iommu_mem_writel(void *opaque, target_phys_addr_t addr, - uint32_t val) -{ - IOMMUState *s = opaque; - target_phys_addr_t saddr; - - saddr = addr >> 2; - DPRINTF("write reg[%d] = %x\n", (int)saddr, val); - switch (saddr) { - case IOMMU_CTRL: - switch (val & IOMMU_CTRL_RNGE) { - case IOMMU_RNGE_16MB: - s->iostart = 0xffffffffff000000ULL; - break; - case IOMMU_RNGE_32MB: - s->iostart = 0xfffffffffe000000ULL; - break; - case IOMMU_RNGE_64MB: - s->iostart = 0xfffffffffc000000ULL; - break; - case IOMMU_RNGE_128MB: - s->iostart = 0xfffffffff8000000ULL; - break; - case IOMMU_RNGE_256MB: - s->iostart = 0xfffffffff0000000ULL; - break; - case IOMMU_RNGE_512MB: - s->iostart = 0xffffffffe0000000ULL; - break; - case IOMMU_RNGE_1GB: - s->iostart = 0xffffffffc0000000ULL; - break; - default: - case IOMMU_RNGE_2GB: - s->iostart = 0xffffffff80000000ULL; - break; - } - DPRINTF("iostart = " TARGET_FMT_plx "\n", s->iostart); - s->regs[saddr] = ((val & IOMMU_CTRL_MASK) | s->version); - break; - case IOMMU_BASE: - s->regs[saddr] = val & IOMMU_BASE_MASK; - break; - case IOMMU_TLBFLUSH: - DPRINTF("tlb flush %x\n", val); - s->regs[saddr] = val & IOMMU_TLBFLUSH_MASK; - break; - case IOMMU_PGFLUSH: - DPRINTF("page flush %x\n", val); - s->regs[saddr] = val & IOMMU_PGFLUSH_MASK; - break; - case IOMMU_AFAR: - s->regs[saddr] = val; - qemu_irq_lower(s->irq); - break; - case IOMMU_AER: - s->regs[saddr] = (val & IOMMU_AER_MASK) | IOMMU_AER_EN_P0_ARB; - break; - case IOMMU_AFSR: - s->regs[saddr] = (val & IOMMU_AFSR_MASK) | IOMMU_AFSR_RESV; - qemu_irq_lower(s->irq); - break; - case IOMMU_SBCFG0: - case IOMMU_SBCFG1: - case IOMMU_SBCFG2: - case IOMMU_SBCFG3: - s->regs[saddr] = val & IOMMU_SBCFG_MASK; - break; - case IOMMU_ARBEN: - // XXX implement SBus probing: fault when reading unmapped - // addresses, fault cause and address stored to MMU/IOMMU - s->regs[saddr] = (val & IOMMU_ARBEN_MASK) | IOMMU_MID; - break; - case IOMMU_MASK_ID: - s->regs[saddr] |= val & IOMMU_MASK_ID_MASK; - break; - default: - s->regs[saddr] = val; - break; - } -} - -static CPUReadMemoryFunc * const iommu_mem_read[3] = { - NULL, - NULL, - iommu_mem_readl, -}; - -static CPUWriteMemoryFunc * const iommu_mem_write[3] = { - NULL, - NULL, - iommu_mem_writel, -}; - -static uint32_t iommu_page_get_flags(IOMMUState *s, target_phys_addr_t addr) -{ - uint32_t ret; - target_phys_addr_t iopte; -#ifdef DEBUG_IOMMU - target_phys_addr_t pa = addr; -#endif - - iopte = s->regs[IOMMU_BASE] << 4; - addr &= ~s->iostart; - iopte += (addr >> (IOMMU_PAGE_SHIFT - 2)) & ~3; - cpu_physical_memory_read(iopte, (uint8_t *)&ret, 4); - tswap32s(&ret); - DPRINTF("get flags addr " TARGET_FMT_plx " => pte " TARGET_FMT_plx - ", *pte = %x\n", pa, iopte, ret); - - return ret; -} - -static target_phys_addr_t iommu_translate_pa(target_phys_addr_t addr, - uint32_t pte) -{ - uint32_t tmppte; - target_phys_addr_t pa; - - tmppte = pte; - pa = ((pte & IOPTE_PAGE) << 4) + (addr & ~IOMMU_PAGE_MASK); - DPRINTF("xlate dva " TARGET_FMT_plx " => pa " TARGET_FMT_plx - " (iopte = %x)\n", addr, pa, tmppte); - - return pa; -} - -static void iommu_bad_addr(IOMMUState *s, target_phys_addr_t addr, - int is_write) -{ - DPRINTF("bad addr " TARGET_FMT_plx "\n", addr); - s->regs[IOMMU_AFSR] = IOMMU_AFSR_ERR | IOMMU_AFSR_LE | IOMMU_AFSR_RESV | - IOMMU_AFSR_FAV; - if (!is_write) - s->regs[IOMMU_AFSR] |= IOMMU_AFSR_RD; - s->regs[IOMMU_AFAR] = addr; - qemu_irq_raise(s->irq); -} - -void sparc_iommu_memory_rw(void *opaque, target_phys_addr_t addr, - uint8_t *buf, int len, int is_write) -{ - int l; - uint32_t flags; - target_phys_addr_t page, phys_addr; - - while (len > 0) { - page = addr & IOMMU_PAGE_MASK; - l = (page + IOMMU_PAGE_SIZE) - addr; - if (l > len) - l = len; - flags = iommu_page_get_flags(opaque, page); - if (!(flags & IOPTE_VALID)) { - iommu_bad_addr(opaque, page, is_write); - return; - } - phys_addr = iommu_translate_pa(addr, flags); - if (is_write) { - if (!(flags & IOPTE_WRITE)) { - iommu_bad_addr(opaque, page, is_write); - return; - } - cpu_physical_memory_write(phys_addr, buf, l); - } else { - cpu_physical_memory_read(phys_addr, buf, l); - } - len -= l; - buf += l; - addr += l; - } -} - -static const VMStateDescription vmstate_iommu = { - .name ="iommu", - .version_id = 2, - .minimum_version_id = 2, - .minimum_version_id_old = 2, - .fields = (VMStateField []) { - VMSTATE_UINT32_ARRAY(regs, IOMMUState, IOMMU_NREGS), - VMSTATE_UINT64(iostart, IOMMUState), - VMSTATE_END_OF_LIST() - } -}; - -static void iommu_reset(DeviceState *d) -{ - IOMMUState *s = container_of(d, IOMMUState, busdev.qdev); - - memset(s->regs, 0, IOMMU_NREGS * 4); - s->iostart = 0; - s->regs[IOMMU_CTRL] = s->version; - s->regs[IOMMU_ARBEN] = IOMMU_MID; - s->regs[IOMMU_AFSR] = IOMMU_AFSR_RESV; - s->regs[IOMMU_AER] = IOMMU_AER_EN_P0_ARB | IOMMU_AER_EN_P1_ARB; - s->regs[IOMMU_MASK_ID] = IOMMU_TS_MASK; -} - -static int iommu_init1(SysBusDevice *dev) -{ - IOMMUState *s = FROM_SYSBUS(IOMMUState, dev); - int io; - - sysbus_init_irq(dev, &s->irq); - - io = cpu_register_io_memory(iommu_mem_read, iommu_mem_write, s); - sysbus_init_mmio(dev, IOMMU_NREGS * sizeof(uint32_t), io); - - return 0; -} - -static SysBusDeviceInfo iommu_info = { - .init = iommu_init1, - .qdev.name = "iommu", - .qdev.size = sizeof(IOMMUState), - .qdev.vmsd = &vmstate_iommu, - .qdev.reset = iommu_reset, - .qdev.props = (Property[]) { - DEFINE_PROP_HEX32("version", IOMMUState, version, 0), - DEFINE_PROP_END_OF_LIST(), - } -}; - -static void iommu_register_devices(void) -{ - sysbus_register_withprop(&iommu_info); -} - -device_init(iommu_register_devices) diff -Nru qemu-kvm-0.12.5+noroms/hw/ipf.c qemu-kvm-0.14.1/hw/ipf.c --- qemu-kvm-0.12.5+noroms/hw/ipf.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/ipf.c 2011-05-11 13:29:46.000000000 +0000 @@ -635,12 +635,6 @@ unit_id++; } } - -#ifdef CONFIG_KVM_DEVICE_ASSIGNMENT - if (kvm_enabled()) - add_assigned_devices(pci_bus, assigned_devices, assigned_devices_index); -#endif /* CONFIG_KVM_DEVICE_ASSIGNMENT */ - } static void ipf_init_pci(ram_addr_t ram_size, diff -Nru qemu-kvm-0.12.5+noroms/hw/isa-bus.c qemu-kvm-0.14.1/hw/isa-bus.c --- qemu-kvm-0.12.5+noroms/hw/isa-bus.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/isa-bus.c 2011-05-11 13:29:46.000000000 +0000 @@ -25,16 +25,18 @@ struct ISABus { BusState qbus; qemu_irq *irqs; - uint32_t assigned; }; static ISABus *isabus; +target_phys_addr_t isa_mem_base = 0; static void isabus_dev_print(Monitor *mon, DeviceState *dev, int indent); +static char *isabus_get_fw_dev_path(DeviceState *dev); static struct BusInfo isa_bus_info = { .name = "ISA", .size = sizeof(ISABus), .print_dev = isabus_dev_print, + .get_fw_dev_path = isabus_get_fw_dev_path, }; ISABus *isa_bus_new(DeviceState *dev) @@ -58,39 +60,52 @@ } /* - * isa_reserve_irq() reserves the ISA irq and returns the corresponding - * qemu_irq entry for the i8259. + * isa_get_irq() returns the corresponding qemu_irq entry for the i8259. * * This function is only for special cases such as the 'ferr', and * temporary use for normal devices until they are converted to qdev. */ -qemu_irq isa_reserve_irq(int isairq) +qemu_irq isa_get_irq(int isairq) { if (isairq < 0 || isairq > 15) { - fprintf(stderr, "isa irq %d invalid\n", isairq); - exit(1); + hw_error("isa irq %d invalid", isairq); } - if (isabus->assigned & (1 << isairq)) { - fprintf(stderr, "isa irq %d already assigned\n", isairq); - exit(1); - } - isabus->assigned |= (1 << isairq); return isabus->irqs[isairq]; } void isa_init_irq(ISADevice *dev, qemu_irq *p, int isairq) { assert(dev->nirqs < ARRAY_SIZE(dev->isairq)); - if (isabus->assigned & (1 << isairq)) { - fprintf(stderr, "isa irq %d already assigned\n", isairq); - exit(1); - } - isabus->assigned |= (1 << isairq); dev->isairq[dev->nirqs] = isairq; - *p = isabus->irqs[isairq]; + *p = isa_get_irq(isairq); dev->nirqs++; } +static void isa_init_ioport_one(ISADevice *dev, uint16_t ioport) +{ + assert(dev->nioports < ARRAY_SIZE(dev->ioports)); + dev->ioports[dev->nioports++] = ioport; +} + +static int isa_cmp_ports(const void *p1, const void *p2) +{ + return *(uint16_t*)p1 - *(uint16_t*)p2; +} + +void isa_init_ioport_range(ISADevice *dev, uint16_t start, uint16_t length) +{ + int i; + for (i = start; i < start + length; i++) { + isa_init_ioport_one(dev, i); + } + qsort(dev->ioports, dev->nioports, sizeof(dev->ioports[0]), isa_cmp_ports); +} + +void isa_init_ioport(ISADevice *dev, uint16_t ioport) +{ + isa_init_ioport_range(dev, ioport, 1); +} + static int isa_qdev_init(DeviceState *qdev, DeviceInfo *base) { ISADevice *dev = DO_UPCAST(ISADevice, qdev, qdev); @@ -114,7 +129,7 @@ DeviceState *dev; if (!isabus) { - hw_error("Tried to create isa device %s with no isa bus present.\n", + hw_error("Tried to create isa device %s with no isa bus present.", name); } dev = qdev_create(&isabus->qbus, name); @@ -152,6 +167,7 @@ static SysBusDeviceInfo isabus_bridge_info = { .init = isabus_bridge_init, .qdev.name = "isabus-bridge", + .qdev.fw_name = "isa", .qdev.size = sizeof(SysBusDevice), .qdev.no_user = 1, }; @@ -161,4 +177,18 @@ sysbus_register_withprop(&isabus_bridge_info); } +static char *isabus_get_fw_dev_path(DeviceState *dev) +{ + ISADevice *d = (ISADevice*)dev; + char path[40]; + int off; + + off = snprintf(path, sizeof(path), "%s", qdev_fw_name(dev)); + if (d->nioports) { + snprintf(path + off, sizeof(path) - off, "@%04x", d->ioports[0]); + } + + return strdup(path); +} + device_init(isabus_register_devices) diff -Nru qemu-kvm-0.12.5+noroms/hw/isa.h qemu-kvm-0.14.1/hw/isa.h --- qemu-kvm-0.12.5+noroms/hw/isa.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/isa.h 2011-05-11 13:29:46.000000000 +0000 @@ -14,6 +14,8 @@ DeviceState qdev; uint32_t isairq[2]; int nirqs; + uint16_t ioports[32]; + int nioports; }; typedef int (*isa_qdev_initfn)(ISADevice *dev); @@ -24,8 +26,10 @@ ISABus *isa_bus_new(DeviceState *dev); void isa_bus_irqs(qemu_irq *irqs); -qemu_irq isa_reserve_irq(int isairq); +qemu_irq isa_get_irq(int isairq); void isa_init_irq(ISADevice *dev, qemu_irq *p, int isairq); +void isa_init_ioport(ISADevice *dev, uint16_t ioport); +void isa_init_ioport_range(ISADevice *dev, uint16_t start, uint16_t length); void isa_qdev_register(ISADeviceInfo *info); ISADevice *isa_create(const char *name); ISADevice *isa_create_simple(const char *name); @@ -41,7 +45,7 @@ void DMA_hold_DREQ (int nchan); void DMA_release_DREQ (int nchan); void DMA_schedule(int nchan); -void DMA_init (int high_page_enable); +void DMA_init(int high_page_enable, qemu_irq *cpu_request_exit); void DMA_register_channel (int nchan, DMA_transfer_handler transfer_handler, void *opaque); diff -Nru qemu-kvm-0.12.5+noroms/hw/isa_mmio.c qemu-kvm-0.14.1/hw/isa_mmio.c --- qemu-kvm-0.12.5+noroms/hw/isa_mmio.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/isa_mmio.c 2011-05-11 13:29:46.000000000 +0000 @@ -31,52 +31,31 @@ cpu_outb(addr & IOPORTS_MASK, val); } -static void isa_mmio_writew (void *opaque, target_phys_addr_t addr, - uint32_t val) +static void isa_mmio_writew(void *opaque, target_phys_addr_t addr, + uint32_t val) { -#ifdef TARGET_WORDS_BIGENDIAN - val = bswap16(val); -#endif cpu_outw(addr & IOPORTS_MASK, val); } -static void isa_mmio_writel (void *opaque, target_phys_addr_t addr, - uint32_t val) +static void isa_mmio_writel(void *opaque, target_phys_addr_t addr, + uint32_t val) { -#ifdef TARGET_WORDS_BIGENDIAN - val = bswap32(val); -#endif cpu_outl(addr & IOPORTS_MASK, val); } static uint32_t isa_mmio_readb (void *opaque, target_phys_addr_t addr) { - uint32_t val; - - val = cpu_inb(addr & IOPORTS_MASK); - return val; + return cpu_inb(addr & IOPORTS_MASK); } -static uint32_t isa_mmio_readw (void *opaque, target_phys_addr_t addr) +static uint32_t isa_mmio_readw(void *opaque, target_phys_addr_t addr) { - uint32_t val; - - val = cpu_inw(addr & IOPORTS_MASK); -#ifdef TARGET_WORDS_BIGENDIAN - val = bswap16(val); -#endif - return val; + return cpu_inw(addr & IOPORTS_MASK); } -static uint32_t isa_mmio_readl (void *opaque, target_phys_addr_t addr) +static uint32_t isa_mmio_readl(void *opaque, target_phys_addr_t addr) { - uint32_t val; - - val = cpu_inl(addr & IOPORTS_MASK); -#ifdef TARGET_WORDS_BIGENDIAN - val = bswap32(val); -#endif - return val; + return cpu_inl(addr & IOPORTS_MASK); } static CPUWriteMemoryFunc * const isa_mmio_write[] = { @@ -91,13 +70,13 @@ &isa_mmio_readl, }; -static int isa_mmio_iomemtype = 0; - void isa_mmio_init(target_phys_addr_t base, target_phys_addr_t size) { - if (!isa_mmio_iomemtype) { - isa_mmio_iomemtype = cpu_register_io_memory(isa_mmio_read, - isa_mmio_write, NULL); - } + int isa_mmio_iomemtype; + + isa_mmio_iomemtype = cpu_register_io_memory(isa_mmio_read, + isa_mmio_write, + NULL, + DEVICE_LITTLE_ENDIAN); cpu_register_physical_memory(base, size, isa_mmio_iomemtype); } diff -Nru qemu-kvm-0.12.5+noroms/hw/ivshmem.c qemu-kvm-0.14.1/hw/ivshmem.c --- qemu-kvm-0.12.5+noroms/hw/ivshmem.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/ivshmem.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,829 @@ +/* + * Inter-VM Shared Memory PCI device. + * + * Author: + * Cam Macdonell + * + * Based On: cirrus_vga.c + * Copyright (c) 2004 Fabrice Bellard + * Copyright (c) 2004 Makoto Suzuki (suzu) + * + * and rtl8139.c + * Copyright (c) 2006 Igor Kovalenko + * + * This code is licensed under the GNU GPL v2. + */ +#include "hw.h" +#include "pc.h" +#include "pci.h" +#include "msix.h" +#include "kvm.h" + +#include +#include + +#define IVSHMEM_IOEVENTFD 0 +#define IVSHMEM_MSI 1 + +#define IVSHMEM_PEER 0 +#define IVSHMEM_MASTER 1 + +#define IVSHMEM_REG_BAR_SIZE 0x100 + +//#define DEBUG_IVSHMEM +#ifdef DEBUG_IVSHMEM +#define IVSHMEM_DPRINTF(fmt, ...) \ + do {printf("IVSHMEM: " fmt, ## __VA_ARGS__); } while (0) +#else +#define IVSHMEM_DPRINTF(fmt, ...) +#endif + +typedef struct Peer { + int nb_eventfds; + int *eventfds; +} Peer; + +typedef struct EventfdEntry { + PCIDevice *pdev; + int vector; +} EventfdEntry; + +typedef struct IVShmemState { + PCIDevice dev; + uint32_t intrmask; + uint32_t intrstatus; + uint32_t doorbell; + + CharDriverState **eventfd_chr; + CharDriverState *server_chr; + int ivshmem_mmio_io_addr; + + pcibus_t mmio_addr; + pcibus_t shm_pci_addr; + uint64_t ivshmem_offset; + uint64_t ivshmem_size; /* size of shared memory region */ + int shm_fd; /* shared memory file descriptor */ + + Peer *peers; + int nb_peers; /* how many guests we have space for */ + int max_peer; /* maximum numbered peer */ + + int vm_id; + uint32_t vectors; + uint32_t features; + EventfdEntry *eventfd_table; + + char * shmobj; + char * sizearg; + char * role; + int role_val; /* scalar to avoid multiple string comparisons */ +} IVShmemState; + +/* registers for the Inter-VM shared memory device */ +enum ivshmem_registers { + INTRMASK = 0, + INTRSTATUS = 4, + IVPOSITION = 8, + DOORBELL = 12, +}; + +static inline uint32_t ivshmem_has_feature(IVShmemState *ivs, + unsigned int feature) { + return (ivs->features & (1 << feature)); +} + +static inline bool is_power_of_two(uint64_t x) { + return (x & (x - 1)) == 0; +} + +static void ivshmem_map(PCIDevice *pci_dev, int region_num, + pcibus_t addr, pcibus_t size, int type) +{ + IVShmemState *s = DO_UPCAST(IVShmemState, dev, pci_dev); + + s->shm_pci_addr = addr; + + if (s->ivshmem_offset > 0) { + cpu_register_physical_memory(s->shm_pci_addr, s->ivshmem_size, + s->ivshmem_offset); + } + + IVSHMEM_DPRINTF("guest pci addr = %" FMT_PCIBUS ", guest h/w addr = %" + PRIu64 ", size = %" FMT_PCIBUS "\n", addr, s->ivshmem_offset, size); + +} + +/* accessing registers - based on rtl8139 */ +static void ivshmem_update_irq(IVShmemState *s, int val) +{ + int isr; + isr = (s->intrstatus & s->intrmask) & 0xffffffff; + + /* don't print ISR resets */ + if (isr) { + IVSHMEM_DPRINTF("Set IRQ to %d (%04x %04x)\n", + isr ? 1 : 0, s->intrstatus, s->intrmask); + } + + qemu_set_irq(s->dev.irq[0], (isr != 0)); +} + +static void ivshmem_IntrMask_write(IVShmemState *s, uint32_t val) +{ + IVSHMEM_DPRINTF("IntrMask write(w) val = 0x%04x\n", val); + + s->intrmask = val; + + ivshmem_update_irq(s, val); +} + +static uint32_t ivshmem_IntrMask_read(IVShmemState *s) +{ + uint32_t ret = s->intrmask; + + IVSHMEM_DPRINTF("intrmask read(w) val = 0x%04x\n", ret); + + return ret; +} + +static void ivshmem_IntrStatus_write(IVShmemState *s, uint32_t val) +{ + IVSHMEM_DPRINTF("IntrStatus write(w) val = 0x%04x\n", val); + + s->intrstatus = val; + + ivshmem_update_irq(s, val); + return; +} + +static uint32_t ivshmem_IntrStatus_read(IVShmemState *s) +{ + uint32_t ret = s->intrstatus; + + /* reading ISR clears all interrupts */ + s->intrstatus = 0; + + ivshmem_update_irq(s, 0); + + return ret; +} + +static void ivshmem_io_writew(void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + + IVSHMEM_DPRINTF("We shouldn't be writing words\n"); +} + +static void ivshmem_io_writel(void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + IVShmemState *s = opaque; + + uint64_t write_one = 1; + uint16_t dest = val >> 16; + uint16_t vector = val & 0xff; + + addr &= 0xfc; + + IVSHMEM_DPRINTF("writing to addr " TARGET_FMT_plx "\n", addr); + switch (addr) + { + case INTRMASK: + ivshmem_IntrMask_write(s, val); + break; + + case INTRSTATUS: + ivshmem_IntrStatus_write(s, val); + break; + + case DOORBELL: + /* check that dest VM ID is reasonable */ + if (dest > s->max_peer) { + IVSHMEM_DPRINTF("Invalid destination VM ID (%d)\n", dest); + break; + } + + /* check doorbell range */ + if (vector < s->peers[dest].nb_eventfds) { + IVSHMEM_DPRINTF("Writing %" PRId64 " to VM %d on vector %d\n", + write_one, dest, vector); + if (write(s->peers[dest].eventfds[vector], + &(write_one), 8) != 8) { + IVSHMEM_DPRINTF("error writing to eventfd\n"); + } + } + break; + default: + IVSHMEM_DPRINTF("Invalid VM Doorbell VM %d\n", dest); + } +} + +static void ivshmem_io_writeb(void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + IVSHMEM_DPRINTF("We shouldn't be writing bytes\n"); +} + +static uint32_t ivshmem_io_readw(void *opaque, target_phys_addr_t addr) +{ + + IVSHMEM_DPRINTF("We shouldn't be reading words\n"); + return 0; +} + +static uint32_t ivshmem_io_readl(void *opaque, target_phys_addr_t addr) +{ + + IVShmemState *s = opaque; + uint32_t ret; + + switch (addr) + { + case INTRMASK: + ret = ivshmem_IntrMask_read(s); + break; + + case INTRSTATUS: + ret = ivshmem_IntrStatus_read(s); + break; + + case IVPOSITION: + /* return my VM ID if the memory is mapped */ + if (s->shm_fd > 0) { + ret = s->vm_id; + } else { + ret = -1; + } + break; + + default: + IVSHMEM_DPRINTF("why are we reading " TARGET_FMT_plx "\n", addr); + ret = 0; + } + + return ret; +} + +static uint32_t ivshmem_io_readb(void *opaque, target_phys_addr_t addr) +{ + IVSHMEM_DPRINTF("We shouldn't be reading bytes\n"); + + return 0; +} + +static CPUReadMemoryFunc * const ivshmem_mmio_read[3] = { + ivshmem_io_readb, + ivshmem_io_readw, + ivshmem_io_readl, +}; + +static CPUWriteMemoryFunc * const ivshmem_mmio_write[3] = { + ivshmem_io_writeb, + ivshmem_io_writew, + ivshmem_io_writel, +}; + +static void ivshmem_receive(void *opaque, const uint8_t *buf, int size) +{ + IVShmemState *s = opaque; + + ivshmem_IntrStatus_write(s, *buf); + + IVSHMEM_DPRINTF("ivshmem_receive 0x%02x\n", *buf); +} + +static int ivshmem_can_receive(void * opaque) +{ + return 8; +} + +static void ivshmem_event(void *opaque, int event) +{ + IVSHMEM_DPRINTF("ivshmem_event %d\n", event); +} + +static void fake_irqfd(void *opaque, const uint8_t *buf, int size) { + + EventfdEntry *entry = opaque; + PCIDevice *pdev = entry->pdev; + + IVSHMEM_DPRINTF("interrupt on vector %p %d\n", pdev, entry->vector); + msix_notify(pdev, entry->vector); +} + +static CharDriverState* create_eventfd_chr_device(void * opaque, int eventfd, + int vector) +{ + /* create a event character device based on the passed eventfd */ + IVShmemState *s = opaque; + CharDriverState * chr; + + chr = qemu_chr_open_eventfd(eventfd); + + if (chr == NULL) { + fprintf(stderr, "creating eventfd for eventfd %d failed\n", eventfd); + exit(-1); + } + + /* if MSI is supported we need multiple interrupts */ + if (ivshmem_has_feature(s, IVSHMEM_MSI)) { + s->eventfd_table[vector].pdev = &s->dev; + s->eventfd_table[vector].vector = vector; + + qemu_chr_add_handlers(chr, ivshmem_can_receive, fake_irqfd, + ivshmem_event, &s->eventfd_table[vector]); + } else { + qemu_chr_add_handlers(chr, ivshmem_can_receive, ivshmem_receive, + ivshmem_event, s); + } + + return chr; + +} + +static int check_shm_size(IVShmemState *s, int fd) { + /* check that the guest isn't going to try and map more memory than the + * the object has allocated return -1 to indicate error */ + + struct stat buf; + + fstat(fd, &buf); + + if (s->ivshmem_size > buf.st_size) { + fprintf(stderr, + "IVSHMEM ERROR: Requested memory size greater" + " than shared object size (%" PRIu64 " > %" PRIu64")\n", + s->ivshmem_size, (uint64_t)buf.st_size); + return -1; + } else { + return 0; + } +} + +/* create the shared memory BAR when we are not using the server, so we can + * create the BAR and map the memory immediately */ +static void create_shared_memory_BAR(IVShmemState *s, int fd) { + + void * ptr; + + s->shm_fd = fd; + + ptr = mmap(0, s->ivshmem_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + + s->ivshmem_offset = qemu_ram_alloc_from_ptr(&s->dev.qdev, "ivshmem.bar2", + s->ivshmem_size, ptr); + + /* region for shared memory */ + pci_register_bar(&s->dev, 2, s->ivshmem_size, + PCI_BASE_ADDRESS_SPACE_MEMORY, ivshmem_map); +} + +static void close_guest_eventfds(IVShmemState *s, int posn) +{ + int i, guest_curr_max; + + guest_curr_max = s->peers[posn].nb_eventfds; + + for (i = 0; i < guest_curr_max; i++) { + kvm_set_ioeventfd_mmio_long(s->peers[posn].eventfds[i], + s->mmio_addr + DOORBELL, (posn << 16) | i, 0); + close(s->peers[posn].eventfds[i]); + } + + qemu_free(s->peers[posn].eventfds); + s->peers[posn].nb_eventfds = 0; +} + +static void setup_ioeventfds(IVShmemState *s) { + + int i, j; + + for (i = 0; i <= s->max_peer; i++) { + for (j = 0; j < s->peers[i].nb_eventfds; j++) { + kvm_set_ioeventfd_mmio_long(s->peers[i].eventfds[j], + s->mmio_addr + DOORBELL, (i << 16) | j, 1); + } + } +} + +/* this function increase the dynamic storage need to store data about other + * guests */ +static void increase_dynamic_storage(IVShmemState *s, int new_min_size) { + + int j, old_nb_alloc; + + old_nb_alloc = s->nb_peers; + + while (new_min_size >= s->nb_peers) + s->nb_peers = s->nb_peers * 2; + + IVSHMEM_DPRINTF("bumping storage to %d guests\n", s->nb_peers); + s->peers = qemu_realloc(s->peers, s->nb_peers * sizeof(Peer)); + + /* zero out new pointers */ + for (j = old_nb_alloc; j < s->nb_peers; j++) { + s->peers[j].eventfds = NULL; + s->peers[j].nb_eventfds = 0; + } +} + +static void ivshmem_read(void *opaque, const uint8_t * buf, int flags) +{ + IVShmemState *s = opaque; + int incoming_fd, tmp_fd; + int guest_max_eventfd; + long incoming_posn; + + memcpy(&incoming_posn, buf, sizeof(long)); + /* pick off s->server_chr->msgfd and store it, posn should accompany msg */ + tmp_fd = qemu_chr_get_msgfd(s->server_chr); + IVSHMEM_DPRINTF("posn is %ld, fd is %d\n", incoming_posn, tmp_fd); + + /* make sure we have enough space for this guest */ + if (incoming_posn >= s->nb_peers) { + increase_dynamic_storage(s, incoming_posn); + } + + if (tmp_fd == -1) { + /* if posn is positive and unseen before then this is our posn*/ + if ((incoming_posn >= 0) && + (s->peers[incoming_posn].eventfds == NULL)) { + /* receive our posn */ + s->vm_id = incoming_posn; + return; + } else { + /* otherwise an fd == -1 means an existing guest has gone away */ + IVSHMEM_DPRINTF("posn %ld has gone away\n", incoming_posn); + close_guest_eventfds(s, incoming_posn); + return; + } + } + + /* because of the implementation of get_msgfd, we need a dup */ + incoming_fd = dup(tmp_fd); + + if (incoming_fd == -1) { + fprintf(stderr, "could not allocate file descriptor %s\n", + strerror(errno)); + return; + } + + /* if the position is -1, then it's shared memory region fd */ + if (incoming_posn == -1) { + + void * map_ptr; + + s->max_peer = 0; + + if (check_shm_size(s, incoming_fd) == -1) { + exit(-1); + } + + /* mmap the region and map into the BAR2 */ + map_ptr = mmap(0, s->ivshmem_size, PROT_READ|PROT_WRITE, MAP_SHARED, + incoming_fd, 0); + s->ivshmem_offset = qemu_ram_alloc_from_ptr(&s->dev.qdev, + "ivshmem.bar2", s->ivshmem_size, map_ptr); + + IVSHMEM_DPRINTF("guest pci addr = %" FMT_PCIBUS ", guest h/w addr = %" + PRIu64 ", size = %" PRIu64 "\n", s->shm_pci_addr, + s->ivshmem_offset, s->ivshmem_size); + + if (s->shm_pci_addr > 0) { + /* map memory into BAR2 */ + cpu_register_physical_memory(s->shm_pci_addr, s->ivshmem_size, + s->ivshmem_offset); + } + + /* only store the fd if it is successfully mapped */ + s->shm_fd = incoming_fd; + + return; + } + + /* each guest has an array of eventfds, and we keep track of how many + * guests for each VM */ + guest_max_eventfd = s->peers[incoming_posn].nb_eventfds; + + if (guest_max_eventfd == 0) { + /* one eventfd per MSI vector */ + s->peers[incoming_posn].eventfds = (int *) qemu_malloc(s->vectors * + sizeof(int)); + } + + /* this is an eventfd for a particular guest VM */ + IVSHMEM_DPRINTF("eventfds[%ld][%d] = %d\n", incoming_posn, + guest_max_eventfd, incoming_fd); + s->peers[incoming_posn].eventfds[guest_max_eventfd] = incoming_fd; + + /* increment count for particular guest */ + s->peers[incoming_posn].nb_eventfds++; + + /* keep track of the maximum VM ID */ + if (incoming_posn > s->max_peer) { + s->max_peer = incoming_posn; + } + + if (incoming_posn == s->vm_id) { + s->eventfd_chr[guest_max_eventfd] = create_eventfd_chr_device(s, + s->peers[s->vm_id].eventfds[guest_max_eventfd], + guest_max_eventfd); + } + + if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD)) { + if (kvm_set_ioeventfd_mmio_long(incoming_fd, s->mmio_addr + DOORBELL, + (incoming_posn << 16) | guest_max_eventfd, 1) < 0) { + fprintf(stderr, "ivshmem: ioeventfd not available\n"); + } + } + + return; +} + +static void ivshmem_reset(DeviceState *d) +{ + IVShmemState *s = DO_UPCAST(IVShmemState, dev.qdev, d); + + s->intrstatus = 0; + return; +} + +static void ivshmem_mmio_map(PCIDevice *pci_dev, int region_num, + pcibus_t addr, pcibus_t size, int type) +{ + IVShmemState *s = DO_UPCAST(IVShmemState, dev, pci_dev); + + s->mmio_addr = addr; + cpu_register_physical_memory(addr + 0, IVSHMEM_REG_BAR_SIZE, + s->ivshmem_mmio_io_addr); + + if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD)) { + setup_ioeventfds(s); + } +} + +static uint64_t ivshmem_get_size(IVShmemState * s) { + + uint64_t value; + char *ptr; + + value = strtoull(s->sizearg, &ptr, 10); + switch (*ptr) { + case 0: case 'M': case 'm': + value <<= 20; + break; + case 'G': case 'g': + value <<= 30; + break; + default: + fprintf(stderr, "qemu: invalid ram size: %s\n", s->sizearg); + exit(1); + } + + /* BARs must be a power of 2 */ + if (!is_power_of_two(value)) { + fprintf(stderr, "ivshmem: size must be power of 2\n"); + exit(1); + } + + return value; +} + +static void ivshmem_setup_msi(IVShmemState * s) { + + int i; + + /* allocate the MSI-X vectors */ + + if (!msix_init(&s->dev, s->vectors, 1, 0)) { + pci_register_bar(&s->dev, 1, + msix_bar_size(&s->dev), + PCI_BASE_ADDRESS_SPACE_MEMORY, + msix_mmio_map); + IVSHMEM_DPRINTF("msix initialized (%d vectors)\n", s->vectors); + } else { + IVSHMEM_DPRINTF("msix initialization failed\n"); + exit(1); + } + + /* 'activate' the vectors */ + for (i = 0; i < s->vectors; i++) { + msix_vector_use(&s->dev, i); + } + + /* allocate Qemu char devices for receiving interrupts */ + s->eventfd_table = qemu_mallocz(s->vectors * sizeof(EventfdEntry)); +} + +static void ivshmem_save(QEMUFile* f, void *opaque) +{ + IVShmemState *proxy = opaque; + + IVSHMEM_DPRINTF("ivshmem_save\n"); + pci_device_save(&proxy->dev, f); + + if (ivshmem_has_feature(proxy, IVSHMEM_MSI)) { + msix_save(&proxy->dev, f); + } else { + qemu_put_be32(f, proxy->intrstatus); + qemu_put_be32(f, proxy->intrmask); + } + +} + +static int ivshmem_load(QEMUFile* f, void *opaque, int version_id) +{ + IVSHMEM_DPRINTF("ivshmem_load\n"); + + IVShmemState *proxy = opaque; + int ret, i; + + if (version_id > 0) { + return -EINVAL; + } + + if (proxy->role_val == IVSHMEM_PEER) { + fprintf(stderr, "ivshmem: 'peer' devices are not migratable\n"); + return -EINVAL; + } + + ret = pci_device_load(&proxy->dev, f); + if (ret) { + return ret; + } + + if (ivshmem_has_feature(proxy, IVSHMEM_MSI)) { + msix_load(&proxy->dev, f); + for (i = 0; i < proxy->vectors; i++) { + msix_vector_use(&proxy->dev, i); + } + } else { + proxy->intrstatus = qemu_get_be32(f); + proxy->intrmask = qemu_get_be32(f); + } + + return 0; +} + +static int pci_ivshmem_init(PCIDevice *dev) +{ + IVShmemState *s = DO_UPCAST(IVShmemState, dev, dev); + uint8_t *pci_conf; + + if (s->sizearg == NULL) + s->ivshmem_size = 4 << 20; /* 4 MB default */ + else { + s->ivshmem_size = ivshmem_get_size(s); + } + + register_savevm(&s->dev.qdev, "ivshmem", 0, 0, ivshmem_save, ivshmem_load, + dev); + + /* IRQFD requires MSI */ + if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD) && + !ivshmem_has_feature(s, IVSHMEM_MSI)) { + fprintf(stderr, "ivshmem: ioeventfd/irqfd requires MSI\n"); + exit(1); + } + + /* check that role is reasonable */ + if (s->role) { + if (strncmp(s->role, "peer", 5) == 0) { + s->role_val = IVSHMEM_PEER; + } else if (strncmp(s->role, "master", 7) == 0) { + s->role_val = IVSHMEM_MASTER; + } else { + fprintf(stderr, "ivshmem: 'role' must be 'peer' or 'master'\n"); + exit(1); + } + } else { + s->role_val = IVSHMEM_MASTER; /* default */ + } + + if (s->role_val == IVSHMEM_PEER) { + register_device_unmigratable(&s->dev.qdev, "ivshmem", s); + } + + pci_conf = s->dev.config; + pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_REDHAT_QUMRANET); + pci_conf[0x02] = 0x10; + pci_conf[0x03] = 0x11; + pci_conf[PCI_COMMAND] = PCI_COMMAND_IO | PCI_COMMAND_MEMORY; + pci_config_set_class(pci_conf, PCI_CLASS_MEMORY_RAM); + pci_conf[PCI_HEADER_TYPE] = PCI_HEADER_TYPE_NORMAL; + + pci_config_set_interrupt_pin(pci_conf, 1); + + s->shm_pci_addr = 0; + s->ivshmem_offset = 0; + s->shm_fd = 0; + + s->ivshmem_mmio_io_addr = cpu_register_io_memory(ivshmem_mmio_read, + ivshmem_mmio_write, s, DEVICE_NATIVE_ENDIAN); + /* region for registers*/ + pci_register_bar(&s->dev, 0, IVSHMEM_REG_BAR_SIZE, + PCI_BASE_ADDRESS_SPACE_MEMORY, ivshmem_mmio_map); + + if ((s->server_chr != NULL) && + (strncmp(s->server_chr->filename, "unix:", 5) == 0)) { + /* if we get a UNIX socket as the parameter we will talk + * to the ivshmem server to receive the memory region */ + + if (s->shmobj != NULL) { + fprintf(stderr, "WARNING: do not specify both 'chardev' " + "and 'shm' with ivshmem\n"); + } + + IVSHMEM_DPRINTF("using shared memory server (socket = %s)\n", + s->server_chr->filename); + + if (ivshmem_has_feature(s, IVSHMEM_MSI)) { + ivshmem_setup_msi(s); + } + + /* we allocate enough space for 16 guests and grow as needed */ + s->nb_peers = 16; + s->vm_id = -1; + + /* allocate/initialize space for interrupt handling */ + s->peers = qemu_mallocz(s->nb_peers * sizeof(Peer)); + + pci_register_bar(&s->dev, 2, s->ivshmem_size, + PCI_BASE_ADDRESS_SPACE_MEMORY, ivshmem_map); + + s->eventfd_chr = qemu_mallocz(s->vectors * sizeof(CharDriverState *)); + + qemu_chr_add_handlers(s->server_chr, ivshmem_can_receive, ivshmem_read, + ivshmem_event, s); + } else { + /* just map the file immediately, we're not using a server */ + int fd; + + if (s->shmobj == NULL) { + fprintf(stderr, "Must specify 'chardev' or 'shm' to ivshmem\n"); + } + + IVSHMEM_DPRINTF("using shm_open (shm object = %s)\n", s->shmobj); + + /* try opening with O_EXCL and if it succeeds zero the memory + * by truncating to 0 */ + if ((fd = shm_open(s->shmobj, O_CREAT|O_RDWR|O_EXCL, + S_IRWXU|S_IRWXG|S_IRWXO)) > 0) { + /* truncate file to length PCI device's memory */ + if (ftruncate(fd, s->ivshmem_size) != 0) { + fprintf(stderr, "ivshmem: could not truncate shared file\n"); + } + + } else if ((fd = shm_open(s->shmobj, O_CREAT|O_RDWR, + S_IRWXU|S_IRWXG|S_IRWXO)) < 0) { + fprintf(stderr, "ivshmem: could not open shared file\n"); + exit(-1); + + } + + if (check_shm_size(s, fd) == -1) { + exit(-1); + } + + create_shared_memory_BAR(s, fd); + + } + + return 0; +} + +static int pci_ivshmem_uninit(PCIDevice *dev) +{ + IVShmemState *s = DO_UPCAST(IVShmemState, dev, dev); + + cpu_unregister_io_memory(s->ivshmem_mmio_io_addr); + unregister_savevm(&dev->qdev, "ivshmem", s); + + return 0; +} + +static PCIDeviceInfo ivshmem_info = { + .qdev.name = "ivshmem", + .qdev.size = sizeof(IVShmemState), + .qdev.reset = ivshmem_reset, + .init = pci_ivshmem_init, + .exit = pci_ivshmem_uninit, + .qdev.props = (Property[]) { + DEFINE_PROP_CHR("chardev", IVShmemState, server_chr), + DEFINE_PROP_STRING("size", IVShmemState, sizearg), + DEFINE_PROP_UINT32("vectors", IVShmemState, vectors, 1), + DEFINE_PROP_BIT("ioeventfd", IVShmemState, features, IVSHMEM_IOEVENTFD, false), + DEFINE_PROP_BIT("msi", IVShmemState, features, IVSHMEM_MSI, true), + DEFINE_PROP_STRING("shm", IVShmemState, shmobj), + DEFINE_PROP_STRING("role", IVShmemState, role), + DEFINE_PROP_END_OF_LIST(), + } +}; + +static void ivshmem_register_devices(void) +{ + pci_qdev_register(&ivshmem_info); +} + +device_init(ivshmem_register_devices) diff -Nru qemu-kvm-0.12.5+noroms/hw/jazz_led.c qemu-kvm-0.14.1/hw/jazz_led.c --- qemu-kvm-0.12.5+noroms/hw/jazz_led.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/jazz_led.c 2011-05-11 13:29:46.000000000 +0000 @@ -29,6 +29,15 @@ //#define DEBUG_LED +#ifdef DEBUG_LED +#define DPRINTF(fmt, ...) \ +do { printf("jazz led: " fmt , ## __VA_ARGS__); } while (0) +#else +#define DPRINTF(fmt, ...) do {} while (0) +#endif +#define BADF(fmt, ...) \ +do { fprintf(stderr, "jazz led ERROR: " fmt , ## __VA_ARGS__);} while (0) + typedef enum { REDRAW_NONE = 0, REDRAW_SEGMENTS = 1, REDRAW_BACKGROUND = 2, } screen_state_t; @@ -49,12 +58,12 @@ val = s->segments; break; default: -#ifdef DEBUG_LED - printf("jazz led: invalid read [0x%x]\n", relative_addr); -#endif + BADF("invalid read at [" TARGET_FMT_plx "]\n", addr); val = 0; } + DPRINTF("read addr=" TARGET_FMT_plx " val=0x%02x\n", addr, val); + return val; } @@ -92,15 +101,15 @@ { LedState *s = opaque; + DPRINTF("write addr=" TARGET_FMT_plx " val=0x%02x\n", addr, val); + switch (addr) { case 0: s->segments = val; s->state |= REDRAW_SEGMENTS; break; default: -#ifdef DEBUG_LED - printf("jazz led: invalid write of 0x%02x at [0x%x]\n", val, relative_addr); -#endif + BADF("invalid write of 0x%08x at [" TARGET_FMT_plx "]\n", val, addr); break; } } @@ -307,7 +316,8 @@ s->state = REDRAW_SEGMENTS | REDRAW_BACKGROUND; - io = cpu_register_io_memory(led_read, led_write, s); + io = cpu_register_io_memory(led_read, led_write, s, + DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base, 1, io); s->ds = graphic_console_init(jazz_led_update_display, diff -Nru qemu-kvm-0.12.5+noroms/hw/lan9118.c qemu-kvm-0.14.1/hw/lan9118.c --- qemu-kvm-0.12.5+noroms/hw/lan9118.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/lan9118.c 2011-05-11 13:29:46.000000000 +0000 @@ -10,6 +10,7 @@ #include "sysbus.h" #include "net.h" #include "devices.h" +#include "sysemu.h" /* For crc32 */ #include @@ -40,8 +41,8 @@ #define CSR_TX_FIFO_INF 0x80 #define CSR_PMT_CTRL 0x84 #define CSR_GPIO_CFG 0x88 -#define CSR_GPT_CFG 0x8c /* TODO */ -#define CSR_GPT_CNT 0x90 /* TODO */ +#define CSR_GPT_CFG 0x8c +#define CSR_GPT_CNT 0x90 #define CSR_WORD_SWAP 0x98 #define CSR_FREE_RUN 0x9c #define CSR_RX_DROP 0xa0 @@ -52,6 +53,7 @@ #define CSR_E2P_DATA 0xb4 /* IRQ_CFG */ +#define IRQ_INT 0x00001000 #define IRQ_EN 0x00000100 #define IRQ_POL 0x00000010 #define IRQ_TYPE 0x00000001 @@ -117,6 +119,16 @@ #define MAC_CR_RXEN 0x00000004 #define MAC_CR_RESERVED 0x7f404213 +#define PHY_INT_ENERGYON 0x80 +#define PHY_INT_AUTONEG_COMPLETE 0x40 +#define PHY_INT_FAULT 0x20 +#define PHY_INT_DOWN 0x10 +#define PHY_INT_AUTONEG_LP 0x08 +#define PHY_INT_PARFAULT 0x04 +#define PHY_INT_AUTONEG_PAGE 0x02 + +#define GPT_TIMER_EN 0x20000000 + enum tx_state { TX_IDLE, TX_B, @@ -141,6 +153,7 @@ NICConf conf; qemu_irq irq; int mmio_index; + ptimer_state *timer; uint32_t irq_cfg; uint32_t int_sts; @@ -151,6 +164,7 @@ uint32_t hw_cfg; uint32_t pmt_ctrl; uint32_t gpio_cfg; + uint32_t gpt_cfg; uint32_t word_swap; uint32_t free_timer_start; uint32_t mac_cmd; @@ -169,9 +183,11 @@ uint32_t phy_status; uint32_t phy_control; uint32_t phy_advertise; + uint32_t phy_int; + uint32_t phy_int_mask; int eeprom_writable; - uint8_t eeprom[8]; + uint8_t eeprom[128]; int tx_fifo_size; LAN9118Packet *txp; @@ -204,6 +220,11 @@ /* TODO: Implement FIFO level IRQs. */ level = (s->int_sts & s->int_en) != 0; + if (level) { + s->irq_cfg |= IRQ_INT; + } else { + s->irq_cfg &= ~IRQ_INT; + } if ((s->irq_cfg & IRQ_EN) == 0) { level = 0; } @@ -231,14 +252,28 @@ lan9118_mac_changed(s); } +static void phy_update_irq(lan9118_state *s) +{ + if (s->phy_int & s->phy_int_mask) { + s->int_sts |= PHY_INT; + } else { + s->int_sts &= ~PHY_INT; + } + lan9118_update(s); +} + static void phy_update_link(lan9118_state *s) { /* Autonegotiation status mirrors link status. */ if (s->nic->nc.link_down) { s->phy_status &= ~0x0024; + s->phy_int |= PHY_INT_DOWN; } else { s->phy_status |= 0x0024; + s->phy_int |= PHY_INT_ENERGYON; + s->phy_int |= PHY_INT_AUTONEG_COMPLETE; } + phy_update_irq(s); } static void lan9118_set_link(VLANClientState *nc) @@ -248,9 +283,11 @@ static void phy_reset(lan9118_state *s) { - s->phy_status = 0x7805; + s->phy_status = 0x7809; s->phy_control = 0x3000; s->phy_advertise = 0x01e1; + s->phy_int_mask = 0; + s->phy_int = 0; phy_update_link(s); } @@ -292,6 +329,10 @@ s->e2p_data = 0; s->free_timer_start = qemu_get_clock(vm_clock) / 40; + ptimer_stop(s->timer); + ptimer_set_count(s->timer, 0xffff); + s->gpt_cfg = 0xffff; + s->mac_cr = MAC_CR_PRMS; s->mac_hashh = 0; s->mac_hashl = 0; @@ -640,6 +681,8 @@ static uint32_t do_phy_read(lan9118_state *s, int reg) { + uint32_t val; + switch (reg) { case 0: /* Basic Control */ return s->phy_control; @@ -656,6 +699,13 @@ case 6: /* Auto-neg Expansion */ return 1; /* TODO 17, 18, 27, 29, 30, 31 */ + case 29: /* Interrupt source. */ + val = s->phy_int; + s->phy_int = 0; + phy_update_irq(s); + return val; + case 30: /* Interrupt mask */ + return s->phy_int_mask; default: BADF("PHY read reg %d\n", reg); return 0; @@ -679,7 +729,11 @@ case 4: /* Auto-neg advertisment */ s->phy_advertise = (val & 0x2d7f) | 0x80; break; - /* TODO 17, 18, 27, 29, 30, 31 */ + /* TODO 17, 18, 27, 31 */ + case 30: /* Interrupt mask */ + s->phy_int_mask = val & 0xff; + phy_update_irq(s); + break; default: BADF("PHY write reg %d = 0x%04x\n", reg, val); } @@ -731,6 +785,12 @@ case MAC_FLOW: s->mac_flow = val & 0xffff0000; break; + case MAC_VLAN1: + /* Writing to this register changes a condition for + * FrameTooLong bit in rx_status. Since we do not set + * FrameTooLong anyway, just ignore write to this. + */ + break; default: hw_error("lan9118: Unimplemented MAC register write: %d = 0x%x\n", s->mac_cmd & 0xf, val); @@ -820,6 +880,15 @@ } } +static void lan9118_tick(void *opaque) +{ + lan9118_state *s = (lan9118_state *)opaque; + if (s->int_en & GPT_INT) { + s->int_sts |= GPT_INT; + } + lan9118_update(s); +} + static void lan9118_writel(void *opaque, target_phys_addr_t offset, uint32_t val) { @@ -835,7 +904,7 @@ switch (offset) { case CSR_IRQ_CFG: /* TODO: Implement interrupt deassertion intervals. */ - s->irq_cfg = (val & IRQ_EN); + s->irq_cfg = (s->irq_cfg & IRQ_INT) | (val & IRQ_EN); break; case CSR_INT_STS: s->int_sts &= ~val; @@ -905,6 +974,18 @@ /* Probably just enabling LEDs. */ s->gpio_cfg = val & 0x7777071f; break; + case CSR_GPT_CFG: + if ((s->gpt_cfg ^ val) & GPT_TIMER_EN) { + if (val & GPT_TIMER_EN) { + ptimer_set_count(s->timer, val & 0xffff); + ptimer_run(s->timer, 0); + } else { + ptimer_stop(s->timer); + ptimer_set_count(s->timer, 0xffff); + } + } + s->gpt_cfg = val & (GPT_TIMER_EN | 0xffff); + break; case CSR_WORD_SWAP: /* Ignored because we're in 32-bit mode. */ s->word_swap = val; @@ -928,7 +1009,7 @@ s->afc_cfg = val & 0x00ffffff; break; case CSR_E2P_CMD: - lan9118_eeprom_cmd(s, (val >> 28) & 7, val & 0xff); + lan9118_eeprom_cmd(s, (val >> 28) & 7, val & 0x7f); break; case CSR_E2P_DATA: s->e2p_data = val & 0xff; @@ -988,6 +1069,10 @@ return s->pmt_ctrl; case CSR_GPIO_CFG: return s->gpio_cfg; + case CSR_GPT_CFG: + return s->gpt_cfg; + case CSR_GPT_CNT: + return ptimer_get_count(s->timer); case CSR_WORD_SWAP: return s->word_swap; case CSR_FREE_RUN: @@ -1041,10 +1126,12 @@ static int lan9118_init1(SysBusDevice *dev) { lan9118_state *s = FROM_SYSBUS(lan9118_state, dev); + QEMUBH *bh; int i; s->mmio_index = cpu_register_io_memory(lan9118_readfn, - lan9118_writefn, s); + lan9118_writefn, s, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, 0x100, s->mmio_index); sysbus_init_irq(dev, &s->irq); qemu_macaddr_default_if_unset(&s->conf.macaddr); @@ -1059,6 +1146,11 @@ s->pmt_ctrl = 1; s->txp = &s->tx_packet; + bh = qemu_bh_new(lan9118_tick, s); + s->timer = ptimer_init(bh); + ptimer_set_freq(s->timer, 10000); + ptimer_set_limit(s->timer, 0xffff, 1); + /* ??? Save/restore. */ return 0; } diff -Nru qemu-kvm-0.12.5+noroms/hw/lance.c qemu-kvm-0.14.1/hw/lance.c --- qemu-kvm-0.12.5+noroms/hw/lance.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/lance.c 2011-05-11 13:29:46.000000000 +0000 @@ -40,8 +40,8 @@ #include "qemu-timer.h" #include "qemu_socket.h" #include "sun4m.h" - #include "pcnet.h" +#include "trace.h" typedef struct { SysBusDevice busdev; @@ -59,10 +59,8 @@ uint32_t val) { SysBusPCNetState *d = opaque; -#ifdef PCNET_DEBUG_IO - printf("lance_mem_writew addr=" TARGET_FMT_plx " val=0x%04x\n", addr, - val & 0xffff); -#endif + + trace_lance_mem_writew(addr, val & 0xffff); pcnet_ioport_writew(&d->state, addr, val & 0xffff); } @@ -72,11 +70,7 @@ uint32_t val; val = pcnet_ioport_readw(&d->state, addr); -#ifdef PCNET_DEBUG_IO - printf("lance_mem_readw addr=" TARGET_FMT_plx " val = 0x%04x\n", addr, - val & 0xffff); -#endif - + trace_lance_mem_readw(addr, val & 0xffff); return val & 0xffff; } @@ -124,7 +118,8 @@ PCNetState *s = &d->state; s->mmio_index = - cpu_register_io_memory(lance_mem_read, lance_mem_write, d); + cpu_register_io_memory(lance_mem_read, lance_mem_write, d, + DEVICE_NATIVE_ENDIAN); qdev_init_gpio_in(&dev->qdev, parent_lance_reset, 1); @@ -147,6 +142,7 @@ static SysBusDeviceInfo lance_info = { .init = lance_init, .qdev.name = "lance", + .qdev.fw_name = "ethernet", .qdev.size = sizeof(SysBusPCNetState), .qdev.reset = lance_reset, .qdev.vmsd = &vmstate_lance, diff -Nru qemu-kvm-0.12.5+noroms/hw/leon3.c qemu-kvm-0.14.1/hw/leon3.c --- qemu-kvm-0.12.5+noroms/hw/leon3.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/leon3.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,217 @@ +/* + * QEMU Leon3 System Emulator + * + * Copyright (c) 2010-2011 AdaCore + * + * 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 "hw.h" +#include "qemu-timer.h" +#include "qemu-char.h" +#include "sysemu.h" +#include "boards.h" +#include "loader.h" +#include "elf.h" +#include "trace.h" + +#include "grlib.h" + +/* Default system clock. */ +#define CPU_CLK (40 * 1000 * 1000) + +#define PROM_FILENAME "u-boot.bin" + +#define MAX_PILS 16 + +typedef struct ResetData { + CPUState *env; + uint32_t entry; /* save kernel entry in case of reset */ +} ResetData; + +static void main_cpu_reset(void *opaque) +{ + ResetData *s = (ResetData *)opaque; + CPUState *env = s->env; + + cpu_reset(env); + + env->halted = 0; + env->pc = s->entry; + env->npc = s->entry + 4; +} + +void leon3_irq_ack(void *irq_manager, int intno) +{ + grlib_irqmp_ack((DeviceState *)irq_manager, intno); +} + +static void leon3_set_pil_in(void *opaque, uint32_t pil_in) +{ + CPUState *env = (CPUState *)opaque; + + assert(env != NULL); + + env->pil_in = pil_in; + + if (env->pil_in && (env->interrupt_index == 0 || + (env->interrupt_index & ~15) == TT_EXTINT)) { + unsigned int i; + + for (i = 15; i > 0; i--) { + if (env->pil_in & (1 << i)) { + int old_interrupt = env->interrupt_index; + + env->interrupt_index = TT_EXTINT | i; + if (old_interrupt != env->interrupt_index) { + trace_leon3_set_irq(i); + cpu_interrupt(env, CPU_INTERRUPT_HARD); + } + break; + } + } + } else if (!env->pil_in && (env->interrupt_index & ~15) == TT_EXTINT) { + trace_leon3_reset_irq(env->interrupt_index & 15); + env->interrupt_index = 0; + cpu_reset_interrupt(env, CPU_INTERRUPT_HARD); + } +} + +static void leon3_generic_hw_init(ram_addr_t ram_size, + const char *boot_device, + const char *kernel_filename, + const char *kernel_cmdline, + const char *initrd_filename, + const char *cpu_model) +{ + CPUState *env; + ram_addr_t ram_offset, prom_offset; + int ret; + char *filename; + qemu_irq *cpu_irqs = NULL; + int bios_size; + int prom_size; + ResetData *reset_info; + + /* Init CPU */ + if (!cpu_model) { + cpu_model = "LEON3"; + } + + env = cpu_init(cpu_model); + if (!env) { + fprintf(stderr, "qemu: Unable to find Sparc CPU definition\n"); + exit(1); + } + + cpu_sparc_set_id(env, 0); + + /* Reset data */ + reset_info = qemu_mallocz(sizeof(ResetData)); + reset_info->env = env; + qemu_register_reset(main_cpu_reset, reset_info); + + /* Allocate IRQ manager */ + grlib_irqmp_create(0x80000200, env, &cpu_irqs, MAX_PILS, &leon3_set_pil_in); + + env->qemu_irq_ack = leon3_irq_manager; + + /* Allocate RAM */ + if ((uint64_t)ram_size > (1UL << 30)) { + fprintf(stderr, + "qemu: Too much memory for this machine: %d, maximum 1G\n", + (unsigned int)(ram_size / (1024 * 1024))); + exit(1); + } + + ram_offset = qemu_ram_alloc(NULL, "leon3.ram", ram_size); + cpu_register_physical_memory(0x40000000, ram_size, ram_offset | IO_MEM_RAM); + + /* Allocate BIOS */ + prom_size = 8 * 1024 * 1024; /* 8Mb */ + prom_offset = qemu_ram_alloc(NULL, "Leon3.bios", prom_size); + cpu_register_physical_memory(0x00000000, prom_size, + prom_offset | IO_MEM_ROM); + + /* Load boot prom */ + if (bios_name == NULL) { + bios_name = PROM_FILENAME; + } + filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); + + bios_size = get_image_size(filename); + + if (bios_size > prom_size) { + fprintf(stderr, "qemu: could not load prom '%s': file too big\n", + filename); + exit(1); + } + + if (bios_size > 0) { + ret = load_image_targphys(filename, 0x00000000, bios_size); + if (ret < 0 || ret > prom_size) { + fprintf(stderr, "qemu: could not load prom '%s'\n", filename); + exit(1); + } + } else if (kernel_filename == NULL) { + fprintf(stderr, "Can't read bios image %s\n", filename); + exit(1); + } + + /* Can directly load an application. */ + if (kernel_filename != NULL) { + long kernel_size; + uint64_t entry; + + kernel_size = load_elf(kernel_filename, NULL, NULL, &entry, NULL, NULL, + 1 /* big endian */, ELF_MACHINE, 0); + if (kernel_size < 0) { + fprintf(stderr, "qemu: could not load kernel '%s'\n", + kernel_filename); + exit(1); + } + if (bios_size <= 0) { + /* If there is no bios/monitor, start the application. */ + env->pc = entry; + env->npc = entry + 4; + reset_info->entry = entry; + } + } + + /* Allocate timers */ + grlib_gptimer_create(0x80000300, 2, CPU_CLK, cpu_irqs, 6); + + /* Allocate uart */ + if (serial_hds[0]) { + grlib_apbuart_create(0x80000100, serial_hds[0], cpu_irqs[3]); + } +} + +QEMUMachine leon3_generic_machine = { + .name = "leon3_generic", + .desc = "Leon-3 generic", + .init = leon3_generic_hw_init, + .use_scsi = 0, +}; + +static void leon3_machine_init(void) +{ + qemu_register_machine(&leon3_generic_machine); +} + +machine_init(leon3_machine_init); diff -Nru qemu-kvm-0.12.5+noroms/hw/loader.c qemu-kvm-0.14.1/hw/loader.c --- qemu-kvm-0.12.5+noroms/hw/loader.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/loader.c 2011-05-11 13:29:46.000000000 +0000 @@ -107,7 +107,7 @@ size = get_image_size(filename); if (size > 0) - rom_add_file_fixed(filename, addr); + rom_add_file_fixed(filename, addr, -1); return size; } @@ -276,9 +276,9 @@ #include "elf_ops.h" /* return < 0 if error, otherwise the number of bytes loaded in memory */ -int load_elf(const char *filename, int64_t address_offset, - uint64_t *pentry, uint64_t *lowaddr, uint64_t *highaddr, - int big_endian, int elf_machine, int clear_lsb) +int load_elf(const char *filename, uint64_t (*translate_fn)(void *, uint64_t), + void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr, + uint64_t *highaddr, int big_endian, int elf_machine, int clear_lsb) { int fd, data_order, target_data_order, must_swab, ret; uint8_t e_ident[EI_NIDENT]; @@ -307,16 +307,17 @@ target_data_order = ELFDATA2LSB; } - if (target_data_order != e_ident[EI_DATA]) - return -1; + if (target_data_order != e_ident[EI_DATA]) { + goto fail; + } lseek(fd, 0, SEEK_SET); if (e_ident[EI_CLASS] == ELFCLASS64) { - ret = load_elf64(filename, fd, address_offset, must_swab, pentry, - lowaddr, highaddr, elf_machine, clear_lsb); + ret = load_elf64(filename, fd, translate_fn, translate_opaque, must_swab, + pentry, lowaddr, highaddr, elf_machine, clear_lsb); } else { - ret = load_elf32(filename, fd, address_offset, must_swab, pentry, - lowaddr, highaddr, elf_machine, clear_lsb); + ret = load_elf32(filename, fd, translate_fn, translate_opaque, must_swab, + pentry, lowaddr, highaddr, elf_machine, clear_lsb); } close(fd); @@ -537,7 +538,6 @@ static FWCfgState *fw_cfg; static QTAILQ_HEAD(, Rom) roms = QTAILQ_HEAD_INITIALIZER(roms); -int rom_enable_driver_roms; static void rom_insert(Rom *rom) { @@ -558,10 +558,11 @@ } int rom_add_file(const char *file, const char *fw_dir, - target_phys_addr_t addr) + target_phys_addr_t addr, int32_t bootindex) { Rom *rom; int rc, fd = -1; + char devpath[100]; rom = qemu_mallocz(sizeof(*rom)); rom->name = qemu_strdup(file); @@ -593,8 +594,25 @@ } close(fd); rom_insert(rom); - if (rom->fw_file && fw_cfg) - fw_cfg_add_file(fw_cfg, rom->fw_dir, rom->fw_file, rom->data, rom->romsize); + if (rom->fw_file && fw_cfg) { + const char *basename; + char fw_file_name[56]; + + basename = strrchr(rom->fw_file, '/'); + if (basename) { + basename++; + } else { + basename = rom->fw_file; + } + snprintf(fw_file_name, sizeof(fw_file_name), "%s/%s", rom->fw_dir, + basename); + fw_cfg_add_file(fw_cfg, fw_file_name, rom->data, rom->romsize); + snprintf(devpath, sizeof(devpath), "/rom@%s", fw_file_name); + } else { + snprintf(devpath, sizeof(devpath), "/rom@" TARGET_FMT_plx, addr); + } + + add_boot_device_path(bootindex, NULL, devpath); return 0; err: @@ -624,16 +642,12 @@ int rom_add_vga(const char *file) { - if (!rom_enable_driver_roms) - return 0; - return rom_add_file(file, "vgaroms", 0); + return rom_add_file(file, "vgaroms", 0, -1); } -int rom_add_option(const char *file) +int rom_add_option(const char *file, int32_t bootindex) { - if (!rom_enable_driver_roms) - return 0; - return rom_add_file(file, "genroms", 0); + return rom_add_file(file, "genroms", 0, bootindex); } static void rom_reset(void *unused) @@ -738,11 +752,6 @@ s = rom->data; l = rom->romsize; - if (rom->addr < addr) { - d = dest; - s += (addr - rom->addr); - l -= (addr - rom->addr); - } if ((d + l) > (dest + size)) { l = dest - d; } diff -Nru qemu-kvm-0.12.5+noroms/hw/loader.h qemu-kvm-0.14.1/hw/loader.h --- qemu-kvm-0.12.5+noroms/hw/loader.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/loader.h 2011-05-11 13:29:46.000000000 +0000 @@ -5,9 +5,10 @@ int get_image_size(const char *filename); int load_image(const char *filename, uint8_t *addr); /* deprecated */ int load_image_targphys(const char *filename, target_phys_addr_t, int max_sz); -int load_elf(const char *filename, int64_t address_offset, - uint64_t *pentry, uint64_t *lowaddr, uint64_t *highaddr, - int big_endian, int elf_machine, int clear_lsb); +int load_elf(const char *filename, uint64_t (*translate_fn)(void *, uint64_t), + void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr, + uint64_t *highaddr, int big_endian, int elf_machine, + int clear_lsb); int load_aout(const char *filename, target_phys_addr_t addr, int max_sz, int bswap_needed, target_phys_addr_t target_page_size); int load_uimage(const char *filename, target_phys_addr_t *ep, @@ -21,7 +22,7 @@ int rom_add_file(const char *file, const char *fw_dir, - target_phys_addr_t addr); + target_phys_addr_t addr, int32_t bootindex); int rom_add_blob(const char *name, const void *blob, size_t len, target_phys_addr_t addr); int rom_load_all(void); @@ -30,8 +31,8 @@ void *rom_ptr(target_phys_addr_t addr); void do_info_roms(Monitor *mon); -#define rom_add_file_fixed(_f, _a) \ - rom_add_file(_f, NULL, _a) +#define rom_add_file_fixed(_f, _a, _i) \ + rom_add_file(_f, NULL, _a, _i) #define rom_add_blob_fixed(_f, _b, _l, _a) \ rom_add_blob(_f, _b, _l, _a) @@ -41,8 +42,7 @@ #define PC_ROM_ALIGN 0x800 #define PC_ROM_SIZE (PC_ROM_MAX - PC_ROM_MIN_VGA) -extern int rom_enable_driver_roms; int rom_add_vga(const char *file); -int rom_add_option(const char *file); +int rom_add_option(const char *file, int32_t bootindex); #endif diff -Nru qemu-kvm-0.12.5+noroms/hw/lsi53c895a.c qemu-kvm-0.14.1/hw/lsi53c895a.c --- qemu-kvm-0.12.5+noroms/hw/lsi53c895a.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/lsi53c895a.c 2011-05-11 13:29:46.000000000 +0000 @@ -175,7 +175,6 @@ typedef struct lsi_request { uint32_t tag; - SCSIDevice *dev; uint32_t dma_len; uint8_t *dma_buf; uint32_t pending; @@ -202,7 +201,6 @@ * 3 if a DMA operation is in progress. */ int waiting; SCSIBus bus; - SCSIDevice *select_dev; int current_lun; /* The tag is a combination of the device ID and the SCSI tag. */ uint32_t select_tag; @@ -283,9 +281,13 @@ static void lsi_soft_reset(LSIState *s) { + lsi_request *p; + DPRINTF("Reset\n"); s->carry = 0; + s->msg_action = 0; + s->msg_len = 0; s->waiting = 0; s->dsa = 0; s->dnad = 0; @@ -294,8 +296,8 @@ memset(s->scratch, 0, sizeof(s->scratch)); s->istat0 = 0; s->istat1 = 0; - s->dcmd = 0; - s->dstat = 0; + s->dcmd = 0x40; + s->dstat = LSI_DSTAT_DFE; s->dien = 0; s->sist0 = 0; s->sist1 = 0; @@ -304,7 +306,7 @@ s->mbox0 = 0; s->mbox1 = 0; s->dfifo = 0; - s->ctest2 = 0; + s->ctest2 = LSI_CTEST2_DACK; s->ctest3 = 0; s->ctest4 = 0; s->ctest5 = 0; @@ -323,6 +325,8 @@ s->scid = 7; s->sxfer = 0; s->socl = 0; + s->sdid = 0; + s->ssid = 0; s->stest1 = 0; s->stest2 = 0; s->stest3 = 0; @@ -345,6 +349,15 @@ s->sbc = 0; s->csbc = 0; s->sbr = 0; + while (!QTAILQ_EMPTY(&s->queue)) { + p = QTAILQ_FIRST(&s->queue); + QTAILQ_REMOVE(&s->queue, p, next); + qemu_free(p); + } + if (s->current) { + qemu_free(s->current); + s->current = NULL; + } } static int lsi_dma_40bit(LSIState *s) @@ -477,10 +490,10 @@ { /* Trigger a phase mismatch. */ if (s->ccntl0 & LSI_CCNTL0_ENPMJ) { - if ((s->ccntl0 & LSI_CCNTL0_PMJCTL) || out) { - s->dsp = s->pmjad1; + if ((s->ccntl0 & LSI_CCNTL0_PMJCTL)) { + s->dsp = out ? s->pmjad1 : s->pmjad2; } else { - s->dsp = s->pmjad2; + s->dsp = (s->scntl2 & LSI_SCNTL2_WSR ? s->pmjad2 : s->pmjad1); } DPRINTF("Data phase mismatch jump to %08x\n", s->dsp); } else { @@ -503,11 +516,25 @@ } } +static void lsi_disconnect(LSIState *s) +{ + s->scntl1 &= ~LSI_SCNTL1_CON; + s->sstat1 &= ~PHASE_MASK; +} + +static void lsi_bad_selection(LSIState *s, uint32_t id) +{ + DPRINTF("Selected absent target %d\n", id); + lsi_script_scsi_interrupt(s, 0, LSI_SIST1_STO); + lsi_disconnect(s); +} + /* Initiate a SCSI layer data transfer. */ static void lsi_do_dma(LSIState *s, int out) { - uint32_t count; + uint32_t count, id; target_phys_addr_t addr; + SCSIDevice *dev; assert(s->current); if (!s->current->dma_len) { @@ -516,6 +543,13 @@ return; } + id = (s->current->tag >> 8) & 0xf; + dev = s->bus.devs[id]; + if (!dev) { + lsi_bad_selection(s, id); + return; + } + count = s->dbc; if (count > s->current->dma_len) count = s->current->dma_len; @@ -535,8 +569,7 @@ s->dbc -= count; if (s->current->dma_buf == NULL) { - s->current->dma_buf = s->current->dev->info->get_buf(s->current->dev, - s->current->tag); + s->current->dma_buf = dev->info->get_buf(dev, s->current->tag); } /* ??? Set SFBR to first data byte. */ @@ -550,10 +583,10 @@ s->current->dma_buf = NULL; if (out) { /* Write the data. */ - s->current->dev->info->write_data(s->current->dev, s->current->tag); + dev->info->write_data(dev, s->current->tag); } else { /* Request any remaining data. */ - s->current->dev->info->read_data(s->current->dev, s->current->tag); + dev->info->read_data(dev, s->current->tag); } } else { s->current->dma_buf += count; @@ -567,7 +600,7 @@ { lsi_request *p = s->current; - DPRINTF("Queueing tag=0x%x\n", s->current_tag); + DPRINTF("Queueing tag=0x%x\n", p->tag); assert(s->current != NULL); assert(s->current->dma_len == 0); QTAILQ_INSERT_TAIL(&s->queue, s->current, next); @@ -600,7 +633,7 @@ id = (p->tag >> 8) & 0xf; s->ssid = id | 0x80; /* LSI53C700 Family Compatibility, see LSI53C895A 4-73 */ - if (!s->dcntl & LSI_DCNTL_COM) { + if (!(s->dcntl & LSI_DCNTL_COM)) { s->sfbr = 1 << (id & 0x7); } DPRINTF("Reselected target %d\n", id); @@ -700,7 +733,9 @@ static void lsi_do_command(LSIState *s) { + SCSIDevice *dev; uint8_t buf[16]; + uint32_t id; int n; DPRINTF("Send command len=%d\n", s->dbc); @@ -710,19 +745,24 @@ s->sfbr = buf[0]; s->command_complete = 0; + id = (s->select_tag >> 8) & 0xf; + dev = s->bus.devs[id]; + if (!dev) { + lsi_bad_selection(s, id); + return; + } + assert(s->current == NULL); s->current = qemu_mallocz(sizeof(lsi_request)); s->current->tag = s->select_tag; - s->current->dev = s->select_dev; - n = s->current->dev->info->send_command(s->current->dev, s->current->tag, buf, - s->current_lun); + n = dev->info->send_command(dev, s->current->tag, buf, s->current_lun); if (n > 0) { lsi_set_phase(s, PHASE_DI); - s->current->dev->info->read_data(s->current->dev, s->current->tag); + dev->info->read_data(dev, s->current->tag); } else if (n < 0) { lsi_set_phase(s, PHASE_DO); - s->current->dev->info->write_data(s->current->dev, s->current->tag); + dev->info->write_data(dev, s->current->tag); } if (!s->command_complete) { @@ -756,12 +796,6 @@ lsi_add_msg_byte(s, 0); /* COMMAND COMPLETE */ } -static void lsi_disconnect(LSIState *s) -{ - s->scntl1 &= ~LSI_SCNTL1_CON; - s->sstat1 &= ~PHASE_MASK; -} - static void lsi_do_msgin(LSIState *s) { int len; @@ -808,10 +842,29 @@ return data; } +/* Skip the next n bytes during a MSGOUT phase. */ +static void lsi_skip_msgbytes(LSIState *s, unsigned int n) +{ + s->dnad += n; + s->dbc -= n; +} + static void lsi_do_msgout(LSIState *s) { uint8_t msg; int len; + uint32_t current_tag; + SCSIDevice *current_dev; + lsi_request *p, *p_next; + int id; + + if (s->current) { + current_tag = s->current->tag; + } else { + current_tag = s->select_tag; + } + id = (current_tag >> 8) & 0xf; + current_dev = s->bus.devs[id]; DPRINTF("MSG out len=%d\n", s->dbc); while (s->dbc) { @@ -830,15 +883,16 @@ case 0x01: len = lsi_get_msgbyte(s); msg = lsi_get_msgbyte(s); + (void)len; /* avoid a warning about unused variable*/ DPRINTF("Extended message 0x%x (len %d)\n", msg, len); switch (msg) { case 1: DPRINTF("SDTR (ignored)\n"); - s->dbc -= 2; + lsi_skip_msgbytes(s, 2); break; case 3: DPRINTF("WDTR (ignored)\n"); - s->dbc -= 1; + lsi_skip_msgbytes(s, 1); break; default: goto bad; @@ -846,7 +900,7 @@ break; case 0x20: /* SIMPLE queue */ s->select_tag |= lsi_get_msgbyte(s) | LSI_TAG_VALID; - DPRINTF("SIMPLE queue tag=0x%x\n", s->current_tag & 0xff); + DPRINTF("SIMPLE queue tag=0x%x\n", s->select_tag & 0xff); break; case 0x21: /* HEAD of queue */ BADF("HEAD queue not implemented\n"); @@ -856,6 +910,51 @@ BADF("ORDERED queue not implemented\n"); s->select_tag |= lsi_get_msgbyte(s) | LSI_TAG_VALID; break; + case 0x0d: + /* The ABORT TAG message clears the current I/O process only. */ + DPRINTF("MSG: ABORT TAG tag=0x%x\n", current_tag); + current_dev->info->cancel_io(current_dev, current_tag); + lsi_disconnect(s); + break; + case 0x06: + case 0x0e: + case 0x0c: + /* The ABORT message clears all I/O processes for the selecting + initiator on the specified logical unit of the target. */ + if (msg == 0x06) { + DPRINTF("MSG: ABORT tag=0x%x\n", current_tag); + } + /* The CLEAR QUEUE message clears all I/O processes for all + initiators on the specified logical unit of the target. */ + if (msg == 0x0e) { + DPRINTF("MSG: CLEAR QUEUE tag=0x%x\n", current_tag); + } + /* The BUS DEVICE RESET message clears all I/O processes for all + initiators on all logical units of the target. */ + if (msg == 0x0c) { + DPRINTF("MSG: BUS DEVICE RESET tag=0x%x\n", current_tag); + } + + /* clear the current I/O process */ + current_dev->info->cancel_io(current_dev, current_tag); + + /* As the current implemented devices scsi_disk and scsi_generic + only support one LUN, we don't need to keep track of LUNs. + Clearing I/O processes for other initiators could be possible + for scsi_generic by sending a SG_SCSI_RESET to the /dev/sgX + device, but this is currently not implemented (and seems not + to be really necessary). So let's simply clear all queued + commands for the current device: */ + id = current_tag & 0x0000ff00; + QTAILQ_FOREACH_SAFE(p, &s->queue, next, p_next) { + if ((p->tag & 0x0000ff00) == id) { + current_dev->info->cancel_io(current_dev, p->tag); + QTAILQ_REMOVE(&s->queue, p, next); + } + } + + lsi_disconnect(s); + break; default: if ((msg & 0x80) == 0) { goto bad; @@ -1077,9 +1176,7 @@ s->sstat0 |= LSI_SSTAT0_WOA; s->scntl1 &= ~LSI_SCNTL1_IARB; if (id >= LSI_MAX_DEVS || !s->bus.devs[id]) { - DPRINTF("Selected absent target %d\n", id); - lsi_script_scsi_interrupt(s, 0, LSI_SIST1_STO); - lsi_disconnect(s); + lsi_bad_selection(s, id); break; } DPRINTF("Selected target %d%s\n", @@ -1087,7 +1184,6 @@ /* ??? Linux drivers compain when this is set. Maybe it only applies in low-level mode (unimplemented). lsi_script_scsi_interrupt(s, LSI_SIST0_CMP, 0); */ - s->select_dev = s->bus.devs[id]; s->select_tag = id << 8; s->scntl1 |= LSI_SCNTL1_CON; if (insn & (1 << 3)) { @@ -1559,8 +1655,19 @@ BADF("Immediate Arbritration not implemented\n"); } if (val & LSI_SCNTL1_RST) { - s->sstat0 |= LSI_SSTAT0_RST; - lsi_script_scsi_interrupt(s, LSI_SIST0_RST, 0); + if (!(s->sstat0 & LSI_SSTAT0_RST)) { + DeviceState *dev; + int id; + + for (id = 0; id < s->bus.ndev; id++) { + if (s->bus.devs[id]) { + dev = &s->bus.devs[id]->qdev; + dev->info->reset(dev); + } + } + s->sstat0 |= LSI_SSTAT0_RST; + lsi_script_scsi_interrupt(s, LSI_SIST0_RST, 0); + } } else { s->sstat0 &= ~LSI_SSTAT0_RST; } @@ -1887,7 +1994,7 @@ val = s->script_ram[addr >> 2]; if (addr & 2) val >>= 16; - return le16_to_cpu(val); + return val; } static uint32_t lsi_ram_readl(void *opaque, target_phys_addr_t addr) @@ -1895,7 +2002,7 @@ LSIState *s = opaque; addr &= 0x1fff; - return le32_to_cpu(s->script_ram[addr >> 2]); + return s->script_ram[addr >> 2]; } static CPUReadMemoryFunc * const lsi_ram_readfn[3] = { @@ -1996,6 +2103,13 @@ cpu_register_physical_memory(addr + 0, 0x400, s->mmio_io_addr); } +static void lsi_scsi_reset(DeviceState *dev) +{ + LSIState *s = DO_UPCAST(LSIState, dev.qdev, dev); + + lsi_soft_reset(s); +} + static void lsi_pre_save(void *opaque) { LSIState *s = opaque; @@ -2114,31 +2228,32 @@ /* PCI base class code */ pci_config_set_class(pci_conf, PCI_CLASS_STORAGE_SCSI); /* PCI subsystem ID */ - pci_conf[0x2e] = 0x00; - pci_conf[0x2f] = 0x10; + pci_conf[PCI_SUBSYSTEM_ID] = 0x00; + pci_conf[PCI_SUBSYSTEM_ID + 1] = 0x10; /* PCI latency timer = 255 */ - pci_conf[0x0d] = 0xff; + pci_conf[PCI_LATENCY_TIMER] = 0xff; + /* TODO: RST# value should be 0 */ /* Interrupt pin 1 */ - pci_conf[0x3d] = 0x01; + pci_conf[PCI_INTERRUPT_PIN] = 0x01; s->mmio_io_addr = cpu_register_io_memory(lsi_mmio_readfn, - lsi_mmio_writefn, s); + lsi_mmio_writefn, s, + DEVICE_NATIVE_ENDIAN); s->ram_io_addr = cpu_register_io_memory(lsi_ram_readfn, - lsi_ram_writefn, s); + lsi_ram_writefn, s, + DEVICE_NATIVE_ENDIAN); - pci_register_bar((struct PCIDevice *)s, 0, 256, + pci_register_bar(&s->dev, 0, 256, PCI_BASE_ADDRESS_SPACE_IO, lsi_io_mapfunc); - pci_register_bar((struct PCIDevice *)s, 1, 0x400, + pci_register_bar(&s->dev, 1, 0x400, PCI_BASE_ADDRESS_SPACE_MEMORY, lsi_mmio_mapfunc); - pci_register_bar((struct PCIDevice *)s, 2, 0x2000, + pci_register_bar(&s->dev, 2, 0x2000, PCI_BASE_ADDRESS_SPACE_MEMORY, lsi_ram_mapfunc); QTAILQ_INIT(&s->queue); - lsi_soft_reset(s); - scsi_bus_new(&s->bus, &dev->qdev, 1, LSI_MAX_DEVS, lsi_command_complete); if (!dev->qdev.hotplugged) { - scsi_bus_legacy_handle_cmdline(&s->bus); + return scsi_bus_legacy_handle_cmdline(&s->bus); } return 0; } @@ -2147,6 +2262,7 @@ .qdev.name = "lsi53c895a", .qdev.alias = "lsi", .qdev.size = sizeof(LSIState), + .qdev.reset = lsi_scsi_reset, .qdev.vmsd = &vmstate_lsi_scsi, .init = lsi_scsi_init, .exit = lsi_scsi_uninit, diff -Nru qemu-kvm-0.12.5+noroms/hw/m48t59.c qemu-kvm-0.14.1/hw/m48t59.c --- qemu-kvm-0.12.5+noroms/hw/m48t59.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/m48t59.c 2011-05-11 13:29:46.000000000 +0000 @@ -49,7 +49,7 @@ * http://www.st.com/stonline/products/literature/od/7001/m48t59y.pdf */ -struct m48t59_t { +struct M48t59State { /* Model parameters */ uint32_t type; // 2 = m48t02, 8 = m48t08, 59 = m48t59 /* Hardware parameters */ @@ -71,12 +71,12 @@ typedef struct M48t59ISAState { ISADevice busdev; - m48t59_t state; + M48t59State state; } M48t59ISAState; typedef struct M48t59SysBusState { SysBusDevice busdev; - m48t59_t state; + M48t59State state; } M48t59SysBusState; /* Fake timer functions */ @@ -86,7 +86,7 @@ { struct tm tm; uint64_t next_time; - m48t59_t *NVRAM = opaque; + M48t59State *NVRAM = opaque; qemu_set_irq(NVRAM->IRQ, 1); if ((NVRAM->buffer[0x1FF5] & 0x80) == 0 && @@ -128,7 +128,7 @@ qemu_set_irq(NVRAM->IRQ, 0); } -static void set_alarm (m48t59_t *NVRAM) +static void set_alarm(M48t59State *NVRAM) { int diff; if (NVRAM->alrm_timer != NULL) { @@ -140,12 +140,12 @@ } /* RTC management helpers */ -static inline void get_time (m48t59_t *NVRAM, struct tm *tm) +static inline void get_time(M48t59State *NVRAM, struct tm *tm) { qemu_get_timedate(tm, NVRAM->time_offset); } -static void set_time (m48t59_t *NVRAM, struct tm *tm) +static void set_time(M48t59State *NVRAM, struct tm *tm) { NVRAM->time_offset = qemu_timedate_diff(tm); set_alarm(NVRAM); @@ -154,7 +154,7 @@ /* Watchdog management */ static void watchdog_cb (void *opaque) { - m48t59_t *NVRAM = opaque; + M48t59State *NVRAM = opaque; NVRAM->buffer[0x1FF0] |= 0x80; if (NVRAM->buffer[0x1FF7] & 0x80) { @@ -168,7 +168,7 @@ } } -static void set_up_watchdog (m48t59_t *NVRAM, uint8_t value) +static void set_up_watchdog(M48t59State *NVRAM, uint8_t value) { uint64_t interval; /* in 1/16 seconds */ @@ -186,7 +186,7 @@ /* Direct access to NVRAM */ void m48t59_write (void *opaque, uint32_t addr, uint32_t val) { - m48t59_t *NVRAM = opaque; + M48t59State *NVRAM = opaque; struct tm tm; int tmp; @@ -354,7 +354,7 @@ uint32_t m48t59_read (void *opaque, uint32_t addr) { - m48t59_t *NVRAM = opaque; + M48t59State *NVRAM = opaque; struct tm tm; uint32_t retval = 0xFF; @@ -461,14 +461,14 @@ void m48t59_set_addr (void *opaque, uint32_t addr) { - m48t59_t *NVRAM = opaque; + M48t59State *NVRAM = opaque; NVRAM->addr = addr; } void m48t59_toggle_lock (void *opaque, int lock) { - m48t59_t *NVRAM = opaque; + M48t59State *NVRAM = opaque; NVRAM->lock ^= 1 << lock; } @@ -476,7 +476,7 @@ /* IO access to NVRAM */ static void NVRAM_writeb (void *opaque, uint32_t addr, uint32_t val) { - m48t59_t *NVRAM = opaque; + M48t59State *NVRAM = opaque; addr -= NVRAM->io_base; NVRAM_PRINTF("%s: 0x%08x => 0x%08x\n", __func__, addr, val); @@ -500,7 +500,7 @@ static uint32_t NVRAM_readb (void *opaque, uint32_t addr) { - m48t59_t *NVRAM = opaque; + M48t59State *NVRAM = opaque; uint32_t retval; addr -= NVRAM->io_base; @@ -519,14 +519,14 @@ static void nvram_writeb (void *opaque, target_phys_addr_t addr, uint32_t value) { - m48t59_t *NVRAM = opaque; + M48t59State *NVRAM = opaque; m48t59_write(NVRAM, addr, value & 0xff); } static void nvram_writew (void *opaque, target_phys_addr_t addr, uint32_t value) { - m48t59_t *NVRAM = opaque; + M48t59State *NVRAM = opaque; m48t59_write(NVRAM, addr, (value >> 8) & 0xff); m48t59_write(NVRAM, addr + 1, value & 0xff); @@ -534,7 +534,7 @@ static void nvram_writel (void *opaque, target_phys_addr_t addr, uint32_t value) { - m48t59_t *NVRAM = opaque; + M48t59State *NVRAM = opaque; m48t59_write(NVRAM, addr, (value >> 24) & 0xff); m48t59_write(NVRAM, addr + 1, (value >> 16) & 0xff); @@ -544,7 +544,7 @@ static uint32_t nvram_readb (void *opaque, target_phys_addr_t addr) { - m48t59_t *NVRAM = opaque; + M48t59State *NVRAM = opaque; uint32_t retval; retval = m48t59_read(NVRAM, addr); @@ -553,7 +553,7 @@ static uint32_t nvram_readw (void *opaque, target_phys_addr_t addr) { - m48t59_t *NVRAM = opaque; + M48t59State *NVRAM = opaque; uint32_t retval; retval = m48t59_read(NVRAM, addr) << 8; @@ -563,7 +563,7 @@ static uint32_t nvram_readl (void *opaque, target_phys_addr_t addr) { - m48t59_t *NVRAM = opaque; + M48t59State *NVRAM = opaque; uint32_t retval; retval = m48t59_read(NVRAM, addr) << 24; @@ -587,7 +587,7 @@ static void m48t59_save(QEMUFile *f, void *opaque) { - m48t59_t *s = opaque; + M48t59State *s = opaque; qemu_put_8s(f, &s->lock); qemu_put_be16s(f, &s->addr); @@ -596,7 +596,7 @@ static int m48t59_load(QEMUFile *f, void *opaque, int version_id) { - m48t59_t *s = opaque; + M48t59State *s = opaque; if (version_id != 1) return -EINVAL; @@ -608,7 +608,7 @@ return 0; } -static void m48t59_reset_common(m48t59_t *NVRAM) +static void m48t59_reset_common(M48t59State *NVRAM) { NVRAM->addr = 0; NVRAM->lock = 0; @@ -622,7 +622,7 @@ static void m48t59_reset_isa(DeviceState *d) { M48t59ISAState *isa = container_of(d, M48t59ISAState, busdev.qdev); - m48t59_t *NVRAM = &isa->state; + M48t59State *NVRAM = &isa->state; m48t59_reset_common(NVRAM); } @@ -630,19 +630,19 @@ static void m48t59_reset_sysbus(DeviceState *d) { M48t59SysBusState *sys = container_of(d, M48t59SysBusState, busdev.qdev); - m48t59_t *NVRAM = &sys->state; + M48t59State *NVRAM = &sys->state; m48t59_reset_common(NVRAM); } /* Initialisation routine */ -m48t59_t *m48t59_init (qemu_irq IRQ, target_phys_addr_t mem_base, - uint32_t io_base, uint16_t size, - int type) +M48t59State *m48t59_init(qemu_irq IRQ, target_phys_addr_t mem_base, + uint32_t io_base, uint16_t size, int type) { DeviceState *dev; SysBusDevice *s; M48t59SysBusState *d; + M48t59State *state; dev = qdev_create(NULL, "m48t59"); qdev_prop_set_uint32(dev, "type", type); @@ -650,25 +650,25 @@ qdev_prop_set_uint32(dev, "io_base", io_base); qdev_init_nofail(dev); s = sysbus_from_qdev(dev); + d = FROM_SYSBUS(M48t59SysBusState, s); + state = &d->state; sysbus_connect_irq(s, 0, IRQ); if (io_base != 0) { - register_ioport_read(io_base, 0x04, 1, NVRAM_readb, s); - register_ioport_write(io_base, 0x04, 1, NVRAM_writeb, s); + register_ioport_read(io_base, 0x04, 1, NVRAM_readb, state); + register_ioport_write(io_base, 0x04, 1, NVRAM_writeb, state); } if (mem_base != 0) { sysbus_mmio_map(s, 0, mem_base); } - d = FROM_SYSBUS(M48t59SysBusState, s); - - return &d->state; + return state; } -m48t59_t *m48t59_init_isa(uint32_t io_base, uint16_t size, int type) +M48t59State *m48t59_init_isa(uint32_t io_base, uint16_t size, int type) { M48t59ISAState *d; ISADevice *dev; - m48t59_t *s; + M48t59State *s; dev = isa_create("m48t59_isa"); qdev_prop_set_uint32(&dev->qdev, "type", type); @@ -681,12 +681,13 @@ if (io_base != 0) { register_ioport_read(io_base, 0x04, 1, NVRAM_readb, s); register_ioport_write(io_base, 0x04, 1, NVRAM_writeb, s); + isa_init_ioport_range(dev, io_base, 4); } return s; } -static void m48t59_init_common(m48t59_t *s) +static void m48t59_init_common(M48t59State *s) { s->buffer = qemu_mallocz(s->size); if (s->type == 59) { @@ -695,13 +696,13 @@ } qemu_get_timedate(&s->alarm, 0); - register_savevm("m48t59", -1, 1, m48t59_save, m48t59_load, s); + register_savevm(NULL, "m48t59", -1, 1, m48t59_save, m48t59_load, s); } static int m48t59_init_isa1(ISADevice *dev) { M48t59ISAState *d = DO_UPCAST(M48t59ISAState, busdev, dev); - m48t59_t *s = &d->state; + M48t59State *s = &d->state; isa_init_irq(dev, &s->IRQ, 8); m48t59_init_common(s); @@ -712,12 +713,13 @@ static int m48t59_init1(SysBusDevice *dev) { M48t59SysBusState *d = FROM_SYSBUS(M48t59SysBusState, dev); - m48t59_t *s = &d->state; + M48t59State *s = &d->state; int mem_index; sysbus_init_irq(dev, &s->IRQ); - mem_index = cpu_register_io_memory(nvram_read, nvram_write, s); + mem_index = cpu_register_io_memory(nvram_read, nvram_write, s, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, s->size, mem_index); m48t59_init_common(s); diff -Nru qemu-kvm-0.12.5+noroms/hw/mac_dbdma.c qemu-kvm-0.14.1/hw/mac_dbdma.c --- qemu-kvm-0.12.5+noroms/hw/mac_dbdma.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/mac_dbdma.c 2011-05-11 13:29:46.000000000 +0000 @@ -184,19 +184,19 @@ static void dbdma_cmdptr_load(DBDMA_channel *ch) { DBDMA_DPRINTF("dbdma_cmdptr_load 0x%08x\n", - be32_to_cpu(ch->regs[DBDMA_CMDPTR_LO])); - cpu_physical_memory_read(be32_to_cpu(ch->regs[DBDMA_CMDPTR_LO]), + ch->regs[DBDMA_CMDPTR_LO]); + cpu_physical_memory_read(ch->regs[DBDMA_CMDPTR_LO], (uint8_t*)&ch->current, sizeof(dbdma_cmd)); } static void dbdma_cmdptr_save(DBDMA_channel *ch) { DBDMA_DPRINTF("dbdma_cmdptr_save 0x%08x\n", - be32_to_cpu(ch->regs[DBDMA_CMDPTR_LO])); + ch->regs[DBDMA_CMDPTR_LO]); DBDMA_DPRINTF("xfer_status 0x%08x res_count 0x%04x\n", le16_to_cpu(ch->current.xfer_status), le16_to_cpu(ch->current.res_count)); - cpu_physical_memory_write(be32_to_cpu(ch->regs[DBDMA_CMDPTR_LO]), + cpu_physical_memory_write(ch->regs[DBDMA_CMDPTR_LO], (uint8_t*)&ch->current, sizeof(dbdma_cmd)); } @@ -204,8 +204,8 @@ { DBDMA_DPRINTF("kill_channel\n"); - ch->regs[DBDMA_STATUS] |= cpu_to_be32(DEAD); - ch->regs[DBDMA_STATUS] &= cpu_to_be32(~ACTIVE); + ch->regs[DBDMA_STATUS] |= DEAD; + ch->regs[DBDMA_STATUS] &= ~ACTIVE; qemu_irq_raise(ch->irq); } @@ -230,10 +230,10 @@ return; } - status = be32_to_cpu(ch->regs[DBDMA_STATUS]) & DEVSTAT; + status = ch->regs[DBDMA_STATUS] & DEVSTAT; - sel_mask = (be32_to_cpu(ch->regs[DBDMA_INTR_SEL]) >> 16) & 0x0f; - sel_value = be32_to_cpu(ch->regs[DBDMA_INTR_SEL]) & 0x0f; + sel_mask = (ch->regs[DBDMA_INTR_SEL] >> 16) & 0x0f; + sel_value = ch->regs[DBDMA_INTR_SEL] & 0x0f; cond = (status & sel_mask) == (sel_value & sel_mask); @@ -268,10 +268,10 @@ return 1; } - status = be32_to_cpu(ch->regs[DBDMA_STATUS]) & DEVSTAT; + status = ch->regs[DBDMA_STATUS] & DEVSTAT; - sel_mask = (be32_to_cpu(ch->regs[DBDMA_WAIT_SEL]) >> 16) & 0x0f; - sel_value = be32_to_cpu(ch->regs[DBDMA_WAIT_SEL]) & 0x0f; + sel_mask = (ch->regs[DBDMA_WAIT_SEL] >> 16) & 0x0f; + sel_value = ch->regs[DBDMA_WAIT_SEL] & 0x0f; cond = (status & sel_mask) == (sel_value & sel_mask); @@ -292,10 +292,10 @@ { uint32_t cp; - ch->regs[DBDMA_STATUS] &= cpu_to_be32(~BT); + ch->regs[DBDMA_STATUS] &= ~BT; - cp = be32_to_cpu(ch->regs[DBDMA_CMDPTR_LO]); - ch->regs[DBDMA_CMDPTR_LO] = cpu_to_be32(cp + sizeof(dbdma_cmd)); + cp = ch->regs[DBDMA_CMDPTR_LO]; + ch->regs[DBDMA_CMDPTR_LO] = cp + sizeof(dbdma_cmd); dbdma_cmdptr_load(ch); } @@ -304,7 +304,7 @@ dbdma_cmd *current = &ch->current; ch->regs[DBDMA_CMDPTR_LO] = current->cmd_dep; - ch->regs[DBDMA_STATUS] |= cpu_to_be32(BT); + ch->regs[DBDMA_STATUS] |= BT; dbdma_cmdptr_load(ch); } @@ -331,10 +331,10 @@ return; } - status = be32_to_cpu(ch->regs[DBDMA_STATUS]) & DEVSTAT; + status = ch->regs[DBDMA_STATUS] & DEVSTAT; - sel_mask = (be32_to_cpu(ch->regs[DBDMA_BRANCH_SEL]) >> 16) & 0x0f; - sel_value = be32_to_cpu(ch->regs[DBDMA_BRANCH_SEL]) & 0x0f; + sel_mask = (ch->regs[DBDMA_BRANCH_SEL] >> 16) & 0x0f; + sel_value = ch->regs[DBDMA_BRANCH_SEL] & 0x0f; cond = (status & sel_mask) == (sel_value & sel_mask); @@ -365,19 +365,19 @@ if (conditional_wait(ch)) goto wait; - current->xfer_status = cpu_to_le16(be32_to_cpu(ch->regs[DBDMA_STATUS])); - current->res_count = cpu_to_le16(be32_to_cpu(io->len)); + current->xfer_status = cpu_to_le16(ch->regs[DBDMA_STATUS]); + current->res_count = cpu_to_le16(io->len); dbdma_cmdptr_save(ch); if (io->is_last) - ch->regs[DBDMA_STATUS] &= cpu_to_be32(~FLUSH); + ch->regs[DBDMA_STATUS] &= ~FLUSH; conditional_interrupt(ch); conditional_branch(ch); wait: ch->processing = 0; - if ((ch->regs[DBDMA_STATUS] & cpu_to_be32(RUN)) && - (ch->regs[DBDMA_STATUS] & cpu_to_be32(ACTIVE))) + if ((ch->regs[DBDMA_STATUS] & RUN) && + (ch->regs[DBDMA_STATUS] & ACTIVE)) channel_run(ch); } @@ -402,7 +402,9 @@ ch->io.dma_end = dbdma_end; ch->io.is_dma_out = 1; ch->processing = 1; - ch->rw(&ch->io); + if (ch->rw) { + ch->rw(&ch->io); + } } static void start_input(DBDMA_channel *ch, int key, uint32_t addr, @@ -425,7 +427,9 @@ ch->io.dma_end = dbdma_end; ch->io.is_dma_out = 0; ch->processing = 1; - ch->rw(&ch->io); + if (ch->rw) { + ch->rw(&ch->io); + } } static void load_word(DBDMA_channel *ch, int key, uint32_t addr, @@ -456,9 +460,9 @@ if (conditional_wait(ch)) goto wait; - current->xfer_status = cpu_to_le16(be32_to_cpu(ch->regs[DBDMA_STATUS])); + current->xfer_status = cpu_to_le16(ch->regs[DBDMA_STATUS]); dbdma_cmdptr_save(ch); - ch->regs[DBDMA_STATUS] &= cpu_to_be32(~FLUSH); + ch->regs[DBDMA_STATUS] &= ~FLUSH; conditional_interrupt(ch); next(ch); @@ -494,9 +498,9 @@ if (conditional_wait(ch)) goto wait; - current->xfer_status = cpu_to_le16(be32_to_cpu(ch->regs[DBDMA_STATUS])); + current->xfer_status = cpu_to_le16(ch->regs[DBDMA_STATUS]); dbdma_cmdptr_save(ch); - ch->regs[DBDMA_STATUS] &= cpu_to_be32(~FLUSH); + ch->regs[DBDMA_STATUS] &= ~FLUSH; conditional_interrupt(ch); next(ch); @@ -512,7 +516,7 @@ if (conditional_wait(ch)) goto wait; - current->xfer_status = cpu_to_le16(be32_to_cpu(ch->regs[DBDMA_STATUS])); + current->xfer_status = cpu_to_le16(ch->regs[DBDMA_STATUS]); dbdma_cmdptr_save(ch); conditional_interrupt(ch); @@ -524,7 +528,7 @@ static void stop(DBDMA_channel *ch) { - ch->regs[DBDMA_STATUS] &= cpu_to_be32(~(ACTIVE|DEAD|FLUSH)); + ch->regs[DBDMA_STATUS] &= ~(ACTIVE|DEAD|FLUSH); /* the stop command does not increment command pointer */ } @@ -541,7 +545,7 @@ /* clear WAKE flag at command fetch */ - ch->regs[DBDMA_STATUS] &= cpu_to_be32(~WAKE); + ch->regs[DBDMA_STATUS] &= ~WAKE; cmd = le16_to_cpu(current->command) & COMMAND_MASK; @@ -618,7 +622,7 @@ int channel; for (channel = 0; channel < DBDMA_CHANNELS; channel++, ch++) { - uint32_t status = be32_to_cpu(ch->regs[DBDMA_STATUS]); + uint32_t status = ch->regs[DBDMA_STATUS]; if (!ch->processing && (status & RUN) && (status & ACTIVE)) channel_run(ch); } @@ -660,12 +664,12 @@ uint16_t mask, value; uint32_t status; - mask = (be32_to_cpu(ch->regs[DBDMA_CONTROL]) >> 16) & 0xffff; - value = be32_to_cpu(ch->regs[DBDMA_CONTROL]) & 0xffff; + mask = (ch->regs[DBDMA_CONTROL] >> 16) & 0xffff; + value = ch->regs[DBDMA_CONTROL] & 0xffff; value &= (RUN | PAUSE | FLUSH | WAKE | DEVSTAT); - status = be32_to_cpu(ch->regs[DBDMA_STATUS]); + status = ch->regs[DBDMA_STATUS]; status = (value & mask) | (status & ~mask); @@ -677,18 +681,18 @@ } if (status & PAUSE) status &= ~ACTIVE; - if ((be32_to_cpu(ch->regs[DBDMA_STATUS]) & RUN) && !(status & RUN)) { + if ((ch->regs[DBDMA_STATUS] & RUN) && !(status & RUN)) { /* RUN is cleared */ status &= ~(ACTIVE|DEAD); } DBDMA_DPRINTF(" status 0x%08x\n", status); - ch->regs[DBDMA_STATUS] = cpu_to_be32(status); + ch->regs[DBDMA_STATUS] = status; if (status & ACTIVE) qemu_bh_schedule(dbdma_bh); - if (status & FLUSH) + if ((status & FLUSH) && ch->flush) ch->flush(&ch->io); } @@ -706,7 +710,7 @@ /* cmdptr cannot be modified if channel is RUN or ACTIVE */ if (reg == DBDMA_CMDPTR_LO && - (ch->regs[DBDMA_STATUS] & cpu_to_be32(RUN | ACTIVE))) + (ch->regs[DBDMA_STATUS] & (RUN | ACTIVE))) return; ch->regs[reg] = value; @@ -717,7 +721,7 @@ break; case DBDMA_CMDPTR_LO: /* 16-byte aligned */ - ch->regs[DBDMA_CMDPTR_LO] &= cpu_to_be32(~0xf); + ch->regs[DBDMA_CMDPTR_LO] &= ~0xf; dbdma_cmdptr_load(ch); break; case DBDMA_STATUS: @@ -837,8 +841,9 @@ s = qemu_mallocz(sizeof(DBDMA_channel) * DBDMA_CHANNELS); - *dbdma_mem_index = cpu_register_io_memory(dbdma_read, dbdma_write, s); - register_savevm("dbdma", -1, 1, dbdma_save, dbdma_load, s); + *dbdma_mem_index = cpu_register_io_memory(dbdma_read, dbdma_write, s, + DEVICE_LITTLE_ENDIAN); + register_savevm(NULL, "dbdma", -1, 1, dbdma_save, dbdma_load, s); qemu_register_reset(dbdma_reset, s); dbdma_bh = qemu_bh_new(DBDMA_run_bh, s); diff -Nru qemu-kvm-0.12.5+noroms/hw/macio.c qemu-kvm-0.14.1/hw/macio.c --- qemu-kvm-0.12.5+noroms/hw/macio.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/macio.c 2011-05-11 13:29:46.000000000 +0000 @@ -110,7 +110,6 @@ pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_APPLE); pci_config_set_device_id(d->config, device_id); pci_config_set_class(d->config, PCI_CLASS_OTHERS << 8); - d->config[PCI_HEADER_TYPE] = PCI_HEADER_TYPE_NORMAL; // header_type d->config[0x3d] = 0x01; // interrupt on pin 1 diff -Nru qemu-kvm-0.12.5+noroms/hw/mac_nvram.c qemu-kvm-0.14.1/hw/mac_nvram.c --- qemu-kvm-0.12.5+noroms/hw/mac_nvram.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/mac_nvram.c 2011-05-11 13:29:46.000000000 +0000 @@ -138,10 +138,11 @@ s->size = size; s->it_shift = it_shift; - s->mem_index = cpu_register_io_memory(nvram_read, nvram_write, s); + s->mem_index = cpu_register_io_memory(nvram_read, nvram_write, s, + DEVICE_NATIVE_ENDIAN); *mem_index = s->mem_index; - register_savevm("macio_nvram", -1, 1, macio_nvram_save, macio_nvram_load, - s); + register_savevm(NULL, "macio_nvram", -1, 1, macio_nvram_save, + macio_nvram_load, s); qemu_register_reset(macio_nvram_reset, s); return s; diff -Nru qemu-kvm-0.12.5+noroms/hw/mainstone.c qemu-kvm-0.14.1/hw/mainstone.c --- qemu-kvm-0.12.5+noroms/hw/mainstone.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/mainstone.c 2011-05-11 13:29:46.000000000 +0000 @@ -17,6 +17,7 @@ #include "mainstone.h" #include "sysemu.h" #include "flash.h" +#include "blockdev.h" static struct keymap map[0xE0] = { [0 ... 0xDF] = { -1, -1 }, @@ -79,6 +80,7 @@ qemu_irq *mst_irq; DriveInfo *dinfo; int i; + int be; if (!cpu_model) cpu_model = "pxa270-c5"; @@ -86,11 +88,14 @@ /* Setup CPU & memory */ cpu = pxa270_init(mainstone_binfo.ram_size, cpu_model); cpu_register_physical_memory(0, MAINSTONE_ROM, - qemu_ram_alloc(MAINSTONE_ROM) | IO_MEM_ROM); - - /* Setup initial (reset) machine state */ - cpu->env->regs[15] = mainstone_binfo.loader_start; + qemu_ram_alloc(NULL, "mainstone.rom", + MAINSTONE_ROM) | IO_MEM_ROM); +#ifdef TARGET_WORDS_BIGENDIAN + be = 1; +#else + be = 0; +#endif /* There are two 32MiB flash devices on the board */ for (i = 0; i < 2; i ++) { dinfo = drive_get(IF_PFLASH, 0, i); @@ -101,9 +106,12 @@ } if (!pflash_cfi01_register(mainstone_flash_base[i], - qemu_ram_alloc(MAINSTONE_FLASH), - dinfo->bdrv, sector_len, - MAINSTONE_FLASH / sector_len, 4, 0, 0, 0, 0)) { + qemu_ram_alloc(NULL, i ? "mainstone.flash1" : + "mainstone.flash0", + MAINSTONE_FLASH), + dinfo->bdrv, sector_len, + MAINSTONE_FLASH / sector_len, 4, 0, 0, 0, 0, + be)) { fprintf(stderr, "qemu: Error registering flash memory.\n"); exit(1); } diff -Nru qemu-kvm-0.12.5+noroms/hw/marvell_88w8618_audio.c qemu-kvm-0.14.1/hw/marvell_88w8618_audio.c --- qemu-kvm-0.12.5+noroms/hw/marvell_88w8618_audio.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/marvell_88w8618_audio.c 2011-05-11 13:29:46.000000000 +0000 @@ -34,7 +34,6 @@ #define MP_AUDIO_CLOCK_24MHZ (1 << 9) #define MP_AUDIO_MONO (1 << 14) -#ifdef HAS_AUDIO typedef struct mv88w8618_audio_state { SysBusDevice busdev; qemu_irq irq; @@ -250,7 +249,8 @@ wm8750_data_req_set(s->wm, mv88w8618_audio_callback, s); iomemtype = cpu_register_io_memory(mv88w8618_audio_readfn, - mv88w8618_audio_writefn, s); + mv88w8618_audio_writefn, s, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, MP_AUDIO_SIZE, iomemtype); return 0; @@ -290,13 +290,10 @@ {/* end of list */} } }; -#endif static void mv88w8618_register_devices(void) { -#ifdef HAS_AUDIO sysbus_register_withprop(&mv88w8618_audio_info); -#endif } device_init(mv88w8618_register_devices) diff -Nru qemu-kvm-0.12.5+noroms/hw/max111x.c qemu-kvm-0.14.1/hw/max111x.c --- qemu-kvm-0.12.5+noroms/hw/max111x.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/max111x.c 2011-05-11 13:29:46.000000000 +0000 @@ -143,7 +143,8 @@ s->input[7] = 0x80; s->com = 0; - register_savevm("max111x", -1, 0, max111x_save, max111x_load, s); + register_savevm(&dev->qdev, "max111x", -1, 0, + max111x_save, max111x_load, s); return 0; } diff -Nru qemu-kvm-0.12.5+noroms/hw/mc146818rtc.c qemu-kvm-0.14.1/hw/mc146818rtc.c --- qemu-kvm-0.12.5+noroms/hw/mc146818rtc.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/mc146818rtc.c 2011-05-11 13:29:46.000000000 +0000 @@ -25,10 +25,24 @@ #include "qemu-timer.h" #include "sysemu.h" #include "pc.h" +#include "apic.h" #include "isa.h" -#include "hpet_emul.h" +#include "mc146818rtc.h" //#define DEBUG_CMOS +//#define DEBUG_COALESCED + +#ifdef DEBUG_CMOS +# define CMOS_DPRINTF(format, ...) printf(format, ## __VA_ARGS__) +#else +# define CMOS_DPRINTF(format, ...) do { } while (0) +#endif + +#ifdef DEBUG_COALESCED +# define DPRINTF_C(format, ...) printf(format, ## __VA_ARGS__) +#else +# define DPRINTF_C(format, ...) do { } while (0) +#endif #define RTC_REINJECT_ON_ACK_COUNT 20 @@ -58,13 +72,14 @@ #define REG_B_UIE 0x10 #define REG_B_SQWE 0x08 #define REG_B_DM 0x04 +#define REG_B_24H 0x02 #define REG_C_UF 0x10 #define REG_C_IRQF 0x80 #define REG_C_PF 0x40 #define REG_C_AF 0x20 -struct RTCState { +typedef struct RTCState { ISADevice dev; uint8_t cmos_data[128]; uint8_t cmos_index; @@ -84,20 +99,7 @@ QEMUTimer *coalesced_timer; QEMUTimer *second_timer; QEMUTimer *second_timer2; -}; - -static void rtc_irq_raise(qemu_irq irq) -{ - /* When HPET is operating in legacy mode, RTC interrupts are disabled - * We block qemu_irq_raise, but not qemu_irq_lower, in case legacy - * mode is established while interrupt is raised. We want it to - * be lowered in any case - */ -#if defined TARGET_I386 - if (!hpet_in_legacy_mode()) -#endif - qemu_irq_raise(irq); -} +} RTCState; static void rtc_set_time(RTCState *s); static void rtc_copy_date(RTCState *s); @@ -123,9 +125,12 @@ if (s->irq_coalesced != 0) { apic_reset_irq_delivered(); s->cmos_data[RTC_REG_C] |= 0xc0; - rtc_irq_raise(s->irq); + DPRINTF_C("cmos: injecting from timer\n"); + qemu_irq_raise(s->irq); if (apic_get_irq_delivered()) { s->irq_coalesced--; + DPRINTF_C("cmos: coalesced irqs decreased to %d\n", + s->irq_coalesced); } } @@ -137,27 +142,20 @@ { int period_code, period; int64_t cur_clock, next_irq_clock; - int enable_pie; period_code = s->cmos_data[RTC_REG_A] & 0x0f; -#if defined TARGET_I386 - /* disable periodic timer if hpet is in legacy mode, since interrupts are - * disabled anyway. - */ - enable_pie = !hpet_in_legacy_mode(); -#else - enable_pie = 1; -#endif if (period_code != 0 - && (((s->cmos_data[RTC_REG_B] & REG_B_PIE) && enable_pie) + && ((s->cmos_data[RTC_REG_B] & REG_B_PIE) || ((s->cmos_data[RTC_REG_B] & REG_B_SQWE) && s->sqw_irq))) { if (period_code <= 2) period_code += 7; /* period in 32 Khz cycles */ period = 1 << (period_code - 1); #ifdef TARGET_I386 - if(period != s->period) + if (period != s->period) { s->irq_coalesced = (s->irq_coalesced * s->period) / period; + DPRINTF_C("cmos: coalesced irqs scaled to %d\n", s->irq_coalesced); + } s->period = period; #endif /* compute 32 khz clock */ @@ -186,14 +184,16 @@ if (s->irq_reinject_on_ack_count >= RTC_REINJECT_ON_ACK_COUNT) s->irq_reinject_on_ack_count = 0; apic_reset_irq_delivered(); - rtc_irq_raise(s->irq); + qemu_irq_raise(s->irq); if (!apic_get_irq_delivered()) { s->irq_coalesced++; rtc_coalesced_timer_update(s); + DPRINTF_C("cmos: coalesced irqs increased to %d\n", + s->irq_coalesced); } } else #endif - rtc_irq_raise(s->irq); + qemu_irq_raise(s->irq); } if (s->cmos_data[RTC_REG_B] & REG_B_SQWE) { /* Not square wave at all but we don't want 2048Hz interrupts! @@ -209,15 +209,12 @@ if ((addr & 1) == 0) { s->cmos_index = data & 0x7f; } else { -#ifdef DEBUG_CMOS - printf("cmos: write index=0x%02x val=0x%02x\n", - s->cmos_index, data); -#endif + CMOS_DPRINTF("cmos: write index=0x%02x val=0x%02x\n", + s->cmos_index, data); switch(s->cmos_index) { case RTC_SECONDS_ALARM: case RTC_MINUTES_ALARM: case RTC_HOURS_ALARM: - /* XXX: not supported */ s->cmos_data[s->cmos_index] = data; break; case RTC_SECONDS: @@ -250,7 +247,15 @@ rtc_set_time(s); } } - s->cmos_data[RTC_REG_B] = data; + if (((s->cmos_data[RTC_REG_B] ^ data) & (REG_B_DM | REG_B_24H)) && + !(data & REG_B_SET)) { + /* If the time format has changed and not in set mode, + update the registers immediately. */ + s->cmos_data[RTC_REG_B] = data; + rtc_copy_date(s); + } else { + s->cmos_data[RTC_REG_B] = data; + } rtc_timer_update(s, qemu_get_clock(rtc_clock)); break; case RTC_REG_C: @@ -289,7 +294,7 @@ tm->tm_sec = rtc_from_bcd(s, s->cmos_data[RTC_SECONDS]); tm->tm_min = rtc_from_bcd(s, s->cmos_data[RTC_MINUTES]); tm->tm_hour = rtc_from_bcd(s, s->cmos_data[RTC_HOURS] & 0x7f); - if (!(s->cmos_data[RTC_REG_B] & 0x02) && + if (!(s->cmos_data[RTC_REG_B] & REG_B_24H) && (s->cmos_data[RTC_HOURS] & 0x80)) { tm->tm_hour += 12; } @@ -297,6 +302,8 @@ tm->tm_mday = rtc_from_bcd(s, s->cmos_data[RTC_DAY_OF_MONTH]); tm->tm_mon = rtc_from_bcd(s, s->cmos_data[RTC_MONTH]) - 1; tm->tm_year = rtc_from_bcd(s, s->cmos_data[RTC_YEAR]) + s->base_year - 1900; + + rtc_change_mon_event(tm); } static void rtc_copy_date(RTCState *s) @@ -306,7 +313,7 @@ s->cmos_data[RTC_SECONDS] = rtc_to_bcd(s, tm->tm_sec); s->cmos_data[RTC_MINUTES] = rtc_to_bcd(s, tm->tm_min); - if (s->cmos_data[RTC_REG_B] & 0x02) { + if (s->cmos_data[RTC_REG_B] & REG_B_24H) { /* 24 hour format */ s->cmos_data[RTC_HOURS] = rtc_to_bcd(s, tm->tm_hour); } else { @@ -415,22 +422,22 @@ /* check alarm */ if (s->cmos_data[RTC_REG_B] & REG_B_AIE) { if (((s->cmos_data[RTC_SECONDS_ALARM] & 0xc0) == 0xc0 || - s->cmos_data[RTC_SECONDS_ALARM] == s->current_tm.tm_sec) && + rtc_from_bcd(s, s->cmos_data[RTC_SECONDS_ALARM]) == s->current_tm.tm_sec) && ((s->cmos_data[RTC_MINUTES_ALARM] & 0xc0) == 0xc0 || - s->cmos_data[RTC_MINUTES_ALARM] == s->current_tm.tm_mon) && + rtc_from_bcd(s, s->cmos_data[RTC_MINUTES_ALARM]) == s->current_tm.tm_min) && ((s->cmos_data[RTC_HOURS_ALARM] & 0xc0) == 0xc0 || - s->cmos_data[RTC_HOURS_ALARM] == s->current_tm.tm_hour)) { + rtc_from_bcd(s, s->cmos_data[RTC_HOURS_ALARM]) == s->current_tm.tm_hour)) { s->cmos_data[RTC_REG_C] |= 0xa0; - rtc_irq_raise(s->irq); + qemu_irq_raise(s->irq); } } /* update ended interrupt */ s->cmos_data[RTC_REG_C] |= REG_C_UF; if (s->cmos_data[RTC_REG_B] & REG_B_UIE) { - s->cmos_data[RTC_REG_C] |= REG_C_IRQF; - rtc_irq_raise(s->irq); + s->cmos_data[RTC_REG_C] |= REG_C_IRQF; + qemu_irq_raise(s->irq); } /* clear update in progress bit */ @@ -468,9 +475,13 @@ s->irq_reinject_on_ack_count < RTC_REINJECT_ON_ACK_COUNT) { s->irq_reinject_on_ack_count++; apic_reset_irq_delivered(); + DPRINTF_C("cmos: injecting on ack\n"); qemu_irq_raise(s->irq); - if (apic_get_irq_delivered()) + if (apic_get_irq_delivered()) { s->irq_coalesced--; + DPRINTF_C("cmos: coalesced irqs decreased to %d\n", + s->irq_coalesced); + } break; } #endif @@ -481,22 +492,22 @@ ret = s->cmos_data[s->cmos_index]; break; } -#ifdef DEBUG_CMOS - printf("cmos: read index=0x%02x val=0x%02x\n", - s->cmos_index, ret); -#endif + CMOS_DPRINTF("cmos: read index=0x%02x val=0x%02x\n", + s->cmos_index, ret); return ret; } } -void rtc_set_memory(RTCState *s, int addr, int val) +void rtc_set_memory(ISADevice *dev, int addr, int val) { + RTCState *s = DO_UPCAST(RTCState, dev, dev); if (addr >= 0 && addr <= 127) s->cmos_data[addr] = val; } -void rtc_set_date(RTCState *s, const struct tm *tm) +void rtc_set_date(ISADevice *dev, const struct tm *tm) { + RTCState *s = DO_UPCAST(RTCState, dev, dev); s->current_tm = *tm; rtc_copy_date(s); } @@ -505,18 +516,19 @@ #define REG_IBM_CENTURY_BYTE 0x32 #define REG_IBM_PS2_CENTURY_BYTE 0x37 -static void rtc_set_date_from_host(RTCState *s) +static void rtc_set_date_from_host(ISADevice *dev) { + RTCState *s = DO_UPCAST(RTCState, dev, dev); struct tm tm; int val; /* set the CMOS date */ qemu_get_timedate(&tm, 0); - rtc_set_date(s, &tm); + rtc_set_date(dev, &tm); val = rtc_to_bcd(s, (tm.tm_year / 100) + 19); - rtc_set_memory(s, REG_IBM_CENTURY_BYTE, val); - rtc_set_memory(s, REG_IBM_PS2_CENTURY_BYTE, val); + rtc_set_memory(dev, REG_IBM_CENTURY_BYTE, val); + rtc_set_memory(dev, REG_IBM_PS2_CENTURY_BYTE, val); } static int rtc_post_load(void *opaque, int version_id) @@ -579,16 +591,13 @@ { RTCState *s = DO_UPCAST(RTCState, dev, dev); int base = 0x70; - int isairq = 8; - - isa_init_irq(dev, &s->irq, isairq); s->cmos_data[RTC_REG_A] = 0x26; s->cmos_data[RTC_REG_B] = 0x02; s->cmos_data[RTC_REG_C] = 0x00; s->cmos_data[RTC_REG_D] = 0x80; - rtc_set_date_from_host(s); + rtc_set_date_from_host(dev); s->periodic_timer = qemu_new_timer(rtc_clock, rtc_periodic_timer, s); #ifdef TARGET_I386 @@ -605,26 +614,35 @@ register_ioport_write(base, 2, 1, cmos_ioport_write, s); register_ioport_read(base, 2, 1, cmos_ioport_read, s); + isa_init_ioport_range(dev, base, 2); - vmstate_register(base, &vmstate_rtc, s); + qdev_set_legacy_instance_id(&dev->qdev, base, 2); qemu_register_reset(rtc_reset, s); return 0; } -RTCState *rtc_init(int base_year) +ISADevice *rtc_init(int base_year, qemu_irq intercept_irq) { ISADevice *dev; + RTCState *s; dev = isa_create("mc146818rtc"); + s = DO_UPCAST(RTCState, dev, dev); qdev_prop_set_int32(&dev->qdev, "base_year", base_year); qdev_init_nofail(&dev->qdev); - return DO_UPCAST(RTCState, dev, dev); + if (intercept_irq) { + s->irq = intercept_irq; + } else { + isa_init_irq(dev, &s->irq, RTC_ISA_IRQ); + } + return dev; } static ISADeviceInfo mc146818rtc_info = { .qdev.name = "mc146818rtc", .qdev.size = sizeof(RTCState), .qdev.no_user = 1, + .qdev.vmsd = &vmstate_rtc, .init = rtc_initfn, .qdev.props = (Property[]) { DEFINE_PROP_INT32("base_year", RTCState, base_year, 1980), diff -Nru qemu-kvm-0.12.5+noroms/hw/mc146818rtc.h qemu-kvm-0.14.1/hw/mc146818rtc.h --- qemu-kvm-0.12.5+noroms/hw/mc146818rtc.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/mc146818rtc.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,12 @@ +#ifndef MC146818RTC_H +#define MC146818RTC_H + +#include "isa.h" + +#define RTC_ISA_IRQ 8 + +ISADevice *rtc_init(int base_year, qemu_irq intercept_irq); +void rtc_set_memory(ISADevice *dev, int addr, int val); +void rtc_set_date(ISADevice *dev, const struct tm *tm); + +#endif /* !MC146818RTC_H */ diff -Nru qemu-kvm-0.12.5+noroms/hw/mcf5206.c qemu-kvm-0.14.1/hw/mcf5206.c --- qemu-kvm-0.12.5+noroms/hw/mcf5206.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/mcf5206.c 2011-05-11 13:29:46.000000000 +0000 @@ -525,7 +525,8 @@ s = (m5206_mbar_state *)qemu_mallocz(sizeof(m5206_mbar_state)); iomemtype = cpu_register_io_memory(m5206_mbar_readfn, - m5206_mbar_writefn, s); + m5206_mbar_writefn, s, + DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base, 0x00001000, iomemtype); pic = qemu_allocate_irqs(m5206_mbar_set_irq, s, 14); diff -Nru qemu-kvm-0.12.5+noroms/hw/mcf5208.c qemu-kvm-0.14.1/hw/mcf5208.c --- qemu-kvm-0.12.5+noroms/hw/mcf5208.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/mcf5208.c 2011-05-11 13:29:46.000000000 +0000 @@ -179,7 +179,8 @@ int i; iomemtype = cpu_register_io_memory(m5208_sys_readfn, - m5208_sys_writefn, NULL); + m5208_sys_writefn, NULL, + DEVICE_NATIVE_ENDIAN); /* SDRAMC. */ cpu_register_physical_memory(0xfc0a8000, 0x00004000, iomemtype); /* Timers. */ @@ -188,7 +189,8 @@ bh = qemu_bh_new(m5208_timer_trigger, s); s->timer = ptimer_init(bh); iomemtype = cpu_register_io_memory(m5208_timer_readfn, - m5208_timer_writefn, s); + m5208_timer_writefn, s, + DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(0xfc080000 + 0x4000 * i, 0x00004000, iomemtype); s->irq = pic[4 + i]; @@ -220,11 +222,11 @@ /* DRAM at 0x40000000 */ cpu_register_physical_memory(0x40000000, ram_size, - qemu_ram_alloc(ram_size) | IO_MEM_RAM); + qemu_ram_alloc(NULL, "mcf5208.ram", ram_size) | IO_MEM_RAM); /* Internal SRAM. */ cpu_register_physical_memory(0x80000000, 16384, - qemu_ram_alloc(16384) | IO_MEM_RAM); + qemu_ram_alloc(NULL, "mcf5208.sram", 16384) | IO_MEM_RAM); /* Internal peripherals. */ pic = mcf_intc_init(0xfc048000, env); @@ -270,8 +272,8 @@ exit(1); } - kernel_size = load_elf(kernel_filename, 0, &elf_entry, NULL, NULL, - 1, ELF_MACHINE, 0); + kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry, + NULL, NULL, 1, ELF_MACHINE, 0); entry = elf_entry; if (kernel_size < 0) { kernel_size = load_uimage(kernel_filename, &entry, NULL, NULL); diff -Nru qemu-kvm-0.12.5+noroms/hw/mcf_fec.c qemu-kvm-0.14.1/hw/mcf_fec.c --- qemu-kvm-0.12.5+noroms/hw/mcf_fec.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/mcf_fec.c 2011-05-11 13:29:46.000000000 +0000 @@ -467,7 +467,8 @@ s = (mcf_fec_state *)qemu_mallocz(sizeof(mcf_fec_state)); s->irq = irq; s->mmio_index = cpu_register_io_memory(mcf_fec_readfn, - mcf_fec_writefn, s); + mcf_fec_writefn, s, + DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base, 0x400, s->mmio_index); memcpy(s->conf.macaddr.a, nd->macaddr, sizeof(nd->macaddr)); diff -Nru qemu-kvm-0.12.5+noroms/hw/mcf_intc.c qemu-kvm-0.14.1/hw/mcf_intc.c --- qemu-kvm-0.12.5+noroms/hw/mcf_intc.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/mcf_intc.c 2011-05-11 13:29:46.000000000 +0000 @@ -149,7 +149,8 @@ mcf_intc_reset(s); iomemtype = cpu_register_io_memory(mcf_intc_readfn, - mcf_intc_writefn, s); + mcf_intc_writefn, s, + DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base, 0x100, iomemtype); return qemu_allocate_irqs(mcf_intc_set_irq, s, 64); diff -Nru qemu-kvm-0.12.5+noroms/hw/mcf_uart.c qemu-kvm-0.14.1/hw/mcf_uart.c --- qemu-kvm-0.12.5+noroms/hw/mcf_uart.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/mcf_uart.c 2011-05-11 13:29:46.000000000 +0000 @@ -304,6 +304,7 @@ s = mcf_uart_init(irq, chr); iomemtype = cpu_register_io_memory(mcf_uart_readfn, - mcf_uart_writefn, s); + mcf_uart_writefn, s, + DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base, 0x40, iomemtype); } diff -Nru qemu-kvm-0.12.5+noroms/hw/mips_addr.c qemu-kvm-0.14.1/hw/mips_addr.c --- qemu-kvm-0.12.5+noroms/hw/mips_addr.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/mips_addr.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,34 @@ +/* + * QEMU MIPS address translation support + * + * 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 "hw.h" +#include "mips_cpudevs.h" + +uint64_t cpu_mips_kseg0_to_phys(void *opaque, uint64_t addr) +{ + return addr & 0x7fffffffll; +} + +uint64_t cpu_mips_phys_to_kseg0(void *opaque, uint64_t addr) +{ + return addr | ~0x7fffffffll; +} diff -Nru qemu-kvm-0.12.5+noroms/hw/mips_cpudevs.h qemu-kvm-0.14.1/hw/mips_cpudevs.h --- qemu-kvm-0.12.5+noroms/hw/mips_cpudevs.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/mips_cpudevs.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,15 @@ +#ifndef HW_MIPS_CPUDEVS_H +#define HW_MIPS_CPUDEVS_H +/* Definitions for MIPS CPU internal devices. */ + +/* mips_addr.c */ +uint64_t cpu_mips_kseg0_to_phys(void *opaque, uint64_t addr); +uint64_t cpu_mips_phys_to_kseg0(void *opaque, uint64_t addr); + +/* mips_int.c */ +void cpu_mips_irq_init_cpu(CPUState *env); + +/* mips_timer.c */ +void cpu_mips_clock_init(CPUState *); + +#endif diff -Nru qemu-kvm-0.12.5+noroms/hw/mips_fulong2e.c qemu-kvm-0.14.1/hw/mips_fulong2e.c --- qemu-kvm-0.12.5+noroms/hw/mips_fulong2e.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/mips_fulong2e.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,408 @@ +/* + * QEMU fulong 2e mini pc support + * + * Copyright (c) 2008 yajin (yajin@vm-kernel.org) + * Copyright (c) 2009 chenming (chenming@rdc.faw.com.cn) + * Copyright (c) 2010 Huacai Chen (zltjiangshi@gmail.com) + * This code is licensed under the GNU GPL v2. + */ + +/* + * Fulong 2e mini pc is based on ICT/ST Loongson 2e CPU (MIPS III like, 800MHz) + * http://www.linux-mips.org/wiki/Fulong + * + * Loongson 2e user manual: + * http://www.loongsondeveloper.com/doc/Loongson2EUserGuide.pdf + */ + +#include "hw.h" +#include "pc.h" +#include "fdc.h" +#include "net.h" +#include "boards.h" +#include "smbus.h" +#include "block.h" +#include "flash.h" +#include "mips.h" +#include "mips_cpudevs.h" +#include "pci.h" +#include "usb-uhci.h" +#include "qemu-char.h" +#include "sysemu.h" +#include "audio/audio.h" +#include "qemu-log.h" +#include "loader.h" +#include "mips-bios.h" +#include "ide.h" +#include "elf.h" +#include "vt82c686.h" +#include "mc146818rtc.h" +#include "blockdev.h" + +#define DEBUG_FULONG2E_INIT + +#define ENVP_ADDR 0x80002000l +#define ENVP_NB_ENTRIES 16 +#define ENVP_ENTRY_SIZE 256 + +#define MAX_IDE_BUS 2 + +/* + * PMON is not part of qemu and released with BSD license, anyone + * who want to build a pmon binary please first git-clone the source + * from the git repository at: + * http://www.loongson.cn/support/git/pmon + * Then follow the "Compile Guide" available at: + * http://dev.lemote.com/code/pmon + * + * Notes: + * 1, don't use the source at http://dev.lemote.com/http_git/pmon.git + * 2, use "Bonito2edev" to replace "dir_corresponding_to_your_target_hardware" + * in the "Compile Guide". + */ +#define FULONG_BIOSNAME "pmon_fulong2e.bin" + +/* PCI SLOT in fulong 2e */ +#define FULONG2E_VIA_SLOT 5 +#define FULONG2E_ATI_SLOT 6 +#define FULONG2E_RTL8139_SLOT 7 + +static PITState *pit; + +static struct _loaderparams { + int ram_size; + const char *kernel_filename; + const char *kernel_cmdline; + const char *initrd_filename; +} loaderparams; + +static void GCC_FMT_ATTR(3, 4) prom_set(uint32_t* prom_buf, int index, + const char *string, ...) +{ + va_list ap; + int32_t table_addr; + + if (index >= ENVP_NB_ENTRIES) + return; + + if (string == NULL) { + prom_buf[index] = 0; + return; + } + + table_addr = sizeof(int32_t) * ENVP_NB_ENTRIES + index * ENVP_ENTRY_SIZE; + prom_buf[index] = tswap32(ENVP_ADDR + table_addr); + + va_start(ap, string); + vsnprintf((char *)prom_buf + table_addr, ENVP_ENTRY_SIZE, string, ap); + va_end(ap); +} + +static int64_t load_kernel (CPUState *env) +{ + int64_t kernel_entry, kernel_low, kernel_high; + int index = 0; + long initrd_size; + ram_addr_t initrd_offset; + uint32_t *prom_buf; + long prom_size; + + if (load_elf(loaderparams.kernel_filename, cpu_mips_kseg0_to_phys, NULL, + (uint64_t *)&kernel_entry, (uint64_t *)&kernel_low, + (uint64_t *)&kernel_high, 0, ELF_MACHINE, 1) < 0) { + fprintf(stderr, "qemu: could not load kernel '%s'\n", + loaderparams.kernel_filename); + exit(1); + } + + /* load initrd */ + initrd_size = 0; + initrd_offset = 0; + if (loaderparams.initrd_filename) { + initrd_size = get_image_size (loaderparams.initrd_filename); + if (initrd_size > 0) { + initrd_offset = (kernel_high + ~TARGET_PAGE_MASK) & TARGET_PAGE_MASK; + if (initrd_offset + initrd_size > ram_size) { + fprintf(stderr, + "qemu: memory too small for initial ram disk '%s'\n", + loaderparams.initrd_filename); + exit(1); + } + initrd_size = load_image_targphys(loaderparams.initrd_filename, + initrd_offset, ram_size - initrd_offset); + } + if (initrd_size == (target_ulong) -1) { + fprintf(stderr, "qemu: could not load initial ram disk '%s'\n", + loaderparams.initrd_filename); + exit(1); + } + } + + /* Setup prom parameters. */ + prom_size = ENVP_NB_ENTRIES * (sizeof(int32_t) + ENVP_ENTRY_SIZE); + prom_buf = qemu_malloc(prom_size); + + prom_set(prom_buf, index++, "%s", loaderparams.kernel_filename); + if (initrd_size > 0) { + prom_set(prom_buf, index++, "rd_start=0x%" PRIx64 " rd_size=%li %s", + cpu_mips_phys_to_kseg0(NULL, initrd_offset), initrd_size, + loaderparams.kernel_cmdline); + } else { + prom_set(prom_buf, index++, "%s", loaderparams.kernel_cmdline); + } + + /* Setup minimum environment variables */ + prom_set(prom_buf, index++, "busclock=33000000"); + prom_set(prom_buf, index++, "cpuclock=100000000"); + prom_set(prom_buf, index++, "memsize=%i", loaderparams.ram_size/1024/1024); + prom_set(prom_buf, index++, "modetty0=38400n8r"); + prom_set(prom_buf, index++, NULL); + + rom_add_blob_fixed("prom", prom_buf, prom_size, + cpu_mips_kseg0_to_phys(NULL, ENVP_ADDR)); + + return kernel_entry; +} + +static void write_bootloader (CPUState *env, uint8_t *base, int64_t kernel_addr) +{ + uint32_t *p; + + /* Small bootloader */ + p = (uint32_t *) base; + + stl_raw(p++, 0x0bf00010); /* j 0x1fc00040 */ + stl_raw(p++, 0x00000000); /* nop */ + + /* Second part of the bootloader */ + p = (uint32_t *) (base + 0x040); + + stl_raw(p++, 0x3c040000); /* lui a0, 0 */ + stl_raw(p++, 0x34840002); /* ori a0, a0, 2 */ + stl_raw(p++, 0x3c050000 | ((ENVP_ADDR >> 16) & 0xffff)); /* lui a1, high(ENVP_ADDR) */ + stl_raw(p++, 0x34a50000 | (ENVP_ADDR & 0xffff)); /* ori a1, a0, low(ENVP_ADDR) */ + stl_raw(p++, 0x3c060000 | (((ENVP_ADDR + 8) >> 16) & 0xffff)); /* lui a2, high(ENVP_ADDR + 8) */ + stl_raw(p++, 0x34c60000 | ((ENVP_ADDR + 8) & 0xffff)); /* ori a2, a2, low(ENVP_ADDR + 8) */ + stl_raw(p++, 0x3c070000 | (loaderparams.ram_size >> 16)); /* lui a3, high(env->ram_size) */ + stl_raw(p++, 0x34e70000 | (loaderparams.ram_size & 0xffff)); /* ori a3, a3, low(env->ram_size) */ + stl_raw(p++, 0x3c1f0000 | ((kernel_addr >> 16) & 0xffff)); /* lui ra, high(kernel_addr) */; + stl_raw(p++, 0x37ff0000 | (kernel_addr & 0xffff)); /* ori ra, ra, low(kernel_addr) */ + stl_raw(p++, 0x03e00008); /* jr ra */ + stl_raw(p++, 0x00000000); /* nop */ +} + + +static void main_cpu_reset(void *opaque) +{ + CPUState *env = opaque; + + cpu_reset(env); + /* TODO: 2E reset stuff */ + if (loaderparams.kernel_filename) { + env->CP0_Status &= ~((1 << CP0St_BEV) | (1 << CP0St_ERL)); + } +} + +uint8_t eeprom_spd[0x80] = { + 0x80,0x08,0x07,0x0d,0x09,0x02,0x40,0x00,0x04,0x70, + 0x70,0x00,0x82,0x10,0x00,0x01,0x0e,0x04,0x0c,0x01, + 0x02,0x20,0x80,0x75,0x70,0x00,0x00,0x50,0x3c,0x50, + 0x2d,0x20,0xb0,0xb0,0x50,0x50,0x00,0x00,0x00,0x00, + 0x00,0x41,0x48,0x3c,0x32,0x75,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x9c,0x7b,0x07,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x48,0x42,0x35,0x34,0x41,0x32, + 0x35,0x36,0x38,0x4b,0x4e,0x2d,0x41,0x37,0x35,0x42, + 0x20,0x30,0x20 +}; + +/* Audio support */ +static void audio_init (PCIBus *pci_bus) +{ + vt82c686b_ac97_init(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 5)); + vt82c686b_mc97_init(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 6)); +} + +/* Network support */ +static void network_init (void) +{ + int i; + + for(i = 0; i < nb_nics; i++) { + NICInfo *nd = &nd_table[i]; + const char *default_devaddr = NULL; + + if (i == 0 && (!nd->model || strcmp(nd->model, "rtl8139") == 0)) { + /* The fulong board has a RTL8139 card using PCI SLOT 7 */ + default_devaddr = "07"; + } + + pci_nic_init_nofail(nd, "rtl8139", default_devaddr); + } +} + +static void cpu_request_exit(void *opaque, int irq, int level) +{ + CPUState *env = cpu_single_env; + + if (env && level) { + cpu_exit(env); + } +} + +static void mips_fulong2e_init(ram_addr_t ram_size, const char *boot_device, + const char *kernel_filename, const char *kernel_cmdline, + const char *initrd_filename, const char *cpu_model) +{ + char *filename; + unsigned long ram_offset, bios_offset; + long bios_size; + int64_t kernel_entry; + qemu_irq *i8259; + qemu_irq *cpu_exit_irq; + int via_devfn; + PCIBus *pci_bus; + uint8_t *eeprom_buf; + i2c_bus *smbus; + int i; + DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; + DeviceState *eeprom; + CPUState *env; + + /* init CPUs */ + if (cpu_model == NULL) { + cpu_model = "Loongson-2E"; + } + env = cpu_init(cpu_model); + if (!env) { + fprintf(stderr, "Unable to find CPU definition\n"); + exit(1); + } + + register_savevm(NULL, "cpu", 0, 3, cpu_save, cpu_load, env); + qemu_register_reset(main_cpu_reset, env); + + /* fulong 2e has 256M ram. */ + ram_size = 256 * 1024 * 1024; + + /* fulong 2e has a 1M flash.Winbond W39L040AP70Z */ + bios_size = 1024 * 1024; + + /* allocate RAM */ + ram_offset = qemu_ram_alloc(NULL, "fulong2e.ram", ram_size); + bios_offset = qemu_ram_alloc(NULL, "fulong2e.bios", bios_size); + + cpu_register_physical_memory(0, ram_size, ram_offset); + cpu_register_physical_memory(0x1fc00000LL, + bios_size, bios_offset | IO_MEM_ROM); + + /* We do not support flash operation, just loading pmon.bin as raw BIOS. + * Please use -L to set the BIOS path and -bios to set bios name. */ + + if (kernel_filename) { + loaderparams.ram_size = ram_size; + loaderparams.kernel_filename = kernel_filename; + loaderparams.kernel_cmdline = kernel_cmdline; + loaderparams.initrd_filename = initrd_filename; + kernel_entry = load_kernel (env); + write_bootloader(env, qemu_get_ram_ptr(bios_offset), kernel_entry); + } else { + if (bios_name == NULL) { + bios_name = FULONG_BIOSNAME; + } + filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); + if (filename) { + bios_size = load_image_targphys(filename, 0x1fc00000LL, + BIOS_SIZE); + qemu_free(filename); + } else { + bios_size = -1; + } + + if ((bios_size < 0 || bios_size > BIOS_SIZE) && !kernel_filename) { + fprintf(stderr, "qemu: Could not load MIPS bios '%s'\n", bios_name); + exit(1); + } + } + + /* Init internal devices */ + cpu_mips_irq_init_cpu(env); + cpu_mips_clock_init(env); + + /* Interrupt controller */ + /* The 8259 -> IP5 */ + i8259 = i8259_init(env->irq[5]); + + /* North bridge, Bonito --> IP2 */ + pci_bus = bonito_init((qemu_irq *)&(env->irq[2])); + + /* South bridge */ + if (drive_get_max_bus(IF_IDE) >= MAX_IDE_BUS) { + fprintf(stderr, "qemu: too many IDE bus\n"); + exit(1); + } + + for(i = 0; i < MAX_IDE_BUS * MAX_IDE_DEVS; i++) { + hd[i] = drive_get(IF_IDE, i / MAX_IDE_DEVS, i % MAX_IDE_DEVS); + } + + via_devfn = vt82c686b_init(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 0)); + if (via_devfn < 0) { + fprintf(stderr, "vt82c686b_init error \n"); + exit(1); + } + + isa_bus_irqs(i8259); + vt82c686b_ide_init(pci_bus, hd, PCI_DEVFN(FULONG2E_VIA_SLOT, 1)); + usb_uhci_vt82c686b_init(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 2)); + usb_uhci_vt82c686b_init(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 3)); + + smbus = vt82c686b_pm_init(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 4), + 0xeee1, NULL); + eeprom_buf = qemu_mallocz(8 * 256); /* XXX: make this persistent */ + memcpy(eeprom_buf, eeprom_spd, sizeof(eeprom_spd)); + /* TODO: Populate SPD eeprom data. */ + eeprom = qdev_create((BusState *)smbus, "smbus-eeprom"); + qdev_prop_set_uint8(eeprom, "address", 0x50); + qdev_prop_set_ptr(eeprom, "data", eeprom_buf); + qdev_init_nofail(eeprom); + + /* init other devices */ + pit = pit_init(0x40, isa_get_irq(0)); + cpu_exit_irq = qemu_allocate_irqs(cpu_request_exit, NULL, 1); + DMA_init(0, cpu_exit_irq); + + /* Super I/O */ + isa_create_simple("i8042"); + + rtc_init(2000, NULL); + + for(i = 0; i < MAX_SERIAL_PORTS; i++) { + if (serial_hds[i]) { + serial_isa_init(i, serial_hds[i]); + } + } + + if (parallel_hds[0]) { + parallel_init(0, parallel_hds[0]); + } + + /* Sound card */ + audio_init(pci_bus); + /* Network card */ + network_init(); +} + +QEMUMachine mips_fulong2e_machine = { + .name = "fulong2e", + .desc = "Fulong 2e mini pc", + .init = mips_fulong2e_init, +}; + +static void mips_fulong2e_machine_init(void) +{ + qemu_register_machine(&mips_fulong2e_machine); +} + +machine_init(mips_fulong2e_machine_init); diff -Nru qemu-kvm-0.12.5+noroms/hw/mips.h qemu-kvm-0.14.1/hw/mips.h --- qemu-kvm-0.12.5+noroms/hw/mips.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/mips.h 2011-05-11 13:29:46.000000000 +0000 @@ -3,7 +3,10 @@ /* Definitions for mips board emulation. */ /* gt64xxx.c */ -PCIBus *pci_gt64120_init(qemu_irq *pic); +PCIBus *gt64120_register(qemu_irq *pic); + +/* bonito.c */ +PCIBus *bonito_init(qemu_irq *pic); /* ds1225y.c */ void *ds1225y_init(target_phys_addr_t mem_base, const char *filename); @@ -18,13 +21,7 @@ void mipsnet_init(int base, qemu_irq irq, NICInfo *nd); /* jazz_led.c */ -extern void jazz_led_init(target_phys_addr_t base); - -/* mips_int.c */ -extern void cpu_mips_irq_init_cpu(CPUState *env); - -/* mips_timer.c */ -extern void cpu_mips_clock_init(CPUState *); +void jazz_led_init(target_phys_addr_t base); /* rc4030.c */ typedef struct rc4030DMAState *rc4030_dma; diff -Nru qemu-kvm-0.12.5+noroms/hw/mips_int.c qemu-kvm-0.14.1/hw/mips_int.c --- qemu-kvm-0.12.5+noroms/hw/mips_int.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/mips_int.c 2011-05-11 13:29:46.000000000 +0000 @@ -1,23 +1,29 @@ +/* + * QEMU MIPS interrupt support + * + * 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 "hw.h" -#include "mips.h" +#include "mips_cpudevs.h" #include "cpu.h" -/* Raise IRQ to CPU if necessary. It must be called every time the active - IRQ may change */ -void cpu_mips_update_irq(CPUState *env) -{ - if ((env->CP0_Status & (1 << CP0St_IE)) && - !(env->CP0_Status & (1 << CP0St_EXL)) && - !(env->CP0_Status & (1 << CP0St_ERL)) && - !(env->hflags & MIPS_HFLAG_DM)) { - if ((env->CP0_Status & env->CP0_Cause & CP0Ca_IP_mask) && - !(env->interrupt_request & CPU_INTERRUPT_HARD)) { - cpu_interrupt(env, CPU_INTERRUPT_HARD); - } - } else - cpu_reset_interrupt(env, CPU_INTERRUPT_HARD); -} - static void cpu_mips_irq_request(void *opaque, int irq, int level) { CPUState *env = (CPUState *)opaque; @@ -30,7 +36,12 @@ } else { env->CP0_Cause &= ~(1 << (irq + CP0Ca_IP)); } - cpu_mips_update_irq(env); + + if (env->CP0_Cause & CP0Ca_IP_mask) { + cpu_interrupt(env, CPU_INTERRUPT_HARD); + } else { + cpu_reset_interrupt(env, CPU_INTERRUPT_HARD); + } } void cpu_mips_irq_init_cpu(CPUState *env) @@ -43,3 +54,12 @@ env->irq[i] = qi[i]; } } + +void cpu_mips_soft_irq(CPUState *env, int irq, int level) +{ + if (irq < 0 || irq > 2) { + return; + } + + qemu_set_irq(env->irq[irq], level); +} diff -Nru qemu-kvm-0.12.5+noroms/hw/mips_jazz.c qemu-kvm-0.14.1/hw/mips_jazz.c --- qemu-kvm-0.12.5+noroms/hw/mips_jazz.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/mips_jazz.c 2011-05-11 13:29:46.000000000 +0000 @@ -24,16 +24,19 @@ #include "hw.h" #include "mips.h" +#include "mips_cpudevs.h" #include "pc.h" #include "isa.h" #include "fdc.h" #include "sysemu.h" -#include "audio/audio.h" +#include "arch_init.h" #include "boards.h" #include "net.h" #include "esp.h" #include "mips-bios.h" #include "loader.h" +#include "mc146818rtc.h" +#include "blockdev.h" enum jazz_model_e { @@ -87,30 +90,17 @@ dma_dummy_writeb, }; -#ifdef HAS_AUDIO -static void audio_init(qemu_irq *pic) +#define MAGNUM_BIOS_SIZE_MAX 0x7e000 +#define MAGNUM_BIOS_SIZE (BIOS_SIZE < MAGNUM_BIOS_SIZE_MAX ? BIOS_SIZE : MAGNUM_BIOS_SIZE_MAX) + +static void cpu_request_exit(void *opaque, int irq, int level) { - struct soundhw *c; - int audio_enabled = 0; + CPUState *env = cpu_single_env; - for (c = soundhw; !audio_enabled && c->name; ++c) { - audio_enabled = c->enabled; - } - - if (audio_enabled) { - for (c = soundhw; c->name; ++c) { - if (c->enabled) { - if (c->isa) { - c->init.init_isa(pic); - } - } - } + if (env && level) { + cpu_exit(env); } } -#endif - -#define MAGNUM_BIOS_SIZE_MAX 0x7e000 -#define MAGNUM_BIOS_SIZE (BIOS_SIZE < MAGNUM_BIOS_SIZE_MAX ? BIOS_SIZE : MAGNUM_BIOS_SIZE_MAX) static void mips_jazz_init (ram_addr_t ram_size, @@ -127,7 +117,8 @@ NICInfo *nd; PITState *pit; DriveInfo *fds[MAX_FD]; - qemu_irq esp_reset; + qemu_irq esp_reset, dma_enable; + qemu_irq *cpu_exit_irq; ram_addr_t ram_offset; ram_addr_t bios_offset; @@ -148,10 +139,10 @@ qemu_register_reset(main_cpu_reset, env); /* allocate RAM */ - ram_offset = qemu_ram_alloc(ram_size); + ram_offset = qemu_ram_alloc(NULL, "mips_jazz.ram", ram_size); cpu_register_physical_memory(0, ram_size, ram_offset | IO_MEM_RAM); - bios_offset = qemu_ram_alloc(MAGNUM_BIOS_SIZE); + bios_offset = qemu_ram_alloc(NULL, "mips_jazz.bios", MAGNUM_BIOS_SIZE); cpu_register_physical_memory(0x1fc00000LL, MAGNUM_BIOS_SIZE, bios_offset | IO_MEM_ROM); cpu_register_physical_memory(0xfff00000LL, @@ -180,14 +171,16 @@ /* Chipset */ rc4030_opaque = rc4030_init(env->irq[6], env->irq[3], &rc4030, &dmas); - s_dma_dummy = cpu_register_io_memory(dma_dummy_read, dma_dummy_write, NULL); + s_dma_dummy = cpu_register_io_memory(dma_dummy_read, dma_dummy_write, NULL, + DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(0x8000d000, 0x00001000, s_dma_dummy); /* ISA devices */ i8259 = i8259_init(env->irq[4]); isa_bus_new(NULL); isa_bus_irqs(i8259); - DMA_init(0); + cpu_exit_irq = qemu_allocate_irqs(cpu_request_exit, NULL, 1); + DMA_init(0, cpu_exit_irq); pit = pit_init(0x40, i8259[0]); pcspk_init(pit); @@ -228,7 +221,7 @@ /* SCSI adapter */ esp_init(0x80002000, 0, rc4030_dma_read, rc4030_dma_write, dmas[0], - rc4030[5], &esp_reset); + rc4030[5], &esp_reset, &dma_enable); /* Floppy */ if (drive_get_max_bus(IF_FLOPPY) >= MAX_FD) { @@ -241,18 +234,29 @@ fdctrl_init_sysbus(rc4030[1], 0, 0x80003000, fds); /* Real time clock */ - rtc_init(1980); - s_rtc = cpu_register_io_memory(rtc_read, rtc_write, NULL); + rtc_init(1980, NULL); + s_rtc = cpu_register_io_memory(rtc_read, rtc_write, NULL, + DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(0x80004000, 0x00001000, s_rtc); /* Keyboard (i8042) */ i8042_mm_init(rc4030[6], rc4030[7], 0x80005000, 0x1000, 0x1); /* Serial ports */ - if (serial_hds[0]) - serial_mm_init(0x80006000, 0, rc4030[8], 8000000/16, serial_hds[0], 1); - if (serial_hds[1]) - serial_mm_init(0x80007000, 0, rc4030[9], 8000000/16, serial_hds[1], 1); + if (serial_hds[0]) { +#ifdef TARGET_WORDS_BIGENDIAN + serial_mm_init(0x80006000, 0, rc4030[8], 8000000/16, serial_hds[0], 1, 1); +#else + serial_mm_init(0x80006000, 0, rc4030[8], 8000000/16, serial_hds[0], 1, 0); +#endif + } + if (serial_hds[1]) { +#ifdef TARGET_WORDS_BIGENDIAN + serial_mm_init(0x80007000, 0, rc4030[9], 8000000/16, serial_hds[1], 1, 1); +#else + serial_mm_init(0x80007000, 0, rc4030[9], 8000000/16, serial_hds[1], 1, 0); +#endif + } /* Parallel port */ if (parallel_hds[0]) @@ -260,9 +264,7 @@ /* Sound card */ /* FIXME: missing Jazz sound at 0x8000c000, rc4030[2] */ -#ifdef HAS_AUDIO - audio_init(i8259); -#endif + audio_init(i8259, NULL); /* NVRAM: Unprotected at 0x9000, Protected at 0xa000, Read only at 0xb000 */ ds1225y_init(0x80009000, "nvram"); diff -Nru qemu-kvm-0.12.5+noroms/hw/mips_malta.c qemu-kvm-0.14.1/hw/mips_malta.c --- qemu-kvm-0.12.5+noroms/hw/mips_malta.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/mips_malta.c 2011-05-11 13:29:46.000000000 +0000 @@ -31,30 +31,25 @@ #include "block.h" #include "flash.h" #include "mips.h" +#include "mips_cpudevs.h" #include "pci.h" #include "usb-uhci.h" #include "vmware_vga.h" #include "qemu-char.h" #include "sysemu.h" -#include "audio/audio.h" +#include "arch_init.h" #include "boards.h" #include "qemu-log.h" #include "mips-bios.h" #include "ide.h" #include "loader.h" #include "elf.h" +#include "mc146818rtc.h" +#include "blockdev.h" //#define DEBUG_BOARD_INIT -#ifdef TARGET_MIPS64 -#define PHYS_TO_VIRT(x) ((x) | ~0x7fffffffULL) -#else -#define PHYS_TO_VIRT(x) ((x) | ~0x7fffffffU) -#endif - -#define ENVP_ADDR (int32_t)0x80002000 -#define VIRT_TO_PHYS_ADDEND (-((int64_t)(int32_t)0x80000000)) - +#define ENVP_ADDR 0x80002000l #define ENVP_NB_ENTRIES 16 #define ENVP_ENTRY_SIZE 256 @@ -441,7 +436,8 @@ s = (MaltaFPGAState *)qemu_mallocz(sizeof(MaltaFPGAState)); malta = cpu_register_io_memory(malta_fpga_read, - malta_fpga_write, s); + malta_fpga_write, s, + DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base, 0x900, malta); /* 0xa00 is less than a page, so will still get the right offsets. */ @@ -449,7 +445,11 @@ s->display = qemu_chr_open("fpga", "vc:320x200", malta_fpga_led_init); - s->uart = serial_mm_init(base + 0x900, 3, uart_irq, 230400, uart_chr, 1); +#ifdef TARGET_WORDS_BIGENDIAN + s->uart = serial_mm_init(base + 0x900, 3, uart_irq, 230400, uart_chr, 1, 1); +#else + s->uart = serial_mm_init(base + 0x900, 3, uart_irq, 230400, uart_chr, 1, 0); +#endif malta_fpga_reset(s); qemu_register_reset(malta_fpga_reset, s); @@ -457,27 +457,6 @@ return s; } -/* Audio support */ -#ifdef HAS_AUDIO -static void audio_init (PCIBus *pci_bus) -{ - struct soundhw *c; - int audio_enabled = 0; - - for (c = soundhw; !audio_enabled && c->name; ++c) { - audio_enabled = c->enabled; - } - - if (audio_enabled) { - for (c = soundhw; c->name; ++c) { - if (c->enabled) { - c->init.init_pci(pci_bus); - } - } - } -} -#endif - /* Network support */ static void network_init(void) { @@ -657,7 +636,8 @@ } -static void prom_set(uint32_t* prom_buf, int index, const char *string, ...) +static void GCC_FMT_ATTR(3, 4) prom_set(uint32_t* prom_buf, int index, + const char *string, ...) { va_list ap; int32_t table_addr; @@ -681,7 +661,7 @@ /* Kernel */ static int64_t load_kernel (void) { - int64_t kernel_entry, kernel_low, kernel_high; + int64_t kernel_entry, kernel_high; long initrd_size; ram_addr_t initrd_offset; int big_endian; @@ -695,9 +675,9 @@ big_endian = 0; #endif - if (load_elf(loaderparams.kernel_filename, VIRT_TO_PHYS_ADDEND, - (uint64_t *)&kernel_entry, (uint64_t *)&kernel_low, - (uint64_t *)&kernel_high, big_endian, ELF_MACHINE, 1) < 0) { + if (load_elf(loaderparams.kernel_filename, cpu_mips_kseg0_to_phys, NULL, + (uint64_t *)&kernel_entry, NULL, (uint64_t *)&kernel_high, + big_endian, ELF_MACHINE, 1) < 0) { fprintf(stderr, "qemu: could not load kernel '%s'\n", loaderparams.kernel_filename); exit(1); @@ -731,13 +711,13 @@ prom_size = ENVP_NB_ENTRIES * (sizeof(int32_t) + ENVP_ENTRY_SIZE); prom_buf = qemu_malloc(prom_size); - prom_set(prom_buf, prom_index++, loaderparams.kernel_filename); + prom_set(prom_buf, prom_index++, "%s", loaderparams.kernel_filename); if (initrd_size > 0) { - prom_set(prom_buf, prom_index++, "rd_start=0x" TARGET_FMT_lx " rd_size=%li %s", - PHYS_TO_VIRT(initrd_offset), initrd_size, + prom_set(prom_buf, prom_index++, "rd_start=0x%" PRIx64 " rd_size=%li %s", + cpu_mips_phys_to_kseg0(NULL, initrd_offset), initrd_size, loaderparams.kernel_cmdline); } else { - prom_set(prom_buf, prom_index++, loaderparams.kernel_cmdline); + prom_set(prom_buf, prom_index++, "%s", loaderparams.kernel_cmdline); } prom_set(prom_buf, prom_index++, "memsize"); @@ -747,7 +727,7 @@ prom_set(prom_buf, prom_index++, NULL); rom_add_blob_fixed("prom", prom_buf, prom_size, - ENVP_ADDR + VIRT_TO_PHYS_ADDEND); + cpu_mips_kseg0_to_phys(NULL, ENVP_ADDR)); return kernel_entry; } @@ -757,7 +737,7 @@ CPUState *env = opaque; cpu_reset(env); - /* The bootload does not need to be rewritten as it is located in a + /* The bootloader does not need to be rewritten as it is located in a read only location. The kernel location and the arguments table location does not change. */ if (loaderparams.kernel_filename) { @@ -765,6 +745,15 @@ } } +static void cpu_request_exit(void *opaque, int irq, int level) +{ + CPUState *env = cpu_single_env; + + if (env && level) { + cpu_exit(env); + } +} + static void mips_malta_init (ram_addr_t ram_size, const char *boot_device, @@ -777,12 +766,9 @@ target_long bios_size; int64_t kernel_entry; PCIBus *pci_bus; - ISADevice *isa_dev; CPUState *env; - RTCState *rtc_state; - fdctrl_t *floppy_controller; - MaltaFPGAState *malta_fpga; qemu_irq *i8259; + qemu_irq *cpu_exit_irq; int piix4_devfn; uint8_t *eeprom_buf; i2c_bus *smbus; @@ -792,6 +778,7 @@ DriveInfo *fd[MAX_FD]; int fl_idx = 0; int fl_sectors = 0; + int be; /* Make sure the first 3 serial ports are associated with a device. */ for(i = 0; i < 3; i++) { @@ -824,8 +811,8 @@ ((unsigned int)ram_size / (1 << 20))); exit(1); } - ram_offset = qemu_ram_alloc(ram_size); - bios_offset = qemu_ram_alloc(BIOS_SIZE); + ram_offset = qemu_ram_alloc(NULL, "mips_malta.ram", ram_size); + bios_offset = qemu_ram_alloc(NULL, "mips_malta.bios", BIOS_SIZE); cpu_register_physical_memory(0, ram_size, ram_offset | IO_MEM_RAM); @@ -836,8 +823,13 @@ cpu_register_physical_memory(0x1fc00000LL, BIOS_SIZE, bios_offset | IO_MEM_ROM); +#ifdef TARGET_WORDS_BIGENDIAN + be = 1; +#else + be = 0; +#endif /* FPGA */ - malta_fpga = malta_fpga_init(0x1f000000LL, env->irq[2], serial_hds[2]); + malta_fpga_init(0x1f000000LL, env->irq[2], serial_hds[2]); /* Load firmware in flash / BIOS unless we boot directly into a kernel. */ if (kernel_filename) { @@ -862,7 +854,7 @@ #endif pflash_cfi01_register(0x1e000000LL, bios_offset, dinfo->bdrv, 65536, fl_sectors, - 4, 0x0000, 0x0000, 0x0000, 0x0000); + 4, 0x0000, 0x0000, 0x0000, 0x0000, be); fl_idx++; } else { /* Load a BIOS image. */ @@ -899,7 +891,7 @@ /* Board ID = 0x420 (Malta Board with CoreLV) XXX: theoretically 0x1e000010 should map to flash and 0x1fc00010 should map to the board ID. */ - stl_phys(0x1fc00010LL, 0x00000420); + stl_p(qemu_get_ram_ptr(bios_offset) + 0x10, 0x00000420); /* Init internal devices */ cpu_mips_irq_init_cpu(env); @@ -910,7 +902,7 @@ i8259 = i8259_init(env->irq[2]); /* Northbridge */ - pci_bus = pci_gt64120_init(i8259); + pci_bus = gt64120_register(i8259); /* Southbridge */ @@ -927,7 +919,8 @@ isa_bus_irqs(i8259); pci_piix4_ide_init(pci_bus, hd, piix4_devfn + 1); usb_uhci_piix4_init(pci_bus, piix4_devfn + 2); - smbus = piix4_pm_init(pci_bus, piix4_devfn + 3, 0x1100, isa_reserve_irq(9)); + smbus = piix4_pm_init(pci_bus, piix4_devfn + 3, 0x1100, isa_get_irq(9), + NULL, NULL, 0); eeprom_buf = qemu_mallocz(8 * 256); /* XXX: make this persistent */ for (i = 0; i < 8; i++) { /* TODO: Populate SPD eeprom data. */ @@ -937,13 +930,14 @@ qdev_prop_set_ptr(eeprom, "data", eeprom_buf + (i * 256)); qdev_init_nofail(eeprom); } - pit = pit_init(0x40, isa_reserve_irq(0)); - DMA_init(0); + pit = pit_init(0x40, isa_get_irq(0)); + cpu_exit_irq = qemu_allocate_irqs(cpu_request_exit, NULL, 1); + DMA_init(0, cpu_exit_irq); /* Super I/O */ - isa_dev = isa_create_simple("i8042"); - - rtc_state = rtc_init(2000); + isa_create_simple("i8042"); + + rtc_init(2000, NULL); serial_isa_init(0, serial_hds[0]); serial_isa_init(1, serial_hds[1]); if (parallel_hds[0]) @@ -951,12 +945,10 @@ for(i = 0; i < MAX_FD; i++) { fd[i] = drive_get(IF_FLOPPY, 0, i); } - floppy_controller = fdctrl_init_isa(fd); + fdctrl_init_isa(fd); /* Sound card */ -#ifdef HAS_AUDIO - audio_init(pci_bus); -#endif + audio_init(NULL, pci_bus); /* Network card */ network_init(); @@ -967,7 +959,7 @@ } else if (vmsvga_enabled) { pci_vmsvga_init(pci_bus); } else if (std_vga_enabled) { - pci_vga_init(pci_bus, 0, 0); + pci_vga_init(pci_bus); } } diff -Nru qemu-kvm-0.12.5+noroms/hw/mips_mipssim.c qemu-kvm-0.14.1/hw/mips_mipssim.c --- qemu-kvm-0.12.5+noroms/hw/mips_mipssim.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/mips_mipssim.c 2011-05-11 13:29:46.000000000 +0000 @@ -26,6 +26,7 @@ */ #include "hw.h" #include "mips.h" +#include "mips_cpudevs.h" #include "pc.h" #include "isa.h" #include "net.h" @@ -35,14 +36,6 @@ #include "loader.h" #include "elf.h" -#ifdef TARGET_MIPS64 -#define PHYS_TO_VIRT(x) ((x) | ~0x7fffffffULL) -#else -#define PHYS_TO_VIRT(x) ((x) | ~0x7fffffffU) -#endif - -#define VIRT_TO_PHYS_ADDEND (-((int64_t)(int32_t)0x80000000)) - static struct _loaderparams { int ram_size; const char *kernel_filename; @@ -57,7 +50,7 @@ static int64_t load_kernel(void) { - int64_t entry, kernel_low, kernel_high; + int64_t entry, kernel_high; long kernel_size; long initrd_size; ram_addr_t initrd_offset; @@ -69,9 +62,10 @@ big_endian = 0; #endif - kernel_size = load_elf(loaderparams.kernel_filename, VIRT_TO_PHYS_ADDEND, - (uint64_t *)&entry, (uint64_t *)&kernel_low, - (uint64_t *)&kernel_high, big_endian, ELF_MACHINE, 1); + kernel_size = load_elf(loaderparams.kernel_filename, cpu_mips_kseg0_to_phys, + NULL, (uint64_t *)&entry, NULL, + (uint64_t *)&kernel_high, big_endian, + ELF_MACHINE, 1); if (kernel_size >= 0) { if ((entry & ~0x7fffffffULL) == 0x80000000) entry = (int32_t)entry; @@ -112,7 +106,10 @@ CPUState *env = s->env; cpu_reset(env); - env->active_tc.PC = s->vector; + env->active_tc.PC = s->vector & ~(target_ulong)1; + if (s->vector & 1) { + env->hflags |= MIPS_HFLAG_M16; + } } static void @@ -147,8 +144,8 @@ qemu_register_reset(main_cpu_reset, reset_info); /* Allocate RAM. */ - ram_offset = qemu_ram_alloc(ram_size); - bios_offset = qemu_ram_alloc(BIOS_SIZE); + ram_offset = qemu_ram_alloc(NULL, "mips_mipssim.ram", ram_size); + bios_offset = qemu_ram_alloc(NULL, "mips_mipssim.bios", BIOS_SIZE); cpu_register_physical_memory(0, ram_size, ram_offset | IO_MEM_RAM); diff -Nru qemu-kvm-0.12.5+noroms/hw/mipsnet.c qemu-kvm-0.14.1/hw/mipsnet.c --- qemu-kvm-0.12.5+noroms/hw/mipsnet.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/mipsnet.c 2011-05-11 13:29:46.000000000 +0000 @@ -81,7 +81,7 @@ MIPSnetState *s = DO_UPCAST(NICState, nc, nc)->opaque; #ifdef DEBUG_MIPSNET_RECEIVE - printf("mipsnet: receiving len=%d\n", size); + printf("mipsnet: receiving len=%zu\n", size); #endif if (!mipsnet_can_receive(nc)) return -1; @@ -239,7 +239,7 @@ { MIPSnetState *s = DO_UPCAST(NICState, nc, nc)->opaque; - unregister_savevm("mipsnet", s); + unregister_savevm(NULL, "mipsnet", s); isa_unassign_ioport(s->io_base, 36); @@ -284,5 +284,5 @@ } mipsnet_reset(s); - register_savevm("mipsnet", 0, 0, mipsnet_save, mipsnet_load, s); + register_savevm(NULL, "mipsnet", 0, 0, mipsnet_save, mipsnet_load, s); } diff -Nru qemu-kvm-0.12.5+noroms/hw/mips_r4k.c qemu-kvm-0.14.1/hw/mips_r4k.c --- qemu-kvm-0.12.5+noroms/hw/mips_r4k.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/mips_r4k.c 2011-05-11 13:29:46.000000000 +0000 @@ -9,6 +9,7 @@ */ #include "hw.h" #include "mips.h" +#include "mips_cpudevs.h" #include "pc.h" #include "isa.h" #include "net.h" @@ -20,10 +21,8 @@ #include "ide.h" #include "loader.h" #include "elf.h" - -#define PHYS_TO_VIRT(x) ((x) | ~(target_ulong)0x7fffffff) - -#define VIRT_TO_PHYS_ADDEND (-((int64_t)(int32_t)0x80000000)) +#include "mc146818rtc.h" +#include "blockdev.h" #define MAX_IDE_BUS 2 @@ -77,7 +76,7 @@ static int64_t load_kernel(void) { - int64_t entry, kernel_low, kernel_high; + int64_t entry, kernel_high; long kernel_size, initrd_size, params_size; ram_addr_t initrd_offset; uint32_t *params_buf; @@ -88,9 +87,10 @@ #else big_endian = 0; #endif - kernel_size = load_elf(loaderparams.kernel_filename, VIRT_TO_PHYS_ADDEND, - (uint64_t *)&entry, (uint64_t *)&kernel_low, - (uint64_t *)&kernel_high, big_endian, ELF_MACHINE, 1); + kernel_size = load_elf(loaderparams.kernel_filename, cpu_mips_kseg0_to_phys, + NULL, (uint64_t *)&entry, NULL, + (uint64_t *)&kernel_high, big_endian, + ELF_MACHINE, 1); if (kernel_size >= 0) { if ((entry & ~0x7fffffffULL) == 0x80000000) entry = (int32_t)entry; @@ -132,8 +132,8 @@ params_buf[1] = tswap32(0x12345678); if (initrd_size > 0) { - snprintf((char *)params_buf + 8, 256, "rd_start=0x" TARGET_FMT_lx " rd_size=%li %s", - PHYS_TO_VIRT((uint32_t)initrd_offset), + snprintf((char *)params_buf + 8, 256, "rd_start=0x%" PRIx64 " rd_size=%li %s", + cpu_mips_phys_to_kseg0(NULL, initrd_offset), initrd_size, loaderparams.kernel_cmdline); } else { snprintf((char *)params_buf + 8, 256, "%s", loaderparams.kernel_cmdline); @@ -167,11 +167,11 @@ int bios_size; CPUState *env; ResetData *reset_info; - RTCState *rtc_state; int i; qemu_irq *i8259; DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; DriveInfo *dinfo; + int be; /* init CPUs */ if (cpu_model == NULL) { @@ -198,13 +198,14 @@ ((unsigned int)ram_size / (1 << 20))); exit(1); } - ram_offset = qemu_ram_alloc(ram_size); + ram_offset = qemu_ram_alloc(NULL, "mips_r4k.ram", ram_size); cpu_register_physical_memory(0, ram_size, ram_offset | IO_MEM_RAM); if (!mips_qemu_iomemtype) { mips_qemu_iomemtype = cpu_register_io_memory(mips_qemu_read, - mips_qemu_write, NULL); + mips_qemu_write, NULL, + DEVICE_NATIVE_ENDIAN); } cpu_register_physical_memory(0x1fbf0000, 0x10000, mips_qemu_iomemtype); @@ -220,18 +221,24 @@ } else { bios_size = -1; } +#ifdef TARGET_WORDS_BIGENDIAN + be = 1; +#else + be = 0; +#endif if ((bios_size > 0) && (bios_size <= BIOS_SIZE)) { - bios_offset = qemu_ram_alloc(BIOS_SIZE); + bios_offset = qemu_ram_alloc(NULL, "mips_r4k.bios", BIOS_SIZE); cpu_register_physical_memory(0x1fc00000, BIOS_SIZE, bios_offset | IO_MEM_ROM); load_image_targphys(filename, 0x1fc00000, BIOS_SIZE); } else if ((dinfo = drive_get(IF_PFLASH, 0, 0)) != NULL) { uint32_t mips_rom = 0x00400000; - bios_offset = qemu_ram_alloc(mips_rom); + bios_offset = qemu_ram_alloc(NULL, "mips_r4k.bios", mips_rom); if (!pflash_cfi01_register(0x1fc00000, bios_offset, - dinfo->bdrv, sector_len, mips_rom / sector_len, - 4, 0, 0, 0, 0)) { + dinfo->bdrv, sector_len, + mips_rom / sector_len, + 4, 0, 0, 0, 0, be)) { fprintf(stderr, "qemu: Error registering flash memory.\n"); } } @@ -261,7 +268,7 @@ isa_bus_new(NULL); isa_bus_irqs(i8259); - rtc_state = rtc_init(2000); + rtc_init(2000, NULL); /* Register 64 KB of ISA IO space at 0x14000000 */ isa_mmio_init(0x14000000, 0x00010000); diff -Nru qemu-kvm-0.12.5+noroms/hw/mips_timer.c qemu-kvm-0.14.1/hw/mips_timer.c --- qemu-kvm-0.12.5+noroms/hw/mips_timer.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/mips_timer.c 2011-05-11 13:29:46.000000000 +0000 @@ -1,5 +1,27 @@ +/* + * QEMU MIPS timer support + * + * 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 "hw.h" -#include "mips.h" +#include "mips_cpudevs.h" #include "qemu-timer.h" #define TIMER_FREQ 100 * 1000 * 1000 @@ -20,16 +42,6 @@ } /* MIPS R4K timer */ -uint32_t cpu_mips_get_count (CPUState *env) -{ - if (env->CP0_Cause & (1 << CP0Ca_DC)) - return env->CP0_Count; - else - return env->CP0_Count + - (uint32_t)muldiv64(qemu_get_clock(vm_clock), - TIMER_FREQ, get_ticks_per_sec()); -} - static void cpu_mips_timer_update(CPUState *env) { uint64_t now, next; @@ -42,6 +54,35 @@ qemu_mod_timer(env->timer, next); } +/* Expire the timer. */ +static void cpu_mips_timer_expire(CPUState *env) +{ + cpu_mips_timer_update(env); + if (env->insn_flags & ISA_MIPS32R2) { + env->CP0_Cause |= 1 << CP0Ca_TI; + } + qemu_irq_raise(env->irq[(env->CP0_IntCtl >> CP0IntCtl_IPTI) & 0x7]); +} + +uint32_t cpu_mips_get_count (CPUState *env) +{ + if (env->CP0_Cause & (1 << CP0Ca_DC)) { + return env->CP0_Count; + } else { + uint64_t now; + + now = qemu_get_clock(vm_clock); + if (qemu_timer_pending(env->timer) + && qemu_timer_expired(env->timer, now)) { + /* The timer has already expired. */ + cpu_mips_timer_expire(env); + } + + return env->CP0_Count + + (uint32_t)muldiv64(now, TIMER_FREQ, get_ticks_per_sec()); + } +} + void cpu_mips_store_count (CPUState *env, uint32_t count) { if (env->CP0_Cause & (1 << CP0Ca_DC)) @@ -94,11 +135,8 @@ the comparator value. Offset the count by one to avoid immediately retriggering the callback before any virtual time has passed. */ env->CP0_Count++; - cpu_mips_timer_update(env); + cpu_mips_timer_expire(env); env->CP0_Count--; - if (env->insn_flags & ISA_MIPS32R2) - env->CP0_Cause |= 1 << CP0Ca_TI; - qemu_irq_raise(env->irq[(env->CP0_IntCtl >> CP0IntCtl_IPTI) & 0x7]); } void cpu_mips_clock_init (CPUState *env) diff -Nru qemu-kvm-0.12.5+noroms/hw/mpcore.c qemu-kvm-0.14.1/hw/mpcore.c --- qemu-kvm-0.12.5+noroms/hw/mpcore.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/mpcore.c 2011-05-11 13:29:46.000000000 +0000 @@ -276,7 +276,8 @@ gic_init(&s->gic, s->num_cpu); s->iomemtype = cpu_register_io_memory(mpcore_priv_readfn, - mpcore_priv_writefn, s); + mpcore_priv_writefn, s, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio_cb(dev, 0x2000, mpcore_priv_map); for (i = 0; i < s->num_cpu * 2; i++) { mpcore_timer_init(s, &s->timer[i], i); diff -Nru qemu-kvm-0.12.5+noroms/hw/msi.c qemu-kvm-0.14.1/hw/msi.c --- qemu-kvm-0.12.5+noroms/hw/msi.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/msi.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,344 @@ +/* + * msi.c + * + * Copyright (c) 2010 Isaku Yamahata + * VA Linux Systems Japan K.K. + * + * 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, see . + */ + +#include "msi.h" +#include "range.h" + +/* Eventually those constants should go to Linux pci_regs.h */ +#define PCI_MSI_PENDING_32 0x10 +#define PCI_MSI_PENDING_64 0x14 + +/* PCI_MSI_ADDRESS_LO */ +#define PCI_MSI_ADDRESS_LO_MASK (~0x3) + +/* If we get rid of cap allocator, we won't need those. */ +#define PCI_MSI_32_SIZEOF 0x0a +#define PCI_MSI_64_SIZEOF 0x0e +#define PCI_MSI_32M_SIZEOF 0x14 +#define PCI_MSI_64M_SIZEOF 0x18 + +#define PCI_MSI_VECTORS_MAX 32 + +/* If we get rid of cap allocator, we won't need this. */ +static inline uint8_t msi_cap_sizeof(uint16_t flags) +{ + switch (flags & (PCI_MSI_FLAGS_MASKBIT | PCI_MSI_FLAGS_64BIT)) { + case PCI_MSI_FLAGS_MASKBIT | PCI_MSI_FLAGS_64BIT: + return PCI_MSI_64M_SIZEOF; + case PCI_MSI_FLAGS_64BIT: + return PCI_MSI_64_SIZEOF; + case PCI_MSI_FLAGS_MASKBIT: + return PCI_MSI_32M_SIZEOF; + case 0: + return PCI_MSI_32_SIZEOF; + default: + abort(); + break; + } + return 0; +} + +//#define MSI_DEBUG + +#ifdef MSI_DEBUG +# define MSI_DPRINTF(fmt, ...) \ + fprintf(stderr, "%s:%d " fmt, __func__, __LINE__, ## __VA_ARGS__) +#else +# define MSI_DPRINTF(fmt, ...) do { } while (0) +#endif +#define MSI_DEV_PRINTF(dev, fmt, ...) \ + MSI_DPRINTF("%s:%x " fmt, (dev)->name, (dev)->devfn, ## __VA_ARGS__) + +static inline unsigned int msi_nr_vectors(uint16_t flags) +{ + return 1U << + ((flags & PCI_MSI_FLAGS_QSIZE) >> (ffs(PCI_MSI_FLAGS_QSIZE) - 1)); +} + +static inline uint8_t msi_flags_off(const PCIDevice* dev) +{ + return dev->msi_cap + PCI_MSI_FLAGS; +} + +static inline uint8_t msi_address_lo_off(const PCIDevice* dev) +{ + return dev->msi_cap + PCI_MSI_ADDRESS_LO; +} + +static inline uint8_t msi_address_hi_off(const PCIDevice* dev) +{ + return dev->msi_cap + PCI_MSI_ADDRESS_HI; +} + +static inline uint8_t msi_data_off(const PCIDevice* dev, bool msi64bit) +{ + return dev->msi_cap + (msi64bit ? PCI_MSI_DATA_64 : PCI_MSI_DATA_32); +} + +static inline uint8_t msi_mask_off(const PCIDevice* dev, bool msi64bit) +{ + return dev->msi_cap + (msi64bit ? PCI_MSI_MASK_64 : PCI_MSI_MASK_32); +} + +static inline uint8_t msi_pending_off(const PCIDevice* dev, bool msi64bit) +{ + return dev->msi_cap + (msi64bit ? PCI_MSI_PENDING_64 : PCI_MSI_PENDING_32); +} + +bool msi_enabled(const PCIDevice *dev) +{ + return msi_present(dev) && + (pci_get_word(dev->config + msi_flags_off(dev)) & + PCI_MSI_FLAGS_ENABLE); +} + +int msi_init(struct PCIDevice *dev, uint8_t offset, + unsigned int nr_vectors, bool msi64bit, bool msi_per_vector_mask) +{ + unsigned int vectors_order; + uint16_t flags; + uint8_t cap_size; + int config_offset; + MSI_DEV_PRINTF(dev, + "init offset: 0x%"PRIx8" vector: %"PRId8 + " 64bit %d mask %d\n", + offset, nr_vectors, msi64bit, msi_per_vector_mask); + + assert(!(nr_vectors & (nr_vectors - 1))); /* power of 2 */ + assert(nr_vectors > 0); + assert(nr_vectors <= PCI_MSI_VECTORS_MAX); + /* the nr of MSI vectors is up to 32 */ + vectors_order = ffs(nr_vectors) - 1; + + flags = vectors_order << (ffs(PCI_MSI_FLAGS_QMASK) - 1); + if (msi64bit) { + flags |= PCI_MSI_FLAGS_64BIT; + } + if (msi_per_vector_mask) { + flags |= PCI_MSI_FLAGS_MASKBIT; + } + + cap_size = msi_cap_sizeof(flags); + config_offset = pci_add_capability(dev, PCI_CAP_ID_MSI, offset, cap_size); + if (config_offset < 0) { + return config_offset; + } + + dev->msi_cap = config_offset; + dev->cap_present |= QEMU_PCI_CAP_MSI; + + pci_set_word(dev->config + msi_flags_off(dev), flags); + pci_set_word(dev->wmask + msi_flags_off(dev), + PCI_MSI_FLAGS_QSIZE | PCI_MSI_FLAGS_ENABLE); + pci_set_long(dev->wmask + msi_address_lo_off(dev), + PCI_MSI_ADDRESS_LO_MASK); + if (msi64bit) { + pci_set_long(dev->wmask + msi_address_hi_off(dev), 0xffffffff); + } + pci_set_word(dev->wmask + msi_data_off(dev, msi64bit), 0xffff); + + if (msi_per_vector_mask) { + /* Make mask bits 0 to nr_vectors - 1 writeable. */ + pci_set_long(dev->wmask + msi_mask_off(dev, msi64bit), + 0xffffffff >> (PCI_MSI_VECTORS_MAX - nr_vectors)); + } + return config_offset; +} + +void msi_uninit(struct PCIDevice *dev) +{ + uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev)); + uint8_t cap_size = msi_cap_sizeof(flags); + pci_del_capability(dev, PCI_CAP_ID_MSIX, cap_size); + MSI_DEV_PRINTF(dev, "uninit\n"); +} + +void msi_reset(PCIDevice *dev) +{ + uint16_t flags; + bool msi64bit; + + flags = pci_get_word(dev->config + msi_flags_off(dev)); + flags &= ~(PCI_MSI_FLAGS_QSIZE | PCI_MSI_FLAGS_ENABLE); + msi64bit = flags & PCI_MSI_FLAGS_64BIT; + + pci_set_word(dev->config + msi_flags_off(dev), flags); + pci_set_long(dev->config + msi_address_lo_off(dev), 0); + if (msi64bit) { + pci_set_long(dev->config + msi_address_hi_off(dev), 0); + } + pci_set_word(dev->config + msi_data_off(dev, msi64bit), 0); + if (flags & PCI_MSI_FLAGS_MASKBIT) { + pci_set_long(dev->config + msi_mask_off(dev, msi64bit), 0); + pci_set_long(dev->config + msi_pending_off(dev, msi64bit), 0); + } + MSI_DEV_PRINTF(dev, "reset\n"); +} + +static bool msi_is_masked(const PCIDevice *dev, unsigned int vector) +{ + uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev)); + uint32_t mask; + assert(vector < PCI_MSI_VECTORS_MAX); + + if (!(flags & PCI_MSI_FLAGS_MASKBIT)) { + return false; + } + + mask = pci_get_long(dev->config + + msi_mask_off(dev, flags & PCI_MSI_FLAGS_64BIT)); + return mask & (1U << vector); +} + +void msi_notify(PCIDevice *dev, unsigned int vector) +{ + uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev)); + bool msi64bit = flags & PCI_MSI_FLAGS_64BIT; + unsigned int nr_vectors = msi_nr_vectors(flags); + uint64_t address; + uint32_t data; + + assert(vector < nr_vectors); + if (msi_is_masked(dev, vector)) { + assert(flags & PCI_MSI_FLAGS_MASKBIT); + pci_long_test_and_set_mask( + dev->config + msi_pending_off(dev, msi64bit), 1U << vector); + MSI_DEV_PRINTF(dev, "pending vector 0x%x\n", vector); + return; + } + + if (msi64bit) { + address = pci_get_quad(dev->config + msi_address_lo_off(dev)); + } else { + address = pci_get_long(dev->config + msi_address_lo_off(dev)); + } + + /* upper bit 31:16 is zero */ + data = pci_get_word(dev->config + msi_data_off(dev, msi64bit)); + if (nr_vectors > 1) { + data &= ~(nr_vectors - 1); + data |= vector; + } + + MSI_DEV_PRINTF(dev, + "notify vector 0x%x" + " address: 0x%"PRIx64" data: 0x%"PRIx32"\n", + vector, address, data); + stl_phys(address, data); +} + +/* call this function after updating configs by pci_default_write_config(). */ +void msi_write_config(PCIDevice *dev, uint32_t addr, uint32_t val, int len) +{ + uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev)); + bool msi64bit = flags & PCI_MSI_FLAGS_64BIT; + bool msi_per_vector_mask = flags & PCI_MSI_FLAGS_MASKBIT; + unsigned int nr_vectors; + uint8_t log_num_vecs; + uint8_t log_max_vecs; + unsigned int vector; + uint32_t pending; + + if (!ranges_overlap(addr, len, dev->msi_cap, msi_cap_sizeof(flags))) { + return; + } + +#ifdef MSI_DEBUG + MSI_DEV_PRINTF(dev, "addr 0x%"PRIx32" val 0x%"PRIx32" len %d\n", + addr, val, len); + MSI_DEV_PRINTF(dev, "ctrl: 0x%"PRIx16" address: 0x%"PRIx32, + flags, + pci_get_long(dev->config + msi_address_lo_off(dev))); + if (msi64bit) { + fprintf(stderr, " address-hi: 0x%"PRIx32, + pci_get_long(dev->config + msi_address_hi_off(dev))); + } + fprintf(stderr, " data: 0x%"PRIx16, + pci_get_word(dev->config + msi_data_off(dev, msi64bit))); + if (flags & PCI_MSI_FLAGS_MASKBIT) { + fprintf(stderr, " mask 0x%"PRIx32" pending 0x%"PRIx32, + pci_get_long(dev->config + msi_mask_off(dev, msi64bit)), + pci_get_long(dev->config + msi_pending_off(dev, msi64bit))); + } + fprintf(stderr, "\n"); +#endif + + if (!(flags & PCI_MSI_FLAGS_ENABLE)) { + return; + } + + /* + * Now MSI is enabled, clear INTx# interrupts. + * the driver is prohibited from writing enable bit to mask + * a service request. But the guest OS could do this. + * So we just discard the interrupts as moderate fallback. + * + * 6.8.3.3. Enabling Operation + * While enabled for MSI or MSI-X operation, a function is prohibited + * from using its INTx# pin (if implemented) to request + * service (MSI, MSI-X, and INTx# are mutually exclusive). + */ + pci_device_deassert_intx(dev); + + /* + * nr_vectors might be set bigger than capable. So clamp it. + * This is not legal by spec, so we can do anything we like, + * just don't crash the host + */ + log_num_vecs = + (flags & PCI_MSI_FLAGS_QSIZE) >> (ffs(PCI_MSI_FLAGS_QSIZE) - 1); + log_max_vecs = + (flags & PCI_MSI_FLAGS_QMASK) >> (ffs(PCI_MSI_FLAGS_QMASK) - 1); + if (log_num_vecs > log_max_vecs) { + flags &= ~PCI_MSI_FLAGS_QSIZE; + flags |= log_max_vecs << (ffs(PCI_MSI_FLAGS_QSIZE) - 1); + pci_set_word(dev->config + msi_flags_off(dev), flags); + } + + if (!msi_per_vector_mask) { + /* if per vector masking isn't supported, + there is no pending interrupt. */ + return; + } + + nr_vectors = msi_nr_vectors(flags); + + /* This will discard pending interrupts, if any. */ + pending = pci_get_long(dev->config + msi_pending_off(dev, msi64bit)); + pending &= 0xffffffff >> (PCI_MSI_VECTORS_MAX - nr_vectors); + pci_set_long(dev->config + msi_pending_off(dev, msi64bit), pending); + + /* deliver pending interrupts which are unmasked */ + for (vector = 0; vector < nr_vectors; ++vector) { + if (msi_is_masked(dev, vector) || !(pending & (1U << vector))) { + continue; + } + + pci_long_test_and_clear_mask( + dev->config + msi_pending_off(dev, msi64bit), 1U << vector); + msi_notify(dev, vector); + } +} + +unsigned int msi_nr_vectors_allocated(const PCIDevice *dev) +{ + uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev)); + return msi_nr_vectors(flags); +} diff -Nru qemu-kvm-0.12.5+noroms/hw/msi.h qemu-kvm-0.14.1/hw/msi.h --- qemu-kvm-0.12.5+noroms/hw/msi.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/msi.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,41 @@ +/* + * msi.h + * + * Copyright (c) 2010 Isaku Yamahata + * VA Linux Systems Japan K.K. + * + * 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, see . + */ + +#ifndef QEMU_MSI_H +#define QEMU_MSI_H + +#include "qemu-common.h" +#include "pci.h" + +bool msi_enabled(const PCIDevice *dev); +int msi_init(struct PCIDevice *dev, uint8_t offset, + unsigned int nr_vectors, bool msi64bit, bool msi_per_vector_mask); +void msi_uninit(struct PCIDevice *dev); +void msi_reset(PCIDevice *dev); +void msi_notify(PCIDevice *dev, unsigned int vector); +void msi_write_config(PCIDevice *dev, uint32_t addr, uint32_t val, int len); +unsigned int msi_nr_vectors_allocated(const PCIDevice *dev); + +static inline bool msi_present(const PCIDevice *dev) +{ + return dev->cap_present & QEMU_PCI_CAP_MSI; +} + +#endif /* QEMU_MSI_H */ diff -Nru qemu-kvm-0.12.5+noroms/hw/msix.c qemu-kvm-0.14.1/hw/msix.c --- qemu-kvm-0.12.5+noroms/hw/msix.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/msix.c 2011-05-11 13:29:46.000000000 +0000 @@ -14,16 +14,8 @@ #include "hw.h" #include "msix.h" #include "pci.h" -#define QEMU_KVM_NO_CPU -#include "qemu-kvm.h" - -/* Declaration from linux/pci_regs.h */ -#define PCI_CAP_ID_MSIX 0x11 /* MSI-X */ -#define PCI_MSIX_FLAGS 2 /* Table at lower 11 bits */ -#define PCI_MSIX_FLAGS_QSIZE 0x7FF -#define PCI_MSIX_FLAGS_ENABLE (1 << 15) -#define PCI_MSIX_FLAGS_MASKALL (1 << 14) -#define PCI_MSIX_FLAGS_BIRMASK (7 << 0) +#include "range.h" +#include "kvm.h" /* MSI-X capability structure */ #define MSIX_TABLE_OFFSET 4 @@ -52,49 +44,41 @@ #define MSIX_MAX_ENTRIES 32 -#ifdef MSIX_DEBUG -#define DEBUG(fmt, ...) \ - do { \ - fprintf(stderr, "%s: " fmt, __func__ , __VA_ARGS__); \ - } while (0) -#else -#define DEBUG(fmt, ...) do { } while(0) -#endif - /* Flag for interrupt controller to declare MSI-X support */ int msix_supported; -#ifdef CONFIG_KVM /* KVM specific MSIX helpers */ static void kvm_msix_free(PCIDevice *dev) { int vector, changed = 0; + struct kvm_msix_message *kmm; + for (vector = 0; vector < dev->msix_entries_nr; ++vector) { if (dev->msix_entry_used[vector]) { - kvm_del_routing_entry(kvm_context, &dev->msix_irq_entries[vector]); + kmm = &dev->msix_irq_entries[vector]; + kvm_del_msix(kmm->gsi, kmm->addr_lo, kmm->addr_hi, kmm->data); changed = 1; } } if (changed) { - kvm_commit_irq_routes(kvm_context); + kvm_commit_irq_routes(); } } -static void kvm_msix_routing_entry(PCIDevice *dev, unsigned vector, - struct kvm_irq_routing_entry *entry) +static void kvm_msix_message_from_vector(PCIDevice *dev, unsigned vector, + struct kvm_msix_message *kmm) { uint8_t *table_entry = dev->msix_table_page + vector * MSIX_ENTRY_SIZE; - entry->type = KVM_IRQ_ROUTING_MSI; - entry->flags = 0; - entry->u.msi.address_lo = pci_get_long(table_entry + MSIX_MSG_ADDR); - entry->u.msi.address_hi = pci_get_long(table_entry + MSIX_MSG_UPPER_ADDR); - entry->u.msi.data = pci_get_long(table_entry + MSIX_MSG_DATA); + + kmm->addr_lo = pci_get_long(table_entry + MSIX_MSG_ADDR); + kmm->addr_hi = pci_get_long(table_entry + MSIX_MSG_UPPER_ADDR); + kmm->data = pci_get_long(table_entry + MSIX_MSG_DATA); } static void kvm_msix_update(PCIDevice *dev, int vector, int was_masked, int is_masked) { - struct kvm_irq_routing_entry e = {}, *entry; + struct kvm_msix_message e = {}, *entry; int mask_cleared = was_masked && !is_masked; /* It is only legal to change an entry when it is masked. Therefore, it is * enough to update the routing in kernel when mask is being cleared. */ @@ -106,17 +90,20 @@ } entry = dev->msix_irq_entries + vector; e.gsi = entry->gsi; - kvm_msix_routing_entry(dev, vector, &e); - if (memcmp(&entry->u.msi, &e.u.msi, sizeof entry->u.msi)) { + kvm_msix_message_from_vector(dev, vector, &e); + if (memcmp(entry, &e, sizeof e) != 0) { int r; - r = kvm_update_routing_entry(kvm_context, entry, &e); + + r = kvm_update_msix(entry->gsi, entry->addr_lo, + entry->addr_hi, entry->data, + e.gsi, e.addr_lo, e.addr_hi, e.data); if (r) { - fprintf(stderr, "%s: kvm_update_routing_entry failed: %s\n", __func__, + fprintf(stderr, "%s: kvm_update_msix failed: %s\n", __func__, strerror(-r)); exit(1); } - memcpy(&entry->u.msi, &e.u.msi, sizeof entry->u.msi); - r = kvm_commit_irq_routes(kvm_context); + *entry = e; + r = kvm_commit_irq_routes(); if (r) { fprintf(stderr, "%s: kvm_commit_irq_routes failed: %s\n", __func__, strerror(-r)); @@ -127,30 +114,30 @@ static int kvm_msix_add(PCIDevice *dev, unsigned vector) { - struct kvm_irq_routing_entry *entry = dev->msix_irq_entries + vector; + struct kvm_msix_message *kmm = dev->msix_irq_entries + vector; int r; - if (!kvm_has_gsi_routing(kvm_context)) { + if (!kvm_has_gsi_routing()) { fprintf(stderr, "Warning: no MSI-X support found. " "At least kernel 2.6.30 is required for MSI-X support.\n" ); return -EOPNOTSUPP; } - r = kvm_get_irq_route_gsi(kvm_context); + r = kvm_get_irq_route_gsi(); if (r < 0) { fprintf(stderr, "%s: kvm_get_irq_route_gsi failed: %s\n", __func__, strerror(-r)); return r; } - entry->gsi = r; - kvm_msix_routing_entry(dev, vector, entry); - r = kvm_add_routing_entry(kvm_context, entry); + kmm->gsi = r; + kvm_msix_message_from_vector(dev, vector, kmm); + r = kvm_add_msix(kmm->gsi, kmm->addr_lo, kmm->addr_hi, kmm->data); if (r < 0) { - fprintf(stderr, "%s: kvm_add_routing_entry failed: %s\n", __func__, strerror(-r)); + fprintf(stderr, "%s: kvm_add_msix failed: %s\n", __func__, strerror(-r)); return r; } - r = kvm_commit_irq_routes(kvm_context); + r = kvm_commit_irq_routes(); if (r < 0) { fprintf(stderr, "%s: kvm_commit_irq_routes failed: %s\n", __func__, strerror(-r)); return r; @@ -160,20 +147,15 @@ static void kvm_msix_del(PCIDevice *dev, unsigned vector) { + struct kvm_msix_message *kmm; + if (dev->msix_entry_used[vector]) { return; } - kvm_del_routing_entry(kvm_context, &dev->msix_irq_entries[vector]); - kvm_commit_irq_routes(kvm_context); + kmm = &dev->msix_irq_entries[vector]; + kvm_del_msix(kmm->gsi, kmm->addr_lo, kmm->addr_hi, kmm->data); + kvm_commit_irq_routes(); } -#else - -static void kvm_msix_free(PCIDevice *dev) {} -static void kvm_msix_update(PCIDevice *dev, int vector, - int was_masked, int is_masked) {} -static int kvm_msix_add(PCIDevice *dev, unsigned vector) { return -1; } -static void kvm_msix_del(PCIDevice *dev, unsigned vector) {} -#endif /* Add MSI-X capability to the config space for the device. */ /* Given a bar and its size, add MSI-X table on top of it @@ -185,35 +167,43 @@ { int config_offset; uint8_t *config; - uint32_t new_size; - if (nentries < 1 || nentries > PCI_MSIX_FLAGS_QSIZE + 1) - return -EINVAL; - if (bar_size > 0x80000000) - return -ENOSPC; + pdev->msix_bar_size = bar_size; + + config_offset = pci_find_capability(pdev, PCI_CAP_ID_MSIX); + + if (!config_offset) { + uint32_t new_size; + + if (nentries < 1 || nentries > PCI_MSIX_FLAGS_QSIZE + 1) + return -EINVAL; + if (bar_size > 0x80000000) + return -ENOSPC; + + /* Add space for MSI-X structures */ + if (!bar_size) { + new_size = MSIX_PAGE_SIZE; + } else if (bar_size < MSIX_PAGE_SIZE) { + bar_size = MSIX_PAGE_SIZE; + new_size = MSIX_PAGE_SIZE * 2; + } else { + new_size = bar_size * 2; + } - /* Add space for MSI-X structures */ - if (!bar_size) { - new_size = MSIX_PAGE_SIZE; - } else if (bar_size < MSIX_PAGE_SIZE) { - bar_size = MSIX_PAGE_SIZE; - new_size = MSIX_PAGE_SIZE * 2; - } else { - new_size = bar_size * 2; - } - - pdev->msix_bar_size = new_size; - config_offset = pci_add_capability(pdev, PCI_CAP_ID_MSIX, MSIX_CAP_LENGTH); - if (config_offset < 0) - return config_offset; - config = pdev->config + config_offset; - - pci_set_word(config + PCI_MSIX_FLAGS, nentries - 1); - /* Table on top of BAR */ - pci_set_long(config + MSIX_TABLE_OFFSET, bar_size | bar_nr); - /* Pending bits on top of that */ - pci_set_long(config + MSIX_PBA_OFFSET, (bar_size + MSIX_PAGE_PENDING) | - bar_nr); + pdev->msix_bar_size = new_size; + config_offset = pci_add_capability(pdev, PCI_CAP_ID_MSIX, + 0, MSIX_CAP_LENGTH); + if (config_offset < 0) + return config_offset; + config = pdev->config + config_offset; + + pci_set_word(config + PCI_MSIX_FLAGS, nentries - 1); + /* Table on top of BAR */ + pci_set_long(config + MSIX_TABLE_OFFSET, bar_size | bar_nr); + /* Pending bits on top of that */ + pci_set_long(config + MSIX_PBA_OFFSET, (bar_size + MSIX_PAGE_PENDING) | + bar_nr); + } pdev->msix_cap = config_offset; /* Make flags bit writeable. */ pdev->wmask[config_offset + MSIX_CONTROL_OFFSET] |= MSIX_ENABLE_MASK | @@ -288,7 +278,7 @@ unsigned enable_pos = dev->msix_cap + MSIX_CONTROL_OFFSET; int vector; - if (addr + len <= enable_pos || addr > enable_pos) { + if (!range_covers_byte(addr, len, enable_pos)) { return; } @@ -296,7 +286,7 @@ return; } - qemu_set_irq(dev->irq[0], 0); + pci_device_deassert_intx(dev); if (msix_function_masked(dev)) { return; @@ -318,6 +308,11 @@ if (kvm_enabled() && kvm_irqchip_in_kernel()) { kvm_msix_update(dev, vector, was_masked, msix_is_masked(dev, vector)); } + if (was_masked != msix_is_masked(dev, vector) && dev->msix_mask_notifier) { + int r = dev->msix_mask_notifier(dev, vector, + msix_is_masked(dev, vector)); + assert(r >= 0); + } msix_handle_mask_update(dev, vector); } @@ -350,16 +345,24 @@ return; if (size <= offset) return; - cpu_register_physical_memory(addr + offset, size - offset, + cpu_register_physical_memory(addr + offset, + MIN(size - offset, MSIX_PAGE_SIZE), d->msix_mmio_index); } static void msix_mask_all(struct PCIDevice *dev, unsigned nentries) { - int vector; + int vector, r; for (vector = 0; vector < nentries; ++vector) { unsigned offset = vector * MSIX_ENTRY_SIZE + MSIX_VECTOR_CTRL; + int was_masked = msix_is_masked(dev, vector); dev->msix_table_page[offset] |= MSIX_VECTOR_MASK; + if (was_masked != msix_is_masked(dev, vector) && + dev->msix_mask_notifier) { + r = dev->msix_mask_notifier(dev, vector, + msix_is_masked(dev, vector)); + assert(r >= 0); + } } } @@ -376,12 +379,7 @@ if (nentries > MSIX_MAX_ENTRIES) return -EINVAL; -#ifdef KVM_CAP_IRQCHIP - if (kvm_enabled() && kvm_irqchip_in_kernel()) { - dev->msix_irq_entries = qemu_malloc(nentries * - sizeof *dev->msix_irq_entries); - } -#endif + dev->msix_mask_notifier = NULL; dev->msix_entry_used = qemu_mallocz(MSIX_MAX_ENTRIES * sizeof *dev->msix_entry_used); @@ -389,7 +387,8 @@ msix_mask_all(dev, nentries); dev->msix_mmio_index = cpu_register_io_memory(msix_mmio_read, - msix_mmio_write, dev); + msix_mmio_write, dev, + DEVICE_NATIVE_ENDIAN); if (dev->msix_mmio_index == -1) { ret = -EBUSY; goto err_index; @@ -400,6 +399,11 @@ if (ret) goto err_config; + if (kvm_enabled() && kvm_irqchip_in_kernel()) { + dev->msix_irq_entries = qemu_malloc(nentries * + sizeof *dev->msix_irq_entries); + } + dev->cap_present |= QEMU_PCI_CAP_MSIX; return 0; @@ -515,12 +519,10 @@ return; } -#ifdef KVM_CAP_IRQCHIP if (kvm_enabled() && kvm_irqchip_in_kernel()) { kvm_set_irq(dev->msix_irq_entries[vector].gsi, 1, NULL); return; } -#endif address = pci_get_long(table_entry + MSIX_MSG_UPPER_ADDR); address = (address << 32) | pci_get_long(table_entry + MSIX_MSG_ADDR); @@ -587,3 +589,66 @@ return; msix_free_irq_entries(dev); } + +/* Invoke the notifier if vector entry is used and unmasked. */ +static int msix_notify_if_unmasked(PCIDevice *dev, unsigned vector, int masked) +{ + assert(dev->msix_mask_notifier); + if (!dev->msix_entry_used[vector] || msix_is_masked(dev, vector)) { + return 0; + } + return dev->msix_mask_notifier(dev, vector, masked); +} + +static int msix_set_mask_notifier_for_vector(PCIDevice *dev, unsigned vector) +{ + /* Notifier has been set. Invoke it on unmasked vectors. */ + return msix_notify_if_unmasked(dev, vector, 0); +} + +static int msix_unset_mask_notifier_for_vector(PCIDevice *dev, unsigned vector) +{ + /* Notifier will be unset. Invoke it to mask unmasked entries. */ + return msix_notify_if_unmasked(dev, vector, 1); +} + +int msix_set_mask_notifier(PCIDevice *dev, msix_mask_notifier_func f) +{ + int r, n; + assert(!dev->msix_mask_notifier); + dev->msix_mask_notifier = f; + for (n = 0; n < dev->msix_entries_nr; ++n) { + r = msix_set_mask_notifier_for_vector(dev, n); + if (r < 0) { + goto undo; + } + } + return 0; + +undo: + while (--n >= 0) { + msix_unset_mask_notifier_for_vector(dev, n); + } + dev->msix_mask_notifier = NULL; + return r; +} + +int msix_unset_mask_notifier(PCIDevice *dev) +{ + int r, n; + assert(dev->msix_mask_notifier); + for (n = 0; n < dev->msix_entries_nr; ++n) { + r = msix_unset_mask_notifier_for_vector(dev, n); + if (r < 0) { + goto undo; + } + } + dev->msix_mask_notifier = NULL; + return 0; + +undo: + while (--n >= 0) { + msix_set_mask_notifier_for_vector(dev, n); + } + return r; +} diff -Nru qemu-kvm-0.12.5+noroms/hw/msix.h qemu-kvm-0.14.1/hw/msix.h --- qemu-kvm-0.12.5+noroms/hw/msix.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/msix.h 2011-05-11 13:29:46.000000000 +0000 @@ -33,4 +33,6 @@ extern int msix_supported; +int msix_set_mask_notifier(PCIDevice *dev, msix_mask_notifier_func); +int msix_unset_mask_notifier(PCIDevice *dev); #endif diff -Nru qemu-kvm-0.12.5+noroms/hw/mst_fpga.c qemu-kvm-0.14.1/hw/mst_fpga.c --- qemu-kvm-0.12.5+noroms/hw/mst_fpga.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/mst_fpga.c 2011-05-11 13:29:46.000000000 +0000 @@ -232,8 +232,9 @@ s->pins = qi; iomemtype = cpu_register_io_memory(mst_fpga_readfn, - mst_fpga_writefn, s); + mst_fpga_writefn, s, DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base, 0x00100000, iomemtype); - register_savevm("mainstone_fpga", 0, 0, mst_fpga_save, mst_fpga_load, s); + register_savevm(NULL, "mainstone_fpga", 0, 0, mst_fpga_save, + mst_fpga_load, s); return qi; } diff -Nru qemu-kvm-0.12.5+noroms/hw/multiboot.c qemu-kvm-0.14.1/hw/multiboot.c --- qemu-kvm-0.12.5+noroms/hw/multiboot.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/multiboot.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,339 @@ +/* + * QEMU PC System Emulator + * + * Copyright (c) 2003-2004 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 "hw.h" +#include "fw_cfg.h" +#include "multiboot.h" +#include "loader.h" +#include "elf.h" +#include "sysemu.h" + +/* Show multiboot debug output */ +//#define DEBUG_MULTIBOOT + +#ifdef DEBUG_MULTIBOOT +#define mb_debug(a...) fprintf(stderr, ## a) +#else +#define mb_debug(a...) +#endif + +#define MULTIBOOT_STRUCT_ADDR 0x9000 + +#if MULTIBOOT_STRUCT_ADDR > 0xf0000 +#error multiboot struct needs to fit in 16 bit real mode +#endif + +enum { + /* Multiboot info */ + MBI_FLAGS = 0, + MBI_MEM_LOWER = 4, + MBI_MEM_UPPER = 8, + MBI_BOOT_DEVICE = 12, + MBI_CMDLINE = 16, + MBI_MODS_COUNT = 20, + MBI_MODS_ADDR = 24, + MBI_MMAP_ADDR = 48, + + MBI_SIZE = 88, + + /* Multiboot modules */ + MB_MOD_START = 0, + MB_MOD_END = 4, + MB_MOD_CMDLINE = 8, + + MB_MOD_SIZE = 16, + + /* Region offsets */ + ADDR_E820_MAP = MULTIBOOT_STRUCT_ADDR + 0, + ADDR_MBI = ADDR_E820_MAP + 0x500, + + /* Multiboot flags */ + MULTIBOOT_FLAGS_MEMORY = 1 << 0, + MULTIBOOT_FLAGS_BOOT_DEVICE = 1 << 1, + MULTIBOOT_FLAGS_CMDLINE = 1 << 2, + MULTIBOOT_FLAGS_MODULES = 1 << 3, + MULTIBOOT_FLAGS_MMAP = 1 << 6, +}; + +typedef struct { + /* buffer holding kernel, cmdlines and mb_infos */ + void *mb_buf; + /* address in target */ + target_phys_addr_t mb_buf_phys; + /* size of mb_buf in bytes */ + unsigned mb_buf_size; + /* offset of mb-info's in bytes */ + target_phys_addr_t offset_mbinfo; + /* offset in buffer for cmdlines in bytes */ + target_phys_addr_t offset_cmdlines; + /* offset of modules in bytes */ + target_phys_addr_t offset_mods; + /* available slots for mb modules infos */ + int mb_mods_avail; + /* currently used slots of mb modules */ + int mb_mods_count; +} MultibootState; + +static uint32_t mb_add_cmdline(MultibootState *s, const char *cmdline) +{ + int len = strlen(cmdline) + 1; + target_phys_addr_t p = s->offset_cmdlines; + + pstrcpy((char *)s->mb_buf + p, len, cmdline); + s->offset_cmdlines += len; + return s->mb_buf_phys + p; +} + +static void mb_add_mod(MultibootState *s, + target_phys_addr_t start, target_phys_addr_t end, + target_phys_addr_t cmdline_phys) +{ + char *p; + assert(s->mb_mods_count < s->mb_mods_avail); + + p = (char *)s->mb_buf + s->offset_mbinfo + MB_MOD_SIZE * s->mb_mods_count; + + stl_p(p + MB_MOD_START, start); + stl_p(p + MB_MOD_END, end); + stl_p(p + MB_MOD_CMDLINE, cmdline_phys); + + mb_debug("mod%02d: "TARGET_FMT_plx" - "TARGET_FMT_plx"\n", + s->mb_mods_count, start, end); + + s->mb_mods_count++; +} + +int load_multiboot(void *fw_cfg, + FILE *f, + const char *kernel_filename, + const char *initrd_filename, + const char *kernel_cmdline, + int kernel_file_size, + uint8_t *header) +{ + int i, is_multiboot = 0; + uint32_t flags = 0; + uint32_t mh_entry_addr; + uint32_t mh_load_addr; + uint32_t mb_kernel_size; + MultibootState mbs; + uint8_t bootinfo[MBI_SIZE]; + uint8_t *mb_bootinfo_data; + + /* Ok, let's see if it is a multiboot image. + The header is 12x32bit long, so the latest entry may be 8192 - 48. */ + for (i = 0; i < (8192 - 48); i += 4) { + if (ldl_p(header+i) == 0x1BADB002) { + uint32_t checksum = ldl_p(header+i+8); + flags = ldl_p(header+i+4); + checksum += flags; + checksum += (uint32_t)0x1BADB002; + if (!checksum) { + is_multiboot = 1; + break; + } + } + } + + if (!is_multiboot) + return 0; /* no multiboot */ + + mb_debug("qemu: I believe we found a multiboot image!\n"); + memset(bootinfo, 0, sizeof(bootinfo)); + memset(&mbs, 0, sizeof(mbs)); + + if (flags & 0x00000004) { /* MULTIBOOT_HEADER_HAS_VBE */ + fprintf(stderr, "qemu: multiboot knows VBE. we don't.\n"); + } + if (!(flags & 0x00010000)) { /* MULTIBOOT_HEADER_HAS_ADDR */ + uint64_t elf_entry; + uint64_t elf_low, elf_high; + int kernel_size; + fclose(f); + + if (((struct elf64_hdr*)header)->e_machine == EM_X86_64) { + fprintf(stderr, "Cannot load x86-64 image, give a 32bit one.\n"); + exit(1); + } + + kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry, + &elf_low, &elf_high, 0, ELF_MACHINE, 0); + if (kernel_size < 0) { + fprintf(stderr, "Error while loading elf kernel\n"); + exit(1); + } + mh_load_addr = elf_low; + mb_kernel_size = elf_high - elf_low; + mh_entry_addr = elf_entry; + + mbs.mb_buf = qemu_malloc(mb_kernel_size); + if (rom_copy(mbs.mb_buf, mh_load_addr, mb_kernel_size) != mb_kernel_size) { + fprintf(stderr, "Error while fetching elf kernel from rom\n"); + exit(1); + } + + mb_debug("qemu: loading multiboot-elf kernel (%#x bytes) with entry %#zx\n", + mb_kernel_size, (size_t)mh_entry_addr); + } else { + /* Valid if mh_flags sets MULTIBOOT_HEADER_HAS_ADDR. */ + uint32_t mh_header_addr = ldl_p(header+i+12); + mh_load_addr = ldl_p(header+i+16); + uint32_t mb_kernel_text_offset = i - (mh_header_addr - mh_load_addr); + + mh_entry_addr = ldl_p(header+i+28); + mb_kernel_size = kernel_file_size - mb_kernel_text_offset; + + /* Valid if mh_flags sets MULTIBOOT_HEADER_HAS_VBE. + uint32_t mh_mode_type = ldl_p(header+i+32); + uint32_t mh_width = ldl_p(header+i+36); + uint32_t mh_height = ldl_p(header+i+40); + uint32_t mh_depth = ldl_p(header+i+44); */ + + mb_debug("multiboot: mh_header_addr = %#x\n", mh_header_addr); + mb_debug("multiboot: mh_load_addr = %#x\n", mh_load_addr); + mb_debug("multiboot: mh_load_end_addr = %#x\n", ldl_p(header+i+20)); + mb_debug("multiboot: mh_bss_end_addr = %#x\n", ldl_p(header+i+24)); + mb_debug("qemu: loading multiboot kernel (%#x bytes) at %#x\n", + mb_kernel_size, mh_load_addr); + + mbs.mb_buf = qemu_malloc(mb_kernel_size); + fseek(f, mb_kernel_text_offset, SEEK_SET); + if (fread(mbs.mb_buf, 1, mb_kernel_size, f) != mb_kernel_size) { + fprintf(stderr, "fread() failed\n"); + exit(1); + } + fclose(f); + } + + mbs.mb_buf_phys = mh_load_addr; + + mbs.mb_buf_size = TARGET_PAGE_ALIGN(mb_kernel_size); + mbs.offset_mbinfo = mbs.mb_buf_size; + + /* Calculate space for cmdlines and mb_mods */ + mbs.mb_buf_size += strlen(kernel_filename) + 1; + mbs.mb_buf_size += strlen(kernel_cmdline) + 1; + if (initrd_filename) { + const char *r = initrd_filename; + mbs.mb_buf_size += strlen(r) + 1; + mbs.mb_mods_avail = 1; + while ((r = strchr(r, ','))) { + mbs.mb_mods_avail++; + r++; + } + mbs.mb_buf_size += MB_MOD_SIZE * mbs.mb_mods_avail; + } + + mbs.mb_buf_size = TARGET_PAGE_ALIGN(mbs.mb_buf_size); + + /* enlarge mb_buf to hold cmdlines and mb-info structs */ + mbs.mb_buf = qemu_realloc(mbs.mb_buf, mbs.mb_buf_size); + mbs.offset_cmdlines = mbs.offset_mbinfo + mbs.mb_mods_avail * MB_MOD_SIZE; + + if (initrd_filename) { + char *next_initrd; + + mbs.offset_mods = mbs.mb_buf_size; + + do { + char *next_space; + int mb_mod_length; + uint32_t offs = mbs.mb_buf_size; + + next_initrd = strchr(initrd_filename, ','); + if (next_initrd) + *next_initrd = '\0'; + /* if a space comes after the module filename, treat everything + after that as parameters */ + target_phys_addr_t c = mb_add_cmdline(&mbs, initrd_filename); + if ((next_space = strchr(initrd_filename, ' '))) + *next_space = '\0'; + mb_debug("multiboot loading module: %s\n", initrd_filename); + mb_mod_length = get_image_size(initrd_filename); + if (mb_mod_length < 0) { + fprintf(stderr, "failed to get %s image size\n", initrd_filename); + exit(1); + } + + mbs.mb_buf_size = TARGET_PAGE_ALIGN(mb_mod_length + mbs.mb_buf_size); + mbs.mb_buf = qemu_realloc(mbs.mb_buf, mbs.mb_buf_size); + + load_image(initrd_filename, (unsigned char *)mbs.mb_buf + offs); + mb_add_mod(&mbs, mbs.mb_buf_phys + offs, + mbs.mb_buf_phys + offs + mb_mod_length, c); + + mb_debug("mod_start: %p\nmod_end: %p\n cmdline: "TARGET_FMT_plx"\n", + (char *)mbs.mb_buf + offs, + (char *)mbs.mb_buf + offs + mb_mod_length, c); + initrd_filename = next_initrd+1; + } while (next_initrd); + } + + /* Commandline support */ + char kcmdline[strlen(kernel_filename) + strlen(kernel_cmdline) + 2]; + snprintf(kcmdline, sizeof(kcmdline), "%s %s", + kernel_filename, kernel_cmdline); + stl_p(bootinfo + MBI_CMDLINE, mb_add_cmdline(&mbs, kcmdline)); + + stl_p(bootinfo + MBI_MODS_ADDR, mbs.mb_buf_phys + mbs.offset_mbinfo); + stl_p(bootinfo + MBI_MODS_COUNT, mbs.mb_mods_count); /* mods_count */ + + /* the kernel is where we want it to be now */ + stl_p(bootinfo + MBI_FLAGS, MULTIBOOT_FLAGS_MEMORY + | MULTIBOOT_FLAGS_BOOT_DEVICE + | MULTIBOOT_FLAGS_CMDLINE + | MULTIBOOT_FLAGS_MODULES + | MULTIBOOT_FLAGS_MMAP); + stl_p(bootinfo + MBI_MEM_LOWER, 640); + stl_p(bootinfo + MBI_MEM_UPPER, (ram_size / 1024) - 1024); + stl_p(bootinfo + MBI_BOOT_DEVICE, 0x8001ffff); /* XXX: use the -boot switch? */ + stl_p(bootinfo + MBI_MMAP_ADDR, ADDR_E820_MAP); + + mb_debug("multiboot: mh_entry_addr = %#x\n", mh_entry_addr); + mb_debug(" mb_buf_phys = "TARGET_FMT_plx"\n", mbs.mb_buf_phys); + mb_debug(" mod_start = "TARGET_FMT_plx"\n", mbs.mb_buf_phys + mbs.offset_mods); + mb_debug(" mb_mods_count = %d\n", mbs.mb_mods_count); + + /* save bootinfo off the stack */ + mb_bootinfo_data = qemu_malloc(sizeof(bootinfo)); + memcpy(mb_bootinfo_data, bootinfo, sizeof(bootinfo)); + + /* Pass variables to option rom */ + fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ENTRY, mh_entry_addr); + fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, mh_load_addr); + fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, mbs.mb_buf_size); + fw_cfg_add_bytes(fw_cfg, FW_CFG_KERNEL_DATA, + mbs.mb_buf, mbs.mb_buf_size); + + fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, ADDR_MBI); + fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, sizeof(bootinfo)); + fw_cfg_add_bytes(fw_cfg, FW_CFG_INITRD_DATA, mb_bootinfo_data, + sizeof(bootinfo)); + + option_rom[nb_option_roms].name = "multiboot.bin"; + option_rom[nb_option_roms].bootindex = 0; + nb_option_roms++; + + return 1; /* yes, we are multiboot */ +} diff -Nru qemu-kvm-0.12.5+noroms/hw/multiboot.h qemu-kvm-0.14.1/hw/multiboot.h --- qemu-kvm-0.12.5+noroms/hw/multiboot.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/multiboot.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,12 @@ +#ifndef QEMU_MULTIBOOT_H +#define QEMU_MULTIBOOT_H + +int load_multiboot(void *fw_cfg, + FILE *f, + const char *kernel_filename, + const char *initrd_filename, + const char *kernel_cmdline, + int kernel_file_size, + uint8_t *header); + +#endif diff -Nru qemu-kvm-0.12.5+noroms/hw/musicpal.c qemu-kvm-0.14.1/hw/musicpal.c --- qemu-kvm-0.12.5+noroms/hw/musicpal.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/musicpal.c 2011-05-11 13:29:46.000000000 +0000 @@ -18,6 +18,7 @@ #include "flash.h" #include "console.h" #include "i2c.h" +#include "blockdev.h" #define MP_MISC_BASE 0x80002000 #define MP_MISC_SIZE 0x00001000 @@ -387,7 +388,8 @@ s->nic = qemu_new_nic(&net_mv88w8618_info, &s->conf, dev->qdev.info->name, dev->qdev.id, s); s->mmio_index = cpu_register_io_memory(mv88w8618_eth_readfn, - mv88w8618_eth_writefn, s); + mv88w8618_eth_writefn, s, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, MP_ETH_SIZE, s->mmio_index); return 0; } @@ -599,7 +601,8 @@ s->brightness = 7; iomemtype = cpu_register_io_memory(musicpal_lcd_readfn, - musicpal_lcd_writefn, s); + musicpal_lcd_writefn, s, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, MP_LCD_SIZE, iomemtype); s->ds = graphic_console_init(lcd_refresh, lcd_invalidate, @@ -724,7 +727,8 @@ qdev_init_gpio_in(&dev->qdev, mv88w8618_pic_set_irq, 32); sysbus_init_irq(dev, &s->parent_irq); iomemtype = cpu_register_io_memory(mv88w8618_pic_readfn, - mv88w8618_pic_writefn, s); + mv88w8618_pic_writefn, s, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, MP_PIC_SIZE, iomemtype); return 0; } @@ -885,7 +889,8 @@ } iomemtype = cpu_register_io_memory(mv88w8618_pit_readfn, - mv88w8618_pit_writefn, s); + mv88w8618_pit_writefn, s, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, MP_PIT_SIZE, iomemtype); return 0; } @@ -975,7 +980,8 @@ s->cfgr0 = 0xfffe4285; /* Default as set by U-Boot for 8 MB flash */ iomemtype = cpu_register_io_memory(mv88w8618_flashcfg_readfn, - mv88w8618_flashcfg_writefn, s); + mv88w8618_flashcfg_writefn, s, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, MP_FLASHCFG_SIZE, iomemtype); return 0; } @@ -1036,7 +1042,8 @@ int iomemtype; iomemtype = cpu_register_io_memory(musicpal_misc_readfn, - musicpal_misc_writefn, NULL); + musicpal_misc_writefn, NULL, + DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(MP_MISC_BASE, MP_MISC_SIZE, iomemtype); } @@ -1081,7 +1088,8 @@ int iomemtype; iomemtype = cpu_register_io_memory(mv88w8618_wlan_readfn, - mv88w8618_wlan_writefn, NULL); + mv88w8618_wlan_writefn, NULL, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, MP_WLAN_SIZE, iomemtype); return 0; } @@ -1292,11 +1300,10 @@ sysbus_init_irq(dev, &s->irq); iomemtype = cpu_register_io_memory(musicpal_gpio_readfn, - musicpal_gpio_writefn, s); + musicpal_gpio_writefn, s, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, MP_GPIO_SIZE, iomemtype); - musicpal_gpio_reset(&dev->qdev); - qdev_init_gpio_out(&dev->qdev, s->out, ARRAY_SIZE(s->out)); qdev_init_gpio_in(&dev->qdev, musicpal_gpio_pin_event, 32); @@ -1488,10 +1495,8 @@ DeviceState *i2c_dev; DeviceState *lcd_dev; DeviceState *key_dev; -#ifdef HAS_AUDIO DeviceState *wm8750_dev; SysBusDevice *s; -#endif i2c_bus *i2c; int i; unsigned long flash_size; @@ -1510,9 +1515,10 @@ /* For now we use a fixed - the original - RAM size */ cpu_register_physical_memory(0, MP_RAM_DEFAULT_SIZE, - qemu_ram_alloc(MP_RAM_DEFAULT_SIZE)); + qemu_ram_alloc(NULL, "musicpal.ram", + MP_RAM_DEFAULT_SIZE)); - sram_off = qemu_ram_alloc(MP_SRAM_SIZE); + sram_off = qemu_ram_alloc(NULL, "musicpal.sram", MP_SRAM_SIZE); cpu_register_physical_memory(MP_SRAM_BASE, MP_SRAM_SIZE, sram_off); dev = sysbus_create_simple("mv88w8618_pic", MP_PIC_BASE, @@ -1525,12 +1531,22 @@ pic[MP_TIMER4_IRQ], NULL); if (serial_hds[0]) { +#ifdef TARGET_WORDS_BIGENDIAN serial_mm_init(MP_UART1_BASE, 2, pic[MP_UART1_IRQ], 1825000, - serial_hds[0], 1); + serial_hds[0], 1, 1); +#else + serial_mm_init(MP_UART1_BASE, 2, pic[MP_UART1_IRQ], 1825000, + serial_hds[0], 1, 0); +#endif } if (serial_hds[1]) { +#ifdef TARGET_WORDS_BIGENDIAN serial_mm_init(MP_UART2_BASE, 2, pic[MP_UART2_IRQ], 1825000, - serial_hds[1], 1); + serial_hds[1], 1, 1); +#else + serial_mm_init(MP_UART2_BASE, 2, pic[MP_UART2_IRQ], 1825000, + serial_hds[1], 1, 0); +#endif } /* Register flash */ @@ -1548,12 +1564,24 @@ * 0xFF800000 (if there is 8 MB flash). So remap flash access if the * image is smaller than 32 MB. */ - pflash_cfi02_register(0-MP_FLASH_SIZE_MAX, qemu_ram_alloc(flash_size), +#ifdef TARGET_WORDS_BIGENDIAN + pflash_cfi02_register(0-MP_FLASH_SIZE_MAX, qemu_ram_alloc(NULL, + "musicpal.flash", flash_size), dinfo->bdrv, 0x10000, (flash_size + 0xffff) >> 16, MP_FLASH_SIZE_MAX / flash_size, 2, 0x00BF, 0x236D, 0x0000, 0x0000, - 0x5555, 0x2AAA); + 0x5555, 0x2AAA, 1); +#else + pflash_cfi02_register(0-MP_FLASH_SIZE_MAX, qemu_ram_alloc(NULL, + "musicpal.flash", flash_size), + dinfo->bdrv, 0x10000, + (flash_size + 0xffff) >> 16, + MP_FLASH_SIZE_MAX / flash_size, + 2, 0x00BF, 0x236D, 0x0000, 0x0000, + 0x5555, 0x2AAA, 0); +#endif + } sysbus_create_simple("mv88w8618_flashcfg", MP_FLASHCFG_BASE, NULL); @@ -1593,7 +1621,6 @@ qdev_connect_gpio_out(key_dev, i, qdev_get_gpio_in(dev, i + 15)); } -#ifdef HAS_AUDIO wm8750_dev = i2c_create_slave(i2c, "wm8750", MP_WM_ADDR); dev = qdev_create(NULL, "mv88w8618_audio"); s = sysbus_from_qdev(dev); @@ -1601,7 +1628,6 @@ qdev_init_nofail(dev); sysbus_mmio_map(s, 0, MP_AUDIO_BASE); sysbus_connect_irq(s, 0, pic[MP_AUDIO_IRQ]); -#endif musicpal_binfo.ram_size = MP_RAM_DEFAULT_SIZE; musicpal_binfo.kernel_filename = kernel_filename; diff -Nru qemu-kvm-0.12.5+noroms/hw/nand.c qemu-kvm-0.14.1/hw/nand.c --- qemu-kvm-0.12.5+noroms/hw/nand.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/nand.c 2011-05-11 13:29:46.000000000 +0000 @@ -13,9 +13,8 @@ # include "hw.h" # include "flash.h" -# include "block.h" +# include "blockdev.h" /* FIXME: Pass block device as an argument. */ -# include "sysemu.h" # define NAND_CMD_READ0 0x00 # define NAND_CMD_READ1 0x01 @@ -212,6 +211,7 @@ static void nand_command(NANDFlashState *s) { + unsigned int offset; switch (s->cmd) { case NAND_CMD_READ0: s->iolen = 0; @@ -233,8 +233,12 @@ case NAND_CMD_NOSERIALREAD2: if (!(nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP)) break; - - s->blk_load(s, s->addr, s->addr & ((1 << s->addr_shift) - 1)); + offset = s->addr & ((1 << s->addr_shift) - 1); + s->blk_load(s, s->addr, offset); + if (s->gnd) + s->iolen = (1 << s->page_shift) - offset; + else + s->iolen = (1 << s->page_shift) + (1 << s->oob_shift) - offset; break; case NAND_CMD_RESET: @@ -380,12 +384,15 @@ if (s->cmd != NAND_CMD_RANDOMREAD2) { s->addrlen = 0; - s->addr = 0; } } if (s->ale) { - s->addr |= value << (s->addrlen * 8); + unsigned int shift = s->addrlen * 8; + unsigned int mask = ~(0xff << shift); + unsigned int v = value << shift; + + s->addr = (s->addr & mask) | v; s->addrlen ++; if (s->addrlen == 1 && s->cmd == NAND_CMD_READID) @@ -435,6 +442,7 @@ return 0; s->iolen --; + s->addr++; return *(s->ioaddr ++); } @@ -494,7 +502,7 @@ is used. */ s->ioaddr = s->io; - register_savevm("nand", -1, 0, nand_save, nand_load, s); + register_savevm(NULL, "nand", -1, 0, nand_save, nand_load, s); return s; } @@ -633,9 +641,6 @@ offset, PAGE_SIZE + OOB_SIZE - offset); s->ioaddr = s->io; } - - s->addr &= PAGE_SIZE - 1; - s->addr += PAGE_SIZE; } static void glue(nand_init_, PAGE_SIZE)(NANDFlashState *s) diff -Nru qemu-kvm-0.12.5+noroms/hw/ne2000.c qemu-kvm-0.14.1/hw/ne2000.c --- qemu-kvm-0.12.5+noroms/hw/ne2000.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/ne2000.c 2011-05-11 13:29:46.000000000 +0000 @@ -26,6 +26,7 @@ #include "net.h" #include "ne2000.h" #include "loader.h" +#include "sysemu.h" /* debug NE2000 card */ //#define DEBUG_NE2000 @@ -723,8 +724,8 @@ pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_REALTEK); pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_REALTEK_8029); pci_config_set_class(pci_conf, PCI_CLASS_NETWORK_ETHERNET); - pci_conf[PCI_HEADER_TYPE] = PCI_HEADER_TYPE_NORMAL; // header_type - pci_conf[0x3d] = 1; // interrupt pin 0 + /* TODO: RST# value should be 0. PCI spec 6.2.4 */ + pci_conf[PCI_INTERRUPT_PIN] = 1; // interrupt pin 0 pci_register_bar(&d->dev, 0, 0x100, PCI_BASE_ADDRESS_SPACE_IO, ne2000_map); @@ -741,11 +742,13 @@ if (!pci_dev->qdev.hotplugged) { static int loaded = 0; if (!loaded) { - rom_add_option("pxe-ne2k_pci.bin"); + rom_add_option("pxe-ne2k_pci.bin", -1); loaded = 1; } } + add_boot_device_path(s->c.bootindex, &pci_dev->qdev, "/ethernet-phy@0"); + return 0; } diff -Nru qemu-kvm-0.12.5+noroms/hw/ne2000-isa.c qemu-kvm-0.14.1/hw/ne2000-isa.c --- qemu-kvm-0.12.5+noroms/hw/ne2000-isa.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/ne2000-isa.c 2011-05-11 13:29:46.000000000 +0000 @@ -68,14 +68,17 @@ register_ioport_write(isa->iobase, 16, 1, ne2000_ioport_write, s); register_ioport_read(isa->iobase, 16, 1, ne2000_ioport_read, s); + isa_init_ioport_range(dev, isa->iobase, 16); register_ioport_write(isa->iobase + 0x10, 1, 1, ne2000_asic_ioport_write, s); register_ioport_read(isa->iobase + 0x10, 1, 1, ne2000_asic_ioport_read, s); register_ioport_write(isa->iobase + 0x10, 2, 2, ne2000_asic_ioport_write, s); register_ioport_read(isa->iobase + 0x10, 2, 2, ne2000_asic_ioport_read, s); + isa_init_ioport_range(dev, isa->iobase + 0x10, 2); register_ioport_write(isa->iobase + 0x1f, 1, 1, ne2000_reset_ioport_write, s); register_ioport_read(isa->iobase + 0x1f, 1, 1, ne2000_reset_ioport_read, s); + isa_init_ioport(dev, isa->iobase + 0x1f); isa_init_irq(dev, &s->irq, isa->isairq); diff -Nru qemu-kvm-0.12.5+noroms/hw/nseries.c qemu-kvm-0.14.1/hw/nseries.c --- qemu-kvm-0.12.5+noroms/hw/nseries.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/nseries.c 2011-05-11 13:29:46.000000000 +0000 @@ -1016,7 +1016,6 @@ n800_dss_init(&s->blizzard); /* CPU setup */ - s->cpu->env->regs[15] = s->cpu->env->boot_info->loader_start; s->cpu->env->GE = 0x5; /* If the machine has a slided keyboard, open it */ @@ -1317,11 +1316,6 @@ if (usb_enabled) n8x0_usb_setup(s); - /* Setup initial (reset) machine state */ - - /* Start at the OneNAND bootloader. */ - s->cpu->env->regs[15] = 0; - if (kernel_filename) { /* Or at the linux loader. */ binfo->kernel_filename = kernel_filename; @@ -1330,10 +1324,9 @@ arm_load_kernel(s->cpu->env, binfo); qemu_register_reset(n8x0_boot_init, s); - n8x0_boot_init(s); } - if (option_rom[0] && (boot_device[0] == 'n' || !kernel_filename)) { + if (option_rom[0].name && (boot_device[0] == 'n' || !kernel_filename)) { int rom_size; uint8_t nolo_tags[0x10000]; /* No, wait, better start at the ROM. */ @@ -1348,7 +1341,7 @@ * * The code above is for loading the `zImage' file from Nokia * images. */ - rom_size = load_image_targphys(option_rom[0], + rom_size = load_image_targphys(option_rom[0].name, OMAP2_Q2_BASE + 0x400000, sdram_size - 0x400000); printf("%i bytes of image loaded\n", rom_size); diff -Nru qemu-kvm-0.12.5+noroms/hw/nvram.h qemu-kvm-0.14.1/hw/nvram.h --- qemu-kvm-0.12.5+noroms/hw/nvram.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/nvram.h 2011-05-11 13:29:46.000000000 +0000 @@ -29,15 +29,14 @@ uint32_t initrd_image, uint32_t initrd_size, uint32_t NVRAM_image, int width, int height, int depth); -typedef struct m48t59_t m48t59_t; +typedef struct M48t59State M48t59State; void m48t59_write (void *private, uint32_t addr, uint32_t val); uint32_t m48t59_read (void *private, uint32_t addr); void m48t59_toggle_lock (void *private, int lock); -m48t59_t *m48t59_init_isa(uint32_t io_base, uint16_t size, int type); -m48t59_t *m48t59_init (qemu_irq IRQ, target_phys_addr_t mem_base, - uint32_t io_base, uint16_t size, - int type); +M48t59State *m48t59_init_isa(uint32_t io_base, uint16_t size, int type); +M48t59State *m48t59_init(qemu_irq IRQ, target_phys_addr_t mem_base, + uint32_t io_base, uint16_t size, int type); void m48t59_set_addr (void *opaque, uint32_t addr); #endif /* !NVRAM_H */ diff -Nru qemu-kvm-0.12.5+noroms/hw/omap1.c qemu-kvm-0.14.1/hw/omap1.c --- qemu-kvm-0.12.5+noroms/hw/omap1.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/omap1.c 2011-05-11 13:29:46.000000000 +0000 @@ -25,6 +25,8 @@ #include "soc_dma.h" /* We use pc-style serial ports. */ #include "pc.h" +#include "blockdev.h" +#include "range.h" /* Should signal the TCMI/GPMC */ uint32_t omap_badwidth_read8(void *opaque, target_phys_addr_t addr) @@ -79,578 +81,6 @@ cpu_physical_memory_write(addr, (void *) &value, 4); } -/* Interrupt Handlers */ -struct omap_intr_handler_bank_s { - uint32_t irqs; - uint32_t inputs; - uint32_t mask; - uint32_t fiq; - uint32_t sens_edge; - uint32_t swi; - unsigned char priority[32]; -}; - -struct omap_intr_handler_s { - qemu_irq *pins; - qemu_irq parent_intr[2]; - unsigned char nbanks; - int level_only; - - /* state */ - uint32_t new_agr[2]; - int sir_intr[2]; - int autoidle; - uint32_t mask; - struct omap_intr_handler_bank_s bank[]; -}; - -static void omap_inth_sir_update(struct omap_intr_handler_s *s, int is_fiq) -{ - int i, j, sir_intr, p_intr, p, f; - uint32_t level; - sir_intr = 0; - p_intr = 255; - - /* Find the interrupt line with the highest dynamic priority. - * Note: 0 denotes the hightest priority. - * If all interrupts have the same priority, the default order is IRQ_N, - * IRQ_N-1,...,IRQ_0. */ - for (j = 0; j < s->nbanks; ++j) { - level = s->bank[j].irqs & ~s->bank[j].mask & - (is_fiq ? s->bank[j].fiq : ~s->bank[j].fiq); - for (f = ffs(level), i = f - 1, level >>= f - 1; f; i += f, - level >>= f) { - p = s->bank[j].priority[i]; - if (p <= p_intr) { - p_intr = p; - sir_intr = 32 * j + i; - } - f = ffs(level >> 1); - } - } - s->sir_intr[is_fiq] = sir_intr; -} - -static inline void omap_inth_update(struct omap_intr_handler_s *s, int is_fiq) -{ - int i; - uint32_t has_intr = 0; - - for (i = 0; i < s->nbanks; ++i) - has_intr |= s->bank[i].irqs & ~s->bank[i].mask & - (is_fiq ? s->bank[i].fiq : ~s->bank[i].fiq); - - if (s->new_agr[is_fiq] & has_intr & s->mask) { - s->new_agr[is_fiq] = 0; - omap_inth_sir_update(s, is_fiq); - qemu_set_irq(s->parent_intr[is_fiq], 1); - } -} - -#define INT_FALLING_EDGE 0 -#define INT_LOW_LEVEL 1 - -static void omap_set_intr(void *opaque, int irq, int req) -{ - struct omap_intr_handler_s *ih = (struct omap_intr_handler_s *) opaque; - uint32_t rise; - - struct omap_intr_handler_bank_s *bank = &ih->bank[irq >> 5]; - int n = irq & 31; - - if (req) { - rise = ~bank->irqs & (1 << n); - if (~bank->sens_edge & (1 << n)) - rise &= ~bank->inputs; - - bank->inputs |= (1 << n); - if (rise) { - bank->irqs |= rise; - omap_inth_update(ih, 0); - omap_inth_update(ih, 1); - } - } else { - rise = bank->sens_edge & bank->irqs & (1 << n); - bank->irqs &= ~rise; - bank->inputs &= ~(1 << n); - } -} - -/* Simplified version with no edge detection */ -static void omap_set_intr_noedge(void *opaque, int irq, int req) -{ - struct omap_intr_handler_s *ih = (struct omap_intr_handler_s *) opaque; - uint32_t rise; - - struct omap_intr_handler_bank_s *bank = &ih->bank[irq >> 5]; - int n = irq & 31; - - if (req) { - rise = ~bank->inputs & (1 << n); - if (rise) { - bank->irqs |= bank->inputs |= rise; - omap_inth_update(ih, 0); - omap_inth_update(ih, 1); - } - } else - bank->irqs = (bank->inputs &= ~(1 << n)) | bank->swi; -} - -static uint32_t omap_inth_read(void *opaque, target_phys_addr_t addr) -{ - struct omap_intr_handler_s *s = (struct omap_intr_handler_s *) opaque; - int i, offset = addr; - int bank_no = offset >> 8; - int line_no; - struct omap_intr_handler_bank_s *bank = &s->bank[bank_no]; - offset &= 0xff; - - switch (offset) { - case 0x00: /* ITR */ - return bank->irqs; - - case 0x04: /* MIR */ - return bank->mask; - - case 0x10: /* SIR_IRQ_CODE */ - case 0x14: /* SIR_FIQ_CODE */ - if (bank_no != 0) - break; - line_no = s->sir_intr[(offset - 0x10) >> 2]; - bank = &s->bank[line_no >> 5]; - i = line_no & 31; - if (((bank->sens_edge >> i) & 1) == INT_FALLING_EDGE) - bank->irqs &= ~(1 << i); - return line_no; - - case 0x18: /* CONTROL_REG */ - if (bank_no != 0) - break; - return 0; - - case 0x1c: /* ILR0 */ - case 0x20: /* ILR1 */ - case 0x24: /* ILR2 */ - case 0x28: /* ILR3 */ - case 0x2c: /* ILR4 */ - case 0x30: /* ILR5 */ - case 0x34: /* ILR6 */ - case 0x38: /* ILR7 */ - case 0x3c: /* ILR8 */ - case 0x40: /* ILR9 */ - case 0x44: /* ILR10 */ - case 0x48: /* ILR11 */ - case 0x4c: /* ILR12 */ - case 0x50: /* ILR13 */ - case 0x54: /* ILR14 */ - case 0x58: /* ILR15 */ - case 0x5c: /* ILR16 */ - case 0x60: /* ILR17 */ - case 0x64: /* ILR18 */ - case 0x68: /* ILR19 */ - case 0x6c: /* ILR20 */ - case 0x70: /* ILR21 */ - case 0x74: /* ILR22 */ - case 0x78: /* ILR23 */ - case 0x7c: /* ILR24 */ - case 0x80: /* ILR25 */ - case 0x84: /* ILR26 */ - case 0x88: /* ILR27 */ - case 0x8c: /* ILR28 */ - case 0x90: /* ILR29 */ - case 0x94: /* ILR30 */ - case 0x98: /* ILR31 */ - i = (offset - 0x1c) >> 2; - return (bank->priority[i] << 2) | - (((bank->sens_edge >> i) & 1) << 1) | - ((bank->fiq >> i) & 1); - - case 0x9c: /* ISR */ - return 0x00000000; - - } - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_inth_write(void *opaque, target_phys_addr_t addr, - uint32_t value) -{ - struct omap_intr_handler_s *s = (struct omap_intr_handler_s *) opaque; - int i, offset = addr; - int bank_no = offset >> 8; - struct omap_intr_handler_bank_s *bank = &s->bank[bank_no]; - offset &= 0xff; - - switch (offset) { - case 0x00: /* ITR */ - /* Important: ignore the clearing if the IRQ is level-triggered and - the input bit is 1 */ - bank->irqs &= value | (bank->inputs & bank->sens_edge); - return; - - case 0x04: /* MIR */ - bank->mask = value; - omap_inth_update(s, 0); - omap_inth_update(s, 1); - return; - - case 0x10: /* SIR_IRQ_CODE */ - case 0x14: /* SIR_FIQ_CODE */ - OMAP_RO_REG(addr); - break; - - case 0x18: /* CONTROL_REG */ - if (bank_no != 0) - break; - if (value & 2) { - qemu_set_irq(s->parent_intr[1], 0); - s->new_agr[1] = ~0; - omap_inth_update(s, 1); - } - if (value & 1) { - qemu_set_irq(s->parent_intr[0], 0); - s->new_agr[0] = ~0; - omap_inth_update(s, 0); - } - return; - - case 0x1c: /* ILR0 */ - case 0x20: /* ILR1 */ - case 0x24: /* ILR2 */ - case 0x28: /* ILR3 */ - case 0x2c: /* ILR4 */ - case 0x30: /* ILR5 */ - case 0x34: /* ILR6 */ - case 0x38: /* ILR7 */ - case 0x3c: /* ILR8 */ - case 0x40: /* ILR9 */ - case 0x44: /* ILR10 */ - case 0x48: /* ILR11 */ - case 0x4c: /* ILR12 */ - case 0x50: /* ILR13 */ - case 0x54: /* ILR14 */ - case 0x58: /* ILR15 */ - case 0x5c: /* ILR16 */ - case 0x60: /* ILR17 */ - case 0x64: /* ILR18 */ - case 0x68: /* ILR19 */ - case 0x6c: /* ILR20 */ - case 0x70: /* ILR21 */ - case 0x74: /* ILR22 */ - case 0x78: /* ILR23 */ - case 0x7c: /* ILR24 */ - case 0x80: /* ILR25 */ - case 0x84: /* ILR26 */ - case 0x88: /* ILR27 */ - case 0x8c: /* ILR28 */ - case 0x90: /* ILR29 */ - case 0x94: /* ILR30 */ - case 0x98: /* ILR31 */ - i = (offset - 0x1c) >> 2; - bank->priority[i] = (value >> 2) & 0x1f; - bank->sens_edge &= ~(1 << i); - bank->sens_edge |= ((value >> 1) & 1) << i; - bank->fiq &= ~(1 << i); - bank->fiq |= (value & 1) << i; - return; - - case 0x9c: /* ISR */ - for (i = 0; i < 32; i ++) - if (value & (1 << i)) { - omap_set_intr(s, 32 * bank_no + i, 1); - return; - } - return; - } - OMAP_BAD_REG(addr); -} - -static CPUReadMemoryFunc * const omap_inth_readfn[] = { - omap_badwidth_read32, - omap_badwidth_read32, - omap_inth_read, -}; - -static CPUWriteMemoryFunc * const omap_inth_writefn[] = { - omap_inth_write, - omap_inth_write, - omap_inth_write, -}; - -void omap_inth_reset(struct omap_intr_handler_s *s) -{ - int i; - - for (i = 0; i < s->nbanks; ++i){ - s->bank[i].irqs = 0x00000000; - s->bank[i].mask = 0xffffffff; - s->bank[i].sens_edge = 0x00000000; - s->bank[i].fiq = 0x00000000; - s->bank[i].inputs = 0x00000000; - s->bank[i].swi = 0x00000000; - memset(s->bank[i].priority, 0, sizeof(s->bank[i].priority)); - - if (s->level_only) - s->bank[i].sens_edge = 0xffffffff; - } - - s->new_agr[0] = ~0; - s->new_agr[1] = ~0; - s->sir_intr[0] = 0; - s->sir_intr[1] = 0; - s->autoidle = 0; - s->mask = ~0; - - qemu_set_irq(s->parent_intr[0], 0); - qemu_set_irq(s->parent_intr[1], 0); -} - -struct omap_intr_handler_s *omap_inth_init(target_phys_addr_t base, - unsigned long size, unsigned char nbanks, qemu_irq **pins, - qemu_irq parent_irq, qemu_irq parent_fiq, omap_clk clk) -{ - int iomemtype; - struct omap_intr_handler_s *s = (struct omap_intr_handler_s *) - qemu_mallocz(sizeof(struct omap_intr_handler_s) + - sizeof(struct omap_intr_handler_bank_s) * nbanks); - - s->parent_intr[0] = parent_irq; - s->parent_intr[1] = parent_fiq; - s->nbanks = nbanks; - s->pins = qemu_allocate_irqs(omap_set_intr, s, nbanks * 32); - if (pins) - *pins = s->pins; - - omap_inth_reset(s); - - iomemtype = cpu_register_io_memory(omap_inth_readfn, - omap_inth_writefn, s); - cpu_register_physical_memory(base, size, iomemtype); - - return s; -} - -static uint32_t omap2_inth_read(void *opaque, target_phys_addr_t addr) -{ - struct omap_intr_handler_s *s = (struct omap_intr_handler_s *) opaque; - int offset = addr; - int bank_no, line_no; - struct omap_intr_handler_bank_s *bank = NULL; - - if ((offset & 0xf80) == 0x80) { - bank_no = (offset & 0x60) >> 5; - if (bank_no < s->nbanks) { - offset &= ~0x60; - bank = &s->bank[bank_no]; - } - } - - switch (offset) { - case 0x00: /* INTC_REVISION */ - return 0x21; - - case 0x10: /* INTC_SYSCONFIG */ - return (s->autoidle >> 2) & 1; - - case 0x14: /* INTC_SYSSTATUS */ - return 1; /* RESETDONE */ - - case 0x40: /* INTC_SIR_IRQ */ - return s->sir_intr[0]; - - case 0x44: /* INTC_SIR_FIQ */ - return s->sir_intr[1]; - - case 0x48: /* INTC_CONTROL */ - return (!s->mask) << 2; /* GLOBALMASK */ - - case 0x4c: /* INTC_PROTECTION */ - return 0; - - case 0x50: /* INTC_IDLE */ - return s->autoidle & 3; - - /* Per-bank registers */ - case 0x80: /* INTC_ITR */ - return bank->inputs; - - case 0x84: /* INTC_MIR */ - return bank->mask; - - case 0x88: /* INTC_MIR_CLEAR */ - case 0x8c: /* INTC_MIR_SET */ - return 0; - - case 0x90: /* INTC_ISR_SET */ - return bank->swi; - - case 0x94: /* INTC_ISR_CLEAR */ - return 0; - - case 0x98: /* INTC_PENDING_IRQ */ - return bank->irqs & ~bank->mask & ~bank->fiq; - - case 0x9c: /* INTC_PENDING_FIQ */ - return bank->irqs & ~bank->mask & bank->fiq; - - /* Per-line registers */ - case 0x100 ... 0x300: /* INTC_ILR */ - bank_no = (offset - 0x100) >> 7; - if (bank_no > s->nbanks) - break; - bank = &s->bank[bank_no]; - line_no = (offset & 0x7f) >> 2; - return (bank->priority[line_no] << 2) | - ((bank->fiq >> line_no) & 1); - } - OMAP_BAD_REG(addr); - return 0; -} - -static void omap2_inth_write(void *opaque, target_phys_addr_t addr, - uint32_t value) -{ - struct omap_intr_handler_s *s = (struct omap_intr_handler_s *) opaque; - int offset = addr; - int bank_no, line_no; - struct omap_intr_handler_bank_s *bank = NULL; - - if ((offset & 0xf80) == 0x80) { - bank_no = (offset & 0x60) >> 5; - if (bank_no < s->nbanks) { - offset &= ~0x60; - bank = &s->bank[bank_no]; - } - } - - switch (offset) { - case 0x10: /* INTC_SYSCONFIG */ - s->autoidle &= 4; - s->autoidle |= (value & 1) << 2; - if (value & 2) /* SOFTRESET */ - omap_inth_reset(s); - return; - - case 0x48: /* INTC_CONTROL */ - s->mask = (value & 4) ? 0 : ~0; /* GLOBALMASK */ - if (value & 2) { /* NEWFIQAGR */ - qemu_set_irq(s->parent_intr[1], 0); - s->new_agr[1] = ~0; - omap_inth_update(s, 1); - } - if (value & 1) { /* NEWIRQAGR */ - qemu_set_irq(s->parent_intr[0], 0); - s->new_agr[0] = ~0; - omap_inth_update(s, 0); - } - return; - - case 0x4c: /* INTC_PROTECTION */ - /* TODO: Make a bitmap (or sizeof(char)map) of access privileges - * for every register, see Chapter 3 and 4 for privileged mode. */ - if (value & 1) - fprintf(stderr, "%s: protection mode enable attempt\n", - __FUNCTION__); - return; - - case 0x50: /* INTC_IDLE */ - s->autoidle &= ~3; - s->autoidle |= value & 3; - return; - - /* Per-bank registers */ - case 0x84: /* INTC_MIR */ - bank->mask = value; - omap_inth_update(s, 0); - omap_inth_update(s, 1); - return; - - case 0x88: /* INTC_MIR_CLEAR */ - bank->mask &= ~value; - omap_inth_update(s, 0); - omap_inth_update(s, 1); - return; - - case 0x8c: /* INTC_MIR_SET */ - bank->mask |= value; - return; - - case 0x90: /* INTC_ISR_SET */ - bank->irqs |= bank->swi |= value; - omap_inth_update(s, 0); - omap_inth_update(s, 1); - return; - - case 0x94: /* INTC_ISR_CLEAR */ - bank->swi &= ~value; - bank->irqs = bank->swi & bank->inputs; - return; - - /* Per-line registers */ - case 0x100 ... 0x300: /* INTC_ILR */ - bank_no = (offset - 0x100) >> 7; - if (bank_no > s->nbanks) - break; - bank = &s->bank[bank_no]; - line_no = (offset & 0x7f) >> 2; - bank->priority[line_no] = (value >> 2) & 0x3f; - bank->fiq &= ~(1 << line_no); - bank->fiq |= (value & 1) << line_no; - return; - - case 0x00: /* INTC_REVISION */ - case 0x14: /* INTC_SYSSTATUS */ - case 0x40: /* INTC_SIR_IRQ */ - case 0x44: /* INTC_SIR_FIQ */ - case 0x80: /* INTC_ITR */ - case 0x98: /* INTC_PENDING_IRQ */ - case 0x9c: /* INTC_PENDING_FIQ */ - OMAP_RO_REG(addr); - return; - } - OMAP_BAD_REG(addr); -} - -static CPUReadMemoryFunc * const omap2_inth_readfn[] = { - omap_badwidth_read32, - omap_badwidth_read32, - omap2_inth_read, -}; - -static CPUWriteMemoryFunc * const omap2_inth_writefn[] = { - omap2_inth_write, - omap2_inth_write, - omap2_inth_write, -}; - -struct omap_intr_handler_s *omap2_inth_init(target_phys_addr_t base, - int size, int nbanks, qemu_irq **pins, - qemu_irq parent_irq, qemu_irq parent_fiq, - omap_clk fclk, omap_clk iclk) -{ - int iomemtype; - struct omap_intr_handler_s *s = (struct omap_intr_handler_s *) - qemu_mallocz(sizeof(struct omap_intr_handler_s) + - sizeof(struct omap_intr_handler_bank_s) * nbanks); - - s->parent_intr[0] = parent_irq; - s->parent_intr[1] = parent_fiq; - s->nbanks = nbanks; - s->level_only = 1; - s->pins = qemu_allocate_irqs(omap_set_intr_noedge, s, nbanks * 32); - if (pins) - *pins = s->pins; - - omap_inth_reset(s); - - iomemtype = cpu_register_io_memory(omap2_inth_readfn, - omap2_inth_writefn, s); - cpu_register_physical_memory(base, size, iomemtype); - - return s; -} - /* MPU OS timers */ struct omap_mpu_timer_s { qemu_irq irq; @@ -819,7 +249,7 @@ s->it_ena = 1; } -struct omap_mpu_timer_s *omap_mpu_timer_init(target_phys_addr_t base, +static struct omap_mpu_timer_s *omap_mpu_timer_init(target_phys_addr_t base, qemu_irq irq, omap_clk clk) { int iomemtype; @@ -834,7 +264,7 @@ omap_timer_clk_setup(s); iomemtype = cpu_register_io_memory(omap_mpu_timer_readfn, - omap_mpu_timer_writefn, s); + omap_mpu_timer_writefn, s, DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base, 0x100, iomemtype); return s; @@ -943,7 +373,7 @@ omap_timer_update(&s->timer); } -struct omap_watchdog_timer_s *omap_wd_timer_init(target_phys_addr_t base, +static struct omap_watchdog_timer_s *omap_wd_timer_init(target_phys_addr_t base, qemu_irq irq, omap_clk clk) { int iomemtype; @@ -957,7 +387,7 @@ omap_timer_clk_setup(&s->timer); iomemtype = cpu_register_io_memory(omap_wd_timer_readfn, - omap_wd_timer_writefn, s); + omap_wd_timer_writefn, s, DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base, 0x100, iomemtype); return s; @@ -1045,7 +475,7 @@ s->timer.ar = 1; } -struct omap_32khz_timer_s *omap_os_timer_init(target_phys_addr_t base, +static struct omap_32khz_timer_s *omap_os_timer_init(target_phys_addr_t base, qemu_irq irq, omap_clk clk) { int iomemtype; @@ -1059,7 +489,7 @@ omap_timer_clk_setup(&s->timer); iomemtype = cpu_register_io_memory(omap_os_timer_readfn, - omap_os_timer_writefn, s); + omap_os_timer_writefn, s, DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base, 0x800, iomemtype); return s; @@ -1286,7 +716,7 @@ struct omap_mpu_state_s *mpu) { int iomemtype = cpu_register_io_memory(omap_ulpd_pm_readfn, - omap_ulpd_pm_writefn, mpu); + omap_ulpd_pm_writefn, mpu, DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base, 0x800, iomemtype); omap_ulpd_pm_reset(mpu); @@ -1501,7 +931,7 @@ struct omap_mpu_state_s *mpu) { int iomemtype = cpu_register_io_memory(omap_pin_cfg_readfn, - omap_pin_cfg_writefn, mpu); + omap_pin_cfg_writefn, mpu, DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base, 0x800, iomemtype); omap_pin_cfg_reset(mpu); @@ -1571,7 +1001,7 @@ static void omap_id_init(struct omap_mpu_state_s *mpu) { int iomemtype = cpu_register_io_memory(omap_id_readfn, - omap_id_writefn, mpu); + omap_id_writefn, mpu, DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory_offset(0xfffe1800, 0x800, iomemtype, 0xfffe1800); cpu_register_physical_memory_offset(0xfffed400, 0x100, iomemtype, 0xfffed400); if (!cpu_is_omap15xx(mpu)) @@ -1654,7 +1084,7 @@ struct omap_mpu_state_s *mpu) { int iomemtype = cpu_register_io_memory(omap_mpui_readfn, - omap_mpui_writefn, mpu); + omap_mpui_writefn, mpu, DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base, 0x100, iomemtype); @@ -1752,7 +1182,7 @@ s->enh_control = 0x000f; } -struct omap_tipb_bridge_s *omap_tipb_bridge_init(target_phys_addr_t base, +static struct omap_tipb_bridge_s *omap_tipb_bridge_init(target_phys_addr_t base, qemu_irq abort_irq, omap_clk clk) { int iomemtype; @@ -1763,7 +1193,7 @@ omap_tipb_bridge_reset(s); iomemtype = cpu_register_io_memory(omap_tipb_bridge_readfn, - omap_tipb_bridge_writefn, s); + omap_tipb_bridge_writefn, s, DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base, 0x100, iomemtype); return s; @@ -1869,7 +1299,7 @@ struct omap_mpu_state_s *mpu) { int iomemtype = cpu_register_io_memory(omap_tcmi_readfn, - omap_tcmi_writefn, mpu); + omap_tcmi_writefn, mpu, DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base, 0x100, iomemtype); omap_tcmi_reset(mpu); @@ -1942,7 +1372,7 @@ omap_clk clk) { int iomemtype = cpu_register_io_memory(omap_dpll_readfn, - omap_dpll_writefn, s); + omap_dpll_writefn, s, DEVICE_NATIVE_ENDIAN); s->dpll = clk; omap_dpll_reset(s); @@ -1950,162 +1380,6 @@ cpu_register_physical_memory(base, 0x100, iomemtype); } -/* UARTs */ -struct omap_uart_s { - target_phys_addr_t base; - SerialState *serial; /* TODO */ - struct omap_target_agent_s *ta; - omap_clk fclk; - qemu_irq irq; - - uint8_t eblr; - uint8_t syscontrol; - uint8_t wkup; - uint8_t cfps; - uint8_t mdr[2]; - uint8_t scr; - uint8_t clksel; -}; - -void omap_uart_reset(struct omap_uart_s *s) -{ - s->eblr = 0x00; - s->syscontrol = 0; - s->wkup = 0x3f; - s->cfps = 0x69; - s->clksel = 0; -} - -struct omap_uart_s *omap_uart_init(target_phys_addr_t base, - qemu_irq irq, omap_clk fclk, omap_clk iclk, - qemu_irq txdma, qemu_irq rxdma, CharDriverState *chr) -{ - struct omap_uart_s *s = (struct omap_uart_s *) - qemu_mallocz(sizeof(struct omap_uart_s)); - - s->base = base; - s->fclk = fclk; - s->irq = irq; - s->serial = serial_mm_init(base, 2, irq, omap_clk_getrate(fclk)/16, - chr ?: qemu_chr_open("null", "null", NULL), 1); - - return s; -} - -static uint32_t omap_uart_read(void *opaque, target_phys_addr_t addr) -{ - struct omap_uart_s *s = (struct omap_uart_s *) opaque; - - addr &= 0xff; - switch (addr) { - case 0x20: /* MDR1 */ - return s->mdr[0]; - case 0x24: /* MDR2 */ - return s->mdr[1]; - case 0x40: /* SCR */ - return s->scr; - case 0x44: /* SSR */ - return 0x0; - case 0x48: /* EBLR (OMAP2) */ - return s->eblr; - case 0x4C: /* OSC_12M_SEL (OMAP1) */ - return s->clksel; - case 0x50: /* MVR */ - return 0x30; - case 0x54: /* SYSC (OMAP2) */ - return s->syscontrol; - case 0x58: /* SYSS (OMAP2) */ - return 1; - case 0x5c: /* WER (OMAP2) */ - return s->wkup; - case 0x60: /* CFPS (OMAP2) */ - return s->cfps; - } - - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_uart_write(void *opaque, target_phys_addr_t addr, - uint32_t value) -{ - struct omap_uart_s *s = (struct omap_uart_s *) opaque; - - addr &= 0xff; - switch (addr) { - case 0x20: /* MDR1 */ - s->mdr[0] = value & 0x7f; - break; - case 0x24: /* MDR2 */ - s->mdr[1] = value & 0xff; - break; - case 0x40: /* SCR */ - s->scr = value & 0xff; - break; - case 0x48: /* EBLR (OMAP2) */ - s->eblr = value & 0xff; - break; - case 0x4C: /* OSC_12M_SEL (OMAP1) */ - s->clksel = value & 1; - break; - case 0x44: /* SSR */ - case 0x50: /* MVR */ - case 0x58: /* SYSS (OMAP2) */ - OMAP_RO_REG(addr); - break; - case 0x54: /* SYSC (OMAP2) */ - s->syscontrol = value & 0x1d; - if (value & 2) - omap_uart_reset(s); - break; - case 0x5c: /* WER (OMAP2) */ - s->wkup = value & 0x7f; - break; - case 0x60: /* CFPS (OMAP2) */ - s->cfps = value & 0xff; - break; - default: - OMAP_BAD_REG(addr); - } -} - -static CPUReadMemoryFunc * const omap_uart_readfn[] = { - omap_uart_read, - omap_uart_read, - omap_badwidth_read8, -}; - -static CPUWriteMemoryFunc * const omap_uart_writefn[] = { - omap_uart_write, - omap_uart_write, - omap_badwidth_write8, -}; - -struct omap_uart_s *omap2_uart_init(struct omap_target_agent_s *ta, - qemu_irq irq, omap_clk fclk, omap_clk iclk, - qemu_irq txdma, qemu_irq rxdma, CharDriverState *chr) -{ - target_phys_addr_t base = omap_l4_attach(ta, 0, 0); - struct omap_uart_s *s = omap_uart_init(base, irq, - fclk, iclk, txdma, rxdma, chr); - int iomemtype = cpu_register_io_memory(omap_uart_readfn, - omap_uart_writefn, s); - - s->ta = ta; - - cpu_register_physical_memory(base + 0x20, 0x100, iomemtype); - - return s; -} - -void omap_uart_attach(struct omap_uart_s *s, CharDriverState *chr) -{ - /* TODO: Should reuse or destroy current s->serial */ - s->serial = serial_mm_init(s->base, 2, s->irq, - omap_clk_getrate(s->fclk) / 16, - chr ?: qemu_chr_open("null", "null", NULL), 1); -} - /* MPU Clock/Reset/Power Mode Control */ static uint32_t omap_clkm_read(void *opaque, target_phys_addr_t addr) { @@ -2334,7 +1608,6 @@ return; case 0x0c: /* ARM_EWUPCT */ - diff = s->clkm.arm_ewupct ^ value; s->clkm.arm_ewupct = value & 0x003f; return; @@ -2503,8 +1776,10 @@ target_phys_addr_t dsp_base, struct omap_mpu_state_s *s) { int iomemtype[2] = { - cpu_register_io_memory(omap_clkm_readfn, omap_clkm_writefn, s), - cpu_register_io_memory(omap_clkdsp_readfn, omap_clkdsp_writefn, s), + cpu_register_io_memory(omap_clkm_readfn, omap_clkm_writefn, s, + DEVICE_NATIVE_ENDIAN), + cpu_register_io_memory(omap_clkdsp_readfn, omap_clkdsp_writefn, s, + DEVICE_NATIVE_ENDIAN), }; s->clkm.arm_idlect1 = 0x03ff; @@ -2758,7 +2033,7 @@ omap_mpuio_reset(s); iomemtype = cpu_register_io_memory(omap_mpuio_readfn, - omap_mpuio_writefn, s); + omap_mpuio_writefn, s, DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base, 0x800, iomemtype); omap_clk_adduser(clk, qemu_allocate_irqs(omap_mpuio_onoff, s, 1)[0]); @@ -2791,187 +2066,6 @@ omap_mpuio_kbd_update(s); } -/* General-Purpose I/O */ -struct omap_gpio_s { - qemu_irq irq; - qemu_irq *in; - qemu_irq handler[16]; - - uint16_t inputs; - uint16_t outputs; - uint16_t dir; - uint16_t edge; - uint16_t mask; - uint16_t ints; - uint16_t pins; -}; - -static void omap_gpio_set(void *opaque, int line, int level) -{ - struct omap_gpio_s *s = (struct omap_gpio_s *) opaque; - uint16_t prev = s->inputs; - - if (level) - s->inputs |= 1 << line; - else - s->inputs &= ~(1 << line); - - if (((s->edge & s->inputs & ~prev) | (~s->edge & ~s->inputs & prev)) & - (1 << line) & s->dir & ~s->mask) { - s->ints |= 1 << line; - qemu_irq_raise(s->irq); - } -} - -static uint32_t omap_gpio_read(void *opaque, target_phys_addr_t addr) -{ - struct omap_gpio_s *s = (struct omap_gpio_s *) opaque; - int offset = addr & OMAP_MPUI_REG_MASK; - - switch (offset) { - case 0x00: /* DATA_INPUT */ - return s->inputs & s->pins; - - case 0x04: /* DATA_OUTPUT */ - return s->outputs; - - case 0x08: /* DIRECTION_CONTROL */ - return s->dir; - - case 0x0c: /* INTERRUPT_CONTROL */ - return s->edge; - - case 0x10: /* INTERRUPT_MASK */ - return s->mask; - - case 0x14: /* INTERRUPT_STATUS */ - return s->ints; - - case 0x18: /* PIN_CONTROL (not in OMAP310) */ - OMAP_BAD_REG(addr); - return s->pins; - } - - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_gpio_write(void *opaque, target_phys_addr_t addr, - uint32_t value) -{ - struct omap_gpio_s *s = (struct omap_gpio_s *) opaque; - int offset = addr & OMAP_MPUI_REG_MASK; - uint16_t diff; - int ln; - - switch (offset) { - case 0x00: /* DATA_INPUT */ - OMAP_RO_REG(addr); - return; - - case 0x04: /* DATA_OUTPUT */ - diff = (s->outputs ^ value) & ~s->dir; - s->outputs = value; - while ((ln = ffs(diff))) { - ln --; - if (s->handler[ln]) - qemu_set_irq(s->handler[ln], (value >> ln) & 1); - diff &= ~(1 << ln); - } - break; - - case 0x08: /* DIRECTION_CONTROL */ - diff = s->outputs & (s->dir ^ value); - s->dir = value; - - value = s->outputs & ~s->dir; - while ((ln = ffs(diff))) { - ln --; - if (s->handler[ln]) - qemu_set_irq(s->handler[ln], (value >> ln) & 1); - diff &= ~(1 << ln); - } - break; - - case 0x0c: /* INTERRUPT_CONTROL */ - s->edge = value; - break; - - case 0x10: /* INTERRUPT_MASK */ - s->mask = value; - break; - - case 0x14: /* INTERRUPT_STATUS */ - s->ints &= ~value; - if (!s->ints) - qemu_irq_lower(s->irq); - break; - - case 0x18: /* PIN_CONTROL (not in OMAP310 TRM) */ - OMAP_BAD_REG(addr); - s->pins = value; - break; - - default: - OMAP_BAD_REG(addr); - return; - } -} - -/* *Some* sources say the memory region is 32-bit. */ -static CPUReadMemoryFunc * const omap_gpio_readfn[] = { - omap_badwidth_read16, - omap_gpio_read, - omap_badwidth_read16, -}; - -static CPUWriteMemoryFunc * const omap_gpio_writefn[] = { - omap_badwidth_write16, - omap_gpio_write, - omap_badwidth_write16, -}; - -static void omap_gpio_reset(struct omap_gpio_s *s) -{ - s->inputs = 0; - s->outputs = ~0; - s->dir = ~0; - s->edge = ~0; - s->mask = ~0; - s->ints = 0; - s->pins = ~0; -} - -struct omap_gpio_s *omap_gpio_init(target_phys_addr_t base, - qemu_irq irq, omap_clk clk) -{ - int iomemtype; - struct omap_gpio_s *s = (struct omap_gpio_s *) - qemu_mallocz(sizeof(struct omap_gpio_s)); - - s->irq = irq; - s->in = qemu_allocate_irqs(omap_gpio_set, s, 16); - omap_gpio_reset(s); - - iomemtype = cpu_register_io_memory(omap_gpio_readfn, - omap_gpio_writefn, s); - cpu_register_physical_memory(base, 0x1000, iomemtype); - - return s; -} - -qemu_irq *omap_gpio_in_get(struct omap_gpio_s *s) -{ - return s->in; -} - -void omap_gpio_out_set(struct omap_gpio_s *s, int line, qemu_irq handler) -{ - if (line >= 16 || line < 0) - hw_error("%s: No GPIO line %i\n", __FUNCTION__, line); - s->handler[line] = handler; -} - /* MicroWire Interface */ struct omap_uwire_s { qemu_irq txirq; @@ -3124,7 +2218,7 @@ omap_uwire_reset(s); iomemtype = cpu_register_io_memory(omap_uwire_readfn, - omap_uwire_writefn, s); + omap_uwire_writefn, s, DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base, 0x800, iomemtype); return s; @@ -3225,7 +2319,7 @@ omap_pwl_reset(s); iomemtype = cpu_register_io_memory(omap_pwl_readfn, - omap_pwl_writefn, s); + omap_pwl_writefn, s, DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base, 0x800, iomemtype); omap_clk_adduser(clk, qemu_allocate_irqs(omap_pwl_clk_update, s, 1)[0]); @@ -3320,7 +2414,7 @@ omap_pwt_reset(s); iomemtype = cpu_register_io_memory(omap_pwt_readfn, - omap_pwt_writefn, s); + omap_pwt_writefn, s, DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base, 0x800, iomemtype); } @@ -3719,7 +2813,7 @@ omap_rtc_tick(s); } -struct omap_rtc_s *omap_rtc_init(target_phys_addr_t base, +static struct omap_rtc_s *omap_rtc_init(target_phys_addr_t base, qemu_irq *irq, omap_clk clk) { int iomemtype; @@ -3733,7 +2827,7 @@ omap_rtc_reset(s); iomemtype = cpu_register_io_memory(omap_rtc_readfn, - omap_rtc_writefn, s); + omap_rtc_writefn, s, DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base, 0x800, iomemtype); return s; @@ -4255,7 +3349,7 @@ omap_mcbsp_reset(s); iomemtype = cpu_register_io_memory(omap_mcbsp_readfn, - omap_mcbsp_writefn, s); + omap_mcbsp_writefn, s, DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base, 0x800, iomemtype); return s; @@ -4416,7 +3510,7 @@ omap_lpg_update(s); } -struct omap_lpg_s *omap_lpg_init(target_phys_addr_t base, omap_clk clk) +static struct omap_lpg_s *omap_lpg_init(target_phys_addr_t base, omap_clk clk) { int iomemtype; struct omap_lpg_s *s = (struct omap_lpg_s *) @@ -4427,7 +3521,7 @@ omap_lpg_reset(s); iomemtype = cpu_register_io_memory(omap_lpg_readfn, - omap_lpg_writefn, s); + omap_lpg_writefn, s, DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base, 0x800, iomemtype); omap_clk_adduser(clk, qemu_allocate_irqs(omap_lpg_clk_update, s, 1)[0]); @@ -4460,7 +3554,7 @@ static void omap_setup_mpui_io(struct omap_mpu_state_s *mpu) { int iomemtype = cpu_register_io_memory(omap_mpui_io_readfn, - omap_mpui_io_writefn, mpu); + omap_mpui_io_writefn, mpu, DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(OMAP_MPUI_BASE, 0x7fff, iomemtype); } @@ -4578,37 +3672,38 @@ static int omap_validate_emiff_addr(struct omap_mpu_state_s *s, target_phys_addr_t addr) { - return addr >= OMAP_EMIFF_BASE && addr < OMAP_EMIFF_BASE + s->sdram_size; + return range_covers_byte(OMAP_EMIFF_BASE, s->sdram_size, addr); } static int omap_validate_emifs_addr(struct omap_mpu_state_s *s, target_phys_addr_t addr) { - return addr >= OMAP_EMIFS_BASE && addr < OMAP_EMIFF_BASE; + return range_covers_byte(OMAP_EMIFS_BASE, OMAP_EMIFF_BASE - OMAP_EMIFS_BASE, + addr); } static int omap_validate_imif_addr(struct omap_mpu_state_s *s, target_phys_addr_t addr) { - return addr >= OMAP_IMIF_BASE && addr < OMAP_IMIF_BASE + s->sram_size; + return range_covers_byte(OMAP_IMIF_BASE, s->sram_size, addr); } static int omap_validate_tipb_addr(struct omap_mpu_state_s *s, target_phys_addr_t addr) { - return addr >= 0xfffb0000 && addr < 0xffff0000; + return range_covers_byte(0xfffb0000, 0xffff0000 - 0xfffb0000, addr); } static int omap_validate_local_addr(struct omap_mpu_state_s *s, target_phys_addr_t addr) { - return addr >= OMAP_LOCALBUS_BASE && addr < OMAP_LOCALBUS_BASE + 0x1000000; + return range_covers_byte(OMAP_LOCALBUS_BASE, 0x1000000, addr); } static int omap_validate_tipb_mpui_addr(struct omap_mpu_state_s *s, target_phys_addr_t addr) { - return addr >= 0xe1010000 && addr < 0xe1020004; + return range_covers_byte(0xe1010000, 0xe1020004 - 0xe1010000, addr); } struct omap_mpu_state_s *omap310_mpu_init(unsigned long sdram_size, @@ -4642,9 +3737,11 @@ /* Memory-mapped stuff */ cpu_register_physical_memory(OMAP_EMIFF_BASE, s->sdram_size, - (emiff_base = qemu_ram_alloc(s->sdram_size)) | IO_MEM_RAM); + (emiff_base = qemu_ram_alloc(NULL, "omap1.dram", + s->sdram_size)) | IO_MEM_RAM); cpu_register_physical_memory(OMAP_IMIF_BASE, s->sram_size, - (imif_base = qemu_ram_alloc(s->sram_size)) | IO_MEM_RAM); + (imif_base = qemu_ram_alloc(NULL, "omap1.sram", + s->sram_size)) | IO_MEM_RAM); omap_clkm_init(0xfffece00, 0xe1008000, s); @@ -4653,8 +3750,8 @@ cpu_irq[ARM_PIC_CPU_IRQ], cpu_irq[ARM_PIC_CPU_FIQ], omap_findclk(s, "arminth_ck")); s->ih[1] = omap_inth_init(0xfffe0000, 0x800, 1, &s->irq[1], - s->ih[0]->pins[OMAP_INT_15XX_IH2_IRQ], NULL, - omap_findclk(s, "arminth_ck")); + omap_inth_get_pin(s->ih[0], OMAP_INT_15XX_IH2_IRQ), + NULL, omap_findclk(s, "arminth_ck")); for (i = 0; i < 6; i ++) dma_irqs[i] = @@ -4716,16 +3813,19 @@ omap_findclk(s, "uart1_ck"), omap_findclk(s, "uart1_ck"), s->drq[OMAP_DMA_UART1_TX], s->drq[OMAP_DMA_UART1_RX], + "uart1", serial_hds[0]); s->uart[1] = omap_uart_init(0xfffb0800, s->irq[1][OMAP_INT_UART2], omap_findclk(s, "uart2_ck"), omap_findclk(s, "uart2_ck"), s->drq[OMAP_DMA_UART2_TX], s->drq[OMAP_DMA_UART2_RX], + "uart2", serial_hds[0] ? serial_hds[1] : NULL); s->uart[2] = omap_uart_init(0xfffb9800, s->irq[0][OMAP_INT_UART3], omap_findclk(s, "uart3_ck"), omap_findclk(s, "uart3_ck"), s->drq[OMAP_DMA_UART3_TX], s->drq[OMAP_DMA_UART3_RX], + "uart3", serial_hds[0] && serial_hds[1] ? serial_hds[2] : NULL); omap_dpll_init(&s->dpll[0], 0xfffecf00, omap_findclk(s, "dpll1")); diff -Nru qemu-kvm-0.12.5+noroms/hw/omap2.c qemu-kvm-0.14.1/hw/omap2.c --- qemu-kvm-0.12.5+noroms/hw/omap2.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/omap2.c 2011-05-11 13:29:46.000000000 +0000 @@ -17,1391 +17,17 @@ * You should have received a copy of the GNU General Public License along * with this program; if not, see . */ -#include "hw.h" -#include "arm-misc.h" -#include "omap.h" -#include "sysemu.h" -#include "qemu-timer.h" -#include "qemu-char.h" -#include "flash.h" -#include "soc_dma.h" -#include "audio/audio.h" - -/* GP timers */ -struct omap_gp_timer_s { - qemu_irq irq; - qemu_irq wkup; - qemu_irq in; - qemu_irq out; - omap_clk clk; - QEMUTimer *timer; - QEMUTimer *match; - struct omap_target_agent_s *ta; - - int in_val; - int out_val; - int64_t time; - int64_t rate; - int64_t ticks_per_sec; - - int16_t config; - int status; - int it_ena; - int wu_ena; - int enable; - int inout; - int capt2; - int pt; - enum { - gpt_trigger_none, gpt_trigger_overflow, gpt_trigger_both - } trigger; - enum { - gpt_capture_none, gpt_capture_rising, - gpt_capture_falling, gpt_capture_both - } capture; - int scpwm; - int ce; - int pre; - int ptv; - int ar; - int st; - int posted; - uint32_t val; - uint32_t load_val; - uint32_t capture_val[2]; - uint32_t match_val; - int capt_num; - - uint16_t writeh; /* LSB */ - uint16_t readh; /* MSB */ -}; - -#define GPT_TCAR_IT (1 << 2) -#define GPT_OVF_IT (1 << 1) -#define GPT_MAT_IT (1 << 0) - -static inline void omap_gp_timer_intr(struct omap_gp_timer_s *timer, int it) -{ - if (timer->it_ena & it) { - if (!timer->status) - qemu_irq_raise(timer->irq); - - timer->status |= it; - /* Or are the status bits set even when masked? - * i.e. is masking applied before or after the status register? */ - } - - if (timer->wu_ena & it) - qemu_irq_pulse(timer->wkup); -} - -static inline void omap_gp_timer_out(struct omap_gp_timer_s *timer, int level) -{ - if (!timer->inout && timer->out_val != level) { - timer->out_val = level; - qemu_set_irq(timer->out, level); - } -} - -static inline uint32_t omap_gp_timer_read(struct omap_gp_timer_s *timer) -{ - uint64_t distance; - - if (timer->st && timer->rate) { - distance = qemu_get_clock(vm_clock) - timer->time; - distance = muldiv64(distance, timer->rate, timer->ticks_per_sec); - - if (distance >= 0xffffffff - timer->val) - return 0xffffffff; - else - return timer->val + distance; - } else - return timer->val; -} - -static inline void omap_gp_timer_sync(struct omap_gp_timer_s *timer) -{ - if (timer->st) { - timer->val = omap_gp_timer_read(timer); - timer->time = qemu_get_clock(vm_clock); - } -} - -static inline void omap_gp_timer_update(struct omap_gp_timer_s *timer) -{ - int64_t expires, matches; - - if (timer->st && timer->rate) { - expires = muldiv64(0x100000000ll - timer->val, - timer->ticks_per_sec, timer->rate); - qemu_mod_timer(timer->timer, timer->time + expires); - - if (timer->ce && timer->match_val >= timer->val) { - matches = muldiv64(timer->match_val - timer->val, - timer->ticks_per_sec, timer->rate); - qemu_mod_timer(timer->match, timer->time + matches); - } else - qemu_del_timer(timer->match); - } else { - qemu_del_timer(timer->timer); - qemu_del_timer(timer->match); - omap_gp_timer_out(timer, timer->scpwm); - } -} - -static inline void omap_gp_timer_trigger(struct omap_gp_timer_s *timer) -{ - if (timer->pt) - /* TODO in overflow-and-match mode if the first event to - * occur is the match, don't toggle. */ - omap_gp_timer_out(timer, !timer->out_val); - else - /* TODO inverted pulse on timer->out_val == 1? */ - qemu_irq_pulse(timer->out); -} - -static void omap_gp_timer_tick(void *opaque) -{ - struct omap_gp_timer_s *timer = (struct omap_gp_timer_s *) opaque; - - if (!timer->ar) { - timer->st = 0; - timer->val = 0; - } else { - timer->val = timer->load_val; - timer->time = qemu_get_clock(vm_clock); - } - - if (timer->trigger == gpt_trigger_overflow || - timer->trigger == gpt_trigger_both) - omap_gp_timer_trigger(timer); - - omap_gp_timer_intr(timer, GPT_OVF_IT); - omap_gp_timer_update(timer); -} - -static void omap_gp_timer_match(void *opaque) -{ - struct omap_gp_timer_s *timer = (struct omap_gp_timer_s *) opaque; - - if (timer->trigger == gpt_trigger_both) - omap_gp_timer_trigger(timer); - - omap_gp_timer_intr(timer, GPT_MAT_IT); -} - -static void omap_gp_timer_input(void *opaque, int line, int on) -{ - struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque; - int trigger; - - switch (s->capture) { - default: - case gpt_capture_none: - trigger = 0; - break; - case gpt_capture_rising: - trigger = !s->in_val && on; - break; - case gpt_capture_falling: - trigger = s->in_val && !on; - break; - case gpt_capture_both: - trigger = (s->in_val == !on); - break; - } - s->in_val = on; - - if (s->inout && trigger && s->capt_num < 2) { - s->capture_val[s->capt_num] = omap_gp_timer_read(s); - - if (s->capt2 == s->capt_num ++) - omap_gp_timer_intr(s, GPT_TCAR_IT); - } -} - -static void omap_gp_timer_clk_update(void *opaque, int line, int on) -{ - struct omap_gp_timer_s *timer = (struct omap_gp_timer_s *) opaque; - - omap_gp_timer_sync(timer); - timer->rate = on ? omap_clk_getrate(timer->clk) : 0; - omap_gp_timer_update(timer); -} - -static void omap_gp_timer_clk_setup(struct omap_gp_timer_s *timer) -{ - omap_clk_adduser(timer->clk, - qemu_allocate_irqs(omap_gp_timer_clk_update, timer, 1)[0]); - timer->rate = omap_clk_getrate(timer->clk); -} - -static void omap_gp_timer_reset(struct omap_gp_timer_s *s) -{ - s->config = 0x000; - s->status = 0; - s->it_ena = 0; - s->wu_ena = 0; - s->inout = 0; - s->capt2 = 0; - s->capt_num = 0; - s->pt = 0; - s->trigger = gpt_trigger_none; - s->capture = gpt_capture_none; - s->scpwm = 0; - s->ce = 0; - s->pre = 0; - s->ptv = 0; - s->ar = 0; - s->st = 0; - s->posted = 1; - s->val = 0x00000000; - s->load_val = 0x00000000; - s->capture_val[0] = 0x00000000; - s->capture_val[1] = 0x00000000; - s->match_val = 0x00000000; - omap_gp_timer_update(s); -} - -static uint32_t omap_gp_timer_readw(void *opaque, target_phys_addr_t addr) -{ - struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque; - - switch (addr) { - case 0x00: /* TIDR */ - return 0x21; - - case 0x10: /* TIOCP_CFG */ - return s->config; - - case 0x14: /* TISTAT */ - /* ??? When's this bit reset? */ - return 1; /* RESETDONE */ - - case 0x18: /* TISR */ - return s->status; - - case 0x1c: /* TIER */ - return s->it_ena; - - case 0x20: /* TWER */ - return s->wu_ena; - - case 0x24: /* TCLR */ - return (s->inout << 14) | - (s->capt2 << 13) | - (s->pt << 12) | - (s->trigger << 10) | - (s->capture << 8) | - (s->scpwm << 7) | - (s->ce << 6) | - (s->pre << 5) | - (s->ptv << 2) | - (s->ar << 1) | - (s->st << 0); - - case 0x28: /* TCRR */ - return omap_gp_timer_read(s); - - case 0x2c: /* TLDR */ - return s->load_val; - - case 0x30: /* TTGR */ - return 0xffffffff; - - case 0x34: /* TWPS */ - return 0x00000000; /* No posted writes pending. */ - - case 0x38: /* TMAR */ - return s->match_val; - - case 0x3c: /* TCAR1 */ - return s->capture_val[0]; - - case 0x40: /* TSICR */ - return s->posted << 2; - - case 0x44: /* TCAR2 */ - return s->capture_val[1]; - } - - OMAP_BAD_REG(addr); - return 0; -} - -static uint32_t omap_gp_timer_readh(void *opaque, target_phys_addr_t addr) -{ - struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque; - uint32_t ret; - - if (addr & 2) - return s->readh; - else { - ret = omap_gp_timer_readw(opaque, addr); - s->readh = ret >> 16; - return ret & 0xffff; - } -} - -static CPUReadMemoryFunc * const omap_gp_timer_readfn[] = { - omap_badwidth_read32, - omap_gp_timer_readh, - omap_gp_timer_readw, -}; - -static void omap_gp_timer_write(void *opaque, target_phys_addr_t addr, - uint32_t value) -{ - struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque; - - switch (addr) { - case 0x00: /* TIDR */ - case 0x14: /* TISTAT */ - case 0x34: /* TWPS */ - case 0x3c: /* TCAR1 */ - case 0x44: /* TCAR2 */ - OMAP_RO_REG(addr); - break; - - case 0x10: /* TIOCP_CFG */ - s->config = value & 0x33d; - if (((value >> 3) & 3) == 3) /* IDLEMODE */ - fprintf(stderr, "%s: illegal IDLEMODE value in TIOCP_CFG\n", - __FUNCTION__); - if (value & 2) /* SOFTRESET */ - omap_gp_timer_reset(s); - break; - - case 0x18: /* TISR */ - if (value & GPT_TCAR_IT) - s->capt_num = 0; - if (s->status && !(s->status &= ~value)) - qemu_irq_lower(s->irq); - break; - - case 0x1c: /* TIER */ - s->it_ena = value & 7; - break; - - case 0x20: /* TWER */ - s->wu_ena = value & 7; - break; - - case 0x24: /* TCLR */ - omap_gp_timer_sync(s); - s->inout = (value >> 14) & 1; - s->capt2 = (value >> 13) & 1; - s->pt = (value >> 12) & 1; - s->trigger = (value >> 10) & 3; - if (s->capture == gpt_capture_none && - ((value >> 8) & 3) != gpt_capture_none) - s->capt_num = 0; - s->capture = (value >> 8) & 3; - s->scpwm = (value >> 7) & 1; - s->ce = (value >> 6) & 1; - s->pre = (value >> 5) & 1; - s->ptv = (value >> 2) & 7; - s->ar = (value >> 1) & 1; - s->st = (value >> 0) & 1; - if (s->inout && s->trigger != gpt_trigger_none) - fprintf(stderr, "%s: GP timer pin must be an output " - "for this trigger mode\n", __FUNCTION__); - if (!s->inout && s->capture != gpt_capture_none) - fprintf(stderr, "%s: GP timer pin must be an input " - "for this capture mode\n", __FUNCTION__); - if (s->trigger == gpt_trigger_none) - omap_gp_timer_out(s, s->scpwm); - /* TODO: make sure this doesn't overflow 32-bits */ - s->ticks_per_sec = get_ticks_per_sec() << (s->pre ? s->ptv + 1 : 0); - omap_gp_timer_update(s); - break; - - case 0x28: /* TCRR */ - s->time = qemu_get_clock(vm_clock); - s->val = value; - omap_gp_timer_update(s); - break; - - case 0x2c: /* TLDR */ - s->load_val = value; - break; - - case 0x30: /* TTGR */ - s->time = qemu_get_clock(vm_clock); - s->val = s->load_val; - omap_gp_timer_update(s); - break; - - case 0x38: /* TMAR */ - omap_gp_timer_sync(s); - s->match_val = value; - omap_gp_timer_update(s); - break; - - case 0x40: /* TSICR */ - s->posted = (value >> 2) & 1; - if (value & 2) /* How much exactly are we supposed to reset? */ - omap_gp_timer_reset(s); - break; - - default: - OMAP_BAD_REG(addr); - } -} - -static void omap_gp_timer_writeh(void *opaque, target_phys_addr_t addr, - uint32_t value) -{ - struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque; - - if (addr & 2) - return omap_gp_timer_write(opaque, addr, (value << 16) | s->writeh); - else - s->writeh = (uint16_t) value; -} - -static CPUWriteMemoryFunc * const omap_gp_timer_writefn[] = { - omap_badwidth_write32, - omap_gp_timer_writeh, - omap_gp_timer_write, -}; - -struct omap_gp_timer_s *omap_gp_timer_init(struct omap_target_agent_s *ta, - qemu_irq irq, omap_clk fclk, omap_clk iclk) -{ - int iomemtype; - struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) - qemu_mallocz(sizeof(struct omap_gp_timer_s)); - - s->ta = ta; - s->irq = irq; - s->clk = fclk; - s->timer = qemu_new_timer(vm_clock, omap_gp_timer_tick, s); - s->match = qemu_new_timer(vm_clock, omap_gp_timer_match, s); - s->in = qemu_allocate_irqs(omap_gp_timer_input, s, 1)[0]; - omap_gp_timer_reset(s); - omap_gp_timer_clk_setup(s); - - iomemtype = l4_register_io_memory(omap_gp_timer_readfn, - omap_gp_timer_writefn, s); - omap_l4_attach(ta, 0, iomemtype); - - return s; -} - -/* 32-kHz Sync Timer of the OMAP2 */ -static uint32_t omap_synctimer_read(struct omap_synctimer_s *s) { - return muldiv64(qemu_get_clock(vm_clock), 0x8000, get_ticks_per_sec()); -} - -static void omap_synctimer_reset(struct omap_synctimer_s *s) -{ - s->val = omap_synctimer_read(s); -} - -static uint32_t omap_synctimer_readw(void *opaque, target_phys_addr_t addr) -{ - struct omap_synctimer_s *s = (struct omap_synctimer_s *) opaque; - - switch (addr) { - case 0x00: /* 32KSYNCNT_REV */ - return 0x21; - - case 0x10: /* CR */ - return omap_synctimer_read(s) - s->val; - } - - OMAP_BAD_REG(addr); - return 0; -} - -static uint32_t omap_synctimer_readh(void *opaque, target_phys_addr_t addr) -{ - struct omap_synctimer_s *s = (struct omap_synctimer_s *) opaque; - uint32_t ret; - - if (addr & 2) - return s->readh; - else { - ret = omap_synctimer_readw(opaque, addr); - s->readh = ret >> 16; - return ret & 0xffff; - } -} - -static CPUReadMemoryFunc * const omap_synctimer_readfn[] = { - omap_badwidth_read32, - omap_synctimer_readh, - omap_synctimer_readw, -}; - -static void omap_synctimer_write(void *opaque, target_phys_addr_t addr, - uint32_t value) -{ - OMAP_BAD_REG(addr); -} - -static CPUWriteMemoryFunc * const omap_synctimer_writefn[] = { - omap_badwidth_write32, - omap_synctimer_write, - omap_synctimer_write, -}; - -void omap_synctimer_init(struct omap_target_agent_s *ta, - struct omap_mpu_state_s *mpu, omap_clk fclk, omap_clk iclk) -{ - struct omap_synctimer_s *s = &mpu->synctimer; - - omap_synctimer_reset(s); - omap_l4_attach(ta, 0, l4_register_io_memory( - omap_synctimer_readfn, omap_synctimer_writefn, s)); -} - -/* General-Purpose Interface of OMAP2 */ -struct omap2_gpio_s { - qemu_irq irq[2]; - qemu_irq wkup; - qemu_irq *in; - qemu_irq handler[32]; - - uint8_t config[2]; - uint32_t inputs; - uint32_t outputs; - uint32_t dir; - uint32_t level[2]; - uint32_t edge[2]; - uint32_t mask[2]; - uint32_t wumask; - uint32_t ints[2]; - uint32_t debounce; - uint8_t delay; -}; - -static inline void omap_gpio_module_int_update(struct omap2_gpio_s *s, - int line) -{ - qemu_set_irq(s->irq[line], s->ints[line] & s->mask[line]); -} - -static void omap_gpio_module_wake(struct omap2_gpio_s *s, int line) -{ - if (!(s->config[0] & (1 << 2))) /* ENAWAKEUP */ - return; - if (!(s->config[0] & (3 << 3))) /* Force Idle */ - return; - if (!(s->wumask & (1 << line))) - return; - - qemu_irq_raise(s->wkup); -} - -static inline void omap_gpio_module_out_update(struct omap2_gpio_s *s, - uint32_t diff) -{ - int ln; - - s->outputs ^= diff; - diff &= ~s->dir; - while ((ln = ffs(diff))) { - ln --; - qemu_set_irq(s->handler[ln], (s->outputs >> ln) & 1); - diff &= ~(1 << ln); - } -} - -static void omap_gpio_module_level_update(struct omap2_gpio_s *s, int line) -{ - s->ints[line] |= s->dir & - ((s->inputs & s->level[1]) | (~s->inputs & s->level[0])); - omap_gpio_module_int_update(s, line); -} - -static inline void omap_gpio_module_int(struct omap2_gpio_s *s, int line) -{ - s->ints[0] |= 1 << line; - omap_gpio_module_int_update(s, 0); - s->ints[1] |= 1 << line; - omap_gpio_module_int_update(s, 1); - omap_gpio_module_wake(s, line); -} - -static void omap_gpio_module_set(void *opaque, int line, int level) -{ - struct omap2_gpio_s *s = (struct omap2_gpio_s *) opaque; - - if (level) { - if (s->dir & (1 << line) & ((~s->inputs & s->edge[0]) | s->level[1])) - omap_gpio_module_int(s, line); - s->inputs |= 1 << line; - } else { - if (s->dir & (1 << line) & ((s->inputs & s->edge[1]) | s->level[0])) - omap_gpio_module_int(s, line); - s->inputs &= ~(1 << line); - } -} - -static void omap_gpio_module_reset(struct omap2_gpio_s *s) -{ - s->config[0] = 0; - s->config[1] = 2; - s->ints[0] = 0; - s->ints[1] = 0; - s->mask[0] = 0; - s->mask[1] = 0; - s->wumask = 0; - s->dir = ~0; - s->level[0] = 0; - s->level[1] = 0; - s->edge[0] = 0; - s->edge[1] = 0; - s->debounce = 0; - s->delay = 0; -} - -static uint32_t omap_gpio_module_read(void *opaque, target_phys_addr_t addr) -{ - struct omap2_gpio_s *s = (struct omap2_gpio_s *) opaque; - - switch (addr) { - case 0x00: /* GPIO_REVISION */ - return 0x18; - - case 0x10: /* GPIO_SYSCONFIG */ - return s->config[0]; - - case 0x14: /* GPIO_SYSSTATUS */ - return 0x01; - - case 0x18: /* GPIO_IRQSTATUS1 */ - return s->ints[0]; - - case 0x1c: /* GPIO_IRQENABLE1 */ - case 0x60: /* GPIO_CLEARIRQENABLE1 */ - case 0x64: /* GPIO_SETIRQENABLE1 */ - return s->mask[0]; - - case 0x20: /* GPIO_WAKEUPENABLE */ - case 0x80: /* GPIO_CLEARWKUENA */ - case 0x84: /* GPIO_SETWKUENA */ - return s->wumask; - - case 0x28: /* GPIO_IRQSTATUS2 */ - return s->ints[1]; - - case 0x2c: /* GPIO_IRQENABLE2 */ - case 0x70: /* GPIO_CLEARIRQENABLE2 */ - case 0x74: /* GPIO_SETIREQNEABLE2 */ - return s->mask[1]; - - case 0x30: /* GPIO_CTRL */ - return s->config[1]; - - case 0x34: /* GPIO_OE */ - return s->dir; - - case 0x38: /* GPIO_DATAIN */ - return s->inputs; - - case 0x3c: /* GPIO_DATAOUT */ - case 0x90: /* GPIO_CLEARDATAOUT */ - case 0x94: /* GPIO_SETDATAOUT */ - return s->outputs; - - case 0x40: /* GPIO_LEVELDETECT0 */ - return s->level[0]; - - case 0x44: /* GPIO_LEVELDETECT1 */ - return s->level[1]; - - case 0x48: /* GPIO_RISINGDETECT */ - return s->edge[0]; - - case 0x4c: /* GPIO_FALLINGDETECT */ - return s->edge[1]; - - case 0x50: /* GPIO_DEBOUNCENABLE */ - return s->debounce; - - case 0x54: /* GPIO_DEBOUNCINGTIME */ - return s->delay; - } - - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_gpio_module_write(void *opaque, target_phys_addr_t addr, - uint32_t value) -{ - struct omap2_gpio_s *s = (struct omap2_gpio_s *) opaque; - uint32_t diff; - int ln; - - switch (addr) { - case 0x00: /* GPIO_REVISION */ - case 0x14: /* GPIO_SYSSTATUS */ - case 0x38: /* GPIO_DATAIN */ - OMAP_RO_REG(addr); - break; - - case 0x10: /* GPIO_SYSCONFIG */ - if (((value >> 3) & 3) == 3) - fprintf(stderr, "%s: bad IDLEMODE value\n", __FUNCTION__); - if (value & 2) - omap_gpio_module_reset(s); - s->config[0] = value & 0x1d; - break; - - case 0x18: /* GPIO_IRQSTATUS1 */ - if (s->ints[0] & value) { - s->ints[0] &= ~value; - omap_gpio_module_level_update(s, 0); - } - break; - - case 0x1c: /* GPIO_IRQENABLE1 */ - s->mask[0] = value; - omap_gpio_module_int_update(s, 0); - break; - - case 0x20: /* GPIO_WAKEUPENABLE */ - s->wumask = value; - break; - - case 0x28: /* GPIO_IRQSTATUS2 */ - if (s->ints[1] & value) { - s->ints[1] &= ~value; - omap_gpio_module_level_update(s, 1); - } - break; - - case 0x2c: /* GPIO_IRQENABLE2 */ - s->mask[1] = value; - omap_gpio_module_int_update(s, 1); - break; - - case 0x30: /* GPIO_CTRL */ - s->config[1] = value & 7; - break; - - case 0x34: /* GPIO_OE */ - diff = s->outputs & (s->dir ^ value); - s->dir = value; - - value = s->outputs & ~s->dir; - while ((ln = ffs(diff))) { - diff &= ~(1 <<-- ln); - qemu_set_irq(s->handler[ln], (value >> ln) & 1); - } - - omap_gpio_module_level_update(s, 0); - omap_gpio_module_level_update(s, 1); - break; - - case 0x3c: /* GPIO_DATAOUT */ - omap_gpio_module_out_update(s, s->outputs ^ value); - break; - - case 0x40: /* GPIO_LEVELDETECT0 */ - s->level[0] = value; - omap_gpio_module_level_update(s, 0); - omap_gpio_module_level_update(s, 1); - break; - - case 0x44: /* GPIO_LEVELDETECT1 */ - s->level[1] = value; - omap_gpio_module_level_update(s, 0); - omap_gpio_module_level_update(s, 1); - break; - - case 0x48: /* GPIO_RISINGDETECT */ - s->edge[0] = value; - break; - - case 0x4c: /* GPIO_FALLINGDETECT */ - s->edge[1] = value; - break; - - case 0x50: /* GPIO_DEBOUNCENABLE */ - s->debounce = value; - break; - - case 0x54: /* GPIO_DEBOUNCINGTIME */ - s->delay = value; - break; - - case 0x60: /* GPIO_CLEARIRQENABLE1 */ - s->mask[0] &= ~value; - omap_gpio_module_int_update(s, 0); - break; - - case 0x64: /* GPIO_SETIRQENABLE1 */ - s->mask[0] |= value; - omap_gpio_module_int_update(s, 0); - break; - - case 0x70: /* GPIO_CLEARIRQENABLE2 */ - s->mask[1] &= ~value; - omap_gpio_module_int_update(s, 1); - break; - - case 0x74: /* GPIO_SETIREQNEABLE2 */ - s->mask[1] |= value; - omap_gpio_module_int_update(s, 1); - break; - - case 0x80: /* GPIO_CLEARWKUENA */ - s->wumask &= ~value; - break; - - case 0x84: /* GPIO_SETWKUENA */ - s->wumask |= value; - break; - - case 0x90: /* GPIO_CLEARDATAOUT */ - omap_gpio_module_out_update(s, s->outputs & value); - break; - - case 0x94: /* GPIO_SETDATAOUT */ - omap_gpio_module_out_update(s, ~s->outputs & value); - break; - - default: - OMAP_BAD_REG(addr); - return; - } -} - -static uint32_t omap_gpio_module_readp(void *opaque, target_phys_addr_t addr) -{ - return omap_gpio_module_readp(opaque, addr) >> ((addr & 3) << 3); -} - -static void omap_gpio_module_writep(void *opaque, target_phys_addr_t addr, - uint32_t value) -{ - uint32_t cur = 0; - uint32_t mask = 0xffff; - - switch (addr & ~3) { - case 0x00: /* GPIO_REVISION */ - case 0x14: /* GPIO_SYSSTATUS */ - case 0x38: /* GPIO_DATAIN */ - OMAP_RO_REG(addr); - break; - - case 0x10: /* GPIO_SYSCONFIG */ - case 0x1c: /* GPIO_IRQENABLE1 */ - case 0x20: /* GPIO_WAKEUPENABLE */ - case 0x2c: /* GPIO_IRQENABLE2 */ - case 0x30: /* GPIO_CTRL */ - case 0x34: /* GPIO_OE */ - case 0x3c: /* GPIO_DATAOUT */ - case 0x40: /* GPIO_LEVELDETECT0 */ - case 0x44: /* GPIO_LEVELDETECT1 */ - case 0x48: /* GPIO_RISINGDETECT */ - case 0x4c: /* GPIO_FALLINGDETECT */ - case 0x50: /* GPIO_DEBOUNCENABLE */ - case 0x54: /* GPIO_DEBOUNCINGTIME */ - cur = omap_gpio_module_read(opaque, addr & ~3) & - ~(mask << ((addr & 3) << 3)); - - /* Fall through. */ - case 0x18: /* GPIO_IRQSTATUS1 */ - case 0x28: /* GPIO_IRQSTATUS2 */ - case 0x60: /* GPIO_CLEARIRQENABLE1 */ - case 0x64: /* GPIO_SETIRQENABLE1 */ - case 0x70: /* GPIO_CLEARIRQENABLE2 */ - case 0x74: /* GPIO_SETIREQNEABLE2 */ - case 0x80: /* GPIO_CLEARWKUENA */ - case 0x84: /* GPIO_SETWKUENA */ - case 0x90: /* GPIO_CLEARDATAOUT */ - case 0x94: /* GPIO_SETDATAOUT */ - value <<= (addr & 3) << 3; - omap_gpio_module_write(opaque, addr, cur | value); - break; - - default: - OMAP_BAD_REG(addr); - return; - } -} - -static CPUReadMemoryFunc * const omap_gpio_module_readfn[] = { - omap_gpio_module_readp, - omap_gpio_module_readp, - omap_gpio_module_read, -}; - -static CPUWriteMemoryFunc * const omap_gpio_module_writefn[] = { - omap_gpio_module_writep, - omap_gpio_module_writep, - omap_gpio_module_write, -}; - -static void omap_gpio_module_init(struct omap2_gpio_s *s, - struct omap_target_agent_s *ta, int region, - qemu_irq mpu, qemu_irq dsp, qemu_irq wkup, - omap_clk fclk, omap_clk iclk) -{ - int iomemtype; - - s->irq[0] = mpu; - s->irq[1] = dsp; - s->wkup = wkup; - s->in = qemu_allocate_irqs(omap_gpio_module_set, s, 32); - - iomemtype = l4_register_io_memory(omap_gpio_module_readfn, - omap_gpio_module_writefn, s); - omap_l4_attach(ta, region, iomemtype); -} - -struct omap_gpif_s { - struct omap2_gpio_s module[5]; - int modules; - - int autoidle; - int gpo; -}; - -static void omap_gpif_reset(struct omap_gpif_s *s) -{ - int i; - - for (i = 0; i < s->modules; i ++) - omap_gpio_module_reset(s->module + i); - - s->autoidle = 0; - s->gpo = 0; -} - -static uint32_t omap_gpif_top_read(void *opaque, target_phys_addr_t addr) -{ - struct omap_gpif_s *s = (struct omap_gpif_s *) opaque; - - switch (addr) { - case 0x00: /* IPGENERICOCPSPL_REVISION */ - return 0x18; - - case 0x10: /* IPGENERICOCPSPL_SYSCONFIG */ - return s->autoidle; - case 0x14: /* IPGENERICOCPSPL_SYSSTATUS */ - return 0x01; - - case 0x18: /* IPGENERICOCPSPL_IRQSTATUS */ - return 0x00; - - case 0x40: /* IPGENERICOCPSPL_GPO */ - return s->gpo; - - case 0x50: /* IPGENERICOCPSPL_GPI */ - return 0x00; - } - - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_gpif_top_write(void *opaque, target_phys_addr_t addr, - uint32_t value) -{ - struct omap_gpif_s *s = (struct omap_gpif_s *) opaque; - - switch (addr) { - case 0x00: /* IPGENERICOCPSPL_REVISION */ - case 0x14: /* IPGENERICOCPSPL_SYSSTATUS */ - case 0x18: /* IPGENERICOCPSPL_IRQSTATUS */ - case 0x50: /* IPGENERICOCPSPL_GPI */ - OMAP_RO_REG(addr); - break; - - case 0x10: /* IPGENERICOCPSPL_SYSCONFIG */ - if (value & (1 << 1)) /* SOFTRESET */ - omap_gpif_reset(s); - s->autoidle = value & 1; - break; - - case 0x40: /* IPGENERICOCPSPL_GPO */ - s->gpo = value & 1; - break; - - default: - OMAP_BAD_REG(addr); - return; - } -} - -static CPUReadMemoryFunc * const omap_gpif_top_readfn[] = { - omap_gpif_top_read, - omap_gpif_top_read, - omap_gpif_top_read, -}; - -static CPUWriteMemoryFunc * const omap_gpif_top_writefn[] = { - omap_gpif_top_write, - omap_gpif_top_write, - omap_gpif_top_write, -}; - -struct omap_gpif_s *omap2_gpio_init(struct omap_target_agent_s *ta, - qemu_irq *irq, omap_clk *fclk, omap_clk iclk, int modules) -{ - int iomemtype, i; - struct omap_gpif_s *s = (struct omap_gpif_s *) - qemu_mallocz(sizeof(struct omap_gpif_s)); - int region[4] = { 0, 2, 4, 5 }; - - s->modules = modules; - for (i = 0; i < modules; i ++) - omap_gpio_module_init(s->module + i, ta, region[i], - irq[i], NULL, NULL, fclk[i], iclk); - - omap_gpif_reset(s); - - iomemtype = l4_register_io_memory(omap_gpif_top_readfn, - omap_gpif_top_writefn, s); - omap_l4_attach(ta, 1, iomemtype); - - return s; -} - -qemu_irq *omap2_gpio_in_get(struct omap_gpif_s *s, int start) -{ - if (start >= s->modules * 32 || start < 0) - hw_error("%s: No GPIO line %i\n", __FUNCTION__, start); - return s->module[start >> 5].in + (start & 31); -} - -void omap2_gpio_out_set(struct omap_gpif_s *s, int line, qemu_irq handler) -{ - if (line >= s->modules * 32 || line < 0) - hw_error("%s: No GPIO line %i\n", __FUNCTION__, line); - s->module[line >> 5].handler[line & 31] = handler; -} - -/* Multichannel SPI */ -struct omap_mcspi_s { - qemu_irq irq; - int chnum; - - uint32_t sysconfig; - uint32_t systest; - uint32_t irqst; - uint32_t irqen; - uint32_t wken; - uint32_t control; - - struct omap_mcspi_ch_s { - qemu_irq txdrq; - qemu_irq rxdrq; - uint32_t (*txrx)(void *opaque, uint32_t, int); - void *opaque; - - uint32_t tx; - uint32_t rx; - - uint32_t config; - uint32_t status; - uint32_t control; - } ch[4]; -}; - -static inline void omap_mcspi_interrupt_update(struct omap_mcspi_s *s) -{ - qemu_set_irq(s->irq, s->irqst & s->irqen); -} - -static inline void omap_mcspi_dmarequest_update(struct omap_mcspi_ch_s *ch) -{ - qemu_set_irq(ch->txdrq, - (ch->control & 1) && /* EN */ - (ch->config & (1 << 14)) && /* DMAW */ - (ch->status & (1 << 1)) && /* TXS */ - ((ch->config >> 12) & 3) != 1); /* TRM */ - qemu_set_irq(ch->rxdrq, - (ch->control & 1) && /* EN */ - (ch->config & (1 << 15)) && /* DMAW */ - (ch->status & (1 << 0)) && /* RXS */ - ((ch->config >> 12) & 3) != 2); /* TRM */ -} - -static void omap_mcspi_transfer_run(struct omap_mcspi_s *s, int chnum) -{ - struct omap_mcspi_ch_s *ch = s->ch + chnum; - - if (!(ch->control & 1)) /* EN */ - return; - if ((ch->status & (1 << 0)) && /* RXS */ - ((ch->config >> 12) & 3) != 2 && /* TRM */ - !(ch->config & (1 << 19))) /* TURBO */ - goto intr_update; - if ((ch->status & (1 << 1)) && /* TXS */ - ((ch->config >> 12) & 3) != 1) /* TRM */ - goto intr_update; - - if (!(s->control & 1) || /* SINGLE */ - (ch->config & (1 << 20))) { /* FORCE */ - if (ch->txrx) - ch->rx = ch->txrx(ch->opaque, ch->tx, /* WL */ - 1 + (0x1f & (ch->config >> 7))); - } - - ch->tx = 0; - ch->status |= 1 << 2; /* EOT */ - ch->status |= 1 << 1; /* TXS */ - if (((ch->config >> 12) & 3) != 2) /* TRM */ - ch->status |= 1 << 0; /* RXS */ - -intr_update: - if ((ch->status & (1 << 0)) && /* RXS */ - ((ch->config >> 12) & 3) != 2 && /* TRM */ - !(ch->config & (1 << 19))) /* TURBO */ - s->irqst |= 1 << (2 + 4 * chnum); /* RX_FULL */ - if ((ch->status & (1 << 1)) && /* TXS */ - ((ch->config >> 12) & 3) != 1) /* TRM */ - s->irqst |= 1 << (0 + 4 * chnum); /* TX_EMPTY */ - omap_mcspi_interrupt_update(s); - omap_mcspi_dmarequest_update(ch); -} - -static void omap_mcspi_reset(struct omap_mcspi_s *s) -{ - int ch; - - s->sysconfig = 0; - s->systest = 0; - s->irqst = 0; - s->irqen = 0; - s->wken = 0; - s->control = 4; - - for (ch = 0; ch < 4; ch ++) { - s->ch[ch].config = 0x060000; - s->ch[ch].status = 2; /* TXS */ - s->ch[ch].control = 0; - - omap_mcspi_dmarequest_update(s->ch + ch); - } - - omap_mcspi_interrupt_update(s); -} - -static uint32_t omap_mcspi_read(void *opaque, target_phys_addr_t addr) -{ - struct omap_mcspi_s *s = (struct omap_mcspi_s *) opaque; - int ch = 0; - uint32_t ret; - - switch (addr) { - case 0x00: /* MCSPI_REVISION */ - return 0x91; - - case 0x10: /* MCSPI_SYSCONFIG */ - return s->sysconfig; - - case 0x14: /* MCSPI_SYSSTATUS */ - return 1; /* RESETDONE */ - - case 0x18: /* MCSPI_IRQSTATUS */ - return s->irqst; - - case 0x1c: /* MCSPI_IRQENABLE */ - return s->irqen; - - case 0x20: /* MCSPI_WAKEUPENABLE */ - return s->wken; - - case 0x24: /* MCSPI_SYST */ - return s->systest; - - case 0x28: /* MCSPI_MODULCTRL */ - return s->control; - - case 0x68: ch ++; - case 0x54: ch ++; - case 0x40: ch ++; - case 0x2c: /* MCSPI_CHCONF */ - return s->ch[ch].config; - - case 0x6c: ch ++; - case 0x58: ch ++; - case 0x44: ch ++; - case 0x30: /* MCSPI_CHSTAT */ - return s->ch[ch].status; - - case 0x70: ch ++; - case 0x5c: ch ++; - case 0x48: ch ++; - case 0x34: /* MCSPI_CHCTRL */ - return s->ch[ch].control; - - case 0x74: ch ++; - case 0x60: ch ++; - case 0x4c: ch ++; - case 0x38: /* MCSPI_TX */ - return s->ch[ch].tx; - - case 0x78: ch ++; - case 0x64: ch ++; - case 0x50: ch ++; - case 0x3c: /* MCSPI_RX */ - s->ch[ch].status &= ~(1 << 0); /* RXS */ - ret = s->ch[ch].rx; - omap_mcspi_transfer_run(s, ch); - return ret; - } - - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_mcspi_write(void *opaque, target_phys_addr_t addr, - uint32_t value) -{ - struct omap_mcspi_s *s = (struct omap_mcspi_s *) opaque; - int ch = 0; - - switch (addr) { - case 0x00: /* MCSPI_REVISION */ - case 0x14: /* MCSPI_SYSSTATUS */ - case 0x30: /* MCSPI_CHSTAT0 */ - case 0x3c: /* MCSPI_RX0 */ - case 0x44: /* MCSPI_CHSTAT1 */ - case 0x50: /* MCSPI_RX1 */ - case 0x58: /* MCSPI_CHSTAT2 */ - case 0x64: /* MCSPI_RX2 */ - case 0x6c: /* MCSPI_CHSTAT3 */ - case 0x78: /* MCSPI_RX3 */ - OMAP_RO_REG(addr); - return; - - case 0x10: /* MCSPI_SYSCONFIG */ - if (value & (1 << 1)) /* SOFTRESET */ - omap_mcspi_reset(s); - s->sysconfig = value & 0x31d; - break; - - case 0x18: /* MCSPI_IRQSTATUS */ - if (!((s->control & (1 << 3)) && (s->systest & (1 << 11)))) { - s->irqst &= ~value; - omap_mcspi_interrupt_update(s); - } - break; - - case 0x1c: /* MCSPI_IRQENABLE */ - s->irqen = value & 0x1777f; - omap_mcspi_interrupt_update(s); - break; - - case 0x20: /* MCSPI_WAKEUPENABLE */ - s->wken = value & 1; - break; - - case 0x24: /* MCSPI_SYST */ - if (s->control & (1 << 3)) /* SYSTEM_TEST */ - if (value & (1 << 11)) { /* SSB */ - s->irqst |= 0x1777f; - omap_mcspi_interrupt_update(s); - } - s->systest = value & 0xfff; - break; - - case 0x28: /* MCSPI_MODULCTRL */ - if (value & (1 << 3)) /* SYSTEM_TEST */ - if (s->systest & (1 << 11)) { /* SSB */ - s->irqst |= 0x1777f; - omap_mcspi_interrupt_update(s); - } - s->control = value & 0xf; - break; - - case 0x68: ch ++; - case 0x54: ch ++; - case 0x40: ch ++; - case 0x2c: /* MCSPI_CHCONF */ - if ((value ^ s->ch[ch].config) & (3 << 14)) /* DMAR | DMAW */ - omap_mcspi_dmarequest_update(s->ch + ch); - if (((value >> 12) & 3) == 3) /* TRM */ - fprintf(stderr, "%s: invalid TRM value (3)\n", __FUNCTION__); - if (((value >> 7) & 0x1f) < 3) /* WL */ - fprintf(stderr, "%s: invalid WL value (%i)\n", - __FUNCTION__, (value >> 7) & 0x1f); - s->ch[ch].config = value & 0x7fffff; - break; - - case 0x70: ch ++; - case 0x5c: ch ++; - case 0x48: ch ++; - case 0x34: /* MCSPI_CHCTRL */ - if (value & ~s->ch[ch].control & 1) { /* EN */ - s->ch[ch].control |= 1; - omap_mcspi_transfer_run(s, ch); - } else - s->ch[ch].control = value & 1; - break; - - case 0x74: ch ++; - case 0x60: ch ++; - case 0x4c: ch ++; - case 0x38: /* MCSPI_TX */ - s->ch[ch].tx = value; - s->ch[ch].status &= ~(1 << 1); /* TXS */ - omap_mcspi_transfer_run(s, ch); - break; - - default: - OMAP_BAD_REG(addr); - return; - } -} - -static CPUReadMemoryFunc * const omap_mcspi_readfn[] = { - omap_badwidth_read32, - omap_badwidth_read32, - omap_mcspi_read, -}; - -static CPUWriteMemoryFunc * const omap_mcspi_writefn[] = { - omap_badwidth_write32, - omap_badwidth_write32, - omap_mcspi_write, -}; - -struct omap_mcspi_s *omap_mcspi_init(struct omap_target_agent_s *ta, int chnum, - qemu_irq irq, qemu_irq *drq, omap_clk fclk, omap_clk iclk) -{ - int iomemtype; - struct omap_mcspi_s *s = (struct omap_mcspi_s *) - qemu_mallocz(sizeof(struct omap_mcspi_s)); - struct omap_mcspi_ch_s *ch = s->ch; - - s->irq = irq; - s->chnum = chnum; - while (chnum --) { - ch->txdrq = *drq ++; - ch->rxdrq = *drq ++; - ch ++; - } - omap_mcspi_reset(s); - - iomemtype = l4_register_io_memory(omap_mcspi_readfn, - omap_mcspi_writefn, s); - omap_l4_attach(ta, 0, iomemtype); - - return s; -} - -void omap_mcspi_attach(struct omap_mcspi_s *s, - uint32_t (*txrx)(void *opaque, uint32_t, int), void *opaque, - int chipselect) -{ - if (chipselect < 0 || chipselect >= s->chnum) - hw_error("%s: Bad chipselect %i\n", __FUNCTION__, chipselect); - - s->ch[chipselect].txrx = txrx; - s->ch[chipselect].opaque = opaque; -} +#include "blockdev.h" +#include "hw.h" +#include "arm-misc.h" +#include "omap.h" +#include "sysemu.h" +#include "qemu-timer.h" +#include "qemu-char.h" +#include "flash.h" +#include "soc_dma.h" +#include "audio/audio.h" /* Enhanced Audio Controller (CODEC only) */ struct omap_eac_s { @@ -1959,7 +585,7 @@ omap_badwidth_write16, }; -struct omap_eac_s *omap_eac_init(struct omap_target_agent_s *ta, +static struct omap_eac_s *omap_eac_init(struct omap_target_agent_s *ta, qemu_irq irq, qemu_irq *drq, omap_clk fclk, omap_clk iclk) { int iomemtype; @@ -1968,16 +594,14 @@ s->irq = irq; s->codec.rxdrq = *drq ++; - s->codec.txdrq = *drq ++; + s->codec.txdrq = *drq; omap_eac_reset(s); -#ifdef HAS_AUDIO AUD_register_card("OMAP EAC", &s->codec.card); iomemtype = cpu_register_io_memory(omap_eac_readfn, - omap_eac_writefn, s); + omap_eac_writefn, s, DEVICE_NATIVE_ENDIAN); omap_l4_attach(ta, 0, iomemtype); -#endif return s; } @@ -2134,232 +758,47 @@ } } -static CPUReadMemoryFunc * const omap_sti_fifo_readfn[] = { - omap_sti_fifo_read, - omap_badwidth_read8, - omap_badwidth_read8, -}; - -static CPUWriteMemoryFunc * const omap_sti_fifo_writefn[] = { - omap_sti_fifo_write, - omap_badwidth_write8, - omap_badwidth_write8, -}; - -static struct omap_sti_s *omap_sti_init(struct omap_target_agent_s *ta, - target_phys_addr_t channel_base, qemu_irq irq, omap_clk clk, - CharDriverState *chr) -{ - int iomemtype; - struct omap_sti_s *s = (struct omap_sti_s *) - qemu_mallocz(sizeof(struct omap_sti_s)); - - s->irq = irq; - omap_sti_reset(s); - - s->chr = chr ?: qemu_chr_open("null", "null", NULL); - - iomemtype = l4_register_io_memory(omap_sti_readfn, - omap_sti_writefn, s); - omap_l4_attach(ta, 0, iomemtype); - - iomemtype = cpu_register_io_memory(omap_sti_fifo_readfn, - omap_sti_fifo_writefn, s); - cpu_register_physical_memory(channel_base, 0x10000, iomemtype); - - return s; -} - -/* L4 Interconnect */ -struct omap_target_agent_s { - struct omap_l4_s *bus; - int regions; - struct omap_l4_region_s *start; - target_phys_addr_t base; - uint32_t component; - uint32_t control; - uint32_t status; -}; - -struct omap_l4_s { - target_phys_addr_t base; - int ta_num; - struct omap_target_agent_s ta[0]; -}; - -#ifdef L4_MUX_HACK -static int omap_l4_io_entries; -static int omap_cpu_io_entry; -static struct omap_l4_entry { - CPUReadMemoryFunc * const *mem_read; - CPUWriteMemoryFunc * const *mem_write; - void *opaque; -} *omap_l4_io_entry; -static CPUReadMemoryFunc * const *omap_l4_io_readb_fn; -static CPUReadMemoryFunc * const *omap_l4_io_readh_fn; -static CPUReadMemoryFunc * const *omap_l4_io_readw_fn; -static CPUWriteMemoryFunc * const *omap_l4_io_writeb_fn; -static CPUWriteMemoryFunc * const *omap_l4_io_writeh_fn; -static CPUWriteMemoryFunc * const *omap_l4_io_writew_fn; -static void **omap_l4_io_opaque; - -int l4_register_io_memory(CPUReadMemoryFunc * const *mem_read, - CPUWriteMemoryFunc * const *mem_write, void *opaque) -{ - omap_l4_io_entry[omap_l4_io_entries].mem_read = mem_read; - omap_l4_io_entry[omap_l4_io_entries].mem_write = mem_write; - omap_l4_io_entry[omap_l4_io_entries].opaque = opaque; - - return omap_l4_io_entries ++; -} - -static uint32_t omap_l4_io_readb(void *opaque, target_phys_addr_t addr) -{ - unsigned int i = (addr - OMAP2_L4_BASE) >> TARGET_PAGE_BITS; - - return omap_l4_io_readb_fn[i](omap_l4_io_opaque[i], addr); -} - -static uint32_t omap_l4_io_readh(void *opaque, target_phys_addr_t addr) -{ - unsigned int i = (addr - OMAP2_L4_BASE) >> TARGET_PAGE_BITS; - - return omap_l4_io_readh_fn[i](omap_l4_io_opaque[i], addr); -} - -static uint32_t omap_l4_io_readw(void *opaque, target_phys_addr_t addr) -{ - unsigned int i = (addr - OMAP2_L4_BASE) >> TARGET_PAGE_BITS; - - return omap_l4_io_readw_fn[i](omap_l4_io_opaque[i], addr); -} - -static void omap_l4_io_writeb(void *opaque, target_phys_addr_t addr, - uint32_t value) -{ - unsigned int i = (addr - OMAP2_L4_BASE) >> TARGET_PAGE_BITS; - - return omap_l4_io_writeb_fn[i](omap_l4_io_opaque[i], addr, value); -} - -static void omap_l4_io_writeh(void *opaque, target_phys_addr_t addr, - uint32_t value) -{ - unsigned int i = (addr - OMAP2_L4_BASE) >> TARGET_PAGE_BITS; - - return omap_l4_io_writeh_fn[i](omap_l4_io_opaque[i], addr, value); -} - -static void omap_l4_io_writew(void *opaque, target_phys_addr_t addr, - uint32_t value) -{ - unsigned int i = (addr - OMAP2_L4_BASE) >> TARGET_PAGE_BITS; - - return omap_l4_io_writew_fn[i](omap_l4_io_opaque[i], addr, value); -} - -static CPUReadMemoryFunc * const omap_l4_io_readfn[] = { - omap_l4_io_readb, - omap_l4_io_readh, - omap_l4_io_readw, -}; - -static CPUWriteMemoryFunc * const omap_l4_io_writefn[] = { - omap_l4_io_writeb, - omap_l4_io_writeh, - omap_l4_io_writew, -}; -#endif - -struct omap_l4_s *omap_l4_init(target_phys_addr_t base, int ta_num) -{ - struct omap_l4_s *bus = qemu_mallocz( - sizeof(*bus) + ta_num * sizeof(*bus->ta)); - - bus->ta_num = ta_num; - bus->base = base; - -#ifdef L4_MUX_HACK - omap_l4_io_entries = 1; - omap_l4_io_entry = qemu_mallocz(125 * sizeof(*omap_l4_io_entry)); - - omap_cpu_io_entry = - cpu_register_io_memory(omap_l4_io_readfn, - omap_l4_io_writefn, bus); -# define L4_PAGES (0xb4000 / TARGET_PAGE_SIZE) - omap_l4_io_readb_fn = qemu_mallocz(sizeof(void *) * L4_PAGES); - omap_l4_io_readh_fn = qemu_mallocz(sizeof(void *) * L4_PAGES); - omap_l4_io_readw_fn = qemu_mallocz(sizeof(void *) * L4_PAGES); - omap_l4_io_writeb_fn = qemu_mallocz(sizeof(void *) * L4_PAGES); - omap_l4_io_writeh_fn = qemu_mallocz(sizeof(void *) * L4_PAGES); - omap_l4_io_writew_fn = qemu_mallocz(sizeof(void *) * L4_PAGES); - omap_l4_io_opaque = qemu_mallocz(sizeof(void *) * L4_PAGES); -#endif - - return bus; -} - -static uint32_t omap_l4ta_read(void *opaque, target_phys_addr_t addr) -{ - struct omap_target_agent_s *s = (struct omap_target_agent_s *) opaque; - - switch (addr) { - case 0x00: /* COMPONENT */ - return s->component; - - case 0x20: /* AGENT_CONTROL */ - return s->control; - - case 0x28: /* AGENT_STATUS */ - return s->status; - } - - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_l4ta_write(void *opaque, target_phys_addr_t addr, - uint32_t value) +static CPUReadMemoryFunc * const omap_sti_fifo_readfn[] = { + omap_sti_fifo_read, + omap_badwidth_read8, + omap_badwidth_read8, +}; + +static CPUWriteMemoryFunc * const omap_sti_fifo_writefn[] = { + omap_sti_fifo_write, + omap_badwidth_write8, + omap_badwidth_write8, +}; + +static struct omap_sti_s *omap_sti_init(struct omap_target_agent_s *ta, + target_phys_addr_t channel_base, qemu_irq irq, omap_clk clk, + CharDriverState *chr) { - struct omap_target_agent_s *s = (struct omap_target_agent_s *) opaque; + int iomemtype; + struct omap_sti_s *s = (struct omap_sti_s *) + qemu_mallocz(sizeof(struct omap_sti_s)); - switch (addr) { - case 0x00: /* COMPONENT */ - case 0x28: /* AGENT_STATUS */ - OMAP_RO_REG(addr); - break; + s->irq = irq; + omap_sti_reset(s); - case 0x20: /* AGENT_CONTROL */ - s->control = value & 0x01000700; - if (value & 1) /* OCP_RESET */ - s->status &= ~1; /* REQ_TIMEOUT */ - break; + s->chr = chr ?: qemu_chr_open("null", "null", NULL); - default: - OMAP_BAD_REG(addr); - } -} + iomemtype = l4_register_io_memory(omap_sti_readfn, + omap_sti_writefn, s); + omap_l4_attach(ta, 0, iomemtype); -static CPUReadMemoryFunc * const omap_l4ta_readfn[] = { - omap_badwidth_read16, - omap_l4ta_read, - omap_badwidth_read16, -}; + iomemtype = cpu_register_io_memory(omap_sti_fifo_readfn, + omap_sti_fifo_writefn, s, DEVICE_NATIVE_ENDIAN); + cpu_register_physical_memory(channel_base, 0x10000, iomemtype); -static CPUWriteMemoryFunc * const omap_l4ta_writefn[] = { - omap_badwidth_write32, - omap_badwidth_write32, - omap_l4ta_write, -}; + return s; +} +/* L4 Interconnect */ #define L4TA(n) (n) #define L4TAO(n) ((n) + 39) -static struct omap_l4_region_s { - target_phys_addr_t offset; - size_t size; - int access; -} omap_l4_region[125] = { +static const struct omap_l4_region_s omap_l4_region[125] = { [ 1] = { 0x40800, 0x800, 32 }, /* Initiator agent */ [ 2] = { 0x41000, 0x1000, 32 }, /* Link agent */ [ 0] = { 0x40000, 0x800, 32 }, /* Address and protection */ @@ -2487,12 +926,7 @@ [124] = { 0xb3000, 0x1000, 32 | 16 | 8 }, /* L4TA39 */ }; -static struct omap_l4_agent_info_s { - int ta; - int region; - int regions; - int ta_region; -} omap_l4_agent_info[54] = { +static const struct omap_l4_agent_info_s omap_l4_agent_info[54] = { { 0, 0, 3, 2 }, /* L4IA initiatior agent */ { L4TAO(1), 3, 2, 1 }, /* Control and pinout module */ { L4TAO(2), 5, 2, 1 }, /* 32K timer */ @@ -2549,167 +983,10 @@ { L4TA(39), 123, 2, 1 }, /* HDQ/1-Wire */ }; -#define omap_l4ta(bus, cs) omap_l4ta_get(bus, L4TA(cs)) -#define omap_l4tao(bus, cs) omap_l4ta_get(bus, L4TAO(cs)) - -struct omap_target_agent_s *omap_l4ta_get(struct omap_l4_s *bus, int cs) -{ - int i, iomemtype; - struct omap_target_agent_s *ta = NULL; - struct omap_l4_agent_info_s *info = NULL; - - for (i = 0; i < bus->ta_num; i ++) - if (omap_l4_agent_info[i].ta == cs) { - ta = &bus->ta[i]; - info = &omap_l4_agent_info[i]; - break; - } - if (!ta) { - fprintf(stderr, "%s: bad target agent (%i)\n", __FUNCTION__, cs); - exit(-1); - } - - ta->bus = bus; - ta->start = &omap_l4_region[info->region]; - ta->regions = info->regions; - - ta->component = ('Q' << 24) | ('E' << 16) | ('M' << 8) | ('U' << 0); - ta->status = 0x00000000; - ta->control = 0x00000200; /* XXX 01000200 for L4TAO */ - - iomemtype = l4_register_io_memory(omap_l4ta_readfn, - omap_l4ta_writefn, ta); - ta->base = omap_l4_attach(ta, info->ta_region, iomemtype); - - return ta; -} - -target_phys_addr_t omap_l4_attach(struct omap_target_agent_s *ta, int region, - int iotype) -{ - target_phys_addr_t base; - ssize_t size; -#ifdef L4_MUX_HACK - int i; -#endif - - if (region < 0 || region >= ta->regions) { - fprintf(stderr, "%s: bad io region (%i)\n", __FUNCTION__, region); - exit(-1); - } - - base = ta->bus->base + ta->start[region].offset; - size = ta->start[region].size; - if (iotype) { -#ifndef L4_MUX_HACK - cpu_register_physical_memory(base, size, iotype); -#else - cpu_register_physical_memory(base, size, omap_cpu_io_entry); - i = (base - ta->bus->base) / TARGET_PAGE_SIZE; - for (; size > 0; size -= TARGET_PAGE_SIZE, i ++) { - omap_l4_io_readb_fn[i] = omap_l4_io_entry[iotype].mem_read[0]; - omap_l4_io_readh_fn[i] = omap_l4_io_entry[iotype].mem_read[1]; - omap_l4_io_readw_fn[i] = omap_l4_io_entry[iotype].mem_read[2]; - omap_l4_io_writeb_fn[i] = omap_l4_io_entry[iotype].mem_write[0]; - omap_l4_io_writeh_fn[i] = omap_l4_io_entry[iotype].mem_write[1]; - omap_l4_io_writew_fn[i] = omap_l4_io_entry[iotype].mem_write[2]; - omap_l4_io_opaque[i] = omap_l4_io_entry[iotype].opaque; - } -#endif - } - - return base; -} - -/* TEST-Chip-level TAP */ -static uint32_t omap_tap_read(void *opaque, target_phys_addr_t addr) -{ - struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque; - - switch (addr) { - case 0x204: /* IDCODE_reg */ - switch (s->mpu_model) { - case omap2420: - case omap2422: - case omap2423: - return 0x5b5d902f; /* ES 2.2 */ - case omap2430: - return 0x5b68a02f; /* ES 2.2 */ - case omap3430: - return 0x1b7ae02f; /* ES 2 */ - default: - hw_error("%s: Bad mpu model\n", __FUNCTION__); - } - - case 0x208: /* PRODUCTION_ID_reg for OMAP2 */ - case 0x210: /* PRODUCTION_ID_reg for OMAP3 */ - switch (s->mpu_model) { - case omap2420: - return 0x000254f0; /* POP ESHS2.1.1 in N91/93/95, ES2 in N800 */ - case omap2422: - return 0x000400f0; - case omap2423: - return 0x000800f0; - case omap2430: - return 0x000000f0; - case omap3430: - return 0x000000f0; - default: - hw_error("%s: Bad mpu model\n", __FUNCTION__); - } - - case 0x20c: - switch (s->mpu_model) { - case omap2420: - case omap2422: - case omap2423: - return 0xcafeb5d9; /* ES 2.2 */ - case omap2430: - return 0xcafeb68a; /* ES 2.2 */ - case omap3430: - return 0xcafeb7ae; /* ES 2 */ - default: - hw_error("%s: Bad mpu model\n", __FUNCTION__); - } - - case 0x218: /* DIE_ID_reg */ - return ('Q' << 24) | ('E' << 16) | ('M' << 8) | ('U' << 0); - case 0x21c: /* DIE_ID_reg */ - return 0x54 << 24; - case 0x220: /* DIE_ID_reg */ - return ('Q' << 24) | ('E' << 16) | ('M' << 8) | ('U' << 0); - case 0x224: /* DIE_ID_reg */ - return ('Q' << 24) | ('E' << 16) | ('M' << 8) | ('U' << 0); - } - - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_tap_write(void *opaque, target_phys_addr_t addr, - uint32_t value) -{ - OMAP_BAD_REG(addr); -} - -static CPUReadMemoryFunc * const omap_tap_readfn[] = { - omap_badwidth_read32, - omap_badwidth_read32, - omap_tap_read, -}; - -static CPUWriteMemoryFunc * const omap_tap_writefn[] = { - omap_badwidth_write32, - omap_badwidth_write32, - omap_tap_write, -}; - -void omap_tap_init(struct omap_target_agent_s *ta, - struct omap_mpu_state_s *mpu) -{ - omap_l4_attach(ta, 0, l4_register_io_memory( - omap_tap_readfn, omap_tap_writefn, mpu)); -} +#define omap_l4ta(bus, cs) \ + omap_l4ta_get(bus, omap_l4_region, omap_l4_agent_info, L4TA(cs)) +#define omap_l4tao(bus, cs) \ + omap_l4ta_get(bus, omap_l4_region, omap_l4_agent_info, L4TAO(cs)) /* Power, Reset, and Clock Management */ struct omap_prcm_s { @@ -3506,7 +1783,7 @@ omap_prcm_reset(s); } -struct omap_prcm_s *omap_prcm_init(struct omap_target_agent_s *ta, +static struct omap_prcm_s *omap_prcm_init(struct omap_target_agent_s *ta, qemu_irq mpu_int, qemu_irq dsp_int, qemu_irq iva_int, struct omap_mpu_state_s *mpu) { @@ -3880,7 +2157,7 @@ s->padconf[0x44] = 0x00000800; } -struct omap_sysctl_s *omap_sysctl_init(struct omap_target_agent_s *ta, +static struct omap_sysctl_s *omap_sysctl_init(struct omap_target_agent_s *ta, omap_clk iclk, struct omap_mpu_state_s *mpu) { int iomemtype; @@ -3897,546 +2174,6 @@ return s; } -/* SDRAM Controller Subsystem */ -struct omap_sdrc_s { - uint8_t config; -}; - -static void omap_sdrc_reset(struct omap_sdrc_s *s) -{ - s->config = 0x10; -} - -static uint32_t omap_sdrc_read(void *opaque, target_phys_addr_t addr) -{ - struct omap_sdrc_s *s = (struct omap_sdrc_s *) opaque; - - switch (addr) { - case 0x00: /* SDRC_REVISION */ - return 0x20; - - case 0x10: /* SDRC_SYSCONFIG */ - return s->config; - - case 0x14: /* SDRC_SYSSTATUS */ - return 1; /* RESETDONE */ - - case 0x40: /* SDRC_CS_CFG */ - case 0x44: /* SDRC_SHARING */ - case 0x48: /* SDRC_ERR_ADDR */ - case 0x4c: /* SDRC_ERR_TYPE */ - case 0x60: /* SDRC_DLLA_SCTRL */ - case 0x64: /* SDRC_DLLA_STATUS */ - case 0x68: /* SDRC_DLLB_CTRL */ - case 0x6c: /* SDRC_DLLB_STATUS */ - case 0x70: /* SDRC_POWER */ - case 0x80: /* SDRC_MCFG_0 */ - case 0x84: /* SDRC_MR_0 */ - case 0x88: /* SDRC_EMR1_0 */ - case 0x8c: /* SDRC_EMR2_0 */ - case 0x90: /* SDRC_EMR3_0 */ - case 0x94: /* SDRC_DCDL1_CTRL */ - case 0x98: /* SDRC_DCDL2_CTRL */ - case 0x9c: /* SDRC_ACTIM_CTRLA_0 */ - case 0xa0: /* SDRC_ACTIM_CTRLB_0 */ - case 0xa4: /* SDRC_RFR_CTRL_0 */ - case 0xa8: /* SDRC_MANUAL_0 */ - case 0xb0: /* SDRC_MCFG_1 */ - case 0xb4: /* SDRC_MR_1 */ - case 0xb8: /* SDRC_EMR1_1 */ - case 0xbc: /* SDRC_EMR2_1 */ - case 0xc0: /* SDRC_EMR3_1 */ - case 0xc4: /* SDRC_ACTIM_CTRLA_1 */ - case 0xc8: /* SDRC_ACTIM_CTRLB_1 */ - case 0xd4: /* SDRC_RFR_CTRL_1 */ - case 0xd8: /* SDRC_MANUAL_1 */ - return 0x00; - } - - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_sdrc_write(void *opaque, target_phys_addr_t addr, - uint32_t value) -{ - struct omap_sdrc_s *s = (struct omap_sdrc_s *) opaque; - - switch (addr) { - case 0x00: /* SDRC_REVISION */ - case 0x14: /* SDRC_SYSSTATUS */ - case 0x48: /* SDRC_ERR_ADDR */ - case 0x64: /* SDRC_DLLA_STATUS */ - case 0x6c: /* SDRC_DLLB_STATUS */ - OMAP_RO_REG(addr); - return; - - case 0x10: /* SDRC_SYSCONFIG */ - if ((value >> 3) != 0x2) - fprintf(stderr, "%s: bad SDRAM idle mode %i\n", - __FUNCTION__, value >> 3); - if (value & 2) - omap_sdrc_reset(s); - s->config = value & 0x18; - break; - - case 0x40: /* SDRC_CS_CFG */ - case 0x44: /* SDRC_SHARING */ - case 0x4c: /* SDRC_ERR_TYPE */ - case 0x60: /* SDRC_DLLA_SCTRL */ - case 0x68: /* SDRC_DLLB_CTRL */ - case 0x70: /* SDRC_POWER */ - case 0x80: /* SDRC_MCFG_0 */ - case 0x84: /* SDRC_MR_0 */ - case 0x88: /* SDRC_EMR1_0 */ - case 0x8c: /* SDRC_EMR2_0 */ - case 0x90: /* SDRC_EMR3_0 */ - case 0x94: /* SDRC_DCDL1_CTRL */ - case 0x98: /* SDRC_DCDL2_CTRL */ - case 0x9c: /* SDRC_ACTIM_CTRLA_0 */ - case 0xa0: /* SDRC_ACTIM_CTRLB_0 */ - case 0xa4: /* SDRC_RFR_CTRL_0 */ - case 0xa8: /* SDRC_MANUAL_0 */ - case 0xb0: /* SDRC_MCFG_1 */ - case 0xb4: /* SDRC_MR_1 */ - case 0xb8: /* SDRC_EMR1_1 */ - case 0xbc: /* SDRC_EMR2_1 */ - case 0xc0: /* SDRC_EMR3_1 */ - case 0xc4: /* SDRC_ACTIM_CTRLA_1 */ - case 0xc8: /* SDRC_ACTIM_CTRLB_1 */ - case 0xd4: /* SDRC_RFR_CTRL_1 */ - case 0xd8: /* SDRC_MANUAL_1 */ - break; - - default: - OMAP_BAD_REG(addr); - return; - } -} - -static CPUReadMemoryFunc * const omap_sdrc_readfn[] = { - omap_badwidth_read32, - omap_badwidth_read32, - omap_sdrc_read, -}; - -static CPUWriteMemoryFunc * const omap_sdrc_writefn[] = { - omap_badwidth_write32, - omap_badwidth_write32, - omap_sdrc_write, -}; - -struct omap_sdrc_s *omap_sdrc_init(target_phys_addr_t base) -{ - int iomemtype; - struct omap_sdrc_s *s = (struct omap_sdrc_s *) - qemu_mallocz(sizeof(struct omap_sdrc_s)); - - omap_sdrc_reset(s); - - iomemtype = cpu_register_io_memory(omap_sdrc_readfn, - omap_sdrc_writefn, s); - cpu_register_physical_memory(base, 0x1000, iomemtype); - - return s; -} - -/* General-Purpose Memory Controller */ -struct omap_gpmc_s { - qemu_irq irq; - - uint8_t sysconfig; - uint16_t irqst; - uint16_t irqen; - uint16_t timeout; - uint16_t config; - uint32_t prefconfig[2]; - int prefcontrol; - int preffifo; - int prefcount; - struct omap_gpmc_cs_file_s { - uint32_t config[7]; - target_phys_addr_t base; - size_t size; - int iomemtype; - void (*base_update)(void *opaque, target_phys_addr_t new); - void (*unmap)(void *opaque); - void *opaque; - } cs_file[8]; - int ecc_cs; - int ecc_ptr; - uint32_t ecc_cfg; - ECCState ecc[9]; -}; - -static void omap_gpmc_int_update(struct omap_gpmc_s *s) -{ - qemu_set_irq(s->irq, s->irqen & s->irqst); -} - -static void omap_gpmc_cs_map(struct omap_gpmc_cs_file_s *f, int base, int mask) -{ - /* TODO: check for overlapping regions and report access errors */ - if ((mask != 0x8 && mask != 0xc && mask != 0xe && mask != 0xf) || - (base < 0 || base >= 0x40) || - (base & 0x0f & ~mask)) { - fprintf(stderr, "%s: wrong cs address mapping/decoding!\n", - __FUNCTION__); - return; - } - - if (!f->opaque) - return; - - f->base = base << 24; - f->size = (0x0fffffff & ~(mask << 24)) + 1; - /* TODO: rather than setting the size of the mapping (which should be - * constant), the mask should cause wrapping of the address space, so - * that the same memory becomes accessible at every size bytes - * starting from base. */ - if (f->iomemtype) - cpu_register_physical_memory(f->base, f->size, f->iomemtype); - - if (f->base_update) - f->base_update(f->opaque, f->base); -} - -static void omap_gpmc_cs_unmap(struct omap_gpmc_cs_file_s *f) -{ - if (f->size) { - if (f->unmap) - f->unmap(f->opaque); - if (f->iomemtype) - cpu_register_physical_memory(f->base, f->size, IO_MEM_UNASSIGNED); - f->base = 0; - f->size = 0; - } -} - -static void omap_gpmc_reset(struct omap_gpmc_s *s) -{ - int i; - - s->sysconfig = 0; - s->irqst = 0; - s->irqen = 0; - omap_gpmc_int_update(s); - s->timeout = 0; - s->config = 0xa00; - s->prefconfig[0] = 0x00004000; - s->prefconfig[1] = 0x00000000; - s->prefcontrol = 0; - s->preffifo = 0; - s->prefcount = 0; - for (i = 0; i < 8; i ++) { - if (s->cs_file[i].config[6] & (1 << 6)) /* CSVALID */ - omap_gpmc_cs_unmap(s->cs_file + i); - s->cs_file[i].config[0] = i ? 1 << 12 : 0; - s->cs_file[i].config[1] = 0x101001; - s->cs_file[i].config[2] = 0x020201; - s->cs_file[i].config[3] = 0x10031003; - s->cs_file[i].config[4] = 0x10f1111; - s->cs_file[i].config[5] = 0; - s->cs_file[i].config[6] = 0xf00 | (i ? 0 : 1 << 6); - if (s->cs_file[i].config[6] & (1 << 6)) /* CSVALID */ - omap_gpmc_cs_map(&s->cs_file[i], - s->cs_file[i].config[6] & 0x1f, /* MASKADDR */ - (s->cs_file[i].config[6] >> 8 & 0xf)); /* BASEADDR */ - } - omap_gpmc_cs_map(s->cs_file, 0, 0xf); - s->ecc_cs = 0; - s->ecc_ptr = 0; - s->ecc_cfg = 0x3fcff000; - for (i = 0; i < 9; i ++) - ecc_reset(&s->ecc[i]); -} - -static uint32_t omap_gpmc_read(void *opaque, target_phys_addr_t addr) -{ - struct omap_gpmc_s *s = (struct omap_gpmc_s *) opaque; - int cs; - struct omap_gpmc_cs_file_s *f; - - switch (addr) { - case 0x000: /* GPMC_REVISION */ - return 0x20; - - case 0x010: /* GPMC_SYSCONFIG */ - return s->sysconfig; - - case 0x014: /* GPMC_SYSSTATUS */ - return 1; /* RESETDONE */ - - case 0x018: /* GPMC_IRQSTATUS */ - return s->irqst; - - case 0x01c: /* GPMC_IRQENABLE */ - return s->irqen; - - case 0x040: /* GPMC_TIMEOUT_CONTROL */ - return s->timeout; - - case 0x044: /* GPMC_ERR_ADDRESS */ - case 0x048: /* GPMC_ERR_TYPE */ - return 0; - - case 0x050: /* GPMC_CONFIG */ - return s->config; - - case 0x054: /* GPMC_STATUS */ - return 0x001; - - case 0x060 ... 0x1d4: - cs = (addr - 0x060) / 0x30; - addr -= cs * 0x30; - f = s->cs_file + cs; - switch (addr) { - case 0x60: /* GPMC_CONFIG1 */ - return f->config[0]; - case 0x64: /* GPMC_CONFIG2 */ - return f->config[1]; - case 0x68: /* GPMC_CONFIG3 */ - return f->config[2]; - case 0x6c: /* GPMC_CONFIG4 */ - return f->config[3]; - case 0x70: /* GPMC_CONFIG5 */ - return f->config[4]; - case 0x74: /* GPMC_CONFIG6 */ - return f->config[5]; - case 0x78: /* GPMC_CONFIG7 */ - return f->config[6]; - case 0x84: /* GPMC_NAND_DATA */ - return 0; - } - break; - - case 0x1e0: /* GPMC_PREFETCH_CONFIG1 */ - return s->prefconfig[0]; - case 0x1e4: /* GPMC_PREFETCH_CONFIG2 */ - return s->prefconfig[1]; - case 0x1ec: /* GPMC_PREFETCH_CONTROL */ - return s->prefcontrol; - case 0x1f0: /* GPMC_PREFETCH_STATUS */ - return (s->preffifo << 24) | - ((s->preffifo > - ((s->prefconfig[0] >> 8) & 0x7f) ? 1 : 0) << 16) | - s->prefcount; - - case 0x1f4: /* GPMC_ECC_CONFIG */ - return s->ecc_cs; - case 0x1f8: /* GPMC_ECC_CONTROL */ - return s->ecc_ptr; - case 0x1fc: /* GPMC_ECC_SIZE_CONFIG */ - return s->ecc_cfg; - case 0x200 ... 0x220: /* GPMC_ECC_RESULT */ - cs = (addr & 0x1f) >> 2; - /* TODO: check correctness */ - return - ((s->ecc[cs].cp & 0x07) << 0) | - ((s->ecc[cs].cp & 0x38) << 13) | - ((s->ecc[cs].lp[0] & 0x1ff) << 3) | - ((s->ecc[cs].lp[1] & 0x1ff) << 19); - - case 0x230: /* GPMC_TESTMODE_CTRL */ - return 0; - case 0x234: /* GPMC_PSA_LSB */ - case 0x238: /* GPMC_PSA_MSB */ - return 0x00000000; - } - - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_gpmc_write(void *opaque, target_phys_addr_t addr, - uint32_t value) -{ - struct omap_gpmc_s *s = (struct omap_gpmc_s *) opaque; - int cs; - struct omap_gpmc_cs_file_s *f; - - switch (addr) { - case 0x000: /* GPMC_REVISION */ - case 0x014: /* GPMC_SYSSTATUS */ - case 0x054: /* GPMC_STATUS */ - case 0x1f0: /* GPMC_PREFETCH_STATUS */ - case 0x200 ... 0x220: /* GPMC_ECC_RESULT */ - case 0x234: /* GPMC_PSA_LSB */ - case 0x238: /* GPMC_PSA_MSB */ - OMAP_RO_REG(addr); - break; - - case 0x010: /* GPMC_SYSCONFIG */ - if ((value >> 3) == 0x3) - fprintf(stderr, "%s: bad SDRAM idle mode %i\n", - __FUNCTION__, value >> 3); - if (value & 2) - omap_gpmc_reset(s); - s->sysconfig = value & 0x19; - break; - - case 0x018: /* GPMC_IRQSTATUS */ - s->irqen = ~value; - omap_gpmc_int_update(s); - break; - - case 0x01c: /* GPMC_IRQENABLE */ - s->irqen = value & 0xf03; - omap_gpmc_int_update(s); - break; - - case 0x040: /* GPMC_TIMEOUT_CONTROL */ - s->timeout = value & 0x1ff1; - break; - - case 0x044: /* GPMC_ERR_ADDRESS */ - case 0x048: /* GPMC_ERR_TYPE */ - break; - - case 0x050: /* GPMC_CONFIG */ - s->config = value & 0xf13; - break; - - case 0x060 ... 0x1d4: - cs = (addr - 0x060) / 0x30; - addr -= cs * 0x30; - f = s->cs_file + cs; - switch (addr) { - case 0x60: /* GPMC_CONFIG1 */ - f->config[0] = value & 0xffef3e13; - break; - case 0x64: /* GPMC_CONFIG2 */ - f->config[1] = value & 0x001f1f8f; - break; - case 0x68: /* GPMC_CONFIG3 */ - f->config[2] = value & 0x001f1f8f; - break; - case 0x6c: /* GPMC_CONFIG4 */ - f->config[3] = value & 0x1f8f1f8f; - break; - case 0x70: /* GPMC_CONFIG5 */ - f->config[4] = value & 0x0f1f1f1f; - break; - case 0x74: /* GPMC_CONFIG6 */ - f->config[5] = value & 0x00000fcf; - break; - case 0x78: /* GPMC_CONFIG7 */ - if ((f->config[6] ^ value) & 0xf7f) { - if (f->config[6] & (1 << 6)) /* CSVALID */ - omap_gpmc_cs_unmap(f); - if (value & (1 << 6)) /* CSVALID */ - omap_gpmc_cs_map(f, value & 0x1f, /* MASKADDR */ - (value >> 8 & 0xf)); /* BASEADDR */ - } - f->config[6] = value & 0x00000f7f; - break; - case 0x7c: /* GPMC_NAND_COMMAND */ - case 0x80: /* GPMC_NAND_ADDRESS */ - case 0x84: /* GPMC_NAND_DATA */ - break; - - default: - goto bad_reg; - } - break; - - case 0x1e0: /* GPMC_PREFETCH_CONFIG1 */ - s->prefconfig[0] = value & 0x7f8f7fbf; - /* TODO: update interrupts, fifos, dmas */ - break; - - case 0x1e4: /* GPMC_PREFETCH_CONFIG2 */ - s->prefconfig[1] = value & 0x3fff; - break; - - case 0x1ec: /* GPMC_PREFETCH_CONTROL */ - s->prefcontrol = value & 1; - if (s->prefcontrol) { - if (s->prefconfig[0] & 1) - s->preffifo = 0x40; - else - s->preffifo = 0x00; - } - /* TODO: start */ - break; - - case 0x1f4: /* GPMC_ECC_CONFIG */ - s->ecc_cs = 0x8f; - break; - case 0x1f8: /* GPMC_ECC_CONTROL */ - if (value & (1 << 8)) - for (cs = 0; cs < 9; cs ++) - ecc_reset(&s->ecc[cs]); - s->ecc_ptr = value & 0xf; - if (s->ecc_ptr == 0 || s->ecc_ptr > 9) { - s->ecc_ptr = 0; - s->ecc_cs &= ~1; - } - break; - case 0x1fc: /* GPMC_ECC_SIZE_CONFIG */ - s->ecc_cfg = value & 0x3fcff1ff; - break; - case 0x230: /* GPMC_TESTMODE_CTRL */ - if (value & 7) - fprintf(stderr, "%s: test mode enable attempt\n", __FUNCTION__); - break; - - default: - bad_reg: - OMAP_BAD_REG(addr); - return; - } -} - -static CPUReadMemoryFunc * const omap_gpmc_readfn[] = { - omap_badwidth_read32, /* TODO */ - omap_badwidth_read32, /* TODO */ - omap_gpmc_read, -}; - -static CPUWriteMemoryFunc * const omap_gpmc_writefn[] = { - omap_badwidth_write32, /* TODO */ - omap_badwidth_write32, /* TODO */ - omap_gpmc_write, -}; - -struct omap_gpmc_s *omap_gpmc_init(target_phys_addr_t base, qemu_irq irq) -{ - int iomemtype; - struct omap_gpmc_s *s = (struct omap_gpmc_s *) - qemu_mallocz(sizeof(struct omap_gpmc_s)); - - omap_gpmc_reset(s); - - iomemtype = cpu_register_io_memory(omap_gpmc_readfn, - omap_gpmc_writefn, s); - cpu_register_physical_memory(base, 0x1000, iomemtype); - - return s; -} - -void omap_gpmc_attach(struct omap_gpmc_s *s, int cs, int iomemtype, - void (*base_upd)(void *opaque, target_phys_addr_t new), - void (*unmap)(void *opaque), void *opaque) -{ - struct omap_gpmc_cs_file_s *f; - - if (cs < 0 || cs >= 8) { - fprintf(stderr, "%s: bad chip-select %i\n", __FUNCTION__, cs); - exit(-1); - } - f = &s->cs_file[cs]; - - f->iomemtype = iomemtype; - f->base_update = base_upd; - f->unmap = unmap; - f->opaque = opaque; - - if (f->config[6] & (1 << 6)) /* CSVALID */ - omap_gpmc_cs_map(f, f->config[6] & 0x1f, /* MASKADDR */ - (f->config[6] >> 8 & 0xf)); /* BASEADDR */ -} - /* General chip reset */ static void omap2_mpu_reset(void *opaque) { @@ -4458,7 +2195,7 @@ omap_gp_timer_reset(mpu->gptimer[9]); omap_gp_timer_reset(mpu->gptimer[10]); omap_gp_timer_reset(mpu->gptimer[11]); - omap_synctimer_reset(&mpu->synctimer); + omap_synctimer_reset(mpu->synctimer); omap_sdrc_reset(mpu->sdrc); omap_gpmc_reset(mpu->gpmc); omap_dss_reset(mpu->dss); @@ -4516,9 +2253,11 @@ /* Memory-mapped stuff */ cpu_register_physical_memory(OMAP2_Q2_BASE, s->sdram_size, - (q2_base = qemu_ram_alloc(s->sdram_size)) | IO_MEM_RAM); + (q2_base = qemu_ram_alloc(NULL, "omap2.dram", + s->sdram_size)) | IO_MEM_RAM); cpu_register_physical_memory(OMAP2_SRAM_BASE, s->sram_size, - (sram_base = qemu_ram_alloc(s->sram_size)) | IO_MEM_RAM); + (sram_base = qemu_ram_alloc(NULL, "omap2.sram", + s->sram_size)) | IO_MEM_RAM); s->l4 = omap_l4_init(OMAP2_L4_BASE, 54); @@ -4552,13 +2291,16 @@ omap_findclk(s, "uart1_fclk"), omap_findclk(s, "uart1_iclk"), s->drq[OMAP24XX_DMA_UART1_TX], - s->drq[OMAP24XX_DMA_UART1_RX], serial_hds[0]); + s->drq[OMAP24XX_DMA_UART1_RX], + "uart1", + serial_hds[0]); s->uart[1] = omap2_uart_init(omap_l4ta(s->l4, 20), s->irq[0][OMAP_INT_24XX_UART2_IRQ], omap_findclk(s, "uart2_fclk"), omap_findclk(s, "uart2_iclk"), s->drq[OMAP24XX_DMA_UART2_TX], s->drq[OMAP24XX_DMA_UART2_RX], + "uart2", serial_hds[0] ? serial_hds[1] : NULL); s->uart[2] = omap2_uart_init(omap_l4ta(s->l4, 21), s->irq[0][OMAP_INT_24XX_UART3_IRQ], @@ -4566,6 +2308,7 @@ omap_findclk(s, "uart3_iclk"), s->drq[OMAP24XX_DMA_UART3_TX], s->drq[OMAP24XX_DMA_UART3_RX], + "uart3", serial_hds[0] && serial_hds[1] ? serial_hds[2] : NULL); s->gptimer[0] = omap_gp_timer_init(omap_l4ta(s->l4, 7), @@ -4619,7 +2362,7 @@ omap_tap_init(omap_l4ta(s->l4, 2), s); - omap_synctimer_init(omap_l4tao(s->l4, 2), s, + s->synctimer = omap_synctimer_init(omap_l4tao(s->l4, 2), s, omap_findclk(s, "clk32-kHz"), omap_findclk(s, "core_l4_iclk")); diff -Nru qemu-kvm-0.12.5+noroms/hw/omap_dma.c qemu-kvm-0.14.1/hw/omap_dma.c --- qemu-kvm-0.12.5+noroms/hw/omap_dma.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/omap_dma.c 2011-05-11 13:29:46.000000000 +0000 @@ -1659,7 +1659,7 @@ omap_dma_clk_update(s, 0, 1); iomemtype = cpu_register_io_memory(omap_dma_readfn, - omap_dma_writefn, s); + omap_dma_writefn, s, DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base, memsize, iomemtype); mpu->drq = s->dma->drq; @@ -2066,7 +2066,7 @@ omap_dma_clk_update(s, 0, !!s->dma->freq); iomemtype = cpu_register_io_memory(omap_dma4_readfn, - omap_dma4_writefn, s); + omap_dma4_writefn, s, DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base, 0x1000, iomemtype); mpu->drq = s->dma->drq; diff -Nru qemu-kvm-0.12.5+noroms/hw/omap_dss.c qemu-kvm-0.14.1/hw/omap_dss.c --- qemu-kvm-0.12.5+noroms/hw/omap_dss.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/omap_dss.c 2011-05-11 13:29:46.000000000 +0000 @@ -1045,7 +1045,7 @@ iomemtype[3] = l4_register_io_memory(omap_venc1_readfn, omap_venc1_writefn, s); iomemtype[4] = cpu_register_io_memory(omap_im3_readfn, - omap_im3_writefn, s); + omap_im3_writefn, s, DEVICE_NATIVE_ENDIAN); omap_l4_attach(ta, 0, iomemtype[0]); omap_l4_attach(ta, 1, iomemtype[1]); omap_l4_attach(ta, 2, iomemtype[2]); diff -Nru qemu-kvm-0.12.5+noroms/hw/omap_gpio.c qemu-kvm-0.14.1/hw/omap_gpio.c --- qemu-kvm-0.12.5+noroms/hw/omap_gpio.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/omap_gpio.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,725 @@ +/* + * TI OMAP processors GPIO emulation. + * + * Copyright (C) 2006-2008 Andrzej Zaborowski + * Copyright (C) 2007-2009 Nokia Corporation + * + * 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 or + * (at your option) version 3 of the License. + * + * 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, see . + */ + +#include "hw.h" +#include "omap.h" +/* General-Purpose I/O */ +struct omap_gpio_s { + qemu_irq irq; + qemu_irq *in; + qemu_irq handler[16]; + + uint16_t inputs; + uint16_t outputs; + uint16_t dir; + uint16_t edge; + uint16_t mask; + uint16_t ints; + uint16_t pins; +}; + +static void omap_gpio_set(void *opaque, int line, int level) +{ + struct omap_gpio_s *s = (struct omap_gpio_s *) opaque; + uint16_t prev = s->inputs; + + if (level) + s->inputs |= 1 << line; + else + s->inputs &= ~(1 << line); + + if (((s->edge & s->inputs & ~prev) | (~s->edge & ~s->inputs & prev)) & + (1 << line) & s->dir & ~s->mask) { + s->ints |= 1 << line; + qemu_irq_raise(s->irq); + } +} + +static uint32_t omap_gpio_read(void *opaque, target_phys_addr_t addr) +{ + struct omap_gpio_s *s = (struct omap_gpio_s *) opaque; + int offset = addr & OMAP_MPUI_REG_MASK; + + switch (offset) { + case 0x00: /* DATA_INPUT */ + return s->inputs & s->pins; + + case 0x04: /* DATA_OUTPUT */ + return s->outputs; + + case 0x08: /* DIRECTION_CONTROL */ + return s->dir; + + case 0x0c: /* INTERRUPT_CONTROL */ + return s->edge; + + case 0x10: /* INTERRUPT_MASK */ + return s->mask; + + case 0x14: /* INTERRUPT_STATUS */ + return s->ints; + + case 0x18: /* PIN_CONTROL (not in OMAP310) */ + OMAP_BAD_REG(addr); + return s->pins; + } + + OMAP_BAD_REG(addr); + return 0; +} + +static void omap_gpio_write(void *opaque, target_phys_addr_t addr, + uint32_t value) +{ + struct omap_gpio_s *s = (struct omap_gpio_s *) opaque; + int offset = addr & OMAP_MPUI_REG_MASK; + uint16_t diff; + int ln; + + switch (offset) { + case 0x00: /* DATA_INPUT */ + OMAP_RO_REG(addr); + return; + + case 0x04: /* DATA_OUTPUT */ + diff = (s->outputs ^ value) & ~s->dir; + s->outputs = value; + while ((ln = ffs(diff))) { + ln --; + if (s->handler[ln]) + qemu_set_irq(s->handler[ln], (value >> ln) & 1); + diff &= ~(1 << ln); + } + break; + + case 0x08: /* DIRECTION_CONTROL */ + diff = s->outputs & (s->dir ^ value); + s->dir = value; + + value = s->outputs & ~s->dir; + while ((ln = ffs(diff))) { + ln --; + if (s->handler[ln]) + qemu_set_irq(s->handler[ln], (value >> ln) & 1); + diff &= ~(1 << ln); + } + break; + + case 0x0c: /* INTERRUPT_CONTROL */ + s->edge = value; + break; + + case 0x10: /* INTERRUPT_MASK */ + s->mask = value; + break; + + case 0x14: /* INTERRUPT_STATUS */ + s->ints &= ~value; + if (!s->ints) + qemu_irq_lower(s->irq); + break; + + case 0x18: /* PIN_CONTROL (not in OMAP310 TRM) */ + OMAP_BAD_REG(addr); + s->pins = value; + break; + + default: + OMAP_BAD_REG(addr); + return; + } +} + +/* *Some* sources say the memory region is 32-bit. */ +static CPUReadMemoryFunc * const omap_gpio_readfn[] = { + omap_badwidth_read16, + omap_gpio_read, + omap_badwidth_read16, +}; + +static CPUWriteMemoryFunc * const omap_gpio_writefn[] = { + omap_badwidth_write16, + omap_gpio_write, + omap_badwidth_write16, +}; + +void omap_gpio_reset(struct omap_gpio_s *s) +{ + s->inputs = 0; + s->outputs = ~0; + s->dir = ~0; + s->edge = ~0; + s->mask = ~0; + s->ints = 0; + s->pins = ~0; +} + +struct omap_gpio_s *omap_gpio_init(target_phys_addr_t base, + qemu_irq irq, omap_clk clk) +{ + int iomemtype; + struct omap_gpio_s *s = (struct omap_gpio_s *) + qemu_mallocz(sizeof(struct omap_gpio_s)); + + s->irq = irq; + s->in = qemu_allocate_irqs(omap_gpio_set, s, 16); + omap_gpio_reset(s); + + iomemtype = cpu_register_io_memory(omap_gpio_readfn, + omap_gpio_writefn, s, DEVICE_NATIVE_ENDIAN); + cpu_register_physical_memory(base, 0x1000, iomemtype); + + return s; +} + +qemu_irq *omap_gpio_in_get(struct omap_gpio_s *s) +{ + return s->in; +} + +void omap_gpio_out_set(struct omap_gpio_s *s, int line, qemu_irq handler) +{ + if (line >= 16 || line < 0) + hw_error("%s: No GPIO line %i\n", __FUNCTION__, line); + s->handler[line] = handler; +} + +/* General-Purpose Interface of OMAP2 */ +struct omap2_gpio_s { + qemu_irq irq[2]; + qemu_irq wkup; + qemu_irq *in; + qemu_irq handler[32]; + + uint8_t config[2]; + uint32_t inputs; + uint32_t outputs; + uint32_t dir; + uint32_t level[2]; + uint32_t edge[2]; + uint32_t mask[2]; + uint32_t wumask; + uint32_t ints[2]; + uint32_t debounce; + uint8_t delay; +}; + +static inline void omap2_gpio_module_int_update(struct omap2_gpio_s *s, + int line) +{ + qemu_set_irq(s->irq[line], s->ints[line] & s->mask[line]); +} + +static void omap2_gpio_module_wake(struct omap2_gpio_s *s, int line) +{ + if (!(s->config[0] & (1 << 2))) /* ENAWAKEUP */ + return; + if (!(s->config[0] & (3 << 3))) /* Force Idle */ + return; + if (!(s->wumask & (1 << line))) + return; + + qemu_irq_raise(s->wkup); +} + +static inline void omap2_gpio_module_out_update(struct omap2_gpio_s *s, + uint32_t diff) +{ + int ln; + + s->outputs ^= diff; + diff &= ~s->dir; + while ((ln = ffs(diff))) { + ln --; + qemu_set_irq(s->handler[ln], (s->outputs >> ln) & 1); + diff &= ~(1 << ln); + } +} + +static void omap2_gpio_module_level_update(struct omap2_gpio_s *s, int line) +{ + s->ints[line] |= s->dir & + ((s->inputs & s->level[1]) | (~s->inputs & s->level[0])); + omap2_gpio_module_int_update(s, line); +} + +static inline void omap2_gpio_module_int(struct omap2_gpio_s *s, int line) +{ + s->ints[0] |= 1 << line; + omap2_gpio_module_int_update(s, 0); + s->ints[1] |= 1 << line; + omap2_gpio_module_int_update(s, 1); + omap2_gpio_module_wake(s, line); +} + +static void omap2_gpio_module_set(void *opaque, int line, int level) +{ + struct omap2_gpio_s *s = (struct omap2_gpio_s *) opaque; + + if (level) { + if (s->dir & (1 << line) & ((~s->inputs & s->edge[0]) | s->level[1])) + omap2_gpio_module_int(s, line); + s->inputs |= 1 << line; + } else { + if (s->dir & (1 << line) & ((s->inputs & s->edge[1]) | s->level[0])) + omap2_gpio_module_int(s, line); + s->inputs &= ~(1 << line); + } +} + +static void omap2_gpio_module_reset(struct omap2_gpio_s *s) +{ + s->config[0] = 0; + s->config[1] = 2; + s->ints[0] = 0; + s->ints[1] = 0; + s->mask[0] = 0; + s->mask[1] = 0; + s->wumask = 0; + s->dir = ~0; + s->level[0] = 0; + s->level[1] = 0; + s->edge[0] = 0; + s->edge[1] = 0; + s->debounce = 0; + s->delay = 0; +} + +static uint32_t omap2_gpio_module_read(void *opaque, target_phys_addr_t addr) +{ + struct omap2_gpio_s *s = (struct omap2_gpio_s *) opaque; + + switch (addr) { + case 0x00: /* GPIO_REVISION */ + return 0x18; + + case 0x10: /* GPIO_SYSCONFIG */ + return s->config[0]; + + case 0x14: /* GPIO_SYSSTATUS */ + return 0x01; + + case 0x18: /* GPIO_IRQSTATUS1 */ + return s->ints[0]; + + case 0x1c: /* GPIO_IRQENABLE1 */ + case 0x60: /* GPIO_CLEARIRQENABLE1 */ + case 0x64: /* GPIO_SETIRQENABLE1 */ + return s->mask[0]; + + case 0x20: /* GPIO_WAKEUPENABLE */ + case 0x80: /* GPIO_CLEARWKUENA */ + case 0x84: /* GPIO_SETWKUENA */ + return s->wumask; + + case 0x28: /* GPIO_IRQSTATUS2 */ + return s->ints[1]; + + case 0x2c: /* GPIO_IRQENABLE2 */ + case 0x70: /* GPIO_CLEARIRQENABLE2 */ + case 0x74: /* GPIO_SETIREQNEABLE2 */ + return s->mask[1]; + + case 0x30: /* GPIO_CTRL */ + return s->config[1]; + + case 0x34: /* GPIO_OE */ + return s->dir; + + case 0x38: /* GPIO_DATAIN */ + return s->inputs; + + case 0x3c: /* GPIO_DATAOUT */ + case 0x90: /* GPIO_CLEARDATAOUT */ + case 0x94: /* GPIO_SETDATAOUT */ + return s->outputs; + + case 0x40: /* GPIO_LEVELDETECT0 */ + return s->level[0]; + + case 0x44: /* GPIO_LEVELDETECT1 */ + return s->level[1]; + + case 0x48: /* GPIO_RISINGDETECT */ + return s->edge[0]; + + case 0x4c: /* GPIO_FALLINGDETECT */ + return s->edge[1]; + + case 0x50: /* GPIO_DEBOUNCENABLE */ + return s->debounce; + + case 0x54: /* GPIO_DEBOUNCINGTIME */ + return s->delay; + } + + OMAP_BAD_REG(addr); + return 0; +} + +static void omap2_gpio_module_write(void *opaque, target_phys_addr_t addr, + uint32_t value) +{ + struct omap2_gpio_s *s = (struct omap2_gpio_s *) opaque; + uint32_t diff; + int ln; + + switch (addr) { + case 0x00: /* GPIO_REVISION */ + case 0x14: /* GPIO_SYSSTATUS */ + case 0x38: /* GPIO_DATAIN */ + OMAP_RO_REG(addr); + break; + + case 0x10: /* GPIO_SYSCONFIG */ + if (((value >> 3) & 3) == 3) + fprintf(stderr, "%s: bad IDLEMODE value\n", __FUNCTION__); + if (value & 2) + omap2_gpio_module_reset(s); + s->config[0] = value & 0x1d; + break; + + case 0x18: /* GPIO_IRQSTATUS1 */ + if (s->ints[0] & value) { + s->ints[0] &= ~value; + omap2_gpio_module_level_update(s, 0); + } + break; + + case 0x1c: /* GPIO_IRQENABLE1 */ + s->mask[0] = value; + omap2_gpio_module_int_update(s, 0); + break; + + case 0x20: /* GPIO_WAKEUPENABLE */ + s->wumask = value; + break; + + case 0x28: /* GPIO_IRQSTATUS2 */ + if (s->ints[1] & value) { + s->ints[1] &= ~value; + omap2_gpio_module_level_update(s, 1); + } + break; + + case 0x2c: /* GPIO_IRQENABLE2 */ + s->mask[1] = value; + omap2_gpio_module_int_update(s, 1); + break; + + case 0x30: /* GPIO_CTRL */ + s->config[1] = value & 7; + break; + + case 0x34: /* GPIO_OE */ + diff = s->outputs & (s->dir ^ value); + s->dir = value; + + value = s->outputs & ~s->dir; + while ((ln = ffs(diff))) { + diff &= ~(1 <<-- ln); + qemu_set_irq(s->handler[ln], (value >> ln) & 1); + } + + omap2_gpio_module_level_update(s, 0); + omap2_gpio_module_level_update(s, 1); + break; + + case 0x3c: /* GPIO_DATAOUT */ + omap2_gpio_module_out_update(s, s->outputs ^ value); + break; + + case 0x40: /* GPIO_LEVELDETECT0 */ + s->level[0] = value; + omap2_gpio_module_level_update(s, 0); + omap2_gpio_module_level_update(s, 1); + break; + + case 0x44: /* GPIO_LEVELDETECT1 */ + s->level[1] = value; + omap2_gpio_module_level_update(s, 0); + omap2_gpio_module_level_update(s, 1); + break; + + case 0x48: /* GPIO_RISINGDETECT */ + s->edge[0] = value; + break; + + case 0x4c: /* GPIO_FALLINGDETECT */ + s->edge[1] = value; + break; + + case 0x50: /* GPIO_DEBOUNCENABLE */ + s->debounce = value; + break; + + case 0x54: /* GPIO_DEBOUNCINGTIME */ + s->delay = value; + break; + + case 0x60: /* GPIO_CLEARIRQENABLE1 */ + s->mask[0] &= ~value; + omap2_gpio_module_int_update(s, 0); + break; + + case 0x64: /* GPIO_SETIRQENABLE1 */ + s->mask[0] |= value; + omap2_gpio_module_int_update(s, 0); + break; + + case 0x70: /* GPIO_CLEARIRQENABLE2 */ + s->mask[1] &= ~value; + omap2_gpio_module_int_update(s, 1); + break; + + case 0x74: /* GPIO_SETIREQNEABLE2 */ + s->mask[1] |= value; + omap2_gpio_module_int_update(s, 1); + break; + + case 0x80: /* GPIO_CLEARWKUENA */ + s->wumask &= ~value; + break; + + case 0x84: /* GPIO_SETWKUENA */ + s->wumask |= value; + break; + + case 0x90: /* GPIO_CLEARDATAOUT */ + omap2_gpio_module_out_update(s, s->outputs & value); + break; + + case 0x94: /* GPIO_SETDATAOUT */ + omap2_gpio_module_out_update(s, ~s->outputs & value); + break; + + default: + OMAP_BAD_REG(addr); + return; + } +} + +static uint32_t omap2_gpio_module_readp(void *opaque, target_phys_addr_t addr) +{ + return omap2_gpio_module_readp(opaque, addr) >> ((addr & 3) << 3); +} + +static void omap2_gpio_module_writep(void *opaque, target_phys_addr_t addr, + uint32_t value) +{ + uint32_t cur = 0; + uint32_t mask = 0xffff; + + switch (addr & ~3) { + case 0x00: /* GPIO_REVISION */ + case 0x14: /* GPIO_SYSSTATUS */ + case 0x38: /* GPIO_DATAIN */ + OMAP_RO_REG(addr); + break; + + case 0x10: /* GPIO_SYSCONFIG */ + case 0x1c: /* GPIO_IRQENABLE1 */ + case 0x20: /* GPIO_WAKEUPENABLE */ + case 0x2c: /* GPIO_IRQENABLE2 */ + case 0x30: /* GPIO_CTRL */ + case 0x34: /* GPIO_OE */ + case 0x3c: /* GPIO_DATAOUT */ + case 0x40: /* GPIO_LEVELDETECT0 */ + case 0x44: /* GPIO_LEVELDETECT1 */ + case 0x48: /* GPIO_RISINGDETECT */ + case 0x4c: /* GPIO_FALLINGDETECT */ + case 0x50: /* GPIO_DEBOUNCENABLE */ + case 0x54: /* GPIO_DEBOUNCINGTIME */ + cur = omap2_gpio_module_read(opaque, addr & ~3) & + ~(mask << ((addr & 3) << 3)); + + /* Fall through. */ + case 0x18: /* GPIO_IRQSTATUS1 */ + case 0x28: /* GPIO_IRQSTATUS2 */ + case 0x60: /* GPIO_CLEARIRQENABLE1 */ + case 0x64: /* GPIO_SETIRQENABLE1 */ + case 0x70: /* GPIO_CLEARIRQENABLE2 */ + case 0x74: /* GPIO_SETIREQNEABLE2 */ + case 0x80: /* GPIO_CLEARWKUENA */ + case 0x84: /* GPIO_SETWKUENA */ + case 0x90: /* GPIO_CLEARDATAOUT */ + case 0x94: /* GPIO_SETDATAOUT */ + value <<= (addr & 3) << 3; + omap2_gpio_module_write(opaque, addr, cur | value); + break; + + default: + OMAP_BAD_REG(addr); + return; + } +} + +static CPUReadMemoryFunc * const omap2_gpio_module_readfn[] = { + omap2_gpio_module_readp, + omap2_gpio_module_readp, + omap2_gpio_module_read, +}; + +static CPUWriteMemoryFunc * const omap2_gpio_module_writefn[] = { + omap2_gpio_module_writep, + omap2_gpio_module_writep, + omap2_gpio_module_write, +}; + +static void omap2_gpio_module_init(struct omap2_gpio_s *s, + struct omap_target_agent_s *ta, int region, + qemu_irq mpu, qemu_irq dsp, qemu_irq wkup, + omap_clk fclk, omap_clk iclk) +{ + int iomemtype; + + s->irq[0] = mpu; + s->irq[1] = dsp; + s->wkup = wkup; + s->in = qemu_allocate_irqs(omap2_gpio_module_set, s, 32); + + iomemtype = l4_register_io_memory(omap2_gpio_module_readfn, + omap2_gpio_module_writefn, s); + omap_l4_attach(ta, region, iomemtype); +} + +struct omap_gpif_s { + struct omap2_gpio_s module[5]; + int modules; + + int autoidle; + int gpo; +}; + +void omap_gpif_reset(struct omap_gpif_s *s) +{ + int i; + + for (i = 0; i < s->modules; i ++) + omap2_gpio_module_reset(s->module + i); + + s->autoidle = 0; + s->gpo = 0; +} + +static uint32_t omap_gpif_top_read(void *opaque, target_phys_addr_t addr) +{ + struct omap_gpif_s *s = (struct omap_gpif_s *) opaque; + + switch (addr) { + case 0x00: /* IPGENERICOCPSPL_REVISION */ + return 0x18; + + case 0x10: /* IPGENERICOCPSPL_SYSCONFIG */ + return s->autoidle; + + case 0x14: /* IPGENERICOCPSPL_SYSSTATUS */ + return 0x01; + + case 0x18: /* IPGENERICOCPSPL_IRQSTATUS */ + return 0x00; + + case 0x40: /* IPGENERICOCPSPL_GPO */ + return s->gpo; + + case 0x50: /* IPGENERICOCPSPL_GPI */ + return 0x00; + } + + OMAP_BAD_REG(addr); + return 0; +} + +static void omap_gpif_top_write(void *opaque, target_phys_addr_t addr, + uint32_t value) +{ + struct omap_gpif_s *s = (struct omap_gpif_s *) opaque; + + switch (addr) { + case 0x00: /* IPGENERICOCPSPL_REVISION */ + case 0x14: /* IPGENERICOCPSPL_SYSSTATUS */ + case 0x18: /* IPGENERICOCPSPL_IRQSTATUS */ + case 0x50: /* IPGENERICOCPSPL_GPI */ + OMAP_RO_REG(addr); + break; + + case 0x10: /* IPGENERICOCPSPL_SYSCONFIG */ + if (value & (1 << 1)) /* SOFTRESET */ + omap_gpif_reset(s); + s->autoidle = value & 1; + break; + + case 0x40: /* IPGENERICOCPSPL_GPO */ + s->gpo = value & 1; + break; + + default: + OMAP_BAD_REG(addr); + return; + } +} + +static CPUReadMemoryFunc * const omap_gpif_top_readfn[] = { + omap_gpif_top_read, + omap_gpif_top_read, + omap_gpif_top_read, +}; + +static CPUWriteMemoryFunc * const omap_gpif_top_writefn[] = { + omap_gpif_top_write, + omap_gpif_top_write, + omap_gpif_top_write, +}; + +struct omap_gpif_s *omap2_gpio_init(struct omap_target_agent_s *ta, + qemu_irq *irq, omap_clk *fclk, omap_clk iclk, int modules) +{ + int iomemtype, i; + struct omap_gpif_s *s = (struct omap_gpif_s *) + qemu_mallocz(sizeof(struct omap_gpif_s)); + int region[4] = { 0, 2, 4, 5 }; + + s->modules = modules; + for (i = 0; i < modules; i ++) + omap2_gpio_module_init(s->module + i, ta, region[i], + irq[i], NULL, NULL, fclk[i], iclk); + + omap_gpif_reset(s); + + iomemtype = l4_register_io_memory(omap_gpif_top_readfn, + omap_gpif_top_writefn, s); + omap_l4_attach(ta, 1, iomemtype); + + return s; +} + +qemu_irq *omap2_gpio_in_get(struct omap_gpif_s *s, int start) +{ + if (start >= s->modules * 32 || start < 0) + hw_error("%s: No GPIO line %i\n", __FUNCTION__, start); + return s->module[start >> 5].in + (start & 31); +} + +void omap2_gpio_out_set(struct omap_gpif_s *s, int line, qemu_irq handler) +{ + if (line >= s->modules * 32 || line < 0) + hw_error("%s: No GPIO line %i\n", __FUNCTION__, line); + s->module[line >> 5].handler[line & 31] = handler; +} diff -Nru qemu-kvm-0.12.5+noroms/hw/omap_gpmc.c qemu-kvm-0.14.1/hw/omap_gpmc.c --- qemu-kvm-0.12.5+noroms/hw/omap_gpmc.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/omap_gpmc.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,419 @@ +/* + * TI OMAP general purpose memory controller emulation. + * + * Copyright (C) 2007-2009 Nokia Corporation + * Original code written by Andrzej Zaborowski + * Enhancements for OMAP3 and NAND support written by Juha Riihimäki + * + * 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 or + * (at your option) any later version of the License. + * + * 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, see . + */ +#include "hw.h" +#include "flash.h" +#include "omap.h" + +/* General-Purpose Memory Controller */ +struct omap_gpmc_s { + qemu_irq irq; + + uint8_t sysconfig; + uint16_t irqst; + uint16_t irqen; + uint16_t timeout; + uint16_t config; + uint32_t prefconfig[2]; + int prefcontrol; + int preffifo; + int prefcount; + struct omap_gpmc_cs_file_s { + uint32_t config[7]; + target_phys_addr_t base; + size_t size; + int iomemtype; + void (*base_update)(void *opaque, target_phys_addr_t new); + void (*unmap)(void *opaque); + void *opaque; + } cs_file[8]; + int ecc_cs; + int ecc_ptr; + uint32_t ecc_cfg; + ECCState ecc[9]; +}; + +static void omap_gpmc_int_update(struct omap_gpmc_s *s) +{ + qemu_set_irq(s->irq, s->irqen & s->irqst); +} + +static void omap_gpmc_cs_map(struct omap_gpmc_cs_file_s *f, int base, int mask) +{ + /* TODO: check for overlapping regions and report access errors */ + if ((mask != 0x8 && mask != 0xc && mask != 0xe && mask != 0xf) || + (base < 0 || base >= 0x40) || + (base & 0x0f & ~mask)) { + fprintf(stderr, "%s: wrong cs address mapping/decoding!\n", + __FUNCTION__); + return; + } + + if (!f->opaque) + return; + + f->base = base << 24; + f->size = (0x0fffffff & ~(mask << 24)) + 1; + /* TODO: rather than setting the size of the mapping (which should be + * constant), the mask should cause wrapping of the address space, so + * that the same memory becomes accessible at every size bytes + * starting from base. */ + if (f->iomemtype) + cpu_register_physical_memory(f->base, f->size, f->iomemtype); + + if (f->base_update) + f->base_update(f->opaque, f->base); +} + +static void omap_gpmc_cs_unmap(struct omap_gpmc_cs_file_s *f) +{ + if (f->size) { + if (f->unmap) + f->unmap(f->opaque); + if (f->iomemtype) + cpu_register_physical_memory(f->base, f->size, IO_MEM_UNASSIGNED); + f->base = 0; + f->size = 0; + } +} + +void omap_gpmc_reset(struct omap_gpmc_s *s) +{ + int i; + + s->sysconfig = 0; + s->irqst = 0; + s->irqen = 0; + omap_gpmc_int_update(s); + s->timeout = 0; + s->config = 0xa00; + s->prefconfig[0] = 0x00004000; + s->prefconfig[1] = 0x00000000; + s->prefcontrol = 0; + s->preffifo = 0; + s->prefcount = 0; + for (i = 0; i < 8; i ++) { + if (s->cs_file[i].config[6] & (1 << 6)) /* CSVALID */ + omap_gpmc_cs_unmap(s->cs_file + i); + s->cs_file[i].config[0] = i ? 1 << 12 : 0; + s->cs_file[i].config[1] = 0x101001; + s->cs_file[i].config[2] = 0x020201; + s->cs_file[i].config[3] = 0x10031003; + s->cs_file[i].config[4] = 0x10f1111; + s->cs_file[i].config[5] = 0; + s->cs_file[i].config[6] = 0xf00 | (i ? 0 : 1 << 6); + if (s->cs_file[i].config[6] & (1 << 6)) /* CSVALID */ + omap_gpmc_cs_map(&s->cs_file[i], + s->cs_file[i].config[6] & 0x1f, /* MASKADDR */ + (s->cs_file[i].config[6] >> 8 & 0xf)); /* BASEADDR */ + } + omap_gpmc_cs_map(s->cs_file, 0, 0xf); + s->ecc_cs = 0; + s->ecc_ptr = 0; + s->ecc_cfg = 0x3fcff000; + for (i = 0; i < 9; i ++) + ecc_reset(&s->ecc[i]); +} + +static uint32_t omap_gpmc_read(void *opaque, target_phys_addr_t addr) +{ + struct omap_gpmc_s *s = (struct omap_gpmc_s *) opaque; + int cs; + struct omap_gpmc_cs_file_s *f; + + switch (addr) { + case 0x000: /* GPMC_REVISION */ + return 0x20; + + case 0x010: /* GPMC_SYSCONFIG */ + return s->sysconfig; + + case 0x014: /* GPMC_SYSSTATUS */ + return 1; /* RESETDONE */ + + case 0x018: /* GPMC_IRQSTATUS */ + return s->irqst; + + case 0x01c: /* GPMC_IRQENABLE */ + return s->irqen; + + case 0x040: /* GPMC_TIMEOUT_CONTROL */ + return s->timeout; + + case 0x044: /* GPMC_ERR_ADDRESS */ + case 0x048: /* GPMC_ERR_TYPE */ + return 0; + + case 0x050: /* GPMC_CONFIG */ + return s->config; + + case 0x054: /* GPMC_STATUS */ + return 0x001; + + case 0x060 ... 0x1d4: + cs = (addr - 0x060) / 0x30; + addr -= cs * 0x30; + f = s->cs_file + cs; + switch (addr) { + case 0x60: /* GPMC_CONFIG1 */ + return f->config[0]; + case 0x64: /* GPMC_CONFIG2 */ + return f->config[1]; + case 0x68: /* GPMC_CONFIG3 */ + return f->config[2]; + case 0x6c: /* GPMC_CONFIG4 */ + return f->config[3]; + case 0x70: /* GPMC_CONFIG5 */ + return f->config[4]; + case 0x74: /* GPMC_CONFIG6 */ + return f->config[5]; + case 0x78: /* GPMC_CONFIG7 */ + return f->config[6]; + case 0x84: /* GPMC_NAND_DATA */ + return 0; + } + break; + + case 0x1e0: /* GPMC_PREFETCH_CONFIG1 */ + return s->prefconfig[0]; + case 0x1e4: /* GPMC_PREFETCH_CONFIG2 */ + return s->prefconfig[1]; + case 0x1ec: /* GPMC_PREFETCH_CONTROL */ + return s->prefcontrol; + case 0x1f0: /* GPMC_PREFETCH_STATUS */ + return (s->preffifo << 24) | + ((s->preffifo > + ((s->prefconfig[0] >> 8) & 0x7f) ? 1 : 0) << 16) | + s->prefcount; + + case 0x1f4: /* GPMC_ECC_CONFIG */ + return s->ecc_cs; + case 0x1f8: /* GPMC_ECC_CONTROL */ + return s->ecc_ptr; + case 0x1fc: /* GPMC_ECC_SIZE_CONFIG */ + return s->ecc_cfg; + case 0x200 ... 0x220: /* GPMC_ECC_RESULT */ + cs = (addr & 0x1f) >> 2; + /* TODO: check correctness */ + return + ((s->ecc[cs].cp & 0x07) << 0) | + ((s->ecc[cs].cp & 0x38) << 13) | + ((s->ecc[cs].lp[0] & 0x1ff) << 3) | + ((s->ecc[cs].lp[1] & 0x1ff) << 19); + + case 0x230: /* GPMC_TESTMODE_CTRL */ + return 0; + case 0x234: /* GPMC_PSA_LSB */ + case 0x238: /* GPMC_PSA_MSB */ + return 0x00000000; + } + + OMAP_BAD_REG(addr); + return 0; +} + +static void omap_gpmc_write(void *opaque, target_phys_addr_t addr, + uint32_t value) +{ + struct omap_gpmc_s *s = (struct omap_gpmc_s *) opaque; + int cs; + struct omap_gpmc_cs_file_s *f; + + switch (addr) { + case 0x000: /* GPMC_REVISION */ + case 0x014: /* GPMC_SYSSTATUS */ + case 0x054: /* GPMC_STATUS */ + case 0x1f0: /* GPMC_PREFETCH_STATUS */ + case 0x200 ... 0x220: /* GPMC_ECC_RESULT */ + case 0x234: /* GPMC_PSA_LSB */ + case 0x238: /* GPMC_PSA_MSB */ + OMAP_RO_REG(addr); + break; + + case 0x010: /* GPMC_SYSCONFIG */ + if ((value >> 3) == 0x3) + fprintf(stderr, "%s: bad SDRAM idle mode %i\n", + __FUNCTION__, value >> 3); + if (value & 2) + omap_gpmc_reset(s); + s->sysconfig = value & 0x19; + break; + + case 0x018: /* GPMC_IRQSTATUS */ + s->irqen = ~value; + omap_gpmc_int_update(s); + break; + + case 0x01c: /* GPMC_IRQENABLE */ + s->irqen = value & 0xf03; + omap_gpmc_int_update(s); + break; + + case 0x040: /* GPMC_TIMEOUT_CONTROL */ + s->timeout = value & 0x1ff1; + break; + + case 0x044: /* GPMC_ERR_ADDRESS */ + case 0x048: /* GPMC_ERR_TYPE */ + break; + + case 0x050: /* GPMC_CONFIG */ + s->config = value & 0xf13; + break; + + case 0x060 ... 0x1d4: + cs = (addr - 0x060) / 0x30; + addr -= cs * 0x30; + f = s->cs_file + cs; + switch (addr) { + case 0x60: /* GPMC_CONFIG1 */ + f->config[0] = value & 0xffef3e13; + break; + case 0x64: /* GPMC_CONFIG2 */ + f->config[1] = value & 0x001f1f8f; + break; + case 0x68: /* GPMC_CONFIG3 */ + f->config[2] = value & 0x001f1f8f; + break; + case 0x6c: /* GPMC_CONFIG4 */ + f->config[3] = value & 0x1f8f1f8f; + break; + case 0x70: /* GPMC_CONFIG5 */ + f->config[4] = value & 0x0f1f1f1f; + break; + case 0x74: /* GPMC_CONFIG6 */ + f->config[5] = value & 0x00000fcf; + break; + case 0x78: /* GPMC_CONFIG7 */ + if ((f->config[6] ^ value) & 0xf7f) { + if (f->config[6] & (1 << 6)) /* CSVALID */ + omap_gpmc_cs_unmap(f); + if (value & (1 << 6)) /* CSVALID */ + omap_gpmc_cs_map(f, value & 0x1f, /* MASKADDR */ + (value >> 8 & 0xf)); /* BASEADDR */ + } + f->config[6] = value & 0x00000f7f; + break; + case 0x7c: /* GPMC_NAND_COMMAND */ + case 0x80: /* GPMC_NAND_ADDRESS */ + case 0x84: /* GPMC_NAND_DATA */ + break; + + default: + goto bad_reg; + } + break; + + case 0x1e0: /* GPMC_PREFETCH_CONFIG1 */ + s->prefconfig[0] = value & 0x7f8f7fbf; + /* TODO: update interrupts, fifos, dmas */ + break; + + case 0x1e4: /* GPMC_PREFETCH_CONFIG2 */ + s->prefconfig[1] = value & 0x3fff; + break; + + case 0x1ec: /* GPMC_PREFETCH_CONTROL */ + s->prefcontrol = value & 1; + if (s->prefcontrol) { + if (s->prefconfig[0] & 1) + s->preffifo = 0x40; + else + s->preffifo = 0x00; + } + /* TODO: start */ + break; + + case 0x1f4: /* GPMC_ECC_CONFIG */ + s->ecc_cs = 0x8f; + break; + case 0x1f8: /* GPMC_ECC_CONTROL */ + if (value & (1 << 8)) + for (cs = 0; cs < 9; cs ++) + ecc_reset(&s->ecc[cs]); + s->ecc_ptr = value & 0xf; + if (s->ecc_ptr == 0 || s->ecc_ptr > 9) { + s->ecc_ptr = 0; + s->ecc_cs &= ~1; + } + break; + case 0x1fc: /* GPMC_ECC_SIZE_CONFIG */ + s->ecc_cfg = value & 0x3fcff1ff; + break; + case 0x230: /* GPMC_TESTMODE_CTRL */ + if (value & 7) + fprintf(stderr, "%s: test mode enable attempt\n", __FUNCTION__); + break; + + default: + bad_reg: + OMAP_BAD_REG(addr); + return; + } +} + +static CPUReadMemoryFunc * const omap_gpmc_readfn[] = { + omap_badwidth_read32, /* TODO */ + omap_badwidth_read32, /* TODO */ + omap_gpmc_read, +}; + +static CPUWriteMemoryFunc * const omap_gpmc_writefn[] = { + omap_badwidth_write32, /* TODO */ + omap_badwidth_write32, /* TODO */ + omap_gpmc_write, +}; + +struct omap_gpmc_s *omap_gpmc_init(target_phys_addr_t base, qemu_irq irq) +{ + int iomemtype; + struct omap_gpmc_s *s = (struct omap_gpmc_s *) + qemu_mallocz(sizeof(struct omap_gpmc_s)); + + omap_gpmc_reset(s); + + iomemtype = cpu_register_io_memory(omap_gpmc_readfn, + omap_gpmc_writefn, s, DEVICE_NATIVE_ENDIAN); + cpu_register_physical_memory(base, 0x1000, iomemtype); + + return s; +} + +void omap_gpmc_attach(struct omap_gpmc_s *s, int cs, int iomemtype, + void (*base_upd)(void *opaque, target_phys_addr_t new), + void (*unmap)(void *opaque), void *opaque) +{ + struct omap_gpmc_cs_file_s *f; + + if (cs < 0 || cs >= 8) { + fprintf(stderr, "%s: bad chip-select %i\n", __FUNCTION__, cs); + exit(-1); + } + f = &s->cs_file[cs]; + + f->iomemtype = iomemtype; + f->base_update = base_upd; + f->unmap = unmap; + f->opaque = opaque; + + if (f->config[6] & (1 << 6)) /* CSVALID */ + omap_gpmc_cs_map(f, f->config[6] & 0x1f, /* MASKADDR */ + (f->config[6] >> 8 & 0xf)); /* BASEADDR */ +} diff -Nru qemu-kvm-0.12.5+noroms/hw/omap_gptimer.c qemu-kvm-0.14.1/hw/omap_gptimer.c --- qemu-kvm-0.12.5+noroms/hw/omap_gptimer.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/omap_gptimer.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,484 @@ +/* + * TI OMAP2 general purpose timers emulation. + * + * Copyright (C) 2007-2008 Nokia Corporation + * Written by Andrzej Zaborowski + * + * 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 or + * (at your option) any later version of the License. + * + * 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, see . + */ +#include "hw.h" +#include "qemu-timer.h" +#include "omap.h" + +/* GP timers */ +struct omap_gp_timer_s { + qemu_irq irq; + qemu_irq wkup; + qemu_irq in; + qemu_irq out; + omap_clk clk; + QEMUTimer *timer; + QEMUTimer *match; + struct omap_target_agent_s *ta; + + int in_val; + int out_val; + int64_t time; + int64_t rate; + int64_t ticks_per_sec; + + int16_t config; + int status; + int it_ena; + int wu_ena; + int enable; + int inout; + int capt2; + int pt; + enum { + gpt_trigger_none, gpt_trigger_overflow, gpt_trigger_both + } trigger; + enum { + gpt_capture_none, gpt_capture_rising, + gpt_capture_falling, gpt_capture_both + } capture; + int scpwm; + int ce; + int pre; + int ptv; + int ar; + int st; + int posted; + uint32_t val; + uint32_t load_val; + uint32_t capture_val[2]; + uint32_t match_val; + int capt_num; + + uint16_t writeh; /* LSB */ + uint16_t readh; /* MSB */ +}; + +#define GPT_TCAR_IT (1 << 2) +#define GPT_OVF_IT (1 << 1) +#define GPT_MAT_IT (1 << 0) + +static inline void omap_gp_timer_intr(struct omap_gp_timer_s *timer, int it) +{ + if (timer->it_ena & it) { + if (!timer->status) + qemu_irq_raise(timer->irq); + + timer->status |= it; + /* Or are the status bits set even when masked? + * i.e. is masking applied before or after the status register? */ + } + + if (timer->wu_ena & it) + qemu_irq_pulse(timer->wkup); +} + +static inline void omap_gp_timer_out(struct omap_gp_timer_s *timer, int level) +{ + if (!timer->inout && timer->out_val != level) { + timer->out_val = level; + qemu_set_irq(timer->out, level); + } +} + +static inline uint32_t omap_gp_timer_read(struct omap_gp_timer_s *timer) +{ + uint64_t distance; + + if (timer->st && timer->rate) { + distance = qemu_get_clock(vm_clock) - timer->time; + distance = muldiv64(distance, timer->rate, timer->ticks_per_sec); + + if (distance >= 0xffffffff - timer->val) + return 0xffffffff; + else + return timer->val + distance; + } else + return timer->val; +} + +static inline void omap_gp_timer_sync(struct omap_gp_timer_s *timer) +{ + if (timer->st) { + timer->val = omap_gp_timer_read(timer); + timer->time = qemu_get_clock(vm_clock); + } +} + +static inline void omap_gp_timer_update(struct omap_gp_timer_s *timer) +{ + int64_t expires, matches; + + if (timer->st && timer->rate) { + expires = muldiv64(0x100000000ll - timer->val, + timer->ticks_per_sec, timer->rate); + qemu_mod_timer(timer->timer, timer->time + expires); + + if (timer->ce && timer->match_val >= timer->val) { + matches = muldiv64(timer->match_val - timer->val, + timer->ticks_per_sec, timer->rate); + qemu_mod_timer(timer->match, timer->time + matches); + } else + qemu_del_timer(timer->match); + } else { + qemu_del_timer(timer->timer); + qemu_del_timer(timer->match); + omap_gp_timer_out(timer, timer->scpwm); + } +} + +static inline void omap_gp_timer_trigger(struct omap_gp_timer_s *timer) +{ + if (timer->pt) + /* TODO in overflow-and-match mode if the first event to + * occur is the match, don't toggle. */ + omap_gp_timer_out(timer, !timer->out_val); + else + /* TODO inverted pulse on timer->out_val == 1? */ + qemu_irq_pulse(timer->out); +} + +static void omap_gp_timer_tick(void *opaque) +{ + struct omap_gp_timer_s *timer = (struct omap_gp_timer_s *) opaque; + + if (!timer->ar) { + timer->st = 0; + timer->val = 0; + } else { + timer->val = timer->load_val; + timer->time = qemu_get_clock(vm_clock); + } + + if (timer->trigger == gpt_trigger_overflow || + timer->trigger == gpt_trigger_both) + omap_gp_timer_trigger(timer); + + omap_gp_timer_intr(timer, GPT_OVF_IT); + omap_gp_timer_update(timer); +} + +static void omap_gp_timer_match(void *opaque) +{ + struct omap_gp_timer_s *timer = (struct omap_gp_timer_s *) opaque; + + if (timer->trigger == gpt_trigger_both) + omap_gp_timer_trigger(timer); + + omap_gp_timer_intr(timer, GPT_MAT_IT); +} + +static void omap_gp_timer_input(void *opaque, int line, int on) +{ + struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque; + int trigger; + + switch (s->capture) { + default: + case gpt_capture_none: + trigger = 0; + break; + case gpt_capture_rising: + trigger = !s->in_val && on; + break; + case gpt_capture_falling: + trigger = s->in_val && !on; + break; + case gpt_capture_both: + trigger = (s->in_val == !on); + break; + } + s->in_val = on; + + if (s->inout && trigger && s->capt_num < 2) { + s->capture_val[s->capt_num] = omap_gp_timer_read(s); + + if (s->capt2 == s->capt_num ++) + omap_gp_timer_intr(s, GPT_TCAR_IT); + } +} + +static void omap_gp_timer_clk_update(void *opaque, int line, int on) +{ + struct omap_gp_timer_s *timer = (struct omap_gp_timer_s *) opaque; + + omap_gp_timer_sync(timer); + timer->rate = on ? omap_clk_getrate(timer->clk) : 0; + omap_gp_timer_update(timer); +} + +static void omap_gp_timer_clk_setup(struct omap_gp_timer_s *timer) +{ + omap_clk_adduser(timer->clk, + qemu_allocate_irqs(omap_gp_timer_clk_update, timer, 1)[0]); + timer->rate = omap_clk_getrate(timer->clk); +} + +void omap_gp_timer_reset(struct omap_gp_timer_s *s) +{ + s->config = 0x000; + s->status = 0; + s->it_ena = 0; + s->wu_ena = 0; + s->inout = 0; + s->capt2 = 0; + s->capt_num = 0; + s->pt = 0; + s->trigger = gpt_trigger_none; + s->capture = gpt_capture_none; + s->scpwm = 0; + s->ce = 0; + s->pre = 0; + s->ptv = 0; + s->ar = 0; + s->st = 0; + s->posted = 1; + s->val = 0x00000000; + s->load_val = 0x00000000; + s->capture_val[0] = 0x00000000; + s->capture_val[1] = 0x00000000; + s->match_val = 0x00000000; + omap_gp_timer_update(s); +} + +static uint32_t omap_gp_timer_readw(void *opaque, target_phys_addr_t addr) +{ + struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque; + + switch (addr) { + case 0x00: /* TIDR */ + return 0x21; + + case 0x10: /* TIOCP_CFG */ + return s->config; + + case 0x14: /* TISTAT */ + /* ??? When's this bit reset? */ + return 1; /* RESETDONE */ + + case 0x18: /* TISR */ + return s->status; + + case 0x1c: /* TIER */ + return s->it_ena; + + case 0x20: /* TWER */ + return s->wu_ena; + + case 0x24: /* TCLR */ + return (s->inout << 14) | + (s->capt2 << 13) | + (s->pt << 12) | + (s->trigger << 10) | + (s->capture << 8) | + (s->scpwm << 7) | + (s->ce << 6) | + (s->pre << 5) | + (s->ptv << 2) | + (s->ar << 1) | + (s->st << 0); + + case 0x28: /* TCRR */ + return omap_gp_timer_read(s); + + case 0x2c: /* TLDR */ + return s->load_val; + + case 0x30: /* TTGR */ + return 0xffffffff; + + case 0x34: /* TWPS */ + return 0x00000000; /* No posted writes pending. */ + + case 0x38: /* TMAR */ + return s->match_val; + + case 0x3c: /* TCAR1 */ + return s->capture_val[0]; + + case 0x40: /* TSICR */ + return s->posted << 2; + + case 0x44: /* TCAR2 */ + return s->capture_val[1]; + } + + OMAP_BAD_REG(addr); + return 0; +} + +static uint32_t omap_gp_timer_readh(void *opaque, target_phys_addr_t addr) +{ + struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque; + uint32_t ret; + + if (addr & 2) + return s->readh; + else { + ret = omap_gp_timer_readw(opaque, addr); + s->readh = ret >> 16; + return ret & 0xffff; + } +} + +static CPUReadMemoryFunc * const omap_gp_timer_readfn[] = { + omap_badwidth_read32, + omap_gp_timer_readh, + omap_gp_timer_readw, +}; + +static void omap_gp_timer_write(void *opaque, target_phys_addr_t addr, + uint32_t value) +{ + struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque; + + switch (addr) { + case 0x00: /* TIDR */ + case 0x14: /* TISTAT */ + case 0x34: /* TWPS */ + case 0x3c: /* TCAR1 */ + case 0x44: /* TCAR2 */ + OMAP_RO_REG(addr); + break; + + case 0x10: /* TIOCP_CFG */ + s->config = value & 0x33d; + if (((value >> 3) & 3) == 3) /* IDLEMODE */ + fprintf(stderr, "%s: illegal IDLEMODE value in TIOCP_CFG\n", + __FUNCTION__); + if (value & 2) /* SOFTRESET */ + omap_gp_timer_reset(s); + break; + + case 0x18: /* TISR */ + if (value & GPT_TCAR_IT) + s->capt_num = 0; + if (s->status && !(s->status &= ~value)) + qemu_irq_lower(s->irq); + break; + + case 0x1c: /* TIER */ + s->it_ena = value & 7; + break; + + case 0x20: /* TWER */ + s->wu_ena = value & 7; + break; + + case 0x24: /* TCLR */ + omap_gp_timer_sync(s); + s->inout = (value >> 14) & 1; + s->capt2 = (value >> 13) & 1; + s->pt = (value >> 12) & 1; + s->trigger = (value >> 10) & 3; + if (s->capture == gpt_capture_none && + ((value >> 8) & 3) != gpt_capture_none) + s->capt_num = 0; + s->capture = (value >> 8) & 3; + s->scpwm = (value >> 7) & 1; + s->ce = (value >> 6) & 1; + s->pre = (value >> 5) & 1; + s->ptv = (value >> 2) & 7; + s->ar = (value >> 1) & 1; + s->st = (value >> 0) & 1; + if (s->inout && s->trigger != gpt_trigger_none) + fprintf(stderr, "%s: GP timer pin must be an output " + "for this trigger mode\n", __FUNCTION__); + if (!s->inout && s->capture != gpt_capture_none) + fprintf(stderr, "%s: GP timer pin must be an input " + "for this capture mode\n", __FUNCTION__); + if (s->trigger == gpt_trigger_none) + omap_gp_timer_out(s, s->scpwm); + /* TODO: make sure this doesn't overflow 32-bits */ + s->ticks_per_sec = get_ticks_per_sec() << (s->pre ? s->ptv + 1 : 0); + omap_gp_timer_update(s); + break; + + case 0x28: /* TCRR */ + s->time = qemu_get_clock(vm_clock); + s->val = value; + omap_gp_timer_update(s); + break; + + case 0x2c: /* TLDR */ + s->load_val = value; + break; + + case 0x30: /* TTGR */ + s->time = qemu_get_clock(vm_clock); + s->val = s->load_val; + omap_gp_timer_update(s); + break; + + case 0x38: /* TMAR */ + omap_gp_timer_sync(s); + s->match_val = value; + omap_gp_timer_update(s); + break; + + case 0x40: /* TSICR */ + s->posted = (value >> 2) & 1; + if (value & 2) /* How much exactly are we supposed to reset? */ + omap_gp_timer_reset(s); + break; + + default: + OMAP_BAD_REG(addr); + } +} + +static void omap_gp_timer_writeh(void *opaque, target_phys_addr_t addr, + uint32_t value) +{ + struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque; + + if (addr & 2) + return omap_gp_timer_write(opaque, addr, (value << 16) | s->writeh); + else + s->writeh = (uint16_t) value; +} + +static CPUWriteMemoryFunc * const omap_gp_timer_writefn[] = { + omap_badwidth_write32, + omap_gp_timer_writeh, + omap_gp_timer_write, +}; + +struct omap_gp_timer_s *omap_gp_timer_init(struct omap_target_agent_s *ta, + qemu_irq irq, omap_clk fclk, omap_clk iclk) +{ + int iomemtype; + struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) + qemu_mallocz(sizeof(struct omap_gp_timer_s)); + + s->ta = ta; + s->irq = irq; + s->clk = fclk; + s->timer = qemu_new_timer(vm_clock, omap_gp_timer_tick, s); + s->match = qemu_new_timer(vm_clock, omap_gp_timer_match, s); + s->in = qemu_allocate_irqs(omap_gp_timer_input, s, 1)[0]; + omap_gp_timer_reset(s); + omap_gp_timer_clk_setup(s); + + iomemtype = l4_register_io_memory(omap_gp_timer_readfn, + omap_gp_timer_writefn, s); + omap_l4_attach(ta, 0, iomemtype); + + return s; +} diff -Nru qemu-kvm-0.12.5+noroms/hw/omap.h qemu-kvm-0.14.1/hw/omap.h --- qemu-kvm-0.12.5+noroms/hw/omap.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/omap.h 2011-05-11 13:29:46.000000000 +0000 @@ -61,16 +61,42 @@ int64_t omap_clk_getrate(omap_clk clk); void omap_clk_reparent(omap_clk clk, omap_clk parent); -/* omap[123].c */ +/* OMAP2 l4 Interconnect */ struct omap_l4_s; +struct omap_l4_region_s { + target_phys_addr_t offset; + size_t size; + int access; +}; +struct omap_l4_agent_info_s { + int ta; + int region; + int regions; + int ta_region; +}; +struct omap_target_agent_s { + struct omap_l4_s *bus; + int regions; + const struct omap_l4_region_s *start; + target_phys_addr_t base; + uint32_t component; + uint32_t control; + uint32_t status; +}; struct omap_l4_s *omap_l4_init(target_phys_addr_t base, int ta_num); struct omap_target_agent_s; -struct omap_target_agent_s *omap_l4ta_get(struct omap_l4_s *bus, int cs); +struct omap_target_agent_s *omap_l4ta_get( + struct omap_l4_s *bus, + const struct omap_l4_region_s *regions, + const struct omap_l4_agent_info_s *agents, + int cs); target_phys_addr_t omap_l4_attach(struct omap_target_agent_s *ta, int region, int iotype); -# define l4_register_io_memory cpu_register_io_memory +int l4_register_io_memory(CPUReadMemoryFunc * const *mem_read, + CPUWriteMemoryFunc * const *mem_write, void *opaque); +/* OMAP interrupt controller */ struct omap_intr_handler_s; struct omap_intr_handler_s *omap_inth_init(target_phys_addr_t base, unsigned long size, unsigned char nbanks, qemu_irq **pins, @@ -80,21 +106,17 @@ qemu_irq parent_irq, qemu_irq parent_fiq, omap_clk fclk, omap_clk iclk); void omap_inth_reset(struct omap_intr_handler_s *s); +qemu_irq omap_inth_get_pin(struct omap_intr_handler_s *s, int n); -struct omap_prcm_s; -struct omap_prcm_s *omap_prcm_init(struct omap_target_agent_s *ta, - qemu_irq mpu_int, qemu_irq dsp_int, qemu_irq iva_int, - struct omap_mpu_state_s *mpu); - -struct omap_sysctl_s; -struct omap_sysctl_s *omap_sysctl_init(struct omap_target_agent_s *ta, - omap_clk iclk, struct omap_mpu_state_s *mpu); - +/* OMAP2 SDRAM controller */ struct omap_sdrc_s; struct omap_sdrc_s *omap_sdrc_init(target_phys_addr_t base); +void omap_sdrc_reset(struct omap_sdrc_s *s); +/* OMAP2 general purpose memory controller */ struct omap_gpmc_s; struct omap_gpmc_s *omap_gpmc_init(target_phys_addr_t base, qemu_irq irq); +void omap_gpmc_reset(struct omap_gpmc_s *s); void omap_gpmc_attach(struct omap_gpmc_s *s, int cs, int iomemtype, void (*base_upd)(void *opaque, target_phys_addr_t new), void (*unmap)(void *opaque), void *opaque); @@ -627,36 +649,27 @@ # define OMAP24XX_DMA_EXT_DMAREQ5 64 /* omap[123].c */ -struct omap_mpu_timer_s; -struct omap_mpu_timer_s *omap_mpu_timer_init(target_phys_addr_t base, - qemu_irq irq, omap_clk clk); - +/* OMAP2 gp timer */ struct omap_gp_timer_s; struct omap_gp_timer_s *omap_gp_timer_init(struct omap_target_agent_s *ta, qemu_irq irq, omap_clk fclk, omap_clk iclk); +void omap_gp_timer_reset(struct omap_gp_timer_s *s); -struct omap_watchdog_timer_s; -struct omap_watchdog_timer_s *omap_wd_timer_init(target_phys_addr_t base, - qemu_irq irq, omap_clk clk); - -struct omap_32khz_timer_s; -struct omap_32khz_timer_s *omap_os_timer_init(target_phys_addr_t base, - qemu_irq irq, omap_clk clk); - -void omap_synctimer_init(struct omap_target_agent_s *ta, +/* OMAP2 sysctimer */ +struct omap_synctimer_s; +struct omap_synctimer_s *omap_synctimer_init(struct omap_target_agent_s *ta, struct omap_mpu_state_s *mpu, omap_clk fclk, omap_clk iclk); - -struct omap_tipb_bridge_s; -struct omap_tipb_bridge_s *omap_tipb_bridge_init(target_phys_addr_t base, - qemu_irq abort_irq, omap_clk clk); +void omap_synctimer_reset(struct omap_synctimer_s *s); struct omap_uart_s; struct omap_uart_s *omap_uart_init(target_phys_addr_t base, qemu_irq irq, omap_clk fclk, omap_clk iclk, - qemu_irq txdma, qemu_irq rxdma, CharDriverState *chr); + qemu_irq txdma, qemu_irq rxdma, + const char *label, CharDriverState *chr); struct omap_uart_s *omap2_uart_init(struct omap_target_agent_s *ta, qemu_irq irq, omap_clk fclk, omap_clk iclk, - qemu_irq txdma, qemu_irq rxdma, CharDriverState *chr); + qemu_irq txdma, qemu_irq rxdma, + const char *label, CharDriverState *chr); void omap_uart_reset(struct omap_uart_s *s); void omap_uart_attach(struct omap_uart_s *s, CharDriverState *chr); @@ -668,15 +681,19 @@ void omap_mpuio_out_set(struct omap_mpuio_s *s, int line, qemu_irq handler); void omap_mpuio_key(struct omap_mpuio_s *s, int row, int col, int down); +/* omap1 gpio module interface */ struct omap_gpio_s; struct omap_gpio_s *omap_gpio_init(target_phys_addr_t base, qemu_irq irq, omap_clk clk); +void omap_gpio_reset(struct omap_gpio_s *s); qemu_irq *omap_gpio_in_get(struct omap_gpio_s *s); void omap_gpio_out_set(struct omap_gpio_s *s, int line, qemu_irq handler); +/* omap2 gpio interface */ struct omap_gpif_s; struct omap_gpif_s *omap2_gpio_init(struct omap_target_agent_s *ta, qemu_irq *irq, omap_clk *fclk, omap_clk iclk, int modules); +void omap_gpif_reset(struct omap_gpif_s *s); qemu_irq *omap2_gpio_in_get(struct omap_gpif_s *s, int start); void omap2_gpio_out_set(struct omap_gpif_s *s, int line, qemu_irq handler); @@ -691,16 +708,14 @@ void omap_uwire_attach(struct omap_uwire_s *s, uWireSlave *slave, int chipselect); +/* OMAP2 spi */ struct omap_mcspi_s; struct omap_mcspi_s *omap_mcspi_init(struct omap_target_agent_s *ta, int chnum, qemu_irq irq, qemu_irq *drq, omap_clk fclk, omap_clk iclk); void omap_mcspi_attach(struct omap_mcspi_s *s, uint32_t (*txrx)(void *opaque, uint32_t, int), void *opaque, int chipselect); - -struct omap_rtc_s; -struct omap_rtc_s *omap_rtc_init(target_phys_addr_t base, - qemu_irq *irq, omap_clk clk); +void omap_mcspi_reset(struct omap_mcspi_s *s); struct I2SCodec { void *opaque; @@ -731,16 +746,9 @@ qemu_irq *irq, qemu_irq *dma, omap_clk clk); void omap_mcbsp_i2s_attach(struct omap_mcbsp_s *s, I2SCodec *slave); -struct omap_lpg_s; -struct omap_lpg_s *omap_lpg_init(target_phys_addr_t base, omap_clk clk); - void omap_tap_init(struct omap_target_agent_s *ta, struct omap_mpu_state_s *mpu); -struct omap_eac_s; -struct omap_eac_s *omap_eac_init(struct omap_target_agent_s *ta, - qemu_irq irq, qemu_irq *drq, omap_clk fclk, omap_clk iclk); - /* omap_lcdc.c */ struct omap_lcd_panel_s; void omap_lcdc_reset(struct omap_lcd_panel_s *s); @@ -933,11 +941,7 @@ struct omap_l4_s *l4; struct omap_gp_timer_s *gptimer[12]; - - struct omap_synctimer_s { - uint32_t val; - uint16_t readh; - } synctimer; + struct omap_synctimer_s *synctimer; struct omap_prcm_s *prcm; struct omap_sdrc_s *sdrc; @@ -1125,7 +1129,8 @@ s->mem_write = mem_write; s->opaque = opaque; s->in = 0; - return cpu_register_io_memory(io_readfn, io_writefn, s); + return cpu_register_io_memory(io_readfn, io_writefn, s, + DEVICE_NATIVE_ENDIAN); } # define cpu_register_io_memory debug_register_io_memory # endif @@ -1133,10 +1138,4 @@ /* Define when we want to reduce the number of IO regions registered. */ /*# define L4_MUX_HACK*/ -# ifdef L4_MUX_HACK -# undef l4_register_io_memory -int l4_register_io_memory(CPUReadMemoryFunc * const *mem_read, - CPUWriteMemoryFunc * const *mem_write, void *opaque); -# endif - #endif /* hw_omap_h */ diff -Nru qemu-kvm-0.12.5+noroms/hw/omap_i2c.c qemu-kvm-0.14.1/hw/omap_i2c.c --- qemu-kvm-0.12.5+noroms/hw/omap_i2c.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/omap_i2c.c 2011-05-11 13:29:46.000000000 +0000 @@ -190,8 +190,9 @@ if (s->rxlen > 2) s->fifo >>= 16; s->rxlen -= 2; - } else - /* XXX: remote access (qualifier) error - what's that? */; + } else { + /* XXX: remote access (qualifier) error - what's that? */ + } if (!s->rxlen) { s->stat &= ~(1 << 3); /* RRDY */ if (((s->control >> 10) & 1) && /* MST */ @@ -436,7 +437,7 @@ omap_i2c_reset(s); iomemtype = cpu_register_io_memory(omap_i2c_readfn, - omap_i2c_writefn, s); + omap_i2c_writefn, s, DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base, 0x800, iomemtype); return s; diff -Nru qemu-kvm-0.12.5+noroms/hw/omap_intc.c qemu-kvm-0.14.1/hw/omap_intc.c --- qemu-kvm-0.12.5+noroms/hw/omap_intc.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/omap_intc.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,598 @@ +/* + * TI OMAP interrupt controller emulation. + * + * Copyright (C) 2006-2008 Andrzej Zaborowski + * Copyright (C) 2007-2008 Nokia Corporation + * + * 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 or + * (at your option) version 3 of the License. + * + * 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, see . + */ +#include "hw.h" +#include "omap.h" + +/* Interrupt Handlers */ +struct omap_intr_handler_bank_s { + uint32_t irqs; + uint32_t inputs; + uint32_t mask; + uint32_t fiq; + uint32_t sens_edge; + uint32_t swi; + unsigned char priority[32]; +}; + +struct omap_intr_handler_s { + qemu_irq *pins; + qemu_irq parent_intr[2]; + unsigned char nbanks; + int level_only; + + /* state */ + uint32_t new_agr[2]; + int sir_intr[2]; + int autoidle; + uint32_t mask; + struct omap_intr_handler_bank_s bank[]; +}; + +inline qemu_irq omap_inth_get_pin(struct omap_intr_handler_s *s, int n) +{ + return s->pins[n]; +} + +static void omap_inth_sir_update(struct omap_intr_handler_s *s, int is_fiq) +{ + int i, j, sir_intr, p_intr, p, f; + uint32_t level; + sir_intr = 0; + p_intr = 255; + + /* Find the interrupt line with the highest dynamic priority. + * Note: 0 denotes the hightest priority. + * If all interrupts have the same priority, the default order is IRQ_N, + * IRQ_N-1,...,IRQ_0. */ + for (j = 0; j < s->nbanks; ++j) { + level = s->bank[j].irqs & ~s->bank[j].mask & + (is_fiq ? s->bank[j].fiq : ~s->bank[j].fiq); + for (f = ffs(level), i = f - 1, level >>= f - 1; f; i += f, + level >>= f) { + p = s->bank[j].priority[i]; + if (p <= p_intr) { + p_intr = p; + sir_intr = 32 * j + i; + } + f = ffs(level >> 1); + } + } + s->sir_intr[is_fiq] = sir_intr; +} + +static inline void omap_inth_update(struct omap_intr_handler_s *s, int is_fiq) +{ + int i; + uint32_t has_intr = 0; + + for (i = 0; i < s->nbanks; ++i) + has_intr |= s->bank[i].irqs & ~s->bank[i].mask & + (is_fiq ? s->bank[i].fiq : ~s->bank[i].fiq); + + if (s->new_agr[is_fiq] & has_intr & s->mask) { + s->new_agr[is_fiq] = 0; + omap_inth_sir_update(s, is_fiq); + qemu_set_irq(s->parent_intr[is_fiq], 1); + } +} + +#define INT_FALLING_EDGE 0 +#define INT_LOW_LEVEL 1 + +static void omap_set_intr(void *opaque, int irq, int req) +{ + struct omap_intr_handler_s *ih = (struct omap_intr_handler_s *) opaque; + uint32_t rise; + + struct omap_intr_handler_bank_s *bank = &ih->bank[irq >> 5]; + int n = irq & 31; + + if (req) { + rise = ~bank->irqs & (1 << n); + if (~bank->sens_edge & (1 << n)) + rise &= ~bank->inputs; + + bank->inputs |= (1 << n); + if (rise) { + bank->irqs |= rise; + omap_inth_update(ih, 0); + omap_inth_update(ih, 1); + } + } else { + rise = bank->sens_edge & bank->irqs & (1 << n); + bank->irqs &= ~rise; + bank->inputs &= ~(1 << n); + } +} + +/* Simplified version with no edge detection */ +static void omap_set_intr_noedge(void *opaque, int irq, int req) +{ + struct omap_intr_handler_s *ih = (struct omap_intr_handler_s *) opaque; + uint32_t rise; + + struct omap_intr_handler_bank_s *bank = &ih->bank[irq >> 5]; + int n = irq & 31; + + if (req) { + rise = ~bank->inputs & (1 << n); + if (rise) { + bank->irqs |= bank->inputs |= rise; + omap_inth_update(ih, 0); + omap_inth_update(ih, 1); + } + } else + bank->irqs = (bank->inputs &= ~(1 << n)) | bank->swi; +} + +static uint32_t omap_inth_read(void *opaque, target_phys_addr_t addr) +{ + struct omap_intr_handler_s *s = (struct omap_intr_handler_s *) opaque; + int i, offset = addr; + int bank_no = offset >> 8; + int line_no; + struct omap_intr_handler_bank_s *bank = &s->bank[bank_no]; + offset &= 0xff; + + switch (offset) { + case 0x00: /* ITR */ + return bank->irqs; + + case 0x04: /* MIR */ + return bank->mask; + + case 0x10: /* SIR_IRQ_CODE */ + case 0x14: /* SIR_FIQ_CODE */ + if (bank_no != 0) + break; + line_no = s->sir_intr[(offset - 0x10) >> 2]; + bank = &s->bank[line_no >> 5]; + i = line_no & 31; + if (((bank->sens_edge >> i) & 1) == INT_FALLING_EDGE) + bank->irqs &= ~(1 << i); + return line_no; + + case 0x18: /* CONTROL_REG */ + if (bank_no != 0) + break; + return 0; + + case 0x1c: /* ILR0 */ + case 0x20: /* ILR1 */ + case 0x24: /* ILR2 */ + case 0x28: /* ILR3 */ + case 0x2c: /* ILR4 */ + case 0x30: /* ILR5 */ + case 0x34: /* ILR6 */ + case 0x38: /* ILR7 */ + case 0x3c: /* ILR8 */ + case 0x40: /* ILR9 */ + case 0x44: /* ILR10 */ + case 0x48: /* ILR11 */ + case 0x4c: /* ILR12 */ + case 0x50: /* ILR13 */ + case 0x54: /* ILR14 */ + case 0x58: /* ILR15 */ + case 0x5c: /* ILR16 */ + case 0x60: /* ILR17 */ + case 0x64: /* ILR18 */ + case 0x68: /* ILR19 */ + case 0x6c: /* ILR20 */ + case 0x70: /* ILR21 */ + case 0x74: /* ILR22 */ + case 0x78: /* ILR23 */ + case 0x7c: /* ILR24 */ + case 0x80: /* ILR25 */ + case 0x84: /* ILR26 */ + case 0x88: /* ILR27 */ + case 0x8c: /* ILR28 */ + case 0x90: /* ILR29 */ + case 0x94: /* ILR30 */ + case 0x98: /* ILR31 */ + i = (offset - 0x1c) >> 2; + return (bank->priority[i] << 2) | + (((bank->sens_edge >> i) & 1) << 1) | + ((bank->fiq >> i) & 1); + + case 0x9c: /* ISR */ + return 0x00000000; + + } + OMAP_BAD_REG(addr); + return 0; +} + +static void omap_inth_write(void *opaque, target_phys_addr_t addr, + uint32_t value) +{ + struct omap_intr_handler_s *s = (struct omap_intr_handler_s *) opaque; + int i, offset = addr; + int bank_no = offset >> 8; + struct omap_intr_handler_bank_s *bank = &s->bank[bank_no]; + offset &= 0xff; + + switch (offset) { + case 0x00: /* ITR */ + /* Important: ignore the clearing if the IRQ is level-triggered and + the input bit is 1 */ + bank->irqs &= value | (bank->inputs & bank->sens_edge); + return; + + case 0x04: /* MIR */ + bank->mask = value; + omap_inth_update(s, 0); + omap_inth_update(s, 1); + return; + + case 0x10: /* SIR_IRQ_CODE */ + case 0x14: /* SIR_FIQ_CODE */ + OMAP_RO_REG(addr); + break; + + case 0x18: /* CONTROL_REG */ + if (bank_no != 0) + break; + if (value & 2) { + qemu_set_irq(s->parent_intr[1], 0); + s->new_agr[1] = ~0; + omap_inth_update(s, 1); + } + if (value & 1) { + qemu_set_irq(s->parent_intr[0], 0); + s->new_agr[0] = ~0; + omap_inth_update(s, 0); + } + return; + + case 0x1c: /* ILR0 */ + case 0x20: /* ILR1 */ + case 0x24: /* ILR2 */ + case 0x28: /* ILR3 */ + case 0x2c: /* ILR4 */ + case 0x30: /* ILR5 */ + case 0x34: /* ILR6 */ + case 0x38: /* ILR7 */ + case 0x3c: /* ILR8 */ + case 0x40: /* ILR9 */ + case 0x44: /* ILR10 */ + case 0x48: /* ILR11 */ + case 0x4c: /* ILR12 */ + case 0x50: /* ILR13 */ + case 0x54: /* ILR14 */ + case 0x58: /* ILR15 */ + case 0x5c: /* ILR16 */ + case 0x60: /* ILR17 */ + case 0x64: /* ILR18 */ + case 0x68: /* ILR19 */ + case 0x6c: /* ILR20 */ + case 0x70: /* ILR21 */ + case 0x74: /* ILR22 */ + case 0x78: /* ILR23 */ + case 0x7c: /* ILR24 */ + case 0x80: /* ILR25 */ + case 0x84: /* ILR26 */ + case 0x88: /* ILR27 */ + case 0x8c: /* ILR28 */ + case 0x90: /* ILR29 */ + case 0x94: /* ILR30 */ + case 0x98: /* ILR31 */ + i = (offset - 0x1c) >> 2; + bank->priority[i] = (value >> 2) & 0x1f; + bank->sens_edge &= ~(1 << i); + bank->sens_edge |= ((value >> 1) & 1) << i; + bank->fiq &= ~(1 << i); + bank->fiq |= (value & 1) << i; + return; + + case 0x9c: /* ISR */ + for (i = 0; i < 32; i ++) + if (value & (1 << i)) { + omap_set_intr(s, 32 * bank_no + i, 1); + return; + } + return; + } + OMAP_BAD_REG(addr); +} + +static CPUReadMemoryFunc * const omap_inth_readfn[] = { + omap_badwidth_read32, + omap_badwidth_read32, + omap_inth_read, +}; + +static CPUWriteMemoryFunc * const omap_inth_writefn[] = { + omap_inth_write, + omap_inth_write, + omap_inth_write, +}; + +void omap_inth_reset(struct omap_intr_handler_s *s) +{ + int i; + + for (i = 0; i < s->nbanks; ++i){ + s->bank[i].irqs = 0x00000000; + s->bank[i].mask = 0xffffffff; + s->bank[i].sens_edge = 0x00000000; + s->bank[i].fiq = 0x00000000; + s->bank[i].inputs = 0x00000000; + s->bank[i].swi = 0x00000000; + memset(s->bank[i].priority, 0, sizeof(s->bank[i].priority)); + + if (s->level_only) + s->bank[i].sens_edge = 0xffffffff; + } + + s->new_agr[0] = ~0; + s->new_agr[1] = ~0; + s->sir_intr[0] = 0; + s->sir_intr[1] = 0; + s->autoidle = 0; + s->mask = ~0; + + qemu_set_irq(s->parent_intr[0], 0); + qemu_set_irq(s->parent_intr[1], 0); +} + +struct omap_intr_handler_s *omap_inth_init(target_phys_addr_t base, + unsigned long size, unsigned char nbanks, qemu_irq **pins, + qemu_irq parent_irq, qemu_irq parent_fiq, omap_clk clk) +{ + int iomemtype; + struct omap_intr_handler_s *s = (struct omap_intr_handler_s *) + qemu_mallocz(sizeof(struct omap_intr_handler_s) + + sizeof(struct omap_intr_handler_bank_s) * nbanks); + + s->parent_intr[0] = parent_irq; + s->parent_intr[1] = parent_fiq; + s->nbanks = nbanks; + s->pins = qemu_allocate_irqs(omap_set_intr, s, nbanks * 32); + if (pins) + *pins = s->pins; + + omap_inth_reset(s); + + iomemtype = cpu_register_io_memory(omap_inth_readfn, + omap_inth_writefn, s, DEVICE_NATIVE_ENDIAN); + cpu_register_physical_memory(base, size, iomemtype); + + return s; +} + +static uint32_t omap2_inth_read(void *opaque, target_phys_addr_t addr) +{ + struct omap_intr_handler_s *s = (struct omap_intr_handler_s *) opaque; + int offset = addr; + int bank_no, line_no; + struct omap_intr_handler_bank_s *bank = NULL; + + if ((offset & 0xf80) == 0x80) { + bank_no = (offset & 0x60) >> 5; + if (bank_no < s->nbanks) { + offset &= ~0x60; + bank = &s->bank[bank_no]; + } + } + + switch (offset) { + case 0x00: /* INTC_REVISION */ + return 0x21; + + case 0x10: /* INTC_SYSCONFIG */ + return (s->autoidle >> 2) & 1; + + case 0x14: /* INTC_SYSSTATUS */ + return 1; /* RESETDONE */ + + case 0x40: /* INTC_SIR_IRQ */ + return s->sir_intr[0]; + + case 0x44: /* INTC_SIR_FIQ */ + return s->sir_intr[1]; + + case 0x48: /* INTC_CONTROL */ + return (!s->mask) << 2; /* GLOBALMASK */ + + case 0x4c: /* INTC_PROTECTION */ + return 0; + + case 0x50: /* INTC_IDLE */ + return s->autoidle & 3; + + /* Per-bank registers */ + case 0x80: /* INTC_ITR */ + return bank->inputs; + + case 0x84: /* INTC_MIR */ + return bank->mask; + + case 0x88: /* INTC_MIR_CLEAR */ + case 0x8c: /* INTC_MIR_SET */ + return 0; + + case 0x90: /* INTC_ISR_SET */ + return bank->swi; + + case 0x94: /* INTC_ISR_CLEAR */ + return 0; + + case 0x98: /* INTC_PENDING_IRQ */ + return bank->irqs & ~bank->mask & ~bank->fiq; + + case 0x9c: /* INTC_PENDING_FIQ */ + return bank->irqs & ~bank->mask & bank->fiq; + + /* Per-line registers */ + case 0x100 ... 0x300: /* INTC_ILR */ + bank_no = (offset - 0x100) >> 7; + if (bank_no > s->nbanks) + break; + bank = &s->bank[bank_no]; + line_no = (offset & 0x7f) >> 2; + return (bank->priority[line_no] << 2) | + ((bank->fiq >> line_no) & 1); + } + OMAP_BAD_REG(addr); + return 0; +} + +static void omap2_inth_write(void *opaque, target_phys_addr_t addr, + uint32_t value) +{ + struct omap_intr_handler_s *s = (struct omap_intr_handler_s *) opaque; + int offset = addr; + int bank_no, line_no; + struct omap_intr_handler_bank_s *bank = NULL; + + if ((offset & 0xf80) == 0x80) { + bank_no = (offset & 0x60) >> 5; + if (bank_no < s->nbanks) { + offset &= ~0x60; + bank = &s->bank[bank_no]; + } + } + + switch (offset) { + case 0x10: /* INTC_SYSCONFIG */ + s->autoidle &= 4; + s->autoidle |= (value & 1) << 2; + if (value & 2) /* SOFTRESET */ + omap_inth_reset(s); + return; + + case 0x48: /* INTC_CONTROL */ + s->mask = (value & 4) ? 0 : ~0; /* GLOBALMASK */ + if (value & 2) { /* NEWFIQAGR */ + qemu_set_irq(s->parent_intr[1], 0); + s->new_agr[1] = ~0; + omap_inth_update(s, 1); + } + if (value & 1) { /* NEWIRQAGR */ + qemu_set_irq(s->parent_intr[0], 0); + s->new_agr[0] = ~0; + omap_inth_update(s, 0); + } + return; + + case 0x4c: /* INTC_PROTECTION */ + /* TODO: Make a bitmap (or sizeof(char)map) of access privileges + * for every register, see Chapter 3 and 4 for privileged mode. */ + if (value & 1) + fprintf(stderr, "%s: protection mode enable attempt\n", + __FUNCTION__); + return; + + case 0x50: /* INTC_IDLE */ + s->autoidle &= ~3; + s->autoidle |= value & 3; + return; + + /* Per-bank registers */ + case 0x84: /* INTC_MIR */ + bank->mask = value; + omap_inth_update(s, 0); + omap_inth_update(s, 1); + return; + + case 0x88: /* INTC_MIR_CLEAR */ + bank->mask &= ~value; + omap_inth_update(s, 0); + omap_inth_update(s, 1); + return; + + case 0x8c: /* INTC_MIR_SET */ + bank->mask |= value; + return; + + case 0x90: /* INTC_ISR_SET */ + bank->irqs |= bank->swi |= value; + omap_inth_update(s, 0); + omap_inth_update(s, 1); + return; + + case 0x94: /* INTC_ISR_CLEAR */ + bank->swi &= ~value; + bank->irqs = bank->swi & bank->inputs; + return; + + /* Per-line registers */ + case 0x100 ... 0x300: /* INTC_ILR */ + bank_no = (offset - 0x100) >> 7; + if (bank_no > s->nbanks) + break; + bank = &s->bank[bank_no]; + line_no = (offset & 0x7f) >> 2; + bank->priority[line_no] = (value >> 2) & 0x3f; + bank->fiq &= ~(1 << line_no); + bank->fiq |= (value & 1) << line_no; + return; + + case 0x00: /* INTC_REVISION */ + case 0x14: /* INTC_SYSSTATUS */ + case 0x40: /* INTC_SIR_IRQ */ + case 0x44: /* INTC_SIR_FIQ */ + case 0x80: /* INTC_ITR */ + case 0x98: /* INTC_PENDING_IRQ */ + case 0x9c: /* INTC_PENDING_FIQ */ + OMAP_RO_REG(addr); + return; + } + OMAP_BAD_REG(addr); +} + +static CPUReadMemoryFunc * const omap2_inth_readfn[] = { + omap_badwidth_read32, + omap_badwidth_read32, + omap2_inth_read, +}; + +static CPUWriteMemoryFunc * const omap2_inth_writefn[] = { + omap2_inth_write, + omap2_inth_write, + omap2_inth_write, +}; + +struct omap_intr_handler_s *omap2_inth_init(target_phys_addr_t base, + int size, int nbanks, qemu_irq **pins, + qemu_irq parent_irq, qemu_irq parent_fiq, + omap_clk fclk, omap_clk iclk) +{ + int iomemtype; + struct omap_intr_handler_s *s = (struct omap_intr_handler_s *) + qemu_mallocz(sizeof(struct omap_intr_handler_s) + + sizeof(struct omap_intr_handler_bank_s) * nbanks); + + s->parent_intr[0] = parent_irq; + s->parent_intr[1] = parent_fiq; + s->nbanks = nbanks; + s->level_only = 1; + s->pins = qemu_allocate_irqs(omap_set_intr_noedge, s, nbanks * 32); + if (pins) + *pins = s->pins; + + omap_inth_reset(s); + + iomemtype = cpu_register_io_memory(omap2_inth_readfn, + omap2_inth_writefn, s, DEVICE_NATIVE_ENDIAN); + cpu_register_physical_memory(base, size, iomemtype); + + return s; +} diff -Nru qemu-kvm-0.12.5+noroms/hw/omap_l4.c qemu-kvm-0.14.1/hw/omap_l4.c --- qemu-kvm-0.12.5+noroms/hw/omap_l4.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/omap_l4.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,272 @@ +/* + * TI OMAP L4 interconnect emulation. + * + * Copyright (C) 2007-2009 Nokia Corporation + * Written by Andrzej Zaborowski + * + * 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 or + * (at your option) any later version of the License. + * + * 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, see . + */ +#include "hw.h" +#include "omap.h" + +#ifdef L4_MUX_HACK +static int omap_l4_io_entries; +static int omap_cpu_io_entry; +static struct omap_l4_entry { + CPUReadMemoryFunc * const *mem_read; + CPUWriteMemoryFunc * const *mem_write; + void *opaque; +} *omap_l4_io_entry; +static CPUReadMemoryFunc * const *omap_l4_io_readb_fn; +static CPUReadMemoryFunc * const *omap_l4_io_readh_fn; +static CPUReadMemoryFunc * const *omap_l4_io_readw_fn; +static CPUWriteMemoryFunc * const *omap_l4_io_writeb_fn; +static CPUWriteMemoryFunc * const *omap_l4_io_writeh_fn; +static CPUWriteMemoryFunc * const *omap_l4_io_writew_fn; +static void **omap_l4_io_opaque; + +int l4_register_io_memory(CPUReadMemoryFunc * const *mem_read, + CPUWriteMemoryFunc * const *mem_write, void *opaque) +{ + omap_l4_io_entry[omap_l4_io_entries].mem_read = mem_read; + omap_l4_io_entry[omap_l4_io_entries].mem_write = mem_write; + omap_l4_io_entry[omap_l4_io_entries].opaque = opaque; + + return omap_l4_io_entries ++; +} + +static uint32_t omap_l4_io_readb(void *opaque, target_phys_addr_t addr) +{ + unsigned int i = (addr - OMAP2_L4_BASE) >> TARGET_PAGE_BITS; + + return omap_l4_io_readb_fn[i](omap_l4_io_opaque[i], addr); +} + +static uint32_t omap_l4_io_readh(void *opaque, target_phys_addr_t addr) +{ + unsigned int i = (addr - OMAP2_L4_BASE) >> TARGET_PAGE_BITS; + + return omap_l4_io_readh_fn[i](omap_l4_io_opaque[i], addr); +} + +static uint32_t omap_l4_io_readw(void *opaque, target_phys_addr_t addr) +{ + unsigned int i = (addr - OMAP2_L4_BASE) >> TARGET_PAGE_BITS; + + return omap_l4_io_readw_fn[i](omap_l4_io_opaque[i], addr); +} + +static void omap_l4_io_writeb(void *opaque, target_phys_addr_t addr, + uint32_t value) +{ + unsigned int i = (addr - OMAP2_L4_BASE) >> TARGET_PAGE_BITS; + + return omap_l4_io_writeb_fn[i](omap_l4_io_opaque[i], addr, value); +} + +static void omap_l4_io_writeh(void *opaque, target_phys_addr_t addr, + uint32_t value) +{ + unsigned int i = (addr - OMAP2_L4_BASE) >> TARGET_PAGE_BITS; + + return omap_l4_io_writeh_fn[i](omap_l4_io_opaque[i], addr, value); +} + +static void omap_l4_io_writew(void *opaque, target_phys_addr_t addr, + uint32_t value) +{ + unsigned int i = (addr - OMAP2_L4_BASE) >> TARGET_PAGE_BITS; + + return omap_l4_io_writew_fn[i](omap_l4_io_opaque[i], addr, value); +} + +static CPUReadMemoryFunc * const omap_l4_io_readfn[] = { + omap_l4_io_readb, + omap_l4_io_readh, + omap_l4_io_readw, +}; + +static CPUWriteMemoryFunc * const omap_l4_io_writefn[] = { + omap_l4_io_writeb, + omap_l4_io_writeh, + omap_l4_io_writew, +}; +#else +int l4_register_io_memory(CPUReadMemoryFunc * const *mem_read, + CPUWriteMemoryFunc * const *mem_write, + void *opaque) +{ + return cpu_register_io_memory(mem_read, mem_write, opaque, + DEVICE_NATIVE_ENDIAN); +} +#endif + +struct omap_l4_s { + target_phys_addr_t base; + int ta_num; + struct omap_target_agent_s ta[0]; +}; + +struct omap_l4_s *omap_l4_init(target_phys_addr_t base, int ta_num) +{ + struct omap_l4_s *bus = qemu_mallocz( + sizeof(*bus) + ta_num * sizeof(*bus->ta)); + + bus->ta_num = ta_num; + bus->base = base; + +#ifdef L4_MUX_HACK + omap_l4_io_entries = 1; + omap_l4_io_entry = qemu_mallocz(125 * sizeof(*omap_l4_io_entry)); + + omap_cpu_io_entry = + cpu_register_io_memory(omap_l4_io_readfn, + omap_l4_io_writefn, bus, DEVICE_NATIVE_ENDIAN); +# define L4_PAGES (0xb4000 / TARGET_PAGE_SIZE) + omap_l4_io_readb_fn = qemu_mallocz(sizeof(void *) * L4_PAGES); + omap_l4_io_readh_fn = qemu_mallocz(sizeof(void *) * L4_PAGES); + omap_l4_io_readw_fn = qemu_mallocz(sizeof(void *) * L4_PAGES); + omap_l4_io_writeb_fn = qemu_mallocz(sizeof(void *) * L4_PAGES); + omap_l4_io_writeh_fn = qemu_mallocz(sizeof(void *) * L4_PAGES); + omap_l4_io_writew_fn = qemu_mallocz(sizeof(void *) * L4_PAGES); + omap_l4_io_opaque = qemu_mallocz(sizeof(void *) * L4_PAGES); +#endif + + return bus; +} + +static uint32_t omap_l4ta_read(void *opaque, target_phys_addr_t addr) +{ + struct omap_target_agent_s *s = (struct omap_target_agent_s *) opaque; + + switch (addr) { + case 0x00: /* COMPONENT */ + return s->component; + + case 0x20: /* AGENT_CONTROL */ + return s->control; + + case 0x28: /* AGENT_STATUS */ + return s->status; + } + + OMAP_BAD_REG(addr); + return 0; +} + +static void omap_l4ta_write(void *opaque, target_phys_addr_t addr, + uint32_t value) +{ + struct omap_target_agent_s *s = (struct omap_target_agent_s *) opaque; + + switch (addr) { + case 0x00: /* COMPONENT */ + case 0x28: /* AGENT_STATUS */ + OMAP_RO_REG(addr); + break; + + case 0x20: /* AGENT_CONTROL */ + s->control = value & 0x01000700; + if (value & 1) /* OCP_RESET */ + s->status &= ~1; /* REQ_TIMEOUT */ + break; + + default: + OMAP_BAD_REG(addr); + } +} + +static CPUReadMemoryFunc * const omap_l4ta_readfn[] = { + omap_badwidth_read16, + omap_l4ta_read, + omap_badwidth_read16, +}; + +static CPUWriteMemoryFunc * const omap_l4ta_writefn[] = { + omap_badwidth_write32, + omap_badwidth_write32, + omap_l4ta_write, +}; + +struct omap_target_agent_s *omap_l4ta_get(struct omap_l4_s *bus, + const struct omap_l4_region_s *regions, + const struct omap_l4_agent_info_s *agents, + int cs) +{ + int i, iomemtype; + struct omap_target_agent_s *ta = NULL; + const struct omap_l4_agent_info_s *info = NULL; + + for (i = 0; i < bus->ta_num; i ++) + if (agents[i].ta == cs) { + ta = &bus->ta[i]; + info = &agents[i]; + break; + } + if (!ta) { + fprintf(stderr, "%s: bad target agent (%i)\n", __FUNCTION__, cs); + exit(-1); + } + + ta->bus = bus; + ta->start = ®ions[info->region]; + ta->regions = info->regions; + + ta->component = ('Q' << 24) | ('E' << 16) | ('M' << 8) | ('U' << 0); + ta->status = 0x00000000; + ta->control = 0x00000200; /* XXX 01000200 for L4TAO */ + + iomemtype = l4_register_io_memory(omap_l4ta_readfn, + omap_l4ta_writefn, ta); + ta->base = omap_l4_attach(ta, info->ta_region, iomemtype); + + return ta; +} + +target_phys_addr_t omap_l4_attach(struct omap_target_agent_s *ta, int region, + int iotype) +{ + target_phys_addr_t base; + ssize_t size; +#ifdef L4_MUX_HACK + int i; +#endif + + if (region < 0 || region >= ta->regions) { + fprintf(stderr, "%s: bad io region (%i)\n", __FUNCTION__, region); + exit(-1); + } + + base = ta->bus->base + ta->start[region].offset; + size = ta->start[region].size; + if (iotype) { +#ifndef L4_MUX_HACK + cpu_register_physical_memory(base, size, iotype); +#else + cpu_register_physical_memory(base, size, omap_cpu_io_entry); + i = (base - ta->bus->base) / TARGET_PAGE_SIZE; + for (; size > 0; size -= TARGET_PAGE_SIZE, i ++) { + omap_l4_io_readb_fn[i] = omap_l4_io_entry[iotype].mem_read[0]; + omap_l4_io_readh_fn[i] = omap_l4_io_entry[iotype].mem_read[1]; + omap_l4_io_readw_fn[i] = omap_l4_io_entry[iotype].mem_read[2]; + omap_l4_io_writeb_fn[i] = omap_l4_io_entry[iotype].mem_write[0]; + omap_l4_io_writeh_fn[i] = omap_l4_io_entry[iotype].mem_write[1]; + omap_l4_io_writew_fn[i] = omap_l4_io_entry[iotype].mem_write[2]; + omap_l4_io_opaque[i] = omap_l4_io_entry[iotype].opaque; + } +#endif + } + + return base; +} diff -Nru qemu-kvm-0.12.5+noroms/hw/omap_lcdc.c qemu-kvm-0.14.1/hw/omap_lcdc.c --- qemu-kvm-0.12.5+noroms/hw/omap_lcdc.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/omap_lcdc.c 2011-05-11 13:29:46.000000000 +0000 @@ -450,7 +450,7 @@ omap_lcdc_reset(s); iomemtype = cpu_register_io_memory(omap_lcdc_readfn, - omap_lcdc_writefn, s); + omap_lcdc_writefn, s, DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base, 0x100, iomemtype); s->state = graphic_console_init(omap_update_display, diff -Nru qemu-kvm-0.12.5+noroms/hw/omap_mmc.c qemu-kvm-0.14.1/hw/omap_mmc.c --- qemu-kvm-0.12.5+noroms/hw/omap_mmc.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/omap_mmc.c 2011-05-11 13:29:46.000000000 +0000 @@ -559,8 +559,9 @@ if (!host->cdet_state && level) { host->status |= 0x0002; omap_mmc_interrupts_update(host); - if (host->cdet_wakeup) - /* TODO: Assert wake-up */; + if (host->cdet_wakeup) { + /* TODO: Assert wake-up */ + } } if (host->cdet_state != level) { @@ -586,7 +587,7 @@ omap_mmc_reset(s); iomemtype = cpu_register_io_memory(omap_mmc_readfn, - omap_mmc_writefn, s); + omap_mmc_writefn, s, DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base, 0x800, iomemtype); /* Instantiate the storage */ diff -Nru qemu-kvm-0.12.5+noroms/hw/omap_sdrc.c qemu-kvm-0.14.1/hw/omap_sdrc.c --- qemu-kvm-0.12.5+noroms/hw/omap_sdrc.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/omap_sdrc.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,165 @@ +/* + * TI OMAP SDRAM controller emulation. + * + * Copyright (C) 2007-2008 Nokia Corporation + * Written by Andrzej Zaborowski + * + * 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 or + * (at your option) any later version of the License. + * + * 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, see . + */ +#include "hw.h" +#include "omap.h" + +/* SDRAM Controller Subsystem */ +struct omap_sdrc_s { + uint8_t config; +}; + +void omap_sdrc_reset(struct omap_sdrc_s *s) +{ + s->config = 0x10; +} + +static uint32_t omap_sdrc_read(void *opaque, target_phys_addr_t addr) +{ + struct omap_sdrc_s *s = (struct omap_sdrc_s *) opaque; + + switch (addr) { + case 0x00: /* SDRC_REVISION */ + return 0x20; + + case 0x10: /* SDRC_SYSCONFIG */ + return s->config; + + case 0x14: /* SDRC_SYSSTATUS */ + return 1; /* RESETDONE */ + + case 0x40: /* SDRC_CS_CFG */ + case 0x44: /* SDRC_SHARING */ + case 0x48: /* SDRC_ERR_ADDR */ + case 0x4c: /* SDRC_ERR_TYPE */ + case 0x60: /* SDRC_DLLA_SCTRL */ + case 0x64: /* SDRC_DLLA_STATUS */ + case 0x68: /* SDRC_DLLB_CTRL */ + case 0x6c: /* SDRC_DLLB_STATUS */ + case 0x70: /* SDRC_POWER */ + case 0x80: /* SDRC_MCFG_0 */ + case 0x84: /* SDRC_MR_0 */ + case 0x88: /* SDRC_EMR1_0 */ + case 0x8c: /* SDRC_EMR2_0 */ + case 0x90: /* SDRC_EMR3_0 */ + case 0x94: /* SDRC_DCDL1_CTRL */ + case 0x98: /* SDRC_DCDL2_CTRL */ + case 0x9c: /* SDRC_ACTIM_CTRLA_0 */ + case 0xa0: /* SDRC_ACTIM_CTRLB_0 */ + case 0xa4: /* SDRC_RFR_CTRL_0 */ + case 0xa8: /* SDRC_MANUAL_0 */ + case 0xb0: /* SDRC_MCFG_1 */ + case 0xb4: /* SDRC_MR_1 */ + case 0xb8: /* SDRC_EMR1_1 */ + case 0xbc: /* SDRC_EMR2_1 */ + case 0xc0: /* SDRC_EMR3_1 */ + case 0xc4: /* SDRC_ACTIM_CTRLA_1 */ + case 0xc8: /* SDRC_ACTIM_CTRLB_1 */ + case 0xd4: /* SDRC_RFR_CTRL_1 */ + case 0xd8: /* SDRC_MANUAL_1 */ + return 0x00; + } + + OMAP_BAD_REG(addr); + return 0; +} + +static void omap_sdrc_write(void *opaque, target_phys_addr_t addr, + uint32_t value) +{ + struct omap_sdrc_s *s = (struct omap_sdrc_s *) opaque; + + switch (addr) { + case 0x00: /* SDRC_REVISION */ + case 0x14: /* SDRC_SYSSTATUS */ + case 0x48: /* SDRC_ERR_ADDR */ + case 0x64: /* SDRC_DLLA_STATUS */ + case 0x6c: /* SDRC_DLLB_STATUS */ + OMAP_RO_REG(addr); + return; + + case 0x10: /* SDRC_SYSCONFIG */ + if ((value >> 3) != 0x2) + fprintf(stderr, "%s: bad SDRAM idle mode %i\n", + __FUNCTION__, value >> 3); + if (value & 2) + omap_sdrc_reset(s); + s->config = value & 0x18; + break; + + case 0x40: /* SDRC_CS_CFG */ + case 0x44: /* SDRC_SHARING */ + case 0x4c: /* SDRC_ERR_TYPE */ + case 0x60: /* SDRC_DLLA_SCTRL */ + case 0x68: /* SDRC_DLLB_CTRL */ + case 0x70: /* SDRC_POWER */ + case 0x80: /* SDRC_MCFG_0 */ + case 0x84: /* SDRC_MR_0 */ + case 0x88: /* SDRC_EMR1_0 */ + case 0x8c: /* SDRC_EMR2_0 */ + case 0x90: /* SDRC_EMR3_0 */ + case 0x94: /* SDRC_DCDL1_CTRL */ + case 0x98: /* SDRC_DCDL2_CTRL */ + case 0x9c: /* SDRC_ACTIM_CTRLA_0 */ + case 0xa0: /* SDRC_ACTIM_CTRLB_0 */ + case 0xa4: /* SDRC_RFR_CTRL_0 */ + case 0xa8: /* SDRC_MANUAL_0 */ + case 0xb0: /* SDRC_MCFG_1 */ + case 0xb4: /* SDRC_MR_1 */ + case 0xb8: /* SDRC_EMR1_1 */ + case 0xbc: /* SDRC_EMR2_1 */ + case 0xc0: /* SDRC_EMR3_1 */ + case 0xc4: /* SDRC_ACTIM_CTRLA_1 */ + case 0xc8: /* SDRC_ACTIM_CTRLB_1 */ + case 0xd4: /* SDRC_RFR_CTRL_1 */ + case 0xd8: /* SDRC_MANUAL_1 */ + break; + + default: + OMAP_BAD_REG(addr); + return; + } +} + +static CPUReadMemoryFunc * const omap_sdrc_readfn[] = { + omap_badwidth_read32, + omap_badwidth_read32, + omap_sdrc_read, +}; + +static CPUWriteMemoryFunc * const omap_sdrc_writefn[] = { + omap_badwidth_write32, + omap_badwidth_write32, + omap_sdrc_write, +}; + +struct omap_sdrc_s *omap_sdrc_init(target_phys_addr_t base) +{ + int iomemtype; + struct omap_sdrc_s *s = (struct omap_sdrc_s *) + qemu_mallocz(sizeof(struct omap_sdrc_s)); + + omap_sdrc_reset(s); + + iomemtype = cpu_register_io_memory(omap_sdrc_readfn, + omap_sdrc_writefn, s, DEVICE_NATIVE_ENDIAN); + cpu_register_physical_memory(base, 0x1000, iomemtype); + + return s; +} diff -Nru qemu-kvm-0.12.5+noroms/hw/omap_spi.c qemu-kvm-0.14.1/hw/omap_spi.c --- qemu-kvm-0.12.5+noroms/hw/omap_spi.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/omap_spi.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,346 @@ +/* + * TI OMAP processor's Multichannel SPI emulation. + * + * Copyright (C) 2007-2009 Nokia Corporation + * + * Original code for OMAP2 by Andrzej Zaborowski + * + * 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 or + * (at your option) any later version of the License. + * + * 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. + */ +#include "hw.h" +#include "omap.h" + +/* Multichannel SPI */ +struct omap_mcspi_s { + qemu_irq irq; + int chnum; + + uint32_t sysconfig; + uint32_t systest; + uint32_t irqst; + uint32_t irqen; + uint32_t wken; + uint32_t control; + + struct omap_mcspi_ch_s { + qemu_irq txdrq; + qemu_irq rxdrq; + uint32_t (*txrx)(void *opaque, uint32_t, int); + void *opaque; + + uint32_t tx; + uint32_t rx; + + uint32_t config; + uint32_t status; + uint32_t control; + } ch[4]; +}; + +static inline void omap_mcspi_interrupt_update(struct omap_mcspi_s *s) +{ + qemu_set_irq(s->irq, s->irqst & s->irqen); +} + +static inline void omap_mcspi_dmarequest_update(struct omap_mcspi_ch_s *ch) +{ + qemu_set_irq(ch->txdrq, + (ch->control & 1) && /* EN */ + (ch->config & (1 << 14)) && /* DMAW */ + (ch->status & (1 << 1)) && /* TXS */ + ((ch->config >> 12) & 3) != 1); /* TRM */ + qemu_set_irq(ch->rxdrq, + (ch->control & 1) && /* EN */ + (ch->config & (1 << 15)) && /* DMAW */ + (ch->status & (1 << 0)) && /* RXS */ + ((ch->config >> 12) & 3) != 2); /* TRM */ +} + +static void omap_mcspi_transfer_run(struct omap_mcspi_s *s, int chnum) +{ + struct omap_mcspi_ch_s *ch = s->ch + chnum; + + if (!(ch->control & 1)) /* EN */ + return; + if ((ch->status & (1 << 0)) && /* RXS */ + ((ch->config >> 12) & 3) != 2 && /* TRM */ + !(ch->config & (1 << 19))) /* TURBO */ + goto intr_update; + if ((ch->status & (1 << 1)) && /* TXS */ + ((ch->config >> 12) & 3) != 1) /* TRM */ + goto intr_update; + + if (!(s->control & 1) || /* SINGLE */ + (ch->config & (1 << 20))) { /* FORCE */ + if (ch->txrx) + ch->rx = ch->txrx(ch->opaque, ch->tx, /* WL */ + 1 + (0x1f & (ch->config >> 7))); + } + + ch->tx = 0; + ch->status |= 1 << 2; /* EOT */ + ch->status |= 1 << 1; /* TXS */ + if (((ch->config >> 12) & 3) != 2) /* TRM */ + ch->status |= 1 << 0; /* RXS */ + +intr_update: + if ((ch->status & (1 << 0)) && /* RXS */ + ((ch->config >> 12) & 3) != 2 && /* TRM */ + !(ch->config & (1 << 19))) /* TURBO */ + s->irqst |= 1 << (2 + 4 * chnum); /* RX_FULL */ + if ((ch->status & (1 << 1)) && /* TXS */ + ((ch->config >> 12) & 3) != 1) /* TRM */ + s->irqst |= 1 << (0 + 4 * chnum); /* TX_EMPTY */ + omap_mcspi_interrupt_update(s); + omap_mcspi_dmarequest_update(ch); +} + +void omap_mcspi_reset(struct omap_mcspi_s *s) +{ + int ch; + + s->sysconfig = 0; + s->systest = 0; + s->irqst = 0; + s->irqen = 0; + s->wken = 0; + s->control = 4; + + for (ch = 0; ch < 4; ch ++) { + s->ch[ch].config = 0x060000; + s->ch[ch].status = 2; /* TXS */ + s->ch[ch].control = 0; + + omap_mcspi_dmarequest_update(s->ch + ch); + } + + omap_mcspi_interrupt_update(s); +} + +static uint32_t omap_mcspi_read(void *opaque, target_phys_addr_t addr) +{ + struct omap_mcspi_s *s = (struct omap_mcspi_s *) opaque; + int ch = 0; + uint32_t ret; + + switch (addr) { + case 0x00: /* MCSPI_REVISION */ + return 0x91; + + case 0x10: /* MCSPI_SYSCONFIG */ + return s->sysconfig; + + case 0x14: /* MCSPI_SYSSTATUS */ + return 1; /* RESETDONE */ + + case 0x18: /* MCSPI_IRQSTATUS */ + return s->irqst; + + case 0x1c: /* MCSPI_IRQENABLE */ + return s->irqen; + + case 0x20: /* MCSPI_WAKEUPENABLE */ + return s->wken; + + case 0x24: /* MCSPI_SYST */ + return s->systest; + + case 0x28: /* MCSPI_MODULCTRL */ + return s->control; + + case 0x68: ch ++; + case 0x54: ch ++; + case 0x40: ch ++; + case 0x2c: /* MCSPI_CHCONF */ + return s->ch[ch].config; + + case 0x6c: ch ++; + case 0x58: ch ++; + case 0x44: ch ++; + case 0x30: /* MCSPI_CHSTAT */ + return s->ch[ch].status; + + case 0x70: ch ++; + case 0x5c: ch ++; + case 0x48: ch ++; + case 0x34: /* MCSPI_CHCTRL */ + return s->ch[ch].control; + + case 0x74: ch ++; + case 0x60: ch ++; + case 0x4c: ch ++; + case 0x38: /* MCSPI_TX */ + return s->ch[ch].tx; + + case 0x78: ch ++; + case 0x64: ch ++; + case 0x50: ch ++; + case 0x3c: /* MCSPI_RX */ + s->ch[ch].status &= ~(1 << 0); /* RXS */ + ret = s->ch[ch].rx; + omap_mcspi_transfer_run(s, ch); + return ret; + } + + OMAP_BAD_REG(addr); + return 0; +} + +static void omap_mcspi_write(void *opaque, target_phys_addr_t addr, + uint32_t value) +{ + struct omap_mcspi_s *s = (struct omap_mcspi_s *) opaque; + int ch = 0; + + switch (addr) { + case 0x00: /* MCSPI_REVISION */ + case 0x14: /* MCSPI_SYSSTATUS */ + case 0x30: /* MCSPI_CHSTAT0 */ + case 0x3c: /* MCSPI_RX0 */ + case 0x44: /* MCSPI_CHSTAT1 */ + case 0x50: /* MCSPI_RX1 */ + case 0x58: /* MCSPI_CHSTAT2 */ + case 0x64: /* MCSPI_RX2 */ + case 0x6c: /* MCSPI_CHSTAT3 */ + case 0x78: /* MCSPI_RX3 */ + OMAP_RO_REG(addr); + return; + + case 0x10: /* MCSPI_SYSCONFIG */ + if (value & (1 << 1)) /* SOFTRESET */ + omap_mcspi_reset(s); + s->sysconfig = value & 0x31d; + break; + + case 0x18: /* MCSPI_IRQSTATUS */ + if (!((s->control & (1 << 3)) && (s->systest & (1 << 11)))) { + s->irqst &= ~value; + omap_mcspi_interrupt_update(s); + } + break; + + case 0x1c: /* MCSPI_IRQENABLE */ + s->irqen = value & 0x1777f; + omap_mcspi_interrupt_update(s); + break; + + case 0x20: /* MCSPI_WAKEUPENABLE */ + s->wken = value & 1; + break; + + case 0x24: /* MCSPI_SYST */ + if (s->control & (1 << 3)) /* SYSTEM_TEST */ + if (value & (1 << 11)) { /* SSB */ + s->irqst |= 0x1777f; + omap_mcspi_interrupt_update(s); + } + s->systest = value & 0xfff; + break; + + case 0x28: /* MCSPI_MODULCTRL */ + if (value & (1 << 3)) /* SYSTEM_TEST */ + if (s->systest & (1 << 11)) { /* SSB */ + s->irqst |= 0x1777f; + omap_mcspi_interrupt_update(s); + } + s->control = value & 0xf; + break; + + case 0x68: ch ++; + case 0x54: ch ++; + case 0x40: ch ++; + case 0x2c: /* MCSPI_CHCONF */ + if ((value ^ s->ch[ch].config) & (3 << 14)) /* DMAR | DMAW */ + omap_mcspi_dmarequest_update(s->ch + ch); + if (((value >> 12) & 3) == 3) /* TRM */ + fprintf(stderr, "%s: invalid TRM value (3)\n", __FUNCTION__); + if (((value >> 7) & 0x1f) < 3) /* WL */ + fprintf(stderr, "%s: invalid WL value (%i)\n", + __FUNCTION__, (value >> 7) & 0x1f); + s->ch[ch].config = value & 0x7fffff; + break; + + case 0x70: ch ++; + case 0x5c: ch ++; + case 0x48: ch ++; + case 0x34: /* MCSPI_CHCTRL */ + if (value & ~s->ch[ch].control & 1) { /* EN */ + s->ch[ch].control |= 1; + omap_mcspi_transfer_run(s, ch); + } else + s->ch[ch].control = value & 1; + break; + + case 0x74: ch ++; + case 0x60: ch ++; + case 0x4c: ch ++; + case 0x38: /* MCSPI_TX */ + s->ch[ch].tx = value; + s->ch[ch].status &= ~(1 << 1); /* TXS */ + omap_mcspi_transfer_run(s, ch); + break; + + default: + OMAP_BAD_REG(addr); + return; + } +} + +static CPUReadMemoryFunc * const omap_mcspi_readfn[] = { + omap_badwidth_read32, + omap_badwidth_read32, + omap_mcspi_read, +}; + +static CPUWriteMemoryFunc * const omap_mcspi_writefn[] = { + omap_badwidth_write32, + omap_badwidth_write32, + omap_mcspi_write, +}; + +struct omap_mcspi_s *omap_mcspi_init(struct omap_target_agent_s *ta, int chnum, + qemu_irq irq, qemu_irq *drq, omap_clk fclk, omap_clk iclk) +{ + int iomemtype; + struct omap_mcspi_s *s = (struct omap_mcspi_s *) + qemu_mallocz(sizeof(struct omap_mcspi_s)); + struct omap_mcspi_ch_s *ch = s->ch; + + s->irq = irq; + s->chnum = chnum; + while (chnum --) { + ch->txdrq = *drq ++; + ch->rxdrq = *drq ++; + ch ++; + } + omap_mcspi_reset(s); + + iomemtype = l4_register_io_memory(omap_mcspi_readfn, + omap_mcspi_writefn, s); + omap_l4_attach(ta, 0, iomemtype); + + return s; +} + +void omap_mcspi_attach(struct omap_mcspi_s *s, + uint32_t (*txrx)(void *opaque, uint32_t, int), void *opaque, + int chipselect) +{ + if (chipselect < 0 || chipselect >= s->chnum) + hw_error("%s: Bad chipselect %i\n", __FUNCTION__, chipselect); + + s->ch[chipselect].txrx = txrx; + s->ch[chipselect].opaque = opaque; +} diff -Nru qemu-kvm-0.12.5+noroms/hw/omap_sx1.c qemu-kvm-0.14.1/hw/omap_sx1.c --- qemu-kvm-0.12.5+noroms/hw/omap_sx1.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/omap_sx1.c 2011-05-11 13:29:46.000000000 +0000 @@ -32,6 +32,7 @@ #include "boards.h" #include "arm-misc.h" #include "flash.h" +#include "blockdev.h" /*****************************************************************************/ /* Siemens SX1 Cellphone V1 */ @@ -126,10 +127,10 @@ static uint32_t cs1val = 0x00215070; static uint32_t cs2val = 0x00001139; static uint32_t cs3val = 0x00001139; - ram_addr_t phys_flash; DriveInfo *dinfo; int fl_idx; uint32_t flash_size = flash0_size; + int be; if (version == 2) { flash_size = flash2_size; @@ -139,22 +140,33 @@ /* External Flash (EMIFS) */ cpu_register_physical_memory(OMAP_CS0_BASE, flash_size, - (phys_flash = qemu_ram_alloc(flash_size)) | IO_MEM_ROM); + qemu_ram_alloc(NULL, "omap_sx1.flash0-0", + flash_size) | IO_MEM_ROM); - io = cpu_register_io_memory(static_readfn, static_writefn, &cs0val); + io = cpu_register_io_memory(static_readfn, static_writefn, &cs0val, + DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(OMAP_CS0_BASE + flash_size, OMAP_CS0_SIZE - flash_size, io); - io = cpu_register_io_memory(static_readfn, static_writefn, &cs2val); + io = cpu_register_io_memory(static_readfn, static_writefn, &cs2val, + DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(OMAP_CS2_BASE, OMAP_CS2_SIZE, io); - io = cpu_register_io_memory(static_readfn, static_writefn, &cs3val); + io = cpu_register_io_memory(static_readfn, static_writefn, &cs3val, + DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(OMAP_CS3_BASE, OMAP_CS3_SIZE, io); fl_idx = 0; +#ifdef TARGET_WORDS_BIGENDIAN + be = 1; +#else + be = 0; +#endif if ((dinfo = drive_get(IF_PFLASH, 0, fl_idx)) != NULL) { - if (!pflash_cfi01_register(OMAP_CS0_BASE, qemu_ram_alloc(flash_size), - dinfo->bdrv, sector_size, flash_size / sector_size, - 4, 0, 0, 0, 0)) { + if (!pflash_cfi01_register(OMAP_CS0_BASE, qemu_ram_alloc(NULL, + "omap_sx1.flash0-1", flash_size), + dinfo->bdrv, sector_size, + flash_size / sector_size, + 4, 0, 0, 0, 0, be)) { fprintf(stderr, "qemu: Error registering flash memory %d.\n", fl_idx); } @@ -164,21 +176,25 @@ if ((version == 1) && (dinfo = drive_get(IF_PFLASH, 0, fl_idx)) != NULL) { cpu_register_physical_memory(OMAP_CS1_BASE, flash1_size, - (phys_flash = qemu_ram_alloc(flash1_size)) | - IO_MEM_ROM); - io = cpu_register_io_memory(static_readfn, static_writefn, &cs1val); + qemu_ram_alloc(NULL, "omap_sx1.flash1-0", + flash1_size) | IO_MEM_ROM); + io = cpu_register_io_memory(static_readfn, static_writefn, &cs1val, + DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(OMAP_CS1_BASE + flash1_size, OMAP_CS1_SIZE - flash1_size, io); - if (!pflash_cfi01_register(OMAP_CS1_BASE, qemu_ram_alloc(flash1_size), - dinfo->bdrv, sector_size, flash1_size / sector_size, - 4, 0, 0, 0, 0)) { + if (!pflash_cfi01_register(OMAP_CS1_BASE, qemu_ram_alloc(NULL, + "omap_sx1.flash1-1", flash1_size), + dinfo->bdrv, sector_size, + flash1_size / sector_size, + 4, 0, 0, 0, 0, be)) { fprintf(stderr, "qemu: Error registering flash memory %d.\n", fl_idx); } fl_idx++; } else { - io = cpu_register_io_memory(static_readfn, static_writefn, &cs1val); + io = cpu_register_io_memory(static_readfn, static_writefn, &cs1val, + DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(OMAP_CS1_BASE, OMAP_CS1_SIZE, io); } @@ -189,15 +205,10 @@ /* Load the kernel. */ if (kernel_filename) { - /* Start at bootloader. */ - cpu->env->regs[15] = sx1_binfo.loader_start; - sx1_binfo.kernel_filename = kernel_filename; sx1_binfo.kernel_cmdline = kernel_cmdline; sx1_binfo.initrd_filename = initrd_filename; arm_load_kernel(cpu->env, &sx1_binfo); - } else { - cpu->env->regs[15] = 0x00000000; } /* TODO: fix next line */ diff -Nru qemu-kvm-0.12.5+noroms/hw/omap_synctimer.c qemu-kvm-0.14.1/hw/omap_synctimer.c --- qemu-kvm-0.12.5+noroms/hw/omap_synctimer.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/omap_synctimer.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,96 @@ +/* + * TI OMAP2 32kHz sync timer emulation. + * + * Copyright (C) 2007-2008 Nokia Corporation + * Written by Andrzej Zaborowski + * + * 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 or + * (at your option) any later version of the License. + * + * 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, see . + */ +#include "hw.h" +#include "qemu-timer.h" +#include "omap.h" +struct omap_synctimer_s { + uint32_t val; + uint16_t readh; +}; + +/* 32-kHz Sync Timer of the OMAP2 */ +static uint32_t omap_synctimer_read(struct omap_synctimer_s *s) { + return muldiv64(qemu_get_clock(vm_clock), 0x8000, get_ticks_per_sec()); +} + +void omap_synctimer_reset(struct omap_synctimer_s *s) +{ + s->val = omap_synctimer_read(s); +} + +static uint32_t omap_synctimer_readw(void *opaque, target_phys_addr_t addr) +{ + struct omap_synctimer_s *s = (struct omap_synctimer_s *) opaque; + + switch (addr) { + case 0x00: /* 32KSYNCNT_REV */ + return 0x21; + + case 0x10: /* CR */ + return omap_synctimer_read(s) - s->val; + } + + OMAP_BAD_REG(addr); + return 0; +} + +static uint32_t omap_synctimer_readh(void *opaque, target_phys_addr_t addr) +{ + struct omap_synctimer_s *s = (struct omap_synctimer_s *) opaque; + uint32_t ret; + + if (addr & 2) + return s->readh; + else { + ret = omap_synctimer_readw(opaque, addr); + s->readh = ret >> 16; + return ret & 0xffff; + } +} + +static CPUReadMemoryFunc * const omap_synctimer_readfn[] = { + omap_badwidth_read32, + omap_synctimer_readh, + omap_synctimer_readw, +}; + +static void omap_synctimer_write(void *opaque, target_phys_addr_t addr, + uint32_t value) +{ + OMAP_BAD_REG(addr); +} + +static CPUWriteMemoryFunc * const omap_synctimer_writefn[] = { + omap_badwidth_write32, + omap_synctimer_write, + omap_synctimer_write, +}; + +struct omap_synctimer_s *omap_synctimer_init(struct omap_target_agent_s *ta, + struct omap_mpu_state_s *mpu, omap_clk fclk, omap_clk iclk) +{ + struct omap_synctimer_s *s = qemu_mallocz(sizeof(*s)); + + omap_synctimer_reset(s); + omap_l4_attach(ta, 0, l4_register_io_memory( + omap_synctimer_readfn, omap_synctimer_writefn, s)); + + return s; +} diff -Nru qemu-kvm-0.12.5+noroms/hw/omap_tap.c qemu-kvm-0.14.1/hw/omap_tap.c --- qemu-kvm-0.12.5+noroms/hw/omap_tap.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/omap_tap.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,112 @@ +/* + * TI OMAP TEST-Chip-level TAP emulation. + * + * Copyright (C) 2007-2008 Nokia Corporation + * Written by Andrzej Zaborowski + * + * 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 or + * (at your option) any later version of the License. + * + * 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, see . + */ + +#include "hw.h" +#include "omap.h" + +/* TEST-Chip-level TAP */ +static uint32_t omap_tap_read(void *opaque, target_phys_addr_t addr) +{ + struct omap_mpu_state_s *s = (struct omap_mpu_state_s *) opaque; + + switch (addr) { + case 0x204: /* IDCODE_reg */ + switch (s->mpu_model) { + case omap2420: + case omap2422: + case omap2423: + return 0x5b5d902f; /* ES 2.2 */ + case omap2430: + return 0x5b68a02f; /* ES 2.2 */ + case omap3430: + return 0x1b7ae02f; /* ES 2 */ + default: + hw_error("%s: Bad mpu model\n", __FUNCTION__); + } + + case 0x208: /* PRODUCTION_ID_reg for OMAP2 */ + case 0x210: /* PRODUCTION_ID_reg for OMAP3 */ + switch (s->mpu_model) { + case omap2420: + return 0x000254f0; /* POP ESHS2.1.1 in N91/93/95, ES2 in N800 */ + case omap2422: + return 0x000400f0; + case omap2423: + return 0x000800f0; + case omap2430: + return 0x000000f0; + case omap3430: + return 0x000000f0; + default: + hw_error("%s: Bad mpu model\n", __FUNCTION__); + } + + case 0x20c: + switch (s->mpu_model) { + case omap2420: + case omap2422: + case omap2423: + return 0xcafeb5d9; /* ES 2.2 */ + case omap2430: + return 0xcafeb68a; /* ES 2.2 */ + case omap3430: + return 0xcafeb7ae; /* ES 2 */ + default: + hw_error("%s: Bad mpu model\n", __FUNCTION__); + } + + case 0x218: /* DIE_ID_reg */ + return ('Q' << 24) | ('E' << 16) | ('M' << 8) | ('U' << 0); + case 0x21c: /* DIE_ID_reg */ + return 0x54 << 24; + case 0x220: /* DIE_ID_reg */ + return ('Q' << 24) | ('E' << 16) | ('M' << 8) | ('U' << 0); + case 0x224: /* DIE_ID_reg */ + return ('Q' << 24) | ('E' << 16) | ('M' << 8) | ('U' << 0); + } + + OMAP_BAD_REG(addr); + return 0; +} + +static void omap_tap_write(void *opaque, target_phys_addr_t addr, + uint32_t value) +{ + OMAP_BAD_REG(addr); +} + +static CPUReadMemoryFunc * const omap_tap_readfn[] = { + omap_badwidth_read32, + omap_badwidth_read32, + omap_tap_read, +}; + +static CPUWriteMemoryFunc * const omap_tap_writefn[] = { + omap_badwidth_write32, + omap_badwidth_write32, + omap_tap_write, +}; + +void omap_tap_init(struct omap_target_agent_s *ta, + struct omap_mpu_state_s *mpu) +{ + omap_l4_attach(ta, 0, l4_register_io_memory( + omap_tap_readfn, omap_tap_writefn, mpu)); +} diff -Nru qemu-kvm-0.12.5+noroms/hw/omap_uart.c qemu-kvm-0.14.1/hw/omap_uart.c --- qemu-kvm-0.12.5+noroms/hw/omap_uart.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/omap_uart.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,196 @@ +/* + * TI OMAP processors UART emulation. + * + * Copyright (C) 2006-2008 Andrzej Zaborowski + * Copyright (C) 2007-2009 Nokia Corporation + * + * 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 or + * (at your option) version 3 of the License. + * + * 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, see . + */ +#include "qemu-char.h" +#include "hw.h" +#include "omap.h" +/* We use pc-style serial ports. */ +#include "pc.h" + +/* UARTs */ +struct omap_uart_s { + target_phys_addr_t base; + SerialState *serial; /* TODO */ + struct omap_target_agent_s *ta; + omap_clk fclk; + qemu_irq irq; + + uint8_t eblr; + uint8_t syscontrol; + uint8_t wkup; + uint8_t cfps; + uint8_t mdr[2]; + uint8_t scr; + uint8_t clksel; +}; + +void omap_uart_reset(struct omap_uart_s *s) +{ + s->eblr = 0x00; + s->syscontrol = 0; + s->wkup = 0x3f; + s->cfps = 0x69; + s->clksel = 0; +} + +struct omap_uart_s *omap_uart_init(target_phys_addr_t base, + qemu_irq irq, omap_clk fclk, omap_clk iclk, + qemu_irq txdma, qemu_irq rxdma, + const char *label, CharDriverState *chr) +{ + struct omap_uart_s *s = (struct omap_uart_s *) + qemu_mallocz(sizeof(struct omap_uart_s)); + + s->base = base; + s->fclk = fclk; + s->irq = irq; +#ifdef TARGET_WORDS_BIGENDIAN + s->serial = serial_mm_init(base, 2, irq, omap_clk_getrate(fclk)/16, + chr ?: qemu_chr_open(label, "null", NULL), 1, + 1); +#else + s->serial = serial_mm_init(base, 2, irq, omap_clk_getrate(fclk)/16, + chr ?: qemu_chr_open(label, "null", NULL), 1, + 0); +#endif + return s; +} + +static uint32_t omap_uart_read(void *opaque, target_phys_addr_t addr) +{ + struct omap_uart_s *s = (struct omap_uart_s *) opaque; + + addr &= 0xff; + switch (addr) { + case 0x20: /* MDR1 */ + return s->mdr[0]; + case 0x24: /* MDR2 */ + return s->mdr[1]; + case 0x40: /* SCR */ + return s->scr; + case 0x44: /* SSR */ + return 0x0; + case 0x48: /* EBLR (OMAP2) */ + return s->eblr; + case 0x4C: /* OSC_12M_SEL (OMAP1) */ + return s->clksel; + case 0x50: /* MVR */ + return 0x30; + case 0x54: /* SYSC (OMAP2) */ + return s->syscontrol; + case 0x58: /* SYSS (OMAP2) */ + return 1; + case 0x5c: /* WER (OMAP2) */ + return s->wkup; + case 0x60: /* CFPS (OMAP2) */ + return s->cfps; + } + + OMAP_BAD_REG(addr); + return 0; +} + +static void omap_uart_write(void *opaque, target_phys_addr_t addr, + uint32_t value) +{ + struct omap_uart_s *s = (struct omap_uart_s *) opaque; + + addr &= 0xff; + switch (addr) { + case 0x20: /* MDR1 */ + s->mdr[0] = value & 0x7f; + break; + case 0x24: /* MDR2 */ + s->mdr[1] = value & 0xff; + break; + case 0x40: /* SCR */ + s->scr = value & 0xff; + break; + case 0x48: /* EBLR (OMAP2) */ + s->eblr = value & 0xff; + break; + case 0x4C: /* OSC_12M_SEL (OMAP1) */ + s->clksel = value & 1; + break; + case 0x44: /* SSR */ + case 0x50: /* MVR */ + case 0x58: /* SYSS (OMAP2) */ + OMAP_RO_REG(addr); + break; + case 0x54: /* SYSC (OMAP2) */ + s->syscontrol = value & 0x1d; + if (value & 2) + omap_uart_reset(s); + break; + case 0x5c: /* WER (OMAP2) */ + s->wkup = value & 0x7f; + break; + case 0x60: /* CFPS (OMAP2) */ + s->cfps = value & 0xff; + break; + default: + OMAP_BAD_REG(addr); + } +} + +static CPUReadMemoryFunc * const omap_uart_readfn[] = { + omap_uart_read, + omap_uart_read, + omap_badwidth_read8, +}; + +static CPUWriteMemoryFunc * const omap_uart_writefn[] = { + omap_uart_write, + omap_uart_write, + omap_badwidth_write8, +}; + +struct omap_uart_s *omap2_uart_init(struct omap_target_agent_s *ta, + qemu_irq irq, omap_clk fclk, omap_clk iclk, + qemu_irq txdma, qemu_irq rxdma, + const char *label, CharDriverState *chr) +{ + target_phys_addr_t base = omap_l4_attach(ta, 0, 0); + struct omap_uart_s *s = omap_uart_init(base, irq, + fclk, iclk, txdma, rxdma, label, chr); + int iomemtype = cpu_register_io_memory(omap_uart_readfn, + omap_uart_writefn, s, DEVICE_NATIVE_ENDIAN); + + s->ta = ta; + + cpu_register_physical_memory(base + 0x20, 0x100, iomemtype); + + return s; +} + +void omap_uart_attach(struct omap_uart_s *s, CharDriverState *chr) +{ + /* TODO: Should reuse or destroy current s->serial */ +#ifdef TARGET_WORDS_BIGENDIAN + s->serial = serial_mm_init(s->base, 2, s->irq, + omap_clk_getrate(s->fclk) / 16, + chr ?: qemu_chr_open("null", "null", NULL), 1, + 1); +#else + s->serial = serial_mm_init(s->base, 2, s->irq, + omap_clk_getrate(s->fclk) / 16, + chr ?: qemu_chr_open("null", "null", NULL), 1, + 0); +#endif +} diff -Nru qemu-kvm-0.12.5+noroms/hw/onenand.c qemu-kvm-0.14.1/hw/onenand.c --- qemu-kvm-0.12.5+noroms/hw/onenand.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/onenand.c 2011-05-11 13:29:46.000000000 +0000 @@ -19,10 +19,10 @@ */ #include "qemu-common.h" +#include "hw.h" #include "flash.h" #include "irq.h" -#include "sysemu.h" -#include "block.h" +#include "blockdev.h" /* 11 for 2kB-page OneNAND ("2nd generation") and 10 for 1kB-page chips */ #define PAGE_SHIFT 11 @@ -631,7 +631,7 @@ s->blockwp = qemu_malloc(s->blocks); s->density_mask = (id & (1 << 11)) ? (1 << (6 + ((id >> 12) & 7))) : 0; s->iomemtype = cpu_register_io_memory(onenand_readfn, - onenand_writefn, s); + onenand_writefn, s, DEVICE_NATIVE_ENDIAN); if (!dinfo) s->image = memset(qemu_malloc(size + (size >> 5)), 0xff, size + (size >> 5)); @@ -639,7 +639,7 @@ s->bdrv = dinfo->bdrv; s->otp = memset(qemu_malloc((64 + 2) << PAGE_SHIFT), 0xff, (64 + 2) << PAGE_SHIFT); - s->ram = qemu_ram_alloc(0xc000 << s->shift); + s->ram = qemu_ram_alloc(NULL, "onenand.ram", 0xc000 << s->shift); ram = qemu_get_ram_ptr(s->ram); s->boot[0] = ram + (0x0000 << s->shift); s->boot[1] = ram + (0x8000 << s->shift); diff -Nru qemu-kvm-0.12.5+noroms/hw/openpic.c qemu-kvm-0.14.1/hw/openpic.c --- qemu-kvm-0.12.5+noroms/hw/openpic.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/openpic.c 2011-05-11 13:29:46.000000000 +0000 @@ -141,6 +141,8 @@ #error "Please select which OpenPic implementation is to be emulated" #endif +#define OPENPIC_PAGE_SIZE 4096 + #define BF_WIDTH(_bits_) \ (((_bits_) + (sizeof(uint32_t) * 8) - 1) / (sizeof(uint32_t) * 8)) @@ -219,20 +221,20 @@ int nb_cpus; /* Timer registers */ struct { - uint32_t ticc; /* Global timer current count register */ - uint32_t tibc; /* Global timer base count register */ + uint32_t ticc; /* Global timer current count register */ + uint32_t tibc; /* Global timer base count register */ } timers[MAX_TMR]; #if MAX_DBL > 0 /* Doorbell registers */ uint32_t dar; /* Doorbell activate register */ struct { - uint32_t dmr; /* Doorbell messaging register */ + uint32_t dmr; /* Doorbell messaging register */ } doorbells[MAX_DBL]; #endif #if MAX_MBX > 0 /* Mailbox registers */ struct { - uint32_t mbr; /* Mailbox register */ + uint32_t mbr; /* Mailbox register */ } mailboxes[MAX_MAILBOXES]; #endif /* IRQ out is used when in bypass mode (not implemented) */ @@ -240,19 +242,10 @@ int max_irq; int irq_ipi0; int irq_tim0; - int need_swap; void (*reset) (void *); void (*irq_raise) (struct openpic_t *, int, IRQ_src_t *); } openpic_t; -static inline uint32_t openpic_swap32(openpic_t *opp, uint32_t val) -{ - if (opp->need_swap) - return bswap32(val); - - return val; -} - static inline void IRQ_setbit (IRQ_queue_t *q, int n_IRQ) { set_bit(q->queue, n_IRQ); @@ -276,14 +269,14 @@ next = -1; priority = -1; for (i = 0; i < opp->max_irq; i++) { - if (IRQ_testbit(q, i)) { + if (IRQ_testbit(q, i)) { DPRINTF("IRQ_check: irq %d set ipvp_pr=%d pr=%d\n", i, IPVP_PRIORITY(opp->src[i].ipvp), priority); - if (IPVP_PRIORITY(opp->src[i].ipvp) > priority) { - next = i; - priority = IPVP_PRIORITY(opp->src[i].ipvp); - } - } + if (IPVP_PRIORITY(opp->src[i].ipvp) > priority) { + next = i; + priority = IPVP_PRIORITY(opp->src[i].ipvp); + } + } } q->next = next; q->priority = priority; @@ -293,7 +286,7 @@ { if (q->next == -1) { /* XXX: optimize */ - IRQ_check(opp, q); + IRQ_check(opp, q); } return q->next; @@ -309,16 +302,16 @@ src = &opp->src[n_IRQ]; priority = IPVP_PRIORITY(src->ipvp); if (priority <= dst->pctp) { - /* Too low priority */ + /* Too low priority */ DPRINTF("%s: IRQ %d has too low priority on CPU %d\n", __func__, n_IRQ, n_CPU); - return; + return; } if (IRQ_testbit(&dst->raised, n_IRQ)) { - /* Interrupt miss */ + /* Interrupt miss */ DPRINTF("%s: IRQ %d was missed on CPU %d\n", __func__, n_IRQ, n_CPU); - return; + return; } set_bit(&src->ipvp, IPVP_ACTIVITY); IRQ_setbit(&dst->raised, n_IRQ); @@ -354,14 +347,14 @@ return; } if (test_bit(&src->ipvp, IPVP_MASK)) { - /* Interrupt source is disabled */ + /* Interrupt source is disabled */ DPRINTF("%s: IRQ %d is disabled\n", __func__, n_IRQ); - return; + return; } if (IPVP_PRIORITY(src->ipvp) == 0) { - /* Priority set to zero */ + /* Priority set to zero */ DPRINTF("%s: IRQ %d has 0 priority\n", __func__, n_IRQ); - return; + return; } if (test_bit(&src->ipvp, IPVP_ACTIVITY)) { /* IRQ already active */ @@ -369,9 +362,9 @@ return; } if (src->ide == 0x00000000) { - /* No target */ + /* No target */ DPRINTF("%s: IRQ %d has no target\n", __func__, n_IRQ); - return; + return; } if (src->ide == (1 << src->last_cpu)) { @@ -434,32 +427,34 @@ opp->micr = 0x00000000; /* Initialise IRQ sources */ for (i = 0; i < opp->max_irq; i++) { - opp->src[i].ipvp = 0xA0000000; - opp->src[i].ide = 0x00000000; + opp->src[i].ipvp = 0xA0000000; + opp->src[i].ide = 0x00000000; } /* Initialise IRQ destinations */ for (i = 0; i < MAX_CPU; i++) { - opp->dst[i].pctp = 0x0000000F; - opp->dst[i].pcsr = 0x00000000; - memset(&opp->dst[i].raised, 0, sizeof(IRQ_queue_t)); - memset(&opp->dst[i].servicing, 0, sizeof(IRQ_queue_t)); + opp->dst[i].pctp = 0x0000000F; + opp->dst[i].pcsr = 0x00000000; + memset(&opp->dst[i].raised, 0, sizeof(IRQ_queue_t)); + opp->dst[i].raised.next = -1; + memset(&opp->dst[i].servicing, 0, sizeof(IRQ_queue_t)); + opp->dst[i].servicing.next = -1; } /* Initialise timers */ for (i = 0; i < MAX_TMR; i++) { - opp->timers[i].ticc = 0x00000000; - opp->timers[i].tibc = 0x80000000; + opp->timers[i].ticc = 0x00000000; + opp->timers[i].tibc = 0x80000000; } /* Initialise doorbells */ #if MAX_DBL > 0 opp->dar = 0x00000000; for (i = 0; i < MAX_DBL; i++) { - opp->doorbells[i].dmr = 0x00000000; + opp->doorbells[i].dmr = 0x00000000; } #endif /* Initialise mailboxes */ #if MAX_MBX > 0 for (i = 0; i < MAX_MBX; i++) { /* ? */ - opp->mailboxes[i].mbr = 0x00000000; + opp->mailboxes[i].mbr = 0x00000000; } #endif /* Go out of RESET state */ @@ -472,11 +467,11 @@ switch (reg) { case IRQ_IPVP: - retval = opp->src[n_IRQ].ipvp; - break; + retval = opp->src[n_IRQ].ipvp; + break; case IRQ_IDE: - retval = opp->src[n_IRQ].ide; - break; + retval = opp->src[n_IRQ].ide; + break; } return retval; @@ -492,95 +487,95 @@ /* NOTE: not fully accurate for special IRQs, but simple and sufficient */ /* ACTIVITY bit is read-only */ - opp->src[n_IRQ].ipvp = + opp->src[n_IRQ].ipvp = (opp->src[n_IRQ].ipvp & 0x40000000) | (val & 0x800F00FF); openpic_update_irq(opp, n_IRQ); DPRINTF("Set IPVP %d to 0x%08x -> 0x%08x\n", n_IRQ, val, opp->src[n_IRQ].ipvp); - break; + break; case IRQ_IDE: - tmp = val & 0xC0000000; + tmp = val & 0xC0000000; tmp |= val & ((1 << MAX_CPU) - 1); - opp->src[n_IRQ].ide = tmp; + opp->src[n_IRQ].ide = tmp; DPRINTF("Set IDE %d to 0x%08x\n", n_IRQ, opp->src[n_IRQ].ide); - break; + break; } } #if 0 // Code provision for Intel model #if MAX_DBL > 0 static uint32_t read_doorbell_register (openpic_t *opp, - int n_dbl, uint32_t offset) + int n_dbl, uint32_t offset) { uint32_t retval; switch (offset) { case DBL_IPVP_OFFSET: - retval = read_IRQreg(opp, IRQ_DBL0 + n_dbl, IRQ_IPVP); - break; + retval = read_IRQreg(opp, IRQ_DBL0 + n_dbl, IRQ_IPVP); + break; case DBL_IDE_OFFSET: - retval = read_IRQreg(opp, IRQ_DBL0 + n_dbl, IRQ_IDE); - break; + retval = read_IRQreg(opp, IRQ_DBL0 + n_dbl, IRQ_IDE); + break; case DBL_DMR_OFFSET: - retval = opp->doorbells[n_dbl].dmr; - break; + retval = opp->doorbells[n_dbl].dmr; + break; } return retval; } static void write_doorbell_register (penpic_t *opp, int n_dbl, - uint32_t offset, uint32_t value) + uint32_t offset, uint32_t value) { switch (offset) { case DBL_IVPR_OFFSET: - write_IRQreg(opp, IRQ_DBL0 + n_dbl, IRQ_IPVP, value); - break; + write_IRQreg(opp, IRQ_DBL0 + n_dbl, IRQ_IPVP, value); + break; case DBL_IDE_OFFSET: - write_IRQreg(opp, IRQ_DBL0 + n_dbl, IRQ_IDE, value); - break; + write_IRQreg(opp, IRQ_DBL0 + n_dbl, IRQ_IDE, value); + break; case DBL_DMR_OFFSET: - opp->doorbells[n_dbl].dmr = value; - break; + opp->doorbells[n_dbl].dmr = value; + break; } } #endif #if MAX_MBX > 0 static uint32_t read_mailbox_register (openpic_t *opp, - int n_mbx, uint32_t offset) + int n_mbx, uint32_t offset) { uint32_t retval; switch (offset) { case MBX_MBR_OFFSET: - retval = opp->mailboxes[n_mbx].mbr; - break; + retval = opp->mailboxes[n_mbx].mbr; + break; case MBX_IVPR_OFFSET: - retval = read_IRQreg(opp, IRQ_MBX0 + n_mbx, IRQ_IPVP); - break; + retval = read_IRQreg(opp, IRQ_MBX0 + n_mbx, IRQ_IPVP); + break; case MBX_DMR_OFFSET: - retval = read_IRQreg(opp, IRQ_MBX0 + n_mbx, IRQ_IDE); - break; + retval = read_IRQreg(opp, IRQ_MBX0 + n_mbx, IRQ_IDE); + break; } return retval; } static void write_mailbox_register (openpic_t *opp, int n_mbx, - uint32_t address, uint32_t value) + uint32_t address, uint32_t value) { switch (offset) { case MBX_MBR_OFFSET: - opp->mailboxes[n_mbx].mbr = value; - break; + opp->mailboxes[n_mbx].mbr = value; + break; case MBX_IVPR_OFFSET: - write_IRQreg(opp, IRQ_MBX0 + n_mbx, IRQ_IPVP, value); - break; + write_IRQreg(opp, IRQ_MBX0 + n_mbx, IRQ_IPVP, value); + break; case MBX_DMR_OFFSET: - write_IRQreg(opp, IRQ_MBX0 + n_mbx, IRQ_IDE, value); - break; + write_IRQreg(opp, IRQ_MBX0 + n_mbx, IRQ_IDE, value); + break; } } #endif @@ -595,9 +590,6 @@ DPRINTF("%s: addr " TARGET_FMT_plx " <= %08x\n", __func__, addr, val); if (addr & 0xF) return; -#if defined TARGET_WORDS_BIGENDIAN - val = openpic_swap32(opp, val); -#endif addr &= 0xFF; switch (addr) { case 0x00: /* FREP */ @@ -606,9 +598,9 @@ if (val & 0x80000000 && opp->reset) opp->reset(opp); opp->glbc = val & ~0x80000000; - break; + break; case 0x80: /* VENI */ - break; + break; case 0x90: /* PINT */ for (idx = 0; idx < opp->nb_cpus; idx++) { if ((val & (1 << idx)) && !(opp->pint & (1 << idx))) { @@ -622,7 +614,7 @@ } } opp->pint = val; - break; + break; #if MAX_IPI > 0 case 0xA0: /* IPI_IPVP */ case 0xB0: @@ -640,7 +632,7 @@ break; case 0xF0: /* TIFR */ opp->tifr = val; - break; + break; default: break; } @@ -662,13 +654,13 @@ break; case 0x20: /* GLBC */ retval = opp->glbc; - break; + break; case 0x80: /* VENI */ retval = opp->veni; - break; + break; case 0x90: /* PINT */ retval = 0x00000000; - break; + break; #if MAX_IPI > 0 case 0xA0: /* IPI_IPVP */ case 0xB0: @@ -679,21 +671,18 @@ idx = (addr - 0xA0) >> 4; retval = read_IRQreg(opp, opp->irq_ipi0 + idx, IRQ_IPVP); } - break; + break; #endif case 0xE0: /* SPVE */ retval = opp->spve; break; case 0xF0: /* TIFR */ retval = opp->tifr; - break; + break; default: break; } DPRINTF("%s: => %08x\n", __func__, retval); -#if defined TARGET_WORDS_BIGENDIAN - retval = openpic_swap32(opp, retval); -#endif return retval; } @@ -706,9 +695,6 @@ DPRINTF("%s: addr %08x <= %08x\n", __func__, addr, val); if (addr & 0xF) return; -#if defined TARGET_WORDS_BIGENDIAN - val = openpic_swap32(opp, val); -#endif addr -= 0x1100; addr &= 0xFFFF; idx = (addr & 0xFFF0) >> 6; @@ -717,18 +703,18 @@ case 0x00: /* TICC */ break; case 0x10: /* TIBC */ - if ((opp->timers[idx].ticc & 0x80000000) != 0 && - (val & 0x80000000) == 0 && + if ((opp->timers[idx].ticc & 0x80000000) != 0 && + (val & 0x80000000) == 0 && (opp->timers[idx].tibc & 0x80000000) != 0) - opp->timers[idx].ticc &= ~0x80000000; - opp->timers[idx].tibc = val; - break; + opp->timers[idx].ticc &= ~0x80000000; + opp->timers[idx].tibc = val; + break; case 0x20: /* TIVP */ write_IRQreg(opp, opp->irq_tim0 + idx, IRQ_IPVP, val); - break; + break; case 0x30: /* TIDE */ write_IRQreg(opp, opp->irq_tim0 + idx, IRQ_IDE, val); - break; + break; } } @@ -748,22 +734,19 @@ addr = addr & 0x30; switch (addr) { case 0x00: /* TICC */ - retval = opp->timers[idx].ticc; + retval = opp->timers[idx].ticc; break; case 0x10: /* TIBC */ - retval = opp->timers[idx].tibc; - break; + retval = opp->timers[idx].tibc; + break; case 0x20: /* TIPV */ retval = read_IRQreg(opp, opp->irq_tim0 + idx, IRQ_IPVP); - break; + break; case 0x30: /* TIDE */ retval = read_IRQreg(opp, opp->irq_tim0 + idx, IRQ_IDE); - break; + break; } DPRINTF("%s: => %08x\n", __func__, retval); -#if defined TARGET_WORDS_BIGENDIAN - retval = openpic_swap32(opp, retval); -#endif return retval; } @@ -776,9 +759,6 @@ DPRINTF("%s: addr %08x <= %08x\n", __func__, addr, val); if (addr & 0xF) return; -#if defined TARGET_WORDS_BIGENDIAN - val = openpic_swap32(opp, val); -#endif addr = addr & 0xFFF0; idx = addr >> 5; if (addr & 0x10) { @@ -810,9 +790,6 @@ retval = read_IRQreg(opp, idx, IRQ_IPVP); } DPRINTF("%s: => %08x\n", __func__, retval); -#if defined TARGET_WORDS_BIGENDIAN - retval = openpic_swap32(opp, retval); -#endif return retval; } @@ -827,9 +804,6 @@ DPRINTF("%s: addr " TARGET_FMT_plx " <= %08x\n", __func__, addr, val); if (addr & 0xF) return; -#if defined TARGET_WORDS_BIGENDIAN - val = openpic_swap32(opp, val); -#endif addr &= 0x1FFF0; idx = addr / 0x1000; dst = &opp->dst[idx]; @@ -847,21 +821,21 @@ break; #endif case 0x80: /* PCTP */ - dst->pctp = val & 0x0000000F; - break; + dst->pctp = val & 0x0000000F; + break; case 0x90: /* WHOAMI */ - /* Read-only register */ - break; + /* Read-only register */ + break; case 0xA0: /* PIAC */ - /* Read-only register */ - break; + /* Read-only register */ + break; case 0xB0: /* PEOI */ DPRINTF("PEOI\n"); - s_IRQ = IRQ_get_next(opp, &dst->servicing); - IRQ_resetbit(&dst->servicing, s_IRQ); - dst->servicing.next = -1; - /* Set up next servicing IRQ */ - s_IRQ = IRQ_get_next(opp, &dst->servicing); + s_IRQ = IRQ_get_next(opp, &dst->servicing); + IRQ_resetbit(&dst->servicing, s_IRQ); + dst->servicing.next = -1; + /* Set up next servicing IRQ */ + s_IRQ = IRQ_get_next(opp, &dst->servicing); /* Check queued interrupts. */ n_IRQ = IRQ_get_next(opp, &dst->raised); src = &opp->src[n_IRQ]; @@ -872,7 +846,7 @@ idx, n_IRQ); opp->irq_raise(opp, idx, src); } - break; + break; default: break; } @@ -896,46 +870,46 @@ addr &= 0xFF0; switch (addr) { case 0x80: /* PCTP */ - retval = dst->pctp; - break; + retval = dst->pctp; + break; case 0x90: /* WHOAMI */ - retval = idx; - break; + retval = idx; + break; case 0xA0: /* PIAC */ DPRINTF("Lower OpenPIC INT output\n"); qemu_irq_lower(dst->irqs[OPENPIC_OUTPUT_INT]); - n_IRQ = IRQ_get_next(opp, &dst->raised); + n_IRQ = IRQ_get_next(opp, &dst->raised); DPRINTF("PIAC: irq=%d\n", n_IRQ); - if (n_IRQ == -1) { - /* No more interrupt pending */ + if (n_IRQ == -1) { + /* No more interrupt pending */ retval = IPVP_VECTOR(opp->spve); - } else { - src = &opp->src[n_IRQ]; - if (!test_bit(&src->ipvp, IPVP_ACTIVITY) || - !(IPVP_PRIORITY(src->ipvp) > dst->pctp)) { - /* - Spurious level-sensitive IRQ - * - Priorities has been changed - * and the pending IRQ isn't allowed anymore - */ - reset_bit(&src->ipvp, IPVP_ACTIVITY); - retval = IPVP_VECTOR(opp->spve); - } else { - /* IRQ enter servicing state */ - IRQ_setbit(&dst->servicing, n_IRQ); - retval = IPVP_VECTOR(src->ipvp); - } - IRQ_resetbit(&dst->raised, n_IRQ); - dst->raised.next = -1; - if (!test_bit(&src->ipvp, IPVP_SENSE)) { + } else { + src = &opp->src[n_IRQ]; + if (!test_bit(&src->ipvp, IPVP_ACTIVITY) || + !(IPVP_PRIORITY(src->ipvp) > dst->pctp)) { + /* - Spurious level-sensitive IRQ + * - Priorities has been changed + * and the pending IRQ isn't allowed anymore + */ + reset_bit(&src->ipvp, IPVP_ACTIVITY); + retval = IPVP_VECTOR(opp->spve); + } else { + /* IRQ enter servicing state */ + IRQ_setbit(&dst->servicing, n_IRQ); + retval = IPVP_VECTOR(src->ipvp); + } + IRQ_resetbit(&dst->raised, n_IRQ); + dst->raised.next = -1; + if (!test_bit(&src->ipvp, IPVP_SENSE)) { /* edge-sensitive IRQ */ - reset_bit(&src->ipvp, IPVP_ACTIVITY); + reset_bit(&src->ipvp, IPVP_ACTIVITY); src->pending = 0; } - } - break; + } + break; case 0xB0: /* PEOI */ - retval = 0; - break; + retval = 0; + break; #if MAX_IPI > 0 case 0x40: /* IDE */ case 0x50: @@ -947,9 +921,6 @@ break; } DPRINTF("%s: => %08x\n", __func__, retval); -#if defined TARGET_WORDS_BIGENDIAN - retval = openpic_swap32(opp, retval); -#endif return retval; } @@ -1047,7 +1018,8 @@ cpu_register_physical_memory(addr, 0x40000, opp->mem_index); #if 0 // Don't implement ISU for now opp_io_memory = cpu_register_io_memory(openpic_src_read, - openpic_src_write); + openpic_src_write, NULL + DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(isu_base, 0x20 * (EXT_IRQ + 2), opp_io_memory); #endif @@ -1206,17 +1178,16 @@ pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_IBM); pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_IBM_OPENPIC2); pci_config_set_class(pci_conf, PCI_CLASS_SYSTEM_OTHER); // FIXME? - pci_conf[PCI_HEADER_TYPE] = PCI_HEADER_TYPE_NORMAL; // header_type pci_conf[0x3d] = 0x00; // no interrupt pin /* Register I/O spaces */ - pci_register_bar((PCIDevice *)opp, 0, 0x40000, + pci_register_bar(&opp->pci_dev, 0, 0x40000, PCI_BASE_ADDRESS_SPACE_MEMORY, &openpic_map); } else { opp = qemu_mallocz(sizeof(openpic_t)); } - opp->mem_index = cpu_register_io_memory(openpic_read, - openpic_write, opp); + opp->mem_index = cpu_register_io_memory(openpic_read, openpic_write, opp, + DEVICE_LITTLE_ENDIAN); // isu_base &= 0xFFFC0000; opp->nb_cpus = nb_cpus; @@ -1244,9 +1215,9 @@ for (i = 0; i < nb_cpus; i++) opp->dst[i].irqs = irqs[i]; opp->irq_out = irq_out; - opp->need_swap = 1; - register_savevm("openpic", 0, 2, openpic_save, openpic_load, opp); + register_savevm(&opp->pci_dev.qdev, "openpic", 0, 2, + openpic_save, openpic_load, opp); qemu_register_reset(openpic_reset, opp); opp->irq_raise = openpic_irq_raise; @@ -1382,7 +1353,7 @@ if (addr & 0xF) return; - addr -= MPIC_EXT_REG_START & (TARGET_PAGE_SIZE - 1); + addr -= MPIC_EXT_REG_START & (OPENPIC_PAGE_SIZE - 1); if (addr < MPIC_EXT_REG_SIZE) { idx += (addr & 0xFFF0) >> 5; if (addr & 0x10) { @@ -1406,7 +1377,7 @@ if (addr & 0xF) return retval; - addr -= MPIC_EXT_REG_START & (TARGET_PAGE_SIZE - 1); + addr -= MPIC_EXT_REG_START & (OPENPIC_PAGE_SIZE - 1); if (addr < MPIC_EXT_REG_SIZE) { idx += (addr & 0xFFF0) >> 5; if (addr & 0x10) { @@ -1432,7 +1403,7 @@ if (addr & 0xF) return; - addr -= MPIC_INT_REG_START & (TARGET_PAGE_SIZE - 1); + addr -= MPIC_INT_REG_START & (OPENPIC_PAGE_SIZE - 1); if (addr < MPIC_INT_REG_SIZE) { idx += (addr & 0xFFF0) >> 5; if (addr & 0x10) { @@ -1456,7 +1427,7 @@ if (addr & 0xF) return retval; - addr -= MPIC_INT_REG_START & (TARGET_PAGE_SIZE - 1); + addr -= MPIC_INT_REG_START & (OPENPIC_PAGE_SIZE - 1); if (addr < MPIC_INT_REG_SIZE) { idx += (addr & 0xFFF0) >> 5; if (addr & 0x10) { @@ -1482,7 +1453,7 @@ if (addr & 0xF) return; - addr -= MPIC_MSG_REG_START & (TARGET_PAGE_SIZE - 1); + addr -= MPIC_MSG_REG_START & (OPENPIC_PAGE_SIZE - 1); if (addr < MPIC_MSG_REG_SIZE) { idx += (addr & 0xFFF0) >> 5; if (addr & 0x10) { @@ -1506,7 +1477,7 @@ if (addr & 0xF) return retval; - addr -= MPIC_MSG_REG_START & (TARGET_PAGE_SIZE - 1); + addr -= MPIC_MSG_REG_START & (OPENPIC_PAGE_SIZE - 1); if (addr < MPIC_MSG_REG_SIZE) { idx += (addr & 0xFFF0) >> 5; if (addr & 0x10) { @@ -1532,7 +1503,7 @@ if (addr & 0xF) return; - addr -= MPIC_MSI_REG_START & (TARGET_PAGE_SIZE - 1); + addr -= MPIC_MSI_REG_START & (OPENPIC_PAGE_SIZE - 1); if (addr < MPIC_MSI_REG_SIZE) { idx += (addr & 0xFFF0) >> 5; if (addr & 0x10) { @@ -1555,7 +1526,7 @@ if (addr & 0xF) return retval; - addr -= MPIC_MSI_REG_START & (TARGET_PAGE_SIZE - 1); + addr -= MPIC_MSI_REG_START & (OPENPIC_PAGE_SIZE - 1); if (addr < MPIC_MSI_REG_SIZE) { idx += (addr & 0xFFF0) >> 5; if (addr & 0x10) { @@ -1683,7 +1654,8 @@ for (i = 0; i < sizeof(list)/sizeof(list[0]); i++) { int mem_index; - mem_index = cpu_register_io_memory(list[i].read, list[i].write, mpp); + mem_index = cpu_register_io_memory(list[i].read, list[i].write, mpp, + DEVICE_BIG_ENDIAN); if (mem_index < 0) { goto free; } @@ -1699,12 +1671,11 @@ for (i = 0; i < nb_cpus; i++) mpp->dst[i].irqs = irqs[i]; mpp->irq_out = irq_out; - mpp->need_swap = 0; /* MPIC has the same endian as target */ mpp->irq_raise = mpic_irq_raise; mpp->reset = mpic_reset; - register_savevm("mpic", 0, 2, openpic_save, openpic_load, mpp); + register_savevm(NULL, "mpic", 0, 2, openpic_save, openpic_load, mpp); qemu_register_reset(mpic_reset, mpp); return qemu_allocate_irqs(openpic_set_irq, mpp, mpp->max_irq); diff -Nru qemu-kvm-0.12.5+noroms/hw/palm.c qemu-kvm-0.14.1/hw/palm.c --- qemu-kvm-0.12.5+noroms/hw/palm.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/palm.c 2011-05-11 13:29:46.000000000 +0000 @@ -206,7 +206,6 @@ static uint32_t cs1val = 0x0000e1a0; static uint32_t cs2val = 0x0000e1a0; static uint32_t cs3val = 0xe1a0e1a0; - ram_addr_t phys_flash; int rom_size, rom_loaded = 0; DisplayState *ds = get_displaystate(); @@ -214,16 +213,21 @@ /* External Flash (EMIFS) */ cpu_register_physical_memory(OMAP_CS0_BASE, flash_size, - (phys_flash = qemu_ram_alloc(flash_size)) | IO_MEM_ROM); + qemu_ram_alloc(NULL, "palmte.flash", + flash_size) | IO_MEM_ROM); - io = cpu_register_io_memory(static_readfn, static_writefn, &cs0val); + io = cpu_register_io_memory(static_readfn, static_writefn, &cs0val, + DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(OMAP_CS0_BASE + flash_size, OMAP_CS0_SIZE - flash_size, io); - io = cpu_register_io_memory(static_readfn, static_writefn, &cs1val); + io = cpu_register_io_memory(static_readfn, static_writefn, &cs1val, + DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(OMAP_CS1_BASE, OMAP_CS1_SIZE, io); - io = cpu_register_io_memory(static_readfn, static_writefn, &cs2val); + io = cpu_register_io_memory(static_readfn, static_writefn, &cs2val, + DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(OMAP_CS2_BASE, OMAP_CS2_SIZE, io); - io = cpu_register_io_memory(static_readfn, static_writefn, &cs3val); + io = cpu_register_io_memory(static_readfn, static_writefn, &cs3val, + DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(OMAP_CS3_BASE, OMAP_CS3_SIZE, io); palmte_microwire_setup(cpu); @@ -234,21 +238,20 @@ /* Setup initial (reset) machine state */ if (nb_option_roms) { - rom_size = get_image_size(option_rom[0]); + rom_size = get_image_size(option_rom[0].name); if (rom_size > flash_size) { fprintf(stderr, "%s: ROM image too big (%x > %x)\n", __FUNCTION__, rom_size, flash_size); rom_size = 0; } if (rom_size > 0) { - rom_size = load_image_targphys(option_rom[0], OMAP_CS0_BASE, + rom_size = load_image_targphys(option_rom[0].name, OMAP_CS0_BASE, flash_size); rom_loaded = 1; - cpu->env->regs[15] = 0x00000000; } if (rom_size < 0) { fprintf(stderr, "%s: error loading '%s'\n", - __FUNCTION__, option_rom[0]); + __FUNCTION__, option_rom[0].name); } } @@ -259,9 +262,6 @@ /* Load the kernel. */ if (kernel_filename) { - /* Start at bootloader. */ - cpu->env->regs[15] = palmte_binfo.loader_start; - palmte_binfo.kernel_filename = kernel_filename; palmte_binfo.kernel_cmdline = kernel_cmdline; palmte_binfo.initrd_filename = initrd_filename; diff -Nru qemu-kvm-0.12.5+noroms/hw/parallel.c qemu-kvm-0.14.1/hw/parallel.c --- qemu-kvm-0.12.5+noroms/hw/parallel.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/parallel.c 2011-05-11 13:29:46.000000000 +0000 @@ -26,6 +26,7 @@ #include "qemu-char.h" #include "isa.h" #include "pc.h" +#include "sysemu.h" //#define DEBUG_PARALLEL @@ -414,15 +415,14 @@ static void parallel_ioport_ecp_write(void *opaque, uint32_t addr, uint32_t val) { - addr &= 7; - pdebug("wecp%d=%02x\n", addr, val); + pdebug("wecp%d=%02x\n", addr & 7, val); } static uint32_t parallel_ioport_ecp_read(void *opaque, uint32_t addr) { uint8_t ret = 0xff; - addr &= 7; - pdebug("recp%d:%02x\n", addr, ret); + + pdebug("recp%d:%02x\n", addr & 7, ret); return ret; } @@ -481,16 +481,21 @@ if (s->hw_driver) { register_ioport_write(base, 8, 1, parallel_ioport_write_hw, s); register_ioport_read(base, 8, 1, parallel_ioport_read_hw, s); + isa_init_ioport_range(dev, base, 8); + register_ioport_write(base+4, 1, 2, parallel_ioport_eppdata_write_hw2, s); register_ioport_read(base+4, 1, 2, parallel_ioport_eppdata_read_hw2, s); register_ioport_write(base+4, 1, 4, parallel_ioport_eppdata_write_hw4, s); register_ioport_read(base+4, 1, 4, parallel_ioport_eppdata_read_hw4, s); + isa_init_ioport(dev, base+4); register_ioport_write(base+0x400, 8, 1, parallel_ioport_ecp_write, s); register_ioport_read(base+0x400, 8, 1, parallel_ioport_ecp_read, s); + isa_init_ioport_range(dev, base+0x400, 8); } else { register_ioport_write(base, 8, 1, parallel_ioport_write_sw, s); register_ioport_read(base, 8, 1, parallel_ioport_read_sw, s); + isa_init_ioport_range(dev, base, 8); } return 0; } @@ -577,7 +582,8 @@ s->it_shift = it_shift; qemu_register_reset(parallel_reset, s); - io_sw = cpu_register_io_memory(parallel_mm_read_sw, parallel_mm_write_sw, s); + io_sw = cpu_register_io_memory(parallel_mm_read_sw, parallel_mm_write_sw, + s, DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base, 8 << it_shift, io_sw); return s; } diff -Nru qemu-kvm-0.12.5+noroms/hw/pc.c qemu-kvm-0.14.1/hw/pc.c --- qemu-kvm-0.12.5+noroms/hw/pc.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/pc.c 2011-05-11 13:29:46.000000000 +0000 @@ -23,36 +23,39 @@ */ #include "hw.h" #include "pc.h" +#include "apic.h" #include "fdc.h" +#include "ide.h" #include "pci.h" #include "vmware_vga.h" -#include "usb-uhci.h" -#include "usb-ohci.h" -#include "prep_pci.h" -#include "apb_pci.h" -#include "block.h" -#include "sysemu.h" -#include "audio/audio.h" -#include "net.h" -#include "smbus.h" -#include "boards.h" #include "monitor.h" #include "fw_cfg.h" #include "hpet_emul.h" -#include "watchdog.h" #include "smbios.h" -#include "ide.h" #include "loader.h" #include "elf.h" +#include "multiboot.h" +#include "mc146818rtc.h" +#include "msix.h" +#include "sysbus.h" +#include "sysemu.h" #include "device-assignment.h" - -#include "qemu-kvm.h" +#include "kvm.h" +#include "blockdev.h" +#include "ui/qemu-spice.h" /* output Bochs bios info messages */ //#define DEBUG_BIOS -/* Show multiboot debug output */ -//#define DEBUG_MULTIBOOT +/* debug PC/ISA interrupts */ +//#define DEBUG_IRQ + +#ifdef DEBUG_IRQ +#define DPRINTF(fmt, ...) \ + do { printf("CPUIRQ: " fmt , ## __VA_ARGS__); } while (0) +#else +#define DPRINTF(fmt, ...) +#endif #define BIOS_FILENAME "bios.bin" #define EXTBOOT_FILENAME "extboot.bin" @@ -66,25 +69,31 @@ #define FW_CFG_ACPI_TABLES (FW_CFG_ARCH_LOCAL + 0) #define FW_CFG_SMBIOS_ENTRIES (FW_CFG_ARCH_LOCAL + 1) #define FW_CFG_IRQ0_OVERRIDE (FW_CFG_ARCH_LOCAL + 2) +#define FW_CFG_E820_TABLE (FW_CFG_ARCH_LOCAL + 3) +#define FW_CFG_HPET (FW_CFG_ARCH_LOCAL + 4) + +#define MSI_ADDR_BASE 0xfee00000 -#define MAX_IDE_BUS 2 +#define E820_NR_ENTRIES 16 -static fdctrl_t *floppy_controller; -static RTCState *rtc_state; -static PITState *pit; -static PCII440FXState *i440fx_state; +struct e820_entry { + uint64_t address; + uint64_t length; + uint32_t type; +} __attribute((__packed__, __aligned__(4))); -qemu_irq *ioapic_irq_hack; +struct e820_table { + uint32_t count; + struct e820_entry entry[E820_NR_ENTRIES]; +} __attribute((__packed__, __aligned__(4))); -typedef struct isa_irq_state { - qemu_irq *i8259; - qemu_irq *ioapic; -} IsaIrqState; +static struct e820_table e820_table; -static void isa_irq_handler(void *opaque, int n, int level) +void isa_irq_handler(void *opaque, int n, int level) { IsaIrqState *isa = (IsaIrqState *)opaque; + DPRINTF("isa_irqs: %s irq %d\n", level? "raise" : "lower", n); if (n < 16) { qemu_set_irq(isa->i8259[n], level); } @@ -98,6 +107,12 @@ /* MSDOS compatibility mode FPU exception support */ static qemu_irq ferr_irq; + +void pc_register_ferr_irq(qemu_irq irq) +{ + ferr_irq = irq; +} + /* XXX: add IGNNE support */ void cpu_set_ferr(CPUX86State *s) { @@ -116,10 +131,22 @@ } /* SMM support */ + +static cpu_set_smm_t smm_set; +static void *smm_arg; + +void cpu_smm_register(cpu_set_smm_t callback, void *arg) +{ + assert(smm_set == NULL); + assert(smm_arg == NULL); + smm_set = callback; + smm_arg = arg; +} + void cpu_smm_update(CPUState *env) { - if (i440fx_state && env == first_cpu) - i440fx_set_smm(i440fx_state, (env->hflags >> HF_SMM_SHIFT) & 1); + if (smm_set && smm_arg && env == first_cpu) + smm_set(!!(env->hflags & HF_SMM_MASK), smm_arg); } @@ -128,7 +155,7 @@ { int intno; - intno = apic_get_interrupt(env); + intno = apic_get_interrupt(env->apic_state); if (intno >= 0) { /* set irq request if a PIC irq is still pending */ /* XXX: improve that */ @@ -136,8 +163,9 @@ return intno; } /* read the irq from the PIC */ - if (!apic_accept_pic_intr(env)) + if (!apic_accept_pic_intr(env->apic_state)) { return -1; + } intno = pic_read_irq(isa_pic); return intno; @@ -147,10 +175,12 @@ { CPUState *env = first_cpu; + DPRINTF("pic_irqs: %s irq %d\n", level? "raise" : "lower", irq); if (env->apic_state) { while (env) { - if (apic_accept_pic_intr(env)) - apic_deliver_pic_intr(env, level); + if (apic_accept_pic_intr(env->apic_state)) { + apic_deliver_pic_intr(env->apic_state, level); + } env = env->next_cpu; } } else { @@ -189,9 +219,9 @@ return val; } -static void cmos_init_hd(int type_ofs, int info_ofs, BlockDriverState *hd) +static void cmos_init_hd(int type_ofs, int info_ofs, BlockDriverState *hd, + ISADevice *s) { - RTCState *s = rtc_state; int cylinders, heads, sectors; bdrv_get_geometry_hint(hd, &cylinders, &heads, §ors); rtc_set_memory(s, type_ofs, 47); @@ -223,44 +253,95 @@ return 0; } -/* copy/pasted from cmos_init, should be made a general function - and used there as well */ -static int pc_boot_set(void *opaque, const char *boot_device) +static int set_boot_dev(ISADevice *s, const char *boot_device, int fd_bootchk) { - Monitor *mon = cur_mon; #define PC_MAX_BOOT_DEVICES 3 - RTCState *s = (RTCState *)opaque; int nbds, bds[3] = { 0, }; int i; nbds = strlen(boot_device); if (nbds > PC_MAX_BOOT_DEVICES) { - monitor_printf(mon, "Too many boot devices for PC\n"); + error_report("Too many boot devices for PC"); return(1); } for (i = 0; i < nbds; i++) { bds[i] = boot_device2nibble(boot_device[i]); if (bds[i] == 0) { - monitor_printf(mon, "Invalid boot device for PC: '%c'\n", - boot_device[i]); + error_report("Invalid boot device for PC: '%c'", + boot_device[i]); return(1); } } rtc_set_memory(s, 0x3d, (bds[1] << 4) | bds[0]); - rtc_set_memory(s, 0x38, (bds[2] << 4)); + rtc_set_memory(s, 0x38, (bds[2] << 4) | (fd_bootchk ? 0x0 : 0x1)); return(0); } -/* hd_table must contain 4 block drivers */ -static void cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size, - const char *boot_device, DriveInfo **hd_table) +static int pc_boot_set(void *opaque, const char *boot_device) +{ + return set_boot_dev(opaque, boot_device, 0); +} + +typedef struct pc_cmos_init_late_arg { + ISADevice *rtc_state; + BusState *idebus0, *idebus1; +} pc_cmos_init_late_arg; + +static void pc_cmos_init_late(void *opaque) { - RTCState *s = rtc_state; - int nbds, bds[3] = { 0, }; + pc_cmos_init_late_arg *arg = opaque; + ISADevice *s = arg->rtc_state; int val; - int fd0, fd1, nb; + BlockDriverState *hd_table[4]; int i; + ide_get_bs(hd_table, arg->idebus0); + ide_get_bs(hd_table + 2, arg->idebus1); + + rtc_set_memory(s, 0x12, (hd_table[0] ? 0xf0 : 0) | (hd_table[1] ? 0x0f : 0)); + if (hd_table[0]) + cmos_init_hd(0x19, 0x1b, hd_table[0], s); + if (hd_table[1]) + cmos_init_hd(0x1a, 0x24, hd_table[1], s); + + val = 0; + for (i = 0; i < 4; i++) { + if (hd_table[i]) { + int cylinders, heads, sectors, translation; + /* NOTE: bdrv_get_geometry_hint() returns the physical + geometry. It is always such that: 1 <= sects <= 63, 1 + <= heads <= 16, 1 <= cylinders <= 16383. The BIOS + geometry can be different if a translation is done. */ + translation = bdrv_get_translation_hint(hd_table[i]); + if (translation == BIOS_ATA_TRANSLATION_AUTO) { + bdrv_get_geometry_hint(hd_table[i], &cylinders, &heads, §ors); + if (cylinders <= 1024 && heads <= 16 && sectors <= 63) { + /* No translation. */ + translation = 0; + } else { + /* LBA translation. */ + translation = 1; + } + } else { + translation--; + } + val |= translation << (i * 2); + } + } + rtc_set_memory(s, 0x39, val); + + qemu_unregister_reset(pc_cmos_init_late, opaque); +} + +void pc_cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size, + const char *boot_device, + BusState *idebus0, BusState *idebus1, + FDCtrl *floppy_controller, ISADevice *s) +{ + int val; + int fd0, fd1, nb; + static pc_cmos_init_late_arg arg; + /* various important CMOS locations needed by PC/Bochs bios */ /* memory size */ @@ -295,22 +376,9 @@ rtc_set_memory(s, 0x5f, smp_cpus - 1); /* set boot devices, and disable floppy signature check if requested */ -#define PC_MAX_BOOT_DEVICES 3 - nbds = strlen(boot_device); - if (nbds > PC_MAX_BOOT_DEVICES) { - fprintf(stderr, "Too many boot devices for PC\n"); + if (set_boot_dev(s, boot_device, fd_bootchk)) { exit(1); } - for (i = 0; i < nbds; i++) { - bds[i] = boot_device2nibble(boot_device[i]); - if (bds[i] == 0) { - fprintf(stderr, "Invalid boot device for PC: '%c'\n", - boot_device[i]); - exit(1); - } - } - rtc_set_memory(s, 0x3d, (bds[1] << 4) | bds[0]); - rtc_set_memory(s, 0x38, (bds[2] << 4) | (fd_bootchk ? 0x0 : 0x1)); /* floppy type */ @@ -341,60 +409,99 @@ rtc_set_memory(s, REG_EQUIPMENT_BYTE, val); /* hard drives */ + arg.rtc_state = s; + arg.idebus0 = idebus0; + arg.idebus1 = idebus1; + qemu_register_reset(pc_cmos_init_late, &arg); +} - rtc_set_memory(s, 0x12, (hd_table[0] ? 0xf0 : 0) | (hd_table[1] ? 0x0f : 0)); - if (hd_table[0]) - cmos_init_hd(0x19, 0x1b, hd_table[0]->bdrv); - if (hd_table[1]) - cmos_init_hd(0x1a, 0x24, hd_table[1]->bdrv); +/* port 92 stuff: could be split off */ +typedef struct Port92State { + ISADevice dev; + uint8_t outport; + qemu_irq *a20_out; +} Port92State; - val = 0; - for (i = 0; i < 4; i++) { - if (hd_table[i]) { - int cylinders, heads, sectors, translation; - /* NOTE: bdrv_get_geometry_hint() returns the physical - geometry. It is always such that: 1 <= sects <= 63, 1 - <= heads <= 16, 1 <= cylinders <= 16383. The BIOS - geometry can be different if a translation is done. */ - translation = bdrv_get_translation_hint(hd_table[i]->bdrv); - if (translation == BIOS_ATA_TRANSLATION_AUTO) { - bdrv_get_geometry_hint(hd_table[i]->bdrv, &cylinders, &heads, §ors); - if (cylinders <= 1024 && heads <= 16 && sectors <= 63) { - /* No translation. */ - translation = 0; - } else { - /* LBA translation. */ - translation = 1; - } - } else { - translation--; - } - val |= translation << (i * 2); - } +static void port92_write(void *opaque, uint32_t addr, uint32_t val) +{ + Port92State *s = opaque; + + DPRINTF("port92: write 0x%02x\n", val); + s->outport = val; + qemu_set_irq(*s->a20_out, (val >> 1) & 1); + if (val & 1) { + qemu_system_reset_request(); } - rtc_set_memory(s, 0x39, val); } -void ioport_set_a20(int enable) +static uint32_t port92_read(void *opaque, uint32_t addr) { - /* XXX: send to all CPUs ? */ - cpu_x86_set_a20(first_cpu, enable); + Port92State *s = opaque; + uint32_t ret; + + ret = s->outport; + DPRINTF("port92: read 0x%02x\n", ret); + return ret; +} + +static void port92_init(ISADevice *dev, qemu_irq *a20_out) +{ + Port92State *s = DO_UPCAST(Port92State, dev, dev); + + s->a20_out = a20_out; +} + +static const VMStateDescription vmstate_port92_isa = { + .name = "port92", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField []) { + VMSTATE_UINT8(outport, Port92State), + VMSTATE_END_OF_LIST() + } +}; + +static void port92_reset(DeviceState *d) +{ + Port92State *s = container_of(d, Port92State, dev.qdev); + + s->outport &= ~1; } -int ioport_get_a20(void) +static int port92_initfn(ISADevice *dev) { - return ((first_cpu->a20_mask >> 20) & 1); + Port92State *s = DO_UPCAST(Port92State, dev, dev); + + register_ioport_read(0x92, 1, 1, port92_read, s); + register_ioport_write(0x92, 1, 1, port92_write, s); + isa_init_ioport(dev, 0x92); + s->outport = 0; + return 0; } -static void ioport92_write(void *opaque, uint32_t addr, uint32_t val) +static ISADeviceInfo port92_info = { + .qdev.name = "port92", + .qdev.size = sizeof(Port92State), + .qdev.vmsd = &vmstate_port92_isa, + .qdev.no_user = 1, + .qdev.reset = port92_reset, + .init = port92_initfn, +}; + +static void port92_register(void) { - ioport_set_a20((val >> 1) & 1); - /* XXX: bit 0 is fast reset */ + isa_qdev_register(&port92_info); } +device_init(port92_register) -static uint32_t ioport92_read(void *opaque, uint32_t addr) +static void handle_a20_line_change(void *opaque, int irq, int level) { - return ioport_get_a20() << 1; + CPUState *cpu = opaque; + + /* XXX: send to all CPUs ? */ + /* XXX: add logic to handle multiple A20 line sources */ + cpu_x86_set_a20(cpu, level); } /***********************************************************/ @@ -409,8 +516,8 @@ /* Bochs BIOS messages */ case 0x400: case 0x401: - fprintf(stderr, "BIOS panic at rombios.c, line %d\n", val); - exit(1); + /* used to be panic, now unused */ + break; case 0x402: case 0x403: #ifdef DEBUG_BIOS @@ -444,6 +551,23 @@ } } +int e820_add_entry(uint64_t address, uint64_t length, uint32_t type) +{ + int index = le32_to_cpu(e820_table.count); + struct e820_entry *entry; + + if (index >= E820_NR_ENTRIES) + return -EBUSY; + entry = &e820_table.entry[index++]; + + entry->address = cpu_to_le64(address); + entry->length = cpu_to_le64(length); + entry->type = cpu_to_le32(type); + + e820_table.count = cpu_to_le32(index); + return index; +} + static void *bochs_bios_init(void) { void *fw_cfg; @@ -475,7 +599,11 @@ if (smbios_table) fw_cfg_add_bytes(fw_cfg, FW_CFG_SMBIOS_ENTRIES, smbios_table, smbios_len); + fw_cfg_add_bytes(fw_cfg, FW_CFG_E820_TABLE, (uint8_t *)&e820_table, + sizeof(struct e820_table)); + fw_cfg_add_bytes(fw_cfg, FW_CFG_HPET, (uint8_t *)&hpet_cfg, + sizeof(struct hpet_fw_config)); /* allocate memory for the NUMA channel: one (64bit) word for the number * of nodes, one word for each VCPU->node and one word for each node to * hold the amount of memory. @@ -513,234 +641,6 @@ return size; } -#define MULTIBOOT_STRUCT_ADDR 0x9000 - -#if MULTIBOOT_STRUCT_ADDR > 0xf0000 -#error multiboot struct needs to fit in 16 bit real mode -#endif - -static int load_multiboot(void *fw_cfg, - FILE *f, - const char *kernel_filename, - const char *initrd_filename, - const char *kernel_cmdline, - uint8_t *header) -{ - int i, is_multiboot = 0; - uint32_t flags = 0; - uint32_t mh_entry_addr; - uint32_t mh_load_addr; - uint32_t mb_kernel_size; - uint32_t mmap_addr = MULTIBOOT_STRUCT_ADDR; - uint32_t mb_bootinfo = MULTIBOOT_STRUCT_ADDR + 0x500; - uint32_t mb_mod_end; - uint8_t bootinfo[0x500]; - uint32_t cmdline = 0x200; - uint8_t *mb_kernel_data; - uint8_t *mb_bootinfo_data; - - /* Ok, let's see if it is a multiboot image. - The header is 12x32bit long, so the latest entry may be 8192 - 48. */ - for (i = 0; i < (8192 - 48); i += 4) { - if (ldl_p(header+i) == 0x1BADB002) { - uint32_t checksum = ldl_p(header+i+8); - flags = ldl_p(header+i+4); - checksum += flags; - checksum += (uint32_t)0x1BADB002; - if (!checksum) { - is_multiboot = 1; - break; - } - } - } - - if (!is_multiboot) - return 0; /* no multiboot */ - -#ifdef DEBUG_MULTIBOOT - fprintf(stderr, "qemu: I believe we found a multiboot image!\n"); -#endif - memset(bootinfo, 0, sizeof(bootinfo)); - - if (flags & 0x00000004) { /* MULTIBOOT_HEADER_HAS_VBE */ - fprintf(stderr, "qemu: multiboot knows VBE. we don't.\n"); - } - if (!(flags & 0x00010000)) { /* MULTIBOOT_HEADER_HAS_ADDR */ - uint64_t elf_entry; - uint64_t elf_low, elf_high; - int kernel_size; - fclose(f); - kernel_size = load_elf(kernel_filename, 0, &elf_entry, &elf_low, &elf_high, - 0, ELF_MACHINE, 0); - if (kernel_size < 0) { - fprintf(stderr, "Error while loading elf kernel\n"); - exit(1); - } - mh_load_addr = elf_low; - mb_kernel_size = elf_high - elf_low; - mh_entry_addr = elf_entry; - - mb_kernel_data = qemu_malloc(mb_kernel_size); - if (rom_copy(mb_kernel_data, mh_load_addr, mb_kernel_size) != mb_kernel_size) { - fprintf(stderr, "Error while fetching elf kernel from rom\n"); - exit(1); - } - -#ifdef DEBUG_MULTIBOOT - fprintf(stderr, "qemu: loading multiboot-elf kernel (%#x bytes) with entry %#zx\n", - mb_kernel_size, (size_t)mh_entry_addr); -#endif - } else { - /* Valid if mh_flags sets MULTIBOOT_HEADER_HAS_ADDR. */ - uint32_t mh_header_addr = ldl_p(header+i+12); - mh_load_addr = ldl_p(header+i+16); -#ifdef DEBUG_MULTIBOOT - uint32_t mh_load_end_addr = ldl_p(header+i+20); - uint32_t mh_bss_end_addr = ldl_p(header+i+24); -#endif - uint32_t mb_kernel_text_offset = i - (mh_header_addr - mh_load_addr); - - mh_entry_addr = ldl_p(header+i+28); - mb_kernel_size = get_file_size(f) - mb_kernel_text_offset; - - /* Valid if mh_flags sets MULTIBOOT_HEADER_HAS_VBE. - uint32_t mh_mode_type = ldl_p(header+i+32); - uint32_t mh_width = ldl_p(header+i+36); - uint32_t mh_height = ldl_p(header+i+40); - uint32_t mh_depth = ldl_p(header+i+44); */ - -#ifdef DEBUG_MULTIBOOT - fprintf(stderr, "multiboot: mh_header_addr = %#x\n", mh_header_addr); - fprintf(stderr, "multiboot: mh_load_addr = %#x\n", mh_load_addr); - fprintf(stderr, "multiboot: mh_load_end_addr = %#x\n", mh_load_end_addr); - fprintf(stderr, "multiboot: mh_bss_end_addr = %#x\n", mh_bss_end_addr); - fprintf(stderr, "qemu: loading multiboot kernel (%#x bytes) at %#x\n", - mb_kernel_size, mh_load_addr); -#endif - - mb_kernel_data = qemu_malloc(mb_kernel_size); - fseek(f, mb_kernel_text_offset, SEEK_SET); - fread(mb_kernel_data, 1, mb_kernel_size, f); - fclose(f); - } - - /* blob size is only the kernel for now */ - mb_mod_end = mh_load_addr + mb_kernel_size; - - /* load modules */ - stl_p(bootinfo + 20, 0x0); /* mods_count */ - if (initrd_filename) { - uint32_t mb_mod_info = 0x100; - uint32_t mb_mod_cmdline = 0x300; - uint32_t mb_mod_start = mh_load_addr; - uint32_t mb_mod_length = mb_kernel_size; - char *next_initrd; - char *next_space; - int mb_mod_count = 0; - - do { - if (mb_mod_info + 16 > mb_mod_cmdline) { - printf("WARNING: Too many modules loaded, aborting.\n"); - break; - } - - next_initrd = strchr(initrd_filename, ','); - if (next_initrd) - *next_initrd = '\0'; - /* if a space comes after the module filename, treat everything - after that as parameters */ - pstrcpy((char*)bootinfo + mb_mod_cmdline, - sizeof(bootinfo) - mb_mod_cmdline, - initrd_filename); - stl_p(bootinfo + mb_mod_info + 8, mb_bootinfo + mb_mod_cmdline); /* string */ - mb_mod_cmdline += strlen(initrd_filename) + 1; - if (mb_mod_cmdline > sizeof(bootinfo)) { - mb_mod_cmdline = sizeof(bootinfo); - printf("WARNING: Too many module cmdlines loaded, aborting.\n"); - break; - } - if ((next_space = strchr(initrd_filename, ' '))) - *next_space = '\0'; -#ifdef DEBUG_MULTIBOOT - printf("multiboot loading module: %s\n", initrd_filename); -#endif - mb_mod_start = (mb_mod_start + mb_mod_length + (TARGET_PAGE_SIZE - 1)) - & (TARGET_PAGE_MASK); - mb_mod_length = get_image_size(initrd_filename); - if (mb_mod_length < 0) { - fprintf(stderr, "failed to get %s image size\n", initrd_filename); - exit(1); - } - mb_mod_end = mb_mod_start + mb_mod_length; - mb_mod_count++; - - /* append module data at the end of last module */ - mb_kernel_data = qemu_realloc(mb_kernel_data, - mb_mod_end - mh_load_addr); - load_image(initrd_filename, - mb_kernel_data + mb_mod_start - mh_load_addr); - - stl_p(bootinfo + mb_mod_info + 0, mb_mod_start); - stl_p(bootinfo + mb_mod_info + 4, mb_mod_start + mb_mod_length); - stl_p(bootinfo + mb_mod_info + 12, 0x0); /* reserved */ -#ifdef DEBUG_MULTIBOOT - printf("mod_start: %#x\nmod_end: %#x\n", mb_mod_start, - mb_mod_start + mb_mod_length); -#endif - initrd_filename = next_initrd+1; - mb_mod_info += 16; - } while (next_initrd); - stl_p(bootinfo + 20, mb_mod_count); /* mods_count */ - stl_p(bootinfo + 24, mb_bootinfo + 0x100); /* mods_addr */ - } - - /* Commandline support */ - stl_p(bootinfo + 16, mb_bootinfo + cmdline); - snprintf((char*)bootinfo + cmdline, 0x100, "%s %s", - kernel_filename, kernel_cmdline); - - /* the kernel is where we want it to be now */ -#define MULTIBOOT_FLAGS_MEMORY (1 << 0) -#define MULTIBOOT_FLAGS_BOOT_DEVICE (1 << 1) -#define MULTIBOOT_FLAGS_CMDLINE (1 << 2) -#define MULTIBOOT_FLAGS_MODULES (1 << 3) -#define MULTIBOOT_FLAGS_MMAP (1 << 6) - stl_p(bootinfo, MULTIBOOT_FLAGS_MEMORY - | MULTIBOOT_FLAGS_BOOT_DEVICE - | MULTIBOOT_FLAGS_CMDLINE - | MULTIBOOT_FLAGS_MODULES - | MULTIBOOT_FLAGS_MMAP); - stl_p(bootinfo + 4, 640); /* mem_lower */ - stl_p(bootinfo + 8, ram_size / 1024); /* mem_upper */ - stl_p(bootinfo + 12, 0x8001ffff); /* XXX: use the -boot switch? */ - stl_p(bootinfo + 48, mmap_addr); /* mmap_addr */ - -#ifdef DEBUG_MULTIBOOT - fprintf(stderr, "multiboot: mh_entry_addr = %#x\n", mh_entry_addr); -#endif - - /* save bootinfo off the stack */ - mb_bootinfo_data = qemu_malloc(sizeof(bootinfo)); - memcpy(mb_bootinfo_data, bootinfo, sizeof(bootinfo)); - - /* Pass variables to option rom */ - fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ENTRY, mh_entry_addr); - fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, mh_load_addr); - fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, mb_mod_end - mh_load_addr); - fw_cfg_add_bytes(fw_cfg, FW_CFG_KERNEL_DATA, mb_kernel_data, - mb_mod_end - mh_load_addr); - - fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, mb_bootinfo); - fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, sizeof(bootinfo)); - fw_cfg_add_bytes(fw_cfg, FW_CFG_INITRD_DATA, mb_bootinfo_data, - sizeof(bootinfo)); - - option_rom[nb_option_roms] = "multiboot.bin"; - nb_option_roms++; - - return 1; /* yes, we are multiboot */ -} - static void load_linux(void *fw_cfg, const char *kernel_filename, const char *initrd_filename, @@ -777,8 +677,8 @@ else { /* This looks like a multiboot kernel. If it is, let's stop treating it like a Linux kernel. */ - if (load_multiboot(fw_cfg, f, kernel_filename, - initrd_filename, kernel_cmdline, header)) + if (load_multiboot(fw_cfg, f, kernel_filename, initrd_filename, + kernel_cmdline, kernel_size, header)) return; protocol = 0; } @@ -871,6 +771,12 @@ } initrd_size = get_image_size(initrd_filename); + if (initrd_size < 0) { + fprintf(stderr, "qemu: error reading initrd %s\n", + initrd_filename); + exit(1); + } + initrd_addr = (initrd_max-initrd_size) & ~4095; initrd_data = qemu_malloc(initrd_size); @@ -894,8 +800,14 @@ setup = qemu_malloc(setup_size); kernel = qemu_malloc(kernel_size); fseek(f, 0, SEEK_SET); - fread(setup, 1, setup_size, f); - fread(kernel, 1, kernel_size, f); + if (fread(setup, 1, setup_size, f) != setup_size) { + fprintf(stderr, "fread() failed\n"); + exit(1); + } + if (fread(kernel, 1, kernel_size, f) != kernel_size) { + fprintf(stderr, "fread() failed\n"); + exit(1); + } fclose(f); memcpy(setup, header, MIN(sizeof(header), setup_size)); @@ -907,14 +819,11 @@ fw_cfg_add_i32(fw_cfg, FW_CFG_SETUP_SIZE, setup_size); fw_cfg_add_bytes(fw_cfg, FW_CFG_SETUP_DATA, setup, setup_size); - option_rom[nb_option_roms] = "linuxboot.bin"; + option_rom[nb_option_roms].name = "linuxboot.bin"; + option_rom[nb_option_roms].bootindex = 0; nb_option_roms++; } -static const int ide_iobase[2] = { 0x1f0, 0x170 }; -static const int ide_iobase2[2] = { 0x3f6, 0x376 }; -static const int ide_irq[2] = { 14, 15 }; - #define NE2000_NB_MAX 6 static const int ne2000_io[NE2000_NB_MAX] = { 0x300, 0x320, 0x340, 0x360, @@ -924,26 +833,7 @@ static const int parallel_io[MAX_PARALLEL_PORTS] = { 0x378, 0x278, 0x3bc }; static const int parallel_irq[MAX_PARALLEL_PORTS] = { 7, 7, 7 }; -#ifdef HAS_AUDIO -static void audio_init (PCIBus *pci_bus, qemu_irq *pic) -{ - struct soundhw *c; - - for (c = soundhw; c->name; ++c) { - if (c->enabled) { - if (c->isa) { - c->init.init_isa(pic); - } else { - if (pci_bus) { - c->init.init_pci(pci_bus); - } - } - } - } -} -#endif - -static void pc_init_ne2k_isa(NICInfo *nd) +void pc_init_ne2k_isa(NICInfo *nd) { static int nb_ne2k = 0; @@ -956,58 +846,121 @@ int cpu_is_bsp(CPUState *env) { - return env->cpuid_apic_id == 0; + /* We hard-wire the BSP to the first CPU. */ + return env->cpu_index == 0; +} + +DeviceState *cpu_get_current_apic(void) +{ + if (cpu_single_env) { + return cpu_single_env->apic_state; + } else { + return NULL; + } +} + +static DeviceState *apic_init(void *env, uint8_t apic_id) +{ + DeviceState *dev; + SysBusDevice *d; + static int apic_mapped; + + dev = qdev_create(NULL, "apic"); + qdev_prop_set_uint8(dev, "id", apic_id); + qdev_prop_set_ptr(dev, "cpu_env", env); + qdev_init_nofail(dev); + d = sysbus_from_qdev(dev); + + /* XXX: mapping more APICs at the same memory location */ + if (apic_mapped == 0) { + /* NOTE: the APIC is directly connected to the CPU - it is not + on the global memory bus. */ + /* XXX: what if the base changes? */ + sysbus_mmio_map(d, 0, MSI_ADDR_BASE); + apic_mapped = 1; + } + + msix_supported = 1; + + return dev; +} + +/* set CMOS shutdown status register (index 0xF) as S3_resume(0xFE) + BIOS will read it and start S3 resume at POST Entry */ +void pc_cmos_set_s3_resume(void *opaque, int irq, int level) +{ + ISADevice *s = opaque; + + if (level) { + rtc_set_memory(s, 0xF, 0xFE); + } +} + +void pc_acpi_smi_interrupt(void *opaque, int irq, int level) +{ + CPUState *s = opaque; + + if (level) { + cpu_interrupt(s, CPU_INTERRUPT_SMI); + } +} + +static void pc_cpu_reset(void *opaque) +{ + CPUState *env = opaque; + + cpu_reset(env); + env->halted = !cpu_is_bsp(env); } CPUState *pc_new_cpu(const char *cpu_model) { CPUState *env; + if (cpu_model == NULL) { +#ifdef TARGET_X86_64 + cpu_model = "qemu64"; +#else + cpu_model = "qemu32"; +#endif + } + env = cpu_init(cpu_model); if (!env) { fprintf(stderr, "Unable to find x86 CPU definition\n"); exit(1); } - env->kvm_cpu_state.regs_modified = 1; if ((env->cpuid_features & CPUID_APIC) || smp_cpus > 1) { env->cpuid_apic_id = env->cpu_index; - /* APIC reset callback resets cpu */ - apic_init(env); - } else { - qemu_register_reset((QEMUResetHandler*)cpu_reset, env); + env->apic_state = apic_init(env, env->cpuid_apic_id); } - - /* kvm needs this to run after the apic is initialized. Otherwise, - * it can access invalid state and crash. - */ - qemu_init_vcpu(env); + qemu_register_reset(pc_cpu_reset, env); + pc_cpu_reset(env); return env; } -/* PC hardware initialisation */ -static void pc_init1(ram_addr_t ram_size, - const char *boot_device, - const char *kernel_filename, - const char *kernel_cmdline, - const char *initrd_filename, - const char *cpu_model, - int pci_enabled) +void pc_cpus_init(const char *cpu_model) +{ + int i; + + /* init CPUs */ + for(i = 0; i < smp_cpus; i++) { + pc_new_cpu(cpu_model); + } +} + +void pc_memory_init(ram_addr_t ram_size, + const char *kernel_filename, + const char *kernel_cmdline, + const char *initrd_filename, + ram_addr_t *below_4g_mem_size_p, + ram_addr_t *above_4g_mem_size_p) { char *filename; int ret, linux_boot, i; ram_addr_t ram_addr, bios_offset, option_rom_offset; ram_addr_t below_4g_mem_size, above_4g_mem_size = 0; int bios_size, isa_bios_size; - PCIBus *pci_bus; - ISADevice *isa_dev; - int piix3_devfn = -1; - CPUState *env; - qemu_irq *cpu_irq; - qemu_irq *isa_irq; - qemu_irq *i8259; - IsaIrqState *isa_irq_state; - DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; - DriveInfo *fd[MAX_FD]; void *fw_cfg; if (ram_size >= 0xe0000000 ) { @@ -1016,46 +969,29 @@ } else { below_4g_mem_size = ram_size; } + *above_4g_mem_size_p = above_4g_mem_size; + *below_4g_mem_size_p = below_4g_mem_size; - linux_boot = (kernel_filename != NULL); - - /* init CPUs */ - if (cpu_model == NULL) { -#ifdef TARGET_X86_64 - cpu_model = "qemu64"; -#else - cpu_model = "qemu32"; -#endif - } - - if (kvm_enabled()) { - kvm_set_boot_cpu_id(0); - } - for (i = 0; i < smp_cpus; i++) { - env = pc_new_cpu(cpu_model); +#if TARGET_PHYS_ADDR_BITS == 32 + if (above_4g_mem_size > 0) { + hw_error("To much RAM for 32-bit physical address"); } - - vmport_init(); +#endif + linux_boot = (kernel_filename != NULL); /* allocate RAM */ - ram_addr = qemu_ram_alloc(below_4g_mem_size); + ram_addr = qemu_ram_alloc(NULL, "pc.ram", + below_4g_mem_size + above_4g_mem_size); cpu_register_physical_memory(0, 0xa0000, ram_addr); cpu_register_physical_memory(0x100000, below_4g_mem_size - 0x100000, ram_addr + 0x100000); - - /* above 4giga memory allocation */ +#if TARGET_PHYS_ADDR_BITS > 32 if (above_4g_mem_size > 0) { -#if TARGET_PHYS_ADDR_BITS == 32 - hw_error("To much RAM for 32-bit physical address"); -#else - ram_addr = qemu_ram_alloc(above_4g_mem_size); - cpu_register_physical_memory(0x100000000ULL, - above_4g_mem_size, - ram_addr); -#endif + cpu_register_physical_memory(0x100000000ULL, above_4g_mem_size, + ram_addr + below_4g_mem_size); } - +#endif /* BIOS load */ if (bios_name == NULL) @@ -1070,8 +1006,8 @@ (bios_size % 65536) != 0) { goto bios_error; } - bios_offset = qemu_ram_alloc(bios_size); - ret = rom_add_file_fixed(bios_name, (uint32_t)(-bios_size)); + bios_offset = qemu_ram_alloc(NULL, "pc.bios", bios_size); + ret = rom_add_file_fixed(bios_name, (uint32_t)(-bios_size), -1); if (ret != 0) { bios_error: fprintf(stderr, "qemu: could not load PC BIOS '%s'\n", bios_name); @@ -1086,18 +1022,20 @@ isa_bios_size = 128 * 1024; cpu_register_physical_memory(0xd0000, (192 * 1024) - isa_bios_size, IO_MEM_UNASSIGNED); - /* kvm tpr optimization needs the bios accessible for write, at least to qemu itself */ cpu_register_physical_memory(0x100000 - isa_bios_size, isa_bios_size, - (bios_offset + bios_size - isa_bios_size) /* | IO_MEM_ROM */); + (bios_offset + bios_size - isa_bios_size) | IO_MEM_ROM); if (extboot_drive) { - option_rom[nb_option_roms++] = qemu_strdup(EXTBOOT_FILENAME); + option_rom[nb_option_roms].name = qemu_strdup(EXTBOOT_FILENAME); + option_rom[nb_option_roms].bootindex = 0; + nb_option_roms++; } - option_rom[nb_option_roms++] = qemu_strdup(VAPIC_FILENAME); + option_rom[nb_option_roms].name = qemu_strdup(VAPIC_FILENAME); + option_rom[nb_option_roms].bootindex = -1; + nb_option_roms++; - rom_enable_driver_roms = 1; - option_rom_offset = qemu_ram_alloc(PC_ROM_SIZE); + option_rom_offset = qemu_ram_alloc(NULL, "pc.rom", PC_ROM_SIZE); cpu_register_physical_memory(PC_ROM_MIN_VGA, PC_ROM_SIZE, option_rom_offset); /* map all the bios at the top of memory */ @@ -1112,78 +1050,89 @@ } for (i = 0; i < nb_option_roms; i++) { - rom_add_option(option_rom[i]); - } - - cpu_irq = qemu_allocate_irqs(pic_irq_request, NULL, 1); -#ifdef KVM_CAP_IRQCHIP - if (kvm_enabled() && kvm_irqchip_in_kernel()) { - isa_irq_state = qemu_mallocz(sizeof(*isa_irq_state)); - isa_irq = i8259 = kvm_i8259_init(cpu_irq[0]); - } else -#endif - { - i8259 = i8259_init(cpu_irq[0]); - isa_irq_state = qemu_mallocz(sizeof(*isa_irq_state)); - isa_irq_state->i8259 = i8259; - isa_irq = qemu_allocate_irqs(isa_irq_handler, isa_irq_state, 24); - } - - if (pci_enabled) { - pci_bus = i440fx_init(&i440fx_state, &piix3_devfn, isa_irq); - } else { - pci_bus = NULL; - isa_bus_new(NULL); + rom_add_option(option_rom[i].name, option_rom[i].bootindex); } - isa_bus_irqs(isa_irq); - - ferr_irq = isa_reserve_irq(13); - - /* init basic PC hardware */ - register_ioport_write(0x80, 1, 1, ioport80_write, NULL); +} - register_ioport_write(0xf0, 1, 1, ioportF0_write, NULL); +qemu_irq *pc_allocate_cpu_irq(void) +{ + return qemu_allocate_irqs(pic_irq_request, NULL, 1); +} +void pc_vga_init(PCIBus *pci_bus) +{ if (cirrus_vga_enabled) { - if (pci_enabled) { + if (pci_bus) { pci_cirrus_vga_init(pci_bus); } else { isa_cirrus_vga_init(); } } else if (vmsvga_enabled) { - if (pci_enabled) + if (pci_bus) pci_vmsvga_init(pci_bus); else fprintf(stderr, "%s: vmware_vga: no PCI bus\n", __FUNCTION__); +#ifdef CONFIG_SPICE + } else if (qxl_enabled) { + if (pci_bus) + pci_create_simple(pci_bus, -1, "qxl-vga"); + else + fprintf(stderr, "%s: qxl: no PCI bus\n", __FUNCTION__); +#endif } else if (std_vga_enabled) { - if (pci_enabled) { - pci_vga_init(pci_bus, 0, 0); + if (pci_bus) { + pci_vga_init(pci_bus); } else { isa_vga_init(); } } +} + +static void cpu_request_exit(void *opaque, int irq, int level) +{ + CPUState *env = cpu_single_env; + + if (env && level) { + cpu_exit(env); + } +} - rtc_state = rtc_init(2000); +void pc_basic_device_init(qemu_irq *isa_irq, + FDCtrl **floppy_controller, + ISADevice **rtc_state) +{ + int i; + DriveInfo *fd[MAX_FD]; + PITState *pit; + qemu_irq rtc_irq = NULL; + qemu_irq *a20_line; + ISADevice *i8042, *port92; + qemu_irq *cpu_exit_irq; - qemu_register_boot_set(pc_boot_set, rtc_state); + register_ioport_write(0x80, 1, 1, ioport80_write, NULL); + + register_ioport_write(0xf0, 1, 1, ioportF0_write, NULL); - register_ioport_read(0x92, 1, 1, ioport92_read, NULL); - register_ioport_write(0x92, 1, 1, ioport92_write, NULL); + if (!no_hpet) { + DeviceState *hpet = sysbus_create_simple("hpet", HPET_BASE, NULL); - if (pci_enabled) { - isa_irq_state->ioapic = ioapic_init(); - ioapic_irq_hack = isa_irq; + for (i = 0; i < 24; i++) { + sysbus_connect_irq(sysbus_from_qdev(hpet), i, isa_irq[i]); + } + rtc_irq = qdev_get_gpio_in(hpet, 0); } + *rtc_state = rtc_init(2000, rtc_irq); + + qemu_register_boot_set(pc_boot_set, *rtc_state); + #ifdef CONFIG_KVM_PIT - if (kvm_enabled() && qemu_kvm_pit_in_kernel()) - pit = kvm_pit_init(0x40, isa_reserve_irq(0)); + if (kvm_enabled() && kvm_pit_in_kernel()) + pit = kvm_pit_init(0x40, isa_get_irq(0)); else #endif - pit = pit_init(0x40, isa_reserve_irq(0)); + + pit = pit_init(0x40, isa_get_irq(0)); pcspk_init(pit); - if (!no_hpet) { - hpet_init(isa_irq); - } for(i = 0; i < MAX_SERIAL_PORTS; i++) { if (serial_hds[i]) { @@ -1197,231 +1146,41 @@ } } - for(i = 0; i < nb_nics; i++) { - NICInfo *nd = &nd_table[i]; + a20_line = qemu_allocate_irqs(handle_a20_line_change, first_cpu, 2); + i8042 = isa_create_simple("i8042"); + i8042_setup_a20_line(i8042, &a20_line[0]); + vmmouse_init(i8042); + port92 = isa_create_simple("port92"); + port92_init(port92, &a20_line[1]); - if (!pci_enabled || (nd->model && strcmp(nd->model, "ne2k_isa") == 0)) - pc_init_ne2k_isa(nd); - else - pci_nic_init_nofail(nd, "rtl8139", NULL); - } - - if (drive_get_max_bus(IF_IDE) >= MAX_IDE_BUS) { - fprintf(stderr, "qemu: too many IDE bus\n"); - exit(1); - } - - for(i = 0; i < MAX_IDE_BUS * MAX_IDE_DEVS; i++) { - hd[i] = drive_get(IF_IDE, i / MAX_IDE_DEVS, i % MAX_IDE_DEVS); - } - - if (pci_enabled) { - pci_piix3_ide_init(pci_bus, hd, piix3_devfn + 1); - } else { - for(i = 0; i < MAX_IDE_BUS; i++) { - isa_ide_init(ide_iobase[i], ide_iobase2[i], ide_irq[i], - hd[MAX_IDE_DEVS * i], hd[MAX_IDE_DEVS * i + 1]); - } - } - - isa_dev = isa_create_simple("i8042"); - DMA_init(0); -#ifdef HAS_AUDIO - audio_init(pci_enabled ? pci_bus : NULL, isa_irq); -#endif + cpu_exit_irq = qemu_allocate_irqs(cpu_request_exit, NULL, 1); + DMA_init(0, cpu_exit_irq); for(i = 0; i < MAX_FD; i++) { fd[i] = drive_get(IF_FLOPPY, 0, i); } - floppy_controller = fdctrl_init_isa(fd); - - cmos_init(below_4g_mem_size, above_4g_mem_size, boot_device, hd); - - if (pci_enabled && usb_enabled) { - usb_uhci_piix3_init(pci_bus, piix3_devfn + 2); - } - - if (pci_enabled && acpi_enabled) { - uint8_t *eeprom_buf = qemu_mallocz(8 * 256); /* XXX: make this persistent */ - i2c_bus *smbus; - - /* TODO: Populate SPD eeprom data. */ - smbus = piix4_pm_init(pci_bus, piix3_devfn + 3, 0xb100, - isa_reserve_irq(9)); - for (i = 0; i < 8; i++) { - DeviceState *eeprom; - eeprom = qdev_create((BusState *)smbus, "smbus-eeprom"); - qdev_prop_set_uint8(eeprom, "address", 0x50 + i); - qdev_prop_set_ptr(eeprom, "data", eeprom_buf + (i * 256)); - qdev_init_nofail(eeprom); - } - piix4_acpi_system_hot_add_init(pci_bus, cpu_model); - } - - if (i440fx_state) { - i440fx_init_memory_mappings(i440fx_state); - } + *floppy_controller = fdctrl_init_isa(fd); +} - if (pci_enabled) { - int max_bus; - int bus; +void pc_pci_device_init(PCIBus *pci_bus) +{ + int max_bus; + int bus; - max_bus = drive_get_max_bus(IF_SCSI); - for (bus = 0; bus <= max_bus; bus++) { - pci_create_simple(pci_bus, -1, "lsi53c895a"); - } + max_bus = drive_get_max_bus(IF_SCSI); + for (bus = 0; bus <= max_bus; bus++) { + pci_create_simple(pci_bus, -1, "lsi53c895a"); } if (extboot_drive) { - DriveInfo *info = extboot_drive; - int cyls, heads, secs; - - if (info->type != IF_IDE && info->type != IF_VIRTIO) { - bdrv_guess_geometry(info->bdrv, &cyls, &heads, &secs); - bdrv_set_geometry_hint(info->bdrv, cyls, heads, secs); - } + DriveInfo *info = extboot_drive; + int cyls, heads, secs; - extboot_init(info->bdrv, 1); - } - - /* Add virtio console devices */ - if (pci_enabled) { - for(i = 0; i < MAX_VIRTIO_CONSOLES; i++) { - if (virtcon_hds[i]) { - pci_create_simple(pci_bus, -1, "virtio-console-pci"); - } + if (info->type != IF_IDE && info->type != IF_VIRTIO) { + bdrv_guess_geometry(info->bdrv, &cyls, &heads, &secs); + bdrv_set_geometry_hint(info->bdrv, cyls, heads, secs); } - } - -#ifdef CONFIG_KVM_DEVICE_ASSIGNMENT - if (kvm_enabled()) { - add_assigned_devices(pci_bus, assigned_devices, assigned_devices_index); - } -#endif /* CONFIG_KVM_DEVICE_ASSIGNMENT */ -} - -static void pc_init_pci(ram_addr_t ram_size, - const char *boot_device, - const char *kernel_filename, - const char *kernel_cmdline, - const char *initrd_filename, - const char *cpu_model) -{ - pc_init1(ram_size, boot_device, - kernel_filename, kernel_cmdline, - initrd_filename, cpu_model, 1); -} - -static void pc_init_isa(ram_addr_t ram_size, - const char *boot_device, - const char *kernel_filename, - const char *kernel_cmdline, - const char *initrd_filename, - const char *cpu_model) -{ - if (cpu_model == NULL) - cpu_model = "486"; - pc_init1(ram_size, boot_device, - kernel_filename, kernel_cmdline, - initrd_filename, cpu_model, 0); -} - -/* set CMOS shutdown status register (index 0xF) as S3_resume(0xFE) - BIOS will read it and start S3 resume at POST Entry */ -void cmos_set_s3_resume(void) -{ - if (rtc_state) - rtc_set_memory(rtc_state, 0xF, 0xFE); -} -static QEMUMachine pc_machine = { - .name = "pc-0.12", - .alias = "pc", - .desc = "Standard PC", - .init = pc_init_pci, - .max_cpus = 255, - .is_default = 1, -}; - -static QEMUMachine pc_machine_v0_11 = { - .name = "pc-0.11", - .desc = "Standard PC, qemu 0.11", - .init = pc_init_pci, - .max_cpus = 255, - .compat_props = (GlobalProperty[]) { - { - .driver = "virtio-blk-pci", - .property = "vectors", - .value = stringify(0), - },{ - .driver = "ide-drive", - .property = "ver", - .value = "0.11", - },{ - .driver = "scsi-disk", - .property = "ver", - .value = "0.11", - },{ - .driver = "PCI", - .property = "rombar", - .value = stringify(0), - }, - { /* end of list */ } + extboot_init(info->bdrv); } -}; - -static QEMUMachine pc_machine_v0_10 = { - .name = "pc-0.10", - .desc = "Standard PC, qemu 0.10", - .init = pc_init_pci, - .max_cpus = 255, - .compat_props = (GlobalProperty[]) { - { - .driver = "virtio-blk-pci", - .property = "class", - .value = stringify(PCI_CLASS_STORAGE_OTHER), - },{ - .driver = "virtio-console-pci", - .property = "class", - .value = stringify(PCI_CLASS_DISPLAY_OTHER), - },{ - .driver = "virtio-net-pci", - .property = "vectors", - .value = stringify(0), - },{ - .driver = "virtio-blk-pci", - .property = "vectors", - .value = stringify(0), - },{ - .driver = "ide-drive", - .property = "ver", - .value = "0.10", - },{ - .driver = "scsi-disk", - .property = "ver", - .value = "0.10", - },{ - .driver = "PCI", - .property = "rombar", - .value = stringify(0), - }, - { /* end of list */ } - }, -}; - -static QEMUMachine isapc_machine = { - .name = "isapc", - .desc = "ISA-only PC", - .init = pc_init_isa, - .max_cpus = 1, -}; - -static void pc_machine_init(void) -{ - qemu_register_machine(&pc_machine); - qemu_register_machine(&pc_machine_v0_11); - qemu_register_machine(&pc_machine_v0_10); - qemu_register_machine(&isapc_machine); } - -machine_init(pc_machine_init); diff -Nru qemu-kvm-0.12.5+noroms/hw/pc.h qemu-kvm-0.14.1/hw/pc.h --- qemu-kvm-0.12.5+noroms/hw/pc.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/pc.h 2011-05-11 13:29:46.000000000 +0000 @@ -2,6 +2,9 @@ #define HW_PC_H #include "qemu-common.h" +#include "ioport.h" +#include "isa.h" +#include "fdc.h" /* PC-style peripherals (also used by other machines). */ @@ -11,7 +14,8 @@ CharDriverState *chr); SerialState *serial_mm_init (target_phys_addr_t base, int it_shift, qemu_irq irq, int baudbase, - CharDriverState *chr, int ioregister); + CharDriverState *chr, int ioregister, + int be); SerialState *serial_isa_init(int index, CharDriverState *chr); void serial_set_frequency(SerialState *s, uint32_t frequency); @@ -35,21 +39,15 @@ void pic_info(Monitor *mon); void irq_info(Monitor *mon); -/* APIC */ -typedef struct IOAPICState IOAPICState; -void apic_deliver_irq(uint8_t dest, uint8_t dest_mode, - uint8_t delivery_mode, - uint8_t vector_num, uint8_t polarity, - uint8_t trigger_mode); -int apic_init(CPUState *env); -int apic_accept_pic_intr(CPUState *env); -void apic_deliver_pic_intr(CPUState *env, int level); -int apic_get_interrupt(CPUState *env); -qemu_irq *ioapic_init(void); -void ioapic_set_irq(void *opaque, int vector, int level); -void apic_reset_irq_delivered(void); -int apic_get_irq_delivered(void); -void apic_set_irq_delivered(void); +/* ISA */ +#define IOAPIC_NUM_PINS 0x18 + +typedef struct isa_irq_state { + qemu_irq *i8259; + qemu_irq ioapic[IOAPIC_NUM_PINS]; +} IsaIrqState; + +void isa_irq_handler(void *opaque, int n, int level); /* i8254.c */ @@ -68,8 +66,8 @@ PITState *kvm_pit_init(int base, qemu_irq irq); -void hpet_disable_pit(void); -void hpet_enable_pit(void); +void hpet_pit_disable(void); +void hpet_pit_enable(void); /* vmport.c */ void vmport_init(void); @@ -84,22 +82,37 @@ void i8042_mm_init(qemu_irq kbd_irq, qemu_irq mouse_irq, target_phys_addr_t base, ram_addr_t size, target_phys_addr_t mask); - -/* mc146818rtc.c */ - -typedef struct RTCState RTCState; - -RTCState *rtc_init(int base_year); -void rtc_set_memory(RTCState *s, int addr, int val); -void rtc_set_date(RTCState *s, const struct tm *tm); -void cmos_set_s3_resume(void); +void i8042_isa_mouse_fake_event(void *opaque); +void i8042_setup_a20_line(ISADevice *dev, qemu_irq *a20_out); /* pc.c */ extern int fd_bootchk; -void ioport_set_a20(int enable); -int ioport_get_a20(void); -CPUState *pc_new_cpu(const char *cpu_model); +void pc_register_ferr_irq(qemu_irq irq); +void pc_cmos_set_s3_resume(void *opaque, int irq, int level); +void pc_acpi_smi_interrupt(void *opaque, int irq, int level); + +void pc_cpus_init(const char *cpu_model); +void pc_memory_init(ram_addr_t ram_size, + const char *kernel_filename, + const char *kernel_cmdline, + const char *initrd_filename, + ram_addr_t *below_4g_mem_size_p, + ram_addr_t *above_4g_mem_size_p); +qemu_irq *pc_allocate_cpu_irq(void); +void pc_vga_init(PCIBus *pci_bus); +void pc_basic_device_init(qemu_irq *isa_irq, + FDCtrl **floppy_controller, + ISADevice **rtc_state); +void pc_init_ne2k_isa(NICInfo *nd); +void pc_cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size, + const char *boot_device, + BusState *ide0, BusState *ide1, + FDCtrl *floppy_controller, ISADevice *s); +void pc_pci_device_init(PCIBus *pci_bus); + +typedef void (*cpu_set_smm_t)(int smm, void *arg); +void cpu_smm_register(cpu_set_smm_t callback, void *arg); /* acpi.c */ extern int acpi_enabled; @@ -110,10 +123,11 @@ int acpi_table_add(const char *table_desc); /* acpi_piix.c */ + i2c_bus *piix4_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base, - qemu_irq sci_irq); + qemu_irq sci_irq, qemu_irq cmos_s3, qemu_irq smi_irq, + int kvm_enabled); void piix4_smbus_register_device(SMBusDevice *dev, uint8_t addr); -void piix4_acpi_system_hot_add_init(PCIBus *bus, const char *model); /* hpet.c */ extern int no_hpet; @@ -129,8 +143,7 @@ struct PCII440FXState; typedef struct PCII440FXState PCII440FXState; -PCIBus *i440fx_init(PCII440FXState **pi440fx_state, int *piix_devfn, qemu_irq *pic); -void i440fx_set_smm(PCII440FXState *d, int val); +PCIBus *i440fx_init(PCII440FXState **pi440fx_state, int *piix_devfn, qemu_irq *pic, ram_addr_t ram_size); void i440fx_init_memory_mappings(PCII440FXState *d); /* piix4.c */ @@ -150,8 +163,7 @@ extern enum vga_retrace_method vga_retrace_method; int isa_vga_init(void); -int pci_vga_init(PCIBus *bus, - unsigned long vga_bios_offset, int vga_bios_size); +int pci_vga_init(PCIBus *bus); int isa_vga_mm_init(target_phys_addr_t vram_base, target_phys_addr_t ctrl_base, int it_shift); @@ -165,8 +177,15 @@ /* extboot.c */ -void extboot_init(BlockDriverState *bs, int cmd); +void extboot_init(BlockDriverState *bs); + +/* e820 types */ +#define E820_RAM 1 +#define E820_RESERVED 2 +#define E820_ACPI 3 +#define E820_NVS 4 +#define E820_UNUSABLE 5 -int cpu_is_bsp(CPUState *env); +int e820_add_entry(uint64_t, uint64_t, uint32_t); #endif diff -Nru qemu-kvm-0.12.5+noroms/hw/pci_bridge.c qemu-kvm-0.14.1/hw/pci_bridge.c --- qemu-kvm-0.12.5+noroms/hw/pci_bridge.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/pci_bridge.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,275 @@ +/* + * QEMU PCI bus manager + * + * Copyright (c) 2004 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 dea + + * 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. + */ +/* + * split out from pci.c + * Copyright (c) 2010 Isaku Yamahata + * VA Linux Systems Japan K.K. + */ + +#include "pci_bridge.h" +#include "pci_internals.h" +#include "range.h" + +/* PCI bridge subsystem vendor ID helper functions */ +#define PCI_SSVID_SIZEOF 8 +#define PCI_SSVID_SVID 4 +#define PCI_SSVID_SSID 6 + +int pci_bridge_ssvid_init(PCIDevice *dev, uint8_t offset, + uint16_t svid, uint16_t ssid) +{ + int pos; + pos = pci_add_capability(dev, PCI_CAP_ID_SSVID, offset, PCI_SSVID_SIZEOF); + if (pos < 0) { + return pos; + } + + pci_set_word(dev->config + pos + PCI_SSVID_SVID, svid); + pci_set_word(dev->config + pos + PCI_SSVID_SSID, ssid); + return pos; +} + +/* Accessor function to get parent bridge device from pci bus. */ +PCIDevice *pci_bridge_get_device(PCIBus *bus) +{ + return bus->parent_dev; +} + +/* Accessor function to get secondary bus from pci-to-pci bridge device */ +PCIBus *pci_bridge_get_sec_bus(PCIBridge *br) +{ + return &br->sec_bus; +} + +static uint32_t pci_config_get_io_base(const PCIDevice *d, + uint32_t base, uint32_t base_upper16) +{ + uint32_t val; + + val = ((uint32_t)d->config[base] & PCI_IO_RANGE_MASK) << 8; + if (d->config[base] & PCI_IO_RANGE_TYPE_32) { + val |= (uint32_t)pci_get_word(d->config + base_upper16) << 16; + } + return val; +} + +static pcibus_t pci_config_get_memory_base(const PCIDevice *d, uint32_t base) +{ + return ((pcibus_t)pci_get_word(d->config + base) & PCI_MEMORY_RANGE_MASK) + << 16; +} + +static pcibus_t pci_config_get_pref_base(const PCIDevice *d, + uint32_t base, uint32_t upper) +{ + pcibus_t tmp; + pcibus_t val; + + tmp = (pcibus_t)pci_get_word(d->config + base); + val = (tmp & PCI_PREF_RANGE_MASK) << 16; + if (tmp & PCI_PREF_RANGE_TYPE_64) { + val |= (pcibus_t)pci_get_long(d->config + upper) << 32; + } + return val; +} + +/* accessor function to get bridge filtering base address */ +pcibus_t pci_bridge_get_base(const PCIDevice *bridge, uint8_t type) +{ + pcibus_t base; + if (type & PCI_BASE_ADDRESS_SPACE_IO) { + base = pci_config_get_io_base(bridge, + PCI_IO_BASE, PCI_IO_BASE_UPPER16); + } else { + if (type & PCI_BASE_ADDRESS_MEM_PREFETCH) { + base = pci_config_get_pref_base( + bridge, PCI_PREF_MEMORY_BASE, PCI_PREF_BASE_UPPER32); + } else { + base = pci_config_get_memory_base(bridge, PCI_MEMORY_BASE); + } + } + + return base; +} + +/* accessor funciton to get bridge filtering limit */ +pcibus_t pci_bridge_get_limit(const PCIDevice *bridge, uint8_t type) +{ + pcibus_t limit; + if (type & PCI_BASE_ADDRESS_SPACE_IO) { + limit = pci_config_get_io_base(bridge, + PCI_IO_LIMIT, PCI_IO_LIMIT_UPPER16); + limit |= 0xfff; /* PCI bridge spec 3.2.5.6. */ + } else { + if (type & PCI_BASE_ADDRESS_MEM_PREFETCH) { + limit = pci_config_get_pref_base( + bridge, PCI_PREF_MEMORY_LIMIT, PCI_PREF_LIMIT_UPPER32); + } else { + limit = pci_config_get_memory_base(bridge, PCI_MEMORY_LIMIT); + } + limit |= 0xfffff; /* PCI bridge spec 3.2.5.{1, 8}. */ + } + return limit; +} + +/* default write_config function for PCI-to-PCI bridge */ +void pci_bridge_write_config(PCIDevice *d, + uint32_t address, uint32_t val, int len) +{ + PCIBridge *s = container_of(d, PCIBridge, dev); + uint16_t oldctl = pci_get_word(d->config + PCI_BRIDGE_CONTROL); + uint16_t newctl; + + pci_default_write_config(d, address, val, len); + + if (/* io base/limit */ + ranges_overlap(address, len, PCI_IO_BASE, 2) || + + /* memory base/limit, prefetchable base/limit and + io base/limit upper 16 */ + ranges_overlap(address, len, PCI_MEMORY_BASE, 20)) { + pci_bridge_update_mappings(&s->sec_bus); + } + + newctl = pci_get_word(d->config + PCI_BRIDGE_CONTROL); + if (~oldctl & newctl & PCI_BRIDGE_CTL_BUS_RESET) { + /* Trigger hot reset on 0->1 transition. */ + pci_bus_reset(&s->sec_bus); + } +} + +void pci_bridge_disable_base_limit(PCIDevice *dev) +{ + uint8_t *conf = dev->config; + + pci_byte_test_and_set_mask(conf + PCI_IO_BASE, + PCI_IO_RANGE_MASK & 0xff); + pci_byte_test_and_clear_mask(conf + PCI_IO_LIMIT, + PCI_IO_RANGE_MASK & 0xff); + pci_word_test_and_set_mask(conf + PCI_MEMORY_BASE, + PCI_MEMORY_RANGE_MASK & 0xffff); + pci_word_test_and_clear_mask(conf + PCI_MEMORY_LIMIT, + PCI_MEMORY_RANGE_MASK & 0xffff); + pci_word_test_and_set_mask(conf + PCI_PREF_MEMORY_BASE, + PCI_PREF_RANGE_MASK & 0xffff); + pci_word_test_and_clear_mask(conf + PCI_PREF_MEMORY_LIMIT, + PCI_PREF_RANGE_MASK & 0xffff); + pci_set_word(conf + PCI_PREF_BASE_UPPER32, 0); + pci_set_word(conf + PCI_PREF_LIMIT_UPPER32, 0); +} + +/* reset bridge specific configuration registers */ +void pci_bridge_reset_reg(PCIDevice *dev) +{ + uint8_t *conf = dev->config; + + conf[PCI_PRIMARY_BUS] = 0; + conf[PCI_SECONDARY_BUS] = 0; + conf[PCI_SUBORDINATE_BUS] = 0; + conf[PCI_SEC_LATENCY_TIMER] = 0; + + /* + * the default values for base/limit registers aren't specified + * in the PCI-to-PCI-bridge spec. So we don't thouch them here. + * Each implementation can override it. + * typical implementation does + * zero base/limit registers or + * disable forwarding: pci_bridge_disable_base_limit() + * If disable forwarding is wanted, call pci_bridge_disable_base_limit() + * after this function. + */ + pci_byte_test_and_clear_mask(conf + PCI_IO_BASE, + PCI_IO_RANGE_MASK & 0xff); + pci_byte_test_and_clear_mask(conf + PCI_IO_LIMIT, + PCI_IO_RANGE_MASK & 0xff); + pci_word_test_and_clear_mask(conf + PCI_MEMORY_BASE, + PCI_MEMORY_RANGE_MASK & 0xffff); + pci_word_test_and_clear_mask(conf + PCI_MEMORY_LIMIT, + PCI_MEMORY_RANGE_MASK & 0xffff); + pci_word_test_and_clear_mask(conf + PCI_PREF_MEMORY_BASE, + PCI_PREF_RANGE_MASK & 0xffff); + pci_word_test_and_clear_mask(conf + PCI_PREF_MEMORY_LIMIT, + PCI_PREF_RANGE_MASK & 0xffff); + pci_set_word(conf + PCI_PREF_BASE_UPPER32, 0); + pci_set_word(conf + PCI_PREF_LIMIT_UPPER32, 0); + + pci_set_word(conf + PCI_BRIDGE_CONTROL, 0); +} + +/* default reset function for PCI-to-PCI bridge */ +void pci_bridge_reset(DeviceState *qdev) +{ + PCIDevice *dev = DO_UPCAST(PCIDevice, qdev, qdev); + pci_bridge_reset_reg(dev); +} + +/* default qdev initialization function for PCI-to-PCI bridge */ +int pci_bridge_initfn(PCIDevice *dev) +{ + PCIBus *parent = dev->bus; + PCIBridge *br = DO_UPCAST(PCIBridge, dev, dev); + PCIBus *sec_bus = &br->sec_bus; + + pci_set_word(dev->config + PCI_STATUS, + PCI_STATUS_66MHZ | PCI_STATUS_FAST_BACK); + pci_config_set_class(dev->config, PCI_CLASS_BRIDGE_PCI); + dev->config[PCI_HEADER_TYPE] = + (dev->config[PCI_HEADER_TYPE] & PCI_HEADER_TYPE_MULTI_FUNCTION) | + PCI_HEADER_TYPE_BRIDGE; + pci_set_word(dev->config + PCI_SEC_STATUS, + PCI_STATUS_66MHZ | PCI_STATUS_FAST_BACK); + + qbus_create_inplace(&sec_bus->qbus, &pci_bus_info, &dev->qdev, + br->bus_name); + sec_bus->parent_dev = dev; + sec_bus->map_irq = br->map_irq; + + QLIST_INIT(&sec_bus->child); + QLIST_INSERT_HEAD(&parent->child, sec_bus, sibling); + return 0; +} + +/* default qdev clean up function for PCI-to-PCI bridge */ +int pci_bridge_exitfn(PCIDevice *pci_dev) +{ + PCIBridge *s = DO_UPCAST(PCIBridge, dev, pci_dev); + assert(QLIST_EMPTY(&s->sec_bus.child)); + QLIST_REMOVE(&s->sec_bus, sibling); + /* qbus_free() is called automatically by qdev_free() */ + return 0; +} + +/* + * before qdev initialization(qdev_init()), this function sets bus_name and + * map_irq callback which are necessry for pci_bridge_initfn() to + * initialize bus. + */ +void pci_bridge_map_irq(PCIBridge *br, const char* bus_name, + pci_map_irq_fn map_irq) +{ + br->map_irq = map_irq; + br->bus_name = bus_name; +} diff -Nru qemu-kvm-0.12.5+noroms/hw/pci_bridge.h qemu-kvm-0.14.1/hw/pci_bridge.h --- qemu-kvm-0.12.5+noroms/hw/pci_bridge.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/pci_bridge.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,66 @@ +/* + * QEMU PCI bridge + * + * Copyright (c) 2004 Fabrice Bellard + * + * 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, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * split out pci bus specific stuff from pci.[hc] to pci_bridge.[hc] + * Copyright (c) 2009 Isaku Yamahata + * VA Linux Systems Japan K.K. + * + */ + +#ifndef QEMU_PCI_BRIDGE_H +#define QEMU_PCI_BRIDGE_H + +#include "pci.h" + +int pci_bridge_ssvid_init(PCIDevice *dev, uint8_t offset, + uint16_t svid, uint16_t ssid); + +PCIDevice *pci_bridge_get_device(PCIBus *bus); +PCIBus *pci_bridge_get_sec_bus(PCIBridge *br); + +pcibus_t pci_bridge_get_base(const PCIDevice *bridge, uint8_t type); +pcibus_t pci_bridge_get_limit(const PCIDevice *bridge, uint8_t type); + +void pci_bridge_write_config(PCIDevice *d, + uint32_t address, uint32_t val, int len); +void pci_bridge_disable_base_limit(PCIDevice *dev); +void pci_bridge_reset_reg(PCIDevice *dev); +void pci_bridge_reset(DeviceState *qdev); + +int pci_bridge_initfn(PCIDevice *pci_dev); +int pci_bridge_exitfn(PCIDevice *pci_dev); + + +/* + * before qdev initialization(qdev_init()), this function sets bus_name and + * map_irq callback which are necessry for pci_bridge_initfn() to + * initialize bus. + */ +void pci_bridge_map_irq(PCIBridge *br, const char* bus_name, + pci_map_irq_fn map_irq); + +#endif /* QEMU_PCI_BRIDGE_H */ +/* + * Local variables: + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 8 + * indent-tab-mode: nil + * End: + */ diff -Nru qemu-kvm-0.12.5+noroms/hw/pci.c qemu-kvm-0.14.1/hw/pci.c --- qemu-kvm-0.12.5+noroms/hw/pci.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/pci.c 2011-05-11 13:29:46.000000000 +0000 @@ -23,13 +23,17 @@ */ #include "hw.h" #include "pci.h" +#include "pci_bridge.h" +#include "pci_internals.h" #include "monitor.h" #include "net.h" #include "sysemu.h" #include "loader.h" -#include "qemu-kvm.h" #include "hw/pc.h" +#include "kvm.h" #include "device-assignment.h" +#include "qemu-objects.h" +#include "range.h" //#define DEBUG_PCI #ifdef DEBUG_PCI @@ -38,45 +42,35 @@ # define PCI_DPRINTF(format, ...) do { } while (0) #endif -struct PCIBus { - BusState qbus; - int devfn_min; - pci_set_irq_fn set_irq; - pci_map_irq_fn map_irq; - pci_hotplug_fn hotplug; - uint32_t config_reg; /* XXX: suppress */ - void *irq_opaque; - PCIDevice *devices[256]; - PCIDevice *parent_dev; - - QLIST_HEAD(, PCIBus) child; /* this will be replaced by qdev later */ - QLIST_ENTRY(PCIBus) sibling;/* this will be replaced by qdev later */ - - /* The bus IRQ state is the logical OR of the connected devices. - Keep a count of the number of devices with raised IRQs. */ - int nirq; - int *irq_count; -}; - static void pcibus_dev_print(Monitor *mon, DeviceState *dev, int indent); +static char *pcibus_get_dev_path(DeviceState *dev); +static char *pcibus_get_fw_dev_path(DeviceState *dev); +static int pcibus_reset(BusState *qbus); -static struct BusInfo pci_bus_info = { +struct BusInfo pci_bus_info = { .name = "PCI", .size = sizeof(PCIBus), .print_dev = pcibus_dev_print, + .get_dev_path = pcibus_get_dev_path, + .get_fw_dev_path = pcibus_get_fw_dev_path, + .reset = pcibus_reset, .props = (Property[]) { DEFINE_PROP_PCI_DEVFN("addr", PCIDevice, devfn, -1), DEFINE_PROP_STRING("romfile", PCIDevice, romfile), DEFINE_PROP_UINT32("rombar", PCIDevice, rom_bar, 1), + DEFINE_PROP_BIT("multifunction", PCIDevice, cap_present, + QEMU_PCI_CAP_MULTIFUNCTION_BITNR, false), + DEFINE_PROP_BIT("command_serr_enable", PCIDevice, cap_present, + QEMU_PCI_CAP_SERR_BITNR, true), DEFINE_PROP_END_OF_LIST() } }; static void pci_update_mappings(PCIDevice *d); static void pci_set_irq(void *opaque, int irq_num, int level); -static int pci_add_option_rom(PCIDevice *pdev); +static int pci_add_option_rom(PCIDevice *pdev, bool is_default_rom); +static void pci_del_option_rom(PCIDevice *pdev); -target_phys_addr_t pci_mem_base; static uint16_t pci_default_sub_vendor_id = PCI_SUBVENDOR_ID_REDHAT_QUMRANET; static uint16_t pci_default_sub_device_id = PCI_SUBDEVICE_ID_QEMU; @@ -146,28 +140,61 @@ } } -static void pci_device_reset(PCIDevice *dev) +void pci_device_deassert_intx(PCIDevice *dev) +{ + int i; + for (i = 0; i < PCI_NUM_PINS; ++i) { + qemu_set_irq(dev->irq[i], 0); + } +} + +/* + * This function is called on #RST and FLR. + * FLR if PCI_EXP_DEVCTL_BCR_FLR is set + */ +void pci_device_reset(PCIDevice *dev) { int r; + /* TODO: call the below unconditionally once all pci devices + * are qdevified */ + if (dev->qdev.info) { + qdev_reset_all(&dev->qdev); + } dev->irq_state = 0; pci_update_irq_status(dev); - dev->config[PCI_COMMAND] &= ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY | - PCI_COMMAND_MASTER); + pci_device_deassert_intx(dev); + /* Clear all writeable bits */ + pci_word_test_and_clear_mask(dev->config + PCI_COMMAND, + pci_get_word(dev->wmask + PCI_COMMAND) | + pci_get_word(dev->w1cmask + PCI_COMMAND)); + pci_word_test_and_clear_mask(dev->config + PCI_STATUS, + pci_get_word(dev->wmask + PCI_STATUS) | + pci_get_word(dev->w1cmask + PCI_STATUS)); dev->config[PCI_CACHE_LINE_SIZE] = 0x0; dev->config[PCI_INTERRUPT_LINE] = 0x0; for (r = 0; r < PCI_NUM_REGIONS; ++r) { - if (!dev->io_regions[r].size) { + PCIIORegion *region = &dev->io_regions[r]; + if (!region->size) { continue; } - pci_set_long(dev->config + pci_bar(dev, r), dev->io_regions[r].type); + + if (!(region->type & PCI_BASE_ADDRESS_SPACE_IO) && + region->type & PCI_BASE_ADDRESS_MEM_TYPE_64) { + pci_set_quad(dev->config + pci_bar(dev, r), region->type); + } else { + pci_set_long(dev->config + pci_bar(dev, r), region->type); + } } pci_update_mappings(dev); } -static void pci_bus_reset(void *opaque) +/* + * Trigger pci bus reset under a given bus. + * To be called on RST# assert. + */ +void pci_bus_reset(PCIBus *bus) { - PCIBus *bus = opaque; int i; for (i = 0; i < bus->nirq; i++) { @@ -180,6 +207,15 @@ } } +static int pcibus_reset(BusState *qbus) +{ + pci_bus_reset(DO_UPCAST(PCIBus, qbus, qbus)); + + /* topology traverse is done by pci_bus_reset(). + Tell qbus/qdev walker not to traverse the tree */ + return 1; +} + static void pci_host_bus_register(int domain, PCIBus *bus) { struct PCIHostBus *host; @@ -202,18 +238,38 @@ return NULL; } +int pci_find_domain(const PCIBus *bus) +{ + PCIDevice *d; + struct PCIHostBus *host; + + /* obtain root bus */ + while ((d = bus->parent_dev) != NULL) { + bus = d->bus; + } + + QLIST_FOREACH(host, &host_buses, next) { + if (host->bus == bus) { + return host->domain; + } + } + + abort(); /* should not be reached */ + return -1; +} + void pci_bus_new_inplace(PCIBus *bus, DeviceState *parent, const char *name, int devfn_min) { qbus_create_inplace(&bus->qbus, &pci_bus_info, parent, name); + assert(PCI_FUNC(devfn_min) == 0); bus->devfn_min = devfn_min; /* host bridge */ QLIST_INIT(&bus->child); pci_host_bus_register(0, bus); /* for now only pci domain 0 is supported */ - vmstate_register(-1, &vmstate_pcibus, bus); - qemu_register_reset(pci_bus_reset, bus); + vmstate_register(NULL, -1, &vmstate_pcibus, bus); } PCIBus *pci_bus_new(DeviceState *parent, const char *name, int devfn_min) @@ -236,10 +292,16 @@ bus->irq_count = qemu_mallocz(nirq * sizeof(bus->irq_count[0])); } -void pci_bus_hotplug(PCIBus *bus, pci_hotplug_fn hotplug) +void pci_bus_hotplug(PCIBus *bus, pci_hotplug_fn hotplug, DeviceState *qdev) { bus->qbus.allow_hotplug = 1; bus->hotplug = hotplug; + bus->hotplug_qdev = qdev; +} + +void pci_bus_set_mem_base(PCIBus *bus, target_phys_addr_t base) +{ + bus->mem_base = base; } PCIBus *pci_register_bus(DeviceState *parent, const char *name, @@ -253,26 +315,6 @@ return bus; } -static void pci_register_secondary_bus(PCIBus *parent, - PCIBus *bus, - PCIDevice *dev, - pci_map_irq_fn map_irq, - const char *name) -{ - qbus_create_inplace(&bus->qbus, &pci_bus_info, &dev->qdev, name); - bus->map_irq = map_irq; - bus->parent_dev = dev; - - QLIST_INIT(&bus->child); - QLIST_INSERT_HEAD(&parent->child, bus, sibling); -} - -static void pci_unregister_secondary_bus(PCIBus *bus) -{ - assert(QLIST_EMPTY(&bus->child)); - QLIST_REMOVE(bus, sibling); -} - int pci_bus_num(PCIBus *s) { if (!s->parent_dev) @@ -291,7 +333,8 @@ qemu_get_buffer(f, config, size); for (i = 0; i < size; ++i) { - if ((config[i] ^ s->config[i]) & s->cmask[i] & ~s->wmask[i]) { + if ((config[i] ^ s->config[i]) & + s->cmask[i] & ~s->wmask[i] & ~s->w1cmask[i]) { qemu_free(config); return -EINVAL; } @@ -415,27 +458,27 @@ return ret; } -static int pci_set_default_subsystem_id(PCIDevice *pci_dev) +static void pci_set_default_subsystem_id(PCIDevice *pci_dev) { - uint16_t *id; - - id = (void*)(&pci_dev->config[PCI_SUBVENDOR_ID]); - id[0] = cpu_to_le16(pci_default_sub_vendor_id); - id[1] = cpu_to_le16(pci_default_sub_device_id); - return 0; + pci_set_word(pci_dev->config + PCI_SUBSYSTEM_VENDOR_ID, + pci_default_sub_vendor_id); + pci_set_word(pci_dev->config + PCI_SUBSYSTEM_ID, + pci_default_sub_device_id); } /* - * Parse pci address in qemu command - * Parse [[:]:], return -1 on error + * Parse [[:]:], return -1 on error if funcp == NULL + * [[:]:]., return -1 on error */ -static int pci_parse_devaddr(const char *addr, int *domp, int *busp, unsigned *slotp) +int pci_parse_devaddr(const char *addr, int *domp, int *busp, + unsigned int *slotp, unsigned int *funcp) { const char *p; char *e; unsigned long val; unsigned long dom = 0, bus = 0; - unsigned slot = 0; + unsigned int slot = 0; + unsigned int func = 0; p = addr; val = strtoul(p, &e, 16); @@ -457,11 +500,24 @@ } } - if (dom > 0xffff || bus > 0xff || val > 0x1f) - return -1; - slot = val; + if (funcp != NULL) { + if (*e != '.') + return -1; + + p = e + 1; + val = strtoul(p, &e, 16); + if (e == p) + return -1; + + func = val; + } + + /* if funcp == NULL func is 0 */ + if (dom > 0xffff || bus > 0xff || slot > 0x1f || func > 7) + return -1; + if (*e) return -1; @@ -472,25 +528,54 @@ *domp = dom; *busp = bus; *slotp = slot; + if (funcp != NULL) + *funcp = func; return 0; } /* - * Parse device bdf in device assignment command: + * Parse device seg and bdf in device assignment command: * - * -pcidevice host=bus:dev.func + * -pcidevice host=[seg:]bus:dev.func * - * Parse :. return -1 on error + * Parse [seg:]:. return -1 on error */ -int pci_parse_host_devaddr(const char *addr, int *busp, +int pci_parse_host_devaddr(const char *addr, int *segp, int *busp, int *slotp, int *funcp) { const char *p; char *e; int val; - int bus = 0, slot = 0, func = 0; + int seg = 0, bus = 0, slot = 0, func = 0; + + /* parse optional seg */ + p = addr; + val = 0; + while (1) { + p = strchr(p, ':'); + if (p) { + val++; + p++; + } else + break; + } + if (val <= 0 || val > 2) + return -1; p = addr; + if (val == 2) { + val = strtoul(p, &e, 16); + if (e == p) + return -1; + if (*e == ':') { + seg = val; + p = e + 1; + } + } else + seg = 0; + + + /* parse bdf */ val = strtoul(p, &e, 16); if (e == p) return -1; @@ -512,12 +597,13 @@ } else return -1; - if (bus > 0xff || slot > 0x1f || func > 0x7) + if (seg > 0xffff || bus > 0xff || slot > 0x1f || func > 0x7) return -1; if (*e) return -1; + *segp = seg; *busp = bus; *slotp = slot; *funcp = func; @@ -531,7 +617,7 @@ if (!strncmp(addr, "pci_addr=", 9)) { addr += 9; } - if (pci_parse_devaddr(addr, domp, busp, slotp)) { + if (pci_parse_devaddr(addr, domp, busp, slotp, NULL)) { monitor_printf(mon, "Invalid pci address\n"); return -1; } @@ -548,12 +634,12 @@ return pci_find_bus(pci_find_root_bus(0), 0); } - if (pci_parse_devaddr(devaddr, &dom, &bus, &slot) < 0) { + if (pci_parse_devaddr(devaddr, &dom, &bus, &slot, NULL) < 0) { return NULL; } *devfnp = slot << 3; - return pci_find_bus(pci_find_root_bus(0), bus); + return pci_find_bus(pci_find_root_bus(dom), bus); } static void pci_init_cmask(PCIDevice *dev) @@ -575,12 +661,28 @@ dev->wmask[PCI_CACHE_LINE_SIZE] = 0xff; dev->wmask[PCI_INTERRUPT_LINE] = 0xff; pci_set_word(dev->wmask + PCI_COMMAND, - PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); + PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | + PCI_COMMAND_INTX_DISABLE); + if (dev->cap_present & QEMU_PCI_CAP_SERR) { + pci_word_test_and_set_mask(dev->wmask + PCI_COMMAND, PCI_COMMAND_SERR); + } memset(dev->wmask + PCI_CONFIG_HEADER_SIZE, 0xff, config_size - PCI_CONFIG_HEADER_SIZE); } +static void pci_init_w1cmask(PCIDevice *dev) +{ + /* + * Note: It's okay to set w1cmask even for readonly bits as + * long as their value is hardwired to 0. + */ + pci_set_word(dev->w1cmask + PCI_STATUS, + PCI_STATUS_PARITY | PCI_STATUS_SIG_TARGET_ABORT | + PCI_STATUS_REC_TARGET_ABORT | PCI_STATUS_REC_MASTER_ABORT | + PCI_STATUS_SIG_SYSTEM_ERROR | PCI_STATUS_DETECTED_PARITY); +} + static void pci_init_wmask_bridge(PCIDevice *d) { /* PCI_PRIMARY_BUS, PCI_SECONDARY_BUS, PCI_SUBORDINATE_BUS and @@ -602,7 +704,76 @@ /* PCI_PREF_BASE_UPPER32 and PCI_PREF_LIMIT_UPPER32 */ memset(d->wmask + PCI_PREF_BASE_UPPER32, 0xff, 8); - pci_set_word(d->wmask + PCI_BRIDGE_CONTROL, 0xffff); +/* TODO: add this define to pci_regs.h in linux and then in qemu. */ +#define PCI_BRIDGE_CTL_VGA_16BIT 0x10 /* VGA 16-bit decode */ +#define PCI_BRIDGE_CTL_DISCARD 0x100 /* Primary discard timer */ +#define PCI_BRIDGE_CTL_SEC_DISCARD 0x200 /* Secondary discard timer */ +#define PCI_BRIDGE_CTL_DISCARD_STATUS 0x400 /* Discard timer status */ +#define PCI_BRIDGE_CTL_DISCARD_SERR 0x800 /* Discard timer SERR# enable */ + pci_set_word(d->wmask + PCI_BRIDGE_CONTROL, + PCI_BRIDGE_CTL_PARITY | + PCI_BRIDGE_CTL_SERR | + PCI_BRIDGE_CTL_ISA | + PCI_BRIDGE_CTL_VGA | + PCI_BRIDGE_CTL_VGA_16BIT | + PCI_BRIDGE_CTL_MASTER_ABORT | + PCI_BRIDGE_CTL_BUS_RESET | + PCI_BRIDGE_CTL_FAST_BACK | + PCI_BRIDGE_CTL_DISCARD | + PCI_BRIDGE_CTL_SEC_DISCARD | + PCI_BRIDGE_CTL_DISCARD_SERR); + /* Below does not do anything as we never set this bit, put here for + * completeness. */ + pci_set_word(d->w1cmask + PCI_BRIDGE_CONTROL, + PCI_BRIDGE_CTL_DISCARD_STATUS); +} + +static int pci_init_multifunction(PCIBus *bus, PCIDevice *dev) +{ + uint8_t slot = PCI_SLOT(dev->devfn); + uint8_t func; + + if (dev->cap_present & QEMU_PCI_CAP_MULTIFUNCTION) { + dev->config[PCI_HEADER_TYPE] |= PCI_HEADER_TYPE_MULTI_FUNCTION; + } + + /* + * multifunction bit is interpreted in two ways as follows. + * - all functions must set the bit to 1. + * Example: Intel X53 + * - function 0 must set the bit, but the rest function (> 0) + * is allowed to leave the bit to 0. + * Example: PIIX3(also in qemu), PIIX4(also in qemu), ICH10, + * + * So OS (at least Linux) checks the bit of only function 0, + * and doesn't see the bit of function > 0. + * + * The below check allows both interpretation. + */ + if (PCI_FUNC(dev->devfn)) { + PCIDevice *f0 = bus->devices[PCI_DEVFN(slot, 0)]; + if (f0 && !(f0->cap_present & QEMU_PCI_CAP_MULTIFUNCTION)) { + /* function 0 should set multifunction bit */ + error_report("PCI: single function device can't be populated " + "in function %x.%x", slot, PCI_FUNC(dev->devfn)); + return -1; + } + return 0; + } + + if (dev->cap_present & QEMU_PCI_CAP_MULTIFUNCTION) { + return 0; + } + /* function 0 indicates single function, so function > 0 must be NULL */ + for (func = 1; func < PCI_FUNC_MAX; ++func) { + if (bus->devices[PCI_DEVFN(slot, func)]) { + error_report("PCI: %x.0 indicates single function, " + "but %x.%x is already populated.", + slot, slot, func); + return -1; + } + } + return 0; } static void pci_config_alloc(PCIDevice *pci_dev) @@ -612,7 +783,8 @@ pci_dev->config = qemu_mallocz(config_size); pci_dev->cmask = qemu_mallocz(config_size); pci_dev->wmask = qemu_mallocz(config_size); - pci_dev->used = qemu_mallocz(config_size); + pci_dev->w1cmask = qemu_mallocz(config_size); + pci_dev->config_map = qemu_mallocz(config_size); } static void pci_config_free(PCIDevice *pci_dev) @@ -620,7 +792,8 @@ qemu_free(pci_dev->config); qemu_free(pci_dev->cmask); qemu_free(pci_dev->wmask); - qemu_free(pci_dev->used); + qemu_free(pci_dev->w1cmask); + qemu_free(pci_dev->config_map); } /* -1 for devfn means auto assign */ @@ -628,20 +801,20 @@ const char *name, int devfn, PCIConfigReadFunc *config_read, PCIConfigWriteFunc *config_write, - uint8_t header_type) + bool is_bridge) { if (devfn < 0) { for(devfn = bus->devfn_min ; devfn < ARRAY_SIZE(bus->devices); - devfn += 8) { + devfn += PCI_FUNC_MAX) { if (!bus->devices[devfn]) goto found; } - qemu_error("PCI: no devfn available for %s, all in use\n", name); + error_report("PCI: no slot/function available for %s, all in use", name); return NULL; found: ; } else if (bus->devices[devfn]) { - qemu_error("PCI: devfn %d not available for %s, in use by %s\n", devfn, - name, bus->devices[devfn]->name); + error_report("PCI: slot %d function %d not available for %s, in use by %s", + PCI_SLOT(devfn), PCI_FUNC(devfn), name, bus->devices[devfn]->name); return NULL; } pci_dev->bus = bus; @@ -650,15 +823,21 @@ pci_dev->irq_state = 0; pci_config_alloc(pci_dev); - header_type &= ~PCI_HEADER_TYPE_MULTI_FUNCTION; - if (header_type == PCI_HEADER_TYPE_NORMAL) { + memset(pci_dev->config_map, 0xff, PCI_CONFIG_HEADER_SIZE); + + if (!is_bridge) { pci_set_default_subsystem_id(pci_dev); } pci_init_cmask(pci_dev); pci_init_wmask(pci_dev); - if (header_type == PCI_HEADER_TYPE_BRIDGE) { + pci_init_w1cmask(pci_dev); + if (is_bridge) { pci_init_wmask_bridge(pci_dev); } + if (pci_init_multifunction(bus, pci_dev)) { + pci_config_free(pci_dev); + return NULL; + } if (!config_read) config_read = pci_default_read_config; @@ -672,6 +851,13 @@ return pci_dev; } +static void do_pci_unregister_device(PCIDevice *pci_dev) +{ + qemu_free_irqs(pci_dev->irq); + pci_dev->bus->devices[pci_dev->devfn] = NULL; + pci_config_free(pci_dev); +} + PCIDevice *pci_register_device(PCIBus *bus, const char *name, int instance_size, int devfn, PCIConfigReadFunc *config_read, @@ -688,9 +874,11 @@ } return pci_dev; } -static target_phys_addr_t pci_to_cpu_addr(target_phys_addr_t addr) + +static target_phys_addr_t pci_to_cpu_addr(PCIBus *bus, + target_phys_addr_t addr) { - return addr + pci_mem_base; + return addr + bus->mem_base; } static void pci_unregister_io_regions(PCIDevice *pci_dev) @@ -705,9 +893,10 @@ if (r->type == PCI_BASE_ADDRESS_SPACE_IO) { isa_unassign_ioport(r->addr, r->filtered_size); } else { - cpu_register_physical_memory(pci_to_cpu_addr(r->addr), - r->filtered_size, - IO_MEM_UNASSIGNED); + cpu_register_physical_memory(pci_to_cpu_addr(pci_dev->bus, + r->addr), + r->filtered_size, + IO_MEM_UNASSIGNED); } } } @@ -724,24 +913,22 @@ return ret; pci_unregister_io_regions(pci_dev); - - qemu_free_irqs(pci_dev->irq); - pci_dev->bus->devices[pci_dev->devfn] = NULL; - pci_config_free(pci_dev); + pci_del_option_rom(pci_dev); + qemu_free(pci_dev->romfile); + do_pci_unregister_device(pci_dev); return 0; } void pci_register_bar(PCIDevice *pci_dev, int region_num, - pcibus_t size, int type, + pcibus_t size, uint8_t type, PCIMapIORegionFunc *map_func) { PCIIORegion *r; uint32_t addr; - pcibus_t wmask; - - if ((unsigned int)region_num >= PCI_NUM_REGIONS) - return; + uint64_t wmask; + assert(region_num >= 0); + assert(region_num < PCI_NUM_REGIONS); if (size & (size-1)) { fprintf(stderr, "ERROR: PCI region size must be pow2 " "type=0x%x, size=0x%"FMT_PCIBUS"\n", type, size); @@ -772,75 +959,6 @@ } } -static uint32_t pci_config_get_io_base(PCIDevice *d, - uint32_t base, uint32_t base_upper16) -{ - uint32_t val; - - val = ((uint32_t)d->config[base] & PCI_IO_RANGE_MASK) << 8; - if (d->config[base] & PCI_IO_RANGE_TYPE_32) { - val |= (uint32_t)pci_get_word(d->config + base_upper16) << 16; - } - return val; -} - -static pcibus_t pci_config_get_memory_base(PCIDevice *d, uint32_t base) -{ - return ((pcibus_t)pci_get_word(d->config + base) & PCI_MEMORY_RANGE_MASK) - << 16; -} - -static pcibus_t pci_config_get_pref_base(PCIDevice *d, - uint32_t base, uint32_t upper) -{ - pcibus_t tmp; - pcibus_t val; - - tmp = (pcibus_t)pci_get_word(d->config + base); - val = (tmp & PCI_PREF_RANGE_MASK) << 16; - if (tmp & PCI_PREF_RANGE_TYPE_64) { - val |= (pcibus_t)pci_get_long(d->config + upper) << 32; - } - return val; -} - -static pcibus_t pci_bridge_get_base(PCIDevice *bridge, uint8_t type) -{ - pcibus_t base; - if (type & PCI_BASE_ADDRESS_SPACE_IO) { - base = pci_config_get_io_base(bridge, - PCI_IO_BASE, PCI_IO_BASE_UPPER16); - } else { - if (type & PCI_BASE_ADDRESS_MEM_PREFETCH) { - base = pci_config_get_pref_base( - bridge, PCI_PREF_MEMORY_BASE, PCI_PREF_BASE_UPPER32); - } else { - base = pci_config_get_memory_base(bridge, PCI_MEMORY_BASE); - } - } - - return base; -} - -static pcibus_t pci_bridge_get_limit(PCIDevice *bridge, uint8_t type) -{ - pcibus_t limit; - if (type & PCI_BASE_ADDRESS_SPACE_IO) { - limit = pci_config_get_io_base(bridge, - PCI_IO_LIMIT, PCI_IO_LIMIT_UPPER16); - limit |= 0xfff; /* PCI bridge spec 3.2.5.6. */ - } else { - if (type & PCI_BASE_ADDRESS_MEM_PREFETCH) { - limit = pci_config_get_pref_base( - bridge, PCI_PREF_MEMORY_LIMIT, PCI_PREF_LIMIT_UPPER32); - } else { - limit = pci_config_get_memory_base(bridge, PCI_MEMORY_LIMIT); - } - limit |= 0xfffff; /* PCI bridge spec 3.2.5.{1, 8}. */ - } - return limit; -} - static void pci_bridge_filter(PCIDevice *d, pcibus_t *addr, pcibus_t *size, uint8_t type) { @@ -979,7 +1097,7 @@ isa_unassign_ioport(r->addr, r->filtered_size); } } else { - cpu_register_physical_memory(pci_to_cpu_addr(r->addr), + cpu_register_physical_memory(pci_to_cpu_addr(d->bus, r->addr), r->filtered_size, IO_MEM_UNASSIGNED); qemu_unregister_coalesced_mmio(r->addr, r->filtered_size); @@ -995,76 +1113,56 @@ * Teach them such cases, such that filtered_size < size and * addr & (size - 1) != 0. */ - r->map_func(d, i, r->addr, r->filtered_size, r->type); + if (r->type & PCI_BASE_ADDRESS_SPACE_IO) { + r->map_func(d, i, r->addr, r->filtered_size, r->type); + } else { + r->map_func(d, i, pci_to_cpu_addr(d->bus, r->addr), + r->filtered_size, r->type); + } } } } -static uint32_t pci_read_config(PCIDevice *d, - uint32_t address, int len) +static inline int pci_irq_disabled(PCIDevice *d) { - uint32_t val = 0; - - len = MIN(len, pci_config_size(d) - address); - memcpy(&val, d->config + address, len); - return le32_to_cpu(val); + return pci_get_word(d->config + PCI_COMMAND) & PCI_COMMAND_INTX_DISABLE; } -uint32_t pci_default_read_config(PCIDevice *d, - uint32_t address, int len) +/* Called after interrupt disabled field update in config space, + * assert/deassert interrupts if necessary. + * Gets original interrupt disable bit value (before update). */ +static void pci_update_irq_disabled(PCIDevice *d, int was_irq_disabled) { - assert(len == 1 || len == 2 || len == 4); - - if (pci_access_cap_config(d, address, len)) { - return d->cap.config_read(d, address, len); - } - - return pci_read_config(d, address, len); -} - -static void pci_write_config(PCIDevice *pci_dev, - uint32_t address, uint32_t val, int len) -{ - int i; - for (i = 0; i < len; i++) { - pci_dev->config[address + i] = val & 0xff; - val >>= 8; + int i, disabled = pci_irq_disabled(d); + if (disabled == was_irq_disabled) + return; + for (i = 0; i < PCI_NUM_PINS; ++i) { + int state = pci_irq_state(d, i); + pci_change_irq_level(d, i, disabled ? -state : state); } } -int pci_access_cap_config(PCIDevice *pci_dev, uint32_t address, int len) -{ - if (pci_dev->cap.supported && address >= pci_dev->cap.start && - (address + len) < pci_dev->cap.start + pci_dev->cap.length) - return 1; - return 0; -} - -uint32_t pci_default_cap_read_config(PCIDevice *pci_dev, - uint32_t address, int len) -{ - return pci_read_config(pci_dev, address, len); -} - -void pci_default_cap_write_config(PCIDevice *pci_dev, - uint32_t address, uint32_t val, int len) +uint32_t pci_default_read_config(PCIDevice *d, + uint32_t address, int len) { - pci_write_config(pci_dev, address, val, len); + uint32_t val = 0; + assert(len == 1 || len == 2 || len == 4); + len = MIN(len, pci_config_size(d) - address); + memcpy(&val, d->config + address, len); + return le32_to_cpu(val); } void pci_default_write_config(PCIDevice *d, uint32_t addr, uint32_t val, int l) { - int i; + int i, was_irq_disabled = pci_irq_disabled(d); uint32_t config_size = pci_config_size(d); - if (pci_access_cap_config(d, addr, l)) { - d->cap.config_write(d, addr, val, l); - return; - } - for (i = 0; i < l && addr + i < config_size; val >>= 8, ++i) { uint8_t wmask = d->wmask[addr + i]; + uint8_t w1cmask = d->w1cmask[addr + i]; + assert(!(wmask & w1cmask)); d->config[addr + i] = (d->config[addr + i] & ~wmask) | (val & wmask); + d->config[addr + i] &= ~(val & w1cmask); /* W1C: Write 1 to Clear */ } #ifdef CONFIG_KVM_DEVICE_ASSIGNMENT @@ -1079,6 +1177,9 @@ ranges_overlap(addr, l, PCI_ROM_ADDRESS1, 4) || range_covers_byte(addr, l, PCI_COMMAND)) pci_update_mappings(d); + + if (range_covers_byte(addr, l, PCI_COMMAND)) + pci_update_irq_disabled(d, was_irq_disabled); } /***********************************************************/ @@ -1100,6 +1201,8 @@ pci_set_irq_state(pci_dev, irq_num, level); pci_update_irq_status(pci_dev); + if (pci_irq_disabled(pci_dev)) + return; pci_change_irq_level(pci_dev, irq_num, change); } @@ -1114,161 +1217,360 @@ typedef struct { uint16_t class; const char *desc; + const char *fw_name; + uint16_t fw_ign_bits; } pci_class_desc; static const pci_class_desc pci_class_descriptions[] = { - { 0x0100, "SCSI controller"}, - { 0x0101, "IDE controller"}, - { 0x0102, "Floppy controller"}, - { 0x0103, "IPI controller"}, - { 0x0104, "RAID controller"}, + { 0x0001, "VGA controller", "display"}, + { 0x0100, "SCSI controller", "scsi"}, + { 0x0101, "IDE controller", "ide"}, + { 0x0102, "Floppy controller", "fdc"}, + { 0x0103, "IPI controller", "ipi"}, + { 0x0104, "RAID controller", "raid"}, { 0x0106, "SATA controller"}, { 0x0107, "SAS controller"}, { 0x0180, "Storage controller"}, - { 0x0200, "Ethernet controller"}, - { 0x0201, "Token Ring controller"}, - { 0x0202, "FDDI controller"}, - { 0x0203, "ATM controller"}, + { 0x0200, "Ethernet controller", "ethernet"}, + { 0x0201, "Token Ring controller", "token-ring"}, + { 0x0202, "FDDI controller", "fddi"}, + { 0x0203, "ATM controller", "atm"}, { 0x0280, "Network controller"}, - { 0x0300, "VGA controller"}, + { 0x0300, "VGA controller", "display", 0x00ff}, { 0x0301, "XGA controller"}, { 0x0302, "3D controller"}, { 0x0380, "Display controller"}, - { 0x0400, "Video controller"}, - { 0x0401, "Audio controller"}, + { 0x0400, "Video controller", "video"}, + { 0x0401, "Audio controller", "sound"}, { 0x0402, "Phone"}, { 0x0480, "Multimedia controller"}, - { 0x0500, "RAM controller"}, - { 0x0501, "Flash controller"}, + { 0x0500, "RAM controller", "memory"}, + { 0x0501, "Flash controller", "flash"}, { 0x0580, "Memory controller"}, - { 0x0600, "Host bridge"}, - { 0x0601, "ISA bridge"}, - { 0x0602, "EISA bridge"}, - { 0x0603, "MC bridge"}, - { 0x0604, "PCI bridge"}, - { 0x0605, "PCMCIA bridge"}, - { 0x0606, "NUBUS bridge"}, - { 0x0607, "CARDBUS bridge"}, + { 0x0600, "Host bridge", "host"}, + { 0x0601, "ISA bridge", "isa"}, + { 0x0602, "EISA bridge", "eisa"}, + { 0x0603, "MC bridge", "mca"}, + { 0x0604, "PCI bridge", "pci"}, + { 0x0605, "PCMCIA bridge", "pcmcia"}, + { 0x0606, "NUBUS bridge", "nubus"}, + { 0x0607, "CARDBUS bridge", "cardbus"}, { 0x0608, "RACEWAY bridge"}, { 0x0680, "Bridge"}, - { 0x0c03, "USB controller"}, + { 0x0700, "Serial port", "serial"}, + { 0x0701, "Parallel port", "parallel"}, + { 0x0800, "Interrupt controller", "interrupt-controller"}, + { 0x0801, "DMA controller", "dma-controller"}, + { 0x0802, "Timer", "timer"}, + { 0x0803, "RTC", "rtc"}, + { 0x0900, "Keyboard", "keyboard"}, + { 0x0901, "Pen", "pen"}, + { 0x0902, "Mouse", "mouse"}, + { 0x0A00, "Dock station", "dock", 0x00ff}, + { 0x0B00, "i386 cpu", "cpu", 0x00ff}, + { 0x0c00, "Fireware contorller", "fireware"}, + { 0x0c01, "Access bus controller", "access-bus"}, + { 0x0c02, "SSA controller", "ssa"}, + { 0x0c03, "USB controller", "usb"}, + { 0x0c04, "Fibre channel controller", "fibre-channel"}, { 0, NULL} }; -static void pci_info_device(PCIBus *bus, PCIDevice *d) +static void pci_for_each_device_under_bus(PCIBus *bus, + void (*fn)(PCIBus *b, PCIDevice *d)) { - Monitor *mon = cur_mon; - int i, class; - PCIIORegion *r; - const pci_class_desc *desc; + PCIDevice *d; + int devfn; - monitor_printf(mon, " Bus %2d, device %3d, function %d:\n", - pci_bus_num(d->bus), - PCI_SLOT(d->devfn), PCI_FUNC(d->devfn)); - class = pci_get_word(d->config + PCI_CLASS_DEVICE); + for(devfn = 0; devfn < ARRAY_SIZE(bus->devices); devfn++) { + d = bus->devices[devfn]; + if (d) { + fn(bus, d); + } + } +} + +void pci_for_each_device(PCIBus *bus, int bus_num, + void (*fn)(PCIBus *b, PCIDevice *d)) +{ + bus = pci_find_bus(bus, bus_num); + + if (bus) { + pci_for_each_device_under_bus(bus, fn); + } +} + +static void pci_device_print(Monitor *mon, QDict *device) +{ + QDict *qdict; + QListEntry *entry; + uint64_t addr, size; + + monitor_printf(mon, " Bus %2" PRId64 ", ", qdict_get_int(device, "bus")); + monitor_printf(mon, "device %3" PRId64 ", function %" PRId64 ":\n", + qdict_get_int(device, "slot"), + qdict_get_int(device, "function")); monitor_printf(mon, " "); - desc = pci_class_descriptions; - while (desc->desc && class != desc->class) - desc++; - if (desc->desc) { - monitor_printf(mon, "%s", desc->desc); + + qdict = qdict_get_qdict(device, "class_info"); + if (qdict_haskey(qdict, "desc")) { + monitor_printf(mon, "%s", qdict_get_str(qdict, "desc")); } else { - monitor_printf(mon, "Class %04x", class); + monitor_printf(mon, "Class %04" PRId64, qdict_get_int(qdict, "class")); + } + + qdict = qdict_get_qdict(device, "id"); + monitor_printf(mon, ": PCI device %04" PRIx64 ":%04" PRIx64 "\n", + qdict_get_int(qdict, "device"), + qdict_get_int(qdict, "vendor")); + + if (qdict_haskey(device, "irq")) { + monitor_printf(mon, " IRQ %" PRId64 ".\n", + qdict_get_int(device, "irq")); } - monitor_printf(mon, ": PCI device %04x:%04x\n", - pci_get_word(d->config + PCI_VENDOR_ID), - pci_get_word(d->config + PCI_DEVICE_ID)); - - if (d->config[PCI_INTERRUPT_PIN] != 0) { - monitor_printf(mon, " IRQ %d.\n", - d->config[PCI_INTERRUPT_LINE]); - } - if (class == 0x0604) { - uint64_t base; - uint64_t limit; - - monitor_printf(mon, " BUS %d.\n", d->config[0x19]); - monitor_printf(mon, " secondary bus %d.\n", - d->config[PCI_SECONDARY_BUS]); - monitor_printf(mon, " subordinate bus %d.\n", - d->config[PCI_SUBORDINATE_BUS]); - base = pci_bridge_get_base(d, PCI_BASE_ADDRESS_SPACE_IO); - limit = pci_bridge_get_limit(d, PCI_BASE_ADDRESS_SPACE_IO); + if (qdict_haskey(device, "pci_bridge")) { + QDict *info; + + qdict = qdict_get_qdict(device, "pci_bridge"); + + info = qdict_get_qdict(qdict, "bus"); + monitor_printf(mon, " BUS %" PRId64 ".\n", + qdict_get_int(info, "number")); + monitor_printf(mon, " secondary bus %" PRId64 ".\n", + qdict_get_int(info, "secondary")); + monitor_printf(mon, " subordinate bus %" PRId64 ".\n", + qdict_get_int(info, "subordinate")); + + info = qdict_get_qdict(qdict, "io_range"); monitor_printf(mon, " IO range [0x%04"PRIx64", 0x%04"PRIx64"]\n", - base, limit); + qdict_get_int(info, "base"), + qdict_get_int(info, "limit")); - base = pci_bridge_get_base(d, PCI_BASE_ADDRESS_SPACE_MEMORY); - limit= pci_bridge_get_limit(d, PCI_BASE_ADDRESS_SPACE_MEMORY); + info = qdict_get_qdict(qdict, "memory_range"); monitor_printf(mon, " memory range [0x%08"PRIx64", 0x%08"PRIx64"]\n", - base, limit); + qdict_get_int(info, "base"), + qdict_get_int(info, "limit")); - base = pci_bridge_get_base(d, PCI_BASE_ADDRESS_SPACE_MEMORY | - PCI_BASE_ADDRESS_MEM_PREFETCH); - limit = pci_bridge_get_limit(d, PCI_BASE_ADDRESS_SPACE_MEMORY | - PCI_BASE_ADDRESS_MEM_PREFETCH); + info = qdict_get_qdict(qdict, "prefetchable_range"); monitor_printf(mon, " prefetchable memory range " - "[0x%08"PRIx64", 0x%08"PRIx64"]\n", base, limit); + "[0x%08"PRIx64", 0x%08"PRIx64"]\n", + qdict_get_int(info, "base"), + qdict_get_int(info, "limit")); + } + + QLIST_FOREACH_ENTRY(qdict_get_qlist(device, "regions"), entry) { + qdict = qobject_to_qdict(qlist_entry_obj(entry)); + monitor_printf(mon, " BAR%d: ", (int) qdict_get_int(qdict, "bar")); + + addr = qdict_get_int(qdict, "address"); + size = qdict_get_int(qdict, "size"); + + if (!strcmp(qdict_get_str(qdict, "type"), "io")) { + monitor_printf(mon, "I/O at 0x%04"FMT_PCIBUS + " [0x%04"FMT_PCIBUS"].\n", + addr, addr + size - 1); + } else { + monitor_printf(mon, "%d bit%s memory at 0x%08"FMT_PCIBUS + " [0x%08"FMT_PCIBUS"].\n", + qdict_get_bool(qdict, "mem_type_64") ? 64 : 32, + qdict_get_bool(qdict, "prefetch") ? + " prefetchable" : "", addr, addr + size - 1); + } } - for(i = 0;i < PCI_NUM_REGIONS; i++) { - r = &d->io_regions[i]; - if (r->size != 0) { - monitor_printf(mon, " BAR%d: ", i); - if (r->type & PCI_BASE_ADDRESS_SPACE_IO) { - monitor_printf(mon, "I/O at 0x%04"FMT_PCIBUS - " [0x%04"FMT_PCIBUS"].\n", - r->addr, r->addr + r->size - 1); - } else { - const char *type = r->type & PCI_BASE_ADDRESS_MEM_TYPE_64 ? - "64 bit" : "32 bit"; - const char *prefetch = - r->type & PCI_BASE_ADDRESS_MEM_PREFETCH ? - " prefetchable" : ""; - monitor_printf(mon, "%s%s memory at 0x%08"FMT_PCIBUS - " [0x%08"FMT_PCIBUS"].\n", - type, prefetch, - r->addr, r->addr + r->size - 1); + monitor_printf(mon, " id \"%s\"\n", qdict_get_str(device, "qdev_id")); + + if (qdict_haskey(device, "pci_bridge")) { + qdict = qdict_get_qdict(device, "pci_bridge"); + if (qdict_haskey(qdict, "devices")) { + QListEntry *dev; + QLIST_FOREACH_ENTRY(qdict_get_qlist(qdict, "devices"), dev) { + pci_device_print(mon, qobject_to_qdict(qlist_entry_obj(dev))); } } } - monitor_printf(mon, " id \"%s\"\n", d->qdev.id ? d->qdev.id : ""); - if (class == 0x0604 && d->config[0x19] != 0) { - pci_for_each_device(bus, d->config[0x19], pci_info_device); +} + +void do_pci_info_print(Monitor *mon, const QObject *data) +{ + QListEntry *bus, *dev; + + QLIST_FOREACH_ENTRY(qobject_to_qlist(data), bus) { + QDict *qdict = qobject_to_qdict(qlist_entry_obj(bus)); + QLIST_FOREACH_ENTRY(qdict_get_qlist(qdict, "devices"), dev) { + pci_device_print(mon, qobject_to_qdict(qlist_entry_obj(dev))); + } } } -static void pci_for_each_device_under_bus(PCIBus *bus, - void (*fn)(PCIBus *b, PCIDevice *d)) +static QObject *pci_get_dev_class(const PCIDevice *dev) +{ + int class; + const pci_class_desc *desc; + + class = pci_get_word(dev->config + PCI_CLASS_DEVICE); + desc = pci_class_descriptions; + while (desc->desc && class != desc->class) + desc++; + + if (desc->desc) { + return qobject_from_jsonf("{ 'desc': %s, 'class': %d }", + desc->desc, class); + } else { + return qobject_from_jsonf("{ 'class': %d }", class); + } +} + +static QObject *pci_get_dev_id(const PCIDevice *dev) +{ + return qobject_from_jsonf("{ 'device': %d, 'vendor': %d }", + pci_get_word(dev->config + PCI_VENDOR_ID), + pci_get_word(dev->config + PCI_DEVICE_ID)); +} + +static QObject *pci_get_regions_list(const PCIDevice *dev) +{ + int i; + QList *regions_list; + + regions_list = qlist_new(); + + for (i = 0; i < PCI_NUM_REGIONS; i++) { + QObject *obj; + const PCIIORegion *r = &dev->io_regions[i]; + + if (!r->size) { + continue; + } + + if (r->type & PCI_BASE_ADDRESS_SPACE_IO) { + obj = qobject_from_jsonf("{ 'bar': %d, 'type': 'io', " + "'address': %" PRId64 ", " + "'size': %" PRId64 " }", + i, r->addr, r->size); + } else { + int mem_type_64 = r->type & PCI_BASE_ADDRESS_MEM_TYPE_64; + + obj = qobject_from_jsonf("{ 'bar': %d, 'type': 'memory', " + "'mem_type_64': %i, 'prefetch': %i, " + "'address': %" PRId64 ", " + "'size': %" PRId64 " }", + i, mem_type_64, + r->type & PCI_BASE_ADDRESS_MEM_PREFETCH, + r->addr, r->size); + } + + qlist_append_obj(regions_list, obj); + } + + return QOBJECT(regions_list); +} + +static QObject *pci_get_devices_list(PCIBus *bus, int bus_num); + +static QObject *pci_get_dev_dict(PCIDevice *dev, PCIBus *bus, int bus_num) +{ + uint8_t type; + QObject *obj; + + obj = qobject_from_jsonf("{ 'bus': %d, 'slot': %d, 'function': %d," "'class_info': %p, 'id': %p, 'regions': %p," + " 'qdev_id': %s }", + bus_num, + PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), + pci_get_dev_class(dev), pci_get_dev_id(dev), + pci_get_regions_list(dev), + dev->qdev.id ? dev->qdev.id : ""); + + if (dev->config[PCI_INTERRUPT_PIN] != 0) { + QDict *qdict = qobject_to_qdict(obj); + qdict_put(qdict, "irq", qint_from_int(dev->config[PCI_INTERRUPT_LINE])); + } + + type = dev->config[PCI_HEADER_TYPE] & ~PCI_HEADER_TYPE_MULTI_FUNCTION; + if (type == PCI_HEADER_TYPE_BRIDGE) { + QDict *qdict; + QObject *pci_bridge; + + pci_bridge = qobject_from_jsonf("{ 'bus': " + "{ 'number': %d, 'secondary': %d, 'subordinate': %d }, " + "'io_range': { 'base': %" PRId64 ", 'limit': %" PRId64 "}, " + "'memory_range': { 'base': %" PRId64 ", 'limit': %" PRId64 "}, " + "'prefetchable_range': { 'base': %" PRId64 ", 'limit': %" PRId64 "} }", + dev->config[PCI_PRIMARY_BUS], dev->config[PCI_SECONDARY_BUS], + dev->config[PCI_SUBORDINATE_BUS], + pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_IO), + pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_IO), + pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_MEMORY), + pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_MEMORY), + pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_MEMORY | + PCI_BASE_ADDRESS_MEM_PREFETCH), + pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_MEMORY | + PCI_BASE_ADDRESS_MEM_PREFETCH)); + + if (dev->config[PCI_SECONDARY_BUS] != 0) { + PCIBus *child_bus = pci_find_bus(bus, dev->config[PCI_SECONDARY_BUS]); + + if (child_bus) { + qdict = qobject_to_qdict(pci_bridge); + qdict_put_obj(qdict, "devices", + pci_get_devices_list(child_bus, + dev->config[PCI_SECONDARY_BUS])); + } + } + qdict = qobject_to_qdict(obj); + qdict_put_obj(qdict, "pci_bridge", pci_bridge); + } + + return obj; +} + +static QObject *pci_get_devices_list(PCIBus *bus, int bus_num) { - PCIDevice *d; int devfn; + PCIDevice *dev; + QList *dev_list; - for(devfn = 0; devfn < ARRAY_SIZE(bus->devices); devfn++) { - d = bus->devices[devfn]; - if (d) - fn(bus, d); + dev_list = qlist_new(); + + for (devfn = 0; devfn < ARRAY_SIZE(bus->devices); devfn++) { + dev = bus->devices[devfn]; + if (dev) { + qlist_append_obj(dev_list, pci_get_dev_dict(dev, bus, bus_num)); + } } + + return QOBJECT(dev_list); } -void pci_for_each_device(PCIBus *bus, int bus_num, - void (*fn)(PCIBus *b, PCIDevice *d)) +static QObject *pci_get_bus_dict(PCIBus *bus, int bus_num) { bus = pci_find_bus(bus, bus_num); - if (bus) { - pci_for_each_device_under_bus(bus, fn); + return qobject_from_jsonf("{ 'bus': %d, 'devices': %p }", + bus_num, pci_get_devices_list(bus, bus_num)); } + + return NULL; } -void pci_info(Monitor *mon) +void do_pci_info(Monitor *mon, QObject **ret_data) { + QList *bus_list; struct PCIHostBus *host; + + bus_list = qlist_new(); + QLIST_FOREACH(host, &host_buses, next) { - pci_for_each_device(host->bus, 0, pci_info_device); + QObject *obj = pci_get_bus_dict(host->bus, 0); + if (obj) { + qlist_append_obj(bus_list, obj); + } } + + *ret_data = QOBJECT(bus_list); } static const char * const pci_nic_models[] = { @@ -1313,15 +1615,13 @@ bus = pci_get_bus_devfn(&devfn, devaddr); if (!bus) { - qemu_error("Invalid PCI device address %s for device %s\n", - devaddr, pci_nic_names[i]); + error_report("Invalid PCI device address %s for device %s", + devaddr, pci_nic_names[i]); return NULL; } pci_dev = pci_create(bus, devfn, pci_nic_names[i]); dev = &pci_dev->qdev; - if (nd->name) - dev->id = qemu_strdup(nd->name); qdev_set_nic_properties(dev, nd); if (qdev_init(dev) < 0) return NULL; @@ -1342,20 +1642,12 @@ return res; } -typedef struct { - PCIDevice dev; - PCIBus bus; - uint32_t vid; - uint32_t did; -} PCIBridge; - - static void pci_bridge_update_mappings_fn(PCIBus *b, PCIDevice *d) { pci_update_mappings(d); } -static void pci_bridge_update_mappings(PCIBus *b) +void pci_bridge_update_mappings(PCIBus *b) { PCIBus *child; @@ -1366,39 +1658,44 @@ } } -static void pci_bridge_write_config(PCIDevice *d, - uint32_t address, uint32_t val, int len) -{ - pci_default_write_config(d, address, val, len); - - if (/* io base/limit */ - ranges_overlap(address, len, PCI_IO_BASE, 2) || - - /* memory base/limit, prefetchable base/limit and - io base/limit upper 16 */ - ranges_overlap(address, len, PCI_MEMORY_BASE, 20)) { - pci_bridge_update_mappings(d->bus); - } +/* Whether a given bus number is in range of the secondary + * bus of the given bridge device. */ +static bool pci_secondary_bus_in_range(PCIDevice *dev, int bus_num) +{ + return !(pci_get_word(dev->config + PCI_BRIDGE_CONTROL) & + PCI_BRIDGE_CTL_BUS_RESET) /* Don't walk the bus if it's reset. */ && + dev->config[PCI_SECONDARY_BUS] < bus_num && + bus_num <= dev->config[PCI_SUBORDINATE_BUS]; } PCIBus *pci_find_bus(PCIBus *bus, int bus_num) { PCIBus *sec; - if (!bus) + if (!bus) { return NULL; + } if (pci_bus_num(bus) == bus_num) { return bus; } - /* try child bus */ - QLIST_FOREACH(sec, &bus->child, sibling) { + /* Consider all bus numbers in range for the host pci bridge. */ + if (bus->parent_dev && + !pci_secondary_bus_in_range(bus->parent_dev, bus_num)) { + return NULL; + } - if (!bus->parent_dev /* pci host bridge */ - || (pci_bus_num(sec) <= bus_num && - bus->parent_dev->config[PCI_SUBORDINATE_BUS])) { - return pci_find_bus(sec, bus_num); + /* try child bus */ + for (; bus; bus = sec) { + QLIST_FOREACH(sec, &bus->child, sibling) { + assert(sec->parent_dev); + if (sec->parent_dev->config[PCI_SECONDARY_BUS] == bus_num) { + return sec; + } + if (pci_secondary_bus_in_range(sec->parent_dev, bus_num)) { + break; + } } } @@ -1415,57 +1712,13 @@ return bus->devices[PCI_DEVFN(slot, function)]; } -static int pci_bridge_initfn(PCIDevice *dev) -{ - PCIBridge *s = DO_UPCAST(PCIBridge, dev, dev); - - pci_config_set_vendor_id(s->dev.config, s->vid); - pci_config_set_device_id(s->dev.config, s->did); - - pci_set_word(dev->config + PCI_STATUS, - PCI_STATUS_66MHZ | PCI_STATUS_FAST_BACK); - pci_config_set_class(dev->config, PCI_CLASS_BRIDGE_PCI); - dev->config[PCI_HEADER_TYPE] = PCI_HEADER_TYPE_BRIDGE; - pci_set_word(dev->config + PCI_SEC_STATUS, - PCI_STATUS_66MHZ | PCI_STATUS_FAST_BACK); - return 0; -} - -static int pci_bridge_exitfn(PCIDevice *pci_dev) -{ - PCIBridge *s = DO_UPCAST(PCIBridge, dev, pci_dev); - PCIBus *bus = &s->bus; - pci_unregister_secondary_bus(bus); - return 0; -} - -PCIBus *pci_bridge_init(PCIBus *bus, int devfn, uint16_t vid, uint16_t did, - pci_map_irq_fn map_irq, const char *name) -{ - PCIDevice *dev; - PCIBridge *s; - - dev = pci_create(bus, devfn, "pci-bridge"); - qdev_prop_set_uint32(&dev->qdev, "vendorid", vid); - qdev_prop_set_uint32(&dev->qdev, "deviceid", did); - qdev_init_nofail(&dev->qdev); - - s = DO_UPCAST(PCIBridge, dev, dev); - pci_register_secondary_bus(bus, &s->bus, &s->dev, map_irq, name); - return &s->bus; -} - -PCIDevice *pci_bridge_get_device(PCIBus *bus) -{ - return bus->parent_dev; -} - static int pci_qdev_init(DeviceState *qdev, DeviceInfo *base) { PCIDevice *pci_dev = (PCIDevice *)qdev; PCIDeviceInfo *info = container_of(base, PCIDeviceInfo, qdev); PCIBus *bus; int devfn, rc; + bool is_default_rom; /* initialize cap_present for pci_is_express() and pci_config_size() */ if (info->is_express) { @@ -1476,29 +1729,54 @@ devfn = pci_dev->devfn; pci_dev = do_pci_register_device(pci_dev, bus, base->name, devfn, info->config_read, info->config_write, - info->header_type); + info->is_bridge); if (pci_dev == NULL) return -1; + if (qdev->hotplugged && info->no_hotplug) { + qerror_report(QERR_DEVICE_NO_HOTPLUG, info->qdev.name); + do_pci_unregister_device(pci_dev); + return -1; + } rc = info->init(pci_dev); - if (rc != 0) + if (rc != 0) { + do_pci_unregister_device(pci_dev); return rc; + } /* rom loading */ - if (pci_dev->romfile == NULL && info->romfile != NULL) + is_default_rom = false; + if (pci_dev->romfile == NULL && info->romfile != NULL) { pci_dev->romfile = qemu_strdup(info->romfile); - pci_add_option_rom(pci_dev); + is_default_rom = true; + } + pci_add_option_rom(pci_dev, is_default_rom); - if (qdev->hotplugged) - bus->hotplug(pci_dev, 1); + if (bus->hotplug) { + /* Let buses differentiate between hotplug and when device is + * enabled during qemu machine creation. */ + rc = bus->hotplug(bus->hotplug_qdev, pci_dev, + qdev->hotplugged ? PCI_HOTPLUG_ENABLED: + PCI_COLDPLUG_ENABLED); + if (rc != 0) { + int r = pci_unregister_device(&pci_dev->qdev); + assert(!r); + return rc; + } + } return 0; } static int pci_unplug_device(DeviceState *qdev) { PCIDevice *dev = DO_UPCAST(PCIDevice, qdev, qdev); + PCIDeviceInfo *info = container_of(qdev->info, PCIDeviceInfo, qdev); - dev->bus->hotplug(dev, 0); - return 0; + if (info->no_hotplug) { + qerror_report(QERR_DEVICE_NO_HOTPLUG, info->qdev.name); + return -1; + } + return dev->bus->hotplug(dev->bus->hotplug_qdev, dev, + PCI_HOTPLUG_DISABLED); } void pci_qdev_register(PCIDeviceInfo *info) @@ -1518,51 +1796,34 @@ } } -PCIDevice *pci_create(PCIBus *bus, int devfn, const char *name) +PCIDevice *pci_create_multifunction(PCIBus *bus, int devfn, bool multifunction, + const char *name) { DeviceState *dev; dev = qdev_create(&bus->qbus, name); qdev_prop_set_uint32(dev, "addr", devfn); + qdev_prop_set_bit(dev, "multifunction", multifunction); return DO_UPCAST(PCIDevice, qdev, dev); } -PCIDevice *pci_create_simple(PCIBus *bus, int devfn, const char *name) +PCIDevice *pci_create_simple_multifunction(PCIBus *bus, int devfn, + bool multifunction, + const char *name) { - PCIDevice *dev = pci_create(bus, devfn, name); + PCIDevice *dev = pci_create_multifunction(bus, devfn, multifunction, name); qdev_init_nofail(&dev->qdev); return dev; } -int pci_enable_capability_support(PCIDevice *pci_dev, - uint32_t config_start, - PCICapConfigReadFunc *config_read, - PCICapConfigWriteFunc *config_write, - PCICapConfigInitFunc *config_init) +PCIDevice *pci_create(PCIBus *bus, int devfn, const char *name) { - if (!pci_dev) - return -ENODEV; - - pci_dev->config[0x06] |= 0x10; // status = capabilities - - if (config_start == 0) - pci_dev->cap.start = PCI_CAPABILITY_CONFIG_DEFAULT_START_ADDR; - else if (config_start >= 0x40 && config_start < 0xff) - pci_dev->cap.start = config_start; - else - return -EINVAL; + return pci_create_multifunction(bus, devfn, false, name); +} - if (config_read) - pci_dev->cap.config_read = config_read; - else - pci_dev->cap.config_read = pci_default_cap_read_config; - if (config_write) - pci_dev->cap.config_write = config_write; - else - pci_dev->cap.config_write = pci_default_cap_write_config; - pci_dev->cap.supported = 1; - pci_dev->config[PCI_CAPABILITY_LIST] = pci_dev->cap.start; - return config_init(pci_dev); +PCIDevice *pci_create_simple(PCIBus *bus, int devfn, const char *name) +{ + return pci_create_simple_multifunction(bus, devfn, false, name); } static int pci_find_space(PCIDevice *pdev, uint8_t size) @@ -1571,7 +1832,7 @@ int offset = PCI_CONFIG_HEADER_SIZE; int i; for (i = PCI_CONFIG_HEADER_SIZE; i < config_size; ++i) - if (pdev->used[i]) + if (pdev->config_map[i]) offset = i + 1; else if (i - offset + 1 == size) return offset; @@ -1596,17 +1857,74 @@ return next; } -static void pci_map_option_rom(PCIDevice *pdev, int region_num, pcibus_t addr, pcibus_t size, int type) +void pci_map_option_rom(PCIDevice *pdev, int region_num, pcibus_t addr, pcibus_t size, int type) { cpu_register_physical_memory(addr, size, pdev->rom_offset); } +/* Patch the PCI vendor and device ids in a PCI rom image if necessary. + This is needed for an option rom which is used for more than one device. */ +static void pci_patch_ids(PCIDevice *pdev, uint8_t *ptr, int size) +{ + uint16_t vendor_id; + uint16_t device_id; + uint16_t rom_vendor_id; + uint16_t rom_device_id; + uint16_t rom_magic; + uint16_t pcir_offset; + uint8_t checksum; + + /* Words in rom data are little endian (like in PCI configuration), + so they can be read / written with pci_get_word / pci_set_word. */ + + /* Only a valid rom will be patched. */ + rom_magic = pci_get_word(ptr); + if (rom_magic != 0xaa55) { + PCI_DPRINTF("Bad ROM magic %04x\n", rom_magic); + return; + } + pcir_offset = pci_get_word(ptr + 0x18); + if (pcir_offset + 8 >= size || memcmp(ptr + pcir_offset, "PCIR", 4)) { + PCI_DPRINTF("Bad PCIR offset 0x%x or signature\n", pcir_offset); + return; + } + + vendor_id = pci_get_word(pdev->config + PCI_VENDOR_ID); + device_id = pci_get_word(pdev->config + PCI_DEVICE_ID); + rom_vendor_id = pci_get_word(ptr + pcir_offset + 4); + rom_device_id = pci_get_word(ptr + pcir_offset + 6); + + PCI_DPRINTF("%s: ROM id %04x%04x / PCI id %04x%04x\n", pdev->romfile, + vendor_id, device_id, rom_vendor_id, rom_device_id); + + checksum = ptr[6]; + + if (vendor_id != rom_vendor_id) { + /* Patch vendor id and checksum (at offset 6 for etherboot roms). */ + checksum += (uint8_t)rom_vendor_id + (uint8_t)(rom_vendor_id >> 8); + checksum -= (uint8_t)vendor_id + (uint8_t)(vendor_id >> 8); + PCI_DPRINTF("ROM checksum %02x / %02x\n", ptr[6], checksum); + ptr[6] = checksum; + pci_set_word(ptr + pcir_offset + 4, vendor_id); + } + + if (device_id != rom_device_id) { + /* Patch device id and checksum (at offset 6 for etherboot roms). */ + checksum += (uint8_t)rom_device_id + (uint8_t)(rom_device_id >> 8); + checksum -= (uint8_t)device_id + (uint8_t)(device_id >> 8); + PCI_DPRINTF("ROM checksum %02x / %02x\n", ptr[6], checksum); + ptr[6] = checksum; + pci_set_word(ptr + pcir_offset + 6, device_id); + } +} + /* Add an option rom for the device */ -static int pci_add_option_rom(PCIDevice *pdev) +static int pci_add_option_rom(PCIDevice *pdev, bool is_default_rom) { int size; char *path; void *ptr; + char name[32]; if (!pdev->romfile) return 0; @@ -1622,7 +1940,7 @@ if (class == 0x0300) { rom_add_vga(pdev->romfile); } else { - rom_add_option(pdev->romfile); + rom_add_option(pdev->romfile, -1); } return 0; } @@ -1634,42 +1952,88 @@ size = get_image_size(path); if (size < 0) { - qemu_error("%s: failed to find romfile \"%s\"\n", __FUNCTION__, - pdev->romfile); + error_report("%s: failed to find romfile \"%s\"", + __FUNCTION__, pdev->romfile); return -1; } if (size & (size - 1)) { size = 1 << qemu_fls(size); } - pdev->rom_offset = qemu_ram_alloc(size); + if (pdev->qdev.info->vmsd) + snprintf(name, sizeof(name), "%s.rom", pdev->qdev.info->vmsd->name); + else + snprintf(name, sizeof(name), "%s.rom", pdev->qdev.info->name); + pdev->rom_offset = qemu_ram_alloc(&pdev->qdev, name, size); ptr = qemu_get_ram_ptr(pdev->rom_offset); load_image(path, ptr); qemu_free(path); + if (is_default_rom) { + /* Only the default rom images will be patched (if needed). */ + pci_patch_ids(pdev, ptr, size); + } + pci_register_bar(pdev, PCI_ROM_SLOT, size, 0, pci_map_option_rom); return 0; } -/* Reserve space and add capability to the linked list in pci config space */ -int pci_add_capability(PCIDevice *pdev, uint8_t cap_id, uint8_t size) +static void pci_del_option_rom(PCIDevice *pdev) { - uint8_t offset = pci_find_space(pdev, size); - uint8_t *config = pdev->config + offset; - if (!offset) - return -ENOSPC; + if (!pdev->rom_offset) + return; + + qemu_ram_free(pdev->rom_offset); + pdev->rom_offset = 0; +} + +/* + * if !offset + * Reserve space and add capability to the linked list in pci config space + * + * if offset = 0, + * Find and reserve space and add capability to the linked list + * in pci config space */ +int pci_add_capability(PCIDevice *pdev, uint8_t cap_id, + uint8_t offset, uint8_t size) +{ + uint8_t *config; + if (!offset) { + offset = pci_find_space(pdev, size); + if (!offset) { + return -ENOSPC; + } + } else { + int i; + + for (i = offset; i < offset + size; i++) { + if (pdev->config_map[i]) { + fprintf(stderr, "ERROR: %04x:%02x:%02x.%x " + "Attempt to add PCI capability %x at offset " + "%x overlaps existing capability %x at offset %x\n", + pci_find_domain(pdev->bus), pci_bus_num(pdev->bus), + PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), + cap_id, offset, pdev->config_map[i], i); + return -EINVAL; + } + } + } + + config = pdev->config + offset; config[PCI_CAP_LIST_ID] = cap_id; config[PCI_CAP_LIST_NEXT] = pdev->config[PCI_CAPABILITY_LIST]; pdev->config[PCI_CAPABILITY_LIST] = offset; - pdev->config[PCI_STATUS] |= PCI_STATUS_CAP_LIST; - memset(pdev->used + offset, 0xFF, size); + memset(pdev->config_map + offset, cap_id, size); /* Make capability read-only by default */ memset(pdev->wmask + offset, 0, size); /* Check capability by default */ memset(pdev->cmask + offset, 0xFF, size); + + pdev->config[PCI_STATUS] |= PCI_STATUS_CAP_LIST; + return offset; } @@ -1682,18 +2046,14 @@ pdev->config[prev] = pdev->config[offset + PCI_CAP_LIST_NEXT]; /* Make capability writeable again */ memset(pdev->wmask + offset, 0xff, size); + memset(pdev->w1cmask + offset, 0, size); /* Clear cmask as device-specific registers can't be checked */ memset(pdev->cmask + offset, 0, size); - memset(pdev->used + offset, 0, size); + memset(pdev->config_map + offset, 0, size); - if (!pdev->config[PCI_CAPABILITY_LIST]) + if (!pdev->config[PCI_CAPABILITY_LIST]) { pdev->config[PCI_STATUS] &= ~PCI_STATUS_CAP_LIST; -} - -/* Reserve space for capability at a known offset (to call after load). */ -void pci_reserve_capability(PCIDevice *pdev, uint8_t offset, uint8_t size) -{ - memset(pdev->used + offset, 0xff, size); + } } uint8_t pci_find_capability(PCIDevice *pdev, uint8_t cap_id) @@ -1721,8 +2081,7 @@ monitor_printf(mon, "%*sclass %s, addr %02x:%02x.%x, " "pci id %04x:%04x (sub %04x:%04x)\n", - indent, "", ctxt, - d->config[PCI_SECONDARY_BUS], + indent, "", ctxt, pci_bus_num(d->bus), PCI_SLOT(d->devfn), PCI_FUNC(d->devfn), pci_get_word(d->config + PCI_VENDOR_ID), pci_get_word(d->config + PCI_DEVICE_ID), @@ -1740,22 +2099,129 @@ } } -static PCIDeviceInfo bridge_info = { - .qdev.name = "pci-bridge", - .qdev.size = sizeof(PCIBridge), - .init = pci_bridge_initfn, - .exit = pci_bridge_exitfn, - .config_write = pci_bridge_write_config, - .qdev.props = (Property[]) { - DEFINE_PROP_HEX32("vendorid", PCIBridge, vid, 0), - DEFINE_PROP_HEX32("deviceid", PCIBridge, did, 0), - DEFINE_PROP_END_OF_LIST(), +static char *pci_dev_fw_name(DeviceState *dev, char *buf, int len) +{ + PCIDevice *d = (PCIDevice *)dev; + const char *name = NULL; + const pci_class_desc *desc = pci_class_descriptions; + int class = pci_get_word(d->config + PCI_CLASS_DEVICE); + + while (desc->desc && + (class & ~desc->fw_ign_bits) != + (desc->class & ~desc->fw_ign_bits)) { + desc++; + } + + if (desc->desc) { + name = desc->fw_name; } -}; -static void pci_register_devices(void) + if (name) { + pstrcpy(buf, len, name); + } else { + snprintf(buf, len, "pci%04x,%04x", + pci_get_word(d->config + PCI_VENDOR_ID), + pci_get_word(d->config + PCI_DEVICE_ID)); + } + + return buf; +} + +static char *pcibus_get_fw_dev_path(DeviceState *dev) { - pci_qdev_register(&bridge_info); + PCIDevice *d = (PCIDevice *)dev; + char path[50], name[33]; + int off; + + off = snprintf(path, sizeof(path), "%s@%x", + pci_dev_fw_name(dev, name, sizeof name), + PCI_SLOT(d->devfn)); + if (PCI_FUNC(d->devfn)) + snprintf(path + off, sizeof(path) + off, ",%x", PCI_FUNC(d->devfn)); + return strdup(path); +} + +static char *pcibus_get_dev_path(DeviceState *dev) +{ + PCIDevice *d = container_of(dev, PCIDevice, qdev); + PCIDevice *t; + int slot_depth; + /* Path format: Domain:00:Slot.Function:Slot.Function....:Slot.Function. + * 00 is added here to make this format compatible with + * domain:Bus:Slot.Func for systems without nested PCI bridges. + * Slot.Function list specifies the slot and function numbers for all + * devices on the path from root to the specific device. */ + char domain[] = "DDDD:00"; + char slot[] = ":SS.F"; + int domain_len = sizeof domain - 1 /* For '\0' */; + int slot_len = sizeof slot - 1 /* For '\0' */; + int path_len; + char *path, *p; + int s; + + /* Calculate # of slots on path between device and root. */; + slot_depth = 0; + for (t = d; t; t = t->bus->parent_dev) { + ++slot_depth; + } + + path_len = domain_len + slot_len * slot_depth; + + /* Allocate memory, fill in the terminating null byte. */ + path = qemu_malloc(path_len + 1 /* For '\0' */); + path[path_len] = '\0'; + + /* First field is the domain. */ + s = snprintf(domain, sizeof domain, "%04x:00", pci_find_domain(d->bus)); + assert(s == domain_len); + memcpy(path, domain, domain_len); + + /* Fill in slot numbers. We walk up from device to root, so need to print + * them in the reverse order, last to first. */ + p = path + path_len; + for (t = d; t; t = t->bus->parent_dev) { + p -= slot_len; + s = snprintf(slot, sizeof slot, ":%02x.%x", + PCI_SLOT(t->devfn), PCI_FUNC(t->devfn)); + assert(s == slot_len); + memcpy(p, slot, slot_len); + } + + return path; } -device_init(pci_register_devices) +static int pci_qdev_find_recursive(PCIBus *bus, + const char *id, PCIDevice **pdev) +{ + DeviceState *qdev = qdev_find_recursive(&bus->qbus, id); + if (!qdev) { + return -ENODEV; + } + + /* roughly check if given qdev is pci device */ + if (qdev->info->init == &pci_qdev_init && + qdev->parent_bus->info == &pci_bus_info) { + *pdev = DO_UPCAST(PCIDevice, qdev, qdev); + return 0; + } + return -EINVAL; +} + +int pci_qdev_find_device(const char *id, PCIDevice **pdev) +{ + struct PCIHostBus *host; + int rc = -ENODEV; + + QLIST_FOREACH(host, &host_buses, next) { + int tmp = pci_qdev_find_recursive(host->bus, id, pdev); + if (!tmp) { + rc = 0; + break; + } + if (tmp != -ENODEV) { + rc = tmp; + } + } + + return rc; +} diff -Nru qemu-kvm-0.12.5+noroms/hw/pcie_aer.c qemu-kvm-0.14.1/hw/pcie_aer.c --- qemu-kvm-0.12.5+noroms/hw/pcie_aer.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/pcie_aer.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,1031 @@ +/* + * pcie_aer.c + * + * Copyright (c) 2010 Isaku Yamahata + * VA Linux Systems Japan K.K. + * + * 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, see . + */ + +#include "sysemu.h" +#include "qemu-objects.h" +#include "monitor.h" +#include "pci_bridge.h" +#include "pcie.h" +#include "msix.h" +#include "msi.h" +#include "pci_internals.h" +#include "pcie_regs.h" + +//#define DEBUG_PCIE +#ifdef DEBUG_PCIE +# define PCIE_DPRINTF(fmt, ...) \ + fprintf(stderr, "%s:%d " fmt, __func__, __LINE__, ## __VA_ARGS__) +#else +# define PCIE_DPRINTF(fmt, ...) do {} while (0) +#endif +#define PCIE_DEV_PRINTF(dev, fmt, ...) \ + PCIE_DPRINTF("%s:%x "fmt, (dev)->name, (dev)->devfn, ## __VA_ARGS__) + +/* From 6.2.7 Error Listing and Rules. Table 6-2, 6-3 and 6-4 */ +static uint32_t pcie_aer_uncor_default_severity(uint32_t status) +{ + switch (status) { + case PCI_ERR_UNC_INTN: + case PCI_ERR_UNC_DLP: + case PCI_ERR_UNC_SDN: + case PCI_ERR_UNC_RX_OVER: + case PCI_ERR_UNC_FCP: + case PCI_ERR_UNC_MALF_TLP: + return PCI_ERR_ROOT_CMD_FATAL_EN; + case PCI_ERR_UNC_POISON_TLP: + case PCI_ERR_UNC_ECRC: + case PCI_ERR_UNC_UNSUP: + case PCI_ERR_UNC_COMP_TIME: + case PCI_ERR_UNC_COMP_ABORT: + case PCI_ERR_UNC_UNX_COMP: + case PCI_ERR_UNC_ACSV: + case PCI_ERR_UNC_MCBTLP: + case PCI_ERR_UNC_ATOP_EBLOCKED: + case PCI_ERR_UNC_TLP_PRF_BLOCKED: + return PCI_ERR_ROOT_CMD_NONFATAL_EN; + default: + abort(); + break; + } + return PCI_ERR_ROOT_CMD_FATAL_EN; +} + +static int aer_log_add_err(PCIEAERLog *aer_log, const PCIEAERErr *err) +{ + if (aer_log->log_num == aer_log->log_max) { + return -1; + } + memcpy(&aer_log->log[aer_log->log_num], err, sizeof *err); + aer_log->log_num++; + return 0; +} + +static void aer_log_del_err(PCIEAERLog *aer_log, PCIEAERErr *err) +{ + assert(aer_log->log_num); + *err = aer_log->log[0]; + aer_log->log_num--; + memmove(&aer_log->log[0], &aer_log->log[1], + aer_log->log_num * sizeof *err); +} + +static void aer_log_clear_all_err(PCIEAERLog *aer_log) +{ + aer_log->log_num = 0; +} + +int pcie_aer_init(PCIDevice *dev, uint16_t offset) +{ + PCIExpressDevice *exp; + + pcie_add_capability(dev, PCI_EXT_CAP_ID_ERR, PCI_ERR_VER, + offset, PCI_ERR_SIZEOF); + exp = &dev->exp; + exp->aer_cap = offset; + + /* log_max is property */ + if (dev->exp.aer_log.log_max == PCIE_AER_LOG_MAX_UNSET) { + dev->exp.aer_log.log_max = PCIE_AER_LOG_MAX_DEFAULT; + } + /* clip down the value to avoid unreasobale memory usage */ + if (dev->exp.aer_log.log_max > PCIE_AER_LOG_MAX_LIMIT) { + return -EINVAL; + } + dev->exp.aer_log.log = qemu_mallocz(sizeof dev->exp.aer_log.log[0] * + dev->exp.aer_log.log_max); + + pci_set_long(dev->w1cmask + offset + PCI_ERR_UNCOR_STATUS, + PCI_ERR_UNC_SUPPORTED); + + pci_set_long(dev->config + offset + PCI_ERR_UNCOR_SEVER, + PCI_ERR_UNC_SEVERITY_DEFAULT); + pci_set_long(dev->wmask + offset + PCI_ERR_UNCOR_SEVER, + PCI_ERR_UNC_SUPPORTED); + + pci_long_test_and_set_mask(dev->w1cmask + offset + PCI_ERR_COR_STATUS, + PCI_ERR_COR_STATUS); + + pci_set_long(dev->config + offset + PCI_ERR_COR_MASK, + PCI_ERR_COR_MASK_DEFAULT); + pci_set_long(dev->wmask + offset + PCI_ERR_COR_MASK, + PCI_ERR_COR_SUPPORTED); + + /* capabilities and control. multiple header logging is supported */ + if (dev->exp.aer_log.log_max > 0) { + pci_set_long(dev->config + offset + PCI_ERR_CAP, + PCI_ERR_CAP_ECRC_GENC | PCI_ERR_CAP_ECRC_CHKC | + PCI_ERR_CAP_MHRC); + pci_set_long(dev->wmask + offset + PCI_ERR_CAP, + PCI_ERR_CAP_ECRC_GENE | PCI_ERR_CAP_ECRC_CHKE | + PCI_ERR_CAP_MHRE); + } else { + pci_set_long(dev->config + offset + PCI_ERR_CAP, + PCI_ERR_CAP_ECRC_GENC | PCI_ERR_CAP_ECRC_CHKC); + pci_set_long(dev->wmask + offset + PCI_ERR_CAP, + PCI_ERR_CAP_ECRC_GENE | PCI_ERR_CAP_ECRC_CHKE); + } + + switch (pcie_cap_get_type(dev)) { + case PCI_EXP_TYPE_ROOT_PORT: + /* this case will be set by pcie_aer_root_init() */ + /* fallthrough */ + case PCI_EXP_TYPE_DOWNSTREAM: + case PCI_EXP_TYPE_UPSTREAM: + pci_word_test_and_set_mask(dev->wmask + PCI_BRIDGE_CONTROL, + PCI_BRIDGE_CTL_SERR); + pci_long_test_and_set_mask(dev->w1cmask + PCI_STATUS, + PCI_SEC_STATUS_RCV_SYSTEM_ERROR); + break; + default: + /* nothing */ + break; + } + return 0; +} + +void pcie_aer_exit(PCIDevice *dev) +{ + qemu_free(dev->exp.aer_log.log); +} + +static void pcie_aer_update_uncor_status(PCIDevice *dev) +{ + uint8_t *aer_cap = dev->config + dev->exp.aer_cap; + PCIEAERLog *aer_log = &dev->exp.aer_log; + + uint16_t i; + for (i = 0; i < aer_log->log_num; i++) { + pci_long_test_and_set_mask(aer_cap + PCI_ERR_UNCOR_STATUS, + dev->exp.aer_log.log[i].status); + } +} + +/* + * return value: + * true: error message needs to be sent up + * false: error message is masked + * + * 6.2.6 Error Message Control + * Figure 6-3 + * all pci express devices part + */ +static bool +pcie_aer_msg_alldev(PCIDevice *dev, const PCIEAERMsg *msg) +{ + if (!(pcie_aer_msg_is_uncor(msg) && + (pci_get_word(dev->config + PCI_COMMAND) & PCI_COMMAND_SERR))) { + return false; + } + + /* Signaled System Error + * + * 7.5.1.1 Command register + * Bit 8 SERR# Enable + * + * When Set, this bit enables reporting of Non-fatal and Fatal + * errors detected by the Function to the Root Complex. Note that + * errors are reported if enabled either through this bit or through + * the PCI Express specific bits in the Device Control register (see + * Section 7.8.4). + */ + pci_word_test_and_set_mask(dev->config + PCI_STATUS, + PCI_STATUS_SIG_SYSTEM_ERROR); + + if (!(msg->severity & + pci_get_word(dev->config + dev->exp.exp_cap + PCI_EXP_DEVCTL))) { + return false; + } + + /* send up error message */ + return true; +} + +/* + * return value: + * true: error message is sent up + * false: error message is masked + * + * 6.2.6 Error Message Control + * Figure 6-3 + * virtual pci bridge part + */ +static bool pcie_aer_msg_vbridge(PCIDevice *dev, const PCIEAERMsg *msg) +{ + uint16_t bridge_control = pci_get_word(dev->config + PCI_BRIDGE_CONTROL); + + if (pcie_aer_msg_is_uncor(msg)) { + /* Received System Error */ + pci_word_test_and_set_mask(dev->config + PCI_SEC_STATUS, + PCI_SEC_STATUS_RCV_SYSTEM_ERROR); + } + + if (!(bridge_control & PCI_BRIDGE_CTL_SERR)) { + return false; + } + return true; +} + +void pcie_aer_root_set_vector(PCIDevice *dev, unsigned int vector) +{ + uint8_t *aer_cap = dev->config + dev->exp.aer_cap; + assert(vector < PCI_ERR_ROOT_IRQ_MAX); + pci_long_test_and_clear_mask(aer_cap + PCI_ERR_ROOT_STATUS, + PCI_ERR_ROOT_IRQ); + pci_long_test_and_set_mask(aer_cap + PCI_ERR_ROOT_STATUS, + vector << PCI_ERR_ROOT_IRQ_SHIFT); +} + +static unsigned int pcie_aer_root_get_vector(PCIDevice *dev) +{ + uint8_t *aer_cap = dev->config + dev->exp.aer_cap; + uint32_t root_status = pci_get_long(aer_cap + PCI_ERR_ROOT_STATUS); + return (root_status & PCI_ERR_ROOT_IRQ) >> PCI_ERR_ROOT_IRQ_SHIFT; +} + +/* Given a status register, get corresponding bits in the command register */ +static uint32_t pcie_aer_status_to_cmd(uint32_t status) +{ + uint32_t cmd = 0; + if (status & PCI_ERR_ROOT_COR_RCV) { + cmd |= PCI_ERR_ROOT_CMD_COR_EN; + } + if (status & PCI_ERR_ROOT_NONFATAL_RCV) { + cmd |= PCI_ERR_ROOT_CMD_NONFATAL_EN; + } + if (status & PCI_ERR_ROOT_FATAL_RCV) { + cmd |= PCI_ERR_ROOT_CMD_FATAL_EN; + } + return cmd; +} + +static void pcie_aer_root_notify(PCIDevice *dev) +{ + if (msix_enabled(dev)) { + msix_notify(dev, pcie_aer_root_get_vector(dev)); + } else if (msi_enabled(dev)) { + msi_notify(dev, pcie_aer_root_get_vector(dev)); + } else { + qemu_set_irq(dev->irq[dev->exp.aer_intx], 1); + } +} + +/* + * 6.2.6 Error Message Control + * Figure 6-3 + * root port part + */ +static void pcie_aer_msg_root_port(PCIDevice *dev, const PCIEAERMsg *msg) +{ + uint16_t cmd; + uint8_t *aer_cap; + uint32_t root_cmd; + uint32_t root_status, prev_status; + + cmd = pci_get_word(dev->config + PCI_COMMAND); + aer_cap = dev->config + dev->exp.aer_cap; + root_cmd = pci_get_long(aer_cap + PCI_ERR_ROOT_COMMAND); + prev_status = root_status = pci_get_long(aer_cap + PCI_ERR_ROOT_STATUS); + + if (cmd & PCI_COMMAND_SERR) { + /* System Error. + * + * The way to report System Error is platform specific and + * it isn't implemented in qemu right now. + * So just discard the error for now. + * OS which cares of aer would receive errors via + * native aer mechanims, so this wouldn't matter. + */ + } + + /* Errro Message Received: Root Error Status register */ + switch (msg->severity) { + case PCI_ERR_ROOT_CMD_COR_EN: + if (root_status & PCI_ERR_ROOT_COR_RCV) { + root_status |= PCI_ERR_ROOT_MULTI_COR_RCV; + } else { + pci_set_word(aer_cap + PCI_ERR_ROOT_COR_SRC, msg->source_id); + } + root_status |= PCI_ERR_ROOT_COR_RCV; + break; + case PCI_ERR_ROOT_CMD_NONFATAL_EN: + root_status |= PCI_ERR_ROOT_NONFATAL_RCV; + break; + case PCI_ERR_ROOT_CMD_FATAL_EN: + if (!(root_status & PCI_ERR_ROOT_UNCOR_RCV)) { + root_status |= PCI_ERR_ROOT_FIRST_FATAL; + } + root_status |= PCI_ERR_ROOT_FATAL_RCV; + break; + default: + abort(); + break; + } + if (pcie_aer_msg_is_uncor(msg)) { + if (root_status & PCI_ERR_ROOT_UNCOR_RCV) { + root_status |= PCI_ERR_ROOT_MULTI_UNCOR_RCV; + } else { + pci_set_word(aer_cap + PCI_ERR_ROOT_SRC, msg->source_id); + } + root_status |= PCI_ERR_ROOT_UNCOR_RCV; + } + pci_set_long(aer_cap + PCI_ERR_ROOT_STATUS, root_status); + + /* 6.2.4.1.2 Interrupt Generation */ + /* All the above did was set some bits in the status register. + * Specifically these that match message severity. + * The below code relies on this fact. */ + if (!(root_cmd & msg->severity) || + (pcie_aer_status_to_cmd(prev_status) & root_cmd)) { + /* Condition is not being set or was already true so nothing to do. */ + return; + } + + pcie_aer_root_notify(dev); +} + +/* + * 6.2.6 Error Message Control Figure 6-3 + * + * Walk up the bus tree from the device, propagate the error message. + */ +static void pcie_aer_msg(PCIDevice *dev, const PCIEAERMsg *msg) +{ + uint8_t type; + + while (dev) { + if (!pci_is_express(dev)) { + /* just ignore it */ + /* TODO: Shouldn't we set PCI_STATUS_SIG_SYSTEM_ERROR? + * Consider e.g. a PCI bridge above a PCI Express device. */ + return; + } + + type = pcie_cap_get_type(dev); + if ((type == PCI_EXP_TYPE_ROOT_PORT || + type == PCI_EXP_TYPE_UPSTREAM || + type == PCI_EXP_TYPE_DOWNSTREAM) && + !pcie_aer_msg_vbridge(dev, msg)) { + return; + } + if (!pcie_aer_msg_alldev(dev, msg)) { + return; + } + if (type == PCI_EXP_TYPE_ROOT_PORT) { + pcie_aer_msg_root_port(dev, msg); + /* Root port can notify system itself, + or send the error message to root complex event collector. */ + /* + * if root port is associated with an event collector, + * return the root complex event collector here. + * For now root complex event collector isn't supported. + */ + return; + } + dev = pci_bridge_get_device(dev->bus); + } +} + +static void pcie_aer_update_log(PCIDevice *dev, const PCIEAERErr *err) +{ + uint8_t *aer_cap = dev->config + dev->exp.aer_cap; + uint8_t first_bit = ffs(err->status) - 1; + uint32_t errcap = pci_get_long(aer_cap + PCI_ERR_CAP); + int i; + + assert(err->status); + assert(err->status & (err->status - 1)); + + errcap &= ~(PCI_ERR_CAP_FEP_MASK | PCI_ERR_CAP_TLP); + errcap |= PCI_ERR_CAP_FEP(first_bit); + + if (err->flags & PCIE_AER_ERR_HEADER_VALID) { + for (i = 0; i < ARRAY_SIZE(err->header); ++i) { + /* 7.10.8 Header Log Register */ + uint8_t *header_log = + aer_cap + PCI_ERR_HEADER_LOG + i * sizeof err->header[0]; + cpu_to_be32wu((uint32_t*)header_log, err->header[i]); + } + } else { + assert(!(err->flags & PCIE_AER_ERR_TLP_PREFIX_PRESENT)); + memset(aer_cap + PCI_ERR_HEADER_LOG, 0, PCI_ERR_HEADER_LOG_SIZE); + } + + if ((err->flags & PCIE_AER_ERR_TLP_PREFIX_PRESENT) && + (pci_get_long(dev->config + dev->exp.exp_cap + PCI_EXP_DEVCTL2) & + PCI_EXP_DEVCAP2_EETLPP)) { + for (i = 0; i < ARRAY_SIZE(err->prefix); ++i) { + /* 7.10.12 tlp prefix log register */ + uint8_t *prefix_log = + aer_cap + PCI_ERR_TLP_PREFIX_LOG + i * sizeof err->prefix[0]; + cpu_to_be32wu((uint32_t*)prefix_log, err->prefix[i]); + } + errcap |= PCI_ERR_CAP_TLP; + } else { + memset(aer_cap + PCI_ERR_TLP_PREFIX_LOG, 0, + PCI_ERR_TLP_PREFIX_LOG_SIZE); + } + pci_set_long(aer_cap + PCI_ERR_CAP, errcap); +} + +static void pcie_aer_clear_log(PCIDevice *dev) +{ + uint8_t *aer_cap = dev->config + dev->exp.aer_cap; + + pci_long_test_and_clear_mask(aer_cap + PCI_ERR_CAP, + PCI_ERR_CAP_FEP_MASK | PCI_ERR_CAP_TLP); + + memset(aer_cap + PCI_ERR_HEADER_LOG, 0, PCI_ERR_HEADER_LOG_SIZE); + memset(aer_cap + PCI_ERR_TLP_PREFIX_LOG, 0, PCI_ERR_TLP_PREFIX_LOG_SIZE); +} + +static void pcie_aer_clear_error(PCIDevice *dev) +{ + uint8_t *aer_cap = dev->config + dev->exp.aer_cap; + uint32_t errcap = pci_get_long(aer_cap + PCI_ERR_CAP); + PCIEAERLog *aer_log = &dev->exp.aer_log; + PCIEAERErr err; + + if (!(errcap & PCI_ERR_CAP_MHRE) || !aer_log->log_num) { + pcie_aer_clear_log(dev); + return; + } + + /* + * If more errors are queued, set corresponding bits in uncorrectable + * error status. + * We emulate uncorrectable error status register as W1CS. + * So set bit in uncorrectable error status here again for multiple + * error recording support. + * + * 6.2.4.2 Multiple Error Handling(Advanced Error Reporting Capability) + */ + pcie_aer_update_uncor_status(dev); + + aer_log_del_err(aer_log, &err); + pcie_aer_update_log(dev, &err); +} + +static int pcie_aer_record_error(PCIDevice *dev, + const PCIEAERErr *err) +{ + uint8_t *aer_cap = dev->config + dev->exp.aer_cap; + uint32_t errcap = pci_get_long(aer_cap + PCI_ERR_CAP); + int fep = PCI_ERR_CAP_FEP(errcap); + + assert(err->status); + assert(err->status & (err->status - 1)); + + if (errcap & PCI_ERR_CAP_MHRE && + (pci_get_long(aer_cap + PCI_ERR_UNCOR_STATUS) & (1U << fep))) { + /* Not first error. queue error */ + if (aer_log_add_err(&dev->exp.aer_log, err) < 0) { + /* overflow */ + return -1; + } + return 0; + } + + pcie_aer_update_log(dev, err); + return 0; +} + +typedef struct PCIEAERInject { + PCIDevice *dev; + uint8_t *aer_cap; + const PCIEAERErr *err; + uint16_t devctl; + uint16_t devsta; + uint32_t error_status; + bool unsupported_request; + bool log_overflow; + PCIEAERMsg msg; +} PCIEAERInject; + +static bool pcie_aer_inject_cor_error(PCIEAERInject *inj, + uint32_t uncor_status, + bool is_advisory_nonfatal) +{ + PCIDevice *dev = inj->dev; + + inj->devsta |= PCI_EXP_DEVSTA_CED; + if (inj->unsupported_request) { + inj->devsta |= PCI_EXP_DEVSTA_URD; + } + pci_set_word(dev->config + dev->exp.exp_cap + PCI_EXP_DEVSTA, inj->devsta); + + if (inj->aer_cap) { + uint32_t mask; + pci_long_test_and_set_mask(inj->aer_cap + PCI_ERR_COR_STATUS, + inj->error_status); + mask = pci_get_long(inj->aer_cap + PCI_ERR_COR_MASK); + if (mask & inj->error_status) { + return false; + } + if (is_advisory_nonfatal) { + uint32_t uncor_mask = + pci_get_long(inj->aer_cap + PCI_ERR_UNCOR_MASK); + if (!(uncor_mask & uncor_status)) { + inj->log_overflow = !!pcie_aer_record_error(dev, inj->err); + } + pci_long_test_and_set_mask(inj->aer_cap + PCI_ERR_UNCOR_STATUS, + uncor_status); + } + } + + if (inj->unsupported_request && !(inj->devctl & PCI_EXP_DEVCTL_URRE)) { + return false; + } + if (!(inj->devctl & PCI_EXP_DEVCTL_CERE)) { + return false; + } + + inj->msg.severity = PCI_ERR_ROOT_CMD_COR_EN; + return true; +} + +static bool pcie_aer_inject_uncor_error(PCIEAERInject *inj, bool is_fatal) +{ + PCIDevice *dev = inj->dev; + uint16_t cmd; + + if (is_fatal) { + inj->devsta |= PCI_EXP_DEVSTA_FED; + } else { + inj->devsta |= PCI_EXP_DEVSTA_NFED; + } + if (inj->unsupported_request) { + inj->devsta |= PCI_EXP_DEVSTA_URD; + } + pci_set_long(dev->config + dev->exp.exp_cap + PCI_EXP_DEVSTA, inj->devsta); + + if (inj->aer_cap) { + uint32_t mask = pci_get_long(inj->aer_cap + PCI_ERR_UNCOR_MASK); + if (mask & inj->error_status) { + pci_long_test_and_set_mask(inj->aer_cap + PCI_ERR_UNCOR_STATUS, + inj->error_status); + return false; + } + + inj->log_overflow = !!pcie_aer_record_error(dev, inj->err); + pci_long_test_and_set_mask(inj->aer_cap + PCI_ERR_UNCOR_STATUS, + inj->error_status); + } + + cmd = pci_get_word(dev->config + PCI_COMMAND); + if (inj->unsupported_request && + !(inj->devctl & PCI_EXP_DEVCTL_URRE) && !(cmd & PCI_COMMAND_SERR)) { + return false; + } + if (is_fatal) { + if (!((cmd & PCI_COMMAND_SERR) || + (inj->devctl & PCI_EXP_DEVCTL_FERE))) { + return false; + } + inj->msg.severity = PCI_ERR_ROOT_CMD_FATAL_EN; + } else { + if (!((cmd & PCI_COMMAND_SERR) || + (inj->devctl & PCI_EXP_DEVCTL_NFERE))) { + return false; + } + inj->msg.severity = PCI_ERR_ROOT_CMD_NONFATAL_EN; + } + return true; +} + +/* + * non-Function specific error must be recorded in all functions. + * It is the responsibility of the caller of this function. + * It is also caller's responsiblity to determine which function should + * report the rerror. + * + * 6.2.4 Error Logging + * 6.2.5 Sqeunce of Device Error Signaling and Logging Operations + * table 6-2: Flowchard Showing Sequence of Device Error Signaling and Logging + * Operations + */ +int pcie_aer_inject_error(PCIDevice *dev, const PCIEAERErr *err) +{ + uint8_t *aer_cap = NULL; + uint16_t devctl = 0; + uint16_t devsta = 0; + uint32_t error_status = err->status; + PCIEAERInject inj; + + if (!pci_is_express(dev)) { + return -ENOSYS; + } + + if (err->flags & PCIE_AER_ERR_IS_CORRECTABLE) { + error_status &= PCI_ERR_COR_SUPPORTED; + } else { + error_status &= PCI_ERR_UNC_SUPPORTED; + } + + /* invalid status bit. one and only one bit must be set */ + if (!error_status || (error_status & (error_status - 1))) { + return -EINVAL; + } + + if (dev->exp.aer_cap) { + uint8_t *exp_cap = dev->config + dev->exp.exp_cap; + aer_cap = dev->config + dev->exp.aer_cap; + devctl = pci_get_long(exp_cap + PCI_EXP_DEVCTL); + devsta = pci_get_long(exp_cap + PCI_EXP_DEVSTA); + } + + inj.dev = dev; + inj.aer_cap = aer_cap; + inj.err = err; + inj.devctl = devctl; + inj.devsta = devsta; + inj.error_status = error_status; + inj.unsupported_request = !(err->flags & PCIE_AER_ERR_IS_CORRECTABLE) && + err->status == PCI_ERR_UNC_UNSUP; + inj.log_overflow = false; + + if (err->flags & PCIE_AER_ERR_IS_CORRECTABLE) { + if (!pcie_aer_inject_cor_error(&inj, 0, false)) { + return 0; + } + } else { + bool is_fatal = + pcie_aer_uncor_default_severity(error_status) == + PCI_ERR_ROOT_CMD_FATAL_EN; + if (aer_cap) { + is_fatal = + error_status & pci_get_long(aer_cap + PCI_ERR_UNCOR_SEVER); + } + if (!is_fatal && (err->flags & PCIE_AER_ERR_MAYBE_ADVISORY)) { + inj.error_status = PCI_ERR_COR_ADV_NONFATAL; + if (!pcie_aer_inject_cor_error(&inj, error_status, true)) { + return 0; + } + } else { + if (!pcie_aer_inject_uncor_error(&inj, is_fatal)) { + return 0; + } + } + } + + /* send up error message */ + inj.msg.source_id = err->source_id; + pcie_aer_msg(dev, &inj.msg); + + if (inj.log_overflow) { + PCIEAERErr header_log_overflow = { + .status = PCI_ERR_COR_HL_OVERFLOW, + .flags = PCIE_AER_ERR_IS_CORRECTABLE, + }; + int ret = pcie_aer_inject_error(dev, &header_log_overflow); + assert(!ret); + } + return 0; +} + +void pcie_aer_write_config(PCIDevice *dev, + uint32_t addr, uint32_t val, int len) +{ + uint8_t *aer_cap = dev->config + dev->exp.aer_cap; + uint32_t errcap = pci_get_long(aer_cap + PCI_ERR_CAP); + uint32_t first_error = 1U << PCI_ERR_CAP_FEP(errcap); + uint32_t uncorsta = pci_get_long(aer_cap + PCI_ERR_UNCOR_STATUS); + + /* uncorrectable error */ + if (!(uncorsta & first_error)) { + /* the bit that corresponds to the first error is cleared */ + pcie_aer_clear_error(dev); + } else if (errcap & PCI_ERR_CAP_MHRE) { + /* When PCI_ERR_CAP_MHRE is enabled and the first error isn't cleared + * nothing should happen. So we have to revert the modification to + * the register. + */ + pcie_aer_update_uncor_status(dev); + } else { + /* capability & control + * PCI_ERR_CAP_MHRE might be cleared, so clear of header log. + */ + aer_log_clear_all_err(&dev->exp.aer_log); + } +} + +void pcie_aer_root_init(PCIDevice *dev) +{ + uint16_t pos = dev->exp.aer_cap; + + pci_set_long(dev->wmask + pos + PCI_ERR_ROOT_COMMAND, + PCI_ERR_ROOT_CMD_EN_MASK); + pci_set_long(dev->w1cmask + pos + PCI_ERR_ROOT_STATUS, + PCI_ERR_ROOT_STATUS_REPORT_MASK); +} + +void pcie_aer_root_reset(PCIDevice *dev) +{ + uint8_t* aer_cap = dev->config + dev->exp.aer_cap; + + pci_set_long(aer_cap + PCI_ERR_ROOT_COMMAND, 0); + + /* + * Advanced Error Interrupt Message Number in Root Error Status Register + * must be updated by chip dependent code because it's chip dependent + * which number is used. + */ +} + +void pcie_aer_root_write_config(PCIDevice *dev, + uint32_t addr, uint32_t val, int len, + uint32_t root_cmd_prev) +{ + uint8_t *aer_cap = dev->config + dev->exp.aer_cap; + uint32_t root_status = pci_get_long(aer_cap + PCI_ERR_ROOT_STATUS); + uint32_t enabled_cmd = pcie_aer_status_to_cmd(root_status); + uint32_t root_cmd = pci_get_long(aer_cap + PCI_ERR_ROOT_COMMAND); + /* 6.2.4.1.2 Interrupt Generation */ + if (!msix_enabled(dev) && !msi_enabled(dev)) { + qemu_set_irq(dev->irq[dev->exp.aer_intx], !!(root_cmd & enabled_cmd)); + return; + } + + if ((root_cmd_prev & enabled_cmd) || !(root_cmd & enabled_cmd)) { + /* Send MSI on transition from false to true. */ + return; + } + + pcie_aer_root_notify(dev); +} + +static const VMStateDescription vmstate_pcie_aer_err = { + .name = "PCIE_AER_ERROR", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(status, PCIEAERErr), + VMSTATE_UINT16(source_id, PCIEAERErr), + VMSTATE_UINT16(flags, PCIEAERErr), + VMSTATE_UINT32_ARRAY(header, PCIEAERErr, 4), + VMSTATE_UINT32_ARRAY(prefix, PCIEAERErr, 4), + VMSTATE_END_OF_LIST() + } +}; + +#define VMSTATE_PCIE_AER_ERRS(_field, _state, _field_num, _vmsd, _type) { \ + .name = (stringify(_field)), \ + .version_id = 0, \ + .num_offset = vmstate_offset_value(_state, _field_num, uint16_t), \ + .size = sizeof(_type), \ + .vmsd = &(_vmsd), \ + .flags = VMS_POINTER | VMS_VARRAY_UINT16 | VMS_STRUCT, \ + .offset = vmstate_offset_pointer(_state, _field, _type), \ +} + +const VMStateDescription vmstate_pcie_aer_log = { + .name = "PCIE_AER_ERROR_LOG", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT16(log_num, PCIEAERLog), + VMSTATE_UINT16(log_max, PCIEAERLog), + VMSTATE_PCIE_AER_ERRS(log, PCIEAERLog, log_num, + vmstate_pcie_aer_err, PCIEAERErr), + VMSTATE_END_OF_LIST() + } +}; + +void pcie_aer_inject_error_print(Monitor *mon, const QObject *data) +{ + QDict *qdict; + int devfn; + assert(qobject_type(data) == QTYPE_QDICT); + qdict = qobject_to_qdict(data); + + devfn = (int)qdict_get_int(qdict, "devfn"); + monitor_printf(mon, "OK id: %s domain: %x, bus: %x devfn: %x.%x\n", + qdict_get_str(qdict, "id"), + (int) qdict_get_int(qdict, "domain"), + (int) qdict_get_int(qdict, "bus"), + PCI_SLOT(devfn), PCI_FUNC(devfn)); +} + +typedef struct PCIEAERErrorName { + const char *name; + uint32_t val; + bool correctable; +} PCIEAERErrorName; + +/* + * AER error name -> value convertion table + * This naming scheme is same to linux aer-injection tool. + */ +static const struct PCIEAERErrorName pcie_aer_error_list[] = { + { + .name = "TRAIN", + .val = PCI_ERR_UNC_TRAIN, + .correctable = false, + }, { + .name = "DLP", + .val = PCI_ERR_UNC_DLP, + .correctable = false, + }, { + .name = "SDN", + .val = PCI_ERR_UNC_SDN, + .correctable = false, + }, { + .name = "POISON_TLP", + .val = PCI_ERR_UNC_POISON_TLP, + .correctable = false, + }, { + .name = "FCP", + .val = PCI_ERR_UNC_FCP, + .correctable = false, + }, { + .name = "COMP_TIME", + .val = PCI_ERR_UNC_COMP_TIME, + .correctable = false, + }, { + .name = "COMP_ABORT", + .val = PCI_ERR_UNC_COMP_ABORT, + .correctable = false, + }, { + .name = "UNX_COMP", + .val = PCI_ERR_UNC_UNX_COMP, + .correctable = false, + }, { + .name = "RX_OVER", + .val = PCI_ERR_UNC_RX_OVER, + .correctable = false, + }, { + .name = "MALF_TLP", + .val = PCI_ERR_UNC_MALF_TLP, + .correctable = false, + }, { + .name = "ECRC", + .val = PCI_ERR_UNC_ECRC, + .correctable = false, + }, { + .name = "UNSUP", + .val = PCI_ERR_UNC_UNSUP, + .correctable = false, + }, { + .name = "ACSV", + .val = PCI_ERR_UNC_ACSV, + .correctable = false, + }, { + .name = "INTN", + .val = PCI_ERR_UNC_INTN, + .correctable = false, + }, { + .name = "MCBTLP", + .val = PCI_ERR_UNC_MCBTLP, + .correctable = false, + }, { + .name = "ATOP_EBLOCKED", + .val = PCI_ERR_UNC_ATOP_EBLOCKED, + .correctable = false, + }, { + .name = "TLP_PRF_BLOCKED", + .val = PCI_ERR_UNC_TLP_PRF_BLOCKED, + .correctable = false, + }, { + .name = "RCVR", + .val = PCI_ERR_COR_RCVR, + .correctable = true, + }, { + .name = "BAD_TLP", + .val = PCI_ERR_COR_BAD_TLP, + .correctable = true, + }, { + .name = "BAD_DLLP", + .val = PCI_ERR_COR_BAD_DLLP, + .correctable = true, + }, { + .name = "REP_ROLL", + .val = PCI_ERR_COR_REP_ROLL, + .correctable = true, + }, { + .name = "REP_TIMER", + .val = PCI_ERR_COR_REP_TIMER, + .correctable = true, + }, { + .name = "ADV_NONFATAL", + .val = PCI_ERR_COR_ADV_NONFATAL, + .correctable = true, + }, { + .name = "INTERNAL", + .val = PCI_ERR_COR_INTERNAL, + .correctable = true, + }, { + .name = "HL_OVERFLOW", + .val = PCI_ERR_COR_HL_OVERFLOW, + .correctable = true, + }, +}; + +static int pcie_aer_parse_error_string(const char *error_name, + uint32_t *status, bool *correctable) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(pcie_aer_error_list); i++) { + const PCIEAERErrorName *e = &pcie_aer_error_list[i]; + if (strcmp(error_name, e->name)) { + continue; + } + + *status = e->val; + *correctable = e->correctable; + return 0; + } + return -EINVAL; +} + +int do_pcie_aer_inejct_error(Monitor *mon, + const QDict *qdict, QObject **ret_data) +{ + const char *id = qdict_get_str(qdict, "id"); + const char *error_name; + uint32_t error_status; + bool correctable; + PCIDevice *dev; + PCIEAERErr err; + int ret; + + ret = pci_qdev_find_device(id, &dev); + if (ret < 0) { + monitor_printf(mon, + "id or pci device path is invalid or device not " + "found. %s\n", id); + return ret; + } + if (!pci_is_express(dev)) { + monitor_printf(mon, "the device doesn't support pci express. %s\n", + id); + return -ENOSYS; + } + + error_name = qdict_get_str(qdict, "error_status"); + if (pcie_aer_parse_error_string(error_name, &error_status, &correctable)) { + char *e = NULL; + error_status = strtoul(error_name, &e, 0); + correctable = !!qdict_get_int(qdict, "correctable"); + if (!e || *e != '\0') { + monitor_printf(mon, "invalid error status value. \"%s\"", + error_name); + return -EINVAL; + } + } + err.source_id = (pci_bus_num(dev->bus) << 8) | dev->devfn; + + err.flags = 0; + if (correctable) { + err.flags |= PCIE_AER_ERR_IS_CORRECTABLE; + } + if (qdict_get_int(qdict, "advisory_non_fatal")) { + err.flags |= PCIE_AER_ERR_MAYBE_ADVISORY; + } + if (qdict_haskey(qdict, "header0")) { + err.flags |= PCIE_AER_ERR_HEADER_VALID; + } + if (qdict_haskey(qdict, "prefix0")) { + err.flags |= PCIE_AER_ERR_TLP_PREFIX_PRESENT; + } + + err.header[0] = qdict_get_try_int(qdict, "header0", 0); + err.header[1] = qdict_get_try_int(qdict, "header1", 0); + err.header[2] = qdict_get_try_int(qdict, "header2", 0); + err.header[3] = qdict_get_try_int(qdict, "header3", 0); + + err.prefix[0] = qdict_get_try_int(qdict, "prefix0", 0); + err.prefix[1] = qdict_get_try_int(qdict, "prefix1", 0); + err.prefix[2] = qdict_get_try_int(qdict, "prefix2", 0); + err.prefix[3] = qdict_get_try_int(qdict, "prefix3", 0); + + ret = pcie_aer_inject_error(dev, &err); + *ret_data = qobject_from_jsonf("{'id': %s, " + "'domain': %d, 'bus': %d, 'devfn': %d, " + "'ret': %d}", + id, + pci_find_domain(dev->bus), + pci_bus_num(dev->bus), dev->devfn, + ret); + assert(*ret_data); + + return 0; +} diff -Nru qemu-kvm-0.12.5+noroms/hw/pcie_aer.h qemu-kvm-0.14.1/hw/pcie_aer.h --- qemu-kvm-0.12.5+noroms/hw/pcie_aer.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/pcie_aer.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,106 @@ +/* + * pcie_aer.h + * + * Copyright (c) 2010 Isaku Yamahata + * VA Linux Systems Japan K.K. + * + * 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, see . + */ + +#ifndef QEMU_PCIE_AER_H +#define QEMU_PCIE_AER_H + +#include "hw.h" + +/* definitions which PCIExpressDevice uses */ + +/* AER log */ +struct PCIEAERLog { + /* This structure is saved/loaded. + So explicitly size them instead of unsigned int */ + + /* the number of currently recorded log in log member */ + uint16_t log_num; + + /* + * The maximum number of the log. Errors can be logged up to this. + * + * This is configurable property. + * The specified value will be clipped down to PCIE_AER_LOG_MAX_LIMIT + * to avoid unreasonable memory usage. + * I bet that 128 log size would be big enough, otherwise too many errors + * for system to function normaly. But could consecutive errors occur? + */ +#define PCIE_AER_LOG_MAX_DEFAULT 8 +#define PCIE_AER_LOG_MAX_LIMIT 128 +#define PCIE_AER_LOG_MAX_UNSET 0xffff + uint16_t log_max; + + /* Error log. log_max-sized array */ + PCIEAERErr *log; +}; + +/* aer error message: error signaling message has only error sevirity and + source id. See 2.2.8.3 error signaling messages */ +struct PCIEAERMsg { + /* + * PCI_ERR_ROOT_CMD_{COR, NONFATAL, FATAL}_EN + * = PCI_EXP_DEVCTL_{CERE, NFERE, FERE} + */ + uint32_t severity; + + uint16_t source_id; /* bdf */ +}; + +static inline bool +pcie_aer_msg_is_uncor(const PCIEAERMsg *msg) +{ + return msg->severity == PCI_ERR_ROOT_CMD_NONFATAL_EN || + msg->severity == PCI_ERR_ROOT_CMD_FATAL_EN; +} + +/* error */ +struct PCIEAERErr { + uint32_t status; /* error status bits */ + uint16_t source_id; /* bdf */ + +#define PCIE_AER_ERR_IS_CORRECTABLE 0x1 /* correctable/uncorrectable */ +#define PCIE_AER_ERR_MAYBE_ADVISORY 0x2 /* maybe advisory non-fatal */ +#define PCIE_AER_ERR_HEADER_VALID 0x4 /* TLP header is logged */ +#define PCIE_AER_ERR_TLP_PREFIX_PRESENT 0x8 /* TLP Prefix is logged */ + uint16_t flags; + + uint32_t header[4]; /* TLP header */ + uint32_t prefix[4]; /* TLP header prefix */ +}; + +extern const VMStateDescription vmstate_pcie_aer_log; + +int pcie_aer_init(PCIDevice *dev, uint16_t offset); +void pcie_aer_exit(PCIDevice *dev); +void pcie_aer_write_config(PCIDevice *dev, + uint32_t addr, uint32_t val, int len); + +/* aer root port */ +void pcie_aer_root_set_vector(PCIDevice *dev, unsigned int vector); +void pcie_aer_root_init(PCIDevice *dev); +void pcie_aer_root_reset(PCIDevice *dev); +void pcie_aer_root_write_config(PCIDevice *dev, + uint32_t addr, uint32_t val, int len, + uint32_t root_cmd_prev); + +/* error injection */ +int pcie_aer_inject_error(PCIDevice *dev, const PCIEAERErr *err); + +#endif /* QEMU_PCIE_AER_H */ diff -Nru qemu-kvm-0.12.5+noroms/hw/pcie.c qemu-kvm-0.14.1/hw/pcie.c --- qemu-kvm-0.12.5+noroms/hw/pcie.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/pcie.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,544 @@ +/* + * pcie.c + * + * Copyright (c) 2010 Isaku Yamahata + * VA Linux Systems Japan K.K. + * + * 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, see . + */ + +#include "sysemu.h" +#include "range.h" +#include "pci_bridge.h" +#include "pcie.h" +#include "msix.h" +#include "msi.h" +#include "pci_internals.h" +#include "pcie_regs.h" +#include "range.h" + +//#define DEBUG_PCIE +#ifdef DEBUG_PCIE +# define PCIE_DPRINTF(fmt, ...) \ + fprintf(stderr, "%s:%d " fmt, __func__, __LINE__, ## __VA_ARGS__) +#else +# define PCIE_DPRINTF(fmt, ...) do {} while (0) +#endif +#define PCIE_DEV_PRINTF(dev, fmt, ...) \ + PCIE_DPRINTF("%s:%x "fmt, (dev)->name, (dev)->devfn, ## __VA_ARGS__) + + +/*************************************************************************** + * pci express capability helper functions + */ +int pcie_cap_init(PCIDevice *dev, uint8_t offset, uint8_t type, uint8_t port) +{ + int pos; + uint8_t *exp_cap; + + assert(pci_is_express(dev)); + + pos = pci_add_capability(dev, PCI_CAP_ID_EXP, offset, + PCI_EXP_VER2_SIZEOF); + if (pos < 0) { + return pos; + } + dev->exp.exp_cap = pos; + exp_cap = dev->config + pos; + + /* capability register + interrupt message number defaults to 0 */ + pci_set_word(exp_cap + PCI_EXP_FLAGS, + ((type << PCI_EXP_FLAGS_TYPE_SHIFT) & PCI_EXP_FLAGS_TYPE) | + PCI_EXP_FLAGS_VER2); + + /* device capability register + * table 7-12: + * roll based error reporting bit must be set by all + * Functions conforming to the ECN, PCI Express Base + * Specification, Revision 1.1., or subsequent PCI Express Base + * Specification revisions. + */ + pci_set_long(exp_cap + PCI_EXP_DEVCAP, PCI_EXP_DEVCAP_RBER); + + pci_set_long(exp_cap + PCI_EXP_LNKCAP, + (port << PCI_EXP_LNKCAP_PN_SHIFT) | + PCI_EXP_LNKCAP_ASPMS_0S | + PCI_EXP_LNK_MLW_1 | + PCI_EXP_LNK_LS_25); + + pci_set_word(exp_cap + PCI_EXP_LNKSTA, + PCI_EXP_LNK_MLW_1 | PCI_EXP_LNK_LS_25); + + pci_set_long(exp_cap + PCI_EXP_DEVCAP2, + PCI_EXP_DEVCAP2_EFF | PCI_EXP_DEVCAP2_EETLPP); + + pci_set_word(dev->wmask + pos, PCI_EXP_DEVCTL2_EETLPPB); + return pos; +} + +void pcie_cap_exit(PCIDevice *dev) +{ + pci_del_capability(dev, PCI_CAP_ID_EXP, PCI_EXP_VER2_SIZEOF); +} + +uint8_t pcie_cap_get_type(const PCIDevice *dev) +{ + uint32_t pos = dev->exp.exp_cap; + assert(pos > 0); + return (pci_get_word(dev->config + pos + PCI_EXP_FLAGS) & + PCI_EXP_FLAGS_TYPE) >> PCI_EXP_FLAGS_TYPE_SHIFT; +} + +/* MSI/MSI-X */ +/* pci express interrupt message number */ +/* 7.8.2 PCI Express Capabilities Register: Interrupt Message Number */ +void pcie_cap_flags_set_vector(PCIDevice *dev, uint8_t vector) +{ + uint8_t *exp_cap = dev->config + dev->exp.exp_cap; + assert(vector < 32); + pci_word_test_and_clear_mask(exp_cap + PCI_EXP_FLAGS, PCI_EXP_FLAGS_IRQ); + pci_word_test_and_set_mask(exp_cap + PCI_EXP_FLAGS, + vector << PCI_EXP_FLAGS_IRQ_SHIFT); +} + +uint8_t pcie_cap_flags_get_vector(PCIDevice *dev) +{ + return (pci_get_word(dev->config + dev->exp.exp_cap + PCI_EXP_FLAGS) & + PCI_EXP_FLAGS_IRQ) >> PCI_EXP_FLAGS_IRQ_SHIFT; +} + +void pcie_cap_deverr_init(PCIDevice *dev) +{ + uint32_t pos = dev->exp.exp_cap; + pci_long_test_and_set_mask(dev->config + pos + PCI_EXP_DEVCAP, + PCI_EXP_DEVCAP_RBER); + pci_long_test_and_set_mask(dev->wmask + pos + PCI_EXP_DEVCTL, + PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_NFERE | + PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE); + pci_long_test_and_set_mask(dev->w1cmask + pos + PCI_EXP_DEVSTA, + PCI_EXP_DEVSTA_CED | PCI_EXP_DEVSTA_NFED | + PCI_EXP_DEVSTA_URD | PCI_EXP_DEVSTA_URD); +} + +void pcie_cap_deverr_reset(PCIDevice *dev) +{ + uint8_t *devctl = dev->config + dev->exp.exp_cap + PCI_EXP_DEVCTL; + pci_long_test_and_clear_mask(devctl, + PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_NFERE | + PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE); +} + +static void hotplug_event_update_event_status(PCIDevice *dev) +{ + uint32_t pos = dev->exp.exp_cap; + uint8_t *exp_cap = dev->config + pos; + uint16_t sltctl = pci_get_word(exp_cap + PCI_EXP_SLTCTL); + uint16_t sltsta = pci_get_word(exp_cap + PCI_EXP_SLTSTA); + + dev->exp.hpev_notified = (sltctl & PCI_EXP_SLTCTL_HPIE) && + (sltsta & sltctl & PCI_EXP_HP_EV_SUPPORTED); +} + +static void hotplug_event_notify(PCIDevice *dev) +{ + bool prev = dev->exp.hpev_notified; + + hotplug_event_update_event_status(dev); + + if (prev == dev->exp.hpev_notified) { + return; + } + + /* Note: the logic above does not take into account whether interrupts + * are masked. The result is that interrupt will be sent when it is + * subsequently unmasked. This appears to be legal: Section 6.7.3.4: + * The Port may optionally send an MSI when there are hot-plug events that + * occur while interrupt generation is disabled, and interrupt generation is + * subsequently enabled. */ + if (msix_enabled(dev)) { + msix_notify(dev, pcie_cap_flags_get_vector(dev)); + } else if (msi_enabled(dev)) { + msi_notify(dev, pcie_cap_flags_get_vector(dev)); + } else { + qemu_set_irq(dev->irq[dev->exp.hpev_intx], dev->exp.hpev_notified); + } +} + +/* + * A PCI Express Hot-Plug Event has occured, so update slot status register + * and notify OS of the event if necessary. + * + * 6.7.3 PCI Express Hot-Plug Events + * 6.7.3.4 Software Notification of Hot-Plug Events + */ +static void pcie_cap_slot_event(PCIDevice *dev, PCIExpressHotPlugEvent event) +{ + /* Minor optimization: if nothing changed - no event is needed. */ + if (pci_word_test_and_set_mask(dev->config + dev->exp.exp_cap + + PCI_EXP_SLTSTA, event)) { + return; + } + hotplug_event_notify(dev); +} + +static int pcie_cap_slot_hotplug(DeviceState *qdev, + PCIDevice *pci_dev, PCIHotplugState state) +{ + PCIDevice *d = DO_UPCAST(PCIDevice, qdev, qdev); + uint8_t *exp_cap = d->config + d->exp.exp_cap; + uint16_t sltsta = pci_get_word(exp_cap + PCI_EXP_SLTSTA); + + /* Don't send event when device is enabled during qemu machine creation: + * it is present on boot, no hotplug event is necessary. We do send an + * event when the device is disabled later. */ + if (state == PCI_COLDPLUG_ENABLED) { + pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTSTA, + PCI_EXP_SLTSTA_PDS); + return 0; + } + + PCIE_DEV_PRINTF(pci_dev, "hotplug state: %d\n", state); + if (sltsta & PCI_EXP_SLTSTA_EIS) { + /* the slot is electromechanically locked. + * This error is propagated up to qdev and then to HMP/QMP. + */ + return -EBUSY; + } + + /* TODO: multifunction hot-plug. + * Right now, only a device of function = 0 is allowed to be + * hot plugged/unplugged. + */ + assert(PCI_FUNC(pci_dev->devfn) == 0); + + if (state == PCI_HOTPLUG_ENABLED) { + pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTSTA, + PCI_EXP_SLTSTA_PDS); + pcie_cap_slot_event(d, PCI_EXP_HP_EV_PDC); + } else { + qdev_free(&pci_dev->qdev); + pci_word_test_and_clear_mask(exp_cap + PCI_EXP_SLTSTA, + PCI_EXP_SLTSTA_PDS); + pcie_cap_slot_event(d, PCI_EXP_HP_EV_PDC); + } + return 0; +} + +/* pci express slot for pci express root/downstream port + PCI express capability slot registers */ +void pcie_cap_slot_init(PCIDevice *dev, uint16_t slot) +{ + uint32_t pos = dev->exp.exp_cap; + + pci_word_test_and_set_mask(dev->config + pos + PCI_EXP_FLAGS, + PCI_EXP_FLAGS_SLOT); + + pci_long_test_and_clear_mask(dev->config + pos + PCI_EXP_SLTCAP, + ~PCI_EXP_SLTCAP_PSN); + pci_long_test_and_set_mask(dev->config + pos + PCI_EXP_SLTCAP, + (slot << PCI_EXP_SLTCAP_PSN_SHIFT) | + PCI_EXP_SLTCAP_EIP | + PCI_EXP_SLTCAP_HPS | + PCI_EXP_SLTCAP_HPC | + PCI_EXP_SLTCAP_PIP | + PCI_EXP_SLTCAP_AIP | + PCI_EXP_SLTCAP_ABP); + + pci_word_test_and_clear_mask(dev->config + pos + PCI_EXP_SLTCTL, + PCI_EXP_SLTCTL_PIC | + PCI_EXP_SLTCTL_AIC); + pci_word_test_and_set_mask(dev->config + pos + PCI_EXP_SLTCTL, + PCI_EXP_SLTCTL_PIC_OFF | + PCI_EXP_SLTCTL_AIC_OFF); + pci_word_test_and_set_mask(dev->wmask + pos + PCI_EXP_SLTCTL, + PCI_EXP_SLTCTL_PIC | + PCI_EXP_SLTCTL_AIC | + PCI_EXP_SLTCTL_HPIE | + PCI_EXP_SLTCTL_CCIE | + PCI_EXP_SLTCTL_PDCE | + PCI_EXP_SLTCTL_ABPE); + /* Although reading PCI_EXP_SLTCTL_EIC returns always 0, + * make the bit writable here in order to detect 1b is written. + * pcie_cap_slot_write_config() test-and-clear the bit, so + * this bit always returns 0 to the guest. + */ + pci_word_test_and_set_mask(dev->wmask + pos + PCI_EXP_SLTCTL, + PCI_EXP_SLTCTL_EIC); + + pci_word_test_and_set_mask(dev->w1cmask + pos + PCI_EXP_SLTSTA, + PCI_EXP_HP_EV_SUPPORTED); + + dev->exp.hpev_notified = false; + + pci_bus_hotplug(pci_bridge_get_sec_bus(DO_UPCAST(PCIBridge, dev, dev)), + pcie_cap_slot_hotplug, &dev->qdev); +} + +void pcie_cap_slot_reset(PCIDevice *dev) +{ + uint8_t *exp_cap = dev->config + dev->exp.exp_cap; + + PCIE_DEV_PRINTF(dev, "reset\n"); + + pci_word_test_and_clear_mask(exp_cap + PCI_EXP_SLTCTL, + PCI_EXP_SLTCTL_EIC | + PCI_EXP_SLTCTL_PIC | + PCI_EXP_SLTCTL_AIC | + PCI_EXP_SLTCTL_HPIE | + PCI_EXP_SLTCTL_CCIE | + PCI_EXP_SLTCTL_PDCE | + PCI_EXP_SLTCTL_ABPE); + pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTCTL, + PCI_EXP_SLTCTL_PIC_OFF | + PCI_EXP_SLTCTL_AIC_OFF); + + pci_word_test_and_clear_mask(exp_cap + PCI_EXP_SLTSTA, + PCI_EXP_SLTSTA_EIS |/* on reset, + the lock is released */ + PCI_EXP_SLTSTA_CC | + PCI_EXP_SLTSTA_PDC | + PCI_EXP_SLTSTA_ABP); + + hotplug_event_update_event_status(dev); +} + +void pcie_cap_slot_write_config(PCIDevice *dev, + uint32_t addr, uint32_t val, int len) +{ + uint32_t pos = dev->exp.exp_cap; + uint8_t *exp_cap = dev->config + pos; + uint16_t sltsta = pci_get_word(exp_cap + PCI_EXP_SLTSTA); + + if (!ranges_overlap(addr, len, pos + PCI_EXP_SLTCTL, 2)) { + return; + } + + if (pci_word_test_and_clear_mask(exp_cap + PCI_EXP_SLTCTL, + PCI_EXP_SLTCTL_EIC)) { + sltsta ^= PCI_EXP_SLTSTA_EIS; /* toggle PCI_EXP_SLTSTA_EIS bit */ + pci_set_word(exp_cap + PCI_EXP_SLTSTA, sltsta); + PCIE_DEV_PRINTF(dev, "PCI_EXP_SLTCTL_EIC: " + "sltsta -> 0x%02"PRIx16"\n", + sltsta); + } + + hotplug_event_notify(dev); + + /* + * 6.7.3.2 Command Completed Events + * + * Software issues a command to a hot-plug capable Downstream Port by + * issuing a write transaction that targets any portion of the Port’s Slot + * Control register. A single write to the Slot Control register is + * considered to be a single command, even if the write affects more than + * one field in the Slot Control register. In response to this transaction, + * the Port must carry out the requested actions and then set the + * associated status field for the command completed event. */ + + /* Real hardware might take a while to complete requested command because + * physical movement would be involved like locking the electromechanical + * lock. However in our case, command is completed instantaneously above, + * so send a command completion event right now. + */ + pcie_cap_slot_event(dev, PCI_EXP_HP_EV_CCI); +} + +int pcie_cap_slot_post_load(void *opaque, int version_id) +{ + PCIDevice *dev = opaque; + hotplug_event_update_event_status(dev); + return 0; +} + +void pcie_cap_slot_push_attention_button(PCIDevice *dev) +{ + pcie_cap_slot_event(dev, PCI_EXP_HP_EV_ABP); +} + +/* root control/capabilities/status. PME isn't emulated for now */ +void pcie_cap_root_init(PCIDevice *dev) +{ + pci_set_word(dev->wmask + dev->exp.exp_cap + PCI_EXP_RTCTL, + PCI_EXP_RTCTL_SECEE | PCI_EXP_RTCTL_SENFEE | + PCI_EXP_RTCTL_SEFEE); +} + +void pcie_cap_root_reset(PCIDevice *dev) +{ + pci_set_word(dev->config + dev->exp.exp_cap + PCI_EXP_RTCTL, 0); +} + +/* function level reset(FLR) */ +void pcie_cap_flr_init(PCIDevice *dev) +{ + pci_long_test_and_set_mask(dev->config + dev->exp.exp_cap + PCI_EXP_DEVCAP, + PCI_EXP_DEVCAP_FLR); + + /* Although reading BCR_FLR returns always 0, + * the bit is made writable here in order to detect the 1b is written + * pcie_cap_flr_write_config() test-and-clear the bit, so + * this bit always returns 0 to the guest. + */ + pci_word_test_and_set_mask(dev->wmask + dev->exp.exp_cap + PCI_EXP_DEVCTL, + PCI_EXP_DEVCTL_BCR_FLR); +} + +void pcie_cap_flr_write_config(PCIDevice *dev, + uint32_t addr, uint32_t val, int len) +{ + uint8_t *devctl = dev->config + dev->exp.exp_cap + PCI_EXP_DEVCTL; + if (pci_get_word(devctl) & PCI_EXP_DEVCTL_BCR_FLR) { + /* Clear PCI_EXP_DEVCTL_BCR_FLR after invoking the reset handler + so the handler can detect FLR by looking at this bit. */ + pci_device_reset(dev); + pci_word_test_and_clear_mask(devctl, PCI_EXP_DEVCTL_BCR_FLR); + } +} + +/* Alternative Routing-ID Interpretation (ARI) */ +/* ari forwarding support for down stream port */ +void pcie_cap_ari_init(PCIDevice *dev) +{ + uint32_t pos = dev->exp.exp_cap; + pci_long_test_and_set_mask(dev->config + pos + PCI_EXP_DEVCAP2, + PCI_EXP_DEVCAP2_ARI); + pci_long_test_and_set_mask(dev->wmask + pos + PCI_EXP_DEVCTL2, + PCI_EXP_DEVCTL2_ARI); +} + +void pcie_cap_ari_reset(PCIDevice *dev) +{ + uint8_t *devctl2 = dev->config + dev->exp.exp_cap + PCI_EXP_DEVCTL2; + pci_long_test_and_clear_mask(devctl2, PCI_EXP_DEVCTL2_ARI); +} + +bool pcie_cap_is_ari_enabled(const PCIDevice *dev) +{ + if (!pci_is_express(dev)) { + return false; + } + if (!dev->exp.exp_cap) { + return false; + } + + return pci_get_long(dev->config + dev->exp.exp_cap + PCI_EXP_DEVCTL2) & + PCI_EXP_DEVCTL2_ARI; +} + +/************************************************************************** + * pci express extended capability allocation functions + * uint16_t ext_cap_id (16 bit) + * uint8_t cap_ver (4 bit) + * uint16_t cap_offset (12 bit) + * uint16_t ext_cap_size + */ + +static uint16_t pcie_find_capability_list(PCIDevice *dev, uint16_t cap_id, + uint16_t *prev_p) +{ + uint16_t prev = 0; + uint16_t next; + uint32_t header = pci_get_long(dev->config + PCI_CONFIG_SPACE_SIZE); + + if (!header) { + /* no extended capability */ + next = 0; + goto out; + } + for (next = PCI_CONFIG_SPACE_SIZE; next; + prev = next, next = PCI_EXT_CAP_NEXT(header)) { + + assert(next >= PCI_CONFIG_SPACE_SIZE); + assert(next <= PCIE_CONFIG_SPACE_SIZE - 8); + + header = pci_get_long(dev->config + next); + if (PCI_EXT_CAP_ID(header) == cap_id) { + break; + } + } + +out: + if (prev_p) { + *prev_p = prev; + } + return next; +} + +uint16_t pcie_find_capability(PCIDevice *dev, uint16_t cap_id) +{ + return pcie_find_capability_list(dev, cap_id, NULL); +} + +static void pcie_ext_cap_set_next(PCIDevice *dev, uint16_t pos, uint16_t next) +{ + uint16_t header = pci_get_long(dev->config + pos); + assert(!(next & (PCI_EXT_CAP_ALIGN - 1))); + header = (header & ~PCI_EXT_CAP_NEXT_MASK) | + ((next << PCI_EXT_CAP_NEXT_SHIFT) & PCI_EXT_CAP_NEXT_MASK); + pci_set_long(dev->config + pos, header); +} + +/* + * caller must supply valid (offset, size) * such that the range shouldn't + * overlap with other capability or other registers. + * This function doesn't check it. + */ +void pcie_add_capability(PCIDevice *dev, + uint16_t cap_id, uint8_t cap_ver, + uint16_t offset, uint16_t size) +{ + uint32_t header; + uint16_t next; + + assert(offset >= PCI_CONFIG_SPACE_SIZE); + assert(offset < offset + size); + assert(offset + size < PCIE_CONFIG_SPACE_SIZE); + assert(size >= 8); + assert(pci_is_express(dev)); + + if (offset == PCI_CONFIG_SPACE_SIZE) { + header = pci_get_long(dev->config + offset); + next = PCI_EXT_CAP_NEXT(header); + } else { + uint16_t prev; + + /* 0 is reserved cap id. use internally to find the last capability + in the linked list */ + next = pcie_find_capability_list(dev, 0, &prev); + + assert(prev >= PCI_CONFIG_SPACE_SIZE); + assert(next == 0); + pcie_ext_cap_set_next(dev, prev, offset); + } + pci_set_long(dev->config + offset, PCI_EXT_CAP(cap_id, cap_ver, next)); + + /* Make capability read-only by default */ + memset(dev->wmask + offset, 0, size); + memset(dev->w1cmask + offset, 0, size); + /* Check capability by default */ + memset(dev->cmask + offset, 0xFF, size); +} + +/************************************************************************** + * pci express extended capability helper functions + */ + +/* ARI */ +void pcie_ari_init(PCIDevice *dev, uint16_t offset, uint16_t nextfn) +{ + pcie_add_capability(dev, PCI_EXT_CAP_ID_ARI, PCI_ARI_VER, + offset, PCI_ARI_SIZEOF); + pci_set_long(dev->config + offset + PCI_ARI_CAP, PCI_ARI_CAP_NFN(nextfn)); +} diff -Nru qemu-kvm-0.12.5+noroms/hw/pcie.h qemu-kvm-0.14.1/hw/pcie.h --- qemu-kvm-0.12.5+noroms/hw/pcie.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/pcie.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,132 @@ +/* + * pcie.h + * + * Copyright (c) 2010 Isaku Yamahata + * VA Linux Systems Japan K.K. + * + * 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, see . + */ + +#ifndef QEMU_PCIE_H +#define QEMU_PCIE_H + +#include "hw.h" +#include "pci_regs.h" +#include "pcie_regs.h" +#include "pcie_aer.h" + +typedef enum { + /* for attention and power indicator */ + PCI_EXP_HP_IND_RESERVED = PCI_EXP_SLTCTL_IND_RESERVED, + PCI_EXP_HP_IND_ON = PCI_EXP_SLTCTL_IND_ON, + PCI_EXP_HP_IND_BLINK = PCI_EXP_SLTCTL_IND_BLINK, + PCI_EXP_HP_IND_OFF = PCI_EXP_SLTCTL_IND_OFF, +} PCIExpressIndicator; + +typedef enum { + /* these bits must match the bits in Slot Control/Status registers. + * PCI_EXP_HP_EV_xxx = PCI_EXP_SLTCTL_xxxE = PCI_EXP_SLTSTA_xxx + * + * Not all the bits of slot control register match with the ones of + * slot status. Not some bits of slot status register is used to + * show status, not to report event occurence. + * So such bits must be masked out when checking the software + * notification condition. + */ + PCI_EXP_HP_EV_ABP = PCI_EXP_SLTCTL_ABPE, + /* attention button pressed */ + PCI_EXP_HP_EV_PDC = PCI_EXP_SLTCTL_PDCE, + /* presence detect changed */ + PCI_EXP_HP_EV_CCI = PCI_EXP_SLTCTL_CCIE, + /* command completed */ + + PCI_EXP_HP_EV_SUPPORTED = PCI_EXP_HP_EV_ABP | + PCI_EXP_HP_EV_PDC | + PCI_EXP_HP_EV_CCI, + /* supported event mask */ + + /* events not listed aren't supported */ +} PCIExpressHotPlugEvent; + +struct PCIExpressDevice { + /* Offset of express capability in config space */ + uint8_t exp_cap; + + /* SLOT */ + unsigned int hpev_intx; /* INTx for hot plug event (0-3:INT[A-D]#) + * default is 0 = INTA# + * If the chip wants to use other interrupt + * line, initialize this member with the + * desired number. + * If the chip dynamically changes this member, + * also initialize it when loaded as + * appropreately. + */ + bool hpev_notified; /* Logical AND of conditions for hot plug event. + Following 6.7.3.4: + Software Notification of Hot-Plug Events, an interrupt + is sent whenever the logical and of these conditions + transitions from false to true. */ + + /* AER */ + uint16_t aer_cap; + PCIEAERLog aer_log; + unsigned int aer_intx; /* INTx for error reporting + * default is 0 = INTA# + * If the chip wants to use other interrupt + * line, initialize this member with the + * desired number. + * If the chip dynamically changes this member, + * also initialize it when loaded as + * appropreately. + */ +}; + +/* PCI express capability helper functions */ +int pcie_cap_init(PCIDevice *dev, uint8_t offset, uint8_t type, uint8_t port); +void pcie_cap_exit(PCIDevice *dev); +uint8_t pcie_cap_get_type(const PCIDevice *dev); +void pcie_cap_flags_set_vector(PCIDevice *dev, uint8_t vector); +uint8_t pcie_cap_flags_get_vector(PCIDevice *dev); + +void pcie_cap_deverr_init(PCIDevice *dev); +void pcie_cap_deverr_reset(PCIDevice *dev); + +void pcie_cap_slot_init(PCIDevice *dev, uint16_t slot); +void pcie_cap_slot_reset(PCIDevice *dev); +void pcie_cap_slot_write_config(PCIDevice *dev, + uint32_t addr, uint32_t val, int len); +int pcie_cap_slot_post_load(void *opaque, int version_id); +void pcie_cap_slot_push_attention_button(PCIDevice *dev); + +void pcie_cap_root_init(PCIDevice *dev); +void pcie_cap_root_reset(PCIDevice *dev); + +void pcie_cap_flr_init(PCIDevice *dev); +void pcie_cap_flr_write_config(PCIDevice *dev, + uint32_t addr, uint32_t val, int len); + +void pcie_cap_ari_init(PCIDevice *dev); +void pcie_cap_ari_reset(PCIDevice *dev); +bool pcie_cap_is_ari_enabled(const PCIDevice *dev); + +/* PCI express extended capability helper functions */ +uint16_t pcie_find_capability(PCIDevice *dev, uint16_t cap_id); +void pcie_add_capability(PCIDevice *dev, + uint16_t cap_id, uint8_t cap_ver, + uint16_t offset, uint16_t size); + +void pcie_ari_init(PCIDevice *dev, uint16_t offset, uint16_t nextfn); + +#endif /* QEMU_PCIE_H */ diff -Nru qemu-kvm-0.12.5+noroms/hw/pcie_host.c qemu-kvm-0.14.1/hw/pcie_host.c --- qemu-kvm-0.12.5+noroms/hw/pcie_host.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/pcie_host.c 2011-05-11 13:29:46.000000000 +0000 @@ -16,8 +16,7 @@ * 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. + * with this program; if not, see . */ #include "hw.h" @@ -138,7 +137,8 @@ { e->base_addr = PCIE_BASE_ADDR_UNMAPPED; e->mmio_index = - cpu_register_io_memory(pcie_mmcfg_read, pcie_mmcfg_write, e); + cpu_register_io_memory(pcie_mmcfg_read, pcie_mmcfg_write, e, + DEVICE_NATIVE_ENDIAN); if (e->mmio_index < 0) { return -1; } diff -Nru qemu-kvm-0.12.5+noroms/hw/pcie_host.h qemu-kvm-0.14.1/hw/pcie_host.h --- qemu-kvm-0.12.5+noroms/hw/pcie_host.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/pcie_host.h 2011-05-11 13:29:46.000000000 +0000 @@ -15,8 +15,7 @@ * 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. + * with this program; if not, see . */ #ifndef PCIE_HOST_H diff -Nru qemu-kvm-0.12.5+noroms/hw/pcie_port.c qemu-kvm-0.14.1/hw/pcie_port.c --- qemu-kvm-0.12.5+noroms/hw/pcie_port.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/pcie_port.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,124 @@ +/* + * pcie_port.c + * + * Copyright (c) 2010 Isaku Yamahata + * VA Linux Systems Japan K.K. + * + * 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, see . + */ + +#include "pcie_port.h" + +void pcie_port_init_reg(PCIDevice *d) +{ + /* Unlike pci bridge, + 66MHz and fast back to back don't apply to pci express port. */ + pci_set_word(d->config + PCI_STATUS, 0); + pci_set_word(d->config + PCI_SEC_STATUS, 0); + + /* Unlike conventional pci bridge, some bits are hardwared to 0. */ + pci_set_word(d->wmask + PCI_BRIDGE_CONTROL, + PCI_BRIDGE_CTL_PARITY | + PCI_BRIDGE_CTL_ISA | + PCI_BRIDGE_CTL_VGA | + PCI_BRIDGE_CTL_SERR | + PCI_BRIDGE_CTL_BUS_RESET); + + /* 7.5.3.5 Prefetchable Memory Base Limit + * The Prefetchable Memory Base and Prefetchable Memory Limit registers + * must indicate that 64-bit addresses are supported, as defined in + * PCI-to-PCI Bridge Architecture Specification, Revision 1.2. + */ + pci_word_test_and_set_mask(d->config + PCI_PREF_MEMORY_BASE, + PCI_PREF_RANGE_TYPE_64); + pci_word_test_and_set_mask(d->config + PCI_PREF_MEMORY_LIMIT, + PCI_PREF_RANGE_TYPE_64); +} + +/************************************************************************** + * (chassis number, pcie physical slot number) -> pcie slot conversion + */ +struct PCIEChassis { + uint8_t number; + + QLIST_HEAD(, PCIESlot) slots; + QLIST_ENTRY(PCIEChassis) next; +}; + +static QLIST_HEAD(, PCIEChassis) chassis = QLIST_HEAD_INITIALIZER(chassis); + +static struct PCIEChassis *pcie_chassis_find(uint8_t chassis_number) +{ + struct PCIEChassis *c; + QLIST_FOREACH(c, &chassis, next) { + if (c->number == chassis_number) { + break; + } + } + return c; +} + +void pcie_chassis_create(uint8_t chassis_number) +{ + struct PCIEChassis *c; + c = pcie_chassis_find(chassis_number); + if (c) { + return; + } + c = qemu_mallocz(sizeof(*c)); + c->number = chassis_number; + QLIST_INIT(&c->slots); + QLIST_INSERT_HEAD(&chassis, c, next); +} + +static PCIESlot *pcie_chassis_find_slot_with_chassis(struct PCIEChassis *c, + uint8_t slot) +{ + PCIESlot *s; + QLIST_FOREACH(s, &c->slots, next) { + if (s->slot == slot) { + break; + } + } + return s; +} + +PCIESlot *pcie_chassis_find_slot(uint8_t chassis_number, uint16_t slot) +{ + struct PCIEChassis *c; + c = pcie_chassis_find(chassis_number); + if (!c) { + return NULL; + } + return pcie_chassis_find_slot_with_chassis(c, slot); +} + +int pcie_chassis_add_slot(struct PCIESlot *slot) +{ + struct PCIEChassis *c; + c = pcie_chassis_find(slot->chassis); + if (!c) { + return -ENODEV; + } + if (pcie_chassis_find_slot_with_chassis(c, slot->slot)) { + return -EBUSY; + } + QLIST_INSERT_HEAD(&c->slots, slot, next); + return 0; +} + +void pcie_chassis_del_slot(PCIESlot *s) +{ + QLIST_REMOVE(s, next); +} diff -Nru qemu-kvm-0.12.5+noroms/hw/pcie_port.h qemu-kvm-0.14.1/hw/pcie_port.h --- qemu-kvm-0.12.5+noroms/hw/pcie_port.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/pcie_port.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,51 @@ +/* + * pcie_port.h + * + * Copyright (c) 2010 Isaku Yamahata + * VA Linux Systems Japan K.K. + * + * 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, see . + */ + +#ifndef QEMU_PCIE_PORT_H +#define QEMU_PCIE_PORT_H + +#include "pci_bridge.h" +#include "pci_internals.h" + +struct PCIEPort { + PCIBridge br; + + /* pci express switch port */ + uint8_t port; +}; + +void pcie_port_init_reg(PCIDevice *d); + +struct PCIESlot { + PCIEPort port; + + /* pci express switch port with slot */ + uint8_t chassis; + uint16_t slot; + QLIST_ENTRY(PCIESlot) next; +}; + +void pcie_chassis_create(uint8_t chassis_number); +void pcie_main_chassis_create(void); +PCIESlot *pcie_chassis_find_slot(uint8_t chassis, uint16_t slot); +int pcie_chassis_add_slot(struct PCIESlot *slot); +void pcie_chassis_del_slot(PCIESlot *s); + +#endif /* QEMU_PCIE_PORT_H */ diff -Nru qemu-kvm-0.12.5+noroms/hw/pcie_regs.h qemu-kvm-0.14.1/hw/pcie_regs.h --- qemu-kvm-0.12.5+noroms/hw/pcie_regs.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/pcie_regs.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,156 @@ +/* + * constants for pcie configurations space from pci express spec. + * + * TODO: + * Those constants and macros should go to Linux pci_regs.h + * Once they're merged, they will go away. + */ +#ifndef QEMU_PCIE_REGS_H +#define QEMU_PCIE_REGS_H + + +/* express capability */ + +#define PCI_EXP_VER2_SIZEOF 0x3c /* express capability of ver. 2 */ +#define PCI_EXT_CAP_VER_SHIFT 16 +#define PCI_EXT_CAP_NEXT_SHIFT 20 +#define PCI_EXT_CAP_NEXT_MASK (0xffc << PCI_EXT_CAP_NEXT_SHIFT) + +#define PCI_EXT_CAP(id, ver, next) \ + ((id) | \ + ((ver) << PCI_EXT_CAP_VER_SHIFT) | \ + ((next) << PCI_EXT_CAP_NEXT_SHIFT)) + +#define PCI_EXT_CAP_ALIGN 4 +#define PCI_EXT_CAP_ALIGNUP(x) \ + (((x) + PCI_EXT_CAP_ALIGN - 1) & ~(PCI_EXT_CAP_ALIGN - 1)) + +/* PCI_EXP_FLAGS */ +#define PCI_EXP_FLAGS_VER2 2 /* for now, supports only ver. 2 */ +#define PCI_EXP_FLAGS_IRQ_SHIFT (ffs(PCI_EXP_FLAGS_IRQ) - 1) +#define PCI_EXP_FLAGS_TYPE_SHIFT (ffs(PCI_EXP_FLAGS_TYPE) - 1) + + +/* PCI_EXP_LINK{CAP, STA} */ +/* link speed */ +#define PCI_EXP_LNK_LS_25 1 + +#define PCI_EXP_LNK_MLW_SHIFT (ffs(PCI_EXP_LNKCAP_MLW) - 1) +#define PCI_EXP_LNK_MLW_1 (1 << PCI_EXP_LNK_MLW_SHIFT) + +/* PCI_EXP_LINKCAP */ +#define PCI_EXP_LNKCAP_ASPMS_SHIFT (ffs(PCI_EXP_LNKCAP_ASPMS) - 1) +#define PCI_EXP_LNKCAP_ASPMS_0S (1 << PCI_EXP_LNKCAP_ASPMS_SHIFT) + +#define PCI_EXP_LNKCAP_PN_SHIFT (ffs(PCI_EXP_LNKCAP_PN) - 1) + +#define PCI_EXP_SLTCAP_PSN_SHIFT (ffs(PCI_EXP_SLTCAP_PSN) - 1) + +#define PCI_EXP_SLTCTL_IND_RESERVED 0x0 +#define PCI_EXP_SLTCTL_IND_ON 0x1 +#define PCI_EXP_SLTCTL_IND_BLINK 0x2 +#define PCI_EXP_SLTCTL_IND_OFF 0x3 +#define PCI_EXP_SLTCTL_AIC_SHIFT (ffs(PCI_EXP_SLTCTL_AIC) - 1) +#define PCI_EXP_SLTCTL_AIC_OFF \ + (PCI_EXP_SLTCTL_IND_OFF << PCI_EXP_SLTCTL_AIC_SHIFT) + +#define PCI_EXP_SLTCTL_PIC_SHIFT (ffs(PCI_EXP_SLTCTL_PIC) - 1) +#define PCI_EXP_SLTCTL_PIC_OFF \ + (PCI_EXP_SLTCTL_IND_OFF << PCI_EXP_SLTCTL_PIC_SHIFT) + +#define PCI_EXP_SLTCTL_SUPPORTED \ + (PCI_EXP_SLTCTL_ABPE | \ + PCI_EXP_SLTCTL_PDCE | \ + PCI_EXP_SLTCTL_CCIE | \ + PCI_EXP_SLTCTL_HPIE | \ + PCI_EXP_SLTCTL_AIC | \ + PCI_EXP_SLTCTL_PCC | \ + PCI_EXP_SLTCTL_EIC) + +#define PCI_EXP_DEVCAP2_EFF 0x100000 +#define PCI_EXP_DEVCAP2_EETLPP 0x200000 + +#define PCI_EXP_DEVCTL2_EETLPPB 0x80 + +/* ARI */ +#define PCI_ARI_VER 1 +#define PCI_ARI_SIZEOF 8 + +/* AER */ +#define PCI_ERR_VER 2 +#define PCI_ERR_SIZEOF 0x48 + +#define PCI_ERR_UNC_SDN 0x00000020 /* surprise down */ +#define PCI_ERR_UNC_ACSV 0x00200000 /* ACS Violation */ +#define PCI_ERR_UNC_INTN 0x00400000 /* Internal Error */ +#define PCI_ERR_UNC_MCBTLP 0x00800000 /* MC Blcoked TLP */ +#define PCI_ERR_UNC_ATOP_EBLOCKED 0x01000000 /* atomic op egress blocked */ +#define PCI_ERR_UNC_TLP_PRF_BLOCKED 0x02000000 /* TLP Prefix Blocked */ +#define PCI_ERR_COR_ADV_NONFATAL 0x00002000 /* Advisory Non-Fatal */ +#define PCI_ERR_COR_INTERNAL 0x00004000 /* Corrected Internal */ +#define PCI_ERR_COR_HL_OVERFLOW 0x00008000 /* Header Long Overflow */ +#define PCI_ERR_CAP_FEP_MASK 0x0000001f +#define PCI_ERR_CAP_MHRC 0x00000200 +#define PCI_ERR_CAP_MHRE 0x00000400 +#define PCI_ERR_CAP_TLP 0x00000800 + +#define PCI_ERR_HEADER_LOG_SIZE 16 +#define PCI_ERR_TLP_PREFIX_LOG 0x38 +#define PCI_ERR_TLP_PREFIX_LOG_SIZE 16 + +#define PCI_SEC_STATUS_RCV_SYSTEM_ERROR 0x4000 + +/* aer root error command/status */ +#define PCI_ERR_ROOT_CMD_EN_MASK (PCI_ERR_ROOT_CMD_COR_EN | \ + PCI_ERR_ROOT_CMD_NONFATAL_EN | \ + PCI_ERR_ROOT_CMD_FATAL_EN) + +#define PCI_ERR_ROOT_IRQ_MAX 32 +#define PCI_ERR_ROOT_IRQ 0xf8000000 +#define PCI_ERR_ROOT_IRQ_SHIFT (ffs(PCI_ERR_ROOT_IRQ) - 1) +#define PCI_ERR_ROOT_STATUS_REPORT_MASK (PCI_ERR_ROOT_COR_RCV | \ + PCI_ERR_ROOT_MULTI_COR_RCV | \ + PCI_ERR_ROOT_UNCOR_RCV | \ + PCI_ERR_ROOT_MULTI_UNCOR_RCV | \ + PCI_ERR_ROOT_FIRST_FATAL | \ + PCI_ERR_ROOT_NONFATAL_RCV | \ + PCI_ERR_ROOT_FATAL_RCV) + +#define PCI_ERR_UNC_SUPPORTED (PCI_ERR_UNC_DLP | \ + PCI_ERR_UNC_SDN | \ + PCI_ERR_UNC_POISON_TLP | \ + PCI_ERR_UNC_FCP | \ + PCI_ERR_UNC_COMP_TIME | \ + PCI_ERR_UNC_COMP_ABORT | \ + PCI_ERR_UNC_UNX_COMP | \ + PCI_ERR_UNC_RX_OVER | \ + PCI_ERR_UNC_MALF_TLP | \ + PCI_ERR_UNC_ECRC | \ + PCI_ERR_UNC_UNSUP | \ + PCI_ERR_UNC_ACSV | \ + PCI_ERR_UNC_INTN | \ + PCI_ERR_UNC_MCBTLP | \ + PCI_ERR_UNC_ATOP_EBLOCKED | \ + PCI_ERR_UNC_TLP_PRF_BLOCKED) + +#define PCI_ERR_UNC_SEVERITY_DEFAULT (PCI_ERR_UNC_DLP | \ + PCI_ERR_UNC_SDN | \ + PCI_ERR_UNC_FCP | \ + PCI_ERR_UNC_RX_OVER | \ + PCI_ERR_UNC_MALF_TLP | \ + PCI_ERR_UNC_INTN) + +#define PCI_ERR_COR_SUPPORTED (PCI_ERR_COR_RCVR | \ + PCI_ERR_COR_BAD_TLP | \ + PCI_ERR_COR_BAD_DLLP | \ + PCI_ERR_COR_REP_ROLL | \ + PCI_ERR_COR_REP_TIMER | \ + PCI_ERR_COR_ADV_NONFATAL | \ + PCI_ERR_COR_INTERNAL | \ + PCI_ERR_COR_HL_OVERFLOW) + +#define PCI_ERR_COR_MASK_DEFAULT (PCI_ERR_COR_ADV_NONFATAL | \ + PCI_ERR_COR_INTERNAL | \ + PCI_ERR_COR_HL_OVERFLOW) + +#endif /* QEMU_PCIE_REGS_H */ diff -Nru qemu-kvm-0.12.5+noroms/hw/pci.h qemu-kvm-0.14.1/hw/pci.h --- qemu-kvm-0.12.5+noroms/hw/pci.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/pci.h 2011-05-11 13:29:46.000000000 +0000 @@ -2,6 +2,7 @@ #define QEMU_PCI_H #include "qemu-common.h" +#include "qobject.h" #include "qdev.h" @@ -10,16 +11,14 @@ /* PCI includes legacy ISA access. */ #include "isa.h" -/* imported from */ -#define PCI_SLOT(devfn) (((devfn) >> 3) & 0x1f) -#define PCI_FUNC(devfn) ((devfn) & 0x07) +#include "pcie.h" /* PCI bus */ -extern target_phys_addr_t pci_mem_base; #define PCI_DEVFN(slot, func) ((((slot) & 0x1f) << 3) | ((func) & 0x07)) #define PCI_SLOT(devfn) (((devfn) >> 3) & 0x1f) #define PCI_FUNC(devfn) ((devfn) & 0x07) +#define PCI_FUNC_MAX 8 /* Class, Vendor and Device IDs from Linux's pci_ids.h */ #include "pci_ids.h" @@ -65,6 +64,7 @@ /* Intel (0x8086) */ #define PCI_DEVICE_ID_INTEL_82551IT 0x1209 #define PCI_DEVICE_ID_INTEL_82557 0x1229 +#define PCI_DEVICE_ID_INTEL_82801IR 0x2922 /* Red Hat / Qumranet (for QEMU) -- see pci-ids.txt */ #define PCI_VENDOR_ID_REDHAT_QUMRANET 0x1af4 @@ -76,7 +76,6 @@ #define PCI_DEVICE_ID_VIRTIO_BALLOON 0x1002 #define PCI_DEVICE_ID_VIRTIO_CONSOLE 0x1003 -typedef uint64_t pcibus_t; #define FMT_PCIBUS PRIx64 typedef void PCIConfigWriteFunc(PCIDevice *pci_dev, @@ -87,12 +86,6 @@ pcibus_t addr, pcibus_t size, int type); typedef int PCIUnregisterFunc(PCIDevice *pci_dev); -typedef void PCICapConfigWriteFunc(PCIDevice *pci_dev, - uint32_t address, uint32_t val, int len); -typedef uint32_t PCICapConfigReadFunc(PCIDevice *pci_dev, - uint32_t address, int len); -typedef int PCICapConfigInitFunc(PCIDevice *pci_dev); - typedef struct PCIIORegion { pcibus_t addr; /* current PCI mapping address. -1 means not mapped */ #define PCI_BAR_UNMAPPED (~(pcibus_t)0) @@ -105,97 +98,10 @@ #define PCI_ROM_SLOT 6 #define PCI_NUM_REGIONS 7 -/* Declarations from linux/pci_regs.h */ -#define PCI_VENDOR_ID 0x00 /* 16 bits */ -#define PCI_DEVICE_ID 0x02 /* 16 bits */ -#define PCI_COMMAND 0x04 /* 16 bits */ -#define PCI_COMMAND_IO 0x1 /* Enable response in I/O space */ -#define PCI_COMMAND_MEMORY 0x2 /* Enable response in Memory space */ -#define PCI_COMMAND_MASTER 0x4 /* Enable bus master */ -#define PCI_STATUS 0x06 /* 16 bits */ -#define PCI_STATUS_INTERRUPT 0x08 -#define PCI_REVISION_ID 0x08 /* 8 bits */ -#define PCI_CLASS_PROG 0x09 /* Reg. Level Programming Interface */ -#define PCI_CLASS_DEVICE 0x0a /* Device class */ -#define PCI_CACHE_LINE_SIZE 0x0c /* 8 bits */ -#define PCI_LATENCY_TIMER 0x0d /* 8 bits */ -#define PCI_HEADER_TYPE 0x0e /* 8 bits */ -#define PCI_HEADER_TYPE_NORMAL 0 -#define PCI_HEADER_TYPE_BRIDGE 1 -#define PCI_HEADER_TYPE_CARDBUS 2 -#define PCI_HEADER_TYPE_MULTI_FUNCTION 0x80 -#define PCI_BASE_ADDRESS_0 0x10 /* 32 bits */ -#define PCI_BASE_ADDRESS_SPACE_IO 0x01 -#define PCI_BASE_ADDRESS_SPACE_MEMORY 0x00 -#define PCI_BASE_ADDRESS_MEM_TYPE_64 0x04 /* 64 bit address */ -#define PCI_BASE_ADDRESS_MEM_PREFETCH 0x08 /* prefetchable? */ -#define PCI_PRIMARY_BUS 0x18 /* Primary bus number */ -#define PCI_SECONDARY_BUS 0x19 /* Secondary bus number */ -#define PCI_SUBORDINATE_BUS 0x1a /* Highest bus number behind the bridge */ -#define PCI_IO_BASE 0x1c /* I/O range behind the bridge */ -#define PCI_IO_LIMIT 0x1d -#define PCI_IO_RANGE_TYPE_32 0x01 -#define PCI_IO_RANGE_MASK (~0x0fUL) -#define PCI_SEC_STATUS 0x1e /* Secondary status register, only bit 14 used */ -#define PCI_MEMORY_BASE 0x20 /* Memory range behind */ -#define PCI_MEMORY_LIMIT 0x22 -#define PCI_MEMORY_RANGE_MASK (~0x0fUL) -#define PCI_PREF_MEMORY_BASE 0x24 /* Prefetchable memory range behind */ -#define PCI_PREF_MEMORY_LIMIT 0x26 -#define PCI_PREF_RANGE_MASK (~0x0fUL) -#define PCI_PREF_RANGE_TYPE_64 0x01 -#define PCI_PREF_BASE_UPPER32 0x28 /* Upper half of prefetchable memory range */ -#define PCI_PREF_LIMIT_UPPER32 0x2c -#define PCI_SUBSYSTEM_VENDOR_ID 0x2c /* 16 bits */ -#define PCI_SUBSYSTEM_ID 0x2e /* 16 bits */ -#define PCI_ROM_ADDRESS 0x30 /* Bits 31..11 are address, 10..1 reserved */ -#define PCI_ROM_ADDRESS_ENABLE 0x01 -#define PCI_IO_BASE_UPPER16 0x30 /* Upper half of I/O addresses */ -#define PCI_IO_LIMIT_UPPER16 0x32 -#define PCI_CAPABILITY_LIST 0x34 /* Offset of first capability list entry */ -#define PCI_ROM_ADDRESS1 0x38 /* Same as PCI_ROM_ADDRESS, but for htype 1 */ -#define PCI_INTERRUPT_LINE 0x3c /* 8 bits */ -#define PCI_INTERRUPT_PIN 0x3d /* 8 bits */ -#define PCI_MIN_GNT 0x3e /* 8 bits */ -#define PCI_BRIDGE_CONTROL 0x3e -#define PCI_MAX_LAT 0x3f /* 8 bits */ - -/* Capability lists */ -#define PCI_CAP_LIST_ID 0 /* Capability ID */ -#define PCI_CAP_LIST_NEXT 1 /* Next capability in the list */ - -#define PCI_REVISION 0x08 /* obsolete, use PCI_REVISION_ID */ -#define PCI_SUBVENDOR_ID 0x2c /* obsolete, use PCI_SUBSYSTEM_VENDOR_ID */ -#define PCI_SUBDEVICE_ID 0x2e /* obsolete, use PCI_SUBSYSTEM_ID */ - -/* Bits in the PCI Status Register (PCI 2.3 spec) */ -#define PCI_STATUS_RESERVED1 0x007 -#define PCI_STATUS_INT_STATUS 0x008 -#ifndef PCI_STATUS_CAP_LIST -#define PCI_STATUS_CAP_LIST 0x010 -#endif -#ifndef PCI_STATUS_66MHZ -#define PCI_STATUS_66MHZ 0x020 -#endif - -#define PCI_STATUS_RESERVED2 0x040 - -#ifndef PCI_STATUS_FAST_BACK -#define PCI_STATUS_FAST_BACK 0x080 -#endif - -#define PCI_STATUS_DEVSEL 0x600 - -#define PCI_STATUS_RESERVED_MASK_LO (PCI_STATUS_RESERVED1 | \ - PCI_STATUS_INT_STATUS | PCI_STATUS_CAPABILITIES | \ - PCI_STATUS_66MHZ | PCI_STATUS_RESERVED2 | PCI_STATUS_FAST_BACK) +#include "pci_regs.h" -#define PCI_STATUS_RESERVED_MASK_HI (PCI_STATUS_DEVSEL >> 8) - -/* Bits in the PCI Command Register (PCI 2.3 spec) */ -#define PCI_COMMAND_RESERVED 0xf800 - -#define PCI_COMMAND_RESERVED_MASK_HI (PCI_COMMAND_RESERVED >> 8) +/* PCI HEADER_TYPE */ +#define PCI_HEADER_TYPE_MULTI_FUNCTION 0x80 /* Size of the standard PCI config header */ #define PCI_CONFIG_HEADER_SIZE 0x40 @@ -208,14 +114,28 @@ /* Bits in cap_present field. */ enum { - QEMU_PCI_CAP_MSIX = 0x1, - QEMU_PCI_CAP_EXPRESS = 0x2, + QEMU_PCI_CAP_MSI = 0x1, + QEMU_PCI_CAP_MSIX = 0x2, + QEMU_PCI_CAP_EXPRESS = 0x4, + + /* multifunction capable device */ +#define QEMU_PCI_CAP_MULTIFUNCTION_BITNR 3 + QEMU_PCI_CAP_MULTIFUNCTION = (1 << QEMU_PCI_CAP_MULTIFUNCTION_BITNR), + + /* command register SERR bit enabled */ +#define QEMU_PCI_CAP_SERR_BITNR 4 + QEMU_PCI_CAP_SERR = (1 << QEMU_PCI_CAP_SERR_BITNR), }; -#define PCI_CAPABILITY_CONFIG_MAX_LENGTH 0x60 -#define PCI_CAPABILITY_CONFIG_DEFAULT_START_ADDR 0x40 -#define PCI_CAPABILITY_CONFIG_MSI_LENGTH 0x10 -#define PCI_CAPABILITY_CONFIG_MSIX_LENGTH 0x10 +typedef int (*msix_mask_notifier_func)(PCIDevice *, unsigned vector, + int masked); + +struct kvm_msix_message { + uint32_t gsi; + uint32_t addr_lo; + uint32_t addr_hi; + uint32_t data; +}; struct PCIDevice { DeviceState qdev; @@ -229,8 +149,11 @@ /* Used to implement R/W bytes */ uint8_t *wmask; - /* Used to allocate config space for capabilities. */ - uint8_t *used; + /* Used to implement RW1C(Write 1 to Clear) bytes */ + uint8_t *w1cmask; + + /* Used to allocate config space and track capabilities. */ + uint8_t *config_map; /* the following fields are read only */ PCIBus *bus; @@ -268,6 +191,12 @@ /* Version id needed for VMState */ int32_t version_id; + /* Offset of MSI capability in config space */ + uint8_t msi_cap; + + /* PCI Express */ + PCIExpressDevice exp; + /* Location of option rom */ char *romfile; ram_addr_t rom_offset; @@ -280,15 +209,9 @@ * on the rest of the region. */ target_phys_addr_t msix_page_size; - struct kvm_irq_routing_entry *msix_irq_entries; + struct kvm_msix_message *msix_irq_entries; - /* Device capability configuration space */ - struct { - int supported; - unsigned int start, length; - PCICapConfigReadFunc *config_read; - PCICapConfigWriteFunc *config_write; - } cap; + msix_mask_notifier_func msix_mask_notifier; }; PCIDevice *pci_register_device(PCIBus *bus, const char *name, @@ -297,23 +220,19 @@ PCIConfigWriteFunc *config_write); void pci_register_bar(PCIDevice *pci_dev, int region_num, - pcibus_t size, int type, + pcibus_t size, uint8_t type, PCIMapIORegionFunc *map_func); -int pci_enable_capability_support(PCIDevice *pci_dev, - uint32_t config_start, - PCICapConfigReadFunc *config_read, - PCICapConfigWriteFunc *config_write, - PCICapConfigInitFunc *config_init); +void pci_map_option_rom(PCIDevice *pdev, int region_num, pcibus_t addr, + pcibus_t size, int type); int pci_map_irq(PCIDevice *pci_dev, int pin); -int pci_add_capability(PCIDevice *pci_dev, uint8_t cap_id, uint8_t cap_size); +int pci_add_capability(PCIDevice *pdev, uint8_t cap_id, + uint8_t offset, uint8_t size); void pci_del_capability(PCIDevice *pci_dev, uint8_t cap_id, uint8_t cap_size); -void pci_reserve_capability(PCIDevice *pci_dev, uint8_t offset, uint8_t size); - uint8_t pci_find_capability(PCIDevice *pci_dev, uint8_t cap_id); uint32_t pci_default_read_config(PCIDevice *d, @@ -322,24 +241,30 @@ uint32_t address, uint32_t val, int len); void pci_device_save(PCIDevice *s, QEMUFile *f); int pci_device_load(PCIDevice *s, QEMUFile *f); -uint32_t pci_default_cap_read_config(PCIDevice *pci_dev, - uint32_t address, int len); -void pci_default_cap_write_config(PCIDevice *pci_dev, - uint32_t address, uint32_t val, int len); -int pci_access_cap_config(PCIDevice *pci_dev, uint32_t address, int len); - typedef void (*pci_set_irq_fn)(void *opaque, int irq_num, int level); typedef int (*pci_map_irq_fn)(PCIDevice *pci_dev, int irq_num); -typedef int (*pci_hotplug_fn)(PCIDevice *pci_dev, int state); + +typedef enum { + PCI_HOTPLUG_DISABLED, + PCI_HOTPLUG_ENABLED, + PCI_COLDPLUG_ENABLED, +} PCIHotplugState; + +typedef int (*pci_hotplug_fn)(DeviceState *qdev, PCIDevice *pci_dev, + PCIHotplugState state); void pci_bus_new_inplace(PCIBus *bus, DeviceState *parent, const char *name, int devfn_min); PCIBus *pci_bus_new(DeviceState *parent, const char *name, int devfn_min); void pci_bus_irqs(PCIBus *bus, pci_set_irq_fn set_irq, pci_map_irq_fn map_irq, void *irq_opaque, int nirq); -void pci_bus_hotplug(PCIBus *bus, pci_hotplug_fn hotplug); +void pci_bus_hotplug(PCIBus *bus, pci_hotplug_fn hotplug, DeviceState *dev); PCIBus *pci_register_bus(DeviceState *parent, const char *name, pci_set_irq_fn set_irq, pci_map_irq_fn map_irq, void *irq_opaque, int devfn_min, int nirq); +void pci_device_reset(PCIDevice *dev); +void pci_bus_reset(PCIBus *bus); + +void pci_bus_set_mem_base(PCIBus *bus, target_phys_addr_t base); PCIDevice *pci_nic_init(NICInfo *nd, const char *default_model, const char *default_devaddr); @@ -348,20 +273,25 @@ int pci_bus_num(PCIBus *s); void pci_for_each_device(PCIBus *bus, int bus_num, void (*fn)(PCIBus *bus, PCIDevice *d)); PCIBus *pci_find_root_bus(int domain); +int pci_find_domain(const PCIBus *bus); PCIBus *pci_find_bus(PCIBus *bus, int bus_num); PCIDevice *pci_find_device(PCIBus *bus, int bus_num, int slot, int function); +int pci_qdev_find_device(const char *id, PCIDevice **pdev); PCIBus *pci_get_bus_devfn(int *devfnp, const char *devaddr); +int pci_parse_devaddr(const char *addr, int *domp, int *busp, + unsigned int *slotp, unsigned int *funcp); int pci_read_devaddr(Monitor *mon, const char *addr, int *domp, int *busp, unsigned *slotp); -int pci_parse_host_devaddr(const char *addr, int *busp, +int pci_parse_host_devaddr(const char *addr, int *segp, int *busp, int *slotp, int *funcp); -void pci_info(Monitor *mon); -PCIBus *pci_bridge_init(PCIBus *bus, int devfn, uint16_t vid, uint16_t did, - pci_map_irq_fn map_irq, const char *name); -PCIDevice *pci_bridge_get_device(PCIBus *bus); +void do_pci_info_print(Monitor *mon, const QObject *data); +void do_pci_info(Monitor *mon, QObject **ret_data); +void pci_bridge_update_mappings(PCIBus *b); + +void pci_device_deassert_intx(PCIDevice *dev); static inline void pci_set_byte(uint8_t *config, uint8_t val) @@ -370,7 +300,7 @@ } static inline uint8_t -pci_get_byte(uint8_t *config) +pci_get_byte(const uint8_t *config) { return *config; } @@ -382,9 +312,9 @@ } static inline uint16_t -pci_get_word(uint8_t *config) +pci_get_word(const uint8_t *config) { - return le16_to_cpupu((uint16_t *)config); + return le16_to_cpupu((const uint16_t *)config); } static inline void @@ -394,9 +324,9 @@ } static inline uint32_t -pci_get_long(uint8_t *config) +pci_get_long(const uint8_t *config) { - return le32_to_cpupu((uint32_t *)config); + return le32_to_cpupu((const uint32_t *)config); } static inline void @@ -406,9 +336,9 @@ } static inline uint64_t -pci_get_quad(uint8_t *config) +pci_get_quad(const uint8_t *config) { - return le64_to_cpup((uint64_t *)config); + return le64_to_cpup((const uint64_t *)config); } static inline void @@ -424,11 +354,99 @@ } static inline void +pci_config_set_revision(uint8_t *pci_config, uint8_t val) +{ + pci_set_byte(&pci_config[PCI_REVISION_ID], val); +} + +static inline void pci_config_set_class(uint8_t *pci_config, uint16_t val) { pci_set_word(&pci_config[PCI_CLASS_DEVICE], val); } +static inline void +pci_config_set_prog_interface(uint8_t *pci_config, uint8_t val) +{ + pci_set_byte(&pci_config[PCI_CLASS_PROG], val); +} + +static inline void +pci_config_set_interrupt_pin(uint8_t *pci_config, uint8_t val) +{ + pci_set_byte(&pci_config[PCI_INTERRUPT_PIN], val); +} + +/* + * helper functions to do bit mask operation on configuration space. + * Just to set bit, use test-and-set and discard returned value. + * Just to clear bit, use test-and-clear and discard returned value. + * NOTE: They aren't atomic. + */ +static inline uint8_t +pci_byte_test_and_clear_mask(uint8_t *config, uint8_t mask) +{ + uint8_t val = pci_get_byte(config); + pci_set_byte(config, val & ~mask); + return val & mask; +} + +static inline uint8_t +pci_byte_test_and_set_mask(uint8_t *config, uint8_t mask) +{ + uint8_t val = pci_get_byte(config); + pci_set_byte(config, val | mask); + return val & mask; +} + +static inline uint16_t +pci_word_test_and_clear_mask(uint8_t *config, uint16_t mask) +{ + uint16_t val = pci_get_word(config); + pci_set_word(config, val & ~mask); + return val & mask; +} + +static inline uint16_t +pci_word_test_and_set_mask(uint8_t *config, uint16_t mask) +{ + uint16_t val = pci_get_word(config); + pci_set_word(config, val | mask); + return val & mask; +} + +static inline uint32_t +pci_long_test_and_clear_mask(uint8_t *config, uint32_t mask) +{ + uint32_t val = pci_get_long(config); + pci_set_long(config, val & ~mask); + return val & mask; +} + +static inline uint32_t +pci_long_test_and_set_mask(uint8_t *config, uint32_t mask) +{ + uint32_t val = pci_get_long(config); + pci_set_long(config, val | mask); + return val & mask; +} + +static inline uint64_t +pci_quad_test_and_clear_mask(uint8_t *config, uint64_t mask) +{ + uint64_t val = pci_get_quad(config); + pci_set_quad(config, val & ~mask); + return val & mask; +} + +static inline uint64_t +pci_quad_test_and_set_mask(uint8_t *config, uint64_t mask) +{ + uint64_t val = pci_get_quad(config); + pci_set_quad(config, val | mask); + return val & mask; +} + typedef int (*pci_qdev_initfn)(PCIDevice *dev); typedef struct { DeviceInfo qdev; @@ -437,12 +455,19 @@ PCIConfigReadFunc *config_read; PCIConfigWriteFunc *config_write; - /* pci config header type */ - uint8_t header_type; + /* + * pci-to-pci bridge or normal device. + * This doesn't mean pci host switch. + * When card bus bridge is supported, this would be enhanced. + */ + int is_bridge; /* pcie stuff */ int is_express; /* is this device pci express? */ + /* device isn't hot-pluggable */ + int no_hotplug; + /* rom bar */ const char *romfile; } PCIDeviceInfo; @@ -450,46 +475,22 @@ void pci_qdev_register(PCIDeviceInfo *info); void pci_qdev_register_many(PCIDeviceInfo *info); +PCIDevice *pci_create_multifunction(PCIBus *bus, int devfn, bool multifunction, + const char *name); +PCIDevice *pci_create_simple_multifunction(PCIBus *bus, int devfn, + bool multifunction, + const char *name); PCIDevice *pci_create(PCIBus *bus, int devfn, const char *name); PCIDevice *pci_create_simple(PCIBus *bus, int devfn, const char *name); -static inline int pci_is_express(PCIDevice *d) +static inline int pci_is_express(const PCIDevice *d) { return d->cap_present & QEMU_PCI_CAP_EXPRESS; } -static inline uint32_t pci_config_size(PCIDevice *d) +static inline uint32_t pci_config_size(const PCIDevice *d) { return pci_is_express(d) ? PCIE_CONFIG_SPACE_SIZE : PCI_CONFIG_SPACE_SIZE; } -/* These are not pci specific. Should move into a separate header. - * Only pci.c uses them, so keep them here for now. - */ - -/* Get last byte of a range from offset + length. - * Undefined for ranges that wrap around 0. */ -static inline uint64_t range_get_last(uint64_t offset, uint64_t len) -{ - return offset + len - 1; -} - -/* Check whether a given range covers a given byte. */ -static inline int range_covers_byte(uint64_t offset, uint64_t len, - uint64_t byte) -{ - return offset <= byte && byte <= range_get_last(offset, len); -} - -/* Check whether 2 given ranges overlap. - * Undefined if ranges that wrap around 0. */ -static inline int ranges_overlap(uint64_t first1, uint64_t len1, - uint64_t first2, uint64_t len2) -{ - uint64_t last1 = range_get_last(first1, len1); - uint64_t last2 = range_get_last(first2, len2); - - return !(last2 < first1 || last1 < first2); -} - #endif diff -Nru qemu-kvm-0.12.5+noroms/hw/pci_host.c qemu-kvm-0.14.1/hw/pci_host.c --- qemu-kvm-0.12.5+noroms/hw/pci_host.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/pci_host.c 2011-05-11 13:29:46.000000000 +0000 @@ -15,8 +15,7 @@ * 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. + * with this program; if not, see . */ #include "pci.h" @@ -56,7 +55,7 @@ if (!pci_dev) return; - PCI_DPRINTF("%s: %s: addr=%02"PRIx32" val=%08"PRI32x" len=%d\n", + PCI_DPRINTF("%s: %s: addr=%02" PRIx32 " val=%08" PRIx32 " len=%d\n", __func__, pci_dev->name, config_addr, val, len); pci_dev->config_write(pci_dev, config_addr, val, len); } @@ -79,152 +78,82 @@ return val; } -static void pci_host_config_writel(void *opaque, target_phys_addr_t addr, - uint32_t val) +static void pci_host_config_write(ReadWriteHandler *handler, + pcibus_t addr, uint32_t val, int len) { - PCIHostState *s = opaque; + PCIHostState *s = container_of(handler, PCIHostState, conf_handler); -#ifdef TARGET_WORDS_BIGENDIAN - val = bswap32(val); -#endif - PCI_DPRINTF("%s addr " TARGET_FMT_plx " val %"PRIx32"\n", - __func__, addr, val); + PCI_DPRINTF("%s addr %" FMT_PCIBUS " %d val %"PRIx32"\n", + __func__, addr, len, val); s->config_reg = val; } -static uint32_t pci_host_config_readl(void *opaque, target_phys_addr_t addr) +static uint32_t pci_host_config_read(ReadWriteHandler *handler, + pcibus_t addr, int len) { - PCIHostState *s = opaque; + PCIHostState *s = container_of(handler, PCIHostState, conf_handler); uint32_t val = s->config_reg; -#ifdef TARGET_WORDS_BIGENDIAN - val = bswap32(val); -#endif - PCI_DPRINTF("%s addr " TARGET_FMT_plx " val %"PRIx32"\n", - __func__, addr, val); + PCI_DPRINTF("%s addr %" FMT_PCIBUS " len %d val %"PRIx32"\n", + __func__, addr, len, val); return val; } -static CPUWriteMemoryFunc * const pci_host_config_write[] = { - &pci_host_config_writel, - &pci_host_config_writel, - &pci_host_config_writel, -}; - -static CPUReadMemoryFunc * const pci_host_config_read[] = { - &pci_host_config_readl, - &pci_host_config_readl, - &pci_host_config_readl, -}; - -int pci_host_conf_register_mmio(PCIHostState *s) +static void pci_host_data_write(ReadWriteHandler *handler, + pcibus_t addr, uint32_t val, int len) { - return cpu_register_io_memory(pci_host_config_read, - pci_host_config_write, s); + PCIHostState *s = container_of(handler, PCIHostState, data_handler); + PCI_DPRINTF("write addr %" FMT_PCIBUS " len %d val %x\n", + addr, len, val); + if (s->config_reg & (1u << 31)) + pci_data_write(s->bus, s->config_reg | (addr & 3), val, len); } -static void pci_host_config_writel_noswap(void *opaque, - target_phys_addr_t addr, - uint32_t val) +static uint32_t pci_host_data_read(ReadWriteHandler *handler, + pcibus_t addr, int len) { - PCIHostState *s = opaque; - - PCI_DPRINTF("%s addr " TARGET_FMT_plx " val %"PRIx32"\n", - __func__, addr, val); - s->config_reg = val; -} - -static uint32_t pci_host_config_readl_noswap(void *opaque, - target_phys_addr_t addr) -{ - PCIHostState *s = opaque; - uint32_t val = s->config_reg; - - PCI_DPRINTF("%s addr " TARGET_FMT_plx " val %"PRIx32"\n", - __func__, addr, val); + PCIHostState *s = container_of(handler, PCIHostState, data_handler); + uint32_t val; + if (!(s->config_reg & (1 << 31))) + return 0xffffffff; + val = pci_data_read(s->bus, s->config_reg | (addr & 3), len); + PCI_DPRINTF("read addr %" FMT_PCIBUS " len %d val %x\n", + addr, len, val); return val; } -static CPUWriteMemoryFunc * const pci_host_config_write_noswap[] = { - &pci_host_config_writel_noswap, - &pci_host_config_writel_noswap, - &pci_host_config_writel_noswap, -}; - -static CPUReadMemoryFunc * const pci_host_config_read_noswap[] = { - &pci_host_config_readl_noswap, - &pci_host_config_readl_noswap, - &pci_host_config_readl_noswap, -}; - -int pci_host_conf_register_mmio_noswap(PCIHostState *s) +static void pci_host_init(PCIHostState *s) { - return cpu_register_io_memory(pci_host_config_read_noswap, - pci_host_config_write_noswap, s); + s->conf_handler.write = pci_host_config_write; + s->conf_handler.read = pci_host_config_read; + s->data_handler.write = pci_host_data_write; + s->data_handler.read = pci_host_data_read; } -static void pci_host_config_writel_ioport(void *opaque, - uint32_t addr, uint32_t val) +int pci_host_conf_register_mmio(PCIHostState *s, int endian) { - PCIHostState *s = opaque; - - PCI_DPRINTF("%s addr %"PRIx32 " val %"PRIx32"\n", __func__, addr, val); - s->config_reg = val; -} - -static uint32_t pci_host_config_readl_ioport(void *opaque, uint32_t addr) -{ - PCIHostState *s = opaque; - uint32_t val = s->config_reg; - - PCI_DPRINTF("%s addr %"PRIx32" val %"PRIx32"\n", __func__, addr, val); - return val; + pci_host_init(s); + return cpu_register_io_memory_simple(&s->conf_handler, endian); } void pci_host_conf_register_ioport(pio_addr_t ioport, PCIHostState *s) { - register_ioport_write(ioport, 4, 4, pci_host_config_writel_ioport, s); - register_ioport_read(ioport, 4, 4, pci_host_config_readl_ioport, s); + pci_host_init(s); + register_ioport_simple(&s->conf_handler, ioport, 4, 4); + sysbus_init_ioports(&s->busdev, ioport, 4); } -#define PCI_ADDR_T target_phys_addr_t -#define PCI_HOST_SUFFIX _mmio - -#include "pci_host_template.h" - -static CPUWriteMemoryFunc * const pci_host_data_write_mmio[] = { - pci_host_data_writeb_mmio, - pci_host_data_writew_mmio, - pci_host_data_writel_mmio, -}; - -static CPUReadMemoryFunc * const pci_host_data_read_mmio[] = { - pci_host_data_readb_mmio, - pci_host_data_readw_mmio, - pci_host_data_readl_mmio, -}; - -int pci_host_data_register_mmio(PCIHostState *s) +int pci_host_data_register_mmio(PCIHostState *s, int endian) { - return cpu_register_io_memory(pci_host_data_read_mmio, - pci_host_data_write_mmio, - s); + pci_host_init(s); + return cpu_register_io_memory_simple(&s->data_handler, endian); } -#undef PCI_ADDR_T -#undef PCI_HOST_SUFFIX - -#define PCI_ADDR_T uint32_t -#define PCI_HOST_SUFFIX _ioport - -#include "pci_host_template.h" - void pci_host_data_register_ioport(pio_addr_t ioport, PCIHostState *s) { - register_ioport_write(ioport, 4, 1, pci_host_data_writeb_ioport, s); - register_ioport_write(ioport, 4, 2, pci_host_data_writew_ioport, s); - register_ioport_write(ioport, 4, 4, pci_host_data_writel_ioport, s); - register_ioport_read(ioport, 4, 1, pci_host_data_readb_ioport, s); - register_ioport_read(ioport, 4, 2, pci_host_data_readw_ioport, s); - register_ioport_read(ioport, 4, 4, pci_host_data_readl_ioport, s); + pci_host_init(s); + register_ioport_simple(&s->data_handler, ioport, 4, 1); + register_ioport_simple(&s->data_handler, ioport, 4, 2); + register_ioport_simple(&s->data_handler, ioport, 4, 4); + sysbus_init_ioports(&s->busdev, ioport, 4); } diff -Nru qemu-kvm-0.12.5+noroms/hw/pci_host.h qemu-kvm-0.14.1/hw/pci_host.h --- qemu-kvm-0.12.5+noroms/hw/pci_host.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/pci_host.h 2011-05-11 13:29:46.000000000 +0000 @@ -29,9 +29,12 @@ #define PCI_HOST_H #include "sysbus.h" +#include "rwhandler.h" struct PCIHostState { SysBusDevice busdev; + ReadWriteHandler conf_handler; + ReadWriteHandler data_handler; uint32_t config_reg; PCIBus *bus; }; @@ -40,9 +43,8 @@ uint32_t pci_data_read(PCIBus *s, uint32_t addr, int len); /* for mmio */ -int pci_host_conf_register_mmio(PCIHostState *s); -int pci_host_conf_register_mmio_noswap(PCIHostState *s); -int pci_host_data_register_mmio(PCIHostState *s); +int pci_host_conf_register_mmio(PCIHostState *s, int endian); +int pci_host_data_register_mmio(PCIHostState *s, int endian); /* for ioio */ void pci_host_conf_register_ioport(pio_addr_t ioport, PCIHostState *s); diff -Nru qemu-kvm-0.12.5+noroms/hw/pci_host_template.h qemu-kvm-0.14.1/hw/pci_host_template.h --- qemu-kvm-0.12.5+noroms/hw/pci_host_template.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/pci_host_template.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,109 +0,0 @@ -/* - * QEMU Common PCI Host bridge configuration data space access routines. - * - * Copyright (c) 2006 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. - */ - -/* Worker routines for a PCI host controller that uses an {address,data} - register pair to access PCI configuration space. */ - -static void glue(pci_host_data_writeb, PCI_HOST_SUFFIX)( - void* opaque, PCI_ADDR_T addr, uint32_t val) -{ - PCIHostState *s = opaque; - - PCI_DPRINTF("writeb addr " TARGET_FMT_plx " val %x\n", - (target_phys_addr_t)addr, val); - if (s->config_reg & (1u << 31)) - pci_data_write(s->bus, s->config_reg | (addr & 3), val, 1); -} - -static void glue(pci_host_data_writew, PCI_HOST_SUFFIX)( - void* opaque, PCI_ADDR_T addr, uint32_t val) -{ - PCIHostState *s = opaque; -#ifdef TARGET_WORDS_BIGENDIAN - val = bswap16(val); -#endif - PCI_DPRINTF("writew addr " TARGET_FMT_plx " val %x\n", - (target_phys_addr_t)addr, val); - if (s->config_reg & (1u << 31)) - pci_data_write(s->bus, s->config_reg | (addr & 3), val, 2); -} - -static void glue(pci_host_data_writel, PCI_HOST_SUFFIX)( - void* opaque, PCI_ADDR_T addr, uint32_t val) -{ - PCIHostState *s = opaque; -#ifdef TARGET_WORDS_BIGENDIAN - val = bswap32(val); -#endif - PCI_DPRINTF("writel addr " TARGET_FMT_plx " val %x\n", - (target_phys_addr_t)addr, val); - if (s->config_reg & (1u << 31)) - pci_data_write(s->bus, s->config_reg, val, 4); -} - -static uint32_t glue(pci_host_data_readb, PCI_HOST_SUFFIX)( - void* opaque, PCI_ADDR_T addr) -{ - PCIHostState *s = opaque; - uint32_t val; - - if (!(s->config_reg & (1 << 31))) - return 0xff; - val = pci_data_read(s->bus, s->config_reg | (addr & 3), 1); - PCI_DPRINTF("readb addr " TARGET_FMT_plx " val %x\n", - (target_phys_addr_t)addr, val); - return val; -} - -static uint32_t glue(pci_host_data_readw, PCI_HOST_SUFFIX)( - void* opaque, PCI_ADDR_T addr) -{ - PCIHostState *s = opaque; - uint32_t val; - if (!(s->config_reg & (1 << 31))) - return 0xffff; - val = pci_data_read(s->bus, s->config_reg | (addr & 3), 2); - PCI_DPRINTF("readw addr " TARGET_FMT_plx " val %x\n", - (target_phys_addr_t)addr, val); -#ifdef TARGET_WORDS_BIGENDIAN - val = bswap16(val); -#endif - return val; -} - -static uint32_t glue(pci_host_data_readl, PCI_HOST_SUFFIX)( - void* opaque, PCI_ADDR_T addr) -{ - PCIHostState *s = opaque; - uint32_t val; - if (!(s->config_reg & (1 << 31))) - return 0xffffffff; - val = pci_data_read(s->bus, s->config_reg | (addr & 3), 4); - PCI_DPRINTF("readl addr " TARGET_FMT_plx " val %x\n", - (target_phys_addr_t)addr, val); -#ifdef TARGET_WORDS_BIGENDIAN - val = bswap32(val); -#endif - return val; -} diff -Nru qemu-kvm-0.12.5+noroms/hw/pci-hotplug.c qemu-kvm-0.14.1/hw/pci-hotplug.c --- qemu-kvm-0.12.5+noroms/hw/pci-hotplug.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/pci-hotplug.c 2011-05-11 13:29:46.000000000 +0000 @@ -26,15 +26,13 @@ #include "boards.h" #include "pci.h" #include "net.h" -#include "sysemu.h" #include "pc.h" #include "monitor.h" -#include "block_int.h" #include "scsi.h" #include "virtio-blk.h" #include "qemu-config.h" -#include "qemu-objects.h" #include "device-assignment.h" +#include "blockdev.h" #if defined(TARGET_I386) static PCIDevice *qemu_pci_hot_add_nic(Monitor *mon, @@ -55,10 +53,8 @@ return NULL; } - opts = qemu_opts_parse(&qemu_net_opts, opts_str ? opts_str : "", NULL); + opts = qemu_opts_parse(qemu_find_opts("net"), opts_str ? opts_str : "", 0); if (!opts) { - monitor_printf(mon, "parsing network options '%s' failed\n", - opts_str ? opts_str : ""); return NULL; } @@ -74,14 +70,15 @@ return pci_nic_init(&nd_table[ret], "rtl8139", devaddr); } -static int scsi_hot_add(DeviceState *adapter, DriveInfo *dinfo, int printinfo) +static int scsi_hot_add(Monitor *mon, DeviceState *adapter, + DriveInfo *dinfo, int printinfo) { SCSIBus *scsibus; SCSIDevice *scsidev; scsibus = DO_UPCAST(SCSIBus, qbus, QLIST_FIRST(&adapter->child_bus)); if (!scsibus || strcmp(scsibus->qbus.info->name, "SCSI") != 0) { - qemu_error("Device is not a SCSI adapter\n"); + error_report("Device is not a SCSI adapter"); return -1; } @@ -94,11 +91,16 @@ * specified). */ dinfo->unit = qemu_opt_get_number(dinfo->opts, "unit", -1); - scsidev = scsi_bus_legacy_add_drive(scsibus, dinfo, dinfo->unit); + dinfo->bus = scsibus->busnr; + scsidev = scsi_bus_legacy_add_drive(scsibus, dinfo->bdrv, dinfo->unit, false); + if (!scsidev) { + return -1; + } dinfo->unit = scsidev->id; if (printinfo) - qemu_error("OK bus %d, unit %d\n", scsibus->busnr, scsidev->id); + monitor_printf(mon, "OK bus %d, unit %d\n", + scsibus->busnr, scsidev->id); return 0; } @@ -106,7 +108,7 @@ { int dom, pci_bus; unsigned slot; - int type, bus; + int type; PCIDevice *dev; DriveInfo *dinfo = NULL; const char *pci_addr = qdict_get_str(qdict, "pci_addr"); @@ -120,19 +122,18 @@ goto err; } type = dinfo->type; - bus = drive_get_max_bus (type); switch (type) { case IF_SCSI: if (pci_read_devaddr(mon, pci_addr, &dom, &pci_bus, &slot)) { goto err; } - dev = pci_find_device(pci_find_root_bus(0), pci_bus, slot, 0); + dev = pci_find_device(pci_find_root_bus(dom), pci_bus, slot, 0); if (!dev) { monitor_printf(mon, "no pci device with address %s\n", pci_addr); goto err; } - if (scsi_hot_add(&dev->qdev, dinfo, 1) != 0) { + if (scsi_hot_add(mon, &dev->qdev, dinfo, 1) != 0) { goto err; } break; @@ -147,7 +148,7 @@ err: if (dinfo) - drive_uninit(dinfo); + drive_put_ref(dinfo); return; } @@ -204,7 +205,7 @@ if (qdev_init(&dev->qdev) < 0) dev = NULL; if (dev && dinfo) { - if (scsi_hot_add(&dev->qdev, dinfo, 0) != 0) { + if (scsi_hot_add(mon, &dev->qdev, dinfo, 0) != 0) { qdev_unplug(&dev->qdev); dev = NULL; } @@ -216,7 +217,11 @@ return NULL; } dev = pci_create(bus, devfn, "virtio-blk-pci"); - qdev_prop_set_drive(&dev->qdev, "drive", dinfo); + if (qdev_prop_set_drive(&dev->qdev, "drive", dinfo->bdrv) < 0) { + qdev_free(&dev->qdev); + dev = NULL; + break; + } if (qdev_init(&dev->qdev) < 0) dev = NULL; break; @@ -226,54 +231,7 @@ return dev; } -#ifdef CONFIG_KVM_DEVICE_ASSIGNMENT -static PCIDevice *qemu_pci_hot_assign_device(Monitor *mon, - const char *devaddr, - const char *opts_str) -{ - QemuOpts *opts; - DeviceState *dev; - - opts = add_assigned_device(opts_str); - if (opts == NULL) { - monitor_printf(mon, "Error adding device; check syntax\n"); - return NULL; - } - dev = qdev_device_add(opts); - return DO_UPCAST(PCIDevice, qdev, dev); -} -#endif /* CONFIG_KVM_DEVICE_ASSIGNMENT */ - -void pci_device_hot_add_print(Monitor *mon, const QObject *data) -{ - QDict *qdict; - - assert(qobject_type(data) == QTYPE_QDICT); - qdict = qobject_to_qdict(data); - - monitor_printf(mon, "OK domain %d, bus %d, slot %d, function %d\n", - (int) qdict_get_int(qdict, "domain"), - (int) qdict_get_int(qdict, "bus"), - (int) qdict_get_int(qdict, "slot"), - (int) qdict_get_int(qdict, "function")); - -} - -/** - * pci_device_hot_add(): Hot add a PCI device - * - * Return a QDict with the following device information: - * - * - "domain": domain number - * - "bus": bus number - * - "slot": slot number - * - "function": function number - * - * Example: - * - * { "domain": 0, "bus": 0, "slot": 5, "function": 0 } - */ -void pci_device_hot_add(Monitor *mon, const QDict *qdict, QObject **ret_data) +void pci_device_hot_add(Monitor *mon, const QDict *qdict) { PCIDevice *dev = NULL; const char *pci_addr = qdict_get_str(qdict, "pci_addr"); @@ -292,48 +250,43 @@ if (!strcmp(pci_addr, "auto")) pci_addr = NULL; - if (strcmp(type, "nic") == 0) + if (strcmp(type, "nic") == 0) { dev = qemu_pci_hot_add_nic(mon, pci_addr, opts); - else if (strcmp(type, "storage") == 0) + } else if (strcmp(type, "storage") == 0) { dev = qemu_pci_hot_add_storage(mon, pci_addr, opts); -#ifdef CONFIG_KVM_DEVICE_ASSIGNMENT - else if (strcmp(type, "host") == 0) - dev = qemu_pci_hot_assign_device(mon, pci_addr, opts); -#endif /* CONFIG_KVM_DEVICE_ASSIGNMENT */ - else + } else { monitor_printf(mon, "invalid type: %s\n", type); + } if (dev) { - *ret_data = - qobject_from_jsonf("{ 'domain': 0, 'bus': %d, 'slot': %d, " - "'function': %d }", pci_bus_num(dev->bus), - PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); - assert(*ret_data != NULL); + monitor_printf(mon, "OK domain %d, bus %d, slot %d, function %d\n", + pci_find_domain(dev->bus), + pci_bus_num(dev->bus), PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn)); } else monitor_printf(mon, "failed to add %s\n", opts); } #endif -void pci_device_hot_remove(Monitor *mon, const char *pci_addr) +static int pci_device_hot_remove(Monitor *mon, const char *pci_addr) { PCIDevice *d; int dom, bus; unsigned slot; if (pci_read_devaddr(mon, pci_addr, &dom, &bus, &slot)) { - return; + return -1; } - d = pci_find_device(pci_find_root_bus(0), bus, slot, 0); + d = pci_find_device(pci_find_root_bus(dom), bus, slot, 0); if (!d) { monitor_printf(mon, "slot %d empty\n", slot); - return; + return -1; } - qdev_unplug(&d->qdev); + return qdev_unplug(&d->qdev); } -void do_pci_device_hot_remove(Monitor *mon, const QDict *qdict, - QObject **ret_data) +void do_pci_device_hot_remove(Monitor *mon, const QDict *qdict) { pci_device_hot_remove(mon, qdict_get_str(qdict, "pci_addr")); } diff -Nru qemu-kvm-0.12.5+noroms/hw/pci_ids.h qemu-kvm-0.14.1/hw/pci_ids.h --- qemu-kvm-0.12.5+noroms/hw/pci_ids.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/pci_ids.h 2011-05-11 13:29:46.000000000 +0000 @@ -15,6 +15,7 @@ #define PCI_CLASS_STORAGE_SCSI 0x0100 #define PCI_CLASS_STORAGE_IDE 0x0101 +#define PCI_CLASS_STORAGE_SATA 0x0106 #define PCI_CLASS_STORAGE_OTHER 0x0180 #define PCI_CLASS_NETWORK_ETHERNET 0x0200 @@ -57,12 +58,15 @@ #define PCI_VENDOR_ID_AMD 0x1022 #define PCI_DEVICE_ID_AMD_LANCE 0x2000 +#define PCI_VENDOR_ID_TI 0x104c + #define PCI_VENDOR_ID_MOTOROLA 0x1057 #define PCI_DEVICE_ID_MOTOROLA_MPC106 0x0002 #define PCI_DEVICE_ID_MOTOROLA_RAVEN 0x4801 #define PCI_VENDOR_ID_APPLE 0x106b #define PCI_DEVICE_ID_APPLE_UNI_N_AGP 0x0020 +#define PCI_DEVICE_ID_APPLE_U3_AGP 0x004b #define PCI_VENDOR_ID_SUN 0x108e #define PCI_DEVICE_ID_SUN_EBUS 0x1000 @@ -77,6 +81,14 @@ #define PCI_VENDOR_ID_XILINX 0x10ee +#define PCI_VENDOR_ID_VIA 0x1106 +#define PCI_DEVICE_ID_VIA_ISA_BRIDGE 0x0686 +#define PCI_DEVICE_ID_VIA_IDE 0x0571 +#define PCI_DEVICE_ID_VIA_UHCI 0x3038 +#define PCI_DEVICE_ID_VIA_ACPI 0x3057 +#define PCI_DEVICE_ID_VIA_AC97 0x3058 +#define PCI_DEVICE_ID_VIA_MC97 0x3068 + #define PCI_VENDOR_ID_MARVELL 0x11ab #define PCI_VENDOR_ID_ENSONIQ 0x1274 diff -Nru qemu-kvm-0.12.5+noroms/hw/pci_internals.h qemu-kvm-0.14.1/hw/pci_internals.h --- qemu-kvm-0.12.5+noroms/hw/pci_internals.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/pci_internals.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,47 @@ +#ifndef QEMU_PCI_INTERNALS_H +#define QEMU_PCI_INTERNALS_H + +/* + * This header files is private to pci.c and pci_bridge.c + * So following structures are opaque to others and shouldn't be + * accessed. + * + * For pci-to-pci bridge needs to include this header file to embed + * PCIBridge in its structure or to get sizeof(PCIBridge), + * However, they shouldn't access those following members directly. + * Use accessor function in pci.h, pci_bridge.h + */ + +extern struct BusInfo pci_bus_info; + +struct PCIBus { + BusState qbus; + int devfn_min; + pci_set_irq_fn set_irq; + pci_map_irq_fn map_irq; + pci_hotplug_fn hotplug; + DeviceState *hotplug_qdev; + void *irq_opaque; + PCIDevice *devices[256]; + PCIDevice *parent_dev; + target_phys_addr_t mem_base; + + QLIST_HEAD(, PCIBus) child; /* this will be replaced by qdev later */ + QLIST_ENTRY(PCIBus) sibling;/* this will be replaced by qdev later */ + + /* The bus IRQ state is the logical OR of the connected devices. + Keep a count of the number of devices with raised IRQs. */ + int nirq; + int *irq_count; +}; + +struct PCIBridge { + PCIDevice dev; + + /* private member */ + PCIBus sec_bus; + pci_map_irq_fn map_irq; + const char *bus_name; +}; + +#endif /* QEMU_PCI_INTERNALS_H */ diff -Nru qemu-kvm-0.12.5+noroms/hw/pci_regs.h qemu-kvm-0.14.1/hw/pci_regs.h --- qemu-kvm-0.12.5+noroms/hw/pci_regs.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/pci_regs.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,672 @@ +/* + * pci_regs.h + * + * PCI standard defines + * Copyright 1994, Drew Eckhardt + * Copyright 1997--1999 Martin Mares + * + * For more information, please consult the following manuals (look at + * http://www.pcisig.com/ for how to get them): + * + * PCI BIOS Specification + * PCI Local Bus Specification + * PCI to PCI Bridge Specification + * PCI System Design Guide + * + * For hypertransport information, please consult the following manuals + * from http://www.hypertransport.org + * + * The Hypertransport I/O Link Specification + */ + +#ifndef LINUX_PCI_REGS_H +#define LINUX_PCI_REGS_H + +/* + * Under PCI, each device has 256 bytes of configuration address space, + * of which the first 64 bytes are standardized as follows: + */ +#define PCI_VENDOR_ID 0x00 /* 16 bits */ +#define PCI_DEVICE_ID 0x02 /* 16 bits */ +#define PCI_COMMAND 0x04 /* 16 bits */ +#define PCI_COMMAND_IO 0x1 /* Enable response in I/O space */ +#define PCI_COMMAND_MEMORY 0x2 /* Enable response in Memory space */ +#define PCI_COMMAND_MASTER 0x4 /* Enable bus mastering */ +#define PCI_COMMAND_SPECIAL 0x8 /* Enable response to special cycles */ +#define PCI_COMMAND_INVALIDATE 0x10 /* Use memory write and invalidate */ +#define PCI_COMMAND_VGA_PALETTE 0x20 /* Enable palette snooping */ +#define PCI_COMMAND_PARITY 0x40 /* Enable parity checking */ +#define PCI_COMMAND_WAIT 0x80 /* Enable address/data stepping */ +#define PCI_COMMAND_SERR 0x100 /* Enable SERR */ +#define PCI_COMMAND_FAST_BACK 0x200 /* Enable back-to-back writes */ +#define PCI_COMMAND_INTX_DISABLE 0x400 /* INTx Emulation Disable */ + +#define PCI_STATUS 0x06 /* 16 bits */ +#define PCI_STATUS_INTERRUPT 0x08 /* Interrupt status */ +#define PCI_STATUS_CAP_LIST 0x10 /* Support Capability List */ + +#ifndef PCI_STATUS_66MHZ +#define PCI_STATUS_66MHZ 0x20 /* Support 66 Mhz PCI 2.1 bus */ +#endif + +#define PCI_STATUS_UDF 0x40 /* Support User Definable Features [obsolete] */ +#ifndef PCI_STATUS_FAST_BACK +#define PCI_STATUS_FAST_BACK 0x80 /* Accept fast-back to back */ +#endif + +#define PCI_STATUS_PARITY 0x100 /* Detected parity error */ +#define PCI_STATUS_DEVSEL_MASK 0x600 /* DEVSEL timing */ +#define PCI_STATUS_DEVSEL_FAST 0x000 +#define PCI_STATUS_DEVSEL_MEDIUM 0x200 +#define PCI_STATUS_DEVSEL_SLOW 0x400 +#define PCI_STATUS_SIG_TARGET_ABORT 0x800 /* Set on target abort */ +#define PCI_STATUS_REC_TARGET_ABORT 0x1000 /* Master ack of " */ +#define PCI_STATUS_REC_MASTER_ABORT 0x2000 /* Set on master abort */ +#define PCI_STATUS_SIG_SYSTEM_ERROR 0x4000 /* Set when we drive SERR */ +#define PCI_STATUS_DETECTED_PARITY 0x8000 /* Set on parity error */ + +#define PCI_CLASS_REVISION 0x08 /* High 24 bits are class, low 8 revision */ +#define PCI_REVISION_ID 0x08 /* Revision ID */ +#define PCI_CLASS_PROG 0x09 /* Reg. Level Programming Interface */ +#define PCI_CLASS_DEVICE 0x0a /* Device class */ + +#define PCI_CACHE_LINE_SIZE 0x0c /* 8 bits */ +#define PCI_LATENCY_TIMER 0x0d /* 8 bits */ +#define PCI_HEADER_TYPE 0x0e /* 8 bits */ +#define PCI_HEADER_TYPE_NORMAL 0 +#define PCI_HEADER_TYPE_BRIDGE 1 +#define PCI_HEADER_TYPE_CARDBUS 2 + +#define PCI_BIST 0x0f /* 8 bits */ +#define PCI_BIST_CODE_MASK 0x0f /* Return result */ +#define PCI_BIST_START 0x40 /* 1 to start BIST, 2 secs or less */ +#define PCI_BIST_CAPABLE 0x80 /* 1 if BIST capable */ + +/* + * Base addresses specify locations in memory or I/O space. + * Decoded size can be determined by writing a value of + * 0xffffffff to the register, and reading it back. Only + * 1 bits are decoded. + */ +#define PCI_BASE_ADDRESS_0 0x10 /* 32 bits */ +#define PCI_BASE_ADDRESS_1 0x14 /* 32 bits [htype 0,1 only] */ +#define PCI_BASE_ADDRESS_2 0x18 /* 32 bits [htype 0 only] */ +#define PCI_BASE_ADDRESS_3 0x1c /* 32 bits */ +#define PCI_BASE_ADDRESS_4 0x20 /* 32 bits */ +#define PCI_BASE_ADDRESS_5 0x24 /* 32 bits */ +#define PCI_BASE_ADDRESS_SPACE 0x01 /* 0 = memory, 1 = I/O */ +#define PCI_BASE_ADDRESS_SPACE_IO 0x01 +#define PCI_BASE_ADDRESS_SPACE_MEMORY 0x00 +#define PCI_BASE_ADDRESS_MEM_TYPE_MASK 0x06 +#define PCI_BASE_ADDRESS_MEM_TYPE_32 0x00 /* 32 bit address */ +#define PCI_BASE_ADDRESS_MEM_TYPE_1M 0x02 /* Below 1M [obsolete] */ +#define PCI_BASE_ADDRESS_MEM_TYPE_64 0x04 /* 64 bit address */ +#define PCI_BASE_ADDRESS_MEM_PREFETCH 0x08 /* prefetchable? */ +#define PCI_BASE_ADDRESS_MEM_MASK (~0x0fUL) +#define PCI_BASE_ADDRESS_IO_MASK (~0x03UL) +/* bit 1 is reserved if address_space = 1 */ + +/* Header type 0 (normal devices) */ +#define PCI_CARDBUS_CIS 0x28 +#define PCI_SUBSYSTEM_VENDOR_ID 0x2c +#define PCI_SUBSYSTEM_ID 0x2e +#define PCI_ROM_ADDRESS 0x30 /* Bits 31..11 are address, 10..1 reserved */ +#define PCI_ROM_ADDRESS_ENABLE 0x01 +#define PCI_ROM_ADDRESS_MASK (~0x7ffUL) + +#define PCI_CAPABILITY_LIST 0x34 /* Offset of first capability list entry */ + +/* 0x35-0x3b are reserved */ +#define PCI_INTERRUPT_LINE 0x3c /* 8 bits */ +#define PCI_INTERRUPT_PIN 0x3d /* 8 bits */ +#define PCI_MIN_GNT 0x3e /* 8 bits */ +#define PCI_MAX_LAT 0x3f /* 8 bits */ + +/* Header type 1 (PCI-to-PCI bridges) */ +#define PCI_PRIMARY_BUS 0x18 /* Primary bus number */ +#define PCI_SECONDARY_BUS 0x19 /* Secondary bus number */ +#define PCI_SUBORDINATE_BUS 0x1a /* Highest bus number behind the bridge */ +#define PCI_SEC_LATENCY_TIMER 0x1b /* Latency timer for secondary interface */ +#define PCI_IO_BASE 0x1c /* I/O range behind the bridge */ +#define PCI_IO_LIMIT 0x1d +#define PCI_IO_RANGE_TYPE_MASK 0x0fUL /* I/O bridging type */ +#define PCI_IO_RANGE_TYPE_16 0x00 +#define PCI_IO_RANGE_TYPE_32 0x01 +#define PCI_IO_RANGE_MASK (~0x0fUL) +#define PCI_SEC_STATUS 0x1e /* Secondary status register, only bit 14 used */ +#define PCI_MEMORY_BASE 0x20 /* Memory range behind */ +#define PCI_MEMORY_LIMIT 0x22 +#define PCI_MEMORY_RANGE_TYPE_MASK 0x0fUL +#define PCI_MEMORY_RANGE_MASK (~0x0fUL) +#define PCI_PREF_MEMORY_BASE 0x24 /* Prefetchable memory range behind */ +#define PCI_PREF_MEMORY_LIMIT 0x26 +#define PCI_PREF_RANGE_TYPE_MASK 0x0fUL +#define PCI_PREF_RANGE_TYPE_32 0x00 +#define PCI_PREF_RANGE_TYPE_64 0x01 +#define PCI_PREF_RANGE_MASK (~0x0fUL) +#define PCI_PREF_BASE_UPPER32 0x28 /* Upper half of prefetchable memory range */ +#define PCI_PREF_LIMIT_UPPER32 0x2c +#define PCI_IO_BASE_UPPER16 0x30 /* Upper half of I/O addresses */ +#define PCI_IO_LIMIT_UPPER16 0x32 +/* 0x34 same as for htype 0 */ +/* 0x35-0x3b is reserved */ +#define PCI_ROM_ADDRESS1 0x38 /* Same as PCI_ROM_ADDRESS, but for htype 1 */ +/* 0x3c-0x3d are same as for htype 0 */ +#define PCI_BRIDGE_CONTROL 0x3e +#define PCI_BRIDGE_CTL_PARITY 0x01 /* Enable parity detection on secondary interface */ +#define PCI_BRIDGE_CTL_SERR 0x02 /* The same for SERR forwarding */ +#define PCI_BRIDGE_CTL_ISA 0x04 /* Enable ISA mode */ +#define PCI_BRIDGE_CTL_VGA 0x08 /* Forward VGA addresses */ +#define PCI_BRIDGE_CTL_MASTER_ABORT 0x20 /* Report master aborts */ +#define PCI_BRIDGE_CTL_BUS_RESET 0x40 /* Secondary bus reset */ +#define PCI_BRIDGE_CTL_FAST_BACK 0x80 /* Fast Back2Back enabled on secondary interface */ + +/* Header type 2 (CardBus bridges) */ +#define PCI_CB_CAPABILITY_LIST 0x14 +/* 0x15 reserved */ +#define PCI_CB_SEC_STATUS 0x16 /* Secondary status */ +#define PCI_CB_PRIMARY_BUS 0x18 /* PCI bus number */ +#define PCI_CB_CARD_BUS 0x19 /* CardBus bus number */ +#define PCI_CB_SUBORDINATE_BUS 0x1a /* Subordinate bus number */ +#define PCI_CB_LATENCY_TIMER 0x1b /* CardBus latency timer */ +#define PCI_CB_MEMORY_BASE_0 0x1c +#define PCI_CB_MEMORY_LIMIT_0 0x20 +#define PCI_CB_MEMORY_BASE_1 0x24 +#define PCI_CB_MEMORY_LIMIT_1 0x28 +#define PCI_CB_IO_BASE_0 0x2c +#define PCI_CB_IO_BASE_0_HI 0x2e +#define PCI_CB_IO_LIMIT_0 0x30 +#define PCI_CB_IO_LIMIT_0_HI 0x32 +#define PCI_CB_IO_BASE_1 0x34 +#define PCI_CB_IO_BASE_1_HI 0x36 +#define PCI_CB_IO_LIMIT_1 0x38 +#define PCI_CB_IO_LIMIT_1_HI 0x3a +#define PCI_CB_IO_RANGE_MASK (~0x03UL) +/* 0x3c-0x3d are same as for htype 0 */ +#define PCI_CB_BRIDGE_CONTROL 0x3e +#define PCI_CB_BRIDGE_CTL_PARITY 0x01 /* Similar to standard bridge control register */ +#define PCI_CB_BRIDGE_CTL_SERR 0x02 +#define PCI_CB_BRIDGE_CTL_ISA 0x04 +#define PCI_CB_BRIDGE_CTL_VGA 0x08 +#define PCI_CB_BRIDGE_CTL_MASTER_ABORT 0x20 +#define PCI_CB_BRIDGE_CTL_CB_RESET 0x40 /* CardBus reset */ +#define PCI_CB_BRIDGE_CTL_16BIT_INT 0x80 /* Enable interrupt for 16-bit cards */ +#define PCI_CB_BRIDGE_CTL_PREFETCH_MEM0 0x100 /* Prefetch enable for both memory regions */ +#define PCI_CB_BRIDGE_CTL_PREFETCH_MEM1 0x200 +#define PCI_CB_BRIDGE_CTL_POST_WRITES 0x400 +#define PCI_CB_SUBSYSTEM_VENDOR_ID 0x40 +#define PCI_CB_SUBSYSTEM_ID 0x42 +#define PCI_CB_LEGACY_MODE_BASE 0x44 /* 16-bit PC Card legacy mode base address (ExCa) */ +/* 0x48-0x7f reserved */ + +/* Capability lists */ + +#define PCI_CAP_LIST_ID 0 /* Capability ID */ +#define PCI_CAP_ID_PM 0x01 /* Power Management */ +#define PCI_CAP_ID_AGP 0x02 /* Accelerated Graphics Port */ +#define PCI_CAP_ID_VPD 0x03 /* Vital Product Data */ +#define PCI_CAP_ID_SLOTID 0x04 /* Slot Identification */ +#define PCI_CAP_ID_MSI 0x05 /* Message Signalled Interrupts */ +#define PCI_CAP_ID_CHSWP 0x06 /* CompactPCI HotSwap */ +#define PCI_CAP_ID_PCIX 0x07 /* PCI-X */ +#define PCI_CAP_ID_HT 0x08 /* HyperTransport */ +#define PCI_CAP_ID_VNDR 0x09 /* Vendor specific */ +#define PCI_CAP_ID_DBG 0x0A /* Debug port */ +#define PCI_CAP_ID_CCRC 0x0B /* CompactPCI Central Resource Control */ +#define PCI_CAP_ID_SHPC 0x0C /* PCI Standard Hot-Plug Controller */ +#define PCI_CAP_ID_SSVID 0x0D /* Bridge subsystem vendor/device ID */ +#define PCI_CAP_ID_AGP3 0x0E /* AGP Target PCI-PCI bridge */ +#define PCI_CAP_ID_EXP 0x10 /* PCI Express */ +#define PCI_CAP_ID_MSIX 0x11 /* MSI-X */ +#define PCI_CAP_ID_AF 0x13 /* PCI Advanced Features */ +#define PCI_CAP_LIST_NEXT 1 /* Next capability in the list */ +#define PCI_CAP_FLAGS 2 /* Capability defined flags (16 bits) */ +#define PCI_CAP_SIZEOF 4 + +/* Power Management Registers */ + +#define PCI_PM_PMC 2 /* PM Capabilities Register */ +#define PCI_PM_CAP_VER_MASK 0x0007 /* Version */ +#define PCI_PM_CAP_PME_CLOCK 0x0008 /* PME clock required */ +#define PCI_PM_CAP_RESERVED 0x0010 /* Reserved field */ +#define PCI_PM_CAP_DSI 0x0020 /* Device specific initialization */ +#define PCI_PM_CAP_AUX_POWER 0x01C0 /* Auxilliary power support mask */ +#define PCI_PM_CAP_D1 0x0200 /* D1 power state support */ +#define PCI_PM_CAP_D2 0x0400 /* D2 power state support */ +#define PCI_PM_CAP_PME 0x0800 /* PME pin supported */ +#define PCI_PM_CAP_PME_MASK 0xF800 /* PME Mask of all supported states */ +#define PCI_PM_CAP_PME_D0 0x0800 /* PME# from D0 */ +#define PCI_PM_CAP_PME_D1 0x1000 /* PME# from D1 */ +#define PCI_PM_CAP_PME_D2 0x2000 /* PME# from D2 */ +#define PCI_PM_CAP_PME_D3 0x4000 /* PME# from D3 (hot) */ +#define PCI_PM_CAP_PME_D3cold 0x8000 /* PME# from D3 (cold) */ +#define PCI_PM_CAP_PME_SHIFT 11 /* Start of the PME Mask in PMC */ +#define PCI_PM_CTRL 4 /* PM control and status register */ +#define PCI_PM_CTRL_STATE_MASK 0x0003 /* Current power state (D0 to D3) */ +#define PCI_PM_CTRL_NO_SOFT_RESET 0x0008 /* No reset for D3hot->D0 */ +#define PCI_PM_CTRL_PME_ENABLE 0x0100 /* PME pin enable */ +#define PCI_PM_CTRL_DATA_SEL_MASK 0x1e00 /* Data select (??) */ +#define PCI_PM_CTRL_DATA_SCALE_MASK 0x6000 /* Data scale (??) */ +#define PCI_PM_CTRL_PME_STATUS 0x8000 /* PME pin status */ +#define PCI_PM_PPB_EXTENSIONS 6 /* PPB support extensions (??) */ +#define PCI_PM_PPB_B2_B3 0x40 /* Stop clock when in D3hot (??) */ +#define PCI_PM_BPCC_ENABLE 0x80 /* Bus power/clock control enable (??) */ +#define PCI_PM_DATA_REGISTER 7 /* (??) */ +#define PCI_PM_SIZEOF 8 + +/* AGP registers */ + +#define PCI_AGP_VERSION 2 /* BCD version number */ +#define PCI_AGP_RFU 3 /* Rest of capability flags */ +#define PCI_AGP_STATUS 4 /* Status register */ +#define PCI_AGP_STATUS_RQ_MASK 0xff000000 /* Maximum number of requests - 1 */ +#define PCI_AGP_STATUS_SBA 0x0200 /* Sideband addressing supported */ +#define PCI_AGP_STATUS_64BIT 0x0020 /* 64-bit addressing supported */ +#define PCI_AGP_STATUS_FW 0x0010 /* FW transfers supported */ +#define PCI_AGP_STATUS_RATE4 0x0004 /* 4x transfer rate supported */ +#define PCI_AGP_STATUS_RATE2 0x0002 /* 2x transfer rate supported */ +#define PCI_AGP_STATUS_RATE1 0x0001 /* 1x transfer rate supported */ +#define PCI_AGP_COMMAND 8 /* Control register */ +#define PCI_AGP_COMMAND_RQ_MASK 0xff000000 /* Master: Maximum number of requests */ +#define PCI_AGP_COMMAND_SBA 0x0200 /* Sideband addressing enabled */ +#define PCI_AGP_COMMAND_AGP 0x0100 /* Allow processing of AGP transactions */ +#define PCI_AGP_COMMAND_64BIT 0x0020 /* Allow processing of 64-bit addresses */ +#define PCI_AGP_COMMAND_FW 0x0010 /* Force FW transfers */ +#define PCI_AGP_COMMAND_RATE4 0x0004 /* Use 4x rate */ +#define PCI_AGP_COMMAND_RATE2 0x0002 /* Use 2x rate */ +#define PCI_AGP_COMMAND_RATE1 0x0001 /* Use 1x rate */ +#define PCI_AGP_SIZEOF 12 + +/* Vital Product Data */ + +#define PCI_VPD_ADDR 2 /* Address to access (15 bits!) */ +#define PCI_VPD_ADDR_MASK 0x7fff /* Address mask */ +#define PCI_VPD_ADDR_F 0x8000 /* Write 0, 1 indicates completion */ +#define PCI_VPD_DATA 4 /* 32-bits of data returned here */ + +/* Slot Identification */ + +#define PCI_SID_ESR 2 /* Expansion Slot Register */ +#define PCI_SID_ESR_NSLOTS 0x1f /* Number of expansion slots available */ +#define PCI_SID_ESR_FIC 0x20 /* First In Chassis Flag */ +#define PCI_SID_CHASSIS_NR 3 /* Chassis Number */ + +/* Message Signalled Interrupts registers */ + +#define PCI_MSI_FLAGS 2 /* Various flags */ +#define PCI_MSI_FLAGS_64BIT 0x80 /* 64-bit addresses allowed */ +#define PCI_MSI_FLAGS_QSIZE 0x70 /* Message queue size configured */ +#define PCI_MSI_FLAGS_QMASK 0x0e /* Maximum queue size available */ +#define PCI_MSI_FLAGS_ENABLE 0x01 /* MSI feature enabled */ +#define PCI_MSI_FLAGS_MASKBIT 0x100 /* 64-bit mask bits allowed */ +#define PCI_MSI_RFU 3 /* Rest of capability flags */ +#define PCI_MSI_ADDRESS_LO 4 /* Lower 32 bits */ +#define PCI_MSI_ADDRESS_HI 8 /* Upper 32 bits (if PCI_MSI_FLAGS_64BIT set) */ +#define PCI_MSI_DATA_32 8 /* 16 bits of data for 32-bit devices */ +#define PCI_MSI_MASK_32 12 /* Mask bits register for 32-bit devices */ +#define PCI_MSI_DATA_64 12 /* 16 bits of data for 64-bit devices */ +#define PCI_MSI_MASK_64 16 /* Mask bits register for 64-bit devices */ + +/* MSI-X registers (these are at offset PCI_MSIX_FLAGS) */ +#define PCI_MSIX_FLAGS 2 +#define PCI_MSIX_FLAGS_QSIZE 0x7FF +#define PCI_MSIX_FLAGS_ENABLE (1 << 15) +#define PCI_MSIX_FLAGS_MASKALL (1 << 14) +#define PCI_MSIX_FLAGS_BIRMASK (7 << 0) + +/* CompactPCI Hotswap Register */ + +#define PCI_CHSWP_CSR 2 /* Control and Status Register */ +#define PCI_CHSWP_DHA 0x01 /* Device Hiding Arm */ +#define PCI_CHSWP_EIM 0x02 /* ENUM# Signal Mask */ +#define PCI_CHSWP_PIE 0x04 /* Pending Insert or Extract */ +#define PCI_CHSWP_LOO 0x08 /* LED On / Off */ +#define PCI_CHSWP_PI 0x30 /* Programming Interface */ +#define PCI_CHSWP_EXT 0x40 /* ENUM# status - extraction */ +#define PCI_CHSWP_INS 0x80 /* ENUM# status - insertion */ + +/* PCI Advanced Feature registers */ + +#define PCI_AF_LENGTH 2 +#define PCI_AF_CAP 3 +#define PCI_AF_CAP_TP 0x01 +#define PCI_AF_CAP_FLR 0x02 +#define PCI_AF_CTRL 4 +#define PCI_AF_CTRL_FLR 0x01 +#define PCI_AF_STATUS 5 +#define PCI_AF_STATUS_TP 0x01 + +/* PCI-X registers */ + +#define PCI_X_CMD 2 /* Modes & Features */ +#define PCI_X_CMD_DPERR_E 0x0001 /* Data Parity Error Recovery Enable */ +#define PCI_X_CMD_ERO 0x0002 /* Enable Relaxed Ordering */ +#define PCI_X_CMD_READ_512 0x0000 /* 512 byte maximum read byte count */ +#define PCI_X_CMD_READ_1K 0x0004 /* 1Kbyte maximum read byte count */ +#define PCI_X_CMD_READ_2K 0x0008 /* 2Kbyte maximum read byte count */ +#define PCI_X_CMD_READ_4K 0x000c /* 4Kbyte maximum read byte count */ +#define PCI_X_CMD_MAX_READ 0x000c /* Max Memory Read Byte Count */ + /* Max # of outstanding split transactions */ +#define PCI_X_CMD_SPLIT_1 0x0000 /* Max 1 */ +#define PCI_X_CMD_SPLIT_2 0x0010 /* Max 2 */ +#define PCI_X_CMD_SPLIT_3 0x0020 /* Max 3 */ +#define PCI_X_CMD_SPLIT_4 0x0030 /* Max 4 */ +#define PCI_X_CMD_SPLIT_8 0x0040 /* Max 8 */ +#define PCI_X_CMD_SPLIT_12 0x0050 /* Max 12 */ +#define PCI_X_CMD_SPLIT_16 0x0060 /* Max 16 */ +#define PCI_X_CMD_SPLIT_32 0x0070 /* Max 32 */ +#define PCI_X_CMD_MAX_SPLIT 0x0070 /* Max Outstanding Split Transactions */ +#define PCI_X_CMD_VERSION(x) (((x) >> 12) & 3) /* Version */ +#define PCI_X_STATUS 4 /* PCI-X capabilities */ +#define PCI_X_STATUS_DEVFN 0x000000ff /* A copy of devfn */ +#define PCI_X_STATUS_BUS 0x0000ff00 /* A copy of bus nr */ +#define PCI_X_STATUS_64BIT 0x00010000 /* 64-bit device */ +#define PCI_X_STATUS_133MHZ 0x00020000 /* 133 MHz capable */ +#define PCI_X_STATUS_SPL_DISC 0x00040000 /* Split Completion Discarded */ +#define PCI_X_STATUS_UNX_SPL 0x00080000 /* Unexpected Split Completion */ +#define PCI_X_STATUS_COMPLEX 0x00100000 /* Device Complexity */ +#define PCI_X_STATUS_MAX_READ 0x00600000 /* Designed Max Memory Read Count */ +#define PCI_X_STATUS_MAX_SPLIT 0x03800000 /* Designed Max Outstanding Split Transactions */ +#define PCI_X_STATUS_MAX_CUM 0x1c000000 /* Designed Max Cumulative Read Size */ +#define PCI_X_STATUS_SPL_ERR 0x20000000 /* Rcvd Split Completion Error Msg */ +#define PCI_X_STATUS_266MHZ 0x40000000 /* 266 MHz capable */ +#define PCI_X_STATUS_533MHZ 0x80000000 /* 533 MHz capable */ + +/* PCI Express capability registers */ + +#define PCI_EXP_FLAGS 2 /* Capabilities register */ +#define PCI_EXP_FLAGS_VERS 0x000f /* Capability version */ +#define PCI_EXP_FLAGS_TYPE 0x00f0 /* Device/Port type */ +#define PCI_EXP_TYPE_ENDPOINT 0x0 /* Express Endpoint */ +#define PCI_EXP_TYPE_LEG_END 0x1 /* Legacy Endpoint */ +#define PCI_EXP_TYPE_ROOT_PORT 0x4 /* Root Port */ +#define PCI_EXP_TYPE_UPSTREAM 0x5 /* Upstream Port */ +#define PCI_EXP_TYPE_DOWNSTREAM 0x6 /* Downstream Port */ +#define PCI_EXP_TYPE_PCI_BRIDGE 0x7 /* PCI/PCI-X Bridge */ +#define PCI_EXP_TYPE_RC_END 0x9 /* Root Complex Integrated Endpoint */ +#define PCI_EXP_TYPE_RC_EC 0x10 /* Root Complex Event Collector */ +#define PCI_EXP_FLAGS_SLOT 0x0100 /* Slot implemented */ +#define PCI_EXP_FLAGS_IRQ 0x3e00 /* Interrupt message number */ +#define PCI_EXP_DEVCAP 4 /* Device capabilities */ +#define PCI_EXP_DEVCAP_PAYLOAD 0x07 /* Max_Payload_Size */ +#define PCI_EXP_DEVCAP_PHANTOM 0x18 /* Phantom functions */ +#define PCI_EXP_DEVCAP_EXT_TAG 0x20 /* Extended tags */ +#define PCI_EXP_DEVCAP_L0S 0x1c0 /* L0s Acceptable Latency */ +#define PCI_EXP_DEVCAP_L1 0xe00 /* L1 Acceptable Latency */ +#define PCI_EXP_DEVCAP_ATN_BUT 0x1000 /* Attention Button Present */ +#define PCI_EXP_DEVCAP_ATN_IND 0x2000 /* Attention Indicator Present */ +#define PCI_EXP_DEVCAP_PWR_IND 0x4000 /* Power Indicator Present */ +#define PCI_EXP_DEVCAP_RBER 0x8000 /* Role-Based Error Reporting */ +#define PCI_EXP_DEVCAP_PWR_VAL 0x3fc0000 /* Slot Power Limit Value */ +#define PCI_EXP_DEVCAP_PWR_SCL 0xc000000 /* Slot Power Limit Scale */ +#define PCI_EXP_DEVCAP_FLR 0x10000000 /* Function Level Reset */ +#define PCI_EXP_DEVCTL 8 /* Device Control */ +#define PCI_EXP_DEVCTL_CERE 0x0001 /* Correctable Error Reporting En. */ +#define PCI_EXP_DEVCTL_NFERE 0x0002 /* Non-Fatal Error Reporting Enable */ +#define PCI_EXP_DEVCTL_FERE 0x0004 /* Fatal Error Reporting Enable */ +#define PCI_EXP_DEVCTL_URRE 0x0008 /* Unsupported Request Reporting En. */ +#define PCI_EXP_DEVCTL_RELAX_EN 0x0010 /* Enable relaxed ordering */ +#define PCI_EXP_DEVCTL_PAYLOAD 0x00e0 /* Max_Payload_Size */ +#define PCI_EXP_DEVCTL_EXT_TAG 0x0100 /* Extended Tag Field Enable */ +#define PCI_EXP_DEVCTL_PHANTOM 0x0200 /* Phantom Functions Enable */ +#define PCI_EXP_DEVCTL_AUX_PME 0x0400 /* Auxiliary Power PM Enable */ +#define PCI_EXP_DEVCTL_NOSNOOP_EN 0x0800 /* Enable No Snoop */ +#define PCI_EXP_DEVCTL_READRQ 0x7000 /* Max_Read_Request_Size */ +#define PCI_EXP_DEVCTL_BCR_FLR 0x8000 /* Bridge Configuration Retry / FLR */ +#define PCI_EXP_DEVSTA 10 /* Device Status */ +#define PCI_EXP_DEVSTA_CED 0x01 /* Correctable Error Detected */ +#define PCI_EXP_DEVSTA_NFED 0x02 /* Non-Fatal Error Detected */ +#define PCI_EXP_DEVSTA_FED 0x04 /* Fatal Error Detected */ +#define PCI_EXP_DEVSTA_URD 0x08 /* Unsupported Request Detected */ +#define PCI_EXP_DEVSTA_AUXPD 0x10 /* AUX Power Detected */ +#define PCI_EXP_DEVSTA_TRPND 0x20 /* Transactions Pending */ +#define PCI_EXP_LNKCAP 12 /* Link Capabilities */ +#define PCI_EXP_LNKCAP_SLS 0x0000000f /* Supported Link Speeds */ +#define PCI_EXP_LNKCAP_MLW 0x000003f0 /* Maximum Link Width */ +#define PCI_EXP_LNKCAP_ASPMS 0x00000c00 /* ASPM Support */ +#define PCI_EXP_LNKCAP_L0SEL 0x00007000 /* L0s Exit Latency */ +#define PCI_EXP_LNKCAP_L1EL 0x00038000 /* L1 Exit Latency */ +#define PCI_EXP_LNKCAP_CLKPM 0x00040000 /* L1 Clock Power Management */ +#define PCI_EXP_LNKCAP_SDERC 0x00080000 /* Suprise Down Error Reporting Capable */ +#define PCI_EXP_LNKCAP_DLLLARC 0x00100000 /* Data Link Layer Link Active Reporting Capable */ +#define PCI_EXP_LNKCAP_LBNC 0x00200000 /* Link Bandwidth Notification Capability */ +#define PCI_EXP_LNKCAP_PN 0xff000000 /* Port Number */ +#define PCI_EXP_LNKCTL 16 /* Link Control */ +#define PCI_EXP_LNKCTL_ASPMC 0x0003 /* ASPM Control */ +#define PCI_EXP_LNKCTL_RCB 0x0008 /* Read Completion Boundary */ +#define PCI_EXP_LNKCTL_LD 0x0010 /* Link Disable */ +#define PCI_EXP_LNKCTL_RL 0x0020 /* Retrain Link */ +#define PCI_EXP_LNKCTL_CCC 0x0040 /* Common Clock Configuration */ +#define PCI_EXP_LNKCTL_ES 0x0080 /* Extended Synch */ +#define PCI_EXP_LNKCTL_CLKREQ_EN 0x100 /* Enable clkreq */ +#define PCI_EXP_LNKCTL_HAWD 0x0200 /* Hardware Autonomous Width Disable */ +#define PCI_EXP_LNKCTL_LBMIE 0x0400 /* Link Bandwidth Management Interrupt Enable */ +#define PCI_EXP_LNKCTL_LABIE 0x0800 /* Lnk Autonomous Bandwidth Interrupt Enable */ +#define PCI_EXP_LNKSTA 18 /* Link Status */ +#define PCI_EXP_LNKSTA_CLS 0x000f /* Current Link Speed */ +#define PCI_EXP_LNKSTA_NLW 0x03f0 /* Nogotiated Link Width */ +#define PCI_EXP_LNKSTA_LT 0x0800 /* Link Training */ +#define PCI_EXP_LNKSTA_SLC 0x1000 /* Slot Clock Configuration */ +#define PCI_EXP_LNKSTA_DLLLA 0x2000 /* Data Link Layer Link Active */ +#define PCI_EXP_LNKSTA_LBMS 0x4000 /* Link Bandwidth Management Status */ +#define PCI_EXP_LNKSTA_LABS 0x8000 /* Link Autonomous Bandwidth Status */ +#define PCI_EXP_SLTCAP 20 /* Slot Capabilities */ +#define PCI_EXP_SLTCAP_ABP 0x00000001 /* Attention Button Present */ +#define PCI_EXP_SLTCAP_PCP 0x00000002 /* Power Controller Present */ +#define PCI_EXP_SLTCAP_MRLSP 0x00000004 /* MRL Sensor Present */ +#define PCI_EXP_SLTCAP_AIP 0x00000008 /* Attention Indicator Present */ +#define PCI_EXP_SLTCAP_PIP 0x00000010 /* Power Indicator Present */ +#define PCI_EXP_SLTCAP_HPS 0x00000020 /* Hot-Plug Surprise */ +#define PCI_EXP_SLTCAP_HPC 0x00000040 /* Hot-Plug Capable */ +#define PCI_EXP_SLTCAP_SPLV 0x00007f80 /* Slot Power Limit Value */ +#define PCI_EXP_SLTCAP_SPLS 0x00018000 /* Slot Power Limit Scale */ +#define PCI_EXP_SLTCAP_EIP 0x00020000 /* Electromechanical Interlock Present */ +#define PCI_EXP_SLTCAP_NCCS 0x00040000 /* No Command Completed Support */ +#define PCI_EXP_SLTCAP_PSN 0xfff80000 /* Physical Slot Number */ +#define PCI_EXP_SLTCTL 24 /* Slot Control */ +#define PCI_EXP_SLTCTL_ABPE 0x0001 /* Attention Button Pressed Enable */ +#define PCI_EXP_SLTCTL_PFDE 0x0002 /* Power Fault Detected Enable */ +#define PCI_EXP_SLTCTL_MRLSCE 0x0004 /* MRL Sensor Changed Enable */ +#define PCI_EXP_SLTCTL_PDCE 0x0008 /* Presence Detect Changed Enable */ +#define PCI_EXP_SLTCTL_CCIE 0x0010 /* Command Completed Interrupt Enable */ +#define PCI_EXP_SLTCTL_HPIE 0x0020 /* Hot-Plug Interrupt Enable */ +#define PCI_EXP_SLTCTL_AIC 0x00c0 /* Attention Indicator Control */ +#define PCI_EXP_SLTCTL_PIC 0x0300 /* Power Indicator Control */ +#define PCI_EXP_SLTCTL_PCC 0x0400 /* Power Controller Control */ +#define PCI_EXP_SLTCTL_EIC 0x0800 /* Electromechanical Interlock Control */ +#define PCI_EXP_SLTCTL_DLLSCE 0x1000 /* Data Link Layer State Changed Enable */ +#define PCI_EXP_SLTSTA 26 /* Slot Status */ +#define PCI_EXP_SLTSTA_ABP 0x0001 /* Attention Button Pressed */ +#define PCI_EXP_SLTSTA_PFD 0x0002 /* Power Fault Detected */ +#define PCI_EXP_SLTSTA_MRLSC 0x0004 /* MRL Sensor Changed */ +#define PCI_EXP_SLTSTA_PDC 0x0008 /* Presence Detect Changed */ +#define PCI_EXP_SLTSTA_CC 0x0010 /* Command Completed */ +#define PCI_EXP_SLTSTA_MRLSS 0x0020 /* MRL Sensor State */ +#define PCI_EXP_SLTSTA_PDS 0x0040 /* Presence Detect State */ +#define PCI_EXP_SLTSTA_EIS 0x0080 /* Electromechanical Interlock Status */ +#define PCI_EXP_SLTSTA_DLLSC 0x0100 /* Data Link Layer State Changed */ +#define PCI_EXP_RTCTL 28 /* Root Control */ +#define PCI_EXP_RTCTL_SECEE 0x01 /* System Error on Correctable Error */ +#define PCI_EXP_RTCTL_SENFEE 0x02 /* System Error on Non-Fatal Error */ +#define PCI_EXP_RTCTL_SEFEE 0x04 /* System Error on Fatal Error */ +#define PCI_EXP_RTCTL_PMEIE 0x08 /* PME Interrupt Enable */ +#define PCI_EXP_RTCTL_CRSSVE 0x10 /* CRS Software Visibility Enable */ +#define PCI_EXP_RTCAP 30 /* Root Capabilities */ +#define PCI_EXP_RTSTA 32 /* Root Status */ +#define PCI_EXP_DEVCAP2 36 /* Device Capabilities 2 */ +#define PCI_EXP_DEVCAP2_ARI 0x20 /* Alternative Routing-ID */ +#define PCI_EXP_DEVCTL2 40 /* Device Control 2 */ +#define PCI_EXP_DEVCTL2_ARI 0x20 /* Alternative Routing-ID */ +#define PCI_EXP_LNKCTL2 48 /* Link Control 2 */ +#define PCI_EXP_SLTCTL2 56 /* Slot Control 2 */ + +/* Extended Capabilities (PCI-X 2.0 and Express) */ +#define PCI_EXT_CAP_ID(header) (header & 0x0000ffff) +#define PCI_EXT_CAP_VER(header) ((header >> 16) & 0xf) +#define PCI_EXT_CAP_NEXT(header) ((header >> 20) & 0xffc) + +#define PCI_EXT_CAP_ID_ERR 1 +#define PCI_EXT_CAP_ID_VC 2 +#define PCI_EXT_CAP_ID_DSN 3 +#define PCI_EXT_CAP_ID_PWR 4 +#define PCI_EXT_CAP_ID_ARI 14 +#define PCI_EXT_CAP_ID_ATS 15 +#define PCI_EXT_CAP_ID_SRIOV 16 + +/* Advanced Error Reporting */ +#define PCI_ERR_UNCOR_STATUS 4 /* Uncorrectable Error Status */ +#define PCI_ERR_UNC_TRAIN 0x00000001 /* Training */ +#define PCI_ERR_UNC_DLP 0x00000010 /* Data Link Protocol */ +#define PCI_ERR_UNC_POISON_TLP 0x00001000 /* Poisoned TLP */ +#define PCI_ERR_UNC_FCP 0x00002000 /* Flow Control Protocol */ +#define PCI_ERR_UNC_COMP_TIME 0x00004000 /* Completion Timeout */ +#define PCI_ERR_UNC_COMP_ABORT 0x00008000 /* Completer Abort */ +#define PCI_ERR_UNC_UNX_COMP 0x00010000 /* Unexpected Completion */ +#define PCI_ERR_UNC_RX_OVER 0x00020000 /* Receiver Overflow */ +#define PCI_ERR_UNC_MALF_TLP 0x00040000 /* Malformed TLP */ +#define PCI_ERR_UNC_ECRC 0x00080000 /* ECRC Error Status */ +#define PCI_ERR_UNC_UNSUP 0x00100000 /* Unsupported Request */ +#define PCI_ERR_UNCOR_MASK 8 /* Uncorrectable Error Mask */ + /* Same bits as above */ +#define PCI_ERR_UNCOR_SEVER 12 /* Uncorrectable Error Severity */ + /* Same bits as above */ +#define PCI_ERR_COR_STATUS 16 /* Correctable Error Status */ +#define PCI_ERR_COR_RCVR 0x00000001 /* Receiver Error Status */ +#define PCI_ERR_COR_BAD_TLP 0x00000040 /* Bad TLP Status */ +#define PCI_ERR_COR_BAD_DLLP 0x00000080 /* Bad DLLP Status */ +#define PCI_ERR_COR_REP_ROLL 0x00000100 /* REPLAY_NUM Rollover */ +#define PCI_ERR_COR_REP_TIMER 0x00001000 /* Replay Timer Timeout */ +#define PCI_ERR_COR_MASK 20 /* Correctable Error Mask */ + /* Same bits as above */ +#define PCI_ERR_CAP 24 /* Advanced Error Capabilities */ +#define PCI_ERR_CAP_FEP(x) ((x) & 31) /* First Error Pointer */ +#define PCI_ERR_CAP_ECRC_GENC 0x00000020 /* ECRC Generation Capable */ +#define PCI_ERR_CAP_ECRC_GENE 0x00000040 /* ECRC Generation Enable */ +#define PCI_ERR_CAP_ECRC_CHKC 0x00000080 /* ECRC Check Capable */ +#define PCI_ERR_CAP_ECRC_CHKE 0x00000100 /* ECRC Check Enable */ +#define PCI_ERR_HEADER_LOG 28 /* Header Log Register (16 bytes) */ +#define PCI_ERR_ROOT_COMMAND 44 /* Root Error Command */ +/* Correctable Err Reporting Enable */ +#define PCI_ERR_ROOT_CMD_COR_EN 0x00000001 +/* Non-fatal Err Reporting Enable */ +#define PCI_ERR_ROOT_CMD_NONFATAL_EN 0x00000002 +/* Fatal Err Reporting Enable */ +#define PCI_ERR_ROOT_CMD_FATAL_EN 0x00000004 +#define PCI_ERR_ROOT_STATUS 48 +#define PCI_ERR_ROOT_COR_RCV 0x00000001 /* ERR_COR Received */ +/* Multi ERR_COR Received */ +#define PCI_ERR_ROOT_MULTI_COR_RCV 0x00000002 +/* ERR_FATAL/NONFATAL Recevied */ +#define PCI_ERR_ROOT_UNCOR_RCV 0x00000004 +/* Multi ERR_FATAL/NONFATAL Recevied */ +#define PCI_ERR_ROOT_MULTI_UNCOR_RCV 0x00000008 +#define PCI_ERR_ROOT_FIRST_FATAL 0x00000010 /* First Fatal */ +#define PCI_ERR_ROOT_NONFATAL_RCV 0x00000020 /* Non-Fatal Received */ +#define PCI_ERR_ROOT_FATAL_RCV 0x00000040 /* Fatal Received */ +#define PCI_ERR_ROOT_COR_SRC 52 +#define PCI_ERR_ROOT_SRC 54 + +/* Virtual Channel */ +#define PCI_VC_PORT_REG1 4 +#define PCI_VC_PORT_REG2 8 +#define PCI_VC_PORT_CTRL 12 +#define PCI_VC_PORT_STATUS 14 +#define PCI_VC_RES_CAP 16 +#define PCI_VC_RES_CTRL 20 +#define PCI_VC_RES_STATUS 26 + +/* Power Budgeting */ +#define PCI_PWR_DSR 4 /* Data Select Register */ +#define PCI_PWR_DATA 8 /* Data Register */ +#define PCI_PWR_DATA_BASE(x) ((x) & 0xff) /* Base Power */ +#define PCI_PWR_DATA_SCALE(x) (((x) >> 8) & 3) /* Data Scale */ +#define PCI_PWR_DATA_PM_SUB(x) (((x) >> 10) & 7) /* PM Sub State */ +#define PCI_PWR_DATA_PM_STATE(x) (((x) >> 13) & 3) /* PM State */ +#define PCI_PWR_DATA_TYPE(x) (((x) >> 15) & 7) /* Type */ +#define PCI_PWR_DATA_RAIL(x) (((x) >> 18) & 7) /* Power Rail */ +#define PCI_PWR_CAP 12 /* Capability */ +#define PCI_PWR_CAP_BUDGET(x) ((x) & 1) /* Included in system budget */ + +/* + * Hypertransport sub capability types + * + * Unfortunately there are both 3 bit and 5 bit capability types defined + * in the HT spec, catering for that is a little messy. You probably don't + * want to use these directly, just use pci_find_ht_capability() and it + * will do the right thing for you. + */ +#define HT_3BIT_CAP_MASK 0xE0 +#define HT_CAPTYPE_SLAVE 0x00 /* Slave/Primary link configuration */ +#define HT_CAPTYPE_HOST 0x20 /* Host/Secondary link configuration */ + +#define HT_5BIT_CAP_MASK 0xF8 +#define HT_CAPTYPE_IRQ 0x80 /* IRQ Configuration */ +#define HT_CAPTYPE_REMAPPING_40 0xA0 /* 40 bit address remapping */ +#define HT_CAPTYPE_REMAPPING_64 0xA2 /* 64 bit address remapping */ +#define HT_CAPTYPE_UNITID_CLUMP 0x90 /* Unit ID clumping */ +#define HT_CAPTYPE_EXTCONF 0x98 /* Extended Configuration Space Access */ +#define HT_CAPTYPE_MSI_MAPPING 0xA8 /* MSI Mapping Capability */ +#define HT_MSI_FLAGS 0x02 /* Offset to flags */ +#define HT_MSI_FLAGS_ENABLE 0x1 /* Mapping enable */ +#define HT_MSI_FLAGS_FIXED 0x2 /* Fixed mapping only */ +#define HT_MSI_FIXED_ADDR 0x00000000FEE00000ULL /* Fixed addr */ +#define HT_MSI_ADDR_LO 0x04 /* Offset to low addr bits */ +#define HT_MSI_ADDR_LO_MASK 0xFFF00000 /* Low address bit mask */ +#define HT_MSI_ADDR_HI 0x08 /* Offset to high addr bits */ +#define HT_CAPTYPE_DIRECT_ROUTE 0xB0 /* Direct routing configuration */ +#define HT_CAPTYPE_VCSET 0xB8 /* Virtual Channel configuration */ +#define HT_CAPTYPE_ERROR_RETRY 0xC0 /* Retry on error configuration */ +#define HT_CAPTYPE_GEN3 0xD0 /* Generation 3 hypertransport configuration */ +#define HT_CAPTYPE_PM 0xE0 /* Hypertransport powermanagement configuration */ + +/* Alternative Routing-ID Interpretation */ +#define PCI_ARI_CAP 0x04 /* ARI Capability Register */ +#define PCI_ARI_CAP_MFVC 0x0001 /* MFVC Function Groups Capability */ +#define PCI_ARI_CAP_ACS 0x0002 /* ACS Function Groups Capability */ +#define PCI_ARI_CAP_NFN(x) (((x) >> 8) & 0xff) /* Next Function Number */ +#define PCI_ARI_CTRL 0x06 /* ARI Control Register */ +#define PCI_ARI_CTRL_MFVC 0x0001 /* MFVC Function Groups Enable */ +#define PCI_ARI_CTRL_ACS 0x0002 /* ACS Function Groups Enable */ +#define PCI_ARI_CTRL_FG(x) (((x) >> 4) & 7) /* Function Group */ + +/* Address Translation Service */ +#define PCI_ATS_CAP 0x04 /* ATS Capability Register */ +#define PCI_ATS_CAP_QDEP(x) ((x) & 0x1f) /* Invalidate Queue Depth */ +#define PCI_ATS_MAX_QDEP 32 /* Max Invalidate Queue Depth */ +#define PCI_ATS_CTRL 0x06 /* ATS Control Register */ +#define PCI_ATS_CTRL_ENABLE 0x8000 /* ATS Enable */ +#define PCI_ATS_CTRL_STU(x) ((x) & 0x1f) /* Smallest Translation Unit */ +#define PCI_ATS_MIN_STU 12 /* shift of minimum STU block */ + +/* Single Root I/O Virtualization */ +#define PCI_SRIOV_CAP 0x04 /* SR-IOV Capabilities */ +#define PCI_SRIOV_CAP_VFM 0x01 /* VF Migration Capable */ +#define PCI_SRIOV_CAP_INTR(x) ((x) >> 21) /* Interrupt Message Number */ +#define PCI_SRIOV_CTRL 0x08 /* SR-IOV Control */ +#define PCI_SRIOV_CTRL_VFE 0x01 /* VF Enable */ +#define PCI_SRIOV_CTRL_VFM 0x02 /* VF Migration Enable */ +#define PCI_SRIOV_CTRL_INTR 0x04 /* VF Migration Interrupt Enable */ +#define PCI_SRIOV_CTRL_MSE 0x08 /* VF Memory Space Enable */ +#define PCI_SRIOV_CTRL_ARI 0x10 /* ARI Capable Hierarchy */ +#define PCI_SRIOV_STATUS 0x0a /* SR-IOV Status */ +#define PCI_SRIOV_STATUS_VFM 0x01 /* VF Migration Status */ +#define PCI_SRIOV_INITIAL_VF 0x0c /* Initial VFs */ +#define PCI_SRIOV_TOTAL_VF 0x0e /* Total VFs */ +#define PCI_SRIOV_NUM_VF 0x10 /* Number of VFs */ +#define PCI_SRIOV_FUNC_LINK 0x12 /* Function Dependency Link */ +#define PCI_SRIOV_VF_OFFSET 0x14 /* First VF Offset */ +#define PCI_SRIOV_VF_STRIDE 0x16 /* Following VF Stride */ +#define PCI_SRIOV_VF_DID 0x1a /* VF Device ID */ +#define PCI_SRIOV_SUP_PGSIZE 0x1c /* Supported Page Sizes */ +#define PCI_SRIOV_SYS_PGSIZE 0x20 /* System Page Size */ +#define PCI_SRIOV_BAR 0x24 /* VF BAR0 */ +#define PCI_SRIOV_NUM_BARS 6 /* Number of VF BARs */ +#define PCI_SRIOV_VFM 0x3c /* VF Migration State Array Offset*/ +#define PCI_SRIOV_VFM_BIR(x) ((x) & 7) /* State BIR */ +#define PCI_SRIOV_VFM_OFFSET(x) ((x) & ~7) /* State Offset */ +#define PCI_SRIOV_VFM_UA 0x0 /* Inactive.Unavailable */ +#define PCI_SRIOV_VFM_MI 0x1 /* Dormant.MigrateIn */ +#define PCI_SRIOV_VFM_MO 0x2 /* Active.MigrateOut */ +#define PCI_SRIOV_VFM_AV 0x3 /* Active.Available */ + +#endif /* LINUX_PCI_REGS_H */ diff -Nru qemu-kvm-0.12.5+noroms/hw/pci-stub.c qemu-kvm-0.14.1/hw/pci-stub.c --- qemu-kvm-0.12.5+noroms/hw/pci-stub.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/pci-stub.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,50 @@ +/* + * PCI stubs for plathome that doesn't support pci bus. + * + * Copyright (c) 2010 Isaku Yamahata + * VA Linux Systems Japan K.K. + * + * 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, see . + */ + +#include "sysemu.h" +#include "monitor.h" +#include "pci.h" + +static void pci_error_message(Monitor *mon) +{ + monitor_printf(mon, "PCI devices not supported\n"); +} + +void do_pci_info(Monitor *mon, QObject **ret_data) +{ + pci_error_message(mon); +} + +void do_pci_info_print(Monitor *mon, const QObject *data) +{ + pci_error_message(mon); +} + +int do_pcie_aer_inejct_error(Monitor *mon, + const QDict *qdict, QObject **ret_data) +{ + pci_error_message(mon); + return -ENOSYS; +} + +void pcie_aer_inject_error_print(Monitor *mon, const QObject *data) +{ + pci_error_message(mon); +} diff -Nru qemu-kvm-0.12.5+noroms/hw/pckbd.c qemu-kvm-0.14.1/hw/pckbd.c --- qemu-kvm-0.12.5+noroms/hw/pckbd.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/pckbd.c 2011-05-11 13:29:46.000000000 +0000 @@ -29,6 +29,12 @@ /* debug PC keyboard */ //#define DEBUG_KBD +#ifdef DEBUG_KBD +#define DPRINTF(fmt, ...) \ + do { printf("KBD: " fmt , ## __VA_ARGS__); } while (0) +#else +#define DPRINTF(fmt, ...) +#endif /* Keyboard Controller Commands */ #define KBD_CCMD_READ_MODE 0x20 /* Read mode bits */ @@ -50,7 +56,9 @@ #define KBD_CCMD_WRITE_MOUSE 0xD4 /* Write the following byte to the mouse */ #define KBD_CCMD_DISABLE_A20 0xDD /* HP vectra only ? */ #define KBD_CCMD_ENABLE_A20 0xDF /* HP vectra only ? */ -#define KBD_CCMD_RESET 0xFE +#define KBD_CCMD_PULSE_BITS_3_0 0xF0 /* Pulse bits 3-0 of the output port P2. */ +#define KBD_CCMD_RESET 0xFE /* Pulse bit 0 of the output port P2 = CPU reset. */ +#define KBD_CCMD_NO_OP 0xFF /* Pulse no bits of the output port P2. */ /* Keyboard Commands */ #define KBD_CMD_SET_LEDS 0xED /* Set keyboard leds */ @@ -87,6 +95,12 @@ #define KBD_MODE_KCC 0x40 /* Scan code conversion to PC format */ #define KBD_MODE_RFU 0x80 +/* Output Port Bits */ +#define KBD_OUT_RESET 0x01 /* 1=normal mode, 0=reset */ +#define KBD_OUT_A20 0x02 /* x86 only */ +#define KBD_OUT_OBF 0x10 /* Keyboard output buffer full */ +#define KBD_OUT_MOUSE_OBF 0x20 /* Mouse output buffer full */ + /* Mouse Commands */ #define AUX_SET_SCALE11 0xE6 /* Set 1:1 scaling */ #define AUX_SET_SCALE21 0xE7 /* Set 2:1 scaling */ @@ -116,6 +130,7 @@ uint8_t write_cmd; /* if non zero, write data to port 60 is expected */ uint8_t status; uint8_t mode; + uint8_t outport; /* Bitmask of devices with data available. */ uint8_t pending; void *kbd; @@ -123,11 +138,10 @@ qemu_irq irq_kbd; qemu_irq irq_mouse; + qemu_irq *a20_out; target_phys_addr_t mask; } KBDState; -static KBDState kbd_state; - /* update irq and KBD_STAT_[MOUSE_]OBF */ /* XXX: not generating the irqs if KBD_MODE_DISABLE_KBD is set may be incorrect, but it avoids having to simulate exact delays */ @@ -138,11 +152,14 @@ irq_kbd_level = 0; irq_mouse_level = 0; s->status &= ~(KBD_STAT_OBF | KBD_STAT_MOUSE_OBF); + s->outport &= ~(KBD_OUT_OBF | KBD_OUT_MOUSE_OBF); if (s->pending) { s->status |= KBD_STAT_OBF; + s->outport |= KBD_OUT_OBF; /* kbd data takes priority over aux data. */ if (s->pending == KBD_PENDING_AUX) { s->status |= KBD_STAT_MOUSE_OBF; + s->outport |= KBD_OUT_MOUSE_OBF; if (s->mode & KBD_MODE_MOUSE_INT) irq_mouse_level = 1; } else { @@ -182,9 +199,7 @@ KBDState *s = opaque; int val; val = s->status; -#if defined(DEBUG_KBD) - printf("kbd: read status=0x%02x\n", val); -#endif + DPRINTF("kbd: read status=0x%02x\n", val); return val; } @@ -196,13 +211,38 @@ ps2_queue(s->kbd, b); } +static void outport_write(KBDState *s, uint32_t val) +{ + DPRINTF("kbd: write outport=0x%02x\n", val); + s->outport = val; + if (s->a20_out) { + qemu_set_irq(*s->a20_out, (val >> 1) & 1); + } + if (!(val & 1)) { + qemu_system_reset_request(); + } +} + static void kbd_write_command(void *opaque, uint32_t addr, uint32_t val) { KBDState *s = opaque; -#ifdef DEBUG_KBD - printf("kbd: write cmd=0x%02x\n", val); -#endif + DPRINTF("kbd: write cmd=0x%02x\n", val); + + /* Bits 3-0 of the output port P2 of the keyboard controller may be pulsed + * low for approximately 6 micro seconds. Bits 3-0 of the KBD_CCMD_PULSE + * command specify the output port bits to be pulsed. + * 0: Bit should be pulsed. 1: Bit should not be modified. + * The only useful version of this command is pulsing bit 0, + * which does a CPU reset. + */ + if((val & KBD_CCMD_PULSE_BITS_3_0) == KBD_CCMD_PULSE_BITS_3_0) { + if(!(val & 1)) + val = KBD_CCMD_RESET; + else + val = KBD_CCMD_NO_OP; + } + switch(val) { case KBD_CCMD_READ_MODE: kbd_queue(s, s->mode, 0); @@ -242,31 +282,25 @@ kbd_queue(s, 0x00, 0); break; case KBD_CCMD_READ_OUTPORT: - /* XXX: check that */ -#ifdef TARGET_I386 - val = 0x01 | (ioport_get_a20() << 1); -#else - val = 0x01; -#endif - if (s->status & KBD_STAT_OBF) - val |= 0x10; - if (s->status & KBD_STAT_MOUSE_OBF) - val |= 0x20; - kbd_queue(s, val, 0); + kbd_queue(s, s->outport, 0); break; -#ifdef TARGET_I386 case KBD_CCMD_ENABLE_A20: - ioport_set_a20(1); + if (s->a20_out) { + qemu_irq_raise(*s->a20_out); + } + s->outport |= KBD_OUT_A20; break; case KBD_CCMD_DISABLE_A20: - ioport_set_a20(0); + if (s->a20_out) { + qemu_irq_lower(*s->a20_out); + } + s->outport &= ~KBD_OUT_A20; break; -#endif case KBD_CCMD_RESET: qemu_system_reset_request(); break; - case 0xff: - /* ignore that - I don't know what is its use */ + case KBD_CCMD_NO_OP: + /* ignore that */ break; default: fprintf(stderr, "qemu: unsupported keyboard cmd=0x%02x\n", val); @@ -284,9 +318,7 @@ else val = ps2_read_data(s->kbd); -#if defined(DEBUG_KBD) - printf("kbd: read data=0x%02x\n", val); -#endif + DPRINTF("kbd: read data=0x%02x\n", val); return val; } @@ -294,9 +326,7 @@ { KBDState *s = opaque; -#ifdef DEBUG_KBD - printf("kbd: write data=0x%02x\n", val); -#endif + DPRINTF("kbd: write data=0x%02x\n", val); switch(s->write_cmd) { case 0: @@ -315,12 +345,7 @@ kbd_queue(s, val, 1); break; case KBD_CCMD_WRITE_OUTPORT: -#ifdef TARGET_I386 - ioport_set_a20((val >> 1) & 1); -#endif - if (!(val & 1)) { - qemu_system_reset_request(); - } + outport_write(s, val); break; case KBD_CCMD_WRITE_MOUSE: ps2_write_mouse(s->mouse, val); @@ -337,6 +362,7 @@ s->mode = KBD_MODE_KBD_INT | KBD_MODE_MOUSE_INT; s->status = KBD_STAT_CMD | KBD_STAT_UNLOCKED; + s->outport = KBD_OUT_RESET | KBD_OUT_A20; } static const VMStateDescription vmstate_kbd = { @@ -390,22 +416,20 @@ target_phys_addr_t base, ram_addr_t size, target_phys_addr_t mask) { - KBDState *s = &kbd_state; + KBDState *s = qemu_mallocz(sizeof(KBDState)); int s_io_memory; s->irq_kbd = kbd_irq; s->irq_mouse = mouse_irq; s->mask = mask; - vmstate_register(0, &vmstate_kbd, s); - s_io_memory = cpu_register_io_memory(kbd_mm_read, kbd_mm_write, s); + vmstate_register(NULL, 0, &vmstate_kbd, s); + s_io_memory = cpu_register_io_memory(kbd_mm_read, kbd_mm_write, s, + DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base, size, s_io_memory); s->kbd = ps2_kbd_init(kbd_update_kbd_irq, s); s->mouse = ps2_mouse_init(kbd_update_aux_irq, s); -#ifdef TARGET_I386 - vmmouse_init(s->mouse); -#endif qemu_register_reset(kbd_reset, s); } @@ -414,6 +438,21 @@ KBDState kbd; } ISAKBDState; +void i8042_isa_mouse_fake_event(void *opaque) +{ + ISADevice *dev = opaque; + KBDState *s = &(DO_UPCAST(ISAKBDState, dev, dev)->kbd); + + ps2_mouse_fake_event(s->mouse); +} + +void i8042_setup_a20_line(ISADevice *dev, qemu_irq *a20_out) +{ + KBDState *s = &(DO_UPCAST(ISAKBDState, dev, dev)->kbd); + + s->a20_out = a20_out; +} + static const VMStateDescription vmstate_kbd_isa = { .name = "pckbd", .version_id = 3, @@ -434,14 +473,13 @@ register_ioport_read(0x60, 1, 1, kbd_read_data, s); register_ioport_write(0x60, 1, 1, kbd_write_data, s); + isa_init_ioport(dev, 0x60); register_ioport_read(0x64, 1, 1, kbd_read_status, s); register_ioport_write(0x64, 1, 1, kbd_write_command, s); + isa_init_ioport(dev, 0x64); s->kbd = ps2_kbd_init(kbd_update_kbd_irq, s); s->mouse = ps2_mouse_init(kbd_update_aux_irq, s); -#ifdef TARGET_I386 - vmmouse_init(s->mouse); -#endif qemu_register_reset(kbd_reset, s); return 0; } diff -Nru qemu-kvm-0.12.5+noroms/hw/pcmcia.h qemu-kvm-0.14.1/hw/pcmcia.h --- qemu-kvm-0.12.5+noroms/hw/pcmcia.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/pcmcia.h 2011-05-11 13:29:46.000000000 +0000 @@ -1,7 +1,6 @@ /* PCMCIA/Cardbus */ #include "qemu-common.h" -#include "sysemu.h" typedef struct { qemu_irq irq; diff -Nru qemu-kvm-0.12.5+noroms/hw/pcnet.c qemu-kvm-0.14.1/hw/pcnet.c --- qemu-kvm-0.12.5+noroms/hw/pcnet.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/pcnet.c 2011-05-11 13:29:46.000000000 +0000 @@ -35,11 +35,11 @@ * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR92C990.txt */ -#include "pci.h" +#include "qdev.h" #include "net.h" -#include "loader.h" #include "qemu-timer.h" #include "qemu_socket.h" +#include "sysemu.h" #include "pcnet.h" @@ -52,11 +52,6 @@ //#define PCNET_DEBUG_MATCH -typedef struct { - PCIDevice pci_dev; - PCNetState state; -} PCIPCNetState; - struct qemu_ether_header { uint8_t ether_dhost[6]; uint8_t ether_shost[6]; @@ -704,7 +699,6 @@ static uint32_t pcnet_csr_readw(PCNetState *s, uint32_t rap); static void pcnet_csr_writew(PCNetState *s, uint32_t rap, uint32_t new_value); static void pcnet_bcr_writew(PCNetState *s, uint32_t rap, uint32_t val); -static uint32_t pcnet_bcr_readw(PCNetState *s, uint32_t rap); static void pcnet_s_reset(PCNetState *s) { @@ -1048,9 +1042,10 @@ int crc_err = 0; int size = size_; - if (CSR_DRX(s) || CSR_STOP(s) || CSR_SPND(s) || !size) + if (CSR_DRX(s) || CSR_STOP(s) || CSR_SPND(s) || !size || + (CSR_LOOP(s) && !s->looptest)) { return -1; - + } #ifdef PCNET_DEBUG printf("pcnet_receive size=%d\n", size); #endif @@ -1537,7 +1532,7 @@ } } -static uint32_t pcnet_bcr_readw(PCNetState *s, uint32_t rap) +uint32_t pcnet_bcr_readw(PCNetState *s, uint32_t rap) { uint32_t val; rap &= 127; @@ -1594,27 +1589,6 @@ pcnet_poll_timer(s); } -static void pcnet_aprom_writeb(void *opaque, uint32_t addr, uint32_t val) -{ - PCNetState *s = opaque; -#ifdef PCNET_DEBUG - printf("pcnet_aprom_writeb addr=0x%08x val=0x%02x\n", addr, val); -#endif - /* Check APROMWE bit to enable write access */ - if (pcnet_bcr_readw(s,2) & 0x80) - s->prom[addr & 15] = val; -} - -static uint32_t pcnet_aprom_readb(void *opaque, uint32_t addr) -{ - PCNetState *s = opaque; - uint32_t val = s->prom[addr &= 15]; -#ifdef PCNET_DEBUG - printf("pcnet_aprom_readb addr=0x%08x val=0x%02x\n", addr, val); -#endif - return val; -} - void pcnet_ioport_writew(void *opaque, uint32_t addr, uint32_t val) { PCNetState *s = opaque; @@ -1667,7 +1641,7 @@ return val; } -static void pcnet_ioport_writel(void *opaque, uint32_t addr, uint32_t val) +void pcnet_ioport_writel(void *opaque, uint32_t addr, uint32_t val) { PCNetState *s = opaque; pcnet_poll_timer(s); @@ -1697,7 +1671,7 @@ pcnet_update_irq(s); } -static uint32_t pcnet_ioport_readl(void *opaque, uint32_t addr) +uint32_t pcnet_ioport_readl(void *opaque, uint32_t addr) { PCNetState *s = opaque; uint32_t val = -1; @@ -1726,125 +1700,6 @@ return val; } -static void pcnet_ioport_map(PCIDevice *pci_dev, int region_num, - pcibus_t addr, pcibus_t size, int type) -{ - PCNetState *d = &DO_UPCAST(PCIPCNetState, pci_dev, pci_dev)->state; - -#ifdef PCNET_DEBUG_IO - printf("pcnet_ioport_map addr=0x%04"FMT_PCIBUS" size=0x%04"FMT_PCIBUS"\n", - addr, size); -#endif - - register_ioport_write(addr, 16, 1, pcnet_aprom_writeb, d); - register_ioport_read(addr, 16, 1, pcnet_aprom_readb, d); - - register_ioport_write(addr + 0x10, 0x10, 2, pcnet_ioport_writew, d); - register_ioport_read(addr + 0x10, 0x10, 2, pcnet_ioport_readw, d); - register_ioport_write(addr + 0x10, 0x10, 4, pcnet_ioport_writel, d); - register_ioport_read(addr + 0x10, 0x10, 4, pcnet_ioport_readl, d); -} - -static void pcnet_mmio_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) -{ - PCNetState *d = opaque; -#ifdef PCNET_DEBUG_IO - printf("pcnet_mmio_writeb addr=0x" TARGET_FMT_plx" val=0x%02x\n", addr, - val); -#endif - if (!(addr & 0x10)) - pcnet_aprom_writeb(d, addr & 0x0f, val); -} - -static uint32_t pcnet_mmio_readb(void *opaque, target_phys_addr_t addr) -{ - PCNetState *d = opaque; - uint32_t val = -1; - if (!(addr & 0x10)) - val = pcnet_aprom_readb(d, addr & 0x0f); -#ifdef PCNET_DEBUG_IO - printf("pcnet_mmio_readb addr=0x" TARGET_FMT_plx " val=0x%02x\n", addr, - val & 0xff); -#endif - return val; -} - -static void pcnet_mmio_writew(void *opaque, target_phys_addr_t addr, uint32_t val) -{ - PCNetState *d = opaque; -#ifdef PCNET_DEBUG_IO - printf("pcnet_mmio_writew addr=0x" TARGET_FMT_plx " val=0x%04x\n", addr, - val); -#endif - if (addr & 0x10) - pcnet_ioport_writew(d, addr & 0x0f, val); - else { - addr &= 0x0f; - pcnet_aprom_writeb(d, addr, val & 0xff); - pcnet_aprom_writeb(d, addr+1, (val & 0xff00) >> 8); - } -} - -static uint32_t pcnet_mmio_readw(void *opaque, target_phys_addr_t addr) -{ - PCNetState *d = opaque; - uint32_t val = -1; - if (addr & 0x10) - val = pcnet_ioport_readw(d, addr & 0x0f); - else { - addr &= 0x0f; - val = pcnet_aprom_readb(d, addr+1); - val <<= 8; - val |= pcnet_aprom_readb(d, addr); - } -#ifdef PCNET_DEBUG_IO - printf("pcnet_mmio_readw addr=0x" TARGET_FMT_plx" val = 0x%04x\n", addr, - val & 0xffff); -#endif - return val; -} - -static void pcnet_mmio_writel(void *opaque, target_phys_addr_t addr, uint32_t val) -{ - PCNetState *d = opaque; -#ifdef PCNET_DEBUG_IO - printf("pcnet_mmio_writel addr=0x" TARGET_FMT_plx" val=0x%08x\n", addr, - val); -#endif - if (addr & 0x10) - pcnet_ioport_writel(d, addr & 0x0f, val); - else { - addr &= 0x0f; - pcnet_aprom_writeb(d, addr, val & 0xff); - pcnet_aprom_writeb(d, addr+1, (val & 0xff00) >> 8); - pcnet_aprom_writeb(d, addr+2, (val & 0xff0000) >> 16); - pcnet_aprom_writeb(d, addr+3, (val & 0xff000000) >> 24); - } -} - -static uint32_t pcnet_mmio_readl(void *opaque, target_phys_addr_t addr) -{ - PCNetState *d = opaque; - uint32_t val; - if (addr & 0x10) - val = pcnet_ioport_readl(d, addr & 0x0f); - else { - addr &= 0x0f; - val = pcnet_aprom_readb(d, addr+3); - val <<= 8; - val |= pcnet_aprom_readb(d, addr+2); - val <<= 8; - val |= pcnet_aprom_readb(d, addr+1); - val <<= 8; - val |= pcnet_aprom_readb(d, addr); - } -#ifdef PCNET_DEBUG_IO - printf("pcnet_mmio_readl addr=0x" TARGET_FMT_plx " val=0x%08x\n", addr, - val); -#endif - return val; -} - static bool is_version_2(void *opaque, int version_id) { return version_id == 2; @@ -1874,18 +1729,6 @@ } }; -static const VMStateDescription vmstate_pci_pcnet = { - .name = "pcnet", - .version_id = 3, - .minimum_version_id = 2, - .minimum_version_id_old = 2, - .fields = (VMStateField []) { - VMSTATE_PCI_DEVICE(pci_dev, PCIPCNetState), - VMSTATE_STRUCT(state, PCIPCNetState, 0, vmstate_pcnet, PCNetState), - VMSTATE_END_OF_LIST() - } -}; - void pcnet_common_cleanup(PCNetState *d) { d->nic = NULL; @@ -1898,151 +1741,8 @@ qemu_macaddr_default_if_unset(&s->conf.macaddr); s->nic = qemu_new_nic(info, &s->conf, dev->info->name, dev->id, s); qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a); - return 0; -} - -/* PCI interface */ - -static CPUWriteMemoryFunc * const pcnet_mmio_write[] = { - &pcnet_mmio_writeb, - &pcnet_mmio_writew, - &pcnet_mmio_writel -}; - -static CPUReadMemoryFunc * const pcnet_mmio_read[] = { - &pcnet_mmio_readb, - &pcnet_mmio_readw, - &pcnet_mmio_readl -}; - -static void pcnet_mmio_map(PCIDevice *pci_dev, int region_num, - pcibus_t addr, pcibus_t size, int type) -{ - PCIPCNetState *d = DO_UPCAST(PCIPCNetState, pci_dev, pci_dev); - -#ifdef PCNET_DEBUG_IO - printf("pcnet_mmio_map addr=0x%08"FMT_PCIBUS" 0x%08"FMT_PCIBUS"\n", - addr, size); -#endif - - cpu_register_physical_memory(addr, PCNET_PNPMMIO_SIZE, d->state.mmio_index); -} - -static void pci_physical_memory_write(void *dma_opaque, target_phys_addr_t addr, - uint8_t *buf, int len, int do_bswap) -{ - cpu_physical_memory_write(addr, buf, len); -} - -static void pci_physical_memory_read(void *dma_opaque, target_phys_addr_t addr, - uint8_t *buf, int len, int do_bswap) -{ - cpu_physical_memory_read(addr, buf, len); -} - -static void pci_pcnet_cleanup(VLANClientState *nc) -{ - PCNetState *d = DO_UPCAST(NICState, nc, nc)->opaque; - pcnet_common_cleanup(d); -} + add_boot_device_path(s->conf.bootindex, dev, "/ethernet-phy@0"); -static int pci_pcnet_uninit(PCIDevice *dev) -{ - PCIPCNetState *d = DO_UPCAST(PCIPCNetState, pci_dev, dev); - - cpu_unregister_io_memory(d->state.mmio_index); - qemu_del_timer(d->state.poll_timer); - qemu_free_timer(d->state.poll_timer); - qemu_del_vlan_client(&d->state.nic->nc); return 0; } - -static NetClientInfo net_pci_pcnet_info = { - .type = NET_CLIENT_TYPE_NIC, - .size = sizeof(NICState), - .can_receive = pcnet_can_receive, - .receive = pcnet_receive, - .cleanup = pci_pcnet_cleanup, -}; - -static int pci_pcnet_init(PCIDevice *pci_dev) -{ - PCIPCNetState *d = DO_UPCAST(PCIPCNetState, pci_dev, pci_dev); - PCNetState *s = &d->state; - uint8_t *pci_conf; - -#if 0 - printf("sizeof(RMD)=%d, sizeof(TMD)=%d\n", - sizeof(struct pcnet_RMD), sizeof(struct pcnet_TMD)); -#endif - - pci_conf = pci_dev->config; - - pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_AMD); - pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_AMD_LANCE); - *(uint16_t *)&pci_conf[0x04] = cpu_to_le16(0x0007); - *(uint16_t *)&pci_conf[0x06] = cpu_to_le16(0x0280); - pci_conf[0x08] = 0x10; - pci_conf[0x09] = 0x00; - pci_config_set_class(pci_conf, PCI_CLASS_NETWORK_ETHERNET); - pci_conf[PCI_HEADER_TYPE] = PCI_HEADER_TYPE_NORMAL; // header_type - - *(uint32_t *)&pci_conf[0x10] = cpu_to_le32(0x00000001); - *(uint32_t *)&pci_conf[0x14] = cpu_to_le32(0x00000000); - - pci_conf[0x3d] = 1; // interrupt pin 0 - pci_conf[0x3e] = 0x06; - pci_conf[0x3f] = 0xff; - - /* Handler for memory-mapped I/O */ - s->mmio_index = - cpu_register_io_memory(pcnet_mmio_read, pcnet_mmio_write, &d->state); - - pci_register_bar((PCIDevice *)d, 0, PCNET_IOPORT_SIZE, - PCI_BASE_ADDRESS_SPACE_IO, pcnet_ioport_map); - - pci_register_bar((PCIDevice *)d, 1, PCNET_PNPMMIO_SIZE, - PCI_BASE_ADDRESS_SPACE_MEMORY, pcnet_mmio_map); - - s->irq = pci_dev->irq[0]; - s->phys_mem_read = pci_physical_memory_read; - s->phys_mem_write = pci_physical_memory_write; - - if (!pci_dev->qdev.hotplugged) { - static int loaded = 0; - if (!loaded) { - rom_add_option("pxe-pcnet.bin"); - loaded = 1; - } - } - - return pcnet_common_init(&pci_dev->qdev, s, &net_pci_pcnet_info); -} - -static void pci_reset(DeviceState *dev) -{ - PCIPCNetState *d = DO_UPCAST(PCIPCNetState, pci_dev.qdev, dev); - - pcnet_h_reset(&d->state); -} - -static PCIDeviceInfo pcnet_info = { - .qdev.name = "pcnet", - .qdev.size = sizeof(PCIPCNetState), - .qdev.reset = pci_reset, - .qdev.vmsd = &vmstate_pci_pcnet, - .init = pci_pcnet_init, - .exit = pci_pcnet_uninit, - .qdev.props = (Property[]) { - DEFINE_NIC_PROPERTIES(PCIPCNetState, state.conf), - DEFINE_PROP_END_OF_LIST(), - } -}; - -static void pcnet_register_devices(void) -{ - pci_qdev_register(&pcnet_info); -} - -device_init(pcnet_register_devices) diff -Nru qemu-kvm-0.12.5+noroms/hw/pcnet.h qemu-kvm-0.14.1/hw/pcnet.h --- qemu-kvm-0.12.5+noroms/hw/pcnet.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/pcnet.h 2011-05-11 13:29:46.000000000 +0000 @@ -32,6 +32,9 @@ void pcnet_h_reset(void *opaque); void pcnet_ioport_writew(void *opaque, uint32_t addr, uint32_t val); uint32_t pcnet_ioport_readw(void *opaque, uint32_t addr); +void pcnet_ioport_writel(void *opaque, uint32_t addr, uint32_t val); +uint32_t pcnet_ioport_readl(void *opaque, uint32_t addr); +uint32_t pcnet_bcr_readw(PCNetState *s, uint32_t rap); int pcnet_can_receive(VLANClientState *nc); ssize_t pcnet_receive(VLANClientState *nc, const uint8_t *buf, size_t size_); void pcnet_common_cleanup(PCNetState *d); diff -Nru qemu-kvm-0.12.5+noroms/hw/pcnet-pci.c qemu-kvm-0.14.1/hw/pcnet-pci.c --- qemu-kvm-0.12.5+noroms/hw/pcnet-pci.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/pcnet-pci.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,346 @@ +/* + * QEMU AMD PC-Net II (Am79C970A) PCI emulation + * + * Copyright (c) 2004 Antony T Curtis + * + * 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. + */ + +/* This software was written to be compatible with the specification: + * AMD Am79C970A PCnet-PCI II Ethernet Controller Data-Sheet + * AMD Publication# 19436 Rev:E Amendment/0 Issue Date: June 2000 + */ + +#include "pci.h" +#include "net.h" +#include "loader.h" +#include "qemu-timer.h" + +#include "pcnet.h" + +//#define PCNET_DEBUG +//#define PCNET_DEBUG_IO +//#define PCNET_DEBUG_BCR +//#define PCNET_DEBUG_CSR +//#define PCNET_DEBUG_RMD +//#define PCNET_DEBUG_TMD +//#define PCNET_DEBUG_MATCH + + +typedef struct { + PCIDevice pci_dev; + PCNetState state; +} PCIPCNetState; + +static void pcnet_aprom_writeb(void *opaque, uint32_t addr, uint32_t val) +{ + PCNetState *s = opaque; +#ifdef PCNET_DEBUG + printf("pcnet_aprom_writeb addr=0x%08x val=0x%02x\n", addr, val); +#endif + /* Check APROMWE bit to enable write access */ + if (pcnet_bcr_readw(s,2) & 0x100) + s->prom[addr & 15] = val; +} + +static uint32_t pcnet_aprom_readb(void *opaque, uint32_t addr) +{ + PCNetState *s = opaque; + uint32_t val = s->prom[addr & 15]; +#ifdef PCNET_DEBUG + printf("pcnet_aprom_readb addr=0x%08x val=0x%02x\n", addr, val); +#endif + return val; +} + +static void pcnet_ioport_map(PCIDevice *pci_dev, int region_num, + pcibus_t addr, pcibus_t size, int type) +{ + PCNetState *d = &DO_UPCAST(PCIPCNetState, pci_dev, pci_dev)->state; + +#ifdef PCNET_DEBUG_IO + printf("pcnet_ioport_map addr=0x%04"FMT_PCIBUS" size=0x%04"FMT_PCIBUS"\n", + addr, size); +#endif + + register_ioport_write(addr, 16, 1, pcnet_aprom_writeb, d); + register_ioport_read(addr, 16, 1, pcnet_aprom_readb, d); + + register_ioport_write(addr + 0x10, 0x10, 2, pcnet_ioport_writew, d); + register_ioport_read(addr + 0x10, 0x10, 2, pcnet_ioport_readw, d); + register_ioport_write(addr + 0x10, 0x10, 4, pcnet_ioport_writel, d); + register_ioport_read(addr + 0x10, 0x10, 4, pcnet_ioport_readl, d); +} + +static void pcnet_mmio_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + PCNetState *d = opaque; +#ifdef PCNET_DEBUG_IO + printf("pcnet_mmio_writeb addr=0x" TARGET_FMT_plx" val=0x%02x\n", addr, + val); +#endif + if (!(addr & 0x10)) + pcnet_aprom_writeb(d, addr & 0x0f, val); +} + +static uint32_t pcnet_mmio_readb(void *opaque, target_phys_addr_t addr) +{ + PCNetState *d = opaque; + uint32_t val = -1; + if (!(addr & 0x10)) + val = pcnet_aprom_readb(d, addr & 0x0f); +#ifdef PCNET_DEBUG_IO + printf("pcnet_mmio_readb addr=0x" TARGET_FMT_plx " val=0x%02x\n", addr, + val & 0xff); +#endif + return val; +} + +static void pcnet_mmio_writew(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + PCNetState *d = opaque; +#ifdef PCNET_DEBUG_IO + printf("pcnet_mmio_writew addr=0x" TARGET_FMT_plx " val=0x%04x\n", addr, + val); +#endif + if (addr & 0x10) + pcnet_ioport_writew(d, addr & 0x0f, val); + else { + addr &= 0x0f; + pcnet_aprom_writeb(d, addr, val & 0xff); + pcnet_aprom_writeb(d, addr+1, (val & 0xff00) >> 8); + } +} + +static uint32_t pcnet_mmio_readw(void *opaque, target_phys_addr_t addr) +{ + PCNetState *d = opaque; + uint32_t val = -1; + if (addr & 0x10) + val = pcnet_ioport_readw(d, addr & 0x0f); + else { + addr &= 0x0f; + val = pcnet_aprom_readb(d, addr+1); + val <<= 8; + val |= pcnet_aprom_readb(d, addr); + } +#ifdef PCNET_DEBUG_IO + printf("pcnet_mmio_readw addr=0x" TARGET_FMT_plx" val = 0x%04x\n", addr, + val & 0xffff); +#endif + return val; +} + +static void pcnet_mmio_writel(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + PCNetState *d = opaque; +#ifdef PCNET_DEBUG_IO + printf("pcnet_mmio_writel addr=0x" TARGET_FMT_plx" val=0x%08x\n", addr, + val); +#endif + if (addr & 0x10) + pcnet_ioport_writel(d, addr & 0x0f, val); + else { + addr &= 0x0f; + pcnet_aprom_writeb(d, addr, val & 0xff); + pcnet_aprom_writeb(d, addr+1, (val & 0xff00) >> 8); + pcnet_aprom_writeb(d, addr+2, (val & 0xff0000) >> 16); + pcnet_aprom_writeb(d, addr+3, (val & 0xff000000) >> 24); + } +} + +static uint32_t pcnet_mmio_readl(void *opaque, target_phys_addr_t addr) +{ + PCNetState *d = opaque; + uint32_t val; + if (addr & 0x10) + val = pcnet_ioport_readl(d, addr & 0x0f); + else { + addr &= 0x0f; + val = pcnet_aprom_readb(d, addr+3); + val <<= 8; + val |= pcnet_aprom_readb(d, addr+2); + val <<= 8; + val |= pcnet_aprom_readb(d, addr+1); + val <<= 8; + val |= pcnet_aprom_readb(d, addr); + } +#ifdef PCNET_DEBUG_IO + printf("pcnet_mmio_readl addr=0x" TARGET_FMT_plx " val=0x%08x\n", addr, + val); +#endif + return val; +} + +static const VMStateDescription vmstate_pci_pcnet = { + .name = "pcnet", + .version_id = 3, + .minimum_version_id = 2, + .minimum_version_id_old = 2, + .fields = (VMStateField []) { + VMSTATE_PCI_DEVICE(pci_dev, PCIPCNetState), + VMSTATE_STRUCT(state, PCIPCNetState, 0, vmstate_pcnet, PCNetState), + VMSTATE_END_OF_LIST() + } +}; + +/* PCI interface */ + +static CPUWriteMemoryFunc * const pcnet_mmio_write[] = { + &pcnet_mmio_writeb, + &pcnet_mmio_writew, + &pcnet_mmio_writel +}; + +static CPUReadMemoryFunc * const pcnet_mmio_read[] = { + &pcnet_mmio_readb, + &pcnet_mmio_readw, + &pcnet_mmio_readl +}; + +static void pcnet_mmio_map(PCIDevice *pci_dev, int region_num, + pcibus_t addr, pcibus_t size, int type) +{ + PCIPCNetState *d = DO_UPCAST(PCIPCNetState, pci_dev, pci_dev); + +#ifdef PCNET_DEBUG_IO + printf("pcnet_mmio_map addr=0x%08"FMT_PCIBUS" 0x%08"FMT_PCIBUS"\n", + addr, size); +#endif + + cpu_register_physical_memory(addr, PCNET_PNPMMIO_SIZE, d->state.mmio_index); +} + +static void pci_physical_memory_write(void *dma_opaque, target_phys_addr_t addr, + uint8_t *buf, int len, int do_bswap) +{ + cpu_physical_memory_write(addr, buf, len); +} + +static void pci_physical_memory_read(void *dma_opaque, target_phys_addr_t addr, + uint8_t *buf, int len, int do_bswap) +{ + cpu_physical_memory_read(addr, buf, len); +} + +static void pci_pcnet_cleanup(VLANClientState *nc) +{ + PCNetState *d = DO_UPCAST(NICState, nc, nc)->opaque; + + pcnet_common_cleanup(d); +} + +static int pci_pcnet_uninit(PCIDevice *dev) +{ + PCIPCNetState *d = DO_UPCAST(PCIPCNetState, pci_dev, dev); + + cpu_unregister_io_memory(d->state.mmio_index); + qemu_del_timer(d->state.poll_timer); + qemu_free_timer(d->state.poll_timer); + qemu_del_vlan_client(&d->state.nic->nc); + return 0; +} + +static NetClientInfo net_pci_pcnet_info = { + .type = NET_CLIENT_TYPE_NIC, + .size = sizeof(NICState), + .can_receive = pcnet_can_receive, + .receive = pcnet_receive, + .cleanup = pci_pcnet_cleanup, +}; + +static int pci_pcnet_init(PCIDevice *pci_dev) +{ + PCIPCNetState *d = DO_UPCAST(PCIPCNetState, pci_dev, pci_dev); + PCNetState *s = &d->state; + uint8_t *pci_conf; + +#if 0 + printf("sizeof(RMD)=%d, sizeof(TMD)=%d\n", + sizeof(struct pcnet_RMD), sizeof(struct pcnet_TMD)); +#endif + + pci_conf = pci_dev->config; + + pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_AMD); + pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_AMD_LANCE); + pci_set_word(pci_conf + PCI_STATUS, + PCI_STATUS_FAST_BACK | PCI_STATUS_DEVSEL_MEDIUM); + pci_conf[PCI_REVISION_ID] = 0x10; + pci_config_set_class(pci_conf, PCI_CLASS_NETWORK_ETHERNET); + + pci_set_word(pci_conf + PCI_SUBSYSTEM_VENDOR_ID, 0x0); + pci_set_word(pci_conf + PCI_SUBSYSTEM_ID, 0x0); + + pci_conf[PCI_INTERRUPT_PIN] = 1; // interrupt pin 0 + pci_conf[PCI_MIN_GNT] = 0x06; + pci_conf[PCI_MAX_LAT] = 0xff; + + /* Handler for memory-mapped I/O */ + s->mmio_index = + cpu_register_io_memory(pcnet_mmio_read, pcnet_mmio_write, &d->state, + DEVICE_NATIVE_ENDIAN); + + pci_register_bar(pci_dev, 0, PCNET_IOPORT_SIZE, + PCI_BASE_ADDRESS_SPACE_IO, pcnet_ioport_map); + + pci_register_bar(pci_dev, 1, PCNET_PNPMMIO_SIZE, + PCI_BASE_ADDRESS_SPACE_MEMORY, pcnet_mmio_map); + + s->irq = pci_dev->irq[0]; + s->phys_mem_read = pci_physical_memory_read; + s->phys_mem_write = pci_physical_memory_write; + + if (!pci_dev->qdev.hotplugged) { + static int loaded = 0; + if (!loaded) { + rom_add_option("pxe-pcnet.bin", -1); + loaded = 1; + } + } + + return pcnet_common_init(&pci_dev->qdev, s, &net_pci_pcnet_info); +} + +static void pci_reset(DeviceState *dev) +{ + PCIPCNetState *d = DO_UPCAST(PCIPCNetState, pci_dev.qdev, dev); + + pcnet_h_reset(&d->state); +} + +static PCIDeviceInfo pcnet_info = { + .qdev.name = "pcnet", + .qdev.size = sizeof(PCIPCNetState), + .qdev.reset = pci_reset, + .qdev.vmsd = &vmstate_pci_pcnet, + .init = pci_pcnet_init, + .exit = pci_pcnet_uninit, + .qdev.props = (Property[]) { + DEFINE_NIC_PROPERTIES(PCIPCNetState, state.conf), + DEFINE_PROP_END_OF_LIST(), + } +}; + +static void pci_pcnet_register_devices(void) +{ + pci_qdev_register(&pcnet_info); +} + +device_init(pci_pcnet_register_devices) diff -Nru qemu-kvm-0.12.5+noroms/hw/pc_piix.c qemu-kvm-0.14.1/hw/pc_piix.c --- qemu-kvm-0.12.5+noroms/hw/pc_piix.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/pc_piix.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,407 @@ +/* + * QEMU PC System Emulator + * + * Copyright (c) 2003-2004 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 "hw.h" +#include "pc.h" +#include "apic.h" +#include "pci.h" +#include "usb-uhci.h" +#include "usb-ohci.h" +#include "net.h" +#include "boards.h" +#include "ide.h" +#include "kvm.h" +#include "sysemu.h" +#include "sysbus.h" +#include "arch_init.h" +#include "blockdev.h" + +qemu_irq *ioapic_irq_hack; + +#define MAX_IDE_BUS 2 + +static const int ide_iobase[MAX_IDE_BUS] = { 0x1f0, 0x170 }; +static const int ide_iobase2[MAX_IDE_BUS] = { 0x3f6, 0x376 }; +static const int ide_irq[MAX_IDE_BUS] = { 14, 15 }; + +const char *global_cpu_model; /* cpu hotadd */ + +static void ioapic_init(IsaIrqState *isa_irq_state) +{ + DeviceState *dev; + SysBusDevice *d; + unsigned int i; + + dev = qdev_create(NULL, "ioapic"); + qdev_init_nofail(dev); + d = sysbus_from_qdev(dev); + sysbus_mmio_map(d, 0, 0xfec00000); + + for (i = 0; i < IOAPIC_NUM_PINS; i++) { + isa_irq_state->ioapic[i] = qdev_get_gpio_in(dev, i); + } +} + +/* PC hardware initialisation */ +static void pc_init1(ram_addr_t ram_size, + const char *boot_device, + const char *kernel_filename, + const char *kernel_cmdline, + const char *initrd_filename, + const char *cpu_model, + int pci_enabled) +{ + int i; + ram_addr_t below_4g_mem_size, above_4g_mem_size; + PCIBus *pci_bus; + PCII440FXState *i440fx_state; + int piix3_devfn = -1; + qemu_irq *cpu_irq; + qemu_irq *isa_irq; + qemu_irq *i8259; + qemu_irq *cmos_s3; + qemu_irq *smi_irq; + IsaIrqState *isa_irq_state; + DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; + FDCtrl *floppy_controller; + BusState *idebus[MAX_IDE_BUS]; + ISADevice *rtc_state; + + global_cpu_model = cpu_model; + + pc_cpus_init(cpu_model); + + vmport_init(); + + /* allocate ram and load rom/bios */ + pc_memory_init(ram_size, kernel_filename, kernel_cmdline, initrd_filename, + &below_4g_mem_size, &above_4g_mem_size); + + cpu_irq = pc_allocate_cpu_irq(); +#ifdef KVM_CAP_IRQCHIP + if (kvm_enabled() && kvm_irqchip_in_kernel()) { + isa_irq_state = qemu_mallocz(sizeof(*isa_irq_state)); + if (pci_enabled) { + ioapic_init(isa_irq_state); + } + isa_irq = i8259 = kvm_i8259_init(cpu_irq[0]); + ioapic_irq_hack = isa_irq; + } else +#endif + { + i8259 = i8259_init(cpu_irq[0]); + isa_irq_state = qemu_mallocz(sizeof(*isa_irq_state)); + isa_irq_state->i8259 = i8259; + if (pci_enabled) { + ioapic_init(isa_irq_state); + } + isa_irq = qemu_allocate_irqs(isa_irq_handler, isa_irq_state, 24); + } + + if (pci_enabled) { + pci_bus = i440fx_init(&i440fx_state, &piix3_devfn, isa_irq, ram_size); + } else { + pci_bus = NULL; + i440fx_state = NULL; + isa_bus_new(NULL); + } + isa_bus_irqs(isa_irq); + + pc_register_ferr_irq(isa_get_irq(13)); + + pc_vga_init(pci_enabled? pci_bus: NULL); + + /* init basic PC hardware */ + pc_basic_device_init(isa_irq, &floppy_controller, &rtc_state); + + for(i = 0; i < nb_nics; i++) { + NICInfo *nd = &nd_table[i]; + + if (!pci_enabled || (nd->model && strcmp(nd->model, "ne2k_isa") == 0)) + pc_init_ne2k_isa(nd); + else + pci_nic_init_nofail(nd, "rtl8139", NULL); + } + + if (drive_get_max_bus(IF_IDE) >= MAX_IDE_BUS) { + fprintf(stderr, "qemu: too many IDE bus\n"); + exit(1); + } + + for(i = 0; i < MAX_IDE_BUS * MAX_IDE_DEVS; i++) { + hd[i] = drive_get(IF_IDE, i / MAX_IDE_DEVS, i % MAX_IDE_DEVS); + } + + if (pci_enabled) { + PCIDevice *dev; + dev = pci_piix3_ide_init(pci_bus, hd, piix3_devfn + 1); + idebus[0] = qdev_get_child_bus(&dev->qdev, "ide.0"); + idebus[1] = qdev_get_child_bus(&dev->qdev, "ide.1"); + } else { + for(i = 0; i < MAX_IDE_BUS; i++) { + ISADevice *dev; + dev = isa_ide_init(ide_iobase[i], ide_iobase2[i], ide_irq[i], + hd[MAX_IDE_DEVS * i], hd[MAX_IDE_DEVS * i + 1]); + idebus[i] = qdev_get_child_bus(&dev->qdev, "ide.0"); + } + } + + audio_init(isa_irq, pci_enabled ? pci_bus : NULL); + + pc_cmos_init(below_4g_mem_size, above_4g_mem_size, boot_device, + idebus[0], idebus[1], floppy_controller, rtc_state); + + if (pci_enabled && usb_enabled) { + usb_uhci_piix3_init(pci_bus, piix3_devfn + 2); + } + + if (pci_enabled && acpi_enabled) { + uint8_t *eeprom_buf = qemu_mallocz(8 * 256); /* XXX: make this persistent */ + i2c_bus *smbus; + + cmos_s3 = qemu_allocate_irqs(pc_cmos_set_s3_resume, rtc_state, 1); + smi_irq = qemu_allocate_irqs(pc_acpi_smi_interrupt, first_cpu, 1); + /* TODO: Populate SPD eeprom data. */ + smbus = piix4_pm_init(pci_bus, piix3_devfn + 3, 0xb100, + isa_get_irq(9), *cmos_s3, *smi_irq, + kvm_enabled()); + for (i = 0; i < 8; i++) { + DeviceState *eeprom; + eeprom = qdev_create((BusState *)smbus, "smbus-eeprom"); + qdev_prop_set_uint8(eeprom, "address", 0x50 + i); + qdev_prop_set_ptr(eeprom, "data", eeprom_buf + (i * 256)); + qdev_init_nofail(eeprom); + } + } + + if (i440fx_state) { + i440fx_init_memory_mappings(i440fx_state); + } + + if (pci_enabled) { + pc_pci_device_init(pci_bus); + } +} + +static void pc_init_pci(ram_addr_t ram_size, + const char *boot_device, + const char *kernel_filename, + const char *kernel_cmdline, + const char *initrd_filename, + const char *cpu_model) +{ + pc_init1(ram_size, boot_device, + kernel_filename, kernel_cmdline, + initrd_filename, cpu_model, 1); +} + +static void pc_init_isa(ram_addr_t ram_size, + const char *boot_device, + const char *kernel_filename, + const char *kernel_cmdline, + const char *initrd_filename, + const char *cpu_model) +{ + if (cpu_model == NULL) + cpu_model = "486"; + pc_init1(ram_size, boot_device, + kernel_filename, kernel_cmdline, + initrd_filename, cpu_model, 0); +} + +static QEMUMachine pc_machine = { + .name = "pc-0.14", + .alias = "pc", + .desc = "Standard PC", + .init = pc_init_pci, + .max_cpus = 255, + .is_default = 1, +}; + +static QEMUMachine pc_machine_v0_13 = { + .name = "pc-0.13", + .desc = "Standard PC", + .init = pc_init_pci, + .max_cpus = 255, + .compat_props = (GlobalProperty[]) { + { + .driver = "virtio-9p-pci", + .property = "vectors", + .value = stringify(0), + },{ + .driver = "VGA", + .property = "rombar", + .value = stringify(0), + },{ + .driver = "vmware-svga", + .property = "rombar", + .value = stringify(0), + },{ + .driver = "PCI", + .property = "command_serr_enable", + .value = "off", + }, + { /* end of list */ } + }, +}; + +static QEMUMachine pc_machine_v0_12 = { + .name = "pc-0.12", + .desc = "Standard PC", + .init = pc_init_pci, + .max_cpus = 255, + .compat_props = (GlobalProperty[]) { + { + .driver = "virtio-serial-pci", + .property = "max_ports", + .value = stringify(1), + },{ + .driver = "virtio-serial-pci", + .property = "vectors", + .value = stringify(0), + },{ + .driver = "VGA", + .property = "rombar", + .value = stringify(0), + },{ + .driver = "vmware-svga", + .property = "rombar", + .value = stringify(0), + },{ + .driver = "PCI", + .property = "command_serr_enable", + .value = "off", + }, + { /* end of list */ } + } +}; + +static QEMUMachine pc_machine_v0_11 = { + .name = "pc-0.11", + .desc = "Standard PC, qemu 0.11", + .init = pc_init_pci, + .max_cpus = 255, + .compat_props = (GlobalProperty[]) { + { + .driver = "virtio-blk-pci", + .property = "vectors", + .value = stringify(0), + },{ + .driver = "virtio-serial-pci", + .property = "max_ports", + .value = stringify(1), + },{ + .driver = "virtio-serial-pci", + .property = "vectors", + .value = stringify(0), + },{ + .driver = "ide-drive", + .property = "ver", + .value = "0.11", + },{ + .driver = "scsi-disk", + .property = "ver", + .value = "0.11", + },{ + .driver = "PCI", + .property = "rombar", + .value = stringify(0), + },{ + .driver = "PCI", + .property = "command_serr_enable", + .value = "off", + }, + { /* end of list */ } + } +}; + +static QEMUMachine pc_machine_v0_10 = { + .name = "pc-0.10", + .desc = "Standard PC, qemu 0.10", + .init = pc_init_pci, + .max_cpus = 255, + .compat_props = (GlobalProperty[]) { + { + .driver = "virtio-blk-pci", + .property = "class", + .value = stringify(PCI_CLASS_STORAGE_OTHER), + },{ + .driver = "virtio-serial-pci", + .property = "class", + .value = stringify(PCI_CLASS_DISPLAY_OTHER), + },{ + .driver = "virtio-serial-pci", + .property = "max_ports", + .value = stringify(1), + },{ + .driver = "virtio-serial-pci", + .property = "vectors", + .value = stringify(0), + },{ + .driver = "virtio-net-pci", + .property = "vectors", + .value = stringify(0), + },{ + .driver = "virtio-blk-pci", + .property = "vectors", + .value = stringify(0), + },{ + .driver = "ide-drive", + .property = "ver", + .value = "0.10", + },{ + .driver = "scsi-disk", + .property = "ver", + .value = "0.10", + },{ + .driver = "PCI", + .property = "rombar", + .value = stringify(0), + },{ + .driver = "PCI", + .property = "command_serr_enable", + .value = "off", + }, + { /* end of list */ } + }, +}; + +static QEMUMachine isapc_machine = { + .name = "isapc", + .desc = "ISA-only PC", + .init = pc_init_isa, + .max_cpus = 1, +}; + +static void pc_machine_init(void) +{ + qemu_register_machine(&pc_machine); + qemu_register_machine(&pc_machine_v0_13); + qemu_register_machine(&pc_machine_v0_12); + qemu_register_machine(&pc_machine_v0_11); + qemu_register_machine(&pc_machine_v0_10); + qemu_register_machine(&isapc_machine); +} + +machine_init(pc_machine_init); diff -Nru qemu-kvm-0.12.5+noroms/hw/pcspk.c qemu-kvm-0.14.1/hw/pcspk.c --- qemu-kvm-0.12.5+noroms/hw/pcspk.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/pcspk.c 2011-05-11 13:29:46.000000000 +0000 @@ -56,7 +56,7 @@ { struct kvm_pit_state pit_state; - if (kvm_enabled() && qemu_kvm_pit_in_kernel()) { + if (kvm_enabled() && kvm_pit_in_kernel()) { kvm_get_pit(kvm_context, &pit_state); pit->channels[2].mode = pit_state.channels[2].mode; pit->channels[2].count = pit_state.channels[2].count; @@ -71,7 +71,7 @@ static void kvm_set_pit_ch2(PITState *pit, struct kvm_pit_state *inkernel_state) { - if (kvm_enabled() && qemu_kvm_pit_in_kernel()) { + if (kvm_enabled() && kvm_pit_in_kernel()) { inkernel_state->channels[2].mode = pit->channels[2].mode; inkernel_state->channels[2].count = pit->channels[2].count; inkernel_state->channels[2].count_load_time = diff -Nru qemu-kvm-0.12.5+noroms/hw/petalogix_s3adsp1800_mmu.c qemu-kvm-0.14.1/hw/petalogix_s3adsp1800_mmu.c --- qemu-kvm-0.12.5+noroms/hw/petalogix_s3adsp1800_mmu.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/petalogix_s3adsp1800_mmu.c 2011-05-11 13:29:46.000000000 +0000 @@ -34,17 +34,26 @@ #include "xilinx.h" #include "loader.h" #include "elf.h" +#include "blockdev.h" #define LMB_BRAM_SIZE (128 * 1024) #define FLASH_SIZE (16 * 1024 * 1024) -static uint32_t bootstrap_pc; +static struct +{ + uint32_t bootstrap_pc; + uint32_t cmdline; + uint32_t fdt; +} boot_info; static void main_cpu_reset(void *opaque) { CPUState *env = opaque; + cpu_reset(env); - env->sregs[SR_PC] = bootstrap_pc; + env->regs[5] = boot_info.cmdline; + env->regs[7] = boot_info.fdt; + env->sregs[SR_PC] = boot_info.bootstrap_pc; } #define BINARY_DEVICE_TREE_FILE "petalogix-s3adsp1800.dtb" @@ -96,6 +105,11 @@ return fdt_size; } +static uint64_t translate_kernel_address(void *opaque, uint64_t addr) +{ + return addr - 0x30000000LL; +} + static void petalogix_s3adsp1800_init(ram_addr_t ram_size, const char *boot_device, @@ -124,20 +138,20 @@ qemu_register_reset(main_cpu_reset, env); /* Attach emulated BRAM through the LMB. */ - phys_lmb_bram = qemu_ram_alloc(LMB_BRAM_SIZE); + phys_lmb_bram = qemu_ram_alloc(NULL, "petalogix_s3adsp1800.lmb_bram", + LMB_BRAM_SIZE); cpu_register_physical_memory(0x00000000, LMB_BRAM_SIZE, phys_lmb_bram | IO_MEM_RAM); - phys_ram = qemu_ram_alloc(ram_size); + phys_ram = qemu_ram_alloc(NULL, "petalogix_s3adsp1800.ram", ram_size); cpu_register_physical_memory(ddr_base, ram_size, phys_ram | IO_MEM_RAM); - phys_flash = qemu_ram_alloc(FLASH_SIZE); + phys_flash = qemu_ram_alloc(NULL, "petalogix_s3adsp1800.flash", FLASH_SIZE); dinfo = drive_get(IF_PFLASH, 0, 0); - pflash_cfi02_register(0xa0000000, phys_flash, + pflash_cfi01_register(0xa0000000, phys_flash, dinfo ? dinfo->bdrv : NULL, (64 * 1024), FLASH_SIZE >> 16, - 1, 1, 0x0000, 0x0000, 0x0000, 0x0000, - 0x555, 0x2aa); + 1, 0x89, 0x18, 0x0000, 0x0, 1); cpu_irq = microblaze_pic_init_cpu(env); dev = xilinx_intc_create(0x81800000, cpu_irq[0], 2); @@ -152,41 +166,48 @@ if (kernel_filename) { uint64_t entry, low, high; - int kcmdline_len; uint32_t base32; /* Boots a kernel elf binary. */ - kernel_size = load_elf(kernel_filename, 0, + kernel_size = load_elf(kernel_filename, NULL, NULL, &entry, &low, &high, 1, ELF_MACHINE, 0); base32 = entry; if (base32 == 0xc0000000) { - kernel_size = load_elf(kernel_filename, -0x30000000LL, - &entry, NULL, NULL, + kernel_size = load_elf(kernel_filename, translate_kernel_address, + NULL, &entry, NULL, NULL, 1, ELF_MACHINE, 0); } /* Always boot into physical ram. */ - bootstrap_pc = ddr_base + (entry & 0x0fffffff); + boot_info.bootstrap_pc = ddr_base + (entry & 0x0fffffff); + + /* If it wasn't an ELF image, try an u-boot image. */ + if (kernel_size < 0) { + target_phys_addr_t uentry, loadaddr; + + kernel_size = load_uimage(kernel_filename, &uentry, &loadaddr, 0); + boot_info.bootstrap_pc = uentry; + high = (loadaddr + kernel_size + 3) & ~3; + } + + /* Not an ELF image nor an u-boot image, try a RAW image. */ if (kernel_size < 0) { - /* If we failed loading ELF's try a raw image. */ kernel_size = load_image_targphys(kernel_filename, ddr_base, ram_size); - bootstrap_pc = ddr_base; + boot_info.bootstrap_pc = ddr_base; + high = (ddr_base + kernel_size + 3) & ~3; } - env->regs[5] = ddr_base + kernel_size + 8192; - if (kernel_cmdline && (kcmdline_len = strlen(kernel_cmdline))) { - pstrcpy_targphys("cmdline", env->regs[5], 256, kernel_cmdline); + boot_info.cmdline = high + 4096; + if (kernel_cmdline && strlen(kernel_cmdline)) { + pstrcpy_targphys("cmdline", boot_info.cmdline, 256, kernel_cmdline); } - env->regs[6] = 0; /* Provide a device-tree. */ - env->regs[7] = ddr_base + kernel_size + 256; - petalogix_load_device_tree(env->regs[7], ram_size, - env->regs[6], 0, + boot_info.fdt = boot_info.cmdline + 4096; + petalogix_load_device_tree(boot_info.fdt, ram_size, + 0, 0, kernel_cmdline); } - - env->sregs[SR_PC] = bootstrap_pc; } static QEMUMachine petalogix_s3adsp1800_machine = { diff -Nru qemu-kvm-0.12.5+noroms/hw/pflash_cfi01.c qemu-kvm-0.14.1/hw/pflash_cfi01.c --- qemu-kvm-0.12.5+noroms/hw/pflash_cfi01.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/pflash_cfi01.c 2011-05-11 13:29:46.000000000 +0000 @@ -72,6 +72,7 @@ uint8_t cfi_len; uint8_t cfi_table[0x52]; target_phys_addr_t counter; + unsigned int writeblock_size; QEMUTimer *timer; ram_addr_t off; int fl_mem; @@ -96,7 +97,7 @@ } static uint32_t pflash_read (pflash_t *pfl, target_phys_addr_t offset, - int width) + int width, int be) { target_phys_addr_t boff; uint32_t ret; @@ -125,29 +126,29 @@ __func__, offset, ret); break; case 2: -#if defined(TARGET_WORDS_BIGENDIAN) - ret = p[offset] << 8; - ret |= p[offset + 1]; -#else - ret = p[offset]; - ret |= p[offset + 1] << 8; -#endif + if (be) { + ret = p[offset] << 8; + ret |= p[offset + 1]; + } else { + ret = p[offset]; + ret |= p[offset + 1] << 8; + } DPRINTF("%s: data offset " TARGET_FMT_plx " %04x\n", __func__, offset, ret); break; case 4: -#if defined(TARGET_WORDS_BIGENDIAN) - ret = p[offset] << 24; - ret |= p[offset + 1] << 16; - ret |= p[offset + 2] << 8; - ret |= p[offset + 3]; -#else - ret = p[offset]; - ret |= p[offset + 1] << 8; - ret |= p[offset + 1] << 8; - ret |= p[offset + 2] << 16; - ret |= p[offset + 3] << 24; -#endif + if (be) { + ret = p[offset] << 24; + ret |= p[offset + 1] << 16; + ret |= p[offset + 2] << 8; + ret |= p[offset + 3]; + } else { + ret = p[offset]; + ret |= p[offset + 1] << 8; + ret |= p[offset + 1] << 8; + ret |= p[offset + 2] << 16; + ret |= p[offset + 3] << 24; + } DPRINTF("%s: data offset " TARGET_FMT_plx " %08x\n", __func__, offset, ret); break; @@ -165,6 +166,22 @@ ret = pfl->status; DPRINTF("%s: status %x\n", __func__, ret); break; + case 0x90: + switch (boff) { + case 0: + ret = pfl->ident[0] << 8 | pfl->ident[1]; + DPRINTF("%s: Manufacturer Code %04x\n", __func__, ret); + break; + case 1: + ret = pfl->ident[2] << 8 | pfl->ident[3]; + DPRINTF("%s: Device ID Code %04x\n", __func__, ret); + break; + default: + DPRINTF("%s: Read Device Information boff=%x\n", __func__, boff); + ret = 0; + break; + } + break; case 0x98: /* Query mode */ if (boff > pfl->cfi_len) ret = 0; @@ -196,7 +213,7 @@ } static inline void pflash_data_write(pflash_t *pfl, target_phys_addr_t offset, - uint32_t value, int width) + uint32_t value, int width, int be) { uint8_t *p = pfl->storage; @@ -206,40 +223,36 @@ switch (width) { case 1: p[offset] = value; - pflash_update(pfl, offset, 1); break; case 2: -#if defined(TARGET_WORDS_BIGENDIAN) - p[offset] = value >> 8; - p[offset + 1] = value; -#else - p[offset] = value; - p[offset + 1] = value >> 8; -#endif - pflash_update(pfl, offset, 2); + if (be) { + p[offset] = value >> 8; + p[offset + 1] = value; + } else { + p[offset] = value; + p[offset + 1] = value >> 8; + } break; case 4: -#if defined(TARGET_WORDS_BIGENDIAN) - p[offset] = value >> 24; - p[offset + 1] = value >> 16; - p[offset + 2] = value >> 8; - p[offset + 3] = value; -#else - p[offset] = value; - p[offset + 1] = value >> 8; - p[offset + 2] = value >> 16; - p[offset + 3] = value >> 24; -#endif - pflash_update(pfl, offset, 4); + if (be) { + p[offset] = value >> 24; + p[offset + 1] = value >> 16; + p[offset + 2] = value >> 8; + p[offset + 3] = value; + } else { + p[offset] = value; + p[offset + 1] = value >> 8; + p[offset + 2] = value >> 16; + p[offset + 3] = value >> 24; + } break; } } static void pflash_write(pflash_t *pfl, target_phys_addr_t offset, - uint32_t value, int width) + uint32_t value, int width, int be) { - target_phys_addr_t boff; uint8_t *p; uint8_t cmd; @@ -248,14 +261,10 @@ DPRINTF("%s: writing offset " TARGET_FMT_plx " value %08x width %d wcycle 0x%x\n", __func__, offset, value, width, pfl->wcycle); - /* Set the device in I/O access mode */ - cpu_register_physical_memory(pfl->base, pfl->total_len, pfl->fl_mem); - boff = offset & (pfl->sector_len - 1); - - if (pfl->width == 2) - boff = boff >> 1; - else if (pfl->width == 4) - boff = boff >> 2; + if (!pfl->wcycle) { + /* Set the device in I/O access mode */ + cpu_register_physical_memory(pfl->base, pfl->total_len, pfl->fl_mem); + } switch (pfl->wcycle) { case 0: @@ -290,6 +299,10 @@ DPRINTF("%s: Read status register\n", __func__); pfl->cmd = cmd; return; + case 0x90: /* Read Device ID */ + DPRINTF("%s: Read Device information\n", __func__); + pfl->cmd = cmd; + return; case 0x98: /* CFI query */ DPRINTF("%s: CFI query\n", __func__); break; @@ -311,7 +324,8 @@ case 0x10: /* Single Byte Program */ case 0x40: /* Single Byte Program */ DPRINTF("%s: Single Byte Program\n", __func__); - pflash_data_write(pfl, offset, value, width); + pflash_data_write(pfl, offset, value, width, be); + pflash_update(pfl, offset, width); pfl->status |= 0x80; /* Ready! */ pfl->wcycle = 0; break; @@ -359,13 +373,18 @@ case 2: switch (pfl->cmd) { case 0xe8: /* Block write */ - pflash_data_write(pfl, offset, value, width); + pflash_data_write(pfl, offset, value, width, be); pfl->status |= 0x80; if (!pfl->counter) { + target_phys_addr_t mask = pfl->writeblock_size - 1; + mask = ~mask; + DPRINTF("%s: block write finished\n", __func__); pfl->wcycle++; + /* Flush the entire write buffer onto backing storage. */ + pflash_update(pfl, offset & mask, pfl->writeblock_size); } pfl->counter--; @@ -413,57 +432,110 @@ } -static uint32_t pflash_readb (void *opaque, target_phys_addr_t addr) +static uint32_t pflash_readb_be(void *opaque, target_phys_addr_t addr) { - return pflash_read(opaque, addr, 1); + return pflash_read(opaque, addr, 1, 1); } -static uint32_t pflash_readw (void *opaque, target_phys_addr_t addr) +static uint32_t pflash_readb_le(void *opaque, target_phys_addr_t addr) +{ + return pflash_read(opaque, addr, 1, 0); +} + +static uint32_t pflash_readw_be(void *opaque, target_phys_addr_t addr) +{ + pflash_t *pfl = opaque; + + return pflash_read(pfl, addr, 2, 1); +} + +static uint32_t pflash_readw_le(void *opaque, target_phys_addr_t addr) +{ + pflash_t *pfl = opaque; + + return pflash_read(pfl, addr, 2, 0); +} + +static uint32_t pflash_readl_be(void *opaque, target_phys_addr_t addr) { pflash_t *pfl = opaque; - return pflash_read(pfl, addr, 2); + return pflash_read(pfl, addr, 4, 1); +} + +static uint32_t pflash_readl_le(void *opaque, target_phys_addr_t addr) +{ + pflash_t *pfl = opaque; + + return pflash_read(pfl, addr, 4, 0); +} + +static void pflash_writeb_be(void *opaque, target_phys_addr_t addr, + uint32_t value) +{ + pflash_write(opaque, addr, value, 1, 1); +} + +static void pflash_writeb_le(void *opaque, target_phys_addr_t addr, + uint32_t value) +{ + pflash_write(opaque, addr, value, 1, 0); } -static uint32_t pflash_readl (void *opaque, target_phys_addr_t addr) +static void pflash_writew_be(void *opaque, target_phys_addr_t addr, + uint32_t value) { pflash_t *pfl = opaque; - return pflash_read(pfl, addr, 4); + pflash_write(pfl, addr, value, 2, 1); } -static void pflash_writeb (void *opaque, target_phys_addr_t addr, - uint32_t value) +static void pflash_writew_le(void *opaque, target_phys_addr_t addr, + uint32_t value) { - pflash_write(opaque, addr, value, 1); + pflash_t *pfl = opaque; + + pflash_write(pfl, addr, value, 2, 0); } -static void pflash_writew (void *opaque, target_phys_addr_t addr, - uint32_t value) +static void pflash_writel_be(void *opaque, target_phys_addr_t addr, + uint32_t value) { pflash_t *pfl = opaque; - pflash_write(pfl, addr, value, 2); + pflash_write(pfl, addr, value, 4, 1); } -static void pflash_writel (void *opaque, target_phys_addr_t addr, - uint32_t value) +static void pflash_writel_le(void *opaque, target_phys_addr_t addr, + uint32_t value) { pflash_t *pfl = opaque; - pflash_write(pfl, addr, value, 4); + pflash_write(pfl, addr, value, 4, 0); } -static CPUWriteMemoryFunc * const pflash_write_ops[] = { - &pflash_writeb, - &pflash_writew, - &pflash_writel, +static CPUWriteMemoryFunc * const pflash_write_ops_be[] = { + &pflash_writeb_be, + &pflash_writew_be, + &pflash_writel_be, +}; + +static CPUReadMemoryFunc * const pflash_read_ops_be[] = { + &pflash_readb_be, + &pflash_readw_be, + &pflash_readl_be, }; -static CPUReadMemoryFunc * const pflash_read_ops[] = { - &pflash_readb, - &pflash_readw, - &pflash_readl, +static CPUWriteMemoryFunc * const pflash_write_ops_le[] = { + &pflash_writeb_le, + &pflash_writew_le, + &pflash_writel_le, +}; + +static CPUReadMemoryFunc * const pflash_read_ops_le[] = { + &pflash_readb_le, + &pflash_readw_le, + &pflash_readl_le, }; /* Count trailing zeroes of a 32 bits quantity */ @@ -490,7 +562,9 @@ } if (!(n & 0x1)) { ret++; +#if 0 /* This is not necessary as n is never 0 */ n = n >> 1; +#endif } #if 0 /* This is not necessary as n is never 0 */ if (!n) @@ -504,7 +578,8 @@ BlockDriverState *bs, uint32_t sector_len, int nb_blocs, int width, uint16_t id0, uint16_t id1, - uint16_t id2, uint16_t id3) + uint16_t id2, uint16_t id3, + int be) { pflash_t *pfl; target_phys_addr_t total_len; @@ -523,8 +598,15 @@ /* FIXME: Allocate ram ourselves. */ pfl->storage = qemu_get_ram_ptr(off); - pfl->fl_mem = cpu_register_io_memory( - pflash_read_ops, pflash_write_ops, pfl); + if (be) { + pfl->fl_mem = cpu_register_io_memory(pflash_read_ops_be, + pflash_write_ops_be, pfl, + DEVICE_NATIVE_ENDIAN); + } else { + pfl->fl_mem = cpu_register_io_memory(pflash_read_ops_le, + pflash_write_ops_le, pfl, + DEVICE_NATIVE_ENDIAN); + } pfl->off = off; cpu_register_physical_memory(base, total_len, off | pfl->fl_mem | IO_MEM_ROMD); @@ -606,7 +688,13 @@ pfl->cfi_table[0x28] = 0x02; pfl->cfi_table[0x29] = 0x00; /* Max number of bytes in multi-bytes write */ - pfl->cfi_table[0x2A] = 0x0B; + if (width == 1) { + pfl->cfi_table[0x2A] = 0x08; + } else { + pfl->cfi_table[0x2A] = 0x0B; + } + pfl->writeblock_size = 1 << pfl->cfi_table[0x2A]; + pfl->cfi_table[0x2B] = 0x00; /* Number of erase block regions (uniform) */ pfl->cfi_table[0x2C] = 0x01; diff -Nru qemu-kvm-0.12.5+noroms/hw/pflash_cfi02.c qemu-kvm-0.14.1/hw/pflash_cfi02.c --- qemu-kvm-0.12.5+noroms/hw/pflash_cfi02.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/pflash_cfi02.c 2011-05-11 13:29:46.000000000 +0000 @@ -103,13 +103,14 @@ pfl->cmd = 0; } -static uint32_t pflash_read (pflash_t *pfl, uint32_t offset, int width) +static uint32_t pflash_read (pflash_t *pfl, target_phys_addr_t offset, + int width, int be) { - uint32_t boff; + target_phys_addr_t boff; uint32_t ret; uint8_t *p; - DPRINTF("%s: offset " TARGET_FMT_lx "\n", __func__, offset); + DPRINTF("%s: offset " TARGET_FMT_plx "\n", __func__, offset); ret = -1; if (pfl->rom_mode) { /* Lazy reset of to ROMD mode */ @@ -140,27 +141,27 @@ // DPRINTF("%s: data offset %08x %02x\n", __func__, offset, ret); break; case 2: -#if defined(TARGET_WORDS_BIGENDIAN) - ret = p[offset] << 8; - ret |= p[offset + 1]; -#else - ret = p[offset]; - ret |= p[offset + 1] << 8; -#endif + if (be) { + ret = p[offset] << 8; + ret |= p[offset + 1]; + } else { + ret = p[offset]; + ret |= p[offset + 1] << 8; + } // DPRINTF("%s: data offset %08x %04x\n", __func__, offset, ret); break; case 4: -#if defined(TARGET_WORDS_BIGENDIAN) - ret = p[offset] << 24; - ret |= p[offset + 1] << 16; - ret |= p[offset + 2] << 8; - ret |= p[offset + 3]; -#else - ret = p[offset]; - ret |= p[offset + 1] << 8; - ret |= p[offset + 2] << 16; - ret |= p[offset + 3] << 24; -#endif + if (be) { + ret = p[offset] << 24; + ret |= p[offset + 1] << 16; + ret |= p[offset + 2] << 8; + ret |= p[offset + 3]; + } else { + ret = p[offset]; + ret |= p[offset + 1] << 8; + ret |= p[offset + 2] << 16; + ret |= p[offset + 3] << 24; + } // DPRINTF("%s: data offset %08x %08x\n", __func__, offset, ret); break; } @@ -184,7 +185,7 @@ default: goto flash_read; } - DPRINTF("%s: ID " TARGET_FMT_ld " %x\n", __func__, boff, ret); + DPRINTF("%s: ID " TARGET_FMT_pld " %x\n", __func__, boff, ret); break; case 0xA0: case 0x10: @@ -222,10 +223,10 @@ } } -static void pflash_write (pflash_t *pfl, uint32_t offset, uint32_t value, - int width) +static void pflash_write (pflash_t *pfl, target_phys_addr_t offset, + uint32_t value, int width, int be) { - uint32_t boff; + target_phys_addr_t boff; uint8_t *p; uint8_t cmd; @@ -237,11 +238,11 @@ #endif goto reset_flash; } - DPRINTF("%s: offset " TARGET_FMT_lx " %08x %d %d\n", __func__, + DPRINTF("%s: offset " TARGET_FMT_plx " %08x %d %d\n", __func__, offset, value, width, pfl->wcycle); offset &= pfl->chip_len - 1; - DPRINTF("%s: offset " TARGET_FMT_lx " %08x %d\n", __func__, + DPRINTF("%s: offset " TARGET_FMT_plx " %08x %d\n", __func__, offset, value, width); boff = offset & (pfl->sector_len - 1); if (pfl->width == 2) @@ -263,7 +264,7 @@ return; } if (boff != pfl->unlock_addr[0] || cmd != 0xAA) { - DPRINTF("%s: unlock0 failed " TARGET_FMT_lx " %02x %04x\n", + DPRINTF("%s: unlock0 failed " TARGET_FMT_plx " %02x %04x\n", __func__, boff, cmd, pfl->unlock_addr[0]); goto reset_flash; } @@ -273,7 +274,7 @@ /* We started an unlock sequence */ check_unlock1: if (boff != pfl->unlock_addr[1] || cmd != 0x55) { - DPRINTF("%s: unlock1 failed " TARGET_FMT_lx " %02x\n", __func__, + DPRINTF("%s: unlock1 failed " TARGET_FMT_plx " %02x\n", __func__, boff, cmd); goto reset_flash; } @@ -282,7 +283,7 @@ case 2: /* We finished an unlock sequence */ if (!pfl->bypass && boff != pfl->unlock_addr[0]) { - DPRINTF("%s: command failed " TARGET_FMT_lx " %02x\n", __func__, + DPRINTF("%s: command failed " TARGET_FMT_plx " %02x\n", __func__, boff, cmd); goto reset_flash; } @@ -307,7 +308,7 @@ /* We need another unlock sequence */ goto check_unlock0; case 0xA0: - DPRINTF("%s: write data offset " TARGET_FMT_lx " %08x %d\n", + DPRINTF("%s: write data offset " TARGET_FMT_plx " %08x %d\n", __func__, offset, value, width); p = pfl->storage; switch (width) { @@ -316,27 +317,27 @@ pflash_update(pfl, offset, 1); break; case 2: -#if defined(TARGET_WORDS_BIGENDIAN) - p[offset] &= value >> 8; - p[offset + 1] &= value; -#else - p[offset] &= value; - p[offset + 1] &= value >> 8; -#endif + if (be) { + p[offset] &= value >> 8; + p[offset + 1] &= value; + } else { + p[offset] &= value; + p[offset + 1] &= value >> 8; + } pflash_update(pfl, offset, 2); break; case 4: -#if defined(TARGET_WORDS_BIGENDIAN) - p[offset] &= value >> 24; - p[offset + 1] &= value >> 16; - p[offset + 2] &= value >> 8; - p[offset + 3] &= value; -#else - p[offset] &= value; - p[offset + 1] &= value >> 8; - p[offset + 2] &= value >> 16; - p[offset + 3] &= value >> 24; -#endif + if (be) { + p[offset] &= value >> 24; + p[offset + 1] &= value >> 16; + p[offset + 2] &= value >> 8; + p[offset + 3] &= value; + } else { + p[offset] &= value; + p[offset + 1] &= value >> 8; + p[offset + 2] &= value >> 16; + p[offset + 3] &= value >> 24; + } pflash_update(pfl, offset, 4); break; } @@ -378,7 +379,7 @@ switch (cmd) { case 0x10: if (boff != pfl->unlock_addr[0]) { - DPRINTF("%s: chip erase: invalid address " TARGET_FMT_lx "\n", + DPRINTF("%s: chip erase: invalid address " TARGET_FMT_plx "\n", __func__, offset); goto reset_flash; } @@ -395,7 +396,7 @@ /* Sector erase */ p = pfl->storage; offset &= ~(pfl->sector_len - 1); - DPRINTF("%s: start sector erase at " TARGET_FMT_lx "\n", __func__, + DPRINTF("%s: start sector erase at " TARGET_FMT_plx "\n", __func__, offset); memset(p + offset, 0xFF, pfl->sector_len); pflash_update(pfl, offset, pfl->sector_len); @@ -451,57 +452,110 @@ } -static uint32_t pflash_readb (void *opaque, target_phys_addr_t addr) +static uint32_t pflash_readb_be(void *opaque, target_phys_addr_t addr) +{ + return pflash_read(opaque, addr, 1, 1); +} + +static uint32_t pflash_readb_le(void *opaque, target_phys_addr_t addr) { - return pflash_read(opaque, addr, 1); + return pflash_read(opaque, addr, 1, 0); } -static uint32_t pflash_readw (void *opaque, target_phys_addr_t addr) +static uint32_t pflash_readw_be(void *opaque, target_phys_addr_t addr) { pflash_t *pfl = opaque; - return pflash_read(pfl, addr, 2); + return pflash_read(pfl, addr, 2, 1); } -static uint32_t pflash_readl (void *opaque, target_phys_addr_t addr) +static uint32_t pflash_readw_le(void *opaque, target_phys_addr_t addr) { pflash_t *pfl = opaque; - return pflash_read(pfl, addr, 4); + return pflash_read(pfl, addr, 2, 0); } -static void pflash_writeb (void *opaque, target_phys_addr_t addr, - uint32_t value) +static uint32_t pflash_readl_be(void *opaque, target_phys_addr_t addr) { - pflash_write(opaque, addr, value, 1); + pflash_t *pfl = opaque; + + return pflash_read(pfl, addr, 4, 1); } -static void pflash_writew (void *opaque, target_phys_addr_t addr, - uint32_t value) +static uint32_t pflash_readl_le(void *opaque, target_phys_addr_t addr) { pflash_t *pfl = opaque; - pflash_write(pfl, addr, value, 2); + return pflash_read(pfl, addr, 4, 0); +} + +static void pflash_writeb_be(void *opaque, target_phys_addr_t addr, + uint32_t value) +{ + pflash_write(opaque, addr, value, 1, 1); } -static void pflash_writel (void *opaque, target_phys_addr_t addr, - uint32_t value) +static void pflash_writeb_le(void *opaque, target_phys_addr_t addr, + uint32_t value) +{ + pflash_write(opaque, addr, value, 1, 0); +} + +static void pflash_writew_be(void *opaque, target_phys_addr_t addr, + uint32_t value) +{ + pflash_t *pfl = opaque; + + pflash_write(pfl, addr, value, 2, 1); +} + +static void pflash_writew_le(void *opaque, target_phys_addr_t addr, + uint32_t value) { pflash_t *pfl = opaque; - pflash_write(pfl, addr, value, 4); + pflash_write(pfl, addr, value, 2, 0); } -static CPUWriteMemoryFunc * const pflash_write_ops[] = { - &pflash_writeb, - &pflash_writew, - &pflash_writel, +static void pflash_writel_be(void *opaque, target_phys_addr_t addr, + uint32_t value) +{ + pflash_t *pfl = opaque; + + pflash_write(pfl, addr, value, 4, 1); +} + +static void pflash_writel_le(void *opaque, target_phys_addr_t addr, + uint32_t value) +{ + pflash_t *pfl = opaque; + + pflash_write(pfl, addr, value, 4, 0); +} + +static CPUWriteMemoryFunc * const pflash_write_ops_be[] = { + &pflash_writeb_be, + &pflash_writew_be, + &pflash_writel_be, +}; + +static CPUReadMemoryFunc * const pflash_read_ops_be[] = { + &pflash_readb_be, + &pflash_readw_be, + &pflash_readl_be, }; -static CPUReadMemoryFunc * const pflash_read_ops[] = { - &pflash_readb, - &pflash_readw, - &pflash_readl, +static CPUWriteMemoryFunc * const pflash_write_ops_le[] = { + &pflash_writeb_le, + &pflash_writew_le, + &pflash_writel_le, +}; + +static CPUReadMemoryFunc * const pflash_read_ops_le[] = { + &pflash_readb_le, + &pflash_readw_le, + &pflash_readl_le, }; /* Count trailing zeroes of a 32 bits quantity */ @@ -528,7 +582,9 @@ } if (!(n & 0x1)) { ret++; +#if 0 /* This is not necessary as n is never 0 */ n = n >> 1; +#endif } #if 0 /* This is not necessary as n is never 0 */ if (!n) @@ -543,7 +599,8 @@ int nb_blocs, int nb_mappings, int width, uint16_t id0, uint16_t id1, uint16_t id2, uint16_t id3, - uint16_t unlock_addr0, uint16_t unlock_addr1) + uint16_t unlock_addr0, uint16_t unlock_addr1, + int be) { pflash_t *pfl; int32_t chip_len; @@ -559,8 +616,15 @@ pfl = qemu_mallocz(sizeof(pflash_t)); /* FIXME: Allocate ram ourselves. */ pfl->storage = qemu_get_ram_ptr(off); - pfl->fl_mem = cpu_register_io_memory(pflash_read_ops, pflash_write_ops, - pfl); + if (be) { + pfl->fl_mem = cpu_register_io_memory(pflash_read_ops_be, + pflash_write_ops_be, + pfl, DEVICE_NATIVE_ENDIAN); + } else { + pfl->fl_mem = cpu_register_io_memory(pflash_read_ops_le, + pflash_write_ops_le, + pfl, DEVICE_NATIVE_ENDIAN); + } pfl->off = off; pfl->base = base; pfl->chip_len = chip_len; diff -Nru qemu-kvm-0.12.5+noroms/hw/piix4.c qemu-kvm-0.14.1/hw/piix4.c --- qemu-kvm-0.12.5+noroms/hw/piix4.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/piix4.c 2011-05-11 13:29:46.000000000 +0000 @@ -87,14 +87,12 @@ uint8_t *pci_conf; isa_bus_new(&d->qdev); - register_savevm("PIIX4", 0, 2, piix_save, piix_load, d); + register_savevm(&d->qdev, "PIIX4", 0, 2, piix_save, piix_load, d); pci_conf = d->config; pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_INTEL); pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_INTEL_82371AB_0); // 82371AB/EB/MB PIIX4 PCI-to-ISA bridge pci_config_set_class(pci_conf, PCI_CLASS_BRIDGE_ISA); - pci_conf[PCI_HEADER_TYPE] = - PCI_HEADER_TYPE_NORMAL | PCI_HEADER_TYPE_MULTI_FUNCTION; // header_type = PCI_multifunction, generic piix4_dev = d; qemu_register_reset(piix4_reset, d); @@ -105,7 +103,7 @@ { PCIDevice *d; - d = pci_create_simple(bus, devfn, "PIIX4"); + d = pci_create_simple_multifunction(bus, devfn, true, "PIIX4"); return d->devfn; } @@ -115,6 +113,7 @@ .qdev.desc = "ISA bridge", .qdev.size = sizeof(PCIDevice), .qdev.no_user = 1, + .no_hotplug = 1, .init = piix4_initfn, },{ /* end of list */ diff -Nru qemu-kvm-0.12.5+noroms/hw/piix_pci.c qemu-kvm-0.14.1/hw/piix_pci.c --- qemu-kvm-0.12.5+noroms/hw/piix_pci.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/piix_pci.c 2011-05-11 13:29:46.000000000 +0000 @@ -28,8 +28,13 @@ #include "pci_host.h" #include "isa.h" #include "sysbus.h" +#include "range.h" +#include "kvm.h" -#include "qemu-kvm.h" +/* + * I440FX chipset data sheet. + * http://download.intel.com/design/chipsets/datashts/29054901.pdf + */ typedef PCIHostState I440FXState; @@ -46,6 +51,11 @@ PIIX3State *piix3; }; + +#define I440FX_PAM 0x59 +#define I440FX_PAM_SIZE 7 +#define I440FX_SMRAM 0x72 + static void piix3_set_irq(void *opaque, int irq_num, int level); /* return the global irq number corresponding to a given device irq @@ -90,16 +100,12 @@ int i, r; uint32_t smram, addr; - if (kvm_enabled()) { - /* FIXME: Support remappings and protection changes. */ - return; - } - update_pam(d, 0xf0000, 0x100000, (d->dev.config[0x59] >> 4) & 3); + update_pam(d, 0xf0000, 0x100000, (d->dev.config[I440FX_PAM] >> 4) & 3); for(i = 0; i < 12; i++) { - r = (d->dev.config[(i >> 1) + 0x5a] >> ((i & 1) * 4)) & 3; + r = (d->dev.config[(i >> 1) + (I440FX_PAM + 1)] >> ((i & 1) * 4)) & 3; update_pam(d, 0xc0000 + 0x4000 * i, 0xc0000 + 0x4000 * (i + 1), r); } - smram = d->dev.config[0x72]; + smram = d->dev.config[I440FX_SMRAM]; if ((d->smm_enabled && (smram & 0x08)) || (smram & 0x40)) { cpu_register_physical_memory(0xa0000, 0x20000, 0xa0000); } else { @@ -110,8 +116,10 @@ } } -void i440fx_set_smm(PCII440FXState *d, int val) +static void i440fx_set_smm(int val, void *arg) { + PCII440FXState *d = arg; + val = (val != 0); if (d->smm_enabled != val) { d->smm_enabled = val; @@ -138,8 +146,10 @@ /* XXX: implement SMRAM.D_LOCK */ pci_default_write_config(dev, address, val, len); - if ((address >= 0x59 && address <= 0x5f) || address == 0x72) + if (ranges_overlap(address, len, I440FX_PAM, I440FX_PAM_SIZE) || + range_covers_byte(address, len, I440FX_SMRAM)) { i440fx_update_memory_mappings(d); + } } static int i440fx_load_old(QEMUFile* f, void *opaque, int version_id) @@ -200,16 +210,16 @@ pci_config_set_device_id(d->dev.config, PCI_DEVICE_ID_INTEL_82441); d->dev.config[0x08] = 0x02; // revision pci_config_set_class(d->dev.config, PCI_CLASS_BRIDGE_HOST); - d->dev.config[PCI_HEADER_TYPE] = PCI_HEADER_TYPE_NORMAL; // header_type - d->dev.config[0x72] = 0x02; /* SMRAM */ + d->dev.config[I440FX_SMRAM] = 0x02; + cpu_smm_register(&i440fx_set_smm, d); return 0; } static PIIX3State *piix3_dev; -PCIBus *i440fx_init(PCII440FXState **pi440fx_state, int *piix3_devfn, qemu_irq *pic) +PCIBus *i440fx_init(PCII440FXState **pi440fx_state, int *piix3_devfn, qemu_irq *pic, ram_addr_t ram_size) { DeviceState *dev; PCIBus *b; @@ -227,13 +237,18 @@ *pi440fx_state = DO_UPCAST(PCII440FXState, dev, d); piix3 = DO_UPCAST(PIIX3State, dev, - pci_create_simple(b, -1, "PIIX3")); + pci_create_simple_multifunction(b, -1, true, "PIIX3")); piix3->pic = pic; pci_bus_irqs(b, piix3_set_irq, pci_slot_get_pirq, piix3, 4); (*pi440fx_state)->piix3 = piix3; *piix3_devfn = piix3->dev.devfn; + ram_size = ram_size / 8 / 1024 / 1024; + if (ram_size > 255) + ram_size = 255; + (*pi440fx_state)->dev.config[0x57]=ram_size; + piix3_dev = piix3; return b; @@ -333,8 +348,6 @@ pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_INTEL); pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_INTEL_82371SB_0); // 82371SB PIIX3 PCI-to-ISA bridge (Step A1) pci_config_set_class(pci_conf, PCI_CLASS_BRIDGE_ISA); - pci_conf[PCI_HEADER_TYPE] = - PCI_HEADER_TYPE_NORMAL | PCI_HEADER_TYPE_MULTI_FUNCTION; // header_type = PCI_multifunction, generic qemu_register_reset(piix3_reset, d); return 0; @@ -347,6 +360,7 @@ .qdev.size = sizeof(PCII440FXState), .qdev.vmsd = &vmstate_i440fx, .qdev.no_user = 1, + .no_hotplug = 1, .init = i440fx_initfn, .config_write = i440fx_write_config, },{ @@ -355,6 +369,7 @@ .qdev.size = sizeof(PIIX3State), .qdev.vmsd = &vmstate_piix3, .qdev.no_user = 1, + .no_hotplug = 1, .init = piix3_initfn, },{ /* end of list */ @@ -364,6 +379,7 @@ static SysBusDeviceInfo i440fx_pcihost_info = { .init = i440fx_pcihost_initfn, .qdev.name = "i440FX-pcihost", + .qdev.fw_name = "pci", .qdev.size = sizeof(I440FXState), .qdev.no_user = 1, }; diff -Nru qemu-kvm-0.12.5+noroms/hw/pl011.c qemu-kvm-0.14.1/hw/pl011.c --- qemu-kvm-0.12.5+noroms/hw/pl011.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/pl011.c 2011-05-11 13:29:46.000000000 +0000 @@ -292,7 +292,8 @@ pl011_state *s = FROM_SYSBUS(pl011_state, dev); iomemtype = cpu_register_io_memory(pl011_readfn, - pl011_writefn, s); + pl011_writefn, s, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, 0x1000,iomemtype); sysbus_init_irq(dev, &s->irq); s->id = id; @@ -306,7 +307,7 @@ qemu_chr_add_handlers(s->chr, pl011_can_receive, pl011_receive, pl011_event, s); } - register_savevm("pl011_uart", -1, 1, pl011_save, pl011_load, s); + register_savevm(&dev->qdev, "pl011_uart", -1, 1, pl011_save, pl011_load, s); return 0; } diff -Nru qemu-kvm-0.12.5+noroms/hw/pl022.c qemu-kvm-0.14.1/hw/pl022.c --- qemu-kvm-0.12.5+noroms/hw/pl022.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/pl022.c 2011-05-11 13:29:46.000000000 +0000 @@ -294,12 +294,13 @@ int iomemtype; iomemtype = cpu_register_io_memory(pl022_readfn, - pl022_writefn, s); + pl022_writefn, s, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, 0x1000, iomemtype); sysbus_init_irq(dev, &s->irq); s->ssi = ssi_create_bus(&dev->qdev, "ssi"); pl022_reset(s); - register_savevm("pl022_ssp", -1, 1, pl022_save, pl022_load, s); + register_savevm(&dev->qdev, "pl022_ssp", -1, 1, pl022_save, pl022_load, s); return 0; } diff -Nru qemu-kvm-0.12.5+noroms/hw/pl031.c qemu-kvm-0.14.1/hw/pl031.c --- qemu-kvm-0.12.5+noroms/hw/pl031.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/pl031.c 2011-05-11 13:29:46.000000000 +0000 @@ -44,6 +44,21 @@ uint32_t is; } pl031_state; +static const VMStateDescription vmstate_pl031 = { + .name = "pl031", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(tick_offset, pl031_state), + VMSTATE_UINT32(mr, pl031_state), + VMSTATE_UINT32(lr, pl031_state), + VMSTATE_UINT32(cr, pl031_state), + VMSTATE_UINT32(im, pl031_state), + VMSTATE_UINT32(is, pl031_state), + VMSTATE_END_OF_LIST() + } +}; + static const unsigned char pl031_id[] = { 0x31, 0x10, 0x14, 0x00, /* Device ID */ 0x0d, 0xf0, 0x05, 0xb1 /* Cell ID */ @@ -189,7 +204,8 @@ pl031_state *s = FROM_SYSBUS(pl031_state, dev); struct tm tm; - iomemtype = cpu_register_io_memory(pl031_readfn, pl031_writefn, s); + iomemtype = cpu_register_io_memory(pl031_readfn, pl031_writefn, s, + DEVICE_NATIVE_ENDIAN); if (iomemtype == -1) { hw_error("pl031_init: Can't register I/O memory\n"); } @@ -205,9 +221,17 @@ return 0; } +static SysBusDeviceInfo pl031_info = { + .init = pl031_init, + .qdev.name = "pl031", + .qdev.size = sizeof(pl031_state), + .qdev.vmsd = &vmstate_pl031, + .qdev.no_user = 1, +}; + static void pl031_register_devices(void) { - sysbus_register_dev("pl031", sizeof(pl031_state), pl031_init); + sysbus_register_withprop(&pl031_info); } device_init(pl031_register_devices) diff -Nru qemu-kvm-0.12.5+noroms/hw/pl050.c qemu-kvm-0.14.1/hw/pl050.c --- qemu-kvm-0.12.5+noroms/hw/pl050.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/pl050.c 2011-05-11 13:29:46.000000000 +0000 @@ -21,6 +21,20 @@ int is_mouse; } pl050_state; +static const VMStateDescription vmstate_pl050 = { + .name = "pl050", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(cr, pl050_state), + VMSTATE_UINT32(clk, pl050_state), + VMSTATE_UINT32(last, pl050_state), + VMSTATE_INT32(pending, pl050_state), + VMSTATE_INT32(is_mouse, pl050_state), + VMSTATE_END_OF_LIST() + } +}; + #define PL050_TXEMPTY (1 << 6) #define PL050_TXBUSY (1 << 5) #define PL050_RXFULL (1 << 4) @@ -128,7 +142,8 @@ int iomemtype; iomemtype = cpu_register_io_memory(pl050_readfn, - pl050_writefn, s); + pl050_writefn, s, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, 0x1000, iomemtype); sysbus_init_irq(dev, &s->irq); s->is_mouse = is_mouse; @@ -136,7 +151,6 @@ s->dev = ps2_mouse_init(pl050_update, s); else s->dev = ps2_kbd_init(pl050_update, s); - /* ??? Save/restore. */ return 0; } @@ -150,12 +164,24 @@ return pl050_init(dev, 1); } +static SysBusDeviceInfo pl050_kbd_info = { + .init = pl050_init_keyboard, + .qdev.name = "pl050_keyboard", + .qdev.size = sizeof(pl050_state), + .qdev.vmsd = &vmstate_pl050, +}; + +static SysBusDeviceInfo pl050_mouse_info = { + .init = pl050_init_mouse, + .qdev.name = "pl050_mouse", + .qdev.size = sizeof(pl050_state), + .qdev.vmsd = &vmstate_pl050, +}; + static void pl050_register_devices(void) { - sysbus_register_dev("pl050_keyboard", sizeof(pl050_state), - pl050_init_keyboard); - sysbus_register_dev("pl050_mouse", sizeof(pl050_state), - pl050_init_mouse); + sysbus_register_withprop(&pl050_kbd_info); + sysbus_register_withprop(&pl050_mouse_info); } device_init(pl050_register_devices) diff -Nru qemu-kvm-0.12.5+noroms/hw/pl061.c qemu-kvm-0.14.1/hw/pl061.c --- qemu-kvm-0.12.5+noroms/hw/pl061.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/pl061.c 2011-05-11 13:29:46.000000000 +0000 @@ -297,13 +297,14 @@ pl061_state *s = FROM_SYSBUS(pl061_state, dev); iomemtype = cpu_register_io_memory(pl061_readfn, - pl061_writefn, s); + pl061_writefn, s, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, 0x1000, iomemtype); sysbus_init_irq(dev, &s->irq); qdev_init_gpio_in(&dev->qdev, pl061_set_irq, 8); qdev_init_gpio_out(&dev->qdev, s->out, 8); pl061_reset(s); - register_savevm("pl061_gpio", -1, 1, pl061_save, pl061_load, s); + register_savevm(&dev->qdev, "pl061_gpio", -1, 1, pl061_save, pl061_load, s); return 0; } diff -Nru qemu-kvm-0.12.5+noroms/hw/pl080.c qemu-kvm-0.14.1/hw/pl080.c --- qemu-kvm-0.12.5+noroms/hw/pl080.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/pl080.c 2011-05-11 13:29:46.000000000 +0000 @@ -52,6 +52,43 @@ qemu_irq irq; } pl080_state; +static const VMStateDescription vmstate_pl080_channel = { + .name = "pl080_channel", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(src, pl080_channel), + VMSTATE_UINT32(dest, pl080_channel), + VMSTATE_UINT32(lli, pl080_channel), + VMSTATE_UINT32(ctrl, pl080_channel), + VMSTATE_UINT32(conf, pl080_channel), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_pl080 = { + .name = "pl080", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8(tc_int, pl080_state), + VMSTATE_UINT8(tc_mask, pl080_state), + VMSTATE_UINT8(err_int, pl080_state), + VMSTATE_UINT8(err_mask, pl080_state), + VMSTATE_UINT32(conf, pl080_state), + VMSTATE_UINT32(sync, pl080_state), + VMSTATE_UINT32(req_single, pl080_state), + VMSTATE_UINT32(req_burst, pl080_state), + VMSTATE_UINT8(tc_int, pl080_state), + VMSTATE_UINT8(tc_int, pl080_state), + VMSTATE_UINT8(tc_int, pl080_state), + VMSTATE_STRUCT_ARRAY(chan, pl080_state, PL080_MAX_CHANNELS, + 1, vmstate_pl080_channel, pl080_channel), + VMSTATE_INT32(running, pl080_state), + VMSTATE_END_OF_LIST() + } +}; + static const unsigned char pl080_id[] = { 0x80, 0x10, 0x04, 0x0a, 0x0d, 0xf0, 0x05, 0xb1 }; @@ -325,11 +362,11 @@ pl080_state *s = FROM_SYSBUS(pl080_state, dev); iomemtype = cpu_register_io_memory(pl080_readfn, - pl080_writefn, s); + pl080_writefn, s, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, 0x1000, iomemtype); sysbus_init_irq(dev, &s->irq); s->nchannels = nchannels; - /* ??? Save/restore. */ return 0; } @@ -343,12 +380,28 @@ return pl08x_init(dev, 2); } +static SysBusDeviceInfo pl080_info = { + .init = pl080_init, + .qdev.name = "pl080", + .qdev.size = sizeof(pl080_state), + .qdev.vmsd = &vmstate_pl080, + .qdev.no_user = 1, +}; + +static SysBusDeviceInfo pl081_info = { + .init = pl081_init, + .qdev.name = "pl081", + .qdev.size = sizeof(pl080_state), + .qdev.vmsd = &vmstate_pl080, + .qdev.no_user = 1, +}; + /* The PL080 and PL081 are the same except for the number of channels they implement (8 and 2 respectively). */ static void pl080_register_devices(void) { - sysbus_register_dev("pl080", sizeof(pl080_state), pl080_init); - sysbus_register_dev("pl081", sizeof(pl080_state), pl081_init); + sysbus_register_withprop(&pl080_info); + sysbus_register_withprop(&pl081_info); } device_init(pl080_register_devices) diff -Nru qemu-kvm-0.12.5+noroms/hw/pl110.c qemu-kvm-0.14.1/hw/pl110.c --- qemu-kvm-0.12.5+noroms/hw/pl110.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/pl110.c 2011-05-11 13:29:46.000000000 +0000 @@ -48,6 +48,28 @@ qemu_irq irq; } pl110_state; +static const VMStateDescription vmstate_pl110 = { + .name = "pl110", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_INT32(versatile, pl110_state), + VMSTATE_UINT32_ARRAY(timing, pl110_state, 4), + VMSTATE_UINT32(cr, pl110_state), + VMSTATE_UINT32(upbase, pl110_state), + VMSTATE_UINT32(lpbase, pl110_state), + VMSTATE_UINT32(int_status, pl110_state), + VMSTATE_UINT32(int_mask, pl110_state), + VMSTATE_INT32(cols, pl110_state), + VMSTATE_INT32(rows, pl110_state), + VMSTATE_UINT32(bpp, pl110_state), + VMSTATE_INT32(invalidate, pl110_state), + VMSTATE_UINT32_ARRAY(pallette, pl110_state, 256), + VMSTATE_UINT32_ARRAY(raw_pallette, pl110_state, 128), + VMSTATE_END_OF_LIST() + } +}; + static const unsigned char pl110_id[] = { 0x10, 0x11, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; @@ -358,13 +380,13 @@ int iomemtype; iomemtype = cpu_register_io_memory(pl110_readfn, - pl110_writefn, s); + pl110_writefn, s, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, 0x1000, iomemtype); sysbus_init_irq(dev, &s->irq); s->ds = graphic_console_init(pl110_update_display, pl110_invalidate_display, NULL, NULL, s); - /* ??? Save/restore. */ return 0; } @@ -375,11 +397,26 @@ return pl110_init(dev); } +static SysBusDeviceInfo pl110_info = { + .init = pl110_init, + .qdev.name = "pl110", + .qdev.size = sizeof(pl110_state), + .qdev.vmsd = &vmstate_pl110, + .qdev.no_user = 1, +}; + +static SysBusDeviceInfo pl110_versatile_info = { + .init = pl110_versatile_init, + .qdev.name = "pl110_versatile", + .qdev.size = sizeof(pl110_state), + .qdev.vmsd = &vmstate_pl110, + .qdev.no_user = 1, +}; + static void pl110_register_devices(void) { - sysbus_register_dev("pl110", sizeof(pl110_state), pl110_init); - sysbus_register_dev("pl110_versatile", sizeof(pl110_state), - pl110_versatile_init); + sysbus_register_withprop(&pl110_info); + sysbus_register_withprop(&pl110_versatile_info); } device_init(pl110_register_devices) diff -Nru qemu-kvm-0.12.5+noroms/hw/pl181.c qemu-kvm-0.14.1/hw/pl181.c --- qemu-kvm-0.12.5+noroms/hw/pl181.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/pl181.c 2011-05-11 13:29:46.000000000 +0000 @@ -7,9 +7,9 @@ * This code is licenced under the GPL. */ +#include "blockdev.h" #include "sysbus.h" #include "sd.h" -#include "sysemu.h" //#define DEBUG_PL181 1 @@ -182,39 +182,40 @@ static void pl181_fifo_run(pl181_state *s) { uint32_t bits; - uint32_t value; + uint32_t value = 0; int n; - int limit; int is_read; is_read = (s->datactrl & PL181_DATA_DIRECTION) != 0; if (s->datacnt != 0 && (!is_read || sd_data_ready(s->card)) && !s->linux_hack) { - limit = is_read ? PL181_FIFO_LEN : 0; - n = 0; - value = 0; - while (s->datacnt && s->fifo_len != limit) { - if (is_read) { + if (is_read) { + n = 0; + while (s->datacnt && s->fifo_len < PL181_FIFO_LEN) { value |= (uint32_t)sd_read_data(s->card) << (n * 8); + s->datacnt--; n++; if (n == 4) { pl181_fifo_push(s, value); - value = 0; n = 0; + value = 0; } - } else { + } + if (n != 0) { + pl181_fifo_push(s, value); + } + } else { /* write */ + n = 0; + while (s->datacnt > 0 && (s->fifo_len > 0 || n > 0)) { if (n == 0) { value = pl181_fifo_pop(s); n = 4; } + n--; + s->datacnt--; sd_write_data(s->card, value & 0xff); value >>= 8; - n--; } - s->datacnt--; - } - if (n && is_read) { - pl181_fifo_push(s, value); } } s->status &= ~(PL181_STATUS_RX_FIFO | PL181_STATUS_TX_FIFO); @@ -449,15 +450,15 @@ { int iomemtype; pl181_state *s = FROM_SYSBUS(pl181_state, dev); - BlockDriverState *bd; + DriveInfo *dinfo; - iomemtype = cpu_register_io_memory(pl181_readfn, - pl181_writefn, s); + iomemtype = cpu_register_io_memory(pl181_readfn, pl181_writefn, s, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, 0x1000, iomemtype); sysbus_init_irq(dev, &s->irq[0]); sysbus_init_irq(dev, &s->irq[1]); - bd = qdev_init_bdrv(&dev->qdev, IF_SD); - s->card = sd_init(bd, 0); + dinfo = drive_get_next(IF_SD); + s->card = sd_init(dinfo ? dinfo->bdrv : NULL, 0); qemu_register_reset(pl181_reset, s); pl181_reset(s); /* ??? Save/restore. */ diff -Nru qemu-kvm-0.12.5+noroms/hw/pl190.c qemu-kvm-0.14.1/hw/pl190.c --- qemu-kvm-0.12.5+noroms/hw/pl190.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/pl190.c 2011-05-11 13:29:46.000000000 +0000 @@ -21,7 +21,6 @@ uint32_t soft_level; uint32_t irq_enable; uint32_t fiq_select; - uint32_t default_addr; uint8_t vect_control[16]; uint32_t vect_addr[PL190_NUM_PRIO]; /* Mask containing interrupts with higher priority than this one. */ @@ -186,7 +185,7 @@ s->priority = s->prev_prio[s->priority]; break; case 13: /* DEFVECTADDR */ - s->default_addr = val; + s->vect_addr[16] = val; break; case 0xc0: /* ITCR */ if (val) { @@ -212,8 +211,9 @@ pl190_write }; -static void pl190_reset(pl190_state *s) +static void pl190_reset(DeviceState *d) { + pl190_state *s = DO_UPCAST(pl190_state, busdev.qdev, d); int i; for (i = 0; i < 16; i++) @@ -233,19 +233,46 @@ int iomemtype; iomemtype = cpu_register_io_memory(pl190_readfn, - pl190_writefn, s); + pl190_writefn, s, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, 0x1000, iomemtype); qdev_init_gpio_in(&dev->qdev, pl190_set_irq, 32); sysbus_init_irq(dev, &s->irq); sysbus_init_irq(dev, &s->fiq); - pl190_reset(s); - /* ??? Save/restore. */ return 0; } +static const VMStateDescription vmstate_pl190 = { + .name = "pl190", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(level, pl190_state), + VMSTATE_UINT32(soft_level, pl190_state), + VMSTATE_UINT32(irq_enable, pl190_state), + VMSTATE_UINT32(fiq_select, pl190_state), + VMSTATE_UINT8_ARRAY(vect_control, pl190_state, 16), + VMSTATE_UINT32_ARRAY(vect_addr, pl190_state, PL190_NUM_PRIO), + VMSTATE_UINT32_ARRAY(prio_mask, pl190_state, PL190_NUM_PRIO+1), + VMSTATE_INT32(protected, pl190_state), + VMSTATE_INT32(priority, pl190_state), + VMSTATE_INT32_ARRAY(prev_prio, pl190_state, PL190_NUM_PRIO), + VMSTATE_END_OF_LIST() + } +}; + +static SysBusDeviceInfo pl190_info = { + .init = pl190_init, + .qdev.name = "pl190", + .qdev.size = sizeof(pl190_state), + .qdev.vmsd = &vmstate_pl190, + .qdev.reset = pl190_reset, + .qdev.no_user = 1, +}; + static void pl190_register_devices(void) { - sysbus_register_dev("pl190", sizeof(pl190_state), pl190_init); + sysbus_register_withprop(&pl190_info); } device_init(pl190_register_devices) diff -Nru qemu-kvm-0.12.5+noroms/hw/pm_smbus.c qemu-kvm-0.14.1/hw/pm_smbus.c --- qemu-kvm-0.12.5+noroms/hw/pm_smbus.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/pm_smbus.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,176 @@ +/* + * PC SMBus implementation + * splitted from acpi.c + * + * Copyright (c) 2006 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2 as published by the Free Software Foundation. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * . + */ +#include "hw.h" +#include "pc.h" +#include "pm_smbus.h" +#include "smbus.h" + +/* no save/load? */ + +#define SMBHSTSTS 0x00 +#define SMBHSTCNT 0x02 +#define SMBHSTCMD 0x03 +#define SMBHSTADD 0x04 +#define SMBHSTDAT0 0x05 +#define SMBHSTDAT1 0x06 +#define SMBBLKDAT 0x07 + +//#define DEBUG + +#ifdef DEBUG +# define SMBUS_DPRINTF(format, ...) printf(format, ## __VA_ARGS__) +#else +# define SMBUS_DPRINTF(format, ...) do { } while (0) +#endif + + +static void smb_transaction(PMSMBus *s) +{ + uint8_t prot = (s->smb_ctl >> 2) & 0x07; + uint8_t read = s->smb_addr & 0x01; + uint8_t cmd = s->smb_cmd; + uint8_t addr = s->smb_addr >> 1; + i2c_bus *bus = s->smbus; + + SMBUS_DPRINTF("SMBus trans addr=0x%02x prot=0x%02x\n", addr, prot); + switch(prot) { + case 0x0: + smbus_quick_command(bus, addr, read); + break; + case 0x1: + if (read) { + s->smb_data0 = smbus_receive_byte(bus, addr); + } else { + smbus_send_byte(bus, addr, cmd); + } + break; + case 0x2: + if (read) { + s->smb_data0 = smbus_read_byte(bus, addr, cmd); + } else { + smbus_write_byte(bus, addr, cmd, s->smb_data0); + } + break; + case 0x3: + if (read) { + uint16_t val; + val = smbus_read_word(bus, addr, cmd); + s->smb_data0 = val; + s->smb_data1 = val >> 8; + } else { + smbus_write_word(bus, addr, cmd, (s->smb_data1 << 8) | s->smb_data0); + } + break; + case 0x5: + if (read) { + s->smb_data0 = smbus_read_block(bus, addr, cmd, s->smb_data); + } else { + smbus_write_block(bus, addr, cmd, s->smb_data, s->smb_data0); + } + break; + default: + goto error; + } + return; + + error: + s->smb_stat |= 0x04; +} + +void smb_ioport_writeb(void *opaque, uint32_t addr, uint32_t val) +{ + PMSMBus *s = opaque; + addr &= 0x3f; + SMBUS_DPRINTF("SMB writeb port=0x%04x val=0x%02x\n", addr, val); + switch(addr) { + case SMBHSTSTS: + s->smb_stat = 0; + s->smb_index = 0; + break; + case SMBHSTCNT: + s->smb_ctl = val; + if (val & 0x40) + smb_transaction(s); + break; + case SMBHSTCMD: + s->smb_cmd = val; + break; + case SMBHSTADD: + s->smb_addr = val; + break; + case SMBHSTDAT0: + s->smb_data0 = val; + break; + case SMBHSTDAT1: + s->smb_data1 = val; + break; + case SMBBLKDAT: + s->smb_data[s->smb_index++] = val; + if (s->smb_index > 31) + s->smb_index = 0; + break; + default: + break; + } +} + +uint32_t smb_ioport_readb(void *opaque, uint32_t addr) +{ + PMSMBus *s = opaque; + uint32_t val; + + addr &= 0x3f; + switch(addr) { + case SMBHSTSTS: + val = s->smb_stat; + break; + case SMBHSTCNT: + s->smb_index = 0; + val = s->smb_ctl & 0x1f; + break; + case SMBHSTCMD: + val = s->smb_cmd; + break; + case SMBHSTADD: + val = s->smb_addr; + break; + case SMBHSTDAT0: + val = s->smb_data0; + break; + case SMBHSTDAT1: + val = s->smb_data1; + break; + case SMBBLKDAT: + val = s->smb_data[s->smb_index++]; + if (s->smb_index > 31) + s->smb_index = 0; + break; + default: + val = 0; + break; + } + SMBUS_DPRINTF("SMB readb port=0x%04x val=0x%02x\n", addr, val); + return val; +} + +void pm_smbus_init(DeviceState *parent, PMSMBus *smb) +{ + smb->smbus = i2c_init_bus(parent, "i2c"); +} diff -Nru qemu-kvm-0.12.5+noroms/hw/pm_smbus.h qemu-kvm-0.14.1/hw/pm_smbus.h --- qemu-kvm-0.12.5+noroms/hw/pm_smbus.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/pm_smbus.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,21 @@ +#ifndef PM_SMBUS_H +#define PM_SMBUS_H + +typedef struct PMSMBus { + i2c_bus *smbus; + + uint8_t smb_stat; + uint8_t smb_ctl; + uint8_t smb_cmd; + uint8_t smb_addr; + uint8_t smb_data0; + uint8_t smb_data1; + uint8_t smb_data[32]; + uint8_t smb_index; +} PMSMBus; + +void pm_smbus_init(DeviceState *parent, PMSMBus *smb); +void smb_ioport_writeb(void *opaque, uint32_t addr, uint32_t val); +uint32_t smb_ioport_readb(void *opaque, uint32_t addr); + +#endif /* !PM_SMBUS_H */ diff -Nru qemu-kvm-0.12.5+noroms/hw/poison.h qemu-kvm-0.14.1/hw/poison.h --- qemu-kvm-0.12.5+noroms/hw/poison.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/poison.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,50 +0,0 @@ -/* Poison identifiers that should not be used when building - target independent device code. */ - -#ifndef HW_POISON_H -#define HW_POISON_H -#ifdef __GNUC__ - -#pragma GCC poison TARGET_I386 -#pragma GCC poison TARGET_X86_64 -#pragma GCC poison TARGET_ALPHA -#pragma GCC poison TARGET_ARM -#pragma GCC poison TARGET_CRIS -#pragma GCC poison TARGET_M68K -#pragma GCC poison TARGET_MIPS -#pragma GCC poison TARGET_MIPS64 -#pragma GCC poison TARGET_PPC -#pragma GCC poison TARGET_PPCEMB -#pragma GCC poison TARGET_PPC64 -#pragma GCC poison TARGET_ABI32 -#pragma GCC poison TARGET_SH4 -#pragma GCC poison TARGET_SPARC -#pragma GCC poison TARGET_SPARC64 - -#pragma GCC poison TARGET_WORDS_BIGENDIAN -#pragma GCC poison BSWAP_NEEDED - -#pragma GCC poison TARGET_LONG_BITS -#pragma GCC poison TARGET_FMT_lx -#pragma GCC poison TARGET_FMT_ld - -#pragma GCC poison TARGET_PAGE_SIZE -#pragma GCC poison TARGET_PAGE_MASK -#pragma GCC poison TARGET_PAGE_BITS -#pragma GCC poison TARGET_PAGE_ALIGN - -#pragma GCC poison CPUState -#pragma GCC poison env - -#pragma GCC poison CPU_INTERRUPT_HARD -#pragma GCC poison CPU_INTERRUPT_EXITTB -#pragma GCC poison CPU_INTERRUPT_TIMER -#pragma GCC poison CPU_INTERRUPT_FIQ -#pragma GCC poison CPU_INTERRUPT_HALT -#pragma GCC poison CPU_INTERRUPT_SMI -#pragma GCC poison CPU_INTERRUPT_DEBUG -#pragma GCC poison CPU_INTERRUPT_VIRQ -#pragma GCC poison CPU_INTERRUPT_NMI - -#endif -#endif diff -Nru qemu-kvm-0.12.5+noroms/hw/ppc405_boards.c qemu-kvm-0.14.1/hw/ppc405_boards.c --- qemu-kvm-0.12.5+noroms/hw/ppc405_boards.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/ppc405_boards.c 2011-05-11 13:29:46.000000000 +0000 @@ -31,6 +31,7 @@ #include "boards.h" #include "qemu-log.h" #include "loader.h" +#include "blockdev.h" #define BIOS_FILENAME "ppc405_rom.bin" #define BIOS_SIZE (2048 * 1024) @@ -163,7 +164,8 @@ fpga = qemu_mallocz(sizeof(ref405ep_fpga_t)); fpga_memory = cpu_register_io_memory(ref405ep_fpga_read, - ref405ep_fpga_write, fpga); + ref405ep_fpga_write, fpga, + DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base, 0x00000100, fpga_memory); qemu_register_reset(&ref405ep_fpga_reset, fpga); } @@ -181,17 +183,18 @@ qemu_irq *pic; ram_addr_t sram_offset, bios_offset, bdloc; target_phys_addr_t ram_bases[2], ram_sizes[2]; - target_ulong sram_size, bios_size; + target_ulong sram_size; + long bios_size; //int phy_addr = 0; //static int phy_addr = 1; - target_ulong kernel_base, kernel_size, initrd_base, initrd_size; + target_ulong kernel_base, initrd_base; + long kernel_size, initrd_size; int linux_boot; int fl_idx, fl_sectors, len; - int ppc_boot_device = boot_device[0]; DriveInfo *dinfo; /* XXX: fix this */ - ram_bases[0] = qemu_ram_alloc(0x08000000); + ram_bases[0] = qemu_ram_alloc(NULL, "ef405ep.ram", 0x08000000); ram_sizes[0] = 0x08000000; ram_bases[1] = 0x00000000; ram_sizes[1] = 0x00000000; @@ -203,7 +206,7 @@ kernel_filename == NULL ? 0 : 1); /* allocate SRAM */ sram_size = 512 * 1024; - sram_offset = qemu_ram_alloc(sram_size); + sram_offset = qemu_ram_alloc(NULL, "ef405ep.sram", sram_size); #ifdef DEBUG_BOARD_INIT printf("%s: register SRAM at offset %08lx\n", __func__, sram_offset); #endif @@ -218,17 +221,18 @@ dinfo = drive_get(IF_PFLASH, 0, fl_idx); if (dinfo) { bios_size = bdrv_getlength(dinfo->bdrv); - bios_offset = qemu_ram_alloc(bios_size); + bios_offset = qemu_ram_alloc(NULL, "ef405ep.bios", bios_size); fl_sectors = (bios_size + 65535) >> 16; #ifdef DEBUG_BOARD_INIT - printf("Register parallel flash %d size " TARGET_FMT_lx - " at offset %08lx addr " TARGET_FMT_lx " '%s' %d\n", + printf("Register parallel flash %d size %lx" + " at offset %08lx addr %lx '%s' %d\n", fl_idx, bios_size, bios_offset, -bios_size, bdrv_get_device_name(dinfo->bdrv), fl_sectors); #endif pflash_cfi02_register((uint32_t)(-bios_size), bios_offset, dinfo->bdrv, 65536, fl_sectors, 1, - 2, 0x0001, 0x22DA, 0x0000, 0x0000, 0x555, 0x2AA); + 2, 0x0001, 0x22DA, 0x0000, 0x0000, 0x555, 0x2AA, + 1); fl_idx++; } else #endif @@ -236,7 +240,7 @@ #ifdef DEBUG_BOARD_INIT printf("Load BIOS from file\n"); #endif - bios_offset = qemu_ram_alloc(BIOS_SIZE); + bios_offset = qemu_ram_alloc(NULL, "ef405ep.bios", BIOS_SIZE); if (bios_name == NULL) bios_name = BIOS_FILENAME; filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); @@ -307,7 +311,7 @@ kernel_filename); exit(1); } - printf("Load kernel size " TARGET_FMT_ld " at " TARGET_FMT_lx, + printf("Load kernel size %ld at " TARGET_FMT_lx, kernel_size, kernel_base); /* load initrd */ if (initrd_filename) { @@ -325,7 +329,6 @@ } env->gpr[4] = initrd_base; env->gpr[5] = initrd_size; - ppc_boot_device = 'm'; if (kernel_cmdline != NULL) { len = strlen(kernel_cmdline); bdloc -= ((len + 255) & ~255); @@ -486,7 +489,8 @@ cpld = qemu_mallocz(sizeof(taihu_cpld_t)); cpld_memory = cpu_register_io_memory(taihu_cpld_read, - taihu_cpld_write, cpld); + taihu_cpld_write, cpld, + DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base, 0x00000100, cpld_memory); qemu_register_reset(&taihu_cpld_reset, cpld); } @@ -499,28 +503,27 @@ const char *cpu_model) { char *filename; - CPUPPCState *env; qemu_irq *pic; ram_addr_t bios_offset; target_phys_addr_t ram_bases[2], ram_sizes[2]; - target_ulong bios_size; - target_ulong kernel_base, kernel_size, initrd_base, initrd_size; + long bios_size; + target_ulong kernel_base, initrd_base; + long kernel_size, initrd_size; int linux_boot; int fl_idx, fl_sectors; - int ppc_boot_device = boot_device[0]; DriveInfo *dinfo; /* RAM is soldered to the board so the size cannot be changed */ - ram_bases[0] = qemu_ram_alloc(0x04000000); + ram_bases[0] = qemu_ram_alloc(NULL, "taihu_405ep.ram-0", 0x04000000); ram_sizes[0] = 0x04000000; - ram_bases[1] = qemu_ram_alloc(0x04000000); + ram_bases[1] = qemu_ram_alloc(NULL, "taihu_405ep.ram-1", 0x04000000); ram_sizes[1] = 0x04000000; ram_size = 0x08000000; #ifdef DEBUG_BOARD_INIT printf("%s: register cpu\n", __func__); #endif - env = ppc405ep_init(ram_bases, ram_sizes, 33333333, &pic, - kernel_filename == NULL ? 0 : 1); + ppc405ep_init(ram_bases, ram_sizes, 33333333, &pic, + kernel_filename == NULL ? 0 : 1); /* allocate and load BIOS */ #ifdef DEBUG_BOARD_INIT printf("%s: register BIOS\n", __func__); @@ -533,16 +536,17 @@ /* XXX: should check that size is 2MB */ // bios_size = 2 * 1024 * 1024; fl_sectors = (bios_size + 65535) >> 16; - bios_offset = qemu_ram_alloc(bios_size); + bios_offset = qemu_ram_alloc(NULL, "taihu_405ep.bios", bios_size); #ifdef DEBUG_BOARD_INIT - printf("Register parallel flash %d size " TARGET_FMT_lx - " at offset %08lx addr " TARGET_FMT_lx " '%s' %d\n", + printf("Register parallel flash %d size %lx" + " at offset %08lx addr %lx '%s' %d\n", fl_idx, bios_size, bios_offset, -bios_size, bdrv_get_device_name(dinfo->bdrv), fl_sectors); #endif pflash_cfi02_register((uint32_t)(-bios_size), bios_offset, dinfo->bdrv, 65536, fl_sectors, 1, - 4, 0x0001, 0x22DA, 0x0000, 0x0000, 0x555, 0x2AA); + 4, 0x0001, 0x22DA, 0x0000, 0x0000, 0x555, 0x2AA, + 1); fl_idx++; } else #endif @@ -552,7 +556,7 @@ #endif if (bios_name == NULL) bios_name = BIOS_FILENAME; - bios_offset = qemu_ram_alloc(BIOS_SIZE); + bios_offset = qemu_ram_alloc(NULL, "taihu_405ep.bios", BIOS_SIZE); filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); if (filename) { bios_size = load_image(filename, qemu_get_ram_ptr(bios_offset)); @@ -576,15 +580,16 @@ bios_size = 32 * 1024 * 1024; fl_sectors = (bios_size + 65535) >> 16; #ifdef DEBUG_BOARD_INIT - printf("Register parallel flash %d size " TARGET_FMT_lx + printf("Register parallel flash %d size %lx" " at offset %08lx addr " TARGET_FMT_lx " '%s'\n", fl_idx, bios_size, bios_offset, (target_ulong)0xfc000000, bdrv_get_device_name(dinfo->bdrv)); #endif - bios_offset = qemu_ram_alloc(bios_size); + bios_offset = qemu_ram_alloc(NULL, "taihu_405ep.flash", bios_size); pflash_cfi02_register(0xfc000000, bios_offset, dinfo->bdrv, 65536, fl_sectors, 1, - 4, 0x0001, 0x22DA, 0x0000, 0x0000, 0x555, 0x2AA); + 4, 0x0001, 0x22DA, 0x0000, 0x0000, 0x555, 0x2AA, + 1); fl_idx++; } /* Register CLPD & LCD display */ @@ -622,7 +627,6 @@ initrd_base = 0; initrd_size = 0; } - ppc_boot_device = 'm'; } else { kernel_base = 0; kernel_size = 0; diff -Nru qemu-kvm-0.12.5+noroms/hw/ppc405_uc.c qemu-kvm-0.14.1/hw/ppc405_uc.c --- qemu-kvm-0.12.5+noroms/hw/ppc405_uc.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/ppc405_uc.c 2011-05-11 13:29:46.000000000 +0000 @@ -68,8 +68,9 @@ stl_phys(bdloc + 0x34, bd->bi_baudrate); for (i = 0; i < 4; i++) stb_phys(bdloc + 0x38 + i, bd->bi_s_version[i]); - for (i = 0; i < 32; i++) - stb_phys(bdloc + 0x3C + i, bd->bi_s_version[i]); + for (i = 0; i < 32; i++) { + stb_phys(bdloc + 0x3C + i, bd->bi_r_version[i]); + } stl_phys(bdloc + 0x5C, bd->bi_plb_busfreq); stl_phys(bdloc + 0x60, bd->bi_pci_busfreq); for (i = 0; i < 6; i++) @@ -107,10 +108,10 @@ uint32_t besr; }; -static target_ulong dcr_read_plb (void *opaque, int dcrn) +static uint32_t dcr_read_plb (void *opaque, int dcrn) { ppc4xx_plb_t *plb; - target_ulong ret; + uint32_t ret; plb = opaque; switch (dcrn) { @@ -132,7 +133,7 @@ return ret; } -static void dcr_write_plb (void *opaque, int dcrn, target_ulong val) +static void dcr_write_plb (void *opaque, int dcrn, uint32_t val) { ppc4xx_plb_t *plb; @@ -189,10 +190,10 @@ uint32_t besr[2]; }; -static target_ulong dcr_read_pob (void *opaque, int dcrn) +static uint32_t dcr_read_pob (void *opaque, int dcrn) { ppc4xx_pob_t *pob; - target_ulong ret; + uint32_t ret; pob = opaque; switch (dcrn) { @@ -212,7 +213,7 @@ return ret; } -static void dcr_write_pob (void *opaque, int dcrn, target_ulong val) +static void dcr_write_pob (void *opaque, int dcrn, uint32_t val) { ppc4xx_pob_t *pob; @@ -383,7 +384,8 @@ #ifdef DEBUG_OPBA printf("%s: offset " TARGET_FMT_plx "\n", __func__, base); #endif - io = cpu_register_io_memory(opba_read, opba_write, opba); + io = cpu_register_io_memory(opba_read, opba_write, opba, + DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base, 0x002, io); qemu_register_reset(ppc4xx_opba_reset, opba); } @@ -410,10 +412,10 @@ EBC0_CFGDATA = 0x013, }; -static target_ulong dcr_read_ebc (void *opaque, int dcrn) +static uint32_t dcr_read_ebc (void *opaque, int dcrn) { ppc4xx_ebc_t *ebc; - target_ulong ret; + uint32_t ret; ebc = opaque; switch (dcrn) { @@ -486,6 +488,7 @@ ret = 0x00000000; break; } + break; default: ret = 0x00000000; break; @@ -494,7 +497,7 @@ return ret; } -static void dcr_write_ebc (void *opaque, int dcrn, target_ulong val) +static void dcr_write_ebc (void *opaque, int dcrn, uint32_t val) { ppc4xx_ebc_t *ebc; @@ -627,20 +630,13 @@ uint32_t pol; }; -static target_ulong dcr_read_dma (void *opaque, int dcrn) +static uint32_t dcr_read_dma (void *opaque, int dcrn) { - ppc405_dma_t *dma; - - dma = opaque; - return 0; } -static void dcr_write_dma (void *opaque, int dcrn, target_ulong val) +static void dcr_write_dma (void *opaque, int dcrn, uint32_t val) { - ppc405_dma_t *dma; - - dma = opaque; } static void ppc405_dma_reset (void *opaque) @@ -738,9 +734,6 @@ static uint32_t ppc405_gpio_readb (void *opaque, target_phys_addr_t addr) { - ppc405_gpio_t *gpio; - - gpio = opaque; #ifdef DEBUG_GPIO printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr); #endif @@ -751,9 +744,6 @@ static void ppc405_gpio_writeb (void *opaque, target_phys_addr_t addr, uint32_t value) { - ppc405_gpio_t *gpio; - - gpio = opaque; #ifdef DEBUG_GPIO printf("%s: addr " TARGET_FMT_plx " val %08" PRIx32 "\n", __func__, addr, value); @@ -762,9 +752,6 @@ static uint32_t ppc405_gpio_readw (void *opaque, target_phys_addr_t addr) { - ppc405_gpio_t *gpio; - - gpio = opaque; #ifdef DEBUG_GPIO printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr); #endif @@ -775,9 +762,6 @@ static void ppc405_gpio_writew (void *opaque, target_phys_addr_t addr, uint32_t value) { - ppc405_gpio_t *gpio; - - gpio = opaque; #ifdef DEBUG_GPIO printf("%s: addr " TARGET_FMT_plx " val %08" PRIx32 "\n", __func__, addr, value); @@ -786,9 +770,6 @@ static uint32_t ppc405_gpio_readl (void *opaque, target_phys_addr_t addr) { - ppc405_gpio_t *gpio; - - gpio = opaque; #ifdef DEBUG_GPIO printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr); #endif @@ -799,9 +780,6 @@ static void ppc405_gpio_writel (void *opaque, target_phys_addr_t addr, uint32_t value) { - ppc405_gpio_t *gpio; - - gpio = opaque; #ifdef DEBUG_GPIO printf("%s: addr " TARGET_FMT_plx " val %08" PRIx32 "\n", __func__, addr, value); @@ -822,9 +800,6 @@ static void ppc405_gpio_reset (void *opaque) { - ppc405_gpio_t *gpio; - - gpio = opaque; } static void ppc405_gpio_init(target_phys_addr_t base) @@ -836,7 +811,8 @@ #ifdef DEBUG_GPIO printf("%s: offset " TARGET_FMT_plx "\n", __func__, base); #endif - io = cpu_register_io_memory(ppc405_gpio_read, ppc405_gpio_write, gpio); + io = cpu_register_io_memory(ppc405_gpio_read, ppc405_gpio_write, gpio, + DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base, 0x038, io); qemu_register_reset(&ppc405_gpio_reset, gpio); } @@ -914,10 +890,10 @@ } } -static target_ulong dcr_read_ocm (void *opaque, int dcrn) +static uint32_t dcr_read_ocm (void *opaque, int dcrn) { ppc405_ocm_t *ocm; - target_ulong ret; + uint32_t ret; ocm = opaque; switch (dcrn) { @@ -941,7 +917,7 @@ return ret; } -static void dcr_write_ocm (void *opaque, int dcrn, target_ulong val) +static void dcr_write_ocm (void *opaque, int dcrn, uint32_t val) { ppc405_ocm_t *ocm; uint32_t isarc, dsarc, isacntl, dsacntl; @@ -994,7 +970,7 @@ ppc405_ocm_t *ocm; ocm = qemu_mallocz(sizeof(ppc405_ocm_t)); - ocm->offset = qemu_ram_alloc(4096); + ocm->offset = qemu_ram_alloc(NULL, "ppc405.ocm", 4096); qemu_register_reset(&ocm_reset, ocm); ppc_dcr_register(env, OCM0_ISARC, ocm, &dcr_read_ocm, &dcr_write_ocm); @@ -1245,7 +1221,8 @@ #ifdef DEBUG_I2C printf("%s: offset " TARGET_FMT_plx "\n", __func__, base); #endif - io = cpu_register_io_memory(i2c_read, i2c_write, i2c); + io = cpu_register_io_memory(i2c_read, i2c_write, i2c, + DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base, 0x011, io); qemu_register_reset(ppc4xx_i2c_reset, i2c); } @@ -1528,7 +1505,7 @@ #ifdef DEBUG_GPT printf("%s: offset " TARGET_FMT_plx "\n", __func__, base); #endif - io = cpu_register_io_memory(gpt_read, gpt_write, gpt); + io = cpu_register_io_memory(gpt_read, gpt_write, gpt, DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base, 0x0d4, io); qemu_register_reset(ppc4xx_gpt_reset, gpt); } @@ -1578,10 +1555,10 @@ static void ppc40x_mal_reset (void *opaque); -static target_ulong dcr_read_mal (void *opaque, int dcrn) +static uint32_t dcr_read_mal (void *opaque, int dcrn) { ppc40x_mal_t *mal; - target_ulong ret; + uint32_t ret; mal = opaque; switch (dcrn) { @@ -1650,7 +1627,7 @@ return ret; } -static void dcr_write_mal (void *opaque, int dcrn, target_ulong val) +static void dcr_write_mal (void *opaque, int dcrn, uint32_t val) { ppc40x_mal_t *mal; int idx; @@ -1951,10 +1928,10 @@ clk_setup(&cpc->clk_setup[PPC405CR_UART_CLK], UART_clk); } -static target_ulong dcr_read_crcpc (void *opaque, int dcrn) +static uint32_t dcr_read_crcpc (void *opaque, int dcrn) { ppc405cr_cpc_t *cpc; - target_ulong ret; + uint32_t ret; cpc = opaque; switch (dcrn) { @@ -1991,7 +1968,7 @@ return ret; } -static void dcr_write_crcpc (void *opaque, int dcrn, target_ulong val) +static void dcr_write_crcpc (void *opaque, int dcrn, uint32_t val) { ppc405cr_cpc_t *cpc; @@ -2182,11 +2159,11 @@ /* Serial ports */ if (serial_hds[0] != NULL) { serial_mm_init(0xef600300, 0, pic[0], PPC_SERIAL_MM_BAUDBASE, - serial_hds[0], 1); + serial_hds[0], 1, 1); } if (serial_hds[1] != NULL) { serial_mm_init(0xef600400, 0, pic[1], PPC_SERIAL_MM_BAUDBASE, - serial_hds[1], 1); + serial_hds[1], 1, 1); } /* IIC controller */ ppc405_i2c_init(0xef600500, pic[2]); @@ -2353,10 +2330,10 @@ clk_setup(&cpc->clk_setup[PPC405EP_UART1_CLK], UART1_clk); } -static target_ulong dcr_read_epcpc (void *opaque, int dcrn) +static uint32_t dcr_read_epcpc (void *opaque, int dcrn) { ppc405ep_cpc_t *cpc; - target_ulong ret; + uint32_t ret; cpc = opaque; switch (dcrn) { @@ -2393,7 +2370,7 @@ return ret; } -static void dcr_write_epcpc (void *opaque, int dcrn, target_ulong val) +static void dcr_write_epcpc (void *opaque, int dcrn, uint32_t val) { ppc405ep_cpc_t *cpc; @@ -2535,11 +2512,11 @@ /* Serial ports */ if (serial_hds[0] != NULL) { serial_mm_init(0xef600300, 0, pic[0], PPC_SERIAL_MM_BAUDBASE, - serial_hds[0], 1); + serial_hds[0], 1, 1); } if (serial_hds[1] != NULL) { serial_mm_init(0xef600400, 0, pic[1], PPC_SERIAL_MM_BAUDBASE, - serial_hds[1], 1); + serial_hds[1], 1, 1); } /* OCM */ ppc405_ocm_init(env); diff -Nru qemu-kvm-0.12.5+noroms/hw/ppc440_bamboo.c qemu-kvm-0.14.1/hw/ppc440_bamboo.c --- qemu-kvm-0.12.5+noroms/hw/ppc440_bamboo.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/ppc440_bamboo.c 2011-05-11 13:29:46.000000000 +0000 @@ -28,18 +28,23 @@ #define BINARY_DEVICE_TREE_FILE "bamboo.dtb" -static void *bamboo_load_device_tree(target_phys_addr_t addr, +/* from u-boot */ +#define KERNEL_ADDR 0x1000000 +#define FDT_ADDR 0x1800000 +#define RAMDISK_ADDR 0x1900000 + +static int bamboo_load_device_tree(target_phys_addr_t addr, uint32_t ramsize, target_phys_addr_t initrd_base, target_phys_addr_t initrd_size, const char *kernel_cmdline) { - void *fdt = NULL; + int ret = -1; #ifdef CONFIG_FDT uint32_t mem_reg_property[] = { 0, 0, ramsize }; char *filename; int fdt_size; - int ret; + void *fdt; filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE); if (!filename) { @@ -76,12 +81,13 @@ if (kvm_enabled()) kvmppc_fdt_update(fdt); - cpu_physical_memory_write (addr, (void *)fdt, fdt_size); + ret = rom_add_blob_fixed(BINARY_DEVICE_TREE_FILE, fdt, fdt_size, addr); + qemu_free(fdt); out: #endif - return fdt; + return ret; } static void bamboo_init(ram_addr_t ram_size, @@ -98,24 +104,14 @@ uint64_t elf_lowaddr; target_phys_addr_t entry = 0; target_phys_addr_t loadaddr = 0; - target_long kernel_size = 0; - target_ulong initrd_base = 0; target_long initrd_size = 0; - target_ulong dt_base = 0; - void *fdt; + int success; int i; /* Setup CPU. */ env = ppc440ep_init(&ram_size, &pcibus, pci_irq_nrs, 1, cpu_model); if (pcibus) { - /* Add virtio console devices */ - for(i = 0; i < MAX_VIRTIO_CONSOLES; i++) { - if (virtcon_hds[i]) { - pci_create_simple(pcibus, -1, "virtio-console-pci"); - } - } - /* Register network interfaces. */ for (i = 0; i < nb_nics; i++) { /* There are no PCI NICs on the Bamboo board, but there are @@ -126,15 +122,15 @@ /* Load kernel. */ if (kernel_filename) { - kernel_size = load_uimage(kernel_filename, &entry, &loadaddr, NULL); - if (kernel_size < 0) { - kernel_size = load_elf(kernel_filename, 0, &elf_entry, &elf_lowaddr, - NULL, 1, ELF_MACHINE, 0); + success = load_uimage(kernel_filename, &entry, &loadaddr, NULL); + if (success < 0) { + success = load_elf(kernel_filename, NULL, NULL, &elf_entry, + &elf_lowaddr, NULL, 1, ELF_MACHINE, 0); entry = elf_entry; loadaddr = elf_lowaddr; } /* XXX try again as binary */ - if (kernel_size < 0) { + if (success < 0) { fprintf(stderr, "qemu: could not load kernel '%s'\n", kernel_filename); exit(1); @@ -143,34 +139,29 @@ /* Load initrd. */ if (initrd_filename) { - initrd_base = kernel_size + loadaddr; - initrd_size = load_image_targphys(initrd_filename, initrd_base, - ram_size - initrd_base); + initrd_size = load_image_targphys(initrd_filename, RAMDISK_ADDR, + ram_size - RAMDISK_ADDR); if (initrd_size < 0) { - fprintf(stderr, "qemu: could not load initial ram disk '%s'\n", - initrd_filename); + fprintf(stderr, "qemu: could not load ram disk '%s' at %x\n", + initrd_filename, RAMDISK_ADDR); exit(1); } } /* If we're loading a kernel directly, we must load the device tree too. */ if (kernel_filename) { - if (initrd_base) - dt_base = initrd_base + initrd_size; - else - dt_base = kernel_size + loadaddr; - - fdt = bamboo_load_device_tree(dt_base, ram_size, - initrd_base, initrd_size, kernel_cmdline); - if (fdt == NULL) { + if (bamboo_load_device_tree(FDT_ADDR, ram_size, RAMDISK_ADDR, + initrd_size, kernel_cmdline) < 0) { fprintf(stderr, "couldn't load device tree\n"); exit(1); } + cpu_synchronize_state(env); + /* Set initial guest state. */ env->gpr[1] = (16<<20) - 8; - env->gpr[3] = dt_base; + env->gpr[3] = FDT_ADDR; env->nip = entry; /* XXX we currently depend on KVM to create some initial TLB entries. */ } @@ -180,14 +171,34 @@ } static QEMUMachine bamboo_machine = { - .name = "bamboo", + .name = "bamboo-0.13", + .alias = "bamboo", + .desc = "bamboo", + .init = bamboo_init, +}; + +static QEMUMachine bamboo_machine_v0_12 = { + .name = "bamboo-0.12", .desc = "bamboo", .init = bamboo_init, + .compat_props = (GlobalProperty[]) { + { + .driver = "virtio-serial-pci", + .property = "max_ports", + .value = stringify(1), + },{ + .driver = "virtio-serial-pci", + .property = "vectors", + .value = stringify(0), + }, + { /* end of list */ } + }, }; static void bamboo_machine_init(void) { qemu_register_machine(&bamboo_machine); + qemu_register_machine(&bamboo_machine_v0_12); } machine_init(bamboo_machine_init); diff -Nru qemu-kvm-0.12.5+noroms/hw/ppc440.c qemu-kvm-0.14.1/hw/ppc440.c --- qemu-kvm-0.12.5+noroms/hw/ppc440.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/ppc440.c 2011-05-11 13:29:46.000000000 +0000 @@ -90,11 +90,11 @@ if (serial_hds[0] != NULL) { serial_mm_init(0xef600300, 0, pic[0], PPC_SERIAL_MM_BAUDBASE, - serial_hds[0], 1); + serial_hds[0], 1, 1); } if (serial_hds[1] != NULL) { serial_mm_init(0xef600400, 0, pic[1], PPC_SERIAL_MM_BAUDBASE, - serial_hds[1], 1); + serial_hds[1], 1, 1); } return env; diff -Nru qemu-kvm-0.12.5+noroms/hw/ppc4xx_devs.c qemu-kvm-0.14.1/hw/ppc4xx_devs.c --- qemu-kvm-0.12.5+noroms/hw/ppc4xx_devs.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/ppc4xx_devs.c 2011-05-11 13:29:46.000000000 +0000 @@ -56,7 +56,7 @@ cpu_clk->cb = NULL; /* We don't care about CPU clock frequency changes */ cpu_clk->opaque = env; /* Set time-base frequency to sysclk */ - tb_clk->cb = ppc_emb_timers_init(env, sysclk); + tb_clk->cb = ppc_emb_timers_init(env, sysclk, PPC_INTERRUPT_PIT); tb_clk->opaque = env; ppc_dcr_init(env, NULL, NULL); /* Register qemu callbacks */ @@ -183,10 +183,10 @@ ppcuic_trigger_irq(uic); } -static target_ulong dcr_read_uic (void *opaque, int dcrn) +static uint32_t dcr_read_uic (void *opaque, int dcrn) { ppcuic_t *uic; - target_ulong ret; + uint32_t ret; uic = opaque; dcrn -= uic->dcr_base; @@ -229,13 +229,13 @@ return ret; } -static void dcr_write_uic (void *opaque, int dcrn, target_ulong val) +static void dcr_write_uic (void *opaque, int dcrn, uint32_t val) { ppcuic_t *uic; uic = opaque; dcrn -= uic->dcr_base; - LOG_UIC("%s: dcr %d val " TARGET_FMT_lx "\n", __func__, dcrn, val); + LOG_UIC("%s: dcr %d val 0x%x\n", __func__, dcrn, val); switch (dcrn) { case DCR_UICSR: uic->uicsr &= ~val; @@ -448,10 +448,10 @@ } } -static target_ulong dcr_read_sdram (void *opaque, int dcrn) +static uint32_t dcr_read_sdram (void *opaque, int dcrn) { ppc4xx_sdram_t *sdram; - target_ulong ret; + uint32_t ret; sdram = opaque; switch (dcrn) { @@ -516,7 +516,7 @@ return ret; } -static void dcr_write_sdram (void *opaque, int dcrn, target_ulong val) +static void dcr_write_sdram (void *opaque, int dcrn, uint32_t val) { ppc4xx_sdram_t *sdram; @@ -619,7 +619,6 @@ /* We pre-initialize RAM banks */ sdram->status = 0x00000000; sdram->cfg = 0x00800000; - sdram_unmap_bcr(sdram); } void ppc4xx_sdram_init (CPUState *env, qemu_irq irq, int nbanks, @@ -668,7 +667,9 @@ unsigned int bank_size = sdram_bank_sizes[j]; if (bank_size <= size_left) { - ram_bases[i] = qemu_ram_alloc(bank_size); + char name[32]; + snprintf(name, sizeof(name), "ppc4xx.sdram%d", i); + ram_bases[i] = qemu_ram_alloc(NULL, name, bank_size); ram_sizes[i] = bank_size; size_left -= bank_size; break; @@ -682,7 +683,7 @@ } ram_size -= size_left; - if (ram_size) + if (size_left) printf("Truncating memory to %d MiB to fit SDRAM controller limits.\n", (int)(ram_size >> 20)); diff -Nru qemu-kvm-0.12.5+noroms/hw/ppc4xx_pci.c qemu-kvm-0.14.1/hw/ppc4xx_pci.c --- qemu-kvm-0.12.5+noroms/hw/ppc4xx_pci.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/ppc4xx_pci.c 2011-05-11 13:29:46.000000000 +0000 @@ -24,7 +24,6 @@ #include "ppc4xx.h" #include "pci.h" #include "pci_host.h" -#include "bswap.h" #undef DEBUG #ifdef DEBUG @@ -102,10 +101,6 @@ { PPC4xxPCIState *ppc4xx_pci = opaque; -#ifdef TARGET_WORDS_BIGENDIAN - value = bswap32(value); -#endif - ppc4xx_pci->pci_state.config_reg = value & ~0x3; } @@ -120,10 +115,6 @@ { struct PPC4xxPCIState *pci = opaque; -#ifdef TARGET_WORDS_BIGENDIAN - value = bswap32(value); -#endif - /* We ignore all target attempts at PCI configuration, effectively * assuming a bidirectional 1:1 mapping of PLB and PCI space. */ @@ -251,10 +242,6 @@ value = 0; } -#ifdef TARGET_WORDS_BIGENDIAN - value = bswap32(value); -#endif - return value; } @@ -372,19 +359,21 @@ /* CFGADDR */ index = cpu_register_io_memory(pci4xx_cfgaddr_read, - pci4xx_cfgaddr_write, controller); + pci4xx_cfgaddr_write, controller, + DEVICE_LITTLE_ENDIAN); if (index < 0) goto free; cpu_register_physical_memory(config_space + PCIC0_CFGADDR, 4, index); /* CFGDATA */ - index = pci_host_data_register_mmio(&controller->pci_state); + index = pci_host_data_register_mmio(&controller->pci_state, 1); if (index < 0) goto free; cpu_register_physical_memory(config_space + PCIC0_CFGDATA, 4, index); /* Internal registers */ - index = cpu_register_io_memory(pci_reg_read, pci_reg_write, controller); + index = cpu_register_io_memory(pci_reg_read, pci_reg_write, controller, + DEVICE_LITTLE_ENDIAN); if (index < 0) goto free; cpu_register_physical_memory(registers, PCI_REG_SIZE, index); @@ -392,8 +381,8 @@ qemu_register_reset(ppc4xx_pci_reset, controller); /* XXX load/save code not tested. */ - register_savevm("ppc4xx_pci", ppc4xx_pci_id++, 1, - ppc4xx_pci_save, ppc4xx_pci_load, controller); + register_savevm(&controller->pci_dev->qdev, "ppc4xx_pci", ppc4xx_pci_id++, + 1, ppc4xx_pci_save, ppc4xx_pci_load, controller); return controller->pci_state.bus; diff -Nru qemu-kvm-0.12.5+noroms/hw/ppc.c qemu-kvm-0.14.1/hw/ppc.c --- qemu-kvm-0.12.5+noroms/hw/ppc.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/ppc.c 2011-05-11 13:29:46.000000000 +0000 @@ -28,6 +28,8 @@ #include "nvram.h" #include "qemu-log.h" #include "loader.h" +#include "kvm.h" +#include "kvm_ppc.h" //#define PPC_DEBUG_IRQ //#define PPC_DEBUG_TB @@ -50,6 +52,8 @@ static void ppc_set_irq (CPUState *env, int n_IRQ, int level) { + unsigned int old_pending = env->pending_interrupts; + if (level) { env->pending_interrupts |= 1 << n_IRQ; cpu_interrupt(env, CPU_INTERRUPT_HARD); @@ -58,6 +62,13 @@ if (env->pending_interrupts == 0) cpu_reset_interrupt(env, CPU_INTERRUPT_HARD); } + + if (old_pending != env->pending_interrupts) { +#ifdef CONFIG_KVM + kvmppc_set_interrupt(env, n_IRQ, level); +#endif + } + LOG_IRQ("%s: %p n_IRQ %d level %d => pending %08" PRIx32 "req %08x\n", __func__, env, n_IRQ, level, env->pending_interrupts, env->interrupt_request); @@ -401,7 +412,7 @@ return muldiv64(vmclk, tb_env->tb_freq, get_ticks_per_sec()) + tb_offset; } -uint32_t cpu_ppc_load_tbl (CPUState *env) +uint64_t cpu_ppc_load_tbl (CPUState *env) { ppc_tb_t *tb_env = env->tb_env; uint64_t tb; @@ -409,7 +420,7 @@ tb = cpu_ppc_get_tb(tb_env, qemu_get_clock(vm_clock), tb_env->tb_offset); LOG_TB("%s: tb %016" PRIx64 "\n", __func__, tb); - return tb & 0xFFFFFFFF; + return tb; } static inline uint32_t _cpu_ppc_load_tbu(CPUState *env) @@ -463,7 +474,7 @@ _cpu_ppc_store_tbu(env, value); } -uint32_t cpu_ppc_load_atbl (CPUState *env) +uint64_t cpu_ppc_load_atbl (CPUState *env) { ppc_tb_t *tb_env = env->tb_env; uint64_t tb; @@ -471,7 +482,7 @@ tb = cpu_ppc_get_tb(tb_env, qemu_get_clock(vm_clock), tb_env->atb_offset); LOG_TB("%s: tb %016" PRIx64 "\n", __func__, tb); - return tb & 0xFFFFFFFF; + return tb; } uint32_t cpu_ppc_load_atbu (CPUState *env) @@ -758,6 +769,9 @@ struct QEMUTimer *fit_timer; uint64_t wdt_next; /* Tick for next WDT interrupt */ struct QEMUTimer *wdt_timer; + + /* 405 have the PIT, 440 have a DECR. */ + unsigned int decr_excp; }; /* Fixed interval timer */ @@ -840,7 +854,7 @@ ppcemb_timer = tb_env->opaque; env->spr[SPR_40x_TSR] |= 1 << 27; if ((env->spr[SPR_40x_TCR] >> 26) & 0x1) - ppc_set_irq(env, PPC_INTERRUPT_PIT, 1); + ppc_set_irq(env, ppcemb_timer->decr_excp, 1); start_stop_pit(env, tb_env, 1); LOG_TB("%s: ar %d ir %d TCR " TARGET_FMT_lx " TSR " TARGET_FMT_lx " " "%016" PRIx64 "\n", __func__, @@ -937,10 +951,15 @@ void store_booke_tsr (CPUState *env, target_ulong val) { + ppc_tb_t *tb_env = env->tb_env; + ppcemb_timer_t *ppcemb_timer; + + ppcemb_timer = tb_env->opaque; + LOG_TB("%s: val " TARGET_FMT_lx "\n", __func__, val); env->spr[SPR_40x_TSR] &= ~(val & 0xFC000000); if (val & 0x80000000) - ppc_set_irq(env, PPC_INTERRUPT_PIT, 0); + ppc_set_irq(env, ppcemb_timer->decr_excp, 0); } void store_booke_tcr (CPUState *env, target_ulong val) @@ -966,7 +985,8 @@ /* XXX: we should also update all timers */ } -clk_setup_cb ppc_emb_timers_init (CPUState *env, uint32_t freq) +clk_setup_cb ppc_emb_timers_init (CPUState *env, uint32_t freq, + unsigned int decr_excp) { ppc_tb_t *tb_env; ppcemb_timer_t *ppcemb_timer; @@ -985,6 +1005,7 @@ qemu_new_timer(vm_clock, &cpu_4xx_fit_cb, env); ppcemb_timer->wdt_timer = qemu_new_timer(vm_clock, &cpu_4xx_wdt_cb, env); + ppcemb_timer->decr_excp = decr_excp; } return &ppc_emb_set_tb_clk; @@ -1009,7 +1030,7 @@ int (*write_error)(int dcrn); }; -int ppc_dcr_read (ppc_dcr_t *dcr_env, int dcrn, target_ulong *valp) +int ppc_dcr_read (ppc_dcr_t *dcr_env, int dcrn, uint32_t *valp) { ppc_dcrn_t *dcr; @@ -1029,7 +1050,7 @@ return -1; } -int ppc_dcr_write (ppc_dcr_t *dcr_env, int dcrn, target_ulong val) +int ppc_dcr_write (ppc_dcr_t *dcr_env, int dcrn, uint32_t val) { ppc_dcrn_t *dcr; @@ -1085,16 +1106,6 @@ return 0; } -#if 0 -/*****************************************************************************/ -/* Handle system reset (for now, just stop emulation) */ -void cpu_reset(CPUState *env) -{ - printf("Reset asked... Stop emulation\n"); - abort(); -} -#endif - /*****************************************************************************/ /* Debug port */ void PPC_debug_write (void *opaque, uint32_t addr, uint32_t val) diff -Nru qemu-kvm-0.12.5+noroms/hw/ppce500_mpc8544ds.c qemu-kvm-0.14.1/hw/ppce500_mpc8544ds.c --- qemu-kvm-0.12.5+noroms/hw/ppce500_mpc8544ds.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/ppce500_mpc8544ds.c 2011-05-11 13:29:46.000000000 +0000 @@ -35,8 +35,10 @@ #define BINARY_DEVICE_TREE_FILE "mpc8544ds.dtb" #define UIMAGE_LOAD_BASE 0 -#define DTB_LOAD_BASE 0x600000 -#define INITRD_LOAD_BASE 0x2000000 +#define DTC_LOAD_PAD 0x500000 +#define DTC_PAD_MASK 0xFFFFF +#define INITRD_LOAD_PAD 0x2000000 +#define INITRD_PAD_MASK 0xFFFFFF #define RAM_SIZES_ALIGN (64UL << 20) @@ -73,18 +75,18 @@ } #endif -static void *mpc8544_load_device_tree(target_phys_addr_t addr, +static int mpc8544_load_device_tree(target_phys_addr_t addr, uint32_t ramsize, target_phys_addr_t initrd_base, target_phys_addr_t initrd_size, const char *kernel_cmdline) { - void *fdt = NULL; + int ret = -1; #ifdef CONFIG_FDT uint32_t mem_reg_property[] = {0, ramsize}; char *filename; int fdt_size; - int ret; + void *fdt; filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE); if (!filename) { @@ -124,6 +126,7 @@ if ((dp = opendir("/proc/device-tree/cpus/")) == NULL) { printf("Can't open directory /proc/device-tree/cpus/\n"); + ret = -1; goto out; } @@ -137,6 +140,7 @@ closedir(dp); if (buf[0] == '\0') { printf("Unknow host!\n"); + ret = -1; goto out; } @@ -144,12 +148,13 @@ mpc8544_copy_soc_cell(fdt, buf, "timebase-frequency"); } - cpu_physical_memory_write (addr, (void *)fdt, fdt_size); + ret = rom_add_blob_fixed(BINARY_DEVICE_TREE_FILE, fdt, fdt_size, addr); + qemu_free(fdt); out: #endif - return fdt; + return ret; } static void mpc8544ds_init(ram_addr_t ram_size, @@ -166,14 +171,12 @@ target_phys_addr_t entry=0; target_phys_addr_t loadaddr=UIMAGE_LOAD_BASE; target_long kernel_size=0; - target_ulong dt_base=DTB_LOAD_BASE; - target_ulong initrd_base=INITRD_LOAD_BASE; + target_ulong dt_base = 0; + target_ulong initrd_base = 0; target_long initrd_size=0; - void *fdt; int i=0; unsigned int pci_irq_nrs[4] = {1, 2, 3, 4}; qemu_irq *irqs, *mpic, *pci_irqs; - SerialState * serial[2]; /* Setup CPU */ env = cpu_ppc_init("e500v2_v30"); @@ -186,7 +189,8 @@ ram_size &= ~(RAM_SIZES_ALIGN - 1); /* Register Memory */ - cpu_register_physical_memory(0, ram_size, qemu_ram_alloc(ram_size)); + cpu_register_physical_memory(0, ram_size, qemu_ram_alloc(NULL, + "mpc8544ds.ram", ram_size)); /* MPIC */ irqs = qemu_mallocz(sizeof(qemu_irq) * OPENPIC_OUTPUT_NB); @@ -195,15 +199,17 @@ mpic = mpic_init(MPC8544_MPIC_REGS_BASE, 1, &irqs, NULL); /* Serial */ - if (serial_hds[0]) - serial[0] = serial_mm_init(MPC8544_SERIAL0_REGS_BASE, - 0, mpic[12+26], 399193, - serial_hds[0], 1); - - if (serial_hds[1]) - serial[0] = serial_mm_init(MPC8544_SERIAL1_REGS_BASE, - 0, mpic[12+26], 399193, - serial_hds[0], 1); + if (serial_hds[0]) { + serial_mm_init(MPC8544_SERIAL0_REGS_BASE, + 0, mpic[12+26], 399193, + serial_hds[0], 1, 1); + } + + if (serial_hds[1]) { + serial_mm_init(MPC8544_SERIAL1_REGS_BASE, + 0, mpic[12+26], 399193, + serial_hds[0], 1, 1); + } /* PCI */ pci_irqs = qemu_malloc(sizeof(qemu_irq) * 4); @@ -228,8 +234,8 @@ if (kernel_filename) { kernel_size = load_uimage(kernel_filename, &entry, &loadaddr, NULL); if (kernel_size < 0) { - kernel_size = load_elf(kernel_filename, 0, &elf_entry, &elf_lowaddr, - NULL, 1, ELF_MACHINE, 0); + kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry, + &elf_lowaddr, NULL, 1, ELF_MACHINE, 0); entry = elf_entry; loadaddr = elf_lowaddr; } @@ -243,6 +249,7 @@ /* Load initrd. */ if (initrd_filename) { + initrd_base = (kernel_size + INITRD_LOAD_PAD) & ~INITRD_PAD_MASK; initrd_size = load_image_targphys(initrd_filename, initrd_base, ram_size - initrd_base); @@ -255,13 +262,15 @@ /* If we're loading a kernel directly, we must load the device tree too. */ if (kernel_filename) { - fdt = mpc8544_load_device_tree(dt_base, ram_size, - initrd_base, initrd_size, kernel_cmdline); - if (fdt == NULL) { + dt_base = (kernel_size + DTC_LOAD_PAD) & ~DTC_PAD_MASK; + if (mpc8544_load_device_tree(dt_base, ram_size, + initrd_base, initrd_size, kernel_cmdline) < 0) { fprintf(stderr, "couldn't load device tree\n"); exit(1); } + cpu_synchronize_state(env); + /* Set initial guest state. */ env->gpr[1] = (16<<20) - 8; env->gpr[3] = dt_base; diff -Nru qemu-kvm-0.12.5+noroms/hw/ppce500_pci.c qemu-kvm-0.14.1/hw/ppce500_pci.c --- qemu-kvm-0.12.5+noroms/hw/ppce500_pci.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/ppce500_pci.c 2011-05-11 13:29:46.000000000 +0000 @@ -15,12 +15,10 @@ */ #include "hw.h" -#include "ppc.h" #include "ppce500.h" #include "pci.h" #include "pci_host.h" #include "bswap.h" -#include "qemu-log.h" #ifdef DEBUG_PCI #define pci_debug(fmt, ...) fprintf(stderr, fmt, ## __VA_ARGS__) @@ -281,7 +279,8 @@ controller->pci_state.bus = pci_register_bus(NULL, "pci", mpc85xx_pci_set_irq, mpc85xx_pci_map_irq, - pci_irqs, 0x88, 4); + pci_irqs, PCI_DEVFN(0x11, 0), + 4); d = pci_register_device(controller->pci_state.bus, "host bridge", sizeof(PCIDevice), 0, NULL, NULL); @@ -293,27 +292,30 @@ controller->pci_dev = d; /* CFGADDR */ - index = pci_host_conf_register_mmio_noswap(&controller->pci_state); + index = pci_host_conf_register_mmio(&controller->pci_state, + DEVICE_BIG_ENDIAN); if (index < 0) goto free; cpu_register_physical_memory(registers + PCIE500_CFGADDR, 4, index); /* CFGDATA */ - index = pci_host_data_register_mmio(&controller->pci_state); + index = pci_host_data_register_mmio(&controller->pci_state, + DEVICE_BIG_ENDIAN); if (index < 0) goto free; cpu_register_physical_memory(registers + PCIE500_CFGDATA, 4, index); index = cpu_register_io_memory(e500_pci_reg_read, - e500_pci_reg_write, controller); + e500_pci_reg_write, controller, + DEVICE_NATIVE_ENDIAN); if (index < 0) goto free; cpu_register_physical_memory(registers + PCIE500_REG_BASE, PCIE500_REG_SIZE, index); /* XXX load/save code not tested. */ - register_savevm("ppce500_pci", ppce500_pci_id++, 1, - ppce500_pci_save, ppce500_pci_load, controller); + register_savevm(&d->qdev, "ppce500_pci", ppce500_pci_id++, + 1, ppce500_pci_save, ppce500_pci_load, controller); return controller->pci_state.bus; diff -Nru qemu-kvm-0.12.5+noroms/hw/ppc.h qemu-kvm-0.14.1/hw/ppc.h --- qemu-kvm-0.12.5+noroms/hw/ppc.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/ppc.h 2011-05-11 13:29:46.000000000 +0000 @@ -13,13 +13,15 @@ clk_setup_cb cpu_ppc_tb_init (CPUState *env, uint32_t freq); /* Embedded PowerPC DCR management */ -typedef target_ulong (*dcr_read_cb)(void *opaque, int dcrn); -typedef void (*dcr_write_cb)(void *opaque, int dcrn, target_ulong val); +typedef uint32_t (*dcr_read_cb)(void *opaque, int dcrn); +typedef void (*dcr_write_cb)(void *opaque, int dcrn, uint32_t val); int ppc_dcr_init (CPUState *env, int (*dcr_read_error)(int dcrn), int (*dcr_write_error)(int dcrn)); int ppc_dcr_register (CPUState *env, int dcrn, void *opaque, dcr_read_cb drc_read, dcr_write_cb dcr_write); -clk_setup_cb ppc_emb_timers_init (CPUState *env, uint32_t freq); +clk_setup_cb ppc_emb_timers_init (CPUState *env, uint32_t freq, + unsigned int decr_excp); + /* Embedded PowerPC reset */ void ppc40x_core_reset (CPUState *env); void ppc40x_chip_reset (CPUState *env); @@ -40,10 +42,15 @@ ARCH_PREP = 0, ARCH_MAC99, ARCH_HEATHROW, + ARCH_MAC99_U3, }; #define FW_CFG_PPC_WIDTH (FW_CFG_ARCH_LOCAL + 0x00) #define FW_CFG_PPC_HEIGHT (FW_CFG_ARCH_LOCAL + 0x01) #define FW_CFG_PPC_DEPTH (FW_CFG_ARCH_LOCAL + 0x02) +#define FW_CFG_PPC_TBFREQ (FW_CFG_ARCH_LOCAL + 0x03) +#define FW_CFG_PPC_IS_KVM (FW_CFG_ARCH_LOCAL + 0x05) +#define FW_CFG_PPC_KVM_HC (FW_CFG_ARCH_LOCAL + 0x06) +#define FW_CFG_PPC_KVM_PID (FW_CFG_ARCH_LOCAL + 0x07) #define PPC_SERIAL_MM_BAUDBASE 399193 diff -Nru qemu-kvm-0.12.5+noroms/hw/ppc_mac.h qemu-kvm-0.14.1/hw/ppc_mac.h --- qemu-kvm-0.12.5+noroms/hw/ppc_mac.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/ppc_mac.h 2011-05-11 13:29:46.000000000 +0000 @@ -30,7 +30,6 @@ #define BIOS_SIZE (1024 * 1024) #define BIOS_FILENAME "ppc_rom.bin" -#define VGABIOS_FILENAME "video.x" #define NVRAM_SIZE 0x2000 #define PROM_FILENAME "openbios-ppc" #define PROM_ADDR 0xfff00000 @@ -58,6 +57,7 @@ /* UniNorth PCI */ PCIBus *pci_pmac_init(qemu_irq *pic); +PCIBus *pci_pmac_u3_init(qemu_irq *pic); /* Mac NVRAM */ typedef struct MacIONVRAMState MacIONVRAMState; diff -Nru qemu-kvm-0.12.5+noroms/hw/ppc_newworld.c qemu-kvm-0.14.1/hw/ppc_newworld.c --- qemu-kvm-0.12.5+noroms/hw/ppc_newworld.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/ppc_newworld.c 2011-05-11 13:29:46.000000000 +0000 @@ -21,6 +21,30 @@ * 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. + * + * PCI bus layout on a real G5 (U3 based): + * + * 0000:f0:0b.0 Host bridge [0600]: Apple Computer Inc. U3 AGP [106b:004b] + * 0000:f0:10.0 VGA compatible controller [0300]: ATI Technologies Inc RV350 AP [Radeon 9600] [1002:4150] + * 0001:00:00.0 Host bridge [0600]: Apple Computer Inc. CPC945 HT Bridge [106b:004a] + * 0001:00:01.0 PCI bridge [0604]: Advanced Micro Devices [AMD] AMD-8131 PCI-X Bridge [1022:7450] (rev 12) + * 0001:00:02.0 PCI bridge [0604]: Advanced Micro Devices [AMD] AMD-8131 PCI-X Bridge [1022:7450] (rev 12) + * 0001:00:03.0 PCI bridge [0604]: Apple Computer Inc. K2 HT-PCI Bridge [106b:0045] + * 0001:00:04.0 PCI bridge [0604]: Apple Computer Inc. K2 HT-PCI Bridge [106b:0046] + * 0001:00:05.0 PCI bridge [0604]: Apple Computer Inc. K2 HT-PCI Bridge [106b:0047] + * 0001:00:06.0 PCI bridge [0604]: Apple Computer Inc. K2 HT-PCI Bridge [106b:0048] + * 0001:00:07.0 PCI bridge [0604]: Apple Computer Inc. K2 HT-PCI Bridge [106b:0049] + * 0001:01:07.0 Class [ff00]: Apple Computer Inc. K2 KeyLargo Mac/IO [106b:0041] (rev 20) + * 0001:01:08.0 USB Controller [0c03]: Apple Computer Inc. K2 KeyLargo USB [106b:0040] + * 0001:01:09.0 USB Controller [0c03]: Apple Computer Inc. K2 KeyLargo USB [106b:0040] + * 0001:02:0b.0 USB Controller [0c03]: NEC Corporation USB [1033:0035] (rev 43) + * 0001:02:0b.1 USB Controller [0c03]: NEC Corporation USB [1033:0035] (rev 43) + * 0001:02:0b.2 USB Controller [0c03]: NEC Corporation USB 2.0 [1033:00e0] (rev 04) + * 0001:03:0d.0 Class [ff00]: Apple Computer Inc. K2 ATA/100 [106b:0043] + * 0001:03:0e.0 FireWire (IEEE 1394) [0c00]: Apple Computer Inc. K2 FireWire [106b:0042] + * 0001:04:0f.0 Ethernet controller [0200]: Apple Computer Inc. K2 GMAC (Sun GEM) [106b:004c] + * 0001:05:0c.0 IDE interface [0101]: Broadcom K2 SATA [1166:0240] + * */ #include "hw.h" #include "ppc.h" @@ -40,9 +64,11 @@ #include "loader.h" #include "elf.h" #include "kvm.h" +#include "kvm_ppc.h" +#include "hw/usb.h" +#include "blockdev.h" #define MAX_IDE_BUS 2 -#define VGA_BIOS_SIZE 65536 #define CFG_ADDR 0xf0000510 /* debug UniNorth */ @@ -89,6 +115,11 @@ return 0; } +static uint64_t translate_kernel_address(void *opaque, uint64_t addr) +{ + return (addr & 0x0fffffff) + KERNEL_LOAD_ADDR; +} + /* PowerPC Mac99 hardware initialisation */ static void ppc_core99_init (ram_addr_t ram_size, const char *boot_device, @@ -97,30 +128,35 @@ const char *initrd_filename, const char *cpu_model) { - CPUState *env = NULL, *envs[MAX_CPUS]; + CPUState *env = NULL; char *filename; qemu_irq *pic, **openpic_irqs; int unin_memory; int linux_boot, i; - ram_addr_t ram_offset, bios_offset, vga_bios_offset; - uint32_t kernel_base, kernel_size, initrd_base, initrd_size; + ram_addr_t ram_offset, bios_offset; + uint32_t kernel_base, initrd_base; + long kernel_size, initrd_size; PCIBus *pci_bus; MacIONVRAMState *nvr; int nvram_mem_index; - int vga_bios_size, bios_size; - qemu_irq *dummy_irq; + int bios_size; int pic_mem_index, dbdma_mem_index, cuda_mem_index, escc_mem_index; + int ide_mem_index[3]; int ppc_boot_device; DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; void *fw_cfg; void *dbdma; - uint8_t *vga_bios_ptr; + int machine_arch; linux_boot = (kernel_filename != NULL); /* init CPUs */ if (cpu_model == NULL) +#ifdef TARGET_PPC64 + cpu_model = "970fx"; +#else cpu_model = "G4"; +#endif for (i = 0; i < smp_cpus; i++) { env = cpu_init(cpu_model); if (!env) { @@ -129,22 +165,15 @@ } /* Set time-base frequency to 100 Mhz */ cpu_ppc_tb_init(env, 100UL * 1000UL * 1000UL); -#if 0 - env->osi_call = vga_osi_call; -#endif qemu_register_reset((QEMUResetHandler*)&cpu_reset, env); - envs[i] = env; } - /* Make sure all register sets take effect */ - cpu_synchronize_state(env); - /* allocate RAM */ - ram_offset = qemu_ram_alloc(ram_size); + ram_offset = qemu_ram_alloc(NULL, "ppc_core99.ram", ram_size); cpu_register_physical_memory(0, ram_size, ram_offset); /* allocate and load BIOS */ - bios_offset = qemu_ram_alloc(BIOS_SIZE); + bios_offset = qemu_ram_alloc(NULL, "ppc_core99.bios", BIOS_SIZE); if (bios_name == NULL) bios_name = PROM_FILENAME; filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); @@ -152,7 +181,8 @@ /* Load OpenBIOS (ELF) */ if (filename) { - bios_size = load_elf(filename, 0, NULL, NULL, NULL, 1, ELF_MACHINE, 0); + bios_size = load_elf(filename, NULL, NULL, NULL, + NULL, NULL, 1, ELF_MACHINE, 0); qemu_free(filename); } else { @@ -163,36 +193,6 @@ exit(1); } - /* allocate and load VGA BIOS */ - vga_bios_offset = qemu_ram_alloc(VGA_BIOS_SIZE); - vga_bios_ptr = qemu_get_ram_ptr(vga_bios_offset); - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, VGABIOS_FILENAME); - if (filename) { - vga_bios_size = load_image(filename, vga_bios_ptr + 8); - qemu_free(filename); - } else { - vga_bios_size = -1; - } - if (vga_bios_size < 0) { - /* if no bios is present, we can still work */ - fprintf(stderr, "qemu: warning: could not load VGA bios '%s'\n", - VGABIOS_FILENAME); - vga_bios_size = 0; - } else { - /* set a specific header (XXX: find real Apple format for NDRV - drivers) */ - vga_bios_ptr[0] = 'N'; - vga_bios_ptr[1] = 'D'; - vga_bios_ptr[2] = 'R'; - vga_bios_ptr[3] = 'V'; - cpu_to_be32w((uint32_t *)(vga_bios_ptr + 4), vga_bios_size); - vga_bios_size += 8; - - /* Round to page boundary */ - vga_bios_size = (vga_bios_size + TARGET_PAGE_SIZE - 1) & - TARGET_PAGE_MASK; - } - if (linux_boot) { uint64_t lowaddr = 0; int bswap_needed; @@ -204,15 +204,8 @@ #endif kernel_base = KERNEL_LOAD_ADDR; - /* Now we can load the kernel. The first step tries to load the kernel - supposing PhysAddr = 0x00000000. If that was wrong the kernel is - loaded again, the new PhysAddr being computed from lowaddr. */ - kernel_size = load_elf(kernel_filename, kernel_base, NULL, &lowaddr, NULL, - 1, ELF_MACHINE, 0); - if (kernel_size > 0 && lowaddr != KERNEL_LOAD_ADDR) { - kernel_size = load_elf(kernel_filename, (2 * kernel_base) - lowaddr, - NULL, NULL, NULL, 1, ELF_MACHINE, 0); - } + kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL, + NULL, &lowaddr, NULL, 1, ELF_MACHINE, 0); if (kernel_size < 0) kernel_size = load_aout(kernel_filename, kernel_base, ram_size - kernel_base, bswap_needed, @@ -267,7 +260,8 @@ isa_mmio_init(0xf2000000, 0x00800000); /* UniN init */ - unin_memory = cpu_register_io_memory(unin_read, unin_write, NULL); + unin_memory = cpu_register_io_memory(unin_read, unin_write, NULL, + DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(0xf8000000, 0x00001000, unin_memory); openpic_irqs = qemu_mallocz(smp_cpus * sizeof(qemu_irq *)); @@ -314,14 +308,18 @@ } } pic = openpic_init(NULL, &pic_mem_index, smp_cpus, openpic_irqs, NULL); - pci_bus = pci_pmac_init(pic); + if (PPC_INPUT(env) == PPC_FLAGS_INPUT_970) { + /* 970 gets a U3 bus */ + pci_bus = pci_pmac_u3_init(pic); + machine_arch = ARCH_MAC99_U3; + } else { + pci_bus = pci_pmac_init(pic); + machine_arch = ARCH_MAC99; + } /* init basic PC hardware */ - pci_vga_init(pci_bus, vga_bios_offset, vga_bios_size); + pci_vga_init(pci_bus); - /* XXX: suppress that */ - dummy_irq = i8259_init(NULL); - - escc_mem_index = escc_init(0x80013000, dummy_irq[4], dummy_irq[5], + escc_mem_index = escc_init(0x80013000, pic[0x25], pic[0x24], serial_hds[0], serial_hds[1], ESCC_CLOCK, 4); for(i = 0; i < nb_nics; i++) @@ -331,27 +329,41 @@ fprintf(stderr, "qemu: too many IDE bus\n"); exit(1); } - for(i = 0; i < MAX_IDE_BUS * MAX_IDE_DEVS; i++) { - hd[i] = drive_get(IF_IDE, i / MAX_IDE_DEVS, i % MAX_IDE_DEVS); - } dbdma = DBDMA_init(&dbdma_mem_index); - pci_cmd646_ide_init(pci_bus, hd, 0); + + /* We only emulate 2 out of 3 IDE controllers for now */ + ide_mem_index[0] = -1; + hd[0] = drive_get(IF_IDE, 0, 0); + hd[1] = drive_get(IF_IDE, 0, 1); + ide_mem_index[1] = pmac_ide_init(hd, pic[0x0d], dbdma, 0x16, pic[0x02]); + hd[0] = drive_get(IF_IDE, 1, 0); + hd[1] = drive_get(IF_IDE, 1, 1); + ide_mem_index[2] = pmac_ide_init(hd, pic[0x0e], dbdma, 0x1a, pic[0x02]); /* cuda also initialize ADB */ + if (machine_arch == ARCH_MAC99_U3) { + usb_enabled = 1; + } cuda_init(&cuda_mem_index, pic[0x19]); adb_kbd_init(&adb_bus); adb_mouse_init(&adb_bus); - macio_init(pci_bus, PCI_DEVICE_ID_APPLE_UNI_N_KEYL, 0, pic_mem_index, - dbdma_mem_index, cuda_mem_index, NULL, 0, NULL, + dbdma_mem_index, cuda_mem_index, NULL, 3, ide_mem_index, escc_mem_index); if (usb_enabled) { usb_ohci_init_pci(pci_bus, -1); } + /* U3 needs to use USB for input because Linux doesn't support via-cuda + on PPC64 */ + if (machine_arch == ARCH_MAC99_U3) { + usbdevice_create("keyboard"); + usbdevice_create("mouse"); + } + if (graphic_depth != 15 && graphic_depth != 32 && graphic_depth != 8) graphic_depth = 15; @@ -364,7 +376,7 @@ fw_cfg = fw_cfg_init(0, 0, CFG_ADDR, CFG_ADDR + 2); fw_cfg_add_i32(fw_cfg, FW_CFG_ID, 1); fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size); - fw_cfg_add_i16(fw_cfg, FW_CFG_MACHINE_ID, ARCH_MAC99); + fw_cfg_add_i16(fw_cfg, FW_CFG_MACHINE_ID, machine_arch); fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, kernel_base); fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, kernel_size); if (kernel_cmdline) { @@ -381,6 +393,21 @@ fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_HEIGHT, graphic_height); fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_DEPTH, graphic_depth); + fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_IS_KVM, kvm_enabled()); + if (kvm_enabled()) { +#ifdef CONFIG_KVM + uint8_t *hypercall; + + fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, kvmppc_get_tbfreq()); + hypercall = qemu_malloc(16); + kvmppc_get_hypercall(env, hypercall, 16); + fw_cfg_add_bytes(fw_cfg, FW_CFG_PPC_KVM_HC, hypercall, 16); + fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_KVM_PID, getpid()); +#endif + } else { + fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, get_ticks_per_sec()); + } + qemu_register_boot_set(fw_cfg_boot_set, fw_cfg); } @@ -389,6 +416,9 @@ .desc = "Mac99 based PowerMAC", .init = ppc_core99_init, .max_cpus = MAX_CPUS, +#ifdef TARGET_PPC64 + .is_default = 1, +#endif }; static void core99_machine_init(void) diff -Nru qemu-kvm-0.12.5+noroms/hw/ppc_oldworld.c qemu-kvm-0.14.1/hw/ppc_oldworld.c --- qemu-kvm-0.12.5+noroms/hw/ppc_oldworld.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/ppc_oldworld.c 2011-05-11 13:29:46.000000000 +0000 @@ -1,3 +1,4 @@ + /* * QEMU OldWorld PowerMac (currently ~G3 Beige) hardware System Emulator * @@ -40,87 +41,24 @@ #include "loader.h" #include "elf.h" #include "kvm.h" +#include "kvm_ppc.h" +#include "blockdev.h" #define MAX_IDE_BUS 2 -#define VGA_BIOS_SIZE 65536 #define CFG_ADDR 0xf0000510 -/* temporary frame buffer OSI calls for the video.x driver. The right - solution is to modify the driver to use VGA PCI I/Os */ -/* XXX: to be removed. This is no way related to emulation */ -static int vga_osi_call (CPUState *env) -{ - static int vga_vbl_enabled; - int linesize; - -#if 0 - printf("osi_call R5=%016" PRIx64 "\n", ppc_dump_gpr(env, 5)); -#endif - - /* same handler as PearPC, coming from the original MOL video - driver. */ - switch(env->gpr[5]) { - case 4: - break; - case 28: /* set_vmode */ - if (env->gpr[6] != 1 || env->gpr[7] != 0) - env->gpr[3] = 1; - else - env->gpr[3] = 0; - break; - case 29: /* get_vmode_info */ - if (env->gpr[6] != 0) { - if (env->gpr[6] != 1 || env->gpr[7] != 0) { - env->gpr[3] = 1; - break; - } - } - env->gpr[3] = 0; - env->gpr[4] = (1 << 16) | 1; /* num_vmodes, cur_vmode */ - env->gpr[5] = (1 << 16) | 0; /* num_depths, cur_depth_mode */ - env->gpr[6] = (graphic_width << 16) | graphic_height; /* w, h */ - env->gpr[7] = 85 << 16; /* refresh rate */ - env->gpr[8] = (graphic_depth + 7) & ~7; /* depth (round to byte) */ - linesize = ((graphic_depth + 7) >> 3) * graphic_width; - linesize = (linesize + 3) & ~3; - env->gpr[9] = (linesize << 16) | 0; /* row_bytes, offset */ - break; - case 31: /* set_video power */ - env->gpr[3] = 0; - break; - case 39: /* video_ctrl */ - if (env->gpr[6] == 0 || env->gpr[6] == 1) - vga_vbl_enabled = env->gpr[6]; - env->gpr[3] = 0; - break; - case 47: - break; - case 59: /* set_color */ - /* R6 = index, R7 = RGB */ - env->gpr[3] = 0; - break; - case 64: /* get color */ - /* R6 = index */ - env->gpr[3] = 0; - break; - case 116: /* set hwcursor */ - /* R6 = x, R7 = y, R8 = visible, R9 = data */ - break; - default: - fprintf(stderr, "unsupported OSI call R5=%016" PRIx64 "\n", - ppc_dump_gpr(env, 5)); - break; - } - - return 1; /* osi_call handled */ -} - static int fw_cfg_boot_set(void *opaque, const char *boot_device) { fw_cfg_add_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]); return 0; } + +static uint64_t translate_kernel_address(void *opaque, uint64_t addr) +{ + return (addr & 0x0fffffff) + KERNEL_LOAD_ADDR; +} + static void ppc_heathrow_init (ram_addr_t ram_size, const char *boot_device, const char *kernel_filename, @@ -128,23 +66,22 @@ const char *initrd_filename, const char *cpu_model) { - CPUState *env = NULL, *envs[MAX_CPUS]; + CPUState *env = NULL; char *filename; qemu_irq *pic, **heathrow_irqs; int linux_boot, i; - ram_addr_t ram_offset, bios_offset, vga_bios_offset; + ram_addr_t ram_offset, bios_offset; uint32_t kernel_base, initrd_base; int32_t kernel_size, initrd_size; PCIBus *pci_bus; MacIONVRAMState *nvr; - int vga_bios_size, bios_size; + int bios_size; int pic_mem_index, nvram_mem_index, dbdma_mem_index, cuda_mem_index; int escc_mem_index, ide_mem_index[2]; uint16_t ppc_boot_device; DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; void *fw_cfg; void *dbdma; - uint8_t *vga_bios_ptr; linux_boot = (kernel_filename != NULL); @@ -159,14 +96,9 @@ } /* Set time-base frequency to 16.6 Mhz */ cpu_ppc_tb_init(env, 16600000UL); - env->osi_call = vga_osi_call; qemu_register_reset((QEMUResetHandler*)&cpu_reset, env); - envs[i] = env; } - /* Make sure all register sets take effect */ - cpu_synchronize_state(env); - /* allocate RAM */ if (ram_size > (2047 << 20)) { fprintf(stderr, @@ -175,11 +107,11 @@ exit(1); } - ram_offset = qemu_ram_alloc(ram_size); + ram_offset = qemu_ram_alloc(NULL, "ppc_heathrow.ram", ram_size); cpu_register_physical_memory(0, ram_size, ram_offset); /* allocate and load BIOS */ - bios_offset = qemu_ram_alloc(BIOS_SIZE); + bios_offset = qemu_ram_alloc(NULL, "ppc_heathrow.bios", BIOS_SIZE); if (bios_name == NULL) bios_name = PROM_FILENAME; filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); @@ -187,8 +119,8 @@ /* Load OpenBIOS (ELF) */ if (filename) { - bios_size = load_elf(filename, 0, NULL, NULL, NULL, - 1, ELF_MACHINE, 0); + bios_size = load_elf(filename, 0, NULL, NULL, NULL, NULL, + 1, ELF_MACHINE, 0); qemu_free(filename); } else { bios_size = -1; @@ -198,36 +130,6 @@ exit(1); } - /* allocate and load VGA BIOS */ - vga_bios_offset = qemu_ram_alloc(VGA_BIOS_SIZE); - vga_bios_ptr = qemu_get_ram_ptr(vga_bios_offset); - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, VGABIOS_FILENAME); - if (filename) { - vga_bios_size = load_image(filename, vga_bios_ptr + 8); - qemu_free(filename); - } else { - vga_bios_size = -1; - } - if (vga_bios_size < 0) { - /* if no bios is present, we can still work */ - fprintf(stderr, "qemu: warning: could not load VGA bios '%s'\n", - VGABIOS_FILENAME); - vga_bios_size = 0; - } else { - /* set a specific header (XXX: find real Apple format for NDRV - drivers) */ - vga_bios_ptr[0] = 'N'; - vga_bios_ptr[1] = 'D'; - vga_bios_ptr[2] = 'R'; - vga_bios_ptr[3] = 'V'; - cpu_to_be32w((uint32_t *)(vga_bios_ptr + 4), vga_bios_size); - vga_bios_size += 8; - - /* Round to page boundary */ - vga_bios_size = (vga_bios_size + TARGET_PAGE_SIZE - 1) & - TARGET_PAGE_MASK; - } - if (linux_boot) { uint64_t lowaddr = 0; int bswap_needed; @@ -238,15 +140,8 @@ bswap_needed = 0; #endif kernel_base = KERNEL_LOAD_ADDR; - /* Now we can load the kernel. The first step tries to load the kernel - supposing PhysAddr = 0x00000000. If that was wrong the kernel is - loaded again, the new PhysAddr being computed from lowaddr. */ - kernel_size = load_elf(kernel_filename, kernel_base, NULL, &lowaddr, NULL, - 1, ELF_MACHINE, 0); - if (kernel_size > 0 && lowaddr != KERNEL_LOAD_ADDR) { - kernel_size = load_elf(kernel_filename, (2 * kernel_base) - lowaddr, - NULL, NULL, NULL, 1, ELF_MACHINE, 0); - } + kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL, + NULL, &lowaddr, NULL, 1, ELF_MACHINE, 0); if (kernel_size < 0) kernel_size = load_aout(kernel_filename, kernel_base, ram_size - kernel_base, bswap_needed, @@ -332,7 +227,7 @@ } pic = heathrow_pic_init(&pic_mem_index, 1, heathrow_irqs); pci_bus = pci_grackle_init(0xfec00000, pic); - pci_vga_init(pci_bus, vga_bios_offset, vga_bios_size); + pci_vga_init(pci_bus); escc_mem_index = escc_init(0x80013000, pic[0x0f], pic[0x10], serial_hds[0], serial_hds[1], ESCC_CLOCK, 4); @@ -401,6 +296,21 @@ fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_HEIGHT, graphic_height); fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_DEPTH, graphic_depth); + fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_IS_KVM, kvm_enabled()); + if (kvm_enabled()) { +#ifdef CONFIG_KVM + uint8_t *hypercall; + + fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, kvmppc_get_tbfreq()); + hypercall = qemu_malloc(16); + kvmppc_get_hypercall(env, hypercall, 16); + fw_cfg_add_bytes(fw_cfg, FW_CFG_PPC_KVM_HC, hypercall, 16); + fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_KVM_PID, getpid()); +#endif + } else { + fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, get_ticks_per_sec()); + } + qemu_register_boot_set(fw_cfg_boot_set, fw_cfg); } @@ -409,7 +319,9 @@ .desc = "Heathrow based PowerMAC", .init = ppc_heathrow_init, .max_cpus = MAX_CPUS, +#ifndef TARGET_PPC64 .is_default = 1, +#endif }; static void heathrow_machine_init(void) diff -Nru qemu-kvm-0.12.5+noroms/hw/ppc_prep.c qemu-kvm-0.14.1/hw/ppc_prep.c --- qemu-kvm-0.12.5+noroms/hw/ppc_prep.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/ppc_prep.c 2011-05-11 13:29:46.000000000 +0000 @@ -36,6 +36,8 @@ #include "qemu-log.h" #include "ide.h" #include "loader.h" +#include "mc146818rtc.h" +#include "blockdev.h" //#define HARD_DEBUG_PPC_IO //#define DEBUG_PPC_IO @@ -143,20 +145,12 @@ static uint32_t PPC_intack_readw (void *opaque, target_phys_addr_t addr) { -#ifdef TARGET_WORDS_BIGENDIAN - return bswap16(_PPC_intack_read(addr)); -#else return _PPC_intack_read(addr); -#endif } static uint32_t PPC_intack_readl (void *opaque, target_phys_addr_t addr) { -#ifdef TARGET_WORDS_BIGENDIAN - return bswap32(_PPC_intack_read(addr)); -#else return _PPC_intack_read(addr); -#endif } static CPUWriteMemoryFunc * const PPC_intack_write[] = { @@ -208,9 +202,6 @@ static void PPC_XCSR_writew (void *opaque, target_phys_addr_t addr, uint32_t value) { -#ifdef TARGET_WORDS_BIGENDIAN - value = bswap16(value); -#endif printf("%s: 0x" TARGET_FMT_plx " => 0x%08" PRIx32 "\n", __func__, addr, value); } @@ -218,9 +209,6 @@ static void PPC_XCSR_writel (void *opaque, target_phys_addr_t addr, uint32_t value) { -#ifdef TARGET_WORDS_BIGENDIAN - value = bswap32(value); -#endif printf("%s: 0x" TARGET_FMT_plx " => 0x%08" PRIx32 "\n", __func__, addr, value); } @@ -241,9 +229,6 @@ printf("%s: 0x" TARGET_FMT_plx " <= %08" PRIx32 "\n", __func__, addr, retval); -#ifdef TARGET_WORDS_BIGENDIAN - retval = bswap16(retval); -#endif return retval; } @@ -254,9 +239,6 @@ printf("%s: 0x" TARGET_FMT_plx " <= %08" PRIx32 "\n", __func__, addr, retval); -#ifdef TARGET_WORDS_BIGENDIAN - retval = bswap32(retval); -#endif return retval; } @@ -277,7 +259,7 @@ /* Fake super-io ports for PREP platform (Intel 82378ZB) */ typedef struct sysctrl_t { qemu_irq reset_irq; - m48t59_t *nvram; + M48t59State *nvram; uint8_t state; uint8_t syscontrol; uint8_t fake_io[2]; @@ -482,9 +464,6 @@ sysctrl_t *sysctrl = opaque; addr = prep_IO_address(sysctrl, addr); -#ifdef TARGET_WORDS_BIGENDIAN - value = bswap16(value); -#endif PPC_IO_DPRINTF("0x" TARGET_FMT_plx " => 0x%08" PRIx32 "\n", addr, value); cpu_outw(addr, value); } @@ -496,9 +475,6 @@ addr = prep_IO_address(sysctrl, addr); ret = cpu_inw(addr); -#ifdef TARGET_WORDS_BIGENDIAN - ret = bswap16(ret); -#endif PPC_IO_DPRINTF("0x" TARGET_FMT_plx " <= 0x%08" PRIx32 "\n", addr, ret); return ret; @@ -510,9 +486,6 @@ sysctrl_t *sysctrl = opaque; addr = prep_IO_address(sysctrl, addr); -#ifdef TARGET_WORDS_BIGENDIAN - value = bswap32(value); -#endif PPC_IO_DPRINTF("0x" TARGET_FMT_plx " => 0x%08" PRIx32 "\n", addr, value); cpu_outl(addr, value); } @@ -524,9 +497,6 @@ addr = prep_IO_address(sysctrl, addr); ret = cpu_inl(addr); -#ifdef TARGET_WORDS_BIGENDIAN - ret = bswap32(ret); -#endif PPC_IO_DPRINTF("0x" TARGET_FMT_plx " <= 0x%08" PRIx32 "\n", addr, ret); return ret; @@ -546,6 +516,15 @@ #define NVRAM_SIZE 0x2000 +static void cpu_request_exit(void *opaque, int irq, int level) +{ + CPUState *env = cpu_single_env; + + if (env && level) { + cpu_exit(env); + } +} + /* PowerPC PREP hardware initialisation */ static void ppc_prep_init (ram_addr_t ram_size, const char *boot_device, @@ -554,16 +533,18 @@ const char *initrd_filename, const char *cpu_model) { - CPUState *env = NULL, *envs[MAX_CPUS]; + CPUState *env = NULL; char *filename; nvram_t nvram; - m48t59_t *m48t59; + M48t59State *m48t59; int PPC_io_memory; int linux_boot, i, nb_nics1, bios_size; ram_addr_t ram_offset, bios_offset; - uint32_t kernel_base, kernel_size, initrd_base, initrd_size; + uint32_t kernel_base, initrd_base; + long kernel_size, initrd_size; PCIBus *pci_bus; qemu_irq *i8259; + qemu_irq *cpu_exit_irq; int ppc_boot_device; DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; DriveInfo *fd[MAX_FD]; @@ -589,15 +570,14 @@ cpu_ppc_tb_init(env, 100UL * 1000UL * 1000UL); } qemu_register_reset((QEMUResetHandler*)&cpu_reset, env); - envs[i] = env; } /* allocate RAM */ - ram_offset = qemu_ram_alloc(ram_size); + ram_offset = qemu_ram_alloc(NULL, "ppc_prep.ram", ram_size); cpu_register_physical_memory(0, ram_size, ram_offset); /* allocate and load BIOS */ - bios_offset = qemu_ram_alloc(BIOS_SIZE); + bios_offset = qemu_ram_alloc(NULL, "ppc_prep.bios", BIOS_SIZE); if (bios_name == NULL) bios_name = BIOS_FILENAME; filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); @@ -620,9 +600,6 @@ if (filename) { qemu_free(filename); } - if (env->nip < 0xFFF80000 && bios_size < 0x00100000) { - hw_error("PowerPC 601 / 620 / 970 need a 1MB BIOS\n"); - } if (linux_boot) { kernel_base = KERNEL_LOAD_ADDR; @@ -678,14 +655,15 @@ // pci_bus = i440fx_init(); /* Register 8 MB of ISA IO space (needed for non-contiguous map) */ PPC_io_memory = cpu_register_io_memory(PPC_prep_io_read, - PPC_prep_io_write, sysctrl); + PPC_prep_io_write, sysctrl, + DEVICE_LITTLE_ENDIAN); cpu_register_physical_memory(0x80000000, 0x00800000, PPC_io_memory); /* init basic PC hardware */ - pci_vga_init(pci_bus, 0, 0); + pci_vga_init(pci_bus); // openpic = openpic_init(0x00000000, 0xF0000000, 1); // pit = pit_init(0x40, i8259[0]); - rtc_init(2000); + rtc_init(2000, NULL); if (serial_hds[0]) serial_isa_init(0, serial_hds[0]); @@ -718,7 +696,10 @@ hd[2 * i + 1]); } isa_create_simple("i8042"); - DMA_init(1); + + cpu_exit_irq = qemu_allocate_irqs(cpu_request_exit, NULL, 1); + DMA_init(1, cpu_exit_irq); + // SB16_init(); for(i = 0; i < MAX_FD; i++) { @@ -740,12 +721,13 @@ register_ioport_write(0x0800, 0x52, 1, &PREP_io_800_writeb, sysctrl); /* PCI intack location */ PPC_io_memory = cpu_register_io_memory(PPC_intack_read, - PPC_intack_write, NULL); + PPC_intack_write, NULL, + DEVICE_LITTLE_ENDIAN); cpu_register_physical_memory(0xBFFFFFF0, 0x4, PPC_io_memory); /* PowerPC control and status register group */ #if 0 PPC_io_memory = cpu_register_io_memory(PPC_XCSR_read, PPC_XCSR_write, - NULL); + NULL, DEVICE_LITTLE_ENDIAN); cpu_register_physical_memory(0xFEFF0000, 0x1000, PPC_io_memory); #endif diff -Nru qemu-kvm-0.12.5+noroms/hw/prep_pci.c qemu-kvm-0.14.1/hw/prep_pci.c --- qemu-kvm-0.12.5+noroms/hw/prep_pci.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/prep_pci.c 2011-05-11 13:29:46.000000000 +0000 @@ -49,18 +49,14 @@ static void PPC_PCIIO_writew (void *opaque, target_phys_addr_t addr, uint32_t val) { PREPPCIState *s = opaque; -#ifdef TARGET_WORDS_BIGENDIAN val = bswap16(val); -#endif pci_data_write(s->bus, PPC_PCIIO_config(addr), val, 2); } static void PPC_PCIIO_writel (void *opaque, target_phys_addr_t addr, uint32_t val) { PREPPCIState *s = opaque; -#ifdef TARGET_WORDS_BIGENDIAN val = bswap32(val); -#endif pci_data_write(s->bus, PPC_PCIIO_config(addr), val, 4); } @@ -77,9 +73,7 @@ PREPPCIState *s = opaque; uint32_t val; val = pci_data_read(s->bus, PPC_PCIIO_config(addr), 2); -#ifdef TARGET_WORDS_BIGENDIAN val = bswap16(val); -#endif return val; } @@ -88,9 +82,7 @@ PREPPCIState *s = opaque; uint32_t val; val = pci_data_read(s->bus, PPC_PCIIO_config(addr), 4); -#ifdef TARGET_WORDS_BIGENDIAN val = bswap32(val); -#endif return val; } @@ -133,7 +125,8 @@ pci_host_data_register_ioport(0xcfc, s); PPC_io_memory = cpu_register_io_memory(PPC_PCIIO_read, - PPC_PCIIO_write, s); + PPC_PCIIO_write, s, + DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(0x80800000, 0x00400000, PPC_io_memory); /* PCI host bridge */ @@ -145,7 +138,6 @@ pci_config_set_class(d->config, PCI_CLASS_BRIDGE_HOST); d->config[0x0C] = 0x08; // cache_line_size d->config[0x0D] = 0x10; // latency_timer - d->config[PCI_HEADER_TYPE] = PCI_HEADER_TYPE_NORMAL; // header_type d->config[0x34] = 0x00; // capabilities_pointer return s->bus; diff -Nru qemu-kvm-0.12.5+noroms/hw/ps2.c qemu-kvm-0.14.1/hw/ps2.c --- qemu-kvm-0.12.5+noroms/hw/ps2.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/ps2.c 2011-05-11 13:29:46.000000000 +0000 @@ -185,6 +185,7 @@ { s->scan_enabled = 1; s->scancode_set = 2; + kbd_put_ledstate(0); } void ps2_write_keyboard(void *opaque, int val) @@ -259,6 +260,7 @@ s->common.write_cmd = -1; break; case KBD_CMD_SET_LEDS: + kbd_put_ledstate(val); ps2_queue(&s->common, KBD_REPLY_ACK); s->common.write_cmd = -1; break; @@ -593,7 +595,7 @@ s->common.update_irq = update_irq; s->common.update_arg = update_arg; s->scancode_set = 2; - vmstate_register(0, &vmstate_ps2_keyboard, s); + vmstate_register(NULL, 0, &vmstate_ps2_keyboard, s); qemu_add_kbd_event_handler(ps2_put_keycode, s); qemu_register_reset(ps2_kbd_reset, s); return s; @@ -605,7 +607,7 @@ s->common.update_irq = update_irq; s->common.update_arg = update_arg; - vmstate_register(0, &vmstate_ps2_mouse, s); + vmstate_register(NULL, 0, &vmstate_ps2_mouse, s); qemu_add_mouse_event_handler(ps2_mouse_event, s, 0, "QEMU PS/2 Mouse"); qemu_register_reset(ps2_mouse_reset, s); return s; diff -Nru qemu-kvm-0.12.5+noroms/hw/pxa2xx.c qemu-kvm-0.14.1/hw/pxa2xx.c --- qemu-kvm-0.12.5+noroms/hw/pxa2xx.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/pxa2xx.c 2011-05-11 13:29:46.000000000 +0000 @@ -15,6 +15,7 @@ #include "ssi.h" #include "qemu-timer.h" #include "qemu-char.h" +#include "blockdev.h" static struct { target_phys_addr_t io_base; @@ -124,7 +125,7 @@ break; default: /* Read-write registers */ - if (addr >= PMCR && addr <= PCMD31 && !(addr & 3)) { + if (!(addr & 3)) { s->pm_regs[addr >> 2] = value; break; } @@ -635,6 +636,7 @@ { s->sssr &= ~(0xf << 12); /* Clear RFL */ s->sssr &= ~(0xf << 8); /* Clear TFL */ + s->sssr &= ~SSSR_TFS; s->sssr &= ~SSSR_TNF; if (s->enable) { s->sssr |= ((s->rx_level - 1) & 0xf) << 12; @@ -642,14 +644,13 @@ s->sssr |= SSSR_RFS; else s->sssr &= ~SSSR_RFS; - if (0 <= SSCR1_TFT(s->sscr[1])) - s->sssr |= SSSR_TFS; - else - s->sssr &= ~SSSR_TFS; if (s->rx_level) s->sssr |= SSSR_RNE; else s->sssr &= ~SSSR_RNE; + /* TX FIFO is never filled, so it is always in underrun + condition if SSP is enabled */ + s->sssr |= SSSR_TFS; s->sssr |= SSSR_TNF; } @@ -858,9 +859,10 @@ sysbus_init_irq(dev, &s->irq); iomemtype = cpu_register_io_memory(pxa2xx_ssp_readfn, - pxa2xx_ssp_writefn, s); + pxa2xx_ssp_writefn, s, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, 0x1000, iomemtype); - register_savevm("pxa2xx_ssp", -1, 0, + register_savevm(&dev->qdev, "pxa2xx_ssp", -1, 0, pxa2xx_ssp_save, pxa2xx_ssp_load, s); s->bus = ssi_create_bus(&dev->qdev, "ssi"); @@ -1474,7 +1476,7 @@ VMSTATE_UINT8(ibmr, PXA2xxI2CState), VMSTATE_UINT8(data, PXA2xxI2CState), VMSTATE_STRUCT_POINTER(slave, PXA2xxI2CState, - vmstate_pxa2xx_i2c, PXA2xxI2CSlaveState *), + vmstate_pxa2xx_i2c_slave, PXA2xxI2CSlaveState *), VMSTATE_END_OF_LIST() } }; @@ -1511,11 +1513,11 @@ s->offset = base - (base & (~region_size) & TARGET_PAGE_MASK); iomemtype = cpu_register_io_memory(pxa2xx_i2c_readfn, - pxa2xx_i2c_writefn, s); + pxa2xx_i2c_writefn, s, DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base & ~region_size, region_size + 1, iomemtype); - vmstate_register(base, &vmstate_pxa2xx_i2c, s); + vmstate_register(NULL, base, &vmstate_pxa2xx_i2c, s); return s; } @@ -1748,10 +1750,10 @@ pxa2xx_i2s_reset(s); iomemtype = cpu_register_io_memory(pxa2xx_i2s_readfn, - pxa2xx_i2s_writefn, s); + pxa2xx_i2s_writefn, s, DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base, 0x100000, iomemtype); - register_savevm("pxa2xx_i2s", base, 0, + register_savevm(NULL, "pxa2xx_i2s", base, 0, pxa2xx_i2s_save, pxa2xx_i2s_load, s); return s; @@ -1876,8 +1878,9 @@ s->control[0] = value; if (!(value & (1 << 4))) /* RXE */ s->rx_len = s->rx_start = 0; - if (!(value & (1 << 3))) /* TXE */ - /* Nop */; + if (!(value & (1 << 3))) { /* TXE */ + /* Nop */ + } s->enable = value & 1; /* ITR */ if (!s->enable) s->status[0] = 0; @@ -2007,14 +2010,15 @@ pxa2xx_fir_reset(s); iomemtype = cpu_register_io_memory(pxa2xx_fir_readfn, - pxa2xx_fir_writefn, s); + pxa2xx_fir_writefn, s, DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base, 0x1000, iomemtype); if (chr) qemu_chr_add_handlers(chr, pxa2xx_fir_is_empty, pxa2xx_fir_rx, pxa2xx_fir_event, s); - register_savevm("pxa2xx_fir", 0, 0, pxa2xx_fir_save, pxa2xx_fir_load, s); + register_savevm(NULL, "pxa2xx_fir", 0, 0, pxa2xx_fir_save, + pxa2xx_fir_load, s); return s; } @@ -2053,9 +2057,11 @@ /* SDRAM & Internal Memory Storage */ cpu_register_physical_memory(PXA2XX_SDRAM_BASE, - sdram_size, qemu_ram_alloc(sdram_size) | IO_MEM_RAM); + sdram_size, qemu_ram_alloc(NULL, "pxa270.sdram", + sdram_size) | IO_MEM_RAM); cpu_register_physical_memory(PXA2XX_INTERNAL_BASE, - 0x40000, qemu_ram_alloc(0x40000) | IO_MEM_RAM); + 0x40000, qemu_ram_alloc(NULL, "pxa270.internal", + 0x40000) | IO_MEM_RAM); s->pic = pxa2xx_pic_init(0x40d00000, s->env); @@ -2076,9 +2082,15 @@ for (i = 0; pxa270_serial[i].io_base; i ++) if (serial_hds[i]) +#ifdef TARGET_WORDS_BIGENDIAN + serial_mm_init(pxa270_serial[i].io_base, 2, + s->pic[pxa270_serial[i].irqn], 14857000/16, + serial_hds[i], 1, 1); +#else serial_mm_init(pxa270_serial[i].io_base, 2, s->pic[pxa270_serial[i].irqn], 14857000/16, - serial_hds[i], 1); + serial_hds[i], 1, 0); +#endif else break; if (serial_hds[i]) @@ -2091,9 +2103,9 @@ s->cm_regs[CCCR >> 2] = 0x02000210; /* 416.0 MHz */ s->clkcfg = 0x00000009; /* Turbo mode active */ iomemtype = cpu_register_io_memory(pxa2xx_cm_readfn, - pxa2xx_cm_writefn, s); + pxa2xx_cm_writefn, s, DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(s->cm_base, 0x1000, iomemtype); - register_savevm("pxa2xx_cm", 0, 0, pxa2xx_cm_save, pxa2xx_cm_load, s); + register_savevm(NULL, "pxa2xx_cm", 0, 0, pxa2xx_cm_save, pxa2xx_cm_load, s); cpu_arm_set_cp_io(s->env, 14, pxa2xx_cp14_read, pxa2xx_cp14_write, s); @@ -2102,15 +2114,15 @@ s->mm_regs[MDREFR >> 2] = 0x03ca4000; s->mm_regs[MECR >> 2] = 0x00000001; /* Two PC Card sockets */ iomemtype = cpu_register_io_memory(pxa2xx_mm_readfn, - pxa2xx_mm_writefn, s); + pxa2xx_mm_writefn, s, DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(s->mm_base, 0x1000, iomemtype); - register_savevm("pxa2xx_mm", 0, 0, pxa2xx_mm_save, pxa2xx_mm_load, s); + register_savevm(NULL, "pxa2xx_mm", 0, 0, pxa2xx_mm_save, pxa2xx_mm_load, s); s->pm_base = 0x40f00000; iomemtype = cpu_register_io_memory(pxa2xx_pm_readfn, - pxa2xx_pm_writefn, s); + pxa2xx_pm_writefn, s, DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(s->pm_base, 0x100, iomemtype); - register_savevm("pxa2xx_pm", 0, 0, pxa2xx_pm_save, pxa2xx_pm_load, s); + register_savevm(NULL, "pxa2xx_pm", 0, 0, pxa2xx_pm_save, pxa2xx_pm_load, s); for (i = 0; pxa27x_ssp[i].io_base; i ++); s->ssp = (SSIBus **)qemu_mallocz(sizeof(SSIBus *) * i); @@ -2122,7 +2134,8 @@ } if (usb_enabled) { - usb_ohci_init_pxa(0x4c000000, 3, -1, s->pic[PXA2XX_PIC_USBH1]); + sysbus_create_simple("sysbus-ohci", 0x4c000000, + s->pic[PXA2XX_PIC_USBH1]); } s->pcmcia[0] = pxa2xx_pcmcia_init(0x20000000); @@ -2130,10 +2143,11 @@ s->rtc_base = 0x40900000; iomemtype = cpu_register_io_memory(pxa2xx_rtc_readfn, - pxa2xx_rtc_writefn, s); + pxa2xx_rtc_writefn, s, DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(s->rtc_base, 0x1000, iomemtype); pxa2xx_rtc_init(s); - register_savevm("pxa2xx_rtc", 0, 0, pxa2xx_rtc_save, pxa2xx_rtc_load, s); + register_savevm(NULL, "pxa2xx_rtc", 0, 0, pxa2xx_rtc_save, + pxa2xx_rtc_load, s); s->i2c[0] = pxa2xx_i2c_init(0x40301600, s->pic[PXA2XX_PIC_I2C], 0xffff); s->i2c[1] = pxa2xx_i2c_init(0x40f00100, s->pic[PXA2XX_PIC_PWRI2C], 0xff); @@ -2144,7 +2158,7 @@ /* GPIO1 resets the processor */ /* The handler can be overridden by board-specific code */ - pxa2xx_gpio_out_set(s->gpio, 1, s->reset); + qdev_connect_gpio_out(s->gpio, 1, s->reset); return s; } @@ -2166,9 +2180,11 @@ /* SDRAM & Internal Memory Storage */ cpu_register_physical_memory(PXA2XX_SDRAM_BASE, sdram_size, - qemu_ram_alloc(sdram_size) | IO_MEM_RAM); + qemu_ram_alloc(NULL, "pxa255.sdram", + sdram_size) | IO_MEM_RAM); cpu_register_physical_memory(PXA2XX_INTERNAL_BASE, PXA2XX_INTERNAL_SIZE, - qemu_ram_alloc(PXA2XX_INTERNAL_SIZE) | IO_MEM_RAM); + qemu_ram_alloc(NULL, "pxa255.internal", + PXA2XX_INTERNAL_SIZE) | IO_MEM_RAM); s->pic = pxa2xx_pic_init(0x40d00000, s->env); @@ -2187,12 +2203,19 @@ s->pic[PXA2XX_PIC_MMC], s->dma); for (i = 0; pxa255_serial[i].io_base; i ++) - if (serial_hds[i]) + if (serial_hds[i]) { +#ifdef TARGET_WORDS_BIGENDIAN serial_mm_init(pxa255_serial[i].io_base, 2, s->pic[pxa255_serial[i].irqn], 14745600/16, - serial_hds[i], 1); - else + serial_hds[i], 1, 1); +#else + serial_mm_init(pxa255_serial[i].io_base, 2, + s->pic[pxa255_serial[i].irqn], 14745600/16, + serial_hds[i], 1, 0); +#endif + } else { break; + } if (serial_hds[i]) s->fir = pxa2xx_fir_init(0x40800000, s->pic[PXA2XX_PIC_ICP], s->dma, serial_hds[i]); @@ -2203,9 +2226,9 @@ s->cm_regs[CCCR >> 2] = 0x02000210; /* 416.0 MHz */ s->clkcfg = 0x00000009; /* Turbo mode active */ iomemtype = cpu_register_io_memory(pxa2xx_cm_readfn, - pxa2xx_cm_writefn, s); + pxa2xx_cm_writefn, s, DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(s->cm_base, 0x1000, iomemtype); - register_savevm("pxa2xx_cm", 0, 0, pxa2xx_cm_save, pxa2xx_cm_load, s); + register_savevm(NULL, "pxa2xx_cm", 0, 0, pxa2xx_cm_save, pxa2xx_cm_load, s); cpu_arm_set_cp_io(s->env, 14, pxa2xx_cp14_read, pxa2xx_cp14_write, s); @@ -2214,15 +2237,15 @@ s->mm_regs[MDREFR >> 2] = 0x03ca4000; s->mm_regs[MECR >> 2] = 0x00000001; /* Two PC Card sockets */ iomemtype = cpu_register_io_memory(pxa2xx_mm_readfn, - pxa2xx_mm_writefn, s); + pxa2xx_mm_writefn, s, DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(s->mm_base, 0x1000, iomemtype); - register_savevm("pxa2xx_mm", 0, 0, pxa2xx_mm_save, pxa2xx_mm_load, s); + register_savevm(NULL, "pxa2xx_mm", 0, 0, pxa2xx_mm_save, pxa2xx_mm_load, s); s->pm_base = 0x40f00000; iomemtype = cpu_register_io_memory(pxa2xx_pm_readfn, - pxa2xx_pm_writefn, s); + pxa2xx_pm_writefn, s, DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(s->pm_base, 0x100, iomemtype); - register_savevm("pxa2xx_pm", 0, 0, pxa2xx_pm_save, pxa2xx_pm_load, s); + register_savevm(NULL, "pxa2xx_pm", 0, 0, pxa2xx_pm_save, pxa2xx_pm_load, s); for (i = 0; pxa255_ssp[i].io_base; i ++); s->ssp = (SSIBus **)qemu_mallocz(sizeof(SSIBus *) * i); @@ -2234,7 +2257,8 @@ } if (usb_enabled) { - usb_ohci_init_pxa(0x4c000000, 3, -1, s->pic[PXA2XX_PIC_USBH1]); + sysbus_create_simple("sysbus-ohci", 0x4c000000, + s->pic[PXA2XX_PIC_USBH1]); } s->pcmcia[0] = pxa2xx_pcmcia_init(0x20000000); @@ -2242,10 +2266,11 @@ s->rtc_base = 0x40900000; iomemtype = cpu_register_io_memory(pxa2xx_rtc_readfn, - pxa2xx_rtc_writefn, s); + pxa2xx_rtc_writefn, s, DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(s->rtc_base, 0x1000, iomemtype); pxa2xx_rtc_init(s); - register_savevm("pxa2xx_rtc", 0, 0, pxa2xx_rtc_save, pxa2xx_rtc_load, s); + register_savevm(NULL, "pxa2xx_rtc", 0, 0, pxa2xx_rtc_save, + pxa2xx_rtc_load, s); s->i2c[0] = pxa2xx_i2c_init(0x40301600, s->pic[PXA2XX_PIC_I2C], 0xffff); s->i2c[1] = pxa2xx_i2c_init(0x40f00100, s->pic[PXA2XX_PIC_PWRI2C], 0xff); @@ -2254,7 +2279,7 @@ /* GPIO1 resets the processor */ /* The handler can be overridden by board-specific code */ - pxa2xx_gpio_out_set(s->gpio, 1, s->reset); + qdev_connect_gpio_out(s->gpio, 1, s->reset); return s; } diff -Nru qemu-kvm-0.12.5+noroms/hw/pxa2xx_dma.c qemu-kvm-0.14.1/hw/pxa2xx_dma.c --- qemu-kvm-0.12.5+noroms/hw/pxa2xx_dma.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/pxa2xx_dma.c 2011-05-11 13:29:46.000000000 +0000 @@ -504,10 +504,10 @@ memset(s->req, 0, sizeof(uint8_t) * PXA2XX_DMA_NUM_REQUESTS); iomemtype = cpu_register_io_memory(pxa2xx_dma_readfn, - pxa2xx_dma_writefn, s); + pxa2xx_dma_writefn, s, DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base, 0x00010000, iomemtype); - register_savevm("pxa2xx_dma", 0, 0, pxa2xx_dma_save, pxa2xx_dma_load, s); + register_savevm(NULL, "pxa2xx_dma", 0, 0, pxa2xx_dma_save, pxa2xx_dma_load, s); return s; } diff -Nru qemu-kvm-0.12.5+noroms/hw/pxa2xx_gpio.c qemu-kvm-0.14.1/hw/pxa2xx_gpio.c --- qemu-kvm-0.12.5+noroms/hw/pxa2xx_gpio.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/pxa2xx_gpio.c 2011-05-11 13:29:46.000000000 +0000 @@ -8,15 +8,18 @@ */ #include "hw.h" +#include "sysbus.h" #include "pxa.h" #define PXA2XX_GPIO_BANKS 4 +typedef struct PXA2xxGPIOInfo PXA2xxGPIOInfo; struct PXA2xxGPIOInfo { - qemu_irq *pic; + SysBusDevice busdev; + qemu_irq irq0, irq1, irqX; int lines; + int ncpu; CPUState *cpu_env; - qemu_irq *in; /* XXX: GNU C vectors are more suitable */ uint32_t ilevel[PXA2XX_GPIO_BANKS]; @@ -66,19 +69,19 @@ static void pxa2xx_gpio_irq_update(PXA2xxGPIOInfo *s) { if (s->status[0] & (1 << 0)) - qemu_irq_raise(s->pic[PXA2XX_PIC_GPIO_0]); + qemu_irq_raise(s->irq0); else - qemu_irq_lower(s->pic[PXA2XX_PIC_GPIO_0]); + qemu_irq_lower(s->irq0); if (s->status[0] & (1 << 1)) - qemu_irq_raise(s->pic[PXA2XX_PIC_GPIO_1]); + qemu_irq_raise(s->irq1); else - qemu_irq_lower(s->pic[PXA2XX_PIC_GPIO_1]); + qemu_irq_lower(s->irq1); if ((s->status[0] & ~3) | s->status[1] | s->status[2] | s->status[3]) - qemu_irq_raise(s->pic[PXA2XX_PIC_GPIO_X]); + qemu_irq_raise(s->irqX); else - qemu_irq_lower(s->pic[PXA2XX_PIC_GPIO_X]); + qemu_irq_lower(s->irqX); } /* Bitmap of pins used as standby and sleep wake-up sources. */ @@ -249,96 +252,89 @@ pxa2xx_gpio_write }; -static void pxa2xx_gpio_save(QEMUFile *f, void *opaque) -{ - PXA2xxGPIOInfo *s = (PXA2xxGPIOInfo *) opaque; - int i; - - qemu_put_be32(f, s->lines); - - for (i = 0; i < PXA2XX_GPIO_BANKS; i ++) { - qemu_put_be32s(f, &s->ilevel[i]); - qemu_put_be32s(f, &s->olevel[i]); - qemu_put_be32s(f, &s->dir[i]); - qemu_put_be32s(f, &s->rising[i]); - qemu_put_be32s(f, &s->falling[i]); - qemu_put_be32s(f, &s->status[i]); - qemu_put_be32s(f, &s->gafr[i * 2 + 0]); - qemu_put_be32s(f, &s->gafr[i * 2 + 1]); - - qemu_put_be32s(f, &s->prev_level[i]); - } -} - -static int pxa2xx_gpio_load(QEMUFile *f, void *opaque, int version_id) +DeviceState *pxa2xx_gpio_init(target_phys_addr_t base, + CPUState *env, qemu_irq *pic, int lines) { - PXA2xxGPIOInfo *s = (PXA2xxGPIOInfo *) opaque; - int i; + DeviceState *dev; - if (qemu_get_be32(f) != s->lines) - return -EINVAL; + dev = qdev_create(NULL, "pxa2xx-gpio"); + qdev_prop_set_int32(dev, "lines", lines); + qdev_prop_set_int32(dev, "ncpu", env->cpu_index); + qdev_init_nofail(dev); - for (i = 0; i < PXA2XX_GPIO_BANKS; i ++) { - qemu_get_be32s(f, &s->ilevel[i]); - qemu_get_be32s(f, &s->olevel[i]); - qemu_get_be32s(f, &s->dir[i]); - qemu_get_be32s(f, &s->rising[i]); - qemu_get_be32s(f, &s->falling[i]); - qemu_get_be32s(f, &s->status[i]); - qemu_get_be32s(f, &s->gafr[i * 2 + 0]); - qemu_get_be32s(f, &s->gafr[i * 2 + 1]); - - qemu_get_be32s(f, &s->prev_level[i]); - } + sysbus_mmio_map(sysbus_from_qdev(dev), 0, base); + sysbus_connect_irq(sysbus_from_qdev(dev), 0, pic[PXA2XX_PIC_GPIO_0]); + sysbus_connect_irq(sysbus_from_qdev(dev), 1, pic[PXA2XX_PIC_GPIO_1]); + sysbus_connect_irq(sysbus_from_qdev(dev), 2, pic[PXA2XX_PIC_GPIO_X]); - return 0; + return dev; } -PXA2xxGPIOInfo *pxa2xx_gpio_init(target_phys_addr_t base, - CPUState *env, qemu_irq *pic, int lines) +static int pxa2xx_gpio_initfn(SysBusDevice *dev) { int iomemtype; PXA2xxGPIOInfo *s; - s = (PXA2xxGPIOInfo *) - qemu_mallocz(sizeof(PXA2xxGPIOInfo)); - memset(s, 0, sizeof(PXA2xxGPIOInfo)); - s->pic = pic; - s->lines = lines; - s->cpu_env = env; - s->in = qemu_allocate_irqs(pxa2xx_gpio_set, s, lines); - - iomemtype = cpu_register_io_memory(pxa2xx_gpio_readfn, - pxa2xx_gpio_writefn, s); - cpu_register_physical_memory(base, 0x00001000, iomemtype); + s = FROM_SYSBUS(PXA2xxGPIOInfo, dev); - register_savevm("pxa2xx_gpio", 0, 0, - pxa2xx_gpio_save, pxa2xx_gpio_load, s); + s->cpu_env = qemu_get_cpu(s->ncpu); - return s; -} + qdev_init_gpio_in(&dev->qdev, pxa2xx_gpio_set, s->lines); + qdev_init_gpio_out(&dev->qdev, s->handler, s->lines); -qemu_irq *pxa2xx_gpio_in_get(PXA2xxGPIOInfo *s) -{ - return s->in; -} + iomemtype = cpu_register_io_memory(pxa2xx_gpio_readfn, + pxa2xx_gpio_writefn, s, DEVICE_NATIVE_ENDIAN); -void pxa2xx_gpio_out_set(PXA2xxGPIOInfo *s, - int line, qemu_irq handler) -{ - if (line >= s->lines) { - printf("%s: No GPIO pin %i\n", __FUNCTION__, line); - return; - } + sysbus_init_mmio(dev, 0x1000, iomemtype); + sysbus_init_irq(dev, &s->irq0); + sysbus_init_irq(dev, &s->irq1); + sysbus_init_irq(dev, &s->irqX); - s->handler[line] = handler; + return 0; } /* * Registers a callback to notify on GPLR reads. This normally * shouldn't be needed but it is used for the hack on Spitz machines. */ -void pxa2xx_gpio_read_notifier(PXA2xxGPIOInfo *s, qemu_irq handler) +void pxa2xx_gpio_read_notifier(DeviceState *dev, qemu_irq handler) { + PXA2xxGPIOInfo *s = FROM_SYSBUS(PXA2xxGPIOInfo, sysbus_from_qdev(dev)); s->read_notify = handler; } + +static const VMStateDescription vmstate_pxa2xx_gpio_regs = { + .name = "pxa2xx-gpio", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField []) { + VMSTATE_INT32(lines, PXA2xxGPIOInfo), + VMSTATE_UINT32_ARRAY(ilevel, PXA2xxGPIOInfo, PXA2XX_GPIO_BANKS), + VMSTATE_UINT32_ARRAY(olevel, PXA2xxGPIOInfo, PXA2XX_GPIO_BANKS), + VMSTATE_UINT32_ARRAY(dir, PXA2xxGPIOInfo, PXA2XX_GPIO_BANKS), + VMSTATE_UINT32_ARRAY(rising, PXA2xxGPIOInfo, PXA2XX_GPIO_BANKS), + VMSTATE_UINT32_ARRAY(falling, PXA2xxGPIOInfo, PXA2XX_GPIO_BANKS), + VMSTATE_UINT32_ARRAY(status, PXA2xxGPIOInfo, PXA2XX_GPIO_BANKS), + VMSTATE_UINT32_ARRAY(gafr, PXA2xxGPIOInfo, PXA2XX_GPIO_BANKS * 2), + VMSTATE_END_OF_LIST(), + }, +}; + +static SysBusDeviceInfo pxa2xx_gpio_info = { + .init = pxa2xx_gpio_initfn, + .qdev.name = "pxa2xx-gpio", + .qdev.desc = "PXA2xx GPIO controller", + .qdev.size = sizeof(PXA2xxGPIOInfo), + .qdev.props = (Property []) { + DEFINE_PROP_INT32("lines", PXA2xxGPIOInfo, lines, 0), + DEFINE_PROP_INT32("ncpu", PXA2xxGPIOInfo, ncpu, 0), + DEFINE_PROP_END_OF_LIST(), + } +}; + +static void pxa2xx_gpio_register(void) +{ + sysbus_register_withprop(&pxa2xx_gpio_info); +} +device_init(pxa2xx_gpio_register); diff -Nru qemu-kvm-0.12.5+noroms/hw/pxa2xx_keypad.c qemu-kvm-0.14.1/hw/pxa2xx_keypad.c --- qemu-kvm-0.12.5+noroms/hw/pxa2xx_keypad.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/pxa2xx_keypad.c 2011-05-11 13:29:46.000000000 +0000 @@ -314,10 +314,10 @@ s->irq = irq; iomemtype = cpu_register_io_memory(pxa2xx_keypad_readfn, - pxa2xx_keypad_writefn, s); + pxa2xx_keypad_writefn, s, DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base, 0x00100000, iomemtype); - register_savevm("pxa2xx_keypad", 0, 0, + register_savevm(NULL, "pxa2xx_keypad", 0, 0, pxa2xx_keypad_save, pxa2xx_keypad_load, s); return s; diff -Nru qemu-kvm-0.12.5+noroms/hw/pxa2xx_lcd.c qemu-kvm-0.14.1/hw/pxa2xx_lcd.c --- qemu-kvm-0.12.5+noroms/hw/pxa2xx_lcd.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/pxa2xx_lcd.c 2011-05-11 13:29:46.000000000 +0000 @@ -796,9 +796,9 @@ if (miny >= 0) { if (s->orientation) - dpy_update(s->ds, miny, 0, maxy, s->xres); + dpy_update(s->ds, miny, 0, maxy - miny, s->xres); else - dpy_update(s->ds, 0, miny, s->xres, maxy); + dpy_update(s->ds, 0, miny, s->xres, maxy - miny); } pxa2xx_lcdc_int_update(s); @@ -929,7 +929,7 @@ pxa2xx_lcdc_orientation(s, graphic_rotate); iomemtype = cpu_register_io_memory(pxa2xx_lcdc_readfn, - pxa2xx_lcdc_writefn, s); + pxa2xx_lcdc_writefn, s, DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base, 0x00100000, iomemtype); s->ds = graphic_console_init(pxa2xx_update_display, @@ -970,7 +970,7 @@ exit(1); } - register_savevm("pxa2xx_lcdc", 0, 0, + register_savevm(NULL, "pxa2xx_lcdc", 0, 0, pxa2xx_lcdc_save, pxa2xx_lcdc_load, s); return s; diff -Nru qemu-kvm-0.12.5+noroms/hw/pxa2xx_mmci.c qemu-kvm-0.14.1/hw/pxa2xx_mmci.c --- qemu-kvm-0.12.5+noroms/hw/pxa2xx_mmci.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/pxa2xx_mmci.c 2011-05-11 13:29:46.000000000 +0000 @@ -528,13 +528,13 @@ s->dma = dma; iomemtype = cpu_register_io_memory(pxa2xx_mmci_readfn, - pxa2xx_mmci_writefn, s); + pxa2xx_mmci_writefn, s, DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base, 0x00100000, iomemtype); /* Instantiate the actual storage */ s->card = sd_init(bd, 0); - register_savevm("pxa2xx_mmci", 0, 0, + register_savevm(NULL, "pxa2xx_mmci", 0, 0, pxa2xx_mmci_save, pxa2xx_mmci_load, s); return s; diff -Nru qemu-kvm-0.12.5+noroms/hw/pxa2xx_pcmcia.c qemu-kvm-0.14.1/hw/pxa2xx_pcmcia.c --- qemu-kvm-0.12.5+noroms/hw/pxa2xx_pcmcia.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/pxa2xx_pcmcia.c 2011-05-11 13:29:46.000000000 +0000 @@ -140,19 +140,19 @@ /* Socket I/O Memory Space */ iomemtype = cpu_register_io_memory(pxa2xx_pcmcia_io_readfn, - pxa2xx_pcmcia_io_writefn, s); + pxa2xx_pcmcia_io_writefn, s, DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base | 0x00000000, 0x04000000, iomemtype); /* Then next 64 MB is reserved */ /* Socket Attribute Memory Space */ iomemtype = cpu_register_io_memory(pxa2xx_pcmcia_attr_readfn, - pxa2xx_pcmcia_attr_writefn, s); + pxa2xx_pcmcia_attr_writefn, s, DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base | 0x08000000, 0x04000000, iomemtype); /* Socket Common Memory Space */ iomemtype = cpu_register_io_memory(pxa2xx_pcmcia_common_readfn, - pxa2xx_pcmcia_common_writefn, s); + pxa2xx_pcmcia_common_writefn, s, DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base | 0x0c000000, 0x04000000, iomemtype); if (base == 0x30000000) diff -Nru qemu-kvm-0.12.5+noroms/hw/pxa2xx_pic.c qemu-kvm-0.14.1/hw/pxa2xx_pic.c --- qemu-kvm-0.12.5+noroms/hw/pxa2xx_pic.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/pxa2xx_pic.c 2011-05-11 13:29:46.000000000 +0000 @@ -300,13 +300,14 @@ /* Enable IC memory-mapped registers access. */ iomemtype = cpu_register_io_memory(pxa2xx_pic_readfn, - pxa2xx_pic_writefn, s); + pxa2xx_pic_writefn, s, DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base, 0x00100000, iomemtype); /* Enable IC coprocessor access. */ cpu_arm_set_cp_io(env, 6, pxa2xx_pic_cp_read, pxa2xx_pic_cp_write, s); - register_savevm("pxa2xx_pic", 0, 0, pxa2xx_pic_save, pxa2xx_pic_load, s); + register_savevm(NULL, "pxa2xx_pic", 0, 0, pxa2xx_pic_save, + pxa2xx_pic_load, s); return qi; } diff -Nru qemu-kvm-0.12.5+noroms/hw/pxa2xx_timer.c qemu-kvm-0.14.1/hw/pxa2xx_timer.c --- qemu-kvm-0.12.5+noroms/hw/pxa2xx_timer.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/pxa2xx_timer.c 2011-05-11 13:29:46.000000000 +0000 @@ -452,10 +452,10 @@ } iomemtype = cpu_register_io_memory(pxa2xx_timer_readfn, - pxa2xx_timer_writefn, s); + pxa2xx_timer_writefn, s, DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base, 0x00001000, iomemtype); - register_savevm("pxa2xx_timer", 0, 0, + register_savevm(NULL, "pxa2xx_timer", 0, 0, pxa2xx_timer_save, pxa2xx_timer_load, s); return s; diff -Nru qemu-kvm-0.12.5+noroms/hw/pxa.h qemu-kvm-0.14.1/hw/pxa.h --- qemu-kvm-0.12.5+noroms/hw/pxa.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/pxa.h 2011-05-11 13:29:46.000000000 +0000 @@ -70,13 +70,9 @@ void pxa27x_timer_init(target_phys_addr_t base, qemu_irq *irqs, qemu_irq irq4); /* pxa2xx_gpio.c */ -typedef struct PXA2xxGPIOInfo PXA2xxGPIOInfo; -PXA2xxGPIOInfo *pxa2xx_gpio_init(target_phys_addr_t base, +DeviceState *pxa2xx_gpio_init(target_phys_addr_t base, CPUState *env, qemu_irq *pic, int lines); -qemu_irq *pxa2xx_gpio_in_get(PXA2xxGPIOInfo *s); -void pxa2xx_gpio_out_set(PXA2xxGPIOInfo *s, - int line, qemu_irq handler); -void pxa2xx_gpio_read_notifier(PXA2xxGPIOInfo *s, qemu_irq handler); +void pxa2xx_gpio_read_notifier(DeviceState *dev, qemu_irq handler); /* pxa2xx_dma.c */ typedef struct PXA2xxDMAState PXA2xxDMAState; @@ -132,7 +128,7 @@ qemu_irq *pic; qemu_irq reset; PXA2xxDMAState *dma; - PXA2xxGPIOInfo *gpio; + DeviceState *gpio; PXA2xxLCDState *lcd; SSIBus **ssp; PXA2xxI2CState *i2c[2]; @@ -213,8 +209,4 @@ PXA2xxState *pxa270_init(unsigned int sdram_size, const char *revision); PXA2xxState *pxa255_init(unsigned int sdram_size); -/* usb-ohci.c */ -void usb_ohci_init_pxa(target_phys_addr_t base, int num_ports, int devfn, - qemu_irq irq); - #endif /* PXA_H */ diff -Nru qemu-kvm-0.12.5+noroms/hw/qdev.c qemu-kvm-0.14.1/hw/qdev.c --- qemu-kvm-0.12.5+noroms/hw/qdev.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/qdev.c 2011-05-11 13:29:46.000000000 +0000 @@ -29,9 +29,10 @@ #include "qdev.h" #include "sysemu.h" #include "monitor.h" -#include "qerror.h" static int qdev_hotplug = 0; +static bool qdev_hot_added = false; +static bool qdev_hot_removed = false; /* This is a nasty hack to allow passing a NULL bus to qdev_create. */ static BusState *main_system_bus; @@ -78,26 +79,11 @@ return NULL; } -/* Create a new device. This only initializes the device state structure - and allows properties to be set. qdev_init should be called to - initialize the actual device emulation. */ -DeviceState *qdev_create(BusState *bus, const char *name) +static DeviceState *qdev_create_from_info(BusState *bus, DeviceInfo *info) { - DeviceInfo *info; DeviceState *dev; - if (!bus) { - if (!main_system_bus) { - main_system_bus = qbus_create(&system_bus_info, NULL, "main-system-bus"); - } - bus = main_system_bus; - } - - info = qdev_find_info(bus->info, name); - if (!info) { - hw_error("Unknown device '%s' for bus '%s'\n", name, bus->info->name); - } - + assert(bus->info == info->bus_info); dev = qemu_mallocz(info->size); dev->info = info; dev->parent_bus = bus; @@ -108,32 +94,46 @@ if (qdev_hotplug) { assert(bus->allow_hotplug); dev->hotplugged = 1; + qdev_hot_added = true; } + dev->instance_id_alias = -1; dev->state = DEV_STATE_CREATED; return dev; } -static int qdev_print_devinfo(DeviceInfo *info, char *dest, int len) +/* Create a new device. This only initializes the device state structure + and allows properties to be set. qdev_init should be called to + initialize the actual device emulation. */ +DeviceState *qdev_create(BusState *bus, const char *name) { - int pos = 0; - int ret; + DeviceInfo *info; + + if (!bus) { + bus = sysbus_get_default(); + } + + info = qdev_find_info(bus->info, name); + if (!info) { + hw_error("Unknown device '%s' for bus '%s'\n", name, bus->info->name); + } - ret = snprintf(dest+pos, len-pos, "name \"%s\", bus %s", - info->name, info->bus_info->name); - pos += MIN(len-pos,ret); + return qdev_create_from_info(bus, info); +} + +static void qdev_print_devinfo(DeviceInfo *info) +{ + error_printf("name \"%s\", bus %s", + info->name, info->bus_info->name); if (info->alias) { - ret = snprintf(dest+pos, len-pos, ", alias \"%s\"", info->alias); - pos += MIN(len-pos,ret); + error_printf(", alias \"%s\"", info->alias); } if (info->desc) { - ret = snprintf(dest+pos, len-pos, ", desc \"%s\"", info->desc); - pos += MIN(len-pos,ret); + error_printf(", desc \"%s\"", info->desc); } if (info->no_user) { - ret = snprintf(dest+pos, len-pos, ", no-user"); - pos += MIN(len-pos,ret); + error_printf(", no-user"); } - return pos; + error_printf("\n"); } static int set_property(const char *name, const char *value, void *opaque) @@ -146,13 +146,52 @@ return 0; if (qdev_prop_parse(dev, name, value) == -1) { - qemu_error("can't set property \"%s\" to \"%s\" for \"%s\"\n", - name, value, dev->info->name); return -1; } return 0; } +int qdev_device_help(QemuOpts *opts) +{ + const char *driver; + DeviceInfo *info; + Property *prop; + + driver = qemu_opt_get(opts, "driver"); + if (driver && !strcmp(driver, "?")) { + for (info = device_info_list; info != NULL; info = info->next) { + if (info->no_user) { + continue; /* not available, don't show */ + } + qdev_print_devinfo(info); + } + return 1; + } + + if (!qemu_opt_get(opts, "?")) { + return 0; + } + + info = qdev_find_info(NULL, driver); + if (!info) { + return 0; + } + + for (prop = info->props; prop && prop->name; prop++) { + /* + * TODO Properties without a parser are just for dirty hacks. + * qdev_prop_ptr is the only such PropertyInfo. It's marked + * for removal. This conditional should be removed along with + * it. + */ + if (!prop->info->parse) { + continue; /* no way to set it, don't show */ + } + error_printf("%s.%s=%s\n", info->name, prop->name, prop->info->name); + } + return 1; +} + DeviceState *qdev_device_add(QemuOpts *opts) { const char *driver, *path, *id; @@ -162,27 +201,15 @@ driver = qemu_opt_get(opts, "driver"); if (!driver) { - qemu_error("-device: no driver specified\n"); - return NULL; - } - if (strcmp(driver, "?") == 0) { - char msg[256]; - for (info = device_info_list; info != NULL; info = info->next) { - qdev_print_devinfo(info, msg, sizeof(msg)); - qemu_error("%s\n", msg); - } + qerror_report(QERR_MISSING_PARAMETER, "driver"); return NULL; } /* find driver */ info = qdev_find_info(NULL, driver); - if (!info) { - qemu_error_new(QERR_DEVICE_NOT_FOUND, driver); - return NULL; - } - if (info->no_user) { - qemu_error("device \"%s\" can't be added via command line\n", - info->name); + if (!info || info->no_user) { + qerror_report(QERR_INVALID_PARAMETER_VALUE, "driver", "a driver name"); + error_printf_unless_qmp("Try with argument '?' for a list.\n"); return NULL; } @@ -190,22 +217,29 @@ path = qemu_opt_get(opts, "bus"); if (path != NULL) { bus = qbus_find(path); + if (!bus) { + return NULL; + } + if (bus->info != info->bus_info) { + qerror_report(QERR_BAD_BUS_FOR_DEVICE, + driver, bus->info->name); + return NULL; + } } else { bus = qbus_find_recursive(main_system_bus, NULL, info->bus_info); - } - if (!bus) { - qemu_error("Did not find %s bus for %s\n", - path ? path : info->bus_info->name, info->name); - return NULL; + if (!bus) { + qerror_report(QERR_NO_BUS_FOR_DEVICE, + info->name, info->bus_info->name); + return NULL; + } } if (qdev_hotplug && !bus->allow_hotplug) { - qemu_error("Bus %s does not support hotplugging\n", - bus->name); + qerror_report(QERR_BUS_NO_HOTPLUG, bus->name); return NULL; } /* create device, set properties */ - qdev = qdev_create(bus, driver); + qdev = qdev_create_from_info(bus, info); id = qemu_opts_id(opts); if (id) { qdev->id = id; @@ -215,20 +249,13 @@ return NULL; } if (qdev_init(qdev) < 0) { - qemu_error("Error initializing device %s\n", driver); + qerror_report(QERR_DEVICE_INIT_FAILED, driver); return NULL; } qdev->opts = opts; return qdev; } -static void qdev_reset(void *opaque) -{ - DeviceState *dev = opaque; - if (dev->info->reset) - dev->info->reset(dev); -} - /* Initialize a device. Device properties should be set before calling this function. IRQs and MMIO regions should be connected/mapped after calling this function. @@ -244,25 +271,73 @@ qdev_free(dev); return rc; } - qemu_register_reset(qdev_reset, dev); - if (dev->info->vmsd) - vmstate_register(-1, dev->info->vmsd, dev); + if (dev->info->vmsd) { + vmstate_register_with_alias_id(dev, -1, dev->info->vmsd, dev, + dev->instance_id_alias, + dev->alias_required_for_version); + } dev->state = DEV_STATE_INITIALIZED; return 0; } +void qdev_set_legacy_instance_id(DeviceState *dev, int alias_id, + int required_for_version) +{ + assert(dev->state == DEV_STATE_CREATED); + dev->instance_id_alias = alias_id; + dev->alias_required_for_version = required_for_version; +} + int qdev_unplug(DeviceState *dev) { if (!dev->parent_bus->allow_hotplug) { - qemu_error("Bus %s does not support hotplugging\n", - dev->parent_bus->name); + qerror_report(QERR_BUS_NO_HOTPLUG, dev->parent_bus->name); return -1; } assert(dev->info->unplug != NULL); + qdev_hot_removed = true; + return dev->info->unplug(dev); } +static int qdev_reset_one(DeviceState *dev, void *opaque) +{ + if (dev->info->reset) { + dev->info->reset(dev); + } + + return 0; +} + +BusState *sysbus_get_default(void) +{ + if (!main_system_bus) { + main_system_bus = qbus_create(&system_bus_info, NULL, + "main-system-bus"); + } + return main_system_bus; +} + +static int qbus_reset_one(BusState *bus, void *opaque) +{ + if (bus->info->reset) { + return bus->info->reset(bus); + } + return 0; +} + +void qdev_reset_all(DeviceState *dev) +{ + qdev_walk_children(dev, qdev_reset_one, qbus_reset_one, NULL); +} + +void qbus_reset_all_fn(void *opaque) +{ + BusState *bus = opaque; + qbus_walk_children(bus, qdev_reset_one, qbus_reset_one, NULL); +} + /* can be used as ->unplug() callback for the simple cases */ int qdev_simple_unplug_cb(DeviceState *dev) { @@ -282,14 +357,17 @@ { DeviceInfo *info = dev->info; - if (qdev_init(dev) < 0) - hw_error("Initialization of device %s failed\n", info->name); + if (qdev_init(dev) < 0) { + error_report("Initialization of device %s failed\n", info->name); + exit(1); + } } /* Unlink device from bus and free the structure. */ void qdev_free(DeviceState *dev) { BusState *bus; + Property *prop; if (dev->state == DEV_STATE_INITIALIZED) { while (dev->num_child_bus) { @@ -297,14 +375,18 @@ qbus_free(bus); } if (dev->info->vmsd) - vmstate_unregister(dev->info->vmsd, dev); + vmstate_unregister(dev, dev->info->vmsd, dev); if (dev->info->exit) dev->info->exit(dev); if (dev->opts) qemu_opts_del(dev->opts); } - qemu_unregister_reset(qdev_reset, dev); QLIST_REMOVE(dev, sibling); + for (prop = dev->info->props; prop && prop->name; prop++) { + if (prop->info->free) { + prop->info->free(dev, prop); + } + } qemu_free(dev); } @@ -317,17 +399,18 @@ qdev_hotplug = 1; } +bool qdev_machine_modified(void) +{ + return qdev_hot_added || qdev_hot_removed; +} + /* Get a character (serial) device interface. */ CharDriverState *qdev_init_chardev(DeviceState *dev) { static int next_serial; - static int next_virtconsole; - /* FIXME: This is a nasty hack that needs to go away. */ - if (strncmp(dev->info->name, "virtio", 6) == 0) { - return virtcon_hds[next_virtconsole++]; - } else { - return serial_hds[next_serial++]; - } + + /* FIXME: This function needs to go away: use chardev properties! */ + return serial_hds[next_serial++]; } BusState *qdev_get_parent_bus(DeviceState *dev) @@ -368,36 +451,68 @@ qdev_prop_set_vlan(dev, "vlan", nd->vlan); if (nd->netdev) qdev_prop_set_netdev(dev, "netdev", nd->netdev); - if (nd->nvectors != NIC_NVECTORS_UNSPECIFIED && + if (nd->nvectors != DEV_NVECTORS_UNSPECIFIED && qdev_prop_exists(dev, "vectors")) { qdev_prop_set_uint32(dev, "vectors", nd->nvectors); } } -static int next_block_unit[IF_COUNT]; +BusState *qdev_get_child_bus(DeviceState *dev, const char *name) +{ + BusState *bus; + + QLIST_FOREACH(bus, &dev->child_bus, sibling) { + if (strcmp(name, bus->name) == 0) { + return bus; + } + } + return NULL; +} -/* Get a block device. This should only be used for single-drive devices - (e.g. SD/Floppy/MTD). Multi-disk devices (scsi/ide) should use the - appropriate bus. */ -BlockDriverState *qdev_init_bdrv(DeviceState *dev, BlockInterfaceType type) +int qbus_walk_children(BusState *bus, qdev_walkerfn *devfn, + qbus_walkerfn *busfn, void *opaque) { - int unit = next_block_unit[type]++; - DriveInfo *dinfo; + DeviceState *dev; + int err; - dinfo = drive_get(type, 0, unit); - return dinfo ? dinfo->bdrv : NULL; + if (busfn) { + err = busfn(bus, opaque); + if (err) { + return err; + } + } + + QLIST_FOREACH(dev, &bus->children, sibling) { + err = qdev_walk_children(dev, devfn, busfn, opaque); + if (err < 0) { + return err; + } + } + + return 0; } -BusState *qdev_get_child_bus(DeviceState *dev, const char *name) +int qdev_walk_children(DeviceState *dev, qdev_walkerfn *devfn, + qbus_walkerfn *busfn, void *opaque) { BusState *bus; + int err; + + if (devfn) { + err = devfn(dev, opaque); + if (err) { + return err; + } + } QLIST_FOREACH(bus, &dev->child_bus, sibling) { - if (strcmp(name, bus->name) == 0) { - return bus; + err = qbus_walk_children(bus, devfn, busfn, opaque); + if (err < 0) { + return err; } } - return NULL; + + return 0; } static BusState *qbus_find_recursive(BusState *bus, const char *name, @@ -428,7 +543,7 @@ return NULL; } -static DeviceState *qdev_find_recursive(BusState *bus, const char *id) +DeviceState *qdev_find_recursive(BusState *bus, const char *id) { DeviceState *dev, *ret; BusState *child; @@ -446,35 +561,33 @@ return NULL; } -static void qbus_list_bus(DeviceState *dev, char *dest, int len) +static void qbus_list_bus(DeviceState *dev) { BusState *child; const char *sep = " "; - int pos = 0; - pos += snprintf(dest+pos, len-pos,"child busses at \"%s\":", - dev->id ? dev->id : dev->info->name); + error_printf("child busses at \"%s\":", + dev->id ? dev->id : dev->info->name); QLIST_FOREACH(child, &dev->child_bus, sibling) { - pos += snprintf(dest+pos, len-pos, "%s\"%s\"", sep, child->name); + error_printf("%s\"%s\"", sep, child->name); sep = ", "; } + error_printf("\n"); } -static void qbus_list_dev(BusState *bus, char *dest, int len) +static void qbus_list_dev(BusState *bus) { DeviceState *dev; const char *sep = " "; - int pos = 0; - pos += snprintf(dest+pos, len-pos, "devices at \"%s\":", - bus->name); + error_printf("devices at \"%s\":", bus->name); QLIST_FOREACH(dev, &bus->children, sibling) { - pos += snprintf(dest+pos, len-pos, "%s\"%s\"", - sep, dev->info->name); + error_printf("%s\"%s\"", sep, dev->info->name); if (dev->id) - pos += snprintf(dest+pos, len-pos, "/\"%s\"", dev->id); + error_printf("/\"%s\"", dev->id); sep = ", "; } + error_printf("\n"); } static BusState *qbus_find_bus(DeviceState *dev, char *elem) @@ -521,7 +634,7 @@ { DeviceState *dev; BusState *bus; - char elem[128], msg[256]; + char elem[128]; int pos, len; /* find start element */ @@ -530,62 +643,75 @@ pos = 0; } else { if (sscanf(path, "%127[^/]%n", elem, &len) != 1) { - qemu_error("path parse error (\"%s\")\n", path); - return NULL; + assert(!path[0]); + elem[0] = len = 0; } bus = qbus_find_recursive(main_system_bus, elem, NULL); if (!bus) { - qemu_error("bus \"%s\" not found\n", elem); + qerror_report(QERR_BUS_NOT_FOUND, elem); return NULL; } pos = len; } for (;;) { + assert(path[pos] == '/' || !path[pos]); + while (path[pos] == '/') { + pos++; + } if (path[pos] == '\0') { - /* we are done */ return bus; } /* find device */ - if (sscanf(path+pos, "/%127[^/]%n", elem, &len) != 1) { - qemu_error("path parse error (\"%s\" pos %d)\n", path, pos); - return NULL; + if (sscanf(path+pos, "%127[^/]%n", elem, &len) != 1) { + assert(0); + elem[0] = len = 0; } pos += len; dev = qbus_find_dev(bus, elem); if (!dev) { - qbus_list_dev(bus, msg, sizeof(msg)); - qemu_error("device \"%s\" not found\n%s\n", elem, msg); + qerror_report(QERR_DEVICE_NOT_FOUND, elem); + if (!monitor_cur_is_qmp()) { + qbus_list_dev(bus); + } return NULL; } + + assert(path[pos] == '/' || !path[pos]); + while (path[pos] == '/') { + pos++; + } if (path[pos] == '\0') { /* last specified element is a device. If it has exactly * one child bus accept it nevertheless */ switch (dev->num_child_bus) { case 0: - qemu_error("device has no child bus (%s)\n", path); + qerror_report(QERR_DEVICE_NO_BUS, elem); return NULL; case 1: return QLIST_FIRST(&dev->child_bus); default: - qbus_list_bus(dev, msg, sizeof(msg)); - qemu_error("device has multiple child busses (%s)\n%s\n", - path, msg); + qerror_report(QERR_DEVICE_MULTIPLE_BUSSES, elem); + if (!monitor_cur_is_qmp()) { + qbus_list_bus(dev); + } return NULL; } } /* find bus */ - if (sscanf(path+pos, "/%127[^/]%n", elem, &len) != 1) { - qemu_error("path parse error (\"%s\" pos %d)\n", path, pos); - return NULL; + if (sscanf(path+pos, "%127[^/]%n", elem, &len) != 1) { + assert(0); + elem[0] = len = 0; } pos += len; bus = qbus_find_bus(dev, elem); if (!bus) { - qbus_list_bus(dev, msg, sizeof(msg)); - qemu_error("child bus \"%s\" not found\n%s\n", elem, msg); + qerror_report(QERR_BUS_NOT_FOUND, elem); + if (!monitor_cur_is_qmp()) { + qbus_list_bus(dev); + } return NULL; } } @@ -624,8 +750,11 @@ if (parent) { QLIST_INSERT_HEAD(&parent->child_bus, bus, sibling); parent->num_child_bus++; + } else if (bus != main_system_bus) { + /* TODO: once all bus devices are qdevified, + only reset handler for main_system_bus should be registered here. */ + qemu_register_reset(qbus_reset_all_fn, bus); } - } BusState *qbus_create(BusInfo *info, DeviceState *parent, const char *name) @@ -648,7 +777,11 @@ if (bus->parent) { QLIST_REMOVE(bus, sibling); bus->parent->num_child_bus--; + } else { + assert(bus != main_system_bus); /* main_system_bus is never freed */ + qemu_unregister_reset(qbus_reset_all_fn, bus); } + qemu_free((void*)bus->name); if (bus->qdev_allocated) { qemu_free(bus); } @@ -665,6 +798,12 @@ if (!props) return; while (props->name) { + /* + * TODO Properties without a print method are just for dirty + * hacks. qdev_prop_ptr is the only such PropertyInfo. It's + * marked for removal. The test props->info->print should be + * removed along with it. + */ if (props->info->print) { props->info->print(dev, props, buf, sizeof(buf)); qdev_printf("%s-prop: %s = %s\n", prefix, props->name, buf); @@ -716,33 +855,72 @@ void do_info_qdm(Monitor *mon) { DeviceInfo *info; - char msg[256]; for (info = device_info_list; info != NULL; info = info->next) { - qdev_print_devinfo(info, msg, sizeof(msg)); - monitor_printf(mon, "%s\n", msg); + qdev_print_devinfo(info); } } -void do_device_add(Monitor *mon, const QDict *qdict) +int do_device_add(Monitor *mon, const QDict *qdict, QObject **ret_data) { QemuOpts *opts; - opts = qemu_opts_parse(&qemu_device_opts, - qdict_get_str(qdict, "config"), "driver"); - if (opts) - qdev_device_add(opts); + opts = qemu_opts_from_qdict(qemu_find_opts("device"), qdict); + if (!opts) { + return -1; + } + if (!monitor_cur_is_qmp() && qdev_device_help(opts)) { + qemu_opts_del(opts); + return 0; + } + if (!qdev_device_add(opts)) { + qemu_opts_del(opts); + return -1; + } + return 0; } -void do_device_del(Monitor *mon, const QDict *qdict) +int do_device_del(Monitor *mon, const QDict *qdict, QObject **ret_data) { const char *id = qdict_get_str(qdict, "id"); DeviceState *dev; dev = qdev_find_recursive(main_system_bus, id); if (NULL == dev) { - qemu_error("Device '%s' not found\n", id); - return; + qerror_report(QERR_DEVICE_NOT_FOUND, id); + return -1; } - qdev_unplug(dev); + return qdev_unplug(dev); +} + +static int qdev_get_fw_dev_path_helper(DeviceState *dev, char *p, int size) +{ + int l = 0; + + if (dev && dev->parent_bus) { + char *d; + l = qdev_get_fw_dev_path_helper(dev->parent_bus->parent, p, size); + if (dev->parent_bus->info->get_fw_dev_path) { + d = dev->parent_bus->info->get_fw_dev_path(dev); + l += snprintf(p + l, size - l, "%s", d); + qemu_free(d); + } else { + l += snprintf(p + l, size - l, "%s", dev->info->name); + } + } + l += snprintf(p + l , size - l, "/"); + + return l; +} + +char* qdev_get_fw_dev_path(DeviceState *dev) +{ + char path[128]; + int l; + + l = qdev_get_fw_dev_path_helper(dev, path, 128); + + path[l-1] = '\0'; + + return strdup(path); } diff -Nru qemu-kvm-0.12.5+noroms/hw/qdev.h qemu-kvm-0.14.1/hw/qdev.h --- qemu-kvm-0.12.5+noroms/hw/qdev.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/qdev.h 2011-05-11 13:29:46.000000000 +0000 @@ -2,7 +2,6 @@ #define QDEV_H #include "hw.h" -#include "sysemu.h" #include "qemu-queue.h" #include "qemu-char.h" #include "qemu-option.h" @@ -24,6 +23,10 @@ DEV_STATE_INITIALIZED, }; +enum { + DEV_NVECTORS_UNSPECIFIED = -1, +}; + /* This structure should not be accessed directly. We declare it here so that it can be embedded in individual device state structures. */ struct DeviceState { @@ -40,13 +43,27 @@ QLIST_HEAD(, BusState) child_bus; int num_child_bus; QLIST_ENTRY(DeviceState) sibling; + int instance_id_alias; + int alias_required_for_version; }; typedef void (*bus_dev_printfn)(Monitor *mon, DeviceState *dev, int indent); +typedef char *(*bus_get_dev_path)(DeviceState *dev); +/* + * This callback is used to create Open Firmware device path in accordance with + * OF spec http://forthworks.com/standards/of1275.pdf. Indicidual bus bindings + * can be found here http://playground.sun.com/1275/bindings/. + */ +typedef char *(*bus_get_fw_dev_path)(DeviceState *dev); +typedef int (qbus_resetfn)(BusState *bus); + struct BusInfo { const char *name; size_t size; bus_dev_printfn print_dev; + bus_get_dev_path get_dev_path; + bus_get_fw_dev_path get_fw_dev_path; + qbus_resetfn *reset; Property *props; }; @@ -64,6 +81,7 @@ const char *name; PropertyInfo *info; int offset; + int bitnr; void *defval; }; @@ -82,6 +100,7 @@ PROP_TYPE_NETDEV, PROP_TYPE_VLAN, PROP_TYPE_PTR, + PROP_TYPE_BIT, }; struct PropertyInfo { @@ -90,6 +109,7 @@ enum PropertyType type; int (*parse)(DeviceState *dev, Property *prop, const char *str); int (*print)(DeviceState *dev, Property *prop, char *dest, size_t len); + void (*free)(DeviceState *dev, Property *prop); }; typedef struct GlobalProperty { @@ -102,13 +122,17 @@ /*** Board API. This should go away once we have a machine config file. ***/ DeviceState *qdev_create(BusState *bus, const char *name); +int qdev_device_help(QemuOpts *opts); DeviceState *qdev_device_add(QemuOpts *opts); int qdev_init(DeviceState *dev) QEMU_WARN_UNUSED_RESULT; void qdev_init_nofail(DeviceState *dev); +void qdev_set_legacy_instance_id(DeviceState *dev, int alias_id, + int required_for_version); int qdev_unplug(DeviceState *dev); void qdev_free(DeviceState *dev); int qdev_simple_unplug_cb(DeviceState *dev); void qdev_machine_creation_done(void); +bool qdev_machine_modified(void); qemu_irq qdev_get_gpio_in(DeviceState *dev, int n); void qdev_connect_gpio_out(DeviceState *dev, int n, qemu_irq pin); @@ -123,6 +147,7 @@ struct DeviceInfo { const char *name; + const char *fw_name; const char *alias; const char *desc; size_t size; @@ -157,22 +182,42 @@ /*** BUS API. ***/ +DeviceState *qdev_find_recursive(BusState *bus, const char *id); + +/* Returns 0 to walk children, > 0 to skip walk, < 0 to terminate walk. */ +typedef int (qbus_walkerfn)(BusState *bus, void *opaque); +typedef int (qdev_walkerfn)(DeviceState *dev, void *opaque); + void qbus_create_inplace(BusState *bus, BusInfo *info, DeviceState *parent, const char *name); BusState *qbus_create(BusInfo *info, DeviceState *parent, const char *name); +/* Returns > 0 if either devfn or busfn skip walk somewhere in cursion, + * < 0 if either devfn or busfn terminate walk somewhere in cursion, + * 0 otherwise. */ +int qbus_walk_children(BusState *bus, qdev_walkerfn *devfn, + qbus_walkerfn *busfn, void *opaque); +int qdev_walk_children(DeviceState *dev, qdev_walkerfn *devfn, + qbus_walkerfn *busfn, void *opaque); +void qdev_reset_all(DeviceState *dev); +void qbus_reset_all_fn(void *opaque); + void qbus_free(BusState *bus); #define FROM_QBUS(type, dev) DO_UPCAST(type, qbus, dev) +/* This should go away once we get rid of the NULL bus hack */ +BusState *sysbus_get_default(void); + /*** monitor commands ***/ void do_info_qtree(Monitor *mon); void do_info_qdm(Monitor *mon); -void do_device_add(Monitor *mon, const QDict *qdict); -void do_device_del(Monitor *mon, const QDict *qdict); +int do_device_add(Monitor *mon, const QDict *qdict, QObject **ret_data); +int do_device_del(Monitor *mon, const QDict *qdict, QObject **ret_data); /*** qdev-properties.c ***/ +extern PropertyInfo qdev_prop_bit; extern PropertyInfo qdev_prop_uint8; extern PropertyInfo qdev_prop_uint16; extern PropertyInfo qdev_prop_uint32; @@ -202,6 +247,14 @@ + type_check(_type,typeof_field(_state, _field)), \ .defval = (_type[]) { _defval }, \ } +#define DEFINE_PROP_BIT(_name, _state, _field, _bit, _defval) { \ + .name = (_name), \ + .info = &(qdev_prop_bit), \ + .bitnr = (_bit), \ + .offset = offsetof(_state, _field) \ + + type_check(uint32_t,typeof_field(_state, _field)), \ + .defval = (bool[]) { (_defval) }, \ + } #define DEFINE_PROP_UINT8(_n, _s, _f, _d) \ DEFINE_PROP_DEFAULT(_n, _s, _f, _d, qdev_prop_uint8, uint8_t) @@ -230,8 +283,8 @@ DEFINE_PROP(_n, _s, _f, qdev_prop_netdev, VLANClientState*) #define DEFINE_PROP_VLAN(_n, _s, _f) \ DEFINE_PROP(_n, _s, _f, qdev_prop_vlan, VLANState*) -#define DEFINE_PROP_DRIVE(_n, _s, _f) \ - DEFINE_PROP(_n, _s, _f, qdev_prop_drive, DriveInfo*) +#define DEFINE_PROP_DRIVE(_n, _s, _f) \ + DEFINE_PROP(_n, _s, _f, qdev_prop_drive, BlockDriverState *) #define DEFINE_PROP_MACADDR(_n, _s, _f) \ DEFINE_PROP(_n, _s, _f, qdev_prop_macaddr, MACAddr) @@ -243,24 +296,32 @@ int qdev_prop_exists(DeviceState *dev, const char *name); int qdev_prop_parse(DeviceState *dev, const char *name, const char *value); void qdev_prop_set(DeviceState *dev, const char *name, void *src, enum PropertyType type); +void qdev_prop_set_bit(DeviceState *dev, const char *name, bool value); void qdev_prop_set_uint8(DeviceState *dev, const char *name, uint8_t value); void qdev_prop_set_uint16(DeviceState *dev, const char *name, uint16_t value); void qdev_prop_set_uint32(DeviceState *dev, const char *name, uint32_t value); void qdev_prop_set_int32(DeviceState *dev, const char *name, int32_t value); void qdev_prop_set_uint64(DeviceState *dev, const char *name, uint64_t value); +void qdev_prop_set_string(DeviceState *dev, const char *name, char *value); void qdev_prop_set_chr(DeviceState *dev, const char *name, CharDriverState *value); void qdev_prop_set_netdev(DeviceState *dev, const char *name, VLANClientState *value); void qdev_prop_set_vlan(DeviceState *dev, const char *name, VLANState *value); -void qdev_prop_set_drive(DeviceState *dev, const char *name, DriveInfo *value); +int qdev_prop_set_drive(DeviceState *dev, const char *name, BlockDriverState *value) QEMU_WARN_UNUSED_RESULT; +void qdev_prop_set_drive_nofail(DeviceState *dev, const char *name, BlockDriverState *value); void qdev_prop_set_macaddr(DeviceState *dev, const char *name, uint8_t *value); /* FIXME: Remove opaque pointer properties. */ void qdev_prop_set_ptr(DeviceState *dev, const char *name, void *value); void qdev_prop_set_defaults(DeviceState *dev, Property *props); -void qdev_prop_register_global(GlobalProperty *prop); void qdev_prop_register_global_list(GlobalProperty *props); void qdev_prop_set_globals(DeviceState *dev); +static inline const char *qdev_fw_name(DeviceState *dev) +{ + return dev->info->fw_name ? : dev->info->alias ? : dev->info->name; +} + +char *qdev_get_fw_dev_path(DeviceState *dev); /* This is a nasty hack to allow passing a NULL bus to qdev_create. */ extern struct BusInfo system_bus_info; diff -Nru qemu-kvm-0.12.5+noroms/hw/qdev-properties.c qemu-kvm-0.14.1/hw/qdev-properties.c --- qemu-kvm-0.12.5+noroms/hw/qdev-properties.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/qdev-properties.c 2011-05-11 13:29:46.000000000 +0000 @@ -1,6 +1,7 @@ -#include "sysemu.h" #include "net.h" #include "qdev.h" +#include "qerror.h" +#include "blockdev.h" void *qdev_get_prop_ptr(DeviceState *dev, Property *prop) { @@ -9,17 +10,72 @@ return ptr; } +static uint32_t qdev_get_prop_mask(Property *prop) +{ + assert(prop->info->type == PROP_TYPE_BIT); + return 0x1 << prop->bitnr; +} + +static void bit_prop_set(DeviceState *dev, Property *props, bool val) +{ + uint32_t *p = qdev_get_prop_ptr(dev, props); + uint32_t mask = qdev_get_prop_mask(props); + if (val) + *p |= mask; + else + *p &= ~mask; +} + +static void qdev_prop_cpy(DeviceState *dev, Property *props, void *src) +{ + if (props->info->type == PROP_TYPE_BIT) { + bool *defval = src; + bit_prop_set(dev, props, *defval); + } else { + char *dst = qdev_get_prop_ptr(dev, props); + memcpy(dst, src, props->info->size); + } +} + +/* Bit */ +static int parse_bit(DeviceState *dev, Property *prop, const char *str) +{ + if (!strncasecmp(str, "on", 2)) + bit_prop_set(dev, prop, true); + else if (!strncasecmp(str, "off", 3)) + bit_prop_set(dev, prop, false); + else + return -EINVAL; + return 0; +} + +static int print_bit(DeviceState *dev, Property *prop, char *dest, size_t len) +{ + uint8_t *p = qdev_get_prop_ptr(dev, prop); + return snprintf(dest, len, (*p & qdev_get_prop_mask(prop)) ? "on" : "off"); +} + +PropertyInfo qdev_prop_bit = { + .name = "on/off", + .type = PROP_TYPE_BIT, + .size = sizeof(uint32_t), + .parse = parse_bit, + .print = print_bit, +}; + /* --- 8bit integer --- */ static int parse_uint8(DeviceState *dev, Property *prop, const char *str) { uint8_t *ptr = qdev_get_prop_ptr(dev, prop); - const char *fmt; + char *end; /* accept both hex and decimal */ - fmt = strncasecmp(str, "0x",2) == 0 ? "%" PRIx8 : "%" PRIu8; - if (sscanf(str, fmt, ptr) != 1) - return -1; + *ptr = strtoul(str, &end, 0); + if ((*end != '\0') || (end == str)) { + return -EINVAL; + } + return 0; } @@ -42,12 +98,14 @@ static int parse_uint16(DeviceState *dev, Property *prop, const char *str) { uint16_t *ptr = qdev_get_prop_ptr(dev, prop); - const char *fmt; + char *end; /* accept both hex and decimal */ - fmt = strncasecmp(str, "0x",2) == 0 ? "%" PRIx16 : "%" PRIu16; - if (sscanf(str, fmt, ptr) != 1) - return -1; + *ptr = strtoul(str, &end, 0); + if ((*end != '\0') || (end == str)) { + return -EINVAL; + } + return 0; } @@ -70,12 +128,14 @@ static int parse_uint32(DeviceState *dev, Property *prop, const char *str) { uint32_t *ptr = qdev_get_prop_ptr(dev, prop); - const char *fmt; + char *end; /* accept both hex and decimal */ - fmt = strncasecmp(str, "0x",2) == 0 ? "%" PRIx32 : "%" PRIu32; - if (sscanf(str, fmt, ptr) != 1) - return -1; + *ptr = strtoul(str, &end, 0); + if ((*end != '\0') || (end == str)) { + return -EINVAL; + } + return 0; } @@ -96,9 +156,13 @@ static int parse_int32(DeviceState *dev, Property *prop, const char *str) { int32_t *ptr = qdev_get_prop_ptr(dev, prop); + char *end; + + *ptr = strtol(str, &end, 10); + if ((*end != '\0') || (end == str)) { + return -EINVAL; + } - if (sscanf(str, "%" PRId32, ptr) != 1) - return -1; return 0; } @@ -121,9 +185,13 @@ static int parse_hex32(DeviceState *dev, Property *prop, const char *str) { uint32_t *ptr = qdev_get_prop_ptr(dev, prop); + char *end; + + *ptr = strtoul(str, &end, 16); + if ((*end != '\0') || (end == str)) { + return -EINVAL; + } - if (sscanf(str, "%" PRIx32, ptr) != 1) - return -1; return 0; } @@ -146,12 +214,14 @@ static int parse_uint64(DeviceState *dev, Property *prop, const char *str) { uint64_t *ptr = qdev_get_prop_ptr(dev, prop); - const char *fmt; + char *end; /* accept both hex and decimal */ - fmt = strncasecmp(str, "0x",2) == 0 ? "%" PRIx64 : "%" PRIu64; - if (sscanf(str, fmt, ptr) != 1) - return -1; + *ptr = strtoull(str, &end, 0); + if ((*end != '\0') || (end == str)) { + return -EINVAL; + } + return 0; } @@ -174,9 +244,13 @@ static int parse_hex64(DeviceState *dev, Property *prop, const char *str) { uint64_t *ptr = qdev_get_prop_ptr(dev, prop); + char *end; + + *ptr = strtoull(str, &end, 16); + if ((*end != '\0') || (end == str)) { + return -EINVAL; + } - if (sscanf(str, "%" PRIx64, ptr) != 1) - return -1; return 0; } @@ -206,6 +280,11 @@ return 0; } +static void free_string(DeviceState *dev, Property *prop) +{ + qemu_free(*(char **)qdev_get_prop_ptr(dev, prop)); +} + static int print_string(DeviceState *dev, Property *prop, char *dest, size_t len) { char **ptr = qdev_get_prop_ptr(dev, prop); @@ -220,32 +299,49 @@ .size = sizeof(char*), .parse = parse_string, .print = print_string, + .free = free_string, }; /* --- drive --- */ static int parse_drive(DeviceState *dev, Property *prop, const char *str) { - DriveInfo **ptr = qdev_get_prop_ptr(dev, prop); + BlockDriverState **ptr = qdev_get_prop_ptr(dev, prop); + BlockDriverState *bs; - *ptr = drive_get_by_id(str); - if (*ptr == NULL) - return -1; + bs = bdrv_find(str); + if (bs == NULL) + return -ENOENT; + if (bdrv_attach(bs, dev) < 0) + return -EEXIST; + *ptr = bs; return 0; } +static void free_drive(DeviceState *dev, Property *prop) +{ + BlockDriverState **ptr = qdev_get_prop_ptr(dev, prop); + + if (*ptr) { + bdrv_detach(*ptr, dev); + blockdev_auto_del(*ptr); + } +} + static int print_drive(DeviceState *dev, Property *prop, char *dest, size_t len) { - DriveInfo **ptr = qdev_get_prop_ptr(dev, prop); - return snprintf(dest, len, "%s", (*ptr) ? (*ptr)->id : ""); + BlockDriverState **ptr = qdev_get_prop_ptr(dev, prop); + return snprintf(dest, len, "%s", + *ptr ? bdrv_get_device_name(*ptr) : ""); } PropertyInfo qdev_prop_drive = { .name = "drive", .type = PROP_TYPE_DRIVE, - .size = sizeof(DriveInfo*), + .size = sizeof(BlockDriverState *), .parse = parse_drive, .print = print_drive, + .free = free_drive, }; /* --- character device --- */ @@ -256,7 +352,7 @@ *ptr = qemu_chr_find(str); if (*ptr == NULL) - return -1; + return -ENOENT; return 0; } @@ -287,7 +383,10 @@ *ptr = qemu_find_netdev(str); if (*ptr == NULL) - return -1; + return -ENOENT; + if ((*ptr)->peer) { + return -EEXIST; + } return 0; } @@ -318,10 +417,10 @@ int id; if (sscanf(str, "%d", &id) != 1) - return -1; + return -EINVAL; *ptr = qemu_find_vlan(id, 1); if (*ptr == NULL) - return -1; + return -ENOENT; return 0; } @@ -346,17 +445,11 @@ /* --- pointer --- */ -static int print_ptr(DeviceState *dev, Property *prop, char *dest, size_t len) -{ - void **ptr = qdev_get_prop_ptr(dev, prop); - return snprintf(dest, len, "<%p>", *ptr); -} - +/* Not a proper property, just for dirty hacks. TODO Remove it! */ PropertyInfo qdev_prop_ptr = { .name = "ptr", .type = PROP_TYPE_PTR, .size = sizeof(void*), - .print = print_ptr, }; /* --- mac address --- */ @@ -374,15 +467,15 @@ for (i = 0, pos = 0; i < 6; i++, pos += 3) { if (!qemu_isxdigit(str[pos])) - return -1; + return -EINVAL; if (!qemu_isxdigit(str[pos+1])) - return -1; + return -EINVAL; if (i == 5) { if (str[pos+2] != '\0') - return -1; + return -EINVAL; } else { if (str[pos+2] != ':' && str[pos+2] != '-') - return -1; + return -EINVAL; } mac->a[i] = strtol(str+pos, &p, 16); } @@ -419,13 +512,13 @@ if (sscanf(str, "%x.%x%n", &slot, &fn, &n) != 2) { fn = 0; if (sscanf(str, "%x%n", &slot, &n) != 1) { - return -1; + return -EINVAL; } } if (str[n] != '\0') - return -1; + return -EINVAL; if (fn > 7) - return -1; + return -EINVAL; *ptr = slot << 3 | fn; return 0; } @@ -488,21 +581,36 @@ int qdev_prop_parse(DeviceState *dev, const char *name, const char *value) { Property *prop; + int ret; prop = qdev_prop_find(dev, name); - if (!prop) { - fprintf(stderr, "property \"%s.%s\" not found\n", - dev->info->name, name); - return -1; - } - if (!prop->info->parse) { - fprintf(stderr, "property \"%s.%s\" has no parser\n", - dev->info->name, name); - return -1; - } - if (prop->info->parse(dev, prop, value) != 0) { - fprintf(stderr, "property \"%s.%s\": failed to parse \"%s\"\n", - dev->info->name, name, value); + /* + * TODO Properties without a parse method are just for dirty + * hacks. qdev_prop_ptr is the only such PropertyInfo. It's + * marked for removal. The test !prop->info->parse should be + * removed along with it. + */ + if (!prop || !prop->info->parse) { + qerror_report(QERR_PROPERTY_NOT_FOUND, dev->info->name, name); + return -1; + } + ret = prop->info->parse(dev, prop, value); + if (ret < 0) { + switch (ret) { + case -EEXIST: + qerror_report(QERR_PROPERTY_VALUE_IN_USE, + dev->info->name, name, value); + break; + default: + case -EINVAL: + qerror_report(QERR_PROPERTY_VALUE_BAD, + dev->info->name, name, value); + break; + case -ENOENT: + qerror_report(QERR_PROPERTY_VALUE_NOT_FOUND, + dev->info->name, name, value); + break; + } return -1; } return 0; @@ -511,7 +619,6 @@ void qdev_prop_set(DeviceState *dev, const char *name, void *src, enum PropertyType type) { Property *prop; - void *dst; prop = qdev_prop_find(dev, name); if (!prop) { @@ -524,8 +631,12 @@ __FUNCTION__, dev->info->name, name); abort(); } - dst = qdev_get_prop_ptr(dev, prop); - memcpy(dst, src, prop->info->size); + qdev_prop_cpy(dev, prop, src); +} + +void qdev_prop_set_bit(DeviceState *dev, const char *name, bool value) +{ + qdev_prop_set(dev, name, &value, PROP_TYPE_BIT); } void qdev_prop_set_uint8(DeviceState *dev, const char *name, uint8_t value) @@ -553,11 +664,33 @@ qdev_prop_set(dev, name, &value, PROP_TYPE_UINT64); } -void qdev_prop_set_drive(DeviceState *dev, const char *name, DriveInfo *value) +void qdev_prop_set_string(DeviceState *dev, const char *name, char *value) +{ + qdev_prop_set(dev, name, &value, PROP_TYPE_STRING); +} + +int qdev_prop_set_drive(DeviceState *dev, const char *name, BlockDriverState *value) { + int res; + + res = bdrv_attach(value, dev); + if (res < 0) { + error_report("Can't attach drive %s to %s.%s: %s", + bdrv_get_device_name(value), + dev->id ? dev->id : dev->info->name, + name, strerror(-res)); + return -1; + } qdev_prop_set(dev, name, &value, PROP_TYPE_DRIVE); + return 0; } +void qdev_prop_set_drive_nofail(DeviceState *dev, const char *name, BlockDriverState *value) +{ + if (qdev_prop_set_drive(dev, name, value) < 0) { + exit(1); + } +} void qdev_prop_set_chr(DeviceState *dev, const char *name, CharDriverState *value) { qdev_prop_set(dev, name, &value, PROP_TYPE_CHR); @@ -585,14 +718,11 @@ void qdev_prop_set_defaults(DeviceState *dev, Property *props) { - char *dst; - if (!props) return; while (props->name) { if (props->defval) { - dst = qdev_get_prop_ptr(dev, props); - memcpy(dst, props->defval, props->info->size); + qdev_prop_cpy(dev, props, props->defval); } props++; } @@ -600,7 +730,7 @@ static QTAILQ_HEAD(, GlobalProperty) global_props = QTAILQ_HEAD_INITIALIZER(global_props); -void qdev_prop_register_global(GlobalProperty *prop) +static void qdev_prop_register_global(GlobalProperty *prop) { QTAILQ_INSERT_TAIL(&global_props, prop, next); } @@ -628,3 +758,20 @@ } } } + +static int qdev_add_one_global(QemuOpts *opts, void *opaque) +{ + GlobalProperty *g; + + g = qemu_mallocz(sizeof(*g)); + g->driver = qemu_opt_get(opts, "driver"); + g->property = qemu_opt_get(opts, "property"); + g->value = qemu_opt_get(opts, "value"); + qdev_prop_register_global(g); + return 0; +} + +void qemu_add_globals(void) +{ + qemu_opts_foreach(qemu_find_opts("global"), qdev_add_one_global, NULL, 0); +} diff -Nru qemu-kvm-0.12.5+noroms/hw/qxl.c qemu-kvm-0.14.1/hw/qxl.c --- qemu-kvm-0.12.5+noroms/hw/qxl.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/qxl.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,1531 @@ +/* + * Copyright (C) 2010 Red Hat, Inc. + * + * written by Yaniv Kamay, Izik Eidus, Gerd Hoffmann + * maintained by Gerd Hoffmann + * + * 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 or + * (at your option) version 3 of the License. + * + * 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, see . + */ + +#include + +#include "qemu-common.h" +#include "qemu-timer.h" +#include "qemu-queue.h" +#include "monitor.h" +#include "sysemu.h" + +#include "qxl.h" + +#undef SPICE_RING_PROD_ITEM +#define SPICE_RING_PROD_ITEM(r, ret) { \ + typeof(r) start = r; \ + typeof(r) end = r + 1; \ + uint32_t prod = (r)->prod & SPICE_RING_INDEX_MASK(r); \ + typeof(&(r)->items[prod]) m_item = &(r)->items[prod]; \ + if (!((uint8_t*)m_item >= (uint8_t*)(start) && (uint8_t*)(m_item + 1) <= (uint8_t*)(end))) { \ + abort(); \ + } \ + ret = &m_item->el; \ + } + +#undef SPICE_RING_CONS_ITEM +#define SPICE_RING_CONS_ITEM(r, ret) { \ + typeof(r) start = r; \ + typeof(r) end = r + 1; \ + uint32_t cons = (r)->cons & SPICE_RING_INDEX_MASK(r); \ + typeof(&(r)->items[cons]) m_item = &(r)->items[cons]; \ + if (!((uint8_t*)m_item >= (uint8_t*)(start) && (uint8_t*)(m_item + 1) <= (uint8_t*)(end))) { \ + abort(); \ + } \ + ret = &m_item->el; \ + } + +#undef ALIGN +#define ALIGN(a, b) (((a) + ((b) - 1)) & ~((b) - 1)) + +#define PIXEL_SIZE 0.2936875 //1280x1024 is 14.8" x 11.9" + +#define QXL_MODE(_x, _y, _b, _o) \ + { .x_res = _x, \ + .y_res = _y, \ + .bits = _b, \ + .stride = (_x) * (_b) / 8, \ + .x_mili = PIXEL_SIZE * (_x), \ + .y_mili = PIXEL_SIZE * (_y), \ + .orientation = _o, \ + } + +#define QXL_MODE_16_32(x_res, y_res, orientation) \ + QXL_MODE(x_res, y_res, 16, orientation), \ + QXL_MODE(x_res, y_res, 32, orientation) + +#define QXL_MODE_EX(x_res, y_res) \ + QXL_MODE_16_32(x_res, y_res, 0), \ + QXL_MODE_16_32(y_res, x_res, 1), \ + QXL_MODE_16_32(x_res, y_res, 2), \ + QXL_MODE_16_32(y_res, x_res, 3) + +static QXLMode qxl_modes[] = { + QXL_MODE_EX(640, 480), + QXL_MODE_EX(800, 480), + QXL_MODE_EX(800, 600), + QXL_MODE_EX(832, 624), + QXL_MODE_EX(960, 640), + QXL_MODE_EX(1024, 600), + QXL_MODE_EX(1024, 768), + QXL_MODE_EX(1152, 864), + QXL_MODE_EX(1152, 870), + QXL_MODE_EX(1280, 720), + QXL_MODE_EX(1280, 760), + QXL_MODE_EX(1280, 768), + QXL_MODE_EX(1280, 800), + QXL_MODE_EX(1280, 960), + QXL_MODE_EX(1280, 1024), + QXL_MODE_EX(1360, 768), + QXL_MODE_EX(1366, 768), + QXL_MODE_EX(1400, 1050), + QXL_MODE_EX(1440, 900), + QXL_MODE_EX(1600, 900), + QXL_MODE_EX(1600, 1200), + QXL_MODE_EX(1680, 1050), + QXL_MODE_EX(1920, 1080), +#if VGA_RAM_SIZE >= (16 * 1024 * 1024) + /* these modes need more than 8 MB video memory */ + QXL_MODE_EX(1920, 1200), + QXL_MODE_EX(1920, 1440), + QXL_MODE_EX(2048, 1536), + QXL_MODE_EX(2560, 1440), + QXL_MODE_EX(2560, 1600), +#endif +#if VGA_RAM_SIZE >= (32 * 1024 * 1024) + /* these modes need more than 16 MB video memory */ + QXL_MODE_EX(2560, 2048), + QXL_MODE_EX(2800, 2100), + QXL_MODE_EX(3200, 2400), +#endif +}; + +static PCIQXLDevice *qxl0; + +static void qxl_send_events(PCIQXLDevice *d, uint32_t events); +static void qxl_destroy_primary(PCIQXLDevice *d); +static void qxl_reset_memslots(PCIQXLDevice *d); +static void qxl_reset_surfaces(PCIQXLDevice *d); +static void qxl_ring_set_dirty(PCIQXLDevice *qxl); + +static inline uint32_t msb_mask(uint32_t val) +{ + uint32_t mask; + + do { + mask = ~(val - 1) & val; + val &= ~mask; + } while (mask < val); + + return mask; +} + +static ram_addr_t qxl_rom_size(void) +{ + uint32_t rom_size = sizeof(QXLRom) + sizeof(QXLModes) + sizeof(qxl_modes); + rom_size = MAX(rom_size, TARGET_PAGE_SIZE); + rom_size = msb_mask(rom_size * 2 - 1); + return rom_size; +} + +static void init_qxl_rom(PCIQXLDevice *d) +{ + QXLRom *rom = qemu_get_ram_ptr(d->rom_offset); + QXLModes *modes = (QXLModes *)(rom + 1); + uint32_t ram_header_size; + uint32_t surface0_area_size; + uint32_t num_pages; + uint32_t fb, maxfb = 0; + int i; + + memset(rom, 0, d->rom_size); + + rom->magic = cpu_to_le32(QXL_ROM_MAGIC); + rom->id = cpu_to_le32(d->id); + rom->log_level = cpu_to_le32(d->guestdebug); + rom->modes_offset = cpu_to_le32(sizeof(QXLRom)); + + rom->slot_gen_bits = MEMSLOT_GENERATION_BITS; + rom->slot_id_bits = MEMSLOT_SLOT_BITS; + rom->slots_start = 1; + rom->slots_end = NUM_MEMSLOTS - 1; + rom->n_surfaces = cpu_to_le32(NUM_SURFACES); + + modes->n_modes = cpu_to_le32(ARRAY_SIZE(qxl_modes)); + for (i = 0; i < modes->n_modes; i++) { + fb = qxl_modes[i].y_res * qxl_modes[i].stride; + if (maxfb < fb) { + maxfb = fb; + } + modes->modes[i].id = cpu_to_le32(i); + modes->modes[i].x_res = cpu_to_le32(qxl_modes[i].x_res); + modes->modes[i].y_res = cpu_to_le32(qxl_modes[i].y_res); + modes->modes[i].bits = cpu_to_le32(qxl_modes[i].bits); + modes->modes[i].stride = cpu_to_le32(qxl_modes[i].stride); + modes->modes[i].x_mili = cpu_to_le32(qxl_modes[i].x_mili); + modes->modes[i].y_mili = cpu_to_le32(qxl_modes[i].y_mili); + modes->modes[i].orientation = cpu_to_le32(qxl_modes[i].orientation); + } + if (maxfb < VGA_RAM_SIZE && d->id == 0) + maxfb = VGA_RAM_SIZE; + + ram_header_size = ALIGN(sizeof(QXLRam), 4096); + surface0_area_size = ALIGN(maxfb, 4096); + num_pages = d->vga.vram_size; + num_pages -= ram_header_size; + num_pages -= surface0_area_size; + num_pages = num_pages / TARGET_PAGE_SIZE; + + rom->draw_area_offset = cpu_to_le32(0); + rom->surface0_area_size = cpu_to_le32(surface0_area_size); + rom->pages_offset = cpu_to_le32(surface0_area_size); + rom->num_pages = cpu_to_le32(num_pages); + rom->ram_header_offset = cpu_to_le32(d->vga.vram_size - ram_header_size); + + d->shadow_rom = *rom; + d->rom = rom; + d->modes = modes; +} + +static void init_qxl_ram(PCIQXLDevice *d) +{ + uint8_t *buf; + uint64_t *item; + + buf = d->vga.vram_ptr; + d->ram = (QXLRam *)(buf + le32_to_cpu(d->shadow_rom.ram_header_offset)); + d->ram->magic = cpu_to_le32(QXL_RAM_MAGIC); + d->ram->int_pending = cpu_to_le32(0); + d->ram->int_mask = cpu_to_le32(0); + SPICE_RING_INIT(&d->ram->cmd_ring); + SPICE_RING_INIT(&d->ram->cursor_ring); + SPICE_RING_INIT(&d->ram->release_ring); + SPICE_RING_PROD_ITEM(&d->ram->release_ring, item); + *item = 0; + qxl_ring_set_dirty(d); +} + +/* can be called from spice server thread context */ +static void qxl_set_dirty(ram_addr_t addr, ram_addr_t end) +{ + while (addr < end) { + cpu_physical_memory_set_dirty(addr); + addr += TARGET_PAGE_SIZE; + } +} + +static void qxl_rom_set_dirty(PCIQXLDevice *qxl) +{ + ram_addr_t addr = qxl->rom_offset; + qxl_set_dirty(addr, addr + qxl->rom_size); +} + +/* called from spice server thread context only */ +static void qxl_ram_set_dirty(PCIQXLDevice *qxl, void *ptr) +{ + ram_addr_t addr = qxl->vga.vram_offset; + void *base = qxl->vga.vram_ptr; + intptr_t offset; + + offset = ptr - base; + offset &= ~(TARGET_PAGE_SIZE-1); + assert(offset < qxl->vga.vram_size); + qxl_set_dirty(addr + offset, addr + offset + TARGET_PAGE_SIZE); +} + +/* can be called from spice server thread context */ +static void qxl_ring_set_dirty(PCIQXLDevice *qxl) +{ + ram_addr_t addr = qxl->vga.vram_offset + qxl->shadow_rom.ram_header_offset; + ram_addr_t end = qxl->vga.vram_offset + qxl->vga.vram_size; + qxl_set_dirty(addr, end); +} + +/* + * keep track of some command state, for savevm/loadvm. + * called from spice server thread context only + */ +static void qxl_track_command(PCIQXLDevice *qxl, struct QXLCommandExt *ext) +{ + switch (le32_to_cpu(ext->cmd.type)) { + case QXL_CMD_SURFACE: + { + QXLSurfaceCmd *cmd = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id); + uint32_t id = le32_to_cpu(cmd->surface_id); + PANIC_ON(id >= NUM_SURFACES); + if (cmd->type == QXL_SURFACE_CMD_CREATE) { + qxl->guest_surfaces.cmds[id] = ext->cmd.data; + qxl->guest_surfaces.count++; + if (qxl->guest_surfaces.max < qxl->guest_surfaces.count) + qxl->guest_surfaces.max = qxl->guest_surfaces.count; + } + if (cmd->type == QXL_SURFACE_CMD_DESTROY) { + qxl->guest_surfaces.cmds[id] = 0; + qxl->guest_surfaces.count--; + } + break; + } + case QXL_CMD_CURSOR: + { + QXLCursorCmd *cmd = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id); + if (cmd->type == QXL_CURSOR_SET) { + qxl->guest_cursor = ext->cmd.data; + } + break; + } + } +} + +/* spice display interface callbacks */ + +static void interface_attach_worker(QXLInstance *sin, QXLWorker *qxl_worker) +{ + PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); + + dprint(qxl, 1, "%s:\n", __FUNCTION__); + qxl->ssd.worker = qxl_worker; +} + +static void interface_set_compression_level(QXLInstance *sin, int level) +{ + PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); + + dprint(qxl, 1, "%s: %d\n", __FUNCTION__, level); + qxl->shadow_rom.compression_level = cpu_to_le32(level); + qxl->rom->compression_level = cpu_to_le32(level); + qxl_rom_set_dirty(qxl); +} + +static void interface_set_mm_time(QXLInstance *sin, uint32_t mm_time) +{ + PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); + + qxl->shadow_rom.mm_clock = cpu_to_le32(mm_time); + qxl->rom->mm_clock = cpu_to_le32(mm_time); + qxl_rom_set_dirty(qxl); +} + +static void interface_get_init_info(QXLInstance *sin, QXLDevInitInfo *info) +{ + PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); + + dprint(qxl, 1, "%s:\n", __FUNCTION__); + info->memslot_gen_bits = MEMSLOT_GENERATION_BITS; + info->memslot_id_bits = MEMSLOT_SLOT_BITS; + info->num_memslots = NUM_MEMSLOTS; + info->num_memslots_groups = NUM_MEMSLOTS_GROUPS; + info->internal_groupslot_id = 0; + info->qxl_ram_size = le32_to_cpu(qxl->shadow_rom.num_pages) << TARGET_PAGE_BITS; + info->n_surfaces = NUM_SURFACES; +} + +/* called from spice server thread context only */ +static int interface_get_command(QXLInstance *sin, struct QXLCommandExt *ext) +{ + PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); + SimpleSpiceUpdate *update; + QXLCommandRing *ring; + QXLCommand *cmd; + int notify; + + switch (qxl->mode) { + case QXL_MODE_VGA: + dprint(qxl, 2, "%s: vga\n", __FUNCTION__); + update = qemu_spice_create_update(&qxl->ssd); + if (update == NULL) { + return false; + } + *ext = update->ext; + qxl_log_command(qxl, "vga", ext); + return true; + case QXL_MODE_COMPAT: + case QXL_MODE_NATIVE: + case QXL_MODE_UNDEFINED: + dprint(qxl, 2, "%s: %s\n", __FUNCTION__, + qxl->cmdflags ? "compat" : "native"); + ring = &qxl->ram->cmd_ring; + if (SPICE_RING_IS_EMPTY(ring)) { + return false; + } + SPICE_RING_CONS_ITEM(ring, cmd); + ext->cmd = *cmd; + ext->group_id = MEMSLOT_GROUP_GUEST; + ext->flags = qxl->cmdflags; + SPICE_RING_POP(ring, notify); + qxl_ring_set_dirty(qxl); + if (notify) { + qxl_send_events(qxl, QXL_INTERRUPT_DISPLAY); + } + qxl->guest_primary.commands++; + qxl_track_command(qxl, ext); + qxl_log_command(qxl, "cmd", ext); + return true; + default: + return false; + } +} + +/* called from spice server thread context only */ +static int interface_req_cmd_notification(QXLInstance *sin) +{ + PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); + int wait = 1; + + switch (qxl->mode) { + case QXL_MODE_COMPAT: + case QXL_MODE_NATIVE: + case QXL_MODE_UNDEFINED: + SPICE_RING_CONS_WAIT(&qxl->ram->cmd_ring, wait); + qxl_ring_set_dirty(qxl); + break; + default: + /* nothing */ + break; + } + return wait; +} + +/* called from spice server thread context only */ +static inline void qxl_push_free_res(PCIQXLDevice *d, int flush) +{ + QXLReleaseRing *ring = &d->ram->release_ring; + uint64_t *item; + int notify; + +#define QXL_FREE_BUNCH_SIZE 32 + + if (ring->prod - ring->cons + 1 == ring->num_items) { + /* ring full -- can't push */ + return; + } + if (!flush && d->oom_running) { + /* collect everything from oom handler before pushing */ + return; + } + if (!flush && d->num_free_res < QXL_FREE_BUNCH_SIZE) { + /* collect a bit more before pushing */ + return; + } + + SPICE_RING_PUSH(ring, notify); + dprint(d, 2, "free: push %d items, notify %s, ring %d/%d [%d,%d]\n", + d->num_free_res, notify ? "yes" : "no", + ring->prod - ring->cons, ring->num_items, + ring->prod, ring->cons); + if (notify) { + qxl_send_events(d, QXL_INTERRUPT_DISPLAY); + } + SPICE_RING_PROD_ITEM(ring, item); + *item = 0; + d->num_free_res = 0; + d->last_release = NULL; + qxl_ring_set_dirty(d); +} + +/* called from spice server thread context only */ +static void interface_release_resource(QXLInstance *sin, + struct QXLReleaseInfoExt ext) +{ + PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); + QXLReleaseRing *ring; + uint64_t *item, id; + + if (ext.group_id == MEMSLOT_GROUP_HOST) { + /* host group -> vga mode update request */ + qemu_spice_destroy_update(&qxl->ssd, (void*)ext.info->id); + return; + } + + /* + * ext->info points into guest-visible memory + * pci bar 0, $command.release_info + */ + ring = &qxl->ram->release_ring; + SPICE_RING_PROD_ITEM(ring, item); + if (*item == 0) { + /* stick head into the ring */ + id = ext.info->id; + ext.info->next = 0; + qxl_ram_set_dirty(qxl, &ext.info->next); + *item = id; + qxl_ring_set_dirty(qxl); + } else { + /* append item to the list */ + qxl->last_release->next = ext.info->id; + qxl_ram_set_dirty(qxl, &qxl->last_release->next); + ext.info->next = 0; + qxl_ram_set_dirty(qxl, &ext.info->next); + } + qxl->last_release = ext.info; + qxl->num_free_res++; + dprint(qxl, 3, "%4d\r", qxl->num_free_res); + qxl_push_free_res(qxl, 0); +} + +/* called from spice server thread context only */ +static int interface_get_cursor_command(QXLInstance *sin, struct QXLCommandExt *ext) +{ + PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); + QXLCursorRing *ring; + QXLCommand *cmd; + int notify; + + switch (qxl->mode) { + case QXL_MODE_COMPAT: + case QXL_MODE_NATIVE: + case QXL_MODE_UNDEFINED: + ring = &qxl->ram->cursor_ring; + if (SPICE_RING_IS_EMPTY(ring)) { + return false; + } + SPICE_RING_CONS_ITEM(ring, cmd); + ext->cmd = *cmd; + ext->group_id = MEMSLOT_GROUP_GUEST; + ext->flags = qxl->cmdflags; + SPICE_RING_POP(ring, notify); + qxl_ring_set_dirty(qxl); + if (notify) { + qxl_send_events(qxl, QXL_INTERRUPT_CURSOR); + } + qxl->guest_primary.commands++; + qxl_track_command(qxl, ext); + qxl_log_command(qxl, "csr", ext); + if (qxl->id == 0) { + qxl_render_cursor(qxl, ext); + } + return true; + default: + return false; + } +} + +/* called from spice server thread context only */ +static int interface_req_cursor_notification(QXLInstance *sin) +{ + PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); + int wait = 1; + + switch (qxl->mode) { + case QXL_MODE_COMPAT: + case QXL_MODE_NATIVE: + case QXL_MODE_UNDEFINED: + SPICE_RING_CONS_WAIT(&qxl->ram->cursor_ring, wait); + qxl_ring_set_dirty(qxl); + break; + default: + /* nothing */ + break; + } + return wait; +} + +/* called from spice server thread context */ +static void interface_notify_update(QXLInstance *sin, uint32_t update_id) +{ + fprintf(stderr, "%s: abort()\n", __FUNCTION__); + abort(); +} + +/* called from spice server thread context only */ +static int interface_flush_resources(QXLInstance *sin) +{ + PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl); + int ret; + + dprint(qxl, 1, "free: guest flush (have %d)\n", qxl->num_free_res); + ret = qxl->num_free_res; + if (ret) { + qxl_push_free_res(qxl, 1); + } + return ret; +} + +static const QXLInterface qxl_interface = { + .base.type = SPICE_INTERFACE_QXL, + .base.description = "qxl gpu", + .base.major_version = SPICE_INTERFACE_QXL_MAJOR, + .base.minor_version = SPICE_INTERFACE_QXL_MINOR, + + .attache_worker = interface_attach_worker, + .set_compression_level = interface_set_compression_level, + .set_mm_time = interface_set_mm_time, + .get_init_info = interface_get_init_info, + + /* the callbacks below are called from spice server thread context */ + .get_command = interface_get_command, + .req_cmd_notification = interface_req_cmd_notification, + .release_resource = interface_release_resource, + .get_cursor_command = interface_get_cursor_command, + .req_cursor_notification = interface_req_cursor_notification, + .notify_update = interface_notify_update, + .flush_resources = interface_flush_resources, +}; + +static void qxl_enter_vga_mode(PCIQXLDevice *d) +{ + if (d->mode == QXL_MODE_VGA) { + return; + } + dprint(d, 1, "%s\n", __FUNCTION__); + qemu_spice_create_host_primary(&d->ssd); + d->mode = QXL_MODE_VGA; + memset(&d->ssd.dirty, 0, sizeof(d->ssd.dirty)); +} + +static void qxl_exit_vga_mode(PCIQXLDevice *d) +{ + if (d->mode != QXL_MODE_VGA) { + return; + } + dprint(d, 1, "%s\n", __FUNCTION__); + qxl_destroy_primary(d); +} + +static void qxl_set_irq(PCIQXLDevice *d) +{ + uint32_t pending = le32_to_cpu(d->ram->int_pending); + uint32_t mask = le32_to_cpu(d->ram->int_mask); + int level = !!(pending & mask); + qemu_set_irq(d->pci.irq[0], level); + qxl_ring_set_dirty(d); +} + +static void qxl_write_config(PCIDevice *d, uint32_t address, + uint32_t val, int len) +{ + PCIQXLDevice *qxl = DO_UPCAST(PCIQXLDevice, pci, d); + VGACommonState *vga = &qxl->vga; + + vga_dirty_log_stop(vga); + pci_default_write_config(d, address, val, len); + if (vga->map_addr && qxl->pci.io_regions[0].addr == -1) { + vga->map_addr = 0; + } + vga_dirty_log_start(vga); +} + +static void qxl_check_state(PCIQXLDevice *d) +{ + QXLRam *ram = d->ram; + + assert(SPICE_RING_IS_EMPTY(&ram->cmd_ring)); + assert(SPICE_RING_IS_EMPTY(&ram->cursor_ring)); +} + +static void qxl_reset_state(PCIQXLDevice *d) +{ + QXLRam *ram = d->ram; + QXLRom *rom = d->rom; + + assert(SPICE_RING_IS_EMPTY(&ram->cmd_ring)); + assert(SPICE_RING_IS_EMPTY(&ram->cursor_ring)); + d->shadow_rom.update_id = cpu_to_le32(0); + *rom = d->shadow_rom; + qxl_rom_set_dirty(d); + init_qxl_ram(d); + d->num_free_res = 0; + d->last_release = NULL; + memset(&d->ssd.dirty, 0, sizeof(d->ssd.dirty)); +} + +static void qxl_soft_reset(PCIQXLDevice *d) +{ + dprint(d, 1, "%s:\n", __FUNCTION__); + qxl_check_state(d); + + if (d->id == 0) { + qxl_enter_vga_mode(d); + } else { + d->mode = QXL_MODE_UNDEFINED; + } +} + +static void qxl_hard_reset(PCIQXLDevice *d, int loadvm) +{ + dprint(d, 1, "%s: start%s\n", __FUNCTION__, + loadvm ? " (loadvm)" : ""); + + qemu_mutex_unlock_iothread(); + d->ssd.worker->reset_cursor(d->ssd.worker); + d->ssd.worker->reset_image_cache(d->ssd.worker); + qemu_mutex_lock_iothread(); + qxl_reset_surfaces(d); + qxl_reset_memslots(d); + + /* pre loadvm reset must not touch QXLRam. This lives in + * device memory, is migrated together with RAM and thus + * already loaded at this point */ + if (!loadvm) { + qxl_reset_state(d); + } + qemu_spice_create_host_memslot(&d->ssd); + qxl_soft_reset(d); + + dprint(d, 1, "%s: done\n", __FUNCTION__); +} + +static void qxl_reset_handler(DeviceState *dev) +{ + PCIQXLDevice *d = DO_UPCAST(PCIQXLDevice, pci.qdev, dev); + qxl_hard_reset(d, 0); +} + +static void qxl_vga_ioport_write(void *opaque, uint32_t addr, uint32_t val) +{ + VGACommonState *vga = opaque; + PCIQXLDevice *qxl = container_of(vga, PCIQXLDevice, vga); + + if (qxl->mode != QXL_MODE_VGA) { + dprint(qxl, 1, "%s\n", __FUNCTION__); + qxl_destroy_primary(qxl); + qxl_soft_reset(qxl); + } + vga_ioport_write(opaque, addr, val); +} + +static void qxl_add_memslot(PCIQXLDevice *d, uint32_t slot_id, uint64_t delta) +{ + static const int regions[] = { + QXL_RAM_RANGE_INDEX, + QXL_VRAM_RANGE_INDEX, + }; + uint64_t guest_start; + uint64_t guest_end; + int pci_region; + pcibus_t pci_start; + pcibus_t pci_end; + intptr_t virt_start; + QXLDevMemSlot memslot; + int i; + + guest_start = le64_to_cpu(d->guest_slots[slot_id].slot.mem_start); + guest_end = le64_to_cpu(d->guest_slots[slot_id].slot.mem_end); + + dprint(d, 1, "%s: slot %d: guest phys 0x%" PRIx64 " - 0x%" PRIx64 "\n", + __FUNCTION__, slot_id, + guest_start, guest_end); + + PANIC_ON(slot_id >= NUM_MEMSLOTS); + PANIC_ON(guest_start > guest_end); + + for (i = 0; i < ARRAY_SIZE(regions); i++) { + pci_region = regions[i]; + pci_start = d->pci.io_regions[pci_region].addr; + pci_end = pci_start + d->pci.io_regions[pci_region].size; + /* mapped? */ + if (pci_start == -1) { + continue; + } + /* start address in range ? */ + if (guest_start < pci_start || guest_start > pci_end) { + continue; + } + /* end address in range ? */ + if (guest_end > pci_end) { + continue; + } + /* passed */ + break; + } + PANIC_ON(i == ARRAY_SIZE(regions)); /* finished loop without match */ + + switch (pci_region) { + case QXL_RAM_RANGE_INDEX: + virt_start = (intptr_t)qemu_get_ram_ptr(d->vga.vram_offset); + break; + case QXL_VRAM_RANGE_INDEX: + virt_start = (intptr_t)qemu_get_ram_ptr(d->vram_offset); + break; + default: + /* should not happen */ + abort(); + } + + memslot.slot_id = slot_id; + memslot.slot_group_id = MEMSLOT_GROUP_GUEST; /* guest group */ + memslot.virt_start = virt_start + (guest_start - pci_start); + memslot.virt_end = virt_start + (guest_end - pci_start); + memslot.addr_delta = memslot.virt_start - delta; + memslot.generation = d->rom->slot_generation = 0; + qxl_rom_set_dirty(d); + + dprint(d, 1, "%s: slot %d: host virt 0x%" PRIx64 " - 0x%" PRIx64 "\n", + __FUNCTION__, memslot.slot_id, + memslot.virt_start, memslot.virt_end); + + d->ssd.worker->add_memslot(d->ssd.worker, &memslot); + d->guest_slots[slot_id].ptr = (void*)memslot.virt_start; + d->guest_slots[slot_id].size = memslot.virt_end - memslot.virt_start; + d->guest_slots[slot_id].delta = delta; + d->guest_slots[slot_id].active = 1; +} + +static void qxl_del_memslot(PCIQXLDevice *d, uint32_t slot_id) +{ + dprint(d, 1, "%s: slot %d\n", __FUNCTION__, slot_id); + d->ssd.worker->del_memslot(d->ssd.worker, MEMSLOT_GROUP_HOST, slot_id); + d->guest_slots[slot_id].active = 0; +} + +static void qxl_reset_memslots(PCIQXLDevice *d) +{ + dprint(d, 1, "%s:\n", __FUNCTION__); + d->ssd.worker->reset_memslots(d->ssd.worker); + memset(&d->guest_slots, 0, sizeof(d->guest_slots)); +} + +static void qxl_reset_surfaces(PCIQXLDevice *d) +{ + dprint(d, 1, "%s:\n", __FUNCTION__); + d->mode = QXL_MODE_UNDEFINED; + qemu_mutex_unlock_iothread(); + d->ssd.worker->destroy_surfaces(d->ssd.worker); + qemu_mutex_lock_iothread(); + memset(&d->guest_surfaces.cmds, 0, sizeof(d->guest_surfaces.cmds)); +} + +/* called from spice server thread context only */ +void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL pqxl, int group_id) +{ + uint64_t phys = le64_to_cpu(pqxl); + uint32_t slot = (phys >> (64 - 8)) & 0xff; + uint64_t offset = phys & 0xffffffffffff; + + switch (group_id) { + case MEMSLOT_GROUP_HOST: + return (void*)offset; + case MEMSLOT_GROUP_GUEST: + PANIC_ON(slot > NUM_MEMSLOTS); + PANIC_ON(!qxl->guest_slots[slot].active); + PANIC_ON(offset < qxl->guest_slots[slot].delta); + offset -= qxl->guest_slots[slot].delta; + PANIC_ON(offset > qxl->guest_slots[slot].size) + return qxl->guest_slots[slot].ptr + offset; + default: + PANIC_ON(1); + } +} + +static void qxl_create_guest_primary(PCIQXLDevice *qxl, int loadvm) +{ + QXLDevSurfaceCreate surface; + QXLSurfaceCreate *sc = &qxl->guest_primary.surface; + + assert(qxl->mode != QXL_MODE_NATIVE); + qxl_exit_vga_mode(qxl); + + dprint(qxl, 1, "%s: %dx%d\n", __FUNCTION__, + le32_to_cpu(sc->width), le32_to_cpu(sc->height)); + + surface.format = le32_to_cpu(sc->format); + surface.height = le32_to_cpu(sc->height); + surface.mem = le64_to_cpu(sc->mem); + surface.position = le32_to_cpu(sc->position); + surface.stride = le32_to_cpu(sc->stride); + surface.width = le32_to_cpu(sc->width); + surface.type = le32_to_cpu(sc->type); + surface.flags = le32_to_cpu(sc->flags); + + surface.mouse_mode = true; + surface.group_id = MEMSLOT_GROUP_GUEST; + if (loadvm) { + surface.flags |= QXL_SURF_FLAG_KEEP_DATA; + } + + qxl->mode = QXL_MODE_NATIVE; + qxl->cmdflags = 0; + qxl->ssd.worker->create_primary_surface(qxl->ssd.worker, 0, &surface); + + /* for local rendering */ + qxl_render_resize(qxl); +} + +static void qxl_destroy_primary(PCIQXLDevice *d) +{ + if (d->mode == QXL_MODE_UNDEFINED) { + return; + } + + dprint(d, 1, "%s\n", __FUNCTION__); + + d->mode = QXL_MODE_UNDEFINED; + qemu_mutex_unlock_iothread(); + d->ssd.worker->destroy_primary_surface(d->ssd.worker, 0); + qemu_mutex_lock_iothread(); +} + +static void qxl_set_mode(PCIQXLDevice *d, int modenr, int loadvm) +{ + pcibus_t start = d->pci.io_regions[QXL_RAM_RANGE_INDEX].addr; + pcibus_t end = d->pci.io_regions[QXL_RAM_RANGE_INDEX].size + start; + QXLMode *mode = d->modes->modes + modenr; + uint64_t devmem = d->pci.io_regions[QXL_RAM_RANGE_INDEX].addr; + QXLMemSlot slot = { + .mem_start = start, + .mem_end = end + }; + QXLSurfaceCreate surface = { + .width = mode->x_res, + .height = mode->y_res, + .stride = -mode->x_res * 4, + .format = SPICE_SURFACE_FMT_32_xRGB, + .flags = loadvm ? QXL_SURF_FLAG_KEEP_DATA : 0, + .mouse_mode = true, + .mem = devmem + d->shadow_rom.draw_area_offset, + }; + + dprint(d, 1, "%s: mode %d [ %d x %d @ %d bpp devmem 0x%lx ]\n", __FUNCTION__, + modenr, mode->x_res, mode->y_res, mode->bits, devmem); + if (!loadvm) { + qxl_hard_reset(d, 0); + } + + d->guest_slots[0].slot = slot; + qxl_add_memslot(d, 0, devmem); + + d->guest_primary.surface = surface; + qxl_create_guest_primary(d, 0); + + d->mode = QXL_MODE_COMPAT; + d->cmdflags = QXL_COMMAND_FLAG_COMPAT; +#ifdef QXL_COMMAND_FLAG_COMPAT_16BPP /* new in spice 0.6.1 */ + if (mode->bits == 16) { + d->cmdflags |= QXL_COMMAND_FLAG_COMPAT_16BPP; + } +#endif + d->shadow_rom.mode = cpu_to_le32(modenr); + d->rom->mode = cpu_to_le32(modenr); + qxl_rom_set_dirty(d); +} + +static void ioport_write(void *opaque, uint32_t addr, uint32_t val) +{ + PCIQXLDevice *d = opaque; + uint32_t io_port = addr - d->io_base; + + switch (io_port) { + case QXL_IO_RESET: + case QXL_IO_SET_MODE: + case QXL_IO_MEMSLOT_ADD: + case QXL_IO_MEMSLOT_DEL: + case QXL_IO_CREATE_PRIMARY: + break; + default: + if (d->mode == QXL_MODE_NATIVE || d->mode == QXL_MODE_COMPAT) + break; + dprint(d, 1, "%s: unexpected port 0x%x in vga mode\n", __FUNCTION__, io_port); + return; + } + + switch (io_port) { + case QXL_IO_UPDATE_AREA: + { + QXLRect update = d->ram->update_area; + qemu_mutex_unlock_iothread(); + d->ssd.worker->update_area(d->ssd.worker, d->ram->update_surface, + &update, NULL, 0, 0); + qemu_mutex_lock_iothread(); + break; + } + case QXL_IO_NOTIFY_CMD: + d->ssd.worker->wakeup(d->ssd.worker); + break; + case QXL_IO_NOTIFY_CURSOR: + d->ssd.worker->wakeup(d->ssd.worker); + break; + case QXL_IO_UPDATE_IRQ: + qxl_set_irq(d); + break; + case QXL_IO_NOTIFY_OOM: + if (!SPICE_RING_IS_EMPTY(&d->ram->release_ring)) { + break; + } + pthread_yield(); + if (!SPICE_RING_IS_EMPTY(&d->ram->release_ring)) { + break; + } + d->oom_running = 1; + d->ssd.worker->oom(d->ssd.worker); + d->oom_running = 0; + break; + case QXL_IO_SET_MODE: + dprint(d, 1, "QXL_SET_MODE %d\n", val); + qxl_set_mode(d, val, 0); + break; + case QXL_IO_LOG: + if (d->guestdebug) { + fprintf(stderr, "qxl/guest: %s", d->ram->log_buf); + } + break; + case QXL_IO_RESET: + dprint(d, 1, "QXL_IO_RESET\n"); + qxl_hard_reset(d, 0); + break; + case QXL_IO_MEMSLOT_ADD: + PANIC_ON(val >= NUM_MEMSLOTS); + PANIC_ON(d->guest_slots[val].active); + d->guest_slots[val].slot = d->ram->mem_slot; + qxl_add_memslot(d, val, 0); + break; + case QXL_IO_MEMSLOT_DEL: + qxl_del_memslot(d, val); + break; + case QXL_IO_CREATE_PRIMARY: + PANIC_ON(val != 0); + dprint(d, 1, "QXL_IO_CREATE_PRIMARY\n"); + d->guest_primary.surface = d->ram->create_surface; + qxl_create_guest_primary(d, 0); + break; + case QXL_IO_DESTROY_PRIMARY: + PANIC_ON(val != 0); + dprint(d, 1, "QXL_IO_DESTROY_PRIMARY\n"); + qxl_destroy_primary(d); + break; + case QXL_IO_DESTROY_SURFACE_WAIT: + d->ssd.worker->destroy_surface_wait(d->ssd.worker, val); + break; + case QXL_IO_DESTROY_ALL_SURFACES: + d->ssd.worker->destroy_surfaces(d->ssd.worker); + break; + default: + fprintf(stderr, "%s: ioport=0x%x, abort()\n", __FUNCTION__, io_port); + abort(); + } +} + +static uint32_t ioport_read(void *opaque, uint32_t addr) +{ + PCIQXLDevice *d = opaque; + + dprint(d, 1, "%s: unexpected\n", __FUNCTION__); + return 0xff; +} + +static void qxl_map(PCIDevice *pci, int region_num, + pcibus_t addr, pcibus_t size, int type) +{ + static const char *names[] = { + [ QXL_IO_RANGE_INDEX ] = "ioports", + [ QXL_RAM_RANGE_INDEX ] = "devram", + [ QXL_ROM_RANGE_INDEX ] = "rom", + [ QXL_VRAM_RANGE_INDEX ] = "vram", + }; + PCIQXLDevice *qxl = DO_UPCAST(PCIQXLDevice, pci, pci); + + dprint(qxl, 1, "%s: bar %d [%s] addr 0x%lx size 0x%lx\n", __FUNCTION__, + region_num, names[region_num], addr, size); + + switch (region_num) { + case QXL_IO_RANGE_INDEX: + register_ioport_write(addr, size, 1, ioport_write, pci); + register_ioport_read(addr, size, 1, ioport_read, pci); + qxl->io_base = addr; + break; + case QXL_RAM_RANGE_INDEX: + cpu_register_physical_memory(addr, size, qxl->vga.vram_offset | IO_MEM_RAM); + qxl->vga.map_addr = addr; + qxl->vga.map_end = addr + size; + if (qxl->id == 0) { + vga_dirty_log_start(&qxl->vga); + } + break; + case QXL_ROM_RANGE_INDEX: + cpu_register_physical_memory(addr, size, qxl->rom_offset | IO_MEM_ROM); + break; + case QXL_VRAM_RANGE_INDEX: + cpu_register_physical_memory(addr, size, qxl->vram_offset | IO_MEM_RAM); + break; + } +} + +static void pipe_read(void *opaque) +{ + PCIQXLDevice *d = opaque; + char dummy; + int len; + + do { + len = read(d->pipe[0], &dummy, sizeof(dummy)); + } while (len == sizeof(dummy)); + qxl_set_irq(d); +} + +/* called from spice server thread context only */ +static void qxl_send_events(PCIQXLDevice *d, uint32_t events) +{ + uint32_t old_pending; + uint32_t le_events = cpu_to_le32(events); + + assert(d->ssd.running); + old_pending = __sync_fetch_and_or(&d->ram->int_pending, le_events); + if ((old_pending & le_events) == le_events) { + return; + } + if (pthread_self() == d->main) { + qxl_set_irq(d); + } else { + if (write(d->pipe[1], d, 1) != 1) { + dprint(d, 1, "%s: write to pipe failed\n", __FUNCTION__); + } + } +} + +static void init_pipe_signaling(PCIQXLDevice *d) +{ + if (pipe(d->pipe) < 0) { + dprint(d, 1, "%s: pipe creation failed\n", __FUNCTION__); + return; + } +#ifdef CONFIG_IOTHREAD + fcntl(d->pipe[0], F_SETFL, O_NONBLOCK); +#else + fcntl(d->pipe[0], F_SETFL, O_NONBLOCK /* | O_ASYNC */); +#endif + fcntl(d->pipe[1], F_SETFL, O_NONBLOCK); + fcntl(d->pipe[0], F_SETOWN, getpid()); + + d->main = pthread_self(); + qemu_set_fd_handler(d->pipe[0], pipe_read, NULL, d); +} + +/* graphics console */ + +static void qxl_hw_update(void *opaque) +{ + PCIQXLDevice *qxl = opaque; + VGACommonState *vga = &qxl->vga; + + switch (qxl->mode) { + case QXL_MODE_VGA: + vga->update(vga); + break; + case QXL_MODE_COMPAT: + case QXL_MODE_NATIVE: + qxl_render_update(qxl); + break; + default: + break; + } +} + +static void qxl_hw_invalidate(void *opaque) +{ + PCIQXLDevice *qxl = opaque; + VGACommonState *vga = &qxl->vga; + + vga->invalidate(vga); +} + +static void qxl_hw_screen_dump(void *opaque, const char *filename) +{ + PCIQXLDevice *qxl = opaque; + VGACommonState *vga = &qxl->vga; + + switch (qxl->mode) { + case QXL_MODE_COMPAT: + case QXL_MODE_NATIVE: + qxl_render_update(qxl); + ppm_save(filename, qxl->ssd.ds->surface); + break; + case QXL_MODE_VGA: + vga->screen_dump(vga, filename); + break; + default: + break; + } +} + +static void qxl_hw_text_update(void *opaque, console_ch_t *chardata) +{ + PCIQXLDevice *qxl = opaque; + VGACommonState *vga = &qxl->vga; + + if (qxl->mode == QXL_MODE_VGA) { + vga->text_update(vga, chardata); + return; + } +} + +static void qxl_vm_change_state_handler(void *opaque, int running, int reason) +{ + PCIQXLDevice *qxl = opaque; + qemu_spice_vm_change_state_handler(&qxl->ssd, running, reason); + + if (!running && qxl->mode == QXL_MODE_NATIVE) { + /* dirty all vram (which holds surfaces) to make sure it is saved */ + /* FIXME #1: should go out during "live" stage */ + /* FIXME #2: we only need to save the areas which are actually used */ + ram_addr_t addr = qxl->vram_offset; + qxl_set_dirty(addr, addr + qxl->vram_size); + } +} + +/* display change listener */ + +static void display_update(struct DisplayState *ds, int x, int y, int w, int h) +{ + if (qxl0->mode == QXL_MODE_VGA) { + qemu_spice_display_update(&qxl0->ssd, x, y, w, h); + } +} + +static void display_resize(struct DisplayState *ds) +{ + if (qxl0->mode == QXL_MODE_VGA) { + qemu_spice_display_resize(&qxl0->ssd); + } +} + +static void display_refresh(struct DisplayState *ds) +{ + if (qxl0->mode == QXL_MODE_VGA) { + qemu_spice_display_refresh(&qxl0->ssd); + } +} + +static DisplayChangeListener display_listener = { + .dpy_update = display_update, + .dpy_resize = display_resize, + .dpy_refresh = display_refresh, +}; + +static int qxl_init_common(PCIQXLDevice *qxl) +{ + uint8_t* config = qxl->pci.config; + uint32_t pci_device_id; + uint32_t pci_device_rev; + uint32_t io_size; + + qxl->mode = QXL_MODE_UNDEFINED; + qxl->generation = 1; + qxl->num_memslots = NUM_MEMSLOTS; + qxl->num_surfaces = NUM_SURFACES; + + switch (qxl->revision) { + case 1: /* spice 0.4 -- qxl-1 */ + pci_device_id = QXL_DEVICE_ID_STABLE; + pci_device_rev = QXL_REVISION_STABLE_V04; + break; + case 2: /* spice 0.6 -- qxl-2 */ + pci_device_id = QXL_DEVICE_ID_STABLE; + pci_device_rev = QXL_REVISION_STABLE_V06; + break; + default: /* experimental */ + pci_device_id = QXL_DEVICE_ID_DEVEL; + pci_device_rev = 1; + break; + } + + pci_config_set_vendor_id(config, REDHAT_PCI_VENDOR_ID); + pci_config_set_device_id(config, pci_device_id); + pci_set_byte(&config[PCI_REVISION_ID], pci_device_rev); + pci_set_byte(&config[PCI_INTERRUPT_PIN], 1); + + qxl->rom_size = qxl_rom_size(); + qxl->rom_offset = qemu_ram_alloc(&qxl->pci.qdev, "qxl.vrom", qxl->rom_size); + init_qxl_rom(qxl); + init_qxl_ram(qxl); + + if (qxl->vram_size < 16 * 1024 * 1024) { + qxl->vram_size = 16 * 1024 * 1024; + } + if (qxl->revision == 1) { + qxl->vram_size = 4096; + } + qxl->vram_size = msb_mask(qxl->vram_size * 2 - 1); + qxl->vram_offset = qemu_ram_alloc(&qxl->pci.qdev, "qxl.vram", qxl->vram_size); + + io_size = msb_mask(QXL_IO_RANGE_SIZE * 2 - 1); + if (qxl->revision == 1) { + io_size = 8; + } + + pci_register_bar(&qxl->pci, QXL_IO_RANGE_INDEX, + io_size, PCI_BASE_ADDRESS_SPACE_IO, qxl_map); + + pci_register_bar(&qxl->pci, QXL_ROM_RANGE_INDEX, + qxl->rom_size, PCI_BASE_ADDRESS_SPACE_MEMORY, + qxl_map); + + pci_register_bar(&qxl->pci, QXL_RAM_RANGE_INDEX, + qxl->vga.vram_size, PCI_BASE_ADDRESS_SPACE_MEMORY, + qxl_map); + + pci_register_bar(&qxl->pci, QXL_VRAM_RANGE_INDEX, qxl->vram_size, + PCI_BASE_ADDRESS_SPACE_MEMORY, qxl_map); + + qxl->ssd.qxl.base.sif = &qxl_interface.base; + qxl->ssd.qxl.id = qxl->id; + qemu_spice_add_interface(&qxl->ssd.qxl.base); + qemu_add_vm_change_state_handler(qxl_vm_change_state_handler, qxl); + + init_pipe_signaling(qxl); + qxl_reset_state(qxl); + + return 0; +} + +static int qxl_init_primary(PCIDevice *dev) +{ + PCIQXLDevice *qxl = DO_UPCAST(PCIQXLDevice, pci, dev); + VGACommonState *vga = &qxl->vga; + ram_addr_t ram_size = msb_mask(qxl->vga.vram_size * 2 - 1); + + qxl->id = 0; + + if (ram_size < 32 * 1024 * 1024) { + ram_size = 32 * 1024 * 1024; + } + vga_common_init(vga, ram_size); + vga_init(vga); + register_ioport_write(0x3c0, 16, 1, qxl_vga_ioport_write, vga); + register_ioport_write(0x3b4, 2, 1, qxl_vga_ioport_write, vga); + register_ioport_write(0x3d4, 2, 1, qxl_vga_ioport_write, vga); + register_ioport_write(0x3ba, 1, 1, qxl_vga_ioport_write, vga); + register_ioport_write(0x3da, 1, 1, qxl_vga_ioport_write, vga); + + vga->ds = graphic_console_init(qxl_hw_update, qxl_hw_invalidate, + qxl_hw_screen_dump, qxl_hw_text_update, qxl); + qxl->ssd.ds = vga->ds; + qxl->ssd.bufsize = (16 * 1024 * 1024); + qxl->ssd.buf = qemu_malloc(qxl->ssd.bufsize); + + qxl0 = qxl; + register_displaychangelistener(vga->ds, &display_listener); + + pci_config_set_class(dev->config, PCI_CLASS_DISPLAY_VGA); + return qxl_init_common(qxl); +} + +static int qxl_init_secondary(PCIDevice *dev) +{ + static int device_id = 1; + PCIQXLDevice *qxl = DO_UPCAST(PCIQXLDevice, pci, dev); + ram_addr_t ram_size = msb_mask(qxl->vga.vram_size * 2 - 1); + + qxl->id = device_id++; + + if (ram_size < 16 * 1024 * 1024) { + ram_size = 16 * 1024 * 1024; + } + qxl->vga.vram_size = ram_size; + qxl->vga.vram_offset = qemu_ram_alloc(&qxl->pci.qdev, "qxl.vgavram", + qxl->vga.vram_size); + qxl->vga.vram_ptr = qemu_get_ram_ptr(qxl->vga.vram_offset); + + pci_config_set_class(dev->config, PCI_CLASS_DISPLAY_OTHER); + return qxl_init_common(qxl); +} + +static void qxl_pre_save(void *opaque) +{ + PCIQXLDevice* d = opaque; + uint8_t *ram_start = d->vga.vram_ptr; + + dprint(d, 1, "%s:\n", __FUNCTION__); + if (d->last_release == NULL) { + d->last_release_offset = 0; + } else { + d->last_release_offset = (uint8_t *)d->last_release - ram_start; + } + assert(d->last_release_offset < d->vga.vram_size); +} + +static int qxl_pre_load(void *opaque) +{ + PCIQXLDevice* d = opaque; + + dprint(d, 1, "%s: start\n", __FUNCTION__); + qxl_hard_reset(d, 1); + qxl_exit_vga_mode(d); + dprint(d, 1, "%s: done\n", __FUNCTION__); + return 0; +} + +static int qxl_post_load(void *opaque, int version) +{ + PCIQXLDevice* d = opaque; + uint8_t *ram_start = d->vga.vram_ptr; + QXLCommandExt *cmds; + int in, out, i, newmode; + + dprint(d, 1, "%s: start\n", __FUNCTION__); + + assert(d->last_release_offset < d->vga.vram_size); + if (d->last_release_offset == 0) { + d->last_release = NULL; + } else { + d->last_release = (QXLReleaseInfo *)(ram_start + d->last_release_offset); + } + + d->modes = (QXLModes*)((uint8_t*)d->rom + d->rom->modes_offset); + + dprint(d, 1, "%s: restore mode\n", __FUNCTION__); + newmode = d->mode; + d->mode = QXL_MODE_UNDEFINED; + switch (newmode) { + case QXL_MODE_UNDEFINED: + break; + case QXL_MODE_VGA: + qxl_enter_vga_mode(d); + break; + case QXL_MODE_NATIVE: + for (i = 0; i < NUM_MEMSLOTS; i++) { + if (!d->guest_slots[i].active) { + continue; + } + qxl_add_memslot(d, i, 0); + } + qxl_create_guest_primary(d, 1); + + /* replay surface-create and cursor-set commands */ + cmds = qemu_mallocz(sizeof(QXLCommandExt) * (NUM_SURFACES + 1)); + for (in = 0, out = 0; in < NUM_SURFACES; in++) { + if (d->guest_surfaces.cmds[in] == 0) { + continue; + } + cmds[out].cmd.data = d->guest_surfaces.cmds[in]; + cmds[out].cmd.type = QXL_CMD_SURFACE; + cmds[out].group_id = MEMSLOT_GROUP_GUEST; + out++; + } + cmds[out].cmd.data = d->guest_cursor; + cmds[out].cmd.type = QXL_CMD_CURSOR; + cmds[out].group_id = MEMSLOT_GROUP_GUEST; + out++; + d->ssd.worker->loadvm_commands(d->ssd.worker, cmds, out); + qemu_free(cmds); + + break; + case QXL_MODE_COMPAT: + qxl_set_mode(d, d->shadow_rom.mode, 1); + break; + } + dprint(d, 1, "%s: done\n", __FUNCTION__); + + return 0; +} + +#define QXL_SAVE_VERSION 21 + +static VMStateDescription qxl_memslot = { + .name = "qxl-memslot", + .version_id = QXL_SAVE_VERSION, + .minimum_version_id = QXL_SAVE_VERSION, + .fields = (VMStateField[]) { + VMSTATE_UINT64(slot.mem_start, struct guest_slots), + VMSTATE_UINT64(slot.mem_end, struct guest_slots), + VMSTATE_UINT32(active, struct guest_slots), + VMSTATE_END_OF_LIST() + } +}; + +static VMStateDescription qxl_surface = { + .name = "qxl-surface", + .version_id = QXL_SAVE_VERSION, + .minimum_version_id = QXL_SAVE_VERSION, + .fields = (VMStateField[]) { + VMSTATE_UINT32(width, QXLSurfaceCreate), + VMSTATE_UINT32(height, QXLSurfaceCreate), + VMSTATE_INT32(stride, QXLSurfaceCreate), + VMSTATE_UINT32(format, QXLSurfaceCreate), + VMSTATE_UINT32(position, QXLSurfaceCreate), + VMSTATE_UINT32(mouse_mode, QXLSurfaceCreate), + VMSTATE_UINT32(flags, QXLSurfaceCreate), + VMSTATE_UINT32(type, QXLSurfaceCreate), + VMSTATE_UINT64(mem, QXLSurfaceCreate), + VMSTATE_END_OF_LIST() + } +}; + +static VMStateDescription qxl_vmstate = { + .name = "qxl", + .version_id = QXL_SAVE_VERSION, + .minimum_version_id = QXL_SAVE_VERSION, + .pre_save = qxl_pre_save, + .pre_load = qxl_pre_load, + .post_load = qxl_post_load, + .fields = (VMStateField []) { + VMSTATE_PCI_DEVICE(pci, PCIQXLDevice), + VMSTATE_STRUCT(vga, PCIQXLDevice, 0, vmstate_vga_common, VGACommonState), + VMSTATE_UINT32(shadow_rom.mode, PCIQXLDevice), + VMSTATE_UINT32(num_free_res, PCIQXLDevice), + VMSTATE_UINT32(last_release_offset, PCIQXLDevice), + VMSTATE_UINT32(mode, PCIQXLDevice), + VMSTATE_UINT32(ssd.unique, PCIQXLDevice), + VMSTATE_INT32_EQUAL(num_memslots, PCIQXLDevice), + VMSTATE_STRUCT_ARRAY(guest_slots, PCIQXLDevice, NUM_MEMSLOTS, 0, + qxl_memslot, struct guest_slots), + VMSTATE_STRUCT(guest_primary.surface, PCIQXLDevice, 0, + qxl_surface, QXLSurfaceCreate), + VMSTATE_INT32_EQUAL(num_surfaces, PCIQXLDevice), + VMSTATE_ARRAY(guest_surfaces.cmds, PCIQXLDevice, NUM_SURFACES, 0, + vmstate_info_uint64, uint64_t), + VMSTATE_UINT64(guest_cursor, PCIQXLDevice), + VMSTATE_END_OF_LIST() + }, +}; + +static PCIDeviceInfo qxl_info_primary = { + .qdev.name = "qxl-vga", + .qdev.desc = "Spice QXL GPU (primary, vga compatible)", + .qdev.size = sizeof(PCIQXLDevice), + .qdev.reset = qxl_reset_handler, + .qdev.vmsd = &qxl_vmstate, + .no_hotplug = 1, + .init = qxl_init_primary, + .config_write = qxl_write_config, + .romfile = "vgabios-qxl.bin", + .qdev.props = (Property[]) { + DEFINE_PROP_UINT32("ram_size", PCIQXLDevice, vga.vram_size, 64 * 1024 * 1024), + DEFINE_PROP_UINT32("vram_size", PCIQXLDevice, vram_size, 64 * 1024 * 1024), + DEFINE_PROP_UINT32("revision", PCIQXLDevice, revision, 2), + DEFINE_PROP_UINT32("debug", PCIQXLDevice, debug, 0), + DEFINE_PROP_UINT32("guestdebug", PCIQXLDevice, guestdebug, 0), + DEFINE_PROP_UINT32("cmdlog", PCIQXLDevice, cmdlog, 0), + DEFINE_PROP_END_OF_LIST(), + } +}; + +static PCIDeviceInfo qxl_info_secondary = { + .qdev.name = "qxl", + .qdev.desc = "Spice QXL GPU (secondary)", + .qdev.size = sizeof(PCIQXLDevice), + .qdev.reset = qxl_reset_handler, + .qdev.vmsd = &qxl_vmstate, + .init = qxl_init_secondary, + .qdev.props = (Property[]) { + DEFINE_PROP_UINT32("ram_size", PCIQXLDevice, vga.vram_size, 64 * 1024 * 1024), + DEFINE_PROP_UINT32("vram_size", PCIQXLDevice, vram_size, 64 * 1024 * 1024), + DEFINE_PROP_UINT32("revision", PCIQXLDevice, revision, 2), + DEFINE_PROP_UINT32("debug", PCIQXLDevice, debug, 0), + DEFINE_PROP_UINT32("guestdebug", PCIQXLDevice, guestdebug, 0), + DEFINE_PROP_UINT32("cmdlog", PCIQXLDevice, cmdlog, 0), + DEFINE_PROP_END_OF_LIST(), + } +}; + +static void qxl_register(void) +{ + pci_qdev_register(&qxl_info_primary); + pci_qdev_register(&qxl_info_secondary); +} + +device_init(qxl_register); diff -Nru qemu-kvm-0.12.5+noroms/hw/qxl.h qemu-kvm-0.14.1/hw/qxl.h --- qemu-kvm-0.12.5+noroms/hw/qxl.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/qxl.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,108 @@ +#include "qemu-common.h" + +#include "console.h" +#include "hw.h" +#include "pci.h" +#include "vga_int.h" + +#include "ui/qemu-spice.h" +#include "ui/spice-display.h" + +enum qxl_mode { + QXL_MODE_UNDEFINED, + QXL_MODE_VGA, + QXL_MODE_COMPAT, /* spice 0.4.x */ + QXL_MODE_NATIVE, +}; + +typedef struct PCIQXLDevice { + PCIDevice pci; + SimpleSpiceDisplay ssd; + int id; + uint32_t debug; + uint32_t guestdebug; + uint32_t cmdlog; + enum qxl_mode mode; + uint32_t cmdflags; + int generation; + uint32_t revision; + + int32_t num_memslots; + int32_t num_surfaces; + + struct guest_slots { + QXLMemSlot slot; + void *ptr; + uint64_t size; + uint64_t delta; + uint32_t active; + } guest_slots[NUM_MEMSLOTS]; + + struct guest_primary { + QXLSurfaceCreate surface; + uint32_t commands; + uint32_t resized; + int32_t stride; + uint32_t bits_pp; + uint32_t bytes_pp; + uint8_t *data, *flipped; + } guest_primary; + + struct surfaces { + QXLPHYSICAL cmds[NUM_SURFACES]; + uint32_t count; + uint32_t max; + } guest_surfaces; + QXLPHYSICAL guest_cursor; + + /* thread signaling */ + pthread_t main; + int pipe[2]; + + /* ram pci bar */ + QXLRam *ram; + VGACommonState vga; + uint32_t num_free_res; + QXLReleaseInfo *last_release; + uint32_t last_release_offset; + uint32_t oom_running; + + /* rom pci bar */ + QXLRom shadow_rom; + QXLRom *rom; + QXLModes *modes; + uint32_t rom_size; + uint64_t rom_offset; + + /* vram pci bar */ + uint32_t vram_size; + uint64_t vram_offset; + + /* io bar */ + uint32_t io_base; +} PCIQXLDevice; + +#define PANIC_ON(x) if ((x)) { \ + printf("%s: PANIC %s failed\n", __FUNCTION__, #x); \ + exit(-1); \ +} + +#define dprint(_qxl, _level, _fmt, ...) \ + do { \ + if (_qxl->debug >= _level) { \ + fprintf(stderr, "qxl-%d: ", _qxl->id); \ + fprintf(stderr, _fmt, ## __VA_ARGS__); \ + } \ + } while (0) + +/* qxl.c */ +void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL phys, int group_id); + +/* qxl-logger.c */ +void qxl_log_cmd_cursor(PCIQXLDevice *qxl, QXLCursorCmd *cmd, int group_id); +void qxl_log_command(PCIQXLDevice *qxl, const char *ring, QXLCommandExt *ext); + +/* qxl-render.c */ +void qxl_render_resize(PCIQXLDevice *qxl); +void qxl_render_update(PCIQXLDevice *qxl); +void qxl_render_cursor(PCIQXLDevice *qxl, QXLCommandExt *ext); diff -Nru qemu-kvm-0.12.5+noroms/hw/qxl-logger.c qemu-kvm-0.14.1/hw/qxl-logger.c --- qemu-kvm-0.12.5+noroms/hw/qxl-logger.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/qxl-logger.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,248 @@ +/* + * qxl command logging -- for debug purposes + * + * Copyright (C) 2010 Red Hat, Inc. + * + * maintained by Gerd Hoffmann + * + * 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 or + * (at your option) version 3 of the License. + * + * 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, see . + */ + +#include "qxl.h" + +static const char *qxl_type[] = { + [ QXL_CMD_NOP ] = "nop", + [ QXL_CMD_DRAW ] = "draw", + [ QXL_CMD_UPDATE ] = "update", + [ QXL_CMD_CURSOR ] = "cursor", + [ QXL_CMD_MESSAGE ] = "message", + [ QXL_CMD_SURFACE ] = "surface", +}; + +static const char *qxl_draw_type[] = { + [ QXL_DRAW_NOP ] = "nop", + [ QXL_DRAW_FILL ] = "fill", + [ QXL_DRAW_OPAQUE ] = "opaque", + [ QXL_DRAW_COPY ] = "copy", + [ QXL_COPY_BITS ] = "copy-bits", + [ QXL_DRAW_BLEND ] = "blend", + [ QXL_DRAW_BLACKNESS ] = "blackness", + [ QXL_DRAW_WHITENESS ] = "whitemess", + [ QXL_DRAW_INVERS ] = "invers", + [ QXL_DRAW_ROP3 ] = "rop3", + [ QXL_DRAW_STROKE ] = "stroke", + [ QXL_DRAW_TEXT ] = "text", + [ QXL_DRAW_TRANSPARENT ] = "transparent", + [ QXL_DRAW_ALPHA_BLEND ] = "alpha-blend", +}; + +static const char *qxl_draw_effect[] = { + [ QXL_EFFECT_BLEND ] = "blend", + [ QXL_EFFECT_OPAQUE ] = "opaque", + [ QXL_EFFECT_REVERT_ON_DUP ] = "revert-on-dup", + [ QXL_EFFECT_BLACKNESS_ON_DUP ] = "blackness-on-dup", + [ QXL_EFFECT_WHITENESS_ON_DUP ] = "whiteness-on-dup", + [ QXL_EFFECT_NOP_ON_DUP ] = "nop-on-dup", + [ QXL_EFFECT_NOP ] = "nop", + [ QXL_EFFECT_OPAQUE_BRUSH ] = "opaque-brush", +}; + +static const char *qxl_surface_cmd[] = { + [ QXL_SURFACE_CMD_CREATE ] = "create", + [ QXL_SURFACE_CMD_DESTROY ] = "destroy", +}; + +static const char *spice_surface_fmt[] = { + [ SPICE_SURFACE_FMT_INVALID ] = "invalid", + [ SPICE_SURFACE_FMT_1_A ] = "alpha/1", + [ SPICE_SURFACE_FMT_8_A ] = "alpha/8", + [ SPICE_SURFACE_FMT_16_555 ] = "555/16", + [ SPICE_SURFACE_FMT_16_565 ] = "565/16", + [ SPICE_SURFACE_FMT_32_xRGB ] = "xRGB/32", + [ SPICE_SURFACE_FMT_32_ARGB ] = "ARGB/32", +}; + +static const char *qxl_cursor_cmd[] = { + [ QXL_CURSOR_SET ] = "set", + [ QXL_CURSOR_MOVE ] = "move", + [ QXL_CURSOR_HIDE ] = "hide", + [ QXL_CURSOR_TRAIL ] = "trail", +}; + +static const char *spice_cursor_type[] = { + [ SPICE_CURSOR_TYPE_ALPHA ] = "alpha", + [ SPICE_CURSOR_TYPE_MONO ] = "mono", + [ SPICE_CURSOR_TYPE_COLOR4 ] = "color4", + [ SPICE_CURSOR_TYPE_COLOR8 ] = "color8", + [ SPICE_CURSOR_TYPE_COLOR16 ] = "color16", + [ SPICE_CURSOR_TYPE_COLOR24 ] = "color24", + [ SPICE_CURSOR_TYPE_COLOR32 ] = "color32", +}; + +static const char *qxl_v2n(const char *n[], size_t l, int v) +{ + if (v >= l || !n[v]) { + return "???"; + } + return n[v]; +} +#define qxl_name(_list, _value) qxl_v2n(_list, ARRAY_SIZE(_list), _value) + +static void qxl_log_image(PCIQXLDevice *qxl, QXLPHYSICAL addr, int group_id) +{ + QXLImage *image; + QXLImageDescriptor *desc; + + image = qxl_phys2virt(qxl, addr, group_id); + desc = &image->descriptor; + fprintf(stderr, " (id %" PRIx64 " type %d flags %d width %d height %d", + desc->id, desc->type, desc->flags, desc->width, desc->height); + switch (desc->type) { + case SPICE_IMAGE_TYPE_BITMAP: + fprintf(stderr, ", fmt %d flags %d x %d y %d stride %d" + " palette %" PRIx64 " data %" PRIx64, + image->bitmap.format, image->bitmap.flags, + image->bitmap.x, image->bitmap.y, + image->bitmap.stride, + image->bitmap.palette, image->bitmap.data); + break; + } + fprintf(stderr, ")"); +} + +static void qxl_log_rect(QXLRect *rect) +{ + fprintf(stderr, " %dx%d+%d+%d", + rect->right - rect->left, + rect->bottom - rect->top, + rect->left, rect->top); +} + +static void qxl_log_cmd_draw_copy(PCIQXLDevice *qxl, QXLCopy *copy, int group_id) +{ + fprintf(stderr, " src %" PRIx64, + copy->src_bitmap); + qxl_log_image(qxl, copy->src_bitmap, group_id); + fprintf(stderr, " area"); + qxl_log_rect(©->src_area); + fprintf(stderr, " rop %d", copy->rop_descriptor); +} + +static void qxl_log_cmd_draw(PCIQXLDevice *qxl, QXLDrawable *draw, int group_id) +{ + fprintf(stderr, ": surface_id %d type %s effect %s", + draw->surface_id, + qxl_name(qxl_draw_type, draw->type), + qxl_name(qxl_draw_effect, draw->effect)); + switch (draw->type) { + case QXL_DRAW_COPY: + qxl_log_cmd_draw_copy(qxl, &draw->u.copy, group_id); + break; + } +} + +static void qxl_log_cmd_draw_compat(PCIQXLDevice *qxl, QXLCompatDrawable *draw, + int group_id) +{ + fprintf(stderr, ": type %s effect %s", + qxl_name(qxl_draw_type, draw->type), + qxl_name(qxl_draw_effect, draw->effect)); + if (draw->bitmap_offset) { + fprintf(stderr, ": bitmap %d", + draw->bitmap_offset); + qxl_log_rect(&draw->bitmap_area); + } + switch (draw->type) { + case QXL_DRAW_COPY: + qxl_log_cmd_draw_copy(qxl, &draw->u.copy, group_id); + break; + } +} + +static void qxl_log_cmd_surface(PCIQXLDevice *qxl, QXLSurfaceCmd *cmd) +{ + fprintf(stderr, ": %s id %d", + qxl_name(qxl_surface_cmd, cmd->type), + cmd->surface_id); + if (cmd->type == QXL_SURFACE_CMD_CREATE) { + fprintf(stderr, " size %dx%d stride %d format %s (count %d, max %d)", + cmd->u.surface_create.width, + cmd->u.surface_create.height, + cmd->u.surface_create.stride, + qxl_name(spice_surface_fmt, cmd->u.surface_create.format), + qxl->guest_surfaces.count, qxl->guest_surfaces.max); + } + if (cmd->type == QXL_SURFACE_CMD_DESTROY) { + fprintf(stderr, " (count %d)", qxl->guest_surfaces.count); + } +} + +void qxl_log_cmd_cursor(PCIQXLDevice *qxl, QXLCursorCmd *cmd, int group_id) +{ + QXLCursor *cursor; + + fprintf(stderr, ": %s", + qxl_name(qxl_cursor_cmd, cmd->type)); + switch (cmd->type) { + case QXL_CURSOR_SET: + fprintf(stderr, " +%d+%d visible %s, shape @ 0x%" PRIx64, + cmd->u.set.position.x, + cmd->u.set.position.y, + cmd->u.set.visible ? "yes" : "no", + cmd->u.set.shape); + cursor = qxl_phys2virt(qxl, cmd->u.set.shape, group_id); + fprintf(stderr, " type %s size %dx%d hot-spot +%d+%d" + " unique 0x%" PRIx64 " data-size %d", + qxl_name(spice_cursor_type, cursor->header.type), + cursor->header.width, cursor->header.height, + cursor->header.hot_spot_x, cursor->header.hot_spot_y, + cursor->header.unique, cursor->data_size); + break; + case QXL_CURSOR_MOVE: + fprintf(stderr, " +%d+%d", cmd->u.position.x, cmd->u.position.y); + break; + } +} + +void qxl_log_command(PCIQXLDevice *qxl, const char *ring, QXLCommandExt *ext) +{ + bool compat = ext->flags & QXL_COMMAND_FLAG_COMPAT; + void *data; + + if (!qxl->cmdlog) { + return; + } + fprintf(stderr, "qxl-%d/%s:", qxl->id, ring); + fprintf(stderr, " cmd @ 0x%" PRIx64 " %s%s", ext->cmd.data, + qxl_name(qxl_type, ext->cmd.type), + compat ? "(compat)" : ""); + + data = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id); + switch (ext->cmd.type) { + case QXL_CMD_DRAW: + if (!compat) { + qxl_log_cmd_draw(qxl, data, ext->group_id); + } else { + qxl_log_cmd_draw_compat(qxl, data, ext->group_id); + } + break; + case QXL_CMD_SURFACE: + qxl_log_cmd_surface(qxl, data); + break; + case QXL_CMD_CURSOR: + qxl_log_cmd_cursor(qxl, data, ext->group_id); + break; + } + fprintf(stderr, "\n"); +} diff -Nru qemu-kvm-0.12.5+noroms/hw/qxl-render.c qemu-kvm-0.14.1/hw/qxl-render.c --- qemu-kvm-0.12.5+noroms/hw/qxl-render.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/qxl-render.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,226 @@ +/* + * qxl local rendering (aka display on sdl/vnc) + * + * Copyright (C) 2010 Red Hat, Inc. + * + * maintained by Gerd Hoffmann + * + * 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 or + * (at your option) version 3 of the License. + * + * 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, see . + */ + +#include "qxl.h" + +static void qxl_flip(PCIQXLDevice *qxl, QXLRect *rect) +{ + uint8_t *src = qxl->guest_primary.data; + uint8_t *dst = qxl->guest_primary.flipped; + int len, i; + + src += (qxl->guest_primary.surface.height - rect->top - 1) * + qxl->guest_primary.stride; + dst += rect->top * qxl->guest_primary.stride; + src += rect->left * qxl->guest_primary.bytes_pp; + dst += rect->left * qxl->guest_primary.bytes_pp; + len = (rect->right - rect->left) * qxl->guest_primary.bytes_pp; + + for (i = rect->top; i < rect->bottom; i++) { + memcpy(dst, src, len); + dst += qxl->guest_primary.stride; + src -= qxl->guest_primary.stride; + } +} + +void qxl_render_resize(PCIQXLDevice *qxl) +{ + QXLSurfaceCreate *sc = &qxl->guest_primary.surface; + + qxl->guest_primary.stride = sc->stride; + qxl->guest_primary.resized++; + switch (sc->format) { + case SPICE_SURFACE_FMT_16_555: + qxl->guest_primary.bytes_pp = 2; + qxl->guest_primary.bits_pp = 15; + break; + case SPICE_SURFACE_FMT_16_565: + qxl->guest_primary.bytes_pp = 2; + qxl->guest_primary.bits_pp = 16; + break; + case SPICE_SURFACE_FMT_32_xRGB: + case SPICE_SURFACE_FMT_32_ARGB: + qxl->guest_primary.bytes_pp = 4; + qxl->guest_primary.bits_pp = 32; + break; + default: + fprintf(stderr, "%s: unhandled format: %x\n", __FUNCTION__, + qxl->guest_primary.surface.format); + qxl->guest_primary.bytes_pp = 4; + qxl->guest_primary.bits_pp = 32; + break; + } +} + +void qxl_render_update(PCIQXLDevice *qxl) +{ + VGACommonState *vga = &qxl->vga; + QXLRect dirty[32], update; + void *ptr; + int i; + + if (qxl->guest_primary.resized) { + qxl->guest_primary.resized = 0; + + if (qxl->guest_primary.flipped) { + qemu_free(qxl->guest_primary.flipped); + qxl->guest_primary.flipped = NULL; + } + qemu_free_displaysurface(vga->ds); + + qxl->guest_primary.data = qemu_get_ram_ptr(qxl->vga.vram_offset); + if (qxl->guest_primary.stride < 0) { + /* spice surface is upside down -> need extra buffer to flip */ + qxl->guest_primary.stride = -qxl->guest_primary.stride; + qxl->guest_primary.flipped = qemu_malloc(qxl->guest_primary.surface.width * + qxl->guest_primary.stride); + ptr = qxl->guest_primary.flipped; + } else { + ptr = qxl->guest_primary.data; + } + dprint(qxl, 1, "%s: %dx%d, stride %d, bpp %d, depth %d, flip %s\n", + __FUNCTION__, + qxl->guest_primary.surface.width, + qxl->guest_primary.surface.height, + qxl->guest_primary.stride, + qxl->guest_primary.bytes_pp, + qxl->guest_primary.bits_pp, + qxl->guest_primary.flipped ? "yes" : "no"); + vga->ds->surface = + qemu_create_displaysurface_from(qxl->guest_primary.surface.width, + qxl->guest_primary.surface.height, + qxl->guest_primary.bits_pp, + qxl->guest_primary.stride, + ptr); + dpy_resize(vga->ds); + } + + if (!qxl->guest_primary.commands) { + return; + } + qxl->guest_primary.commands = 0; + + update.left = 0; + update.right = qxl->guest_primary.surface.width; + update.top = 0; + update.bottom = qxl->guest_primary.surface.height; + + memset(dirty, 0, sizeof(dirty)); + qxl->ssd.worker->update_area(qxl->ssd.worker, 0, &update, + dirty, ARRAY_SIZE(dirty), 1); + + for (i = 0; i < ARRAY_SIZE(dirty); i++) { + if (qemu_spice_rect_is_empty(dirty+i)) { + break; + } + if (qxl->guest_primary.flipped) { + qxl_flip(qxl, dirty+i); + } + dpy_update(vga->ds, + dirty[i].left, dirty[i].top, + dirty[i].right - dirty[i].left, + dirty[i].bottom - dirty[i].top); + } +} + +static QEMUCursor *qxl_cursor(PCIQXLDevice *qxl, QXLCursor *cursor) +{ + QEMUCursor *c; + uint8_t *image, *mask; + int size; + + c = cursor_alloc(cursor->header.width, cursor->header.height); + c->hot_x = cursor->header.hot_spot_x; + c->hot_y = cursor->header.hot_spot_y; + switch (cursor->header.type) { + case SPICE_CURSOR_TYPE_ALPHA: + size = cursor->header.width * cursor->header.height * sizeof(uint32_t); + memcpy(c->data, cursor->chunk.data, size); + if (qxl->debug > 2) { + cursor_print_ascii_art(c, "qxl/alpha"); + } + break; + case SPICE_CURSOR_TYPE_MONO: + mask = cursor->chunk.data; + image = mask + cursor_get_mono_bpl(c) * c->width; + cursor_set_mono(c, 0xffffff, 0x000000, image, 1, mask); + if (qxl->debug > 2) { + cursor_print_ascii_art(c, "qxl/mono"); + } + break; + default: + fprintf(stderr, "%s: not implemented: type %d\n", + __FUNCTION__, cursor->header.type); + goto fail; + } + return c; + +fail: + cursor_put(c); + return NULL; +} + + +/* called from spice server thread context only */ +void qxl_render_cursor(PCIQXLDevice *qxl, QXLCommandExt *ext) +{ + QXLCursorCmd *cmd = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id); + QXLCursor *cursor; + QEMUCursor *c; + int x = -1, y = -1; + + if (!qxl->ssd.ds->mouse_set || !qxl->ssd.ds->cursor_define) { + return; + } + + if (qxl->debug > 1 && cmd->type != QXL_CURSOR_MOVE) { + fprintf(stderr, "%s", __FUNCTION__); + qxl_log_cmd_cursor(qxl, cmd, ext->group_id); + fprintf(stderr, "\n"); + } + switch (cmd->type) { + case QXL_CURSOR_SET: + x = cmd->u.set.position.x; + y = cmd->u.set.position.y; + cursor = qxl_phys2virt(qxl, cmd->u.set.shape, ext->group_id); + if (cursor->chunk.data_size != cursor->data_size) { + fprintf(stderr, "%s: multiple chunks\n", __FUNCTION__); + return; + } + c = qxl_cursor(qxl, cursor); + if (c == NULL) { + c = cursor_builtin_left_ptr(); + } + qemu_mutex_lock_iothread(); + qxl->ssd.ds->cursor_define(c); + qxl->ssd.ds->mouse_set(x, y, 1); + qemu_mutex_unlock_iothread(); + cursor_put(c); + break; + case QXL_CURSOR_MOVE: + x = cmd->u.position.x; + y = cmd->u.position.y; + qemu_mutex_lock_iothread(); + qxl->ssd.ds->mouse_set(x, y, 1); + qemu_mutex_unlock_iothread(); + break; + } +} diff -Nru qemu-kvm-0.12.5+noroms/hw/r2d.c qemu-kvm-0.14.1/hw/r2d.c --- qemu-kvm-0.12.5+noroms/hw/r2d.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/r2d.c 2011-05-11 13:29:46.000000000 +0000 @@ -23,25 +23,33 @@ * THE SOFTWARE. */ +#include "sysbus.h" #include "hw.h" #include "sh.h" #include "devices.h" #include "sysemu.h" #include "boards.h" #include "pci.h" -#include "sh_pci.h" #include "net.h" #include "sh7750_regs.h" #include "ide.h" #include "loader.h" +#include "usb.h" +#include "flash.h" +#include "blockdev.h" + +#define FLASH_BASE 0x00000000 +#define FLASH_SIZE 0x02000000 #define SDRAM_BASE 0x0c000000 /* Physical location of SDRAM: Area 3 */ #define SDRAM_SIZE 0x04000000 #define SM501_VRAM_SIZE 0x800000 +#define BOOT_PARAMS_OFFSET 0x0010000 /* CONFIG_BOOT_LINK_OFFSET of Linux kernel */ -#define LINUX_LOAD_OFFSET 0x800000 +#define LINUX_LOAD_OFFSET 0x0800000 +#define INITRD_LOAD_OFFSET 0x1800000 #define PA_IRLMSK 0x00 #define PA_POWOFF 0x30 @@ -66,7 +74,6 @@ uint16_t keyctlclr; uint16_t pad0; uint16_t pad1; - uint16_t powoff; uint16_t verreg; uint16_t inport; uint16_t outport; @@ -128,7 +135,7 @@ case PA_OUTPORT: return s->outport; case PA_POWOFF: - return s->powoff; + return 0x00; case PA_VERREG: return 0x10; } @@ -150,8 +157,10 @@ s->outport = value; break; case PA_POWOFF: - s->powoff = value; - break; + if (value & 1) { + qemu_system_shutdown_request(); + } + break; case PA_VERREG: /* Discard writes */ break; @@ -180,23 +189,39 @@ s->irl = irl; iomemtype = cpu_register_io_memory(r2d_fpga_readfn, - r2d_fpga_writefn, s); + r2d_fpga_writefn, s, + DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base, 0x40, iomemtype); return qemu_allocate_irqs(r2d_fpga_irq_set, s, NR_IRQS); } -static void r2d_pci_set_irq(void *opaque, int n, int l) +typedef struct ResetData { + CPUState *env; + uint32_t vector; +} ResetData; + +static void main_cpu_reset(void *opaque) { - qemu_irq *p = opaque; + ResetData *s = (ResetData *)opaque; + CPUState *env = s->env; - qemu_set_irq(p[n], l); + cpu_reset(env); + env->pc = s->vector; } -static int r2d_pci_map_irq(PCIDevice *d, int irq_num) +static struct __attribute__((__packed__)) { - const int intx[] = { PCI_INTA, PCI_INTB, PCI_INTC, PCI_INTD }; - return intx[d->devfn >> 3]; -} + int mount_root_rdonly; + int ramdisk_flags; + int orig_root_dev; + int loader_type; + int initrd_start; + int initrd_size; + + char pad[232]; + + char kernel_cmdline[256]; +} boot_params; static void r2d_init(ram_addr_t ram_size, const char *boot_device, @@ -204,10 +229,10 @@ const char *initrd_filename, const char *cpu_model) { CPUState *env; + ResetData *reset_info; struct SH7750State *s; ram_addr_t sdram_addr; qemu_irq *irq; - PCIBus *pci; DriveInfo *dinfo; int i; @@ -219,49 +244,87 @@ fprintf(stderr, "Unable to find CPU definition\n"); exit(1); } + reset_info = qemu_mallocz(sizeof(ResetData)); + reset_info->env = env; + reset_info->vector = env->pc; + qemu_register_reset(main_cpu_reset, reset_info); /* Allocate memory space */ - sdram_addr = qemu_ram_alloc(SDRAM_SIZE); + sdram_addr = qemu_ram_alloc(NULL, "r2d.sdram", SDRAM_SIZE); cpu_register_physical_memory(SDRAM_BASE, SDRAM_SIZE, sdram_addr); /* Register peripherals */ s = sh7750_init(env); irq = r2d_fpga_init(0x04000000, sh7750_irl(s)); - pci = sh_pci_register_bus(r2d_pci_set_irq, r2d_pci_map_irq, irq, 0, 4); + sysbus_create_varargs("sh_pci", 0x1e200000, irq[PCI_INTA], irq[PCI_INTB], + irq[PCI_INTC], irq[PCI_INTD], NULL); sm501_init(0x10000000, SM501_VRAM_SIZE, irq[SM501], serial_hds[2]); /* onboard CF (True IDE mode, Master only). */ - if ((dinfo = drive_get(IF_IDE, 0, 0)) != NULL) - mmio_ide_init(0x14001000, 0x1400080c, irq[CF_IDE], 1, - dinfo, NULL); + dinfo = drive_get(IF_IDE, 0, 0); + mmio_ide_init(0x14001000, 0x1400080c, irq[CF_IDE], 1, + dinfo, NULL); + + /* onboard flash memory */ + dinfo = drive_get(IF_PFLASH, 0, 0); + pflash_cfi02_register(0x0, qemu_ram_alloc(NULL, "r2d.flash", FLASH_SIZE), + dinfo ? dinfo->bdrv : NULL, (16 * 1024), + FLASH_SIZE >> 16, + 1, 4, 0x0000, 0x0000, 0x0000, 0x0000, + 0x555, 0x2aa, 0); /* NIC: rtl8139 on-board, and 2 slots. */ for (i = 0; i < nb_nics; i++) pci_nic_init_nofail(&nd_table[i], "rtl8139", i==0 ? "2" : NULL); + /* USB keyboard */ + usbdevice_create("keyboard"); + /* Todo: register on board registers */ + memset(&boot_params, 0, sizeof(boot_params)); + if (kernel_filename) { - int kernel_size; - /* initialization which should be done by firmware */ - stl_phys(SH7750_BCR1, 1<<3); /* cs3 SDRAM */ - stw_phys(SH7750_BCR2, 3<<(3*2)); /* cs3 32bit */ - - if (kernel_cmdline) { - kernel_size = load_image_targphys(kernel_filename, - SDRAM_BASE + LINUX_LOAD_OFFSET, - SDRAM_SIZE - LINUX_LOAD_OFFSET); - env->pc = (SDRAM_BASE + LINUX_LOAD_OFFSET) | 0xa0000000; - pstrcpy_targphys("cmdline", SDRAM_BASE + 0x10100, 256, kernel_cmdline); - } else { - kernel_size = load_image_targphys(kernel_filename, SDRAM_BASE, SDRAM_SIZE); - env->pc = SDRAM_BASE | 0xa0000000; /* Start from P2 area */ - } + int kernel_size; - if (kernel_size < 0) { - fprintf(stderr, "qemu: could not load kernel '%s'\n", kernel_filename); - exit(1); - } + kernel_size = load_image_targphys(kernel_filename, + SDRAM_BASE + LINUX_LOAD_OFFSET, + INITRD_LOAD_OFFSET - LINUX_LOAD_OFFSET); + if (kernel_size < 0) { + fprintf(stderr, "qemu: could not load kernel '%s'\n", kernel_filename); + exit(1); + } + + /* initialization which should be done by firmware */ + stl_phys(SH7750_BCR1, 1<<3); /* cs3 SDRAM */ + stw_phys(SH7750_BCR2, 3<<(3*2)); /* cs3 32bit */ + reset_info->vector = (SDRAM_BASE + LINUX_LOAD_OFFSET) | 0xa0000000; /* Start from P2 area */ + } + + if (initrd_filename) { + int initrd_size; + + initrd_size = load_image_targphys(initrd_filename, + SDRAM_BASE + INITRD_LOAD_OFFSET, + SDRAM_SIZE - INITRD_LOAD_OFFSET); + + if (initrd_size < 0) { + fprintf(stderr, "qemu: could not load initrd '%s'\n", initrd_filename); + exit(1); + } + + /* initialization which should be done by firmware */ + boot_params.loader_type = 1; + boot_params.initrd_start = INITRD_LOAD_OFFSET; + boot_params.initrd_size = initrd_size; + } + + if (kernel_cmdline) { + strncpy(boot_params.kernel_cmdline, kernel_cmdline, + sizeof(boot_params.kernel_cmdline)); } + + rom_add_blob_fixed("boot_params", &boot_params, sizeof(boot_params), + SDRAM_BASE + BOOT_PARAMS_OFFSET); } static QEMUMachine r2d_machine = { diff -Nru qemu-kvm-0.12.5+noroms/hw/rc4030.c qemu-kvm-0.14.1/hw/rc4030.c --- qemu-kvm-0.12.5+noroms/hw/rc4030.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/rc4030.c 2011-05-11 13:29:46.000000000 +0000 @@ -240,8 +240,9 @@ break; } - if ((addr & ~3) != 0x230) + if ((addr & ~3) != 0x230) { DPRINTF("read 0x%02x at " TARGET_FMT_plx "\n", val, addr); + } return val; } @@ -748,7 +749,10 @@ printf("rc4030 dma: Copying %d bytes %s host %p\n", len, is_write ? "from" : "to", buf); for (i = 0; i < len; i += 16) { - int n = min(16, len - i); + int n = 16; + if (n > len - i) { + n = len - i; + } for (j = 0; j < n; j++) printf("%02x ", buf[i + j]); while (j++ < 16) @@ -812,12 +816,14 @@ s->jazz_bus_irq = jazz_bus; qemu_register_reset(rc4030_reset, s); - register_savevm("rc4030", 0, 2, rc4030_save, rc4030_load, s); + register_savevm(NULL, "rc4030", 0, 2, rc4030_save, rc4030_load, s); rc4030_reset(s); - s_chipset = cpu_register_io_memory(rc4030_read, rc4030_write, s); + s_chipset = cpu_register_io_memory(rc4030_read, rc4030_write, s, + DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(0x80000000, 0x300, s_chipset); - s_jazzio = cpu_register_io_memory(jazzio_read, jazzio_write, s); + s_jazzio = cpu_register_io_memory(jazzio_read, jazzio_write, s, + DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(0xf0000000, 0x00001000, s_jazzio); return s; diff -Nru qemu-kvm-0.12.5+noroms/hw/realview.c qemu-kvm-0.14.1/hw/realview.c --- qemu-kvm-0.12.5+noroms/hw/realview.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/realview.c 2011-05-11 13:29:46.000000000 +0000 @@ -18,6 +18,7 @@ #include "boards.h" #include "bitbang_i2c.h" #include "sysbus.h" +#include "blockdev.h" #define SMP_BOOT_ADDR 0xe0000000 @@ -80,7 +81,8 @@ bus = i2c_init_bus(&dev->qdev, "i2c"); s->bitbang = bitbang_i2c_init(bus); iomemtype = cpu_register_io_memory(realview_i2c_readfn, - realview_i2c_writefn, s); + realview_i2c_writefn, s, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, 0x1000, iomemtype); return 0; } @@ -192,11 +194,19 @@ proc_id = 0x02000000; } - ram_offset = qemu_ram_alloc(ram_size); + if (is_pb && ram_size > 0x20000000) { + /* Core tile RAM. */ + low_ram_size = ram_size - 0x20000000; + ram_size = 0x20000000; + ram_offset = qemu_ram_alloc(NULL, "realview.lowmem", low_ram_size); + cpu_register_physical_memory(0x20000000, low_ram_size, + ram_offset | IO_MEM_RAM); + } + + ram_offset = qemu_ram_alloc(NULL, "realview.highmem", ram_size); low_ram_size = ram_size; if (low_ram_size > 0x10000000) low_ram_size = 0x10000000; - /* ??? RAM should repeat to fill physical memory space. */ /* SDRAM at address zero. */ cpu_register_physical_memory(0, low_ram_size, ram_offset | IO_MEM_RAM); if (is_pb) { @@ -346,7 +356,7 @@ startup code. I guess this works on real hardware because the BootROM happens to be in ROM/flash or in memory that isn't clobbered until after Linux boots the secondary CPUs. */ - ram_offset = qemu_ram_alloc(0x1000); + ram_offset = qemu_ram_alloc(NULL, "realview.hack", 0x1000); cpu_register_physical_memory(SMP_BOOT_ADDR, 0x1000, ram_offset | IO_MEM_RAM); @@ -356,7 +366,7 @@ realview_binfo.initrd_filename = initrd_filename; realview_binfo.nb_cpus = smp_cpus; realview_binfo.board_id = realview_board_id[board_type]; - realview_binfo.loader_start = is_pb ? 0x70000000 : 0; + realview_binfo.loader_start = (board_type == BOARD_PB_A8 ? 0x70000000 : 0); arm_load_kernel(first_cpu, &realview_binfo); } diff -Nru qemu-kvm-0.12.5+noroms/hw/realview_gic.c qemu-kvm-0.14.1/hw/realview_gic.c --- qemu-kvm-0.12.5+noroms/hw/realview_gic.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/realview_gic.c 2011-05-11 13:29:46.000000000 +0000 @@ -64,7 +64,8 @@ gic_init(&s->gic); s->iomemtype = cpu_register_io_memory(realview_gic_cpu_readfn, - realview_gic_cpu_writefn, s); + realview_gic_cpu_writefn, s, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio_cb(dev, 0x2000, realview_gic_map); return 0; } diff -Nru qemu-kvm-0.12.5+noroms/hw/rtl8139.c qemu-kvm-0.14.1/hw/rtl8139.c --- qemu-kvm-0.12.5+noroms/hw/rtl8139.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/rtl8139.c 2011-05-11 13:29:46.000000000 +0000 @@ -41,6 +41,10 @@ * segmentation offloading * Removed slirp.h dependency * Added rx/tx buffer reset when enabling rx/tx operation + * + * 2010-Feb-04 Frediano Ziglio: Rewrote timer support using QEMU timer only + * when strictly needed (required for for + * Darwin) */ #include "hw.h" @@ -48,6 +52,7 @@ #include "qemu-timer.h" #include "net.h" #include "loader.h" +#include "sysemu.h" /* debug RTL8139 card */ //#define DEBUG_RTL8139 1 @@ -60,9 +65,6 @@ /* Calculate CRCs properly on Rx packets */ #define RTL8139_CALCULATE_RXCRC 1 -/* Uncomment to enable on-board timer interrupts */ -//#define RTL8139_ONBOARD_TIMER 1 - #if defined(RTL8139_CALCULATE_RXCRC) /* For crc32 */ #include @@ -491,9 +493,14 @@ /* PCI interrupt timer */ QEMUTimer *timer; + int64_t TimerExpire; + /* Support migration to/from old versions */ + int rtl8139_mmio_io_addr_dummy; } RTL8139State; +static void rtl8139_set_next_tctr_time(RTL8139State *s, int64_t current_time); + static void prom9346_decode_command(EEprom9346 *eeprom, uint8_t command) { DEBUG_PRINT(("RTL8139: eeprom command 0x%02x\n", command)); @@ -1909,6 +1916,7 @@ cpu_physical_memory_read(cplus_tx_ring_desc, (uint8_t *)&val, 4); txdw0 = le32_to_cpu(val); + /* TODO: implement VLAN tagging support, VLAN tag data is read to txdw1 */ cpu_physical_memory_read(cplus_tx_ring_desc+4, (uint8_t *)&val, 4); txdw1 = le32_to_cpu(val); cpu_physical_memory_read(cplus_tx_ring_desc+8, (uint8_t *)&val, 4); @@ -1920,6 +1928,9 @@ descriptor, txdw0, txdw1, txbufLO, txbufHI)); + /* TODO: the following discard cast should clean clang analyzer output */ + (void)txdw1; + /* w0 ownership flag */ #define CP_TX_OWN (1<<31) /* w0 end of ring flag */ @@ -2045,6 +2056,7 @@ /* update ring data */ val = cpu_to_le32(txdw0); cpu_physical_memory_write(cplus_tx_ring_desc, (uint8_t *)&val, 4); + /* TODO: implement VLAN tagging support, VLAN tag data is read to txdw1 */ // val = cpu_to_le32(txdw1); // cpu_physical_memory_write(cplus_tx_ring_desc+4, &val, 4); @@ -2517,7 +2529,9 @@ s->IntrMask = val; + rtl8139_set_next_tctr_time(s, qemu_get_clock(vm_clock)); rtl8139_update_irq(s); + } static uint32_t rtl8139_IntrMask_read(RTL8139State *s) @@ -2550,12 +2564,22 @@ rtl8139_update_irq(s); s->IntrStatus = newStatus; + /* + * Computing if we miss an interrupt here is not that correct but + * considered that we should have had already an interrupt + * and probably emulated is slower is better to assume this resetting was + * done before testing on previous rtl8139_update_irq lead to IRQ loosing + */ + rtl8139_set_next_tctr_time(s, qemu_get_clock(vm_clock)); rtl8139_update_irq(s); + #endif } static uint32_t rtl8139_IntrStatus_read(RTL8139State *s) { + rtl8139_set_next_tctr_time(s, qemu_get_clock(vm_clock)); + uint32_t ret = s->IntrStatus; DEBUG_PRINT(("RTL8139: IntrStatus read(w) val=0x%04x\n", ret)); @@ -2734,6 +2758,46 @@ } } +static void rtl8139_set_next_tctr_time(RTL8139State *s, int64_t current_time) +{ + int64_t pci_time, next_time; + uint32_t low_pci; + + DEBUG_PRINT(("RTL8139: entered rtl8139_set_next_tctr_time\n")); + + if (s->TimerExpire && current_time >= s->TimerExpire) { + s->IntrStatus |= PCSTimeout; + rtl8139_update_irq(s); + } + + /* Set QEMU timer only if needed that is + * - TimerInt <> 0 (we have a timer) + * - mask = 1 (we want an interrupt timer) + * - irq = 0 (irq is not already active) + * If any of above change we need to compute timer again + * Also we must check if timer is passed without QEMU timer + */ + s->TimerExpire = 0; + if (!s->TimerInt) { + return; + } + + pci_time = muldiv64(current_time - s->TCTR_base, PCI_FREQUENCY, + get_ticks_per_sec()); + low_pci = pci_time & 0xffffffff; + pci_time = pci_time - low_pci + s->TimerInt; + if (low_pci >= s->TimerInt) { + pci_time += 0x100000000LL; + } + next_time = s->TCTR_base + muldiv64(pci_time, get_ticks_per_sec(), + PCI_FREQUENCY); + s->TimerExpire = next_time; + + if ((s->IntrMask & PCSTimeout) != 0 && (s->IntrStatus & PCSTimeout) == 0) { + qemu_mod_timer(s->timer, next_time); + } +} + static void rtl8139_io_writel(void *opaque, uint8_t addr, uint32_t val) { RTL8139State *s = opaque; @@ -2779,13 +2843,16 @@ case Timer: DEBUG_PRINT(("RTL8139: TCTR Timer reset on write\n")); - s->TCTR = 0; s->TCTR_base = qemu_get_clock(vm_clock); + rtl8139_set_next_tctr_time(s, s->TCTR_base); break; case FlashReg: DEBUG_PRINT(("RTL8139: FlashReg TimerInt write val=0x%08x\n", val)); - s->TimerInt = val; + if (s->TimerInt != val) { + s->TimerInt = val; + rtl8139_set_next_tctr_time(s, qemu_get_clock(vm_clock)); + } break; default: @@ -2995,7 +3062,8 @@ break; case Timer: - ret = s->TCTR; + ret = muldiv64(qemu_get_clock(vm_clock) - s->TCTR_base, + PCI_FREQUENCY, get_ticks_per_sec()); DEBUG_PRINT(("RTL8139: TCTR Timer read val=0x%08x\n", ret)); break; @@ -3060,17 +3128,11 @@ static void rtl8139_mmio_writew(void *opaque, target_phys_addr_t addr, uint32_t val) { -#ifdef TARGET_WORDS_BIGENDIAN - val = bswap16(val); -#endif rtl8139_io_writew(opaque, addr & 0xFF, val); } static void rtl8139_mmio_writel(void *opaque, target_phys_addr_t addr, uint32_t val) { -#ifdef TARGET_WORDS_BIGENDIAN - val = bswap32(val); -#endif rtl8139_io_writel(opaque, addr & 0xFF, val); } @@ -3082,24 +3144,19 @@ static uint32_t rtl8139_mmio_readw(void *opaque, target_phys_addr_t addr) { uint32_t val = rtl8139_io_readw(opaque, addr & 0xFF); -#ifdef TARGET_WORDS_BIGENDIAN - val = bswap16(val); -#endif return val; } static uint32_t rtl8139_mmio_readl(void *opaque, target_phys_addr_t addr) { uint32_t val = rtl8139_io_readl(opaque, addr & 0xFF); -#ifdef TARGET_WORDS_BIGENDIAN - val = bswap32(val); -#endif return val; } static int rtl8139_post_load(void *opaque, int version_id) { RTL8139State* s = opaque; + rtl8139_set_next_tctr_time(s, qemu_get_clock(vm_clock)); if (version_id < 4) { s->cplus_enabled = s->CpCmd != 0; } @@ -3107,12 +3164,40 @@ return 0; } +static bool rtl8139_hotplug_ready_needed(void *opaque) +{ + return qdev_machine_modified(); +} + +static const VMStateDescription vmstate_rtl8139_hotplug_ready ={ + .name = "rtl8139/hotplug_ready", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField []) { + VMSTATE_END_OF_LIST() + } +}; + +static void rtl8139_pre_save(void *opaque) +{ + RTL8139State* s = opaque; + int64_t current_time = qemu_get_clock(vm_clock); + + /* set IntrStatus correctly */ + rtl8139_set_next_tctr_time(s, current_time); + s->TCTR = muldiv64(current_time - s->TCTR_base, PCI_FREQUENCY, + get_ticks_per_sec()); + s->rtl8139_mmio_io_addr_dummy = s->rtl8139_mmio_io_addr; +} + static const VMStateDescription vmstate_rtl8139 = { .name = "rtl8139", .version_id = 4, .minimum_version_id = 3, .minimum_version_id_old = 3, .post_load = rtl8139_post_load, + .pre_save = rtl8139_pre_save, .fields = (VMStateField []) { VMSTATE_PCI_DEVICE(dev, RTL8139State), VMSTATE_PARTIAL_BUFFER(phys, RTL8139State, 6), @@ -3156,7 +3241,7 @@ VMSTATE_UNUSED(4), VMSTATE_MACADDR(conf.macaddr, RTL8139State), - VMSTATE_INT32(rtl8139_mmio_io_addr, RTL8139State), + VMSTATE_INT32(rtl8139_mmio_io_addr_dummy, RTL8139State), VMSTATE_UINT32(currTxDesc, RTL8139State), VMSTATE_UINT32(currCPlusRxDesc, RTL8139State), @@ -3185,6 +3270,14 @@ VMSTATE_UINT32_V(cplus_enabled, RTL8139State, 4), VMSTATE_END_OF_LIST() + }, + .subsections = (VMStateSubsection []) { + { + .vmsd = &vmstate_rtl8139_hotplug_ready, + .needed = rtl8139_hotplug_ready_needed, + }, { + /* empty */ + } } }; @@ -3226,59 +3319,20 @@ rtl8139_mmio_writel, }; -static inline int64_t rtl8139_get_next_tctr_time(RTL8139State *s, int64_t current_time) -{ - int64_t next_time = current_time + - muldiv64(1, get_ticks_per_sec(), PCI_FREQUENCY); - if (next_time <= current_time) - next_time = current_time + 1; - return next_time; -} - -#ifdef RTL8139_ONBOARD_TIMER static void rtl8139_timer(void *opaque) { RTL8139State *s = opaque; - int is_timeout = 0; - - int64_t curr_time; - uint32_t curr_tick; - if (!s->clock_enabled) { DEBUG_PRINT(("RTL8139: >>> timer: clock is not running\n")); return; } - curr_time = qemu_get_clock(vm_clock); - - curr_tick = muldiv64(curr_time - s->TCTR_base, PCI_FREQUENCY, - get_ticks_per_sec()); - - if (s->TimerInt && curr_tick >= s->TimerInt) - { - if (s->TCTR < s->TimerInt || curr_tick < s->TCTR) - { - is_timeout = 1; - } - } - - s->TCTR = curr_tick; - -// DEBUG_PRINT(("RTL8139: >>> timer: tick=%08u\n", s->TCTR)); - - if (is_timeout) - { - DEBUG_PRINT(("RTL8139: >>> timer: timeout tick=%08u\n", s->TCTR)); - s->IntrStatus |= PCSTimeout; - rtl8139_update_irq(s); - } - - qemu_mod_timer(s->timer, - rtl8139_get_next_tctr_time(s,curr_time)); + s->IntrStatus |= PCSTimeout; + rtl8139_update_irq(s); + rtl8139_set_next_tctr_time(s, qemu_get_clock(vm_clock)); } -#endif /* RTL8139_ONBOARD_TIMER */ static void rtl8139_cleanup(VLANClientState *nc) { @@ -3296,10 +3350,8 @@ qemu_free(s->cplus_txbuffer); s->cplus_txbuffer = NULL; } -#ifdef RTL8139_ONBOARD_TIMER qemu_del_timer(s->timer); qemu_free_timer(s->timer); -#endif qemu_del_vlan_client(&s->nic->nc); return 0; } @@ -3320,16 +3372,17 @@ pci_conf = s->dev.config; pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_REALTEK); pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_REALTEK_8139); - pci_conf[0x04] = 0x05; /* command = I/O space, Bus Master */ - pci_conf[0x08] = RTL8139_PCI_REVID; /* PCI revision ID; >=0x20 is for 8139C+ */ + pci_conf[PCI_REVISION_ID] = RTL8139_PCI_REVID; /* >=0x20 is for 8139C+ */ pci_config_set_class(pci_conf, PCI_CLASS_NETWORK_ETHERNET); - pci_conf[PCI_HEADER_TYPE] = PCI_HEADER_TYPE_NORMAL; /* header_type */ - pci_conf[0x3d] = 1; /* interrupt pin 0 */ - pci_conf[0x34] = 0xdc; + pci_conf[PCI_INTERRUPT_PIN] = 1; /* interrupt pin 0 */ + /* TODO: start of capability list, but no capability + * list bit in status register, and offset 0xdc seems unused. */ + pci_conf[PCI_CAPABILITY_LIST] = 0xdc; /* I/O handler for memory-mapped I/O */ s->rtl8139_mmio_io_addr = - cpu_register_io_memory(rtl8139_mmio_read, rtl8139_mmio_write, s); + cpu_register_io_memory(rtl8139_mmio_read, rtl8139_mmio_write, s, + DEVICE_LITTLE_ENDIAN); pci_register_bar(&s->dev, 0, 0x100, PCI_BASE_ADDRESS_SPACE_IO, rtl8139_ioport_map); @@ -3347,12 +3400,12 @@ s->cplus_txbuffer_len = 0; s->cplus_txbuffer_offset = 0; -#ifdef RTL8139_ONBOARD_TIMER + s->TimerExpire = 0; s->timer = qemu_new_timer(vm_clock, rtl8139_timer, s); + rtl8139_set_next_tctr_time(s, qemu_get_clock(vm_clock)); + + add_boot_device_path(s->conf.bootindex, &dev->qdev, "/ethernet-phy@0"); - qemu_mod_timer(s->timer, - rtl8139_get_next_tctr_time(s,qemu_get_clock(vm_clock))); -#endif /* RTL8139_ONBOARD_TIMER */ return 0; } diff -Nru qemu-kvm-0.12.5+noroms/hw/s390-virtio-bus.c qemu-kvm-0.14.1/hw/s390-virtio-bus.c --- qemu-kvm-0.12.5+noroms/hw/s390-virtio-bus.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/s390-virtio-bus.c 2011-05-11 13:29:46.000000000 +0000 @@ -26,7 +26,8 @@ #include "loader.h" #include "elf.h" #include "hw/virtio.h" -#include "hw/virtio-console.h" +#include "hw/virtio-serial.h" +#include "hw/virtio-net.h" #include "hw/sysbus.h" #include "kvm.h" @@ -56,7 +57,6 @@ static const VirtIOBindings virtio_s390_bindings; static ram_addr_t s390_virtio_device_num_vq(VirtIOS390Device *dev); -static void s390_virtio_device_sync(VirtIOS390Device *dev); VirtIOS390Bus *s390_virtio_bus_init(ram_addr_t *ram_size) { @@ -101,6 +101,7 @@ bus->dev_offs += dev_len; virtio_bind_device(vdev, &virtio_s390_bindings, dev); + dev->host_features = vdev->get_features(vdev, dev->host_features); s390_virtio_device_sync(dev); return 0; @@ -110,7 +111,7 @@ { VirtIODevice *vdev; - vdev = virtio_net_init((DeviceState *)dev, &dev->nic); + vdev = virtio_net_init((DeviceState *)dev, &dev->nic, &dev->net); if (!vdev) { return -1; } @@ -122,7 +123,7 @@ { VirtIODevice *vdev; - vdev = virtio_blk_init((DeviceState *)dev, dev->dinfo); + vdev = virtio_blk_init((DeviceState *)dev, &dev->block); if (!vdev) { return -1; } @@ -130,7 +131,7 @@ return s390_virtio_device_init(dev, vdev); } -static int s390_virtio_console_init(VirtIOS390Device *dev) +static int s390_virtio_serial_init(VirtIOS390Device *dev) { VirtIOS390Bus *bus; VirtIODevice *vdev; @@ -138,7 +139,7 @@ bus = DO_UPCAST(VirtIOS390Bus, bus, dev->qdev.parent_bus); - vdev = virtio_console_init((DeviceState *)dev); + vdev = virtio_serial_init((DeviceState *)dev, dev->max_virtserial_ports); if (!vdev) { return -1; } @@ -184,7 +185,7 @@ return r; } -static void s390_virtio_device_sync(VirtIOS390Device *dev) +void s390_virtio_device_sync(VirtIOS390Device *dev) { VirtIOS390Bus *bus = DO_UPCAST(VirtIOS390Bus, bus, dev->qdev.parent_bus); ram_addr_t cur_offs; @@ -222,9 +223,7 @@ cur_offs += num_vq * VIRTIO_VQCONFIG_LEN; /* Sync feature bitmap */ - if (dev->vdev->get_features) { - stl_phys(cur_offs, dev->vdev->get_features(dev->vdev)); - } + stl_phys(cur_offs, dev->host_features); dev->feat_offs = cur_offs + dev->feat_len; cur_offs += dev->feat_len * 2; @@ -243,7 +242,7 @@ VirtIODevice *vdev = dev->vdev; uint32_t features; - vdev->status = ldub_phys(dev->dev_offs + VIRTIO_DEV_OFFS_STATUS); + virtio_set_status(vdev, ldub_phys(dev->dev_offs + VIRTIO_DEV_OFFS_STATUS)); /* Update guest supported feature bitmap */ @@ -251,7 +250,7 @@ if (vdev->set_features) { vdev->set_features(vdev, features); } - vdev->features = features; + vdev->guest_features = features; } VirtIOS390Device *s390_virtio_bus_console(VirtIOS390Bus *bus) @@ -310,10 +309,17 @@ kvm_s390_virtio_irq(s390_cpu_addr2state(0), 0, token); } +static unsigned virtio_s390_get_features(void *opaque) +{ + VirtIOS390Device *dev = (VirtIOS390Device*)opaque; + return dev->host_features; +} + /**************** S390 Virtio Bus Device Descriptions *******************/ static const VirtIOBindings virtio_s390_bindings = { .notify = virtio_s390_notify, + .get_features = virtio_s390_get_features, }; static VirtIOS390DeviceInfo s390_virtio_net = { @@ -322,6 +328,11 @@ .qdev.size = sizeof(VirtIOS390Device), .qdev.props = (Property[]) { DEFINE_NIC_PROPERTIES(VirtIOS390Device, nic), + DEFINE_PROP_UINT32("x-txtimer", VirtIOS390Device, + net.txtimer, TX_TIMER_INTERVAL), + DEFINE_PROP_INT32("x-txburst", VirtIOS390Device, + net.txburst, TX_BURST), + DEFINE_PROP_STRING("tx", VirtIOS390Device, net.tx), DEFINE_PROP_END_OF_LIST(), }, }; @@ -331,16 +342,19 @@ .qdev.name = "virtio-blk-s390", .qdev.size = sizeof(VirtIOS390Device), .qdev.props = (Property[]) { - DEFINE_PROP_DRIVE("drive", VirtIOS390Device, dinfo), + DEFINE_BLOCK_PROPERTIES(VirtIOS390Device, block), DEFINE_PROP_END_OF_LIST(), }, }; -static VirtIOS390DeviceInfo s390_virtio_console = { - .init = s390_virtio_console_init, - .qdev.name = "virtio-console-s390", +static VirtIOS390DeviceInfo s390_virtio_serial = { + .init = s390_virtio_serial_init, + .qdev.name = "virtio-serial-s390", + .qdev.alias = "virtio-serial", .qdev.size = sizeof(VirtIOS390Device), .qdev.props = (Property[]) { + DEFINE_PROP_UINT32("max_ports", VirtIOS390Device, max_virtserial_ports, + 31), DEFINE_PROP_END_OF_LIST(), }, }; @@ -364,7 +378,7 @@ static void s390_virtio_register(void) { - s390_virtio_bus_register_withprop(&s390_virtio_console); + s390_virtio_bus_register_withprop(&s390_virtio_serial); s390_virtio_bus_register_withprop(&s390_virtio_blk); s390_virtio_bus_register_withprop(&s390_virtio_net); } diff -Nru qemu-kvm-0.12.5+noroms/hw/s390-virtio-bus.h qemu-kvm-0.14.1/hw/s390-virtio-bus.h --- qemu-kvm-0.12.5+noroms/hw/s390-virtio-bus.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/s390-virtio-bus.h 2011-05-11 13:29:46.000000000 +0000 @@ -17,6 +17,8 @@ * License along with this library; if not, see . */ +#include "virtio-net.h" + #define VIRTIO_DEV_OFFS_TYPE 0 /* 8 bits */ #define VIRTIO_DEV_OFFS_NUM_VQ 1 /* 8 bits */ #define VIRTIO_DEV_OFFS_FEATURE_LEN 2 /* 8 bits */ @@ -38,8 +40,12 @@ ram_addr_t feat_offs; uint8_t feat_len; VirtIODevice *vdev; - DriveInfo *dinfo; + BlockConf block; NICConf nic; + uint32_t host_features; + /* Max. number of ports we can have for a the virtio-serial device */ + uint32_t max_virtserial_ports; + virtio_net_conf net; } VirtIOS390Device; typedef struct VirtIOS390Bus { @@ -52,13 +58,12 @@ } VirtIOS390Bus; -extern void s390_virtio_device_update_status(VirtIOS390Device *dev); +void s390_virtio_device_update_status(VirtIOS390Device *dev); -extern VirtIOS390Device *s390_virtio_bus_console(VirtIOS390Bus *bus); -extern VirtIOS390Bus *s390_virtio_bus_init(ram_addr_t *ram_size); +VirtIOS390Device *s390_virtio_bus_console(VirtIOS390Bus *bus); +VirtIOS390Bus *s390_virtio_bus_init(ram_addr_t *ram_size); -extern VirtIOS390Device *s390_virtio_bus_find_vring(VirtIOS390Bus *bus, - ram_addr_t mem, - int *vq_num); -extern VirtIOS390Device *s390_virtio_bus_find_mem(VirtIOS390Bus *bus, - ram_addr_t mem); +VirtIOS390Device *s390_virtio_bus_find_vring(VirtIOS390Bus *bus, + ram_addr_t mem, int *vq_num); +VirtIOS390Device *s390_virtio_bus_find_mem(VirtIOS390Bus *bus, ram_addr_t mem); +void s390_virtio_device_sync(VirtIOS390Device *dev); diff -Nru qemu-kvm-0.12.5+noroms/hw/s390-virtio.c qemu-kvm-0.14.1/hw/s390-virtio.c --- qemu-kvm-0.12.5+noroms/hw/s390-virtio.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/s390-virtio.c 2011-05-11 13:29:46.000000000 +0000 @@ -19,6 +19,7 @@ #include "hw.h" #include "block.h" +#include "blockdev.h" #include "sysemu.h" #include "net.h" #include "boards.h" @@ -26,7 +27,6 @@ #include "loader.h" #include "elf.h" #include "hw/virtio.h" -#include "hw/virtio-console.h" #include "hw/sysbus.h" #include "kvm.h" @@ -53,6 +53,10 @@ #define INITRD_PARM_SIZE 0x010410UL #define PARMFILE_START 0x001000UL +#define ZIPL_START 0x009000UL +#define ZIPL_LOAD_ADDR 0x009000UL +#define ZIPL_FILENAME "s390-zipl.rom" + #define MAX_BLK_DEVS 10 static VirtIOS390Bus *s390_bus; @@ -100,10 +104,11 @@ break; case KVM_S390_VIRTIO_RESET: { - /* Virtio_reset resets the internal addresses, so we'd have to sync - them up again. We don't want to reallocate a vring though, so let's - just not reset. */ - /* virtio_reset(dev->vdev); */ + VirtIOS390Device *dev; + + dev = s390_virtio_bus_find_mem(s390_bus, mem); + virtio_reset(dev->vdev); + s390_virtio_device_sync(dev); break; } case KVM_S390_VIRTIO_SET_STATUS: @@ -153,7 +158,7 @@ s390_bus = s390_virtio_bus_init(&ram_size); /* allocate RAM */ - ram_addr = qemu_ram_alloc(ram_size); + ram_addr = qemu_ram_alloc(NULL, "s390.ram", ram_size); cpu_register_physical_memory(0, ram_size, ram_addr); /* init CPUs */ @@ -186,9 +191,30 @@ exit(1); } - cpu_synchronize_state(env); env->psw.addr = KERN_IMAGE_START; env->psw.mask = 0x0000000180000000ULL; + } else { + ram_addr_t bios_size = 0; + char *bios_filename; + + /* Load zipl bootloader */ + if (bios_name == NULL) { + bios_name = ZIPL_FILENAME; + } + + bios_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); + bios_size = load_image(bios_filename, qemu_get_ram_ptr(ZIPL_LOAD_ADDR)); + + if ((long)bios_size < 0) { + hw_error("could not load bootloader '%s'\n", bios_name); + } + + if (bios_size > 4096) { + hw_error("stage1 bootloader is > 4k\n"); + } + + env->psw.addr = ZIPL_START; + env->psw.mask = 0x0000000180000000ULL; } if (initrd_filename) { @@ -207,13 +233,6 @@ strlen(kernel_cmdline), 1); } - /* Create VirtIO console */ - for(i = 0; i < MAX_VIRTIO_CONSOLES; i++) { - if (virtcon_hds[i]) { - qdev_init_nofail(qdev_create((BusState *)s390_bus, "virtio-console-s390")); - } - } - /* Create VirtIO network adapters */ for(i = 0; i < nb_nics; i++) { NICInfo *nd = &nd_table[i]; @@ -244,7 +263,7 @@ } dev = qdev_create((BusState *)s390_bus, "virtio-blk-s390"); - qdev_prop_set_drive(dev, "drive", dinfo); + qdev_prop_set_drive_nofail(dev, "drive", dinfo->bdrv); qdev_init_nofail(dev); } } diff -Nru qemu-kvm-0.12.5+noroms/hw/sb16.c qemu-kvm-0.14.1/hw/sb16.c --- qemu-kvm-0.12.5+noroms/hw/sb16.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/sb16.c 2011-05-11 13:29:46.000000000 +0000 @@ -782,8 +782,10 @@ break; case 0xe2: +#ifdef DEBUG d0 = dsp_get_data (s); - ldebug ("E2 = %#x\n", d0); + dolog ("E2 = %#x\n", d0); +#endif break; case 0xe4: @@ -1366,16 +1368,20 @@ for (i = 0; i < ARRAY_SIZE (dsp_write_ports); i++) { register_ioport_write (s->port + dsp_write_ports[i], 1, 1, dsp_write, s); + isa_init_ioport(dev, s->port + dsp_write_ports[i]); } for (i = 0; i < ARRAY_SIZE (dsp_read_ports); i++) { register_ioport_read (s->port + dsp_read_ports[i], 1, 1, dsp_read, s); + isa_init_ioport(dev, s->port + dsp_read_ports[i]); } register_ioport_write (s->port + 0x4, 1, 1, mixer_write_indexb, s); register_ioport_write (s->port + 0x4, 1, 2, mixer_write_indexw, s); + isa_init_ioport(dev, s->port + 0x4); register_ioport_read (s->port + 0x5, 1, 1, mixer_read, s); register_ioport_write (s->port + 0x5, 1, 1, mixer_write_datab, s); + isa_init_ioport(dev, s->port + 0x5); DMA_register_channel (s->hdma, SB_read_DMA, s); DMA_register_channel (s->dma, SB_read_DMA, s); diff -Nru qemu-kvm-0.12.5+noroms/hw/sbi.c qemu-kvm-0.14.1/hw/sbi.c --- qemu-kvm-0.12.5+noroms/hw/sbi.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/sbi.c 2011-05-11 13:29:46.000000000 +0000 @@ -125,7 +125,8 @@ sysbus_init_irq(dev, &s->cpu_irqs[i]); } - sbi_io_memory = cpu_register_io_memory(sbi_mem_read, sbi_mem_write, s); + sbi_io_memory = cpu_register_io_memory(sbi_mem_read, sbi_mem_write, s, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, SBI_SIZE, sbi_io_memory); return 0; diff -Nru qemu-kvm-0.12.5+noroms/hw/scsi-bus.c qemu-kvm-0.14.1/hw/scsi-bus.c --- qemu-kvm-0.12.5+noroms/hw/scsi-bus.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/scsi-bus.c 2011-05-11 13:29:46.000000000 +0000 @@ -1,13 +1,16 @@ #include "hw.h" -#include "sysemu.h" +#include "qemu-error.h" #include "scsi.h" #include "scsi-defs.h" -#include "block.h" #include "qdev.h" +#include "blockdev.h" + +static char *scsibus_get_fw_dev_path(DeviceState *dev); static struct BusInfo scsi_bus_info = { .name = "SCSI", .size = sizeof(SCSIBus), + .get_fw_dev_path = scsibus_get_fw_dev_path, .props = (Property[]) { DEFINE_PROP_UINT32("scsi-id", SCSIDevice, id, -1), DEFINE_PROP_END_OF_LIST(), @@ -41,7 +44,7 @@ } } if (dev->id >= bus->ndev) { - qemu_error("bad scsi device id: %d\n", dev->id); + error_report("bad scsi device id: %d", dev->id); goto err; } @@ -84,43 +87,47 @@ } /* handle legacy '-drive if=scsi,...' cmd line args */ -/* FIXME callers should check for failure, but don't */ -SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, DriveInfo *dinfo, int unit) +SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockDriverState *bdrv, + int unit, bool removable) { const char *driver; DeviceState *dev; - driver = bdrv_is_sg(dinfo->bdrv) ? "scsi-generic" : "scsi-disk"; + driver = bdrv_is_sg(bdrv) ? "scsi-generic" : "scsi-disk"; dev = qdev_create(&bus->qbus, driver); qdev_prop_set_uint32(dev, "scsi-id", unit); - qdev_prop_set_drive(dev, "drive", dinfo); + if (qdev_prop_exists(dev, "removable")) { + qdev_prop_set_bit(dev, "removable", removable); + } + if (qdev_prop_set_drive(dev, "drive", bdrv) < 0) { + qdev_free(dev); + return NULL; + } if (qdev_init(dev) < 0) return NULL; return DO_UPCAST(SCSIDevice, qdev, dev); } -void scsi_bus_legacy_handle_cmdline(SCSIBus *bus) +int scsi_bus_legacy_handle_cmdline(SCSIBus *bus) { + Location loc; DriveInfo *dinfo; - int unit; + int res = 0, unit; - for (unit = 0; unit < MAX_SCSI_DEVS; unit++) { + loc_push_none(&loc); + for (unit = 0; unit < bus->ndev; unit++) { dinfo = drive_get(IF_SCSI, bus->busnr, unit); if (dinfo == NULL) { continue; } - scsi_bus_legacy_add_drive(bus, dinfo, unit); + qemu_opts_loc_restore(dinfo->opts); + if (!scsi_bus_legacy_add_drive(bus, dinfo->bdrv, unit, false)) { + res = -1; + break; + } } -} - -void scsi_dev_clear_sense(SCSIDevice *dev) -{ - memset(&dev->sense, 0, sizeof(dev->sense)); -} - -void scsi_dev_set_sense(SCSIDevice *dev, uint8_t key) -{ - dev->sense.key = key; + loc_pop(&loc); + return res; } SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, uint32_t lun) @@ -133,6 +140,7 @@ req->tag = tag; req->lun = lun; req->status = -1; + req->enqueued = true; QTAILQ_INSERT_TAIL(&d->requests, req, next); return req; } @@ -149,9 +157,17 @@ return NULL; } +static void scsi_req_dequeue(SCSIRequest *req) +{ + if (req->enqueued) { + QTAILQ_REMOVE(&req->dev->requests, req, next); + req->enqueued = false; + } +} + void scsi_req_free(SCSIRequest *req) { - QTAILQ_REMOVE(&req->dev->requests, req, next); + scsi_req_dequeue(req); qemu_free(req); } @@ -189,6 +205,8 @@ case SEEK_6: case WRITE_FILEMARKS: case SPACE: + case RESERVE: + case RELEASE: case ERASE: case ALLOW_MEDIUM_REMOVAL: case VERIFY: @@ -243,6 +261,13 @@ case INQUIRY: req->cmd.xfer = cmd[4] | (cmd[3] << 8); break; + case MAINTENANCE_OUT: + case MAINTENANCE_IN: + if (req->dev->type == TYPE_ROM) { + /* GPCMD_REPORT_KEY and GPCMD_SEND_KEY from multi media commands */ + req->cmd.xfer = cmd[9] | (cmd[8] << 8); + } + break; } return 0; } @@ -293,7 +318,6 @@ case WRITE_BUFFER: case FORMAT_UNIT: case REASSIGN_BLOCKS: - case RESERVE: case SEARCH_EQUAL: case SEARCH_HIGH: case SEARCH_LOW: @@ -307,6 +331,8 @@ case MEDIUM_SCAN: case SEND_VOLUME_TAG: case WRITE_LONG_2: + case PERSISTENT_RESERVE_OUT: + case MAINTENANCE_OUT: req->cmd.mode = SCSI_XFER_TO_DEV; break; default: @@ -374,6 +400,7 @@ static const char *names[] = { [ TEST_UNIT_READY ] = "TEST_UNIT_READY", [ REZERO_UNIT ] = "REZERO_UNIT", + /* REWIND and REZERO_UNIT use the same operation code */ [ REQUEST_SENSE ] = "REQUEST_SENSE", [ FORMAT_UNIT ] = "FORMAT_UNIT", [ READ_BLOCK_LIMITS ] = "READ_BLOCK_LIMITS", @@ -386,6 +413,8 @@ [ SPACE ] = "SPACE", [ INQUIRY ] = "INQUIRY", [ RECOVER_BUFFERED_DATA ] = "RECOVER_BUFFERED_DATA", + [ MAINTENANCE_IN ] = "MAINTENANCE_IN", + [ MAINTENANCE_OUT ] = "MAINTENANCE_OUT", [ MODE_SELECT ] = "MODE_SELECT", [ RESERVE ] = "RESERVE", [ RELEASE ] = "RELEASE", @@ -409,7 +438,7 @@ [ SEARCH_LOW ] = "SEARCH_LOW", [ SET_LIMITS ] = "SET_LIMITS", [ PRE_FETCH ] = "PRE_FETCH", - [ READ_POSITION ] = "READ_POSITION", + /* READ_POSITION and PRE_FETCH use the same operation code */ [ SYNCHRONIZE_CACHE ] = "SYNCHRONIZE_CACHE", [ LOCK_UNLOCK_CACHE ] = "LOCK_UNLOCK_CACHE", [ READ_DEFECT_DATA ] = "READ_DEFECT_DATA", @@ -443,7 +472,6 @@ [ SEND_VOLUME_TAG ] = "SEND_VOLUME_TAG", [ WRITE_LONG_2 ] = "WRITE_LONG_2", - [ REWIND ] = "REWIND", [ REPORT_DENSITY_SUPPORT ] = "REPORT_DENSITY_SUPPORT", [ GET_CONFIGURATION ] = "GET_CONFIGURATION", [ READ_16 ] = "READ_16", @@ -492,7 +520,28 @@ void scsi_req_complete(SCSIRequest *req) { assert(req->status != -1); + scsi_req_dequeue(req); req->bus->complete(req->bus, SCSI_REASON_DONE, req->tag, req->status); } + +static char *scsibus_get_fw_dev_path(DeviceState *dev) +{ + SCSIDevice *d = (SCSIDevice*)dev; + SCSIBus *bus = scsi_bus_from_device(d); + char path[100]; + int i; + + for (i = 0; i < bus->ndev; i++) { + if (bus->devs[i] == d) { + break; + } + } + + assert(i != bus->ndev); + + snprintf(path, sizeof(path), "%s@%x", qdev_fw_name(dev), i); + + return strdup(path); +} diff -Nru qemu-kvm-0.12.5+noroms/hw/scsi-defs.h qemu-kvm-0.14.1/hw/scsi-defs.h --- qemu-kvm-0.12.5+noroms/hw/scsi-defs.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/scsi-defs.h 2011-05-11 13:29:46.000000000 +0000 @@ -12,9 +12,8 @@ Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public - License along with the GNU C Library; if not, write to the Free - Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307 USA. */ + License along with this library; if not, see . +*/ /* * This header file contains public constants and structures used by @@ -85,6 +84,9 @@ #define MODE_SENSE_10 0x5a #define PERSISTENT_RESERVE_IN 0x5e #define PERSISTENT_RESERVE_OUT 0x5f +#define WRITE_SAME_16 0x93 +#define MAINTENANCE_IN 0xa3 +#define MAINTENANCE_OUT 0xa4 #define MOVE_MEDIUM 0xa5 #define READ_12 0xa8 #define WRITE_12 0xaa @@ -110,18 +112,20 @@ #define BLANK 0xa1 /* - * Status codes + * SAM Status codes */ #define GOOD 0x00 -#define CHECK_CONDITION 0x01 -#define CONDITION_GOOD 0x02 -#define BUSY 0x04 -#define INTERMEDIATE_GOOD 0x08 -#define INTERMEDIATE_C_GOOD 0x0a -#define RESERVATION_CONFLICT 0x0c -#define COMMAND_TERMINATED 0x11 -#define QUEUE_FULL 0x14 +#define CHECK_CONDITION 0x02 +#define CONDITION_GOOD 0x04 +#define BUSY 0x08 +#define INTERMEDIATE_GOOD 0x10 +#define INTERMEDIATE_C_GOOD 0x14 +#define RESERVATION_CONFLICT 0x18 +#define COMMAND_TERMINATED 0x22 +#define TASK_SET_FULL 0x28 +#define ACA_ACTIVE 0x30 +#define TASK_ABORTED 0x40 #define STATUS_MASK 0x3e diff -Nru qemu-kvm-0.12.5+noroms/hw/scsi-disk.c qemu-kvm-0.14.1/hw/scsi-disk.c --- qemu-kvm-0.12.5+noroms/hw/scsi-disk.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/scsi-disk.c 2011-05-11 13:29:46.000000000 +0000 @@ -19,8 +19,6 @@ * the host adapter emulator. */ -#include -#include //#define DEBUG_SCSI #ifdef DEBUG_SCSI @@ -34,17 +32,27 @@ do { fprintf(stderr, "scsi-disk: " fmt , ## __VA_ARGS__); } while (0) #include "qemu-common.h" -#include "block.h" +#include "qemu-error.h" #include "scsi.h" #include "scsi-defs.h" +#include "sysemu.h" +#include "blockdev.h" #define SCSI_DMA_BUF_SIZE 131072 #define SCSI_MAX_INQUIRY_LEN 256 -#define SCSI_REQ_STATUS_RETRY 0x01 +#define SCSI_REQ_STATUS_RETRY 0x01 +#define SCSI_REQ_STATUS_RETRY_TYPE_MASK 0x06 +#define SCSI_REQ_STATUS_RETRY_READ 0x00 +#define SCSI_REQ_STATUS_RETRY_WRITE 0x02 +#define SCSI_REQ_STATUS_RETRY_FLUSH 0x04 typedef struct SCSIDiskState SCSIDiskState; +typedef struct SCSISense { + uint8_t key; +} SCSISense; + typedef struct SCSIDiskReq { SCSIRequest req; /* ??? We should probably keep track of whether the data transfer is @@ -60,22 +68,30 @@ struct SCSIDiskState { SCSIDevice qdev; + BlockDriverState *bs; /* The qemu block layer uses a fixed 512 byte sector size. This is the number of 512 byte blocks in a single scsi sector. */ int cluster_size; + uint32_t removable; uint64_t max_lba; QEMUBH *bh; char *version; + char *serial; + SCSISense sense; }; -static SCSIDiskReq *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun) +static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type); +static int scsi_disk_emulate_command(SCSIDiskReq *r, uint8_t *outbuf); + +static SCSIDiskReq *scsi_new_request(SCSIDiskState *s, uint32_t tag, + uint32_t lun) { SCSIRequest *req; SCSIDiskReq *r; - req = scsi_req_alloc(sizeof(SCSIDiskReq), d, tag, lun); + req = scsi_req_alloc(sizeof(SCSIDiskReq), &s->qdev, tag, lun); r = DO_UPCAST(SCSIDiskReq, req, req); - r->iov.iov_base = qemu_memalign(512, SCSI_DMA_BUF_SIZE); + r->iov.iov_base = qemu_blockalign(s->bs, SCSI_DMA_BUF_SIZE); return r; } @@ -90,10 +106,22 @@ return DO_UPCAST(SCSIDiskReq, req, scsi_req_find(&s->qdev, tag)); } -static void scsi_req_set_status(SCSIRequest *req, int status, int sense_code) +static void scsi_disk_clear_sense(SCSIDiskState *s) +{ + memset(&s->sense, 0, sizeof(s->sense)); +} + +static void scsi_disk_set_sense(SCSIDiskState *s, uint8_t key) { - req->status = status; - scsi_dev_set_sense(req->dev, sense_code); + s->sense.key = key; +} + +static void scsi_req_set_status(SCSIDiskReq *r, int status, int sense_code) +{ + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); + + r->req.status = status; + scsi_disk_set_sense(s, sense_code); } /* Helper function for command completion. */ @@ -101,7 +129,7 @@ { DPRINTF("Command complete tag=0x%x status=%d sense=%d\n", r->req.tag, status, sense); - scsi_req_set_status(&r->req, status, sense); + scsi_req_set_status(r, status, sense); scsi_req_complete(&r->req); scsi_remove_request(r); } @@ -124,34 +152,32 @@ static void scsi_read_complete(void * opaque, int ret) { SCSIDiskReq *r = (SCSIDiskReq *)opaque; + int n; + + r->req.aiocb = NULL; if (ret) { - DPRINTF("IO error\n"); - r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, 0); - scsi_command_complete(r, CHECK_CONDITION, NO_SENSE); - return; + if (scsi_handle_rw_error(r, -ret, SCSI_REQ_STATUS_RETRY_READ)) { + return; + } } - DPRINTF("Data ready tag=0x%x len=%" PRId64 "\n", r->req.tag, r->iov.iov_len); + DPRINTF("Data ready tag=0x%x len=%zd\n", r->req.tag, r->iov.iov_len); + + n = r->iov.iov_len / 512; + r->sector += n; + r->sector_count -= n; r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, r->iov.iov_len); } -/* Read more data from scsi device into buffer. */ -static void scsi_read_data(SCSIDevice *d, uint32_t tag) + +static void scsi_read_request(SCSIDiskReq *r) { - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d); - SCSIDiskReq *r; + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); uint32_t n; - r = scsi_find_request(s, tag); - if (!r) { - BADF("Bad read tag 0x%x\n", tag); - /* ??? This is the wrong error. */ - scsi_command_complete(r, CHECK_CONDITION, HARDWARE_ERROR); - return; - } if (r->sector_count == (uint32_t)-1) { - DPRINTF("Read buf_len=%" PRId64 "\n", r->iov.iov_len); + DPRINTF("Read buf_len=%zd\n", r->iov.iov_len); r->sector_count = 0; r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, r->iov.iov_len); return; @@ -162,36 +188,65 @@ return; } + /* No data transfer may already be in progress */ + assert(r->req.aiocb == NULL); + n = r->sector_count; if (n > SCSI_DMA_BUF_SIZE / 512) n = SCSI_DMA_BUF_SIZE / 512; r->iov.iov_len = n * 512; qemu_iovec_init_external(&r->qiov, &r->iov, 1); - r->req.aiocb = bdrv_aio_readv(s->qdev.dinfo->bdrv, r->sector, &r->qiov, n, + r->req.aiocb = bdrv_aio_readv(s->bs, r->sector, &r->qiov, n, scsi_read_complete, r); - if (r->req.aiocb == NULL) + if (r->req.aiocb == NULL) { + scsi_read_complete(r, -EIO); + } +} + +/* Read more data from scsi device into buffer. */ +static void scsi_read_data(SCSIDevice *d, uint32_t tag) +{ + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d); + SCSIDiskReq *r; + + r = scsi_find_request(s, tag); + if (!r) { + BADF("Bad read tag 0x%x\n", tag); + /* ??? This is the wrong error. */ scsi_command_complete(r, CHECK_CONDITION, HARDWARE_ERROR); - r->sector += n; - r->sector_count -= n; + return; + } + + scsi_read_request(r); } -static int scsi_handle_write_error(SCSIDiskReq *r, int error) +static int scsi_handle_rw_error(SCSIDiskReq *r, int error, int type) { + int is_read = (type == SCSI_REQ_STATUS_RETRY_READ); SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); - BlockInterfaceErrorAction action = - drive_get_on_error(s->qdev.dinfo->bdrv, 0); + BlockErrorAction action = bdrv_get_on_error(s->bs, is_read); - if (action == BLOCK_ERR_IGNORE) + if (action == BLOCK_ERR_IGNORE) { + bdrv_mon_event(s->bs, BDRV_ACTION_IGNORE, is_read); return 0; + } if ((error == ENOSPC && action == BLOCK_ERR_STOP_ENOSPC) || action == BLOCK_ERR_STOP_ANY) { - r->status |= SCSI_REQ_STATUS_RETRY; + + type &= SCSI_REQ_STATUS_RETRY_TYPE_MASK; + r->status |= SCSI_REQ_STATUS_RETRY | type; + + bdrv_mon_event(s->bs, BDRV_ACTION_STOP, is_read); vm_stop(0); } else { + if (type == SCSI_REQ_STATUS_RETRY_READ) { + r->req.bus->complete(r->req.bus, SCSI_REASON_DATA, r->req.tag, 0); + } scsi_command_complete(r, CHECK_CONDITION, HARDWARE_ERROR); + bdrv_mon_event(s->bs, BDRV_ACTION_REPORT, is_read); } return 1; @@ -206,8 +261,9 @@ r->req.aiocb = NULL; if (ret) { - if (scsi_handle_write_error(r, -ret)) + if (scsi_handle_rw_error(r, -ret, SCSI_REQ_STATUS_RETRY_WRITE)) { return; + } } n = r->iov.iov_len / 512; @@ -231,14 +287,17 @@ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); uint32_t n; + /* No data transfer may already be in progress */ + assert(r->req.aiocb == NULL); + n = r->iov.iov_len / 512; if (n) { qemu_iovec_init_external(&r->qiov, &r->iov, 1); - r->req.aiocb = bdrv_aio_writev(s->qdev.dinfo->bdrv, r->sector, &r->qiov, n, + r->req.aiocb = bdrv_aio_writev(s->bs, r->sector, &r->qiov, n, scsi_write_complete, r); - if (r->req.aiocb == NULL) - scsi_command_complete(r, CHECK_CONDITION, - HARDWARE_ERROR); + if (r->req.aiocb == NULL) { + scsi_write_complete(r, -EIO); + } } else { /* Invoke completion routine to fetch data from host. */ scsi_write_complete(r, 0); @@ -260,9 +319,6 @@ return 1; } - if (r->req.aiocb) - BADF("Data transfer already in progress\n"); - scsi_write_request(r); return 0; @@ -280,8 +336,25 @@ QTAILQ_FOREACH(req, &s->qdev.requests, next) { r = DO_UPCAST(SCSIDiskReq, req, req); if (r->status & SCSI_REQ_STATUS_RETRY) { - r->status &= ~SCSI_REQ_STATUS_RETRY; - scsi_write_request(r); + int status = r->status; + int ret; + + r->status &= + ~(SCSI_REQ_STATUS_RETRY | SCSI_REQ_STATUS_RETRY_TYPE_MASK); + + switch (status & SCSI_REQ_STATUS_RETRY_TYPE_MASK) { + case SCSI_REQ_STATUS_RETRY_READ: + scsi_read_request(r); + break; + case SCSI_REQ_STATUS_RETRY_WRITE: + scsi_write_request(r); + break; + case SCSI_REQ_STATUS_RETRY_FLUSH: + ret = scsi_disk_emulate_command(r, r->iov.iov_base); + if (ret == 0) { + scsi_command_complete(r, GOOD, NO_SENSE); + } + } } } } @@ -315,7 +388,6 @@ static int scsi_disk_emulate_inquiry(SCSIRequest *req, uint8_t *outbuf) { - BlockDriverState *bdrv = req->dev->dinfo->bdrv; SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev); int buflen = 0; @@ -334,7 +406,7 @@ return -1; } - if (bdrv_get_type_hint(bdrv) == BDRV_TYPE_CDROM) { + if (bdrv_get_type_hint(s->bs) == BDRV_TYPE_CDROM) { outbuf[buflen++] = 5; } else { outbuf[buflen++] = 0; @@ -344,19 +416,24 @@ switch (page_code) { case 0x00: /* Supported page codes, mandatory */ + { + int pages; DPRINTF("Inquiry EVPD[Supported pages] " "buffer size %zd\n", req->cmd.xfer); - outbuf[buflen++] = 3; // number of pages + pages = buflen++; outbuf[buflen++] = 0x00; // list of supported pages (this page) outbuf[buflen++] = 0x80; // unit serial number outbuf[buflen++] = 0x83; // device identification + if (bdrv_get_type_hint(s->bs) != BDRV_TYPE_CDROM) { + outbuf[buflen++] = 0xb0; // block limits + outbuf[buflen++] = 0xb2; // thin provisioning + } + outbuf[pages] = buflen - pages - 1; // number of pages break; - + } case 0x80: /* Device serial number, optional */ { - const char *serial = req->dev->dinfo->serial ? - req->dev->dinfo->serial : "0"; - int l = strlen(serial); + int l = strlen(s->serial); if (l > req->cmd.xfer) l = req->cmd.xfer; @@ -366,7 +443,7 @@ DPRINTF("Inquiry EVPD[Serial number] " "buffer size %zd\n", req->cmd.xfer); outbuf[buflen++] = l; - memcpy(outbuf+buflen, serial, l); + memcpy(outbuf+buflen, s->serial, l); buflen += l; break; } @@ -374,23 +451,68 @@ case 0x83: /* Device identification page, mandatory */ { int max_len = 255 - 8; - int id_len = strlen(bdrv_get_device_name(bdrv)); + int id_len = strlen(bdrv_get_device_name(s->bs)); if (id_len > max_len) id_len = max_len; DPRINTF("Inquiry EVPD[Device identification] " "buffer size %zd\n", req->cmd.xfer); - outbuf[buflen++] = 3 + id_len; + outbuf[buflen++] = 4 + id_len; outbuf[buflen++] = 0x2; // ASCII outbuf[buflen++] = 0; // not officially assigned outbuf[buflen++] = 0; // reserved outbuf[buflen++] = id_len; // length of data following - memcpy(outbuf+buflen, bdrv_get_device_name(bdrv), id_len); + memcpy(outbuf+buflen, bdrv_get_device_name(s->bs), id_len); buflen += id_len; break; } + case 0xb0: /* block limits */ + { + unsigned int unmap_sectors = + s->qdev.conf.discard_granularity / s->qdev.blocksize; + unsigned int min_io_size = + s->qdev.conf.min_io_size / s->qdev.blocksize; + unsigned int opt_io_size = + s->qdev.conf.opt_io_size / s->qdev.blocksize; + + if (bdrv_get_type_hint(s->bs) == BDRV_TYPE_CDROM) { + DPRINTF("Inquiry (EVPD[%02X] not supported for CDROM\n", + page_code); + return -1; + } + /* required VPD size with unmap support */ + outbuf[3] = buflen = 0x3c; + + memset(outbuf + 4, 0, buflen - 4); + + /* optimal transfer length granularity */ + outbuf[6] = (min_io_size >> 8) & 0xff; + outbuf[7] = min_io_size & 0xff; + + /* optimal transfer length */ + outbuf[12] = (opt_io_size >> 24) & 0xff; + outbuf[13] = (opt_io_size >> 16) & 0xff; + outbuf[14] = (opt_io_size >> 8) & 0xff; + outbuf[15] = opt_io_size & 0xff; + + /* optimal unmap granularity */ + outbuf[28] = (unmap_sectors >> 24) & 0xff; + outbuf[29] = (unmap_sectors >> 16) & 0xff; + outbuf[30] = (unmap_sectors >> 8) & 0xff; + outbuf[31] = unmap_sectors & 0xff; + break; + } + case 0xb2: /* thin provisioning */ + { + outbuf[3] = buflen = 8; + outbuf[4] = 0; + outbuf[5] = 0x40; /* write same with unmap supported */ + outbuf[6] = 0; + outbuf[7] = 0; + break; + } default: BADF("Error: unsupported Inquiry (EVPD[%02X]) " "buffer size %zd\n", page_code, req->cmd.xfer); @@ -425,21 +547,25 @@ return buflen; } - if (bdrv_get_type_hint(bdrv) == BDRV_TYPE_CDROM) { + if (bdrv_get_type_hint(s->bs) == BDRV_TYPE_CDROM) { outbuf[0] = 5; outbuf[1] = 0x80; memcpy(&outbuf[16], "QEMU CD-ROM ", 16); } else { outbuf[0] = 0; + outbuf[1] = s->removable ? 0x80 : 0; memcpy(&outbuf[16], "QEMU HARDDISK ", 16); } memcpy(&outbuf[8], "QEMU ", 8); memset(&outbuf[32], 0, 4); - memcpy(&outbuf[32], s->version ? s->version : QEMU_VERSION, - MIN(4, strlen(s->version ? s->version : QEMU_VERSION))); - /* Identify device as SCSI-3 rev 1. - Some later commands are also implemented. */ - outbuf[2] = 3; + memcpy(&outbuf[32], s->version, MIN(4, strlen(s->version))); + /* + * We claim conformance to SPC-3, which is required for guests + * to ask for modern features like READ CAPACITY(16) or the + * block characteristics VPD page by default. Not all of SPC-3 + * is actually implemented, but we're good enough. + */ + outbuf[2] = 5; outbuf[3] = 2; /* Format 2 */ if (buflen > 36) { @@ -455,16 +581,26 @@ return buflen; } -static int mode_sense_page(SCSIRequest *req, int page, uint8_t *p) +static int mode_sense_page(SCSIRequest *req, int page, uint8_t *p, + int page_control) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev); - BlockDriverState *bdrv = req->dev->dinfo->bdrv; + BlockDriverState *bdrv = s->bs; int cylinders, heads, secs; + /* + * If Changeable Values are requested, a mask denoting those mode parameters + * that are changeable shall be returned. As we currently don't support + * parameter changes via MODE_SELECT all bits are returned set to zero. + * The buffer was already menset to zero by the caller of this function. + */ switch (page) { case 4: /* Rigid disk device geometry page. */ p[0] = 4; p[1] = 0x16; + if (page_control == 1) { /* Changeable Values */ + return p[1] + 2; + } /* if a geometry hint is available, use it */ bdrv_get_geometry_hint(bdrv, &cylinders, &heads, &secs); p[2] = (cylinders >> 16) & 0xff; @@ -489,11 +625,14 @@ /* Medium rotation rate [rpm], 5400 rpm */ p[20] = (5400 >> 8) & 0xff; p[21] = 5400 & 0xff; - return 0x16; + return p[1] + 2; case 5: /* Flexible disk device geometry page. */ p[0] = 5; p[1] = 0x1e; + if (page_control == 1) { /* Changeable Values */ + return p[1] + 2; + } /* Transfer rate [kbit/s], 5Mbit/s */ p[2] = 5000 >> 8; p[3] = 5000 & 0xff; @@ -525,21 +664,27 @@ /* Medium rotation rate [rpm], 5400 rpm */ p[28] = (5400 >> 8) & 0xff; p[29] = 5400 & 0xff; - return 0x1e; + return p[1] + 2; case 8: /* Caching page. */ p[0] = 8; p[1] = 0x12; - if (bdrv_enable_write_cache(s->qdev.dinfo->bdrv)) { + if (page_control == 1) { /* Changeable Values */ + return p[1] + 2; + } + if (bdrv_enable_write_cache(s->bs)) { p[2] = 4; /* WCE */ } - return 20; + return p[1] + 2; case 0x2a: /* CD Capabilities and Mechanical Status page. */ if (bdrv_get_type_hint(bdrv) != BDRV_TYPE_CDROM) return 0; p[0] = 0x2a; p[1] = 0x14; + if (page_control == 1) { /* Changeable Values */ + return p[1] + 2; + } p[2] = 3; // CD-R & CD-RW read p[3] = 0; // Writing not supported p[4] = 0x7f; /* Audio, composite, digital out, @@ -547,7 +692,7 @@ p[5] = 0xff; /* CD DA, DA accurate, RW supported, RW corrected, C2 errors, ISRC, UPC, Bar code */ - p[6] = 0x2d | (bdrv_is_locked(s->qdev.dinfo->bdrv)? 2 : 0); + p[6] = 0x2d | (bdrv_is_locked(s->bs)? 2 : 0); /* Locking supported, jumper present, eject, tray */ p[7] = 0; /* no volume & mute control, no changer */ @@ -563,7 +708,7 @@ p[19] = (16 * 176) & 0xff; p[20] = (16 * 176) >> 8; // 16x write speed current p[21] = (16 * 176) & 0xff; - return 22; + return p[1] + 2; default: return 0; @@ -573,32 +718,47 @@ static int scsi_disk_emulate_mode_sense(SCSIRequest *req, uint8_t *outbuf) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev); - BlockDriverState *bdrv = req->dev->dinfo->bdrv; uint64_t nb_sectors; - int page, dbd, buflen; + int page, dbd, buflen, page_control; uint8_t *p; + uint8_t dev_specific_param; dbd = req->cmd.buf[1] & 0x8; page = req->cmd.buf[2] & 0x3f; - DPRINTF("Mode Sense (page %d, len %zd)\n", page, req->cmd.xfer); + page_control = (req->cmd.buf[2] & 0xc0) >> 6; + DPRINTF("Mode Sense(%d) (page %d, xfer %zd, page_control %d)\n", + (req->cmd.buf[0] == MODE_SENSE) ? 6 : 10, page, req->cmd.xfer, page_control); memset(outbuf, 0, req->cmd.xfer); p = outbuf; - p[1] = 0; /* Default media type. */ - p[3] = 0; /* Block descriptor length. */ - if (bdrv_get_type_hint(bdrv) == BDRV_TYPE_CDROM || - bdrv_is_read_only(bdrv)) { - p[2] = 0x80; /* Readonly. */ - } - p += 4; - - bdrv_get_geometry(bdrv, &nb_sectors); - if ((~dbd) & nb_sectors) { - outbuf[3] = 8; /* Block descriptor length */ + if (bdrv_is_read_only(s->bs)) { + dev_specific_param = 0x80; /* Readonly. */ + } else { + dev_specific_param = 0x00; + } + + if (req->cmd.buf[0] == MODE_SENSE) { + p[1] = 0; /* Default media type. */ + p[2] = dev_specific_param; + p[3] = 0; /* Block descriptor length. */ + p += 4; + } else { /* MODE_SENSE_10 */ + p[2] = 0; /* Default media type. */ + p[3] = dev_specific_param; + p[6] = p[7] = 0; /* Block descriptor length. */ + p += 8; + } + + bdrv_get_geometry(s->bs, &nb_sectors); + if (!dbd && nb_sectors) { + if (req->cmd.buf[0] == MODE_SENSE) { + outbuf[3] = 8; /* Block descriptor length */ + } else { /* MODE_SENSE_10 */ + outbuf[7] = 8; /* Block descriptor length */ + } nb_sectors /= s->cluster_size; - nb_sectors--; if (nb_sectors > 0xffffff) - nb_sectors = 0xffffff; + nb_sectors = 0; p[0] = 0; /* media density code */ p[1] = (nb_sectors >> 16) & 0xff; p[2] = (nb_sectors >> 8) & 0xff; @@ -610,21 +770,37 @@ p += 8; } + if (page_control == 3) { /* Saved Values */ + return -1; /* ILLEGAL_REQUEST */ + } + switch (page) { case 0x04: case 0x05: case 0x08: case 0x2a: - p += mode_sense_page(req, page, p); + p += mode_sense_page(req, page, p, page_control); break; case 0x3f: - p += mode_sense_page(req, 0x08, p); - p += mode_sense_page(req, 0x2a, p); + p += mode_sense_page(req, 0x08, p, page_control); + p += mode_sense_page(req, 0x2a, p, page_control); break; + default: + return -1; /* ILLEGAL_REQUEST */ } buflen = p - outbuf; - outbuf[0] = buflen - 4; + /* + * The mode data length field specifies the length in bytes of the + * following data that is available to be transferred. The mode data + * length does not include itself. + */ + if (req->cmd.buf[0] == MODE_SENSE) { + outbuf[0] = buflen - 1; + } else { /* MODE_SENSE_10 */ + outbuf[0] = ((buflen - 2) >> 8) & 0xff; + outbuf[1] = (buflen - 2) & 0xff; + } if (buflen > req->cmd.xfer) buflen = req->cmd.xfer; return buflen; @@ -633,14 +809,13 @@ static int scsi_disk_emulate_read_toc(SCSIRequest *req, uint8_t *outbuf) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev); - BlockDriverState *bdrv = req->dev->dinfo->bdrv; int start_track, format, msf, toclen; uint64_t nb_sectors; msf = req->cmd.buf[1] & 2; format = req->cmd.buf[2] & 0xf; start_track = req->cmd.buf[6]; - bdrv_get_geometry(bdrv, &nb_sectors); + bdrv_get_geometry(s->bs, &nb_sectors); DPRINTF("Read TOC (track %d format %d msf %d)\n", start_track, format, msf >> 1); nb_sectors /= s->cluster_size; switch (format) { @@ -666,16 +841,17 @@ return toclen; } -static int scsi_disk_emulate_command(SCSIRequest *req, uint8_t *outbuf) +static int scsi_disk_emulate_command(SCSIDiskReq *r, uint8_t *outbuf) { + SCSIRequest *req = &r->req; SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev); - BlockDriverState *bdrv = req->dev->dinfo->bdrv; uint64_t nb_sectors; int buflen = 0; + int ret; switch (req->cmd.buf[0]) { case TEST_UNIT_READY: - if (!bdrv_is_inserted(bdrv)) + if (!bdrv_is_inserted(s->bs)) goto not_ready; break; case REQUEST_SENSE: @@ -683,7 +859,7 @@ goto illegal_request; memset(outbuf, 0, 4); buflen = 4; - if (req->dev->sense.key == NOT_READY && req->cmd.xfer >= 18) { + if (s->sense.key == NOT_READY && req->cmd.xfer >= 18) { memset(outbuf, 0, 18); buflen = 18; outbuf[7] = 10; @@ -693,8 +869,8 @@ } outbuf[0] = 0xf0; outbuf[1] = 0; - outbuf[2] = req->dev->sense.key; - scsi_dev_clear_sense(req->dev); + outbuf[2] = s->sense.key; + scsi_disk_clear_sense(s); break; case INQUIRY: buflen = scsi_disk_emulate_inquiry(req, outbuf); @@ -729,18 +905,18 @@ goto illegal_request; break; case START_STOP: - if (bdrv_get_type_hint(bdrv) == BDRV_TYPE_CDROM && (req->cmd.buf[4] & 2)) { + if (bdrv_get_type_hint(s->bs) == BDRV_TYPE_CDROM && (req->cmd.buf[4] & 2)) { /* load/eject medium */ - bdrv_eject(bdrv, !(req->cmd.buf[4] & 1)); + bdrv_eject(s->bs, !(req->cmd.buf[4] & 1)); } break; case ALLOW_MEDIUM_REMOVAL: - bdrv_set_locked(bdrv, req->cmd.buf[4] & 1); + bdrv_set_locked(s->bs, req->cmd.buf[4] & 1); break; case READ_CAPACITY: /* The normal LEN field for this command is zero. */ memset(outbuf, 0, 8); - bdrv_get_geometry(bdrv, &nb_sectors); + bdrv_get_geometry(s->bs, &nb_sectors); if (!nb_sectors) goto not_ready; nb_sectors /= s->cluster_size; @@ -762,7 +938,12 @@ buflen = 8; break; case SYNCHRONIZE_CACHE: - bdrv_flush(bdrv); + ret = bdrv_flush(s->bs); + if (ret < 0) { + if (scsi_handle_rw_error(r, -ret, SCSI_REQ_STATUS_RETRY_FLUSH)) { + return -1; + } + } break; case GET_CONFIGURATION: memset(outbuf, 0, 8); @@ -776,7 +957,7 @@ if ((req->cmd.buf[1] & 31) == 0x10) { DPRINTF("SAI READ CAPACITY(16)\n"); memset(outbuf, 0, req->cmd.xfer); - bdrv_get_geometry(bdrv, &nb_sectors); + bdrv_get_geometry(s->bs, &nb_sectors); if (!nb_sectors) goto not_ready; nb_sectors /= s->cluster_size; @@ -796,6 +977,14 @@ outbuf[9] = 0; outbuf[10] = s->cluster_size * 2; outbuf[11] = 0; + outbuf[12] = 0; + outbuf[13] = get_physical_block_exp(&s->qdev.conf); + + /* set TPE bit if the format supports discard */ + if (s->qdev.conf.discard_granularity) { + outbuf[14] = 0x80; + } + /* Protection, exponent and lowest lba field left blank. */ buflen = req->cmd.xfer; break; @@ -811,19 +1000,25 @@ break; case VERIFY: break; + case REZERO_UNIT: + DPRINTF("Rezero Unit\n"); + if (!bdrv_is_inserted(s->bs)) { + goto not_ready; + } + break; default: goto illegal_request; } - scsi_req_set_status(req, GOOD, NO_SENSE); + scsi_req_set_status(r, GOOD, NO_SENSE); return buflen; not_ready: - scsi_req_set_status(req, CHECK_CONDITION, NOT_READY); - return 0; + scsi_command_complete(r, CHECK_CONDITION, NOT_READY); + return -1; illegal_request: - scsi_req_set_status(req, CHECK_CONDITION, ILLEGAL_REQUEST); - return 0; + scsi_command_complete(r, CHECK_CONDITION, ILLEGAL_REQUEST); + return -1; } /* Execute a scsi command. Returns the length of the data expected by the @@ -835,9 +1030,7 @@ uint8_t *buf, int lun) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, d); - uint64_t lba; uint32_t len; - int cmdlen; int is_write; uint8_t command; uint8_t *outbuf; @@ -852,59 +1045,25 @@ } /* ??? Tags are not unique for different luns. We only implement a single lun, so this should not matter. */ - r = scsi_new_request(d, tag, lun); + r = scsi_new_request(s, tag, lun); outbuf = (uint8_t *)r->iov.iov_base; is_write = 0; DPRINTF("Command: lun=%d tag=0x%x data=0x%02x", lun, tag, buf[0]); - switch (command >> 5) { - case 0: - lba = (uint64_t) buf[3] | ((uint64_t) buf[2] << 8) | - (((uint64_t) buf[1] & 0x1f) << 16); - len = buf[4]; - cmdlen = 6; - break; - case 1: - case 2: - lba = (uint64_t) buf[5] | ((uint64_t) buf[4] << 8) | - ((uint64_t) buf[3] << 16) | ((uint64_t) buf[2] << 24); - len = buf[8] | (buf[7] << 8); - cmdlen = 10; - break; - case 4: - lba = (uint64_t) buf[9] | ((uint64_t) buf[8] << 8) | - ((uint64_t) buf[7] << 16) | ((uint64_t) buf[6] << 24) | - ((uint64_t) buf[5] << 32) | ((uint64_t) buf[4] << 40) | - ((uint64_t) buf[3] << 48) | ((uint64_t) buf[2] << 56); - len = buf[13] | (buf[12] << 8) | (buf[11] << 16) | (buf[10] << 24); - cmdlen = 16; - break; - case 5: - lba = (uint64_t) buf[5] | ((uint64_t) buf[4] << 8) | - ((uint64_t) buf[3] << 16) | ((uint64_t) buf[2] << 24); - len = buf[9] | (buf[8] << 8) | (buf[7] << 16) | (buf[6] << 24); - cmdlen = 12; - break; - default: + + if (scsi_req_parse(&r->req, buf) != 0) { BADF("Unsupported command length, command %x\n", command); goto fail; } #ifdef DEBUG_SCSI { int i; - for (i = 1; i < cmdlen; i++) { + for (i = 1; i < r->req.cmd.len; i++) { printf(" 0x%02x", buf[i]); } printf("\n"); } #endif - if (scsi_req_parse(&r->req, buf) != 0) { - BADF("Unsupported command length, command %x\n", command); - goto fail; - } - assert(r->req.cmd.len == cmdlen); - assert(r->req.cmd.lba == lba); - if (lun || buf[1] >> 5) { /* Only LUN 0 supported. */ DPRINTF("Unimplemented LUN %d\n", lun ? lun : buf[1] >> 5); @@ -930,41 +1089,96 @@ case SERVICE_ACTION_IN: case REPORT_LUNS: case VERIFY: - rc = scsi_disk_emulate_command(&r->req, outbuf); - if (rc > 0) { - r->iov.iov_len = rc; - } else { - scsi_req_complete(&r->req); - scsi_remove_request(r); + case REZERO_UNIT: + rc = scsi_disk_emulate_command(r, outbuf); + if (rc < 0) { return 0; } + + r->iov.iov_len = rc; break; case READ_6: case READ_10: case READ_12: case READ_16: - DPRINTF("Read (sector %" PRId64 ", count %d)\n", lba, len); - if (lba > s->max_lba) + len = r->req.cmd.xfer / d->blocksize; + DPRINTF("Read (sector %" PRId64 ", count %d)\n", r->req.cmd.lba, len); + if (r->req.cmd.lba > s->max_lba) goto illegal_lba; - r->sector = lba * s->cluster_size; + r->sector = r->req.cmd.lba * s->cluster_size; r->sector_count = len * s->cluster_size; break; case WRITE_6: case WRITE_10: case WRITE_12: case WRITE_16: - DPRINTF("Write (sector %" PRId64 ", count %d)\n", lba, len); - if (lba > s->max_lba) + case WRITE_VERIFY: + case WRITE_VERIFY_12: + case WRITE_VERIFY_16: + len = r->req.cmd.xfer / d->blocksize; + DPRINTF("Write %s(sector %" PRId64 ", count %d)\n", + (command & 0xe) == 0xe ? "And Verify " : "", + r->req.cmd.lba, len); + if (r->req.cmd.lba > s->max_lba) goto illegal_lba; - r->sector = lba * s->cluster_size; + r->sector = r->req.cmd.lba * s->cluster_size; r->sector_count = len * s->cluster_size; is_write = 1; break; + case MODE_SELECT: + DPRINTF("Mode Select(6) (len %lu)\n", (long)r->req.cmd.xfer); + /* We don't support mode parameter changes. + Allow the mode parameter header + block descriptors only. */ + if (r->req.cmd.xfer > 12) { + goto fail; + } + break; + case MODE_SELECT_10: + DPRINTF("Mode Select(10) (len %lu)\n", (long)r->req.cmd.xfer); + /* We don't support mode parameter changes. + Allow the mode parameter header + block descriptors only. */ + if (r->req.cmd.xfer > 16) { + goto fail; + } + break; + case SEEK_6: + case SEEK_10: + DPRINTF("Seek(%d) (sector %" PRId64 ")\n", command == SEEK_6 ? 6 : 10, + r->req.cmd.lba); + if (r->req.cmd.lba > s->max_lba) { + goto illegal_lba; + } + break; + case WRITE_SAME_16: + len = r->req.cmd.xfer / d->blocksize; + + DPRINTF("WRITE SAME(16) (sector %" PRId64 ", count %d)\n", + r->req.cmd.lba, len); + + if (r->req.cmd.lba > s->max_lba) { + goto illegal_lba; + } + + /* + * We only support WRITE SAME with the unmap bit set for now. + */ + if (!(buf[1] & 0x8)) { + goto fail; + } + + rc = bdrv_discard(s->bs, r->req.cmd.lba * s->cluster_size, + len * s->cluster_size); + if (rc < 0) { + /* XXX: better error code ?*/ + goto fail; + } + + break; default: - DPRINTF("Unknown SCSI command (%2.2x)\n", buf[0]); + DPRINTF("Unknown SCSI command (%2.2x)\n", buf[0]); fail: scsi_command_complete(r, CHECK_CONDITION, ILLEGAL_REQUEST); - return 0; + return 0; illegal_lba: scsi_command_complete(r, CHECK_CONDITION, HARDWARE_ERROR); return 0; @@ -982,48 +1196,96 @@ } } -static void scsi_destroy(SCSIDevice *dev) +static void scsi_disk_purge_requests(SCSIDiskState *s) { - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); SCSIDiskReq *r; while (!QTAILQ_EMPTY(&s->qdev.requests)) { r = DO_UPCAST(SCSIDiskReq, req, QTAILQ_FIRST(&s->qdev.requests)); + if (r->req.aiocb) { + bdrv_aio_cancel(r->req.aiocb); + } scsi_remove_request(r); } - drive_uninit(s->qdev.dinfo); +} + +static void scsi_disk_reset(DeviceState *dev) +{ + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev.qdev, dev); + uint64_t nb_sectors; + + scsi_disk_purge_requests(s); + + bdrv_get_geometry(s->bs, &nb_sectors); + nb_sectors /= s->cluster_size; + if (nb_sectors) { + nb_sectors--; + } + s->max_lba = nb_sectors; +} + +static void scsi_destroy(SCSIDevice *dev) +{ + SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); + + scsi_disk_purge_requests(s); + blockdev_mark_auto_del(s->qdev.conf.bs); } static int scsi_disk_initfn(SCSIDevice *dev) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); - uint64_t nb_sectors; + int is_cd; + DriveInfo *dinfo; + + if (!s->qdev.conf.bs) { + error_report("scsi-disk: drive property not set"); + return -1; + } + s->bs = s->qdev.conf.bs; + is_cd = bdrv_get_type_hint(s->bs) == BDRV_TYPE_CDROM; + + if (!is_cd && !bdrv_is_inserted(s->bs)) { + error_report("Device needs media, but drive is empty"); + return -1; + } + + if (!s->serial) { + /* try to fall back to value set with legacy -drive serial=... */ + dinfo = drive_get_by_blockdev(s->bs); + s->serial = qemu_strdup(*dinfo->serial ? dinfo->serial : "0"); + } + + if (!s->version) { + s->version = qemu_strdup(QEMU_VERSION); + } - if (!s->qdev.dinfo || !s->qdev.dinfo->bdrv) { - qemu_error("scsi-disk: drive property not set\n"); + if (bdrv_is_sg(s->bs)) { + error_report("scsi-disk: unwanted /dev/sg*"); return -1; } - if (bdrv_get_type_hint(s->qdev.dinfo->bdrv) == BDRV_TYPE_CDROM) { - s->cluster_size = 4; + if (is_cd) { + s->qdev.blocksize = 2048; } else { - s->cluster_size = 1; + s->qdev.blocksize = s->qdev.conf.logical_block_size; } - s->qdev.blocksize = 512 * s->cluster_size; + s->cluster_size = s->qdev.blocksize / 512; + s->bs->buffer_alignment = s->qdev.blocksize; + s->qdev.type = TYPE_DISK; - bdrv_get_geometry(s->qdev.dinfo->bdrv, &nb_sectors); - nb_sectors /= s->cluster_size; - if (nb_sectors) - nb_sectors--; - s->max_lba = nb_sectors; qemu_add_vm_change_state_handler(scsi_dma_restart_cb, s); + bdrv_set_removable(s->bs, is_cd); + add_boot_device_path(s->qdev.conf.bootindex, &dev->qdev, ",0"); return 0; } static SCSIDeviceInfo scsi_disk_info = { .qdev.name = "scsi-disk", + .qdev.fw_name = "disk", .qdev.desc = "virtual scsi disk or cdrom", .qdev.size = sizeof(SCSIDiskState), + .qdev.reset = scsi_disk_reset, .init = scsi_disk_initfn, .destroy = scsi_destroy, .send_command = scsi_send_command, @@ -1032,8 +1294,10 @@ .cancel_io = scsi_cancel_io, .get_buf = scsi_get_buf, .qdev.props = (Property[]) { - DEFINE_PROP_DRIVE("drive", SCSIDiskState, qdev.dinfo), + DEFINE_BLOCK_PROPERTIES(SCSIDiskState, qdev.conf), DEFINE_PROP_STRING("ver", SCSIDiskState, version), + DEFINE_PROP_STRING("serial", SCSIDiskState, serial), + DEFINE_PROP_BIT("removable", SCSIDiskState, removable, 0, false), DEFINE_PROP_END_OF_LIST(), }, }; diff -Nru qemu-kvm-0.12.5+noroms/hw/scsi-generic.c qemu-kvm-0.14.1/hw/scsi-generic.c --- qemu-kvm-0.12.5+noroms/hw/scsi-generic.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/scsi-generic.c 2011-05-11 13:29:46.000000000 +0000 @@ -12,8 +12,9 @@ */ #include "qemu-common.h" -#include "block.h" +#include "qemu-error.h" #include "scsi.h" +#include "blockdev.h" #ifdef __linux__ @@ -58,6 +59,7 @@ struct SCSIGenericState { SCSIDevice qdev; + BlockDriverState *bs; int lun; int driver_status; uint8_t sensebuf[SCSI_SENSE_BUF_SIZE]; @@ -94,17 +96,17 @@ s->senselen = r->io_header.sb_len_wr; if (ret != 0) - r->req.status = BUSY << 1; + r->req.status = BUSY; else { if (s->driver_status & SG_ERR_DRIVER_TIMEOUT) { - r->req.status = BUSY << 1; + r->req.status = BUSY; BADF("Driver Timeout\n"); } else if (r->io_header.status) r->req.status = r->io_header.status; else if (s->driver_status & SG_ERR_DRIVER_SENSE) - r->req.status = CHECK_CONDITION << 1; + r->req.status = CHECK_CONDITION; else - r->req.status = GOOD << 1; + r->req.status = GOOD; } DPRINTF("Command complete 0x%p tag=0x%x status=%d\n", r, r->req.tag, r->req.status); @@ -162,7 +164,7 @@ int len; if (ret) { - DPRINTF("IO error\n"); + DPRINTF("IO error ret %d\n", ret); scsi_command_complete(r, ret); return; } @@ -212,7 +214,7 @@ return; } - ret = execute_command(s->qdev.dinfo->bdrv, r, SG_DXFER_FROM_DEV, scsi_read_complete); + ret = execute_command(s->bs, r, SG_DXFER_FROM_DEV, scsi_read_complete); if (ret == -1) { scsi_command_complete(r, -EINVAL); return; @@ -234,7 +236,7 @@ if (r->req.cmd.buf[0] == MODE_SELECT && r->req.cmd.buf[4] == 12 && s->qdev.type == TYPE_TAPE) { s->qdev.blocksize = (r->buf[9] << 16) | (r->buf[10] << 8) | r->buf[11]; - DPRINTF("block size %d\n", s->blocksize); + DPRINTF("block size %d\n", s->qdev.blocksize); } scsi_command_complete(r, ret); @@ -263,7 +265,7 @@ return 0; } - ret = execute_command(s->qdev.dinfo->bdrv, r, SG_DXFER_TO_DEV, scsi_write_complete); + ret = execute_command(s->bs, r, SG_DXFER_TO_DEV, scsi_write_complete); if (ret == -1) { scsi_command_complete(r, -EINVAL); return 1; @@ -331,7 +333,7 @@ s->senselen = 7; s->driver_status = SG_ERR_DRIVER_SENSE; bus = scsi_bus_from_device(d); - bus->complete(bus, SCSI_REASON_DONE, tag, CHECK_CONDITION << 1); + bus->complete(bus, SCSI_REASON_DONE, tag, CHECK_CONDITION); return 0; } @@ -349,15 +351,25 @@ } scsi_req_fixup(&r->req); - DPRINTF("Command: lun=%d tag=0x%x data=0x%02x len %d\n", lun, tag, - cmd[0], r->req.cmd.xfer); + DPRINTF("Command: lun=%d tag=0x%x len %zd data=0x%02x", lun, tag, + r->req.cmd.xfer, cmd[0]); + +#ifdef DEBUG_SCSI + { + int i; + for (i = 1; i < r->req.cmd.len; i++) { + printf(" 0x%02x", cmd[i]); + } + printf("\n"); + } +#endif if (r->req.cmd.xfer == 0) { if (r->buf != NULL) qemu_free(r->buf); r->buflen = 0; r->buf = NULL; - ret = execute_command(s->qdev.dinfo->bdrv, r, SG_DXFER_NONE, scsi_command_complete); + ret = execute_command(s->bs, r, SG_DXFER_NONE, scsi_command_complete); if (ret == -1) { scsi_command_complete(r, -EINVAL); return 0; @@ -443,16 +455,32 @@ return (buf[9] << 16) | (buf[10] << 8) | buf[11]; } -static void scsi_destroy(SCSIDevice *d) +static void scsi_generic_purge_requests(SCSIGenericState *s) { - SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d); SCSIGenericReq *r; while (!QTAILQ_EMPTY(&s->qdev.requests)) { r = DO_UPCAST(SCSIGenericReq, req, QTAILQ_FIRST(&s->qdev.requests)); + if (r->req.aiocb) { + bdrv_aio_cancel(r->req.aiocb); + } scsi_remove_request(r); } - drive_uninit(s->qdev.dinfo); +} + +static void scsi_generic_reset(DeviceState *dev) +{ + SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev.qdev, dev); + + scsi_generic_purge_requests(s); +} + +static void scsi_destroy(SCSIDevice *d) +{ + SCSIGenericState *s = DO_UPCAST(SCSIGenericState, qdev, d); + + scsi_generic_purge_requests(s); + blockdev_mark_auto_del(s->qdev.conf.bs); } static int scsi_generic_initfn(SCSIDevice *dev) @@ -461,27 +489,37 @@ int sg_version; struct sg_scsi_id scsiid; - if (!s->qdev.dinfo || !s->qdev.dinfo->bdrv) { - qemu_error("scsi-generic: drive property not set\n"); + if (!s->qdev.conf.bs) { + error_report("scsi-generic: drive property not set"); return -1; } + s->bs = s->qdev.conf.bs; /* check we are really using a /dev/sg* file */ - if (!bdrv_is_sg(s->qdev.dinfo->bdrv)) { - qemu_error("scsi-generic: not /dev/sg*\n"); + if (!bdrv_is_sg(s->bs)) { + error_report("scsi-generic: not /dev/sg*"); + return -1; + } + + if (bdrv_get_on_error(s->bs, 0) != BLOCK_ERR_STOP_ENOSPC) { + error_report("Device doesn't support drive option werror"); + return -1; + } + if (bdrv_get_on_error(s->bs, 1) != BLOCK_ERR_REPORT) { + error_report("Device doesn't support drive option rerror"); return -1; } /* check we are using a driver managing SG_IO (version 3 and after */ - if (bdrv_ioctl(s->qdev.dinfo->bdrv, SG_GET_VERSION_NUM, &sg_version) < 0 || + if (bdrv_ioctl(s->bs, SG_GET_VERSION_NUM, &sg_version) < 0 || sg_version < 30000) { - qemu_error("scsi-generic: scsi generic interface too old\n"); + error_report("scsi-generic: scsi generic interface too old"); return -1; } /* get LUN of the /dev/sg? */ - if (bdrv_ioctl(s->qdev.dinfo->bdrv, SG_GET_SCSI_ID, &scsiid)) { - qemu_error("scsi-generic: SG_GET_SCSI_ID ioctl failed\n"); + if (bdrv_ioctl(s->bs, SG_GET_SCSI_ID, &scsiid)) { + error_report("scsi-generic: SG_GET_SCSI_ID ioctl failed"); return -1; } @@ -491,11 +529,11 @@ s->qdev.type = scsiid.scsi_type; DPRINTF("device type %d\n", s->qdev.type); if (s->qdev.type == TYPE_TAPE) { - s->qdev.blocksize = get_stream_blocksize(s->qdev.dinfo->bdrv); + s->qdev.blocksize = get_stream_blocksize(s->bs); if (s->qdev.blocksize == -1) s->qdev.blocksize = 0; } else { - s->qdev.blocksize = get_blocksize(s->qdev.dinfo->bdrv); + s->qdev.blocksize = get_blocksize(s->bs); /* removable media returns 0 if not present */ if (s->qdev.blocksize <= 0) { if (s->qdev.type == TYPE_ROM || s->qdev.type == TYPE_WORM) @@ -507,6 +545,7 @@ DPRINTF("block size %d\n", s->qdev.blocksize); s->driver_status = 0; memset(s->sensebuf, 0, sizeof(s->sensebuf)); + bdrv_set_removable(s->bs, 0); return 0; } @@ -514,6 +553,7 @@ .qdev.name = "scsi-generic", .qdev.desc = "pass through generic scsi device (/dev/sg*)", .qdev.size = sizeof(SCSIGenericState), + .qdev.reset = scsi_generic_reset, .init = scsi_generic_initfn, .destroy = scsi_destroy, .send_command = scsi_send_command, @@ -522,7 +562,7 @@ .cancel_io = scsi_cancel_io, .get_buf = scsi_get_buf, .qdev.props = (Property[]) { - DEFINE_PROP_DRIVE("drive", SCSIGenericState, qdev.dinfo), + DEFINE_BLOCK_PROPERTIES(SCSIGenericState, qdev.conf), DEFINE_PROP_END_OF_LIST(), }, }; diff -Nru qemu-kvm-0.12.5+noroms/hw/scsi.h qemu-kvm-0.14.1/hw/scsi.h --- qemu-kvm-0.12.5+noroms/hw/scsi.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/scsi.h 2011-05-11 13:29:46.000000000 +0000 @@ -3,6 +3,9 @@ #include "qdev.h" #include "block.h" +#include "block_int.h" + +#define MAX_SCSI_DEVS 255 #define SCSI_CMD_BUF_SIZE 16 @@ -24,10 +27,6 @@ SCSI_XFER_TO_DEV, /* WRITE, MODE_SELECT, ... */ }; -typedef struct SCSISense { - uint8_t key; -} SCSISense; - typedef struct SCSIRequest { SCSIBus *bus; SCSIDevice *dev; @@ -42,6 +41,7 @@ enum SCSIXferMode mode; } cmd; BlockDriverAIOCB *aiocb; + bool enqueued; QTAILQ_ENTRY(SCSIRequest) next; } SCSIRequest; @@ -49,12 +49,11 @@ { DeviceState qdev; uint32_t id; - DriveInfo *dinfo; + BlockConf conf; SCSIDeviceInfo *info; QTAILQ_HEAD(, SCSIRequest) requests; int blocksize; int type; - struct SCSISense sense; }; /* cdrom.c */ @@ -84,7 +83,7 @@ int tcq, ndev; scsi_completionfn complete; - SCSIDevice *devs[8]; + SCSIDevice *devs[MAX_SCSI_DEVS]; }; void scsi_bus_new(SCSIBus *bus, DeviceState *host, int tcq, int ndev, @@ -96,11 +95,9 @@ return DO_UPCAST(SCSIBus, qbus, d->qdev.parent_bus); } -SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, DriveInfo *dinfo, int unit); -void scsi_bus_legacy_handle_cmdline(SCSIBus *bus); - -void scsi_dev_clear_sense(SCSIDevice *dev); -void scsi_dev_set_sense(SCSIDevice *dev, uint8_t key); +SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, BlockDriverState *bdrv, + int unit, bool removable); +int scsi_bus_legacy_handle_cmdline(SCSIBus *bus); SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, uint32_t lun); SCSIRequest *scsi_req_find(SCSIDevice *d, uint32_t tag); diff -Nru qemu-kvm-0.12.5+noroms/hw/sd.c qemu-kvm-0.14.1/hw/sd.c --- qemu-kvm-0.12.5+noroms/hw/sd.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/sd.c 2011-05-11 13:29:46.000000000 +0000 @@ -31,6 +31,7 @@ #include "hw.h" #include "block.h" +#include "block_int.h" #include "sd.h" //#define DEBUG_SD 1 @@ -421,9 +422,14 @@ sd->pwd_len = 0; } -static void sd_cardchange(void *opaque) +static void sd_cardchange(void *opaque, int reason) { SDState *sd = opaque; + + if (!(reason & CHANGE_MEDIA)) { + return; + } + qemu_set_irq(sd->inserted_cb, bdrv_is_inserted(sd->bdrv)); if (bdrv_is_inserted(sd->bdrv)) { sd_reset(sd, sd->bdrv); @@ -440,7 +446,7 @@ SDState *sd; sd = (SDState *) qemu_mallocz(sizeof(SDState)); - sd->buf = qemu_memalign(512, 512); + sd->buf = qemu_blockalign(bs, 512); sd->spi = is_spi; sd->enable = 1; sd_reset(sd, bs); @@ -669,6 +675,10 @@ } break; + case 5: /* CMD5: reserved for SDIO cards */ + sd->card_status |= ILLEGAL_COMMAND; + return sd_r0; + case 6: /* CMD6: SWITCH_FUNCTION */ if (sd->spi) goto bad_cmd; @@ -1139,12 +1149,8 @@ } static sd_rsp_type_t sd_app_command(SDState *sd, - SDRequest req) { - uint32_t rca; - - if (sd_cmd_type[req.cmd] == sd_ac || sd_cmd_type[req.cmd] == sd_adtc) - rca = req.arg >> 16; - + SDRequest req) +{ DPRINTF("ACMD%d 0x%08x\n", req.cmd, req.arg); switch (req.cmd) { case 6: /* ACMD6: SET_BUS_WIDTH */ @@ -1162,6 +1168,7 @@ case 13: /* ACMD13: SD_STATUS */ switch (sd->state) { case sd_transfer_state: + sd->state = sd_sendingdata_state; sd->data_start = 0; sd->data_offset = 0; return sd_r1; @@ -1176,6 +1183,7 @@ case sd_transfer_state: *(uint32_t *) sd->data = sd->blk_written; + sd->state = sd_sendingdata_state; sd->data_start = 0; sd->data_offset = 0; return sd_r1; diff -Nru qemu-kvm-0.12.5+noroms/hw/serial.c qemu-kvm-0.14.1/hw/serial.c --- qemu-kvm-0.12.5+noroms/hw/serial.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/serial.c 2011-05-11 13:29:46.000000000 +0000 @@ -27,6 +27,7 @@ #include "isa.h" #include "pc.h" #include "qemu-timer.h" +#include "sysemu.h" //#define DEBUG_SERIAL @@ -98,6 +99,14 @@ #define RECV_FIFO 1 #define MAX_XMIT_RETRY 4 +#ifdef DEBUG_SERIAL +#define DPRINTF(fmt, ...) \ +do { fprintf(stderr, "serial: " fmt , ## __VA_ARGS__); } while (0) +#else +#define DPRINTF(fmt, ...) \ +do {} while (0) +#endif + typedef struct SerialFIFO { uint8_t data[UART_FIFO_LENGTH]; uint8_t count; @@ -169,11 +178,19 @@ { SerialFIFO *f = (fifo) ? &s->recv_fifo : &s->xmit_fifo; - f->data[f->head++] = chr; + /* Receive overruns do not overwrite FIFO contents. */ + if (fifo == XMIT_FIFO || f->count < UART_FIFO_LENGTH) { + + f->data[f->head++] = chr; + + if (f->head == UART_FIFO_LENGTH) + f->head = 0; + } - if (f->head == UART_FIFO_LENGTH) - f->head = 0; - f->count++; + if (f->count < UART_FIFO_LENGTH) + f->count++; + else if (fifo == RECV_FIFO) + s->lsr |= UART_LSR_OE; return 1; } @@ -258,10 +275,9 @@ ssp.stop_bits = stop_bits; s->char_transmit_time = (get_ticks_per_sec() / speed) * frame_size; qemu_chr_ioctl(s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp); -#if 0 - printf("speed=%d parity=%c data=%d stop=%d\n", + + DPRINTF("speed=%d parity=%c data=%d stop=%d\n", speed, parity, data_bits, stop_bits); -#endif } static void serial_update_msl(SerialState *s) @@ -351,9 +367,7 @@ SerialState *s = opaque; addr &= 7; -#ifdef DEBUG_SERIAL - printf("serial: write addr=0x%02x val=0x%02x\n", addr, val); -#endif + DPRINTF("write addr=0x%02x val=0x%02x\n", addr, val); switch(addr) { default: case 0: @@ -363,15 +377,15 @@ } else { s->thr = (uint8_t) val; if(s->fcr & UART_FCR_FE) { - fifo_put(s, XMIT_FIFO, s->thr); - s->thr_ipending = 0; - s->lsr &= ~UART_LSR_TEMT; - s->lsr &= ~UART_LSR_THRE; - serial_update_irq(s); + fifo_put(s, XMIT_FIFO, s->thr); + s->thr_ipending = 0; + s->lsr &= ~UART_LSR_TEMT; + s->lsr &= ~UART_LSR_THRE; + serial_update_irq(s); } else { - s->thr_ipending = 0; - s->lsr &= ~UART_LSR_THRE; - serial_update_irq(s); + s->thr_ipending = 0; + s->lsr &= ~UART_LSR_THRE; + serial_update_irq(s); } serial_xmit(s); } @@ -533,8 +547,10 @@ break; case 2: ret = s->iir; + if ((ret & UART_IIR_ID) == UART_IIR_THRI) { s->thr_ipending = 0; - serial_update_irq(s); + serial_update_irq(s); + } break; case 3: ret = s->lcr; @@ -544,9 +560,9 @@ break; case 5: ret = s->lsr; - /* Clear break interrupt */ - if (s->lsr & UART_LSR_BI) { - s->lsr &= ~UART_LSR_BI; + /* Clear break and overrun interrupts */ + if (s->lsr & (UART_LSR_BI|UART_LSR_OE)) { + s->lsr &= ~(UART_LSR_BI|UART_LSR_OE); serial_update_irq(s); } break; @@ -572,9 +588,7 @@ ret = s->scr; break; } -#ifdef DEBUG_SERIAL - printf("serial: read addr=0x%02x val=0x%02x\n", addr, ret); -#endif + DPRINTF("read addr=0x%02x val=0x%02x\n", addr, ret); return ret; } @@ -629,6 +643,8 @@ /* call the timeout receive callback in 4 char transmit time */ qemu_mod_timer(s->fifo_timeout_timer, qemu_get_clock (vm_clock) + s->char_transmit_time * 4); } else { + if (s->lsr & UART_LSR_DR) + s->lsr |= UART_LSR_OE; s->rbr = buf[0]; s->lsr |= UART_LSR_DR; } @@ -638,9 +654,7 @@ static void serial_event(void *opaque, int event) { SerialState *s = opaque; -#ifdef DEBUG_SERIAL - printf("serial: event %x\n", event); -#endif + DPRINTF("event %x\n", event); if (event == CHR_EVENT_BREAK) serial_receive_break(s); } @@ -660,6 +674,7 @@ } /* Initialize fcr via setter to perform essential side-effects */ serial_ioport_write(s, 0x02, s->fcr_vmstate); + serial_update_parameters(s); return 0; } @@ -759,10 +774,11 @@ s->baudbase = 115200; isa_init_irq(dev, &s->irq, isa->isairq); serial_init_core(s); - vmstate_register(isa->iobase, &vmstate_serial, s); + qdev_set_legacy_instance_id(&dev->qdev, isa->iobase, 3); register_ioport_write(isa->iobase, 8, 1, serial_ioport_write, s); register_ioport_read(isa->iobase, 8, 1, serial_ioport_read, s); + isa_init_ioport_range(dev, isa->iobase, 8); return 0; } @@ -778,6 +794,16 @@ return &DO_UPCAST(ISASerialState, dev, dev)->state; } +static const VMStateDescription vmstate_isa_serial = { + .name = "serial", + .version_id = 3, + .minimum_version_id = 2, + .fields = (VMStateField []) { + VMSTATE_STRUCT(state, ISASerialState, 0, vmstate_serial, SerialState), + VMSTATE_END_OF_LIST() + } +}; + SerialState *serial_init(int base, qemu_irq irq, int baudbase, CharDriverState *chr) { @@ -790,7 +816,7 @@ s->chr = chr; serial_init_core(s); - vmstate_register(base, &vmstate_serial, s); + vmstate_register(NULL, base, &vmstate_serial, s); register_ioport_write(base, 8, 1, serial_ioport_write, s); register_ioport_read(base, 8, 1, serial_ioport_read, s); @@ -813,65 +839,106 @@ serial_ioport_write(s, addr >> s->it_shift, value & 0xFF); } -static uint32_t serial_mm_readw(void *opaque, target_phys_addr_t addr) +static uint32_t serial_mm_readw_be(void *opaque, target_phys_addr_t addr) { SerialState *s = opaque; uint32_t val; val = serial_ioport_read(s, addr >> s->it_shift) & 0xFFFF; -#ifdef TARGET_WORDS_BIGENDIAN val = bswap16(val); -#endif return val; } -static void serial_mm_writew(void *opaque, target_phys_addr_t addr, - uint32_t value) +static uint32_t serial_mm_readw_le(void *opaque, target_phys_addr_t addr) +{ + SerialState *s = opaque; + uint32_t val; + + val = serial_ioport_read(s, addr >> s->it_shift) & 0xFFFF; + return val; +} + +static void serial_mm_writew_be(void *opaque, target_phys_addr_t addr, + uint32_t value) { SerialState *s = opaque; -#ifdef TARGET_WORDS_BIGENDIAN + value = bswap16(value); -#endif serial_ioport_write(s, addr >> s->it_shift, value & 0xFFFF); } -static uint32_t serial_mm_readl(void *opaque, target_phys_addr_t addr) +static void serial_mm_writew_le(void *opaque, target_phys_addr_t addr, + uint32_t value) +{ + SerialState *s = opaque; + + serial_ioport_write(s, addr >> s->it_shift, value & 0xFFFF); +} + +static uint32_t serial_mm_readl_be(void *opaque, target_phys_addr_t addr) { SerialState *s = opaque; uint32_t val; val = serial_ioport_read(s, addr >> s->it_shift); -#ifdef TARGET_WORDS_BIGENDIAN val = bswap32(val); -#endif return val; } -static void serial_mm_writel(void *opaque, target_phys_addr_t addr, - uint32_t value) +static uint32_t serial_mm_readl_le(void *opaque, target_phys_addr_t addr) { SerialState *s = opaque; -#ifdef TARGET_WORDS_BIGENDIAN + uint32_t val; + + val = serial_ioport_read(s, addr >> s->it_shift); + return val; +} + +static void serial_mm_writel_be(void *opaque, target_phys_addr_t addr, + uint32_t value) +{ + SerialState *s = opaque; + value = bswap32(value); -#endif serial_ioport_write(s, addr >> s->it_shift, value); } -static CPUReadMemoryFunc * const serial_mm_read[] = { +static void serial_mm_writel_le(void *opaque, target_phys_addr_t addr, + uint32_t value) +{ + SerialState *s = opaque; + + serial_ioport_write(s, addr >> s->it_shift, value); +} + +static CPUReadMemoryFunc * const serial_mm_read_be[] = { &serial_mm_readb, - &serial_mm_readw, - &serial_mm_readl, + &serial_mm_readw_be, + &serial_mm_readl_be, }; -static CPUWriteMemoryFunc * const serial_mm_write[] = { +static CPUWriteMemoryFunc * const serial_mm_write_be[] = { &serial_mm_writeb, - &serial_mm_writew, - &serial_mm_writel, + &serial_mm_writew_be, + &serial_mm_writel_be, +}; + +static CPUReadMemoryFunc * const serial_mm_read_le[] = { + &serial_mm_readb, + &serial_mm_readw_le, + &serial_mm_readl_le, +}; + +static CPUWriteMemoryFunc * const serial_mm_write_le[] = { + &serial_mm_writeb, + &serial_mm_writew_le, + &serial_mm_writel_le, }; SerialState *serial_mm_init (target_phys_addr_t base, int it_shift, qemu_irq irq, int baudbase, - CharDriverState *chr, int ioregister) + CharDriverState *chr, int ioregister, + int be) { SerialState *s; int s_io_memory; @@ -884,11 +951,18 @@ s->chr = chr; serial_init_core(s); - vmstate_register(base, &vmstate_serial, s); + vmstate_register(NULL, base, &vmstate_serial, s); if (ioregister) { - s_io_memory = cpu_register_io_memory(serial_mm_read, - serial_mm_write, s); + if (be) { + s_io_memory = cpu_register_io_memory(serial_mm_read_be, + serial_mm_write_be, s, + DEVICE_NATIVE_ENDIAN); + } else { + s_io_memory = cpu_register_io_memory(serial_mm_read_le, + serial_mm_write_le, s, + DEVICE_NATIVE_ENDIAN); + } cpu_register_physical_memory(base, 8 << it_shift, s_io_memory); } serial_update_msl(s); @@ -898,6 +972,7 @@ static ISADeviceInfo serial_isa_info = { .qdev.name = "isa-serial", .qdev.size = sizeof(ISASerialState), + .qdev.vmsd = &vmstate_isa_serial, .init = serial_isa_initfn, .qdev.props = (Property[]) { DEFINE_PROP_UINT32("index", ISASerialState, index, -1), diff -Nru qemu-kvm-0.12.5+noroms/hw/sh7750.c qemu-kvm-0.14.1/hw/sh7750.c --- qemu-kvm-0.12.5+noroms/hw/sh7750.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/sh7750.c 2011-05-11 13:29:46.000000000 +0000 @@ -206,7 +206,7 @@ switch (addr) { default: error_access("byte read", addr); - assert(0); + abort(); } } @@ -240,7 +240,7 @@ return 0; default: error_access("word read", addr); - assert(0); + abort(); } } @@ -287,7 +287,7 @@ return s->cpu->prr; default: error_access("long read", addr); - assert(0); + abort(); } } @@ -303,7 +303,7 @@ } error_access("byte write", addr); - assert(0); + abort(); } static void sh7750_mem_writew(void *opaque, target_phys_addr_t addr, @@ -349,12 +349,12 @@ s->gpioic = mem_value; if (mem_value != 0) { fprintf(stderr, "I/O interrupts not implemented\n"); - assert(0); + abort(); } return; default: error_access("word write", addr); - assert(0); + abort(); } } @@ -433,7 +433,7 @@ return; default: error_access("long write", addr); - assert(0); + abort(); } } @@ -618,13 +618,14 @@ static uint32_t invalid_read(void *opaque, target_phys_addr_t addr) { - assert(0); + abort(); return 0; } static uint32_t sh7750_mmct_readl(void *opaque, target_phys_addr_t addr) { + SH7750State *s = opaque; uint32_t ret = 0; switch (MM_REGION_TYPE(addr)) { @@ -633,21 +634,23 @@ /* do nothing */ break; case MM_ITLB_ADDR: + ret = cpu_sh4_read_mmaped_itlb_addr(s->cpu, addr); + break; case MM_ITLB_DATA: - /* XXXXX */ - assert(0); - break; + ret = cpu_sh4_read_mmaped_itlb_data(s->cpu, addr); + break; case MM_OCACHE_ADDR: case MM_OCACHE_DATA: /* do nothing */ break; case MM_UTLB_ADDR: + ret = cpu_sh4_read_mmaped_utlb_addr(s->cpu, addr); + break; case MM_UTLB_DATA: - /* XXXXX */ - assert(0); - break; + ret = cpu_sh4_read_mmaped_utlb_data(s->cpu, addr); + break; default: - assert(0); + abort(); } return ret; @@ -656,7 +659,7 @@ static void invalid_write(void *opaque, target_phys_addr_t addr, uint32_t mem_value) { - assert(0); + abort(); } static void sh7750_mmct_writel(void *opaque, target_phys_addr_t addr, @@ -670,9 +673,11 @@ /* do nothing */ break; case MM_ITLB_ADDR: + cpu_sh4_write_mmaped_itlb_addr(s->cpu, addr, mem_value); + break; case MM_ITLB_DATA: - /* XXXXX */ - assert(0); + cpu_sh4_write_mmaped_itlb_data(s->cpu, addr, mem_value); + abort(); break; case MM_OCACHE_ADDR: case MM_OCACHE_DATA: @@ -682,11 +687,10 @@ cpu_sh4_write_mmaped_utlb_addr(s->cpu, addr, mem_value); break; case MM_UTLB_DATA: - /* XXXXX */ - assert(0); + cpu_sh4_write_mmaped_utlb_data(s->cpu, addr, mem_value); break; default: - assert(0); + abort(); break; } } @@ -713,7 +717,8 @@ s->cpu = cpu; s->periph_freq = 60000000; /* 60MHz */ sh7750_io_memory = cpu_register_io_memory(sh7750_mem_read, - sh7750_mem_write, s); + sh7750_mem_write, s, + DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory_offset(0x1f000000, 0x1000, sh7750_io_memory, 0x1f000000); cpu_register_physical_memory_offset(0xff000000, 0x1000, @@ -728,7 +733,8 @@ sh7750_io_memory, 0x1fc00000); sh7750_mm_cache_and_tlb = cpu_register_io_memory(sh7750_mmct_read, - sh7750_mmct_write, s); + sh7750_mmct_write, s, + DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(0xf0000000, 0x08000000, sh7750_mm_cache_and_tlb); diff -Nru qemu-kvm-0.12.5+noroms/hw/sharpsl.h qemu-kvm-0.14.1/hw/sharpsl.h --- qemu-kvm-0.12.5+noroms/hw/sharpsl.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/sharpsl.h 2011-05-11 13:29:46.000000000 +0000 @@ -10,13 +10,6 @@ fprintf(stderr, "%s: " format, __FUNCTION__, ##__VA_ARGS__) /* zaurus.c */ -typedef struct ScoopInfo ScoopInfo; -ScoopInfo *scoop_init(PXA2xxState *cpu, - int instance, target_phys_addr_t target_base); -void scoop_gpio_set(void *opaque, int line, int level); -qemu_irq *scoop_gpio_in_get(ScoopInfo *s); -void scoop_gpio_out_set(ScoopInfo *s, int line, - qemu_irq handler); #define SL_PXA_PARAM_BASE 0xa0000a00 void sl_bootparam_write(target_phys_addr_t ptr); diff -Nru qemu-kvm-0.12.5+noroms/hw/sh_intc.c qemu-kvm-0.14.1/hw/sh_intc.c --- qemu-kvm-0.12.5+noroms/hw/sh_intc.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/sh_intc.c 2011-05-11 13:29:46.000000000 +0000 @@ -105,7 +105,7 @@ } } - assert(0); + abort(); } #define INTC_MODE_NONE 0 @@ -181,7 +181,7 @@ } } - assert(0); + abort(); } static void sh_intc_toggle_mask(struct intc_desc *desc, intc_enum id, @@ -260,7 +260,7 @@ case INTC_MODE_ENABLE_REG | INTC_MODE_IS_PRIO: break; case INTC_MODE_DUAL_SET: value |= *valuep; break; case INTC_MODE_DUAL_CLR: value = *valuep & ~value; break; - default: assert(0); + default: abort(); } for (k = 0; k <= first; k++) { @@ -431,9 +431,8 @@ desc->nr_prio_regs = nr_prio_regs; i = sizeof(struct intc_source) * nr_sources; - desc->sources = qemu_malloc(i); + desc->sources = qemu_mallocz(i); - memset(desc->sources, 0, i); for (i = 0; i < desc->nr_sources; i++) { struct intc_source *source = desc->sources + i; @@ -443,7 +442,8 @@ desc->irqs = qemu_allocate_irqs(sh_intc_set_irq, desc, nr_sources); desc->iomemtype = cpu_register_io_memory(sh_intc_readfn, - sh_intc_writefn, desc); + sh_intc_writefn, desc, + DEVICE_NATIVE_ENDIAN); if (desc->mask_regs) { for (i = 0; i < desc->nr_mask_regs; i++) { struct intc_mask_reg *mr = desc->mask_regs + i; diff -Nru qemu-kvm-0.12.5+noroms/hw/sh_pci.c qemu-kvm-0.14.1/hw/sh_pci.c --- qemu-kvm-0.12.5+noroms/hw/sh_pci.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/sh_pci.c 2011-05-11 13:29:46.000000000 +0000 @@ -21,24 +21,26 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "hw.h" +#include "sysbus.h" #include "sh.h" #include "pci.h" #include "pci_host.h" -#include "sh_pci.h" #include "bswap.h" -typedef struct { +typedef struct SHPCIState { + SysBusDevice busdev; PCIBus *bus; PCIDevice *dev; + qemu_irq irq[4]; + int memconfig; uint32_t par; uint32_t mbr; uint32_t iobr; -} SHPCIC; +} SHPCIState; static void sh_pci_reg_write (void *p, target_phys_addr_t addr, uint32_t val) { - SHPCIC *pcic = p; + SHPCIState *pcic = p; switch(addr) { case 0 ... 0xfc: cpu_to_le32w((uint32_t*)(pcic->dev->config + addr), val); @@ -65,7 +67,7 @@ static uint32_t sh_pci_reg_read (void *p, target_phys_addr_t addr) { - SHPCIC *pcic = p; + SHPCIState *pcic = p; switch(addr) { case 0 ... 0xfc: return le32_to_cpup((uint32_t*)(pcic->dev->config + addr)); @@ -91,31 +93,69 @@ { NULL, NULL, sh_pci_reg_write }, }; -PCIBus *sh_pci_register_bus(pci_set_irq_fn set_irq, pci_map_irq_fn map_irq, - void *opaque, int devfn_min, int nirq) +static int sh_pci_map_irq(PCIDevice *d, int irq_num) +{ + return (d->devfn >> 3); +} + +static void sh_pci_set_irq(void *opaque, int irq_num, int level) +{ + qemu_irq *pic = opaque; + + qemu_set_irq(pic[irq_num], level); +} + +static void sh_pci_map(SysBusDevice *dev, target_phys_addr_t base) +{ + SHPCIState *s = FROM_SYSBUS(SHPCIState, dev); + + cpu_register_physical_memory(P4ADDR(base), 0x224, s->memconfig); + cpu_register_physical_memory(A7ADDR(base), 0x224, s->memconfig); + + s->iobr = 0xfe240000; + isa_mmio_init(s->iobr, 0x40000); +} + +static int sh_pci_init_device(SysBusDevice *dev) +{ + SHPCIState *s; + int i; + + s = FROM_SYSBUS(SHPCIState, dev); + for (i = 0; i < 4; i++) { + sysbus_init_irq(dev, &s->irq[i]); + } + s->bus = pci_register_bus(&s->busdev.qdev, "pci", + sh_pci_set_irq, sh_pci_map_irq, + s->irq, PCI_DEVFN(0, 0), 4); + s->memconfig = cpu_register_io_memory(sh_pci_reg.r, sh_pci_reg.w, + s, DEVICE_NATIVE_ENDIAN); + sysbus_init_mmio_cb(dev, 0x224, sh_pci_map); + s->dev = pci_create_simple(s->bus, PCI_DEVFN(0, 0), "sh_pci_host"); + return 0; +} + +static int sh_pci_host_init(PCIDevice *d) { - SHPCIC *p; - int reg; + pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_HITACHI); + pci_config_set_device_id(d->config, PCI_DEVICE_ID_HITACHI_SH7751R); + pci_set_word(d->config + PCI_COMMAND, PCI_COMMAND_WAIT); + pci_set_word(d->config + PCI_STATUS, PCI_STATUS_CAP_LIST | + PCI_STATUS_FAST_BACK | PCI_STATUS_DEVSEL_MEDIUM); + return 0; +} - p = qemu_mallocz(sizeof(SHPCIC)); - p->bus = pci_register_bus(NULL, "pci", - set_irq, map_irq, opaque, devfn_min, nirq); - - p->dev = pci_register_device(p->bus, "SH PCIC", sizeof(PCIDevice), - -1, NULL, NULL); - reg = cpu_register_io_memory(sh_pci_reg.r, sh_pci_reg.w, p); - cpu_register_physical_memory(0x1e200000, 0x224, reg); - cpu_register_physical_memory(0xfe200000, 0x224, reg); - - p->iobr = 0xfe240000; - isa_mmio_init(p->iobr, 0x40000); - - pci_config_set_vendor_id(p->dev->config, PCI_VENDOR_ID_HITACHI); - pci_config_set_device_id(p->dev->config, PCI_DEVICE_ID_HITACHI_SH7751R); - p->dev->config[0x04] = 0x80; - p->dev->config[0x05] = 0x00; - p->dev->config[0x06] = 0x90; - p->dev->config[0x07] = 0x02; +static PCIDeviceInfo sh_pci_host_info = { + .qdev.name = "sh_pci_host", + .qdev.size = sizeof(PCIDevice), + .init = sh_pci_host_init, +}; - return p->bus; +static void sh_pci_register_devices(void) +{ + sysbus_register_dev("sh_pci", sizeof(SHPCIState), + sh_pci_init_device); + pci_qdev_register(&sh_pci_host_info); } + +device_init(sh_pci_register_devices) diff -Nru qemu-kvm-0.12.5+noroms/hw/sh_pci.h qemu-kvm-0.14.1/hw/sh_pci.h --- qemu-kvm-0.12.5+noroms/hw/sh_pci.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/sh_pci.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ -#ifndef QEMU_SH_PCI_H -#define QEMU_SH_PCI_H - -#include "qemu-common.h" - -PCIBus *sh_pci_register_bus(pci_set_irq_fn set_irq, pci_map_irq_fn map_irq, - void *pic, int devfn_min, int nirq); - -#endif diff -Nru qemu-kvm-0.12.5+noroms/hw/sh_serial.c qemu-kvm-0.14.1/hw/sh_serial.c --- qemu-kvm-0.12.5+noroms/hw/sh_serial.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/sh_serial.c 2011-05-11 13:29:46.000000000 +0000 @@ -74,7 +74,7 @@ s->rx_tail = 0; } -static void sh_serial_ioport_write(void *opaque, uint32_t offs, uint32_t val) +static void sh_serial_write(void *opaque, uint32_t offs, uint32_t val) { sh_serial_state *s = opaque; unsigned char ch; @@ -182,10 +182,10 @@ } fprintf(stderr, "sh_serial: unsupported write to 0x%02x\n", offs); - assert(0); + abort(); } -static uint32_t sh_serial_ioport_read(void *opaque, uint32_t offs) +static uint32_t sh_serial_read(void *opaque, uint32_t offs) { sh_serial_state *s = opaque; uint32_t ret = ~0; @@ -282,7 +282,7 @@ if (ret & ~((1 << 16) - 1)) { fprintf(stderr, "sh_serial: unsupported read from 0x%02x\n", offs); - assert(0); + abort(); } return ret; @@ -293,26 +293,6 @@ return s->scr & (1 << 4); } -static void sh_serial_receive_byte(sh_serial_state *s, int ch) -{ - if (s->feat & SH_SERIAL_FEAT_SCIF) { - if (s->rx_cnt < SH_RX_FIFO_LENGTH) { - s->rx_fifo[s->rx_head++] = ch; - if (s->rx_head == SH_RX_FIFO_LENGTH) - s->rx_head = 0; - s->rx_cnt++; - if (s->rx_cnt >= s->rtrg) { - s->flags |= SH_SERIAL_FLAG_RDF; - if (s->scr & (1 << 6) && s->rxi) { - qemu_set_irq(s->rxi, 1); - } - } - } - } else { - s->rx_fifo[0] = ch; - } -} - static void sh_serial_receive_break(sh_serial_state *s) { if (s->feat & SH_SERIAL_FEAT_SCIF) @@ -328,7 +308,27 @@ static void sh_serial_receive1(void *opaque, const uint8_t *buf, int size) { sh_serial_state *s = opaque; - sh_serial_receive_byte(s, buf[0]); + + if (s->feat & SH_SERIAL_FEAT_SCIF) { + int i; + for (i = 0; i < size; i++) { + if (s->rx_cnt < SH_RX_FIFO_LENGTH) { + s->rx_fifo[s->rx_head++] = buf[i]; + if (s->rx_head == SH_RX_FIFO_LENGTH) { + s->rx_head = 0; + } + s->rx_cnt++; + if (s->rx_cnt >= s->rtrg) { + s->flags |= SH_SERIAL_FLAG_RDF; + if (s->scr & (1 << 6) && s->rxi) { + qemu_set_irq(s->rxi, 1); + } + } + } + } + } else { + s->rx_fifo[0] = buf[0]; + } } static void sh_serial_event(void *opaque, int event) @@ -338,19 +338,6 @@ sh_serial_receive_break(s); } -static uint32_t sh_serial_read (void *opaque, target_phys_addr_t addr) -{ - sh_serial_state *s = opaque; - return sh_serial_ioport_read(s, addr); -} - -static void sh_serial_write (void *opaque, - target_phys_addr_t addr, uint32_t value) -{ - sh_serial_state *s = opaque; - sh_serial_ioport_write(s, addr, value); -} - static CPUReadMemoryFunc * const sh_serial_readfn[] = { &sh_serial_read, &sh_serial_read, @@ -395,7 +382,8 @@ sh_serial_clear_fifo(s); s_io_memory = cpu_register_io_memory(sh_serial_readfn, - sh_serial_writefn, s); + sh_serial_writefn, s, + DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(P4ADDR(base), 0x28, s_io_memory); cpu_register_physical_memory(A7ADDR(base), 0x28, s_io_memory); diff -Nru qemu-kvm-0.12.5+noroms/hw/sh_timer.c qemu-kvm-0.14.1/hw/sh_timer.c --- qemu-kvm-0.12.5+noroms/hw/sh_timer.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/sh_timer.c 2011-05-11 13:29:46.000000000 +0000 @@ -319,7 +319,8 @@ s->timer[2] = sh_timer_init(freq, timer_feat | TIMER_FEAT_CAPT, ch2_irq0); /* ch2_irq1 not supported */ iomemtype = cpu_register_io_memory(tmu012_readfn, - tmu012_writefn, s); + tmu012_writefn, s, + DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(P4ADDR(base), 0x00001000, iomemtype); cpu_register_physical_memory(A7ADDR(base), 0x00001000, iomemtype); /* ??? Save/restore. */ diff -Nru qemu-kvm-0.12.5+noroms/hw/slavio_intctl.c qemu-kvm-0.14.1/hw/slavio_intctl.c --- qemu-kvm-0.12.5+noroms/hw/slavio_intctl.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/slavio_intctl.c 2011-05-11 13:29:46.000000000 +0000 @@ -25,16 +25,9 @@ #include "sun4m.h" #include "monitor.h" #include "sysbus.h" +#include "trace.h" //#define DEBUG_IRQ_COUNT -//#define DEBUG_IRQ - -#ifdef DEBUG_IRQ -#define DPRINTF(fmt, ...) \ - do { printf("IRQ: " fmt , ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) -#endif /* * Registers of interrupt controller in sun4m. @@ -97,7 +90,7 @@ ret = 0; break; } - DPRINTF("read cpu %d reg 0x" TARGET_FMT_plx " = %x\n", s->cpu, addr, ret); + trace_slavio_intctl_mem_readl(s->cpu, addr, ret); return ret; } @@ -109,21 +102,19 @@ uint32_t saddr; saddr = addr >> 2; - DPRINTF("write cpu %d reg 0x" TARGET_FMT_plx " = %x\n", s->cpu, addr, val); + trace_slavio_intctl_mem_writel(s->cpu, addr, val); switch (saddr) { case 1: // clear pending softints val &= CPU_SOFTIRQ_MASK | CPU_IRQ_INT15_IN; s->intreg_pending &= ~val; slavio_check_interrupts(s->master, 1); - DPRINTF("Cleared cpu %d irq mask %x, curmask %x\n", s->cpu, val, - s->intreg_pending); + trace_slavio_intctl_mem_writel_clear(s->cpu, val, s->intreg_pending); break; case 2: // set softint val &= CPU_SOFTIRQ_MASK; s->intreg_pending |= val; slavio_check_interrupts(s->master, 1); - DPRINTF("Set cpu %d irq mask %x, curmask %x\n", s->cpu, val, - s->intreg_pending); + trace_slavio_intctl_mem_writel_set(s->cpu, val, s->intreg_pending); break; default: break; @@ -163,7 +154,7 @@ ret = 0; break; } - DPRINTF("read system reg 0x" TARGET_FMT_plx " = %x\n", addr, ret); + trace_slavio_intctlm_mem_readl(addr, ret); return ret; } @@ -175,29 +166,26 @@ uint32_t saddr; saddr = addr >> 2; - DPRINTF("write system reg 0x" TARGET_FMT_plx " = %x\n", addr, val); + trace_slavio_intctlm_mem_writel(addr, val); switch (saddr) { case 2: // clear (enable) // Force clear unused bits val &= MASTER_IRQ_MASK; s->intregm_disabled &= ~val; - DPRINTF("Enabled master irq mask %x, curmask %x\n", val, - s->intregm_disabled); + trace_slavio_intctlm_mem_writel_enable(val, s->intregm_disabled); slavio_check_interrupts(s, 1); break; - case 3: // set (disable, clear pending) + case 3: // set (disable; doesn't affect pending) // Force clear unused bits val &= MASTER_IRQ_MASK; s->intregm_disabled |= val; - s->intregm_pending &= ~val; slavio_check_interrupts(s, 1); - DPRINTF("Disabled master irq mask %x, curmask %x\n", val, - s->intregm_disabled); + trace_slavio_intctlm_mem_writel_disable(val, s->intregm_disabled); break; case 4: s->target_cpu = val & (MAX_CPUS - 1); slavio_check_interrupts(s, 1); - DPRINTF("Set master irq cpu %d\n", s->target_cpu); + trace_slavio_intctlm_mem_writel_target(s->target_cpu); break; default: break; @@ -265,7 +253,7 @@ pending &= ~s->intregm_disabled; - DPRINTF("pending %x disabled %x\n", pending, s->intregm_disabled); + trace_slavio_check_interrupts(pending, s->intregm_disabled); for (i = 0; i < MAX_CPUS; i++) { pil_pending = 0; @@ -290,15 +278,23 @@ } } - /* Level 15 and CPU timer interrupts are not maskable */ - pil_pending |= s->slaves[i].intreg_pending & - (CPU_IRQ_INT15_IN | CPU_IRQ_TIMER_IN); + /* Level 15 and CPU timer interrupts are only masked when + the MASTER_DISABLE bit is set */ + if (!(s->intregm_disabled & MASTER_DISABLE)) { + pil_pending |= s->slaves[i].intreg_pending & + (CPU_IRQ_INT15_IN | CPU_IRQ_TIMER_IN); + } /* Add soft interrupts */ pil_pending |= (s->slaves[i].intreg_pending & CPU_SOFTIRQ_MASK) >> 16; if (set_irqs) { - for (j = MAX_PILS; j > 0; j--) { + /* Since there is not really an interrupt 0 (and pil_pending + * and irl_out bit zero are thus always zero) there is no need + * to do anything with cpu_irqs[i][0] and it is OK not to do + * the j=0 iteration of this loop. + */ + for (j = MAX_PILS-1; j > 0; j--) { if (pil_pending & (1 << j)) { if (!(s->slaves[i].irl_out & (1 << j))) { qemu_irq_raise(s->cpu_irqs[i][j]); @@ -325,8 +321,7 @@ uint32_t pil = intbit_to_level[irq]; unsigned int i; - DPRINTF("Set cpu %d irq %d -> pil %d level %d\n", s->target_cpu, irq, pil, - level); + trace_slavio_set_irq(s->target_cpu, irq, pil, level); if (pil > 0) { if (level) { #ifdef DEBUG_IRQ_COUNT @@ -354,7 +349,7 @@ { SLAVIO_INTCTLState *s = opaque; - DPRINTF("Set cpu %d local timer level %d\n", cpu, level); + trace_slavio_set_timer_irq_cpu(cpu, level); if (level) { s->slaves[cpu].intreg_pending |= CPU_IRQ_TIMER_IN; @@ -432,7 +427,8 @@ qdev_init_gpio_in(&dev->qdev, slavio_set_irq_all, 32 + MAX_CPUS); io_memory = cpu_register_io_memory(slavio_intctlm_mem_read, - slavio_intctlm_mem_write, s); + slavio_intctlm_mem_write, s, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, INTCTLM_SIZE, io_memory); for (i = 0; i < MAX_CPUS; i++) { @@ -441,7 +437,8 @@ } io_memory = cpu_register_io_memory(slavio_intctl_mem_read, slavio_intctl_mem_write, - &s->slaves[i]); + &s->slaves[i], + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, INTCTL_SIZE, io_memory); s->slaves[i].cpu = i; s->slaves[i].master = s; diff -Nru qemu-kvm-0.12.5+noroms/hw/slavio_misc.c qemu-kvm-0.14.1/hw/slavio_misc.c --- qemu-kvm-0.12.5+noroms/hw/slavio_misc.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/slavio_misc.c 2011-05-11 13:29:46.000000000 +0000 @@ -24,9 +24,7 @@ #include "sysemu.h" #include "sysbus.h" - -/* debug misc */ -//#define DEBUG_MISC +#include "trace.h" /* * This is the auxio port, chip control and system control part of @@ -36,13 +34,6 @@ * This also includes the PMC CPU idle controller. */ -#ifdef DEBUG_MISC -#define MISC_DPRINTF(fmt, ...) \ - do { printf("MISC: " fmt , ## __VA_ARGS__); } while (0) -#else -#define MISC_DPRINTF(fmt, ...) -#endif - typedef struct MiscState { SysBusDevice busdev; qemu_irq irq; @@ -79,10 +70,10 @@ MiscState *s = opaque; if ((s->aux2 & AUX2_PWRFAIL) && (s->config & CFG_PWRINTEN)) { - MISC_DPRINTF("Raise IRQ\n"); + trace_slavio_misc_update_irq_raise(); qemu_irq_raise(s->irq); } else { - MISC_DPRINTF("Lower IRQ\n"); + trace_slavio_misc_update_irq_lower(); qemu_irq_lower(s->irq); } } @@ -99,7 +90,7 @@ { MiscState *s = opaque; - MISC_DPRINTF("Power fail: %d, config: %d\n", power_failing, s->config); + trace_slavio_set_power_fail(power_failing, s->config); if (power_failing && (s->config & CFG_PWRINTEN)) { s->aux2 |= AUX2_PWRFAIL; } else { @@ -113,7 +104,7 @@ { MiscState *s = opaque; - MISC_DPRINTF("Write config %2.2x\n", val & 0xff); + trace_slavio_cfg_mem_writeb(val & 0xff); s->config = val & 0xff; slavio_misc_update_irq(s); } @@ -124,7 +115,7 @@ uint32_t ret = 0; ret = s->config; - MISC_DPRINTF("Read config %2.2x\n", ret); + trace_slavio_cfg_mem_readb(ret); return ret; } @@ -145,7 +136,7 @@ { MiscState *s = opaque; - MISC_DPRINTF("Write diag %2.2x\n", val & 0xff); + trace_slavio_diag_mem_writeb(val & 0xff); s->diag = val & 0xff; } @@ -155,7 +146,7 @@ uint32_t ret = 0; ret = s->diag; - MISC_DPRINTF("Read diag %2.2x\n", ret); + trace_slavio_diag_mem_readb(ret); return ret; } @@ -176,7 +167,7 @@ { MiscState *s = opaque; - MISC_DPRINTF("Write modem control %2.2x\n", val & 0xff); + trace_slavio_mdm_mem_writeb(val & 0xff); s->mctrl = val & 0xff; } @@ -186,7 +177,7 @@ uint32_t ret = 0; ret = s->mctrl; - MISC_DPRINTF("Read modem control %2.2x\n", ret); + trace_slavio_mdm_mem_readb(ret); return ret; } @@ -207,7 +198,7 @@ { MiscState *s = opaque; - MISC_DPRINTF("Write aux1 %2.2x\n", val & 0xff); + trace_slavio_aux1_mem_writeb(val & 0xff); if (val & AUX1_TC) { // Send a pulse to floppy terminal count line if (s->fdc_tc) { @@ -225,8 +216,7 @@ uint32_t ret = 0; ret = s->aux1; - MISC_DPRINTF("Read aux1 %2.2x\n", ret); - + trace_slavio_aux1_mem_readb(ret); return ret; } @@ -248,7 +238,7 @@ MiscState *s = opaque; val &= AUX2_PWRINTCLR | AUX2_PWROFF; - MISC_DPRINTF("Write aux2 %2.2x\n", val); + trace_slavio_aux2_mem_writeb(val & 0xff); val |= s->aux2 & AUX2_PWRFAIL; if (val & AUX2_PWRINTCLR) // Clear Power Fail int val &= AUX2_PWROFF; @@ -264,8 +254,7 @@ uint32_t ret = 0; ret = s->aux2; - MISC_DPRINTF("Read aux2 %2.2x\n", ret); - + trace_slavio_aux2_mem_readb(ret); return ret; } @@ -285,7 +274,7 @@ { APCState *s = opaque; - MISC_DPRINTF("Write power management %2.2x\n", val & 0xff); + trace_apc_mem_writeb(val & 0xff); qemu_irq_raise(s->cpu_halt); } @@ -293,7 +282,7 @@ { uint32_t ret = 0; - MISC_DPRINTF("Read power management %2.2x\n", ret); + trace_apc_mem_readb(ret); return ret; } @@ -321,7 +310,7 @@ default: break; } - MISC_DPRINTF("Read system control %08x\n", ret); + trace_slavio_sysctrl_mem_readl(ret); return ret; } @@ -330,7 +319,7 @@ { MiscState *s = opaque; - MISC_DPRINTF("Write system control %08x\n", val); + trace_slavio_sysctrl_mem_writel(val); switch (addr) { case 0: if (val & SYS_RESET) { @@ -367,7 +356,7 @@ default: break; } - MISC_DPRINTF("Read diagnostic LED %04x\n", ret); + trace_slavio_led_mem_readw(ret); return ret; } @@ -376,7 +365,7 @@ { MiscState *s = opaque; - MISC_DPRINTF("Write diagnostic LED %04x\n", val & 0xffff); + trace_slavio_led_mem_readw(val & 0xffff); switch (addr) { case 0: s->leds = val; @@ -423,7 +412,8 @@ sysbus_init_irq(dev, &s->cpu_halt); /* Power management (APC) XXX: not a Slavio device */ - io = cpu_register_io_memory(apc_mem_read, apc_mem_write, s); + io = cpu_register_io_memory(apc_mem_read, apc_mem_write, s, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, MISC_SIZE, io); return 0; } @@ -439,39 +429,46 @@ /* 8 bit registers */ /* Slavio control */ io = cpu_register_io_memory(slavio_cfg_mem_read, - slavio_cfg_mem_write, s); + slavio_cfg_mem_write, s, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, MISC_SIZE, io); /* Diagnostics */ io = cpu_register_io_memory(slavio_diag_mem_read, - slavio_diag_mem_write, s); + slavio_diag_mem_write, s, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, MISC_SIZE, io); /* Modem control */ io = cpu_register_io_memory(slavio_mdm_mem_read, - slavio_mdm_mem_write, s); + slavio_mdm_mem_write, s, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, MISC_SIZE, io); /* 16 bit registers */ /* ss600mp diag LEDs */ io = cpu_register_io_memory(slavio_led_mem_read, - slavio_led_mem_write, s); + slavio_led_mem_write, s, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, MISC_SIZE, io); /* 32 bit registers */ /* System control */ io = cpu_register_io_memory(slavio_sysctrl_mem_read, - slavio_sysctrl_mem_write, s); + slavio_sysctrl_mem_write, s, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, SYSCTRL_SIZE, io); /* AUX 1 (Misc System Functions) */ io = cpu_register_io_memory(slavio_aux1_mem_read, - slavio_aux1_mem_write, s); + slavio_aux1_mem_write, s, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, MISC_SIZE, io); /* AUX 2 (Software Powerdown Control) */ io = cpu_register_io_memory(slavio_aux2_mem_read, - slavio_aux2_mem_write, s); + slavio_aux2_mem_write, s, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, MISC_SIZE, io); qdev_init_gpio_in(&dev->qdev, slavio_set_power_fail, 1); diff -Nru qemu-kvm-0.12.5+noroms/hw/slavio_timer.c qemu-kvm-0.14.1/hw/slavio_timer.c --- qemu-kvm-0.12.5+noroms/hw/slavio_timer.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/slavio_timer.c 2011-05-11 13:29:46.000000000 +0000 @@ -25,15 +25,7 @@ #include "sun4m.h" #include "qemu-timer.h" #include "sysbus.h" - -//#define DEBUG_TIMER - -#ifdef DEBUG_TIMER -#define DPRINTF(fmt, ...) \ - do { printf("TIMER: " fmt , ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) do {} while (0) -#endif +#include "trace.h" /* * Registers of hardware timer in sun4m. @@ -88,8 +80,8 @@ #define TIMER_MAX_COUNT32 0x7ffffe00ULL #define TIMER_REACHED 0x80000000 #define TIMER_PERIOD 500ULL // 500ns -#define LIMIT_TO_PERIODS(l) ((l) >> 9) -#define PERIODS_TO_LIMIT(l) ((l) << 9) +#define LIMIT_TO_PERIODS(l) (((l) >> 9) - 1) +#define PERIODS_TO_LIMIT(l) (((l) + 1) << 9) static int slavio_timer_is_user(TimerContext *tc) { @@ -112,8 +104,7 @@ } count = limit - PERIODS_TO_LIMIT(ptimer_get_count(t->timer)); - DPRINTF("get_out: limit %" PRIx64 " count %x%08x\n", t->limit, t->counthigh, - t->count); + trace_slavio_timer_get_out(t->limit, t->counthigh, t->count); t->count = count & TIMER_COUNT_MASK32; t->counthigh = count >> 32; } @@ -126,9 +117,13 @@ CPUTimerState *t = &s->cputimer[tc->timer_index]; slavio_timer_get_out(t); - DPRINTF("callback: count %x%08x\n", t->counthigh, t->count); - t->reached = TIMER_REACHED; - if (!slavio_timer_is_user(tc)) { + trace_slavio_timer_irq(t->counthigh, t->count); + /* if limit is 0 (free-run), there will be no match */ + if (t->limit != 0) { + t->reached = TIMER_REACHED; + } + /* there is no interrupt if user timer or free-run */ + if (!slavio_timer_is_user(tc) && t->limit != 0) { qemu_irq_raise(t->irq); } } @@ -184,12 +179,11 @@ ret = s->cputimer_mode; break; default: - DPRINTF("invalid read address " TARGET_FMT_plx "\n", addr); + trace_slavio_timer_mem_readl_invalid(addr); ret = 0; break; } - DPRINTF("read " TARGET_FMT_plx " = %08x\n", addr, ret); - + trace_slavio_timer_mem_readl(addr, ret); return ret; } @@ -202,7 +196,7 @@ unsigned int timer_index = tc->timer_index; CPUTimerState *t = &s->cputimer[timer_index]; - DPRINTF("write " TARGET_FMT_plx " %08x\n", addr, val); + trace_slavio_timer_mem_writel(addr, val); saddr = addr >> 2; switch (saddr) { case TIMER_LIMIT: @@ -214,8 +208,7 @@ t->counthigh = val & (TIMER_MAX_COUNT64 >> 32); t->reached = 0; count = ((uint64_t)t->counthigh << 32) | t->count; - DPRINTF("processor %d user timer set to %016" PRIx64 "\n", - timer_index, count); + trace_slavio_timer_mem_writel_limit(timer_index, count); ptimer_set_count(t->timer, LIMIT_TO_PERIODS(t->limit - count)); } else { // set limit, reset counter @@ -240,11 +233,11 @@ t->count = val & TIMER_MAX_COUNT64; t->reached = 0; count = ((uint64_t)t->counthigh) << 32 | t->count; - DPRINTF("processor %d user timer set to %016" PRIx64 "\n", - timer_index, count); + trace_slavio_timer_mem_writel_limit(timer_index, count); ptimer_set_count(t->timer, LIMIT_TO_PERIODS(t->limit - count)); - } else - DPRINTF("not user timer\n"); + } else { + trace_slavio_timer_mem_writel_counter_invalid(); + } break; case TIMER_COUNTER_NORST: // set limit without resetting counter @@ -259,13 +252,11 @@ if (slavio_timer_is_user(tc)) { // start/stop user counter if ((val & 1) && !t->running) { - DPRINTF("processor %d user timer started\n", - timer_index); + trace_slavio_timer_mem_writel_status_start(timer_index); ptimer_run(t->timer, 0); t->running = 1; } else if (!(val & 1) && t->running) { - DPRINTF("processor %d user timer stopped\n", - timer_index); + trace_slavio_timer_mem_writel_status_stop(timer_index); ptimer_stop(t->timer); t->running = 0; } @@ -294,8 +285,7 @@ // set this processors user timer bit in config // register s->cputimer_mode |= processor; - DPRINTF("processor %d changed from counter to user " - "timer\n", timer_index); + trace_slavio_timer_mem_writel_mode_user(timer_index); } else { // user timer -> counter // stop the user timer if it is running if (curr_timer->running) { @@ -307,17 +297,16 @@ // clear this processors user timer bit in config // register s->cputimer_mode &= ~processor; - DPRINTF("processor %d changed from user timer to " - "counter\n", timer_index); + trace_slavio_timer_mem_writel_mode_counter(timer_index); } } } } else { - DPRINTF("not system timer\n"); + trace_slavio_timer_mem_writel_mode_invalid(); } break; default: - DPRINTF("invalid write address " TARGET_FMT_plx "\n", addr); + trace_slavio_timer_mem_writel_invalid(addr); break; } } @@ -373,12 +362,12 @@ curr_timer->limit = 0; curr_timer->count = 0; curr_timer->reached = 0; - if (i < s->num_cpus) { + if (i <= s->num_cpus) { ptimer_set_limit(curr_timer->timer, LIMIT_TO_PERIODS(TIMER_MAX_COUNT32), 1); ptimer_run(curr_timer->timer, 0); + curr_timer->running = 1; } - curr_timer->running = 1; } s->cputimer_mode = 0; } @@ -401,7 +390,8 @@ ptimer_set_period(s->cputimer[i].timer, TIMER_PERIOD); io = cpu_register_io_memory(slavio_timer_mem_read, - slavio_timer_mem_write, tc); + slavio_timer_mem_write, tc, + DEVICE_NATIVE_ENDIAN); if (i == 0) { sysbus_init_mmio(dev, SYS_TIMER_SIZE, io); } else { diff -Nru qemu-kvm-0.12.5+noroms/hw/sm501.c qemu-kvm-0.14.1/hw/sm501.c --- qemu-kvm-0.12.5+noroms/hw/sm501.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/sm501.c 2011-05-11 13:29:46.000000000 +0000 @@ -27,18 +27,21 @@ #include "pc.h" #include "console.h" #include "devices.h" +#include "sysbus.h" +#include "qdev-addr.h" +#include "range.h" /* - * Status: 2008/11/02 + * Status: 2010/05/07 * - Minimum implementation for Linux console : mmio regs and CRT layer. - * - Always updates full screen. + * - 2D grapihcs acceleration partially supported : only fill rectangle. * * TODO: * - Panel support - * - Hardware cursor support * - Touch panel support * - USB support * - UART support + * - More 2D graphics engine support * - Performance tuning */ @@ -434,6 +437,8 @@ /* end of register definitions */ +#define SM501_HWC_WIDTH (64) +#define SM501_HWC_HEIGHT (64) /* SM501 local memory size taken from "linux/drivers/mfd/sm501.c" */ static const uint32_t sm501_mem_local_size[] = { @@ -506,6 +511,19 @@ uint32_t dc_crt_hwc_color_1_2; uint32_t dc_crt_hwc_color_3; + uint32_t twoD_source; + uint32_t twoD_destination; + uint32_t twoD_dimension; + uint32_t twoD_control; + uint32_t twoD_pitch; + uint32_t twoD_foreground; + uint32_t twoD_stretch; + uint32_t twoD_color_compare_mask; + uint32_t twoD_mask; + uint32_t twoD_window_width; + uint32_t twoD_source_base; + uint32_t twoD_destination_base; + } SM501State; static uint32_t get_local_mem_size_index(uint32_t size) @@ -526,6 +544,188 @@ return index; } +/** + * Check the availability of hardware cursor. + * @param crt 0 for PANEL, 1 for CRT. + */ +static inline int is_hwc_enabled(SM501State *state, int crt) +{ + uint32_t addr = crt ? state->dc_crt_hwc_addr : state->dc_panel_hwc_addr; + return addr & 0x80000000; +} + +/** + * Get the address which holds cursor pattern data. + * @param crt 0 for PANEL, 1 for CRT. + */ +static inline uint32_t get_hwc_address(SM501State *state, int crt) +{ + uint32_t addr = crt ? state->dc_crt_hwc_addr : state->dc_panel_hwc_addr; + return (addr & 0x03FFFFF0)/* >> 4*/; +} + +/** + * Get the cursor position in y coordinate. + * @param crt 0 for PANEL, 1 for CRT. + */ +static inline uint32_t get_hwc_y(SM501State *state, int crt) +{ + uint32_t location = crt ? state->dc_crt_hwc_location + : state->dc_panel_hwc_location; + return (location & 0x07FF0000) >> 16; +} + +/** + * Get the cursor position in x coordinate. + * @param crt 0 for PANEL, 1 for CRT. + */ +static inline uint32_t get_hwc_x(SM501State *state, int crt) +{ + uint32_t location = crt ? state->dc_crt_hwc_location + : state->dc_panel_hwc_location; + return location & 0x000007FF; +} + +/** + * Get the cursor position in x coordinate. + * @param crt 0 for PANEL, 1 for CRT. + * @param index 0, 1, 2 or 3 which specifies color of corsor dot. + */ +static inline uint16_t get_hwc_color(SM501State *state, int crt, int index) +{ + uint16_t color_reg = 0; + uint16_t color_565 = 0; + + if (index == 0) { + return 0; + } + + switch (index) { + case 1: + case 2: + color_reg = crt ? state->dc_crt_hwc_color_1_2 + : state->dc_panel_hwc_color_1_2; + break; + case 3: + color_reg = crt ? state->dc_crt_hwc_color_3 + : state->dc_panel_hwc_color_3; + break; + default: + printf("invalid hw cursor color.\n"); + abort(); + } + + switch (index) { + case 1: + case 3: + color_565 = (uint16_t)(color_reg & 0xFFFF); + break; + case 2: + color_565 = (uint16_t)((color_reg >> 16) & 0xFFFF); + break; + } + return color_565; +} + +static int within_hwc_y_range(SM501State *state, int y, int crt) +{ + int hwc_y = get_hwc_y(state, crt); + return (hwc_y <= y && y < hwc_y + SM501_HWC_HEIGHT); +} + +static void sm501_2d_operation(SM501State * s) +{ + /* obtain operation parameters */ + int operation = (s->twoD_control >> 16) & 0x1f; + int rtl = s->twoD_control & 0x8000000; + int src_x = (s->twoD_source >> 16) & 0x01FFF; + int src_y = s->twoD_source & 0xFFFF; + int dst_x = (s->twoD_destination >> 16) & 0x01FFF; + int dst_y = s->twoD_destination & 0xFFFF; + int operation_width = (s->twoD_dimension >> 16) & 0x1FFF; + int operation_height = s->twoD_dimension & 0xFFFF; + uint32_t color = s->twoD_foreground; + int format_flags = (s->twoD_stretch >> 20) & 0x3; + int addressing = (s->twoD_stretch >> 16) & 0xF; + + /* get frame buffer info */ + uint8_t * src = s->local_mem + (s->twoD_source_base & 0x03FFFFFF); + uint8_t * dst = s->local_mem + (s->twoD_destination_base & 0x03FFFFFF); + int src_width = (s->dc_crt_h_total & 0x00000FFF) + 1; + int dst_width = (s->dc_crt_h_total & 0x00000FFF) + 1; + + if (addressing != 0x0) { + printf("%s: only XY addressing is supported.\n", __func__); + abort(); + } + + if ((s->twoD_source_base & 0x08000000) || + (s->twoD_destination_base & 0x08000000)) { + printf("%s: only local memory is supported.\n", __func__); + abort(); + } + + switch (operation) { + case 0x00: /* copy area */ +#define COPY_AREA(_bpp, _pixel_type, rtl) { \ + int y, x, index_d, index_s; \ + for (y = 0; y < operation_height; y++) { \ + for (x = 0; x < operation_width; x++) { \ + if (rtl) { \ + index_s = ((src_y - y) * src_width + src_x - x) * _bpp; \ + index_d = ((dst_y - y) * dst_width + dst_x - x) * _bpp; \ + } else { \ + index_s = ((src_y + y) * src_width + src_x + x) * _bpp; \ + index_d = ((dst_y + y) * dst_width + dst_x + x) * _bpp; \ + } \ + *(_pixel_type*)&dst[index_d] = *(_pixel_type*)&src[index_s];\ + } \ + } \ + } + switch (format_flags) { + case 0: + COPY_AREA(1, uint8_t, rtl); + break; + case 1: + COPY_AREA(2, uint16_t, rtl); + break; + case 2: + COPY_AREA(4, uint32_t, rtl); + break; + } + break; + + case 0x01: /* fill rectangle */ +#define FILL_RECT(_bpp, _pixel_type) { \ + int y, x; \ + for (y = 0; y < operation_height; y++) { \ + for (x = 0; x < operation_width; x++) { \ + int index = ((dst_y + y) * dst_width + dst_x + x) * _bpp; \ + *(_pixel_type*)&dst[index] = (_pixel_type)color; \ + } \ + } \ + } + + switch (format_flags) { + case 0: + FILL_RECT(1, uint8_t); + break; + case 1: + FILL_RECT(2, uint16_t); + break; + case 2: + FILL_RECT(4, uint32_t); + break; + } + break; + + default: + printf("non-implemented SM501 2D operation. %d\n", operation); + abort(); + break; + } +} + static uint32_t sm501_system_config_read(void *opaque, target_phys_addr_t addr) { SM501State * s = (SM501State *)opaque; @@ -572,7 +772,7 @@ default: printf("sm501 system config : not implemented register read." " addr=%x\n", (int)addr); - assert(0); + abort(); } return ret; @@ -622,7 +822,7 @@ default: printf("sm501 system config : not implemented register write." " addr=%x, val=%x\n", (int)addr, value); - assert(0); + abort(); } } @@ -646,7 +846,7 @@ /* TODO : consider BYTE/WORD access */ /* TODO : consider endian */ - assert(0 <= addr && addr < 0x400 * 3); + assert(range_covers_byte(0, 0x400 * 3, addr)); return *(uint32_t*)&s->dc_palette[addr]; } @@ -660,7 +860,7 @@ /* TODO : consider BYTE/WORD access */ /* TODO : consider endian */ - assert(0 <= addr && addr < 0x400 * 3); + assert(range_covers_byte(0, 0x400 * 3, addr)); *(uint32_t*)&s->dc_palette[addr] = value; } @@ -736,13 +936,13 @@ ret = s->dc_crt_hwc_addr; break; case SM501_DC_CRT_HWC_LOC: - ret = s->dc_crt_hwc_addr; + ret = s->dc_crt_hwc_location; break; case SM501_DC_CRT_HWC_COLOR_1_2: - ret = s->dc_crt_hwc_addr; + ret = s->dc_crt_hwc_color_1_2; break; case SM501_DC_CRT_HWC_COLOR_3: - ret = s->dc_crt_hwc_addr; + ret = s->dc_crt_hwc_color_3; break; case SM501_DC_PANEL_PALETTE ... SM501_DC_PANEL_PALETTE + 0x400*3 - 4: @@ -752,7 +952,7 @@ default: printf("sm501 disp ctrl : not implemented register read." " addr=%x\n", (int)addr); - assert(0); + abort(); } return ret; @@ -809,13 +1009,13 @@ s->dc_panel_hwc_addr = value & 0x8FFFFFF0; break; case SM501_DC_PANEL_HWC_LOC: - s->dc_panel_hwc_addr = value & 0x0FFF0FFF; + s->dc_panel_hwc_location = value & 0x0FFF0FFF; break; case SM501_DC_PANEL_HWC_COLOR_1_2: - s->dc_panel_hwc_addr = value; + s->dc_panel_hwc_color_1_2 = value; break; case SM501_DC_PANEL_HWC_COLOR_3: - s->dc_panel_hwc_addr = value & 0x0000FFFF; + s->dc_panel_hwc_color_3 = value & 0x0000FFFF; break; case SM501_DC_CRT_CONTROL: @@ -844,13 +1044,13 @@ s->dc_crt_hwc_addr = value & 0x8FFFFFF0; break; case SM501_DC_CRT_HWC_LOC: - s->dc_crt_hwc_addr = value & 0x0FFF0FFF; + s->dc_crt_hwc_location = value & 0x0FFF0FFF; break; case SM501_DC_CRT_HWC_COLOR_1_2: - s->dc_crt_hwc_addr = value; + s->dc_crt_hwc_color_1_2 = value; break; case SM501_DC_CRT_HWC_COLOR_3: - s->dc_crt_hwc_addr = value & 0x0000FFFF; + s->dc_crt_hwc_color_3 = value & 0x0000FFFF; break; case SM501_DC_PANEL_PALETTE ... SM501_DC_PANEL_PALETTE + 0x400*3 - 4: @@ -860,7 +1060,7 @@ default: printf("sm501 disp ctrl : not implemented register write." " addr=%x, val=%x\n", (int)addr, value); - assert(0); + abort(); } } @@ -876,6 +1076,95 @@ &sm501_disp_ctrl_write, }; +static uint32_t sm501_2d_engine_read(void *opaque, target_phys_addr_t addr) +{ + SM501State * s = (SM501State *)opaque; + uint32_t ret = 0; + SM501_DPRINTF("sm501 2d engine regs : read addr=%x\n", (int)addr); + + switch(addr) { + case SM501_2D_SOURCE_BASE: + ret = s->twoD_source_base; + break; + default: + printf("sm501 disp ctrl : not implemented register read." + " addr=%x\n", (int)addr); + abort(); + } + + return ret; +} + +static void sm501_2d_engine_write(void *opaque, + target_phys_addr_t addr, uint32_t value) +{ + SM501State * s = (SM501State *)opaque; + SM501_DPRINTF("sm501 2d engine regs : write addr=%x, val=%x\n", + addr, value); + + switch(addr) { + case SM501_2D_SOURCE: + s->twoD_source = value; + break; + case SM501_2D_DESTINATION: + s->twoD_destination = value; + break; + case SM501_2D_DIMENSION: + s->twoD_dimension = value; + break; + case SM501_2D_CONTROL: + s->twoD_control = value; + + /* do 2d operation if start flag is set. */ + if (value & 0x80000000) { + sm501_2d_operation(s); + s->twoD_control &= ~0x80000000; /* start flag down */ + } + + break; + case SM501_2D_PITCH: + s->twoD_pitch = value; + break; + case SM501_2D_FOREGROUND: + s->twoD_foreground = value; + break; + case SM501_2D_STRETCH: + s->twoD_stretch = value; + break; + case SM501_2D_COLOR_COMPARE_MASK: + s->twoD_color_compare_mask = value; + break; + case SM501_2D_MASK: + s->twoD_mask = value; + break; + case SM501_2D_WINDOW_WIDTH: + s->twoD_window_width = value; + break; + case SM501_2D_SOURCE_BASE: + s->twoD_source_base = value; + break; + case SM501_2D_DESTINATION_BASE: + s->twoD_destination_base = value; + break; + default: + printf("sm501 2d engine : not implemented register write." + " addr=%x, val=%x\n", (int)addr, value); + abort(); + } +} + +static CPUReadMemoryFunc * const sm501_2d_engine_readfn[] = { + NULL, + NULL, + &sm501_2d_engine_read, +}; + +static CPUWriteMemoryFunc * const sm501_2d_engine_writefn[] = { + NULL, + NULL, + &sm501_2d_engine_write, +}; + /* draw line functions for all console modes */ #include "pixel_ops.h" @@ -883,6 +1172,9 @@ typedef void draw_line_func(uint8_t *d, const uint8_t *s, int width, const uint32_t *pal); +typedef void draw_hwc_line_func(SM501State * s, int crt, uint8_t * palette, + int c_y, uint8_t *d, int width); + #define DEPTH 8 #include "sm501_template.h" @@ -937,6 +1229,16 @@ draw_line32_16bgr, }; +static draw_hwc_line_func * draw_hwc_line_funcs[] = { + draw_hwc_line_8, + draw_hwc_line_15, + draw_hwc_line_16, + draw_hwc_line_32, + draw_hwc_line_32bgr, + draw_hwc_line_15bgr, + draw_hwc_line_16bgr, +}; + static inline int get_depth_index(DisplayState *s) { switch(ds_get_bits_per_pixel(s)) { @@ -966,12 +1268,14 @@ int dst_bpp = ds_get_bytes_per_pixel(s->ds) + (ds_get_bits_per_pixel(s->ds) % 8 ? 1 : 0); uint32_t * palette = (uint32_t *)&s->dc_palette[SM501_DC_CRT_PALETTE - SM501_DC_PANEL_PALETTE]; + uint8_t hwc_palette[3 * 3]; int ds_depth_index = get_depth_index(s->ds); draw_line_func * draw_line = NULL; + draw_hwc_line_func * draw_hwc_line = NULL; int full_update = 0; int y_start = -1; - int page_min = 0x7fffffff; - int page_max = -1; + ram_addr_t page_min = ~0l; + ram_addr_t page_max = 0l; ram_addr_t offset = s->local_mem_offset; /* choose draw_line function */ @@ -991,10 +1295,26 @@ default: printf("sm501 draw crt : invalid DC_CRT_CONTROL=%x.\n", s->dc_crt_control); - assert(0); + abort(); break; } + /* set up to draw hardware cursor */ + if (is_hwc_enabled(s, 1)) { + int i; + + /* get cursor palette */ + for (i = 0; i < 3; i++) { + uint16_t rgb565 = get_hwc_color(s, 1, i + 1); + hwc_palette[i * 3 + 0] = (rgb565 & 0xf800) >> 8; /* red */ + hwc_palette[i * 3 + 1] = (rgb565 & 0x07e0) >> 3; /* green */ + hwc_palette[i * 3 + 2] = (rgb565 & 0x001f) << 3; /* blue */ + } + + /* choose cursor draw line function */ + draw_hwc_line = draw_hwc_line_funcs[ds_depth_index]; + } + /* adjust console size */ if (s->last_width != width || s->last_height != height) { qemu_console_resize(s->ds, width, height); @@ -1005,7 +1325,8 @@ /* draw each line according to conditions */ for (y = 0; y < height; y++) { - int update = full_update; + int update_hwc = draw_hwc_line ? within_hwc_y_range(s, y, 1) : 0; + int update = full_update || update_hwc; ram_addr_t page0 = offset & TARGET_PAGE_MASK; ram_addr_t page1 = (offset + width * src_bpp - 1) & TARGET_PAGE_MASK; ram_addr_t page; @@ -1017,7 +1338,16 @@ /* draw line and change status */ if (update) { - draw_line(&(ds_get_data(s->ds)[y * width * dst_bpp]), src, width, palette); + uint8_t * d = &(ds_get_data(s->ds)[y * width * dst_bpp]); + + /* draw graphics layer */ + draw_line(d, src, width, palette); + + /* draw haredware cursor */ + if (update_hwc) { + draw_hwc_line(s, 1, hwc_palette, y - get_hwc_y(s, 1), d, width); + } + if (y_start < 0) y_start = y; if (page0 < page_min) @@ -1041,9 +1371,10 @@ dpy_update(s->ds, 0, y_start, width, y - y_start); /* clear dirty flags */ - if (page_max != -1) + if (page_min != ~0l) { cpu_physical_memory_reset_dirty(page_min, page_max + TARGET_PAGE_SIZE, VGA_DIRTY_FLAG); + } } static void sm501_update_display(void *opaque) @@ -1058,8 +1389,10 @@ CharDriverState *chr) { SM501State * s; + DeviceState *dev; int sm501_system_config_index; int sm501_disp_ctrl_index; + int sm501_2d_engine_index; /* allocate management data region */ s = (SM501State *)qemu_mallocz(sizeof(SM501State)); @@ -1074,30 +1407,49 @@ s->dc_crt_control = 0x00010000; /* allocate local memory */ - s->local_mem_offset = qemu_ram_alloc(local_mem_bytes); + s->local_mem_offset = qemu_ram_alloc(NULL, "sm501.local", local_mem_bytes); s->local_mem = qemu_get_ram_ptr(s->local_mem_offset); cpu_register_physical_memory(base, local_mem_bytes, s->local_mem_offset); /* map mmio */ sm501_system_config_index = cpu_register_io_memory(sm501_system_config_readfn, - sm501_system_config_writefn, s); + sm501_system_config_writefn, s, + DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base + MMIO_BASE_OFFSET, 0x6c, sm501_system_config_index); sm501_disp_ctrl_index = cpu_register_io_memory(sm501_disp_ctrl_readfn, - sm501_disp_ctrl_writefn, s); + sm501_disp_ctrl_writefn, s, + DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base + MMIO_BASE_OFFSET + SM501_DC, 0x1000, sm501_disp_ctrl_index); + sm501_2d_engine_index = cpu_register_io_memory(sm501_2d_engine_readfn, + sm501_2d_engine_writefn, s, + DEVICE_NATIVE_ENDIAN); + cpu_register_physical_memory(base + MMIO_BASE_OFFSET + SM501_2D_ENGINE, + 0x54, sm501_2d_engine_index); /* bridge to usb host emulation module */ - usb_ohci_init_sm501(base + MMIO_BASE_OFFSET + SM501_USB_HOST, base, - 2, -1, irq); + dev = qdev_create(NULL, "sysbus-ohci"); + qdev_prop_set_uint32(dev, "num-ports", 2); + qdev_prop_set_taddr(dev, "dma-offset", base); + qdev_init_nofail(dev); + sysbus_mmio_map(sysbus_from_qdev(dev), 0, + base + MMIO_BASE_OFFSET + SM501_USB_HOST); + sysbus_connect_irq(sysbus_from_qdev(dev), 0, irq); /* bridge to serial emulation module */ - if (chr) - serial_mm_init(base + MMIO_BASE_OFFSET + SM501_UART0, 2, - NULL, /* TODO : chain irq to IRL */ - 115200, chr, 1); + if (chr) { +#ifdef TARGET_WORDS_BIGENDIAN + serial_mm_init(base + MMIO_BASE_OFFSET + SM501_UART0, 2, + NULL, /* TODO : chain irq to IRL */ + 115200, chr, 1, 1); +#else + serial_mm_init(base + MMIO_BASE_OFFSET + SM501_UART0, 2, + NULL, /* TODO : chain irq to IRL */ + 115200, chr, 1, 0); +#endif + } /* create qemu graphic console */ s->ds = graphic_console_init(sm501_update_display, NULL, diff -Nru qemu-kvm-0.12.5+noroms/hw/sm501_template.h qemu-kvm-0.14.1/hw/sm501_template.h --- qemu-kvm-0.12.5+noroms/hw/sm501_template.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/sm501_template.h 2011-05-11 13:29:46.000000000 +0000 @@ -96,6 +96,48 @@ } while (-- width != 0); } +/** + * Draw hardware cursor image on the given line. + */ +static void glue(draw_hwc_line_, PIXEL_NAME)(SM501State * s, int crt, + uint8_t * palette, int c_y, uint8_t *d, int width) +{ + int x, i; + uint8_t bitset = 0; + + /* get hardware cursor pattern */ + uint32_t cursor_addr = get_hwc_address(s, crt); + assert(0 <= c_y && c_y < SM501_HWC_HEIGHT); + cursor_addr += 64 * c_y / 4; /* 4 pixels per byte */ + cursor_addr += s->base; + + /* get cursor position */ + x = get_hwc_x(s, crt); + d += x * BPP; + + for (i = 0; i < SM501_HWC_WIDTH && x + i < width; i++) { + uint8_t v; + + /* get pixel value */ + if (i % 4 == 0) { + cpu_physical_memory_rw(cursor_addr, &bitset, 1, 0); + cursor_addr++; + } + v = bitset & 3; + bitset >>= 2; + + /* write pixel */ + if (v) { + v--; + uint8_t r = palette[v * 3 + 0]; + uint8_t g = palette[v * 3 + 1]; + uint8_t b = palette[v * 3 + 2]; + ((PIXEL_TYPE *) d)[0] = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b); + } + d += BPP; + } +} + #undef DEPTH #undef BPP #undef PIXEL_TYPE diff -Nru qemu-kvm-0.12.5+noroms/hw/smc91c111.c qemu-kvm-0.14.1/hw/smc91c111.c --- qemu-kvm-0.12.5+noroms/hw/smc91c111.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/smc91c111.c 2011-05-11 13:29:46.000000000 +0000 @@ -46,6 +46,35 @@ int mmio_index; } smc91c111_state; +static const VMStateDescription vmstate_smc91c111 = { + .name = "smc91c111", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField []) { + VMSTATE_UINT16(tcr, smc91c111_state), + VMSTATE_UINT16(rcr, smc91c111_state), + VMSTATE_UINT16(cr, smc91c111_state), + VMSTATE_UINT16(ctr, smc91c111_state), + VMSTATE_UINT16(gpr, smc91c111_state), + VMSTATE_UINT16(ptr, smc91c111_state), + VMSTATE_UINT16(ercv, smc91c111_state), + VMSTATE_INT32(bank, smc91c111_state), + VMSTATE_INT32(packet_num, smc91c111_state), + VMSTATE_INT32(tx_alloc, smc91c111_state), + VMSTATE_INT32(allocated, smc91c111_state), + VMSTATE_INT32(tx_fifo_len, smc91c111_state), + VMSTATE_INT32_ARRAY(tx_fifo, smc91c111_state, NUM_PACKETS), + VMSTATE_INT32(rx_fifo_len, smc91c111_state), + VMSTATE_INT32_ARRAY(rx_fifo, smc91c111_state, NUM_PACKETS), + VMSTATE_INT32(tx_fifo_done_len, smc91c111_state), + VMSTATE_INT32_ARRAY(tx_fifo_done, smc91c111_state, NUM_PACKETS), + VMSTATE_BUFFER_UNSAFE(data, smc91c111_state, 0, NUM_PACKETS * 2048), + VMSTATE_UINT8(int_level, smc91c111_state), + VMSTATE_UINT8(int_mask, smc91c111_state), + VMSTATE_END_OF_LIST() + } +}; + #define RCR_SOFT_RST 0x8000 #define RCR_STRIP_CRC 0x0200 #define RCR_RXEN 0x0100 @@ -160,7 +189,6 @@ int i; int len; int control; - int add_crc; int packetnum; uint8_t *p; @@ -187,20 +215,22 @@ len = 64; } #if 0 - /* The card is supposed to append the CRC to the frame. However - none of the other network traffic has the CRC appended. - Suspect this is low level ethernet detail we don't need to worry - about. */ - add_crc = (control & 0x10) || (s->tcr & TCR_NOCRC) == 0; - if (add_crc) { - uint32_t crc; - - crc = crc32(~0, p, len); - memcpy(p + len, &crc, 4); - len += 4; + { + int add_crc; + + /* The card is supposed to append the CRC to the frame. + However none of the other network traffic has the CRC + appended. Suspect this is low level ethernet detail we + don't need to worry about. */ + add_crc = (control & 0x10) || (s->tcr & TCR_NOCRC) == 0; + if (add_crc) { + uint32_t crc; + + crc = crc32(~0, p, len); + memcpy(p + len, &crc, 4); + len += 4; + } } -#else - add_crc = 0; #endif if (s->ctr & CTR_AUTO_RELEASE) /* Race? */ @@ -250,6 +280,7 @@ { smc91c111_state *s = (smc91c111_state *)opaque; + offset = offset & 0xf; if (offset == 14) { s->bank = value; return; @@ -276,6 +307,8 @@ case 10: case 11: /* RPCR */ /* Ignored */ return; + case 12: case 13: /* Reserved */ + return; } break; @@ -421,6 +454,7 @@ { smc91c111_state *s = (smc91c111_state *)opaque; + offset = offset & 0xf; if (offset == 14) { return s->bank; } @@ -461,6 +495,8 @@ case 10: case 11: /* RPCR */ /* Not implemented. */ return 0; + case 12: case 13: /* Reserved */ + return 0; } break; @@ -664,14 +700,14 @@ *(p++) = crc & 0xff; crc >>= 8; *(p++) = crc & 0xff; crc >>= 8; *(p++) = crc & 0xff; crc >>= 8; - *(p++) = crc & 0xff; crc >>= 8; + *(p++) = crc & 0xff; } if (size & 1) { *(p++) = buf[size - 1]; - *(p++) = 0x60; + *p = 0x60; } else { *(p++) = 0; - *(p++) = 0x40; + *p = 0x40; } /* TODO: Raise early RX interrupt? */ s->int_level |= INT_RCV; @@ -712,7 +748,8 @@ smc91c111_state *s = FROM_SYSBUS(smc91c111_state, dev); s->mmio_index = cpu_register_io_memory(smc91c111_readfn, - smc91c111_writefn, s); + smc91c111_writefn, s, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, 16, s->mmio_index); sysbus_init_irq(dev, &s->irq); qemu_macaddr_default_if_unset(&s->conf.macaddr); @@ -730,6 +767,7 @@ .init = smc91c111_init1, .qdev.name = "smc91c111", .qdev.size = sizeof(smc91c111_state), + .qdev.vmsd = &vmstate_smc91c111, .qdev.props = (Property[]) { DEFINE_NIC_PROPERTIES(smc91c111_state, conf), DEFINE_PROP_END_OF_LIST(), diff -Nru qemu-kvm-0.12.5+noroms/hw/soc_dma.c qemu-kvm-0.14.1/hw/soc_dma.c --- qemu-kvm-0.12.5+noroms/hw/soc_dma.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/soc_dma.c 2011-05-11 13:29:46.000000000 +0000 @@ -192,12 +192,13 @@ if (s->enabled_count) /* We completely ignore channel priorities and stuff */ s->channel_freq = s->soc.freq / s->enabled_count; - else + else { /* TODO: Signal that we want to disable the functional clock and let * the platform code decide what to do with it, i.e. check that * auto-idle is enabled in the clock controller and if we are stopping * the clock, do the same with any parent clocks that had only one - * user keeping them on and auto-idle enabled. */; + * user keeping them on and auto-idle enabled. */ + } } void soc_dma_set_request(struct soc_dma_ch_s *ch, int level) diff -Nru qemu-kvm-0.12.5+noroms/hw/sparc32_dma.c qemu-kvm-0.14.1/hw/sparc32_dma.c --- qemu-kvm-0.12.5+noroms/hw/sparc32_dma.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/sparc32_dma.c 2011-05-11 13:29:46.000000000 +0000 @@ -3,6 +3,9 @@ * * Copyright (c) 2006 Fabrice Bellard * + * Modifications: + * 2010-Feb-14 Artyom Tarasenko : reworked irq generation + * * 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 @@ -26,9 +29,7 @@ #include "sparc32_dma.h" #include "sun4m.h" #include "sysbus.h" - -/* debug DMA */ -//#define DEBUG_DMA +#include "trace.h" /* * This is the DMA controller part of chip STP2000 (Master I/O), also @@ -38,27 +39,27 @@ * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/DMA2.txt */ -#ifdef DEBUG_DMA -#define DPRINTF(fmt, ...) \ - do { printf("DMA: " fmt , ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) -#endif - #define DMA_REGS 4 #define DMA_SIZE (4 * sizeof(uint32_t)) /* We need the mask, because one instance of the device is not page aligned (ledma, start address 0x0010) */ #define DMA_MASK (DMA_SIZE - 1) +/* OBP says 0x20 bytes for ledma, the extras are aliased to espdma */ +#define DMA_ETH_SIZE (8 * sizeof(uint32_t)) +#define DMA_MAX_REG_OFFSET (2 * DMA_SIZE - 1) #define DMA_VER 0xa0000000 #define DMA_INTR 1 #define DMA_INTREN 0x10 #define DMA_WRITE_MEM 0x100 +#define DMA_EN 0x200 #define DMA_LOADED 0x04000000 #define DMA_DRAIN_FIFO 0x40 #define DMA_RESET 0x80 +/* XXX SCSI and ethernet should have different read-only bit masks */ +#define DMA_CSR_RO_MASK 0xfe000007 + typedef struct DMAState DMAState; struct DMAState { @@ -66,7 +67,13 @@ uint32_t dmaregs[DMA_REGS]; qemu_irq irq; void *iommu; - qemu_irq dev_reset; + qemu_irq gpio[2]; + uint32_t is_ledma; +}; + +enum { + GPIO_RESET = 0, + GPIO_DMA, }; /* Note: on sparc, the lance 16 bit bus is swapped */ @@ -76,9 +83,8 @@ DMAState *s = opaque; int i; - DPRINTF("DMA write, direction: %c, addr 0x%8.8x\n", - s->dmaregs[0] & DMA_WRITE_MEM ? 'w': 'r', s->dmaregs[1]); addr |= s->dmaregs[3]; + trace_ledma_memory_read(addr); if (do_bswap) { sparc_iommu_memory_read(s->iommu, addr, buf, len); } else { @@ -98,9 +104,8 @@ int l, i; uint16_t tmp_buf[32]; - DPRINTF("DMA read, direction: %c, addr 0x%8.8x\n", - s->dmaregs[0] & DMA_WRITE_MEM ? 'w': 'r', s->dmaregs[1]); addr |= s->dmaregs[3]; + trace_ledma_memory_write(addr); if (do_bswap) { sparc_iommu_memory_write(s->iommu, addr, buf, len); } else { @@ -125,13 +130,19 @@ { DMAState *s = opaque; if (level) { - DPRINTF("Raise IRQ\n"); s->dmaregs[0] |= DMA_INTR; - qemu_irq_raise(s->irq); + if (s->dmaregs[0] & DMA_INTREN) { + trace_sparc32_dma_set_irq_raise(); + qemu_irq_raise(s->irq); + } } else { - s->dmaregs[0] &= ~DMA_INTR; - DPRINTF("Lower IRQ\n"); - qemu_irq_lower(s->irq); + if (s->dmaregs[0] & DMA_INTR) { + s->dmaregs[0] &= ~DMA_INTR; + if (s->dmaregs[0] & DMA_INTREN) { + trace_sparc32_dma_set_irq_lower(); + qemu_irq_lower(s->irq); + } + } } } @@ -139,10 +150,8 @@ { DMAState *s = opaque; - DPRINTF("DMA read, direction: %c, addr 0x%8.8x\n", - s->dmaregs[0] & DMA_WRITE_MEM ? 'w': 'r', s->dmaregs[1]); + trace_espdma_memory_read(s->dmaregs[1]); sparc_iommu_memory_read(s->iommu, s->dmaregs[1], buf, len); - s->dmaregs[0] |= DMA_INTR; s->dmaregs[1] += len; } @@ -150,10 +159,8 @@ { DMAState *s = opaque; - DPRINTF("DMA write, direction: %c, addr 0x%8.8x\n", - s->dmaregs[0] & DMA_WRITE_MEM ? 'w': 'r', s->dmaregs[1]); + trace_espdma_memory_write(s->dmaregs[1]); sparc_iommu_memory_write(s->iommu, s->dmaregs[1], buf, len); - s->dmaregs[0] |= DMA_INTR; s->dmaregs[1] += len; } @@ -162,10 +169,14 @@ DMAState *s = opaque; uint32_t saddr; + if (s->is_ledma && (addr > DMA_MAX_REG_OFFSET)) { + /* aliased to espdma, but we can't get there from here */ + /* buggy driver if using undocumented behavior, just return 0 */ + trace_sparc32_dma_mem_readl(addr, 0); + return 0; + } saddr = (addr & DMA_MASK) >> 2; - DPRINTF("read dmareg " TARGET_FMT_plx ": 0x%8.8x\n", addr, - s->dmaregs[saddr]); - + trace_sparc32_dma_mem_readl(addr, s->dmaregs[saddr]); return s->dmaregs[saddr]; } @@ -174,32 +185,53 @@ DMAState *s = opaque; uint32_t saddr; + if (s->is_ledma && (addr > DMA_MAX_REG_OFFSET)) { + /* aliased to espdma, but we can't get there from here */ + trace_sparc32_dma_mem_writel(addr, 0, val); + return; + } saddr = (addr & DMA_MASK) >> 2; - DPRINTF("write dmareg " TARGET_FMT_plx ": 0x%8.8x -> 0x%8.8x\n", addr, - s->dmaregs[saddr], val); + trace_sparc32_dma_mem_writel(addr, s->dmaregs[saddr], val); switch (saddr) { case 0: - if (!(val & DMA_INTREN)) { - DPRINTF("Lower IRQ\n"); - qemu_irq_lower(s->irq); + if (val & DMA_INTREN) { + if (s->dmaregs[0] & DMA_INTR) { + trace_sparc32_dma_set_irq_raise(); + qemu_irq_raise(s->irq); + } + } else { + if (s->dmaregs[0] & (DMA_INTR | DMA_INTREN)) { + trace_sparc32_dma_set_irq_lower(); + qemu_irq_lower(s->irq); + } } if (val & DMA_RESET) { - qemu_irq_raise(s->dev_reset); - qemu_irq_lower(s->dev_reset); + qemu_irq_raise(s->gpio[GPIO_RESET]); + qemu_irq_lower(s->gpio[GPIO_RESET]); } else if (val & DMA_DRAIN_FIFO) { val &= ~DMA_DRAIN_FIFO; } else if (val == 0) val = DMA_DRAIN_FIFO; - val &= 0x0fffffff; + + if (val & DMA_EN && !(s->dmaregs[0] & DMA_EN)) { + trace_sparc32_dma_enable_raise(); + qemu_irq_raise(s->gpio[GPIO_DMA]); + } else if (!(val & DMA_EN) && !!(s->dmaregs[0] & DMA_EN)) { + trace_sparc32_dma_enable_lower(); + qemu_irq_lower(s->gpio[GPIO_DMA]); + } + + val &= ~DMA_CSR_RO_MASK; val |= DMA_VER; + s->dmaregs[0] = (s->dmaregs[0] & DMA_CSR_RO_MASK) | val; break; case 1: s->dmaregs[0] |= DMA_LOADED; - break; + /* fall through */ default: + s->dmaregs[saddr] = val; break; } - s->dmaregs[saddr] = val; } static CPUReadMemoryFunc * const dma_mem_read[3] = { @@ -237,14 +269,17 @@ { DMAState *s = FROM_SYSBUS(DMAState, dev); int dma_io_memory; + int reg_size; sysbus_init_irq(dev, &s->irq); - dma_io_memory = cpu_register_io_memory(dma_mem_read, dma_mem_write, s); - sysbus_init_mmio(dev, DMA_SIZE, dma_io_memory); + dma_io_memory = cpu_register_io_memory(dma_mem_read, dma_mem_write, s, + DEVICE_NATIVE_ENDIAN); + reg_size = s->is_ledma ? DMA_ETH_SIZE : DMA_SIZE; + sysbus_init_mmio(dev, reg_size, dma_io_memory); qdev_init_gpio_in(&dev->qdev, dma_set_irq, 1); - qdev_init_gpio_out(&dev->qdev, &s->dev_reset, 1); + qdev_init_gpio_out(&dev->qdev, s->gpio, 2); return 0; } @@ -257,6 +292,7 @@ .qdev.reset = dma_reset, .qdev.props = (Property[]) { DEFINE_PROP_PTR("iommu_opaque", DMAState, iommu), + DEFINE_PROP_UINT32("is_ledma", DMAState, is_ledma, 0), DEFINE_PROP_END_OF_LIST(), } }; diff -Nru qemu-kvm-0.12.5+noroms/hw/spitz.c qemu-kvm-0.14.1/hw/spitz.c --- qemu-kvm-0.12.5+noroms/hw/spitz.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/spitz.c 2011-05-11 13:29:46.000000000 +0000 @@ -22,6 +22,8 @@ #include "block.h" #include "audio/audio.h" #include "boards.h" +#include "blockdev.h" +#include "sysbus.h" #undef REG_FMT #define REG_FMT "0x%02lx" @@ -45,8 +47,11 @@ #define FLASHCTL_NCE (FLASHCTL_CE0 | FLASHCTL_CE1) typedef struct { + SysBusDevice busdev; NANDFlashState *nand; uint8_t ctl; + uint8_t manf_id; + uint8_t chip_id; ECCState ecc; } SLNANDState; @@ -129,56 +134,53 @@ } } -static void sl_save(QEMUFile *f, void *opaque) -{ - SLNANDState *s = (SLNANDState *) opaque; - - qemu_put_8s(f, &s->ctl); - ecc_put(f, &s->ecc); -} - -static int sl_load(QEMUFile *f, void *opaque, int version_id) -{ - SLNANDState *s = (SLNANDState *) opaque; - - qemu_get_8s(f, &s->ctl); - ecc_get(f, &s->ecc); - - return 0; -} - enum { FLASH_128M, FLASH_1024M, }; +static CPUReadMemoryFunc * const sl_readfn[] = { + sl_readb, + sl_readb, + sl_readl, +}; +static CPUWriteMemoryFunc * const sl_writefn[] = { + sl_writeb, + sl_writeb, + sl_writeb, +}; + static void sl_flash_register(PXA2xxState *cpu, int size) { + DeviceState *dev; + + dev = qdev_create(NULL, "sl-nand"); + + qdev_prop_set_uint8(dev, "manf_id", NAND_MFR_SAMSUNG); + if (size == FLASH_128M) + qdev_prop_set_uint8(dev, "chip_id", 0x73); + else if (size == FLASH_1024M) + qdev_prop_set_uint8(dev, "chip_id", 0xf1); + + qdev_init_nofail(dev); + sysbus_mmio_map(sysbus_from_qdev(dev), 0, FLASH_BASE); +} + +static int sl_nand_init(SysBusDevice *dev) { int iomemtype; SLNANDState *s; - CPUReadMemoryFunc * const sl_readfn[] = { - sl_readb, - sl_readb, - sl_readl, - }; - CPUWriteMemoryFunc * const sl_writefn[] = { - sl_writeb, - sl_writeb, - sl_writeb, - }; - s = (SLNANDState *) qemu_mallocz(sizeof(SLNANDState)); + s = FROM_SYSBUS(SLNANDState, dev); + s->ctl = 0; - if (size == FLASH_128M) - s->nand = nand_init(NAND_MFR_SAMSUNG, 0x73); - else if (size == FLASH_1024M) - s->nand = nand_init(NAND_MFR_SAMSUNG, 0xf1); + s->nand = nand_init(s->manf_id, s->chip_id); iomemtype = cpu_register_io_memory(sl_readfn, - sl_writefn, s); - cpu_register_physical_memory(FLASH_BASE, 0x40, iomemtype); + sl_writefn, s, DEVICE_NATIVE_ENDIAN); - register_savevm("sl_flash", 0, 0, sl_save, sl_load, s); + sysbus_init_mmio(dev, 0x40, iomemtype); + + return 0; } /* Spitz Keyboard */ @@ -217,11 +219,10 @@ SPITZ_GPIO_AK_INT, SPITZ_GPIO_SYNC, SPITZ_GPIO_ON_KEY, SPITZ_GPIO_SWA, SPITZ_GPIO_SWB, }; -static int spitz_gpio_invert[5] = { 0, 0, 0, 0, 0, }; typedef struct { + SysBusDevice busdev; qemu_irq sense[SPITZ_KEY_SENSE_NUM]; - qemu_irq *strobe; qemu_irq gpiomap[5]; int keymap[0x80]; uint16_t keyrow[SPITZ_KEY_SENSE_NUM]; @@ -272,8 +273,7 @@ /* Handle the additional keys */ if ((spitz_keycode >> 4) == SPITZ_KEY_SENSE_NUM) { - qemu_set_irq(s->gpiomap[spitz_keycode & 0xf], (keycode < 0x80) ^ - spitz_gpio_invert[spitz_keycode & 0xf]); + qemu_set_irq(s->gpiomap[spitz_keycode & 0xf], (keycode < 0x80)); return; } @@ -291,8 +291,9 @@ #define QUEUE_KEY(c) s->fifo[(s->fifopos + s->fifolen ++) & 0xf] = c -static void spitz_keyboard_handler(SpitzKeyboardState *s, int keycode) +static void spitz_keyboard_handler(void *opaque, int keycode) { + SpitzKeyboardState *s = opaque; uint16_t code; int mapcode; switch (keycode) { @@ -438,34 +439,15 @@ s->imodifiers = 0; s->fifopos = 0; s->fifolen = 0; - s->kbdtimer = qemu_new_timer(vm_clock, spitz_keyboard_tick, s); - spitz_keyboard_tick(s); } #undef SHIFT #undef CTRL #undef FN -static void spitz_keyboard_save(QEMUFile *f, void *opaque) +static int spitz_keyboard_post_load(void *opaque, int version_id) { SpitzKeyboardState *s = (SpitzKeyboardState *) opaque; - int i; - - qemu_put_be16s(f, &s->sense_state); - qemu_put_be16s(f, &s->strobe_state); - for (i = 0; i < 5; i ++) - qemu_put_byte(f, spitz_gpio_invert[i]); -} - -static int spitz_keyboard_load(QEMUFile *f, void *opaque, int version_id) -{ - SpitzKeyboardState *s = (SpitzKeyboardState *) opaque; - int i; - - qemu_get_be16s(f, &s->sense_state); - qemu_get_be16s(f, &s->strobe_state); - for (i = 0; i < 5; i ++) - spitz_gpio_invert[i] = qemu_get_byte(f); /* Release all pressed keys */ memset(s->keyrow, 0, sizeof(s->keyrow)); @@ -480,12 +462,40 @@ static void spitz_keyboard_register(PXA2xxState *cpu) { - int i, j; + int i; + DeviceState *dev; + SpitzKeyboardState *s; + + dev = sysbus_create_simple("spitz-keyboard", -1, NULL); + s = FROM_SYSBUS(SpitzKeyboardState, sysbus_from_qdev(dev)); + + for (i = 0; i < SPITZ_KEY_SENSE_NUM; i ++) + qdev_connect_gpio_out(dev, i, qdev_get_gpio_in(cpu->gpio, spitz_gpio_key_sense[i])); + + for (i = 0; i < 5; i ++) + s->gpiomap[i] = qdev_get_gpio_in(cpu->gpio, spitz_gpiomap[i]); + + if (!graphic_rotate) + s->gpiomap[4] = qemu_irq_invert(s->gpiomap[4]); + + for (i = 0; i < 5; i++) + qemu_set_irq(s->gpiomap[i], 0); + + for (i = 0; i < SPITZ_KEY_STROBE_NUM; i ++) + qdev_connect_gpio_out(cpu->gpio, spitz_gpio_key_strobe[i], + qdev_get_gpio_in(dev, i)); + + qemu_mod_timer(s->kbdtimer, qemu_get_clock(vm_clock)); + + qemu_add_kbd_event_handler(spitz_keyboard_handler, s); +} + +static int spitz_keyboard_init(SysBusDevice *dev) +{ SpitzKeyboardState *s; + int i, j; - s = (SpitzKeyboardState *) - qemu_mallocz(sizeof(SpitzKeyboardState)); - memset(s, 0, sizeof(SpitzKeyboardState)); + s = FROM_SYSBUS(SpitzKeyboardState, dev); for (i = 0; i < 0x80; i ++) s->keymap[i] = -1; @@ -494,22 +504,13 @@ if (spitz_keymap[i][j] != -1) s->keymap[spitz_keymap[i][j]] = (i << 4) | j; - for (i = 0; i < SPITZ_KEY_SENSE_NUM; i ++) - s->sense[i] = pxa2xx_gpio_in_get(cpu->gpio)[spitz_gpio_key_sense[i]]; - - for (i = 0; i < 5; i ++) - s->gpiomap[i] = pxa2xx_gpio_in_get(cpu->gpio)[spitz_gpiomap[i]]; - - s->strobe = qemu_allocate_irqs(spitz_keyboard_strobe, s, - SPITZ_KEY_STROBE_NUM); - for (i = 0; i < SPITZ_KEY_STROBE_NUM; i ++) - pxa2xx_gpio_out_set(cpu->gpio, spitz_gpio_key_strobe[i], s->strobe[i]); - spitz_keyboard_pre_map(s); - qemu_add_kbd_event_handler((QEMUPutKBDEvent *) spitz_keyboard_handler, s); - register_savevm("spitz_keyboard", 0, 0, - spitz_keyboard_save, spitz_keyboard_load, s); + s->kbdtimer = qemu_new_timer(vm_clock, spitz_keyboard_tick, s); + qdev_init_gpio_in(&dev->qdev, spitz_keyboard_strobe, SPITZ_KEY_STROBE_NUM); + qdev_init_gpio_out(&dev->qdev, s->sense, SPITZ_KEY_SENSE_NUM); + + return 0; } /* LCD backlight controller */ @@ -525,8 +526,8 @@ typedef struct { SSISlave ssidev; - int bl_intensity; - int bl_power; + uint32_t bl_intensity; + uint32_t bl_power; } SpitzLCDTG; static void spitz_bl_update(SpitzLCDTG *s) @@ -590,21 +591,6 @@ return 0; } -static void spitz_lcdtg_save(QEMUFile *f, void *opaque) -{ - SpitzLCDTG *s = (SpitzLCDTG *)opaque; - qemu_put_be32(f, s->bl_intensity); - qemu_put_be32(f, s->bl_power); -} - -static int spitz_lcdtg_load(QEMUFile *f, void *opaque, int version_id) -{ - SpitzLCDTG *s = (SpitzLCDTG *)opaque; - s->bl_intensity = qemu_get_be32(f); - s->bl_power = qemu_get_be32(f); - return 0; -} - static int spitz_lcdtg_init(SSISlave *dev) { SpitzLCDTG *s = FROM_SSI_SLAVE(SpitzLCDTG, dev); @@ -613,8 +599,6 @@ s->bl_power = 0; s->bl_intensity = 0x20; - register_savevm("spitz-lcdtg", -1, 1, - spitz_lcdtg_save, spitz_lcdtg_load, s); return 0; } @@ -633,7 +617,7 @@ typedef struct { SSISlave ssidev; SSIBus *bus[3]; - int enable[3]; + uint32_t enable[3]; } CorgiSSPState; static uint32_t corgi_ssp_transfer(SSISlave *dev, uint32_t value) @@ -675,30 +659,6 @@ max111x_set_input(max1111, MAX1111_BATT_TEMP, 0); } -static void spitz_ssp_save(QEMUFile *f, void *opaque) -{ - CorgiSSPState *s = (CorgiSSPState *)opaque; - int i; - - for (i = 0; i < 3; i++) { - qemu_put_be32(f, s->enable[i]); - } -} - -static int spitz_ssp_load(QEMUFile *f, void *opaque, int version_id) -{ - CorgiSSPState *s = (CorgiSSPState *)opaque; - int i; - - if (version_id != 1) { - return -EINVAL; - } - for (i = 0; i < 3; i++) { - s->enable[i] = qemu_get_be32(f); - } - return 0; -} - static int corgi_ssp_init(SSISlave *dev) { CorgiSSPState *s = FROM_SSI_SLAVE(CorgiSSPState, dev); @@ -708,7 +668,6 @@ s->bus[1] = ssi_create_bus(&dev->qdev, "ssi1"); s->bus[2] = ssi_create_bus(&dev->qdev, "ssi2"); - register_savevm("spitz_ssp", -1, 1, spitz_ssp_save, spitz_ssp_load, s); return 0; } @@ -721,12 +680,12 @@ mux = ssi_create_slave(cpu->ssp[CORGI_SSP_PORT - 1], "corgi-ssp"); bus = qdev_get_child_bus(mux, "ssi0"); - dev = ssi_create_slave(bus, "spitz-lcdtg"); + ssi_create_slave(bus, "spitz-lcdtg"); bus = qdev_get_child_bus(mux, "ssi1"); dev = ssi_create_slave(bus, "ads7846"); qdev_connect_gpio_out(dev, 0, - pxa2xx_gpio_in_get(cpu->gpio)[SPITZ_GPIO_TP_INT]); + qdev_get_gpio_in(cpu->gpio, SPITZ_GPIO_TP_INT)); bus = qdev_get_child_bus(mux, "ssi2"); max1111 = ssi_create_slave(bus, "max1111"); @@ -734,11 +693,11 @@ max111x_set_input(max1111, MAX1111_BATT_TEMP, 0); max111x_set_input(max1111, MAX1111_ACIN_VOLT, SPITZ_CHARGEON_ACIN); - pxa2xx_gpio_out_set(cpu->gpio, SPITZ_GPIO_LCDCON_CS, + qdev_connect_gpio_out(cpu->gpio, SPITZ_GPIO_LCDCON_CS, qdev_get_gpio_in(mux, 0)); - pxa2xx_gpio_out_set(cpu->gpio, SPITZ_GPIO_ADS7846_CS, + qdev_connect_gpio_out(cpu->gpio, SPITZ_GPIO_ADS7846_CS, qdev_get_gpio_in(mux, 1)); - pxa2xx_gpio_out_set(cpu->gpio, SPITZ_GPIO_MAX1111_CS, + qdev_connect_gpio_out(cpu->gpio, SPITZ_GPIO_MAX1111_CS, qdev_get_gpio_in(mux, 2)); } @@ -768,7 +727,6 @@ #define SPITZ_GPIO_WM 5 -#ifdef HAS_AUDIO static void spitz_wm8750_addr(void *opaque, int line, int level) { i2c_slave *wm = (i2c_slave *) opaque; @@ -777,28 +735,25 @@ else i2c_set_slave_address(wm, SPITZ_WM_ADDRL); } -#endif static void spitz_i2c_setup(PXA2xxState *cpu) { /* Attach the CPU on one end of our I2C bus. */ i2c_bus *bus = pxa2xx_i2c_bus(cpu->i2c[0]); -#ifdef HAS_AUDIO DeviceState *wm; /* Attach a WM8750 to the bus */ wm = i2c_create_slave(bus, "wm8750", 0); spitz_wm8750_addr(wm, 0, 0); - pxa2xx_gpio_out_set(cpu->gpio, SPITZ_GPIO_WM, + qdev_connect_gpio_out(cpu->gpio, SPITZ_GPIO_WM, qemu_allocate_irqs(spitz_wm8750_addr, wm, 1)[0]); /* .. and to the sound interface. */ cpu->i2s->opaque = wm; cpu->i2s->codec_out = wm8750_dac_dat; cpu->i2s->codec_in = wm8750_adc_dat; wm8750_data_req_set(wm, cpu->i2s->data_req, cpu->i2s); -#endif } static void spitz_akita_i2c_setup(PXA2xxState *cpu) @@ -853,21 +808,21 @@ #define SPITZ_SCP2_MIC_BIAS 9 static void spitz_scoop_gpio_setup(PXA2xxState *cpu, - ScoopInfo *scp0, ScoopInfo *scp1) + DeviceState *scp0, DeviceState *scp1) { qemu_irq *outsignals = qemu_allocate_irqs(spitz_out_switch, cpu, 8); - scoop_gpio_out_set(scp0, SPITZ_SCP_CHRG_ON, outsignals[0]); - scoop_gpio_out_set(scp0, SPITZ_SCP_JK_B, outsignals[1]); - scoop_gpio_out_set(scp0, SPITZ_SCP_LED_GREEN, outsignals[2]); - scoop_gpio_out_set(scp0, SPITZ_SCP_LED_ORANGE, outsignals[3]); + qdev_connect_gpio_out(scp0, SPITZ_SCP_CHRG_ON, outsignals[0]); + qdev_connect_gpio_out(scp0, SPITZ_SCP_JK_B, outsignals[1]); + qdev_connect_gpio_out(scp0, SPITZ_SCP_LED_GREEN, outsignals[2]); + qdev_connect_gpio_out(scp0, SPITZ_SCP_LED_ORANGE, outsignals[3]); if (scp1) { - scoop_gpio_out_set(scp1, SPITZ_SCP2_BACKLIGHT_CONT, outsignals[4]); - scoop_gpio_out_set(scp1, SPITZ_SCP2_BACKLIGHT_ON, outsignals[5]); + qdev_connect_gpio_out(scp1, SPITZ_SCP2_BACKLIGHT_CONT, outsignals[4]); + qdev_connect_gpio_out(scp1, SPITZ_SCP2_BACKLIGHT_ON, outsignals[5]); } - scoop_gpio_out_set(scp0, SPITZ_SCP_ADC_TEMP_ON, outsignals[6]); + qdev_connect_gpio_out(scp0, SPITZ_SCP_ADC_TEMP_ON, outsignals[6]); } #define SPITZ_GPIO_HSYNC 22 @@ -885,7 +840,7 @@ static void spitz_lcd_hsync_handler(void *opaque, int line, int level) { PXA2xxState *cpu = (PXA2xxState *) opaque; - qemu_set_irq(pxa2xx_gpio_in_get(cpu->gpio)[SPITZ_GPIO_HSYNC], spitz_hsync); + qemu_set_irq(qdev_get_gpio_in(cpu->gpio, SPITZ_GPIO_HSYNC), spitz_hsync); spitz_hsync ^= 1; } @@ -905,36 +860,24 @@ /* MMC/SD host */ pxa2xx_mmci_handlers(cpu->mmc, - pxa2xx_gpio_in_get(cpu->gpio)[SPITZ_GPIO_SD_WP], - pxa2xx_gpio_in_get(cpu->gpio)[SPITZ_GPIO_SD_DETECT]); + qdev_get_gpio_in(cpu->gpio, SPITZ_GPIO_SD_WP), + qdev_get_gpio_in(cpu->gpio, SPITZ_GPIO_SD_DETECT)); /* Battery lock always closed */ - qemu_irq_raise(pxa2xx_gpio_in_get(cpu->gpio)[SPITZ_GPIO_BAT_COVER]); + qemu_irq_raise(qdev_get_gpio_in(cpu->gpio, SPITZ_GPIO_BAT_COVER)); /* Handle reset */ - pxa2xx_gpio_out_set(cpu->gpio, SPITZ_GPIO_ON_RESET, cpu->reset); + qdev_connect_gpio_out(cpu->gpio, SPITZ_GPIO_ON_RESET, cpu->reset); /* PCMCIA signals: card's IRQ and Card-Detect */ if (slots >= 1) pxa2xx_pcmcia_set_irq_cb(cpu->pcmcia[0], - pxa2xx_gpio_in_get(cpu->gpio)[SPITZ_GPIO_CF1_IRQ], - pxa2xx_gpio_in_get(cpu->gpio)[SPITZ_GPIO_CF1_CD]); + qdev_get_gpio_in(cpu->gpio, SPITZ_GPIO_CF1_IRQ), + qdev_get_gpio_in(cpu->gpio, SPITZ_GPIO_CF1_CD)); if (slots >= 2) pxa2xx_pcmcia_set_irq_cb(cpu->pcmcia[1], - pxa2xx_gpio_in_get(cpu->gpio)[SPITZ_GPIO_CF2_IRQ], - pxa2xx_gpio_in_get(cpu->gpio)[SPITZ_GPIO_CF2_CD]); - - /* Initialise the screen rotation related signals */ - spitz_gpio_invert[3] = 0; /* Always open */ - if (graphic_rotate) { /* Tablet mode */ - spitz_gpio_invert[4] = 0; - } else { /* Portrait mode */ - spitz_gpio_invert[4] = 1; - } - qemu_set_irq(pxa2xx_gpio_in_get(cpu->gpio)[SPITZ_GPIO_SWA], - spitz_gpio_invert[3]); - qemu_set_irq(pxa2xx_gpio_in_get(cpu->gpio)[SPITZ_GPIO_SWB], - spitz_gpio_invert[4]); + qdev_get_gpio_in(cpu->gpio, SPITZ_GPIO_CF2_IRQ), + qdev_get_gpio_in(cpu->gpio, SPITZ_GPIO_CF2_CD)); } /* Board init. */ @@ -954,7 +897,7 @@ const char *cpu_model, enum spitz_model_e model, int arm_id) { PXA2xxState *cpu; - ScoopInfo *scp0, *scp1 = NULL; + DeviceState *scp0, *scp1 = NULL; if (!cpu_model) cpu_model = (model == terrier) ? "pxa270-c5" : "pxa270-c0"; @@ -965,16 +908,16 @@ sl_flash_register(cpu, (model == spitz) ? FLASH_128M : FLASH_1024M); cpu_register_physical_memory(0, SPITZ_ROM, - qemu_ram_alloc(SPITZ_ROM) | IO_MEM_ROM); + qemu_ram_alloc(NULL, "spitz.rom", SPITZ_ROM) | IO_MEM_ROM); /* Setup peripherals */ spitz_keyboard_register(cpu); spitz_ssp_attach(cpu); - scp0 = scoop_init(cpu, 0, 0x10800000); + scp0 = sysbus_create_simple("scoop", 0x10800000, NULL); if (model != akita) { - scp1 = scoop_init(cpu, 1, 0x08800040); + scp1 = sysbus_create_simple("scoop", 0x08800040, NULL); } spitz_scoop_gpio_setup(cpu, scp0, scp1); @@ -993,9 +936,6 @@ /* A 4.0 GB microdrive is permanently sitting in CF slot 0. */ spitz_microdrive_attach(cpu, 0); - /* Setup initial (reset) machine state */ - cpu->env->regs[15] = spitz_binfo.loader_start; - spitz_binfo.kernel_filename = kernel_filename; spitz_binfo.kernel_cmdline = kernel_cmdline; spitz_binfo.initrd_filename = initrd_filename; @@ -1074,16 +1014,94 @@ machine_init(spitz_machine_init); +static bool is_version_0(void *opaque, int version_id) +{ + return version_id == 0; +} + +static VMStateDescription vmstate_sl_nand_info = { + .name = "sl-nand", + .version_id = 0, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .fields = (VMStateField []) { + VMSTATE_UINT8(ctl, SLNANDState), + VMSTATE_STRUCT(ecc, SLNANDState, 0, vmstate_ecc_state, ECCState), + VMSTATE_END_OF_LIST(), + }, +}; + +static SysBusDeviceInfo sl_nand_info = { + .init = sl_nand_init, + .qdev.name = "sl-nand", + .qdev.size = sizeof(SLNANDState), + .qdev.vmsd = &vmstate_sl_nand_info, + .qdev.props = (Property []) { + DEFINE_PROP_UINT8("manf_id", SLNANDState, manf_id, NAND_MFR_SAMSUNG), + DEFINE_PROP_UINT8("chip_id", SLNANDState, chip_id, 0xf1), + DEFINE_PROP_END_OF_LIST(), + }, +}; + +static VMStateDescription vmstate_spitz_kbd = { + .name = "spitz-keyboard", + .version_id = 1, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .post_load = spitz_keyboard_post_load, + .fields = (VMStateField []) { + VMSTATE_UINT16(sense_state, SpitzKeyboardState), + VMSTATE_UINT16(strobe_state, SpitzKeyboardState), + VMSTATE_UNUSED_TEST(is_version_0, 5), + VMSTATE_END_OF_LIST(), + }, +}; + +static SysBusDeviceInfo spitz_keyboard_info = { + .init = spitz_keyboard_init, + .qdev.name = "spitz-keyboard", + .qdev.size = sizeof(SpitzKeyboardState), + .qdev.vmsd = &vmstate_spitz_kbd, + .qdev.props = (Property []) { + DEFINE_PROP_END_OF_LIST(), + }, +}; + +static const VMStateDescription vmstate_corgi_ssp_regs = { + .name = "corgi-ssp", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField []) { + VMSTATE_UINT32_ARRAY(enable, CorgiSSPState, 3), + VMSTATE_END_OF_LIST(), + } +}; + static SSISlaveInfo corgi_ssp_info = { .qdev.name = "corgi-ssp", .qdev.size = sizeof(CorgiSSPState), + .qdev.vmsd = &vmstate_corgi_ssp_regs, .init = corgi_ssp_init, .transfer = corgi_ssp_transfer }; +static const VMStateDescription vmstate_spitz_lcdtg_regs = { + .name = "spitz-lcdtg", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField []) { + VMSTATE_UINT32(bl_intensity, SpitzLCDTG), + VMSTATE_UINT32(bl_power, SpitzLCDTG), + VMSTATE_END_OF_LIST(), + } +}; + static SSISlaveInfo spitz_lcdtg_info = { .qdev.name = "spitz-lcdtg", .qdev.size = sizeof(SpitzLCDTG), + .qdev.vmsd = &vmstate_spitz_lcdtg_regs, .init = spitz_lcdtg_init, .transfer = spitz_lcdtg_transfer }; @@ -1092,6 +1110,8 @@ { ssi_register_slave(&corgi_ssp_info); ssi_register_slave(&spitz_lcdtg_info); + sysbus_register_withprop(&spitz_keyboard_info); + sysbus_register_withprop(&sl_nand_info); } device_init(spitz_register_devices) diff -Nru qemu-kvm-0.12.5+noroms/hw/ssd0323.c qemu-kvm-0.14.1/hw/ssd0323.c --- qemu-kvm-0.12.5+noroms/hw/ssd0323.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/ssd0323.c 2011-05-11 13:29:46.000000000 +0000 @@ -335,7 +335,8 @@ qdev_init_gpio_in(&dev->qdev, ssd0323_cd, 1); - register_savevm("ssd0323_oled", -1, 1, ssd0323_save, ssd0323_load, s); + register_savevm(&dev->qdev, "ssd0323_oled", -1, 1, + ssd0323_save, ssd0323_load, s); return 0; } diff -Nru qemu-kvm-0.12.5+noroms/hw/ssi-sd.c qemu-kvm-0.14.1/hw/ssi-sd.c --- qemu-kvm-0.12.5+noroms/hw/ssi-sd.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/ssi-sd.c 2011-05-11 13:29:46.000000000 +0000 @@ -7,9 +7,9 @@ * This code is licenced under the GNU GPL v2. */ +#include "blockdev.h" #include "ssi.h" #include "sd.h" -#include "sysemu.h" //#define DEBUG_SSI_SD 1 @@ -232,12 +232,12 @@ static int ssi_sd_init(SSISlave *dev) { ssi_sd_state *s = FROM_SSI_SLAVE(ssi_sd_state, dev); - BlockDriverState *bs; + DriveInfo *dinfo; s->mode = SSI_SD_CMD; - bs = qdev_init_bdrv(&dev->qdev, IF_SD); - s->sd = sd_init(bs, 1); - register_savevm("ssi_sd", -1, 1, ssi_sd_save, ssi_sd_load, s); + dinfo = drive_get_next(IF_SD); + s->sd = sd_init(dinfo ? dinfo->bdrv : NULL, 1); + register_savevm(&dev->qdev, "ssi_sd", -1, 1, ssi_sd_save, ssi_sd_load, s); return 0; } diff -Nru qemu-kvm-0.12.5+noroms/hw/stellaris.c qemu-kvm-0.14.1/hw/stellaris.c --- qemu-kvm-0.12.5+noroms/hw/stellaris.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/stellaris.c 2011-05-11 13:29:46.000000000 +0000 @@ -348,13 +348,15 @@ qdev_init_gpio_out(&dev->qdev, &s->trigger, 1); iomemtype = cpu_register_io_memory(gptm_readfn, - gptm_writefn, s); + gptm_writefn, s, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, 0x1000, iomemtype); s->opaque[0] = s->opaque[1] = s; s->timer[0] = qemu_new_timer(vm_clock, gptm_tick, &s->opaque[0]); s->timer[1] = qemu_new_timer(vm_clock, gptm_tick, &s->opaque[1]); - register_savevm("stellaris_gptm", -1, 1, gptm_save, gptm_load, s); + register_savevm(&dev->qdev, "stellaris_gptm", -1, 1, + gptm_save, gptm_load, s); return 0; } @@ -670,10 +672,11 @@ s->user1 = macaddr[3] | (macaddr[4] << 8) | (macaddr[5] << 16); iomemtype = cpu_register_io_memory(ssys_readfn, - ssys_writefn, s); + ssys_writefn, s, + DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base, 0x00001000, iomemtype); ssys_reset(s); - register_savevm("stellaris_sys", -1, 1, ssys_save, ssys_load, s); + register_savevm(NULL, "stellaris_sys", -1, 1, ssys_save, ssys_load, s); return 0; } @@ -883,11 +886,12 @@ s->bus = bus; iomemtype = cpu_register_io_memory(stellaris_i2c_readfn, - stellaris_i2c_writefn, s); + stellaris_i2c_writefn, s, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, 0x1000, iomemtype); /* ??? For now we only implement the master interface. */ stellaris_i2c_reset(s); - register_savevm("stellaris_i2c", -1, 1, + register_savevm(&dev->qdev, "stellaris_i2c", -1, 1, stellaris_i2c_save, stellaris_i2c_load, s); return 0; } @@ -1192,11 +1196,12 @@ } iomemtype = cpu_register_io_memory(stellaris_adc_readfn, - stellaris_adc_writefn, s); + stellaris_adc_writefn, s, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, 0x1000, iomemtype); stellaris_adc_reset(s); qdev_init_gpio_in(&dev->qdev, stellaris_adc_trigger, 1); - register_savevm("stellaris_adc", -1, 1, + register_savevm(&dev->qdev, "stellaris_adc", -1, 1, stellaris_adc_save, stellaris_adc_load, s); return 0; } @@ -1256,7 +1261,7 @@ s->bus[1] = ssi_create_bus(&dev->qdev, "ssi1"); qdev_init_gpio_in(&dev->qdev, stellaris_ssi_bus_select, 1); - register_savevm("stellaris_ssi_bus", -1, 1, + register_savevm(&dev->qdev, "stellaris_ssi_bus", -1, 1, stellaris_ssi_bus_save, stellaris_ssi_bus_load, s); return 0; } @@ -1367,7 +1372,7 @@ gpio_out[GPIO_D][0] = qdev_get_gpio_in(mux, 0); bus = qdev_get_child_bus(mux, "ssi0"); - dev = ssi_create_slave(bus, "ssi-sd"); + ssi_create_slave(bus, "ssi-sd"); bus = qdev_get_child_bus(mux, "ssi1"); dev = ssi_create_slave(bus, "ssd0323"); diff -Nru qemu-kvm-0.12.5+noroms/hw/stellaris_enet.c qemu-kvm-0.14.1/hw/stellaris_enet.c --- qemu-kvm-0.12.5+noroms/hw/stellaris_enet.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/stellaris_enet.c 2011-05-11 13:29:46.000000000 +0000 @@ -389,7 +389,7 @@ { stellaris_enet_state *s = DO_UPCAST(NICState, nc, nc)->opaque; - unregister_savevm("stellaris_enet", s); + unregister_savevm(&s->busdev.qdev, "stellaris_enet", s); cpu_unregister_io_memory(s->mmio_index); @@ -409,7 +409,8 @@ stellaris_enet_state *s = FROM_SYSBUS(stellaris_enet_state, dev); s->mmio_index = cpu_register_io_memory(stellaris_enet_readfn, - stellaris_enet_writefn, s); + stellaris_enet_writefn, s, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, 0x1000, s->mmio_index); sysbus_init_irq(dev, &s->irq); qemu_macaddr_default_if_unset(&s->conf.macaddr); @@ -419,7 +420,7 @@ qemu_format_nic_info_str(&s->nic->nc, s->conf.macaddr.a); stellaris_enet_reset(s); - register_savevm("stellaris_enet", -1, 1, + register_savevm(&s->busdev.qdev, "stellaris_enet", -1, 1, stellaris_enet_save, stellaris_enet_load, s); return 0; } diff -Nru qemu-kvm-0.12.5+noroms/hw/stellaris_input.c qemu-kvm-0.14.1/hw/stellaris_input.c --- qemu-kvm-0.12.5+noroms/hw/stellaris_input.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/stellaris_input.c 2011-05-11 13:29:46.000000000 +0000 @@ -86,6 +86,6 @@ } s->num_buttons = n; qemu_add_kbd_event_handler(stellaris_gamepad_put_key, s); - register_savevm("stellaris_gamepad", -1, 1, + register_savevm(NULL, "stellaris_gamepad", -1, 1, stellaris_gamepad_save, stellaris_gamepad_load, s); } diff -Nru qemu-kvm-0.12.5+noroms/hw/sun4c_intctl.c qemu-kvm-0.14.1/hw/sun4c_intctl.c --- qemu-kvm-0.12.5+noroms/hw/sun4c_intctl.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/sun4c_intctl.c 2011-05-11 13:29:46.000000000 +0000 @@ -196,7 +196,8 @@ unsigned int i; io_memory = cpu_register_io_memory(sun4c_intctl_mem_read, - sun4c_intctl_mem_write, s); + sun4c_intctl_mem_write, s, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, INTCTL_SIZE, io_memory); qdev_init_gpio_in(&dev->qdev, sun4c_set_irq, 8); diff -Nru qemu-kvm-0.12.5+noroms/hw/sun4m.c qemu-kvm-0.14.1/hw/sun4m.c --- qemu-kvm-0.12.5+noroms/hw/sun4m.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/sun4m.c 2011-05-11 13:29:46.000000000 +0000 @@ -36,11 +36,12 @@ #include "isa.h" #include "fw_cfg.h" #include "escc.h" +#include "empty_slot.h" #include "qdev-addr.h" #include "loader.h" #include "elf.h" - -//#define DEBUG_IRQ +#include "blockdev.h" +#include "trace.h" /* * Sun4m architecture was used in the following machines: @@ -70,13 +71,6 @@ * See for example: http://www.sunhelp.org/faq/sunref1.html */ -#ifdef DEBUG_IRQ -#define DPRINTF(fmt, ...) \ - do { printf("CPUIRQ: " fmt , ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) -#endif - #define KERNEL_LOAD_ADDR 0x00004000 #define CMDLINE_ADDR 0x007ff000 #define INITRD_LOAD_ADDR 0x00800000 @@ -88,15 +82,20 @@ #define MAX_CPUS 16 #define MAX_PILS 16 +#define MAX_VSIMMS 4 #define ESCC_CLOCK 4915200 struct sun4m_hwdef { - target_phys_addr_t iommu_base, slavio_base; + target_phys_addr_t iommu_base, iommu_pad_base, iommu_pad_len, slavio_base; target_phys_addr_t intctl_base, counter_base, nvram_base, ms_kb_base; target_phys_addr_t serial_base, fd_base; - target_phys_addr_t idreg_base, dma_base, esp_base, le_base; + target_phys_addr_t afx_base, idreg_base, dma_base, esp_base, le_base; target_phys_addr_t tcx_base, cs_base, apc_base, aux1_base, aux2_base; + target_phys_addr_t bpp_base, dbri_base, sx_base; + struct { + target_phys_addr_t reg_base, vram_base; + } vsimm[MAX_VSIMMS]; target_phys_addr_t ecc_base; uint32_t ecc_version; uint8_t nvram_machine_id; @@ -151,7 +150,11 @@ void DMA_hold_DREQ (int nchan) {} void DMA_release_DREQ (int nchan) {} void DMA_schedule(int nchan) {} -void DMA_init (int high_page_enable) {} + +void DMA_init(int high_page_enable, qemu_irq *cpu_request_exit) +{ +} + void DMA_register_channel (int nchan, DMA_transfer_handler transfer_handler, void *opaque) @@ -164,9 +167,9 @@ return 0; } -static void nvram_init(m48t59_t *nvram, uint8_t *macaddr, const char *cmdline, - const char *boot_devices, ram_addr_t RAM_size, - uint32_t kernel_size, +static void nvram_init(M48t59State *nvram, uint8_t *macaddr, + const char *cmdline, const char *boot_devices, + ram_addr_t RAM_size, uint32_t kernel_size, int width, int height, int depth, int nvram_machine_id, const char *arch) { @@ -237,14 +240,14 @@ env->interrupt_index = TT_EXTINT | i; if (old_interrupt != env->interrupt_index) { - DPRINTF("Set CPU IRQ %d\n", i); + trace_sun4m_cpu_interrupt(i); cpu_interrupt(env, CPU_INTERRUPT_HARD); } break; } } } else if (!env->pil_in && (env->interrupt_index & ~15) == TT_EXTINT) { - DPRINTF("Reset CPU IRQ %d\n", env->interrupt_index & 15); + trace_sun4m_cpu_reset_interrupt(env->interrupt_index & 15); env->interrupt_index = 0; cpu_reset_interrupt(env, CPU_INTERRUPT_HARD); } @@ -255,12 +258,12 @@ CPUState *env = opaque; if (level) { - DPRINTF("Raise CPU IRQ %d\n", irq); + trace_sun4m_cpu_set_irq_raise(irq); env->halted = 0; env->pil_in |= 1 << irq; cpu_check_irqs(env); } else { - DPRINTF("Lower CPU IRQ %d\n", irq); + trace_sun4m_cpu_set_irq_lower(irq); env->pil_in &= ~(1 << irq); cpu_check_irqs(env); } @@ -292,6 +295,11 @@ cpu_interrupt(cpu_single_env, CPU_INTERRUPT_HALT); } +static uint64_t translate_kernel_address(void *opaque, uint64_t addr) +{ + return addr - 0xf0000000ULL; +} + static unsigned long sun4m_load_kernel(const char *kernel_filename, const char *initrd_filename, ram_addr_t RAM_size) @@ -312,8 +320,8 @@ #else bswap_needed = 0; #endif - kernel_size = load_elf(kernel_filename, -0xf0000000ULL, NULL, NULL, - NULL, 1, ELF_MACHINE, 0); + kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL, + NULL, NULL, NULL, 1, ELF_MACHINE, 0); if (kernel_size < 0) kernel_size = load_aout(kernel_filename, KERNEL_LOAD_ADDR, RAM_size - KERNEL_LOAD_ADDR, bswap_needed, @@ -370,13 +378,14 @@ } static void *sparc32_dma_init(target_phys_addr_t daddr, qemu_irq parent_irq, - void *iommu, qemu_irq *dev_irq) + void *iommu, qemu_irq *dev_irq, int is_ledma) { DeviceState *dev; SysBusDevice *s; dev = qdev_create(NULL, "sparc32_dma"); qdev_prop_set_ptr(dev, "iommu_opaque", iommu); + qdev_prop_set_uint32(dev, "is_ledma", is_ledma); qdev_init_nofail(dev); s = sysbus_from_qdev(dev); sysbus_connect_irq(s, 0, parent_irq); @@ -582,7 +591,7 @@ { ram_addr_t idreg_offset; - idreg_offset = qemu_ram_alloc(sizeof(idreg_data)); + idreg_offset = qemu_ram_alloc(NULL, "sun4m.idreg", sizeof(idreg_data)); sysbus_init_mmio(dev, sizeof(idreg_data), idreg_offset | IO_MEM_ROM); return 0; } @@ -600,7 +609,48 @@ device_init(idreg_register_devices); +/* SS-5 TCX AFX register */ +static void afx_init(target_phys_addr_t addr) +{ + DeviceState *dev; + SysBusDevice *s; + + dev = qdev_create(NULL, "tcx_afx"); + qdev_init_nofail(dev); + s = sysbus_from_qdev(dev); + + sysbus_mmio_map(s, 0, addr); +} + +static int afx_init1(SysBusDevice *dev) +{ + ram_addr_t afx_offset; + + afx_offset = qemu_ram_alloc(NULL, "sun4m.afx", 4); + sysbus_init_mmio(dev, 4, afx_offset | IO_MEM_RAM); + return 0; +} + +static SysBusDeviceInfo afx_info = { + .init = afx_init1, + .qdev.name = "tcx_afx", + .qdev.size = sizeof(SysBusDevice), +}; + +static void afx_register_devices(void) +{ + sysbus_register_withprop(&afx_info); +} + +device_init(afx_register_devices); + /* Boot PROM (OpenBIOS) */ +static uint64_t translate_prom_address(void *opaque, uint64_t addr) +{ + target_phys_addr_t *base_addr = (target_phys_addr_t *)opaque; + return addr + *base_addr - PROM_VADDR; +} + static void prom_init(target_phys_addr_t addr, const char *bios_name) { DeviceState *dev; @@ -620,8 +670,8 @@ } filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); if (filename) { - ret = load_elf(filename, addr - PROM_VADDR, NULL, NULL, NULL, - 1, ELF_MACHINE, 0); + ret = load_elf(filename, translate_prom_address, &addr, NULL, + NULL, NULL, 1, ELF_MACHINE, 0); if (ret < 0 || ret > PROM_SIZE_MAX) { ret = load_image_targphys(filename, addr, PROM_SIZE_MAX); } @@ -639,7 +689,7 @@ { ram_addr_t prom_offset; - prom_offset = qemu_ram_alloc(PROM_SIZE_MAX); + prom_offset = qemu_ram_alloc(NULL, "sun4m.prom", PROM_SIZE_MAX); sysbus_init_mmio(dev, PROM_SIZE_MAX, prom_offset | IO_MEM_ROM); return 0; } @@ -674,7 +724,7 @@ RAM_size = d->size; - ram_offset = qemu_ram_alloc(RAM_size); + ram_offset = qemu_ram_alloc(NULL, "sun4m.ram", RAM_size); sysbus_init_mmio(dev, RAM_size, ram_offset); return 0; } @@ -721,8 +771,8 @@ device_init(ram_register_devices); -static CPUState *cpu_devinit(const char *cpu_model, unsigned int id, - uint64_t prom_addr, qemu_irq **cpu_irqs) +static void cpu_devinit(const char *cpu_model, unsigned int id, + uint64_t prom_addr, qemu_irq **cpu_irqs) { CPUState *env; @@ -741,8 +791,6 @@ } *cpu_irqs = qemu_allocate_irqs(cpu_set_irq, env, MAX_PILS); env->prom_addr = prom_addr; - - return env; } static void sun4m_hw_init(const struct sun4m_hwdef *hwdef, ram_addr_t RAM_size, @@ -751,24 +799,24 @@ const char *kernel_cmdline, const char *initrd_filename, const char *cpu_model) { - CPUState *envs[MAX_CPUS]; unsigned int i; void *iommu, *espdma, *ledma, *nvram; qemu_irq *cpu_irqs[MAX_CPUS], slavio_irq[32], slavio_cpu_irq[MAX_CPUS], espdma_irq, ledma_irq; - qemu_irq esp_reset; + qemu_irq esp_reset, dma_enable; qemu_irq fdc_tc; qemu_irq *cpu_halt; unsigned long kernel_size; DriveInfo *fd[MAX_FD]; void *fw_cfg; + unsigned int num_vsimms; /* init CPUs */ if (!cpu_model) cpu_model = hwdef->default_cpu_model; for(i = 0; i < smp_cpus; i++) { - envs[i] = cpu_devinit(cpu_model, i, hwdef->slavio_base, &cpu_irqs[i]); + cpu_devinit(cpu_model, i, hwdef->slavio_base, &cpu_irqs[i]); } for (i = smp_cpus; i < MAX_CPUS; i++) @@ -777,6 +825,10 @@ /* set up devices */ ram_init(0, RAM_size, hwdef->max_mem); + /* models without ECC don't trap when missing ram is accessed */ + if (!hwdef->ecc_base) { + empty_slot_init(RAM_size, hwdef->max_mem - RAM_size); + } prom_init(hwdef->slavio_base, bios_name); @@ -795,21 +847,47 @@ idreg_init(hwdef->idreg_base); } + if (hwdef->afx_base) { + afx_init(hwdef->afx_base); + } + iommu = iommu_init(hwdef->iommu_base, hwdef->iommu_version, slavio_irq[30]); + if (hwdef->iommu_pad_base) { + /* On the real hardware (SS-5, LX) the MMU is not padded, but aliased. + Software shouldn't use aliased addresses, neither should it crash + when does. Using empty_slot instead of aliasing can help with + debugging such accesses */ + empty_slot_init(hwdef->iommu_pad_base,hwdef->iommu_pad_len); + } + espdma = sparc32_dma_init(hwdef->dma_base, slavio_irq[18], - iommu, &espdma_irq); + iommu, &espdma_irq, 0); ledma = sparc32_dma_init(hwdef->dma_base + 16ULL, - slavio_irq[16], iommu, &ledma_irq); + slavio_irq[16], iommu, &ledma_irq, 1); if (graphic_depth != 8 && graphic_depth != 24) { fprintf(stderr, "qemu: Unsupported depth: %d\n", graphic_depth); exit (1); } - tcx_init(hwdef->tcx_base, 0x00100000, graphic_width, graphic_height, - graphic_depth); + num_vsimms = 0; + if (num_vsimms == 0) { + tcx_init(hwdef->tcx_base, 0x00100000, graphic_width, graphic_height, + graphic_depth); + } + + for (i = num_vsimms; i < MAX_VSIMMS; i++) { + /* vsimm registers probed by OBP */ + if (hwdef->vsimm[i].reg_base) { + empty_slot_init(hwdef->vsimm[i].reg_base, 0x2000); + } + } + + if (hwdef->sx_base) { + empty_slot_init(hwdef->sx_base, 0x2000); + } lance_init(&nd_table[0], hwdef->le_base, ledma, ledma_irq); @@ -845,17 +923,31 @@ exit(1); } - esp_reset = qdev_get_gpio_in(espdma, 0); esp_init(hwdef->esp_base, 2, espdma_memory_read, espdma_memory_write, - espdma, espdma_irq, &esp_reset); + espdma, espdma_irq, &esp_reset, &dma_enable); + qdev_connect_gpio_out(espdma, 0, esp_reset); + qdev_connect_gpio_out(espdma, 1, dma_enable); if (hwdef->cs_base) { sysbus_create_simple("SUNW,CS4231", hwdef->cs_base, slavio_irq[5]); } + if (hwdef->dbri_base) { + /* ISDN chip with attached CS4215 audio codec */ + /* prom space */ + empty_slot_init(hwdef->dbri_base+0x1000, 0x30); + /* reg space */ + empty_slot_init(hwdef->dbri_base+0x10000, 0x100); + } + + if (hwdef->bpp_base) { + /* parallel port */ + empty_slot_init(hwdef->bpp_base, 0x20); + } + kernel_size = sun4m_load_kernel(kernel_filename, initrd_filename, RAM_size); @@ -878,8 +970,14 @@ if (kernel_cmdline) { fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_CMDLINE, CMDLINE_ADDR); pstrcpy_targphys("cmdline", CMDLINE_ADDR, TARGET_PAGE_SIZE, kernel_cmdline); + fw_cfg_add_bytes(fw_cfg, FW_CFG_CMDLINE_DATA, + (uint8_t*)strdup(kernel_cmdline), + strlen(kernel_cmdline) + 1); + fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, + strlen(kernel_cmdline) + 1); } else { fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_CMDLINE, 0); + fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, 0); } fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, INITRD_LOAD_ADDR); fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, 0); // not used @@ -906,6 +1004,8 @@ /* SS-5 */ { .iommu_base = 0x10000000, + .iommu_pad_base = 0x10004000, + .iommu_pad_len = 0x0fffb000, .tcx_base = 0x50000000, .cs_base = 0x6c000000, .slavio_base = 0x70000000, @@ -920,6 +1020,7 @@ .esp_base = 0x78800000, .le_base = 0x78c00000, .apc_base = 0x6a000000, + .afx_base = 0x6e000000, .aux1_base = 0x71900000, .aux2_base = 0x71910000, .nvram_machine_id = 0x80, @@ -993,9 +1094,25 @@ .dma_base = 0xef0400000ULL, .esp_base = 0xef0800000ULL, .le_base = 0xef0c00000ULL, + .bpp_base = 0xef4800000ULL, .apc_base = 0xefa000000ULL, // XXX should not exist .aux1_base = 0xff1800000ULL, .aux2_base = 0xff1a01000ULL, + .dbri_base = 0xee0000000ULL, + .sx_base = 0xf80000000ULL, + .vsimm = { + { + .reg_base = 0x9c000000ULL, + .vram_base = 0xfc000000ULL + }, { + .reg_base = 0x90000000ULL, + .vram_base = 0xf0000000ULL + }, { + .reg_base = 0x94000000ULL + }, { + .reg_base = 0x98000000ULL + } + }, .ecc_base = 0xf00000000ULL, .ecc_version = 0x20000000, // version 0, implementation 2 .nvram_machine_id = 0x72, @@ -1031,6 +1148,8 @@ /* LX */ { .iommu_base = 0x10000000, + .iommu_pad_base = 0x10004000, + .iommu_pad_len = 0x0fffb000, .tcx_base = 0x50000000, .slavio_base = 0x70000000, .ms_kb_base = 0x71000000, @@ -1365,12 +1484,11 @@ const char *kernel_cmdline, const char *initrd_filename, const char *cpu_model) { - CPUState *envs[MAX_CPUS]; unsigned int i; void *iounits[MAX_IOUNITS], *espdma, *ledma, *nvram; qemu_irq *cpu_irqs[MAX_CPUS], sbi_irq[32], sbi_cpu_irq[MAX_CPUS], espdma_irq, ledma_irq; - qemu_irq esp_reset; + qemu_irq esp_reset, dma_enable; unsigned long kernel_size; void *fw_cfg; DeviceState *dev; @@ -1380,7 +1498,7 @@ cpu_model = hwdef->default_cpu_model; for(i = 0; i < smp_cpus; i++) { - envs[i] = cpu_devinit(cpu_model, i, hwdef->slavio_base, &cpu_irqs[i]); + cpu_devinit(cpu_model, i, hwdef->slavio_base, &cpu_irqs[i]); } for (i = smp_cpus; i < MAX_CPUS; i++) @@ -1407,10 +1525,11 @@ sbi_irq[0]); espdma = sparc32_dma_init(hwdef->espdma_base, sbi_irq[3], - iounits[0], &espdma_irq); + iounits[0], &espdma_irq, 0); + /* should be lebuffer instead */ ledma = sparc32_dma_init(hwdef->ledma_base, sbi_irq[4], - iounits[0], &ledma_irq); + iounits[0], &ledma_irq, 0); if (graphic_depth != 8 && graphic_depth != 24) { fprintf(stderr, "qemu: Unsupported depth: %d\n", graphic_depth); @@ -1437,10 +1556,12 @@ exit(1); } - esp_reset = qdev_get_gpio_in(espdma, 0); esp_init(hwdef->esp_base, 2, espdma_memory_read, espdma_memory_write, - espdma, espdma_irq, &esp_reset); + espdma, espdma_irq, &esp_reset, &dma_enable); + + qdev_connect_gpio_out(espdma, 0, esp_reset); + qdev_connect_gpio_out(espdma, 1, dma_enable); kernel_size = sun4m_load_kernel(kernel_filename, initrd_filename, RAM_size); @@ -1460,6 +1581,9 @@ if (kernel_cmdline) { fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_CMDLINE, CMDLINE_ADDR); pstrcpy_targphys("cmdline", CMDLINE_ADDR, TARGET_PAGE_SIZE, kernel_cmdline); + fw_cfg_add_bytes(fw_cfg, FW_CFG_CMDLINE_DATA, + (uint8_t*)strdup(kernel_cmdline), + strlen(kernel_cmdline) + 1); } else { fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_CMDLINE, 0); } @@ -1554,10 +1678,9 @@ const char *kernel_cmdline, const char *initrd_filename, const char *cpu_model) { - CPUState *env; void *iommu, *espdma, *ledma, *nvram; qemu_irq *cpu_irqs, slavio_irq[8], espdma_irq, ledma_irq; - qemu_irq esp_reset; + qemu_irq esp_reset, dma_enable; qemu_irq fdc_tc; unsigned long kernel_size; DriveInfo *fd[MAX_FD]; @@ -1569,7 +1692,7 @@ if (!cpu_model) cpu_model = hwdef->default_cpu_model; - env = cpu_devinit(cpu_model, 0, hwdef->slavio_base, &cpu_irqs); + cpu_devinit(cpu_model, 0, hwdef->slavio_base, &cpu_irqs); /* set up devices */ ram_init(0, RAM_size, hwdef->max_mem); @@ -1586,10 +1709,10 @@ slavio_irq[1]); espdma = sparc32_dma_init(hwdef->dma_base, slavio_irq[2], - iommu, &espdma_irq); + iommu, &espdma_irq, 0); ledma = sparc32_dma_init(hwdef->dma_base + 16ULL, - slavio_irq[3], iommu, &ledma_irq); + slavio_irq[3], iommu, &ledma_irq, 1); if (graphic_depth != 8 && graphic_depth != 24) { fprintf(stderr, "qemu: Unsupported depth: %d\n", graphic_depth); @@ -1625,10 +1748,12 @@ exit(1); } - esp_reset = qdev_get_gpio_in(espdma, 0); esp_init(hwdef->esp_base, 2, espdma_memory_read, espdma_memory_write, - espdma, espdma_irq, &esp_reset); + espdma, espdma_irq, &esp_reset, &dma_enable); + + qdev_connect_gpio_out(espdma, 0, esp_reset); + qdev_connect_gpio_out(espdma, 1, dma_enable); kernel_size = sun4m_load_kernel(kernel_filename, initrd_filename, RAM_size); @@ -1648,6 +1773,9 @@ if (kernel_cmdline) { fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_CMDLINE, CMDLINE_ADDR); pstrcpy_targphys("cmdline", CMDLINE_ADDR, TARGET_PAGE_SIZE, kernel_cmdline); + fw_cfg_add_bytes(fw_cfg, FW_CFG_CMDLINE_DATA, + (uint8_t*)strdup(kernel_cmdline), + strlen(kernel_cmdline) + 1); } else { fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_CMDLINE, 0); } diff -Nru qemu-kvm-0.12.5+noroms/hw/sun4m_iommu.c qemu-kvm-0.14.1/hw/sun4m_iommu.c --- qemu-kvm-0.12.5+noroms/hw/sun4m_iommu.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/sun4m_iommu.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,378 @@ +/* + * QEMU Sun4m iommu emulation + * + * Copyright (c) 2003-2005 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "sun4m.h" +#include "sysbus.h" +#include "trace.h" + +/* + * I/O MMU used by Sun4m systems + * + * Chipset docs: + * "Sun-4M System Architecture (revision 2.0) by Chuck Narad", 950-1373-01, + * http://mediacast.sun.com/users/Barton808/media/Sun4M_SystemArchitecture_edited2.pdf + */ + +#define IOMMU_NREGS (4*4096/4) +#define IOMMU_CTRL (0x0000 >> 2) +#define IOMMU_CTRL_IMPL 0xf0000000 /* Implementation */ +#define IOMMU_CTRL_VERS 0x0f000000 /* Version */ +#define IOMMU_CTRL_RNGE 0x0000001c /* Mapping RANGE */ +#define IOMMU_RNGE_16MB 0x00000000 /* 0xff000000 -> 0xffffffff */ +#define IOMMU_RNGE_32MB 0x00000004 /* 0xfe000000 -> 0xffffffff */ +#define IOMMU_RNGE_64MB 0x00000008 /* 0xfc000000 -> 0xffffffff */ +#define IOMMU_RNGE_128MB 0x0000000c /* 0xf8000000 -> 0xffffffff */ +#define IOMMU_RNGE_256MB 0x00000010 /* 0xf0000000 -> 0xffffffff */ +#define IOMMU_RNGE_512MB 0x00000014 /* 0xe0000000 -> 0xffffffff */ +#define IOMMU_RNGE_1GB 0x00000018 /* 0xc0000000 -> 0xffffffff */ +#define IOMMU_RNGE_2GB 0x0000001c /* 0x80000000 -> 0xffffffff */ +#define IOMMU_CTRL_ENAB 0x00000001 /* IOMMU Enable */ +#define IOMMU_CTRL_MASK 0x0000001d + +#define IOMMU_BASE (0x0004 >> 2) +#define IOMMU_BASE_MASK 0x07fffc00 + +#define IOMMU_TLBFLUSH (0x0014 >> 2) +#define IOMMU_TLBFLUSH_MASK 0xffffffff + +#define IOMMU_PGFLUSH (0x0018 >> 2) +#define IOMMU_PGFLUSH_MASK 0xffffffff + +#define IOMMU_AFSR (0x1000 >> 2) +#define IOMMU_AFSR_ERR 0x80000000 /* LE, TO, or BE asserted */ +#define IOMMU_AFSR_LE 0x40000000 /* SBUS reports error after + transaction */ +#define IOMMU_AFSR_TO 0x20000000 /* Write access took more than + 12.8 us. */ +#define IOMMU_AFSR_BE 0x10000000 /* Write access received error + acknowledge */ +#define IOMMU_AFSR_SIZE 0x0e000000 /* Size of transaction causing error */ +#define IOMMU_AFSR_S 0x01000000 /* Sparc was in supervisor mode */ +#define IOMMU_AFSR_RESV 0x00800000 /* Reserved, forced to 0x8 by + hardware */ +#define IOMMU_AFSR_ME 0x00080000 /* Multiple errors occurred */ +#define IOMMU_AFSR_RD 0x00040000 /* A read operation was in progress */ +#define IOMMU_AFSR_FAV 0x00020000 /* IOMMU afar has valid contents */ +#define IOMMU_AFSR_MASK 0xff0fffff + +#define IOMMU_AFAR (0x1004 >> 2) + +#define IOMMU_AER (0x1008 >> 2) /* Arbiter Enable Register */ +#define IOMMU_AER_EN_P0_ARB 0x00000001 /* MBus master 0x8 (Always 1) */ +#define IOMMU_AER_EN_P1_ARB 0x00000002 /* MBus master 0x9 */ +#define IOMMU_AER_EN_P2_ARB 0x00000004 /* MBus master 0xa */ +#define IOMMU_AER_EN_P3_ARB 0x00000008 /* MBus master 0xb */ +#define IOMMU_AER_EN_0 0x00010000 /* SBus slot 0 */ +#define IOMMU_AER_EN_1 0x00020000 /* SBus slot 1 */ +#define IOMMU_AER_EN_2 0x00040000 /* SBus slot 2 */ +#define IOMMU_AER_EN_3 0x00080000 /* SBus slot 3 */ +#define IOMMU_AER_EN_F 0x00100000 /* SBus on-board */ +#define IOMMU_AER_SBW 0x80000000 /* S-to-M asynchronous writes */ +#define IOMMU_AER_MASK 0x801f000f + +#define IOMMU_SBCFG0 (0x1010 >> 2) /* SBUS configration per-slot */ +#define IOMMU_SBCFG1 (0x1014 >> 2) /* SBUS configration per-slot */ +#define IOMMU_SBCFG2 (0x1018 >> 2) /* SBUS configration per-slot */ +#define IOMMU_SBCFG3 (0x101c >> 2) /* SBUS configration per-slot */ +#define IOMMU_SBCFG_SAB30 0x00010000 /* Phys-address bit 30 when + bypass enabled */ +#define IOMMU_SBCFG_BA16 0x00000004 /* Slave supports 16 byte bursts */ +#define IOMMU_SBCFG_BA8 0x00000002 /* Slave supports 8 byte bursts */ +#define IOMMU_SBCFG_BYPASS 0x00000001 /* Bypass IOMMU, treat all addresses + produced by this device as pure + physical. */ +#define IOMMU_SBCFG_MASK 0x00010003 + +#define IOMMU_ARBEN (0x2000 >> 2) /* SBUS arbitration enable */ +#define IOMMU_ARBEN_MASK 0x001f0000 +#define IOMMU_MID 0x00000008 + +#define IOMMU_MASK_ID (0x3018 >> 2) /* Mask ID */ +#define IOMMU_MASK_ID_MASK 0x00ffffff + +#define IOMMU_MSII_MASK 0x26000000 /* microSPARC II mask number */ +#define IOMMU_TS_MASK 0x23000000 /* turboSPARC mask number */ + +/* The format of an iopte in the page tables */ +#define IOPTE_PAGE 0xffffff00 /* Physical page number (PA[35:12]) */ +#define IOPTE_CACHE 0x00000080 /* Cached (in vme IOCACHE or + Viking/MXCC) */ +#define IOPTE_WRITE 0x00000004 /* Writeable */ +#define IOPTE_VALID 0x00000002 /* IOPTE is valid */ +#define IOPTE_WAZ 0x00000001 /* Write as zeros */ + +#define IOMMU_PAGE_SHIFT 12 +#define IOMMU_PAGE_SIZE (1 << IOMMU_PAGE_SHIFT) +#define IOMMU_PAGE_MASK ~(IOMMU_PAGE_SIZE - 1) + +typedef struct IOMMUState { + SysBusDevice busdev; + uint32_t regs[IOMMU_NREGS]; + target_phys_addr_t iostart; + uint32_t version; + qemu_irq irq; +} IOMMUState; + +static uint32_t iommu_mem_readl(void *opaque, target_phys_addr_t addr) +{ + IOMMUState *s = opaque; + target_phys_addr_t saddr; + uint32_t ret; + + saddr = addr >> 2; + switch (saddr) { + default: + ret = s->regs[saddr]; + break; + case IOMMU_AFAR: + case IOMMU_AFSR: + ret = s->regs[saddr]; + qemu_irq_lower(s->irq); + break; + } + trace_sun4m_iommu_mem_readl(saddr, ret); + return ret; +} + +static void iommu_mem_writel(void *opaque, target_phys_addr_t addr, + uint32_t val) +{ + IOMMUState *s = opaque; + target_phys_addr_t saddr; + + saddr = addr >> 2; + trace_sun4m_iommu_mem_writel(saddr, val); + switch (saddr) { + case IOMMU_CTRL: + switch (val & IOMMU_CTRL_RNGE) { + case IOMMU_RNGE_16MB: + s->iostart = 0xffffffffff000000ULL; + break; + case IOMMU_RNGE_32MB: + s->iostart = 0xfffffffffe000000ULL; + break; + case IOMMU_RNGE_64MB: + s->iostart = 0xfffffffffc000000ULL; + break; + case IOMMU_RNGE_128MB: + s->iostart = 0xfffffffff8000000ULL; + break; + case IOMMU_RNGE_256MB: + s->iostart = 0xfffffffff0000000ULL; + break; + case IOMMU_RNGE_512MB: + s->iostart = 0xffffffffe0000000ULL; + break; + case IOMMU_RNGE_1GB: + s->iostart = 0xffffffffc0000000ULL; + break; + default: + case IOMMU_RNGE_2GB: + s->iostart = 0xffffffff80000000ULL; + break; + } + trace_sun4m_iommu_mem_writel_ctrl(s->iostart); + s->regs[saddr] = ((val & IOMMU_CTRL_MASK) | s->version); + break; + case IOMMU_BASE: + s->regs[saddr] = val & IOMMU_BASE_MASK; + break; + case IOMMU_TLBFLUSH: + trace_sun4m_iommu_mem_writel_tlbflush(val); + s->regs[saddr] = val & IOMMU_TLBFLUSH_MASK; + break; + case IOMMU_PGFLUSH: + trace_sun4m_iommu_mem_writel_pgflush(val); + s->regs[saddr] = val & IOMMU_PGFLUSH_MASK; + break; + case IOMMU_AFAR: + s->regs[saddr] = val; + qemu_irq_lower(s->irq); + break; + case IOMMU_AER: + s->regs[saddr] = (val & IOMMU_AER_MASK) | IOMMU_AER_EN_P0_ARB; + break; + case IOMMU_AFSR: + s->regs[saddr] = (val & IOMMU_AFSR_MASK) | IOMMU_AFSR_RESV; + qemu_irq_lower(s->irq); + break; + case IOMMU_SBCFG0: + case IOMMU_SBCFG1: + case IOMMU_SBCFG2: + case IOMMU_SBCFG3: + s->regs[saddr] = val & IOMMU_SBCFG_MASK; + break; + case IOMMU_ARBEN: + // XXX implement SBus probing: fault when reading unmapped + // addresses, fault cause and address stored to MMU/IOMMU + s->regs[saddr] = (val & IOMMU_ARBEN_MASK) | IOMMU_MID; + break; + case IOMMU_MASK_ID: + s->regs[saddr] |= val & IOMMU_MASK_ID_MASK; + break; + default: + s->regs[saddr] = val; + break; + } +} + +static CPUReadMemoryFunc * const iommu_mem_read[3] = { + NULL, + NULL, + iommu_mem_readl, +}; + +static CPUWriteMemoryFunc * const iommu_mem_write[3] = { + NULL, + NULL, + iommu_mem_writel, +}; + +static uint32_t iommu_page_get_flags(IOMMUState *s, target_phys_addr_t addr) +{ + uint32_t ret; + target_phys_addr_t iopte; + target_phys_addr_t pa = addr; + + iopte = s->regs[IOMMU_BASE] << 4; + addr &= ~s->iostart; + iopte += (addr >> (IOMMU_PAGE_SHIFT - 2)) & ~3; + cpu_physical_memory_read(iopte, (uint8_t *)&ret, 4); + tswap32s(&ret); + trace_sun4m_iommu_page_get_flags(pa, iopte, ret); + return ret; +} + +static target_phys_addr_t iommu_translate_pa(target_phys_addr_t addr, + uint32_t pte) +{ + target_phys_addr_t pa; + + pa = ((pte & IOPTE_PAGE) << 4) + (addr & ~IOMMU_PAGE_MASK); + trace_sun4m_iommu_translate_pa(addr, pa, pte); + return pa; +} + +static void iommu_bad_addr(IOMMUState *s, target_phys_addr_t addr, + int is_write) +{ + trace_sun4m_iommu_bad_addr(addr); + s->regs[IOMMU_AFSR] = IOMMU_AFSR_ERR | IOMMU_AFSR_LE | IOMMU_AFSR_RESV | + IOMMU_AFSR_FAV; + if (!is_write) + s->regs[IOMMU_AFSR] |= IOMMU_AFSR_RD; + s->regs[IOMMU_AFAR] = addr; + qemu_irq_raise(s->irq); +} + +void sparc_iommu_memory_rw(void *opaque, target_phys_addr_t addr, + uint8_t *buf, int len, int is_write) +{ + int l; + uint32_t flags; + target_phys_addr_t page, phys_addr; + + while (len > 0) { + page = addr & IOMMU_PAGE_MASK; + l = (page + IOMMU_PAGE_SIZE) - addr; + if (l > len) + l = len; + flags = iommu_page_get_flags(opaque, page); + if (!(flags & IOPTE_VALID)) { + iommu_bad_addr(opaque, page, is_write); + return; + } + phys_addr = iommu_translate_pa(addr, flags); + if (is_write) { + if (!(flags & IOPTE_WRITE)) { + iommu_bad_addr(opaque, page, is_write); + return; + } + cpu_physical_memory_write(phys_addr, buf, l); + } else { + cpu_physical_memory_read(phys_addr, buf, l); + } + len -= l; + buf += l; + addr += l; + } +} + +static const VMStateDescription vmstate_iommu = { + .name ="iommu", + .version_id = 2, + .minimum_version_id = 2, + .minimum_version_id_old = 2, + .fields = (VMStateField []) { + VMSTATE_UINT32_ARRAY(regs, IOMMUState, IOMMU_NREGS), + VMSTATE_UINT64(iostart, IOMMUState), + VMSTATE_END_OF_LIST() + } +}; + +static void iommu_reset(DeviceState *d) +{ + IOMMUState *s = container_of(d, IOMMUState, busdev.qdev); + + memset(s->regs, 0, IOMMU_NREGS * 4); + s->iostart = 0; + s->regs[IOMMU_CTRL] = s->version; + s->regs[IOMMU_ARBEN] = IOMMU_MID; + s->regs[IOMMU_AFSR] = IOMMU_AFSR_RESV; + s->regs[IOMMU_AER] = IOMMU_AER_EN_P0_ARB | IOMMU_AER_EN_P1_ARB; + s->regs[IOMMU_MASK_ID] = IOMMU_TS_MASK; +} + +static int iommu_init1(SysBusDevice *dev) +{ + IOMMUState *s = FROM_SYSBUS(IOMMUState, dev); + int io; + + sysbus_init_irq(dev, &s->irq); + + io = cpu_register_io_memory(iommu_mem_read, iommu_mem_write, s, + DEVICE_NATIVE_ENDIAN); + sysbus_init_mmio(dev, IOMMU_NREGS * sizeof(uint32_t), io); + + return 0; +} + +static SysBusDeviceInfo iommu_info = { + .init = iommu_init1, + .qdev.name = "iommu", + .qdev.size = sizeof(IOMMUState), + .qdev.vmsd = &vmstate_iommu, + .qdev.reset = iommu_reset, + .qdev.props = (Property[]) { + DEFINE_PROP_HEX32("version", IOMMUState, version, 0), + DEFINE_PROP_END_OF_LIST(), + } +}; + +static void iommu_register_devices(void) +{ + sysbus_register_withprop(&iommu_info); +} + +device_init(iommu_register_devices) diff -Nru qemu-kvm-0.12.5+noroms/hw/sun4u.c qemu-kvm-0.14.1/hw/sun4u.c --- qemu-kvm-0.12.5+noroms/hw/sun4u.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/sun4u.c 2011-05-11 13:29:46.000000000 +0000 @@ -37,14 +37,31 @@ #include "ide.h" #include "loader.h" #include "elf.h" +#include "blockdev.h" //#define DEBUG_IRQ +//#define DEBUG_EBUS +//#define DEBUG_TIMER #ifdef DEBUG_IRQ -#define DPRINTF(fmt, ...) \ +#define CPUIRQ_DPRINTF(fmt, ...) \ do { printf("CPUIRQ: " fmt , ## __VA_ARGS__); } while (0) #else -#define DPRINTF(fmt, ...) +#define CPUIRQ_DPRINTF(fmt, ...) +#endif + +#ifdef DEBUG_EBUS +#define EBUS_DPRINTF(fmt, ...) \ + do { printf("EBUS: " fmt , ## __VA_ARGS__); } while (0) +#else +#define EBUS_DPRINTF(fmt, ...) +#endif + +#ifdef DEBUG_TIMER +#define TIMER_DPRINTF(fmt, ...) \ + do { printf("TIMER: " fmt , ## __VA_ARGS__); } while (0) +#else +#define TIMER_DPRINTF(fmt, ...) #endif #define KERNEL_LOAD_ADDR 0x00404000 @@ -54,7 +71,7 @@ #define PROM_VADDR 0x000ffd00000ULL #define APB_SPECIAL_BASE 0x1fe00000000ULL #define APB_MEM_BASE 0x1ff00000000ULL -#define VGA_BASE (APB_MEM_BASE + 0x400000ULL) +#define APB_PCI_IO_BASE (APB_SPECIAL_BASE + 0x02000000ULL) #define PROM_FILENAME "openbios-sparc64" #define NVRAM_SIZE 0x2000 #define MAX_IDE_BUS 2 @@ -65,7 +82,6 @@ #define MAX_PILS 16 -#define TICK_INT_DIS 0x8000000000000000ULL #define TICK_MAX 0x7fffffffffffffffULL struct hwdef { @@ -90,7 +106,11 @@ void DMA_hold_DREQ (int nchan) {} void DMA_release_DREQ (int nchan) {} void DMA_schedule(int nchan) {} -void DMA_init (int high_page_enable) {} + +void DMA_init(int high_page_enable, qemu_irq *cpu_request_exit) +{ +} + void DMA_register_channel (int nchan, DMA_transfer_handler transfer_handler, void *opaque) @@ -103,16 +123,15 @@ return 0; } -static int sun4u_NVRAM_set_params (m48t59_t *nvram, uint16_t NVRAM_size, - const char *arch, - ram_addr_t RAM_size, - const char *boot_devices, - uint32_t kernel_image, uint32_t kernel_size, - const char *cmdline, - uint32_t initrd_image, uint32_t initrd_size, - uint32_t NVRAM_image, - int width, int height, int depth, - const uint8_t *macaddr) +static int sun4u_NVRAM_set_params(M48t59State *nvram, uint16_t NVRAM_size, + const char *arch, ram_addr_t RAM_size, + const char *boot_devices, + uint32_t kernel_image, uint32_t kernel_size, + const char *cmdline, + uint32_t initrd_image, uint32_t initrd_size, + uint32_t NVRAM_image, + int width, int height, int depth, + const uint8_t *macaddr) { unsigned int i; uint32_t start, end; @@ -162,6 +181,7 @@ int linux_boot; unsigned int i; long kernel_size; + uint8_t *ptr; linux_boot = (kernel_filename != NULL); @@ -174,8 +194,8 @@ #else bswap_needed = 0; #endif - kernel_size = load_elf(kernel_filename, 0, NULL, NULL, NULL, - 1, ELF_MACHINE, 0); + kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, + NULL, NULL, 1, ELF_MACHINE, 0); if (kernel_size < 0) kernel_size = load_aout(kernel_filename, KERNEL_LOAD_ADDR, RAM_size - KERNEL_LOAD_ADDR, bswap_needed, @@ -204,9 +224,10 @@ } if (*initrd_size > 0) { for (i = 0; i < 64 * TARGET_PAGE_SIZE; i += TARGET_PAGE_SIZE) { - if (ldl_phys(KERNEL_LOAD_ADDR + i) == 0x48647253) { // HdrS - stl_phys(KERNEL_LOAD_ADDR + i + 16, INITRD_LOAD_ADDR); - stl_phys(KERNEL_LOAD_ADDR + i + 20, *initrd_size); + ptr = rom_ptr(KERNEL_LOAD_ADDR + i); + if (ldl_p(ptr + 8) == 0x48647253) { /* HdrS */ + stl_p(ptr + 24, INITRD_LOAD_ADDR + KERNEL_LOAD_ADDR - 0x4000); + stl_p(ptr + 28, *initrd_size); break; } } @@ -225,43 +246,71 @@ void cpu_check_irqs(CPUState *env) { - uint32_t pil = env->pil_in | (env->softint & ~SOFTINT_TIMER) | - ((env->softint & SOFTINT_TIMER) << 14); + uint32_t pil = env->pil_in | + (env->softint & ~(SOFTINT_TIMER | SOFTINT_STIMER)); + + /* check if TM or SM in SOFTINT are set + setting these also causes interrupt 14 */ + if (env->softint & (SOFTINT_TIMER | SOFTINT_STIMER)) { + pil |= 1 << 14; + } + + if (!pil) { + if (env->interrupt_request & CPU_INTERRUPT_HARD) { + CPUIRQ_DPRINTF("Reset CPU IRQ (current interrupt %x)\n", + env->interrupt_index); + env->interrupt_index = 0; + cpu_reset_interrupt(env, CPU_INTERRUPT_HARD); + } + return; + } + + if (cpu_interrupts_enabled(env)) { - if (pil && (env->interrupt_index == 0 || - (env->interrupt_index & ~15) == TT_EXTINT)) { unsigned int i; - for (i = 15; i > 0; i--) { + for (i = 15; i > env->psrpil; i--) { if (pil & (1 << i)) { int old_interrupt = env->interrupt_index; + int new_interrupt = TT_EXTINT | i; - env->interrupt_index = TT_EXTINT | i; - if (old_interrupt != env->interrupt_index) { - DPRINTF("Set CPU IRQ %d\n", i); + if (env->tl > 0 && cpu_tsptr(env)->tt > new_interrupt) { + CPUIRQ_DPRINTF("Not setting CPU IRQ: TL=%d " + "current %x >= pending %x\n", + env->tl, cpu_tsptr(env)->tt, new_interrupt); + } else if (old_interrupt != new_interrupt) { + env->interrupt_index = new_interrupt; + CPUIRQ_DPRINTF("Set CPU IRQ %d old=%x new=%x\n", i, + old_interrupt, new_interrupt); cpu_interrupt(env, CPU_INTERRUPT_HARD); } break; } } - } else if (!pil && (env->interrupt_index & ~15) == TT_EXTINT) { - DPRINTF("Reset CPU IRQ %d\n", env->interrupt_index & 15); - env->interrupt_index = 0; - cpu_reset_interrupt(env, CPU_INTERRUPT_HARD); + } else { + CPUIRQ_DPRINTF("Interrupts disabled, pil=%08x pil_in=%08x softint=%08x " + "current interrupt %x\n", + pil, env->pil_in, env->softint, env->interrupt_index); } } +static void cpu_kick_irq(CPUState *env) +{ + env->halted = 0; + cpu_check_irqs(env); +} + static void cpu_set_irq(void *opaque, int irq, int level) { CPUState *env = opaque; if (level) { - DPRINTF("Raise CPU IRQ %d\n", irq); + CPUIRQ_DPRINTF("Raise CPU IRQ %d\n", irq); env->halted = 0; env->pil_in |= 1 << irq; cpu_check_irqs(env); } else { - DPRINTF("Lower CPU IRQ %d\n", irq); + CPUIRQ_DPRINTF("Lower CPU IRQ %d\n", irq); env->pil_in &= ~(1 << irq); cpu_check_irqs(env); } @@ -272,6 +321,52 @@ uint64_t prom_addr; } ResetData; +void cpu_put_timer(QEMUFile *f, CPUTimer *s) +{ + qemu_put_be32s(f, &s->frequency); + qemu_put_be32s(f, &s->disabled); + qemu_put_be64s(f, &s->disabled_mask); + qemu_put_sbe64s(f, &s->clock_offset); + + qemu_put_timer(f, s->qtimer); +} + +void cpu_get_timer(QEMUFile *f, CPUTimer *s) +{ + qemu_get_be32s(f, &s->frequency); + qemu_get_be32s(f, &s->disabled); + qemu_get_be64s(f, &s->disabled_mask); + qemu_get_sbe64s(f, &s->clock_offset); + + qemu_get_timer(f, s->qtimer); +} + +static CPUTimer* cpu_timer_create(const char* name, CPUState *env, + QEMUBHFunc *cb, uint32_t frequency, + uint64_t disabled_mask) +{ + CPUTimer *timer = qemu_mallocz(sizeof (CPUTimer)); + + timer->name = name; + timer->frequency = frequency; + timer->disabled_mask = disabled_mask; + + timer->disabled = 1; + timer->clock_offset = qemu_get_clock(vm_clock); + + timer->qtimer = qemu_new_timer(vm_clock, cb, env); + + return timer; +} + +static void cpu_timer_reset(CPUTimer *timer) +{ + timer->disabled = 1; + timer->clock_offset = qemu_get_clock(vm_clock); + + qemu_del_timer(timer->qtimer); +} + static void main_cpu_reset(void *opaque) { ResetData *s = (ResetData *)opaque; @@ -279,15 +374,11 @@ static unsigned int nr_resets; cpu_reset(env); - env->tick_cmpr = TICK_INT_DIS | 0; - ptimer_set_limit(env->tick, TICK_MAX, 1); - ptimer_run(env->tick, 1); - env->stick_cmpr = TICK_INT_DIS | 0; - ptimer_set_limit(env->stick, TICK_MAX, 1); - ptimer_run(env->stick, 1); - env->hstick_cmpr = TICK_INT_DIS | 0; - ptimer_set_limit(env->hstick, TICK_MAX, 1); - ptimer_run(env->hstick, 1); + + cpu_timer_reset(env->tick); + cpu_timer_reset(env->stick); + cpu_timer_reset(env->hstick); + env->gregs[1] = 0; // Memory start env->gregs[2] = ram_size; // Memory size env->gregs[3] = 0; // Machine description XXX @@ -304,50 +395,134 @@ { CPUState *env = opaque; - if (!(env->tick_cmpr & TICK_INT_DIS)) { - env->softint |= SOFTINT_TIMER; - cpu_interrupt(env, CPU_INTERRUPT_TIMER); + CPUTimer* timer = env->tick; + + if (timer->disabled) { + CPUIRQ_DPRINTF("tick_irq: softint disabled\n"); + return; + } else { + CPUIRQ_DPRINTF("tick: fire\n"); } + + env->softint |= SOFTINT_TIMER; + cpu_kick_irq(env); } static void stick_irq(void *opaque) { CPUState *env = opaque; - if (!(env->stick_cmpr & TICK_INT_DIS)) { - env->softint |= SOFTINT_STIMER; - cpu_interrupt(env, CPU_INTERRUPT_TIMER); + CPUTimer* timer = env->stick; + + if (timer->disabled) { + CPUIRQ_DPRINTF("stick_irq: softint disabled\n"); + return; + } else { + CPUIRQ_DPRINTF("stick: fire\n"); } + + env->softint |= SOFTINT_STIMER; + cpu_kick_irq(env); } static void hstick_irq(void *opaque) { CPUState *env = opaque; - if (!(env->hstick_cmpr & TICK_INT_DIS)) { - cpu_interrupt(env, CPU_INTERRUPT_TIMER); + CPUTimer* timer = env->hstick; + + if (timer->disabled) { + CPUIRQ_DPRINTF("hstick_irq: softint disabled\n"); + return; + } else { + CPUIRQ_DPRINTF("hstick: fire\n"); } + + env->softint |= SOFTINT_STIMER; + cpu_kick_irq(env); } -void cpu_tick_set_count(void *opaque, uint64_t count) +static int64_t cpu_to_timer_ticks(int64_t cpu_ticks, uint32_t frequency) { - ptimer_set_count(opaque, -count); + return muldiv64(cpu_ticks, get_ticks_per_sec(), frequency); +} + +static uint64_t timer_to_cpu_ticks(int64_t timer_ticks, uint32_t frequency) +{ + return muldiv64(timer_ticks, frequency, get_ticks_per_sec()); +} + +void cpu_tick_set_count(CPUTimer *timer, uint64_t count) +{ + uint64_t real_count = count & ~timer->disabled_mask; + uint64_t disabled_bit = count & timer->disabled_mask; + + int64_t vm_clock_offset = qemu_get_clock(vm_clock) - + cpu_to_timer_ticks(real_count, timer->frequency); + + TIMER_DPRINTF("%s set_count count=0x%016lx (%s) p=%p\n", + timer->name, real_count, + timer->disabled?"disabled":"enabled", timer); + + timer->disabled = disabled_bit ? 1 : 0; + timer->clock_offset = vm_clock_offset; } -uint64_t cpu_tick_get_count(void *opaque) +uint64_t cpu_tick_get_count(CPUTimer *timer) { - return -ptimer_get_count(opaque); + uint64_t real_count = timer_to_cpu_ticks( + qemu_get_clock(vm_clock) - timer->clock_offset, + timer->frequency); + + TIMER_DPRINTF("%s get_count count=0x%016lx (%s) p=%p\n", + timer->name, real_count, + timer->disabled?"disabled":"enabled", timer); + + if (timer->disabled) + real_count |= timer->disabled_mask; + + return real_count; } -void cpu_tick_set_limit(void *opaque, uint64_t limit) +void cpu_tick_set_limit(CPUTimer *timer, uint64_t limit) { - ptimer_set_limit(opaque, -limit, 0); + int64_t now = qemu_get_clock(vm_clock); + + uint64_t real_limit = limit & ~timer->disabled_mask; + timer->disabled = (limit & timer->disabled_mask) ? 1 : 0; + + int64_t expires = cpu_to_timer_ticks(real_limit, timer->frequency) + + timer->clock_offset; + + if (expires < now) { + expires = now + 1; + } + + TIMER_DPRINTF("%s set_limit limit=0x%016lx (%s) p=%p " + "called with limit=0x%016lx at 0x%016lx (delta=0x%016lx)\n", + timer->name, real_limit, + timer->disabled?"disabled":"enabled", + timer, limit, + timer_to_cpu_ticks(now - timer->clock_offset, + timer->frequency), + timer_to_cpu_ticks(expires - now, timer->frequency)); + + if (!real_limit) { + TIMER_DPRINTF("%s set_limit limit=ZERO - not starting timer\n", + timer->name); + qemu_del_timer(timer->qtimer); + } else if (timer->disabled) { + qemu_del_timer(timer->qtimer); + } else { + qemu_mod_timer(timer->qtimer, expires); + } } static void ebus_mmio_mapfunc(PCIDevice *pci_dev, int region_num, pcibus_t addr, pcibus_t size, int type) { - DPRINTF("Mapping region %d registers at %08x\n", region_num, addr); + EBUS_DPRINTF("Mapping region %d registers at %" FMT_PCIBUS "\n", + region_num, addr); switch (region_num) { case 0: isa_mmio_init(addr, 0x1000000); @@ -388,7 +563,6 @@ s->config[0x09] = 0x00; // programming i/f pci_config_set_class(s->config, PCI_CLASS_BRIDGE_OTHER); s->config[0x0D] = 0x0a; // latency_timer - s->config[PCI_HEADER_TYPE] = PCI_HEADER_TYPE_NORMAL; // header_type pci_register_bar(s, 0, 0x1000000, PCI_BASE_ADDRESS_SPACE_MEMORY, ebus_mmio_mapfunc); @@ -410,6 +584,12 @@ device_init(pci_ebus_register); +static uint64_t translate_prom_address(void *opaque, uint64_t addr) +{ + target_phys_addr_t *base_addr = (target_phys_addr_t *)opaque; + return addr + *base_addr - PROM_VADDR; +} + /* Boot PROM (OpenBIOS) */ static void prom_init(target_phys_addr_t addr, const char *bios_name) { @@ -430,8 +610,8 @@ } filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); if (filename) { - ret = load_elf(filename, addr - PROM_VADDR, NULL, NULL, NULL, - 1, ELF_MACHINE, 0); + ret = load_elf(filename, translate_prom_address, &addr, + NULL, NULL, NULL, 1, ELF_MACHINE, 0); if (ret < 0 || ret > PROM_SIZE_MAX) { ret = load_image_targphys(filename, addr, PROM_SIZE_MAX); } @@ -449,7 +629,7 @@ { ram_addr_t prom_offset; - prom_offset = qemu_ram_alloc(PROM_SIZE_MAX); + prom_offset = qemu_ram_alloc(NULL, "sun4u.prom", PROM_SIZE_MAX); sysbus_init_mmio(dev, PROM_SIZE_MAX, prom_offset | IO_MEM_ROM); return 0; } @@ -485,7 +665,7 @@ RAM_size = d->size; - ram_offset = qemu_ram_alloc(RAM_size); + ram_offset = qemu_ram_alloc(NULL, "sun4u.ram", RAM_size); sysbus_init_mmio(dev, RAM_size, ram_offset); return 0; } @@ -527,9 +707,12 @@ static CPUState *cpu_devinit(const char *cpu_model, const struct hwdef *hwdef) { CPUState *env; - QEMUBH *bh; ResetData *reset_info; + uint32_t tick_frequency = 100*1000000; + uint32_t stick_frequency = 100*1000000; + uint32_t hstick_frequency = 100*1000000; + if (!cpu_model) cpu_model = hwdef->default_cpu_model; env = cpu_init(cpu_model); @@ -537,17 +720,15 @@ fprintf(stderr, "Unable to find Sparc CPU definition\n"); exit(1); } - bh = qemu_bh_new(tick_irq, env); - env->tick = ptimer_init(bh); - ptimer_set_period(env->tick, 1ULL); - - bh = qemu_bh_new(stick_irq, env); - env->stick = ptimer_init(bh); - ptimer_set_period(env->stick, 1ULL); - - bh = qemu_bh_new(hstick_irq, env); - env->hstick = ptimer_init(bh); - ptimer_set_period(env->hstick, 1ULL); + + env->tick = cpu_timer_create("tick", env, tick_irq, + tick_frequency, TICK_NPT_MASK); + + env->stick = cpu_timer_create("stick", env, stick_irq, + stick_frequency, TICK_INT_DIS); + + env->hstick = cpu_timer_create("hstick", env, hstick_irq, + hstick_frequency, TICK_INT_DIS); reset_info = qemu_mallocz(sizeof(ResetData)); reset_info->env = env; @@ -564,7 +745,7 @@ const struct hwdef *hwdef) { CPUState *env; - m48t59_t *nvram; + M48t59State *nvram; unsigned int i; long initrd_size, kernel_size; PCIBus *pci_bus, *pci_bus2, *pci_bus3; @@ -585,8 +766,8 @@ irq = qemu_allocate_irqs(cpu_set_irq, env, MAX_PILS); pci_bus = pci_apb_init(APB_SPECIAL_BASE, APB_MEM_BASE, irq, &pci_bus2, &pci_bus3); - isa_mem_base = VGA_BASE; - pci_vga_init(pci_bus, 0, 0); + isa_mem_base = APB_PCI_IO_BASE; + pci_vga_init(pci_bus); // XXX Should be pci_bus3 pci_ebus_init(pci_bus, -1); @@ -594,7 +775,7 @@ i = 0; if (hwdef->console_serial_base) { serial_mm_init(hwdef->console_serial_base, 0, NULL, 115200, - serial_hds[i], 1); + serial_hds[i], 1, 1); i++; } for(; i < MAX_SERIAL_PORTS; i++) { @@ -650,10 +831,13 @@ fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, KERNEL_LOAD_ADDR); fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, kernel_size); if (kernel_cmdline) { - fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_CMDLINE, CMDLINE_ADDR); - pstrcpy_targphys("cmdline", CMDLINE_ADDR, TARGET_PAGE_SIZE, kernel_cmdline); + fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, + strlen(kernel_cmdline) + 1); + fw_cfg_add_bytes(fw_cfg, FW_CFG_CMDLINE_DATA, + (uint8_t*)strdup(kernel_cmdline), + strlen(kernel_cmdline) + 1); } else { - fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_CMDLINE, 0); + fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, 0); } fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, INITRD_LOAD_ADDR); fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, initrd_size); @@ -675,7 +859,7 @@ static const struct hwdef hwdefs[] = { /* Sun4u generic PC-like machine */ { - .default_cpu_model = "TI UltraSparc II", + .default_cpu_model = "TI UltraSparc IIi", .machine_id = sun4u_id, .prom_addr = 0x1fff0000000ULL, .console_serial_base = 0, diff -Nru qemu-kvm-0.12.5+noroms/hw/syborg.c qemu-kvm-0.14.1/hw/syborg.c --- qemu-kvm-0.12.5+noroms/hw/syborg.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/syborg.c 2011-05-11 13:29:46.000000000 +0000 @@ -51,7 +51,7 @@ } /* RAM at address zero. */ - ram_addr = qemu_ram_alloc(ram_size); + ram_addr = qemu_ram_alloc(NULL, "syborg.ram", ram_size); cpu_register_physical_memory(0, ram_size, ram_addr | IO_MEM_RAM); cpu_pic = arm_pic_init_cpu(env); diff -Nru qemu-kvm-0.12.5+noroms/hw/syborg_fb.c qemu-kvm-0.14.1/hw/syborg_fb.c --- qemu-kvm-0.12.5+noroms/hw/syborg_fb.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/syborg_fb.c 2011-05-11 13:29:46.000000000 +0000 @@ -510,7 +510,8 @@ sysbus_init_irq(dev, &s->irq); iomemtype = cpu_register_io_memory(syborg_fb_readfn, - syborg_fb_writefn, s); + syborg_fb_writefn, s, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, 0x1000, iomemtype); s->ds = graphic_console_init(syborg_fb_update_display, @@ -526,7 +527,7 @@ if (!s->rows) s->rows = ds_get_height(s->ds); - register_savevm("syborg_framebuffer", -1, 1, + register_savevm(&dev->qdev, "syborg_framebuffer", -1, 1, syborg_fb_save, syborg_fb_load, s); return 0; } diff -Nru qemu-kvm-0.12.5+noroms/hw/syborg_interrupt.c qemu-kvm-0.14.1/hw/syborg_interrupt.c --- qemu-kvm-0.12.5+noroms/hw/syborg_interrupt.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/syborg_interrupt.c 2011-05-11 13:29:46.000000000 +0000 @@ -210,11 +210,13 @@ sysbus_init_irq(dev, &s->parent_irq); qdev_init_gpio_in(&dev->qdev, syborg_int_set_irq, s->num_irqs); iomemtype = cpu_register_io_memory(syborg_int_readfn, - syborg_int_writefn, s); + syborg_int_writefn, s, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, 0x1000, iomemtype); s->flags = qemu_mallocz(s->num_irqs * sizeof(syborg_int_flags)); - register_savevm("syborg_int", -1, 1, syborg_int_save, syborg_int_load, s); + register_savevm(&dev->qdev, "syborg_int", -1, 1, syborg_int_save, + syborg_int_load, s); return 0; } diff -Nru qemu-kvm-0.12.5+noroms/hw/syborg_keyboard.c qemu-kvm-0.14.1/hw/syborg_keyboard.c --- qemu-kvm-0.12.5+noroms/hw/syborg_keyboard.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/syborg_keyboard.c 2011-05-11 13:29:46.000000000 +0000 @@ -210,7 +210,8 @@ sysbus_init_irq(dev, &s->irq); iomemtype = cpu_register_io_memory(syborg_keyboard_readfn, - syborg_keyboard_writefn, s); + syborg_keyboard_writefn, s, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, 0x1000, iomemtype); if (s->fifo_size <= 0) { fprintf(stderr, "syborg_keyboard: fifo too small\n"); @@ -220,7 +221,7 @@ qemu_add_kbd_event_handler(syborg_keyboard_event, s); - register_savevm("syborg_keyboard", -1, 1, + register_savevm(&dev->qdev, "syborg_keyboard", -1, 1, syborg_keyboard_save, syborg_keyboard_load, s); return 0; } diff -Nru qemu-kvm-0.12.5+noroms/hw/syborg_pointer.c qemu-kvm-0.14.1/hw/syborg_pointer.c --- qemu-kvm-0.12.5+noroms/hw/syborg_pointer.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/syborg_pointer.c 2011-05-11 13:29:46.000000000 +0000 @@ -206,7 +206,8 @@ sysbus_init_irq(dev, &s->irq); iomemtype = cpu_register_io_memory(syborg_pointer_readfn, - syborg_pointer_writefn, s); + syborg_pointer_writefn, s, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, 0x1000, iomemtype); if (s->fifo_size <= 0) { @@ -218,7 +219,7 @@ qemu_add_mouse_event_handler(syborg_pointer_event, s, s->absolute, "Syborg Pointer"); - register_savevm("syborg_pointer", -1, 1, + register_savevm(&dev->qdev, "syborg_pointer", -1, 1, syborg_pointer_save, syborg_pointer_load, s); return 0; } diff -Nru qemu-kvm-0.12.5+noroms/hw/syborg_rtc.c qemu-kvm-0.14.1/hw/syborg_rtc.c --- qemu-kvm-0.12.5+noroms/hw/syborg_rtc.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/syborg_rtc.c 2011-05-11 13:29:46.000000000 +0000 @@ -130,13 +130,15 @@ int iomemtype; iomemtype = cpu_register_io_memory(syborg_rtc_readfn, - syborg_rtc_writefn, s); + syborg_rtc_writefn, s, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, 0x1000, iomemtype); qemu_get_timedate(&tm, 0); s->offset = (uint64_t)mktime(&tm) * 1000000000; - register_savevm("syborg_rtc", -1, 1, syborg_rtc_save, syborg_rtc_load, s); + register_savevm(&dev->qdev, "syborg_rtc", -1, 1, + syborg_rtc_save, syborg_rtc_load, s); return 0; } diff -Nru qemu-kvm-0.12.5+noroms/hw/syborg_serial.c qemu-kvm-0.14.1/hw/syborg_serial.c --- qemu-kvm-0.12.5+noroms/hw/syborg_serial.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/syborg_serial.c 2011-05-11 13:29:46.000000000 +0000 @@ -322,7 +322,8 @@ sysbus_init_irq(dev, &s->irq); iomemtype = cpu_register_io_memory(syborg_serial_readfn, - syborg_serial_writefn, s); + syborg_serial_writefn, s, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, 0x1000, iomemtype); s->chr = qdev_init_chardev(&dev->qdev); if (s->chr) { @@ -335,7 +336,7 @@ } s->read_fifo = qemu_mallocz(s->fifo_size * sizeof(s->read_fifo[0])); - register_savevm("syborg_serial", -1, 1, + register_savevm(&dev->qdev, "syborg_serial", -1, 1, syborg_serial_save, syborg_serial_load, s); return 0; } diff -Nru qemu-kvm-0.12.5+noroms/hw/syborg_timer.c qemu-kvm-0.14.1/hw/syborg_timer.c --- qemu-kvm-0.12.5+noroms/hw/syborg_timer.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/syborg_timer.c 2011-05-11 13:29:46.000000000 +0000 @@ -215,13 +215,14 @@ } sysbus_init_irq(dev, &s->irq); iomemtype = cpu_register_io_memory(syborg_timer_readfn, - syborg_timer_writefn, s); + syborg_timer_writefn, s, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, 0x1000, iomemtype); bh = qemu_bh_new(syborg_timer_tick, s); s->timer = ptimer_init(bh); ptimer_set_freq(s->timer, s->freq); - register_savevm("syborg_timer", -1, 1, + register_savevm(&dev->qdev, "syborg_timer", -1, 1, syborg_timer_save, syborg_timer_load, s); return 0; } diff -Nru qemu-kvm-0.12.5+noroms/hw/syborg_virtio.c qemu-kvm-0.14.1/hw/syborg_virtio.c --- qemu-kvm-0.12.5+noroms/hw/syborg_virtio.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/syborg_virtio.c 2011-05-11 13:29:46.000000000 +0000 @@ -25,6 +25,7 @@ #include "syborg.h" #include "sysbus.h" #include "virtio.h" +#include "virtio-net.h" #include "sysemu.h" //#define DEBUG_SYBORG_VIRTIO @@ -66,6 +67,8 @@ uint32_t int_enable; uint32_t id; NICConf nic; + uint32_t host_features; + virtio_net_conf net; } SyborgVirtIOProxy; static uint32_t syborg_virtio_readl(void *opaque, target_phys_addr_t offset) @@ -86,11 +89,10 @@ ret = s->id; break; case SYBORG_VIRTIO_HOST_FEATURES: - ret = vdev->get_features(vdev); - ret |= vdev->binding->get_features(s); + ret = s->host_features; break; case SYBORG_VIRTIO_GUEST_FEATURES: - ret = vdev->features; + ret = vdev->guest_features; break; case SYBORG_VIRTIO_QUEUE_BASE: ret = virtio_queue_get_addr(vdev, vdev->queue_sel); @@ -132,7 +134,7 @@ case SYBORG_VIRTIO_GUEST_FEATURES: if (vdev->set_features) vdev->set_features(vdev, value); - vdev->features = value; + vdev->guest_features = value; break; case SYBORG_VIRTIO_QUEUE_BASE: if (value == 0) @@ -148,7 +150,7 @@ virtio_queue_notify(vdev, value); break; case SYBORG_VIRTIO_STATUS: - vdev->status = value & 0xFF; + virtio_set_status(vdev, value & 0xFF); if (vdev->status == 0) virtio_reset(vdev); break; @@ -244,9 +246,8 @@ static unsigned syborg_virtio_get_features(void *opaque) { - unsigned ret = 0; - ret |= (1 << VIRTIO_F_NOTIFY_ON_EMPTY); - return ret; + SyborgVirtIOProxy *proxy = opaque; + return proxy->host_features; } static VirtIOBindings syborg_virtio_bindings = { @@ -264,7 +265,8 @@ proxy->vdev->nvectors = 0; sysbus_init_irq(&proxy->busdev, &proxy->irq); iomemtype = cpu_register_io_memory(syborg_virtio_readfn, - syborg_virtio_writefn, proxy); + syborg_virtio_writefn, proxy, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(&proxy->busdev, 0x1000, iomemtype); proxy->id = ((uint32_t)0x1af4 << 16) | vdev->device_id; @@ -272,6 +274,8 @@ qemu_register_reset(virtio_reset, vdev); virtio_bind_device(vdev, &syborg_virtio_bindings, proxy); + proxy->host_features |= (0x1 << VIRTIO_F_NOTIFY_ON_EMPTY); + proxy->host_features = vdev->get_features(vdev, proxy->host_features); return 0; } @@ -282,7 +286,7 @@ VirtIODevice *vdev; SyborgVirtIOProxy *proxy = FROM_SYSBUS(SyborgVirtIOProxy, dev); - vdev = virtio_net_init(&dev->qdev, &proxy->nic); + vdev = virtio_net_init(&dev->qdev, &proxy->nic, &proxy->net); return syborg_virtio_init(proxy, vdev); } @@ -292,6 +296,12 @@ .qdev.size = sizeof(SyborgVirtIOProxy), .qdev.props = (Property[]) { DEFINE_NIC_PROPERTIES(SyborgVirtIOProxy, nic), + DEFINE_VIRTIO_NET_FEATURES(SyborgVirtIOProxy, host_features), + DEFINE_PROP_UINT32("x-txtimer", SyborgVirtIOProxy, + net.txtimer, TX_TIMER_INTERVAL), + DEFINE_PROP_INT32("x-txburst", SyborgVirtIOProxy, + net.txburst, TX_BURST), + DEFINE_PROP_STRING("tx", SyborgVirtIOProxy, net.tx), DEFINE_PROP_END_OF_LIST(), } }; diff -Nru qemu-kvm-0.12.5+noroms/hw/sysbus.c qemu-kvm-0.14.1/hw/sysbus.c --- qemu-kvm-0.12.5+noroms/hw/sysbus.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/sysbus.c 2011-05-11 13:29:46.000000000 +0000 @@ -22,11 +22,13 @@ #include "monitor.h" static void sysbus_dev_print(Monitor *mon, DeviceState *dev, int indent); +static char *sysbus_get_fw_dev_path(DeviceState *dev); struct BusInfo system_bus_info = { .name = "System", .size = sizeof(BusState), .print_dev = sysbus_dev_print, + .get_fw_dev_path = sysbus_get_fw_dev_path, }; void sysbus_connect_irq(SysBusDevice *dev, int n, qemu_irq irq) @@ -82,7 +84,8 @@ } } -void sysbus_init_mmio(SysBusDevice *dev, target_phys_addr_t size, int iofunc) +void sysbus_init_mmio(SysBusDevice *dev, target_phys_addr_t size, + ram_addr_t iofunc) { int n; @@ -105,6 +108,16 @@ dev->mmio[n].cb = cb; } +void sysbus_init_ioports(SysBusDevice *dev, pio_addr_t ioport, pio_addr_t size) +{ + pio_addr_t i; + + for (i = 0; i < size; i++) { + assert(dev->num_pio < QDEV_MAX_PIO); + dev->pio[dev->num_pio++] = ioport++; + } +} + static int sysbus_device_init(DeviceState *dev, DeviceInfo *base) { SysBusDeviceInfo *info = container_of(base, SysBusDeviceInfo, qdev); @@ -170,3 +183,21 @@ indent, "", s->mmio[i].addr, s->mmio[i].size); } } + +static char *sysbus_get_fw_dev_path(DeviceState *dev) +{ + SysBusDevice *s = sysbus_from_qdev(dev); + char path[40]; + int off; + + off = snprintf(path, sizeof(path), "%s", qdev_fw_name(dev)); + + if (s->num_mmio) { + snprintf(path + off, sizeof(path) - off, "@"TARGET_FMT_plx, + s->mmio[0].addr); + } else if (s->num_pio) { + snprintf(path + off, sizeof(path) - off, "@i%04x", s->pio[0]); + } + + return strdup(path); +} diff -Nru qemu-kvm-0.12.5+noroms/hw/sysbus.h qemu-kvm-0.14.1/hw/sysbus.h --- qemu-kvm-0.12.5+noroms/hw/sysbus.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/sysbus.h 2011-05-11 13:29:46.000000000 +0000 @@ -6,6 +6,7 @@ #include "qdev.h" #define QDEV_MAX_MMIO 32 +#define QDEV_MAX_PIO 32 #define QDEV_MAX_IRQ 256 typedef struct SysBusDevice SysBusDevice; @@ -21,8 +22,10 @@ target_phys_addr_t addr; target_phys_addr_t size; mmio_mapfunc cb; - int iofunc; + ram_addr_t iofunc; } mmio[QDEV_MAX_MMIO]; + int num_pio; + pio_addr_t pio[QDEV_MAX_PIO]; }; typedef int (*sysbus_initfn)(SysBusDevice *dev); @@ -39,11 +42,13 @@ void sysbus_register_dev(const char *name, size_t size, sysbus_initfn init); void sysbus_register_withprop(SysBusDeviceInfo *info); void *sysbus_new(void); -void sysbus_init_mmio(SysBusDevice *dev, target_phys_addr_t size, int iofunc); +void sysbus_init_mmio(SysBusDevice *dev, target_phys_addr_t size, + ram_addr_t iofunc); void sysbus_init_mmio_cb(SysBusDevice *dev, target_phys_addr_t size, mmio_mapfunc cb); void sysbus_init_irq(SysBusDevice *dev, qemu_irq *p); void sysbus_pass_irq(SysBusDevice *dev, SysBusDevice *target); +void sysbus_init_ioports(SysBusDevice *dev, pio_addr_t ioport, pio_addr_t size); void sysbus_connect_irq(SysBusDevice *dev, int n, qemu_irq irq); diff -Nru qemu-kvm-0.12.5+noroms/hw/tc58128.c qemu-kvm-0.14.1/hw/tc58128.c --- qemu-kvm-0.12.5+noroms/hw/tc58128.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/tc58128.c 2011-05-11 13:29:46.000000000 +0000 @@ -82,7 +82,7 @@ break; default: fprintf(stderr, "unknown flash command 0x%02x\n", command); - assert(0); + abort(); } } @@ -110,12 +110,12 @@ break; default: /* Invalid data */ - assert(0); + abort(); } dev->address_cycle++; break; default: - assert(0); + abort(); } } @@ -164,7 +164,7 @@ *periph_pdtra &= 0xff00; *periph_pdtra |= handle_read(&tc58128_devs[dev]); } else { - assert(0); + abort(); } return 1; } diff -Nru qemu-kvm-0.12.5+noroms/hw/tc6393xb.c qemu-kvm-0.14.1/hw/tc6393xb.c --- qemu-kvm-0.12.5+noroms/hw/tc6393xb.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/tc6393xb.c 2011-05-11 13:29:46.000000000 +0000 @@ -590,10 +590,10 @@ s->flash = nand_init(NAND_MFR_TOSHIBA, 0x76); iomemtype = cpu_register_io_memory(tc6393xb_readfn, - tc6393xb_writefn, s); + tc6393xb_writefn, s, DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory(base, 0x10000, iomemtype); - s->vram_addr = qemu_ram_alloc(0x100000); + s->vram_addr = qemu_ram_alloc(NULL, "tc6393xb.vram", 0x100000); s->vram_ptr = qemu_get_ram_ptr(s->vram_addr); cpu_register_physical_memory(base + 0x100000, 0x100000, s->vram_addr); s->scr_width = 480; diff -Nru qemu-kvm-0.12.5+noroms/hw/tc6393xb_template.h qemu-kvm-0.14.1/hw/tc6393xb_template.h --- qemu-kvm-0.12.5+noroms/hw/tc6393xb_template.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/tc6393xb_template.h 2011-05-11 13:29:46.000000000 +0000 @@ -38,12 +38,10 @@ static void glue(tc6393xb_draw_graphic, BITS)(TC6393xbState *s) { int i; - int w_display; uint16_t *data_buffer; uint8_t *data_display; data_buffer = s->vram_ptr; - w_display = s->scr_width * BITS / 8; data_display = ds_get_data(s->ds); for(i = 0; i < s->scr_height; i++) { #if (BITS == 16) diff -Nru qemu-kvm-0.12.5+noroms/hw/tcx.c qemu-kvm-0.14.1/hw/tcx.c --- qemu-kvm-0.12.5+noroms/hw/tcx.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/tcx.c 2011-05-11 13:29:46.000000000 +0000 @@ -161,7 +161,7 @@ p8++; b = *p8++; g = *p8++; - r = *p8++; + r = *p8; if (bgr) dval = rgb_to_pixel32bgr(r, g, b); else @@ -510,7 +510,7 @@ int size; uint8_t *vram_base; - vram_offset = qemu_ram_alloc(s->vram_size * (1 + 4 + 4)); + vram_offset = qemu_ram_alloc(NULL, "tcx.vram", s->vram_size * (1 + 4 + 4)); vram_base = qemu_get_ram_ptr(vram_offset); s->vram_offset = vram_offset; @@ -522,12 +522,13 @@ vram_base += size; /* DAC */ - io_memory = cpu_register_io_memory(tcx_dac_read, tcx_dac_write, s); + io_memory = cpu_register_io_memory(tcx_dac_read, tcx_dac_write, s, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, TCX_DAC_NREGS, io_memory); /* TEC (dummy) */ dummy_memory = cpu_register_io_memory(tcx_dummy_read, tcx_dummy_write, - s); + s, DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, TCX_TEC_NREGS, dummy_memory); /* THC: NetBSD writes here even with 8-bit display: dummy */ sysbus_init_mmio(dev, TCX_THC_NREGS_24, dummy_memory); diff -Nru qemu-kvm-0.12.5+noroms/hw/testdev.c qemu-kvm-0.14.1/hw/testdev.c --- qemu-kvm-0.12.5+noroms/hw/testdev.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/testdev.c 2011-05-11 13:29:46.000000000 +0000 @@ -1,3 +1,4 @@ +#include #include "hw.h" #include "qdev.h" #include "isa.h" @@ -27,21 +28,99 @@ return ram_size; } +extern qemu_irq *ioapic_irq_hack; + static void test_device_irq_line(void *opaque, uint32_t addr, uint32_t data) { - extern qemu_irq *ioapic_irq_hack; - qemu_set_irq(ioapic_irq_hack[addr - 0x2000], !!data); } +static uint32 test_device_ioport_data; + +static void test_device_ioport_write(void *opaque, uint32_t addr, uint32_t data) +{ + test_device_ioport_data = data; +} + +static uint32_t test_device_ioport_read(void *opaque, uint32_t addr) +{ + return test_device_ioport_data; +} + +static void test_device_flush_page(void *opaque, uint32_t addr, uint32_t data) +{ + target_phys_addr_t len = 4096; + void *a = cpu_physical_memory_map(data & ~0xffful, &len, 0); + + mprotect(a, 4096, PROT_NONE); + mprotect(a, 4096, PROT_READ|PROT_WRITE); + cpu_physical_memory_unmap(a, len, 0, 0); +} + +static char *iomem_buf; + +static uint32_t test_iomem_readb(void *opaque, target_phys_addr_t addr) +{ + return iomem_buf[addr]; +} + +static uint32_t test_iomem_readw(void *opaque, target_phys_addr_t addr) +{ + return *(uint16_t*)(iomem_buf + addr); +} + +static uint32_t test_iomem_readl(void *opaque, target_phys_addr_t addr) +{ + return *(uint32_t*)(iomem_buf + addr); +} + +static void test_iomem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + iomem_buf[addr] = val; +} + +static void test_iomem_writew(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + *(uint16_t*)(iomem_buf + addr) = val; +} + +static void test_iomem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + *(uint32_t*)(iomem_buf + addr) = val; +} + +static CPUReadMemoryFunc * const test_iomem_read[3] = { + test_iomem_readb, + test_iomem_readw, + test_iomem_readl, +}; + +static CPUWriteMemoryFunc * const test_iomem_write[3] = { + test_iomem_writeb, + test_iomem_writew, + test_iomem_writel, +}; + static int init_test_device(ISADevice *isa) { struct testdev *dev = DO_UPCAST(struct testdev, dev, isa); + int iomem; register_ioport_write(0xf1, 1, 1, test_device_serial_write, dev); register_ioport_write(0xf4, 1, 4, test_device_exit, dev); register_ioport_read(0xd1, 1, 4, test_device_memsize_read, dev); + register_ioport_read(0xe0, 1, 1, test_device_ioport_read, dev); + register_ioport_write(0xe0, 1, 1, test_device_ioport_write, dev); + register_ioport_read(0xe0, 1, 2, test_device_ioport_read, dev); + register_ioport_write(0xe0, 1, 2, test_device_ioport_write, dev); + register_ioport_read(0xe0, 1, 4, test_device_ioport_read, dev); + register_ioport_write(0xe0, 1, 4, test_device_ioport_write, dev); + register_ioport_write(0xe4, 1, 4, test_device_flush_page, dev); register_ioport_write(0x2000, 24, 1, test_device_irq_line, NULL); + iomem_buf = qemu_mallocz(0x10000); + iomem = cpu_register_io_memory(test_iomem_read, test_iomem_write, NULL, + DEVICE_NATIVE_ENDIAN); + cpu_register_physical_memory(0xff000000, 0x10000, iomem); return 0; } diff -Nru qemu-kvm-0.12.5+noroms/hw/tmp105.c qemu-kvm-0.14.1/hw/tmp105.c --- qemu-kvm-0.12.5+noroms/hw/tmp105.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/tmp105.c 2011-05-11 13:29:46.000000000 +0000 @@ -173,16 +173,12 @@ s->len = 0; } -static void tmp105_post_save(void *opaque) -{ - TMP105State *s = opaque; - s->faults = tmp105_faultq[(s->config >> 3) & 3]; /* F */ -} - static int tmp105_post_load(void *opaque, int version_id) { TMP105State *s = opaque; + s->faults = tmp105_faultq[(s->config >> 3) & 3]; /* F */ + tmp105_interrupt_update(s); return 0; } @@ -192,7 +188,6 @@ .version_id = 0, .minimum_version_id = 0, .minimum_version_id_old = 0, - .post_save = tmp105_post_save, .post_load = tmp105_post_load, .fields = (VMStateField []) { VMSTATE_UINT8(len, TMP105State), diff -Nru qemu-kvm-0.12.5+noroms/hw/tosa.c qemu-kvm-0.14.1/hw/tosa.c --- qemu-kvm-0.12.5+noroms/hw/tosa.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/tosa.c 2011-05-11 13:29:46.000000000 +0000 @@ -19,6 +19,8 @@ #include "boards.h" #include "i2c.h" #include "ssi.h" +#include "blockdev.h" +#include "sysbus.h" #define TOSA_RAM 0x04000000 #define TOSA_ROM 0x00800000 @@ -85,34 +87,34 @@ static void tosa_gpio_setup(PXA2xxState *cpu, - ScoopInfo *scp0, - ScoopInfo *scp1, + DeviceState *scp0, + DeviceState *scp1, TC6393xbState *tmio) { qemu_irq *outsignals = qemu_allocate_irqs(tosa_out_switch, cpu, 4); /* MMC/SD host */ pxa2xx_mmci_handlers(cpu->mmc, - scoop_gpio_in_get(scp0)[TOSA_GPIO_SD_WP], - qemu_irq_invert(pxa2xx_gpio_in_get(cpu->gpio)[TOSA_GPIO_nSD_DETECT])); + qdev_get_gpio_in(scp0, TOSA_GPIO_SD_WP), + qemu_irq_invert(qdev_get_gpio_in(cpu->gpio, TOSA_GPIO_nSD_DETECT))); /* Handle reset */ - pxa2xx_gpio_out_set(cpu->gpio, TOSA_GPIO_ON_RESET, cpu->reset); + qdev_connect_gpio_out(cpu->gpio, TOSA_GPIO_ON_RESET, cpu->reset); /* PCMCIA signals: card's IRQ and Card-Detect */ pxa2xx_pcmcia_set_irq_cb(cpu->pcmcia[0], - pxa2xx_gpio_in_get(cpu->gpio)[TOSA_GPIO_CF_IRQ], - pxa2xx_gpio_in_get(cpu->gpio)[TOSA_GPIO_CF_CD]); + qdev_get_gpio_in(cpu->gpio, TOSA_GPIO_CF_IRQ), + qdev_get_gpio_in(cpu->gpio, TOSA_GPIO_CF_CD)); pxa2xx_pcmcia_set_irq_cb(cpu->pcmcia[1], - pxa2xx_gpio_in_get(cpu->gpio)[TOSA_GPIO_JC_CF_IRQ], + qdev_get_gpio_in(cpu->gpio, TOSA_GPIO_JC_CF_IRQ), NULL); - scoop_gpio_out_set(scp1, TOSA_GPIO_BT_LED, outsignals[0]); - scoop_gpio_out_set(scp1, TOSA_GPIO_NOTE_LED, outsignals[1]); - scoop_gpio_out_set(scp1, TOSA_GPIO_CHRG_ERR_LED, outsignals[2]); - scoop_gpio_out_set(scp1, TOSA_GPIO_WLAN_LED, outsignals[3]); + qdev_connect_gpio_out(scp1, TOSA_GPIO_BT_LED, outsignals[0]); + qdev_connect_gpio_out(scp1, TOSA_GPIO_NOTE_LED, outsignals[1]); + qdev_connect_gpio_out(scp1, TOSA_GPIO_CHRG_ERR_LED, outsignals[2]); + qdev_connect_gpio_out(scp1, TOSA_GPIO_WLAN_LED, outsignals[3]); - scoop_gpio_out_set(scp1, TOSA_GPIO_TC6393XB_L3V_ON, tc6393xb_l3v_get(tmio)); + qdev_connect_gpio_out(scp1, TOSA_GPIO_TC6393XB_L3V_ON, tc6393xb_l3v_get(tmio)); } static uint32_t tosa_ssp_tansfer(SSISlave *dev, uint32_t value) @@ -207,7 +209,7 @@ { PXA2xxState *cpu; TC6393xbState *tmio; - ScoopInfo *scp0, *scp1; + DeviceState *scp0, *scp1; if (!cpu_model) cpu_model = "pxa255"; @@ -215,13 +217,13 @@ cpu = pxa255_init(tosa_binfo.ram_size); cpu_register_physical_memory(0, TOSA_ROM, - qemu_ram_alloc(TOSA_ROM) | IO_MEM_ROM); + qemu_ram_alloc(NULL, "tosa.rom", TOSA_ROM) | IO_MEM_ROM); tmio = tc6393xb_init(0x10000000, - pxa2xx_gpio_in_get(cpu->gpio)[TOSA_GPIO_TC6393XB_INT]); + qdev_get_gpio_in(cpu->gpio, TOSA_GPIO_TC6393XB_INT)); - scp0 = scoop_init(cpu, 0, 0x08800000); - scp1 = scoop_init(cpu, 1, 0x14800040); + scp0 = sysbus_create_simple("scoop", 0x08800000, NULL); + scp1 = sysbus_create_simple("scoop", 0x14800040, NULL); tosa_gpio_setup(cpu, scp0, scp1, tmio); @@ -229,9 +231,6 @@ tosa_tg_init(cpu); - /* Setup initial (reset) machine state */ - cpu->env->regs[15] = tosa_binfo.loader_start; - tosa_binfo.kernel_filename = kernel_filename; tosa_binfo.kernel_cmdline = kernel_cmdline; tosa_binfo.initrd_filename = initrd_filename; diff -Nru qemu-kvm-0.12.5+noroms/hw/tsc2005.c qemu-kvm-0.14.1/hw/tsc2005.c --- qemu-kvm-0.12.5+noroms/hw/tsc2005.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/tsc2005.c 2011-05-11 13:29:46.000000000 +0000 @@ -548,7 +548,7 @@ "QEMU TSC2005-driven Touchscreen"); qemu_register_reset((void *) tsc2005_reset, s); - register_savevm("tsc2005", -1, 0, tsc2005_save, tsc2005_load, s); + register_savevm(NULL, "tsc2005", -1, 0, tsc2005_save, tsc2005_load, s); return s; } diff -Nru qemu-kvm-0.12.5+noroms/hw/tsc210x.c qemu-kvm-0.14.1/hw/tsc210x.c --- qemu-kvm-0.12.5+noroms/hw/tsc210x.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/tsc210x.c 2011-05-11 13:29:46.000000000 +0000 @@ -1143,7 +1143,7 @@ AUD_register_card(s->name, &s->card); qemu_register_reset((void *) tsc210x_reset, s); - register_savevm(s->name, -1, 0, + register_savevm(NULL, s->name, -1, 0, tsc210x_save, tsc210x_load, s); return &s->chip; @@ -1194,7 +1194,7 @@ AUD_register_card(s->name, &s->card); qemu_register_reset((void *) tsc210x_reset, s); - register_savevm(s->name, -1, 0, tsc210x_save, tsc210x_load, s); + register_savevm(NULL, s->name, -1, 0, tsc210x_save, tsc210x_load, s); return &s->chip; } diff -Nru qemu-kvm-0.12.5+noroms/hw/tusb6010.c qemu-kvm-0.14.1/hw/tusb6010.c --- qemu-kvm-0.12.5+noroms/hw/tusb6010.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/tusb6010.c 2011-05-11 13:29:46.000000000 +0000 @@ -425,7 +425,6 @@ return s->rx_config[epnum]; case TUSB_EP_MAX_PACKET_SIZE_OFFSET ... (TUSB_EP_MAX_PACKET_SIZE_OFFSET + 0x3b): - epnum = (offset - TUSB_EP_MAX_PACKET_SIZE_OFFSET) >> 2; return 0x00000000; /* TODO */ case TUSB_WAIT_COUNT: return 0x00; /* TODO */ @@ -630,7 +629,6 @@ break; case TUSB_EP_MAX_PACKET_SIZE_OFFSET ... (TUSB_EP_MAX_PACKET_SIZE_OFFSET + 0x3b): - epnum = (offset - TUSB_EP_MAX_PACKET_SIZE_OFFSET) >> 2; return; /* TODO */ case TUSB_WAIT_COUNT: return; /* TODO */ @@ -742,7 +740,7 @@ s->intr = 0x00000000; s->otg_timer_val = 0; s->iomemtype[1] = cpu_register_io_memory(tusb_async_readfn, - tusb_async_writefn, s); + tusb_async_writefn, s, DEVICE_NATIVE_ENDIAN); s->irq = intr; s->otg_timer = qemu_new_timer(vm_clock, tusb_otg_tick, s); s->pwr_timer = qemu_new_timer(vm_clock, tusb_power_tick, s); diff -Nru qemu-kvm-0.12.5+noroms/hw/unin_pci.c qemu-kvm-0.14.1/hw/unin_pci.c --- qemu-kvm-0.12.5+noroms/hw/unin_pci.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/unin_pci.c 2011-05-11 13:29:46.000000000 +0000 @@ -36,22 +36,31 @@ #define UNIN_DPRINTF(fmt, ...) #endif +static const int unin_irq_line[] = { 0x1b, 0x1c, 0x1d, 0x1e }; + typedef struct UNINState { SysBusDevice busdev; PCIHostState host_state; + ReadWriteHandler data_handler; } UNINState; -/* Don't know if this matches real hardware, but it agrees with OHW. */ static int pci_unin_map_irq(PCIDevice *pci_dev, int irq_num) { - return (irq_num + (pci_dev->devfn >> 3)) & 3; + int retval; + int devfn = pci_dev->devfn & 0x00FFFFFF; + + retval = (((devfn >> 11) & 0x1F) + irq_num) & 3; + + return retval; } static void pci_unin_set_irq(void *opaque, int irq_num, int level) { qemu_irq *pic = opaque; - qemu_set_irq(pic[irq_num + 8], level); + UNIN_DPRINTF("%s: setting INT %d = %d\n", __func__, + unin_irq_line[irq_num], level); + qemu_set_irq(pic[unin_irq_line[irq_num]], level); } static void pci_unin_save(QEMUFile* f, void *opaque) @@ -75,6 +84,62 @@ { } +static uint32_t unin_get_config_reg(uint32_t reg, uint32_t addr) +{ + uint32_t retval; + + if (reg & (1u << 31)) { + /* XXX OpenBIOS compatibility hack */ + retval = reg | (addr & 3); + } else if (reg & 1) { + /* CFA1 style */ + retval = (reg & ~7u) | (addr & 7); + } else { + uint32_t slot, func; + + /* Grab CFA0 style values */ + slot = ffs(reg & 0xfffff800) - 1; + func = (reg >> 8) & 7; + + /* ... and then convert them to x86 format */ + /* config pointer */ + retval = (reg & (0xff - 7)) | (addr & 7); + /* slot */ + retval |= slot << 11; + /* fn */ + retval |= func << 8; + } + + + UNIN_DPRINTF("Converted config space accessor %08x/%08x -> %08x\n", + reg, addr, retval); + + return retval; +} + +static void unin_data_write(ReadWriteHandler *handler, + pcibus_t addr, uint32_t val, int len) +{ + UNINState *s = container_of(handler, UNINState, data_handler); + UNIN_DPRINTF("write addr %" FMT_PCIBUS " len %d val %x\n", addr, len, val); + pci_data_write(s->host_state.bus, + unin_get_config_reg(s->host_state.config_reg, addr), + val, len); +} + +static uint32_t unin_data_read(ReadWriteHandler *handler, + pcibus_t addr, int len) +{ + UNINState *s = container_of(handler, UNINState, data_handler); + uint32_t val; + + val = pci_data_read(s->host_state.bus, + unin_get_config_reg(s->host_state.config_reg, addr), + len); + UNIN_DPRINTF("read addr %" FMT_PCIBUS " len %d val %x\n", addr, len, val); + return val; +} + static int pci_unin_main_init_device(SysBusDevice *dev) { UNINState *s; @@ -84,29 +149,42 @@ /* Uninorth main bus */ s = FROM_SYSBUS(UNINState, dev); - pci_mem_config = pci_host_conf_register_mmio(&s->host_state); - pci_mem_data = pci_host_data_register_mmio(&s->host_state); + pci_mem_config = pci_host_conf_register_mmio(&s->host_state, + DEVICE_LITTLE_ENDIAN); + s->data_handler.read = unin_data_read; + s->data_handler.write = unin_data_write; + pci_mem_data = cpu_register_io_memory_simple(&s->data_handler, + DEVICE_LITTLE_ENDIAN); sysbus_init_mmio(dev, 0x1000, pci_mem_config); sysbus_init_mmio(dev, 0x1000, pci_mem_data); - register_savevm("uninorth", 0, 1, pci_unin_save, pci_unin_load, &s->host_state); + register_savevm(&dev->qdev, "uninorth", 0, 1, + pci_unin_save, pci_unin_load, &s->host_state); qemu_register_reset(pci_unin_reset, &s->host_state); return 0; } -static int pci_dec_21154_init_device(SysBusDevice *dev) +static int pci_u3_agp_init_device(SysBusDevice *dev) { UNINState *s; int pci_mem_config, pci_mem_data; - /* Uninorth bridge */ + /* Uninorth U3 AGP bus */ s = FROM_SYSBUS(UNINState, dev); - // XXX: s = &pci_bridge[2]; - pci_mem_config = pci_host_conf_register_mmio_noswap(&s->host_state); - pci_mem_data = pci_host_data_register_mmio(&s->host_state); + pci_mem_config = pci_host_conf_register_mmio(&s->host_state, + DEVICE_LITTLE_ENDIAN); + s->data_handler.read = unin_data_read; + s->data_handler.write = unin_data_write; + pci_mem_data = cpu_register_io_memory_simple(&s->data_handler, + DEVICE_LITTLE_ENDIAN); sysbus_init_mmio(dev, 0x1000, pci_mem_config); sysbus_init_mmio(dev, 0x1000, pci_mem_data); + + register_savevm(&dev->qdev, "uninorth", 0, 1, + pci_unin_save, pci_unin_load, &s->host_state); + qemu_register_reset(pci_unin_reset, &s->host_state); + return 0; } @@ -118,8 +196,10 @@ /* Uninorth AGP bus */ s = FROM_SYSBUS(UNINState, dev); - pci_mem_config = pci_host_conf_register_mmio_noswap(&s->host_state); - pci_mem_data = pci_host_data_register_mmio(&s->host_state); + pci_mem_config = pci_host_conf_register_mmio(&s->host_state, + DEVICE_LITTLE_ENDIAN); + pci_mem_data = pci_host_data_register_mmio(&s->host_state, + DEVICE_LITTLE_ENDIAN); sysbus_init_mmio(dev, 0x1000, pci_mem_config); sysbus_init_mmio(dev, 0x1000, pci_mem_data); return 0; @@ -133,8 +213,10 @@ /* Uninorth internal bus */ s = FROM_SYSBUS(UNINState, dev); - pci_mem_config = pci_host_conf_register_mmio_noswap(&s->host_state); - pci_mem_data = pci_host_data_register_mmio(&s->host_state); + pci_mem_config = pci_host_conf_register_mmio(&s->host_state, + DEVICE_LITTLE_ENDIAN); + pci_mem_data = pci_host_data_register_mmio(&s->host_state, + DEVICE_LITTLE_ENDIAN); sysbus_init_mmio(dev, 0x1000, pci_mem_config); sysbus_init_mmio(dev, 0x1000, pci_mem_data); return 0; @@ -154,10 +236,10 @@ d = FROM_SYSBUS(UNINState, s); d->host_state.bus = pci_register_bus(&d->busdev.qdev, "pci", pci_unin_set_irq, pci_unin_map_irq, - pic, 11 << 3, 4); + pic, PCI_DEVFN(11, 0), 4); #if 0 - pci_create_simple(d->host_state.bus, 11 << 3, "uni-north"); + pci_create_simple(d->host_state.bus, PCI_DEVFN(11, 0), "uni-north"); #endif sysbus_mmio_map(s, 0, 0xf2800000); @@ -166,11 +248,11 @@ /* DEC 21154 bridge */ #if 0 /* XXX: not activated as PPC BIOS doesn't handle multiple buses properly */ - pci_create_simple(d->host_state.bus, 12 << 3, "dec-21154"); + pci_create_simple(d->host_state.bus, PCI_DEVFN(12, 0), "dec-21154"); #endif /* Uninorth AGP bus */ - pci_create_simple(d->host_state.bus, 11 << 3, "uni-north-agp"); + pci_create_simple(d->host_state.bus, PCI_DEVFN(11, 0), "uni-north-agp"); dev = qdev_create(NULL, "uni-north-agp"); qdev_init_nofail(dev); s = sysbus_from_qdev(dev); @@ -180,7 +262,7 @@ /* Uninorth internal bus */ #if 0 /* XXX: not needed for now */ - pci_create_simple(d->host_state.bus, 14 << 3, "uni-north-pci"); + pci_create_simple(d->host_state.bus, PCI_DEVFN(14, 0), "uni-north-pci"); dev = qdev_create(NULL, "uni-north-pci"); qdev_init_nofail(dev); s = sysbus_from_qdev(dev); @@ -191,6 +273,31 @@ return d->host_state.bus; } +PCIBus *pci_pmac_u3_init(qemu_irq *pic) +{ + DeviceState *dev; + SysBusDevice *s; + UNINState *d; + + /* Uninorth AGP bus */ + + dev = qdev_create(NULL, "u3-agp"); + qdev_init_nofail(dev); + s = sysbus_from_qdev(dev); + d = FROM_SYSBUS(UNINState, s); + + d->host_state.bus = pci_register_bus(&d->busdev.qdev, "pci", + pci_unin_set_irq, pci_unin_map_irq, + pic, PCI_DEVFN(11, 0), 4); + + sysbus_mmio_map(s, 0, 0xf0800000); + sysbus_mmio_map(s, 1, 0xf0c00000); + + pci_create_simple(d->host_state.bus, 11 << 3, "u3-agp"); + + return d->host_state.bus; +} + static int unin_main_pci_host_init(PCIDevice *d) { pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_APPLE); @@ -199,40 +306,10 @@ pci_config_set_class(d->config, PCI_CLASS_BRIDGE_HOST); d->config[0x0C] = 0x08; // cache_line_size d->config[0x0D] = 0x10; // latency_timer - d->config[PCI_HEADER_TYPE] = PCI_HEADER_TYPE_NORMAL; // header_type d->config[0x34] = 0x00; // capabilities_pointer return 0; } -static int dec_21154_pci_host_init(PCIDevice *d) -{ - /* pci-to-pci bridge */ - pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_DEC); - pci_config_set_device_id(d->config, PCI_DEVICE_ID_DEC_21154); - d->config[0x08] = 0x05; // revision - pci_config_set_class(d->config, PCI_CLASS_BRIDGE_PCI); - d->config[0x0C] = 0x08; // cache_line_size - d->config[0x0D] = 0x20; // latency_timer - d->config[PCI_HEADER_TYPE] = PCI_HEADER_TYPE_BRIDGE; // header_type - - d->config[0x18] = 0x01; // primary_bus - d->config[0x19] = 0x02; // secondary_bus - d->config[0x1A] = 0x02; // subordinate_bus - d->config[0x1B] = 0x20; // secondary_latency_timer - d->config[0x1C] = 0x11; // io_base - d->config[0x1D] = 0x01; // io_limit - d->config[0x20] = 0x00; // memory_base - d->config[0x21] = 0x80; - d->config[0x22] = 0x00; // memory_limit - d->config[0x23] = 0x80; - d->config[0x24] = 0x01; // prefetchable_memory_base - d->config[0x25] = 0x80; - d->config[0x26] = 0xF1; // prefectchable_memory_limit - d->config[0x27] = 0x7F; - // d->config[0x34] = 0xdc // capabilities_pointer - return 0; -} - static int unin_agp_pci_host_init(PCIDevice *d) { pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_APPLE); @@ -241,11 +318,24 @@ pci_config_set_class(d->config, PCI_CLASS_BRIDGE_HOST); d->config[0x0C] = 0x08; // cache_line_size d->config[0x0D] = 0x10; // latency_timer - d->config[PCI_HEADER_TYPE] = PCI_HEADER_TYPE_NORMAL; // header_type // d->config[0x34] = 0x80; // capabilities_pointer return 0; } +static int u3_agp_pci_host_init(PCIDevice *d) +{ + pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_APPLE); + pci_config_set_device_id(d->config, PCI_DEVICE_ID_APPLE_U3_AGP); + /* revision */ + d->config[0x08] = 0x00; + pci_config_set_class(d->config, PCI_CLASS_BRIDGE_HOST); + /* cache line size */ + d->config[0x0C] = 0x08; + /* latency timer */ + d->config[0x0D] = 0x10; + return 0; +} + static int unin_internal_pci_host_init(PCIDevice *d) { pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_APPLE); @@ -254,7 +344,6 @@ pci_config_set_class(d->config, PCI_CLASS_BRIDGE_HOST); d->config[0x0C] = 0x08; // cache_line_size d->config[0x0D] = 0x10; // latency_timer - d->config[PCI_HEADER_TYPE] = PCI_HEADER_TYPE_NORMAL; // header_type d->config[0x34] = 0x00; // capabilities_pointer return 0; } @@ -265,10 +354,10 @@ .init = unin_main_pci_host_init, }; -static PCIDeviceInfo dec_21154_pci_host_info = { - .qdev.name = "dec-21154", +static PCIDeviceInfo u3_agp_pci_host_info = { + .qdev.name = "u3-agp", .qdev.size = sizeof(PCIDevice), - .init = dec_21154_pci_host_init, + .init = u3_agp_pci_host_init, }; static PCIDeviceInfo unin_agp_pci_host_info = { @@ -288,9 +377,9 @@ sysbus_register_dev("uni-north", sizeof(UNINState), pci_unin_main_init_device); pci_qdev_register(&unin_main_pci_host_info); - sysbus_register_dev("dec-21154", sizeof(UNINState), - pci_dec_21154_init_device); - pci_qdev_register(&dec_21154_pci_host_info); + sysbus_register_dev("u3-agp", sizeof(UNINState), + pci_u3_agp_init_device); + pci_qdev_register(&u3_agp_pci_host_info); sysbus_register_dev("uni-north-agp", sizeof(UNINState), pci_unin_agp_init_device); pci_qdev_register(&unin_agp_pci_host_info); diff -Nru qemu-kvm-0.12.5+noroms/hw/usb-bt.c qemu-kvm-0.14.1/hw/usb-bt.c --- qemu-kvm-0.12.5+noroms/hw/usb-bt.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/usb-bt.c 2011-05-11 13:29:46.000000000 +0000 @@ -20,6 +20,7 @@ #include "qemu-common.h" #include "usb.h" +#include "usb-desc.h" #include "net.h" #include "bt.h" @@ -51,251 +52,202 @@ #define USB_ACL_EP 2 #define USB_SCO_EP 3 -static const uint8_t qemu_bt_dev_descriptor[] = { - 0x12, /* u8 bLength; */ - USB_DT_DEVICE, /* u8 bDescriptorType; Device */ - 0x10, 0x01, /* u16 bcdUSB; v1.10 */ - - 0xe0, /* u8 bDeviceClass; Wireless */ - 0x01, /* u8 bDeviceSubClass; Radio Frequency */ - 0x01, /* u8 bDeviceProtocol; Bluetooth */ - 0x40, /* u8 bMaxPacketSize0; 64 Bytes */ - - 0x12, 0x0a, /* u16 idVendor; */ - 0x01, 0x00, /* u16 idProduct; Bluetooth Dongle (HCI mode) */ - 0x58, 0x19, /* u16 bcdDevice; (some devices have 0x48, 0x02) */ - - 0x00, /* u8 iManufacturer; */ - 0x00, /* u8 iProduct; */ - 0x00, /* u8 iSerialNumber; */ - 0x01, /* u8 bNumConfigurations; */ +enum { + STR_MANUFACTURER = 1, + STR_SERIALNUMBER, }; -static const uint8_t qemu_bt_config_descriptor[] = { - /* one configuration */ - 0x09, /* u8 bLength; */ - USB_DT_CONFIG, /* u8 bDescriptorType; */ - 0xb1, 0x00, /* u16 wTotalLength; */ - 0x02, /* u8 bNumInterfaces; (2) */ - 0x01, /* u8 bConfigurationValue; */ - 0x00, /* u8 iConfiguration; */ - 0xc0, /* u8 bmAttributes; - Bit 7: must be set, - 6: Self-powered, - 5: Remote wakeup, - 4..0: resvd */ - 0x00, /* u8 MaxPower; */ - - /* USB 1.1: - * USB 2.0, single TT organization (mandatory): - * one interface, protocol 0 - * - * USB 2.0, multiple TT organization (optional): - * two interfaces, protocols 1 (like single TT) - * and 2 (multiple TT mode) ... config is - * sometimes settable - * NOT IMPLEMENTED - */ - - /* interface one */ - 0x09, /* u8 if_bLength; */ - USB_DT_INTERFACE, /* u8 if_bDescriptorType; */ - 0x00, /* u8 if_bInterfaceNumber; */ - 0x00, /* u8 if_bAlternateSetting; */ - 0x03, /* u8 if_bNumEndpoints; */ - 0xe0, /* u8 if_bInterfaceClass; Wireless */ - 0x01, /* u8 if_bInterfaceSubClass; Radio Frequency */ - 0x01, /* u8 if_bInterfaceProtocol; Bluetooth */ - 0x00, /* u8 if_iInterface; */ - - /* endpoint one */ - 0x07, /* u8 ep_bLength; */ - USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */ - USB_DIR_IN | USB_EVT_EP, /* u8 ep_bEndpointAddress; */ - 0x03, /* u8 ep_bmAttributes; Interrupt */ - 0x10, 0x00, /* u16 ep_wMaxPacketSize; */ - 0x02, /* u8 ep_bInterval; */ - - /* endpoint two */ - 0x07, /* u8 ep_bLength; */ - USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */ - USB_DIR_OUT | USB_ACL_EP, /* u8 ep_bEndpointAddress; */ - 0x02, /* u8 ep_bmAttributes; Bulk */ - 0x40, 0x00, /* u16 ep_wMaxPacketSize; */ - 0x0a, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ - - /* endpoint three */ - 0x07, /* u8 ep_bLength; */ - USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */ - USB_DIR_IN | USB_ACL_EP, /* u8 ep_bEndpointAddress; */ - 0x02, /* u8 ep_bmAttributes; Bulk */ - 0x40, 0x00, /* u16 ep_wMaxPacketSize; */ - 0x0a, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ - - /* interface two setting one */ - 0x09, /* u8 if_bLength; */ - USB_DT_INTERFACE, /* u8 if_bDescriptorType; */ - 0x01, /* u8 if_bInterfaceNumber; */ - 0x00, /* u8 if_bAlternateSetting; */ - 0x02, /* u8 if_bNumEndpoints; */ - 0xe0, /* u8 if_bInterfaceClass; Wireless */ - 0x01, /* u8 if_bInterfaceSubClass; Radio Frequency */ - 0x01, /* u8 if_bInterfaceProtocol; Bluetooth */ - 0x00, /* u8 if_iInterface; */ - - /* endpoint one */ - 0x07, /* u8 ep_bLength; */ - USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */ - USB_DIR_OUT | USB_SCO_EP, /* u8 ep_bEndpointAddress; */ - 0x01, /* u8 ep_bmAttributes; Isochronous */ - 0x00, 0x00, /* u16 ep_wMaxPacketSize; */ - 0x01, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ - - /* endpoint two */ - 0x07, /* u8 ep_bLength; */ - USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */ - USB_DIR_IN | USB_SCO_EP, /* u8 ep_bEndpointAddress; */ - 0x01, /* u8 ep_bmAttributes; Isochronous */ - 0x00, 0x00, /* u16 ep_wMaxPacketSize; */ - 0x01, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ - - /* interface two setting two */ - 0x09, /* u8 if_bLength; */ - USB_DT_INTERFACE, /* u8 if_bDescriptorType; */ - 0x01, /* u8 if_bInterfaceNumber; */ - 0x01, /* u8 if_bAlternateSetting; */ - 0x02, /* u8 if_bNumEndpoints; */ - 0xe0, /* u8 if_bInterfaceClass; Wireless */ - 0x01, /* u8 if_bInterfaceSubClass; Radio Frequency */ - 0x01, /* u8 if_bInterfaceProtocol; Bluetooth */ - 0x00, /* u8 if_iInterface; */ - - /* endpoint one */ - 0x07, /* u8 ep_bLength; */ - USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */ - USB_DIR_OUT | USB_SCO_EP, /* u8 ep_bEndpointAddress; */ - 0x01, /* u8 ep_bmAttributes; Isochronous */ - 0x09, 0x00, /* u16 ep_wMaxPacketSize; */ - 0x01, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ - - /* endpoint two */ - 0x07, /* u8 ep_bLength; */ - USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */ - USB_DIR_IN | USB_SCO_EP, /* u8 ep_bEndpointAddress; */ - 0x01, /* u8 ep_bmAttributes; Isochronous */ - 0x09, 0x00, /* u16 ep_wMaxPacketSize; */ - 0x01, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ - - /* interface two setting three */ - 0x09, /* u8 if_bLength; */ - USB_DT_INTERFACE, /* u8 if_bDescriptorType; */ - 0x01, /* u8 if_bInterfaceNumber; */ - 0x02, /* u8 if_bAlternateSetting; */ - 0x02, /* u8 if_bNumEndpoints; */ - 0xe0, /* u8 if_bInterfaceClass; Wireless */ - 0x01, /* u8 if_bInterfaceSubClass; Radio Frequency */ - 0x01, /* u8 if_bInterfaceProtocol; Bluetooth */ - 0x00, /* u8 if_iInterface; */ - - /* endpoint one */ - 0x07, /* u8 ep_bLength; */ - USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */ - USB_DIR_OUT | USB_SCO_EP, /* u8 ep_bEndpointAddress; */ - 0x01, /* u8 ep_bmAttributes; Isochronous */ - 0x11, 0x00, /* u16 ep_wMaxPacketSize; */ - 0x01, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ - - /* endpoint two */ - 0x07, /* u8 ep_bLength; */ - USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */ - USB_DIR_IN | USB_SCO_EP, /* u8 ep_bEndpointAddress; */ - 0x01, /* u8 ep_bmAttributes; Isochronous */ - 0x11, 0x00, /* u16 ep_wMaxPacketSize; */ - 0x01, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ - - /* interface two setting four */ - 0x09, /* u8 if_bLength; */ - USB_DT_INTERFACE, /* u8 if_bDescriptorType; */ - 0x01, /* u8 if_bInterfaceNumber; */ - 0x03, /* u8 if_bAlternateSetting; */ - 0x02, /* u8 if_bNumEndpoints; */ - 0xe0, /* u8 if_bInterfaceClass; Wireless */ - 0x01, /* u8 if_bInterfaceSubClass; Radio Frequency */ - 0x01, /* u8 if_bInterfaceProtocol; Bluetooth */ - 0x00, /* u8 if_iInterface; */ - - /* endpoint one */ - 0x07, /* u8 ep_bLength; */ - USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */ - USB_DIR_OUT | USB_SCO_EP, /* u8 ep_bEndpointAddress; */ - 0x01, /* u8 ep_bmAttributes; Isochronous */ - 0x19, 0x00, /* u16 ep_wMaxPacketSize; */ - 0x01, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ - - /* endpoint two */ - 0x07, /* u8 ep_bLength; */ - USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */ - USB_DIR_IN | USB_SCO_EP, /* u8 ep_bEndpointAddress; */ - 0x01, /* u8 ep_bmAttributes; Isochronous */ - 0x19, 0x00, /* u16 ep_wMaxPacketSize; */ - 0x01, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ - - /* interface two setting five */ - 0x09, /* u8 if_bLength; */ - USB_DT_INTERFACE, /* u8 if_bDescriptorType; */ - 0x01, /* u8 if_bInterfaceNumber; */ - 0x04, /* u8 if_bAlternateSetting; */ - 0x02, /* u8 if_bNumEndpoints; */ - 0xe0, /* u8 if_bInterfaceClass; Wireless */ - 0x01, /* u8 if_bInterfaceSubClass; Radio Frequency */ - 0x01, /* u8 if_bInterfaceProtocol; Bluetooth */ - 0x00, /* u8 if_iInterface; */ - - /* endpoint one */ - 0x07, /* u8 ep_bLength; */ - USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */ - USB_DIR_OUT | USB_SCO_EP, /* u8 ep_bEndpointAddress; */ - 0x01, /* u8 ep_bmAttributes; Isochronous */ - 0x21, 0x00, /* u16 ep_wMaxPacketSize; */ - 0x01, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ - - /* endpoint two */ - 0x07, /* u8 ep_bLength; */ - USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */ - USB_DIR_IN | USB_SCO_EP, /* u8 ep_bEndpointAddress; */ - 0x01, /* u8 ep_bmAttributes; Isochronous */ - 0x21, 0x00, /* u16 ep_wMaxPacketSize; */ - 0x01, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ - - /* interface two setting six */ - 0x09, /* u8 if_bLength; */ - USB_DT_INTERFACE, /* u8 if_bDescriptorType; */ - 0x01, /* u8 if_bInterfaceNumber; */ - 0x05, /* u8 if_bAlternateSetting; */ - 0x02, /* u8 if_bNumEndpoints; */ - 0xe0, /* u8 if_bInterfaceClass; Wireless */ - 0x01, /* u8 if_bInterfaceSubClass; Radio Frequency */ - 0x01, /* u8 if_bInterfaceProtocol; Bluetooth */ - 0x00, /* u8 if_iInterface; */ - - /* endpoint one */ - 0x07, /* u8 ep_bLength; */ - USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */ - USB_DIR_OUT | USB_SCO_EP, /* u8 ep_bEndpointAddress; */ - 0x01, /* u8 ep_bmAttributes; Isochronous */ - 0x31, 0x00, /* u16 ep_wMaxPacketSize; */ - 0x01, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ - - /* endpoint two */ - 0x07, /* u8 ep_bLength; */ - USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */ - USB_DIR_IN | USB_SCO_EP, /* u8 ep_bEndpointAddress; */ - 0x01, /* u8 ep_bmAttributes; Isochronous */ - 0x31, 0x00, /* u16 ep_wMaxPacketSize; */ - 0x01, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ +static const USBDescStrings desc_strings = { + [STR_MANUFACTURER] = "QEMU " QEMU_VERSION, + [STR_SERIALNUMBER] = "1", +}; - /* If implemented, the DFU interface descriptor goes here with no - * endpoints or alternative settings. */ +static const USBDescIface desc_iface_bluetooth[] = { + { + .bInterfaceNumber = 0, + .bNumEndpoints = 3, + .bInterfaceClass = 0xe0, /* Wireless */ + .bInterfaceSubClass = 0x01, /* Radio Frequency */ + .bInterfaceProtocol = 0x01, /* Bluetooth */ + .eps = (USBDescEndpoint[]) { + { + .bEndpointAddress = USB_DIR_IN | USB_EVT_EP, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = 0x10, + .bInterval = 0x02, + }, + { + .bEndpointAddress = USB_DIR_OUT | USB_ACL_EP, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = 0x40, + .bInterval = 0x0a, + }, + { + .bEndpointAddress = USB_DIR_IN | USB_ACL_EP, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = 0x40, + .bInterval = 0x0a, + }, + }, + },{ + .bInterfaceNumber = 1, + .bAlternateSetting = 0, + .bNumEndpoints = 2, + .bInterfaceClass = 0xe0, /* Wireless */ + .bInterfaceSubClass = 0x01, /* Radio Frequency */ + .bInterfaceProtocol = 0x01, /* Bluetooth */ + .eps = (USBDescEndpoint[]) { + { + .bEndpointAddress = USB_DIR_OUT | USB_SCO_EP, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = 0, + .bInterval = 0x01, + }, + { + .bEndpointAddress = USB_DIR_IN | USB_SCO_EP, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = 0, + .bInterval = 0x01, + }, + }, + },{ + .bInterfaceNumber = 1, + .bAlternateSetting = 1, + .bNumEndpoints = 2, + .bInterfaceClass = 0xe0, /* Wireless */ + .bInterfaceSubClass = 0x01, /* Radio Frequency */ + .bInterfaceProtocol = 0x01, /* Bluetooth */ + .eps = (USBDescEndpoint[]) { + { + .bEndpointAddress = USB_DIR_OUT | USB_SCO_EP, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = 0x09, + .bInterval = 0x01, + }, + { + .bEndpointAddress = USB_DIR_IN | USB_SCO_EP, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = 0x09, + .bInterval = 0x01, + }, + }, + },{ + .bInterfaceNumber = 1, + .bAlternateSetting = 2, + .bNumEndpoints = 2, + .bInterfaceClass = 0xe0, /* Wireless */ + .bInterfaceSubClass = 0x01, /* Radio Frequency */ + .bInterfaceProtocol = 0x01, /* Bluetooth */ + .eps = (USBDescEndpoint[]) { + { + .bEndpointAddress = USB_DIR_OUT | USB_SCO_EP, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = 0x11, + .bInterval = 0x01, + }, + { + .bEndpointAddress = USB_DIR_IN | USB_SCO_EP, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = 0x11, + .bInterval = 0x01, + }, + }, + },{ + .bInterfaceNumber = 1, + .bAlternateSetting = 3, + .bNumEndpoints = 2, + .bInterfaceClass = 0xe0, /* Wireless */ + .bInterfaceSubClass = 0x01, /* Radio Frequency */ + .bInterfaceProtocol = 0x01, /* Bluetooth */ + .eps = (USBDescEndpoint[]) { + { + .bEndpointAddress = USB_DIR_OUT | USB_SCO_EP, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = 0x19, + .bInterval = 0x01, + }, + { + .bEndpointAddress = USB_DIR_IN | USB_SCO_EP, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = 0x19, + .bInterval = 0x01, + }, + }, + },{ + .bInterfaceNumber = 1, + .bAlternateSetting = 4, + .bNumEndpoints = 2, + .bInterfaceClass = 0xe0, /* Wireless */ + .bInterfaceSubClass = 0x01, /* Radio Frequency */ + .bInterfaceProtocol = 0x01, /* Bluetooth */ + .eps = (USBDescEndpoint[]) { + { + .bEndpointAddress = USB_DIR_OUT | USB_SCO_EP, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = 0x21, + .bInterval = 0x01, + }, + { + .bEndpointAddress = USB_DIR_IN | USB_SCO_EP, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = 0x21, + .bInterval = 0x01, + }, + }, + },{ + .bInterfaceNumber = 1, + .bAlternateSetting = 5, + .bNumEndpoints = 2, + .bInterfaceClass = 0xe0, /* Wireless */ + .bInterfaceSubClass = 0x01, /* Radio Frequency */ + .bInterfaceProtocol = 0x01, /* Bluetooth */ + .eps = (USBDescEndpoint[]) { + { + .bEndpointAddress = USB_DIR_OUT | USB_SCO_EP, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = 0x31, + .bInterval = 0x01, + }, + { + .bEndpointAddress = USB_DIR_IN | USB_SCO_EP, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = 0x31, + .bInterval = 0x01, + }, + }, + } +}; + +static const USBDescDevice desc_device_bluetooth = { + .bcdUSB = 0x0110, + .bDeviceClass = 0xe0, /* Wireless */ + .bDeviceSubClass = 0x01, /* Radio Frequency */ + .bDeviceProtocol = 0x01, /* Bluetooth */ + .bMaxPacketSize0 = 64, + .bNumConfigurations = 1, + .confs = (USBDescConfig[]) { + { + .bNumInterfaces = 2, + .bConfigurationValue = 1, + .bmAttributes = 0xc0, + .bMaxPower = 0, + .nif = ARRAY_SIZE(desc_iface_bluetooth), + .ifs = desc_iface_bluetooth, + }, + }, +}; + +static const USBDesc desc_bluetooth = { + .id = { + .idVendor = 0x0a12, + .idProduct = 0x0001, + .bcdDevice = 0x1958, + .iManufacturer = STR_MANUFACTURER, + .iProduct = 0, + .iSerialNumber = STR_SERIALNUMBER, + }, + .full = &desc_device_bluetooth, + .str = desc_strings, }; static void usb_bt_fifo_reset(struct usb_hci_in_fifo_s *fifo) @@ -424,85 +376,38 @@ int index, int length, uint8_t *data) { struct USBBtState *s = (struct USBBtState *) dev->opaque; - int ret = 0; + int ret; + + ret = usb_desc_handle_control(dev, request, value, index, length, data); + if (ret >= 0) { + switch (request) { + case DeviceRequest | USB_REQ_GET_CONFIGURATION: + s->config = 0; + break; + case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: + s->config = 1; + usb_bt_fifo_reset(&s->evt); + usb_bt_fifo_reset(&s->acl); + usb_bt_fifo_reset(&s->sco); + break; + } + return ret; + } + ret = 0; switch (request) { - case DeviceRequest | USB_REQ_GET_STATUS: case InterfaceRequest | USB_REQ_GET_STATUS: case EndpointRequest | USB_REQ_GET_STATUS: - data[0] = (1 << USB_DEVICE_SELF_POWERED) | - (dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP); + data[0] = 0x00; data[1] = 0x00; ret = 2; break; - case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: case InterfaceOutRequest | USB_REQ_CLEAR_FEATURE: case EndpointOutRequest | USB_REQ_CLEAR_FEATURE: - if (value == USB_DEVICE_REMOTE_WAKEUP) { - dev->remote_wakeup = 0; - } else { - goto fail; - } - ret = 0; - break; - case DeviceOutRequest | USB_REQ_SET_FEATURE: + goto fail; case InterfaceOutRequest | USB_REQ_SET_FEATURE: case EndpointOutRequest | USB_REQ_SET_FEATURE: - if (value == USB_DEVICE_REMOTE_WAKEUP) { - dev->remote_wakeup = 1; - } else { - goto fail; - } - ret = 0; - break; - case DeviceOutRequest | USB_REQ_SET_ADDRESS: - dev->addr = value; - ret = 0; - break; - case DeviceRequest | USB_REQ_GET_DESCRIPTOR: - switch (value >> 8) { - case USB_DT_DEVICE: - ret = sizeof(qemu_bt_dev_descriptor); - memcpy(data, qemu_bt_dev_descriptor, ret); - break; - case USB_DT_CONFIG: - ret = sizeof(qemu_bt_config_descriptor); - memcpy(data, qemu_bt_config_descriptor, ret); - break; - case USB_DT_STRING: - switch(value & 0xff) { - case 0: - /* language ids */ - data[0] = 4; - data[1] = 3; - data[2] = 0x09; - data[3] = 0x04; - ret = 4; - break; - default: - goto fail; - } - break; - default: - goto fail; - } - break; - case DeviceRequest | USB_REQ_GET_CONFIGURATION: - data[0] = qemu_bt_config_descriptor[0x5]; - ret = 1; - s->config = 0; - break; - case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: - ret = 0; - if (value != qemu_bt_config_descriptor[0x5] && value != 0) { - printf("%s: Wrong SET_CONFIGURATION request (%i)\n", - __FUNCTION__, value); - goto fail; - } - s->config = 1; - usb_bt_fifo_reset(&s->evt); - usb_bt_fifo_reset(&s->acl); - usb_bt_fifo_reset(&s->sco); + goto fail; break; case InterfaceRequest | USB_REQ_GET_INTERFACE: if (value != 0 || (index & ~1) || length != 1) @@ -618,8 +523,7 @@ static int usb_bt_initfn(USBDevice *dev) { - struct USBBtState *s = DO_UPCAST(struct USBBtState, dev, dev); - s->dev.speed = USB_SPEED_HIGH; + usb_desc_init(dev); return 0; } @@ -648,6 +552,7 @@ .product_desc = "QEMU BT dongle", .qdev.name = "usb-bt-dongle", .qdev.size = sizeof(struct USBBtState), + .usb_desc = &desc_bluetooth, .init = usb_bt_initfn, .handle_packet = usb_generic_handle_packet, .handle_reset = usb_bt_handle_reset, diff -Nru qemu-kvm-0.12.5+noroms/hw/usb-bus.c qemu-kvm-0.14.1/hw/usb-bus.c --- qemu-kvm-0.12.5+noroms/hw/usb-bus.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/usb-bus.c 2011-05-11 13:29:46.000000000 +0000 @@ -6,14 +6,39 @@ static void usb_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent); +static char *usb_get_dev_path(DeviceState *dev); +static char *usb_get_fw_dev_path(DeviceState *qdev); + static struct BusInfo usb_bus_info = { .name = "USB", .size = sizeof(USBBus), .print_dev = usb_bus_dev_print, + .get_dev_path = usb_get_dev_path, + .get_fw_dev_path = usb_get_fw_dev_path, + .props = (Property[]) { + DEFINE_PROP_STRING("port", USBDevice, port_path), + DEFINE_PROP_END_OF_LIST() + }, }; static int next_usb_bus = 0; static QTAILQ_HEAD(, USBBus) busses = QTAILQ_HEAD_INITIALIZER(busses); +const VMStateDescription vmstate_usb_device = { + .name = "USBDevice", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField []) { + VMSTATE_UINT8(addr, USBDevice), + VMSTATE_INT32(state, USBDevice), + VMSTATE_INT32(remote_wakeup, USBDevice), + VMSTATE_INT32(setup_state, USBDevice), + VMSTATE_INT32(setup_len, USBDevice), + VMSTATE_INT32(setup_index, USBDevice), + VMSTATE_UINT8_ARRAY(setup_buf, USBDevice, 8), + VMSTATE_END_OF_LIST(), + } +}; + void usb_bus_new(USBBus *bus, DeviceState *host) { qbus_create_inplace(&bus->qbus, &usb_bus_info, host, NULL); @@ -46,6 +71,7 @@ pstrcpy(dev->product_desc, sizeof(dev->product_desc), info->product_desc); dev->info = info; dev->auto_attach = 1; + QLIST_INIT(&dev->strings); rc = dev->info->init(dev); if (rc == 0 && dev->auto_attach) usb_device_attach(dev); @@ -110,15 +136,28 @@ } void usb_register_port(USBBus *bus, USBPort *port, void *opaque, int index, - usb_attachfn attach) + USBPortOps *ops, int speedmask) { port->opaque = opaque; port->index = index; - port->attach = attach; + port->opaque = opaque; + port->index = index; + port->ops = ops; + port->speedmask = speedmask; QTAILQ_INSERT_TAIL(&bus->free, port, next); bus->nfree++; } +void usb_port_location(USBPort *downstream, USBPort *upstream, int portnr) +{ + if (upstream) { + snprintf(downstream->path, sizeof(downstream->path), "%s.%d", + upstream->path, portnr); + } else { + snprintf(downstream->path, sizeof(downstream->path), "%d", portnr); + } +} + void usb_unregister_port(USBBus *bus, USBPort *port) { if (port->dev) @@ -137,9 +176,22 @@ dev->product_desc); return; } - dev->attached++; + if (dev->port_path) { + QTAILQ_FOREACH(port, &bus->free, next) { + if (strcmp(port->path, dev->port_path) == 0) { + break; + } + } + if (port == NULL) { + fprintf(stderr, "Warning: usb port %s (bus %s) not found\n", + dev->port_path, bus->qbus.name); + return; + } + } else { + port = QTAILQ_FIRST(&bus->free); + } - port = QTAILQ_FIRST(&bus->free); + dev->attached++; QTAILQ_REMOVE(&bus->free, port, next); bus->nfree--; @@ -152,11 +204,11 @@ int usb_device_attach(USBDevice *dev) { USBBus *bus = usb_bus_from_device(dev); - USBDevice *hub; - if (bus->nfree == 1) { - /* Create a new hub and chain it on. */ - hub = usb_create_simple(bus, "usb-hub"); + if (bus->nfree == 1 && dev->port_path == NULL) { + /* Create a new hub and chain it on + (unless a physical port location is specified). */ + usb_create_simple(bus, "usb-hub"); } do_attach(dev); return 0; @@ -229,12 +281,45 @@ USBDevice *dev = DO_UPCAST(USBDevice, qdev, qdev); USBBus *bus = usb_bus_from_device(dev); - monitor_printf(mon, "%*saddr %d.%d, speed %s, name %s%s\n", + monitor_printf(mon, "%*saddr %d.%d, port %s, speed %s, name %s%s\n", indent, "", bus->busnr, dev->addr, + dev->port ? dev->port->path : "-", usb_speed(dev->speed), dev->product_desc, dev->attached ? ", attached" : ""); } +static char *usb_get_dev_path(DeviceState *qdev) +{ + USBDevice *dev = DO_UPCAST(USBDevice, qdev, qdev); + return qemu_strdup(dev->port->path); +} + +static char *usb_get_fw_dev_path(DeviceState *qdev) +{ + USBDevice *dev = DO_UPCAST(USBDevice, qdev, qdev); + char *fw_path, *in; + ssize_t pos = 0, fw_len; + long nr; + + fw_len = 32 + strlen(dev->port->path) * 6; + fw_path = qemu_malloc(fw_len); + in = dev->port->path; + while (fw_len - pos > 0) { + nr = strtol(in, &in, 10); + if (in[0] == '.') { + /* some hub between root port and device */ + pos += snprintf(fw_path + pos, fw_len - pos, "hub@%ld/", nr); + in++; + } else { + /* the device itself */ + pos += snprintf(fw_path + pos, fw_len - pos, "%s@%ld", + qdev_fw_name(qdev), nr); + break; + } + } + return fw_path; +} + void usb_info(Monitor *mon) { USBBus *bus; @@ -251,8 +336,8 @@ dev = port->dev; if (!dev) continue; - monitor_printf(mon, " Device %d.%d, Speed %s Mb/s, Product %s\n", - bus->busnr, dev->addr, usb_speed(dev->speed), + monitor_printf(mon, " Device %d.%d, Port %s, Speed %s Mb/s, Product %s\n", + bus->busnr, dev->addr, port->path, usb_speed(dev->speed), dev->product_desc); } } @@ -293,14 +378,14 @@ if (info == NULL) { #if 0 /* no error because some drivers are not converted (yet) */ - qemu_error("usbdevice %s not found\n", driver); + error_report("usbdevice %s not found", driver); #endif return NULL; } if (!usb->usbdevice_init) { if (*params) { - qemu_error("usbdevice %s accepts no params\n", driver); + error_report("usbdevice %s accepts no params", driver); return NULL; } return usb_create_simple(bus, usb->qdev.name); diff -Nru qemu-kvm-0.12.5+noroms/hw/usb.c qemu-kvm-0.14.1/hw/usb.c --- qemu-kvm-0.12.5+noroms/hw/usb.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/usb.c 2011-05-11 13:29:46.000000000 +0000 @@ -28,7 +28,32 @@ void usb_attach(USBPort *port, USBDevice *dev) { - port->attach(port, dev); + if (dev != NULL) { + /* attach */ + if (port->dev) { + usb_attach(port, NULL); + } + dev->port = port; + port->dev = dev; + port->ops->attach(port); + usb_send_msg(dev, USB_MSG_ATTACH); + } else { + /* detach */ + dev = port->dev; + port->ops->detach(port); + if (dev) { + usb_send_msg(dev, USB_MSG_DETACH); + dev->port = NULL; + port->dev = NULL; + } + } +} + +void usb_wakeup(USBDevice *dev) +{ + if (dev->remote_wakeup && dev->port && dev->port->ops->wakeup) { + dev->port->ops->wakeup(dev); + } } /**********************/ @@ -169,6 +194,9 @@ switch(p->pid) { case USB_MSG_ATTACH: s->state = USB_STATE_ATTACHED; + if (s->info->handle_attach) { + s->info->handle_attach(s); + } return 0; case USB_MSG_DETACH: @@ -179,7 +207,9 @@ s->remote_wakeup = 0; s->addr = 0; s->state = USB_STATE_DEFAULT; - s->info->handle_reset(s); + if (s->info->handle_reset) { + s->info->handle_reset(s); + } return 0; } diff -Nru qemu-kvm-0.12.5+noroms/hw/usb-desc.c qemu-kvm-0.14.1/hw/usb-desc.c --- qemu-kvm-0.12.5+noroms/hw/usb-desc.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/usb-desc.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,406 @@ +#include "usb.h" +#include "usb-desc.h" +#include "trace.h" + +/* ------------------------------------------------------------------ */ + +static uint8_t usb_lo(uint16_t val) +{ + return val & 0xff; +} + +static uint8_t usb_hi(uint16_t val) +{ + return (val >> 8) & 0xff; +} + +int usb_desc_device(const USBDescID *id, const USBDescDevice *dev, + uint8_t *dest, size_t len) +{ + uint8_t bLength = 0x12; + + if (len < bLength) { + return -1; + } + + dest[0x00] = bLength; + dest[0x01] = USB_DT_DEVICE; + + dest[0x02] = usb_lo(dev->bcdUSB); + dest[0x03] = usb_hi(dev->bcdUSB); + dest[0x04] = dev->bDeviceClass; + dest[0x05] = dev->bDeviceSubClass; + dest[0x06] = dev->bDeviceProtocol; + dest[0x07] = dev->bMaxPacketSize0; + + dest[0x08] = usb_lo(id->idVendor); + dest[0x09] = usb_hi(id->idVendor); + dest[0x0a] = usb_lo(id->idProduct); + dest[0x0b] = usb_hi(id->idProduct); + dest[0x0c] = usb_lo(id->bcdDevice); + dest[0x0d] = usb_hi(id->bcdDevice); + dest[0x0e] = id->iManufacturer; + dest[0x0f] = id->iProduct; + dest[0x10] = id->iSerialNumber; + + dest[0x11] = dev->bNumConfigurations; + + return bLength; +} + +int usb_desc_device_qualifier(const USBDescDevice *dev, + uint8_t *dest, size_t len) +{ + uint8_t bLength = 0x0a; + + if (len < bLength) { + return -1; + } + + dest[0x00] = bLength; + dest[0x01] = USB_DT_DEVICE_QUALIFIER; + + dest[0x02] = usb_lo(dev->bcdUSB); + dest[0x03] = usb_hi(dev->bcdUSB); + dest[0x04] = dev->bDeviceClass; + dest[0x05] = dev->bDeviceSubClass; + dest[0x06] = dev->bDeviceProtocol; + dest[0x07] = dev->bMaxPacketSize0; + dest[0x08] = dev->bNumConfigurations; + dest[0x09] = 0; /* reserved */ + + return bLength; +} + +int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len) +{ + uint8_t bLength = 0x09; + uint16_t wTotalLength = 0; + int i, rc, count; + + if (len < bLength) { + return -1; + } + + dest[0x00] = bLength; + dest[0x01] = USB_DT_CONFIG; + dest[0x04] = conf->bNumInterfaces; + dest[0x05] = conf->bConfigurationValue; + dest[0x06] = conf->iConfiguration; + dest[0x07] = conf->bmAttributes; + dest[0x08] = conf->bMaxPower; + wTotalLength += bLength; + + count = conf->nif ? conf->nif : conf->bNumInterfaces; + for (i = 0; i < count; i++) { + rc = usb_desc_iface(conf->ifs + i, dest + wTotalLength, len - wTotalLength); + if (rc < 0) { + return rc; + } + wTotalLength += rc; + } + + dest[0x02] = usb_lo(wTotalLength); + dest[0x03] = usb_hi(wTotalLength); + return wTotalLength; +} + +int usb_desc_iface(const USBDescIface *iface, uint8_t *dest, size_t len) +{ + uint8_t bLength = 0x09; + int i, rc, pos = 0; + + if (len < bLength) { + return -1; + } + + dest[0x00] = bLength; + dest[0x01] = USB_DT_INTERFACE; + dest[0x02] = iface->bInterfaceNumber; + dest[0x03] = iface->bAlternateSetting; + dest[0x04] = iface->bNumEndpoints; + dest[0x05] = iface->bInterfaceClass; + dest[0x06] = iface->bInterfaceSubClass; + dest[0x07] = iface->bInterfaceProtocol; + dest[0x08] = iface->iInterface; + pos += bLength; + + for (i = 0; i < iface->ndesc; i++) { + rc = usb_desc_other(iface->descs + i, dest + pos, len - pos); + if (rc < 0) { + return rc; + } + pos += rc; + } + + for (i = 0; i < iface->bNumEndpoints; i++) { + rc = usb_desc_endpoint(iface->eps + i, dest + pos, len - pos); + if (rc < 0) { + return rc; + } + pos += rc; + } + + return pos; +} + +int usb_desc_endpoint(const USBDescEndpoint *ep, uint8_t *dest, size_t len) +{ + uint8_t bLength = 0x07; + + if (len < bLength) { + return -1; + } + + dest[0x00] = bLength; + dest[0x01] = USB_DT_ENDPOINT; + dest[0x02] = ep->bEndpointAddress; + dest[0x03] = ep->bmAttributes; + dest[0x04] = usb_lo(ep->wMaxPacketSize); + dest[0x05] = usb_hi(ep->wMaxPacketSize); + dest[0x06] = ep->bInterval; + + return bLength; +} + +int usb_desc_other(const USBDescOther *desc, uint8_t *dest, size_t len) +{ + int bLength = desc->length ? desc->length : desc->data[0]; + + if (len < bLength) { + return -1; + } + + memcpy(dest, desc->data, bLength); + return bLength; +} + +/* ------------------------------------------------------------------ */ + +static void usb_desc_setdefaults(USBDevice *dev) +{ + const USBDesc *desc = dev->info->usb_desc; + + assert(desc != NULL); + switch (dev->speed) { + case USB_SPEED_LOW: + case USB_SPEED_FULL: + dev->device = desc->full; + break; + case USB_SPEED_HIGH: + dev->device = desc->high; + break; + } + dev->config = dev->device->confs; +} + +void usb_desc_init(USBDevice *dev) +{ + dev->speed = USB_SPEED_FULL; + usb_desc_setdefaults(dev); +} + +void usb_desc_attach(USBDevice *dev) +{ + const USBDesc *desc = dev->info->usb_desc; + + assert(desc != NULL); + if (desc->high && (dev->port->speedmask & USB_SPEED_MASK_HIGH)) { + dev->speed = USB_SPEED_HIGH; + } else if (desc->full && (dev->port->speedmask & USB_SPEED_MASK_FULL)) { + dev->speed = USB_SPEED_FULL; + } else { + fprintf(stderr, "usb: port/device speed mismatch for \"%s\"\n", + dev->info->product_desc); + return; + } + usb_desc_setdefaults(dev); +} + +void usb_desc_set_string(USBDevice *dev, uint8_t index, const char *str) +{ + USBDescString *s; + + QLIST_FOREACH(s, &dev->strings, next) { + if (s->index == index) { + break; + } + } + if (s == NULL) { + s = qemu_mallocz(sizeof(*s)); + s->index = index; + QLIST_INSERT_HEAD(&dev->strings, s, next); + } + qemu_free(s->str); + s->str = qemu_strdup(str); +} + +const char *usb_desc_get_string(USBDevice *dev, uint8_t index) +{ + USBDescString *s; + + QLIST_FOREACH(s, &dev->strings, next) { + if (s->index == index) { + return s->str; + } + } + return NULL; +} + +int usb_desc_string(USBDevice *dev, int index, uint8_t *dest, size_t len) +{ + uint8_t bLength, pos, i; + const char *str; + + if (len < 4) { + return -1; + } + + if (index == 0) { + /* language ids */ + dest[0] = 4; + dest[1] = USB_DT_STRING; + dest[2] = 0x09; + dest[3] = 0x04; + return 4; + } + + str = usb_desc_get_string(dev, index); + if (str == NULL) { + str = dev->info->usb_desc->str[index]; + if (str == NULL) { + return 0; + } + } + + bLength = strlen(str) * 2 + 2; + dest[0] = bLength; + dest[1] = USB_DT_STRING; + i = 0; pos = 2; + while (pos+1 < bLength && pos+1 < len) { + dest[pos++] = str[i++]; + dest[pos++] = 0; + } + return pos; +} + +int usb_desc_get_descriptor(USBDevice *dev, int value, uint8_t *dest, size_t len) +{ + const USBDesc *desc = dev->info->usb_desc; + const USBDescDevice *other_dev; + uint8_t buf[256]; + uint8_t type = value >> 8; + uint8_t index = value & 0xff; + int ret = -1; + + if (dev->speed == USB_SPEED_HIGH) { + other_dev = dev->info->usb_desc->full; + } else { + other_dev = dev->info->usb_desc->high; + } + + switch(type) { + case USB_DT_DEVICE: + ret = usb_desc_device(&desc->id, dev->device, buf, sizeof(buf)); + trace_usb_desc_device(dev->addr, len, ret); + break; + case USB_DT_CONFIG: + if (index < dev->device->bNumConfigurations) { + ret = usb_desc_config(dev->device->confs + index, buf, sizeof(buf)); + } + trace_usb_desc_config(dev->addr, index, len, ret); + break; + case USB_DT_STRING: + ret = usb_desc_string(dev, index, buf, sizeof(buf)); + trace_usb_desc_string(dev->addr, index, len, ret); + break; + + case USB_DT_DEVICE_QUALIFIER: + if (other_dev != NULL) { + ret = usb_desc_device_qualifier(other_dev, buf, sizeof(buf)); + } + trace_usb_desc_device_qualifier(dev->addr, len, ret); + break; + case USB_DT_OTHER_SPEED_CONFIG: + if (other_dev != NULL && index < other_dev->bNumConfigurations) { + ret = usb_desc_config(other_dev->confs + index, buf, sizeof(buf)); + buf[0x01] = USB_DT_OTHER_SPEED_CONFIG; + } + trace_usb_desc_other_speed_config(dev->addr, index, len, ret); + break; + + default: + fprintf(stderr, "%s: %d unknown type %d (len %zd)\n", __FUNCTION__, + dev->addr, type, len); + break; + } + + if (ret > 0) { + if (ret > len) { + ret = len; + } + memcpy(dest, buf, ret); + } + return ret; +} + +int usb_desc_handle_control(USBDevice *dev, int request, int value, + int index, int length, uint8_t *data) +{ + const USBDesc *desc = dev->info->usb_desc; + int i, ret = -1; + + assert(desc != NULL); + switch(request) { + case DeviceOutRequest | USB_REQ_SET_ADDRESS: + dev->addr = value; + trace_usb_set_addr(dev->addr); + ret = 0; + break; + + case DeviceRequest | USB_REQ_GET_DESCRIPTOR: + ret = usb_desc_get_descriptor(dev, value, data, length); + break; + + case DeviceRequest | USB_REQ_GET_CONFIGURATION: + data[0] = dev->config->bConfigurationValue; + ret = 1; + break; + case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: + for (i = 0; i < dev->device->bNumConfigurations; i++) { + if (dev->device->confs[i].bConfigurationValue == value) { + dev->config = dev->device->confs + i; + ret = 0; + } + } + trace_usb_set_config(dev->addr, value, ret); + break; + + case DeviceRequest | USB_REQ_GET_STATUS: + data[0] = 0; + if (dev->config->bmAttributes & 0x40) { + data[0] |= 1 << USB_DEVICE_SELF_POWERED; + } + if (dev->remote_wakeup) { + data[0] |= 1 << USB_DEVICE_REMOTE_WAKEUP; + } + data[1] = 0x00; + ret = 2; + break; + case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: + if (value == USB_DEVICE_REMOTE_WAKEUP) { + dev->remote_wakeup = 0; + ret = 0; + } + trace_usb_clear_device_feature(dev->addr, value, ret); + break; + case DeviceOutRequest | USB_REQ_SET_FEATURE: + if (value == USB_DEVICE_REMOTE_WAKEUP) { + dev->remote_wakeup = 1; + ret = 0; + } + trace_usb_set_device_feature(dev->addr, value, ret); + break; + } + return ret; +} diff -Nru qemu-kvm-0.12.5+noroms/hw/usb-desc.h qemu-kvm-0.14.1/hw/usb-desc.h --- qemu-kvm-0.12.5+noroms/hw/usb-desc.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/usb-desc.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,92 @@ +#ifndef QEMU_HW_USB_DESC_H +#define QEMU_HW_USB_DESC_H + +#include + +struct USBDescID { + uint16_t idVendor; + uint16_t idProduct; + uint16_t bcdDevice; + uint8_t iManufacturer; + uint8_t iProduct; + uint8_t iSerialNumber; +}; + +struct USBDescDevice { + uint16_t bcdUSB; + uint8_t bDeviceClass; + uint8_t bDeviceSubClass; + uint8_t bDeviceProtocol; + uint8_t bMaxPacketSize0; + uint8_t bNumConfigurations; + + const USBDescConfig *confs; +}; + +struct USBDescConfig { + uint8_t bNumInterfaces; + uint8_t bConfigurationValue; + uint8_t iConfiguration; + uint8_t bmAttributes; + uint8_t bMaxPower; + + uint8_t nif; + const USBDescIface *ifs; +}; + +struct USBDescIface { + uint8_t bInterfaceNumber; + uint8_t bAlternateSetting; + uint8_t bNumEndpoints; + uint8_t bInterfaceClass; + uint8_t bInterfaceSubClass; + uint8_t bInterfaceProtocol; + uint8_t iInterface; + + uint8_t ndesc; + USBDescOther *descs; + USBDescEndpoint *eps; +}; + +struct USBDescEndpoint { + uint8_t bEndpointAddress; + uint8_t bmAttributes; + uint16_t wMaxPacketSize; + uint8_t bInterval; +}; + +struct USBDescOther { + uint8_t length; + uint8_t *data; +}; + +typedef const char *USBDescStrings[256]; + +struct USBDesc { + USBDescID id; + const USBDescDevice *full; + const USBDescDevice *high; + const char* const *str; +}; + +/* generate usb packages from structs */ +int usb_desc_device(const USBDescID *id, const USBDescDevice *dev, + uint8_t *dest, size_t len); +int usb_desc_device_qualifier(const USBDescDevice *dev, + uint8_t *dest, size_t len); +int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len); +int usb_desc_iface(const USBDescIface *iface, uint8_t *dest, size_t len); +int usb_desc_endpoint(const USBDescEndpoint *ep, uint8_t *dest, size_t len); +int usb_desc_other(const USBDescOther *desc, uint8_t *dest, size_t len); + +/* control message emulation helpers */ +void usb_desc_init(USBDevice *dev); +void usb_desc_attach(USBDevice *dev); +void usb_desc_set_string(USBDevice *dev, uint8_t index, const char *str); +const char *usb_desc_get_string(USBDevice *dev, uint8_t index); +int usb_desc_string(USBDevice *dev, int index, uint8_t *dest, size_t len); +int usb_desc_get_descriptor(USBDevice *dev, int value, uint8_t *dest, size_t len); +int usb_desc_handle_control(USBDevice *dev, int request, int value, + int index, int length, uint8_t *data); + +#endif /* QEMU_HW_USB_DESC_H */ diff -Nru qemu-kvm-0.12.5+noroms/hw/usb.h qemu-kvm-0.14.1/hw/usb.h --- qemu-kvm-0.12.5+noroms/hw/usb.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/usb.h 2011-05-11 13:29:46.000000000 +0000 @@ -44,6 +44,12 @@ #define USB_SPEED_LOW 0 #define USB_SPEED_FULL 1 #define USB_SPEED_HIGH 2 +#define USB_SPEED_SUPER 3 + +#define USB_SPEED_MASK_LOW (1 << USB_SPEED_LOW) +#define USB_SPEED_MASK_FULL (1 << USB_SPEED_FULL) +#define USB_SPEED_MASK_HIGH (1 << USB_SPEED_HIGH) +#define USB_SPEED_MASK_SUPER (1 << USB_SPEED_SUPER) #define USB_STATE_NOTATTACHED 0 #define USB_STATE_ATTACHED 1 @@ -91,6 +97,10 @@ #define EndpointRequest ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_ENDPOINT)<<8) #define EndpointOutRequest \ ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_ENDPOINT)<<8) +#define ClassInterfaceRequest \ + ((USB_DIR_IN|USB_TYPE_CLASS|USB_RECIP_INTERFACE)<<8) +#define ClassInterfaceOutRequest \ + ((USB_DIR_OUT|USB_TYPE_CLASS|USB_RECIP_INTERFACE)<<8) #define USB_REQ_GET_STATUS 0x00 #define USB_REQ_CLEAR_FEATURE 0x01 @@ -112,6 +122,8 @@ #define USB_DT_STRING 0x03 #define USB_DT_INTERFACE 0x04 #define USB_DT_ENDPOINT 0x05 +#define USB_DT_DEVICE_QUALIFIER 0x06 +#define USB_DT_OTHER_SPEED_CONFIG 0x07 #define USB_ENDPOINT_XFER_CONTROL 0 #define USB_ENDPOINT_XFER_ISOC 1 @@ -124,10 +136,27 @@ typedef struct USBDeviceInfo USBDeviceInfo; typedef struct USBPacket USBPacket; +typedef struct USBDesc USBDesc; +typedef struct USBDescID USBDescID; +typedef struct USBDescDevice USBDescDevice; +typedef struct USBDescConfig USBDescConfig; +typedef struct USBDescIface USBDescIface; +typedef struct USBDescEndpoint USBDescEndpoint; +typedef struct USBDescOther USBDescOther; +typedef struct USBDescString USBDescString; + +struct USBDescString { + uint8_t index; + char *str; + QLIST_ENTRY(USBDescString) next; +}; + /* definition of a USB device */ struct USBDevice { DeviceState qdev; USBDeviceInfo *info; + USBPort *port; + char *port_path; void *opaque; int speed; @@ -136,13 +165,17 @@ int auto_attach; int attached; - int state; + int32_t state; uint8_t setup_buf[8]; uint8_t data_buf[1024]; - int remote_wakeup; - int setup_state; - int setup_len; - int setup_index; + int32_t remote_wakeup; + int32_t setup_state; + int32_t setup_len; + int32_t setup_index; + + QLIST_HEAD(, USBDescString) strings; + const USBDescDevice *device; + const USBDescConfig *config; }; struct USBDeviceInfo { @@ -164,6 +197,11 @@ void (*handle_destroy)(USBDevice *dev); /* + * Attach the device + */ + void (*handle_attach)(USBDevice *dev); + + /* * Reset the device */ void (*handle_reset)(USBDevice *dev); @@ -186,18 +224,25 @@ int (*handle_data)(USBDevice *dev, USBPacket *p); const char *product_desc; + const USBDesc *usb_desc; /* handle legacy -usbdevice command line options */ const char *usbdevice_name; USBDevice *(*usbdevice_init)(const char *params); }; -typedef void (*usb_attachfn)(USBPort *port, USBDevice *dev); +typedef struct USBPortOps { + void (*attach)(USBPort *port); + void (*detach)(USBPort *port); + void (*wakeup)(USBDevice *dev); +} USBPortOps; /* USB port on which a device can be connected */ struct USBPort { USBDevice *dev; - usb_attachfn attach; + int speedmask; + char path[16]; + USBPortOps *ops; void *opaque; int index; /* internal port index, may be used with the opaque */ QTAILQ_ENTRY(USBPort) next; @@ -246,6 +291,7 @@ } void usb_attach(USBPort *port, USBDevice *dev); +void usb_wakeup(USBDevice *dev); int usb_generic_handle_packet(USBDevice *s, USBPacket *p); int set_usb_string(uint8_t *buf, const char *str); void usb_send_msg(USBDevice *dev, int msg); @@ -308,7 +354,8 @@ USBDevice *usb_create_simple(USBBus *bus, const char *name); USBDevice *usbdevice_create(const char *cmdline); void usb_register_port(USBBus *bus, USBPort *port, void *opaque, int index, - usb_attachfn attach); + USBPortOps *ops, int speedmask); +void usb_port_location(USBPort *downstream, USBPort *upstream, int portnr); void usb_unregister_port(USBBus *bus, USBPort *port); int usb_device_attach(USBDevice *dev); int usb_device_detach(USBDevice *dev); diff -Nru qemu-kvm-0.12.5+noroms/hw/usb-hid.c qemu-kvm-0.14.1/hw/usb-hid.c --- qemu-kvm-0.12.5+noroms/hw/usb-hid.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/usb-hid.c 2011-05-11 13:29:46.000000000 +0000 @@ -25,6 +25,8 @@ #include "hw.h" #include "console.h" #include "usb.h" +#include "usb-desc.h" +#include "sysemu.h" /* HID interface requests */ #define GET_REPORT 0xa101 @@ -43,18 +45,27 @@ #define USB_TABLET 2 #define USB_KEYBOARD 3 +typedef struct USBPointerEvent { + int32_t xdx, ydy; /* relative iff it's a mouse, otherwise absolute */ + int32_t dz, buttons_state; +} USBPointerEvent; + +#define QUEUE_LENGTH 16 /* should be enough for a triple-click */ +#define QUEUE_MASK (QUEUE_LENGTH-1u) +#define QUEUE_INCR(v) ((v)++, (v) &= QUEUE_MASK) + typedef struct USBMouseState { - int dx, dy, dz, buttons_state; - int x, y; + USBPointerEvent queue[QUEUE_LENGTH]; int mouse_grabbed; QEMUPutMouseEntry *eh_entry; } USBMouseState; typedef struct USBKeyboardState { + uint32_t keycodes[QUEUE_LENGTH]; uint16_t modifiers; uint8_t leds; uint8_t key[16]; - int keys; + int32_t keys; } USBKeyboardState; typedef struct USBHIDState { @@ -63,198 +74,217 @@ USBMouseState ptr; USBKeyboardState kbd; }; + uint32_t head; /* index into circular queue */ + uint32_t n; int kind; - int protocol; + int32_t protocol; uint8_t idle; + int64_t next_idle_clock; int changed; void *datain_opaque; void (*datain)(void *); } USBHIDState; -/* mostly the same values as the Bochs USB Mouse device */ -static const uint8_t qemu_mouse_dev_descriptor[] = { - 0x12, /* u8 bLength; */ - 0x01, /* u8 bDescriptorType; Device */ - 0x00, 0x01, /* u16 bcdUSB; v1.0 */ - - 0x00, /* u8 bDeviceClass; */ - 0x00, /* u8 bDeviceSubClass; */ - 0x00, /* u8 bDeviceProtocol; [ low/full speeds only ] */ - 0x08, /* u8 bMaxPacketSize0; 8 Bytes */ - - 0x27, 0x06, /* u16 idVendor; */ - 0x01, 0x00, /* u16 idProduct; */ - 0x00, 0x00, /* u16 bcdDevice */ - - 0x03, /* u8 iManufacturer; */ - 0x02, /* u8 iProduct; */ - 0x01, /* u8 iSerialNumber; */ - 0x01 /* u8 bNumConfigurations; */ -}; - -static const uint8_t qemu_mouse_config_descriptor[] = { - /* one configuration */ - 0x09, /* u8 bLength; */ - 0x02, /* u8 bDescriptorType; Configuration */ - 0x22, 0x00, /* u16 wTotalLength; */ - 0x01, /* u8 bNumInterfaces; (1) */ - 0x01, /* u8 bConfigurationValue; */ - 0x04, /* u8 iConfiguration; */ - 0xa0, /* u8 bmAttributes; - Bit 7: must be set, - 6: Self-powered, - 5: Remote wakeup, - 4..0: resvd */ - 50, /* u8 MaxPower; */ - - /* USB 1.1: - * USB 2.0, single TT organization (mandatory): - * one interface, protocol 0 - * - * USB 2.0, multiple TT organization (optional): - * two interfaces, protocols 1 (like single TT) - * and 2 (multiple TT mode) ... config is - * sometimes settable - * NOT IMPLEMENTED - */ - - /* one interface */ - 0x09, /* u8 if_bLength; */ - 0x04, /* u8 if_bDescriptorType; Interface */ - 0x00, /* u8 if_bInterfaceNumber; */ - 0x00, /* u8 if_bAlternateSetting; */ - 0x01, /* u8 if_bNumEndpoints; */ - 0x03, /* u8 if_bInterfaceClass; */ - 0x01, /* u8 if_bInterfaceSubClass; */ - 0x02, /* u8 if_bInterfaceProtocol; [usb1.1 or single tt] */ - 0x07, /* u8 if_iInterface; */ - - /* HID descriptor */ - 0x09, /* u8 bLength; */ - 0x21, /* u8 bDescriptorType; */ - 0x01, 0x00, /* u16 HID_class */ - 0x00, /* u8 country_code */ - 0x01, /* u8 num_descriptors */ - 0x22, /* u8 type; Report */ - 52, 0, /* u16 len */ - - /* one endpoint (status change endpoint) */ - 0x07, /* u8 ep_bLength; */ - 0x05, /* u8 ep_bDescriptorType; Endpoint */ - 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */ - 0x03, /* u8 ep_bmAttributes; Interrupt */ - 0x04, 0x00, /* u16 ep_wMaxPacketSize; */ - 0x0a, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ -}; - -static const uint8_t qemu_tablet_config_descriptor[] = { - /* one configuration */ - 0x09, /* u8 bLength; */ - 0x02, /* u8 bDescriptorType; Configuration */ - 0x22, 0x00, /* u16 wTotalLength; */ - 0x01, /* u8 bNumInterfaces; (1) */ - 0x01, /* u8 bConfigurationValue; */ - 0x05, /* u8 iConfiguration; */ - 0xa0, /* u8 bmAttributes; - Bit 7: must be set, - 6: Self-powered, - 5: Remote wakeup, - 4..0: resvd */ - 50, /* u8 MaxPower; */ - - /* USB 1.1: - * USB 2.0, single TT organization (mandatory): - * one interface, protocol 0 - * - * USB 2.0, multiple TT organization (optional): - * two interfaces, protocols 1 (like single TT) - * and 2 (multiple TT mode) ... config is - * sometimes settable - * NOT IMPLEMENTED - */ - - /* one interface */ - 0x09, /* u8 if_bLength; */ - 0x04, /* u8 if_bDescriptorType; Interface */ - 0x00, /* u8 if_bInterfaceNumber; */ - 0x00, /* u8 if_bAlternateSetting; */ - 0x01, /* u8 if_bNumEndpoints; */ - 0x03, /* u8 if_bInterfaceClass; */ - 0x01, /* u8 if_bInterfaceSubClass; */ - 0x02, /* u8 if_bInterfaceProtocol; [usb1.1 or single tt] */ - 0x07, /* u8 if_iInterface; */ - - /* HID descriptor */ - 0x09, /* u8 bLength; */ - 0x21, /* u8 bDescriptorType; */ - 0x01, 0x00, /* u16 HID_class */ - 0x00, /* u8 country_code */ - 0x01, /* u8 num_descriptors */ - 0x22, /* u8 type; Report */ - 74, 0, /* u16 len */ - - /* one endpoint (status change endpoint) */ - 0x07, /* u8 ep_bLength; */ - 0x05, /* u8 ep_bDescriptorType; Endpoint */ - 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */ - 0x03, /* u8 ep_bmAttributes; Interrupt */ - 0x08, 0x00, /* u16 ep_wMaxPacketSize; */ - 0x0a, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ -}; - -static const uint8_t qemu_keyboard_config_descriptor[] = { - /* one configuration */ - 0x09, /* u8 bLength; */ - USB_DT_CONFIG, /* u8 bDescriptorType; Configuration */ - 0x22, 0x00, /* u16 wTotalLength; */ - 0x01, /* u8 bNumInterfaces; (1) */ - 0x01, /* u8 bConfigurationValue; */ - 0x06, /* u8 iConfiguration; */ - 0xa0, /* u8 bmAttributes; - Bit 7: must be set, - 6: Self-powered, - 5: Remote wakeup, - 4..0: resvd */ - 0x32, /* u8 MaxPower; */ - - /* USB 1.1: - * USB 2.0, single TT organization (mandatory): - * one interface, protocol 0 - * - * USB 2.0, multiple TT organization (optional): - * two interfaces, protocols 1 (like single TT) - * and 2 (multiple TT mode) ... config is - * sometimes settable - * NOT IMPLEMENTED - */ - - /* one interface */ - 0x09, /* u8 if_bLength; */ - USB_DT_INTERFACE, /* u8 if_bDescriptorType; Interface */ - 0x00, /* u8 if_bInterfaceNumber; */ - 0x00, /* u8 if_bAlternateSetting; */ - 0x01, /* u8 if_bNumEndpoints; */ - 0x03, /* u8 if_bInterfaceClass; HID */ - 0x01, /* u8 if_bInterfaceSubClass; Boot */ - 0x01, /* u8 if_bInterfaceProtocol; Keyboard */ - 0x07, /* u8 if_iInterface; */ - - /* HID descriptor */ - 0x09, /* u8 bLength; */ - USB_DT_HID, /* u8 bDescriptorType; */ - 0x11, 0x01, /* u16 HID_class */ - 0x00, /* u8 country_code */ - 0x01, /* u8 num_descriptors */ - USB_DT_REPORT, /* u8 type; Report */ - 0x3f, 0x00, /* u16 len */ - - /* one endpoint (status change endpoint) */ - 0x07, /* u8 ep_bLength; */ - USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; Endpoint */ - USB_DIR_IN | 0x01, /* u8 ep_bEndpointAddress; IN Endpoint 1 */ - 0x03, /* u8 ep_bmAttributes; Interrupt */ - 0x08, 0x00, /* u16 ep_wMaxPacketSize; */ - 0x0a, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ +enum { + STR_MANUFACTURER = 1, + STR_PRODUCT_MOUSE, + STR_PRODUCT_TABLET, + STR_PRODUCT_KEYBOARD, + STR_SERIALNUMBER, + STR_CONFIG_MOUSE, + STR_CONFIG_TABLET, + STR_CONFIG_KEYBOARD, +}; + +static const USBDescStrings desc_strings = { + [STR_MANUFACTURER] = "QEMU " QEMU_VERSION, + [STR_PRODUCT_MOUSE] = "QEMU USB Mouse", + [STR_PRODUCT_TABLET] = "QEMU USB Tablet", + [STR_PRODUCT_KEYBOARD] = "QEMU USB Keyboard", + [STR_SERIALNUMBER] = "42", /* == remote wakeup works */ + [STR_CONFIG_MOUSE] = "HID Mouse", + [STR_CONFIG_TABLET] = "HID Tablet", + [STR_CONFIG_KEYBOARD] = "HID Keyboard", +}; + +static const USBDescIface desc_iface_mouse = { + .bInterfaceNumber = 0, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_HID, + .bInterfaceSubClass = 0x01, /* boot */ + .bInterfaceProtocol = 0x02, + .ndesc = 1, + .descs = (USBDescOther[]) { + { + /* HID descriptor */ + .data = (uint8_t[]) { + 0x09, /* u8 bLength */ + USB_DT_HID, /* u8 bDescriptorType */ + 0x01, 0x00, /* u16 HID_class */ + 0x00, /* u8 country_code */ + 0x01, /* u8 num_descriptors */ + USB_DT_REPORT, /* u8 type: Report */ + 52, 0, /* u16 len */ + }, + }, + }, + .eps = (USBDescEndpoint[]) { + { + .bEndpointAddress = USB_DIR_IN | 0x01, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = 4, + .bInterval = 0x0a, + }, + }, +}; + +static const USBDescIface desc_iface_tablet = { + .bInterfaceNumber = 0, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_HID, + .bInterfaceSubClass = 0x01, /* boot */ + .bInterfaceProtocol = 0x02, + .ndesc = 1, + .descs = (USBDescOther[]) { + { + /* HID descriptor */ + .data = (uint8_t[]) { + 0x09, /* u8 bLength */ + USB_DT_HID, /* u8 bDescriptorType */ + 0x01, 0x00, /* u16 HID_class */ + 0x00, /* u8 country_code */ + 0x01, /* u8 num_descriptors */ + USB_DT_REPORT, /* u8 type: Report */ + 74, 0, /* u16 len */ + }, + }, + }, + .eps = (USBDescEndpoint[]) { + { + .bEndpointAddress = USB_DIR_IN | 0x01, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = 8, + .bInterval = 0x0a, + }, + }, +}; + +static const USBDescIface desc_iface_keyboard = { + .bInterfaceNumber = 0, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_HID, + .bInterfaceSubClass = 0x01, /* boot */ + .bInterfaceProtocol = 0x01, /* keyboard */ + .ndesc = 1, + .descs = (USBDescOther[]) { + { + /* HID descriptor */ + .data = (uint8_t[]) { + 0x09, /* u8 bLength */ + USB_DT_HID, /* u8 bDescriptorType */ + 0x11, 0x01, /* u16 HID_class */ + 0x00, /* u8 country_code */ + 0x01, /* u8 num_descriptors */ + USB_DT_REPORT, /* u8 type: Report */ + 0x3f, 0, /* u16 len */ + }, + }, + }, + .eps = (USBDescEndpoint[]) { + { + .bEndpointAddress = USB_DIR_IN | 0x01, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = 8, + .bInterval = 0x0a, + }, + }, +}; + +static const USBDescDevice desc_device_mouse = { + .bcdUSB = 0x0100, + .bMaxPacketSize0 = 8, + .bNumConfigurations = 1, + .confs = (USBDescConfig[]) { + { + .bNumInterfaces = 1, + .bConfigurationValue = 1, + .iConfiguration = STR_CONFIG_MOUSE, + .bmAttributes = 0xa0, + .bMaxPower = 50, + .ifs = &desc_iface_mouse, + }, + }, +}; + +static const USBDescDevice desc_device_tablet = { + .bcdUSB = 0x0100, + .bMaxPacketSize0 = 8, + .bNumConfigurations = 1, + .confs = (USBDescConfig[]) { + { + .bNumInterfaces = 1, + .bConfigurationValue = 1, + .iConfiguration = STR_CONFIG_TABLET, + .bmAttributes = 0xa0, + .bMaxPower = 50, + .ifs = &desc_iface_tablet, + }, + }, +}; + +static const USBDescDevice desc_device_keyboard = { + .bcdUSB = 0x0100, + .bMaxPacketSize0 = 8, + .bNumConfigurations = 1, + .confs = (USBDescConfig[]) { + { + .bNumInterfaces = 1, + .bConfigurationValue = 1, + .iConfiguration = STR_CONFIG_KEYBOARD, + .bmAttributes = 0xa0, + .bMaxPower = 50, + .ifs = &desc_iface_keyboard, + }, + }, +}; + +static const USBDesc desc_mouse = { + .id = { + .idVendor = 0x0627, + .idProduct = 0x0001, + .bcdDevice = 0, + .iManufacturer = STR_MANUFACTURER, + .iProduct = STR_PRODUCT_MOUSE, + .iSerialNumber = STR_SERIALNUMBER, + }, + .full = &desc_device_mouse, + .str = desc_strings, +}; + +static const USBDesc desc_tablet = { + .id = { + .idVendor = 0x0627, + .idProduct = 0x0001, + .bcdDevice = 0, + .iManufacturer = STR_MANUFACTURER, + .iProduct = STR_PRODUCT_TABLET, + .iSerialNumber = STR_SERIALNUMBER, + }, + .full = &desc_device_tablet, + .str = desc_strings, +}; + +static const USBDesc desc_keyboard = { + .id = { + .idVendor = 0x0627, + .idProduct = 0x0001, + .bcdDevice = 0, + .iManufacturer = STR_MANUFACTURER, + .iProduct = STR_PRODUCT_KEYBOARD, + .iSerialNumber = STR_SERIALNUMBER, + }, + .full = &desc_device_keyboard, + .str = desc_strings, }; static const uint8_t qemu_mouse_hid_report_descriptor[] = { @@ -397,7 +427,7 @@ 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x4a, 0x52, 0x4b, 0x00, 0x50, 0x00, 0x4f, 0x00, 0x4d, 0x51, 0x4e, 0x49, 0x4c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xe3, 0xe7, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -410,42 +440,83 @@ if (hs->datain) hs->datain(hs->datain_opaque); + + usb_wakeup(&hs->dev); +} + +static void usb_pointer_event_clear(USBPointerEvent *e, int buttons) { + e->xdx = e->ydy = e->dz = 0; + e->buttons_state = buttons; } -static void usb_mouse_event(void *opaque, - int dx1, int dy1, int dz1, int buttons_state) +static void usb_pointer_event_combine(USBPointerEvent *e, int xyrel, + int x1, int y1, int z1) { + if (xyrel) { + e->xdx += x1; + e->ydy += y1; + } else { + e->xdx = x1; + e->ydy = y1; + } + e->dz += z1; +} + +static void usb_pointer_event(void *opaque, + int x1, int y1, int z1, int buttons_state) { USBHIDState *hs = opaque; USBMouseState *s = &hs->ptr; + unsigned use_slot = (hs->head + hs->n - 1) & QUEUE_MASK; + unsigned previous_slot = (use_slot - 1) & QUEUE_MASK; - s->dx += dx1; - s->dy += dy1; - s->dz += dz1; - s->buttons_state = buttons_state; - + /* We combine events where feasible to keep the queue small. We shouldn't + * combine anything with the first event of a particular button state, as + * that would change the location of the button state change. When the + * queue is empty, a second event is needed because we don't know if + * the first event changed the button state. */ + if (hs->n == QUEUE_LENGTH) { + /* Queue full. Discard old button state, combine motion normally. */ + s->queue[use_slot].buttons_state = buttons_state; + } else if (hs->n < 2 || + s->queue[use_slot].buttons_state != buttons_state || + s->queue[previous_slot].buttons_state != s->queue[use_slot].buttons_state) { + /* Cannot or should not combine, so add an empty item to the queue. */ + QUEUE_INCR(use_slot); + hs->n++; + usb_pointer_event_clear(&s->queue[use_slot], buttons_state); + } + usb_pointer_event_combine(&s->queue[use_slot], + hs->kind == USB_MOUSE, + x1, y1, z1); usb_hid_changed(hs); } -static void usb_tablet_event(void *opaque, - int x, int y, int dz, int buttons_state) +static void usb_keyboard_event(void *opaque, int keycode) { USBHIDState *hs = opaque; - USBMouseState *s = &hs->ptr; - - s->x = x; - s->y = y; - s->dz += dz; - s->buttons_state = buttons_state; + USBKeyboardState *s = &hs->kbd; + int slot; + if (hs->n == QUEUE_LENGTH) { + fprintf(stderr, "usb-kbd: warning: key event queue full\n"); + return; + } + slot = (hs->head + hs->n) & QUEUE_MASK; hs->n++; + s->keycodes[slot] = keycode; usb_hid_changed(hs); } -static void usb_keyboard_event(void *opaque, int keycode) +static void usb_keyboard_process_keycode(USBHIDState *hs) { - USBHIDState *hs = opaque; USBKeyboardState *s = &hs->kbd; uint8_t hid_code, key; - int i; + int i, keycode, slot; + + if (hs->n == 0) { + return; + } + slot = hs->head & QUEUE_MASK; QUEUE_INCR(hs->head); hs->n--; + keycode = s->keycodes[slot]; key = keycode & 0x7f; hid_code = usb_hid_usage_keys[key | ((s->modifiers >> 1) & (1 << 7))]; @@ -458,15 +529,18 @@ case 0xe0: if (s->modifiers & (1 << 9)) { s->modifiers ^= 3 << 8; + usb_hid_changed(hs); return; } case 0xe1 ... 0xe7: if (keycode & (1 << 7)) { s->modifiers &= ~(1 << (hid_code & 0x0f)); + usb_hid_changed(hs); return; } case 0xe8 ... 0xef: s->modifiers |= 1 << (hid_code & 0x0f); + usb_hid_changed(hs); return; } @@ -475,7 +549,6 @@ if (s->key[i] == hid_code) { s->key[i] = s->key[-- s->keys]; s->key[s->keys] = 0x00; - usb_hid_changed(hs); break; } if (i < 0) @@ -490,8 +563,6 @@ } else return; } - - usb_hid_changed(hs); } static inline int int_clamp(int val, int vmin, int vmax) @@ -504,88 +575,96 @@ return val; } -static int usb_mouse_poll(USBHIDState *hs, uint8_t *buf, int len) +static int usb_pointer_poll(USBHIDState *hs, uint8_t *buf, int len) { int dx, dy, dz, b, l; + int index; USBMouseState *s = &hs->ptr; + USBPointerEvent *e; if (!s->mouse_grabbed) { - s->eh_entry = qemu_add_mouse_event_handler(usb_mouse_event, hs, - 0, "QEMU USB Mouse"); - s->mouse_grabbed = 1; + qemu_activate_mouse_event_handler(s->eh_entry); + s->mouse_grabbed = 1; } - dx = int_clamp(s->dx, -127, 127); - dy = int_clamp(s->dy, -127, 127); - dz = int_clamp(s->dz, -127, 127); - - s->dx -= dx; - s->dy -= dy; - s->dz -= dz; - - /* Appears we have to invert the wheel direction */ - dz = 0 - dz; + /* When the buffer is empty, return the last event. Relative + movements will all be zero. */ + index = (hs->n ? hs->head : hs->head - 1); + e = &s->queue[index & QUEUE_MASK]; + + if (hs->kind == USB_MOUSE) { + dx = int_clamp(e->xdx, -127, 127); + dy = int_clamp(e->ydy, -127, 127); + e->xdx -= dx; + e->ydy -= dy; + } else { + dx = e->xdx; + dy = e->ydy; + } + dz = int_clamp(e->dz, -127, 127); + e->dz -= dz; b = 0; - if (s->buttons_state & MOUSE_EVENT_LBUTTON) + if (e->buttons_state & MOUSE_EVENT_LBUTTON) b |= 0x01; - if (s->buttons_state & MOUSE_EVENT_RBUTTON) + if (e->buttons_state & MOUSE_EVENT_RBUTTON) b |= 0x02; - if (s->buttons_state & MOUSE_EVENT_MBUTTON) + if (e->buttons_state & MOUSE_EVENT_MBUTTON) b |= 0x04; - l = 0; - if (len > l) - buf[l ++] = b; - if (len > l) - buf[l ++] = dx; - if (len > l) - buf[l ++] = dy; - if (len > l) - buf[l ++] = dz; - return l; -} - -static int usb_tablet_poll(USBHIDState *hs, uint8_t *buf, int len) -{ - int dz, b, l; - USBMouseState *s = &hs->ptr; - - if (!s->mouse_grabbed) { - s->eh_entry = qemu_add_mouse_event_handler(usb_tablet_event, hs, - 1, "QEMU USB Tablet"); - s->mouse_grabbed = 1; + if (hs->n && + !e->dz && + (hs->kind == USB_TABLET || (!e->xdx && !e->ydy))) { + /* that deals with this event */ + QUEUE_INCR(hs->head); + hs->n--; } - dz = int_clamp(s->dz, -127, 127); - s->dz -= dz; - /* Appears we have to invert the wheel direction */ dz = 0 - dz; - b = 0; - if (s->buttons_state & MOUSE_EVENT_LBUTTON) - b |= 0x01; - if (s->buttons_state & MOUSE_EVENT_RBUTTON) - b |= 0x02; - if (s->buttons_state & MOUSE_EVENT_MBUTTON) - b |= 0x04; + l = 0; + switch (hs->kind) { + case USB_MOUSE: + if (len > l) + buf[l++] = b; + if (len > l) + buf[l++] = dx; + if (len > l) + buf[l++] = dy; + if (len > l) + buf[l++] = dz; + break; + + case USB_TABLET: + if (len > l) + buf[l++] = b; + if (len > l) + buf[l++] = dx & 0xff; + if (len > l) + buf[l++] = dx >> 8; + if (len > l) + buf[l++] = dy & 0xff; + if (len > l) + buf[l++] = dy >> 8; + if (len > l) + buf[l++] = dz; + break; - buf[0] = b; - buf[1] = s->x & 0xff; - buf[2] = s->x >> 8; - buf[3] = s->y & 0xff; - buf[4] = s->y >> 8; - buf[5] = dz; - l = 6; + default: + abort(); + } return l; } -static int usb_keyboard_poll(USBKeyboardState *s, uint8_t *buf, int len) +static int usb_keyboard_poll(USBHIDState *hs, uint8_t *buf, int len) { + USBKeyboardState *s = &hs->kbd; if (len < 2) return 0; + usb_keyboard_process_keycode(hs); + buf[0] = s->modifiers & 0xff; buf[1] = 0; if (s->keys > 6) @@ -599,12 +678,20 @@ static int usb_keyboard_write(USBKeyboardState *s, uint8_t *buf, int len) { if (len > 0) { + int ledstate = 0; /* 0x01: Num Lock LED * 0x02: Caps Lock LED * 0x04: Scroll Lock LED * 0x08: Compose LED * 0x10: Kana LED */ s->leds = buf[0]; + if (s->leds & 0x04) + ledstate |= QEMU_SCROLL_LOCK_LED; + if (s->leds & 0x01) + ledstate |= QEMU_NUM_LOCK_LED; + if (s->leds & 0x02) + ledstate |= QEMU_CAPS_LOCK_LED; + kbd_put_ledstate(ledstate); } return 0; } @@ -613,12 +700,9 @@ { USBHIDState *s = (USBHIDState *)dev; - s->ptr.dx = 0; - s->ptr.dy = 0; - s->ptr.dz = 0; - s->ptr.x = 0; - s->ptr.y = 0; - s->ptr.buttons_state = 0; + memset(s->ptr.queue, 0, sizeof (s->ptr.queue)); + s->head = 0; + s->n = 0; s->protocol = 1; } @@ -627,113 +711,32 @@ USBHIDState *s = (USBHIDState *)dev; qemu_add_kbd_event_handler(usb_keyboard_event, s); + memset(s->kbd.keycodes, 0, sizeof (s->kbd.keycodes)); + s->head = 0; + s->n = 0; + memset(s->kbd.key, 0, sizeof (s->kbd.key)); + s->kbd.keys = 0; s->protocol = 1; } +static void usb_hid_set_next_idle(USBHIDState *s, int64_t curtime) +{ + s->next_idle_clock = curtime + (get_ticks_per_sec() * s->idle * 4) / 1000; +} + static int usb_hid_handle_control(USBDevice *dev, int request, int value, int index, int length, uint8_t *data) { USBHIDState *s = (USBHIDState *)dev; - int ret = 0; + int ret; + ret = usb_desc_handle_control(dev, request, value, index, length, data); + if (ret >= 0) { + return ret; + } + + ret = 0; switch(request) { - case DeviceRequest | USB_REQ_GET_STATUS: - data[0] = (1 << USB_DEVICE_SELF_POWERED) | - (dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP); - data[1] = 0x00; - ret = 2; - break; - case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: - if (value == USB_DEVICE_REMOTE_WAKEUP) { - dev->remote_wakeup = 0; - } else { - goto fail; - } - ret = 0; - break; - case DeviceOutRequest | USB_REQ_SET_FEATURE: - if (value == USB_DEVICE_REMOTE_WAKEUP) { - dev->remote_wakeup = 1; - } else { - goto fail; - } - ret = 0; - break; - case DeviceOutRequest | USB_REQ_SET_ADDRESS: - dev->addr = value; - ret = 0; - break; - case DeviceRequest | USB_REQ_GET_DESCRIPTOR: - switch(value >> 8) { - case USB_DT_DEVICE: - memcpy(data, qemu_mouse_dev_descriptor, - sizeof(qemu_mouse_dev_descriptor)); - ret = sizeof(qemu_mouse_dev_descriptor); - break; - case USB_DT_CONFIG: - if (s->kind == USB_MOUSE) { - memcpy(data, qemu_mouse_config_descriptor, - sizeof(qemu_mouse_config_descriptor)); - ret = sizeof(qemu_mouse_config_descriptor); - } else if (s->kind == USB_TABLET) { - memcpy(data, qemu_tablet_config_descriptor, - sizeof(qemu_tablet_config_descriptor)); - ret = sizeof(qemu_tablet_config_descriptor); - } else if (s->kind == USB_KEYBOARD) { - memcpy(data, qemu_keyboard_config_descriptor, - sizeof(qemu_keyboard_config_descriptor)); - ret = sizeof(qemu_keyboard_config_descriptor); - } - break; - case USB_DT_STRING: - switch(value & 0xff) { - case 0: - /* language ids */ - data[0] = 4; - data[1] = 3; - data[2] = 0x09; - data[3] = 0x04; - ret = 4; - break; - case 1: - /* serial number */ - ret = set_usb_string(data, "1"); - break; - case 2: - /* product description */ - ret = set_usb_string(data, s->dev.product_desc); - break; - case 3: - /* vendor description */ - ret = set_usb_string(data, "QEMU " QEMU_VERSION); - break; - case 4: - ret = set_usb_string(data, "HID Mouse"); - break; - case 5: - ret = set_usb_string(data, "HID Tablet"); - break; - case 6: - ret = set_usb_string(data, "HID Keyboard"); - break; - case 7: - ret = set_usb_string(data, "Endpoint1 Interrupt Pipe"); - break; - default: - goto fail; - } - break; - default: - goto fail; - } - break; - case DeviceRequest | USB_REQ_GET_CONFIGURATION: - data[0] = 1; - ret = 1; - break; - case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: - ret = 0; - break; case DeviceRequest | USB_REQ_GET_INTERFACE: data[0] = 0; ret = 1; @@ -764,12 +767,10 @@ } break; case GET_REPORT: - if (s->kind == USB_MOUSE) - ret = usb_mouse_poll(s, data, length); - else if (s->kind == USB_TABLET) - ret = usb_tablet_poll(s, data, length); + if (s->kind == USB_MOUSE || s->kind == USB_TABLET) + ret = usb_pointer_poll(s, data, length); else if (s->kind == USB_KEYBOARD) - ret = usb_keyboard_poll(&s->kbd, data, length); + ret = usb_keyboard_poll(s, data, length); break; case SET_REPORT: if (s->kind == USB_KEYBOARD) @@ -795,6 +796,7 @@ break; case SET_IDLE: s->idle = (uint8_t) (value >> 8); + usb_hid_set_next_idle(s, qemu_get_clock(vm_clock)); ret = 0; break; default: @@ -813,16 +815,17 @@ switch(p->pid) { case USB_TOKEN_IN: if (p->devep == 1) { - /* TODO: Implement finite idle delays. */ - if (!(s->changed || s->idle)) + int64_t curtime = qemu_get_clock(vm_clock); + if (!s->changed && (!s->idle || s->next_idle_clock - curtime > 0)) return USB_RET_NAK; - s->changed = 0; - if (s->kind == USB_MOUSE) - ret = usb_mouse_poll(s, p->data, p->len); - else if (s->kind == USB_TABLET) - ret = usb_tablet_poll(s, p->data, p->len); - else if (s->kind == USB_KEYBOARD) - ret = usb_keyboard_poll(&s->kbd, p->data, p->len); + usb_hid_set_next_idle(s, curtime); + if (s->kind == USB_MOUSE || s->kind == USB_TABLET) { + ret = usb_pointer_poll(s, p->data, p->len); + } + else if (s->kind == USB_KEYBOARD) { + ret = usb_keyboard_poll(s, p->data, p->len); + } + s->changed = s->n > 0; } else { goto fail; } @@ -840,16 +843,30 @@ { USBHIDState *s = (USBHIDState *)dev; - if (s->kind != USB_KEYBOARD) + switch(s->kind) { + case USB_KEYBOARD: + qemu_remove_kbd_event_handler(); + break; + default: qemu_remove_mouse_event_handler(s->ptr.eh_entry); - /* TODO: else */ + } } static int usb_hid_initfn(USBDevice *dev, int kind) { USBHIDState *s = DO_UPCAST(USBHIDState, dev, dev); - s->dev.speed = USB_SPEED_FULL; + + usb_desc_init(dev); s->kind = kind; + + if (s->kind == USB_MOUSE) { + s->ptr.eh_entry = qemu_add_mouse_event_handler(usb_pointer_event, s, + 0, "QEMU USB Mouse"); + } else if (s->kind == USB_TABLET) { + s->ptr.eh_entry = qemu_add_mouse_event_handler(usb_pointer_event, s, + 1, "QEMU USB Tablet"); + } + /* Force poll routine to be run and grab input the first time. */ s->changed = 1; return 0; @@ -878,12 +895,73 @@ s->datain = datain; } +static int usb_hid_post_load(void *opaque, int version_id) +{ + USBHIDState *s = opaque; + + if (s->idle) { + usb_hid_set_next_idle(s, qemu_get_clock(vm_clock)); + } + return 0; +} + +static const VMStateDescription vmstate_usb_ptr_queue = { + .name = "usb-ptr-queue", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField []) { + VMSTATE_INT32(xdx, USBPointerEvent), + VMSTATE_INT32(ydy, USBPointerEvent), + VMSTATE_INT32(dz, USBPointerEvent), + VMSTATE_INT32(buttons_state, USBPointerEvent), + VMSTATE_END_OF_LIST() + } +}; +static const VMStateDescription vmstate_usb_ptr = { + .name = "usb-ptr", + .version_id = 1, + .minimum_version_id = 1, + .post_load = usb_hid_post_load, + .fields = (VMStateField []) { + VMSTATE_USB_DEVICE(dev, USBHIDState), + VMSTATE_STRUCT_ARRAY(ptr.queue, USBHIDState, QUEUE_LENGTH, 0, + vmstate_usb_ptr_queue, USBPointerEvent), + VMSTATE_UINT32(head, USBHIDState), + VMSTATE_UINT32(n, USBHIDState), + VMSTATE_INT32(protocol, USBHIDState), + VMSTATE_UINT8(idle, USBHIDState), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_usb_kbd = { + .name = "usb-kbd", + .version_id = 1, + .minimum_version_id = 1, + .post_load = usb_hid_post_load, + .fields = (VMStateField []) { + VMSTATE_USB_DEVICE(dev, USBHIDState), + VMSTATE_UINT32_ARRAY(kbd.keycodes, USBHIDState, QUEUE_LENGTH), + VMSTATE_UINT32(head, USBHIDState), + VMSTATE_UINT32(n, USBHIDState), + VMSTATE_UINT16(kbd.modifiers, USBHIDState), + VMSTATE_UINT8(kbd.leds, USBHIDState), + VMSTATE_UINT8_ARRAY(kbd.key, USBHIDState, 16), + VMSTATE_INT32(kbd.keys, USBHIDState), + VMSTATE_INT32(protocol, USBHIDState), + VMSTATE_UINT8(idle, USBHIDState), + VMSTATE_END_OF_LIST() + } +}; + static struct USBDeviceInfo hid_info[] = { { .product_desc = "QEMU USB Tablet", .qdev.name = "usb-tablet", .usbdevice_name = "tablet", .qdev.size = sizeof(USBHIDState), + .qdev.vmsd = &vmstate_usb_ptr, + .usb_desc = &desc_tablet, .init = usb_tablet_initfn, .handle_packet = usb_generic_handle_packet, .handle_reset = usb_mouse_handle_reset, @@ -895,6 +973,8 @@ .qdev.name = "usb-mouse", .usbdevice_name = "mouse", .qdev.size = sizeof(USBHIDState), + .qdev.vmsd = &vmstate_usb_ptr, + .usb_desc = &desc_mouse, .init = usb_mouse_initfn, .handle_packet = usb_generic_handle_packet, .handle_reset = usb_mouse_handle_reset, @@ -906,6 +986,8 @@ .qdev.name = "usb-kbd", .usbdevice_name = "keyboard", .qdev.size = sizeof(USBHIDState), + .qdev.vmsd = &vmstate_usb_kbd, + .usb_desc = &desc_keyboard, .init = usb_keyboard_initfn, .handle_packet = usb_generic_handle_packet, .handle_reset = usb_keyboard_handle_reset, diff -Nru qemu-kvm-0.12.5+noroms/hw/usb-hub.c qemu-kvm-0.14.1/hw/usb-hub.c --- qemu-kvm-0.12.5+noroms/hw/usb-hub.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/usb-hub.c 2011-05-11 13:29:46.000000000 +0000 @@ -23,10 +23,11 @@ */ #include "qemu-common.h" #include "usb.h" +#include "usb-desc.h" //#define DEBUG -#define MAX_PORTS 8 +#define NUM_PORTS 8 typedef struct USBHubPort { USBPort port; @@ -36,8 +37,7 @@ typedef struct USBHubState { USBDevice dev; - int nb_ports; - USBHubPort ports[MAX_PORTS]; + USBHubPort ports[NUM_PORTS]; } USBHubState; #define ClearHubFeature (0x2000 | USB_REQ_CLEAR_FEATURE) @@ -83,6 +83,60 @@ /* same as Linux kernel root hubs */ +enum { + STR_MANUFACTURER = 1, + STR_PRODUCT, + STR_SERIALNUMBER, +}; + +static const USBDescStrings desc_strings = { + [STR_MANUFACTURER] = "QEMU " QEMU_VERSION, + [STR_PRODUCT] = "QEMU USB Hub", + [STR_SERIALNUMBER] = "314159", +}; + +static const USBDescIface desc_iface_hub = { + .bInterfaceNumber = 0, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_HUB, + .eps = (USBDescEndpoint[]) { + { + .bEndpointAddress = USB_DIR_IN | 0x01, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = 1 + (NUM_PORTS + 7) / 8, + .bInterval = 0xff, + }, + } +}; + +static const USBDescDevice desc_device_hub = { + .bcdUSB = 0x0110, + .bDeviceClass = USB_CLASS_HUB, + .bMaxPacketSize0 = 8, + .bNumConfigurations = 1, + .confs = (USBDescConfig[]) { + { + .bNumInterfaces = 1, + .bConfigurationValue = 1, + .bmAttributes = 0xe0, + .ifs = &desc_iface_hub, + }, + }, +}; + +static const USBDesc desc_hub = { + .id = { + .idVendor = 0, + .idProduct = 0, + .bcdDevice = 0x0101, + .iManufacturer = STR_MANUFACTURER, + .iProduct = STR_PRODUCT, + .iSerialNumber = STR_SERIALNUMBER, + }, + .full = &desc_device_hub, + .str = desc_strings, +}; + static const uint8_t qemu_hub_dev_descriptor[] = { 0x12, /* u8 bLength; */ 0x01, /* u8 bDescriptorType; Device */ @@ -113,7 +167,7 @@ 0x01, /* u8 bNumInterfaces; (1) */ 0x01, /* u8 bConfigurationValue; */ 0x00, /* u8 iConfiguration; */ - 0xc0, /* u8 bmAttributes; + 0xe0, /* u8 bmAttributes; Bit 7: must be set, 6: Self-powered, 5: Remote wakeup, @@ -164,37 +218,51 @@ /* DeviceRemovable and PortPwrCtrlMask patched in later */ }; -static void usb_hub_attach(USBPort *port1, USBDevice *dev) +static void usb_hub_attach(USBPort *port1) { USBHubState *s = port1->opaque; USBHubPort *port = &s->ports[port1->index]; - if (dev) { - if (port->port.dev) - usb_attach(port1, NULL); - - port->wPortStatus |= PORT_STAT_CONNECTION; - port->wPortChange |= PORT_STAT_C_CONNECTION; - if (dev->speed == USB_SPEED_LOW) - port->wPortStatus |= PORT_STAT_LOW_SPEED; - else - port->wPortStatus &= ~PORT_STAT_LOW_SPEED; - port->port.dev = dev; - /* send the attach message */ - usb_send_msg(dev, USB_MSG_ATTACH); + port->wPortStatus |= PORT_STAT_CONNECTION; + port->wPortChange |= PORT_STAT_C_CONNECTION; + if (port->port.dev->speed == USB_SPEED_LOW) { + port->wPortStatus |= PORT_STAT_LOW_SPEED; } else { - dev = port->port.dev; - if (dev) { - port->wPortStatus &= ~PORT_STAT_CONNECTION; - port->wPortChange |= PORT_STAT_C_CONNECTION; - if (port->wPortStatus & PORT_STAT_ENABLE) { - port->wPortStatus &= ~PORT_STAT_ENABLE; - port->wPortChange |= PORT_STAT_C_ENABLE; - } - /* send the detach message */ - usb_send_msg(dev, USB_MSG_DETACH); - port->port.dev = NULL; - } + port->wPortStatus &= ~PORT_STAT_LOW_SPEED; + } +} + +static void usb_hub_detach(USBPort *port1) +{ + USBHubState *s = port1->opaque; + USBHubPort *port = &s->ports[port1->index]; + + port->wPortStatus &= ~PORT_STAT_CONNECTION; + port->wPortChange |= PORT_STAT_C_CONNECTION; + if (port->wPortStatus & PORT_STAT_ENABLE) { + port->wPortStatus &= ~PORT_STAT_ENABLE; + port->wPortChange |= PORT_STAT_C_ENABLE; + } +} + +static void usb_hub_wakeup(USBDevice *dev) +{ + USBHubState *s = dev->port->opaque; + USBHubPort *port = &s->ports[dev->port->index]; + + if (port->wPortStatus & PORT_STAT_SUSPEND) { + port->wPortChange |= PORT_STAT_C_SUSPEND; + usb_wakeup(&s->dev); + } +} + +static void usb_hub_handle_attach(USBDevice *dev) +{ + USBHubState *s = DO_UPCAST(USBHubState, dev, dev); + int i; + + for (i = 0; i < NUM_PORTS; i++) { + usb_port_location(&s->ports[i].port, dev->port, i+1); } } @@ -209,93 +277,18 @@ USBHubState *s = (USBHubState *)dev; int ret; + ret = usb_desc_handle_control(dev, request, value, index, length, data); + if (ret >= 0) { + return ret; + } + switch(request) { - case DeviceRequest | USB_REQ_GET_STATUS: - data[0] = (1 << USB_DEVICE_SELF_POWERED) | - (dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP); - data[1] = 0x00; - ret = 2; - break; - case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: - if (value == USB_DEVICE_REMOTE_WAKEUP) { - dev->remote_wakeup = 0; - } else { - goto fail; - } - ret = 0; - break; case EndpointOutRequest | USB_REQ_CLEAR_FEATURE: if (value == 0 && index != 0x81) { /* clear ep halt */ goto fail; } ret = 0; break; - case DeviceOutRequest | USB_REQ_SET_FEATURE: - if (value == USB_DEVICE_REMOTE_WAKEUP) { - dev->remote_wakeup = 1; - } else { - goto fail; - } - ret = 0; - break; - case DeviceOutRequest | USB_REQ_SET_ADDRESS: - dev->addr = value; - ret = 0; - break; - case DeviceRequest | USB_REQ_GET_DESCRIPTOR: - switch(value >> 8) { - case USB_DT_DEVICE: - memcpy(data, qemu_hub_dev_descriptor, - sizeof(qemu_hub_dev_descriptor)); - ret = sizeof(qemu_hub_dev_descriptor); - break; - case USB_DT_CONFIG: - memcpy(data, qemu_hub_config_descriptor, - sizeof(qemu_hub_config_descriptor)); - - /* status change endpoint size based on number - * of ports */ - data[22] = (s->nb_ports + 1 + 7) / 8; - - ret = sizeof(qemu_hub_config_descriptor); - break; - case USB_DT_STRING: - switch(value & 0xff) { - case 0: - /* language ids */ - data[0] = 4; - data[1] = 3; - data[2] = 0x09; - data[3] = 0x04; - ret = 4; - break; - case 1: - /* serial number */ - ret = set_usb_string(data, "314159"); - break; - case 2: - /* product description */ - ret = set_usb_string(data, "QEMU USB Hub"); - break; - case 3: - /* vendor description */ - ret = set_usb_string(data, "QEMU " QEMU_VERSION); - break; - default: - goto fail; - } - break; - default: - goto fail; - } - break; - case DeviceRequest | USB_REQ_GET_CONFIGURATION: - data[0] = 1; - ret = 1; - break; - case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: - ret = 0; - break; case DeviceRequest | USB_REQ_GET_INTERFACE: data[0] = 0; ret = 1; @@ -315,8 +308,9 @@ { unsigned int n = index - 1; USBHubPort *port; - if (n >= s->nb_ports) + if (n >= NUM_PORTS) { goto fail; + } port = &s->ports[n]; data[0] = port->wPortStatus; data[1] = port->wPortStatus >> 8; @@ -338,8 +332,9 @@ unsigned int n = index - 1; USBHubPort *port; USBDevice *dev; - if (n >= s->nb_ports) + if (n >= NUM_PORTS) { goto fail; + } port = &s->ports[n]; dev = port->port.dev; switch(value) { @@ -366,11 +361,11 @@ { unsigned int n = index - 1; USBHubPort *port; - USBDevice *dev; - if (n >= s->nb_ports) + + if (n >= NUM_PORTS) { goto fail; + } port = &s->ports[n]; - dev = port->port.dev; switch(value) { case PORT_ENABLE: port->wPortStatus &= ~PORT_STAT_ENABLE; @@ -404,17 +399,17 @@ unsigned int n, limit, var_hub_size = 0; memcpy(data, qemu_hub_hub_descriptor, sizeof(qemu_hub_hub_descriptor)); - data[2] = s->nb_ports; + data[2] = NUM_PORTS; /* fill DeviceRemovable bits */ - limit = ((s->nb_ports + 1 + 7) / 8) + 7; + limit = ((NUM_PORTS + 1 + 7) / 8) + 7; for (n = 7; n < limit; n++) { data[n] = 0x00; var_hub_size++; } /* fill PortPwrCtrlMask bits */ - limit = limit + ((s->nb_ports + 7) / 8); + limit = limit + ((NUM_PORTS + 7) / 8); for (;n < limit; n++) { data[n] = 0xff; var_hub_size++; @@ -443,14 +438,14 @@ USBHubPort *port; unsigned int status; int i, n; - n = (s->nb_ports + 1 + 7) / 8; + n = (NUM_PORTS + 1 + 7) / 8; if (p->len == 1) { /* FreeBSD workaround */ n = 1; } else if (n > p->len) { return USB_RET_BABBLE; } status = 0; - for(i = 0; i < s->nb_ports; i++) { + for(i = 0; i < NUM_PORTS; i++) { port = &s->ports[i]; if (port->wPortChange) status |= (1 << (i + 1)); @@ -482,7 +477,7 @@ USBDevice *dev; int i, ret; - for(i = 0; i < s->nb_ports; i++) { + for(i = 0; i < NUM_PORTS; i++) { port = &s->ports[i]; dev = port->port.dev; if (dev && (port->wPortStatus & PORT_STAT_ENABLE)) { @@ -519,36 +514,69 @@ USBHubState *s = (USBHubState *)dev; int i; - for (i = 0; i < s->nb_ports; i++) { + for (i = 0; i < NUM_PORTS; i++) { usb_unregister_port(usb_bus_from_device(dev), &s->ports[i].port); } } +static USBPortOps usb_hub_port_ops = { + .attach = usb_hub_attach, + .detach = usb_hub_detach, + .wakeup = usb_hub_wakeup, +}; + static int usb_hub_initfn(USBDevice *dev) { USBHubState *s = DO_UPCAST(USBHubState, dev, dev); USBHubPort *port; int i; - s->dev.speed = USB_SPEED_FULL, - s->nb_ports = MAX_PORTS; /* FIXME: make configurable */ - for (i = 0; i < s->nb_ports; i++) { + usb_desc_init(dev); + for (i = 0; i < NUM_PORTS; i++) { port = &s->ports[i]; usb_register_port(usb_bus_from_device(dev), - &port->port, s, i, usb_hub_attach); + &port->port, s, i, &usb_hub_port_ops, + USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL); port->wPortStatus = PORT_STAT_POWER; port->wPortChange = 0; } return 0; } +static const VMStateDescription vmstate_usb_hub_port = { + .name = "usb-hub-port", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField []) { + VMSTATE_UINT16(wPortStatus, USBHubPort), + VMSTATE_UINT16(wPortChange, USBHubPort), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_usb_hub = { + .name = "usb-hub", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField []) { + VMSTATE_USB_DEVICE(dev, USBHubState), + VMSTATE_STRUCT_ARRAY(ports, USBHubState, NUM_PORTS, 0, + vmstate_usb_hub_port, USBHubPort), + VMSTATE_END_OF_LIST() + } +}; + static struct USBDeviceInfo hub_info = { .product_desc = "QEMU USB Hub", .qdev.name = "usb-hub", + .qdev.fw_name = "hub", .qdev.size = sizeof(USBHubState), + .qdev.vmsd = &vmstate_usb_hub, + .usb_desc = &desc_hub, .init = usb_hub_initfn, .handle_packet = usb_hub_handle_packet, + .handle_attach = usb_hub_handle_attach, .handle_reset = usb_hub_handle_reset, .handle_control = usb_hub_handle_control, .handle_data = usb_hub_handle_data, diff -Nru qemu-kvm-0.12.5+noroms/hw/usb-msd.c qemu-kvm-0.14.1/hw/usb-msd.c --- qemu-kvm-0.12.5+noroms/hw/usb-msd.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/usb-msd.c 2011-05-11 13:29:46.000000000 +0000 @@ -11,10 +11,12 @@ #include "qemu-option.h" #include "qemu-config.h" #include "usb.h" -#include "block.h" +#include "usb-desc.h" #include "scsi.h" #include "console.h" #include "monitor.h" +#include "sysemu.h" +#include "blockdev.h" //#define DEBUG_MSD @@ -47,8 +49,9 @@ uint32_t residue; uint32_t tag; SCSIBus bus; - DriveInfo *dinfo; + BlockConf conf; SCSIDevice *scsi_dev; + uint32_t removable; int result; /* For async completion. */ USBPacket *packet; @@ -71,69 +74,102 @@ uint8_t status; }; -static const uint8_t qemu_msd_dev_descriptor[] = { - 0x12, /* u8 bLength; */ - 0x01, /* u8 bDescriptorType; Device */ - 0x00, 0x01, /* u16 bcdUSB; v1.0 */ - - 0x00, /* u8 bDeviceClass; */ - 0x00, /* u8 bDeviceSubClass; */ - 0x00, /* u8 bDeviceProtocol; [ low/full speeds only ] */ - 0x08, /* u8 bMaxPacketSize0; 8 Bytes */ - - /* Vendor and product id are arbitrary. */ - 0x00, 0x00, /* u16 idVendor; */ - 0x00, 0x00, /* u16 idProduct; */ - 0x00, 0x00, /* u16 bcdDevice */ - - 0x01, /* u8 iManufacturer; */ - 0x02, /* u8 iProduct; */ - 0x03, /* u8 iSerialNumber; */ - 0x01 /* u8 bNumConfigurations; */ -}; - -static const uint8_t qemu_msd_config_descriptor[] = { - - /* one configuration */ - 0x09, /* u8 bLength; */ - 0x02, /* u8 bDescriptorType; Configuration */ - 0x20, 0x00, /* u16 wTotalLength; */ - 0x01, /* u8 bNumInterfaces; (1) */ - 0x01, /* u8 bConfigurationValue; */ - 0x00, /* u8 iConfiguration; */ - 0xc0, /* u8 bmAttributes; - Bit 7: must be set, - 6: Self-powered, - 5: Remote wakeup, - 4..0: resvd */ - 0x00, /* u8 MaxPower; */ - - /* one interface */ - 0x09, /* u8 if_bLength; */ - 0x04, /* u8 if_bDescriptorType; Interface */ - 0x00, /* u8 if_bInterfaceNumber; */ - 0x00, /* u8 if_bAlternateSetting; */ - 0x02, /* u8 if_bNumEndpoints; */ - 0x08, /* u8 if_bInterfaceClass; MASS STORAGE */ - 0x06, /* u8 if_bInterfaceSubClass; SCSI */ - 0x50, /* u8 if_bInterfaceProtocol; Bulk Only */ - 0x00, /* u8 if_iInterface; */ - - /* Bulk-In endpoint */ - 0x07, /* u8 ep_bLength; */ - 0x05, /* u8 ep_bDescriptorType; Endpoint */ - 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */ - 0x02, /* u8 ep_bmAttributes; Bulk */ - 0x40, 0x00, /* u16 ep_wMaxPacketSize; */ - 0x00, /* u8 ep_bInterval; */ - - /* Bulk-Out endpoint */ - 0x07, /* u8 ep_bLength; */ - 0x05, /* u8 ep_bDescriptorType; Endpoint */ - 0x02, /* u8 ep_bEndpointAddress; OUT Endpoint 2 */ - 0x02, /* u8 ep_bmAttributes; Bulk */ - 0x40, 0x00, /* u16 ep_wMaxPacketSize; */ - 0x00 /* u8 ep_bInterval; */ +enum { + STR_MANUFACTURER = 1, + STR_PRODUCT, + STR_SERIALNUMBER, + STR_CONFIG_FULL, + STR_CONFIG_HIGH, +}; + +static const USBDescStrings desc_strings = { + [STR_MANUFACTURER] = "QEMU " QEMU_VERSION, + [STR_PRODUCT] = "QEMU USB HARDDRIVE", + [STR_SERIALNUMBER] = "1", + [STR_CONFIG_FULL] = "Full speed config (usb 1.1)", + [STR_CONFIG_HIGH] = "High speed config (usb 2.0)", +}; + +static const USBDescIface desc_iface_full = { + .bInterfaceNumber = 0, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_MASS_STORAGE, + .bInterfaceSubClass = 0x06, /* SCSI */ + .bInterfaceProtocol = 0x50, /* Bulk */ + .eps = (USBDescEndpoint[]) { + { + .bEndpointAddress = USB_DIR_IN | 0x01, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = 64, + },{ + .bEndpointAddress = USB_DIR_OUT | 0x02, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = 64, + }, + } +}; + +static const USBDescDevice desc_device_full = { + .bcdUSB = 0x0200, + .bMaxPacketSize0 = 8, + .bNumConfigurations = 1, + .confs = (USBDescConfig[]) { + { + .bNumInterfaces = 1, + .bConfigurationValue = 1, + .iConfiguration = STR_CONFIG_FULL, + .bmAttributes = 0xc0, + .ifs = &desc_iface_full, + }, + }, +}; + +static const USBDescIface desc_iface_high = { + .bInterfaceNumber = 0, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_MASS_STORAGE, + .bInterfaceSubClass = 0x06, /* SCSI */ + .bInterfaceProtocol = 0x50, /* Bulk */ + .eps = (USBDescEndpoint[]) { + { + .bEndpointAddress = USB_DIR_IN | 0x01, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = 512, + },{ + .bEndpointAddress = USB_DIR_OUT | 0x02, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = 512, + }, + } +}; + +static const USBDescDevice desc_device_high = { + .bcdUSB = 0x0200, + .bMaxPacketSize0 = 64, + .bNumConfigurations = 1, + .confs = (USBDescConfig[]) { + { + .bNumInterfaces = 1, + .bConfigurationValue = 1, + .iConfiguration = STR_CONFIG_HIGH, + .bmAttributes = 0xc0, + .ifs = &desc_iface_high, + }, + }, +}; + +static const USBDesc desc = { + .id = { + .idVendor = 0, + .idProduct = 0, + .bcdDevice = 0, + .iManufacturer = STR_MANUFACTURER, + .iProduct = STR_PRODUCT, + .iSerialNumber = STR_SERIALNUMBER, + }, + .full = &desc_device_full, + .high = &desc_device_high, + .str = desc_strings, }; static void usb_msd_copy_data(MSDState *s) @@ -152,7 +188,7 @@ s->usb_buf += len; s->scsi_buf += len; s->data_len -= len; - if (s->scsi_len == 0) { + if (s->scsi_len == 0 || s->data_len == 0) { if (s->mode == USB_MSDM_DATAIN) { s->scsi_dev->info->read_data(s->scsi_dev, s->tag); } else if (s->mode == USB_MSDM_DATAOUT) { @@ -161,15 +197,18 @@ } } -static void usb_msd_send_status(MSDState *s) +static void usb_msd_send_status(MSDState *s, USBPacket *p) { struct usb_msd_csw csw; + int len; csw.sig = cpu_to_le32(0x53425355); csw.tag = cpu_to_le32(s->tag); csw.residue = s->residue; csw.status = s->result; - memcpy(s->usb_buf, &csw, 13); + + len = MIN(sizeof(csw), p->len); + memcpy(p->data, &csw, len); } static void usb_msd_command_complete(SCSIBus *bus, int reason, uint32_t tag, @@ -189,7 +228,7 @@ if (s->data_len == 0 && s->mode == USB_MSDM_DATAOUT) { /* A deferred packet with no write data remaining must be the status read packet. */ - usb_msd_send_status(s); + usb_msd_send_status(s, p); s->mode = USB_MSDM_CBW; } else { if (s->data_len) { @@ -235,84 +274,15 @@ int index, int length, uint8_t *data) { MSDState *s = (MSDState *)dev; - int ret = 0; + int ret; + ret = usb_desc_handle_control(dev, request, value, index, length, data); + if (ret >= 0) { + return ret; + } + + ret = 0; switch (request) { - case DeviceRequest | USB_REQ_GET_STATUS: - data[0] = (1 << USB_DEVICE_SELF_POWERED) | - (dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP); - data[1] = 0x00; - ret = 2; - break; - case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: - if (value == USB_DEVICE_REMOTE_WAKEUP) { - dev->remote_wakeup = 0; - } else { - goto fail; - } - ret = 0; - break; - case DeviceOutRequest | USB_REQ_SET_FEATURE: - if (value == USB_DEVICE_REMOTE_WAKEUP) { - dev->remote_wakeup = 1; - } else { - goto fail; - } - ret = 0; - break; - case DeviceOutRequest | USB_REQ_SET_ADDRESS: - dev->addr = value; - ret = 0; - break; - case DeviceRequest | USB_REQ_GET_DESCRIPTOR: - switch(value >> 8) { - case USB_DT_DEVICE: - memcpy(data, qemu_msd_dev_descriptor, - sizeof(qemu_msd_dev_descriptor)); - ret = sizeof(qemu_msd_dev_descriptor); - break; - case USB_DT_CONFIG: - memcpy(data, qemu_msd_config_descriptor, - sizeof(qemu_msd_config_descriptor)); - ret = sizeof(qemu_msd_config_descriptor); - break; - case USB_DT_STRING: - switch(value & 0xff) { - case 0: - /* language ids */ - data[0] = 4; - data[1] = 3; - data[2] = 0x09; - data[3] = 0x04; - ret = 4; - break; - case 1: - /* vendor description */ - ret = set_usb_string(data, "QEMU " QEMU_VERSION); - break; - case 2: - /* product description */ - ret = set_usb_string(data, "QEMU USB HARDDRIVE"); - break; - case 3: - /* serial number */ - ret = set_usb_string(data, "1"); - break; - default: - goto fail; - } - break; - default: - goto fail; - } - break; - case DeviceRequest | USB_REQ_GET_CONFIGURATION: - data[0] = 1; - ret = 1; - break; - case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: - ret = 0; - break; case DeviceRequest | USB_REQ_GET_INTERFACE: data[0] = 0; ret = 1; @@ -321,23 +291,22 @@ ret = 0; break; case EndpointOutRequest | USB_REQ_CLEAR_FEATURE: - if (value == 0 && index != 0x81) { /* clear ep halt */ - goto fail; - } + ret = 0; + break; + case InterfaceOutRequest | USB_REQ_SET_INTERFACE: ret = 0; break; /* Class specific requests. */ - case MassStorageReset: + case ClassInterfaceOutRequest | MassStorageReset: /* Reset state ready for the next CBW. */ s->mode = USB_MSDM_CBW; ret = 0; break; - case GetMaxLun: + case ClassInterfaceRequest | GetMaxLun: data[0] = 0; ret = 1; break; default: - fail: ret = USB_RET_STALL; break; } @@ -460,15 +429,13 @@ if (len < 13) goto fail; - s->usb_len = len; - s->usb_buf = data; - usb_msd_send_status(s); + usb_msd_send_status(s, p); s->mode = USB_MSDM_CBW; ret = 13; break; case USB_MSDM_DATAIN: - DPRINTF("Data in %d/%d\n", len, s->data_len); + DPRINTF("Data in %d/%d, scsi_len %d\n", len, s->data_len, s->scsi_len); if (len > s->data_len) len = s->data_len; s->usb_buf = data; @@ -522,28 +489,50 @@ static int usb_msd_initfn(USBDevice *dev) { MSDState *s = DO_UPCAST(MSDState, dev, dev); + BlockDriverState *bs = s->conf.bs; + DriveInfo *dinfo; - if (!s->dinfo || !s->dinfo->bdrv) { - qemu_error("usb-msd: drive property not set\n"); + if (!bs) { + error_report("usb-msd: drive property not set"); return -1; } - s->dev.speed = USB_SPEED_FULL; + /* + * Hack alert: this pretends to be a block device, but it's really + * a SCSI bus that can serve only a single device, which it + * creates automatically. But first it needs to detach from its + * blockdev, or else scsi_bus_legacy_add_drive() dies when it + * attaches again. + * + * The hack is probably a bad idea. + */ + bdrv_detach(bs, &s->dev.qdev); + s->conf.bs = NULL; + + dinfo = drive_get_by_blockdev(bs); + if (dinfo && dinfo->serial) { + usb_desc_set_string(dev, STR_SERIALNUMBER, dinfo->serial); + } + + usb_desc_init(dev); scsi_bus_new(&s->bus, &s->dev.qdev, 0, 1, usb_msd_command_complete); - s->scsi_dev = scsi_bus_legacy_add_drive(&s->bus, s->dinfo, 0); + s->scsi_dev = scsi_bus_legacy_add_drive(&s->bus, bs, 0, !!s->removable); + if (!s->scsi_dev) { + return -1; + } s->bus.qbus.allow_hotplug = 0; usb_msd_handle_reset(dev); - if (bdrv_key_required(s->dinfo->bdrv)) { - if (s->dev.qdev.hotplugged) { - monitor_read_bdrv_key_start(cur_mon, s->dinfo->bdrv, - usb_msd_password_cb, s); + if (bdrv_key_required(bs)) { + if (cur_mon) { + monitor_read_bdrv_key_start(cur_mon, bs, usb_msd_password_cb, s); s->dev.auto_attach = 0; } else { autostart = 0; } } + add_boot_device_path(s->conf.bootindex, &dev->qdev, "/disk@0,0"); return 0; } @@ -554,13 +543,12 @@ QemuOpts *opts; DriveInfo *dinfo; USBDevice *dev; - int fatal_error; const char *p1; char fmt[32]; /* parse -usbdevice disk: syntax into drive opts */ snprintf(id, sizeof(id), "usb%d", nr++); - opts = qemu_opts_create(&qemu_drive_opts, id, 0); + opts = qemu_opts_create(qemu_find_opts("drive"), id, 0); p1 = strchr(filename, ':'); if (p1++) { @@ -584,7 +572,7 @@ qemu_opt_set(opts, "if", "none"); /* create host drive */ - dinfo = drive_init(opts, NULL, &fatal_error); + dinfo = drive_init(opts, 0); if (!dinfo) { qemu_opts_del(opts); return NULL; @@ -595,7 +583,10 @@ if (!dev) { return NULL; } - qdev_prop_set_drive(&dev->qdev, "drive", dinfo); + if (qdev_prop_set_drive(&dev->qdev, "drive", dinfo->bdrv) < 0) { + qdev_free(&dev->qdev); + return NULL; + } if (qdev_init(&dev->qdev) < 0) return NULL; @@ -605,16 +596,20 @@ static struct USBDeviceInfo msd_info = { .product_desc = "QEMU USB MSD", .qdev.name = "usb-storage", + .qdev.fw_name = "storage", .qdev.size = sizeof(MSDState), + .usb_desc = &desc, .init = usb_msd_initfn, .handle_packet = usb_generic_handle_packet, + .handle_attach = usb_desc_attach, .handle_reset = usb_msd_handle_reset, .handle_control = usb_msd_handle_control, .handle_data = usb_msd_handle_data, .usbdevice_name = "disk", .usbdevice_init = usb_msd_init, .qdev.props = (Property[]) { - DEFINE_PROP_DRIVE("drive", MSDState, dinfo), + DEFINE_BLOCK_PROPERTIES(MSDState, conf), + DEFINE_PROP_BIT("removable", MSDState, removable, 0, false), DEFINE_PROP_END_OF_LIST(), }, }; diff -Nru qemu-kvm-0.12.5+noroms/hw/usb-musb.c qemu-kvm-0.14.1/hw/usb-musb.c --- qemu-kvm-0.12.5+noroms/hw/usb-musb.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/usb-musb.c 2011-05-11 13:29:46.000000000 +0000 @@ -24,6 +24,7 @@ #include "qemu-timer.h" #include "usb.h" #include "irq.h" +#include "hw.h" /* Common USB registers */ #define MUSB_HDRC_FADDR 0x00 /* 8-bit */ @@ -248,7 +249,23 @@ #define MGC_M_ULPI_REGCTL_COMPLETE 0x02 #define MGC_M_ULPI_REGCTL_REG 0x01 -static void musb_attach(USBPort *port, USBDevice *dev); +/* #define MUSB_DEBUG */ + +#ifdef MUSB_DEBUG +#define TRACE(fmt,...) fprintf(stderr, "%s@%d: " fmt "\n", __FUNCTION__, \ + __LINE__, ##__VA_ARGS__) +#else +#define TRACE(...) +#endif + + +static void musb_attach(USBPort *port); +static void musb_detach(USBPort *port); + +static USBPortOps musb_port_ops = { + .attach = musb_attach, + .detach = musb_detach, +}; typedef struct { uint16_t faddr[2]; @@ -263,7 +280,7 @@ uint8_t fifosize; int timeout[2]; /* Always in microframes */ - uint32_t *buf[2]; + uint8_t *buf[2]; int fifolen[2]; int fifostart[2]; int fifoaddr[2]; @@ -299,7 +316,7 @@ int setup_len; int session; - uint32_t buf[0x2000]; + uint8_t buf[0x8000]; /* Duplicating the world since 2008!... probably we should have 32 * logical, single endpoints instead. */ @@ -332,7 +349,9 @@ } usb_bus_new(&s->bus, NULL /* FIXME */); - usb_register_port(&s->bus, &s->port, s, 0, musb_attach); + usb_register_port(&s->bus, &s->port, s, 0, &musb_port_ops, + USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL); + usb_port_location(&s->port, NULL, 1); return s; } @@ -449,34 +468,20 @@ } /* Attach or detach a device on our only port. */ -static void musb_attach(USBPort *port, USBDevice *dev) +static void musb_attach(USBPort *port) { MUSBState *s = (MUSBState *) port->opaque; - USBDevice *curr; - port = &s->port; - curr = port->dev; - - if (dev) { - if (curr) { - usb_attach(port, NULL); - /* TODO: signal some interrupts */ - } - - musb_intr_set(s, musb_irq_vbus_request, 1); - - /* Send the attach message to device */ - usb_send_msg(dev, USB_MSG_ATTACH); - } else if (curr) { - /* Send the detach message */ - usb_send_msg(curr, USB_MSG_DETACH); - - musb_intr_set(s, musb_irq_disconnect, 1); - } + musb_intr_set(s, musb_irq_vbus_request, 1); + musb_session_update(s, 0, s->session); +} - port->dev = dev; +static void musb_detach(USBPort *port) +{ + MUSBState *s = (MUSBState *) port->opaque; - musb_session_update(s, !!curr, s->session); + musb_intr_set(s, musb_irq_disconnect, 1); + musb_session_update(s, 1, s->session); } static inline void musb_cb_tick0(void *opaque) @@ -774,7 +779,7 @@ MUSBEndPoint *ep = s->ep + epnum; int pid; int total, valid = 0; - + TRACE("start %d, len %d", ep->fifostart[0], ep->fifolen[0] ); ep->fifostart[0] += ep->fifolen[0]; ep->fifolen[0] = 0; @@ -789,18 +794,18 @@ } /* If the packet is not fully ready yet, wait for a next segment. */ - if (epnum && (ep->fifostart[0] << 2) < total) + if (epnum && (ep->fifostart[0]) < total) return; if (!valid) - total = ep->fifostart[0] << 2; + total = ep->fifostart[0]; pid = USB_TOKEN_OUT; if (!epnum && (ep->csr[0] & MGC_M_CSR0_H_SETUPPKT)) { pid = USB_TOKEN_SETUP; - if (total != 8) - printf("%s: illegal SETUPPKT length of %i bytes\n", - __FUNCTION__, total); + if (total != 8) { + TRACE("illegal SETUPPKT length of %i bytes", total); + } /* Controller should retry SETUP packets three times on errors * but it doesn't make sense for us to do that. */ } @@ -817,12 +822,13 @@ /* If we already have a packet, which didn't fit into the * 64 bytes of the FIFO, only move the FIFO start and return. (Obsolete) */ if (ep->packey[1].pid == USB_TOKEN_IN && ep->status[1] >= 0 && - (ep->fifostart[1] << 2) + ep->rxcount < + (ep->fifostart[1]) + ep->rxcount < ep->packey[1].len) { - ep->fifostart[1] += ep->rxcount >> 2; + TRACE("0x%08x, %d", ep->fifostart[1], ep->rxcount ); + ep->fifostart[1] += ep->rxcount; ep->fifolen[1] = 0; - ep->rxcount = MIN(ep->packey[0].len - (ep->fifostart[1] << 2), + ep->rxcount = MIN(ep->packey[0].len - (ep->fifostart[1]), ep->maxp[1]); ep->csr[1] &= ~MGC_M_RXCSR_H_REQPKT; @@ -872,6 +878,36 @@ total, musb_rx_packet_complete, 1); } +static uint8_t musb_read_fifo(MUSBEndPoint *ep) +{ + uint8_t value; + if (ep->fifolen[1] >= 64) { + /* We have a FIFO underrun */ + TRACE("EP%d FIFO is now empty, stop reading", ep->epnum); + return 0x00000000; + } + /* In DMA mode clear RXPKTRDY and set REQPKT automatically + * (if AUTOREQ is set) */ + + ep->csr[1] &= ~MGC_M_RXCSR_FIFOFULL; + value=ep->buf[1][ep->fifostart[1] + ep->fifolen[1] ++]; + TRACE("EP%d 0x%02x, %d", ep->epnum, value, ep->fifolen[1] ); + return value; +} + +static void musb_write_fifo(MUSBEndPoint *ep, uint8_t value) +{ + TRACE("EP%d = %02x", ep->epnum, value); + if (ep->fifolen[0] >= 64) { + /* We have a FIFO overrun */ + TRACE("EP%d FIFO exceeded 64 bytes, stop feeding data", ep->epnum); + return; + } + + ep->buf[0][ep->fifostart[0] + ep->fifolen[0] ++] = value; + ep->csr[0] |= MGC_M_TXCSR_FIFONOTEMPTY; +} + static void musb_ep_frame_cancel(MUSBEndPoint *ep, int dir) { if (ep->intv_timer[dir]) @@ -895,7 +931,7 @@ return s->ep[ep].hport[1]; default: - printf("%s: unknown register at %02x\n", __FUNCTION__, addr); + TRACE("unknown register 0x%02x", addr); return 0x00; }; } @@ -905,6 +941,12 @@ MUSBState *s = (MUSBState *) opaque; switch (addr) { + case MUSB_HDRC_TXFUNCADDR: + s->ep[ep].faddr[0] = value; + break; + case MUSB_HDRC_RXFUNCADDR: + s->ep[ep].faddr[1] = value; + break; case MUSB_HDRC_TXHUBADDR: s->ep[ep].haddr[0] = value; break; @@ -919,7 +961,8 @@ break; default: - printf("%s: unknown register at %02x\n", __FUNCTION__, addr); + TRACE("unknown register 0x%02x", addr); + break; }; } @@ -975,9 +1018,11 @@ return 0x00; case MUSB_HDRC_FIFOSIZE: return ep ? s->ep[ep].fifosize : s->ep[ep].config; + case MUSB_HDRC_RXCOUNT: + return s->ep[ep].rxcount; default: - printf("%s: unknown register at %02x\n", __FUNCTION__, addr); + TRACE("unknown register 0x%02x", addr); return 0x00; }; } @@ -1004,13 +1049,12 @@ case (MUSB_HDRC_FIFOSIZE & ~1): break; case MUSB_HDRC_FIFOSIZE: - printf("%s: somebody messes with fifosize (now %i bytes)\n", - __FUNCTION__, value); + TRACE("somebody messes with fifosize (now %i bytes)", value); s->ep[ep].fifosize = value; break; - default: - printf("%s: unknown register at %02x\n", __FUNCTION__, addr); + TRACE("unknown register 0x%02x", addr); + break; }; } @@ -1194,8 +1238,12 @@ ep = (addr >> 4) & 0xf; return musb_ep_readb(s, ep, addr & 0xf); + case MUSB_HDRC_FIFO ... (MUSB_HDRC_FIFO + 0x3f): + ep = ((addr - MUSB_HDRC_FIFO) >> 2) & 0xf; + return musb_read_fifo(s->ep + ep); + default: - printf("%s: unknown register at %02x\n", __FUNCTION__, (int) addr); + TRACE("unknown register 0x%02x", (int) addr); return 0x00; }; } @@ -1276,8 +1324,14 @@ musb_ep_writeb(s, ep, addr & 0xf, value); break; + case MUSB_HDRC_FIFO ... (MUSB_HDRC_FIFO + 0x3f): + ep = ((addr - MUSB_HDRC_FIFO) >> 2) & 0xf; + musb_write_fifo(s->ep + ep, value & 0xff); + break; + default: - printf("%s: unknown register at %02x\n", __FUNCTION__, (int) addr); + TRACE("unknown register 0x%02x", (int) addr); + break; }; } @@ -1326,6 +1380,10 @@ ep = (addr >> 4) & 0xf; return musb_ep_readh(s, ep, addr & 0xf); + case MUSB_HDRC_FIFO ... (MUSB_HDRC_FIFO + 0x3f): + ep = ((addr - MUSB_HDRC_FIFO) >> 2) & 0xf; + return (musb_read_fifo(s->ep + ep) | musb_read_fifo(s->ep + ep) << 8); + default: return musb_readb(s, addr) | (musb_readb(s, addr | 1) << 8); }; @@ -1353,12 +1411,12 @@ case MUSB_HDRC_TXFIFOADDR: s->ep[s->idx].fifoaddr[0] = value; s->ep[s->idx].buf[0] = - s->buf + ((value << 1) & (sizeof(s->buf) / 4 - 1)); + s->buf + ((value << 3) & 0x7ff ); break; case MUSB_HDRC_RXFIFOADDR: s->ep[s->idx].fifoaddr[1] = value; s->ep[s->idx].buf[1] = - s->buf + ((value << 1) & (sizeof(s->buf) / 4 - 1)); + s->buf + ((value << 3) & 0x7ff); break; case MUSB_HDRC_EP_IDX ... (MUSB_HDRC_EP_IDX + 0xf): @@ -1375,6 +1433,12 @@ musb_ep_writeh(s, ep, addr & 0xf, value); break; + case MUSB_HDRC_FIFO ... (MUSB_HDRC_FIFO + 0x3f): + ep = ((addr - MUSB_HDRC_FIFO) >> 2) & 0xf; + musb_write_fifo(s->ep + ep, value & 0xff); + musb_write_fifo(s->ep + ep, (value >> 8) & 0xff); + break; + default: musb_writeb(s, addr, value & 0xff); musb_writeb(s, addr | 1, value >> 8); @@ -1384,28 +1448,17 @@ static uint32_t musb_readw(void *opaque, target_phys_addr_t addr) { MUSBState *s = (MUSBState *) opaque; - MUSBEndPoint *ep; - int epnum; + int ep; switch (addr) { case MUSB_HDRC_FIFO ... (MUSB_HDRC_FIFO + 0x3f): - epnum = ((addr - MUSB_HDRC_FIFO) >> 2) & 0xf; - ep = s->ep + epnum; - - if (ep->fifolen[1] >= 16) { - /* We have a FIFO underrun */ - printf("%s: EP%i FIFO is now empty, stop reading\n", - __FUNCTION__, epnum); - return 0x00000000; - } - /* In DMA mode clear RXPKTRDY and set REQPKT automatically - * (if AUTOREQ is set) */ - - ep->csr[1] &= ~MGC_M_RXCSR_FIFOFULL; - return ep->buf[1][ep->fifostart[1] + ep->fifolen[1] ++]; - + ep = ((addr - MUSB_HDRC_FIFO) >> 2) & 0xf; + return ( musb_read_fifo(s->ep + ep) | + musb_read_fifo(s->ep + ep) << 8 | + musb_read_fifo(s->ep + ep) << 16 | + musb_read_fifo(s->ep + ep) << 24 ); default: - printf("%s: unknown register at %02x\n", __FUNCTION__, (int) addr); + TRACE("unknown register 0x%02x", (int) addr); return 0x00000000; }; } @@ -1413,28 +1466,19 @@ static void musb_writew(void *opaque, target_phys_addr_t addr, uint32_t value) { MUSBState *s = (MUSBState *) opaque; - MUSBEndPoint *ep; - int epnum; + int ep; switch (addr) { case MUSB_HDRC_FIFO ... (MUSB_HDRC_FIFO + 0x3f): - epnum = ((addr - MUSB_HDRC_FIFO) >> 2) & 0xf; - ep = s->ep + epnum; - - if (ep->fifolen[0] >= 16) { - /* We have a FIFO overrun */ - printf("%s: EP%i FIFO exceeded 64 bytes, stop feeding data\n", - __FUNCTION__, epnum); + ep = ((addr - MUSB_HDRC_FIFO) >> 2) & 0xf; + musb_write_fifo(s->ep + ep, value & 0xff); + musb_write_fifo(s->ep + ep, (value >> 8 ) & 0xff); + musb_write_fifo(s->ep + ep, (value >> 16) & 0xff); + musb_write_fifo(s->ep + ep, (value >> 24) & 0xff); break; - } - - ep->buf[0][ep->fifostart[0] + ep->fifolen[0] ++] = value; - if (epnum) - ep->csr[0] |= MGC_M_TXCSR_FIFONOTEMPTY; - break; - default: - printf("%s: unknown register at %02x\n", __FUNCTION__, (int) addr); + TRACE("unknown register 0x%02x", (int) addr); + break; }; } diff -Nru qemu-kvm-0.12.5+noroms/hw/usb-net.c qemu-kvm-0.14.1/hw/usb-net.c --- qemu-kvm-0.12.5+noroms/hw/usb-net.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/usb-net.c 2011-05-11 13:29:46.000000000 +0000 @@ -25,8 +25,10 @@ #include "qemu-common.h" #include "usb.h" +#include "usb-desc.h" #include "net.h" #include "qemu-queue.h" +#include "sysemu.h" /*#define TRAFFIC_DEBUG*/ /* Thanks to NetChip Technologies for donating this product ID. @@ -71,11 +73,6 @@ #define USB_DT_CS_INTERFACE 0x24 #define USB_DT_CS_ENDPOINT 0x25 -#define ClassInterfaceRequest \ - ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) -#define ClassInterfaceOutRequest \ - ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) - #define USB_CDC_SEND_ENCAPSULATED_COMMAND 0x00 #define USB_CDC_GET_ENCAPSULATED_RESPONSE 0x01 #define USB_CDC_REQ_SET_LINE_CODING 0x20 @@ -93,182 +90,209 @@ #define ETH_FRAME_LEN 1514 /* Max. octets in frame sans FCS */ -/* - * mostly the same descriptor as the linux gadget rndis driver - */ -static const uint8_t qemu_net_dev_descriptor[] = { - 0x12, /* u8 bLength; */ - USB_DT_DEVICE, /* u8 bDescriptorType; Device */ - 0x00, 0x02, /* u16 bcdUSB; v2.0 */ - USB_CLASS_COMM, /* u8 bDeviceClass; */ - 0x00, /* u8 bDeviceSubClass; */ - 0x00, /* u8 bDeviceProtocol; [ low/full only ] */ - 0x40, /* u8 bMaxPacketSize0 */ - RNDIS_VENDOR_NUM & 0xff, RNDIS_VENDOR_NUM >> 8, /* u16 idVendor; */ - RNDIS_PRODUCT_NUM & 0xff, RNDIS_PRODUCT_NUM >> 8, /* u16 idProduct; */ - 0x00, 0x00, /* u16 bcdDevice */ - STRING_MANUFACTURER, /* u8 iManufacturer; */ - STRING_PRODUCT, /* u8 iProduct; */ - STRING_SERIALNUMBER, /* u8 iSerialNumber; */ - 0x02, /* u8 bNumConfigurations; */ +static const USBDescStrings usb_net_stringtable = { + [STRING_MANUFACTURER] = "QEMU", + [STRING_PRODUCT] = "RNDIS/QEMU USB Network Device", + [STRING_ETHADDR] = "400102030405", + [STRING_DATA] = "QEMU USB Net Data Interface", + [STRING_CONTROL] = "QEMU USB Net Control Interface", + [STRING_RNDIS_CONTROL] = "QEMU USB Net RNDIS Control Interface", + [STRING_CDC] = "QEMU USB Net CDC", + [STRING_SUBSET] = "QEMU USB Net Subset", + [STRING_RNDIS] = "QEMU USB Net RNDIS", + [STRING_SERIALNUMBER] = "1", +}; + +static const USBDescIface desc_iface_rndis[] = { + { + /* RNDIS Control Interface */ + .bInterfaceNumber = 0, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_COMM, + .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM, + .bInterfaceProtocol = USB_CDC_ACM_PROTO_VENDOR, + .iInterface = STRING_RNDIS_CONTROL, + .ndesc = 4, + .descs = (USBDescOther[]) { + { + /* Header Descriptor */ + .data = (uint8_t[]) { + 0x05, /* u8 bLength */ + USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ + USB_CDC_HEADER_TYPE, /* u8 bDescriptorSubType */ + 0x10, 0x01, /* le16 bcdCDC */ + }, + },{ + /* Call Management Descriptor */ + .data = (uint8_t[]) { + 0x05, /* u8 bLength */ + USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ + USB_CDC_CALL_MANAGEMENT_TYPE, /* u8 bDescriptorSubType */ + 0x00, /* u8 bmCapabilities */ + 0x01, /* u8 bDataInterface */ + }, + },{ + /* ACM Descriptor */ + .data = (uint8_t[]) { + 0x04, /* u8 bLength */ + USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ + USB_CDC_ACM_TYPE, /* u8 bDescriptorSubType */ + 0x00, /* u8 bmCapabilities */ + }, + },{ + /* Union Descriptor */ + .data = (uint8_t[]) { + 0x05, /* u8 bLength */ + USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ + USB_CDC_UNION_TYPE, /* u8 bDescriptorSubType */ + 0x00, /* u8 bMasterInterface0 */ + 0x01, /* u8 bSlaveInterface0 */ + }, + }, + }, + .eps = (USBDescEndpoint[]) { + { + .bEndpointAddress = USB_DIR_IN | 0x01, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = STATUS_BYTECOUNT, + .bInterval = 1 << LOG2_STATUS_INTERVAL_MSEC, + }, + } + },{ + /* RNDIS Data Interface */ + .bInterfaceNumber = 1, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_CDC_DATA, + .iInterface = STRING_DATA, + .eps = (USBDescEndpoint[]) { + { + .bEndpointAddress = USB_DIR_IN | 0x02, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = 0x40, + },{ + .bEndpointAddress = USB_DIR_OUT | 0x02, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = 0x40, + } + } + } }; -static const uint8_t qemu_net_rndis_config_descriptor[] = { - /* Configuration Descriptor */ - 0x09, /* u8 bLength */ - USB_DT_CONFIG, /* u8 bDescriptorType */ - 0x43, 0x00, /* le16 wTotalLength */ - 0x02, /* u8 bNumInterfaces */ - DEV_RNDIS_CONFIG_VALUE, /* u8 bConfigurationValue */ - STRING_RNDIS, /* u8 iConfiguration */ - 0xc0, /* u8 bmAttributes */ - 0x32, /* u8 bMaxPower */ - /* RNDIS Control Interface */ - 0x09, /* u8 bLength */ - USB_DT_INTERFACE, /* u8 bDescriptorType */ - 0x00, /* u8 bInterfaceNumber */ - 0x00, /* u8 bAlternateSetting */ - 0x01, /* u8 bNumEndpoints */ - USB_CLASS_COMM, /* u8 bInterfaceClass */ - USB_CDC_SUBCLASS_ACM, /* u8 bInterfaceSubClass */ - USB_CDC_ACM_PROTO_VENDOR, /* u8 bInterfaceProtocol */ - STRING_RNDIS_CONTROL, /* u8 iInterface */ - /* Header Descriptor */ - 0x05, /* u8 bLength */ - USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ - USB_CDC_HEADER_TYPE, /* u8 bDescriptorSubType */ - 0x10, 0x01, /* le16 bcdCDC */ - /* Call Management Descriptor */ - 0x05, /* u8 bLength */ - USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ - USB_CDC_CALL_MANAGEMENT_TYPE, /* u8 bDescriptorSubType */ - 0x00, /* u8 bmCapabilities */ - 0x01, /* u8 bDataInterface */ - /* ACM Descriptor */ - 0x04, /* u8 bLength */ - USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ - USB_CDC_ACM_TYPE, /* u8 bDescriptorSubType */ - 0x00, /* u8 bmCapabilities */ - /* Union Descriptor */ - 0x05, /* u8 bLength */ - USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ - USB_CDC_UNION_TYPE, /* u8 bDescriptorSubType */ - 0x00, /* u8 bMasterInterface0 */ - 0x01, /* u8 bSlaveInterface0 */ - /* Status Descriptor */ - 0x07, /* u8 bLength */ - USB_DT_ENDPOINT, /* u8 bDescriptorType */ - USB_DIR_IN | 1, /* u8 bEndpointAddress */ - USB_ENDPOINT_XFER_INT, /* u8 bmAttributes */ - STATUS_BYTECOUNT & 0xff, STATUS_BYTECOUNT >> 8, /* le16 wMaxPacketSize */ - 1 << LOG2_STATUS_INTERVAL_MSEC, /* u8 bInterval */ - /* RNDIS Data Interface */ - 0x09, /* u8 bLength */ - USB_DT_INTERFACE, /* u8 bDescriptorType */ - 0x01, /* u8 bInterfaceNumber */ - 0x00, /* u8 bAlternateSetting */ - 0x02, /* u8 bNumEndpoints */ - USB_CLASS_CDC_DATA, /* u8 bInterfaceClass */ - 0x00, /* u8 bInterfaceSubClass */ - 0x00, /* u8 bInterfaceProtocol */ - STRING_DATA, /* u8 iInterface */ - /* Source Endpoint */ - 0x07, /* u8 bLength */ - USB_DT_ENDPOINT, /* u8 bDescriptorType */ - USB_DIR_IN | 2, /* u8 bEndpointAddress */ - USB_ENDPOINT_XFER_BULK, /* u8 bmAttributes */ - 0x40, 0x00, /* le16 wMaxPacketSize */ - 0x00, /* u8 bInterval */ - /* Sink Endpoint */ - 0x07, /* u8 bLength */ - USB_DT_ENDPOINT, /* u8 bDescriptorType */ - USB_DIR_OUT | 2, /* u8 bEndpointAddress */ - USB_ENDPOINT_XFER_BULK, /* u8 bmAttributes */ - 0x40, 0x00, /* le16 wMaxPacketSize */ - 0x00 /* u8 bInterval */ +static const USBDescIface desc_iface_cdc[] = { + { + /* CDC Control Interface */ + .bInterfaceNumber = 0, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_COMM, + .bInterfaceSubClass = USB_CDC_SUBCLASS_ETHERNET, + .bInterfaceProtocol = USB_CDC_PROTO_NONE, + .iInterface = STRING_CONTROL, + .ndesc = 3, + .descs = (USBDescOther[]) { + { + /* Header Descriptor */ + .data = (uint8_t[]) { + 0x05, /* u8 bLength */ + USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ + USB_CDC_HEADER_TYPE, /* u8 bDescriptorSubType */ + 0x10, 0x01, /* le16 bcdCDC */ + }, + },{ + /* Union Descriptor */ + .data = (uint8_t[]) { + 0x05, /* u8 bLength */ + USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ + USB_CDC_UNION_TYPE, /* u8 bDescriptorSubType */ + 0x00, /* u8 bMasterInterface0 */ + 0x01, /* u8 bSlaveInterface0 */ + }, + },{ + /* Ethernet Descriptor */ + .data = (uint8_t[]) { + 0x0d, /* u8 bLength */ + USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ + USB_CDC_ETHERNET_TYPE, /* u8 bDescriptorSubType */ + STRING_ETHADDR, /* u8 iMACAddress */ + 0x00, 0x00, 0x00, 0x00, /* le32 bmEthernetStatistics */ + ETH_FRAME_LEN & 0xff, + ETH_FRAME_LEN >> 8, /* le16 wMaxSegmentSize */ + 0x00, 0x00, /* le16 wNumberMCFilters */ + 0x00, /* u8 bNumberPowerFilters */ + }, + }, + }, + .eps = (USBDescEndpoint[]) { + { + .bEndpointAddress = USB_DIR_IN | 0x01, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = STATUS_BYTECOUNT, + .bInterval = 1 << LOG2_STATUS_INTERVAL_MSEC, + }, + } + },{ + /* CDC Data Interface (off) */ + .bInterfaceNumber = 1, + .bAlternateSetting = 0, + .bNumEndpoints = 0, + .bInterfaceClass = USB_CLASS_CDC_DATA, + },{ + /* CDC Data Interface */ + .bInterfaceNumber = 1, + .bAlternateSetting = 1, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_CDC_DATA, + .iInterface = STRING_DATA, + .eps = (USBDescEndpoint[]) { + { + .bEndpointAddress = USB_DIR_IN | 0x02, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = 0x40, + },{ + .bEndpointAddress = USB_DIR_OUT | 0x02, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = 0x40, + } + } + } }; -static const uint8_t qemu_net_cdc_config_descriptor[] = { - /* Configuration Descriptor */ - 0x09, /* u8 bLength */ - USB_DT_CONFIG, /* u8 bDescriptorType */ - 0x50, 0x00, /* le16 wTotalLength */ - 0x02, /* u8 bNumInterfaces */ - DEV_CONFIG_VALUE, /* u8 bConfigurationValue */ - STRING_CDC, /* u8 iConfiguration */ - 0xc0, /* u8 bmAttributes */ - 0x32, /* u8 bMaxPower */ - /* CDC Control Interface */ - 0x09, /* u8 bLength */ - USB_DT_INTERFACE, /* u8 bDescriptorType */ - 0x00, /* u8 bInterfaceNumber */ - 0x00, /* u8 bAlternateSetting */ - 0x01, /* u8 bNumEndpoints */ - USB_CLASS_COMM, /* u8 bInterfaceClass */ - USB_CDC_SUBCLASS_ETHERNET, /* u8 bInterfaceSubClass */ - USB_CDC_PROTO_NONE, /* u8 bInterfaceProtocol */ - STRING_CONTROL, /* u8 iInterface */ - /* Header Descriptor */ - 0x05, /* u8 bLength */ - USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ - USB_CDC_HEADER_TYPE, /* u8 bDescriptorSubType */ - 0x10, 0x01, /* le16 bcdCDC */ - /* Union Descriptor */ - 0x05, /* u8 bLength */ - USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ - USB_CDC_UNION_TYPE, /* u8 bDescriptorSubType */ - 0x00, /* u8 bMasterInterface0 */ - 0x01, /* u8 bSlaveInterface0 */ - /* Ethernet Descriptor */ - 0x0d, /* u8 bLength */ - USB_DT_CS_INTERFACE, /* u8 bDescriptorType */ - USB_CDC_ETHERNET_TYPE, /* u8 bDescriptorSubType */ - STRING_ETHADDR, /* u8 iMACAddress */ - 0x00, 0x00, 0x00, 0x00, /* le32 bmEthernetStatistics */ - ETH_FRAME_LEN & 0xff, ETH_FRAME_LEN >> 8, /* le16 wMaxSegmentSize */ - 0x00, 0x00, /* le16 wNumberMCFilters */ - 0x00, /* u8 bNumberPowerFilters */ - /* Status Descriptor */ - 0x07, /* u8 bLength */ - USB_DT_ENDPOINT, /* u8 bDescriptorType */ - USB_DIR_IN | 1, /* u8 bEndpointAddress */ - USB_ENDPOINT_XFER_INT, /* u8 bmAttributes */ - STATUS_BYTECOUNT & 0xff, STATUS_BYTECOUNT >> 8, /* le16 wMaxPacketSize */ - 1 << LOG2_STATUS_INTERVAL_MSEC, /* u8 bInterval */ - /* CDC Data (nop) Interface */ - 0x09, /* u8 bLength */ - USB_DT_INTERFACE, /* u8 bDescriptorType */ - 0x01, /* u8 bInterfaceNumber */ - 0x00, /* u8 bAlternateSetting */ - 0x00, /* u8 bNumEndpoints */ - USB_CLASS_CDC_DATA, /* u8 bInterfaceClass */ - 0x00, /* u8 bInterfaceSubClass */ - 0x00, /* u8 bInterfaceProtocol */ - 0x00, /* u8 iInterface */ - /* CDC Data Interface */ - 0x09, /* u8 bLength */ - USB_DT_INTERFACE, /* u8 bDescriptorType */ - 0x01, /* u8 bInterfaceNumber */ - 0x01, /* u8 bAlternateSetting */ - 0x02, /* u8 bNumEndpoints */ - USB_CLASS_CDC_DATA, /* u8 bInterfaceClass */ - 0x00, /* u8 bInterfaceSubClass */ - 0x00, /* u8 bInterfaceProtocol */ - STRING_DATA, /* u8 iInterface */ - /* Source Endpoint */ - 0x07, /* u8 bLength */ - USB_DT_ENDPOINT, /* u8 bDescriptorType */ - USB_DIR_IN | 2, /* u8 bEndpointAddress */ - USB_ENDPOINT_XFER_BULK, /* u8 bmAttributes */ - 0x40, 0x00, /* le16 wMaxPacketSize */ - 0x00, /* u8 bInterval */ - /* Sink Endpoint */ - 0x07, /* u8 bLength */ - USB_DT_ENDPOINT, /* u8 bDescriptorType */ - USB_DIR_OUT | 2, /* u8 bEndpointAddress */ - USB_ENDPOINT_XFER_BULK, /* u8 bmAttributes */ - 0x40, 0x00, /* le16 wMaxPacketSize */ - 0x00 /* u8 bInterval */ +static const USBDescDevice desc_device_net = { + .bcdUSB = 0x0200, + .bDeviceClass = USB_CLASS_COMM, + .bMaxPacketSize0 = 0x40, + .bNumConfigurations = 2, + .confs = (USBDescConfig[]) { + { + .bNumInterfaces = 2, + .bConfigurationValue = DEV_RNDIS_CONFIG_VALUE, + .iConfiguration = STRING_RNDIS, + .bmAttributes = 0xc0, + .bMaxPower = 0x32, + .nif = ARRAY_SIZE(desc_iface_rndis), + .ifs = desc_iface_rndis, + },{ + .bNumInterfaces = 2, + .bConfigurationValue = DEV_CONFIG_VALUE, + .iConfiguration = STRING_CDC, + .bmAttributes = 0xc0, + .bMaxPower = 0x32, + .nif = ARRAY_SIZE(desc_iface_cdc), + .ifs = desc_iface_cdc, + } + }, +}; + +static const USBDesc desc_net = { + .id = { + .idVendor = RNDIS_VENDOR_NUM, + .idProduct = RNDIS_PRODUCT_NUM, + .bcdDevice = 0, + .iManufacturer = STRING_MANUFACTURER, + .iProduct = STRING_PRODUCT, + .iSerialNumber = STRING_SERIALNUMBER, + }, + .full = &desc_device_net, + .str = usb_net_stringtable, }; /* @@ -603,7 +627,6 @@ typedef struct USBNetState { USBDevice dev; - unsigned int rndis; enum rndis_state rndis_state; uint32_t medium; uint32_t speed; @@ -624,6 +647,11 @@ QTAILQ_HEAD(rndis_resp_head, rndis_response) rndis_resp; } USBNetState; +static int is_rndis(USBNetState *s) +{ + return s->dev.config->bConfigurationValue == DEV_RNDIS_CONFIG_VALUE; +} + static int ndis_query(USBNetState *s, uint32_t oid, uint8_t *inbuf, unsigned int inlen, uint8_t *outbuf, size_t outlen) @@ -977,11 +1005,10 @@ static int rndis_parse(USBNetState *s, uint8_t *data, int length) { - uint32_t msg_type, msg_length; + uint32_t msg_type; le32 *tmp = (le32 *) data; - msg_type = le32_to_cpup(tmp++); - msg_length = le32_to_cpup(tmp++); + msg_type = le32_to_cpup(tmp); switch (msg_type) { case RNDIS_INITIALIZE_MSG: @@ -1015,59 +1042,23 @@ { } -static const char * const usb_net_stringtable[] = { - [STRING_MANUFACTURER] = "QEMU", - [STRING_PRODUCT] = "RNDIS/QEMU USB Network Device", - [STRING_ETHADDR] = "400102030405", - [STRING_DATA] = "QEMU USB Net Data Interface", - [STRING_CONTROL] = "QEMU USB Net Control Interface", - [STRING_RNDIS_CONTROL] = "QEMU USB Net RNDIS Control Interface", - [STRING_CDC] = "QEMU USB Net CDC", - [STRING_SUBSET] = "QEMU USB Net Subset", - [STRING_RNDIS] = "QEMU USB Net RNDIS", - [STRING_SERIALNUMBER] = "1", -}; - static int usb_net_handle_control(USBDevice *dev, int request, int value, int index, int length, uint8_t *data) { USBNetState *s = (USBNetState *) dev; - int ret = 0; - - switch(request) { - case DeviceRequest | USB_REQ_GET_STATUS: - data[0] = (1 << USB_DEVICE_SELF_POWERED) | - (dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP); - data[1] = 0x00; - ret = 2; - break; - - case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: - if (value == USB_DEVICE_REMOTE_WAKEUP) { - dev->remote_wakeup = 0; - } else { - goto fail; - } - ret = 0; - break; - - case DeviceOutRequest | USB_REQ_SET_FEATURE: - if (value == USB_DEVICE_REMOTE_WAKEUP) { - dev->remote_wakeup = 1; - } else { - goto fail; - } - ret = 0; - break; + int ret; - case DeviceOutRequest | USB_REQ_SET_ADDRESS: - dev->addr = value; - ret = 0; - break; + ret = usb_desc_handle_control(dev, request, value, index, length, data); + if (ret >= 0) { + return ret; + } + ret = 0; + switch(request) { case ClassInterfaceOutRequest | USB_CDC_SEND_ENCAPSULATED_COMMAND: - if (!s->rndis || value || index != 0) + if (!is_rndis(s) || value || index != 0) { goto fail; + } #ifdef TRAFFIC_DEBUG { unsigned int i; @@ -1084,8 +1075,9 @@ break; case ClassInterfaceRequest | USB_CDC_GET_ENCAPSULATED_RESPONSE: - if (!s->rndis || value || index != 0) + if (!is_rndis(s) || value || index != 0) { goto fail; + } ret = rndis_get_response(s, data); if (!ret) { data[0] = 0; @@ -1105,85 +1097,6 @@ #endif break; - case DeviceRequest | USB_REQ_GET_DESCRIPTOR: - switch(value >> 8) { - case USB_DT_DEVICE: - ret = sizeof(qemu_net_dev_descriptor); - memcpy(data, qemu_net_dev_descriptor, ret); - break; - - case USB_DT_CONFIG: - switch (value & 0xff) { - case 0: - ret = sizeof(qemu_net_rndis_config_descriptor); - memcpy(data, qemu_net_rndis_config_descriptor, ret); - break; - - case 1: - ret = sizeof(qemu_net_cdc_config_descriptor); - memcpy(data, qemu_net_cdc_config_descriptor, ret); - break; - - default: - goto fail; - } - - data[2] = ret & 0xff; - data[3] = ret >> 8; - break; - - case USB_DT_STRING: - switch (value & 0xff) { - case 0: - /* language ids */ - data[0] = 4; - data[1] = 3; - data[2] = 0x09; - data[3] = 0x04; - ret = 4; - break; - - case STRING_ETHADDR: - ret = set_usb_string(data, s->usbstring_mac); - break; - - default: - if (usb_net_stringtable[value & 0xff]) { - ret = set_usb_string(data, - usb_net_stringtable[value & 0xff]); - break; - } - - goto fail; - } - break; - - default: - goto fail; - } - break; - - case DeviceRequest | USB_REQ_GET_CONFIGURATION: - data[0] = s->rndis ? DEV_RNDIS_CONFIG_VALUE : DEV_CONFIG_VALUE; - ret = 1; - break; - - case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: - switch (value & 0xff) { - case DEV_CONFIG_VALUE: - s->rndis = 0; - break; - - case DEV_RNDIS_CONFIG_VALUE: - s->rndis = 1; - break; - - default: - goto fail; - } - ret = 0; - break; - case DeviceRequest | USB_REQ_GET_INTERFACE: case InterfaceRequest | USB_REQ_GET_INTERFACE: data[0] = 0; @@ -1254,7 +1167,7 @@ memcpy(p->data, &s->in_buf[s->in_ptr], ret); s->in_ptr += ret; if (s->in_ptr >= s->in_len && - (s->rndis || (s->in_len & (64 - 1)) || !ret)) { + (is_rndis(s) || (s->in_len & (64 - 1)) || !ret)) { /* no short packet necessary */ s->in_ptr = s->in_len = 0; } @@ -1303,7 +1216,7 @@ memcpy(&s->out_buf[s->out_ptr], p->data, sz); s->out_ptr += sz; - if (!s->rndis) { + if (!is_rndis(s)) { if (ret < 64) { qemu_send_packet(&s->nic->nc, s->out_buf, s->out_ptr); s->out_ptr = 0; @@ -1374,7 +1287,7 @@ USBNetState *s = DO_UPCAST(NICState, nc, nc)->opaque; struct rndis_packet_msg_type *msg; - if (s->rndis) { + if (is_rndis(s)) { msg = (struct rndis_packet_msg_type *) s->in_buf; if (!s->rndis_state == RNDIS_DATA_INITIALIZED) return -1; @@ -1410,8 +1323,9 @@ { USBNetState *s = DO_UPCAST(NICState, nc, nc)->opaque; - if (s->rndis && !s->rndis_state == RNDIS_DATA_INITIALIZED) + if (is_rndis(s) && !s->rndis_state == RNDIS_DATA_INITIALIZED) { return 1; + } return !s->in_len; } @@ -1444,9 +1358,8 @@ { USBNetState *s = DO_UPCAST(USBNetState, dev, dev); - s->dev.speed = USB_SPEED_FULL; + usb_desc_init(dev); - s->rndis = 1; s->rndis_state = RNDIS_UNINITIALIZED; QTAILQ_INIT(&s->rndis_resp); @@ -1468,7 +1381,9 @@ s->conf.macaddr.a[3], s->conf.macaddr.a[4], s->conf.macaddr.a[5]); + usb_desc_set_string(dev, STRING_ETHADDR, s->usbstring_mac); + add_boot_device_path(s->conf.bootindex, &dev->qdev, "/ethernet@0"); return 0; } @@ -1478,7 +1393,7 @@ QemuOpts *opts; int idx; - opts = qemu_opts_parse(&qemu_net_opts, cmdline, NULL); + opts = qemu_opts_parse(qemu_find_opts("net"), cmdline, 0); if (!opts) { return NULL; } @@ -1502,7 +1417,9 @@ static struct USBDeviceInfo net_info = { .product_desc = "QEMU USB Network Interface", .qdev.name = "usb-net", + .qdev.fw_name = "network", .qdev.size = sizeof(USBNetState), + .usb_desc = &desc_net, .init = usb_net_initfn, .handle_packet = usb_generic_handle_packet, .handle_reset = usb_net_handle_reset, diff -Nru qemu-kvm-0.12.5+noroms/hw/usb-ohci.c qemu-kvm-0.14.1/hw/usb-ohci.c --- qemu-kvm-0.12.5+noroms/hw/usb-ohci.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/usb-ohci.c 2011-05-11 13:29:46.000000000 +0000 @@ -30,9 +30,9 @@ #include "qemu-timer.h" #include "usb.h" #include "pci.h" -#include "pxa.h" -#include "devices.h" #include "usb-ohci.h" +#include "sysbus.h" +#include "qdev-addr.h" //#define DEBUG_OHCI /* Dump packet contents. */ @@ -42,9 +42,9 @@ //#define OHCI_TIME_WARP 1 #ifdef DEBUG_OHCI -#define dprintf printf +#define DPRINTF printf #else -#define dprintf(...) +#define DPRINTF(...) #endif /* Number of Downstream Ports on the root hub. */ @@ -59,16 +59,9 @@ uint32_t ctrl; } OHCIPort; -enum ohci_type { - OHCI_TYPE_PCI, - OHCI_TYPE_PXA, - OHCI_TYPE_SM501, -}; - typedef struct { USBBus bus; qemu_irq irq; - enum ohci_type type; int mem; int num_ports; const char *name; @@ -329,52 +322,46 @@ } /* Attach or detach a device on a root hub port. */ -static void ohci_attach(USBPort *port1, USBDevice *dev) +static void ohci_attach(USBPort *port1) { OHCIState *s = port1->opaque; OHCIPort *port = &s->rhport[port1->index]; - uint32_t old_state = port->ctrl; - if (dev) { - if (port->port.dev) { - usb_attach(port1, NULL); - } - /* set connect status */ - port->ctrl |= OHCI_PORT_CCS | OHCI_PORT_CSC; + /* set connect status */ + port->ctrl |= OHCI_PORT_CCS | OHCI_PORT_CSC; - /* update speed */ - if (dev->speed == USB_SPEED_LOW) - port->ctrl |= OHCI_PORT_LSDA; - else - port->ctrl &= ~OHCI_PORT_LSDA; - port->port.dev = dev; - - /* notify of remote-wakeup */ - if ((s->ctl & OHCI_CTL_HCFS) == OHCI_USB_SUSPEND) - ohci_set_interrupt(s, OHCI_INTR_RD); - - /* send the attach message */ - usb_send_msg(dev, USB_MSG_ATTACH); - dprintf("usb-ohci: Attached port %d\n", port1->index); + /* update speed */ + if (port->port.dev->speed == USB_SPEED_LOW) { + port->ctrl |= OHCI_PORT_LSDA; } else { - /* set connect status */ - if (port->ctrl & OHCI_PORT_CCS) { - port->ctrl &= ~OHCI_PORT_CCS; - port->ctrl |= OHCI_PORT_CSC; - } - /* disable port */ - if (port->ctrl & OHCI_PORT_PES) { - port->ctrl &= ~OHCI_PORT_PES; - port->ctrl |= OHCI_PORT_PESC; - } - dev = port->port.dev; - if (dev) { - /* send the detach message */ - usb_send_msg(dev, USB_MSG_DETACH); - } - port->port.dev = NULL; - dprintf("usb-ohci: Detached port %d\n", port1->index); + port->ctrl &= ~OHCI_PORT_LSDA; + } + + /* notify of remote-wakeup */ + if ((s->ctl & OHCI_CTL_HCFS) == OHCI_USB_SUSPEND) { + ohci_set_interrupt(s, OHCI_INTR_RD); + } + + DPRINTF("usb-ohci: Attached port %d\n", port1->index); +} + +static void ohci_detach(USBPort *port1) +{ + OHCIState *s = port1->opaque; + OHCIPort *port = &s->rhport[port1->index]; + uint32_t old_state = port->ctrl; + + /* set connect status */ + if (port->ctrl & OHCI_PORT_CCS) { + port->ctrl &= ~OHCI_PORT_CCS; + port->ctrl |= OHCI_PORT_CSC; } + /* disable port */ + if (port->ctrl & OHCI_PORT_PES) { + port->ctrl &= ~OHCI_PORT_PES; + port->ctrl |= OHCI_PORT_PESC; + } + DPRINTF("usb-ohci: Detached port %d\n", port1->index); if (old_state != port->ctrl) ohci_set_interrupt(s, OHCI_INTR_RHSC); @@ -420,14 +407,15 @@ { port = &ohci->rhport[i]; port->ctrl = 0; - if (port->port.dev) - ohci_attach(&port->port, port->port.dev); + if (port->port.dev) { + usb_attach(&port->port, port->port.dev); + } } if (ohci->async_td) { usb_cancel_packet(&ohci->usb_packet); ohci->async_td = 0; } - dprintf("usb-ohci: Reset %s\n", ohci->name); + DPRINTF("usb-ohci: Reset %s\n", ohci->name); } /* Get an array of dwords from main memory */ @@ -593,7 +581,7 @@ { OHCIState *ohci = opaque; #ifdef DEBUG_PACKET - dprintf("Async packet complete\n"); + DPRINTF("Async packet complete\n"); #endif ohci->async_complete = 1; ohci_process_lists(ohci, 1); @@ -606,7 +594,9 @@ { int dir; size_t len = 0; +#ifdef DEBUG_ISOCH const char *str = NULL; +#endif int pid; int ret; int i; @@ -648,12 +638,12 @@ #endif if (relative_frame_number < 0) { - dprintf("usb-ohci: ISO_TD R=%d < 0\n", relative_frame_number); + DPRINTF("usb-ohci: ISO_TD R=%d < 0\n", relative_frame_number); return 1; } else if (relative_frame_number > frame_count) { /* ISO TD expired - retire the TD to the Done Queue and continue with the next ISO TD of the same ED */ - dprintf("usb-ohci: ISO_TD R=%d > FC=%d\n", relative_frame_number, + DPRINTF("usb-ohci: ISO_TD R=%d > FC=%d\n", relative_frame_number, frame_count); OHCI_SET_BM(iso_td.flags, TD_CC, OHCI_CC_DATAOVERRUN); ed->head &= ~OHCI_DPTR_MASK; @@ -670,15 +660,21 @@ dir = OHCI_BM(ed->flags, ED_D); switch (dir) { case OHCI_TD_DIR_IN: +#ifdef DEBUG_ISOCH str = "in"; +#endif pid = USB_TOKEN_IN; break; case OHCI_TD_DIR_OUT: +#ifdef DEBUG_ISOCH str = "out"; +#endif pid = USB_TOKEN_OUT; break; case OHCI_TD_DIR_SETUP: +#ifdef DEBUG_ISOCH str = "setup"; +#endif pid = USB_TOKEN_SETUP; break; default: @@ -841,7 +837,9 @@ { int dir; size_t len = 0; +#ifdef DEBUG_PACKET const char *str = NULL; +#endif int pid; int ret; int i; @@ -856,7 +854,7 @@ completion = (addr == ohci->async_td); if (completion && !ohci->async_complete) { #ifdef DEBUG_PACKET - dprintf("Skipping async TD\n"); + DPRINTF("Skipping async TD\n"); #endif return 1; } @@ -878,15 +876,21 @@ switch (dir) { case OHCI_TD_DIR_IN: +#ifdef DEBUG_PACKET str = "in"; +#endif pid = USB_TOKEN_IN; break; case OHCI_TD_DIR_OUT: +#ifdef DEBUG_PACKET str = "out"; +#endif pid = USB_TOKEN_OUT; break; case OHCI_TD_DIR_SETUP: +#ifdef DEBUG_PACKET str = "setup"; +#endif pid = USB_TOKEN_SETUP; break; default: @@ -907,14 +911,14 @@ flag_r = (td.flags & OHCI_TD_R) != 0; #ifdef DEBUG_PACKET - dprintf(" TD @ 0x%.8x %" PRId64 " bytes %s r=%d cbp=0x%.8x be=0x%.8x\n", - addr, len, str, flag_r, td.cbp, td.be); + DPRINTF(" TD @ 0x%.8x %" PRId64 " bytes %s r=%d cbp=0x%.8x be=0x%.8x\n", + addr, (int64_t)len, str, flag_r, td.cbp, td.be); if (len > 0 && dir != OHCI_TD_DIR_IN) { - dprintf(" data:"); + DPRINTF(" data:"); for (i = 0; i < len; i++) printf(" %.2x", ohci->usb_buf[i]); - dprintf("\n"); + DPRINTF("\n"); } #endif if (completion) { @@ -935,7 +939,7 @@ timely manner. */ #ifdef DEBUG_PACKET - dprintf("Too many pending packets\n"); + DPRINTF("Too many pending packets\n"); #endif return 1; } @@ -951,7 +955,7 @@ break; } #ifdef DEBUG_PACKET - dprintf("ret=%d\n", ret); + DPRINTF("ret=%d\n", ret); #endif if (ret == USB_RET_ASYNC) { ohci->async_td = addr; @@ -962,10 +966,10 @@ if (dir == OHCI_TD_DIR_IN) { ohci_copy_td(ohci, &td, ohci->usb_buf, ret, 1); #ifdef DEBUG_PACKET - dprintf(" data:"); + DPRINTF(" data:"); for (i = 0; i < ret; i++) printf(" %.2x", ohci->usb_buf[i]); - dprintf("\n"); + DPRINTF("\n"); #endif } else { ret = len; @@ -994,21 +998,21 @@ ed->head |= OHCI_ED_C; } else { if (ret >= 0) { - dprintf("usb-ohci: Underrun\n"); + DPRINTF("usb-ohci: Underrun\n"); OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_DATAUNDERRUN); } else { switch (ret) { case USB_RET_NODEV: OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_DEVICENOTRESPONDING); case USB_RET_NAK: - dprintf("usb-ohci: got NAK\n"); + DPRINTF("usb-ohci: got NAK\n"); return 1; case USB_RET_STALL: - dprintf("usb-ohci: got STALL\n"); + DPRINTF("usb-ohci: got STALL\n"); OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_STALL); break; case USB_RET_BABBLE: - dprintf("usb-ohci: got BABBLE\n"); + DPRINTF("usb-ohci: got BABBLE\n"); OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_DATAOVERRUN); break; default: @@ -1067,7 +1071,7 @@ while ((ed.head & OHCI_DPTR_MASK) != ed.tail) { #ifdef DEBUG_PACKET - dprintf("ED @ 0x%.8x fa=%u en=%u d=%u s=%u k=%u f=%u mps=%u " + DPRINTF("ED @ 0x%.8x fa=%u en=%u d=%u s=%u k=%u f=%u mps=%u " "h=%u c=%u\n head=0x%.8x tailp=0x%.8x next=0x%.8x\n", cur, OHCI_BM(ed.flags, ED_FA), OHCI_BM(ed.flags, ED_EN), OHCI_BM(ed.flags, ED_D), (ed.flags & OHCI_ED_S)!= 0, @@ -1106,9 +1110,10 @@ static void ohci_process_lists(OHCIState *ohci, int completion) { if ((ohci->ctl & OHCI_CTL_CLE) && (ohci->status & OHCI_STATUS_CLF)) { - if (ohci->ctrl_cur && ohci->ctrl_cur != ohci->ctrl_head) - dprintf("usb-ohci: head %x, cur %x\n", - ohci->ctrl_head, ohci->ctrl_cur); + if (ohci->ctrl_cur && ohci->ctrl_cur != ohci->ctrl_head) { + DPRINTF("usb-ohci: head %x, cur %x\n", + ohci->ctrl_head, ohci->ctrl_cur); + } if (!ohci_service_ed_list(ohci, ohci->ctrl_head, completion)) { ohci->ctrl_cur = 0; ohci->status &= ~OHCI_STATUS_CLF; @@ -1191,7 +1196,7 @@ return 0; } - dprintf("usb-ohci: %s: USB Operational\n", ohci->name); + DPRINTF("usb-ohci: %s: USB Operational\n", ohci->name); ohci_sof(ohci); @@ -1244,7 +1249,7 @@ val &= OHCI_FMI_FI; if (val != ohci->fi) { - dprintf("usb-ohci: %s: FrameInterval = 0x%x (%u)\n", + DPRINTF("usb-ohci: %s: FrameInterval = 0x%x (%u)\n", ohci->name, ohci->fi, ohci->fi); } @@ -1283,14 +1288,14 @@ break; case OHCI_USB_SUSPEND: ohci_bus_stop(ohci); - dprintf("usb-ohci: %s: USB Suspended\n", ohci->name); + DPRINTF("usb-ohci: %s: USB Suspended\n", ohci->name); break; case OHCI_USB_RESUME: - dprintf("usb-ohci: %s: USB Resume\n", ohci->name); + DPRINTF("usb-ohci: %s: USB Resume\n", ohci->name); break; case OHCI_USB_RESET: ohci_reset(ohci); - dprintf("usb-ohci: %s: USB Reset\n", ohci->name); + DPRINTF("usb-ohci: %s: USB Reset\n", ohci->name); break; } } @@ -1335,7 +1340,7 @@ for (i = 0; i < ohci->num_ports; i++) ohci_port_power(ohci, i, 0); - dprintf("usb-ohci: powered down all ports\n"); + DPRINTF("usb-ohci: powered down all ports\n"); } if (val & OHCI_RHS_LPSC) { @@ -1343,7 +1348,7 @@ for (i = 0; i < ohci->num_ports; i++) ohci_port_power(ohci, i, 1); - dprintf("usb-ohci: powered up all ports\n"); + DPRINTF("usb-ohci: powered up all ports\n"); } if (val & OHCI_RHS_DRWE) @@ -1374,11 +1379,12 @@ ohci_port_set_if_connected(ohci, portnum, val & OHCI_PORT_PES); - if (ohci_port_set_if_connected(ohci, portnum, val & OHCI_PORT_PSS)) - dprintf("usb-ohci: port %d: SUSPEND\n", portnum); + if (ohci_port_set_if_connected(ohci, portnum, val & OHCI_PORT_PSS)) { + DPRINTF("usb-ohci: port %d: SUSPEND\n", portnum); + } if (ohci_port_set_if_connected(ohci, portnum, val & OHCI_PORT_PRS)) { - dprintf("usb-ohci: port %d: RESET\n", portnum); + DPRINTF("usb-ohci: port %d: RESET\n", portnum); usb_send_msg(port->port.dev, USB_MSG_RESET); port->ctrl &= ~OHCI_PORT_PRS; /* ??? Should this also set OHCI_PORT_PESC. */ @@ -1404,6 +1410,8 @@ OHCIState *ohci = ptr; uint32_t retval; + addr &= 0xff; + /* Only aligned reads are allowed on OHCI */ if (addr & 3) { fprintf(stderr, "usb-ohci: Mis-aligned read\n"); @@ -1517,9 +1525,6 @@ } } -#ifdef TARGET_WORDS_BIGENDIAN - retval = bswap32(retval); -#endif return retval; } @@ -1527,9 +1532,7 @@ { OHCIState *ohci = ptr; -#ifdef TARGET_WORDS_BIGENDIAN - val = bswap32(val); -#endif + addr &= 0xff; /* Only aligned reads are allowed on OHCI */ if (addr & 3) { @@ -1661,10 +1664,13 @@ ohci_mem_write }; +static USBPortOps ohci_port_ops = { + .attach = ohci_attach, + .detach = ohci_detach, +}; + static void usb_ohci_init(OHCIState *ohci, DeviceState *dev, - int num_ports, int devfn, - qemu_irq irq, enum ohci_type type, - const char *name, uint32_t localmem_base) + int num_ports, uint32_t localmem_base) { int i; @@ -1680,21 +1686,22 @@ usb_bit_time = 1; } #endif - dprintf("usb-ohci: usb_bit_time=%" PRId64 " usb_frame_time=%" PRId64 "\n", + DPRINTF("usb-ohci: usb_bit_time=%" PRId64 " usb_frame_time=%" PRId64 "\n", usb_frame_time, usb_bit_time); } - ohci->mem = cpu_register_io_memory(ohci_readfn, ohci_writefn, ohci); + ohci->mem = cpu_register_io_memory(ohci_readfn, ohci_writefn, ohci, + DEVICE_LITTLE_ENDIAN); ohci->localmem_base = localmem_base; - ohci->name = name; - ohci->irq = irq; - ohci->type = type; + ohci->name = dev->info->name; usb_bus_new(&ohci->bus, dev); ohci->num_ports = num_ports; for (i = 0; i < num_ports; i++) { - usb_register_port(&ohci->bus, &ohci->rhport[i].port, ohci, i, ohci_attach); + usb_register_port(&ohci->bus, &ohci->rhport[i].port, ohci, i, &ohci_port_ops, + USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL); + usb_port_location(&ohci->rhport[i].port, NULL, i+1); } ohci->async_td = 0; @@ -1721,15 +1728,16 @@ pci_config_set_vendor_id(ohci->pci_dev.config, PCI_VENDOR_ID_APPLE); pci_config_set_device_id(ohci->pci_dev.config, PCI_DEVICE_ID_APPLE_IPID_USB); - ohci->pci_dev.config[0x09] = 0x10; /* OHCI */ + ohci->pci_dev.config[PCI_CLASS_PROG] = 0x10; /* OHCI */ pci_config_set_class(ohci->pci_dev.config, PCI_CLASS_SERIAL_USB); - ohci->pci_dev.config[0x3d] = 0x01; /* interrupt pin 1 */ + /* TODO: RST# value should be 0. */ + ohci->pci_dev.config[PCI_INTERRUPT_PIN] = 0x01; /* interrupt pin 1 */ - usb_ohci_init(&ohci->state, &dev->qdev, num_ports, - ohci->pci_dev.devfn, ohci->pci_dev.irq[0], - OHCI_TYPE_PCI, ohci->pci_dev.name, 0); + usb_ohci_init(&ohci->state, &dev->qdev, num_ports, 0); + ohci->state.irq = ohci->pci_dev.irq[0]; - pci_register_bar((struct PCIDevice *)ohci, 0, 256, + /* TODO: avoid cast below by using dev */ + pci_register_bar(&ohci->pci_dev, 0, 256, PCI_BASE_ADDRESS_SPACE_MEMORY, ohci_mapfunc); return 0; } @@ -1739,37 +1747,46 @@ pci_create_simple(bus, devfn, "pci-ohci"); } -void usb_ohci_init_pxa(target_phys_addr_t base, int num_ports, int devfn, - qemu_irq irq) -{ - OHCIState *ohci = (OHCIState *)qemu_mallocz(sizeof(OHCIState)); - - usb_ohci_init(ohci, NULL /* FIXME */, num_ports, devfn, irq, - OHCI_TYPE_PXA, "OHCI USB", 0); - - cpu_register_physical_memory(base, 0x1000, ohci->mem); -} - -void usb_ohci_init_sm501(uint32_t mmio_base, uint32_t localmem_base, - int num_ports, int devfn, qemu_irq irq) -{ - OHCIState *ohci = (OHCIState *)qemu_mallocz(sizeof(OHCIState)); - - usb_ohci_init(ohci, NULL /* FIXME */, num_ports, devfn, irq, - OHCI_TYPE_SM501, "OHCI USB", localmem_base); +typedef struct { + SysBusDevice busdev; + OHCIState ohci; + uint32_t num_ports; + target_phys_addr_t dma_offset; +} OHCISysBusState; + +static int ohci_init_pxa(SysBusDevice *dev) +{ + OHCISysBusState *s = FROM_SYSBUS(OHCISysBusState, dev); + + usb_ohci_init(&s->ohci, &dev->qdev, s->num_ports, s->dma_offset); + sysbus_init_irq(dev, &s->ohci.irq); + sysbus_init_mmio(dev, 0x1000, s->ohci.mem); - cpu_register_physical_memory(mmio_base, 0x1000, ohci->mem); + return 0; } -static PCIDeviceInfo ohci_info = { +static PCIDeviceInfo ohci_pci_info = { .qdev.name = "pci-ohci", .qdev.desc = "Apple USB Controller", .qdev.size = sizeof(OHCIPCIState), .init = usb_ohci_initfn_pci, }; +static SysBusDeviceInfo ohci_sysbus_info = { + .init = ohci_init_pxa, + .qdev.name = "sysbus-ohci", + .qdev.desc = "OHCI USB Controller", + .qdev.size = sizeof(OHCISysBusState), + .qdev.props = (Property[]) { + DEFINE_PROP_UINT32("num-ports", OHCISysBusState, num_ports, 3), + DEFINE_PROP_TADDR("dma-offset", OHCISysBusState, dma_offset, 3), + DEFINE_PROP_END_OF_LIST(), + } +}; + static void ohci_register(void) { - pci_qdev_register(&ohci_info); + pci_qdev_register(&ohci_pci_info); + sysbus_register_withprop(&ohci_sysbus_info); } device_init(ohci_register); diff -Nru qemu-kvm-0.12.5+noroms/hw/usb-serial.c qemu-kvm-0.14.1/hw/usb-serial.c --- qemu-kvm-0.12.5+noroms/hw/usb-serial.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/usb-serial.c 2011-05-11 13:29:46.000000000 +0000 @@ -9,7 +9,9 @@ */ #include "qemu-common.h" +#include "qemu-error.h" #include "usb.h" +#include "usb-desc.h" #include "qemu-char.h" //#define DEBUG_Serial @@ -90,8 +92,6 @@ typedef struct { USBDevice dev; - uint32_t vendorid; - uint32_t productid; uint8_t recv_buf[RECV_BUF]; uint16_t recv_ptr; uint16_t recv_used; @@ -103,69 +103,78 @@ CharDriverState *cs; } USBSerialState; -static const uint8_t qemu_serial_dev_descriptor[] = { - 0x12, /* u8 bLength; */ - 0x01, /* u8 bDescriptorType; Device */ - 0x00, 0x02, /* u16 bcdUSB; v2.0 */ - - 0x00, /* u8 bDeviceClass; */ - 0x00, /* u8 bDeviceSubClass; */ - 0x00, /* u8 bDeviceProtocol; [ low/full speeds only ] */ - 0x08, /* u8 bMaxPacketSize0; 8 Bytes */ - - /* Vendor and product id are arbitrary. */ - 0x03, 0x04, /* u16 idVendor; */ - 0x00, 0xFF, /* u16 idProduct; */ - 0x00, 0x04, /* u16 bcdDevice */ - - 0x01, /* u8 iManufacturer; */ - 0x02, /* u8 iProduct; */ - 0x03, /* u8 iSerialNumber; */ - 0x01 /* u8 bNumConfigurations; */ +enum { + STR_MANUFACTURER = 1, + STR_PRODUCT_SERIAL, + STR_PRODUCT_BRAILLE, + STR_SERIALNUMBER, }; -static const uint8_t qemu_serial_config_descriptor[] = { +static const USBDescStrings desc_strings = { + [STR_MANUFACTURER] = "QEMU " QEMU_VERSION, + [STR_PRODUCT_SERIAL] = "QEMU USB SERIAL", + [STR_PRODUCT_BRAILLE] = "QEMU USB BRAILLE", + [STR_SERIALNUMBER] = "1", +}; - /* one configuration */ - 0x09, /* u8 bLength; */ - 0x02, /* u8 bDescriptorType; Configuration */ - 0x20, 0x00, /* u16 wTotalLength; */ - 0x01, /* u8 bNumInterfaces; (1) */ - 0x01, /* u8 bConfigurationValue; */ - 0x00, /* u8 iConfiguration; */ - 0x80, /* u8 bmAttributes; - Bit 7: must be set, - 6: Self-powered, - 5: Remote wakeup, - 4..0: resvd */ - 100/2, /* u8 MaxPower; */ - - /* one interface */ - 0x09, /* u8 if_bLength; */ - 0x04, /* u8 if_bDescriptorType; Interface */ - 0x00, /* u8 if_bInterfaceNumber; */ - 0x00, /* u8 if_bAlternateSetting; */ - 0x02, /* u8 if_bNumEndpoints; */ - 0xff, /* u8 if_bInterfaceClass; Vendor Specific */ - 0xff, /* u8 if_bInterfaceSubClass; Vendor Specific */ - 0xff, /* u8 if_bInterfaceProtocol; Vendor Specific */ - 0x02, /* u8 if_iInterface; */ - - /* Bulk-In endpoint */ - 0x07, /* u8 ep_bLength; */ - 0x05, /* u8 ep_bDescriptorType; Endpoint */ - 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */ - 0x02, /* u8 ep_bmAttributes; Bulk */ - 0x40, 0x00, /* u16 ep_wMaxPacketSize; */ - 0x00, /* u8 ep_bInterval; */ - - /* Bulk-Out endpoint */ - 0x07, /* u8 ep_bLength; */ - 0x05, /* u8 ep_bDescriptorType; Endpoint */ - 0x02, /* u8 ep_bEndpointAddress; OUT Endpoint 2 */ - 0x02, /* u8 ep_bmAttributes; Bulk */ - 0x40, 0x00, /* u16 ep_wMaxPacketSize; */ - 0x00 /* u8 ep_bInterval; */ +static const USBDescIface desc_iface0 = { + .bInterfaceNumber = 0, + .bNumEndpoints = 2, + .bInterfaceClass = 0xff, + .bInterfaceSubClass = 0xff, + .bInterfaceProtocol = 0xff, + .eps = (USBDescEndpoint[]) { + { + .bEndpointAddress = USB_DIR_IN | 0x01, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = 64, + },{ + .bEndpointAddress = USB_DIR_OUT | 0x02, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = 64, + }, + } +}; + +static const USBDescDevice desc_device = { + .bcdUSB = 0x0200, + .bMaxPacketSize0 = 8, + .bNumConfigurations = 1, + .confs = (USBDescConfig[]) { + { + .bNumInterfaces = 1, + .bConfigurationValue = 1, + .bmAttributes = 0x80, + .bMaxPower = 50, + .ifs = &desc_iface0, + }, + }, +}; + +static const USBDesc desc_serial = { + .id = { + .idVendor = 0x0403, + .idProduct = 0x6001, + .bcdDevice = 0x0400, + .iManufacturer = STR_MANUFACTURER, + .iProduct = STR_PRODUCT_SERIAL, + .iSerialNumber = STR_SERIALNUMBER, + }, + .full = &desc_device, + .str = desc_strings, +}; + +static const USBDesc desc_braille = { + .id = { + .idVendor = 0x0403, + .idProduct = 0xfe72, + .bcdDevice = 0x0400, + .iManufacturer = STR_MANUFACTURER, + .iProduct = STR_PRODUCT_BRAILLE, + .iSerialNumber = STR_SERIALNUMBER, + }, + .full = &desc_device, + .str = desc_strings, }; static void usb_serial_reset(USBSerialState *s) @@ -213,89 +222,16 @@ int index, int length, uint8_t *data) { USBSerialState *s = (USBSerialState *)dev; - int ret = 0; + int ret; - //DPRINTF("got control %x, value %x\n",request, value); + DPRINTF("got control %x, value %x\n",request, value); + ret = usb_desc_handle_control(dev, request, value, index, length, data); + if (ret >= 0) { + return ret; + } + + ret = 0; switch (request) { - case DeviceRequest | USB_REQ_GET_STATUS: - data[0] = (0 << USB_DEVICE_SELF_POWERED) | - (dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP); - data[1] = 0x00; - ret = 2; - break; - case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: - if (value == USB_DEVICE_REMOTE_WAKEUP) { - dev->remote_wakeup = 0; - } else { - goto fail; - } - ret = 0; - break; - case DeviceOutRequest | USB_REQ_SET_FEATURE: - if (value == USB_DEVICE_REMOTE_WAKEUP) { - dev->remote_wakeup = 1; - } else { - goto fail; - } - ret = 0; - break; - case DeviceOutRequest | USB_REQ_SET_ADDRESS: - dev->addr = value; - ret = 0; - break; - case DeviceRequest | USB_REQ_GET_DESCRIPTOR: - switch(value >> 8) { - case USB_DT_DEVICE: - memcpy(data, qemu_serial_dev_descriptor, - sizeof(qemu_serial_dev_descriptor)); - data[8] = s->vendorid & 0xff; - data[9] = ((s->vendorid) >> 8) & 0xff; - data[10] = s->productid & 0xff; - data[11] = ((s->productid) >> 8) & 0xff; - ret = sizeof(qemu_serial_dev_descriptor); - break; - case USB_DT_CONFIG: - memcpy(data, qemu_serial_config_descriptor, - sizeof(qemu_serial_config_descriptor)); - ret = sizeof(qemu_serial_config_descriptor); - break; - case USB_DT_STRING: - switch(value & 0xff) { - case 0: - /* language ids */ - data[0] = 4; - data[1] = 3; - data[2] = 0x09; - data[3] = 0x04; - ret = 4; - break; - case 1: - /* vendor description */ - ret = set_usb_string(data, "QEMU " QEMU_VERSION); - break; - case 2: - /* product description */ - ret = set_usb_string(data, "QEMU USB SERIAL"); - break; - case 3: - /* serial number */ - ret = set_usb_string(data, "1"); - break; - default: - goto fail; - } - break; - default: - goto fail; - } - break; - case DeviceRequest | USB_REQ_GET_CONFIGURATION: - data[0] = 1; - ret = 1; - break; - case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: - ret = 0; - break; case DeviceRequest | USB_REQ_GET_INTERFACE: data[0] = 0; ret = 1; @@ -448,7 +384,7 @@ /* handle serial break */ if (s->event_trigger && s->event_trigger & FTDI_BI) { s->event_trigger &= ~FTDI_BI; - *data++ = FTDI_BI; + *data = FTDI_BI; ret = 2; break; } else { @@ -542,7 +478,13 @@ static int usb_serial_initfn(USBDevice *dev) { USBSerialState *s = DO_UPCAST(USBSerialState, dev, dev); - s->dev.speed = USB_SPEED_FULL; + + usb_desc_init(dev); + + if (!s->cs) { + error_report("Property chardev is required"); + return -1; + } qemu_chr_add_handlers(s->cs, usb_serial_can_read, usb_serial_read, usb_serial_event, s); @@ -564,26 +506,26 @@ if (strstart(filename, "vendorid=", &p)) { vendorid = strtol(p, &e, 16); if (e == p || (*e && *e != ',' && *e != ':')) { - qemu_error("bogus vendor ID %s\n", p); + error_report("bogus vendor ID %s", p); return NULL; } filename = e; } else if (strstart(filename, "productid=", &p)) { productid = strtol(p, &e, 16); if (e == p || (*e && *e != ',' && *e != ':')) { - qemu_error("bogus product ID %s\n", p); + error_report("bogus product ID %s", p); return NULL; } filename = e; } else { - qemu_error("unrecognized serial USB option %s\n", filename); + error_report("unrecognized serial USB option %s", filename); return NULL; } while(*filename == ',') filename++; } if (!*filename) { - qemu_error("character device specification needed\n"); + error_report("character device specification needed"); return NULL; } filename++; @@ -627,6 +569,7 @@ .product_desc = "QEMU USB Serial", .qdev.name = "usb-serial", .qdev.size = sizeof(USBSerialState), + .usb_desc = &desc_serial, .init = usb_serial_initfn, .handle_packet = usb_generic_handle_packet, .handle_reset = usb_serial_handle_reset, @@ -636,9 +579,7 @@ .usbdevice_name = "serial", .usbdevice_init = usb_serial_init, .qdev.props = (Property[]) { - DEFINE_PROP_CHR("chardev", USBSerialState, cs), - DEFINE_PROP_HEX32("vendorid", USBSerialState, vendorid, 0x0403), - DEFINE_PROP_HEX32("productid", USBSerialState, productid, 0x6001), + DEFINE_PROP_CHR("chardev", USBSerialState, cs), DEFINE_PROP_END_OF_LIST(), }, }; @@ -647,6 +588,7 @@ .product_desc = "QEMU USB Braille", .qdev.name = "usb-braille", .qdev.size = sizeof(USBSerialState), + .usb_desc = &desc_braille, .init = usb_serial_initfn, .handle_packet = usb_generic_handle_packet, .handle_reset = usb_serial_handle_reset, @@ -656,9 +598,7 @@ .usbdevice_name = "braille", .usbdevice_init = usb_braille_init, .qdev.props = (Property[]) { - DEFINE_PROP_CHR("chardev", USBSerialState, cs), - DEFINE_PROP_HEX32("vendorid", USBSerialState, vendorid, 0x0403), - DEFINE_PROP_HEX32("productid", USBSerialState, productid, 0xfe72), + DEFINE_PROP_CHR("chardev", USBSerialState, cs), DEFINE_PROP_END_OF_LIST(), }, }; diff -Nru qemu-kvm-0.12.5+noroms/hw/usb-uhci.c qemu-kvm-0.14.1/hw/usb-uhci.c --- qemu-kvm-0.12.5+noroms/hw/usb-uhci.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/usb-uhci.c 2011-05-11 13:29:46.000000000 +0000 @@ -57,13 +57,18 @@ #define TD_CTRL_NAK (1 << 19) #define TD_CTRL_TIMEOUT (1 << 18) +#define UHCI_PORT_SUSPEND (1 << 12) #define UHCI_PORT_RESET (1 << 9) #define UHCI_PORT_LSDA (1 << 8) +#define UHCI_PORT_RD (1 << 6) #define UHCI_PORT_ENC (1 << 3) #define UHCI_PORT_EN (1 << 2) #define UHCI_PORT_CSC (1 << 1) #define UHCI_PORT_CCS (1 << 0) +#define UHCI_PORT_READ_ONLY (0x1bb) +#define UHCI_PORT_WRITE_CLEAR (UHCI_PORT_CSC | UHCI_PORT_ENC) + #define FRAME_TIMER_FREQ 1000 #define FRAME_MAX_LOOPS 100 @@ -71,7 +76,7 @@ #define NB_PORTS 2 #ifdef DEBUG -#define dprintf printf +#define DPRINTF printf static const char *pid2str(int pid) { @@ -84,7 +89,7 @@ } #else -#define dprintf(...) +#define DPRINTF(...) #endif #ifdef DEBUG_DUMP_DATA @@ -112,6 +117,7 @@ uint32_t td; uint32_t token; int8_t valid; + uint8_t isoc; uint8_t done; uint8_t buffer[2048]; } UHCIAsync; @@ -131,6 +137,7 @@ uint32_t fl_base_addr; /* frame list base address */ uint8_t sof_timing; uint8_t status2; /* bit 0 and 1 are used to generate UHCI_STS_USBINT */ + int64_t expire_time; QEMUTimer *frame_timer; UHCIPort ports[NB_PORTS]; @@ -164,6 +171,7 @@ async->td = 0; async->token = 0; async->done = 0; + async->isoc = 0; async->next = NULL; return async; @@ -198,7 +206,7 @@ static void uhci_async_cancel(UHCIState *s, UHCIAsync *async) { - dprintf("uhci: cancel td 0x%x token 0x%x done %u\n", + DPRINTF("uhci: cancel td 0x%x token 0x%x done %u\n", async->td, async->token, async->done); if (!async->done) @@ -304,8 +312,6 @@ return match; } -static void uhci_attach(USBPort *port1, USBDevice *dev); - static void uhci_update_irq(UHCIState *s) { int level; @@ -329,7 +335,7 @@ int i; UHCIPort *port; - dprintf("uhci: full reset\n"); + DPRINTF("uhci: full reset\n"); pci_conf = s->dev.config; @@ -345,8 +351,9 @@ for(i = 0; i < NB_PORTS; i++) { port = &s->ports[i]; port->ctrl = 0x0080; - if (port->port.dev) - uhci_attach(&port->port, port->port.dev); + if (port->port.dev) { + usb_attach(&port->port, port->port.dev); + } } uhci_async_cancel_all(s); @@ -372,7 +379,7 @@ static const VMStateDescription vmstate_uhci = { .name = "uhci", - .version_id = 1, + .version_id = 2, .minimum_version_id = 1, .minimum_version_id_old = 1, .pre_save = uhci_pre_save, @@ -389,6 +396,7 @@ VMSTATE_UINT8(sof_timing, UHCIState), VMSTATE_UINT8(status2, UHCIState), VMSTATE_TIMER(frame_timer, UHCIState), + VMSTATE_INT64_V(expire_time, UHCIState, 2), VMSTATE_END_OF_LIST() } }; @@ -427,7 +435,7 @@ UHCIState *s = opaque; addr &= 0x1f; - dprintf("uhci: writew port=0x%04x val=0x%04x\n", addr, val); + DPRINTF("uhci: writew port=0x%04x val=0x%04x\n", addr, val); switch(addr) { case 0x00: @@ -494,9 +502,10 @@ usb_send_msg(dev, USB_MSG_RESET); } } - port->ctrl = (port->ctrl & 0x01fb) | (val & ~0x01fb); + port->ctrl &= UHCI_PORT_READ_ONLY; + port->ctrl |= (val & ~UHCI_PORT_READ_ONLY); /* some bits are reset when a '1' is written to them */ - port->ctrl &= ~(val & 0x000a); + port->ctrl &= ~(val & UHCI_PORT_WRITE_CLEAR); } break; } @@ -538,7 +547,7 @@ break; } - dprintf("uhci: readw port=0x%04x val=0x%04x\n", addr, val); + DPRINTF("uhci: readw port=0x%04x val=0x%04x\n", addr, val); return val; } @@ -548,7 +557,7 @@ UHCIState *s = opaque; addr &= 0x1f; - dprintf("uhci: writel port=0x%04x val=0x%08x\n", addr, val); + DPRINTF("uhci: writel port=0x%04x val=0x%08x\n", addr, val); switch(addr) { case 0x08: @@ -589,49 +598,52 @@ } } -static void uhci_attach(USBPort *port1, USBDevice *dev) +static void uhci_attach(USBPort *port1) { UHCIState *s = port1->opaque; UHCIPort *port = &s->ports[port1->index]; - if (dev) { - if (port->port.dev) { - usb_attach(port1, NULL); - } - /* set connect status */ - port->ctrl |= UHCI_PORT_CCS | UHCI_PORT_CSC; + /* set connect status */ + port->ctrl |= UHCI_PORT_CCS | UHCI_PORT_CSC; - /* update speed */ - if (dev->speed == USB_SPEED_LOW) - port->ctrl |= UHCI_PORT_LSDA; - else - port->ctrl &= ~UHCI_PORT_LSDA; + /* update speed */ + if (port->port.dev->speed == USB_SPEED_LOW) { + port->ctrl |= UHCI_PORT_LSDA; + } else { + port->ctrl &= ~UHCI_PORT_LSDA; + } - uhci_resume(s); + uhci_resume(s); +} - port->port.dev = dev; - /* send the attach message */ - usb_send_msg(dev, USB_MSG_ATTACH); - } else { - /* set connect status */ - if (port->ctrl & UHCI_PORT_CCS) { - port->ctrl &= ~UHCI_PORT_CCS; - port->ctrl |= UHCI_PORT_CSC; - } - /* disable port */ - if (port->ctrl & UHCI_PORT_EN) { - port->ctrl &= ~UHCI_PORT_EN; - port->ctrl |= UHCI_PORT_ENC; - } +static void uhci_detach(USBPort *port1) +{ + UHCIState *s = port1->opaque; + UHCIPort *port = &s->ports[port1->index]; - uhci_resume(s); + /* set connect status */ + if (port->ctrl & UHCI_PORT_CCS) { + port->ctrl &= ~UHCI_PORT_CCS; + port->ctrl |= UHCI_PORT_CSC; + } + /* disable port */ + if (port->ctrl & UHCI_PORT_EN) { + port->ctrl &= ~UHCI_PORT_EN; + port->ctrl |= UHCI_PORT_ENC; + } - dev = port->port.dev; - if (dev) { - /* send the detach message */ - usb_send_msg(dev, USB_MSG_DETACH); - } - port->port.dev = NULL; + uhci_resume(s); +} + +static void uhci_wakeup(USBDevice *dev) +{ + USBBus *bus = usb_bus_from_device(dev); + UHCIState *s = container_of(bus, UHCIState, bus); + UHCIPort *port = s->ports + dev->port->index; + + if (port->ctrl & UHCI_PORT_SUSPEND && !(port->ctrl & UHCI_PORT_RD)) { + port->ctrl |= UHCI_PORT_RD; + uhci_resume(s); } } @@ -639,7 +651,7 @@ { int i, ret; - dprintf("uhci: packet enter. pid %s addr 0x%02x ep %d len %d\n", + DPRINTF("uhci: packet enter. pid %s addr 0x%02x ep %d len %d\n", pid2str(p->pid), p->devaddr, p->devep, p->len); if (p->pid == USB_TOKEN_OUT || p->pid == USB_TOKEN_SETUP) dump_data(p->data, p->len); @@ -653,7 +665,7 @@ ret = dev->info->handle_packet(dev, p); } - dprintf("uhci: packet exit. ret %d len %d\n", ret, p->len); + DPRINTF("uhci: packet exit. ret %d len %d\n", ret, p->len); if (p->pid == USB_TOKEN_IN && ret > 0) dump_data(p->data, ret); @@ -695,7 +707,6 @@ if (pid == USB_TOKEN_IN) { if (len > max_len) { - len = max_len; ret = USB_RET_BABBLE; goto out; } @@ -708,7 +719,7 @@ if ((td->ctrl & TD_CTRL_SPD) && len < max_len) { *int_mask |= 0x02; /* short packet: do not update QH */ - dprintf("uhci: short packet. td 0x%x token 0x%x\n", async->td, async->token); + DPRINTF("uhci: short packet. td 0x%x token 0x%x\n", async->td, async->token); return 1; } } @@ -763,13 +774,25 @@ { UHCIAsync *async; int len = 0, max_len; - uint8_t pid; + uint8_t pid, isoc; + uint32_t token; /* Is active ? */ if (!(td->ctrl & TD_CTRL_ACTIVE)) return 1; - async = uhci_async_find_td(s, addr, td->token); + /* token field is not unique for isochronous requests, + * so use the destination buffer + */ + if (td->ctrl & TD_CTRL_IOS) { + token = td->buffer; + isoc = 1; + } else { + token = td->token; + isoc = 0; + } + + async = uhci_async_find_td(s, addr, token); if (async) { /* Already submitted */ async->valid = 32; @@ -786,9 +809,13 @@ if (!async) return 1; - async->valid = 10; + /* valid needs to be large enough to handle 10 frame delay + * for initial isochronous requests + */ + async->valid = 32; async->td = addr; - async->token = td->token; + async->token = token; + async->isoc = isoc; max_len = ((td->token >> 21) + 1) & 0x7ff; pid = td->token & 0xff; @@ -840,11 +867,32 @@ UHCIState *s = opaque; UHCIAsync *async = (UHCIAsync *) packet; - dprintf("uhci: async complete. td 0x%x token 0x%x\n", async->td, async->token); + DPRINTF("uhci: async complete. td 0x%x token 0x%x\n", async->td, async->token); - async->done = 1; + if (async->isoc) { + UHCI_TD td; + uint32_t link = async->td; + uint32_t int_mask = 0, val; - uhci_process_frame(s); + cpu_physical_memory_read(link & ~0xf, (uint8_t *) &td, sizeof(td)); + le32_to_cpus(&td.link); + le32_to_cpus(&td.ctrl); + le32_to_cpus(&td.token); + le32_to_cpus(&td.buffer); + + uhci_async_unlink(s, async); + uhci_complete_td(s, &td, async, &int_mask); + s->pending_int_mask |= int_mask; + + /* update the status bits of the TD */ + val = cpu_to_le32(td.ctrl); + cpu_physical_memory_write((link & ~0xf) + 4, + (const uint8_t *)&val, sizeof(val)); + uhci_async_free(s, async); + } else { + async->done = 1; + uhci_process_frame(s); + } } static int is_valid(uint32_t link) @@ -900,7 +948,7 @@ frame_addr = s->fl_base_addr + ((s->frnum & 0x3ff) << 2); - dprintf("uhci: processing frame %d addr 0x%x\n" , s->frnum, frame_addr); + DPRINTF("uhci: processing frame %d addr 0x%x\n" , s->frnum, frame_addr); cpu_physical_memory_read(frame_addr, (uint8_t *)&link, 4); le32_to_cpus(&link); @@ -922,7 +970,7 @@ * are already done, and async completion handler will re-process * the frame when something is ready. */ - dprintf("uhci: detected loop. qh 0x%x\n", link); + DPRINTF("uhci: detected loop. qh 0x%x\n", link); break; } @@ -930,7 +978,7 @@ le32_to_cpus(&qh.link); le32_to_cpus(&qh.el_link); - dprintf("uhci: QH 0x%x load. link 0x%x elink 0x%x\n", + DPRINTF("uhci: QH 0x%x load. link 0x%x elink 0x%x\n", link, qh.link, qh.el_link); if (!is_valid(qh.el_link)) { @@ -952,7 +1000,7 @@ le32_to_cpus(&td.token); le32_to_cpus(&td.buffer); - dprintf("uhci: TD 0x%x load. link 0x%x ctrl 0x%x token 0x%x qh 0x%x\n", + DPRINTF("uhci: TD 0x%x load. link 0x%x ctrl 0x%x token 0x%x qh 0x%x\n", link, td.link, td.ctrl, td.token, curr_qh); old_td_ctrl = td.ctrl; @@ -970,7 +1018,7 @@ } if (ret == 2 || ret == 1) { - dprintf("uhci: TD 0x%x %s. link 0x%x ctrl 0x%x token 0x%x qh 0x%x\n", + DPRINTF("uhci: TD 0x%x %s. link 0x%x ctrl 0x%x token 0x%x qh 0x%x\n", link, ret == 2 ? "pend" : "skip", td.link, td.ctrl, td.token, curr_qh); @@ -980,7 +1028,7 @@ /* completed TD */ - dprintf("uhci: TD 0x%x done. link 0x%x ctrl 0x%x token 0x%x qh 0x%x\n", + DPRINTF("uhci: TD 0x%x done. link 0x%x ctrl 0x%x token 0x%x qh 0x%x\n", link, td.link, td.ctrl, td.token, curr_qh); link = td.link; @@ -995,7 +1043,7 @@ if (!depth_first(link)) { /* done with this QH */ - dprintf("uhci: QH 0x%x done. link 0x%x elink 0x%x\n", + DPRINTF("uhci: QH 0x%x done. link 0x%x elink 0x%x\n", curr_qh, qh.link, qh.el_link); curr_qh = 0; @@ -1006,13 +1054,15 @@ /* go to the next entry */ } - s->pending_int_mask = int_mask; + s->pending_int_mask |= int_mask; } static void uhci_frame_timer(void *opaque) { UHCIState *s = opaque; - int64_t expire_time; + + /* prepare the timer for the next frame */ + s->expire_time += (get_ticks_per_sec() / FRAME_TIMER_FREQ); if (!(s->cmd & UHCI_CMD_RS)) { /* Full stop */ @@ -1020,7 +1070,7 @@ /* set hchalted bit in status - UHCI11D 2.1.2 */ s->status |= UHCI_STS_HCHALTED; - dprintf("uhci: halted\n"); + DPRINTF("uhci: halted\n"); return; } @@ -1030,11 +1080,12 @@ s->status |= UHCI_STS_USBINT; uhci_update_irq(s); } + s->pending_int_mask = 0; /* Start new frame */ s->frnum = (s->frnum + 1) & 0x7ff; - dprintf("uhci: new frame #%u\n" , s->frnum); + DPRINTF("uhci: new frame #%u\n" , s->frnum); uhci_async_validate_begin(s); @@ -1042,10 +1093,7 @@ uhci_async_validate_end(s); - /* prepare the timer for the next frame */ - expire_time = qemu_get_clock(vm_clock) + - (get_ticks_per_sec() / FRAME_TIMER_FREQ); - qemu_mod_timer(s->frame_timer, expire_time); + qemu_mod_timer(s->frame_timer, s->expire_time); } static void uhci_map(PCIDevice *pci_dev, int region_num, @@ -1061,23 +1109,33 @@ register_ioport_read(addr, 32, 1, uhci_ioport_readb, s); } +static USBPortOps uhci_port_ops = { + .attach = uhci_attach, + .detach = uhci_detach, + .wakeup = uhci_wakeup, +}; + static int usb_uhci_common_initfn(UHCIState *s) { uint8_t *pci_conf = s->dev.config; int i; - pci_conf[0x08] = 0x01; // revision number - pci_conf[0x09] = 0x00; + pci_conf[PCI_REVISION_ID] = 0x01; // revision number + pci_conf[PCI_CLASS_PROG] = 0x00; pci_config_set_class(pci_conf, PCI_CLASS_SERIAL_USB); - pci_conf[PCI_HEADER_TYPE] = PCI_HEADER_TYPE_NORMAL; // header_type - pci_conf[0x3d] = 4; // interrupt pin 3 + /* TODO: reset value should be 0. */ + pci_conf[PCI_INTERRUPT_PIN] = 4; // interrupt pin 3 pci_conf[0x60] = 0x10; // release number usb_bus_new(&s->bus, &s->dev.qdev); for(i = 0; i < NB_PORTS; i++) { - usb_register_port(&s->bus, &s->ports[i].port, s, i, uhci_attach); + usb_register_port(&s->bus, &s->ports[i].port, s, i, &uhci_port_ops, + USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL); + usb_port_location(&s->ports[i].port, NULL, i+1); } s->frame_timer = qemu_new_timer(vm_clock, uhci_frame_timer, s); + s->expire_time = qemu_get_clock(vm_clock) + + (get_ticks_per_sec() / FRAME_TIMER_FREQ); s->num_ports_vmstate = NB_PORTS; qemu_register_reset(uhci_reset, s); @@ -1110,6 +1168,24 @@ return usb_uhci_common_initfn(s); } +static int usb_uhci_vt82c686b_initfn(PCIDevice *dev) +{ + UHCIState *s = DO_UPCAST(UHCIState, dev, dev); + uint8_t *pci_conf = s->dev.config; + + pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_VIA); + pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_VIA_UHCI); + + /* USB misc control 1/2 */ + pci_set_long(pci_conf + 0x40,0x00001000); + /* PM capability */ + pci_set_long(pci_conf + 0x80,0x00020001); + /* USB legacy support */ + pci_set_long(pci_conf + 0xc0,0x00002000); + + return usb_uhci_common_initfn(s); +} + static PCIDeviceInfo uhci_info[] = { { .qdev.name = "piix3-usb-uhci", @@ -1122,6 +1198,11 @@ .qdev.vmsd = &vmstate_uhci, .init = usb_uhci_piix4_initfn, },{ + .qdev.name = "vt82c686b-usb-uhci", + .qdev.size = sizeof(UHCIState), + .qdev.vmsd = &vmstate_uhci, + .init = usb_uhci_vt82c686b_initfn, + },{ /* end of list */ } }; @@ -1141,3 +1222,8 @@ { pci_create_simple(bus, devfn, "piix4-usb-uhci"); } + +void usb_uhci_vt82c686b_init(PCIBus *bus, int devfn) +{ + pci_create_simple(bus, devfn, "vt82c686b-usb-uhci"); +} diff -Nru qemu-kvm-0.12.5+noroms/hw/usb-uhci.h qemu-kvm-0.14.1/hw/usb-uhci.h --- qemu-kvm-0.12.5+noroms/hw/usb-uhci.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/usb-uhci.h 2011-05-11 13:29:46.000000000 +0000 @@ -5,5 +5,6 @@ void usb_uhci_piix3_init(PCIBus *bus, int devfn); void usb_uhci_piix4_init(PCIBus *bus, int devfn); +void usb_uhci_vt82c686b_init(PCIBus *bus, int devfn); #endif diff -Nru qemu-kvm-0.12.5+noroms/hw/usb-wacom.c qemu-kvm-0.14.1/hw/usb-wacom.c --- qemu-kvm-0.12.5+noroms/hw/usb-wacom.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/usb-wacom.c 2011-05-11 13:29:46.000000000 +0000 @@ -28,6 +28,7 @@ #include "hw.h" #include "console.h" #include "usb.h" +#include "usb-desc.h" /* Interface requests */ #define WACOM_GET_REPORT 0x2101 @@ -54,68 +55,75 @@ int changed; } USBWacomState; -static const uint8_t qemu_wacom_dev_descriptor[] = { - 0x12, /* u8 bLength; */ - 0x01, /* u8 bDescriptorType; Device */ - 0x10, 0x10, /* u16 bcdUSB; v1.10 */ - - 0x00, /* u8 bDeviceClass; */ - 0x00, /* u8 bDeviceSubClass; */ - 0x00, /* u8 bDeviceProtocol; [ low/full speeds only ] */ - 0x08, /* u8 bMaxPacketSize0; 8 Bytes */ - - 0x6a, 0x05, /* u16 idVendor; */ - 0x00, 0x00, /* u16 idProduct; */ - 0x10, 0x42, /* u16 bcdDevice */ - - 0x01, /* u8 iManufacturer; */ - 0x02, /* u8 iProduct; */ - 0x00, /* u8 iSerialNumber; */ - 0x01, /* u8 bNumConfigurations; */ +enum { + STR_MANUFACTURER = 1, + STR_PRODUCT, + STR_SERIALNUMBER, }; -static const uint8_t qemu_wacom_config_descriptor[] = { - /* one configuration */ - 0x09, /* u8 bLength; */ - 0x02, /* u8 bDescriptorType; Configuration */ - 0x22, 0x00, /* u16 wTotalLength; */ - 0x01, /* u8 bNumInterfaces; (1) */ - 0x01, /* u8 bConfigurationValue; */ - 0x00, /* u8 iConfiguration; */ - 0x80, /* u8 bmAttributes; - Bit 7: must be set, - 6: Self-powered, - 5: Remote wakeup, - 4..0: resvd */ - 40, /* u8 MaxPower; */ - - /* one interface */ - 0x09, /* u8 if_bLength; */ - 0x04, /* u8 if_bDescriptorType; Interface */ - 0x00, /* u8 if_bInterfaceNumber; */ - 0x00, /* u8 if_bAlternateSetting; */ - 0x01, /* u8 if_bNumEndpoints; */ - 0x03, /* u8 if_bInterfaceClass; HID */ - 0x01, /* u8 if_bInterfaceSubClass; Boot */ - 0x02, /* u8 if_bInterfaceProtocol; [usb1.1 or single tt] */ - 0x00, /* u8 if_iInterface; */ - - /* HID descriptor */ - 0x09, /* u8 bLength; */ - 0x21, /* u8 bDescriptorType; */ - 0x01, 0x10, /* u16 HID_class */ - 0x00, /* u8 country_code */ - 0x01, /* u8 num_descriptors */ - 0x22, /* u8 type; Report */ - 0x6e, 0x00, /* u16 len */ - - /* one endpoint (status change endpoint) */ - 0x07, /* u8 ep_bLength; */ - 0x05, /* u8 ep_bDescriptorType; Endpoint */ - 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */ - 0x03, /* u8 ep_bmAttributes; Interrupt */ - 0x08, 0x00, /* u16 ep_wMaxPacketSize; */ - 0x0a, /* u8 ep_bInterval; */ +static const USBDescStrings desc_strings = { + [STR_MANUFACTURER] = "QEMU " QEMU_VERSION, + [STR_PRODUCT] = "Wacom PenPartner", + [STR_SERIALNUMBER] = "1", +}; + +static const USBDescIface desc_iface_wacom = { + .bInterfaceNumber = 0, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_HID, + .bInterfaceSubClass = 0x01, /* boot */ + .bInterfaceProtocol = 0x02, + .ndesc = 1, + .descs = (USBDescOther[]) { + { + /* HID descriptor */ + .data = (uint8_t[]) { + 0x09, /* u8 bLength */ + 0x21, /* u8 bDescriptorType */ + 0x01, 0x10, /* u16 HID_class */ + 0x00, /* u8 country_code */ + 0x01, /* u8 num_descriptors */ + 0x22, /* u8 type: Report */ + 0x6e, 0, /* u16 len */ + }, + }, + }, + .eps = (USBDescEndpoint[]) { + { + .bEndpointAddress = USB_DIR_IN | 0x01, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = 8, + .bInterval = 0x0a, + }, + }, +}; + +static const USBDescDevice desc_device_wacom = { + .bcdUSB = 0x0110, + .bMaxPacketSize0 = 8, + .bNumConfigurations = 1, + .confs = (USBDescConfig[]) { + { + .bNumInterfaces = 1, + .bConfigurationValue = 1, + .bmAttributes = 0x80, + .bMaxPower = 40, + .ifs = &desc_iface_wacom, + }, + }, +}; + +static const USBDesc desc_wacom = { + .id = { + .idVendor = 0x056a, + .idProduct = 0x0000, + .bcdDevice = 0x4210, + .iManufacturer = STR_MANUFACTURER, + .iProduct = STR_PRODUCT, + .iSerialNumber = STR_SERIALNUMBER, + }, + .full = &desc_device_wacom, + .str = desc_strings, }; static void usb_mouse_event(void *opaque, @@ -160,6 +168,7 @@ if (!s->mouse_grabbed) { s->eh_entry = qemu_add_mouse_event_handler(usb_mouse_event, s, 0, "QEMU PenPartner tablet"); + qemu_activate_mouse_event_handler(s->eh_entry); s->mouse_grabbed = 1; } @@ -197,6 +206,7 @@ if (!s->mouse_grabbed) { s->eh_entry = qemu_add_mouse_event_handler(usb_wacom_event, s, 1, "QEMU PenPartner tablet"); + qemu_activate_mouse_event_handler(s->eh_entry); s->mouse_grabbed = 1; } @@ -243,89 +253,15 @@ int index, int length, uint8_t *data) { USBWacomState *s = (USBWacomState *) dev; - int ret = 0; + int ret; + + ret = usb_desc_handle_control(dev, request, value, index, length, data); + if (ret >= 0) { + return ret; + } + ret = 0; switch (request) { - case DeviceRequest | USB_REQ_GET_STATUS: - data[0] = (1 << USB_DEVICE_SELF_POWERED) | - (dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP); - data[1] = 0x00; - ret = 2; - break; - case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: - if (value == USB_DEVICE_REMOTE_WAKEUP) { - dev->remote_wakeup = 0; - } else { - goto fail; - } - ret = 0; - break; - case DeviceOutRequest | USB_REQ_SET_FEATURE: - if (value == USB_DEVICE_REMOTE_WAKEUP) { - dev->remote_wakeup = 1; - } else { - goto fail; - } - ret = 0; - break; - case DeviceOutRequest | USB_REQ_SET_ADDRESS: - dev->addr = value; - ret = 0; - break; - case DeviceRequest | USB_REQ_GET_DESCRIPTOR: - switch (value >> 8) { - case USB_DT_DEVICE: - memcpy(data, qemu_wacom_dev_descriptor, - sizeof(qemu_wacom_dev_descriptor)); - ret = sizeof(qemu_wacom_dev_descriptor); - break; - case USB_DT_CONFIG: - memcpy(data, qemu_wacom_config_descriptor, - sizeof(qemu_wacom_config_descriptor)); - ret = sizeof(qemu_wacom_config_descriptor); - break; - case USB_DT_STRING: - switch (value & 0xff) { - case 0: - /* language ids */ - data[0] = 4; - data[1] = 3; - data[2] = 0x09; - data[3] = 0x04; - ret = 4; - break; - case 1: - /* serial number */ - ret = set_usb_string(data, "1"); - break; - case 2: - ret = set_usb_string(data, "Wacom PenPartner"); - break; - case 3: - /* vendor description */ - ret = set_usb_string(data, "QEMU " QEMU_VERSION); - break; - case 4: - ret = set_usb_string(data, "Wacom Tablet"); - break; - case 5: - ret = set_usb_string(data, "Endpoint1 Interrupt Pipe"); - break; - default: - goto fail; - } - break; - default: - goto fail; - } - break; - case DeviceRequest | USB_REQ_GET_CONFIGURATION: - data[0] = 1; - ret = 1; - break; - case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: - ret = 0; - break; case DeviceRequest | USB_REQ_GET_INTERFACE: data[0] = 0; ret = 1; @@ -334,8 +270,10 @@ ret = 0; break; case WACOM_SET_REPORT: - qemu_remove_mouse_event_handler(s->eh_entry); - s->mouse_grabbed = 0; + if (s->mouse_grabbed) { + qemu_remove_mouse_event_handler(s->eh_entry); + s->mouse_grabbed = 0; + } s->mode = data[0]; ret = 0; break; @@ -360,7 +298,6 @@ ret = 0; break; default: - fail: ret = USB_RET_STALL; break; } @@ -397,13 +334,16 @@ { USBWacomState *s = (USBWacomState *) dev; - qemu_remove_mouse_event_handler(s->eh_entry); + if (s->mouse_grabbed) { + qemu_remove_mouse_event_handler(s->eh_entry); + s->mouse_grabbed = 0; + } } static int usb_wacom_initfn(USBDevice *dev) { USBWacomState *s = DO_UPCAST(USBWacomState, dev, dev); - s->dev.speed = USB_SPEED_FULL; + usb_desc_init(dev); s->changed = 1; return 0; } @@ -413,6 +353,7 @@ .qdev.name = "usb-wacom-tablet", .qdev.desc = "QEMU PenPartner Tablet", .usbdevice_name = "wacom-tablet", + .usb_desc = &desc_wacom, .qdev.size = sizeof(USBWacomState), .init = usb_wacom_initfn, .handle_packet = usb_generic_handle_packet, diff -Nru qemu-kvm-0.12.5+noroms/hw/versatilepb.c qemu-kvm-0.14.1/hw/versatilepb.c --- qemu-kvm-0.12.5+noroms/hw/versatilepb.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/versatilepb.c 2011-05-11 13:29:46.000000000 +0000 @@ -16,6 +16,7 @@ #include "pci.h" #include "usb-ohci.h" #include "boards.h" +#include "blockdev.h" /* Primary interrupt controller. */ @@ -29,6 +30,18 @@ int irq; } vpb_sic_state; +static const VMStateDescription vmstate_vpb_sic = { + .name = "versatilepb_sic", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(level, vpb_sic_state), + VMSTATE_UINT32(mask, vpb_sic_state), + VMSTATE_UINT32(pic_enable, vpb_sic_state), + VMSTATE_END_OF_LIST() + } +}; + static void vpb_sic_update(vpb_sic_state *s) { uint32_t flags; @@ -142,9 +155,9 @@ } s->irq = 31; iomemtype = cpu_register_io_memory(vpb_sic_readfn, - vpb_sic_writefn, s); + vpb_sic_writefn, s, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, 0x1000, iomemtype); - /* ??? Save/restore. */ return 0; } @@ -180,7 +193,7 @@ fprintf(stderr, "Unable to find CPU definition\n"); exit(1); } - ram_offset = qemu_ram_alloc(ram_size); + ram_offset = qemu_ram_alloc(NULL, "versatile.ram", ram_size); /* ??? RAM should repeat to fill physical memory space. */ /* SDRAM at address zero. */ cpu_register_physical_memory(0, ram_size, ram_offset | IO_MEM_RAM); @@ -333,10 +346,17 @@ machine_init(versatile_machine_init); +static SysBusDeviceInfo vpb_sic_info = { + .init = vpb_sic_init, + .qdev.name = "versatilepb_sic", + .qdev.size = sizeof(vpb_sic_state), + .qdev.vmsd = &vmstate_vpb_sic, + .qdev.no_user = 1, +}; + static void versatilepb_register_devices(void) { - sysbus_register_dev("versatilepb_sic", sizeof(vpb_sic_state), - vpb_sic_init); + sysbus_register_withprop(&vpb_sic_info); } device_init(versatilepb_register_devices) diff -Nru qemu-kvm-0.12.5+noroms/hw/versatile_pci.c qemu-kvm-0.14.1/hw/versatile_pci.c --- qemu-kvm-0.12.5+noroms/hw/versatile_pci.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/versatile_pci.c 2011-05-11 13:29:46.000000000 +0000 @@ -32,18 +32,12 @@ static void pci_vpb_config_writew (void *opaque, target_phys_addr_t addr, uint32_t val) { -#ifdef TARGET_WORDS_BIGENDIAN - val = bswap16(val); -#endif pci_data_write(opaque, vpb_pci_config_addr (addr), val, 2); } static void pci_vpb_config_writel (void *opaque, target_phys_addr_t addr, uint32_t val) { -#ifdef TARGET_WORDS_BIGENDIAN - val = bswap32(val); -#endif pci_data_write(opaque, vpb_pci_config_addr (addr), val, 4); } @@ -58,9 +52,6 @@ { uint32_t val; val = pci_data_read(opaque, vpb_pci_config_addr (addr), 2); -#ifdef TARGET_WORDS_BIGENDIAN - val = bswap16(val); -#endif return val; } @@ -68,9 +59,6 @@ { uint32_t val; val = pci_data_read(opaque, vpb_pci_config_addr (addr), 4); -#ifdef TARGET_WORDS_BIGENDIAN - val = bswap32(val); -#endif return val; } @@ -123,12 +111,13 @@ } bus = pci_register_bus(&dev->qdev, "pci", pci_vpb_set_irq, pci_vpb_map_irq, s->irq, - 11 << 3, 4); + PCI_DEVFN(11, 0), 4); /* ??? Register memory space. */ s->mem_config = cpu_register_io_memory(pci_vpb_config_read, - pci_vpb_config_write, bus); + pci_vpb_config_write, bus, + DEVICE_LITTLE_ENDIAN); sysbus_init_mmio_cb(dev, 0x04000000, pci_vpb_map); pci_create_simple(bus, -1, "versatile_pci_host"); @@ -147,14 +136,10 @@ pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_XILINX); /* Both boards have the same device ID. Oh well. */ pci_config_set_device_id(d->config, PCI_DEVICE_ID_XILINX_XC2VP30); - d->config[0x04] = 0x00; - d->config[0x05] = 0x00; - d->config[0x06] = 0x20; - d->config[0x07] = 0x02; - d->config[0x08] = 0x00; // revision - d->config[0x09] = 0x00; // programming i/f + pci_set_word(d->config + PCI_STATUS, + PCI_STATUS_66MHZ | PCI_STATUS_DEVSEL_MEDIUM); pci_config_set_class(d->config, PCI_CLASS_PROCESSOR_CO); - d->config[0x0D] = 0x10; // latency_timer + pci_set_byte(d->config + PCI_LATENCY_TIMER, 0x10); return 0; } diff -Nru qemu-kvm-0.12.5+noroms/hw/vga.c qemu-kvm-0.14.1/hw/vga.c --- qemu-kvm-0.12.5+noroms/hw/vga.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/vga.c 2011-05-11 13:29:46.000000000 +0000 @@ -169,7 +169,10 @@ int vretr_start_line; int vretr_end_line; - int div2, sldiv2, dots; + int dots; +#if 0 + int div2, sldiv2; +#endif int clocking_mode; int clock_sel; const int clk_hz[] = {25175000, 28322000, 25175000, 25175000}; @@ -190,8 +193,6 @@ vretr_end_line = s->cr[0x11] & 0xf; - div2 = (s->cr[0x17] >> 2) & 1; - sldiv2 = (s->cr[0x17] >> 3) & 1; clocking_mode = (s->sr[0x01] >> 3) & 1; clock_sel = (s->msr >> 2) & 3; @@ -216,6 +217,8 @@ r->htotal = htotal_chars; #if 0 + div2 = (s->cr[0x17] >> 2) & 1; + sldiv2 = (s->cr[0x17] >> 3) & 1; printf ( "hz=%f\n" "htotal = %d\n" @@ -229,7 +232,7 @@ "clocking_mode = %d\n" "clock_sel = %d %d\n" "dots = %d\n" - "ticks/char = %lld\n" + "ticks/char = %" PRId64 "\n" "\n", (double) get_ticks_per_sec() / (r->ticks_per_char * r->total_chars), htotal_chars, @@ -522,7 +525,7 @@ VGACommonState *s = opaque; uint32_t val; - if (s->vbe_index <= VBE_DISPI_INDEX_NB) { + if (s->vbe_index < VBE_DISPI_INDEX_NB) { if (s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_GETCAPS) { switch(s->vbe_index) { /* XXX: do not hardcode ? */ @@ -542,6 +545,8 @@ } else { val = s->vbe_regs[s->vbe_index]; } + } else if (s->vbe_index == VBE_DISPI_INDEX_VIDEO_MEMORY_64K) { + val = s->vram_size / (64 * 1024); } else { val = 0; } @@ -762,30 +767,18 @@ static uint32_t vga_mem_readw(void *opaque, target_phys_addr_t addr) { uint32_t v; -#ifdef TARGET_WORDS_BIGENDIAN - v = vga_mem_readb(opaque, addr) << 8; - v |= vga_mem_readb(opaque, addr + 1); -#else v = vga_mem_readb(opaque, addr); v |= vga_mem_readb(opaque, addr + 1) << 8; -#endif return v; } static uint32_t vga_mem_readl(void *opaque, target_phys_addr_t addr) { uint32_t v; -#ifdef TARGET_WORDS_BIGENDIAN - v = vga_mem_readb(opaque, addr) << 24; - v |= vga_mem_readb(opaque, addr + 1) << 16; - v |= vga_mem_readb(opaque, addr + 2) << 8; - v |= vga_mem_readb(opaque, addr + 3); -#else v = vga_mem_readb(opaque, addr); v |= vga_mem_readb(opaque, addr + 1) << 8; v |= vga_mem_readb(opaque, addr + 2) << 16; v |= vga_mem_readb(opaque, addr + 3) << 24; -#endif return v; } @@ -926,28 +919,16 @@ static void vga_mem_writew(void *opaque, target_phys_addr_t addr, uint32_t val) { -#ifdef TARGET_WORDS_BIGENDIAN - vga_mem_writeb(opaque, addr, (val >> 8) & 0xff); - vga_mem_writeb(opaque, addr + 1, val & 0xff); -#else vga_mem_writeb(opaque, addr, val & 0xff); vga_mem_writeb(opaque, addr + 1, (val >> 8) & 0xff); -#endif } static void vga_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) { -#ifdef TARGET_WORDS_BIGENDIAN - vga_mem_writeb(opaque, addr, (val >> 24) & 0xff); - vga_mem_writeb(opaque, addr + 1, (val >> 16) & 0xff); - vga_mem_writeb(opaque, addr + 2, (val >> 8) & 0xff); - vga_mem_writeb(opaque, addr + 3, val & 0xff); -#else vga_mem_writeb(opaque, addr, val & 0xff); vga_mem_writeb(opaque, addr + 1, (val >> 8) & 0xff); vga_mem_writeb(opaque, addr + 2, (val >> 16) & 0xff); vga_mem_writeb(opaque, addr + 3, (val >> 24) & 0xff); -#endif } typedef void vga_draw_glyph8_func(uint8_t *d, int linesize, @@ -1166,7 +1147,7 @@ } } -static vga_draw_glyph8_func *vga_draw_glyph8_table[NB_DEPTHS] = { +static vga_draw_glyph8_func * const vga_draw_glyph8_table[NB_DEPTHS] = { vga_draw_glyph8_8, vga_draw_glyph8_16, vga_draw_glyph8_16, @@ -1176,7 +1157,7 @@ vga_draw_glyph8_16, }; -static vga_draw_glyph8_func *vga_draw_glyph16_table[NB_DEPTHS] = { +static vga_draw_glyph8_func * const vga_draw_glyph16_table[NB_DEPTHS] = { vga_draw_glyph16_8, vga_draw_glyph16_16, vga_draw_glyph16_16, @@ -1186,7 +1167,7 @@ vga_draw_glyph16_16, }; -static vga_draw_glyph9_func *vga_draw_glyph9_table[NB_DEPTHS] = { +static vga_draw_glyph9_func * const vga_draw_glyph9_table[NB_DEPTHS] = { vga_draw_glyph9_8, vga_draw_glyph9_16, vga_draw_glyph9_16, @@ -1246,7 +1227,7 @@ typedef unsigned int rgb_to_pixel_dup_func(unsigned int r, unsigned int g, unsigned b); -static rgb_to_pixel_dup_func *rgb_to_pixel_dup_table[NB_DEPTHS] = { +static rgb_to_pixel_dup_func * const rgb_to_pixel_dup_table[NB_DEPTHS] = { rgb_to_pixel8_dup, rgb_to_pixel15_dup, rgb_to_pixel16_dup, @@ -1305,7 +1286,6 @@ line_offset = s->line_offset; vga_get_text_resolution(s, &width, &height, &cw, &cheight); - x_incr = cw * ((ds_get_bits_per_pixel(s->ds) + 7) >> 3); if ((height * width) > CH_ATTR_SIZE) { /* better than nothing: exit if transient size is too big */ return; @@ -1445,7 +1425,7 @@ VGA_DRAW_LINE_NB, }; -static vga_draw_line_func *vga_draw_line_table[NB_DEPTHS * VGA_DRAW_LINE_NB] = { +static vga_draw_line_func * const vga_draw_line_table[NB_DEPTHS * VGA_DRAW_LINE_NB] = { vga_draw_line2_8, vga_draw_line2_16, vga_draw_line2_16, @@ -1638,9 +1618,9 @@ if (kvm_enabled() && s->map_addr && s1) kvm_log_stop(s->map_addr, s->map_end - s->map_addr); - if (kvm_enabled() && s->lfb_vram_mapped && s2) { - kvm_log_stop(isa_mem_base + 0xa0000, 0x80000); - kvm_log_stop(isa_mem_base + 0xa8000, 0x80000); + if (kvm_enabled() && s->lfb_vram_mapped && s1) { + kvm_log_stop(isa_mem_base + 0xa0000, 0x8000); + kvm_log_stop(isa_mem_base + 0xa8000, 0x8000); } #ifdef CONFIG_BOCHS_VBE @@ -1961,8 +1941,6 @@ s->map_addr = 0; s->map_end = 0; s->lfb_vram_mapped = 0; - s->bios_offset = 0; - s->bios_size = 0; s->sr_index = 0; memset(s->sr, '\0', sizeof(s->sr)); s->gr_index = 0; @@ -1987,7 +1965,7 @@ #ifdef CONFIG_BOCHS_VBE s->vbe_index = 0; memset(s->vbe_regs, '\0', sizeof(s->vbe_regs)); - s->vbe_regs[VBE_DISPI_INDEX_ID] = VBE_DISPI_ID0; + s->vbe_regs[VBE_DISPI_INDEX_ID] = VBE_DISPI_ID5; s->vbe_start_addr = 0; s->vbe_line_offset = 0; s->vbe_bank_mask = (s->vram_size >> 16) - 1; @@ -2126,14 +2104,14 @@ if (full_update) { for (i = 0; i < size; src ++, dst ++, i ++) - console_write_ch(dst, VMEM2CHTYPE(*src)); + console_write_ch(dst, VMEM2CHTYPE(le32_to_cpu(*src))); dpy_update(s->ds, 0, 0, width, height); } else { c_max = 0; for (i = 0; i < size; src ++, dst ++, i ++) { - console_write_ch(&val, VMEM2CHTYPE(*src)); + console_write_ch(&val, VMEM2CHTYPE(le32_to_cpu(*src))); if (*dst != val) { *dst = val; c_max = i; @@ -2142,7 +2120,7 @@ } c_min = i; for (; i < size; src ++, dst ++, i ++) { - console_write_ch(&val, VMEM2CHTYPE(*src)); + console_write_ch(&val, VMEM2CHTYPE(le32_to_cpu(*src))); if (*dst != val) { *dst = val; c_max = i; @@ -2288,7 +2266,7 @@ #else s->is_vbe_vmstate = 0; #endif - s->vram_offset = qemu_ram_alloc(vga_ram_size); + s->vram_offset = qemu_ram_alloc(NULL, "vga.vram", vga_ram_size); s->vram_ptr = qemu_get_ram_ptr(s->vram_offset); s->vram_size = vga_ram_size; s->get_bpp = vga_get_bpp; @@ -2340,13 +2318,6 @@ register_ioport_write(0x1ce, 1, 2, vbe_ioport_write_index, s); register_ioport_write(0x1cf, 1, 2, vbe_ioport_write_data, s); - - /* old Bochs IO ports */ - register_ioport_read(0xff80, 1, 2, vbe_ioport_read_index, s); - register_ioport_read(0xff81, 1, 2, vbe_ioport_read_data, s); - - register_ioport_write(0xff80, 1, 2, vbe_ioport_write_index, s); - register_ioport_write(0xff81, 1, 2, vbe_ioport_write_data, s); #else register_ioport_read(0x1ce, 1, 2, vbe_ioport_read_index, s); register_ioport_read(0x1d0, 1, 2, vbe_ioport_read_data, s); @@ -2356,7 +2327,8 @@ #endif #endif /* CONFIG_BOCHS_VBE */ - vga_io_memory = cpu_register_io_memory(vga_mem_read, vga_mem_write, s); + vga_io_memory = cpu_register_io_memory(vga_mem_read, vga_mem_write, s, + DEVICE_LITTLE_ENDIAN); cpu_register_physical_memory(isa_mem_base + 0x000a0000, 0x20000, vga_io_memory); qemu_register_coalesced_mmio(isa_mem_base + 0x000a0000, 0x20000); diff -Nru qemu-kvm-0.12.5+noroms/hw/vga_int.h qemu-kvm-0.14.1/hw/vga_int.h --- qemu-kvm-0.12.5+noroms/hw/vga_int.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/vga_int.h 2011-05-11 13:29:46.000000000 +0000 @@ -47,13 +47,15 @@ #define VBE_DISPI_INDEX_VIRT_HEIGHT 0x7 #define VBE_DISPI_INDEX_X_OFFSET 0x8 #define VBE_DISPI_INDEX_Y_OFFSET 0x9 -#define VBE_DISPI_INDEX_NB 0xa +#define VBE_DISPI_INDEX_NB 0xa /* size of vbe_regs[] */ +#define VBE_DISPI_INDEX_VIDEO_MEMORY_64K 0xa /* read-only, not in vbe_regs */ #define VBE_DISPI_ID0 0xB0C0 #define VBE_DISPI_ID1 0xB0C1 #define VBE_DISPI_ID2 0xB0C2 #define VBE_DISPI_ID3 0xB0C3 #define VBE_DISPI_ID4 0xB0C4 +#define VBE_DISPI_ID5 0xB0C5 #define VBE_DISPI_DISABLED 0x00 #define VBE_DISPI_ENABLED 0x01 @@ -104,14 +106,12 @@ typedef struct VGACommonState { uint8_t *vram_ptr; ram_addr_t vram_offset; - unsigned int vram_size; + uint32_t vram_size; uint32_t lfb_addr; uint32_t lfb_end; uint32_t map_addr; uint32_t map_end; uint32_t lfb_vram_mapped; /* whether 0xa0000 is mapped as ram */ - uint32_t bios_offset; - uint32_t bios_size; uint32_t latch; uint8_t sr_index; uint8_t sr[256]; diff -Nru qemu-kvm-0.12.5+noroms/hw/vga-isa.c qemu-kvm-0.14.1/hw/vga-isa.c --- qemu-kvm-0.12.5+noroms/hw/vga-isa.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/vga-isa.c 2011-05-11 13:29:46.000000000 +0000 @@ -37,7 +37,7 @@ vga_common_init(s, VGA_RAM_SIZE); vga_init(s); - vmstate_register(0, &vmstate_vga_common, s); + vmstate_register(NULL, 0, &vmstate_vga_common, s); s->ds = graphic_console_init(s->update, s->invalidate, s->screen_dump, s->text_update, s); diff -Nru qemu-kvm-0.12.5+noroms/hw/vga-isa-mm.c qemu-kvm-0.14.1/hw/vga-isa-mm.c --- qemu-kvm-0.12.5+noroms/hw/vga-isa-mm.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/vga-isa-mm.c 2011-05-11 13:29:46.000000000 +0000 @@ -97,10 +97,12 @@ int s_ioport_ctrl, vga_io_memory; s->it_shift = it_shift; - s_ioport_ctrl = cpu_register_io_memory(vga_mm_read_ctrl, vga_mm_write_ctrl, s); - vga_io_memory = cpu_register_io_memory(vga_mem_read, vga_mem_write, s); + s_ioport_ctrl = cpu_register_io_memory(vga_mm_read_ctrl, vga_mm_write_ctrl, s, + DEVICE_NATIVE_ENDIAN); + vga_io_memory = cpu_register_io_memory(vga_mem_read, vga_mem_write, s, + DEVICE_NATIVE_ENDIAN); - vmstate_register(0, &vmstate_vga_common, s); + vmstate_register(NULL, 0, &vmstate_vga_common, s); cpu_register_physical_memory(ctrl_base, 0x100000, s_ioport_ctrl); s->vga.bank_offset = 0; @@ -121,10 +123,6 @@ s->vga.ds = graphic_console_init(s->vga.update, s->vga.invalidate, s->vga.screen_dump, s->vga.text_update, s); -#ifdef CONFIG_BOCHS_VBE - /* XXX: use optimized standard vga accesses */ - cpu_register_physical_memory(VBE_DISPI_LFB_PHYSICAL_ADDRESS, - VGA_RAM_SIZE, s->vga.vram_offset); -#endif + vga_init_vbe(&s->vga); return 0; } diff -Nru qemu-kvm-0.12.5+noroms/hw/vga-pci.c qemu-kvm-0.14.1/hw/vga-pci.c --- qemu-kvm-0.12.5+noroms/hw/vga-pci.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/vga-pci.c 2011-05-11 13:29:46.000000000 +0000 @@ -52,14 +52,11 @@ { PCIVGAState *d = (PCIVGAState *)pci_dev; VGACommonState *s = &d->vga; - if (region_num == PCI_ROM_SLOT) { - cpu_register_physical_memory(addr, s->bios_size, s->bios_offset); - } else { - cpu_register_physical_memory(addr, s->vram_size, s->vram_offset); - s->map_addr = addr; - s->map_end = addr + s->vram_size; - vga_dirty_log_start(s); - } + + cpu_register_physical_memory(addr, s->vram_size, s->vram_offset); + s->map_addr = addr; + s->map_end = addr + s->vram_size; + vga_dirty_log_start(s); } static void pci_vga_write_config(PCIDevice *d, @@ -92,38 +89,22 @@ pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_QEMU); pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_QEMU_VGA); pci_config_set_class(pci_conf, PCI_CLASS_DISPLAY_VGA); - pci_conf[PCI_HEADER_TYPE] = PCI_HEADER_TYPE_NORMAL; // header_type /* XXX: VGA_RAM_SIZE must be a power of two */ pci_register_bar(&d->dev, 0, VGA_RAM_SIZE, PCI_BASE_ADDRESS_MEM_PREFETCH, vga_map); - if (s->bios_size) { - unsigned int bios_total_size; - /* must be a power of two */ - bios_total_size = 1; - while (bios_total_size < s->bios_size) - bios_total_size <<= 1; - pci_register_bar(&d->dev, PCI_ROM_SLOT, bios_total_size, - PCI_BASE_ADDRESS_MEM_PREFETCH, vga_map); + if (!dev->rom_bar) { + /* compatibility with pc-0.13 and older */ + vga_init_vbe(s); } - vga_init_vbe(s); - /* ROM BIOS */ - rom_add_vga(VGABIOS_FILENAME); return 0; } -int pci_vga_init(PCIBus *bus, - unsigned long vga_bios_offset, int vga_bios_size) +int pci_vga_init(PCIBus *bus) { - PCIDevice *dev; - - dev = pci_create(bus, -1, "VGA"); - qdev_prop_set_uint32(&dev->qdev, "bios-offset", vga_bios_offset); - qdev_prop_set_uint32(&dev->qdev, "bios-size", vga_bios_size); - qdev_init_nofail(&dev->qdev); - + pci_create_simple(bus, -1, "VGA"); return 0; } @@ -131,13 +112,10 @@ .qdev.name = "VGA", .qdev.size = sizeof(PCIVGAState), .qdev.vmsd = &vmstate_vga_pci, + .no_hotplug = 1, .init = pci_vga_initfn, .config_write = pci_vga_write_config, - .qdev.props = (Property[]) { - DEFINE_PROP_HEX32("bios-offset", PCIVGAState, vga.bios_offset, 0), - DEFINE_PROP_HEX32("bios-size", PCIVGAState, vga.bios_size, 0), - DEFINE_PROP_END_OF_LIST(), - } + .romfile = "vgabios-stdvga.bin", }; static void vga_register(void) diff -Nru qemu-kvm-0.12.5+noroms/hw/vhost.c qemu-kvm-0.14.1/hw/vhost.c --- qemu-kvm-0.12.5+noroms/hw/vhost.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/vhost.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,727 @@ +/* + * vhost support + * + * Copyright Red Hat, Inc. 2010 + * + * Authors: + * Michael S. Tsirkin + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + */ + +#include +#include "vhost.h" +#include "hw/hw.h" +#include "range.h" +#include + +static void vhost_dev_sync_region(struct vhost_dev *dev, + uint64_t mfirst, uint64_t mlast, + uint64_t rfirst, uint64_t rlast) +{ + uint64_t start = MAX(mfirst, rfirst); + uint64_t end = MIN(mlast, rlast); + vhost_log_chunk_t *from = dev->log + start / VHOST_LOG_CHUNK; + vhost_log_chunk_t *to = dev->log + end / VHOST_LOG_CHUNK + 1; + uint64_t addr = (start / VHOST_LOG_CHUNK) * VHOST_LOG_CHUNK; + + assert(end / VHOST_LOG_CHUNK < dev->log_size); + assert(start / VHOST_LOG_CHUNK < dev->log_size); + if (end < start) { + return; + } + for (;from < to; ++from) { + vhost_log_chunk_t log; + int bit; + /* We first check with non-atomic: much cheaper, + * and we expect non-dirty to be the common case. */ + if (!*from) { + addr += VHOST_LOG_CHUNK; + continue; + } + /* Data must be read atomically. We don't really + * need the barrier semantics of __sync + * builtins, but it's easier to use them than + * roll our own. */ + log = __sync_fetch_and_and(from, 0); + while ((bit = sizeof(log) > sizeof(int) ? + ffsll(log) : ffs(log))) { + ram_addr_t ram_addr; + bit -= 1; + ram_addr = cpu_get_physical_page_desc(addr + bit * VHOST_LOG_PAGE); + cpu_physical_memory_set_dirty(ram_addr); + log &= ~(0x1ull << bit); + } + addr += VHOST_LOG_CHUNK; + } +} + +static int vhost_client_sync_dirty_bitmap(CPUPhysMemoryClient *client, + target_phys_addr_t start_addr, + target_phys_addr_t end_addr) +{ + struct vhost_dev *dev = container_of(client, struct vhost_dev, client); + int i; + if (!dev->log_enabled || !dev->started) { + return 0; + } + for (i = 0; i < dev->mem->nregions; ++i) { + struct vhost_memory_region *reg = dev->mem->regions + i; + vhost_dev_sync_region(dev, start_addr, end_addr, + reg->guest_phys_addr, + range_get_last(reg->guest_phys_addr, + reg->memory_size)); + } + for (i = 0; i < dev->nvqs; ++i) { + struct vhost_virtqueue *vq = dev->vqs + i; + vhost_dev_sync_region(dev, start_addr, end_addr, vq->used_phys, + range_get_last(vq->used_phys, vq->used_size)); + } + return 0; +} + +/* Assign/unassign. Keep an unsorted array of non-overlapping + * memory regions in dev->mem. */ +static void vhost_dev_unassign_memory(struct vhost_dev *dev, + uint64_t start_addr, + uint64_t size) +{ + int from, to, n = dev->mem->nregions; + /* Track overlapping/split regions for sanity checking. */ + int overlap_start = 0, overlap_end = 0, overlap_middle = 0, split = 0; + + for (from = 0, to = 0; from < n; ++from, ++to) { + struct vhost_memory_region *reg = dev->mem->regions + to; + uint64_t reglast; + uint64_t memlast; + uint64_t change; + + /* clone old region */ + if (to != from) { + memcpy(reg, dev->mem->regions + from, sizeof *reg); + } + + /* No overlap is simple */ + if (!ranges_overlap(reg->guest_phys_addr, reg->memory_size, + start_addr, size)) { + continue; + } + + /* Split only happens if supplied region + * is in the middle of an existing one. Thus it can not + * overlap with any other existing region. */ + assert(!split); + + reglast = range_get_last(reg->guest_phys_addr, reg->memory_size); + memlast = range_get_last(start_addr, size); + + /* Remove whole region */ + if (start_addr <= reg->guest_phys_addr && memlast >= reglast) { + --dev->mem->nregions; + --to; + assert(to >= 0); + ++overlap_middle; + continue; + } + + /* Shrink region */ + if (memlast >= reglast) { + reg->memory_size = start_addr - reg->guest_phys_addr; + assert(reg->memory_size); + assert(!overlap_end); + ++overlap_end; + continue; + } + + /* Shift region */ + if (start_addr <= reg->guest_phys_addr) { + change = memlast + 1 - reg->guest_phys_addr; + reg->memory_size -= change; + reg->guest_phys_addr += change; + reg->userspace_addr += change; + assert(reg->memory_size); + assert(!overlap_start); + ++overlap_start; + continue; + } + + /* This only happens if supplied region + * is in the middle of an existing one. Thus it can not + * overlap with any other existing region. */ + assert(!overlap_start); + assert(!overlap_end); + assert(!overlap_middle); + /* Split region: shrink first part, shift second part. */ + memcpy(dev->mem->regions + n, reg, sizeof *reg); + reg->memory_size = start_addr - reg->guest_phys_addr; + assert(reg->memory_size); + change = memlast + 1 - reg->guest_phys_addr; + reg = dev->mem->regions + n; + reg->memory_size -= change; + assert(reg->memory_size); + reg->guest_phys_addr += change; + reg->userspace_addr += change; + /* Never add more than 1 region */ + assert(dev->mem->nregions == n); + ++dev->mem->nregions; + ++split; + } +} + +/* Called after unassign, so no regions overlap the given range. */ +static void vhost_dev_assign_memory(struct vhost_dev *dev, + uint64_t start_addr, + uint64_t size, + uint64_t uaddr) +{ + int from, to; + struct vhost_memory_region *merged = NULL; + for (from = 0, to = 0; from < dev->mem->nregions; ++from, ++to) { + struct vhost_memory_region *reg = dev->mem->regions + to; + uint64_t prlast, urlast; + uint64_t pmlast, umlast; + uint64_t s, e, u; + + /* clone old region */ + if (to != from) { + memcpy(reg, dev->mem->regions + from, sizeof *reg); + } + prlast = range_get_last(reg->guest_phys_addr, reg->memory_size); + pmlast = range_get_last(start_addr, size); + urlast = range_get_last(reg->userspace_addr, reg->memory_size); + umlast = range_get_last(uaddr, size); + + /* check for overlapping regions: should never happen. */ + assert(prlast < start_addr || pmlast < reg->guest_phys_addr); + /* Not an adjacent or overlapping region - do not merge. */ + if ((prlast + 1 != start_addr || urlast + 1 != uaddr) && + (pmlast + 1 != reg->guest_phys_addr || + umlast + 1 != reg->userspace_addr)) { + continue; + } + + if (merged) { + --to; + assert(to >= 0); + } else { + merged = reg; + } + u = MIN(uaddr, reg->userspace_addr); + s = MIN(start_addr, reg->guest_phys_addr); + e = MAX(pmlast, prlast); + uaddr = merged->userspace_addr = u; + start_addr = merged->guest_phys_addr = s; + size = merged->memory_size = e - s + 1; + assert(merged->memory_size); + } + + if (!merged) { + struct vhost_memory_region *reg = dev->mem->regions + to; + memset(reg, 0, sizeof *reg); + reg->memory_size = size; + assert(reg->memory_size); + reg->guest_phys_addr = start_addr; + reg->userspace_addr = uaddr; + ++to; + } + assert(to <= dev->mem->nregions + 1); + dev->mem->nregions = to; +} + +static uint64_t vhost_get_log_size(struct vhost_dev *dev) +{ + uint64_t log_size = 0; + int i; + for (i = 0; i < dev->mem->nregions; ++i) { + struct vhost_memory_region *reg = dev->mem->regions + i; + uint64_t last = range_get_last(reg->guest_phys_addr, + reg->memory_size); + log_size = MAX(log_size, last / VHOST_LOG_CHUNK + 1); + } + for (i = 0; i < dev->nvqs; ++i) { + struct vhost_virtqueue *vq = dev->vqs + i; + uint64_t last = vq->used_phys + vq->used_size - 1; + log_size = MAX(log_size, last / VHOST_LOG_CHUNK + 1); + } + return log_size; +} + +static inline void vhost_dev_log_resize(struct vhost_dev* dev, uint64_t size) +{ + vhost_log_chunk_t *log; + uint64_t log_base; + int r; + if (size) { + log = qemu_mallocz(size * sizeof *log); + } else { + log = NULL; + } + log_base = (uint64_t)(unsigned long)log; + r = ioctl(dev->control, VHOST_SET_LOG_BASE, &log_base); + assert(r >= 0); + vhost_client_sync_dirty_bitmap(&dev->client, 0, + (target_phys_addr_t)~0x0ull); + if (dev->log) { + qemu_free(dev->log); + } + dev->log = log; + dev->log_size = size; +} + +static int vhost_verify_ring_mappings(struct vhost_dev *dev, + uint64_t start_addr, + uint64_t size) +{ + int i; + for (i = 0; i < dev->nvqs; ++i) { + struct vhost_virtqueue *vq = dev->vqs + i; + target_phys_addr_t l; + void *p; + + if (!ranges_overlap(start_addr, size, vq->ring_phys, vq->ring_size)) { + continue; + } + l = vq->ring_size; + p = cpu_physical_memory_map(vq->ring_phys, &l, 1); + if (!p || l != vq->ring_size) { + fprintf(stderr, "Unable to map ring buffer for ring %d\n", i); + return -ENOMEM; + } + if (p != vq->ring) { + fprintf(stderr, "Ring buffer relocated for ring %d\n", i); + return -EBUSY; + } + cpu_physical_memory_unmap(p, l, 0, 0); + } + return 0; +} + +static void vhost_client_set_memory(CPUPhysMemoryClient *client, + target_phys_addr_t start_addr, + ram_addr_t size, + ram_addr_t phys_offset) +{ + struct vhost_dev *dev = container_of(client, struct vhost_dev, client); + ram_addr_t flags = phys_offset & ~TARGET_PAGE_MASK; + int s = offsetof(struct vhost_memory, regions) + + (dev->mem->nregions + 1) * sizeof dev->mem->regions[0]; + uint64_t log_size; + int r; + dev->mem = qemu_realloc(dev->mem, s); + + assert(size); + + vhost_dev_unassign_memory(dev, start_addr, size); + if (flags == IO_MEM_RAM) { + /* Add given mapping, merging adjacent regions if any */ + vhost_dev_assign_memory(dev, start_addr, size, + (uintptr_t)qemu_get_ram_ptr(phys_offset)); + } else { + /* Remove old mapping for this memory, if any. */ + vhost_dev_unassign_memory(dev, start_addr, size); + } + + if (!dev->started) { + return; + } + + if (dev->started) { + r = vhost_verify_ring_mappings(dev, start_addr, size); + assert(r >= 0); + } + + if (!dev->log_enabled) { + r = ioctl(dev->control, VHOST_SET_MEM_TABLE, dev->mem); + assert(r >= 0); + return; + } + log_size = vhost_get_log_size(dev); + /* We allocate an extra 4K bytes to log, + * to reduce the * number of reallocations. */ +#define VHOST_LOG_BUFFER (0x1000 / sizeof *dev->log) + /* To log more, must increase log size before table update. */ + if (dev->log_size < log_size) { + vhost_dev_log_resize(dev, log_size + VHOST_LOG_BUFFER); + } + r = ioctl(dev->control, VHOST_SET_MEM_TABLE, dev->mem); + assert(r >= 0); + /* To log less, can only decrease log size after table update. */ + if (dev->log_size > log_size + VHOST_LOG_BUFFER) { + vhost_dev_log_resize(dev, log_size); + } +} + +static int vhost_virtqueue_set_addr(struct vhost_dev *dev, + struct vhost_virtqueue *vq, + unsigned idx, bool enable_log) +{ + struct vhost_vring_addr addr = { + .index = idx, + .desc_user_addr = (uint64_t)(unsigned long)vq->desc, + .avail_user_addr = (uint64_t)(unsigned long)vq->avail, + .used_user_addr = (uint64_t)(unsigned long)vq->used, + .log_guest_addr = vq->used_phys, + .flags = enable_log ? (1 << VHOST_VRING_F_LOG) : 0, + }; + int r = ioctl(dev->control, VHOST_SET_VRING_ADDR, &addr); + if (r < 0) { + return -errno; + } + return 0; +} + +static int vhost_dev_set_features(struct vhost_dev *dev, bool enable_log) +{ + uint64_t features = dev->acked_features; + int r; + if (enable_log) { + features |= 0x1 << VHOST_F_LOG_ALL; + } + r = ioctl(dev->control, VHOST_SET_FEATURES, &features); + return r < 0 ? -errno : 0; +} + +static int vhost_dev_set_log(struct vhost_dev *dev, bool enable_log) +{ + int r, t, i; + r = vhost_dev_set_features(dev, enable_log); + if (r < 0) { + goto err_features; + } + for (i = 0; i < dev->nvqs; ++i) { + r = vhost_virtqueue_set_addr(dev, dev->vqs + i, i, + enable_log); + if (r < 0) { + goto err_vq; + } + } + return 0; +err_vq: + for (; i >= 0; --i) { + t = vhost_virtqueue_set_addr(dev, dev->vqs + i, i, + dev->log_enabled); + assert(t >= 0); + } + t = vhost_dev_set_features(dev, dev->log_enabled); + assert(t >= 0); +err_features: + return r; +} + +static int vhost_client_migration_log(CPUPhysMemoryClient *client, + int enable) +{ + struct vhost_dev *dev = container_of(client, struct vhost_dev, client); + int r; + if (!!enable == dev->log_enabled) { + return 0; + } + if (!dev->started) { + dev->log_enabled = enable; + return 0; + } + if (!enable) { + r = vhost_dev_set_log(dev, false); + if (r < 0) { + return r; + } + if (dev->log) { + qemu_free(dev->log); + } + dev->log = NULL; + dev->log_size = 0; + } else { + vhost_dev_log_resize(dev, vhost_get_log_size(dev)); + r = vhost_dev_set_log(dev, true); + if (r < 0) { + return r; + } + } + dev->log_enabled = enable; + return 0; +} + +static int vhost_virtqueue_init(struct vhost_dev *dev, + struct VirtIODevice *vdev, + struct vhost_virtqueue *vq, + unsigned idx) +{ + target_phys_addr_t s, l, a; + int r; + struct vhost_vring_file file = { + .index = idx, + }; + struct vhost_vring_state state = { + .index = idx, + }; + struct VirtQueue *vvq = virtio_get_queue(vdev, idx); + + if (!vdev->binding->set_host_notifier) { + fprintf(stderr, "binding does not support host notifiers\n"); + return -ENOSYS; + } + + vq->num = state.num = virtio_queue_get_num(vdev, idx); + r = ioctl(dev->control, VHOST_SET_VRING_NUM, &state); + if (r) { + return -errno; + } + + state.num = virtio_queue_get_last_avail_idx(vdev, idx); + r = ioctl(dev->control, VHOST_SET_VRING_BASE, &state); + if (r) { + return -errno; + } + + s = l = virtio_queue_get_desc_size(vdev, idx); + a = virtio_queue_get_desc_addr(vdev, idx); + vq->desc = cpu_physical_memory_map(a, &l, 0); + if (!vq->desc || l != s) { + r = -ENOMEM; + goto fail_alloc_desc; + } + s = l = virtio_queue_get_avail_size(vdev, idx); + a = virtio_queue_get_avail_addr(vdev, idx); + vq->avail = cpu_physical_memory_map(a, &l, 0); + if (!vq->avail || l != s) { + r = -ENOMEM; + goto fail_alloc_avail; + } + vq->used_size = s = l = virtio_queue_get_used_size(vdev, idx); + vq->used_phys = a = virtio_queue_get_used_addr(vdev, idx); + vq->used = cpu_physical_memory_map(a, &l, 1); + if (!vq->used || l != s) { + r = -ENOMEM; + goto fail_alloc_used; + } + + vq->ring_size = s = l = virtio_queue_get_ring_size(vdev, idx); + vq->ring_phys = a = virtio_queue_get_ring_addr(vdev, idx); + vq->ring = cpu_physical_memory_map(a, &l, 1); + if (!vq->ring || l != s) { + r = -ENOMEM; + goto fail_alloc_ring; + } + + r = vhost_virtqueue_set_addr(dev, vq, idx, dev->log_enabled); + if (r < 0) { + r = -errno; + goto fail_alloc; + } + r = vdev->binding->set_host_notifier(vdev->binding_opaque, idx, true); + if (r < 0) { + fprintf(stderr, "Error binding host notifier: %d\n", -r); + goto fail_host_notifier; + } + + file.fd = event_notifier_get_fd(virtio_queue_get_host_notifier(vvq)); + r = ioctl(dev->control, VHOST_SET_VRING_KICK, &file); + if (r) { + r = -errno; + goto fail_kick; + } + + file.fd = event_notifier_get_fd(virtio_queue_get_guest_notifier(vvq)); + r = ioctl(dev->control, VHOST_SET_VRING_CALL, &file); + if (r) { + r = -errno; + goto fail_call; + } + + return 0; + +fail_call: +fail_kick: + vdev->binding->set_host_notifier(vdev->binding_opaque, idx, false); +fail_host_notifier: +fail_alloc: + cpu_physical_memory_unmap(vq->ring, virtio_queue_get_ring_size(vdev, idx), + 0, 0); +fail_alloc_ring: + cpu_physical_memory_unmap(vq->used, virtio_queue_get_used_size(vdev, idx), + 0, 0); +fail_alloc_used: + cpu_physical_memory_unmap(vq->avail, virtio_queue_get_avail_size(vdev, idx), + 0, 0); +fail_alloc_avail: + cpu_physical_memory_unmap(vq->desc, virtio_queue_get_desc_size(vdev, idx), + 0, 0); +fail_alloc_desc: + return r; +} + +static void vhost_virtqueue_cleanup(struct vhost_dev *dev, + struct VirtIODevice *vdev, + struct vhost_virtqueue *vq, + unsigned idx) +{ + struct vhost_vring_state state = { + .index = idx, + }; + int r; + r = vdev->binding->set_host_notifier(vdev->binding_opaque, idx, false); + if (r < 0) { + fprintf(stderr, "vhost VQ %d host cleanup failed: %d\n", idx, r); + fflush(stderr); + } + assert (r >= 0); + r = ioctl(dev->control, VHOST_GET_VRING_BASE, &state); + if (r < 0) { + fprintf(stderr, "vhost VQ %d ring restore failed: %d\n", idx, r); + fflush(stderr); + } + virtio_queue_set_last_avail_idx(vdev, idx, state.num); + assert (r >= 0); + cpu_physical_memory_unmap(vq->ring, virtio_queue_get_ring_size(vdev, idx), + 0, virtio_queue_get_ring_size(vdev, idx)); + cpu_physical_memory_unmap(vq->used, virtio_queue_get_used_size(vdev, idx), + 1, virtio_queue_get_used_size(vdev, idx)); + cpu_physical_memory_unmap(vq->avail, virtio_queue_get_avail_size(vdev, idx), + 0, virtio_queue_get_avail_size(vdev, idx)); + cpu_physical_memory_unmap(vq->desc, virtio_queue_get_desc_size(vdev, idx), + 0, virtio_queue_get_desc_size(vdev, idx)); +} + +int vhost_dev_init(struct vhost_dev *hdev, int devfd, bool force) +{ + uint64_t features; + int r; + if (devfd >= 0) { + hdev->control = devfd; + } else { + hdev->control = open("/dev/vhost-net", O_RDWR); + if (hdev->control < 0) { + return -errno; + } + } + r = ioctl(hdev->control, VHOST_SET_OWNER, NULL); + if (r < 0) { + goto fail; + } + + r = ioctl(hdev->control, VHOST_GET_FEATURES, &features); + if (r < 0) { + goto fail; + } + hdev->features = features; + + hdev->client.set_memory = vhost_client_set_memory; + hdev->client.sync_dirty_bitmap = vhost_client_sync_dirty_bitmap; + hdev->client.migration_log = vhost_client_migration_log; + hdev->mem = qemu_mallocz(offsetof(struct vhost_memory, regions)); + hdev->log = NULL; + hdev->log_size = 0; + hdev->log_enabled = false; + hdev->started = false; + cpu_register_phys_memory_client(&hdev->client); + hdev->force = force; + return 0; +fail: + r = -errno; + close(hdev->control); + return r; +} + +void vhost_dev_cleanup(struct vhost_dev *hdev) +{ + cpu_unregister_phys_memory_client(&hdev->client); + qemu_free(hdev->mem); + close(hdev->control); +} + +bool vhost_dev_query(struct vhost_dev *hdev, VirtIODevice *vdev) +{ + return !vdev->binding->query_guest_notifiers || + vdev->binding->query_guest_notifiers(vdev->binding_opaque) || + hdev->force; +} + +int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev) +{ + int i, r; + if (!vdev->binding->set_guest_notifiers) { + fprintf(stderr, "binding does not support guest notifiers\n"); + r = -ENOSYS; + goto fail; + } + + r = vdev->binding->set_guest_notifiers(vdev->binding_opaque, true); + if (r < 0) { + fprintf(stderr, "Error binding guest notifier: %d\n", -r); + goto fail_notifiers; + } + + r = vhost_dev_set_features(hdev, hdev->log_enabled); + if (r < 0) { + goto fail_features; + } + r = ioctl(hdev->control, VHOST_SET_MEM_TABLE, hdev->mem); + if (r < 0) { + r = -errno; + goto fail_mem; + } + for (i = 0; i < hdev->nvqs; ++i) { + r = vhost_virtqueue_init(hdev, + vdev, + hdev->vqs + i, + i); + if (r < 0) { + goto fail_vq; + } + } + + if (hdev->log_enabled) { + hdev->log_size = vhost_get_log_size(hdev); + hdev->log = hdev->log_size ? + qemu_mallocz(hdev->log_size * sizeof *hdev->log) : NULL; + r = ioctl(hdev->control, VHOST_SET_LOG_BASE, + (uint64_t)(unsigned long)hdev->log); + if (r < 0) { + r = -errno; + goto fail_log; + } + } + + hdev->started = true; + + return 0; +fail_log: +fail_vq: + while (--i >= 0) { + vhost_virtqueue_cleanup(hdev, + vdev, + hdev->vqs + i, + i); + } +fail_mem: +fail_features: + vdev->binding->set_guest_notifiers(vdev->binding_opaque, false); +fail_notifiers: +fail: + return r; +} + +void vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev) +{ + int i, r; + + for (i = 0; i < hdev->nvqs; ++i) { + vhost_virtqueue_cleanup(hdev, + vdev, + hdev->vqs + i, + i); + } + vhost_client_sync_dirty_bitmap(&hdev->client, 0, + (target_phys_addr_t)~0x0ull); + r = vdev->binding->set_guest_notifiers(vdev->binding_opaque, false); + if (r < 0) { + fprintf(stderr, "vhost guest notifier cleanup failed: %d\n", r); + fflush(stderr); + } + assert (r >= 0); + + hdev->started = false; + qemu_free(hdev->log); + hdev->log_size = 0; +} diff -Nru qemu-kvm-0.12.5+noroms/hw/vhost.h qemu-kvm-0.14.1/hw/vhost.h --- qemu-kvm-0.12.5+noroms/hw/vhost.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/vhost.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,50 @@ +#ifndef VHOST_H +#define VHOST_H + +#include "hw/hw.h" +#include "hw/virtio.h" + +/* Generic structures common for any vhost based device. */ +struct vhost_virtqueue { + int kick; + int call; + void *desc; + void *avail; + void *used; + int num; + unsigned long long used_phys; + unsigned used_size; + void *ring; + unsigned long long ring_phys; + unsigned ring_size; +}; + +typedef unsigned long vhost_log_chunk_t; +#define VHOST_LOG_PAGE 0x1000 +#define VHOST_LOG_BITS (8 * sizeof(vhost_log_chunk_t)) +#define VHOST_LOG_CHUNK (VHOST_LOG_PAGE * VHOST_LOG_BITS) + +struct vhost_memory; +struct vhost_dev { + CPUPhysMemoryClient client; + int control; + struct vhost_memory *mem; + struct vhost_virtqueue *vqs; + int nvqs; + unsigned long long features; + unsigned long long acked_features; + unsigned long long backend_features; + bool started; + bool log_enabled; + vhost_log_chunk_t *log; + unsigned long long log_size; + bool force; +}; + +int vhost_dev_init(struct vhost_dev *hdev, int devfd, bool force); +void vhost_dev_cleanup(struct vhost_dev *hdev); +bool vhost_dev_query(struct vhost_dev *hdev, VirtIODevice *vdev); +int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev); +void vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev); + +#endif diff -Nru qemu-kvm-0.12.5+noroms/hw/vhost_net.c qemu-kvm-0.14.1/hw/vhost_net.c --- qemu-kvm-0.12.5+noroms/hw/vhost_net.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/vhost_net.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,229 @@ +/* + * vhost-net support + * + * Copyright Red Hat, Inc. 2010 + * + * Authors: + * Michael S. Tsirkin + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + */ + +#include "net.h" +#include "net/tap.h" + +#include "virtio-net.h" +#include "vhost_net.h" + +#include "config.h" + +#ifdef CONFIG_VHOST_NET +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "vhost.h" + +struct vhost_net { + struct vhost_dev dev; + struct vhost_virtqueue vqs[2]; + int backend; + VLANClientState *vc; +}; + +unsigned vhost_net_get_features(struct vhost_net *net, unsigned features) +{ + /* Clear features not supported by host kernel. */ + if (!(net->dev.features & (1 << VIRTIO_F_NOTIFY_ON_EMPTY))) { + features &= ~(1 << VIRTIO_F_NOTIFY_ON_EMPTY); + } + if (!(net->dev.features & (1 << VIRTIO_RING_F_INDIRECT_DESC))) { + features &= ~(1 << VIRTIO_RING_F_INDIRECT_DESC); + } + if (!(net->dev.features & (1 << VIRTIO_NET_F_MRG_RXBUF))) { + features &= ~(1 << VIRTIO_NET_F_MRG_RXBUF); + } + return features; +} + +void vhost_net_ack_features(struct vhost_net *net, unsigned features) +{ + net->dev.acked_features = net->dev.backend_features; + if (features & (1 << VIRTIO_F_NOTIFY_ON_EMPTY)) { + net->dev.acked_features |= (1 << VIRTIO_F_NOTIFY_ON_EMPTY); + } + if (features & (1 << VIRTIO_RING_F_INDIRECT_DESC)) { + net->dev.acked_features |= (1 << VIRTIO_RING_F_INDIRECT_DESC); + } + if (features & (1 << VIRTIO_NET_F_MRG_RXBUF)) { + net->dev.acked_features |= (1 << VIRTIO_NET_F_MRG_RXBUF); + } +} + +static int vhost_net_get_fd(VLANClientState *backend) +{ + switch (backend->info->type) { + case NET_CLIENT_TYPE_TAP: + return tap_get_fd(backend); + default: + fprintf(stderr, "vhost-net requires tap backend\n"); + return -EBADFD; + } +} + +struct vhost_net *vhost_net_init(VLANClientState *backend, int devfd, + bool force) +{ + int r; + struct vhost_net *net = qemu_malloc(sizeof *net); + if (!backend) { + fprintf(stderr, "vhost-net requires backend to be setup\n"); + goto fail; + } + r = vhost_net_get_fd(backend); + if (r < 0) { + goto fail; + } + net->vc = backend; + net->dev.backend_features = tap_has_vnet_hdr(backend) ? 0 : + (1 << VHOST_NET_F_VIRTIO_NET_HDR); + net->backend = r; + + r = vhost_dev_init(&net->dev, devfd, force); + if (r < 0) { + goto fail; + } + if (!tap_has_vnet_hdr_len(backend, + sizeof(struct virtio_net_hdr_mrg_rxbuf))) { + net->dev.features &= ~(1 << VIRTIO_NET_F_MRG_RXBUF); + } + if (~net->dev.features & net->dev.backend_features) { + fprintf(stderr, "vhost lacks feature mask %" PRIu64 " for backend\n", + (uint64_t)(~net->dev.features & net->dev.backend_features)); + vhost_dev_cleanup(&net->dev); + goto fail; + } + + /* Set sane init value. Override when guest acks. */ + vhost_net_ack_features(net, 0); + return net; +fail: + qemu_free(net); + return NULL; +} + +bool vhost_net_query(VHostNetState *net, VirtIODevice *dev) +{ + return vhost_dev_query(&net->dev, dev); +} + +int vhost_net_start(struct vhost_net *net, + VirtIODevice *dev) +{ + struct vhost_vring_file file = { }; + int r; + if (net->dev.acked_features & (1 << VIRTIO_NET_F_MRG_RXBUF)) { + tap_set_vnet_hdr_len(net->vc, + sizeof(struct virtio_net_hdr_mrg_rxbuf)); + } + + net->dev.nvqs = 2; + net->dev.vqs = net->vqs; + r = vhost_dev_start(&net->dev, dev); + if (r < 0) { + return r; + } + + net->vc->info->poll(net->vc, false); + qemu_set_fd_handler(net->backend, NULL, NULL, NULL); + file.fd = net->backend; + for (file.index = 0; file.index < net->dev.nvqs; ++file.index) { + r = ioctl(net->dev.control, VHOST_NET_SET_BACKEND, &file); + if (r < 0) { + r = -errno; + goto fail; + } + } + return 0; +fail: + file.fd = -1; + while (file.index-- > 0) { + int r = ioctl(net->dev.control, VHOST_NET_SET_BACKEND, &file); + assert(r >= 0); + } + net->vc->info->poll(net->vc, true); + vhost_dev_stop(&net->dev, dev); + if (net->dev.acked_features & (1 << VIRTIO_NET_F_MRG_RXBUF)) { + tap_set_vnet_hdr_len(net->vc, sizeof(struct virtio_net_hdr)); + } + return r; +} + +void vhost_net_stop(struct vhost_net *net, + VirtIODevice *dev) +{ + struct vhost_vring_file file = { .fd = -1 }; + + for (file.index = 0; file.index < net->dev.nvqs; ++file.index) { + int r = ioctl(net->dev.control, VHOST_NET_SET_BACKEND, &file); + assert(r >= 0); + } + net->vc->info->poll(net->vc, true); + vhost_dev_stop(&net->dev, dev); + if (net->dev.acked_features & (1 << VIRTIO_NET_F_MRG_RXBUF)) { + tap_set_vnet_hdr_len(net->vc, sizeof(struct virtio_net_hdr)); + } +} + +void vhost_net_cleanup(struct vhost_net *net) +{ + vhost_dev_cleanup(&net->dev); + if (net->dev.acked_features & (1 << VIRTIO_NET_F_MRG_RXBUF)) { + tap_set_vnet_hdr_len(net->vc, sizeof(struct virtio_net_hdr)); + } + qemu_free(net); +} +#else +struct vhost_net *vhost_net_init(VLANClientState *backend, int devfd, + bool force) +{ + return NULL; +} + +bool vhost_net_query(VHostNetState *net, VirtIODevice *dev) +{ + return false; +} + +int vhost_net_start(struct vhost_net *net, + VirtIODevice *dev) +{ + return -ENOSYS; +} +void vhost_net_stop(struct vhost_net *net, + VirtIODevice *dev) +{ +} + +void vhost_net_cleanup(struct vhost_net *net) +{ +} + +unsigned vhost_net_get_features(struct vhost_net *net, unsigned features) +{ + return features; +} +void vhost_net_ack_features(struct vhost_net *net, unsigned features) +{ +} +#endif diff -Nru qemu-kvm-0.12.5+noroms/hw/vhost_net.h qemu-kvm-0.14.1/hw/vhost_net.h --- qemu-kvm-0.12.5+noroms/hw/vhost_net.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/vhost_net.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,20 @@ +#ifndef VHOST_NET_H +#define VHOST_NET_H + +#include "net.h" + +struct vhost_net; +typedef struct vhost_net VHostNetState; + +VHostNetState *vhost_net_init(VLANClientState *backend, int devfd, bool force); + +bool vhost_net_query(VHostNetState *net, VirtIODevice *dev); +int vhost_net_start(VHostNetState *net, VirtIODevice *dev); +void vhost_net_stop(VHostNetState *net, VirtIODevice *dev); + +void vhost_net_cleanup(VHostNetState *net); + +unsigned vhost_net_get_features(VHostNetState *net, unsigned features); +void vhost_net_ack_features(VHostNetState *net, unsigned features); + +#endif diff -Nru qemu-kvm-0.12.5+noroms/hw/virtex_ml507.c qemu-kvm-0.14.1/hw/virtex_ml507.c --- qemu-kvm-0.12.5+noroms/hw/virtex_ml507.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/virtex_ml507.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,276 @@ +/* + * Model of Xilinx Virtex5 ML507 PPC-440 refdesign. + * + * Copyright (c) 2010 Edgar E. Iglesias. + * + * 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 "sysbus.h" +#include "hw.h" +#include "pc.h" +#include "net.h" +#include "flash.h" +#include "sysemu.h" +#include "devices.h" +#include "boards.h" +#include "device_tree.h" +#include "loader.h" +#include "elf.h" +#include "qemu-log.h" + +#include "ppc.h" +#include "ppc4xx.h" +#include "ppc440.h" +#include "ppc405.h" + +#include "blockdev.h" +#include "xilinx.h" + +#define EPAPR_MAGIC (0x45504150) +#define FLASH_SIZE (16 * 1024 * 1024) + +static struct boot_info +{ + uint32_t bootstrap_pc; + uint32_t cmdline; + uint32_t fdt; + uint32_t ima_size; + void *vfdt; +} boot_info; + +/* Create reset TLB entries for BookE, spanning the 32bit addr space. */ +static void mmubooke_create_initial_mapping(CPUState *env, + target_ulong va, + target_phys_addr_t pa) +{ + ppcemb_tlb_t *tlb = &env->tlb[0].tlbe; + + tlb->attr = 0; + tlb->prot = PAGE_VALID | ((PAGE_READ | PAGE_WRITE | PAGE_EXEC) << 4); + tlb->size = 1 << 31; /* up to 0x80000000 */ + tlb->EPN = va & TARGET_PAGE_MASK; + tlb->RPN = pa & TARGET_PAGE_MASK; + tlb->PID = 0; + + tlb = &env->tlb[1].tlbe; + tlb->attr = 0; + tlb->prot = PAGE_VALID | ((PAGE_READ | PAGE_WRITE | PAGE_EXEC) << 4); + tlb->size = 1 << 31; /* up to 0xffffffff */ + tlb->EPN = 0x80000000 & TARGET_PAGE_MASK; + tlb->RPN = 0x80000000 & TARGET_PAGE_MASK; + tlb->PID = 0; +} + +static CPUState *ppc440_init_xilinx(ram_addr_t *ram_size, + int do_init, + const char *cpu_model, + clk_setup_t *cpu_clk, clk_setup_t *tb_clk, + uint32_t sysclk) +{ + CPUState *env; + qemu_irq *irqs; + + env = cpu_init(cpu_model); + if (!env) { + fprintf(stderr, "Unable to initialize CPU!\n"); + exit(1); + } + + cpu_clk->cb = NULL; /* We don't care about CPU clock frequency changes */ + cpu_clk->opaque = env; + /* Set time-base frequency to sysclk */ + tb_clk->cb = ppc_emb_timers_init(env, sysclk, PPC_INTERRUPT_DECR); + tb_clk->opaque = env; + + ppc_dcr_init(env, NULL, NULL); + + /* interrupt controller */ + irqs = qemu_mallocz(sizeof(qemu_irq) * PPCUIC_OUTPUT_NB); + irqs[PPCUIC_OUTPUT_INT] = ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_INT]; + irqs[PPCUIC_OUTPUT_CINT] = ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_CINT]; + ppcuic_init(env, irqs, 0x0C0, 0, 1); + return env; +} + +static void main_cpu_reset(void *opaque) +{ + CPUState *env = opaque; + struct boot_info *bi = env->load_info; + + cpu_reset(env); + /* Linux Kernel Parameters (passing device tree): + * r3: pointer to the fdt + * r4: 0 + * r5: 0 + * r6: epapr magic + * r7: size of IMA in bytes + * r8: 0 + * r9: 0 + */ + env->gpr[1] = (16<<20) - 8; + /* Provide a device-tree. */ + env->gpr[3] = bi->fdt; + env->nip = bi->bootstrap_pc; + + /* Create a mapping for the kernel. */ + mmubooke_create_initial_mapping(env, 0, 0); + env->gpr[6] = tswap32(EPAPR_MAGIC); + env->gpr[7] = bi->ima_size; +} + +#define BINARY_DEVICE_TREE_FILE "virtex-ml507.dtb" +static int xilinx_load_device_tree(target_phys_addr_t addr, + uint32_t ramsize, + target_phys_addr_t initrd_base, + target_phys_addr_t initrd_size, + const char *kernel_cmdline) +{ + char *path; + int fdt_size; +#ifdef CONFIG_FDT + void *fdt; + int r; + + /* Try the local "ppc.dtb" override. */ + fdt = load_device_tree("ppc.dtb", &fdt_size); + if (!fdt) { + path = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE); + if (path) { + fdt = load_device_tree(path, &fdt_size); + qemu_free(path); + } + if (!fdt) { + return 0; + } + } + + r = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs", kernel_cmdline); + if (r < 0) + fprintf(stderr, "couldn't set /chosen/bootargs\n"); + cpu_physical_memory_write (addr, (void *)fdt, fdt_size); +#else + /* We lack libfdt so we cannot manipulate the fdt. Just pass on the blob + to the kernel. */ + fdt_size = load_image_targphys("ppc.dtb", addr, 0x10000); + if (fdt_size < 0) { + path = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE); + if (path) { + fdt_size = load_image_targphys(path, addr, 0x10000); + qemu_free(path); + } + } + + if (kernel_cmdline) { + fprintf(stderr, + "Warning: missing libfdt, cannot pass cmdline to kernel!\n"); + } +#endif + return fdt_size; +} + +static void virtex_init(ram_addr_t ram_size, + const char *boot_device, + const char *kernel_filename, + const char *kernel_cmdline, + const char *initrd_filename, const char *cpu_model) +{ + DeviceState *dev; + CPUState *env; + target_phys_addr_t ram_base = 0; + DriveInfo *dinfo; + ram_addr_t phys_ram; + ram_addr_t phys_flash; + qemu_irq irq[32], *cpu_irq; + clk_setup_t clk_setup[7]; + int kernel_size; + int i; + + /* init CPUs */ + if (cpu_model == NULL) { + cpu_model = "440-Xilinx"; + } + + memset(clk_setup, 0, sizeof(clk_setup)); + env = ppc440_init_xilinx(&ram_size, 1, cpu_model, &clk_setup[0], + &clk_setup[1], 400000000); + qemu_register_reset(main_cpu_reset, env); + + phys_ram = qemu_ram_alloc(NULL, "ram", ram_size); + cpu_register_physical_memory(ram_base, ram_size, phys_ram | IO_MEM_RAM); + + phys_flash = qemu_ram_alloc(NULL, "virtex.flash", FLASH_SIZE); + dinfo = drive_get(IF_PFLASH, 0, 0); + pflash_cfi01_register(0xfc000000, phys_flash, + dinfo ? dinfo->bdrv : NULL, (64 * 1024), + FLASH_SIZE >> 16, + 1, 0x89, 0x18, 0x0000, 0x0, 1); + + cpu_irq = (qemu_irq *) &env->irq_inputs[PPC40x_INPUT_INT]; + dev = xilinx_intc_create(0x81800000, cpu_irq[0], 0); + for (i = 0; i < 32; i++) { + irq[i] = qdev_get_gpio_in(dev, i); + } + + serial_mm_init(0x83e01003ULL, 2, irq[9], 115200, serial_hds[0], 1, 0); + + /* 2 timers at irq 2 @ 62 Mhz. */ + xilinx_timer_create(0x83c00000, irq[3], 2, 62 * 1000000); + + if (kernel_filename) { + uint64_t entry, low, high; + target_phys_addr_t boot_offset; + + /* Boots a kernel elf binary. */ + kernel_size = load_elf(kernel_filename, NULL, NULL, + &entry, &low, &high, 1, ELF_MACHINE, 0); + boot_info.bootstrap_pc = entry & 0x00ffffff; + + if (kernel_size < 0) { + boot_offset = 0x1200000; + /* If we failed loading ELF's try a raw image. */ + kernel_size = load_image_targphys(kernel_filename, + boot_offset, + ram_size); + boot_info.bootstrap_pc = boot_offset; + high = boot_info.bootstrap_pc + kernel_size + 8192; + } + + boot_info.ima_size = kernel_size; + + /* Provide a device-tree. */ + boot_info.fdt = high + (8192 * 2); + boot_info.fdt &= ~8191; + xilinx_load_device_tree(boot_info.fdt, ram_size, 0, 0, kernel_cmdline); + } + env->load_info = &boot_info; +} + +static QEMUMachine virtex_machine = { + .name = "virtex-ml507", + .desc = "Xilinx Virtex ML507 reference design", + .init = virtex_init, +}; + +static void virtex_machine_init(void) +{ + qemu_register_machine(&virtex_machine); +} + +machine_init(virtex_machine_init); diff -Nru qemu-kvm-0.12.5+noroms/hw/virtio-9p.c qemu-kvm-0.14.1/hw/virtio-9p.c --- qemu-kvm-0.12.5+noroms/hw/virtio-9p.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/virtio-9p.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,3744 @@ +/* + * Virtio 9p backend + * + * Copyright IBM, Corp. 2010 + * + * Authors: + * Anthony Liguori + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ + +#include "virtio.h" +#include "pc.h" +#include "qemu_socket.h" +#include "virtio-9p.h" +#include "fsdev/qemu-fsdev.h" +#include "virtio-9p-debug.h" +#include "virtio-9p-xattr.h" + +int debug_9p_pdu; + +enum { + Oread = 0x00, + Owrite = 0x01, + Ordwr = 0x02, + Oexec = 0x03, + Oexcl = 0x04, + Otrunc = 0x10, + Orexec = 0x20, + Orclose = 0x40, + Oappend = 0x80, +}; + +static int omode_to_uflags(int8_t mode) +{ + int ret = 0; + + switch (mode & 3) { + case Oread: + ret = O_RDONLY; + break; + case Ordwr: + ret = O_RDWR; + break; + case Owrite: + ret = O_WRONLY; + break; + case Oexec: + ret = O_RDONLY; + break; + } + + if (mode & Otrunc) { + ret |= O_TRUNC; + } + + if (mode & Oappend) { + ret |= O_APPEND; + } + + if (mode & Oexcl) { + ret |= O_EXCL; + } + + return ret; +} + +void cred_init(FsCred *credp) +{ + credp->fc_uid = -1; + credp->fc_gid = -1; + credp->fc_mode = -1; + credp->fc_rdev = -1; +} + +static int v9fs_do_lstat(V9fsState *s, V9fsString *path, struct stat *stbuf) +{ + return s->ops->lstat(&s->ctx, path->data, stbuf); +} + +static ssize_t v9fs_do_readlink(V9fsState *s, V9fsString *path, V9fsString *buf) +{ + ssize_t len; + + buf->data = qemu_malloc(1024); + + len = s->ops->readlink(&s->ctx, path->data, buf->data, 1024 - 1); + if (len > -1) { + buf->size = len; + buf->data[len] = 0; + } + + return len; +} + +static int v9fs_do_close(V9fsState *s, int fd) +{ + return s->ops->close(&s->ctx, fd); +} + +static int v9fs_do_closedir(V9fsState *s, DIR *dir) +{ + return s->ops->closedir(&s->ctx, dir); +} + +static int v9fs_do_open(V9fsState *s, V9fsString *path, int flags) +{ + return s->ops->open(&s->ctx, path->data, flags); +} + +static DIR *v9fs_do_opendir(V9fsState *s, V9fsString *path) +{ + return s->ops->opendir(&s->ctx, path->data); +} + +static void v9fs_do_rewinddir(V9fsState *s, DIR *dir) +{ + return s->ops->rewinddir(&s->ctx, dir); +} + +static off_t v9fs_do_telldir(V9fsState *s, DIR *dir) +{ + return s->ops->telldir(&s->ctx, dir); +} + +static struct dirent *v9fs_do_readdir(V9fsState *s, DIR *dir) +{ + return s->ops->readdir(&s->ctx, dir); +} + +static void v9fs_do_seekdir(V9fsState *s, DIR *dir, off_t off) +{ + return s->ops->seekdir(&s->ctx, dir, off); +} + +static int v9fs_do_preadv(V9fsState *s, int fd, const struct iovec *iov, + int iovcnt, int64_t offset) +{ + return s->ops->preadv(&s->ctx, fd, iov, iovcnt, offset); +} + +static int v9fs_do_pwritev(V9fsState *s, int fd, const struct iovec *iov, + int iovcnt, int64_t offset) +{ + return s->ops->pwritev(&s->ctx, fd, iov, iovcnt, offset); +} + +static int v9fs_do_chmod(V9fsState *s, V9fsString *path, mode_t mode) +{ + FsCred cred; + cred_init(&cred); + cred.fc_mode = mode; + return s->ops->chmod(&s->ctx, path->data, &cred); +} + +static int v9fs_do_mknod(V9fsState *s, char *name, + mode_t mode, dev_t dev, uid_t uid, gid_t gid) +{ + FsCred cred; + cred_init(&cred); + cred.fc_uid = uid; + cred.fc_gid = gid; + cred.fc_mode = mode; + cred.fc_rdev = dev; + return s->ops->mknod(&s->ctx, name, &cred); +} + +static int v9fs_do_mkdir(V9fsState *s, char *name, mode_t mode, + uid_t uid, gid_t gid) +{ + FsCred cred; + + cred_init(&cred); + cred.fc_uid = uid; + cred.fc_gid = gid; + cred.fc_mode = mode; + + return s->ops->mkdir(&s->ctx, name, &cred); +} + +static int v9fs_do_fstat(V9fsState *s, int fd, struct stat *stbuf) +{ + return s->ops->fstat(&s->ctx, fd, stbuf); +} + +static int v9fs_do_open2(V9fsState *s, char *fullname, uid_t uid, gid_t gid, + int flags, int mode) +{ + FsCred cred; + + cred_init(&cred); + cred.fc_uid = uid; + cred.fc_gid = gid; + cred.fc_mode = mode & 07777; + flags = flags; + + return s->ops->open2(&s->ctx, fullname, flags, &cred); +} + +static int v9fs_do_symlink(V9fsState *s, V9fsFidState *fidp, + const char *oldpath, const char *newpath, gid_t gid) +{ + FsCred cred; + cred_init(&cred); + cred.fc_uid = fidp->uid; + cred.fc_gid = gid; + cred.fc_mode = 0777; + + return s->ops->symlink(&s->ctx, oldpath, newpath, &cred); +} + +static int v9fs_do_link(V9fsState *s, V9fsString *oldpath, V9fsString *newpath) +{ + return s->ops->link(&s->ctx, oldpath->data, newpath->data); +} + +static int v9fs_do_truncate(V9fsState *s, V9fsString *path, off_t size) +{ + return s->ops->truncate(&s->ctx, path->data, size); +} + +static int v9fs_do_rename(V9fsState *s, V9fsString *oldpath, + V9fsString *newpath) +{ + return s->ops->rename(&s->ctx, oldpath->data, newpath->data); +} + +static int v9fs_do_chown(V9fsState *s, V9fsString *path, uid_t uid, gid_t gid) +{ + FsCred cred; + cred_init(&cred); + cred.fc_uid = uid; + cred.fc_gid = gid; + + return s->ops->chown(&s->ctx, path->data, &cred); +} + +static int v9fs_do_utimensat(V9fsState *s, V9fsString *path, + const struct timespec times[2]) +{ + return s->ops->utimensat(&s->ctx, path->data, times); +} + +static int v9fs_do_remove(V9fsState *s, V9fsString *path) +{ + return s->ops->remove(&s->ctx, path->data); +} + +static int v9fs_do_fsync(V9fsState *s, int fd, int datasync) +{ + return s->ops->fsync(&s->ctx, fd, datasync); +} + +static int v9fs_do_statfs(V9fsState *s, V9fsString *path, struct statfs *stbuf) +{ + return s->ops->statfs(&s->ctx, path->data, stbuf); +} + +static ssize_t v9fs_do_lgetxattr(V9fsState *s, V9fsString *path, + V9fsString *xattr_name, + void *value, size_t size) +{ + return s->ops->lgetxattr(&s->ctx, path->data, + xattr_name->data, value, size); +} + +static ssize_t v9fs_do_llistxattr(V9fsState *s, V9fsString *path, + void *value, size_t size) +{ + return s->ops->llistxattr(&s->ctx, path->data, + value, size); +} + +static int v9fs_do_lsetxattr(V9fsState *s, V9fsString *path, + V9fsString *xattr_name, + void *value, size_t size, int flags) +{ + return s->ops->lsetxattr(&s->ctx, path->data, + xattr_name->data, value, size, flags); +} + +static int v9fs_do_lremovexattr(V9fsState *s, V9fsString *path, + V9fsString *xattr_name) +{ + return s->ops->lremovexattr(&s->ctx, path->data, + xattr_name->data); +} + + +static void v9fs_string_init(V9fsString *str) +{ + str->data = NULL; + str->size = 0; +} + +static void v9fs_string_free(V9fsString *str) +{ + qemu_free(str->data); + str->data = NULL; + str->size = 0; +} + +static void v9fs_string_null(V9fsString *str) +{ + v9fs_string_free(str); +} + +static int number_to_string(void *arg, char type) +{ + unsigned int ret = 0; + + switch (type) { + case 'u': { + unsigned int num = *(unsigned int *)arg; + + do { + ret++; + num = num/10; + } while (num); + break; + } + case 'U': { + unsigned long num = *(unsigned long *)arg; + do { + ret++; + num = num/10; + } while (num); + break; + } + default: + printf("Number_to_string: Unknown number format\n"); + return -1; + } + + return ret; +} + +static int GCC_FMT_ATTR(2, 0) +v9fs_string_alloc_printf(char **strp, const char *fmt, va_list ap) +{ + va_list ap2; + char *iter = (char *)fmt; + int len = 0; + int nr_args = 0; + char *arg_char_ptr; + unsigned int arg_uint; + unsigned long arg_ulong; + + /* Find the number of %'s that denotes an argument */ + for (iter = strstr(iter, "%"); iter; iter = strstr(iter, "%")) { + nr_args++; + iter++; + } + + len = strlen(fmt) - 2*nr_args; + + if (!nr_args) { + goto alloc_print; + } + + va_copy(ap2, ap); + + iter = (char *)fmt; + + /* Now parse the format string */ + for (iter = strstr(iter, "%"); iter; iter = strstr(iter, "%")) { + iter++; + switch (*iter) { + case 'u': + arg_uint = va_arg(ap2, unsigned int); + len += number_to_string((void *)&arg_uint, 'u'); + break; + case 'l': + if (*++iter == 'u') { + arg_ulong = va_arg(ap2, unsigned long); + len += number_to_string((void *)&arg_ulong, 'U'); + } else { + return -1; + } + break; + case 's': + arg_char_ptr = va_arg(ap2, char *); + len += strlen(arg_char_ptr); + break; + case 'c': + len += 1; + break; + default: + fprintf(stderr, + "v9fs_string_alloc_printf:Incorrect format %c", *iter); + return -1; + } + iter++; + } + +alloc_print: + *strp = qemu_malloc((len + 1) * sizeof(**strp)); + + return vsprintf(*strp, fmt, ap); +} + +static void GCC_FMT_ATTR(2, 3) +v9fs_string_sprintf(V9fsString *str, const char *fmt, ...) +{ + va_list ap; + int err; + + v9fs_string_free(str); + + va_start(ap, fmt); + err = v9fs_string_alloc_printf(&str->data, fmt, ap); + BUG_ON(err == -1); + va_end(ap); + + str->size = err; +} + +static void v9fs_string_copy(V9fsString *lhs, V9fsString *rhs) +{ + v9fs_string_free(lhs); + v9fs_string_sprintf(lhs, "%s", rhs->data); +} + +static size_t v9fs_string_size(V9fsString *str) +{ + return str->size; +} + +static V9fsFidState *lookup_fid(V9fsState *s, int32_t fid) +{ + V9fsFidState *f; + + for (f = s->fid_list; f; f = f->next) { + if (f->fid == fid) { + return f; + } + } + + return NULL; +} + +static V9fsFidState *alloc_fid(V9fsState *s, int32_t fid) +{ + V9fsFidState *f; + + f = lookup_fid(s, fid); + if (f) { + return NULL; + } + + f = qemu_mallocz(sizeof(V9fsFidState)); + + f->fid = fid; + f->fid_type = P9_FID_NONE; + + f->next = s->fid_list; + s->fid_list = f; + + return f; +} + +static int v9fs_xattr_fid_clunk(V9fsState *s, V9fsFidState *fidp) +{ + int retval = 0; + + if (fidp->fs.xattr.copied_len == -1) { + /* getxattr/listxattr fid */ + goto free_value; + } + /* + * if this is fid for setxattr. clunk should + * result in setxattr localcall + */ + if (fidp->fs.xattr.len != fidp->fs.xattr.copied_len) { + /* clunk after partial write */ + retval = -EINVAL; + goto free_out; + } + if (fidp->fs.xattr.len) { + retval = v9fs_do_lsetxattr(s, &fidp->path, &fidp->fs.xattr.name, + fidp->fs.xattr.value, + fidp->fs.xattr.len, + fidp->fs.xattr.flags); + } else { + retval = v9fs_do_lremovexattr(s, &fidp->path, &fidp->fs.xattr.name); + } +free_out: + v9fs_string_free(&fidp->fs.xattr.name); +free_value: + if (fidp->fs.xattr.value) { + qemu_free(fidp->fs.xattr.value); + } + return retval; +} + +static int free_fid(V9fsState *s, int32_t fid) +{ + int retval = 0; + V9fsFidState **fidpp, *fidp; + + for (fidpp = &s->fid_list; *fidpp; fidpp = &(*fidpp)->next) { + if ((*fidpp)->fid == fid) { + break; + } + } + + if (*fidpp == NULL) { + return -ENOENT; + } + + fidp = *fidpp; + *fidpp = fidp->next; + + if (fidp->fid_type == P9_FID_FILE) { + v9fs_do_close(s, fidp->fs.fd); + } else if (fidp->fid_type == P9_FID_DIR) { + v9fs_do_closedir(s, fidp->fs.dir); + } else if (fidp->fid_type == P9_FID_XATTR) { + retval = v9fs_xattr_fid_clunk(s, fidp); + } + v9fs_string_free(&fidp->path); + qemu_free(fidp); + + return retval; +} + +#define P9_QID_TYPE_DIR 0x80 +#define P9_QID_TYPE_SYMLINK 0x02 + +#define P9_STAT_MODE_DIR 0x80000000 +#define P9_STAT_MODE_APPEND 0x40000000 +#define P9_STAT_MODE_EXCL 0x20000000 +#define P9_STAT_MODE_MOUNT 0x10000000 +#define P9_STAT_MODE_AUTH 0x08000000 +#define P9_STAT_MODE_TMP 0x04000000 +#define P9_STAT_MODE_SYMLINK 0x02000000 +#define P9_STAT_MODE_LINK 0x01000000 +#define P9_STAT_MODE_DEVICE 0x00800000 +#define P9_STAT_MODE_NAMED_PIPE 0x00200000 +#define P9_STAT_MODE_SOCKET 0x00100000 +#define P9_STAT_MODE_SETUID 0x00080000 +#define P9_STAT_MODE_SETGID 0x00040000 +#define P9_STAT_MODE_SETVTX 0x00010000 + +#define P9_STAT_MODE_TYPE_BITS (P9_STAT_MODE_DIR | \ + P9_STAT_MODE_SYMLINK | \ + P9_STAT_MODE_LINK | \ + P9_STAT_MODE_DEVICE | \ + P9_STAT_MODE_NAMED_PIPE | \ + P9_STAT_MODE_SOCKET) + +/* This is the algorithm from ufs in spfs */ +static void stat_to_qid(const struct stat *stbuf, V9fsQID *qidp) +{ + size_t size; + + size = MIN(sizeof(stbuf->st_ino), sizeof(qidp->path)); + memcpy(&qidp->path, &stbuf->st_ino, size); + qidp->version = stbuf->st_mtime ^ (stbuf->st_size << 8); + qidp->type = 0; + if (S_ISDIR(stbuf->st_mode)) { + qidp->type |= P9_QID_TYPE_DIR; + } + if (S_ISLNK(stbuf->st_mode)) { + qidp->type |= P9_QID_TYPE_SYMLINK; + } +} + +static int fid_to_qid(V9fsState *s, V9fsFidState *fidp, V9fsQID *qidp) +{ + struct stat stbuf; + int err; + + err = v9fs_do_lstat(s, &fidp->path, &stbuf); + if (err) { + return err; + } + + stat_to_qid(&stbuf, qidp); + return 0; +} + +static V9fsPDU *alloc_pdu(V9fsState *s) +{ + V9fsPDU *pdu = NULL; + + if (!QLIST_EMPTY(&s->free_list)) { + pdu = QLIST_FIRST(&s->free_list); + QLIST_REMOVE(pdu, next); + } + return pdu; +} + +static void free_pdu(V9fsState *s, V9fsPDU *pdu) +{ + if (pdu) { + QLIST_INSERT_HEAD(&s->free_list, pdu, next); + } +} + +size_t pdu_packunpack(void *addr, struct iovec *sg, int sg_count, + size_t offset, size_t size, int pack) +{ + int i = 0; + size_t copied = 0; + + for (i = 0; size && i < sg_count; i++) { + size_t len; + if (offset >= sg[i].iov_len) { + /* skip this sg */ + offset -= sg[i].iov_len; + continue; + } else { + len = MIN(sg[i].iov_len - offset, size); + if (pack) { + memcpy(sg[i].iov_base + offset, addr, len); + } else { + memcpy(addr, sg[i].iov_base + offset, len); + } + size -= len; + copied += len; + addr += len; + if (size) { + offset = 0; + continue; + } + } + } + + return copied; +} + +static size_t pdu_unpack(void *dst, V9fsPDU *pdu, size_t offset, size_t size) +{ + return pdu_packunpack(dst, pdu->elem.out_sg, pdu->elem.out_num, + offset, size, 0); +} + +static size_t pdu_pack(V9fsPDU *pdu, size_t offset, const void *src, + size_t size) +{ + return pdu_packunpack((void *)src, pdu->elem.in_sg, pdu->elem.in_num, + offset, size, 1); +} + +static int pdu_copy_sg(V9fsPDU *pdu, size_t offset, int rx, struct iovec *sg) +{ + size_t pos = 0; + int i, j; + struct iovec *src_sg; + unsigned int num; + + if (rx) { + src_sg = pdu->elem.in_sg; + num = pdu->elem.in_num; + } else { + src_sg = pdu->elem.out_sg; + num = pdu->elem.out_num; + } + + j = 0; + for (i = 0; i < num; i++) { + if (offset <= pos) { + sg[j].iov_base = src_sg[i].iov_base; + sg[j].iov_len = src_sg[i].iov_len; + j++; + } else if (offset < (src_sg[i].iov_len + pos)) { + sg[j].iov_base = src_sg[i].iov_base; + sg[j].iov_len = src_sg[i].iov_len; + sg[j].iov_base += (offset - pos); + sg[j].iov_len -= (offset - pos); + j++; + } + pos += src_sg[i].iov_len; + } + + return j; +} + +static size_t pdu_unmarshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...) +{ + size_t old_offset = offset; + va_list ap; + int i; + + va_start(ap, fmt); + for (i = 0; fmt[i]; i++) { + switch (fmt[i]) { + case 'b': { + uint8_t *valp = va_arg(ap, uint8_t *); + offset += pdu_unpack(valp, pdu, offset, sizeof(*valp)); + break; + } + case 'w': { + uint16_t val, *valp; + valp = va_arg(ap, uint16_t *); + val = le16_to_cpupu(valp); + offset += pdu_unpack(&val, pdu, offset, sizeof(val)); + *valp = val; + break; + } + case 'd': { + uint32_t val, *valp; + valp = va_arg(ap, uint32_t *); + val = le32_to_cpupu(valp); + offset += pdu_unpack(&val, pdu, offset, sizeof(val)); + *valp = val; + break; + } + case 'q': { + uint64_t val, *valp; + valp = va_arg(ap, uint64_t *); + val = le64_to_cpup(valp); + offset += pdu_unpack(&val, pdu, offset, sizeof(val)); + *valp = val; + break; + } + case 'v': { + struct iovec *iov = va_arg(ap, struct iovec *); + int *iovcnt = va_arg(ap, int *); + *iovcnt = pdu_copy_sg(pdu, offset, 0, iov); + break; + } + case 's': { + V9fsString *str = va_arg(ap, V9fsString *); + offset += pdu_unmarshal(pdu, offset, "w", &str->size); + /* FIXME: sanity check str->size */ + str->data = qemu_malloc(str->size + 1); + offset += pdu_unpack(str->data, pdu, offset, str->size); + str->data[str->size] = 0; + break; + } + case 'Q': { + V9fsQID *qidp = va_arg(ap, V9fsQID *); + offset += pdu_unmarshal(pdu, offset, "bdq", + &qidp->type, &qidp->version, &qidp->path); + break; + } + case 'S': { + V9fsStat *statp = va_arg(ap, V9fsStat *); + offset += pdu_unmarshal(pdu, offset, "wwdQdddqsssssddd", + &statp->size, &statp->type, &statp->dev, + &statp->qid, &statp->mode, &statp->atime, + &statp->mtime, &statp->length, + &statp->name, &statp->uid, &statp->gid, + &statp->muid, &statp->extension, + &statp->n_uid, &statp->n_gid, + &statp->n_muid); + break; + } + case 'I': { + V9fsIattr *iattr = va_arg(ap, V9fsIattr *); + offset += pdu_unmarshal(pdu, offset, "ddddqqqqq", + &iattr->valid, &iattr->mode, + &iattr->uid, &iattr->gid, &iattr->size, + &iattr->atime_sec, &iattr->atime_nsec, + &iattr->mtime_sec, &iattr->mtime_nsec); + break; + } + default: + break; + } + } + + va_end(ap); + + return offset - old_offset; +} + +static size_t pdu_marshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...) +{ + size_t old_offset = offset; + va_list ap; + int i; + + va_start(ap, fmt); + for (i = 0; fmt[i]; i++) { + switch (fmt[i]) { + case 'b': { + uint8_t val = va_arg(ap, int); + offset += pdu_pack(pdu, offset, &val, sizeof(val)); + break; + } + case 'w': { + uint16_t val; + cpu_to_le16w(&val, va_arg(ap, int)); + offset += pdu_pack(pdu, offset, &val, sizeof(val)); + break; + } + case 'd': { + uint32_t val; + cpu_to_le32w(&val, va_arg(ap, uint32_t)); + offset += pdu_pack(pdu, offset, &val, sizeof(val)); + break; + } + case 'q': { + uint64_t val; + cpu_to_le64w(&val, va_arg(ap, uint64_t)); + offset += pdu_pack(pdu, offset, &val, sizeof(val)); + break; + } + case 'v': { + struct iovec *iov = va_arg(ap, struct iovec *); + int *iovcnt = va_arg(ap, int *); + *iovcnt = pdu_copy_sg(pdu, offset, 1, iov); + break; + } + case 's': { + V9fsString *str = va_arg(ap, V9fsString *); + offset += pdu_marshal(pdu, offset, "w", str->size); + offset += pdu_pack(pdu, offset, str->data, str->size); + break; + } + case 'Q': { + V9fsQID *qidp = va_arg(ap, V9fsQID *); + offset += pdu_marshal(pdu, offset, "bdq", + qidp->type, qidp->version, qidp->path); + break; + } + case 'S': { + V9fsStat *statp = va_arg(ap, V9fsStat *); + offset += pdu_marshal(pdu, offset, "wwdQdddqsssssddd", + statp->size, statp->type, statp->dev, + &statp->qid, statp->mode, statp->atime, + statp->mtime, statp->length, &statp->name, + &statp->uid, &statp->gid, &statp->muid, + &statp->extension, statp->n_uid, + statp->n_gid, statp->n_muid); + break; + } + case 'A': { + V9fsStatDotl *statp = va_arg(ap, V9fsStatDotl *); + offset += pdu_marshal(pdu, offset, "qQdddqqqqqqqqqqqqqqq", + statp->st_result_mask, + &statp->qid, statp->st_mode, + statp->st_uid, statp->st_gid, + statp->st_nlink, statp->st_rdev, + statp->st_size, statp->st_blksize, statp->st_blocks, + statp->st_atime_sec, statp->st_atime_nsec, + statp->st_mtime_sec, statp->st_mtime_nsec, + statp->st_ctime_sec, statp->st_ctime_nsec, + statp->st_btime_sec, statp->st_btime_nsec, + statp->st_gen, statp->st_data_version); + break; + } + default: + break; + } + } + va_end(ap); + + return offset - old_offset; +} + +static void complete_pdu(V9fsState *s, V9fsPDU *pdu, ssize_t len) +{ + int8_t id = pdu->id + 1; /* Response */ + + if (len < 0) { + int err = -len; + len = 7; + + if (s->proto_version != V9FS_PROTO_2000L) { + V9fsString str; + + str.data = strerror(err); + str.size = strlen(str.data); + + len += pdu_marshal(pdu, len, "s", &str); + id = P9_RERROR; + } + + len += pdu_marshal(pdu, len, "d", err); + + if (s->proto_version == V9FS_PROTO_2000L) { + id = P9_RLERROR; + } + } + + /* fill out the header */ + pdu_marshal(pdu, 0, "dbw", (int32_t)len, id, pdu->tag); + + /* keep these in sync */ + pdu->size = len; + pdu->id = id; + + /* push onto queue and notify */ + virtqueue_push(s->vq, &pdu->elem, len); + + /* FIXME: we should batch these completions */ + virtio_notify(&s->vdev, s->vq); + + free_pdu(s, pdu); +} + +static mode_t v9mode_to_mode(uint32_t mode, V9fsString *extension) +{ + mode_t ret; + + ret = mode & 0777; + if (mode & P9_STAT_MODE_DIR) { + ret |= S_IFDIR; + } + + if (mode & P9_STAT_MODE_SYMLINK) { + ret |= S_IFLNK; + } + if (mode & P9_STAT_MODE_SOCKET) { + ret |= S_IFSOCK; + } + if (mode & P9_STAT_MODE_NAMED_PIPE) { + ret |= S_IFIFO; + } + if (mode & P9_STAT_MODE_DEVICE) { + if (extension && extension->data[0] == 'c') { + ret |= S_IFCHR; + } else { + ret |= S_IFBLK; + } + } + + if (!(ret&~0777)) { + ret |= S_IFREG; + } + + if (mode & P9_STAT_MODE_SETUID) { + ret |= S_ISUID; + } + if (mode & P9_STAT_MODE_SETGID) { + ret |= S_ISGID; + } + if (mode & P9_STAT_MODE_SETVTX) { + ret |= S_ISVTX; + } + + return ret; +} + +static int donttouch_stat(V9fsStat *stat) +{ + if (stat->type == -1 && + stat->dev == -1 && + stat->qid.type == -1 && + stat->qid.version == -1 && + stat->qid.path == -1 && + stat->mode == -1 && + stat->atime == -1 && + stat->mtime == -1 && + stat->length == -1 && + !stat->name.size && + !stat->uid.size && + !stat->gid.size && + !stat->muid.size && + stat->n_uid == -1 && + stat->n_gid == -1 && + stat->n_muid == -1) { + return 1; + } + + return 0; +} + +static void v9fs_stat_free(V9fsStat *stat) +{ + v9fs_string_free(&stat->name); + v9fs_string_free(&stat->uid); + v9fs_string_free(&stat->gid); + v9fs_string_free(&stat->muid); + v9fs_string_free(&stat->extension); +} + +static uint32_t stat_to_v9mode(const struct stat *stbuf) +{ + uint32_t mode; + + mode = stbuf->st_mode & 0777; + if (S_ISDIR(stbuf->st_mode)) { + mode |= P9_STAT_MODE_DIR; + } + + if (S_ISLNK(stbuf->st_mode)) { + mode |= P9_STAT_MODE_SYMLINK; + } + + if (S_ISSOCK(stbuf->st_mode)) { + mode |= P9_STAT_MODE_SOCKET; + } + + if (S_ISFIFO(stbuf->st_mode)) { + mode |= P9_STAT_MODE_NAMED_PIPE; + } + + if (S_ISBLK(stbuf->st_mode) || S_ISCHR(stbuf->st_mode)) { + mode |= P9_STAT_MODE_DEVICE; + } + + if (stbuf->st_mode & S_ISUID) { + mode |= P9_STAT_MODE_SETUID; + } + + if (stbuf->st_mode & S_ISGID) { + mode |= P9_STAT_MODE_SETGID; + } + + if (stbuf->st_mode & S_ISVTX) { + mode |= P9_STAT_MODE_SETVTX; + } + + return mode; +} + +static int stat_to_v9stat(V9fsState *s, V9fsString *name, + const struct stat *stbuf, + V9fsStat *v9stat) +{ + int err; + const char *str; + + memset(v9stat, 0, sizeof(*v9stat)); + + stat_to_qid(stbuf, &v9stat->qid); + v9stat->mode = stat_to_v9mode(stbuf); + v9stat->atime = stbuf->st_atime; + v9stat->mtime = stbuf->st_mtime; + v9stat->length = stbuf->st_size; + + v9fs_string_null(&v9stat->uid); + v9fs_string_null(&v9stat->gid); + v9fs_string_null(&v9stat->muid); + + v9stat->n_uid = stbuf->st_uid; + v9stat->n_gid = stbuf->st_gid; + v9stat->n_muid = 0; + + v9fs_string_null(&v9stat->extension); + + if (v9stat->mode & P9_STAT_MODE_SYMLINK) { + err = v9fs_do_readlink(s, name, &v9stat->extension); + if (err == -1) { + err = -errno; + return err; + } + v9stat->extension.data[err] = 0; + v9stat->extension.size = err; + } else if (v9stat->mode & P9_STAT_MODE_DEVICE) { + v9fs_string_sprintf(&v9stat->extension, "%c %u %u", + S_ISCHR(stbuf->st_mode) ? 'c' : 'b', + major(stbuf->st_rdev), minor(stbuf->st_rdev)); + } else if (S_ISDIR(stbuf->st_mode) || S_ISREG(stbuf->st_mode)) { + v9fs_string_sprintf(&v9stat->extension, "%s %lu", + "HARDLINKCOUNT", (unsigned long)stbuf->st_nlink); + } + + str = strrchr(name->data, '/'); + if (str) { + str += 1; + } else { + str = name->data; + } + + v9fs_string_sprintf(&v9stat->name, "%s", str); + + v9stat->size = 61 + + v9fs_string_size(&v9stat->name) + + v9fs_string_size(&v9stat->uid) + + v9fs_string_size(&v9stat->gid) + + v9fs_string_size(&v9stat->muid) + + v9fs_string_size(&v9stat->extension); + return 0; +} + +#define P9_STATS_MODE 0x00000001ULL +#define P9_STATS_NLINK 0x00000002ULL +#define P9_STATS_UID 0x00000004ULL +#define P9_STATS_GID 0x00000008ULL +#define P9_STATS_RDEV 0x00000010ULL +#define P9_STATS_ATIME 0x00000020ULL +#define P9_STATS_MTIME 0x00000040ULL +#define P9_STATS_CTIME 0x00000080ULL +#define P9_STATS_INO 0x00000100ULL +#define P9_STATS_SIZE 0x00000200ULL +#define P9_STATS_BLOCKS 0x00000400ULL + +#define P9_STATS_BTIME 0x00000800ULL +#define P9_STATS_GEN 0x00001000ULL +#define P9_STATS_DATA_VERSION 0x00002000ULL + +#define P9_STATS_BASIC 0x000007ffULL /* Mask for fields up to BLOCKS */ +#define P9_STATS_ALL 0x00003fffULL /* Mask for All fields above */ + + +static void stat_to_v9stat_dotl(V9fsState *s, const struct stat *stbuf, + V9fsStatDotl *v9lstat) +{ + memset(v9lstat, 0, sizeof(*v9lstat)); + + v9lstat->st_mode = stbuf->st_mode; + v9lstat->st_nlink = stbuf->st_nlink; + v9lstat->st_uid = stbuf->st_uid; + v9lstat->st_gid = stbuf->st_gid; + v9lstat->st_rdev = stbuf->st_rdev; + v9lstat->st_size = stbuf->st_size; + v9lstat->st_blksize = stbuf->st_blksize; + v9lstat->st_blocks = stbuf->st_blocks; + v9lstat->st_atime_sec = stbuf->st_atime; + v9lstat->st_atime_nsec = stbuf->st_atim.tv_nsec; + v9lstat->st_mtime_sec = stbuf->st_mtime; + v9lstat->st_mtime_nsec = stbuf->st_mtim.tv_nsec; + v9lstat->st_ctime_sec = stbuf->st_ctime; + v9lstat->st_ctime_nsec = stbuf->st_ctim.tv_nsec; + /* Currently we only support BASIC fields in stat */ + v9lstat->st_result_mask = P9_STATS_BASIC; + + stat_to_qid(stbuf, &v9lstat->qid); +} + +static struct iovec *adjust_sg(struct iovec *sg, int len, int *iovcnt) +{ + while (len && *iovcnt) { + if (len < sg->iov_len) { + sg->iov_len -= len; + sg->iov_base += len; + len = 0; + } else { + len -= sg->iov_len; + sg++; + *iovcnt -= 1; + } + } + + return sg; +} + +static struct iovec *cap_sg(struct iovec *sg, int cap, int *cnt) +{ + int i; + int total = 0; + + for (i = 0; i < *cnt; i++) { + if ((total + sg[i].iov_len) > cap) { + sg[i].iov_len -= ((total + sg[i].iov_len) - cap); + i++; + break; + } + total += sg[i].iov_len; + } + + *cnt = i; + + return sg; +} + +static void print_sg(struct iovec *sg, int cnt) +{ + int i; + + printf("sg[%d]: {", cnt); + for (i = 0; i < cnt; i++) { + if (i) { + printf(", "); + } + printf("(%p, %zd)", sg[i].iov_base, sg[i].iov_len); + } + printf("}\n"); +} + +static void v9fs_fix_path(V9fsString *dst, V9fsString *src, int len) +{ + V9fsString str; + v9fs_string_init(&str); + v9fs_string_copy(&str, dst); + v9fs_string_sprintf(dst, "%s%s", src->data, str.data+len); + v9fs_string_free(&str); +} + +static void v9fs_version(V9fsState *s, V9fsPDU *pdu) +{ + V9fsString version; + size_t offset = 7; + + pdu_unmarshal(pdu, offset, "ds", &s->msize, &version); + + if (!strcmp(version.data, "9P2000.u")) { + s->proto_version = V9FS_PROTO_2000U; + } else if (!strcmp(version.data, "9P2000.L")) { + s->proto_version = V9FS_PROTO_2000L; + } else { + v9fs_string_sprintf(&version, "unknown"); + } + + offset += pdu_marshal(pdu, offset, "ds", s->msize, &version); + complete_pdu(s, pdu, offset); + + v9fs_string_free(&version); +} + +static void v9fs_attach(V9fsState *s, V9fsPDU *pdu) +{ + int32_t fid, afid, n_uname; + V9fsString uname, aname; + V9fsFidState *fidp; + V9fsQID qid; + size_t offset = 7; + ssize_t err; + + pdu_unmarshal(pdu, offset, "ddssd", &fid, &afid, &uname, &aname, &n_uname); + + fidp = alloc_fid(s, fid); + if (fidp == NULL) { + err = -EINVAL; + goto out; + } + + fidp->uid = n_uname; + + v9fs_string_sprintf(&fidp->path, "%s", "/"); + err = fid_to_qid(s, fidp, &qid); + if (err) { + err = -EINVAL; + free_fid(s, fid); + goto out; + } + + offset += pdu_marshal(pdu, offset, "Q", &qid); + + err = offset; +out: + complete_pdu(s, pdu, err); + v9fs_string_free(&uname); + v9fs_string_free(&aname); +} + +static void v9fs_stat_post_lstat(V9fsState *s, V9fsStatState *vs, int err) +{ + if (err == -1) { + err = -errno; + goto out; + } + + err = stat_to_v9stat(s, &vs->fidp->path, &vs->stbuf, &vs->v9stat); + if (err) { + goto out; + } + vs->offset += pdu_marshal(vs->pdu, vs->offset, "wS", 0, &vs->v9stat); + err = vs->offset; + +out: + complete_pdu(s, vs->pdu, err); + v9fs_stat_free(&vs->v9stat); + qemu_free(vs); +} + +static void v9fs_stat(V9fsState *s, V9fsPDU *pdu) +{ + int32_t fid; + V9fsStatState *vs; + ssize_t err = 0; + + vs = qemu_malloc(sizeof(*vs)); + vs->pdu = pdu; + vs->offset = 7; + + memset(&vs->v9stat, 0, sizeof(vs->v9stat)); + + pdu_unmarshal(vs->pdu, vs->offset, "d", &fid); + + vs->fidp = lookup_fid(s, fid); + if (vs->fidp == NULL) { + err = -ENOENT; + goto out; + } + + err = v9fs_do_lstat(s, &vs->fidp->path, &vs->stbuf); + v9fs_stat_post_lstat(s, vs, err); + return; + +out: + complete_pdu(s, vs->pdu, err); + v9fs_stat_free(&vs->v9stat); + qemu_free(vs); +} + +static void v9fs_getattr_post_lstat(V9fsState *s, V9fsStatStateDotl *vs, + int err) +{ + if (err == -1) { + err = -errno; + goto out; + } + + stat_to_v9stat_dotl(s, &vs->stbuf, &vs->v9stat_dotl); + vs->offset += pdu_marshal(vs->pdu, vs->offset, "A", &vs->v9stat_dotl); + err = vs->offset; + +out: + complete_pdu(s, vs->pdu, err); + qemu_free(vs); +} + +static void v9fs_getattr(V9fsState *s, V9fsPDU *pdu) +{ + int32_t fid; + V9fsStatStateDotl *vs; + ssize_t err = 0; + V9fsFidState *fidp; + uint64_t request_mask; + + vs = qemu_malloc(sizeof(*vs)); + vs->pdu = pdu; + vs->offset = 7; + + memset(&vs->v9stat_dotl, 0, sizeof(vs->v9stat_dotl)); + + pdu_unmarshal(vs->pdu, vs->offset, "dq", &fid, &request_mask); + + fidp = lookup_fid(s, fid); + if (fidp == NULL) { + err = -ENOENT; + goto out; + } + + /* Currently we only support BASIC fields in stat, so there is no + * need to look at request_mask. + */ + err = v9fs_do_lstat(s, &fidp->path, &vs->stbuf); + v9fs_getattr_post_lstat(s, vs, err); + return; + +out: + complete_pdu(s, vs->pdu, err); + qemu_free(vs); +} + +/* From Linux kernel code */ +#define ATTR_MODE (1 << 0) +#define ATTR_UID (1 << 1) +#define ATTR_GID (1 << 2) +#define ATTR_SIZE (1 << 3) +#define ATTR_ATIME (1 << 4) +#define ATTR_MTIME (1 << 5) +#define ATTR_CTIME (1 << 6) +#define ATTR_MASK 127 +#define ATTR_ATIME_SET (1 << 7) +#define ATTR_MTIME_SET (1 << 8) + +static void v9fs_setattr_post_truncate(V9fsState *s, V9fsSetattrState *vs, + int err) +{ + if (err == -1) { + err = -errno; + goto out; + } + err = vs->offset; + +out: + complete_pdu(s, vs->pdu, err); + qemu_free(vs); +} + +static void v9fs_setattr_post_chown(V9fsState *s, V9fsSetattrState *vs, int err) +{ + if (err == -1) { + err = -errno; + goto out; + } + + if (vs->v9iattr.valid & (ATTR_SIZE)) { + err = v9fs_do_truncate(s, &vs->fidp->path, vs->v9iattr.size); + } + v9fs_setattr_post_truncate(s, vs, err); + return; + +out: + complete_pdu(s, vs->pdu, err); + qemu_free(vs); +} + +static void v9fs_setattr_post_utimensat(V9fsState *s, V9fsSetattrState *vs, + int err) +{ + if (err == -1) { + err = -errno; + goto out; + } + + /* If the only valid entry in iattr is ctime we can call + * chown(-1,-1) to update the ctime of the file + */ + if ((vs->v9iattr.valid & (ATTR_UID | ATTR_GID)) || + ((vs->v9iattr.valid & ATTR_CTIME) + && !((vs->v9iattr.valid & ATTR_MASK) & ~ATTR_CTIME))) { + if (!(vs->v9iattr.valid & ATTR_UID)) { + vs->v9iattr.uid = -1; + } + if (!(vs->v9iattr.valid & ATTR_GID)) { + vs->v9iattr.gid = -1; + } + err = v9fs_do_chown(s, &vs->fidp->path, vs->v9iattr.uid, + vs->v9iattr.gid); + } + v9fs_setattr_post_chown(s, vs, err); + return; + +out: + complete_pdu(s, vs->pdu, err); + qemu_free(vs); +} + +static void v9fs_setattr_post_chmod(V9fsState *s, V9fsSetattrState *vs, int err) +{ + if (err == -1) { + err = -errno; + goto out; + } + + if (vs->v9iattr.valid & (ATTR_ATIME | ATTR_MTIME)) { + struct timespec times[2]; + if (vs->v9iattr.valid & ATTR_ATIME) { + if (vs->v9iattr.valid & ATTR_ATIME_SET) { + times[0].tv_sec = vs->v9iattr.atime_sec; + times[0].tv_nsec = vs->v9iattr.atime_nsec; + } else { + times[0].tv_nsec = UTIME_NOW; + } + } else { + times[0].tv_nsec = UTIME_OMIT; + } + + if (vs->v9iattr.valid & ATTR_MTIME) { + if (vs->v9iattr.valid & ATTR_MTIME_SET) { + times[1].tv_sec = vs->v9iattr.mtime_sec; + times[1].tv_nsec = vs->v9iattr.mtime_nsec; + } else { + times[1].tv_nsec = UTIME_NOW; + } + } else { + times[1].tv_nsec = UTIME_OMIT; + } + err = v9fs_do_utimensat(s, &vs->fidp->path, times); + } + v9fs_setattr_post_utimensat(s, vs, err); + return; + +out: + complete_pdu(s, vs->pdu, err); + qemu_free(vs); +} + +static void v9fs_setattr(V9fsState *s, V9fsPDU *pdu) +{ + int32_t fid; + V9fsSetattrState *vs; + int err = 0; + + vs = qemu_malloc(sizeof(*vs)); + vs->pdu = pdu; + vs->offset = 7; + + pdu_unmarshal(pdu, vs->offset, "dI", &fid, &vs->v9iattr); + + vs->fidp = lookup_fid(s, fid); + if (vs->fidp == NULL) { + err = -EINVAL; + goto out; + } + + if (vs->v9iattr.valid & ATTR_MODE) { + err = v9fs_do_chmod(s, &vs->fidp->path, vs->v9iattr.mode); + } + + v9fs_setattr_post_chmod(s, vs, err); + return; + +out: + complete_pdu(s, vs->pdu, err); + qemu_free(vs); +} + +static void v9fs_walk_complete(V9fsState *s, V9fsWalkState *vs, int err) +{ + complete_pdu(s, vs->pdu, err); + + if (vs->nwnames) { + for (vs->name_idx = 0; vs->name_idx < vs->nwnames; vs->name_idx++) { + v9fs_string_free(&vs->wnames[vs->name_idx]); + } + + qemu_free(vs->wnames); + qemu_free(vs->qids); + } +} + +static void v9fs_walk_marshal(V9fsWalkState *vs) +{ + int i; + vs->offset = 7; + vs->offset += pdu_marshal(vs->pdu, vs->offset, "w", vs->nwnames); + + for (i = 0; i < vs->nwnames; i++) { + vs->offset += pdu_marshal(vs->pdu, vs->offset, "Q", &vs->qids[i]); + } +} + +static void v9fs_walk_post_newfid_lstat(V9fsState *s, V9fsWalkState *vs, + int err) +{ + if (err == -1) { + free_fid(s, vs->newfidp->fid); + v9fs_string_free(&vs->path); + err = -ENOENT; + goto out; + } + + stat_to_qid(&vs->stbuf, &vs->qids[vs->name_idx]); + + vs->name_idx++; + if (vs->name_idx < vs->nwnames) { + v9fs_string_sprintf(&vs->path, "%s/%s", vs->newfidp->path.data, + vs->wnames[vs->name_idx].data); + v9fs_string_copy(&vs->newfidp->path, &vs->path); + + err = v9fs_do_lstat(s, &vs->newfidp->path, &vs->stbuf); + v9fs_walk_post_newfid_lstat(s, vs, err); + return; + } + + v9fs_string_free(&vs->path); + v9fs_walk_marshal(vs); + err = vs->offset; +out: + v9fs_walk_complete(s, vs, err); +} + +static void v9fs_walk_post_oldfid_lstat(V9fsState *s, V9fsWalkState *vs, + int err) +{ + if (err == -1) { + v9fs_string_free(&vs->path); + err = -ENOENT; + goto out; + } + + stat_to_qid(&vs->stbuf, &vs->qids[vs->name_idx]); + vs->name_idx++; + if (vs->name_idx < vs->nwnames) { + + v9fs_string_sprintf(&vs->path, "%s/%s", + vs->fidp->path.data, vs->wnames[vs->name_idx].data); + v9fs_string_copy(&vs->fidp->path, &vs->path); + + err = v9fs_do_lstat(s, &vs->fidp->path, &vs->stbuf); + v9fs_walk_post_oldfid_lstat(s, vs, err); + return; + } + + v9fs_string_free(&vs->path); + v9fs_walk_marshal(vs); + err = vs->offset; +out: + v9fs_walk_complete(s, vs, err); +} + +static void v9fs_walk(V9fsState *s, V9fsPDU *pdu) +{ + int32_t fid, newfid; + V9fsWalkState *vs; + int err = 0; + int i; + + vs = qemu_malloc(sizeof(*vs)); + vs->pdu = pdu; + vs->wnames = NULL; + vs->qids = NULL; + vs->offset = 7; + + vs->offset += pdu_unmarshal(vs->pdu, vs->offset, "ddw", &fid, + &newfid, &vs->nwnames); + + if (vs->nwnames) { + vs->wnames = qemu_mallocz(sizeof(vs->wnames[0]) * vs->nwnames); + + vs->qids = qemu_mallocz(sizeof(vs->qids[0]) * vs->nwnames); + + for (i = 0; i < vs->nwnames; i++) { + vs->offset += pdu_unmarshal(vs->pdu, vs->offset, "s", + &vs->wnames[i]); + } + } + + vs->fidp = lookup_fid(s, fid); + if (vs->fidp == NULL) { + err = -ENOENT; + goto out; + } + + /* FIXME: is this really valid? */ + if (fid == newfid) { + + BUG_ON(vs->fidp->fid_type != P9_FID_NONE); + v9fs_string_init(&vs->path); + vs->name_idx = 0; + + if (vs->name_idx < vs->nwnames) { + v9fs_string_sprintf(&vs->path, "%s/%s", + vs->fidp->path.data, vs->wnames[vs->name_idx].data); + v9fs_string_copy(&vs->fidp->path, &vs->path); + + err = v9fs_do_lstat(s, &vs->fidp->path, &vs->stbuf); + v9fs_walk_post_oldfid_lstat(s, vs, err); + return; + } + } else { + vs->newfidp = alloc_fid(s, newfid); + if (vs->newfidp == NULL) { + err = -EINVAL; + goto out; + } + + vs->newfidp->uid = vs->fidp->uid; + v9fs_string_init(&vs->path); + vs->name_idx = 0; + v9fs_string_copy(&vs->newfidp->path, &vs->fidp->path); + + if (vs->name_idx < vs->nwnames) { + v9fs_string_sprintf(&vs->path, "%s/%s", vs->newfidp->path.data, + vs->wnames[vs->name_idx].data); + v9fs_string_copy(&vs->newfidp->path, &vs->path); + + err = v9fs_do_lstat(s, &vs->newfidp->path, &vs->stbuf); + v9fs_walk_post_newfid_lstat(s, vs, err); + return; + } + } + + v9fs_walk_marshal(vs); + err = vs->offset; +out: + v9fs_walk_complete(s, vs, err); +} + +static int32_t get_iounit(V9fsState *s, V9fsString *name) +{ + struct statfs stbuf; + int32_t iounit = 0; + + /* + * iounit should be multiples of f_bsize (host filesystem block size + * and as well as less than (client msize - P9_IOHDRSZ)) + */ + if (!v9fs_do_statfs(s, name, &stbuf)) { + iounit = stbuf.f_bsize; + iounit *= (s->msize - P9_IOHDRSZ)/stbuf.f_bsize; + } + + if (!iounit) { + iounit = s->msize - P9_IOHDRSZ; + } + return iounit; +} + +static void v9fs_open_post_opendir(V9fsState *s, V9fsOpenState *vs, int err) +{ + if (vs->fidp->fs.dir == NULL) { + err = -errno; + goto out; + } + vs->fidp->fid_type = P9_FID_DIR; + vs->offset += pdu_marshal(vs->pdu, vs->offset, "Qd", &vs->qid, 0); + err = vs->offset; +out: + complete_pdu(s, vs->pdu, err); + qemu_free(vs); + +} + +static void v9fs_open_post_getiounit(V9fsState *s, V9fsOpenState *vs) +{ + int err; + vs->offset += pdu_marshal(vs->pdu, vs->offset, "Qd", &vs->qid, vs->iounit); + err = vs->offset; + complete_pdu(s, vs->pdu, err); + qemu_free(vs); +} + +static void v9fs_open_post_open(V9fsState *s, V9fsOpenState *vs, int err) +{ + if (vs->fidp->fs.fd == -1) { + err = -errno; + goto out; + } + vs->fidp->fid_type = P9_FID_FILE; + vs->iounit = get_iounit(s, &vs->fidp->path); + v9fs_open_post_getiounit(s, vs); + return; +out: + complete_pdu(s, vs->pdu, err); + qemu_free(vs); +} + +static void v9fs_open_post_lstat(V9fsState *s, V9fsOpenState *vs, int err) +{ + int flags; + + if (err) { + err = -errno; + goto out; + } + + stat_to_qid(&vs->stbuf, &vs->qid); + + if (S_ISDIR(vs->stbuf.st_mode)) { + vs->fidp->fs.dir = v9fs_do_opendir(s, &vs->fidp->path); + v9fs_open_post_opendir(s, vs, err); + } else { + if (s->proto_version == V9FS_PROTO_2000L) { + flags = vs->mode; + flags &= ~(O_NOCTTY | O_ASYNC | O_CREAT); + /* Ignore direct disk access hint until the server supports it. */ + flags &= ~O_DIRECT; + } else { + flags = omode_to_uflags(vs->mode); + } + vs->fidp->fs.fd = v9fs_do_open(s, &vs->fidp->path, flags); + v9fs_open_post_open(s, vs, err); + } + return; +out: + complete_pdu(s, vs->pdu, err); + qemu_free(vs); +} + +static void v9fs_open(V9fsState *s, V9fsPDU *pdu) +{ + int32_t fid; + V9fsOpenState *vs; + ssize_t err = 0; + + vs = qemu_malloc(sizeof(*vs)); + vs->pdu = pdu; + vs->offset = 7; + vs->mode = 0; + + if (s->proto_version == V9FS_PROTO_2000L) { + pdu_unmarshal(vs->pdu, vs->offset, "dd", &fid, &vs->mode); + } else { + pdu_unmarshal(vs->pdu, vs->offset, "db", &fid, &vs->mode); + } + + vs->fidp = lookup_fid(s, fid); + if (vs->fidp == NULL) { + err = -ENOENT; + goto out; + } + + BUG_ON(vs->fidp->fid_type != P9_FID_NONE); + + err = v9fs_do_lstat(s, &vs->fidp->path, &vs->stbuf); + + v9fs_open_post_lstat(s, vs, err); + return; +out: + complete_pdu(s, pdu, err); + qemu_free(vs); +} + +static void v9fs_post_lcreate(V9fsState *s, V9fsLcreateState *vs, int err) +{ + if (err == 0) { + v9fs_string_copy(&vs->fidp->path, &vs->fullname); + stat_to_qid(&vs->stbuf, &vs->qid); + vs->offset += pdu_marshal(vs->pdu, vs->offset, "Qd", &vs->qid, + &vs->iounit); + err = vs->offset; + } else { + vs->fidp->fid_type = P9_FID_NONE; + err = -errno; + if (vs->fidp->fs.fd > 0) { + close(vs->fidp->fs.fd); + } + } + + complete_pdu(s, vs->pdu, err); + v9fs_string_free(&vs->name); + v9fs_string_free(&vs->fullname); + qemu_free(vs); +} + +static void v9fs_lcreate_post_get_iounit(V9fsState *s, V9fsLcreateState *vs, + int err) +{ + if (err) { + err = -errno; + goto out; + } + err = v9fs_do_lstat(s, &vs->fullname, &vs->stbuf); + +out: + v9fs_post_lcreate(s, vs, err); +} + +static void v9fs_lcreate_post_do_open2(V9fsState *s, V9fsLcreateState *vs, + int err) +{ + if (vs->fidp->fs.fd == -1) { + err = -errno; + goto out; + } + vs->fidp->fid_type = P9_FID_FILE; + vs->iounit = get_iounit(s, &vs->fullname); + v9fs_lcreate_post_get_iounit(s, vs, err); + return; + +out: + v9fs_post_lcreate(s, vs, err); +} + +static void v9fs_lcreate(V9fsState *s, V9fsPDU *pdu) +{ + int32_t dfid, flags, mode; + gid_t gid; + V9fsLcreateState *vs; + ssize_t err = 0; + + vs = qemu_malloc(sizeof(*vs)); + vs->pdu = pdu; + vs->offset = 7; + + v9fs_string_init(&vs->fullname); + + pdu_unmarshal(vs->pdu, vs->offset, "dsddd", &dfid, &vs->name, &flags, + &mode, &gid); + + vs->fidp = lookup_fid(s, dfid); + if (vs->fidp == NULL) { + err = -ENOENT; + goto out; + } + + v9fs_string_sprintf(&vs->fullname, "%s/%s", vs->fidp->path.data, + vs->name.data); + + /* Ignore direct disk access hint until the server supports it. */ + flags &= ~O_DIRECT; + + vs->fidp->fs.fd = v9fs_do_open2(s, vs->fullname.data, vs->fidp->uid, + gid, flags, mode); + v9fs_lcreate_post_do_open2(s, vs, err); + return; + +out: + complete_pdu(s, vs->pdu, err); + v9fs_string_free(&vs->name); + qemu_free(vs); +} + +static void v9fs_post_do_fsync(V9fsState *s, V9fsPDU *pdu, int err) +{ + if (err == -1) { + err = -errno; + } + complete_pdu(s, pdu, err); +} + +static void v9fs_fsync(V9fsState *s, V9fsPDU *pdu) +{ + int32_t fid; + size_t offset = 7; + V9fsFidState *fidp; + int datasync; + int err; + + pdu_unmarshal(pdu, offset, "dd", &fid, &datasync); + fidp = lookup_fid(s, fid); + if (fidp == NULL) { + err = -ENOENT; + v9fs_post_do_fsync(s, pdu, err); + return; + } + err = v9fs_do_fsync(s, fidp->fs.fd, datasync); + v9fs_post_do_fsync(s, pdu, err); +} + +static void v9fs_clunk(V9fsState *s, V9fsPDU *pdu) +{ + int32_t fid; + size_t offset = 7; + int err; + + pdu_unmarshal(pdu, offset, "d", &fid); + + err = free_fid(s, fid); + if (err < 0) { + goto out; + } + + offset = 7; + err = offset; +out: + complete_pdu(s, pdu, err); +} + +static void v9fs_read_post_readdir(V9fsState *, V9fsReadState *, ssize_t); + +static void v9fs_read_post_seekdir(V9fsState *s, V9fsReadState *vs, ssize_t err) +{ + if (err) { + goto out; + } + v9fs_stat_free(&vs->v9stat); + v9fs_string_free(&vs->name); + vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", vs->count); + vs->offset += vs->count; + err = vs->offset; +out: + complete_pdu(s, vs->pdu, err); + qemu_free(vs); + return; +} + +static void v9fs_read_post_dir_lstat(V9fsState *s, V9fsReadState *vs, + ssize_t err) +{ + if (err) { + err = -errno; + goto out; + } + err = stat_to_v9stat(s, &vs->name, &vs->stbuf, &vs->v9stat); + if (err) { + goto out; + } + + vs->len = pdu_marshal(vs->pdu, vs->offset + 4 + vs->count, "S", + &vs->v9stat); + if ((vs->len != (vs->v9stat.size + 2)) || + ((vs->count + vs->len) > vs->max_count)) { + v9fs_do_seekdir(s, vs->fidp->fs.dir, vs->dir_pos); + v9fs_read_post_seekdir(s, vs, err); + return; + } + vs->count += vs->len; + v9fs_stat_free(&vs->v9stat); + v9fs_string_free(&vs->name); + vs->dir_pos = vs->dent->d_off; + vs->dent = v9fs_do_readdir(s, vs->fidp->fs.dir); + v9fs_read_post_readdir(s, vs, err); + return; +out: + v9fs_do_seekdir(s, vs->fidp->fs.dir, vs->dir_pos); + v9fs_read_post_seekdir(s, vs, err); + return; + +} + +static void v9fs_read_post_readdir(V9fsState *s, V9fsReadState *vs, ssize_t err) +{ + if (vs->dent) { + memset(&vs->v9stat, 0, sizeof(vs->v9stat)); + v9fs_string_init(&vs->name); + v9fs_string_sprintf(&vs->name, "%s/%s", vs->fidp->path.data, + vs->dent->d_name); + err = v9fs_do_lstat(s, &vs->name, &vs->stbuf); + v9fs_read_post_dir_lstat(s, vs, err); + return; + } + + vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", vs->count); + vs->offset += vs->count; + err = vs->offset; + complete_pdu(s, vs->pdu, err); + qemu_free(vs); + return; +} + +static void v9fs_read_post_telldir(V9fsState *s, V9fsReadState *vs, ssize_t err) +{ + vs->dent = v9fs_do_readdir(s, vs->fidp->fs.dir); + v9fs_read_post_readdir(s, vs, err); + return; +} + +static void v9fs_read_post_rewinddir(V9fsState *s, V9fsReadState *vs, + ssize_t err) +{ + vs->dir_pos = v9fs_do_telldir(s, vs->fidp->fs.dir); + v9fs_read_post_telldir(s, vs, err); + return; +} + +static void v9fs_read_post_preadv(V9fsState *s, V9fsReadState *vs, ssize_t err) +{ + if (err < 0) { + /* IO error return the error */ + err = -errno; + goto out; + } + vs->total += vs->len; + vs->sg = adjust_sg(vs->sg, vs->len, &vs->cnt); + if (vs->total < vs->count && vs->len > 0) { + do { + if (0) { + print_sg(vs->sg, vs->cnt); + } + vs->len = v9fs_do_preadv(s, vs->fidp->fs.fd, vs->sg, vs->cnt, + vs->off); + if (vs->len > 0) { + vs->off += vs->len; + } + } while (vs->len == -1 && errno == EINTR); + if (vs->len == -1) { + err = -errno; + } + v9fs_read_post_preadv(s, vs, err); + return; + } + vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", vs->total); + vs->offset += vs->count; + err = vs->offset; + +out: + complete_pdu(s, vs->pdu, err); + qemu_free(vs); +} + +static void v9fs_xattr_read(V9fsState *s, V9fsReadState *vs) +{ + ssize_t err = 0; + int read_count; + int64_t xattr_len; + + xattr_len = vs->fidp->fs.xattr.len; + read_count = xattr_len - vs->off; + if (read_count > vs->count) { + read_count = vs->count; + } else if (read_count < 0) { + /* + * read beyond XATTR value + */ + read_count = 0; + } + vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", read_count); + vs->offset += pdu_pack(vs->pdu, vs->offset, + ((char *)vs->fidp->fs.xattr.value) + vs->off, + read_count); + err = vs->offset; + complete_pdu(s, vs->pdu, err); + qemu_free(vs); +} + +static void v9fs_read(V9fsState *s, V9fsPDU *pdu) +{ + int32_t fid; + V9fsReadState *vs; + ssize_t err = 0; + + vs = qemu_malloc(sizeof(*vs)); + vs->pdu = pdu; + vs->offset = 7; + vs->total = 0; + vs->len = 0; + vs->count = 0; + + pdu_unmarshal(vs->pdu, vs->offset, "dqd", &fid, &vs->off, &vs->count); + + vs->fidp = lookup_fid(s, fid); + if (vs->fidp == NULL) { + err = -EINVAL; + goto out; + } + + if (vs->fidp->fid_type == P9_FID_DIR) { + vs->max_count = vs->count; + vs->count = 0; + if (vs->off == 0) { + v9fs_do_rewinddir(s, vs->fidp->fs.dir); + } + v9fs_read_post_rewinddir(s, vs, err); + return; + } else if (vs->fidp->fid_type == P9_FID_FILE) { + vs->sg = vs->iov; + pdu_marshal(vs->pdu, vs->offset + 4, "v", vs->sg, &vs->cnt); + vs->sg = cap_sg(vs->sg, vs->count, &vs->cnt); + if (vs->total <= vs->count) { + vs->len = v9fs_do_preadv(s, vs->fidp->fs.fd, vs->sg, vs->cnt, + vs->off); + if (vs->len > 0) { + vs->off += vs->len; + } + err = vs->len; + v9fs_read_post_preadv(s, vs, err); + } + return; + } else if (vs->fidp->fid_type == P9_FID_XATTR) { + v9fs_xattr_read(s, vs); + return; + } else { + err = -EINVAL; + } +out: + complete_pdu(s, pdu, err); + qemu_free(vs); +} + +typedef struct V9fsReadDirState { + V9fsPDU *pdu; + V9fsFidState *fidp; + V9fsQID qid; + off_t saved_dir_pos; + struct dirent *dent; + int32_t count; + int32_t max_count; + size_t offset; + int64_t initial_offset; + V9fsString name; +} V9fsReadDirState; + +static void v9fs_readdir_post_seekdir(V9fsState *s, V9fsReadDirState *vs) +{ + vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", vs->count); + vs->offset += vs->count; + complete_pdu(s, vs->pdu, vs->offset); + qemu_free(vs); + return; +} + +/* Size of each dirent on the wire: size of qid (13) + size of offset (8) + * size of type (1) + size of name.size (2) + strlen(name.data) + */ +#define V9_READDIR_DATA_SZ (24 + strlen(vs->name.data)) + +static void v9fs_readdir_post_readdir(V9fsState *s, V9fsReadDirState *vs) +{ + int len; + size_t size; + + if (vs->dent) { + v9fs_string_init(&vs->name); + v9fs_string_sprintf(&vs->name, "%s", vs->dent->d_name); + + if ((vs->count + V9_READDIR_DATA_SZ) > vs->max_count) { + /* Ran out of buffer. Set dir back to old position and return */ + v9fs_do_seekdir(s, vs->fidp->fs.dir, vs->saved_dir_pos); + v9fs_readdir_post_seekdir(s, vs); + return; + } + + /* Fill up just the path field of qid because the client uses + * only that. To fill the entire qid structure we will have + * to stat each dirent found, which is expensive + */ + size = MIN(sizeof(vs->dent->d_ino), sizeof(vs->qid.path)); + memcpy(&vs->qid.path, &vs->dent->d_ino, size); + /* Fill the other fields with dummy values */ + vs->qid.type = 0; + vs->qid.version = 0; + + len = pdu_marshal(vs->pdu, vs->offset+4+vs->count, "Qqbs", + &vs->qid, vs->dent->d_off, + vs->dent->d_type, &vs->name); + vs->count += len; + v9fs_string_free(&vs->name); + vs->saved_dir_pos = vs->dent->d_off; + vs->dent = v9fs_do_readdir(s, vs->fidp->fs.dir); + v9fs_readdir_post_readdir(s, vs); + return; + } + + vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", vs->count); + vs->offset += vs->count; + complete_pdu(s, vs->pdu, vs->offset); + qemu_free(vs); + return; +} + +static void v9fs_readdir_post_telldir(V9fsState *s, V9fsReadDirState *vs) +{ + vs->dent = v9fs_do_readdir(s, vs->fidp->fs.dir); + v9fs_readdir_post_readdir(s, vs); + return; +} + +static void v9fs_readdir_post_setdir(V9fsState *s, V9fsReadDirState *vs) +{ + vs->saved_dir_pos = v9fs_do_telldir(s, vs->fidp->fs.dir); + v9fs_readdir_post_telldir(s, vs); + return; +} + +static void v9fs_readdir(V9fsState *s, V9fsPDU *pdu) +{ + int32_t fid; + V9fsReadDirState *vs; + ssize_t err = 0; + size_t offset = 7; + + vs = qemu_malloc(sizeof(*vs)); + vs->pdu = pdu; + vs->offset = 7; + vs->count = 0; + + pdu_unmarshal(vs->pdu, offset, "dqd", &fid, &vs->initial_offset, + &vs->max_count); + + vs->fidp = lookup_fid(s, fid); + if (vs->fidp == NULL || !(vs->fidp->fs.dir)) { + err = -EINVAL; + goto out; + } + + if (vs->initial_offset == 0) { + v9fs_do_rewinddir(s, vs->fidp->fs.dir); + } else { + v9fs_do_seekdir(s, vs->fidp->fs.dir, vs->initial_offset); + } + + v9fs_readdir_post_setdir(s, vs); + return; + +out: + complete_pdu(s, pdu, err); + qemu_free(vs); + return; +} + +static void v9fs_write_post_pwritev(V9fsState *s, V9fsWriteState *vs, + ssize_t err) +{ + if (err < 0) { + /* IO error return the error */ + err = -errno; + goto out; + } + vs->total += vs->len; + vs->sg = adjust_sg(vs->sg, vs->len, &vs->cnt); + if (vs->total < vs->count && vs->len > 0) { + do { + if (0) { + print_sg(vs->sg, vs->cnt); + } + vs->len = v9fs_do_pwritev(s, vs->fidp->fs.fd, vs->sg, vs->cnt, + vs->off); + if (vs->len > 0) { + vs->off += vs->len; + } + } while (vs->len == -1 && errno == EINTR); + if (vs->len == -1) { + err = -errno; + } + v9fs_write_post_pwritev(s, vs, err); + return; + } + vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", vs->total); + err = vs->offset; +out: + complete_pdu(s, vs->pdu, err); + qemu_free(vs); +} + +static void v9fs_xattr_write(V9fsState *s, V9fsWriteState *vs) +{ + int i, to_copy; + ssize_t err = 0; + int write_count; + int64_t xattr_len; + + xattr_len = vs->fidp->fs.xattr.len; + write_count = xattr_len - vs->off; + if (write_count > vs->count) { + write_count = vs->count; + } else if (write_count < 0) { + /* + * write beyond XATTR value len specified in + * xattrcreate + */ + err = -ENOSPC; + goto out; + } + vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", write_count); + err = vs->offset; + vs->fidp->fs.xattr.copied_len += write_count; + /* + * Now copy the content from sg list + */ + for (i = 0; i < vs->cnt; i++) { + if (write_count > vs->sg[i].iov_len) { + to_copy = vs->sg[i].iov_len; + } else { + to_copy = write_count; + } + memcpy((char *)vs->fidp->fs.xattr.value + vs->off, + vs->sg[i].iov_base, to_copy); + /* updating vs->off since we are not using below */ + vs->off += to_copy; + write_count -= to_copy; + } +out: + complete_pdu(s, vs->pdu, err); + qemu_free(vs); +} + +static void v9fs_write(V9fsState *s, V9fsPDU *pdu) +{ + int32_t fid; + V9fsWriteState *vs; + ssize_t err; + + vs = qemu_malloc(sizeof(*vs)); + + vs->pdu = pdu; + vs->offset = 7; + vs->sg = vs->iov; + vs->total = 0; + vs->len = 0; + + pdu_unmarshal(vs->pdu, vs->offset, "dqdv", &fid, &vs->off, &vs->count, + vs->sg, &vs->cnt); + + vs->fidp = lookup_fid(s, fid); + if (vs->fidp == NULL) { + err = -EINVAL; + goto out; + } + + if (vs->fidp->fid_type == P9_FID_FILE) { + if (vs->fidp->fs.fd == -1) { + err = -EINVAL; + goto out; + } + } else if (vs->fidp->fid_type == P9_FID_XATTR) { + /* + * setxattr operation + */ + v9fs_xattr_write(s, vs); + return; + } else { + err = -EINVAL; + goto out; + } + vs->sg = cap_sg(vs->sg, vs->count, &vs->cnt); + if (vs->total <= vs->count) { + vs->len = v9fs_do_pwritev(s, vs->fidp->fs.fd, vs->sg, vs->cnt, vs->off); + if (vs->len > 0) { + vs->off += vs->len; + } + err = vs->len; + v9fs_write_post_pwritev(s, vs, err); + } + return; +out: + complete_pdu(s, vs->pdu, err); + qemu_free(vs); +} + +static void v9fs_create_post_getiounit(V9fsState *s, V9fsCreateState *vs) +{ + int err; + v9fs_string_copy(&vs->fidp->path, &vs->fullname); + stat_to_qid(&vs->stbuf, &vs->qid); + + vs->offset += pdu_marshal(vs->pdu, vs->offset, "Qd", &vs->qid, vs->iounit); + err = vs->offset; + + complete_pdu(s, vs->pdu, err); + v9fs_string_free(&vs->name); + v9fs_string_free(&vs->extension); + v9fs_string_free(&vs->fullname); + qemu_free(vs); +} + +static void v9fs_post_create(V9fsState *s, V9fsCreateState *vs, int err) +{ + if (err == 0) { + vs->iounit = get_iounit(s, &vs->fidp->path); + v9fs_create_post_getiounit(s, vs); + return; + } + + complete_pdu(s, vs->pdu, err); + v9fs_string_free(&vs->name); + v9fs_string_free(&vs->extension); + v9fs_string_free(&vs->fullname); + qemu_free(vs); +} + +static void v9fs_create_post_perms(V9fsState *s, V9fsCreateState *vs, int err) +{ + if (err) { + err = -errno; + } + v9fs_post_create(s, vs, err); +} + +static void v9fs_create_post_opendir(V9fsState *s, V9fsCreateState *vs, + int err) +{ + if (!vs->fidp->fs.dir) { + err = -errno; + } + vs->fidp->fid_type = P9_FID_DIR; + v9fs_post_create(s, vs, err); +} + +static void v9fs_create_post_dir_lstat(V9fsState *s, V9fsCreateState *vs, + int err) +{ + if (err) { + err = -errno; + goto out; + } + + vs->fidp->fs.dir = v9fs_do_opendir(s, &vs->fullname); + v9fs_create_post_opendir(s, vs, err); + return; + +out: + v9fs_post_create(s, vs, err); +} + +static void v9fs_create_post_mkdir(V9fsState *s, V9fsCreateState *vs, int err) +{ + if (err) { + err = -errno; + goto out; + } + + err = v9fs_do_lstat(s, &vs->fullname, &vs->stbuf); + v9fs_create_post_dir_lstat(s, vs, err); + return; + +out: + v9fs_post_create(s, vs, err); +} + +static void v9fs_create_post_fstat(V9fsState *s, V9fsCreateState *vs, int err) +{ + if (err) { + vs->fidp->fid_type = P9_FID_NONE; + close(vs->fidp->fs.fd); + err = -errno; + } + v9fs_post_create(s, vs, err); + return; +} + +static void v9fs_create_post_open2(V9fsState *s, V9fsCreateState *vs, int err) +{ + if (vs->fidp->fs.fd == -1) { + err = -errno; + goto out; + } + vs->fidp->fid_type = P9_FID_FILE; + err = v9fs_do_fstat(s, vs->fidp->fs.fd, &vs->stbuf); + v9fs_create_post_fstat(s, vs, err); + + return; + +out: + v9fs_post_create(s, vs, err); + +} + +static void v9fs_create_post_lstat(V9fsState *s, V9fsCreateState *vs, int err) +{ + + if (err == 0 || errno != ENOENT) { + err = -errno; + goto out; + } + + if (vs->perm & P9_STAT_MODE_DIR) { + err = v9fs_do_mkdir(s, vs->fullname.data, vs->perm & 0777, + vs->fidp->uid, -1); + v9fs_create_post_mkdir(s, vs, err); + } else if (vs->perm & P9_STAT_MODE_SYMLINK) { + err = v9fs_do_symlink(s, vs->fidp, vs->extension.data, + vs->fullname.data, -1); + v9fs_create_post_perms(s, vs, err); + } else if (vs->perm & P9_STAT_MODE_LINK) { + int32_t nfid = atoi(vs->extension.data); + V9fsFidState *nfidp = lookup_fid(s, nfid); + if (nfidp == NULL) { + err = -errno; + v9fs_post_create(s, vs, err); + } + err = v9fs_do_link(s, &nfidp->path, &vs->fullname); + v9fs_create_post_perms(s, vs, err); + } else if (vs->perm & P9_STAT_MODE_DEVICE) { + char ctype; + uint32_t major, minor; + mode_t nmode = 0; + + if (sscanf(vs->extension.data, "%c %u %u", &ctype, &major, + &minor) != 3) { + err = -errno; + v9fs_post_create(s, vs, err); + } + + switch (ctype) { + case 'c': + nmode = S_IFCHR; + break; + case 'b': + nmode = S_IFBLK; + break; + default: + err = -EIO; + v9fs_post_create(s, vs, err); + } + + nmode |= vs->perm & 0777; + err = v9fs_do_mknod(s, vs->fullname.data, nmode, + makedev(major, minor), vs->fidp->uid, -1); + v9fs_create_post_perms(s, vs, err); + } else if (vs->perm & P9_STAT_MODE_NAMED_PIPE) { + err = v9fs_do_mknod(s, vs->fullname.data, S_IFIFO | (vs->perm & 0777), + 0, vs->fidp->uid, -1); + v9fs_post_create(s, vs, err); + } else if (vs->perm & P9_STAT_MODE_SOCKET) { + err = v9fs_do_mknod(s, vs->fullname.data, S_IFSOCK | (vs->perm & 0777), + 0, vs->fidp->uid, -1); + v9fs_post_create(s, vs, err); + } else { + vs->fidp->fs.fd = v9fs_do_open2(s, vs->fullname.data, vs->fidp->uid, + -1, omode_to_uflags(vs->mode)|O_CREAT, vs->perm); + + v9fs_create_post_open2(s, vs, err); + } + + return; + +out: + v9fs_post_create(s, vs, err); +} + +static void v9fs_create(V9fsState *s, V9fsPDU *pdu) +{ + int32_t fid; + V9fsCreateState *vs; + int err = 0; + + vs = qemu_malloc(sizeof(*vs)); + vs->pdu = pdu; + vs->offset = 7; + + v9fs_string_init(&vs->fullname); + + pdu_unmarshal(vs->pdu, vs->offset, "dsdbs", &fid, &vs->name, + &vs->perm, &vs->mode, &vs->extension); + + vs->fidp = lookup_fid(s, fid); + if (vs->fidp == NULL) { + err = -EINVAL; + goto out; + } + + v9fs_string_sprintf(&vs->fullname, "%s/%s", vs->fidp->path.data, + vs->name.data); + + err = v9fs_do_lstat(s, &vs->fullname, &vs->stbuf); + v9fs_create_post_lstat(s, vs, err); + return; + +out: + complete_pdu(s, vs->pdu, err); + v9fs_string_free(&vs->name); + v9fs_string_free(&vs->extension); + qemu_free(vs); +} + +static void v9fs_post_symlink(V9fsState *s, V9fsSymlinkState *vs, int err) +{ + if (err == 0) { + stat_to_qid(&vs->stbuf, &vs->qid); + vs->offset += pdu_marshal(vs->pdu, vs->offset, "Q", &vs->qid); + err = vs->offset; + } else { + err = -errno; + } + complete_pdu(s, vs->pdu, err); + v9fs_string_free(&vs->name); + v9fs_string_free(&vs->symname); + v9fs_string_free(&vs->fullname); + qemu_free(vs); +} + +static void v9fs_symlink_post_do_symlink(V9fsState *s, V9fsSymlinkState *vs, + int err) +{ + if (err) { + goto out; + } + err = v9fs_do_lstat(s, &vs->fullname, &vs->stbuf); +out: + v9fs_post_symlink(s, vs, err); +} + +static void v9fs_symlink(V9fsState *s, V9fsPDU *pdu) +{ + int32_t dfid; + V9fsSymlinkState *vs; + int err = 0; + gid_t gid; + + vs = qemu_malloc(sizeof(*vs)); + vs->pdu = pdu; + vs->offset = 7; + + v9fs_string_init(&vs->fullname); + + pdu_unmarshal(vs->pdu, vs->offset, "dssd", &dfid, &vs->name, + &vs->symname, &gid); + + vs->dfidp = lookup_fid(s, dfid); + if (vs->dfidp == NULL) { + err = -EINVAL; + goto out; + } + + v9fs_string_sprintf(&vs->fullname, "%s/%s", vs->dfidp->path.data, + vs->name.data); + err = v9fs_do_symlink(s, vs->dfidp, vs->symname.data, + vs->fullname.data, gid); + v9fs_symlink_post_do_symlink(s, vs, err); + return; + +out: + complete_pdu(s, vs->pdu, err); + v9fs_string_free(&vs->name); + v9fs_string_free(&vs->symname); + qemu_free(vs); +} + +static void v9fs_flush(V9fsState *s, V9fsPDU *pdu) +{ + /* A nop call with no return */ + complete_pdu(s, pdu, 7); +} + +static void v9fs_link(V9fsState *s, V9fsPDU *pdu) +{ + int32_t dfid, oldfid; + V9fsFidState *dfidp, *oldfidp; + V9fsString name, fullname; + size_t offset = 7; + int err = 0; + + v9fs_string_init(&fullname); + + pdu_unmarshal(pdu, offset, "dds", &dfid, &oldfid, &name); + + dfidp = lookup_fid(s, dfid); + if (dfidp == NULL) { + err = -errno; + goto out; + } + + oldfidp = lookup_fid(s, oldfid); + if (oldfidp == NULL) { + err = -errno; + goto out; + } + + v9fs_string_sprintf(&fullname, "%s/%s", dfidp->path.data, name.data); + err = offset; + err = v9fs_do_link(s, &oldfidp->path, &fullname); + if (err) { + err = -errno; + } + v9fs_string_free(&fullname); + +out: + v9fs_string_free(&name); + complete_pdu(s, pdu, err); +} + +static void v9fs_remove_post_remove(V9fsState *s, V9fsRemoveState *vs, + int err) +{ + if (err < 0) { + err = -errno; + } else { + err = vs->offset; + } + + /* For TREMOVE we need to clunk the fid even on failed remove */ + free_fid(s, vs->fidp->fid); + + complete_pdu(s, vs->pdu, err); + qemu_free(vs); +} + +static void v9fs_remove(V9fsState *s, V9fsPDU *pdu) +{ + int32_t fid; + V9fsRemoveState *vs; + int err = 0; + + vs = qemu_malloc(sizeof(*vs)); + vs->pdu = pdu; + vs->offset = 7; + + pdu_unmarshal(vs->pdu, vs->offset, "d", &fid); + + vs->fidp = lookup_fid(s, fid); + if (vs->fidp == NULL) { + err = -EINVAL; + goto out; + } + + err = v9fs_do_remove(s, &vs->fidp->path); + v9fs_remove_post_remove(s, vs, err); + return; + +out: + complete_pdu(s, pdu, err); + qemu_free(vs); +} + +static void v9fs_wstat_post_truncate(V9fsState *s, V9fsWstatState *vs, int err) +{ + if (err < 0) { + goto out; + } + + err = vs->offset; + +out: + v9fs_stat_free(&vs->v9stat); + complete_pdu(s, vs->pdu, err); + qemu_free(vs); +} + +static void v9fs_wstat_post_rename(V9fsState *s, V9fsWstatState *vs, int err) +{ + if (err < 0) { + goto out; + } + if (vs->v9stat.length != -1) { + if (v9fs_do_truncate(s, &vs->fidp->path, vs->v9stat.length) < 0) { + err = -errno; + } + } + v9fs_wstat_post_truncate(s, vs, err); + return; + +out: + v9fs_stat_free(&vs->v9stat); + complete_pdu(s, vs->pdu, err); + qemu_free(vs); +} + +static int v9fs_complete_rename(V9fsState *s, V9fsRenameState *vs) +{ + int err = 0; + char *old_name, *new_name; + char *end; + + if (vs->newdirfid != -1) { + V9fsFidState *dirfidp; + dirfidp = lookup_fid(s, vs->newdirfid); + + if (dirfidp == NULL) { + err = -ENOENT; + goto out; + } + + BUG_ON(dirfidp->fid_type != P9_FID_NONE); + + new_name = qemu_mallocz(dirfidp->path.size + vs->name.size + 2); + + strcpy(new_name, dirfidp->path.data); + strcat(new_name, "/"); + strcat(new_name + dirfidp->path.size, vs->name.data); + } else { + old_name = vs->fidp->path.data; + end = strrchr(old_name, '/'); + if (end) { + end++; + } else { + end = old_name; + } + new_name = qemu_mallocz(end - old_name + vs->name.size + 1); + + strncat(new_name, old_name, end - old_name); + strncat(new_name + (end - old_name), vs->name.data, vs->name.size); + } + + v9fs_string_free(&vs->name); + vs->name.data = qemu_strdup(new_name); + vs->name.size = strlen(new_name); + + if (strcmp(new_name, vs->fidp->path.data) != 0) { + if (v9fs_do_rename(s, &vs->fidp->path, &vs->name)) { + err = -errno; + } else { + V9fsFidState *fidp; + /* + * Fixup fid's pointing to the old name to + * start pointing to the new name + */ + for (fidp = s->fid_list; fidp; fidp = fidp->next) { + if (vs->fidp == fidp) { + /* + * we replace name of this fid towards the end + * so that our below strcmp will work + */ + continue; + } + if (!strncmp(vs->fidp->path.data, fidp->path.data, + strlen(vs->fidp->path.data))) { + /* replace the name */ + v9fs_fix_path(&fidp->path, &vs->name, + strlen(vs->fidp->path.data)); + } + } + v9fs_string_copy(&vs->fidp->path, &vs->name); + } + } +out: + v9fs_string_free(&vs->name); + return err; +} + +static void v9fs_rename_post_rename(V9fsState *s, V9fsRenameState *vs, int err) +{ + complete_pdu(s, vs->pdu, err); + qemu_free(vs); +} + +static void v9fs_wstat_post_chown(V9fsState *s, V9fsWstatState *vs, int err) +{ + if (err < 0) { + goto out; + } + + if (vs->v9stat.name.size != 0) { + V9fsRenameState *vr; + + vr = qemu_mallocz(sizeof(V9fsRenameState)); + vr->newdirfid = -1; + vr->pdu = vs->pdu; + vr->fidp = vs->fidp; + vr->offset = vs->offset; + vr->name.size = vs->v9stat.name.size; + vr->name.data = qemu_strdup(vs->v9stat.name.data); + + err = v9fs_complete_rename(s, vr); + qemu_free(vr); + } + v9fs_wstat_post_rename(s, vs, err); + return; + +out: + v9fs_stat_free(&vs->v9stat); + complete_pdu(s, vs->pdu, err); + qemu_free(vs); +} + +static void v9fs_rename(V9fsState *s, V9fsPDU *pdu) +{ + int32_t fid; + V9fsRenameState *vs; + ssize_t err = 0; + + vs = qemu_malloc(sizeof(*vs)); + vs->pdu = pdu; + vs->offset = 7; + + pdu_unmarshal(vs->pdu, vs->offset, "dds", &fid, &vs->newdirfid, &vs->name); + + vs->fidp = lookup_fid(s, fid); + if (vs->fidp == NULL) { + err = -ENOENT; + goto out; + } + + BUG_ON(vs->fidp->fid_type != P9_FID_NONE); + + err = v9fs_complete_rename(s, vs); + v9fs_rename_post_rename(s, vs, err); + return; +out: + complete_pdu(s, vs->pdu, err); + qemu_free(vs); +} + +static void v9fs_wstat_post_utime(V9fsState *s, V9fsWstatState *vs, int err) +{ + if (err < 0) { + goto out; + } + + if (vs->v9stat.n_gid != -1 || vs->v9stat.n_uid != -1) { + if (v9fs_do_chown(s, &vs->fidp->path, vs->v9stat.n_uid, + vs->v9stat.n_gid)) { + err = -errno; + } + } + v9fs_wstat_post_chown(s, vs, err); + return; + +out: + v9fs_stat_free(&vs->v9stat); + complete_pdu(s, vs->pdu, err); + qemu_free(vs); +} + +static void v9fs_wstat_post_chmod(V9fsState *s, V9fsWstatState *vs, int err) +{ + if (err < 0) { + goto out; + } + + if (vs->v9stat.mtime != -1 || vs->v9stat.atime != -1) { + struct timespec times[2]; + if (vs->v9stat.atime != -1) { + times[0].tv_sec = vs->v9stat.atime; + times[0].tv_nsec = 0; + } else { + times[0].tv_nsec = UTIME_OMIT; + } + if (vs->v9stat.mtime != -1) { + times[1].tv_sec = vs->v9stat.mtime; + times[1].tv_nsec = 0; + } else { + times[1].tv_nsec = UTIME_OMIT; + } + + if (v9fs_do_utimensat(s, &vs->fidp->path, times)) { + err = -errno; + } + } + + v9fs_wstat_post_utime(s, vs, err); + return; + +out: + v9fs_stat_free(&vs->v9stat); + complete_pdu(s, vs->pdu, err); + qemu_free(vs); +} + +static void v9fs_wstat_post_fsync(V9fsState *s, V9fsWstatState *vs, int err) +{ + if (err == -1) { + err = -errno; + } + v9fs_stat_free(&vs->v9stat); + complete_pdu(s, vs->pdu, err); + qemu_free(vs); +} + +static void v9fs_wstat_post_lstat(V9fsState *s, V9fsWstatState *vs, int err) +{ + uint32_t v9_mode; + + if (err == -1) { + err = -errno; + goto out; + } + + v9_mode = stat_to_v9mode(&vs->stbuf); + + if ((vs->v9stat.mode & P9_STAT_MODE_TYPE_BITS) != + (v9_mode & P9_STAT_MODE_TYPE_BITS)) { + /* Attempting to change the type */ + err = -EIO; + goto out; + } + + if (v9fs_do_chmod(s, &vs->fidp->path, v9mode_to_mode(vs->v9stat.mode, + &vs->v9stat.extension))) { + err = -errno; + } + v9fs_wstat_post_chmod(s, vs, err); + return; + +out: + v9fs_stat_free(&vs->v9stat); + complete_pdu(s, vs->pdu, err); + qemu_free(vs); +} + +static void v9fs_wstat(V9fsState *s, V9fsPDU *pdu) +{ + int32_t fid; + V9fsWstatState *vs; + int err = 0; + + vs = qemu_malloc(sizeof(*vs)); + vs->pdu = pdu; + vs->offset = 7; + + pdu_unmarshal(pdu, vs->offset, "dwS", &fid, &vs->unused, &vs->v9stat); + + vs->fidp = lookup_fid(s, fid); + if (vs->fidp == NULL) { + err = -EINVAL; + goto out; + } + + /* do we need to sync the file? */ + if (donttouch_stat(&vs->v9stat)) { + err = v9fs_do_fsync(s, vs->fidp->fs.fd, 0); + v9fs_wstat_post_fsync(s, vs, err); + return; + } + + if (vs->v9stat.mode != -1) { + err = v9fs_do_lstat(s, &vs->fidp->path, &vs->stbuf); + v9fs_wstat_post_lstat(s, vs, err); + return; + } + + v9fs_wstat_post_chmod(s, vs, err); + return; + +out: + v9fs_stat_free(&vs->v9stat); + complete_pdu(s, vs->pdu, err); + qemu_free(vs); +} + +static void v9fs_statfs_post_statfs(V9fsState *s, V9fsStatfsState *vs, int err) +{ + int32_t bsize_factor; + + if (err) { + err = -errno; + goto out; + } + + /* + * compute bsize factor based on host file system block size + * and client msize + */ + bsize_factor = (s->msize - P9_IOHDRSZ)/vs->stbuf.f_bsize; + if (!bsize_factor) { + bsize_factor = 1; + } + vs->v9statfs.f_type = vs->stbuf.f_type; + vs->v9statfs.f_bsize = vs->stbuf.f_bsize; + vs->v9statfs.f_bsize *= bsize_factor; + /* + * f_bsize is adjusted(multiplied) by bsize factor, so we need to + * adjust(divide) the number of blocks, free blocks and available + * blocks by bsize factor + */ + vs->v9statfs.f_blocks = vs->stbuf.f_blocks/bsize_factor; + vs->v9statfs.f_bfree = vs->stbuf.f_bfree/bsize_factor; + vs->v9statfs.f_bavail = vs->stbuf.f_bavail/bsize_factor; + vs->v9statfs.f_files = vs->stbuf.f_files; + vs->v9statfs.f_ffree = vs->stbuf.f_ffree; + vs->v9statfs.fsid_val = (unsigned int) vs->stbuf.f_fsid.__val[0] | + (unsigned long long)vs->stbuf.f_fsid.__val[1] << 32; + vs->v9statfs.f_namelen = vs->stbuf.f_namelen; + + vs->offset += pdu_marshal(vs->pdu, vs->offset, "ddqqqqqqd", + vs->v9statfs.f_type, vs->v9statfs.f_bsize, vs->v9statfs.f_blocks, + vs->v9statfs.f_bfree, vs->v9statfs.f_bavail, vs->v9statfs.f_files, + vs->v9statfs.f_ffree, vs->v9statfs.fsid_val, + vs->v9statfs.f_namelen); + +out: + complete_pdu(s, vs->pdu, vs->offset); + qemu_free(vs); +} + +static void v9fs_statfs(V9fsState *s, V9fsPDU *pdu) +{ + V9fsStatfsState *vs; + ssize_t err = 0; + + vs = qemu_malloc(sizeof(*vs)); + vs->pdu = pdu; + vs->offset = 7; + + memset(&vs->v9statfs, 0, sizeof(vs->v9statfs)); + + pdu_unmarshal(vs->pdu, vs->offset, "d", &vs->fid); + + vs->fidp = lookup_fid(s, vs->fid); + if (vs->fidp == NULL) { + err = -ENOENT; + goto out; + } + + err = v9fs_do_statfs(s, &vs->fidp->path, &vs->stbuf); + v9fs_statfs_post_statfs(s, vs, err); + return; + +out: + complete_pdu(s, vs->pdu, err); + qemu_free(vs); +} + +static void v9fs_mknod_post_lstat(V9fsState *s, V9fsMkState *vs, int err) +{ + if (err == -1) { + err = -errno; + goto out; + } + + stat_to_qid(&vs->stbuf, &vs->qid); + vs->offset += pdu_marshal(vs->pdu, vs->offset, "Q", &vs->qid); + err = vs->offset; +out: + complete_pdu(s, vs->pdu, err); + v9fs_string_free(&vs->fullname); + v9fs_string_free(&vs->name); + qemu_free(vs); +} + +static void v9fs_mknod_post_mknod(V9fsState *s, V9fsMkState *vs, int err) +{ + if (err == -1) { + err = -errno; + goto out; + } + + err = v9fs_do_lstat(s, &vs->fullname, &vs->stbuf); + v9fs_mknod_post_lstat(s, vs, err); + return; +out: + complete_pdu(s, vs->pdu, err); + v9fs_string_free(&vs->fullname); + v9fs_string_free(&vs->name); + qemu_free(vs); +} + +static void v9fs_mknod(V9fsState *s, V9fsPDU *pdu) +{ + int32_t fid; + V9fsMkState *vs; + int err = 0; + V9fsFidState *fidp; + gid_t gid; + int mode; + int major, minor; + + vs = qemu_malloc(sizeof(*vs)); + vs->pdu = pdu; + vs->offset = 7; + + v9fs_string_init(&vs->fullname); + pdu_unmarshal(vs->pdu, vs->offset, "dsdddd", &fid, &vs->name, &mode, + &major, &minor, &gid); + + fidp = lookup_fid(s, fid); + if (fidp == NULL) { + err = -ENOENT; + goto out; + } + + v9fs_string_sprintf(&vs->fullname, "%s/%s", fidp->path.data, vs->name.data); + err = v9fs_do_mknod(s, vs->fullname.data, mode, makedev(major, minor), + fidp->uid, gid); + v9fs_mknod_post_mknod(s, vs, err); + return; + +out: + complete_pdu(s, vs->pdu, err); + v9fs_string_free(&vs->fullname); + v9fs_string_free(&vs->name); + qemu_free(vs); +} + +/* + * Implement posix byte range locking code + * Server side handling of locking code is very simple, because 9p server in + * QEMU can handle only one client. And most of the lock handling + * (like conflict, merging) etc is done by the VFS layer itself, so no need to + * do any thing in * qemu 9p server side lock code path. + * So when a TLOCK request comes, always return success + */ + +static void v9fs_lock(V9fsState *s, V9fsPDU *pdu) +{ + int32_t fid, err = 0; + V9fsLockState *vs; + + vs = qemu_mallocz(sizeof(*vs)); + vs->pdu = pdu; + vs->offset = 7; + + vs->flock = qemu_malloc(sizeof(*vs->flock)); + pdu_unmarshal(vs->pdu, vs->offset, "dbdqqds", &fid, &vs->flock->type, + &vs->flock->flags, &vs->flock->start, &vs->flock->length, + &vs->flock->proc_id, &vs->flock->client_id); + + vs->status = P9_LOCK_ERROR; + + /* We support only block flag now (that too ignored currently) */ + if (vs->flock->flags & ~P9_LOCK_FLAGS_BLOCK) { + err = -EINVAL; + goto out; + } + vs->fidp = lookup_fid(s, fid); + if (vs->fidp == NULL) { + err = -ENOENT; + goto out; + } + + err = v9fs_do_fstat(s, vs->fidp->fs.fd, &vs->stbuf); + if (err < 0) { + err = -errno; + goto out; + } + vs->status = P9_LOCK_SUCCESS; +out: + vs->offset += pdu_marshal(vs->pdu, vs->offset, "b", vs->status); + complete_pdu(s, vs->pdu, err); + qemu_free(vs->flock); + qemu_free(vs); +} + +/* + * When a TGETLOCK request comes, always return success because all lock + * handling is done by client's VFS layer. + */ + +static void v9fs_getlock(V9fsState *s, V9fsPDU *pdu) +{ + int32_t fid, err = 0; + V9fsGetlockState *vs; + + vs = qemu_mallocz(sizeof(*vs)); + vs->pdu = pdu; + vs->offset = 7; + + vs->glock = qemu_malloc(sizeof(*vs->glock)); + pdu_unmarshal(vs->pdu, vs->offset, "dbqqds", &fid, &vs->glock->type, + &vs->glock->start, &vs->glock->length, &vs->glock->proc_id, + &vs->glock->client_id); + + vs->fidp = lookup_fid(s, fid); + if (vs->fidp == NULL) { + err = -ENOENT; + goto out; + } + + err = v9fs_do_fstat(s, vs->fidp->fs.fd, &vs->stbuf); + if (err < 0) { + err = -errno; + goto out; + } + vs->glock->type = F_UNLCK; + vs->offset += pdu_marshal(vs->pdu, vs->offset, "bqqds", vs->glock->type, + vs->glock->start, vs->glock->length, vs->glock->proc_id, + &vs->glock->client_id); +out: + complete_pdu(s, vs->pdu, err); + qemu_free(vs->glock); + qemu_free(vs); +} + +static void v9fs_mkdir_post_lstat(V9fsState *s, V9fsMkState *vs, int err) +{ + if (err == -1) { + err = -errno; + goto out; + } + + stat_to_qid(&vs->stbuf, &vs->qid); + vs->offset += pdu_marshal(vs->pdu, vs->offset, "Q", &vs->qid); + err = vs->offset; +out: + complete_pdu(s, vs->pdu, err); + v9fs_string_free(&vs->fullname); + v9fs_string_free(&vs->name); + qemu_free(vs); +} + +static void v9fs_mkdir_post_mkdir(V9fsState *s, V9fsMkState *vs, int err) +{ + if (err == -1) { + err = -errno; + goto out; + } + + err = v9fs_do_lstat(s, &vs->fullname, &vs->stbuf); + v9fs_mkdir_post_lstat(s, vs, err); + return; +out: + complete_pdu(s, vs->pdu, err); + v9fs_string_free(&vs->fullname); + v9fs_string_free(&vs->name); + qemu_free(vs); +} + +static void v9fs_mkdir(V9fsState *s, V9fsPDU *pdu) +{ + int32_t fid; + V9fsMkState *vs; + int err = 0; + V9fsFidState *fidp; + gid_t gid; + int mode; + + vs = qemu_malloc(sizeof(*vs)); + vs->pdu = pdu; + vs->offset = 7; + + v9fs_string_init(&vs->fullname); + pdu_unmarshal(vs->pdu, vs->offset, "dsdd", &fid, &vs->name, &mode, + &gid); + + fidp = lookup_fid(s, fid); + if (fidp == NULL) { + err = -ENOENT; + goto out; + } + + v9fs_string_sprintf(&vs->fullname, "%s/%s", fidp->path.data, vs->name.data); + err = v9fs_do_mkdir(s, vs->fullname.data, mode, fidp->uid, gid); + v9fs_mkdir_post_mkdir(s, vs, err); + return; + +out: + complete_pdu(s, vs->pdu, err); + v9fs_string_free(&vs->fullname); + v9fs_string_free(&vs->name); + qemu_free(vs); +} + +static void v9fs_post_xattr_getvalue(V9fsState *s, V9fsXattrState *vs, int err) +{ + + if (err < 0) { + err = -errno; + free_fid(s, vs->xattr_fidp->fid); + goto out; + } + vs->offset += pdu_marshal(vs->pdu, vs->offset, "q", vs->size); + err = vs->offset; +out: + complete_pdu(s, vs->pdu, err); + v9fs_string_free(&vs->name); + qemu_free(vs); + return; +} + +static void v9fs_post_xattr_check(V9fsState *s, V9fsXattrState *vs, ssize_t err) +{ + if (err < 0) { + err = -errno; + free_fid(s, vs->xattr_fidp->fid); + goto out; + } + /* + * Read the xattr value + */ + vs->xattr_fidp->fs.xattr.len = vs->size; + vs->xattr_fidp->fid_type = P9_FID_XATTR; + vs->xattr_fidp->fs.xattr.copied_len = -1; + if (vs->size) { + vs->xattr_fidp->fs.xattr.value = qemu_malloc(vs->size); + err = v9fs_do_lgetxattr(s, &vs->xattr_fidp->path, + &vs->name, vs->xattr_fidp->fs.xattr.value, + vs->xattr_fidp->fs.xattr.len); + } + v9fs_post_xattr_getvalue(s, vs, err); + return; +out: + complete_pdu(s, vs->pdu, err); + v9fs_string_free(&vs->name); + qemu_free(vs); +} + +static void v9fs_post_lxattr_getvalue(V9fsState *s, + V9fsXattrState *vs, int err) +{ + if (err < 0) { + err = -errno; + free_fid(s, vs->xattr_fidp->fid); + goto out; + } + vs->offset += pdu_marshal(vs->pdu, vs->offset, "q", vs->size); + err = vs->offset; +out: + complete_pdu(s, vs->pdu, err); + v9fs_string_free(&vs->name); + qemu_free(vs); + return; +} + +static void v9fs_post_lxattr_check(V9fsState *s, + V9fsXattrState *vs, ssize_t err) +{ + if (err < 0) { + err = -errno; + free_fid(s, vs->xattr_fidp->fid); + goto out; + } + /* + * Read the xattr value + */ + vs->xattr_fidp->fs.xattr.len = vs->size; + vs->xattr_fidp->fid_type = P9_FID_XATTR; + vs->xattr_fidp->fs.xattr.copied_len = -1; + if (vs->size) { + vs->xattr_fidp->fs.xattr.value = qemu_malloc(vs->size); + err = v9fs_do_llistxattr(s, &vs->xattr_fidp->path, + vs->xattr_fidp->fs.xattr.value, + vs->xattr_fidp->fs.xattr.len); + } + v9fs_post_lxattr_getvalue(s, vs, err); + return; +out: + complete_pdu(s, vs->pdu, err); + v9fs_string_free(&vs->name); + qemu_free(vs); +} + +static void v9fs_xattrwalk(V9fsState *s, V9fsPDU *pdu) +{ + ssize_t err = 0; + V9fsXattrState *vs; + int32_t fid, newfid; + + vs = qemu_malloc(sizeof(*vs)); + vs->pdu = pdu; + vs->offset = 7; + + pdu_unmarshal(vs->pdu, vs->offset, "dds", &fid, &newfid, &vs->name); + vs->file_fidp = lookup_fid(s, fid); + if (vs->file_fidp == NULL) { + err = -ENOENT; + goto out; + } + + vs->xattr_fidp = alloc_fid(s, newfid); + if (vs->xattr_fidp == NULL) { + err = -EINVAL; + goto out; + } + + v9fs_string_copy(&vs->xattr_fidp->path, &vs->file_fidp->path); + if (vs->name.data[0] == 0) { + /* + * listxattr request. Get the size first + */ + vs->size = v9fs_do_llistxattr(s, &vs->xattr_fidp->path, + NULL, 0); + if (vs->size < 0) { + err = vs->size; + } + v9fs_post_lxattr_check(s, vs, err); + return; + } else { + /* + * specific xattr fid. We check for xattr + * presence also collect the xattr size + */ + vs->size = v9fs_do_lgetxattr(s, &vs->xattr_fidp->path, + &vs->name, NULL, 0); + if (vs->size < 0) { + err = vs->size; + } + v9fs_post_xattr_check(s, vs, err); + return; + } +out: + complete_pdu(s, vs->pdu, err); + v9fs_string_free(&vs->name); + qemu_free(vs); +} + +static void v9fs_xattrcreate(V9fsState *s, V9fsPDU *pdu) +{ + int flags; + int32_t fid; + ssize_t err = 0; + V9fsXattrState *vs; + + vs = qemu_malloc(sizeof(*vs)); + vs->pdu = pdu; + vs->offset = 7; + + pdu_unmarshal(vs->pdu, vs->offset, "dsqd", + &fid, &vs->name, &vs->size, &flags); + + vs->file_fidp = lookup_fid(s, fid); + if (vs->file_fidp == NULL) { + err = -EINVAL; + goto out; + } + + /* Make the file fid point to xattr */ + vs->xattr_fidp = vs->file_fidp; + vs->xattr_fidp->fid_type = P9_FID_XATTR; + vs->xattr_fidp->fs.xattr.copied_len = 0; + vs->xattr_fidp->fs.xattr.len = vs->size; + vs->xattr_fidp->fs.xattr.flags = flags; + v9fs_string_init(&vs->xattr_fidp->fs.xattr.name); + v9fs_string_copy(&vs->xattr_fidp->fs.xattr.name, &vs->name); + if (vs->size) + vs->xattr_fidp->fs.xattr.value = qemu_malloc(vs->size); + else + vs->xattr_fidp->fs.xattr.value = NULL; + +out: + complete_pdu(s, vs->pdu, err); + v9fs_string_free(&vs->name); + qemu_free(vs); +} + +static void v9fs_readlink_post_readlink(V9fsState *s, V9fsReadLinkState *vs, + int err) +{ + if (err < 0) { + err = -errno; + goto out; + } + vs->offset += pdu_marshal(vs->pdu, vs->offset, "s", &vs->target); + err = vs->offset; +out: + complete_pdu(s, vs->pdu, err); + v9fs_string_free(&vs->target); + qemu_free(vs); +} + +static void v9fs_readlink(V9fsState *s, V9fsPDU *pdu) +{ + int32_t fid; + V9fsReadLinkState *vs; + int err = 0; + V9fsFidState *fidp; + + vs = qemu_malloc(sizeof(*vs)); + vs->pdu = pdu; + vs->offset = 7; + + pdu_unmarshal(vs->pdu, vs->offset, "d", &fid); + + fidp = lookup_fid(s, fid); + if (fidp == NULL) { + err = -ENOENT; + goto out; + } + + v9fs_string_init(&vs->target); + err = v9fs_do_readlink(s, &fidp->path, &vs->target); + v9fs_readlink_post_readlink(s, vs, err); + return; +out: + complete_pdu(s, vs->pdu, err); + qemu_free(vs); +} + +typedef void (pdu_handler_t)(V9fsState *s, V9fsPDU *pdu); + +static pdu_handler_t *pdu_handlers[] = { + [P9_TREADDIR] = v9fs_readdir, + [P9_TSTATFS] = v9fs_statfs, + [P9_TGETATTR] = v9fs_getattr, + [P9_TSETATTR] = v9fs_setattr, + [P9_TXATTRWALK] = v9fs_xattrwalk, + [P9_TXATTRCREATE] = v9fs_xattrcreate, + [P9_TMKNOD] = v9fs_mknod, + [P9_TRENAME] = v9fs_rename, + [P9_TLOCK] = v9fs_lock, + [P9_TGETLOCK] = v9fs_getlock, + [P9_TREADLINK] = v9fs_readlink, + [P9_TMKDIR] = v9fs_mkdir, + [P9_TVERSION] = v9fs_version, + [P9_TLOPEN] = v9fs_open, + [P9_TATTACH] = v9fs_attach, + [P9_TSTAT] = v9fs_stat, + [P9_TWALK] = v9fs_walk, + [P9_TCLUNK] = v9fs_clunk, + [P9_TFSYNC] = v9fs_fsync, + [P9_TOPEN] = v9fs_open, + [P9_TREAD] = v9fs_read, +#if 0 + [P9_TAUTH] = v9fs_auth, +#endif + [P9_TFLUSH] = v9fs_flush, + [P9_TLINK] = v9fs_link, + [P9_TSYMLINK] = v9fs_symlink, + [P9_TCREATE] = v9fs_create, + [P9_TLCREATE] = v9fs_lcreate, + [P9_TWRITE] = v9fs_write, + [P9_TWSTAT] = v9fs_wstat, + [P9_TREMOVE] = v9fs_remove, +}; + +static void submit_pdu(V9fsState *s, V9fsPDU *pdu) +{ + pdu_handler_t *handler; + + if (debug_9p_pdu) { + pprint_pdu(pdu); + } + + BUG_ON(pdu->id >= ARRAY_SIZE(pdu_handlers)); + + handler = pdu_handlers[pdu->id]; + BUG_ON(handler == NULL); + + handler(s, pdu); +} + +static void handle_9p_output(VirtIODevice *vdev, VirtQueue *vq) +{ + V9fsState *s = (V9fsState *)vdev; + V9fsPDU *pdu; + ssize_t len; + + while ((pdu = alloc_pdu(s)) && + (len = virtqueue_pop(vq, &pdu->elem)) != 0) { + uint8_t *ptr; + + BUG_ON(pdu->elem.out_num == 0 || pdu->elem.in_num == 0); + BUG_ON(pdu->elem.out_sg[0].iov_len < 7); + + ptr = pdu->elem.out_sg[0].iov_base; + + memcpy(&pdu->size, ptr, 4); + pdu->id = ptr[4]; + memcpy(&pdu->tag, ptr + 5, 2); + + submit_pdu(s, pdu); + } + + free_pdu(s, pdu); +} + +static uint32_t virtio_9p_get_features(VirtIODevice *vdev, uint32_t features) +{ + features |= 1 << VIRTIO_9P_MOUNT_TAG; + return features; +} + +static V9fsState *to_virtio_9p(VirtIODevice *vdev) +{ + return (V9fsState *)vdev; +} + +static void virtio_9p_get_config(VirtIODevice *vdev, uint8_t *config) +{ + struct virtio_9p_config *cfg; + V9fsState *s = to_virtio_9p(vdev); + + cfg = qemu_mallocz(sizeof(struct virtio_9p_config) + + s->tag_len); + stw_raw(&cfg->tag_len, s->tag_len); + memcpy(cfg->tag, s->tag, s->tag_len); + memcpy(config, cfg, s->config_size); + qemu_free(cfg); +} + +VirtIODevice *virtio_9p_init(DeviceState *dev, V9fsConf *conf) + { + V9fsState *s; + int i, len; + struct stat stat; + FsTypeEntry *fse; + + + s = (V9fsState *)virtio_common_init("virtio-9p", + VIRTIO_ID_9P, + sizeof(struct virtio_9p_config)+ + MAX_TAG_LEN, + sizeof(V9fsState)); + + /* initialize pdu allocator */ + QLIST_INIT(&s->free_list); + for (i = 0; i < (MAX_REQ - 1); i++) { + QLIST_INSERT_HEAD(&s->free_list, &s->pdus[i], next); + } + + s->vq = virtio_add_queue(&s->vdev, MAX_REQ, handle_9p_output); + + fse = get_fsdev_fsentry(conf->fsdev_id); + + if (!fse) { + /* We don't have a fsdev identified by fsdev_id */ + fprintf(stderr, "Virtio-9p device couldn't find fsdev with the " + "id = %s\n", conf->fsdev_id ? conf->fsdev_id : "NULL"); + exit(1); + } + + if (!fse->path || !conf->tag) { + /* we haven't specified a mount_tag or the path */ + fprintf(stderr, "fsdev with id %s needs path " + "and Virtio-9p device needs mount_tag arguments\n", + conf->fsdev_id); + exit(1); + } + + if (!strcmp(fse->security_model, "passthrough")) { + /* Files on the Fileserver set to client user credentials */ + s->ctx.fs_sm = SM_PASSTHROUGH; + s->ctx.xops = passthrough_xattr_ops; + } else if (!strcmp(fse->security_model, "mapped")) { + /* Files on the fileserver are set to QEMU credentials. + * Client user credentials are saved in extended attributes. + */ + s->ctx.fs_sm = SM_MAPPED; + s->ctx.xops = mapped_xattr_ops; + } else if (!strcmp(fse->security_model, "none")) { + /* + * Files on the fileserver are set to QEMU credentials. + */ + s->ctx.fs_sm = SM_NONE; + s->ctx.xops = none_xattr_ops; + } else { + fprintf(stderr, "Default to security_model=none. You may want" + " enable advanced security model using " + "security option:\n\t security_model=passthrough \n\t " + "security_model=mapped\n"); + s->ctx.fs_sm = SM_NONE; + s->ctx.xops = none_xattr_ops; + } + + if (lstat(fse->path, &stat)) { + fprintf(stderr, "share path %s does not exist\n", fse->path); + exit(1); + } else if (!S_ISDIR(stat.st_mode)) { + fprintf(stderr, "share path %s is not a directory \n", fse->path); + exit(1); + } + + s->ctx.fs_root = qemu_strdup(fse->path); + len = strlen(conf->tag); + if (len > MAX_TAG_LEN) { + len = MAX_TAG_LEN; + } + /* s->tag is non-NULL terminated string */ + s->tag = qemu_malloc(len); + memcpy(s->tag, conf->tag, len); + s->tag_len = len; + s->ctx.uid = -1; + + s->ops = fse->ops; + s->vdev.get_features = virtio_9p_get_features; + s->config_size = sizeof(struct virtio_9p_config) + + s->tag_len; + s->vdev.get_config = virtio_9p_get_config; + + return &s->vdev; +} diff -Nru qemu-kvm-0.12.5+noroms/hw/virtio-9p-debug.c qemu-kvm-0.14.1/hw/virtio-9p-debug.c --- qemu-kvm-0.12.5+noroms/hw/virtio-9p-debug.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/virtio-9p-debug.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,645 @@ +/* + * Virtio 9p PDU debug + * + * Copyright IBM, Corp. 2010 + * + * Authors: + * Anthony Liguori + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ +#include "virtio.h" +#include "pc.h" +#include "virtio-9p.h" +#include "virtio-9p-debug.h" + +#define BUG_ON(cond) assert(!(cond)) + +static FILE *llogfile; + +static struct iovec *get_sg(V9fsPDU *pdu, int rx) +{ + if (rx) { + return pdu->elem.in_sg; + } + return pdu->elem.out_sg; +} + +static int get_sg_count(V9fsPDU *pdu, int rx) +{ + if (rx) { + return pdu->elem.in_num; + } + return pdu->elem.out_num; + +} + +static void pprint_int8(V9fsPDU *pdu, int rx, size_t *offsetp, + const char *name) +{ + size_t copied; + int count = get_sg_count(pdu, rx); + size_t offset = *offsetp; + struct iovec *sg = get_sg(pdu, rx); + int8_t value; + + copied = do_pdu_unpack(&value, sg, count, offset, sizeof(value)); + + BUG_ON(copied != sizeof(value)); + offset += sizeof(value); + fprintf(llogfile, "%s=0x%x", name, value); + *offsetp = offset; +} + +static void pprint_int16(V9fsPDU *pdu, int rx, size_t *offsetp, + const char *name) +{ + size_t copied; + int count = get_sg_count(pdu, rx); + struct iovec *sg = get_sg(pdu, rx); + size_t offset = *offsetp; + int16_t value; + + + copied = do_pdu_unpack(&value, sg, count, offset, sizeof(value)); + + BUG_ON(copied != sizeof(value)); + offset += sizeof(value); + fprintf(llogfile, "%s=0x%x", name, value); + *offsetp = offset; +} + +static void pprint_int32(V9fsPDU *pdu, int rx, size_t *offsetp, + const char *name) +{ + size_t copied; + int count = get_sg_count(pdu, rx); + struct iovec *sg = get_sg(pdu, rx); + size_t offset = *offsetp; + int32_t value; + + + copied = do_pdu_unpack(&value, sg, count, offset, sizeof(value)); + + BUG_ON(copied != sizeof(value)); + offset += sizeof(value); + fprintf(llogfile, "%s=0x%x", name, value); + *offsetp = offset; +} + +static void pprint_int64(V9fsPDU *pdu, int rx, size_t *offsetp, + const char *name) +{ + size_t copied; + int count = get_sg_count(pdu, rx); + struct iovec *sg = get_sg(pdu, rx); + size_t offset = *offsetp; + int64_t value; + + + copied = do_pdu_unpack(&value, sg, count, offset, sizeof(value)); + + BUG_ON(copied != sizeof(value)); + offset += sizeof(value); + fprintf(llogfile, "%s=0x%" PRIx64, name, value); + *offsetp = offset; +} + +static void pprint_str(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name) +{ + int sg_count = get_sg_count(pdu, rx); + struct iovec *sg = get_sg(pdu, rx); + size_t offset = *offsetp; + uint16_t tmp_size, size; + size_t result; + size_t copied = 0; + int i = 0; + + /* get the size */ + copied = do_pdu_unpack(&tmp_size, sg, sg_count, offset, sizeof(tmp_size)); + BUG_ON(copied != sizeof(tmp_size)); + size = le16_to_cpupu(&tmp_size); + offset += copied; + + fprintf(llogfile, "%s=", name); + for (i = 0; size && i < sg_count; i++) { + size_t len; + if (offset >= sg[i].iov_len) { + /* skip this sg */ + offset -= sg[i].iov_len; + continue; + } else { + len = MIN(sg[i].iov_len - offset, size); + result = fwrite(sg[i].iov_base + offset, 1, len, llogfile); + BUG_ON(result != len); + size -= len; + copied += len; + if (size) { + offset = 0; + continue; + } + } + } + *offsetp += copied; +} + +static void pprint_qid(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name) +{ + fprintf(llogfile, "%s={", name); + pprint_int8(pdu, rx, offsetp, "type"); + pprint_int32(pdu, rx, offsetp, ", version"); + pprint_int64(pdu, rx, offsetp, ", path"); + fprintf(llogfile, "}"); +} + +static void pprint_stat(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name) +{ + fprintf(llogfile, "%s={", name); + pprint_int16(pdu, rx, offsetp, "size"); + pprint_int16(pdu, rx, offsetp, ", type"); + pprint_int32(pdu, rx, offsetp, ", dev"); + pprint_qid(pdu, rx, offsetp, ", qid"); + pprint_int32(pdu, rx, offsetp, ", mode"); + pprint_int32(pdu, rx, offsetp, ", atime"); + pprint_int32(pdu, rx, offsetp, ", mtime"); + pprint_int64(pdu, rx, offsetp, ", length"); + pprint_str(pdu, rx, offsetp, ", name"); + pprint_str(pdu, rx, offsetp, ", uid"); + pprint_str(pdu, rx, offsetp, ", gid"); + pprint_str(pdu, rx, offsetp, ", muid"); + pprint_str(pdu, rx, offsetp, ", extension"); + pprint_int32(pdu, rx, offsetp, ", uid"); + pprint_int32(pdu, rx, offsetp, ", gid"); + pprint_int32(pdu, rx, offsetp, ", muid"); + fprintf(llogfile, "}"); +} + +static void pprint_stat_dotl(V9fsPDU *pdu, int rx, size_t *offsetp, + const char *name) +{ + fprintf(llogfile, "%s={", name); + pprint_qid(pdu, rx, offsetp, "qid"); + pprint_int32(pdu, rx, offsetp, ", st_mode"); + pprint_int64(pdu, rx, offsetp, ", st_nlink"); + pprint_int32(pdu, rx, offsetp, ", st_uid"); + pprint_int32(pdu, rx, offsetp, ", st_gid"); + pprint_int64(pdu, rx, offsetp, ", st_rdev"); + pprint_int64(pdu, rx, offsetp, ", st_size"); + pprint_int64(pdu, rx, offsetp, ", st_blksize"); + pprint_int64(pdu, rx, offsetp, ", st_blocks"); + pprint_int64(pdu, rx, offsetp, ", atime"); + pprint_int64(pdu, rx, offsetp, ", atime_nsec"); + pprint_int64(pdu, rx, offsetp, ", mtime"); + pprint_int64(pdu, rx, offsetp, ", mtime_nsec"); + pprint_int64(pdu, rx, offsetp, ", ctime"); + pprint_int64(pdu, rx, offsetp, ", ctime_nsec"); + fprintf(llogfile, "}"); +} + + + +static void pprint_strs(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name) +{ + int sg_count = get_sg_count(pdu, rx); + struct iovec *sg = get_sg(pdu, rx); + size_t offset = *offsetp; + uint16_t tmp_count, count, i; + size_t copied = 0; + + fprintf(llogfile, "%s={", name); + + /* Get the count */ + copied = do_pdu_unpack(&tmp_count, sg, sg_count, offset, sizeof(tmp_count)); + BUG_ON(copied != sizeof(tmp_count)); + count = le16_to_cpupu(&tmp_count); + offset += copied; + + for (i = 0; i < count; i++) { + char str[512]; + if (i) { + fprintf(llogfile, ", "); + } + snprintf(str, sizeof(str), "[%d]", i); + pprint_str(pdu, rx, &offset, str); + } + + fprintf(llogfile, "}"); + + *offsetp = offset; +} + +static void pprint_qids(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name) +{ + int sg_count = get_sg_count(pdu, rx); + struct iovec *sg = get_sg(pdu, rx); + size_t offset = *offsetp; + uint16_t tmp_count, count, i; + size_t copied = 0; + + fprintf(llogfile, "%s={", name); + + copied = do_pdu_unpack(&tmp_count, sg, sg_count, offset, sizeof(tmp_count)); + BUG_ON(copied != sizeof(tmp_count)); + count = le16_to_cpupu(&tmp_count); + offset += copied; + + for (i = 0; i < count; i++) { + char str[512]; + if (i) { + fprintf(llogfile, ", "); + } + snprintf(str, sizeof(str), "[%d]", i); + pprint_qid(pdu, rx, &offset, str); + } + + fprintf(llogfile, "}"); + + *offsetp = offset; +} + +static void pprint_sg(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name) +{ + struct iovec *sg = get_sg(pdu, rx); + unsigned int count; + int i; + + if (rx) { + count = pdu->elem.in_num; + } else { + count = pdu->elem.out_num; + } + + fprintf(llogfile, "%s={", name); + for (i = 0; i < count; i++) { + if (i) { + fprintf(llogfile, ", "); + } + fprintf(llogfile, "(%p, 0x%zx)", sg[i].iov_base, sg[i].iov_len); + } + fprintf(llogfile, "}"); +} + +/* FIXME: read from a directory fid returns serialized stat_t's */ +#ifdef DEBUG_DATA +static void pprint_data(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name) +{ + struct iovec *sg = get_sg(pdu, rx); + size_t offset = *offsetp; + unsigned int count; + int32_t size; + int total, i, j; + ssize_t len; + + if (rx) { + count = pdu->elem.in_num; + } else + count = pdu->elem.out_num; + } + + BUG_ON((offset + sizeof(size)) > sg[0].iov_len); + + memcpy(&size, sg[0].iov_base + offset, sizeof(size)); + offset += sizeof(size); + + fprintf(llogfile, "size: %x\n", size); + + sg[0].iov_base += 11; /* skip header */ + sg[0].iov_len -= 11; + + total = 0; + for (i = 0; i < count; i++) { + total += sg[i].iov_len; + if (total >= size) { + /* trim sg list so writev does the right thing */ + sg[i].iov_len -= (total - size); + i++; + break; + } + } + + fprintf(llogfile, "%s={\"", name); + fflush(llogfile); + for (j = 0; j < i; j++) { + if (j) { + fprintf(llogfile, "\", \""); + fflush(llogfile); + } + + do { + len = writev(fileno(llogfile), &sg[j], 1); + } while (len == -1 && errno == EINTR); + fprintf(llogfile, "len == %ld: %m\n", len); + BUG_ON(len != sg[j].iov_len); + } + fprintf(llogfile, "\"}"); + + sg[0].iov_base -= 11; + sg[0].iov_len += 11; + +} +#endif + +void pprint_pdu(V9fsPDU *pdu) +{ + size_t offset = 7; + + if (llogfile == NULL) { + llogfile = fopen("/tmp/pdu.log", "w"); + } + + BUG_ON(!llogfile); + + switch (pdu->id) { + case P9_TREADDIR: + fprintf(llogfile, "TREADDIR: ("); + pprint_int32(pdu, 0, &offset, "fid"); + pprint_int64(pdu, 0, &offset, ", initial offset"); + pprint_int32(pdu, 0, &offset, ", max count"); + break; + case P9_RREADDIR: + fprintf(llogfile, "RREADDIR: ("); + pprint_int32(pdu, 1, &offset, "count"); +#ifdef DEBUG_DATA + pprint_data(pdu, 1, &offset, ", data"); +#endif + break; + case P9_TMKDIR: + fprintf(llogfile, "TMKDIR: ("); + pprint_int32(pdu, 0, &offset, "fid"); + pprint_str(pdu, 0, &offset, "name"); + pprint_int32(pdu, 0, &offset, "mode"); + pprint_int32(pdu, 0, &offset, "gid"); + break; + case P9_RMKDIR: + fprintf(llogfile, "RMKDIR: ("); + pprint_qid(pdu, 0, &offset, "qid"); + break; + case P9_TVERSION: + fprintf(llogfile, "TVERSION: ("); + pprint_int32(pdu, 0, &offset, "msize"); + pprint_str(pdu, 0, &offset, ", version"); + break; + case P9_RVERSION: + fprintf(llogfile, "RVERSION: ("); + pprint_int32(pdu, 1, &offset, "msize"); + pprint_str(pdu, 1, &offset, ", version"); + break; + case P9_TGETATTR: + fprintf(llogfile, "TGETATTR: ("); + pprint_int32(pdu, 0, &offset, "fid"); + break; + case P9_RGETATTR: + fprintf(llogfile, "RGETATTR: ("); + pprint_stat_dotl(pdu, 1, &offset, "getattr"); + break; + case P9_TAUTH: + fprintf(llogfile, "TAUTH: ("); + pprint_int32(pdu, 0, &offset, "afid"); + pprint_str(pdu, 0, &offset, ", uname"); + pprint_str(pdu, 0, &offset, ", aname"); + pprint_int32(pdu, 0, &offset, ", n_uname"); + break; + case P9_RAUTH: + fprintf(llogfile, "RAUTH: ("); + pprint_qid(pdu, 1, &offset, "qid"); + break; + case P9_TATTACH: + fprintf(llogfile, "TATTACH: ("); + pprint_int32(pdu, 0, &offset, "fid"); + pprint_int32(pdu, 0, &offset, ", afid"); + pprint_str(pdu, 0, &offset, ", uname"); + pprint_str(pdu, 0, &offset, ", aname"); + pprint_int32(pdu, 0, &offset, ", n_uname"); + break; + case P9_RATTACH: + fprintf(llogfile, "RATTACH: ("); + pprint_qid(pdu, 1, &offset, "qid"); + break; + case P9_TERROR: + fprintf(llogfile, "TERROR: ("); + break; + case P9_RERROR: + fprintf(llogfile, "RERROR: ("); + pprint_str(pdu, 1, &offset, "ename"); + pprint_int32(pdu, 1, &offset, ", ecode"); + break; + case P9_TFLUSH: + fprintf(llogfile, "TFLUSH: ("); + pprint_int16(pdu, 0, &offset, "oldtag"); + break; + case P9_RFLUSH: + fprintf(llogfile, "RFLUSH: ("); + break; + case P9_TWALK: + fprintf(llogfile, "TWALK: ("); + pprint_int32(pdu, 0, &offset, "fid"); + pprint_int32(pdu, 0, &offset, ", newfid"); + pprint_strs(pdu, 0, &offset, ", wnames"); + break; + case P9_RWALK: + fprintf(llogfile, "RWALK: ("); + pprint_qids(pdu, 1, &offset, "wqids"); + break; + case P9_TOPEN: + fprintf(llogfile, "TOPEN: ("); + pprint_int32(pdu, 0, &offset, "fid"); + pprint_int8(pdu, 0, &offset, ", mode"); + break; + case P9_ROPEN: + fprintf(llogfile, "ROPEN: ("); + pprint_qid(pdu, 1, &offset, "qid"); + pprint_int32(pdu, 1, &offset, ", iounit"); + break; + case P9_TCREATE: + fprintf(llogfile, "TCREATE: ("); + pprint_int32(pdu, 0, &offset, "fid"); + pprint_str(pdu, 0, &offset, ", name"); + pprint_int32(pdu, 0, &offset, ", perm"); + pprint_int8(pdu, 0, &offset, ", mode"); + pprint_str(pdu, 0, &offset, ", extension"); + break; + case P9_RCREATE: + fprintf(llogfile, "RCREATE: ("); + pprint_qid(pdu, 1, &offset, "qid"); + pprint_int32(pdu, 1, &offset, ", iounit"); + break; + case P9_TSYMLINK: + fprintf(llogfile, "TSYMLINK: ("); + pprint_int32(pdu, 0, &offset, "fid"); + pprint_str(pdu, 0, &offset, ", name"); + pprint_str(pdu, 0, &offset, ", symname"); + pprint_int32(pdu, 0, &offset, ", gid"); + break; + case P9_RSYMLINK: + fprintf(llogfile, "RSYMLINK: ("); + pprint_qid(pdu, 1, &offset, "qid"); + break; + case P9_TLCREATE: + fprintf(llogfile, "TLCREATE: ("); + pprint_int32(pdu, 0, &offset, "dfid"); + pprint_str(pdu, 0, &offset, ", name"); + pprint_int32(pdu, 0, &offset, ", flags"); + pprint_int32(pdu, 0, &offset, ", mode"); + pprint_int32(pdu, 0, &offset, ", gid"); + break; + case P9_RLCREATE: + fprintf(llogfile, "RLCREATE: ("); + pprint_qid(pdu, 1, &offset, "qid"); + pprint_int32(pdu, 1, &offset, ", iounit"); + break; + case P9_TMKNOD: + fprintf(llogfile, "TMKNOD: ("); + pprint_int32(pdu, 0, &offset, "fid"); + pprint_str(pdu, 0, &offset, "name"); + pprint_int32(pdu, 0, &offset, "mode"); + pprint_int32(pdu, 0, &offset, "major"); + pprint_int32(pdu, 0, &offset, "minor"); + pprint_int32(pdu, 0, &offset, "gid"); + break; + case P9_RMKNOD: + fprintf(llogfile, "RMKNOD: )"); + pprint_qid(pdu, 0, &offset, "qid"); + break; + case P9_TREADLINK: + fprintf(llogfile, "TREADLINK: ("); + pprint_int32(pdu, 0, &offset, "fid"); + break; + case P9_RREADLINK: + fprintf(llogfile, "RREADLINK: ("); + pprint_str(pdu, 0, &offset, "target"); + break; + case P9_TREAD: + fprintf(llogfile, "TREAD: ("); + pprint_int32(pdu, 0, &offset, "fid"); + pprint_int64(pdu, 0, &offset, ", offset"); + pprint_int32(pdu, 0, &offset, ", count"); + pprint_sg(pdu, 0, &offset, ", sg"); + break; + case P9_RREAD: + fprintf(llogfile, "RREAD: ("); + pprint_int32(pdu, 1, &offset, "count"); + pprint_sg(pdu, 1, &offset, ", sg"); + offset = 7; +#ifdef DEBUG_DATA + pprint_data(pdu, 1, &offset, ", data"); +#endif + break; + case P9_TWRITE: + fprintf(llogfile, "TWRITE: ("); + pprint_int32(pdu, 0, &offset, "fid"); + pprint_int64(pdu, 0, &offset, ", offset"); + pprint_int32(pdu, 0, &offset, ", count"); + break; + case P9_RWRITE: + fprintf(llogfile, "RWRITE: ("); + pprint_int32(pdu, 1, &offset, "count"); + break; + case P9_TCLUNK: + fprintf(llogfile, "TCLUNK: ("); + pprint_int32(pdu, 0, &offset, "fid"); + break; + case P9_RCLUNK: + fprintf(llogfile, "RCLUNK: ("); + break; + case P9_TFSYNC: + fprintf(llogfile, "TFSYNC: ("); + pprint_int32(pdu, 0, &offset, "fid"); + break; + case P9_RFSYNC: + fprintf(llogfile, "RFSYNC: ("); + break; + case P9_TLINK: + fprintf(llogfile, "TLINK: ("); + pprint_int32(pdu, 0, &offset, "dfid"); + pprint_int32(pdu, 0, &offset, ", fid"); + pprint_str(pdu, 0, &offset, ", newpath"); + break; + case P9_RLINK: + fprintf(llogfile, "RLINK: ("); + break; + case P9_TREMOVE: + fprintf(llogfile, "TREMOVE: ("); + pprint_int32(pdu, 0, &offset, "fid"); + break; + case P9_RREMOVE: + fprintf(llogfile, "RREMOVE: ("); + break; + case P9_TSTAT: + fprintf(llogfile, "TSTAT: ("); + pprint_int32(pdu, 0, &offset, "fid"); + break; + case P9_RSTAT: + fprintf(llogfile, "RSTAT: ("); + offset += 2; /* ignored */ + pprint_stat(pdu, 1, &offset, "stat"); + break; + case P9_TWSTAT: + fprintf(llogfile, "TWSTAT: ("); + pprint_int32(pdu, 0, &offset, "fid"); + offset += 2; /* ignored */ + pprint_stat(pdu, 0, &offset, ", stat"); + break; + case P9_RWSTAT: + fprintf(llogfile, "RWSTAT: ("); + break; + case P9_TXATTRWALK: + fprintf(llogfile, "TXATTRWALK: ("); + pprint_int32(pdu, 0, &offset, "fid"); + pprint_int32(pdu, 0, &offset, ", newfid"); + pprint_str(pdu, 0, &offset, ", xattr name"); + break; + case P9_RXATTRWALK: + fprintf(llogfile, "RXATTRWALK: ("); + pprint_int64(pdu, 1, &offset, "xattrsize"); + case P9_TXATTRCREATE: + fprintf(llogfile, "TXATTRCREATE: ("); + pprint_int32(pdu, 0, &offset, "fid"); + pprint_str(pdu, 0, &offset, ", name"); + pprint_int64(pdu, 0, &offset, ", xattrsize"); + pprint_int32(pdu, 0, &offset, ", flags"); + break; + case P9_RXATTRCREATE: + fprintf(llogfile, "RXATTRCREATE: ("); + break; + case P9_TLOCK: + fprintf(llogfile, "TLOCK: ("); + pprint_int32(pdu, 0, &offset, "fid"); + pprint_int8(pdu, 0, &offset, ", type"); + pprint_int32(pdu, 0, &offset, ", flags"); + pprint_int64(pdu, 0, &offset, ", start"); + pprint_int64(pdu, 0, &offset, ", length"); + pprint_int32(pdu, 0, &offset, ", proc_id"); + pprint_str(pdu, 0, &offset, ", client_id"); + break; + case P9_RLOCK: + fprintf(llogfile, "RLOCK: ("); + pprint_int8(pdu, 0, &offset, "status"); + break; + case P9_TGETLOCK: + fprintf(llogfile, "TGETLOCK: ("); + pprint_int32(pdu, 0, &offset, "fid"); + pprint_int8(pdu, 0, &offset, ", type"); + pprint_int64(pdu, 0, &offset, ", start"); + pprint_int64(pdu, 0, &offset, ", length"); + pprint_int32(pdu, 0, &offset, ", proc_id"); + pprint_str(pdu, 0, &offset, ", client_id"); + break; + case P9_RGETLOCK: + fprintf(llogfile, "RGETLOCK: ("); + pprint_int8(pdu, 0, &offset, "type"); + pprint_int64(pdu, 0, &offset, ", start"); + pprint_int64(pdu, 0, &offset, ", length"); + pprint_int32(pdu, 0, &offset, ", proc_id"); + pprint_str(pdu, 0, &offset, ", client_id"); + break; + default: + fprintf(llogfile, "unknown(%d): (", pdu->id); + break; + } + + fprintf(llogfile, ")\n"); + /* Flush the log message out */ + fflush(llogfile); +} diff -Nru qemu-kvm-0.12.5+noroms/hw/virtio-9p-debug.h qemu-kvm-0.14.1/hw/virtio-9p-debug.h --- qemu-kvm-0.12.5+noroms/hw/virtio-9p-debug.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/virtio-9p-debug.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,6 @@ +#ifndef _QEMU_VIRTIO_9P_DEBUG_H +#define _QEMU_VIRTIO_9P_DEBUG_H + +void pprint_pdu(V9fsPDU *pdu); + +#endif diff -Nru qemu-kvm-0.12.5+noroms/hw/virtio-9p.h qemu-kvm-0.14.1/hw/virtio-9p.h --- qemu-kvm-0.12.5+noroms/hw/virtio-9p.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/virtio-9p.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,507 @@ +#ifndef _QEMU_VIRTIO_9P_H +#define _QEMU_VIRTIO_9P_H + +#include +#include +#include +#include + +#include "file-op-9p.h" + +/* The feature bitmap for virtio 9P */ +/* The mount point is specified in a config variable */ +#define VIRTIO_9P_MOUNT_TAG 0 + +enum { + P9_TLERROR = 6, + P9_RLERROR, + P9_TSTATFS = 8, + P9_RSTATFS, + P9_TLOPEN = 12, + P9_RLOPEN, + P9_TLCREATE = 14, + P9_RLCREATE, + P9_TSYMLINK = 16, + P9_RSYMLINK, + P9_TMKNOD = 18, + P9_RMKNOD, + P9_TRENAME = 20, + P9_RRENAME, + P9_TREADLINK = 22, + P9_RREADLINK, + P9_TGETATTR = 24, + P9_RGETATTR, + P9_TSETATTR = 26, + P9_RSETATTR, + P9_TXATTRWALK = 30, + P9_RXATTRWALK, + P9_TXATTRCREATE = 32, + P9_RXATTRCREATE, + P9_TREADDIR = 40, + P9_RREADDIR, + P9_TFSYNC = 50, + P9_RFSYNC, + P9_TLOCK = 52, + P9_RLOCK, + P9_TGETLOCK = 54, + P9_RGETLOCK, + P9_TLINK = 70, + P9_RLINK, + P9_TMKDIR = 72, + P9_RMKDIR, + P9_TVERSION = 100, + P9_RVERSION, + P9_TAUTH = 102, + P9_RAUTH, + P9_TATTACH = 104, + P9_RATTACH, + P9_TERROR = 106, + P9_RERROR, + P9_TFLUSH = 108, + P9_RFLUSH, + P9_TWALK = 110, + P9_RWALK, + P9_TOPEN = 112, + P9_ROPEN, + P9_TCREATE = 114, + P9_RCREATE, + P9_TREAD = 116, + P9_RREAD, + P9_TWRITE = 118, + P9_RWRITE, + P9_TCLUNK = 120, + P9_RCLUNK, + P9_TREMOVE = 122, + P9_RREMOVE, + P9_TSTAT = 124, + P9_RSTAT, + P9_TWSTAT = 126, + P9_RWSTAT, +}; + + +/* qid.types */ +enum { + P9_QTDIR = 0x80, + P9_QTAPPEND = 0x40, + P9_QTEXCL = 0x20, + P9_QTMOUNT = 0x10, + P9_QTAUTH = 0x08, + P9_QTTMP = 0x04, + P9_QTSYMLINK = 0x02, + P9_QTLINK = 0x01, + P9_QTFILE = 0x00, +}; + +enum p9_proto_version { + V9FS_PROTO_2000U = 0x01, + V9FS_PROTO_2000L = 0x02, +}; + +#define P9_NOTAG (u16)(~0) +#define P9_NOFID (u32)(~0) +#define P9_MAXWELEM 16 + +/* + * ample room for Twrite/Rread header + * size[4] Tread/Twrite tag[2] fid[4] offset[8] count[4] + */ +#define P9_IOHDRSZ 24 + +typedef struct V9fsPDU V9fsPDU; + +struct V9fsPDU +{ + uint32_t size; + uint16_t tag; + uint8_t id; + VirtQueueElement elem; + QLIST_ENTRY(V9fsPDU) next; +}; + + +/* FIXME + * 1) change user needs to set groups and stuff + */ + +/* from Linux's linux/virtio_9p.h */ + +/* The ID for virtio console */ +#define VIRTIO_ID_9P 9 +#define MAX_REQ 128 +#define MAX_TAG_LEN 32 + +#define BUG_ON(cond) assert(!(cond)) + +typedef struct V9fsFidState V9fsFidState; + +typedef struct V9fsString +{ + int16_t size; + char *data; +} V9fsString; + +typedef struct V9fsQID +{ + int8_t type; + int32_t version; + int64_t path; +} V9fsQID; + +typedef struct V9fsStat +{ + int16_t size; + int16_t type; + int32_t dev; + V9fsQID qid; + int32_t mode; + int32_t atime; + int32_t mtime; + int64_t length; + V9fsString name; + V9fsString uid; + V9fsString gid; + V9fsString muid; + /* 9p2000.u */ + V9fsString extension; + int32_t n_uid; + int32_t n_gid; + int32_t n_muid; +} V9fsStat; + +enum { + P9_FID_NONE = 0, + P9_FID_FILE, + P9_FID_DIR, + P9_FID_XATTR, +}; + +typedef struct V9fsXattr +{ + int64_t copied_len; + int64_t len; + void *value; + V9fsString name; + int flags; +} V9fsXattr; + +struct V9fsFidState +{ + int fid_type; + int32_t fid; + V9fsString path; + union { + int fd; + DIR *dir; + V9fsXattr xattr; + } fs; + uid_t uid; + V9fsFidState *next; +}; + +typedef struct V9fsState +{ + VirtIODevice vdev; + VirtQueue *vq; + V9fsPDU pdus[MAX_REQ]; + QLIST_HEAD(, V9fsPDU) free_list; + V9fsFidState *fid_list; + FileOperations *ops; + FsContext ctx; + uint16_t tag_len; + uint8_t *tag; + size_t config_size; + enum p9_proto_version proto_version; + int32_t msize; +} V9fsState; + +typedef struct V9fsCreateState { + V9fsPDU *pdu; + size_t offset; + V9fsFidState *fidp; + V9fsQID qid; + int32_t perm; + int8_t mode; + struct stat stbuf; + V9fsString name; + V9fsString extension; + V9fsString fullname; + int iounit; +} V9fsCreateState; + +typedef struct V9fsLcreateState { + V9fsPDU *pdu; + size_t offset; + V9fsFidState *fidp; + V9fsQID qid; + int32_t iounit; + struct stat stbuf; + V9fsString name; + V9fsString fullname; +} V9fsLcreateState; + +typedef struct V9fsStatState { + V9fsPDU *pdu; + size_t offset; + V9fsStat v9stat; + V9fsFidState *fidp; + struct stat stbuf; +} V9fsStatState; + +typedef struct V9fsStatDotl { + uint64_t st_result_mask; + V9fsQID qid; + uint32_t st_mode; + uint32_t st_uid; + uint32_t st_gid; + uint64_t st_nlink; + uint64_t st_rdev; + uint64_t st_size; + uint64_t st_blksize; + uint64_t st_blocks; + uint64_t st_atime_sec; + uint64_t st_atime_nsec; + uint64_t st_mtime_sec; + uint64_t st_mtime_nsec; + uint64_t st_ctime_sec; + uint64_t st_ctime_nsec; + uint64_t st_btime_sec; + uint64_t st_btime_nsec; + uint64_t st_gen; + uint64_t st_data_version; +} V9fsStatDotl; + +typedef struct V9fsStatStateDotl { + V9fsPDU *pdu; + size_t offset; + V9fsStatDotl v9stat_dotl; + struct stat stbuf; +} V9fsStatStateDotl; + + +typedef struct V9fsWalkState { + V9fsPDU *pdu; + size_t offset; + int16_t nwnames; + int name_idx; + V9fsQID *qids; + V9fsFidState *fidp; + V9fsFidState *newfidp; + V9fsString path; + V9fsString *wnames; + struct stat stbuf; +} V9fsWalkState; + +typedef struct V9fsOpenState { + V9fsPDU *pdu; + size_t offset; + int32_t mode; + V9fsFidState *fidp; + V9fsQID qid; + struct stat stbuf; + int iounit; +} V9fsOpenState; + +typedef struct V9fsReadState { + V9fsPDU *pdu; + size_t offset; + int32_t count; + int32_t total; + int64_t off; + V9fsFidState *fidp; + struct iovec iov[128]; /* FIXME: bad, bad, bad */ + struct iovec *sg; + off_t dir_pos; + struct dirent *dent; + struct stat stbuf; + V9fsString name; + V9fsStat v9stat; + int32_t len; + int32_t cnt; + int32_t max_count; +} V9fsReadState; + +typedef struct V9fsWriteState { + V9fsPDU *pdu; + size_t offset; + int32_t len; + int32_t count; + int32_t total; + int64_t off; + V9fsFidState *fidp; + struct iovec iov[128]; /* FIXME: bad, bad, bad */ + struct iovec *sg; + int cnt; +} V9fsWriteState; + +typedef struct V9fsRemoveState { + V9fsPDU *pdu; + size_t offset; + V9fsFidState *fidp; +} V9fsRemoveState; + +typedef struct V9fsWstatState +{ + V9fsPDU *pdu; + size_t offset; + int16_t unused; + V9fsStat v9stat; + V9fsFidState *fidp; + struct stat stbuf; +} V9fsWstatState; + +typedef struct V9fsSymlinkState +{ + V9fsPDU *pdu; + size_t offset; + V9fsString name; + V9fsString symname; + V9fsString fullname; + V9fsFidState *dfidp; + V9fsQID qid; + struct stat stbuf; +} V9fsSymlinkState; + +typedef struct V9fsIattr +{ + int32_t valid; + int32_t mode; + int32_t uid; + int32_t gid; + int64_t size; + int64_t atime_sec; + int64_t atime_nsec; + int64_t mtime_sec; + int64_t mtime_nsec; +} V9fsIattr; + +typedef struct V9fsSetattrState +{ + V9fsPDU *pdu; + size_t offset; + V9fsIattr v9iattr; + V9fsFidState *fidp; +} V9fsSetattrState; + +struct virtio_9p_config +{ + /* number of characters in tag */ + uint16_t tag_len; + /* Variable size tag name */ + uint8_t tag[0]; +} __attribute__((packed)); + +typedef struct V9fsStatfs +{ + uint32_t f_type; + uint32_t f_bsize; + uint64_t f_blocks; + uint64_t f_bfree; + uint64_t f_bavail; + uint64_t f_files; + uint64_t f_ffree; + uint64_t fsid_val; + uint32_t f_namelen; +} V9fsStatfs; + +typedef struct V9fsStatfsState { + V9fsPDU *pdu; + size_t offset; + int32_t fid; + V9fsStatfs v9statfs; + V9fsFidState *fidp; + struct statfs stbuf; +} V9fsStatfsState; + +typedef struct V9fsMkState { + V9fsPDU *pdu; + size_t offset; + V9fsQID qid; + struct stat stbuf; + V9fsString name; + V9fsString fullname; +} V9fsMkState; + +typedef struct V9fsRenameState { + V9fsPDU *pdu; + V9fsFidState *fidp; + size_t offset; + int32_t newdirfid; + V9fsString name; +} V9fsRenameState; + +typedef struct V9fsXattrState +{ + V9fsPDU *pdu; + size_t offset; + V9fsFidState *file_fidp; + V9fsFidState *xattr_fidp; + V9fsString name; + int64_t size; + int flags; + void *value; +} V9fsXattrState; + +#define P9_LOCK_SUCCESS 0 +#define P9_LOCK_BLOCKED 1 +#define P9_LOCK_ERROR 2 +#define P9_LOCK_GRACE 3 + +#define P9_LOCK_FLAGS_BLOCK 1 +#define P9_LOCK_FLAGS_RECLAIM 2 + +typedef struct V9fsFlock +{ + uint8_t type; + uint32_t flags; + uint64_t start; /* absolute offset */ + uint64_t length; + uint32_t proc_id; + V9fsString client_id; +} V9fsFlock; + +typedef struct V9fsLockState +{ + V9fsPDU *pdu; + size_t offset; + int8_t status; + struct stat stbuf; + V9fsFidState *fidp; + V9fsFlock *flock; +} V9fsLockState; + +typedef struct V9fsGetlock +{ + uint8_t type; + uint64_t start; /* absolute offset */ + uint64_t length; + uint32_t proc_id; + V9fsString client_id; +} V9fsGetlock; + +typedef struct V9fsGetlockState +{ + V9fsPDU *pdu; + size_t offset; + struct stat stbuf; + V9fsFidState *fidp; + V9fsGetlock *glock; +} V9fsGetlockState; + +typedef struct V9fsReadLinkState +{ + V9fsPDU *pdu; + size_t offset; + V9fsString target; +} V9fsReadLinkState; + +size_t pdu_packunpack(void *addr, struct iovec *sg, int sg_count, + size_t offset, size_t size, int pack); + +static inline size_t do_pdu_unpack(void *dst, struct iovec *sg, int sg_count, + size_t offset, size_t size) +{ + return pdu_packunpack(dst, sg, sg_count, offset, size, 0); +} + +#endif diff -Nru qemu-kvm-0.12.5+noroms/hw/virtio-9p-local.c qemu-kvm-0.14.1/hw/virtio-9p-local.c --- qemu-kvm-0.12.5+noroms/hw/virtio-9p-local.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/virtio-9p-local.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,563 @@ +/* + * Virtio 9p Posix callback + * + * Copyright IBM, Corp. 2010 + * + * Authors: + * Anthony Liguori + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ +#include "virtio.h" +#include "virtio-9p.h" +#include "virtio-9p-xattr.h" +#include +#include +#include +#include +#include +#include + + +static int local_lstat(FsContext *fs_ctx, const char *path, struct stat *stbuf) +{ + int err; + err = lstat(rpath(fs_ctx, path), stbuf); + if (err) { + return err; + } + if (fs_ctx->fs_sm == SM_MAPPED) { + /* Actual credentials are part of extended attrs */ + uid_t tmp_uid; + gid_t tmp_gid; + mode_t tmp_mode; + dev_t tmp_dev; + if (getxattr(rpath(fs_ctx, path), "user.virtfs.uid", &tmp_uid, + sizeof(uid_t)) > 0) { + stbuf->st_uid = tmp_uid; + } + if (getxattr(rpath(fs_ctx, path), "user.virtfs.gid", &tmp_gid, + sizeof(gid_t)) > 0) { + stbuf->st_gid = tmp_gid; + } + if (getxattr(rpath(fs_ctx, path), "user.virtfs.mode", &tmp_mode, + sizeof(mode_t)) > 0) { + stbuf->st_mode = tmp_mode; + } + if (getxattr(rpath(fs_ctx, path), "user.virtfs.rdev", &tmp_dev, + sizeof(dev_t)) > 0) { + stbuf->st_rdev = tmp_dev; + } + } + return err; +} + +static int local_set_xattr(const char *path, FsCred *credp) +{ + int err; + if (credp->fc_uid != -1) { + err = setxattr(path, "user.virtfs.uid", &credp->fc_uid, sizeof(uid_t), + 0); + if (err) { + return err; + } + } + if (credp->fc_gid != -1) { + err = setxattr(path, "user.virtfs.gid", &credp->fc_gid, sizeof(gid_t), + 0); + if (err) { + return err; + } + } + if (credp->fc_mode != -1) { + err = setxattr(path, "user.virtfs.mode", &credp->fc_mode, + sizeof(mode_t), 0); + if (err) { + return err; + } + } + if (credp->fc_rdev != -1) { + err = setxattr(path, "user.virtfs.rdev", &credp->fc_rdev, + sizeof(dev_t), 0); + if (err) { + return err; + } + } + return 0; +} + +static int local_post_create_passthrough(FsContext *fs_ctx, const char *path, + FsCred *credp) +{ + if (chmod(rpath(fs_ctx, path), credp->fc_mode & 07777) < 0) { + return -1; + } + if (lchown(rpath(fs_ctx, path), credp->fc_uid, credp->fc_gid) < 0) { + /* + * If we fail to change ownership and if we are + * using security model none. Ignore the error + */ + if (fs_ctx->fs_sm != SM_NONE) { + return -1; + } + } + return 0; +} + +static ssize_t local_readlink(FsContext *fs_ctx, const char *path, + char *buf, size_t bufsz) +{ + ssize_t tsize = -1; + if (fs_ctx->fs_sm == SM_MAPPED) { + int fd; + fd = open(rpath(fs_ctx, path), O_RDONLY); + if (fd == -1) { + return -1; + } + do { + tsize = read(fd, (void *)buf, bufsz); + } while (tsize == -1 && errno == EINTR); + close(fd); + return tsize; + } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) || + (fs_ctx->fs_sm == SM_NONE)) { + tsize = readlink(rpath(fs_ctx, path), buf, bufsz); + } + return tsize; +} + +static int local_close(FsContext *ctx, int fd) +{ + return close(fd); +} + +static int local_closedir(FsContext *ctx, DIR *dir) +{ + return closedir(dir); +} + +static int local_open(FsContext *ctx, const char *path, int flags) +{ + return open(rpath(ctx, path), flags); +} + +static DIR *local_opendir(FsContext *ctx, const char *path) +{ + return opendir(rpath(ctx, path)); +} + +static void local_rewinddir(FsContext *ctx, DIR *dir) +{ + return rewinddir(dir); +} + +static off_t local_telldir(FsContext *ctx, DIR *dir) +{ + return telldir(dir); +} + +static struct dirent *local_readdir(FsContext *ctx, DIR *dir) +{ + return readdir(dir); +} + +static void local_seekdir(FsContext *ctx, DIR *dir, off_t off) +{ + return seekdir(dir, off); +} + +static ssize_t local_preadv(FsContext *ctx, int fd, const struct iovec *iov, + int iovcnt, off_t offset) +{ +#ifdef CONFIG_PREADV + return preadv(fd, iov, iovcnt, offset); +#else + int err = lseek(fd, offset, SEEK_SET); + if (err == -1) { + return err; + } else { + return readv(fd, iov, iovcnt); + } +#endif +} + +static ssize_t local_pwritev(FsContext *ctx, int fd, const struct iovec *iov, + int iovcnt, off_t offset) +{ +#ifdef CONFIG_PREADV + return pwritev(fd, iov, iovcnt, offset); +#else + int err = lseek(fd, offset, SEEK_SET); + if (err == -1) { + return err; + } else { + return writev(fd, iov, iovcnt); + } +#endif +} + +static int local_chmod(FsContext *fs_ctx, const char *path, FsCred *credp) +{ + if (fs_ctx->fs_sm == SM_MAPPED) { + return local_set_xattr(rpath(fs_ctx, path), credp); + } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) || + (fs_ctx->fs_sm == SM_NONE)) { + return chmod(rpath(fs_ctx, path), credp->fc_mode); + } + return -1; +} + +static int local_mknod(FsContext *fs_ctx, const char *path, FsCred *credp) +{ + int err = -1; + int serrno = 0; + + /* Determine the security model */ + if (fs_ctx->fs_sm == SM_MAPPED) { + err = mknod(rpath(fs_ctx, path), SM_LOCAL_MODE_BITS|S_IFREG, 0); + if (err == -1) { + return err; + } + local_set_xattr(rpath(fs_ctx, path), credp); + if (err == -1) { + serrno = errno; + goto err_end; + } + } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) || + (fs_ctx->fs_sm == SM_NONE)) { + err = mknod(rpath(fs_ctx, path), credp->fc_mode, credp->fc_rdev); + if (err == -1) { + return err; + } + err = local_post_create_passthrough(fs_ctx, path, credp); + if (err == -1) { + serrno = errno; + goto err_end; + } + } + return err; + +err_end: + remove(rpath(fs_ctx, path)); + errno = serrno; + return err; +} + +static int local_mkdir(FsContext *fs_ctx, const char *path, FsCred *credp) +{ + int err = -1; + int serrno = 0; + + /* Determine the security model */ + if (fs_ctx->fs_sm == SM_MAPPED) { + err = mkdir(rpath(fs_ctx, path), SM_LOCAL_DIR_MODE_BITS); + if (err == -1) { + return err; + } + credp->fc_mode = credp->fc_mode|S_IFDIR; + err = local_set_xattr(rpath(fs_ctx, path), credp); + if (err == -1) { + serrno = errno; + goto err_end; + } + } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) || + (fs_ctx->fs_sm == SM_NONE)) { + err = mkdir(rpath(fs_ctx, path), credp->fc_mode); + if (err == -1) { + return err; + } + err = local_post_create_passthrough(fs_ctx, path, credp); + if (err == -1) { + serrno = errno; + goto err_end; + } + } + return err; + +err_end: + remove(rpath(fs_ctx, path)); + errno = serrno; + return err; +} + +static int local_fstat(FsContext *fs_ctx, int fd, struct stat *stbuf) +{ + int err; + err = fstat(fd, stbuf); + if (err) { + return err; + } + if (fs_ctx->fs_sm == SM_MAPPED) { + /* Actual credentials are part of extended attrs */ + uid_t tmp_uid; + gid_t tmp_gid; + mode_t tmp_mode; + dev_t tmp_dev; + + if (fgetxattr(fd, "user.virtfs.uid", &tmp_uid, sizeof(uid_t)) > 0) { + stbuf->st_uid = tmp_uid; + } + if (fgetxattr(fd, "user.virtfs.gid", &tmp_gid, sizeof(gid_t)) > 0) { + stbuf->st_gid = tmp_gid; + } + if (fgetxattr(fd, "user.virtfs.mode", &tmp_mode, sizeof(mode_t)) > 0) { + stbuf->st_mode = tmp_mode; + } + if (fgetxattr(fd, "user.virtfs.rdev", &tmp_dev, sizeof(dev_t)) > 0) { + stbuf->st_rdev = tmp_dev; + } + } + return err; +} + +static int local_open2(FsContext *fs_ctx, const char *path, int flags, + FsCred *credp) +{ + int fd = -1; + int err = -1; + int serrno = 0; + + /* Determine the security model */ + if (fs_ctx->fs_sm == SM_MAPPED) { + fd = open(rpath(fs_ctx, path), flags, SM_LOCAL_MODE_BITS); + if (fd == -1) { + return fd; + } + credp->fc_mode = credp->fc_mode|S_IFREG; + /* Set cleint credentials in xattr */ + err = local_set_xattr(rpath(fs_ctx, path), credp); + if (err == -1) { + serrno = errno; + goto err_end; + } + } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) || + (fs_ctx->fs_sm == SM_NONE)) { + fd = open(rpath(fs_ctx, path), flags, credp->fc_mode); + if (fd == -1) { + return fd; + } + err = local_post_create_passthrough(fs_ctx, path, credp); + if (err == -1) { + serrno = errno; + goto err_end; + } + } + return fd; + +err_end: + close(fd); + remove(rpath(fs_ctx, path)); + errno = serrno; + return err; +} + + +static int local_symlink(FsContext *fs_ctx, const char *oldpath, + const char *newpath, FsCred *credp) +{ + int err = -1; + int serrno = 0; + + /* Determine the security model */ + if (fs_ctx->fs_sm == SM_MAPPED) { + int fd; + ssize_t oldpath_size, write_size; + fd = open(rpath(fs_ctx, newpath), O_CREAT|O_EXCL|O_RDWR, + SM_LOCAL_MODE_BITS); + if (fd == -1) { + return fd; + } + /* Write the oldpath (target) to the file. */ + oldpath_size = strlen(oldpath) + 1; + do { + write_size = write(fd, (void *)oldpath, oldpath_size); + } while (write_size == -1 && errno == EINTR); + + if (write_size != oldpath_size) { + serrno = errno; + close(fd); + err = -1; + goto err_end; + } + close(fd); + /* Set cleint credentials in symlink's xattr */ + credp->fc_mode = credp->fc_mode|S_IFLNK; + err = local_set_xattr(rpath(fs_ctx, newpath), credp); + if (err == -1) { + serrno = errno; + goto err_end; + } + } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) || + (fs_ctx->fs_sm == SM_NONE)) { + err = symlink(oldpath, rpath(fs_ctx, newpath)); + if (err) { + return err; + } + err = lchown(rpath(fs_ctx, newpath), credp->fc_uid, credp->fc_gid); + if (err == -1) { + /* + * If we fail to change ownership and if we are + * using security model none. Ignore the error + */ + if (fs_ctx->fs_sm != SM_NONE) { + serrno = errno; + goto err_end; + } else + err = 0; + } + } + return err; + +err_end: + remove(rpath(fs_ctx, newpath)); + errno = serrno; + return err; +} + +static int local_link(FsContext *ctx, const char *oldpath, const char *newpath) +{ + char *tmp = qemu_strdup(rpath(ctx, oldpath)); + int err, serrno = 0; + + if (tmp == NULL) { + return -ENOMEM; + } + + err = link(tmp, rpath(ctx, newpath)); + if (err == -1) { + serrno = errno; + } + + qemu_free(tmp); + + if (err == -1) { + errno = serrno; + } + + return err; +} + +static int local_truncate(FsContext *ctx, const char *path, off_t size) +{ + return truncate(rpath(ctx, path), size); +} + +static int local_rename(FsContext *ctx, const char *oldpath, + const char *newpath) +{ + char *tmp; + int err; + + tmp = qemu_strdup(rpath(ctx, oldpath)); + + err = rename(tmp, rpath(ctx, newpath)); + if (err == -1) { + int serrno = errno; + qemu_free(tmp); + errno = serrno; + } else { + qemu_free(tmp); + } + + return err; + +} + +static int local_chown(FsContext *fs_ctx, const char *path, FsCred *credp) +{ + if ((credp->fc_uid == -1 && credp->fc_gid == -1) || + (fs_ctx->fs_sm == SM_PASSTHROUGH)) { + return lchown(rpath(fs_ctx, path), credp->fc_uid, credp->fc_gid); + } else if (fs_ctx->fs_sm == SM_MAPPED) { + return local_set_xattr(rpath(fs_ctx, path), credp); + } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) || + (fs_ctx->fs_sm == SM_NONE)) { + return lchown(rpath(fs_ctx, path), credp->fc_uid, credp->fc_gid); + } + return -1; +} + +static int local_utimensat(FsContext *s, const char *path, + const struct timespec *buf) +{ + return qemu_utimensat(AT_FDCWD, rpath(s, path), buf, AT_SYMLINK_NOFOLLOW); +} + +static int local_remove(FsContext *ctx, const char *path) +{ + return remove(rpath(ctx, path)); +} + +static int local_fsync(FsContext *ctx, int fd, int datasync) +{ + if (datasync) { + return qemu_fdatasync(fd); + } else { + return fsync(fd); + } +} + +static int local_statfs(FsContext *s, const char *path, struct statfs *stbuf) +{ + return statfs(rpath(s, path), stbuf); +} + +static ssize_t local_lgetxattr(FsContext *ctx, const char *path, + const char *name, void *value, size_t size) +{ + return v9fs_get_xattr(ctx, path, name, value, size); +} + +static ssize_t local_llistxattr(FsContext *ctx, const char *path, + void *value, size_t size) +{ + return v9fs_list_xattr(ctx, path, value, size); +} + +static int local_lsetxattr(FsContext *ctx, const char *path, const char *name, + void *value, size_t size, int flags) +{ + return v9fs_set_xattr(ctx, path, name, value, size, flags); +} + +static int local_lremovexattr(FsContext *ctx, + const char *path, const char *name) +{ + return v9fs_remove_xattr(ctx, path, name); +} + + +FileOperations local_ops = { + .lstat = local_lstat, + .readlink = local_readlink, + .close = local_close, + .closedir = local_closedir, + .open = local_open, + .opendir = local_opendir, + .rewinddir = local_rewinddir, + .telldir = local_telldir, + .readdir = local_readdir, + .seekdir = local_seekdir, + .preadv = local_preadv, + .pwritev = local_pwritev, + .chmod = local_chmod, + .mknod = local_mknod, + .mkdir = local_mkdir, + .fstat = local_fstat, + .open2 = local_open2, + .symlink = local_symlink, + .link = local_link, + .truncate = local_truncate, + .rename = local_rename, + .chown = local_chown, + .utimensat = local_utimensat, + .remove = local_remove, + .fsync = local_fsync, + .statfs = local_statfs, + .lgetxattr = local_lgetxattr, + .llistxattr = local_llistxattr, + .lsetxattr = local_lsetxattr, + .lremovexattr = local_lremovexattr, +}; diff -Nru qemu-kvm-0.12.5+noroms/hw/virtio-9p-posix-acl.c qemu-kvm-0.14.1/hw/virtio-9p-posix-acl.c --- qemu-kvm-0.12.5+noroms/hw/virtio-9p-posix-acl.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/virtio-9p-posix-acl.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,140 @@ +/* + * Virtio 9p system.posix* xattr callback + * + * Copyright IBM, Corp. 2010 + * + * Authors: + * Aneesh Kumar K.V + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ + +#include +#include +#include "virtio.h" +#include "virtio-9p.h" +#include "file-op-9p.h" +#include "virtio-9p-xattr.h" + +#define MAP_ACL_ACCESS "user.virtfs.system.posix_acl_access" +#define MAP_ACL_DEFAULT "user.virtfs.system.posix_acl_default" +#define ACL_ACCESS "system.posix_acl_access" +#define ACL_DEFAULT "system.posix_acl_default" + +static ssize_t mp_pacl_getxattr(FsContext *ctx, const char *path, + const char *name, void *value, size_t size) +{ + return lgetxattr(rpath(ctx, path), MAP_ACL_ACCESS, value, size); +} + +static ssize_t mp_pacl_listxattr(FsContext *ctx, const char *path, + char *name, void *value, size_t osize) +{ + ssize_t len = sizeof(ACL_ACCESS); + + if (!value) { + return len; + } + + if (osize < len) { + errno = ERANGE; + return -1; + } + + strncpy(value, ACL_ACCESS, len); + return 0; +} + +static int mp_pacl_setxattr(FsContext *ctx, const char *path, const char *name, + void *value, size_t size, int flags) +{ + return lsetxattr(rpath(ctx, path), MAP_ACL_ACCESS, value, size, flags); +} + +static int mp_pacl_removexattr(FsContext *ctx, + const char *path, const char *name) +{ + int ret; + ret = lremovexattr(rpath(ctx, path), MAP_ACL_ACCESS); + if (ret == -1 && errno == ENODATA) { + /* + * We don't get ENODATA error when trying to remote a + * posix acl that is not present. So don't throw the error + * even in case of mapped security model + */ + errno = 0; + ret = 0; + } + return ret; +} + +static ssize_t mp_dacl_getxattr(FsContext *ctx, const char *path, + const char *name, void *value, size_t size) +{ + return lgetxattr(rpath(ctx, path), MAP_ACL_DEFAULT, value, size); +} + +static ssize_t mp_dacl_listxattr(FsContext *ctx, const char *path, + char *name, void *value, size_t osize) +{ + ssize_t len = sizeof(ACL_DEFAULT); + + if (!value) { + return len; + } + + if (osize < len) { + errno = ERANGE; + return -1; + } + + strncpy(value, ACL_DEFAULT, len); + return 0; +} + +static int mp_dacl_setxattr(FsContext *ctx, const char *path, const char *name, + void *value, size_t size, int flags) +{ + return lsetxattr(rpath(ctx, path), MAP_ACL_DEFAULT, value, size, flags); +} + +static int mp_dacl_removexattr(FsContext *ctx, + const char *path, const char *name) +{ + return lremovexattr(rpath(ctx, path), MAP_ACL_DEFAULT); +} + + +XattrOperations mapped_pacl_xattr = { + .name = "system.posix_acl_access", + .getxattr = mp_pacl_getxattr, + .setxattr = mp_pacl_setxattr, + .listxattr = mp_pacl_listxattr, + .removexattr = mp_pacl_removexattr, +}; + +XattrOperations mapped_dacl_xattr = { + .name = "system.posix_acl_default", + .getxattr = mp_dacl_getxattr, + .setxattr = mp_dacl_setxattr, + .listxattr = mp_dacl_listxattr, + .removexattr = mp_dacl_removexattr, +}; + +XattrOperations passthrough_acl_xattr = { + .name = "system.posix_acl_", + .getxattr = pt_getxattr, + .setxattr = pt_setxattr, + .listxattr = pt_listxattr, + .removexattr = pt_removexattr, +}; + +XattrOperations none_acl_xattr = { + .name = "system.posix_acl_", + .getxattr = notsup_getxattr, + .setxattr = notsup_setxattr, + .listxattr = notsup_listxattr, + .removexattr = notsup_removexattr, +}; diff -Nru qemu-kvm-0.12.5+noroms/hw/virtio-9p-xattr.c qemu-kvm-0.14.1/hw/virtio-9p-xattr.c --- qemu-kvm-0.12.5+noroms/hw/virtio-9p-xattr.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/virtio-9p-xattr.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,159 @@ +/* + * Virtio 9p xattr callback + * + * Copyright IBM, Corp. 2010 + * + * Authors: + * Aneesh Kumar K.V + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ + +#include "virtio.h" +#include "virtio-9p.h" +#include "file-op-9p.h" +#include "virtio-9p-xattr.h" + + +static XattrOperations *get_xattr_operations(XattrOperations **h, + const char *name) +{ + XattrOperations *xops; + for (xops = *(h)++; xops != NULL; xops = *(h)++) { + if (!strncmp(name, xops->name, strlen(xops->name))) { + return xops; + } + } + return NULL; +} + +ssize_t v9fs_get_xattr(FsContext *ctx, const char *path, + const char *name, void *value, size_t size) +{ + XattrOperations *xops = get_xattr_operations(ctx->xops, name); + if (xops) { + return xops->getxattr(ctx, path, name, value, size); + } + errno = -EOPNOTSUPP; + return -1; +} + +ssize_t pt_listxattr(FsContext *ctx, const char *path, + char *name, void *value, size_t size) +{ + int name_size = strlen(name) + 1; + if (!value) { + return name_size; + } + + if (size < name_size) { + errno = ERANGE; + return -1; + } + + strncpy(value, name, name_size); + return name_size; +} + + +/* + * Get the list and pass to each layer to find out whether + * to send the data or not + */ +ssize_t v9fs_list_xattr(FsContext *ctx, const char *path, + void *value, size_t vsize) +{ + ssize_t size = 0; + void *ovalue = value; + XattrOperations *xops; + char *orig_value, *orig_value_start; + ssize_t xattr_len, parsed_len = 0, attr_len; + + /* Get the actual len */ + xattr_len = llistxattr(rpath(ctx, path), value, 0); + if (xattr_len <= 0) { + return xattr_len; + } + + /* Now fetch the xattr and find the actual size */ + orig_value = qemu_malloc(xattr_len); + xattr_len = llistxattr(rpath(ctx, path), orig_value, xattr_len); + + /* store the orig pointer */ + orig_value_start = orig_value; + while (xattr_len > parsed_len) { + xops = get_xattr_operations(ctx->xops, orig_value); + if (!xops) { + goto next_entry; + } + + if (!value) { + size += xops->listxattr(ctx, path, orig_value, value, vsize); + } else { + size = xops->listxattr(ctx, path, orig_value, value, vsize); + if (size < 0) { + goto err_out; + } + value += size; + vsize -= size; + } +next_entry: + /* Got the next entry */ + attr_len = strlen(orig_value) + 1; + parsed_len += attr_len; + orig_value += attr_len; + } + if (value) { + size = value - ovalue; + } + +err_out: + qemu_free(orig_value_start); + return size; +} + +int v9fs_set_xattr(FsContext *ctx, const char *path, const char *name, + void *value, size_t size, int flags) +{ + XattrOperations *xops = get_xattr_operations(ctx->xops, name); + if (xops) { + return xops->setxattr(ctx, path, name, value, size, flags); + } + errno = -EOPNOTSUPP; + return -1; + +} + +int v9fs_remove_xattr(FsContext *ctx, + const char *path, const char *name) +{ + XattrOperations *xops = get_xattr_operations(ctx->xops, name); + if (xops) { + return xops->removexattr(ctx, path, name); + } + errno = -EOPNOTSUPP; + return -1; + +} + +XattrOperations *mapped_xattr_ops[] = { + &mapped_user_xattr, + &mapped_pacl_xattr, + &mapped_dacl_xattr, + NULL, +}; + +XattrOperations *passthrough_xattr_ops[] = { + &passthrough_user_xattr, + &passthrough_acl_xattr, + NULL, +}; + +/* for .user none model should be same as passthrough */ +XattrOperations *none_xattr_ops[] = { + &passthrough_user_xattr, + &none_acl_xattr, + NULL, +}; diff -Nru qemu-kvm-0.12.5+noroms/hw/virtio-9p-xattr.h qemu-kvm-0.14.1/hw/virtio-9p-xattr.h --- qemu-kvm-0.12.5+noroms/hw/virtio-9p-xattr.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/virtio-9p-xattr.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,102 @@ +/* + * Virtio 9p + * + * Copyright IBM, Corp. 2010 + * + * Authors: + * Aneesh Kumar K.V + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ +#ifndef _QEMU_VIRTIO_9P_XATTR_H +#define _QEMU_VIRTIO_9P_XATTR_H + +#include + +typedef struct xattr_operations +{ + const char *name; + ssize_t (*getxattr)(FsContext *ctx, const char *path, + const char *name, void *value, size_t size); + ssize_t (*listxattr)(FsContext *ctx, const char *path, + char *name, void *value, size_t size); + int (*setxattr)(FsContext *ctx, const char *path, const char *name, + void *value, size_t size, int flags); + int (*removexattr)(FsContext *ctx, + const char *path, const char *name); +} XattrOperations; + + +extern XattrOperations mapped_user_xattr; +extern XattrOperations passthrough_user_xattr; + +extern XattrOperations mapped_pacl_xattr; +extern XattrOperations mapped_dacl_xattr; +extern XattrOperations passthrough_acl_xattr; +extern XattrOperations none_acl_xattr; + +extern XattrOperations *mapped_xattr_ops[]; +extern XattrOperations *passthrough_xattr_ops[]; +extern XattrOperations *none_xattr_ops[]; + +ssize_t v9fs_get_xattr(FsContext *ctx, const char *path, const char *name, + void *value, size_t size); +ssize_t v9fs_list_xattr(FsContext *ctx, const char *path, void *value, + size_t vsize); +int v9fs_set_xattr(FsContext *ctx, const char *path, const char *name, + void *value, size_t size, int flags); +int v9fs_remove_xattr(FsContext *ctx, const char *path, const char *name); +ssize_t pt_listxattr(FsContext *ctx, const char *path, char *name, void *value, + size_t size); + +static inline ssize_t pt_getxattr(FsContext *ctx, const char *path, + const char *name, void *value, size_t size) +{ + return lgetxattr(rpath(ctx, path), name, value, size); +} + +static inline int pt_setxattr(FsContext *ctx, const char *path, + const char *name, void *value, + size_t size, int flags) +{ + return lsetxattr(rpath(ctx, path), name, value, size, flags); +} + +static inline int pt_removexattr(FsContext *ctx, + const char *path, const char *name) +{ + return lremovexattr(rpath(ctx, path), name); +} + +static inline ssize_t notsup_getxattr(FsContext *ctx, const char *path, + const char *name, void *value, + size_t size) +{ + errno = ENOTSUP; + return -1; +} + +static inline int notsup_setxattr(FsContext *ctx, const char *path, + const char *name, void *value, + size_t size, int flags) +{ + errno = ENOTSUP; + return -1; +} + +static inline ssize_t notsup_listxattr(FsContext *ctx, const char *path, + char *name, void *value, size_t size) +{ + return 0; +} + +static inline int notsup_removexattr(FsContext *ctx, + const char *path, const char *name) +{ + errno = ENOTSUP; + return -1; +} + +#endif diff -Nru qemu-kvm-0.12.5+noroms/hw/virtio-9p-xattr-user.c qemu-kvm-0.14.1/hw/virtio-9p-xattr-user.c --- qemu-kvm-0.12.5+noroms/hw/virtio-9p-xattr-user.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/virtio-9p-xattr-user.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,109 @@ +/* + * Virtio 9p user. xattr callback + * + * Copyright IBM, Corp. 2010 + * + * Authors: + * Aneesh Kumar K.V + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ + +#include +#include "virtio.h" +#include "virtio-9p.h" +#include "file-op-9p.h" +#include "virtio-9p-xattr.h" + + +static ssize_t mp_user_getxattr(FsContext *ctx, const char *path, + const char *name, void *value, size_t size) +{ + if (strncmp(name, "user.virtfs.", 12) == 0) { + /* + * Don't allow fetch of user.virtfs namesapce + * in case of mapped security + */ + errno = ENOATTR; + return -1; + } + return lgetxattr(rpath(ctx, path), name, value, size); +} + +static ssize_t mp_user_listxattr(FsContext *ctx, const char *path, + char *name, void *value, size_t size) +{ + int name_size = strlen(name) + 1; + if (strncmp(name, "user.virtfs.", 12) == 0) { + + /* check if it is a mapped posix acl */ + if (strncmp(name, "user.virtfs.system.posix_acl_", 29) == 0) { + /* adjust the name and size */ + name += 12; + name_size -= 12; + } else { + /* + * Don't allow fetch of user.virtfs namesapce + * in case of mapped security + */ + return 0; + } + } + if (!value) { + return name_size; + } + + if (size < name_size) { + errno = ERANGE; + return -1; + } + + strncpy(value, name, name_size); + return name_size; +} + +static int mp_user_setxattr(FsContext *ctx, const char *path, const char *name, + void *value, size_t size, int flags) +{ + if (strncmp(name, "user.virtfs.", 12) == 0) { + /* + * Don't allow fetch of user.virtfs namesapce + * in case of mapped security + */ + errno = EACCES; + return -1; + } + return lsetxattr(rpath(ctx, path), name, value, size, flags); +} + +static int mp_user_removexattr(FsContext *ctx, + const char *path, const char *name) +{ + if (strncmp(name, "user.virtfs.", 12) == 0) { + /* + * Don't allow fetch of user.virtfs namesapce + * in case of mapped security + */ + errno = EACCES; + return -1; + } + return lremovexattr(rpath(ctx, path), name); +} + +XattrOperations mapped_user_xattr = { + .name = "user.", + .getxattr = mp_user_getxattr, + .setxattr = mp_user_setxattr, + .listxattr = mp_user_listxattr, + .removexattr = mp_user_removexattr, +}; + +XattrOperations passthrough_user_xattr = { + .name = "user.", + .getxattr = pt_getxattr, + .setxattr = pt_setxattr, + .listxattr = pt_listxattr, + .removexattr = pt_removexattr, +}; diff -Nru qemu-kvm-0.12.5+noroms/hw/virtio-balloon.c qemu-kvm-0.14.1/hw/virtio-balloon.c --- qemu-kvm-0.12.5+noroms/hw/virtio-balloon.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/virtio-balloon.c 2011-05-11 13:29:46.000000000 +0000 @@ -11,26 +11,40 @@ * */ +#include "iov.h" #include "qemu-common.h" #include "virtio.h" #include "pc.h" #include "sysemu.h" #include "cpu.h" +#include "monitor.h" #include "balloon.h" #include "virtio-balloon.h" #include "kvm.h" #include "qemu-kvm.h" +#include "qlist.h" +#include "qint.h" +#include "qstring.h" #if defined(__linux__) #include #endif +/* Disable guest-provided stats by now (https://bugzilla.redhat.com/show_bug.cgi?id=623903) */ +#define ENABLE_GUEST_STATS 0 + + typedef struct VirtIOBalloon { VirtIODevice vdev; - VirtQueue *ivq, *dvq; + VirtQueue *ivq, *dvq, *svq; uint32_t num_pages; uint32_t actual; + uint64_t stats[VIRTIO_BALLOON_S_NR]; + VirtQueueElement stats_vq_elem; + size_t stats_vq_offset; + MonitorCompletion *stats_callback; + void *stats_opaque_callback_data; } VirtIOBalloon; static VirtIOBalloon *to_virtio_balloon(VirtIODevice *vdev) @@ -42,36 +56,48 @@ { #if defined(__linux__) if (!kvm_enabled() || kvm_has_sync_mmu()) - madvise(addr, TARGET_PAGE_SIZE, - deflate ? MADV_WILLNEED : MADV_DONTNEED); + qemu_madvise(addr, TARGET_PAGE_SIZE, + deflate ? QEMU_MADV_WILLNEED : QEMU_MADV_DONTNEED); #endif } -/* FIXME: once we do a virtio refactoring, this will get subsumed into common - * code */ -static size_t memcpy_from_iovector(void *data, size_t offset, size_t size, - struct iovec *iov, int iovlen) +/* + * reset_stats - Mark all items in the stats array as unset + * + * This function needs to be called at device intialization and before + * before updating to a set of newly-generated stats. This will ensure that no + * stale values stick around in case the guest reports a subset of the supported + * statistics. + */ +static inline void reset_stats(VirtIOBalloon *dev) { int i; - uint8_t *ptr = data; - size_t iov_off = 0; - size_t data_off = 0; - - for (i = 0; i < iovlen && size; i++) { - if (offset < (iov_off + iov[i].iov_len)) { - size_t len = MIN((iov_off + iov[i].iov_len) - offset , size); - - memcpy(ptr + data_off, iov[i].iov_base + (offset - iov_off), len); - - data_off += len; - offset += len; - size -= len; - } + for (i = 0; i < VIRTIO_BALLOON_S_NR; dev->stats[i++] = -1); +} - iov_off += iov[i].iov_len; - } +static void stat_put(QDict *dict, const char *label, uint64_t val) +{ + if (val != -1) + qdict_put(dict, label, qint_from_int(val)); +} + +static QObject *get_stats_qobject(VirtIOBalloon *dev) +{ + QDict *dict = qdict_new(); + uint64_t actual = ram_size - ((uint64_t) dev->actual << + VIRTIO_BALLOON_PFN_SHIFT); + + stat_put(dict, "actual", actual); +#if ENABLE_GUEST_STATS + stat_put(dict, "mem_swapped_in", dev->stats[VIRTIO_BALLOON_S_SWAP_IN]); + stat_put(dict, "mem_swapped_out", dev->stats[VIRTIO_BALLOON_S_SWAP_OUT]); + stat_put(dict, "major_page_faults", dev->stats[VIRTIO_BALLOON_S_MAJFLT]); + stat_put(dict, "minor_page_faults", dev->stats[VIRTIO_BALLOON_S_MINFLT]); + stat_put(dict, "free_mem", dev->stats[VIRTIO_BALLOON_S_MEMFREE]); + stat_put(dict, "total_mem", dev->stats[VIRTIO_BALLOON_S_MEMTOT]); +#endif - return data_off; + return QOBJECT(dict); } static void virtio_balloon_handle_output(VirtIODevice *vdev, VirtQueue *vq) @@ -83,8 +109,7 @@ size_t offset = 0; uint32_t pfn; - while (memcpy_from_iovector(&pfn, offset, 4, - elem.out_sg, elem.out_num) == 4) { + while (iov_to_buf(elem.out_sg, elem.out_num, &pfn, offset, 4) == 4) { ram_addr_t pa; ram_addr_t addr; @@ -105,6 +130,51 @@ } } +static void complete_stats_request(VirtIOBalloon *vb) +{ + QObject *stats; + + if (!vb->stats_opaque_callback_data) + return; + + stats = get_stats_qobject(vb); + vb->stats_callback(vb->stats_opaque_callback_data, stats); + qobject_decref(stats); + vb->stats_opaque_callback_data = NULL; + vb->stats_callback = NULL; +} + +static void virtio_balloon_receive_stats(VirtIODevice *vdev, VirtQueue *vq) +{ + VirtIOBalloon *s = DO_UPCAST(VirtIOBalloon, vdev, vdev); + VirtQueueElement *elem = &s->stats_vq_elem; + VirtIOBalloonStat stat; + size_t offset = 0; + + if (!virtqueue_pop(vq, elem)) { + return; + } + + /* Initialize the stats to get rid of any stale values. This is only + * needed to handle the case where a guest supports fewer stats than it + * used to (ie. it has booted into an old kernel). + */ + reset_stats(s); + + while (iov_to_buf(elem->out_sg, elem->out_num, &stat, offset, sizeof(stat)) + == sizeof(stat)) { + uint16_t tag = tswap16(stat.tag); + uint64_t val = tswap64(stat.val); + + offset += sizeof(stat); + if (tag < VIRTIO_BALLOON_S_NR) + s->stats[tag] = val; + } + s->stats_vq_offset = offset; + + complete_stats_request(s); +} + static void virtio_balloon_get_config(VirtIODevice *vdev, uint8_t *config_data) { VirtIOBalloon *dev = to_virtio_balloon(vdev); @@ -125,12 +195,14 @@ dev->actual = config.actual; } -static uint32_t virtio_balloon_get_features(VirtIODevice *vdev) +static uint32_t virtio_balloon_get_features(VirtIODevice *vdev, uint32_t f) { - return 0; + f |= (1 << VIRTIO_BALLOON_F_STATS_VQ); + return f; } -static ram_addr_t virtio_balloon_to_target(void *opaque, ram_addr_t target) +static void virtio_balloon_to_target(void *opaque, ram_addr_t target, + MonitorCompletion cb, void *cb_data) { VirtIOBalloon *dev = opaque; @@ -140,9 +212,26 @@ if (target) { dev->num_pages = (ram_size - target) >> VIRTIO_BALLOON_PFN_SHIFT; virtio_notify_config(&dev->vdev); + } else { + /* For now, only allow one request at a time. This restriction can be + * removed later by queueing callback and data pairs. + */ + if (dev->stats_callback != NULL) { + return; + } + dev->stats_callback = cb; + dev->stats_opaque_callback_data = cb_data; + if (ENABLE_GUEST_STATS && (dev->vdev.guest_features & (1 << VIRTIO_BALLOON_F_STATS_VQ))) { + virtqueue_push(dev->svq, &dev->stats_vq_elem, dev->stats_vq_offset); + virtio_notify(&dev->vdev, dev->svq); + } else { + /* Stats are not supported. Clear out any stale values that might + * have been set by a more featureful guest kernel. + */ + reset_stats(dev); + complete_stats_request(dev); + } } - - return ram_size - (dev->actual << VIRTIO_BALLOON_PFN_SHIFT); } static void virtio_balloon_save(QEMUFile *f, void *opaque) @@ -166,7 +255,6 @@ s->num_pages = qemu_get_be32(f); s->actual = qemu_get_be32(f); - return 0; } @@ -184,10 +272,13 @@ s->ivq = virtio_add_queue(&s->vdev, 128, virtio_balloon_handle_output); s->dvq = virtio_add_queue(&s->vdev, 128, virtio_balloon_handle_output); + s->svq = virtio_add_queue(&s->vdev, 128, virtio_balloon_receive_stats); + reset_stats(s); qemu_add_balloon_handler(virtio_balloon_to_target, s); - register_savevm("virtio-balloon", -1, 1, virtio_balloon_save, virtio_balloon_load, s); + register_savevm(dev, "virtio-balloon", -1, 1, + virtio_balloon_save, virtio_balloon_load, s); return &s->vdev; } diff -Nru qemu-kvm-0.12.5+noroms/hw/virtio-balloon.h qemu-kvm-0.14.1/hw/virtio-balloon.h --- qemu-kvm-0.12.5+noroms/hw/virtio-balloon.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/virtio-balloon.h 2011-05-11 13:29:46.000000000 +0000 @@ -25,6 +25,7 @@ /* The feature bitmap for virtio balloon */ #define VIRTIO_BALLOON_F_MUST_TELL_HOST 0 /* Tell before reclaiming pages */ +#define VIRTIO_BALLOON_F_STATS_VQ 1 /* Memory stats virtqueue */ /* Size of a PFN in the balloon interface. */ #define VIRTIO_BALLOON_PFN_SHIFT 12 @@ -37,4 +38,18 @@ uint32_t actual; }; +/* Memory Statistics */ +#define VIRTIO_BALLOON_S_SWAP_IN 0 /* Amount of memory swapped in */ +#define VIRTIO_BALLOON_S_SWAP_OUT 1 /* Amount of memory swapped out */ +#define VIRTIO_BALLOON_S_MAJFLT 2 /* Number of major faults */ +#define VIRTIO_BALLOON_S_MINFLT 3 /* Number of minor faults */ +#define VIRTIO_BALLOON_S_MEMFREE 4 /* Total amount of free memory */ +#define VIRTIO_BALLOON_S_MEMTOT 5 /* Total amount of memory */ +#define VIRTIO_BALLOON_S_NR 6 + +typedef struct VirtIOBalloonStat { + uint16_t tag; + uint64_t val; +} __attribute__((packed)) VirtIOBalloonStat; + #endif diff -Nru qemu-kvm-0.12.5+noroms/hw/virtio-blk.c qemu-kvm-0.14.1/hw/virtio-blk.c --- qemu-kvm-0.12.5+noroms/hw/virtio-blk.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/virtio-blk.c 2011-05-11 13:29:46.000000000 +0000 @@ -12,9 +12,10 @@ */ #include -#include +#include "qemu-error.h" +#include "trace.h" +#include "blockdev.h" #include "virtio-blk.h" -#include "block_int.h" #ifdef __linux__ # include #endif @@ -25,9 +26,11 @@ BlockDriverState *bs; VirtQueue *vq; void *rq; - char serial_str[BLOCK_SERIAL_STRLEN + 1]; QEMUBH *bh; - size_t config_size; + BlockConf *conf; + unsigned short sector_mask; + char sn[BLOCK_SERIAL_STRLEN]; + DeviceState *qdev; } VirtIOBlock; static VirtIOBlock *to_virtio_blk(VirtIODevice *vdev) @@ -35,47 +38,6 @@ return (VirtIOBlock *)vdev; } -/* store identify data in little endian format - */ -static inline void put_le16(uint16_t *p, unsigned int v) -{ - *p = cpu_to_le16(v); -} - -/* copy to *dst from *src, nul pad dst tail as needed to len bytes - */ -static inline void padstr(char *dst, const char *src, int len) -{ - while (len--) - *dst++ = *src ? *src++ : '\0'; -} - -/* setup simulated identify data as appropriate for virtio block device - * - * ref: AT Attachment 8 - ATA/ATAPI Command Set (ATA8-ACS) - */ -static inline void virtio_identify_template(struct virtio_blk_config *bc) -{ - uint16_t *p = &bc->identify[0]; - uint64_t lba_sectors = bc->capacity; - - memset(p, 0, sizeof(bc->identify)); - put_le16(p + 0, 0x0); /* ATA device */ - padstr((char *)(p + 23), QEMU_VERSION, 8); /* firmware revision */ - padstr((char *)(p + 27), "QEMU VIRT_BLK", 40); /* model# */ - put_le16(p + 47, 0x80ff); /* max xfer 255 sectors */ - put_le16(p + 49, 0x0b00); /* support IORDY/LBA/DMA */ - put_le16(p + 59, 0x1ff); /* cur xfer 255 sectors */ - put_le16(p + 80, 0x1f0); /* support ATA8/7/6/5/4 */ - put_le16(p + 81, 0x16); - put_le16(p + 82, 0x400); - put_le16(p + 83, 0x400); - put_le16(p + 100, lba_sectors); - put_le16(p + 101, lba_sectors >> 16); - put_le16(p + 102, lba_sectors >> 32); - put_le16(p + 103, lba_sectors >> 48); -} - typedef struct VirtIOBlockReq { VirtIOBlock *dev; @@ -91,7 +53,9 @@ { VirtIOBlock *s = req->dev; - req->in->status = status; + trace_virtio_blk_req_complete(req, status); + + stb_p(&req->in->status, status); virtqueue_push(s->vq, &req->elem, req->qiov.size + sizeof(*req->in)); virtio_notify(&s->vdev, s->vq); @@ -101,20 +65,23 @@ static int virtio_blk_handle_rw_error(VirtIOBlockReq *req, int error, int is_read) { - BlockInterfaceErrorAction action = - drive_get_on_error(req->dev->bs, is_read); + BlockErrorAction action = bdrv_get_on_error(req->dev->bs, is_read); VirtIOBlock *s = req->dev; - if (action == BLOCK_ERR_IGNORE) + if (action == BLOCK_ERR_IGNORE) { + bdrv_mon_event(s->bs, BDRV_ACTION_IGNORE, is_read); return 0; + } if ((error == ENOSPC && action == BLOCK_ERR_STOP_ENOSPC) || action == BLOCK_ERR_STOP_ANY) { req->next = s->rq; s->rq = req; + bdrv_mon_event(s->bs, BDRV_ACTION_STOP, is_read); vm_stop(0); } else { virtio_blk_req_complete(req, VIRTIO_BLK_S_IOERR); + bdrv_mon_event(s->bs, BDRV_ACTION_REPORT, is_read); } return 1; @@ -124,8 +91,10 @@ { VirtIOBlockReq *req = opaque; + trace_virtio_blk_rw_complete(req, ret); + if (ret) { - int is_read = !(req->out->type & VIRTIO_BLK_T_OUT); + int is_read = !(ldl_p(&req->out->type) & VIRTIO_BLK_T_OUT); if (virtio_blk_handle_rw_error(req, -ret, is_read)) return; } @@ -137,13 +106,21 @@ { VirtIOBlockReq *req = opaque; - virtio_blk_req_complete(req, ret ? VIRTIO_BLK_S_IOERR : VIRTIO_BLK_S_OK); + if (ret) { + if (virtio_blk_handle_rw_error(req, -ret, 0)) { + return; + } + } + + virtio_blk_req_complete(req, VIRTIO_BLK_S_OK); } static VirtIOBlockReq *virtio_blk_alloc_request(VirtIOBlock *s) { - VirtIOBlockReq *req = qemu_mallocz(sizeof(*req)); + VirtIOBlockReq *req = qemu_malloc(sizeof(*req)); req->dev = s; + req->qiov.size = 0; + req->next = NULL; return req; } @@ -165,7 +142,7 @@ static void virtio_blk_handle_scsi(VirtIOBlockReq *req) { struct sg_io_hdr hdr; - int ret, size = 0; + int ret; int status; int i; @@ -194,7 +171,6 @@ * before the regular inhdr. */ req->scsi = (void *)req->elem.in_sg[req->elem.in_num - 2].iov_base; - size = sizeof(*req->in) + sizeof(*req->scsi); memset(&hdr, 0, sizeof(struct sg_io_hdr)); hdr.interface_id = 'S'; @@ -226,7 +202,6 @@ hdr.dxfer_len += req->elem.in_sg[i].iov_len; hdr.dxferp = req->elem.in_sg; - size += hdr.dxfer_len; } else { /* * Some SCSI commands don't actually transfer any data. @@ -236,7 +211,6 @@ hdr.sbp = req->elem.in_sg[req->elem.in_num - 3].iov_base; hdr.mx_sb_len = req->elem.in_sg[req->elem.in_num - 3].iov_len; - size += hdr.mx_sb_len; ret = bdrv_ioctl(req->dev->bs, SG_IO, &hdr); if (ret) { @@ -249,10 +223,10 @@ status = VIRTIO_BLK_S_OK; } - req->scsi->errors = hdr.status; - req->scsi->residual = hdr.resid; - req->scsi->sense_len = hdr.sb_len_wr; - req->scsi->data_len = hdr.dxfer_len; + stl_p(&req->scsi->errors, hdr.status); + stl_p(&req->scsi->residual, hdr.resid); + stl_p(&req->scsi->sense_len, hdr.sb_len_wr); + stl_p(&req->scsi->data_len, hdr.dxfer_len); virtio_blk_req_complete(req, status); } @@ -263,106 +237,138 @@ } #endif /* __linux__ */ -static void do_multiwrite(BlockDriverState *bs, BlockRequest *blkreq, - int num_writes) +typedef struct MultiReqBuffer { + BlockRequest blkreq[32]; + unsigned int num_writes; +} MultiReqBuffer; + +static void virtio_submit_multiwrite(BlockDriverState *bs, MultiReqBuffer *mrb) { int i, ret; - ret = bdrv_aio_multiwrite(bs, blkreq, num_writes); + if (!mrb->num_writes) { + return; + } + + ret = bdrv_aio_multiwrite(bs, mrb->blkreq, mrb->num_writes); if (ret != 0) { - for (i = 0; i < num_writes; i++) { - if (blkreq[i].error) { - virtio_blk_rw_complete(blkreq[i].opaque, -EIO); + for (i = 0; i < mrb->num_writes; i++) { + if (mrb->blkreq[i].error) { + virtio_blk_rw_complete(mrb->blkreq[i].opaque, -EIO); } } } + + mrb->num_writes = 0; } -static void virtio_blk_handle_flush(BlockRequest *blkreq, int *num_writes, - VirtIOBlockReq *req, BlockDriverState **old_bs) +static void virtio_blk_handle_flush(VirtIOBlockReq *req, MultiReqBuffer *mrb) { BlockDriverAIOCB *acb; /* * Make sure all outstanding writes are posted to the backing device. */ - if (*old_bs != NULL) { - do_multiwrite(*old_bs, blkreq, *num_writes); - } - *num_writes = 0; - *old_bs = req->dev->bs; + virtio_submit_multiwrite(req->dev->bs, mrb); acb = bdrv_aio_flush(req->dev->bs, virtio_blk_flush_complete, req); if (!acb) { - virtio_blk_req_complete(req, VIRTIO_BLK_S_IOERR); + virtio_blk_flush_complete(req, -EIO); } } -static void virtio_blk_handle_write(BlockRequest *blkreq, int *num_writes, - VirtIOBlockReq *req, BlockDriverState **old_bs) +static void virtio_blk_handle_write(VirtIOBlockReq *req, MultiReqBuffer *mrb) { - if (req->dev->bs != *old_bs || *num_writes == 32) { - if (*old_bs != NULL) { - do_multiwrite(*old_bs, blkreq, *num_writes); - } - *num_writes = 0; - *old_bs = req->dev->bs; + BlockRequest *blkreq; + uint64_t sector; + + sector = ldq_p(&req->out->sector); + + trace_virtio_blk_handle_write(req, sector, req->qiov.size / 512); + + if (sector & req->dev->sector_mask) { + virtio_blk_rw_complete(req, -EIO); + return; + } + if (req->qiov.size % req->dev->conf->logical_block_size) { + virtio_blk_rw_complete(req, -EIO); + return; + } + + if (mrb->num_writes == 32) { + virtio_submit_multiwrite(req->dev->bs, mrb); } - blkreq[*num_writes].sector = req->out->sector; - blkreq[*num_writes].nb_sectors = req->qiov.size / 512; - blkreq[*num_writes].qiov = &req->qiov; - blkreq[*num_writes].cb = virtio_blk_rw_complete; - blkreq[*num_writes].opaque = req; - blkreq[*num_writes].error = 0; + blkreq = &mrb->blkreq[mrb->num_writes]; + blkreq->sector = sector; + blkreq->nb_sectors = req->qiov.size / BDRV_SECTOR_SIZE; + blkreq->qiov = &req->qiov; + blkreq->cb = virtio_blk_rw_complete; + blkreq->opaque = req; + blkreq->error = 0; - (*num_writes)++; + mrb->num_writes++; } static void virtio_blk_handle_read(VirtIOBlockReq *req) { BlockDriverAIOCB *acb; + uint64_t sector; + + sector = ldq_p(&req->out->sector); + + if (sector & req->dev->sector_mask) { + virtio_blk_rw_complete(req, -EIO); + return; + } + if (req->qiov.size % req->dev->conf->logical_block_size) { + virtio_blk_rw_complete(req, -EIO); + return; + } - acb = bdrv_aio_readv(req->dev->bs, req->out->sector, &req->qiov, - req->qiov.size / 512, virtio_blk_rw_complete, req); + acb = bdrv_aio_readv(req->dev->bs, sector, &req->qiov, + req->qiov.size / BDRV_SECTOR_SIZE, + virtio_blk_rw_complete, req); if (!acb) { virtio_blk_rw_complete(req, -EIO); } } -typedef struct MultiReqBuffer { - BlockRequest blkreq[32]; - int num_writes; - BlockDriverState *old_bs; -} MultiReqBuffer; - static void virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb) { + uint32_t type; + if (req->elem.out_num < 1 || req->elem.in_num < 1) { - fprintf(stderr, "virtio-blk missing headers\n"); + error_report("virtio-blk missing headers"); exit(1); } if (req->elem.out_sg[0].iov_len < sizeof(*req->out) || req->elem.in_sg[req->elem.in_num - 1].iov_len < sizeof(*req->in)) { - fprintf(stderr, "virtio-blk header not in correct element\n"); + error_report("virtio-blk header not in correct element"); exit(1); } req->out = (void *)req->elem.out_sg[0].iov_base; req->in = (void *)req->elem.in_sg[req->elem.in_num - 1].iov_base; - if (req->out->type & VIRTIO_BLK_T_FLUSH) { - virtio_blk_handle_flush(mrb->blkreq, &mrb->num_writes, - req, &mrb->old_bs); - } else if (req->out->type & VIRTIO_BLK_T_SCSI_CMD) { + type = ldl_p(&req->out->type); + + if (type & VIRTIO_BLK_T_FLUSH) { + virtio_blk_handle_flush(req, mrb); + } else if (type & VIRTIO_BLK_T_SCSI_CMD) { virtio_blk_handle_scsi(req); - } else if (req->out->type & VIRTIO_BLK_T_OUT) { + } else if (type & VIRTIO_BLK_T_GET_ID) { + VirtIOBlock *s = req->dev; + + memcpy(req->elem.in_sg[0].iov_base, s->sn, + MIN(req->elem.in_sg[0].iov_len, sizeof(s->sn))); + virtio_blk_req_complete(req, VIRTIO_BLK_S_OK); + } else if (type & VIRTIO_BLK_T_OUT) { qemu_iovec_init_external(&req->qiov, &req->elem.out_sg[1], req->elem.out_num - 1); - virtio_blk_handle_write(mrb->blkreq, &mrb->num_writes, - req, &mrb->old_bs); + virtio_blk_handle_write(req, mrb); } else { qemu_iovec_init_external(&req->qiov, &req->elem.in_sg[0], req->elem.in_num - 1); @@ -376,16 +382,13 @@ VirtIOBlockReq *req; MultiReqBuffer mrb = { .num_writes = 0, - .old_bs = NULL, }; while ((req = virtio_blk_get_request(s))) { virtio_blk_handle_request(req, &mrb); } - if (mrb.num_writes > 0) { - do_multiwrite(mrb.old_bs, mrb.blkreq, mrb.num_writes); - } + virtio_submit_multiwrite(s->bs, &mrb); /* * FIXME: Want to check for completions before returning to guest mode, @@ -400,7 +403,6 @@ VirtIOBlockReq *req = s->rq; MultiReqBuffer mrb = { .num_writes = 0, - .old_bs = NULL, }; qemu_bh_delete(s->bh); @@ -413,9 +415,7 @@ req = req->next; } - if (mrb.num_writes > 0) { - do_multiwrite(mrb.old_bs, mrb.blkreq, mrb.num_writes); - } + virtio_submit_multiwrite(s->bs, &mrb); } static void virtio_blk_dma_restart_cb(void *opaque, int running, int reason) @@ -456,29 +456,27 @@ stl_raw(&blkcfg.seg_max, 128 - 2); stw_raw(&blkcfg.cylinders, cylinders); blkcfg.heads = heads; - blkcfg.sectors = secs; + blkcfg.sectors = secs & ~s->sector_mask; + blkcfg.blk_size = s->conf->logical_block_size; blkcfg.size_max = 0; - virtio_identify_template(&blkcfg); - memcpy(&blkcfg.identify[VIRTIO_BLK_ID_SN], s->serial_str, - VIRTIO_BLK_ID_SN_BYTES); - memcpy(config, &blkcfg, s->config_size); + blkcfg.physical_block_exp = get_physical_block_exp(s->conf); + blkcfg.alignment_offset = 0; + blkcfg.min_io_size = s->conf->min_io_size / blkcfg.blk_size; + blkcfg.opt_io_size = s->conf->opt_io_size / blkcfg.blk_size; + memcpy(config, &blkcfg, sizeof(struct virtio_blk_config)); } -static uint32_t virtio_blk_get_features(VirtIODevice *vdev) +static uint32_t virtio_blk_get_features(VirtIODevice *vdev, uint32_t features) { VirtIOBlock *s = to_virtio_blk(vdev); - uint32_t features = 0; features |= (1 << VIRTIO_BLK_F_SEG_MAX); features |= (1 << VIRTIO_BLK_F_GEOMETRY); + features |= (1 << VIRTIO_BLK_F_TOPOLOGY); + features |= (1 << VIRTIO_BLK_F_BLK_SIZE); if (bdrv_enable_write_cache(s->bs)) features |= (1 << VIRTIO_BLK_F_WCACHE); -#ifdef __linux__ - features |= (1 << VIRTIO_BLK_F_SCSI); -#endif - if (strcmp(s->serial_str, "0")) - features |= 1 << VIRTIO_BLK_F_IDENTIFY; if (bdrv_is_read_only(s->bs)) features |= 1 << VIRTIO_BLK_F_RO; @@ -513,43 +511,78 @@ VirtIOBlockReq *req = virtio_blk_alloc_request(s); qemu_get_buffer(f, (unsigned char*)&req->elem, sizeof(req->elem)); req->next = s->rq; - s->rq = req->next; + s->rq = req; + + virtqueue_map_sg(req->elem.in_sg, req->elem.in_addr, + req->elem.in_num, 1); + virtqueue_map_sg(req->elem.out_sg, req->elem.out_addr, + req->elem.out_num, 0); } return 0; } -VirtIODevice *virtio_blk_init(DeviceState *dev, DriveInfo *dinfo) +static void virtio_blk_change_cb(void *opaque, int reason) +{ + VirtIOBlock *s = opaque; + + if (reason & CHANGE_SIZE) { + virtio_notify_config(&s->vdev); + } +} + +VirtIODevice *virtio_blk_init(DeviceState *dev, BlockConf *conf) { VirtIOBlock *s; int cylinders, heads, secs; static int virtio_blk_id; - char *ps = (char *)drive_get_serial(dinfo->bdrv); - size_t size = strlen(ps) ? sizeof(struct virtio_blk_config) : - offsetof(struct virtio_blk_config, _blk_size); + DriveInfo *dinfo; + + if (!conf->bs) { + error_report("virtio-blk-pci: drive property not set"); + return NULL; + } + if (!bdrv_is_inserted(conf->bs)) { + error_report("Device needs media, but drive is empty"); + return NULL; + } s = (VirtIOBlock *)virtio_common_init("virtio-blk", VIRTIO_ID_BLOCK, - size, + sizeof(struct virtio_blk_config), sizeof(VirtIOBlock)); - s->config_size = size; s->vdev.get_config = virtio_blk_update_config; s->vdev.get_features = virtio_blk_get_features; s->vdev.reset = virtio_blk_reset; - s->bs = dinfo->bdrv; + s->bs = conf->bs; + s->conf = conf; s->rq = NULL; - if (strlen(ps)) - strncpy(s->serial_str, ps, sizeof(s->serial_str)); - else - snprintf(s->serial_str, sizeof(s->serial_str), "0"); + s->sector_mask = (s->conf->logical_block_size / BDRV_SECTOR_SIZE) - 1; bdrv_guess_geometry(s->bs, &cylinders, &heads, &secs); - bdrv_set_geometry_hint(s->bs, cylinders, heads, secs); + + /* NB: per existing s/n string convention the string is terminated + * by '\0' only when less than sizeof (s->sn) + */ + dinfo = drive_get_by_blockdev(s->bs); + strncpy(s->sn, dinfo->serial, sizeof (s->sn)); s->vq = virtio_add_queue(&s->vdev, 128, virtio_blk_handle_output); qemu_add_vm_change_state_handler(virtio_blk_dma_restart_cb, s); - register_savevm("virtio-blk", virtio_blk_id++, 2, + s->qdev = dev; + register_savevm(dev, "virtio-blk", virtio_blk_id++, 2, virtio_blk_save, virtio_blk_load, s); + bdrv_set_removable(s->bs, 0); + bdrv_set_change_cb(s->bs, virtio_blk_change_cb, s); + s->bs->buffer_alignment = conf->logical_block_size; + + add_boot_device_path(conf->bootindex, dev, "/disk@0,0"); return &s->vdev; } + +void virtio_blk_exit(VirtIODevice *vdev) +{ + VirtIOBlock *s = to_virtio_blk(vdev); + unregister_savevm(s->qdev, "virtio-blk", s); +} diff -Nru qemu-kvm-0.12.5+noroms/hw/virtio-blk.h qemu-kvm-0.14.1/hw/virtio-blk.h --- qemu-kvm-0.12.5+noroms/hw/virtio-blk.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/virtio-blk.h 2011-05-11 13:29:46.000000000 +0000 @@ -30,12 +30,9 @@ #define VIRTIO_BLK_F_RO 5 /* Disk is read-only */ #define VIRTIO_BLK_F_BLK_SIZE 6 /* Block size of disk is available*/ #define VIRTIO_BLK_F_SCSI 7 /* Supports scsi command passthru */ -#define VIRTIO_BLK_F_IDENTIFY 8 /* ATA IDENTIFY supported */ +/* #define VIRTIO_BLK_F_IDENTIFY 8 ATA IDENTIFY supported, DEPRECATED */ #define VIRTIO_BLK_F_WCACHE 9 /* write cache enabled */ - -#define VIRTIO_BLK_ID_LEN 256 /* length of identify u16 array */ -#define VIRTIO_BLK_ID_SN 10 /* start of char * serial# */ -#define VIRTIO_BLK_ID_SN_BYTES 20 /* length in bytes of serial# */ +#define VIRTIO_BLK_F_TOPOLOGY 10 /* Topology information is available */ struct virtio_blk_config { @@ -45,8 +42,11 @@ uint16_t cylinders; uint8_t heads; uint8_t sectors; - uint32_t _blk_size; /* structure pad, currently unused */ - uint16_t identify[VIRTIO_BLK_ID_LEN]; + uint32_t blk_size; + uint8_t physical_block_exp; + uint8_t alignment_offset; + uint16_t min_io_size; + uint32_t opt_io_size; } __attribute__((packed)); /* These two define direction. */ @@ -59,6 +59,9 @@ /* Flush the volatile write cache */ #define VIRTIO_BLK_T_FLUSH 4 +/* return the device ID string */ +#define VIRTIO_BLK_T_GET_ID 8 + /* Barrier before this op. */ #define VIRTIO_BLK_T_BARRIER 0x80000000 @@ -92,4 +95,12 @@ uint32_t residual; }; +#ifdef __linux__ +#define DEFINE_VIRTIO_BLK_FEATURES(_state, _field) \ + DEFINE_VIRTIO_COMMON_FEATURES(_state, _field), \ + DEFINE_PROP_BIT("scsi", _state, _field, VIRTIO_BLK_F_SCSI, true) +#else +#define DEFINE_VIRTIO_BLK_FEATURES(_state, _field) \ + DEFINE_VIRTIO_COMMON_FEATURES(_state, _field) +#endif #endif diff -Nru qemu-kvm-0.12.5+noroms/hw/virtio.c qemu-kvm-0.14.1/hw/virtio.c --- qemu-kvm-0.12.5+noroms/hw/virtio.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/virtio.c 2011-05-11 13:29:46.000000000 +0000 @@ -13,6 +13,8 @@ #include +#include "trace.h" +#include "qemu-error.h" #include "virtio.h" #include "sysemu.h" @@ -73,10 +75,11 @@ int inuse; uint16_t vector; void (*handle_output)(VirtIODevice *vdev, VirtQueue *vq); + VirtIODevice *vdev; + EventNotifier guest_notifier; + EventNotifier host_notifier; }; -#define VIRTIO_PCI_QUEUE_MAX 16 - /* virt queue functions */ static void virtqueue_init(VirtQueue *vq) { @@ -204,6 +207,8 @@ unsigned int offset; int i; + trace_virtqueue_fill(vq, elem, len, idx); + offset = 0; for (i = 0; i < elem->in_num; i++) { size_t size = MIN(len - offset, elem->in_sg[i].iov_len); @@ -231,6 +236,7 @@ { /* Make sure buffer is written before we update index. */ wmb(); + trace_virtqueue_flush(vq, count); vring_used_idx_increment(vq, count); vq->inuse -= count; } @@ -248,8 +254,8 @@ /* Check it isn't doing very strange things with descriptor numbers. */ if (num_heads > vq->vring.num) { - fprintf(stderr, "Guest moved used index from %u to %u", - idx, vring_avail_idx(vq)); + error_report("Guest moved used index from %u to %u", + idx, vring_avail_idx(vq)); exit(1); } @@ -266,7 +272,7 @@ /* If their number is silly, that's a fatal mistake. */ if (head >= vq->vring.num) { - fprintf(stderr, "Guest says index %u is available", head); + error_report("Guest says index %u is available", head); exit(1); } @@ -288,7 +294,7 @@ wmb(); if (next >= max) { - fprintf(stderr, "Desc next is %u", next); + error_report("Desc next is %u", next); exit(1); } @@ -315,13 +321,13 @@ if (vring_desc_flags(desc_pa, i) & VRING_DESC_F_INDIRECT) { if (vring_desc_len(desc_pa, i) % sizeof(VRingDesc)) { - fprintf(stderr, "Invalid size for indirect buffer table\n"); + error_report("Invalid size for indirect buffer table"); exit(1); } /* If we've got too many, that implies a descriptor loop. */ if (num_bufs >= max) { - fprintf(stderr, "Looped descriptor"); + error_report("Looped descriptor"); exit(1); } @@ -335,7 +341,7 @@ do { /* If we've got too many, that implies a descriptor loop. */ if (++num_bufs > max) { - fprintf(stderr, "Looped descriptor"); + error_report("Looped descriptor"); exit(1); } @@ -359,11 +365,26 @@ return 0; } +void virtqueue_map_sg(struct iovec *sg, target_phys_addr_t *addr, + size_t num_sg, int is_write) +{ + unsigned int i; + target_phys_addr_t len; + + for (i = 0; i < num_sg; i++) { + len = sg[i].iov_len; + sg[i].iov_base = cpu_physical_memory_map(addr[i], &len, is_write); + if (sg[i].iov_base == NULL || len != sg[i].iov_len) { + error_report("virtio: trying to map MMIO memory"); + exit(1); + } + } +} + int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem) { unsigned int i, head, max; target_phys_addr_t desc_pa = vq->vring.desc; - target_phys_addr_t len; if (!virtqueue_num_heads(vq, vq->last_avail_idx)) return 0; @@ -377,7 +398,7 @@ if (vring_desc_flags(desc_pa, i) & VRING_DESC_F_INDIRECT) { if (vring_desc_len(desc_pa, i) % sizeof(VRingDesc)) { - fprintf(stderr, "Invalid size for indirect buffer table\n"); + error_report("Invalid size for indirect buffer table"); exit(1); } @@ -387,40 +408,36 @@ i = 0; } + /* Collect all the descriptors */ do { struct iovec *sg; - int is_write = 0; if (vring_desc_flags(desc_pa, i) & VRING_DESC_F_WRITE) { elem->in_addr[elem->in_num] = vring_desc_addr(desc_pa, i); sg = &elem->in_sg[elem->in_num++]; - is_write = 1; - } else + } else { + elem->out_addr[elem->out_num] = vring_desc_addr(desc_pa, i); sg = &elem->out_sg[elem->out_num++]; + } - /* Grab the first descriptor, and check it's OK. */ sg->iov_len = vring_desc_len(desc_pa, i); - len = sg->iov_len; - - sg->iov_base = cpu_physical_memory_map(vring_desc_addr(desc_pa, i), - &len, is_write); - - if (sg->iov_base == NULL || len != sg->iov_len) { - fprintf(stderr, "virtio: trying to map MMIO memory\n"); - exit(1); - } /* If we've got too many, that implies a descriptor loop. */ if ((elem->in_num + elem->out_num) > max) { - fprintf(stderr, "Looped descriptor"); + error_report("Looped descriptor"); exit(1); } } while ((i = virtqueue_next_desc(desc_pa, i, max)) != max); + /* Now map what we have collected */ + virtqueue_map_sg(elem->in_sg, elem->in_addr, elem->in_num, 1); + virtqueue_map_sg(elem->out_sg, elem->out_addr, elem->out_num, 0); + elem->index = head; vq->inuse++; + trace_virtqueue_pop(vq, elem, elem->in_num, elem->out_num); return elem->in_num + elem->out_num; } @@ -442,10 +459,12 @@ VirtIODevice *vdev = opaque; int i; + virtio_set_status(vdev, 0); + if (vdev->reset) vdev->reset(vdev); - vdev->features = 0; + vdev->guest_features = 0; vdev->queue_sel = 0; vdev->status = 0; vdev->isr = 0; @@ -556,10 +575,19 @@ return vdev->vq[n].vring.num; } +void virtio_queue_notify_vq(VirtQueue *vq) +{ + if (vq->vring.desc) { + VirtIODevice *vdev = vq->vdev; + trace_virtio_queue_notify(vdev, vq - vdev->vq, vq); + vq->handle_output(vdev, vq); + } +} + void virtio_queue_notify(VirtIODevice *vdev, int n) { - if (n < VIRTIO_PCI_QUEUE_MAX && vdev->vq[n].vring.desc) { - vdev->vq[n].handle_output(vdev, &vdev->vq[n]); + if (n < VIRTIO_PCI_QUEUE_MAX) { + virtio_queue_notify_vq(&vdev->vq[n]); } } @@ -594,14 +622,22 @@ return &vdev->vq[i]; } +void virtio_irq(VirtQueue *vq) +{ + trace_virtio_irq(vq); + vq->vdev->isr |= 0x01; + virtio_notify_vector(vq->vdev, vq->vector); +} + void virtio_notify(VirtIODevice *vdev, VirtQueue *vq) { /* Always notify when queue is empty (when feature acknowledge) */ if ((vring_avail_flags(vq) & VRING_AVAIL_F_NO_INTERRUPT) && - (!(vdev->features & (1 << VIRTIO_F_NOTIFY_ON_EMPTY)) || + (!(vdev->guest_features & (1 << VIRTIO_F_NOTIFY_ON_EMPTY)) || (vq->inuse || vring_avail_idx(vq) != vq->last_avail_idx))) return; + trace_virtio_notify(vdev, vq); vdev->isr |= 0x01; virtio_notify_vector(vdev, vq->vector); } @@ -625,7 +661,7 @@ qemu_put_8s(f, &vdev->status); qemu_put_8s(f, &vdev->isr); qemu_put_be16s(f, &vdev->queue_sel); - qemu_put_be32s(f, &vdev->features); + qemu_put_be32s(f, &vdev->guest_features); qemu_put_be32(f, vdev->config_len); qemu_put_buffer(f, vdev->config, vdev->config_len); @@ -652,7 +688,7 @@ { int num, i, ret; uint32_t features; - uint32_t supported_features = vdev->get_features(vdev) | + uint32_t supported_features = vdev->binding->get_features(vdev->binding_opaque); if (vdev->binding->load_config) { @@ -666,11 +702,13 @@ qemu_get_be16s(f, &vdev->queue_sel); qemu_get_be32s(f, &features); if (features & ~supported_features) { - fprintf(stderr, "Features 0x%x unsupported. Allowed features: 0x%x\n", - features, supported_features); + error_report("Features 0x%x unsupported. Allowed features: 0x%x", + features, supported_features); return -1; } - vdev->features = features; + if (vdev->set_features) + vdev->set_features(vdev, features); + vdev->guest_features = features; vdev->config_len = qemu_get_be32(f); qemu_get_buffer(f, vdev->config, vdev->config_len); @@ -682,8 +720,24 @@ qemu_get_be16s(f, &vdev->vq[i].last_avail_idx); if (vdev->vq[i].pa) { + uint16_t nheads; virtqueue_init(&vdev->vq[i]); - } + nheads = vring_avail_idx(&vdev->vq[i]) - vdev->vq[i].last_avail_idx; + /* Check it isn't doing very strange things with descriptor numbers. */ + if (nheads > vdev->vq[i].vring.num) { + error_report("VQ %d size 0x%x Guest index 0x%x " + "inconsistent with Host index 0x%x: delta 0x%x\n", + i, vdev->vq[i].vring.num, + vring_avail_idx(&vdev->vq[i]), + vdev->vq[i].last_avail_idx, nheads); + return -1; + } + } else if (vdev->vq[i].last_avail_idx) { + error_report("VQ %d address 0x0 " + "inconsistent with Host index 0x%x\n", + i, vdev->vq[i].last_avail_idx); + return -1; + } if (vdev->binding->load_queue) { ret = vdev->binding->load_queue(vdev->binding_opaque, i, f); if (ret) @@ -697,11 +751,31 @@ void virtio_cleanup(VirtIODevice *vdev) { + qemu_del_vm_change_state_handler(vdev->vmstate); if (vdev->config) qemu_free(vdev->config); qemu_free(vdev->vq); } +static void virtio_vmstate_change(void *opaque, int running, int reason) +{ + VirtIODevice *vdev = opaque; + bool backend_run = running && (vdev->status & VIRTIO_CONFIG_S_DRIVER_OK); + vdev->vm_running = running; + + if (backend_run) { + virtio_set_status(vdev, vdev->status); + } + + if (vdev->binding->vmstate_change) { + vdev->binding->vmstate_change(vdev->binding_opaque, backend_run); + } + + if (!backend_run) { + virtio_set_status(vdev, vdev->status); + } +} + VirtIODevice *virtio_common_init(const char *name, uint16_t device_id, size_t config_size, size_t struct_size) { @@ -716,8 +790,10 @@ vdev->queue_sel = 0; vdev->config_vector = VIRTIO_NO_VECTOR; vdev->vq = qemu_mallocz(sizeof(VirtQueue) * VIRTIO_PCI_QUEUE_MAX); - for(i = 0; i < VIRTIO_PCI_QUEUE_MAX; i++) + for(i = 0; i < VIRTIO_PCI_QUEUE_MAX; i++) { vdev->vq[i].vector = VIRTIO_NO_VECTOR; + vdev->vq[i].vdev = vdev; + } vdev->name = name; vdev->config_len = config_size; @@ -726,6 +802,8 @@ else vdev->config = NULL; + vdev->vmstate = qemu_add_vm_change_state_handler(virtio_vmstate_change, vdev); + return vdev; } @@ -735,3 +813,70 @@ vdev->binding = binding; vdev->binding_opaque = opaque; } + +target_phys_addr_t virtio_queue_get_desc_addr(VirtIODevice *vdev, int n) +{ + return vdev->vq[n].vring.desc; +} + +target_phys_addr_t virtio_queue_get_avail_addr(VirtIODevice *vdev, int n) +{ + return vdev->vq[n].vring.avail; +} + +target_phys_addr_t virtio_queue_get_used_addr(VirtIODevice *vdev, int n) +{ + return vdev->vq[n].vring.used; +} + +target_phys_addr_t virtio_queue_get_ring_addr(VirtIODevice *vdev, int n) +{ + return vdev->vq[n].vring.desc; +} + +target_phys_addr_t virtio_queue_get_desc_size(VirtIODevice *vdev, int n) +{ + return sizeof(VRingDesc) * vdev->vq[n].vring.num; +} + +target_phys_addr_t virtio_queue_get_avail_size(VirtIODevice *vdev, int n) +{ + return offsetof(VRingAvail, ring) + + sizeof(uint64_t) * vdev->vq[n].vring.num; +} + +target_phys_addr_t virtio_queue_get_used_size(VirtIODevice *vdev, int n) +{ + return offsetof(VRingUsed, ring) + + sizeof(VRingUsedElem) * vdev->vq[n].vring.num; +} + +target_phys_addr_t virtio_queue_get_ring_size(VirtIODevice *vdev, int n) +{ + return vdev->vq[n].vring.used - vdev->vq[n].vring.desc + + virtio_queue_get_used_size(vdev, n); +} + +uint16_t virtio_queue_get_last_avail_idx(VirtIODevice *vdev, int n) +{ + return vdev->vq[n].last_avail_idx; +} + +void virtio_queue_set_last_avail_idx(VirtIODevice *vdev, int n, uint16_t idx) +{ + vdev->vq[n].last_avail_idx = idx; +} + +VirtQueue *virtio_get_queue(VirtIODevice *vdev, int n) +{ + return vdev->vq + n; +} + +EventNotifier *virtio_queue_get_guest_notifier(VirtQueue *vq) +{ + return &vq->guest_notifier; +} +EventNotifier *virtio_queue_get_host_notifier(VirtQueue *vq) +{ + return &vq->host_notifier; +} diff -Nru qemu-kvm-0.12.5+noroms/hw/virtio-console.c qemu-kvm-0.14.1/hw/virtio-console.c --- qemu-kvm-0.12.5+noroms/hw/virtio-console.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/virtio-console.c 2011-05-11 13:29:46.000000000 +0000 @@ -1,146 +1,141 @@ /* - * Virtio Console Device + * Virtio Console and Generic Serial Port Devices * - * Copyright IBM, Corp. 2008 + * Copyright Red Hat, Inc. 2009, 2010 * * Authors: - * Christian Ehrhardt + * Amit Shah * * This work is licensed under the terms of the GNU GPL, version 2. See * the COPYING file in the top-level directory. - * */ -#include "hw.h" #include "qemu-char.h" -#include "virtio.h" -#include "virtio-console.h" - +#include "virtio-serial.h" -typedef struct VirtIOConsole -{ - VirtIODevice vdev; - VirtQueue *ivq, *ovq; +typedef struct VirtConsole { + VirtIOSerialPort port; CharDriverState *chr; -} VirtIOConsole; +} VirtConsole; -static VirtIOConsole *to_virtio_console(VirtIODevice *vdev) -{ - return (VirtIOConsole *)vdev; -} -static void virtio_console_handle_output(VirtIODevice *vdev, VirtQueue *vq) +/* Callback function that's called when the guest sends us data */ +static ssize_t flush_buf(VirtIOSerialPort *port, const uint8_t *buf, size_t len) { - VirtIOConsole *s = to_virtio_console(vdev); - VirtQueueElement elem; - - while (virtqueue_pop(vq, &elem)) { - ssize_t len = 0; - int d; + VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port); - for (d = 0; d < elem.out_num; d++) { - len += qemu_chr_write(s->chr, (uint8_t *)elem.out_sg[d].iov_base, - elem.out_sg[d].iov_len); - } - virtqueue_push(vq, &elem, len); - virtio_notify(vdev, vq); - } + return qemu_chr_write(vcon->chr, buf, len); } -static void virtio_console_handle_input(VirtIODevice *vdev, VirtQueue *vq) +/* Readiness of the guest to accept data on a port */ +static int chr_can_read(void *opaque) { -} + VirtConsole *vcon = opaque; -static uint32_t virtio_console_get_features(VirtIODevice *vdev) -{ - return 0; + return virtio_serial_guest_ready(&vcon->port); } -static int vcon_can_read(void *opaque) +/* Send data from a char device over to the guest */ +static void chr_read(void *opaque, const uint8_t *buf, int size) { - VirtIOConsole *s = (VirtIOConsole *) opaque; + VirtConsole *vcon = opaque; - if (!virtio_queue_ready(s->ivq) || - !(s->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK) || - virtio_queue_empty(s->ivq)) - return 0; - - /* current implementations have a page sized buffer. - * We fall back to a one byte per read if there is not enough room. - * It would be cool to have a function that returns the available byte - * instead of checking for a limit */ - if (virtqueue_avail_bytes(s->ivq, TARGET_PAGE_SIZE, 0)) - return TARGET_PAGE_SIZE; - if (virtqueue_avail_bytes(s->ivq, 1, 0)) - return 1; - return 0; + virtio_serial_write(&vcon->port, buf, size); } -static void vcon_read(void *opaque, const uint8_t *buf, int size) +static void chr_event(void *opaque, int event) { - VirtIOConsole *s = (VirtIOConsole *) opaque; - VirtQueueElement elem; - int offset = 0; - - /* The current kernel implementation has only one outstanding input - * buffer of PAGE_SIZE. Nevertheless, this function is prepared to - * handle multiple buffers with multiple sg element for input */ - while (offset < size) { - int i = 0; - if (!virtqueue_pop(s->ivq, &elem)) - break; - while (offset < size && i < elem.in_num) { - int len = MIN(elem.in_sg[i].iov_len, size - offset); - memcpy(elem.in_sg[i].iov_base, buf + offset, len); - offset += len; - i++; - } - virtqueue_push(s->ivq, &elem, size); + VirtConsole *vcon = opaque; + + switch (event) { + case CHR_EVENT_OPENED: + virtio_serial_open(&vcon->port); + break; + case CHR_EVENT_CLOSED: + virtio_serial_close(&vcon->port); + break; } - virtio_notify(&s->vdev, s->ivq); } -static void vcon_event(void *opaque, int event) +static int generic_port_init(VirtConsole *vcon, VirtIOSerialDevice *dev) { - /* we will ignore any event for the time being */ + vcon->port.info = dev->info; + + if (vcon->chr) { + qemu_chr_add_handlers(vcon->chr, chr_can_read, chr_read, chr_event, + vcon); + vcon->port.info->have_data = flush_buf; + } + return 0; } -static void virtio_console_save(QEMUFile *f, void *opaque) +/* Virtio Console Ports */ +static int virtconsole_initfn(VirtIOSerialDevice *dev) { - VirtIOConsole *s = opaque; + VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, &dev->qdev); + VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port); - virtio_save(&s->vdev, f); + port->is_console = true; + return generic_port_init(vcon, dev); } -static int virtio_console_load(QEMUFile *f, void *opaque, int version_id) +static int virtconsole_exitfn(VirtIOSerialDevice *dev) { - VirtIOConsole *s = opaque; + VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, &dev->qdev); + VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port); - if (version_id != 1) - return -EINVAL; + if (vcon->chr) { + port->info->have_data = NULL; + qemu_chr_close(vcon->chr); + } - virtio_load(&s->vdev, f); return 0; } -VirtIODevice *virtio_console_init(DeviceState *dev) -{ - VirtIOConsole *s; - s = (VirtIOConsole *)virtio_common_init("virtio-console", - VIRTIO_ID_CONSOLE, - 0, sizeof(VirtIOConsole)); - if (s == NULL) - return NULL; +static VirtIOSerialPortInfo virtconsole_info = { + .qdev.name = "virtconsole", + .qdev.size = sizeof(VirtConsole), + .init = virtconsole_initfn, + .exit = virtconsole_exitfn, + .qdev.props = (Property[]) { + DEFINE_PROP_UINT8("is_console", VirtConsole, port.is_console, 1), + DEFINE_PROP_UINT32("nr", VirtConsole, port.id, VIRTIO_CONSOLE_BAD_ID), + DEFINE_PROP_CHR("chardev", VirtConsole, chr), + DEFINE_PROP_STRING("name", VirtConsole, port.name), + DEFINE_PROP_END_OF_LIST(), + }, +}; + +static void virtconsole_register(void) +{ + virtio_serial_port_qdev_register(&virtconsole_info); +} +device_init(virtconsole_register) + +/* Generic Virtio Serial Ports */ +static int virtserialport_initfn(VirtIOSerialDevice *dev) +{ + VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, &dev->qdev); + VirtConsole *vcon = DO_UPCAST(VirtConsole, port, port); + + return generic_port_init(vcon, dev); +} + +static VirtIOSerialPortInfo virtserialport_info = { + .qdev.name = "virtserialport", + .qdev.size = sizeof(VirtConsole), + .init = virtserialport_initfn, + .exit = virtconsole_exitfn, + .qdev.props = (Property[]) { + DEFINE_PROP_UINT32("nr", VirtConsole, port.id, VIRTIO_CONSOLE_BAD_ID), + DEFINE_PROP_CHR("chardev", VirtConsole, chr), + DEFINE_PROP_STRING("name", VirtConsole, port.name), + DEFINE_PROP_END_OF_LIST(), + }, +}; - s->vdev.get_features = virtio_console_get_features; - - s->ivq = virtio_add_queue(&s->vdev, 128, virtio_console_handle_input); - s->ovq = virtio_add_queue(&s->vdev, 128, virtio_console_handle_output); - - s->chr = qdev_init_chardev(dev); - qemu_chr_add_handlers(s->chr, vcon_can_read, vcon_read, vcon_event, s); - - register_savevm("virtio-console", -1, 1, virtio_console_save, virtio_console_load, s); - - return &s->vdev; +static void virtserialport_register(void) +{ + virtio_serial_port_qdev_register(&virtserialport_info); } +device_init(virtserialport_register) diff -Nru qemu-kvm-0.12.5+noroms/hw/virtio-console.h qemu-kvm-0.14.1/hw/virtio-console.h --- qemu-kvm-0.12.5+noroms/hw/virtio-console.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/virtio-console.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,19 +0,0 @@ -/* - * Virtio Console Support - * - * Copyright IBM, Corp. 2008 - * - * Authors: - * Christian Ehrhardt - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - */ -#ifndef _QEMU_VIRTIO_CONSOLE_H -#define _QEMU_VIRTIO_CONSOLE_H - -/* The ID for virtio console */ -#define VIRTIO_ID_CONSOLE 3 - -#endif diff -Nru qemu-kvm-0.12.5+noroms/hw/virtio.h qemu-kvm-0.14.1/hw/virtio.h --- qemu-kvm-0.12.5+noroms/hw/virtio.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/virtio.h 2011-05-11 13:29:46.000000000 +0000 @@ -18,6 +18,11 @@ #include "net.h" #include "qdev.h" #include "sysemu.h" +#include "block_int.h" +#include "event_notifier.h" +#ifdef CONFIG_LINUX +#include "9p.h" +#endif /* from Linux's linux/virtio_config.h */ @@ -67,7 +72,6 @@ } typedef struct VirtQueue VirtQueue; -typedef struct VirtIODevice VirtIODevice; #define VIRTQUEUE_MAX_SIZE 1024 @@ -77,6 +81,7 @@ unsigned int out_num; unsigned int in_num; target_phys_addr_t in_addr[VIRTQUEUE_MAX_SIZE]; + target_phys_addr_t out_addr[VIRTQUEUE_MAX_SIZE]; struct iovec in_sg[VIRTQUEUE_MAX_SIZE]; struct iovec out_sg[VIRTQUEUE_MAX_SIZE]; } VirtQueueElement; @@ -87,10 +92,15 @@ void (*save_queue)(void * opaque, int n, QEMUFile *f); int (*load_config)(void * opaque, QEMUFile *f); int (*load_queue)(void * opaque, int n, QEMUFile *f); + int (*load_done)(void * opaque, QEMUFile *f); unsigned (*get_features)(void * opaque); + bool (*query_guest_notifiers)(void * opaque); + int (*set_guest_notifiers)(void * opaque, bool assigned); + int (*set_host_notifier)(void * opaque, int n, bool assigned); + void (*vmstate_change)(void * opaque, bool running); } VirtIOBindings; -#define VIRTIO_PCI_QUEUE_MAX 16 +#define VIRTIO_PCI_QUEUE_MAX 64 #define VIRTIO_NO_VECTOR 0xffff @@ -100,23 +110,34 @@ uint8_t status; uint8_t isr; uint16_t queue_sel; - uint32_t features; + uint32_t guest_features; size_t config_len; void *config; uint16_t config_vector; int nvectors; - uint32_t (*get_features)(VirtIODevice *vdev); + uint32_t (*get_features)(VirtIODevice *vdev, uint32_t requested_features); uint32_t (*bad_features)(VirtIODevice *vdev); void (*set_features)(VirtIODevice *vdev, uint32_t val); void (*get_config)(VirtIODevice *vdev, uint8_t *config); void (*set_config)(VirtIODevice *vdev, const uint8_t *config); void (*reset)(VirtIODevice *vdev); + void (*set_status)(VirtIODevice *vdev, uint8_t val); VirtQueue *vq; const VirtIOBindings *binding; void *binding_opaque; uint16_t device_id; + bool vm_running; + VMChangeStateEntry *vmstate; }; +static inline void virtio_set_status(VirtIODevice *vdev, uint8_t val) +{ + if (vdev->set_status) { + vdev->set_status(vdev, val); + } + vdev->status = val; +} + VirtQueue *virtio_add_queue(VirtIODevice *vdev, int queue_size, void (*handle_output)(VirtIODevice *, VirtQueue *)); @@ -127,6 +148,8 @@ void virtqueue_fill(VirtQueue *vq, const VirtQueueElement *elem, unsigned int len, unsigned int idx); +void virtqueue_map_sg(struct iovec *sg, target_phys_addr_t *addr, + size_t num_sg, int is_write); int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem); int virtqueue_avail_bytes(VirtQueue *vq, int in_bytes, int out_bytes); @@ -169,11 +192,38 @@ void *opaque); /* Base devices. */ -VirtIODevice *virtio_blk_init(DeviceState *dev, DriveInfo *dinfo); -VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf); -VirtIODevice *virtio_console_init(DeviceState *dev); +VirtIODevice *virtio_blk_init(DeviceState *dev, BlockConf *conf); +struct virtio_net_conf; +VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf, + struct virtio_net_conf *net); +VirtIODevice *virtio_serial_init(DeviceState *dev, uint32_t max_nr_ports); VirtIODevice *virtio_balloon_init(DeviceState *dev); +#ifdef CONFIG_LINUX +VirtIODevice *virtio_9p_init(DeviceState *dev, V9fsConf *conf); +#endif + void virtio_net_exit(VirtIODevice *vdev); +void virtio_blk_exit(VirtIODevice *vdev); +void virtio_serial_exit(VirtIODevice *vdev); +#define DEFINE_VIRTIO_COMMON_FEATURES(_state, _field) \ + DEFINE_PROP_BIT("indirect_desc", _state, _field, \ + VIRTIO_RING_F_INDIRECT_DESC, true) + +target_phys_addr_t virtio_queue_get_desc_addr(VirtIODevice *vdev, int n); +target_phys_addr_t virtio_queue_get_avail_addr(VirtIODevice *vdev, int n); +target_phys_addr_t virtio_queue_get_used_addr(VirtIODevice *vdev, int n); +target_phys_addr_t virtio_queue_get_ring_addr(VirtIODevice *vdev, int n); +target_phys_addr_t virtio_queue_get_desc_size(VirtIODevice *vdev, int n); +target_phys_addr_t virtio_queue_get_avail_size(VirtIODevice *vdev, int n); +target_phys_addr_t virtio_queue_get_used_size(VirtIODevice *vdev, int n); +target_phys_addr_t virtio_queue_get_ring_size(VirtIODevice *vdev, int n); +uint16_t virtio_queue_get_last_avail_idx(VirtIODevice *vdev, int n); +void virtio_queue_set_last_avail_idx(VirtIODevice *vdev, int n, uint16_t idx); +VirtQueue *virtio_get_queue(VirtIODevice *vdev, int n); +EventNotifier *virtio_queue_get_guest_notifier(VirtQueue *vq); +EventNotifier *virtio_queue_get_host_notifier(VirtQueue *vq); +void virtio_queue_notify_vq(VirtQueue *vq); +void virtio_irq(VirtQueue *vq); #endif diff -Nru qemu-kvm-0.12.5+noroms/hw/virtio-net.c qemu-kvm-0.14.1/hw/virtio-net.c --- qemu-kvm-0.12.5+noroms/hw/virtio-net.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/virtio-net.c 2011-05-11 13:29:46.000000000 +0000 @@ -11,12 +11,15 @@ * */ +#include "iov.h" #include "virtio.h" #include "net.h" #include "net/checksum.h" #include "net/tap.h" +#include "qemu-error.h" #include "qemu-timer.h" #include "virtio-net.h" +#include "vhost_net.h" #define VIRTIO_NET_VM_VERSION 11 @@ -33,7 +36,10 @@ VirtQueue *ctrl_vq; NICState *nic; QEMUTimer *tx_timer; - int tx_timer_active; + QEMUBH *tx_bh; + uint32_t tx_timeout; + int32_t tx_burst; + int tx_waiting; uint32_t has_vnet_hdr; uint8_t has_ufo; struct { @@ -47,6 +53,7 @@ uint8_t nomulti; uint8_t nouni; uint8_t nobcast; + uint8_t vhost_started; struct { int in_use; int first_multi; @@ -55,6 +62,7 @@ uint8_t *macs; } mac_table; uint32_t *vlans; + DeviceState *qdev; } VirtIONet; /* TODO @@ -71,7 +79,7 @@ VirtIONet *n = to_virtio_net(vdev); struct virtio_net_config netcfg; - netcfg.status = n->status; + stw_p(&netcfg.status, n->status); memcpy(netcfg.mac, n->mac, ETH_ALEN); memcpy(config, &netcfg, sizeof(netcfg)); } @@ -89,6 +97,71 @@ } } +static bool virtio_net_started(VirtIONet *n, uint8_t status) +{ + return (status & VIRTIO_CONFIG_S_DRIVER_OK) && + (n->status & VIRTIO_NET_S_LINK_UP) && n->vdev.vm_running; +} + +static void virtio_net_vhost_status(VirtIONet *n, uint8_t status) +{ + if (!n->nic->nc.peer) { + return; + } + if (n->nic->nc.peer->info->type != NET_CLIENT_TYPE_TAP) { + return; + } + + if (!tap_get_vhost_net(n->nic->nc.peer)) { + return; + } + if (!!n->vhost_started == virtio_net_started(n, status)) { + return; + } + if (!n->vhost_started) { + int r; + if (!vhost_net_query(tap_get_vhost_net(n->nic->nc.peer), &n->vdev)) { + return; + } + r = vhost_net_start(tap_get_vhost_net(n->nic->nc.peer), &n->vdev); + if (r < 0) { + error_report("unable to start vhost net: %d: " + "falling back on userspace virtio", -r); + } else { + n->vhost_started = 1; + } + } else { + vhost_net_stop(tap_get_vhost_net(n->nic->nc.peer), &n->vdev); + n->vhost_started = 0; + } +} + +static void virtio_net_set_status(struct VirtIODevice *vdev, uint8_t status) +{ + VirtIONet *n = to_virtio_net(vdev); + + virtio_net_vhost_status(n, status); + + if (!n->tx_waiting) { + return; + } + + if (virtio_net_started(n, status) && !n->vhost_started) { + if (n->tx_timer) { + qemu_mod_timer(n->tx_timer, + qemu_get_clock(vm_clock) + n->tx_timeout); + } else { + qemu_bh_schedule(n->tx_bh); + } + } else { + if (n->tx_timer) { + qemu_del_timer(n->tx_timer); + } else { + qemu_bh_cancel(n->tx_bh); + } + } +} + static void virtio_net_set_link_status(VLANClientState *nc) { VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque; @@ -101,6 +174,8 @@ if (n->status != old_status) virtio_notify_config(&n->vdev); + + virtio_net_set_status(&n->vdev, n->vdev.status); } static void virtio_net_reset(VirtIODevice *vdev) @@ -147,37 +222,39 @@ return n->has_ufo; } -static uint32_t virtio_net_get_features(VirtIODevice *vdev) +static uint32_t virtio_net_get_features(VirtIODevice *vdev, uint32_t features) { VirtIONet *n = to_virtio_net(vdev); - uint32_t features = (1 << VIRTIO_NET_F_MAC) | - (1 << VIRTIO_NET_F_MRG_RXBUF) | - (1 << VIRTIO_NET_F_STATUS) | - (1 << VIRTIO_NET_F_CTRL_VQ) | - (1 << VIRTIO_NET_F_CTRL_RX) | - (1 << VIRTIO_NET_F_CTRL_VLAN) | - (1 << VIRTIO_NET_F_CTRL_RX_EXTRA); + + features |= (1 << VIRTIO_NET_F_MAC); if (peer_has_vnet_hdr(n)) { tap_using_vnet_hdr(n->nic->nc.peer, 1); + } else { + features &= ~(0x1 << VIRTIO_NET_F_CSUM); + features &= ~(0x1 << VIRTIO_NET_F_HOST_TSO4); + features &= ~(0x1 << VIRTIO_NET_F_HOST_TSO6); + features &= ~(0x1 << VIRTIO_NET_F_HOST_ECN); - features |= (1 << VIRTIO_NET_F_CSUM); - features |= (1 << VIRTIO_NET_F_HOST_TSO4); - features |= (1 << VIRTIO_NET_F_HOST_TSO6); - features |= (1 << VIRTIO_NET_F_HOST_ECN); - - features |= (1 << VIRTIO_NET_F_GUEST_CSUM); - features |= (1 << VIRTIO_NET_F_GUEST_TSO4); - features |= (1 << VIRTIO_NET_F_GUEST_TSO6); - features |= (1 << VIRTIO_NET_F_GUEST_ECN); - - if (peer_has_ufo(n)) { - features |= (1 << VIRTIO_NET_F_GUEST_UFO); - features |= (1 << VIRTIO_NET_F_HOST_UFO); - } + features &= ~(0x1 << VIRTIO_NET_F_GUEST_CSUM); + features &= ~(0x1 << VIRTIO_NET_F_GUEST_TSO4); + features &= ~(0x1 << VIRTIO_NET_F_GUEST_TSO6); + features &= ~(0x1 << VIRTIO_NET_F_GUEST_ECN); } - return features; + if (!peer_has_vnet_hdr(n) || !peer_has_ufo(n)) { + features &= ~(0x1 << VIRTIO_NET_F_GUEST_UFO); + features &= ~(0x1 << VIRTIO_NET_F_HOST_UFO); + } + + if (!n->nic->nc.peer || + n->nic->nc.peer->info->type != NET_CLIENT_TYPE_TAP) { + return features; + } + if (!tap_get_vhost_net(n->nic->nc.peer)) { + return features; + } + return vhost_net_get_features(tap_get_vhost_net(n->nic->nc.peer), features); } static uint32_t virtio_net_bad_features(VirtIODevice *vdev) @@ -192,7 +269,7 @@ features |= (1 << VIRTIO_NET_F_HOST_TSO6); features |= (1 << VIRTIO_NET_F_HOST_ECN); - return features & virtio_net_get_features(vdev); + return features; } static void virtio_net_set_features(VirtIODevice *vdev, uint32_t features) @@ -209,6 +286,14 @@ (features >> VIRTIO_NET_F_GUEST_ECN) & 1, (features >> VIRTIO_NET_F_GUEST_UFO) & 1); } + if (!n->nic->nc.peer || + n->nic->nc.peer->info->type != NET_CLIENT_TYPE_TAP) { + return; + } + if (!tap_get_vhost_net(n->nic->nc.peer)) { + return; + } + vhost_net_ack_features(tap_get_vhost_net(n->nic->nc.peer), features); } static int virtio_net_handle_rx_mode(VirtIONet *n, uint8_t cmd, @@ -217,7 +302,7 @@ uint8_t on; if (elem->out_num != 2 || elem->out_sg[1].iov_len != sizeof(on)) { - fprintf(stderr, "virtio-net ctrl invalid rx mode command\n"); + error_report("virtio-net ctrl invalid rx mode command"); exit(1); } @@ -257,7 +342,7 @@ n->mac_table.multi_overflow = 0; memset(n->mac_table.macs, 0, MAC_TABLE_ENTRIES * ETH_ALEN); - mac_data.entries = ldl_le_p(elem->out_sg[1].iov_base); + mac_data.entries = ldl_p(elem->out_sg[1].iov_base); if (sizeof(mac_data.entries) + (mac_data.entries * ETH_ALEN) > elem->out_sg[1].iov_len) @@ -273,7 +358,7 @@ n->mac_table.first_multi = n->mac_table.in_use; - mac_data.entries = ldl_le_p(elem->out_sg[2].iov_base); + mac_data.entries = ldl_p(elem->out_sg[2].iov_base); if (sizeof(mac_data.entries) + (mac_data.entries * ETH_ALEN) > elem->out_sg[2].iov_len) @@ -299,11 +384,11 @@ uint16_t vid; if (elem->out_num != 2 || elem->out_sg[1].iov_len != sizeof(vid)) { - fprintf(stderr, "virtio-net ctrl invalid vlan command\n"); + error_report("virtio-net ctrl invalid vlan command"); return VIRTIO_NET_ERR; } - vid = lduw_le_p(elem->out_sg[1].iov_base); + vid = lduw_p(elem->out_sg[1].iov_base); if (vid >= MAX_VLAN) return VIRTIO_NET_ERR; @@ -327,13 +412,13 @@ while (virtqueue_pop(vq, &elem)) { if ((elem.in_num < 1) || (elem.out_num < 1)) { - fprintf(stderr, "virtio-net ctrl missing headers\n"); + error_report("virtio-net ctrl missing headers"); exit(1); } if (elem.out_sg[0].iov_len < sizeof(ctrl) || elem.in_sg[elem.in_num - 1].iov_len < sizeof(status)) { - fprintf(stderr, "virtio-net ctrl header not in correct element\n"); + error_report("virtio-net ctrl header not in correct element"); exit(1); } @@ -370,6 +455,9 @@ static int virtio_net_can_receive(VLANClientState *nc) { VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque; + if (!n->vdev.vm_running) { + return 0; + } if (!virtio_queue_ready(n->rx_vq) || !(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK)) @@ -427,21 +515,6 @@ } } -static int iov_fill(struct iovec *iov, int iovcnt, const void *buf, int count) -{ - int offset, i; - - offset = i = 0; - while (offset < count && i < iovcnt) { - int len = MIN(iov[i].iov_len, count - offset); - memcpy(iov[i].iov_base, buf + offset, len); - offset += len; - i++; - } - - return offset; -} - static int receive_header(VirtIONet *n, struct iovec *iov, int iovcnt, const void *buf, size_t size, size_t hdr_len) { @@ -523,21 +596,23 @@ { VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque; struct virtio_net_hdr_mrg_rxbuf *mhdr = NULL; - size_t hdr_len, offset, i; + size_t guest_hdr_len, offset, i, host_hdr_len; if (!virtio_net_can_receive(&n->nic->nc)) return -1; - if (!virtio_net_has_buffers(n, size)) + /* hdr_len refers to the header we supply to the guest */ + guest_hdr_len = n->mergeable_rx_bufs ? + sizeof(struct virtio_net_hdr_mrg_rxbuf) : sizeof(struct virtio_net_hdr); + + + host_hdr_len = n->has_vnet_hdr ? sizeof(struct virtio_net_hdr) : 0; + if (!virtio_net_has_buffers(n, size + guest_hdr_len - host_hdr_len)) return 0; if (!receive_filter(n, buf, size)) return size; - /* hdr_len refers to the header we supply to the guest */ - hdr_len = n->mergeable_rx_bufs ? - sizeof(struct virtio_net_hdr_mrg_rxbuf) : sizeof(struct virtio_net_hdr); - offset = i = 0; while (offset < size) { @@ -545,23 +620,26 @@ int len, total; struct iovec sg[VIRTQUEUE_MAX_SIZE]; - len = total = 0; + total = 0; - if ((i != 0 && !n->mergeable_rx_bufs) || - virtqueue_pop(n->rx_vq, &elem) == 0) { + if (virtqueue_pop(n->rx_vq, &elem) == 0) { if (i == 0) return -1; - fprintf(stderr, "virtio-net truncating packet\n"); + error_report("virtio-net unexpected empty queue: " + "i %zd mergeable %d offset %zd, size %zd, " + "guest hdr len %zd, host hdr len %zd guest features 0x%x", + i, n->mergeable_rx_bufs, offset, size, + guest_hdr_len, host_hdr_len, n->vdev.guest_features); exit(1); } if (elem.in_num < 1) { - fprintf(stderr, "virtio-net receive queue contains no in buffers\n"); + error_report("virtio-net receive queue contains no in buffers"); exit(1); } - if (!n->mergeable_rx_bufs && elem.in_sg[0].iov_len != hdr_len) { - fprintf(stderr, "virtio-net header not in first element\n"); + if (!n->mergeable_rx_bufs && elem.in_sg[0].iov_len != guest_hdr_len) { + error_report("virtio-net header not in first element"); exit(1); } @@ -572,23 +650,36 @@ mhdr = (struct virtio_net_hdr_mrg_rxbuf *)sg[0].iov_base; offset += receive_header(n, sg, elem.in_num, - buf + offset, size - offset, hdr_len); - total += hdr_len; + buf + offset, size - offset, guest_hdr_len); + total += guest_hdr_len; } /* copy in packet. ugh */ - len = iov_fill(sg, elem.in_num, - buf + offset, size - offset); + len = iov_from_buf(sg, elem.in_num, + buf + offset, size - offset); total += len; + offset += len; + /* If buffers can't be merged, at this point we + * must have consumed the complete packet. + * Otherwise, drop it. */ + if (!n->mergeable_rx_bufs && offset < size) { +#if 0 + error_report("virtio-net truncated non-mergeable packet: " + "i %zd mergeable %d offset %zd, size %zd, " + "guest hdr len %zd, host hdr len %zd", + i, n->mergeable_rx_bufs, + offset, size, guest_hdr_len, host_hdr_len); +#endif + return size; + } /* signal other side */ virtqueue_fill(n->rx_vq, &elem, total, i++); - - offset += len; } - if (mhdr) - mhdr->num_buffers = i; + if (mhdr) { + stw_p(&mhdr->num_buffers, i); + } virtqueue_flush(n->rx_vq, i); virtio_notify(&n->vdev, n->rx_vq); @@ -596,7 +687,7 @@ return size; } -static void virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq); +static int32_t virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq); static void virtio_net_tx_complete(VLANClientState *nc, ssize_t len) { @@ -612,16 +703,19 @@ } /* TX */ -static void virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq) +static int32_t virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq) { VirtQueueElement elem; + int32_t num_packets = 0; + if (!(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK)) { + return num_packets; + } - if (!(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK)) - return; + assert(n->vdev.vm_running); if (n->async_tx.elem.out_num) { virtio_queue_set_notification(n->tx_vq, 0); - return; + return num_packets; } while (virtqueue_pop(vq, &elem)) { @@ -636,7 +730,7 @@ sizeof(struct virtio_net_hdr); if (out_num < 1 || out_sg->iov_len != hdr_len) { - fprintf(stderr, "virtio-net header not in first element\n"); + error_report("virtio-net header not in first element"); exit(1); } @@ -658,38 +752,66 @@ virtio_queue_set_notification(n->tx_vq, 0); n->async_tx.elem = elem; n->async_tx.len = len; - return; + return -EBUSY; } len += ret; virtqueue_push(vq, &elem, len); virtio_notify(&n->vdev, vq); + + if (++num_packets >= n->tx_burst) { + break; + } } + return num_packets; } -static void virtio_net_handle_tx(VirtIODevice *vdev, VirtQueue *vq) +static void virtio_net_handle_tx_timer(VirtIODevice *vdev, VirtQueue *vq) { VirtIONet *n = to_virtio_net(vdev); - if (n->tx_timer_active) { + /* This happens when device was stopped but VCPU wasn't. */ + if (!n->vdev.vm_running) { + n->tx_waiting = 1; + return; + } + + if (n->tx_waiting) { virtio_queue_set_notification(vq, 1); qemu_del_timer(n->tx_timer); - n->tx_timer_active = 0; + n->tx_waiting = 0; virtio_net_flush_tx(n, vq); } else { qemu_mod_timer(n->tx_timer, - qemu_get_clock(vm_clock) + TX_TIMER_INTERVAL); - n->tx_timer_active = 1; + qemu_get_clock(vm_clock) + n->tx_timeout); + n->tx_waiting = 1; virtio_queue_set_notification(vq, 0); } } +static void virtio_net_handle_tx_bh(VirtIODevice *vdev, VirtQueue *vq) +{ + VirtIONet *n = to_virtio_net(vdev); + + if (unlikely(n->tx_waiting)) { + return; + } + n->tx_waiting = 1; + /* This happens when device was stopped but VCPU wasn't. */ + if (!n->vdev.vm_running) { + return; + } + virtio_queue_set_notification(vq, 0); + qemu_bh_schedule(n->tx_bh); +} + static void virtio_net_tx_timer(void *opaque) { VirtIONet *n = opaque; + assert(n->vdev.vm_running); - n->tx_timer_active = 0; + n->tx_waiting = 0; /* Just in case the driver is not ready on more */ if (!(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK)) @@ -699,14 +821,54 @@ virtio_net_flush_tx(n, n->tx_vq); } +static void virtio_net_tx_bh(void *opaque) +{ + VirtIONet *n = opaque; + int32_t ret; + + assert(n->vdev.vm_running); + + n->tx_waiting = 0; + + /* Just in case the driver is not ready on more */ + if (unlikely(!(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK))) + return; + + ret = virtio_net_flush_tx(n, n->tx_vq); + if (ret == -EBUSY) { + return; /* Notification re-enable handled by tx_complete */ + } + + /* If we flush a full burst of packets, assume there are + * more coming and immediately reschedule */ + if (ret >= n->tx_burst) { + qemu_bh_schedule(n->tx_bh); + n->tx_waiting = 1; + return; + } + + /* If less than a full burst, re-enable notification and flush + * anything that may have come in while we weren't looking. If + * we find something, assume the guest is still active and reschedule */ + virtio_queue_set_notification(n->tx_vq, 1); + if (virtio_net_flush_tx(n, n->tx_vq) > 0) { + virtio_queue_set_notification(n->tx_vq, 0); + qemu_bh_schedule(n->tx_bh); + n->tx_waiting = 1; + } +} + static void virtio_net_save(QEMUFile *f, void *opaque) { VirtIONet *n = opaque; + /* At this point, backend must be stopped, otherwise + * it might keep writing to memory. */ + assert(!n->vhost_started); virtio_save(&n->vdev, f); qemu_put_buffer(f, n->mac, ETH_ALEN); - qemu_put_be32(f, n->tx_timer_active); + qemu_put_be32(f, n->tx_waiting); qemu_put_be32(f, n->mergeable_rx_bufs); qemu_put_be16(f, n->status); qemu_put_byte(f, n->promisc); @@ -735,7 +897,7 @@ virtio_load(&n->vdev, f); qemu_get_buffer(f, n->mac, ETH_ALEN); - n->tx_timer_active = qemu_get_be32(f); + n->tx_waiting = qemu_get_be32(f); n->mergeable_rx_bufs = qemu_get_be32(f); if (version_id >= 3) @@ -769,18 +931,18 @@ if (version_id >= 7) { if (qemu_get_be32(f) && !peer_has_vnet_hdr(n)) { - qemu_error("virtio-net: saved image requires vnet_hdr=on\n"); + error_report("virtio-net: saved image requires vnet_hdr=on"); return -1; } if (n->has_vnet_hdr) { tap_using_vnet_hdr(n->nic->nc.peer, 1); tap_set_offload(n->nic->nc.peer, - (n->vdev.features >> VIRTIO_NET_F_GUEST_CSUM) & 1, - (n->vdev.features >> VIRTIO_NET_F_GUEST_TSO4) & 1, - (n->vdev.features >> VIRTIO_NET_F_GUEST_TSO6) & 1, - (n->vdev.features >> VIRTIO_NET_F_GUEST_ECN) & 1, - (n->vdev.features >> VIRTIO_NET_F_GUEST_UFO) & 1); + (n->vdev.guest_features >> VIRTIO_NET_F_GUEST_CSUM) & 1, + (n->vdev.guest_features >> VIRTIO_NET_F_GUEST_TSO4) & 1, + (n->vdev.guest_features >> VIRTIO_NET_F_GUEST_TSO6) & 1, + (n->vdev.guest_features >> VIRTIO_NET_F_GUEST_ECN) & 1, + (n->vdev.guest_features >> VIRTIO_NET_F_GUEST_UFO) & 1); } } @@ -798,7 +960,7 @@ if (version_id >= 11) { if (qemu_get_byte(f) && !peer_has_ufo(n)) { - qemu_error("virtio-net: saved image requires TUN_F_UFO support\n"); + error_report("virtio-net: saved image requires TUN_F_UFO support"); return -1; } } @@ -810,12 +972,6 @@ } } n->mac_table.first_multi = i; - - if (n->tx_timer_active) { - qemu_mod_timer(n->tx_timer, - qemu_get_clock(vm_clock) + TX_TIMER_INTERVAL); - } - return 0; } @@ -835,10 +991,10 @@ .link_status_changed = virtio_net_set_link_status, }; -VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf) +VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf, + virtio_net_conf *net) { VirtIONet *n; - static int virtio_net_id; n = (VirtIONet *)virtio_common_init("virtio-net", VIRTIO_ID_NET, sizeof(struct virtio_net_config), @@ -850,8 +1006,24 @@ n->vdev.set_features = virtio_net_set_features; n->vdev.bad_features = virtio_net_bad_features; n->vdev.reset = virtio_net_reset; + n->vdev.set_status = virtio_net_set_status; n->rx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_rx); - n->tx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_tx); + + if (net->tx && strcmp(net->tx, "timer") && strcmp(net->tx, "bh")) { + error_report("virtio-net: " + "Unknown option tx=%s, valid options: \"timer\" \"bh\"", + net->tx); + error_report("Defaulting to \"bh\""); + } + + if (net->tx && !strcmp(net->tx, "timer")) { + n->tx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_tx_timer); + n->tx_timer = qemu_new_timer(vm_clock, virtio_net_tx_timer, n); + n->tx_timeout = net->txtimer; + } else { + n->tx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_tx_bh); + n->tx_bh = qemu_bh_new(virtio_net_tx_bh, n); + } n->ctrl_vq = virtio_add_queue(&n->vdev, 64, virtio_net_handle_ctrl); qemu_macaddr_default_if_unset(&conf->macaddr); memcpy(&n->mac[0], &conf->macaddr, sizeof(n->mac)); @@ -861,8 +1033,8 @@ qemu_format_nic_info_str(&n->nic->nc, conf->macaddr.a); - n->tx_timer = qemu_new_timer(vm_clock, virtio_net_tx_timer, n); - n->tx_timer_active = 0; + n->tx_waiting = 0; + n->tx_burst = net->txburst; n->mergeable_rx_bufs = 0; n->promisc = 1; /* for compatibility */ @@ -870,9 +1042,12 @@ n->vlans = qemu_mallocz(MAX_VLAN >> 3); - register_savevm("virtio-net", virtio_net_id++, VIRTIO_NET_VM_VERSION, + n->qdev = dev; + register_savevm(dev, "virtio-net", -1, VIRTIO_NET_VM_VERSION, virtio_net_save, virtio_net_load, n); + add_boot_device_path(conf->bootindex, dev, "/ethernet-phy@0"); + return &n->vdev; } @@ -880,15 +1055,22 @@ { VirtIONet *n = DO_UPCAST(VirtIONet, vdev, vdev); + /* This will stop vhost backend if appropriate. */ + virtio_net_set_status(vdev, 0); + qemu_purge_queued_packets(&n->nic->nc); - unregister_savevm("virtio-net", n); + unregister_savevm(n->qdev, "virtio-net", n); qemu_free(n->mac_table.macs); qemu_free(n->vlans); - qemu_del_timer(n->tx_timer); - qemu_free_timer(n->tx_timer); + if (n->tx_timer) { + qemu_del_timer(n->tx_timer); + qemu_free_timer(n->tx_timer); + } else { + qemu_bh_delete(n->tx_bh); + } virtio_cleanup(&n->vdev); qemu_del_vlan_client(&n->nic->nc); diff -Nru qemu-kvm-0.12.5+noroms/hw/virtio-net.h qemu-kvm-0.14.1/hw/virtio-net.h --- qemu-kvm-0.12.5+noroms/hw/virtio-net.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/virtio-net.h 2011-05-11 13:29:46.000000000 +0000 @@ -49,13 +49,27 @@ #define TX_TIMER_INTERVAL 150000 /* 150 us */ +/* Limit the number of packets that can be sent via a single flush + * of the TX queue. This gives us a guaranteed exit condition and + * ensures fairness in the io path. 256 conveniently matches the + * length of the TX queue and shows a good balance of performance + * and latency. */ +#define TX_BURST 256 + +typedef struct virtio_net_conf +{ + uint32_t txtimer; + int32_t txburst; + char *tx; +} virtio_net_conf; + /* Maximum packet size we can receive from tap device: header + 64k */ #define VIRTIO_NET_MAX_BUFSIZE (sizeof(struct virtio_net_hdr) + (64 << 10)) struct virtio_net_config { - /* The config defining mac address (6 bytes) */ - uint8_t mac[6]; + /* The config defining mac address ($ETH_ALEN bytes) */ + uint8_t mac[ETH_ALEN]; /* See VIRTIO_NET_F_STATUS and VIRTIO_NET_S_* above */ uint16_t status; } __attribute__((packed)); @@ -153,4 +167,23 @@ #define VIRTIO_NET_CTRL_VLAN_ADD 0 #define VIRTIO_NET_CTRL_VLAN_DEL 1 +#define DEFINE_VIRTIO_NET_FEATURES(_state, _field) \ + DEFINE_VIRTIO_COMMON_FEATURES(_state, _field), \ + DEFINE_PROP_BIT("csum", _state, _field, VIRTIO_NET_F_CSUM, true), \ + DEFINE_PROP_BIT("guest_csum", _state, _field, VIRTIO_NET_F_GUEST_CSUM, true), \ + DEFINE_PROP_BIT("gso", _state, _field, VIRTIO_NET_F_GSO, true), \ + DEFINE_PROP_BIT("guest_tso4", _state, _field, VIRTIO_NET_F_GUEST_TSO4, true), \ + DEFINE_PROP_BIT("guest_tso6", _state, _field, VIRTIO_NET_F_GUEST_TSO6, true), \ + DEFINE_PROP_BIT("guest_ecn", _state, _field, VIRTIO_NET_F_GUEST_ECN, true), \ + DEFINE_PROP_BIT("guest_ufo", _state, _field, VIRTIO_NET_F_GUEST_UFO, true), \ + DEFINE_PROP_BIT("host_tso4", _state, _field, VIRTIO_NET_F_HOST_TSO4, true), \ + DEFINE_PROP_BIT("host_tso6", _state, _field, VIRTIO_NET_F_HOST_TSO6, true), \ + DEFINE_PROP_BIT("host_ecn", _state, _field, VIRTIO_NET_F_HOST_ECN, true), \ + DEFINE_PROP_BIT("host_ufo", _state, _field, VIRTIO_NET_F_HOST_UFO, true), \ + DEFINE_PROP_BIT("mrg_rxbuf", _state, _field, VIRTIO_NET_F_MRG_RXBUF, true), \ + DEFINE_PROP_BIT("status", _state, _field, VIRTIO_NET_F_STATUS, true), \ + DEFINE_PROP_BIT("ctrl_vq", _state, _field, VIRTIO_NET_F_CTRL_VQ, true), \ + DEFINE_PROP_BIT("ctrl_rx", _state, _field, VIRTIO_NET_F_CTRL_RX, true), \ + DEFINE_PROP_BIT("ctrl_vlan", _state, _field, VIRTIO_NET_F_CTRL_VLAN, true), \ + DEFINE_PROP_BIT("ctrl_rx_extra", _state, _field, VIRTIO_NET_F_CTRL_RX_EXTRA, true) #endif diff -Nru qemu-kvm-0.12.5+noroms/hw/virtio-pci.c qemu-kvm-0.14.1/hw/virtio-pci.c --- qemu-kvm-0.12.5+noroms/hw/virtio-pci.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/virtio-pci.c 2011-05-11 13:29:46.000000000 +0000 @@ -16,11 +16,15 @@ #include #include "virtio.h" +#include "virtio-blk.h" +#include "virtio-net.h" #include "pci.h" -#include "sysemu.h" +#include "qemu-error.h" #include "msix.h" #include "net.h" #include "loader.h" +#include "kvm.h" +#include "blockdev.h" /* from Linux's linux/virtio_pci.h */ @@ -76,6 +80,14 @@ * 12 is historical, and due to x86 page size. */ #define VIRTIO_PCI_QUEUE_ADDR_SHIFT 12 +/* Flags track per-device state like workarounds for quirks in older guests. */ +#define VIRTIO_PCI_FLAG_BUS_MASTER_BUG (1 << 0) + +/* Performance improves when virtqueue kick processing is decoupled from the + * vcpu thread using ioeventfd for some devices. */ +#define VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT 1 +#define VIRTIO_PCI_FLAG_USE_IOEVENTFD (1 << VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT) + /* QEMU doesn't strictly need write barriers since everything runs in * lock-step. We'll leave the calls to wmb() in though to make it obvious for * KVM or if kqemu gets SMP support. @@ -87,11 +99,21 @@ typedef struct { PCIDevice pci_dev; VirtIODevice *vdev; + uint32_t flags; uint32_t addr; uint32_t class_code; uint32_t nvectors; - DriveInfo *dinfo; + BlockConf block; NICConf nic; + uint32_t host_features; +#ifdef CONFIG_LINUX + V9fsConf fsconf; +#endif + /* Max. number of ports we can have for a the virtio-serial device */ + uint32_t max_virtserial_ports; + virtio_net_conf net; + bool ioeventfd_disabled; + bool ioeventfd_started; } VirtIOPCIProxy; /* virtio device */ @@ -157,11 +179,139 @@ return 0; } +static int virtio_pci_set_host_notifier_internal(VirtIOPCIProxy *proxy, + int n, bool assign) +{ + VirtQueue *vq = virtio_get_queue(proxy->vdev, n); + EventNotifier *notifier = virtio_queue_get_host_notifier(vq); + int r; + if (assign) { + r = event_notifier_init(notifier, 1); + if (r < 0) { + error_report("%s: unable to init event notifier: %d", + __func__, r); + return r; + } + r = kvm_set_ioeventfd_pio_word(event_notifier_get_fd(notifier), + proxy->addr + VIRTIO_PCI_QUEUE_NOTIFY, + n, assign); + if (r < 0) { + error_report("%s: unable to map ioeventfd: %d", + __func__, r); + event_notifier_cleanup(notifier); + } + } else { + r = kvm_set_ioeventfd_pio_word(event_notifier_get_fd(notifier), + proxy->addr + VIRTIO_PCI_QUEUE_NOTIFY, + n, assign); + if (r < 0) { + error_report("%s: unable to unmap ioeventfd: %d", + __func__, r); + return r; + } + + /* Handle the race condition where the guest kicked and we deassigned + * before we got around to handling the kick. + */ + if (event_notifier_test_and_clear(notifier)) { + virtio_queue_notify_vq(vq); + } + + event_notifier_cleanup(notifier); + } + return r; +} + +static void virtio_pci_host_notifier_read(void *opaque) +{ + VirtQueue *vq = opaque; + EventNotifier *n = virtio_queue_get_host_notifier(vq); + if (event_notifier_test_and_clear(n)) { + virtio_queue_notify_vq(vq); + } +} + +static void virtio_pci_set_host_notifier_fd_handler(VirtIOPCIProxy *proxy, + int n, bool assign) +{ + VirtQueue *vq = virtio_get_queue(proxy->vdev, n); + EventNotifier *notifier = virtio_queue_get_host_notifier(vq); + if (assign) { + qemu_set_fd_handler(event_notifier_get_fd(notifier), + virtio_pci_host_notifier_read, NULL, vq); + } else { + qemu_set_fd_handler(event_notifier_get_fd(notifier), + NULL, NULL, NULL); + } +} + +static void virtio_pci_start_ioeventfd(VirtIOPCIProxy *proxy) +{ + int n, r; + + if (!(proxy->flags & VIRTIO_PCI_FLAG_USE_IOEVENTFD) || + proxy->ioeventfd_disabled || + proxy->ioeventfd_started) { + return; + } + + for (n = 0; n < VIRTIO_PCI_QUEUE_MAX; n++) { + if (!virtio_queue_get_num(proxy->vdev, n)) { + continue; + } + + r = virtio_pci_set_host_notifier_internal(proxy, n, true); + if (r < 0) { + goto assign_error; + } + + virtio_pci_set_host_notifier_fd_handler(proxy, n, true); + } + proxy->ioeventfd_started = true; + return; + +assign_error: + while (--n >= 0) { + if (!virtio_queue_get_num(proxy->vdev, n)) { + continue; + } + + virtio_pci_set_host_notifier_fd_handler(proxy, n, false); + r = virtio_pci_set_host_notifier_internal(proxy, n, false); + assert(r >= 0); + } + proxy->ioeventfd_started = false; + error_report("%s: failed. Fallback to a userspace (slower).", __func__); +} + +static void virtio_pci_stop_ioeventfd(VirtIOPCIProxy *proxy) +{ + int r; + int n; + + if (!proxy->ioeventfd_started) { + return; + } + + for (n = 0; n < VIRTIO_PCI_QUEUE_MAX; n++) { + if (!virtio_queue_get_num(proxy->vdev, n)) { + continue; + } + + virtio_pci_set_host_notifier_fd_handler(proxy, n, false); + r = virtio_pci_set_host_notifier_internal(proxy, n, false); + assert(r >= 0); + } + proxy->ioeventfd_started = false; +} + static void virtio_pci_reset(DeviceState *d) { VirtIOPCIProxy *proxy = container_of(d, VirtIOPCIProxy, pci_dev.qdev); + virtio_pci_stop_ioeventfd(proxy); virtio_reset(proxy->vdev); msix_reset(&proxy->pci_dev); + proxy->flags &= ~VIRTIO_PCI_FLAG_BUS_MASTER_BUG; } static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val) @@ -175,17 +325,18 @@ /* Guest does not negotiate properly? We have to assume nothing. */ if (val & (1 << VIRTIO_F_BAD_FEATURE)) { if (vdev->bad_features) - val = vdev->bad_features(vdev); + val = proxy->host_features & vdev->bad_features(vdev); else val = 0; } if (vdev->set_features) vdev->set_features(vdev, val); - vdev->features = val; + vdev->guest_features = val; break; case VIRTIO_PCI_QUEUE_PFN: pa = (target_phys_addr_t)val << VIRTIO_PCI_QUEUE_ADDR_SHIFT; if (pa == 0) { + virtio_pci_stop_ioeventfd(proxy); virtio_reset(proxy->vdev); msix_unuse_all_vectors(&proxy->pci_dev); } @@ -200,11 +351,28 @@ virtio_queue_notify(vdev, val); break; case VIRTIO_PCI_STATUS: - vdev->status = val & 0xFF; + if (!(val & VIRTIO_CONFIG_S_DRIVER_OK)) { + virtio_pci_stop_ioeventfd(proxy); + } + + virtio_set_status(vdev, val & 0xFF); + + if (val & VIRTIO_CONFIG_S_DRIVER_OK) { + virtio_pci_start_ioeventfd(proxy); + } + if (vdev->status == 0) { virtio_reset(proxy->vdev); msix_unuse_all_vectors(&proxy->pci_dev); } + + /* Linux before 2.6.34 sets the device as OK without enabling + the PCI device bus master bit. In this case we need to disable + some safety checks. */ + if ((val & VIRTIO_CONFIG_S_DRIVER_OK) && + !(proxy->pci_dev.config[PCI_COMMAND] & PCI_COMMAND_MASTER)) { + proxy->flags |= VIRTIO_PCI_FLAG_BUS_MASTER_BUG; + } break; case VIRTIO_MSI_CONFIG_VECTOR: msix_vector_unuse(&proxy->pci_dev, vdev->config_vector); @@ -222,8 +390,8 @@ virtio_queue_set_vector(vdev, vdev->queue_sel, val); break; default: - fprintf(stderr, "%s: unexpected address 0x%x value 0x%x\n", - __func__, addr, val); + error_report("%s: unexpected address 0x%x value 0x%x", + __func__, addr, val); break; } } @@ -235,11 +403,10 @@ switch (addr) { case VIRTIO_PCI_HOST_FEATURES: - ret = vdev->get_features(vdev); - ret |= vdev->binding->get_features(proxy); + ret = proxy->host_features; break; case VIRTIO_PCI_GUEST_FEATURES: - ret = vdev->features; + ret = vdev->guest_features; break; case VIRTIO_PCI_QUEUE_PFN: ret = virtio_queue_get_addr(vdev, vdev->queue_sel) @@ -372,7 +539,11 @@ if (PCI_COMMAND == address) { if (!(val & PCI_COMMAND_MASTER)) { - proxy->vdev->status &= ~VIRTIO_CONFIG_S_DRIVER_OK; + if (!(proxy->flags & VIRTIO_PCI_FLAG_BUS_MASTER_BUG)) { + virtio_pci_stop_ioeventfd(proxy); + virtio_set_status(proxy->vdev, + proxy->vdev->status & ~VIRTIO_CONFIG_S_DRIVER_OK); + } } } @@ -382,11 +553,191 @@ static unsigned virtio_pci_get_features(void *opaque) { - unsigned ret = 0; - ret |= (1 << VIRTIO_F_NOTIFY_ON_EMPTY); - ret |= (1 << VIRTIO_RING_F_INDIRECT_DESC); - ret |= (1 << VIRTIO_F_BAD_FEATURE); - return ret; + VirtIOPCIProxy *proxy = opaque; + return proxy->host_features; +} + +static void virtio_pci_guest_notifier_read(void *opaque) +{ + VirtQueue *vq = opaque; + EventNotifier *n = virtio_queue_get_guest_notifier(vq); + if (event_notifier_test_and_clear(n)) { + virtio_irq(vq); + } +} + +static int virtio_pci_mask_vq(PCIDevice *dev, unsigned vector, + VirtQueue *vq, int masked) +{ +#ifdef CONFIG_KVM + EventNotifier *notifier = virtio_queue_get_guest_notifier(vq); + int r = kvm_set_irqfd(dev->msix_irq_entries[vector].gsi, + event_notifier_get_fd(notifier), + !masked); + if (r < 0) { + return (r == -ENOSYS) ? 0 : r; + } + if (masked) { + qemu_set_fd_handler(event_notifier_get_fd(notifier), + virtio_pci_guest_notifier_read, NULL, vq); + } else { + qemu_set_fd_handler(event_notifier_get_fd(notifier), + NULL, NULL, NULL); + } + return 0; +#else + return -ENOSYS; +#endif +} + +static int virtio_pci_mask_notifier(PCIDevice *dev, unsigned vector, + int masked) +{ + VirtIOPCIProxy *proxy = container_of(dev, VirtIOPCIProxy, pci_dev); + VirtIODevice *vdev = proxy->vdev; + int r, n; + + for (n = 0; n < VIRTIO_PCI_QUEUE_MAX; n++) { + if (!virtio_queue_get_num(vdev, n)) { + break; + } + if (virtio_queue_vector(vdev, n) != vector) { + continue; + } + r = virtio_pci_mask_vq(dev, vector, virtio_get_queue(vdev, n), masked); + if (r < 0) { + goto undo; + } + } + return 0; +undo: + while (--n >= 0) { + if (virtio_queue_vector(vdev, n) != vector) { + continue; + } + virtio_pci_mask_vq(dev, vector, virtio_get_queue(vdev, n), !masked); + } + return r; +} + + +static int virtio_pci_set_guest_notifier(void *opaque, int n, bool assign) +{ + VirtIOPCIProxy *proxy = opaque; + VirtQueue *vq = virtio_get_queue(proxy->vdev, n); + EventNotifier *notifier = virtio_queue_get_guest_notifier(vq); + + if (assign) { + int r = event_notifier_init(notifier, 0); + if (r < 0) { + return r; + } + qemu_set_fd_handler(event_notifier_get_fd(notifier), + virtio_pci_guest_notifier_read, NULL, vq); + } else { + qemu_set_fd_handler(event_notifier_get_fd(notifier), + NULL, NULL, NULL); + /* Test and clear notifier before closing it, + * in case poll callback didn't have time to run. */ + virtio_pci_guest_notifier_read(vq); + event_notifier_cleanup(notifier); + } + + return 0; +} + +static bool virtio_pci_query_guest_notifiers(void *opaque) +{ + VirtIOPCIProxy *proxy = opaque; + return msix_enabled(&proxy->pci_dev); +} + +static int virtio_pci_set_guest_notifiers(void *opaque, bool assign) +{ + VirtIOPCIProxy *proxy = opaque; + VirtIODevice *vdev = proxy->vdev; + int r, n; + + /* Must unset mask notifier while guest notifier + * is still assigned */ + if (!assign) { + r = msix_unset_mask_notifier(&proxy->pci_dev); + assert(r >= 0); + } + + for (n = 0; n < VIRTIO_PCI_QUEUE_MAX; n++) { + if (!virtio_queue_get_num(vdev, n)) { + break; + } + + r = virtio_pci_set_guest_notifier(opaque, n, assign); + if (r < 0) { + goto assign_error; + } + } + + /* Must set mask notifier after guest notifier + * has been assigned */ + if (assign) { + r = msix_set_mask_notifier(&proxy->pci_dev, + virtio_pci_mask_notifier); + if (r < 0) { + goto assign_error; + } + } + + return 0; + +assign_error: + /* We get here on assignment failure. Recover by undoing for VQs 0 .. n. */ + if (assign) { + msix_unset_mask_notifier(&proxy->pci_dev); + } + + while (--n >= 0) { + virtio_pci_set_guest_notifier(opaque, n, !assign); + } + + if (!assign) { + msix_set_mask_notifier(&proxy->pci_dev, + virtio_pci_mask_notifier); + } + return r; +} + +static int virtio_pci_set_host_notifier(void *opaque, int n, bool assign) +{ + VirtIOPCIProxy *proxy = opaque; + + /* Stop using ioeventfd for virtqueue kick if the device starts using host + * notifiers. This makes it easy to avoid stepping on each others' toes. + */ + proxy->ioeventfd_disabled = assign; + if (assign) { + virtio_pci_stop_ioeventfd(proxy); + } + /* We don't need to start here: it's not needed because backend + * currently only stops on status change away from ok, + * reset, vmstop and such. If we do add code to start here, + * need to check vmstate, device state etc. */ + return virtio_pci_set_host_notifier_internal(proxy, n, assign); +} + +static void virtio_pci_vmstate_change(void *opaque, bool running) +{ + VirtIOPCIProxy *proxy = opaque; + + if (running) { + /* Try to find out if the guest has bus master disabled, but is + in ready state. Then we have a buggy guest OS. */ + if ((proxy->vdev->status & VIRTIO_CONFIG_S_DRIVER_OK) && + !(proxy->pci_dev.config[PCI_COMMAND] & PCI_COMMAND_MASTER)) { + proxy->flags |= VIRTIO_PCI_FLAG_BUS_MASTER_BUG; + } + virtio_pci_start_ioeventfd(proxy); + } else { + virtio_pci_stop_ioeventfd(proxy); + } } static const VirtIOBindings virtio_pci_bindings = { @@ -396,6 +747,10 @@ .save_queue = virtio_pci_save_queue, .load_queue = virtio_pci_load_queue, .get_features = virtio_pci_get_features, + .query_guest_notifiers = virtio_pci_query_guest_notifiers, + .set_host_notifier = virtio_pci_set_host_notifier, + .set_guest_notifiers = virtio_pci_set_guest_notifiers, + .vmstate_change = virtio_pci_vmstate_change, }; static void virtio_init_pci(VirtIOPCIProxy *proxy, VirtIODevice *vdev, @@ -415,7 +770,6 @@ config[0x09] = pif; pci_config_set_class(config, class_code); - config[PCI_HEADER_TYPE] = PCI_HEADER_TYPE_NORMAL; config[0x2c] = vendor & 0xFF; config[0x2d] = (vendor >> 8) & 0xFF; @@ -441,7 +795,14 @@ pci_register_bar(&proxy->pci_dev, 0, size, PCI_BASE_ADDRESS_SPACE_IO, virtio_map); + if (!kvm_has_many_ioeventfds()) { + proxy->flags &= ~VIRTIO_PCI_FLAG_USE_IOEVENTFD; + } + virtio_bind_device(vdev, &virtio_pci_bindings, proxy); + proxy->host_features |= 0x1 << VIRTIO_F_NOTIFY_ON_EMPTY; + proxy->host_features |= 0x1 << VIRTIO_F_BAD_FEATURE; + proxy->host_features = vdev->get_features(vdev, proxy->host_features); } static int virtio_blk_init_pci(PCIDevice *pci_dev) @@ -453,11 +814,10 @@ proxy->class_code != PCI_CLASS_STORAGE_OTHER) proxy->class_code = PCI_CLASS_STORAGE_SCSI; - if (!proxy->dinfo) { - qemu_error("virtio-blk-pci: drive property not set\n"); + vdev = virtio_blk_init(&pci_dev->qdev, &proxy->block); + if (!vdev) { return -1; } - vdev = virtio_blk_init(&pci_dev->qdev, proxy->dinfo); vdev->nvectors = proxy->nvectors; virtio_init_pci(proxy, vdev, PCI_VENDOR_ID_REDHAT_QUMRANET, @@ -477,11 +837,13 @@ { VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); - drive_uninit(proxy->dinfo); + virtio_pci_stop_ioeventfd(proxy); + virtio_blk_exit(proxy->vdev); + blockdev_mark_auto_del(proxy->block.bs); return virtio_exit_pci(pci_dev); } -static int virtio_console_init_pci(PCIDevice *pci_dev) +static int virtio_serial_init_pci(PCIDevice *pci_dev) { VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); VirtIODevice *vdev; @@ -491,23 +853,35 @@ proxy->class_code != PCI_CLASS_OTHERS) /* qemu-kvm */ proxy->class_code = PCI_CLASS_COMMUNICATION_OTHER; - vdev = virtio_console_init(&pci_dev->qdev); + vdev = virtio_serial_init(&pci_dev->qdev, proxy->max_virtserial_ports); if (!vdev) { return -1; } + vdev->nvectors = proxy->nvectors == DEV_NVECTORS_UNSPECIFIED + ? proxy->max_virtserial_ports + 1 + : proxy->nvectors; virtio_init_pci(proxy, vdev, PCI_VENDOR_ID_REDHAT_QUMRANET, PCI_DEVICE_ID_VIRTIO_CONSOLE, proxy->class_code, 0x00); + proxy->nvectors = vdev->nvectors; return 0; } +static int virtio_serial_exit_pci(PCIDevice *pci_dev) +{ + VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); + + virtio_serial_exit(proxy->vdev); + return virtio_exit_pci(pci_dev); +} + static int virtio_net_init_pci(PCIDevice *pci_dev) { VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); VirtIODevice *vdev; - vdev = virtio_net_init(&pci_dev->qdev, &proxy->nic); + vdev = virtio_net_init(&pci_dev->qdev, &proxy->nic, &proxy->net); vdev->nvectors = proxy->nvectors; virtio_init_pci(proxy, vdev, @@ -525,6 +899,7 @@ { VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); + virtio_pci_stop_ioeventfd(proxy); virtio_net_exit(proxy->vdev); return virtio_exit_pci(pci_dev); } @@ -543,16 +918,39 @@ return 0; } +#ifdef CONFIG_VIRTFS +static int virtio_9p_init_pci(PCIDevice *pci_dev) +{ + VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); + VirtIODevice *vdev; + + vdev = virtio_9p_init(&pci_dev->qdev, &proxy->fsconf); + vdev->nvectors = proxy->nvectors; + virtio_init_pci(proxy, vdev, + PCI_VENDOR_ID_REDHAT_QUMRANET, + 0x1009, + 0x2, + 0x00); + /* make the actual value visible */ + proxy->nvectors = vdev->nvectors; + return 0; +} +#endif + static PCIDeviceInfo virtio_info[] = { { .qdev.name = "virtio-blk-pci", + .qdev.alias = "virtio-blk", .qdev.size = sizeof(VirtIOPCIProxy), .init = virtio_blk_init_pci, .exit = virtio_blk_exit_pci, .qdev.props = (Property[]) { DEFINE_PROP_HEX32("class", VirtIOPCIProxy, class_code, 0), - DEFINE_PROP_DRIVE("drive", VirtIOPCIProxy, dinfo), + DEFINE_BLOCK_PROPERTIES(VirtIOPCIProxy, block), + DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, + VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2), + DEFINE_VIRTIO_BLK_FEATURES(VirtIOPCIProxy, host_features), DEFINE_PROP_END_OF_LIST(), }, .qdev.reset = virtio_pci_reset, @@ -563,18 +961,32 @@ .exit = virtio_net_exit_pci, .romfile = "pxe-virtio.bin", .qdev.props = (Property[]) { + DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, + VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, false), DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 3), + DEFINE_VIRTIO_NET_FEATURES(VirtIOPCIProxy, host_features), DEFINE_NIC_PROPERTIES(VirtIOPCIProxy, nic), + DEFINE_PROP_UINT32("x-txtimer", VirtIOPCIProxy, + net.txtimer, TX_TIMER_INTERVAL), + DEFINE_PROP_INT32("x-txburst", VirtIOPCIProxy, + net.txburst, TX_BURST), + DEFINE_PROP_STRING("tx", VirtIOPCIProxy, net.tx), DEFINE_PROP_END_OF_LIST(), }, .qdev.reset = virtio_pci_reset, },{ - .qdev.name = "virtio-console-pci", + .qdev.name = "virtio-serial-pci", + .qdev.alias = "virtio-serial", .qdev.size = sizeof(VirtIOPCIProxy), - .init = virtio_console_init_pci, - .exit = virtio_exit_pci, + .init = virtio_serial_init_pci, + .exit = virtio_serial_exit_pci, .qdev.props = (Property[]) { + DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, + DEV_NVECTORS_UNSPECIFIED), DEFINE_PROP_HEX32("class", VirtIOPCIProxy, class_code, 0), + DEFINE_VIRTIO_COMMON_FEATURES(VirtIOPCIProxy, host_features), + DEFINE_PROP_UINT32("max_ports", VirtIOPCIProxy, max_virtserial_ports, + 31), DEFINE_PROP_END_OF_LIST(), }, .qdev.reset = virtio_pci_reset, @@ -583,8 +995,25 @@ .qdev.size = sizeof(VirtIOPCIProxy), .init = virtio_balloon_init_pci, .exit = virtio_exit_pci, + .qdev.props = (Property[]) { + DEFINE_VIRTIO_COMMON_FEATURES(VirtIOPCIProxy, host_features), + DEFINE_PROP_END_OF_LIST(), + }, .qdev.reset = virtio_pci_reset, },{ +#ifdef CONFIG_VIRTFS + .qdev.name = "virtio-9p-pci", + .qdev.size = sizeof(VirtIOPCIProxy), + .init = virtio_9p_init_pci, + .qdev.props = (Property[]) { + DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2), + DEFINE_VIRTIO_COMMON_FEATURES(VirtIOPCIProxy, host_features), + DEFINE_PROP_STRING("mount_tag", VirtIOPCIProxy, fsconf.tag), + DEFINE_PROP_STRING("fsdev", VirtIOPCIProxy, fsconf.fsdev_id), + DEFINE_PROP_END_OF_LIST(), + }, + }, { +#endif /* end of list */ } }; diff -Nru qemu-kvm-0.12.5+noroms/hw/virtio-serial-bus.c qemu-kvm-0.14.1/hw/virtio-serial-bus.c --- qemu-kvm-0.12.5+noroms/hw/virtio-serial-bus.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/virtio-serial-bus.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,908 @@ +/* + * A bus for connecting virtio serial and console ports + * + * Copyright (C) 2009, 2010 Red Hat, Inc. + * + * Author(s): + * Amit Shah + * + * Some earlier parts are: + * Copyright IBM, Corp. 2008 + * authored by + * Christian Ehrhardt + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + */ + +#include "iov.h" +#include "monitor.h" +#include "qemu-queue.h" +#include "sysbus.h" +#include "virtio-serial.h" + +/* The virtio-serial bus on top of which the ports will ride as devices */ +struct VirtIOSerialBus { + BusState qbus; + + /* This is the parent device that provides the bus for ports. */ + VirtIOSerial *vser; + + /* The maximum number of ports that can ride on top of this bus */ + uint32_t max_nr_ports; +}; + +struct VirtIOSerial { + VirtIODevice vdev; + + VirtQueue *c_ivq, *c_ovq; + /* Arrays of ivqs and ovqs: one per port */ + VirtQueue **ivqs, **ovqs; + + VirtIOSerialBus *bus; + + DeviceState *qdev; + + QTAILQ_HEAD(, VirtIOSerialPort) ports; + + /* bitmap for identifying active ports */ + uint32_t *ports_map; + + struct virtio_console_config config; +}; + +static VirtIOSerialPort *find_port_by_id(VirtIOSerial *vser, uint32_t id) +{ + VirtIOSerialPort *port; + + if (id == VIRTIO_CONSOLE_BAD_ID) { + return NULL; + } + + QTAILQ_FOREACH(port, &vser->ports, next) { + if (port->id == id) + return port; + } + return NULL; +} + +static VirtIOSerialPort *find_port_by_vq(VirtIOSerial *vser, VirtQueue *vq) +{ + VirtIOSerialPort *port; + + QTAILQ_FOREACH(port, &vser->ports, next) { + if (port->ivq == vq || port->ovq == vq) + return port; + } + return NULL; +} + +static bool use_multiport(VirtIOSerial *vser) +{ + return vser->vdev.guest_features & (1 << VIRTIO_CONSOLE_F_MULTIPORT); +} + +static size_t write_to_port(VirtIOSerialPort *port, + const uint8_t *buf, size_t size) +{ + VirtQueueElement elem; + VirtQueue *vq; + size_t offset; + + vq = port->ivq; + if (!virtio_queue_ready(vq)) { + return 0; + } + + offset = 0; + while (offset < size) { + size_t len; + + if (!virtqueue_pop(vq, &elem)) { + break; + } + + len = iov_from_buf(elem.in_sg, elem.in_num, + buf + offset, size - offset); + offset += len; + + virtqueue_push(vq, &elem, len); + } + + virtio_notify(&port->vser->vdev, vq); + return offset; +} + +static void discard_vq_data(VirtQueue *vq, VirtIODevice *vdev) +{ + VirtQueueElement elem; + + if (!virtio_queue_ready(vq)) { + return; + } + while (virtqueue_pop(vq, &elem)) { + virtqueue_push(vq, &elem, 0); + } + virtio_notify(vdev, vq); +} + +static void do_flush_queued_data(VirtIOSerialPort *port, VirtQueue *vq, + VirtIODevice *vdev) +{ + assert(port); + assert(virtio_queue_ready(vq)); + + while (!port->throttled) { + unsigned int i; + + /* Pop an elem only if we haven't left off a previous one mid-way */ + if (!port->elem.out_num) { + if (!virtqueue_pop(vq, &port->elem)) { + break; + } + port->iov_idx = 0; + port->iov_offset = 0; + } + + for (i = port->iov_idx; i < port->elem.out_num; i++) { + size_t buf_size; + ssize_t ret; + + buf_size = port->elem.out_sg[i].iov_len - port->iov_offset; + ret = port->info->have_data(port, + port->elem.out_sg[i].iov_base + + port->iov_offset, + buf_size); + if (ret < 0 && ret != -EAGAIN) { + /* We don't handle any other type of errors here */ + abort(); + } + if (ret == -EAGAIN || (ret >= 0 && ret < buf_size)) { + virtio_serial_throttle_port(port, true); + port->iov_idx = i; + if (ret > 0) { + port->iov_offset += ret; + } + break; + } + port->iov_offset = 0; + } + if (port->throttled) { + break; + } + virtqueue_push(vq, &port->elem, 0); + port->elem.out_num = 0; + } + virtio_notify(vdev, vq); +} + +static void flush_queued_data(VirtIOSerialPort *port) +{ + assert(port); + + if (!virtio_queue_ready(port->ovq)) { + return; + } + do_flush_queued_data(port, port->ovq, &port->vser->vdev); +} + +static size_t send_control_msg(VirtIOSerialPort *port, void *buf, size_t len) +{ + VirtQueueElement elem; + VirtQueue *vq; + struct virtio_console_control *cpkt; + + vq = port->vser->c_ivq; + if (!virtio_queue_ready(vq)) { + return 0; + } + if (!virtqueue_pop(vq, &elem)) { + return 0; + } + + cpkt = (struct virtio_console_control *)buf; + stl_p(&cpkt->id, port->id); + memcpy(elem.in_sg[0].iov_base, buf, len); + + virtqueue_push(vq, &elem, len); + virtio_notify(&port->vser->vdev, vq); + return len; +} + +static size_t send_control_event(VirtIOSerialPort *port, uint16_t event, + uint16_t value) +{ + struct virtio_console_control cpkt; + + stw_p(&cpkt.event, event); + stw_p(&cpkt.value, value); + + return send_control_msg(port, &cpkt, sizeof(cpkt)); +} + +/* Functions for use inside qemu to open and read from/write to ports */ +int virtio_serial_open(VirtIOSerialPort *port) +{ + /* Don't allow opening an already-open port */ + if (port->host_connected) { + return 0; + } + /* Send port open notification to the guest */ + port->host_connected = true; + send_control_event(port, VIRTIO_CONSOLE_PORT_OPEN, 1); + + return 0; +} + +int virtio_serial_close(VirtIOSerialPort *port) +{ + port->host_connected = false; + /* + * If there's any data the guest sent which the app didn't + * consume, reset the throttling flag and discard the data. + */ + port->throttled = false; + discard_vq_data(port->ovq, &port->vser->vdev); + + send_control_event(port, VIRTIO_CONSOLE_PORT_OPEN, 0); + + return 0; +} + +/* Individual ports/apps call this function to write to the guest. */ +ssize_t virtio_serial_write(VirtIOSerialPort *port, const uint8_t *buf, + size_t size) +{ + if (!port || !port->host_connected || !port->guest_connected) { + return 0; + } + return write_to_port(port, buf, size); +} + +/* + * Readiness of the guest to accept data on a port. + * Returns max. data the guest can receive + */ +size_t virtio_serial_guest_ready(VirtIOSerialPort *port) +{ + VirtQueue *vq = port->ivq; + + if (!virtio_queue_ready(vq) || + !(port->vser->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK) || + virtio_queue_empty(vq)) { + return 0; + } + if (use_multiport(port->vser) && !port->guest_connected) { + return 0; + } + + if (virtqueue_avail_bytes(vq, 4096, 0)) { + return 4096; + } + if (virtqueue_avail_bytes(vq, 1, 0)) { + return 1; + } + return 0; +} + +void virtio_serial_throttle_port(VirtIOSerialPort *port, bool throttle) +{ + if (!port) { + return; + } + + port->throttled = throttle; + if (throttle) { + return; + } + + flush_queued_data(port); +} + +/* Guest wants to notify us of some event */ +static void handle_control_message(VirtIOSerial *vser, void *buf, size_t len) +{ + struct VirtIOSerialPort *port; + struct virtio_console_control cpkt, *gcpkt; + uint8_t *buffer; + size_t buffer_len; + + gcpkt = buf; + + if (len < sizeof(cpkt)) { + /* The guest sent an invalid control packet */ + return; + } + + cpkt.event = lduw_p(&gcpkt->event); + cpkt.value = lduw_p(&gcpkt->value); + + port = find_port_by_id(vser, ldl_p(&gcpkt->id)); + if (!port && cpkt.event != VIRTIO_CONSOLE_DEVICE_READY) + return; + + switch(cpkt.event) { + case VIRTIO_CONSOLE_DEVICE_READY: + if (!cpkt.value) { + error_report("virtio-serial-bus: Guest failure in adding device %s\n", + vser->bus->qbus.name); + break; + } + /* + * The device is up, we can now tell the device about all the + * ports we have here. + */ + QTAILQ_FOREACH(port, &vser->ports, next) { + send_control_event(port, VIRTIO_CONSOLE_PORT_ADD, 1); + } + break; + + case VIRTIO_CONSOLE_PORT_READY: + if (!cpkt.value) { + error_report("virtio-serial-bus: Guest failure in adding port %u for device %s\n", + port->id, vser->bus->qbus.name); + break; + } + /* + * Now that we know the guest asked for the port name, we're + * sure the guest has initialised whatever state is necessary + * for this port. Now's a good time to let the guest know if + * this port is a console port so that the guest can hook it + * up to hvc. + */ + if (port->is_console) { + send_control_event(port, VIRTIO_CONSOLE_CONSOLE_PORT, 1); + } + + if (port->name) { + stw_p(&cpkt.event, VIRTIO_CONSOLE_PORT_NAME); + stw_p(&cpkt.value, 1); + + buffer_len = sizeof(cpkt) + strlen(port->name) + 1; + buffer = qemu_malloc(buffer_len); + + memcpy(buffer, &cpkt, sizeof(cpkt)); + memcpy(buffer + sizeof(cpkt), port->name, strlen(port->name)); + buffer[buffer_len - 1] = 0; + + send_control_msg(port, buffer, buffer_len); + qemu_free(buffer); + } + + if (port->host_connected) { + send_control_event(port, VIRTIO_CONSOLE_PORT_OPEN, 1); + } + + /* + * When the guest has asked us for this information it means + * the guest is all setup and has its virtqueues + * initialised. If some app is interested in knowing about + * this event, let it know. + */ + if (port->info->guest_ready) { + port->info->guest_ready(port); + } + break; + + case VIRTIO_CONSOLE_PORT_OPEN: + port->guest_connected = cpkt.value; + if (cpkt.value && port->info->guest_open) { + /* Send the guest opened notification if an app is interested */ + port->info->guest_open(port); + } + + if (!cpkt.value && port->info->guest_close) { + /* Send the guest closed notification if an app is interested */ + port->info->guest_close(port); + } + break; + } +} + +static void control_in(VirtIODevice *vdev, VirtQueue *vq) +{ +} + +static void control_out(VirtIODevice *vdev, VirtQueue *vq) +{ + VirtQueueElement elem; + VirtIOSerial *vser; + uint8_t *buf; + size_t len; + + vser = DO_UPCAST(VirtIOSerial, vdev, vdev); + + len = 0; + buf = NULL; + while (virtqueue_pop(vq, &elem)) { + size_t cur_len, copied; + + cur_len = iov_size(elem.out_sg, elem.out_num); + /* + * Allocate a new buf only if we didn't have one previously or + * if the size of the buf differs + */ + if (cur_len > len) { + qemu_free(buf); + + buf = qemu_malloc(cur_len); + len = cur_len; + } + copied = iov_to_buf(elem.out_sg, elem.out_num, buf, 0, len); + + handle_control_message(vser, buf, copied); + virtqueue_push(vq, &elem, 0); + } + qemu_free(buf); + virtio_notify(vdev, vq); +} + +/* Guest wrote something to some port. */ +static void handle_output(VirtIODevice *vdev, VirtQueue *vq) +{ + VirtIOSerial *vser; + VirtIOSerialPort *port; + bool discard; + + vser = DO_UPCAST(VirtIOSerial, vdev, vdev); + port = find_port_by_vq(vser, vq); + + discard = false; + if (!port || !port->host_connected || !port->info->have_data) { + discard = true; + } + + if (discard) { + discard_vq_data(vq, vdev); + return; + } + if (port->throttled) { + return; + } + + do_flush_queued_data(port, vq, vdev); +} + +static void handle_input(VirtIODevice *vdev, VirtQueue *vq) +{ +} + +static uint32_t get_features(VirtIODevice *vdev, uint32_t features) +{ + VirtIOSerial *vser; + + vser = DO_UPCAST(VirtIOSerial, vdev, vdev); + + if (vser->bus->max_nr_ports > 1) { + features |= (1 << VIRTIO_CONSOLE_F_MULTIPORT); + } + return features; +} + +/* Guest requested config info */ +static void get_config(VirtIODevice *vdev, uint8_t *config_data) +{ + VirtIOSerial *vser; + + vser = DO_UPCAST(VirtIOSerial, vdev, vdev); + memcpy(config_data, &vser->config, sizeof(struct virtio_console_config)); +} + +static void set_config(VirtIODevice *vdev, const uint8_t *config_data) +{ + struct virtio_console_config config; + + memcpy(&config, config_data, sizeof(config)); +} + +static void virtio_serial_save(QEMUFile *f, void *opaque) +{ + VirtIOSerial *s = opaque; + VirtIOSerialPort *port; + uint32_t nr_active_ports; + unsigned int i; + + /* The virtio device */ + virtio_save(&s->vdev, f); + + /* The config space */ + qemu_put_be16s(f, &s->config.cols); + qemu_put_be16s(f, &s->config.rows); + + qemu_put_be32s(f, &s->config.max_nr_ports); + + /* The ports map */ + + for (i = 0; i < (s->config.max_nr_ports + 31) / 32; i++) { + qemu_put_be32s(f, &s->ports_map[i]); + } + + /* Ports */ + + nr_active_ports = 0; + QTAILQ_FOREACH(port, &s->ports, next) { + nr_active_ports++; + } + + qemu_put_be32s(f, &nr_active_ports); + + /* + * Items in struct VirtIOSerialPort. + */ + QTAILQ_FOREACH(port, &s->ports, next) { + uint32_t elem_popped; + + qemu_put_be32s(f, &port->id); + qemu_put_byte(f, port->guest_connected); + qemu_put_byte(f, port->host_connected); + + elem_popped = 0; + if (port->elem.out_num) { + elem_popped = 1; + } + qemu_put_be32s(f, &elem_popped); + if (elem_popped) { + qemu_put_be32s(f, &port->iov_idx); + qemu_put_be64s(f, &port->iov_offset); + + qemu_put_buffer(f, (unsigned char *)&port->elem, + sizeof(port->elem)); + } + } +} + +static int virtio_serial_load(QEMUFile *f, void *opaque, int version_id) +{ + VirtIOSerial *s = opaque; + VirtIOSerialPort *port; + uint32_t max_nr_ports, nr_active_ports, ports_map; + unsigned int i; + + if (version_id > 3) { + return -EINVAL; + } + + /* The virtio device */ + virtio_load(&s->vdev, f); + + if (version_id < 2) { + return 0; + } + + /* The config space */ + qemu_get_be16s(f, &s->config.cols); + qemu_get_be16s(f, &s->config.rows); + + qemu_get_be32s(f, &max_nr_ports); + if (max_nr_ports > s->config.max_nr_ports) { + /* Source could have had more ports than us. Fail migration. */ + return -EINVAL; + } + + for (i = 0; i < (max_nr_ports + 31) / 32; i++) { + qemu_get_be32s(f, &ports_map); + + if (ports_map != s->ports_map[i]) { + /* + * Ports active on source and destination don't + * match. Fail migration. + */ + return -EINVAL; + } + } + + qemu_get_be32s(f, &nr_active_ports); + + /* Items in struct VirtIOSerialPort */ + for (i = 0; i < nr_active_ports; i++) { + uint32_t id; + bool host_connected; + + id = qemu_get_be32(f); + port = find_port_by_id(s, id); + + port->guest_connected = qemu_get_byte(f); + host_connected = qemu_get_byte(f); + if (host_connected != port->host_connected) { + /* + * We have to let the guest know of the host connection + * status change + */ + send_control_event(port, VIRTIO_CONSOLE_PORT_OPEN, + port->host_connected); + } + + if (version_id > 2) { + uint32_t elem_popped; + + qemu_get_be32s(f, &elem_popped); + if (elem_popped) { + qemu_get_be32s(f, &port->iov_idx); + qemu_get_be64s(f, &port->iov_offset); + + qemu_get_buffer(f, (unsigned char *)&port->elem, + sizeof(port->elem)); + virtqueue_map_sg(port->elem.in_sg, port->elem.in_addr, + port->elem.in_num, 1); + virtqueue_map_sg(port->elem.out_sg, port->elem.out_addr, + port->elem.out_num, 1); + + /* + * Port was throttled on source machine. Let's + * unthrottle it here so data starts flowing again. + */ + virtio_serial_throttle_port(port, false); + } + } + } + return 0; +} + +static void virtser_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent); + +static struct BusInfo virtser_bus_info = { + .name = "virtio-serial-bus", + .size = sizeof(VirtIOSerialBus), + .print_dev = virtser_bus_dev_print, +}; + +static VirtIOSerialBus *virtser_bus_new(DeviceState *dev) +{ + VirtIOSerialBus *bus; + + bus = FROM_QBUS(VirtIOSerialBus, qbus_create(&virtser_bus_info, dev, NULL)); + bus->qbus.allow_hotplug = 1; + + return bus; +} + +static void virtser_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent) +{ + VirtIOSerialDevice *dev = DO_UPCAST(VirtIOSerialDevice, qdev, qdev); + VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, &dev->qdev); + + monitor_printf(mon, "%*s dev-prop-int: id: %u\n", + indent, "", port->id); + monitor_printf(mon, "%*s dev-prop-int: guest_connected: %d\n", + indent, "", port->guest_connected); + monitor_printf(mon, "%*s dev-prop-int: host_connected: %d\n", + indent, "", port->host_connected); + monitor_printf(mon, "%*s dev-prop-int: throttled: %d\n", + indent, "", port->throttled); +} + +/* This function is only used if a port id is not provided by the user */ +static uint32_t find_free_port_id(VirtIOSerial *vser) +{ + unsigned int i; + + for (i = 0; i < (vser->config.max_nr_ports + 31) / 32; i++) { + uint32_t map, bit; + + map = vser->ports_map[i]; + bit = ffs(~map); + if (bit) { + return (bit - 1) + i * 32; + } + } + return VIRTIO_CONSOLE_BAD_ID; +} + +static void mark_port_added(VirtIOSerial *vser, uint32_t port_id) +{ + unsigned int i; + + i = port_id / 32; + vser->ports_map[i] |= 1U << (port_id % 32); +} + +static void add_port(VirtIOSerial *vser, uint32_t port_id) +{ + mark_port_added(vser, port_id); + + send_control_event(find_port_by_id(vser, port_id), + VIRTIO_CONSOLE_PORT_ADD, 1); +} + +static void remove_port(VirtIOSerial *vser, uint32_t port_id) +{ + VirtIOSerialPort *port; + unsigned int i; + + i = port_id / 32; + vser->ports_map[i] &= ~(1U << (port_id % 32)); + + port = find_port_by_id(vser, port_id); + /* Flush out any unconsumed buffers first */ + discard_vq_data(port->ovq, &port->vser->vdev); + + send_control_event(port, VIRTIO_CONSOLE_PORT_REMOVE, 1); +} + +static int virtser_port_qdev_init(DeviceState *qdev, DeviceInfo *base) +{ + VirtIOSerialDevice *dev = DO_UPCAST(VirtIOSerialDevice, qdev, qdev); + VirtIOSerialPortInfo *info = DO_UPCAST(VirtIOSerialPortInfo, qdev, base); + VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, &dev->qdev); + VirtIOSerialBus *bus = DO_UPCAST(VirtIOSerialBus, qbus, qdev->parent_bus); + int ret; + bool plugging_port0; + + port->vser = bus->vser; + + /* + * Is the first console port we're seeing? If so, put it up at + * location 0. This is done for backward compatibility (old + * kernel, new qemu). + */ + plugging_port0 = port->is_console && !find_port_by_id(port->vser, 0); + + if (find_port_by_id(port->vser, port->id)) { + error_report("virtio-serial-bus: A port already exists at id %u\n", + port->id); + return -1; + } + + if (port->id == VIRTIO_CONSOLE_BAD_ID) { + if (plugging_port0) { + port->id = 0; + } else { + port->id = find_free_port_id(port->vser); + if (port->id == VIRTIO_CONSOLE_BAD_ID) { + error_report("virtio-serial-bus: Maximum port limit for this device reached\n"); + return -1; + } + } + } + + if (port->id >= port->vser->config.max_nr_ports) { + error_report("virtio-serial-bus: Out-of-range port id specified, max. allowed: %u\n", + port->vser->config.max_nr_ports - 1); + return -1; + } + + dev->info = info; + ret = info->init(dev); + if (ret) { + return ret; + } + + if (!use_multiport(port->vser)) { + /* + * Allow writes to guest in this case; we have no way of + * knowing if a guest port is connected. + */ + port->guest_connected = true; + } + + port->elem.out_num = 0; + + QTAILQ_INSERT_TAIL(&port->vser->ports, port, next); + port->ivq = port->vser->ivqs[port->id]; + port->ovq = port->vser->ovqs[port->id]; + + add_port(port->vser, port->id); + + /* Send an update to the guest about this new port added */ + virtio_notify_config(&port->vser->vdev); + + return ret; +} + +static int virtser_port_qdev_exit(DeviceState *qdev) +{ + VirtIOSerialDevice *dev = DO_UPCAST(VirtIOSerialDevice, qdev, qdev); + VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, &dev->qdev); + VirtIOSerial *vser = port->vser; + + remove_port(port->vser, port->id); + + QTAILQ_REMOVE(&vser->ports, port, next); + + if (port->info->exit) + port->info->exit(dev); + + return 0; +} + +void virtio_serial_port_qdev_register(VirtIOSerialPortInfo *info) +{ + info->qdev.init = virtser_port_qdev_init; + info->qdev.bus_info = &virtser_bus_info; + info->qdev.exit = virtser_port_qdev_exit; + info->qdev.unplug = qdev_simple_unplug_cb; + qdev_register(&info->qdev); +} + +VirtIODevice *virtio_serial_init(DeviceState *dev, uint32_t max_nr_ports) +{ + VirtIOSerial *vser; + VirtIODevice *vdev; + uint32_t i, max_supported_ports; + + if (!max_nr_ports) + return NULL; + + /* Each port takes 2 queues, and one pair is for the control queue */ + max_supported_ports = VIRTIO_PCI_QUEUE_MAX / 2 - 1; + + if (max_nr_ports > max_supported_ports) { + error_report("maximum ports supported: %u", max_supported_ports); + return NULL; + } + + vdev = virtio_common_init("virtio-serial", VIRTIO_ID_CONSOLE, + sizeof(struct virtio_console_config), + sizeof(VirtIOSerial)); + + vser = DO_UPCAST(VirtIOSerial, vdev, vdev); + + /* Spawn a new virtio-serial bus on which the ports will ride as devices */ + vser->bus = virtser_bus_new(dev); + vser->bus->vser = vser; + QTAILQ_INIT(&vser->ports); + + vser->bus->max_nr_ports = max_nr_ports; + vser->ivqs = qemu_malloc(max_nr_ports * sizeof(VirtQueue *)); + vser->ovqs = qemu_malloc(max_nr_ports * sizeof(VirtQueue *)); + + /* Add a queue for host to guest transfers for port 0 (backward compat) */ + vser->ivqs[0] = virtio_add_queue(vdev, 128, handle_input); + /* Add a queue for guest to host transfers for port 0 (backward compat) */ + vser->ovqs[0] = virtio_add_queue(vdev, 128, handle_output); + + /* TODO: host to guest notifications can get dropped + * if the queue fills up. Implement queueing in host, + * this might also make it possible to reduce the control + * queue size: as guest preposts buffers there, + * this will save 4Kbyte of guest memory per entry. */ + + /* control queue: host to guest */ + vser->c_ivq = virtio_add_queue(vdev, 32, control_in); + /* control queue: guest to host */ + vser->c_ovq = virtio_add_queue(vdev, 32, control_out); + + for (i = 1; i < vser->bus->max_nr_ports; i++) { + /* Add a per-port queue for host to guest transfers */ + vser->ivqs[i] = virtio_add_queue(vdev, 128, handle_input); + /* Add a per-per queue for guest to host transfers */ + vser->ovqs[i] = virtio_add_queue(vdev, 128, handle_output); + } + + vser->config.max_nr_ports = max_nr_ports; + vser->ports_map = qemu_mallocz(((max_nr_ports + 31) / 32) + * sizeof(vser->ports_map[0])); + /* + * Reserve location 0 for a console port for backward compat + * (old kernel, new qemu) + */ + mark_port_added(vser, 0); + + vser->vdev.get_features = get_features; + vser->vdev.get_config = get_config; + vser->vdev.set_config = set_config; + + vser->qdev = dev; + + /* + * Register for the savevm section with the virtio-console name + * to preserve backward compat + */ + register_savevm(dev, "virtio-console", -1, 3, virtio_serial_save, + virtio_serial_load, vser); + + return vdev; +} + +void virtio_serial_exit(VirtIODevice *vdev) +{ + VirtIOSerial *vser = DO_UPCAST(VirtIOSerial, vdev, vdev); + + unregister_savevm(vser->qdev, "virtio-console", vser); + + qemu_free(vser->ivqs); + qemu_free(vser->ovqs); + qemu_free(vser->ports_map); + + virtio_cleanup(vdev); +} diff -Nru qemu-kvm-0.12.5+noroms/hw/virtio-serial.h qemu-kvm-0.14.1/hw/virtio-serial.h --- qemu-kvm-0.12.5+noroms/hw/virtio-serial.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/virtio-serial.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,202 @@ +/* + * Virtio Serial / Console Support + * + * Copyright IBM, Corp. 2008 + * Copyright Red Hat, Inc. 2009, 2010 + * + * Authors: + * Christian Ehrhardt + * Amit Shah + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ +#ifndef _QEMU_VIRTIO_SERIAL_H +#define _QEMU_VIRTIO_SERIAL_H + +#include "qdev.h" +#include "virtio.h" + +/* == Interface shared between the guest kernel and qemu == */ + +/* The Virtio ID for virtio console / serial ports */ +#define VIRTIO_ID_CONSOLE 3 + +/* Features supported */ +#define VIRTIO_CONSOLE_F_MULTIPORT 1 + +#define VIRTIO_CONSOLE_BAD_ID (~(uint32_t)0) + +struct virtio_console_config { + /* + * These two fields are used by VIRTIO_CONSOLE_F_SIZE which + * isn't implemented here yet + */ + uint16_t cols; + uint16_t rows; + + uint32_t max_nr_ports; +} __attribute__((packed)); + +struct virtio_console_control { + uint32_t id; /* Port number */ + uint16_t event; /* The kind of control event (see below) */ + uint16_t value; /* Extra information for the key */ +}; + +/* Some events for the internal messages (control packets) */ +#define VIRTIO_CONSOLE_DEVICE_READY 0 +#define VIRTIO_CONSOLE_PORT_ADD 1 +#define VIRTIO_CONSOLE_PORT_REMOVE 2 +#define VIRTIO_CONSOLE_PORT_READY 3 +#define VIRTIO_CONSOLE_CONSOLE_PORT 4 +#define VIRTIO_CONSOLE_RESIZE 5 +#define VIRTIO_CONSOLE_PORT_OPEN 6 +#define VIRTIO_CONSOLE_PORT_NAME 7 + +/* == In-qemu interface == */ + +typedef struct VirtIOSerial VirtIOSerial; +typedef struct VirtIOSerialBus VirtIOSerialBus; +typedef struct VirtIOSerialPort VirtIOSerialPort; +typedef struct VirtIOSerialPortInfo VirtIOSerialPortInfo; + +typedef struct VirtIOSerialDevice { + DeviceState qdev; + VirtIOSerialPortInfo *info; +} VirtIOSerialDevice; + +/* + * This is the state that's shared between all the ports. Some of the + * state is configurable via command-line options. Some of it can be + * set by individual devices in their initfn routines. Some of the + * state is set by the generic qdev device init routine. + */ +struct VirtIOSerialPort { + DeviceState dev; + VirtIOSerialPortInfo *info; + + QTAILQ_ENTRY(VirtIOSerialPort) next; + + /* + * This field gives us the virtio device as well as the qdev bus + * that we are associated with + */ + VirtIOSerial *vser; + + VirtQueue *ivq, *ovq; + + /* + * This name is sent to the guest and exported via sysfs. + * The guest could create symlinks based on this information. + * The name is in the reverse fqdn format, like org.qemu.console.0 + */ + char *name; + + /* + * This id helps identify ports between the guest and the host. + * The guest sends a "header" with this id with each data packet + * that it sends and the host can then find out which associated + * device to send out this data to + */ + uint32_t id; + + /* + * This is the elem that we pop from the virtqueue. A slow + * backend that consumes guest data (e.g. the file backend for + * qemu chardevs) can cause the guest to block till all the output + * is flushed. This isn't desired, so we keep a note of the last + * element popped and continue consuming it once the backend + * becomes writable again. + */ + VirtQueueElement elem; + + /* + * The index and the offset into the iov buffer that was popped in + * elem above. + */ + uint32_t iov_idx; + uint64_t iov_offset; + + /* Identify if this is a port that binds with hvc in the guest */ + uint8_t is_console; + + /* Is the corresponding guest device open? */ + bool guest_connected; + /* Is this device open for IO on the host? */ + bool host_connected; + /* Do apps not want to receive data? */ + bool throttled; +}; + +struct VirtIOSerialPortInfo { + DeviceInfo qdev; + /* + * The per-port (or per-app) init function that's called when a + * new device is found on the bus. + */ + int (*init)(VirtIOSerialDevice *dev); + /* + * Per-port exit function that's called when a port gets + * hot-unplugged or removed. + */ + int (*exit)(VirtIOSerialDevice *dev); + + /* Callbacks for guest events */ + /* Guest opened device. */ + void (*guest_open)(VirtIOSerialPort *port); + /* Guest closed device. */ + void (*guest_close)(VirtIOSerialPort *port); + + /* Guest is now ready to accept data (virtqueues set up). */ + void (*guest_ready)(VirtIOSerialPort *port); + + /* + * Guest wrote some data to the port. This data is handed over to + * the app via this callback. The app can return a size less than + * 'len'. In this case, throttling will be enabled for this port. + */ + ssize_t (*have_data)(VirtIOSerialPort *port, const uint8_t *buf, + size_t len); +}; + +/* Interface to the virtio-serial bus */ + +/* + * Individual ports/apps should call this function to register the port + * with the virtio-serial bus + */ +void virtio_serial_port_qdev_register(VirtIOSerialPortInfo *info); + +/* + * Open a connection to the port + * Returns 0 on success (always). + */ +int virtio_serial_open(VirtIOSerialPort *port); + +/* + * Close the connection to the port + * Returns 0 on success (always). + */ +int virtio_serial_close(VirtIOSerialPort *port); + +/* + * Send data to Guest + */ +ssize_t virtio_serial_write(VirtIOSerialPort *port, const uint8_t *buf, + size_t size); + +/* + * Query whether a guest is ready to receive data. + */ +size_t virtio_serial_guest_ready(VirtIOSerialPort *port); + +/* + * Flow control: Ports can signal to the virtio-serial core to stop + * sending data or re-start sending data, depending on the 'throttle' + * value here. + */ +void virtio_serial_throttle_port(VirtIOSerialPort *port, bool throttle); + +#endif diff -Nru qemu-kvm-0.12.5+noroms/hw/vmmouse.c qemu-kvm-0.14.1/hw/vmmouse.c --- qemu-kvm-0.12.5+noroms/hw/vmmouse.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/vmmouse.c 2011-05-11 13:29:46.000000000 +0000 @@ -97,19 +97,32 @@ /* need to still generate PS2 events to notify driver to read from queue */ - ps2_mouse_fake_event(s->ps2_mouse); + i8042_isa_mouse_fake_event(s->ps2_mouse); } -static void vmmouse_update_handler(VMMouseState *s) +static void vmmouse_remove_handler(VMMouseState *s) { if (s->entry) { qemu_remove_mouse_event_handler(s->entry); s->entry = NULL; } - if (s->status == 0) +} + +static void vmmouse_update_handler(VMMouseState *s, int absolute) +{ + if (s->status != 0) { + return; + } + if (s->absolute != absolute) { + s->absolute = absolute; + vmmouse_remove_handler(s); + } + if (s->entry == NULL) { s->entry = qemu_add_mouse_event_handler(vmmouse_mouse_event, s, s->absolute, "vmmouse"); + qemu_activate_mouse_event_handler(s->entry); + } } static void vmmouse_read_id(VMMouseState *s) @@ -121,28 +134,25 @@ s->queue[s->nb_queue++] = VMMOUSE_VERSION; s->status = 0; - vmmouse_update_handler(s); } static void vmmouse_request_relative(VMMouseState *s) { DPRINTF("vmmouse_request_relative()\n"); - s->absolute = 0; - vmmouse_update_handler(s); + vmmouse_update_handler(s, 0); } static void vmmouse_request_absolute(VMMouseState *s) { DPRINTF("vmmouse_request_absolute()\n"); - s->absolute = 1; - vmmouse_update_handler(s); + vmmouse_update_handler(s, 1); } static void vmmouse_disable(VMMouseState *s) { DPRINTF("vmmouse_disable()\n"); s->status = 0xffff; - vmmouse_update_handler(s); + vmmouse_remove_handler(s); } static void vmmouse_data(VMMouseState *s, uint32_t *data, uint32_t size) @@ -154,7 +164,7 @@ if (size == 0 || size > 6 || size > s->nb_queue) { printf("vmmouse: driver requested too much data %d\n", size); s->status = 0xffff; - vmmouse_update_handler(s); + vmmouse_remove_handler(s); return; } @@ -239,7 +249,8 @@ { VMMouseState *s = opaque; - vmmouse_update_handler(s); + vmmouse_remove_handler(s); + vmmouse_update_handler(s, s->absolute); return 0; } @@ -274,7 +285,7 @@ vmport_register(VMMOUSE_STATUS, vmmouse_ioport_read, s); vmport_register(VMMOUSE_COMMAND, vmmouse_ioport_read, s); vmport_register(VMMOUSE_DATA, vmmouse_ioport_read, s); - vmstate_register(0, &vmstate_vmmouse, s); + vmstate_register(NULL, 0, &vmstate_vmmouse, s); return s; } diff -Nru qemu-kvm-0.12.5+noroms/hw/vmport.c qemu-kvm-0.14.1/hw/vmport.c --- qemu-kvm-0.12.5+noroms/hw/vmport.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/vmport.c 2011-05-11 13:29:46.000000000 +0000 @@ -21,12 +21,11 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ - #include "hw.h" #include "isa.h" #include "pc.h" #include "sysemu.h" -#include "qemu-kvm.h" +#include "kvm.h" //#define VMPORT_DEBUG @@ -59,10 +58,8 @@ CPUState *env = cpu_single_env; unsigned char command; uint32_t eax; - uint32_t ret; - if (kvm_enabled()) - kvm_save_registers(env); + cpu_synchronize_state(env); eax = env->regs[R_EAX]; if (eax != VMPORT_MAGIC) @@ -79,12 +76,7 @@ return eax; } - ret = s->func[command](s->opaque[command], addr); - - if (kvm_enabled()) - kvm_load_registers(env); - - return ret; + return s->func[command](s->opaque[command], addr); } static void vmport_ioport_write(void *opaque, uint32_t addr, uint32_t val) diff -Nru qemu-kvm-0.12.5+noroms/hw/vmware_vga.c qemu-kvm-0.14.1/hw/vmware_vga.c --- qemu-kvm-0.12.5+noroms/hw/vmware_vga.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/vmware_vga.c 2011-05-11 13:29:46.000000000 +0000 @@ -114,14 +114,12 @@ # define SVGA_IO_BASE SVGA_LEGACY_BASE_PORT # define SVGA_IO_MUL 1 # define SVGA_FIFO_SIZE 0x10000 -# define SVGA_MEM_BASE 0xe0000000 # define SVGA_PCI_DEVICE_ID PCI_DEVICE_ID_VMWARE_SVGA2 #else # define SVGA_ID SVGA_ID_1 # define SVGA_IO_BASE SVGA_LEGACY_BASE_PORT # define SVGA_IO_MUL 4 # define SVGA_FIFO_SIZE 0x10000 -# define SVGA_MEM_BASE 0xe0000000 # define SVGA_PCI_DEVICE_ID PCI_DEVICE_ID_VMWARE_SVGA #endif @@ -477,23 +475,57 @@ static inline void vmsvga_cursor_define(struct vmsvga_state_s *s, struct vmsvga_cursor_definition_s *c) { - int i; - for (i = SVGA_BITMAP_SIZE(c->width, c->height) - 1; i >= 0; i --) - c->mask[i] = ~c->mask[i]; + QEMUCursor *qc; + int i, pixels; + + qc = cursor_alloc(c->width, c->height); + qc->hot_x = c->hot_x; + qc->hot_y = c->hot_y; + switch (c->bpp) { + case 1: + cursor_set_mono(qc, 0xffffff, 0x000000, (void*)c->image, + 1, (void*)c->mask); +#ifdef DEBUG + cursor_print_ascii_art(qc, "vmware/mono"); +#endif + break; + case 32: + /* fill alpha channel from mask, set color to zero */ + cursor_set_mono(qc, 0x000000, 0x000000, (void*)c->mask, + 1, (void*)c->mask); + /* add in rgb values */ + pixels = c->width * c->height; + for (i = 0; i < pixels; i++) { + qc->data[i] |= c->image[i] & 0xffffff; + } +#ifdef DEBUG + cursor_print_ascii_art(qc, "vmware/32bit"); +#endif + break; + default: + fprintf(stderr, "%s: unhandled bpp %d, using fallback cursor\n", + __FUNCTION__, c->bpp); + cursor_put(qc); + qc = cursor_builtin_left_ptr(); + } if (s->vga.ds->cursor_define) - s->vga.ds->cursor_define(c->width, c->height, c->bpp, c->hot_x, c->hot_y, - (uint8_t *) c->image, (uint8_t *) c->mask); + s->vga.ds->cursor_define(qc); + cursor_put(qc); } #endif #define CMD(f) le32_to_cpu(s->cmd->f) -static inline int vmsvga_fifo_empty(struct vmsvga_state_s *s) +static inline int vmsvga_fifo_length(struct vmsvga_state_s *s) { + int num; if (!s->config || !s->enable) - return 1; - return (s->cmd->next_cmd == s->cmd->stop); + return 0; + num = CMD(next_cmd) - CMD(stop); + if (num < 0) + num += CMD(max) - CMD(min); + return num >> 2; } static inline uint32_t vmsvga_fifo_read_raw(struct vmsvga_state_s *s) @@ -513,13 +545,23 @@ static void vmsvga_fifo_run(struct vmsvga_state_s *s) { uint32_t cmd, colour; - int args = 0; + int args, len; int x, y, dx, dy, width, height; struct vmsvga_cursor_definition_s cursor; - while (!vmsvga_fifo_empty(s)) + uint32_t cmd_start; + + len = vmsvga_fifo_length(s); + while (len > 0) { + /* May need to go back to the start of the command if incomplete */ + cmd_start = s->cmd->stop; + switch (cmd = vmsvga_fifo_read(s)) { case SVGA_CMD_UPDATE: case SVGA_CMD_UPDATE_VERBOSE: + len -= 5; + if (len < 0) + goto rewind; + x = vmsvga_fifo_read(s); y = vmsvga_fifo_read(s); width = vmsvga_fifo_read(s); @@ -528,6 +570,10 @@ break; case SVGA_CMD_RECT_FILL: + len -= 6; + if (len < 0) + goto rewind; + colour = vmsvga_fifo_read(s); x = vmsvga_fifo_read(s); y = vmsvga_fifo_read(s); @@ -537,10 +583,15 @@ vmsvga_fill_rect(s, colour, x, y, width, height); break; #else + args = 0; goto badcmd; #endif case SVGA_CMD_RECT_COPY: + len -= 7; + if (len < 0) + goto rewind; + x = vmsvga_fifo_read(s); y = vmsvga_fifo_read(s); dx = vmsvga_fifo_read(s); @@ -551,10 +602,15 @@ vmsvga_copy_rect(s, x, y, dx, dy, width, height); break; #else + args = 0; goto badcmd; #endif case SVGA_CMD_DEFINE_CURSOR: + len -= 8; + if (len < 0) + goto rewind; + cursor.id = vmsvga_fifo_read(s); cursor.hot_x = vmsvga_fifo_read(s); cursor.hot_y = vmsvga_fifo_read(s); @@ -563,11 +619,14 @@ vmsvga_fifo_read(s); cursor.bpp = vmsvga_fifo_read(s); - if (SVGA_BITMAP_SIZE(x, y) > sizeof cursor.mask || - SVGA_PIXMAP_SIZE(x, y, cursor.bpp) > sizeof cursor.image) { - args = SVGA_BITMAP_SIZE(x, y) + SVGA_PIXMAP_SIZE(x, y, cursor.bpp); - goto badcmd; - } + args = SVGA_BITMAP_SIZE(x, y) + SVGA_PIXMAP_SIZE(x, y, cursor.bpp); + if (SVGA_BITMAP_SIZE(x, y) > sizeof cursor.mask || + SVGA_PIXMAP_SIZE(x, y, cursor.bpp) > sizeof cursor.image) + goto badcmd; + + len -= args; + if (len < 0) + goto rewind; for (args = 0; args < SVGA_BITMAP_SIZE(x, y); args ++) cursor.mask[args] = vmsvga_fifo_read_raw(s); @@ -586,6 +645,10 @@ * for so we can avoid FIFO desync if driver uses them illegally. */ case SVGA_CMD_DEFINE_ALPHA_CURSOR: + len -= 6; + if (len < 0) + goto rewind; + vmsvga_fifo_read(s); vmsvga_fifo_read(s); vmsvga_fifo_read(s); @@ -600,6 +663,10 @@ args = 7; goto badcmd; case SVGA_CMD_DRAW_GLYPH_CLIPPED: + len -= 4; + if (len < 0) + goto rewind; + vmsvga_fifo_read(s); vmsvga_fifo_read(s); args = 7 + (vmsvga_fifo_read(s) >> 2); @@ -620,13 +687,22 @@ break; /* Nop */ default: + args = 0; badcmd: + len -= args; + if (len < 0) + goto rewind; while (args --) vmsvga_fifo_read(s); printf("%s: Unknown command 0x%02x in SVGA command FIFO\n", __FUNCTION__, cmd); break; + + rewind: + s->cmd->stop = cmd_start; + break; } + } s->syncing = 0; } @@ -779,11 +855,11 @@ s->invalidated = 1; s->vga.invalidate(&s->vga); if (s->enable) { - s->fb_size = ((s->depth + 7) >> 3) * s->new_width * s->new_height; - vga_dirty_log_stop(&s->vga); - } else { - vga_dirty_log_start(&s->vga); - } + s->fb_size = ((s->depth + 7) >> 3) * s->new_width * s->new_height; + vga_dirty_log_stop(&s->vga); + } else { + vga_dirty_log_start(&s->vga); + } break; case SVGA_REG_WIDTH: @@ -1134,16 +1210,12 @@ s->fifo_size = SVGA_FIFO_SIZE; - s->fifo_offset = qemu_ram_alloc(s->fifo_size); + s->fifo_offset = qemu_ram_alloc(NULL, "vmsvga.fifo", s->fifo_size); s->fifo_ptr = qemu_get_ram_ptr(s->fifo_offset); vga_common_init(&s->vga, vga_ram_size); vga_init(&s->vga); - vmstate_register(0, &vmstate_vga_common, &s->vga); - - vga_init_vbe(&s->vga); - - rom_add_vga(VGABIOS_FILENAME); + vmstate_register(NULL, 0, &vmstate_vga_common, &s->vga); vmsvga_reset(s); } @@ -1178,7 +1250,7 @@ s->vram_base = addr; #ifdef DIRECT_VRAM iomemtype = cpu_register_io_memory(vmsvga_vram_read, - vmsvga_vram_write, s); + vmsvga_vram_write, s, DEVICE_NATIVE_ENDIAN); #else iomemtype = s->vga.vram_offset | IO_MEM_RAM; #endif @@ -1210,16 +1282,14 @@ pci_config_set_vendor_id(s->card.config, PCI_VENDOR_ID_VMWARE); pci_config_set_device_id(s->card.config, SVGA_PCI_DEVICE_ID); - s->card.config[PCI_COMMAND] = 0x07; /* I/O + Memory */ pci_config_set_class(s->card.config, PCI_CLASS_DISPLAY_VGA); - s->card.config[0x0c] = 0x08; /* Cache line size */ - s->card.config[0x0d] = 0x40; /* Latency timer */ - s->card.config[PCI_HEADER_TYPE] = PCI_HEADER_TYPE_NORMAL; - s->card.config[0x2c] = PCI_VENDOR_ID_VMWARE & 0xff; - s->card.config[0x2d] = PCI_VENDOR_ID_VMWARE >> 8; - s->card.config[0x2e] = SVGA_PCI_DEVICE_ID & 0xff; - s->card.config[0x2f] = SVGA_PCI_DEVICE_ID >> 8; - s->card.config[0x3c] = 0xff; /* End */ + s->card.config[PCI_CACHE_LINE_SIZE] = 0x08; /* Cache line size */ + s->card.config[PCI_LATENCY_TIMER] = 0x40; /* Latency timer */ + s->card.config[PCI_SUBSYSTEM_VENDOR_ID] = PCI_VENDOR_ID_VMWARE & 0xff; + s->card.config[PCI_SUBSYSTEM_VENDOR_ID + 1] = PCI_VENDOR_ID_VMWARE >> 8; + s->card.config[PCI_SUBSYSTEM_ID] = SVGA_PCI_DEVICE_ID & 0xff; + s->card.config[PCI_SUBSYSTEM_ID + 1] = SVGA_PCI_DEVICE_ID >> 8; + s->card.config[PCI_INTERRUPT_LINE] = 0xff; /* End */ pci_register_bar(&s->card, 0, 0x10, PCI_BASE_ADDRESS_SPACE_IO, pci_vmsvga_map_ioport); @@ -1227,10 +1297,15 @@ PCI_BASE_ADDRESS_MEM_PREFETCH, pci_vmsvga_map_mem); pci_register_bar(&s->card, 2, SVGA_FIFO_SIZE, - PCI_BASE_ADDRESS_MEM_PREFETCH, pci_vmsvga_map_fifo); + PCI_BASE_ADDRESS_MEM_PREFETCH, pci_vmsvga_map_fifo); vmsvga_init(&s->chip, VGA_RAM_SIZE); + if (!dev->rom_bar) { + /* compatibility with pc-0.13 and older */ + vga_init_vbe(&s->chip.vga); + } + return 0; } @@ -1243,7 +1318,9 @@ .qdev.name = "vmware-svga", .qdev.size = sizeof(struct pci_vmsvga_state_s), .qdev.vmsd = &vmstate_vmware_vga, + .no_hotplug = 1, .init = pci_vmsvga_initfn, + .romfile = "vgabios-vmware.bin", }; static void vmsvga_register(void) diff -Nru qemu-kvm-0.12.5+noroms/hw/vt82c686.c qemu-kvm-0.14.1/hw/vt82c686.c --- qemu-kvm-0.12.5+noroms/hw/vt82c686.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/vt82c686.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,594 @@ +/* + * VT82C686B south bridge support + * + * Copyright (c) 2008 yajin (yajin@vm-kernel.org) + * Copyright (c) 2009 chenming (chenming@rdc.faw.com.cn) + * Copyright (c) 2010 Huacai Chen (zltjiangshi@gmail.com) + * This code is licensed under the GNU GPL v2. + */ + +#include "hw.h" +#include "pc.h" +#include "vt82c686.h" +#include "i2c.h" +#include "smbus.h" +#include "pci.h" +#include "isa.h" +#include "sysbus.h" +#include "mips.h" +#include "apm.h" +#include "acpi.h" +#include "pm_smbus.h" +#include "sysemu.h" +#include "qemu-timer.h" + +typedef uint32_t pci_addr_t; +#include "pci_host.h" +//#define DEBUG_VT82C686B + +#ifdef DEBUG_VT82C686B +#define DPRINTF(fmt, ...) fprintf(stderr, "%s: " fmt, __FUNCTION__, ##__VA_ARGS__) +#else +#define DPRINTF(fmt, ...) +#endif + +typedef struct SuperIOConfig +{ + uint8_t config[0xff]; + uint8_t index; + uint8_t data; +} SuperIOConfig; + +typedef struct VT82C686BState { + PCIDevice dev; + SuperIOConfig superio_conf; +} VT82C686BState; + +static void superio_ioport_writeb(void *opaque, uint32_t addr, uint32_t data) +{ + int can_write; + SuperIOConfig *superio_conf = opaque; + + DPRINTF("superio_ioport_writeb address 0x%x val 0x%x \n", addr, data); + if (addr == 0x3f0) { + superio_conf->index = data & 0xff; + } else { + /* 0x3f1 */ + switch (superio_conf->index) { + case 0x00 ... 0xdf: + case 0xe4: + case 0xe5: + case 0xe9 ... 0xed: + case 0xf3: + case 0xf5: + case 0xf7: + case 0xf9 ... 0xfb: + case 0xfd ... 0xff: + can_write = 0; + break; + default: + can_write = 1; + + if (can_write) { + switch (superio_conf->index) { + case 0xe7: + if ((data & 0xff) != 0xfe) { + DPRINTF("chage uart 1 base. unsupported yet \n"); + } + break; + case 0xe8: + if ((data & 0xff) != 0xbe) { + DPRINTF("chage uart 2 base. unsupported yet \n"); + } + break; + + default: + superio_conf->config[superio_conf->index] = data & 0xff; + } + } + } + superio_conf->config[superio_conf->index] = data & 0xff; + } +} + +static uint32_t superio_ioport_readb(void *opaque, uint32_t addr) +{ + SuperIOConfig *superio_conf = opaque; + + DPRINTF("superio_ioport_readb address 0x%x \n", addr); + return (superio_conf->config[superio_conf->index]); +} + +static void vt82c686b_reset(void * opaque) +{ + PCIDevice *d = opaque; + uint8_t *pci_conf = d->config; + VT82C686BState *vt82c = DO_UPCAST(VT82C686BState, dev, d); + + pci_set_long(pci_conf + PCI_CAPABILITY_LIST, 0x000000c0); + pci_set_word(pci_conf + PCI_COMMAND, PCI_COMMAND_IO | PCI_COMMAND_MEMORY | + PCI_COMMAND_MASTER | PCI_COMMAND_SPECIAL); + pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_DEVSEL_MEDIUM); + + pci_conf[0x48] = 0x01; /* Miscellaneous Control 3 */ + pci_conf[0x4a] = 0x04; /* IDE interrupt Routing */ + pci_conf[0x4f] = 0x03; /* DMA/Master Mem Access Control 3 */ + pci_conf[0x50] = 0x2d; /* PnP DMA Request Control */ + pci_conf[0x59] = 0x04; + pci_conf[0x5a] = 0x04; /* KBC/RTC Control*/ + pci_conf[0x5f] = 0x04; + pci_conf[0x77] = 0x10; /* GPIO Control 1/2/3/4 */ + + vt82c->superio_conf.config[0xe0] = 0x3c; + vt82c->superio_conf.config[0xe2] = 0x03; + vt82c->superio_conf.config[0xe3] = 0xfc; + vt82c->superio_conf.config[0xe6] = 0xde; + vt82c->superio_conf.config[0xe7] = 0xfe; + vt82c->superio_conf.config[0xe8] = 0xbe; +} + +/* write config pci function0 registers. PCI-ISA bridge */ +static void vt82c686b_write_config(PCIDevice * d, uint32_t address, + uint32_t val, int len) +{ + VT82C686BState *vt686 = DO_UPCAST(VT82C686BState, dev, d); + + DPRINTF("vt82c686b_write_config address 0x%x val 0x%x len 0x%x \n", + address, val, len); + + pci_default_write_config(d, address, val, len); + if (address == 0x85) { /* enable or disable super IO configure */ + if (val & 0x2) { + /* floppy also uses 0x3f0 and 0x3f1. + * But we do not emulate flopy,so just set it here. */ + isa_unassign_ioport(0x3f0, 2); + register_ioport_read(0x3f0, 2, 1, superio_ioport_readb, + &vt686->superio_conf); + register_ioport_write(0x3f0, 2, 1, superio_ioport_writeb, + &vt686->superio_conf); + } else { + isa_unassign_ioport(0x3f0, 2); + } + } +} + +#define ACPI_DBG_IO_ADDR 0xb044 + +typedef struct VT686PMState { + PCIDevice dev; + uint16_t pmsts; + uint16_t pmen; + uint16_t pmcntrl; + APMState apm; + QEMUTimer *tmr_timer; + int64_t tmr_overflow_time; + PMSMBus smb; + uint32_t smb_io_base; +} VT686PMState; + +typedef struct VT686AC97State { + PCIDevice dev; +} VT686AC97State; + +typedef struct VT686MC97State { + PCIDevice dev; +} VT686MC97State; + +#define RTC_EN (1 << 10) +#define PWRBTN_EN (1 << 8) +#define GBL_EN (1 << 5) +#define TMROF_EN (1 << 0) +#define SUS_EN (1 << 13) + +#define ACPI_ENABLE 0xf1 +#define ACPI_DISABLE 0xf0 + +static uint32_t get_pmtmr(VT686PMState *s) +{ + uint32_t d; + d = muldiv64(qemu_get_clock(vm_clock), PM_TIMER_FREQUENCY, get_ticks_per_sec()); + return d & 0xffffff; +} + +static int get_pmsts(VT686PMState *s) +{ + int64_t d; + int pmsts; + pmsts = s->pmsts; + d = muldiv64(qemu_get_clock(vm_clock), PM_TIMER_FREQUENCY, get_ticks_per_sec()); + if (d >= s->tmr_overflow_time) + s->pmsts |= TMROF_EN; + return pmsts; +} + +static void pm_update_sci(VT686PMState *s) +{ + int sci_level, pmsts; + int64_t expire_time; + + pmsts = get_pmsts(s); + sci_level = (((pmsts & s->pmen) & + (RTC_EN | PWRBTN_EN | GBL_EN | TMROF_EN)) != 0); + qemu_set_irq(s->dev.irq[0], sci_level); + /* schedule a timer interruption if needed */ + if ((s->pmen & TMROF_EN) && !(pmsts & TMROF_EN)) { + expire_time = muldiv64(s->tmr_overflow_time, get_ticks_per_sec(), PM_TIMER_FREQUENCY); + qemu_mod_timer(s->tmr_timer, expire_time); + } else { + qemu_del_timer(s->tmr_timer); + } +} + +static void pm_tmr_timer(void *opaque) +{ + VT686PMState *s = opaque; + pm_update_sci(s); +} + +static void pm_ioport_writew(void *opaque, uint32_t addr, uint32_t val) +{ + VT686PMState *s = opaque; + + addr &= 0x0f; + switch (addr) { + case 0x00: + { + int64_t d; + int pmsts; + pmsts = get_pmsts(s); + if (pmsts & val & TMROF_EN) { + /* if TMRSTS is reset, then compute the new overflow time */ + d = muldiv64(qemu_get_clock(vm_clock), PM_TIMER_FREQUENCY, get_ticks_per_sec()); + s->tmr_overflow_time = (d + 0x800000LL) & ~0x7fffffLL; + } + s->pmsts &= ~val; + pm_update_sci(s); + } + break; + case 0x02: + s->pmen = val; + pm_update_sci(s); + break; + case 0x04: + { + int sus_typ; + s->pmcntrl = val & ~(SUS_EN); + if (val & SUS_EN) { + /* change suspend type */ + sus_typ = (val >> 10) & 3; + switch (sus_typ) { + case 0: /* soft power off */ + qemu_system_shutdown_request(); + break; + default: + break; + } + } + } + break; + default: + break; + } + DPRINTF("PM writew port=0x%04x val=0x%02x\n", addr, val); +} + +static uint32_t pm_ioport_readw(void *opaque, uint32_t addr) +{ + VT686PMState *s = opaque; + uint32_t val; + + addr &= 0x0f; + switch (addr) { + case 0x00: + val = get_pmsts(s); + break; + case 0x02: + val = s->pmen; + break; + case 0x04: + val = s->pmcntrl; + break; + default: + val = 0; + break; + } + DPRINTF("PM readw port=0x%04x val=0x%02x\n", addr, val); + return val; +} + +static void pm_ioport_writel(void *opaque, uint32_t addr, uint32_t val) +{ + addr &= 0x0f; + DPRINTF("PM writel port=0x%04x val=0x%08x\n", addr, val); +} + +static uint32_t pm_ioport_readl(void *opaque, uint32_t addr) +{ + VT686PMState *s = opaque; + uint32_t val; + + addr &= 0x0f; + switch (addr) { + case 0x08: + val = get_pmtmr(s); + break; + default: + val = 0; + break; + } + DPRINTF("PM readl port=0x%04x val=0x%08x\n", addr, val); + return val; +} + +static void pm_io_space_update(VT686PMState *s) +{ + uint32_t pm_io_base; + + if (s->dev.config[0x80] & 1) { + pm_io_base = pci_get_long(s->dev.config + 0x40); + pm_io_base &= 0xffc0; + + /* XXX: need to improve memory and ioport allocation */ + DPRINTF("PM: mapping to 0x%x\n", pm_io_base); + register_ioport_write(pm_io_base, 64, 2, pm_ioport_writew, s); + register_ioport_read(pm_io_base, 64, 2, pm_ioport_readw, s); + register_ioport_write(pm_io_base, 64, 4, pm_ioport_writel, s); + register_ioport_read(pm_io_base, 64, 4, pm_ioport_readl, s); + } +} + +static void pm_write_config(PCIDevice *d, + uint32_t address, uint32_t val, int len) +{ + DPRINTF("pm_write_config address 0x%x val 0x%x len 0x%x \n", + address, val, len); + pci_default_write_config(d, address, val, len); +} + +static int vmstate_acpi_post_load(void *opaque, int version_id) +{ + VT686PMState *s = opaque; + + pm_io_space_update(s); + return 0; +} + +static const VMStateDescription vmstate_acpi = { + .name = "vt82c686b_pm", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .post_load = vmstate_acpi_post_load, + .fields = (VMStateField []) { + VMSTATE_PCI_DEVICE(dev, VT686PMState), + VMSTATE_UINT16(pmsts, VT686PMState), + VMSTATE_UINT16(pmen, VT686PMState), + VMSTATE_UINT16(pmcntrl, VT686PMState), + VMSTATE_STRUCT(apm, VT686PMState, 0, vmstate_apm, APMState), + VMSTATE_TIMER(tmr_timer, VT686PMState), + VMSTATE_INT64(tmr_overflow_time, VT686PMState), + VMSTATE_END_OF_LIST() + } +}; + +/* + * TODO: vt82c686b_ac97_init() and vt82c686b_mc97_init() + * just register a PCI device now, functionalities will be implemented later. + */ + +static int vt82c686b_ac97_initfn(PCIDevice *dev) +{ + VT686AC97State *s = DO_UPCAST(VT686AC97State, dev, dev); + uint8_t *pci_conf = s->dev.config; + + pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_VIA); + pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_VIA_AC97); + pci_config_set_class(pci_conf, PCI_CLASS_MULTIMEDIA_AUDIO); + pci_config_set_revision(pci_conf, 0x50); + + pci_set_word(pci_conf + PCI_COMMAND, PCI_COMMAND_INVALIDATE | + PCI_COMMAND_PARITY); + pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_CAP_LIST | + PCI_STATUS_DEVSEL_MEDIUM); + pci_set_long(pci_conf + PCI_INTERRUPT_PIN, 0x03); + + return 0; +} + +void vt82c686b_ac97_init(PCIBus *bus, int devfn) +{ + PCIDevice *dev; + + dev = pci_create(bus, devfn, "VT82C686B_AC97"); + qdev_init_nofail(&dev->qdev); +} + +static PCIDeviceInfo via_ac97_info = { + .qdev.name = "VT82C686B_AC97", + .qdev.desc = "AC97", + .qdev.size = sizeof(VT686AC97State), + .init = vt82c686b_ac97_initfn, +}; + +static void vt82c686b_ac97_register(void) +{ + pci_qdev_register(&via_ac97_info); +} + +device_init(vt82c686b_ac97_register); + +static int vt82c686b_mc97_initfn(PCIDevice *dev) +{ + VT686MC97State *s = DO_UPCAST(VT686MC97State, dev, dev); + uint8_t *pci_conf = s->dev.config; + + pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_VIA); + pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_VIA_MC97); + pci_config_set_class(pci_conf, PCI_CLASS_COMMUNICATION_OTHER); + pci_config_set_revision(pci_conf, 0x30); + + pci_set_word(pci_conf + PCI_COMMAND, PCI_COMMAND_INVALIDATE | + PCI_COMMAND_VGA_PALETTE); + pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_DEVSEL_MEDIUM); + pci_set_long(pci_conf + PCI_INTERRUPT_PIN, 0x03); + + return 0; +} + +void vt82c686b_mc97_init(PCIBus *bus, int devfn) +{ + PCIDevice *dev; + + dev = pci_create(bus, devfn, "VT82C686B_MC97"); + qdev_init_nofail(&dev->qdev); +} + +static PCIDeviceInfo via_mc97_info = { + .qdev.name = "VT82C686B_MC97", + .qdev.desc = "MC97", + .qdev.size = sizeof(VT686MC97State), + .init = vt82c686b_mc97_initfn, +}; + +static void vt82c686b_mc97_register(void) +{ + pci_qdev_register(&via_mc97_info); +} + +device_init(vt82c686b_mc97_register); + +/* vt82c686 pm init */ +static int vt82c686b_pm_initfn(PCIDevice *dev) +{ + VT686PMState *s = DO_UPCAST(VT686PMState, dev, dev); + uint8_t *pci_conf; + + pci_conf = s->dev.config; + pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_VIA); + pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_VIA_ACPI); + pci_config_set_class(pci_conf, PCI_CLASS_BRIDGE_OTHER); + pci_config_set_revision(pci_conf, 0x40); + + pci_set_word(pci_conf + PCI_COMMAND, 0); + pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_FAST_BACK | + PCI_STATUS_DEVSEL_MEDIUM); + + /* 0x48-0x4B is Power Management I/O Base */ + pci_set_long(pci_conf + 0x48, 0x00000001); + + /* SMB ports:0xeee0~0xeeef */ + s->smb_io_base =((s->smb_io_base & 0xfff0) + 0x0); + pci_conf[0x90] = s->smb_io_base | 1; + pci_conf[0x91] = s->smb_io_base >> 8; + pci_conf[0xd2] = 0x90; + register_ioport_write(s->smb_io_base, 0xf, 1, smb_ioport_writeb, &s->smb); + register_ioport_read(s->smb_io_base, 0xf, 1, smb_ioport_readb, &s->smb); + + apm_init(&s->apm, NULL, s); + + s->tmr_timer = qemu_new_timer(vm_clock, pm_tmr_timer, s); + + pm_smbus_init(&s->dev.qdev, &s->smb); + + return 0; +} + +i2c_bus *vt82c686b_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base, + qemu_irq sci_irq) +{ + PCIDevice *dev; + VT686PMState *s; + + dev = pci_create(bus, devfn, "VT82C686B_PM"); + qdev_prop_set_uint32(&dev->qdev, "smb_io_base", smb_io_base); + + s = DO_UPCAST(VT686PMState, dev, dev); + + qdev_init_nofail(&dev->qdev); + + return s->smb.smbus; +} + +static PCIDeviceInfo via_pm_info = { + .qdev.name = "VT82C686B_PM", + .qdev.desc = "PM", + .qdev.size = sizeof(VT686PMState), + .qdev.vmsd = &vmstate_acpi, + .init = vt82c686b_pm_initfn, + .config_write = pm_write_config, + .qdev.props = (Property[]) { + DEFINE_PROP_UINT32("smb_io_base", VT686PMState, smb_io_base, 0), + DEFINE_PROP_END_OF_LIST(), + } +}; + +static void vt82c686b_pm_register(void) +{ + pci_qdev_register(&via_pm_info); +} + +device_init(vt82c686b_pm_register); + +static const VMStateDescription vmstate_via = { + .name = "vt82c686b", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField []) { + VMSTATE_PCI_DEVICE(dev, VT82C686BState), + VMSTATE_END_OF_LIST() + } +}; + +/* init the PCI-to-ISA bridge */ +static int vt82c686b_initfn(PCIDevice *d) +{ + uint8_t *pci_conf; + uint8_t *wmask; + int i; + + isa_bus_new(&d->qdev); + + pci_conf = d->config; + pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_VIA); + pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_VIA_ISA_BRIDGE); + pci_config_set_class(pci_conf, PCI_CLASS_BRIDGE_ISA); + pci_config_set_prog_interface(pci_conf, 0x0); + pci_config_set_revision(pci_conf,0x40); /* Revision 4.0 */ + + wmask = d->wmask; + for (i = 0x00; i < 0xff; i++) { + if (i<=0x03 || (i>=0x08 && i<=0x3f)) { + wmask[i] = 0x00; + } + } + + qemu_register_reset(vt82c686b_reset, d); + + return 0; +} + +int vt82c686b_init(PCIBus *bus, int devfn) +{ + PCIDevice *d; + + d = pci_create_simple_multifunction(bus, devfn, true, "VT82C686B"); + + return d->devfn; +} + +static PCIDeviceInfo via_info = { + .qdev.name = "VT82C686B", + .qdev.desc = "ISA bridge", + .qdev.size = sizeof(VT82C686BState), + .qdev.vmsd = &vmstate_via, + .qdev.no_user = 1, + .init = vt82c686b_initfn, + .config_write = vt82c686b_write_config, +}; + +static void vt82c686b_register(void) +{ + pci_qdev_register(&via_info); +} +device_init(vt82c686b_register); diff -Nru qemu-kvm-0.12.5+noroms/hw/vt82c686.h qemu-kvm-0.14.1/hw/vt82c686.h --- qemu-kvm-0.12.5+noroms/hw/vt82c686.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/vt82c686.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,11 @@ +#ifndef HW_VT82C686_H +#define HW_VT82C686_H + +/* vt82c686.c */ +int vt82c686b_init(PCIBus * bus, int devfn); +void vt82c686b_ac97_init(PCIBus *bus, int devfn); +void vt82c686b_mc97_init(PCIBus *bus, int devfn); +i2c_bus *vt82c686b_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base, + qemu_irq sci_irq); + +#endif diff -Nru qemu-kvm-0.12.5+noroms/hw/watchdog.c qemu-kvm-0.14.1/hw/watchdog.c --- qemu-kvm-0.12.5+noroms/hw/watchdog.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/watchdog.c 2011-05-11 13:29:46.000000000 +0000 @@ -23,6 +23,8 @@ #include "qemu-option.h" #include "qemu-config.h" #include "qemu-queue.h" +#include "qemu-objects.h" +#include "monitor.h" #include "sysemu.h" #include "hw/watchdog.h" @@ -64,7 +66,7 @@ QLIST_FOREACH(model, &watchdog_list, entry) { if (strcasecmp(model->wdt_name, p) == 0) { /* add the device */ - opts = qemu_opts_create(&qemu_device_opts, NULL, 0); + opts = qemu_opts_create(qemu_find_opts("device"), NULL, 0); qemu_opt_set(opts, "driver", p); return 0; } @@ -98,6 +100,15 @@ return 0; } +static void watchdog_mon_event(const char *action) +{ + QObject *data; + + data = qobject_from_jsonf("{ 'action': %s }", action); + monitor_protocol_event(QEVENT_WATCHDOG, data); + qobject_decref(data); +} + /* This actually performs the "action" once a watchdog has expired, * ie. reboot, shutdown, exit, etc. */ @@ -105,26 +116,32 @@ { switch(watchdog_action) { case WDT_RESET: /* same as 'system_reset' in monitor */ + watchdog_mon_event("reset"); qemu_system_reset_request(); break; case WDT_SHUTDOWN: /* same as 'system_powerdown' in monitor */ + watchdog_mon_event("shutdown"); qemu_system_powerdown_request(); break; case WDT_POWEROFF: /* same as 'quit' command in monitor */ + watchdog_mon_event("poweroff"); exit(0); break; case WDT_PAUSE: /* same as 'stop' command in monitor */ + watchdog_mon_event("pause"); vm_stop(0); break; case WDT_DEBUG: + watchdog_mon_event("debug"); fprintf(stderr, "watchdog: timer fired\n"); break; case WDT_NONE: + watchdog_mon_event("none"); break; } } diff -Nru qemu-kvm-0.12.5+noroms/hw/watchdog.h qemu-kvm-0.14.1/hw/watchdog.h --- qemu-kvm-0.12.5+noroms/hw/watchdog.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/watchdog.h 2011-05-11 13:29:46.000000000 +0000 @@ -35,9 +35,9 @@ typedef struct WatchdogTimerModel WatchdogTimerModel; /* in hw/watchdog.c */ -extern int select_watchdog(const char *p); -extern int select_watchdog_action(const char *action); -extern void watchdog_add_model(WatchdogTimerModel *model); -extern void watchdog_perform_action(void); +int select_watchdog(const char *p); +int select_watchdog_action(const char *action); +void watchdog_add_model(WatchdogTimerModel *model); +void watchdog_perform_action(void); #endif /* QEMU_WATCHDOG_H */ diff -Nru qemu-kvm-0.12.5+noroms/hw/wdt_i6300esb.c qemu-kvm-0.14.1/hw/wdt_i6300esb.c --- qemu-kvm-0.12.5+noroms/hw/wdt_i6300esb.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/wdt_i6300esb.c 2011-05-11 13:29:46.000000000 +0000 @@ -140,14 +140,27 @@ qemu_del_timer(d->timer); } -static void i6300esb_reset(I6300State *d) +static void i6300esb_reset(DeviceState *dev) { - /* XXX We should probably reset other parts of the state here, - * but we should also reset our state on general machine reset - * too. For now just disable the timer so it doesn't fire - * again after the reboot. - */ + PCIDevice *pdev = DO_UPCAST(PCIDevice, qdev, dev); + I6300State *d = DO_UPCAST(I6300State, dev, pdev); + + i6300esb_debug("I6300State = %p\n", d); + i6300esb_disable_timer(d); + + /* NB: Don't change d->previous_reboot_flag in this function. */ + + d->reboot_enabled = 1; + d->clock_scale = CLOCK_SCALE_1KHZ; + d->int_type = INT_TYPE_IRQ; + d->free_run = 0; + d->locked = 0; + d->enabled = 0; + d->timer1_preload = 0xfffff; + d->timer2_preload = 0xfffff; + d->stage = 1; + d->unlock_state = 0; } /* This function is called when the watchdog expires. Note that @@ -181,7 +194,7 @@ if (d->reboot_enabled) { d->previous_reboot_flag = 1; watchdog_perform_action(); /* This reboots, exits, etc */ - i6300esb_reset(d); + i6300esb_reset(&d->dev.qdev); } /* In "free running mode" we start stage 1 again. */ @@ -361,7 +374,8 @@ i6300esb_debug("addr = %"FMT_PCIBUS", size = %"FMT_PCIBUS", type = %d\n", addr, size, type); - io_mem = cpu_register_io_memory(mem_read, mem_write, d); + io_mem = cpu_register_io_memory(mem_read, mem_write, d, + DEVICE_NATIVE_ENDIAN); cpu_register_physical_memory (addr, 0x10, io_mem); /* qemu_register_coalesced_mmio (addr, 0x10); ? */ } @@ -394,24 +408,15 @@ I6300State *d = DO_UPCAST(I6300State, dev, dev); uint8_t *pci_conf; - d->reboot_enabled = 1; - d->clock_scale = CLOCK_SCALE_1KHZ; - d->int_type = INT_TYPE_IRQ; - d->free_run = 0; - d->locked = 0; - d->enabled = 0; + i6300esb_debug("I6300State = %p\n", d); + d->timer = qemu_new_timer(vm_clock, i6300esb_timer_expired, d); - d->timer1_preload = 0xfffff; - d->timer2_preload = 0xfffff; - d->stage = 1; - d->unlock_state = 0; d->previous_reboot_flag = 0; pci_conf = d->dev.config; pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_INTEL); pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_INTEL_ESB_9); pci_config_set_class(pci_conf, PCI_CLASS_SYSTEM_OTHER); - pci_conf[0x0e] = 0x00; pci_register_bar(&d->dev, 0, 0x10, PCI_BASE_ADDRESS_SPACE_MEMORY, i6300esb_map); @@ -428,6 +433,7 @@ .qdev.name = "i6300esb", .qdev.size = sizeof(I6300State), .qdev.vmsd = &vmstate_i6300esb, + .qdev.reset = i6300esb_reset, .config_read = i6300esb_config_read, .config_write = i6300esb_config_write, .init = i6300esb_init, diff -Nru qemu-kvm-0.12.5+noroms/hw/wdt_ib700.c qemu-kvm-0.14.1/hw/wdt_ib700.c --- qemu-kvm-0.12.5+noroms/hw/wdt_ib700.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/wdt_ib700.c 2011-05-11 13:29:46.000000000 +0000 @@ -53,7 +53,7 @@ 30, 28, 26, 24, 22, 20, 18, 16, 14, 12, 10, 8, 6, 4, 2, 0 }; - int64 timeout; + int64_t timeout; ib700_debug("addr = %x, data = %x\n", addr, data); @@ -97,6 +97,8 @@ { IB700State *s = DO_UPCAST(IB700State, dev, dev); + ib700_debug("watchdog init\n"); + s->timer = qemu_new_timer(vm_clock, ib700_timer_expired, s); register_ioport_write(0x441, 2, 1, ib700_write_disable_reg, s); register_ioport_write(0x443, 2, 1, ib700_write_enable_reg, s); @@ -104,16 +106,26 @@ return 0; } +static void wdt_ib700_reset(DeviceState *dev) +{ + IB700State *s = DO_UPCAST(IB700State, dev.qdev, dev); + + ib700_debug("watchdog reset\n"); + + qemu_del_timer(s->timer); +} + static WatchdogTimerModel model = { .wdt_name = "ib700", .wdt_description = "iBASE 700", }; static ISADeviceInfo wdt_ib700_info = { - .qdev.name = "ib700", - .qdev.size = sizeof(IB700State), - .qdev.vmsd = &vmstate_ib700, - .init = wdt_ib700_init, + .qdev.name = "ib700", + .qdev.size = sizeof(IB700State), + .qdev.vmsd = &vmstate_ib700, + .qdev.reset = wdt_ib700_reset, + .init = wdt_ib700_init, }; static void wdt_ib700_register_devices(void) diff -Nru qemu-kvm-0.12.5+noroms/hw/wm8750.c qemu-kvm-0.14.1/hw/wm8750.c --- qemu-kvm-0.12.5+noroms/hw/wm8750.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/wm8750.c 2011-05-11 13:29:46.000000000 +0000 @@ -62,12 +62,11 @@ static inline void wm8750_in_load(WM8750State *s) { - int acquired; if (s->idx_in + s->req_in <= sizeof(s->data_in)) return; s->idx_in = audio_MAX(0, (int) sizeof(s->data_in) - s->req_in); - acquired = AUD_read(*s->in[0], s->data_in + s->idx_in, - sizeof(s->data_in) - s->idx_in); + AUD_read(*s->in[0], s->data_in + s->idx_in, + sizeof(s->data_in) - s->idx_in); } static inline void wm8750_out_flush(WM8750State *s) @@ -172,7 +171,6 @@ int i; struct audsettings in_fmt; struct audsettings out_fmt; - struct audsettings monoout_fmt; wm8750_out_flush(s); @@ -213,10 +211,6 @@ out_fmt.nchannels = 2; out_fmt.freq = s->dac_hz; out_fmt.fmt = AUD_FMT_S16; - monoout_fmt.endianness = 0; - monoout_fmt.nchannels = 1; - monoout_fmt.freq = s->rate->dac_hz; - monoout_fmt.fmt = AUD_FMT_S16; s->dac_voice[0] = AUD_open_out(&s->card, s->dac_voice[0], CODEC ".speaker", s, wm8750_audio_out_cb, &out_fmt); diff -Nru qemu-kvm-0.12.5+noroms/hw/xen_backend.h qemu-kvm-0.14.1/hw/xen_backend.h --- qemu-kvm-0.12.5+noroms/hw/xen_backend.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/xen_backend.h 2011-05-11 13:29:46.000000000 +0000 @@ -4,7 +4,6 @@ #include "xen_common.h" #include "sysemu.h" #include "net.h" -#include "block_int.h" /* ------------------------------------------------------------- */ @@ -85,7 +84,7 @@ void xen_be_unbind_evtchn(struct XenDevice *xendev); int xen_be_send_notify(struct XenDevice *xendev); void xen_be_printf(struct XenDevice *xendev, int msg_level, const char *fmt, ...) - __attribute__ ((format(printf, 3, 4))); + GCC_FMT_ATTR(3, 4); /* actual backend drivers */ extern struct XenDevOps xen_console_ops; /* xen_console.c */ diff -Nru qemu-kvm-0.12.5+noroms/hw/xen_devconfig.c qemu-kvm-0.14.1/hw/xen_devconfig.c --- qemu-kvm-0.12.5+noroms/hw/xen_devconfig.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/xen_devconfig.c 2011-05-11 13:29:46.000000000 +0000 @@ -1,4 +1,6 @@ #include "xen_backend.h" +#include "blockdev.h" +#include "block_int.h" /* XXX */ /* ------------------------------------------------------------- */ diff -Nru qemu-kvm-0.12.5+noroms/hw/xen_disk.c qemu-kvm-0.14.1/hw/xen_disk.c --- qemu-kvm-0.12.5+noroms/hw/xen_disk.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/xen_disk.c 2011-05-11 13:29:46.000000000 +0000 @@ -41,6 +41,7 @@ #include "qemu-char.h" #include "xen_blkif.h" #include "xen_backend.h" +#include "blockdev.h" /* ------------------------------------------------------------- */ @@ -180,6 +181,10 @@ ioreq->prot = PROT_WRITE; /* to memory */ break; case BLKIF_OP_WRITE_BARRIER: + if (!ioreq->req.nr_segments) { + ioreq->presync = 1; + return 0; + } if (!syncwrite) ioreq->presync = ioreq->postsync = 1; /* fall through */ @@ -304,7 +309,7 @@ int i, rc, len = 0; off_t pos; - if (ioreq_map(ioreq) == -1) + if (ioreq->req.nr_segments && ioreq_map(ioreq) == -1) goto err; if (ioreq->presync) bdrv_flush(blkdev->bs); @@ -328,6 +333,8 @@ break; case BLKIF_OP_WRITE: case BLKIF_OP_WRITE_BARRIER: + if (!ioreq->req.nr_segments) + break; pos = ioreq->start; for (i = 0; i < ioreq->v.niov; i++) { rc = bdrv_write(blkdev->bs, pos / BLOCK_SIZE, @@ -385,7 +392,7 @@ { struct XenBlkDev *blkdev = ioreq->blkdev; - if (ioreq_map(ioreq) == -1) + if (ioreq->req.nr_segments && ioreq_map(ioreq) == -1) goto err; ioreq->aio_inflight++; @@ -402,6 +409,8 @@ case BLKIF_OP_WRITE: case BLKIF_OP_WRITE_BARRIER: ioreq->aio_inflight++; + if (!ioreq->req.nr_segments) + break; bdrv_aio_writev(blkdev->bs, ioreq->start / BLOCK_SIZE, &ioreq->v, ioreq->v.size / BLOCK_SIZE, qemu_aio_complete, ioreq); @@ -575,7 +584,7 @@ static int blk_init(struct XenDevice *xendev) { struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); - int index, mode, qflags, have_barriers, info = 0; + int index, qflags, have_barriers, info = 0; char *h; /* read xenstore entries */ @@ -609,11 +618,9 @@ /* read-only ? */ if (strcmp(blkdev->mode, "w") == 0) { - mode = O_RDWR; qflags = BDRV_O_RDWR; } else { - mode = O_RDONLY; - qflags = BDRV_O_RDONLY; + qflags = 0; info |= VDISK_READONLY; } @@ -627,17 +634,12 @@ if (!blkdev->dinfo) { /* setup via xenbus -> create new block driver instance */ xen_be_printf(&blkdev->xendev, 2, "create new bdrv (xenbus setup)\n"); - blkdev->bs = bdrv_new(blkdev->dev); - if (blkdev->bs) { - if (bdrv_open2(blkdev->bs, blkdev->filename, qflags, - bdrv_find_whitelisted_format(blkdev->fileproto)) - != 0) { - bdrv_delete(blkdev->bs); - blkdev->bs = NULL; - } - } - if (!blkdev->bs) - return -1; + blkdev->bs = bdrv_new(blkdev->dev); + if (bdrv_open(blkdev->bs, blkdev->filename, qflags, + bdrv_find_whitelisted_format(blkdev->fileproto)) != 0) { + bdrv_delete(blkdev->bs); + return -1; + } } else { /* setup via qemu cmdline -> already setup for us */ xen_be_printf(&blkdev->xendev, 2, "get configured bdrv (cmdline setup)\n"); diff -Nru qemu-kvm-0.12.5+noroms/hw/xen_domainbuild.c qemu-kvm-0.14.1/hw/xen_domainbuild.c --- qemu-kvm-0.12.5+noroms/hw/xen_domainbuild.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/xen_domainbuild.c 2011-05-11 13:29:46.000000000 +0000 @@ -3,6 +3,7 @@ #include "xen_domainbuild.h" #include "sysemu.h" #include "qemu-timer.h" +#include "qemu-log.h" #include @@ -156,15 +157,18 @@ return; } -static void xen_domain_watcher(void) +static int xen_domain_watcher(void) { int qemu_running = 1; int fd[2], i, n, rc; char byte; - pipe(fd); + if (pipe(fd) != 0) { + qemu_log("%s: Huh? pipe error: %s\n", __FUNCTION__, strerror(errno)); + return -1; + } if (fork() != 0) - return; /* not child */ + return 0; /* not child */ /* close all file handles, except stdio/out/err, * our watch pipe and the xen interface handle */ @@ -238,7 +242,9 @@ } qemu_log("xen: created domain %d\n", xen_domid); atexit(xen_domain_cleanup); - xen_domain_watcher(); + if (xen_domain_watcher() == -1) { + goto err; + } xenstore_domain_init1(kernel, ramdisk, cmdline); diff -Nru qemu-kvm-0.12.5+noroms/hw/xenfb.c qemu-kvm-0.14.1/hw/xenfb.c --- qemu-kvm-0.12.5+noroms/hw/xenfb.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/xenfb.c 2011-05-11 13:29:46.000000000 +0000 @@ -29,7 +29,6 @@ #include #include #include -#include #include #include #include @@ -983,12 +982,14 @@ wait_more: i++; - main_loop_wait(10); /* miliseconds */ + main_loop_wait(true); xfb = xen_be_find_xendev("vfb", domid, 0); xin = xen_be_find_xendev("vkbd", domid, 0); if (!xfb || !xin) { - if (i < 256) + if (i < 256) { + usleep(10000); goto wait_more; + } xen_be_printf(NULL, 1, "displaystate setup failed\n"); return; } diff -Nru qemu-kvm-0.12.5+noroms/hw/xen_machine_pv.c qemu-kvm-0.14.1/hw/xen_machine_pv.c --- qemu-kvm-0.12.5+noroms/hw/xen_machine_pv.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/xen_machine_pv.c 2011-05-11 13:29:46.000000000 +0000 @@ -28,9 +28,7 @@ #include "boards.h" #include "xen_backend.h" #include "xen_domainbuild.h" - -uint32_t xen_domid; -enum xen_mode xen_mode = XEN_EMULATE; +#include "blockdev.h" static void xen_init_pv(ram_addr_t ram_size, const char *boot_device, diff -Nru qemu-kvm-0.12.5+noroms/hw/xilinx_ethlite.c qemu-kvm-0.14.1/hw/xilinx_ethlite.c --- qemu-kvm-0.12.5+noroms/hw/xilinx_ethlite.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/xilinx_ethlite.c 2011-05-11 13:29:46.000000000 +0000 @@ -224,7 +224,7 @@ sysbus_init_irq(dev, &s->irq); s->rxbuf = 0; - regs = cpu_register_io_memory(eth_read, eth_write, s); + regs = cpu_register_io_memory(eth_read, eth_write, s, DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, R_MAX * 4, regs); qemu_macaddr_default_if_unset(&s->conf.macaddr); diff -Nru qemu-kvm-0.12.5+noroms/hw/xilinx_intc.c qemu-kvm-0.14.1/hw/xilinx_intc.c --- qemu-kvm-0.12.5+noroms/hw/xilinx_intc.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/xilinx_intc.c 2011-05-11 13:29:46.000000000 +0000 @@ -153,7 +153,7 @@ qdev_init_gpio_in(&dev->qdev, irq_handler, 32); sysbus_init_irq(dev, &p->parent_irq); - pic_regs = cpu_register_io_memory(pic_read, pic_write, p); + pic_regs = cpu_register_io_memory(pic_read, pic_write, p, DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, R_MAX * 4, pic_regs); return 0; } diff -Nru qemu-kvm-0.12.5+noroms/hw/xilinx_timer.c qemu-kvm-0.14.1/hw/xilinx_timer.c --- qemu-kvm-0.12.5+noroms/hw/xilinx_timer.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/xilinx_timer.c 2011-05-11 13:29:46.000000000 +0000 @@ -210,7 +210,8 @@ ptimer_set_freq(xt->ptimer, t->freq_hz); } - timer_regs = cpu_register_io_memory(timer_read, timer_write, t); + timer_regs = cpu_register_io_memory(timer_read, timer_write, t, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, R_MAX * 4 * t->nr_timers, timer_regs); return 0; } diff -Nru qemu-kvm-0.12.5+noroms/hw/xilinx_uartlite.c qemu-kvm-0.14.1/hw/xilinx_uartlite.c --- qemu-kvm-0.12.5+noroms/hw/xilinx_uartlite.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/xilinx_uartlite.c 2011-05-11 13:29:46.000000000 +0000 @@ -201,7 +201,8 @@ sysbus_init_irq(dev, &s->irq); uart_update_status(s); - uart_regs = cpu_register_io_memory(uart_read, uart_write, s); + uart_regs = cpu_register_io_memory(uart_read, uart_write, s, + DEVICE_NATIVE_ENDIAN); sysbus_init_mmio(dev, R_MAX * 4, uart_regs); s->chr = qdev_init_chardev(&dev->qdev); diff -Nru qemu-kvm-0.12.5+noroms/hw/xio3130_downstream.c qemu-kvm-0.14.1/hw/xio3130_downstream.c --- qemu-kvm-0.12.5+noroms/hw/xio3130_downstream.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/xio3130_downstream.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,211 @@ +/* + * x3130_downstream.c + * TI X3130 pci express downstream port switch + * + * Copyright (c) 2010 Isaku Yamahata + * VA Linux Systems Japan K.K. + * + * 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, see . + */ + +#include "pci_ids.h" +#include "msi.h" +#include "pcie.h" +#include "xio3130_downstream.h" + +#define PCI_DEVICE_ID_TI_XIO3130D 0x8233 /* downstream port */ +#define XIO3130_REVISION 0x1 +#define XIO3130_MSI_OFFSET 0x70 +#define XIO3130_MSI_SUPPORTED_FLAGS PCI_MSI_FLAGS_64BIT +#define XIO3130_MSI_NR_VECTOR 1 +#define XIO3130_SSVID_OFFSET 0x80 +#define XIO3130_SSVID_SVID 0 +#define XIO3130_SSVID_SSID 0 +#define XIO3130_EXP_OFFSET 0x90 +#define XIO3130_AER_OFFSET 0x100 + +static void xio3130_downstream_write_config(PCIDevice *d, uint32_t address, + uint32_t val, int len) +{ + pci_bridge_write_config(d, address, val, len); + pcie_cap_flr_write_config(d, address, val, len); + pcie_cap_slot_write_config(d, address, val, len); + msi_write_config(d, address, val, len); + pcie_aer_write_config(d, address, val, len); +} + +static void xio3130_downstream_reset(DeviceState *qdev) +{ + PCIDevice *d = DO_UPCAST(PCIDevice, qdev, qdev); + msi_reset(d); + pcie_cap_deverr_reset(d); + pcie_cap_slot_reset(d); + pcie_cap_ari_reset(d); + pci_bridge_reset(qdev); +} + +static int xio3130_downstream_initfn(PCIDevice *d) +{ + PCIBridge* br = DO_UPCAST(PCIBridge, dev, d); + PCIEPort *p = DO_UPCAST(PCIEPort, br, br); + PCIESlot *s = DO_UPCAST(PCIESlot, port, p); + int rc; + int tmp; + + rc = pci_bridge_initfn(d); + if (rc < 0) { + return rc; + } + + pcie_port_init_reg(d); + pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_TI); + pci_config_set_device_id(d->config, PCI_DEVICE_ID_TI_XIO3130D); + d->config[PCI_REVISION_ID] = XIO3130_REVISION; + + rc = msi_init(d, XIO3130_MSI_OFFSET, XIO3130_MSI_NR_VECTOR, + XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_64BIT, + XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT); + if (rc < 0) { + goto err_bridge; + } + rc = pci_bridge_ssvid_init(d, XIO3130_SSVID_OFFSET, + XIO3130_SSVID_SVID, XIO3130_SSVID_SSID); + if (rc < 0) { + goto err_bridge; + } + rc = pcie_cap_init(d, XIO3130_EXP_OFFSET, PCI_EXP_TYPE_DOWNSTREAM, + p->port); + if (rc < 0) { + goto err_msi; + } + pcie_cap_flr_init(d); + pcie_cap_deverr_init(d); + pcie_cap_slot_init(d, s->slot); + pcie_chassis_create(s->chassis); + rc = pcie_chassis_add_slot(s); + if (rc < 0) { + goto err_pcie_cap; + } + pcie_cap_ari_init(d); + rc = pcie_aer_init(d, XIO3130_AER_OFFSET); + if (rc < 0) { + goto err; + } + + return 0; + +err: + pcie_chassis_del_slot(s); +err_pcie_cap: + pcie_cap_exit(d); +err_msi: + msi_uninit(d); +err_bridge: + tmp = pci_bridge_exitfn(d); + assert(!tmp); + return rc; +} + +static int xio3130_downstream_exitfn(PCIDevice *d) +{ + PCIBridge* br = DO_UPCAST(PCIBridge, dev, d); + PCIEPort *p = DO_UPCAST(PCIEPort, br, br); + PCIESlot *s = DO_UPCAST(PCIESlot, port, p); + + pcie_aer_exit(d); + pcie_chassis_del_slot(s); + pcie_cap_exit(d); + msi_uninit(d); + return pci_bridge_exitfn(d); +} + +PCIESlot *xio3130_downstream_init(PCIBus *bus, int devfn, bool multifunction, + const char *bus_name, pci_map_irq_fn map_irq, + uint8_t port, uint8_t chassis, + uint16_t slot) +{ + PCIDevice *d; + PCIBridge *br; + DeviceState *qdev; + + d = pci_create_multifunction(bus, devfn, multifunction, + "xio3130-downstream"); + if (!d) { + return NULL; + } + br = DO_UPCAST(PCIBridge, dev, d); + + qdev = &br->dev.qdev; + pci_bridge_map_irq(br, bus_name, map_irq); + qdev_prop_set_uint8(qdev, "port", port); + qdev_prop_set_uint8(qdev, "chassis", chassis); + qdev_prop_set_uint16(qdev, "slot", slot); + qdev_init_nofail(qdev); + + return DO_UPCAST(PCIESlot, port, DO_UPCAST(PCIEPort, br, br)); +} + +static const VMStateDescription vmstate_xio3130_downstream = { + .name = "xio3130-express-downstream-port", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .post_load = pcie_cap_slot_post_load, + .fields = (VMStateField[]) { + VMSTATE_PCIE_DEVICE(port.br.dev, PCIESlot), + VMSTATE_STRUCT(port.br.dev.exp.aer_log, PCIESlot, 0, + vmstate_pcie_aer_log, PCIEAERLog), + VMSTATE_END_OF_LIST() + } +}; + +static PCIDeviceInfo xio3130_downstream_info = { + .qdev.name = "xio3130-downstream", + .qdev.desc = "TI X3130 Downstream Port of PCI Express Switch", + .qdev.size = sizeof(PCIESlot), + .qdev.reset = xio3130_downstream_reset, + .qdev.vmsd = &vmstate_xio3130_downstream, + + .is_express = 1, + .is_bridge = 1, + .config_write = xio3130_downstream_write_config, + .init = xio3130_downstream_initfn, + .exit = xio3130_downstream_exitfn, + + .qdev.props = (Property[]) { + DEFINE_PROP_UINT8("port", PCIESlot, port.port, 0), + DEFINE_PROP_UINT8("chassis", PCIESlot, chassis, 0), + DEFINE_PROP_UINT16("slot", PCIESlot, slot, 0), + DEFINE_PROP_UINT16("aer_log_max", PCIESlot, + port.br.dev.exp.aer_log.log_max, + PCIE_AER_LOG_MAX_DEFAULT), + DEFINE_PROP_END_OF_LIST(), + } +}; + +static void xio3130_downstream_register(void) +{ + pci_qdev_register(&xio3130_downstream_info); +} + +device_init(xio3130_downstream_register); + +/* + * Local variables: + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 8 + * indent-tab-mode: nil + * End: + */ diff -Nru qemu-kvm-0.12.5+noroms/hw/xio3130_downstream.h qemu-kvm-0.14.1/hw/xio3130_downstream.h --- qemu-kvm-0.12.5+noroms/hw/xio3130_downstream.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/xio3130_downstream.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,11 @@ +#ifndef QEMU_XIO3130_DOWNSTREAM_H +#define QEMU_XIO3130_DOWNSTREAM_H + +#include "pcie_port.h" + +PCIESlot *xio3130_downstream_init(PCIBus *bus, int devfn, bool multifunction, + const char *bus_name, pci_map_irq_fn map_irq, + uint8_t port, uint8_t chassis, + uint16_t slot); + +#endif /* QEMU_XIO3130_DOWNSTREAM_H */ diff -Nru qemu-kvm-0.12.5+noroms/hw/xio3130_upstream.c qemu-kvm-0.14.1/hw/xio3130_upstream.c --- qemu-kvm-0.12.5+noroms/hw/xio3130_upstream.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/xio3130_upstream.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,186 @@ +/* + * xio3130_upstream.c + * TI X3130 pci express upstream port switch + * + * Copyright (c) 2010 Isaku Yamahata + * VA Linux Systems Japan K.K. + * + * 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, see . + */ + +#include "pci_ids.h" +#include "msi.h" +#include "pcie.h" +#include "xio3130_upstream.h" + +#define PCI_DEVICE_ID_TI_XIO3130U 0x8232 /* upstream port */ +#define XIO3130_REVISION 0x2 +#define XIO3130_MSI_OFFSET 0x70 +#define XIO3130_MSI_SUPPORTED_FLAGS PCI_MSI_FLAGS_64BIT +#define XIO3130_MSI_NR_VECTOR 1 +#define XIO3130_SSVID_OFFSET 0x80 +#define XIO3130_SSVID_SVID 0 +#define XIO3130_SSVID_SSID 0 +#define XIO3130_EXP_OFFSET 0x90 +#define XIO3130_AER_OFFSET 0x100 + +static void xio3130_upstream_write_config(PCIDevice *d, uint32_t address, + uint32_t val, int len) +{ + pci_bridge_write_config(d, address, val, len); + pcie_cap_flr_write_config(d, address, val, len); + msi_write_config(d, address, val, len); + pcie_aer_write_config(d, address, val, len); +} + +static void xio3130_upstream_reset(DeviceState *qdev) +{ + PCIDevice *d = DO_UPCAST(PCIDevice, qdev, qdev); + msi_reset(d); + pci_bridge_reset(qdev); + pcie_cap_deverr_reset(d); +} + +static int xio3130_upstream_initfn(PCIDevice *d) +{ + PCIBridge* br = DO_UPCAST(PCIBridge, dev, d); + PCIEPort *p = DO_UPCAST(PCIEPort, br, br); + int rc; + int tmp; + + rc = pci_bridge_initfn(d); + if (rc < 0) { + return rc; + } + + pcie_port_init_reg(d); + pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_TI); + pci_config_set_device_id(d->config, PCI_DEVICE_ID_TI_XIO3130U); + d->config[PCI_REVISION_ID] = XIO3130_REVISION; + + rc = msi_init(d, XIO3130_MSI_OFFSET, XIO3130_MSI_NR_VECTOR, + XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_64BIT, + XIO3130_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT); + if (rc < 0) { + goto err_bridge; + } + rc = pci_bridge_ssvid_init(d, XIO3130_SSVID_OFFSET, + XIO3130_SSVID_SVID, XIO3130_SSVID_SSID); + if (rc < 0) { + goto err_bridge; + } + rc = pcie_cap_init(d, XIO3130_EXP_OFFSET, PCI_EXP_TYPE_UPSTREAM, + p->port); + if (rc < 0) { + goto err_msi; + } + pcie_cap_flr_init(d); + pcie_cap_deverr_init(d); + rc = pcie_aer_init(d, XIO3130_AER_OFFSET); + if (rc < 0) { + goto err; + } + + return 0; + +err: + pcie_cap_exit(d); +err_msi: + msi_uninit(d); +err_bridge: + tmp = pci_bridge_exitfn(d); + assert(!tmp); + return rc; +} + +static int xio3130_upstream_exitfn(PCIDevice *d) +{ + pcie_aer_exit(d); + pcie_cap_exit(d); + msi_uninit(d); + return pci_bridge_exitfn(d); +} + +PCIEPort *xio3130_upstream_init(PCIBus *bus, int devfn, bool multifunction, + const char *bus_name, pci_map_irq_fn map_irq, + uint8_t port) +{ + PCIDevice *d; + PCIBridge *br; + DeviceState *qdev; + + d = pci_create_multifunction(bus, devfn, multifunction, "x3130-upstream"); + if (!d) { + return NULL; + } + br = DO_UPCAST(PCIBridge, dev, d); + + qdev = &br->dev.qdev; + pci_bridge_map_irq(br, bus_name, map_irq); + qdev_prop_set_uint8(qdev, "port", port); + qdev_init_nofail(qdev); + + return DO_UPCAST(PCIEPort, br, br); +} + +static const VMStateDescription vmstate_xio3130_upstream = { + .name = "xio3130-express-upstream-port", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_PCIE_DEVICE(br.dev, PCIEPort), + VMSTATE_STRUCT(br.dev.exp.aer_log, PCIEPort, 0, vmstate_pcie_aer_log, + PCIEAERLog), + VMSTATE_END_OF_LIST() + } +}; + +static PCIDeviceInfo xio3130_upstream_info = { + .qdev.name = "x3130-upstream", + .qdev.desc = "TI X3130 Upstream Port of PCI Express Switch", + .qdev.size = sizeof(PCIEPort), + .qdev.reset = xio3130_upstream_reset, + .qdev.vmsd = &vmstate_xio3130_upstream, + + .is_express = 1, + .is_bridge = 1, + .config_write = xio3130_upstream_write_config, + .init = xio3130_upstream_initfn, + .exit = xio3130_upstream_exitfn, + + .qdev.props = (Property[]) { + DEFINE_PROP_UINT8("port", PCIEPort, port, 0), + DEFINE_PROP_UINT16("aer_log_max", PCIEPort, br.dev.exp.aer_log.log_max, + PCIE_AER_LOG_MAX_DEFAULT), + DEFINE_PROP_END_OF_LIST(), + } +}; + +static void xio3130_upstream_register(void) +{ + pci_qdev_register(&xio3130_upstream_info); +} + +device_init(xio3130_upstream_register); + + +/* + * Local variables: + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 8 + * indent-tab-mode: nil + * End: + */ diff -Nru qemu-kvm-0.12.5+noroms/hw/xio3130_upstream.h qemu-kvm-0.14.1/hw/xio3130_upstream.h --- qemu-kvm-0.12.5+noroms/hw/xio3130_upstream.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/hw/xio3130_upstream.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,10 @@ +#ifndef QEMU_XIO3130_UPSTREAM_H +#define QEMU_XIO3130_UPSTREAM_H + +#include "pcie_port.h" + +PCIEPort *xio3130_upstream_init(PCIBus *bus, int devfn, bool multifunction, + const char *bus_name, pci_map_irq_fn map_irq, + uint8_t port); + +#endif /* QEMU_XIO3130_H */ diff -Nru qemu-kvm-0.12.5+noroms/hw/zaurus.c qemu-kvm-0.14.1/hw/zaurus.c --- qemu-kvm-0.12.5+noroms/hw/zaurus.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hw/zaurus.c 2011-05-11 13:29:46.000000000 +0000 @@ -18,15 +18,17 @@ #include "hw.h" #include "pxa.h" #include "sharpsl.h" +#include "sysbus.h" #undef REG_FMT #define REG_FMT "0x%02lx" /* SCOOP devices */ +typedef struct ScoopInfo ScoopInfo; struct ScoopInfo { + SysBusDevice busdev; qemu_irq handler[16]; - qemu_irq *in; uint16_t status; uint16_t power; uint32_t gpio_level; @@ -70,7 +72,7 @@ { ScoopInfo *s = (ScoopInfo *) opaque; - switch (addr) { + switch (addr & 0x3f) { case SCOOP_MCR: return s->mcr; case SCOOP_CDR: @@ -104,7 +106,7 @@ ScoopInfo *s = (ScoopInfo *) opaque; value &= 0xffff; - switch (addr) { + switch (addr & 0x3f) { case SCOOP_MCR: s->mcr = value; break; @@ -153,7 +155,7 @@ scoop_writeb, }; -void scoop_gpio_set(void *opaque, int line, int level) +static void scoop_gpio_set(void *opaque, int line, int level) { ScoopInfo *s = (ScoopInfo *) opaque; @@ -163,77 +165,66 @@ s->gpio_level &= ~(1 << line); } -qemu_irq *scoop_gpio_in_get(ScoopInfo *s) +static int scoop_init(SysBusDevice *dev) { - return s->in; -} + ScoopInfo *s = FROM_SYSBUS(ScoopInfo, dev); + int iomemtype; -void scoop_gpio_out_set(ScoopInfo *s, int line, - qemu_irq handler) { - if (line >= 16) { - fprintf(stderr, "No GPIO pin %i\n", line); - exit(-1); - } + s->status = 0x02; + qdev_init_gpio_out(&s->busdev.qdev, s->handler, 16); + qdev_init_gpio_in(&s->busdev.qdev, scoop_gpio_set, 16); + iomemtype = cpu_register_io_memory(scoop_readfn, + scoop_writefn, s, DEVICE_NATIVE_ENDIAN); - s->handler[line] = handler; -} + sysbus_init_mmio(dev, 0x1000, iomemtype); -static void scoop_save(QEMUFile *f, void *opaque) -{ - ScoopInfo *s = (ScoopInfo *) opaque; - qemu_put_be16s(f, &s->status); - qemu_put_be16s(f, &s->power); - qemu_put_be32s(f, &s->gpio_level); - qemu_put_be32s(f, &s->gpio_dir); - qemu_put_be32s(f, &s->prev_level); - qemu_put_be16s(f, &s->mcr); - qemu_put_be16s(f, &s->cdr); - qemu_put_be16s(f, &s->ccr); - qemu_put_be16s(f, &s->irr); - qemu_put_be16s(f, &s->imr); - qemu_put_be16s(f, &s->isr); + return 0; } -static int scoop_load(QEMUFile *f, void *opaque, int version_id) +static bool is_version_0 (void *opaque, int version_id) { - uint16_t dummy; - ScoopInfo *s = (ScoopInfo *) opaque; - qemu_get_be16s(f, &s->status); - qemu_get_be16s(f, &s->power); - qemu_get_be32s(f, &s->gpio_level); - qemu_get_be32s(f, &s->gpio_dir); - qemu_get_be32s(f, &s->prev_level); - qemu_get_be16s(f, &s->mcr); - qemu_get_be16s(f, &s->cdr); - qemu_get_be16s(f, &s->ccr); - qemu_get_be16s(f, &s->irr); - qemu_get_be16s(f, &s->imr); - qemu_get_be16s(f, &s->isr); - if (version_id < 1) - qemu_get_be16s(f, &dummy); - - return 0; + return version_id == 0; } -ScoopInfo *scoop_init(PXA2xxState *cpu, - int instance, - target_phys_addr_t target_base) { - int iomemtype; - ScoopInfo *s; - s = (ScoopInfo *) - qemu_mallocz(sizeof(ScoopInfo)); - memset(s, 0, sizeof(ScoopInfo)); +static const VMStateDescription vmstate_scoop_regs = { + .name = "scoop", + .version_id = 1, + .minimum_version_id = 0, + .minimum_version_id_old = 0, + .fields = (VMStateField []) { + VMSTATE_UINT16(status, ScoopInfo), + VMSTATE_UINT16(power, ScoopInfo), + VMSTATE_UINT32(gpio_level, ScoopInfo), + VMSTATE_UINT32(gpio_dir, ScoopInfo), + VMSTATE_UINT32(prev_level, ScoopInfo), + VMSTATE_UINT16(mcr, ScoopInfo), + VMSTATE_UINT16(cdr, ScoopInfo), + VMSTATE_UINT16(ccr, ScoopInfo), + VMSTATE_UINT16(irr, ScoopInfo), + VMSTATE_UINT16(imr, ScoopInfo), + VMSTATE_UINT16(isr, ScoopInfo), + VMSTATE_UNUSED_TEST(is_version_0, 2), + VMSTATE_END_OF_LIST(), + }, +}; - s->status = 0x02; - s->in = qemu_allocate_irqs(scoop_gpio_set, s, 16); - iomemtype = cpu_register_io_memory(scoop_readfn, - scoop_writefn, s); - cpu_register_physical_memory(target_base, 0x1000, iomemtype); - register_savevm("scoop", instance, 1, scoop_save, scoop_load, s); +static SysBusDeviceInfo scoop_sysbus_info = { + .init = scoop_init, + .qdev.name = "scoop", + .qdev.desc = "Scoop2 Sharp custom ASIC", + .qdev.size = sizeof(ScoopInfo), + .qdev.vmsd = &vmstate_scoop_regs, + .qdev.props = (Property[]) { + DEFINE_PROP_END_OF_LIST(), + } +}; - return s; +static void scoop_register(void) +{ + sysbus_register_withprop(&scoop_sysbus_info); } +device_init(scoop_register); /* Write the bootloader parameters memory area. */ diff -Nru qemu-kvm-0.12.5+noroms/hxtool qemu-kvm-0.14.1/hxtool --- qemu-kvm-0.12.5+noroms/hxtool 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/hxtool 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ -#!/bin/sh - -hxtoh() -{ - flag=1 - while read -r str; do - case $str in - HXCOMM*) - ;; - STEXI*|ETEXI*) flag=$(($flag^1)) - ;; - *) - test $flag -eq 1 && printf "%s\n" "$str" - ;; - esac - done -} - -hxtotexi() -{ - flag=0 - while read -r str; do - case "$str" in - HXCOMM*) - ;; - STEXI*|ETEXI*) flag=$(($flag^1)) - ;; - DEFHEADING*) - echo "$(expr "$str" : "DEFHEADING(\(.*\))")" - ;; - *) - test $flag -eq 1 && echo "$str" - ;; - esac - done -} - -case "$1" in -"-h") hxtoh ;; -"-t") hxtotexi ;; -*) exit 1 ;; -esac - -exit 0 diff -Nru qemu-kvm-0.12.5+noroms/i386-dis.c qemu-kvm-0.14.1/i386-dis.c --- qemu-kvm-0.12.5+noroms/i386-dis.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/i386-dis.c 2011-05-11 13:29:46.000000000 +0000 @@ -155,7 +155,8 @@ #include -static int fetch_data (struct disassemble_info *, bfd_byte *); +static int fetch_data2(struct disassemble_info *, bfd_byte *); +static int fetch_data(struct disassemble_info *, bfd_byte *); static void ckprefix (void); static const char *prefix_name (int, int); static int print_insn (bfd_vma, disassemble_info *); @@ -280,12 +281,8 @@ /* Make sure that bytes from INFO->PRIVATE_DATA->BUFFER (inclusive) to ADDR (exclusive) are valid. Returns 1 for success, longjmps on error. */ -#define FETCH_DATA(info, addr) \ - ((addr) <= ((struct dis_private *) (info->private_data))->max_fetched \ - ? 1 : fetch_data ((info), (addr))) - static int -fetch_data (struct disassemble_info *info, bfd_byte *addr) +fetch_data2(struct disassemble_info *info, bfd_byte *addr) { int status; struct dis_private *priv = (struct dis_private *) info->private_data; @@ -313,6 +310,17 @@ return 1; } +static int +fetch_data(struct disassemble_info *info, bfd_byte *addr) +{ + if (addr <= ((struct dis_private *) (info->private_data))->max_fetched) { + return 1; + } else { + return fetch_data2(info, addr); + } +} + + #define XX { NULL, 0 } #define Eb { OP_E, b_mode } @@ -3320,7 +3328,7 @@ rex_used = 0; while (1) { - FETCH_DATA (the_info, codep + 1); + fetch_data(the_info, codep + 1); newrex = 0; switch (*codep) { @@ -3684,7 +3692,7 @@ insn_codep = codep; sizeflag = priv.orig_sizeflag; - FETCH_DATA (info, codep + 1); + fetch_data(info, codep + 1); two_source_ops = (*codep == 0x62) || (*codep == 0xc8); if (((prefixes & PREFIX_FWAIT) @@ -3706,7 +3714,7 @@ if (*codep == 0x0f) { unsigned char threebyte; - FETCH_DATA (info, codep + 2); + fetch_data(info, codep + 2); threebyte = *++codep; dp = &dis386_twobyte[threebyte]; need_modrm = twobyte_has_modrm[*codep]; @@ -3717,7 +3725,7 @@ codep++; if (dp->name == NULL && dp->op[0].bytemode == IS_3BYTE_OPCODE) { - FETCH_DATA (info, codep + 2); + fetch_data(info, codep + 2); op = *codep++; switch (threebyte) { @@ -3802,7 +3810,7 @@ } else if (need_modrm) { - FETCH_DATA (info, codep + 1); + fetch_data(info, codep + 1); modrm.mod = (*codep >> 6) & 3; modrm.reg = (*codep >> 3) & 7; modrm.rm = *codep & 7; @@ -4968,7 +4976,7 @@ if (base == 4) { havesib = 1; - FETCH_DATA (the_info, codep + 1); + fetch_data(the_info, codep + 1); index = (*codep >> 3) & 7; if (address_mode == mode_64bit || index != 0x4) /* When INDEX == 0x4 in 32 bit mode, SCALE is ignored. */ @@ -4993,7 +5001,7 @@ } break; case 1: - FETCH_DATA (the_info, codep + 1); + fetch_data (the_info, codep + 1); disp = *codep++; if ((disp & 0x80) != 0) disp -= 0x100; @@ -5104,7 +5112,7 @@ } break; case 1: - FETCH_DATA (the_info, codep + 1); + fetch_data(the_info, codep + 1); disp = *codep++; if ((disp & 0x80) != 0) disp -= 0x100; @@ -5226,7 +5234,7 @@ unsigned int a; unsigned int b; - FETCH_DATA (the_info, codep + 8); + fetch_data(the_info, codep + 8); a = *codep++ & 0xff; a |= (*codep++ & 0xff) << 8; a |= (*codep++ & 0xff) << 16; @@ -5248,7 +5256,7 @@ { bfd_signed_vma x = 0; - FETCH_DATA (the_info, codep + 4); + fetch_data(the_info, codep + 4); x = *codep++ & (bfd_signed_vma) 0xff; x |= (*codep++ & (bfd_signed_vma) 0xff) << 8; x |= (*codep++ & (bfd_signed_vma) 0xff) << 16; @@ -5261,7 +5269,7 @@ { bfd_signed_vma x = 0; - FETCH_DATA (the_info, codep + 4); + fetch_data(the_info, codep + 4); x = *codep++ & (bfd_signed_vma) 0xff; x |= (*codep++ & (bfd_signed_vma) 0xff) << 8; x |= (*codep++ & (bfd_signed_vma) 0xff) << 16; @@ -5277,7 +5285,7 @@ { int x = 0; - FETCH_DATA (the_info, codep + 2); + fetch_data(the_info, codep + 2); x = *codep++ & 0xff; x |= (*codep++ & 0xff) << 8; return x; @@ -5418,7 +5426,7 @@ switch (bytemode) { case b_mode: - FETCH_DATA (the_info, codep + 1); + fetch_data(the_info, codep + 1); op = *codep++; mask = 0xff; break; @@ -5480,7 +5488,7 @@ switch (bytemode) { case b_mode: - FETCH_DATA (the_info, codep + 1); + fetch_data(the_info, codep + 1); op = *codep++; mask = 0xff; break; @@ -5520,16 +5528,14 @@ OP_sI (int bytemode, int sizeflag) { bfd_signed_vma op; - bfd_signed_vma mask = -1; switch (bytemode) { case b_mode: - FETCH_DATA (the_info, codep + 1); + fetch_data(the_info, codep + 1); op = *codep++; if ((op & 0x80) != 0) op -= 0x100; - mask = 0xffffffff; break; case v_mode: USED_REX (REX_W); @@ -5538,11 +5544,9 @@ else if (sizeflag & DFLAG) { op = get32s (); - mask = 0xffffffff; } else { - mask = 0xffffffff; op = get16 (); if ((op & 0x8000) != 0) op -= 0x10000; @@ -5551,7 +5555,6 @@ break; case w_mode: op = get16 (); - mask = 0xffffffff; if ((op & 0x8000) != 0) op -= 0x10000; break; @@ -5575,7 +5578,7 @@ switch (bytemode) { case b_mode: - FETCH_DATA (the_info, codep + 1); + fetch_data(the_info, codep + 1); disp = *codep++; if ((disp & 0x80) != 0) disp -= 0x100; @@ -6097,7 +6100,7 @@ { const char *mnemonic; - FETCH_DATA (the_info, codep + 1); + fetch_data(the_info, codep + 1); /* AMD 3DNow! instructions are specified by an opcode suffix in the place where an 8-bit immediate would normally go. ie. the last byte of the instruction. */ @@ -6133,7 +6136,7 @@ { unsigned int cmp_type; - FETCH_DATA (the_info, codep + 1); + fetch_data(the_info, codep + 1); obufp = obuf + strlen (obuf); cmp_type = *codep++ & 0xff; if (cmp_type < 8) diff -Nru qemu-kvm-0.12.5+noroms/ia64-dis.c qemu-kvm-0.14.1/ia64-dis.c --- qemu-kvm-0.12.5+noroms/ia64-dis.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/ia64-dis.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,10599 @@ +/* ia64-dis.c -- Disassemble ia64 instructions + Copyright 1998, 1999, 2000, 2002 Free Software Foundation, Inc. + Contributed by David Mosberger-Tang + + This file is part of GDB, GAS, and the GNU binutils. + + GDB, GAS, and the GNU binutils are free software; you can redistribute + them and/or modify them under the terms of the GNU General Public + License as published by the Free Software Foundation; either version + 2, or (at your option) any later version. + + GDB, GAS, and the GNU binutils are distributed in the hope that they + 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 file; see the file COPYING. If not, see + . */ + +#include +#include + +#include "dis-asm.h" + +/* ia64.h -- Header file for ia64 opcode table + Copyright (C) 1998, 1999, 2000, 2002, 2005, 2006 + Free Software Foundation, Inc. + Contributed by David Mosberger-Tang */ + +#include + +typedef uint64_t ia64_insn; + +enum ia64_insn_type + { + IA64_TYPE_NIL = 0, /* illegal type */ + IA64_TYPE_A, /* integer alu (I- or M-unit) */ + IA64_TYPE_I, /* non-alu integer (I-unit) */ + IA64_TYPE_M, /* memory (M-unit) */ + IA64_TYPE_B, /* branch (B-unit) */ + IA64_TYPE_F, /* floating-point (F-unit) */ + IA64_TYPE_X, /* long encoding (X-unit) */ + IA64_TYPE_DYN, /* Dynamic opcode */ + IA64_NUM_TYPES + }; + +enum ia64_unit + { + IA64_UNIT_NIL = 0, /* illegal unit */ + IA64_UNIT_I, /* integer unit */ + IA64_UNIT_M, /* memory unit */ + IA64_UNIT_B, /* branching unit */ + IA64_UNIT_F, /* floating-point unit */ + IA64_UNIT_L, /* long "unit" */ + IA64_UNIT_X, /* may be integer or branch unit */ + IA64_NUM_UNITS + }; + +/* Changes to this enumeration must be propagated to the operand table in + bfd/cpu-ia64-opc.c + */ +enum ia64_opnd + { + IA64_OPND_NIL, /* no operand---MUST BE FIRST!*/ + + /* constants */ + IA64_OPND_AR_CSD, /* application register csd (ar.csd) */ + IA64_OPND_AR_CCV, /* application register ccv (ar.ccv) */ + IA64_OPND_AR_PFS, /* application register pfs (ar.pfs) */ + IA64_OPND_C1, /* the constant 1 */ + IA64_OPND_C8, /* the constant 8 */ + IA64_OPND_C16, /* the constant 16 */ + IA64_OPND_GR0, /* gr0 */ + IA64_OPND_IP, /* instruction pointer (ip) */ + IA64_OPND_PR, /* predicate register (pr) */ + IA64_OPND_PR_ROT, /* rotating predicate register (pr.rot) */ + IA64_OPND_PSR, /* processor status register (psr) */ + IA64_OPND_PSR_L, /* processor status register L (psr.l) */ + IA64_OPND_PSR_UM, /* processor status register UM (psr.um) */ + + /* register operands: */ + IA64_OPND_AR3, /* third application register # (bits 20-26) */ + IA64_OPND_B1, /* branch register # (bits 6-8) */ + IA64_OPND_B2, /* branch register # (bits 13-15) */ + IA64_OPND_CR3, /* third control register # (bits 20-26) */ + IA64_OPND_F1, /* first floating-point register # */ + IA64_OPND_F2, /* second floating-point register # */ + IA64_OPND_F3, /* third floating-point register # */ + IA64_OPND_F4, /* fourth floating-point register # */ + IA64_OPND_P1, /* first predicate # */ + IA64_OPND_P2, /* second predicate # */ + IA64_OPND_R1, /* first register # */ + IA64_OPND_R2, /* second register # */ + IA64_OPND_R3, /* third register # */ + IA64_OPND_R3_2, /* third register # (limited to gr0-gr3) */ + + /* memory operands: */ + IA64_OPND_MR3, /* memory at addr of third register # */ + + /* indirect operands: */ + IA64_OPND_CPUID_R3, /* cpuid[reg] */ + IA64_OPND_DBR_R3, /* dbr[reg] */ + IA64_OPND_DTR_R3, /* dtr[reg] */ + IA64_OPND_ITR_R3, /* itr[reg] */ + IA64_OPND_IBR_R3, /* ibr[reg] */ + IA64_OPND_MSR_R3, /* msr[reg] */ + IA64_OPND_PKR_R3, /* pkr[reg] */ + IA64_OPND_PMC_R3, /* pmc[reg] */ + IA64_OPND_PMD_R3, /* pmd[reg] */ + IA64_OPND_RR_R3, /* rr[reg] */ + + /* immediate operands: */ + IA64_OPND_CCNT5, /* 5-bit count (31 - bits 20-24) */ + IA64_OPND_CNT2a, /* 2-bit count (1 + bits 27-28) */ + IA64_OPND_CNT2b, /* 2-bit count (bits 27-28): 1, 2, 3 */ + IA64_OPND_CNT2c, /* 2-bit count (bits 30-31): 0, 7, 15, or 16 */ + IA64_OPND_CNT5, /* 5-bit count (bits 14-18) */ + IA64_OPND_CNT6, /* 6-bit count (bits 27-32) */ + IA64_OPND_CPOS6a, /* 6-bit count (63 - bits 20-25) */ + IA64_OPND_CPOS6b, /* 6-bit count (63 - bits 14-19) */ + IA64_OPND_CPOS6c, /* 6-bit count (63 - bits 31-36) */ + IA64_OPND_IMM1, /* signed 1-bit immediate (bit 36) */ + IA64_OPND_IMMU2, /* unsigned 2-bit immediate (bits 13-14) */ + IA64_OPND_IMMU5b, /* unsigned 5-bit immediate (32 + bits 14-18) */ + IA64_OPND_IMMU7a, /* unsigned 7-bit immediate (bits 13-19) */ + IA64_OPND_IMMU7b, /* unsigned 7-bit immediate (bits 20-26) */ + IA64_OPND_SOF, /* 8-bit stack frame size */ + IA64_OPND_SOL, /* 8-bit size of locals */ + IA64_OPND_SOR, /* 6-bit number of rotating registers (scaled by 8) */ + IA64_OPND_IMM8, /* signed 8-bit immediate (bits 13-19 & 36) */ + IA64_OPND_IMM8U4, /* cmp4*u signed 8-bit immediate (bits 13-19 & 36) */ + IA64_OPND_IMM8M1, /* signed 8-bit immediate -1 (bits 13-19 & 36) */ + IA64_OPND_IMM8M1U4, /* cmp4*u signed 8-bit immediate -1 (bits 13-19 & 36)*/ + IA64_OPND_IMM8M1U8, /* cmp*u signed 8-bit immediate -1 (bits 13-19 & 36) */ + IA64_OPND_IMMU9, /* unsigned 9-bit immediate (bits 33-34, 20-26) */ + IA64_OPND_IMM9a, /* signed 9-bit immediate (bits 6-12, 27, 36) */ + IA64_OPND_IMM9b, /* signed 9-bit immediate (bits 13-19, 27, 36) */ + IA64_OPND_IMM14, /* signed 14-bit immediate (bits 13-19, 27-32, 36) */ + IA64_OPND_IMM17, /* signed 17-bit immediate (2*bits 6-12, 24-31, 36) */ + IA64_OPND_IMMU21, /* unsigned 21-bit immediate (bits 6-25, 36) */ + IA64_OPND_IMM22, /* signed 22-bit immediate (bits 13-19, 22-36) */ + IA64_OPND_IMMU24, /* unsigned 24-bit immediate (bits 6-26, 31-32, 36) */ + IA64_OPND_IMM44, /* signed 44-bit immediate (2^16*bits 6-32, 36) */ + IA64_OPND_IMMU62, /* unsigned 62-bit immediate */ + IA64_OPND_IMMU64, /* unsigned 64-bit immediate (lotsa bits...) */ + IA64_OPND_INC3, /* signed 3-bit (bits 13-15): +/-1, 4, 8, 16 */ + IA64_OPND_LEN4, /* 4-bit count (bits 27-30 + 1) */ + IA64_OPND_LEN6, /* 6-bit count (bits 27-32 + 1) */ + IA64_OPND_MBTYPE4, /* 4-bit mux type (bits 20-23) */ + IA64_OPND_MHTYPE8, /* 8-bit mux type (bits 20-27) */ + IA64_OPND_POS6, /* 6-bit count (bits 14-19) */ + IA64_OPND_TAG13, /* signed 13-bit tag (ip + 16*bits 6-12, 33-34) */ + IA64_OPND_TAG13b, /* signed 13-bit tag (ip + 16*bits 24-32) */ + IA64_OPND_TGT25, /* signed 25-bit (ip + 16*bits 6-25, 36) */ + IA64_OPND_TGT25b, /* signed 25-bit (ip + 16*bits 6-12, 20-32, 36) */ + IA64_OPND_TGT25c, /* signed 25-bit (ip + 16*bits 13-32, 36) */ + IA64_OPND_TGT64, /* 64-bit (ip + 16*bits 13-32, 36, 2-40(L)) */ + IA64_OPND_LDXMOV, /* any symbol, generates R_IA64_LDXMOV. */ + + IA64_OPND_COUNT /* # of operand types (MUST BE LAST!) */ + }; + +enum ia64_dependency_mode +{ + IA64_DV_RAW, + IA64_DV_WAW, + IA64_DV_WAR, +}; + +enum ia64_dependency_semantics +{ + IA64_DVS_NONE, + IA64_DVS_IMPLIED, + IA64_DVS_IMPLIEDF, + IA64_DVS_DATA, + IA64_DVS_INSTR, + IA64_DVS_SPECIFIC, + IA64_DVS_STOP, + IA64_DVS_OTHER, +}; + +enum ia64_resource_specifier +{ + IA64_RS_ANY, + IA64_RS_AR_K, + IA64_RS_AR_UNAT, + IA64_RS_AR, /* 8-15, 20, 22-23, 31, 33-35, 37-39, 41-43, 45-47, 67-111 */ + IA64_RS_ARb, /* 48-63, 112-127 */ + IA64_RS_BR, + IA64_RS_CFM, + IA64_RS_CPUID, + IA64_RS_CR_IRR, + IA64_RS_CR_LRR, + IA64_RS_CR, /* 3-7,10-15,18,26-63,75-79,82-127 */ + IA64_RS_DBR, + IA64_RS_FR, + IA64_RS_FRb, + IA64_RS_GR0, + IA64_RS_GR, + IA64_RS_IBR, + IA64_RS_INSERVICE, /* CR[EOI] or CR[IVR] */ + IA64_RS_MSR, + IA64_RS_PKR, + IA64_RS_PMC, + IA64_RS_PMD, + IA64_RS_PR, /* non-rotating, 1-15 */ + IA64_RS_PRr, /* rotating, 16-62 */ + IA64_RS_PR63, + IA64_RS_RR, + + IA64_RS_ARX, /* ARs not in RS_AR or RS_ARb */ + IA64_RS_CRX, /* CRs not in RS_CR */ + IA64_RS_PSR, /* PSR bits */ + IA64_RS_RSE, /* implementation-specific RSE resources */ + IA64_RS_AR_FPSR, +}; + +enum ia64_rse_resource +{ + IA64_RSE_N_STACKED_PHYS, + IA64_RSE_BOF, + IA64_RSE_STORE_REG, + IA64_RSE_LOAD_REG, + IA64_RSE_BSPLOAD, + IA64_RSE_RNATBITINDEX, + IA64_RSE_CFLE, + IA64_RSE_NDIRTY, +}; + +/* Information about a given resource dependency */ +struct ia64_dependency +{ + /* Name of the resource */ + const char *name; + /* Does this dependency need further specification? */ + enum ia64_resource_specifier specifier; + /* Mode of dependency */ + enum ia64_dependency_mode mode; + /* Dependency semantics */ + enum ia64_dependency_semantics semantics; + /* Register index, if applicable (distinguishes AR, CR, and PSR deps) */ +#define REG_NONE (-1) + int regindex; + /* Special info on semantics */ + const char *info; +}; + +/* Two arrays of indexes into the ia64_dependency table. + chks are dependencies to check for conflicts when an opcode is + encountered; regs are dependencies to register (mark as used) when an + opcode is used. chks correspond to readers (RAW) or writers (WAW or + WAR) of a resource, while regs correspond to writers (RAW or WAW) and + readers (WAR) of a resource. */ +struct ia64_opcode_dependency +{ + int nchks; + const unsigned short *chks; + int nregs; + const unsigned short *regs; +}; + +/* encode/extract the note/index for a dependency */ +#define RDEP(N,X) (((N)<<11)|(X)) +#define NOTE(X) (((X)>>11)&0x1F) +#define DEP(X) ((X)&0x7FF) + +/* A template descriptor describes the execution units that are active + for each of the three slots. It also specifies the location of + instruction group boundaries that may be present between two slots. */ +struct ia64_templ_desc + { + int group_boundary; /* 0=no boundary, 1=between slot 0 & 1, etc. */ + enum ia64_unit exec_unit[3]; + const char *name; + }; + +/* The opcode table is an array of struct ia64_opcode. */ + +struct ia64_opcode + { + /* The opcode name. */ + const char *name; + + /* The type of the instruction: */ + enum ia64_insn_type type; + + /* Number of output operands: */ + int num_outputs; + + /* The opcode itself. Those bits which will be filled in with + operands are zeroes. */ + ia64_insn opcode; + + /* The opcode mask. This is used by the disassembler. This is a + mask containing ones indicating those bits which must match the + opcode field, and zeroes indicating those bits which need not + match (and are presumably filled in by operands). */ + ia64_insn mask; + + /* An array of operand codes. Each code is an index into the + operand table. They appear in the order which the operands must + appear in assembly code, and are terminated by a zero. */ + enum ia64_opnd operands[5]; + + /* One bit flags for the opcode. These are primarily used to + indicate specific processors and environments support the + instructions. The defined values are listed below. */ + unsigned int flags; + + /* Used by ia64_find_next_opcode (). */ + short ent_index; + + /* Opcode dependencies. */ + const struct ia64_opcode_dependency *dependencies; + }; + +/* Values defined for the flags field of a struct ia64_opcode. */ + +#define IA64_OPCODE_FIRST (1<<0) /* must be first in an insn group */ +#define IA64_OPCODE_X_IN_MLX (1<<1) /* insn is allowed in X slot of MLX */ +#define IA64_OPCODE_LAST (1<<2) /* must be last in an insn group */ +#define IA64_OPCODE_PRIV (1<<3) /* privileged instruct */ +#define IA64_OPCODE_SLOT2 (1<<4) /* insn allowed in slot 2 only */ +#define IA64_OPCODE_NO_PRED (1<<5) /* insn cannot be predicated */ +#define IA64_OPCODE_PSEUDO (1<<6) /* insn is a pseudo-op */ +#define IA64_OPCODE_F2_EQ_F3 (1<<7) /* constraint: F2 == F3 */ +#define IA64_OPCODE_LEN_EQ_64MCNT (1<<8) /* constraint: LEN == 64-CNT */ +#define IA64_OPCODE_MOD_RRBS (1<<9) /* modifies all rrbs in CFM */ +#define IA64_OPCODE_POSTINC (1<<10) /* postincrement MR3 operand */ + +/* A macro to extract the major opcode from an instruction. */ +#define IA64_OP(i) (((i) >> 37) & 0xf) + +enum ia64_operand_class + { + IA64_OPND_CLASS_CST, /* constant */ + IA64_OPND_CLASS_REG, /* register */ + IA64_OPND_CLASS_IND, /* indirect register */ + IA64_OPND_CLASS_ABS, /* absolute value */ + IA64_OPND_CLASS_REL, /* IP-relative value */ + }; + +/* The operands table is an array of struct ia64_operand. */ + +struct ia64_operand +{ + enum ia64_operand_class class; + + /* Set VALUE as the operand bits for the operand of type SELF in the + instruction pointed to by CODE. If an error occurs, *CODE is not + modified and the returned string describes the cause of the + error. If no error occurs, NULL is returned. */ + const char *(*insert) (const struct ia64_operand *self, ia64_insn value, + ia64_insn *code); + + /* Extract the operand bits for an operand of type SELF from + instruction CODE store them in *VALUE. If an error occurs, the + cause of the error is described by the string returned. If no + error occurs, NULL is returned. */ + const char *(*extract) (const struct ia64_operand *self, ia64_insn code, + ia64_insn *value); + + /* A string whose meaning depends on the operand class. */ + + const char *str; + + struct bit_field + { + /* The number of bits in the operand. */ + int bits; + + /* How far the operand is left shifted in the instruction. */ + int shift; + } + field[4]; /* no operand has more than this many bit-fields */ + + unsigned int flags; + + const char *desc; /* brief description */ +}; + +/* Values defined for the flags field of a struct ia64_operand. */ + +/* Disassemble as signed decimal (instead of hex): */ +#define IA64_OPND_FLAG_DECIMAL_SIGNED (1<<0) +/* Disassemble as unsigned decimal (instead of hex): */ +#define IA64_OPND_FLAG_DECIMAL_UNSIGNED (1<<1) + +#define NELEMS(a) ((int) (sizeof (a) / sizeof (a[0]))) + +static const char* +ins_rsvd (const struct ia64_operand *self ATTRIBUTE_UNUSED, + ia64_insn value ATTRIBUTE_UNUSED, ia64_insn *code ATTRIBUTE_UNUSED) +{ + return "internal error---this shouldn't happen"; +} + +static const char* +ext_rsvd (const struct ia64_operand *self ATTRIBUTE_UNUSED, + ia64_insn code ATTRIBUTE_UNUSED, ia64_insn *valuep ATTRIBUTE_UNUSED) +{ + return "internal error---this shouldn't happen"; +} + +static const char* +ins_const (const struct ia64_operand *self ATTRIBUTE_UNUSED, + ia64_insn value ATTRIBUTE_UNUSED, ia64_insn *code ATTRIBUTE_UNUSED) +{ + return 0; +} + +static const char* +ext_const (const struct ia64_operand *self ATTRIBUTE_UNUSED, + ia64_insn code ATTRIBUTE_UNUSED, ia64_insn *valuep ATTRIBUTE_UNUSED) +{ + return 0; +} + +static const char* +ins_reg (const struct ia64_operand *self, ia64_insn value, ia64_insn *code) +{ + if (value >= 1u << self->field[0].bits) + return "register number out of range"; + + *code |= value << self->field[0].shift; + return 0; +} + +static const char* +ext_reg (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep) +{ + *valuep = ((code >> self->field[0].shift) + & ((1u << self->field[0].bits) - 1)); + return 0; +} + +static const char* +ins_immu (const struct ia64_operand *self, ia64_insn value, ia64_insn *code) +{ + ia64_insn new = 0; + int i; + + for (i = 0; i < NELEMS (self->field) && self->field[i].bits; ++i) + { + new |= ((value & ((((ia64_insn) 1) << self->field[i].bits) - 1)) + << self->field[i].shift); + value >>= self->field[i].bits; + } + if (value) + return "integer operand out of range"; + + *code |= new; + return 0; +} + +static const char* +ext_immu (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep) +{ + uint64_t value = 0; + int i, bits = 0, total = 0; + + for (i = 0; i < NELEMS (self->field) && self->field[i].bits; ++i) + { + bits = self->field[i].bits; + value |= ((code >> self->field[i].shift) + & ((((uint64_t) 1) << bits) - 1)) << total; + total += bits; + } + *valuep = value; + return 0; +} + +static const char* +ins_immu5b (const struct ia64_operand *self, ia64_insn value, + ia64_insn *code) +{ + if (value < 32 || value > 63) + return "value must be between 32 and 63"; + return ins_immu (self, value - 32, code); +} + +static const char* +ext_immu5b (const struct ia64_operand *self, ia64_insn code, + ia64_insn *valuep) +{ + const char *result; + + result = ext_immu (self, code, valuep); + if (result) + return result; + + *valuep = *valuep + 32; + return 0; +} + +static const char* +ins_immus8 (const struct ia64_operand *self, ia64_insn value, ia64_insn *code) +{ + if (value & 0x7) + return "value not an integer multiple of 8"; + return ins_immu (self, value >> 3, code); +} + +static const char* +ext_immus8 (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep) +{ + const char *result; + + result = ext_immu (self, code, valuep); + if (result) + return result; + + *valuep = *valuep << 3; + return 0; +} + +static const char* +ins_imms_scaled (const struct ia64_operand *self, ia64_insn value, + ia64_insn *code, int scale) +{ + int64_t svalue = value, sign_bit = 0; + ia64_insn new = 0; + int i; + + svalue >>= scale; + + for (i = 0; i < NELEMS (self->field) && self->field[i].bits; ++i) + { + new |= ((svalue & ((((ia64_insn) 1) << self->field[i].bits) - 1)) + << self->field[i].shift); + sign_bit = (svalue >> (self->field[i].bits - 1)) & 1; + svalue >>= self->field[i].bits; + } + if ((!sign_bit && svalue != 0) || (sign_bit && svalue != -1)) + return "integer operand out of range"; + + *code |= new; + return 0; +} + +static const char* +ext_imms_scaled (const struct ia64_operand *self, ia64_insn code, + ia64_insn *valuep, int scale) +{ + int i, bits = 0, total = 0; + int64_t val = 0, sign; + + for (i = 0; i < NELEMS (self->field) && self->field[i].bits; ++i) + { + bits = self->field[i].bits; + val |= ((code >> self->field[i].shift) + & ((((uint64_t) 1) << bits) - 1)) << total; + total += bits; + } + /* sign extend: */ + sign = (int64_t) 1 << (total - 1); + val = (val ^ sign) - sign; + + *valuep = (val << scale); + return 0; +} + +static const char* +ins_imms (const struct ia64_operand *self, ia64_insn value, ia64_insn *code) +{ + return ins_imms_scaled (self, value, code, 0); +} + +static const char* +ins_immsu4 (const struct ia64_operand *self, ia64_insn value, ia64_insn *code) +{ + value = ((value & 0xffffffff) ^ 0x80000000) - 0x80000000; + + return ins_imms_scaled (self, value, code, 0); +} + +static const char* +ext_imms (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep) +{ + return ext_imms_scaled (self, code, valuep, 0); +} + +static const char* +ins_immsm1 (const struct ia64_operand *self, ia64_insn value, ia64_insn *code) +{ + --value; + return ins_imms_scaled (self, value, code, 0); +} + +static const char* +ins_immsm1u4 (const struct ia64_operand *self, ia64_insn value, + ia64_insn *code) +{ + value = ((value & 0xffffffff) ^ 0x80000000) - 0x80000000; + + --value; + return ins_imms_scaled (self, value, code, 0); +} + +static const char* +ext_immsm1 (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep) +{ + const char *res = ext_imms_scaled (self, code, valuep, 0); + + ++*valuep; + return res; +} + +static const char* +ins_imms1 (const struct ia64_operand *self, ia64_insn value, ia64_insn *code) +{ + return ins_imms_scaled (self, value, code, 1); +} + +static const char* +ext_imms1 (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep) +{ + return ext_imms_scaled (self, code, valuep, 1); +} + +static const char* +ins_imms4 (const struct ia64_operand *self, ia64_insn value, ia64_insn *code) +{ + return ins_imms_scaled (self, value, code, 4); +} + +static const char* +ext_imms4 (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep) +{ + return ext_imms_scaled (self, code, valuep, 4); +} + +static const char* +ins_imms16 (const struct ia64_operand *self, ia64_insn value, ia64_insn *code) +{ + return ins_imms_scaled (self, value, code, 16); +} + +static const char* +ext_imms16 (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep) +{ + return ext_imms_scaled (self, code, valuep, 16); +} + +static const char* +ins_cimmu (const struct ia64_operand *self, ia64_insn value, ia64_insn *code) +{ + ia64_insn mask = (((ia64_insn) 1) << self->field[0].bits) - 1; + return ins_immu (self, value ^ mask, code); +} + +static const char* +ext_cimmu (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep) +{ + const char *result; + ia64_insn mask; + + mask = (((ia64_insn) 1) << self->field[0].bits) - 1; + result = ext_immu (self, code, valuep); + if (!result) + { + mask = (((ia64_insn) 1) << self->field[0].bits) - 1; + *valuep ^= mask; + } + return result; +} + +static const char* +ins_cnt (const struct ia64_operand *self, ia64_insn value, ia64_insn *code) +{ + --value; + if (value >= ((uint64_t) 1) << self->field[0].bits) + return "count out of range"; + + *code |= value << self->field[0].shift; + return 0; +} + +static const char* +ext_cnt (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep) +{ + *valuep = ((code >> self->field[0].shift) + & ((((uint64_t) 1) << self->field[0].bits) - 1)) + 1; + return 0; +} + +static const char* +ins_cnt2b (const struct ia64_operand *self, ia64_insn value, ia64_insn *code) +{ + --value; + + if (value > 2) + return "count must be in range 1..3"; + + *code |= value << self->field[0].shift; + return 0; +} + +static const char* +ext_cnt2b (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep) +{ + *valuep = ((code >> self->field[0].shift) & 0x3) + 1; + return 0; +} + +static const char* +ins_cnt2c (const struct ia64_operand *self, ia64_insn value, ia64_insn *code) +{ + switch (value) + { + case 0: value = 0; break; + case 7: value = 1; break; + case 15: value = 2; break; + case 16: value = 3; break; + default: return "count must be 0, 7, 15, or 16"; + } + *code |= value << self->field[0].shift; + return 0; +} + +static const char* +ext_cnt2c (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep) +{ + ia64_insn value; + + value = (code >> self->field[0].shift) & 0x3; + switch (value) + { + case 0: value = 0; break; + case 1: value = 7; break; + case 2: value = 15; break; + case 3: value = 16; break; + } + *valuep = value; + return 0; +} + +static const char* +ins_inc3 (const struct ia64_operand *self, ia64_insn value, ia64_insn *code) +{ + int64_t val = value; + uint64_t sign = 0; + + if (val < 0) + { + sign = 0x4; + value = -value; + } + switch (value) + { + case 1: value = 3; break; + case 4: value = 2; break; + case 8: value = 1; break; + case 16: value = 0; break; + default: return "count must be +/- 1, 4, 8, or 16"; + } + *code |= (sign | value) << self->field[0].shift; + return 0; +} + +static const char* +ext_inc3 (const struct ia64_operand *self, ia64_insn code, ia64_insn *valuep) +{ + int64_t val; + int negate; + + val = (code >> self->field[0].shift) & 0x7; + negate = val & 0x4; + switch (val & 0x3) + { + case 0: val = 16; break; + case 1: val = 8; break; + case 2: val = 4; break; + case 3: val = 1; break; + } + if (negate) + val = -val; + + *valuep = val; + return 0; +} + +#define CST IA64_OPND_CLASS_CST +#define REG IA64_OPND_CLASS_REG +#define IND IA64_OPND_CLASS_IND +#define ABS IA64_OPND_CLASS_ABS +#define REL IA64_OPND_CLASS_REL + +#define SDEC IA64_OPND_FLAG_DECIMAL_SIGNED +#define UDEC IA64_OPND_FLAG_DECIMAL_UNSIGNED + +const struct ia64_operand elf64_ia64_operands[IA64_OPND_COUNT] = + { + /* constants: */ + { CST, ins_const, ext_const, "NIL", {{ 0, 0}}, 0, "" }, + { CST, ins_const, ext_const, "ar.csd", {{ 0, 0}}, 0, "ar.csd" }, + { CST, ins_const, ext_const, "ar.ccv", {{ 0, 0}}, 0, "ar.ccv" }, + { CST, ins_const, ext_const, "ar.pfs", {{ 0, 0}}, 0, "ar.pfs" }, + { CST, ins_const, ext_const, "1", {{ 0, 0}}, 0, "1" }, + { CST, ins_const, ext_const, "8", {{ 0, 0}}, 0, "8" }, + { CST, ins_const, ext_const, "16", {{ 0, 0}}, 0, "16" }, + { CST, ins_const, ext_const, "r0", {{ 0, 0}}, 0, "r0" }, + { CST, ins_const, ext_const, "ip", {{ 0, 0}}, 0, "ip" }, + { CST, ins_const, ext_const, "pr", {{ 0, 0}}, 0, "pr" }, + { CST, ins_const, ext_const, "pr.rot", {{ 0, 0}}, 0, "pr.rot" }, + { CST, ins_const, ext_const, "psr", {{ 0, 0}}, 0, "psr" }, + { CST, ins_const, ext_const, "psr.l", {{ 0, 0}}, 0, "psr.l" }, + { CST, ins_const, ext_const, "psr.um", {{ 0, 0}}, 0, "psr.um" }, + + /* register operands: */ + { REG, ins_reg, ext_reg, "ar", {{ 7, 20}}, 0, /* AR3 */ + "an application register" }, + { REG, ins_reg, ext_reg, "b", {{ 3, 6}}, 0, /* B1 */ + "a branch register" }, + { REG, ins_reg, ext_reg, "b", {{ 3, 13}}, 0, /* B2 */ + "a branch register"}, + { REG, ins_reg, ext_reg, "cr", {{ 7, 20}}, 0, /* CR */ + "a control register"}, + { REG, ins_reg, ext_reg, "f", {{ 7, 6}}, 0, /* F1 */ + "a floating-point register" }, + { REG, ins_reg, ext_reg, "f", {{ 7, 13}}, 0, /* F2 */ + "a floating-point register" }, + { REG, ins_reg, ext_reg, "f", {{ 7, 20}}, 0, /* F3 */ + "a floating-point register" }, + { REG, ins_reg, ext_reg, "f", {{ 7, 27}}, 0, /* F4 */ + "a floating-point register" }, + { REG, ins_reg, ext_reg, "p", {{ 6, 6}}, 0, /* P1 */ + "a predicate register" }, + { REG, ins_reg, ext_reg, "p", {{ 6, 27}}, 0, /* P2 */ + "a predicate register" }, + { REG, ins_reg, ext_reg, "r", {{ 7, 6}}, 0, /* R1 */ + "a general register" }, + { REG, ins_reg, ext_reg, "r", {{ 7, 13}}, 0, /* R2 */ + "a general register" }, + { REG, ins_reg, ext_reg, "r", {{ 7, 20}}, 0, /* R3 */ + "a general register" }, + { REG, ins_reg, ext_reg, "r", {{ 2, 20}}, 0, /* R3_2 */ + "a general register r0-r3" }, + + /* memory operands: */ + { IND, ins_reg, ext_reg, "", {{7, 20}}, 0, /* MR3 */ + "a memory address" }, + + /* indirect operands: */ + { IND, ins_reg, ext_reg, "cpuid", {{7, 20}}, 0, /* CPUID_R3 */ + "a cpuid register" }, + { IND, ins_reg, ext_reg, "dbr", {{7, 20}}, 0, /* DBR_R3 */ + "a dbr register" }, + { IND, ins_reg, ext_reg, "dtr", {{7, 20}}, 0, /* DTR_R3 */ + "a dtr register" }, + { IND, ins_reg, ext_reg, "itr", {{7, 20}}, 0, /* ITR_R3 */ + "an itr register" }, + { IND, ins_reg, ext_reg, "ibr", {{7, 20}}, 0, /* IBR_R3 */ + "an ibr register" }, + { IND, ins_reg, ext_reg, "msr", {{7, 20}}, 0, /* MSR_R3 */ + "an msr register" }, + { IND, ins_reg, ext_reg, "pkr", {{7, 20}}, 0, /* PKR_R3 */ + "a pkr register" }, + { IND, ins_reg, ext_reg, "pmc", {{7, 20}}, 0, /* PMC_R3 */ + "a pmc register" }, + { IND, ins_reg, ext_reg, "pmd", {{7, 20}}, 0, /* PMD_R3 */ + "a pmd register" }, + { IND, ins_reg, ext_reg, "rr", {{7, 20}}, 0, /* RR_R3 */ + "an rr register" }, + + /* immediate operands: */ + { ABS, ins_cimmu, ext_cimmu, 0, {{ 5, 20 }}, UDEC, /* CCNT5 */ + "a 5-bit count (0-31)" }, + { ABS, ins_cnt, ext_cnt, 0, {{ 2, 27 }}, UDEC, /* CNT2a */ + "a 2-bit count (1-4)" }, + { ABS, ins_cnt2b, ext_cnt2b, 0, {{ 2, 27 }}, UDEC, /* CNT2b */ + "a 2-bit count (1-3)" }, + { ABS, ins_cnt2c, ext_cnt2c, 0, {{ 2, 30 }}, UDEC, /* CNT2c */ + "a count (0, 7, 15, or 16)" }, + { ABS, ins_immu, ext_immu, 0, {{ 5, 14}}, UDEC, /* CNT5 */ + "a 5-bit count (0-31)" }, + { ABS, ins_immu, ext_immu, 0, {{ 6, 27}}, UDEC, /* CNT6 */ + "a 6-bit count (0-63)" }, + { ABS, ins_cimmu, ext_cimmu, 0, {{ 6, 20}}, UDEC, /* CPOS6a */ + "a 6-bit bit pos (0-63)" }, + { ABS, ins_cimmu, ext_cimmu, 0, {{ 6, 14}}, UDEC, /* CPOS6b */ + "a 6-bit bit pos (0-63)" }, + { ABS, ins_cimmu, ext_cimmu, 0, {{ 6, 31}}, UDEC, /* CPOS6c */ + "a 6-bit bit pos (0-63)" }, + { ABS, ins_imms, ext_imms, 0, {{ 1, 36}}, SDEC, /* IMM1 */ + "a 1-bit integer (-1, 0)" }, + { ABS, ins_immu, ext_immu, 0, {{ 2, 13}}, UDEC, /* IMMU2 */ + "a 2-bit unsigned (0-3)" }, + { ABS, ins_immu5b, ext_immu5b, 0, {{ 5, 14}}, UDEC, /* IMMU5b */ + "a 5-bit unsigned (32 + (0-31))" }, + { ABS, ins_immu, ext_immu, 0, {{ 7, 13}}, 0, /* IMMU7a */ + "a 7-bit unsigned (0-127)" }, + { ABS, ins_immu, ext_immu, 0, {{ 7, 20}}, 0, /* IMMU7b */ + "a 7-bit unsigned (0-127)" }, + { ABS, ins_immu, ext_immu, 0, {{ 7, 13}}, UDEC, /* SOF */ + "a frame size (register count)" }, + { ABS, ins_immu, ext_immu, 0, {{ 7, 20}}, UDEC, /* SOL */ + "a local register count" }, + { ABS, ins_immus8,ext_immus8,0, {{ 4, 27}}, UDEC, /* SOR */ + "a rotating register count (integer multiple of 8)" }, + { ABS, ins_imms, ext_imms, 0, /* IMM8 */ + {{ 7, 13}, { 1, 36}}, SDEC, + "an 8-bit integer (-128-127)" }, + { ABS, ins_immsu4, ext_imms, 0, /* IMM8U4 */ + {{ 7, 13}, { 1, 36}}, SDEC, + "an 8-bit signed integer for 32-bit unsigned compare (-128-127)" }, + { ABS, ins_immsm1, ext_immsm1, 0, /* IMM8M1 */ + {{ 7, 13}, { 1, 36}}, SDEC, + "an 8-bit integer (-127-128)" }, + { ABS, ins_immsm1u4, ext_immsm1, 0, /* IMM8M1U4 */ + {{ 7, 13}, { 1, 36}}, SDEC, + "an 8-bit integer for 32-bit unsigned compare (-127-(-1),1-128,0x100000000)" }, + { ABS, ins_immsm1, ext_immsm1, 0, /* IMM8M1U8 */ + {{ 7, 13}, { 1, 36}}, SDEC, + "an 8-bit integer for 64-bit unsigned compare (-127-(-1),1-128,0x10000000000000000)" }, + { ABS, ins_immu, ext_immu, 0, {{ 2, 33}, { 7, 20}}, 0, /* IMMU9 */ + "a 9-bit unsigned (0-511)" }, + { ABS, ins_imms, ext_imms, 0, /* IMM9a */ + {{ 7, 6}, { 1, 27}, { 1, 36}}, SDEC, + "a 9-bit integer (-256-255)" }, + { ABS, ins_imms, ext_imms, 0, /* IMM9b */ + {{ 7, 13}, { 1, 27}, { 1, 36}}, SDEC, + "a 9-bit integer (-256-255)" }, + { ABS, ins_imms, ext_imms, 0, /* IMM14 */ + {{ 7, 13}, { 6, 27}, { 1, 36}}, SDEC, + "a 14-bit integer (-8192-8191)" }, + { ABS, ins_imms1, ext_imms1, 0, /* IMM17 */ + {{ 7, 6}, { 8, 24}, { 1, 36}}, 0, + "a 17-bit integer (-65536-65535)" }, + { ABS, ins_immu, ext_immu, 0, {{20, 6}, { 1, 36}}, 0, /* IMMU21 */ + "a 21-bit unsigned" }, + { ABS, ins_imms, ext_imms, 0, /* IMM22 */ + {{ 7, 13}, { 9, 27}, { 5, 22}, { 1, 36}}, SDEC, + "a 22-bit signed integer" }, + { ABS, ins_immu, ext_immu, 0, /* IMMU24 */ + {{21, 6}, { 2, 31}, { 1, 36}}, 0, + "a 24-bit unsigned" }, + { ABS, ins_imms16,ext_imms16,0, {{27, 6}, { 1, 36}}, 0, /* IMM44 */ + "a 44-bit unsigned (least 16 bits ignored/zeroes)" }, + { ABS, ins_rsvd, ext_rsvd, 0, {{0, 0}}, 0, /* IMMU62 */ + "a 62-bit unsigned" }, + { ABS, ins_rsvd, ext_rsvd, 0, {{0, 0}}, 0, /* IMMU64 */ + "a 64-bit unsigned" }, + { ABS, ins_inc3, ext_inc3, 0, {{ 3, 13}}, SDEC, /* INC3 */ + "an increment (+/- 1, 4, 8, or 16)" }, + { ABS, ins_cnt, ext_cnt, 0, {{ 4, 27}}, UDEC, /* LEN4 */ + "a 4-bit length (1-16)" }, + { ABS, ins_cnt, ext_cnt, 0, {{ 6, 27}}, UDEC, /* LEN6 */ + "a 6-bit length (1-64)" }, + { ABS, ins_immu, ext_immu, 0, {{ 4, 20}}, 0, /* MBTYPE4 */ + "a mix type (@rev, @mix, @shuf, @alt, or @brcst)" }, + { ABS, ins_immu, ext_immu, 0, {{ 8, 20}}, 0, /* MBTYPE8 */ + "an 8-bit mix type" }, + { ABS, ins_immu, ext_immu, 0, {{ 6, 14}}, UDEC, /* POS6 */ + "a 6-bit bit pos (0-63)" }, + { REL, ins_imms4, ext_imms4, 0, {{ 7, 6}, { 2, 33}}, 0, /* TAG13 */ + "a branch tag" }, + { REL, ins_imms4, ext_imms4, 0, {{ 9, 24}}, 0, /* TAG13b */ + "a branch tag" }, + { REL, ins_imms4, ext_imms4, 0, {{20, 6}, { 1, 36}}, 0, /* TGT25 */ + "a branch target" }, + { REL, ins_imms4, ext_imms4, 0, /* TGT25b */ + {{ 7, 6}, {13, 20}, { 1, 36}}, 0, + "a branch target" }, + { REL, ins_imms4, ext_imms4, 0, {{20, 13}, { 1, 36}}, 0, /* TGT25c */ + "a branch target" }, + { REL, ins_rsvd, ext_rsvd, 0, {{0, 0}}, 0, /* TGT64 */ + "a branch target" }, + + { ABS, ins_const, ext_const, 0, {{0, 0}}, 0, /* LDXMOV */ + "ldxmov target" }, + }; + + +/* ia64-asmtab.h -- Header for compacted IA-64 opcode tables. + Copyright 1999, 2000 Free Software Foundation, Inc. + Contributed by Bob Manson of Cygnus Support + + This file is part of GDB, GAS, and the GNU binutils. + + GDB, GAS, and the GNU binutils are free software; you can redistribute + them and/or modify them under the terms of the GNU General Public + License as published by the Free Software Foundation; either version + 2, or (at your option) any later version. + + GDB, GAS, and the GNU binutils are distributed in the hope that they + 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 file; see the file COPYING. If not, see + . */ + +/* The primary opcode table is made up of the following: */ +struct ia64_main_table +{ + /* The entry in the string table that corresponds to the name of this + opcode. */ + unsigned short name_index; + + /* The type of opcode; corresponds to the TYPE field in + struct ia64_opcode. */ + unsigned char opcode_type; + + /* The number of outputs for this opcode. */ + unsigned char num_outputs; + + /* The base insn value for this opcode. It may be modified by completers. */ + ia64_insn opcode; + + /* The mask of valid bits in OPCODE. Zeros indicate operand fields. */ + ia64_insn mask; + + /* The operands of this instruction. Corresponds to the OPERANDS field + in struct ia64_opcode. */ + unsigned char operands[5]; + + /* The flags for this instruction. Corresponds to the FLAGS field in + struct ia64_opcode. */ + short flags; + + /* The tree of completers for this instruction; this is an offset into + completer_table. */ + short completers; +}; + +/* Each instruction has a set of possible "completers", or additional + suffixes that can alter the instruction's behavior, and which has + potentially different dependencies. + + The completer entries modify certain bits in the instruction opcode. + Which bits are to be modified are marked by the BITS, MASK and + OFFSET fields. The completer entry may also note dependencies for the + opcode. + + These completers are arranged in a DAG; the pointers are indexes + into the completer_table array. The completer DAG is searched by + find_completer () and ia64_find_matching_opcode (). + + Note that each completer needs to be applied in turn, so that if we + have the instruction + cmp.lt.unc + the completer entries for both "lt" and "unc" would need to be applied + to the opcode's value. + + Some instructions do not require any completers; these contain an + empty completer entry. Instructions that require a completer do + not contain an empty entry. + + Terminal completers (those completers that validly complete an + instruction) are marked by having the TERMINAL_COMPLETER flag set. + + Only dependencies listed in the terminal completer for an opcode are + considered to apply to that opcode instance. */ + +struct ia64_completer_table +{ + /* The bit value that this completer sets. */ + unsigned int bits; + + /* And its mask. 1s are bits that are to be modified in the + instruction. */ + unsigned int mask; + + /* The entry in the string table that corresponds to the name of this + completer. */ + unsigned short name_index; + + /* An alternative completer, or -1 if this is the end of the chain. */ + short alternative; + + /* A pointer to the DAG of completers that can potentially follow + this one, or -1. */ + short subentries; + + /* The bit offset in the instruction where BITS and MASK should be + applied. */ + unsigned char offset : 7; + + unsigned char terminal_completer : 1; + + /* Index into the dependency list table */ + short dependencies; +}; + +/* This contains sufficient information for the disassembler to resolve + the complete name of the original instruction. */ +struct ia64_dis_names +{ + /* COMPLETER_INDEX represents the tree of completers that make up + the instruction. The LSB represents the top of the tree for the + specified instruction. + + A 0 bit indicates to go to the next alternate completer via the + alternative field; a 1 bit indicates that the current completer + is part of the instruction, and to go down the subentries index. + We know we've reached the final completer when we run out of 1 + bits. + + There is always at least one 1 bit. */ + unsigned int completer_index : 20; + + /* The index in the main_table[] array for the instruction. */ + unsigned short insn_index : 11; + + /* If set, the next entry in this table is an alternate possibility + for this instruction encoding. Which one to use is determined by + the instruction type and other factors (see opcode_verify ()). */ + unsigned int next_flag : 1; + + /* The disassembly priority of this entry among instructions. */ + unsigned short priority; +}; + +static const char * const ia64_strings[] = { + "", "0", "1", "a", "acq", "add", "addl", "addp4", "adds", "alloc", "and", + "andcm", "b", "bias", "br", "break", "brl", "brp", "bsw", "c", "call", + "cexit", "chk", "cloop", "clr", "clrrrb", "cmp", "cmp4", "cmp8xchg16", + "cmpxchg1", "cmpxchg2", "cmpxchg4", "cmpxchg8", "cond", "cover", "ctop", + "czx1", "czx2", "d", "dep", "dpnt", "dptk", "e", "epc", "eq", "excl", + "exit", "exp", "extr", "f", "fabs", "fadd", "famax", "famin", "fand", + "fandcm", "fault", "fc", "fchkf", "fclass", "fclrf", "fcmp", "fcvt", + "fetchadd4", "fetchadd8", "few", "fill", "flushrs", "fma", "fmax", + "fmerge", "fmin", "fmix", "fmpy", "fms", "fneg", "fnegabs", "fnma", + "fnmpy", "fnorm", "for", "fpabs", "fpack", "fpamax", "fpamin", "fpcmp", + "fpcvt", "fpma", "fpmax", "fpmerge", "fpmin", "fpmpy", "fpms", "fpneg", + "fpnegabs", "fpnma", "fpnmpy", "fprcpa", "fprsqrta", "frcpa", "frsqrta", + "fselect", "fsetc", "fsub", "fswap", "fsxt", "fwb", "fx", "fxor", "fxu", + "g", "ga", "ge", "getf", "geu", "gt", "gtu", "h", "hint", "hu", "i", "ia", + "imp", "invala", "itc", "itr", "l", "ld1", "ld16", "ld2", "ld4", "ld8", + "ldf", "ldf8", "ldfd", "ldfe", "ldfp8", "ldfpd", "ldfps", "ldfs", "le", + "leu", "lfetch", "loadrs", "loop", "lr", "lt", "ltu", "lu", "m", "many", + "mf", "mix1", "mix2", "mix4", "mov", "movl", "mux1", "mux2", "nc", "ne", + "neq", "nge", "ngt", "nl", "nle", "nlt", "nm", "nop", "nr", "ns", "nt1", + "nt2", "nta", "nz", "or", "orcm", "ord", "pack2", "pack4", "padd1", + "padd2", "padd4", "pavg1", "pavg2", "pavgsub1", "pavgsub2", "pcmp1", + "pcmp2", "pcmp4", "pmax1", "pmax2", "pmin1", "pmin2", "pmpy2", "pmpyshr2", + "popcnt", "pr", "probe", "psad1", "pshl2", "pshl4", "pshladd2", "pshr2", + "pshr4", "pshradd2", "psub1", "psub2", "psub4", "ptc", "ptr", "r", "raz", + "rel", "ret", "rfi", "rsm", "rum", "rw", "s", "s0", "s1", "s2", "s3", + "sa", "se", "setf", "shl", "shladd", "shladdp4", "shr", "shrp", "sig", + "spill", "spnt", "sptk", "srlz", "ssm", "sss", "st1", "st16", "st2", + "st4", "st8", "stf", "stf8", "stfd", "stfe", "stfs", "sub", "sum", "sxt1", + "sxt2", "sxt4", "sync", "tak", "tbit", "tf", "thash", "tnat", "tpa", + "trunc", "ttag", "u", "unc", "unord", "unpack1", "unpack2", "unpack4", + "uss", "uus", "uuu", "vmsw", "w", "wexit", "wtop", "x", "xchg1", "xchg2", + "xchg4", "xchg8", "xf", "xma", "xmpy", "xor", "xuf", "z", "zxt1", "zxt2", + "zxt4", +}; + +static const struct ia64_dependency +dependencies[] = { + { "ALAT", 0, 0, 0, -1, NULL, }, + { "AR[BSP]", 26, 0, 2, 17, NULL, }, + { "AR[BSPSTORE]", 26, 0, 2, 18, NULL, }, + { "AR[CCV]", 26, 0, 2, 32, NULL, }, + { "AR[CFLG]", 26, 0, 2, 27, NULL, }, + { "AR[CSD]", 26, 0, 2, 25, NULL, }, + { "AR[EC]", 26, 0, 2, 66, NULL, }, + { "AR[EFLAG]", 26, 0, 2, 24, NULL, }, + { "AR[FCR]", 26, 0, 2, 21, NULL, }, + { "AR[FDR]", 26, 0, 2, 30, NULL, }, + { "AR[FIR]", 26, 0, 2, 29, NULL, }, + { "AR[FPSR].sf0.controls", 30, 0, 2, -1, NULL, }, + { "AR[FPSR].sf1.controls", 30, 0, 2, -1, NULL, }, + { "AR[FPSR].sf2.controls", 30, 0, 2, -1, NULL, }, + { "AR[FPSR].sf3.controls", 30, 0, 2, -1, NULL, }, + { "AR[FPSR].sf0.flags", 30, 0, 2, -1, NULL, }, + { "AR[FPSR].sf1.flags", 30, 0, 2, -1, NULL, }, + { "AR[FPSR].sf2.flags", 30, 0, 2, -1, NULL, }, + { "AR[FPSR].sf3.flags", 30, 0, 2, -1, NULL, }, + { "AR[FPSR].traps", 30, 0, 2, -1, NULL, }, + { "AR[FPSR].rv", 30, 0, 2, -1, NULL, }, + { "AR[FSR]", 26, 0, 2, 28, NULL, }, + { "AR[ITC]", 26, 0, 2, 44, NULL, }, + { "AR[K%], % in 0 - 7", 1, 0, 2, -1, NULL, }, + { "AR[LC]", 26, 0, 2, 65, NULL, }, + { "AR[PFS]", 26, 0, 2, 64, NULL, }, + { "AR[PFS]", 26, 0, 2, 64, NULL, }, + { "AR[PFS]", 26, 0, 0, 64, NULL, }, + { "AR[RNAT]", 26, 0, 2, 19, NULL, }, + { "AR[RSC]", 26, 0, 2, 16, NULL, }, + { "AR[SSD]", 26, 0, 2, 26, NULL, }, + { "AR[UNAT]{%}, % in 0 - 63", 2, 0, 2, -1, NULL, }, + { "AR%, % in 8-15, 20, 22-23, 31, 33-35, 37-39, 41-43, 45-47, 67-111", 3, 0, 0, -1, NULL, }, + { "AR%, % in 48-63, 112-127", 4, 0, 2, -1, NULL, }, + { "BR%, % in 0 - 7", 5, 0, 2, -1, NULL, }, + { "BR%, % in 0 - 7", 5, 0, 0, -1, NULL, }, + { "BR%, % in 0 - 7", 5, 0, 2, -1, NULL, }, + { "CFM", 6, 0, 2, -1, NULL, }, + { "CFM", 6, 0, 2, -1, NULL, }, + { "CFM", 6, 0, 2, -1, NULL, }, + { "CFM", 6, 0, 2, -1, NULL, }, + { "CFM", 6, 0, 0, -1, NULL, }, + { "CPUID#", 7, 0, 5, -1, NULL, }, + { "CR[CMCV]", 27, 0, 3, 74, NULL, }, + { "CR[DCR]", 27, 0, 3, 0, NULL, }, + { "CR[EOI]", 27, 0, 7, 67, "SC Section 5.8.3.4, \"End of External Interrupt Register (EOI Ã CR67)\" on page 2:119", }, + { "CR[GPTA]", 27, 0, 3, 9, NULL, }, + { "CR[IFA]", 27, 0, 1, 20, NULL, }, + { "CR[IFA]", 27, 0, 3, 20, NULL, }, + { "CR[IFS]", 27, 0, 3, 23, NULL, }, + { "CR[IFS]", 27, 0, 1, 23, NULL, }, + { "CR[IFS]", 27, 0, 1, 23, NULL, }, + { "CR[IHA]", 27, 0, 3, 25, NULL, }, + { "CR[IIM]", 27, 0, 3, 24, NULL, }, + { "CR[IIP]", 27, 0, 3, 19, NULL, }, + { "CR[IIP]", 27, 0, 1, 19, NULL, }, + { "CR[IIPA]", 27, 0, 3, 22, NULL, }, + { "CR[IPSR]", 27, 0, 3, 16, NULL, }, + { "CR[IPSR]", 27, 0, 1, 16, NULL, }, + { "CR[IRR%], % in 0 - 3", 8, 0, 3, -1, NULL, }, + { "CR[ISR]", 27, 0, 3, 17, NULL, }, + { "CR[ITIR]", 27, 0, 3, 21, NULL, }, + { "CR[ITIR]", 27, 0, 1, 21, NULL, }, + { "CR[ITM]", 27, 0, 3, 1, NULL, }, + { "CR[ITV]", 27, 0, 3, 72, NULL, }, + { "CR[IVA]", 27, 0, 4, 2, NULL, }, + { "CR[IVR]", 27, 0, 7, 65, "SC Section 5.8.3.2, \"External Interrupt Vector Register (IVR Ã CR65)\" on page 2:118", }, + { "CR[LID]", 27, 0, 7, 64, "SC Section 5.8.3.1, \"Local ID (LID Ã CR64)\" on page 2:117", }, + { "CR[LRR%], % in 0 - 1", 9, 0, 3, -1, NULL, }, + { "CR[PMV]", 27, 0, 3, 73, NULL, }, + { "CR[PTA]", 27, 0, 3, 8, NULL, }, + { "CR[TPR]", 27, 0, 3, 66, NULL, }, + { "CR[TPR]", 27, 0, 7, 66, "SC Section 5.8.3.3, \"Task Priority Register (TPR Ã CR66)\" on page 2:119", }, + { "CR[TPR]", 27, 0, 1, 66, NULL, }, + { "CR%, % in 3-7, 10-15, 18, 26-63, 75-79, 82-127", 10, 0, 0, -1, NULL, }, + { "DBR#", 11, 0, 2, -1, NULL, }, + { "DBR#", 11, 0, 3, -1, NULL, }, + { "DTC", 0, 0, 3, -1, NULL, }, + { "DTC", 0, 0, 2, -1, NULL, }, + { "DTC", 0, 0, 0, -1, NULL, }, + { "DTC", 0, 0, 2, -1, NULL, }, + { "DTC_LIMIT*", 0, 0, 2, -1, NULL, }, + { "DTR", 0, 0, 3, -1, NULL, }, + { "DTR", 0, 0, 2, -1, NULL, }, + { "DTR", 0, 0, 3, -1, NULL, }, + { "DTR", 0, 0, 0, -1, NULL, }, + { "DTR", 0, 0, 2, -1, NULL, }, + { "FR%, % in 0 - 1", 12, 0, 0, -1, NULL, }, + { "FR%, % in 2 - 127", 13, 0, 2, -1, NULL, }, + { "FR%, % in 2 - 127", 13, 0, 0, -1, NULL, }, + { "GR0", 14, 0, 0, -1, NULL, }, + { "GR%, % in 1 - 127", 15, 0, 0, -1, NULL, }, + { "GR%, % in 1 - 127", 15, 0, 2, -1, NULL, }, + { "IBR#", 16, 0, 2, -1, NULL, }, + { "InService*", 17, 0, 3, -1, NULL, }, + { "InService*", 17, 0, 2, -1, NULL, }, + { "InService*", 17, 0, 2, -1, NULL, }, + { "IP", 0, 0, 0, -1, NULL, }, + { "ITC", 0, 0, 4, -1, NULL, }, + { "ITC", 0, 0, 2, -1, NULL, }, + { "ITC", 0, 0, 0, -1, NULL, }, + { "ITC", 0, 0, 4, -1, NULL, }, + { "ITC", 0, 0, 2, -1, NULL, }, + { "ITC_LIMIT*", 0, 0, 2, -1, NULL, }, + { "ITR", 0, 0, 2, -1, NULL, }, + { "ITR", 0, 0, 4, -1, NULL, }, + { "ITR", 0, 0, 2, -1, NULL, }, + { "ITR", 0, 0, 0, -1, NULL, }, + { "ITR", 0, 0, 4, -1, NULL, }, + { "memory", 0, 0, 0, -1, NULL, }, + { "MSR#", 18, 0, 5, -1, NULL, }, + { "PKR#", 19, 0, 3, -1, NULL, }, + { "PKR#", 19, 0, 0, -1, NULL, }, + { "PKR#", 19, 0, 2, -1, NULL, }, + { "PKR#", 19, 0, 2, -1, NULL, }, + { "PMC#", 20, 0, 2, -1, NULL, }, + { "PMC#", 20, 0, 7, -1, "SC Section 7.2.1, \"Generic Performance Counter Registers\" for PMC[0].fr on page 2:150", }, + { "PMD#", 21, 0, 2, -1, NULL, }, + { "PR0", 0, 0, 0, -1, NULL, }, + { "PR%, % in 1 - 15", 22, 0, 2, -1, NULL, }, + { "PR%, % in 1 - 15", 22, 0, 2, -1, NULL, }, + { "PR%, % in 1 - 15", 22, 0, 0, -1, NULL, }, + { "PR%, % in 16 - 62", 23, 0, 2, -1, NULL, }, + { "PR%, % in 16 - 62", 23, 0, 2, -1, NULL, }, + { "PR%, % in 16 - 62", 23, 0, 0, -1, NULL, }, + { "PR63", 24, 0, 2, -1, NULL, }, + { "PR63", 24, 0, 2, -1, NULL, }, + { "PR63", 24, 0, 0, -1, NULL, }, + { "PSR.ac", 28, 0, 1, 3, NULL, }, + { "PSR.ac", 28, 0, 3, 3, NULL, }, + { "PSR.ac", 28, 0, 2, 3, NULL, }, + { "PSR.ac", 28, 0, 2, 3, NULL, }, + { "PSR.be", 28, 0, 1, 1, NULL, }, + { "PSR.be", 28, 0, 3, 1, NULL, }, + { "PSR.be", 28, 0, 2, 1, NULL, }, + { "PSR.be", 28, 0, 2, 1, NULL, }, + { "PSR.bn", 28, 0, 2, 44, NULL, }, + { "PSR.cpl", 28, 0, 1, 32, NULL, }, + { "PSR.cpl", 28, 0, 2, 32, NULL, }, + { "PSR.da", 28, 0, 2, 38, NULL, }, + { "PSR.db", 28, 0, 3, 24, NULL, }, + { "PSR.db", 28, 0, 2, 24, NULL, }, + { "PSR.db", 28, 0, 2, 24, NULL, }, + { "PSR.dd", 28, 0, 2, 39, NULL, }, + { "PSR.dfh", 28, 0, 3, 19, NULL, }, + { "PSR.dfh", 28, 0, 2, 19, NULL, }, + { "PSR.dfh", 28, 0, 2, 19, NULL, }, + { "PSR.dfl", 28, 0, 3, 18, NULL, }, + { "PSR.dfl", 28, 0, 2, 18, NULL, }, + { "PSR.dfl", 28, 0, 2, 18, NULL, }, + { "PSR.di", 28, 0, 3, 22, NULL, }, + { "PSR.di", 28, 0, 2, 22, NULL, }, + { "PSR.di", 28, 0, 2, 22, NULL, }, + { "PSR.dt", 28, 0, 3, 17, NULL, }, + { "PSR.dt", 28, 0, 2, 17, NULL, }, + { "PSR.dt", 28, 0, 2, 17, NULL, }, + { "PSR.ed", 28, 0, 2, 43, NULL, }, + { "PSR.i", 28, 0, 2, 14, NULL, }, + { "PSR.ia", 28, 0, 0, 14, NULL, }, + { "PSR.ic", 28, 0, 2, 13, NULL, }, + { "PSR.ic", 28, 0, 3, 13, NULL, }, + { "PSR.ic", 28, 0, 2, 13, NULL, }, + { "PSR.id", 28, 0, 0, 14, NULL, }, + { "PSR.is", 28, 0, 0, 14, NULL, }, + { "PSR.it", 28, 0, 2, 14, NULL, }, + { "PSR.lp", 28, 0, 2, 25, NULL, }, + { "PSR.lp", 28, 0, 3, 25, NULL, }, + { "PSR.lp", 28, 0, 2, 25, NULL, }, + { "PSR.mc", 28, 0, 2, 35, NULL, }, + { "PSR.mfh", 28, 0, 2, 5, NULL, }, + { "PSR.mfl", 28, 0, 2, 4, NULL, }, + { "PSR.pk", 28, 0, 3, 15, NULL, }, + { "PSR.pk", 28, 0, 2, 15, NULL, }, + { "PSR.pk", 28, 0, 2, 15, NULL, }, + { "PSR.pp", 28, 0, 2, 21, NULL, }, + { "PSR.ri", 28, 0, 0, 41, NULL, }, + { "PSR.rt", 28, 0, 2, 27, NULL, }, + { "PSR.rt", 28, 0, 3, 27, NULL, }, + { "PSR.rt", 28, 0, 2, 27, NULL, }, + { "PSR.si", 28, 0, 2, 23, NULL, }, + { "PSR.si", 28, 0, 3, 23, NULL, }, + { "PSR.si", 28, 0, 2, 23, NULL, }, + { "PSR.sp", 28, 0, 2, 20, NULL, }, + { "PSR.sp", 28, 0, 3, 20, NULL, }, + { "PSR.sp", 28, 0, 2, 20, NULL, }, + { "PSR.ss", 28, 0, 2, 40, NULL, }, + { "PSR.tb", 28, 0, 3, 26, NULL, }, + { "PSR.tb", 28, 0, 2, 26, NULL, }, + { "PSR.tb", 28, 0, 2, 26, NULL, }, + { "PSR.up", 28, 0, 2, 2, NULL, }, + { "PSR.vm", 28, 0, 1, 46, NULL, }, + { "PSR.vm", 28, 0, 2, 46, NULL, }, + { "RR#", 25, 0, 3, -1, NULL, }, + { "RR#", 25, 0, 2, -1, NULL, }, + { "RSE", 29, 0, 2, -1, NULL, }, + { "ALAT", 0, 1, 0, -1, NULL, }, + { "AR[BSP]", 26, 1, 2, 17, NULL, }, + { "AR[BSPSTORE]", 26, 1, 2, 18, NULL, }, + { "AR[CCV]", 26, 1, 2, 32, NULL, }, + { "AR[CFLG]", 26, 1, 2, 27, NULL, }, + { "AR[CSD]", 26, 1, 2, 25, NULL, }, + { "AR[EC]", 26, 1, 2, 66, NULL, }, + { "AR[EFLAG]", 26, 1, 2, 24, NULL, }, + { "AR[FCR]", 26, 1, 2, 21, NULL, }, + { "AR[FDR]", 26, 1, 2, 30, NULL, }, + { "AR[FIR]", 26, 1, 2, 29, NULL, }, + { "AR[FPSR].sf0.controls", 30, 1, 2, -1, NULL, }, + { "AR[FPSR].sf1.controls", 30, 1, 2, -1, NULL, }, + { "AR[FPSR].sf2.controls", 30, 1, 2, -1, NULL, }, + { "AR[FPSR].sf3.controls", 30, 1, 2, -1, NULL, }, + { "AR[FPSR].sf0.flags", 30, 1, 0, -1, NULL, }, + { "AR[FPSR].sf0.flags", 30, 1, 2, -1, NULL, }, + { "AR[FPSR].sf0.flags", 30, 1, 2, -1, NULL, }, + { "AR[FPSR].sf1.flags", 30, 1, 0, -1, NULL, }, + { "AR[FPSR].sf1.flags", 30, 1, 2, -1, NULL, }, + { "AR[FPSR].sf1.flags", 30, 1, 2, -1, NULL, }, + { "AR[FPSR].sf2.flags", 30, 1, 0, -1, NULL, }, + { "AR[FPSR].sf2.flags", 30, 1, 2, -1, NULL, }, + { "AR[FPSR].sf2.flags", 30, 1, 2, -1, NULL, }, + { "AR[FPSR].sf3.flags", 30, 1, 0, -1, NULL, }, + { "AR[FPSR].sf3.flags", 30, 1, 2, -1, NULL, }, + { "AR[FPSR].sf3.flags", 30, 1, 2, -1, NULL, }, + { "AR[FPSR].rv", 30, 1, 2, -1, NULL, }, + { "AR[FPSR].traps", 30, 1, 2, -1, NULL, }, + { "AR[FSR]", 26, 1, 2, 28, NULL, }, + { "AR[ITC]", 26, 1, 2, 44, NULL, }, + { "AR[K%], % in 0 - 7", 1, 1, 2, -1, NULL, }, + { "AR[LC]", 26, 1, 2, 65, NULL, }, + { "AR[PFS]", 26, 1, 0, 64, NULL, }, + { "AR[PFS]", 26, 1, 2, 64, NULL, }, + { "AR[PFS]", 26, 1, 2, 64, NULL, }, + { "AR[RNAT]", 26, 1, 2, 19, NULL, }, + { "AR[RSC]", 26, 1, 2, 16, NULL, }, + { "AR[SSD]", 26, 1, 2, 26, NULL, }, + { "AR[UNAT]{%}, % in 0 - 63", 2, 1, 2, -1, NULL, }, + { "AR%, % in 8-15, 20, 22-23, 31, 33-35, 37-39, 41-43, 45-47, 67-111", 3, 1, 0, -1, NULL, }, + { "AR%, % in 48 - 63, 112-127", 4, 1, 2, -1, NULL, }, + { "BR%, % in 0 - 7", 5, 1, 2, -1, NULL, }, + { "BR%, % in 0 - 7", 5, 1, 2, -1, NULL, }, + { "BR%, % in 0 - 7", 5, 1, 2, -1, NULL, }, + { "BR%, % in 0 - 7", 5, 1, 0, -1, NULL, }, + { "CFM", 6, 1, 2, -1, NULL, }, + { "CPUID#", 7, 1, 0, -1, NULL, }, + { "CR[CMCV]", 27, 1, 2, 74, NULL, }, + { "CR[DCR]", 27, 1, 2, 0, NULL, }, + { "CR[EOI]", 27, 1, 7, 67, "SC Section 5.8.3.4, \"End of External Interrupt Register (EOI Ã CR67)\" on page 2:119", }, + { "CR[GPTA]", 27, 1, 2, 9, NULL, }, + { "CR[IFA]", 27, 1, 2, 20, NULL, }, + { "CR[IFS]", 27, 1, 2, 23, NULL, }, + { "CR[IHA]", 27, 1, 2, 25, NULL, }, + { "CR[IIM]", 27, 1, 2, 24, NULL, }, + { "CR[IIP]", 27, 1, 2, 19, NULL, }, + { "CR[IIPA]", 27, 1, 2, 22, NULL, }, + { "CR[IPSR]", 27, 1, 2, 16, NULL, }, + { "CR[IRR%], % in 0 - 3", 8, 1, 2, -1, NULL, }, + { "CR[ISR]", 27, 1, 2, 17, NULL, }, + { "CR[ITIR]", 27, 1, 2, 21, NULL, }, + { "CR[ITM]", 27, 1, 2, 1, NULL, }, + { "CR[ITV]", 27, 1, 2, 72, NULL, }, + { "CR[IVA]", 27, 1, 2, 2, NULL, }, + { "CR[IVR]", 27, 1, 7, 65, "SC", }, + { "CR[LID]", 27, 1, 7, 64, "SC", }, + { "CR[LRR%], % in 0 - 1", 9, 1, 2, -1, NULL, }, + { "CR[PMV]", 27, 1, 2, 73, NULL, }, + { "CR[PTA]", 27, 1, 2, 8, NULL, }, + { "CR[TPR]", 27, 1, 2, 66, NULL, }, + { "CR%, % in 3-7, 10-15, 18, 26-63, 75-79, 82-127", 10, 1, 0, -1, NULL, }, + { "DBR#", 11, 1, 2, -1, NULL, }, + { "DTC", 0, 1, 0, -1, NULL, }, + { "DTC", 0, 1, 2, -1, NULL, }, + { "DTC", 0, 1, 2, -1, NULL, }, + { "DTC_LIMIT*", 0, 1, 2, -1, NULL, }, + { "DTR", 0, 1, 2, -1, NULL, }, + { "DTR", 0, 1, 2, -1, NULL, }, + { "DTR", 0, 1, 2, -1, NULL, }, + { "DTR", 0, 1, 0, -1, NULL, }, + { "FR%, % in 0 - 1", 12, 1, 0, -1, NULL, }, + { "FR%, % in 2 - 127", 13, 1, 2, -1, NULL, }, + { "GR0", 14, 1, 0, -1, NULL, }, + { "GR%, % in 1 - 127", 15, 1, 2, -1, NULL, }, + { "IBR#", 16, 1, 2, -1, NULL, }, + { "InService*", 17, 1, 7, -1, "SC", }, + { "IP", 0, 1, 0, -1, NULL, }, + { "ITC", 0, 1, 0, -1, NULL, }, + { "ITC", 0, 1, 2, -1, NULL, }, + { "ITC", 0, 1, 2, -1, NULL, }, + { "ITR", 0, 1, 2, -1, NULL, }, + { "ITR", 0, 1, 2, -1, NULL, }, + { "ITR", 0, 1, 0, -1, NULL, }, + { "memory", 0, 1, 0, -1, NULL, }, + { "MSR#", 18, 1, 7, -1, "SC", }, + { "PKR#", 19, 1, 0, -1, NULL, }, + { "PKR#", 19, 1, 0, -1, NULL, }, + { "PKR#", 19, 1, 2, -1, NULL, }, + { "PMC#", 20, 1, 2, -1, NULL, }, + { "PMD#", 21, 1, 2, -1, NULL, }, + { "PR0", 0, 1, 0, -1, NULL, }, + { "PR%, % in 1 - 15", 22, 1, 0, -1, NULL, }, + { "PR%, % in 1 - 15", 22, 1, 0, -1, NULL, }, + { "PR%, % in 1 - 15", 22, 1, 2, -1, NULL, }, + { "PR%, % in 1 - 15", 22, 1, 2, -1, NULL, }, + { "PR%, % in 16 - 62", 23, 1, 0, -1, NULL, }, + { "PR%, % in 16 - 62", 23, 1, 0, -1, NULL, }, + { "PR%, % in 16 - 62", 23, 1, 2, -1, NULL, }, + { "PR%, % in 16 - 62", 23, 1, 2, -1, NULL, }, + { "PR63", 24, 1, 0, -1, NULL, }, + { "PR63", 24, 1, 0, -1, NULL, }, + { "PR63", 24, 1, 2, -1, NULL, }, + { "PR63", 24, 1, 2, -1, NULL, }, + { "PSR.ac", 28, 1, 2, 3, NULL, }, + { "PSR.be", 28, 1, 2, 1, NULL, }, + { "PSR.bn", 28, 1, 2, 44, NULL, }, + { "PSR.cpl", 28, 1, 2, 32, NULL, }, + { "PSR.da", 28, 1, 2, 38, NULL, }, + { "PSR.db", 28, 1, 2, 24, NULL, }, + { "PSR.dd", 28, 1, 2, 39, NULL, }, + { "PSR.dfh", 28, 1, 2, 19, NULL, }, + { "PSR.dfl", 28, 1, 2, 18, NULL, }, + { "PSR.di", 28, 1, 2, 22, NULL, }, + { "PSR.dt", 28, 1, 2, 17, NULL, }, + { "PSR.ed", 28, 1, 2, 43, NULL, }, + { "PSR.i", 28, 1, 2, 14, NULL, }, + { "PSR.ia", 28, 1, 2, 14, NULL, }, + { "PSR.ic", 28, 1, 2, 13, NULL, }, + { "PSR.id", 28, 1, 2, 14, NULL, }, + { "PSR.is", 28, 1, 2, 14, NULL, }, + { "PSR.it", 28, 1, 2, 14, NULL, }, + { "PSR.lp", 28, 1, 2, 25, NULL, }, + { "PSR.mc", 28, 1, 2, 35, NULL, }, + { "PSR.mfh", 28, 1, 0, 5, NULL, }, + { "PSR.mfh", 28, 1, 2, 5, NULL, }, + { "PSR.mfh", 28, 1, 2, 5, NULL, }, + { "PSR.mfl", 28, 1, 0, 4, NULL, }, + { "PSR.mfl", 28, 1, 2, 4, NULL, }, + { "PSR.mfl", 28, 1, 2, 4, NULL, }, + { "PSR.pk", 28, 1, 2, 15, NULL, }, + { "PSR.pp", 28, 1, 2, 21, NULL, }, + { "PSR.ri", 28, 1, 2, 41, NULL, }, + { "PSR.rt", 28, 1, 2, 27, NULL, }, + { "PSR.si", 28, 1, 2, 23, NULL, }, + { "PSR.sp", 28, 1, 2, 20, NULL, }, + { "PSR.ss", 28, 1, 2, 40, NULL, }, + { "PSR.tb", 28, 1, 2, 26, NULL, }, + { "PSR.up", 28, 1, 2, 2, NULL, }, + { "PSR.vm", 28, 1, 2, 46, NULL, }, + { "RR#", 25, 1, 2, -1, NULL, }, + { "RSE", 29, 1, 2, -1, NULL, }, + { "PR63", 24, 2, 6, -1, NULL, }, +}; + +static const unsigned short dep0[] = { + 97, 282, 2140, 2327, +}; + +static const unsigned short dep1[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, + 2327, 4135, 20616, +}; + +static const unsigned short dep2[] = { + 97, 282, 2166, 2167, 2169, 2170, 2172, 2173, 2175, 2344, 2347, 2348, 2351, + 2352, 2355, 2356, +}; + +static const unsigned short dep3[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, + 2344, 2347, 2348, 2351, 2352, 2355, 2356, 4135, 20616, +}; + +static const unsigned short dep4[] = { + 97, 282, 22646, 22647, 22649, 22650, 22652, 22653, 22655, 22824, 22827, 22828, + 22831, 22832, 22835, 22836, +}; + +static const unsigned short dep5[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, + 4135, 20616, 22824, 22827, 22828, 22831, 22832, 22835, 22836, +}; + +static const unsigned short dep6[] = { + 97, 282, 2166, 2167, 2169, 2170, 2172, 2173, 2175, 2344, 2345, 2347, 2349, + 2351, 2353, 2355, +}; + +static const unsigned short dep7[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, + 2344, 2345, 2348, 2349, 2352, 2353, 2356, 4135, 20616, +}; + +static const unsigned short dep8[] = { + 97, 282, 2166, 2167, 2169, 2170, 2172, 2173, 2175, 2344, 2346, 2348, 2350, + 2352, 2354, 2356, +}; + +static const unsigned short dep9[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, + 2344, 2346, 2347, 2350, 2351, 2354, 2355, 4135, 20616, +}; + +static const unsigned short dep10[] = { + 97, 282, 2166, 2167, 2169, 2170, 2172, 2173, 2175, 2344, 2345, 2346, 2347, + 2348, 2349, 2350, 2351, 2352, 2353, 2354, 2355, 2356, +}; + +static const unsigned short dep11[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, + 2344, 2345, 2346, 2347, 2348, 2349, 2350, 2351, 2352, 2353, 2354, 2355, 2356, + 4135, 20616, +}; + +static const unsigned short dep12[] = { + 97, 282, 2395, +}; + +static const unsigned short dep13[] = { + 40, 41, 97, 158, 162, 164, 175, 185, 186, 188, 282, 2082, 2083, 2166, 2168, + 2169, 2171, 2172, 2174, 2175, 4135, +}; + +static const unsigned short dep14[] = { + 97, 163, 282, 325, 2395, 28866, 29018, +}; + +static const unsigned short dep15[] = { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 28, 29, 30, 31, 32, 33, 40, 41, 97, 150, 152, 158, 162, + 164, 175, 185, 186, 188, 282, 325, 2082, 2083, 2166, 2168, 2169, 2171, 2172, + 2174, 2175, 4135, 28866, 29018, +}; + +static const unsigned short dep16[] = { + 1, 6, 40, 97, 137, 196, 201, 241, 282, 312, 2395, 28866, 29018, +}; + +static const unsigned short dep17[] = { + 1, 25, 27, 38, 40, 41, 97, 158, 162, 164, 166, 167, 175, 185, 186, 188, 196, + 201, 241, 282, 312, 2082, 2083, 2166, 2168, 2169, 2171, 2172, 2174, 2175, + 4135, 28866, 29018, +}; + +static const unsigned short dep18[] = { + 1, 40, 51, 97, 196, 241, 248, 282, 28866, 29018, +}; + +static const unsigned short dep19[] = { + 1, 38, 40, 41, 97, 158, 160, 161, 162, 175, 185, 190, 191, 196, 241, 248, + 282, 4135, 28866, 29018, +}; + +static const unsigned short dep20[] = { + 40, 97, 241, 282, +}; + +static const unsigned short dep21[] = { + 97, 158, 162, 175, 185, 241, 282, +}; + +static const unsigned short dep22[] = { + 1, 40, 97, 131, 135, 136, 138, 139, 142, 143, 146, 149, 152, 155, 156, 157, + 158, 161, 162, 163, 164, 167, 168, 169, 170, 173, 174, 175, 178, 181, 184, + 185, 188, 189, 191, 196, 241, 282, 309, 310, 311, 312, 313, 314, 315, 316, + 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 330, 331, 333, + 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 28866, 29018, +}; + +static const unsigned short dep23[] = { + 1, 38, 40, 41, 50, 51, 55, 58, 73, 97, 137, 138, 158, 162, 175, 185, 190, + 191, 196, 241, 282, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, + 320, 321, 322, 323, 324, 325, 326, 327, 328, 330, 331, 333, 334, 335, 336, + 337, 338, 339, 340, 341, 342, 343, 344, 4135, 28866, 29018, +}; + +static const unsigned short dep24[] = { + 97, 136, 282, 311, +}; + +static const unsigned short dep25[] = { + 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 311, +}; + +static const unsigned short dep26[] = { + 97, 137, 282, 312, +}; + +static const unsigned short dep27[] = { + 25, 26, 97, 98, 101, 105, 108, 137, 138, 158, 162, 164, 175, 185, 282, 312, + +}; + +static const unsigned short dep28[] = { + 97, 190, 282, 344, +}; + +static const unsigned short dep29[] = { + 97, 98, 101, 105, 108, 137, 138, 158, 162, 164, 175, 185, 282, 344, +}; + +static const unsigned short dep30[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2166, 2168, 2169, 2171, 2172, 2174, 2175, + 4135, +}; + +static const unsigned short dep31[] = { + 1, 25, 40, 97, 196, 228, 229, 241, 282, 2082, 2285, 2288, 2395, 28866, 29018, + +}; + +static const unsigned short dep32[] = { + 1, 6, 38, 40, 41, 97, 137, 138, 158, 162, 164, 175, 185, 186, 188, 196, 228, + 230, 241, 282, 2082, 2083, 2166, 2168, 2169, 2171, 2172, 2174, 2175, 2286, + 2288, 4135, 28866, 29018, +}; + +static const unsigned short dep33[] = { + 97, 282, +}; + +static const unsigned short dep34[] = { + 97, 158, 162, 175, 185, 282, 2082, 2084, +}; + +static const unsigned short dep35[] = { + 40, 41, 97, 158, 162, 164, 175, 185, 186, 188, 282, 2166, 2168, 2169, 2171, + 2172, 2174, 2175, 4135, +}; + +static const unsigned short dep36[] = { + 6, 37, 38, 39, 97, 125, 126, 201, 241, 282, 307, 308, 2395, +}; + +static const unsigned short dep37[] = { + 6, 37, 40, 41, 97, 158, 162, 164, 175, 185, 186, 188, 201, 241, 282, 307, + 308, 347, 2166, 2168, 2169, 2171, 2172, 2174, 2175, 4135, +}; + +static const unsigned short dep38[] = { + 24, 97, 227, 282, 2395, +}; + +static const unsigned short dep39[] = { + 24, 40, 41, 97, 158, 162, 164, 175, 185, 186, 188, 227, 282, 2166, 2168, 2169, + 2171, 2172, 2174, 2175, 4135, +}; + +static const unsigned short dep40[] = { + 6, 24, 37, 38, 39, 97, 125, 126, 201, 227, 241, 282, 307, 308, 2395, +}; + +static const unsigned short dep41[] = { + 6, 24, 37, 40, 41, 97, 158, 162, 164, 175, 185, 186, 188, 201, 227, 241, 282, + 307, 308, 347, 2166, 2168, 2169, 2171, 2172, 2174, 2175, 4135, +}; + +static const unsigned short dep42[] = { + 1, 6, 38, 40, 41, 97, 137, 138, 158, 162, 164, 175, 185, 186, 188, 196, 228, + 230, 241, 282, 2166, 2168, 2169, 2171, 2172, 2174, 2175, 2286, 2288, 4135, + 28866, 29018, +}; + +static const unsigned short dep43[] = { + 97, 158, 162, 175, 185, 282, +}; + +static const unsigned short dep44[] = { + 15, 97, 210, 211, 282, 2136, 2325, 18601, 18602, 18761, 18762, 18764, 18765, + 22646, 22647, 22648, 22650, 22651, 22653, 22654, 22824, 22827, 22828, 22831, + 22832, 22835, 22836, +}; + +static const unsigned short dep45[] = { + 11, 19, 20, 40, 41, 97, 158, 162, 175, 185, 210, 212, 282, 2135, 2136, 2137, + 2166, 2167, 2170, 2173, 2325, 4135, 16528, 16530, 16531, 16533, 18761, 18763, + 18764, 18766, 22824, 22827, 22828, 22831, 22832, 22835, 22836, +}; + +static const unsigned short dep46[] = { + 15, 16, 17, 18, 97, 210, 211, 213, 214, 216, 217, 219, 220, 282, 2136, 2325, + 18601, 18602, 18761, 18762, 18764, 18765, 22646, 22647, 22648, 22650, 22651, + 22653, 22654, 22824, 22827, 22828, 22831, 22832, 22835, 22836, +}; + +static const unsigned short dep47[] = { + 11, 12, 13, 14, 19, 20, 40, 41, 97, 158, 162, 175, 185, 210, 212, 213, 215, + 216, 218, 219, 221, 282, 2135, 2136, 2137, 2166, 2167, 2170, 2173, 2325, 4135, + 16528, 16530, 16531, 16533, 18761, 18763, 18764, 18766, 22824, 22827, 22828, + 22831, 22832, 22835, 22836, +}; + +static const unsigned short dep48[] = { + 16, 97, 213, 214, 282, 2136, 2325, 18601, 18602, 18761, 18762, 18764, 18765, + 22646, 22647, 22648, 22650, 22651, 22653, 22654, 22824, 22827, 22828, 22831, + 22832, 22835, 22836, +}; + +static const unsigned short dep49[] = { + 12, 19, 20, 40, 41, 97, 158, 162, 175, 185, 213, 215, 282, 2135, 2136, 2137, + 2166, 2167, 2170, 2173, 2325, 4135, 16528, 16530, 16531, 16533, 18761, 18763, + 18764, 18766, 22824, 22827, 22828, 22831, 22832, 22835, 22836, +}; + +static const unsigned short dep50[] = { + 17, 97, 216, 217, 282, 2136, 2325, 18601, 18602, 18761, 18762, 18764, 18765, + 22646, 22647, 22648, 22650, 22651, 22653, 22654, 22824, 22827, 22828, 22831, + 22832, 22835, 22836, +}; + +static const unsigned short dep51[] = { + 13, 19, 20, 40, 41, 97, 158, 162, 175, 185, 216, 218, 282, 2135, 2136, 2137, + 2166, 2167, 2170, 2173, 2325, 4135, 16528, 16530, 16531, 16533, 18761, 18763, + 18764, 18766, 22824, 22827, 22828, 22831, 22832, 22835, 22836, +}; + +static const unsigned short dep52[] = { + 18, 97, 219, 220, 282, 2136, 2325, 18601, 18602, 18761, 18762, 18764, 18765, + 22646, 22647, 22648, 22650, 22651, 22653, 22654, 22824, 22827, 22828, 22831, + 22832, 22835, 22836, +}; + +static const unsigned short dep53[] = { + 14, 19, 20, 40, 41, 97, 158, 162, 175, 185, 219, 221, 282, 2135, 2136, 2137, + 2166, 2167, 2170, 2173, 2325, 4135, 16528, 16530, 16531, 16533, 18761, 18763, + 18764, 18766, 22824, 22827, 22828, 22831, 22832, 22835, 22836, +}; + +static const unsigned short dep54[] = { + 15, 97, 210, 211, 282, 2136, 2325, 18601, 18602, 18761, 18762, 18764, 18765, + +}; + +static const unsigned short dep55[] = { + 11, 19, 20, 40, 41, 97, 158, 162, 175, 185, 210, 212, 282, 2135, 2136, 2137, + 2166, 2167, 2170, 2173, 2325, 4135, 16528, 16530, 16531, 16533, 18761, 18763, + 18764, 18766, +}; + +static const unsigned short dep56[] = { + 15, 16, 17, 18, 97, 210, 211, 213, 214, 216, 217, 219, 220, 282, 2136, 2325, + 18601, 18602, 18761, 18762, 18764, 18765, +}; + +static const unsigned short dep57[] = { + 11, 12, 13, 14, 19, 20, 40, 41, 97, 158, 162, 175, 185, 210, 212, 213, 215, + 216, 218, 219, 221, 282, 2135, 2136, 2137, 2166, 2167, 2170, 2173, 2325, 4135, + 16528, 16530, 16531, 16533, 18761, 18763, 18764, 18766, +}; + +static const unsigned short dep58[] = { + 16, 97, 213, 214, 282, 2136, 2325, 18601, 18602, 18761, 18762, 18764, 18765, + +}; + +static const unsigned short dep59[] = { + 12, 19, 20, 40, 41, 97, 158, 162, 175, 185, 213, 215, 282, 2135, 2136, 2137, + 2166, 2167, 2170, 2173, 2325, 4135, 16528, 16530, 16531, 16533, 18761, 18763, + 18764, 18766, +}; + +static const unsigned short dep60[] = { + 17, 97, 216, 217, 282, 2136, 2325, 18601, 18602, 18761, 18762, 18764, 18765, + +}; + +static const unsigned short dep61[] = { + 13, 19, 20, 40, 41, 97, 158, 162, 175, 185, 216, 218, 282, 2135, 2136, 2137, + 2166, 2167, 2170, 2173, 2325, 4135, 16528, 16530, 16531, 16533, 18761, 18763, + 18764, 18766, +}; + +static const unsigned short dep62[] = { + 18, 97, 219, 220, 282, 2136, 2325, 18601, 18602, 18761, 18762, 18764, 18765, + +}; + +static const unsigned short dep63[] = { + 14, 19, 20, 40, 41, 97, 158, 162, 175, 185, 219, 221, 282, 2135, 2136, 2137, + 2166, 2167, 2170, 2173, 2325, 4135, 16528, 16530, 16531, 16533, 18761, 18763, + 18764, 18766, +}; + +static const unsigned short dep64[] = { + 97, 282, 2136, 2325, 18601, 18602, 18761, 18762, 18764, 18765, +}; + +static const unsigned short dep65[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2135, 2136, 2137, 2166, 2167, 2170, 2173, + 2325, 4135, 16528, 16530, 16531, 16533, 18761, 18763, 18764, 18766, +}; + +static const unsigned short dep66[] = { + 11, 97, 206, 282, +}; + +static const unsigned short dep67[] = { + 11, 40, 41, 97, 158, 162, 175, 185, 206, 282, 2166, 2167, 2170, 2173, 4135, + +}; + +static const unsigned short dep68[] = { + 11, 40, 41, 97, 158, 162, 175, 185, 282, 2166, 2167, 2170, 2173, 4135, +}; + +static const unsigned short dep69[] = { + 12, 97, 207, 282, +}; + +static const unsigned short dep70[] = { + 11, 40, 41, 97, 158, 162, 175, 185, 207, 282, 2166, 2167, 2170, 2173, 4135, + +}; + +static const unsigned short dep71[] = { + 13, 97, 208, 282, +}; + +static const unsigned short dep72[] = { + 11, 40, 41, 97, 158, 162, 175, 185, 208, 282, 2166, 2167, 2170, 2173, 4135, + +}; + +static const unsigned short dep73[] = { + 14, 97, 209, 282, +}; + +static const unsigned short dep74[] = { + 11, 40, 41, 97, 158, 162, 175, 185, 209, 282, 2166, 2167, 2170, 2173, 4135, + +}; + +static const unsigned short dep75[] = { + 15, 97, 211, 212, 282, +}; + +static const unsigned short dep76[] = { + 40, 41, 97, 158, 162, 175, 185, 211, 212, 282, 2166, 2167, 2170, 2173, 4135, + +}; + +static const unsigned short dep77[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2166, 2167, 2170, 2173, 4135, +}; + +static const unsigned short dep78[] = { + 16, 97, 214, 215, 282, +}; + +static const unsigned short dep79[] = { + 40, 41, 97, 158, 162, 175, 185, 214, 215, 282, 2166, 2167, 2170, 2173, 4135, + +}; + +static const unsigned short dep80[] = { + 17, 97, 217, 218, 282, +}; + +static const unsigned short dep81[] = { + 40, 41, 97, 158, 162, 175, 185, 217, 218, 282, 2166, 2167, 2170, 2173, 4135, + +}; + +static const unsigned short dep82[] = { + 18, 97, 220, 221, 282, +}; + +static const unsigned short dep83[] = { + 40, 41, 97, 158, 162, 175, 185, 220, 221, 282, 2166, 2167, 2170, 2173, 4135, + +}; + +static const unsigned short dep84[] = { + 15, 19, 20, 40, 41, 97, 158, 162, 164, 175, 185, 186, 188, 282, 2166, 2167, + 2170, 2173, 4135, +}; + +static const unsigned short dep85[] = { + 15, 16, 19, 20, 40, 41, 97, 158, 162, 164, 175, 185, 186, 188, 282, 2166, + 2167, 2170, 2173, 4135, +}; + +static const unsigned short dep86[] = { + 15, 17, 19, 20, 40, 41, 97, 158, 162, 164, 175, 185, 186, 188, 282, 2166, + 2167, 2170, 2173, 4135, +}; + +static const unsigned short dep87[] = { + 15, 18, 19, 20, 40, 41, 97, 158, 162, 164, 175, 185, 186, 188, 282, 2166, + 2167, 2170, 2173, 4135, +}; + +static const unsigned short dep88[] = { + 15, 97, 210, 211, 282, +}; + +static const unsigned short dep89[] = { + 11, 19, 20, 40, 41, 97, 158, 162, 175, 185, 210, 212, 282, 2166, 2167, 2170, + 2173, 4135, +}; + +static const unsigned short dep90[] = { + 15, 16, 17, 18, 97, 210, 211, 213, 214, 216, 217, 219, 220, 282, +}; + +static const unsigned short dep91[] = { + 11, 12, 13, 14, 19, 20, 40, 41, 97, 158, 162, 175, 185, 210, 212, 213, 215, + 216, 218, 219, 221, 282, 2166, 2167, 2170, 2173, 4135, +}; + +static const unsigned short dep92[] = { + 16, 97, 213, 214, 282, +}; + +static const unsigned short dep93[] = { + 12, 19, 20, 40, 41, 97, 158, 162, 175, 185, 213, 215, 282, 2166, 2167, 2170, + 2173, 4135, +}; + +static const unsigned short dep94[] = { + 17, 97, 216, 217, 282, +}; + +static const unsigned short dep95[] = { + 13, 19, 20, 40, 41, 97, 158, 162, 175, 185, 216, 218, 282, 2166, 2167, 2170, + 2173, 4135, +}; + +static const unsigned short dep96[] = { + 18, 97, 219, 220, 282, +}; + +static const unsigned short dep97[] = { + 14, 19, 20, 40, 41, 97, 158, 162, 175, 185, 219, 221, 282, 2166, 2167, 2170, + 2173, 4135, +}; + +static const unsigned short dep98[] = { + 15, 97, 210, 211, 282, 2166, 2167, 2168, 2170, 2171, 2173, 2174, 2344, 2347, + 2348, 2351, 2352, 2355, 2356, +}; + +static const unsigned short dep99[] = { + 11, 19, 20, 40, 41, 97, 158, 162, 175, 185, 210, 212, 282, 2135, 2136, 2137, + 2166, 2167, 2170, 2173, 2344, 2347, 2348, 2351, 2352, 2355, 2356, 4135, 16528, + 16530, 16531, 16533, +}; + +static const unsigned short dep100[] = { + 15, 16, 17, 18, 97, 210, 211, 213, 214, 216, 217, 219, 220, 282, 2166, 2167, + 2168, 2170, 2171, 2173, 2174, 2344, 2347, 2348, 2351, 2352, 2355, 2356, +}; + +static const unsigned short dep101[] = { + 11, 12, 13, 14, 19, 20, 40, 41, 97, 158, 162, 175, 185, 210, 212, 213, 215, + 216, 218, 219, 221, 282, 2135, 2136, 2137, 2166, 2167, 2170, 2173, 2344, 2347, + 2348, 2351, 2352, 2355, 2356, 4135, 16528, 16530, 16531, 16533, +}; + +static const unsigned short dep102[] = { + 16, 97, 213, 214, 282, 2166, 2167, 2168, 2170, 2171, 2173, 2174, 2344, 2347, + 2348, 2351, 2352, 2355, 2356, +}; + +static const unsigned short dep103[] = { + 12, 19, 20, 40, 41, 97, 158, 162, 175, 185, 213, 215, 282, 2135, 2136, 2137, + 2166, 2167, 2170, 2173, 2344, 2347, 2348, 2351, 2352, 2355, 2356, 4135, 16528, + 16530, 16531, 16533, +}; + +static const unsigned short dep104[] = { + 17, 97, 216, 217, 282, 2166, 2167, 2168, 2170, 2171, 2173, 2174, 2344, 2347, + 2348, 2351, 2352, 2355, 2356, +}; + +static const unsigned short dep105[] = { + 13, 19, 20, 40, 41, 97, 158, 162, 175, 185, 216, 218, 282, 2135, 2136, 2137, + 2166, 2167, 2170, 2173, 2344, 2347, 2348, 2351, 2352, 2355, 2356, 4135, 16528, + 16530, 16531, 16533, +}; + +static const unsigned short dep106[] = { + 18, 97, 219, 220, 282, 2166, 2167, 2168, 2170, 2171, 2173, 2174, 2344, 2347, + 2348, 2351, 2352, 2355, 2356, +}; + +static const unsigned short dep107[] = { + 14, 19, 20, 40, 41, 97, 158, 162, 175, 185, 219, 221, 282, 2135, 2136, 2137, + 2166, 2167, 2170, 2173, 2344, 2347, 2348, 2351, 2352, 2355, 2356, 4135, 16528, + 16530, 16531, 16533, +}; + +static const unsigned short dep108[] = { + 15, 97, 210, 211, 282, 22646, 22647, 22648, 22650, 22651, 22653, 22654, 22824, + 22827, 22828, 22831, 22832, 22835, 22836, +}; + +static const unsigned short dep109[] = { + 11, 19, 20, 40, 41, 97, 158, 162, 175, 185, 210, 212, 282, 2135, 2136, 2137, + 2166, 2167, 2170, 2173, 4135, 16528, 16530, 16531, 16533, 22824, 22827, 22828, + 22831, 22832, 22835, 22836, +}; + +static const unsigned short dep110[] = { + 15, 16, 17, 18, 97, 210, 211, 213, 214, 216, 217, 219, 220, 282, 22646, 22647, + 22648, 22650, 22651, 22653, 22654, 22824, 22827, 22828, 22831, 22832, 22835, + 22836, +}; + +static const unsigned short dep111[] = { + 11, 12, 13, 14, 19, 20, 40, 41, 97, 158, 162, 175, 185, 210, 212, 213, 215, + 216, 218, 219, 221, 282, 2135, 2136, 2137, 2166, 2167, 2170, 2173, 4135, 16528, + 16530, 16531, 16533, 22824, 22827, 22828, 22831, 22832, 22835, 22836, +}; + +static const unsigned short dep112[] = { + 16, 97, 213, 214, 282, 22646, 22647, 22648, 22650, 22651, 22653, 22654, 22824, + 22827, 22828, 22831, 22832, 22835, 22836, +}; + +static const unsigned short dep113[] = { + 12, 19, 20, 40, 41, 97, 158, 162, 175, 185, 213, 215, 282, 2135, 2136, 2137, + 2166, 2167, 2170, 2173, 4135, 16528, 16530, 16531, 16533, 22824, 22827, 22828, + 22831, 22832, 22835, 22836, +}; + +static const unsigned short dep114[] = { + 17, 97, 216, 217, 282, 22646, 22647, 22648, 22650, 22651, 22653, 22654, 22824, + 22827, 22828, 22831, 22832, 22835, 22836, +}; + +static const unsigned short dep115[] = { + 13, 19, 20, 40, 41, 97, 158, 162, 175, 185, 216, 218, 282, 2135, 2136, 2137, + 2166, 2167, 2170, 2173, 4135, 16528, 16530, 16531, 16533, 22824, 22827, 22828, + 22831, 22832, 22835, 22836, +}; + +static const unsigned short dep116[] = { + 18, 97, 219, 220, 282, 22646, 22647, 22648, 22650, 22651, 22653, 22654, 22824, + 22827, 22828, 22831, 22832, 22835, 22836, +}; + +static const unsigned short dep117[] = { + 14, 19, 20, 40, 41, 97, 158, 162, 175, 185, 219, 221, 282, 2135, 2136, 2137, + 2166, 2167, 2170, 2173, 4135, 16528, 16530, 16531, 16533, 22824, 22827, 22828, + 22831, 22832, 22835, 22836, +}; + +static const unsigned short dep118[] = { + 97, 282, 2166, 2167, 2168, 2170, 2171, 2173, 2174, 2344, 2347, 2348, 2351, + 2352, 2355, 2356, +}; + +static const unsigned short dep119[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2135, 2136, 2137, 2166, 2167, 2170, 2173, + 2344, 2347, 2348, 2351, 2352, 2355, 2356, 4135, 16528, 16530, 16531, 16533, + +}; + +static const unsigned short dep120[] = { + 97, 282, 22646, 22647, 22648, 22650, 22651, 22653, 22654, 22824, 22827, 22828, + 22831, 22832, 22835, 22836, +}; + +static const unsigned short dep121[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2135, 2136, 2137, 2166, 2167, 2170, 2173, + 4135, 16528, 16530, 16531, 16533, 22824, 22827, 22828, 22831, 22832, 22835, + 22836, +}; + +static const unsigned short dep122[] = { + 19, 20, 40, 41, 97, 158, 162, 175, 185, 282, 2135, 2136, 2137, 2166, 2167, + 2170, 2173, 2325, 4135, 16528, 16530, 16531, 16533, 18761, 18763, 18764, 18766, + +}; + +static const unsigned short dep123[] = { + 40, 41, 97, 158, 162, 164, 175, 185, 186, 188, 282, 2138, 2139, 2140, 2166, + 2167, 2170, 2173, 4135, 20616, +}; + +static const unsigned short dep124[] = { + 97, 282, 2083, 2084, 2286, 2287, +}; + +static const unsigned short dep125[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, + 2285, 2287, 4135, 20616, +}; + +static const unsigned short dep126[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2082, 2084, 2166, 2167, 2170, 2173, 2327, + 4135, 20616, +}; + +static const unsigned short dep127[] = { + 97, 282, 14455, 14457, 14458, 14460, 14461, 14463, 14635, 14636, 14639, 14640, + 14643, 14644, +}; + +static const unsigned short dep128[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2138, 2139, 2140, 4135, 14635, 14636, + 14639, 14640, 14643, 14644, 20616, 24694, 24695, 24698, 24701, +}; + +static const unsigned short dep129[] = { + 97, 122, 124, 125, 127, 282, 303, 304, 307, 308, +}; + +static const unsigned short dep130[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 303, 304, 307, 308, 4135, 24694, 24695, + 24698, 24701, +}; + +static const unsigned short dep131[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2166, 2167, 2170, 2173, 2327, 4135, 20616, + +}; + +static const unsigned short dep132[] = { + 40, 41, 97, 119, 122, 125, 158, 162, 175, 185, 282, 2327, 4135, 20616, 24694, + +}; + +static const unsigned short dep133[] = { + 6, 24, 26, 27, 97, 201, 227, 230, 282, 2081, 2284, +}; + +static const unsigned short dep134[] = { + 40, 41, 97, 158, 162, 175, 185, 201, 227, 229, 282, 2138, 2139, 2140, 2166, + 2167, 2170, 2173, 2284, 4135, 20616, +}; + +static const unsigned short dep135[] = { + 6, 24, 25, 26, 40, 41, 97, 158, 162, 175, 185, 282, 2081, 2166, 2167, 2170, + 2173, 2327, 4135, 20616, +}; + +static const unsigned short dep136[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2166, 2167, 2170, 2173, 2344, 2347, 2348, + 2351, 2352, 2355, 2356, 4135, +}; + +static const unsigned short dep137[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2166, 2167, 2170, 2173, 4135, 22824, + 22827, 22828, 22831, 22832, 22835, 22836, +}; + +static const unsigned short dep138[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2166, 2167, 2170, 2173, 2344, 2345, 2348, + 2349, 2352, 2353, 2356, 4135, +}; + +static const unsigned short dep139[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2166, 2167, 2170, 2173, 2344, 2346, 2347, + 2350, 2351, 2354, 2355, 4135, +}; + +static const unsigned short dep140[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2166, 2167, 2170, 2173, 2344, 2345, 2346, + 2347, 2348, 2349, 2350, 2351, 2352, 2353, 2354, 2355, 2356, 4135, +}; + +static const unsigned short dep141[] = { + 0, 40, 41, 97, 158, 162, 164, 175, 185, 186, 188, 282, 2166, 2167, 2170, 2173, + 4135, +}; + +static const unsigned short dep142[] = { + 0, 97, 195, 282, +}; + +static const unsigned short dep143[] = { + 0, 40, 41, 97, 158, 162, 164, 175, 185, 186, 188, 195, 282, 2166, 2167, 2170, + 2173, 4135, +}; + +static const unsigned short dep144[] = { + 40, 41, 97, 158, 162, 175, 185, 195, 282, 2166, 2167, 2170, 2173, 4135, +}; + +static const unsigned short dep145[] = { + 2, 28, 97, 197, 231, 282, 28866, 29018, +}; + +static const unsigned short dep146[] = { + 1, 2, 28, 29, 97, 158, 162, 175, 177, 178, 185, 197, 231, 282, 28866, 29018, + +}; + +static const unsigned short dep147[] = { + 1, 28, 29, 38, 40, 41, 97, 158, 162, 175, 177, 178, 185, 197, 231, 282, 4135, + 28866, 29018, +}; + +static const unsigned short dep148[] = { + 0, 40, 41, 97, 158, 162, 175, 185, 195, 282, 2166, 2167, 2170, 2173, 4135, + +}; + +static const unsigned short dep149[] = { + 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, + 28, 29, 30, 31, 97, 196, 197, 198, 199, 200, 202, 203, 204, 205, 206, 207, + 208, 209, 211, 212, 214, 215, 217, 218, 220, 221, 222, 223, 224, 225, 231, + 232, 233, 234, 282, 2071, 2081, 2274, 2284, 28866, 29018, +}; + +static const unsigned short dep150[] = { + 29, 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 196, 197, 198, 199, + 200, 202, 203, 204, 205, 206, 207, 208, 209, 211, 212, 214, 215, 217, 218, + 220, 221, 222, 223, 224, 225, 231, 232, 233, 234, 282, 2138, 2139, 2140, 2166, + 2167, 2170, 2173, 2274, 2284, 4135, 20616, 28866, 29018, +}; + +static const unsigned short dep151[] = { + 97, 282, 14464, 14466, 14468, 14470, 14505, 14506, 14525, 14645, 14646, 14666, + 14667, 14669, 14670, 14679, +}; + +static const unsigned short dep152[] = { + 40, 41, 97, 158, 162, 175, 183, 184, 185, 282, 2166, 2167, 2170, 2173, 4135, + 14645, 14646, 14666, 14667, 14669, 14670, 14679, +}; + +static const unsigned short dep153[] = { + 14464, 14466, 14468, 14470, 14505, 14506, 14525, 14645, 14646, 14666, 14667, + 14669, 14670, 14679, +}; + +static const unsigned short dep154[] = { + 183, 184, 14645, 14646, 14666, 14667, 14669, 14670, 14679, +}; + +static const unsigned short dep155[] = { + 97, 282, 14465, 14466, 14469, 14470, 14480, 14481, 14483, 14484, 14486, 14487, + 14489, 14490, 14493, 14495, 14496, 14505, 14506, 14507, 14508, 14510, 14515, + 14516, 14518, 14519, 14525, 14645, 14646, 14652, 14653, 14654, 14655, 14657, + 14659, 14666, 14667, 14669, 14670, 14671, 14672, 14675, 14676, 14679, +}; + +static const unsigned short dep156[] = { + 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 2166, 2167, 2170, + 2173, 4135, 14645, 14646, 14652, 14653, 14654, 14655, 14657, 14659, 14666, + 14667, 14669, 14670, 14671, 14672, 14675, 14676, 14679, 34888, +}; + +static const unsigned short dep157[] = { + 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 2166, 2167, 2170, + 2173, 4135, 14645, 14646, 14652, 14653, 14654, 14655, 14657, 14659, 14666, + 14667, 14669, 14670, 14671, 14672, 14675, 14676, 14679, +}; + +static const unsigned short dep158[] = { + 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, + 28, 29, 30, 31, 40, 41, 97, 137, 138, 158, 162, 175, 180, 181, 185, 190, 191, + 282, 2071, 2081, 2166, 2167, 2170, 2173, 2327, 4135, 20616, 28866, +}; + +static const unsigned short dep159[] = { + 43, 44, 45, 46, 47, 48, 49, 50, 52, 53, 54, 55, 56, 57, 58, 60, 61, 62, 63, + 64, 65, 67, 69, 70, 71, 72, 73, 94, 96, 97, 243, 244, 245, 246, 247, 248, + 249, 250, 251, 252, 253, 255, 256, 257, 258, 259, 261, 263, 264, 265, 281, + 282, 2116, 2310, +}; + +static const unsigned short dep160[] = { + 40, 41, 96, 97, 137, 138, 158, 160, 161, 162, 175, 185, 190, 191, 243, 244, + 245, 246, 247, 248, 249, 250, 251, 252, 253, 255, 256, 257, 258, 259, 261, + 263, 264, 265, 281, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2310, 4135, + 20616, +}; + +static const unsigned short dep161[] = { + 59, 95, 97, 254, 281, 282, 2140, 2327, +}; + +static const unsigned short dep162[] = { + 40, 41, 43, 44, 46, 48, 49, 51, 52, 53, 54, 56, 57, 60, 61, 63, 64, 65, 66, + 67, 69, 70, 71, 94, 95, 97, 137, 138, 158, 160, 161, 162, 175, 185, 190, 191, + 254, 281, 282, 2107, 2116, 2166, 2167, 2170, 2173, 2327, 4135, 20616, +}; + +static const unsigned short dep163[] = { + 2, 28, 41, 97, 197, 231, 241, 282, 2140, 2327, 28866, 29018, +}; + +static const unsigned short dep164[] = { + 2, 25, 26, 28, 29, 38, 40, 41, 97, 158, 162, 175, 177, 178, 185, 197, 231, + 241, 282, 2327, 4135, 20616, 28866, 29018, +}; + +static const unsigned short dep165[] = { + 97, 129, 130, 133, 134, 140, 141, 144, 145, 147, 148, 150, 151, 153, 154, + 157, 159, 160, 165, 166, 169, 170, 171, 172, 174, 176, 177, 179, 180, 182, + 183, 186, 187, 189, 282, 309, 310, 314, 316, 317, 318, 319, 321, 323, 327, + 330, 331, 333, 334, 335, 336, 338, 339, 340, 342, 343, +}; + +static const unsigned short dep166[] = { + 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 309, 310, 314, 316, + 317, 318, 319, 321, 323, 327, 330, 331, 333, 334, 335, 336, 338, 339, 340, + 342, 343, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 4135, 20616, 34888, +}; + +static const unsigned short dep167[] = { + 97, 128, 130, 132, 134, 169, 170, 189, 282, 309, 310, 330, 331, 333, 334, + 343, +}; + +static const unsigned short dep168[] = { + 40, 41, 97, 158, 162, 175, 183, 184, 185, 282, 309, 310, 330, 331, 333, 334, + 343, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 4135, 20616, +}; + +static const unsigned short dep169[] = { + 40, 41, 97, 130, 131, 134, 135, 137, 138, 141, 142, 145, 146, 148, 149, 151, + 152, 154, 155, 157, 158, 159, 161, 162, 164, 165, 167, 168, 169, 170, 172, + 173, 174, 175, 176, 178, 179, 181, 182, 184, 185, 187, 188, 189, 190, 191, + 282, 2166, 2167, 2170, 2173, 2327, 4135, 20616, +}; + +static const unsigned short dep170[] = { + 40, 41, 97, 130, 131, 134, 135, 158, 162, 169, 170, 175, 185, 189, 282, 2166, + 2167, 2170, 2173, 2327, 4135, 20616, +}; + +static const unsigned short dep171[] = { + 40, 41, 70, 76, 77, 82, 84, 97, 111, 137, 138, 153, 155, 158, 162, 171, 173, + 175, 185, 192, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2327, 4135, + 20616, +}; + +static const unsigned short dep172[] = { + 40, 41, 70, 76, 77, 82, 84, 97, 111, 137, 138, 139, 140, 142, 143, 153, 155, + 158, 162, 171, 173, 175, 185, 192, 282, 2138, 2139, 2140, 2166, 2167, 2170, + 2173, 4135, 20616, +}; + +static const unsigned short dep173[] = { + 77, 78, 97, 101, 102, 269, 270, 282, 284, 285, +}; + +static const unsigned short dep174[] = { + 40, 41, 47, 62, 78, 80, 86, 97, 99, 102, 137, 138, 158, 160, 161, 162, 175, + 185, 190, 191, 192, 269, 270, 282, 284, 285, 2138, 2139, 2140, 2166, 2167, + 2170, 2173, 4135, 20616, +}; + +static const unsigned short dep175[] = { + 40, 41, 47, 62, 78, 80, 97, 99, 102, 104, 106, 137, 138, 158, 160, 161, 162, + 175, 185, 190, 191, 192, 269, 270, 282, 284, 285, 2138, 2139, 2140, 2166, + 2167, 2170, 2173, 4135, 20616, +}; + +static const unsigned short dep176[] = { + 97, 282, 12480, 12481, 12633, +}; + +static const unsigned short dep177[] = { + 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 2138, 2139, 2140, + 2166, 2167, 2170, 2173, 4135, 12633, 20616, +}; + +static const unsigned short dep178[] = { + 97, 282, 6219, 6220, 6411, +}; + +static const unsigned short dep179[] = { + 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 2138, 2139, 2140, + 2166, 2167, 2170, 2173, 4135, 6411, 20616, +}; + +static const unsigned short dep180[] = { + 97, 282, 6237, 6424, +}; + +static const unsigned short dep181[] = { + 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 2138, 2139, 2140, + 2166, 2167, 2170, 2173, 4135, 6424, 20616, +}; + +static const unsigned short dep182[] = { + 97, 282, 6255, 6256, 6257, 6258, 6435, 6437, 8484, +}; + +static const unsigned short dep183[] = { + 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 2138, 2139, 2140, + 2166, 2167, 2170, 2173, 4135, 6258, 6436, 6437, 8304, 8483, 20616, +}; + +static const unsigned short dep184[] = { + 97, 282, 6259, 6260, 6438, +}; + +static const unsigned short dep185[] = { + 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 2138, 2139, 2140, + 2166, 2167, 2170, 2173, 4135, 6438, 20616, +}; + +static const unsigned short dep186[] = { + 97, 282, 6261, 6439, +}; + +static const unsigned short dep187[] = { + 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 2138, 2139, 2140, + 2166, 2167, 2170, 2173, 4135, 6439, 20616, +}; + +static const unsigned short dep188[] = { + 97, 282, 10350, 10530, +}; + +static const unsigned short dep189[] = { + 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 2138, 2139, 2140, + 2166, 2167, 2170, 2173, 4135, 10530, 20616, +}; + +static const unsigned short dep190[] = { + 77, 78, 82, 83, 97, 101, 102, 269, 270, 272, 273, 282, 284, 285, +}; + +static const unsigned short dep191[] = { + 40, 41, 47, 62, 78, 80, 83, 86, 97, 99, 102, 137, 138, 158, 160, 161, 162, + 175, 185, 190, 191, 192, 269, 270, 272, 274, 282, 284, 285, 2138, 2139, 2140, + 2166, 2167, 2170, 2173, 4135, 20616, +}; + +static const unsigned short dep192[] = { + 77, 78, 97, 101, 102, 104, 105, 269, 270, 282, 284, 285, 286, 287, +}; + +static const unsigned short dep193[] = { + 40, 41, 47, 62, 78, 80, 97, 99, 102, 104, 106, 137, 138, 158, 160, 161, 162, + 175, 185, 190, 191, 192, 269, 270, 282, 284, 285, 286, 287, 2138, 2139, 2140, + 2166, 2167, 2170, 2173, 4135, 20616, +}; + +static const unsigned short dep194[] = { + 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 2138, 2139, 2140, + 2166, 2167, 2170, 2173, 2327, 4135, 12481, 20616, +}; + +static const unsigned short dep195[] = { + 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 2138, 2139, 2140, + 2166, 2167, 2170, 2173, 2327, 4135, 6219, 20616, +}; + +static const unsigned short dep196[] = { + 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 2138, 2139, 2140, + 2166, 2167, 2170, 2173, 2327, 4135, 6237, 20616, +}; + +static const unsigned short dep197[] = { + 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 2138, 2139, 2140, + 2166, 2167, 2170, 2173, 2327, 4135, 6257, 8303, 20616, +}; + +static const unsigned short dep198[] = { + 40, 41, 97, 137, 138, 158, 162, 175, 185, 190, 191, 282, 2138, 2139, 2140, + 2166, 2167, 2170, 2173, 2327, 4135, 6259, 20616, +}; + +static const unsigned short dep199[] = { + 40, 41, 97, 137, 138, 158, 162, 175, 183, 184, 185, 282, 2138, 2139, 2140, + 2166, 2167, 2170, 2173, 2327, 4135, 6260, 6261, 20616, +}; + +static const unsigned short dep200[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, + 2327, 4135, 10350, 20616, +}; + +static const unsigned short dep201[] = { + 40, 41, 97, 158, 162, 175, 185, 190, 191, 282, 2138, 2139, 2140, 2166, 2167, + 2170, 2173, 2327, 4135, 6186, 20616, +}; + +static const unsigned short dep202[] = { + 77, 79, 80, 97, 98, 99, 100, 268, 269, 282, 283, 284, +}; + +static const unsigned short dep203[] = { + 40, 41, 78, 79, 83, 85, 97, 100, 102, 104, 107, 137, 138, 158, 162, 175, 185, + 190, 191, 192, 268, 270, 282, 283, 285, 2138, 2139, 2140, 2166, 2167, 2170, + 2173, 4135, 20616, +}; + +static const unsigned short dep204[] = { + 77, 79, 80, 81, 97, 98, 99, 100, 103, 268, 269, 271, 282, 283, 284, +}; + +static const unsigned short dep205[] = { + 40, 41, 78, 79, 81, 83, 85, 97, 100, 102, 103, 104, 107, 137, 138, 158, 162, + 175, 185, 190, 191, 192, 268, 270, 271, 282, 283, 285, 2138, 2139, 2140, 2166, + 2167, 2170, 2173, 4135, 20616, +}; + +static const unsigned short dep206[] = { + 77, 79, 80, 84, 85, 86, 97, 98, 99, 100, 268, 269, 274, 275, 282, 283, 284, + +}; + +static const unsigned short dep207[] = { + 40, 41, 78, 79, 83, 85, 97, 100, 102, 137, 138, 158, 162, 175, 185, 190, 191, + 192, 268, 270, 273, 275, 282, 283, 285, 2138, 2139, 2140, 2166, 2167, 2170, + 2173, 4135, 20616, +}; + +static const unsigned short dep208[] = { + 77, 79, 80, 97, 98, 99, 100, 106, 107, 108, 268, 269, 282, 283, 284, 287, + 288, +}; + +static const unsigned short dep209[] = { + 40, 41, 78, 79, 97, 100, 102, 104, 107, 137, 138, 158, 162, 175, 185, 190, + 191, 192, 268, 270, 282, 283, 285, 286, 288, 2138, 2139, 2140, 2166, 2167, + 2170, 2173, 4135, 20616, +}; + +static const unsigned short dep210[] = { + 40, 41, 46, 70, 97, 158, 162, 175, 185, 190, 191, 192, 282, 2138, 2139, 2140, + 2166, 2167, 2170, 2173, 2327, 4135, 20616, +}; + +static const unsigned short dep211[] = { + 40, 41, 97, 158, 162, 175, 185, 190, 191, 192, 282, 2138, 2139, 2140, 2166, + 2167, 2170, 2173, 2327, 4135, 20616, +}; + +static const unsigned short dep212[] = { + 40, 41, 70, 77, 82, 84, 97, 137, 138, 153, 155, 158, 162, 175, 185, 190, 191, + 192, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2327, 4135, 20616, +}; + +static const unsigned short dep213[] = { + 40, 41, 97, 158, 162, 164, 175, 185, 186, 188, 282, 2135, 2136, 2137, 2138, + 2139, 2140, 2166, 2167, 2170, 2173, 4135, 16528, 16530, 16531, 16533, 20616, + +}; + +static const unsigned short dep214[] = { + 40, 41, 70, 77, 82, 84, 97, 153, 155, 158, 162, 175, 185, 192, 282, 2138, + 2139, 2140, 2166, 2167, 2170, 2173, 4135, 20616, +}; + +static const unsigned short dep215[] = { + 40, 41, 78, 79, 97, 100, 137, 138, 158, 162, 175, 185, 190, 191, 268, 270, + 282, 283, 285, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 4135, 20616, +}; + +static const unsigned short dep216[] = { + 40, 41, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, 135, 137, + 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, 191, + 192, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2327, 4135, 20616, +}; + +static const unsigned short dep217[] = { + 5, 97, 200, 282, 2140, 2327, +}; + +static const unsigned short dep218[] = { + 40, 41, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, 135, 137, + 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, 191, + 192, 200, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2327, 4135, 20616, + +}; + +static const unsigned short dep219[] = { + 40, 41, 44, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, 135, + 137, 138, 139, 140, 142, 143, 153, 155, 156, 158, 162, 171, 173, 175, 185, + 190, 191, 192, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2327, 4135, + 20616, +}; + +static const unsigned short dep220[] = { + 0, 97, 195, 282, 2140, 2327, +}; + +static const unsigned short dep221[] = { + 0, 40, 41, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, 135, + 137, 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, + 191, 192, 195, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2327, 4135, + 20616, +}; + +static const unsigned short dep222[] = { + 0, 40, 41, 44, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, + 135, 137, 138, 139, 140, 142, 143, 153, 155, 156, 158, 162, 171, 173, 175, + 185, 190, 191, 192, 195, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2327, + 4135, 20616, +}; + +static const unsigned short dep223[] = { + 31, 40, 41, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, 135, + 137, 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, + 191, 192, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2327, 4135, 20616, + +}; + +static const unsigned short dep224[] = { + 0, 97, 195, 282, 2327, 26715, +}; + +static const unsigned short dep225[] = { + 0, 97, 109, 195, 282, 289, +}; + +static const unsigned short dep226[] = { + 0, 40, 41, 70, 76, 77, 82, 84, 97, 111, 128, 129, 131, 132, 133, 135, 137, + 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, 191, + 192, 195, 282, 289, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 4135, 20616, + +}; + +static const unsigned short dep227[] = { + 0, 5, 40, 41, 70, 76, 77, 82, 84, 97, 111, 128, 129, 131, 132, 133, 135, 137, + 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, 191, + 192, 195, 282, 289, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 4135, 20616, + +}; + +static const unsigned short dep228[] = { + 0, 31, 97, 109, 195, 234, 282, 289, +}; + +static const unsigned short dep229[] = { + 0, 40, 41, 70, 76, 77, 82, 84, 97, 111, 128, 129, 131, 132, 133, 135, 137, + 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, 191, + 192, 195, 234, 282, 289, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 4135, 20616, + +}; + +static const unsigned short dep230[] = { + 0, 97, 109, 195, 282, 289, 2140, 2327, +}; + +static const unsigned short dep231[] = { + 0, 3, 40, 41, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, 135, + 137, 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, + 191, 192, 195, 282, 289, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2327, 4135, + 20616, +}; + +static const unsigned short dep232[] = { + 0, 3, 5, 40, 41, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, + 135, 137, 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, + 190, 191, 192, 195, 282, 289, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2327, + 4135, 20616, +}; + +static const unsigned short dep233[] = { + 0, 40, 41, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, 135, + 137, 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, + 191, 192, 195, 282, 289, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2327, 4135, + 20616, +}; + +static const unsigned short dep234[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2135, 2136, 2137, 2166, 2167, 2170, 2173, + 2327, 4135, 16528, 16530, 16531, 16533, 20616, +}; + +static const unsigned short dep235[] = { + 0, 40, 41, 70, 76, 77, 82, 84, 97, 111, 128, 129, 131, 132, 133, 135, 137, + 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, 191, + 192, 195, 282, 289, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2327, 4135, + 20616, +}; + +static const unsigned short dep236[] = { + 0, 31, 97, 109, 195, 234, 282, 289, 2140, 2327, +}; + +static const unsigned short dep237[] = { + 0, 40, 41, 70, 76, 77, 82, 84, 97, 111, 128, 129, 131, 132, 133, 135, 137, + 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, 191, + 192, 195, 234, 282, 289, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2327, 4135, + 20616, +}; + +static const unsigned short dep238[] = { + 40, 41, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, 135, 137, + 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, 191, + 192, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2325, 4135, 16528, 16530, + 16531, 16533, 18761, 18763, 18764, 18766, 20616, +}; + +static const unsigned short dep239[] = { + 40, 41, 44, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, 135, + 137, 138, 139, 140, 142, 143, 153, 155, 156, 158, 162, 171, 173, 175, 185, + 190, 191, 192, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2325, 4135, + 16528, 16530, 16531, 16533, 18761, 18763, 18764, 18766, 20616, +}; + +static const unsigned short dep240[] = { + 0, 97, 195, 282, 2136, 2325, 18601, 18602, 18761, 18762, 18764, 18765, +}; + +static const unsigned short dep241[] = { + 0, 40, 41, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, 135, + 137, 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, + 191, 192, 195, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2325, 4135, + 16528, 16530, 16531, 16533, 18761, 18763, 18764, 18766, 20616, +}; + +static const unsigned short dep242[] = { + 0, 40, 41, 44, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, + 135, 137, 138, 139, 140, 142, 143, 153, 155, 156, 158, 162, 171, 173, 175, + 185, 190, 191, 192, 195, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2325, + 4135, 16528, 16530, 16531, 16533, 18761, 18763, 18764, 18766, 20616, +}; + +static const unsigned short dep243[] = { + 0, 97, 195, 282, 2137, 2325, 18601, 18602, 18761, 18762, 18764, 18765, +}; + +static const unsigned short dep244[] = { + 97, 282, 2136, 2140, 2325, 2327, 18601, 18602, 18761, 18762, 18764, 18765, + +}; + +static const unsigned short dep245[] = { + 40, 41, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, 135, 137, + 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, 191, + 192, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2325, 2327, 4135, 16528, + 16530, 16531, 16533, 18761, 18763, 18764, 18766, 20616, +}; + +static const unsigned short dep246[] = { + 40, 41, 44, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, 135, + 137, 138, 139, 140, 142, 143, 153, 155, 156, 158, 162, 171, 173, 175, 185, + 190, 191, 192, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2325, 2327, + 4135, 16528, 16530, 16531, 16533, 18761, 18763, 18764, 18766, 20616, +}; + +static const unsigned short dep247[] = { + 0, 97, 195, 282, 2136, 2140, 2325, 2327, 18601, 18602, 18761, 18762, 18764, + 18765, +}; + +static const unsigned short dep248[] = { + 0, 40, 41, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, 135, + 137, 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, + 191, 192, 195, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2325, 2327, + 4135, 16528, 16530, 16531, 16533, 18761, 18763, 18764, 18766, 20616, +}; + +static const unsigned short dep249[] = { + 0, 40, 41, 44, 70, 76, 77, 82, 84, 97, 109, 111, 128, 129, 131, 132, 133, + 135, 137, 138, 139, 140, 142, 143, 153, 155, 156, 158, 162, 171, 173, 175, + 185, 190, 191, 192, 195, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, 2325, + 2327, 4135, 16528, 16530, 16531, 16533, 18761, 18763, 18764, 18766, 20616, + +}; + +static const unsigned short dep250[] = { + 0, 97, 195, 282, 2137, 2140, 2325, 2327, 18601, 18602, 18761, 18762, 18764, + 18765, +}; + +static const unsigned short dep251[] = { + 0, 40, 41, 70, 76, 77, 82, 84, 97, 111, 128, 129, 131, 132, 133, 135, 137, + 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, 191, + 192, 195, 282, 289, 2135, 2136, 2137, 2138, 2139, 2140, 2166, 2167, 2170, + 2173, 4135, 16528, 16530, 16531, 16533, 20616, +}; + +static const unsigned short dep252[] = { + 40, 41, 70, 76, 77, 82, 84, 97, 137, 138, 139, 140, 142, 143, 153, 155, 156, + 158, 162, 171, 173, 175, 185, 192, 282, 2166, 2167, 2170, 2173, 4135, +}; + +static const unsigned short dep253[] = { + 40, 41, 70, 76, 77, 82, 84, 97, 137, 138, 139, 140, 142, 143, 153, 155, 156, + 158, 162, 171, 173, 175, 185, 192, 282, 2138, 2139, 2140, 2166, 2167, 2170, + 2173, 2327, 4135, 20616, +}; + +static const unsigned short dep254[] = { + 40, 41, 97, 158, 162, 175, 185, 282, 2138, 2139, 2140, 2166, 2167, 2170, 2173, + 2325, 4135, 16528, 16530, 16531, 16533, 18761, 18763, 18764, 18766, 20616, + +}; + +static const unsigned short dep255[] = { + 0, 40, 41, 70, 76, 77, 82, 84, 97, 111, 128, 129, 131, 132, 133, 135, 137, + 138, 139, 140, 142, 143, 153, 155, 158, 162, 171, 173, 175, 185, 190, 191, + 192, 195, 282, 289, 2135, 2136, 2137, 2138, 2139, 2140, 2166, 2167, 2170, + 2173, 2327, 4135, 16528, 16530, 16531, 16533, 20616, +}; + +static const unsigned short dep256[] = { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 24, 26, 27, 28, 29, 30, 31, 97, 196, 197, 198, 199, 200, 201, 202, 203, + 204, 205, 206, 207, 208, 209, 211, 212, 214, 215, 217, 218, 220, 221, 222, + 223, 224, 225, 227, 230, 231, 232, 233, 234, 282, 2071, 2081, 2140, 2274, + 2284, 2327, 28866, 29018, +}; + +static const unsigned short dep257[] = { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 24, 25, 26, 28, 29, 30, 31, 40, 41, 97, 137, 138, 158, 162, 175, 180, + 181, 185, 190, 191, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, + 207, 208, 209, 211, 212, 214, 215, 217, 218, 220, 221, 222, 223, 224, 225, + 227, 229, 231, 232, 233, 234, 282, 2071, 2081, 2138, 2139, 2140, 2166, 2167, + 2170, 2173, 2274, 2284, 2327, 4135, 20616, 28866, 29018, +}; + +#define NELS(X) (sizeof(X)/sizeof(X[0])) +static const struct ia64_opcode_dependency +op_dependencies[] = { + { NELS(dep1), dep1, NELS(dep0), dep0, }, + { NELS(dep3), dep3, NELS(dep2), dep2, }, + { NELS(dep5), dep5, NELS(dep4), dep4, }, + { NELS(dep7), dep7, NELS(dep6), dep6, }, + { NELS(dep9), dep9, NELS(dep8), dep8, }, + { NELS(dep11), dep11, NELS(dep10), dep10, }, + { NELS(dep13), dep13, NELS(dep12), dep12, }, + { NELS(dep15), dep15, NELS(dep14), dep14, }, + { NELS(dep17), dep17, NELS(dep16), dep16, }, + { NELS(dep19), dep19, NELS(dep18), dep18, }, + { NELS(dep21), dep21, NELS(dep20), dep20, }, + { NELS(dep23), dep23, NELS(dep22), dep22, }, + { NELS(dep25), dep25, NELS(dep24), dep24, }, + { NELS(dep27), dep27, NELS(dep26), dep26, }, + { NELS(dep29), dep29, NELS(dep28), dep28, }, + { NELS(dep30), dep30, NELS(dep12), dep12, }, + { NELS(dep32), dep32, NELS(dep31), dep31, }, + { NELS(dep34), dep34, NELS(dep33), dep33, }, + { NELS(dep35), dep35, NELS(dep12), dep12, }, + { NELS(dep37), dep37, NELS(dep36), dep36, }, + { NELS(dep39), dep39, NELS(dep38), dep38, }, + { NELS(dep41), dep41, NELS(dep40), dep40, }, + { NELS(dep42), dep42, NELS(dep31), dep31, }, + { NELS(dep43), dep43, NELS(dep33), dep33, }, + { NELS(dep45), dep45, NELS(dep44), dep44, }, + { NELS(dep47), dep47, NELS(dep46), dep46, }, + { NELS(dep49), dep49, NELS(dep48), dep48, }, + { NELS(dep51), dep51, NELS(dep50), dep50, }, + { NELS(dep53), dep53, NELS(dep52), dep52, }, + { NELS(dep55), dep55, NELS(dep54), dep54, }, + { NELS(dep57), dep57, NELS(dep56), dep56, }, + { NELS(dep59), dep59, NELS(dep58), dep58, }, + { NELS(dep61), dep61, NELS(dep60), dep60, }, + { NELS(dep63), dep63, NELS(dep62), dep62, }, + { NELS(dep65), dep65, NELS(dep64), dep64, }, + { NELS(dep67), dep67, NELS(dep66), dep66, }, + { NELS(dep68), dep68, NELS(dep33), dep33, }, + { NELS(dep70), dep70, NELS(dep69), dep69, }, + { NELS(dep72), dep72, NELS(dep71), dep71, }, + { NELS(dep74), dep74, NELS(dep73), dep73, }, + { NELS(dep76), dep76, NELS(dep75), dep75, }, + { NELS(dep77), dep77, NELS(dep33), dep33, }, + { NELS(dep79), dep79, NELS(dep78), dep78, }, + { NELS(dep81), dep81, NELS(dep80), dep80, }, + { NELS(dep83), dep83, NELS(dep82), dep82, }, + { NELS(dep84), dep84, NELS(dep33), dep33, }, + { NELS(dep85), dep85, NELS(dep33), dep33, }, + { NELS(dep86), dep86, NELS(dep33), dep33, }, + { NELS(dep87), dep87, NELS(dep33), dep33, }, + { NELS(dep89), dep89, NELS(dep88), dep88, }, + { NELS(dep91), dep91, NELS(dep90), dep90, }, + { NELS(dep93), dep93, NELS(dep92), dep92, }, + { NELS(dep95), dep95, NELS(dep94), dep94, }, + { NELS(dep97), dep97, NELS(dep96), dep96, }, + { NELS(dep99), dep99, NELS(dep98), dep98, }, + { NELS(dep101), dep101, NELS(dep100), dep100, }, + { NELS(dep103), dep103, NELS(dep102), dep102, }, + { NELS(dep105), dep105, NELS(dep104), dep104, }, + { NELS(dep107), dep107, NELS(dep106), dep106, }, + { NELS(dep109), dep109, NELS(dep108), dep108, }, + { NELS(dep111), dep111, NELS(dep110), dep110, }, + { NELS(dep113), dep113, NELS(dep112), dep112, }, + { NELS(dep115), dep115, NELS(dep114), dep114, }, + { NELS(dep117), dep117, NELS(dep116), dep116, }, + { NELS(dep119), dep119, NELS(dep118), dep118, }, + { NELS(dep121), dep121, NELS(dep120), dep120, }, + { NELS(dep122), dep122, NELS(dep64), dep64, }, + { NELS(dep123), dep123, NELS(dep33), dep33, }, + { NELS(dep125), dep125, NELS(dep124), dep124, }, + { NELS(dep126), dep126, NELS(dep0), dep0, }, + { NELS(dep128), dep128, NELS(dep127), dep127, }, + { NELS(dep130), dep130, NELS(dep129), dep129, }, + { NELS(dep131), dep131, NELS(dep0), dep0, }, + { NELS(dep132), dep132, NELS(dep0), dep0, }, + { NELS(dep134), dep134, NELS(dep133), dep133, }, + { NELS(dep135), dep135, NELS(dep0), dep0, }, + { NELS(dep136), dep136, NELS(dep2), dep2, }, + { NELS(dep137), dep137, NELS(dep4), dep4, }, + { NELS(dep138), dep138, NELS(dep6), dep6, }, + { NELS(dep139), dep139, NELS(dep8), dep8, }, + { NELS(dep140), dep140, NELS(dep10), dep10, }, + { NELS(dep141), dep141, NELS(dep33), dep33, }, + { NELS(dep143), dep143, NELS(dep142), dep142, }, + { NELS(dep144), dep144, NELS(dep142), dep142, }, + { NELS(dep146), dep146, NELS(dep145), dep145, }, + { NELS(dep147), dep147, NELS(dep145), dep145, }, + { NELS(dep148), dep148, NELS(dep142), dep142, }, + { NELS(dep150), dep150, NELS(dep149), dep149, }, + { NELS(dep152), dep152, NELS(dep151), dep151, }, + { NELS(dep154), dep154, NELS(dep153), dep153, }, + { NELS(dep156), dep156, NELS(dep155), dep155, }, + { NELS(dep157), dep157, NELS(dep155), dep155, }, + { NELS(dep158), dep158, NELS(dep0), dep0, }, + { NELS(dep160), dep160, NELS(dep159), dep159, }, + { NELS(dep162), dep162, NELS(dep161), dep161, }, + { NELS(dep164), dep164, NELS(dep163), dep163, }, + { NELS(dep166), dep166, NELS(dep165), dep165, }, + { NELS(dep168), dep168, NELS(dep167), dep167, }, + { NELS(dep169), dep169, NELS(dep0), dep0, }, + { NELS(dep170), dep170, NELS(dep0), dep0, }, + { NELS(dep171), dep171, NELS(dep0), dep0, }, + { NELS(dep172), dep172, NELS(dep33), dep33, }, + { NELS(dep174), dep174, NELS(dep173), dep173, }, + { NELS(dep175), dep175, NELS(dep173), dep173, }, + { NELS(dep177), dep177, NELS(dep176), dep176, }, + { NELS(dep179), dep179, NELS(dep178), dep178, }, + { NELS(dep181), dep181, NELS(dep180), dep180, }, + { NELS(dep183), dep183, NELS(dep182), dep182, }, + { NELS(dep185), dep185, NELS(dep184), dep184, }, + { NELS(dep187), dep187, NELS(dep186), dep186, }, + { NELS(dep189), dep189, NELS(dep188), dep188, }, + { NELS(dep191), dep191, NELS(dep190), dep190, }, + { NELS(dep193), dep193, NELS(dep192), dep192, }, + { NELS(dep194), dep194, NELS(dep0), dep0, }, + { NELS(dep195), dep195, NELS(dep0), dep0, }, + { NELS(dep196), dep196, NELS(dep0), dep0, }, + { NELS(dep197), dep197, NELS(dep0), dep0, }, + { NELS(dep198), dep198, NELS(dep0), dep0, }, + { NELS(dep199), dep199, NELS(dep0), dep0, }, + { NELS(dep200), dep200, NELS(dep0), dep0, }, + { NELS(dep201), dep201, NELS(dep0), dep0, }, + { NELS(dep203), dep203, NELS(dep202), dep202, }, + { NELS(dep205), dep205, NELS(dep204), dep204, }, + { NELS(dep207), dep207, NELS(dep206), dep206, }, + { NELS(dep209), dep209, NELS(dep208), dep208, }, + { NELS(dep210), dep210, NELS(dep0), dep0, }, + { NELS(dep211), dep211, NELS(dep0), dep0, }, + { NELS(dep212), dep212, NELS(dep0), dep0, }, + { NELS(dep213), dep213, NELS(dep33), dep33, }, + { NELS(dep214), dep214, NELS(dep33), dep33, }, + { NELS(dep215), dep215, NELS(dep202), dep202, }, + { NELS(dep216), dep216, NELS(dep0), dep0, }, + { NELS(dep218), dep218, NELS(dep217), dep217, }, + { NELS(dep219), dep219, NELS(dep0), dep0, }, + { NELS(dep221), dep221, NELS(dep220), dep220, }, + { NELS(dep222), dep222, NELS(dep220), dep220, }, + { NELS(dep223), dep223, NELS(dep0), dep0, }, + { NELS(dep221), dep221, NELS(dep224), dep224, }, + { NELS(dep226), dep226, NELS(dep225), dep225, }, + { NELS(dep227), dep227, NELS(dep225), dep225, }, + { NELS(dep229), dep229, NELS(dep228), dep228, }, + { NELS(dep231), dep231, NELS(dep230), dep230, }, + { NELS(dep232), dep232, NELS(dep230), dep230, }, + { NELS(dep233), dep233, NELS(dep230), dep230, }, + { NELS(dep234), dep234, NELS(dep0), dep0, }, + { NELS(dep235), dep235, NELS(dep230), dep230, }, + { NELS(dep237), dep237, NELS(dep236), dep236, }, + { NELS(dep238), dep238, NELS(dep64), dep64, }, + { NELS(dep239), dep239, NELS(dep64), dep64, }, + { NELS(dep241), dep241, NELS(dep240), dep240, }, + { NELS(dep242), dep242, NELS(dep240), dep240, }, + { NELS(dep241), dep241, NELS(dep243), dep243, }, + { NELS(dep245), dep245, NELS(dep244), dep244, }, + { NELS(dep246), dep246, NELS(dep244), dep244, }, + { NELS(dep248), dep248, NELS(dep247), dep247, }, + { NELS(dep249), dep249, NELS(dep247), dep247, }, + { NELS(dep248), dep248, NELS(dep250), dep250, }, + { NELS(dep251), dep251, NELS(dep225), dep225, }, + { NELS(dep252), dep252, NELS(dep33), dep33, }, + { NELS(dep253), dep253, NELS(dep0), dep0, }, + { NELS(dep254), dep254, NELS(dep64), dep64, }, + { NELS(dep255), dep255, NELS(dep230), dep230, }, + { 0, NULL, 0, NULL, }, + { NELS(dep257), dep257, NELS(dep256), dep256, }, +}; + +static const struct ia64_completer_table +completer_table[] = { + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 95 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 95 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, 594, -1, 0, 1, 6 }, + { 0x0, 0x0, 0, 657, -1, 0, 1, 18 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 162 }, + { 0x0, 0x0, 0, 756, -1, 0, 1, 18 }, + { 0x0, 0x0, 0, 2198, -1, 0, 1, 10 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 9 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 13 }, + { 0x1, 0x1, 0, -1, -1, 13, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 34 }, + { 0x0, 0x0, 0, 2406, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 34 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 34 }, + { 0x0, 0x0, 0, 1140, -1, 0, 1, 129 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 45 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 41 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 84 }, + { 0x0, 0x0, 0, 2246, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, 2473, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, 2250, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 34 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 34 }, + { 0x0, 0x0, 0, 2252, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, 2482, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, 2485, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 34 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 34 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 34 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, 2507, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 34 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 34 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, 2510, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 25 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 25 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 25 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 25 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 34 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 36 }, + { 0x0, 0x0, 0, 2518, -1, 0, 1, 30 }, + { 0x0, 0x0, 0, 1409, -1, 0, 1, 34 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 41 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 34 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 162 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 83 }, + { 0x0, 0x0, 0, 1457, -1, 0, 1, 131 }, + { 0x0, 0x0, 0, 1466, -1, 0, 1, 131 }, + { 0x0, 0x0, 0, 1475, -1, 0, 1, 131 }, + { 0x0, 0x0, 0, 1477, -1, 0, 1, 132 }, + { 0x0, 0x0, 0, 1479, -1, 0, 1, 132 }, + { 0x0, 0x0, 0, 1488, -1, 0, 1, 131 }, + { 0x0, 0x0, 0, 1497, -1, 0, 1, 131 }, + { 0x0, 0x0, 0, 1506, -1, 0, 1, 131 }, + { 0x0, 0x0, 0, 1515, -1, 0, 1, 131 }, + { 0x0, 0x0, 0, 1524, -1, 0, 1, 131 }, + { 0x0, 0x0, 0, 1533, -1, 0, 1, 131 }, + { 0x0, 0x0, 0, 1543, -1, 0, 1, 131 }, + { 0x0, 0x0, 0, 1553, -1, 0, 1, 131 }, + { 0x0, 0x0, 0, 1563, -1, 0, 1, 131 }, + { 0x0, 0x0, 0, 1572, -1, 0, 1, 147 }, + { 0x0, 0x0, 0, 1578, -1, 0, 1, 152 }, + { 0x0, 0x0, 0, 1584, -1, 0, 1, 152 }, + { 0x0, 0x0, 0, 1590, -1, 0, 1, 147 }, + { 0x0, 0x0, 0, 1596, -1, 0, 1, 152 }, + { 0x0, 0x0, 0, 1602, -1, 0, 1, 152 }, + { 0x0, 0x0, 0, 1608, -1, 0, 1, 147 }, + { 0x0, 0x0, 0, 1614, -1, 0, 1, 152 }, + { 0x0, 0x0, 0, 1620, -1, 0, 1, 152 }, + { 0x0, 0x0, 0, 1626, -1, 0, 1, 147 }, + { 0x0, 0x0, 0, 1632, -1, 0, 1, 152 }, + { 0x0, 0x0, 0, 1638, -1, 0, 1, 147 }, + { 0x0, 0x0, 0, 1644, -1, 0, 1, 152 }, + { 0x0, 0x0, 0, 1650, -1, 0, 1, 147 }, + { 0x0, 0x0, 0, 1656, -1, 0, 1, 152 }, + { 0x0, 0x0, 0, 1662, -1, 0, 1, 147 }, + { 0x0, 0x0, 0, 1668, -1, 0, 1, 152 }, + { 0x0, 0x0, 0, 1674, -1, 0, 1, 152 }, + { 0x0, 0x0, 0, 1678, -1, 0, 1, 158 }, + { 0x0, 0x0, 0, 1682, -1, 0, 1, 159 }, + { 0x0, 0x0, 0, 1686, -1, 0, 1, 159 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 85 }, + { 0x0, 0x0, 0, 258, -1, 0, 1, 41 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 34 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 68 }, + { 0x1, 0x1, 0, 1166, -1, 20, 1, 68 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 69 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 70 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 70 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 71 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 72 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 73 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 93 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 94 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 96 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 97 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 98 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 99 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 104 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 105 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 106 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 107 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 108 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 109 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 110 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 113 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 114 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 115 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 116 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 117 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 118 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 119 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 120 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 163 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 163 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 163 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 72 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 162 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, 2858, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, 2859, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, 2210, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, 2211, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, 2873, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, 2874, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, 2875, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, 2876, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, 2877, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, 2860, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, 2861, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 11 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 91 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 89 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x1, 0x1, 0, -1, -1, 13, 1, 0 }, + { 0x0, 0x0, 0, 2879, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 90 }, + { 0x0, 0x0, 0, 1966, -1, 0, 1, 138 }, + { 0x0, 0x0, 0, 1968, -1, 0, 1, 145 }, + { 0x0, 0x0, 0, 1970, -1, 0, 1, 139 }, + { 0x0, 0x0, 0, 1972, -1, 0, 1, 139 }, + { 0x0, 0x0, 0, 1974, -1, 0, 1, 138 }, + { 0x0, 0x0, 0, 1976, -1, 0, 1, 145 }, + { 0x0, 0x0, 0, 1978, -1, 0, 1, 138 }, + { 0x0, 0x0, 0, 1980, -1, 0, 1, 145 }, + { 0x0, 0x0, 0, 1983, -1, 0, 1, 138 }, + { 0x0, 0x0, 0, 1986, -1, 0, 1, 145 }, + { 0x0, 0x0, 0, 1989, -1, 0, 1, 157 }, + { 0x0, 0x0, 0, 1990, -1, 0, 1, 161 }, + { 0x0, 0x0, 0, 1991, -1, 0, 1, 157 }, + { 0x0, 0x0, 0, 1992, -1, 0, 1, 161 }, + { 0x0, 0x0, 0, 1993, -1, 0, 1, 157 }, + { 0x0, 0x0, 0, 1994, -1, 0, 1, 161 }, + { 0x0, 0x0, 0, 1995, -1, 0, 1, 157 }, + { 0x0, 0x0, 0, 1996, -1, 0, 1, 161 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 88 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 127 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 125 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 127 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 126 }, + { 0x0, 0x0, 0, 1687, -1, 0, 1, 143 }, + { 0x0, 0x0, 0, 1688, -1, 0, 1, 143 }, + { 0x0, 0x0, 0, 1689, -1, 0, 1, 143 }, + { 0x0, 0x0, 0, 1690, -1, 0, 1, 143 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 0, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 1, 224, -1, 0, 1, 12 }, + { 0x0, 0x0, 1, 225, -1, 0, 1, 14 }, + { 0x1, 0x1, 2, -1, -1, 27, 1, 12 }, + { 0x1, 0x1, 2, -1, -1, 27, 1, 14 }, + { 0x0, 0x0, 3, -1, 1340, 0, 0, -1 }, + { 0x0, 0x0, 3, -1, 1341, 0, 0, -1 }, + { 0x1, 0x1, 3, 2749, 1450, 33, 1, 134 }, + { 0x1, 0x1, 3, 2750, 1459, 33, 1, 134 }, + { 0x1, 0x1, 3, 2751, 1468, 33, 1, 134 }, + { 0x1, 0x1, 3, 2752, 1481, 33, 1, 134 }, + { 0x1, 0x1, 3, 2753, 1490, 33, 1, 134 }, + { 0x1, 0x1, 3, 2754, 1499, 33, 1, 134 }, + { 0x1, 0x1, 3, 2755, 1508, 33, 1, 134 }, + { 0x1, 0x1, 3, 2756, 1517, 33, 1, 134 }, + { 0x1, 0x1, 3, 2757, 1526, 33, 1, 134 }, + { 0x1, 0x1, 3, 2758, 1535, 33, 1, 134 }, + { 0x1, 0x1, 3, 2759, 1545, 33, 1, 134 }, + { 0x1, 0x1, 3, 2760, 1555, 33, 1, 134 }, + { 0x1, 0x1, 3, 2761, 1568, 33, 1, 149 }, + { 0x1, 0x1, 3, 2762, 1574, 33, 1, 154 }, + { 0x1, 0x1, 3, 2763, 1580, 33, 1, 154 }, + { 0x1, 0x1, 3, 2764, 1586, 33, 1, 149 }, + { 0x1, 0x1, 3, 2765, 1592, 33, 1, 154 }, + { 0x1, 0x1, 3, 2766, 1598, 33, 1, 154 }, + { 0x1, 0x1, 3, 2767, 1604, 33, 1, 149 }, + { 0x1, 0x1, 3, 2768, 1610, 33, 1, 154 }, + { 0x1, 0x1, 3, 2769, 1616, 33, 1, 154 }, + { 0x1, 0x1, 3, 2770, 1622, 33, 1, 149 }, + { 0x1, 0x1, 3, 2771, 1628, 33, 1, 154 }, + { 0x1, 0x1, 3, 2772, 1634, 33, 1, 149 }, + { 0x1, 0x1, 3, 2773, 1640, 33, 1, 154 }, + { 0x1, 0x1, 3, 2774, 1646, 33, 1, 149 }, + { 0x1, 0x1, 3, 2775, 1652, 33, 1, 154 }, + { 0x1, 0x1, 3, 2776, 1658, 33, 1, 149 }, + { 0x1, 0x1, 3, 2777, 1664, 33, 1, 154 }, + { 0x1, 0x1, 3, 2778, 1670, 33, 1, 154 }, + { 0x1, 0x1, 3, -1, -1, 27, 1, 41 }, + { 0x0, 0x0, 4, 2212, 1425, 0, 1, 142 }, + { 0x0, 0x0, 4, 2213, 1427, 0, 1, 142 }, + { 0x0, 0x0, 4, 2214, 1429, 0, 1, 141 }, + { 0x0, 0x0, 4, 2215, 1431, 0, 1, 141 }, + { 0x0, 0x0, 4, 2216, 1433, 0, 1, 141 }, + { 0x0, 0x0, 4, 2217, 1435, 0, 1, 141 }, + { 0x0, 0x0, 4, 2218, 1437, 0, 1, 141 }, + { 0x0, 0x0, 4, 2219, 1439, 0, 1, 141 }, + { 0x0, 0x0, 4, 2220, 1441, 0, 1, 141 }, + { 0x0, 0x0, 4, 2221, 1443, 0, 1, 141 }, + { 0x0, 0x0, 4, 2222, 1445, 0, 1, 143 }, + { 0x0, 0x0, 4, 2223, 1447, 0, 1, 143 }, + { 0x1, 0x1, 4, -1, 1454, 33, 1, 137 }, + { 0x5, 0x5, 4, 552, 1453, 32, 1, 131 }, + { 0x1, 0x1, 4, -1, 1463, 33, 1, 137 }, + { 0x5, 0x5, 4, 553, 1462, 32, 1, 131 }, + { 0x1, 0x1, 4, -1, 1472, 33, 1, 137 }, + { 0x5, 0x5, 4, 554, 1471, 32, 1, 131 }, + { 0x1, 0x1, 4, -1, 1476, 32, 1, 132 }, + { 0x1, 0x1, 4, -1, 1478, 32, 1, 132 }, + { 0x1, 0x1, 4, -1, 1485, 33, 1, 137 }, + { 0x5, 0x5, 4, 555, 1484, 32, 1, 131 }, + { 0x1, 0x1, 4, -1, 1494, 33, 1, 137 }, + { 0x5, 0x5, 4, 556, 1493, 32, 1, 131 }, + { 0x1, 0x1, 4, -1, 1503, 33, 1, 137 }, + { 0x5, 0x5, 4, 557, 1502, 32, 1, 131 }, + { 0x1, 0x1, 4, -1, 1512, 33, 1, 137 }, + { 0x5, 0x5, 4, 558, 1511, 32, 1, 131 }, + { 0x1, 0x1, 4, -1, 1521, 33, 1, 137 }, + { 0x5, 0x5, 4, 559, 1520, 32, 1, 131 }, + { 0x1, 0x1, 4, -1, 1530, 33, 1, 137 }, + { 0x5, 0x5, 4, 560, 1529, 32, 1, 131 }, + { 0x1, 0x1, 4, -1, 1540, 33, 1, 137 }, + { 0x5, 0x5, 4, 1036, 1538, 32, 1, 131 }, + { 0x1, 0x1, 4, -1, 1550, 33, 1, 137 }, + { 0x5, 0x5, 4, 1037, 1548, 32, 1, 131 }, + { 0x1, 0x1, 4, -1, 1560, 33, 1, 137 }, + { 0x5, 0x5, 4, 1038, 1558, 32, 1, 131 }, + { 0x1, 0x21, 10, 2013, -1, 33, 1, 3 }, + { 0x200001, 0x200001, 10, 2014, -1, 12, 1, 3 }, + { 0x1, 0x21, 10, 420, -1, 33, 1, 3 }, + { 0x200001, 0x200001, 10, 2074, -1, 12, 1, 3 }, + { 0x0, 0x0, 10, -1, 2075, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2076, 0, 0, -1 }, + { 0x0, 0x0, 10, 2017, -1, 0, 1, 3 }, + { 0x1, 0x1, 10, 2018, -1, 12, 1, 3 }, + { 0x1, 0x1, 10, 2019, -1, 33, 1, 3 }, + { 0x200001, 0x200001, 10, 2020, -1, 12, 1, 3 }, + { 0x0, 0x0, 10, 430, -1, 0, 1, 3 }, + { 0x1, 0x1, 10, 2080, -1, 12, 1, 3 }, + { 0x1, 0x1, 10, 434, -1, 33, 1, 3 }, + { 0x200001, 0x200001, 10, 2082, -1, 12, 1, 3 }, + { 0x0, 0x0, 10, 438, -1, 0, 1, 3 }, + { 0x1, 0x1, 10, 2084, -1, 12, 1, 3 }, + { 0x1, 0x1, 10, 442, -1, 33, 1, 3 }, + { 0x200001, 0x200001, 10, 2086, -1, 12, 1, 3 }, + { 0x0, 0x0, 10, 446, -1, 0, 1, 3 }, + { 0x1, 0x1, 10, 2088, -1, 12, 1, 3 }, + { 0x1, 0x1, 10, 450, -1, 33, 1, 3 }, + { 0x200001, 0x200001, 10, 2090, -1, 12, 1, 3 }, + { 0x1, 0x21, 10, 2033, -1, 33, 1, 3 }, + { 0x200001, 0x200001, 10, 2034, -1, 12, 1, 3 }, + { 0x1, 0x21, 10, 460, -1, 33, 1, 3 }, + { 0x200001, 0x200001, 10, 2096, -1, 12, 1, 3 }, + { 0x0, 0x0, 10, -1, 2097, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2098, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2101, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2102, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2103, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2104, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2105, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2106, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2107, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2108, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2109, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2110, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2111, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2112, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2113, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2114, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2115, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2116, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2117, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2118, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2119, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2120, 0, 0, -1 }, + { 0x1, 0x21, 10, 2037, -1, 33, 1, 3 }, + { 0x200001, 0x200001, 10, 2038, -1, 12, 1, 3 }, + { 0x1, 0x21, 10, 468, -1, 33, 1, 3 }, + { 0x200001, 0x200001, 10, 2122, -1, 12, 1, 3 }, + { 0x0, 0x0, 10, -1, 2123, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2124, 0, 0, -1 }, + { 0x0, 0x0, 10, 2041, -1, 0, 1, 3 }, + { 0x1, 0x1, 10, 2042, -1, 12, 1, 3 }, + { 0x1, 0x1, 10, 2043, -1, 33, 1, 3 }, + { 0x200001, 0x200001, 10, 2044, -1, 12, 1, 3 }, + { 0x0, 0x0, 10, 478, -1, 0, 1, 3 }, + { 0x1, 0x1, 10, 2128, -1, 12, 1, 3 }, + { 0x1, 0x1, 10, 482, -1, 33, 1, 3 }, + { 0x200001, 0x200001, 10, 2130, -1, 12, 1, 3 }, + { 0x0, 0x0, 10, 486, -1, 0, 1, 3 }, + { 0x1, 0x1, 10, 2132, -1, 12, 1, 3 }, + { 0x1, 0x1, 10, 490, -1, 33, 1, 3 }, + { 0x200001, 0x200001, 10, 2134, -1, 12, 1, 3 }, + { 0x0, 0x0, 10, 494, -1, 0, 1, 3 }, + { 0x1, 0x1, 10, 2136, -1, 12, 1, 3 }, + { 0x1, 0x1, 10, 498, -1, 33, 1, 3 }, + { 0x200001, 0x200001, 10, 2138, -1, 12, 1, 3 }, + { 0x1, 0x21, 10, 2057, -1, 33, 1, 3 }, + { 0x200001, 0x200001, 10, 2058, -1, 12, 1, 3 }, + { 0x1, 0x21, 10, 508, -1, 33, 1, 3 }, + { 0x200001, 0x200001, 10, 2144, -1, 12, 1, 3 }, + { 0x0, 0x0, 10, -1, 2145, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2146, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2149, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2150, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2151, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2152, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2153, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2154, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2155, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2156, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2157, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2158, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2159, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2160, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2161, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2162, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2163, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2164, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2165, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2166, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2167, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2168, 0, 0, -1 }, + { 0x1, 0x1, 10, 2061, -1, 36, 1, 3 }, + { 0x1000001, 0x1000001, 10, 2062, -1, 12, 1, 3 }, + { 0x1, 0x1, 10, 2063, -1, 36, 1, 3 }, + { 0x1000001, 0x1000001, 10, 2064, -1, 12, 1, 3 }, + { 0x0, 0x0, 10, -1, 2169, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2171, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2173, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2175, 0, 0, -1 }, + { 0x1, 0x1, 10, 2065, -1, 36, 1, 78 }, + { 0x1000001, 0x1000001, 10, 2066, -1, 12, 1, 78 }, + { 0x1, 0x1, 10, 2067, -1, 36, 1, 78 }, + { 0x1000001, 0x1000001, 10, 2068, -1, 12, 1, 78 }, + { 0x0, 0x0, 10, -1, 2177, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2179, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2181, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2183, 0, 0, -1 }, + { 0x1, 0x1, 10, 2069, -1, 36, 1, 3 }, + { 0x1000001, 0x1000001, 10, 2070, -1, 12, 1, 3 }, + { 0x1, 0x1, 10, 2071, -1, 36, 1, 3 }, + { 0x1000001, 0x1000001, 10, 2072, -1, 12, 1, 3 }, + { 0x0, 0x0, 10, -1, 2185, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2187, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2189, 0, 0, -1 }, + { 0x0, 0x0, 10, -1, 2191, 0, 0, -1 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x200001, 0x4200001, 11, 2015, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x1, 0x1, 11, 300, -1, 33, 1, 3 }, + { 0x0, 0x0, 11, 2077, -1, 0, 1, 3 }, + { 0x1, 0x1, 11, 2078, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x1, 0x1, 11, 2021, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x0, 0x0, 11, 308, -1, 0, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x200001, 0x200001, 11, 2023, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x1, 0x1, 11, 310, -1, 33, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x1, 0x1, 11, 2025, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x0, 0x0, 11, 312, -1, 0, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x200001, 0x200001, 11, 2027, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x1, 0x1, 11, 314, -1, 33, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x1, 0x1, 11, 2029, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x0, 0x0, 11, 316, -1, 0, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x200001, 0x200001, 11, 2031, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x1, 0x1, 11, 318, -1, 33, 1, 3 }, + { 0x0, 0x0, 11, 2091, -1, 0, 1, 3 }, + { 0x1, 0x1, 11, 2092, -1, 12, 1, 3 }, + { 0x1, 0x1, 11, 2093, -1, 33, 1, 3 }, + { 0x200001, 0x200001, 11, 2094, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x200001, 0x4200001, 11, 2035, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x1, 0x1, 11, 322, -1, 33, 1, 3 }, + { 0x0, 0x0, 11, 2099, -1, 0, 1, 3 }, + { 0x1, 0x1, 11, 2100, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x200001, 0x4200001, 11, 2039, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x1, 0x1, 11, 348, -1, 33, 1, 3 }, + { 0x0, 0x0, 11, 2125, -1, 0, 1, 3 }, + { 0x1, 0x1, 11, 2126, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x1, 0x1, 11, 2045, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x0, 0x0, 11, 356, -1, 0, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x200001, 0x200001, 11, 2047, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x1, 0x1, 11, 358, -1, 33, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x1, 0x1, 11, 2049, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x0, 0x0, 11, 360, -1, 0, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x200001, 0x200001, 11, 2051, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x1, 0x1, 11, 362, -1, 33, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x1, 0x1, 11, 2053, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x0, 0x0, 11, 364, -1, 0, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x200001, 0x200001, 11, 2055, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x1, 0x1, 11, 366, -1, 33, 1, 3 }, + { 0x0, 0x0, 11, 2139, -1, 0, 1, 3 }, + { 0x1, 0x1, 11, 2140, -1, 12, 1, 3 }, + { 0x1, 0x1, 11, 2141, -1, 33, 1, 3 }, + { 0x200001, 0x200001, 11, 2142, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x200001, 0x4200001, 11, 2059, -1, 12, 1, 3 }, + { 0x2, 0x3, 11, -1, -1, 37, 1, 5 }, + { 0x1, 0x1, 11, 370, -1, 33, 1, 3 }, + { 0x0, 0x0, 11, 2147, -1, 0, 1, 3 }, + { 0x1, 0x1, 11, 2148, -1, 12, 1, 3 }, + { 0x1, 0x1, 11, -1, -1, 36, 1, 5 }, + { 0x1, 0x1, 11, -1, -1, 36, 1, 5 }, + { 0x1, 0x1, 11, -1, -1, 36, 1, 5 }, + { 0x1, 0x1, 11, -1, -1, 36, 1, 5 }, + { 0x1, 0x1, 11, 2170, -1, 36, 1, 3 }, + { 0x1000001, 0x1000001, 11, 2172, -1, 12, 1, 3 }, + { 0x1, 0x1, 11, 2174, -1, 36, 1, 3 }, + { 0x1000001, 0x1000001, 11, 2176, -1, 12, 1, 3 }, + { 0x1, 0x1, 11, -1, -1, 36, 1, 80 }, + { 0x1, 0x1, 11, -1, -1, 36, 1, 80 }, + { 0x1, 0x1, 11, -1, -1, 36, 1, 80 }, + { 0x1, 0x1, 11, -1, -1, 36, 1, 80 }, + { 0x1, 0x1, 11, 2178, -1, 36, 1, 78 }, + { 0x1000001, 0x1000001, 11, 2180, -1, 12, 1, 78 }, + { 0x1, 0x1, 11, 2182, -1, 36, 1, 78 }, + { 0x1000001, 0x1000001, 11, 2184, -1, 12, 1, 78 }, + { 0x1, 0x1, 11, -1, -1, 36, 1, 5 }, + { 0x1, 0x1, 11, -1, -1, 36, 1, 5 }, + { 0x1, 0x1, 11, -1, -1, 36, 1, 5 }, + { 0x1, 0x1, 11, -1, -1, 36, 1, 5 }, + { 0x1, 0x1, 11, 2186, -1, 36, 1, 3 }, + { 0x1000001, 0x1000001, 11, 2188, -1, 12, 1, 3 }, + { 0x1, 0x1, 11, 2190, -1, 36, 1, 3 }, + { 0x1000001, 0x1000001, 11, 2192, -1, 12, 1, 3 }, + { 0x0, 0x0, 12, -1, -1, 0, 1, 15 }, + { 0x0, 0x0, 12, -1, -1, 0, 1, 15 }, + { 0x0, 0x0, 12, -1, -1, 0, 1, 15 }, + { 0x1, 0x1, 13, 272, 1452, 34, 1, 131 }, + { 0x1, 0x1, 13, 274, 1461, 34, 1, 131 }, + { 0x1, 0x1, 13, 276, 1470, 34, 1, 131 }, + { 0x1, 0x1, 13, 280, 1483, 34, 1, 131 }, + { 0x1, 0x1, 13, 282, 1492, 34, 1, 131 }, + { 0x1, 0x1, 13, 284, 1501, 34, 1, 131 }, + { 0x1, 0x1, 13, 286, 1510, 34, 1, 131 }, + { 0x1, 0x1, 13, 288, 1519, 34, 1, 131 }, + { 0x1, 0x1, 13, 290, 1528, 34, 1, 131 }, + { 0x1, 0x1, 13, 292, 1537, 34, 1, 131 }, + { 0x1, 0x1, 13, 294, 1547, 34, 1, 131 }, + { 0x1, 0x1, 13, 296, 1557, 34, 1, 131 }, + { 0x0, 0x0, 19, -1, 795, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 796, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 797, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 798, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 799, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 800, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 801, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 802, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 803, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 804, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 805, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 806, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 807, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 808, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 809, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 810, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 811, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 812, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 813, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 814, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 815, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 816, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 817, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 818, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 819, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 820, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 821, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 822, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 823, 0, 0, -1 }, + { 0x0, 0x0, 19, -1, 824, 0, 0, -1 }, + { 0x0, 0x0, 20, -1, 2827, 0, 0, -1 }, + { 0x0, 0x0, 20, -1, 2828, 0, 0, -1 }, + { 0x0, 0x0, 20, -1, 2843, 0, 0, -1 }, + { 0x0, 0x0, 20, -1, 2844, 0, 0, -1 }, + { 0x0, 0x0, 20, -1, 2849, 0, 0, -1 }, + { 0x0, 0x0, 20, -1, 2850, 0, 0, -1 }, + { 0x0, 0x0, 21, 831, 2839, 0, 0, -1 }, + { 0x0, 0x0, 21, 832, 2841, 0, 0, -1 }, + { 0x0, 0x0, 23, -1, 2837, 0, 0, -1 }, + { 0x0, 0x0, 23, -1, 2838, 0, 0, -1 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, 1272, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 7 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 7 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 7 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 7 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 7 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 7 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 7 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 7 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 6 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 7 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 7 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 7 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 7 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 8 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 8 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 8 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 8 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 8 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 8 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 8 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 8 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 8 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 8 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 8 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 8 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 16 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 16 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 16 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 16 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 16 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 16 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 16 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 16 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 16 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 16 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 16 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 16 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, 1293, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 19 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 20 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 20 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 20 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 20 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 20 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 20 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 20 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 20 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 20 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 20 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 20 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 20 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 21 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, 1326, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 18 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 35, 1, 22 }, + { 0x1, 0x1, 24, -1, -1, 33, 1, 82 }, + { 0x1, 0x1, 24, -1, -1, 33, 1, 82 }, + { 0x1, 0x1, 24, 1342, 1455, 35, 1, 137 }, + { 0x1, 0x1, 24, 1343, 1464, 35, 1, 137 }, + { 0x1, 0x1, 24, 1344, 1473, 35, 1, 137 }, + { 0x1, 0x1, 24, 1345, 1486, 35, 1, 137 }, + { 0x1, 0x1, 24, 1346, 1495, 35, 1, 137 }, + { 0x1, 0x1, 24, 1347, 1504, 35, 1, 137 }, + { 0x1, 0x1, 24, 1348, 1513, 35, 1, 137 }, + { 0x1, 0x1, 24, 1349, 1522, 35, 1, 137 }, + { 0x1, 0x1, 24, 1350, 1531, 35, 1, 137 }, + { 0x1, 0x1, 24, 1351, 1541, 35, 1, 137 }, + { 0x1, 0x1, 24, 1352, 1551, 35, 1, 137 }, + { 0x1, 0x1, 24, 1353, 1561, 35, 1, 137 }, + { 0x1, 0x1, 24, 1354, 1570, 35, 1, 151 }, + { 0x1, 0x1, 24, 1355, 1576, 35, 1, 156 }, + { 0x1, 0x1, 24, 1356, 1582, 35, 1, 156 }, + { 0x1, 0x1, 24, 1357, 1588, 35, 1, 151 }, + { 0x1, 0x1, 24, 1358, 1594, 35, 1, 156 }, + { 0x1, 0x1, 24, 1359, 1600, 35, 1, 156 }, + { 0x1, 0x1, 24, 1360, 1606, 35, 1, 151 }, + { 0x1, 0x1, 24, 1361, 1612, 35, 1, 156 }, + { 0x1, 0x1, 24, 1362, 1618, 35, 1, 156 }, + { 0x1, 0x1, 24, 1363, 1624, 35, 1, 151 }, + { 0x1, 0x1, 24, 1364, 1630, 35, 1, 156 }, + { 0x1, 0x1, 24, 1365, 1636, 35, 1, 151 }, + { 0x1, 0x1, 24, 1366, 1642, 35, 1, 156 }, + { 0x1, 0x1, 24, 1367, 1648, 35, 1, 151 }, + { 0x1, 0x1, 24, 1368, 1654, 35, 1, 156 }, + { 0x1, 0x1, 24, 1369, 1660, 35, 1, 151 }, + { 0x1, 0x1, 24, 1370, 1666, 35, 1, 156 }, + { 0x1, 0x1, 24, 1371, 1672, 35, 1, 156 }, + { 0x0, 0x0, 33, 2821, 2819, 0, 0, -1 }, + { 0x0, 0x0, 33, 2824, 2822, 0, 0, -1 }, + { 0x0, 0x0, 33, 2830, 2829, 0, 0, -1 }, + { 0x0, 0x0, 33, 2832, 2831, 0, 0, -1 }, + { 0x0, 0x0, 33, 2846, 2845, 0, 0, -1 }, + { 0x0, 0x0, 33, 2848, 2847, 0, 0, -1 }, + { 0x0, 0x0, 35, -1, 2840, 0, 0, -1 }, + { 0x0, 0x0, 35, -1, 2842, 0, 0, -1 }, + { 0x1, 0x1, 38, -1, 2290, 37, 1, 30 }, + { 0x1, 0x1, 38, -1, 2349, 37, 1, 30 }, + { 0x0, 0x0, 38, -1, 2352, 0, 0, -1 }, + { 0x1, 0x1, 38, -1, -1, 37, 1, 30 }, + { 0x1, 0x1, 38, -1, 2357, 37, 1, 30 }, + { 0x0, 0x0, 38, -1, 2360, 0, 0, -1 }, + { 0x1, 0x1, 38, -1, -1, 37, 1, 30 }, + { 0x0, 0x0, 38, -1, 2363, 0, 0, -1 }, + { 0x1, 0x1, 38, -1, -1, 37, 1, 30 }, + { 0x1, 0x1, 38, -1, 2366, 37, 1, 30 }, + { 0x1, 0x1, 38, -1, 2369, 37, 1, 30 }, + { 0x1, 0x1, 38, -1, 2402, 37, 1, 30 }, + { 0x3, 0x3, 38, -1, -1, 30, 1, 144 }, + { 0x0, 0x0, 38, 1142, -1, 0, 1, 102 }, + { 0x0, 0x0, 38, -1, -1, 0, 1, 111 }, + { 0x0, 0x0, 38, 1148, -1, 0, 1, 123 }, + { 0x3, 0x3, 38, -1, -1, 30, 1, 160 }, + { 0x0, 0x0, 38, 1149, -1, 0, 1, 41 }, + { 0x0, 0x0, 40, -1, 973, 0, 0, -1 }, + { 0x0, 0x0, 40, -1, 981, 0, 0, -1 }, + { 0x0, 0x0, 40, 1151, 977, 0, 0, -1 }, + { 0x3, 0x3, 40, -1, 622, 33, 1, 6 }, + { 0x18000001, 0x18000001, 40, -1, 630, 6, 1, 7 }, + { 0x3, 0x3, 40, 1152, 626, 33, 1, 6 }, + { 0x0, 0x0, 40, -1, 985, 0, 0, -1 }, + { 0x3, 0x3, 40, -1, 642, 33, 1, 8 }, + { 0x0, 0x0, 40, -1, 989, 0, 0, -1 }, + { 0x3, 0x3, 40, -1, 654, 33, 1, 16 }, + { 0x0, 0x0, 40, -1, 994, 0, 0, -1 }, + { 0x0, 0x0, 40, -1, 998, 0, 0, -1 }, + { 0x3, 0x3, 40, -1, 677, 33, 1, 18 }, + { 0x3, 0x3, 40, -1, 681, 33, 1, 18 }, + { 0x0, 0x0, 40, -1, 1002, 0, 0, -1 }, + { 0x0, 0x0, 40, -1, 1006, 0, 0, -1 }, + { 0x3, 0x3, 40, -1, 701, 33, 1, 19 }, + { 0x18000001, 0x18000001, 40, -1, 705, 6, 1, 19 }, + { 0x0, 0x0, 40, -1, 1010, 0, 0, -1 }, + { 0x3, 0x3, 40, -1, 717, 33, 1, 20 }, + { 0x0, 0x0, 40, -1, 1014, 0, 0, -1 }, + { 0x0, 0x0, 40, -1, 1018, 0, 0, -1 }, + { 0x3, 0x3, 40, -1, 737, 33, 1, 21 }, + { 0x18000001, 0x18000001, 40, -1, 741, 6, 1, 21 }, + { 0x0, 0x0, 40, -1, 1022, 0, 0, -1 }, + { 0x3, 0x3, 40, -1, 753, 33, 1, 22 }, + { 0x0, 0x0, 40, -1, 1027, 0, 0, -1 }, + { 0x0, 0x0, 40, -1, 1031, 0, 0, -1 }, + { 0x3, 0x3, 40, -1, 776, 33, 1, 18 }, + { 0x3, 0x3, 40, -1, 780, 33, 1, 18 }, + { 0x0, 0x0, 40, -1, 1035, 0, 0, -1 }, + { 0x3, 0x3, 40, -1, 792, 33, 1, 22 }, + { 0x0, 0x0, 41, 851, 972, 0, 0, -1 }, + { 0x0, 0x0, 41, 852, 980, 0, 0, -1 }, + { 0x0, 0x0, 41, 853, 976, 0, 0, -1 }, + { 0x1, 0x1, 41, 854, 621, 34, 1, 6 }, + { 0x10000001, 0x10000001, 41, 855, 629, 6, 1, 7 }, + { 0x1, 0x1, 41, 856, 625, 34, 1, 6 }, + { 0x0, 0x0, 41, 857, 984, 0, 0, -1 }, + { 0x1, 0x1, 41, 858, 641, 34, 1, 8 }, + { 0x0, 0x0, 41, 859, 988, 0, 0, -1 }, + { 0x1, 0x1, 41, 860, 653, 34, 1, 16 }, + { 0x0, 0x0, 41, 861, 993, 0, 0, -1 }, + { 0x0, 0x0, 41, 862, 997, 0, 0, -1 }, + { 0x1, 0x1, 41, 863, 676, 34, 1, 18 }, + { 0x1, 0x1, 41, 864, 680, 34, 1, 18 }, + { 0x0, 0x0, 41, 865, 1001, 0, 0, -1 }, + { 0x0, 0x0, 41, 866, 1005, 0, 0, -1 }, + { 0x1, 0x1, 41, 867, 700, 34, 1, 19 }, + { 0x10000001, 0x10000001, 41, 868, 704, 6, 1, 19 }, + { 0x0, 0x0, 41, 869, 1009, 0, 0, -1 }, + { 0x1, 0x1, 41, 870, 716, 34, 1, 20 }, + { 0x0, 0x0, 41, 871, 1013, 0, 0, -1 }, + { 0x0, 0x0, 41, 872, 1017, 0, 0, -1 }, + { 0x1, 0x1, 41, 873, 736, 34, 1, 21 }, + { 0x10000001, 0x10000001, 41, 874, 740, 6, 1, 21 }, + { 0x0, 0x0, 41, 875, 1021, 0, 0, -1 }, + { 0x1, 0x1, 41, 876, 752, 34, 1, 22 }, + { 0x0, 0x0, 41, 877, 1026, 0, 0, -1 }, + { 0x0, 0x0, 41, 878, 1030, 0, 0, -1 }, + { 0x1, 0x1, 41, 879, 775, 34, 1, 18 }, + { 0x1, 0x1, 41, 880, 779, 34, 1, 18 }, + { 0x0, 0x0, 41, 881, 1034, 0, 0, -1 }, + { 0x1, 0x1, 41, 882, 791, 34, 1, 22 }, + { 0x800001, 0x800001, 41, -1, 1156, 4, 1, 17 }, + { 0x1, 0x1, 41, 2236, 1154, 4, 1, 17 }, + { 0x1, 0x1, 41, 957, 1159, 4, 1, 23 }, + { 0x2, 0x3, 41, -1, 1164, 20, 1, 68 }, + { 0x1, 0x1, 41, 2237, 1162, 21, 1, 68 }, + { 0x0, 0x0, 42, -1, -1, 0, 1, 86 }, + { 0x0, 0x0, 42, -1, -1, 0, 1, 86 }, + { 0x0, 0x0, 42, -1, -1, 0, 1, 130 }, + { 0x1, 0x1, 44, 1372, 297, 38, 1, 1 }, + { 0x1, 0x1, 44, 1373, 299, 38, 1, 1 }, + { 0x0, 0x0, 44, -1, 302, 0, 0, -1 }, + { 0x0, 0x0, 44, -1, 424, 0, 0, -1 }, + { 0x1, 0x1, 44, 1377, 319, 38, 1, 1 }, + { 0x1, 0x1, 44, 1378, 321, 38, 1, 1 }, + { 0x0, 0x0, 44, -1, 324, 0, 0, -1 }, + { 0x0, 0x0, 44, -1, 464, 0, 0, -1 }, + { 0x0, 0x0, 44, -1, 326, 0, 0, -1 }, + { 0x0, 0x0, 44, -1, 344, 0, 0, -1 }, + { 0x1, 0x1, 44, 1384, 345, 38, 1, 1 }, + { 0x1, 0x1, 44, 1385, 347, 38, 1, 1 }, + { 0x0, 0x0, 44, -1, 350, 0, 0, -1 }, + { 0x0, 0x0, 44, -1, 472, 0, 0, -1 }, + { 0x1, 0x1, 44, 1389, 367, 38, 1, 1 }, + { 0x1, 0x1, 44, 1390, 369, 38, 1, 1 }, + { 0x0, 0x0, 44, -1, 372, 0, 0, -1 }, + { 0x0, 0x0, 44, -1, 512, 0, 0, -1 }, + { 0x0, 0x0, 44, -1, 374, 0, 0, -1 }, + { 0x0, 0x0, 44, -1, 392, 0, 0, -1 }, + { 0x0, 0x0, 44, 1248, 2297, 0, 0, -1 }, + { 0x0, 0x0, 44, 1249, 2305, 0, 1, 55 }, + { 0x0, 0x0, 44, 1250, 2972, 0, 1, 55 }, + { 0x0, 0x0, 44, 1251, 2373, 0, 0, -1 }, + { 0x0, 0x0, 44, 1252, -1, 0, 1, 50 }, + { 0x0, 0x0, 44, 1120, -1, 0, 1, 0 }, + { 0x0, 0x0, 44, 1121, -1, 0, 1, 0 }, + { 0x0, 0x0, 44, 1122, -1, 0, 1, 0 }, + { 0x1, 0x1, 45, -1, 1676, 30, 1, 158 }, + { 0x1, 0x1, 45, 963, 1675, 30, 1, 158 }, + { 0x1, 0x1, 45, -1, 1680, 30, 1, 159 }, + { 0x1, 0x1, 45, 964, 1679, 30, 1, 159 }, + { 0x1, 0x1, 45, -1, 1684, 30, 1, 159 }, + { 0x1, 0x1, 45, 965, 1683, 30, 1, 159 }, + { 0x3, 0x3, 46, -1, 1160, 3, 1, 23 }, + { 0x1, 0x1, 47, 2257, -1, 30, 1, 144 }, + { 0x1, 0x1, 47, 2288, -1, 30, 1, 160 }, + { 0x0, 0x0, 49, -1, -1, 0, 1, 41 }, + { 0x0, 0x0, 49, -1, -1, 0, 1, 41 }, + { 0x0, 0x0, 49, -1, -1, 0, 1, 41 }, + { 0x1, 0x1, 56, -1, 1677, 31, 1, 158 }, + { 0x1, 0x1, 56, -1, 1681, 31, 1, 159 }, + { 0x1, 0x1, 56, -1, 1685, 31, 1, 159 }, + { 0x0, 0x0, 56, -1, -1, 0, 1, 101 }, + { 0x2, 0x3, 56, -1, -1, 27, 1, 101 }, + { 0x1, 0x1, 56, -1, -1, 28, 1, 101 }, + { 0x0, 0x0, 65, 14, 592, 0, 1, 6 }, + { 0x0, 0x0, 65, 1273, 595, 0, 1, 6 }, + { 0x1, 0x1, 65, 1274, 597, 33, 1, 6 }, + { 0x1, 0x1, 65, 1275, 599, 34, 1, 6 }, + { 0x3, 0x3, 65, 1276, 601, 33, 1, 6 }, + { 0x0, 0x0, 65, 1277, 603, 0, 1, 6 }, + { 0x1, 0x1, 65, 1278, 605, 33, 1, 6 }, + { 0x1, 0x1, 65, 1279, 607, 34, 1, 6 }, + { 0x3, 0x3, 65, 1280, 609, 33, 1, 6 }, + { 0x1, 0x1, 65, 1281, 611, 6, 1, 7 }, + { 0x8000001, 0x8000001, 65, 1282, 613, 6, 1, 7 }, + { 0x10000001, 0x10000001, 65, 1283, 615, 6, 1, 7 }, + { 0x18000001, 0x18000001, 65, 1284, 617, 6, 1, 7 }, + { 0x0, 0x0, 65, 1285, 631, 0, 1, 8 }, + { 0x1, 0x1, 65, 1286, 633, 33, 1, 8 }, + { 0x1, 0x1, 65, 1287, 635, 34, 1, 8 }, + { 0x3, 0x3, 65, 1288, 637, 33, 1, 8 }, + { 0x0, 0x0, 65, 1289, 643, 0, 1, 16 }, + { 0x1, 0x1, 65, 1290, 645, 33, 1, 16 }, + { 0x1, 0x1, 65, 1291, 647, 34, 1, 16 }, + { 0x3, 0x3, 65, 1292, 649, 33, 1, 16 }, + { 0x0, 0x0, 65, 15, 655, 0, 1, 18 }, + { 0x0, 0x0, 65, 1294, 658, 0, 1, 18 }, + { 0x1, 0x1, 65, 1295, 660, 33, 1, 18 }, + { 0x1, 0x1, 65, 1296, 662, 34, 1, 18 }, + { 0x3, 0x3, 65, 1297, 664, 33, 1, 18 }, + { 0x0, 0x0, 65, 1298, 666, 0, 1, 18 }, + { 0x1, 0x1, 65, 1299, 668, 33, 1, 18 }, + { 0x1, 0x1, 65, 1300, 670, 34, 1, 18 }, + { 0x3, 0x3, 65, 1301, 672, 33, 1, 18 }, + { 0x0, 0x0, 65, 1302, 682, 0, 1, 19 }, + { 0x1, 0x1, 65, 1303, 684, 33, 1, 19 }, + { 0x1, 0x1, 65, 1304, 686, 34, 1, 19 }, + { 0x3, 0x3, 65, 1305, 688, 33, 1, 19 }, + { 0x1, 0x1, 65, 1306, 690, 6, 1, 19 }, + { 0x8000001, 0x8000001, 65, 1307, 692, 6, 1, 19 }, + { 0x10000001, 0x10000001, 65, 1308, 694, 6, 1, 19 }, + { 0x18000001, 0x18000001, 65, 1309, 696, 6, 1, 19 }, + { 0x0, 0x0, 65, 1310, 706, 0, 1, 20 }, + { 0x1, 0x1, 65, 1311, 708, 33, 1, 20 }, + { 0x1, 0x1, 65, 1312, 710, 34, 1, 20 }, + { 0x3, 0x3, 65, 1313, 712, 33, 1, 20 }, + { 0x0, 0x0, 65, 1314, 718, 0, 1, 21 }, + { 0x1, 0x1, 65, 1315, 720, 33, 1, 21 }, + { 0x1, 0x1, 65, 1316, 722, 34, 1, 21 }, + { 0x3, 0x3, 65, 1317, 724, 33, 1, 21 }, + { 0x1, 0x1, 65, 1318, 726, 6, 1, 21 }, + { 0x8000001, 0x8000001, 65, 1319, 728, 6, 1, 21 }, + { 0x10000001, 0x10000001, 65, 1320, 730, 6, 1, 21 }, + { 0x18000001, 0x18000001, 65, 1321, 732, 6, 1, 21 }, + { 0x0, 0x0, 65, 1322, 742, 0, 1, 22 }, + { 0x1, 0x1, 65, 1323, 744, 33, 1, 22 }, + { 0x1, 0x1, 65, 1324, 746, 34, 1, 22 }, + { 0x3, 0x3, 65, 1325, 748, 33, 1, 22 }, + { 0x0, 0x0, 65, 17, 754, 0, 1, 18 }, + { 0x0, 0x0, 65, 1327, 757, 0, 1, 18 }, + { 0x1, 0x1, 65, 1328, 759, 33, 1, 18 }, + { 0x1, 0x1, 65, 1329, 761, 34, 1, 18 }, + { 0x3, 0x3, 65, 1330, 763, 33, 1, 18 }, + { 0x0, 0x0, 65, 1331, 765, 0, 1, 18 }, + { 0x1, 0x1, 65, 1332, 767, 33, 1, 18 }, + { 0x1, 0x1, 65, 1333, 769, 34, 1, 18 }, + { 0x3, 0x3, 65, 1334, 771, 33, 1, 18 }, + { 0x0, 0x0, 65, 1335, 781, 0, 1, 22 }, + { 0x1, 0x1, 65, 1336, 783, 33, 1, 22 }, + { 0x1, 0x1, 65, 1337, 785, 34, 1, 22 }, + { 0x3, 0x3, 65, 1338, 787, 33, 1, 22 }, + { 0x3, 0x3, 66, 561, 1539, 33, 1, 136 }, + { 0x3, 0x3, 66, 562, 1549, 33, 1, 136 }, + { 0x3, 0x3, 66, 563, 1559, 33, 1, 136 }, + { 0x0, 0x0, 66, -1, 1564, 0, 1, 147 }, + { 0x0, 0x0, 66, -1, 1565, 0, 1, 152 }, + { 0x0, 0x0, 66, -1, 1566, 0, 1, 152 }, + { 0x0, 0x0, 107, 1046, 2345, 0, 0, -1 }, + { 0x0, 0x0, 107, 1047, 2864, 0, 1, 30 }, + { 0x0, 0x0, 107, 1048, 2386, 0, 0, -1 }, + { 0x0, 0x0, 107, 1049, 2868, 0, 1, 30 }, + { 0x0, 0x0, 109, -1, 2347, 0, 0, -1 }, + { 0x1, 0x1, 109, -1, 2865, 27, 1, 30 }, + { 0x0, 0x0, 109, -1, 2388, 0, 0, -1 }, + { 0x1, 0x1, 109, -1, 2869, 27, 1, 30 }, + { 0x0, 0x0, 110, 1051, -1, 0, 1, 122 }, + { 0x1, 0x1, 111, -1, -1, 27, 1, 122 }, + { 0x0, 0x0, 112, 1082, 2894, 0, 1, 1 }, + { 0x0, 0x0, 112, 1083, 2897, 0, 1, 1 }, + { 0x0, 0x0, 112, 1224, 305, 0, 0, -1 }, + { 0x0, 0x0, 112, 1225, 309, 0, 0, -1 }, + { 0x0, 0x0, 112, 1185, 440, 0, 0, -1 }, + { 0x0, 0x0, 112, 1186, 448, 0, 0, -1 }, + { 0x0, 0x0, 112, -1, 456, 0, 0, -1 }, + { 0x0, 0x0, 112, 1084, 2910, 0, 1, 1 }, + { 0x0, 0x0, 112, 1085, 2913, 0, 1, 1 }, + { 0x0, 0x0, 112, -1, 330, 0, 0, -1 }, + { 0x0, 0x0, 112, -1, 334, 0, 0, -1 }, + { 0x0, 0x0, 112, 1233, 335, 0, 0, -1 }, + { 0x0, 0x0, 112, 1234, 339, 0, 0, -1 }, + { 0x0, 0x0, 112, 1086, 2934, 0, 1, 1 }, + { 0x0, 0x0, 112, 1087, 2937, 0, 1, 1 }, + { 0x0, 0x0, 112, 1237, 353, 0, 0, -1 }, + { 0x0, 0x0, 112, 1238, 357, 0, 0, -1 }, + { 0x0, 0x0, 112, 1198, 488, 0, 0, -1 }, + { 0x0, 0x0, 112, 1199, 496, 0, 0, -1 }, + { 0x0, 0x0, 112, -1, 504, 0, 0, -1 }, + { 0x0, 0x0, 112, 1391, 2948, 0, 1, 1 }, + { 0x0, 0x0, 112, 1392, 2950, 0, 1, 1 }, + { 0x0, 0x0, 112, -1, 378, 0, 0, -1 }, + { 0x0, 0x0, 112, -1, 382, 0, 0, -1 }, + { 0x0, 0x0, 112, 1246, 383, 0, 0, -1 }, + { 0x0, 0x0, 112, 1247, 387, 0, 0, -1 }, + { 0x0, 0x0, 112, -1, 2315, 0, 0, -1 }, + { 0x1, 0x9, 112, -1, 2319, 33, 1, 55 }, + { 0x1, 0x9, 112, -1, 2981, 33, 1, 55 }, + { 0x2, 0x3, 112, 1408, 2382, 27, 1, 50 }, + { 0x1, 0x1, 114, 1374, 2895, 37, 1, 1 }, + { 0x1, 0x1, 114, 1375, 2898, 37, 1, 1 }, + { 0x1, 0x1, 114, 1379, 2911, 37, 1, 1 }, + { 0x1, 0x1, 114, 1380, 2914, 37, 1, 1 }, + { 0x1, 0x1, 114, 1386, 2935, 37, 1, 1 }, + { 0x1, 0x1, 114, 1387, 2938, 37, 1, 1 }, + { 0x0, 0x0, 114, -1, 2958, 0, 1, 1 }, + { 0x0, 0x0, 114, -1, 2959, 0, 1, 1 }, + { 0x0, 0x0, 115, 1123, 2890, 0, 1, 1 }, + { 0x0, 0x0, 115, 1124, 2892, 0, 1, 1 }, + { 0x0, 0x0, 115, 1183, 303, 0, 0, -1 }, + { 0x0, 0x0, 115, 1184, 307, 0, 0, -1 }, + { 0x0, 0x0, 115, -1, 444, 0, 0, -1 }, + { 0x0, 0x0, 115, -1, 452, 0, 0, -1 }, + { 0x0, 0x0, 115, 1228, 454, 0, 0, -1 }, + { 0x0, 0x0, 115, -1, 2908, 0, 1, 1 }, + { 0x0, 0x0, 115, -1, 2909, 0, 1, 1 }, + { 0x0, 0x0, 115, 1231, 328, 0, 0, -1 }, + { 0x0, 0x0, 115, 1232, 332, 0, 0, -1 }, + { 0x0, 0x0, 115, 1192, 337, 0, 0, -1 }, + { 0x0, 0x0, 115, 1193, 341, 0, 0, -1 }, + { 0x0, 0x0, 115, 1127, 2930, 0, 1, 1 }, + { 0x0, 0x0, 115, 1128, 2932, 0, 1, 1 }, + { 0x0, 0x0, 115, 1196, 351, 0, 0, -1 }, + { 0x0, 0x0, 115, 1197, 355, 0, 0, -1 }, + { 0x0, 0x0, 115, -1, 492, 0, 0, -1 }, + { 0x0, 0x0, 115, -1, 500, 0, 0, -1 }, + { 0x0, 0x0, 115, 1241, 502, 0, 0, -1 }, + { 0x0, 0x0, 115, -1, 2946, 0, 1, 1 }, + { 0x0, 0x0, 115, -1, 2947, 0, 1, 1 }, + { 0x0, 0x0, 115, 1244, 376, 0, 0, -1 }, + { 0x0, 0x0, 115, 1245, 380, 0, 0, -1 }, + { 0x0, 0x0, 115, 1205, 385, 0, 0, -1 }, + { 0x0, 0x0, 115, 1206, 389, 0, 0, -1 }, + { 0x0, 0x0, 115, 1078, 2313, 0, 0, -1 }, + { 0x0, 0x0, 115, 1079, 2317, 0, 1, 55 }, + { 0x0, 0x0, 115, 1080, 2980, 0, 1, 55 }, + { 0x0, 0x0, 115, 1081, 2381, 0, 1, 50 }, + { 0x1, 0x1, 115, -1, -1, 27, 1, 0 }, + { 0x1, 0x1, 115, -1, -1, 27, 1, 0 }, + { 0x1, 0x1, 115, -1, -1, 27, 1, 0 }, + { 0x1, 0x1, 116, -1, 2891, 37, 1, 1 }, + { 0x1, 0x1, 116, -1, 2893, 37, 1, 1 }, + { 0x0, 0x0, 116, -1, 2918, 0, 1, 1 }, + { 0x0, 0x0, 116, -1, 2919, 0, 1, 1 }, + { 0x1, 0x1, 116, -1, 2931, 37, 1, 1 }, + { 0x1, 0x1, 116, -1, 2933, 37, 1, 1 }, + { 0x0, 0x0, 116, -1, 2956, 0, 1, 1 }, + { 0x0, 0x0, 116, -1, 2957, 0, 1, 1 }, + { 0x0, 0x0, 117, 1176, -1, 0, 1, 0 }, + { 0x0, 0x0, 117, 1177, -1, 0, 1, 0 }, + { 0x0, 0x0, 117, 1178, -1, 0, 1, 0 }, + { 0x3, 0x3, 117, 1136, -1, 34, 1, 34 }, + { 0x3, 0x3, 117, 1137, -1, 34, 1, 41 }, + { 0x1, 0x1, 119, -1, -1, 35, 1, 34 }, + { 0x1, 0x1, 119, -1, -1, 35, 1, 41 }, + { 0x0, 0x0, 120, -1, -1, 0, 1, 41 }, + { 0x0, 0x0, 120, -1, -1, 0, 1, 67 }, + { 0x1, 0x1, 120, -1, -1, 36, 1, 129 }, + { 0x0, 0x0, 120, -1, -1, 0, 1, 41 }, + { 0x1, 0x1, 120, -1, -1, 27, 1, 103 }, + { 0x0, 0x0, 120, -1, -1, 0, 1, 112 }, + { 0x0, 0x0, 120, -1, -1, 0, 1, 74 }, + { 0x0, 0x0, 120, -1, -1, 0, 1, 74 }, + { 0x0, 0x0, 120, -1, -1, 0, 1, 75 }, + { 0x0, 0x0, 120, -1, -1, 0, 1, 41 }, + { 0x1, 0x1, 120, -1, -1, 27, 1, 124 }, + { 0x1, 0x1, 120, -1, -1, 27, 1, 41 }, + { 0x0, 0x0, 120, -1, -1, 0, 1, 41 }, + { 0x0, 0x0, 121, -1, 2820, 0, 0, -1 }, + { 0x0, 0x0, 121, -1, 2823, 0, 0, -1 }, + { 0x1, 0x1, 122, -1, -1, 35, 1, 17 }, + { 0x1, 0x1, 122, -1, -1, 35, 1, 17 }, + { 0x1, 0x1, 122, -1, -1, 35, 1, 17 }, + { 0x1, 0x1, 122, -1, -1, 35, 1, 17 }, + { 0x1, 0x1, 122, -1, -1, 35, 1, 23 }, + { 0x1, 0x1, 122, -1, -1, 35, 1, 23 }, + { 0x1, 0x1, 122, -1, -1, 35, 1, 23 }, + { 0x1, 0x1, 122, -1, -1, 35, 1, 23 }, + { 0x1, 0x1, 122, -1, -1, 23, 1, 68 }, + { 0x1, 0x1, 122, -1, -1, 23, 1, 68 }, + { 0x1, 0x1, 122, -1, -1, 23, 1, 68 }, + { 0x1, 0x1, 122, -1, -1, 23, 1, 68 }, + { 0x1, 0x1, 122, 918, -1, 23, 1, 68 }, + { 0x9, 0x9, 122, 919, -1, 20, 1, 68 }, + { 0x0, 0x0, 126, 2199, -1, 0, 1, 0 }, + { 0x0, 0x0, 126, 2200, -1, 0, 1, 0 }, + { 0x1, 0x1, 126, -1, -1, 28, 1, 34 }, + { 0x1, 0x1, 126, -1, -1, 27, 1, 34 }, + { 0x1, 0x1, 126, -1, -1, 29, 1, 0 }, + { 0x1, 0x1, 126, -1, -1, 29, 1, 0 }, + { 0x1, 0x1, 126, -1, -1, 29, 1, 0 }, + { 0x1, 0x1, 126, -1, -1, 29, 1, 0 }, + { 0x0, 0x0, 126, -1, -1, 0, 1, 121 }, + { 0x1, 0x1, 126, -1, -1, 29, 1, 0 }, + { 0x1, 0x1, 126, -1, -1, 29, 1, 0 }, + { 0x1, 0x1, 126, -1, -1, 29, 1, 0 }, + { 0x0, 0x0, 126, 1134, -1, 0, 1, 34 }, + { 0x0, 0x0, 126, 1262, -1, 0, 1, 41 }, + { 0x0, 0x0, 140, 1212, 2886, 0, 1, 1 }, + { 0x0, 0x0, 140, 1213, 2888, 0, 1, 1 }, + { 0x0, 0x0, 140, 1054, 304, 0, 0, -1 }, + { 0x0, 0x0, 140, 1055, 432, 0, 0, -1 }, + { 0x0, 0x0, 140, 1094, 313, 0, 0, -1 }, + { 0x0, 0x0, 140, 1095, 317, 0, 0, -1 }, + { 0x0, 0x0, 140, 1096, 453, 0, 0, -1 }, + { 0x0, 0x0, 140, -1, 2906, 0, 1, 1 }, + { 0x0, 0x0, 140, -1, 2907, 0, 1, 1 }, + { 0x0, 0x0, 140, 1099, 327, 0, 0, -1 }, + { 0x0, 0x0, 140, 1100, 331, 0, 0, -1 }, + { 0x0, 0x0, 140, -1, 338, 0, 0, -1 }, + { 0x0, 0x0, 140, -1, 342, 0, 0, -1 }, + { 0x0, 0x0, 140, 1216, 2926, 0, 1, 1 }, + { 0x0, 0x0, 140, 1217, 2928, 0, 1, 1 }, + { 0x0, 0x0, 140, 1067, 352, 0, 0, -1 }, + { 0x0, 0x0, 140, 1068, 480, 0, 0, -1 }, + { 0x0, 0x0, 140, 1107, 361, 0, 0, -1 }, + { 0x0, 0x0, 140, 1108, 365, 0, 0, -1 }, + { 0x0, 0x0, 140, 1109, 501, 0, 0, -1 }, + { 0x0, 0x0, 140, -1, 2944, 0, 1, 1 }, + { 0x0, 0x0, 140, -1, 2945, 0, 1, 1 }, + { 0x0, 0x0, 140, 1112, 375, 0, 0, -1 }, + { 0x0, 0x0, 140, 1113, 379, 0, 0, -1 }, + { 0x0, 0x0, 140, -1, 386, 0, 0, -1 }, + { 0x0, 0x0, 140, -1, 390, 0, 0, -1 }, + { 0x0, 0x0, 140, 3012, 2301, 0, 0, -1 }, + { 0x1, 0x1, 140, 3013, 2309, 33, 1, 55 }, + { 0x1, 0x1, 140, 3014, 2974, 33, 1, 55 }, + { 0x0, 0x0, 140, 3015, 2375, 0, 0, -1 }, + { 0x1, 0x1, 140, 3016, -1, 28, 1, 50 }, + { 0x1, 0x1, 141, -1, 2887, 37, 1, 1 }, + { 0x1, 0x1, 141, -1, 2889, 37, 1, 1 }, + { 0x0, 0x0, 141, -1, 2916, 0, 1, 1 }, + { 0x0, 0x0, 141, -1, 2917, 0, 1, 1 }, + { 0x1, 0x1, 141, -1, 2927, 37, 1, 1 }, + { 0x1, 0x1, 141, -1, 2929, 37, 1, 1 }, + { 0x0, 0x0, 141, -1, 2954, 0, 1, 1 }, + { 0x0, 0x0, 141, -1, 2955, 0, 1, 1 }, + { 0x1, 0x1, 144, 917, 1158, 3, 1, 23 }, + { 0x0, 0x0, 145, 2201, -1, 0, 1, 34 }, + { 0x0, 0x0, 146, 923, 2880, 0, 1, 1 }, + { 0x0, 0x0, 146, 924, 2883, 0, 1, 1 }, + { 0x0, 0x0, 146, -1, 306, 0, 0, -1 }, + { 0x0, 0x0, 146, -1, 436, 0, 0, -1 }, + { 0x0, 0x0, 146, 1056, 311, 0, 0, -1 }, + { 0x0, 0x0, 146, 1057, 315, 0, 0, -1 }, + { 0x0, 0x0, 146, 1058, 455, 0, 0, -1 }, + { 0x0, 0x0, 146, 927, 2900, 0, 1, 1 }, + { 0x0, 0x0, 146, 928, 2903, 0, 1, 1 }, + { 0x0, 0x0, 146, 1061, 329, 0, 0, -1 }, + { 0x0, 0x0, 146, 1062, 333, 0, 0, -1 }, + { 0x0, 0x0, 146, 1101, 336, 0, 0, -1 }, + { 0x0, 0x0, 146, 1102, 340, 0, 0, -1 }, + { 0x0, 0x0, 146, 933, 2920, 0, 1, 1 }, + { 0x0, 0x0, 146, 934, 2923, 0, 1, 1 }, + { 0x0, 0x0, 146, -1, 354, 0, 0, -1 }, + { 0x0, 0x0, 146, -1, 484, 0, 0, -1 }, + { 0x0, 0x0, 146, 1069, 359, 0, 0, -1 }, + { 0x0, 0x0, 146, 1070, 363, 0, 0, -1 }, + { 0x0, 0x0, 146, 1071, 503, 0, 0, -1 }, + { 0x0, 0x0, 146, 937, 2940, 0, 1, 1 }, + { 0x0, 0x0, 146, 938, 2942, 0, 1, 1 }, + { 0x0, 0x0, 146, 1074, 377, 0, 0, -1 }, + { 0x0, 0x0, 146, 1075, 381, 0, 0, -1 }, + { 0x0, 0x0, 146, 1114, 384, 0, 0, -1 }, + { 0x0, 0x0, 146, 1115, 388, 0, 0, -1 }, + { 0x0, 0x0, 146, 1207, 2299, 0, 0, -1 }, + { 0x1, 0x1, 146, 1208, 2307, 36, 1, 55 }, + { 0x1, 0x1, 146, 1209, 2973, 36, 1, 55 }, + { 0x0, 0x0, 146, 1210, 2374, 0, 0, -1 }, + { 0x1, 0x1, 146, 1211, -1, 27, 1, 50 }, + { 0x1, 0x1, 147, -1, 2882, 37, 1, 1 }, + { 0x1, 0x1, 147, -1, 2885, 37, 1, 1 }, + { 0x1, 0x1, 147, -1, 2902, 37, 1, 1 }, + { 0x1, 0x1, 147, -1, 2905, 37, 1, 1 }, + { 0x1, 0x1, 147, -1, 2922, 37, 1, 1 }, + { 0x1, 0x1, 147, -1, 2925, 37, 1, 1 }, + { 0x0, 0x0, 147, -1, 2952, 0, 1, 1 }, + { 0x0, 0x0, 147, -1, 2953, 0, 1, 1 }, + { 0x0, 0x0, 148, -1, -1, 0, 1, 34 }, + { 0x0, 0x0, 148, 1135, -1, 0, 1, 41 }, + { 0x0, 0x0, 149, -1, -1, 0, 1, 41 }, + { 0x0, 0x0, 149, -1, -1, 0, 1, 67 }, + { 0x0, 0x0, 149, -1, 2960, 0, 1, 64 }, + { 0x0, 0x0, 149, -1, 2961, 0, 1, 64 }, + { 0x0, 0x0, 149, -1, -1, 0, 1, 41 }, + { 0x0, 0x0, 149, -1, -1, 0, 1, 87 }, + { 0x0, 0x0, 149, -1, -1, 0, 1, 87 }, + { 0x0, 0x0, 149, -1, -1, 0, 1, 92 }, + { 0x0, 0x0, 149, -1, -1, 0, 1, 41 }, + { 0x1, 0x1, 150, -1, 593, 12, 1, 6 }, + { 0x1, 0x1, 150, -1, 596, 12, 1, 6 }, + { 0x200001, 0x200001, 150, -1, 598, 12, 1, 6 }, + { 0x400001, 0x400001, 150, -1, 600, 12, 1, 6 }, + { 0x600001, 0x600001, 150, -1, 602, 12, 1, 6 }, + { 0x1, 0x1, 150, -1, 604, 12, 1, 6 }, + { 0x200001, 0x200001, 150, -1, 606, 12, 1, 6 }, + { 0x400001, 0x400001, 150, -1, 608, 12, 1, 6 }, + { 0x600001, 0x600001, 150, -1, 610, 12, 1, 6 }, + { 0x41, 0x41, 150, -1, 612, 6, 1, 7 }, + { 0x8000041, 0x8000041, 150, -1, 614, 6, 1, 7 }, + { 0x10000041, 0x10000041, 150, -1, 616, 6, 1, 7 }, + { 0x18000041, 0x18000041, 150, -1, 618, 6, 1, 7 }, + { 0x1, 0x1, 150, -1, 632, 12, 1, 8 }, + { 0x200001, 0x200001, 150, -1, 634, 12, 1, 8 }, + { 0x400001, 0x400001, 150, -1, 636, 12, 1, 8 }, + { 0x600001, 0x600001, 150, -1, 638, 12, 1, 8 }, + { 0x1, 0x1, 150, -1, 644, 12, 1, 16 }, + { 0x200001, 0x200001, 150, -1, 646, 12, 1, 16 }, + { 0x400001, 0x400001, 150, -1, 648, 12, 1, 16 }, + { 0x600001, 0x600001, 150, -1, 650, 12, 1, 16 }, + { 0x1, 0x1, 150, -1, 656, 12, 1, 18 }, + { 0x1, 0x1, 150, -1, 659, 12, 1, 18 }, + { 0x200001, 0x200001, 150, -1, 661, 12, 1, 18 }, + { 0x400001, 0x400001, 150, -1, 663, 12, 1, 18 }, + { 0x600001, 0x600001, 150, -1, 665, 12, 1, 18 }, + { 0x1, 0x1, 150, -1, 667, 12, 1, 18 }, + { 0x200001, 0x200001, 150, -1, 669, 12, 1, 18 }, + { 0x400001, 0x400001, 150, -1, 671, 12, 1, 18 }, + { 0x600001, 0x600001, 150, -1, 673, 12, 1, 18 }, + { 0x1, 0x1, 150, -1, 683, 12, 1, 19 }, + { 0x200001, 0x200001, 150, -1, 685, 12, 1, 19 }, + { 0x400001, 0x400001, 150, -1, 687, 12, 1, 19 }, + { 0x600001, 0x600001, 150, -1, 689, 12, 1, 19 }, + { 0x41, 0x41, 150, -1, 691, 6, 1, 19 }, + { 0x8000041, 0x8000041, 150, -1, 693, 6, 1, 19 }, + { 0x10000041, 0x10000041, 150, -1, 695, 6, 1, 19 }, + { 0x18000041, 0x18000041, 150, -1, 697, 6, 1, 19 }, + { 0x1, 0x1, 150, -1, 707, 12, 1, 20 }, + { 0x200001, 0x200001, 150, -1, 709, 12, 1, 20 }, + { 0x400001, 0x400001, 150, -1, 711, 12, 1, 20 }, + { 0x600001, 0x600001, 150, -1, 713, 12, 1, 20 }, + { 0x1, 0x1, 150, -1, 719, 12, 1, 21 }, + { 0x200001, 0x200001, 150, -1, 721, 12, 1, 21 }, + { 0x400001, 0x400001, 150, -1, 723, 12, 1, 21 }, + { 0x600001, 0x600001, 150, -1, 725, 12, 1, 21 }, + { 0x41, 0x41, 150, -1, 727, 6, 1, 21 }, + { 0x8000041, 0x8000041, 150, -1, 729, 6, 1, 21 }, + { 0x10000041, 0x10000041, 150, -1, 731, 6, 1, 21 }, + { 0x18000041, 0x18000041, 150, -1, 733, 6, 1, 21 }, + { 0x1, 0x1, 150, -1, 743, 12, 1, 22 }, + { 0x200001, 0x200001, 150, -1, 745, 12, 1, 22 }, + { 0x400001, 0x400001, 150, -1, 747, 12, 1, 22 }, + { 0x600001, 0x600001, 150, -1, 749, 12, 1, 22 }, + { 0x1, 0x1, 150, -1, 755, 12, 1, 18 }, + { 0x1, 0x1, 150, -1, 758, 12, 1, 18 }, + { 0x200001, 0x200001, 150, -1, 760, 12, 1, 18 }, + { 0x400001, 0x400001, 150, -1, 762, 12, 1, 18 }, + { 0x600001, 0x600001, 150, -1, 764, 12, 1, 18 }, + { 0x1, 0x1, 150, -1, 766, 12, 1, 18 }, + { 0x200001, 0x200001, 150, -1, 768, 12, 1, 18 }, + { 0x400001, 0x400001, 150, -1, 770, 12, 1, 18 }, + { 0x600001, 0x600001, 150, -1, 772, 12, 1, 18 }, + { 0x1, 0x1, 150, -1, 782, 12, 1, 22 }, + { 0x200001, 0x200001, 150, -1, 784, 12, 1, 22 }, + { 0x400001, 0x400001, 150, -1, 786, 12, 1, 22 }, + { 0x600001, 0x600001, 150, -1, 788, 12, 1, 22 }, + { 0x0, 0x0, 155, -1, -1, 0, 1, 131 }, + { 0x0, 0x0, 159, 793, -1, 0, 1, 81 }, + { 0x0, 0x0, 159, 794, -1, 0, 1, 81 }, + { 0x9, 0x9, 159, -1, 1456, 32, 1, 137 }, + { 0x9, 0x9, 159, -1, 1465, 32, 1, 137 }, + { 0x9, 0x9, 159, -1, 1474, 32, 1, 137 }, + { 0x9, 0x9, 159, -1, 1487, 32, 1, 137 }, + { 0x9, 0x9, 159, -1, 1496, 32, 1, 137 }, + { 0x9, 0x9, 159, -1, 1505, 32, 1, 137 }, + { 0x9, 0x9, 159, -1, 1514, 32, 1, 137 }, + { 0x9, 0x9, 159, -1, 1523, 32, 1, 137 }, + { 0x9, 0x9, 159, -1, 1532, 32, 1, 137 }, + { 0x9, 0x9, 159, -1, 1542, 32, 1, 137 }, + { 0x9, 0x9, 159, -1, 1552, 32, 1, 137 }, + { 0x9, 0x9, 159, -1, 1562, 32, 1, 137 }, + { 0x9, 0x9, 159, -1, 1571, 32, 1, 151 }, + { 0x9, 0x9, 159, -1, 1577, 32, 1, 156 }, + { 0x9, 0x9, 159, -1, 1583, 32, 1, 156 }, + { 0x9, 0x9, 159, -1, 1589, 32, 1, 151 }, + { 0x9, 0x9, 159, -1, 1595, 32, 1, 156 }, + { 0x9, 0x9, 159, -1, 1601, 32, 1, 156 }, + { 0x9, 0x9, 159, -1, 1607, 32, 1, 151 }, + { 0x9, 0x9, 159, -1, 1613, 32, 1, 156 }, + { 0x9, 0x9, 159, -1, 1619, 32, 1, 156 }, + { 0x9, 0x9, 159, -1, 1625, 32, 1, 151 }, + { 0x9, 0x9, 159, -1, 1631, 32, 1, 156 }, + { 0x9, 0x9, 159, -1, 1637, 32, 1, 151 }, + { 0x9, 0x9, 159, -1, 1643, 32, 1, 156 }, + { 0x9, 0x9, 159, -1, 1649, 32, 1, 151 }, + { 0x9, 0x9, 159, -1, 1655, 32, 1, 156 }, + { 0x9, 0x9, 159, -1, 1661, 32, 1, 151 }, + { 0x9, 0x9, 159, -1, 1667, 32, 1, 156 }, + { 0x9, 0x9, 159, -1, 1673, 32, 1, 156 }, + { 0x0, 0x0, 160, 1253, 298, 0, 0, -1 }, + { 0x0, 0x0, 160, 1254, 422, 0, 0, -1 }, + { 0x1, 0x1, 160, -1, 2896, 38, 1, 1 }, + { 0x1, 0x1, 160, 925, 2899, 38, 1, 1 }, + { 0x0, 0x0, 160, 926, 423, 0, 0, -1 }, + { 0x0, 0x0, 160, 1255, 320, 0, 0, -1 }, + { 0x0, 0x0, 160, 1256, 462, 0, 0, -1 }, + { 0x1, 0x1, 160, -1, 2912, 38, 1, 1 }, + { 0x1, 0x1, 160, 929, 2915, 38, 1, 1 }, + { 0x0, 0x0, 160, 930, 463, 0, 0, -1 }, + { 0x0, 0x0, 160, 931, 325, 0, 0, -1 }, + { 0x0, 0x0, 160, 932, 343, 0, 0, -1 }, + { 0x0, 0x0, 160, 1257, 346, 0, 0, -1 }, + { 0x0, 0x0, 160, 1258, 470, 0, 0, -1 }, + { 0x1, 0x1, 160, -1, 2936, 38, 1, 1 }, + { 0x1, 0x1, 160, 935, 2939, 38, 1, 1 }, + { 0x0, 0x0, 160, 936, 471, 0, 0, -1 }, + { 0x0, 0x0, 160, -1, 368, 0, 0, -1 }, + { 0x0, 0x0, 160, -1, 510, 0, 0, -1 }, + { 0x1, 0x1, 160, -1, 2949, 38, 1, 1 }, + { 0x1, 0x1, 160, 939, 2951, 38, 1, 1 }, + { 0x0, 0x0, 160, 940, 511, 0, 0, -1 }, + { 0x0, 0x0, 160, 941, 373, 0, 0, -1 }, + { 0x0, 0x0, 160, 942, 391, 0, 0, -1 }, + { 0x0, 0x0, 161, 1415, 2321, 0, 0, -1 }, + { 0x0, 0x0, 161, 1416, 2329, 0, 1, 55 }, + { 0x0, 0x0, 161, 1417, 2990, 0, 1, 55 }, + { 0x0, 0x0, 161, 1418, 2377, 0, 0, -1 }, + { 0x1, 0x1, 161, 1419, -1, 29, 1, 50 }, + { 0x0, 0x0, 162, -1, 2339, 0, 0, -1 }, + { 0x1, 0x9, 162, -1, 2343, 33, 1, 55 }, + { 0x1, 0x9, 162, -1, 2999, 33, 1, 55 }, + { 0x6, 0x7, 162, -1, 2384, 27, 1, 50 }, + { 0x0, 0x0, 163, 1401, 2337, 0, 0, -1 }, + { 0x0, 0x0, 163, 1402, 2341, 0, 1, 55 }, + { 0x0, 0x0, 163, 1403, 2998, 0, 1, 55 }, + { 0x1, 0x1, 163, 1404, 2383, 29, 1, 50 }, + { 0x1, 0x1, 164, 1422, -1, 27, 1, 34 }, + { 0x0, 0x0, 165, 2193, 2325, 0, 0, -1 }, + { 0x1, 0x1, 165, 2194, 2333, 33, 1, 55 }, + { 0x1, 0x1, 165, 2195, 2992, 33, 1, 55 }, + { 0x0, 0x0, 165, 2196, 2379, 0, 0, -1 }, + { 0x3, 0x3, 165, 2197, -1, 28, 1, 50 }, + { 0x0, 0x0, 166, 1410, 2323, 0, 0, -1 }, + { 0x1, 0x1, 166, 1411, 2331, 36, 1, 55 }, + { 0x1, 0x1, 166, 1412, 2991, 36, 1, 55 }, + { 0x0, 0x0, 166, 1413, 2378, 0, 0, -1 }, + { 0x5, 0x5, 166, 1414, -1, 27, 1, 50 }, + { 0x0, 0x0, 167, -1, 2962, 0, 1, 64 }, + { 0x0, 0x0, 167, -1, 2963, 0, 1, 64 }, + { 0x1, 0x1, 169, -1, -1, 28, 1, 34 }, + { 0x1, 0x1, 170, 2779, -1, 27, 1, 34 }, + { 0x1, 0x1, 170, 2780, -1, 27, 1, 34 }, + { 0x1, 0x1, 171, 1703, -1, 28, 1, 142 }, + { 0x1, 0x1, 171, 1704, -1, 28, 1, 142 }, + { 0x1, 0x1, 171, 1705, -1, 28, 1, 142 }, + { 0x1, 0x1, 171, 1706, -1, 28, 1, 142 }, + { 0x1, 0x1, 171, 1707, -1, 28, 1, 141 }, + { 0x1, 0x1, 171, 1708, -1, 28, 1, 141 }, + { 0x1, 0x1, 171, 1709, -1, 28, 1, 141 }, + { 0x1, 0x1, 171, 1710, -1, 28, 1, 141 }, + { 0x1, 0x1, 171, 1711, -1, 28, 1, 141 }, + { 0x1, 0x1, 171, 1712, -1, 28, 1, 141 }, + { 0x1, 0x1, 171, 1713, -1, 28, 1, 141 }, + { 0x1, 0x1, 171, 1714, -1, 28, 1, 141 }, + { 0x1, 0x1, 171, 1715, -1, 28, 1, 141 }, + { 0x1, 0x1, 171, 1716, -1, 28, 1, 141 }, + { 0x1, 0x1, 171, 1717, -1, 28, 1, 141 }, + { 0x1, 0x1, 171, 1718, -1, 28, 1, 141 }, + { 0x1, 0x1, 171, 1719, -1, 28, 1, 141 }, + { 0x1, 0x1, 171, 1720, -1, 28, 1, 141 }, + { 0x1, 0x1, 171, 1721, -1, 28, 1, 141 }, + { 0x1, 0x1, 171, 1722, -1, 28, 1, 141 }, + { 0x1, 0x1, 171, 1723, -1, 28, 1, 143 }, + { 0x1, 0x1, 171, 1724, -1, 28, 1, 143 }, + { 0x1, 0x1, 171, 1725, -1, 28, 1, 143 }, + { 0x1, 0x1, 171, 1726, -1, 28, 1, 143 }, + { 0x1, 0x1, 171, 1727, -1, 28, 1, 133 }, + { 0x1, 0x1, 171, 1728, -1, 28, 1, 134 }, + { 0x1, 0x1, 171, 1729, -1, 28, 1, 135 }, + { 0x1, 0x1, 171, 1730, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1731, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1732, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1733, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1734, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1735, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1736, -1, 28, 1, 133 }, + { 0x1, 0x1, 171, 1737, -1, 28, 1, 134 }, + { 0x1, 0x1, 171, 1738, -1, 28, 1, 135 }, + { 0x1, 0x1, 171, 1739, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1740, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1741, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1742, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1743, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1744, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1745, -1, 28, 1, 133 }, + { 0x1, 0x1, 171, 1746, -1, 28, 1, 134 }, + { 0x1, 0x1, 171, 1747, -1, 28, 1, 135 }, + { 0x1, 0x1, 171, 1748, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1749, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1750, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1751, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1752, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1753, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1754, -1, 28, 1, 132 }, + { 0x1, 0x1, 171, 1755, -1, 28, 1, 132 }, + { 0x1, 0x1, 171, 1756, -1, 28, 1, 132 }, + { 0x1, 0x1, 171, 1757, -1, 28, 1, 132 }, + { 0x1, 0x1, 171, 1758, -1, 28, 1, 133 }, + { 0x1, 0x1, 171, 1759, -1, 28, 1, 134 }, + { 0x1, 0x1, 171, 1760, -1, 28, 1, 135 }, + { 0x1, 0x1, 171, 1761, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1762, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1763, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1764, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1765, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1766, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1767, -1, 28, 1, 133 }, + { 0x1, 0x1, 171, 1768, -1, 28, 1, 134 }, + { 0x1, 0x1, 171, 1769, -1, 28, 1, 135 }, + { 0x1, 0x1, 171, 1770, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1771, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1772, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1773, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1774, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1775, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1776, -1, 28, 1, 133 }, + { 0x1, 0x1, 171, 1777, -1, 28, 1, 134 }, + { 0x1, 0x1, 171, 1778, -1, 28, 1, 135 }, + { 0x1, 0x1, 171, 1779, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1780, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1781, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1782, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1783, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1784, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1785, -1, 28, 1, 133 }, + { 0x1, 0x1, 171, 1786, -1, 28, 1, 134 }, + { 0x1, 0x1, 171, 1787, -1, 28, 1, 135 }, + { 0x1, 0x1, 171, 1788, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1789, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1790, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1791, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1792, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1793, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1794, -1, 28, 1, 133 }, + { 0x1, 0x1, 171, 1795, -1, 28, 1, 134 }, + { 0x1, 0x1, 171, 1796, -1, 28, 1, 135 }, + { 0x1, 0x1, 171, 1797, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1798, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1799, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1800, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1801, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1802, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1803, -1, 28, 1, 133 }, + { 0x1, 0x1, 171, 1804, -1, 28, 1, 134 }, + { 0x1, 0x1, 171, 1805, -1, 28, 1, 135 }, + { 0x1, 0x1, 171, 1806, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1807, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1808, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1809, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1810, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1811, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1812, -1, 28, 1, 133 }, + { 0x1, 0x1, 171, 1813, -1, 28, 1, 134 }, + { 0x1, 0x1, 171, 1814, -1, 28, 1, 135 }, + { 0x1, 0x1, 171, 1815, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1816, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1817, -1, 28, 1, 136 }, + { 0x1, 0x1, 171, 1818, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1819, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1820, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1821, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1822, -1, 28, 1, 133 }, + { 0x1, 0x1, 171, 1823, -1, 28, 1, 134 }, + { 0x1, 0x1, 171, 1824, -1, 28, 1, 135 }, + { 0x1, 0x1, 171, 1825, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1826, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1827, -1, 28, 1, 136 }, + { 0x1, 0x1, 171, 1828, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1829, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1830, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1831, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1832, -1, 28, 1, 133 }, + { 0x1, 0x1, 171, 1833, -1, 28, 1, 134 }, + { 0x1, 0x1, 171, 1834, -1, 28, 1, 135 }, + { 0x1, 0x1, 171, 1835, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1836, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1837, -1, 28, 1, 136 }, + { 0x1, 0x1, 171, 1838, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1839, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1840, -1, 28, 1, 137 }, + { 0x1, 0x1, 171, 1841, -1, 28, 1, 131 }, + { 0x1, 0x1, 171, 1842, -1, 28, 1, 147 }, + { 0x1, 0x1, 171, 1843, -1, 28, 1, 152 }, + { 0x1, 0x1, 171, 1844, -1, 28, 1, 152 }, + { 0x1, 0x1, 171, 1845, -1, 28, 1, 148 }, + { 0x1, 0x1, 171, 1846, -1, 28, 1, 149 }, + { 0x1, 0x1, 171, 1847, -1, 28, 1, 150 }, + { 0x1, 0x1, 171, 1848, -1, 28, 1, 151 }, + { 0x1, 0x1, 171, 1849, -1, 28, 1, 151 }, + { 0x1, 0x1, 171, 1850, -1, 28, 1, 147 }, + { 0x1, 0x1, 171, 1851, -1, 28, 1, 153 }, + { 0x1, 0x1, 171, 1852, -1, 28, 1, 154 }, + { 0x1, 0x1, 171, 1853, -1, 28, 1, 155 }, + { 0x1, 0x1, 171, 1854, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1855, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1856, -1, 28, 1, 152 }, + { 0x1, 0x1, 171, 1857, -1, 28, 1, 153 }, + { 0x1, 0x1, 171, 1858, -1, 28, 1, 154 }, + { 0x1, 0x1, 171, 1859, -1, 28, 1, 155 }, + { 0x1, 0x1, 171, 1860, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1861, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1862, -1, 28, 1, 152 }, + { 0x1, 0x1, 171, 1863, -1, 28, 1, 148 }, + { 0x1, 0x1, 171, 1864, -1, 28, 1, 149 }, + { 0x1, 0x1, 171, 1865, -1, 28, 1, 150 }, + { 0x1, 0x1, 171, 1866, -1, 28, 1, 151 }, + { 0x1, 0x1, 171, 1867, -1, 28, 1, 151 }, + { 0x1, 0x1, 171, 1868, -1, 28, 1, 147 }, + { 0x1, 0x1, 171, 1869, -1, 28, 1, 153 }, + { 0x1, 0x1, 171, 1870, -1, 28, 1, 154 }, + { 0x1, 0x1, 171, 1871, -1, 28, 1, 155 }, + { 0x1, 0x1, 171, 1872, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1873, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1874, -1, 28, 1, 152 }, + { 0x1, 0x1, 171, 1875, -1, 28, 1, 153 }, + { 0x1, 0x1, 171, 1876, -1, 28, 1, 154 }, + { 0x1, 0x1, 171, 1877, -1, 28, 1, 155 }, + { 0x1, 0x1, 171, 1878, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1879, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1880, -1, 28, 1, 152 }, + { 0x1, 0x1, 171, 1881, -1, 28, 1, 148 }, + { 0x1, 0x1, 171, 1882, -1, 28, 1, 149 }, + { 0x1, 0x1, 171, 1883, -1, 28, 1, 150 }, + { 0x1, 0x1, 171, 1884, -1, 28, 1, 151 }, + { 0x1, 0x1, 171, 1885, -1, 28, 1, 151 }, + { 0x1, 0x1, 171, 1886, -1, 28, 1, 147 }, + { 0x1, 0x1, 171, 1887, -1, 28, 1, 153 }, + { 0x1, 0x1, 171, 1888, -1, 28, 1, 154 }, + { 0x1, 0x1, 171, 1889, -1, 28, 1, 155 }, + { 0x1, 0x1, 171, 1890, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1891, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1892, -1, 28, 1, 152 }, + { 0x1, 0x1, 171, 1893, -1, 28, 1, 153 }, + { 0x1, 0x1, 171, 1894, -1, 28, 1, 154 }, + { 0x1, 0x1, 171, 1895, -1, 28, 1, 155 }, + { 0x1, 0x1, 171, 1896, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1897, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1898, -1, 28, 1, 152 }, + { 0x1, 0x1, 171, 1899, -1, 28, 1, 148 }, + { 0x1, 0x1, 171, 1900, -1, 28, 1, 149 }, + { 0x1, 0x1, 171, 1901, -1, 28, 1, 150 }, + { 0x1, 0x1, 171, 1902, -1, 28, 1, 151 }, + { 0x1, 0x1, 171, 1903, -1, 28, 1, 151 }, + { 0x1, 0x1, 171, 1904, -1, 28, 1, 147 }, + { 0x1, 0x1, 171, 1905, -1, 28, 1, 153 }, + { 0x1, 0x1, 171, 1906, -1, 28, 1, 154 }, + { 0x1, 0x1, 171, 1907, -1, 28, 1, 155 }, + { 0x1, 0x1, 171, 1908, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1909, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1910, -1, 28, 1, 152 }, + { 0x1, 0x1, 171, 1911, -1, 28, 1, 148 }, + { 0x1, 0x1, 171, 1912, -1, 28, 1, 149 }, + { 0x1, 0x1, 171, 1913, -1, 28, 1, 150 }, + { 0x1, 0x1, 171, 1914, -1, 28, 1, 151 }, + { 0x1, 0x1, 171, 1915, -1, 28, 1, 151 }, + { 0x1, 0x1, 171, 1916, -1, 28, 1, 147 }, + { 0x1, 0x1, 171, 1917, -1, 28, 1, 153 }, + { 0x1, 0x1, 171, 1918, -1, 28, 1, 154 }, + { 0x1, 0x1, 171, 1919, -1, 28, 1, 155 }, + { 0x1, 0x1, 171, 1920, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1921, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1922, -1, 28, 1, 152 }, + { 0x1, 0x1, 171, 1923, -1, 28, 1, 148 }, + { 0x1, 0x1, 171, 1924, -1, 28, 1, 149 }, + { 0x1, 0x1, 171, 1925, -1, 28, 1, 150 }, + { 0x1, 0x1, 171, 1926, -1, 28, 1, 151 }, + { 0x1, 0x1, 171, 1927, -1, 28, 1, 151 }, + { 0x1, 0x1, 171, 1928, -1, 28, 1, 147 }, + { 0x1, 0x1, 171, 1929, -1, 28, 1, 153 }, + { 0x1, 0x1, 171, 1930, -1, 28, 1, 154 }, + { 0x1, 0x1, 171, 1931, -1, 28, 1, 155 }, + { 0x1, 0x1, 171, 1932, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1933, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1934, -1, 28, 1, 152 }, + { 0x1, 0x1, 171, 1935, -1, 28, 1, 148 }, + { 0x1, 0x1, 171, 1936, -1, 28, 1, 149 }, + { 0x1, 0x1, 171, 1937, -1, 28, 1, 150 }, + { 0x1, 0x1, 171, 1938, -1, 28, 1, 151 }, + { 0x1, 0x1, 171, 1939, -1, 28, 1, 151 }, + { 0x1, 0x1, 171, 1940, -1, 28, 1, 147 }, + { 0x1, 0x1, 171, 1941, -1, 28, 1, 153 }, + { 0x1, 0x1, 171, 1942, -1, 28, 1, 154 }, + { 0x1, 0x1, 171, 1943, -1, 28, 1, 155 }, + { 0x1, 0x1, 171, 1944, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1945, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1946, -1, 28, 1, 152 }, + { 0x1, 0x1, 171, 1947, -1, 28, 1, 153 }, + { 0x1, 0x1, 171, 1948, -1, 28, 1, 154 }, + { 0x1, 0x1, 171, 1949, -1, 28, 1, 155 }, + { 0x1, 0x1, 171, 1950, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1951, -1, 28, 1, 156 }, + { 0x1, 0x1, 171, 1952, -1, 28, 1, 152 }, + { 0x1, 0x1, 171, 1691, -1, 28, 1, 158 }, + { 0x1, 0x1, 171, 1692, -1, 28, 1, 158 }, + { 0x1, 0x1, 171, 1693, -1, 28, 1, 158 }, + { 0x1, 0x1, 171, 1694, -1, 28, 1, 158 }, + { 0x1, 0x1, 171, 1695, -1, 28, 1, 159 }, + { 0x1, 0x1, 171, 1696, -1, 28, 1, 159 }, + { 0x1, 0x1, 171, 1697, -1, 28, 1, 159 }, + { 0x1, 0x1, 171, 1698, -1, 28, 1, 159 }, + { 0x1, 0x1, 171, 1699, -1, 28, 1, 159 }, + { 0x1, 0x1, 171, 1700, -1, 28, 1, 159 }, + { 0x1, 0x1, 171, 1701, -1, 28, 1, 159 }, + { 0x1, 0x1, 171, 1702, -1, 28, 1, 159 }, + { 0x1, 0x1, 171, 1997, -1, 28, 1, 143 }, + { 0x1, 0x1, 171, 1998, -1, 28, 1, 143 }, + { 0x1, 0x1, 171, 1999, -1, 28, 1, 143 }, + { 0x1, 0x1, 171, 2000, -1, 28, 1, 143 }, + { 0x1, 0x1, 172, 1953, -1, 29, 1, 158 }, + { 0x1, 0x1, 172, 1954, -1, 29, 1, 158 }, + { 0x1, 0x1, 172, 1955, -1, 29, 1, 158 }, + { 0x1, 0x1, 172, 1956, -1, 29, 1, 158 }, + { 0x1, 0x1, 172, 1957, -1, 29, 1, 159 }, + { 0x1, 0x1, 172, 1958, -1, 29, 1, 159 }, + { 0x1, 0x1, 172, 1959, -1, 29, 1, 159 }, + { 0x1, 0x1, 172, 1960, -1, 29, 1, 159 }, + { 0x1, 0x1, 172, 1961, -1, 29, 1, 159 }, + { 0x1, 0x1, 172, 1962, -1, 29, 1, 159 }, + { 0x1, 0x1, 172, 1963, -1, 29, 1, 159 }, + { 0x1, 0x1, 172, 1964, -1, 29, 1, 159 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 142 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 142 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 142 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 142 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 141 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 141 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 141 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 141 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 141 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 141 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 141 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 141 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 141 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 141 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 141 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 141 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 141 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 141 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 141 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 141 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 143 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 143 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 143 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 143 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 133 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 134 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 135 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 271, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 2258, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 133 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 134 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 135 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 273, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 2259, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 133 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 134 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 135 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 275, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 2260, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 132 }, + { 0x3, 0x3, 173, 277, -1, 28, 1, 132 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 132 }, + { 0x3, 0x3, 173, 278, -1, 28, 1, 132 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 133 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 134 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 135 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 279, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 2261, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 133 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 134 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 135 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 281, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 2262, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 133 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 134 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 135 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 283, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 2263, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 133 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 134 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 135 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 285, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 2264, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 133 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 134 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 135 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 287, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 2265, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 133 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 134 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 135 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 289, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 2266, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 133 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 134 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 135 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 136 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 291, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 2267, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 133 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 134 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 135 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 136 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 293, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 2268, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 133 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 134 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 135 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 136 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 295, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 137 }, + { 0x3, 0x3, 173, 2269, -1, 28, 1, 131 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 147 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 152 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 152 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 148 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 149 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 150 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 151 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 151 }, + { 0x3, 0x3, 173, 2270, -1, 28, 1, 147 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 153 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 154 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 155 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, 2271, -1, 28, 1, 152 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 153 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 154 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 155 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, 2272, -1, 28, 1, 152 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 148 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 149 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 150 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 151 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 151 }, + { 0x3, 0x3, 173, 2273, -1, 28, 1, 147 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 153 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 154 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 155 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, 2274, -1, 28, 1, 152 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 153 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 154 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 155 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, 2275, -1, 28, 1, 152 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 148 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 149 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 150 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 151 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 151 }, + { 0x3, 0x3, 173, 2276, -1, 28, 1, 147 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 153 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 154 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 155 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, 2277, -1, 28, 1, 152 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 153 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 154 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 155 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, 2278, -1, 28, 1, 152 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 148 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 149 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 150 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 151 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 151 }, + { 0x3, 0x3, 173, 2279, -1, 28, 1, 147 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 153 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 154 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 155 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, 2280, -1, 28, 1, 152 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 148 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 149 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 150 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 151 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 151 }, + { 0x3, 0x3, 173, 2281, -1, 28, 1, 147 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 153 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 154 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 155 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, 2282, -1, 28, 1, 152 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 148 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 149 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 150 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 151 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 151 }, + { 0x3, 0x3, 173, 2283, -1, 28, 1, 147 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 153 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 154 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 155 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, 2284, -1, 28, 1, 152 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 148 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 149 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 150 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 151 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 151 }, + { 0x3, 0x3, 173, 2285, -1, 28, 1, 147 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 153 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 154 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 155 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, 2286, -1, 28, 1, 152 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 153 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 154 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 155 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 156 }, + { 0x3, 0x3, 173, 2287, -1, 28, 1, 152 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 158 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 158 }, + { 0x3, 0x3, 173, 951, -1, 28, 1, 158 }, + { 0x3, 0x3, 173, 952, -1, 28, 1, 158 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 159 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 159 }, + { 0x3, 0x3, 173, 953, -1, 28, 1, 159 }, + { 0x3, 0x3, 173, 954, -1, 28, 1, 159 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 159 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 159 }, + { 0x3, 0x3, 173, 955, -1, 28, 1, 159 }, + { 0x3, 0x3, 173, 956, -1, 28, 1, 159 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 138 }, + { 0x3, 0x3, 173, 2224, -1, 28, 1, 138 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 145 }, + { 0x3, 0x3, 173, 2225, -1, 28, 1, 145 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 139 }, + { 0x3, 0x3, 173, 2226, -1, 28, 1, 139 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 139 }, + { 0x3, 0x3, 173, 2227, -1, 28, 1, 139 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 138 }, + { 0x3, 0x3, 173, 2228, -1, 28, 1, 138 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 145 }, + { 0x3, 0x3, 173, 2229, -1, 28, 1, 145 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 138 }, + { 0x3, 0x3, 173, 2230, -1, 28, 1, 138 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 145 }, + { 0x3, 0x3, 173, 2231, -1, 28, 1, 145 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 138 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 140 }, + { 0x3, 0x3, 173, 2232, -1, 28, 1, 138 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 145 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 146 }, + { 0x3, 0x3, 173, 2233, -1, 28, 1, 145 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 157 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 161 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 157 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 161 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 157 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 161 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 157 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 161 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 157 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 161 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 143 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 143 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 143 }, + { 0x3, 0x3, 173, -1, -1, 28, 1, 143 }, + { 0x0, 0x0, 174, -1, 394, 0, 0, -1 }, + { 0x0, 0x0, 174, -1, 396, 0, 0, -1 }, + { 0x0, 0x0, 174, 3042, 3002, 0, 1, 1 }, + { 0x0, 0x0, 174, 3043, 3003, 0, 1, 1 }, + { 0x0, 0x0, 174, -1, 402, 0, 0, -1 }, + { 0x0, 0x0, 174, -1, 404, 0, 0, -1 }, + { 0x0, 0x0, 174, 3046, 3006, 0, 1, 76 }, + { 0x0, 0x0, 174, 3047, 3007, 0, 1, 76 }, + { 0x0, 0x0, 174, -1, 410, 0, 0, -1 }, + { 0x0, 0x0, 174, -1, 412, 0, 0, -1 }, + { 0x0, 0x0, 174, 3050, 3010, 0, 1, 1 }, + { 0x0, 0x0, 174, 3051, 3011, 0, 1, 1 }, + { 0x11, 0x31, 175, 2881, 417, 33, 1, 4 }, + { 0x2200001, 0x2200001, 175, -1, 418, 12, 1, 4 }, + { 0x11, 0x31, 175, 2073, 419, 33, 1, 4 }, + { 0x2200001, 0x2200001, 175, -1, 421, 12, 1, 4 }, + { 0x1, 0x1, 175, -1, 425, 37, 1, 4 }, + { 0x2000001, 0x2000001, 175, -1, 426, 12, 1, 4 }, + { 0x11, 0x11, 175, -1, 427, 33, 1, 4 }, + { 0x2200001, 0x2200001, 175, -1, 428, 12, 1, 4 }, + { 0x1, 0x1, 175, 2079, 429, 37, 1, 4 }, + { 0x2000001, 0x2000001, 175, -1, 431, 12, 1, 4 }, + { 0x11, 0x11, 175, 2081, 433, 33, 1, 4 }, + { 0x2200001, 0x2200001, 175, -1, 435, 12, 1, 4 }, + { 0x1, 0x1, 175, 2083, 437, 37, 1, 4 }, + { 0x2000001, 0x2000001, 175, -1, 439, 12, 1, 4 }, + { 0x11, 0x11, 175, 2085, 441, 33, 1, 4 }, + { 0x2200001, 0x2200001, 175, -1, 443, 12, 1, 4 }, + { 0x1, 0x1, 175, 2087, 445, 37, 1, 4 }, + { 0x2000001, 0x2000001, 175, -1, 447, 12, 1, 4 }, + { 0x11, 0x11, 175, 2089, 449, 33, 1, 4 }, + { 0x2200001, 0x2200001, 175, -1, 451, 12, 1, 4 }, + { 0x11, 0x31, 175, 2901, 457, 33, 1, 4 }, + { 0x2200001, 0x2200001, 175, -1, 458, 12, 1, 4 }, + { 0x11, 0x31, 175, 2095, 459, 33, 1, 4 }, + { 0x2200001, 0x2200001, 175, -1, 461, 12, 1, 4 }, + { 0x11, 0x31, 175, 2921, 465, 33, 1, 4 }, + { 0x2200001, 0x2200001, 175, -1, 466, 12, 1, 4 }, + { 0x11, 0x31, 175, 2121, 467, 33, 1, 4 }, + { 0x2200001, 0x2200001, 175, -1, 469, 12, 1, 4 }, + { 0x1, 0x1, 175, -1, 473, 37, 1, 4 }, + { 0x2000001, 0x2000001, 175, -1, 474, 12, 1, 4 }, + { 0x11, 0x11, 175, -1, 475, 33, 1, 4 }, + { 0x2200001, 0x2200001, 175, -1, 476, 12, 1, 4 }, + { 0x1, 0x1, 175, 2127, 477, 37, 1, 4 }, + { 0x2000001, 0x2000001, 175, -1, 479, 12, 1, 4 }, + { 0x11, 0x11, 175, 2129, 481, 33, 1, 4 }, + { 0x2200001, 0x2200001, 175, -1, 483, 12, 1, 4 }, + { 0x1, 0x1, 175, 2131, 485, 37, 1, 4 }, + { 0x2000001, 0x2000001, 175, -1, 487, 12, 1, 4 }, + { 0x11, 0x11, 175, 2133, 489, 33, 1, 4 }, + { 0x2200001, 0x2200001, 175, -1, 491, 12, 1, 4 }, + { 0x1, 0x1, 175, 2135, 493, 37, 1, 4 }, + { 0x2000001, 0x2000001, 175, -1, 495, 12, 1, 4 }, + { 0x11, 0x11, 175, 2137, 497, 33, 1, 4 }, + { 0x2200001, 0x2200001, 175, -1, 499, 12, 1, 4 }, + { 0x11, 0x31, 175, 2941, 505, 33, 1, 4 }, + { 0x2200001, 0x2200001, 175, -1, 506, 12, 1, 4 }, + { 0x11, 0x31, 175, 2143, 507, 33, 1, 4 }, + { 0x2200001, 0x2200001, 175, -1, 509, 12, 1, 4 }, + { 0x1, 0x1, 175, -1, 513, 33, 1, 4 }, + { 0x200001, 0x200001, 175, -1, 514, 12, 1, 4 }, + { 0x1, 0x1, 175, -1, 515, 33, 1, 4 }, + { 0x200001, 0x200001, 175, -1, 516, 12, 1, 4 }, + { 0x1, 0x1, 175, -1, 521, 33, 1, 79 }, + { 0x200001, 0x200001, 175, -1, 522, 12, 1, 79 }, + { 0x1, 0x1, 175, -1, 523, 33, 1, 79 }, + { 0x200001, 0x200001, 175, -1, 524, 12, 1, 79 }, + { 0x1, 0x1, 175, -1, 529, 33, 1, 4 }, + { 0x200001, 0x200001, 175, -1, 530, 12, 1, 4 }, + { 0x1, 0x1, 175, -1, 531, 33, 1, 4 }, + { 0x200001, 0x200001, 175, -1, 532, 12, 1, 4 }, + { 0x2200001, 0x6200001, 176, 2884, -1, 12, 1, 4 }, + { 0x11, 0x11, 176, 2016, -1, 33, 1, 4 }, + { 0x1, 0x1, 176, -1, -1, 33, 1, 5 }, + { 0x4200001, 0x4200001, 176, -1, -1, 12, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 37, 1, 4 }, + { 0x2000001, 0x2000001, 176, -1, -1, 12, 1, 4 }, + { 0x2000001, 0x2000001, 176, -1, -1, 12, 1, 4 }, + { 0x1, 0x1, 176, 2022, -1, 37, 1, 4 }, + { 0x2200001, 0x2200001, 176, -1, -1, 12, 1, 4 }, + { 0x11, 0x11, 176, 2024, -1, 33, 1, 4 }, + { 0x2000001, 0x2000001, 176, -1, -1, 12, 1, 4 }, + { 0x1, 0x1, 176, 2026, -1, 37, 1, 4 }, + { 0x2200001, 0x2200001, 176, -1, -1, 12, 1, 4 }, + { 0x11, 0x11, 176, 2028, -1, 33, 1, 4 }, + { 0x2000001, 0x2000001, 176, -1, -1, 12, 1, 4 }, + { 0x1, 0x1, 176, 2030, -1, 37, 1, 4 }, + { 0x2200001, 0x2200001, 176, -1, -1, 12, 1, 4 }, + { 0x11, 0x11, 176, 2032, -1, 33, 1, 4 }, + { 0x1, 0x1, 176, -1, -1, 37, 1, 4 }, + { 0x2000001, 0x2000001, 176, -1, -1, 12, 1, 4 }, + { 0x11, 0x11, 176, -1, -1, 33, 1, 4 }, + { 0x2200001, 0x2200001, 176, -1, -1, 12, 1, 4 }, + { 0x2200001, 0x6200001, 176, 2904, -1, 12, 1, 4 }, + { 0x11, 0x11, 176, 2036, -1, 33, 1, 4 }, + { 0x1, 0x1, 176, -1, -1, 33, 1, 5 }, + { 0x4200001, 0x4200001, 176, -1, -1, 12, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 37, 1, 4 }, + { 0x2000001, 0x2000001, 176, -1, -1, 12, 1, 4 }, + { 0x0, 0x0, 176, -1, -1, 0, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 12, 1, 5 }, + { 0x0, 0x0, 176, -1, -1, 0, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 12, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 33, 1, 5 }, + { 0x200001, 0x200001, 176, -1, -1, 12, 1, 5 }, + { 0x0, 0x0, 176, -1, -1, 0, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 12, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 33, 1, 5 }, + { 0x200001, 0x200001, 176, -1, -1, 12, 1, 5 }, + { 0x0, 0x0, 176, -1, -1, 0, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 12, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 33, 1, 5 }, + { 0x200001, 0x200001, 176, -1, -1, 12, 1, 5 }, + { 0x0, 0x0, 176, -1, -1, 0, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 12, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 33, 1, 5 }, + { 0x200001, 0x200001, 176, -1, -1, 12, 1, 5 }, + { 0x0, 0x0, 176, -1, -1, 0, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 12, 1, 5 }, + { 0x2200001, 0x6200001, 176, 2924, -1, 12, 1, 4 }, + { 0x11, 0x11, 176, 2040, -1, 33, 1, 4 }, + { 0x1, 0x1, 176, -1, -1, 33, 1, 5 }, + { 0x4200001, 0x4200001, 176, -1, -1, 12, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 37, 1, 4 }, + { 0x2000001, 0x2000001, 176, -1, -1, 12, 1, 4 }, + { 0x2000001, 0x2000001, 176, -1, -1, 12, 1, 4 }, + { 0x1, 0x1, 176, 2046, -1, 37, 1, 4 }, + { 0x2200001, 0x2200001, 176, -1, -1, 12, 1, 4 }, + { 0x11, 0x11, 176, 2048, -1, 33, 1, 4 }, + { 0x2000001, 0x2000001, 176, -1, -1, 12, 1, 4 }, + { 0x1, 0x1, 176, 2050, -1, 37, 1, 4 }, + { 0x2200001, 0x2200001, 176, -1, -1, 12, 1, 4 }, + { 0x11, 0x11, 176, 2052, -1, 33, 1, 4 }, + { 0x2000001, 0x2000001, 176, -1, -1, 12, 1, 4 }, + { 0x1, 0x1, 176, 2054, -1, 37, 1, 4 }, + { 0x2200001, 0x2200001, 176, -1, -1, 12, 1, 4 }, + { 0x11, 0x11, 176, 2056, -1, 33, 1, 4 }, + { 0x1, 0x1, 176, -1, -1, 37, 1, 4 }, + { 0x2000001, 0x2000001, 176, -1, -1, 12, 1, 4 }, + { 0x11, 0x11, 176, -1, -1, 33, 1, 4 }, + { 0x2200001, 0x2200001, 176, -1, -1, 12, 1, 4 }, + { 0x2200001, 0x6200001, 176, 2943, -1, 12, 1, 4 }, + { 0x11, 0x11, 176, 2060, -1, 33, 1, 4 }, + { 0x1, 0x1, 176, -1, -1, 33, 1, 5 }, + { 0x4200001, 0x4200001, 176, -1, -1, 12, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 37, 1, 4 }, + { 0x2000001, 0x2000001, 176, -1, -1, 12, 1, 4 }, + { 0x0, 0x0, 176, -1, -1, 0, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 12, 1, 5 }, + { 0x0, 0x0, 176, -1, -1, 0, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 12, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 33, 1, 5 }, + { 0x200001, 0x200001, 176, -1, -1, 12, 1, 5 }, + { 0x0, 0x0, 176, -1, -1, 0, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 12, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 33, 1, 5 }, + { 0x200001, 0x200001, 176, -1, -1, 12, 1, 5 }, + { 0x0, 0x0, 176, -1, -1, 0, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 12, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 33, 1, 5 }, + { 0x200001, 0x200001, 176, -1, -1, 12, 1, 5 }, + { 0x0, 0x0, 176, -1, -1, 0, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 12, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 33, 1, 5 }, + { 0x200001, 0x200001, 176, -1, -1, 12, 1, 5 }, + { 0x0, 0x0, 176, -1, -1, 0, 1, 5 }, + { 0x1, 0x1, 176, -1, -1, 12, 1, 5 }, + { 0x9, 0x9, 176, -1, -1, 33, 1, 5 }, + { 0x1, 0x1, 176, 397, -1, 33, 1, 4 }, + { 0x1200001, 0x1200001, 176, -1, -1, 12, 1, 5 }, + { 0x200001, 0x200001, 176, 398, -1, 12, 1, 4 }, + { 0x9, 0x9, 176, -1, -1, 33, 1, 5 }, + { 0x1, 0x1, 176, 399, -1, 33, 1, 4 }, + { 0x1200001, 0x1200001, 176, -1, -1, 12, 1, 5 }, + { 0x200001, 0x200001, 176, 400, -1, 12, 1, 4 }, + { 0x9, 0x9, 176, -1, -1, 33, 1, 80 }, + { 0x1, 0x1, 176, 405, -1, 33, 1, 79 }, + { 0x1200001, 0x1200001, 176, -1, -1, 12, 1, 80 }, + { 0x200001, 0x200001, 176, 406, -1, 12, 1, 79 }, + { 0x9, 0x9, 176, -1, -1, 33, 1, 80 }, + { 0x1, 0x1, 176, 407, -1, 33, 1, 79 }, + { 0x1200001, 0x1200001, 176, -1, -1, 12, 1, 80 }, + { 0x200001, 0x200001, 176, 408, -1, 12, 1, 79 }, + { 0x9, 0x9, 176, -1, -1, 33, 1, 5 }, + { 0x1, 0x1, 176, 413, -1, 33, 1, 4 }, + { 0x1200001, 0x1200001, 176, -1, -1, 12, 1, 5 }, + { 0x200001, 0x200001, 176, 414, -1, 12, 1, 4 }, + { 0x9, 0x9, 176, -1, -1, 33, 1, 5 }, + { 0x1, 0x1, 176, 415, -1, 33, 1, 4 }, + { 0x1200001, 0x1200001, 176, -1, -1, 12, 1, 5 }, + { 0x200001, 0x200001, 176, 416, -1, 12, 1, 4 }, + { 0x0, 0x0, 177, -1, 2327, 0, 0, -1 }, + { 0x9, 0x9, 177, -1, 2335, 33, 1, 50 }, + { 0x9, 0x9, 177, -1, 2993, 33, 1, 50 }, + { 0x0, 0x0, 177, -1, 2380, 0, 0, -1 }, + { 0x7, 0x7, 177, -1, -1, 27, 1, 50 }, + { 0x1, 0x1, 197, -1, -1, 27, 1, 10 }, + { 0x1, 0x1, 211, -1, -1, 29, 1, 0 }, + { 0x1, 0x1, 211, -1, -1, 29, 1, 0 }, + { 0x2, 0x3, 211, 1169, -1, 27, 1, 34 }, + { 0x0, 0x0, 211, 1170, -1, 0, 1, 34 }, + { 0x0, 0x0, 211, 1171, -1, 0, 1, 0 }, + { 0x0, 0x0, 211, 1172, -1, 0, 1, 0 }, + { 0x0, 0x0, 211, 1173, -1, 0, 1, 0 }, + { 0x0, 0x0, 211, 1174, -1, 0, 1, 0 }, + { 0x0, 0x0, 211, 3026, -1, 0, 1, 100 }, + { 0x0, 0x0, 211, 3027, -1, 0, 1, 100 }, + { 0x0, 0x0, 211, 3028, 967, 0, 0, -1 }, + { 0x1, 0x1, 212, -1, -1, 27, 1, 0 }, + { 0x1, 0x1, 212, -1, -1, 27, 1, 0 }, + { 0x1, 0x1, 213, -1, 1426, 32, 1, 142 }, + { 0x1, 0x1, 213, -1, 1428, 32, 1, 142 }, + { 0x1, 0x1, 213, -1, 1430, 32, 1, 141 }, + { 0x1, 0x1, 213, -1, 1432, 32, 1, 141 }, + { 0x1, 0x1, 213, -1, 1434, 32, 1, 141 }, + { 0x1, 0x1, 213, -1, 1436, 32, 1, 141 }, + { 0x1, 0x1, 213, -1, 1438, 32, 1, 141 }, + { 0x1, 0x1, 213, -1, 1440, 32, 1, 141 }, + { 0x1, 0x1, 213, -1, 1442, 32, 1, 141 }, + { 0x1, 0x1, 213, -1, 1444, 32, 1, 141 }, + { 0x1, 0x1, 213, -1, 1446, 32, 1, 143 }, + { 0x1, 0x1, 213, -1, 1448, 32, 1, 143 }, + { 0x1, 0x1, 213, -1, 1965, 32, 1, 138 }, + { 0x1, 0x1, 213, -1, 1967, 32, 1, 145 }, + { 0x1, 0x1, 213, -1, 1969, 32, 1, 139 }, + { 0x1, 0x1, 213, -1, 1971, 32, 1, 139 }, + { 0x1, 0x1, 213, -1, 1973, 32, 1, 138 }, + { 0x1, 0x1, 213, -1, 1975, 32, 1, 145 }, + { 0x1, 0x1, 213, -1, 1977, 32, 1, 138 }, + { 0x1, 0x1, 213, -1, 1979, 32, 1, 145 }, + { 0x1, 0x1, 213, 2783, 1981, 32, 1, 138 }, + { 0x1, 0x1, 213, 2784, 1984, 32, 1, 145 }, + { 0x0, 0x0, 214, -1, 2825, 0, 0, -1 }, + { 0x0, 0x0, 214, -1, 2826, 0, 0, -1 }, + { 0x0, 0x0, 214, -1, 2851, 0, 0, -1 }, + { 0x5, 0x5, 214, -1, 2854, 20, 1, 68 }, + { 0x0, 0x0, 218, 2209, 966, 0, 0, -1 }, + { 0x0, 0x0, 219, -1, 1139, 0, 0, -1 }, + { 0x0, 0x0, 219, -1, 1264, 0, 0, -1 }, + { 0x0, 0x0, 219, -1, -1, 0, 1, 128 }, + { 0x0, 0x0, 219, -1, -1, 0, 1, 67 }, + { 0x1, 0x1, 219, 833, 2289, 36, 1, 66 }, + { 0x1, 0x1, 219, 834, 2348, 36, 1, 66 }, + { 0x0, 0x0, 219, 835, 2351, 0, 0, -1 }, + { 0x1, 0x1, 219, 836, -1, 36, 1, 66 }, + { 0x0, 0x0, 219, 1423, -1, 0, 1, 34 }, + { 0x1, 0x1, 219, 837, 2356, 36, 1, 66 }, + { 0x0, 0x0, 219, 838, 2359, 0, 0, -1 }, + { 0x1, 0x1, 219, 839, -1, 36, 1, 66 }, + { 0x0, 0x0, 219, 840, 2362, 0, 0, -1 }, + { 0x1, 0x1, 219, 841, -1, 36, 1, 66 }, + { 0x1, 0x1, 219, 842, 2365, 36, 1, 66 }, + { 0x1, 0x1, 219, 843, 2368, 36, 1, 66 }, + { 0x0, 0x0, 219, 1424, -1, 0, 1, 34 }, + { 0x1, 0x1, 219, 844, 2401, 36, 1, 66 }, + { 0x1, 0x1, 219, 845, -1, 31, 1, 144 }, + { 0x1, 0x1, 219, 228, 1449, 32, 1, 133 }, + { 0x1, 0x1, 219, 229, 1458, 32, 1, 133 }, + { 0x1, 0x1, 219, 230, 1467, 32, 1, 133 }, + { 0x1, 0x1, 219, 231, 1480, 32, 1, 133 }, + { 0x1, 0x1, 219, 232, 1489, 32, 1, 133 }, + { 0x1, 0x1, 219, 233, 1498, 32, 1, 133 }, + { 0x1, 0x1, 219, 234, 1507, 32, 1, 133 }, + { 0x1, 0x1, 219, 235, 1516, 32, 1, 133 }, + { 0x1, 0x1, 219, 236, 1525, 32, 1, 133 }, + { 0x1, 0x1, 219, 237, 1534, 32, 1, 133 }, + { 0x1, 0x1, 219, 238, 1544, 32, 1, 133 }, + { 0x1, 0x1, 219, 239, 1554, 32, 1, 133 }, + { 0x1, 0x1, 219, 240, 1567, 32, 1, 148 }, + { 0x1, 0x1, 219, 241, 1573, 32, 1, 153 }, + { 0x1, 0x1, 219, 242, 1579, 32, 1, 153 }, + { 0x1, 0x1, 219, 243, 1585, 32, 1, 148 }, + { 0x1, 0x1, 219, 244, 1591, 32, 1, 153 }, + { 0x1, 0x1, 219, 245, 1597, 32, 1, 153 }, + { 0x1, 0x1, 219, 246, 1603, 32, 1, 148 }, + { 0x1, 0x1, 219, 247, 1609, 32, 1, 153 }, + { 0x1, 0x1, 219, 248, 1615, 32, 1, 153 }, + { 0x1, 0x1, 219, 249, 1621, 32, 1, 148 }, + { 0x1, 0x1, 219, 250, 1627, 32, 1, 153 }, + { 0x1, 0x1, 219, 251, 1633, 32, 1, 148 }, + { 0x1, 0x1, 219, 252, 1639, 32, 1, 153 }, + { 0x1, 0x1, 219, 253, 1645, 32, 1, 148 }, + { 0x1, 0x1, 219, 254, 1651, 32, 1, 153 }, + { 0x1, 0x1, 219, 255, 1657, 32, 1, 148 }, + { 0x1, 0x1, 219, 256, 1663, 32, 1, 153 }, + { 0x1, 0x1, 219, 257, 1669, 32, 1, 153 }, + { 0x1, 0x1, 219, 849, -1, 31, 1, 160 }, + { 0x0, 0x0, 220, 2404, -1, 0, 1, 66 }, + { 0x0, 0x0, 220, 2405, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 25, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 2407, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 2408, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 2409, -1, 0, 1, 45 }, + { 0x0, 0x0, 220, 2410, -1, 0, 1, 40 }, + { 0x1, 0x1, 220, 2411, -1, 12, 1, 59 }, + { 0x0, 0x0, 220, 2412, -1, 0, 1, 54 }, + { 0x1000001, 0x1000001, 220, 2413, -1, 12, 1, 59 }, + { 0x1, 0x1, 220, 2414, -1, 36, 1, 54 }, + { 0x200001, 0x200001, 220, 2415, -1, 12, 1, 59 }, + { 0x1, 0x1, 220, 2416, -1, 33, 1, 54 }, + { 0x1200001, 0x1200001, 220, 2417, -1, 12, 1, 49 }, + { 0x9, 0x9, 220, 2418, -1, 33, 1, 49 }, + { 0x0, 0x0, 220, 2419, -1, 0, 1, 59 }, + { 0x0, 0x0, 220, 2420, -1, 0, 1, 54 }, + { 0x0, 0x0, 220, 2421, -1, 0, 1, 59 }, + { 0x0, 0x0, 220, 2422, -1, 0, 1, 54 }, + { 0x0, 0x0, 220, 2423, -1, 0, 1, 59 }, + { 0x0, 0x0, 220, 2424, -1, 0, 1, 54 }, + { 0x0, 0x0, 220, 2425, -1, 0, 1, 49 }, + { 0x0, 0x0, 220, 2426, -1, 0, 1, 49 }, + { 0x1, 0x1, 220, 2427, -1, 12, 1, 59 }, + { 0x0, 0x0, 220, 2428, -1, 0, 1, 54 }, + { 0x200001, 0x1200001, 220, 2429, -1, 12, 1, 59 }, + { 0x1, 0x9, 220, 2430, -1, 33, 1, 54 }, + { 0x0, 0x0, 220, 2431, -1, 0, 1, 59 }, + { 0x0, 0x0, 220, 2432, -1, 0, 1, 54 }, + { 0x0, 0x0, 220, 2433, -1, 0, 1, 59 }, + { 0x0, 0x0, 220, 2434, -1, 0, 1, 54 }, + { 0x1, 0x1, 220, 2435, -1, 12, 1, 59 }, + { 0x0, 0x0, 220, 2436, -1, 0, 1, 54 }, + { 0x1000001, 0x1000001, 220, 2437, -1, 12, 1, 59 }, + { 0x1, 0x1, 220, 2438, -1, 36, 1, 54 }, + { 0x200001, 0x200001, 220, 2439, -1, 12, 1, 59 }, + { 0x1, 0x1, 220, 2440, -1, 33, 1, 54 }, + { 0x1200001, 0x1200001, 220, 2441, -1, 12, 1, 49 }, + { 0x9, 0x9, 220, 2442, -1, 33, 1, 49 }, + { 0x0, 0x0, 220, 2443, -1, 0, 1, 59 }, + { 0x0, 0x0, 220, 2444, -1, 0, 1, 54 }, + { 0x0, 0x0, 220, 2445, -1, 0, 1, 59 }, + { 0x0, 0x0, 220, 2446, -1, 0, 1, 54 }, + { 0x0, 0x0, 220, 2447, -1, 0, 1, 59 }, + { 0x0, 0x0, 220, 2448, -1, 0, 1, 54 }, + { 0x0, 0x0, 220, 2449, -1, 0, 1, 49 }, + { 0x0, 0x0, 220, 2450, -1, 0, 1, 49 }, + { 0x1, 0x1, 220, 2451, -1, 12, 1, 59 }, + { 0x0, 0x0, 220, 2452, -1, 0, 1, 54 }, + { 0x200001, 0x1200001, 220, 2453, -1, 12, 1, 59 }, + { 0x1, 0x9, 220, 2454, -1, 33, 1, 54 }, + { 0x0, 0x0, 220, 2455, -1, 0, 1, 59 }, + { 0x0, 0x0, 220, 2456, -1, 0, 1, 54 }, + { 0x0, 0x0, 220, 2457, -1, 0, 1, 59 }, + { 0x0, 0x0, 220, 2458, -1, 0, 1, 54 }, + { 0x1, 0x1, 220, 2459, -1, 28, 1, 29 }, + { 0x0, 0x0, 220, 2460, -1, 0, 1, 29 }, + { 0x3, 0x3, 220, 2461, -1, 27, 1, 29 }, + { 0x1, 0x1, 220, 2462, -1, 27, 1, 29 }, + { 0x0, 0x0, 220, 2463, -1, 0, 1, 66 }, + { 0x0, 0x0, 220, 2464, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 2465, -1, 0, 1, 29 }, + { 0x1, 0x1, 220, 2466, -1, 36, 1, 66 }, + { 0x1, 0x1, 220, 2467, -1, 37, 1, 29 }, + { 0x0, 0x0, 220, 2468, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 2469, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 2470, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 2471, -1, 0, 1, 66 }, + { 0x0, 0x0, 220, 2472, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 37, -1, 0, 1, 29 }, + { 0x1, 0x1, 220, 2474, -1, 36, 1, 66 }, + { 0x1, 0x1, 220, 2475, -1, 37, 1, 29 }, + { 0x0, 0x0, 220, 2476, -1, 0, 1, 29 }, + { 0x1, 0x1, 220, 2477, -1, 36, 1, 66 }, + { 0x1, 0x1, 220, 2478, -1, 37, 1, 29 }, + { 0x0, 0x0, 220, 2479, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 2480, -1, 0, 1, 66 }, + { 0x0, 0x0, 220, 2481, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 42, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 2483, -1, 0, 1, 66 }, + { 0x0, 0x0, 220, 2484, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 43, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 2486, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 2487, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 2488, -1, 0, 1, 49 }, + { 0x1, 0x1, 220, 2489, -1, 27, 1, 49 }, + { 0x1, 0x1, 220, 2490, -1, 28, 1, 49 }, + { 0x3, 0x3, 220, 2491, -1, 27, 1, 49 }, + { 0x1, 0x1, 220, 2492, -1, 29, 1, 49 }, + { 0x5, 0x5, 220, 2493, -1, 27, 1, 49 }, + { 0x3, 0x3, 220, 2494, -1, 28, 1, 49 }, + { 0x7, 0x7, 220, 2495, -1, 27, 1, 49 }, + { 0x0, 0x0, 220, 2496, -1, 0, 1, 49 }, + { 0x0, 0x0, 220, 2497, -1, 0, 1, 49 }, + { 0x0, 0x0, 220, 2498, -1, 0, 1, 49 }, + { 0x0, 0x0, 220, 2499, -1, 0, 1, 49 }, + { 0x1, 0x1, 220, 2500, -1, 28, 1, 29 }, + { 0x0, 0x0, 220, 2501, -1, 0, 1, 29 }, + { 0x3, 0x3, 220, 2502, -1, 27, 1, 29 }, + { 0x1, 0x1, 220, 2503, -1, 27, 1, 29 }, + { 0x0, 0x0, 220, 2504, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 2505, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 2506, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 52, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 2508, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 2509, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 57, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 2511, -1, 0, 1, 24 }, + { 0x0, 0x0, 220, 2512, -1, 0, 1, 24 }, + { 0x0, 0x0, 220, 2513, -1, 0, 1, 24 }, + { 0x0, 0x0, 220, 2514, -1, 0, 1, 24 }, + { 0x0, 0x0, 220, 2515, -1, 0, 1, 35 }, + { 0x0, 0x0, 220, 2516, -1, 0, 1, 66 }, + { 0x0, 0x0, 220, 2517, -1, 0, 1, 29 }, + { 0x0, 0x0, 220, 64, -1, 0, 1, 29 }, + { 0x1, 0x1, 221, 2519, -1, 34, 1, 66 }, + { 0x1, 0x1, 221, 2520, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2521, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2522, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2523, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2524, -1, 34, 1, 46 }, + { 0x1, 0x1, 221, 2525, -1, 34, 1, 42 }, + { 0x400001, 0x400001, 221, 2526, -1, 12, 1, 61 }, + { 0x1, 0x1, 221, 2527, -1, 34, 1, 56 }, + { 0x1400001, 0x1400001, 221, 2528, -1, 12, 1, 61 }, + { 0x5, 0x5, 221, 2529, -1, 34, 1, 56 }, + { 0x600001, 0x600001, 221, 2530, -1, 12, 1, 61 }, + { 0x3, 0x3, 221, 2531, -1, 33, 1, 56 }, + { 0x1600001, 0x1600001, 221, 2532, -1, 12, 1, 51 }, + { 0xb, 0xb, 221, 2533, -1, 33, 1, 51 }, + { 0x1, 0x1, 221, 2534, -1, 34, 1, 61 }, + { 0x1, 0x1, 221, 2535, -1, 34, 1, 56 }, + { 0x1, 0x1, 221, 2536, -1, 34, 1, 61 }, + { 0x1, 0x1, 221, 2537, -1, 34, 1, 56 }, + { 0x1, 0x1, 221, 2538, -1, 34, 1, 61 }, + { 0x1, 0x1, 221, 2539, -1, 34, 1, 56 }, + { 0x1, 0x1, 221, 2540, -1, 34, 1, 51 }, + { 0x1, 0x1, 221, 2541, -1, 34, 1, 51 }, + { 0x400001, 0x400001, 221, 2542, -1, 12, 1, 61 }, + { 0x1, 0x1, 221, 2543, -1, 34, 1, 56 }, + { 0x600001, 0x1600001, 221, 2544, -1, 12, 1, 61 }, + { 0x3, 0xb, 221, 2545, -1, 33, 1, 56 }, + { 0x1, 0x1, 221, 2546, -1, 34, 1, 61 }, + { 0x1, 0x1, 221, 2547, -1, 34, 1, 56 }, + { 0x1, 0x1, 221, 2548, -1, 34, 1, 61 }, + { 0x1, 0x1, 221, 2549, -1, 34, 1, 56 }, + { 0x400001, 0x400001, 221, 2550, -1, 12, 1, 61 }, + { 0x1, 0x1, 221, 2551, -1, 34, 1, 56 }, + { 0x1400001, 0x1400001, 221, 2552, -1, 12, 1, 61 }, + { 0x5, 0x5, 221, 2553, -1, 34, 1, 56 }, + { 0x600001, 0x600001, 221, 2554, -1, 12, 1, 61 }, + { 0x3, 0x3, 221, 2555, -1, 33, 1, 56 }, + { 0x1600001, 0x1600001, 221, 2556, -1, 12, 1, 51 }, + { 0xb, 0xb, 221, 2557, -1, 33, 1, 51 }, + { 0x1, 0x1, 221, 2558, -1, 34, 1, 61 }, + { 0x1, 0x1, 221, 2559, -1, 34, 1, 56 }, + { 0x1, 0x1, 221, 2560, -1, 34, 1, 61 }, + { 0x1, 0x1, 221, 2561, -1, 34, 1, 56 }, + { 0x1, 0x1, 221, 2562, -1, 34, 1, 61 }, + { 0x1, 0x1, 221, 2563, -1, 34, 1, 56 }, + { 0x1, 0x1, 221, 2564, -1, 34, 1, 51 }, + { 0x1, 0x1, 221, 2565, -1, 34, 1, 51 }, + { 0x400001, 0x400001, 221, 2566, -1, 12, 1, 61 }, + { 0x1, 0x1, 221, 2567, -1, 34, 1, 56 }, + { 0x600001, 0x1600001, 221, 2568, -1, 12, 1, 61 }, + { 0x3, 0xb, 221, 2569, -1, 33, 1, 56 }, + { 0x1, 0x1, 221, 2570, -1, 34, 1, 61 }, + { 0x1, 0x1, 221, 2571, -1, 34, 1, 56 }, + { 0x1, 0x1, 221, 2572, -1, 34, 1, 61 }, + { 0x1, 0x1, 221, 2573, -1, 34, 1, 56 }, + { 0x41, 0x41, 221, 2574, -1, 28, 1, 31 }, + { 0x1, 0x1, 221, 2575, -1, 34, 1, 31 }, + { 0x83, 0x83, 221, 2576, -1, 27, 1, 31 }, + { 0x81, 0x81, 221, 2577, -1, 27, 1, 31 }, + { 0x1, 0x1, 221, 2578, -1, 34, 1, 66 }, + { 0x1, 0x1, 221, 2579, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2580, -1, 34, 1, 31 }, + { 0x5, 0x5, 221, 2581, -1, 34, 1, 66 }, + { 0x9, 0x9, 221, 2582, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2583, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2584, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2585, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2586, -1, 34, 1, 66 }, + { 0x1, 0x1, 221, 2587, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2588, -1, 34, 1, 31 }, + { 0x5, 0x5, 221, 2589, -1, 34, 1, 66 }, + { 0x9, 0x9, 221, 2590, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2591, -1, 34, 1, 31 }, + { 0x5, 0x5, 221, 2592, -1, 34, 1, 66 }, + { 0x9, 0x9, 221, 2593, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2594, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2595, -1, 34, 1, 66 }, + { 0x1, 0x1, 221, 2596, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2597, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2598, -1, 34, 1, 66 }, + { 0x1, 0x1, 221, 2599, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2600, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2601, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2602, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2603, -1, 34, 1, 51 }, + { 0x81, 0x81, 221, 2604, -1, 27, 1, 51 }, + { 0x41, 0x41, 221, 2605, -1, 28, 1, 51 }, + { 0x83, 0x83, 221, 2606, -1, 27, 1, 51 }, + { 0x21, 0x21, 221, 2607, -1, 29, 1, 51 }, + { 0x85, 0x85, 221, 2608, -1, 27, 1, 51 }, + { 0x43, 0x43, 221, 2609, -1, 28, 1, 51 }, + { 0x87, 0x87, 221, 2610, -1, 27, 1, 51 }, + { 0x1, 0x1, 221, 2611, -1, 34, 1, 51 }, + { 0x1, 0x1, 221, 2612, -1, 34, 1, 51 }, + { 0x1, 0x1, 221, 2613, -1, 34, 1, 51 }, + { 0x1, 0x1, 221, 2614, -1, 34, 1, 51 }, + { 0x41, 0x41, 221, 2615, -1, 28, 1, 31 }, + { 0x1, 0x1, 221, 2616, -1, 34, 1, 31 }, + { 0x83, 0x83, 221, 2617, -1, 27, 1, 31 }, + { 0x81, 0x81, 221, 2618, -1, 27, 1, 31 }, + { 0x1, 0x1, 221, 2619, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2620, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2621, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2622, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2623, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2624, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2625, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2626, -1, 34, 1, 26 }, + { 0x1, 0x1, 221, 2627, -1, 34, 1, 26 }, + { 0x1, 0x1, 221, 2628, -1, 34, 1, 26 }, + { 0x1, 0x1, 221, 2629, -1, 34, 1, 26 }, + { 0x1, 0x1, 221, 2630, -1, 34, 1, 37 }, + { 0x1, 0x1, 221, 2631, -1, 34, 1, 66 }, + { 0x1, 0x1, 221, 2632, -1, 34, 1, 31 }, + { 0x1, 0x1, 221, 2633, -1, 34, 1, 31 }, + { 0x1, 0x1, 222, 2634, -1, 35, 1, 66 }, + { 0x1, 0x1, 222, 2635, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2636, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2637, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2638, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2639, -1, 35, 1, 47 }, + { 0x1, 0x1, 222, 2640, -1, 35, 1, 43 }, + { 0x800001, 0x800001, 222, 2641, -1, 12, 1, 62 }, + { 0x1, 0x1, 222, 2642, -1, 35, 1, 57 }, + { 0x1800001, 0x1800001, 222, 2643, -1, 12, 1, 62 }, + { 0x3, 0x3, 222, 2644, -1, 35, 1, 57 }, + { 0xa00001, 0xa00001, 222, 2645, -1, 12, 1, 62 }, + { 0x5, 0x5, 222, 2646, -1, 33, 1, 57 }, + { 0x1a00001, 0x1a00001, 222, 2647, -1, 12, 1, 52 }, + { 0xd, 0xd, 222, 2648, -1, 33, 1, 52 }, + { 0x1, 0x1, 222, 2649, -1, 35, 1, 62 }, + { 0x1, 0x1, 222, 2650, -1, 35, 1, 57 }, + { 0x1, 0x1, 222, 2651, -1, 35, 1, 62 }, + { 0x1, 0x1, 222, 2652, -1, 35, 1, 57 }, + { 0x1, 0x1, 222, 2653, -1, 35, 1, 62 }, + { 0x1, 0x1, 222, 2654, -1, 35, 1, 57 }, + { 0x1, 0x1, 222, 2655, -1, 35, 1, 52 }, + { 0x1, 0x1, 222, 2656, -1, 35, 1, 52 }, + { 0x800001, 0x800001, 222, 2657, -1, 12, 1, 62 }, + { 0x1, 0x1, 222, 2658, -1, 35, 1, 57 }, + { 0xa00001, 0x1a00001, 222, 2659, -1, 12, 1, 62 }, + { 0x5, 0xd, 222, 2660, -1, 33, 1, 57 }, + { 0x1, 0x1, 222, 2661, -1, 35, 1, 62 }, + { 0x1, 0x1, 222, 2662, -1, 35, 1, 57 }, + { 0x1, 0x1, 222, 2663, -1, 35, 1, 62 }, + { 0x1, 0x1, 222, 2664, -1, 35, 1, 57 }, + { 0x800001, 0x800001, 222, 2665, -1, 12, 1, 62 }, + { 0x1, 0x1, 222, 2666, -1, 35, 1, 57 }, + { 0x1800001, 0x1800001, 222, 2667, -1, 12, 1, 62 }, + { 0x3, 0x3, 222, 2668, -1, 35, 1, 57 }, + { 0xa00001, 0xa00001, 222, 2669, -1, 12, 1, 62 }, + { 0x5, 0x5, 222, 2670, -1, 33, 1, 57 }, + { 0x1a00001, 0x1a00001, 222, 2671, -1, 12, 1, 52 }, + { 0xd, 0xd, 222, 2672, -1, 33, 1, 52 }, + { 0x1, 0x1, 222, 2673, -1, 35, 1, 62 }, + { 0x1, 0x1, 222, 2674, -1, 35, 1, 57 }, + { 0x1, 0x1, 222, 2675, -1, 35, 1, 62 }, + { 0x1, 0x1, 222, 2676, -1, 35, 1, 57 }, + { 0x1, 0x1, 222, 2677, -1, 35, 1, 62 }, + { 0x1, 0x1, 222, 2678, -1, 35, 1, 57 }, + { 0x1, 0x1, 222, 2679, -1, 35, 1, 52 }, + { 0x1, 0x1, 222, 2680, -1, 35, 1, 52 }, + { 0x800001, 0x800001, 222, 2681, -1, 12, 1, 62 }, + { 0x1, 0x1, 222, 2682, -1, 35, 1, 57 }, + { 0xa00001, 0x1a00001, 222, 2683, -1, 12, 1, 62 }, + { 0x5, 0xd, 222, 2684, -1, 33, 1, 57 }, + { 0x1, 0x1, 222, 2685, -1, 35, 1, 62 }, + { 0x1, 0x1, 222, 2686, -1, 35, 1, 57 }, + { 0x1, 0x1, 222, 2687, -1, 35, 1, 62 }, + { 0x1, 0x1, 222, 2688, -1, 35, 1, 57 }, + { 0x81, 0x81, 222, 2689, -1, 28, 1, 32 }, + { 0x1, 0x1, 222, 2690, -1, 35, 1, 32 }, + { 0x103, 0x103, 222, 2691, -1, 27, 1, 32 }, + { 0x101, 0x101, 222, 2692, -1, 27, 1, 32 }, + { 0x1, 0x1, 222, 2693, -1, 35, 1, 66 }, + { 0x1, 0x1, 222, 2694, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2695, -1, 35, 1, 32 }, + { 0x3, 0x3, 222, 2696, -1, 35, 1, 66 }, + { 0x5, 0x5, 222, 2697, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2698, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2699, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2700, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2701, -1, 35, 1, 66 }, + { 0x1, 0x1, 222, 2702, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2703, -1, 35, 1, 32 }, + { 0x3, 0x3, 222, 2704, -1, 35, 1, 66 }, + { 0x5, 0x5, 222, 2705, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2706, -1, 35, 1, 32 }, + { 0x3, 0x3, 222, 2707, -1, 35, 1, 66 }, + { 0x5, 0x5, 222, 2708, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2709, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2710, -1, 35, 1, 66 }, + { 0x1, 0x1, 222, 2711, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2712, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2713, -1, 35, 1, 66 }, + { 0x1, 0x1, 222, 2714, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2715, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2716, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2717, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2718, -1, 35, 1, 52 }, + { 0x101, 0x101, 222, 2719, -1, 27, 1, 52 }, + { 0x81, 0x81, 222, 2720, -1, 28, 1, 52 }, + { 0x103, 0x103, 222, 2721, -1, 27, 1, 52 }, + { 0x41, 0x41, 222, 2722, -1, 29, 1, 52 }, + { 0x105, 0x105, 222, 2723, -1, 27, 1, 52 }, + { 0x83, 0x83, 222, 2724, -1, 28, 1, 52 }, + { 0x107, 0x107, 222, 2725, -1, 27, 1, 52 }, + { 0x1, 0x1, 222, 2726, -1, 35, 1, 52 }, + { 0x1, 0x1, 222, 2727, -1, 35, 1, 52 }, + { 0x1, 0x1, 222, 2728, -1, 35, 1, 52 }, + { 0x1, 0x1, 222, 2729, -1, 35, 1, 52 }, + { 0x81, 0x81, 222, 2730, -1, 28, 1, 32 }, + { 0x1, 0x1, 222, 2731, -1, 35, 1, 32 }, + { 0x103, 0x103, 222, 2732, -1, 27, 1, 32 }, + { 0x101, 0x101, 222, 2733, -1, 27, 1, 32 }, + { 0x1, 0x1, 222, 2734, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2735, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2736, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2737, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2738, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2739, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2740, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2741, -1, 35, 1, 27 }, + { 0x1, 0x1, 222, 2742, -1, 35, 1, 27 }, + { 0x1, 0x1, 222, 2743, -1, 35, 1, 27 }, + { 0x1, 0x1, 222, 2744, -1, 35, 1, 27 }, + { 0x1, 0x1, 222, 2745, -1, 35, 1, 38 }, + { 0x1, 0x1, 222, 2746, -1, 35, 1, 66 }, + { 0x1, 0x1, 222, 2747, -1, 35, 1, 32 }, + { 0x1, 0x1, 222, 2748, -1, 35, 1, 32 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 66 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, 2243, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 48 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 44 }, + { 0xc00001, 0xc00001, 223, -1, -1, 12, 1, 63 }, + { 0x3, 0x3, 223, 2964, -1, 34, 1, 58 }, + { 0x1c00001, 0x1c00001, 223, -1, -1, 12, 1, 63 }, + { 0x7, 0x7, 223, 2965, -1, 34, 1, 58 }, + { 0xe00001, 0xe00001, 223, -1, -1, 12, 1, 63 }, + { 0x7, 0x7, 223, 2966, -1, 33, 1, 58 }, + { 0x1e00001, 0x1e00001, 223, -1, -1, 12, 1, 53 }, + { 0xf, 0xf, 223, 2967, -1, 33, 1, 53 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 63 }, + { 0x3, 0x3, 223, 2968, -1, 34, 1, 58 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 63 }, + { 0x3, 0x3, 223, 2969, -1, 34, 1, 58 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 63 }, + { 0x3, 0x3, 223, 2970, -1, 34, 1, 58 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 53 }, + { 0x3, 0x3, 223, 2971, -1, 34, 1, 53 }, + { 0xc00001, 0xc00001, 223, -1, -1, 12, 1, 63 }, + { 0x3, 0x3, 223, 2976, -1, 34, 1, 58 }, + { 0xe00001, 0x1e00001, 223, -1, -1, 12, 1, 63 }, + { 0x7, 0xf, 223, 2977, -1, 33, 1, 58 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 63 }, + { 0x3, 0x3, 223, 2978, -1, 34, 1, 58 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 63 }, + { 0x3, 0x3, 223, 2979, -1, 34, 1, 58 }, + { 0xc00001, 0xc00001, 223, -1, -1, 12, 1, 63 }, + { 0x3, 0x3, 223, 2982, -1, 34, 1, 58 }, + { 0x1c00001, 0x1c00001, 223, -1, -1, 12, 1, 63 }, + { 0x7, 0x7, 223, 2983, -1, 34, 1, 58 }, + { 0xe00001, 0xe00001, 223, -1, -1, 12, 1, 63 }, + { 0x7, 0x7, 223, 2984, -1, 33, 1, 58 }, + { 0x1e00001, 0x1e00001, 223, -1, -1, 12, 1, 53 }, + { 0xf, 0xf, 223, 2985, -1, 33, 1, 53 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 63 }, + { 0x3, 0x3, 223, 2986, -1, 34, 1, 58 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 63 }, + { 0x3, 0x3, 223, 2987, -1, 34, 1, 58 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 63 }, + { 0x3, 0x3, 223, 2988, -1, 34, 1, 58 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 53 }, + { 0x3, 0x3, 223, 2989, -1, 34, 1, 53 }, + { 0xc00001, 0xc00001, 223, -1, -1, 12, 1, 63 }, + { 0x3, 0x3, 223, 2994, -1, 34, 1, 58 }, + { 0xe00001, 0x1e00001, 223, -1, -1, 12, 1, 63 }, + { 0x7, 0xf, 223, 2995, -1, 33, 1, 58 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 63 }, + { 0x3, 0x3, 223, 2996, -1, 34, 1, 58 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 63 }, + { 0x3, 0x3, 223, 2997, -1, 34, 1, 58 }, + { 0xc1, 0xc1, 223, -1, -1, 28, 1, 33 }, + { 0x3, 0x3, 223, 2862, -1, 34, 1, 33 }, + { 0x183, 0x183, 223, -1, -1, 27, 1, 33 }, + { 0x181, 0x181, 223, 2863, -1, 27, 1, 33 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 66 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, 2244, -1, 34, 1, 33 }, + { 0x7, 0x7, 223, -1, -1, 34, 1, 66 }, + { 0xb, 0xb, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, 2245, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 66 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, 2248, -1, 34, 1, 33 }, + { 0x7, 0x7, 223, -1, -1, 34, 1, 66 }, + { 0xb, 0xb, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, 2249, -1, 34, 1, 33 }, + { 0x7, 0x7, 223, -1, -1, 34, 1, 66 }, + { 0xb, 0xb, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, 2251, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 66 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, 2253, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 66 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, 2254, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 53 }, + { 0x181, 0x181, 223, -1, -1, 27, 1, 53 }, + { 0xc1, 0xc1, 223, -1, -1, 28, 1, 53 }, + { 0x183, 0x183, 223, -1, -1, 27, 1, 53 }, + { 0x61, 0x61, 223, -1, -1, 29, 1, 53 }, + { 0x185, 0x185, 223, -1, -1, 27, 1, 53 }, + { 0xc3, 0xc3, 223, -1, -1, 28, 1, 53 }, + { 0x187, 0x187, 223, -1, -1, 27, 1, 53 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 53 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 53 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 53 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 53 }, + { 0xc1, 0xc1, 223, -1, -1, 28, 1, 33 }, + { 0x3, 0x3, 223, 2866, -1, 34, 1, 33 }, + { 0x183, 0x183, 223, -1, -1, 27, 1, 33 }, + { 0x181, 0x181, 223, 2867, -1, 27, 1, 33 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 28 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 28 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 28 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 28 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 39 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 66 }, + { 0x3, 0x3, 223, -1, -1, 34, 1, 33 }, + { 0x3, 0x3, 223, 2256, -1, 34, 1, 33 }, + { 0x3, 0x3, 224, 540, 1451, 32, 1, 135 }, + { 0x3, 0x3, 224, 541, 1460, 32, 1, 135 }, + { 0x3, 0x3, 224, 542, 1469, 32, 1, 135 }, + { 0x3, 0x3, 224, 543, 1482, 32, 1, 135 }, + { 0x3, 0x3, 224, 544, 1491, 32, 1, 135 }, + { 0x3, 0x3, 224, 545, 1500, 32, 1, 135 }, + { 0x3, 0x3, 224, 546, 1509, 32, 1, 135 }, + { 0x3, 0x3, 224, 547, 1518, 32, 1, 135 }, + { 0x3, 0x3, 224, 548, 1527, 32, 1, 135 }, + { 0x3, 0x3, 224, 549, 1536, 32, 1, 135 }, + { 0x3, 0x3, 224, 550, 1546, 32, 1, 135 }, + { 0x3, 0x3, 224, 551, 1556, 32, 1, 135 }, + { 0x3, 0x3, 224, 564, 1569, 32, 1, 150 }, + { 0x3, 0x3, 224, 565, 1575, 32, 1, 155 }, + { 0x3, 0x3, 224, 566, 1581, 32, 1, 155 }, + { 0x3, 0x3, 224, 567, 1587, 32, 1, 150 }, + { 0x3, 0x3, 224, 568, 1593, 32, 1, 155 }, + { 0x3, 0x3, 224, 569, 1599, 32, 1, 155 }, + { 0x3, 0x3, 224, 570, 1605, 32, 1, 150 }, + { 0x3, 0x3, 224, 571, 1611, 32, 1, 155 }, + { 0x3, 0x3, 224, 572, 1617, 32, 1, 155 }, + { 0x3, 0x3, 224, 573, 1623, 32, 1, 150 }, + { 0x3, 0x3, 224, 574, 1629, 32, 1, 155 }, + { 0x3, 0x3, 224, 575, 1635, 32, 1, 150 }, + { 0x3, 0x3, 224, 576, 1641, 32, 1, 155 }, + { 0x3, 0x3, 224, 577, 1647, 32, 1, 150 }, + { 0x3, 0x3, 224, 578, 1653, 32, 1, 155 }, + { 0x3, 0x3, 224, 579, 1659, 32, 1, 150 }, + { 0x3, 0x3, 224, 580, 1665, 32, 1, 155 }, + { 0x3, 0x3, 224, 581, 1671, 32, 1, 155 }, + { 0x1, 0x1, 225, -1, -1, 28, 1, 34 }, + { 0x1, 0x1, 225, -1, -1, 28, 1, 34 }, + { 0x0, 0x0, 232, 958, -1, 0, 1, 144 }, + { 0x0, 0x0, 232, 959, -1, 0, 1, 160 }, + { 0x1, 0x1, 233, -1, 1982, 33, 1, 140 }, + { 0x1, 0x1, 233, -1, 1985, 33, 1, 146 }, + { 0x0, 0x0, 233, -1, 1987, 0, 1, 157 }, + { 0x0, 0x0, 233, -1, 1988, 0, 1, 161 }, + { 0x0, 0x0, 234, 883, 971, 0, 0, -1 }, + { 0x0, 0x0, 234, 884, 979, 0, 0, -1 }, + { 0x0, 0x0, 234, 885, 975, 0, 0, -1 }, + { 0x1, 0x1, 234, 886, 620, 33, 1, 6 }, + { 0x8000001, 0x8000001, 234, 887, 628, 6, 1, 7 }, + { 0x1, 0x1, 234, 888, 624, 33, 1, 6 }, + { 0x0, 0x0, 234, 889, 983, 0, 0, -1 }, + { 0x1, 0x1, 234, 890, 640, 33, 1, 8 }, + { 0x0, 0x0, 234, 891, 987, 0, 0, -1 }, + { 0x1, 0x1, 234, 892, 652, 33, 1, 16 }, + { 0x0, 0x0, 234, 893, 992, 0, 0, -1 }, + { 0x0, 0x0, 234, 894, 996, 0, 0, -1 }, + { 0x1, 0x1, 234, 895, 675, 33, 1, 18 }, + { 0x1, 0x1, 234, 896, 679, 33, 1, 18 }, + { 0x0, 0x0, 234, 897, 1000, 0, 0, -1 }, + { 0x0, 0x0, 234, 898, 1004, 0, 0, -1 }, + { 0x1, 0x1, 234, 899, 699, 33, 1, 19 }, + { 0x8000001, 0x8000001, 234, 900, 703, 6, 1, 19 }, + { 0x0, 0x0, 234, 901, 1008, 0, 0, -1 }, + { 0x1, 0x1, 234, 902, 715, 33, 1, 20 }, + { 0x0, 0x0, 234, 903, 1012, 0, 0, -1 }, + { 0x0, 0x0, 234, 904, 1016, 0, 0, -1 }, + { 0x1, 0x1, 234, 905, 735, 33, 1, 21 }, + { 0x8000001, 0x8000001, 234, 906, 739, 6, 1, 21 }, + { 0x0, 0x0, 234, 907, 1020, 0, 0, -1 }, + { 0x1, 0x1, 234, 908, 751, 33, 1, 22 }, + { 0x0, 0x0, 234, 909, 1025, 0, 0, -1 }, + { 0x0, 0x0, 234, 910, 1029, 0, 0, -1 }, + { 0x1, 0x1, 234, 911, 774, 33, 1, 18 }, + { 0x1, 0x1, 234, 912, 778, 33, 1, 18 }, + { 0x0, 0x0, 234, 913, 1033, 0, 0, -1 }, + { 0x1, 0x1, 234, 914, 790, 33, 1, 22 }, + { 0x0, 0x0, 235, 2787, 970, 0, 0, -1 }, + { 0x0, 0x0, 235, 2788, 978, 0, 0, -1 }, + { 0x0, 0x0, 235, 2789, 974, 0, 0, -1 }, + { 0x0, 0x0, 235, 2790, 619, 0, 1, 6 }, + { 0x1, 0x1, 235, 2791, 627, 6, 1, 7 }, + { 0x0, 0x0, 235, 2792, 623, 0, 1, 6 }, + { 0x0, 0x0, 235, 2793, 982, 0, 0, -1 }, + { 0x0, 0x0, 235, 2794, 639, 0, 1, 8 }, + { 0x0, 0x0, 235, 2795, 986, 0, 0, -1 }, + { 0x0, 0x0, 235, 2796, 651, 0, 1, 16 }, + { 0x0, 0x0, 235, 2797, 991, 0, 0, -1 }, + { 0x0, 0x0, 235, 2798, 995, 0, 0, -1 }, + { 0x0, 0x0, 235, 2799, 674, 0, 1, 18 }, + { 0x0, 0x0, 235, 2800, 678, 0, 1, 18 }, + { 0x0, 0x0, 235, 2801, 999, 0, 0, -1 }, + { 0x0, 0x0, 235, 2802, 1003, 0, 0, -1 }, + { 0x0, 0x0, 235, 2803, 698, 0, 1, 19 }, + { 0x1, 0x1, 235, 2804, 702, 6, 1, 19 }, + { 0x0, 0x0, 235, 2805, 1007, 0, 0, -1 }, + { 0x0, 0x0, 235, 2806, 714, 0, 1, 20 }, + { 0x0, 0x0, 235, 2807, 1011, 0, 0, -1 }, + { 0x0, 0x0, 235, 2808, 1015, 0, 0, -1 }, + { 0x0, 0x0, 235, 2809, 734, 0, 1, 21 }, + { 0x1, 0x1, 235, 2810, 738, 6, 1, 21 }, + { 0x0, 0x0, 235, 2811, 1019, 0, 0, -1 }, + { 0x0, 0x0, 235, 2812, 750, 0, 1, 22 }, + { 0x0, 0x0, 235, 2813, 1024, 0, 0, -1 }, + { 0x0, 0x0, 235, 2814, 1028, 0, 0, -1 }, + { 0x0, 0x0, 235, 2815, 773, 0, 1, 18 }, + { 0x0, 0x0, 235, 2816, 777, 0, 1, 18 }, + { 0x0, 0x0, 235, 2817, 1032, 0, 0, -1 }, + { 0x0, 0x0, 235, 2818, 789, 0, 1, 22 }, + { 0x1, 0x1, 235, 915, 1155, 27, 1, 17 }, + { 0x0, 0x0, 235, 916, 1153, 0, 1, 17 }, + { 0x0, 0x0, 235, 1220, 1157, 0, 1, 23 }, + { 0x0, 0x1, 235, 1165, 1163, 20, 1, 68 }, + { 0x0, 0x0, 235, 111, 1161, 0, 1, 68 }, + { 0x1, 0x1, 238, -1, -1, 29, 1, 0 }, + { 0x0, 0x0, 238, -1, -1, 0, 1, 0 }, + { 0x1, 0x1, 238, 3022, -1, 27, 1, 0 }, + { 0x1, 0x1, 238, 3023, -1, 27, 1, 0 }, + { 0x1, 0x1, 238, 3024, -1, 27, 1, 0 }, + { 0x1, 0x1, 238, 3025, -1, 27, 1, 0 }, + { 0x0, 0x0, 261, -1, 2344, 0, 0, -1 }, + { 0x0, 0x0, 261, -1, 2346, 0, 0, -1 }, + { 0x1, 0x1, 261, -1, -1, 28, 1, 30 }, + { 0x1, 0x1, 261, -1, -1, 28, 1, 30 }, + { 0x0, 0x0, 261, -1, 2385, 0, 0, -1 }, + { 0x0, 0x0, 261, -1, 2387, 0, 0, -1 }, + { 0x1, 0x1, 261, -1, -1, 28, 1, 30 }, + { 0x1, 0x1, 261, -1, -1, 28, 1, 30 }, + { 0x0, 0x0, 263, 23, -1, 0, 1, 0 }, + { 0x0, 0x0, 263, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 263, -1, -1, 0, 1, 0 }, + { 0x0, 0x1, 263, -1, -1, 29, 1, 0 }, + { 0x0, 0x1, 263, -1, -1, 29, 1, 0 }, + { 0x0, 0x1, 263, -1, -1, 29, 1, 0 }, + { 0x0, 0x1, 263, -1, -1, 29, 1, 0 }, + { 0x0, 0x1, 263, -1, -1, 29, 1, 0 }, + { 0x0, 0x0, 263, 180, -1, 0, 1, 0 }, + { 0x0, 0x1, 263, -1, -1, 29, 1, 0 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, 301, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, 323, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, 349, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, 371, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 65 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 65 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 65 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 65 }, + { 0x0, 0x0, 264, -1, 2296, 0, 0, -1 }, + { 0x0, 0x0, 264, -1, 2298, 0, 0, -1 }, + { 0x0, 0x0, 264, -1, 2300, 0, 0, -1 }, + { 0x0, 0x0, 264, -1, 2302, 0, 0, -1 }, + { 0x1, 0x1, 264, -1, 2304, 12, 1, 60 }, + { 0x1, 0x1, 264, -1, 2306, 12, 1, 60 }, + { 0x1, 0x1, 264, -1, 2308, 12, 1, 60 }, + { 0x1, 0x1, 264, -1, 2310, 12, 1, 50 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 60 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 60 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 60 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 50 }, + { 0x0, 0x0, 264, -1, 2312, 0, 0, -1 }, + { 0x0, 0x0, 264, -1, 2314, 0, 0, -1 }, + { 0x1, 0x1, 264, -1, 2316, 12, 1, 60 }, + { 0x1, 0x1, 264, -1, 2318, 12, 1, 60 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 60 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 60 }, + { 0x0, 0x0, 264, -1, 2320, 0, 0, -1 }, + { 0x0, 0x0, 264, -1, 2322, 0, 0, -1 }, + { 0x0, 0x0, 264, -1, 2324, 0, 0, -1 }, + { 0x0, 0x0, 264, -1, 2326, 0, 0, -1 }, + { 0x1, 0x1, 264, -1, 2328, 12, 1, 60 }, + { 0x1, 0x1, 264, -1, 2330, 12, 1, 60 }, + { 0x1, 0x1, 264, -1, 2332, 12, 1, 60 }, + { 0x1, 0x1, 264, -1, 2334, 12, 1, 50 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 60 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 60 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 60 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 50 }, + { 0x0, 0x0, 264, -1, 2336, 0, 0, -1 }, + { 0x0, 0x0, 264, -1, 2338, 0, 0, -1 }, + { 0x1, 0x1, 264, -1, 2340, 12, 1, 60 }, + { 0x1, 0x1, 264, -1, 2342, 12, 1, 60 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 60 }, + { 0x1, 0x1, 264, -1, -1, 12, 1, 60 }, + { 0x1, 0x1, 264, 393, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, 395, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, 517, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, 519, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, 401, -1, 12, 1, 77 }, + { 0x1, 0x1, 264, 403, -1, 12, 1, 77 }, + { 0x1, 0x1, 264, 525, -1, 12, 1, 77 }, + { 0x1, 0x1, 264, 527, -1, 12, 1, 77 }, + { 0x1, 0x1, 264, 409, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, 411, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, 533, -1, 12, 1, 2 }, + { 0x1, 0x1, 264, 535, -1, 12, 1, 2 }, + { 0x0, 0x0, 265, -1, 2303, 0, 0, -1 }, + { 0x9, 0x9, 265, -1, 2311, 33, 1, 50 }, + { 0x9, 0x9, 265, -1, 2975, 33, 1, 50 }, + { 0x0, 0x0, 265, 1399, 2376, 0, 0, -1 }, + { 0x3, 0x3, 265, 1400, -1, 27, 1, 50 }, + { 0x0, 0x0, 269, 2856, -1, 0, 1, 0 }, + { 0x3, 0x3, 270, -1, -1, 27, 1, 0 }, + { 0x3, 0x3, 270, -1, -1, 27, 1, 0 }, + { 0x3, 0x3, 270, -1, -1, 27, 1, 0 }, + { 0x3, 0x3, 270, -1, -1, 27, 1, 0 }, + { 0x1, 0x1, 271, 3018, -1, 28, 1, 0 }, + { 0x1, 0x1, 271, 3019, -1, 28, 1, 0 }, + { 0x1, 0x1, 271, 3020, -1, 28, 1, 0 }, + { 0x1, 0x1, 271, 3021, -1, 28, 1, 0 }, + { 0x1, 0x1, 273, -1, -1, 27, 1, 100 }, + { 0x1, 0x1, 273, -1, -1, 27, 1, 100 }, + { 0x0, 0x0, 273, -1, 968, 0, 0, -1 }, + { 0x0, 0x0, 274, 3031, 2833, 0, 0, -1 }, + { 0x0, 0x0, 274, 3032, 2835, 0, 0, -1 }, + { 0x0, 0x0, 275, -1, 2834, 0, 0, -1 }, + { 0x0, 0x0, 275, -1, 2836, 0, 0, -1 }, + { 0x0, 0x0, 276, -1, -1, 0, 1, 41 }, + { 0x0, 0x0, 276, -1, -1, 0, 1, 41 }, + { 0x0, 0x0, 276, -1, -1, 0, 1, 41 }, + { 0x0, 0x0, 281, -1, -1, 0, 1, 34 }, + { 0x0, 0x0, 285, -1, 2350, 0, 1, 30 }, + { 0x0, 0x0, 286, -1, -1, 0, 1, 0 }, + { 0x0, 0x0, 286, -1, -1, 0, 1, 72 }, + { 0x0, 0x0, 286, 2001, 3000, 0, 1, 1 }, + { 0x0, 0x0, 286, 2002, 3001, 0, 1, 1 }, + { 0x0, 0x0, 286, -1, 518, 0, 0, -1 }, + { 0x0, 0x0, 286, -1, 520, 0, 0, -1 }, + { 0x0, 0x0, 286, 2005, 3004, 0, 1, 76 }, + { 0x0, 0x0, 286, 2006, 3005, 0, 1, 76 }, + { 0x0, 0x0, 286, -1, 526, 0, 0, -1 }, + { 0x0, 0x0, 286, -1, 528, 0, 0, -1 }, + { 0x0, 0x0, 286, 2009, 3008, 0, 1, 1 }, + { 0x0, 0x0, 286, 2010, 3009, 0, 1, 1 }, + { 0x0, 0x0, 286, -1, 534, 0, 0, -1 }, + { 0x0, 0x0, 286, -1, 536, 0, 0, -1 }, +}; + +static const struct ia64_main_table +main_table[] = { + { 5, 1, 1, 0x0000010000000000ull, 0x000001eff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 0, }, + { 5, 1, 1, 0x0000010008000000ull, 0x000001eff8000000ull, { 24, 25, 26, 4, 0 }, 0x0, 1, }, + { 5, 7, 1, 0x0000000000000000ull, 0x0000000000000000ull, { 24, 67, 27, 0, 0 }, 0x0, 2, }, + { 5, 7, 1, 0x0000000000000000ull, 0x0000000000000000ull, { 24, 64, 26, 0, 0 }, 0x0, 3, }, + { 6, 1, 1, 0x0000012000000000ull, 0x000001e000000000ull, { 24, 67, 27, 0, 0 }, 0x0, 4, }, + { 7, 1, 1, 0x0000010040000000ull, 0x000001eff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 5, }, + { 7, 1, 1, 0x0000010c00000000ull, 0x000001ee00000000ull, { 24, 64, 26, 0, 0 }, 0x0, 6, }, + { 8, 1, 1, 0x0000010800000000ull, 0x000001ee00000000ull, { 24, 64, 26, 0, 0 }, 0x0, 7, }, + { 9, 3, 1, 0x0000002c00000000ull, 0x000001ee00000000ull, { 24, 3, 53, 54, 55 }, 0x221, 8, }, + { 9, 3, 1, 0x0000002c00000000ull, 0x000001ee00000000ull, { 24, 53, 54, 55, 0 }, 0x261, 9, }, + { 10, 1, 1, 0x0000010060000000ull, 0x000001eff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 10, }, + { 10, 1, 1, 0x0000010160000000ull, 0x000001eff8000000ull, { 24, 56, 26, 0, 0 }, 0x0, 11, }, + { 11, 1, 1, 0x0000010068000000ull, 0x000001eff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 12, }, + { 11, 1, 1, 0x0000010168000000ull, 0x000001eff8000000ull, { 24, 56, 26, 0, 0 }, 0x0, 13, }, + { 14, 4, 0, 0x0000000100000000ull, 0x000001eff80011ffull, { 16, 0, 0, 0, 0 }, 0x40, 969, }, + { 14, 4, 0, 0x0000000100000000ull, 0x000001eff80011c0ull, { 16, 0, 0, 0, 0 }, 0x0, 825, }, + { 14, 4, 0, 0x0000000100000000ull, 0x000001eff80011c0ull, { 16, 0, 0, 0, 0 }, 0x40, 826, }, + { 14, 4, 0, 0x0000000108000100ull, 0x000001eff80011c0ull, { 16, 0, 0, 0, 0 }, 0x200, 2234, }, + { 14, 4, 0, 0x0000000108000100ull, 0x000001eff80011c0ull, { 16, 0, 0, 0, 0 }, 0x240, 2235, }, + { 14, 4, 1, 0x0000002100000000ull, 0x000001ef00001000ull, { 15, 16, 0, 0, 0 }, 0x0, 582, }, + { 14, 4, 1, 0x0000002100000000ull, 0x000001ef00001000ull, { 15, 16, 0, 0, 0 }, 0x40, 583, }, + { 14, 4, 0, 0x0000008000000000ull, 0x000001ee000011ffull, { 82, 0, 0, 0, 0 }, 0x40, 990, }, + { 14, 4, 0, 0x0000008000000000ull, 0x000001ee000011c0ull, { 82, 0, 0, 0, 0 }, 0x0, 827, }, + { 14, 4, 0, 0x0000008000000000ull, 0x000001ee000011c0ull, { 82, 0, 0, 0, 0 }, 0x40, 828, }, + { 14, 4, 0, 0x0000008000000080ull, 0x000001ee000011c0ull, { 82, 0, 0, 0, 0 }, 0x210, 3029, }, + { 14, 4, 0, 0x0000008000000080ull, 0x000001ee000011c0ull, { 82, 0, 0, 0, 0 }, 0x250, 3030, }, + { 14, 4, 0, 0x0000008000000140ull, 0x000001ee000011c0ull, { 82, 0, 0, 0, 0 }, 0x30, 590, }, + { 14, 4, 0, 0x0000008000000140ull, 0x000001ee000011c0ull, { 82, 0, 0, 0, 0 }, 0x70, 591, }, + { 14, 4, 0, 0x0000008000000180ull, 0x000001ee000011c0ull, { 82, 0, 0, 0, 0 }, 0x230, 588, }, + { 14, 4, 0, 0x0000008000000180ull, 0x000001ee000011c0ull, { 82, 0, 0, 0, 0 }, 0x270, 589, }, + { 14, 4, 1, 0x000000a000000000ull, 0x000001ee00001000ull, { 15, 82, 0, 0, 0 }, 0x0, 584, }, + { 14, 4, 1, 0x000000a000000000ull, 0x000001ee00001000ull, { 15, 82, 0, 0, 0 }, 0x40, 585, }, + { 15, 4, 0, 0x0000000000000000ull, 0x000001e1f8000000ull, { 66, 0, 0, 0, 0 }, 0x0, 537, }, + { 15, 5, 0, 0x0000000000000000ull, 0x000001e3f8000000ull, { 66, 0, 0, 0, 0 }, 0x0, 960, }, + { 15, 2, 0, 0x0000000000000000ull, 0x000001eff8000000ull, { 66, 0, 0, 0, 0 }, 0x2, 1138, }, + { 15, 3, 0, 0x0000000000000000ull, 0x000001eff8000000ull, { 66, 0, 0, 0, 0 }, 0x0, 1263, }, + { 15, 6, 0, 0x0000000000000000ull, 0x000001eff8000000ull, { 70, 0, 0, 0, 0 }, 0x0, 3033, }, + { 15, 7, 0, 0x0000000000000000ull, 0x0000000000000000ull, { 66, 0, 0, 0, 0 }, 0x0, 16, }, + { 16, 6, 0, 0x0000018000000000ull, 0x000001ee000011ffull, { 83, 0, 0, 0, 0 }, 0x40, 1023, }, + { 16, 6, 0, 0x0000018000000000ull, 0x000001ee000011c0ull, { 83, 0, 0, 0, 0 }, 0x0, 829, }, + { 16, 6, 0, 0x0000018000000000ull, 0x000001ee000011c0ull, { 83, 0, 0, 0, 0 }, 0x40, 830, }, + { 16, 6, 1, 0x000001a000000000ull, 0x000001ee00001000ull, { 15, 83, 0, 0, 0 }, 0x0, 586, }, + { 16, 6, 1, 0x000001a000000000ull, 0x000001ee00001000ull, { 15, 83, 0, 0, 0 }, 0x40, 587, }, + { 17, 4, 0, 0x0000004080000000ull, 0x000001e9f8000018ull, { 16, 78, 0, 0, 0 }, 0x20, 2852, }, + { 17, 4, 0, 0x000000e000000000ull, 0x000001e800000018ull, { 82, 78, 0, 0, 0 }, 0x20, 2853, }, + { 18, 4, 0, 0x0000000060000000ull, 0x000001e1f8000000ull, { 0, 0, 0, 0, 0 }, 0x2c, 222, }, + { 22, 2, 0, 0x0000000200000000ull, 0x000001ee00000000ull, { 25, 81, 0, 0, 0 }, 0x0, 2239, }, + { 22, 3, 0, 0x0000000800000000ull, 0x000001ee00000000ull, { 24, 82, 0, 0, 0 }, 0x0, 226, }, + { 22, 3, 0, 0x0000000c00000000ull, 0x000001ee00000000ull, { 18, 82, 0, 0, 0 }, 0x0, 227, }, + { 22, 3, 0, 0x0000002200000000ull, 0x000001ee00000000ull, { 25, 81, 0, 0, 0 }, 0x0, 2240, }, + { 22, 3, 0, 0x0000002600000000ull, 0x000001ee00000000ull, { 19, 81, 0, 0, 0 }, 0x0, 2241, }, + { 22, 7, 0, 0x0000000000000000ull, 0x0000000000000000ull, { 25, 81, 0, 0, 0 }, 0x0, 2242, }, + { 25, 4, 0, 0x0000000020000000ull, 0x000001e1f8000000ull, { 0, 0, 0, 0, 0 }, 0x224, 18, }, + { 26, 1, 2, 0x0000018000000000ull, 0x000001fe00001000ull, { 22, 23, 25, 26, 0 }, 0x0, 1222, }, + { 26, 1, 1, 0x0000018000000000ull, 0x000001fe00001000ull, { 22, 25, 26, 0, 0 }, 0x40, 1223, }, + { 26, 1, 2, 0x0000018000000000ull, 0x000001fe00001000ull, { 23, 22, 26, 25, 0 }, 0x0, 1181, }, + { 26, 1, 1, 0x0000018000000000ull, 0x000001fe00001000ull, { 23, 26, 25, 0, 0 }, 0x40, 1182, }, + { 26, 1, 2, 0x0000018000000000ull, 0x000001fe00001000ull, { 22, 23, 26, 25, 0 }, 0x0, 1090, }, + { 26, 1, 1, 0x0000018000000000ull, 0x000001fe00001000ull, { 22, 26, 25, 0, 0 }, 0x40, 1091, }, + { 26, 1, 2, 0x0000018000000000ull, 0x000001fe00001000ull, { 23, 22, 25, 26, 0 }, 0x0, 1052, }, + { 26, 1, 1, 0x0000018000000000ull, 0x000001fe00001000ull, { 23, 25, 26, 0, 0 }, 0x40, 1053, }, + { 26, 1, 2, 0x0000018200000000ull, 0x000001fe00001000ull, { 22, 23, 25, 26, 0 }, 0x40, 1376, }, + { 26, 1, 2, 0x0000019000000000ull, 0x000001fe00001000ull, { 22, 23, 7, 26, 0 }, 0x0, 1092, }, + { 26, 1, 1, 0x0000019000000000ull, 0x000001fe00001000ull, { 22, 7, 26, 0, 0 }, 0x40, 1093, }, + { 26, 1, 2, 0x0000019000000000ull, 0x000001fe00001000ull, { 22, 23, 26, 7, 0 }, 0x40, 1226, }, + { 26, 1, 1, 0x0000019000000000ull, 0x000001fe00001000ull, { 22, 26, 7, 0, 0 }, 0x40, 1227, }, + { 26, 1, 2, 0x0000019000000000ull, 0x000001fe00001000ull, { 22, 23, 7, 26, 0 }, 0x40, 1187, }, + { 26, 1, 2, 0x0000018800000000ull, 0x000001ee00001000ull, { 22, 23, 56, 26, 0 }, 0x0, 1229, }, + { 26, 1, 1, 0x0000018800000000ull, 0x000001ee00001000ull, { 22, 56, 26, 0, 0 }, 0x40, 1230, }, + { 26, 1, 2, 0x0000018800000000ull, 0x000001ee00001000ull, { 22, 23, 58, 26, 0 }, 0x0, 1188, }, + { 26, 1, 1, 0x0000018800000000ull, 0x000001ee00001000ull, { 22, 58, 26, 0, 0 }, 0x40, 1189, }, + { 26, 1, 2, 0x0000018800000000ull, 0x000001ee00001000ull, { 23, 22, 58, 26, 0 }, 0x0, 1097, }, + { 26, 1, 1, 0x0000018800000000ull, 0x000001ee00001000ull, { 23, 58, 26, 0, 0 }, 0x40, 1098, }, + { 26, 1, 2, 0x0000018800000000ull, 0x000001ee00001000ull, { 23, 22, 56, 26, 0 }, 0x0, 1059, }, + { 26, 1, 1, 0x0000018800000000ull, 0x000001ee00001000ull, { 23, 56, 26, 0, 0 }, 0x40, 1060, }, + { 26, 1, 2, 0x0000018a00000000ull, 0x000001ee00001000ull, { 22, 23, 56, 26, 0 }, 0x40, 1381, }, + { 26, 1, 2, 0x000001a800000000ull, 0x000001ee00001000ull, { 22, 23, 60, 26, 0 }, 0x0, 1214, }, + { 26, 1, 1, 0x000001a800000000ull, 0x000001ee00001000ull, { 22, 60, 26, 0, 0 }, 0x40, 1215, }, + { 26, 1, 2, 0x000001a800000000ull, 0x000001ee00001000ull, { 23, 22, 60, 26, 0 }, 0x0, 1125, }, + { 26, 1, 1, 0x000001a800000000ull, 0x000001ee00001000ull, { 23, 60, 26, 0, 0 }, 0x40, 1126, }, + { 26, 1, 2, 0x000001c200000000ull, 0x000001fe00001000ull, { 23, 22, 25, 26, 0 }, 0x40, 1382, }, + { 26, 1, 2, 0x000001d000000000ull, 0x000001fe00001000ull, { 23, 22, 7, 26, 0 }, 0x40, 1190, }, + { 26, 1, 1, 0x000001d000000000ull, 0x000001fe00001000ull, { 23, 7, 26, 0, 0 }, 0x40, 1191, }, + { 26, 1, 2, 0x000001d000000000ull, 0x000001fe00001000ull, { 23, 22, 26, 7, 0 }, 0x40, 1063, }, + { 26, 1, 1, 0x000001d000000000ull, 0x000001fe00001000ull, { 23, 26, 7, 0, 0 }, 0x40, 1064, }, + { 26, 1, 2, 0x000001ca00000000ull, 0x000001ee00001000ull, { 23, 22, 56, 26, 0 }, 0x40, 1383, }, + { 27, 1, 2, 0x0000018400000000ull, 0x000001fe00001000ull, { 22, 23, 25, 26, 0 }, 0x0, 1235, }, + { 27, 1, 1, 0x0000018400000000ull, 0x000001fe00001000ull, { 22, 25, 26, 0, 0 }, 0x40, 1236, }, + { 27, 1, 2, 0x0000018400000000ull, 0x000001fe00001000ull, { 23, 22, 26, 25, 0 }, 0x0, 1194, }, + { 27, 1, 1, 0x0000018400000000ull, 0x000001fe00001000ull, { 23, 26, 25, 0, 0 }, 0x40, 1195, }, + { 27, 1, 2, 0x0000018400000000ull, 0x000001fe00001000ull, { 22, 23, 26, 25, 0 }, 0x0, 1103, }, + { 27, 1, 1, 0x0000018400000000ull, 0x000001fe00001000ull, { 22, 26, 25, 0, 0 }, 0x40, 1104, }, + { 27, 1, 2, 0x0000018400000000ull, 0x000001fe00001000ull, { 23, 22, 25, 26, 0 }, 0x0, 1065, }, + { 27, 1, 1, 0x0000018400000000ull, 0x000001fe00001000ull, { 23, 25, 26, 0, 0 }, 0x40, 1066, }, + { 27, 1, 2, 0x0000018600000000ull, 0x000001fe00001000ull, { 22, 23, 25, 26, 0 }, 0x40, 1388, }, + { 27, 1, 2, 0x0000019400000000ull, 0x000001fe00001000ull, { 22, 23, 7, 26, 0 }, 0x0, 1105, }, + { 27, 1, 1, 0x0000019400000000ull, 0x000001fe00001000ull, { 22, 7, 26, 0, 0 }, 0x40, 1106, }, + { 27, 1, 2, 0x0000019400000000ull, 0x000001fe00001000ull, { 22, 23, 26, 7, 0 }, 0x40, 1239, }, + { 27, 1, 1, 0x0000019400000000ull, 0x000001fe00001000ull, { 22, 26, 7, 0, 0 }, 0x40, 1240, }, + { 27, 1, 2, 0x0000019400000000ull, 0x000001fe00001000ull, { 22, 23, 7, 26, 0 }, 0x40, 1200, }, + { 27, 1, 2, 0x0000018c00000000ull, 0x000001ee00001000ull, { 22, 23, 56, 26, 0 }, 0x0, 1242, }, + { 27, 1, 1, 0x0000018c00000000ull, 0x000001ee00001000ull, { 22, 56, 26, 0, 0 }, 0x40, 1243, }, + { 27, 1, 2, 0x0000018c00000000ull, 0x000001ee00001000ull, { 22, 23, 58, 26, 0 }, 0x0, 1201, }, + { 27, 1, 1, 0x0000018c00000000ull, 0x000001ee00001000ull, { 22, 58, 26, 0, 0 }, 0x40, 1202, }, + { 27, 1, 2, 0x0000018c00000000ull, 0x000001ee00001000ull, { 23, 22, 58, 26, 0 }, 0x0, 1110, }, + { 27, 1, 1, 0x0000018c00000000ull, 0x000001ee00001000ull, { 23, 58, 26, 0, 0 }, 0x40, 1111, }, + { 27, 1, 2, 0x0000018c00000000ull, 0x000001ee00001000ull, { 23, 22, 56, 26, 0 }, 0x0, 1072, }, + { 27, 1, 1, 0x0000018c00000000ull, 0x000001ee00001000ull, { 23, 56, 26, 0, 0 }, 0x40, 1073, }, + { 27, 1, 2, 0x0000018e00000000ull, 0x000001ee00001000ull, { 22, 23, 56, 26, 0 }, 0x40, 1393, }, + { 27, 1, 2, 0x000001ac00000000ull, 0x000001ee00001000ull, { 22, 23, 57, 26, 0 }, 0x0, 1259, }, + { 27, 1, 1, 0x000001ac00000000ull, 0x000001ee00001000ull, { 22, 57, 26, 0, 0 }, 0x40, 1260, }, + { 27, 1, 2, 0x000001ac00000000ull, 0x000001ee00001000ull, { 22, 23, 59, 26, 0 }, 0x0, 1218, }, + { 27, 1, 1, 0x000001ac00000000ull, 0x000001ee00001000ull, { 22, 59, 26, 0, 0 }, 0x40, 1219, }, + { 27, 1, 2, 0x000001ac00000000ull, 0x000001ee00001000ull, { 23, 22, 59, 26, 0 }, 0x0, 1129, }, + { 27, 1, 1, 0x000001ac00000000ull, 0x000001ee00001000ull, { 23, 59, 26, 0, 0 }, 0x40, 1130, }, + { 27, 1, 2, 0x000001ac00000000ull, 0x000001ee00001000ull, { 23, 22, 57, 26, 0 }, 0x0, 1088, }, + { 27, 1, 1, 0x000001ac00000000ull, 0x000001ee00001000ull, { 23, 57, 26, 0, 0 }, 0x40, 1089, }, + { 27, 1, 2, 0x000001c600000000ull, 0x000001fe00001000ull, { 23, 22, 25, 26, 0 }, 0x40, 1394, }, + { 27, 1, 2, 0x000001d400000000ull, 0x000001fe00001000ull, { 23, 22, 7, 26, 0 }, 0x40, 1203, }, + { 27, 1, 1, 0x000001d400000000ull, 0x000001fe00001000ull, { 23, 7, 26, 0, 0 }, 0x40, 1204, }, + { 27, 1, 2, 0x000001d400000000ull, 0x000001fe00001000ull, { 23, 22, 26, 7, 0 }, 0x40, 1076, }, + { 27, 1, 1, 0x000001d400000000ull, 0x000001fe00001000ull, { 23, 26, 7, 0, 0 }, 0x40, 1077, }, + { 27, 1, 2, 0x000001ce00000000ull, 0x000001ee00001000ull, { 23, 22, 56, 26, 0 }, 0x40, 1395, }, + { 28, 3, 1, 0x0000008808000000ull, 0x000001fff8000000ull, { 24, 28, 25, 1, 2 }, 0x0, 259, }, + { 28, 3, 1, 0x0000008808000000ull, 0x000001fff8000000ull, { 24, 28, 25, 0, 0 }, 0x40, 260, }, + { 29, 3, 1, 0x0000008008000000ull, 0x000001fff8000000ull, { 24, 28, 25, 2, 0 }, 0x0, 261, }, + { 29, 3, 1, 0x0000008008000000ull, 0x000001fff8000000ull, { 24, 28, 25, 0, 0 }, 0x40, 262, }, + { 30, 3, 1, 0x0000008048000000ull, 0x000001fff8000000ull, { 24, 28, 25, 2, 0 }, 0x0, 263, }, + { 30, 3, 1, 0x0000008048000000ull, 0x000001fff8000000ull, { 24, 28, 25, 0, 0 }, 0x40, 264, }, + { 31, 3, 1, 0x0000008088000000ull, 0x000001fff8000000ull, { 24, 28, 25, 2, 0 }, 0x0, 265, }, + { 31, 3, 1, 0x0000008088000000ull, 0x000001fff8000000ull, { 24, 28, 25, 0, 0 }, 0x40, 266, }, + { 32, 3, 1, 0x00000080c8000000ull, 0x000001fff8000000ull, { 24, 28, 25, 2, 0 }, 0x0, 267, }, + { 32, 3, 1, 0x00000080c8000000ull, 0x000001fff8000000ull, { 24, 28, 25, 0, 0 }, 0x40, 268, }, + { 34, 4, 0, 0x0000000010000000ull, 0x000001e1f8000000ull, { 0, 0, 0, 0, 0 }, 0x224, 19, }, + { 36, 2, 1, 0x00000000c0000000ull, 0x000001eff8000000ull, { 24, 26, 0, 0, 0 }, 0x0, 1167, }, + { 37, 2, 1, 0x00000000c8000000ull, 0x000001eff8000000ull, { 24, 26, 0, 0, 0 }, 0x0, 1168, }, + { 39, 2, 1, 0x0000008000000000ull, 0x000001e000000000ull, { 24, 25, 26, 47, 73 }, 0x0, 20, }, + { 39, 2, 1, 0x000000a600000000ull, 0x000001ee04000000ull, { 24, 25, 45, 74, 0 }, 0x0, 3038, }, + { 39, 2, 1, 0x000000a604000000ull, 0x000001ee04000000ull, { 24, 56, 45, 74, 0 }, 0x0, 3039, }, + { 39, 2, 1, 0x000000ae00000000ull, 0x000001ee00000000ull, { 24, 48, 26, 46, 74 }, 0x0, 21, }, + { 43, 4, 0, 0x0000000080000000ull, 0x000001e1f8000000ull, { 0, 0, 0, 0, 0 }, 0x20, 22, }, + { 48, 2, 1, 0x000000a400000000ull, 0x000001ee00002000ull, { 24, 26, 77, 74, 0 }, 0x0, 2870, }, + { 50, 5, 1, 0x0000000080000000ull, 0x000001e3f80fe000ull, { 18, 20, 0, 0, 0 }, 0x40, 24, }, + { 51, 5, 1, 0x0000010008000000ull, 0x000001fff8000000ull, { 18, 20, 19, 0, 0 }, 0x40, 2291, }, + { 52, 5, 1, 0x00000000b8000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 2292, }, + { 52, 5, 1, 0x00000000b8000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x40, 26, }, + { 53, 5, 1, 0x00000000b0000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 2293, }, + { 53, 5, 1, 0x00000000b0000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x40, 27, }, + { 54, 5, 1, 0x0000000160000000ull, 0x000001e3f8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 28, }, + { 55, 5, 1, 0x0000000168000000ull, 0x000001e3f8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 29, }, + { 57, 3, 0, 0x0000002180000000ull, 0x000001fff8000000ull, { 26, 0, 0, 0, 0 }, 0x0, 30, }, + { 58, 5, 0, 0x0000000040000000ull, 0x000001eff8000000ull, { 80, 0, 0, 0, 0 }, 0x0, 2294, }, + { 58, 5, 0, 0x0000000040000000ull, 0x000001eff8000000ull, { 80, 0, 0, 0, 0 }, 0x40, 31, }, + { 59, 5, 2, 0x000000a000000000ull, 0x000001e000001000ull, { 22, 23, 19, 61, 0 }, 0x0, 1265, }, + { 59, 5, 1, 0x000000a000000000ull, 0x000001e000001000ull, { 22, 19, 61, 0, 0 }, 0x40, 1266, }, + { 59, 5, 2, 0x000000a000000000ull, 0x000001e000001000ull, { 23, 22, 19, 61, 0 }, 0x40, 1420, }, + { 59, 5, 1, 0x000000a000000000ull, 0x000001e000001000ull, { 23, 19, 61, 0, 0 }, 0x40, 1421, }, + { 60, 5, 0, 0x0000000028000000ull, 0x000001eff8000000ull, { 0, 0, 0, 0, 0 }, 0x0, 2295, }, + { 60, 5, 0, 0x0000000028000000ull, 0x000001eff8000000ull, { 0, 0, 0, 0, 0 }, 0x40, 32, }, + { 61, 5, 2, 0x0000008000000000ull, 0x000001fe00001000ull, { 22, 23, 19, 20, 0 }, 0x0, 943, }, + { 61, 5, 1, 0x0000008000000000ull, 0x000001fe00001000ull, { 22, 19, 20, 0, 0 }, 0x40, 944, }, + { 61, 5, 2, 0x0000008000000000ull, 0x000001fe00001000ull, { 22, 23, 19, 20, 0 }, 0x40, 945, }, + { 61, 5, 2, 0x0000009000000000ull, 0x000001fe00001000ull, { 22, 23, 20, 19, 0 }, 0x0, 1116, }, + { 61, 5, 1, 0x0000009000000000ull, 0x000001fe00001000ull, { 22, 20, 19, 0, 0 }, 0x40, 1117, }, + { 61, 5, 2, 0x0000009000000000ull, 0x000001fe00001000ull, { 22, 23, 20, 19, 0 }, 0x40, 1118, }, + { 61, 5, 2, 0x0000008000000000ull, 0x000001fe00001000ull, { 23, 22, 19, 20, 0 }, 0x0, 1396, }, + { 61, 5, 1, 0x0000008000000000ull, 0x000001fe00001000ull, { 23, 19, 20, 0, 0 }, 0x40, 1397, }, + { 61, 5, 2, 0x0000008000000000ull, 0x000001fe00001000ull, { 23, 22, 19, 20, 0 }, 0x40, 1398, }, + { 61, 5, 2, 0x0000009000000000ull, 0x000001fe00001000ull, { 23, 22, 20, 19, 0 }, 0x0, 1405, }, + { 61, 5, 1, 0x0000009000000000ull, 0x000001fe00001000ull, { 23, 20, 19, 0, 0 }, 0x40, 1406, }, + { 61, 5, 2, 0x0000009000000000ull, 0x000001fe00001000ull, { 23, 22, 20, 19, 0 }, 0x40, 1407, }, + { 62, 5, 1, 0x00000000c0000000ull, 0x000001eff8000000ull, { 18, 19, 0, 0, 0 }, 0x0, 1042, }, + { 62, 5, 1, 0x00000000c0000000ull, 0x000001eff8000000ull, { 18, 19, 0, 0, 0 }, 0x40, 1043, }, + { 62, 5, 1, 0x00000000e0000000ull, 0x000001e3f8000000ull, { 18, 19, 0, 0, 0 }, 0x0, 3036, }, + { 62, 5, 1, 0x0000010008000000ull, 0x000001fff80fe000ull, { 18, 20, 0, 0, 0 }, 0x40, 3037, }, + { 63, 3, 1, 0x0000008488000000ull, 0x000001fff8000000ull, { 24, 28, 72, 0, 0 }, 0x0, 269, }, + { 64, 3, 1, 0x00000084c8000000ull, 0x000001fff8000000ull, { 24, 28, 72, 0, 0 }, 0x0, 270, }, + { 67, 3, 0, 0x0000000060000000ull, 0x000001eff8000000ull, { 0, 0, 0, 0, 0 }, 0x21, 33, }, + { 68, 5, 1, 0x0000010000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x0, 2353, }, + { 68, 5, 1, 0x0000010000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x40, 34, }, + { 69, 5, 1, 0x00000000a8000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 2354, }, + { 69, 5, 1, 0x00000000a8000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x40, 35, }, + { 70, 5, 1, 0x0000000080000000ull, 0x000001e3f8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 2247, }, + { 71, 5, 1, 0x00000000a0000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 2355, }, + { 71, 5, 1, 0x00000000a0000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x40, 36, }, + { 72, 5, 1, 0x00000001c8000000ull, 0x000001e3f8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 1221, }, + { 73, 5, 1, 0x0000010000000000ull, 0x000001fc000fe000ull, { 18, 20, 21, 0, 0 }, 0x40, 2358, }, + { 74, 5, 1, 0x0000014000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x0, 2361, }, + { 74, 5, 1, 0x0000014000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x40, 38, }, + { 75, 5, 1, 0x0000000088000000ull, 0x000001e3f8000000ull, { 18, 20, 0, 0, 0 }, 0xc0, 39, }, + { 76, 5, 1, 0x0000000088000000ull, 0x000001e3f80fe000ull, { 18, 20, 0, 0, 0 }, 0x40, 40, }, + { 77, 5, 1, 0x0000018000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x0, 2364, }, + { 77, 5, 1, 0x0000018000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x40, 41, }, + { 78, 5, 1, 0x0000018000000000ull, 0x000001fc000fe000ull, { 18, 20, 21, 0, 0 }, 0x40, 2367, }, + { 79, 5, 1, 0x0000010008000000ull, 0x000001fff80fe000ull, { 18, 20, 0, 0, 0 }, 0x40, 2370, }, + { 80, 5, 1, 0x0000000170000000ull, 0x000001e3f8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 44, }, + { 81, 5, 1, 0x0000002080000000ull, 0x000001e3f80fe000ull, { 18, 20, 0, 0, 0 }, 0x40, 45, }, + { 82, 5, 1, 0x0000000140000000ull, 0x000001e3f8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 46, }, + { 83, 5, 1, 0x00000020b8000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 2371, }, + { 83, 5, 1, 0x00000020b8000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x40, 47, }, + { 84, 5, 1, 0x00000020b0000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 2372, }, + { 84, 5, 1, 0x00000020b0000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x40, 48, }, + { 85, 5, 1, 0x0000002180000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 946, }, + { 85, 5, 1, 0x0000002180000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x40, 947, }, + { 85, 5, 1, 0x0000002188000000ull, 0x000001eff8000000ull, { 18, 20, 19, 0, 0 }, 0x40, 1119, }, + { 86, 5, 1, 0x00000020c0000000ull, 0x000001eff8000000ull, { 18, 19, 0, 0, 0 }, 0x0, 1044, }, + { 86, 5, 1, 0x00000020c0000000ull, 0x000001eff8000000ull, { 18, 19, 0, 0, 0 }, 0x40, 1045, }, + { 87, 5, 1, 0x0000013000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x0, 2389, }, + { 87, 5, 1, 0x0000013000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x40, 49, }, + { 88, 5, 1, 0x00000020a8000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 2390, }, + { 88, 5, 1, 0x00000020a8000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x40, 50, }, + { 89, 5, 1, 0x0000002080000000ull, 0x000001e3f8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 2255, }, + { 90, 5, 1, 0x00000020a0000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 2391, }, + { 90, 5, 1, 0x00000020a0000000ull, 0x000001eff8000000ull, { 18, 19, 20, 0, 0 }, 0x40, 51, }, + { 91, 5, 1, 0x0000013000000000ull, 0x000001fc000fe000ull, { 18, 20, 21, 0, 0 }, 0x40, 2392, }, + { 92, 5, 1, 0x0000017000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x0, 2393, }, + { 92, 5, 1, 0x0000017000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x40, 53, }, + { 93, 5, 1, 0x0000002088000000ull, 0x000001e3f8000000ull, { 18, 20, 0, 0, 0 }, 0xc0, 54, }, + { 94, 5, 1, 0x0000002088000000ull, 0x000001e3f80fe000ull, { 18, 20, 0, 0, 0 }, 0x40, 55, }, + { 95, 5, 1, 0x000001b000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x0, 2394, }, + { 95, 5, 1, 0x000001b000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x40, 56, }, + { 96, 5, 1, 0x000001b000000000ull, 0x000001fc000fe000ull, { 18, 20, 21, 0, 0 }, 0x40, 2395, }, + { 97, 5, 2, 0x0000002200000000ull, 0x000001fe00000000ull, { 18, 23, 19, 20, 0 }, 0x0, 2396, }, + { 97, 5, 2, 0x0000002200000000ull, 0x000001fe00000000ull, { 18, 23, 19, 20, 0 }, 0x40, 58, }, + { 98, 5, 2, 0x0000003200000000ull, 0x000001fe00000000ull, { 18, 23, 20, 0, 0 }, 0x0, 2397, }, + { 98, 5, 2, 0x0000003200000000ull, 0x000001fe00000000ull, { 18, 23, 20, 0, 0 }, 0x40, 59, }, + { 99, 5, 2, 0x0000000200000000ull, 0x000001fe00000000ull, { 18, 23, 19, 20, 0 }, 0x0, 2398, }, + { 99, 5, 2, 0x0000000200000000ull, 0x000001fe00000000ull, { 18, 23, 19, 20, 0 }, 0x40, 60, }, + { 100, 5, 2, 0x0000001200000000ull, 0x000001fe00000000ull, { 18, 23, 20, 0, 0 }, 0x0, 2399, }, + { 100, 5, 2, 0x0000001200000000ull, 0x000001fe00000000ull, { 18, 23, 20, 0, 0 }, 0x40, 61, }, + { 101, 5, 1, 0x000001c000000000ull, 0x000001f000000000ull, { 18, 20, 21, 19, 0 }, 0x0, 62, }, + { 102, 5, 0, 0x0000000020000000ull, 0x000001eff8000000ull, { 51, 52, 0, 0, 0 }, 0x0, 2400, }, + { 102, 5, 0, 0x0000000020000000ull, 0x000001eff8000000ull, { 51, 52, 0, 0, 0 }, 0x40, 63, }, + { 103, 5, 1, 0x0000014008000000ull, 0x000001fff8000000ull, { 18, 20, 19, 0, 0 }, 0x40, 2403, }, + { 104, 5, 1, 0x00000001a0000000ull, 0x000001e3f8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 65, }, + { 105, 5, 1, 0x00000001e0000000ull, 0x000001e3f8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 2202, }, + { 106, 3, 0, 0x0000000100000000ull, 0x000001eff8000000ull, { 0, 0, 0, 0, 0 }, 0x0, 66, }, + { 108, 5, 1, 0x0000000178000000ull, 0x000001e3f8000000ull, { 18, 19, 20, 0, 0 }, 0x0, 67, }, + { 113, 3, 1, 0x0000008708000000ull, 0x000001ffc8000000ull, { 24, 19, 0, 0, 0 }, 0x0, 2781, }, + { 118, 4, 0, 0x0000004008000000ull, 0x000001e1f8000000ull, { 66, 0, 0, 0, 0 }, 0x0, 538, }, + { 118, 5, 0, 0x000000000c000000ull, 0x000001e3fc000000ull, { 66, 0, 0, 0, 0 }, 0x0, 961, }, + { 118, 2, 0, 0x000000000c000000ull, 0x000001effc000000ull, { 66, 0, 0, 0, 0 }, 0x2, 1141, }, + { 118, 3, 0, 0x000000000c000000ull, 0x000001effc000000ull, { 66, 0, 0, 0, 0 }, 0x0, 1267, }, + { 118, 6, 0, 0x000000000c000000ull, 0x000001effc000000ull, { 70, 0, 0, 0, 0 }, 0x0, 3034, }, + { 118, 7, 0, 0x0000000000000000ull, 0x0000000000000000ull, { 66, 0, 0, 0, 0 }, 0x0, 68, }, + { 123, 3, 0, 0x0000000080000000ull, 0x000001eff8000000ull, { 0, 0, 0, 0, 0 }, 0x0, 69, }, + { 123, 3, 0, 0x0000000090000000ull, 0x000001eff8000000ull, { 24, 0, 0, 0, 0 }, 0x0, 920, }, + { 123, 3, 0, 0x0000000098000000ull, 0x000001eff8000000ull, { 18, 0, 0, 0, 0 }, 0x0, 921, }, + { 124, 3, 0, 0x0000002170000000ull, 0x000001eff8000000ull, { 25, 0, 0, 0, 0 }, 0xc, 846, }, + { 125, 3, 1, 0x0000002070000000ull, 0x000001eff8000000ull, { 31, 25, 0, 0, 0 }, 0x8, 847, }, + { 125, 3, 1, 0x0000002078000000ull, 0x000001eff8000000ull, { 32, 25, 0, 0, 0 }, 0x8, 1143, }, + { 127, 3, 1, 0x0000008000000000ull, 0x000001fff8000000ull, { 24, 28, 0, 0, 0 }, 0x0, 70, }, + { 127, 3, 1, 0x0000009000000000ull, 0x000001fff8000000ull, { 24, 28, 25, 0, 0 }, 0x400, 71, }, + { 127, 3, 1, 0x000000a000000000ull, 0x000001eff0000000ull, { 24, 28, 63, 0, 0 }, 0x400, 72, }, + { 128, 3, 2, 0x0000008a08000000ull, 0x000001fff8000000ull, { 24, 1, 28, 0, 0 }, 0x0, 73, }, + { 128, 3, 1, 0x0000008a08000000ull, 0x000001fff8000000ull, { 24, 28, 0, 0, 0 }, 0x40, 74, }, + { 129, 3, 1, 0x0000008040000000ull, 0x000001fff8000000ull, { 24, 28, 0, 0, 0 }, 0x0, 75, }, + { 129, 3, 1, 0x0000009040000000ull, 0x000001fff8000000ull, { 24, 28, 25, 0, 0 }, 0x400, 76, }, + { 129, 3, 1, 0x000000a040000000ull, 0x000001eff0000000ull, { 24, 28, 63, 0, 0 }, 0x400, 77, }, + { 130, 3, 1, 0x0000008080000000ull, 0x000001fff8000000ull, { 24, 28, 0, 0, 0 }, 0x0, 78, }, + { 130, 3, 1, 0x0000009080000000ull, 0x000001fff8000000ull, { 24, 28, 25, 0, 0 }, 0x400, 79, }, + { 130, 3, 1, 0x000000a080000000ull, 0x000001eff0000000ull, { 24, 28, 63, 0, 0 }, 0x400, 80, }, + { 131, 3, 1, 0x00000080c0000000ull, 0x000001fff8000000ull, { 24, 28, 0, 0, 0 }, 0x0, 81, }, + { 131, 3, 1, 0x00000080c0000000ull, 0x000001fff8000000ull, { 24, 28, 84, 0, 0 }, 0x0, 1339, }, + { 131, 3, 1, 0x00000090c0000000ull, 0x000001fff8000000ull, { 24, 28, 25, 0, 0 }, 0x400, 82, }, + { 131, 3, 1, 0x000000a0c0000000ull, 0x000001eff0000000ull, { 24, 28, 63, 0, 0 }, 0x400, 83, }, + { 132, 3, 1, 0x000000c6c0000000ull, 0x000001fff8000000ull, { 18, 28, 0, 0, 0 }, 0x0, 1039, }, + { 132, 3, 1, 0x000000d6c0000000ull, 0x000001fff8000000ull, { 18, 28, 25, 0, 0 }, 0x400, 1040, }, + { 132, 3, 1, 0x000000e6c0000000ull, 0x000001eff0000000ull, { 18, 28, 63, 0, 0 }, 0x400, 1041, }, + { 133, 3, 1, 0x000000c040000000ull, 0x000001fff8000000ull, { 18, 28, 0, 0, 0 }, 0x0, 84, }, + { 133, 3, 1, 0x000000d040000000ull, 0x000001fff8000000ull, { 18, 28, 25, 0, 0 }, 0x400, 85, }, + { 133, 3, 1, 0x000000e040000000ull, 0x000001eff0000000ull, { 18, 28, 63, 0, 0 }, 0x400, 86, }, + { 134, 3, 1, 0x000000c0c0000000ull, 0x000001fff8000000ull, { 18, 28, 0, 0, 0 }, 0x0, 87, }, + { 134, 3, 1, 0x000000d0c0000000ull, 0x000001fff8000000ull, { 18, 28, 25, 0, 0 }, 0x400, 88, }, + { 134, 3, 1, 0x000000e0c0000000ull, 0x000001eff0000000ull, { 18, 28, 63, 0, 0 }, 0x400, 89, }, + { 135, 3, 1, 0x000000c000000000ull, 0x000001fff8000000ull, { 18, 28, 0, 0, 0 }, 0x0, 90, }, + { 135, 3, 1, 0x000000d000000000ull, 0x000001fff8000000ull, { 18, 28, 25, 0, 0 }, 0x400, 91, }, + { 135, 3, 1, 0x000000e000000000ull, 0x000001eff0000000ull, { 18, 28, 63, 0, 0 }, 0x400, 92, }, + { 136, 3, 2, 0x000000c048000000ull, 0x000001fff8000000ull, { 18, 19, 28, 0, 0 }, 0x0, 93, }, + { 136, 3, 2, 0x000000d048000000ull, 0x000001fff8000000ull, { 18, 19, 28, 6, 0 }, 0x400, 94, }, + { 137, 3, 2, 0x000000c0c8000000ull, 0x000001fff8000000ull, { 18, 19, 28, 0, 0 }, 0x0, 95, }, + { 137, 3, 2, 0x000000d0c8000000ull, 0x000001fff8000000ull, { 18, 19, 28, 6, 0 }, 0x400, 96, }, + { 138, 3, 2, 0x000000c088000000ull, 0x000001fff8000000ull, { 18, 19, 28, 0, 0 }, 0x0, 97, }, + { 138, 3, 2, 0x000000d088000000ull, 0x000001fff8000000ull, { 18, 19, 28, 5, 0 }, 0x400, 98, }, + { 139, 3, 1, 0x000000c080000000ull, 0x000001fff8000000ull, { 18, 28, 0, 0, 0 }, 0x0, 99, }, + { 139, 3, 1, 0x000000d080000000ull, 0x000001fff8000000ull, { 18, 28, 25, 0, 0 }, 0x400, 100, }, + { 139, 3, 1, 0x000000e080000000ull, 0x000001eff0000000ull, { 18, 28, 63, 0, 0 }, 0x400, 101, }, + { 142, 3, 0, 0x000000cb00000000ull, 0x000001fff8000000ull, { 28, 0, 0, 0, 0 }, 0x0, 102, }, + { 142, 3, 0, 0x000000db00000000ull, 0x000001fff8000000ull, { 28, 25, 0, 0, 0 }, 0x400, 103, }, + { 142, 3, 0, 0x000000eb00000000ull, 0x000001eff0000000ull, { 28, 63, 0, 0, 0 }, 0x400, 104, }, + { 143, 3, 0, 0x0000000050000000ull, 0x000001eff8000000ull, { 0, 0, 0, 0, 0 }, 0x21, 105, }, + { 151, 3, 0, 0x0000000110000000ull, 0x000001eff8000000ull, { 0, 0, 0, 0, 0 }, 0x0, 106, }, + { 152, 2, 1, 0x000000e880000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 2203, }, + { 153, 2, 1, 0x000000ea80000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 2204, }, + { 154, 2, 1, 0x000000f880000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 2205, }, + { 155, 1, 1, 0x0000010800000000ull, 0x000001fff80fe000ull, { 24, 26, 0, 0, 0 }, 0x0, 107, }, + { 155, 1, 1, 0x0000012000000000ull, 0x000001e000300000ull, { 24, 67, 0, 0, 0 }, 0x40, 108, }, + { 155, 5, 1, 0x0000000080000000ull, 0x000001e3f8000000ull, { 18, 20, 0, 0, 0 }, 0xc0, 109, }, + { 155, 2, 1, 0x0000000e00100000ull, 0x000001ee00f00000ull, { 15, 25, 0, 0, 0 }, 0x40, 110, }, + { 155, 2, 1, 0x0000000e00000000ull, 0x000001ee00f00000ull, { 15, 25, 79, 0, 0 }, 0x0, 2855, }, + { 155, 2, 1, 0x0000000188000000ull, 0x000001eff8000000ull, { 24, 16, 0, 0, 0 }, 0x0, 112, }, + { 155, 2, 1, 0x0000000600000000ull, 0x000001ee00000000ull, { 9, 25, 65, 0, 0 }, 0x0, 113, }, + { 155, 2, 1, 0x00000016ff001fc0ull, 0x000001feff001fc0ull, { 9, 25, 0, 0, 0 }, 0x40, 114, }, + { 155, 2, 1, 0x0000000400000000ull, 0x000001ee00000000ull, { 10, 69, 0, 0, 0 }, 0x0, 115, }, + { 155, 2, 1, 0x0000000180000000ull, 0x000001eff8000000ull, { 24, 8, 0, 0, 0 }, 0x0, 116, }, + { 155, 2, 1, 0x0000000198000000ull, 0x000001eff8000000ull, { 24, 9, 0, 0, 0 }, 0x0, 117, }, + { 155, 2, 1, 0x0000000150000000ull, 0x000001eff8000000ull, { 14, 25, 0, 0, 0 }, 0x0, 1144, }, + { 155, 2, 1, 0x0000000050000000ull, 0x000001eff8000000ull, { 14, 56, 0, 0, 0 }, 0x0, 1145, }, + { 155, 2, 1, 0x0000000190000000ull, 0x000001eff8000000ull, { 24, 14, 0, 0, 0 }, 0x0, 1146, }, + { 155, 3, 1, 0x0000000140000000ull, 0x000001eff8000000ull, { 14, 56, 0, 0, 0 }, 0x0, 1268, }, + { 155, 3, 1, 0x0000002150000000ull, 0x000001eff8000000ull, { 14, 25, 0, 0, 0 }, 0x0, 1269, }, + { 155, 3, 1, 0x0000002110000000ull, 0x000001eff8000000ull, { 24, 14, 0, 0, 0 }, 0x0, 1270, }, + { 155, 3, 1, 0x0000002160000000ull, 0x000001eff8000000ull, { 17, 25, 0, 0, 0 }, 0x8, 118, }, + { 155, 3, 1, 0x0000002120000000ull, 0x000001eff8000000ull, { 24, 17, 0, 0, 0 }, 0x8, 119, }, + { 155, 3, 1, 0x0000002168000000ull, 0x000001eff8000000ull, { 12, 25, 0, 0, 0 }, 0x8, 120, }, + { 155, 3, 1, 0x0000002148000000ull, 0x000001eff8000000ull, { 13, 25, 0, 0, 0 }, 0x0, 121, }, + { 155, 3, 1, 0x0000002128000000ull, 0x000001eff8000000ull, { 24, 11, 0, 0, 0 }, 0x8, 122, }, + { 155, 3, 1, 0x0000002108000000ull, 0x000001eff8000000ull, { 24, 13, 0, 0, 0 }, 0x0, 123, }, + { 155, 3, 1, 0x0000002000000000ull, 0x000001eff8000000ull, { 38, 25, 0, 0, 0 }, 0x8, 124, }, + { 155, 3, 1, 0x0000002008000000ull, 0x000001eff8000000ull, { 30, 25, 0, 0, 0 }, 0x8, 125, }, + { 155, 3, 1, 0x0000002010000000ull, 0x000001eff8000000ull, { 33, 25, 0, 0, 0 }, 0x8, 126, }, + { 155, 3, 1, 0x0000002018000000ull, 0x000001eff8000000ull, { 35, 25, 0, 0, 0 }, 0x8, 127, }, + { 155, 3, 1, 0x0000002020000000ull, 0x000001eff8000000ull, { 36, 25, 0, 0, 0 }, 0x8, 128, }, + { 155, 3, 1, 0x0000002028000000ull, 0x000001eff8000000ull, { 37, 25, 0, 0, 0 }, 0x8, 129, }, + { 155, 3, 1, 0x0000002030000000ull, 0x000001eff8000000ull, { 34, 25, 0, 0, 0 }, 0x8, 130, }, + { 155, 3, 1, 0x0000002080000000ull, 0x000001eff8000000ull, { 24, 38, 0, 0, 0 }, 0x8, 131, }, + { 155, 3, 1, 0x0000002088000000ull, 0x000001eff8000000ull, { 24, 30, 0, 0, 0 }, 0x8, 132, }, + { 155, 3, 1, 0x0000002090000000ull, 0x000001eff8000000ull, { 24, 33, 0, 0, 0 }, 0x8, 133, }, + { 155, 3, 1, 0x0000002098000000ull, 0x000001eff8000000ull, { 24, 35, 0, 0, 0 }, 0x8, 134, }, + { 155, 3, 1, 0x00000020a0000000ull, 0x000001eff8000000ull, { 24, 36, 0, 0, 0 }, 0x8, 135, }, + { 155, 3, 1, 0x00000020a8000000ull, 0x000001eff8000000ull, { 24, 37, 0, 0, 0 }, 0x0, 136, }, + { 155, 3, 1, 0x00000020b0000000ull, 0x000001eff8000000ull, { 24, 34, 0, 0, 0 }, 0x8, 137, }, + { 155, 3, 1, 0x00000020b8000000ull, 0x000001eff8000000ull, { 24, 29, 0, 0, 0 }, 0x0, 138, }, + { 155, 7, 1, 0x0000000000000000ull, 0x0000000000000000ull, { 24, 14, 0, 0, 0 }, 0x0, 139, }, + { 155, 7, 1, 0x0000000000000000ull, 0x0000000000000000ull, { 14, 56, 0, 0, 0 }, 0x0, 140, }, + { 155, 7, 1, 0x0000000000000000ull, 0x0000000000000000ull, { 14, 25, 0, 0, 0 }, 0x0, 141, }, + { 156, 6, 1, 0x000000c000000000ull, 0x000001e000100000ull, { 24, 71, 0, 0, 0 }, 0x0, 142, }, + { 157, 2, 1, 0x000000eca0000000ull, 0x000001fff0000000ull, { 24, 25, 75, 0, 0 }, 0x0, 143, }, + { 158, 2, 1, 0x000000eea0000000ull, 0x000001fff0000000ull, { 24, 25, 76, 0, 0 }, 0x0, 144, }, + { 168, 4, 0, 0x0000004000000000ull, 0x000001e1f8000000ull, { 66, 0, 0, 0, 0 }, 0x0, 539, }, + { 168, 5, 0, 0x0000000008000000ull, 0x000001e3fc000000ull, { 66, 0, 0, 0, 0 }, 0x0, 962, }, + { 168, 2, 0, 0x0000000008000000ull, 0x000001effc000000ull, { 66, 0, 0, 0, 0 }, 0x2, 1147, }, + { 168, 3, 0, 0x0000000008000000ull, 0x000001effc000000ull, { 66, 0, 0, 0, 0 }, 0x0, 1271, }, + { 168, 6, 0, 0x0000000008000000ull, 0x000001effc000000ull, { 70, 0, 0, 0, 0 }, 0x0, 3035, }, + { 168, 7, 0, 0x0000000000000000ull, 0x0000000000000000ull, { 66, 0, 0, 0, 0 }, 0x0, 145, }, + { 175, 1, 1, 0x0000010070000000ull, 0x000001eff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 146, }, + { 175, 1, 1, 0x0000010170000000ull, 0x000001eff8000000ull, { 24, 56, 26, 0, 0 }, 0x0, 147, }, + { 178, 2, 1, 0x000000ea00000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 3017, }, + { 179, 2, 1, 0x000000f820000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 2857, }, + { 180, 1, 1, 0x0000010400000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 148, }, + { 181, 1, 1, 0x0000010600000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 149, }, + { 182, 1, 1, 0x0000011400000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 150, }, + { 183, 1, 1, 0x0000010450000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 151, }, + { 184, 1, 1, 0x0000010650000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 152, }, + { 185, 1, 1, 0x0000010470000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 153, }, + { 186, 1, 1, 0x0000010670000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 154, }, + { 187, 1, 1, 0x0000010520000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 948, }, + { 188, 1, 1, 0x0000010720000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 949, }, + { 189, 1, 1, 0x0000011520000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 950, }, + { 190, 2, 1, 0x000000e850000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 2871, }, + { 191, 2, 1, 0x000000ea70000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 155, }, + { 192, 2, 1, 0x000000e810000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 2872, }, + { 193, 2, 1, 0x000000ea30000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 156, }, + { 194, 2, 1, 0x000000ead0000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 2206, }, + { 195, 2, 1, 0x000000e230000000ull, 0x000001ff30000000ull, { 24, 25, 26, 42, 0 }, 0x0, 157, }, + { 196, 2, 1, 0x000000e690000000ull, 0x000001fff0000000ull, { 24, 26, 0, 0, 0 }, 0x0, 158, }, + { 198, 3, 1, 0x00000021c0000000ull, 0x000001eff8000000ull, { 24, 26, 25, 0, 0 }, 0x0, 2207, }, + { 198, 3, 1, 0x00000020c0000000ull, 0x000001eff8000000ull, { 24, 26, 49, 0, 0 }, 0x0, 2208, }, + { 198, 3, 0, 0x0000002188000000ull, 0x000001eff8000000ull, { 26, 49, 0, 0, 0 }, 0x0, 2238, }, + { 199, 2, 1, 0x000000e8b0000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 159, }, + { 200, 2, 1, 0x000000e240000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 160, }, + { 200, 2, 1, 0x000000ee50000000ull, 0x000001fff0000000ull, { 24, 25, 39, 0, 0 }, 0x0, 161, }, + { 201, 2, 1, 0x000000f040000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 162, }, + { 201, 2, 1, 0x000000fc50000000ull, 0x000001fff0000000ull, { 24, 25, 39, 0, 0 }, 0x0, 163, }, + { 202, 1, 1, 0x0000010680000000ull, 0x000001ffe0000000ull, { 24, 25, 41, 26, 0 }, 0x0, 164, }, + { 203, 2, 1, 0x000000e220000000ull, 0x000001fff0000000ull, { 24, 26, 25, 0, 0 }, 0x0, 165, }, + { 203, 2, 1, 0x000000e630000000ull, 0x000001fff0000000ull, { 24, 26, 43, 0, 0 }, 0x0, 166, }, + { 204, 2, 1, 0x000000f020000000ull, 0x000001fff0000000ull, { 24, 26, 25, 0, 0 }, 0x0, 167, }, + { 204, 2, 1, 0x000000f430000000ull, 0x000001fff0000000ull, { 24, 26, 43, 0, 0 }, 0x0, 168, }, + { 205, 1, 1, 0x00000106c0000000ull, 0x000001ffe0000000ull, { 24, 25, 41, 26, 0 }, 0x0, 169, }, + { 206, 1, 1, 0x0000010420000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 170, }, + { 207, 1, 1, 0x0000010620000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 171, }, + { 208, 1, 1, 0x0000011420000000ull, 0x000001fff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 172, }, + { 209, 3, 0, 0x0000002048000000ull, 0x000001eff8000000ull, { 26, 25, 0, 0, 0 }, 0x8, 1175, }, + { 209, 3, 0, 0x0000002050000000ull, 0x000001eff8000000ull, { 26, 25, 0, 0, 0 }, 0xc, 1050, }, + { 209, 3, 0, 0x00000021a0000000ull, 0x000001eff8000000ull, { 26, 0, 0, 0, 0 }, 0x8, 922, }, + { 210, 3, 0, 0x0000002060000000ull, 0x000001eff8000000ull, { 26, 25, 0, 0, 0 }, 0x8, 848, }, + { 215, 4, 0, 0x0000000040000000ull, 0x000001e1f8000000ull, { 0, 0, 0, 0, 0 }, 0x22c, 173, }, + { 216, 3, 0, 0x0000000038000000ull, 0x000001ee78000000ull, { 68, 0, 0, 0, 0 }, 0x8, 174, }, + { 217, 3, 0, 0x0000000028000000ull, 0x000001ee78000000ull, { 68, 0, 0, 0, 0 }, 0x0, 175, }, + { 226, 3, 1, 0x000000c708000000ull, 0x000001ffc8000000ull, { 18, 25, 0, 0, 0 }, 0x0, 2782, }, + { 227, 2, 1, 0x000000a600000000ull, 0x000001ee04000000ull, { 24, 25, 45, 0, 0 }, 0x140, 176, }, + { 227, 2, 1, 0x000000f240000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 177, }, + { 228, 1, 1, 0x0000010080000000ull, 0x000001efe0000000ull, { 24, 25, 40, 26, 0 }, 0x0, 178, }, + { 229, 1, 1, 0x00000100c0000000ull, 0x000001efe0000000ull, { 24, 25, 40, 26, 0 }, 0x0, 179, }, + { 230, 2, 1, 0x000000a400000000ull, 0x000001ee00002000ull, { 24, 26, 77, 0, 0 }, 0x140, 2878, }, + { 230, 2, 1, 0x000000f220000000ull, 0x000001fff0000000ull, { 24, 26, 25, 0, 0 }, 0x0, 181, }, + { 231, 2, 1, 0x000000ac00000000ull, 0x000001ee00000000ull, { 24, 25, 26, 44, 0 }, 0x0, 182, }, + { 236, 3, 0, 0x0000000180000000ull, 0x000001eff8000000ull, { 0, 0, 0, 0, 0 }, 0x0, 850, }, + { 237, 3, 0, 0x0000000030000000ull, 0x000001ee78000000ull, { 68, 0, 0, 0, 0 }, 0x8, 183, }, + { 239, 3, 1, 0x0000008c00000000ull, 0x000001fff8000000ull, { 28, 25, 0, 0, 0 }, 0x0, 184, }, + { 239, 3, 1, 0x000000ac00000000ull, 0x000001eff0000000ull, { 28, 25, 62, 0, 0 }, 0x400, 185, }, + { 240, 3, 1, 0x0000008c08000000ull, 0x000001fff8000000ull, { 28, 25, 1, 0, 0 }, 0x0, 186, }, + { 240, 3, 1, 0x0000008c08000000ull, 0x000001fff8000000ull, { 28, 25, 0, 0, 0 }, 0x40, 187, }, + { 241, 3, 1, 0x0000008c40000000ull, 0x000001fff8000000ull, { 28, 25, 0, 0, 0 }, 0x0, 188, }, + { 241, 3, 1, 0x000000ac40000000ull, 0x000001eff0000000ull, { 28, 25, 62, 0, 0 }, 0x400, 189, }, + { 242, 3, 1, 0x0000008c80000000ull, 0x000001fff8000000ull, { 28, 25, 0, 0, 0 }, 0x0, 190, }, + { 242, 3, 1, 0x000000ac80000000ull, 0x000001eff0000000ull, { 28, 25, 62, 0, 0 }, 0x400, 191, }, + { 243, 3, 1, 0x0000008cc0000000ull, 0x000001fff8000000ull, { 28, 25, 0, 0, 0 }, 0x0, 192, }, + { 243, 3, 1, 0x000000acc0000000ull, 0x000001eff0000000ull, { 28, 25, 62, 0, 0 }, 0x400, 193, }, + { 244, 3, 1, 0x000000cec0000000ull, 0x000001fff8000000ull, { 28, 19, 0, 0, 0 }, 0x0, 2785, }, + { 244, 3, 1, 0x000000eec0000000ull, 0x000001eff0000000ull, { 28, 19, 62, 0, 0 }, 0x400, 2786, }, + { 245, 3, 1, 0x000000cc40000000ull, 0x000001fff8000000ull, { 28, 19, 0, 0, 0 }, 0x0, 194, }, + { 245, 3, 1, 0x000000ec40000000ull, 0x000001eff0000000ull, { 28, 19, 62, 0, 0 }, 0x400, 195, }, + { 246, 3, 1, 0x000000ccc0000000ull, 0x000001fff8000000ull, { 28, 19, 0, 0, 0 }, 0x0, 196, }, + { 246, 3, 1, 0x000000ecc0000000ull, 0x000001eff0000000ull, { 28, 19, 62, 0, 0 }, 0x400, 197, }, + { 247, 3, 1, 0x000000cc00000000ull, 0x000001fff8000000ull, { 28, 19, 0, 0, 0 }, 0x0, 198, }, + { 247, 3, 1, 0x000000ec00000000ull, 0x000001eff0000000ull, { 28, 19, 62, 0, 0 }, 0x400, 199, }, + { 248, 3, 1, 0x000000cc80000000ull, 0x000001fff8000000ull, { 28, 19, 0, 0, 0 }, 0x0, 200, }, + { 248, 3, 1, 0x000000ec80000000ull, 0x000001eff0000000ull, { 28, 19, 62, 0, 0 }, 0x400, 201, }, + { 249, 1, 1, 0x0000010028000000ull, 0x000001eff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 202, }, + { 249, 1, 1, 0x0000010020000000ull, 0x000001eff8000000ull, { 24, 25, 26, 4, 0 }, 0x0, 203, }, + { 249, 1, 1, 0x0000010128000000ull, 0x000001eff8000000ull, { 24, 56, 26, 0, 0 }, 0x0, 204, }, + { 250, 3, 0, 0x0000000020000000ull, 0x000001ee78000000ull, { 68, 0, 0, 0, 0 }, 0x0, 205, }, + { 251, 2, 1, 0x00000000a0000000ull, 0x000001eff8000000ull, { 24, 26, 0, 0, 0 }, 0x0, 206, }, + { 252, 2, 1, 0x00000000a8000000ull, 0x000001eff8000000ull, { 24, 26, 0, 0, 0 }, 0x0, 207, }, + { 253, 2, 1, 0x00000000b0000000ull, 0x000001eff8000000ull, { 24, 26, 0, 0, 0 }, 0x0, 208, }, + { 254, 3, 0, 0x0000000198000000ull, 0x000001eff8000000ull, { 0, 0, 0, 0, 0 }, 0x0, 1150, }, + { 255, 3, 1, 0x00000020f8000000ull, 0x000001eff8000000ull, { 24, 26, 0, 0, 0 }, 0x8, 209, }, + { 256, 2, 2, 0x000000a000000000ull, 0x000001fe00003000ull, { 22, 23, 26, 77, 0 }, 0x0, 3040, }, + { 256, 2, 1, 0x000000a000000000ull, 0x000001fe00003000ull, { 22, 26, 77, 0, 0 }, 0x40, 3041, }, + { 256, 2, 2, 0x000000a000000000ull, 0x000001fe00003000ull, { 23, 22, 26, 77, 0 }, 0x40, 2003, }, + { 256, 2, 1, 0x000000a000000000ull, 0x000001fe00003000ull, { 23, 26, 77, 0, 0 }, 0x40, 2004, }, + { 257, 2, 2, 0x000000a000082000ull, 0x000001fe00083000ull, { 22, 23, 50, 0, 0 }, 0x0, 3044, }, + { 257, 2, 1, 0x000000a000082000ull, 0x000001fe00083000ull, { 22, 50, 0, 0, 0 }, 0x40, 3045, }, + { 257, 2, 2, 0x000000a000082000ull, 0x000001fe00083000ull, { 23, 22, 50, 0, 0 }, 0x40, 2007, }, + { 257, 2, 1, 0x000000a000082000ull, 0x000001fe00083000ull, { 23, 50, 0, 0, 0 }, 0x40, 2008, }, + { 258, 3, 1, 0x00000020d0000000ull, 0x000001eff8000000ull, { 24, 26, 0, 0, 0 }, 0x0, 210, }, + { 259, 2, 2, 0x000000a000002000ull, 0x000001fe00003000ull, { 22, 23, 26, 0, 0 }, 0x0, 3048, }, + { 259, 2, 1, 0x000000a000002000ull, 0x000001fe00003000ull, { 22, 26, 0, 0, 0 }, 0x40, 3049, }, + { 259, 2, 2, 0x000000a000002000ull, 0x000001fe00003000ull, { 23, 22, 26, 0, 0 }, 0x40, 2011, }, + { 259, 2, 1, 0x000000a000002000ull, 0x000001fe00003000ull, { 23, 26, 0, 0, 0 }, 0x40, 2012, }, + { 260, 3, 1, 0x00000020f0000000ull, 0x000001eff8000000ull, { 24, 26, 0, 0, 0 }, 0x8, 211, }, + { 262, 3, 1, 0x00000020d8000000ull, 0x000001eff8000000ull, { 24, 26, 0, 0, 0 }, 0x0, 212, }, + { 266, 2, 1, 0x000000e840000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 1131, }, + { 267, 2, 1, 0x000000ea40000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 1132, }, + { 268, 2, 1, 0x000000f840000000ull, 0x000001fff0000000ull, { 24, 25, 26, 0, 0 }, 0x0, 1133, }, + { 272, 4, 0, 0x00000000c0000000ull, 0x000001e1f8000000ull, { 0, 0, 0, 0, 0 }, 0x28, 223, }, + { 277, 3, 1, 0x0000008208000000ull, 0x000001fff8000000ull, { 24, 28, 25, 0, 0 }, 0x0, 213, }, + { 278, 3, 1, 0x0000008248000000ull, 0x000001fff8000000ull, { 24, 28, 25, 0, 0 }, 0x0, 214, }, + { 279, 3, 1, 0x0000008288000000ull, 0x000001fff8000000ull, { 24, 28, 25, 0, 0 }, 0x0, 215, }, + { 280, 3, 1, 0x00000082c8000000ull, 0x000001fff8000000ull, { 24, 28, 25, 0, 0 }, 0x0, 216, }, + { 282, 5, 1, 0x000001d000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x0, 1179, }, + { 282, 5, 1, 0x000001d000000000ull, 0x000001fc00000000ull, { 18, 20, 21, 19, 0 }, 0x40, 1261, }, + { 283, 5, 1, 0x000001d000000000ull, 0x000001fc000fe000ull, { 18, 20, 21, 0, 0 }, 0x40, 1180, }, + { 284, 1, 1, 0x0000010078000000ull, 0x000001eff8000000ull, { 24, 25, 26, 0, 0 }, 0x0, 217, }, + { 284, 1, 1, 0x0000010178000000ull, 0x000001eff8000000ull, { 24, 56, 26, 0, 0 }, 0x0, 218, }, + { 287, 2, 1, 0x0000000080000000ull, 0x000001eff8000000ull, { 24, 26, 0, 0, 0 }, 0x0, 219, }, + { 288, 2, 1, 0x0000000088000000ull, 0x000001eff8000000ull, { 24, 26, 0, 0, 0 }, 0x0, 220, }, + { 289, 2, 1, 0x0000000090000000ull, 0x000001eff8000000ull, { 24, 26, 0, 0, 0 }, 0x0, 221, }, +}; + +static const char dis_table[] = { +0xa0, 0xc7, 0xc8, 0xa0, 0x2e, 0xd8, 0xa0, 0x2c, 0xc0, 0xa0, 0x1c, 0x00, +0x98, 0xb0, 0x02, 0x50, 0x90, 0x50, 0x90, 0x28, 0x24, 0x39, 0x28, 0x24, +0x39, 0x20, 0x90, 0x28, 0x24, 0x39, 0x18, 0x24, 0x39, 0x10, 0x91, 0x60, +0x90, 0x28, 0x24, 0x39, 0x00, 0x10, 0x10, 0x58, 0x41, 0x61, 0xc7, 0xc0, +0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, +0x10, 0x10, 0x52, 0xc0, 0xc0, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, +0x10, 0x10, 0x10, 0x24, 0x24, 0x70, 0x90, 0x28, 0x24, 0x38, 0xf0, 0x24, +0x38, 0xe8, 0xa8, 0x0b, 0x48, 0x15, 0x20, 0x97, 0x20, 0x95, 0xc8, 0x9a, +0xb8, 0x05, 0x38, 0x91, 0x18, 0x90, 0xa0, 0x90, 0x60, 0x80, 0x90, 0x20, +0x34, 0xa6, 0xa4, 0x25, 0x00, 0x34, 0xa3, 0x80, 0xa4, 0x36, 0xa0, 0x36, +0xd9, 0x90, 0x50, 0x90, 0x28, 0x80, 0x36, 0xcf, 0x80, 0x34, 0x86, 0x81, +0x33, 0xe2, 0x90, 0xe0, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x24, 0x10, 0x34, +0x83, 0xa4, 0x1f, 0x08, 0x34, 0x80, 0x90, 0x38, 0xa4, 0x38, 0xa0, 0x37, +0x1a, 0xa4, 0x38, 0x48, 0x37, 0x0e, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x37, +0x20, 0x36, 0xef, 0xa4, 0x36, 0xf8, 0x36, 0xea, 0x80, 0xa4, 0x23, 0xf0, +0x34, 0x7f, 0x92, 0x18, 0x91, 0xc0, 0x80, 0x91, 0x80, 0x90, 0xf8, 0xdb, +0x84, 0x60, 0xf9, 0x40, 0xc0, 0xc0, 0x80, 0xa4, 0x42, 0x68, 0x8c, 0x43, +0xc8, 0x84, 0x38, 0x83, 0xc0, 0xc0, 0x80, 0xa4, 0x42, 0x58, 0x8c, 0x43, +0xa8, 0x84, 0x38, 0x81, 0xd3, 0x82, 0x40, 0x50, 0xc0, 0xc0, 0x81, 0x38, +0x35, 0x50, 0xc0, 0xc0, 0x81, 0x38, 0x33, 0xa4, 0x1f, 0x18, 0x33, 0xe4, +0x80, 0x90, 0x28, 0x80, 0x33, 0xe0, 0x80, 0x34, 0x88, 0x81, 0x90, 0x38, +0xa4, 0x24, 0x80, 0x34, 0x8b, 0xa4, 0x24, 0x48, 0x34, 0x85, 0xc0, 0x40, +0x10, 0x10, 0x90, 0x38, 0xa4, 0x1e, 0xf0, 0x33, 0xdf, 0xa4, 0x1e, 0xe0, +0x33, 0xdd, 0x18, 0x24, 0x24, 0xf8, 0x83, 0x90, 0xa8, 0xd3, 0x82, 0xc0, +0xc0, 0xc0, 0x80, 0xa4, 0x42, 0x38, 0x38, 0x6d, 0xc0, 0xc0, 0x80, 0xa4, +0x42, 0x28, 0x38, 0x69, 0xd3, 0x82, 0x40, 0x50, 0xc0, 0xc0, 0x81, 0x38, +0x2f, 0x50, 0xc0, 0xc0, 0x81, 0x38, 0x2d, 0x92, 0xb8, 0x99, 0x84, 0x24, +0x68, 0x90, 0x78, 0x90, 0x50, 0x10, 0x10, 0x80, 0xa4, 0x36, 0x98, 0x36, +0xd8, 0x82, 0x36, 0xce, 0x90, 0x80, 0x10, 0x10, 0x90, 0x38, 0xa4, 0x38, +0x98, 0x37, 0x19, 0xa4, 0x38, 0x40, 0x37, 0x0d, 0x80, 0x90, 0x38, 0xa4, +0x37, 0x18, 0x36, 0xee, 0xa4, 0x36, 0xf0, 0x36, 0xe9, 0x83, 0x90, 0xa8, +0xd3, 0x82, 0xc0, 0xc0, 0xc0, 0x80, 0xa4, 0x42, 0x08, 0x38, 0x61, 0xc0, +0xc0, 0x80, 0xa4, 0x41, 0xf8, 0x38, 0x5d, 0xd3, 0x82, 0x40, 0x50, 0xc0, +0xc0, 0x81, 0x38, 0x29, 0x50, 0xc0, 0xc0, 0x81, 0x38, 0x27, 0x18, 0x24, +0x24, 0x78, 0x83, 0x90, 0xa8, 0xd3, 0x82, 0xc0, 0xc0, 0xc0, 0x80, 0xa4, +0x41, 0xd8, 0x38, 0x55, 0xc0, 0xc0, 0x80, 0xa4, 0x41, 0xc8, 0x38, 0x51, +0xd3, 0x82, 0x40, 0x50, 0xc0, 0xc0, 0x81, 0x38, 0x23, 0x50, 0xc0, 0xc0, +0x81, 0x38, 0x21, 0x94, 0x50, 0x92, 0xf8, 0x99, 0x84, 0x1f, 0x48, 0x90, +0x78, 0x90, 0x50, 0x10, 0x10, 0x80, 0xa4, 0x36, 0x90, 0x36, 0xd7, 0x82, +0x36, 0xcd, 0x90, 0x80, 0x10, 0x10, 0x90, 0x38, 0xa4, 0x38, 0x90, 0x37, +0x18, 0xa4, 0x38, 0x38, 0x37, 0x0c, 0x80, 0x90, 0x38, 0xa4, 0x37, 0x10, +0x36, 0xed, 0xa4, 0x36, 0xe8, 0x36, 0xe8, 0x83, 0x90, 0xe8, 0xd3, 0x83, +0xc0, 0xc0, 0xc0, 0x80, 0xa4, 0x42, 0x78, 0x8c, 0x43, 0xe8, 0x84, 0x38, +0x85, 0xc0, 0xc0, 0x80, 0xa4, 0x42, 0x60, 0x8c, 0x43, 0xb8, 0x84, 0x38, +0x82, 0xd3, 0x82, 0x40, 0x50, 0xc0, 0xc0, 0x81, 0x38, 0x37, 0x50, 0xc0, +0xc0, 0x81, 0x38, 0x34, 0x18, 0x24, 0x1f, 0x40, 0x83, 0x90, 0xa8, 0xd3, +0x82, 0xc0, 0xc0, 0xc0, 0x80, 0xa4, 0x42, 0x48, 0x38, 0x71, 0xc0, 0xc0, +0x80, 0xa4, 0x42, 0x30, 0x38, 0x6b, 0xd3, 0x82, 0x40, 0x50, 0xc0, 0xc0, +0x81, 0x38, 0x31, 0x50, 0xc0, 0xc0, 0x81, 0x38, 0x2e, 0x92, 0xb8, 0x99, +0x84, 0x1f, 0x38, 0x90, 0x78, 0x90, 0x50, 0x10, 0x10, 0x80, 0xa4, 0x36, +0x88, 0x36, 0xd6, 0x82, 0x36, 0xcc, 0x90, 0x80, 0x10, 0x10, 0x90, 0x38, +0xa4, 0x38, 0x88, 0x37, 0x17, 0xa4, 0x38, 0x30, 0x37, 0x0b, 0x80, 0x90, +0x38, 0xa4, 0x37, 0x08, 0x36, 0xec, 0xa4, 0x36, 0xe0, 0x36, 0xe7, 0x83, +0x90, 0xa8, 0xd3, 0x82, 0xc0, 0xc0, 0xc0, 0x80, 0xa4, 0x42, 0x18, 0x38, +0x65, 0xc0, 0xc0, 0x80, 0xa4, 0x42, 0x00, 0x38, 0x5f, 0xd3, 0x82, 0x40, +0x50, 0xc0, 0xc0, 0x81, 0x38, 0x2b, 0x50, 0xc0, 0xc0, 0x81, 0x38, 0x28, +0x18, 0x20, 0x01, 0x48, 0x83, 0x90, 0xa8, 0xd3, 0x82, 0xc0, 0xc0, 0xc0, +0x80, 0xa4, 0x41, 0xe8, 0x38, 0x59, 0xc0, 0xc0, 0x80, 0xa4, 0x41, 0xd0, +0x38, 0x53, 0xd3, 0x82, 0x40, 0x50, 0xc0, 0xc0, 0x81, 0x38, 0x25, 0x50, +0xc0, 0xc0, 0x81, 0x38, 0x22, 0xda, 0x06, 0xe0, 0xf9, 0x80, 0x90, 0x60, +0x90, 0x38, 0xa4, 0x24, 0xe8, 0x34, 0x9b, 0x80, 0x34, 0x98, 0x90, 0x38, +0xa4, 0x24, 0x90, 0x34, 0x96, 0x80, 0x34, 0x93, 0x90, 0x60, 0x90, 0x38, +0xa4, 0x24, 0xd0, 0x34, 0x9c, 0x80, 0x34, 0x99, 0x90, 0x38, 0xa4, 0x24, +0xa8, 0x34, 0x97, 0x80, 0x34, 0x94, 0xc8, 0x40, 0x19, 0x00, 0x91, 0x58, +0x90, 0x60, 0x82, 0x90, 0x20, 0x36, 0xcb, 0xa4, 0x36, 0x48, 0x36, 0xca, +0x90, 0xc0, 0x80, 0x90, 0x90, 0x90, 0x48, 0xc9, 0xe1, 0xc1, 0x00, 0x85, +0x37, 0x03, 0xc9, 0xe1, 0xc0, 0x40, 0x85, 0x37, 0x00, 0x80, 0x36, 0xff, +0x10, 0x10, 0x81, 0x36, 0xdb, 0x90, 0xa8, 0x10, 0x10, 0x90, 0x28, 0x81, +0x36, 0xf9, 0x90, 0x38, 0xa4, 0x37, 0xa0, 0x36, 0xf5, 0xa4, 0x37, 0x90, +0x36, 0xf3, 0x90, 0x70, 0x10, 0x10, 0x90, 0x38, 0xa4, 0x37, 0xb8, 0x36, +0xf8, 0x80, 0x36, 0xf6, 0x90, 0x60, 0x90, 0x28, 0x24, 0x37, 0xf0, 0xa4, +0x37, 0xe0, 0x36, 0xfd, 0x80, 0xa4, 0x37, 0xd0, 0x36, 0xfb, 0x80, 0x90, +0xf8, 0x90, 0x90, 0x90, 0x50, 0x90, 0x28, 0x80, 0x38, 0x17, 0x80, 0x38, +0x20, 0x80, 0xa4, 0x40, 0xf0, 0x38, 0x1f, 0x90, 0x28, 0x81, 0x38, 0x1d, +0x80, 0xa4, 0x40, 0xd8, 0x38, 0x1c, 0x90, 0x28, 0x82, 0x38, 0x1a, 0x81, +0xa4, 0x40, 0xc0, 0x38, 0x19, 0x98, 0xe8, 0x01, 0xb0, 0x90, 0x88, 0x90, +0x60, 0xa4, 0x36, 0x38, 0x10, 0x10, 0x10, 0x10, 0x83, 0x33, 0xb7, 0x24, +0x36, 0x30, 0x90, 0x28, 0x24, 0x36, 0x28, 0x24, 0x36, 0x20, 0x90, 0x88, +0x90, 0x60, 0xa4, 0x36, 0x10, 0x10, 0x10, 0x10, 0x10, 0x83, 0x33, 0xb6, +0x24, 0x36, 0x08, 0x90, 0x28, 0x24, 0x36, 0x00, 0x24, 0x35, 0xf8, 0xa8, +0x09, 0x00, 0x0e, 0x20, 0x96, 0x48, 0x95, 0xe8, 0x93, 0x38, 0x91, 0xa0, +0x90, 0xd0, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x1e, 0x60, 0x33, 0xcd, 0xa4, +0x1e, 0x50, 0x33, 0xcb, 0x90, 0x38, 0xa4, 0x1e, 0x40, 0x33, 0xc9, 0x80, +0x33, 0xc7, 0x90, 0x60, 0x90, 0x28, 0x24, 0x1e, 0x00, 0xa4, 0x1d, 0xf0, +0x33, 0xbf, 0x90, 0x38, 0xa4, 0x1d, 0xe0, 0x33, 0xbd, 0xa4, 0x1e, 0x28, +0x33, 0xc6, 0x90, 0xe0, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x1e, 0x18, 0x33, +0xc4, 0xa4, 0x1e, 0x08, 0x33, 0xc2, 0x90, 0x38, 0xa4, 0x35, 0xb0, 0x36, +0xbc, 0xa4, 0x35, 0x50, 0x36, 0xb0, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x32, +0x90, 0x36, 0x5e, 0xa4, 0x32, 0x60, 0x36, 0x58, 0x10, 0x10, 0xa4, 0x1d, +0xd0, 0x33, 0xbb, 0x99, 0x60, 0x02, 0x70, 0x90, 0x90, 0x90, 0x50, 0x90, +0x28, 0x24, 0x1e, 0x90, 0x80, 0x33, 0xda, 0x80, 0xa4, 0x1e, 0x98, 0x33, +0xd8, 0x90, 0x50, 0x90, 0x28, 0x24, 0x1e, 0xa0, 0x80, 0x33, 0xdb, 0x90, +0x38, 0xa4, 0x1e, 0xa8, 0x33, 0xd9, 0xa4, 0x1e, 0x70, 0x33, 0xcf, 0x90, +0xe0, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x34, 0xe8, 0x36, 0xa5, 0xa4, 0x34, +0x48, 0x36, 0x92, 0x90, 0x38, 0xa4, 0x33, 0xe0, 0x36, 0x83, 0xa4, 0x33, +0x50, 0x36, 0x72, 0x81, 0xa4, 0x1e, 0x80, 0x33, 0xd1, 0xe4, 0xa2, 0x04, +0x40, 0x38, 0x13, 0x18, 0x24, 0x1d, 0xc8, 0xe4, 0xe2, 0x02, 0xc0, 0x38, +0x0d, 0x92, 0x40, 0x91, 0x08, 0x10, 0x10, 0x90, 0x80, 0x10, 0x10, 0x90, +0x38, 0xa4, 0x35, 0xa8, 0x36, 0xbb, 0xa4, 0x35, 0x48, 0x36, 0xaf, 0x80, +0x90, 0x38, 0xa4, 0x32, 0x88, 0x36, 0x5d, 0xa4, 0x32, 0x58, 0x36, 0x57, +0x18, 0x20, 0x00, 0xf8, 0x80, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x34, 0xd8, +0x36, 0xa4, 0xa4, 0x34, 0x40, 0x36, 0x90, 0x90, 0x38, 0xa4, 0x33, 0xd0, +0x36, 0x82, 0xa4, 0x33, 0x48, 0x36, 0x70, 0xe4, 0xa2, 0x01, 0x40, 0x38, +0x07, 0x18, 0x24, 0x1d, 0xc0, 0xe4, 0xe1, 0xff, 0xc0, 0x38, 0x01, 0x92, +0x90, 0x92, 0x40, 0x91, 0x08, 0x10, 0x10, 0x90, 0x80, 0x10, 0x10, 0x90, +0x38, 0xa4, 0x35, 0xa0, 0x36, 0xba, 0xa4, 0x35, 0x40, 0x36, 0xae, 0x80, +0x90, 0x38, 0xa4, 0x32, 0x80, 0x36, 0x5c, 0xa4, 0x32, 0x50, 0x36, 0x56, +0x18, 0x20, 0x00, 0xf8, 0x80, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x34, 0xc8, +0x36, 0xa3, 0xa4, 0x34, 0x38, 0x36, 0x8e, 0x90, 0x38, 0xa4, 0x33, 0xc0, +0x36, 0x81, 0xa4, 0x33, 0x40, 0x36, 0x6e, 0xe4, 0xa2, 0x04, 0x80, 0x38, +0x15, 0x10, 0x10, 0xe4, 0xe2, 0x03, 0x00, 0x38, 0x0f, 0x92, 0x50, 0x99, +0x1c, 0x1e, 0xb0, 0x10, 0x10, 0x90, 0x80, 0x10, 0x10, 0x90, 0x38, 0xa4, +0x35, 0x98, 0x36, 0xb9, 0xa4, 0x35, 0x38, 0x36, 0xad, 0x80, 0x90, 0x38, +0xa4, 0x32, 0x78, 0x36, 0x5b, 0xa4, 0x32, 0x48, 0x36, 0x55, 0x18, 0x20, +0x00, 0xf8, 0x80, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x34, 0xb8, 0x36, 0xa2, +0xa4, 0x34, 0x30, 0x36, 0x8c, 0x90, 0x38, 0xa4, 0x33, 0xb0, 0x36, 0x80, +0xa4, 0x33, 0x38, 0x36, 0x6c, 0xe4, 0xa2, 0x01, 0x80, 0x38, 0x09, 0x10, +0x10, 0xe4, 0xe2, 0x00, 0x00, 0x38, 0x03, 0xc0, 0x40, 0x80, 0x10, 0x10, +0x81, 0x90, 0x90, 0x90, 0x48, 0xc9, 0xe1, 0x98, 0x80, 0x85, 0x36, 0x66, +0xc9, 0xe1, 0x99, 0x00, 0x85, 0x36, 0x63, 0x80, 0x36, 0x61, 0x80, 0xd8, +0x47, 0x80, 0x0d, 0xc0, 0xc0, 0x80, 0x10, 0x10, 0x82, 0x90, 0x58, 0xd5, +0x81, 0x80, 0x80, 0x37, 0xfd, 0x80, 0x37, 0xfb, 0xd5, 0x81, 0x80, 0x80, +0x37, 0xf9, 0x80, 0x37, 0xf7, 0xc0, 0x80, 0x10, 0x10, 0x82, 0x90, 0x58, +0xd5, 0x81, 0x80, 0x80, 0x37, 0xfe, 0x80, 0x37, 0xfc, 0xd5, 0x81, 0x80, +0x80, 0x37, 0xfa, 0x80, 0x37, 0xf8, 0xc0, 0x80, 0x83, 0xa4, 0x3f, 0xa8, +0x37, 0xf6, 0xa0, 0x59, 0x60, 0xa0, 0x41, 0xe0, 0xa8, 0x1e, 0xb0, 0x34, +0x88, 0xa0, 0x12, 0x38, 0xa0, 0x0b, 0x48, 0x96, 0x00, 0x9a, 0xf0, 0x05, +0xc0, 0x91, 0x70, 0x90, 0xb8, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x15, 0x58, +0x33, 0xb5, 0xa4, 0x15, 0x78, 0x33, 0xb4, 0x10, 0x10, 0xa4, 0x15, 0x68, +0x33, 0xb3, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x14, 0xf8, 0x33, 0x9a, 0xa4, +0x15, 0x18, 0x33, 0x99, 0x10, 0x10, 0xa4, 0x15, 0x08, 0x33, 0x98, 0x90, +0xb8, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x14, 0x98, 0x33, 0x7f, 0xa4, 0x14, +0xb8, 0x33, 0x7e, 0x10, 0x10, 0xa4, 0x14, 0xa8, 0x33, 0x7d, 0x90, 0x70, +0x90, 0x38, 0xa4, 0x14, 0x38, 0x33, 0x63, 0xa4, 0x14, 0x58, 0x33, 0x62, +0x10, 0x10, 0xa4, 0x14, 0x48, 0x33, 0x61, 0x91, 0x70, 0x90, 0xb8, 0x90, +0x70, 0x90, 0x38, 0xa4, 0x15, 0x28, 0x33, 0xb0, 0xa4, 0x15, 0x48, 0x33, +0xb2, 0x10, 0x10, 0xa4, 0x15, 0x38, 0x33, 0xb1, 0x90, 0x70, 0x90, 0x38, +0xa4, 0x14, 0xc8, 0x33, 0x95, 0xa4, 0x14, 0xe8, 0x33, 0x97, 0x10, 0x10, +0xa4, 0x14, 0xd8, 0x33, 0x96, 0x90, 0xb8, 0x90, 0x70, 0x90, 0x38, 0xa4, +0x14, 0x68, 0x33, 0x7a, 0xa4, 0x14, 0x88, 0x33, 0x7c, 0x10, 0x10, 0xa4, +0x14, 0x78, 0x33, 0x7b, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x14, 0x08, 0x33, +0x5e, 0xa4, 0x14, 0x28, 0x33, 0x60, 0x10, 0x10, 0xa4, 0x14, 0x18, 0x33, +0x5f, 0xe4, 0xe1, 0x8b, 0x40, 0x36, 0x41, 0x9a, 0xf0, 0x05, 0x00, 0x91, +0x70, 0x90, 0xb8, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x13, 0xa0, 0x33, 0xad, +0xa4, 0x13, 0x98, 0x33, 0xaf, 0x10, 0x10, 0xa4, 0x13, 0x90, 0x33, 0xae, +0x90, 0x70, 0x90, 0x38, 0xa4, 0x13, 0x88, 0x33, 0x92, 0xa4, 0x13, 0x80, +0x33, 0x94, 0x10, 0x10, 0xa4, 0x13, 0x78, 0x33, 0x93, 0x90, 0xb8, 0x90, +0x70, 0x90, 0x38, 0xa4, 0x13, 0x70, 0x33, 0x77, 0xa4, 0x13, 0x68, 0x33, +0x79, 0x10, 0x10, 0xa4, 0x13, 0x60, 0x33, 0x78, 0x90, 0x70, 0x90, 0x38, +0xa4, 0x13, 0x58, 0x33, 0x5b, 0xa4, 0x13, 0x50, 0x33, 0x5d, 0x10, 0x10, +0xa4, 0x13, 0x48, 0x33, 0x5c, 0x91, 0x10, 0x90, 0x88, 0x90, 0x50, 0x90, +0x28, 0x80, 0x33, 0xaa, 0x80, 0x33, 0xac, 0x10, 0x10, 0x80, 0x33, 0xab, +0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x8f, 0x80, 0x33, 0x91, 0x10, 0x10, +0x80, 0x33, 0x90, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x74, +0x80, 0x33, 0x76, 0x10, 0x10, 0x80, 0x33, 0x75, 0x90, 0x50, 0x90, 0x28, +0x80, 0x33, 0x58, 0x80, 0x33, 0x5a, 0x10, 0x10, 0x80, 0x33, 0x59, 0xe4, +0xe1, 0x66, 0x40, 0x35, 0xc1, 0x95, 0x40, 0x9a, 0x90, 0x05, 0x00, 0x91, +0x10, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0xa7, 0x80, 0x33, +0xa9, 0x10, 0x10, 0x80, 0x33, 0xa8, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, +0x8c, 0x80, 0x33, 0x8e, 0x10, 0x10, 0x80, 0x33, 0x8d, 0x90, 0xb8, 0x90, +0x70, 0x90, 0x38, 0xa4, 0x13, 0x30, 0x33, 0x71, 0xa4, 0x13, 0x40, 0x33, +0x73, 0x10, 0x10, 0xa4, 0x13, 0x38, 0x33, 0x72, 0x90, 0x70, 0x90, 0x38, +0xa4, 0x13, 0x00, 0x33, 0x55, 0xa4, 0x13, 0x10, 0x33, 0x57, 0x10, 0x10, +0xa4, 0x13, 0x08, 0x33, 0x56, 0x91, 0x10, 0x90, 0x88, 0x90, 0x50, 0x90, +0x28, 0x80, 0x33, 0xa4, 0x80, 0x33, 0xa6, 0x10, 0x10, 0x80, 0x33, 0xa5, +0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x89, 0x80, 0x33, 0x8b, 0x10, 0x10, +0x80, 0x33, 0x8a, 0x90, 0xb8, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x13, 0x18, +0x33, 0x6e, 0xa4, 0x13, 0x28, 0x33, 0x70, 0x10, 0x10, 0xa4, 0x13, 0x20, +0x33, 0x6f, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x12, 0xe8, 0x33, 0x52, 0xa4, +0x12, 0xf8, 0x33, 0x54, 0x10, 0x10, 0xa4, 0x12, 0xf0, 0x33, 0x53, 0xe4, +0xe1, 0x8a, 0x40, 0x36, 0x3d, 0x98, 0xb8, 0x01, 0x68, 0x10, 0x10, 0x10, +0x10, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x4f, 0x80, 0x33, 0x51, 0x10, +0x10, 0x80, 0x33, 0x50, 0x90, 0x60, 0x90, 0x30, 0x60, 0xa0, 0x97, 0x00, +0x60, 0xa0, 0x96, 0xc0, 0x90, 0x30, 0x60, 0xa0, 0x96, 0x80, 0x60, 0xa0, +0x96, 0x40, 0xe4, 0xe1, 0x64, 0x40, 0x35, 0xb9, 0xa0, 0x08, 0x08, 0x94, +0xe0, 0x9a, 0x60, 0x04, 0xa0, 0x91, 0x40, 0x90, 0xb8, 0x90, 0x70, 0x90, +0x38, 0xa4, 0x13, 0xd8, 0x33, 0x9e, 0xa4, 0x13, 0xf8, 0x33, 0xa3, 0x10, +0x10, 0xa4, 0x13, 0xe8, 0x33, 0xa2, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, +0x83, 0x80, 0x33, 0x88, 0x10, 0x10, 0x80, 0x33, 0x87, 0x90, 0x88, 0x90, +0x50, 0x90, 0x28, 0x80, 0x33, 0x68, 0x80, 0x33, 0x6d, 0x10, 0x10, 0x80, +0x33, 0x6c, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x49, 0x80, 0x33, 0x4e, +0x10, 0x10, 0x80, 0x33, 0x4d, 0x91, 0x40, 0x90, 0xb8, 0x90, 0x70, 0x90, +0x38, 0xa4, 0x13, 0xa8, 0x33, 0x9b, 0xa4, 0x13, 0xc8, 0x33, 0x9d, 0x10, +0x10, 0xa4, 0x13, 0xb8, 0x33, 0x9c, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, +0x80, 0x80, 0x33, 0x82, 0x10, 0x10, 0x80, 0x33, 0x81, 0x90, 0x88, 0x90, +0x50, 0x90, 0x28, 0x80, 0x33, 0x65, 0x80, 0x33, 0x67, 0x10, 0x10, 0x80, +0x33, 0x66, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x46, 0x80, 0x33, 0x48, +0x10, 0x10, 0x80, 0x33, 0x47, 0xe4, 0xe1, 0x89, 0x40, 0x36, 0x39, 0x9a, +0x60, 0x02, 0xe0, 0x91, 0x40, 0x90, 0xb8, 0x90, 0x70, 0x90, 0x38, 0xa4, +0x1a, 0x20, 0x33, 0x9f, 0xa4, 0x1a, 0x10, 0x33, 0xa1, 0x10, 0x10, 0xa4, +0x1a, 0x00, 0x33, 0xa0, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x84, 0x80, +0x33, 0x86, 0x10, 0x10, 0x80, 0x33, 0x85, 0x90, 0x88, 0x90, 0x50, 0x90, +0x28, 0x80, 0x33, 0x69, 0x80, 0x33, 0x6b, 0x10, 0x10, 0x80, 0x33, 0x6a, +0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x4a, 0x80, 0x33, 0x4c, 0x10, 0x10, +0x80, 0x33, 0x4b, 0x81, 0x90, 0x50, 0x90, 0x28, 0x24, 0x19, 0xd0, 0x24, +0x19, 0xf0, 0x10, 0x10, 0x24, 0x19, 0xe0, 0xe4, 0xe1, 0x62, 0x40, 0x35, +0xb1, 0x93, 0x90, 0x99, 0xb8, 0x03, 0x50, 0x90, 0xe8, 0x90, 0x88, 0x90, +0x40, 0x80, 0xa4, 0x15, 0xb8, 0x32, 0xca, 0x10, 0x10, 0xa4, 0x15, 0xa8, +0x32, 0xc9, 0x90, 0x28, 0x81, 0x32, 0xc6, 0x10, 0x10, 0x80, 0x32, 0xc5, +0x90, 0x60, 0x90, 0x28, 0x81, 0x32, 0xc2, 0x10, 0x10, 0x80, 0x32, 0xc1, +0x90, 0x28, 0x81, 0x32, 0xbe, 0x10, 0x10, 0x80, 0x32, 0xbd, 0x90, 0xe8, +0x90, 0x88, 0x90, 0x40, 0x80, 0xa4, 0x15, 0x88, 0x32, 0xc7, 0x10, 0x10, +0xa4, 0x15, 0x98, 0x32, 0xc8, 0x90, 0x28, 0x81, 0x32, 0xc3, 0x10, 0x10, +0x80, 0x32, 0xc4, 0x90, 0x60, 0x90, 0x28, 0x81, 0x32, 0xbf, 0x10, 0x10, +0x80, 0x32, 0xc0, 0x90, 0x28, 0x81, 0x32, 0xbb, 0x10, 0x10, 0x80, 0x32, +0xbc, 0xe4, 0xe1, 0x88, 0x40, 0x36, 0x35, 0x88, 0x00, 0x88, 0x10, 0x10, +0x10, 0x10, 0x90, 0x28, 0x81, 0x32, 0xb9, 0x10, 0x10, 0x80, 0x32, 0xba, +0xe4, 0xe1, 0x60, 0x40, 0x35, 0xa9, 0xa0, 0x0e, 0x80, 0xa0, 0x09, 0x08, +0x94, 0x80, 0x9a, 0x30, 0x04, 0x40, 0x91, 0x10, 0x90, 0x88, 0x90, 0x50, +0x90, 0x28, 0x80, 0x33, 0x39, 0x80, 0x33, 0x38, 0x10, 0x10, 0x80, 0x33, +0x37, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x1e, 0x80, 0x33, 0x1d, 0x10, +0x10, 0x80, 0x33, 0x1c, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, +0x03, 0x80, 0x33, 0x02, 0x10, 0x10, 0x80, 0x33, 0x01, 0x90, 0x50, 0x90, +0x28, 0x80, 0x32, 0xe8, 0x80, 0x32, 0xe7, 0x10, 0x10, 0x80, 0x32, 0xe6, +0x91, 0x10, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x34, 0x80, +0x33, 0x36, 0x10, 0x10, 0x80, 0x33, 0x35, 0x90, 0x50, 0x90, 0x28, 0x80, +0x33, 0x19, 0x80, 0x33, 0x1b, 0x10, 0x10, 0x80, 0x33, 0x1a, 0x90, 0x88, +0x90, 0x50, 0x90, 0x28, 0x80, 0x32, 0xfe, 0x80, 0x33, 0x00, 0x10, 0x10, +0x80, 0x32, 0xff, 0x90, 0x50, 0x90, 0x28, 0x80, 0x32, 0xe3, 0x80, 0x32, +0xe5, 0x10, 0x10, 0x80, 0x32, 0xe4, 0xe4, 0xe1, 0x7a, 0x40, 0x36, 0x11, +0x9a, 0x30, 0x04, 0x40, 0x91, 0x10, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, +0x80, 0x33, 0x31, 0x80, 0x33, 0x33, 0x10, 0x10, 0x80, 0x33, 0x32, 0x90, +0x50, 0x90, 0x28, 0x80, 0x33, 0x16, 0x80, 0x33, 0x18, 0x10, 0x10, 0x80, +0x33, 0x17, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x32, 0xfb, 0x80, +0x32, 0xfd, 0x10, 0x10, 0x80, 0x32, 0xfc, 0x90, 0x50, 0x90, 0x28, 0x80, +0x32, 0xe0, 0x80, 0x32, 0xe2, 0x10, 0x10, 0x80, 0x32, 0xe1, 0x91, 0x10, +0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x2e, 0x80, 0x33, 0x30, +0x10, 0x10, 0x80, 0x33, 0x2f, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x13, +0x80, 0x33, 0x15, 0x10, 0x10, 0x80, 0x33, 0x14, 0x90, 0x88, 0x90, 0x50, +0x90, 0x28, 0x80, 0x32, 0xf8, 0x80, 0x32, 0xfa, 0x10, 0x10, 0x80, 0x32, +0xf9, 0x90, 0x50, 0x90, 0x28, 0x80, 0x32, 0xdd, 0x80, 0x32, 0xdf, 0x10, +0x10, 0x80, 0x32, 0xde, 0xe4, 0xe1, 0x59, 0x40, 0x35, 0x79, 0x94, 0x80, +0x9a, 0x30, 0x04, 0x40, 0x91, 0x10, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, +0x80, 0x33, 0x2b, 0x80, 0x33, 0x2d, 0x10, 0x10, 0x80, 0x33, 0x2c, 0x90, +0x50, 0x90, 0x28, 0x80, 0x33, 0x10, 0x80, 0x33, 0x12, 0x10, 0x10, 0x80, +0x33, 0x11, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x32, 0xf5, 0x80, +0x32, 0xf7, 0x10, 0x10, 0x80, 0x32, 0xf6, 0x90, 0x50, 0x90, 0x28, 0x80, +0x32, 0xda, 0x80, 0x32, 0xdc, 0x10, 0x10, 0x80, 0x32, 0xdb, 0x91, 0x10, +0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x28, 0x80, 0x33, 0x2a, +0x10, 0x10, 0x80, 0x33, 0x29, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x0d, +0x80, 0x33, 0x0f, 0x10, 0x10, 0x80, 0x33, 0x0e, 0x90, 0x88, 0x90, 0x50, +0x90, 0x28, 0x80, 0x32, 0xf2, 0x80, 0x32, 0xf4, 0x10, 0x10, 0x80, 0x32, +0xf3, 0x90, 0x50, 0x90, 0x28, 0x80, 0x32, 0xd7, 0x80, 0x32, 0xd9, 0x10, +0x10, 0x80, 0x32, 0xd8, 0xe4, 0xe1, 0x78, 0x40, 0x36, 0x09, 0x88, 0x00, +0xb0, 0x10, 0x10, 0x10, 0x10, 0x90, 0x50, 0x90, 0x28, 0x80, 0x32, 0xd4, +0x80, 0x32, 0xd6, 0x10, 0x10, 0x80, 0x32, 0xd5, 0xe4, 0xe1, 0x58, 0x40, +0x35, 0x75, 0x96, 0xe8, 0x94, 0x80, 0x9a, 0x30, 0x04, 0x40, 0x91, 0x10, +0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x22, 0x80, 0x33, 0x27, +0x10, 0x10, 0x80, 0x33, 0x26, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x07, +0x80, 0x33, 0x0c, 0x10, 0x10, 0x80, 0x33, 0x0b, 0x90, 0x88, 0x90, 0x50, +0x90, 0x28, 0x80, 0x32, 0xec, 0x80, 0x32, 0xf1, 0x10, 0x10, 0x80, 0x32, +0xf0, 0x90, 0x50, 0x90, 0x28, 0x80, 0x32, 0xce, 0x80, 0x32, 0xd3, 0x10, +0x10, 0x80, 0x32, 0xd2, 0x91, 0x10, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, +0x80, 0x33, 0x1f, 0x80, 0x33, 0x21, 0x10, 0x10, 0x80, 0x33, 0x20, 0x90, +0x50, 0x90, 0x28, 0x80, 0x33, 0x04, 0x80, 0x33, 0x06, 0x10, 0x10, 0x80, +0x33, 0x05, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x32, 0xe9, 0x80, +0x32, 0xeb, 0x10, 0x10, 0x80, 0x32, 0xea, 0x90, 0x50, 0x90, 0x28, 0x80, +0x32, 0xcb, 0x80, 0x32, 0xcd, 0x10, 0x10, 0x80, 0x32, 0xcc, 0xe4, 0xe1, +0x76, 0x40, 0x36, 0x01, 0x88, 0x02, 0x28, 0x91, 0x10, 0x90, 0x88, 0x90, +0x50, 0x90, 0x28, 0x80, 0x33, 0x23, 0x80, 0x33, 0x25, 0x10, 0x10, 0x80, +0x33, 0x24, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0x08, 0x80, 0x33, 0x0a, +0x10, 0x10, 0x80, 0x33, 0x09, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, +0x32, 0xed, 0x80, 0x32, 0xef, 0x10, 0x10, 0x80, 0x32, 0xee, 0x90, 0x50, +0x90, 0x28, 0x80, 0x32, 0xcf, 0x80, 0x32, 0xd1, 0x10, 0x10, 0x80, 0x32, +0xd0, 0xe4, 0xe1, 0x57, 0x40, 0x35, 0x71, 0x90, 0x40, 0xe5, 0x21, 0x74, +0x40, 0x35, 0xf9, 0xe5, 0x21, 0x56, 0x40, 0x35, 0x6d, 0x9e, 0xb4, 0x23, +0xe8, 0x93, 0x70, 0x91, 0xd8, 0xd5, 0x07, 0x80, 0xd0, 0xc4, 0x40, 0x90, +0x48, 0x80, 0x8c, 0x3f, 0x38, 0x84, 0x37, 0xf1, 0xa4, 0x3d, 0x18, 0x37, +0xbb, 0x90, 0x28, 0x24, 0x3c, 0x58, 0xa4, 0x3a, 0xd8, 0x37, 0x73, 0xd0, +0xc4, 0x40, 0x90, 0x48, 0x80, 0x8c, 0x3f, 0x18, 0x84, 0x37, 0xef, 0xa4, +0x3d, 0x08, 0x37, 0xb9, 0x90, 0x28, 0x24, 0x3c, 0x48, 0xa4, 0x3a, 0xc8, +0x37, 0x71, 0xd5, 0x06, 0x80, 0xd0, 0xc3, 0x40, 0x90, 0x28, 0x80, 0x37, +0xdb, 0xa4, 0x3c, 0xe8, 0x37, 0xb5, 0x90, 0x28, 0x24, 0x3c, 0x28, 0xa4, +0x3a, 0xa8, 0x37, 0x6d, 0xd0, 0xc3, 0x40, 0x90, 0x28, 0x80, 0x37, 0xd7, +0xa4, 0x3c, 0xd8, 0x37, 0xb3, 0x90, 0x28, 0x24, 0x3c, 0x18, 0xa4, 0x3a, +0x98, 0x37, 0x6b, 0x91, 0x98, 0xd5, 0x06, 0x80, 0xd0, 0xc3, 0x40, 0x90, +0x28, 0x80, 0x37, 0xcf, 0xa4, 0x3c, 0xb8, 0x37, 0xaf, 0x90, 0x28, 0x24, +0x3b, 0xf8, 0xa4, 0x3a, 0x78, 0x37, 0x67, 0xd0, 0xc3, 0x40, 0x90, 0x28, +0x80, 0x37, 0xcb, 0xa4, 0x3c, 0xa8, 0x37, 0xad, 0x90, 0x28, 0x24, 0x3b, +0xe8, 0xa4, 0x3a, 0x68, 0x37, 0x65, 0xd5, 0x06, 0x80, 0xd0, 0xc3, 0x40, +0x90, 0x28, 0x80, 0x37, 0xc3, 0xa4, 0x3c, 0x88, 0x37, 0xa9, 0x90, 0x28, +0x24, 0x3b, 0xc8, 0xa4, 0x3a, 0x48, 0x37, 0x61, 0xd0, 0xc3, 0x40, 0x90, +0x28, 0x80, 0x37, 0xbf, 0xa4, 0x3c, 0x78, 0x37, 0xa7, 0x90, 0x28, 0x24, +0x3b, 0xb8, 0xa4, 0x3a, 0x38, 0x37, 0x5f, 0x93, 0x70, 0x91, 0xd8, 0xd5, +0x07, 0x80, 0xd0, 0xc4, 0x40, 0x90, 0x48, 0x80, 0x8c, 0x3f, 0x58, 0x84, +0x37, 0xf3, 0xa4, 0x3d, 0x28, 0x37, 0xbd, 0x90, 0x28, 0x24, 0x3c, 0x68, +0xa4, 0x3a, 0xe8, 0x37, 0x75, 0xd0, 0xc4, 0x40, 0x90, 0x48, 0x80, 0x8c, +0x3f, 0x28, 0x84, 0x37, 0xf0, 0xa4, 0x3d, 0x10, 0x37, 0xba, 0x90, 0x28, +0x24, 0x3c, 0x50, 0xa4, 0x3a, 0xd0, 0x37, 0x72, 0xd5, 0x06, 0x80, 0xd0, +0xc3, 0x40, 0x90, 0x28, 0x80, 0x37, 0xdf, 0xa4, 0x3c, 0xf8, 0x37, 0xb7, +0x90, 0x28, 0x24, 0x3c, 0x38, 0xa4, 0x3a, 0xb8, 0x37, 0x6f, 0xd0, 0xc3, +0x40, 0x90, 0x28, 0x80, 0x37, 0xd9, 0xa4, 0x3c, 0xe0, 0x37, 0xb4, 0x90, +0x28, 0x24, 0x3c, 0x20, 0xa4, 0x3a, 0xa0, 0x37, 0x6c, 0x91, 0x98, 0xd5, +0x06, 0x80, 0xd0, 0xc3, 0x40, 0x90, 0x28, 0x80, 0x37, 0xd3, 0xa4, 0x3c, +0xc8, 0x37, 0xb1, 0x90, 0x28, 0x24, 0x3c, 0x08, 0xa4, 0x3a, 0x88, 0x37, +0x69, 0xd0, 0xc3, 0x40, 0x90, 0x28, 0x80, 0x37, 0xcd, 0xa4, 0x3c, 0xb0, +0x37, 0xae, 0x90, 0x28, 0x24, 0x3b, 0xf0, 0xa4, 0x3a, 0x70, 0x37, 0x66, +0xd5, 0x06, 0x80, 0xd0, 0xc3, 0x40, 0x90, 0x28, 0x80, 0x37, 0xc7, 0xa4, +0x3c, 0x98, 0x37, 0xab, 0x90, 0x28, 0x24, 0x3b, 0xd8, 0xa4, 0x3a, 0x58, +0x37, 0x63, 0xd0, 0xc3, 0x40, 0x90, 0x28, 0x80, 0x37, 0xc1, 0xa4, 0x3c, +0x80, 0x37, 0xa8, 0x90, 0x28, 0x24, 0x3b, 0xc0, 0xa4, 0x3a, 0x40, 0x37, +0x60, 0x99, 0xd8, 0x03, 0x90, 0x81, 0x90, 0xe0, 0x5b, 0x41, 0x40, 0x03, +0x40, 0x51, 0x40, 0xc0, 0xa4, 0x23, 0x80, 0x34, 0x60, 0xd1, 0x42, 0x00, +0xa4, 0x22, 0x80, 0x34, 0x40, 0xa4, 0x21, 0x80, 0x34, 0x20, 0x5b, 0x41, +0x40, 0x03, 0x40, 0x51, 0x40, 0xc0, 0xa4, 0x22, 0xa0, 0x34, 0x64, 0xd1, +0x42, 0x00, 0xa4, 0x21, 0xa0, 0x34, 0x44, 0xa4, 0x20, 0xa0, 0x34, 0x24, +0x81, 0x90, 0xe0, 0x5b, 0x41, 0x40, 0x03, 0x40, 0x51, 0x40, 0xc0, 0xa4, +0x22, 0xe0, 0x34, 0x6c, 0xd1, 0x42, 0x00, 0xa4, 0x21, 0xe0, 0x34, 0x4c, +0xa4, 0x20, 0xe0, 0x34, 0x2c, 0x5b, 0x41, 0x40, 0x03, 0x40, 0x51, 0x40, +0xc0, 0xa4, 0x22, 0xc0, 0x34, 0x68, 0xd1, 0x42, 0x00, 0xa4, 0x21, 0xc0, +0x34, 0x48, 0xa4, 0x20, 0xc0, 0x34, 0x28, 0xa8, 0x0b, 0x18, 0x13, 0xa8, +0x96, 0x80, 0x93, 0x40, 0x99, 0x90, 0x03, 0x00, 0x90, 0xc0, 0x90, 0x60, +0x90, 0x38, 0xa4, 0x12, 0xb8, 0x32, 0x58, 0x24, 0x12, 0xb0, 0x90, 0x38, +0xa4, 0x11, 0xe0, 0x32, 0x3d, 0x24, 0x11, 0xd8, 0x90, 0x60, 0x90, 0x38, +0xa4, 0x11, 0x08, 0x32, 0x22, 0x24, 0x11, 0x00, 0x90, 0x38, 0xa4, 0x10, +0x30, 0x32, 0x07, 0x24, 0x10, 0x28, 0x90, 0xc0, 0x90, 0x60, 0x90, 0x38, +0xa4, 0x12, 0xa8, 0x32, 0x53, 0x24, 0x12, 0xa0, 0x90, 0x38, 0xa4, 0x11, +0xd0, 0x32, 0x38, 0x24, 0x11, 0xc8, 0x90, 0x60, 0x90, 0x38, 0xa4, 0x10, +0xf8, 0x32, 0x1d, 0x24, 0x10, 0xf0, 0x90, 0x38, 0xa4, 0x10, 0x20, 0x32, +0x02, 0x24, 0x10, 0x18, 0xe4, 0xe1, 0xd0, 0x40, 0x37, 0x43, 0x99, 0x90, +0x03, 0x00, 0x90, 0xc0, 0x90, 0x60, 0x90, 0x38, 0xa4, 0x12, 0x90, 0x32, +0x50, 0x24, 0x12, 0x88, 0x90, 0x38, 0xa4, 0x11, 0xb8, 0x32, 0x35, 0x24, +0x11, 0xb0, 0x90, 0x60, 0x90, 0x38, 0xa4, 0x10, 0xe0, 0x32, 0x1a, 0x24, +0x10, 0xd8, 0x90, 0x38, 0xa4, 0x10, 0x08, 0x31, 0xff, 0x24, 0x10, 0x00, +0x90, 0xc0, 0x90, 0x60, 0x90, 0x38, 0xa4, 0x12, 0x78, 0x32, 0x4d, 0x24, +0x12, 0x70, 0x90, 0x38, 0xa4, 0x11, 0xa0, 0x32, 0x32, 0x24, 0x11, 0x98, +0x90, 0x60, 0x90, 0x38, 0xa4, 0x10, 0xc8, 0x32, 0x17, 0x24, 0x10, 0xc0, +0x90, 0x38, 0xa4, 0x0f, 0xf0, 0x31, 0xfc, 0x24, 0x0f, 0xe8, 0xe4, 0xe1, +0xce, 0xc0, 0x37, 0x3d, 0x93, 0x78, 0x99, 0x90, 0x03, 0x00, 0x90, 0xc0, +0x90, 0x60, 0x90, 0x38, 0xa4, 0x12, 0x60, 0x32, 0x4a, 0x24, 0x12, 0x58, +0x90, 0x38, 0xa4, 0x11, 0x88, 0x32, 0x2f, 0x24, 0x11, 0x80, 0x90, 0x60, +0x90, 0x38, 0xa4, 0x10, 0xb0, 0x32, 0x14, 0x24, 0x10, 0xa8, 0x90, 0x38, +0xa4, 0x0f, 0xd8, 0x31, 0xf9, 0x24, 0x0f, 0xd0, 0x90, 0xc0, 0x90, 0x60, +0x90, 0x38, 0xa4, 0x12, 0x48, 0x32, 0x47, 0x24, 0x12, 0x40, 0x90, 0x38, +0xa4, 0x11, 0x70, 0x32, 0x2c, 0x24, 0x11, 0x68, 0x90, 0x60, 0x90, 0x38, +0xa4, 0x10, 0x98, 0x32, 0x11, 0x24, 0x10, 0x90, 0x90, 0x38, 0xa4, 0x0f, +0xc0, 0x31, 0xf6, 0x24, 0x0f, 0xb8, 0xec, 0xa1, 0x1e, 0x00, 0x02, 0x00, +0x34, 0x7a, 0xa4, 0x39, 0xa8, 0x37, 0x37, 0x88, 0x00, 0x88, 0x10, 0x10, +0x10, 0x10, 0x90, 0x38, 0xa4, 0x0f, 0xa8, 0x31, 0xf3, 0x24, 0x0f, 0xa0, +0xe9, 0x61, 0x1d, 0x40, 0x02, 0x00, 0x34, 0x76, 0xe3, 0x61, 0xcb, 0xc0, +0x37, 0x31, 0x95, 0x08, 0x93, 0x40, 0x99, 0x90, 0x03, 0x00, 0x90, 0xc0, +0x90, 0x60, 0x90, 0x38, 0xa4, 0x12, 0x30, 0x32, 0x41, 0x24, 0x12, 0x28, +0x90, 0x38, 0xa4, 0x11, 0x58, 0x32, 0x26, 0x24, 0x11, 0x50, 0x90, 0x60, +0x90, 0x38, 0xa4, 0x10, 0x80, 0x32, 0x0b, 0x24, 0x10, 0x78, 0x90, 0x38, +0xa4, 0x0f, 0x90, 0x31, 0xed, 0x24, 0x0f, 0x88, 0x90, 0xc0, 0x90, 0x60, +0x90, 0x38, 0xa4, 0x12, 0x00, 0x32, 0x3e, 0x24, 0x11, 0xf8, 0x90, 0x38, +0xa4, 0x11, 0x28, 0x32, 0x23, 0x24, 0x11, 0x20, 0x90, 0x60, 0x90, 0x38, +0xa4, 0x10, 0x50, 0x32, 0x08, 0x24, 0x10, 0x48, 0x90, 0x38, 0xa4, 0x0f, +0x60, 0x31, 0xea, 0x24, 0x0f, 0x58, 0xe4, 0xe1, 0xd0, 0x80, 0x37, 0x45, +0x88, 0x01, 0x88, 0x90, 0xc0, 0x90, 0x60, 0x90, 0x38, 0xa4, 0x12, 0x20, +0x32, 0x42, 0x24, 0x12, 0x18, 0x90, 0x38, 0xa4, 0x11, 0x48, 0x32, 0x27, +0x24, 0x11, 0x40, 0x90, 0x60, 0x90, 0x38, 0xa4, 0x10, 0x70, 0x32, 0x0c, +0x24, 0x10, 0x68, 0x90, 0x38, 0xa4, 0x0f, 0x80, 0x31, 0xee, 0x24, 0x0f, +0x78, 0xe4, 0xe1, 0xcf, 0x00, 0x37, 0x3f, 0x92, 0xd0, 0x99, 0x50, 0x02, +0x80, 0x90, 0xa0, 0x90, 0x50, 0x90, 0x28, 0x80, 0x31, 0xe9, 0x24, 0x0f, +0x40, 0x90, 0x28, 0x80, 0x31, 0xe5, 0x24, 0x0f, 0x20, 0x90, 0x50, 0x90, +0x28, 0x80, 0x31, 0xe1, 0x24, 0x0f, 0x00, 0x90, 0x28, 0x80, 0x31, 0xdd, +0x24, 0x0e, 0xe0, 0x90, 0xa0, 0x90, 0x50, 0x90, 0x28, 0x80, 0x31, 0xe6, +0x24, 0x0f, 0x38, 0x90, 0x28, 0x80, 0x31, 0xe2, 0x24, 0x0f, 0x18, 0x90, +0x50, 0x90, 0x28, 0x80, 0x31, 0xde, 0x24, 0x0e, 0xf8, 0x90, 0x28, 0x80, +0x31, 0xda, 0x24, 0x0e, 0xd8, 0xec, 0xe1, 0xcd, 0xa1, 0x1f, 0x00, 0x37, +0x39, 0x88, 0x00, 0x78, 0x10, 0x10, 0x10, 0x10, 0x90, 0x28, 0x80, 0x31, +0xd8, 0x24, 0x0e, 0xc8, 0xec, 0xe1, 0xcc, 0x21, 0x1d, 0x00, 0x37, 0x33, +0xe5, 0xa1, 0x55, 0x40, 0x35, 0x51, 0xa0, 0x2a, 0x10, 0xa8, 0x16, 0x60, +0x29, 0xd8, 0xa0, 0x0c, 0x48, 0xa0, 0x0a, 0xc8, 0x95, 0x60, 0x92, 0xb0, +0x91, 0x40, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x31, 0xa1, 0x80, +0x31, 0xa0, 0x10, 0x10, 0x80, 0x31, 0x9f, 0x90, 0x70, 0x90, 0x38, 0xa4, +0x08, 0x98, 0x31, 0xb3, 0xa4, 0x08, 0x90, 0x31, 0xb2, 0x10, 0x10, 0xa4, +0x08, 0x88, 0x31, 0xb1, 0x90, 0xb8, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x09, +0xb8, 0x31, 0xd7, 0xa4, 0x09, 0xb0, 0x31, 0xd6, 0x10, 0x10, 0xa4, 0x09, +0xa8, 0x31, 0xd5, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x09, 0x28, 0x31, 0xc5, +0xa4, 0x09, 0x20, 0x31, 0xc4, 0x10, 0x10, 0xa4, 0x09, 0x18, 0x31, 0xc3, +0x91, 0x40, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x31, 0x9c, 0x80, +0x31, 0x9e, 0x10, 0x10, 0x80, 0x31, 0x9d, 0x90, 0x70, 0x90, 0x38, 0xa4, +0x08, 0x70, 0x31, 0xae, 0xa4, 0x08, 0x80, 0x31, 0xb0, 0x10, 0x10, 0xa4, +0x08, 0x78, 0x31, 0xaf, 0x90, 0xb8, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x09, +0x90, 0x31, 0xd2, 0xa4, 0x09, 0xa0, 0x31, 0xd4, 0x10, 0x10, 0xa4, 0x09, +0x98, 0x31, 0xd3, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x09, 0x00, 0x31, 0xc0, +0xa4, 0x09, 0x10, 0x31, 0xc2, 0x10, 0x10, 0xa4, 0x09, 0x08, 0x31, 0xc1, +0x92, 0xb0, 0x91, 0x40, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x31, +0x99, 0x80, 0x31, 0x9b, 0x10, 0x10, 0x80, 0x31, 0x9a, 0x90, 0x70, 0x90, +0x38, 0xa4, 0x08, 0x58, 0x31, 0xab, 0xa4, 0x08, 0x68, 0x31, 0xad, 0x10, +0x10, 0xa4, 0x08, 0x60, 0x31, 0xac, 0x90, 0xb8, 0x90, 0x70, 0x90, 0x38, +0xa4, 0x09, 0x78, 0x31, 0xcf, 0xa4, 0x09, 0x88, 0x31, 0xd1, 0x10, 0x10, +0xa4, 0x09, 0x80, 0x31, 0xd0, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x08, 0xe8, +0x31, 0xbd, 0xa4, 0x08, 0xf8, 0x31, 0xbf, 0x10, 0x10, 0xa4, 0x08, 0xf0, +0x31, 0xbe, 0x91, 0x40, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, 0x31, +0x96, 0x80, 0x31, 0x98, 0x10, 0x10, 0x80, 0x31, 0x97, 0x90, 0x70, 0x90, +0x38, 0xa4, 0x08, 0x40, 0x31, 0xa8, 0xa4, 0x08, 0x50, 0x31, 0xaa, 0x10, +0x10, 0xa4, 0x08, 0x48, 0x31, 0xa9, 0x90, 0xb8, 0x90, 0x70, 0x90, 0x38, +0xa4, 0x09, 0x60, 0x31, 0xcc, 0xa4, 0x09, 0x70, 0x31, 0xce, 0x10, 0x10, +0xa4, 0x09, 0x68, 0x31, 0xcd, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x08, 0xd0, +0x31, 0xba, 0xa4, 0x08, 0xe0, 0x31, 0xbc, 0x10, 0x10, 0xa4, 0x08, 0xd8, +0x31, 0xbb, 0x10, 0x10, 0x90, 0xa8, 0x10, 0x10, 0x10, 0x10, 0x90, 0x50, +0x90, 0x28, 0x80, 0x31, 0x8d, 0x80, 0x31, 0x8f, 0x10, 0x10, 0x80, 0x31, +0x8e, 0x90, 0x60, 0x90, 0x30, 0x60, 0xa0, 0x2a, 0xc0, 0x60, 0xa0, 0x2a, +0x80, 0x90, 0x30, 0x60, 0xa0, 0x2a, 0x40, 0x60, 0xa0, 0x2a, 0x00, 0x97, +0xf0, 0x95, 0x60, 0x92, 0xb0, 0x91, 0x40, 0x90, 0x88, 0x90, 0x50, 0x90, +0x28, 0x80, 0x31, 0x93, 0x80, 0x31, 0x95, 0x10, 0x10, 0x80, 0x31, 0x94, +0x90, 0x70, 0x90, 0x38, 0xa4, 0x08, 0x28, 0x31, 0xa5, 0xa4, 0x08, 0x38, +0x31, 0xa7, 0x10, 0x10, 0xa4, 0x08, 0x30, 0x31, 0xa6, 0x90, 0xb8, 0x90, +0x70, 0x90, 0x38, 0xa4, 0x09, 0x48, 0x31, 0xc9, 0xa4, 0x09, 0x58, 0x31, +0xcb, 0x10, 0x10, 0xa4, 0x09, 0x50, 0x31, 0xca, 0x90, 0x70, 0x90, 0x38, +0xa4, 0x08, 0xb8, 0x31, 0xb7, 0xa4, 0x08, 0xc8, 0x31, 0xb9, 0x10, 0x10, +0xa4, 0x08, 0xc0, 0x31, 0xb8, 0x91, 0x40, 0x90, 0x88, 0x90, 0x50, 0x90, +0x28, 0x80, 0x31, 0x90, 0x80, 0x31, 0x92, 0x10, 0x10, 0x80, 0x31, 0x91, +0x90, 0x70, 0x90, 0x38, 0xa4, 0x08, 0x10, 0x31, 0xa2, 0xa4, 0x08, 0x20, +0x31, 0xa4, 0x10, 0x10, 0xa4, 0x08, 0x18, 0x31, 0xa3, 0x90, 0xb8, 0x90, +0x70, 0x90, 0x38, 0xa4, 0x09, 0x30, 0x31, 0xc6, 0xa4, 0x09, 0x40, 0x31, +0xc8, 0x10, 0x10, 0xa4, 0x09, 0x38, 0x31, 0xc7, 0x90, 0x70, 0x90, 0x38, +0xa4, 0x08, 0xa0, 0x31, 0xb4, 0xa4, 0x08, 0xb0, 0x31, 0xb6, 0x10, 0x10, +0xa4, 0x08, 0xa8, 0x31, 0xb5, 0x10, 0x10, 0x91, 0x40, 0x90, 0xa0, 0x90, +0x50, 0x90, 0x28, 0x80, 0x30, 0xcb, 0x80, 0x30, 0xca, 0x90, 0x28, 0x80, +0x30, 0xc9, 0x80, 0x30, 0xc8, 0x90, 0x50, 0x90, 0x28, 0x80, 0x30, 0xc4, +0x80, 0x30, 0xc7, 0x90, 0x28, 0x80, 0x30, 0xc6, 0x80, 0x30, 0xc5, 0x90, +0xa0, 0x90, 0x50, 0x90, 0x28, 0x80, 0x30, 0xbc, 0x80, 0x30, 0xc3, 0x90, +0x28, 0x80, 0x30, 0xc2, 0x80, 0x30, 0xc1, 0x90, 0x50, 0x90, 0x28, 0x80, +0x30, 0xbd, 0x80, 0x30, 0xc0, 0x90, 0x28, 0x80, 0x30, 0xbf, 0x80, 0x30, +0xbe, 0x91, 0x88, 0x80, 0x90, 0xc0, 0x90, 0x60, 0x90, 0x28, 0x81, 0x31, +0x3b, 0x10, 0x10, 0x80, 0x31, 0x3a, 0x90, 0x28, 0x81, 0x31, 0x3d, 0x10, +0x10, 0x80, 0x31, 0x3c, 0x90, 0x60, 0x90, 0x28, 0x81, 0x31, 0x41, 0x10, +0x10, 0x80, 0x31, 0x40, 0x90, 0x28, 0x81, 0x31, 0x3f, 0x10, 0x10, 0x80, +0x31, 0x3e, 0x80, 0x10, 0x10, 0x10, 0x10, 0x90, 0x28, 0x81, 0x31, 0x38, +0x10, 0x10, 0x80, 0x31, 0x39, 0xa0, 0x0b, 0x90, 0xa0, 0x0a, 0xc8, 0x95, +0x60, 0x92, 0xb0, 0x91, 0x40, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, +0x31, 0x56, 0x80, 0x31, 0x55, 0x10, 0x10, 0x80, 0x31, 0x54, 0x90, 0x70, +0x90, 0x38, 0xa4, 0x06, 0xe8, 0x31, 0x68, 0xa4, 0x06, 0xe0, 0x31, 0x67, +0x10, 0x10, 0xa4, 0x06, 0xd8, 0x31, 0x66, 0x90, 0xb8, 0x90, 0x70, 0x90, +0x38, 0xa4, 0x08, 0x08, 0x31, 0x8c, 0xa4, 0x08, 0x00, 0x31, 0x8b, 0x10, +0x10, 0xa4, 0x07, 0xf8, 0x31, 0x8a, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x07, +0x78, 0x31, 0x7a, 0xa4, 0x07, 0x70, 0x31, 0x79, 0x10, 0x10, 0xa4, 0x07, +0x68, 0x31, 0x78, 0x91, 0x40, 0x90, 0x88, 0x90, 0x50, 0x90, 0x28, 0x80, +0x31, 0x51, 0x80, 0x31, 0x53, 0x10, 0x10, 0x80, 0x31, 0x52, 0x90, 0x70, +0x90, 0x38, 0xa4, 0x06, 0xc0, 0x31, 0x63, 0xa4, 0x06, 0xd0, 0x31, 0x65, +0x10, 0x10, 0xa4, 0x06, 0xc8, 0x31, 0x64, 0x90, 0xb8, 0x90, 0x70, 0x90, +0x38, 0xa4, 0x07, 0xe0, 0x31, 0x87, 0xa4, 0x07, 0xf0, 0x31, 0x89, 0x10, +0x10, 0xa4, 0x07, 0xe8, 0x31, 0x88, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x07, +0x50, 0x31, 0x75, 0xa4, 0x07, 0x60, 0x31, 0x77, 0x10, 0x10, 0xa4, 0x07, +0x58, 0x31, 0x76, 0x92, 0xb0, 0x91, 0x40, 0x90, 0x88, 0x90, 0x50, 0x90, +0x28, 0x80, 0x31, 0x4e, 0x80, 0x31, 0x50, 0x10, 0x10, 0x80, 0x31, 0x4f, +0x90, 0x70, 0x90, 0x38, 0xa4, 0x06, 0xa8, 0x31, 0x60, 0xa4, 0x06, 0xb8, +0x31, 0x62, 0x10, 0x10, 0xa4, 0x06, 0xb0, 0x31, 0x61, 0x90, 0xb8, 0x90, +0x70, 0x90, 0x38, 0xa4, 0x07, 0xc8, 0x31, 0x84, 0xa4, 0x07, 0xd8, 0x31, +0x86, 0x10, 0x10, 0xa4, 0x07, 0xd0, 0x31, 0x85, 0x90, 0x70, 0x90, 0x38, +0xa4, 0x07, 0x38, 0x31, 0x72, 0xa4, 0x07, 0x48, 0x31, 0x74, 0x10, 0x10, +0xa4, 0x07, 0x40, 0x31, 0x73, 0x91, 0x40, 0x90, 0x88, 0x90, 0x50, 0x90, +0x28, 0x80, 0x31, 0x4b, 0x80, 0x31, 0x4d, 0x10, 0x10, 0x80, 0x31, 0x4c, +0x90, 0x70, 0x90, 0x38, 0xa4, 0x06, 0x90, 0x31, 0x5d, 0xa4, 0x06, 0xa0, +0x31, 0x5f, 0x10, 0x10, 0xa4, 0x06, 0x98, 0x31, 0x5e, 0x90, 0xb8, 0x90, +0x70, 0x90, 0x38, 0xa4, 0x07, 0xb0, 0x31, 0x81, 0xa4, 0x07, 0xc0, 0x31, +0x83, 0x10, 0x10, 0xa4, 0x07, 0xb8, 0x31, 0x82, 0x90, 0x70, 0x90, 0x38, +0xa4, 0x07, 0x20, 0x31, 0x6f, 0xa4, 0x07, 0x30, 0x31, 0x71, 0x10, 0x10, +0xa4, 0x07, 0x28, 0x31, 0x70, 0x10, 0x10, 0x80, 0x10, 0x10, 0x10, 0x10, +0x90, 0x50, 0x90, 0x28, 0x80, 0x31, 0x42, 0x80, 0x31, 0x44, 0x10, 0x10, +0x80, 0x31, 0x43, 0x80, 0x95, 0x60, 0x92, 0xb0, 0x91, 0x40, 0x90, 0x88, +0x90, 0x50, 0x90, 0x28, 0x80, 0x31, 0x48, 0x80, 0x31, 0x4a, 0x10, 0x10, +0x80, 0x31, 0x49, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x06, 0x78, 0x31, 0x5a, +0xa4, 0x06, 0x88, 0x31, 0x5c, 0x10, 0x10, 0xa4, 0x06, 0x80, 0x31, 0x5b, +0x90, 0xb8, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x07, 0x98, 0x31, 0x7e, 0xa4, +0x07, 0xa8, 0x31, 0x80, 0x10, 0x10, 0xa4, 0x07, 0xa0, 0x31, 0x7f, 0x90, +0x70, 0x90, 0x38, 0xa4, 0x07, 0x08, 0x31, 0x6c, 0xa4, 0x07, 0x18, 0x31, +0x6e, 0x10, 0x10, 0xa4, 0x07, 0x10, 0x31, 0x6d, 0x91, 0x40, 0x90, 0x88, +0x90, 0x50, 0x90, 0x28, 0x80, 0x31, 0x45, 0x80, 0x31, 0x47, 0x10, 0x10, +0x80, 0x31, 0x46, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x06, 0x60, 0x31, 0x57, +0xa4, 0x06, 0x70, 0x31, 0x59, 0x10, 0x10, 0xa4, 0x06, 0x68, 0x31, 0x58, +0x90, 0xb8, 0x90, 0x70, 0x90, 0x38, 0xa4, 0x07, 0x80, 0x31, 0x7b, 0xa4, +0x07, 0x90, 0x31, 0x7d, 0x10, 0x10, 0xa4, 0x07, 0x88, 0x31, 0x7c, 0x90, +0x70, 0x90, 0x38, 0xa4, 0x06, 0xf0, 0x31, 0x69, 0xa4, 0x07, 0x00, 0x31, +0x6b, 0x10, 0x10, 0xa4, 0x06, 0xf8, 0x31, 0x6a, 0x10, 0x10, 0x91, 0x40, +0x90, 0xa0, 0x90, 0x50, 0x90, 0x28, 0x80, 0x30, 0xbb, 0x80, 0x30, 0xba, +0x90, 0x28, 0x80, 0x30, 0xb9, 0x80, 0x30, 0xb8, 0x90, 0x50, 0x90, 0x28, +0x80, 0x30, 0xb4, 0x80, 0x30, 0xb7, 0x90, 0x28, 0x80, 0x30, 0xb6, 0x80, +0x30, 0xb5, 0x90, 0xa0, 0x90, 0x50, 0x90, 0x28, 0x80, 0x30, 0xac, 0x80, +0x30, 0xb3, 0x90, 0x28, 0x80, 0x30, 0xb2, 0x80, 0x30, 0xb1, 0x90, 0x50, +0x90, 0x28, 0x80, 0x30, 0xad, 0x80, 0x30, 0xb0, 0x90, 0x28, 0x80, 0x30, +0xaf, 0x80, 0x30, 0xae, 0xc3, 0xc0, 0x30, 0x42, 0x9c, 0xe8, 0x07, 0x60, +0x91, 0x90, 0x90, 0xf0, 0x10, 0x10, 0x80, 0x88, 0x00, 0x80, 0x90, 0x50, +0x90, 0x28, 0x80, 0x33, 0xf8, 0x80, 0x33, 0xf9, 0x81, 0x33, 0xef, 0xd0, +0x41, 0x80, 0x24, 0x20, 0x90, 0x24, 0x20, 0x98, 0x10, 0x10, 0x80, 0x90, +0x58, 0x80, 0x90, 0x28, 0x24, 0x1f, 0x90, 0x24, 0x1f, 0x98, 0x81, 0x24, +0x1f, 0x50, 0x92, 0x68, 0x91, 0x00, 0x80, 0x90, 0x90, 0x90, 0x30, 0x80, +0x24, 0x20, 0x00, 0x90, 0x38, 0xa4, 0x1f, 0xf8, 0x34, 0x06, 0x80, 0x34, +0x05, 0x80, 0x90, 0x28, 0x80, 0x34, 0x0f, 0xa4, 0x1f, 0xe0, 0x34, 0x0e, +0x80, 0x90, 0xc0, 0x90, 0x60, 0x90, 0x28, 0x80, 0x34, 0x09, 0xa4, 0x1f, +0xf0, 0x34, 0x08, 0x90, 0x28, 0x80, 0x34, 0x04, 0xa4, 0x1f, 0xe8, 0x34, +0x03, 0x90, 0x50, 0x90, 0x28, 0x80, 0x34, 0x0d, 0x80, 0x34, 0x0c, 0x90, +0x28, 0x24, 0x20, 0x88, 0x24, 0x20, 0x80, 0x90, 0x58, 0x80, 0x10, 0x10, +0x80, 0x10, 0x10, 0x80, 0x33, 0xfb, 0x80, 0x90, 0x40, 0x10, 0x10, 0x80, +0x24, 0x1f, 0x60, 0x80, 0x10, 0x10, 0x80, 0x33, 0xfa, 0x91, 0x58, 0x91, +0x00, 0x90, 0x80, 0x81, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, 0xf6, 0x80, +0x33, 0xf7, 0x81, 0x33, 0xee, 0x81, 0x90, 0x50, 0x90, 0x28, 0x80, 0x33, +0xf4, 0x80, 0x33, 0xf5, 0x81, 0x33, 0xed, 0x83, 0x90, 0x28, 0x24, 0x1f, +0x80, 0x24, 0x1f, 0x88, 0x90, 0xe8, 0x81, 0x90, 0x88, 0x90, 0x38, 0x10, +0x10, 0x80, 0x34, 0x07, 0x90, 0x28, 0x80, 0x34, 0x02, 0x80, 0x34, 0x01, +0x80, 0x90, 0x28, 0x80, 0x34, 0x0b, 0x80, 0x34, 0x0a, 0x82, 0x10, 0x10, +0x80, 0x24, 0x1f, 0x58, 0x97, 0x10, 0x9e, 0x10, 0x06, 0x98, 0x93, 0x00, +0x91, 0x80, 0x90, 0xc0, 0x90, 0x60, 0x90, 0x38, 0xa4, 0x03, 0x80, 0x30, +0x71, 0x24, 0x03, 0x78, 0x90, 0x38, 0xa4, 0x04, 0x10, 0x30, 0x83, 0x24, +0x04, 0x08, 0x90, 0x60, 0x90, 0x38, 0xa4, 0x05, 0x30, 0x30, 0xa7, 0x24, +0x05, 0x28, 0x90, 0x38, 0xa4, 0x04, 0xa0, 0x30, 0x95, 0x24, 0x04, 0x98, +0x90, 0xc0, 0x90, 0x60, 0x90, 0x38, 0xa4, 0x03, 0x70, 0x30, 0x6c, 0x24, +0x03, 0x68, 0x90, 0x38, 0xa4, 0x04, 0x00, 0x30, 0x7e, 0x24, 0x03, 0xf8, +0x90, 0x60, 0x90, 0x38, 0xa4, 0x05, 0x20, 0x30, 0xa2, 0x24, 0x05, 0x18, +0x90, 0x38, 0xa4, 0x04, 0x90, 0x30, 0x90, 0x24, 0x04, 0x88, 0x91, 0x80, +0x90, 0xc0, 0x90, 0x60, 0x90, 0x38, 0xa4, 0x03, 0x58, 0x30, 0x69, 0x24, +0x03, 0x50, 0x90, 0x38, 0xa4, 0x03, 0xe8, 0x30, 0x7b, 0x24, 0x03, 0xe0, +0x90, 0x60, 0x90, 0x38, 0xa4, 0x05, 0x08, 0x30, 0x9f, 0x24, 0x05, 0x00, +0x90, 0x38, 0xa4, 0x04, 0x78, 0x30, 0x8d, 0x24, 0x04, 0x70, 0x90, 0xc0, +0x90, 0x60, 0x90, 0x38, 0xa4, 0x03, 0x40, 0x30, 0x66, 0x24, 0x03, 0x38, +0x90, 0x38, 0xa4, 0x03, 0xd0, 0x30, 0x78, 0x24, 0x03, 0xc8, 0x90, 0x60, +0x90, 0x38, 0xa4, 0x04, 0xf0, 0x30, 0x9c, 0x24, 0x04, 0xe8, 0x90, 0x38, +0xa4, 0x04, 0x60, 0x30, 0x8a, 0x24, 0x04, 0x58, 0x10, 0x10, 0x80, 0x10, +0x10, 0x10, 0x10, 0x90, 0x38, 0xa4, 0x02, 0xf8, 0x30, 0x5d, 0x24, 0x02, +0xf0, 0xd7, 0x42, 0x00, 0xa4, 0x39, 0x58, 0x37, 0x2d, 0xa4, 0x39, 0x38, +0x37, 0x29, 0x9c, 0xe0, 0x06, 0x90, 0x93, 0x00, 0x91, 0x80, 0x90, 0xc0, +0x90, 0x60, 0x90, 0x38, 0xa4, 0x03, 0x28, 0x30, 0x63, 0x24, 0x03, 0x20, +0x90, 0x38, 0xa4, 0x03, 0xb8, 0x30, 0x75, 0x24, 0x03, 0xb0, 0x90, 0x60, +0x90, 0x38, 0xa4, 0x04, 0xd8, 0x30, 0x99, 0x24, 0x04, 0xd0, 0x90, 0x38, +0xa4, 0x04, 0x48, 0x30, 0x87, 0x24, 0x04, 0x40, 0x90, 0xc0, 0x90, 0x60, +0x90, 0x38, 0xa4, 0x03, 0x10, 0x30, 0x60, 0x24, 0x03, 0x08, 0x90, 0x38, +0xa4, 0x03, 0xa0, 0x30, 0x72, 0x24, 0x03, 0x98, 0x90, 0x60, 0x90, 0x38, +0xa4, 0x04, 0xc0, 0x30, 0x96, 0x24, 0x04, 0xb8, 0x90, 0x38, 0xa4, 0x04, +0x30, 0x30, 0x84, 0x24, 0x04, 0x28, 0x10, 0x10, 0x90, 0xe0, 0x90, 0x70, +0x90, 0x38, 0xa4, 0x02, 0x88, 0x30, 0x52, 0xa4, 0x02, 0x78, 0x30, 0x50, +0x90, 0x38, 0xa4, 0x02, 0x70, 0x30, 0x4b, 0xa4, 0x02, 0x60, 0x30, 0x4d, +0x90, 0x70, 0x90, 0x38, 0xa4, 0x02, 0x50, 0x30, 0x43, 0xa4, 0x02, 0x40, +0x30, 0x49, 0x90, 0x38, 0xa4, 0x02, 0x38, 0x30, 0x44, 0xa4, 0x02, 0x28, +0x30, 0x46, 0x91, 0x48, 0x80, 0x90, 0xa0, 0x90, 0x50, 0x90, 0x28, 0x80, +0x30, 0x56, 0x24, 0x02, 0xa8, 0x90, 0x28, 0x80, 0x30, 0x58, 0x24, 0x02, +0xb8, 0x90, 0x50, 0x90, 0x28, 0x80, 0x30, 0x5c, 0x24, 0x02, 0xd8, 0x90, +0x28, 0x80, 0x30, 0x5a, 0x24, 0x02, 0xc8, 0x80, 0x10, 0x10, 0x10, 0x10, +0x90, 0x28, 0x80, 0x30, 0x53, 0x24, 0x02, 0xa0, 0xd7, 0x42, 0x00, 0xa4, +0x39, 0x60, 0x37, 0x2e, 0xa4, 0x39, 0x40, 0x37, 0x2a, 0xa0, 0x14, 0x68, +0xa0, 0x10, 0x90, 0xa0, 0x0c, 0x60, 0x9e, 0x88, 0x09, 0xd0, 0x94, 0xf0, +0x90, 0xb0, 0x88, 0x00, 0x68, 0x84, 0x10, 0x10, 0xc9, 0xe1, 0x4c, 0x40, +0x85, 0x35, 0x4d, 0xcb, 0x61, 0x45, 0x00, 0x85, 0x35, 0x23, 0x9a, 0x00, +0x03, 0xf8, 0x91, 0x98, 0x80, 0x91, 0x10, 0x90, 0xa0, 0x90, 0x68, 0x90, +0x20, 0x3a, 0x75, 0xc9, 0xe2, 0x9c, 0xc0, 0x85, 0x35, 0x4b, 0xa4, 0x53, +0x88, 0x3a, 0x72, 0x90, 0x38, 0xa4, 0x53, 0x50, 0x3a, 0x6b, 0xa4, 0x53, +0x40, 0x3a, 0x69, 0x90, 0x48, 0x10, 0x10, 0xa4, 0x53, 0x08, 0x3a, 0x62, +0x10, 0x10, 0x80, 0x3a, 0x5e, 0x81, 0x10, 0x10, 0x80, 0xa4, 0x52, 0xd8, +0x3a, 0x5c, 0x91, 0xb0, 0x91, 0x60, 0x90, 0xe0, 0x90, 0x70, 0x90, 0x38, +0xa4, 0x53, 0x78, 0x3a, 0x70, 0xa4, 0x53, 0x68, 0x3a, 0x6e, 0x90, 0x38, +0xa4, 0x53, 0x30, 0x3a, 0x67, 0xa4, 0x53, 0x20, 0x3a, 0x65, 0x90, 0x48, +0x10, 0x10, 0xa4, 0x52, 0xf8, 0x3a, 0x60, 0x10, 0x10, 0x80, 0x3a, 0x5d, +0x90, 0x28, 0x80, 0x3a, 0x56, 0x80, 0x3a, 0x55, 0x81, 0x10, 0x10, 0x80, +0xa4, 0x52, 0xc8, 0x3a, 0x5a, 0xcb, 0x61, 0x44, 0xc0, 0x85, 0x35, 0x22, +0x90, 0xd8, 0x88, 0x00, 0x90, 0x84, 0x90, 0x38, 0xc1, 0xc0, 0x85, 0x3a, +0x78, 0xc9, 0xe1, 0x4c, 0x00, 0x85, 0x35, 0x49, 0xcb, 0x61, 0x44, 0x80, +0x85, 0x35, 0x21, 0x88, 0x00, 0x68, 0x84, 0x10, 0x10, 0xc9, 0xe1, 0x4b, +0xc0, 0x85, 0x35, 0x47, 0xcb, 0x61, 0x44, 0x40, 0x85, 0x35, 0x20, 0x91, +0xf8, 0x90, 0xb0, 0x88, 0x00, 0x68, 0x84, 0x10, 0x10, 0xc9, 0xe1, 0x4b, +0x40, 0x85, 0x35, 0x43, 0xcb, 0x61, 0x43, 0xc0, 0x85, 0x35, 0x1e, 0x88, +0x01, 0x00, 0x90, 0xa0, 0x81, 0x90, 0x70, 0x80, 0x90, 0x20, 0x3a, 0x6c, +0xc9, 0xe1, 0x4b, 0x00, 0x85, 0x35, 0x41, 0x81, 0x3a, 0x63, 0x81, 0x10, +0x10, 0x80, 0xa4, 0x52, 0xb8, 0x3a, 0x58, 0xcb, 0x61, 0x43, 0x80, 0x85, +0x35, 0x1d, 0x90, 0xb0, 0x88, 0x00, 0x68, 0x84, 0x10, 0x10, 0xc9, 0xe1, +0x4a, 0xc0, 0x85, 0x35, 0x3f, 0xcb, 0x61, 0x43, 0x40, 0x85, 0x35, 0x1c, +0x88, 0x00, 0x68, 0x84, 0x10, 0x10, 0xc9, 0xe1, 0x4a, 0x80, 0x85, 0x35, +0x3d, 0xcb, 0x61, 0x43, 0x00, 0x85, 0x35, 0x1b, 0x92, 0x38, 0x81, 0x91, +0x68, 0x91, 0x18, 0x90, 0x80, 0x90, 0x40, 0x80, 0xa4, 0x54, 0x38, 0x3a, +0x88, 0x80, 0xa4, 0x54, 0x30, 0x3a, 0x85, 0x90, 0x28, 0x81, 0x3a, 0x84, +0x90, 0x38, 0xa4, 0x54, 0x10, 0x3a, 0x83, 0xa4, 0x54, 0x00, 0x3a, 0x81, +0x90, 0x28, 0x80, 0x3a, 0x7f, 0x80, 0x3a, 0x7e, 0x80, 0x90, 0x40, 0x10, +0x10, 0x80, 0x24, 0x53, 0xe8, 0x10, 0x10, 0x90, 0x38, 0xa4, 0x53, 0xd8, +0x3a, 0x7c, 0xa4, 0x53, 0xc8, 0x3a, 0x7a, 0x90, 0x28, 0x80, 0x3a, 0x77, +0x80, 0x3a, 0x76, 0x9a, 0xd0, 0x03, 0xe0, 0x91, 0x60, 0x90, 0xb0, 0x88, +0x00, 0x68, 0x84, 0x10, 0x10, 0xc9, 0xe1, 0x4a, 0x00, 0x85, 0x35, 0x39, +0xcb, 0x61, 0x42, 0x80, 0x85, 0x35, 0x19, 0x88, 0x00, 0x68, 0x84, 0x10, +0x10, 0xc9, 0xe1, 0x49, 0xc0, 0x85, 0x35, 0x37, 0xcb, 0x61, 0x42, 0x40, +0x85, 0x35, 0x18, 0x90, 0xb0, 0x88, 0x00, 0x68, 0x84, 0x10, 0x10, 0xc9, +0xe1, 0x49, 0x80, 0x85, 0x35, 0x35, 0xcb, 0x61, 0x42, 0x00, 0x85, 0x35, +0x17, 0x88, 0x00, 0x68, 0x84, 0x10, 0x10, 0xc9, 0xe1, 0x49, 0x40, 0x85, +0x35, 0x33, 0xcb, 0x61, 0x41, 0xc0, 0x85, 0x35, 0x16, 0x90, 0x90, 0x90, +0x48, 0xcb, 0xa1, 0x40, 0x00, 0x85, 0x35, 0x05, 0xcb, 0xa1, 0x3f, 0xc0, +0x85, 0x35, 0x04, 0x90, 0x48, 0xcb, 0xa1, 0x3f, 0x80, 0x85, 0x35, 0x03, +0xcb, 0xa1, 0x3f, 0x40, 0x85, 0x35, 0x02, 0xcb, 0xa2, 0x94, 0xc0, 0x80, +0x3a, 0x54, 0x92, 0x40, 0x91, 0x20, 0x90, 0x90, 0x90, 0x48, 0x8c, 0x27, +0x60, 0x84, 0x24, 0x27, 0xd8, 0x8c, 0x27, 0x58, 0x84, 0x24, 0x27, 0xd0, +0x90, 0x48, 0x8c, 0x27, 0x50, 0x84, 0x24, 0x27, 0xc8, 0x8c, 0x27, 0x48, +0x84, 0x24, 0x27, 0xc0, 0x90, 0x90, 0x90, 0x48, 0x8c, 0x27, 0x38, 0x84, +0x24, 0x27, 0xb0, 0x8c, 0x27, 0x30, 0x84, 0x24, 0x27, 0xa8, 0x90, 0x48, +0x8c, 0x27, 0x28, 0x84, 0x24, 0x27, 0xa0, 0x8c, 0x27, 0x20, 0x84, 0x24, +0x27, 0x98, 0x91, 0x20, 0x90, 0x90, 0x90, 0x48, 0x8c, 0x27, 0x10, 0x84, +0x24, 0x27, 0x88, 0x8c, 0x27, 0x08, 0x84, 0x24, 0x27, 0x80, 0x90, 0x48, +0x8c, 0x27, 0x00, 0x84, 0x24, 0x27, 0x78, 0x8c, 0x26, 0xf8, 0x84, 0x24, +0x27, 0x70, 0x90, 0x38, 0xa4, 0x26, 0xe0, 0x34, 0xdd, 0xa4, 0x26, 0xd0, +0x34, 0xdb, 0xa0, 0x0f, 0x50, 0xa0, 0x09, 0x08, 0x9a, 0x30, 0x04, 0x40, +0x91, 0x90, 0x90, 0xc8, 0x98, 0x50, 0x00, 0x80, 0xe5, 0x22, 0x92, 0xc0, +0x3a, 0x43, 0xe5, 0x22, 0x8a, 0xc0, 0x3a, 0x3f, 0xcb, 0x61, 0x32, 0x40, +0x85, 0x34, 0xd8, 0x98, 0x50, 0x00, 0x80, 0xe5, 0x22, 0x82, 0xc0, 0x3a, +0x03, 0xe5, 0x22, 0x7a, 0xc0, 0x39, 0xff, 0xcb, 0x61, 0x32, 0x00, 0x85, +0x34, 0xd7, 0x90, 0x48, 0xcb, 0xa1, 0x31, 0xc0, 0x85, 0x34, 0xd6, 0xcb, +0xa1, 0x31, 0x80, 0x85, 0x34, 0xd5, 0x91, 0x90, 0x90, 0xc8, 0x98, 0x50, +0x00, 0x80, 0xe5, 0x22, 0x6c, 0xc0, 0x39, 0xcb, 0xe5, 0x22, 0x60, 0xc0, +0x39, 0x9b, 0xcb, 0x61, 0x31, 0x00, 0x85, 0x34, 0xd3, 0x98, 0x50, 0x00, +0x80, 0xe5, 0x22, 0x54, 0xc0, 0x39, 0x6b, 0xe5, 0x22, 0x48, 0xc0, 0x39, +0x3b, 0xcb, 0x61, 0x30, 0xc0, 0x85, 0x34, 0xd2, 0x90, 0x48, 0xcb, 0xa1, +0x30, 0x80, 0x85, 0x34, 0xd1, 0xcb, 0xa1, 0x30, 0x40, 0x85, 0x34, 0xd0, +0x92, 0x20, 0x91, 0x30, 0x90, 0xb8, 0xd5, 0x03, 0x00, 0xc0, 0xc0, 0x81, +0x8c, 0x01, 0xa0, 0x84, 0x30, 0x3e, 0xc0, 0xc0, 0x81, 0x8c, 0x01, 0x80, +0x84, 0x30, 0x3c, 0xd5, 0x02, 0x00, 0xc0, 0xc0, 0x81, 0x30, 0x28, 0xc0, +0xc0, 0x81, 0x30, 0x24, 0x90, 0x78, 0xd5, 0x02, 0x00, 0xc0, 0xc0, 0x81, +0x30, 0x1c, 0xc0, 0xc0, 0x81, 0x30, 0x18, 0xd5, 0x02, 0x00, 0xc0, 0xc0, +0x81, 0x30, 0x10, 0xc0, 0xc0, 0x81, 0x30, 0x0c, 0x91, 0x70, 0x90, 0xd8, +0xd5, 0x03, 0x80, 0xc8, 0xe2, 0x40, 0xc0, 0x81, 0x8c, 0x01, 0xc0, 0x84, +0x30, 0x40, 0xc8, 0xe2, 0x42, 0xc0, 0x81, 0x8c, 0x01, 0x90, 0x84, 0x30, +0x3d, 0xd5, 0x02, 0x80, 0xc8, 0xe2, 0x3f, 0xc0, 0x81, 0x30, 0x2c, 0xc8, +0xe2, 0x3a, 0x40, 0x81, 0x30, 0x26, 0x90, 0x98, 0xd5, 0x02, 0x80, 0xc8, +0xe2, 0x2f, 0x40, 0x81, 0x30, 0x20, 0xc8, 0xe2, 0x31, 0x40, 0x81, 0x30, +0x1a, 0xd5, 0x02, 0x80, 0xc8, 0xe2, 0x2e, 0x40, 0x81, 0x30, 0x14, 0xc8, +0xe2, 0x28, 0xc0, 0x81, 0x30, 0x0e, 0x9a, 0x30, 0x04, 0x40, 0x91, 0x90, +0x90, 0xc8, 0x98, 0x50, 0x00, 0x80, 0xe5, 0x22, 0x86, 0xc0, 0x3a, 0x13, +0xe5, 0x22, 0x88, 0xc0, 0x3a, 0x37, 0xcb, 0x61, 0x2f, 0xc0, 0x85, 0x34, +0xce, 0x98, 0x50, 0x00, 0x80, 0xe5, 0x22, 0x76, 0xc0, 0x39, 0xd3, 0xe5, +0x22, 0x78, 0xc0, 0x39, 0xf7, 0xcb, 0x61, 0x2f, 0x80, 0x85, 0x34, 0xcd, +0x90, 0x48, 0xcb, 0xa1, 0x2f, 0x40, 0x85, 0x34, 0xcc, 0xcb, 0xa1, 0x2f, +0x00, 0x85, 0x34, 0xcb, 0x91, 0x90, 0x90, 0xc8, 0x98, 0x50, 0x00, 0x80, +0xe5, 0x22, 0x68, 0xc0, 0x39, 0xbb, 0xe5, 0x22, 0x5c, 0xc0, 0x39, 0x8b, +0xcb, 0x61, 0x2d, 0x40, 0x85, 0x34, 0xba, 0x98, 0x50, 0x00, 0x80, 0xe5, +0x22, 0x50, 0xc0, 0x39, 0x5b, 0xe5, 0x22, 0x44, 0xc0, 0x39, 0x2b, 0xcb, +0x61, 0x2d, 0x00, 0x85, 0x34, 0xb9, 0x90, 0x48, 0xcb, 0xa1, 0x2c, 0xc0, +0x85, 0x34, 0xb8, 0xcb, 0xa1, 0x2c, 0x80, 0x85, 0x34, 0xb7, 0x91, 0x00, +0x90, 0x80, 0x90, 0x40, 0xe5, 0x20, 0x02, 0x40, 0x30, 0x0a, 0xe5, 0x20, +0x01, 0x80, 0x30, 0x07, 0x90, 0x40, 0xe5, 0x20, 0x00, 0xc0, 0x30, 0x04, +0xe5, 0x20, 0x00, 0x00, 0x30, 0x01, 0x90, 0x80, 0x90, 0x40, 0xe5, 0x22, +0x35, 0xc0, 0x38, 0xcd, 0xe5, 0x22, 0x38, 0x00, 0x38, 0xf5, 0x90, 0x40, +0xe5, 0x22, 0x24, 0x40, 0x38, 0x87, 0xe5, 0x22, 0x26, 0x80, 0x38, 0xaf, +0x80, 0x99, 0x28, 0x02, 0xf0, 0x8c, 0x25, 0x48, 0x90, 0x80, 0x90, 0x40, +0xe5, 0x22, 0x8c, 0xc0, 0x3a, 0x2f, 0xe5, 0x22, 0x89, 0xc0, 0x3a, 0x3b, +0x90, 0x40, 0xe5, 0x22, 0x7c, 0xc0, 0x39, 0xef, 0xe5, 0x22, 0x79, 0xc0, +0x39, 0xfb, 0x91, 0x48, 0x90, 0xc8, 0x98, 0x50, 0x00, 0x80, 0xe5, 0x22, +0x6a, 0xc0, 0x39, 0xc3, 0xe5, 0x22, 0x5e, 0xc0, 0x39, 0x93, 0xcb, 0x61, +0x2b, 0x00, 0x85, 0x34, 0xb0, 0x90, 0x40, 0xe5, 0x22, 0x52, 0xc0, 0x39, +0x63, 0xe5, 0x22, 0x46, 0xc0, 0x39, 0x33, 0x90, 0x48, 0xcb, 0xa1, 0x2a, +0x80, 0x85, 0x34, 0xae, 0xcb, 0xa1, 0x2a, 0xc0, 0x85, 0x34, 0xaf, 0x10, +0x10, 0x90, 0x80, 0x90, 0x40, 0xe5, 0x22, 0x3c, 0x40, 0x38, 0xed, 0xe5, +0x22, 0x39, 0x40, 0x38, 0xfb, 0x90, 0x40, 0xe5, 0x22, 0x2a, 0xc0, 0x38, +0xa7, 0xe5, 0x22, 0x27, 0xc0, 0x38, 0xb5, +}; + +static const struct ia64_dis_names ia64_dis_names[] = { +{ 0x51, 41, 0, 10 }, +{ 0x31, 41, 1, 20 }, +{ 0x11, 42, 0, 19 }, +{ 0x29, 41, 0, 12 }, +{ 0x19, 41, 1, 24 }, +{ 0x9, 42, 0, 23 }, +{ 0x15, 41, 0, 14 }, +{ 0xd, 41, 1, 28 }, +{ 0x5, 42, 0, 27 }, +{ 0xb, 41, 0, 16 }, +{ 0x7, 41, 1, 32 }, +{ 0x3, 42, 0, 31 }, +{ 0x51, 39, 1, 58 }, +{ 0x50, 39, 0, 34 }, +{ 0xd1, 39, 1, 57 }, +{ 0xd0, 39, 0, 33 }, +{ 0x31, 39, 1, 68 }, +{ 0x30, 39, 1, 44 }, +{ 0x11, 40, 1, 67 }, +{ 0x10, 40, 0, 43 }, +{ 0x71, 39, 1, 66 }, +{ 0x70, 39, 1, 42 }, +{ 0x31, 40, 1, 65 }, +{ 0x30, 40, 0, 41 }, +{ 0x29, 39, 1, 60 }, +{ 0x28, 39, 0, 36 }, +{ 0x69, 39, 1, 59 }, +{ 0x68, 39, 0, 35 }, +{ 0x19, 39, 1, 72 }, +{ 0x18, 39, 1, 48 }, +{ 0x9, 40, 1, 71 }, +{ 0x8, 40, 0, 47 }, +{ 0x39, 39, 1, 70 }, +{ 0x38, 39, 1, 46 }, +{ 0x19, 40, 1, 69 }, +{ 0x18, 40, 0, 45 }, +{ 0x15, 39, 1, 62 }, +{ 0x14, 39, 0, 38 }, +{ 0x35, 39, 1, 61 }, +{ 0x34, 39, 0, 37 }, +{ 0xd, 39, 1, 76 }, +{ 0xc, 39, 1, 52 }, +{ 0x5, 40, 1, 75 }, +{ 0x4, 40, 0, 51 }, +{ 0x1d, 39, 1, 74 }, +{ 0x1c, 39, 1, 50 }, +{ 0xd, 40, 1, 73 }, +{ 0xc, 40, 0, 49 }, +{ 0xb, 39, 1, 64 }, +{ 0xa, 39, 0, 40 }, +{ 0x1b, 39, 1, 63 }, +{ 0x1a, 39, 0, 39 }, +{ 0x7, 39, 1, 80 }, +{ 0x6, 39, 1, 56 }, +{ 0x3, 40, 1, 79 }, +{ 0x2, 40, 0, 55 }, +{ 0xf, 39, 1, 78 }, +{ 0xe, 39, 1, 54 }, +{ 0x7, 40, 1, 77 }, +{ 0x6, 40, 0, 53 }, +{ 0x8, 38, 0, 82 }, +{ 0x18, 38, 0, 81 }, +{ 0x1, 38, 1, 86 }, +{ 0x2, 38, 0, 85 }, +{ 0x3, 38, 1, 84 }, +{ 0x4, 38, 0, 83 }, +{ 0x1, 336, 0, 87 }, +{ 0x20, 289, 0, 98 }, +{ 0x220, 289, 0, 94 }, +{ 0x1220, 289, 0, 91 }, +{ 0xa20, 289, 0, 92 }, +{ 0x620, 289, 0, 93 }, +{ 0x120, 289, 0, 95 }, +{ 0xa0, 289, 0, 96 }, +{ 0x60, 289, 0, 97 }, +{ 0x10, 289, 0, 102 }, +{ 0x90, 289, 0, 99 }, +{ 0x50, 289, 0, 100 }, +{ 0x30, 289, 0, 101 }, +{ 0x8, 289, 0, 103 }, +{ 0x4, 289, 0, 104 }, +{ 0x2, 289, 0, 105 }, +{ 0x1, 289, 0, 106 }, +{ 0x1, 411, 0, 108 }, +{ 0x3, 411, 0, 107 }, +{ 0x2, 417, 0, 109 }, +{ 0x1, 417, 0, 110 }, +{ 0x2, 413, 0, 111 }, +{ 0x1, 413, 0, 112 }, +{ 0x2, 415, 0, 113 }, +{ 0x1, 415, 0, 114 }, +{ 0x2, 419, 0, 115 }, +{ 0x1, 419, 0, 116 }, +{ 0x1, 268, 0, 143 }, +{ 0x5, 268, 0, 141 }, +{ 0x3, 268, 0, 142 }, +{ 0x140, 277, 0, 119 }, +{ 0x540, 277, 0, 117 }, +{ 0x340, 277, 0, 118 }, +{ 0xc0, 277, 0, 131 }, +{ 0x2c0, 277, 0, 129 }, +{ 0x1c0, 277, 0, 130 }, +{ 0x20, 277, 0, 146 }, +{ 0xa0, 277, 0, 144 }, +{ 0x60, 277, 0, 145 }, +{ 0x10, 277, 0, 158 }, +{ 0x50, 277, 0, 156 }, +{ 0x30, 277, 0, 157 }, +{ 0x8, 277, 0, 170 }, +{ 0x28, 277, 0, 168 }, +{ 0x18, 277, 0, 169 }, +{ 0x4, 277, 0, 180 }, +{ 0x2, 277, 0, 181 }, +{ 0x1, 277, 0, 182 }, +{ 0x140, 271, 0, 122 }, +{ 0x540, 271, 0, 120 }, +{ 0x340, 271, 0, 121 }, +{ 0xc0, 271, 0, 134 }, +{ 0x2c0, 271, 0, 132 }, +{ 0x1c0, 271, 0, 133 }, +{ 0x20, 271, 0, 149 }, +{ 0xa0, 271, 0, 147 }, +{ 0x60, 271, 0, 148 }, +{ 0x10, 271, 0, 161 }, +{ 0x50, 271, 0, 159 }, +{ 0x30, 271, 0, 160 }, +{ 0x8, 271, 0, 173 }, +{ 0x28, 271, 0, 171 }, +{ 0x18, 271, 0, 172 }, +{ 0x4, 271, 0, 183 }, +{ 0x2, 271, 0, 184 }, +{ 0x1, 271, 0, 185 }, +{ 0x140, 274, 0, 125 }, +{ 0x540, 274, 0, 123 }, +{ 0x340, 274, 0, 124 }, +{ 0xc0, 274, 0, 137 }, +{ 0x2c0, 274, 0, 135 }, +{ 0x1c0, 274, 0, 136 }, +{ 0x20, 274, 0, 152 }, +{ 0xa0, 274, 0, 150 }, +{ 0x60, 274, 0, 151 }, +{ 0x10, 274, 0, 164 }, +{ 0x50, 274, 0, 162 }, +{ 0x30, 274, 0, 163 }, +{ 0x8, 274, 0, 176 }, +{ 0x28, 274, 0, 174 }, +{ 0x18, 274, 0, 175 }, +{ 0x4, 274, 0, 186 }, +{ 0x2, 274, 0, 187 }, +{ 0x1, 274, 0, 188 }, +{ 0x140, 286, 0, 128 }, +{ 0x540, 286, 0, 126 }, +{ 0x340, 286, 0, 127 }, +{ 0xc0, 286, 0, 140 }, +{ 0x2c0, 286, 0, 138 }, +{ 0x1c0, 286, 0, 139 }, +{ 0x20, 286, 0, 155 }, +{ 0xa0, 286, 0, 153 }, +{ 0x60, 286, 0, 154 }, +{ 0x10, 286, 0, 167 }, +{ 0x50, 286, 0, 165 }, +{ 0x30, 286, 0, 166 }, +{ 0x8, 286, 0, 179 }, +{ 0x28, 286, 0, 177 }, +{ 0x18, 286, 0, 178 }, +{ 0x4, 286, 0, 189 }, +{ 0x2, 286, 0, 190 }, +{ 0x1, 286, 0, 191 }, +{ 0x8, 390, 0, 192 }, +{ 0x4, 390, 0, 193 }, +{ 0x2, 390, 0, 194 }, +{ 0x1, 390, 0, 195 }, +{ 0x20, 288, 0, 203 }, +{ 0x220, 288, 0, 199 }, +{ 0x1220, 288, 0, 196 }, +{ 0xa20, 288, 0, 197 }, +{ 0x620, 288, 0, 198 }, +{ 0x120, 288, 0, 200 }, +{ 0xa0, 288, 0, 201 }, +{ 0x60, 288, 0, 202 }, +{ 0x10, 288, 0, 207 }, +{ 0x90, 288, 0, 204 }, +{ 0x50, 288, 0, 205 }, +{ 0x30, 288, 0, 206 }, +{ 0x8, 288, 0, 208 }, +{ 0x4, 288, 0, 209 }, +{ 0x2, 288, 0, 210 }, +{ 0x1, 288, 0, 211 }, +{ 0x20, 287, 0, 219 }, +{ 0x220, 287, 0, 215 }, +{ 0x1220, 287, 0, 212 }, +{ 0xa20, 287, 0, 213 }, +{ 0x620, 287, 0, 214 }, +{ 0x120, 287, 0, 216 }, +{ 0xa0, 287, 0, 217 }, +{ 0x60, 287, 0, 218 }, +{ 0x10, 287, 0, 223 }, +{ 0x90, 287, 0, 220 }, +{ 0x50, 287, 0, 221 }, +{ 0x30, 287, 0, 222 }, +{ 0x8, 287, 0, 224 }, +{ 0x4, 287, 0, 225 }, +{ 0x2, 287, 0, 226 }, +{ 0x1, 287, 0, 227 }, +{ 0x140, 279, 0, 230 }, +{ 0x540, 279, 0, 228 }, +{ 0x340, 279, 0, 229 }, +{ 0xc0, 279, 0, 239 }, +{ 0x2c0, 279, 0, 237 }, +{ 0x1c0, 279, 0, 238 }, +{ 0x20, 279, 0, 248 }, +{ 0xa0, 279, 0, 246 }, +{ 0x60, 279, 0, 247 }, +{ 0x10, 279, 0, 257 }, +{ 0x50, 279, 0, 255 }, +{ 0x30, 279, 0, 256 }, +{ 0x8, 279, 0, 266 }, +{ 0x28, 279, 0, 264 }, +{ 0x18, 279, 0, 265 }, +{ 0x4, 279, 0, 273 }, +{ 0x2, 279, 0, 274 }, +{ 0x1, 279, 0, 275 }, +{ 0x140, 281, 0, 233 }, +{ 0x540, 281, 0, 231 }, +{ 0x340, 281, 0, 232 }, +{ 0xc0, 281, 0, 242 }, +{ 0x2c0, 281, 0, 240 }, +{ 0x1c0, 281, 0, 241 }, +{ 0x20, 281, 0, 251 }, +{ 0xa0, 281, 0, 249 }, +{ 0x60, 281, 0, 250 }, +{ 0x10, 281, 0, 260 }, +{ 0x50, 281, 0, 258 }, +{ 0x30, 281, 0, 259 }, +{ 0x8, 281, 0, 269 }, +{ 0x28, 281, 0, 267 }, +{ 0x18, 281, 0, 268 }, +{ 0x4, 281, 0, 276 }, +{ 0x2, 281, 0, 277 }, +{ 0x1, 281, 0, 278 }, +{ 0x140, 283, 0, 236 }, +{ 0x540, 283, 0, 234 }, +{ 0x340, 283, 0, 235 }, +{ 0xc0, 283, 0, 245 }, +{ 0x2c0, 283, 0, 243 }, +{ 0x1c0, 283, 0, 244 }, +{ 0x20, 283, 0, 254 }, +{ 0xa0, 283, 0, 252 }, +{ 0x60, 283, 0, 253 }, +{ 0x10, 283, 0, 263 }, +{ 0x50, 283, 0, 261 }, +{ 0x30, 283, 0, 262 }, +{ 0x8, 283, 0, 272 }, +{ 0x28, 283, 0, 270 }, +{ 0x18, 283, 0, 271 }, +{ 0x4, 283, 0, 279 }, +{ 0x2, 283, 0, 280 }, +{ 0x1, 283, 0, 281 }, +{ 0x140, 278, 0, 284 }, +{ 0x540, 278, 0, 282 }, +{ 0x340, 278, 0, 283 }, +{ 0xc0, 278, 0, 293 }, +{ 0x2c0, 278, 0, 291 }, +{ 0x1c0, 278, 0, 292 }, +{ 0x20, 278, 0, 302 }, +{ 0xa0, 278, 0, 300 }, +{ 0x60, 278, 0, 301 }, +{ 0x10, 278, 0, 311 }, +{ 0x50, 278, 0, 309 }, +{ 0x30, 278, 0, 310 }, +{ 0x8, 278, 0, 320 }, +{ 0x28, 278, 0, 318 }, +{ 0x18, 278, 0, 319 }, +{ 0x4, 278, 0, 327 }, +{ 0x2, 278, 0, 328 }, +{ 0x1, 278, 0, 329 }, +{ 0x140, 280, 0, 287 }, +{ 0x540, 280, 0, 285 }, +{ 0x340, 280, 0, 286 }, +{ 0xc0, 280, 0, 296 }, +{ 0x2c0, 280, 0, 294 }, +{ 0x1c0, 280, 0, 295 }, +{ 0x20, 280, 0, 305 }, +{ 0xa0, 280, 0, 303 }, +{ 0x60, 280, 0, 304 }, +{ 0x10, 280, 0, 314 }, +{ 0x50, 280, 0, 312 }, +{ 0x30, 280, 0, 313 }, +{ 0x8, 280, 0, 323 }, +{ 0x28, 280, 0, 321 }, +{ 0x18, 280, 0, 322 }, +{ 0x4, 280, 0, 330 }, +{ 0x2, 280, 0, 331 }, +{ 0x1, 280, 0, 332 }, +{ 0x140, 282, 0, 290 }, +{ 0x540, 282, 0, 288 }, +{ 0x340, 282, 0, 289 }, +{ 0xc0, 282, 0, 299 }, +{ 0x2c0, 282, 0, 297 }, +{ 0x1c0, 282, 0, 298 }, +{ 0x20, 282, 0, 308 }, +{ 0xa0, 282, 0, 306 }, +{ 0x60, 282, 0, 307 }, +{ 0x10, 282, 0, 317 }, +{ 0x50, 282, 0, 315 }, +{ 0x30, 282, 0, 316 }, +{ 0x8, 282, 0, 326 }, +{ 0x28, 282, 0, 324 }, +{ 0x18, 282, 0, 325 }, +{ 0x4, 282, 0, 333 }, +{ 0x2, 282, 0, 334 }, +{ 0x1, 282, 0, 335 }, +{ 0x1, 410, 0, 337 }, +{ 0x3, 410, 0, 336 }, +{ 0x2, 416, 0, 338 }, +{ 0x1, 416, 0, 339 }, +{ 0x2, 412, 0, 340 }, +{ 0x1, 412, 0, 341 }, +{ 0x2, 414, 0, 342 }, +{ 0x1, 414, 0, 343 }, +{ 0x2, 418, 0, 344 }, +{ 0x1, 418, 0, 345 }, +{ 0x1, 267, 0, 372 }, +{ 0x5, 267, 0, 370 }, +{ 0x3, 267, 0, 371 }, +{ 0x140, 276, 0, 348 }, +{ 0x540, 276, 0, 346 }, +{ 0x340, 276, 0, 347 }, +{ 0xc0, 276, 0, 360 }, +{ 0x2c0, 276, 0, 358 }, +{ 0x1c0, 276, 0, 359 }, +{ 0x20, 276, 0, 375 }, +{ 0xa0, 276, 0, 373 }, +{ 0x60, 276, 0, 374 }, +{ 0x10, 276, 0, 387 }, +{ 0x50, 276, 0, 385 }, +{ 0x30, 276, 0, 386 }, +{ 0x8, 276, 0, 399 }, +{ 0x28, 276, 0, 397 }, +{ 0x18, 276, 0, 398 }, +{ 0x4, 276, 0, 409 }, +{ 0x2, 276, 0, 410 }, +{ 0x1, 276, 0, 411 }, +{ 0x140, 270, 0, 351 }, +{ 0x540, 270, 0, 349 }, +{ 0x340, 270, 0, 350 }, +{ 0xc0, 270, 0, 363 }, +{ 0x2c0, 270, 0, 361 }, +{ 0x1c0, 270, 0, 362 }, +{ 0x20, 270, 0, 378 }, +{ 0xa0, 270, 0, 376 }, +{ 0x60, 270, 0, 377 }, +{ 0x10, 270, 0, 390 }, +{ 0x50, 270, 0, 388 }, +{ 0x30, 270, 0, 389 }, +{ 0x8, 270, 0, 402 }, +{ 0x28, 270, 0, 400 }, +{ 0x18, 270, 0, 401 }, +{ 0x4, 270, 0, 412 }, +{ 0x2, 270, 0, 413 }, +{ 0x1, 270, 0, 414 }, +{ 0x140, 273, 0, 354 }, +{ 0x540, 273, 0, 352 }, +{ 0x340, 273, 0, 353 }, +{ 0xc0, 273, 0, 366 }, +{ 0x2c0, 273, 0, 364 }, +{ 0x1c0, 273, 0, 365 }, +{ 0x20, 273, 0, 381 }, +{ 0xa0, 273, 0, 379 }, +{ 0x60, 273, 0, 380 }, +{ 0x10, 273, 0, 393 }, +{ 0x50, 273, 0, 391 }, +{ 0x30, 273, 0, 392 }, +{ 0x8, 273, 0, 405 }, +{ 0x28, 273, 0, 403 }, +{ 0x18, 273, 0, 404 }, +{ 0x4, 273, 0, 415 }, +{ 0x2, 273, 0, 416 }, +{ 0x1, 273, 0, 417 }, +{ 0x140, 285, 0, 357 }, +{ 0x540, 285, 0, 355 }, +{ 0x340, 285, 0, 356 }, +{ 0xc0, 285, 0, 369 }, +{ 0x2c0, 285, 0, 367 }, +{ 0x1c0, 285, 0, 368 }, +{ 0x20, 285, 0, 384 }, +{ 0xa0, 285, 0, 382 }, +{ 0x60, 285, 0, 383 }, +{ 0x10, 285, 0, 396 }, +{ 0x50, 285, 0, 394 }, +{ 0x30, 285, 0, 395 }, +{ 0x8, 285, 0, 408 }, +{ 0x28, 285, 0, 406 }, +{ 0x18, 285, 0, 407 }, +{ 0x4, 285, 0, 418 }, +{ 0x2, 285, 0, 419 }, +{ 0x1, 285, 0, 420 }, +{ 0x1, 266, 0, 447 }, +{ 0x5, 266, 0, 445 }, +{ 0x3, 266, 0, 446 }, +{ 0x140, 275, 0, 423 }, +{ 0x540, 275, 0, 421 }, +{ 0x340, 275, 0, 422 }, +{ 0xc0, 275, 0, 435 }, +{ 0x2c0, 275, 0, 433 }, +{ 0x1c0, 275, 0, 434 }, +{ 0x20, 275, 0, 450 }, +{ 0xa0, 275, 0, 448 }, +{ 0x60, 275, 0, 449 }, +{ 0x10, 275, 0, 462 }, +{ 0x50, 275, 0, 460 }, +{ 0x30, 275, 0, 461 }, +{ 0x8, 275, 0, 474 }, +{ 0x28, 275, 0, 472 }, +{ 0x18, 275, 0, 473 }, +{ 0x4, 275, 0, 484 }, +{ 0x2, 275, 0, 485 }, +{ 0x1, 275, 0, 486 }, +{ 0x140, 269, 0, 426 }, +{ 0x540, 269, 0, 424 }, +{ 0x340, 269, 0, 425 }, +{ 0xc0, 269, 0, 438 }, +{ 0x2c0, 269, 0, 436 }, +{ 0x1c0, 269, 0, 437 }, +{ 0x20, 269, 0, 453 }, +{ 0xa0, 269, 0, 451 }, +{ 0x60, 269, 0, 452 }, +{ 0x10, 269, 0, 465 }, +{ 0x50, 269, 0, 463 }, +{ 0x30, 269, 0, 464 }, +{ 0x8, 269, 0, 477 }, +{ 0x28, 269, 0, 475 }, +{ 0x18, 269, 0, 476 }, +{ 0x4, 269, 0, 487 }, +{ 0x2, 269, 0, 488 }, +{ 0x1, 269, 0, 489 }, +{ 0x140, 272, 0, 429 }, +{ 0x540, 272, 0, 427 }, +{ 0x340, 272, 0, 428 }, +{ 0xc0, 272, 0, 441 }, +{ 0x2c0, 272, 0, 439 }, +{ 0x1c0, 272, 0, 440 }, +{ 0x20, 272, 0, 456 }, +{ 0xa0, 272, 0, 454 }, +{ 0x60, 272, 0, 455 }, +{ 0x10, 272, 0, 468 }, +{ 0x50, 272, 0, 466 }, +{ 0x30, 272, 0, 467 }, +{ 0x8, 272, 0, 480 }, +{ 0x28, 272, 0, 478 }, +{ 0x18, 272, 0, 479 }, +{ 0x4, 272, 0, 490 }, +{ 0x2, 272, 0, 491 }, +{ 0x1, 272, 0, 492 }, +{ 0x140, 284, 0, 432 }, +{ 0x540, 284, 0, 430 }, +{ 0x340, 284, 0, 431 }, +{ 0xc0, 284, 0, 444 }, +{ 0x2c0, 284, 0, 442 }, +{ 0x1c0, 284, 0, 443 }, +{ 0x20, 284, 0, 459 }, +{ 0xa0, 284, 0, 457 }, +{ 0x60, 284, 0, 458 }, +{ 0x10, 284, 0, 471 }, +{ 0x50, 284, 0, 469 }, +{ 0x30, 284, 0, 470 }, +{ 0x8, 284, 0, 483 }, +{ 0x28, 284, 0, 481 }, +{ 0x18, 284, 0, 482 }, +{ 0x4, 284, 0, 493 }, +{ 0x2, 284, 0, 494 }, +{ 0x1, 284, 0, 495 }, +{ 0x8, 409, 0, 497 }, +{ 0x18, 409, 0, 496 }, +{ 0x4, 409, 0, 499 }, +{ 0xc, 409, 0, 498 }, +{ 0x2, 409, 0, 506 }, +{ 0x1, 409, 0, 507 }, +{ 0x4, 407, 0, 501 }, +{ 0xc, 407, 0, 500 }, +{ 0x2, 407, 0, 508 }, +{ 0x1, 407, 0, 509 }, +{ 0x4, 405, 0, 503 }, +{ 0xc, 405, 0, 502 }, +{ 0x2, 405, 0, 510 }, +{ 0x1, 405, 0, 511 }, +{ 0x4, 401, 0, 505 }, +{ 0xc, 401, 0, 504 }, +{ 0x2, 401, 0, 512 }, +{ 0x1, 401, 0, 513 }, +{ 0xa00, 265, 0, 528 }, +{ 0x2a00, 265, 0, 526 }, +{ 0x1a00, 265, 0, 527 }, +{ 0x600, 265, 0, 540 }, +{ 0x2600, 265, 0, 516 }, +{ 0xa600, 265, 0, 514 }, +{ 0x6600, 265, 0, 515 }, +{ 0x1600, 265, 0, 538 }, +{ 0xe00, 265, 0, 539 }, +{ 0x100, 265, 0, 552 }, +{ 0x500, 265, 0, 550 }, +{ 0x300, 265, 0, 551 }, +{ 0x80, 265, 0, 555 }, +{ 0x280, 265, 0, 553 }, +{ 0x180, 265, 0, 554 }, +{ 0x40, 265, 0, 567 }, +{ 0x140, 265, 0, 565 }, +{ 0xc0, 265, 0, 566 }, +{ 0x20, 265, 0, 579 }, +{ 0xa0, 265, 0, 577 }, +{ 0x60, 265, 0, 578 }, +{ 0x10, 265, 0, 591 }, +{ 0x50, 265, 0, 589 }, +{ 0x30, 265, 0, 590 }, +{ 0x8, 265, 0, 603 }, +{ 0x28, 265, 0, 601 }, +{ 0x18, 265, 0, 602 }, +{ 0x4, 265, 0, 613 }, +{ 0x2, 265, 0, 614 }, +{ 0x1, 265, 0, 615 }, +{ 0x500, 261, 0, 531 }, +{ 0x1500, 261, 0, 529 }, +{ 0xd00, 261, 0, 530 }, +{ 0x300, 261, 0, 543 }, +{ 0x1300, 261, 0, 519 }, +{ 0x5300, 261, 0, 517 }, +{ 0x3300, 261, 0, 518 }, +{ 0xb00, 261, 0, 541 }, +{ 0x700, 261, 0, 542 }, +{ 0x80, 261, 0, 558 }, +{ 0x280, 261, 0, 556 }, +{ 0x180, 261, 0, 557 }, +{ 0x40, 261, 0, 570 }, +{ 0x140, 261, 0, 568 }, +{ 0xc0, 261, 0, 569 }, +{ 0x20, 261, 0, 582 }, +{ 0xa0, 261, 0, 580 }, +{ 0x60, 261, 0, 581 }, +{ 0x10, 261, 0, 594 }, +{ 0x50, 261, 0, 592 }, +{ 0x30, 261, 0, 593 }, +{ 0x8, 261, 0, 606 }, +{ 0x28, 261, 0, 604 }, +{ 0x18, 261, 0, 605 }, +{ 0x4, 261, 0, 616 }, +{ 0x2, 261, 0, 617 }, +{ 0x1, 261, 0, 618 }, +{ 0x500, 258, 0, 534 }, +{ 0x1500, 258, 0, 532 }, +{ 0xd00, 258, 0, 533 }, +{ 0x300, 258, 0, 546 }, +{ 0x1300, 258, 0, 522 }, +{ 0x5300, 258, 0, 520 }, +{ 0x3300, 258, 0, 521 }, +{ 0xb00, 258, 0, 544 }, +{ 0x700, 258, 0, 545 }, +{ 0x80, 258, 0, 561 }, +{ 0x280, 258, 0, 559 }, +{ 0x180, 258, 0, 560 }, +{ 0x40, 258, 0, 573 }, +{ 0x140, 258, 0, 571 }, +{ 0xc0, 258, 0, 572 }, +{ 0x20, 258, 0, 585 }, +{ 0xa0, 258, 0, 583 }, +{ 0x60, 258, 0, 584 }, +{ 0x10, 258, 0, 597 }, +{ 0x50, 258, 0, 595 }, +{ 0x30, 258, 0, 596 }, +{ 0x8, 258, 0, 609 }, +{ 0x28, 258, 0, 607 }, +{ 0x18, 258, 0, 608 }, +{ 0x4, 258, 0, 619 }, +{ 0x2, 258, 0, 620 }, +{ 0x1, 258, 0, 621 }, +{ 0x500, 253, 0, 537 }, +{ 0x1500, 253, 0, 535 }, +{ 0xd00, 253, 0, 536 }, +{ 0x300, 253, 0, 549 }, +{ 0x1300, 253, 0, 525 }, +{ 0x5300, 253, 0, 523 }, +{ 0x3300, 253, 0, 524 }, +{ 0xb00, 253, 0, 547 }, +{ 0x700, 253, 0, 548 }, +{ 0x80, 253, 0, 564 }, +{ 0x280, 253, 0, 562 }, +{ 0x180, 253, 0, 563 }, +{ 0x40, 253, 0, 576 }, +{ 0x140, 253, 0, 574 }, +{ 0xc0, 253, 0, 575 }, +{ 0x20, 253, 0, 588 }, +{ 0xa0, 253, 0, 586 }, +{ 0x60, 253, 0, 587 }, +{ 0x10, 253, 0, 600 }, +{ 0x50, 253, 0, 598 }, +{ 0x30, 253, 0, 599 }, +{ 0x8, 253, 0, 612 }, +{ 0x28, 253, 0, 610 }, +{ 0x18, 253, 0, 611 }, +{ 0x4, 253, 0, 622 }, +{ 0x2, 253, 0, 623 }, +{ 0x1, 253, 0, 624 }, +{ 0x8, 238, 0, 625 }, +{ 0x4, 238, 0, 626 }, +{ 0x2, 238, 0, 627 }, +{ 0x1, 238, 0, 628 }, +{ 0x2, 176, 0, 631 }, +{ 0xa, 176, 0, 629 }, +{ 0x6, 176, 0, 630 }, +{ 0x1, 176, 0, 637 }, +{ 0x5, 176, 0, 635 }, +{ 0x3, 176, 0, 636 }, +{ 0x2, 175, 0, 634 }, +{ 0xa, 175, 0, 632 }, +{ 0x6, 175, 0, 633 }, +{ 0x1, 175, 0, 640 }, +{ 0x5, 175, 0, 638 }, +{ 0x3, 175, 0, 639 }, +{ 0x4, 451, 0, 641 }, +{ 0x2, 451, 0, 642 }, +{ 0x1, 451, 0, 643 }, +{ 0x4, 450, 0, 644 }, +{ 0x2, 450, 0, 645 }, +{ 0x1, 450, 0, 646 }, +{ 0x4, 449, 0, 647 }, +{ 0x2, 449, 0, 648 }, +{ 0x1, 449, 0, 649 }, +{ 0x4, 448, 0, 650 }, +{ 0x2, 448, 0, 651 }, +{ 0x1, 448, 0, 652 }, +{ 0x2, 123, 1, 658 }, +{ 0x2, 124, 0, 657 }, +{ 0xa, 123, 1, 654 }, +{ 0xa, 124, 0, 653 }, +{ 0x6, 123, 1, 656 }, +{ 0x6, 124, 0, 655 }, +{ 0x1, 123, 1, 688 }, +{ 0x1, 124, 0, 687 }, +{ 0x5, 123, 1, 684 }, +{ 0x5, 124, 0, 683 }, +{ 0x3, 123, 1, 686 }, +{ 0x3, 124, 0, 685 }, +{ 0x2, 131, 1, 664 }, +{ 0x2, 132, 0, 663 }, +{ 0xa, 131, 1, 660 }, +{ 0xa, 132, 0, 659 }, +{ 0x6, 131, 1, 662 }, +{ 0x6, 132, 0, 661 }, +{ 0x1, 131, 1, 694 }, +{ 0x1, 132, 0, 693 }, +{ 0x5, 131, 1, 690 }, +{ 0x5, 132, 0, 689 }, +{ 0x3, 131, 1, 692 }, +{ 0x3, 132, 0, 691 }, +{ 0x2, 129, 1, 670 }, +{ 0x2, 130, 0, 669 }, +{ 0xa, 129, 1, 666 }, +{ 0xa, 130, 0, 665 }, +{ 0x6, 129, 1, 668 }, +{ 0x6, 130, 0, 667 }, +{ 0x1, 129, 1, 700 }, +{ 0x1, 130, 0, 699 }, +{ 0x5, 129, 1, 696 }, +{ 0x5, 130, 0, 695 }, +{ 0x3, 129, 1, 698 }, +{ 0x3, 130, 0, 697 }, +{ 0x2, 127, 1, 676 }, +{ 0x2, 128, 0, 675 }, +{ 0xa, 127, 1, 672 }, +{ 0xa, 128, 0, 671 }, +{ 0x6, 127, 1, 674 }, +{ 0x6, 128, 0, 673 }, +{ 0x1, 127, 1, 706 }, +{ 0x1, 128, 0, 705 }, +{ 0x5, 127, 1, 702 }, +{ 0x5, 128, 0, 701 }, +{ 0x3, 127, 1, 704 }, +{ 0x3, 128, 0, 703 }, +{ 0x2, 125, 1, 682 }, +{ 0x2, 126, 0, 681 }, +{ 0xa, 125, 1, 678 }, +{ 0xa, 126, 0, 677 }, +{ 0x6, 125, 1, 680 }, +{ 0x6, 126, 0, 679 }, +{ 0x1, 125, 1, 712 }, +{ 0x1, 126, 0, 711 }, +{ 0x5, 125, 1, 708 }, +{ 0x5, 126, 0, 707 }, +{ 0x3, 125, 1, 710 }, +{ 0x3, 126, 0, 709 }, +{ 0x4, 402, 1, 718 }, +{ 0x4, 403, 0, 717 }, +{ 0xc, 402, 1, 716 }, +{ 0xc, 403, 0, 715 }, +{ 0x2, 402, 1, 728 }, +{ 0x2, 403, 0, 727 }, +{ 0x1, 402, 1, 730 }, +{ 0x1, 403, 0, 729 }, +{ 0x8, 408, 0, 714 }, +{ 0x18, 408, 0, 713 }, +{ 0x4, 408, 0, 720 }, +{ 0xc, 408, 0, 719 }, +{ 0x2, 408, 0, 731 }, +{ 0x1, 408, 0, 732 }, +{ 0x4, 406, 0, 722 }, +{ 0xc, 406, 0, 721 }, +{ 0x2, 406, 0, 733 }, +{ 0x1, 406, 0, 734 }, +{ 0x4, 404, 0, 724 }, +{ 0xc, 404, 0, 723 }, +{ 0x2, 404, 0, 735 }, +{ 0x1, 404, 0, 736 }, +{ 0x4, 400, 0, 726 }, +{ 0xc, 400, 0, 725 }, +{ 0x2, 400, 0, 737 }, +{ 0x1, 400, 0, 738 }, +{ 0xa00, 264, 0, 753 }, +{ 0x2a00, 264, 0, 751 }, +{ 0x1a00, 264, 0, 752 }, +{ 0x600, 264, 0, 765 }, +{ 0x2600, 264, 0, 741 }, +{ 0xa600, 264, 0, 739 }, +{ 0x6600, 264, 0, 740 }, +{ 0x1600, 264, 0, 763 }, +{ 0xe00, 264, 0, 764 }, +{ 0x100, 264, 0, 777 }, +{ 0x500, 264, 0, 775 }, +{ 0x300, 264, 0, 776 }, +{ 0x80, 264, 0, 780 }, +{ 0x280, 264, 0, 778 }, +{ 0x180, 264, 0, 779 }, +{ 0x40, 264, 0, 792 }, +{ 0x140, 264, 0, 790 }, +{ 0xc0, 264, 0, 791 }, +{ 0x20, 264, 0, 804 }, +{ 0xa0, 264, 0, 802 }, +{ 0x60, 264, 0, 803 }, +{ 0x10, 264, 0, 816 }, +{ 0x50, 264, 0, 814 }, +{ 0x30, 264, 0, 815 }, +{ 0x8, 264, 0, 828 }, +{ 0x28, 264, 0, 826 }, +{ 0x18, 264, 0, 827 }, +{ 0x4, 264, 0, 838 }, +{ 0x2, 264, 0, 839 }, +{ 0x1, 264, 0, 840 }, +{ 0x500, 260, 0, 756 }, +{ 0x1500, 260, 0, 754 }, +{ 0xd00, 260, 0, 755 }, +{ 0x300, 260, 0, 768 }, +{ 0x1300, 260, 0, 744 }, +{ 0x5300, 260, 0, 742 }, +{ 0x3300, 260, 0, 743 }, +{ 0xb00, 260, 0, 766 }, +{ 0x700, 260, 0, 767 }, +{ 0x80, 260, 0, 783 }, +{ 0x280, 260, 0, 781 }, +{ 0x180, 260, 0, 782 }, +{ 0x40, 260, 0, 795 }, +{ 0x140, 260, 0, 793 }, +{ 0xc0, 260, 0, 794 }, +{ 0x20, 260, 0, 807 }, +{ 0xa0, 260, 0, 805 }, +{ 0x60, 260, 0, 806 }, +{ 0x10, 260, 0, 819 }, +{ 0x50, 260, 0, 817 }, +{ 0x30, 260, 0, 818 }, +{ 0x8, 260, 0, 831 }, +{ 0x28, 260, 0, 829 }, +{ 0x18, 260, 0, 830 }, +{ 0x4, 260, 0, 841 }, +{ 0x2, 260, 0, 842 }, +{ 0x1, 260, 0, 843 }, +{ 0x500, 257, 0, 759 }, +{ 0x1500, 257, 0, 757 }, +{ 0xd00, 257, 0, 758 }, +{ 0x300, 257, 0, 771 }, +{ 0x1300, 257, 0, 747 }, +{ 0x5300, 257, 0, 745 }, +{ 0x3300, 257, 0, 746 }, +{ 0xb00, 257, 0, 769 }, +{ 0x700, 257, 0, 770 }, +{ 0x80, 257, 0, 786 }, +{ 0x280, 257, 0, 784 }, +{ 0x180, 257, 0, 785 }, +{ 0x40, 257, 0, 798 }, +{ 0x140, 257, 0, 796 }, +{ 0xc0, 257, 0, 797 }, +{ 0x20, 257, 0, 810 }, +{ 0xa0, 257, 0, 808 }, +{ 0x60, 257, 0, 809 }, +{ 0x10, 257, 0, 822 }, +{ 0x50, 257, 0, 820 }, +{ 0x30, 257, 0, 821 }, +{ 0x8, 257, 0, 834 }, +{ 0x28, 257, 0, 832 }, +{ 0x18, 257, 0, 833 }, +{ 0x4, 257, 0, 844 }, +{ 0x2, 257, 0, 845 }, +{ 0x1, 257, 0, 846 }, +{ 0x500, 252, 0, 762 }, +{ 0x1500, 252, 0, 760 }, +{ 0xd00, 252, 0, 761 }, +{ 0x300, 252, 0, 774 }, +{ 0x1300, 252, 0, 750 }, +{ 0x5300, 252, 0, 748 }, +{ 0x3300, 252, 0, 749 }, +{ 0xb00, 252, 0, 772 }, +{ 0x700, 252, 0, 773 }, +{ 0x80, 252, 0, 789 }, +{ 0x280, 252, 0, 787 }, +{ 0x180, 252, 0, 788 }, +{ 0x40, 252, 0, 801 }, +{ 0x140, 252, 0, 799 }, +{ 0xc0, 252, 0, 800 }, +{ 0x20, 252, 0, 813 }, +{ 0xa0, 252, 0, 811 }, +{ 0x60, 252, 0, 812 }, +{ 0x10, 252, 0, 825 }, +{ 0x50, 252, 0, 823 }, +{ 0x30, 252, 0, 824 }, +{ 0x8, 252, 0, 837 }, +{ 0x28, 252, 0, 835 }, +{ 0x18, 252, 0, 836 }, +{ 0x4, 252, 0, 847 }, +{ 0x2, 252, 0, 848 }, +{ 0x1, 252, 0, 849 }, +{ 0x8, 254, 1, 895 }, +{ 0x8, 255, 0, 894 }, +{ 0x28, 254, 1, 891 }, +{ 0x28, 255, 0, 890 }, +{ 0x18, 254, 1, 893 }, +{ 0x18, 255, 0, 892 }, +{ 0x4, 254, 1, 957 }, +{ 0x4, 255, 0, 956 }, +{ 0x2, 254, 1, 959 }, +{ 0x2, 255, 0, 958 }, +{ 0x1, 254, 1, 961 }, +{ 0x1, 255, 0, 960 }, +{ 0xa00, 262, 0, 865 }, +{ 0x2a00, 262, 0, 863 }, +{ 0x1a00, 262, 0, 864 }, +{ 0x600, 262, 0, 877 }, +{ 0x2600, 262, 0, 853 }, +{ 0xa600, 262, 0, 851 }, +{ 0x6600, 262, 0, 852 }, +{ 0x1600, 262, 0, 875 }, +{ 0xe00, 262, 0, 876 }, +{ 0x100, 262, 0, 889 }, +{ 0x500, 262, 0, 887 }, +{ 0x300, 262, 0, 888 }, +{ 0x80, 262, 0, 898 }, +{ 0x280, 262, 0, 896 }, +{ 0x180, 262, 0, 897 }, +{ 0x40, 262, 0, 910 }, +{ 0x140, 262, 0, 908 }, +{ 0xc0, 262, 0, 909 }, +{ 0x20, 262, 0, 922 }, +{ 0xa0, 262, 0, 920 }, +{ 0x60, 262, 0, 921 }, +{ 0x10, 262, 0, 934 }, +{ 0x50, 262, 0, 932 }, +{ 0x30, 262, 0, 933 }, +{ 0x8, 262, 0, 946 }, +{ 0x28, 262, 0, 944 }, +{ 0x18, 262, 0, 945 }, +{ 0x4, 262, 0, 962 }, +{ 0x2, 262, 0, 963 }, +{ 0x1, 262, 1, 964 }, +{ 0x1, 263, 0, 850 }, +{ 0x500, 259, 0, 868 }, +{ 0x1500, 259, 0, 866 }, +{ 0xd00, 259, 0, 867 }, +{ 0x300, 259, 0, 880 }, +{ 0x1300, 259, 0, 856 }, +{ 0x5300, 259, 0, 854 }, +{ 0x3300, 259, 0, 855 }, +{ 0xb00, 259, 0, 878 }, +{ 0x700, 259, 0, 879 }, +{ 0x80, 259, 0, 901 }, +{ 0x280, 259, 0, 899 }, +{ 0x180, 259, 0, 900 }, +{ 0x40, 259, 0, 913 }, +{ 0x140, 259, 0, 911 }, +{ 0xc0, 259, 0, 912 }, +{ 0x20, 259, 0, 925 }, +{ 0xa0, 259, 0, 923 }, +{ 0x60, 259, 0, 924 }, +{ 0x10, 259, 0, 937 }, +{ 0x50, 259, 0, 935 }, +{ 0x30, 259, 0, 936 }, +{ 0x8, 259, 0, 949 }, +{ 0x28, 259, 0, 947 }, +{ 0x18, 259, 0, 948 }, +{ 0x4, 259, 0, 965 }, +{ 0x2, 259, 0, 966 }, +{ 0x1, 259, 0, 967 }, +{ 0x500, 256, 0, 871 }, +{ 0x1500, 256, 0, 869 }, +{ 0xd00, 256, 0, 870 }, +{ 0x300, 256, 0, 883 }, +{ 0x1300, 256, 0, 859 }, +{ 0x5300, 256, 0, 857 }, +{ 0x3300, 256, 0, 858 }, +{ 0xb00, 256, 0, 881 }, +{ 0x700, 256, 0, 882 }, +{ 0x80, 256, 0, 904 }, +{ 0x280, 256, 0, 902 }, +{ 0x180, 256, 0, 903 }, +{ 0x40, 256, 0, 916 }, +{ 0x140, 256, 0, 914 }, +{ 0xc0, 256, 0, 915 }, +{ 0x20, 256, 0, 928 }, +{ 0xa0, 256, 0, 926 }, +{ 0x60, 256, 0, 927 }, +{ 0x10, 256, 0, 940 }, +{ 0x50, 256, 0, 938 }, +{ 0x30, 256, 0, 939 }, +{ 0x8, 256, 0, 952 }, +{ 0x28, 256, 0, 950 }, +{ 0x18, 256, 0, 951 }, +{ 0x4, 256, 0, 968 }, +{ 0x2, 256, 0, 969 }, +{ 0x1, 256, 0, 970 }, +{ 0x500, 251, 0, 874 }, +{ 0x1500, 251, 0, 872 }, +{ 0xd00, 251, 0, 873 }, +{ 0x300, 251, 0, 886 }, +{ 0x1300, 251, 0, 862 }, +{ 0x5300, 251, 0, 860 }, +{ 0x3300, 251, 0, 861 }, +{ 0xb00, 251, 0, 884 }, +{ 0x700, 251, 0, 885 }, +{ 0x80, 251, 0, 907 }, +{ 0x280, 251, 0, 905 }, +{ 0x180, 251, 0, 906 }, +{ 0x40, 251, 0, 919 }, +{ 0x140, 251, 0, 917 }, +{ 0xc0, 251, 0, 918 }, +{ 0x20, 251, 0, 931 }, +{ 0xa0, 251, 0, 929 }, +{ 0x60, 251, 0, 930 }, +{ 0x10, 251, 0, 943 }, +{ 0x50, 251, 0, 941 }, +{ 0x30, 251, 0, 942 }, +{ 0x8, 251, 0, 955 }, +{ 0x28, 251, 0, 953 }, +{ 0x18, 251, 0, 954 }, +{ 0x4, 251, 0, 971 }, +{ 0x2, 251, 0, 972 }, +{ 0x1, 251, 0, 973 }, +{ 0x2, 150, 0, 975 }, +{ 0x1, 150, 0, 976 }, +{ 0x1, 50, 0, 977 }, +{ 0x3, 49, 0, 978 }, +{ 0x1, 428, 0, 979 }, +{ 0x1, 442, 0, 980 }, +{ 0x2, 386, 0, 983 }, +{ 0x1, 386, 0, 984 }, +{ 0x2, 384, 0, 985 }, +{ 0x1, 384, 0, 986 }, +{ 0x1, 383, 0, 987 }, +{ 0x1, 328, 0, 992 }, +{ 0x1, 327, 0, 993 }, +{ 0x1, 326, 0, 994 }, +{ 0x1, 325, 0, 995 }, +{ 0x1, 250, 0, 996 }, +{ 0x1, 249, 0, 997 }, +{ 0x1, 324, 0, 998 }, +{ 0x1, 323, 0, 999 }, +{ 0x1, 322, 0, 1000 }, +{ 0x1, 321, 0, 1001 }, +{ 0x1, 320, 0, 1002 }, +{ 0x1, 319, 0, 1003 }, +{ 0x1, 318, 0, 1004 }, +{ 0x2, 248, 0, 1005 }, +{ 0x1, 248, 0, 1006 }, +{ 0x2, 366, 0, 1012 }, +{ 0x1, 366, 0, 1013 }, +{ 0x1, 317, 0, 1014 }, +{ 0x1, 316, 0, 1015 }, +{ 0x1, 315, 0, 1016 }, +{ 0x1, 314, 0, 1017 }, +{ 0x1, 8, 1, 1019 }, +{ 0x1, 9, 0, 1018 }, +{ 0x1, 313, 0, 1020 }, +{ 0x1, 312, 0, 1021 }, +{ 0x1, 311, 0, 1022 }, +{ 0x1, 310, 0, 1023 }, +{ 0x1, 388, 0, 1024 }, +{ 0x1, 399, 0, 1025 }, +{ 0x1, 389, 0, 1026 }, +{ 0x1, 423, 0, 1027 }, +{ 0x1, 309, 0, 1031 }, +{ 0x1, 247, 0, 1032 }, +{ 0x1, 177, 0, 1035 }, +{ 0x2, 291, 0, 1039 }, +{ 0x1, 291, 0, 1040 }, +{ 0x1, 236, 0, 1041 }, +{ 0x5, 48, 0, 1043 }, +{ 0x3, 48, 0, 1044 }, +{ 0x5, 47, 0, 1045 }, +{ 0x3, 47, 0, 1046 }, +{ 0x1, 365, 0, 1047 }, +{ 0x1, 373, 0, 1048 }, +{ 0x1, 371, 0, 1049 }, +{ 0x1, 392, 0, 1050 }, +{ 0x1, 372, 0, 1051 }, +{ 0x1, 370, 0, 1052 }, +{ 0x2, 378, 0, 1053 }, +{ 0x1, 378, 0, 1055 }, +{ 0x2, 376, 0, 1054 }, +{ 0x1, 376, 0, 1056 }, +{ 0x2, 396, 0, 1057 }, +{ 0x1, 396, 0, 1060 }, +{ 0x2, 377, 0, 1058 }, +{ 0x1, 377, 0, 1061 }, +{ 0x2, 375, 0, 1059 }, +{ 0x1, 375, 0, 1062 }, +{ 0x1, 338, 0, 1063 }, +{ 0x1, 337, 0, 1064 }, +{ 0x1, 369, 0, 1065 }, +{ 0x1, 360, 0, 1066 }, +{ 0x1, 362, 0, 1067 }, +{ 0x1, 359, 0, 1068 }, +{ 0x1, 361, 0, 1069 }, +{ 0x2, 446, 0, 1070 }, +{ 0x1, 446, 0, 1073 }, +{ 0x2, 445, 0, 1071 }, +{ 0x1, 445, 0, 1074 }, +{ 0x2, 444, 0, 1072 }, +{ 0x1, 444, 0, 1075 }, +{ 0x1, 348, 0, 1076 }, +{ 0x2, 347, 0, 1077 }, +{ 0x1, 347, 0, 1078 }, +{ 0x2, 294, 0, 1079 }, +{ 0x1, 294, 0, 1082 }, +{ 0x2, 293, 0, 1080 }, +{ 0x1, 293, 0, 1083 }, +{ 0x2, 292, 0, 1081 }, +{ 0x1, 292, 0, 1084 }, +{ 0x2, 363, 0, 1085 }, +{ 0x1, 363, 0, 1086 }, +{ 0x2, 364, 0, 1087 }, +{ 0x1, 364, 0, 1088 }, +{ 0xa, 438, 1, 1100 }, +{ 0xa, 439, 1, 1099 }, +{ 0xa, 440, 1, 1098 }, +{ 0xa, 441, 0, 1097 }, +{ 0x1a, 438, 1, 1092 }, +{ 0x1a, 439, 1, 1091 }, +{ 0x32, 440, 1, 1090 }, +{ 0x32, 441, 0, 1089 }, +{ 0x6, 438, 1, 1108 }, +{ 0x6, 439, 1, 1107 }, +{ 0x6, 440, 1, 1106 }, +{ 0x6, 441, 0, 1105 }, +{ 0x1, 438, 1, 1120 }, +{ 0x1, 439, 1, 1119 }, +{ 0x1, 440, 1, 1118 }, +{ 0x1, 441, 0, 1117 }, +{ 0x9, 438, 1, 1104 }, +{ 0x9, 439, 1, 1103 }, +{ 0x9, 440, 1, 1102 }, +{ 0x9, 441, 0, 1101 }, +{ 0x19, 438, 1, 1096 }, +{ 0x19, 439, 1, 1095 }, +{ 0x31, 440, 1, 1094 }, +{ 0x31, 441, 0, 1093 }, +{ 0x5, 438, 1, 1112 }, +{ 0x5, 439, 1, 1111 }, +{ 0x5, 440, 1, 1110 }, +{ 0x5, 441, 0, 1109 }, +{ 0x3, 438, 1, 1116 }, +{ 0x3, 439, 1, 1115 }, +{ 0x3, 440, 1, 1114 }, +{ 0x3, 441, 0, 1113 }, +{ 0xa, 429, 1, 1132 }, +{ 0xa, 430, 1, 1131 }, +{ 0xa, 431, 1, 1130 }, +{ 0xa, 432, 0, 1129 }, +{ 0x1a, 429, 1, 1124 }, +{ 0x1a, 430, 1, 1123 }, +{ 0x32, 431, 1, 1122 }, +{ 0x32, 432, 0, 1121 }, +{ 0x6, 429, 1, 1140 }, +{ 0x6, 430, 1, 1139 }, +{ 0x6, 431, 1, 1138 }, +{ 0x6, 432, 0, 1137 }, +{ 0x1, 429, 1, 1152 }, +{ 0x1, 430, 1, 1151 }, +{ 0x1, 431, 1, 1150 }, +{ 0x1, 432, 0, 1149 }, +{ 0x9, 429, 1, 1136 }, +{ 0x9, 430, 1, 1135 }, +{ 0x9, 431, 1, 1134 }, +{ 0x9, 432, 0, 1133 }, +{ 0x19, 429, 1, 1128 }, +{ 0x19, 430, 1, 1127 }, +{ 0x31, 431, 1, 1126 }, +{ 0x31, 432, 0, 1125 }, +{ 0x5, 429, 1, 1144 }, +{ 0x5, 430, 1, 1143 }, +{ 0x5, 431, 1, 1142 }, +{ 0x5, 432, 0, 1141 }, +{ 0x3, 429, 1, 1148 }, +{ 0x3, 430, 1, 1147 }, +{ 0x3, 431, 1, 1146 }, +{ 0x3, 432, 0, 1145 }, +{ 0xa, 433, 1, 1164 }, +{ 0xa, 434, 1, 1163 }, +{ 0xa, 435, 1, 1162 }, +{ 0xa, 436, 0, 1161 }, +{ 0x1a, 433, 1, 1156 }, +{ 0x1a, 434, 1, 1155 }, +{ 0x32, 435, 1, 1154 }, +{ 0x32, 436, 0, 1153 }, +{ 0x6, 433, 1, 1172 }, +{ 0x6, 434, 1, 1171 }, +{ 0x6, 435, 1, 1170 }, +{ 0x6, 436, 0, 1169 }, +{ 0x1, 433, 1, 1184 }, +{ 0x1, 434, 1, 1183 }, +{ 0x1, 435, 1, 1182 }, +{ 0x1, 436, 0, 1181 }, +{ 0x9, 433, 1, 1168 }, +{ 0x9, 434, 1, 1167 }, +{ 0x9, 435, 1, 1166 }, +{ 0x9, 436, 0, 1165 }, +{ 0x19, 433, 1, 1160 }, +{ 0x19, 434, 1, 1159 }, +{ 0x31, 435, 1, 1158 }, +{ 0x31, 436, 0, 1157 }, +{ 0x5, 433, 1, 1176 }, +{ 0x5, 434, 1, 1175 }, +{ 0x5, 435, 1, 1174 }, +{ 0x5, 436, 0, 1173 }, +{ 0x3, 433, 1, 1180 }, +{ 0x3, 434, 1, 1179 }, +{ 0x3, 435, 1, 1178 }, +{ 0x3, 436, 0, 1177 }, +{ 0x1, 139, 0, 1185 }, +{ 0x1, 138, 0, 1186 }, +{ 0x1, 391, 1, 1188 }, +{ 0x1, 137, 0, 1187 }, +{ 0x2, 395, 1, 1190 }, +{ 0x2, 141, 0, 1189 }, +{ 0x1, 395, 1, 1192 }, +{ 0x1, 141, 0, 1191 }, +{ 0x1, 397, 0, 1193 }, +{ 0x1, 136, 0, 1194 }, +{ 0x2, 135, 0, 1195 }, +{ 0x2, 134, 0, 1196 }, +{ 0x1, 459, 1, 1202 }, +{ 0x1, 246, 0, 1033 }, +{ 0x1, 458, 0, 1203 }, +{ 0x1, 457, 1, 1204 }, +{ 0x1, 245, 0, 1042 }, +{ 0x1, 308, 0, 1205 }, +{ 0x1, 307, 1, 1206 }, +{ 0x1, 290, 0, 1034 }, +{ 0x1, 306, 0, 1207 }, +{ 0x1, 305, 1, 1208 }, +{ 0x1, 427, 0, 1036 }, +{ 0x1, 304, 1, 1209 }, +{ 0x1, 398, 0, 1038 }, +{ 0x1, 303, 0, 1210 }, +{ 0x1, 302, 0, 1211 }, +{ 0x1, 301, 0, 1212 }, +{ 0x1, 300, 1, 1213 }, +{ 0x2, 398, 0, 1037 }, +{ 0x10, 299, 0, 1217 }, +{ 0x90, 299, 0, 1215 }, +{ 0x190, 299, 0, 1214 }, +{ 0x50, 299, 0, 1216 }, +{ 0x30, 299, 0, 1219 }, +{ 0x70, 299, 0, 1218 }, +{ 0x8, 299, 0, 1221 }, +{ 0x18, 299, 0, 1220 }, +{ 0x4, 299, 0, 1222 }, +{ 0x1, 299, 0, 1225 }, +{ 0x3, 299, 0, 1224 }, +{ 0x1, 298, 1, 1226 }, +{ 0x2, 299, 0, 1223 }, +{ 0x3, 46, 0, 1227 }, +{ 0x1, 241, 1, 1228 }, +{ 0x1, 242, 1, 1028 }, +{ 0x1, 243, 0, 88 }, +{ 0x1, 341, 1, 1229 }, +{ 0x1, 342, 1, 1029 }, +{ 0x1, 343, 0, 89 }, +{ 0x1, 34, 1, 1230 }, +{ 0x1, 35, 1, 1030 }, +{ 0x1, 36, 0, 90 }, +{ 0x1, 230, 0, 1231 }, +{ 0x4, 452, 0, 1232 }, +{ 0x2, 452, 0, 1233 }, +{ 0x1, 452, 1, 1235 }, +{ 0x1, 453, 0, 1234 }, +{ 0x8, 454, 0, 1236 }, +{ 0x4, 454, 0, 1237 }, +{ 0x1, 454, 1, 1239 }, +{ 0x2, 454, 0, 1238 }, +{ 0x8, 219, 0, 1240 }, +{ 0x4, 219, 0, 1241 }, +{ 0x2, 219, 0, 1242 }, +{ 0x1, 219, 1, 1244 }, +{ 0x1, 220, 0, 1243 }, +{ 0x10, 221, 0, 1245 }, +{ 0x8, 221, 0, 1246 }, +{ 0x4, 221, 0, 1247 }, +{ 0x1, 221, 1, 1249 }, +{ 0x2, 221, 0, 1248 }, +{ 0x220, 191, 0, 1250 }, +{ 0x120, 191, 0, 1251 }, +{ 0xa0, 191, 0, 1252 }, +{ 0x60, 191, 1, 1254 }, +{ 0x4, 192, 0, 1253 }, +{ 0x110, 191, 0, 1260 }, +{ 0x90, 191, 0, 1261 }, +{ 0x50, 191, 0, 1262 }, +{ 0x30, 191, 1, 1264 }, +{ 0x2, 192, 0, 1263 }, +{ 0x8, 191, 0, 1265 }, +{ 0x4, 191, 0, 1266 }, +{ 0x2, 191, 0, 1267 }, +{ 0x1, 191, 1, 1269 }, +{ 0x1, 192, 0, 1268 }, +{ 0x440, 193, 0, 1255 }, +{ 0x240, 193, 0, 1256 }, +{ 0x140, 193, 0, 1257 }, +{ 0xc0, 193, 1, 1259 }, +{ 0x40, 193, 0, 1258 }, +{ 0x220, 193, 0, 1270 }, +{ 0x120, 193, 0, 1271 }, +{ 0xa0, 193, 0, 1272 }, +{ 0x60, 193, 1, 1274 }, +{ 0x20, 193, 0, 1273 }, +{ 0x10, 193, 0, 1275 }, +{ 0x8, 193, 0, 1276 }, +{ 0x4, 193, 0, 1277 }, +{ 0x1, 193, 1, 1279 }, +{ 0x2, 193, 0, 1278 }, +{ 0x8, 215, 0, 1280 }, +{ 0x4, 215, 0, 1281 }, +{ 0x2, 215, 0, 1282 }, +{ 0x1, 215, 1, 1284 }, +{ 0x1, 216, 0, 1283 }, +{ 0x220, 187, 0, 1285 }, +{ 0x120, 187, 0, 1286 }, +{ 0xa0, 187, 0, 1287 }, +{ 0x60, 187, 1, 1289 }, +{ 0x4, 188, 0, 1288 }, +{ 0x110, 187, 0, 1295 }, +{ 0x90, 187, 0, 1296 }, +{ 0x50, 187, 0, 1297 }, +{ 0x30, 187, 1, 1299 }, +{ 0x2, 188, 0, 1298 }, +{ 0x8, 187, 0, 1300 }, +{ 0x4, 187, 0, 1301 }, +{ 0x2, 187, 0, 1302 }, +{ 0x1, 187, 1, 1304 }, +{ 0x1, 188, 0, 1303 }, +{ 0x440, 233, 0, 1290 }, +{ 0x240, 233, 0, 1291 }, +{ 0x140, 233, 0, 1292 }, +{ 0xc0, 233, 1, 1294 }, +{ 0x40, 233, 0, 1293 }, +{ 0x220, 233, 0, 1305 }, +{ 0x120, 233, 0, 1306 }, +{ 0xa0, 233, 0, 1307 }, +{ 0x60, 233, 1, 1309 }, +{ 0x20, 233, 0, 1308 }, +{ 0x10, 233, 0, 1310 }, +{ 0x8, 233, 0, 1311 }, +{ 0x4, 233, 0, 1312 }, +{ 0x1, 233, 1, 1314 }, +{ 0x2, 233, 0, 1313 }, +{ 0x8, 207, 0, 1315 }, +{ 0x4, 207, 0, 1316 }, +{ 0x2, 207, 0, 1317 }, +{ 0x1, 207, 1, 1319 }, +{ 0x1, 208, 0, 1318 }, +{ 0x10, 214, 0, 1320 }, +{ 0x8, 214, 0, 1321 }, +{ 0x4, 214, 0, 1322 }, +{ 0x1, 214, 1, 1324 }, +{ 0x2, 214, 0, 1323 }, +{ 0x220, 178, 0, 1325 }, +{ 0x120, 178, 0, 1326 }, +{ 0xa0, 178, 0, 1327 }, +{ 0x60, 178, 1, 1329 }, +{ 0x4, 179, 0, 1328 }, +{ 0x110, 178, 0, 1350 }, +{ 0x90, 178, 0, 1351 }, +{ 0x50, 178, 0, 1352 }, +{ 0x30, 178, 1, 1354 }, +{ 0x2, 179, 0, 1353 }, +{ 0x8, 178, 0, 1355 }, +{ 0x4, 178, 0, 1356 }, +{ 0x2, 178, 0, 1357 }, +{ 0x1, 178, 1, 1359 }, +{ 0x1, 179, 0, 1358 }, +{ 0x440, 186, 0, 1330 }, +{ 0x240, 186, 0, 1331 }, +{ 0x140, 186, 0, 1332 }, +{ 0xc0, 186, 1, 1334 }, +{ 0x40, 186, 0, 1333 }, +{ 0x220, 186, 0, 1360 }, +{ 0x120, 186, 0, 1361 }, +{ 0xa0, 186, 0, 1362 }, +{ 0x60, 186, 1, 1364 }, +{ 0x20, 186, 0, 1363 }, +{ 0x10, 186, 0, 1365 }, +{ 0x8, 186, 0, 1366 }, +{ 0x4, 186, 0, 1367 }, +{ 0x1, 186, 1, 1369 }, +{ 0x2, 186, 0, 1368 }, +{ 0x440, 143, 0, 1335 }, +{ 0x240, 143, 0, 1336 }, +{ 0x140, 143, 0, 1337 }, +{ 0xc0, 143, 1, 1339 }, +{ 0x40, 143, 0, 1338 }, +{ 0x220, 143, 0, 1370 }, +{ 0x120, 143, 0, 1371 }, +{ 0xa0, 143, 0, 1372 }, +{ 0x60, 143, 1, 1374 }, +{ 0x20, 143, 0, 1373 }, +{ 0x10, 143, 0, 1375 }, +{ 0x8, 143, 0, 1376 }, +{ 0x1, 143, 1, 1379 }, +{ 0x2, 143, 0, 1378 }, +{ 0x440, 194, 1, 1345 }, +{ 0x441, 174, 0, 1340 }, +{ 0x240, 194, 1, 1346 }, +{ 0x241, 174, 0, 1341 }, +{ 0x140, 194, 1, 1347 }, +{ 0x141, 174, 0, 1342 }, +{ 0xc0, 194, 1, 1349 }, +{ 0x40, 194, 1, 1348 }, +{ 0xc1, 174, 1, 1344 }, +{ 0x41, 174, 0, 1343 }, +{ 0x220, 194, 1, 1390 }, +{ 0x221, 174, 0, 1380 }, +{ 0x120, 194, 1, 1391 }, +{ 0x121, 174, 0, 1381 }, +{ 0xa0, 194, 1, 1392 }, +{ 0xa1, 174, 0, 1382 }, +{ 0x60, 194, 1, 1394 }, +{ 0x20, 194, 1, 1393 }, +{ 0x61, 174, 1, 1384 }, +{ 0x21, 174, 0, 1383 }, +{ 0x10, 194, 1, 1395 }, +{ 0x11, 174, 0, 1385 }, +{ 0x8, 194, 1, 1396 }, +{ 0x9, 174, 0, 1386 }, +{ 0x4, 194, 1, 1397 }, +{ 0x5, 174, 0, 1387 }, +{ 0x1, 194, 1, 1399 }, +{ 0x2, 194, 1, 1398 }, +{ 0x3, 174, 1, 1389 }, +{ 0x1, 174, 0, 1388 }, +{ 0x1, 153, 1, 1407 }, +{ 0x1, 154, 1, 1406 }, +{ 0x1, 155, 1, 1405 }, +{ 0x1, 156, 0, 1404 }, +{ 0x3, 153, 1, 1403 }, +{ 0x3, 154, 1, 1402 }, +{ 0x3, 155, 1, 1401 }, +{ 0x3, 156, 0, 1400 }, +{ 0x1108, 159, 1, 1569 }, +{ 0x1108, 160, 1, 1568 }, +{ 0x1108, 165, 1, 1409 }, +{ 0x1108, 166, 0, 1408 }, +{ 0x908, 159, 1, 1571 }, +{ 0x908, 160, 1, 1570 }, +{ 0x908, 165, 1, 1411 }, +{ 0x908, 166, 0, 1410 }, +{ 0x508, 159, 1, 1573 }, +{ 0x508, 160, 1, 1572 }, +{ 0x508, 165, 1, 1413 }, +{ 0x508, 166, 0, 1412 }, +{ 0x308, 159, 1, 1577 }, +{ 0x308, 160, 1, 1576 }, +{ 0x108, 160, 1, 1574 }, +{ 0x18, 161, 1, 1575 }, +{ 0x308, 165, 1, 1417 }, +{ 0x308, 166, 1, 1416 }, +{ 0x108, 166, 1, 1414 }, +{ 0x18, 167, 0, 1415 }, +{ 0x88, 159, 1, 1609 }, +{ 0x88, 160, 1, 1608 }, +{ 0x88, 165, 1, 1489 }, +{ 0x88, 166, 0, 1488 }, +{ 0x48, 159, 1, 1611 }, +{ 0x48, 160, 1, 1610 }, +{ 0x48, 165, 1, 1491 }, +{ 0x48, 166, 0, 1490 }, +{ 0x28, 159, 1, 1613 }, +{ 0x28, 160, 1, 1612 }, +{ 0x28, 165, 1, 1493 }, +{ 0x28, 166, 0, 1492 }, +{ 0x18, 159, 1, 1617 }, +{ 0x18, 160, 1, 1616 }, +{ 0x8, 160, 1, 1614 }, +{ 0x8, 161, 1, 1615 }, +{ 0x18, 165, 1, 1497 }, +{ 0x18, 166, 1, 1496 }, +{ 0x8, 166, 1, 1494 }, +{ 0x8, 167, 0, 1495 }, +{ 0x884, 159, 1, 1579 }, +{ 0x884, 160, 1, 1578 }, +{ 0x442, 162, 1, 1469 }, +{ 0x442, 163, 1, 1468 }, +{ 0x884, 165, 1, 1439 }, +{ 0x884, 166, 1, 1438 }, +{ 0x442, 168, 1, 1419 }, +{ 0x442, 169, 0, 1418 }, +{ 0x484, 159, 1, 1581 }, +{ 0x484, 160, 1, 1580 }, +{ 0x242, 162, 1, 1471 }, +{ 0x242, 163, 1, 1470 }, +{ 0x484, 165, 1, 1441 }, +{ 0x484, 166, 1, 1440 }, +{ 0x242, 168, 1, 1421 }, +{ 0x242, 169, 0, 1420 }, +{ 0x284, 159, 1, 1583 }, +{ 0x284, 160, 1, 1582 }, +{ 0x142, 162, 1, 1473 }, +{ 0x142, 163, 1, 1472 }, +{ 0x284, 165, 1, 1443 }, +{ 0x284, 166, 1, 1442 }, +{ 0x142, 168, 1, 1423 }, +{ 0x142, 169, 0, 1422 }, +{ 0x184, 159, 1, 1587 }, +{ 0x184, 160, 1, 1586 }, +{ 0x84, 160, 1, 1584 }, +{ 0xc, 161, 1, 1585 }, +{ 0xc2, 162, 1, 1477 }, +{ 0xc2, 163, 1, 1476 }, +{ 0x42, 163, 1, 1474 }, +{ 0x6, 164, 1, 1475 }, +{ 0x184, 165, 1, 1447 }, +{ 0x184, 166, 1, 1446 }, +{ 0x84, 166, 1, 1444 }, +{ 0xc, 167, 1, 1445 }, +{ 0xc2, 168, 1, 1427 }, +{ 0xc2, 169, 1, 1426 }, +{ 0x42, 169, 1, 1424 }, +{ 0x6, 170, 0, 1425 }, +{ 0x44, 159, 1, 1619 }, +{ 0x44, 160, 1, 1618 }, +{ 0x22, 162, 1, 1549 }, +{ 0x22, 163, 1, 1548 }, +{ 0x44, 165, 1, 1519 }, +{ 0x44, 166, 1, 1518 }, +{ 0x22, 168, 1, 1499 }, +{ 0x22, 169, 0, 1498 }, +{ 0x24, 159, 1, 1621 }, +{ 0x24, 160, 1, 1620 }, +{ 0x12, 162, 1, 1551 }, +{ 0x12, 163, 1, 1550 }, +{ 0x24, 165, 1, 1521 }, +{ 0x24, 166, 1, 1520 }, +{ 0x12, 168, 1, 1501 }, +{ 0x12, 169, 0, 1500 }, +{ 0x14, 159, 1, 1623 }, +{ 0x14, 160, 1, 1622 }, +{ 0xa, 162, 1, 1553 }, +{ 0xa, 163, 1, 1552 }, +{ 0x14, 165, 1, 1523 }, +{ 0x14, 166, 1, 1522 }, +{ 0xa, 168, 1, 1503 }, +{ 0xa, 169, 0, 1502 }, +{ 0xc, 159, 1, 1627 }, +{ 0xc, 160, 1, 1626 }, +{ 0x4, 160, 1, 1624 }, +{ 0x4, 161, 1, 1625 }, +{ 0x6, 162, 1, 1557 }, +{ 0x6, 163, 1, 1556 }, +{ 0x2, 163, 1, 1554 }, +{ 0x2, 164, 1, 1555 }, +{ 0xc, 165, 1, 1527 }, +{ 0xc, 166, 1, 1526 }, +{ 0x4, 166, 1, 1524 }, +{ 0x4, 167, 1, 1525 }, +{ 0x6, 168, 1, 1507 }, +{ 0x6, 169, 1, 1506 }, +{ 0x2, 169, 1, 1504 }, +{ 0x2, 170, 0, 1505 }, +{ 0x442, 159, 1, 1589 }, +{ 0x442, 160, 1, 1588 }, +{ 0x221, 162, 1, 1479 }, +{ 0x221, 163, 1, 1478 }, +{ 0x442, 165, 1, 1449 }, +{ 0x442, 166, 1, 1448 }, +{ 0x221, 168, 1, 1429 }, +{ 0x221, 169, 0, 1428 }, +{ 0x242, 159, 1, 1591 }, +{ 0x242, 160, 1, 1590 }, +{ 0x121, 162, 1, 1481 }, +{ 0x121, 163, 1, 1480 }, +{ 0x242, 165, 1, 1451 }, +{ 0x242, 166, 1, 1450 }, +{ 0x121, 168, 1, 1431 }, +{ 0x121, 169, 0, 1430 }, +{ 0x142, 159, 1, 1593 }, +{ 0x142, 160, 1, 1592 }, +{ 0xa1, 162, 1, 1483 }, +{ 0xa1, 163, 1, 1482 }, +{ 0x142, 165, 1, 1453 }, +{ 0x142, 166, 1, 1452 }, +{ 0xa1, 168, 1, 1433 }, +{ 0xa1, 169, 0, 1432 }, +{ 0xc2, 159, 1, 1597 }, +{ 0xc2, 160, 1, 1596 }, +{ 0x42, 160, 1, 1594 }, +{ 0x6, 161, 1, 1595 }, +{ 0x61, 162, 1, 1487 }, +{ 0x61, 163, 1, 1486 }, +{ 0x21, 163, 1, 1484 }, +{ 0x3, 164, 1, 1485 }, +{ 0xc2, 165, 1, 1457 }, +{ 0xc2, 166, 1, 1456 }, +{ 0x42, 166, 1, 1454 }, +{ 0x6, 167, 1, 1455 }, +{ 0x61, 168, 1, 1437 }, +{ 0x61, 169, 1, 1436 }, +{ 0x21, 169, 1, 1434 }, +{ 0x3, 170, 0, 1435 }, +{ 0x22, 159, 1, 1629 }, +{ 0x22, 160, 1, 1628 }, +{ 0x11, 162, 1, 1559 }, +{ 0x11, 163, 1, 1558 }, +{ 0x22, 165, 1, 1529 }, +{ 0x22, 166, 1, 1528 }, +{ 0x11, 168, 1, 1509 }, +{ 0x11, 169, 0, 1508 }, +{ 0x12, 159, 1, 1631 }, +{ 0x12, 160, 1, 1630 }, +{ 0x9, 162, 1, 1561 }, +{ 0x9, 163, 1, 1560 }, +{ 0x12, 165, 1, 1531 }, +{ 0x12, 166, 1, 1530 }, +{ 0x9, 168, 1, 1511 }, +{ 0x9, 169, 0, 1510 }, +{ 0xa, 159, 1, 1633 }, +{ 0xa, 160, 1, 1632 }, +{ 0x5, 162, 1, 1563 }, +{ 0x5, 163, 1, 1562 }, +{ 0xa, 165, 1, 1533 }, +{ 0xa, 166, 1, 1532 }, +{ 0x5, 168, 1, 1513 }, +{ 0x5, 169, 0, 1512 }, +{ 0x6, 159, 1, 1637 }, +{ 0x6, 160, 1, 1636 }, +{ 0x2, 160, 1, 1634 }, +{ 0x2, 161, 1, 1635 }, +{ 0x3, 162, 1, 1567 }, +{ 0x3, 163, 1, 1566 }, +{ 0x1, 163, 1, 1564 }, +{ 0x1, 164, 1, 1565 }, +{ 0x6, 165, 1, 1537 }, +{ 0x6, 166, 1, 1536 }, +{ 0x2, 166, 1, 1534 }, +{ 0x2, 167, 1, 1535 }, +{ 0x3, 168, 1, 1517 }, +{ 0x3, 169, 1, 1516 }, +{ 0x1, 169, 1, 1514 }, +{ 0x1, 170, 0, 1515 }, +{ 0x221, 159, 1, 1599 }, +{ 0x221, 160, 1, 1598 }, +{ 0x221, 165, 1, 1459 }, +{ 0x221, 166, 0, 1458 }, +{ 0x121, 159, 1, 1601 }, +{ 0x121, 160, 1, 1600 }, +{ 0x121, 165, 1, 1461 }, +{ 0x121, 166, 0, 1460 }, +{ 0xa1, 159, 1, 1603 }, +{ 0xa1, 160, 1, 1602 }, +{ 0xa1, 165, 1, 1463 }, +{ 0xa1, 166, 0, 1462 }, +{ 0x61, 159, 1, 1607 }, +{ 0x61, 160, 1, 1606 }, +{ 0x21, 160, 1, 1604 }, +{ 0x3, 161, 1, 1605 }, +{ 0x61, 165, 1, 1467 }, +{ 0x61, 166, 1, 1466 }, +{ 0x21, 166, 1, 1464 }, +{ 0x3, 167, 0, 1465 }, +{ 0x11, 159, 1, 1639 }, +{ 0x11, 160, 1, 1638 }, +{ 0x11, 165, 1, 1539 }, +{ 0x11, 166, 0, 1538 }, +{ 0x9, 159, 1, 1641 }, +{ 0x9, 160, 1, 1640 }, +{ 0x9, 165, 1, 1541 }, +{ 0x9, 166, 0, 1540 }, +{ 0x5, 159, 1, 1643 }, +{ 0x5, 160, 1, 1642 }, +{ 0x5, 165, 1, 1543 }, +{ 0x5, 166, 0, 1542 }, +{ 0x3, 159, 1, 1647 }, +{ 0x3, 160, 1, 1646 }, +{ 0x1, 160, 1, 1644 }, +{ 0x1, 161, 1, 1645 }, +{ 0x3, 165, 1, 1547 }, +{ 0x3, 166, 1, 1546 }, +{ 0x1, 166, 1, 1544 }, +{ 0x1, 167, 0, 1545 }, +{ 0x442, 205, 0, 1648 }, +{ 0x242, 205, 0, 1649 }, +{ 0x142, 205, 0, 1650 }, +{ 0xc2, 205, 1, 1652 }, +{ 0x6, 206, 1, 1651 }, +{ 0x1, 443, 0, 981 }, +{ 0x22, 205, 0, 1658 }, +{ 0x12, 205, 0, 1659 }, +{ 0xa, 205, 0, 1660 }, +{ 0x6, 205, 1, 1662 }, +{ 0x2, 206, 1, 1661 }, +{ 0x2, 367, 0, 1010 }, +{ 0x221, 205, 0, 1653 }, +{ 0x121, 205, 0, 1654 }, +{ 0xa1, 205, 0, 1655 }, +{ 0x61, 205, 1, 1657 }, +{ 0x3, 206, 1, 1656 }, +{ 0x1, 437, 0, 982 }, +{ 0x11, 205, 0, 1663 }, +{ 0x9, 205, 0, 1664 }, +{ 0x5, 205, 0, 1665 }, +{ 0x3, 205, 1, 1667 }, +{ 0x1, 206, 1, 1666 }, +{ 0x1, 367, 0, 1011 }, +{ 0x4, 211, 0, 1668 }, +{ 0x1, 211, 0, 1670 }, +{ 0x1, 218, 0, 1671 }, +{ 0x1, 217, 1, 1672 }, +{ 0x2, 211, 0, 1669 }, +{ 0x1, 196, 0, 1673 }, +{ 0x880, 202, 0, 1674 }, +{ 0x480, 202, 0, 1675 }, +{ 0x280, 202, 0, 1676 }, +{ 0x180, 202, 1, 1678 }, +{ 0x80, 203, 0, 1677 }, +{ 0x440, 202, 1, 1689 }, +{ 0x88, 204, 0, 1679 }, +{ 0x240, 202, 1, 1690 }, +{ 0x48, 204, 0, 1680 }, +{ 0x140, 202, 1, 1691 }, +{ 0x28, 204, 0, 1681 }, +{ 0xc0, 202, 1, 1693 }, +{ 0x40, 203, 1, 1692 }, +{ 0x18, 204, 1, 1683 }, +{ 0x8, 204, 0, 1682 }, +{ 0x220, 202, 1, 1694 }, +{ 0x44, 204, 0, 1684 }, +{ 0x120, 202, 1, 1695 }, +{ 0x24, 204, 0, 1685 }, +{ 0xa0, 202, 1, 1696 }, +{ 0x14, 204, 0, 1686 }, +{ 0x60, 202, 1, 1698 }, +{ 0x20, 203, 1, 1697 }, +{ 0xc, 204, 1, 1688 }, +{ 0x4, 204, 0, 1687 }, +{ 0x110, 202, 0, 1699 }, +{ 0x90, 202, 0, 1700 }, +{ 0x50, 202, 0, 1701 }, +{ 0x30, 202, 1, 1703 }, +{ 0x10, 203, 1, 1702 }, +{ 0x1, 385, 0, 974 }, +{ 0x88, 202, 0, 1704 }, +{ 0x48, 202, 0, 1705 }, +{ 0x28, 202, 0, 1706 }, +{ 0x18, 202, 1, 1708 }, +{ 0x8, 203, 1, 1707 }, +{ 0xc, 368, 0, 1007 }, +{ 0x44, 202, 1, 1719 }, +{ 0x22, 204, 0, 1709 }, +{ 0x24, 202, 1, 1720 }, +{ 0x12, 204, 0, 1710 }, +{ 0x14, 202, 1, 1721 }, +{ 0xa, 204, 0, 1711 }, +{ 0xc, 202, 1, 1723 }, +{ 0x4, 203, 1, 1722 }, +{ 0x6, 204, 1, 1713 }, +{ 0x2, 204, 1, 1712 }, +{ 0x6, 368, 0, 1008 }, +{ 0x22, 202, 1, 1724 }, +{ 0x11, 204, 0, 1714 }, +{ 0x12, 202, 1, 1725 }, +{ 0x9, 204, 0, 1715 }, +{ 0xa, 202, 1, 1726 }, +{ 0x5, 204, 0, 1716 }, +{ 0x6, 202, 1, 1728 }, +{ 0x2, 203, 1, 1727 }, +{ 0x3, 204, 1, 1718 }, +{ 0x1, 204, 1, 1717 }, +{ 0x3, 368, 0, 1009 }, +{ 0x11, 202, 0, 1729 }, +{ 0x9, 202, 0, 1730 }, +{ 0x5, 202, 0, 1731 }, +{ 0x3, 202, 1, 1733 }, +{ 0x1, 203, 0, 1732 }, +{ 0x8, 198, 0, 1734 }, +{ 0x4, 198, 0, 1735 }, +{ 0x2, 198, 0, 1736 }, +{ 0x1, 198, 1, 1738 }, +{ 0x1, 199, 1, 1737 }, +{ 0x1, 332, 0, 988 }, +{ 0x8, 200, 0, 1739 }, +{ 0x4, 200, 0, 1740 }, +{ 0x2, 200, 0, 1741 }, +{ 0x1, 200, 1, 1743 }, +{ 0x1, 201, 1, 1742 }, +{ 0x1, 331, 0, 989 }, +{ 0x8, 209, 0, 1744 }, +{ 0x4, 209, 0, 1745 }, +{ 0x2, 209, 0, 1746 }, +{ 0x1, 209, 1, 1748 }, +{ 0x1, 210, 1, 1747 }, +{ 0x1, 330, 0, 990 }, +{ 0x8, 212, 0, 1749 }, +{ 0x4, 212, 0, 1750 }, +{ 0x2, 212, 0, 1751 }, +{ 0x1, 212, 1, 1753 }, +{ 0x1, 213, 1, 1752 }, +{ 0x1, 329, 0, 991 }, +{ 0x8, 224, 0, 1754 }, +{ 0x4, 224, 0, 1755 }, +{ 0x2, 224, 0, 1756 }, +{ 0x1, 224, 1, 1758 }, +{ 0x1, 225, 0, 1757 }, +{ 0x8, 222, 0, 1759 }, +{ 0x4, 222, 0, 1760 }, +{ 0x2, 222, 0, 1761 }, +{ 0x1, 222, 1, 1763 }, +{ 0x1, 223, 0, 1762 }, +{ 0x1, 240, 0, 1764 }, +{ 0x1, 340, 0, 1765 }, +{ 0x1, 33, 0, 1766 }, +{ 0x8, 151, 0, 1767 }, +{ 0x4, 151, 0, 1768 }, +{ 0x2, 151, 0, 1769 }, +{ 0x1, 151, 1, 1771 }, +{ 0x1, 152, 0, 1770 }, +{ 0x8, 157, 0, 1772 }, +{ 0x4, 157, 0, 1773 }, +{ 0x2, 157, 0, 1774 }, +{ 0x1, 157, 1, 1776 }, +{ 0x1, 158, 0, 1775 }, +{ 0x8, 231, 0, 1777 }, +{ 0x4, 231, 0, 1778 }, +{ 0x2, 231, 0, 1779 }, +{ 0x1, 231, 1, 1781 }, +{ 0x1, 232, 0, 1780 }, +{ 0x1, 173, 0, 1782 }, +{ 0x442, 171, 0, 1783 }, +{ 0x242, 171, 0, 1784 }, +{ 0x142, 171, 0, 1785 }, +{ 0xc2, 171, 1, 1787 }, +{ 0x6, 172, 0, 1786 }, +{ 0x22, 171, 0, 1793 }, +{ 0x12, 171, 0, 1794 }, +{ 0xa, 171, 0, 1795 }, +{ 0x6, 171, 1, 1797 }, +{ 0x2, 172, 1, 1796 }, +{ 0x1, 135, 0, 1197 }, +{ 0x221, 171, 0, 1788 }, +{ 0x121, 171, 0, 1789 }, +{ 0xa1, 171, 0, 1790 }, +{ 0x61, 171, 1, 1792 }, +{ 0x3, 172, 0, 1791 }, +{ 0x11, 171, 0, 1798 }, +{ 0x9, 171, 0, 1799 }, +{ 0x5, 171, 0, 1800 }, +{ 0x3, 171, 1, 1802 }, +{ 0x1, 172, 1, 1801 }, +{ 0x1, 134, 0, 1198 }, +{ 0x1, 237, 0, 1803 }, +{ 0x1, 195, 0, 1804 }, +{ 0x1, 149, 0, 1805 }, +{ 0x1, 148, 0, 1806 }, +{ 0x4, 234, 0, 1807 }, +{ 0x2, 234, 0, 1808 }, +{ 0x1, 234, 0, 1809 }, +{ 0x1, 197, 0, 1810 }, +{ 0x2, 235, 0, 1811 }, +{ 0x1, 235, 0, 1812 }, +{ 0x4, 185, 0, 1813 }, +{ 0x2, 185, 0, 1814 }, +{ 0x1, 185, 0, 1815 }, +{ 0x4, 182, 0, 1816 }, +{ 0x1, 190, 0, 1819 }, +{ 0x1, 189, 1, 1820 }, +{ 0x2, 182, 0, 1817 }, +{ 0x1, 142, 0, 1821 }, +{ 0x1, 297, 1, 1822 }, +{ 0x1, 182, 0, 1818 }, +{ 0x8, 144, 0, 1823 }, +{ 0x4, 144, 0, 1824 }, +{ 0x2, 144, 0, 1825 }, +{ 0x1, 144, 1, 1827 }, +{ 0x1, 145, 0, 1826 }, +{ 0x8, 146, 0, 1828 }, +{ 0x4, 146, 0, 1829 }, +{ 0x2, 146, 0, 1830 }, +{ 0x1, 146, 1, 1832 }, +{ 0x1, 147, 1, 1831 }, +{ 0x1, 426, 0, 1199 }, +{ 0x8, 180, 0, 1833 }, +{ 0x4, 180, 0, 1834 }, +{ 0x2, 180, 0, 1835 }, +{ 0x1, 180, 1, 1837 }, +{ 0x1, 181, 1, 1836 }, +{ 0x1, 425, 0, 1200 }, +{ 0x8, 183, 0, 1838 }, +{ 0x4, 183, 0, 1839 }, +{ 0x2, 183, 0, 1840 }, +{ 0x1, 183, 1, 1842 }, +{ 0x1, 184, 1, 1841 }, +{ 0x1, 424, 0, 1201 }, +{ 0x8, 228, 0, 1843 }, +{ 0x4, 228, 0, 1844 }, +{ 0x2, 228, 0, 1845 }, +{ 0x1, 228, 1, 1847 }, +{ 0x1, 229, 0, 1846 }, +{ 0x8, 226, 0, 1848 }, +{ 0x4, 226, 0, 1849 }, +{ 0x2, 226, 0, 1850 }, +{ 0x1, 226, 1, 1852 }, +{ 0x1, 227, 0, 1851 }, +{ 0x8, 44, 0, 1857 }, +{ 0x18, 44, 0, 1853 }, +{ 0x4, 44, 0, 1858 }, +{ 0xc, 44, 0, 1854 }, +{ 0x2, 44, 0, 1859 }, +{ 0x6, 44, 0, 1855 }, +{ 0x1, 44, 0, 1860 }, +{ 0x3, 44, 0, 1856 }, +{ 0x51, 30, 0, 1862 }, +{ 0xd1, 30, 0, 1861 }, +{ 0x31, 30, 1, 1872 }, +{ 0x11, 31, 0, 1871 }, +{ 0x71, 30, 1, 1870 }, +{ 0x31, 31, 0, 1869 }, +{ 0x29, 30, 0, 1864 }, +{ 0x69, 30, 0, 1863 }, +{ 0x19, 30, 1, 1876 }, +{ 0x9, 31, 0, 1875 }, +{ 0x39, 30, 1, 1874 }, +{ 0x19, 31, 0, 1873 }, +{ 0x15, 30, 0, 1866 }, +{ 0x35, 30, 0, 1865 }, +{ 0xd, 30, 1, 1880 }, +{ 0x5, 31, 0, 1879 }, +{ 0x1d, 30, 1, 1878 }, +{ 0xd, 31, 0, 1877 }, +{ 0xb, 30, 0, 1868 }, +{ 0x1b, 30, 0, 1867 }, +{ 0x7, 30, 1, 1884 }, +{ 0x3, 31, 0, 1883 }, +{ 0xf, 30, 1, 1882 }, +{ 0x7, 31, 0, 1881 }, +{ 0xa2, 28, 0, 1886 }, +{ 0x1a2, 28, 0, 1885 }, +{ 0x62, 28, 1, 1896 }, +{ 0x22, 29, 0, 1895 }, +{ 0xe2, 28, 1, 1894 }, +{ 0x62, 29, 0, 1893 }, +{ 0x52, 28, 0, 1888 }, +{ 0xd2, 28, 0, 1887 }, +{ 0x32, 28, 1, 1900 }, +{ 0x12, 29, 0, 1899 }, +{ 0x72, 28, 1, 1898 }, +{ 0x32, 29, 0, 1897 }, +{ 0x2a, 28, 0, 1890 }, +{ 0x6a, 28, 0, 1889 }, +{ 0x1a, 28, 1, 1904 }, +{ 0xa, 29, 0, 1903 }, +{ 0x3a, 28, 1, 1902 }, +{ 0x1a, 29, 0, 1901 }, +{ 0x16, 28, 0, 1892 }, +{ 0x36, 28, 0, 1891 }, +{ 0xe, 28, 1, 1908 }, +{ 0x6, 29, 0, 1907 }, +{ 0x1e, 28, 1, 1906 }, +{ 0xe, 29, 0, 1905 }, +{ 0x51, 28, 0, 1910 }, +{ 0xd1, 28, 0, 1909 }, +{ 0x31, 28, 1, 1920 }, +{ 0x11, 29, 0, 1919 }, +{ 0x71, 28, 1, 1918 }, +{ 0x31, 29, 0, 1917 }, +{ 0x29, 28, 0, 1912 }, +{ 0x69, 28, 0, 1911 }, +{ 0x19, 28, 1, 1924 }, +{ 0x9, 29, 0, 1923 }, +{ 0x39, 28, 1, 1922 }, +{ 0x19, 29, 0, 1921 }, +{ 0x15, 28, 0, 1914 }, +{ 0x35, 28, 0, 1913 }, +{ 0xd, 28, 1, 1928 }, +{ 0x5, 29, 0, 1927 }, +{ 0x1d, 28, 1, 1926 }, +{ 0xd, 29, 0, 1925 }, +{ 0xb, 28, 0, 1916 }, +{ 0x1b, 28, 0, 1915 }, +{ 0x7, 28, 1, 1932 }, +{ 0x3, 29, 0, 1931 }, +{ 0xf, 28, 1, 1930 }, +{ 0x7, 29, 0, 1929 }, +{ 0x51, 26, 0, 1934 }, +{ 0xd1, 26, 0, 1933 }, +{ 0x31, 26, 1, 1944 }, +{ 0x11, 27, 0, 1943 }, +{ 0x71, 26, 1, 1942 }, +{ 0x31, 27, 0, 1941 }, +{ 0x29, 26, 0, 1936 }, +{ 0x69, 26, 0, 1935 }, +{ 0x19, 26, 1, 1948 }, +{ 0x9, 27, 0, 1947 }, +{ 0x39, 26, 1, 1946 }, +{ 0x19, 27, 0, 1945 }, +{ 0x15, 26, 0, 1938 }, +{ 0x35, 26, 0, 1937 }, +{ 0xd, 26, 1, 1952 }, +{ 0x5, 27, 0, 1951 }, +{ 0x1d, 26, 1, 1950 }, +{ 0xd, 27, 0, 1949 }, +{ 0xb, 26, 0, 1940 }, +{ 0x1b, 26, 0, 1939 }, +{ 0x7, 26, 1, 1956 }, +{ 0x3, 27, 0, 1955 }, +{ 0xf, 26, 1, 1954 }, +{ 0x7, 27, 0, 1953 }, +{ 0xa2, 24, 0, 1958 }, +{ 0x1a2, 24, 0, 1957 }, +{ 0x62, 24, 1, 1968 }, +{ 0x22, 25, 0, 1967 }, +{ 0xe2, 24, 1, 1966 }, +{ 0x62, 25, 0, 1965 }, +{ 0x52, 24, 0, 1960 }, +{ 0xd2, 24, 0, 1959 }, +{ 0x32, 24, 1, 1972 }, +{ 0x12, 25, 0, 1971 }, +{ 0x72, 24, 1, 1970 }, +{ 0x32, 25, 0, 1969 }, +{ 0x2a, 24, 0, 1962 }, +{ 0x6a, 24, 0, 1961 }, +{ 0x1a, 24, 1, 1976 }, +{ 0xa, 25, 0, 1975 }, +{ 0x3a, 24, 1, 1974 }, +{ 0x1a, 25, 0, 1973 }, +{ 0x16, 24, 0, 1964 }, +{ 0x36, 24, 0, 1963 }, +{ 0xe, 24, 1, 1980 }, +{ 0x6, 25, 0, 1979 }, +{ 0x1e, 24, 1, 1978 }, +{ 0xe, 25, 0, 1977 }, +{ 0x51, 24, 0, 1982 }, +{ 0xd1, 24, 0, 1981 }, +{ 0x31, 24, 1, 1992 }, +{ 0x11, 25, 0, 1991 }, +{ 0x71, 24, 1, 1990 }, +{ 0x31, 25, 0, 1989 }, +{ 0x29, 24, 0, 1984 }, +{ 0x69, 24, 0, 1983 }, +{ 0x19, 24, 1, 1996 }, +{ 0x9, 25, 0, 1995 }, +{ 0x39, 24, 1, 1994 }, +{ 0x19, 25, 0, 1993 }, +{ 0x15, 24, 0, 1986 }, +{ 0x35, 24, 0, 1985 }, +{ 0xd, 24, 1, 2000 }, +{ 0x5, 25, 0, 1999 }, +{ 0x1d, 24, 1, 1998 }, +{ 0xd, 25, 0, 1997 }, +{ 0xb, 24, 0, 1988 }, +{ 0x1b, 24, 0, 1987 }, +{ 0x7, 24, 1, 2004 }, +{ 0x3, 25, 0, 2003 }, +{ 0xf, 24, 1, 2002 }, +{ 0x7, 25, 0, 2001 }, +{ 0x51, 22, 1, 2030 }, +{ 0x50, 22, 0, 2006 }, +{ 0xd1, 22, 1, 2029 }, +{ 0xd0, 22, 0, 2005 }, +{ 0x31, 22, 1, 2040 }, +{ 0x30, 22, 1, 2016 }, +{ 0x11, 23, 1, 2039 }, +{ 0x10, 23, 0, 2015 }, +{ 0x71, 22, 1, 2038 }, +{ 0x70, 22, 1, 2014 }, +{ 0x31, 23, 1, 2037 }, +{ 0x30, 23, 0, 2013 }, +{ 0x29, 22, 1, 2032 }, +{ 0x28, 22, 0, 2008 }, +{ 0x69, 22, 1, 2031 }, +{ 0x68, 22, 0, 2007 }, +{ 0x19, 22, 1, 2044 }, +{ 0x18, 22, 1, 2020 }, +{ 0x9, 23, 1, 2043 }, +{ 0x8, 23, 0, 2019 }, +{ 0x39, 22, 1, 2042 }, +{ 0x38, 22, 1, 2018 }, +{ 0x19, 23, 1, 2041 }, +{ 0x18, 23, 0, 2017 }, +{ 0x15, 22, 1, 2034 }, +{ 0x14, 22, 0, 2010 }, +{ 0x35, 22, 1, 2033 }, +{ 0x34, 22, 0, 2009 }, +{ 0xd, 22, 1, 2048 }, +{ 0xc, 22, 1, 2024 }, +{ 0x5, 23, 1, 2047 }, +{ 0x4, 23, 0, 2023 }, +{ 0x1d, 22, 1, 2046 }, +{ 0x1c, 22, 1, 2022 }, +{ 0xd, 23, 1, 2045 }, +{ 0xc, 23, 0, 2021 }, +{ 0xb, 22, 1, 2036 }, +{ 0xa, 22, 0, 2012 }, +{ 0x1b, 22, 1, 2035 }, +{ 0x1a, 22, 0, 2011 }, +{ 0x7, 22, 1, 2052 }, +{ 0x6, 22, 1, 2028 }, +{ 0x3, 23, 1, 2051 }, +{ 0x2, 23, 0, 2027 }, +{ 0xf, 22, 1, 2050 }, +{ 0xe, 22, 1, 2026 }, +{ 0x7, 23, 1, 2049 }, +{ 0x6, 23, 0, 2025 }, +{ 0x8, 21, 0, 2054 }, +{ 0x18, 21, 0, 2053 }, +{ 0x1, 21, 1, 2058 }, +{ 0x2, 21, 0, 2057 }, +{ 0x3, 21, 1, 2056 }, +{ 0x4, 21, 0, 2055 }, +{ 0x1, 239, 0, 2059 }, +{ 0x1, 339, 0, 2060 }, +{ 0x14, 43, 0, 2063 }, +{ 0x34, 43, 0, 2061 }, +{ 0xc, 43, 0, 2064 }, +{ 0x1c, 43, 0, 2062 }, +{ 0x2, 43, 0, 2067 }, +{ 0x6, 43, 0, 2065 }, +{ 0x1, 43, 0, 2068 }, +{ 0x3, 43, 0, 2066 }, +{ 0x51, 19, 0, 2070 }, +{ 0xd1, 19, 0, 2069 }, +{ 0x31, 19, 1, 2080 }, +{ 0x11, 20, 0, 2079 }, +{ 0x71, 19, 1, 2078 }, +{ 0x31, 20, 0, 2077 }, +{ 0x29, 19, 0, 2072 }, +{ 0x69, 19, 0, 2071 }, +{ 0x19, 19, 1, 2084 }, +{ 0x9, 20, 0, 2083 }, +{ 0x39, 19, 1, 2082 }, +{ 0x19, 20, 0, 2081 }, +{ 0x15, 19, 0, 2074 }, +{ 0x35, 19, 0, 2073 }, +{ 0xd, 19, 1, 2088 }, +{ 0x5, 20, 0, 2087 }, +{ 0x1d, 19, 1, 2086 }, +{ 0xd, 20, 0, 2085 }, +{ 0xb, 19, 0, 2076 }, +{ 0x1b, 19, 0, 2075 }, +{ 0x7, 19, 1, 2092 }, +{ 0x3, 20, 0, 2091 }, +{ 0xf, 19, 1, 2090 }, +{ 0x7, 20, 0, 2089 }, +{ 0x1, 32, 0, 2093 }, +{ 0x2, 447, 0, 2094 }, +{ 0x1, 447, 0, 2095 }, +{ 0x1, 140, 0, 2096 }, +{ 0x2, 45, 0, 2097 }, +{ 0x1, 45, 0, 2098 }, +{ 0x1, 387, 0, 2099 }, +{ 0x2, 52, 0, 2100 }, +{ 0x1, 52, 0, 2101 }, +{ 0x1, 133, 0, 2102 }, +{ 0x51, 17, 0, 2104 }, +{ 0xd1, 17, 0, 2103 }, +{ 0x31, 17, 1, 2114 }, +{ 0x11, 18, 0, 2113 }, +{ 0x71, 17, 1, 2112 }, +{ 0x31, 18, 0, 2111 }, +{ 0x29, 17, 0, 2106 }, +{ 0x69, 17, 0, 2105 }, +{ 0x19, 17, 1, 2118 }, +{ 0x9, 18, 0, 2117 }, +{ 0x39, 17, 1, 2116 }, +{ 0x19, 18, 0, 2115 }, +{ 0x15, 17, 0, 2108 }, +{ 0x35, 17, 0, 2107 }, +{ 0xd, 17, 1, 2122 }, +{ 0x5, 18, 0, 2121 }, +{ 0x1d, 17, 1, 2120 }, +{ 0xd, 18, 0, 2119 }, +{ 0xb, 17, 0, 2110 }, +{ 0x1b, 17, 0, 2109 }, +{ 0x7, 17, 1, 2126 }, +{ 0x3, 18, 0, 2125 }, +{ 0xf, 17, 1, 2124 }, +{ 0x7, 18, 0, 2123 }, +{ 0xa20, 15, 0, 2128 }, +{ 0x1a20, 15, 0, 2127 }, +{ 0x620, 15, 1, 2138 }, +{ 0x220, 16, 0, 2137 }, +{ 0xe20, 15, 1, 2136 }, +{ 0x620, 16, 0, 2135 }, +{ 0x520, 15, 0, 2130 }, +{ 0xd20, 15, 0, 2129 }, +{ 0x320, 15, 1, 2142 }, +{ 0x120, 16, 0, 2141 }, +{ 0x720, 15, 1, 2140 }, +{ 0x320, 16, 0, 2139 }, +{ 0x2a0, 15, 0, 2132 }, +{ 0x6a0, 15, 0, 2131 }, +{ 0x1a0, 15, 1, 2146 }, +{ 0xa0, 16, 0, 2145 }, +{ 0x3a0, 15, 1, 2144 }, +{ 0x1a0, 16, 0, 2143 }, +{ 0x160, 15, 0, 2134 }, +{ 0x360, 15, 0, 2133 }, +{ 0xe0, 15, 1, 2150 }, +{ 0x60, 16, 0, 2149 }, +{ 0x1e0, 15, 1, 2148 }, +{ 0xe0, 16, 0, 2147 }, +{ 0x51, 15, 1, 2176 }, +{ 0x50, 15, 0, 2152 }, +{ 0xd1, 15, 1, 2175 }, +{ 0xd0, 15, 0, 2151 }, +{ 0x31, 15, 1, 2186 }, +{ 0x30, 15, 1, 2162 }, +{ 0x11, 16, 1, 2185 }, +{ 0x10, 16, 0, 2161 }, +{ 0x71, 15, 1, 2184 }, +{ 0x70, 15, 1, 2160 }, +{ 0x31, 16, 1, 2183 }, +{ 0x30, 16, 0, 2159 }, +{ 0x29, 15, 1, 2178 }, +{ 0x28, 15, 0, 2154 }, +{ 0x69, 15, 1, 2177 }, +{ 0x68, 15, 0, 2153 }, +{ 0x19, 15, 1, 2190 }, +{ 0x18, 15, 1, 2166 }, +{ 0x9, 16, 1, 2189 }, +{ 0x8, 16, 0, 2165 }, +{ 0x39, 15, 1, 2188 }, +{ 0x38, 15, 1, 2164 }, +{ 0x19, 16, 1, 2187 }, +{ 0x18, 16, 0, 2163 }, +{ 0x15, 15, 1, 2180 }, +{ 0x14, 15, 0, 2156 }, +{ 0x35, 15, 1, 2179 }, +{ 0x34, 15, 0, 2155 }, +{ 0xd, 15, 1, 2194 }, +{ 0xc, 15, 1, 2170 }, +{ 0x5, 16, 1, 2193 }, +{ 0x4, 16, 0, 2169 }, +{ 0x1d, 15, 1, 2192 }, +{ 0x1c, 15, 1, 2168 }, +{ 0xd, 16, 1, 2191 }, +{ 0xc, 16, 0, 2167 }, +{ 0xb, 15, 1, 2182 }, +{ 0xa, 15, 0, 2158 }, +{ 0x1b, 15, 1, 2181 }, +{ 0x1a, 15, 0, 2157 }, +{ 0x7, 15, 1, 2198 }, +{ 0x6, 15, 1, 2174 }, +{ 0x3, 16, 1, 2197 }, +{ 0x2, 16, 0, 2173 }, +{ 0xf, 15, 1, 2196 }, +{ 0xe, 15, 1, 2172 }, +{ 0x7, 16, 1, 2195 }, +{ 0x6, 16, 0, 2171 }, +{ 0x8, 14, 0, 2200 }, +{ 0x18, 14, 0, 2199 }, +{ 0x1, 14, 1, 2204 }, +{ 0x2, 14, 0, 2203 }, +{ 0x3, 14, 1, 2202 }, +{ 0x4, 14, 0, 2201 }, +{ 0x1, 109, 1, 2356 }, +{ 0x1, 110, 1, 2355 }, +{ 0x1, 111, 1, 2354 }, +{ 0x1, 112, 1, 2353 }, +{ 0x1, 113, 1, 2352 }, +{ 0x1, 114, 1, 2351 }, +{ 0x1, 115, 1, 2350 }, +{ 0x1, 116, 1, 2349 }, +{ 0x39, 41, 1, 22 }, +{ 0x19, 42, 0, 21 }, +{ 0x3, 109, 1, 2348 }, +{ 0x3, 110, 1, 2347 }, +{ 0x3, 111, 1, 2346 }, +{ 0x3, 112, 1, 2345 }, +{ 0x3, 113, 1, 2344 }, +{ 0x3, 114, 1, 2343 }, +{ 0x3, 115, 1, 2342 }, +{ 0x3, 116, 1, 2341 }, +{ 0x69, 41, 0, 11 }, +{ 0x14, 100, 1, 2336 }, +{ 0x22, 101, 1, 2333 }, +{ 0x44, 101, 1, 2335 }, +{ 0xa, 108, 1, 2334 }, +{ 0xd1, 41, 0, 9 }, +{ 0x34, 100, 1, 2208 }, +{ 0xc4, 101, 1, 2207 }, +{ 0x1c, 107, 1, 2205 }, +{ 0xe, 122, 0, 2206 }, +{ 0xc, 100, 1, 2496 }, +{ 0xa, 101, 1, 2493 }, +{ 0x14, 101, 1, 2495 }, +{ 0x6, 108, 0, 2494 }, +{ 0x2, 100, 1, 2220 }, +{ 0x2, 101, 1, 2219 }, +{ 0x2, 106, 1, 2218 }, +{ 0x2, 107, 0, 2217 }, +{ 0x12, 100, 1, 2216 }, +{ 0x42, 101, 1, 2215 }, +{ 0x6, 106, 1, 2214 }, +{ 0x6, 107, 0, 2213 }, +{ 0xa, 100, 1, 2340 }, +{ 0x12, 101, 1, 2339 }, +{ 0x24, 101, 1, 2337 }, +{ 0x5, 108, 1, 2338 }, +{ 0x71, 41, 1, 18 }, +{ 0x31, 42, 0, 17 }, +{ 0x1a, 100, 1, 2212 }, +{ 0x32, 101, 1, 2211 }, +{ 0x1a, 107, 1, 2209 }, +{ 0x7, 122, 0, 2210 }, +{ 0x6, 100, 1, 2500 }, +{ 0x6, 101, 1, 2499 }, +{ 0xc, 101, 1, 2497 }, +{ 0x3, 108, 0, 2498 }, +{ 0x1, 100, 1, 2516 }, +{ 0x1, 101, 1, 2515 }, +{ 0x1, 102, 1, 2514 }, +{ 0x1, 103, 1, 2513 }, +{ 0x1, 104, 1, 2512 }, +{ 0x1, 105, 1, 2511 }, +{ 0x1, 106, 1, 2510 }, +{ 0x1, 107, 0, 2509 }, +{ 0x3, 100, 1, 2508 }, +{ 0x3, 101, 1, 2507 }, +{ 0x3, 102, 1, 2506 }, +{ 0x3, 103, 1, 2505 }, +{ 0x3, 104, 1, 2504 }, +{ 0x3, 105, 1, 2503 }, +{ 0x3, 106, 1, 2502 }, +{ 0x3, 107, 0, 2501 }, +{ 0x8, 67, 1, 2380 }, +{ 0x8, 68, 1, 2379 }, +{ 0x2, 73, 1, 2374 }, +{ 0x2, 74, 1, 2373 }, +{ 0x1, 76, 1, 2378 }, +{ 0x1, 77, 1, 2377 }, +{ 0x1, 78, 1, 2376 }, +{ 0x1, 79, 1, 2375 }, +{ 0xf, 41, 1, 30 }, +{ 0x7, 42, 0, 29 }, +{ 0x18, 67, 1, 2372 }, +{ 0x18, 68, 1, 2371 }, +{ 0x6, 73, 1, 2366 }, +{ 0x6, 74, 1, 2365 }, +{ 0x3, 76, 1, 2370 }, +{ 0x3, 77, 1, 2369 }, +{ 0x3, 78, 1, 2368 }, +{ 0x3, 79, 1, 2367 }, +{ 0x1b, 41, 0, 15 }, +{ 0x14, 67, 1, 2360 }, +{ 0x22, 68, 1, 2357 }, +{ 0x44, 68, 1, 2359 }, +{ 0xa, 75, 1, 2358 }, +{ 0x35, 41, 0, 13 }, +{ 0x34, 67, 1, 2224 }, +{ 0xc4, 68, 1, 2223 }, +{ 0x38, 74, 1, 2221 }, +{ 0xe, 85, 0, 2222 }, +{ 0xc, 67, 1, 2520 }, +{ 0xa, 68, 1, 2517 }, +{ 0x14, 68, 1, 2519 }, +{ 0x6, 75, 0, 2518 }, +{ 0x2, 67, 1, 2236 }, +{ 0x2, 68, 1, 2235 }, +{ 0x4, 73, 1, 2234 }, +{ 0x4, 74, 0, 2233 }, +{ 0x12, 67, 1, 2232 }, +{ 0x42, 68, 1, 2231 }, +{ 0xc, 73, 1, 2230 }, +{ 0xc, 74, 0, 2229 }, +{ 0xa, 67, 1, 2364 }, +{ 0x12, 68, 1, 2363 }, +{ 0x24, 68, 1, 2361 }, +{ 0x5, 75, 1, 2362 }, +{ 0x1d, 41, 1, 26 }, +{ 0xd, 42, 0, 25 }, +{ 0x1a, 67, 1, 2228 }, +{ 0x32, 68, 1, 2227 }, +{ 0x34, 74, 1, 2225 }, +{ 0x7, 85, 0, 2226 }, +{ 0x6, 67, 1, 2524 }, +{ 0x6, 68, 1, 2523 }, +{ 0xc, 68, 1, 2521 }, +{ 0x3, 75, 0, 2522 }, +{ 0x1, 67, 1, 2540 }, +{ 0x1, 68, 1, 2539 }, +{ 0x1, 69, 1, 2538 }, +{ 0x1, 70, 1, 2537 }, +{ 0x1, 71, 1, 2536 }, +{ 0x1, 72, 1, 2535 }, +{ 0x1, 73, 1, 2534 }, +{ 0x1, 74, 0, 2533 }, +{ 0x3, 67, 1, 2532 }, +{ 0x3, 68, 1, 2531 }, +{ 0x3, 69, 1, 2530 }, +{ 0x3, 70, 1, 2529 }, +{ 0x3, 71, 1, 2528 }, +{ 0x3, 72, 1, 2527 }, +{ 0x3, 73, 1, 2526 }, +{ 0x3, 74, 0, 2525 }, +{ 0x28, 95, 1, 2388 }, +{ 0x44, 96, 1, 2383 }, +{ 0x88, 96, 1, 2387 }, +{ 0x44, 97, 1, 2382 }, +{ 0x88, 97, 1, 2386 }, +{ 0x44, 98, 1, 2381 }, +{ 0x88, 98, 1, 2385 }, +{ 0x28, 99, 0, 2384 }, +{ 0x68, 95, 1, 2244 }, +{ 0x188, 96, 1, 2243 }, +{ 0x188, 97, 1, 2242 }, +{ 0x188, 98, 1, 2241 }, +{ 0x38, 118, 1, 2240 }, +{ 0x38, 119, 1, 2239 }, +{ 0x38, 120, 1, 2238 }, +{ 0x38, 121, 0, 2237 }, +{ 0x18, 95, 1, 2548 }, +{ 0x14, 96, 1, 2543 }, +{ 0x28, 96, 1, 2547 }, +{ 0x14, 97, 1, 2542 }, +{ 0x28, 97, 1, 2546 }, +{ 0x14, 98, 1, 2541 }, +{ 0x28, 98, 1, 2545 }, +{ 0x18, 99, 0, 2544 }, +{ 0x14, 95, 1, 2396 }, +{ 0x24, 96, 1, 2395 }, +{ 0x48, 96, 1, 2391 }, +{ 0x24, 97, 1, 2394 }, +{ 0x48, 97, 1, 2390 }, +{ 0x24, 98, 1, 2393 }, +{ 0x48, 98, 1, 2389 }, +{ 0x14, 99, 0, 2392 }, +{ 0x34, 95, 1, 2252 }, +{ 0x64, 96, 1, 2251 }, +{ 0x64, 97, 1, 2250 }, +{ 0x64, 98, 1, 2249 }, +{ 0x1c, 118, 1, 2248 }, +{ 0x1c, 119, 1, 2247 }, +{ 0x1c, 120, 1, 2246 }, +{ 0x1c, 121, 0, 2245 }, +{ 0xc, 95, 1, 2556 }, +{ 0xc, 96, 1, 2555 }, +{ 0x18, 96, 1, 2551 }, +{ 0xc, 97, 1, 2554 }, +{ 0x18, 97, 1, 2550 }, +{ 0xc, 98, 1, 2553 }, +{ 0x18, 98, 1, 2549 }, +{ 0xc, 99, 0, 2552 }, +{ 0xa, 95, 1, 2404 }, +{ 0x11, 96, 1, 2399 }, +{ 0x22, 96, 1, 2403 }, +{ 0x11, 97, 1, 2398 }, +{ 0x22, 97, 1, 2402 }, +{ 0x11, 98, 1, 2397 }, +{ 0x22, 98, 1, 2401 }, +{ 0xa, 99, 0, 2400 }, +{ 0x1a, 95, 1, 2260 }, +{ 0x62, 96, 1, 2259 }, +{ 0x62, 97, 1, 2258 }, +{ 0x62, 98, 1, 2257 }, +{ 0xe, 118, 1, 2256 }, +{ 0xe, 119, 1, 2255 }, +{ 0xe, 120, 1, 2254 }, +{ 0xe, 121, 0, 2253 }, +{ 0x6, 95, 1, 2564 }, +{ 0x5, 96, 1, 2559 }, +{ 0xa, 96, 1, 2563 }, +{ 0x5, 97, 1, 2558 }, +{ 0xa, 97, 1, 2562 }, +{ 0x5, 98, 1, 2557 }, +{ 0xa, 98, 1, 2561 }, +{ 0x6, 99, 0, 2560 }, +{ 0x5, 95, 1, 2412 }, +{ 0x9, 96, 1, 2411 }, +{ 0x12, 96, 1, 2407 }, +{ 0x9, 97, 1, 2410 }, +{ 0x12, 97, 1, 2406 }, +{ 0x9, 98, 1, 2409 }, +{ 0x12, 98, 1, 2405 }, +{ 0x5, 99, 0, 2408 }, +{ 0xd, 95, 1, 2268 }, +{ 0x19, 96, 1, 2267 }, +{ 0x19, 97, 1, 2266 }, +{ 0x19, 98, 1, 2265 }, +{ 0x7, 118, 1, 2264 }, +{ 0x7, 119, 1, 2263 }, +{ 0x7, 120, 1, 2262 }, +{ 0x7, 121, 0, 2261 }, +{ 0x3, 95, 1, 2572 }, +{ 0x3, 96, 1, 2571 }, +{ 0x6, 96, 1, 2567 }, +{ 0x3, 97, 1, 2570 }, +{ 0x6, 97, 1, 2566 }, +{ 0x3, 98, 1, 2569 }, +{ 0x6, 98, 1, 2565 }, +{ 0x3, 99, 0, 2568 }, +{ 0x28, 62, 1, 2420 }, +{ 0x44, 63, 1, 2415 }, +{ 0x88, 63, 1, 2419 }, +{ 0x44, 64, 1, 2414 }, +{ 0x88, 64, 1, 2418 }, +{ 0x44, 65, 1, 2413 }, +{ 0x88, 65, 1, 2417 }, +{ 0x28, 66, 0, 2416 }, +{ 0x68, 62, 1, 2276 }, +{ 0x188, 63, 1, 2275 }, +{ 0x188, 64, 1, 2274 }, +{ 0x188, 65, 1, 2273 }, +{ 0x38, 81, 1, 2272 }, +{ 0x38, 82, 1, 2271 }, +{ 0x38, 83, 1, 2270 }, +{ 0x38, 84, 0, 2269 }, +{ 0x18, 62, 1, 2580 }, +{ 0x14, 63, 1, 2575 }, +{ 0x28, 63, 1, 2579 }, +{ 0x14, 64, 1, 2574 }, +{ 0x28, 64, 1, 2578 }, +{ 0x14, 65, 1, 2573 }, +{ 0x28, 65, 1, 2577 }, +{ 0x18, 66, 0, 2576 }, +{ 0x14, 62, 1, 2428 }, +{ 0x24, 63, 1, 2427 }, +{ 0x48, 63, 1, 2423 }, +{ 0x24, 64, 1, 2426 }, +{ 0x48, 64, 1, 2422 }, +{ 0x24, 65, 1, 2425 }, +{ 0x48, 65, 1, 2421 }, +{ 0x14, 66, 0, 2424 }, +{ 0x34, 62, 1, 2284 }, +{ 0x64, 63, 1, 2283 }, +{ 0x64, 64, 1, 2282 }, +{ 0x64, 65, 1, 2281 }, +{ 0x1c, 81, 1, 2280 }, +{ 0x1c, 82, 1, 2279 }, +{ 0x1c, 83, 1, 2278 }, +{ 0x1c, 84, 0, 2277 }, +{ 0xc, 62, 1, 2588 }, +{ 0xc, 63, 1, 2587 }, +{ 0x18, 63, 1, 2583 }, +{ 0xc, 64, 1, 2586 }, +{ 0x18, 64, 1, 2582 }, +{ 0xc, 65, 1, 2585 }, +{ 0x18, 65, 1, 2581 }, +{ 0xc, 66, 0, 2584 }, +{ 0xa, 62, 1, 2436 }, +{ 0x11, 63, 1, 2431 }, +{ 0x22, 63, 1, 2435 }, +{ 0x11, 64, 1, 2430 }, +{ 0x22, 64, 1, 2434 }, +{ 0x11, 65, 1, 2429 }, +{ 0x22, 65, 1, 2433 }, +{ 0xa, 66, 0, 2432 }, +{ 0x1a, 62, 1, 2292 }, +{ 0x62, 63, 1, 2291 }, +{ 0x62, 64, 1, 2290 }, +{ 0x62, 65, 1, 2289 }, +{ 0xe, 81, 1, 2288 }, +{ 0xe, 82, 1, 2287 }, +{ 0xe, 83, 1, 2286 }, +{ 0xe, 84, 0, 2285 }, +{ 0x6, 62, 1, 2596 }, +{ 0x5, 63, 1, 2591 }, +{ 0xa, 63, 1, 2595 }, +{ 0x5, 64, 1, 2590 }, +{ 0xa, 64, 1, 2594 }, +{ 0x5, 65, 1, 2589 }, +{ 0xa, 65, 1, 2593 }, +{ 0x6, 66, 0, 2592 }, +{ 0x5, 62, 1, 2444 }, +{ 0x9, 63, 1, 2443 }, +{ 0x12, 63, 1, 2439 }, +{ 0x9, 64, 1, 2442 }, +{ 0x12, 64, 1, 2438 }, +{ 0x9, 65, 1, 2441 }, +{ 0x12, 65, 1, 2437 }, +{ 0x5, 66, 0, 2440 }, +{ 0xd, 62, 1, 2300 }, +{ 0x19, 63, 1, 2299 }, +{ 0x19, 64, 1, 2298 }, +{ 0x19, 65, 1, 2297 }, +{ 0x7, 81, 1, 2296 }, +{ 0x7, 82, 1, 2295 }, +{ 0x7, 83, 1, 2294 }, +{ 0x7, 84, 0, 2293 }, +{ 0x3, 62, 1, 2604 }, +{ 0x3, 63, 1, 2603 }, +{ 0x6, 63, 1, 2599 }, +{ 0x3, 64, 1, 2602 }, +{ 0x6, 64, 1, 2598 }, +{ 0x3, 65, 1, 2601 }, +{ 0x6, 65, 1, 2597 }, +{ 0x3, 66, 0, 2600 }, +{ 0x8, 86, 1, 2468 }, +{ 0x8, 87, 1, 2467 }, +{ 0x2, 88, 1, 2466 }, +{ 0x2, 89, 1, 2465 }, +{ 0x2, 90, 1, 2464 }, +{ 0x2, 91, 1, 2463 }, +{ 0x2, 92, 1, 2462 }, +{ 0x2, 93, 0, 2461 }, +{ 0x18, 86, 1, 2460 }, +{ 0x18, 87, 1, 2459 }, +{ 0x6, 88, 1, 2458 }, +{ 0x6, 89, 1, 2457 }, +{ 0x6, 90, 1, 2456 }, +{ 0x6, 91, 1, 2455 }, +{ 0x6, 92, 1, 2454 }, +{ 0x6, 93, 0, 2453 }, +{ 0x14, 86, 1, 2448 }, +{ 0x22, 87, 1, 2445 }, +{ 0x44, 87, 1, 2447 }, +{ 0xa, 94, 0, 2446 }, +{ 0x34, 86, 1, 2304 }, +{ 0xc4, 87, 1, 2303 }, +{ 0x38, 93, 1, 2301 }, +{ 0xe, 117, 0, 2302 }, +{ 0xc, 86, 1, 2608 }, +{ 0xa, 87, 1, 2605 }, +{ 0x14, 87, 1, 2607 }, +{ 0x6, 94, 0, 2606 }, +{ 0x2, 86, 1, 2316 }, +{ 0x2, 87, 1, 2315 }, +{ 0x4, 92, 1, 2314 }, +{ 0x4, 93, 0, 2313 }, +{ 0x12, 86, 1, 2312 }, +{ 0x42, 87, 1, 2311 }, +{ 0xc, 92, 1, 2310 }, +{ 0xc, 93, 0, 2309 }, +{ 0xa, 86, 1, 2452 }, +{ 0x12, 87, 1, 2451 }, +{ 0x24, 87, 1, 2449 }, +{ 0x5, 94, 0, 2450 }, +{ 0x1a, 86, 1, 2308 }, +{ 0x32, 87, 1, 2307 }, +{ 0x34, 93, 1, 2305 }, +{ 0x7, 117, 0, 2306 }, +{ 0x6, 86, 1, 2612 }, +{ 0x6, 87, 1, 2611 }, +{ 0xc, 87, 1, 2609 }, +{ 0x3, 94, 0, 2610 }, +{ 0x1, 86, 1, 2628 }, +{ 0x1, 87, 1, 2627 }, +{ 0x1, 88, 1, 2626 }, +{ 0x1, 89, 1, 2625 }, +{ 0x1, 90, 1, 2624 }, +{ 0x1, 91, 1, 2623 }, +{ 0x1, 92, 1, 2622 }, +{ 0x1, 93, 0, 2621 }, +{ 0x3, 86, 1, 2620 }, +{ 0x3, 87, 1, 2619 }, +{ 0x3, 88, 1, 2618 }, +{ 0x3, 89, 1, 2617 }, +{ 0x3, 90, 1, 2616 }, +{ 0x3, 91, 1, 2615 }, +{ 0x3, 92, 1, 2614 }, +{ 0x3, 93, 0, 2613 }, +{ 0x8, 53, 1, 2492 }, +{ 0x8, 54, 1, 2491 }, +{ 0x2, 55, 1, 2490 }, +{ 0x2, 56, 1, 2489 }, +{ 0x2, 57, 1, 2488 }, +{ 0x2, 58, 1, 2487 }, +{ 0x2, 59, 1, 2486 }, +{ 0x2, 60, 0, 2485 }, +{ 0x18, 53, 1, 2484 }, +{ 0x18, 54, 1, 2483 }, +{ 0x6, 55, 1, 2482 }, +{ 0x6, 56, 1, 2481 }, +{ 0x6, 57, 1, 2480 }, +{ 0x6, 58, 1, 2479 }, +{ 0x6, 59, 1, 2478 }, +{ 0x6, 60, 0, 2477 }, +{ 0x14, 53, 1, 2472 }, +{ 0x22, 54, 1, 2469 }, +{ 0x44, 54, 1, 2471 }, +{ 0xa, 61, 0, 2470 }, +{ 0x34, 53, 1, 2320 }, +{ 0xc4, 54, 1, 2319 }, +{ 0x38, 60, 1, 2317 }, +{ 0xe, 80, 0, 2318 }, +{ 0xc, 53, 1, 2632 }, +{ 0xa, 54, 1, 2629 }, +{ 0x14, 54, 1, 2631 }, +{ 0x6, 61, 0, 2630 }, +{ 0x2, 53, 1, 2332 }, +{ 0x2, 54, 1, 2331 }, +{ 0x4, 59, 1, 2330 }, +{ 0x4, 60, 0, 2329 }, +{ 0x12, 53, 1, 2328 }, +{ 0x42, 54, 1, 2327 }, +{ 0xc, 59, 1, 2326 }, +{ 0xc, 60, 0, 2325 }, +{ 0xa, 53, 1, 2476 }, +{ 0x12, 54, 1, 2475 }, +{ 0x24, 54, 1, 2473 }, +{ 0x5, 61, 0, 2474 }, +{ 0x1a, 53, 1, 2324 }, +{ 0x32, 54, 1, 2323 }, +{ 0x34, 60, 1, 2321 }, +{ 0x7, 80, 0, 2322 }, +{ 0x6, 53, 1, 2636 }, +{ 0x6, 54, 1, 2635 }, +{ 0xc, 54, 1, 2633 }, +{ 0x3, 61, 0, 2634 }, +{ 0x1, 53, 1, 2652 }, +{ 0x1, 54, 1, 2651 }, +{ 0x1, 55, 1, 2650 }, +{ 0x1, 56, 1, 2649 }, +{ 0x1, 57, 1, 2648 }, +{ 0x1, 58, 1, 2647 }, +{ 0x1, 59, 1, 2646 }, +{ 0x1, 60, 0, 2645 }, +{ 0x3, 53, 1, 2644 }, +{ 0x3, 54, 1, 2643 }, +{ 0x3, 55, 1, 2642 }, +{ 0x3, 56, 1, 2641 }, +{ 0x3, 57, 1, 2640 }, +{ 0x3, 58, 1, 2639 }, +{ 0x3, 59, 1, 2638 }, +{ 0x3, 60, 0, 2637 }, +{ 0x1, 4, 0, 2653 }, +{ 0x1, 296, 0, 2654 }, +{ 0x1, 379, 0, 2655 }, +{ 0x1, 374, 0, 2656 }, +{ 0x2, 358, 0, 2657 }, +{ 0x1, 358, 0, 2660 }, +{ 0x2, 357, 0, 2658 }, +{ 0x1, 357, 0, 2661 }, +{ 0x2, 356, 0, 2659 }, +{ 0x1, 356, 0, 2662 }, +{ 0x1, 355, 0, 2663 }, +{ 0x1, 354, 0, 2664 }, +{ 0x2, 353, 0, 2665 }, +{ 0x1, 353, 0, 2667 }, +{ 0x2, 352, 0, 2666 }, +{ 0x1, 352, 0, 2668 }, +{ 0x1, 382, 0, 2675 }, +{ 0x8, 381, 0, 2669 }, +{ 0x4, 381, 0, 2671 }, +{ 0x2, 381, 0, 2673 }, +{ 0x1, 381, 0, 2676 }, +{ 0x8, 380, 0, 2670 }, +{ 0x4, 380, 0, 2672 }, +{ 0x2, 380, 0, 2674 }, +{ 0x1, 380, 0, 2677 }, +{ 0x1, 351, 0, 2684 }, +{ 0x8, 350, 0, 2678 }, +{ 0x4, 350, 0, 2680 }, +{ 0x2, 350, 0, 2682 }, +{ 0x1, 350, 0, 2685 }, +{ 0x8, 349, 0, 2679 }, +{ 0x4, 349, 0, 2681 }, +{ 0x2, 349, 1, 2683 }, +{ 0x4, 143, 0, 1377 }, +{ 0x1, 349, 0, 2686 }, +{ 0x1, 6, 0, 2687 }, +{ 0x1, 7, 0, 2688 }, +{ 0x1, 295, 0, 2689 }, +{ 0x1, 456, 0, 2690 }, +{ 0x1, 346, 0, 2691 }, +{ 0x1, 13, 0, 2692 }, +{ 0x1, 11, 0, 2693 }, +{ 0x1, 422, 0, 2694 }, +{ 0x1, 394, 0, 2695 }, +{ 0x1, 393, 0, 2696 }, +{ 0x1, 455, 0, 2697 }, +{ 0x1, 345, 0, 2698 }, +{ 0x1, 12, 0, 2699 }, +{ 0x1, 10, 0, 2700 }, +{ 0x1, 5, 0, 2701 }, +{ 0x1, 421, 0, 2702 }, +{ 0x1, 420, 0, 2703 }, +{ 0x1, 1, 0, 2704 }, +{ 0x1, 0, 0, 2705 }, +}; + + +/* ia64-opc.c -- Functions to access the compacted opcode table + Copyright 1999, 2000, 2001, 2003, 2005 Free Software Foundation, Inc. + Written by Bob Manson of Cygnus Solutions, + + This file is part of GDB, GAS, and the GNU binutils. + + GDB, GAS, and the GNU binutils are free software; you can redistribute + them and/or modify them under the terms of the GNU General Public + License as published by the Free Software Foundation; either version + 2, or (at your option) any later version. + + GDB, GAS, and the GNU binutils are distributed in the hope that they + 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 file; see the file COPYING. If not, see + . */ + +static const struct ia64_templ_desc ia64_templ_desc[16] = + { + { 0, { IA64_UNIT_M, IA64_UNIT_I, IA64_UNIT_I }, "MII" }, /* 0 */ + { 2, { IA64_UNIT_M, IA64_UNIT_I, IA64_UNIT_I }, "MII" }, + { 0, { IA64_UNIT_M, IA64_UNIT_L, IA64_UNIT_X }, "MLX" }, + { 0, { 0, }, "-3-" }, + { 0, { IA64_UNIT_M, IA64_UNIT_M, IA64_UNIT_I }, "MMI" }, /* 4 */ + { 1, { IA64_UNIT_M, IA64_UNIT_M, IA64_UNIT_I }, "MMI" }, + { 0, { IA64_UNIT_M, IA64_UNIT_F, IA64_UNIT_I }, "MFI" }, + { 0, { IA64_UNIT_M, IA64_UNIT_M, IA64_UNIT_F }, "MMF" }, + { 0, { IA64_UNIT_M, IA64_UNIT_I, IA64_UNIT_B }, "MIB" }, /* 8 */ + { 0, { IA64_UNIT_M, IA64_UNIT_B, IA64_UNIT_B }, "MBB" }, + { 0, { 0, }, "-a-" }, + { 0, { IA64_UNIT_B, IA64_UNIT_B, IA64_UNIT_B }, "BBB" }, + { 0, { IA64_UNIT_M, IA64_UNIT_M, IA64_UNIT_B }, "MMB" }, /* c */ + { 0, { 0, }, "-d-" }, + { 0, { IA64_UNIT_M, IA64_UNIT_F, IA64_UNIT_B }, "MFB" }, + { 0, { 0, }, "-f-" }, + }; + +/* Apply the completer referred to by COMPLETER_INDEX to OPCODE, and + return the result. */ + +static ia64_insn +apply_completer (ia64_insn opcode, int completer_index) +{ + ia64_insn mask = completer_table[completer_index].mask; + ia64_insn bits = completer_table[completer_index].bits; + int shiftamt = (completer_table[completer_index].offset & 63); + + mask = mask << shiftamt; + bits = bits << shiftamt; + opcode = (opcode & ~mask) | bits; + return opcode; +} + +/* Extract BITS number of bits starting from OP_POINTER + BITOFFSET in + the dis_table array, and return its value. (BITOFFSET is numbered + starting from MSB to LSB, so a BITOFFSET of 0 indicates the MSB of the + first byte in OP_POINTER.) */ + +static int +extract_op_bits (int op_pointer, int bitoffset, int bits) +{ + int res = 0; + + op_pointer += (bitoffset / 8); + + if (bitoffset % 8) + { + unsigned int op = dis_table[op_pointer++]; + int numb = 8 - (bitoffset % 8); + int mask = (1 << numb) - 1; + int bata = (bits < numb) ? bits : numb; + int delta = numb - bata; + + res = (res << bata) | ((op & mask) >> delta); + bitoffset += bata; + bits -= bata; + } + while (bits >= 8) + { + res = (res << 8) | (dis_table[op_pointer++] & 255); + bits -= 8; + } + if (bits > 0) + { + unsigned int op = (dis_table[op_pointer++] & 255); + res = (res << bits) | (op >> (8 - bits)); + } + return res; +} + +/* Examine the state machine entry at OP_POINTER in the dis_table + array, and extract its values into OPVAL and OP. The length of the + state entry in bits is returned. */ + +static int +extract_op (int op_pointer, int *opval, unsigned int *op) +{ + int oplen = 5; + + *op = dis_table[op_pointer]; + + if ((*op) & 0x40) + { + opval[0] = extract_op_bits (op_pointer, oplen, 5); + oplen += 5; + } + switch ((*op) & 0x30) + { + case 0x10: + { + opval[1] = extract_op_bits (op_pointer, oplen, 8); + oplen += 8; + opval[1] += op_pointer; + break; + } + case 0x20: + { + opval[1] = extract_op_bits (op_pointer, oplen, 16); + if (! (opval[1] & 32768)) + { + opval[1] += op_pointer; + } + oplen += 16; + break; + } + case 0x30: + { + oplen--; + opval[2] = extract_op_bits (op_pointer, oplen, 12); + oplen += 12; + opval[2] |= 32768; + break; + } + } + if (((*op) & 0x08) && (((*op) & 0x30) != 0x30)) + { + opval[2] = extract_op_bits (op_pointer, oplen, 16); + oplen += 16; + if (! (opval[2] & 32768)) + { + opval[2] += op_pointer; + } + } + return oplen; +} + +/* Returns a non-zero value if the opcode in the main_table list at + PLACE matches OPCODE and is of type TYPE. */ + +static int +opcode_verify (ia64_insn opcode, int place, enum ia64_insn_type type) +{ + if (main_table[place].opcode_type != type) + { + return 0; + } + if (main_table[place].flags + & (IA64_OPCODE_F2_EQ_F3 | IA64_OPCODE_LEN_EQ_64MCNT)) + { + const struct ia64_operand *o1, *o2; + ia64_insn f2, f3; + + if (main_table[place].flags & IA64_OPCODE_F2_EQ_F3) + { + o1 = elf64_ia64_operands + IA64_OPND_F2; + o2 = elf64_ia64_operands + IA64_OPND_F3; + (*o1->extract) (o1, opcode, &f2); + (*o2->extract) (o2, opcode, &f3); + if (f2 != f3) + return 0; + } + else + { + ia64_insn len, count; + + /* length must equal 64-count: */ + o1 = elf64_ia64_operands + IA64_OPND_LEN6; + o2 = elf64_ia64_operands + main_table[place].operands[2]; + (*o1->extract) (o1, opcode, &len); + (*o2->extract) (o2, opcode, &count); + if (len != 64 - count) + return 0; + } + } + return 1; +} + +/* Find an instruction entry in the ia64_dis_names array that matches + opcode OPCODE and is of type TYPE. Returns either a positive index + into the array, or a negative value if an entry for OPCODE could + not be found. Checks all matches and returns the one with the highest + priority. */ + +static int +locate_opcode_ent (ia64_insn opcode, enum ia64_insn_type type) +{ + int currtest[41]; + int bitpos[41]; + int op_ptr[41]; + int currstatenum = 0; + short found_disent = -1; + short found_priority = -1; + + currtest[currstatenum] = 0; + op_ptr[currstatenum] = 0; + bitpos[currstatenum] = 40; + + while (1) + { + int op_pointer = op_ptr[currstatenum]; + unsigned int op; + int currbitnum = bitpos[currstatenum]; + int oplen; + int opval[3] = {0}; + int next_op; + int currbit; + + oplen = extract_op (op_pointer, opval, &op); + + bitpos[currstatenum] = currbitnum; + + /* Skip opval[0] bits in the instruction. */ + if (op & 0x40) + { + currbitnum -= opval[0]; + } + + /* The value of the current bit being tested. */ + currbit = opcode & (((ia64_insn) 1) << currbitnum) ? 1 : 0; + next_op = -1; + + /* We always perform the tests specified in the current state in + a particular order, falling through to the next test if the + previous one failed. */ + switch (currtest[currstatenum]) + { + case 0: + currtest[currstatenum]++; + if (currbit == 0 && (op & 0x80)) + { + /* Check for a zero bit. If this test solely checks for + a zero bit, we can check for up to 8 consecutive zero + bits (the number to check is specified by the lower 3 + bits in the state code.) + + If the state instruction matches, we go to the very + next state instruction; otherwise, try the next test. */ + + if ((op & 0xf8) == 0x80) + { + int count = op & 0x7; + int x; + + for (x = 0; x <= count; x++) + { + int i = + opcode & (((ia64_insn) 1) << (currbitnum - x)) ? 1 : 0; + if (i) + { + break; + } + } + if (x > count) + { + next_op = op_pointer + ((oplen + 7) / 8); + currbitnum -= count; + break; + } + } + else if (! currbit) + { + next_op = op_pointer + ((oplen + 7) / 8); + break; + } + } + /* FALLTHROUGH */ + case 1: + /* If the bit in the instruction is one, go to the state + instruction specified by opval[1]. */ + currtest[currstatenum]++; + if (currbit && (op & 0x30) != 0 && ((op & 0x30) != 0x30)) + { + next_op = opval[1]; + break; + } + /* FALLTHROUGH */ + case 2: + /* Don't care. Skip the current bit and go to the state + instruction specified by opval[2]. + + An encoding of 0x30 is special; this means that a 12-bit + offset into the ia64_dis_names[] array is specified. */ + currtest[currstatenum]++; + if ((op & 0x08) || ((op & 0x30) == 0x30)) + { + next_op = opval[2]; + break; + } + } + + /* If bit 15 is set in the address of the next state, an offset + in the ia64_dis_names array was specified instead. We then + check to see if an entry in the list of opcodes matches the + opcode we were given; if so, we have succeeded. */ + + if ((next_op >= 0) && (next_op & 32768)) + { + short disent = next_op & 32767; + short priority = -1; + + if (next_op > 65535) + { + abort (); + } + + /* Run through the list of opcodes to check, trying to find + one that matches. */ + while (disent >= 0) + { + int place = ia64_dis_names[disent].insn_index; + + priority = ia64_dis_names[disent].priority; + + if (opcode_verify (opcode, place, type) + && priority > found_priority) + { + break; + } + if (ia64_dis_names[disent].next_flag) + { + disent++; + } + else + { + disent = -1; + } + } + + if (disent >= 0) + { + found_disent = disent; + found_priority = priority; + } + /* Try the next test in this state, regardless of whether a match + was found. */ + next_op = -2; + } + + /* next_op == -1 is "back up to the previous state". + next_op == -2 is "stay in this state and try the next test". + Otherwise, transition to the state indicated by next_op. */ + + if (next_op == -1) + { + currstatenum--; + if (currstatenum < 0) + { + return found_disent; + } + } + else if (next_op >= 0) + { + currstatenum++; + bitpos[currstatenum] = currbitnum - 1; + op_ptr[currstatenum] = next_op; + currtest[currstatenum] = 0; + } + } +} + +/* Construct an ia64_opcode entry based on OPCODE, NAME and PLACE. */ + +static struct ia64_opcode * +make_ia64_opcode (ia64_insn opcode, const char *name, int place, int depind) +{ + struct ia64_opcode *res = + (struct ia64_opcode *) malloc (sizeof (struct ia64_opcode)); + res->name = strdup (name); + res->type = main_table[place].opcode_type; + res->num_outputs = main_table[place].num_outputs; + res->opcode = opcode; + res->mask = main_table[place].mask; + res->operands[0] = main_table[place].operands[0]; + res->operands[1] = main_table[place].operands[1]; + res->operands[2] = main_table[place].operands[2]; + res->operands[3] = main_table[place].operands[3]; + res->operands[4] = main_table[place].operands[4]; + res->flags = main_table[place].flags; + res->ent_index = place; + res->dependencies = &op_dependencies[depind]; + return res; +} + +/* Determine the ia64_opcode entry for the opcode specified by INSN + and TYPE. If a valid entry is not found, return NULL. */ +static struct ia64_opcode * +ia64_dis_opcode (ia64_insn insn, enum ia64_insn_type type) +{ + int disent = locate_opcode_ent (insn, type); + + if (disent < 0) + { + return NULL; + } + else + { + unsigned int cb = ia64_dis_names[disent].completer_index; + static char name[128]; + int place = ia64_dis_names[disent].insn_index; + int ci = main_table[place].completers; + ia64_insn tinsn = main_table[place].opcode; + + strcpy (name, ia64_strings [main_table[place].name_index]); + + while (cb) + { + if (cb & 1) + { + int cname = completer_table[ci].name_index; + + tinsn = apply_completer (tinsn, ci); + + if (ia64_strings[cname][0] != '\0') + { + strcat (name, "."); + strcat (name, ia64_strings[cname]); + } + if (cb != 1) + { + ci = completer_table[ci].subentries; + } + } + else + { + ci = completer_table[ci].alternative; + } + if (ci < 0) + { + abort (); + } + cb = cb >> 1; + } + if (tinsn != (insn & main_table[place].mask)) + { + abort (); + } + return make_ia64_opcode (insn, name, place, + completer_table[ci].dependencies); + } +} + +/* Free any resources used by ENT. */ +static void +ia64_free_opcode (struct ia64_opcode *ent) +{ + free ((void *)ent->name); + free (ent); +} + +/* Disassemble ia64 instruction. */ + +/* Return the instruction type for OPCODE found in unit UNIT. */ + +static enum ia64_insn_type +unit_to_type (ia64_insn opcode, enum ia64_unit unit) +{ + enum ia64_insn_type type; + int op; + + op = IA64_OP (opcode); + + if (op >= 8 && (unit == IA64_UNIT_I || unit == IA64_UNIT_M)) + { + type = IA64_TYPE_A; + } + else + { + switch (unit) + { + case IA64_UNIT_I: + type = IA64_TYPE_I; break; + case IA64_UNIT_M: + type = IA64_TYPE_M; break; + case IA64_UNIT_B: + type = IA64_TYPE_B; break; + case IA64_UNIT_F: + type = IA64_TYPE_F; break; + case IA64_UNIT_L: + case IA64_UNIT_X: + type = IA64_TYPE_X; break; + default: + type = -1; + } + } + return type; +} + +int +print_insn_ia64 (bfd_vma memaddr, struct disassemble_info *info) +{ + ia64_insn t0, t1, slot[3], template, s_bit, insn; + int slotnum, j, status, need_comma, retval, slot_multiplier; + const struct ia64_operand *odesc; + const struct ia64_opcode *idesc; + const char *err, *str, *tname; + uint64_t value; + bfd_byte bundle[16]; + enum ia64_unit unit; + char regname[16]; + + if (info->bytes_per_line == 0) + info->bytes_per_line = 6; + info->display_endian = info->endian; + + slot_multiplier = info->bytes_per_line; + retval = slot_multiplier; + + slotnum = (((long) memaddr) & 0xf) / slot_multiplier; + if (slotnum > 2) + return -1; + + memaddr -= (memaddr & 0xf); + status = (*info->read_memory_func) (memaddr, bundle, sizeof (bundle), info); + if (status != 0) + { + (*info->memory_error_func) (status, memaddr, info); + return -1; + } + /* bundles are always in little-endian byte order */ + t0 = bfd_getl64 (bundle); + t1 = bfd_getl64 (bundle + 8); + s_bit = t0 & 1; + template = (t0 >> 1) & 0xf; + slot[0] = (t0 >> 5) & 0x1ffffffffffLL; + slot[1] = ((t0 >> 46) & 0x3ffff) | ((t1 & 0x7fffff) << 18); + slot[2] = (t1 >> 23) & 0x1ffffffffffLL; + + tname = ia64_templ_desc[template].name; + if (slotnum == 0) + (*info->fprintf_func) (info->stream, "[%s] ", tname); + else + (*info->fprintf_func) (info->stream, " "); + + unit = ia64_templ_desc[template].exec_unit[slotnum]; + + if (template == 2 && slotnum == 1) + { + /* skip L slot in MLI template: */ + slotnum = 2; + retval += slot_multiplier; + } + + insn = slot[slotnum]; + + if (unit == IA64_UNIT_NIL) + goto decoding_failed; + + idesc = ia64_dis_opcode (insn, unit_to_type (insn, unit)); + if (idesc == NULL) + goto decoding_failed; + + /* print predicate, if any: */ + + if ((idesc->flags & IA64_OPCODE_NO_PRED) + || (insn & 0x3f) == 0) + (*info->fprintf_func) (info->stream, " "); + else + (*info->fprintf_func) (info->stream, "(p%02d) ", (int)(insn & 0x3f)); + + /* now the actual instruction: */ + + (*info->fprintf_func) (info->stream, "%s", idesc->name); + if (idesc->operands[0]) + (*info->fprintf_func) (info->stream, " "); + + need_comma = 0; + for (j = 0; j < NELEMS (idesc->operands) && idesc->operands[j]; ++j) + { + odesc = elf64_ia64_operands + idesc->operands[j]; + + if (need_comma) + (*info->fprintf_func) (info->stream, ","); + + if (odesc - elf64_ia64_operands == IA64_OPND_IMMU64) + { + /* special case of 64 bit immediate load: */ + value = ((insn >> 13) & 0x7f) | (((insn >> 27) & 0x1ff) << 7) + | (((insn >> 22) & 0x1f) << 16) | (((insn >> 21) & 0x1) << 21) + | (slot[1] << 22) | (((insn >> 36) & 0x1) << 63); + } + else if (odesc - elf64_ia64_operands == IA64_OPND_IMMU62) + { + /* 62-bit immediate for nop.x/break.x */ + value = ((slot[1] & 0x1ffffffffffLL) << 21) + | (((insn >> 36) & 0x1) << 20) + | ((insn >> 6) & 0xfffff); + } + else if (odesc - elf64_ia64_operands == IA64_OPND_TGT64) + { + /* 60-bit immediate for long branches. */ + value = (((insn >> 13) & 0xfffff) + | (((insn >> 36) & 1) << 59) + | (((slot[1] >> 2) & 0x7fffffffffLL) << 20)) << 4; + } + else + { + err = (*odesc->extract) (odesc, insn, &value); + if (err) + { + (*info->fprintf_func) (info->stream, "%s", err); + goto done; + } + } + + switch (odesc->class) + { + case IA64_OPND_CLASS_CST: + (*info->fprintf_func) (info->stream, "%s", odesc->str); + break; + + case IA64_OPND_CLASS_REG: + if (odesc->str[0] == 'a' && odesc->str[1] == 'r') + { + switch (value) + { + case 0: case 1: case 2: case 3: + case 4: case 5: case 6: case 7: + sprintf (regname, "ar.k%u", (unsigned int) value); + break; + case 16: strcpy (regname, "ar.rsc"); break; + case 17: strcpy (regname, "ar.bsp"); break; + case 18: strcpy (regname, "ar.bspstore"); break; + case 19: strcpy (regname, "ar.rnat"); break; + case 32: strcpy (regname, "ar.ccv"); break; + case 36: strcpy (regname, "ar.unat"); break; + case 40: strcpy (regname, "ar.fpsr"); break; + case 44: strcpy (regname, "ar.itc"); break; + case 64: strcpy (regname, "ar.pfs"); break; + case 65: strcpy (regname, "ar.lc"); break; + case 66: strcpy (regname, "ar.ec"); break; + default: + sprintf (regname, "ar%u", (unsigned int) value); + break; + } + (*info->fprintf_func) (info->stream, "%s", regname); + } + else + (*info->fprintf_func) (info->stream, "%s%d", odesc->str, (int)value); + break; + + case IA64_OPND_CLASS_IND: + (*info->fprintf_func) (info->stream, "%s[r%d]", odesc->str, (int)value); + break; + + case IA64_OPND_CLASS_ABS: + str = 0; + if (odesc - elf64_ia64_operands == IA64_OPND_MBTYPE4) + switch (value) + { + case 0x0: str = "@brcst"; break; + case 0x8: str = "@mix"; break; + case 0x9: str = "@shuf"; break; + case 0xa: str = "@alt"; break; + case 0xb: str = "@rev"; break; + } + + if (str) + (*info->fprintf_func) (info->stream, "%s", str); + else if (odesc->flags & IA64_OPND_FLAG_DECIMAL_SIGNED) + (*info->fprintf_func) (info->stream, "%" PRId64, + (int64_t) value); + else if (odesc->flags & IA64_OPND_FLAG_DECIMAL_UNSIGNED) + (*info->fprintf_func) (info->stream, "%" PRIu64, + (uint64_t) value); + else + (*info->fprintf_func) (info->stream, "0x%" PRIx64, + (uint64_t) value); + break; + + case IA64_OPND_CLASS_REL: + (*info->print_address_func) (memaddr + value, info); + break; + } + + need_comma = 1; + if (j + 1 == idesc->num_outputs) + { + (*info->fprintf_func) (info->stream, "="); + need_comma = 0; + } + } + if (slotnum + 1 == ia64_templ_desc[template].group_boundary + || ((slotnum == 2) && s_bit)) + (*info->fprintf_func) (info->stream, ";;"); + + done: + ia64_free_opcode ((struct ia64_opcode *)idesc); + failed: + if (slotnum == 2) + retval += 16 - 3*slot_multiplier; + return retval; + + decoding_failed: + (*info->fprintf_func) (info->stream, " data8 %#011llx", (long long) insn); + goto failed; +} diff -Nru qemu-kvm-0.12.5+noroms/input.c qemu-kvm-0.14.1/input.c --- qemu-kvm-0.12.5+noroms/input.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/input.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,288 @@ +/* + * 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 "sysemu.h" +#include "net.h" +#include "monitor.h" +#include "console.h" +#include "qjson.h" + +static QEMUPutKBDEvent *qemu_put_kbd_event; +static void *qemu_put_kbd_event_opaque; +static QTAILQ_HEAD(, QEMUPutLEDEntry) led_handlers = QTAILQ_HEAD_INITIALIZER(led_handlers); +static QTAILQ_HEAD(, QEMUPutMouseEntry) mouse_handlers = + QTAILQ_HEAD_INITIALIZER(mouse_handlers); +static NotifierList mouse_mode_notifiers = + NOTIFIER_LIST_INITIALIZER(mouse_mode_notifiers); + +void qemu_add_kbd_event_handler(QEMUPutKBDEvent *func, void *opaque) +{ + qemu_put_kbd_event_opaque = opaque; + qemu_put_kbd_event = func; +} + +void qemu_remove_kbd_event_handler(void) +{ + qemu_put_kbd_event_opaque = NULL; + qemu_put_kbd_event = NULL; +} + +static void check_mode_change(void) +{ + static int current_is_absolute, current_has_absolute; + int is_absolute; + int has_absolute; + + is_absolute = kbd_mouse_is_absolute(); + has_absolute = kbd_mouse_has_absolute(); + + if (is_absolute != current_is_absolute || + has_absolute != current_has_absolute) { + notifier_list_notify(&mouse_mode_notifiers); + } + + current_is_absolute = is_absolute; + current_has_absolute = has_absolute; +} + +QEMUPutMouseEntry *qemu_add_mouse_event_handler(QEMUPutMouseEvent *func, + void *opaque, int absolute, + const char *name) +{ + QEMUPutMouseEntry *s; + static int mouse_index = 0; + + s = qemu_mallocz(sizeof(QEMUPutMouseEntry)); + + s->qemu_put_mouse_event = func; + s->qemu_put_mouse_event_opaque = opaque; + s->qemu_put_mouse_event_absolute = absolute; + s->qemu_put_mouse_event_name = qemu_strdup(name); + s->index = mouse_index++; + + QTAILQ_INSERT_TAIL(&mouse_handlers, s, node); + + check_mode_change(); + + return s; +} + +void qemu_activate_mouse_event_handler(QEMUPutMouseEntry *entry) +{ + QTAILQ_REMOVE(&mouse_handlers, entry, node); + QTAILQ_INSERT_HEAD(&mouse_handlers, entry, node); + + check_mode_change(); +} + +void qemu_remove_mouse_event_handler(QEMUPutMouseEntry *entry) +{ + QTAILQ_REMOVE(&mouse_handlers, entry, node); + + qemu_free(entry->qemu_put_mouse_event_name); + qemu_free(entry); + + check_mode_change(); +} + +QEMUPutLEDEntry *qemu_add_led_event_handler(QEMUPutLEDEvent *func, + void *opaque) +{ + QEMUPutLEDEntry *s; + + s = qemu_mallocz(sizeof(QEMUPutLEDEntry)); + + s->put_led = func; + s->opaque = opaque; + QTAILQ_INSERT_TAIL(&led_handlers, s, next); + return s; +} + +void qemu_remove_led_event_handler(QEMUPutLEDEntry *entry) +{ + if (entry == NULL) + return; + QTAILQ_REMOVE(&led_handlers, entry, next); + qemu_free(entry); +} + +void kbd_put_keycode(int keycode) +{ + if (qemu_put_kbd_event) { + qemu_put_kbd_event(qemu_put_kbd_event_opaque, keycode); + } +} + +void kbd_put_ledstate(int ledstate) +{ + QEMUPutLEDEntry *cursor; + + QTAILQ_FOREACH(cursor, &led_handlers, next) { + cursor->put_led(cursor->opaque, ledstate); + } +} + +void kbd_mouse_event(int dx, int dy, int dz, int buttons_state) +{ + QEMUPutMouseEntry *entry; + QEMUPutMouseEvent *mouse_event; + void *mouse_event_opaque; + int width; + + if (QTAILQ_EMPTY(&mouse_handlers)) { + return; + } + + entry = QTAILQ_FIRST(&mouse_handlers); + + mouse_event = entry->qemu_put_mouse_event; + mouse_event_opaque = entry->qemu_put_mouse_event_opaque; + + if (mouse_event) { + if (graphic_rotate) { + if (entry->qemu_put_mouse_event_absolute) + width = 0x7fff; + else + width = graphic_width - 1; + mouse_event(mouse_event_opaque, + width - dy, dx, dz, buttons_state); + } else + mouse_event(mouse_event_opaque, + dx, dy, dz, buttons_state); + } +} + +int kbd_mouse_is_absolute(void) +{ + if (QTAILQ_EMPTY(&mouse_handlers)) { + return 0; + } + + return QTAILQ_FIRST(&mouse_handlers)->qemu_put_mouse_event_absolute; +} + +int kbd_mouse_has_absolute(void) +{ + QEMUPutMouseEntry *entry; + + QTAILQ_FOREACH(entry, &mouse_handlers, node) { + if (entry->qemu_put_mouse_event_absolute) { + return 1; + } + } + + return 0; +} + +static void info_mice_iter(QObject *data, void *opaque) +{ + QDict *mouse; + Monitor *mon = opaque; + + mouse = qobject_to_qdict(data); + monitor_printf(mon, "%c Mouse #%" PRId64 ": %s%s\n", + (qdict_get_bool(mouse, "current") ? '*' : ' '), + qdict_get_int(mouse, "index"), qdict_get_str(mouse, "name"), + qdict_get_bool(mouse, "absolute") ? " (absolute)" : ""); +} + +void do_info_mice_print(Monitor *mon, const QObject *data) +{ + QList *mice_list; + + mice_list = qobject_to_qlist(data); + if (qlist_empty(mice_list)) { + monitor_printf(mon, "No mouse devices connected\n"); + return; + } + + qlist_iter(mice_list, info_mice_iter, mon); +} + +void do_info_mice(Monitor *mon, QObject **ret_data) +{ + QEMUPutMouseEntry *cursor; + QList *mice_list; + int current; + + mice_list = qlist_new(); + + if (QTAILQ_EMPTY(&mouse_handlers)) { + goto out; + } + + current = QTAILQ_FIRST(&mouse_handlers)->index; + + QTAILQ_FOREACH(cursor, &mouse_handlers, node) { + QObject *obj; + obj = qobject_from_jsonf("{ 'name': %s," + " 'index': %d," + " 'current': %i," + " 'absolute': %i }", + cursor->qemu_put_mouse_event_name, + cursor->index, + cursor->index == current, + !!cursor->qemu_put_mouse_event_absolute); + qlist_append_obj(mice_list, obj); + } + +out: + *ret_data = QOBJECT(mice_list); +} + +void do_mouse_set(Monitor *mon, const QDict *qdict) +{ + QEMUPutMouseEntry *cursor; + int index = qdict_get_int(qdict, "index"); + int found = 0; + + if (QTAILQ_EMPTY(&mouse_handlers)) { + monitor_printf(mon, "No mouse devices connected\n"); + return; + } + + QTAILQ_FOREACH(cursor, &mouse_handlers, node) { + if (cursor->index == index) { + found = 1; + qemu_activate_mouse_event_handler(cursor); + break; + } + } + + if (!found) { + monitor_printf(mon, "Mouse at given index not found\n"); + } + + check_mode_change(); +} + +void qemu_add_mouse_mode_change_notifier(Notifier *notify) +{ + notifier_list_add(&mouse_mode_notifiers, notify); +} + +void qemu_remove_mouse_mode_change_notifier(Notifier *notify) +{ + notifier_list_remove(&mouse_mode_notifiers, notify); +} diff -Nru qemu-kvm-0.12.5+noroms/ioport.c qemu-kvm-0.14.1/ioport.c --- qemu-kvm-0.12.5+noroms/ioport.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/ioport.c 2011-05-11 13:29:46.000000000 +0000 @@ -26,6 +26,7 @@ */ #include "ioport.h" +#include "trace.h" /***********************************************************/ /* IO Port */ @@ -173,6 +174,70 @@ return 0; } +static uint32_t ioport_readb_thunk(void *opaque, uint32_t addr) +{ + IORange *ioport = opaque; + uint64_t data; + + ioport->ops->read(ioport, addr - ioport->base, 1, &data); + return data; +} + +static uint32_t ioport_readw_thunk(void *opaque, uint32_t addr) +{ + IORange *ioport = opaque; + uint64_t data; + + ioport->ops->read(ioport, addr - ioport->base, 2, &data); + return data; +} + +static uint32_t ioport_readl_thunk(void *opaque, uint32_t addr) +{ + IORange *ioport = opaque; + uint64_t data; + + ioport->ops->read(ioport, addr - ioport->base, 4, &data); + return data; +} + +static void ioport_writeb_thunk(void *opaque, uint32_t addr, uint32_t data) +{ + IORange *ioport = opaque; + + ioport->ops->write(ioport, addr - ioport->base, 1, data); +} + +static void ioport_writew_thunk(void *opaque, uint32_t addr, uint32_t data) +{ + IORange *ioport = opaque; + + ioport->ops->write(ioport, addr - ioport->base, 2, data); +} + +static void ioport_writel_thunk(void *opaque, uint32_t addr, uint32_t data) +{ + IORange *ioport = opaque; + + ioport->ops->write(ioport, addr - ioport->base, 4, data); +} + +void ioport_register(IORange *ioport) +{ + register_ioport_read(ioport->base, ioport->len, 1, + ioport_readb_thunk, ioport); + register_ioport_read(ioport->base, ioport->len, 2, + ioport_readw_thunk, ioport); + register_ioport_read(ioport->base, ioport->len, 4, + ioport_readl_thunk, ioport); + register_ioport_write(ioport->base, ioport->len, 1, + ioport_writeb_thunk, ioport); + register_ioport_write(ioport->base, ioport->len, 2, + ioport_writew_thunk, ioport); + register_ioport_write(ioport->base, ioport->len, 4, + ioport_writel_thunk, ioport); +} + void isa_unassign_ioport(pio_addr_t start, int length) { int i; @@ -195,18 +260,21 @@ void cpu_outb(pio_addr_t addr, uint8_t val) { LOG_IOPORT("outb: %04"FMT_pioaddr" %02"PRIx8"\n", addr, val); + trace_cpu_out(addr, val); ioport_write(0, addr, val); } void cpu_outw(pio_addr_t addr, uint16_t val) { LOG_IOPORT("outw: %04"FMT_pioaddr" %04"PRIx16"\n", addr, val); + trace_cpu_out(addr, val); ioport_write(1, addr, val); } void cpu_outl(pio_addr_t addr, uint32_t val) { LOG_IOPORT("outl: %04"FMT_pioaddr" %08"PRIx32"\n", addr, val); + trace_cpu_out(addr, val); ioport_write(2, addr, val); } @@ -214,6 +282,7 @@ { uint8_t val; val = ioport_read(0, addr); + trace_cpu_in(addr, val); LOG_IOPORT("inb : %04"FMT_pioaddr" %02"PRIx8"\n", addr, val); return val; } @@ -222,6 +291,7 @@ { uint16_t val; val = ioport_read(1, addr); + trace_cpu_in(addr, val); LOG_IOPORT("inw : %04"FMT_pioaddr" %04"PRIx16"\n", addr, val); return val; } @@ -230,6 +300,7 @@ { uint32_t val; val = ioport_read(2, addr); + trace_cpu_in(addr, val); LOG_IOPORT("inl : %04"FMT_pioaddr" %08"PRIx32"\n", addr, val); return val; } diff -Nru qemu-kvm-0.12.5+noroms/ioport.h qemu-kvm-0.14.1/ioport.h --- qemu-kvm-0.12.5+noroms/ioport.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/ioport.h 2011-05-11 13:29:46.000000000 +0000 @@ -25,6 +25,7 @@ #define IOPORT_H #include "qemu-common.h" +#include "iorange.h" typedef uint32_t pio_addr_t; #define FMT_pioaddr PRIx32 @@ -36,6 +37,7 @@ typedef void (IOPortWriteFunc)(void *opaque, uint32_t address, uint32_t data); typedef uint32_t (IOPortReadFunc)(void *opaque, uint32_t address); +void ioport_register(IORange *iorange); int register_ioport_read(pio_addr_t start, int length, int size, IOPortReadFunc *func, void *opaque); int register_ioport_write(pio_addr_t start, int length, int size, diff -Nru qemu-kvm-0.12.5+noroms/iorange.h qemu-kvm-0.14.1/iorange.h --- qemu-kvm-0.12.5+noroms/iorange.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/iorange.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,30 @@ +#ifndef IORANGE_H +#define IORANGE_H + +#include + +typedef struct IORange IORange; +typedef struct IORangeOps IORangeOps; + +struct IORangeOps { + void (*read)(IORange *iorange, uint64_t offset, unsigned width, + uint64_t *data); + void (*write)(IORange *iorange, uint64_t offset, unsigned width, + uint64_t data); +}; + +struct IORange { + const IORangeOps *ops; + uint64_t base; + uint64_t len; +}; + +static inline void iorange_init(IORange *iorange, const IORangeOps *ops, + uint64_t base, uint64_t len) +{ + iorange->ops = ops; + iorange->base = base; + iorange->len = len; +} + +#endif diff -Nru qemu-kvm-0.12.5+noroms/iov.c qemu-kvm-0.14.1/iov.c --- qemu-kvm-0.12.5+noroms/iov.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/iov.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,70 @@ +/* + * Helpers for getting linearized buffers from iov / filling buffers into iovs + * + * Copyright IBM, Corp. 2007, 2008 + * Copyright (C) 2010 Red Hat, Inc. + * + * Author(s): + * Anthony Liguori + * Amit Shah + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + */ + +#include "iov.h" + +size_t iov_from_buf(struct iovec *iov, unsigned int iovcnt, + const void *buf, size_t size) +{ + size_t offset; + unsigned int i; + + offset = 0; + for (i = 0; offset < size && i < iovcnt; i++) { + size_t len; + + len = MIN(iov[i].iov_len, size - offset); + + memcpy(iov[i].iov_base, buf + offset, len); + offset += len; + } + return offset; +} + +size_t iov_to_buf(const struct iovec *iov, const unsigned int iovcnt, + void *buf, size_t offset, size_t size) +{ + uint8_t *ptr; + size_t iov_off, buf_off; + unsigned int i; + + ptr = buf; + iov_off = 0; + buf_off = 0; + for (i = 0; i < iovcnt && size; i++) { + if (offset < (iov_off + iov[i].iov_len)) { + size_t len = MIN((iov_off + iov[i].iov_len) - offset , size); + + memcpy(ptr + buf_off, iov[i].iov_base + (offset - iov_off), len); + + buf_off += len; + offset += len; + size -= len; + } + iov_off += iov[i].iov_len; + } + return buf_off; +} + +size_t iov_size(const struct iovec *iov, const unsigned int iovcnt) +{ + size_t len; + unsigned int i; + + len = 0; + for (i = 0; i < iovcnt; i++) { + len += iov[i].iov_len; + } + return len; +} diff -Nru qemu-kvm-0.12.5+noroms/iov.h qemu-kvm-0.14.1/iov.h --- qemu-kvm-0.12.5+noroms/iov.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/iov.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,19 @@ +/* + * Helpers for getting linearized buffers from iov / filling buffers into iovs + * + * Copyright (C) 2010 Red Hat, Inc. + * + * Author(s): + * Amit Shah + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + */ + +#include "qemu-common.h" + +size_t iov_from_buf(struct iovec *iov, unsigned int iovcnt, + const void *buf, size_t size); +size_t iov_to_buf(const struct iovec *iov, const unsigned int iovcnt, + void *buf, size_t offset, size_t size); +size_t iov_size(const struct iovec *iov, const unsigned int iovcnt); diff -Nru qemu-kvm-0.12.5+noroms/json-lexer.c qemu-kvm-0.14.1/json-lexer.c --- qemu-kvm-0.12.5+noroms/json-lexer.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/json-lexer.c 2011-05-11 13:29:46.000000000 +0000 @@ -29,7 +29,6 @@ enum json_lexer_state { ERROR = 0, - IN_DONE_STRING, IN_DQ_UCODE3, IN_DQ_UCODE2, IN_DQ_UCODE1, @@ -57,19 +56,19 @@ IN_ESCAPE_I, IN_ESCAPE_I6, IN_ESCAPE_I64, - IN_ESCAPE_DONE, IN_WHITESPACE, - IN_OPERATOR_DONE, IN_START, }; #define TERMINAL(state) [0 ... 0x7F] = (state) -static const uint8_t json_lexer[][256] = { - [IN_DONE_STRING] = { - TERMINAL(JSON_STRING), - }, +/* Return whether TERMINAL is a terminal state and the transition to it + from OLD_STATE required lookahead. This happens whenever the table + below uses the TERMINAL macro. */ +#define TERMINAL_NEEDED_LOOKAHEAD(old_state, terminal) \ + (json_lexer[(old_state)][0] == (terminal)) +static const uint8_t json_lexer[][256] = { /* double quote string */ [IN_DQ_UCODE3] = { ['0' ... '9'] = IN_DQ_STRING, @@ -97,6 +96,8 @@ ['n'] = IN_DQ_STRING, ['r'] = IN_DQ_STRING, ['t'] = IN_DQ_STRING, + ['/'] = IN_DQ_STRING, + ['\\'] = IN_DQ_STRING, ['\''] = IN_DQ_STRING, ['\"'] = IN_DQ_STRING, ['u'] = IN_DQ_UCODE0, @@ -104,7 +105,7 @@ [IN_DQ_STRING] = { [1 ... 0xFF] = IN_DQ_STRING, ['\\'] = IN_DQ_STRING_ESCAPE, - ['"'] = IN_DONE_STRING, + ['"'] = JSON_STRING, }, /* single quote string */ @@ -134,6 +135,8 @@ ['n'] = IN_SQ_STRING, ['r'] = IN_SQ_STRING, ['t'] = IN_SQ_STRING, + ['/'] = IN_DQ_STRING, + ['\\'] = IN_DQ_STRING, ['\''] = IN_SQ_STRING, ['\"'] = IN_SQ_STRING, ['u'] = IN_SQ_UCODE0, @@ -141,7 +144,7 @@ [IN_SQ_STRING] = { [1 ... 0xFF] = IN_SQ_STRING, ['\\'] = IN_SQ_STRING_ESCAPE, - ['\''] = IN_DONE_STRING, + ['\''] = JSON_STRING, }, /* Zero */ @@ -207,27 +210,18 @@ ['\n'] = IN_WHITESPACE, }, - /* operator */ - [IN_OPERATOR_DONE] = { - TERMINAL(JSON_OPERATOR), - }, - /* escape */ - [IN_ESCAPE_DONE] = { - TERMINAL(JSON_ESCAPE), - }, - [IN_ESCAPE_LL] = { - ['d'] = IN_ESCAPE_DONE, + ['d'] = JSON_ESCAPE, }, [IN_ESCAPE_L] = { - ['d'] = IN_ESCAPE_DONE, + ['d'] = JSON_ESCAPE, ['l'] = IN_ESCAPE_LL, }, [IN_ESCAPE_I64] = { - ['d'] = IN_ESCAPE_DONE, + ['d'] = JSON_ESCAPE, }, [IN_ESCAPE_I6] = { @@ -239,11 +233,11 @@ }, [IN_ESCAPE] = { - ['d'] = IN_ESCAPE_DONE, - ['i'] = IN_ESCAPE_DONE, - ['p'] = IN_ESCAPE_DONE, - ['s'] = IN_ESCAPE_DONE, - ['f'] = IN_ESCAPE_DONE, + ['d'] = JSON_ESCAPE, + ['i'] = JSON_ESCAPE, + ['p'] = JSON_ESCAPE, + ['s'] = JSON_ESCAPE, + ['f'] = JSON_ESCAPE, ['l'] = IN_ESCAPE_L, ['I'] = IN_ESCAPE_I, }, @@ -255,12 +249,12 @@ ['0'] = IN_ZERO, ['1' ... '9'] = IN_NONZERO_NUMBER, ['-'] = IN_NEG_NONZERO_NUMBER, - ['{'] = IN_OPERATOR_DONE, - ['}'] = IN_OPERATOR_DONE, - ['['] = IN_OPERATOR_DONE, - [']'] = IN_OPERATOR_DONE, - [','] = IN_OPERATOR_DONE, - [':'] = IN_OPERATOR_DONE, + ['{'] = JSON_OPERATOR, + ['}'] = JSON_OPERATOR, + ['['] = JSON_OPERATOR, + [']'] = JSON_OPERATOR, + [','] = JSON_OPERATOR, + [':'] = JSON_OPERATOR, ['a' ... 'z'] = IN_KEYWORD, ['%'] = IN_ESCAPE, [' '] = IN_WHITESPACE, @@ -275,11 +269,12 @@ lexer->emit = func; lexer->state = IN_START; lexer->token = qstring_new(); + lexer->x = lexer->y = 0; } static int json_lexer_feed_char(JSONLexer *lexer, char ch) { - char buf[2]; + int char_consumed, new_state; lexer->x++; if (ch == '\n') { @@ -287,32 +282,33 @@ lexer->y++; } - lexer->state = json_lexer[lexer->state][(uint8_t)ch]; - - switch (lexer->state) { - case JSON_OPERATOR: - case JSON_ESCAPE: - case JSON_INTEGER: - case JSON_FLOAT: - case JSON_KEYWORD: - case JSON_STRING: - lexer->emit(lexer, lexer->token, lexer->state, lexer->x, lexer->y); - case JSON_SKIP: - lexer->state = json_lexer[IN_START][(uint8_t)ch]; - QDECREF(lexer->token); - lexer->token = qstring_new(); - break; - case ERROR: - return -EINVAL; - default: - break; - } - - buf[0] = ch; - buf[1] = 0; - - qstring_append(lexer->token, buf); + do { + new_state = json_lexer[lexer->state][(uint8_t)ch]; + char_consumed = !TERMINAL_NEEDED_LOOKAHEAD(lexer->state, new_state); + if (char_consumed) { + qstring_append_chr(lexer->token, ch); + } + switch (new_state) { + case JSON_OPERATOR: + case JSON_ESCAPE: + case JSON_INTEGER: + case JSON_FLOAT: + case JSON_KEYWORD: + case JSON_STRING: + lexer->emit(lexer, lexer->token, new_state, lexer->x, lexer->y); + case JSON_SKIP: + QDECREF(lexer->token); + lexer->token = qstring_new(); + new_state = IN_START; + break; + case ERROR: + return -EINVAL; + default: + break; + } + lexer->state = new_state; + } while (!char_consumed); return 0; } @@ -334,7 +330,7 @@ int json_lexer_flush(JSONLexer *lexer) { - return json_lexer_feed_char(lexer, 0); + return lexer->state == IN_START ? 0 : json_lexer_feed_char(lexer, 0); } void json_lexer_destroy(JSONLexer *lexer) diff -Nru qemu-kvm-0.12.5+noroms/json-parser.c qemu-kvm-0.14.1/json-parser.c --- qemu-kvm-0.12.5+noroms/json-parser.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/json-parser.c 2011-05-11 13:29:46.000000000 +0000 @@ -11,7 +11,7 @@ * */ -#include +#include #include "qemu-common.h" #include "qstring.h" @@ -91,9 +91,15 @@ /** * Error handler */ -static void parse_error(JSONParserContext *ctxt, QObject *token, const char *msg, ...) +static void GCC_FMT_ATTR(3, 4) parse_error(JSONParserContext *ctxt, + QObject *token, const char *msg, ...) { - fprintf(stderr, "parse error: %s\n", msg); + va_list ap; + va_start(ap, msg); + fprintf(stderr, "parse error: "); + vfprintf(stderr, msg, ap); + fprintf(stderr, "\n"); + va_end(ap); } /** @@ -200,6 +206,10 @@ qstring_append(str, "\b"); ptr++; break; + case 'f': + qstring_append(str, "\f"); + ptr++; + break; case 'n': qstring_append(str, "\n"); ptr++; @@ -247,8 +257,6 @@ } } - ptr++; - return str; out: diff -Nru qemu-kvm-0.12.5+noroms/json-streamer.c qemu-kvm-0.14.1/json-streamer.c --- qemu-kvm-0.12.5+noroms/json-streamer.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/json-streamer.c 2011-05-11 13:29:46.000000000 +0000 @@ -43,11 +43,11 @@ } dict = qdict_new(); - qdict_put_obj(dict, "type", QOBJECT(qint_from_int(type))); + qdict_put(dict, "type", qint_from_int(type)); QINCREF(token); - qdict_put_obj(dict, "token", QOBJECT(token)); - qdict_put_obj(dict, "x", QOBJECT(qint_from_int(x))); - qdict_put_obj(dict, "y", QOBJECT(qint_from_int(y))); + qdict_put(dict, "token", token); + qdict_put(dict, "x", qint_from_int(x)); + qdict_put(dict, "y", qint_from_int(y)); qlist_append(parser->tokens, dict); diff -Nru qemu-kvm-0.12.5+noroms/keymaps.c qemu-kvm-0.14.1/keymaps.c --- qemu-kvm-0.12.5+noroms/keymaps.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/keymaps.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,192 +0,0 @@ -/* - * QEMU keysym to keycode conversion using rdesktop keymaps - * - * Copyright (c) 2004 Johannes Schindelin - * - * 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 "keymaps.h" -#include "sysemu.h" - -static int get_keysym(const name2keysym_t *table, - const char *name) -{ - const name2keysym_t *p; - for(p = table; p->name != NULL; p++) { - if (!strcmp(p->name, name)) - return p->keysym; - } - return 0; -} - - -static void add_to_key_range(struct key_range **krp, int code) { - struct key_range *kr; - for (kr = *krp; kr; kr = kr->next) { - if (code >= kr->start && code <= kr->end) - break; - if (code == kr->start - 1) { - kr->start--; - break; - } - if (code == kr->end + 1) { - kr->end++; - break; - } - } - if (kr == NULL) { - kr = qemu_mallocz(sizeof(*kr)); - kr->start = kr->end = code; - kr->next = *krp; - *krp = kr; - } -} - -static kbd_layout_t *parse_keyboard_layout(const name2keysym_t *table, - const char *language, - kbd_layout_t * k) -{ - FILE *f; - char * filename; - char line[1024]; - int len; - - filename = qemu_find_file(QEMU_FILE_TYPE_KEYMAP, language); - - if (!k) - k = qemu_mallocz(sizeof(kbd_layout_t)); - if (!(filename && (f = fopen(filename, "r")))) { - fprintf(stderr, - "Could not read keymap file: '%s'\n", language); - return NULL; - } - qemu_free(filename); - for(;;) { - if (fgets(line, 1024, f) == NULL) - break; - len = strlen(line); - if (len > 0 && line[len - 1] == '\n') - line[len - 1] = '\0'; - if (line[0] == '#') - continue; - if (!strncmp(line, "map ", 4)) - continue; - if (!strncmp(line, "include ", 8)) { - parse_keyboard_layout(table, line + 8, k); - } else { - char *end_of_keysym = line; - while (*end_of_keysym != 0 && *end_of_keysym != ' ') - end_of_keysym++; - if (*end_of_keysym) { - int keysym; - *end_of_keysym = 0; - keysym = get_keysym(table, line); - if (keysym == 0) { - // fprintf(stderr, "Warning: unknown keysym %s\n", line); - } else { - const char *rest = end_of_keysym + 1; - char *rest2; - int keycode = strtol(rest, &rest2, 0); - - if (rest && strstr(rest, "numlock")) { - add_to_key_range(&k->keypad_range, keycode); - add_to_key_range(&k->numlock_range, keysym); - //fprintf(stderr, "keypad keysym %04x keycode %d\n", keysym, keycode); - } - - /* if(keycode&0x80) - keycode=(keycode<<8)^0x80e0; */ - if (keysym < MAX_NORMAL_KEYCODE) { - //fprintf(stderr,"Setting keysym %s (%d) to %d\n",line,keysym,keycode); - k->keysym2keycode[keysym] = keycode; - } else { - if (k->extra_count >= MAX_EXTRA_COUNT) { - fprintf(stderr, - "Warning: Could not assign keysym %s (0x%x) because of memory constraints.\n", - line, keysym); - } else { -#if 0 - fprintf(stderr, "Setting %d: %d,%d\n", - k->extra_count, keysym, keycode); -#endif - k->keysym2keycode_extra[k->extra_count]. - keysym = keysym; - k->keysym2keycode_extra[k->extra_count]. - keycode = keycode; - k->extra_count++; - } - } - } - } - } - } - fclose(f); - return k; -} - - -void *init_keyboard_layout(const name2keysym_t *table, const char *language) -{ - return parse_keyboard_layout(table, language, NULL); -} - - -int keysym2scancode(void *kbd_layout, int keysym) -{ - kbd_layout_t *k = kbd_layout; - if (keysym < MAX_NORMAL_KEYCODE) { - if (k->keysym2keycode[keysym] == 0) - fprintf(stderr, "Warning: no scancode found for keysym %d\n", - keysym); - return k->keysym2keycode[keysym]; - } else { - int i; -#ifdef XK_ISO_Left_Tab - if (keysym == XK_ISO_Left_Tab) - keysym = XK_Tab; -#endif - for (i = 0; i < k->extra_count; i++) - if (k->keysym2keycode_extra[i].keysym == keysym) - return k->keysym2keycode_extra[i].keycode; - } - return 0; -} - -int keycode_is_keypad(void *kbd_layout, int keycode) -{ - kbd_layout_t *k = kbd_layout; - struct key_range *kr; - - for (kr = k->keypad_range; kr; kr = kr->next) - if (keycode >= kr->start && keycode <= kr->end) - return 1; - return 0; -} - -int keysym_is_numlock(void *kbd_layout, int keysym) -{ - kbd_layout_t *k = kbd_layout; - struct key_range *kr; - - for (kr = k->numlock_range; kr; kr = kr->next) - if (keysym >= kr->start && keysym <= kr->end) - return 1; - return 0; -} diff -Nru qemu-kvm-0.12.5+noroms/keymaps.h qemu-kvm-0.14.1/keymaps.h --- qemu-kvm-0.12.5+noroms/keymaps.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/keymaps.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,60 +0,0 @@ -/* - * QEMU keysym to keycode conversion using rdesktop keymaps - * - * Copyright (c) 2004 Johannes Schindelin - * - * 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. - */ - -#ifndef __QEMU_KEYMAPS_H__ -#define __QEMU_KEYMAPS_H__ - -#include "qemu-common.h" - -typedef struct { - const char* name; - int keysym; -} name2keysym_t; - -struct key_range { - int start; - int end; - struct key_range *next; -}; - -#define MAX_NORMAL_KEYCODE 512 -#define MAX_EXTRA_COUNT 256 -typedef struct { - uint16_t keysym2keycode[MAX_NORMAL_KEYCODE]; - struct { - int keysym; - uint16_t keycode; - } keysym2keycode_extra[MAX_EXTRA_COUNT]; - int extra_count; - struct key_range *keypad_range; - struct key_range *numlock_range; -} kbd_layout_t; - - -void *init_keyboard_layout(const name2keysym_t *table, const char *language); -int keysym2scancode(void *kbd_layout, int keysym); -int keycode_is_keypad(void *kbd_layout, int keycode); -int keysym_is_numlock(void *kbd_layout, int keysym); - -#endif /* __QEMU_KEYMAPS_H__ */ diff -Nru qemu-kvm-0.12.5+noroms/kvm/configure qemu-kvm-0.14.1/kvm/configure --- qemu-kvm-0.12.5+noroms/kvm/configure 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/configure 2011-05-11 13:29:46.000000000 +0000 @@ -38,14 +38,7 @@ --qemu-cflags=CFLAGS CFLAGS to add to qemu configuration --qemu-ldflags=LDFLAGS LDFLAGS to add to qemu configuration - Any additional option is given to qemu's configure verbatim; including: - EOF - cd qemu - ./configure --help | egrep "enable-|disable-" \ - | grep -v user | grep -v system | grep -v kqemu | grep -v kvm \ - | sed -e "s/^ / /g" \ - | sed -e"s/ enable/enable/g" | sed -e "s/ disable/disable/g" exit 1 } @@ -131,16 +124,6 @@ --arch="$arch" --processor="$processor" \ ${cross_prefix:+"--cross-prefix=$cross_prefix"}) -#configure qemu -(cd qemu; ./configure --target-list=$target_exec \ - --disable-kqemu \ - --extra-cflags="-I $PWD/../libkvm $qemu_cflags" \ - --extra-ldflags="-L $PWD/../libkvm $qemu_ldflags" \ - --kerneldir="$libkvm_kerneldir" \ - --prefix="$prefix" \ - ${cross_prefix:+"--cross-prefix=$cross_prefix"} \ - ${cross_prefix:+"--cpu=$arch"} "${qemu_opts[@]}" -) || usage cat < config.mak diff -Nru qemu-kvm-0.12.5+noroms/kvm/doxygen.conf qemu-kvm-0.14.1/kvm/doxygen.conf --- qemu-kvm-0.12.5+noroms/kvm/doxygen.conf 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/doxygen.conf 1970-01-01 00:00:00.000000000 +0000 @@ -1,1252 +0,0 @@ -# Doxyfile 1.5.1 - -# This file describes the settings to be used by the documentation system -# doxygen (www.doxygen.org) for a project -# -# All text after a hash (#) is considered a comment and will be ignored -# The format is: -# TAG = value [value, ...] -# For lists items can also be appended using: -# TAG += value [value, ...] -# Values that contain spaces should be placed between quotes (" ") - -#--------------------------------------------------------------------------- -# Project related configuration options -#--------------------------------------------------------------------------- - -# The PROJECT_NAME tag is a single word (or a sequence of words surrounded -# by quotes) that should identify the project. - -PROJECT_NAME = KVM - -# The PROJECT_NUMBER tag can be used to enter a project or revision number. -# This could be handy for archiving the generated documentation or -# if some version control system is used. - -PROJECT_NUMBER = Release 7 - -# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) -# base path where the generated documentation will be put. -# If a relative path is entered, it will be relative to the location -# where doxygen was started. If left blank the current directory will be used. - -OUTPUT_DIRECTORY = docs - -# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create -# 4096 sub-directories (in 2 levels) under the output directory of each output -# format and will distribute the generated files over these directories. -# Enabling this option can be useful when feeding doxygen a huge amount of -# source files, where putting all generated files in the same directory would -# otherwise cause performance problems for the file system. - -CREATE_SUBDIRS = NO - -# The OUTPUT_LANGUAGE tag is used to specify the language in which all -# documentation generated by doxygen is written. Doxygen will use this -# information to generate all constant output in the proper language. -# The default language is English, other supported languages are: -# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, -# Croatian, Czech, Danish, Dutch, Finnish, French, German, Greek, Hungarian, -# Italian, Japanese, Japanese-en (Japanese with English messages), Korean, -# Korean-en, Lithuanian, Norwegian, Polish, Portuguese, Romanian, Russian, -# Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian. - -OUTPUT_LANGUAGE = English - -# This tag can be used to specify the encoding used in the generated output. -# The encoding is not always determined by the language that is chosen, -# but also whether or not the output is meant for Windows or non-Windows users. -# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES -# forces the Windows encoding (this is the default for the Windows binary), -# whereas setting the tag to NO uses a Unix-style encoding (the default for -# all platforms other than Windows). - -USE_WINDOWS_ENCODING = NO - -# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will -# include brief member descriptions after the members that are listed in -# the file and class documentation (similar to JavaDoc). -# Set to NO to disable this. - -BRIEF_MEMBER_DESC = YES - -# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend -# the brief description of a member or function before the detailed description. -# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the -# brief descriptions will be completely suppressed. - -REPEAT_BRIEF = YES - -# This tag implements a quasi-intelligent brief description abbreviator -# that is used to form the text in various listings. Each string -# in this list, if found as the leading text of the brief description, will be -# stripped from the text and the result after processing the whole list, is -# used as the annotated text. Otherwise, the brief description is used as-is. -# If left blank, the following values are used ("$name" is automatically -# replaced with the name of the entity): "The $name class" "The $name widget" -# "The $name file" "is" "provides" "specifies" "contains" -# "represents" "a" "an" "the" - -ABBREVIATE_BRIEF = - -# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then -# Doxygen will generate a detailed section even if there is only a brief -# description. - -ALWAYS_DETAILED_SEC = NO - -# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all -# inherited members of a class in the documentation of that class as if those -# members were ordinary class members. Constructors, destructors and assignment -# operators of the base classes will not be shown. - -INLINE_INHERITED_MEMB = NO - -# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full -# path before files name in the file list and in the header files. If set -# to NO the shortest path that makes the file name unique will be used. - -FULL_PATH_NAMES = YES - -# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag -# can be used to strip a user-defined part of the path. Stripping is -# only done if one of the specified strings matches the left-hand part of -# the path. The tag can be used to show relative paths in the file list. -# If left blank the directory from which doxygen is run is used as the -# path to strip. - -STRIP_FROM_PATH = - -# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of -# the path mentioned in the documentation of a class, which tells -# the reader which header file to include in order to use a class. -# If left blank only the name of the header file containing the class -# definition is used. Otherwise one should specify the include paths that -# are normally passed to the compiler using the -I flag. - -STRIP_FROM_INC_PATH = - -# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter -# (but less readable) file names. This can be useful is your file systems -# doesn't support long names like on DOS, Mac, or CD-ROM. - -SHORT_NAMES = NO - -# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen -# will interpret the first line (until the first dot) of a JavaDoc-style -# comment as the brief description. If set to NO, the JavaDoc -# comments will behave just like the Qt-style comments (thus requiring an -# explicit @brief command for a brief description. - -JAVADOC_AUTOBRIEF = NO - -# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen -# treat a multi-line C++ special comment block (i.e. a block of //! or /// -# comments) as a brief description. This used to be the default behaviour. -# The new default is to treat a multi-line C++ comment block as a detailed -# description. Set this tag to YES if you prefer the old behaviour instead. - -MULTILINE_CPP_IS_BRIEF = NO - -# If the DETAILS_AT_TOP tag is set to YES then Doxygen -# will output the detailed description near the top, like JavaDoc. -# If set to NO, the detailed description appears after the member -# documentation. - -DETAILS_AT_TOP = YES - -# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented -# member inherits the documentation from any documented member that it -# re-implements. - -INHERIT_DOCS = YES - -# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce -# a new page for each member. If set to NO, the documentation of a member will -# be part of the file/class/namespace that contains it. - -SEPARATE_MEMBER_PAGES = NO - -# The TAB_SIZE tag can be used to set the number of spaces in a tab. -# Doxygen uses this value to replace tabs by spaces in code fragments. - -TAB_SIZE = 8 - -# This tag can be used to specify a number of aliases that acts -# as commands in the documentation. An alias has the form "name=value". -# For example adding "sideeffect=\par Side Effects:\n" will allow you to -# put the command \sideeffect (or @sideeffect) in the documentation, which -# will result in a user-defined paragraph with heading "Side Effects:". -# You can put \n's in the value part of an alias to insert newlines. - -ALIASES = - -# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C -# sources only. Doxygen will then generate output that is more tailored for C. -# For instance, some of the names that are used will be different. The list -# of all members will be omitted, etc. - -OPTIMIZE_OUTPUT_FOR_C = YES - -# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java -# sources only. Doxygen will then generate output that is more tailored for Java. -# For instance, namespaces will be presented as packages, qualified scopes -# will look different, etc. - -OPTIMIZE_OUTPUT_JAVA = NO - -# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want to -# include (a tag file for) the STL sources as input, then you should -# set this tag to YES in order to let doxygen match functions declarations and -# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. -# func(std::string) {}). This also make the inheritance and collaboration -# diagrams that involve STL classes more complete and accurate. - -BUILTIN_STL_SUPPORT = NO - -# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC -# tag is set to YES, then doxygen will reuse the documentation of the first -# member in the group (if any) for the other members of the group. By default -# all members of a group must be documented explicitly. - -DISTRIBUTE_GROUP_DOC = NO - -# Set the SUBGROUPING tag to YES (the default) to allow class member groups of -# the same type (for instance a group of public functions) to be put as a -# subgroup of that type (e.g. under the Public Functions section). Set it to -# NO to prevent subgrouping. Alternatively, this can be done per class using -# the \nosubgrouping command. - -SUBGROUPING = YES - -#--------------------------------------------------------------------------- -# Build related configuration options -#--------------------------------------------------------------------------- - -# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in -# documentation are documented, even if no documentation was available. -# Private class members and static file members will be hidden unless -# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES - -EXTRACT_ALL = NO - -# If the EXTRACT_PRIVATE tag is set to YES all private members of a class -# will be included in the documentation. - -EXTRACT_PRIVATE = NO - -# If the EXTRACT_STATIC tag is set to YES all static members of a file -# will be included in the documentation. - -EXTRACT_STATIC = NO - -# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) -# defined locally in source files will be included in the documentation. -# If set to NO only classes defined in header files are included. - -EXTRACT_LOCAL_CLASSES = YES - -# This flag is only useful for Objective-C code. When set to YES local -# methods, which are defined in the implementation section but not in -# the interface are included in the documentation. -# If set to NO (the default) only methods in the interface are included. - -EXTRACT_LOCAL_METHODS = NO - -# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all -# undocumented members of documented classes, files or namespaces. -# If set to NO (the default) these members will be included in the -# various overviews, but no documentation section is generated. -# This option has no effect if EXTRACT_ALL is enabled. - -HIDE_UNDOC_MEMBERS = NO - -# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all -# undocumented classes that are normally visible in the class hierarchy. -# If set to NO (the default) these classes will be included in the various -# overviews. This option has no effect if EXTRACT_ALL is enabled. - -HIDE_UNDOC_CLASSES = NO - -# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all -# friend (class|struct|union) declarations. -# If set to NO (the default) these declarations will be included in the -# documentation. - -HIDE_FRIEND_COMPOUNDS = NO - -# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any -# documentation blocks found inside the body of a function. -# If set to NO (the default) these blocks will be appended to the -# function's detailed documentation block. - -HIDE_IN_BODY_DOCS = NO - -# The INTERNAL_DOCS tag determines if documentation -# that is typed after a \internal command is included. If the tag is set -# to NO (the default) then the documentation will be excluded. -# Set it to YES to include the internal documentation. - -INTERNAL_DOCS = NO - -# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate -# file names in lower-case letters. If set to YES upper-case letters are also -# allowed. This is useful if you have classes or files whose names only differ -# in case and if your file system supports case sensitive file names. Windows -# and Mac users are advised to set this option to NO. - -CASE_SENSE_NAMES = YES - -# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen -# will show members with their full class and namespace scopes in the -# documentation. If set to YES the scope will be hidden. - -HIDE_SCOPE_NAMES = NO - -# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen -# will put a list of the files that are included by a file in the documentation -# of that file. - -SHOW_INCLUDE_FILES = YES - -# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] -# is inserted in the documentation for inline members. - -INLINE_INFO = YES - -# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen -# will sort the (detailed) documentation of file and class members -# alphabetically by member name. If set to NO the members will appear in -# declaration order. - -SORT_MEMBER_DOCS = YES - -# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the -# brief documentation of file, namespace and class members alphabetically -# by member name. If set to NO (the default) the members will appear in -# declaration order. - -SORT_BRIEF_DOCS = NO - -# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be -# sorted by fully-qualified names, including namespaces. If set to -# NO (the default), the class list will be sorted only by class name, -# not including the namespace part. -# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. -# Note: This option applies only to the class list, not to the -# alphabetical list. - -SORT_BY_SCOPE_NAME = NO - -# The GENERATE_TODOLIST tag can be used to enable (YES) or -# disable (NO) the todo list. This list is created by putting \todo -# commands in the documentation. - -GENERATE_TODOLIST = YES - -# The GENERATE_TESTLIST tag can be used to enable (YES) or -# disable (NO) the test list. This list is created by putting \test -# commands in the documentation. - -GENERATE_TESTLIST = YES - -# The GENERATE_BUGLIST tag can be used to enable (YES) or -# disable (NO) the bug list. This list is created by putting \bug -# commands in the documentation. - -GENERATE_BUGLIST = YES - -# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or -# disable (NO) the deprecated list. This list is created by putting -# \deprecated commands in the documentation. - -GENERATE_DEPRECATEDLIST= YES - -# The ENABLED_SECTIONS tag can be used to enable conditional -# documentation sections, marked by \if sectionname ... \endif. - -ENABLED_SECTIONS = - -# The MAX_INITIALIZER_LINES tag determines the maximum number of lines -# the initial value of a variable or define consists of for it to appear in -# the documentation. If the initializer consists of more lines than specified -# here it will be hidden. Use a value of 0 to hide initializers completely. -# The appearance of the initializer of individual variables and defines in the -# documentation can be controlled using \showinitializer or \hideinitializer -# command in the documentation regardless of this setting. - -MAX_INITIALIZER_LINES = 30 - -# Set the SHOW_USED_FILES tag to NO to disable the list of files generated -# at the bottom of the documentation of classes and structs. If set to YES the -# list will mention the files that were used to generate the documentation. - -SHOW_USED_FILES = YES - -# If the sources in your project are distributed over multiple directories -# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy -# in the documentation. The default is NO. - -SHOW_DIRECTORIES = NO - -# The FILE_VERSION_FILTER tag can be used to specify a program or script that -# doxygen should invoke to get the current version for each file (typically from the -# version control system). Doxygen will invoke the program by executing (via -# popen()) the command , where is the value of -# the FILE_VERSION_FILTER tag, and is the name of an input file -# provided by doxygen. Whatever the program writes to standard output -# is used as the file version. See the manual for examples. - -FILE_VERSION_FILTER = - -#--------------------------------------------------------------------------- -# configuration options related to warning and progress messages -#--------------------------------------------------------------------------- - -# The QUIET tag can be used to turn on/off the messages that are generated -# by doxygen. Possible values are YES and NO. If left blank NO is used. - -QUIET = YES - -# The WARNINGS tag can be used to turn on/off the warning messages that are -# generated by doxygen. Possible values are YES and NO. If left blank -# NO is used. - -WARNINGS = NO - -# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings -# for undocumented members. If EXTRACT_ALL is set to YES then this flag will -# automatically be disabled. - -WARN_IF_UNDOCUMENTED = NO - -# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as not documenting some -# parameters in a documented function, or documenting parameters that -# don't exist or using markup commands wrongly. - -WARN_IF_DOC_ERROR = YES - -# This WARN_NO_PARAMDOC option can be abled to get warnings for -# functions that are documented, but have no documentation for their parameters -# or return value. If set to NO (the default) doxygen will only warn about -# wrong or incomplete parameter documentation, but not about the absence of -# documentation. - -WARN_NO_PARAMDOC = NO - -# The WARN_FORMAT tag determines the format of the warning messages that -# doxygen can produce. The string should contain the $file, $line, and $text -# tags, which will be replaced by the file and line number from which the -# warning originated and the warning text. Optionally the format may contain -# $version, which will be replaced by the version of the file (if it could -# be obtained via FILE_VERSION_FILTER) - -WARN_FORMAT = "$file:$line: $text" - -# The WARN_LOGFILE tag can be used to specify a file to which warning -# and error messages should be written. If left blank the output is written -# to stderr. - -WARN_LOGFILE = - -#--------------------------------------------------------------------------- -# configuration options related to the input files -#--------------------------------------------------------------------------- - -# The INPUT tag can be used to specify the files and/or directories that contain -# documented source files. You may enter file names like "myfile.cpp" or -# directories like "/usr/src/myproject". Separate the files or directories -# with spaces. - -INPUT = user/ kernel/ - -# If the value of the INPUT tag contains directories, you can use the -# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank the following patterns are tested: -# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx -# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py - -FILE_PATTERNS = - -# The RECURSIVE tag can be used to turn specify whether or not subdirectories -# should be searched for input files as well. Possible values are YES and NO. -# If left blank NO is used. - -RECURSIVE = YES - -# The EXCLUDE tag can be used to specify files and/or directories that should -# excluded from the INPUT source files. This way you can easily exclude a -# subdirectory from a directory tree whose root is specified with the INPUT tag. - -EXCLUDE = - -# The EXCLUDE_SYMLINKS tag can be used select whether or not files or -# directories that are symbolic links (a Unix filesystem feature) are excluded -# from the input. - -EXCLUDE_SYMLINKS = NO - -# If the value of the INPUT tag contains directories, you can use the -# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude -# certain files from those directories. Note that the wildcards are matched -# against the file with absolute path, so to exclude all test directories -# for example use the pattern */test/* - -EXCLUDE_PATTERNS = - -# The EXAMPLE_PATH tag can be used to specify one or more files or -# directories that contain example code fragments that are included (see -# the \include command). - -EXAMPLE_PATH = - -# If the value of the EXAMPLE_PATH tag contains directories, you can use the -# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank all files are included. - -EXAMPLE_PATTERNS = - -# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be -# searched for input files to be used with the \include or \dontinclude -# commands irrespective of the value of the RECURSIVE tag. -# Possible values are YES and NO. If left blank NO is used. - -EXAMPLE_RECURSIVE = NO - -# The IMAGE_PATH tag can be used to specify one or more files or -# directories that contain image that are included in the documentation (see -# the \image command). - -IMAGE_PATH = - -# The INPUT_FILTER tag can be used to specify a program that doxygen should -# invoke to filter for each input file. Doxygen will invoke the filter program -# by executing (via popen()) the command , where -# is the value of the INPUT_FILTER tag, and is the name of an -# input file. Doxygen will then use the output that the filter program writes -# to standard output. If FILTER_PATTERNS is specified, this tag will be -# ignored. - -INPUT_FILTER = - -# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern -# basis. Doxygen will compare the file name with each pattern and apply the -# filter if there is a match. The filters are a list of the form: -# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further -# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER -# is applied to all files. - -FILTER_PATTERNS = - -# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using -# INPUT_FILTER) will be used to filter the input files when producing source -# files to browse (i.e. when SOURCE_BROWSER is set to YES). - -FILTER_SOURCE_FILES = NO - -#--------------------------------------------------------------------------- -# configuration options related to source browsing -#--------------------------------------------------------------------------- - -# If the SOURCE_BROWSER tag is set to YES then a list of source files will -# be generated. Documented entities will be cross-referenced with these sources. -# Note: To get rid of all source code in the generated output, make sure also -# VERBATIM_HEADERS is set to NO. - -SOURCE_BROWSER = NO - -# Setting the INLINE_SOURCES tag to YES will include the body -# of functions and classes directly in the documentation. - -INLINE_SOURCES = NO - -# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct -# doxygen to hide any special comment blocks from generated source code -# fragments. Normal C and C++ comments will always remain visible. - -STRIP_CODE_COMMENTS = YES - -# If the REFERENCED_BY_RELATION tag is set to YES (the default) -# then for each documented function all documented -# functions referencing it will be listed. - -REFERENCED_BY_RELATION = YES - -# If the REFERENCES_RELATION tag is set to YES (the default) -# then for each documented function all documented entities -# called/used by that function will be listed. - -REFERENCES_RELATION = YES - -# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) -# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from -# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will -# link to the source code. Otherwise they will link to the documentstion. - -REFERENCES_LINK_SOURCE = NO - -# If the USE_HTAGS tag is set to YES then the references to source code -# will point to the HTML generated by the htags(1) tool instead of doxygen -# built-in source browser. The htags tool is part of GNU's global source -# tagging system (see http://www.gnu.org/software/global/global.html). You -# will need version 4.8.6 or higher. - -USE_HTAGS = NO - -# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen -# will generate a verbatim copy of the header file for each class for -# which an include is specified. Set to NO to disable this. - -VERBATIM_HEADERS = NO - -#--------------------------------------------------------------------------- -# configuration options related to the alphabetical class index -#--------------------------------------------------------------------------- - -# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index -# of all compounds will be generated. Enable this if the project -# contains a lot of classes, structs, unions or interfaces. - -ALPHABETICAL_INDEX = NO - -# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then -# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns -# in which this list will be split (can be a number in the range [1..20]) - -COLS_IN_ALPHA_INDEX = 5 - -# In case all classes in a project start with a common prefix, all -# classes will be put under the same header in the alphabetical index. -# The IGNORE_PREFIX tag can be used to specify one or more prefixes that -# should be ignored while generating the index headers. - -IGNORE_PREFIX = - -#--------------------------------------------------------------------------- -# configuration options related to the HTML output -#--------------------------------------------------------------------------- - -# If the GENERATE_HTML tag is set to YES (the default) Doxygen will -# generate HTML output. - -GENERATE_HTML = YES - -# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `html' will be used as the default path. - -HTML_OUTPUT = html - -# The HTML_FILE_EXTENSION tag can be used to specify the file extension for -# each generated HTML page (for example: .htm,.php,.asp). If it is left blank -# doxygen will generate files with .html extension. - -HTML_FILE_EXTENSION = .html - -# The HTML_HEADER tag can be used to specify a personal HTML header for -# each generated HTML page. If it is left blank doxygen will generate a -# standard header. - -HTML_HEADER = - -# The HTML_FOOTER tag can be used to specify a personal HTML footer for -# each generated HTML page. If it is left blank doxygen will generate a -# standard footer. - -HTML_FOOTER = - -# The HTML_STYLESHEET tag can be used to specify a user-defined cascading -# style sheet that is used by each HTML page. It can be used to -# fine-tune the look of the HTML output. If the tag is left blank doxygen -# will generate a default style sheet. Note that doxygen will try to copy -# the style sheet file to the HTML output directory, so don't put your own -# stylesheet in the HTML output directory as well, or it will be erased! - -HTML_STYLESHEET = - -# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, -# files or namespaces will be aligned in HTML using tables. If set to -# NO a bullet list will be used. - -HTML_ALIGN_MEMBERS = YES - -# If the GENERATE_HTMLHELP tag is set to YES, additional index files -# will be generated that can be used as input for tools like the -# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) -# of the generated HTML documentation. - -GENERATE_HTMLHELP = NO - -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can -# be used to specify the file name of the resulting .chm file. You -# can add a path in front of the file if the result should not be -# written to the html output directory. - -CHM_FILE = - -# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can -# be used to specify the location (absolute path including file name) of -# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run -# the HTML help compiler on the generated index.hhp. - -HHC_LOCATION = - -# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag -# controls if a separate .chi index file is generated (YES) or that -# it should be included in the master .chm file (NO). - -GENERATE_CHI = NO - -# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag -# controls whether a binary table of contents is generated (YES) or a -# normal table of contents (NO) in the .chm file. - -BINARY_TOC = NO - -# The TOC_EXPAND flag can be set to YES to add extra items for group members -# to the contents of the HTML help documentation and to the tree view. - -TOC_EXPAND = NO - -# The DISABLE_INDEX tag can be used to turn on/off the condensed index at -# top of each HTML page. The value NO (the default) enables the index and -# the value YES disables it. - -DISABLE_INDEX = NO - -# This tag can be used to set the number of enum values (range [1..20]) -# that doxygen will group on one line in the generated HTML documentation. - -ENUM_VALUES_PER_LINE = 4 - -# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be -# generated containing a tree-like index structure (just like the one that -# is generated for HTML Help). For this to work a browser that supports -# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, -# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are -# probably better off using the HTML help feature. - -GENERATE_TREEVIEW = NO - -# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be -# used to set the initial width (in pixels) of the frame in which the tree -# is shown. - -TREEVIEW_WIDTH = 250 - -#--------------------------------------------------------------------------- -# configuration options related to the LaTeX output -#--------------------------------------------------------------------------- - -# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will -# generate Latex output. - -GENERATE_LATEX = NO - -# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `latex' will be used as the default path. - -LATEX_OUTPUT = latex - -# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be -# invoked. If left blank `latex' will be used as the default command name. - -LATEX_CMD_NAME = latex - -# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to -# generate index for LaTeX. If left blank `makeindex' will be used as the -# default command name. - -MAKEINDEX_CMD_NAME = makeindex - -# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact -# LaTeX documents. This may be useful for small projects and may help to -# save some trees in general. - -COMPACT_LATEX = NO - -# The PAPER_TYPE tag can be used to set the paper type that is used -# by the printer. Possible values are: a4, a4wide, letter, legal and -# executive. If left blank a4wide will be used. - -PAPER_TYPE = a4wide - -# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX -# packages that should be included in the LaTeX output. - -EXTRA_PACKAGES = - -# The LATEX_HEADER tag can be used to specify a personal LaTeX header for -# the generated latex document. The header should contain everything until -# the first chapter. If it is left blank doxygen will generate a -# standard header. Notice: only use this tag if you know what you are doing! - -LATEX_HEADER = - -# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated -# is prepared for conversion to pdf (using ps2pdf). The pdf file will -# contain links (just like the HTML output) instead of page references -# This makes the output suitable for online browsing using a pdf viewer. - -PDF_HYPERLINKS = NO - -# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of -# plain latex in the generated Makefile. Set this option to YES to get a -# higher quality PDF documentation. - -USE_PDFLATEX = NO - -# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. -# command to the generated LaTeX files. This will instruct LaTeX to keep -# running if errors occur, instead of asking the user for help. -# This option is also used when generating formulas in HTML. - -LATEX_BATCHMODE = NO - -# If LATEX_HIDE_INDICES is set to YES then doxygen will not -# include the index chapters (such as File Index, Compound Index, etc.) -# in the output. - -LATEX_HIDE_INDICES = NO - -#--------------------------------------------------------------------------- -# configuration options related to the RTF output -#--------------------------------------------------------------------------- - -# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output -# The RTF output is optimized for Word 97 and may not look very pretty with -# other RTF readers or editors. - -GENERATE_RTF = NO - -# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `rtf' will be used as the default path. - -RTF_OUTPUT = rtf - -# If the COMPACT_RTF tag is set to YES Doxygen generates more compact -# RTF documents. This may be useful for small projects and may help to -# save some trees in general. - -COMPACT_RTF = NO - -# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated -# will contain hyperlink fields. The RTF file will -# contain links (just like the HTML output) instead of page references. -# This makes the output suitable for online browsing using WORD or other -# programs which support those fields. -# Note: wordpad (write) and others do not support links. - -RTF_HYPERLINKS = NO - -# Load stylesheet definitions from file. Syntax is similar to doxygen's -# config file, i.e. a series of assignments. You only have to provide -# replacements, missing definitions are set to their default value. - -RTF_STYLESHEET_FILE = - -# Set optional variables used in the generation of an rtf document. -# Syntax is similar to doxygen's config file. - -RTF_EXTENSIONS_FILE = - -#--------------------------------------------------------------------------- -# configuration options related to the man page output -#--------------------------------------------------------------------------- - -# If the GENERATE_MAN tag is set to YES (the default) Doxygen will -# generate man pages - -GENERATE_MAN = NO - -# The MAN_OUTPUT tag is used to specify where the man pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `man' will be used as the default path. - -MAN_OUTPUT = man - -# The MAN_EXTENSION tag determines the extension that is added to -# the generated man pages (default is the subroutine's section .3) - -MAN_EXTENSION = .3 - -# If the MAN_LINKS tag is set to YES and Doxygen generates man output, -# then it will generate one additional man file for each entity -# documented in the real man page(s). These additional files -# only source the real man page, but without them the man command -# would be unable to find the correct page. The default is NO. - -MAN_LINKS = NO - -#--------------------------------------------------------------------------- -# configuration options related to the XML output -#--------------------------------------------------------------------------- - -# If the GENERATE_XML tag is set to YES Doxygen will -# generate an XML file that captures the structure of -# the code including all documentation. - -GENERATE_XML = NO - -# The XML_OUTPUT tag is used to specify where the XML pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `xml' will be used as the default path. - -XML_OUTPUT = xml - -# The XML_SCHEMA tag can be used to specify an XML schema, -# which can be used by a validating XML parser to check the -# syntax of the XML files. - -XML_SCHEMA = - -# The XML_DTD tag can be used to specify an XML DTD, -# which can be used by a validating XML parser to check the -# syntax of the XML files. - -XML_DTD = - -# If the XML_PROGRAMLISTING tag is set to YES Doxygen will -# dump the program listings (including syntax highlighting -# and cross-referencing information) to the XML output. Note that -# enabling this will significantly increase the size of the XML output. - -XML_PROGRAMLISTING = YES - -#--------------------------------------------------------------------------- -# configuration options for the AutoGen Definitions output -#--------------------------------------------------------------------------- - -# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will -# generate an AutoGen Definitions (see autogen.sf.net) file -# that captures the structure of the code including all -# documentation. Note that this feature is still experimental -# and incomplete at the moment. - -GENERATE_AUTOGEN_DEF = NO - -#--------------------------------------------------------------------------- -# configuration options related to the Perl module output -#--------------------------------------------------------------------------- - -# If the GENERATE_PERLMOD tag is set to YES Doxygen will -# generate a Perl module file that captures the structure of -# the code including all documentation. Note that this -# feature is still experimental and incomplete at the -# moment. - -GENERATE_PERLMOD = NO - -# If the PERLMOD_LATEX tag is set to YES Doxygen will generate -# the necessary Makefile rules, Perl scripts and LaTeX code to be able -# to generate PDF and DVI output from the Perl module output. - -PERLMOD_LATEX = NO - -# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be -# nicely formatted so it can be parsed by a human reader. This is useful -# if you want to understand what is going on. On the other hand, if this -# tag is set to NO the size of the Perl module output will be much smaller -# and Perl will parse it just the same. - -PERLMOD_PRETTY = YES - -# The names of the make variables in the generated doxyrules.make file -# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. -# This is useful so different doxyrules.make files included by the same -# Makefile don't overwrite each other's variables. - -PERLMOD_MAKEVAR_PREFIX = - -#--------------------------------------------------------------------------- -# Configuration options related to the preprocessor -#--------------------------------------------------------------------------- - -# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will -# evaluate all C-preprocessor directives found in the sources and include -# files. - -ENABLE_PREPROCESSING = YES - -# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro -# names in the source code. If set to NO (the default) only conditional -# compilation will be performed. Macro expansion can be done in a controlled -# way by setting EXPAND_ONLY_PREDEF to YES. - -MACRO_EXPANSION = NO - -# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES -# then the macro expansion is limited to the macros specified with the -# PREDEFINED and EXPAND_AS_DEFINED tags. - -EXPAND_ONLY_PREDEF = NO - -# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files -# in the INCLUDE_PATH (see below) will be search if a #include is found. - -SEARCH_INCLUDES = YES - -# The INCLUDE_PATH tag can be used to specify one or more directories that -# contain include files that are not input files but should be processed by -# the preprocessor. - -INCLUDE_PATH = - -# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard -# patterns (like *.h and *.hpp) to filter out the header-files in the -# directories. If left blank, the patterns specified with FILE_PATTERNS will -# be used. - -INCLUDE_FILE_PATTERNS = - -# The PREDEFINED tag can be used to specify one or more macro names that -# are defined before the preprocessor is started (similar to the -D option of -# gcc). The argument of the tag is a list of macros of the form: name -# or name=definition (no spaces). If the definition and the = are -# omitted =1 is assumed. To prevent a macro definition from being -# undefined via #undef or recursively expanded use the := operator -# instead of the = operator. - -PREDEFINED = - -# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then -# this tag can be used to specify a list of macro names that should be expanded. -# The macro definition that is found in the sources will be used. -# Use the PREDEFINED tag if you want to use a different macro definition. - -EXPAND_AS_DEFINED = - -# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then -# doxygen's preprocessor will remove all function-like macros that are alone -# on a line, have an all uppercase name, and do not end with a semicolon. Such -# function macros are typically used for boiler-plate code, and will confuse -# the parser if not removed. - -SKIP_FUNCTION_MACROS = YES - -#--------------------------------------------------------------------------- -# Configuration::additions related to external references -#--------------------------------------------------------------------------- - -# The TAGFILES option can be used to specify one or more tagfiles. -# Optionally an initial location of the external documentation -# can be added for each tagfile. The format of a tag file without -# this location is as follows: -# TAGFILES = file1 file2 ... -# Adding location for the tag files is done as follows: -# TAGFILES = file1=loc1 "file2 = loc2" ... -# where "loc1" and "loc2" can be relative or absolute paths or -# URLs. If a location is present for each tag, the installdox tool -# does not have to be run to correct the links. -# Note that each tag file must have a unique name -# (where the name does NOT include the path) -# If a tag file is not located in the directory in which doxygen -# is run, you must also specify the path to the tagfile here. - -TAGFILES = - -# When a file name is specified after GENERATE_TAGFILE, doxygen will create -# a tag file that is based on the input files it reads. - -GENERATE_TAGFILE = - -# If the ALLEXTERNALS tag is set to YES all external classes will be listed -# in the class index. If set to NO only the inherited external classes -# will be listed. - -ALLEXTERNALS = NO - -# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed -# in the modules index. If set to NO, only the current project's groups will -# be listed. - -EXTERNAL_GROUPS = YES - -# The PERL_PATH should be the absolute path and name of the perl script -# interpreter (i.e. the result of `which perl'). - -PERL_PATH = /usr/bin/perl - -#--------------------------------------------------------------------------- -# Configuration options related to the dot tool -#--------------------------------------------------------------------------- - -# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will -# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base -# or super classes. Setting the tag to NO turns the diagrams off. Note that -# this option is superseded by the HAVE_DOT option below. This is only a -# fallback. It is recommended to install and use dot, since it yields more -# powerful graphs. - -CLASS_DIAGRAMS = YES - -# If set to YES, the inheritance and collaboration graphs will hide -# inheritance and usage relations if the target is undocumented -# or is not a class. - -HIDE_UNDOC_RELATIONS = YES - -# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is -# available from the path. This tool is part of Graphviz, a graph visualization -# toolkit from AT&T and Lucent Bell Labs. The other options in this section -# have no effect if this option is set to NO (the default) - -HAVE_DOT = NO - -# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for each documented class showing the direct and -# indirect inheritance relations. Setting this tag to YES will force the -# the CLASS_DIAGRAMS tag to NO. - -CLASS_GRAPH = YES - -# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for each documented class showing the direct and -# indirect implementation dependencies (inheritance, containment, and -# class references variables) of the class with other documented classes. - -COLLABORATION_GRAPH = YES - -# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for groups, showing the direct groups dependencies - -GROUP_GRAPHS = YES - -# If the UML_LOOK tag is set to YES doxygen will generate inheritance and -# collaboration diagrams in a style similar to the OMG's Unified Modeling -# Language. - -UML_LOOK = NO - -# If set to YES, the inheritance and collaboration graphs will show the -# relations between templates and their instances. - -TEMPLATE_RELATIONS = NO - -# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT -# tags are set to YES then doxygen will generate a graph for each documented -# file showing the direct and indirect include dependencies of the file with -# other documented files. - -INCLUDE_GRAPH = YES - -# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and -# HAVE_DOT tags are set to YES then doxygen will generate a graph for each -# documented header file showing the documented files that directly or -# indirectly include this file. - -INCLUDED_BY_GRAPH = YES - -# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will -# generate a call dependency graph for every global function or class method. -# Note that enabling this option will significantly increase the time of a run. -# So in most cases it will be better to enable call graphs for selected -# functions only using the \callgraph command. - -CALL_GRAPH = NO - -# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then doxygen will -# generate a caller dependency graph for every global function or class method. -# Note that enabling this option will significantly increase the time of a run. -# So in most cases it will be better to enable caller graphs for selected -# functions only using the \callergraph command. - -CALLER_GRAPH = NO - -# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen -# will graphical hierarchy of all classes instead of a textual one. - -GRAPHICAL_HIERARCHY = YES - -# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES -# then doxygen will show the dependencies a directory has on other directories -# in a graphical way. The dependency relations are determined by the #include -# relations between the files in the directories. - -DIRECTORY_GRAPH = YES - -# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images -# generated by dot. Possible values are png, jpg, or gif -# If left blank png will be used. - -DOT_IMAGE_FORMAT = png - -# The tag DOT_PATH can be used to specify the path where the dot tool can be -# found. If left blank, it is assumed the dot tool can be found in the path. - -DOT_PATH = - -# The DOTFILE_DIRS tag can be used to specify one or more directories that -# contain dot files that are included in the documentation (see the -# \dotfile command). - -DOTFILE_DIRS = - -# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width -# (in pixels) of the graphs generated by dot. If a graph becomes larger than -# this value, doxygen will try to truncate the graph, so that it fits within -# the specified constraint. Beware that most browsers cannot cope with very -# large images. - -MAX_DOT_GRAPH_WIDTH = 1024 - -# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height -# (in pixels) of the graphs generated by dot. If a graph becomes larger than -# this value, doxygen will try to truncate the graph, so that it fits within -# the specified constraint. Beware that most browsers cannot cope with very -# large images. - -MAX_DOT_GRAPH_HEIGHT = 1024 - -# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the -# graphs generated by dot. A depth value of 3 means that only nodes reachable -# from the root by following a path via at most 3 edges will be shown. Nodes -# that lay further from the root node will be omitted. Note that setting this -# option to 1 or 2 may greatly reduce the computation time needed for large -# code bases. Also note that a graph may be further truncated if the graph's -# image dimensions are not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH -# and MAX_DOT_GRAPH_HEIGHT). If 0 is used for the depth value (the default), -# the graph is not depth-constrained. - -MAX_DOT_GRAPH_DEPTH = 0 - -# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent -# background. This is disabled by default, which results in a white background. -# Warning: Depending on the platform used, enabling this option may lead to -# badly anti-aliased labels on the edges of a graph (i.e. they become hard to -# read). - -DOT_TRANSPARENT = NO - -# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output -# files in one run (i.e. multiple -o and -T options on the command line). This -# makes dot run faster, but since only newer versions of dot (>1.8.10) -# support this, this feature is disabled by default. - -DOT_MULTI_TARGETS = NO - -# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will -# generate a legend page explaining the meaning of the various boxes and -# arrows in the dot generated graphs. - -GENERATE_LEGEND = YES - -# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will -# remove the intermediate dot files that are used to generate -# the various graphs. - -DOT_CLEANUP = YES - -#--------------------------------------------------------------------------- -# Configuration::additions related to the search engine -#--------------------------------------------------------------------------- - -# The SEARCHENGINE tag specifies whether or not a search engine should be -# used. If set to NO the values of all tags below this one will be ignored. - -SEARCHENGINE = NO diff -Nru qemu-kvm-0.12.5+noroms/kvm/include/linux/kvm.h qemu-kvm-0.14.1/kvm/include/linux/kvm.h --- qemu-kvm-0.12.5+noroms/kvm/include/linux/kvm.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/include/linux/kvm.h 2011-05-11 13:29:46.000000000 +0000 @@ -103,7 +103,7 @@ /* for kvm_memory_region::flags */ #define KVM_MEM_LOG_DIRTY_PAGES 1UL - +#define KVM_MEMSLOT_INVALID (1UL << 1) /* for KVM_IRQ_LINE */ struct kvm_irq_level { @@ -160,6 +160,7 @@ #define KVM_EXIT_DCR 15 #define KVM_EXIT_NMI 16 #define KVM_EXIT_INTERNAL_ERROR 17 +#define KVM_EXIT_OSI 18 /* For KVM_EXIT_INTERNAL_ERROR */ #define KVM_INTERNAL_ERROR_EMULATION 1 @@ -259,6 +260,10 @@ __u32 ndata; __u64 data[16]; } internal; + /* KVM_EXIT_OSI */ + struct { + __u64 gprs[32]; + } osi; /* Fix the size of the union. */ char padding[256]; }; @@ -400,6 +405,15 @@ __u8 pad[36]; }; +/* for KVM_ENABLE_CAP */ +struct kvm_enable_cap { + /* in */ + __u32 cap; + __u32 flags; + __u64 args[4]; + __u8 pad[64]; +}; + #define KVMIO 0xAE /* @@ -497,6 +511,25 @@ #endif #define KVM_CAP_S390_PSW 42 #define KVM_CAP_PPC_SEGSTATE 43 +#define KVM_CAP_HYPERV 44 +#define KVM_CAP_HYPERV_VAPIC 45 +#define KVM_CAP_HYPERV_SPIN 46 +#define KVM_CAP_PCI_SEGMENT 47 +#define KVM_CAP_PPC_PAIRED_SINGLES 48 +#define KVM_CAP_INTR_SHADOW 49 +#ifdef __KVM_HAVE_DEBUGREGS +#define KVM_CAP_DEBUGREGS 50 +#endif +#define KVM_CAP_X86_ROBUST_SINGLESTEP 51 +#define KVM_CAP_PPC_OSI 52 +#define KVM_CAP_PPC_UNSET_IRQ 53 +#define KVM_CAP_ENABLE_CAP 54 +#ifdef __KVM_HAVE_XSAVE +#define KVM_CAP_XSAVE 55 +#endif +#ifdef __KVM_HAVE_XCRS +#define KVM_CAP_XCRS 56 +#endif #ifdef KVM_CAP_IRQ_ROUTING @@ -683,6 +716,16 @@ /* Available with KVM_CAP_VCPU_EVENTS */ #define KVM_GET_VCPU_EVENTS _IOR(KVMIO, 0x9f, struct kvm_vcpu_events) #define KVM_SET_VCPU_EVENTS _IOW(KVMIO, 0xa0, struct kvm_vcpu_events) +/* Available with KVM_CAP_DEBUGREGS */ +#define KVM_GET_DEBUGREGS _IOR(KVMIO, 0xa1, struct kvm_debugregs) +#define KVM_SET_DEBUGREGS _IOW(KVMIO, 0xa2, struct kvm_debugregs) +#define KVM_ENABLE_CAP _IOW(KVMIO, 0xa3, struct kvm_enable_cap) +/* Available with KVM_CAP_XSAVE */ +#define KVM_GET_XSAVE _IOR(KVMIO, 0xa4, struct kvm_xsave) +#define KVM_SET_XSAVE _IOW(KVMIO, 0xa5, struct kvm_xsave) +/* Available with KVM_CAP_XCRS */ +#define KVM_GET_XCRS _IOR(KVMIO, 0xa6, struct kvm_xcrs) +#define KVM_SET_XCRS _IOW(KVMIO, 0xa7, struct kvm_xcrs) #define KVM_DEV_ASSIGN_ENABLE_IOMMU (1 << 0) @@ -691,8 +734,9 @@ __u32 busnr; __u32 devfn; __u32 flags; + __u32 segnr; union { - __u32 reserved[12]; + __u32 reserved[11]; }; }; diff -Nru qemu-kvm-0.12.5+noroms/kvm/include/linux/vhost.h qemu-kvm-0.14.1/kvm/include/linux/vhost.h --- qemu-kvm-0.12.5+noroms/kvm/include/linux/vhost.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/include/linux/vhost.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,130 @@ +#ifndef _LINUX_VHOST_H +#define _LINUX_VHOST_H +/* Userspace interface for in-kernel virtio accelerators. */ + +/* vhost is used to reduce the number of system calls involved in virtio. + * + * Existing virtio net code is used in the guest without modification. + * + * This header includes interface used by userspace hypervisor for + * device configuration. + */ + +#include + +#include +#include +#include + +struct vhost_vring_state { + unsigned int index; + unsigned int num; +}; + +struct vhost_vring_file { + unsigned int index; + int fd; /* Pass -1 to unbind from file. */ + +}; + +struct vhost_vring_addr { + unsigned int index; + /* Option flags. */ + unsigned int flags; + /* Flag values: */ + /* Whether log address is valid. If set enables logging. */ +#define VHOST_VRING_F_LOG 0 + + /* Start of array of descriptors (virtually contiguous) */ + __u64 desc_user_addr; + /* Used structure address. Must be 32 bit aligned */ + __u64 used_user_addr; + /* Available structure address. Must be 16 bit aligned */ + __u64 avail_user_addr; + /* Logging support. */ + /* Log writes to used structure, at offset calculated from specified + * address. Address must be 32 bit aligned. */ + __u64 log_guest_addr; +}; + +struct vhost_memory_region { + __u64 guest_phys_addr; + __u64 memory_size; /* bytes */ + __u64 userspace_addr; + __u64 flags_padding; /* No flags are currently specified. */ +}; + +/* All region addresses and sizes must be 4K aligned. */ +#define VHOST_PAGE_SIZE 0x1000 + +struct vhost_memory { + __u32 nregions; + __u32 padding; + struct vhost_memory_region regions[0]; +}; + +/* ioctls */ + +#define VHOST_VIRTIO 0xAF + +/* Features bitmask for forward compatibility. Transport bits are used for + * vhost specific features. */ +#define VHOST_GET_FEATURES _IOR(VHOST_VIRTIO, 0x00, __u64) +#define VHOST_SET_FEATURES _IOW(VHOST_VIRTIO, 0x00, __u64) + +/* Set current process as the (exclusive) owner of this file descriptor. This + * must be called before any other vhost command. Further calls to + * VHOST_OWNER_SET fail until VHOST_OWNER_RESET is called. */ +#define VHOST_SET_OWNER _IO(VHOST_VIRTIO, 0x01) +/* Give up ownership, and reset the device to default values. + * Allows subsequent call to VHOST_OWNER_SET to succeed. */ +#define VHOST_RESET_OWNER _IO(VHOST_VIRTIO, 0x02) + +/* Set up/modify memory layout */ +#define VHOST_SET_MEM_TABLE _IOW(VHOST_VIRTIO, 0x03, struct vhost_memory) + +/* Write logging setup. */ +/* Memory writes can optionally be logged by setting bit at an offset + * (calculated from the physical address) from specified log base. + * The bit is set using an atomic 32 bit operation. */ +/* Set base address for logging. */ +#define VHOST_SET_LOG_BASE _IOW(VHOST_VIRTIO, 0x04, __u64) +/* Specify an eventfd file descriptor to signal on log write. */ +#define VHOST_SET_LOG_FD _IOW(VHOST_VIRTIO, 0x07, int) + +/* Ring setup. */ +/* Set number of descriptors in ring. This parameter can not + * be modified while ring is running (bound to a device). */ +#define VHOST_SET_VRING_NUM _IOW(VHOST_VIRTIO, 0x10, struct vhost_vring_state) +/* Set addresses for the ring. */ +#define VHOST_SET_VRING_ADDR _IOW(VHOST_VIRTIO, 0x11, struct vhost_vring_addr) +/* Base value where queue looks for available descriptors */ +#define VHOST_SET_VRING_BASE _IOW(VHOST_VIRTIO, 0x12, struct vhost_vring_state) +/* Get accessor: reads index, writes value in num */ +#define VHOST_GET_VRING_BASE _IOWR(VHOST_VIRTIO, 0x12, struct vhost_vring_state) + +/* The following ioctls use eventfd file descriptors to signal and poll + * for events. */ + +/* Set eventfd to poll for added buffers */ +#define VHOST_SET_VRING_KICK _IOW(VHOST_VIRTIO, 0x20, struct vhost_vring_file) +/* Set eventfd to signal when buffers have beed used */ +#define VHOST_SET_VRING_CALL _IOW(VHOST_VIRTIO, 0x21, struct vhost_vring_file) +/* Set eventfd to signal an error */ +#define VHOST_SET_VRING_ERR _IOW(VHOST_VIRTIO, 0x22, struct vhost_vring_file) + +/* VHOST_NET specific defines */ + +/* Attach virtio net ring to a raw socket, or tap device. + * The socket must be already bound to an ethernet device, this device will be + * used for transmit. Pass fd -1 to unbind from the socket and the transmit + * device. This can be used to stop the ring (e.g. for migration). */ +#define VHOST_NET_SET_BACKEND _IOW(VHOST_VIRTIO, 0x30, struct vhost_vring_file) + +/* Feature bits */ +/* Log all write descriptors. Can be changed while device is active. */ +#define VHOST_F_LOG_ALL 26 +/* vhost-net should add virtio_net_hdr for RX, and strip for TX packets. */ +#define VHOST_NET_F_VIRTIO_NET_HDR 27 + +#endif diff -Nru qemu-kvm-0.12.5+noroms/kvm/include/x86/asm/kvm.h qemu-kvm-0.14.1/kvm/include/x86/asm/kvm.h --- qemu-kvm-0.12.5+noroms/kvm/include/x86/asm/kvm.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/include/x86/asm/kvm.h 2011-05-11 13:29:46.000000000 +0000 @@ -21,6 +21,9 @@ #define __KVM_HAVE_PIT_STATE2 #define __KVM_HAVE_XEN_HVM #define __KVM_HAVE_VCPU_EVENTS +#define __KVM_HAVE_DEBUGREGS +#define __KVM_HAVE_XSAVE +#define __KVM_HAVE_XCRS /* Architectural interrupt line count. */ #define KVM_NR_INTERRUPTS 256 @@ -257,6 +260,11 @@ /* When set in flags, include corresponding fields on KVM_SET_VCPU_EVENTS */ #define KVM_VCPUEVENT_VALID_NMI_PENDING 0x00000001 #define KVM_VCPUEVENT_VALID_SIPI_VECTOR 0x00000002 +#define KVM_VCPUEVENT_VALID_SHADOW 0x00000004 + +/* Interrupt shadow states */ +#define KVM_X86_SHADOW_INT_MOV_SS 0x01 +#define KVM_X86_SHADOW_INT_STI 0x02 /* for KVM_GET/SET_VCPU_EVENTS */ struct kvm_vcpu_events { @@ -271,7 +279,7 @@ __u8 injected; __u8 nr; __u8 soft; - __u8 pad; + __u8 shadow; } interrupt; struct { __u8 injected; @@ -284,4 +292,33 @@ __u32 reserved[10]; }; +/* for KVM_GET/SET_DEBUGREGS */ +struct kvm_debugregs { + __u64 db[4]; + __u64 dr6; + __u64 dr7; + __u64 flags; + __u64 reserved[9]; +}; + +/* for KVM_CAP_XSAVE */ +struct kvm_xsave { + __u32 region[1024]; +}; + +#define KVM_MAX_XCRS 16 + +struct kvm_xcr { + __u32 xcr; + __u32 reserved; + __u64 value; +}; + +struct kvm_xcrs { + __u32 nr_xcrs; + __u32 flags; + struct kvm_xcr xcrs[KVM_MAX_XCRS]; + __u64 padding[16]; +}; + #endif /* _ASM_X86_KVM_H */ diff -Nru qemu-kvm-0.12.5+noroms/kvm/kvm_stat qemu-kvm-0.14.1/kvm/kvm_stat --- qemu-kvm-0.12.5+noroms/kvm/kvm_stat 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/kvm_stat 2011-05-11 13:29:46.000000000 +0000 @@ -3,21 +3,290 @@ import curses import sys, os, time, optparse +class DebugfsProvider(object): + def __init__(self): + self.base = '/sys/kernel/debug/kvm' + self._fields = os.listdir(self.base) + def fields(self): + return self._fields + def select(self, fields): + self._fields = fields + def read(self): + def val(key): + return int(file(self.base + '/' + key).read()) + return dict([(key, val(key)) for key in self._fields]) + +vmx_exit_reasons = { + 0: 'EXCEPTION_NMI', + 1: 'EXTERNAL_INTERRUPT', + 2: 'TRIPLE_FAULT', + 7: 'PENDING_INTERRUPT', + 8: 'NMI_WINDOW', + 9: 'TASK_SWITCH', + 10: 'CPUID', + 12: 'HLT', + 14: 'INVLPG', + 15: 'RDPMC', + 16: 'RDTSC', + 18: 'VMCALL', + 19: 'VMCLEAR', + 20: 'VMLAUNCH', + 21: 'VMPTRLD', + 22: 'VMPTRST', + 23: 'VMREAD', + 24: 'VMRESUME', + 25: 'VMWRITE', + 26: 'VMOFF', + 27: 'VMON', + 28: 'CR_ACCESS', + 29: 'DR_ACCESS', + 30: 'IO_INSTRUCTION', + 31: 'MSR_READ', + 32: 'MSR_WRITE', + 33: 'INVALID_STATE', + 36: 'MWAIT_INSTRUCTION', + 39: 'MONITOR_INSTRUCTION', + 40: 'PAUSE_INSTRUCTION', + 41: 'MCE_DURING_VMENTRY', + 43: 'TPR_BELOW_THRESHOLD', + 44: 'APIC_ACCESS', + 48: 'EPT_VIOLATION', + 49: 'EPT_MISCONFIG', + 54: 'WBINVD', + 55: 'XSETBV', +} + +svm_exit_reasons = { + 0x000: 'READ_CR0', + 0x003: 'READ_CR3', + 0x004: 'READ_CR4', + 0x008: 'READ_CR8', + 0x010: 'WRITE_CR0', + 0x013: 'WRITE_CR3', + 0x014: 'WRITE_CR4', + 0x018: 'WRITE_CR8', + 0x020: 'READ_DR0', + 0x021: 'READ_DR1', + 0x022: 'READ_DR2', + 0x023: 'READ_DR3', + 0x024: 'READ_DR4', + 0x025: 'READ_DR5', + 0x026: 'READ_DR6', + 0x027: 'READ_DR7', + 0x030: 'WRITE_DR0', + 0x031: 'WRITE_DR1', + 0x032: 'WRITE_DR2', + 0x033: 'WRITE_DR3', + 0x034: 'WRITE_DR4', + 0x035: 'WRITE_DR5', + 0x036: 'WRITE_DR6', + 0x037: 'WRITE_DR7', + 0x040: 'EXCP_BASE', + 0x060: 'INTR', + 0x061: 'NMI', + 0x062: 'SMI', + 0x063: 'INIT', + 0x064: 'VINTR', + 0x065: 'CR0_SEL_WRITE', + 0x066: 'IDTR_READ', + 0x067: 'GDTR_READ', + 0x068: 'LDTR_READ', + 0x069: 'TR_READ', + 0x06a: 'IDTR_WRITE', + 0x06b: 'GDTR_WRITE', + 0x06c: 'LDTR_WRITE', + 0x06d: 'TR_WRITE', + 0x06e: 'RDTSC', + 0x06f: 'RDPMC', + 0x070: 'PUSHF', + 0x071: 'POPF', + 0x072: 'CPUID', + 0x073: 'RSM', + 0x074: 'IRET', + 0x075: 'SWINT', + 0x076: 'INVD', + 0x077: 'PAUSE', + 0x078: 'HLT', + 0x079: 'INVLPG', + 0x07a: 'INVLPGA', + 0x07b: 'IOIO', + 0x07c: 'MSR', + 0x07d: 'TASK_SWITCH', + 0x07e: 'FERR_FREEZE', + 0x07f: 'SHUTDOWN', + 0x080: 'VMRUN', + 0x081: 'VMMCALL', + 0x082: 'VMLOAD', + 0x083: 'VMSAVE', + 0x084: 'STGI', + 0x085: 'CLGI', + 0x086: 'SKINIT', + 0x087: 'RDTSCP', + 0x088: 'ICEBP', + 0x089: 'WBINVD', + 0x08a: 'MONITOR', + 0x08b: 'MWAIT', + 0x08c: 'MWAIT_COND', + 0x400: 'NPF', +} + +vendor_exit_reasons = { + 'vmx': vmx_exit_reasons, + 'svm': svm_exit_reasons, +} + +exit_reasons = None + +for line in file('/proc/cpuinfo').readlines(): + if line.startswith('flags'): + for flag in line.split(): + if flag in vendor_exit_reasons: + exit_reasons = vendor_exit_reasons[flag] + +filters = { + 'kvm_exit': ('exit_reason', exit_reasons) +} + +def invert(d): + return dict((x[1], x[0]) for x in d.iteritems()) + +for f in filters: + filters[f] = (filters[f][0], invert(filters[f][1])) + +import ctypes, struct, array + +libc = ctypes.CDLL('libc.so.6') +syscall = libc.syscall +class perf_event_attr(ctypes.Structure): + _fields_ = [('type', ctypes.c_uint32), + ('size', ctypes.c_uint32), + ('config', ctypes.c_uint64), + ('sample_freq', ctypes.c_uint64), + ('sample_type', ctypes.c_uint64), + ('read_format', ctypes.c_uint64), + ('flags', ctypes.c_uint64), + ('wakeup_events', ctypes.c_uint32), + ('bp_type', ctypes.c_uint32), + ('bp_addr', ctypes.c_uint64), + ('bp_len', ctypes.c_uint64), + ] +def _perf_event_open(attr, pid, cpu, group_fd, flags): + return syscall(298, ctypes.pointer(attr), ctypes.c_int(pid), + ctypes.c_int(cpu), ctypes.c_int(group_fd), + ctypes.c_long(flags)) + +PERF_TYPE_HARDWARE = 0 +PERF_TYPE_SOFTWARE = 1 +PERF_TYPE_TRACEPOINT = 2 +PERF_TYPE_HW_CACHE = 3 +PERF_TYPE_RAW = 4 +PERF_TYPE_BREAKPOINT = 5 + +PERF_SAMPLE_IP = 1 << 0 +PERF_SAMPLE_TID = 1 << 1 +PERF_SAMPLE_TIME = 1 << 2 +PERF_SAMPLE_ADDR = 1 << 3 +PERF_SAMPLE_READ = 1 << 4 +PERF_SAMPLE_CALLCHAIN = 1 << 5 +PERF_SAMPLE_ID = 1 << 6 +PERF_SAMPLE_CPU = 1 << 7 +PERF_SAMPLE_PERIOD = 1 << 8 +PERF_SAMPLE_STREAM_ID = 1 << 9 +PERF_SAMPLE_RAW = 1 << 10 + +PERF_FORMAT_TOTAL_TIME_ENABLED = 1 << 0 +PERF_FORMAT_TOTAL_TIME_RUNNING = 1 << 1 +PERF_FORMAT_ID = 1 << 2 +PERF_FORMAT_GROUP = 1 << 3 + +import re + +class TracepointProvider(object): + def __init__(self): + self.base = '/sys/kernel/debug/tracing/events/kvm/' + fields = [f + for f in os.listdir(self.base) + if os.path.isdir(self.base + '/' + f)] + extra = [] + for f in fields: + if f in filters: + subfield, values = filters[f] + for name, number in values.iteritems(): + extra.append(f + '(' + name + ')') + fields += extra + self.select(fields) + def fields(self): + return self._fields + def select(self, _fields): + self._fields = _fields + cpure = r'cpu([0-9]+)' + self.cpus = [int(re.match(cpure, x).group(1)) + for x in os.listdir('/sys/devices/system/cpu') + if re.match(cpure, x)] + import resource + nfiles = len(self.cpus) * 1000 + resource.setrlimit(resource.RLIMIT_NOFILE, (nfiles, nfiles)) + fds = [] + self.group_leaders = [] + for cpu in self.cpus: + group_leader = -1 + for f in _fields: + fbase, sub = f, None + m = re.match(r'(.*)\((.*)\)', f) + if m: + fbase, sub = m.groups() + attr = perf_event_attr() + attr.type = PERF_TYPE_TRACEPOINT + attr.size = ctypes.sizeof(attr) + id = int(file(self.base + fbase + '/id').read()) + attr.config = id + attr.sample_type = (PERF_SAMPLE_RAW + | PERF_SAMPLE_TIME + | PERF_SAMPLE_CPU) + attr.sample_period = 1 + attr.read_format = PERF_FORMAT_GROUP + fd = _perf_event_open(attr, -1, cpu, group_leader, 0) + if fd == -1: + raise Exception('perf_event_open failed') + if sub: + import fcntl + filter = '%s==%d\0' % (filters[fbase][0], + filters[fbase][1][sub]) + fcntl.ioctl(fd, 0x40082406, filter) + if group_leader == -1: + group_leader = fd + fds.append(fd) + self.group_leaders.append(group_leader) + self.fds = fds + self.files = [os.fdopen(group_leader) + for group_leader in self.group_leaders] + def read(self): + ret = dict([(f, 0) for f in self._fields]) + bytes = 8 * (1 + len(self._fields)) + fmt = 'xxxxxxxx' + 'q' * len(self._fields) + for file in self.files: + a = struct.unpack(fmt, file.read(bytes)) + for field, val in zip(self._fields, a): + ret[field] += val + return ret + class Stats: - def __init__(self, fields = None): + def __init__(self, provider, fields = None): def wanted(key): import re if not fields: return True return re.match(fields, key) != None - self.base = '/sys/kernel/debug/kvm' - self.values = {} - for key in os.listdir(self.base): - if wanted(key): - self.values[key] = None + self.provider = provider + self.values = dict([(key, None) + for key in provider.fields() + if wanted(key)]) + self.provider.select(self.values.keys()) def get(self): - for key, oldval in self.values.iteritems(): - newval = int(file(self.base + '/' + key).read()) + new = self.provider.read() + for key in self.provider.fields(): + oldval = self.values[key] + newval = new[key] newdelta = None if oldval is not None: newdelta = newval - oldval[0] @@ -32,34 +301,43 @@ print "and ensure the kvm modules are loaded" sys.exit(1) -label_width = 20 +label_width = 40 number_width = 10 def tui(screen, stats): curses.use_default_colors() curses.noecho() - def refresh(): + def refresh(sleeptime): screen.erase() screen.addstr(0, 0, 'kvm statistics') row = 2 s = stats.get() - for key in sorted(s.keys()): + def sortkey(x): + if s[x][1]: + return (-s[x][1], -s[x][0]) + else: + return (0, -s[x][0]) + for key in sorted(s.keys(), key = sortkey): if row >= screen.getmaxyx()[0]: break values = s[key] + if not values[0] and not values[1]: + break col = 1 screen.addstr(row, col, key) col += label_width screen.addstr(row, col, '%10d' % (values[0],)) col += number_width if values[1] is not None: - screen.addstr(row, col, '%8d' % (values[1],)) + screen.addstr(row, col, '%8d' % (values[1] / sleeptime,)) row += 1 screen.refresh() + sleeptime = 0.25 while True: - refresh() - curses.halfdelay(10) + refresh(sleeptime) + curses.halfdelay(int(sleeptime * 10)) + sleeptime = 3 try: c = screen.getkey() if c == 'q': @@ -118,7 +396,12 @@ ) (options, args) = options.parse_args(sys.argv) -stats = Stats(fields = options.fields) +try: + provider = TracepointProvider() +except: + provider = DebugfsProvider() + +stats = Stats(provider, fields = options.fields) if options.log: log(stats) diff -Nru qemu-kvm-0.12.5+noroms/kvm/libkvm/libkvm.h qemu-kvm-0.14.1/kvm/libkvm/libkvm.h --- qemu-kvm-0.12.5+noroms/kvm/libkvm/libkvm.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/libkvm/libkvm.h 2011-05-11 13:29:46.000000000 +0000 @@ -359,36 +359,6 @@ #if defined(__i386__) || defined(__x86_64__) /*! - * \brief Setup a vcpu's cpuid instruction emulation - * - * Set up a table of cpuid function to cpuid outputs.\n - * - * \param kvm Pointer to the current kvm_context - * \param vcpu Which virtual CPU should be initialized - * \param nent number of entries to be installed - * \param entries cpuid function entries table - * \return 0 on success, or -errno on error - */ -int kvm_setup_cpuid(kvm_context_t kvm, int vcpu, int nent, - struct kvm_cpuid_entry *entries); - -/*! - * \brief Setup a vcpu's cpuid instruction emulation - * - * Set up a table of cpuid function to cpuid outputs. - * This call replaces the older kvm_setup_cpuid interface by adding a few - * parameters to support cpuid functions that have sub-leaf values. - * - * \param kvm Pointer to the current kvm_context - * \param vcpu Which virtual CPU should be initialized - * \param nent number of entries to be installed - * \param entries cpuid function entries table - * \return 0 on success, or -errno on error - */ -int kvm_setup_cpuid2(kvm_context_t kvm, int vcpu, int nent, - struct kvm_cpuid_entry2 *entries); - -/*! * \brief Setting the number of shadow pages to be allocated to the vm * * \param kvm pointer to kvm_context diff -Nru qemu-kvm-0.12.5+noroms/kvm/libkvm/libkvm-x86.c qemu-kvm-0.14.1/kvm/libkvm/libkvm-x86.c --- qemu-kvm-0.12.5+noroms/kvm/libkvm/libkvm-x86.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/libkvm/libkvm-x86.c 2011-05-11 13:29:46.000000000 +0000 @@ -466,45 +466,6 @@ return kvm->run[vcpu]->cr8; } -int kvm_setup_cpuid(kvm_context_t kvm, int vcpu, int nent, - struct kvm_cpuid_entry *entries) -{ - struct kvm_cpuid *cpuid; - int r; - - cpuid = malloc(sizeof(*cpuid) + nent * sizeof(*entries)); - if (!cpuid) - return -ENOMEM; - - cpuid->nent = nent; - memcpy(cpuid->entries, entries, nent * sizeof(*entries)); - r = ioctl(kvm->vcpu_fd[vcpu], KVM_SET_CPUID, cpuid); - - free(cpuid); - return r; -} - -int kvm_setup_cpuid2(kvm_context_t kvm, int vcpu, int nent, - struct kvm_cpuid_entry2 *entries) -{ - struct kvm_cpuid2 *cpuid; - int r; - - cpuid = malloc(sizeof(*cpuid) + nent * sizeof(*entries)); - if (!cpuid) - return -ENOMEM; - - cpuid->nent = nent; - memcpy(cpuid->entries, entries, nent * sizeof(*entries)); - r = ioctl(kvm->vcpu_fd[vcpu], KVM_SET_CPUID2, cpuid); - if (r == -1) { - fprintf(stderr, "kvm_setup_cpuid2: %m\n"); - r = -errno; - } - free(cpuid); - return r; -} - int kvm_set_shadow_pages(kvm_context_t kvm, unsigned int nrshadow_pages) { #ifdef KVM_CAP_MMU_SHADOW_CACHE_CONTROL diff -Nru qemu-kvm-0.12.5+noroms/kvm/scripts/make-release qemu-kvm-0.14.1/kvm/scripts/make-release --- qemu-kvm-0.12.5+noroms/kvm/scripts/make-release 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/scripts/make-release 2011-05-11 13:29:46.000000000 +0000 @@ -1,7 +1,7 @@ #!/bin/bash -e usage() { - echo "usage: $0 [--upload] [--formal] commit [name]" + echo "usage: $0 [--upload] [--formal] commit [name] [tarball] [user]" exit 1 } @@ -12,7 +12,7 @@ releasedir=~/sf-release [[ -z "$TMP" ]] && TMP="/tmp" -tmpdir="$TMP/qemu-kvm-make-release.$$" +tmpdir=`mktemp -d "$TMP/qemu-kvm-make-release.XXXXXXXXXX"` while [[ "$1" = -* ]]; do opt="$1" shift @@ -40,25 +40,39 @@ name="$commit" fi -tarball="$releasedir/$name.tar" +tarball="$3" +if [[ -z "$tarball" ]]; then + tarball="$releasedir/$name.tar.gz" +fi +#strip trailing .gz if any +tarball=${tarball/%.gz/} cd "$(dirname "$0")"/../.. +mkdir -p "$(dirname "$tarball")" git archive --prefix="$name/" --format=tar "$commit" > "$tarball" -mkdir -p "$tmpdir" +mtime=`git show --pretty=format:%ct "$commit""^{commit}" -- | head -n 1` +tarargs="--owner=root --group=root" + +mkdir -p "$tmpdir/$name" git cat-file -p "${commit}:roms" | awk ' { print $4, $3 } ' \ - > "$tmpdir/EXTERNAL_DEPENDENCIES" -tar -rf "$tarball" --transform "s,^,$name/," -C "$tmpdir" \ - "EXTERNAL_DEPENDENCIES" + > "$tmpdir/$name/EXTERNAL_DEPENDENCIES" +touch -d "@$mtime" "$tmpdir/$name/EXTERNAL_DEPENDENCIES" +tar -rf "$tarball" -C "$tmpdir" \ + $tarargs \ + "$name/EXTERNAL_DEPENDENCIES" rm -rf "$tmpdir" if [[ -n "$formal" ]]; then - mkdir -p "$tmpdir" - echo "$name" > "$tmpdir/KVM_VERSION" - tar -rf "$tarball" --transform "s,^,$name/," -C "$tmpdir" "KVM_VERSION" + mkdir -p "$tmpdir/$name" + echo "$name" > "$tmpdir/$name/KVM_VERSION" + touch -d "@$mtime" "$tmpdir/$name/KVM_VERSION" + tar -rf "$tarball" -C "$tmpdir" "$name/KVM_VERSION" \ + $tarargs rm -rf "$tmpdir" fi +rm -f "$tarball.gz" gzip -9 "$tarball" tarball="$tarball.gz" diff -Nru qemu-kvm-0.12.5+noroms/kvm/scripts/vmxcap qemu-kvm-0.14.1/kvm/scripts/vmxcap --- qemu-kvm-0.12.5+noroms/kvm/scripts/vmxcap 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/scripts/vmxcap 2011-05-11 13:29:46.000000000 +0000 @@ -196,6 +196,7 @@ 8: 'Paging-structure memory type UC', 14: 'Paging-structure memory type WB', 16: '2MB EPT pages', + 17: '1GB EPT pages', 20: 'INVEPT supported', 25: 'Single-context INVEPT', 26: 'All-context INVEPT', diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/balloon_ctl.c qemu-kvm-0.14.1/kvm/user/balloon_ctl.c --- qemu-kvm-0.12.5+noroms/kvm/user/balloon_ctl.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/balloon_ctl.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,92 +0,0 @@ -/* - * This binary provides access to the guest's balloon driver - * module. - * - * Copyright (C) 2007 Qumranet - * - * Author: - * - * Dor Laor - * - * This work is licensed under the GNU LGPL license, version 2. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#define __user -#include - -#define PAGE_SIZE 4096ul - - -static int balloon_op(int *fd, int bytes) -{ - struct kvm_balloon_op bop; - int r; - - bop.npages = bytes/PAGE_SIZE; - r = ioctl(*fd, KVM_BALLOON_OP, &bop); - if (r == -1) - return -errno; - printf("Ballon handled %d pages successfully\n", bop.npages); - - return 0; -} - -static int balloon_init(int *fd) -{ - *fd = open("/dev/kvm_balloon", O_RDWR); - if (*fd == -1) { - perror("open /dev/kvm_balloon"); - return -1; - } - - return 0; -} - -int main(int argc, char *argv[]) -{ - int fd; - int r; - int bytes; - - if (argc != 3) { - perror("Please provide op=[i|d], bytes\n"); - return 1; - } - bytes = atoi(argv[2]); - - switch (*argv[1]) { - case 'i': - break; - case 'd': - bytes = -bytes; - break; - default: - perror("Wrong op param\n"); - return 1; - } - - if (balloon_init(&fd)) { - perror("balloon_init failed\n"); - return 1; - } - - if ((r = balloon_op(&fd, bytes))) { - perror("balloon_op failed\n"); - goto out; - } - -out: - close(fd); - - return r; -} - diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/bootstrap.lds qemu-kvm-0.14.1/kvm/user/bootstrap.lds --- qemu-kvm-0.12.5+noroms/kvm/user/bootstrap.lds 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/bootstrap.lds 1970-01-01 00:00:00.000000000 +0000 @@ -1,15 +0,0 @@ -OUTPUT_FORMAT(binary) - -SECTIONS -{ - . = 0; - stext = .; - .text : { *(.init) *(.text) } - . = ALIGN(4K); - .data : { *(.data) } - . = ALIGN(16); - .bss : { *(.bss) } - . = ALIGN(4K); - edata = .; -} - diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/config-i386.mak qemu-kvm-0.14.1/kvm/user/config-i386.mak --- qemu-kvm-0.12.5+noroms/kvm/user/config-i386.mak 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/config-i386.mak 1970-01-01 00:00:00.000000000 +0000 @@ -1,10 +0,0 @@ -TEST_DIR=test/x86 -cstart.o = $(TEST_DIR)/cstart.o -bits = 32 -ldarch = elf32-i386 -CFLAGS += -D__i386__ -CFLAGS += -I $(KERNELDIR)/include - -tests= - -include config-x86-common.mak diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/config-ia64.mak qemu-kvm-0.14.1/kvm/user/config-ia64.mak --- qemu-kvm-0.12.5+noroms/kvm/user/config-ia64.mak 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/config-ia64.mak 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -bits = 64 -CFLAGS += -m64 -CFLAGS += -D__ia64__ -CFLAGS += -I../include/ia64 - -all: - diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/config-powerpc-440.mak qemu-kvm-0.14.1/kvm/user/config-powerpc-440.mak --- qemu-kvm-0.12.5+noroms/kvm/user/config-powerpc-440.mak 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/config-powerpc-440.mak 1970-01-01 00:00:00.000000000 +0000 @@ -1,15 +0,0 @@ - - -# for some reason binutils hates tlbsx unless we say we're 405 :( -CFLAGS += -Wa,-m405 -I test/lib/powerpc/44x - -cflatobjs += \ - test/lib/powerpc/44x/map.o \ - test/lib/powerpc/44x/tlbwe.o \ - test/lib/powerpc/44x/timebase.o - -simpletests += \ - test/powerpc/44x/tlbsx.bin \ - test/powerpc/44x/tlbwe_16KB.bin \ - test/powerpc/44x/tlbwe_hole.bin \ - test/powerpc/44x/tlbwe.bin diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/config-powerpc.mak qemu-kvm-0.14.1/kvm/user/config-powerpc.mak --- qemu-kvm-0.12.5+noroms/kvm/user/config-powerpc.mak 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/config-powerpc.mak 1970-01-01 00:00:00.000000000 +0000 @@ -1,39 +0,0 @@ -CFLAGS += -I../include/powerpc -CFLAGS += -Wa,-mregnames -I test/lib -CFLAGS += -ffreestanding - -cstart := test/powerpc/cstart.o - -cflatobjs += \ - test/lib/powerpc/io.o - -$(libcflat): LDFLAGS += -nostdlib - -# these tests do not use libcflat -simpletests := \ - test/powerpc/spin.bin \ - test/powerpc/io.bin \ - test/powerpc/sprg.bin - -# theses tests use cstart.o, libcflat, and libgcc -tests := \ - test/powerpc/exit.bin \ - test/powerpc/helloworld.bin - -include config-powerpc-$(PROCESSOR).mak - - -all: kvmtrace kvmctl $(libcflat) $(simpletests) $(tests) - -$(simpletests): %.bin: %.o - $(CC) -nostdlib $^ -Wl,-T,flat.lds -o $@ - -$(tests): %.bin: $(cstart) %.o $(libcflat) - $(CC) -nostdlib $^ $(libgcc) -Wl,-T,flat.lds -o $@ - -kvmctl_objs = main-ppc.o iotable.o ../libkvm/libkvm.a - -arch_clean: - $(RM) $(simpletests) $(tests) $(cstart) - $(RM) $(patsubst %.bin, %.elf, $(simpletests) $(tests)) - $(RM) $(patsubst %.bin, %.o, $(simpletests) $(tests)) diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/configure qemu-kvm-0.14.1/kvm/user/configure --- qemu-kvm-0.12.5+noroms/kvm/user/configure 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/configure 1970-01-01 00:00:00.000000000 +0000 @@ -1,75 +0,0 @@ -#!/bin/bash - -prefix=/usr/local -kerneldir=/lib/modules/$(uname -r)/build -cc=gcc -ld=ld -objcopy=objcopy -ar=ar -arch=`uname -m | sed -e s/i.86/i386/` -processor="$arch" -cross_prefix= - -usage() { - cat <<-EOF - Usage: $0 [options] - - Options include: - --arch=ARCH architecture to compile for ($arch) - --cross-prefix=PREFIX cross compiler prefix - --cc=CC c compiler to use ($cc) - --ld=LD ld linker to use ($ld) - --prefix=PREFIX where to install things ($prefix) - --kerneldir=DIR kernel build directory for kvm.h ($kerneldir) -EOF - exit 1 -} - -while [[ "$1" = -* ]]; do - opt="$1"; shift - arg= - if [[ "$opt" = *=* ]]; then - arg="${opt#*=}" - opt="${opt%%=*}" - fi - case "$opt" in - --prefix) - prefix="$arg" - ;; - --kerneldir) - kerneldir="$arg" - ;; - --arch) - arch="$arg" - ;; - --processor) - processor="$arg" - ;; - --cross-prefix) - cross_prefix="$arg" - ;; - --cc) - cc="$arg" - ;; - --ld) - ld="$arg" - ;; - --help) - usage - ;; - *) - usage - ;; - esac -done - -cat < config.mak -PREFIX=$prefix -KERNELDIR=$(readlink -f $kerneldir) -ARCH=$arch -PROCESSOR=$processor -CC=$cross_prefix$cc -LD=$cross_prefix$ld -OBJCOPY=$cross_prefix$objcopy -AR=$cross_prefix$ar -EOF diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/config-x86_64.mak qemu-kvm-0.14.1/kvm/user/config-x86_64.mak --- qemu-kvm-0.12.5+noroms/kvm/user/config-x86_64.mak 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/config-x86_64.mak 1970-01-01 00:00:00.000000000 +0000 @@ -1,12 +0,0 @@ -TEST_DIR=test/x86 -cstart.o = $(TEST_DIR)/cstart64.o -bits = 64 -ldarch = elf64-x86-64 -CFLAGS += -D__x86_64__ - -tests = $(TEST_DIR)/access.flat $(TEST_DIR)/sieve.flat \ - $(TEST_DIR)/simple.flat $(TEST_DIR)/stringio.flat \ - $(TEST_DIR)/memtest1.flat $(TEST_DIR)/emulator.flat \ - $(TEST_DIR)/hypercall.flat $(TEST_DIR)/apic.flat - -include config-x86-common.mak diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/config-x86-common.mak qemu-kvm-0.14.1/kvm/user/config-x86-common.mak --- qemu-kvm-0.12.5+noroms/kvm/user/config-x86-common.mak 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/config-x86-common.mak 1970-01-01 00:00:00.000000000 +0000 @@ -1,77 +0,0 @@ -#This is a make file with common rules for both x86 & x86-64 - -CFLAGS += -I../include/x86 - -all: kvmtrace test_cases - -balloon_ctl: balloon_ctl.o - -cflatobjs += \ - test/lib/x86/io.o \ - test/lib/x86/smp.o - -cflatobjs += test/lib/x86/fwcfg.o -cflatobjs += test/lib/x86/apic.o - -$(libcflat): LDFLAGS += -nostdlib -$(libcflat): CFLAGS += -ffreestanding -I test/lib - -CFLAGS += -m$(bits) - -FLATLIBS = test/lib/libcflat.a $(libgcc) -%.flat: %.o $(FLATLIBS) - $(CC) $(CFLAGS) -nostdlib -o $@ -Wl,-T,flat.lds $^ $(FLATLIBS) - -tests-common = $(TEST_DIR)/bootstrap \ - $(TEST_DIR)/vmexit.flat $(TEST_DIR)/tsc.flat \ - $(TEST_DIR)/smptest.flat $(TEST_DIR)/port80.flat \ - $(TEST_DIR)/realmode.flat $(TEST_DIR)/msr.flat - -test_cases: $(tests-common) $(tests) - -$(TEST_DIR)/%.o: CFLAGS += -std=gnu99 -ffreestanding -I test/lib -I test/lib/x86 - -$(TEST_DIR)/bootstrap: $(TEST_DIR)/bootstrap.o - $(CC) -nostdlib -o $@ -Wl,-T,bootstrap.lds $^ - -$(TEST_DIR)/access.flat: $(cstart.o) $(TEST_DIR)/access.o $(TEST_DIR)/print.o - -$(TEST_DIR)/hypercall.flat: $(cstart.o) $(TEST_DIR)/hypercall.o $(TEST_DIR)/print.o - -$(TEST_DIR)/sieve.flat: $(cstart.o) $(TEST_DIR)/sieve.o \ - $(TEST_DIR)/print.o $(TEST_DIR)/vm.o - -$(TEST_DIR)/vmexit.flat: $(cstart.o) $(TEST_DIR)/vmexit.o - -$(TEST_DIR)/test32.flat: $(TEST_DIR)/test32.o - -$(TEST_DIR)/smptest.flat: $(cstart.o) $(TEST_DIR)/smptest.o - -$(TEST_DIR)/emulator.flat: $(cstart.o) $(TEST_DIR)/emulator.o \ - $(TEST_DIR)/vm.o $(TEST_DIR)/print.o - -$(TEST_DIR)/port80.flat: $(cstart.o) $(TEST_DIR)/port80.o - -$(TEST_DIR)/tsc.flat: $(cstart.o) $(TEST_DIR)/tsc.o - -$(TEST_DIR)/apic.flat: $(cstart.o) $(TEST_DIR)/apic.o $(TEST_DIR)/vm.o \ - $(TEST_DIR)/print.o - -$(TEST_DIR)/realmode.flat: $(TEST_DIR)/realmode.o - $(CC) -m32 -nostdlib -o $@ -Wl,-T,$(TEST_DIR)/realmode.lds $^ - -$(TEST_DIR)/realmode.o: bits = 32 - -$(TEST_DIR)/memtest1.flat: $(TEST_DIR)/memtest1.o - -$(TEST_DIR)/stringio.flat: $(TEST_DIR)/stringio.o - -$(TEST_DIR)/simple.flat: $(TEST_DIR)/simple.o - -$(TEST_DIR)/msr.flat: $(cstart.o) $(TEST_DIR)/msr.o - -arch_clean: - $(RM) $(TEST_DIR)/bootstrap $(TEST_DIR)/*.o $(TEST_DIR)/*.flat \ - $(TEST_DIR)/.*.d $(TEST_DIR)/lib/.*.d $(TEST_DIR)/lib/*.o - --include $(TEST_DIR)/.*.d test/lib/.*.d test/lib/x86/.*.d diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/COPYRIGHT qemu-kvm-0.14.1/kvm/user/COPYRIGHT --- qemu-kvm-0.12.5+noroms/kvm/user/COPYRIGHT 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/COPYRIGHT 1970-01-01 00:00:00.000000000 +0000 @@ -1,4 +0,0 @@ -Copyright (C) 2006 Qumranet. - -The files in this directory and its subdirectories are licensed under the -GNU LGPL, version 2. diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/flat.lds qemu-kvm-0.14.1/kvm/user/flat.lds --- qemu-kvm-0.12.5+noroms/kvm/user/flat.lds 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/flat.lds 1970-01-01 00:00:00.000000000 +0000 @@ -1,15 +0,0 @@ -SECTIONS -{ - . = 4M + SIZEOF_HEADERS; - stext = .; - .text : { *(.init) *(.text) *(.text.*) } - . = ALIGN(4K); - .data : { *(.data) } - . = ALIGN(16); - .rodata : { *(.rodata) } - . = ALIGN(16); - .bss : { *(.bss) } - . = ALIGN(4K); - edata = .; -} - diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/formats qemu-kvm-0.14.1/kvm/user/formats --- qemu-kvm-0.12.5+noroms/kvm/user/formats 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/formats 1970-01-01 00:00:00.000000000 +0000 @@ -1,31 +0,0 @@ -0x00000000 %(ts)d (+%(relts)12d) unknown (0x%(event)016x) vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ 0x%(1)08x 0x%(2)08x 0x%(3)08x 0x%(4)08x 0x%(5)08x ] - -0x00010001 %(ts)d (+%(relts)12d) VMENTRY vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x -0x00010002 %(ts)d (+%(relts)12d) VMEXIT vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ exitcode = 0x%(1)08x, rip = 0x%(3)08x %(2)08x ] -0x00020001 %(ts)d (+%(relts)12d) PAGE_FAULT vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ errorcode = 0x%(1)08x, virt = 0x%(3)08x %(2)08x ] -0x00020002 %(ts)d (+%(relts)12d) INJ_VIRQ vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ vector = 0x%(1)02x ] -0x00020003 %(ts)d (+%(relts)12d) REDELIVER_EVT vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ vector = 0x%(1)02x ] -0x00020004 %(ts)d (+%(relts)12d) PEND_INTR vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ vector = 0x%(1)02x ] -0x00020005 %(ts)d (+%(relts)12d) IO_READ vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ port = 0x%(1)04x, size = %(2)d ] -0x00020006 %(ts)d (+%(relts)12d) IO_WRITE vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ port = 0x%(1)04x, size = %(2)d ] -0x00020007 %(ts)d (+%(relts)12d) CR_READ vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ CR# = %(1)d, value = 0x%(3)08x %(2)08x ] -0x00020008 %(ts)d (+%(relts)12d) CR_WRITE vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ CR# = %(1)d, value = 0x%(3)08x %(2)08x ] -0x00020009 %(ts)d (+%(relts)12d) DR_READ vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ DR# = %(1)d, value = 0x%(2)08x ] -0x0002000A %(ts)d (+%(relts)12d) DR_WRITE vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ DR# = %(1)d, value = 0x%(2)08x ] -0x0002000B %(ts)d (+%(relts)12d) MSR_READ vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ MSR# = 0x%(1)08x, data = 0x%(3)08x %(2)08x ] -0x0002000C %(ts)d (+%(relts)12d) MSR_WRITE vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ MSR# = 0x%(1)08x, data = 0x%(3)08x %(2)08x ] -0x0002000D %(ts)d (+%(relts)12d) CPUID vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ func = 0x%(1)08x, eax = 0x%(2)08x, ebx = 0x%(3)08x, ecx = 0x%(4)08x edx = 0x%(5)08x] -0x0002000E %(ts)d (+%(relts)12d) INTR vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ vector = 0x%(1)02x ] -0x0002000F %(ts)d (+%(relts)12d) NMI vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x -0x00020010 %(ts)d (+%(relts)12d) VMMCALL vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ func = 0x%(1)08x ] -0x00020011 %(ts)d (+%(relts)12d) HLT vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x -0x00020012 %(ts)d (+%(relts)12d) CLTS vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x -0x00020013 %(ts)d (+%(relts)12d) LMSW vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ value = 0x%(1)08x ] -0x00020014 %(ts)d (+%(relts)12d) APIC_ACCESS vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ offset = 0x%(1)08x ] -0x00020015 %(ts)d (+%(relts)12d) TDP_FAULT vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ errorcode = 0x%(1)08x, virt = 0x%(3)08x %(2)08x ] -# ppc: tlb traces -0x00020016 GTLB_WRITE vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ index = 0x%(1)08x, tid = 0x%(2)08x, word1=0x%(3)08x, word2=0x%(4)08x, word3=0x%(5)08x ] -0x00020017 STLB_WRITE vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ index = 0x%(1)08x, tid = 0x%(2)08x, word1=0x%(3)08x, word2=0x%(4)08x, word3=0x%(5)08x ] -0x00020018 STLB_INVAL vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ index = 0x%(1)08x, tid = 0x%(2)08x, word1=0x%(3)08x, word2=0x%(4)08x, word3=0x%(5)08x ] -# ppc: instruction emulation - this type is handled more complex in kvmtrace_format, but listed to show the eventid and transported data -#0x00020019 %(ts)d (+%(relts)12d) PPC_INSTR vcpu = 0x%(vcpu)08x pid = 0x%(pid)08x [ instr = 0x%(1)08x, pc = 0x%(2)08x, emul = 0x%(3)08x, nsec = %(4)08d ] diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/iotable.c qemu-kvm-0.14.1/kvm/user/iotable.c --- qemu-kvm-0.12.5+noroms/kvm/user/iotable.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/iotable.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ -/* - * Kernel-based Virtual Machine test driver - * - * This test driver provides a simple way of testing kvm, without a full - * device model. - * - * Copyright (C) 2006 Qumranet - * - * Authors: - * - * Avi Kivity - * Yaniv Kamay - * - * This work is licensed under the GNU LGPL license, version 2. - */ - -#include -#include -#include - -#include "iotable.h" - -struct io_table_entry *io_table_lookup(struct io_table *io_table, uint64_t addr) -{ - int i; - - for (i = 0; i < io_table->nr_entries; i++) { - if (io_table->entries[i].start <= addr && - addr < io_table->entries[i].end) - return &io_table->entries[i]; - } - - return NULL; -} - -int io_table_register(struct io_table *io_table, uint64_t start, uint64_t size, - io_table_handler_t *handler, void *opaque) -{ - struct io_table_entry *entry; - - if (io_table->nr_entries == MAX_IO_TABLE) - return -ENOSPC; - - entry = &io_table->entries[io_table->nr_entries]; - io_table->nr_entries++; - - entry->start = start; - entry->end = start + size; - entry->handler = handler; - entry->opaque = opaque; - - return 0; -} diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/iotable.h qemu-kvm-0.14.1/kvm/user/iotable.h --- qemu-kvm-0.12.5+noroms/kvm/user/iotable.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/iotable.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,40 +0,0 @@ -/* - * Kernel-based Virtual Machine test driver - * - * This test driver provides a simple way of testing kvm, without a full - * device model. - * - * Copyright (C) 2006 Qumranet - * - * Authors: - * - * Avi Kivity - * Yaniv Kamay - * - * This work is licensed under the GNU LGPL license, version 2. - */ - -#include - -#define MAX_IO_TABLE 50 - -typedef int (io_table_handler_t)(void *, int, int, uint64_t, uint64_t *); - -struct io_table_entry -{ - uint64_t start; - uint64_t end; - io_table_handler_t *handler; - void *opaque; -}; - -struct io_table -{ - int nr_entries; - struct io_table_entry entries[MAX_IO_TABLE]; -}; - -struct io_table_entry *io_table_lookup(struct io_table *io_table, - uint64_t addr); -int io_table_register(struct io_table *io_table, uint64_t start, uint64_t size, - io_table_handler_t *handler, void *opaque); diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/kvmtrace.c qemu-kvm-0.14.1/kvm/user/kvmtrace.c --- qemu-kvm-0.12.5+noroms/kvm/user/kvmtrace.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/kvmtrace.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,706 +0,0 @@ -/* - * kvm tracing application - * - * This tool is used for collecting trace buffer data - * for kvm trace. - * - * Based on blktrace 0.99.3 - * - * Copyright (C) 2005 Jens Axboe - * Copyright (C) 2006 Jens Axboe - * Copyright (C) 2008 Eric Liu - * - * This work is licensed under the GNU LGPL license, version 2. - */ - -#define _GNU_SOURCE - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifndef __user -#define __user -#endif -#include - -static char kvmtrace_version[] = "0.1"; - -/* - * You may want to increase this even more, if you are logging at a high - * rate and see skipped/missed events - */ -#define BUF_SIZE (512 * 1024) -#define BUF_NR (8) - -#define OFILE_BUF (128 * 1024) - -#define DEBUGFS_TYPE 0x64626720 - -#define max(a, b) ((a) > (b) ? (a) : (b)) - -#define S_OPTS "r:o:w:?Vb:n:D:" -static struct option l_opts[] = { - { - .name = "relay", - .has_arg = required_argument, - .flag = NULL, - .val = 'r' - }, - { - .name = "output", - .has_arg = required_argument, - .flag = NULL, - .val = 'o' - }, - { - .name = "stopwatch", - .has_arg = required_argument, - .flag = NULL, - .val = 'w' - }, - { - .name = "version", - .has_arg = no_argument, - .flag = NULL, - .val = 'V' - }, - { - .name = "buffer-size", - .has_arg = required_argument, - .flag = NULL, - .val = 'b' - }, - { - .name = "num-sub-buffers", - .has_arg = required_argument, - .flag = NULL, - .val = 'n' - }, - { - .name = "output-dir", - .has_arg = required_argument, - .flag = NULL, - .val = 'D' - }, - { - .name = NULL, - } -}; - -struct thread_information { - int cpu; - pthread_t thread; - - int fd; - char fn[MAXPATHLEN + 64]; - - FILE *ofile; - char *ofile_buffer; - - int (*get_subbuf)(struct thread_information *, unsigned int); - int (*read_data)(struct thread_information *, void *, unsigned int); - - unsigned long long data_read; - - struct kvm_trace_information *trace_info; - - int exited; - - /* - * mmap controlled output files - */ - unsigned long long fs_size; - unsigned long long fs_max_size; - unsigned long fs_off; - void *fs_buf; - unsigned long fs_buf_len; - -}; - -struct kvm_trace_information { - int fd; - volatile int trace_started; - unsigned long lost_records; - struct thread_information *threads; - unsigned long buf_size; - unsigned long buf_nr; -}; - -static struct kvm_trace_information trace_information; - -static int ncpus; -static char default_debugfs_path[] = "/sys/kernel/debug"; - -/* command line option globals */ -static char *debugfs_path; -static char *output_name; -static char *output_dir; -static int stop_watch; -static unsigned long buf_size = BUF_SIZE; -static unsigned long buf_nr = BUF_NR; -static unsigned int page_size; - -#define for_each_cpu_online(cpu) \ - for (cpu = 0; cpu < ncpus; cpu++) -#define for_each_tip(tip, i) \ - for (i = 0, tip = trace_information.threads; i < ncpus; i++, tip++) - -#define is_done() (*(volatile int *)(&done)) -static volatile int done; - -#define is_trace_stopped() (*(volatile int *)(&trace_stopped)) -static volatile int trace_stopped; - -static void exit_trace(int status); - -static void handle_sigint(__attribute__((__unused__)) int sig) -{ - ioctl(trace_information.fd, KVM_TRACE_PAUSE); - done = 1; -} - -static int get_lost_records() -{ - int fd; - char tmp[MAXPATHLEN + 64]; - - snprintf(tmp, sizeof(tmp), "%s/kvm/lost_records", debugfs_path); - fd = open(tmp, O_RDONLY); - if (fd < 0) { - /* - * this may be ok, if the kernel doesn't support dropped counts - */ - if (errno == ENOENT) - return 0; - - fprintf(stderr, "Couldn't open dropped file %s\n", tmp); - return -1; - } - - if (read(fd, tmp, sizeof(tmp)) < 0) { - perror(tmp); - close(fd); - return -1; - } - close(fd); - - return atoi(tmp); -} - -static void wait_for_data(struct thread_information *tip, int timeout) -{ - struct pollfd pfd = { .fd = tip->fd, .events = POLLIN }; - - while (!is_done()) { - if (poll(&pfd, 1, timeout) < 0) { - perror("poll"); - break; - } - if (pfd.revents & POLLIN) - break; - } -} - -static int read_data(struct thread_information *tip, void *buf, - unsigned int len) -{ - int ret = 0; - - do { - wait_for_data(tip, 100); - - ret = read(tip->fd, buf, len); - - if (!ret) - continue; - else if (ret > 0) - return ret; - else { - if (errno != EAGAIN) { - perror(tip->fn); - fprintf(stderr, "Thread %d failed read of %s\n", - tip->cpu, tip->fn); - break; - } - continue; - } - } while (!is_done()); - - return ret; - -} - -/* - * For file output, truncate and mmap the file appropriately - */ -static int mmap_subbuf(struct thread_information *tip, unsigned int maxlen) -{ - int ofd = fileno(tip->ofile); - int ret; - unsigned long nr; - unsigned long size; - - /* - * extend file, if we have to. use chunks of 16 subbuffers. - */ - if (tip->fs_off + maxlen > tip->fs_buf_len) { - if (tip->fs_buf) { - munlock(tip->fs_buf, tip->fs_buf_len); - munmap(tip->fs_buf, tip->fs_buf_len); - tip->fs_buf = NULL; - } - - tip->fs_off = tip->fs_size & (page_size - 1); - nr = max(16, tip->trace_info->buf_nr); - size = tip->trace_info->buf_size; - tip->fs_buf_len = (nr * size) - tip->fs_off; - tip->fs_max_size += tip->fs_buf_len; - - if (ftruncate(ofd, tip->fs_max_size) < 0) { - perror("ftruncate"); - return -1; - } - - tip->fs_buf = mmap(NULL, tip->fs_buf_len, PROT_WRITE, - MAP_SHARED, ofd, tip->fs_size - tip->fs_off); - if (tip->fs_buf == MAP_FAILED) { - perror("mmap"); - return -1; - } - mlock(tip->fs_buf, tip->fs_buf_len); - } - - ret = tip->read_data(tip, tip->fs_buf + tip->fs_off, maxlen); - if (ret >= 0) { - tip->data_read += ret; - tip->fs_size += ret; - tip->fs_off += ret; - return 0; - } - - return -1; -} - -static void tip_ftrunc_final(struct thread_information *tip) -{ - /* - * truncate to right size and cleanup mmap - */ - if (tip->ofile) { - int ofd = fileno(tip->ofile); - - if (tip->fs_buf) - munmap(tip->fs_buf, tip->fs_buf_len); - - ftruncate(ofd, tip->fs_size); - } -} - -static void *thread_main(void *arg) -{ - struct thread_information *tip = arg; - pid_t pid = getpid(); - cpu_set_t cpu_mask; - - CPU_ZERO(&cpu_mask); - CPU_SET((tip->cpu), &cpu_mask); - - if (sched_setaffinity(pid, sizeof(cpu_mask), &cpu_mask) == -1) { - perror("sched_setaffinity"); - exit_trace(1); - } - - snprintf(tip->fn, sizeof(tip->fn), "%s/kvm/trace%d", - debugfs_path, tip->cpu); - tip->fd = open(tip->fn, O_RDONLY); - if (tip->fd < 0) { - perror(tip->fn); - fprintf(stderr, "Thread %d failed open of %s\n", tip->cpu, - tip->fn); - exit_trace(1); - } - while (!is_done()) { - if (tip->get_subbuf(tip, tip->trace_info->buf_size) < 0) - break; - } - - /* - * trace is stopped, pull data until we get a short read - */ - while (tip->get_subbuf(tip, tip->trace_info->buf_size) > 0) - ; - - tip_ftrunc_final(tip); - tip->exited = 1; - return NULL; -} - -static int fill_ofname(struct thread_information *tip, char *dst) -{ - struct stat sb; - int len = 0; - - if (output_dir) - len = sprintf(dst, "%s/", output_dir); - else - len = sprintf(dst, "./"); - - if (stat(dst, &sb) < 0) { - if (errno != ENOENT) { - perror("stat"); - return 1; - } - if (mkdir(dst, 0755) < 0) { - perror(dst); - fprintf(stderr, "Can't make output dir\n"); - return 1; - } - } - - sprintf(dst + len, "%s.kvmtrace.%d", output_name, tip->cpu); - - return 0; -} - -static void fill_ops(struct thread_information *tip) -{ - tip->get_subbuf = mmap_subbuf; - tip->read_data = read_data; -} - -static void close_thread(struct thread_information *tip) -{ - if (tip->fd != -1) - close(tip->fd); - if (tip->ofile) - fclose(tip->ofile); - if (tip->ofile_buffer) - free(tip->ofile_buffer); - - tip->fd = -1; - tip->ofile = NULL; - tip->ofile_buffer = NULL; -} - -static int tip_open_output(struct thread_information *tip) -{ - int mode, vbuf_size; - char op[NAME_MAX]; - - if (fill_ofname(tip, op)) - return 1; - - tip->ofile = fopen(op, "w+"); - mode = _IOFBF; - vbuf_size = OFILE_BUF; - - if (tip->ofile == NULL) { - perror(op); - return 1; - } - - tip->ofile_buffer = malloc(vbuf_size); - if (setvbuf(tip->ofile, tip->ofile_buffer, mode, vbuf_size)) { - perror("setvbuf"); - close_thread(tip); - return 1; - } - - fill_ops(tip); - return 0; -} - -static int start_threads(int cpu) -{ - struct thread_information *tip; - - tip = trace_information.threads + cpu; - tip->cpu = cpu; - tip->trace_info = &trace_information; - tip->fd = -1; - - if (tip_open_output(tip)) - return 1; - - if (pthread_create(&tip->thread, NULL, thread_main, tip)) { - perror("pthread_create"); - close_thread(tip); - return 1; - } - - return 0; -} - -static void stop_threads() -{ - struct thread_information *tip; - unsigned long ret; - int i; - - for_each_tip(tip, i) { - if (tip->thread) - (void) pthread_join(tip->thread, (void *) &ret); - close_thread(tip); - } -} - -static int start_trace(void) -{ - int fd; - struct kvm_user_trace_setup kuts; - - fd = trace_information.fd = open("/dev/kvm", O_RDWR); - if (fd == -1) { - perror("/dev/kvm"); - return 1; - } - - memset(&kuts, 0, sizeof(kuts)); - kuts.buf_size = trace_information.buf_size = buf_size; - kuts.buf_nr = trace_information.buf_nr = buf_nr; - - if (ioctl(trace_information.fd , KVM_TRACE_ENABLE, &kuts) < 0) { - perror("KVM_TRACE_ENABLE"); - close(fd); - return 1; - } - trace_information.trace_started = 1; - - return 0; -} - -static void cleanup_trace(void) -{ - if (trace_information.fd == -1) - return; - - trace_information.lost_records = get_lost_records(); - - if (trace_information.trace_started) { - trace_information.trace_started = 0; - if (ioctl(trace_information.fd, KVM_TRACE_DISABLE) < 0) - perror("KVM_TRACE_DISABLE"); - } - - close(trace_information.fd); - trace_information.fd = -1; -} - -static void stop_all_traces(void) -{ - if (!is_trace_stopped()) { - trace_stopped = 1; - stop_threads(); - cleanup_trace(); - } -} - -static void exit_trace(int status) -{ - stop_all_traces(); - exit(status); -} - -static int start_kvm_trace(void) -{ - int i, size; - struct thread_information *tip; - - size = ncpus * sizeof(struct thread_information); - tip = malloc(size); - if (!tip) { - fprintf(stderr, "Out of memory, threads (%d)\n", size); - return 1; - } - memset(tip, 0, size); - trace_information.threads = tip; - - if (start_trace()) - return 1; - - for_each_cpu_online(i) { - if (start_threads(i)) { - fprintf(stderr, "Failed to start worker threads\n"); - break; - } - } - - if (i != ncpus) { - stop_threads(); - cleanup_trace(); - return 1; - } - - return 0; -} - -static void wait_for_threads(void) -{ - struct thread_information *tip; - int i, tips_running; - - do { - tips_running = 0; - usleep(100000); - - for_each_tip(tip, i) - tips_running += !tip->exited; - - } while (tips_running); -} - -static void show_stats(void) -{ - struct thread_information *tip; - unsigned long long data_read; - int i; - - data_read = 0; - for_each_tip(tip, i) { - printf(" CPU%3d: %8llu KiB data\n", - tip->cpu, (tip->data_read + 1023) >> 10); - data_read += tip->data_read; - } - - printf(" Total: lost %lu, %8llu KiB data\n", - trace_information.lost_records, (data_read + 1023) >> 10); - - if (trace_information.lost_records) - fprintf(stderr, "You have lost records, " - "consider using a larger buffer size (-b)\n"); -} - -static char usage_str[] = \ - "[ -r debugfs path ] [ -D output dir ] [ -b buffer size ]\n" \ - "[ -n number of buffers] [ -o ] [ -w time ] [ -V ]\n\n" \ - "\t-r Path to mounted debugfs, defaults to /sys/kernel/debug\n" \ - "\t-o File(s) to send output to\n" \ - "\t-D Directory to prepend to output file names\n" \ - "\t-w Stop after defined time, in seconds\n" \ - "\t-b Sub buffer size in KiB\n" \ - "\t-n Number of sub buffers\n" \ - "\t-V Print program version info\n\n"; - -static void show_usage(char *prog) -{ - fprintf(stderr, "Usage: %s %s %s", prog, kvmtrace_version, usage_str); - exit(EXIT_FAILURE); -} - -void parse_args(int argc, char **argv) -{ - int c; - - while ((c = getopt_long(argc, argv, S_OPTS, l_opts, NULL)) >= 0) { - switch (c) { - case 'r': - debugfs_path = optarg; - break; - case 'o': - output_name = optarg; - break; - case 'w': - stop_watch = atoi(optarg); - if (stop_watch <= 0) { - fprintf(stderr, - "Invalid stopwatch value (%d secs)\n", - stop_watch); - exit(EXIT_FAILURE); - } - break; - case 'V': - printf("%s version %s\n", argv[0], kvmtrace_version); - exit(EXIT_SUCCESS); - case 'b': - buf_size = strtoul(optarg, NULL, 10); - if (buf_size <= 0 || buf_size > 16*1024) { - fprintf(stderr, - "Invalid buffer size (%lu)\n", - buf_size); - exit(EXIT_FAILURE); - } - buf_size <<= 10; - break; - case 'n': - buf_nr = strtoul(optarg, NULL, 10); - if (buf_nr <= 0) { - fprintf(stderr, - "Invalid buffer nr (%lu)\n", buf_nr); - exit(EXIT_FAILURE); - } - break; - case 'D': - output_dir = optarg; - break; - default: - show_usage(argv[0]); - } - } - - if (optind < argc || output_name == NULL) - show_usage(argv[0]); -} - -int main(int argc, char *argv[]) -{ - struct statfs st; - - parse_args(argc, argv); - - if (!debugfs_path) - debugfs_path = default_debugfs_path; - - if (statfs(debugfs_path, &st) < 0) { - perror("statfs"); - fprintf(stderr, "%s does not appear to be a valid path\n", - debugfs_path); - return 1; - } else if (st.f_type != (long) DEBUGFS_TYPE) { - fprintf(stderr, "%s does not appear to be a debug filesystem," - " please mount debugfs.\n", - debugfs_path); - return 1; - } - - page_size = getpagesize(); - - ncpus = sysconf(_SC_NPROCESSORS_ONLN); - if (ncpus < 0) { - fprintf(stderr, "sysconf(_SC_NPROCESSORS_ONLN) failed\n"); - return 1; - } - - signal(SIGINT, handle_sigint); - signal(SIGHUP, handle_sigint); - signal(SIGTERM, handle_sigint); - signal(SIGALRM, handle_sigint); - signal(SIGPIPE, SIG_IGN); - - if (start_kvm_trace() != 0) - return 1; - - if (stop_watch) - alarm(stop_watch); - - wait_for_threads(); - stop_all_traces(); - show_stats(); - - return 0; -} diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/kvmtrace_format qemu-kvm-0.14.1/kvm/user/kvmtrace_format --- qemu-kvm-0.12.5+noroms/kvm/user/kvmtrace_format 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/kvmtrace_format 1970-01-01 00:00:00.000000000 +0000 @@ -1,532 +0,0 @@ -#!/usr/bin/env python - -# by Mark Williamson, (C) 2004 Intel Research Cambridge - -# Program for reformatting trace buffer output according to user-supplied rules - -import re, sys, string, signal, struct, os, getopt, operator - -PREFIX = '/usr' -DATADIR = os.path.join(PREFIX, 'share') -KVMDIR = os.path.join(DATADIR, 'kvm') -FORMATS_FILE = os.path.join(KVMDIR, 'formats') - -def usage(): - print >> sys.stderr, \ - "Usage: " + sys.argv[0] + """ defs-file - Parses trace data in binary format, as output by kvmtrace and - reformats it according to the rules in a file of definitions. The - rules in this file should have the format ({ and } show grouping - and are not part of the syntax): - - {event_id}{whitespace}{text format string} - - The textual format string may include format specifiers, such as: - %(ts)d, %(event)d, %(pid)d %(vcpu)d %(1)d, %(2)d, - %(3)d, %(4)d, %(5)d - [ the 'd' format specifier outputs in decimal, alternatively 'x' - will output in hexadecimal and 'o' will output in octal ] - - Which correspond to the event ID, timestamp counter, pid - , vcpu and the 5 data fields from the trace record. There should be - one such rule for each type of event. - Depending on your system and the volume of trace buffer data, - this script may not be able to keep up with the output of kvmtrace - if it is piped directly. In these circumstances you should have - kvmtrace output to a file for processing off-line. - - kvmtrace_format has the following additional switches - -s - if this switch is set additional trace statistics are - created and printed at the end of the output - """ - sys.exit(1) - -def read_defs(defs_file): - defs = {} - - fd = open(defs_file) - - reg = re.compile('(\S+)\s+(\S.*)') - - while True: - line = fd.readline() - if not line: - break - - if line[0] == '#' or line[0] == '\n': - continue - - m = reg.match(line) - - if not m: print >> sys.stderr, "Bad format file" ; sys.exit(1) - - defs[str(eval(m.group(1)))] = m.group(2) - - return defs - -def sighand(x,y): - global interrupted - interrupted = 1 - -# ppc instruction decoding for event type 0x00020019 (PPC_INSTR) -# some globals for statistic summaries -stat_ppc_instr_mnemonic = {}; -stat_ppc_instr_spr = {}; -stat_ppc_instr_dcr = {}; -stat_ppc_instr_tlb = {}; - -def ppc_instr_print_summary(sortedlist, colname): - print "\n\n%14s + %10s" % (colname, "count") - print "%s" % (15*"-"+"+"+11*"-") - sum = 0 - for value, key in sortedlist: - sum += key - print "%14s | %10d" % (value, key) - print "%14s = %10d" % ("sum", sum) - - -def ppc_instr_summary(): - # don't print empty statistics - if stat_ppc_instr_mnemonic: - ppc_instr_print_summary(sorted(stat_ppc_instr_mnemonic.iteritems(), key=operator.itemgetter(1), reverse=True), "mnemonic") - if stat_ppc_instr_spr: - ppc_instr_print_summary(sorted(stat_ppc_instr_spr.iteritems(), key=operator.itemgetter(1), reverse=True), "mnemonic-spr") - if stat_ppc_instr_dcr: - ppc_instr_print_summary(sorted(stat_ppc_instr_dcr.iteritems(), key=operator.itemgetter(1), reverse=True), "mnemonic-dcr") - if stat_ppc_instr_tlb: - ppc_instr_print_summary(sorted(stat_ppc_instr_tlb.iteritems(), key=operator.itemgetter(1), reverse=True), "mnemonic-tlb") - -def get_op(instr): - return (instr >> 26); - -def get_xop(instr): - return (instr >> 1) & 0x3ff; - -def get_sprn(instr): - return ((instr >> 16) & 0x1f) | ((instr >> 6) & 0x3e0) - -def get_dcrn(instr): - return ((instr >> 16) & 0x1f) | ((instr >> 6) & 0x3e0); - -def get_tlbwe_type(instr): - ws = (instr >> 11) & 0x1f; - if ws == 0: - return "PAGEID" - elif ws == 1: - return "XLAT" - elif ws == 2: - return "ATTRIB" - else: - return "UNKNOWN" - -def get_name(instr): - if get_op(instr)==3: - return "trap" - elif get_op(instr)==19: - if get_xop(instr) == 50: - return "rfi" - else: - return "unknown" - elif get_op(instr)==31: - if get_xop(instr) == 83: - return "mfmsr" - - elif get_xop(instr) == 87: - return "lbzx" - - elif get_xop(instr) == 131: - return "wrtee" - - elif get_xop(instr) == 146: - return "mtmsr" - - elif get_xop(instr) == 163: - return "wrteei" - - elif get_xop(instr) == 215: - return "stbx" - - elif get_xop(instr) == 247: - return "stbux" - - elif get_xop(instr) == 279: - return "lhzx" - - elif get_xop(instr) == 311: - return "lhzux" - - elif get_xop(instr) == 323: - return "mfdcr" - - elif get_xop(instr) == 339: - return "mfspr" - - elif get_xop(instr) == 407: - return "sthx" - - elif get_xop(instr) == 439: - return "sthux" - - elif get_xop(instr) == 451: - return "mtdcr" - - elif get_xop(instr) == 467: - return "mtspr" - - elif get_xop(instr) == 470: - return "dcbi" - - elif get_xop(instr) == 534: - return "lwbrx" - - elif get_xop(instr) == 566: - return "tlbsync" - - elif get_xop(instr) == 662: - return "stwbrx" - - elif get_xop(instr) == 978: - return "tlbwe" - - elif get_xop(instr) == 914: - return "tlbsx" - - elif get_xop(instr) == 790: - return "lhbrx" - - elif get_xop(instr) == 918: - return "sthbrx" - - elif get_xop(instr) == 966: - return "iccci" - - else: - return "unknown" - - elif get_op(instr) == 32: - return "lwz" - - elif get_op(instr) == 33: - return "lwzu" - - elif get_op(instr) == 34: - return "lbz" - - elif get_op(instr) == 35: - return "lbzu" - - elif get_op(instr) == 36: - return "stw" - - elif get_op(instr) == 37: - return "stwu" - - elif get_op(instr) == 38: - return "stb" - - elif get_op(instr) == 39: - return "stbu" - - elif get_op(instr) == 40: - return "lhz" - - elif get_op(instr) == 41: - return "lhzu" - - elif get_op(instr) == 44: - return "sth" - - elif get_op(instr) == 45: - return "sthu" - - else: - return "unknown" - -def get_sprn_name(sprn): - if sprn == 0x01a: - return "SRR0" - elif sprn == 0x01b: - return "SRR1" - elif sprn == 0x3b2: - return "MMUCR" - elif sprn == 0x030: - return "PID" - elif sprn == 0x03f: - return "IVPR" - elif sprn == 0x3b3: - return "CCR0" - elif sprn == 0x378: - return "CCR1" - elif sprn == 0x11f: - return "PVR" - elif sprn == 0x03d: - return "DEAR" - elif sprn == 0x03e: - return "ESR" - elif sprn == 0x134: - return "DBCR0" - elif sprn == 0x135: - return "DBCR1" - elif sprn == 0x11c: - return "TBWL" - elif sprn == 0x11d: - return "TBWU" - elif sprn == 0x016: - return "DEC" - elif sprn == 0x150: - return "TSR" - elif sprn == 0x154: - return "TCR" - elif sprn == 0x110: - return "SPRG0" - elif sprn == 0x111: - return "SPRG1" - elif sprn == 0x112: - return "SPRG2" - elif sprn == 0x113: - return "SPRG3" - elif sprn == 0x114: - return "SPRG4" - elif sprn == 0x115: - return "SPRG5" - elif sprn == 0x116: - return "SPRG6" - elif sprn == 0x117: - return "SPRG7" - elif sprn == 0x190: - return "IVOR0" - elif sprn == 0x191: - return "IVOR1" - elif sprn == 0x192: - return "IVOR2" - elif sprn == 0x193: - return "IVOR3" - elif sprn == 0x194: - return "IVOR4" - elif sprn == 0x195: - return "IVOR5" - elif sprn == 0x196: - return "IVOR6" - elif sprn == 0x197: - return "IVOR7" - elif sprn == 0x198: - return "IVOR8" - elif sprn == 0x199: - return "IVOR9" - elif sprn == 0x19a: - return "IVOR10" - elif sprn == 0x19b: - return "IVOR11" - elif sprn == 0x19c: - return "IVOR12" - elif sprn == 0x19d: - return "IVOR13" - elif sprn == 0x19e: - return "IVOR14" - elif sprn == 0x19f: - return "IVOR15" - else: - return "UNKNOWN" - -def get_special(instr): - name = get_name(instr); - if stat_ppc_instr_mnemonic.has_key(name): - stat_ppc_instr_mnemonic[name] += 1 - else: - stat_ppc_instr_mnemonic[name] = 1 - - if get_op(instr) == 31: - if (get_xop(instr) == 339) or (get_xop(instr) == 467): - sprn = get_sprn(instr); - sprn_name = get_sprn_name(sprn); - stat_idx = name+"-"+sprn_name - if stat_ppc_instr_spr.has_key(stat_idx): - stat_ppc_instr_spr[stat_idx] += 1 - else: - stat_ppc_instr_spr[stat_idx] = 1 - return ("- sprn 0x%03x %8s" % (sprn, sprn_name)) - elif (get_xop(instr) == 323 ) or (get_xop(instr) == 451): - dcrn = get_dcrn(instr); - stat_idx = name+"-"+("%04X"%dcrn) - if stat_ppc_instr_dcr.has_key(stat_idx): - stat_ppc_instr_dcr[stat_idx] += 1 - else: - stat_ppc_instr_dcr[stat_idx] = 1 - return ("- dcrn 0x%03x" % dcrn) - elif (get_xop(instr) == 978 ) or (get_xop(instr) == 451): - tlbwe_type = get_tlbwe_type(instr) - stat_idx = name+"-"+tlbwe_type - if stat_ppc_instr_tlb.has_key(stat_idx): - stat_ppc_instr_tlb[stat_idx] += 1 - else: - stat_ppc_instr_tlb[stat_idx] = 1 - return ("- ws -> %8s" % tlbwe_type) - return "" - -##### Main code - -summary = False - -try: - opts, arg = getopt.getopt(sys.argv[1:], "sc:" ) - for opt in opts: - if opt[0] == '-s' : summary = True - -except getopt.GetoptError: - usage() - -signal.signal(signal.SIGTERM, sighand) -signal.signal(signal.SIGHUP, sighand) -signal.signal(signal.SIGINT, sighand) - -interrupted = 0 - -if len(arg) > 0: - defs = read_defs(arg[0]) -else: - defs = read_defs(FORMATS_FILE) - -# structure of trace record (as output by kvmtrace): -# HDR(I) {TSC(Q)} D1(I) D2(I) D3(I) D4(I) D5(I) -# -# HDR consists of EVENT:28:, n_data:3:, ts_in:1: -# pid:32, vcpu_id:32 -# EVENT means Event ID -# n_data means number of data (like D1, D2, ...) -# ts_in means Timestamp data exists(1) or not(0). -# if ts_in == 0, TSC(Q) does not exists. -# -HDRREC = "> sys.stderr, "Bad data file: magic number error." - break; - else: - HDRREC = ">III" - TSCREC = ">Q" - D1REC = ">I" - D2REC = ">II" - D3REC = ">III" - D4REC = ">IIII" - D5REC = ">IIIII" - continue - - line = sys.stdin.read(struct.calcsize(HDRREC)) - if not line: - break - (event, pid, vcpu_id) = struct.unpack(HDRREC, line) - - n_data = event >> 28 & 0x7 - ts_in = event >> 31 - - d1 = 0 - d2 = 0 - d3 = 0 - d4 = 0 - d5 = 0 - - ts = 0 - - if ts_in == 1: - line = sys.stdin.read(struct.calcsize(TSCREC)) - if not line: - break - ts = struct.unpack(TSCREC, line)[0] - if n_data == 1: - line = sys.stdin.read(struct.calcsize(D1REC)) - if not line: - break - d1 = struct.unpack(D1REC, line)[0] - if n_data == 2: - line = sys.stdin.read(struct.calcsize(D2REC)) - if not line: - break - (d1, d2) = struct.unpack(D2REC, line) - if n_data == 3: - line = sys.stdin.read(struct.calcsize(D3REC)) - if not line: - break - (d1, d2, d3) = struct.unpack(D3REC, line) - if n_data == 4: - line = sys.stdin.read(struct.calcsize(D4REC)) - if not line: - break - (d1, d2, d3, d4) = struct.unpack(D4REC, line) - if n_data == 5: - line = sys.stdin.read(struct.calcsize(D5REC)) - if not line: - break - (d1, d2, d3, d4, d5) = struct.unpack(D5REC, line) - - event &= 0x0fffffff - - # provide relative TSC - - if last_ts > 0 and ts_in == 1: - relts = ts - last_ts - else: - relts = 0 - - if ts_in == 1: - last_ts = ts - - args = {'ts' : ts, - 'event' : event, - 'relts': relts, - 'pid' : pid, - 'vcpu' : vcpu_id, - '1' : d1, - '2' : d2, - '3' : d3, - '4' : d4, - '5' : d5 } - - # some event types need more than just formats mapping they are if/elif - # chained here and the last default else is the mapping via formats - if event == 0x00020019: - pdata = (ts, relts, vcpu_id, pid, d1, d2, d3, get_name(d1), get_special(d1)) - print "%d (+%12d) PPC_INSTR vcpu = 0x%08x pid = 0x%08x [ instr = 0x%08x, pc = 0x%08x, emul = %01d, mnemonic = %8s %s" % pdata - else: - try: - if defs.has_key(str(event)): - print defs[str(event)] % args - else: - if defs.has_key(str(0)): print defs[str(0)] % args - except TypeError: - if defs.has_key(str(event)): - print defs[str(event)] - print args - else: - if defs.has_key(str(0)): - print defs[str(0)] - print args - - except IOError, struct.error: sys.exit() - -if summary: - ppc_instr_summary() diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/main.c qemu-kvm-0.14.1/kvm/user/main.c --- qemu-kvm-0.12.5+noroms/kvm/user/main.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/main.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,611 +0,0 @@ -/* - * Kernel-based Virtual Machine test driver - * - * This test driver provides a simple way of testing kvm, without a full - * device model. - * - * Copyright (C) 2006 Qumranet - * - * Authors: - * - * Avi Kivity - * Yaniv Kamay - * - * This work is licensed under the GNU LGPL license, version 2. - */ - -#define _GNU_SOURCE - -#include -#include "test/lib/x86/fake-apic.h" -#include "test/x86/ioram.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "iotable.h" - -static uint8_t ioram[IORAM_LEN]; - -static int gettid(void) -{ - return syscall(__NR_gettid); -} - -static int tkill(int pid, int sig) -{ - return syscall(__NR_tkill, pid, sig); -} - -kvm_context_t kvm; - -#define MAX_VCPUS 4 - -#define IPI_SIGNAL (SIGRTMIN + 4) - -static int ncpus = 1; -static sem_t init_sem; -static __thread int vcpu; -static int apic_ipi_vector = 0xff; -static sigset_t kernel_sigmask; -static sigset_t ipi_sigmask; -static uint64_t memory_size = 128 * 1024 * 1024; - -static struct io_table pio_table; - -struct vcpu_info { - int id; - pid_t tid; - sem_t sipi_sem; -}; - -struct vcpu_info *vcpus; - -static uint32_t apic_sipi_addr; - -static void apic_send_sipi(int vcpu) -{ - sem_post(&vcpus[vcpu].sipi_sem); -} - -static void apic_send_ipi(int vcpu) -{ - struct vcpu_info *v; - - if (vcpu < 0 || vcpu >= ncpus) - return; - v = &vcpus[vcpu]; - tkill(v->tid, IPI_SIGNAL); -} - -static int apic_io(void *opaque, int size, int is_write, - uint64_t addr, uint64_t *value) -{ - if (!is_write) - *value = -1u; - - switch (addr - APIC_BASE) { - case APIC_REG_NCPU: - if (!is_write) - *value = ncpus; - break; - case APIC_REG_ID: - if (!is_write) - *value = vcpu; - break; - case APIC_REG_SIPI_ADDR: - if (!is_write) - *value = apic_sipi_addr; - else - apic_sipi_addr = *value; - break; - case APIC_REG_SEND_SIPI: - if (is_write) - apic_send_sipi(*value); - break; - case APIC_REG_IPI_VECTOR: - if (!is_write) - *value = apic_ipi_vector; - else - apic_ipi_vector = *value; - break; - case APIC_REG_SEND_IPI: - if (is_write) - apic_send_ipi(*value); - break; - } - - return 0; -} - -static int apic_init(void) -{ - return io_table_register(&pio_table, APIC_BASE, - APIC_SIZE, apic_io, NULL); -} - -static int misc_io(void *opaque, int size, int is_write, - uint64_t addr, uint64_t *value) -{ - static int newline = 1; - - if (!is_write) - *value = -1; - - switch (addr) { - case 0xff: // irq injector - if (is_write) { - printf("injecting interrupt 0x%x\n", (uint8_t)*value); - kvm_inject_irq(kvm, 0, *value); - } - break; - case 0xf1: // serial - if (is_write) { - if (newline) - fputs("GUEST: ", stdout); - putchar(*value); - newline = *value == '\n'; - } - break; - case 0xd1: - if (!is_write) - *value = memory_size; - break; - case 0xf4: // exit - if (is_write) - exit(*value); - break; - } - - return 0; -} - -static int misc_init(void) -{ - int err; - - err = io_table_register(&pio_table, 0xff, 1, misc_io, NULL); - if (err < 0) - return err; - - err = io_table_register(&pio_table, 0xf1, 1, misc_io, NULL); - if (err < 0) - return err; - - err = io_table_register(&pio_table, 0xf4, 1, misc_io, NULL); - if (err < 0) - return err; - - return io_table_register(&pio_table, 0xd1, 1, misc_io, NULL); -} - -#define IRQCHIP_IO_BASE 0x2000 - -static int irqchip_io(void *opaque, int size, int is_write, - uint64_t addr, uint64_t *value) -{ - addr -= IRQCHIP_IO_BASE; - - if (is_write) { - kvm_set_irq_level(kvm, addr, *value, NULL); - } - return 0; -} - -static int test_inb(void *opaque, uint16_t addr, uint8_t *value) -{ - struct io_table_entry *entry; - - entry = io_table_lookup(&pio_table, addr); - if (entry) { - uint64_t val; - entry->handler(entry->opaque, 1, 0, addr, &val); - *value = val; - } else { - *value = -1; - printf("inb 0x%x\n", addr); - } - - return 0; -} - -static int test_inw(void *opaque, uint16_t addr, uint16_t *value) -{ - struct io_table_entry *entry; - - entry = io_table_lookup(&pio_table, addr); - if (entry) { - uint64_t val; - entry->handler(entry->opaque, 2, 0, addr, &val); - *value = val; - } else { - *value = -1; - printf("inw 0x%x\n", addr); - } - - return 0; -} - -static int test_inl(void *opaque, uint16_t addr, uint32_t *value) -{ - struct io_table_entry *entry; - - entry = io_table_lookup(&pio_table, addr); - if (entry) { - uint64_t val; - entry->handler(entry->opaque, 4, 0, addr, &val); - *value = val; - } else { - *value = -1; - printf("inl 0x%x\n", addr); - } - - return 0; -} - -static int test_outb(void *opaque, uint16_t addr, uint8_t value) -{ - struct io_table_entry *entry; - - entry = io_table_lookup(&pio_table, addr); - if (entry) { - uint64_t val = value; - entry->handler(entry->opaque, 1, 1, addr, &val); - } else - printf("outb $0x%x, 0x%x\n", value, addr); - - return 0; -} - -static int test_outw(void *opaque, uint16_t addr, uint16_t value) -{ - struct io_table_entry *entry; - - entry = io_table_lookup(&pio_table, addr); - if (entry) { - uint64_t val = value; - entry->handler(entry->opaque, 2, 1, addr, &val); - } else - printf("outw $0x%x, 0x%x\n", value, addr); - - return 0; -} - -static int test_outl(void *opaque, uint16_t addr, uint32_t value) -{ - struct io_table_entry *entry; - - entry = io_table_lookup(&pio_table, addr); - if (entry) { - uint64_t val = value; - entry->handler(entry->opaque, 4, 1, addr, &val); - } else - printf("outl $0x%x, 0x%x\n", value, addr); - - return 0; -} - -#ifdef KVM_CAP_SET_GUEST_DEBUG -static int test_debug(void *opaque, void *vcpu, - struct kvm_debug_exit_arch *arch_info) -{ - printf("test_debug\n"); - return 0; -} -#endif - -static int test_halt(void *opaque, int vcpu) -{ - int n; - - sigwait(&ipi_sigmask, &n); - kvm_inject_irq(kvm, vcpus[vcpu].id, apic_ipi_vector); - return 0; -} - -static int test_io_window(void *opaque) -{ - return 0; -} - -static int test_try_push_interrupts(void *opaque) -{ - return 0; -} - -#ifdef KVM_CAP_USER_NMI -static void test_push_nmi(void *opaque) -{ -} -#endif - -static void test_post_kvm_run(void *opaque, void *vcpu) -{ -} - -static int test_pre_kvm_run(void *opaque, void *vcpu) -{ - return 0; -} - -static int test_mem_read(void *opaque, uint64_t addr, uint8_t *data, int len) -{ - if (addr < IORAM_BASE_PHYS || addr + len > IORAM_BASE_PHYS + IORAM_LEN) - return 1; - memcpy(data, ioram + addr - IORAM_BASE_PHYS, len); - return 0; -} - -static int test_mem_write(void *opaque, uint64_t addr, uint8_t *data, int len) -{ - if (addr < IORAM_BASE_PHYS || addr + len > IORAM_BASE_PHYS + IORAM_LEN) - return 1; - memcpy(ioram + addr - IORAM_BASE_PHYS, data, len); - return 0; -} - -static int test_shutdown(void *opaque, void *env) -{ - printf("shutdown\n"); - kvm_show_regs(kvm, 0); - exit(1); - return 1; -} - -static struct kvm_callbacks test_callbacks = { - .inb = test_inb, - .inw = test_inw, - .inl = test_inl, - .outb = test_outb, - .outw = test_outw, - .outl = test_outl, - .mmio_read = test_mem_read, - .mmio_write = test_mem_write, -#ifdef KVM_CAP_SET_GUEST_DEBUG - .debug = test_debug, -#endif - .halt = test_halt, - .io_window = test_io_window, - .try_push_interrupts = test_try_push_interrupts, -#ifdef KVM_CAP_USER_NMI - .push_nmi = test_push_nmi, -#endif - .post_kvm_run = test_post_kvm_run, - .pre_kvm_run = test_pre_kvm_run, - .shutdown = test_shutdown, -}; - -static void load_file(void *mem, const char *fname) -{ - int r; - int fd; - - fd = open(fname, O_RDONLY); - if (fd == -1) { - perror("open"); - exit(1); - } - while ((r = read(fd, mem, 4096)) != -1 && r != 0) - mem += r; - if (r == -1) { - perror("read"); - exit(1); - } -} - -static void enter_32(kvm_context_t kvm) -{ - struct kvm_regs regs = { - .rsp = 0x80000, /* 512KB */ - .rip = 0x100000, /* 1MB */ - .rflags = 2, - }; - struct kvm_sregs sregs = { - .cs = { 0, -1u, 8, 11, 1, 0, 1, 1, 0, 1, 0, 0 }, - .ds = { 0, -1u, 16, 3, 1, 0, 1, 1, 0, 1, 0, 0 }, - .es = { 0, -1u, 16, 3, 1, 0, 1, 1, 0, 1, 0, 0 }, - .fs = { 0, -1u, 16, 3, 1, 0, 1, 1, 0, 1, 0, 0 }, - .gs = { 0, -1u, 16, 3, 1, 0, 1, 1, 0, 1, 0, 0 }, - .ss = { 0, -1u, 16, 3, 1, 0, 1, 1, 0, 1, 0, 0 }, - - .tr = { 0, 10000, 24, 11, 1, 0, 0, 0, 0, 0, 0, 0 }, - .ldt = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }, - .gdt = { 0, 0 }, - .idt = { 0, 0 }, - .cr0 = 0x37, - .cr3 = 0, - .cr4 = 0, - .efer = 0, - .apic_base = 0, - .interrupt_bitmap = { 0 }, - }; - - kvm_set_regs(kvm, 0, ®s); - kvm_set_sregs(kvm, 0, &sregs); -} - -static void init_vcpu(int n) -{ - sigemptyset(&ipi_sigmask); - sigaddset(&ipi_sigmask, IPI_SIGNAL); - sigprocmask(SIG_UNBLOCK, &ipi_sigmask, NULL); - sigprocmask(SIG_BLOCK, &ipi_sigmask, &kernel_sigmask); - vcpus[n].id = n; - vcpus[n].tid = gettid(); - vcpu = n; - kvm_set_signal_mask(kvm, n, &kernel_sigmask); - sem_post(&init_sem); -} - -static void *do_create_vcpu(void *_n) -{ - int n = (long)_n; - struct kvm_regs regs; - - kvm_create_vcpu(kvm, n); - init_vcpu(n); - sem_wait(&vcpus[n].sipi_sem); - kvm_get_regs(kvm, n, ®s); - regs.rip = apic_sipi_addr; - kvm_set_regs(kvm, n, ®s); - kvm_run(kvm, n, &vcpus[n]); - return NULL; -} - -static void start_vcpu(int n) -{ - pthread_t thread; - - sem_init(&vcpus[n].sipi_sem, 0, 0); - pthread_create(&thread, NULL, do_create_vcpu, (void *)(long)n); -} - -static void usage(const char *progname) -{ - fprintf(stderr, -"Usage: %s [OPTIONS] [bootstrap] flatfile\n" -"KVM test harness.\n" -"\n" -" -s, --smp=NUM create a VM with NUM virtual CPUs\n" -" -p, --protected-mode start VM in protected mode\n" -" -m, --memory=NUM[GMKB] allocate NUM memory for virtual machine. A suffix\n" -" can be used to change the unit (default: `M')\n" -" -h, --help display this help screen and exit\n" -"\n" -"Report bugs to .\n" - , progname); -} - -static void sig_ignore(int sig) -{ - write(1, "boo\n", 4); -} - -int main(int argc, char **argv) -{ - void *vm_mem; - int i; - const char *sopts = "s:phm:"; - struct option lopts[] = { - { "smp", 1, 0, 's' }, - { "protected-mode", 0, 0, 'p' }, - { "memory", 1, 0, 'm' }, - { "help", 0, 0, 'h' }, - { 0 }, - }; - int opt_ind, ch; - bool enter_protected_mode = false; - int nb_args; - char *endptr; - - while ((ch = getopt_long(argc, argv, sopts, lopts, &opt_ind)) != -1) { - switch (ch) { - case 's': - ncpus = atoi(optarg); - break; - case 'p': - enter_protected_mode = true; - break; - case 'm': - memory_size = strtoull(optarg, &endptr, 0); - switch (*endptr) { - case 'G': case 'g': - memory_size <<= 30; - break; - case '\0': - case 'M': case 'm': - memory_size <<= 20; - break; - case 'K': case 'k': - memory_size <<= 10; - break; - default: - fprintf(stderr, - "Unrecongized memory suffix: %c\n", - *endptr); - exit(1); - } - if (memory_size == 0) { - fprintf(stderr, - "Invalid memory size: 0\n"); - exit(1); - } - break; - case 'h': - usage(argv[0]); - exit(0); - case '?': - default: - fprintf(stderr, - "Try `%s --help' for more information.\n", - argv[0]); - exit(1); - } - } - - nb_args = argc - optind; - if (nb_args < 1 || nb_args > 2) { - fprintf(stderr, - "Incorrect number of arguments.\n" - "Try `%s --help' for more information.\n", - argv[0]); - exit(1); - } - - signal(IPI_SIGNAL, sig_ignore); - - vcpus = calloc(ncpus, sizeof *vcpus); - if (!vcpus) { - fprintf(stderr, "calloc failed\n"); - return 1; - } - - kvm = kvm_init(&test_callbacks, 0); - if (!kvm) { - fprintf(stderr, "kvm_init failed\n"); - return 1; - } - if (kvm_create(kvm, memory_size, &vm_mem) < 0) { - kvm_finalize(kvm); - fprintf(stderr, "kvm_create failed\n"); - return 1; - } - - vm_mem = kvm_create_phys_mem(kvm, 0, memory_size, 0, 1); - - if (enter_protected_mode) - enter_32(kvm); - else - load_file(vm_mem + 0xf0000, argv[optind]); - - if (nb_args > 1) - load_file(vm_mem + 0x100000, argv[optind + 1]); - - apic_init(); - misc_init(); - - io_table_register(&pio_table, IRQCHIP_IO_BASE, 0x20, irqchip_io, NULL); - - sem_init(&init_sem, 0, 0); - for (i = 0; i < ncpus; ++i) - start_vcpu(i); - for (i = 0; i < ncpus; ++i) - sem_wait(&init_sem); - - kvm_run(kvm, 0, &vcpus[0]); - - return 0; -} diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/main-ppc.c qemu-kvm-0.14.1/kvm/user/main-ppc.c --- qemu-kvm-0.12.5+noroms/kvm/user/main-ppc.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/main-ppc.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,383 +0,0 @@ -/* - * Kernel-based Virtual Machine test driver - * - * This test driver provides a simple way of testing kvm, without a full - * device model. - * - * Copyright (C) 2006 Qumranet - * Copyright IBM Corp. 2008 - * - * Authors: - * - * Avi Kivity - * Yaniv Kamay - * Hollis Blanchard - * - * This work is licensed under the GNU LGPL license, version 2. - */ - -#define _GNU_SOURCE - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "iotable.h" - -static int gettid(void) -{ - return syscall(__NR_gettid); -} - -kvm_context_t kvm; - -#define IPI_SIGNAL (SIGRTMIN + 4) - -struct io_table mmio_table; - -static int ncpus = 1; -static sem_t exited_sem; -static __thread int vcpu; -static sigset_t kernel_sigmask; -static sigset_t ipi_sigmask; -static uint64_t memory_size = 128 * 1024 * 1024; - -struct vcpu_info { - pid_t tid; -}; - -struct vcpu_info *vcpus; - -/* Must match flat.lds linker script */ -#define VM_TEST_LOAD_ADDRESS 0x100000 - -static int test_debug(void *opaque, void *vcpu) -{ - printf("test_debug\n"); - return 0; -} - -static int test_halt(void *opaque, int vcpu) -{ - int n; - - sigwait(&ipi_sigmask, &n); - return 0; -} - -static int test_io_window(void *opaque) -{ - return 0; -} - -static int test_try_push_interrupts(void *opaque) -{ - return 0; -} - -static void test_post_kvm_run(void *opaque, void *vcpu) -{ -} - -static int test_pre_kvm_run(void *opaque, void *vcpu) -{ - return 0; -} - -static int mmio_handler(void *opaque, int len, int is_write, uint64_t offset, - uint64_t *data) -{ - int r = 0; - - switch (offset) { - case 0: /* putc */ - putc(*(char *)data, stdout); - fflush(stdout); - break; - case 1: /* exit */ - r = *(char *)data; - break; - default: - printf("%s: offset %"PRIx64" len %d data %"PRIx64"\n", - __func__, offset, len, *(uint64_t *)data); - r = -EINVAL; - } - - return r; -} - -static int test_mem_read(void *opaque, uint64_t addr, uint8_t *data, int len) -{ - struct io_table_entry *iodev; - -#if 0 - printf("%s: addr %"PRIx64" len %d\n", __func__, addr, len); -#endif - - iodev = io_table_lookup(&mmio_table, addr); - if (!iodev) { - printf("couldn't find device\n"); - return -ENODEV; - } - - return iodev->handler(iodev->opaque, len, 0, addr - iodev->start, - (uint64_t *)data); -} - -static int test_mem_write(void *opaque, uint64_t addr, uint8_t *data, int len) -{ - struct io_table_entry *iodev; - -#if 0 - printf("%s: addr %"PRIx64" len %d data %"PRIx64"\n", - __func__, addr, len, *(uint64_t *)data); -#endif - - iodev = io_table_lookup(&mmio_table, addr); - if (!iodev) { - printf("couldn't find device\n"); - return -ENODEV; - } - - return iodev->handler(iodev->opaque, len, 1, addr - iodev->start, - (uint64_t *)data); -} - -static int test_dcr_read(int vcpu, uint32_t dcrn, uint32_t *data) -{ - printf("%s: dcrn %04X\n", __func__, dcrn); - *data = 0; - return 0; -} - -static int test_dcr_write(int vcpu, uint32_t dcrn, uint32_t data) -{ - printf("%s: dcrn %04X data %04X\n", __func__, dcrn, data); - return 0; -} - -static struct kvm_callbacks test_callbacks = { - .mmio_read = test_mem_read, - .mmio_write = test_mem_write, - .debug = test_debug, - .halt = test_halt, - .io_window = test_io_window, - .try_push_interrupts = test_try_push_interrupts, - .post_kvm_run = test_post_kvm_run, - .pre_kvm_run = test_pre_kvm_run, - .powerpc_dcr_read = test_dcr_read, - .powerpc_dcr_write = test_dcr_write, -}; - -static unsigned long load_file(void *mem, const char *fname, int inval_icache) -{ - ssize_t r; - int fd; - unsigned long bytes = 0; - - fd = open(fname, O_RDONLY); - if (fd == -1) { - perror("open"); - exit(1); - } - - while ((r = read(fd, mem, 4096)) != -1 && r != 0) { - mem += r; - bytes += r; - } - - if (r == -1) { - perror("read"); - printf("read %d bytes\n", bytes); - exit(1); - } - - return bytes; -} - -#define ICACHE_LINE_SIZE 32 - -void sync_caches(void *mem, unsigned long len) -{ - unsigned long i; - - for (i = 0; i < len; i += ICACHE_LINE_SIZE) - asm volatile ("dcbst %0, %1" : : "g"(mem), "r"(i)); - asm volatile ("sync"); - for (i = 0; i < len; i += ICACHE_LINE_SIZE) - asm volatile ("icbi %0, %1" : : "g"(mem), "r"(i)); - asm volatile ("sync; isync"); -} - -static void init_vcpu(int n) -{ - sigemptyset(&ipi_sigmask); - sigaddset(&ipi_sigmask, IPI_SIGNAL); - sigprocmask(SIG_UNBLOCK, &ipi_sigmask, NULL); - sigprocmask(SIG_BLOCK, &ipi_sigmask, &kernel_sigmask); - vcpus[n].tid = gettid(); - vcpu = n; - kvm_set_signal_mask(kvm, n, &kernel_sigmask); -} - -static void *do_create_vcpu(void *_n) -{ - struct kvm_regs regs; - int n = (long)_n; - - kvm_create_vcpu(kvm, n); - init_vcpu(n); - - kvm_get_regs(kvm, n, ®s); - regs.pc = VM_TEST_LOAD_ADDRESS; - kvm_set_regs(kvm, n, ®s); - - kvm_run(kvm, n, &vcpus[n]); - sem_post(&exited_sem); - return NULL; -} - -static void start_vcpu(int n) -{ - pthread_t thread; - - pthread_create(&thread, NULL, do_create_vcpu, (void *)(long)n); -} - -static void usage(const char *progname) -{ - fprintf(stderr, -"Usage: %s [OPTIONS] [bootstrap] flatfile\n" -"KVM test harness.\n" -"\n" -" -s, --smp=NUM create a VM with NUM virtual CPUs\n" -" -m, --memory=NUM[GMKB] allocate NUM memory for virtual machine. A suffix\n" -" can be used to change the unit (default: `M')\n" -" -h, --help display this help screen and exit\n" -"\n" -"Report bugs to .\n" - , progname); -} - -static void sig_ignore(int sig) -{ - write(1, "boo\n", 4); -} - -int main(int argc, char **argv) -{ - void *vm_mem; - unsigned long len; - int i; - const char *sopts = "s:phm:"; - struct option lopts[] = { - { "smp", 1, 0, 's' }, - { "memory", 1, 0, 'm' }, - { "help", 0, 0, 'h' }, - { 0 }, - }; - int opt_ind, ch; - int nb_args; - char *endptr; - - while ((ch = getopt_long(argc, argv, sopts, lopts, &opt_ind)) != -1) { - switch (ch) { - case 's': - ncpus = atoi(optarg); - break; - case 'm': - memory_size = strtoull(optarg, &endptr, 0); - switch (*endptr) { - case 'G': case 'g': - memory_size <<= 30; - break; - case '\0': - case 'M': case 'm': - memory_size <<= 20; - break; - case 'K': case 'k': - memory_size <<= 10; - break; - default: - fprintf(stderr, - "Unrecongized memory suffix: %c\n", - *endptr); - exit(1); - } - if (memory_size == 0) { - fprintf(stderr, - "Invalid memory size: 0\n"); - exit(1); - } - break; - case 'h': - usage(argv[0]); - exit(0); - case '?': - default: - fprintf(stderr, - "Try `%s --help' for more information.\n", - argv[0]); - exit(1); - } - } - - nb_args = argc - optind; - if (nb_args < 1 || nb_args > 2) { - fprintf(stderr, - "Incorrect number of arguments.\n" - "Try `%s --help' for more information.\n", - argv[0]); - exit(1); - } - - signal(IPI_SIGNAL, sig_ignore); - - vcpus = calloc(ncpus, sizeof *vcpus); - if (!vcpus) { - fprintf(stderr, "calloc failed\n"); - return 1; - } - - kvm = kvm_init(&test_callbacks, 0); - if (!kvm) { - fprintf(stderr, "kvm_init failed\n"); - return 1; - } - if (kvm_create(kvm, memory_size, &vm_mem) < 0) { - kvm_finalize(kvm); - fprintf(stderr, "kvm_create failed\n"); - return 1; - } - - vm_mem = kvm_create_phys_mem(kvm, 0, memory_size, 0, 1); - - len = load_file(vm_mem + VM_TEST_LOAD_ADDRESS, argv[optind], 1); - sync_caches(vm_mem + VM_TEST_LOAD_ADDRESS, len); - - io_table_register(&mmio_table, 0xf0000000, 64, mmio_handler, NULL); - - sem_init(&exited_sem, 0, 0); - for (i = 0; i < ncpus; ++i) - start_vcpu(i); - /* Wait for all vcpus to exit. */ - for (i = 0; i < ncpus; ++i) - sem_wait(&exited_sem); - - return 0; -} diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/Makefile qemu-kvm-0.14.1/kvm/user/Makefile --- qemu-kvm-0.12.5+noroms/kvm/user/Makefile 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/Makefile 1970-01-01 00:00:00.000000000 +0000 @@ -1,60 +0,0 @@ - -include config.mak - -DESTDIR := - -.PHONY: arch_clean clean - -#make sure env CFLAGS variable is not used -CFLAGS = - -libgcc := $(shell $(CC) --print-libgcc-file-name) - -libcflat := test/lib/libcflat.a -cflatobjs := \ - test/lib/panic.o \ - test/lib/printf.o \ - test/lib/string.o - -#include architecure specific make rules -include config-$(ARCH).mak - -# cc-option -# Usage: OP_CFLAGS+=$(call cc-option, -falign-functions=0, -malign-functions=0) - -cc-option = $(shell if $(CC) $(1) -S -o /dev/null -xc /dev/null \ - > /dev/null 2>&1; then echo "$(1)"; else echo "$(2)"; fi ;) - -CFLAGS += -O1 -CFLAGS += $(autodepend-flags) -g -fomit-frame-pointer -Wall -CFLAGS += $(call cc-option, -fno-stack-protector, "") -CFLAGS += $(call cc-option, -fno-stack-protector-all, "") -CFLAGS += -I../include -CFLAGS += -I ../libkvm - -LDFLAGS += $(CFLAGS) -L ../libkvm - -CXXFLAGS = $(autodepend-flags) - -autodepend-flags = -MMD -MF $(dir $*).$(notdir $*).d - -LDFLAGS += -pthread -lrt - -kvmtrace_objs= kvmtrace.o - -kvmctl: $(kvmctl_objs) - $(CC) $(LDFLAGS) $^ -o $@ - -kvmtrace: $(kvmtrace_objs) - $(CC) $(LDFLAGS) $^ -o $@ - -$(libcflat): $(cflatobjs) - $(AR) rcs $@ $^ - -%.o: %.S - $(CC) $(CFLAGS) -c -nostdlib -o $@ $< - --include .*.d - -clean: arch_clean - $(RM) kvmctl kvmtrace *.o *.a .*.d $(libcflat) $(cflatobjs) diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/test/lib/libcflat.h qemu-kvm-0.14.1/kvm/user/test/lib/libcflat.h --- qemu-kvm-0.12.5+noroms/kvm/user/test/lib/libcflat.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/test/lib/libcflat.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,42 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License, version 2, as - * published by the Free Software Foundation. - * - * 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, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * Copyright IBM Corp. 2008 - * - * Authors: Hollis Blanchard - */ - -#ifndef __LIBCFLAT_H -#define __LIBCFLAT_H - -#include - -typedef unsigned char u8; -typedef unsigned short u16; -typedef unsigned u32; -typedef unsigned long ulong; -typedef unsigned long long u64; - -extern void exit(int code); -extern void panic(char *fmt, ...); - -extern unsigned long strlen(const char *buf); -extern char *strcat(char *dest, const char *src); - -extern int printf(const char *fmt, ...); -extern int vsnprintf(char *buf, int size, const char *fmt, va_list va); - -extern void puts(const char *s); - -#endif diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/test/lib/panic.c qemu-kvm-0.14.1/kvm/user/test/lib/panic.c --- qemu-kvm-0.12.5+noroms/kvm/user/test/lib/panic.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/test/lib/panic.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -#include "libcflat.h" - -void panic(char *fmt, ...) -{ - va_list va; - char buf[2000]; - - va_start(va, fmt); - vsnprintf(buf, sizeof(buf), fmt, va); - va_end(va); - puts(buf); - exit(-1); -} diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/test/lib/powerpc/44x/map.c qemu-kvm-0.14.1/kvm/user/test/lib/powerpc/44x/map.c --- qemu-kvm-0.12.5+noroms/kvm/user/test/lib/powerpc/44x/map.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/test/lib/powerpc/44x/map.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,51 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License, version 2, as - * published by the Free Software Foundation. - * - * 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, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * Copyright IBM Corp. 2008 - * - * Authors: Hollis Blanchard - */ - -#include "libcflat.h" - -#define TLB_SIZE 64 - -extern void tlbwe(unsigned int index, - unsigned char tid, - unsigned int word0, - unsigned int word1, - unsigned int word2); - -unsigned int next_free_index; - -#define PAGE_SHIFT 12 -#define PAGE_MASK (~((1<= TLB_SIZE) - panic("TLB overflow"); - - w0 = (vaddr & PAGE_MASK) | V; - w1 = paddr & PAGE_MASK; - w2 = 0x3; - - tlbwe(next_free_index, 0, w0, w1, w2); -} diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/test/lib/powerpc/44x/timebase.h qemu-kvm-0.14.1/kvm/user/test/lib/powerpc/44x/timebase.h --- qemu-kvm-0.12.5+noroms/kvm/user/test/lib/powerpc/44x/timebase.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/test/lib/powerpc/44x/timebase.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,25 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License, version 2, as - * published by the Free Software Foundation. - * - * 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, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * Copyright IBM Corp. 2008 - * - * Authors: Hollis Blanchard - */ - -#ifndef __TIMEBASE_H__ -#define __TIMEBASE_H__ - -unsigned long long mftb(void); - -#endif /* __TIMEBASE_H__ */ diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/test/lib/powerpc/44x/timebase.S qemu-kvm-0.14.1/kvm/user/test/lib/powerpc/44x/timebase.S --- qemu-kvm-0.12.5+noroms/kvm/user/test/lib/powerpc/44x/timebase.S 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/test/lib/powerpc/44x/timebase.S 1970-01-01 00:00:00.000000000 +0000 @@ -1,28 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License, version 2, as - * published by the Free Software Foundation. - * - * 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, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * Copyright IBM Corp. 2008 - * - * Authors: Hollis Blanchard - */ - -/* unsigned long long mftb(void); */ -.global mftb -mftb: - mftbu r5 - mftbl r4 - mftbu r3 - cmpw r3, r5 - bne mftb - blr diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/test/lib/powerpc/44x/tlbwe.S qemu-kvm-0.14.1/kvm/user/test/lib/powerpc/44x/tlbwe.S --- qemu-kvm-0.12.5+noroms/kvm/user/test/lib/powerpc/44x/tlbwe.S 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/test/lib/powerpc/44x/tlbwe.S 1970-01-01 00:00:00.000000000 +0000 @@ -1,29 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License, version 2, as - * published by the Free Software Foundation. - * - * 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, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * Copyright IBM Corp. 2008 - * - * Authors: Hollis Blanchard - */ - -#define SPRN_MMUCR 0x3b2 - -/* tlbwe(uint index, uint8_t tid, uint word0, uint word1, uint word2) */ -.global tlbwe -tlbwe: - mtspr SPRN_MMUCR, r4 - tlbwe r5, r3, 0 - tlbwe r6, r3, 1 - tlbwe r7, r3, 2 - blr diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/test/lib/powerpc/io.c qemu-kvm-0.14.1/kvm/user/test/lib/powerpc/io.c --- qemu-kvm-0.12.5+noroms/kvm/user/test/lib/powerpc/io.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/test/lib/powerpc/io.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,35 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License, version 2, as - * published by the Free Software Foundation. - * - * 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, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * Copyright IBM Corp. 2008 - * - * Authors: Hollis Blanchard - */ - -#include "libcflat.h" - -#define BASE 0xf0000000 -#define _putc ((volatile char *)(BASE)) -#define _exit ((volatile char *)(BASE+1)) - -void puts(const char *s) -{ - while (*s != '\0') - *_putc = *s++; -} - -void exit(int code) -{ - *_exit = code; -} diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/test/lib/printf.c qemu-kvm-0.14.1/kvm/user/test/lib/printf.c --- qemu-kvm-0.12.5+noroms/kvm/user/test/lib/printf.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/test/lib/printf.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,179 +0,0 @@ -#include "libcflat.h" - -typedef struct pstream { - char *buffer; - int remain; - int added; -} pstream_t; - -static void addchar(pstream_t *p, char c) -{ - if (p->remain) { - *p->buffer++ = c; - --p->remain; - } - ++p->added; -} - -void print_str(pstream_t *p, const char *s) -{ - while (*s) - addchar(p, *s++); -} - -static char digits[16] = "0123456789abcdef"; - -void print_int(pstream_t *ps, long long n, int base) -{ - char buf[sizeof(long) * 3 + 2], *p = buf; - int s = 0, i; - - if (n < 0) { - n = -n; - s = 1; - } - - while (n) { - *p++ = digits[n % base]; - n /= base; - } - - if (s) - *p++ = '-'; - - if (p == buf) - *p++ = '0'; - - for (i = 0; i < (p - buf) / 2; ++i) { - char tmp; - - tmp = buf[i]; - buf[i] = p[-1-i]; - p[-1-i] = tmp; - } - - *p = 0; - - print_str(ps, buf); -} - -void print_unsigned(pstream_t *ps, unsigned long long n, int base) -{ - char buf[sizeof(long) * 3 + 1], *p = buf; - int i; - - while (n) { - *p++ = digits[n % base]; - n /= base; - } - - if (p == buf) - *p++ = '0'; - - for (i = 0; i < (p - buf) / 2; ++i) { - char tmp; - - tmp = buf[i]; - buf[i] = p[-1-i]; - p[-1-i] = tmp; - } - - *p = 0; - - print_str(ps, buf); -} - -int vsnprintf(char *buf, int size, const char *fmt, va_list va) -{ - pstream_t s; - - s.buffer = buf; - s.remain = size - 1; - s.added = 0; - while (*fmt) { - char f = *fmt++; - int nlong = 0; - - if (f != '%') { - addchar(&s, f); - continue; - } - morefmt: - f = *fmt++; - switch (f) { - case '%': - addchar(&s, '%'); - break; - case '\0': - --fmt; - break; - case 'l': - ++nlong; - goto morefmt; - case 'd': - switch (nlong) { - case 0: - print_int(&s, va_arg(va, int), 10); - break; - case 1: - print_int(&s, va_arg(va, long), 10); - break; - default: - print_int(&s, va_arg(va, long long), 10); - break; - } - break; - case 'x': - switch (nlong) { - case 0: - print_unsigned(&s, va_arg(va, unsigned), 16); - break; - case 1: - print_unsigned(&s, va_arg(va, unsigned long), 16); - break; - default: - print_unsigned(&s, va_arg(va, unsigned long long), 16); - break; - } - break; - case 'p': - print_str(&s, "0x"); - print_unsigned(&s, (unsigned long)va_arg(va, void *), 16); - break; - case 's': - print_str(&s, va_arg(va, const char *)); - break; - default: - addchar(&s, f); - break; - } - } - *s.buffer = 0; - ++s.added; - return s.added; -} - - -int snprintf(char *buf, int size, const char *fmt, ...) -{ - va_list va; - int r; - - va_start(va, fmt); - r = vsnprintf(buf, size, fmt, va); - va_end(va); - return r; -} - -int printf(const char *fmt, ...) -{ - va_list va; - char buf[2000]; - int r; - - va_start(va, fmt); - r = vsnprintf(buf, sizeof buf, fmt, va); - va_end(va); - puts(buf); - return r; -} diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/test/lib/string.c qemu-kvm-0.14.1/kvm/user/test/lib/string.c --- qemu-kvm-0.12.5+noroms/kvm/user/test/lib/string.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/test/lib/string.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,21 +0,0 @@ -#include "libcflat.h" - -unsigned long strlen(const char *buf) -{ - unsigned long len = 0; - - while (*buf++) - ++len; - return len; -} - -char *strcat(char *dest, const char *src) -{ - char *p = dest; - - while (*p) - ++p; - while ((*p++ = *src++) != 0) - ; - return dest; -} diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/test/lib/x86/apic.c qemu-kvm-0.14.1/kvm/user/test/lib/x86/apic.c --- qemu-kvm-0.12.5+noroms/kvm/user/test/lib/x86/apic.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/test/lib/x86/apic.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,143 +0,0 @@ -#include "libcflat.h" -#include "apic.h" - -static void *g_apic = (void *)0xfee00000; -static void *g_ioapic = (void *)0xfec00000; - -struct apic_ops { - u32 (*reg_read)(unsigned reg); - void (*reg_write)(unsigned reg, u32 val); - void (*icr_write)(u32 val, u32 dest); - u32 (*id)(void); -}; - -static void outb(unsigned char data, unsigned short port) -{ - asm volatile ("out %0, %1" : : "a"(data), "d"(port)); -} - -static u32 xapic_read(unsigned reg) -{ - return *(volatile u32 *)(g_apic + reg); -} - -static void xapic_write(unsigned reg, u32 val) -{ - *(volatile u32 *)(g_apic + reg) = val; -} - -static void xapic_icr_write(u32 val, u32 dest) -{ - while (xapic_read(APIC_ICR) & APIC_ICR_BUSY) - ; - xapic_write(APIC_ICR2, dest << 24); - xapic_write(APIC_ICR, val); -} - -static uint32_t xapic_id(void) -{ - return xapic_read(APIC_ID) >> 24; -} - -static const struct apic_ops xapic_ops = { - .reg_read = xapic_read, - .reg_write = xapic_write, - .icr_write = xapic_icr_write, - .id = xapic_id, -}; - -static const struct apic_ops *apic_ops = &xapic_ops; - -static u32 x2apic_read(unsigned reg) -{ - unsigned a, d; - - asm volatile ("rdmsr" : "=a"(a), "=d"(d) : "c"(APIC_BASE_MSR + reg/16)); - return a | (u64)d << 32; -} - -static void x2apic_write(unsigned reg, u32 val) -{ - asm volatile ("wrmsr" : : "a"(val), "d"(0), "c"(APIC_BASE_MSR + reg/16)); -} - -static void x2apic_icr_write(u32 val, u32 dest) -{ - asm volatile ("wrmsr" : : "a"(val), "d"(dest), - "c"(APIC_BASE_MSR + APIC_ICR/16)); -} - -static uint32_t x2apic_id(void) -{ - return xapic_read(APIC_ID); -} - -static const struct apic_ops x2apic_ops = { - .reg_read = x2apic_read, - .reg_write = x2apic_write, - .icr_write = x2apic_icr_write, - .id = x2apic_id, -}; - -u32 apic_read(unsigned reg) -{ - return apic_ops->reg_read(reg); -} - -void apic_write(unsigned reg, u32 val) -{ - apic_ops->reg_write(reg, val); -} - -void apic_icr_write(u32 val, u32 dest) -{ - apic_ops->icr_write(val, dest); -} - -uint32_t apic_id(void) -{ - return apic_ops->id(); -} - -#define MSR_APIC_BASE 0x0000001b - -int enable_x2apic(void) -{ - unsigned a, b, c, d; - - asm ("cpuid" : "=a"(a), "=b"(b), "=c"(c), "=d"(d) : "0"(1)); - - if (c & (1 << 21)) { - asm ("rdmsr" : "=a"(a), "=d"(d) : "c"(MSR_APIC_BASE)); - a |= 1 << 10; - asm ("wrmsr" : : "a"(a), "d"(d), "c"(MSR_APIC_BASE)); - apic_ops = &x2apic_ops; - return 1; - } else { - return 0; - } -} - -void ioapic_write_reg(unsigned reg, u32 value) -{ - *(volatile u32 *)g_ioapic = reg; - *(volatile u32 *)(g_ioapic + 0x10) = value; -} - -void ioapic_write_redir(unsigned line, ioapic_redir_entry_t e) -{ - ioapic_write_reg(0x10 + line * 2 + 0, ((u32 *)&e)[0]); - ioapic_write_reg(0x10 + line * 2 + 1, ((u32 *)&e)[1]); -} - -void enable_apic(void) -{ - printf("enabling apic\n"); - xapic_write(0xf0, 0x1ff); /* spurious vector register */ -} - -void mask_pic_interrupts(void) -{ - outb(0xff, 0x21); - outb(0xff, 0xa1); -} diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/test/lib/x86/apic-defs.h qemu-kvm-0.14.1/kvm/user/test/lib/x86/apic-defs.h --- qemu-kvm-0.12.5+noroms/kvm/user/test/lib/x86/apic-defs.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/test/lib/x86/apic-defs.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,133 +0,0 @@ -#ifndef _ASM_X86_APICDEF_H -#define _ASM_X86_APICDEF_H - -/* - * Constants for various Intel APICs. (local APIC, IOAPIC, etc.) - * - * Alan Cox , 1995. - * Ingo Molnar , 1999, 2000 - */ - -#define APIC_DEFAULT_PHYS_BASE 0xfee00000 - -#define APIC_ID 0x20 - -#define APIC_LVR 0x30 -#define APIC_LVR_MASK 0xFF00FF -#define GET_APIC_VERSION(x) ((x) & 0xFFu) -#define GET_APIC_MAXLVT(x) (((x) >> 16) & 0xFFu) -#ifdef CONFIG_X86_32 -# define APIC_INTEGRATED(x) ((x) & 0xF0u) -#else -# define APIC_INTEGRATED(x) (1) -#endif -#define APIC_XAPIC(x) ((x) >= 0x14) -#define APIC_TASKPRI 0x80 -#define APIC_TPRI_MASK 0xFFu -#define APIC_ARBPRI 0x90 -#define APIC_ARBPRI_MASK 0xFFu -#define APIC_PROCPRI 0xA0 -#define APIC_EOI 0xB0 -#define APIC_EIO_ACK 0x0 -#define APIC_RRR 0xC0 -#define APIC_LDR 0xD0 -#define APIC_LDR_MASK (0xFFu << 24) -#define GET_APIC_LOGICAL_ID(x) (((x) >> 24) & 0xFFu) -#define SET_APIC_LOGICAL_ID(x) (((x) << 24)) -#define APIC_ALL_CPUS 0xFFu -#define APIC_DFR 0xE0 -#define APIC_DFR_CLUSTER 0x0FFFFFFFul -#define APIC_DFR_FLAT 0xFFFFFFFFul -#define APIC_SPIV 0xF0 -#define APIC_SPIV_FOCUS_DISABLED (1 << 9) -#define APIC_SPIV_APIC_ENABLED (1 << 8) -#define APIC_ISR 0x100 -#define APIC_ISR_NR 0x8 /* Number of 32 bit ISR registers. */ -#define APIC_TMR 0x180 -#define APIC_IRR 0x200 -#define APIC_ESR 0x280 -#define APIC_ESR_SEND_CS 0x00001 -#define APIC_ESR_RECV_CS 0x00002 -#define APIC_ESR_SEND_ACC 0x00004 -#define APIC_ESR_RECV_ACC 0x00008 -#define APIC_ESR_SENDILL 0x00020 -#define APIC_ESR_RECVILL 0x00040 -#define APIC_ESR_ILLREGA 0x00080 -#define APIC_ICR 0x300 -#define APIC_DEST_SELF 0x40000 -#define APIC_DEST_ALLINC 0x80000 -#define APIC_DEST_ALLBUT 0xC0000 -#define APIC_ICR_RR_MASK 0x30000 -#define APIC_ICR_RR_INVALID 0x00000 -#define APIC_ICR_RR_INPROG 0x10000 -#define APIC_ICR_RR_VALID 0x20000 -#define APIC_INT_LEVELTRIG 0x08000 -#define APIC_INT_ASSERT 0x04000 -#define APIC_ICR_BUSY 0x01000 -#define APIC_DEST_LOGICAL 0x00800 -#define APIC_DEST_PHYSICAL 0x00000 -#define APIC_DM_FIXED 0x00000 -#define APIC_DM_LOWEST 0x00100 -#define APIC_DM_SMI 0x00200 -#define APIC_DM_REMRD 0x00300 -#define APIC_DM_NMI 0x00400 -#define APIC_DM_INIT 0x00500 -#define APIC_DM_STARTUP 0x00600 -#define APIC_DM_EXTINT 0x00700 -#define APIC_VECTOR_MASK 0x000FF -#define APIC_ICR2 0x310 -#define GET_APIC_DEST_FIELD(x) (((x) >> 24) & 0xFF) -#define SET_APIC_DEST_FIELD(x) ((x) << 24) -#define APIC_LVTT 0x320 -#define APIC_LVTTHMR 0x330 -#define APIC_LVTPC 0x340 -#define APIC_LVT0 0x350 -#define APIC_LVT_TIMER_BASE_MASK (0x3 << 18) -#define GET_APIC_TIMER_BASE(x) (((x) >> 18) & 0x3) -#define SET_APIC_TIMER_BASE(x) (((x) << 18)) -#define APIC_TIMER_BASE_CLKIN 0x0 -#define APIC_TIMER_BASE_TMBASE 0x1 -#define APIC_TIMER_BASE_DIV 0x2 -#define APIC_LVT_TIMER_PERIODIC (1 << 17) -#define APIC_LVT_MASKED (1 << 16) -#define APIC_LVT_LEVEL_TRIGGER (1 << 15) -#define APIC_LVT_REMOTE_IRR (1 << 14) -#define APIC_INPUT_POLARITY (1 << 13) -#define APIC_SEND_PENDING (1 << 12) -#define APIC_MODE_MASK 0x700 -#define GET_APIC_DELIVERY_MODE(x) (((x) >> 8) & 0x7) -#define SET_APIC_DELIVERY_MODE(x, y) (((x) & ~0x700) | ((y) << 8)) -#define APIC_MODE_FIXED 0x0 -#define APIC_MODE_NMI 0x4 -#define APIC_MODE_EXTINT 0x7 -#define APIC_LVT1 0x360 -#define APIC_LVTERR 0x370 -#define APIC_TMICT 0x380 -#define APIC_TMCCT 0x390 -#define APIC_TDCR 0x3E0 -#define APIC_SELF_IPI 0x3F0 -#define APIC_TDR_DIV_TMBASE (1 << 2) -#define APIC_TDR_DIV_1 0xB -#define APIC_TDR_DIV_2 0x0 -#define APIC_TDR_DIV_4 0x1 -#define APIC_TDR_DIV_8 0x2 -#define APIC_TDR_DIV_16 0x3 -#define APIC_TDR_DIV_32 0x8 -#define APIC_TDR_DIV_64 0x9 -#define APIC_TDR_DIV_128 0xA -#define APIC_EILVT0 0x500 -#define APIC_EILVT_NR_AMD_K8 1 /* # of extended interrupts */ -#define APIC_EILVT_NR_AMD_10H 4 -#define APIC_EILVT_LVTOFF(x) (((x) >> 4) & 0xF) -#define APIC_EILVT_MSG_FIX 0x0 -#define APIC_EILVT_MSG_SMI 0x2 -#define APIC_EILVT_MSG_NMI 0x4 -#define APIC_EILVT_MSG_EXT 0x7 -#define APIC_EILVT_MASKED (1 << 16) -#define APIC_EILVT1 0x510 -#define APIC_EILVT2 0x520 -#define APIC_EILVT3 0x530 - -#define APIC_BASE_MSR 0x800 - -#endif /* _ASM_X86_APICDEF_H */ diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/test/lib/x86/apic.h qemu-kvm-0.14.1/kvm/user/test/lib/x86/apic.h --- qemu-kvm-0.12.5+noroms/kvm/user/test/lib/x86/apic.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/test/lib/x86/apic.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ -#ifndef CFLAT_APIC_H -#define CFLAT_APIC_H - -#include -#include "apic-defs.h" - -typedef struct { - uint8_t vector; - uint8_t delivery_mode:3; - uint8_t dest_mode:1; - uint8_t delivery_status:1; - uint8_t polarity:1; - uint8_t remote_irr:1; - uint8_t trig_mode:1; - uint8_t mask:1; - uint8_t reserve:7; - uint8_t reserved[4]; - uint8_t dest_id; -} ioapic_redir_entry_t; - -void mask_pic_interrupts(void); - -void ioapic_write_redir(unsigned line, ioapic_redir_entry_t e); -void ioapic_write_reg(unsigned reg, uint32_t value); - -void enable_apic(void); -uint32_t apic_read(unsigned reg); -void apic_write(unsigned reg, uint32_t val); -void apic_icr_write(uint32_t val, uint32_t dest); -uint32_t apic_id(void); - -int enable_x2apic(void); - -#endif diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/test/lib/x86/fake-apic.h qemu-kvm-0.14.1/kvm/user/test/lib/x86/fake-apic.h --- qemu-kvm-0.12.5+noroms/kvm/user/test/lib/x86/fake-apic.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/test/lib/x86/fake-apic.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,14 +0,0 @@ -#ifndef SILLY_APIC_H -#define SILLY_APIC_H - -#define APIC_BASE 0x1000 -#define APIC_SIZE 0x100 - -#define APIC_REG_NCPU 0x00 -#define APIC_REG_ID 0x04 -#define APIC_REG_SIPI_ADDR 0x08 -#define APIC_REG_SEND_SIPI 0x0c -#define APIC_REG_IPI_VECTOR 0x10 -#define APIC_REG_SEND_IPI 0x14 - -#endif diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/test/lib/x86/fwcfg.c qemu-kvm-0.14.1/kvm/user/test/lib/x86/fwcfg.c --- qemu-kvm-0.12.5+noroms/kvm/user/test/lib/x86/fwcfg.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/test/lib/x86/fwcfg.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,40 +0,0 @@ -#include "fwcfg.h" - -uint64_t fwcfg_get_u(uint16_t index, int bytes) -{ - uint64_t r = 0; - uint8_t b; - int i; - - asm volatile ("out %0, %1" : : "a"(index), "d"((uint16_t)BIOS_CFG_IOPORT)); - for (i = 0; i < bytes; ++i) { - asm volatile ("in %1, %0" : "=a"(b) : "d"((uint16_t)(BIOS_CFG_IOPORT + 1))); - r |= (uint64_t)b << (i * 8); - } - return r; -} - -uint8_t fwcfg_get_u8(unsigned index) -{ - return fwcfg_get_u(index, 1); -} - -uint16_t fwcfg_get_u16(unsigned index) -{ - return fwcfg_get_u(index, 2); -} - -uint32_t fwcfg_get_u32(unsigned index) -{ - return fwcfg_get_u(index, 4); -} - -uint64_t fwcfg_get_u64(unsigned index) -{ - return fwcfg_get_u(index, 8); -} - -unsigned fwcfg_get_nb_cpus(void) -{ - return fwcfg_get_u16(FW_CFG_NB_CPUS); -} diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/test/lib/x86/fwcfg.h qemu-kvm-0.14.1/kvm/user/test/lib/x86/fwcfg.h --- qemu-kvm-0.12.5+noroms/kvm/user/test/lib/x86/fwcfg.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/test/lib/x86/fwcfg.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ -#ifndef FWCFG_H -#define FWCFG_H - -#include - -#define FW_CFG_SIGNATURE 0x00 -#define FW_CFG_ID 0x01 -#define FW_CFG_UUID 0x02 -#define FW_CFG_RAM_SIZE 0x03 -#define FW_CFG_NOGRAPHIC 0x04 -#define FW_CFG_NB_CPUS 0x05 -#define FW_CFG_MACHINE_ID 0x06 -#define FW_CFG_KERNEL_ADDR 0x07 -#define FW_CFG_KERNEL_SIZE 0x08 -#define FW_CFG_KERNEL_CMDLINE 0x09 -#define FW_CFG_INITRD_ADDR 0x0a -#define FW_CFG_INITRD_SIZE 0x0b -#define FW_CFG_BOOT_DEVICE 0x0c -#define FW_CFG_NUMA 0x0d -#define FW_CFG_BOOT_MENU 0x0e -#define FW_CFG_MAX_CPUS 0x0f -#define FW_CFG_MAX_ENTRY 0x10 - -#define FW_CFG_WRITE_CHANNEL 0x4000 -#define FW_CFG_ARCH_LOCAL 0x8000 -#define FW_CFG_ENTRY_MASK ~(FW_CFG_WRITE_CHANNEL | FW_CFG_ARCH_LOCAL) - -#define FW_CFG_INVALID 0xffff - -#define BIOS_CFG_IOPORT 0x510 - -#define FW_CFG_ACPI_TABLES (FW_CFG_ARCH_LOCAL + 0) -#define FW_CFG_SMBIOS_ENTRIES (FW_CFG_ARCH_LOCAL + 1) -#define FW_CFG_IRQ0_OVERRIDE (FW_CFG_ARCH_LOCAL + 2) - -uint8_t fwcfg_get_u8(unsigned index); -uint16_t fwcfg_get_u16(unsigned index); -uint32_t fwcfg_get_u32(unsigned index); -uint64_t fwcfg_get_u64(unsigned index); - -unsigned fwcfg_get_nb_cpus(void); - -#endif - diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/test/lib/x86/io.c qemu-kvm-0.14.1/kvm/user/test/lib/x86/io.c --- qemu-kvm-0.12.5+noroms/kvm/user/test/lib/x86/io.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/test/lib/x86/io.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,23 +0,0 @@ -#include "libcflat.h" -#include "smp.h" - -static struct spinlock lock; - -static void print_serial(const char *buf) -{ - unsigned long len = strlen(buf); - - asm volatile ("rep/outsb" : "+S"(buf), "+c"(len) : "d"(0xf1)); -} - -void puts(const char *s) -{ - spin_lock(&lock); - print_serial(s); - spin_unlock(&lock); -} - -void exit(int code) -{ - asm volatile("out %0, %1" : : "a"(code), "d"((short)0xf4)); -} diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/test/lib/x86/smp.c qemu-kvm-0.14.1/kvm/user/test/lib/x86/smp.c --- qemu-kvm-0.12.5+noroms/kvm/user/test/lib/x86/smp.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/test/lib/x86/smp.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,128 +0,0 @@ - -#include -#include "smp.h" -#include "apic.h" -#include "fwcfg.h" - -#define IPI_VECTOR 0x20 - -static struct spinlock ipi_lock; -static void (*ipi_function)(void *data); -static void *ipi_data; -static volatile int ipi_done; - -static __attribute__((used)) void ipi() -{ - ipi_function(ipi_data); - apic_write(APIC_EOI, 0); - ipi_done = 1; -} - -asm ( - "ipi_entry: \n" - " call ipi \n" -#ifndef __x86_64__ - " iret" -#else - " iretq" -#endif - ); - - -static void set_ipi_descriptor(void (*ipi_entry)(void)) -{ - unsigned short *desc = (void *)(IPI_VECTOR * sizeof(long) * 2); - unsigned short cs; - unsigned long ipi = (unsigned long)ipi_entry; - - asm ("mov %%cs, %0" : "=r"(cs)); - desc[0] = ipi; - desc[1] = cs; - desc[2] = 0x8e00; - desc[3] = ipi >> 16; -#ifdef __x86_64__ - desc[4] = ipi >> 32; - desc[5] = ipi >> 48; - desc[6] = 0; - desc[7] = 0; -#endif -} - -void spin_lock(struct spinlock *lock) -{ - int v = 1; - - do { - asm volatile ("xchg %1, %0" : "+m"(lock->v), "+r"(v)); - } while (v); - asm volatile ("" : : : "memory"); -} - -void spin_unlock(struct spinlock *lock) -{ - asm volatile ("" : : : "memory"); - lock->v = 0; -} - -int cpu_count(void) -{ - return fwcfg_get_nb_cpus(); -} - -int smp_id(void) -{ - unsigned id; - - asm ("mov %%gs:0, %0" : "=r"(id)); - return id; -} - -static void setup_smp_id(void *data) -{ - asm ("mov %0, %%gs:0" : : "r"(apic_id()) : "memory"); -} - -static void __on_cpu(int cpu, void (*function)(void *data), void *data, - int wait) -{ - spin_lock(&ipi_lock); - if (cpu == smp_id()) - function(data); - else { - ipi_done = 0; - ipi_function = function; - ipi_data = data; - apic_icr_write(APIC_INT_ASSERT | APIC_DEST_PHYSICAL | APIC_DM_FIXED - | IPI_VECTOR, - cpu); - if (wait) { - while (!ipi_done) - ; - } - } - spin_unlock(&ipi_lock); -} - -void on_cpu(int cpu, void (*function)(void *data), void *data) -{ - __on_cpu(cpu, function, data, 1); -} - -void on_cpu_async(int cpu, void (*function)(void *data), void *data) -{ - __on_cpu(cpu, function, data, 0); -} - - -void smp_init(void) -{ - int i; - void ipi_entry(void); - - set_ipi_descriptor(ipi_entry); - - setup_smp_id(0); - for (i = 1; i < cpu_count(); ++i) - on_cpu(i, setup_smp_id, 0); - -} diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/test/lib/x86/smp.h qemu-kvm-0.14.1/kvm/user/test/lib/x86/smp.h --- qemu-kvm-0.12.5+noroms/kvm/user/test/lib/x86/smp.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/test/lib/x86/smp.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,17 +0,0 @@ -#ifndef __SMP_H -#define __SMP_H - -struct spinlock { - int v; -}; - -void smp_init(void); - -int cpu_count(void); -int smp_id(void); -void on_cpu(int cpu, void (*function)(void *data), void *data); -void on_cpu_async(int cpu, void (*function)(void *data), void *data); -void spin_lock(struct spinlock *lock); -void spin_unlock(struct spinlock *lock); - -#endif diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/test/powerpc/44x/tlbsx.S qemu-kvm-0.14.1/kvm/user/test/powerpc/44x/tlbsx.S --- qemu-kvm-0.12.5+noroms/kvm/user/test/powerpc/44x/tlbsx.S 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/test/powerpc/44x/tlbsx.S 1970-01-01 00:00:00.000000000 +0000 @@ -1,33 +0,0 @@ -#define SPRN_MMUCR 0x3b2 - -#define TLBWORD0 0x10000210 -#define TLBWORD1 0x10000000 -#define TLBWORD2 0x00000003 - -.global _start -_start: - li r4, 0 - mtspr SPRN_MMUCR, r4 - - li r3, 23 - - lis r4, TLBWORD0@h - ori r4, r4, TLBWORD0@l - tlbwe r4, r3, 0 - - lis r4, TLBWORD1@h - ori r4, r4, TLBWORD1@l - tlbwe r4, r3, 1 - - lis r4, TLBWORD2@h - ori r4, r4, TLBWORD2@l - tlbwe r4, r3, 2 - - lis r4, 0x1000 - tlbsx r5, r4, r0 - cmpwi r5, 23 - beq good - trap - -good: - b . diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/test/powerpc/44x/tlbwe_16KB.S qemu-kvm-0.14.1/kvm/user/test/powerpc/44x/tlbwe_16KB.S --- qemu-kvm-0.12.5+noroms/kvm/user/test/powerpc/44x/tlbwe_16KB.S 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/test/powerpc/44x/tlbwe_16KB.S 1970-01-01 00:00:00.000000000 +0000 @@ -1,35 +0,0 @@ -#define SPRN_MMUCR 0x3b2 - -/* 16KB mapping at 4MB */ -#define TLBWORD0 0x00400220 -#define TLBWORD1 0x00400000 -#define TLBWORD2 0x00000003 - -.global _start -_start: - li r4, 0 - mtspr SPRN_MMUCR, r4 - - li r3, 5 - - lis r4, TLBWORD0@h - ori r4, r4, TLBWORD0@l - tlbwe r4, r3, 0 - - lis r4, TLBWORD1@h - ori r4, r4, TLBWORD1@l - tlbwe r4, r3, 1 - - lis r4, TLBWORD2@h - ori r4, r4, TLBWORD2@l - tlbwe r4, r3, 2 - - /* load from 4MB */ - lis r3, 0x0040 - lwz r4, 0(r3) - - /* load from 4MB+8KB */ - ori r3, r3, 0x2000 - lwz r4, 0(r3) - - b . diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/test/powerpc/44x/tlbwe_hole.S qemu-kvm-0.14.1/kvm/user/test/powerpc/44x/tlbwe_hole.S --- qemu-kvm-0.12.5+noroms/kvm/user/test/powerpc/44x/tlbwe_hole.S 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/test/powerpc/44x/tlbwe_hole.S 1970-01-01 00:00:00.000000000 +0000 @@ -1,27 +0,0 @@ -#define SPRN_MMUCR 0x3b2 - -/* Try to map real address 1GB. */ -#define TLBWORD0 0x40000210 -#define TLBWORD1 0x40000000 -#define TLBWORD2 0x00000003 - -.global _start -_start: - li r4, 0 - mtspr SPRN_MMUCR, r4 - - li r3, 23 - - lis r4, TLBWORD0@h - ori r4, r4, TLBWORD0@l - tlbwe r4, r3, 0 - - lis r4, TLBWORD1@h - ori r4, r4, TLBWORD1@l - tlbwe r4, r3, 1 - - lis r4, TLBWORD2@h - ori r4, r4, TLBWORD2@l - tlbwe r4, r3, 2 - - b . diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/test/powerpc/44x/tlbwe.S qemu-kvm-0.14.1/kvm/user/test/powerpc/44x/tlbwe.S --- qemu-kvm-0.12.5+noroms/kvm/user/test/powerpc/44x/tlbwe.S 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/test/powerpc/44x/tlbwe.S 1970-01-01 00:00:00.000000000 +0000 @@ -1,27 +0,0 @@ -#define SPRN_MMUCR 0x3b2 - -/* Create a mapping at 4MB */ -#define TLBWORD0 0x00400210 -#define TLBWORD1 0x00400000 -#define TLBWORD2 0x00000003 - -.global _start -_start: - li r4, 0 - mtspr SPRN_MMUCR, r4 - - li r3, 23 - - lis r4, TLBWORD0@h - ori r4, r4, TLBWORD0@l - tlbwe r4, r3, 0 - - lis r4, TLBWORD1@h - ori r4, r4, TLBWORD1@l - tlbwe r4, r3, 1 - - lis r4, TLBWORD2@h - ori r4, r4, TLBWORD2@l - tlbwe r4, r3, 2 - - b . diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/test/powerpc/cstart.S qemu-kvm-0.14.1/kvm/user/test/powerpc/cstart.S --- qemu-kvm-0.12.5+noroms/kvm/user/test/powerpc/cstart.S 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/test/powerpc/cstart.S 1970-01-01 00:00:00.000000000 +0000 @@ -1,38 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License, version 2, as - * published by the Free Software Foundation; - * - * 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, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * Copyright IBM Corp. 2008 - * - * Authors: Hollis Blanchard - */ - -#define OUTPUT_VADDR 0xf0000000 -#define OUTPUT_PADDR 0xf0000000 - -.globl _start -_start: - /* In the future we might need to assign a stack and zero BSS here. */ - - /* Map the debug page 1:1. */ - lis r3, OUTPUT_VADDR@h - ori r3, r3, OUTPUT_VADDR@l - lis r4, OUTPUT_PADDR@h - ori r4, r4, OUTPUT_PADDR@l - bl map - - /* Call main() and pass return code to exit(). */ - bl main - bl exit - - b . diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/test/powerpc/exit.c qemu-kvm-0.14.1/kvm/user/test/powerpc/exit.c --- qemu-kvm-0.12.5+noroms/kvm/user/test/powerpc/exit.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/test/powerpc/exit.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,23 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License, version 2, as - * published by the Free Software Foundation; - * - * 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, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * Copyright IBM Corp. 2008 - * - * Authors: Hollis Blanchard - */ - -int main(void) -{ - return 1; -} diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/test/powerpc/helloworld.c qemu-kvm-0.14.1/kvm/user/test/powerpc/helloworld.c --- qemu-kvm-0.12.5+noroms/kvm/user/test/powerpc/helloworld.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/test/powerpc/helloworld.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,27 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License, version 2, as - * published by the Free Software Foundation; - * - * 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, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * Copyright IBM Corp. 2008 - * - * Authors: Deepa Srinivasan - */ - -#include "libcflat.h" - -int main() -{ - printf("Hello World\n"); - - return 1; -} diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/test/powerpc/io.S qemu-kvm-0.14.1/kvm/user/test/powerpc/io.S --- qemu-kvm-0.12.5+noroms/kvm/user/test/powerpc/io.S 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/test/powerpc/io.S 1970-01-01 00:00:00.000000000 +0000 @@ -1,32 +0,0 @@ -#define SPRN_MMUCR 0x3b2 - -#define TLBWORD0 0xf0000210 -#define TLBWORD1 0xf0000000 -#define TLBWORD2 0x00000003 - -.global _start -_start: - li r4, 0 - mtspr SPRN_MMUCR, r4 - - li r3, 2 - - lis r4, TLBWORD0@h - ori r4, r4, TLBWORD0@l - tlbwe r4, r3, 0 - - lis r4, TLBWORD1@h - ori r4, r4, TLBWORD1@l - tlbwe r4, r3, 1 - - lis r4, TLBWORD2@h - ori r4, r4, TLBWORD2@l - tlbwe r4, r3, 2 - - lis r3, 0xf000 - lis r4, 0x1234 - ori r4, r4, 0x5678 - stb r4, 0(r3) - lbz r5, 0(r3) - - b . diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/test/powerpc/spin.S qemu-kvm-0.14.1/kvm/user/test/powerpc/spin.S --- qemu-kvm-0.12.5+noroms/kvm/user/test/powerpc/spin.S 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/test/powerpc/spin.S 1970-01-01 00:00:00.000000000 +0000 @@ -1,4 +0,0 @@ - -.global _start -_start: - b . diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/test/powerpc/sprg.S qemu-kvm-0.14.1/kvm/user/test/powerpc/sprg.S --- qemu-kvm-0.12.5+noroms/kvm/user/test/powerpc/sprg.S 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/test/powerpc/sprg.S 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ - -.global _start -_start: - li r3, 42 - mtsprg 0, r3 - mfsprg r4, 0 - b . diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/test/x86/access.c qemu-kvm-0.14.1/kvm/user/test/x86/access.c --- qemu-kvm-0.12.5+noroms/kvm/user/test/x86/access.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/test/x86/access.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,604 +0,0 @@ - -#include "libcflat.h" - -#define smp_id() 0 - -#define true 1 -#define false 0 - -typedef unsigned long pt_element_t; - -#define PAGE_SIZE ((pt_element_t)4096) -#define PAGE_MASK (~(PAGE_SIZE-1)) - -#define PT_BASE_ADDR_MASK ((pt_element_t)((((pt_element_t)1 << 40) - 1) & PAGE_MASK)) -#define PT_PSE_BASE_ADDR_MASK (PT_BASE_ADDR_MASK & ~(1ull << 21)) - -#define PT_PRESENT_MASK ((pt_element_t)1 << 0) -#define PT_WRITABLE_MASK ((pt_element_t)1 << 1) -#define PT_USER_MASK ((pt_element_t)1 << 2) -#define PT_ACCESSED_MASK ((pt_element_t)1 << 5) -#define PT_DIRTY_MASK ((pt_element_t)1 << 6) -#define PT_PSE_MASK ((pt_element_t)1 << 7) -#define PT_NX_MASK ((pt_element_t)1 << 63) - -#define CR0_WP_MASK (1UL << 16) - -#define PFERR_PRESENT_MASK (1U << 0) -#define PFERR_WRITE_MASK (1U << 1) -#define PFERR_USER_MASK (1U << 2) -#define PFERR_RESERVED_MASK (1U << 3) -#define PFERR_FETCH_MASK (1U << 4) - -#define MSR_EFER 0xc0000080 -#define EFER_NX_MASK (1ull << 11) - -/* - * page table access check tests - */ - -enum { - AC_PTE_PRESENT, - AC_PTE_WRITABLE, - AC_PTE_USER, - AC_PTE_ACCESSED, - AC_PTE_DIRTY, - AC_PTE_NX, - AC_PTE_BIT51, - - AC_PDE_PRESENT, - AC_PDE_WRITABLE, - AC_PDE_USER, - AC_PDE_ACCESSED, - AC_PDE_DIRTY, - AC_PDE_PSE, - AC_PDE_NX, - AC_PDE_BIT51, - - AC_ACCESS_USER, - AC_ACCESS_WRITE, - AC_ACCESS_FETCH, - AC_ACCESS_TWICE, - // AC_ACCESS_PTE, - - AC_CPU_EFER_NX, - AC_CPU_CR0_WP, - - NR_AC_FLAGS -}; - -const char *ac_names[] = { - [AC_PTE_PRESENT] = "pte.p", - [AC_PTE_ACCESSED] = "pte.a", - [AC_PTE_WRITABLE] = "pte.rw", - [AC_PTE_USER] = "pte.user", - [AC_PTE_DIRTY] = "pte.d", - [AC_PTE_NX] = "pte.nx", - [AC_PTE_BIT51] = "pte.51", - [AC_PDE_PRESENT] = "pde.p", - [AC_PDE_ACCESSED] = "pde.a", - [AC_PDE_WRITABLE] = "pde.rw", - [AC_PDE_USER] = "pde.user", - [AC_PDE_DIRTY] = "pde.d", - [AC_PDE_PSE] = "pde.pse", - [AC_PDE_NX] = "pde.nx", - [AC_PDE_BIT51] = "pde.51", - [AC_ACCESS_WRITE] = "write", - [AC_ACCESS_USER] = "user", - [AC_ACCESS_FETCH] = "fetch", - [AC_ACCESS_TWICE] = "twice", - [AC_CPU_EFER_NX] = "efer.nx", - [AC_CPU_CR0_WP] = "cr0.wp", -}; - -static inline void *va(pt_element_t phys) -{ - return (void *)phys; -} - -static unsigned long read_cr0() -{ - unsigned long cr0; - - asm volatile ("mov %%cr0, %0" : "=r"(cr0)); - - return cr0; -} - -static void write_cr0(unsigned long cr0) -{ - asm volatile ("mov %0, %%cr0" : : "r"(cr0)); -} - -typedef struct { - unsigned short offset0; - unsigned short selector; - unsigned short ist : 3; - unsigned short : 5; - unsigned short type : 4; - unsigned short : 1; - unsigned short dpl : 2; - unsigned short p : 1; - unsigned short offset1; - unsigned offset2; - unsigned reserved; -} idt_entry_t; - -typedef struct { - unsigned flags[NR_AC_FLAGS]; - void *virt; - pt_element_t phys; - pt_element_t pt_pool; - unsigned pt_pool_size; - unsigned pt_pool_current; - pt_element_t *ptep; - pt_element_t expected_pte; - pt_element_t *pdep; - pt_element_t expected_pde; - int expected_fault; - unsigned expected_error; - idt_entry_t idt[256]; -} ac_test_t; - -typedef struct { - unsigned short limit; - unsigned long linear_addr; -} __attribute__((packed)) descriptor_table_t; - -void lidt(idt_entry_t *idt, int nentries) -{ - descriptor_table_t dt; - - dt.limit = nentries * sizeof(*idt) - 1; - dt.linear_addr = (unsigned long)idt; - asm volatile ("lidt %0" : : "m"(dt)); -} - -void memset(void *a, unsigned char v, int n) -{ - unsigned char *x = a; - - while (n--) - *x++ = v; -} - -unsigned short read_cs() -{ - unsigned short r; - - asm volatile ("mov %%cs, %0" : "=r"(r)); - return r; -} - -unsigned long long rdmsr(unsigned index) -{ - unsigned a, d; - - asm volatile("rdmsr" : "=a"(a), "=d"(d) : "c"(index)); - return ((unsigned long long)d << 32) | a; -} - -void wrmsr(unsigned index, unsigned long long val) -{ - unsigned a = val, d = val >> 32; - - asm volatile("wrmsr" : : "a"(a), "d"(d), "c"(index)); -} - -void set_idt_entry(idt_entry_t *e, void *addr, int dpl) -{ - memset(e, 0, sizeof *e); - e->offset0 = (unsigned long)addr; - e->selector = read_cs(); - e->ist = 0; - e->type = 14; - e->dpl = dpl; - e->p = 1; - e->offset1 = (unsigned long)addr >> 16; - e->offset2 = (unsigned long)addr >> 32; -} - -void set_cr0_wp(int wp) -{ - unsigned long cr0 = read_cr0(); - - cr0 &= ~CR0_WP_MASK; - if (wp) - cr0 |= CR0_WP_MASK; - write_cr0(cr0); -} - -void set_efer_nx(int nx) -{ - unsigned long long efer; - - efer = rdmsr(MSR_EFER); - efer &= ~EFER_NX_MASK; - if (nx) - efer |= EFER_NX_MASK; - wrmsr(MSR_EFER, efer); -} - - -void ac_test_init(ac_test_t *at) -{ - wrmsr(MSR_EFER, rdmsr(MSR_EFER) | EFER_NX_MASK); - set_cr0_wp(1); - for (int i = 0; i < NR_AC_FLAGS; ++i) - at->flags[i] = 0; - at->virt = (void *)(0x123400000000 + 16 * smp_id()); - at->phys = 32 * 1024 * 1024; - at->pt_pool = 33 * 1024 * 1024; - at->pt_pool_size = 120 * 1024 * 1024 - at->pt_pool; - at->pt_pool_current = 0; - memset(at->idt, 0, sizeof at->idt); - lidt(at->idt, 256); - extern char page_fault, kernel_entry; - set_idt_entry(&at->idt[14], &page_fault, 0); - set_idt_entry(&at->idt[0x20], &kernel_entry, 3); -} - -int ac_test_bump_one(ac_test_t *at) -{ - for (int i = 0; i < NR_AC_FLAGS; ++i) - if (!at->flags[i]) { - at->flags[i] = 1; - return 1; - } else - at->flags[i] = 0; - return 0; -} - -_Bool ac_test_legal(ac_test_t *at) -{ - if (at->flags[AC_ACCESS_FETCH] && at->flags[AC_ACCESS_WRITE]) - return false; - return true; -} - -int ac_test_bump(ac_test_t *at) -{ - int ret; - - ret = ac_test_bump_one(at); - while (ret && !ac_test_legal(at)) - ret = ac_test_bump_one(at); - return ret; -} - -unsigned long read_cr3() -{ - unsigned long cr3; - - asm volatile ("mov %%cr3, %0" : "=r"(cr3)); - return cr3; -} - -void invlpg(void *addr) -{ - asm volatile ("invlpg (%0)" : : "r"(addr)); -} - -pt_element_t ac_test_alloc_pt(ac_test_t *at) -{ - pt_element_t ret = at->pt_pool + at->pt_pool_current; - at->pt_pool_current += PAGE_SIZE; - return ret; -} - -_Bool ac_test_enough_room(ac_test_t *at) -{ - return at->pt_pool_current + 4 * PAGE_SIZE <= at->pt_pool_size; -} - -void ac_test_reset_pt_pool(ac_test_t *at) -{ - at->pt_pool_current = 0; -} - -void ac_test_setup_pte(ac_test_t *at) -{ - unsigned long root = read_cr3(); - int pde_valid, pte_valid; - - if (!ac_test_enough_room(at)) - ac_test_reset_pt_pool(at); - - at->ptep = 0; - for (int i = 4; i >= 1 && (i >= 2 || !at->flags[AC_PDE_PSE]); --i) { - pt_element_t *vroot = va(root & PT_BASE_ADDR_MASK); - unsigned index = ((unsigned long)at->virt >> (12 + (i-1) * 9)) & 511; - pt_element_t pte = 0; - switch (i) { - case 4: - case 3: - pte = vroot[index]; - pte = ac_test_alloc_pt(at) | PT_PRESENT_MASK; - pte |= PT_WRITABLE_MASK | PT_USER_MASK; - break; - case 2: - if (!at->flags[AC_PDE_PSE]) - pte = ac_test_alloc_pt(at); - else { - pte = at->phys & PT_PSE_BASE_ADDR_MASK; - pte |= PT_PSE_MASK; - } - if (at->flags[AC_PDE_PRESENT]) - pte |= PT_PRESENT_MASK; - if (at->flags[AC_PDE_WRITABLE]) - pte |= PT_WRITABLE_MASK; - if (at->flags[AC_PDE_USER]) - pte |= PT_USER_MASK; - if (at->flags[AC_PDE_ACCESSED]) - pte |= PT_ACCESSED_MASK; - if (at->flags[AC_PDE_DIRTY]) - pte |= PT_DIRTY_MASK; - if (at->flags[AC_PDE_NX]) - pte |= PT_NX_MASK; - if (at->flags[AC_PDE_BIT51]) - pte |= 1ull << 51; - at->pdep = &vroot[index]; - break; - case 1: - pte = at->phys & PT_BASE_ADDR_MASK; - if (at->flags[AC_PTE_PRESENT]) - pte |= PT_PRESENT_MASK; - if (at->flags[AC_PTE_WRITABLE]) - pte |= PT_WRITABLE_MASK; - if (at->flags[AC_PTE_USER]) - pte |= PT_USER_MASK; - if (at->flags[AC_PTE_ACCESSED]) - pte |= PT_ACCESSED_MASK; - if (at->flags[AC_PTE_DIRTY]) - pte |= PT_DIRTY_MASK; - if (at->flags[AC_PTE_NX]) - pte |= PT_NX_MASK; - if (at->flags[AC_PTE_BIT51]) - pte |= 1ull << 51; - at->ptep = &vroot[index]; - break; - } - vroot[index] = pte; - root = vroot[index]; - } - invlpg(at->virt); - if (at->ptep) - at->expected_pte = *at->ptep; - at->expected_pde = *at->pdep; - at->expected_fault = 0; - at->expected_error = PFERR_PRESENT_MASK; - - pde_valid = at->flags[AC_PDE_PRESENT] - && !at->flags[AC_PDE_BIT51] - && !(at->flags[AC_PDE_NX] && !at->flags[AC_CPU_EFER_NX]); - pte_valid = pde_valid - && at->flags[AC_PTE_PRESENT] - && !at->flags[AC_PTE_BIT51] - && !(at->flags[AC_PTE_NX] && !at->flags[AC_CPU_EFER_NX]); - if (at->flags[AC_ACCESS_TWICE]) { - if (pde_valid) { - at->expected_pde |= PT_ACCESSED_MASK; - if (pte_valid) - at->expected_pte |= PT_ACCESSED_MASK; - } - } - - if (at->flags[AC_ACCESS_USER]) - at->expected_error |= PFERR_USER_MASK; - - if (at->flags[AC_ACCESS_WRITE]) - at->expected_error |= PFERR_WRITE_MASK; - - if (at->flags[AC_ACCESS_FETCH]) - at->expected_error |= PFERR_FETCH_MASK; - - if (!at->flags[AC_PDE_PRESENT]) { - at->expected_fault = 1; - at->expected_error &= ~PFERR_PRESENT_MASK; - } else if (!pde_valid) { - at->expected_fault = 1; - at->expected_error |= PFERR_RESERVED_MASK; - } - - if (at->flags[AC_ACCESS_USER] && !at->flags[AC_PDE_USER]) - at->expected_fault = 1; - - if (at->flags[AC_ACCESS_WRITE] - && !at->flags[AC_PDE_WRITABLE] - && (at->flags[AC_CPU_CR0_WP] || at->flags[AC_ACCESS_USER])) - at->expected_fault = 1; - - if (at->flags[AC_ACCESS_FETCH] && at->flags[AC_PDE_NX]) - at->expected_fault = 1; - - if (at->expected_fault) - goto fault; - - at->expected_pde |= PT_ACCESSED_MASK; - - if (at->flags[AC_PDE_PSE]) { - if (at->flags[AC_ACCESS_WRITE]) - at->expected_pde |= PT_DIRTY_MASK; - goto no_pte; - } - - if (!at->flags[AC_PTE_PRESENT]) { - at->expected_fault = 1; - at->expected_error &= ~PFERR_PRESENT_MASK; - } else if (!pte_valid) { - at->expected_fault = 1; - at->expected_error |= PFERR_RESERVED_MASK; - } - - if (at->flags[AC_ACCESS_USER] && !at->flags[AC_PTE_USER]) - at->expected_fault = 1; - - if (at->flags[AC_ACCESS_WRITE] - && !at->flags[AC_PTE_WRITABLE] - && (at->flags[AC_CPU_CR0_WP] || at->flags[AC_ACCESS_USER])) - at->expected_fault = 1; - - if (at->flags[AC_ACCESS_FETCH] && at->flags[AC_PTE_NX]) - at->expected_fault = 1; - - if (at->expected_fault) - goto fault; - - at->expected_pte |= PT_ACCESSED_MASK; - if (at->flags[AC_ACCESS_WRITE]) - at->expected_pte |= PT_DIRTY_MASK; - -no_pte: -fault: - ; -} - -int ac_test_do_access(ac_test_t *at) -{ - static unsigned unique = 42; - int fault = 0; - unsigned e; - static unsigned char user_stack[4096]; - unsigned long rsp; - - ++unique; - - *((unsigned char *)at->phys) = 0xc3; /* ret */ - - unsigned r = unique; - set_cr0_wp(at->flags[AC_CPU_CR0_WP]); - set_efer_nx(at->flags[AC_CPU_EFER_NX]); - - if (at->flags[AC_ACCESS_TWICE]) { - asm volatile ( - "mov $fixed2, %%rsi \n\t" - "mov (%[addr]), %[reg] \n\t" - "fixed2:" - : [reg]"=r"(r), [fault]"=a"(fault), "=b"(e) - : [addr]"r"(at->virt) - : "rsi" - ); - fault = 0; - } - - asm volatile ("mov $fixed1, %%rsi \n\t" - "mov %%rsp, %%rdx \n\t" - "cmp $0, %[user] \n\t" - "jz do_access \n\t" - "push %%rax; mov %[user_ds], %%ax; mov %%ax, %%ds; pop %%rax \n\t" - "pushq %[user_ds] \n\t" - "pushq %[user_stack_top] \n\t" - "pushfq \n\t" - "pushq %[user_cs] \n\t" - "pushq $do_access \n\t" - "iretq \n" - "do_access: \n\t" - "cmp $0, %[fetch] \n\t" - "jnz 2f \n\t" - "cmp $0, %[write] \n\t" - "jnz 1f \n\t" - "mov (%[addr]), %[reg] \n\t" - "jmp done \n\t" - "1: mov %[reg], (%[addr]) \n\t" - "jmp done \n\t" - "2: call *%[addr] \n\t" - "done: \n" - "fixed1: \n" - "int %[kernel_entry_vector] \n\t" - "back_to_kernel:" - : [reg]"+r"(r), "+a"(fault), "=b"(e), "=&d"(rsp) - : [addr]"r"(at->virt), - [write]"r"(at->flags[AC_ACCESS_WRITE]), - [user]"r"(at->flags[AC_ACCESS_USER]), - [fetch]"r"(at->flags[AC_ACCESS_FETCH]), - [user_ds]"i"(32+3), - [user_cs]"i"(24+3), - [user_stack_top]"r"(user_stack + sizeof user_stack), - [kernel_entry_vector]"i"(0x20) - : "rsi"); - - asm volatile (".section .text.pf \n\t" - "page_fault: \n\t" - "pop %rbx \n\t" - "mov %rsi, (%rsp) \n\t" - "movl $1, %eax \n\t" - "iretq \n\t" - ".section .text"); - - asm volatile (".section .text.entry \n\t" - "kernel_entry: \n\t" - "mov %rdx, %rsp \n\t" - "jmp back_to_kernel \n\t" - ".section .text"); - - if (fault && !at->expected_fault) { - printf("FAIL: unexpected fault\n"); - return 0; - } - if (!fault && at->expected_fault) { - printf("FAIL: unexpected access\n"); - return 0; - } - if (fault && e != at->expected_error) { - printf("FAIL: error code %x expected %x\n", e, at->expected_error); - return 0; - } - if (at->ptep && *at->ptep != at->expected_pte) { - printf("FAIL: pte %x expected %x\n", *at->ptep, at->expected_pte); - return 0; - } - - if (*at->pdep != at->expected_pde) { - printf("FAIL: pde %x expected %x\n", *at->pdep, at->expected_pde); - return 0; - } - - printf("PASS\n"); - return 1; -} - -int ac_test_exec(ac_test_t *at) -{ - int r; - char line[5000]; - - *line = 0; - strcat(line, "test"); - for (int i = 0; i < NR_AC_FLAGS; ++i) - if (at->flags[i]) { - strcat(line, " "); - strcat(line, ac_names[i]); - } - strcat(line, ": "); - printf("%s", line); - ac_test_setup_pte(at); - r = ac_test_do_access(at); - return r; -} - -int ac_test_run(void) -{ - static ac_test_t at; - int tests, successes; - - printf("run\n"); - tests = successes = 0; - ac_test_init(&at); - do { - ++tests; - successes += ac_test_exec(&at); - } while (ac_test_bump(&at)); - - printf("\n%d tests, %d failures\n", tests, tests - successes); - - return successes == tests; -} - -int main() -{ - int r; - - printf("starting test\n\n"); - r = ac_test_run(); - return r ? 0 : 1; -} diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/test/x86/apic.c qemu-kvm-0.14.1/kvm/user/test/x86/apic.c --- qemu-kvm-0.12.5+noroms/kvm/user/test/x86/apic.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/test/x86/apic.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,317 +0,0 @@ -#include "libcflat.h" -#include "apic.h" -#include "vm.h" - -typedef struct { - unsigned short offset0; - unsigned short selector; - unsigned short ist : 3; - unsigned short : 5; - unsigned short type : 4; - unsigned short : 1; - unsigned short dpl : 2; - unsigned short p : 1; - unsigned short offset1; -#ifdef __x86_64__ - unsigned offset2; - unsigned reserved; -#endif -} idt_entry_t; - -typedef struct { - ulong regs[sizeof(ulong)*2]; - ulong func; - ulong rip; - ulong cs; - ulong rflags; -} isr_regs_t; - -#ifdef __x86_64__ -# define R "r" -#else -# define R "e" -#endif - -extern char isr_entry_point[]; - -asm ( - "isr_entry_point: \n" -#ifdef __x86_64__ - "push %r15 \n\t" - "push %r14 \n\t" - "push %r13 \n\t" - "push %r12 \n\t" - "push %r11 \n\t" - "push %r10 \n\t" - "push %r9 \n\t" - "push %r8 \n\t" -#endif - "push %"R "di \n\t" - "push %"R "si \n\t" - "push %"R "bp \n\t" - "push %"R "sp \n\t" - "push %"R "bx \n\t" - "push %"R "dx \n\t" - "push %"R "cx \n\t" - "push %"R "ax \n\t" -#ifdef __x86_64__ - "mov %rsp, %rdi \n\t" - "callq *8*16(%rsp) \n\t" -#else - "push %esp \n\t" - "calll *4+4*8(%esp) \n\t" - "add $4, %esp \n\t" -#endif - "pop %"R "ax \n\t" - "pop %"R "cx \n\t" - "pop %"R "dx \n\t" - "pop %"R "bx \n\t" - "pop %"R "bp \n\t" - "pop %"R "bp \n\t" - "pop %"R "si \n\t" - "pop %"R "di \n\t" -#ifdef __x86_64__ - "pop %r8 \n\t" - "pop %r9 \n\t" - "pop %r10 \n\t" - "pop %r11 \n\t" - "pop %r12 \n\t" - "pop %r13 \n\t" - "pop %r14 \n\t" - "pop %r15 \n\t" -#endif -#ifdef __x86_64__ - "add $8, %rsp \n\t" - "iretq \n\t" -#else - "add $4, %esp \n\t" - "iretl \n\t" -#endif - ); - -static idt_entry_t idt[256]; - -static int g_fail; -static int g_tests; - -static void outb(unsigned char data, unsigned short port) -{ - asm volatile ("out %0, %1" : : "a"(data), "d"(port)); -} - -static void report(const char *msg, int pass) -{ - ++g_tests; - printf("%s: %s\n", msg, (pass ? "PASS" : "FAIL")); - if (!pass) - ++g_fail; -} - -static void test_lapic_existence(void) -{ - u32 lvr; - - lvr = apic_read(APIC_LVR); - printf("apic version: %x\n", lvr); - report("apic existence", (u16)lvr == 0x14); -} - -#define MSR_APIC_BASE 0x0000001b - -void test_enable_x2apic(void) -{ - if (enable_x2apic()) { - printf("x2apic enabled\n"); - } else { - printf("x2apic not detected\n"); - } -} - -static u16 read_cs(void) -{ - u16 v; - - asm("mov %%cs, %0" : "=rm"(v)); - return v; -} - -static void init_idt(void) -{ - struct { - u16 limit; - ulong idt; - } __attribute__((packed)) idt_ptr = { - sizeof(idt_entry_t) * 256 - 1, - (ulong)&idt, - }; - - asm volatile("lidt %0" : : "m"(idt_ptr)); -} - -static void set_idt_entry(unsigned vec, void (*func)(isr_regs_t *regs)) -{ - u8 *thunk = vmalloc(50); - ulong ptr = (ulong)thunk; - idt_entry_t ent = { - .offset0 = ptr, - .selector = read_cs(), - .ist = 0, - .type = 14, - .dpl = 0, - .p = 1, - .offset1 = ptr >> 16, -#ifdef __x86_64__ - .offset2 = ptr >> 32, -#endif - }; -#ifdef __x86_64__ - /* sub $8, %rsp */ - *thunk++ = 0x48; *thunk++ = 0x83; *thunk++ = 0xec; *thunk++ = 0x08; - /* mov $func_low, %(rsp) */ - *thunk++ = 0xc7; *thunk++ = 0x04; *thunk++ = 0x24; - *(u32 *)thunk = (ulong)func; thunk += 4; - /* mov $func_high, %(rsp+4) */ - *thunk++ = 0xc7; *thunk++ = 0x44; *thunk++ = 0x24; *thunk++ = 0x04; - *(u32 *)thunk = (ulong)func >> 32; thunk += 4; - /* jmp isr_entry_point */ - *thunk ++ = 0xe9; - *(u32 *)thunk = (ulong)isr_entry_point - (ulong)(thunk + 4); -#else - /* push $func */ - *thunk++ = 0x68; - *(u32 *)thunk = (ulong)func; - /* jmp isr_entry_point */ - *thunk ++ = 0xe9; - *(u32 *)thunk = (ulong)isr_entry_point - (ulong)(thunk + 4); -#endif - idt[vec] = ent; -} - -static void irq_disable(void) -{ - asm volatile("cli"); -} - -static void irq_enable(void) -{ - asm volatile("sti"); -} - -static void eoi(void) -{ - apic_write(APIC_EOI, 0); -} - -static int ipi_count; - -static void self_ipi_isr(isr_regs_t *regs) -{ - ++ipi_count; - eoi(); -} - -static void test_self_ipi(void) -{ - int vec = 0xf1; - - set_idt_entry(vec, self_ipi_isr); - irq_enable(); - apic_icr_write(APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_FIXED | vec, - 0); - asm volatile ("nop"); - report("self ipi", ipi_count == 1); -} - -static void set_ioapic_redir(unsigned line, unsigned vec) -{ - ioapic_redir_entry_t e = { - .vector = vec, - .delivery_mode = 0, - .trig_mode = 0, - }; - - ioapic_write_redir(line, e); -} - -static void set_irq_line(unsigned line, int val) -{ - asm volatile("out %0, %1" : : "a"((u8)val), "d"((u16)(0x2000 + line))); -} - -static void toggle_irq_line(unsigned line) -{ - set_irq_line(line, 1); - set_irq_line(line, 0); -} - -static int g_isr_77; - -static void ioapic_isr_77(isr_regs_t *regs) -{ - ++g_isr_77; - eoi(); -} - -static void test_ioapic_intr(void) -{ - set_idt_entry(0x77, ioapic_isr_77); - set_ioapic_redir(0x10, 0x77); - toggle_irq_line(0x10); - asm volatile ("nop"); - report("ioapic interrupt", g_isr_77 == 1); -} - -static int g_78, g_66, g_66_after_78; -static ulong g_66_rip, g_78_rip; - -static void ioapic_isr_78(isr_regs_t *regs) -{ - ++g_78; - g_78_rip = regs->rip; - eoi(); -} - -static void ioapic_isr_66(isr_regs_t *regs) -{ - ++g_66; - if (g_78) - ++g_66_after_78; - g_66_rip = regs->rip; - eoi(); -} - -static void test_ioapic_simultaneous(void) -{ - set_idt_entry(0x78, ioapic_isr_78); - set_idt_entry(0x66, ioapic_isr_66); - set_ioapic_redir(0x10, 0x78); - set_ioapic_redir(0x11, 0x66); - irq_disable(); - toggle_irq_line(0x11); - toggle_irq_line(0x10); - irq_enable(); - asm volatile ("nop"); - report("ioapic simultaneous interrupt", - g_66 && g_78 && g_66_after_78 && g_66_rip == g_78_rip); -} - -int main() -{ - setup_vm(); - - test_lapic_existence(); - - mask_pic_interrupts(); - enable_apic(); - test_enable_x2apic(); - init_idt(); - - test_self_ipi(); - - test_ioapic_intr(); - test_ioapic_simultaneous(); - - printf("\nsummary: %d tests, %d failures\n", g_tests, g_fail); - - return g_fail != 0; -} diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/test/x86/bootstrap.S qemu-kvm-0.14.1/kvm/user/test/x86/bootstrap.S --- qemu-kvm-0.12.5+noroms/kvm/user/test/x86/bootstrap.S 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/test/x86/bootstrap.S 1970-01-01 00:00:00.000000000 +0000 @@ -1,137 +0,0 @@ -/* - * minimal bootstrap to set up flat 32-bit protected mode - */ - -#include "fake-apic.h" - -bstart = 0xf0000 - -.code16 - -stack_top = 0x1000 -cpu_up = 0x1000 -cpu_up_pmode = 0x1004 - -pmode_stack_start = 0x10000 -pmode_stack_shift = 16 -pmode_stack_size = (1 << pmode_stack_shift) - -ipi_vec = 0xf0 - -start: - mov $stack_top, %sp - call smp_init - - cs lidtl idt_desc - cs lgdtl gdt_desc - mov %cr0, %eax - or $1, %eax - mov %eax, %cr0 - ljmpl $8, $pmode + bstart - -smp_init: - mov $ipi_vec, %eax - mov $(APIC_BASE + APIC_REG_IPI_VECTOR), %dx - out %eax, %dx - movw $ap_switch_to_pmode, ipi_vec*4 - movw %cs, %ax - mov %ax, ipi_vec*4+2 - mov $sipi, %eax - mov $(APIC_BASE + APIC_REG_SIPI_ADDR), %dx - outl %eax, %dx - mov $(APIC_BASE + APIC_REG_NCPU), %dx - inl %dx, %eax - mov %eax, %ecx - mov $1, %esi -smp_loop: - cmp %esi, %ecx - jbe smp_done - mov %esi, %eax - mov $(APIC_BASE + APIC_REG_SEND_SIPI), %dx - outl %eax, %dx -wait_for_cpu: - cmp cpu_up, %esi - jne wait_for_cpu - mov %esi, %eax - mov $(APIC_BASE + APIC_REG_SEND_IPI), %dx - out %eax, %dx -wait_for_cpu_pmode: - cmp cpu_up_pmode, %esi - jne wait_for_cpu_pmode - - inc %esi - jmp smp_loop -smp_done: - ret - -sipi: - mov $(APIC_BASE + APIC_REG_ID), %dx - inl %dx, %eax - mov %eax, cpu_up - shl $12, %eax - addl $stack_top, %eax - movl %eax, %esp - sti - nop -1: hlt - jmp 1b - -ap_switch_to_pmode: - cs lidtl idt_desc - cs lgdtl gdt_desc - mov %cr0, %eax - or $1, %eax - mov %eax, %cr0 - ljmpl $8, $ap_pmode + bstart - -.code32 -ap_pmode: - mov $0x10, %ax - mov %ax, %ds - mov %ax, %es - mov %ax, %fs - mov %ax, %gs - mov %ax, %ss - mov $(APIC_BASE + APIC_REG_ID), %dx - in %dx, %eax - mov %eax, cpu_up_pmode - shl $pmode_stack_shift, %eax - lea pmode_stack_start + pmode_stack_size(%eax), %esp - sti - nop -ap_pmode_wait: - hlt - jmp ap_pmode_wait - -pmode: - mov $0x10, %ax - mov %ax, %ds - mov %ax, %es - mov %ax, %fs - mov %ax, %gs - mov %ax, %ss - mov $pmode_stack_start + pmode_stack_size, %esp - ljmp $8, $0x100000 - -.align 16 - -idt_desc: - .word 8*256-1 - .long 0 - -gdt_desc: - .word gdt_end - gdt - 1 - .long gdt + bstart - -.align 16 - -gdt: - .quad 0 - .quad 0x00cf9b000000ffff // flat 32-bit code segment - .quad 0x00cf93000000ffff // flat 32-bit data segment -gdt_end: - -. = 0xfff0 - .code16 - ljmp $0xf000, $start -.align 65536 diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/test/x86/cstart64.S qemu-kvm-0.14.1/kvm/user/test/x86/cstart64.S --- qemu-kvm-0.12.5+noroms/kvm/user/test/x86/cstart64.S 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/test/x86/cstart64.S 1970-01-01 00:00:00.000000000 +0000 @@ -1,225 +0,0 @@ - -#include "apic-defs.h" - -.globl boot_idt -boot_idt = 0 - -ipi_vector = 0x20 - -max_cpus = 4 - -.bss - - . = . + 4096 * max_cpus - .align 16 -stacktop: - - . = . + 4096 - .align 16 -ring0stacktop: - -.data - -.align 4096 -ptl2: -i = 0 - .rept 512 * 4 - .quad 0x1e7 | (i << 21) - i = i + 1 - .endr - -.align 4096 -ptl3: - .quad ptl2 + 7 + 0 * 4096 - .quad ptl2 + 7 + 1 * 4096 - .quad ptl2 + 7 + 2 * 4096 - .quad ptl2 + 7 + 3 * 4096 - -.align 4096 -ptl4: - .quad ptl3 + 7 - -.align 4096 - -gdt64_desc: - .word gdt64_end - gdt64 - 1 - .quad gdt64 - -gdt64: - .quad 0 - .quad 0x00af9b000000ffff // 64-bit code segment - .quad 0x00cf93000000ffff // 64-bit data segment - .quad 0x00affb000000ffff // 64-bit code segment (user) - .quad 0x00cff3000000ffff // 64-bit data segment (user) -tss_descr: - .rept max_cpus - .quad 0x000089000000ffff // 64-bit avail tss - .quad 0 // tss high addr - .endr -gdt64_end: - -i = 0 -tss: - .rept max_cpus - .long 0 - .quad ring0stacktop - i * 4096 - .quad 0, 0, 0 - .quad 0, 0, 0, 0, 0, 0, 0, 0 - .long 0, 0, 0 -i = i + 1 - .endr -tss_end: - -.section .init - -.code32 - -mb_magic = 0x1BADB002 -mb_flags = 0x0 - - # multiboot header - .long mb_magic, mb_flags, 0 - (mb_magic + mb_flags) - -MSR_GS_BASE = 0xc0000101 - -.macro setup_percpu_area - lea -4096(%esp), %eax - mov $0, %edx - mov $MSR_GS_BASE, %ecx - wrmsr -.endm - -.globl start -start: - mov $stacktop, %esp - setup_percpu_area - call prepare_64 - jmpl $8, $start64 - -prepare_64: - lgdt gdt64_desc - - mov %cr4, %eax - bts $5, %eax // pae - mov %eax, %cr4 - - mov $ptl4, %eax - mov %eax, %cr3 - -efer = 0xc0000080 - mov $efer, %ecx - rdmsr - bts $8, %eax - wrmsr - - mov %cr0, %eax - bts $0, %eax - bts $31, %eax - mov %eax, %cr0 - ret - -smp_stacktop: .long 0xa0000 - -.align 16 - -gdt32: - .quad 0 - .quad 0x00cf9b000000ffff // flat 32-bit code segment - .quad 0x00cf93000000ffff // flat 32-bit data segment -gdt32_end: - -.code16 -sipi_entry: - mov %cr0, %eax - or $1, %eax - mov %eax, %cr0 - lgdtl gdt32_descr - sipi_entry - ljmpl $8, $ap_start32 - -gdt32_descr: - .word gdt32_end - gdt32 - 1 - .long gdt32 - -sipi_end: - -.code32 -ap_start32: - mov $0x10, %ax - mov %ax, %ds - mov %ax, %es - mov %ax, %fs - mov %ax, %gs - mov %ax, %ss - mov $-4096, %esp - lock/xaddl %esp, smp_stacktop - setup_percpu_area - call prepare_64 - ljmpl $8, $ap_start64 - -.code64 -ap_start64: - call load_tss - call enable_apic - call enable_x2apic - sti - nop - lock incw cpu_online_count - -1: hlt - jmp 1b - -start64: - call load_tss - call mask_pic_interrupts - call enable_apic - call smp_init - call enable_x2apic - call main - mov %eax, %edi - call exit - -idt_descr: - .word 16 * 256 - 1 - .quad boot_idt - -load_tss: - lidtq idt_descr - mov $0, %eax - mov %ax, %ss - mov $(APIC_DEFAULT_PHYS_BASE + APIC_ID), %eax - mov (%rax), %eax - shr $24, %eax - mov %eax, %ebx - shl $4, %ebx - mov $((tss_end - tss) / max_cpus), %edx - imul %edx - add $tss, %rax - mov %ax, tss_descr+2(%rbx) - shr $16, %rax - mov %al, tss_descr+4(%rbx) - shr $8, %rax - mov %al, tss_descr+7(%rbx) - shr $8, %rax - mov %eax, tss_descr+8(%rbx) - lea tss_descr-gdt64(%rbx), %rax - ltr %ax - ret - -smp_init: - cld - lea sipi_entry, %rsi - xor %rdi, %rdi - mov $(sipi_end - sipi_entry), %rcx - rep/movsb - mov $APIC_DEFAULT_PHYS_BASE, %eax - movl $(APIC_DEST_ALLBUT | APIC_DEST_PHYSICAL | APIC_DM_INIT | APIC_INT_ASSERT), APIC_ICR(%rax) - movl $(APIC_DEST_ALLBUT | APIC_DEST_PHYSICAL | APIC_DM_INIT), APIC_ICR(%rax) - movl $(APIC_DEST_ALLBUT | APIC_DEST_PHYSICAL | APIC_DM_STARTUP), APIC_ICR(%rax) - call fwcfg_get_nb_cpus -1: pause - cmpw %ax, cpu_online_count - jne 1b -smp_init_done: - ret - -cpu_online_count: .word 1 diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/test/x86/cstart.S qemu-kvm-0.14.1/kvm/user/test/x86/cstart.S --- qemu-kvm-0.12.5+noroms/kvm/user/test/x86/cstart.S 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/test/x86/cstart.S 1970-01-01 00:00:00.000000000 +0000 @@ -1,19 +0,0 @@ - - -.bss - -.section .init - -mb_magic = 0x1BADB002 -mb_flags = 0x0 - - # multiboot header - .long mb_magic, mb_flags, 0 - (mb_magic + mb_flags) - -.globl start -start: - call main - push %eax - call exit - - diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/test/x86/emulator.c qemu-kvm-0.14.1/kvm/user/test/x86/emulator.c --- qemu-kvm-0.12.5+noroms/kvm/user/test/x86/emulator.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/test/x86/emulator.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,258 +0,0 @@ -#include "ioram.h" -#include "vm.h" -#include "libcflat.h" - -#define memset __builtin_memset - -int fails, tests; - -void report(const char *name, int result) -{ - ++tests; - if (result) - printf("PASS: %s\n", name); - else { - printf("FAIL: %s\n", name); - ++fails; - } -} - -void test_cmps(void *mem) -{ - unsigned char *m1 = mem, *m2 = mem + 1024; - unsigned char m3[1024]; - void *rsi, *rdi; - long rcx, tmp; - - for (int i = 0; i < 100; ++i) - m1[i] = m2[i] = m3[i] = i; - for (int i = 100; i < 200; ++i) - m1[i] = (m3[i] = m2[i] = i) + 1; - - rsi = m1; rdi = m3; rcx = 30; - asm volatile("xor %[tmp], %[tmp] \n\t" - "repe/cmpsb" - : "+S"(rsi), "+D"(rdi), "+c"(rcx), [tmp]"=&r"(tmp) - : : "cc"); - report("repe/cmpsb (1)", rcx == 0 && rsi == m1 + 30 && rdi == m3 + 30); - - rsi = m1; rdi = m3; rcx = 15; - asm volatile("xor %[tmp], %[tmp] \n\t" - "repe/cmpsw" - : "+S"(rsi), "+D"(rdi), "+c"(rcx), [tmp]"=&r"(tmp) - : : "cc"); - report("repe/cmpsw (1)", rcx == 0 && rsi == m1 + 30 && rdi == m3 + 30); - - rsi = m1; rdi = m3; rcx = 7; - asm volatile("xor %[tmp], %[tmp] \n\t" - "repe/cmpsl" - : "+S"(rsi), "+D"(rdi), "+c"(rcx), [tmp]"=&r"(tmp) - : : "cc"); - report("repe/cmpll (1)", rcx == 0 && rsi == m1 + 28 && rdi == m3 + 28); - - rsi = m1; rdi = m3; rcx = 4; - asm volatile("xor %[tmp], %[tmp] \n\t" - "repe/cmpsq" - : "+S"(rsi), "+D"(rdi), "+c"(rcx), [tmp]"=&r"(tmp) - : : "cc"); - report("repe/cmpsq (1)", rcx == 0 && rsi == m1 + 32 && rdi == m3 + 32); - - rsi = m1; rdi = m3; rcx = 130; - asm volatile("xor %[tmp], %[tmp] \n\t" - "repe/cmpsb" - : "+S"(rsi), "+D"(rdi), "+c"(rcx), [tmp]"=&r"(tmp) - : : "cc"); - report("repe/cmpsb (2)", - rcx == 29 && rsi == m1 + 101 && rdi == m3 + 101); - - rsi = m1; rdi = m3; rcx = 65; - asm volatile("xor %[tmp], %[tmp] \n\t" - "repe/cmpsw" - : "+S"(rsi), "+D"(rdi), "+c"(rcx), [tmp]"=&r"(tmp) - : : "cc"); - report("repe/cmpsw (2)", - rcx == 14 && rsi == m1 + 102 && rdi == m3 + 102); - - rsi = m1; rdi = m3; rcx = 32; - asm volatile("xor %[tmp], %[tmp] \n\t" - "repe/cmpsl" - : "+S"(rsi), "+D"(rdi), "+c"(rcx), [tmp]"=&r"(tmp) - : : "cc"); - report("repe/cmpll (2)", - rcx == 6 && rsi == m1 + 104 && rdi == m3 + 104); - - rsi = m1; rdi = m3; rcx = 16; - asm volatile("xor %[tmp], %[tmp] \n\t" - "repe/cmpsq" - : "+S"(rsi), "+D"(rdi), "+c"(rcx), [tmp]"=&r"(tmp) - : : "cc"); - report("repe/cmpsq (2)", - rcx == 3 && rsi == m1 + 104 && rdi == m3 + 104); - -} - -void test_cr8(void) -{ - unsigned long src, dst; - - dst = 777; - src = 3; - asm volatile("mov %[src], %%cr8; mov %%cr8, %[dst]" - : [dst]"+r"(dst), [src]"+r"(src)); - report("mov %cr8", dst == 3 && src == 3); -} - -void test_push(void *mem) -{ - unsigned long tmp; - unsigned long *stack_top = mem + 4096; - unsigned long *new_stack_top; - unsigned long memw = 0x123456789abcdeful; - - memset(mem, 0x55, (void *)stack_top - mem); - - asm volatile("mov %%rsp, %[tmp] \n\t" - "mov %[stack_top], %%rsp \n\t" - "pushq $-7 \n\t" - "pushq %[reg] \n\t" - "pushq (%[mem]) \n\t" - "pushq $-7070707 \n\t" - "mov %%rsp, %[new_stack_top] \n\t" - "mov %[tmp], %%rsp" - : [tmp]"=&r"(tmp), [new_stack_top]"=r"(new_stack_top) - : [stack_top]"r"(stack_top), - [reg]"r"(-17l), [mem]"r"(&memw) - : "memory"); - - report("push $imm8", stack_top[-1] == -7ul); - report("push %reg", stack_top[-2] == -17ul); - report("push mem", stack_top[-3] == 0x123456789abcdeful); - report("push $imm", stack_top[-4] == -7070707); -} - -void test_pop(void *mem) -{ - unsigned long tmp; - unsigned long *stack_top = mem + 4096; - unsigned long memw = 0x123456789abcdeful; - static unsigned long tmp2; - - memset(mem, 0x55, (void *)stack_top - mem); - - asm volatile("pushq %[val] \n\t" - "popq (%[mem])" - : : [val]"m"(memw), [mem]"r"(mem) : "memory"); - report("pop mem", *(unsigned long *)mem == memw); - - memw = 7 - memw; - asm volatile("mov %%rsp, %[tmp] \n\t" - "mov %[stack_top], %%rsp \n\t" - "pushq %[val] \n\t" - "popq %[tmp2] \n\t" - "mov %[tmp], %%rsp" - : [tmp]"=&r"(tmp), [tmp2]"=m"(tmp2) - : [val]"r"(memw), [stack_top]"r"(stack_top) - : "memory"); - report("pop mem (2)", tmp2 == memw); - - memw = 129443 - memw; - asm volatile("mov %%rsp, %[tmp] \n\t" - "mov %[stack_top], %%rsp \n\t" - "pushq %[val] \n\t" - "popq %[tmp2] \n\t" - "mov %[tmp], %%rsp" - : [tmp]"=&r"(tmp), [tmp2]"=r"(tmp2) - : [val]"r"(memw), [stack_top]"r"(stack_top) - : "memory"); - report("pop reg", tmp2 == memw); - - asm volatile("mov %%rsp, %[tmp] \n\t" - "mov %[stack_top], %%rsp \n\t" - "push $1f \n\t" - "ret \n\t" - "2: jmp 2b \n\t" - "1: mov %[tmp], %%rsp" - : [tmp]"=&r"(tmp) : [stack_top]"r"(stack_top) - : "memory"); - report("ret", 1); -} - -unsigned long read_cr0(void) -{ - unsigned long cr0; - - asm volatile ("mov %%cr0, %0" : "=r"(cr0)); - return cr0; -} - -void test_smsw(void) -{ - char mem[16]; - unsigned short msw, msw_orig, *pmsw; - int i, zero; - - msw_orig = read_cr0(); - - asm("smsw %0" : "=r"(msw)); - report("smsw (1)", msw == msw_orig); - - memset(mem, 0, 16); - pmsw = (void *)mem; - asm("smsw %0" : "=m"(pmsw[4])); - zero = 1; - for (i = 0; i < 8; ++i) - if (i != 4 && pmsw[i]) - zero = 0; - report("smsw (2)", msw == pmsw[4] && zero); -} - -void test_lmsw(void) -{ - char mem[16]; - unsigned short msw, *pmsw; - unsigned long cr0; - - cr0 = read_cr0(); - - msw = cr0 ^ 8; - asm("lmsw %0" : : "r"(msw)); - printf("before %lx after %lx\n", cr0, read_cr0()); - report("lmsw (1)", (cr0 ^ read_cr0()) == 8); - - pmsw = (void *)mem; - *pmsw = cr0; - asm("lmsw %0" : : "m"(*pmsw)); - printf("before %lx after %lx\n", cr0, read_cr0()); - report("lmsw (2)", cr0 == read_cr0()); -} - -int main() -{ - void *mem; - unsigned long t1, t2; - - setup_vm(); - mem = vmap(IORAM_BASE_PHYS, IORAM_LEN); - - // test mov reg, r/m and mov r/m, reg - t1 = 0x123456789abcdef; - asm volatile("mov %[t1], (%[mem]) \n\t" - "mov (%[mem]), %[t2]" - : [t2]"=r"(t2) - : [t1]"r"(t1), [mem]"r"(mem) - : "memory"); - report("mov reg, r/m (1)", t2 == 0x123456789abcdef); - - test_cmps(mem); - - test_push(mem); - test_pop(mem); - - test_cr8(); - - test_smsw(); - test_lmsw(); - - printf("\nSUMMARY: %d tests, %d failures\n", tests, fails); - return fails ? 1 : 0; -} diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/test/x86/exit.c qemu-kvm-0.14.1/kvm/user/test/x86/exit.c --- qemu-kvm-0.12.5+noroms/kvm/user/test/x86/exit.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/test/x86/exit.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -#include "runtime.h" - -void exit(unsigned code) -{ - asm volatile("out %al, %dx" : : "a"(code), "d"(0xf4)); - asm volatile("cli; hlt"); -} diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/test/x86/hypercall.c qemu-kvm-0.14.1/kvm/user/test/x86/hypercall.c --- qemu-kvm-0.12.5+noroms/kvm/user/test/x86/hypercall.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/test/x86/hypercall.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,31 +0,0 @@ -#include "libcflat.h" - -#define KVM_HYPERCALL_INTEL ".byte 0x0f,0x01,0xc1" -#define KVM_HYPERCALL_AMD ".byte 0x0f,0x01,0xd9" - -static inline long kvm_hypercall0_intel(unsigned int nr) -{ - long ret; - asm volatile(KVM_HYPERCALL_INTEL - : "=a"(ret) - : "a"(nr)); - return ret; -} - -static inline long kvm_hypercall0_amd(unsigned int nr) -{ - long ret; - asm volatile(KVM_HYPERCALL_AMD - : "=a"(ret) - : "a"(nr)); - return ret; -} - -int main(int ac, char **av) -{ - kvm_hypercall0_intel(-1u); - printf("Hypercall via VMCALL: OK\n"); - kvm_hypercall0_amd(-1u); - printf("Hypercall via VMMCALL: OK\n"); - return 0; -} diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/test/x86/ioram.h qemu-kvm-0.14.1/kvm/user/test/x86/ioram.h --- qemu-kvm-0.12.5+noroms/kvm/user/test/x86/ioram.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/test/x86/ioram.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -#ifndef __IO_RAM_H -#define __IO_RAM_H - -#define IORAM_BASE_PHYS 0xff000000UL -#define IORAM_LEN 0x10000UL - -#endif diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/test/x86/memtest1.S qemu-kvm-0.14.1/kvm/user/test/x86/memtest1.S --- qemu-kvm-0.12.5+noroms/kvm/user/test/x86/memtest1.S 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/test/x86/memtest1.S 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ -.text - -start: - mov $0x1000,%r8 - mov $0x0a,%ecx - -init_page: - dec %ecx - jne no_io - mov $0x0,%al - out %al,$0x80 - mov $0x0a,%ecx - -no_io: - mov %r8,(%r8) - add $0x1000,%r8 - cmp $0x8000000,%r8 - jne init_page - mov $0x1000,%r8 - mov $0x0a,%ecx - -test_loop: - dec %ecx - jne no_io2 - mov $0x0,%al - out %al,$0x80 - mov $0x0a,%ecx - -no_io2: - mov (%r8),%r9 - cmp %r8,%r9 - jne err - add $0x1000,%r8 - cmp $0x8000000,%r8 - jne test_loop - mov $0x1000,%r8 - jmp test_loop - -err: - mov $0xffffffffffffffff,%r12 - mov $0xffffffffffffffff,%r13 - mov $0x0,%al - out %al,$0x80 - jmp err diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/test/x86/msr.c qemu-kvm-0.14.1/kvm/user/test/x86/msr.c --- qemu-kvm-0.12.5+noroms/kvm/user/test/x86/msr.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/test/x86/msr.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,52 +0,0 @@ -/* msr tests */ - -#include "libcflat.h" - -#define MSR_KERNEL_GS_BASE 0xc0000102 /* SwapGS GS shadow */ - -int nr_passed, nr_tests; - -#ifdef __x86_64__ -static void report(const char *name, int passed) -{ - ++nr_tests; - if (passed) - ++nr_passed; - printf("%s: %s\n", name, passed ? "PASS" : "FAIL"); -} - -static void wrmsr(unsigned index, unsigned long long value) -{ - asm volatile ("wrmsr" : : "c"(index), "A"(value)); -} - -static unsigned long long rdmsr(unsigned index) -{ - unsigned long long value; - - asm volatile ("rdmsr" : "=A"(value) : "c"(index)); - - return value; -} -#endif - -static void test_kernel_gs_base(void) -{ -#ifdef __x86_64__ - unsigned long long v1 = 0x123456789abcdef, v2; - - wrmsr(MSR_KERNEL_GS_BASE, v1); - v2 = rdmsr(MSR_KERNEL_GS_BASE); - report("MSR_KERNEL_GS_BASE", v1 == v2); -#endif -} - -int main(int ac, char **av) -{ - test_kernel_gs_base(); - - printf("%d tests, %d failures\n", nr_tests, nr_tests - nr_passed); - - return nr_passed == nr_tests ? 0 : 1; -} - diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/test/x86/port80.c qemu-kvm-0.14.1/kvm/user/test/x86/port80.c --- qemu-kvm-0.12.5+noroms/kvm/user/test/x86/port80.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/test/x86/port80.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,12 +0,0 @@ -#include "libcflat.h" - -int main() -{ - int i; - - printf("begining port 0x80 write test\n"); - for (i = 0; i < 10000000; ++i) - asm volatile("outb %al, $0x80"); - printf("done\n"); - return 0; -} diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/test/x86/print.h qemu-kvm-0.14.1/kvm/user/test/x86/print.h --- qemu-kvm-0.12.5+noroms/kvm/user/test/x86/print.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/test/x86/print.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,19 +0,0 @@ -#ifndef PRINT_H -#define PRINT_H - -.macro PRINT text - -.data - -333: .asciz "\text\n" - -.previous - - push %rdi - lea 333b, %rdi - call print - pop %rdi - -.endm - -#endif diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/test/x86/print.S qemu-kvm-0.14.1/kvm/user/test/x86/print.S --- qemu-kvm-0.12.5+noroms/kvm/user/test/x86/print.S 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/test/x86/print.S 1970-01-01 00:00:00.000000000 +0000 @@ -1,31 +0,0 @@ - -#include "print.h" - -#define PSEUDO_SERIAL_PORT 0xf1 - - -.text - PRINT "boo" - hlt -1: jmp 1b - -.globl print -print: - push %rax - push %rsi - push %rdx - - mov %rdi, %rsi - mov $(PSEUDO_SERIAL_PORT), %edx - -putchar: - cmpb $0, (%rsi) - jz done - outsb - jmp putchar -done: - - pop %rdx - pop %rsi - pop %rax - ret diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/test/x86/realmode.c qemu-kvm-0.14.1/kvm/user/test/x86/realmode.c --- qemu-kvm-0.12.5+noroms/kvm/user/test/x86/realmode.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/test/x86/realmode.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,624 +0,0 @@ -asm(".code16gcc"); - -typedef unsigned char u8; -typedef unsigned short u16; -typedef unsigned u32; -typedef unsigned long long u64; - -void test_function(void); - -asm( - "test_function: \n\t" - "mov $0x1234, %eax \n\t" - "ret" - ); - -static int strlen(const char *str) -{ - int n; - - for (n = 0; *str; ++str) - ++n; - return n; -} - -static void print_serial(const char *buf) -{ - unsigned long len = strlen(buf); - - asm volatile ("cld; addr32/rep/outsb" : "+S"(buf), "+c"(len) : "d"(0xf1)); -} - -static void exit(int code) -{ - asm volatile("out %0, %1" : : "a"(code), "d"((short)0xf4)); -} - -struct regs { - u32 eax, ebx, ecx, edx; - u32 esi, edi, esp, ebp; - u32 eip, eflags; -}; - -static u64 gdt[] = { - 0, - 0x00cf9b000000ffffull, // flat 32-bit code segment - 0x00cf93000000ffffull, // flat 32-bit data segment -}; - -static struct { - u16 limit; - void *base; -} __attribute__((packed)) gdt_descr = { - sizeof(gdt) - 1, - gdt, -}; - -static void exec_in_big_real_mode(const struct regs *inregs, - struct regs *outregs, - const u8 *insn, int insn_len) -{ - unsigned long tmp; - static struct regs save; - int i; - extern u8 test_insn[], test_insn_end[]; - - for (i = 0; i < insn_len; ++i) - test_insn[i] = insn[i]; - for (; i < test_insn_end - test_insn; ++i) - test_insn[i] = 0x90; // nop - - save = *inregs; - asm volatile( - "lgdtl %[gdt_descr] \n\t" - "mov %%cr0, %[tmp] \n\t" - "or $1, %[tmp] \n\t" - "mov %[tmp], %%cr0 \n\t" - "mov %[bigseg], %%gs \n\t" - "and $-2, %[tmp] \n\t" - "mov %[tmp], %%cr0 \n\t" - - "xchg %%eax, %[save]+0 \n\t" - "xchg %%ebx, %[save]+4 \n\t" - "xchg %%ecx, %[save]+8 \n\t" - "xchg %%edx, %[save]+12 \n\t" - "xchg %%esi, %[save]+16 \n\t" - "xchg %%edi, %[save]+20 \n\t" - "xchg %%esp, %[save]+24 \n\t" - "xchg %%ebp, %[save]+28 \n\t" - - "test_insn: . = . + 16\n\t" - "test_insn_end: \n\t" - - "xchg %%eax, %[save]+0 \n\t" - "xchg %%ebx, %[save]+4 \n\t" - "xchg %%ecx, %[save]+8 \n\t" - "xchg %%edx, %[save]+12 \n\t" - "xchg %%esi, %[save]+16 \n\t" - "xchg %%edi, %[save]+20 \n\t" - "xchg %%esp, %[save]+24 \n\t" - "xchg %%ebp, %[save]+28 \n\t" - - /* Save EFLAGS in outregs*/ - "pushfl \n\t" - "popl %[save]+36 \n\t" - - "xor %[tmp], %[tmp] \n\t" - "mov %[tmp], %%gs \n\t" - : [tmp]"=&r"(tmp), [save]"+m"(save) - : [gdt_descr]"m"(gdt_descr), [bigseg]"r"((short)16) - : "cc", "memory" - ); - *outregs = save; -} - -#define R_AX 1 -#define R_BX 2 -#define R_CX 4 -#define R_DX 8 -#define R_SI 16 -#define R_DI 32 -#define R_SP 64 -#define R_BP 128 - -int regs_equal(const struct regs *r1, const struct regs *r2, int ignore) -{ - const u32 *p1 = &r1->eax, *p2 = &r2->eax; // yuck - int i; - - for (i = 0; i < 8; ++i) - if (!(ignore & (1 << i)) && p1[i] != p2[i]) - return 0; - return 1; -} - -#define MK_INSN(name, str) \ - asm ( \ - ".pushsection \".text\" \n\t" \ - "insn_" #name ": " str " \n\t" \ - "insn_" #name "_end: \n\t" \ - ".popsection \n\t" \ - ); \ - extern u8 insn_##name[], insn_##name##_end[] - -void test_shld(void) -{ - struct regs inregs = { .eax = 0xbe, .edx = 0xef000000 }, outregs; - MK_INSN(shld_test, "shld $8,%edx,%eax\n\t"); - - exec_in_big_real_mode(&inregs, &outregs, - insn_shld_test, - insn_shld_test_end - insn_shld_test); - if (outregs.eax != 0xbeef) - print_serial("shld: failure\n"); - else - print_serial("shld: success\n"); -} - -void test_mov_imm(void) -{ - struct regs inregs = { 0 }, outregs; - MK_INSN(mov_r32_imm_1, "mov $1234567890, %eax"); - MK_INSN(mov_r16_imm_1, "mov $1234, %ax"); - MK_INSN(mov_r8_imm_1, "mov $0x12, %ah"); - MK_INSN(mov_r8_imm_2, "mov $0x34, %al"); - MK_INSN(mov_r8_imm_3, "mov $0x12, %ah\n\t" "mov $0x34, %al\n\t"); - - exec_in_big_real_mode(&inregs, &outregs, - insn_mov_r16_imm_1, - insn_mov_r16_imm_1_end - insn_mov_r16_imm_1); - if (!regs_equal(&inregs, &outregs, R_AX) || outregs.eax != 1234) - print_serial("mov test 1: FAIL\n"); - - /* test mov $imm, %eax */ - exec_in_big_real_mode(&inregs, &outregs, - insn_mov_r32_imm_1, - insn_mov_r32_imm_1_end - insn_mov_r32_imm_1); - if (!regs_equal(&inregs, &outregs, R_AX) || outregs.eax != 1234567890) - print_serial("mov test 2: FAIL\n"); - - /* test mov $imm, %al/%ah */ - exec_in_big_real_mode(&inregs, &outregs, - insn_mov_r8_imm_1, - insn_mov_r8_imm_1_end - insn_mov_r8_imm_1); - if (!regs_equal(&inregs, &outregs, R_AX) || outregs.eax != 0x1200) - print_serial("mov test 3: FAIL\n"); - exec_in_big_real_mode(&inregs, &outregs, - insn_mov_r8_imm_2, - insn_mov_r8_imm_2_end - insn_mov_r8_imm_2); - if (!regs_equal(&inregs, &outregs, R_AX) || outregs.eax != 0x34) - print_serial("mov test 4: FAIL\n"); - exec_in_big_real_mode(&inregs, &outregs, - insn_mov_r8_imm_3, - insn_mov_r8_imm_3_end - insn_mov_r8_imm_3); - if (!regs_equal(&inregs, &outregs, R_AX) || outregs.eax != 0x1234) - print_serial("mov test 5: FAIL\n"); -} - -void test_cmp_imm(void) -{ - struct regs inregs = { 0 }, outregs; - MK_INSN(cmp_test1, "mov $0x34, %al\n\t" - "cmp $0x34, %al\n\t"); - MK_INSN(cmp_test2, "mov $0x34, %al\n\t" - "cmp $0x39, %al\n\t"); - MK_INSN(cmp_test3, "mov $0x34, %al\n\t" - "cmp $0x24, %al\n\t"); - - /* test cmp imm8 with AL */ - /* ZF: (bit 6) Zero Flag becomes 1 if an operation results - * in a 0 writeback, or 0 register - */ - exec_in_big_real_mode(&inregs, &outregs, - insn_cmp_test1, - insn_cmp_test1_end - insn_cmp_test1); - if ((outregs.eflags & (1<<6)) != (1<<6)) - print_serial("cmp test 1: FAIL\n"); - - exec_in_big_real_mode(&inregs, &outregs, - insn_cmp_test2, - insn_cmp_test2_end - insn_cmp_test2); - if ((outregs.eflags & (1<<6)) != 0) - print_serial("cmp test 2: FAIL\n"); - - exec_in_big_real_mode(&inregs, &outregs, - insn_cmp_test3, - insn_cmp_test3_end - insn_cmp_test3); - if ((outregs.eflags & (1<<6)) != 0) - print_serial("cmp test 3: FAIL\n"); -} - -void test_add_imm(void) -{ - struct regs inregs = { 0 }, outregs; - MK_INSN(add_test1, "mov $0x43211234, %eax \n\t" - "add $0x12344321, %eax \n\t"); - MK_INSN(add_test2, "mov $0x12, %eax \n\t" - "add $0x21, %al\n\t"); - - exec_in_big_real_mode(&inregs, &outregs, - insn_add_test1, - insn_add_test1_end - insn_add_test1); - if (outregs.eax != 0x55555555) - print_serial("add test 1: FAIL\n"); - - exec_in_big_real_mode(&inregs, &outregs, - insn_add_test2, - insn_add_test2_end - insn_add_test2); - if (outregs.eax != 0x33) - print_serial("add test 2: FAIL\n"); -} - -void test_eflags_insn(void) -{ - struct regs inregs = { 0 }, outregs; - MK_INSN(clc, "clc"); - MK_INSN(cli, "cli"); - MK_INSN(sti, "sti"); - MK_INSN(cld, "cld"); - MK_INSN(std, "std"); - - exec_in_big_real_mode(&inregs, &outregs, - insn_clc, - insn_clc_end - insn_clc); - if (outregs.eflags & 1) - print_serial("clc test: FAIL\n"); - - exec_in_big_real_mode(&inregs, &outregs, - insn_cli, - insn_cli_end - insn_cli); - if (outregs.eflags & (1 << 9)) - print_serial("cli test: FAIL\n"); - - exec_in_big_real_mode(&inregs, &outregs, - insn_sti, - insn_sti_end - insn_sti); - if (!(outregs.eflags & (1 << 9))) - print_serial("sti test: FAIL\n"); - - exec_in_big_real_mode(&inregs, &outregs, - insn_cld, - insn_cld_end - insn_cld); - if (outregs.eflags & (1 << 10)) - print_serial("cld test: FAIL\n"); - - exec_in_big_real_mode(&inregs, &outregs, - insn_std, - insn_std_end - insn_std); - if (!(outregs.eflags & (1 << 10))) - print_serial("std test: FAIL\n"); -} - -void test_io(void) -{ - struct regs inregs = { 0 }, outregs; - MK_INSN(io_test1, "mov $0xff, %al \n\t" - "out %al, $0x10 \n\t" - "in $0x10, %al \n\t"); - MK_INSN(io_test2, "mov $0xffff, %ax \n\t" - "out %ax, $0x10 \n\t" - "in $0x10, %ax \n\t"); - MK_INSN(io_test3, "mov $0xffffffff, %eax \n\t" - "out %eax, $0x10 \n\t" - "in $0x10, %eax \n\t"); - MK_INSN(io_test4, "mov $0x10, %dx \n\t" - "mov $0xff, %al \n\t" - "out %al, %dx \n\t" - "in %dx, %al \n\t"); - MK_INSN(io_test5, "mov $0x10, %dx \n\t" - "mov $0xffff, %ax \n\t" - "out %ax, %dx \n\t" - "in %dx, %ax \n\t"); - MK_INSN(io_test6, "mov $0x10, %dx \n\t" - "mov $0xffffffff, %eax \n\t" - "out %eax, %dx \n\t" - "in %dx, %eax \n\t"); - - exec_in_big_real_mode(&inregs, &outregs, - insn_io_test1, - insn_io_test1_end - insn_io_test1); - - if (!regs_equal(&inregs, &outregs, R_AX) || outregs.eax != 0xff) - print_serial("I/O test 1: FAIL\n"); - - exec_in_big_real_mode(&inregs, &outregs, - insn_io_test2, - insn_io_test2_end - insn_io_test2); - - if (!regs_equal(&inregs, &outregs, R_AX) || outregs.eax != 0xffff) - print_serial("I/O test 2: FAIL\n"); - - exec_in_big_real_mode(&inregs, &outregs, - insn_io_test3, - insn_io_test3_end - insn_io_test3); - - if (!regs_equal(&inregs, &outregs, R_AX) || outregs.eax != 0xffffffff) - print_serial("I/O test 3: FAIL\n"); - - exec_in_big_real_mode(&inregs, &outregs, - insn_io_test4, - insn_io_test4_end - insn_io_test4); - - if (!regs_equal(&inregs, &outregs, R_AX|R_DX) || outregs.eax != 0xff) - print_serial("I/O test 4: FAIL\n"); - - exec_in_big_real_mode(&inregs, &outregs, - insn_io_test5, - insn_io_test5_end - insn_io_test5); - - if (!regs_equal(&inregs, &outregs, R_AX|R_DX) || outregs.eax != 0xffff) - print_serial("I/O test 5: FAIL\n"); - - exec_in_big_real_mode(&inregs, &outregs, - insn_io_test6, - insn_io_test6_end - insn_io_test6); - - if (!regs_equal(&inregs, &outregs, R_AX|R_DX) || outregs.eax != 0xffffffff) - print_serial("I/O test 6: FAIL\n"); - -} - -void test_call(void) -{ - struct regs inregs = { 0 }, outregs; - u32 esp[16]; - - inregs.esp = (u32)esp; - - MK_INSN(call1, "mov $test_function, %eax \n\t" - "call *%eax\n\t"); - MK_INSN(call_near1, "jmp 2f\n\t" - "1: mov $0x1234, %eax\n\t" - "ret\n\t" - "2: call 1b\t"); - MK_INSN(call_near2, "call 1f\n\t" - "jmp 2f\n\t" - "1: mov $0x1234, %eax\n\t" - "ret\n\t" - "2:\t"); - - exec_in_big_real_mode(&inregs, &outregs, - insn_call1, - insn_call1_end - insn_call1); - if(!regs_equal(&inregs, &outregs, R_AX) || outregs.eax != 0x1234) - print_serial("Call Test 1: FAIL\n"); - - exec_in_big_real_mode(&inregs, &outregs, - insn_call_near1, insn_call_near1_end - insn_call_near1); - if(!regs_equal(&inregs, &outregs, R_AX) || outregs.eax != 0x1234) - print_serial("Call near Test 1: FAIL\n"); - exec_in_big_real_mode(&inregs, &outregs, - insn_call_near2, insn_call_near2_end - insn_call_near2); - if(!regs_equal(&inregs, &outregs, R_AX) || outregs.eax != 0x1234) - print_serial("Call near Test 2: FAIL\n"); -} - -void test_jcc_short(void) -{ - struct regs inregs = { 0 }, outregs; - MK_INSN(jnz_short1, "jnz 1f\n\t" - "mov $0x1234, %eax\n\t" - "1:\n\t"); - MK_INSN(jnz_short2, "1:\n\t" - "cmp $0x1234, %eax\n\t" - "mov $0x1234, %eax\n\t" - "jnz 1b\n\t"); - MK_INSN(jmp_short1, "jmp 1f\n\t" - "mov $0x1234, %eax\n\t" - "1:\n\t"); - - exec_in_big_real_mode(&inregs, &outregs, - insn_jnz_short1, insn_jnz_short1_end - insn_jnz_short1); - if(!regs_equal(&inregs, &outregs, 0)) - print_serial("JNZ sort Test 1: FAIL\n"); - - exec_in_big_real_mode(&inregs, &outregs, - insn_jnz_short2, insn_jnz_short2_end - insn_jnz_short2); - if(!regs_equal(&inregs, &outregs, R_AX) || !(outregs.eflags & (1 << 6))) - print_serial("JNZ sort Test 2: FAIL\n"); - - exec_in_big_real_mode(&inregs, &outregs, - insn_jmp_short1, insn_jmp_short1_end - insn_jmp_short1); - if(!regs_equal(&inregs, &outregs, 0)) - print_serial("JMP sort Test 1: FAIL\n"); -} - -void test_jcc_near(void) -{ - struct regs inregs = { 0 }, outregs; - /* encode near jmp manually. gas will not do it if offsets < 127 byte */ - MK_INSN(jnz_near1, ".byte 0x0f, 0x85, 0x06, 0x00\n\t" - "mov $0x1234, %eax\n\t"); - MK_INSN(jnz_near2, "cmp $0x1234, %eax\n\t" - "mov $0x1234, %eax\n\t" - ".byte 0x0f, 0x85, 0xf0, 0xff\n\t"); - MK_INSN(jmp_near1, ".byte 0xE9, 0x06, 0x00\n\t" - "mov $0x1234, %eax\n\t"); - - exec_in_big_real_mode(&inregs, &outregs, - insn_jnz_near1, insn_jnz_near1_end - insn_jnz_near1); - if(!regs_equal(&inregs, &outregs, 0)) - print_serial("JNZ near Test 1: FAIL\n"); - - exec_in_big_real_mode(&inregs, &outregs, - insn_jnz_near2, insn_jnz_near2_end - insn_jnz_near2); - if(!regs_equal(&inregs, &outregs, R_AX) || !(outregs.eflags & (1 << 6))) - print_serial("JNZ near Test 2: FAIL\n"); - - exec_in_big_real_mode(&inregs, &outregs, - insn_jmp_near1, insn_jmp_near1_end - insn_jmp_near1); - if(!regs_equal(&inregs, &outregs, 0)) - print_serial("JMP near Test 1: FAIL\n"); -} - -void test_long_jmp() -{ - struct regs inregs = { 0 }, outregs; - u32 esp[16]; - - inregs.esp = (u32)esp; - MK_INSN(long_jmp, "call 1f\n\t" - "jmp 2f\n\t" - "1: jmp $0, $test_function\n\t" - "2:\n\t"); - exec_in_big_real_mode(&inregs, &outregs, - insn_long_jmp, - insn_long_jmp_end - insn_long_jmp); - if(!regs_equal(&inregs, &outregs, R_AX) || outregs.eax != 0x1234) - print_serial("Long JMP Test: FAIL\n"); -} -void test_push_pop() -{ - struct regs inregs = { 0 }, outregs; - MK_INSN(push32, "mov $0x12345678, %eax\n\t" - "push %eax\n\t" - "pop %ebx\n\t"); - MK_INSN(push16, "mov $0x1234, %ax\n\t" - "push %ax\n\t" - "pop %bx\n\t"); - - MK_INSN(push_es, "mov $0x231, %bx\n\t" //Just write a dummy value to see if it gets overwritten - "mov $0x123, %ax\n\t" - "mov %ax, %es\n\t" - "push %es\n\t" - "pop %bx \n\t" - ); - MK_INSN(pop_es, "push %ax\n\t" - "pop %es\n\t" - "mov %es, %bx\n\t" - ); - MK_INSN(push_pop_ss, "push %ss\n\t" - "pushw %ax\n\t" - "popw %ss\n\t" - "mov %ss, %bx\n\t" - "pop %ss\n\t" - ); - MK_INSN(push_pop_fs, "push %fs\n\t" - "pushl %eax\n\t" - "popl %fs\n\t" - "mov %fs, %ebx\n\t" - "pop %fs\n\t" - ); - - exec_in_big_real_mode(&inregs, &outregs, - insn_push32, - insn_push32_end - insn_push32); - if (!regs_equal(&inregs, &outregs, R_AX|R_BX) || outregs.eax != outregs.ebx || outregs.eax != 0x12345678) - print_serial("Push/Pop Test 1: FAIL\n"); - - exec_in_big_real_mode(&inregs, &outregs, - insn_push16, - insn_push16_end - insn_push16); - - if (!regs_equal(&inregs, &outregs, R_AX|R_BX) || outregs.eax != outregs.ebx || outregs.eax != 0x1234) - print_serial("Push/Pop Test 2: FAIL\n"); - - exec_in_big_real_mode(&inregs, &outregs, - insn_push_es, - insn_push_es_end - insn_push_es); - if (!regs_equal(&inregs, &outregs, R_AX|R_BX) || outregs.ebx != outregs.eax || outregs.eax != 0x123) - print_serial("Push/Pop Test 3: FAIL\n"); - - exec_in_big_real_mode(&inregs, &outregs, - insn_pop_es, - insn_pop_es_end - insn_pop_es); - - if (!regs_equal(&inregs, &outregs, R_AX|R_BX) || outregs.ebx != outregs.eax) - print_serial("Push/Pop Test 4: FAIL\n"); - - exec_in_big_real_mode(&inregs, &outregs, - insn_push_pop_ss, - insn_push_pop_ss_end - insn_push_pop_ss); - - if (!regs_equal(&inregs, &outregs, R_AX|R_BX) || outregs.ebx != outregs.eax) - print_serial("Push/Pop Test 5: FAIL\n"); - - exec_in_big_real_mode(&inregs, &outregs, - insn_push_pop_fs, - insn_push_pop_fs_end - insn_push_pop_fs); - - if (!regs_equal(&inregs, &outregs, R_AX|R_BX) || outregs.ebx != outregs.eax) - print_serial("Push/Pop Test 6: FAIL\n"); -} - -void test_null(void) -{ - struct regs inregs = { 0 }, outregs; - exec_in_big_real_mode(&inregs, &outregs, 0, 0); - if (!regs_equal(&inregs, &outregs, 0)) - print_serial("null test: FAIL\n"); -} - -void realmode_start(void) -{ - test_null(); - - test_shld(); - test_push_pop(); - test_mov_imm(); - test_cmp_imm(); - test_add_imm(); - test_io(); - test_eflags_insn(); - test_jcc_short(); - test_jcc_near(); - /* test_call() uses short jump so call it after testing jcc */ - test_call(); - /* long jmp test uses call near so test it after testing call */ - test_long_jmp(); - - exit(0); -} - -unsigned long long r_gdt[] = { 0, 0x9b000000ffff, 0x93000000ffff }; - -struct __attribute__((packed)) { - unsigned short limit; - void *base; -} r_gdt_descr = { sizeof(r_gdt) - 1, &r_gdt }; - -asm( - ".section .init \n\t" - - ".code32 \n\t" - - "mb_magic = 0x1BADB002 \n\t" - "mb_flags = 0x0 \n\t" - - "# multiboot header \n\t" - ".long mb_magic, mb_flags, 0 - (mb_magic + mb_flags) \n\t" - - ".globl start \n\t" - ".data \n\t" - ". = . + 4096 \n\t" - "stacktop: \n\t" - - ".text \n\t" - "start: \n\t" - "lgdt r_gdt_descr \n\t" - "ljmp $8, $1f; 1: \n\t" - ".code16gcc \n\t" - "mov $16, %eax \n\t" - "mov %ax, %ds \n\t" - "mov %ax, %es \n\t" - "mov %ax, %fs \n\t" - "mov %ax, %gs \n\t" - "mov %ax, %ss \n\t" - "mov %cr0, %eax \n\t" - "btc $0, %eax \n\t" - "mov %eax, %cr0 \n\t" - "ljmp $0, $realmode_entry \n\t" - - "realmode_entry: \n\t" - - "xor %ax, %ax \n\t" - "mov %ax, %ds \n\t" - "mov %ax, %es \n\t" - "mov %ax, %ss \n\t" - "mov %ax, %fs \n\t" - "mov %ax, %gs \n\t" - "mov $stacktop, %sp\n\t" - "ljmp $0, $realmode_start \n\t" - - ".code16gcc \n\t" - ); diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/test/x86/realmode.lds qemu-kvm-0.14.1/kvm/user/test/x86/realmode.lds --- qemu-kvm-0.12.5+noroms/kvm/user/test/x86/realmode.lds 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/test/x86/realmode.lds 1970-01-01 00:00:00.000000000 +0000 @@ -1,12 +0,0 @@ -SECTIONS -{ - . = 16K; - stext = .; - .text : { *(.init) *(.text) } - . = ALIGN(4K); - .data : { *(.data) *(.rodata*) } - . = ALIGN(16); - .bss : { *(.bss) } - edata = .; -} - diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/test/x86/runtime.h qemu-kvm-0.14.1/kvm/user/test/x86/runtime.h --- qemu-kvm-0.12.5+noroms/kvm/user/test/x86/runtime.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/test/x86/runtime.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,6 +0,0 @@ -#ifndef H_RUNTIME -#define H_RUNTIME - -void exit(unsigned code) __attribute__((__noreturn__)); - -#endif diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/test/x86/sieve.c qemu-kvm-0.14.1/kvm/user/test/x86/sieve.c --- qemu-kvm-0.12.5+noroms/kvm/user/test/x86/sieve.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/test/x86/sieve.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,89 +0,0 @@ -#include "vm.h" - -void print(const char *text); - -void printi(int n) -{ - char buf[10], *p = buf; - int s = 0, i; - - if (n < 0) { - n = -n; - s = 1; - } - - while (n) { - *p++ = '0' + n % 10; - n /= 10; - } - - if (s) - *p++ = '-'; - - if (p == buf) - *p++ = '0'; - - for (i = 0; i < (p - buf) / 2; ++i) { - char tmp; - - tmp = buf[i]; - buf[i] = p[-1-i]; - p[-1-i] = tmp; - } - - *p = 0; - - print(buf); -} - -int sieve(char* data, int size) -{ - int i, j, r = 0; - - for (i = 0; i < size; ++i) - data[i] = 1; - - data[0] = data[1] = 0; - - for (i = 2; i < size; ++i) - if (data[i]) { - ++r; - for (j = i*2; j < size; j += i) - data[j] = 0; - } - return r; -} - -void test_sieve(const char *msg, char *data, int size) -{ - int r; - - print(msg); - print(": "); - r = sieve(data, size); - printi(r); - print("\n"); -} - -#define STATIC_SIZE 1000000 -#define VSIZE 100000000 -char static_data[STATIC_SIZE]; - -int main() -{ - void *v; - int i; - - print("starting sieve\n"); - test_sieve("static", static_data, STATIC_SIZE); - setup_vm(); - print("mapped: "); - test_sieve("mapped", static_data, STATIC_SIZE); - for (i = 0; i < 30; ++i) { - v = vmalloc(VSIZE); - test_sieve("virtual", v, VSIZE); - vfree(v); - } - - return 0; -} diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/test/x86/simple.S qemu-kvm-0.14.1/kvm/user/test/x86/simple.S --- qemu-kvm-0.12.5+noroms/kvm/user/test/x86/simple.S 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/test/x86/simple.S 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ - - .text - - mov $0, %al - mov $10000, %ebx -1: - mov %rbx, %rcx -2: - loop 2b - out %al, $0x80 - inc %al - add $10000, %rbx - jmp 1b diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/test/x86/smptest.c qemu-kvm-0.14.1/kvm/user/test/x86/smptest.c --- qemu-kvm-0.12.5+noroms/kvm/user/test/x86/smptest.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/test/x86/smptest.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,25 +0,0 @@ -#include "libcflat.h" -#include "smp.h" - -static void ipi_test(void *data) -{ - int n = (long)data; - - printf("ipi called, cpu %d\n", n); - if (n != smp_id()) - printf("but wrong cpu %d\n", smp_id()); -} - -int main() -{ - int ncpus; - int i; - - smp_init(); - - ncpus = cpu_count(); - printf("found %d cpus\n", ncpus); - for (i = 0; i < ncpus; ++i) - on_cpu(i, ipi_test, (void *)(long)i); - return 0; -} diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/test/x86/stringio.S qemu-kvm-0.14.1/kvm/user/test/x86/stringio.S --- qemu-kvm-0.12.5+noroms/kvm/user/test/x86/stringio.S 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/test/x86/stringio.S 1970-01-01 00:00:00.000000000 +0000 @@ -1,31 +0,0 @@ - -.data - -.macro str name, value - -\name : .long 1f-2f -2: .ascii "\value" -1: -.endm - - str "forward", "forward" - str "backward", "backward" - -.text - - - cld - movl forward, %ecx - lea 4+forward, %rsi - movw $1, %dx - rep outsb - - std - movl backward, %ecx - lea 4+backward-1(%rcx), %rsi - movw $2, %dx - rep outsb - - hlt - - diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/test/x86/test32.S qemu-kvm-0.14.1/kvm/user/test/x86/test32.S --- qemu-kvm-0.12.5+noroms/kvm/user/test/x86/test32.S 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/test/x86/test32.S 1970-01-01 00:00:00.000000000 +0000 @@ -1,8 +0,0 @@ -.code32 - -.text - -1: - mov $0x12, %al - out %al, $0x80 - jmp 1b diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/test/x86/tsc.c qemu-kvm-0.14.1/kvm/user/test/x86/tsc.c --- qemu-kvm-0.12.5+noroms/kvm/user/test/x86/tsc.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/test/x86/tsc.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,38 +0,0 @@ -#include "libcflat.h" - -u64 rdtsc(void) -{ - unsigned a, d; - - asm volatile("rdtsc" : "=a"(a), "=d"(d)); - return a | (u64)d << 32; -} - -void wrtsc(u64 tsc) -{ - unsigned a = tsc, d = tsc >> 32; - - asm volatile("wrmsr" : : "a"(a), "d"(d), "c"(0x10)); -} - -void test_wrtsc(u64 t1) -{ - u64 t2; - - wrtsc(t1); - t2 = rdtsc(); - printf("rdtsc after wrtsc(%lld): %lld\n", t1, t2); -} - -int main() -{ - u64 t1, t2; - - t1 = rdtsc(); - t2 = rdtsc(); - printf("rdtsc latency %lld\n", (unsigned)(t2 - t1)); - - test_wrtsc(0); - test_wrtsc(100000000000ull); - return 0; -} diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/test/x86/vm.c qemu-kvm-0.14.1/kvm/user/test/x86/vm.c --- qemu-kvm-0.12.5+noroms/kvm/user/test/x86/vm.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/test/x86/vm.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,271 +0,0 @@ - -#include "vm.h" - -void print(const char *s); - -#define PAGE_SIZE 4096ul -#define LARGE_PAGE_SIZE (512 * PAGE_SIZE) - -static void *free = 0; -static void *vfree_top = 0; - -static unsigned long virt_to_phys(const void *virt) -{ - return (unsigned long)virt; -} - -static void *phys_to_virt(unsigned long phys) -{ - return (void *)phys; -} - -void *memset(void *data, int c, unsigned long len) -{ - char *s = data; - - while (len--) - *s++ = c; - - return data; -} - -static void free_memory(void *mem, unsigned long size) -{ - while (size >= PAGE_SIZE) { - *(void **)mem = free; - free = mem; - mem += PAGE_SIZE; - size -= PAGE_SIZE; - } -} - -void *alloc_page() -{ - void *p; - - if (!free) - return 0; - - p = free; - free = *(void **)free; - - return p; -} - -void free_page(void *page) -{ - *(void **)page = free; - free = page; -} - -extern char edata; -static unsigned long end_of_memory; - -#define PTE_PRESENT (1ull << 0) -#define PTE_PSE (1ull << 7) -#define PTE_WRITE (1ull << 1) -#define PTE_ADDR (0xffffffffff000ull) - -static void install_pte(unsigned long *cr3, - int pte_level, - void *virt, - unsigned long pte) -{ - int level; - unsigned long *pt = cr3; - unsigned offset; - - for (level = 4; level > pte_level; --level) { - offset = ((unsigned long)virt >> ((level-1) * 9 + 12)) & 511; - if (!(pt[offset] & PTE_PRESENT)) { - unsigned long *new_pt = alloc_page(); - memset(new_pt, 0, PAGE_SIZE); - pt[offset] = virt_to_phys(new_pt) | PTE_PRESENT | PTE_WRITE; - } - pt = phys_to_virt(pt[offset] & 0xffffffffff000ull); - } - offset = ((unsigned long)virt >> (((level-1) * 9) + 12)) & 511; - pt[offset] = pte; -} - -static unsigned long get_pte(unsigned long *cr3, void *virt) -{ - int level; - unsigned long *pt = cr3, pte; - unsigned offset; - - for (level = 4; level > 1; --level) { - offset = ((unsigned long)virt >> (((level-1) * 9) + 12)) & 511; - pte = pt[offset]; - if (!(pte & PTE_PRESENT)) - return 0; - if (level == 2 && (pte & PTE_PSE)) - return pte; - pt = phys_to_virt(pte & 0xffffffffff000ull); - } - offset = ((unsigned long)virt >> (((level-1) * 9) + 12)) & 511; - pte = pt[offset]; - return pte; -} - -static void install_large_page(unsigned long *cr3, - unsigned long phys, - void *virt) -{ - install_pte(cr3, 2, virt, phys | PTE_PRESENT | PTE_WRITE | PTE_PSE); -} - -static void install_page(unsigned long *cr3, - unsigned long phys, - void *virt) -{ - install_pte(cr3, 1, virt, phys | PTE_PRESENT | PTE_WRITE); -} - -static inline void load_cr3(unsigned long cr3) -{ - asm ( "mov %0, %%cr3" : : "r"(cr3) ); -} - -static inline unsigned long read_cr3() -{ - unsigned long cr3; - - asm volatile ( "mov %%cr3, %0" : "=r"(cr3) ); - return cr3; -} - -static inline void load_cr0(unsigned long cr0) -{ - asm volatile ( "mov %0, %%cr0" : : "r"(cr0) ); -} - -static inline unsigned long read_cr0() -{ - unsigned long cr0; - - asm volatile ( "mov %%cr0, %0" : "=r"(cr0) ); - return cr0; -} - -static inline void load_cr4(unsigned long cr4) -{ - asm volatile ( "mov %0, %%cr4" : : "r"(cr4) ); -} - -static inline unsigned long read_cr4() -{ - unsigned long cr4; - - asm volatile ( "mov %%cr4, %0" : "=r"(cr4) ); - return cr4; -} - -struct gdt_table_descr -{ - unsigned short len; - unsigned long *table; -} __attribute__((packed)); - -static inline void load_gdt(unsigned long *table, int nent) -{ - struct gdt_table_descr descr; - - descr.len = nent * 8 - 1; - descr.table = table; - asm volatile ( "lgdt %0" : : "m"(descr) ); -} - -#define SEG_CS_32 8 -#define SEG_CS_64 16 - -struct ljmp { - void *ofs; - unsigned short seg; -}; - -static void setup_mmu(unsigned long len) -{ - unsigned long *cr3 = alloc_page(); - unsigned long phys = 0; - - if (len < (1ul << 32)) - len = 1ul << 32; /* map mmio 1:1 */ - - memset(cr3, 0, PAGE_SIZE); - while (phys + LARGE_PAGE_SIZE <= len) { - install_large_page(cr3, phys, (void *)phys); - phys += LARGE_PAGE_SIZE; - } - while (phys + PAGE_SIZE <= len) { - install_page(cr3, phys, (void *)phys); - phys += PAGE_SIZE; - } - - load_cr3(virt_to_phys(cr3)); - print("paging enabled\n"); -} - -static unsigned int inl(unsigned short port) -{ - unsigned int val; - asm volatile("inl %w1, %0" : "=a"(val) : "Nd"(port)); - return val; -} - -void setup_vm() -{ - end_of_memory = inl(0xd1); - free_memory(&edata, end_of_memory - (unsigned long)&edata); - setup_mmu(end_of_memory); -} - -void *vmalloc(unsigned long size) -{ - void *mem, *p; - unsigned pages; - - size += sizeof(unsigned long); - - size = (size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1); - vfree_top -= size; - mem = p = vfree_top; - pages = size / PAGE_SIZE; - while (pages--) { - install_page(phys_to_virt(read_cr3()), virt_to_phys(alloc_page()), p); - p += PAGE_SIZE; - } - *(unsigned long *)mem = size; - mem += sizeof(unsigned long); - return mem; -} - -void vfree(void *mem) -{ - unsigned long size = ((unsigned long *)mem)[-1]; - - while (size) { - free_page(phys_to_virt(get_pte(phys_to_virt(read_cr3()), mem) & PTE_ADDR)); - mem += PAGE_SIZE; - size -= PAGE_SIZE; - } -} - -void *vmap(unsigned long long phys, unsigned long size) -{ - void *mem, *p; - unsigned pages; - - size = (size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1); - vfree_top -= size; - phys &= ~(unsigned long long)(PAGE_SIZE - 1); - - mem = p = vfree_top; - pages = size / PAGE_SIZE; - while (pages--) { - install_page(phys_to_virt(read_cr3()), phys, p); - phys += PAGE_SIZE; - p += PAGE_SIZE; - } - return mem; -} diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/test/x86/vmexit.c qemu-kvm-0.14.1/kvm/user/test/x86/vmexit.c --- qemu-kvm-0.12.5+noroms/kvm/user/test/x86/vmexit.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/test/x86/vmexit.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,164 +0,0 @@ - -#include "libcflat.h" -#include "smp.h" - -static inline unsigned long long rdtsc() -{ - long long r; - -#ifdef __x86_64__ - unsigned a, d; - - asm volatile ("rdtsc" : "=a"(a), "=d"(d)); - r = a | ((long long)d << 32); -#else - asm volatile ("rdtsc" : "=A"(r)); -#endif - return r; -} - -static unsigned int inl(unsigned short port) -{ - unsigned int val; - asm volatile("inl %w1, %0" : "=a"(val) : "Nd"(port)); - return val; -} - -#define GOAL (1ull << 30) - -#ifdef __x86_64__ -# define R "r" -#else -# define R "e" -#endif - -static void cpuid(void) -{ - asm volatile ("push %%"R "bx; cpuid; pop %%"R "bx" - : : : "eax", "ecx", "edx"); -} - -static void vmcall(void) -{ - unsigned long a = 0, b, c, d; - - asm volatile ("vmcall" : "+a"(a), "=b"(b), "=c"(c), "=d"(d)); -} - -static void mov_from_cr8(void) -{ - unsigned long cr8; - - asm volatile ("mov %%cr8, %0" : "=r"(cr8)); -} - -static void mov_to_cr8(void) -{ - unsigned long cr8 = 0; - - asm volatile ("mov %0, %%cr8" : : "r"(cr8)); -} - -static int is_smp(void) -{ - return cpu_count() > 1; -} - -static void nop(void *junk) -{ -} - -static void ipi(void) -{ - on_cpu(1, nop, 0); -} - -static void ipi_halt(void) -{ - unsigned long long t; - - on_cpu(1, nop, 0); - t = rdtsc() + 2000; - while (rdtsc() < t) - ; -} - -static void inl_pmtimer(void) -{ - inl(0xb008); -} - -static struct test { - void (*func)(void); - const char *name; - int (*valid)(void); - int parallel; -} tests[] = { - { cpuid, "cpuid", .parallel = 1, }, - { vmcall, "vmcall", .parallel = 1, }, - { mov_from_cr8, "mov_from_cr8", .parallel = 1, }, - { mov_to_cr8, "mov_to_cr8" , .parallel = 1, }, - { inl_pmtimer, "inl_from_pmtimer", .parallel = 1, }, - { ipi, "ipi", is_smp, .parallel = 0, }, - { ipi_halt, "ipi+halt", is_smp, .parallel = 0, }, -}; - -unsigned iterations; -volatile int nr_cpus_done; - -static void run_test(void *_func) -{ - int i; - void (*func)(void) = _func; - - for (i = 0; i < iterations; ++i) - func(); - - nr_cpus_done++; -} - -static void do_test(struct test *test) -{ - int i; - unsigned long long t1, t2; - void (*func)(void) = test->func; - - iterations = 32; - - if (test->valid && !test->valid()) { - printf("%s (skipped)\n", test->name); - return; - } - - do { - iterations *= 2; - t1 = rdtsc(); - - if (!test->parallel) { - for (i = 0; i < iterations; ++i) - func(); - } else { - nr_cpus_done = 0; - for (i = cpu_count(); i > 0; i--) - on_cpu_async(i-1, run_test, func); - while (nr_cpus_done < cpu_count()) - ; - } - t2 = rdtsc(); - } while ((t2 - t1) < GOAL); - printf("%s %d\n", test->name, (int)((t2 - t1) / iterations)); -} - -#define ARRAY_SIZE(_x) (sizeof(_x) / sizeof((_x)[0])) - -int main(void) -{ - int i; - - smp_init(); - - for (i = 0; i < ARRAY_SIZE(tests); ++i) - do_test(&tests[i]); - - return 0; -} diff -Nru qemu-kvm-0.12.5+noroms/kvm/user/test/x86/vm.h qemu-kvm-0.14.1/kvm/user/test/x86/vm.h --- qemu-kvm-0.12.5+noroms/kvm/user/test/x86/vm.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm/user/test/x86/vm.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,10 +0,0 @@ -#ifndef VM_H -#define VM_H - -void setup_vm(); - -void *vmalloc(unsigned long size); -void vfree(void *mem); -void *vmap(unsigned long long phys, unsigned long size); - -#endif diff -Nru qemu-kvm-0.12.5+noroms/kvm-all.c qemu-kvm-0.14.1/kvm-all.c --- qemu-kvm-0.12.5+noroms/kvm-all.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm-all.c 2011-05-11 13:29:46.000000000 +0000 @@ -21,25 +21,33 @@ #include #include "qemu-common.h" +#include "qemu-barrier.h" #include "sysemu.h" #include "hw/hw.h" #include "gdbstub.h" #include "kvm.h" +#include "bswap.h" + +/* This check must be after config-host.h is included */ +#ifdef CONFIG_EVENTFD +#include +#endif -#ifdef KVM_UPSTREAM /* KVM uses PAGE_SIZE in it's definition of COALESCED_MMIO_MAX */ #define PAGE_SIZE TARGET_PAGE_SIZE //#define DEBUG_KVM #ifdef DEBUG_KVM -#define dprintf(fmt, ...) \ +#define DPRINTF(fmt, ...) \ do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0) #else -#define dprintf(fmt, ...) \ +#define DPRINTF(fmt, ...) \ do { } while (0) #endif +#ifdef OBSOLETE_KVM_IMPL + typedef struct KVMSlot { target_phys_addr_t start_addr; @@ -51,36 +59,50 @@ typedef struct kvm_dirty_log KVMDirtyLog; -int kvm_allowed = 0; - struct KVMState { KVMSlot slots[32]; int fd; int vmfd; int coalesced_mmio; + struct kvm_coalesced_mmio_ring *coalesced_mmio_ring; int broken_set_mem_region; int migration_log; int vcpu_events; + int robust_singlestep; + int debugregs; #ifdef KVM_CAP_SET_GUEST_DEBUG struct kvm_sw_breakpoint_head kvm_sw_breakpoints; #endif int irqchip_in_kernel; int pit_in_kernel; + int xsave, xcrs; + int many_ioeventfds; }; static KVMState *kvm_state; + +static const KVMCapabilityInfo kvm_required_capabilites[] = { + KVM_CAP_INFO(USER_MEMORY), + KVM_CAP_INFO(DESTROY_MEMORY_REGION_WORKS), + KVM_CAP_LAST_INFO +}; + +#endif + static KVMSlot *kvm_alloc_slot(KVMState *s) { int i; for (i = 0; i < ARRAY_SIZE(s->slots); i++) { /* KVM private memory slots */ - if (i >= 8 && i < 12) + if (i >= 8 && i < 12) { continue; - if (s->slots[i].memory_size == 0) + } + if (s->slots[i].memory_size == 0) { return &s->slots[i]; + } } fprintf(stderr, "%s: no free slot available\n", __func__); @@ -132,6 +154,24 @@ return found; } +int kvm_physical_memory_addr_from_ram(KVMState *s, ram_addr_t ram_addr, + target_phys_addr_t *phys_addr) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(s->slots); i++) { + KVMSlot *mem = &s->slots[i]; + + if (ram_addr >= mem->phys_offset && + ram_addr < mem->phys_offset + mem->memory_size) { + *phys_addr = mem->start_addr + (ram_addr - mem->phys_offset); + return 1; + } + } + + return 0; +} + static int kvm_set_user_memory_region(KVMState *s, KVMSlot *slot) { struct kvm_userspace_memory_region mem; @@ -139,7 +179,7 @@ mem.slot = slot->slot; mem.guest_phys_addr = slot->start_addr; mem.memory_size = slot->memory_size; - mem.userspace_addr = (unsigned long)qemu_get_ram_ptr(slot->phys_offset); + mem.userspace_addr = (unsigned long)qemu_safe_ram_ptr(slot->phys_offset); mem.flags = slot->flags; if (s->migration_log) { mem.flags |= KVM_MEM_LOG_DIRTY_PAGES; @@ -147,15 +187,12 @@ return kvm_vm_ioctl(s, KVM_SET_USER_MEMORY_REGION, &mem); } +#ifdef OBSOLETE_KVM_IMPL static void kvm_reset_vcpu(void *opaque) { CPUState *env = opaque; kvm_arch_reset_vcpu(env); - if (kvm_arch_put_registers(env)) { - fprintf(stderr, "Fatal: kvm vcpu reset failed\n"); - abort(); - } } #endif @@ -164,24 +201,24 @@ return kvm_state->irqchip_in_kernel; } -#ifdef KVM_UPSTREAM int kvm_pit_in_kernel(void) { return kvm_state->pit_in_kernel; } +#ifdef OBSOLETE_KVM_IMPL int kvm_init_vcpu(CPUState *env) { KVMState *s = kvm_state; long mmap_size; int ret; - dprintf("kvm_init_vcpu\n"); + DPRINTF("kvm_init_vcpu\n"); ret = kvm_vm_ioctl(s, KVM_CREATE_VCPU, env->cpu_index); if (ret < 0) { - dprintf("kvm_create_vcpu failed\n"); + DPRINTF("kvm_create_vcpu failed\n"); goto err; } @@ -190,7 +227,7 @@ mmap_size = kvm_ioctl(s, KVM_GET_VCPU_MMAP_SIZE, 0); if (mmap_size < 0) { - dprintf("KVM_GET_VCPU_MMAP_SIZE failed\n"); + DPRINTF("KVM_GET_VCPU_MMAP_SIZE failed\n"); goto err; } @@ -198,19 +235,24 @@ env->kvm_fd, 0); if (env->kvm_run == MAP_FAILED) { ret = -errno; - dprintf("mmap'ing vcpu state failed\n"); + DPRINTF("mmap'ing vcpu state failed\n"); goto err; } + if (s->coalesced_mmio && !s->coalesced_mmio_ring) { + s->coalesced_mmio_ring = + (void *)env->kvm_run + s->coalesced_mmio * PAGE_SIZE; + } + ret = kvm_arch_init_vcpu(env); if (ret == 0) { qemu_register_reset(kvm_reset_vcpu, env); kvm_arch_reset_vcpu(env); - ret = kvm_arch_put_registers(env); } err: return ret; } +#endif /* * dirty pages logging control @@ -247,19 +289,17 @@ int kvm_log_start(target_phys_addr_t phys_addr, ram_addr_t size) { - return kvm_dirty_pages_log_change(phys_addr, size, - KVM_MEM_LOG_DIRTY_PAGES, - KVM_MEM_LOG_DIRTY_PAGES); + return kvm_dirty_pages_log_change(phys_addr, size, KVM_MEM_LOG_DIRTY_PAGES, + KVM_MEM_LOG_DIRTY_PAGES); } int kvm_log_stop(target_phys_addr_t phys_addr, ram_addr_t size) { - return kvm_dirty_pages_log_change(phys_addr, size, - 0, - KVM_MEM_LOG_DIRTY_PAGES); + return kvm_dirty_pages_log_change(phys_addr, size, 0, + KVM_MEM_LOG_DIRTY_PAGES); } -int kvm_set_migration_log(int enable) +static int kvm_set_migration_log(int enable) { KVMState *s = kvm_state; KVMSlot *mem; @@ -270,6 +310,9 @@ for (i = 0; i < ARRAY_SIZE(s->slots); i++) { mem = &s->slots[i]; + if (!mem->memory_size) { + continue; + } if (!!(mem->flags & KVM_MEM_LOG_DIRTY_PAGES) == enable) { continue; } @@ -281,11 +324,41 @@ return 0; } -static int test_le_bit(unsigned long nr, unsigned char *addr) -{ - return (addr[nr >> 3] >> (nr & 7)) & 1; +/* get kvm's dirty pages bitmap and update qemu's */ +static int kvm_get_dirty_pages_log_range(unsigned long start_addr, + unsigned long *bitmap, + unsigned long offset, + unsigned long mem_size) +{ + unsigned int i, j; + unsigned long page_number, addr, addr1, c; + ram_addr_t ram_addr; + unsigned int len = ((mem_size / TARGET_PAGE_SIZE) + HOST_LONG_BITS - 1) / + HOST_LONG_BITS; + + /* + * bitmap-traveling is faster than memory-traveling (for addr...) + * especially when most of the memory is not dirty. + */ + for (i = 0; i < len; i++) { + if (bitmap[i] != 0) { + c = leul_to_cpu(bitmap[i]); + do { + j = ffsl(c) - 1; + c &= ~(1ul << j); + page_number = i * HOST_LONG_BITS + j; + addr1 = page_number * TARGET_PAGE_SIZE; + addr = offset + addr1; + ram_addr = cpu_get_physical_page_desc(addr); + cpu_physical_memory_set_dirty(ram_addr); + } while (c != 0); + } + } + return 0; } +#define ALIGN(x, y) (((x)+(y)-1) & ~((y)-1)) + /** * kvm_physical_sync_dirty_bitmap - Grab dirty bitmap from kernel space * This function updates qemu's dirty bitmap using cpu_physical_memory_set_dirty(). @@ -294,13 +367,11 @@ * @start_add: start of logged region. * @end_addr: end of logged region. */ -int kvm_physical_sync_dirty_bitmap(target_phys_addr_t start_addr, - target_phys_addr_t end_addr) +static int kvm_physical_sync_dirty_bitmap(target_phys_addr_t start_addr, + target_phys_addr_t end_addr) { KVMState *s = kvm_state; unsigned long size, allocated_size = 0; - target_phys_addr_t phys_addr; - ram_addr_t addr; KVMDirtyLog d; KVMSlot *mem; int ret = 0; @@ -312,7 +383,7 @@ break; } - size = ((mem->memory_size >> TARGET_PAGE_BITS) + 7) / 8; + size = ALIGN(((mem->memory_size) >> TARGET_PAGE_BITS), HOST_LONG_BITS) / 8; if (!d.dirty_bitmap) { d.dirty_bitmap = qemu_malloc(size); } else if (size > allocated_size) { @@ -324,33 +395,23 @@ d.slot = mem->slot; if (kvm_vm_ioctl(s, KVM_GET_DIRTY_LOG, &d) == -1) { - dprintf("ioctl failed %d\n", errno); + DPRINTF("ioctl failed %d\n", errno); ret = -1; break; } - for (phys_addr = mem->start_addr, addr = mem->phys_offset; - phys_addr < mem->start_addr + mem->memory_size; - phys_addr += TARGET_PAGE_SIZE, addr += TARGET_PAGE_SIZE) { - unsigned char *bitmap = (unsigned char *)d.dirty_bitmap; - unsigned nr = (phys_addr - mem->start_addr) >> TARGET_PAGE_BITS; - - if (test_le_bit(nr, bitmap)) { - cpu_physical_memory_set_dirty(addr); - } - } - start_addr = phys_addr; + kvm_get_dirty_pages_log_range(mem->start_addr, d.dirty_bitmap, + mem->start_addr, mem->memory_size); + start_addr = mem->start_addr + mem->memory_size; } qemu_free(d.dirty_bitmap); return ret; } -#endif int kvm_coalesce_mmio_region(target_phys_addr_t start, ram_addr_t size) { int ret = -ENOSYS; -#ifdef KVM_CAP_COALESCED_MMIO KVMState *s = kvm_state; if (s->coalesced_mmio) { @@ -361,7 +422,6 @@ ret = kvm_vm_ioctl(s, KVM_REGISTER_COALESCED_MMIO, &zone); } -#endif return ret; } @@ -369,7 +429,6 @@ int kvm_uncoalesce_mmio_region(target_phys_addr_t start, ram_addr_t size) { int ret = -ENOSYS; -#ifdef KVM_CAP_COALESCED_MMIO KVMState *s = kvm_state; if (s->coalesced_mmio) { @@ -380,7 +439,6 @@ ret = kvm_vm_ioctl(s, KVM_UNREGISTER_COALESCED_MMIO, &zone); } -#endif return ret; } @@ -396,30 +454,238 @@ return ret; } -#ifdef KVM_UPSTREAM -int kvm_init(int smp_cpus) +static int kvm_check_many_ioeventfds(void) +{ + /* Userspace can use ioeventfd for io notification. This requires a host + * that supports eventfd(2) and an I/O thread; since eventfd does not + * support SIGIO it cannot interrupt the vcpu. + * + * Older kernels have a 6 device limit on the KVM io bus. Find out so we + * can avoid creating too many ioeventfds. + */ +#if defined(CONFIG_EVENTFD) && defined(CONFIG_IOTHREAD) + int ioeventfds[7]; + int i, ret = 0; + for (i = 0; i < ARRAY_SIZE(ioeventfds); i++) { + ioeventfds[i] = eventfd(0, EFD_CLOEXEC); + if (ioeventfds[i] < 0) { + break; + } + ret = kvm_set_ioeventfd_pio_word(ioeventfds[i], 0, i, true); + if (ret < 0) { + close(ioeventfds[i]); + break; + } + } + + /* Decide whether many devices are supported or not */ + ret = i == ARRAY_SIZE(ioeventfds); + + while (i-- > 0) { + kvm_set_ioeventfd_pio_word(ioeventfds[i], 0, i, false); + close(ioeventfds[i]); + } + return ret; +#else + return 0; +#endif +} + +#ifdef OBSOLETE_KVM_IMPL +static const KVMCapabilityInfo * +kvm_check_extension_list(KVMState *s, const KVMCapabilityInfo *list) +{ + while (list->name) { + if (!kvm_check_extension(s, list->value)) { + return list; + } + list++; + } + return NULL; +} +#endif + +static void kvm_set_phys_mem(target_phys_addr_t start_addr, ram_addr_t size, + ram_addr_t phys_offset) +{ + KVMState *s = kvm_state; + ram_addr_t flags = phys_offset & ~TARGET_PAGE_MASK; + KVMSlot *mem, old; + int err; + + /* kvm works in page size chunks, but the function may be called + with sub-page size and unaligned start address. */ + size = TARGET_PAGE_ALIGN(size); + start_addr = TARGET_PAGE_ALIGN(start_addr); + + /* KVM does not support read-only slots */ + phys_offset &= ~IO_MEM_ROM; + + while (1) { + mem = kvm_lookup_overlapping_slot(s, start_addr, start_addr + size); + if (!mem) { + break; + } + + if (flags < IO_MEM_UNASSIGNED && start_addr >= mem->start_addr && + (start_addr + size <= mem->start_addr + mem->memory_size) && + (phys_offset - start_addr == mem->phys_offset - mem->start_addr)) { + /* The new slot fits into the existing one and comes with + * identical parameters - nothing to be done. */ + return; + } + + old = *mem; + + /* unregister the overlapping slot */ + mem->memory_size = 0; + err = kvm_set_user_memory_region(s, mem); + if (err) { + fprintf(stderr, "%s: error unregistering overlapping slot: %s\n", + __func__, strerror(-err)); + abort(); + } + + /* Workaround for older KVM versions: we can't join slots, even not by + * unregistering the previous ones and then registering the larger + * slot. We have to maintain the existing fragmentation. Sigh. + * + * This workaround assumes that the new slot starts at the same + * address as the first existing one. If not or if some overlapping + * slot comes around later, we will fail (not seen in practice so far) + * - and actually require a recent KVM version. */ + if (s->broken_set_mem_region && + old.start_addr == start_addr && old.memory_size < size && + flags < IO_MEM_UNASSIGNED) { + mem = kvm_alloc_slot(s); + mem->memory_size = old.memory_size; + mem->start_addr = old.start_addr; + mem->phys_offset = old.phys_offset; + mem->flags = 0; + + err = kvm_set_user_memory_region(s, mem); + if (err) { + fprintf(stderr, "%s: error updating slot: %s\n", __func__, + strerror(-err)); + abort(); + } + + start_addr += old.memory_size; + phys_offset += old.memory_size; + size -= old.memory_size; + continue; + } + + /* register prefix slot */ + if (old.start_addr < start_addr) { + mem = kvm_alloc_slot(s); + mem->memory_size = start_addr - old.start_addr; + mem->start_addr = old.start_addr; + mem->phys_offset = old.phys_offset; + mem->flags = 0; + + err = kvm_set_user_memory_region(s, mem); + if (err) { + fprintf(stderr, "%s: error registering prefix slot: %s\n", + __func__, strerror(-err)); + abort(); + } + } + + /* register suffix slot */ + if (old.start_addr + old.memory_size > start_addr + size) { + ram_addr_t size_delta; + + mem = kvm_alloc_slot(s); + mem->start_addr = start_addr + size; + size_delta = mem->start_addr - old.start_addr; + mem->memory_size = old.memory_size - size_delta; + mem->phys_offset = old.phys_offset + size_delta; + mem->flags = 0; + + err = kvm_set_user_memory_region(s, mem); + if (err) { + fprintf(stderr, "%s: error registering suffix slot: %s\n", + __func__, strerror(-err)); + abort(); + } + } + } + + /* in case the KVM bug workaround already "consumed" the new slot */ + if (!size) { + return; + } + /* KVM does not need to know about this memory */ + if (flags >= IO_MEM_UNASSIGNED) { + return; + } + mem = kvm_alloc_slot(s); + mem->memory_size = size; + mem->start_addr = start_addr; + mem->phys_offset = phys_offset; + mem->flags = 0; + + err = kvm_set_user_memory_region(s, mem); + if (err) { + fprintf(stderr, "%s: error registering slot: %s\n", __func__, + strerror(-err)); + abort(); + } +} + +static void kvm_client_set_memory(struct CPUPhysMemoryClient *client, + target_phys_addr_t start_addr, + ram_addr_t size, ram_addr_t phys_offset) +{ + kvm_set_phys_mem(start_addr, size, phys_offset); +} + +static int kvm_client_sync_dirty_bitmap(struct CPUPhysMemoryClient *client, + target_phys_addr_t start_addr, + target_phys_addr_t end_addr) +{ + return kvm_physical_sync_dirty_bitmap(start_addr, end_addr); +} + +static int kvm_client_migration_log(struct CPUPhysMemoryClient *client, + int enable) +{ + return kvm_set_migration_log(enable); +} + +static CPUPhysMemoryClient kvm_cpu_phys_memory_client = { + .set_memory = kvm_client_set_memory, + .sync_dirty_bitmap = kvm_client_sync_dirty_bitmap, + .migration_log = kvm_client_migration_log, +}; + +void kvm_cpu_register_phys_memory_client(void) +{ + cpu_register_phys_memory_client(&kvm_cpu_phys_memory_client); +} + +#ifdef OBSOLETE_KVM_IMPL + +int kvm_init(void) { static const char upgrade_note[] = "Please upgrade to at least kernel 2.6.29 or recent kvm-kmod\n" "(see http://sourceforge.net/projects/kvm).\n"; KVMState *s; + const KVMCapabilityInfo *missing_cap; int ret; int i; - if (smp_cpus > 1) { - fprintf(stderr, "No SMP KVM support, use '-smp 1'\n"); - return -EINVAL; - } - s = qemu_mallocz(sizeof(KVMState)); #ifdef KVM_CAP_SET_GUEST_DEBUG QTAILQ_INIT(&s->kvm_sw_breakpoints); #endif - for (i = 0; i < ARRAY_SIZE(s->slots); i++) + for (i = 0; i < ARRAY_SIZE(s->slots); i++) { s->slots[i].slot = i; - + } s->vmfd = -1; s->fd = qemu_open("/dev/kvm", O_RDWR); if (s->fd == -1) { @@ -430,8 +696,9 @@ ret = kvm_ioctl(s, KVM_GET_API_VERSION, 0); if (ret < KVM_API_VERSION) { - if (ret > 0) + if (ret > 0) { ret = -EINVAL; + } fprintf(stderr, "kvm version too old\n"); goto err; } @@ -443,42 +710,31 @@ } s->vmfd = kvm_ioctl(s, KVM_CREATE_VM, 0); - if (s->vmfd < 0) - goto err; - - /* initially, KVM allocated its own memory and we had to jump through - * hooks to make phys_ram_base point to this. Modern versions of KVM - * just use a user allocated buffer so we can use regular pages - * unmodified. Make sure we have a sufficiently modern version of KVM. - */ - if (!kvm_check_extension(s, KVM_CAP_USER_MEMORY)) { - ret = -EINVAL; - fprintf(stderr, "kvm does not support KVM_CAP_USER_MEMORY\n%s", - upgrade_note); + if (s->vmfd < 0) { +#ifdef TARGET_S390X + fprintf(stderr, "Please add the 'switch_amode' kernel parameter to " + "your host kernel command line\n"); +#endif goto err; } - /* There was a nasty bug in < kvm-80 that prevents memory slots from being - * destroyed properly. Since we rely on this capability, refuse to work - * with any kernel without this capability. */ - if (!kvm_check_extension(s, KVM_CAP_DESTROY_MEMORY_REGION_WORKS)) { + missing_cap = kvm_check_extension_list(s, kvm_required_capabilites); + if (!missing_cap) { + missing_cap = + kvm_check_extension_list(s, kvm_arch_required_capabilities); + } + if (missing_cap) { ret = -EINVAL; - - fprintf(stderr, - "KVM kernel module broken (DESTROY_MEMORY_REGION).\n%s", - upgrade_note); + fprintf(stderr, "kvm does not support %s\n%s", + missing_cap->name, upgrade_note); goto err; } -#ifdef KVM_CAP_COALESCED_MMIO s->coalesced_mmio = kvm_check_extension(s, KVM_CAP_COALESCED_MMIO); -#else - s->coalesced_mmio = 0; -#endif s->broken_set_mem_region = 1; #ifdef KVM_CAP_JOIN_MEMORY_REGIONS_WORKS - ret = kvm_ioctl(s, KVM_CHECK_EXTENSION, KVM_CAP_JOIN_MEMORY_REGIONS_WORKS); + ret = kvm_check_extension(s, KVM_CAP_JOIN_MEMORY_REGIONS_WORKS); if (ret > 0) { s->broken_set_mem_region = 0; } @@ -489,20 +745,47 @@ s->vcpu_events = kvm_check_extension(s, KVM_CAP_VCPU_EVENTS); #endif - ret = kvm_arch_init(s, smp_cpus); - if (ret < 0) + s->robust_singlestep = 0; +#ifdef KVM_CAP_X86_ROBUST_SINGLESTEP + s->robust_singlestep = + kvm_check_extension(s, KVM_CAP_X86_ROBUST_SINGLESTEP); +#endif + + s->debugregs = 0; +#ifdef KVM_CAP_DEBUGREGS + s->debugregs = kvm_check_extension(s, KVM_CAP_DEBUGREGS); +#endif + + s->xsave = 0; +#ifdef KVM_CAP_XSAVE + s->xsave = kvm_check_extension(s, KVM_CAP_XSAVE); +#endif + + s->xcrs = 0; +#ifdef KVM_CAP_XCRS + s->xcrs = kvm_check_extension(s, KVM_CAP_XCRS); +#endif + + ret = kvm_arch_init(s); + if (ret < 0) { goto err; + } kvm_state = s; + cpu_register_phys_memory_client(&kvm_cpu_phys_memory_client); + + s->many_ioeventfds = kvm_check_many_ioeventfds(); return 0; err: if (s) { - if (s->vmfd != -1) + if (s->vmfd != -1) { close(s->vmfd); - if (s->fd != -1) + } + if (s->fd != -1) { close(s->fd); + } } qemu_free(s); @@ -549,78 +832,135 @@ return 1; } -#ifdef KVM_UPSTREAM -static void kvm_run_coalesced_mmio(CPUState *env, struct kvm_run *run) +#ifdef KVM_CAP_INTERNAL_ERROR_DATA +static int kvm_handle_internal_error(CPUState *env, struct kvm_run *run) { -#ifdef KVM_CAP_COALESCED_MMIO - KVMState *s = kvm_state; - if (s->coalesced_mmio) { - struct kvm_coalesced_mmio_ring *ring; + fprintf(stderr, "KVM internal error."); + if (kvm_check_extension(kvm_state, KVM_CAP_INTERNAL_ERROR_DATA)) { + int i; + + fprintf(stderr, " Suberror: %d\n", run->internal.suberror); + for (i = 0; i < run->internal.ndata; ++i) { + fprintf(stderr, "extra data[%d]: %"PRIx64"\n", + i, (uint64_t)run->internal.data[i]); + } + } else { + fprintf(stderr, "\n"); + } + if (run->internal.suberror == KVM_INTERNAL_ERROR_EMULATION) { + fprintf(stderr, "emulation failure\n"); + if (!kvm_arch_stop_on_emulation_error(env)) { + cpu_dump_state(env, stderr, fprintf, CPU_DUMP_CODE); + return 0; + } + } + /* FIXME: Should trigger a qmp message to let management know + * something went wrong. + */ + return -1; +} +#endif - ring = (void *)run + (s->coalesced_mmio * TARGET_PAGE_SIZE); +void kvm_flush_coalesced_mmio_buffer(void) +{ + KVMState *s = kvm_state; + if (s->coalesced_mmio_ring) { + struct kvm_coalesced_mmio_ring *ring = s->coalesced_mmio_ring; while (ring->first != ring->last) { struct kvm_coalesced_mmio *ent; ent = &ring->coalesced_mmio[ring->first]; cpu_physical_memory_write(ent->phys_addr, ent->data, ent->len); - /* FIXME smp_wmb() */ + smp_wmb(); ring->first = (ring->first + 1) % KVM_COALESCED_MMIO_MAX; } } -#endif } -void kvm_cpu_synchronize_state(CPUState *env) +#ifdef OBSOLETE_KVM_IMPL + +static void do_kvm_cpu_synchronize_state(void *_env) { - if (!env->kvm_state->regs_modified) { + CPUState *env = _env; + + if (!env->kvm_vcpu_dirty) { kvm_arch_get_registers(env); - env->kvm_state->regs_modified = 1; + env->kvm_vcpu_dirty = 1; + } +} + +void kvm_cpu_synchronize_state(CPUState *env) +{ + if (!env->kvm_vcpu_dirty) { + run_on_cpu(env, do_kvm_cpu_synchronize_state, env); } } +void kvm_cpu_synchronize_post_reset(CPUState *env) +{ + kvm_arch_put_registers(env, KVM_PUT_RESET_STATE); + env->kvm_vcpu_dirty = 0; +} + +void kvm_cpu_synchronize_post_init(CPUState *env) +{ + kvm_arch_put_registers(env, KVM_PUT_FULL_STATE); + env->kvm_vcpu_dirty = 0; +} + int kvm_cpu_exec(CPUState *env) { struct kvm_run *run = env->kvm_run; int ret; - dprintf("kvm_cpu_exec()\n"); + DPRINTF("kvm_cpu_exec()\n"); do { +#ifndef CONFIG_IOTHREAD if (env->exit_request) { - dprintf("interrupt exit requested\n"); + DPRINTF("interrupt exit requested\n"); ret = 0; break; } +#endif - if (env->kvm_state->regs_modified) { - kvm_arch_put_registers(env); - env->kvm_state->regs_modified = 0; + if (kvm_arch_process_irqchip_events(env)) { + ret = 0; + break; + } + + if (env->kvm_vcpu_dirty) { + kvm_arch_put_registers(env, KVM_PUT_RUNTIME_STATE); + env->kvm_vcpu_dirty = 0; } kvm_arch_pre_run(env, run); + cpu_single_env = NULL; qemu_mutex_unlock_iothread(); ret = kvm_vcpu_ioctl(env, KVM_RUN, 0); qemu_mutex_lock_iothread(); + cpu_single_env = env; kvm_arch_post_run(env, run); + kvm_flush_coalesced_mmio_buffer(); + if (ret == -EINTR || ret == -EAGAIN) { - dprintf("io window exit\n"); + cpu_exit(env); + DPRINTF("io window exit\n"); ret = 0; break; } if (ret < 0) { - dprintf("kvm run failed %s\n", strerror(-ret)); + DPRINTF("kvm run failed %s\n", strerror(-ret)); abort(); } - kvm_run_coalesced_mmio(env, run); - ret = 0; /* exit loop */ switch (run->exit_reason) { case KVM_EXIT_IO: - dprintf("handle_io\n"); + DPRINTF("handle_io\n"); ret = kvm_handle_io(run->io.port, (uint8_t *)run + run->io.data_offset, run->io.direction, @@ -628,7 +968,7 @@ run->io.count); break; case KVM_EXIT_MMIO: - dprintf("handle_mmio\n"); + DPRINTF("handle_mmio\n"); cpu_physical_memory_rw(run->mmio.phys_addr, run->mmio.data, run->mmio.len, @@ -636,28 +976,27 @@ ret = 1; break; case KVM_EXIT_IRQ_WINDOW_OPEN: - dprintf("irq_window_open\n"); + DPRINTF("irq_window_open\n"); break; case KVM_EXIT_SHUTDOWN: - dprintf("shutdown\n"); + DPRINTF("shutdown\n"); qemu_system_reset_request(); ret = 1; break; case KVM_EXIT_UNKNOWN: - dprintf("kvm_exit_unknown\n"); - break; - case KVM_EXIT_FAIL_ENTRY: - dprintf("kvm_exit_fail_entry\n"); + fprintf(stderr, "KVM: unknown exit, hardware reason %" PRIx64 "\n", + (uint64_t)run->hw.hardware_exit_reason); + ret = -1; break; - case KVM_EXIT_EXCEPTION: - dprintf("kvm_exit_exception\n"); +#ifdef KVM_CAP_INTERNAL_ERROR_DATA + case KVM_EXIT_INTERNAL_ERROR: + ret = kvm_handle_internal_error(env, run); break; +#endif case KVM_EXIT_DEBUG: - dprintf("kvm_exit_debug\n"); + DPRINTF("kvm_exit_debug\n"); #ifdef KVM_CAP_SET_GUEST_DEBUG if (kvm_arch_debug(&run->debug.arch)) { - gdb_set_stop_cpu(env); - vm_stop(EXCP_DEBUG); env->exception_index = EXCP_DEBUG; return 0; } @@ -666,12 +1005,17 @@ #endif /* KVM_CAP_SET_GUEST_DEBUG */ break; default: - dprintf("kvm_arch_handle_exit\n"); + DPRINTF("kvm_arch_handle_exit\n"); ret = kvm_arch_handle_exit(env, run); break; } } while (ret > 0); + if (ret < 0) { + cpu_dump_state(env, stderr, fprintf, CPU_DUMP_CODE); + vm_stop(0); + env->exit_request = 1; + } if (env->exit_request) { env->exit_request = 0; env->exception_index = EXCP_INTERRUPT; @@ -680,144 +1024,6 @@ return ret; } -void kvm_set_phys_mem(target_phys_addr_t start_addr, - ram_addr_t size, - ram_addr_t phys_offset) -{ - KVMState *s = kvm_state; - ram_addr_t flags = phys_offset & ~TARGET_PAGE_MASK; - KVMSlot *mem, old; - int err; - - if (start_addr & ~TARGET_PAGE_MASK) { - if (flags >= IO_MEM_UNASSIGNED) { - if (!kvm_lookup_overlapping_slot(s, start_addr, - start_addr + size)) { - return; - } - fprintf(stderr, "Unaligned split of a KVM memory slot\n"); - } else { - fprintf(stderr, "Only page-aligned memory slots supported\n"); - } - abort(); - } - - /* KVM does not support read-only slots */ - phys_offset &= ~IO_MEM_ROM; - - while (1) { - mem = kvm_lookup_overlapping_slot(s, start_addr, start_addr + size); - if (!mem) { - break; - } - - if (flags < IO_MEM_UNASSIGNED && start_addr >= mem->start_addr && - (start_addr + size <= mem->start_addr + mem->memory_size) && - (phys_offset - start_addr == mem->phys_offset - mem->start_addr)) { - /* The new slot fits into the existing one and comes with - * identical parameters - nothing to be done. */ - return; - } - - old = *mem; - - /* unregister the overlapping slot */ - mem->memory_size = 0; - err = kvm_set_user_memory_region(s, mem); - if (err) { - fprintf(stderr, "%s: error unregistering overlapping slot: %s\n", - __func__, strerror(-err)); - abort(); - } - - /* Workaround for older KVM versions: we can't join slots, even not by - * unregistering the previous ones and then registering the larger - * slot. We have to maintain the existing fragmentation. Sigh. - * - * This workaround assumes that the new slot starts at the same - * address as the first existing one. If not or if some overlapping - * slot comes around later, we will fail (not seen in practice so far) - * - and actually require a recent KVM version. */ - if (s->broken_set_mem_region && - old.start_addr == start_addr && old.memory_size < size && - flags < IO_MEM_UNASSIGNED) { - mem = kvm_alloc_slot(s); - mem->memory_size = old.memory_size; - mem->start_addr = old.start_addr; - mem->phys_offset = old.phys_offset; - mem->flags = 0; - - err = kvm_set_user_memory_region(s, mem); - if (err) { - fprintf(stderr, "%s: error updating slot: %s\n", __func__, - strerror(-err)); - abort(); - } - - start_addr += old.memory_size; - phys_offset += old.memory_size; - size -= old.memory_size; - continue; - } - - /* register prefix slot */ - if (old.start_addr < start_addr) { - mem = kvm_alloc_slot(s); - mem->memory_size = start_addr - old.start_addr; - mem->start_addr = old.start_addr; - mem->phys_offset = old.phys_offset; - mem->flags = 0; - - err = kvm_set_user_memory_region(s, mem); - if (err) { - fprintf(stderr, "%s: error registering prefix slot: %s\n", - __func__, strerror(-err)); - abort(); - } - } - - /* register suffix slot */ - if (old.start_addr + old.memory_size > start_addr + size) { - ram_addr_t size_delta; - - mem = kvm_alloc_slot(s); - mem->start_addr = start_addr + size; - size_delta = mem->start_addr - old.start_addr; - mem->memory_size = old.memory_size - size_delta; - mem->phys_offset = old.phys_offset + size_delta; - mem->flags = 0; - - err = kvm_set_user_memory_region(s, mem); - if (err) { - fprintf(stderr, "%s: error registering suffix slot: %s\n", - __func__, strerror(-err)); - abort(); - } - } - } - - /* in case the KVM bug workaround already "consumed" the new slot */ - if (!size) - return; - - /* KVM does not need to know about this memory */ - if (flags >= IO_MEM_UNASSIGNED) - return; - - mem = kvm_alloc_slot(s); - mem->memory_size = size; - mem->start_addr = start_addr; - mem->phys_offset = phys_offset; - mem->flags = 0; - - err = kvm_set_user_memory_region(s, mem); - if (err) { - fprintf(stderr, "%s: error registering slot: %s\n", __func__, - strerror(-err)); - abort(); - } -} - #endif int kvm_ioctl(KVMState *s, int type, ...) { @@ -830,9 +1036,9 @@ va_end(ap); ret = ioctl(s->fd, type, arg); - if (ret == -1) + if (ret == -1) { ret = -errno; - + } return ret; } @@ -847,9 +1053,9 @@ va_end(ap); ret = ioctl(s->vmfd, type, arg); - if (ret == -1) + if (ret == -1) { ret = -errno; - + } return ret; } @@ -864,21 +1070,15 @@ va_end(ap); ret = ioctl(env->kvm_fd, type, arg); - if (ret == -1) + if (ret == -1) { ret = -errno; - + } return ret; } int kvm_has_sync_mmu(void) { -#ifdef KVM_CAP_SYNC_MMU - KVMState *s = kvm_state; - - return kvm_check_extension(s, KVM_CAP_SYNC_MMU); -#else - return 0; -#endif + return kvm_check_extension(kvm_state, KVM_CAP_SYNC_MMU); } int kvm_has_vcpu_events(void) @@ -886,43 +1086,52 @@ return kvm_state->vcpu_events; } -#ifdef KVM_UPSTREAM +int kvm_has_robust_singlestep(void) +{ + return kvm_state->robust_singlestep; +} + +int kvm_has_debugregs(void) +{ + return kvm_state->debugregs; +} + +int kvm_has_xsave(void) +{ + return kvm_state->xsave; +} + +int kvm_has_xcrs(void) +{ + return kvm_state->xcrs; +} + +int kvm_has_many_ioeventfds(void) +{ + if (!kvm_enabled()) { + return 0; + } + return kvm_state->many_ioeventfds; +} + void kvm_setup_guest_memory(void *start, size_t size) { if (!kvm_has_sync_mmu()) { -#ifdef MADV_DONTFORK - int ret = madvise(start, size, MADV_DONTFORK); + int ret = qemu_madvise(start, size, QEMU_MADV_DONTFORK); if (ret) { - perror("madvice"); + perror("qemu_madvise"); + fprintf(stderr, + "Need MADV_DONTFORK in absence of synchronous KVM MMU\n"); exit(1); } -#else - fprintf(stderr, - "Need MADV_DONTFORK in absence of synchronous KVM MMU\n"); - exit(1); -#endif } } -#endif /* KVM_UPSTREAM */ - #ifdef KVM_CAP_SET_GUEST_DEBUG - -#ifdef KVM_UPSTREAM -static void on_vcpu(CPUState *env, void (*func)(void *data), void *data) -{ -#ifdef CONFIG_IOTHREAD - if (env == cpu_single_env) { - func(data); - return; - } - abort(); -#else - func(data); -#endif -} -#endif /* KVM_UPSTREAM */ +#ifndef OBSOLETE_KVM_IMPL +#define run_on_cpu on_vcpu +#endif /* !OBSOLETE_KVM_IMPL */ struct kvm_sw_breakpoint *kvm_find_sw_breakpoint(CPUState *env, target_ulong pc) @@ -930,8 +1139,9 @@ struct kvm_sw_breakpoint *bp; QTAILQ_FOREACH(bp, &env->kvm_state->kvm_sw_breakpoints, entry) { - if (bp->pc == pc) + if (bp->pc == pc) { return bp; + } } return NULL; } @@ -941,8 +1151,6 @@ return !QTAILQ_EMPTY(&env->kvm_state->kvm_sw_breakpoints); } -#ifdef KVM_UPSTREAM - struct kvm_set_guest_debug_data { struct kvm_guest_debug dbg; CPUState *env; @@ -954,10 +1162,6 @@ struct kvm_set_guest_debug_data *dbg_data = data; CPUState *env = dbg_data->env; - if (env->kvm_state->regs_modified) { - kvm_arch_put_registers(env); - env->kvm_state->regs_modified = 0; - } dbg_data->err = kvm_vcpu_ioctl(env, KVM_SET_GUEST_DEBUG, &dbg_data->dbg); } @@ -965,18 +1169,17 @@ { struct kvm_set_guest_debug_data data; - data.dbg.control = 0; - if (env->singlestep_enabled) - data.dbg.control = KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_SINGLESTEP; + data.dbg.control = reinject_trap; + if (env->singlestep_enabled) { + data.dbg.control |= KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_SINGLESTEP; + } kvm_arch_update_guest_debug(env, &data.dbg); - data.dbg.control |= reinject_trap; data.env = env; - on_vcpu(env, kvm_invoke_set_guest_debug, &data); + run_on_cpu(env, kvm_invoke_set_guest_debug, &data); return data.err; } -#endif int kvm_insert_breakpoint(CPUState *current_env, target_ulong addr, target_ulong len, int type) @@ -993,8 +1196,9 @@ } bp = qemu_malloc(sizeof(struct kvm_sw_breakpoint)); - if (!bp) + if (!bp) { return -ENOMEM; + } bp->pc = addr; bp->use_count = 1; @@ -1008,14 +1212,16 @@ bp, entry); } else { err = kvm_arch_insert_hw_breakpoint(addr, len, type); - if (err) + if (err) { return err; + } } for (env = first_cpu; env != NULL; env = env->next_cpu) { err = kvm_update_guest_debug(env, 0); - if (err) + if (err) { return err; + } } return 0; } @@ -1029,8 +1235,9 @@ if (type == GDB_BREAKPOINT_SW) { bp = kvm_find_sw_breakpoint(current_env, addr); - if (!bp) + if (!bp) { return -ENOENT; + } if (bp->use_count > 1) { bp->use_count--; @@ -1038,21 +1245,24 @@ } err = kvm_arch_remove_sw_breakpoint(current_env, bp); - if (err) + if (err) { return err; + } QTAILQ_REMOVE(¤t_env->kvm_state->kvm_sw_breakpoints, bp, entry); qemu_free(bp); } else { err = kvm_arch_remove_hw_breakpoint(addr, len, type); - if (err) + if (err) { return err; + } } for (env = first_cpu; env != NULL; env = env->next_cpu) { err = kvm_update_guest_debug(env, 0); - if (err) + if (err) { return err; + } } return 0; } @@ -1067,15 +1277,17 @@ if (kvm_arch_remove_sw_breakpoint(current_env, bp) != 0) { /* Try harder to find a CPU that currently sees the breakpoint. */ for (env = first_cpu; env != NULL; env = env->next_cpu) { - if (kvm_arch_remove_sw_breakpoint(env, bp) == 0) + if (kvm_arch_remove_sw_breakpoint(env, bp) == 0) { break; + } } } } kvm_arch_remove_all_hw_breakpoints(); - for (env = first_cpu; env != NULL; env = env->next_cpu) + for (env = first_cpu; env != NULL; env = env->next_cpu) { kvm_update_guest_debug(env, 0); + } } #else /* !KVM_CAP_SET_GUEST_DEBUG */ @@ -1102,4 +1314,102 @@ } #endif /* !KVM_CAP_SET_GUEST_DEBUG */ +int kvm_set_signal_mask(CPUState *env, const sigset_t *sigset) +{ + struct kvm_signal_mask *sigmask; + int r; + + if (!sigset) { + return kvm_vcpu_ioctl(env, KVM_SET_SIGNAL_MASK, NULL); + } + + sigmask = qemu_malloc(sizeof(*sigmask) + sizeof(*sigset)); + + sigmask->len = 8; + memcpy(sigmask->sigset, sigset, sizeof(*sigset)); + r = kvm_vcpu_ioctl(env, KVM_SET_SIGNAL_MASK, sigmask); + free(sigmask); + + return r; +} + +int kvm_set_ioeventfd_mmio_long(int fd, uint32_t addr, uint32_t val, bool assign) +{ +#ifdef KVM_IOEVENTFD + int ret; + struct kvm_ioeventfd iofd; + + iofd.datamatch = val; + iofd.addr = addr; + iofd.len = 4; + iofd.flags = KVM_IOEVENTFD_FLAG_DATAMATCH; + iofd.fd = fd; + + if (!kvm_enabled()) { + return -ENOSYS; + } + + if (!assign) { + iofd.flags |= KVM_IOEVENTFD_FLAG_DEASSIGN; + } + + ret = kvm_vm_ioctl(kvm_state, KVM_IOEVENTFD, &iofd); + + if (ret < 0) { + return -errno; + } + + return 0; +#else + return -ENOSYS; +#endif +} + +int kvm_set_ioeventfd_pio_word(int fd, uint16_t addr, uint16_t val, bool assign) +{ +#ifdef KVM_IOEVENTFD + struct kvm_ioeventfd kick = { + .datamatch = val, + .addr = addr, + .len = 2, + .flags = KVM_IOEVENTFD_FLAG_DATAMATCH | KVM_IOEVENTFD_FLAG_PIO, + .fd = fd, + }; + int r; + if (!kvm_enabled()) { + return -ENOSYS; + } + if (!assign) { + kick.flags |= KVM_IOEVENTFD_FLAG_DEASSIGN; + } + r = kvm_vm_ioctl(kvm_state, KVM_IOEVENTFD, &kick); + if (r < 0) { + return r; + } + return 0; +#else + return -ENOSYS; +#endif +} + +#if defined(KVM_IRQFD) +int kvm_set_irqfd(int gsi, int fd, bool assigned) +{ + struct kvm_irqfd irqfd = { + .fd = fd, + .gsi = gsi, + .flags = assigned ? 0 : KVM_IRQFD_FLAG_DEASSIGN, + }; + int r; + if (!kvm_enabled() || !kvm_irqchip_in_kernel()) + return -ENOSYS; + + r = kvm_vm_ioctl(kvm_state, KVM_IRQFD, &irqfd); + if (r < 0) + return r; + return 0; +} +#endif + +#undef PAGE_SIZE #include "qemu-kvm.c" diff -Nru qemu-kvm-0.12.5+noroms/kvm.h qemu-kvm-0.14.1/kvm.h --- qemu-kvm-0.12.5+noroms/kvm.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm.h 2011-05-11 13:29:46.000000000 +0000 @@ -14,53 +14,66 @@ #ifndef QEMU_KVM_H #define QEMU_KVM_H -#include "config.h" +#include +#include "config-host.h" #include "qemu-queue.h" +#ifdef NEED_CPU_H #include "qemu-kvm.h" - -#ifdef KVM_UPSTREAM +#endif #ifdef CONFIG_KVM +#include +#endif + extern int kvm_allowed; +#if defined CONFIG_KVM || !defined NEED_CPU_H #define kvm_enabled() (kvm_allowed) #else #define kvm_enabled() (0) #endif +#ifdef OBSOLETE_KVM_IMPL struct kvm_run; +typedef struct KVMCapabilityInfo { + const char *name; + int value; +} KVMCapabilityInfo; + +#define KVM_CAP_INFO(CAP) { "KVM_CAP_" stringify(CAP), KVM_CAP_##CAP } +#define KVM_CAP_LAST_INFO { NULL, 0 } + /* external API */ -int kvm_init(int smp_cpus); +int kvm_init(void); +#endif /* OBSOLETE_KVM_IMPL */ +int kvm_has_sync_mmu(void); +int kvm_has_vcpu_events(void); +int kvm_has_robust_singlestep(void); +int kvm_has_debugregs(void); +int kvm_has_xsave(void); +int kvm_has_xcrs(void); +int kvm_has_many_ioeventfds(void); + +#ifdef NEED_CPU_H int kvm_init_vcpu(CPUState *env); int kvm_cpu_exec(CPUState *env); -void kvm_set_phys_mem(target_phys_addr_t start_addr, - ram_addr_t size, - ram_addr_t phys_offset); - -int kvm_physical_sync_dirty_bitmap(target_phys_addr_t start_addr, - target_phys_addr_t end_addr); - +#if !defined(CONFIG_USER_ONLY) int kvm_log_start(target_phys_addr_t phys_addr, ram_addr_t size); int kvm_log_stop(target_phys_addr_t phys_addr, ram_addr_t size); -int kvm_set_migration_log(int enable); - -int kvm_has_sync_mmu(void); -#endif /* KVM_UPSTREAM */ -int kvm_has_vcpu_events(void); -int kvm_put_vcpu_events(CPUState *env); -int kvm_get_vcpu_events(CPUState *env); -#ifdef KVM_UPSTREAM +void kvm_cpu_register_phys_memory_client(void); void kvm_setup_guest_memory(void *start, size_t size); int kvm_coalesce_mmio_region(target_phys_addr_t start, ram_addr_t size); int kvm_uncoalesce_mmio_region(target_phys_addr_t start, ram_addr_t size); +void kvm_flush_coalesced_mmio_buffer(void); +#endif int kvm_insert_breakpoint(CPUState *current_env, target_ulong addr, target_ulong len, int type); @@ -68,9 +81,11 @@ target_ulong len, int type); void kvm_remove_all_breakpoints(CPUState *current_env); int kvm_update_guest_debug(CPUState *env, unsigned long reinject_trap); +#ifndef _WIN32 +int kvm_set_signal_mask(CPUState *env, const sigset_t *sigset); +#endif int kvm_pit_in_kernel(void); -int kvm_irqchip_in_kernel(void); /* internal API */ @@ -85,23 +100,39 @@ /* Arch specific hooks */ +#ifdef OBSOLETE_KVM_IMPL +extern const KVMCapabilityInfo kvm_arch_required_capabilities[]; +#endif + int kvm_arch_post_run(CPUState *env, struct kvm_run *run); int kvm_arch_handle_exit(CPUState *env, struct kvm_run *run); int kvm_arch_pre_run(CPUState *env, struct kvm_run *run); +#ifdef OBSOLETE_KVM_IMPL +int kvm_arch_process_irqchip_events(CPUState *env); +#endif + int kvm_arch_get_registers(CPUState *env); -int kvm_arch_put_registers(CPUState *env); +/* state subset only touched by the VCPU itself during runtime */ +#define KVM_PUT_RUNTIME_STATE 1 +/* state subset modified during VCPU reset */ +#define KVM_PUT_RESET_STATE 2 +/* full state set, modified during initialization or on vmload */ +#define KVM_PUT_FULL_STATE 3 -int kvm_arch_init(KVMState *s, int smp_cpus); +int kvm_arch_put_registers(CPUState *env, int level); + +int kvm_arch_init(KVMState *s); int kvm_arch_init_vcpu(CPUState *env); -#endif void kvm_arch_reset_vcpu(CPUState *env); -#ifdef KVM_UPSTREAM + +int kvm_on_sigbus_vcpu(CPUState *env, int code, void *addr); +int kvm_on_sigbus(int code, void *addr); struct kvm_guest_debug; struct kvm_debug_exit_arch; @@ -134,11 +165,15 @@ void kvm_arch_update_guest_debug(CPUState *env, struct kvm_guest_debug *dbg); +bool kvm_arch_stop_on_emulation_error(CPUState *env); + int kvm_check_extension(KVMState *s, unsigned int extension); uint32_t kvm_arch_get_supported_cpuid(CPUState *env, uint32_t function, - int reg); + uint32_t index, int reg); void kvm_cpu_synchronize_state(CPUState *env); +void kvm_cpu_synchronize_post_reset(CPUState *env); +void kvm_cpu_synchronize_post_init(CPUState *env); /* generic hooks - to be moved/refactored once there are more users */ @@ -149,6 +184,54 @@ } } +static inline void cpu_synchronize_post_reset(CPUState *env) +{ + if (kvm_enabled()) { + kvm_cpu_synchronize_post_reset(env); + } +} + +static inline void cpu_synchronize_post_init(CPUState *env) +{ + if (kvm_enabled()) { + kvm_cpu_synchronize_post_init(env); + } +} + +#if !defined(CONFIG_USER_ONLY) +int kvm_physical_memory_addr_from_ram(KVMState *s, ram_addr_t ram_addr, + target_phys_addr_t *phys_addr); #endif #endif +int kvm_set_ioeventfd_mmio_long(int fd, uint32_t adr, uint32_t val, bool assign); + +#if defined(KVM_IRQFD) && defined(CONFIG_KVM) +int kvm_set_irqfd(int gsi, int fd, bool assigned); +#else +static inline +int kvm_set_irqfd(int gsi, int fd, bool assigned) +{ + return -ENOSYS; +} +#endif + +int kvm_set_ioeventfd_pio_word(int fd, uint16_t adr, uint16_t val, bool assign); + +int kvm_has_gsi_routing(void); +int kvm_get_irq_route_gsi(void); +int kvm_add_msix(uint32_t gsi, uint32_t addr_lo, + uint32_t addr_hi, uint32_t data); +int kvm_del_msix(uint32_t gsi, uint32_t addr_lo, + uint32_t addr_hi, uint32_t data); +int kvm_update_msix(uint32_t old_gsi, uint32_t old_addr_lo, + uint32_t old_addr_hi, uint32_t old_data, + uint32_t new_gsi, uint32_t new_addr_lo, + uint32_t new_addr_hi, uint32_t new_data); +int kvm_commit_irq_routes(void); + +int kvm_irqchip_in_kernel(void); + +int kvm_set_irq(int irq, int level, int *status); + +#endif diff -Nru qemu-kvm-0.12.5+noroms/kvm-stub.c qemu-kvm-0.14.1/kvm-stub.c --- qemu-kvm-0.12.5+noroms/kvm-stub.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/kvm-stub.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,193 @@ +/* + * QEMU KVM stub + * + * Copyright Red Hat, Inc. 2010 + * + * Author: Paolo Bonzini + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu-common.h" +#include "sysemu.h" +#include "hw/hw.h" +#include "exec-all.h" +#include "gdbstub.h" +#include "kvm.h" + +int kvm_irqchip_in_kernel(void) +{ + return 0; +} + +int kvm_pit_in_kernel(void) +{ + return 0; +} + + +int kvm_init_vcpu(CPUState *env) +{ + return -ENOSYS; +} + +int kvm_log_start(target_phys_addr_t phys_addr, ram_addr_t size) +{ + return -ENOSYS; +} + +int kvm_log_stop(target_phys_addr_t phys_addr, ram_addr_t size) +{ + return -ENOSYS; +} + +int kvm_coalesce_mmio_region(target_phys_addr_t start, ram_addr_t size) +{ + return -ENOSYS; +} + +int kvm_uncoalesce_mmio_region(target_phys_addr_t start, ram_addr_t size) +{ + return -ENOSYS; +} + +int kvm_check_extension(KVMState *s, unsigned int extension) +{ + return 0; +} + +int kvm_init(void) +{ + return -ENOSYS; +} + +void kvm_flush_coalesced_mmio_buffer(void) +{ +} + +void kvm_cpu_synchronize_state(CPUState *env) +{ +} + +void kvm_cpu_synchronize_post_reset(CPUState *env) +{ +} + +void kvm_cpu_synchronize_post_init(CPUState *env) +{ +} + +int kvm_cpu_exec(CPUState *env) +{ + abort (); +} + +int kvm_has_sync_mmu(void) +{ + return 0; +} + +int kvm_has_vcpu_events(void) +{ + return 0; +} + +int kvm_has_robust_singlestep(void) +{ + return 0; +} + +int kvm_has_many_ioeventfds(void) +{ + return 0; +} + +void kvm_setup_guest_memory(void *start, size_t size) +{ +} + +int kvm_update_guest_debug(CPUState *env, unsigned long reinject_trap) +{ + tb_flush(env); + return 0; +} + +int kvm_insert_breakpoint(CPUState *current_env, target_ulong addr, + target_ulong len, int type) +{ + return -EINVAL; +} + +int kvm_remove_breakpoint(CPUState *current_env, target_ulong addr, + target_ulong len, int type) +{ + return -EINVAL; +} + +void kvm_remove_all_breakpoints(CPUState *current_env) +{ +} + +#ifndef _WIN32 +int kvm_set_signal_mask(CPUState *env, const sigset_t *sigset) +{ + abort(); +} +#endif + +int kvm_set_ioeventfd_pio_word(int fd, uint16_t addr, uint16_t val, bool assign) +{ + return -ENOSYS; +} + +int kvm_set_ioeventfd_mmio_long(int fd, uint32_t adr, uint32_t val, bool assign) +{ + return -ENOSYS; +} + +int kvm_has_gsi_routing(void) +{ + return 0; +} + +int kvm_get_irq_route_gsi(void) +{ + return -ENOSYS; +} + +int kvm_add_msix(uint32_t gsi, uint32_t addr_lo, + uint32_t addr_hi, uint32_t data) +{ + return -ENOSYS; +} + +int kvm_del_msix(uint32_t gsi, uint32_t addr_lo, + uint32_t addr_hi, uint32_t data) +{ + return -ENOSYS; +} + +int kvm_update_msix(uint32_t old_gsi, uint32_t old_addr_lo, + uint32_t old_addr_hi, uint32_t old_data, + uint32_t new_gsi, uint32_t new_addr_lo, + uint32_t new_addr_hi, uint32_t new_data) +{ + return -ENOSYS; +} + +int kvm_commit_irq_routes(void) +{ + return -ENOSYS; +} + +int kvm_set_irq(int irq, int level, int *status) +{ + assert(0); + return -ENOSYS; +} +int kvm_on_sigbus(int code, void *addr) +{ + return 1; +} diff -Nru qemu-kvm-0.12.5+noroms/kvm-tpr-opt.c qemu-kvm-0.14.1/kvm-tpr-opt.c --- qemu-kvm-0.12.5+noroms/kvm-tpr-opt.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/kvm-tpr-opt.c 2011-05-11 13:29:46.000000000 +0000 @@ -14,18 +14,18 @@ #include "hw/hw.h" #include "hw/isa.h" #include "sysemu.h" -#include "qemu-kvm.h" +#include "kvm.h" #include "cpu.h" #include -static uint64_t map_addr(struct kvm_sregs *sregs, target_ulong virt, unsigned *perms) +static uint64_t map_addr(CPUState *env, target_ulong virt, unsigned *perms) { uint64_t mask = ((1ull << 48) - 1) & ~4095ull; uint64_t p, pp = 7; - p = sregs->cr3; - if (sregs->cr4 & 0x20) { + p = env->cr[3]; + if (env->cr[4] & 0x20) { p &= ~31ull; p = ldq_phys(p + 8 * (virt >> 30)); if (!(p & 1)) @@ -68,26 +68,12 @@ static uint8_t read_byte_virt(CPUState *env, target_ulong virt) { - struct kvm_sregs sregs; - - kvm_get_sregs(env, &sregs); - return ldub_phys(map_addr(&sregs, virt, NULL)); + return ldub_phys(map_addr(env, virt, NULL)); } static void write_byte_virt(CPUState *env, target_ulong virt, uint8_t b) { - struct kvm_sregs sregs; - - kvm_get_sregs(env, &sregs); - stb_phys(map_addr(&sregs, virt, NULL), b); -} - -static __u64 kvm_rsp_read(CPUState *env) -{ - struct kvm_regs regs; - - kvm_get_regs(env, ®s); - return regs.rsp; + cpu_physical_memory_write_rom(map_addr(env, virt, NULL), &b, 1); } struct vapic_bios { @@ -121,7 +107,7 @@ cpu_physical_memory_rw(vbios_desc_phys, (void *)&vapic_bios, sizeof vapic_bios, 0); vapic_bios.real_tpr = real_tpr; vapic_bios.vcpu_shift = 7; - cpu_physical_memory_rw(vbios_desc_phys, (void *)&vapic_bios, sizeof vapic_bios, 1); + cpu_physical_memory_write_rom(vbios_desc_phys, (void *)&vapic_bios, sizeof vapic_bios); } static unsigned modrm_reg(uint8_t modrm) @@ -143,7 +129,7 @@ if ((rip & 0xf0000000) != 0x80000000 && (rip & 0xf0000000) != 0xe0000000) return 0; - if (kvm_rsp_read(env) == 0) + if (env->regs[R_ESP] == 0) return 0; b1 = read_byte_virt(env, rip); b2 = read_byte_virt(env, rip + 1); @@ -185,18 +171,16 @@ { uint32_t probe; uint64_t phys; - struct kvm_sregs sregs; unsigned perms; uint32_t i; uint32_t offset, fixup, start = vapic_bios_addr ? : 0xe0000; + uint32_t patch; if (bios_enabled) return 1; - kvm_get_sregs(env, &sregs); - probe = (rip & 0xf0000000) + start; - phys = map_addr(&sregs, probe, &perms); + phys = map_addr(env, probe, &perms); if (phys != start) return 0; bios_addr = probe; @@ -215,7 +199,8 @@ for (i = vapic_bios.fixup_start; i < vapic_bios.fixup_end; i += 4) { offset = ldl_phys(phys + i - vapic_bios.virt_base); fixup = phys + offset; - stl_phys(fixup, ldl_phys(fixup) + bios_addr - vapic_bios.virt_base); + patch = ldl_phys(fixup) + bios_addr - vapic_bios.virt_base; + cpu_physical_memory_write_rom(fixup, (uint8_t *)&patch, 4); } vapic_phys = vapic_bios.vapic - vapic_bios.virt_base + phys; return 1; @@ -242,16 +227,9 @@ return 0; kvm_enable_vapic(env, vapic_phys + (pcr_cpu << 7)); - cpu_physical_memory_rw(vapic_phys + (pcr_cpu << 7) + 4, &one, 1, 1); - env->update_vapic = 0; - bios_enabled = 1; - return 1; -} - -static int enable_vapic(CPUState *env) -{ + cpu_physical_memory_write_rom(vapic_phys + (pcr_cpu << 7) + 4, &one, 1); + env->kvm_vcpu_update_vapic = 0; bios_enabled = 1; - env->update_vapic = 1; return 1; } @@ -310,6 +288,7 @@ void kvm_tpr_access_report(CPUState *env, uint64_t rip, int is_write) { + cpu_synchronize_state(env); if (!instruction_is_ok(env, rip, is_write)) return; if (!bios_is_mapped(env, rip)) @@ -319,13 +298,6 @@ patch_instruction(env, rip); } -void kvm_tpr_vcpu_start(CPUState *env) -{ - kvm_enable_tpr_access_reporting(env); - if (bios_enabled) - kvm_tpr_enable_vapic(env); -} - static void tpr_save(QEMUFile *f, void *s) { int i; @@ -358,7 +330,7 @@ CPUState *env = first_cpu->next_cpu; for (env = first_cpu; env != NULL; env = env->next_cpu) - enable_vapic(env); + env->kvm_vcpu_update_vapic = 1; } return 0; @@ -366,33 +338,30 @@ static void vtpr_ioport_write16(void *opaque, uint32_t addr, uint32_t val) { - struct kvm_regs regs; CPUState *env = cpu_single_env; - struct kvm_sregs sregs; - kvm_get_regs(env, ®s); - kvm_get_sregs(env, &sregs); - vapic_bios_addr = ((sregs.cs.base + regs.rip) & ~(512 - 1)) + val; + + cpu_synchronize_state(env); + + vapic_bios_addr = ((env->segs[R_CS].base + env->eip) & ~(512 - 1)) + val; bios_enabled = 0; } static void vtpr_ioport_write(void *opaque, uint32_t addr, uint32_t val) { CPUState *env = cpu_single_env; - struct kvm_regs regs; - struct kvm_sregs sregs; uint32_t rip; - kvm_get_regs(env, ®s); - rip = regs.rip - 2; + cpu_synchronize_state(env); + + rip = env->eip - 2; write_byte_virt(env, rip, 0x66); write_byte_virt(env, rip + 1, 0x90); if (bios_enabled) return; if (!bios_is_mapped(env, rip)) printf("bios not mapped?\n"); - kvm_get_sregs(env, &sregs); for (addr = 0xfffff000u; addr >= 0x80000000u; addr -= 4096) - if (map_addr(&sregs, addr, NULL) == 0xfee00000u) { + if (map_addr(env, addr, NULL) == 0xfee00000u) { real_tpr = addr + 0x80; break; } @@ -401,10 +370,11 @@ kvm_tpr_enable_vapic(env); } -void kvm_tpr_opt_setup(void) +static void kvm_tpr_opt_setup(void) { - register_savevm("kvm-tpr-opt", 0, 1, tpr_save, tpr_load, NULL); + register_savevm(NULL, "kvm-tpr-opt", 0, 1, tpr_save, tpr_load, NULL); register_ioport_write(0x7e, 1, 1, vtpr_ioport_write, NULL); register_ioport_write(0x7e, 2, 2, vtpr_ioport_write16, NULL); } +device_init(kvm_tpr_opt_setup); diff -Nru qemu-kvm-0.12.5+noroms/KVM_VERSION qemu-kvm-0.14.1/KVM_VERSION --- qemu-kvm-0.12.5+noroms/KVM_VERSION 2010-07-27 19:47:05.000000000 +0000 +++ qemu-kvm-0.14.1/KVM_VERSION 2011-05-11 13:29:46.000000000 +0000 @@ -1 +1 @@ -qemu-kvm-0.12.5 +qemu-kvm-0.14.1 diff -Nru qemu-kvm-0.12.5+noroms/linux-aio.c qemu-kvm-0.14.1/linux-aio.c --- qemu-kvm-0.12.5+noroms/linux-aio.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/linux-aio.c 2011-05-11 13:29:46.000000000 +0000 @@ -123,7 +123,7 @@ do { ret = read(s->efd, &val, sizeof(val)); - } while (ret == 1 && errno == EINTR); + } while (ret == -1 && errno == EINTR); if (ret == -1 && errno == EAGAIN) break; diff -Nru qemu-kvm-0.12.5+noroms/linux-user/alpha/syscall.h qemu-kvm-0.14.1/linux-user/alpha/syscall.h --- qemu-kvm-0.12.5+noroms/linux-user/alpha/syscall.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/linux-user/alpha/syscall.h 2011-05-11 13:29:46.000000000 +0000 @@ -39,3 +39,215 @@ }; #define UNAME_MACHINE "alpha" + +#undef TARGET_EDEADLK +#define TARGET_EDEADLK 11 +#undef TARGET_EAGAIN +#define TARGET_EAGAIN 35 +#undef TARGET_EINPROGRESS +#define TARGET_EINPROGRESS 36 +#undef TARGET_EALREADY +#define TARGET_EALREADY 37 +#undef TARGET_ENOTSOCK +#define TARGET_ENOTSOCK 38 +#undef TARGET_EDESTADDRREQ +#define TARGET_EDESTADDRREQ 39 +#undef TARGET_EMSGSIZE +#define TARGET_EMSGSIZE 40 +#undef TARGET_EPROTOTYPE +#define TARGET_EPROTOTYPE 41 +#undef TARGET_ENOPROTOOPT +#define TARGET_ENOPROTOOPT 42 +#undef TARGET_EPROTONOSUPPORT +#define TARGET_EPROTONOSUPPORT 43 +#undef TARGET_ESOCKTNOSUPPORT +#define TARGET_ESOCKTNOSUPPORT 44 +#undef TARGET_EOPNOTSUPP +#define TARGET_EOPNOTSUPP 45 +#undef TARGET_EPFNOSUPPORT +#define TARGET_EPFNOSUPPORT 46 +#undef TARGET_EAFNOSUPPORT +#define TARGET_EAFNOSUPPORT 47 +#undef TARGET_EADDRINUSE +#define TARGET_EADDRINUSE 48 +#undef TARGET_EADDRNOTAVAIL +#define TARGET_EADDRNOTAVAIL 49 +#undef TARGET_ENETDOWN +#define TARGET_ENETDOWN 50 +#undef TARGET_ENETUNREACH +#define TARGET_ENETUNREACH 51 +#undef TARGET_ENETRESET +#define TARGET_ENETRESET 52 +#undef TARGET_ECONNABORTED +#define TARGET_ECONNABORTED 53 +#undef TARGET_ECONNRESET +#define TARGET_ECONNRESET 54 +#undef TARGET_ENOBUFS +#define TARGET_ENOBUFS 55 +#undef TARGET_EISCONN +#define TARGET_EISCONN 56 +#undef TARGET_ENOTCONN +#define TARGET_ENOTCONN 57 +#undef TARGET_ESHUTDOWN +#define TARGET_ESHUTDOWN 58 +#undef TARGET_ETOOMANYREFS +#define TARGET_ETOOMANYREFS 59 +#undef TARGET_ETIMEDOUT +#define TARGET_ETIMEDOUT 60 +#undef TARGET_ECONNREFUSED +#define TARGET_ECONNREFUSED 61 +#undef TARGET_ELOOP +#define TARGET_ELOOP 62 +#undef TARGET_ENAMETOOLONG +#define TARGET_ENAMETOOLONG 63 +#undef TARGET_EHOSTDOWN +#define TARGET_EHOSTDOWN 64 +#undef TARGET_EHOSTUNREACH +#define TARGET_EHOSTUNREACH 65 +#undef TARGET_ENOTEMPTY +#define TARGET_ENOTEMPTY 66 +// Unused 67 +#undef TARGET_EUSERS +#define TARGET_EUSERS 68 +#undef TARGET_EDQUOT +#define TARGET_EDQUOT 69 +#undef TARGET_ESTALE +#define TARGET_ESTALE 70 +#undef TARGET_EREMOTE +#define TARGET_EREMOTE 71 +// Unused 72-76 +#undef TARGET_ENOLCK +#define TARGET_ENOLCK 77 +#undef TARGET_ENOSYS +#define TARGET_ENOSYS 78 +// Unused 79 +#undef TARGET_ENOMSG +#define TARGET_ENOMSG 80 +#undef TARGET_EIDRM +#define TARGET_EIDRM 81 +#undef TARGET_ENOSR +#define TARGET_ENOSR 82 +#undef TARGET_ETIME +#define TARGET_ETIME 83 +#undef TARGET_EBADMSG +#define TARGET_EBADMSG 84 +#undef TARGET_EPROTO +#define TARGET_EPROTO 85 +#undef TARGET_ENODATA +#define TARGET_ENODATA 86 +#undef TARGET_ENOSTR +#define TARGET_ENOSTR 87 +#undef TARGET_ECHRNG +#define TARGET_ECHRNG 88 +#undef TARGET_EL2NSYNC +#define TARGET_EL2NSYNC 89 +#undef TARGET_EL3HLT +#define TARGET_EL3HLT 90 +#undef TARGET_EL3RST +#define TARGET_EL3RST 91 +#undef TARGET_ENOPKG +#define TARGET_ENOPKG 92 +#undef TARGET_ELNRNG +#define TARGET_ELNRNG 93 +#undef TARGET_EUNATCH +#define TARGET_EUNATCH 94 +#undef TARGET_ENOCSI +#define TARGET_ENOCSI 95 +#undef TARGET_EL2HLT +#define TARGET_EL2HLT 96 +#undef TARGET_EBADE +#define TARGET_EBADE 97 +#undef TARGET_EBADR +#define TARGET_EBADR 98 +#undef TARGET_EXFULL +#define TARGET_EXFULL 99 +#undef TARGET_ENOANO +#define TARGET_ENOANO 100 +#undef TARGET_EBADRQC +#define TARGET_EBADRQC 101 +#undef TARGET_EBADSLT +#define TARGET_EBADSLT 102 +// Unused 103 +#undef TARGET_EBFONT +#define TARGET_EBFONT 104 +#undef TARGET_ENONET +#define TARGET_ENONET 105 +#undef TARGET_ENOLINK +#define TARGET_ENOLINK 106 +#undef TARGET_EADV +#define TARGET_EADV 107 +#undef TARGET_ESRMNT +#define TARGET_ESRMNT 108 +#undef TARGET_ECOMM +#define TARGET_ECOMM 109 +#undef TARGET_EMULTIHOP +#define TARGET_EMULTIHOP 110 +#undef TARGET_EDOTDOT +#define TARGET_EDOTDOT 111 +#undef TARGET_EOVERFLOW +#define TARGET_EOVERFLOW 112 +#undef TARGET_ENOTUNIQ +#define TARGET_ENOTUNIQ 113 +#undef TARGET_EBADFD +#define TARGET_EBADFD 114 +#undef TARGET_EREMCHG +#define TARGET_EREMCHG 115 +#undef TARGET_EILSEQ +#define TARGET_EILSEQ 116 + +// Same as default 117-121 + +#undef TARGET_ELIBACC +#define TARGET_ELIBACC 122 +#undef TARGET_ELIBBAD +#define TARGET_ELIBBAD 123 +#undef TARGET_ELIBSCN +#define TARGET_ELIBSCN 124 +#undef TARGET_ELIBMAX +#define TARGET_ELIBMAX 125 +#undef TARGET_ELIBEXEC +#define TARGET_ELIBEXEC 126 +#undef TARGET_ERESTART +#define TARGET_ERESTART 127 +#undef TARGET_ESTRPIPE +#define TARGET_ESTRPIPE 128 +#undef TARGET_ENOMEDIUM +#define TARGET_ENOMEDIUM 129 +#undef TARGET_EMEDIUMTYPE +#define TARGET_EMEDIUMTYPE 130 +#undef TARGET_ECANCELED +#define TARGET_ECANCELED 131 +#undef TARGET_ENOKEY +#define TARGET_ENOKEY 132 +#undef TARGET_EKEYEXPIRED +#define TARGET_EKEYEXPIRED 133 +#undef TARGET_EKEYREVOKED +#define TARGET_EKEYREVOKED 134 +#undef TARGET_EKEYREJECTED +#define TARGET_EKEYREJECTED 135 +#undef TARGET_EOWNERDEAD +#define TARGET_EOWNERDEAD 136 +#undef TARGET_ENOTRECOVERABLE +#define TARGET_ENOTRECOVERABLE 137 +#undef TARGET_ERFKILL +#define TARGET_ERFKILL 138 + +// For sys_osf_getsysinfo +#define TARGET_GSI_UACPROC 8 +#define TARGET_GSI_IEEE_FP_CONTROL 45 +#define TARGET_GSI_IEEE_STATE_AT_SIGNAL 46 +#define TARGET_GSI_PROC_TYPE 60 +#define TARGET_GSI_GET_HWRPB 101 + +// For sys_ofs_setsysinfo +#define TARGET_SSI_NVPAIRS 1 +#define TARGET_SSI_IEEE_FP_CONTROL 14 +#define TARGET_SSI_IEEE_STATE_AT_SIGNAL 15 +#define TARGET_SSI_IEEE_IGNORE_STATE_AT_SIGNAL 16 +#define TARGET_SSI_IEEE_RAISE_EXCEPTION 1001 + +#define TARGET_SSIN_UACPROC 6 + +#define TARGET_UAC_NOPRINT 1 +#define TARGET_UAC_NOFIX 2 +#define TARGET_UAC_SIGBUS 4 diff -Nru qemu-kvm-0.12.5+noroms/linux-user/alpha/syscall_nr.h qemu-kvm-0.14.1/linux-user/alpha/syscall_nr.h --- qemu-kvm-0.12.5+noroms/linux-user/alpha/syscall_nr.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/linux-user/alpha/syscall_nr.h 2011-05-11 13:29:46.000000000 +0000 @@ -411,3 +411,11 @@ #define TARGET_NR_signalfd 476 #define TARGET_NR_timerfd 477 #define TARGET_NR_eventfd 478 + +/* The following aliases are defined in order to match up with the + standard i386 syscalls implemented in syscalls.c. */ +#define TARGET_NR_chown32 TARGET_NR_chown +#define TARGET_NR_setuid32 TARGET_NR_setuid +#define TARGET_NR_setgid32 TARGET_NR_setgid +#define TARGET_NR_setfsuid32 TARGET_NR_setfsuid +#define TARGET_NR_setfsgid32 TARGET_NR_setfsgid diff -Nru qemu-kvm-0.12.5+noroms/linux-user/alpha/target_signal.h qemu-kvm-0.14.1/linux-user/alpha/target_signal.h --- qemu-kvm-0.12.5+noroms/linux-user/alpha/target_signal.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/linux-user/alpha/target_signal.h 2011-05-11 13:29:46.000000000 +0000 @@ -26,4 +26,31 @@ return state->ir[IR_SP]; } +/* From . */ +#define TARGET_GEN_INTOVF -1 /* integer overflow */ +#define TARGET_GEN_INTDIV -2 /* integer division by zero */ +#define TARGET_GEN_FLTOVF -3 /* fp overflow */ +#define TARGET_GEN_FLTDIV -4 /* fp division by zero */ +#define TARGET_GEN_FLTUND -5 /* fp underflow */ +#define TARGET_GEN_FLTINV -6 /* invalid fp operand */ +#define TARGET_GEN_FLTINE -7 /* inexact fp operand */ +#define TARGET_GEN_DECOVF -8 /* decimal overflow (for COBOL??) */ +#define TARGET_GEN_DECDIV -9 /* decimal division by zero */ +#define TARGET_GEN_DECINV -10 /* invalid decimal operand */ +#define TARGET_GEN_ROPRAND -11 /* reserved operand */ +#define TARGET_GEN_ASSERTERR -12 /* assertion error */ +#define TARGET_GEN_NULPTRERR -13 /* null pointer error */ +#define TARGET_GEN_STKOVF -14 /* stack overflow */ +#define TARGET_GEN_STRLENERR -15 /* string length error */ +#define TARGET_GEN_SUBSTRERR -16 /* substring error */ +#define TARGET_GEN_RANGERR -17 /* range error */ +#define TARGET_GEN_SUBRNG -18 +#define TARGET_GEN_SUBRNG1 -19 +#define TARGET_GEN_SUBRNG2 -20 +#define TARGET_GEN_SUBRNG3 -21 +#define TARGET_GEN_SUBRNG4 -22 +#define TARGET_GEN_SUBRNG5 -23 +#define TARGET_GEN_SUBRNG6 -24 +#define TARGET_GEN_SUBRNG7 -25 + #endif /* TARGET_SIGNAL_H */ diff -Nru qemu-kvm-0.12.5+noroms/linux-user/arm/nwfpe/double_cpdo.c qemu-kvm-0.14.1/linux-user/arm/nwfpe/double_cpdo.c --- qemu-kvm-0.12.5+noroms/linux-user/arm/nwfpe/double_cpdo.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/linux-user/arm/nwfpe/double_cpdo.c 2011-05-11 13:29:46.000000000 +0000 @@ -15,8 +15,7 @@ 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., 675 Mass Ave, Cambridge, MA 02139, USA. + along with this program; if not, see . */ #include "fpa11.h" diff -Nru qemu-kvm-0.12.5+noroms/linux-user/arm/nwfpe/extended_cpdo.c qemu-kvm-0.14.1/linux-user/arm/nwfpe/extended_cpdo.c --- qemu-kvm-0.12.5+noroms/linux-user/arm/nwfpe/extended_cpdo.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/linux-user/arm/nwfpe/extended_cpdo.c 2011-05-11 13:29:46.000000000 +0000 @@ -15,8 +15,7 @@ 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., 675 Mass Ave, Cambridge, MA 02139, USA. + along with this program; if not, see . */ #include "fpa11.h" diff -Nru qemu-kvm-0.12.5+noroms/linux-user/arm/nwfpe/fpa11.c qemu-kvm-0.14.1/linux-user/arm/nwfpe/fpa11.c --- qemu-kvm-0.12.5+noroms/linux-user/arm/nwfpe/fpa11.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/linux-user/arm/nwfpe/fpa11.c 2011-05-11 13:29:46.000000000 +0000 @@ -15,8 +15,7 @@ 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., 675 Mass Ave, Cambridge, MA 02139, USA. + along with this program; if not, see . */ #include "fpa11.h" diff -Nru qemu-kvm-0.12.5+noroms/linux-user/arm/nwfpe/fpa11_cpdo.c qemu-kvm-0.14.1/linux-user/arm/nwfpe/fpa11_cpdo.c --- qemu-kvm-0.12.5+noroms/linux-user/arm/nwfpe/fpa11_cpdo.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/linux-user/arm/nwfpe/fpa11_cpdo.c 2011-05-11 13:29:46.000000000 +0000 @@ -15,8 +15,7 @@ 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., 675 Mass Ave, Cambridge, MA 02139, USA. + along with this program; if not, see . */ #include "fpa11.h" diff -Nru qemu-kvm-0.12.5+noroms/linux-user/arm/nwfpe/fpa11_cpdt.c qemu-kvm-0.14.1/linux-user/arm/nwfpe/fpa11_cpdt.c --- qemu-kvm-0.12.5+noroms/linux-user/arm/nwfpe/fpa11_cpdt.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/linux-user/arm/nwfpe/fpa11_cpdt.c 2011-05-11 13:29:46.000000000 +0000 @@ -16,8 +16,7 @@ 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., 675 Mass Ave, Cambridge, MA 02139, USA. + along with this program; if not, see . */ #include "fpa11.h" diff -Nru qemu-kvm-0.12.5+noroms/linux-user/arm/nwfpe/fpa11_cprt.c qemu-kvm-0.14.1/linux-user/arm/nwfpe/fpa11_cprt.c --- qemu-kvm-0.12.5+noroms/linux-user/arm/nwfpe/fpa11_cprt.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/linux-user/arm/nwfpe/fpa11_cprt.c 2011-05-11 13:29:46.000000000 +0000 @@ -16,8 +16,7 @@ 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., 675 Mass Ave, Cambridge, MA 02139, USA. + along with this program; if not, see . */ #include "fpa11.h" @@ -200,21 +199,21 @@ { case typeSingle: //printk("single.\n"); - if (float32_is_nan(fpa11->fpreg[Fn].fSingle)) + if (float32_is_any_nan(fpa11->fpreg[Fn].fSingle)) goto unordered; rFn = float32_to_floatx80(fpa11->fpreg[Fn].fSingle, &fpa11->fp_status); break; case typeDouble: //printk("double.\n"); - if (float64_is_nan(fpa11->fpreg[Fn].fDouble)) + if (float64_is_any_nan(fpa11->fpreg[Fn].fDouble)) goto unordered; rFn = float64_to_floatx80(fpa11->fpreg[Fn].fDouble, &fpa11->fp_status); break; case typeExtended: //printk("extended.\n"); - if (floatx80_is_nan(fpa11->fpreg[Fn].fExtended)) + if (floatx80_is_any_nan(fpa11->fpreg[Fn].fExtended)) goto unordered; rFn = fpa11->fpreg[Fn].fExtended; break; @@ -226,7 +225,7 @@ { //printk("Fm is a constant: #%d.\n",Fm); rFm = getExtendedConstant(Fm); - if (floatx80_is_nan(rFm)) + if (floatx80_is_any_nan(rFm)) goto unordered; } else @@ -236,21 +235,21 @@ { case typeSingle: //printk("single.\n"); - if (float32_is_nan(fpa11->fpreg[Fm].fSingle)) + if (float32_is_any_nan(fpa11->fpreg[Fm].fSingle)) goto unordered; rFm = float32_to_floatx80(fpa11->fpreg[Fm].fSingle, &fpa11->fp_status); break; case typeDouble: //printk("double.\n"); - if (float64_is_nan(fpa11->fpreg[Fm].fDouble)) + if (float64_is_any_nan(fpa11->fpreg[Fm].fDouble)) goto unordered; rFm = float64_to_floatx80(fpa11->fpreg[Fm].fDouble, &fpa11->fp_status); break; case typeExtended: //printk("extended.\n"); - if (floatx80_is_nan(fpa11->fpreg[Fm].fExtended)) + if (floatx80_is_any_nan(fpa11->fpreg[Fm].fExtended)) goto unordered; rFm = fpa11->fpreg[Fm].fExtended; break; diff -Nru qemu-kvm-0.12.5+noroms/linux-user/arm/nwfpe/fpa11.h qemu-kvm-0.14.1/linux-user/arm/nwfpe/fpa11.h --- qemu-kvm-0.12.5+noroms/linux-user/arm/nwfpe/fpa11.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/linux-user/arm/nwfpe/fpa11.h 2011-05-11 13:29:46.000000000 +0000 @@ -15,8 +15,7 @@ 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., 675 Mass Ave, Cambridge, MA 02139, USA. + along with this program; if not, see . */ #ifndef __FPA11_H__ @@ -90,9 +89,9 @@ extern FPA11* qemufpa; -extern void resetFPA11(void); -extern void SetRoundingMode(const unsigned int); -extern void SetRoundingPrecision(const unsigned int); +void resetFPA11(void); +void SetRoundingMode(const unsigned int); +void SetRoundingPrecision(const unsigned int); static inline unsigned int readRegister(unsigned int reg) { diff -Nru qemu-kvm-0.12.5+noroms/linux-user/arm/nwfpe/fpa11.inl qemu-kvm-0.14.1/linux-user/arm/nwfpe/fpa11.inl --- qemu-kvm-0.12.5+noroms/linux-user/arm/nwfpe/fpa11.inl 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/linux-user/arm/nwfpe/fpa11.inl 2011-05-11 13:29:46.000000000 +0000 @@ -15,8 +15,7 @@ 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., 675 Mass Ave, Cambridge, MA 02139, USA. + along with this program; if not, see . */ #include "fpa11.h" diff -Nru qemu-kvm-0.12.5+noroms/linux-user/arm/nwfpe/fpopcode.c qemu-kvm-0.14.1/linux-user/arm/nwfpe/fpopcode.c --- qemu-kvm-0.12.5+noroms/linux-user/arm/nwfpe/fpopcode.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/linux-user/arm/nwfpe/fpopcode.c 2011-05-11 13:29:46.000000000 +0000 @@ -15,8 +15,7 @@ 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., 675 Mass Ave, Cambridge, MA 02139, USA. + along with this program; if not, see . */ #include "fpa11.h" diff -Nru qemu-kvm-0.12.5+noroms/linux-user/arm/nwfpe/fpopcode.h qemu-kvm-0.14.1/linux-user/arm/nwfpe/fpopcode.h --- qemu-kvm-0.12.5+noroms/linux-user/arm/nwfpe/fpopcode.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/linux-user/arm/nwfpe/fpopcode.h 2011-05-11 13:29:46.000000000 +0000 @@ -15,8 +15,7 @@ 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., 675 Mass Ave, Cambridge, MA 02139, USA. + along with this program; if not, see . */ #ifndef __FPOPCODE_H__ @@ -385,7 +384,7 @@ return float32Constant[nIndex]; } -extern unsigned int getRegisterCount(const unsigned int opcode); -extern unsigned int getDestinationSize(const unsigned int opcode); +unsigned int getRegisterCount(const unsigned int opcode); +unsigned int getDestinationSize(const unsigned int opcode); #endif diff -Nru qemu-kvm-0.12.5+noroms/linux-user/arm/nwfpe/fpsr.h qemu-kvm-0.14.1/linux-user/arm/nwfpe/fpsr.h --- qemu-kvm-0.12.5+noroms/linux-user/arm/nwfpe/fpsr.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/linux-user/arm/nwfpe/fpsr.h 2011-05-11 13:29:46.000000000 +0000 @@ -15,8 +15,7 @@ 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., 675 Mass Ave, Cambridge, MA 02139, USA. + along with this program; if not, see . */ #ifndef __FPSR_H__ diff -Nru qemu-kvm-0.12.5+noroms/linux-user/arm/nwfpe/single_cpdo.c qemu-kvm-0.14.1/linux-user/arm/nwfpe/single_cpdo.c --- qemu-kvm-0.12.5+noroms/linux-user/arm/nwfpe/single_cpdo.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/linux-user/arm/nwfpe/single_cpdo.c 2011-05-11 13:29:46.000000000 +0000 @@ -15,8 +15,7 @@ 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., 675 Mass Ave, Cambridge, MA 02139, USA. + along with this program; if not, see . */ #include "fpa11.h" diff -Nru qemu-kvm-0.12.5+noroms/linux-user/arm/syscall_nr.h qemu-kvm-0.14.1/linux-user/arm/syscall_nr.h --- qemu-kvm-0.12.5+noroms/linux-user/arm/syscall_nr.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/linux-user/arm/syscall_nr.h 2011-05-11 13:29:46.000000000 +0000 @@ -338,7 +338,7 @@ #define TARGET_NR_readlinkat (332) #define TARGET_NR_fchmodat (333) #define TARGET_NR_faccessat (334) - /* 335 for pselect6 */ +#define TARGET_NR_pselect6 (335) /* 336 for ppoll */ #define TARGET_NR_unshare (337) #define TARGET_NR_set_robust_list (338) diff -Nru qemu-kvm-0.12.5+noroms/linux-user/cpu-uname.c qemu-kvm-0.14.1/linux-user/cpu-uname.c --- qemu-kvm-0.12.5+noroms/linux-user/cpu-uname.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/linux-user/cpu-uname.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,72 @@ +/* + * cpu to uname machine name map + * + * Copyright (c) 2009 Loïc Minier + * + * 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, see . + */ + +#include + +#include "qemu.h" +//#include "qemu-common.h" +#include "cpu-uname.h" + +/* return highest utsname machine name for emulated instruction set + * + * NB: the default emulated CPU ("any") might not match any existing CPU, e.g. + * on ARM it has all features turned on, so there is no perfect arch string to + * return here */ +const char *cpu_to_uname_machine(void *cpu_env) +{ +#ifdef TARGET_ARM + /* utsname machine name on linux arm is CPU arch name + endianness, e.g. + * armv7l; to get a list of CPU arch names from the linux source, use: + * grep arch_name: -A1 linux/arch/arm/mm/proc-*.S + * see arch/arm/kernel/setup.c: setup_processor() + * + * to test by CPU id, compare cpu_env->cp15.c0_cpuid to ARM_CPUID_* + * defines and to test by CPU feature, use arm_feature(cpu_env, + * ARM_FEATURE_*) */ + + /* in theory, endianness is configurable on some ARM CPUs, but this isn't + * used in user mode emulation */ +#ifdef TARGET_WORDS_BIGENDIAN +#define utsname_suffix "b" +#else +#define utsname_suffix "l" +#endif + if (arm_feature(cpu_env, ARM_FEATURE_V7)) + return "armv7" utsname_suffix; + if (arm_feature(cpu_env, ARM_FEATURE_V6)) + return "armv6" utsname_suffix; + /* earliest emulated CPU is ARMv5TE; qemu can emulate the 1026, but not its + * Jazelle support */ + return "armv5te" utsname_suffix; +#elif defined(TARGET_X86_64) + return "x86-64"; +#elif defined(TARGET_I386) + /* see arch/x86/kernel/cpu/bugs.c: check_bugs(), 386, 486, 586, 686 */ + uint32_t cpuid_version = ((CPUX86State *)cpu_env)->cpuid_version; + int family = ((cpuid_version >> 8) & 0x0f) + ((cpuid_version >> 20) & 0xff); + if (family == 4) + return "i486"; + if (family == 5) + return "i586"; + return "i686"; +#else + /* default is #define-d in each arch/ subdir */ + return UNAME_MACHINE; +#endif +} diff -Nru qemu-kvm-0.12.5+noroms/linux-user/cpu-uname.h qemu-kvm-0.14.1/linux-user/cpu-uname.h --- qemu-kvm-0.12.5+noroms/linux-user/cpu-uname.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/linux-user/cpu-uname.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1 @@ +const char *cpu_to_uname_machine(void *cpu_env); diff -Nru qemu-kvm-0.12.5+noroms/linux-user/elfload32.c qemu-kvm-0.14.1/linux-user/elfload32.c --- qemu-kvm-0.12.5+noroms/linux-user/elfload32.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/linux-user/elfload32.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,30 +0,0 @@ -#define TARGET_ABI32 -#define load_elf_binary load_elf_binary32 -#define do_init_thread do_init_thread32 - -#include "elfload.c" - -#undef load_elf_binary -#undef do_init_thread - -int load_elf_binary(struct linux_binprm *bprm, struct target_pt_regs *regs, - struct image_info *info); - -int load_elf_binary_multi(struct linux_binprm *bprm, - struct target_pt_regs *regs, - struct image_info *info) -{ - struct elfhdr *elf_ex; - int retval; - - elf_ex = (struct elfhdr *) bprm->buf; /* exec-header */ - if (elf_ex->e_ident[EI_CLASS] == ELFCLASS64) { - retval = load_elf_binary(bprm, regs, info); - } else { - retval = load_elf_binary32(bprm, regs, info); - if (personality(info->personality) == PER_LINUX) - info->personality = PER_LINUX32; - } - - return retval; -} diff -Nru qemu-kvm-0.12.5+noroms/linux-user/elfload.c qemu-kvm-0.14.1/linux-user/elfload.c --- qemu-kvm-0.12.5+noroms/linux-user/elfload.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/linux-user/elfload.c 2011-05-11 13:29:46.000000000 +0000 @@ -35,18 +35,17 @@ * These occupy the top three bytes. */ enum { - ADDR_NO_RANDOMIZE = 0x0040000, /* disable randomization of VA space */ - FDPIC_FUNCPTRS = 0x0080000, /* userspace function ptrs point to descriptors - * (signal handling) - */ - MMAP_PAGE_ZERO = 0x0100000, - ADDR_COMPAT_LAYOUT = 0x0200000, - READ_IMPLIES_EXEC = 0x0400000, - ADDR_LIMIT_32BIT = 0x0800000, - SHORT_INODE = 0x1000000, - WHOLE_SECONDS = 0x2000000, - STICKY_TIMEOUTS = 0x4000000, - ADDR_LIMIT_3GB = 0x8000000, + ADDR_NO_RANDOMIZE = 0x0040000, /* disable randomization of VA space */ + FDPIC_FUNCPTRS = 0x0080000, /* userspace function ptrs point to + descriptors (signal handling) */ + MMAP_PAGE_ZERO = 0x0100000, + ADDR_COMPAT_LAYOUT = 0x0200000, + READ_IMPLIES_EXEC = 0x0400000, + ADDR_LIMIT_32BIT = 0x0800000, + SHORT_INODE = 0x1000000, + WHOLE_SECONDS = 0x2000000, + STICKY_TIMEOUTS = 0x4000000, + ADDR_LIMIT_3GB = 0x8000000, }; /* @@ -56,36 +55,35 @@ * conflict with error returns. */ enum { - PER_LINUX = 0x0000, - PER_LINUX_32BIT = 0x0000 | ADDR_LIMIT_32BIT, - PER_LINUX_FDPIC = 0x0000 | FDPIC_FUNCPTRS, - PER_SVR4 = 0x0001 | STICKY_TIMEOUTS | MMAP_PAGE_ZERO, - PER_SVR3 = 0x0002 | STICKY_TIMEOUTS | SHORT_INODE, - PER_SCOSVR3 = 0x0003 | STICKY_TIMEOUTS | - WHOLE_SECONDS | SHORT_INODE, - PER_OSR5 = 0x0003 | STICKY_TIMEOUTS | WHOLE_SECONDS, - PER_WYSEV386 = 0x0004 | STICKY_TIMEOUTS | SHORT_INODE, - PER_ISCR4 = 0x0005 | STICKY_TIMEOUTS, - PER_BSD = 0x0006, - PER_SUNOS = 0x0006 | STICKY_TIMEOUTS, - PER_XENIX = 0x0007 | STICKY_TIMEOUTS | SHORT_INODE, - PER_LINUX32 = 0x0008, - PER_LINUX32_3GB = 0x0008 | ADDR_LIMIT_3GB, - PER_IRIX32 = 0x0009 | STICKY_TIMEOUTS,/* IRIX5 32-bit */ - PER_IRIXN32 = 0x000a | STICKY_TIMEOUTS,/* IRIX6 new 32-bit */ - PER_IRIX64 = 0x000b | STICKY_TIMEOUTS,/* IRIX6 64-bit */ - PER_RISCOS = 0x000c, - PER_SOLARIS = 0x000d | STICKY_TIMEOUTS, - PER_UW7 = 0x000e | STICKY_TIMEOUTS | MMAP_PAGE_ZERO, - PER_OSF4 = 0x000f, /* OSF/1 v4 */ - PER_HPUX = 0x0010, - PER_MASK = 0x00ff, + PER_LINUX = 0x0000, + PER_LINUX_32BIT = 0x0000 | ADDR_LIMIT_32BIT, + PER_LINUX_FDPIC = 0x0000 | FDPIC_FUNCPTRS, + PER_SVR4 = 0x0001 | STICKY_TIMEOUTS | MMAP_PAGE_ZERO, + PER_SVR3 = 0x0002 | STICKY_TIMEOUTS | SHORT_INODE, + PER_SCOSVR3 = 0x0003 | STICKY_TIMEOUTS | WHOLE_SECONDS | SHORT_INODE, + PER_OSR5 = 0x0003 | STICKY_TIMEOUTS | WHOLE_SECONDS, + PER_WYSEV386 = 0x0004 | STICKY_TIMEOUTS | SHORT_INODE, + PER_ISCR4 = 0x0005 | STICKY_TIMEOUTS, + PER_BSD = 0x0006, + PER_SUNOS = 0x0006 | STICKY_TIMEOUTS, + PER_XENIX = 0x0007 | STICKY_TIMEOUTS | SHORT_INODE, + PER_LINUX32 = 0x0008, + PER_LINUX32_3GB = 0x0008 | ADDR_LIMIT_3GB, + PER_IRIX32 = 0x0009 | STICKY_TIMEOUTS,/* IRIX5 32-bit */ + PER_IRIXN32 = 0x000a | STICKY_TIMEOUTS,/* IRIX6 new 32-bit */ + PER_IRIX64 = 0x000b | STICKY_TIMEOUTS,/* IRIX6 64-bit */ + PER_RISCOS = 0x000c, + PER_SOLARIS = 0x000d | STICKY_TIMEOUTS, + PER_UW7 = 0x000e | STICKY_TIMEOUTS | MMAP_PAGE_ZERO, + PER_OSF4 = 0x000f, /* OSF/1 v4 */ + PER_HPUX = 0x0010, + PER_MASK = 0x00ff, }; /* * Return the base personality without flags. */ -#define personality(pers) (pers & PER_MASK) +#define personality(pers) (pers & PER_MASK) /* this flag is uneffective under linux too, should be deleted */ #ifndef MAP_DENYWRITE @@ -97,6 +95,22 @@ #define ELIBBAD 80 #endif +#ifdef TARGET_WORDS_BIGENDIAN +#define ELF_DATA ELFDATA2MSB +#else +#define ELF_DATA ELFDATA2LSB +#endif + +typedef target_ulong target_elf_greg_t; +#ifdef USE_UID16 +typedef uint16_t target_uid_t; +typedef uint16_t target_gid_t; +#else +typedef uint32_t target_uid_t; +typedef uint32_t target_gid_t; +#endif +typedef int32_t target_pid_t; + #ifdef TARGET_I386 #define ELF_PLATFORM get_elf_platform() @@ -116,7 +130,7 @@ static uint32_t get_elf_hwcap(void) { - return thread_env->cpuid_features; + return thread_env->cpuid_features; } #ifdef TARGET_X86_64 @@ -124,7 +138,6 @@ #define elf_check_arch(x) ( ((x) == ELF_ARCH) ) #define ELF_CLASS ELFCLASS64 -#define ELF_DATA ELFDATA2LSB #define ELF_ARCH EM_X86_64 static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) @@ -134,11 +147,6 @@ regs->rip = infop->entry; } -typedef target_ulong target_elf_greg_t; -typedef uint32_t target_uid_t; -typedef uint32_t target_gid_t; -typedef int32_t target_pid_t; - #define ELF_NREG 27 typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; @@ -192,11 +200,11 @@ /* * These are used to set parameters in the core dumps. */ -#define ELF_CLASS ELFCLASS32 -#define ELF_DATA ELFDATA2LSB -#define ELF_ARCH EM_386 +#define ELF_CLASS ELFCLASS32 +#define ELF_ARCH EM_386 -static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) +static inline void init_thread(struct target_pt_regs *regs, + struct image_info *infop) { regs->esp = infop->start_stack; regs->eip = infop->entry; @@ -211,11 +219,6 @@ regs->edx = 0; } -typedef target_ulong target_elf_greg_t; -typedef uint16_t target_uid_t; -typedef uint16_t target_gid_t; -typedef int32_t target_pid_t; - #define ELF_NREG 17 typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; @@ -249,7 +252,7 @@ #endif #define USE_ELF_CORE_DUMP -#define ELF_EXEC_PAGESIZE 4096 +#define ELF_EXEC_PAGESIZE 4096 #endif @@ -259,21 +262,17 @@ #define elf_check_arch(x) ( (x) == EM_ARM ) -#define ELF_CLASS ELFCLASS32 -#ifdef TARGET_WORDS_BIGENDIAN -#define ELF_DATA ELFDATA2MSB -#else -#define ELF_DATA ELFDATA2LSB -#endif -#define ELF_ARCH EM_ARM +#define ELF_CLASS ELFCLASS32 +#define ELF_ARCH EM_ARM -static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) +static inline void init_thread(struct target_pt_regs *regs, + struct image_info *infop) { abi_long stack = infop->start_stack; memset(regs, 0, sizeof(*regs)); regs->ARM_cpsr = 0x10; if (infop->entry & 1) - regs->ARM_cpsr |= CPSR_T; + regs->ARM_cpsr |= CPSR_T; regs->ARM_pc = infop->entry & 0xfffffffe; regs->ARM_sp = infop->start_stack; /* FIXME - what to for failure of get_user()? */ @@ -286,62 +285,57 @@ regs->ARM_r10 = infop->start_data; } -typedef uint32_t target_elf_greg_t; -typedef uint16_t target_uid_t; -typedef uint16_t target_gid_t; -typedef int32_t target_pid_t; - #define ELF_NREG 18 typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUState *env) { - (*regs)[0] = env->regs[0]; - (*regs)[1] = env->regs[1]; - (*regs)[2] = env->regs[2]; - (*regs)[3] = env->regs[3]; - (*regs)[4] = env->regs[4]; - (*regs)[5] = env->regs[5]; - (*regs)[6] = env->regs[6]; - (*regs)[7] = env->regs[7]; - (*regs)[8] = env->regs[8]; - (*regs)[9] = env->regs[9]; - (*regs)[10] = env->regs[10]; - (*regs)[11] = env->regs[11]; - (*regs)[12] = env->regs[12]; - (*regs)[13] = env->regs[13]; - (*regs)[14] = env->regs[14]; - (*regs)[15] = env->regs[15]; + (*regs)[0] = tswapl(env->regs[0]); + (*regs)[1] = tswapl(env->regs[1]); + (*regs)[2] = tswapl(env->regs[2]); + (*regs)[3] = tswapl(env->regs[3]); + (*regs)[4] = tswapl(env->regs[4]); + (*regs)[5] = tswapl(env->regs[5]); + (*regs)[6] = tswapl(env->regs[6]); + (*regs)[7] = tswapl(env->regs[7]); + (*regs)[8] = tswapl(env->regs[8]); + (*regs)[9] = tswapl(env->regs[9]); + (*regs)[10] = tswapl(env->regs[10]); + (*regs)[11] = tswapl(env->regs[11]); + (*regs)[12] = tswapl(env->regs[12]); + (*regs)[13] = tswapl(env->regs[13]); + (*regs)[14] = tswapl(env->regs[14]); + (*regs)[15] = tswapl(env->regs[15]); - (*regs)[16] = cpsr_read((CPUState *)env); - (*regs)[17] = env->regs[0]; /* XXX */ + (*regs)[16] = tswapl(cpsr_read((CPUState *)env)); + (*regs)[17] = tswapl(env->regs[0]); /* XXX */ } #define USE_ELF_CORE_DUMP -#define ELF_EXEC_PAGESIZE 4096 +#define ELF_EXEC_PAGESIZE 4096 enum { - ARM_HWCAP_ARM_SWP = 1 << 0, - ARM_HWCAP_ARM_HALF = 1 << 1, - ARM_HWCAP_ARM_THUMB = 1 << 2, - ARM_HWCAP_ARM_26BIT = 1 << 3, - ARM_HWCAP_ARM_FAST_MULT = 1 << 4, - ARM_HWCAP_ARM_FPA = 1 << 5, - ARM_HWCAP_ARM_VFP = 1 << 6, - ARM_HWCAP_ARM_EDSP = 1 << 7, - ARM_HWCAP_ARM_JAVA = 1 << 8, - ARM_HWCAP_ARM_IWMMXT = 1 << 9, - ARM_HWCAP_ARM_THUMBEE = 1 << 10, - ARM_HWCAP_ARM_NEON = 1 << 11, - ARM_HWCAP_ARM_VFPv3 = 1 << 12, - ARM_HWCAP_ARM_VFPv3D16 = 1 << 13, + ARM_HWCAP_ARM_SWP = 1 << 0, + ARM_HWCAP_ARM_HALF = 1 << 1, + ARM_HWCAP_ARM_THUMB = 1 << 2, + ARM_HWCAP_ARM_26BIT = 1 << 3, + ARM_HWCAP_ARM_FAST_MULT = 1 << 4, + ARM_HWCAP_ARM_FPA = 1 << 5, + ARM_HWCAP_ARM_VFP = 1 << 6, + ARM_HWCAP_ARM_EDSP = 1 << 7, + ARM_HWCAP_ARM_JAVA = 1 << 8, + ARM_HWCAP_ARM_IWMMXT = 1 << 9, + ARM_HWCAP_ARM_THUMBEE = 1 << 10, + ARM_HWCAP_ARM_NEON = 1 << 11, + ARM_HWCAP_ARM_VFPv3 = 1 << 12, + ARM_HWCAP_ARM_VFPv3D16 = 1 << 13, }; -#define ELF_HWCAP (ARM_HWCAP_ARM_SWP | ARM_HWCAP_ARM_HALF \ - | ARM_HWCAP_ARM_THUMB | ARM_HWCAP_ARM_FAST_MULT \ - | ARM_HWCAP_ARM_FPA | ARM_HWCAP_ARM_VFP \ - | ARM_HWCAP_ARM_NEON | ARM_HWCAP_ARM_VFPv3 ) +#define ELF_HWCAP (ARM_HWCAP_ARM_SWP | ARM_HWCAP_ARM_HALF \ + | ARM_HWCAP_ARM_THUMB | ARM_HWCAP_ARM_FAST_MULT \ + | ARM_HWCAP_ARM_FPA | ARM_HWCAP_ARM_VFP \ + | ARM_HWCAP_ARM_NEON | ARM_HWCAP_ARM_VFPv3 ) #endif @@ -357,12 +351,12 @@ #endif #define ELF_CLASS ELFCLASS64 -#define ELF_DATA ELFDATA2MSB #define ELF_ARCH EM_SPARCV9 -#define STACK_BIAS 2047 +#define STACK_BIAS 2047 -static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) +static inline void init_thread(struct target_pt_regs *regs, + struct image_info *infop) { #ifndef TARGET_ABI32 regs->tstate = 0; @@ -386,10 +380,10 @@ #define elf_check_arch(x) ( (x) == EM_SPARC ) #define ELF_CLASS ELFCLASS32 -#define ELF_DATA ELFDATA2MSB #define ELF_ARCH EM_SPARC -static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) +static inline void init_thread(struct target_pt_regs *regs, + struct image_info *infop) { regs->psr = 0; regs->pc = infop->entry; @@ -409,22 +403,17 @@ #define elf_check_arch(x) ( (x) == EM_PPC64 ) -#define ELF_CLASS ELFCLASS64 +#define ELF_CLASS ELFCLASS64 #else #define elf_check_arch(x) ( (x) == EM_PPC ) -#define ELF_CLASS ELFCLASS32 +#define ELF_CLASS ELFCLASS32 #endif -#ifdef TARGET_WORDS_BIGENDIAN -#define ELF_DATA ELFDATA2MSB -#else -#define ELF_DATA ELFDATA2LSB -#endif -#define ELF_ARCH EM_PPC +#define ELF_ARCH EM_PPC /* Feature masks for the Aux Vector Hardware Capabilities (AT_HWCAP). See arch/powerpc/include/asm/cputable.h. */ @@ -469,7 +458,7 @@ /* We don't have to be terribly complete here; the high points are Altivec/FP/SPE support. Anything else is just a bonus. */ -#define GET_FEATURE(flag, feature) \ +#define GET_FEATURE(flag, feature) \ do {if (e->insns_flags & flag) features |= feature; } while(0) GET_FEATURE(PPC_64B, QEMU_PPC_FEATURE_64); GET_FEATURE(PPC_FLOAT, QEMU_PPC_FEATURE_HAS_FPU); @@ -485,15 +474,6 @@ } /* - * We need to put in some extra aux table entries to tell glibc what - * the cache block size is, so it can use the dcbz instruction safely. - */ -#define AT_DCACHEBSIZE 19 -#define AT_ICACHEBSIZE 20 -#define AT_UCACHEBSIZE 21 -/* A special ignored type value for PPC, for glibc compatibility. */ -#define AT_IGNOREPPC 22 -/* * The requirements here are: * - keep the final alignment of sp (sp & 0xf) * - make sure the 32-bit value at the first 16 byte aligned position of @@ -503,48 +483,55 @@ * even if DLINFO_ARCH_ITEMS goes to zero or is undefined. */ #define DLINFO_ARCH_ITEMS 5 -#define ARCH_DLINFO \ -do { \ - NEW_AUX_ENT(AT_DCACHEBSIZE, 0x20); \ - NEW_AUX_ENT(AT_ICACHEBSIZE, 0x20); \ - NEW_AUX_ENT(AT_UCACHEBSIZE, 0); \ - /* \ - * Now handle glibc compatibility. \ - */ \ - NEW_AUX_ENT(AT_IGNOREPPC, AT_IGNOREPPC); \ - NEW_AUX_ENT(AT_IGNOREPPC, AT_IGNOREPPC); \ - } while (0) +#define ARCH_DLINFO \ + do { \ + NEW_AUX_ENT(AT_DCACHEBSIZE, 0x20); \ + NEW_AUX_ENT(AT_ICACHEBSIZE, 0x20); \ + NEW_AUX_ENT(AT_UCACHEBSIZE, 0); \ + /* \ + * Now handle glibc compatibility. \ + */ \ + NEW_AUX_ENT(AT_IGNOREPPC, AT_IGNOREPPC); \ + NEW_AUX_ENT(AT_IGNOREPPC, AT_IGNOREPPC); \ + } while (0) static inline void init_thread(struct target_pt_regs *_regs, struct image_info *infop) { - abi_ulong pos = infop->start_stack; - abi_ulong tmp; -#if defined(TARGET_PPC64) && !defined(TARGET_ABI32) - abi_ulong entry, toc; -#endif - _regs->gpr[1] = infop->start_stack; #if defined(TARGET_PPC64) && !defined(TARGET_ABI32) - entry = ldq_raw(infop->entry) + infop->load_addr; - toc = ldq_raw(infop->entry + 8) + infop->load_addr; - _regs->gpr[2] = toc; - infop->entry = entry; + _regs->gpr[2] = ldq_raw(infop->entry + 8) + infop->load_addr; + infop->entry = ldq_raw(infop->entry) + infop->load_addr; #endif _regs->nip = infop->entry; - /* Note that isn't exactly what regular kernel does - * but this is what the ABI wants and is needed to allow - * execution of PPC BSD programs. - */ - /* FIXME - what to for failure of get_user()? */ - get_user_ual(_regs->gpr[3], pos); - pos += sizeof(abi_ulong); - _regs->gpr[4] = pos; - for (tmp = 1; tmp != 0; pos += sizeof(abi_ulong)) - tmp = ldl(pos); - _regs->gpr[5] = pos; } -#define ELF_EXEC_PAGESIZE 4096 +/* See linux kernel: arch/powerpc/include/asm/elf.h. */ +#define ELF_NREG 48 +typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; + +static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUState *env) +{ + int i; + target_ulong ccr = 0; + + for (i = 0; i < ARRAY_SIZE(env->gpr); i++) { + (*regs)[i] = tswapl(env->gpr[i]); + } + + (*regs)[32] = tswapl(env->nip); + (*regs)[33] = tswapl(env->msr); + (*regs)[35] = tswapl(env->ctr); + (*regs)[36] = tswapl(env->lr); + (*regs)[37] = tswapl(env->xer); + + for (i = 0; i < ARRAY_SIZE(env->crf); i++) { + ccr |= env->crf[i] << (32 - ((i + 1) * 4)); + } + (*regs)[38] = tswapl(ccr); +} + +#define USE_ELF_CORE_DUMP +#define ELF_EXEC_PAGESIZE 4096 #endif @@ -559,20 +546,62 @@ #else #define ELF_CLASS ELFCLASS32 #endif -#ifdef TARGET_WORDS_BIGENDIAN -#define ELF_DATA ELFDATA2MSB -#else -#define ELF_DATA ELFDATA2LSB -#endif #define ELF_ARCH EM_MIPS -static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) +static inline void init_thread(struct target_pt_regs *regs, + struct image_info *infop) { regs->cp0_status = 2 << CP0St_KSU; regs->cp0_epc = infop->entry; regs->regs[29] = infop->start_stack; } +/* See linux kernel: arch/mips/include/asm/elf.h. */ +#define ELF_NREG 45 +typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; + +/* See linux kernel: arch/mips/include/asm/reg.h. */ +enum { +#ifdef TARGET_MIPS64 + TARGET_EF_R0 = 0, +#else + TARGET_EF_R0 = 6, +#endif + TARGET_EF_R26 = TARGET_EF_R0 + 26, + TARGET_EF_R27 = TARGET_EF_R0 + 27, + TARGET_EF_LO = TARGET_EF_R0 + 32, + TARGET_EF_HI = TARGET_EF_R0 + 33, + TARGET_EF_CP0_EPC = TARGET_EF_R0 + 34, + TARGET_EF_CP0_BADVADDR = TARGET_EF_R0 + 35, + TARGET_EF_CP0_STATUS = TARGET_EF_R0 + 36, + TARGET_EF_CP0_CAUSE = TARGET_EF_R0 + 37 +}; + +/* See linux kernel: arch/mips/kernel/process.c:elf_dump_regs. */ +static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUState *env) +{ + int i; + + for (i = 0; i < TARGET_EF_R0; i++) { + (*regs)[i] = 0; + } + (*regs)[TARGET_EF_R0] = 0; + + for (i = 1; i < ARRAY_SIZE(env->active_tc.gpr); i++) { + (*regs)[TARGET_EF_R0 + i] = tswapl(env->active_tc.gpr[i]); + } + + (*regs)[TARGET_EF_R26] = 0; + (*regs)[TARGET_EF_R27] = 0; + (*regs)[TARGET_EF_LO] = tswapl(env->active_tc.LO[0]); + (*regs)[TARGET_EF_HI] = tswapl(env->active_tc.HI[0]); + (*regs)[TARGET_EF_CP0_EPC] = tswapl(env->active_tc.PC); + (*regs)[TARGET_EF_CP0_BADVADDR] = tswapl(env->CP0_BadVAddr); + (*regs)[TARGET_EF_CP0_STATUS] = tswapl(env->CP0_Status); + (*regs)[TARGET_EF_CP0_CAUSE] = tswapl(env->CP0_Cause); +} + +#define USE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 #endif /* TARGET_MIPS */ @@ -581,13 +610,13 @@ #define ELF_START_MMAP 0x80000000 -#define elf_check_arch(x) ( (x) == EM_XILINX_MICROBLAZE ) +#define elf_check_arch(x) ( (x) == EM_MICROBLAZE || (x) == EM_MICROBLAZE_OLD) #define ELF_CLASS ELFCLASS32 -#define ELF_DATA ELFDATA2MSB -#define ELF_ARCH EM_MIPS +#define ELF_ARCH EM_MICROBLAZE -static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) +static inline void init_thread(struct target_pt_regs *regs, + struct image_info *infop) { regs->pc = infop->entry; regs->r1 = infop->start_stack; @@ -596,6 +625,24 @@ #define ELF_EXEC_PAGESIZE 4096 +#define USE_ELF_CORE_DUMP +#define ELF_NREG 38 +typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; + +/* See linux kernel: arch/mips/kernel/process.c:elf_dump_regs. */ +static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUState *env) +{ + int i, pos = 0; + + for (i = 0; i < 32; i++) { + (*regs)[pos++] = tswapl(env->regs[i]); + } + + for (i = 0; i < 6; i++) { + (*regs)[pos++] = tswapl(env->sregs[i]); + } +} + #endif /* TARGET_MICROBLAZE */ #ifdef TARGET_SH4 @@ -605,16 +652,50 @@ #define elf_check_arch(x) ( (x) == EM_SH ) #define ELF_CLASS ELFCLASS32 -#define ELF_DATA ELFDATA2LSB #define ELF_ARCH EM_SH -static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) +static inline void init_thread(struct target_pt_regs *regs, + struct image_info *infop) { - /* Check other registers XXXXX */ - regs->pc = infop->entry; - regs->regs[15] = infop->start_stack; + /* Check other registers XXXXX */ + regs->pc = infop->entry; + regs->regs[15] = infop->start_stack; +} + +/* See linux kernel: arch/sh/include/asm/elf.h. */ +#define ELF_NREG 23 +typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; + +/* See linux kernel: arch/sh/include/asm/ptrace.h. */ +enum { + TARGET_REG_PC = 16, + TARGET_REG_PR = 17, + TARGET_REG_SR = 18, + TARGET_REG_GBR = 19, + TARGET_REG_MACH = 20, + TARGET_REG_MACL = 21, + TARGET_REG_SYSCALL = 22 +}; + +static inline void elf_core_copy_regs(target_elf_gregset_t *regs, + const CPUState *env) +{ + int i; + + for (i = 0; i < 16; i++) { + (*regs[i]) = tswapl(env->gregs[i]); + } + + (*regs)[TARGET_REG_PC] = tswapl(env->pc); + (*regs)[TARGET_REG_PR] = tswapl(env->pr); + (*regs)[TARGET_REG_SR] = tswapl(env->sr); + (*regs)[TARGET_REG_GBR] = tswapl(env->gbr); + (*regs)[TARGET_REG_MACH] = tswapl(env->mach); + (*regs)[TARGET_REG_MACL] = tswapl(env->macl); + (*regs)[TARGET_REG_SYSCALL] = 0; /* FIXME */ } +#define USE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 #endif @@ -626,12 +707,12 @@ #define elf_check_arch(x) ( (x) == EM_CRIS ) #define ELF_CLASS ELFCLASS32 -#define ELF_DATA ELFDATA2LSB #define ELF_ARCH EM_CRIS -static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) +static inline void init_thread(struct target_pt_regs *regs, + struct image_info *infop) { - regs->erp = infop->entry; + regs->erp = infop->entry; } #define ELF_EXEC_PAGESIZE 8192 @@ -644,21 +725,50 @@ #define elf_check_arch(x) ( (x) == EM_68K ) -#define ELF_CLASS ELFCLASS32 -#define ELF_DATA ELFDATA2MSB -#define ELF_ARCH EM_68K +#define ELF_CLASS ELFCLASS32 +#define ELF_ARCH EM_68K /* ??? Does this need to do anything? -#define ELF_PLAT_INIT(_r) */ + #define ELF_PLAT_INIT(_r) */ -static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) +static inline void init_thread(struct target_pt_regs *regs, + struct image_info *infop) { regs->usp = infop->start_stack; regs->sr = 0; regs->pc = infop->entry; } -#define ELF_EXEC_PAGESIZE 8192 +/* See linux kernel: arch/m68k/include/asm/elf.h. */ +#define ELF_NREG 20 +typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; + +static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUState *env) +{ + (*regs)[0] = tswapl(env->dregs[1]); + (*regs)[1] = tswapl(env->dregs[2]); + (*regs)[2] = tswapl(env->dregs[3]); + (*regs)[3] = tswapl(env->dregs[4]); + (*regs)[4] = tswapl(env->dregs[5]); + (*regs)[5] = tswapl(env->dregs[6]); + (*regs)[6] = tswapl(env->dregs[7]); + (*regs)[7] = tswapl(env->aregs[0]); + (*regs)[8] = tswapl(env->aregs[1]); + (*regs)[9] = tswapl(env->aregs[2]); + (*regs)[10] = tswapl(env->aregs[3]); + (*regs)[11] = tswapl(env->aregs[4]); + (*regs)[12] = tswapl(env->aregs[5]); + (*regs)[13] = tswapl(env->aregs[6]); + (*regs)[14] = tswapl(env->dregs[0]); + (*regs)[15] = tswapl(env->aregs[7]); + (*regs)[16] = tswapl(env->dregs[0]); /* FIXME: orig_d0 */ + (*regs)[17] = tswapl(env->sr); + (*regs)[18] = tswapl(env->pc); + (*regs)[19] = 0; /* FIXME: regs->format | regs->vector */ +} + +#define USE_ELF_CORE_DUMP +#define ELF_EXEC_PAGESIZE 8192 #endif @@ -669,17 +779,14 @@ #define elf_check_arch(x) ( (x) == ELF_ARCH ) #define ELF_CLASS ELFCLASS64 -#define ELF_DATA ELFDATA2MSB #define ELF_ARCH EM_ALPHA -static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) +static inline void init_thread(struct target_pt_regs *regs, + struct image_info *infop) { regs->pc = infop->entry; regs->ps = 8; regs->usp = infop->start_stack; - regs->unique = infop->start_data; /* ? */ - printf("Set unique value to " TARGET_FMT_lx " (" TARGET_FMT_lx ")\n", - regs->unique, infop->start_data); } #define ELF_EXEC_PAGESIZE 8192 @@ -705,14 +812,14 @@ struct exec { - unsigned int a_info; /* Use macros N_MAGIC, etc for access */ - unsigned int a_text; /* length of text, in bytes */ - unsigned int a_data; /* length of data, in bytes */ - unsigned int a_bss; /* length of uninitialized data area, in bytes */ - unsigned int a_syms; /* length of symbol table data in file, in bytes */ - unsigned int a_entry; /* start address */ - unsigned int a_trsize; /* length of relocation info for text, in bytes */ - unsigned int a_drsize; /* length of relocation info for data, in bytes */ + unsigned int a_info; /* Use macros N_MAGIC, etc for access */ + unsigned int a_text; /* length of text, in bytes */ + unsigned int a_data; /* length of data, in bytes */ + unsigned int a_bss; /* length of uninitialized data area, in bytes */ + unsigned int a_syms; /* length of symbol table data in file, in bytes */ + unsigned int a_entry; /* start address */ + unsigned int a_trsize; /* length of relocation info for text, in bytes */ + unsigned int a_drsize; /* length of relocation info for data, in bytes */ }; @@ -722,72 +829,66 @@ #define ZMAGIC 0413 #define QMAGIC 0314 -/* max code+data+bss space allocated to elf interpreter */ -#define INTERP_MAP_SIZE (32 * 1024 * 1024) - -/* max code+data+bss+brk space allocated to ET_DYN executables */ -#define ET_DYN_MAP_SIZE (128 * 1024 * 1024) - /* Necessary parameters */ #define TARGET_ELF_EXEC_PAGESIZE TARGET_PAGE_SIZE #define TARGET_ELF_PAGESTART(_v) ((_v) & ~(unsigned long)(TARGET_ELF_EXEC_PAGESIZE-1)) #define TARGET_ELF_PAGEOFFSET(_v) ((_v) & (TARGET_ELF_EXEC_PAGESIZE-1)) -#define INTERPRETER_NONE 0 -#define INTERPRETER_AOUT 1 -#define INTERPRETER_ELF 2 - #define DLINFO_ITEMS 12 static inline void memcpy_fromfs(void * to, const void * from, unsigned long n) { - memcpy(to, from, n); + memcpy(to, from, n); } -static int load_aout_interp(void * exptr, int interp_fd); - #ifdef BSWAP_NEEDED static void bswap_ehdr(struct elfhdr *ehdr) { - bswap16s(&ehdr->e_type); /* Object file type */ - bswap16s(&ehdr->e_machine); /* Architecture */ - bswap32s(&ehdr->e_version); /* Object file version */ - bswaptls(&ehdr->e_entry); /* Entry point virtual address */ - bswaptls(&ehdr->e_phoff); /* Program header table file offset */ - bswaptls(&ehdr->e_shoff); /* Section header table file offset */ - bswap32s(&ehdr->e_flags); /* Processor-specific flags */ - bswap16s(&ehdr->e_ehsize); /* ELF header size in bytes */ - bswap16s(&ehdr->e_phentsize); /* Program header table entry size */ - bswap16s(&ehdr->e_phnum); /* Program header table entry count */ - bswap16s(&ehdr->e_shentsize); /* Section header table entry size */ - bswap16s(&ehdr->e_shnum); /* Section header table entry count */ - bswap16s(&ehdr->e_shstrndx); /* Section header string table index */ -} - -static void bswap_phdr(struct elf_phdr *phdr) -{ - bswap32s(&phdr->p_type); /* Segment type */ - bswaptls(&phdr->p_offset); /* Segment file offset */ - bswaptls(&phdr->p_vaddr); /* Segment virtual address */ - bswaptls(&phdr->p_paddr); /* Segment physical address */ - bswaptls(&phdr->p_filesz); /* Segment size in file */ - bswaptls(&phdr->p_memsz); /* Segment size in memory */ - bswap32s(&phdr->p_flags); /* Segment flags */ - bswaptls(&phdr->p_align); /* Segment alignment */ -} - -static void bswap_shdr(struct elf_shdr *shdr) -{ - bswap32s(&shdr->sh_name); - bswap32s(&shdr->sh_type); - bswaptls(&shdr->sh_flags); - bswaptls(&shdr->sh_addr); - bswaptls(&shdr->sh_offset); - bswaptls(&shdr->sh_size); - bswap32s(&shdr->sh_link); - bswap32s(&shdr->sh_info); - bswaptls(&shdr->sh_addralign); - bswaptls(&shdr->sh_entsize); + bswap16s(&ehdr->e_type); /* Object file type */ + bswap16s(&ehdr->e_machine); /* Architecture */ + bswap32s(&ehdr->e_version); /* Object file version */ + bswaptls(&ehdr->e_entry); /* Entry point virtual address */ + bswaptls(&ehdr->e_phoff); /* Program header table file offset */ + bswaptls(&ehdr->e_shoff); /* Section header table file offset */ + bswap32s(&ehdr->e_flags); /* Processor-specific flags */ + bswap16s(&ehdr->e_ehsize); /* ELF header size in bytes */ + bswap16s(&ehdr->e_phentsize); /* Program header table entry size */ + bswap16s(&ehdr->e_phnum); /* Program header table entry count */ + bswap16s(&ehdr->e_shentsize); /* Section header table entry size */ + bswap16s(&ehdr->e_shnum); /* Section header table entry count */ + bswap16s(&ehdr->e_shstrndx); /* Section header string table index */ +} + +static void bswap_phdr(struct elf_phdr *phdr, int phnum) +{ + int i; + for (i = 0; i < phnum; ++i, ++phdr) { + bswap32s(&phdr->p_type); /* Segment type */ + bswap32s(&phdr->p_flags); /* Segment flags */ + bswaptls(&phdr->p_offset); /* Segment file offset */ + bswaptls(&phdr->p_vaddr); /* Segment virtual address */ + bswaptls(&phdr->p_paddr); /* Segment physical address */ + bswaptls(&phdr->p_filesz); /* Segment size in file */ + bswaptls(&phdr->p_memsz); /* Segment size in memory */ + bswaptls(&phdr->p_align); /* Segment alignment */ + } +} + +static void bswap_shdr(struct elf_shdr *shdr, int shnum) +{ + int i; + for (i = 0; i < shnum; ++i, ++shdr) { + bswap32s(&shdr->sh_name); + bswap32s(&shdr->sh_type); + bswaptls(&shdr->sh_flags); + bswaptls(&shdr->sh_addr); + bswaptls(&shdr->sh_offset); + bswaptls(&shdr->sh_size); + bswap32s(&shdr->sh_link); + bswap32s(&shdr->sh_info); + bswaptls(&shdr->sh_addralign); + bswaptls(&shdr->sh_entsize); + } } static void bswap_sym(struct elf_sym *sym) @@ -797,21 +898,41 @@ bswaptls(&sym->st_size); bswap16s(&sym->st_shndx); } +#else +static inline void bswap_ehdr(struct elfhdr *ehdr) { } +static inline void bswap_phdr(struct elf_phdr *phdr, int phnum) { } +static inline void bswap_shdr(struct elf_shdr *shdr, int shnum) { } +static inline void bswap_sym(struct elf_sym *sym) { } #endif #ifdef USE_ELF_CORE_DUMP static int elf_core_dump(int, const CPUState *); +#endif /* USE_ELF_CORE_DUMP */ +static void load_symbols(struct elfhdr *hdr, int fd, abi_ulong load_bias); -#ifdef BSWAP_NEEDED -static void bswap_note(struct elf_note *en) -{ - bswap32s(&en->n_namesz); - bswap32s(&en->n_descsz); - bswap32s(&en->n_type); +/* Verify the portions of EHDR within E_IDENT for the target. + This can be performed before bswapping the entire header. */ +static bool elf_check_ident(struct elfhdr *ehdr) +{ + return (ehdr->e_ident[EI_MAG0] == ELFMAG0 + && ehdr->e_ident[EI_MAG1] == ELFMAG1 + && ehdr->e_ident[EI_MAG2] == ELFMAG2 + && ehdr->e_ident[EI_MAG3] == ELFMAG3 + && ehdr->e_ident[EI_CLASS] == ELF_CLASS + && ehdr->e_ident[EI_DATA] == ELF_DATA + && ehdr->e_ident[EI_VERSION] == EV_CURRENT); +} + +/* Verify the portions of EHDR outside of E_IDENT for the target. + This has to wait until after bswapping the header. */ +static bool elf_check_ehdr(struct elfhdr *ehdr) +{ + return (elf_check_arch(ehdr->e_machine) + && ehdr->e_ehsize == sizeof(struct elfhdr) + && ehdr->e_phentsize == sizeof(struct elf_phdr) + && ehdr->e_shentsize == sizeof(struct elf_shdr) + && (ehdr->e_type == ET_EXEC || ehdr->e_type == ET_DYN)); } -#endif /* BSWAP_NEEDED */ - -#endif /* USE_ELF_CORE_DUMP */ /* * 'copy_elf_strings()' copies argument/envelope strings from user @@ -826,24 +947,24 @@ int len, offset = 0; if (!p) { - return 0; /* bullet-proofing */ + return 0; /* bullet-proofing */ } while (argc-- > 0) { tmp = argv[argc]; if (!tmp) { - fprintf(stderr, "VFS: argc is wrong"); - exit(-1); - } + fprintf(stderr, "VFS: argc is wrong"); + exit(-1); + } tmp1 = tmp; - while (*tmp++); - len = tmp - tmp1; - if (p < len) { /* this shouldn't happen - 128kB */ - return 0; - } - while (len) { - --p; --tmp; --len; - if (--offset < 0) { - offset = p % TARGET_PAGE_SIZE; + while (*tmp++); + len = tmp - tmp1; + if (p < len) { /* this shouldn't happen - 128kB */ + return 0; + } + while (len) { + --p; --tmp; --len; + if (--offset < 0) { + offset = p % TARGET_PAGE_SIZE; pag = (char *)page[p/TARGET_PAGE_SIZE]; if (!pag) { pag = (char *)malloc(TARGET_PAGE_SIZE); @@ -851,20 +972,20 @@ page[p/TARGET_PAGE_SIZE] = pag; if (!pag) return 0; - } - } - if (len == 0 || offset == 0) { - *(pag + offset) = *tmp; - } - else { - int bytes_to_copy = (len > offset) ? offset : len; - tmp -= bytes_to_copy; - p -= bytes_to_copy; - offset -= bytes_to_copy; - len -= bytes_to_copy; - memcpy_fromfs(pag + offset, tmp, bytes_to_copy + 1); - } - } + } + } + if (len == 0 || offset == 0) { + *(pag + offset) = *tmp; + } + else { + int bytes_to_copy = (len > offset) ? offset : len; + tmp -= bytes_to_copy; + p -= bytes_to_copy; + offset -= bytes_to_copy; + len -= bytes_to_copy; + memcpy_fromfs(pag + offset, tmp, bytes_to_copy + 1); + } + } } return p; } @@ -872,331 +993,440 @@ static abi_ulong setup_arg_pages(abi_ulong p, struct linux_binprm *bprm, struct image_info *info) { - abi_ulong stack_base, size, error; + abi_ulong stack_base, size, error, guard; int i; /* Create enough stack to hold everything. If we don't use - * it for args, we'll use it for something else... - */ - size = x86_stack_size; - if (size < MAX_ARG_PAGES*TARGET_PAGE_SIZE) + it for args, we'll use it for something else. */ + size = guest_stack_size; + if (size < MAX_ARG_PAGES*TARGET_PAGE_SIZE) { size = MAX_ARG_PAGES*TARGET_PAGE_SIZE; - error = target_mmap(0, - size + qemu_host_page_size, - PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, - -1, 0); + } + guard = TARGET_PAGE_SIZE; + if (guard < qemu_real_host_page_size) { + guard = qemu_real_host_page_size; + } + + error = target_mmap(0, size + guard, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (error == -1) { - perror("stk mmap"); + perror("mmap stack"); exit(-1); } - /* we reserve one extra page at the top of the stack as guard */ - target_mprotect(error + size, qemu_host_page_size, PROT_NONE); - stack_base = error + size - MAX_ARG_PAGES*TARGET_PAGE_SIZE; + /* We reserve one extra page at the top of the stack as guard. */ + target_mprotect(error, guard, PROT_NONE); + + info->stack_limit = error + guard; + stack_base = info->stack_limit + size - MAX_ARG_PAGES*TARGET_PAGE_SIZE; p += stack_base; for (i = 0 ; i < MAX_ARG_PAGES ; i++) { - if (bprm->page[i]) { - info->rss++; + if (bprm->page[i]) { + info->rss++; /* FIXME - check return value of memcpy_to_target() for failure */ - memcpy_to_target(stack_base, bprm->page[i], TARGET_PAGE_SIZE); - free(bprm->page[i]); - } + memcpy_to_target(stack_base, bprm->page[i], TARGET_PAGE_SIZE); + free(bprm->page[i]); + } stack_base += TARGET_PAGE_SIZE; } return p; } -static void set_brk(abi_ulong start, abi_ulong end) -{ - /* page-align the start and end addresses... */ - start = HOST_PAGE_ALIGN(start); - end = HOST_PAGE_ALIGN(end); - if (end <= start) - return; - if(target_mmap(start, end - start, - PROT_READ | PROT_WRITE | PROT_EXEC, - MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0) == -1) { - perror("cannot mmap brk"); - exit(-1); - } -} - - -/* We need to explicitly zero any fractional pages after the data - section (i.e. bss). This would contain the junk from the file that - should not be in memory. */ -static void padzero(abi_ulong elf_bss, abi_ulong last_bss) -{ - abi_ulong nbyte; - - if (elf_bss >= last_bss) - return; - - /* XXX: this is really a hack : if the real host page size is - smaller than the target page size, some pages after the end - of the file may not be mapped. A better fix would be to - patch target_mmap(), but it is more complicated as the file - size must be known */ - if (qemu_real_host_page_size < qemu_host_page_size) { - abi_ulong end_addr, end_addr1; - end_addr1 = (elf_bss + qemu_real_host_page_size - 1) & - ~(qemu_real_host_page_size - 1); - end_addr = HOST_PAGE_ALIGN(elf_bss); - if (end_addr1 < end_addr) { - mmap((void *)g2h(end_addr1), end_addr - end_addr1, - PROT_READ|PROT_WRITE|PROT_EXEC, - MAP_FIXED|MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); - } +/* Map and zero the bss. We need to explicitly zero any fractional pages + after the data section (i.e. bss). */ +static void zero_bss(abi_ulong elf_bss, abi_ulong last_bss, int prot) +{ + uintptr_t host_start, host_map_start, host_end; + + last_bss = TARGET_PAGE_ALIGN(last_bss); + + /* ??? There is confusion between qemu_real_host_page_size and + qemu_host_page_size here and elsewhere in target_mmap, which + may lead to the end of the data section mapping from the file + not being mapped. At least there was an explicit test and + comment for that here, suggesting that "the file size must + be known". The comment probably pre-dates the introduction + of the fstat system call in target_mmap which does in fact + find out the size. What isn't clear is if the workaround + here is still actually needed. For now, continue with it, + but merge it with the "normal" mmap that would allocate the bss. */ + + host_start = (uintptr_t) g2h(elf_bss); + host_end = (uintptr_t) g2h(last_bss); + host_map_start = (host_start + qemu_real_host_page_size - 1); + host_map_start &= -qemu_real_host_page_size; + + if (host_map_start < host_end) { + void *p = mmap((void *)host_map_start, host_end - host_map_start, + prot, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (p == MAP_FAILED) { + perror("cannot mmap brk"); + exit(-1); } - nbyte = elf_bss & (qemu_host_page_size-1); - if (nbyte) { - nbyte = qemu_host_page_size - nbyte; - do { - /* FIXME - what to do if put_user() fails? */ - put_user_u8(0, elf_bss); - elf_bss++; - } while (--nbyte); - } -} + /* Since we didn't use target_mmap, make sure to record + the validity of the pages with qemu. */ + page_set_flags(elf_bss & TARGET_PAGE_MASK, last_bss, prot|PAGE_VALID); + } + if (host_start < host_map_start) { + memset((void *)host_start, 0, host_map_start - host_start); + } +} static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc, - struct elfhdr * exec, - abi_ulong load_addr, - abi_ulong load_bias, - abi_ulong interp_load_addr, int ibcs, - struct image_info *info) -{ - abi_ulong sp; - int size; - abi_ulong u_platform; - const char *k_platform; - const int n = sizeof(elf_addr_t); - - sp = p; - u_platform = 0; - k_platform = ELF_PLATFORM; - if (k_platform) { - size_t len = strlen(k_platform) + 1; - sp -= (len + n - 1) & ~(n - 1); - u_platform = sp; - /* FIXME - check return value of memcpy_to_target() for failure */ - memcpy_to_target(sp, k_platform, len); - } - /* - * Force 16 byte _final_ alignment here for generality. - */ - sp = sp &~ (abi_ulong)15; - size = (DLINFO_ITEMS + 1) * 2; - if (k_platform) - size += 2; + struct elfhdr *exec, + struct image_info *info, + struct image_info *interp_info) +{ + abi_ulong sp; + int size; + abi_ulong u_platform; + const char *k_platform; + const int n = sizeof(elf_addr_t); + + sp = p; + u_platform = 0; + k_platform = ELF_PLATFORM; + if (k_platform) { + size_t len = strlen(k_platform) + 1; + sp -= (len + n - 1) & ~(n - 1); + u_platform = sp; + /* FIXME - check return value of memcpy_to_target() for failure */ + memcpy_to_target(sp, k_platform, len); + } + /* + * Force 16 byte _final_ alignment here for generality. + */ + sp = sp &~ (abi_ulong)15; + size = (DLINFO_ITEMS + 1) * 2; + if (k_platform) + size += 2; #ifdef DLINFO_ARCH_ITEMS - size += DLINFO_ARCH_ITEMS * 2; + size += DLINFO_ARCH_ITEMS * 2; #endif - size += envc + argc + 2; - size += (!ibcs ? 3 : 1); /* argc itself */ - size *= n; - if (size & 15) - sp -= 16 - (size & 15); + size += envc + argc + 2; + size += 1; /* argc itself */ + size *= n; + if (size & 15) + sp -= 16 - (size & 15); - /* This is correct because Linux defines - * elf_addr_t as Elf32_Off / Elf64_Off - */ -#define NEW_AUX_ENT(id, val) do { \ - sp -= n; put_user_ual(val, sp); \ - sp -= n; put_user_ual(id, sp); \ - } while(0) - - NEW_AUX_ENT (AT_NULL, 0); - - /* There must be exactly DLINFO_ITEMS entries here. */ - NEW_AUX_ENT(AT_PHDR, (abi_ulong)(load_addr + exec->e_phoff)); - NEW_AUX_ENT(AT_PHENT, (abi_ulong)(sizeof (struct elf_phdr))); - NEW_AUX_ENT(AT_PHNUM, (abi_ulong)(exec->e_phnum)); - NEW_AUX_ENT(AT_PAGESZ, (abi_ulong)(TARGET_PAGE_SIZE)); - NEW_AUX_ENT(AT_BASE, (abi_ulong)(interp_load_addr)); - NEW_AUX_ENT(AT_FLAGS, (abi_ulong)0); - NEW_AUX_ENT(AT_ENTRY, load_bias + exec->e_entry); - NEW_AUX_ENT(AT_UID, (abi_ulong) getuid()); - NEW_AUX_ENT(AT_EUID, (abi_ulong) geteuid()); - NEW_AUX_ENT(AT_GID, (abi_ulong) getgid()); - NEW_AUX_ENT(AT_EGID, (abi_ulong) getegid()); - NEW_AUX_ENT(AT_HWCAP, (abi_ulong) ELF_HWCAP); - NEW_AUX_ENT(AT_CLKTCK, (abi_ulong) sysconf(_SC_CLK_TCK)); - if (k_platform) - NEW_AUX_ENT(AT_PLATFORM, u_platform); + /* This is correct because Linux defines + * elf_addr_t as Elf32_Off / Elf64_Off + */ +#define NEW_AUX_ENT(id, val) do { \ + sp -= n; put_user_ual(val, sp); \ + sp -= n; put_user_ual(id, sp); \ + } while(0) + + NEW_AUX_ENT (AT_NULL, 0); + + /* There must be exactly DLINFO_ITEMS entries here. */ + NEW_AUX_ENT(AT_PHDR, (abi_ulong)(info->load_addr + exec->e_phoff)); + NEW_AUX_ENT(AT_PHENT, (abi_ulong)(sizeof (struct elf_phdr))); + NEW_AUX_ENT(AT_PHNUM, (abi_ulong)(exec->e_phnum)); + NEW_AUX_ENT(AT_PAGESZ, (abi_ulong)(TARGET_PAGE_SIZE)); + NEW_AUX_ENT(AT_BASE, (abi_ulong)(interp_info ? interp_info->load_addr : 0)); + NEW_AUX_ENT(AT_FLAGS, (abi_ulong)0); + NEW_AUX_ENT(AT_ENTRY, info->entry); + NEW_AUX_ENT(AT_UID, (abi_ulong) getuid()); + NEW_AUX_ENT(AT_EUID, (abi_ulong) geteuid()); + NEW_AUX_ENT(AT_GID, (abi_ulong) getgid()); + NEW_AUX_ENT(AT_EGID, (abi_ulong) getegid()); + NEW_AUX_ENT(AT_HWCAP, (abi_ulong) ELF_HWCAP); + NEW_AUX_ENT(AT_CLKTCK, (abi_ulong) sysconf(_SC_CLK_TCK)); + if (k_platform) + NEW_AUX_ENT(AT_PLATFORM, u_platform); #ifdef ARCH_DLINFO - /* - * ARCH_DLINFO must come last so platform specific code can enforce - * special alignment requirements on the AUXV if necessary (eg. PPC). - */ - ARCH_DLINFO; + /* + * ARCH_DLINFO must come last so platform specific code can enforce + * special alignment requirements on the AUXV if necessary (eg. PPC). + */ + ARCH_DLINFO; #endif #undef NEW_AUX_ENT - info->saved_auxv = sp; + info->saved_auxv = sp; - sp = loader_build_argptr(envc, argc, sp, p, !ibcs); - return sp; + sp = loader_build_argptr(envc, argc, sp, p, 0); + return sp; } +/* Load an ELF image into the address space. + + IMAGE_NAME is the filename of the image, to use in error messages. + IMAGE_FD is the open file descriptor for the image. + + BPRM_BUF is a copy of the beginning of the file; this of course + contains the elf file header at offset 0. It is assumed that this + buffer is sufficiently aligned to present no problems to the host + in accessing data at aligned offsets within the buffer. + + On return: INFO values will be filled in, as necessary or available. */ -static abi_ulong load_elf_interp(struct elfhdr * interp_elf_ex, - int interpreter_fd, - abi_ulong *interp_load_addr) +static void load_elf_image(const char *image_name, int image_fd, + struct image_info *info, char **pinterp_name, + char bprm_buf[BPRM_BUF_SIZE]) { - struct elf_phdr *elf_phdata = NULL; - struct elf_phdr *eppnt; - abi_ulong load_addr = 0; - int load_addr_set = 0; - int retval; - abi_ulong last_bss, elf_bss; - abi_ulong error; - int i; + struct elfhdr *ehdr = (struct elfhdr *)bprm_buf; + struct elf_phdr *phdr; + abi_ulong load_addr, load_bias, loaddr, hiaddr, error; + int i, retval; + const char *errmsg; - elf_bss = 0; - last_bss = 0; - error = 0; + /* First of all, some simple consistency checks */ + errmsg = "Invalid ELF image for this architecture"; + if (!elf_check_ident(ehdr)) { + goto exit_errmsg; + } + bswap_ehdr(ehdr); + if (!elf_check_ehdr(ehdr)) { + goto exit_errmsg; + } -#ifdef BSWAP_NEEDED - bswap_ehdr(interp_elf_ex); -#endif - /* First of all, some simple consistency checks */ - if ((interp_elf_ex->e_type != ET_EXEC && - interp_elf_ex->e_type != ET_DYN) || - !elf_check_arch(interp_elf_ex->e_machine)) { - return ~((abi_ulong)0UL); - } - - - /* Now read in all of the header information */ - - if (sizeof(struct elf_phdr) * interp_elf_ex->e_phnum > TARGET_PAGE_SIZE) - return ~(abi_ulong)0UL; - - elf_phdata = (struct elf_phdr *) - malloc(sizeof(struct elf_phdr) * interp_elf_ex->e_phnum); - - if (!elf_phdata) - return ~((abi_ulong)0UL); - - /* - * If the size of this structure has changed, then punt, since - * we will be doing the wrong thing. - */ - if (interp_elf_ex->e_phentsize != sizeof(struct elf_phdr)) { - free(elf_phdata); - return ~((abi_ulong)0UL); + i = ehdr->e_phnum * sizeof(struct elf_phdr); + if (ehdr->e_phoff + i <= BPRM_BUF_SIZE) { + phdr = (struct elf_phdr *)(bprm_buf + ehdr->e_phoff); + } else { + phdr = (struct elf_phdr *) alloca(i); + retval = pread(image_fd, phdr, i, ehdr->e_phoff); + if (retval != i) { + goto exit_read; } + } + bswap_phdr(phdr, ehdr->e_phnum); - retval = lseek(interpreter_fd, interp_elf_ex->e_phoff, SEEK_SET); - if(retval >= 0) { - retval = read(interpreter_fd, - (char *) elf_phdata, - sizeof(struct elf_phdr) * interp_elf_ex->e_phnum); - } - if (retval < 0) { - perror("load_elf_interp"); - exit(-1); - free (elf_phdata); - return retval; - } -#ifdef BSWAP_NEEDED - eppnt = elf_phdata; - for (i=0; ie_phnum; i++, eppnt++) { - bswap_phdr(eppnt); + /* Find the maximum size of the image and allocate an appropriate + amount of memory to handle that. */ + loaddr = -1, hiaddr = 0; + for (i = 0; i < ehdr->e_phnum; ++i) { + if (phdr[i].p_type == PT_LOAD) { + abi_ulong a = phdr[i].p_vaddr; + if (a < loaddr) { + loaddr = a; + } + a += phdr[i].p_memsz; + if (a > hiaddr) { + hiaddr = a; + } } -#endif + } - if (interp_elf_ex->e_type == ET_DYN) { - /* in order to avoid hardcoding the interpreter load - address in qemu, we allocate a big enough memory zone */ - error = target_mmap(0, INTERP_MAP_SIZE, - PROT_NONE, MAP_PRIVATE | MAP_ANON, + load_addr = loaddr; + if (ehdr->e_type == ET_DYN) { + /* The image indicates that it can be loaded anywhere. Find a + location that can hold the memory space required. If the + image is pre-linked, LOADDR will be non-zero. Since we do + not supply MAP_FIXED here we'll use that address if and + only if it remains available. */ + load_addr = target_mmap(loaddr, hiaddr - loaddr, PROT_NONE, + MAP_PRIVATE | MAP_ANON | MAP_NORESERVE, -1, 0); + if (load_addr == -1) { + goto exit_perror; + } + } else if (pinterp_name != NULL) { + /* This is the main executable. Make sure that the low + address does not conflict with MMAP_MIN_ADDR or the + QEMU application itself. */ +#if defined(CONFIG_USE_GUEST_BASE) + /* + * In case where user has not explicitly set the guest_base, we + * probe here that should we set it automatically. + */ + if (!have_guest_base && !reserved_va) { + unsigned long host_start, real_start, host_size; + + /* Round addresses to page boundaries. */ + loaddr &= qemu_host_page_mask; + hiaddr = HOST_PAGE_ALIGN(hiaddr); + + if (loaddr < mmap_min_addr) { + host_start = HOST_PAGE_ALIGN(mmap_min_addr); + } else { + host_start = loaddr; + if (host_start != loaddr) { + errmsg = "Address overflow loading ELF binary"; + goto exit_errmsg; + } + } + host_size = hiaddr - loaddr; + while (1) { + /* Do not use mmap_find_vma here because that is limited to the + guest address space. We are going to make the + guest address space fit whatever we're given. */ + real_start = (unsigned long) + mmap((void *)host_start, host_size, PROT_NONE, + MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE, -1, 0); + if (real_start == (unsigned long)-1) { + goto exit_perror; + } + if (real_start == host_start) { + break; + } + /* That address didn't work. Unmap and try a different one. + The address the host picked because is typically right at + the top of the host address space and leaves the guest with + no usable address space. Resort to a linear search. We + already compensated for mmap_min_addr, so this should not + happen often. Probably means we got unlucky and host + address space randomization put a shared library somewhere + inconvenient. */ + munmap((void *)real_start, host_size); + host_start += qemu_host_page_size; + if (host_start == loaddr) { + /* Theoretically possible if host doesn't have any suitably + aligned areas. Normally the first mmap will fail. */ + errmsg = "Unable to find space for application"; + goto exit_errmsg; + } + } + qemu_log("Relocating guest address space from 0x" + TARGET_ABI_FMT_lx " to 0x%lx\n", loaddr, real_start); + guest_base = real_start - loaddr; + } +#endif + } + load_bias = load_addr - loaddr; + + info->load_bias = load_bias; + info->load_addr = load_addr; + info->entry = ehdr->e_entry + load_bias; + info->start_code = -1; + info->end_code = 0; + info->start_data = -1; + info->end_data = 0; + info->brk = 0; + + for (i = 0; i < ehdr->e_phnum; i++) { + struct elf_phdr *eppnt = phdr + i; + if (eppnt->p_type == PT_LOAD) { + abi_ulong vaddr, vaddr_po, vaddr_ps, vaddr_ef, vaddr_em; + int elf_prot = 0; + + if (eppnt->p_flags & PF_R) elf_prot = PROT_READ; + if (eppnt->p_flags & PF_W) elf_prot |= PROT_WRITE; + if (eppnt->p_flags & PF_X) elf_prot |= PROT_EXEC; + + vaddr = load_bias + eppnt->p_vaddr; + vaddr_po = TARGET_ELF_PAGEOFFSET(vaddr); + vaddr_ps = TARGET_ELF_PAGESTART(vaddr); + + error = target_mmap(vaddr_ps, eppnt->p_filesz + vaddr_po, + elf_prot, MAP_PRIVATE | MAP_FIXED, + image_fd, eppnt->p_offset - vaddr_po); if (error == -1) { - perror("mmap"); - exit(-1); + goto exit_perror; + } + + vaddr_ef = vaddr + eppnt->p_filesz; + vaddr_em = vaddr + eppnt->p_memsz; + + /* If the load segment requests extra zeros (e.g. bss), map it. */ + if (vaddr_ef < vaddr_em) { + zero_bss(vaddr_ef, vaddr_em, elf_prot); + } + + /* Find the full program boundaries. */ + if (elf_prot & PROT_EXEC) { + if (vaddr < info->start_code) { + info->start_code = vaddr; + } + if (vaddr_ef > info->end_code) { + info->end_code = vaddr_ef; + } + } + if (elf_prot & PROT_WRITE) { + if (vaddr < info->start_data) { + info->start_data = vaddr; + } + if (vaddr_ef > info->end_data) { + info->end_data = vaddr_ef; + } + if (vaddr_em > info->brk) { + info->brk = vaddr_em; + } + } + } else if (eppnt->p_type == PT_INTERP && pinterp_name) { + char *interp_name; + + if (*pinterp_name) { + errmsg = "Multiple PT_INTERP entries"; + goto exit_errmsg; } - load_addr = error; - load_addr_set = 1; + interp_name = malloc(eppnt->p_filesz); + if (!interp_name) { + goto exit_perror; + } + + if (eppnt->p_offset + eppnt->p_filesz <= BPRM_BUF_SIZE) { + memcpy(interp_name, bprm_buf + eppnt->p_offset, + eppnt->p_filesz); + } else { + retval = pread(image_fd, interp_name, eppnt->p_filesz, + eppnt->p_offset); + if (retval != eppnt->p_filesz) { + goto exit_perror; + } + } + if (interp_name[eppnt->p_filesz - 1] != 0) { + errmsg = "Invalid PT_INTERP entry"; + goto exit_errmsg; + } + *pinterp_name = interp_name; } + } - eppnt = elf_phdata; - for(i=0; ie_phnum; i++, eppnt++) - if (eppnt->p_type == PT_LOAD) { - int elf_type = MAP_PRIVATE | MAP_DENYWRITE; - int elf_prot = 0; - abi_ulong vaddr = 0; - abi_ulong k; - - if (eppnt->p_flags & PF_R) elf_prot = PROT_READ; - if (eppnt->p_flags & PF_W) elf_prot |= PROT_WRITE; - if (eppnt->p_flags & PF_X) elf_prot |= PROT_EXEC; - if (interp_elf_ex->e_type == ET_EXEC || load_addr_set) { - elf_type |= MAP_FIXED; - vaddr = eppnt->p_vaddr; - } - error = target_mmap(load_addr+TARGET_ELF_PAGESTART(vaddr), - eppnt->p_filesz + TARGET_ELF_PAGEOFFSET(eppnt->p_vaddr), - elf_prot, - elf_type, - interpreter_fd, - eppnt->p_offset - TARGET_ELF_PAGEOFFSET(eppnt->p_vaddr)); - - if (error == -1) { - /* Real error */ - close(interpreter_fd); - free(elf_phdata); - return ~((abi_ulong)0UL); - } - - if (!load_addr_set && interp_elf_ex->e_type == ET_DYN) { - load_addr = error; - load_addr_set = 1; - } - - /* - * Find the end of the file mapping for this phdr, and keep - * track of the largest address we see for this. - */ - k = load_addr + eppnt->p_vaddr + eppnt->p_filesz; - if (k > elf_bss) elf_bss = k; - - /* - * Do the same thing for the memory mapping - between - * elf_bss and last_bss is the bss section. - */ - k = load_addr + eppnt->p_memsz + eppnt->p_vaddr; - if (k > last_bss) last_bss = k; - } - - /* Now use mmap to map the library into memory. */ - - close(interpreter_fd); - - /* - * Now fill out the bss section. First pad the last page up - * to the page boundary, and then perform a mmap to make sure - * that there are zeromapped pages up to and including the last - * bss page. - */ - padzero(elf_bss, last_bss); - elf_bss = TARGET_ELF_PAGESTART(elf_bss + qemu_host_page_size - 1); /* What we have mapped so far */ - - /* Map the last of the bss segment */ - if (last_bss > elf_bss) { - target_mmap(elf_bss, last_bss-elf_bss, - PROT_READ|PROT_WRITE|PROT_EXEC, - MAP_FIXED|MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); - } - free(elf_phdata); + if (info->end_data == 0) { + info->start_data = info->end_code; + info->end_data = info->end_code; + info->brk = info->end_code; + } + + if (qemu_log_enabled()) { + load_symbols(ehdr, image_fd, load_bias); + } - *interp_load_addr = load_addr; - return ((abi_ulong) interp_elf_ex->e_entry) + load_addr; + close(image_fd); + return; + + exit_read: + if (retval >= 0) { + errmsg = "Incomplete read of file header"; + goto exit_errmsg; + } + exit_perror: + errmsg = strerror(errno); + exit_errmsg: + fprintf(stderr, "%s: %s\n", image_name, errmsg); + exit(-1); +} + +static void load_elf_interp(const char *filename, struct image_info *info, + char bprm_buf[BPRM_BUF_SIZE]) +{ + int fd, retval; + + fd = open(path(filename), O_RDONLY); + if (fd < 0) { + goto exit_perror; + } + + retval = read(fd, bprm_buf, BPRM_BUF_SIZE); + if (retval < 0) { + goto exit_perror; + } + if (retval < BPRM_BUF_SIZE) { + memset(bprm_buf + retval, 0, BPRM_BUF_SIZE - retval); + } + + load_elf_image(filename, fd, info, NULL, bprm_buf); + return; + + exit_perror: + fprintf(stderr, "%s: %s\n", filename, strerror(errno)); + exit(-1); } static int symfind(const void *s0, const void *s1) @@ -1245,88 +1475,99 @@ } /* Best attempt to load symbols from this ELF object. */ -static void load_symbols(struct elfhdr *hdr, int fd) +static void load_symbols(struct elfhdr *hdr, int fd, abi_ulong load_bias) { - unsigned int i, nsyms; - struct elf_shdr sechdr, symtab, strtab; + int i, shnum, nsyms, sym_idx = 0, str_idx = 0; + struct elf_shdr *shdr; char *strings; struct syminfo *s; - struct elf_sym *syms; + struct elf_sym *syms, *new_syms; - lseek(fd, hdr->e_shoff, SEEK_SET); - for (i = 0; i < hdr->e_shnum; i++) { - if (read(fd, &sechdr, sizeof(sechdr)) != sizeof(sechdr)) - return; -#ifdef BSWAP_NEEDED - bswap_shdr(&sechdr); -#endif - if (sechdr.sh_type == SHT_SYMTAB) { - symtab = sechdr; - lseek(fd, hdr->e_shoff - + sizeof(sechdr) * sechdr.sh_link, SEEK_SET); - if (read(fd, &strtab, sizeof(strtab)) - != sizeof(strtab)) - return; -#ifdef BSWAP_NEEDED - bswap_shdr(&strtab); -#endif + shnum = hdr->e_shnum; + i = shnum * sizeof(struct elf_shdr); + shdr = (struct elf_shdr *)alloca(i); + if (pread(fd, shdr, i, hdr->e_shoff) != i) { + return; + } + + bswap_shdr(shdr, shnum); + for (i = 0; i < shnum; ++i) { + if (shdr[i].sh_type == SHT_SYMTAB) { + sym_idx = i; + str_idx = shdr[i].sh_link; goto found; } } - return; /* Shouldn't happen... */ + + /* There will be no symbol table if the file was stripped. */ + return; found: - /* Now know where the strtab and symtab are. Snarf them. */ + /* Now know where the strtab and symtab are. Snarf them. */ s = malloc(sizeof(*s)); - syms = malloc(symtab.sh_size); - if (!syms) - return; - s->disas_strtab = strings = malloc(strtab.sh_size); - if (!s->disas_strtab) + if (!s) { return; + } - lseek(fd, symtab.sh_offset, SEEK_SET); - if (read(fd, syms, symtab.sh_size) != symtab.sh_size) + i = shdr[str_idx].sh_size; + s->disas_strtab = strings = malloc(i); + if (!strings || pread(fd, strings, i, shdr[str_idx].sh_offset) != i) { + free(s); + free(strings); return; + } - nsyms = symtab.sh_size / sizeof(struct elf_sym); + i = shdr[sym_idx].sh_size; + syms = malloc(i); + if (!syms || pread(fd, syms, i, shdr[sym_idx].sh_offset) != i) { + free(s); + free(strings); + free(syms); + return; + } - i = 0; - while (i < nsyms) { -#ifdef BSWAP_NEEDED + nsyms = i / sizeof(struct elf_sym); + for (i = 0; i < nsyms; ) { bswap_sym(syms + i); -#endif - // Throw away entries which we do not need. - if (syms[i].st_shndx == SHN_UNDEF || - syms[i].st_shndx >= SHN_LORESERVE || - ELF_ST_TYPE(syms[i].st_info) != STT_FUNC) { - nsyms--; - if (i < nsyms) { + /* Throw away entries which we do not need. */ + if (syms[i].st_shndx == SHN_UNDEF + || syms[i].st_shndx >= SHN_LORESERVE + || ELF_ST_TYPE(syms[i].st_info) != STT_FUNC) { + if (i < --nsyms) { syms[i] = syms[nsyms]; } - continue; - } + } else { #if defined(TARGET_ARM) || defined (TARGET_MIPS) - /* The bottom address bit marks a Thumb or MIPS16 symbol. */ - syms[i].st_value &= ~(target_ulong)1; + /* The bottom address bit marks a Thumb or MIPS16 symbol. */ + syms[i].st_value &= ~(target_ulong)1; #endif - i++; + syms[i].st_value += load_bias; + i++; + } } - syms = realloc(syms, nsyms * sizeof(*syms)); + + /* Attempt to free the storage associated with the local symbols + that we threw away. Whether or not this has any effect on the + memory allocation depends on the malloc implementation and how + many symbols we managed to discard. */ + new_syms = realloc(syms, nsyms * sizeof(*syms)); + if (new_syms == NULL) { + free(s); + free(syms); + free(strings); + return; + } + syms = new_syms; qsort(syms, nsyms, sizeof(*syms), symcmp); - lseek(fd, strtab.sh_offset, SEEK_SET); - if (read(fd, strings, strtab.sh_size) != strtab.sh_size) - return; s->disas_num_syms = nsyms; #if ELF_CLASS == ELFCLASS32 s->disas_symtab.elf32 = syms; - s->lookup_symbol = (lookup_symbol_t)lookup_symbolxx; #else s->disas_symtab.elf64 = syms; - s->lookup_symbol = (lookup_symbol_t)lookup_symbolxx; #endif + s->lookup_symbol = lookup_symbolxx; s->next = syminfos; syminfos = s; } @@ -1334,416 +1575,67 @@ int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * regs, struct image_info * info) { + struct image_info interp_info; struct elfhdr elf_ex; - struct elfhdr interp_elf_ex; - struct exec interp_ex; - int interpreter_fd = -1; /* avoid warning */ - abi_ulong load_addr, load_bias; - int load_addr_set = 0; - unsigned int interpreter_type = INTERPRETER_NONE; - unsigned char ibcs2_interpreter; - int i; - abi_ulong mapped_addr; - struct elf_phdr * elf_ppnt; - struct elf_phdr *elf_phdata; - abi_ulong elf_bss, k, elf_brk; - int retval; - char * elf_interpreter; - abi_ulong elf_entry, interp_load_addr = 0; - int status; - abi_ulong start_code, end_code, start_data, end_data; - abi_ulong reloc_func_desc = 0; - abi_ulong elf_stack; - char passed_fileno[6]; - - ibcs2_interpreter = 0; - status = 0; - load_addr = 0; - load_bias = 0; - elf_ex = *((struct elfhdr *) bprm->buf); /* exec-header */ -#ifdef BSWAP_NEEDED - bswap_ehdr(&elf_ex); -#endif + char *elf_interpreter = NULL; - /* First of all, some simple consistency checks */ - if ((elf_ex.e_type != ET_EXEC && elf_ex.e_type != ET_DYN) || - (! elf_check_arch(elf_ex.e_machine))) { - return -ENOEXEC; - } + info->start_mmap = (abi_ulong)ELF_START_MMAP; + info->mmap = 0; + info->rss = 0; + + load_elf_image(bprm->filename, bprm->fd, info, + &elf_interpreter, bprm->buf); + + /* ??? We need a copy of the elf header for passing to create_elf_tables. + If we do nothing, we'll have overwritten this when we re-use bprm->buf + when we load the interpreter. */ + elf_ex = *(struct elfhdr *)bprm->buf; bprm->p = copy_elf_strings(1, &bprm->filename, bprm->page, bprm->p); bprm->p = copy_elf_strings(bprm->envc,bprm->envp,bprm->page,bprm->p); bprm->p = copy_elf_strings(bprm->argc,bprm->argv,bprm->page,bprm->p); if (!bprm->p) { - retval = -E2BIG; - } - - /* Now read in all of the header information */ - elf_phdata = (struct elf_phdr *)malloc(elf_ex.e_phentsize*elf_ex.e_phnum); - if (elf_phdata == NULL) { - return -ENOMEM; - } - - retval = lseek(bprm->fd, elf_ex.e_phoff, SEEK_SET); - if(retval > 0) { - retval = read(bprm->fd, (char *) elf_phdata, - elf_ex.e_phentsize * elf_ex.e_phnum); - } - - if (retval < 0) { - perror("load_elf_binary"); - exit(-1); - free (elf_phdata); - return -errno; - } - -#ifdef BSWAP_NEEDED - elf_ppnt = elf_phdata; - for (i=0; ip_type == PT_INTERP) { - if ( elf_interpreter != NULL ) - { - free (elf_phdata); - free(elf_interpreter); - close(bprm->fd); - return -EINVAL; - } - - /* This is the program interpreter used for - * shared libraries - for now assume that this - * is an a.out format binary - */ - - elf_interpreter = (char *)malloc(elf_ppnt->p_filesz); - - if (elf_interpreter == NULL) { - free (elf_phdata); - close(bprm->fd); - return -ENOMEM; - } - - retval = lseek(bprm->fd, elf_ppnt->p_offset, SEEK_SET); - if(retval >= 0) { - retval = read(bprm->fd, elf_interpreter, elf_ppnt->p_filesz); - } - if(retval < 0) { - perror("load_elf_binary2"); - exit(-1); - } - - /* If the program interpreter is one of these two, - then assume an iBCS2 image. Otherwise assume - a native linux image. */ - - /* JRP - Need to add X86 lib dir stuff here... */ - - if (strcmp(elf_interpreter,"/usr/lib/libc.so.1") == 0 || - strcmp(elf_interpreter,"/usr/lib/ld.so.1") == 0) { - ibcs2_interpreter = 1; - } - -#if 0 - printf("Using ELF interpreter %s\n", path(elf_interpreter)); -#endif - if (retval >= 0) { - retval = open(path(elf_interpreter), O_RDONLY); - if(retval >= 0) { - interpreter_fd = retval; - } - else { - perror(elf_interpreter); - exit(-1); - /* retval = -errno; */ - } - } - - if (retval >= 0) { - retval = lseek(interpreter_fd, 0, SEEK_SET); - if(retval >= 0) { - retval = read(interpreter_fd,bprm->buf,128); - } - } - if (retval >= 0) { - interp_ex = *((struct exec *) bprm->buf); /* aout exec-header */ - interp_elf_ex = *((struct elfhdr *) bprm->buf); /* elf exec-header */ - } - if (retval < 0) { - perror("load_elf_binary3"); - exit(-1); - free (elf_phdata); - free(elf_interpreter); - close(bprm->fd); - return retval; - } - } - elf_ppnt++; - } - - /* Some simple consistency checks for the interpreter */ - if (elf_interpreter){ - interpreter_type = INTERPRETER_ELF | INTERPRETER_AOUT; - - /* Now figure out which format our binary is */ - if ((N_MAGIC(interp_ex) != OMAGIC) && (N_MAGIC(interp_ex) != ZMAGIC) && - (N_MAGIC(interp_ex) != QMAGIC)) { - interpreter_type = INTERPRETER_ELF; - } - - if (interp_elf_ex.e_ident[0] != 0x7f || - strncmp((char *)&interp_elf_ex.e_ident[1], "ELF",3) != 0) { - interpreter_type &= ~INTERPRETER_ELF; - } - - if (!interpreter_type) { - free(elf_interpreter); - free(elf_phdata); - close(bprm->fd); - return -ELIBBAD; - } - } - - /* OK, we are done with that, now set up the arg stuff, - and then start this sucker up */ - - { - char * passed_p; - - if (interpreter_type == INTERPRETER_AOUT) { - snprintf(passed_fileno, sizeof(passed_fileno), "%d", bprm->fd); - passed_p = passed_fileno; - - if (elf_interpreter) { - bprm->p = copy_elf_strings(1,&passed_p,bprm->page,bprm->p); - bprm->argc++; - } - } - if (!bprm->p) { - if (elf_interpreter) { - free(elf_interpreter); - } - free (elf_phdata); - close(bprm->fd); - return -E2BIG; - } - } - - /* OK, This is the point of no return */ - info->end_data = 0; - info->end_code = 0; - info->start_mmap = (abi_ulong)ELF_START_MMAP; - info->mmap = 0; - elf_entry = (abi_ulong) elf_ex.e_entry; - -#if defined(CONFIG_USE_GUEST_BASE) - /* - * In case where user has not explicitly set the guest_base, we - * probe here that should we set it automatically. - */ - if (!have_guest_base) { - /* - * Go through ELF program header table and find out whether - * any of the segments drop below our current mmap_min_addr and - * in that case set guest_base to corresponding address. - */ - for (i = 0, elf_ppnt = elf_phdata; i < elf_ex.e_phnum; - i++, elf_ppnt++) { - if (elf_ppnt->p_type != PT_LOAD) - continue; - if (HOST_PAGE_ALIGN(elf_ppnt->p_vaddr) < mmap_min_addr) { - guest_base = HOST_PAGE_ALIGN(mmap_min_addr); - break; - } - } + fprintf(stderr, "%s: %s\n", bprm->filename, strerror(E2BIG)); + exit(-1); } -#endif /* CONFIG_USE_GUEST_BASE */ /* Do this so that we can load the interpreter, if need be. We will change some of these later */ - info->rss = 0; bprm->p = setup_arg_pages(bprm->p, bprm, info); - info->start_stack = bprm->p; - - /* Now we do a little grungy work by mmaping the ELF image into - * the correct location in memory. At this point, we assume that - * the image should be loaded at fixed address, not at a variable - * address. - */ - - for(i = 0, elf_ppnt = elf_phdata; i < elf_ex.e_phnum; i++, elf_ppnt++) { - int elf_prot = 0; - int elf_flags = 0; - abi_ulong error; - - if (elf_ppnt->p_type != PT_LOAD) - continue; - - if (elf_ppnt->p_flags & PF_R) elf_prot |= PROT_READ; - if (elf_ppnt->p_flags & PF_W) elf_prot |= PROT_WRITE; - if (elf_ppnt->p_flags & PF_X) elf_prot |= PROT_EXEC; - elf_flags = MAP_PRIVATE | MAP_DENYWRITE; - if (elf_ex.e_type == ET_EXEC || load_addr_set) { - elf_flags |= MAP_FIXED; - } else if (elf_ex.e_type == ET_DYN) { - /* Try and get dynamic programs out of the way of the default mmap - base, as well as whatever program they might try to exec. This - is because the brk will follow the loader, and is not movable. */ - /* NOTE: for qemu, we do a big mmap to get enough space - without hardcoding any address */ - error = target_mmap(0, ET_DYN_MAP_SIZE, - PROT_NONE, MAP_PRIVATE | MAP_ANON, - -1, 0); - if (error == -1) { - perror("mmap"); - exit(-1); - } - load_bias = TARGET_ELF_PAGESTART(error - elf_ppnt->p_vaddr); - } - error = target_mmap(TARGET_ELF_PAGESTART(load_bias + elf_ppnt->p_vaddr), - (elf_ppnt->p_filesz + - TARGET_ELF_PAGEOFFSET(elf_ppnt->p_vaddr)), - elf_prot, - (MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE), - bprm->fd, - (elf_ppnt->p_offset - - TARGET_ELF_PAGEOFFSET(elf_ppnt->p_vaddr))); - if (error == -1) { - perror("mmap"); - exit(-1); - } + if (elf_interpreter) { + load_elf_interp(elf_interpreter, &interp_info, bprm->buf); -#ifdef LOW_ELF_STACK - if (TARGET_ELF_PAGESTART(elf_ppnt->p_vaddr) < elf_stack) - elf_stack = TARGET_ELF_PAGESTART(elf_ppnt->p_vaddr); -#endif + /* If the program interpreter is one of these two, then assume + an iBCS2 image. Otherwise assume a native linux image. */ - if (!load_addr_set) { - load_addr_set = 1; - load_addr = elf_ppnt->p_vaddr - elf_ppnt->p_offset; - if (elf_ex.e_type == ET_DYN) { - load_bias += error - - TARGET_ELF_PAGESTART(load_bias + elf_ppnt->p_vaddr); - load_addr += load_bias; - reloc_func_desc = load_bias; - } + if (strcmp(elf_interpreter, "/usr/lib/libc.so.1") == 0 + || strcmp(elf_interpreter, "/usr/lib/ld.so.1") == 0) { + info->personality = PER_SVR4; + + /* Why this, you ask??? Well SVr4 maps page 0 as read-only, + and some applications "depend" upon this behavior. Since + we do not have the power to recompile these, we emulate + the SVr4 behavior. Sigh. */ + target_mmap(0, qemu_host_page_size, PROT_READ | PROT_EXEC, + MAP_FIXED | MAP_PRIVATE, -1, 0); } - k = elf_ppnt->p_vaddr; - if (k < start_code) - start_code = k; - if (start_data < k) - start_data = k; - k = elf_ppnt->p_vaddr + elf_ppnt->p_filesz; - if (k > elf_bss) - elf_bss = k; - if ((elf_ppnt->p_flags & PF_X) && end_code < k) - end_code = k; - if (end_data < k) - end_data = k; - k = elf_ppnt->p_vaddr + elf_ppnt->p_memsz; - if (k > elf_brk) elf_brk = k; - } - - elf_entry += load_bias; - elf_bss += load_bias; - elf_brk += load_bias; - start_code += load_bias; - end_code += load_bias; - start_data += load_bias; - end_data += load_bias; + } - if (elf_interpreter) { - if (interpreter_type & 1) { - elf_entry = load_aout_interp(&interp_ex, interpreter_fd); - } - else if (interpreter_type & 2) { - elf_entry = load_elf_interp(&interp_elf_ex, interpreter_fd, - &interp_load_addr); - } - reloc_func_desc = interp_load_addr; - - close(interpreter_fd); - free(elf_interpreter); - - if (elf_entry == ~((abi_ulong)0UL)) { - printf("Unable to load interpreter\n"); - free(elf_phdata); - exit(-1); - return 0; - } - } - - free(elf_phdata); - - if (qemu_log_enabled()) - load_symbols(&elf_ex, bprm->fd); - - if (interpreter_type != INTERPRETER_AOUT) close(bprm->fd); - info->personality = (ibcs2_interpreter ? PER_SVR4 : PER_LINUX); - -#ifdef LOW_ELF_STACK - info->start_stack = bprm->p = elf_stack - 4; -#endif - bprm->p = create_elf_tables(bprm->p, - bprm->argc, - bprm->envc, - &elf_ex, - load_addr, load_bias, - interp_load_addr, - (interpreter_type == INTERPRETER_AOUT ? 0 : 1), - info); - info->load_addr = reloc_func_desc; - info->start_brk = info->brk = elf_brk; - info->end_code = end_code; - info->start_code = start_code; - info->start_data = start_data; - info->end_data = end_data; + bprm->p = create_elf_tables(bprm->p, bprm->argc, bprm->envc, &elf_ex, + info, (elf_interpreter ? &interp_info : NULL)); info->start_stack = bprm->p; - /* Calling set_brk effectively mmaps the pages that we need for the bss and break - sections */ - set_brk(elf_bss, elf_brk); - - padzero(elf_bss, elf_brk); - -#if 0 - printf("(start_brk) %x\n" , info->start_brk); - printf("(end_code) %x\n" , info->end_code); - printf("(start_code) %x\n" , info->start_code); - printf("(end_data) %x\n" , info->end_data); - printf("(start_stack) %x\n" , info->start_stack); - printf("(brk) %x\n" , info->brk); -#endif - - if ( info->personality == PER_SVR4 ) - { - /* Why this, you ask??? Well SVr4 maps page 0 as read-only, - and some applications "depend" upon this behavior. - Since we do not have the power to recompile these, we - emulate the SVr4 behavior. Sigh. */ - mapped_addr = target_mmap(0, qemu_host_page_size, PROT_READ | PROT_EXEC, - MAP_FIXED | MAP_PRIVATE, -1, 0); + /* If we have an interpreter, set that as the program's entry point. + Copy the load_addr as well, to help PPC64 interpret the entry + point as a function descriptor. Do this after creating elf tables + so that we copy the original program entry point into the AUXV. */ + if (elf_interpreter) { + info->load_addr = interp_info.load_addr; + info->entry = interp_info.entry; + free(elf_interpreter); } - info->entry = elf_entry; - #ifdef USE_ELF_CORE_DUMP bprm->core_dump = &elf_core_dump; #endif @@ -1752,7 +1644,6 @@ } #ifdef USE_ELF_CORE_DUMP - /* * Definitions to generate Intel SVR4-like core files. * These mostly have the same names as the SVR4 types with "target_elf_" @@ -1767,7 +1658,7 @@ * Core dump code is copied from linux kernel (fs/binfmt_elf.c). * * Porting ELF coredump for target is (quite) simple process. First you - * define ELF_USE_CORE_DUMP in target ELF code (where init_thread() for + * define USE_ELF_CORE_DUMP in target ELF code (where init_thread() for * the target resides): * * #define USE_ELF_CORE_DUMP @@ -1779,13 +1670,6 @@ * #define ELF_NREG * typedef taret_elf_greg_t target_elf_gregset_t[ELF_NREG]; * - * Then define following types to match target types. Actual types can - * be found from linux kernel (arch//include/asm/posix_types.h): - * - * typedef target_uid_t; - * typedef target_gid_t; - * typedef target_pid_t; - * * Last step is to implement target specific function that copies registers * from given cpu into just specified register set. Prototype is: * @@ -1896,17 +1780,17 @@ static struct mm_struct *vma_init(void); static void vma_delete(struct mm_struct *); static int vma_add_mapping(struct mm_struct *, abi_ulong, - abi_ulong, abi_ulong); + abi_ulong, abi_ulong); static int vma_get_mapping_count(const struct mm_struct *); static struct vm_area_struct *vma_first(const struct mm_struct *); static struct vm_area_struct *vma_next(struct vm_area_struct *); static abi_ulong vma_dump_size(const struct vm_area_struct *); -static int vma_walker(void *priv, unsigned long start, unsigned long end, - unsigned long flags); +static int vma_walker(void *priv, abi_ulong start, abi_ulong end, + unsigned long flags); static void fill_elf_header(struct elfhdr *, int, uint16_t, uint32_t); static void fill_note(struct memelfnote *, const char *, int, - unsigned int, void *); + unsigned int, void *); static void fill_prstatus(struct target_elf_prstatus *, const TaskState *, int); static int fill_psinfo(struct target_elf_prpsinfo *, const TaskState *); static void fill_auxv_note(struct memelfnote *, const TaskState *); @@ -1922,9 +1806,6 @@ static int write_note_info(struct elf_note_info *, int); #ifdef BSWAP_NEEDED -static void bswap_prstatus(struct target_elf_prstatus *); -static void bswap_psinfo(struct target_elf_prpsinfo *); - static void bswap_prstatus(struct target_elf_prstatus *prstatus) { prstatus->pr_info.si_signo = tswapl(prstatus->pr_info.si_signo); @@ -1952,6 +1833,17 @@ psinfo->pr_pgrp = tswap32(psinfo->pr_pgrp); psinfo->pr_sid = tswap32(psinfo->pr_sid); } + +static void bswap_note(struct elf_note *en) +{ + bswap32s(&en->n_namesz); + bswap32s(&en->n_descsz); + bswap32s(&en->n_type); +} +#else +static inline void bswap_prstatus(struct target_elf_prstatus *p) { } +static inline void bswap_psinfo(struct target_elf_prpsinfo *p) {} +static inline void bswap_note(struct elf_note *en) { } #endif /* BSWAP_NEEDED */ /* @@ -1986,7 +1878,7 @@ } static int vma_add_mapping(struct mm_struct *mm, abi_ulong start, - abi_ulong end, abi_ulong flags) + abi_ulong end, abi_ulong flags) { struct vm_area_struct *vma; @@ -2054,23 +1946,17 @@ return (vma->vma_end - vma->vma_start); } -static int vma_walker(void *priv, unsigned long start, unsigned long end, - unsigned long flags) +static int vma_walker(void *priv, abi_ulong start, abi_ulong end, + unsigned long flags) { struct mm_struct *mm = (struct mm_struct *)priv; - /* - * Don't dump anything that qemu has reserved for internal use. - */ - if (flags & PAGE_RESERVED) - return (0); - vma_add_mapping(mm, start, end, flags); return (0); } static void fill_note(struct memelfnote *note, const char *name, int type, - unsigned int sz, void *data) + unsigned int sz, void *data) { unsigned int namesz; @@ -2091,7 +1977,7 @@ } static void fill_elf_header(struct elfhdr *elf, int segs, uint16_t machine, - uint32_t flags) + uint32_t flags) { (void) memset(elf, 0, sizeof(*elf)); @@ -2110,9 +1996,7 @@ elf->e_phentsize = sizeof(struct elf_phdr); elf->e_phnum = segs; -#ifdef BSWAP_NEEDED bswap_ehdr(elf); -#endif } static void fill_elf_note_phdr(struct elf_phdr *phdr, int sz, off_t offset) @@ -2126,9 +2010,7 @@ phdr->p_flags = 0; phdr->p_align = 0; -#ifdef BSWAP_NEEDED - bswap_phdr(phdr); -#endif + bswap_phdr(phdr, 1); } static size_t note_size(const struct memelfnote *note) @@ -2137,7 +2019,7 @@ } static void fill_prstatus(struct target_elf_prstatus *prstatus, - const TaskState *ts, int signr) + const TaskState *ts, int signr) { (void) memset(prstatus, 0, sizeof (*prstatus)); prstatus->pr_info.si_signo = prstatus->pr_cursig = signr; @@ -2146,9 +2028,7 @@ prstatus->pr_pgrp = getpgrp(); prstatus->pr_sid = getsid(0); -#ifdef BSWAP_NEEDED bswap_prstatus(prstatus); -#endif } static int fill_psinfo(struct target_elf_prpsinfo *psinfo, const TaskState *ts) @@ -2178,13 +2058,11 @@ filename = strdup(ts->bprm->filename); base_filename = strdup(basename(filename)); (void) strncpy(psinfo->pr_fname, base_filename, - sizeof(psinfo->pr_fname)); + sizeof(psinfo->pr_fname)); free(base_filename); free(filename); -#ifdef BSWAP_NEEDED bswap_psinfo(psinfo); -#endif return (0); } @@ -2227,7 +2105,7 @@ * Returns 0 in case of success, -1 otherwise (errno is set). */ static int core_dump_filename(const TaskState *ts, char *buf, - size_t bufsize) + size_t bufsize) { char timestamp[64]; char *filename = NULL; @@ -2239,16 +2117,16 @@ if (gettimeofday(&tv, NULL) < 0) { (void) fprintf(stderr, "unable to get current timestamp: %s", - strerror(errno)); + strerror(errno)); return (-1); } filename = strdup(ts->bprm->filename); base_filename = strdup(basename(filename)); (void) strftime(timestamp, sizeof (timestamp), "%Y%m%d-%H%M%S", - localtime_r(&tv.tv_sec, &tm)); + localtime_r(&tv.tv_sec, &tm)); (void) snprintf(buf, bufsize, "qemu_%s_%s_%d.core", - base_filename, timestamp, (int)getpid()); + base_filename, timestamp, (int)getpid()); free(base_filename); free(filename); @@ -2309,9 +2187,7 @@ en.n_type = men->type; en.n_descsz = men->datasz; -#ifdef BSWAP_NEEDED bswap_note(&en); -#endif if (dump_write(fd, &en, sizeof(en)) != 0) return (-1); @@ -2333,7 +2209,7 @@ fill_prstatus(&ets->prstatus, ts, 0); elf_core_copy_regs(&ets->prstatus.pr_reg, env); fill_note(&ets->notes[0], "CORE", NT_PRSTATUS, sizeof (ets->prstatus), - &ets->prstatus); + &ets->prstatus); QTAILQ_INSERT_TAIL(&info->thread_list, ets, ets_link); @@ -2341,7 +2217,7 @@ } static int fill_note_info(struct elf_note_info *info, - long signr, const CPUState *env) + long signr, const CPUState *env) { #define NUMNOTES 3 CPUState *cpu = NULL; @@ -2369,10 +2245,10 @@ fill_prstatus(info->prstatus, ts, signr); elf_core_copy_regs(&info->prstatus->pr_reg, env); fill_note(&info->notes[0], "CORE", NT_PRSTATUS, - sizeof (*info->prstatus), info->prstatus); + sizeof (*info->prstatus), info->prstatus); fill_psinfo(info->psinfo, ts); fill_note(&info->notes[1], "CORE", NT_PRPSINFO, - sizeof (*info->psinfo), info->psinfo); + sizeof (*info->psinfo), info->psinfo); fill_auxv_note(&info->notes[2], ts); info->numnote = 3; @@ -2419,7 +2295,7 @@ /* write prstatus for each thread */ for (ets = info->thread_list.tqh_first; ets != NULL; - ets = ets->ets_link.tqe_next) { + ets = ets->ets_link.tqe_next) { if ((error = write_note(&ets->notes[0], fd)) != 0) return (error); } @@ -2487,13 +2363,13 @@ errno = 0; getrlimit(RLIMIT_CORE, &dumpsize); if (dumpsize.rlim_cur == 0) - return 0; + return 0; if (core_dump_filename(ts, corefile, sizeof (corefile)) < 0) return (-errno); if ((fd = open(corefile, O_WRONLY | O_CREAT, - S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) < 0) + S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) < 0) return (-errno); /* @@ -2582,7 +2458,7 @@ end = vma->vma_start + vma_dump_size(vma); for (addr = vma->vma_start; addr < end; - addr += TARGET_PAGE_SIZE) { + addr += TARGET_PAGE_SIZE) { char page[TARGET_PAGE_SIZE]; int error; @@ -2592,8 +2468,8 @@ */ error = copy_from_user(page, addr, sizeof (page)); if (error != 0) { - (void) fprintf(stderr, "unable to dump " TARGET_FMT_lx "\n", - addr); + (void) fprintf(stderr, "unable to dump " TARGET_ABI_FMT_lx "\n", + addr); errno = -error; goto out; } @@ -2602,7 +2478,7 @@ } } -out: + out: free_note_info(&info); if (mm != NULL) vma_delete(mm); @@ -2612,15 +2488,8 @@ return (-errno); return (0); } - #endif /* USE_ELF_CORE_DUMP */ -static int load_aout_interp(void * exptr, int interp_fd) -{ - printf("a.out interpreter not yet supported\n"); - return(0); -} - void do_init_thread(struct target_pt_regs *regs, struct image_info *infop) { init_thread(regs, infop); diff -Nru qemu-kvm-0.12.5+noroms/linux-user/flatload.c qemu-kvm-0.14.1/linux-user/flatload.c --- qemu-kvm-0.12.5+noroms/linux-user/flatload.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/linux-user/flatload.c 2011-05-11 13:29:46.000000000 +0000 @@ -338,7 +338,7 @@ static void old_reloc(struct lib_info *libinfo, uint32_t rl) { #ifdef DEBUG - char *segment[] = { "TEXT", "DATA", "BSS", "*UNKNOWN*" }; + const char *segment[] = { "TEXT", "DATA", "BSS", "*UNKNOWN*" }; #endif uint32_t *ptr; uint32_t offset; @@ -383,7 +383,8 @@ struct lib_info *libinfo, int id, abi_ulong *extra_stack) { struct flat_hdr * hdr; - abi_ulong textpos = 0, datapos = 0, result; + abi_ulong textpos = 0, datapos = 0; + abi_long result; abi_ulong realdatastart = 0; abi_ulong text_len, data_len, bss_len, stack_len, flags; abi_ulong memp = 0; /* for finding the brk area */ @@ -802,6 +803,7 @@ info->end_data = libinfo[0].end_data; info->start_brk = libinfo[0].start_brk; info->start_stack = sp; + info->stack_limit = libinfo[0].start_brk; info->entry = start_addr; info->code_offset = info->start_code; info->data_offset = info->start_data - libinfo[0].text_len; diff -Nru qemu-kvm-0.12.5+noroms/linux-user/ioctls.h qemu-kvm-0.14.1/linux-user/ioctls.h --- qemu-kvm-0.12.5+noroms/linux-user/ioctls.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/linux-user/ioctls.h 2011-05-11 13:29:46.000000000 +0000 @@ -76,6 +76,10 @@ #ifdef FIGETBSZ IOCTL(FIGETBSZ, IOC_R, MK_PTR(TYPE_LONG)) #endif +#ifdef CONFIG_FIEMAP + IOCTL_SPECIAL(FS_IOC_FIEMAP, IOC_W | IOC_R, do_ioctl_fs_ioc_fiemap, + MK_PTR(MK_STRUCT(STRUCT_fiemap))) +#endif IOCTL(SIOCATMARK, 0, TYPE_NULL) IOCTL(SIOCADDRT, IOC_W, MK_PTR(MK_STRUCT(STRUCT_rtentry))) @@ -308,10 +312,8 @@ IOCTL(LOOP_CLR_FD, 0, TYPE_INT) IOCTL(LOOP_SET_STATUS, IOC_W, MK_PTR(MK_STRUCT(STRUCT_loop_info))) IOCTL(LOOP_GET_STATUS, IOC_W, MK_PTR(MK_STRUCT(STRUCT_loop_info))) -#if 0 /* These have some problems - not fully tested */ IOCTL(LOOP_SET_STATUS64, IOC_W, MK_PTR(MK_STRUCT(STRUCT_loop_info64))) IOCTL(LOOP_GET_STATUS64, IOC_W, MK_PTR(MK_STRUCT(STRUCT_loop_info64))) -#endif IOCTL(LOOP_CHANGE_FD, 0, TYPE_INT) IOCTL(MTIOCTOP, IOC_W, MK_PTR(MK_STRUCT(STRUCT_mtop))) diff -Nru qemu-kvm-0.12.5+noroms/linux-user/linuxload.c qemu-kvm-0.14.1/linux-user/linuxload.c --- qemu-kvm-0.12.5+noroms/linux-user/linuxload.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/linux-user/linuxload.c 2011-05-11 13:29:46.000000000 +0000 @@ -96,18 +96,16 @@ } } - retval = lseek(bprm->fd, 0L, SEEK_SET); - if(retval >= 0) { - retval = read(bprm->fd, bprm->buf, 128); - } - if(retval < 0) { + retval = read(bprm->fd, bprm->buf, BPRM_BUF_SIZE); + if (retval < 0) { perror("prepare_binprm"); exit(-1); - /* return(-errno); */ } - else { - return(retval); + if (retval < BPRM_BUF_SIZE) { + /* Make sure the rest of the loader won't read garbage. */ + memset(bprm->buf + retval, 0, BPRM_BUF_SIZE - retval); } + return retval; } /* Construct the envp and argv tables on the target stack. */ @@ -163,8 +161,7 @@ int i; bprm->p = TARGET_PAGE_SIZE*MAX_ARG_PAGES-sizeof(unsigned int); - for (i=0 ; ipage[i] = NULL; + memset(bprm->page, 0, sizeof(bprm->page)); retval = open(filename, O_RDONLY); if (retval < 0) return retval; @@ -177,18 +174,12 @@ retval = prepare_binprm(bprm); - infop->host_argv = argv; - if(retval>=0) { if (bprm->buf[0] == 0x7f && bprm->buf[1] == 'E' && bprm->buf[2] == 'L' && bprm->buf[3] == 'F') { -#ifndef TARGET_HAS_ELFLOAD32 - retval = load_elf_binary(bprm,regs,infop); -#else - retval = load_elf_binary_multi(bprm, regs, infop); -#endif + retval = load_elf_binary(bprm, regs, infop); #if defined(TARGET_HAS_BFLT) } else if (bprm->buf[0] == 'b' && bprm->buf[1] == 'F' diff -Nru qemu-kvm-0.12.5+noroms/linux-user/m68k-sim.c qemu-kvm-0.14.1/linux-user/m68k-sim.c --- qemu-kvm-0.12.5+noroms/linux-user/m68k-sim.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/linux-user/m68k-sim.c 2011-05-11 13:29:46.000000000 +0000 @@ -38,7 +38,7 @@ #define SYS_ISATTY 29 #define SYS_LSEEK 199 -struct m86k_sim_stat { +struct m68k_sim_stat { uint16_t sim_st_dev; uint16_t sim_st_ino; uint32_t sim_st_mode; @@ -138,10 +138,10 @@ { struct stat s; int rc; - struct m86k_sim_stat *p; + struct m68k_sim_stat *p; rc = check_err(env, fstat(ARG(0), &s)); if (rc == 0) { - p = (struct m86k_sim_stat *)(unsigned long)ARG(1); + p = (struct m68k_sim_stat *)(unsigned long)ARG(1); p->sim_st_dev = tswap16(s.st_dev); p->sim_st_ino = tswap16(s.st_ino); p->sim_st_mode = tswap32(s.st_mode); diff -Nru qemu-kvm-0.12.5+noroms/linux-user/main.c qemu-kvm-0.14.1/linux-user/main.c --- qemu-kvm-0.12.5+noroms/linux-user/main.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/linux-user/main.c 2011-05-11 13:29:46.000000000 +0000 @@ -24,14 +24,15 @@ #include #include #include +#include #include "qemu.h" #include "qemu-common.h" #include "cache-utils.h" /* For tb_lock */ #include "exec-all.h" - - +#include "tcg.h" +#include "qemu-timer.h" #include "envlist.h" #define DEBUG_LOGFILE "/tmp/qemu.log" @@ -39,19 +40,20 @@ char *exec_path; int singlestep; -#if defined(CONFIG_USE_GUEST_BASE) unsigned long mmap_min_addr; +#if defined(CONFIG_USE_GUEST_BASE) unsigned long guest_base; int have_guest_base; +unsigned long reserved_va; #endif -static const char *interp_prefix = CONFIG_QEMU_PREFIX; +static const char *interp_prefix = CONFIG_QEMU_INTERP_PREFIX; const char *qemu_uname_release = CONFIG_UNAME_RELEASE; /* XXX: on x86 MAP_GROWSDOWN only works if ESP <= address + 32, so we allocate a bigger stack. Need a better solution, for example by remapping the process stack directly at the right place */ -unsigned long x86_stack_size = 512 * 1024; +unsigned long guest_stack_size = 8 * 1024 * 1024UL; void gemu_log(const char *fmt, ...) { @@ -99,13 +101,14 @@ /* Make sure everything is in a consistent state for calling fork(). */ void fork_start(void) { - mmap_fork_start(); pthread_mutex_lock(&tb_lock); pthread_mutex_lock(&exclusive_lock); + mmap_fork_start(); } void fork_end(int child) { + mmap_fork_end(child); if (child) { /* Child processes created by fork() only have a single thread. Discard information about the parent threads. */ @@ -122,7 +125,6 @@ pthread_mutex_unlock(&exclusive_lock); pthread_mutex_unlock(&tb_lock); } - mmap_fork_end(child); } /* Wait for pending exclusive operations to complete. The exclusive lock @@ -548,6 +550,8 @@ case 3: segv = get_user_u32(val, addr); break; + default: + abort(); } if (segv) { env->cp15.c6_data = addr; @@ -585,7 +589,7 @@ } if (size == 3) { val = env->regs[(env->exclusive_info >> 12) & 0xf]; - segv = put_user_u32(val, addr); + segv = put_user_u32(val, addr + 4); if (segv) { env->cp15.c6_data = addr + 4; goto done; @@ -937,7 +941,8 @@ void cpu_loop (CPUSPARCState *env) { - int trapnr, ret; + int trapnr; + abi_long ret; target_siginfo_t info; while (1) { @@ -955,7 +960,7 @@ env->regwptr[0], env->regwptr[1], env->regwptr[2], env->regwptr[3], env->regwptr[4], env->regwptr[5]); - if ((unsigned int)ret >= (unsigned int)(-515)) { + if ((abi_ulong)ret >= (abi_ulong)(-515)) { #if defined(TARGET_SPARC64) && !defined(TARGET_ABI32) env->xcc |= PSR_CARRY; #else @@ -1068,9 +1073,9 @@ return 0; } -uint32_t cpu_ppc_load_tbl (CPUState *env) +uint64_t cpu_ppc_load_tbl (CPUState *env) { - return cpu_ppc_get_tb(env) & 0xFFFFFFFF; + return cpu_ppc_get_tb(env); } uint32_t cpu_ppc_load_tbu (CPUState *env) @@ -1078,9 +1083,9 @@ return cpu_ppc_get_tb(env) >> 32; } -uint32_t cpu_ppc_load_atbl (CPUState *env) +uint64_t cpu_ppc_load_atbl (CPUState *env) { - return cpu_ppc_get_tb(env) & 0xFFFFFFFF; + return cpu_ppc_get_tb(env); } uint32_t cpu_ppc_load_atbu (CPUState *env) @@ -1097,12 +1102,12 @@ } /* XXX: to be fixed */ -int ppc_dcr_read (ppc_dcr_t *dcr_env, int dcrn, target_ulong *valp) +int ppc_dcr_read (ppc_dcr_t *dcr_env, int dcrn, uint32_t *valp) { return -1; } -int ppc_dcr_write (ppc_dcr_t *dcr_env, int dcrn, target_ulong val) +int ppc_dcr_write (ppc_dcr_t *dcr_env, int dcrn, uint32_t val) { return -1; } @@ -2228,6 +2233,37 @@ env->regs[3] = ret; env->sregs[SR_PC] = env->regs[14]; break; + case EXCP_HW_EXCP: + env->regs[17] = env->sregs[SR_PC] + 4; + if (env->iflags & D_FLAG) { + env->sregs[SR_ESR] |= 1 << 12; + env->sregs[SR_PC] -= 4; + /* FIXME: if branch was immed, replay the imm aswell. */ + } + + env->iflags &= ~(IMM_FLAG | D_FLAG); + + switch (env->sregs[SR_ESR] & 31) { + case ESR_EC_FPU: + info.si_signo = SIGFPE; + info.si_errno = 0; + if (env->sregs[SR_FSR] & FSR_IO) { + info.si_code = TARGET_FPE_FLTINV; + } + if (env->sregs[SR_FSR] & FSR_DZ) { + info.si_code = TARGET_FPE_FLTDIV; + } + info._sifields._sigfault._addr = 0; + queue_signal(env, info.si_signo, &info); + break; + default: + printf ("Unhandled hw-exception: 0x%x\n", + env->sregs[SR_ESR] & 5); + cpu_dump_state(env, stderr, fprintf, 0); + exit (1); + break; + } + break; case EXCP_DEBUG: { int sig; @@ -2345,14 +2381,65 @@ #endif /* TARGET_M68K */ #ifdef TARGET_ALPHA +static void do_store_exclusive(CPUAlphaState *env, int reg, int quad) +{ + target_ulong addr, val, tmp; + target_siginfo_t info; + int ret = 0; + + addr = env->lock_addr; + tmp = env->lock_st_addr; + env->lock_addr = -1; + env->lock_st_addr = 0; + + start_exclusive(); + mmap_lock(); + + if (addr == tmp) { + if (quad ? get_user_s64(val, addr) : get_user_s32(val, addr)) { + goto do_sigsegv; + } + + if (val == env->lock_value) { + tmp = env->ir[reg]; + if (quad ? put_user_u64(tmp, addr) : put_user_u32(tmp, addr)) { + goto do_sigsegv; + } + ret = 1; + } + } + env->ir[reg] = ret; + env->pc += 4; + + mmap_unlock(); + end_exclusive(); + return; + + do_sigsegv: + mmap_unlock(); + end_exclusive(); + + info.si_signo = TARGET_SIGSEGV; + info.si_errno = 0; + info.si_code = TARGET_SEGV_MAPERR; + info._sifields._sigfault._addr = addr; + queue_signal(env, TARGET_SIGSEGV, &info); +} + void cpu_loop (CPUState *env) { int trapnr; target_siginfo_t info; + abi_long sysret; while (1) { trapnr = cpu_alpha_exec (env); + /* All of the traps imply a transition through PALcode, which + implies an REI instruction has been executed. Which means + that the intr_flag should be cleared. */ + env->intr_flag = 0; + switch (trapnr) { case EXCP_RESET: fprintf(stderr, "Reset requested. Exit\n"); @@ -2363,16 +2450,25 @@ exit(1); break; case EXCP_ARITH: - fprintf(stderr, "Arithmetic trap.\n"); - exit(1); + env->lock_addr = -1; + info.si_signo = TARGET_SIGFPE; + info.si_errno = 0; + info.si_code = TARGET_FPE_FLTINV; + info._sifields._sigfault._addr = env->pc; + queue_signal(env, info.si_signo, &info); break; case EXCP_HW_INTERRUPT: fprintf(stderr, "External interrupt. Exit\n"); exit(1); break; case EXCP_DFAULT: - fprintf(stderr, "MMU data fault\n"); - exit(1); + env->lock_addr = -1; + info.si_signo = TARGET_SIGSEGV; + info.si_errno = 0; + info.si_code = (page_get_flags(env->ipr[IPR_EXC_ADDR]) & PAGE_VALID + ? TARGET_SEGV_ACCERR : TARGET_SEGV_MAPERR); + info._sifields._sigfault._addr = env->ipr[IPR_EXC_ADDR]; + queue_signal(env, info.si_signo, &info); break; case EXCP_DTB_MISS_PAL: fprintf(stderr, "MMU data TLB miss in PALcode\n"); @@ -2391,38 +2487,133 @@ exit(1); break; case EXCP_UNALIGN: - fprintf(stderr, "Unaligned access\n"); - exit(1); + env->lock_addr = -1; + info.si_signo = TARGET_SIGBUS; + info.si_errno = 0; + info.si_code = TARGET_BUS_ADRALN; + info._sifields._sigfault._addr = env->ipr[IPR_EXC_ADDR]; + queue_signal(env, info.si_signo, &info); break; case EXCP_OPCDEC: - fprintf(stderr, "Invalid instruction\n"); - exit(1); + do_sigill: + env->lock_addr = -1; + info.si_signo = TARGET_SIGILL; + info.si_errno = 0; + info.si_code = TARGET_ILL_ILLOPC; + info._sifields._sigfault._addr = env->pc; + queue_signal(env, info.si_signo, &info); break; case EXCP_FEN: - fprintf(stderr, "Floating-point not allowed\n"); - exit(1); + /* No-op. Linux simply re-enables the FPU. */ break; case EXCP_CALL_PAL ... (EXCP_CALL_PALP - 1): - call_pal(env, (trapnr >> 6) | 0x80); + env->lock_addr = -1; + switch ((trapnr >> 6) | 0x80) { + case 0x80: + /* BPT */ + info.si_signo = TARGET_SIGTRAP; + info.si_errno = 0; + info.si_code = TARGET_TRAP_BRKPT; + info._sifields._sigfault._addr = env->pc; + queue_signal(env, info.si_signo, &info); + break; + case 0x81: + /* BUGCHK */ + info.si_signo = TARGET_SIGTRAP; + info.si_errno = 0; + info.si_code = 0; + info._sifields._sigfault._addr = env->pc; + queue_signal(env, info.si_signo, &info); + break; + case 0x83: + /* CALLSYS */ + trapnr = env->ir[IR_V0]; + sysret = do_syscall(env, trapnr, + env->ir[IR_A0], env->ir[IR_A1], + env->ir[IR_A2], env->ir[IR_A3], + env->ir[IR_A4], env->ir[IR_A5]); + if (trapnr == TARGET_NR_sigreturn + || trapnr == TARGET_NR_rt_sigreturn) { + break; + } + /* Syscall writes 0 to V0 to bypass error check, similar + to how this is handled internal to Linux kernel. */ + if (env->ir[IR_V0] == 0) { + env->ir[IR_V0] = sysret; + } else { + env->ir[IR_V0] = (sysret < 0 ? -sysret : sysret); + env->ir[IR_A3] = (sysret < 0); + } + break; + case 0x86: + /* IMB */ + /* ??? We can probably elide the code using page_unprotect + that is checking for self-modifying code. Instead we + could simply call tb_flush here. Until we work out the + changes required to turn off the extra write protection, + this can be a no-op. */ + break; + case 0x9E: + /* RDUNIQUE */ + /* Handled in the translator for usermode. */ + abort(); + case 0x9F: + /* WRUNIQUE */ + /* Handled in the translator for usermode. */ + abort(); + case 0xAA: + /* GENTRAP */ + info.si_signo = TARGET_SIGFPE; + switch (env->ir[IR_A0]) { + case TARGET_GEN_INTOVF: + info.si_code = TARGET_FPE_INTOVF; + break; + case TARGET_GEN_INTDIV: + info.si_code = TARGET_FPE_INTDIV; + break; + case TARGET_GEN_FLTOVF: + info.si_code = TARGET_FPE_FLTOVF; + break; + case TARGET_GEN_FLTUND: + info.si_code = TARGET_FPE_FLTUND; + break; + case TARGET_GEN_FLTINV: + info.si_code = TARGET_FPE_FLTINV; + break; + case TARGET_GEN_FLTINE: + info.si_code = TARGET_FPE_FLTRES; + break; + case TARGET_GEN_ROPRAND: + info.si_code = 0; + break; + default: + info.si_signo = TARGET_SIGTRAP; + info.si_code = 0; + break; + } + info.si_errno = 0; + info._sifields._sigfault._addr = env->pc; + queue_signal(env, info.si_signo, &info); + break; + default: + goto do_sigill; + } break; case EXCP_CALL_PALP ... (EXCP_CALL_PALE - 1): - fprintf(stderr, "Privileged call to PALcode\n"); - exit(1); - break; + goto do_sigill; case EXCP_DEBUG: - { - int sig; - - sig = gdb_handlesig (env, TARGET_SIGTRAP); - if (sig) - { - info.si_signo = sig; - info.si_errno = 0; - info.si_code = TARGET_TRAP_BRKPT; - queue_signal(env, info.si_signo, &info); - } + info.si_signo = gdb_handlesig (env, TARGET_SIGTRAP); + if (info.si_signo) { + env->lock_addr = -1; + info.si_errno = 0; + info.si_code = TARGET_TRAP_BRKPT; + queue_signal(env, info.si_signo, &info); } break; + case EXCP_STL_C: + case EXCP_STQ_C: + do_store_exclusive(env, env->error_code, trapnr - EXCP_STL_C); + break; default: printf ("Unhandled trap: 0x%x\n", trapnr); cpu_dump_state(env, stderr, fprintf, 0); @@ -2451,6 +2642,7 @@ "-0 argv0 forces target process argv[0] to be argv0\n" #if defined(CONFIG_USE_GUEST_BASE) "-B address set guest_base address to address\n" + "-R size reserve size bytes for guest virtual address space\n" #endif "\n" "Debug options:\n" @@ -2471,7 +2663,7 @@ , TARGET_ARCH, interp_prefix, - x86_stack_size, + guest_stack_size, DEBUG_LOGFILE); exit(1); } @@ -2519,7 +2711,7 @@ struct target_pt_regs regs1, *regs = ®s1; struct image_info info1, *info = &info1; struct linux_binprm bprm; - TaskState ts1, *ts = &ts1; + TaskState *ts; CPUState *env; int optind; const char *r; @@ -2550,7 +2742,22 @@ (void) envlist_setenv(envlist, *wrk); } + /* Read the stack limit from the kernel. If it's "unlimited", + then we can do little else besides use the default. */ + { + struct rlimit lim; + if (getrlimit(RLIMIT_STACK, &lim) == 0 + && lim.rlim_cur != RLIM_INFINITY + && lim.rlim_cur == (target_long)lim.rlim_cur) { + guest_stack_size = lim.rlim_cur; + } + } + cpu_model = NULL; +#if defined(cpudef_setup) + cpudef_setup(); /* parse cpu definitions in target config file (TBD) */ +#endif + optind = 1; for(;;) { if (optind >= argc) @@ -2583,6 +2790,12 @@ r = argv[optind++]; if (envlist_setenv(envlist, r) != 0) usage(); + } else if (!strcmp(r, "ignore-environment")) { + envlist_free(envlist); + if ((envlist = envlist_create()) == NULL) { + (void) fprintf(stderr, "Unable to allocate envlist\n"); + exit(1); + } } else if (!strcmp(r, "U")) { r = argv[optind++]; if (envlist_unsetenv(envlist, r) != 0) @@ -2594,13 +2807,13 @@ if (optind >= argc) break; r = argv[optind++]; - x86_stack_size = strtol(r, (char **)&r, 0); - if (x86_stack_size <= 0) + guest_stack_size = strtoul(r, (char **)&r, 0); + if (guest_stack_size == 0) usage(); if (*r == 'M') - x86_stack_size *= 1024 * 1024; + guest_stack_size *= 1024 * 1024; else if (*r == 'k' || *r == 'K') - x86_stack_size *= 1024; + guest_stack_size *= 1024; } else if (!strcmp(r, "L")) { interp_prefix = argv[optind++]; } else if (!strcmp(r, "p")) { @@ -2622,8 +2835,10 @@ cpu_model = argv[optind++]; if (cpu_model == NULL || strcmp(cpu_model, "?") == 0) { /* XXX: implement xxx_cpu_list for targets that still miss it */ -#if defined(cpu_list) - cpu_list(stdout, &fprintf); +#if defined(cpu_list_id) + cpu_list_id(stdout, &fprintf, ""); +#elif defined(cpu_list) + cpu_list(stdout, &fprintf); /* deprecated */ #endif exit(1); } @@ -2631,6 +2846,39 @@ } else if (!strcmp(r, "B")) { guest_base = strtol(argv[optind++], NULL, 0); have_guest_base = 1; + } else if (!strcmp(r, "R")) { + char *p; + int shift = 0; + reserved_va = strtoul(argv[optind++], &p, 0); + switch (*p) { + case 'k': + case 'K': + shift = 10; + break; + case 'M': + shift = 20; + break; + case 'G': + shift = 30; + break; + } + if (shift) { + unsigned long unshifted = reserved_va; + p++; + reserved_va <<= shift; + if (((reserved_va >> shift) != unshifted) +#if HOST_LONG_BITS > TARGET_VIRT_ADDR_SPACE_BITS + || (reserved_va > (1ul << TARGET_VIRT_ADDR_SPACE_BITS)) +#endif + ) { + fprintf(stderr, "Reserved virtual address too big\n"); + exit(1); + } + } + if (*p) { + fprintf(stderr, "Unrecognised -R size suffix '%s'\n", p); + exit(1); + } #endif } else if (!strcmp(r, "drop-ld-preload")) { (void) envlist_unsetenv(envlist, "LD_PRELOAD"); @@ -2720,15 +2968,41 @@ */ guest_base = HOST_PAGE_ALIGN(guest_base); + if (reserved_va) { + void *p; + int flags; + + flags = MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE; + if (have_guest_base) { + flags |= MAP_FIXED; + } + p = mmap((void *)guest_base, reserved_va, PROT_NONE, flags, -1, 0); + if (p == MAP_FAILED) { + fprintf(stderr, "Unable to reserve guest address space\n"); + exit(1); + } + guest_base = (unsigned long)p; + /* Make sure the address is properly aligned. */ + if (guest_base & ~qemu_host_page_mask) { + munmap(p, reserved_va); + p = mmap((void *)guest_base, reserved_va + qemu_host_page_size, + PROT_NONE, flags, -1, 0); + if (p == MAP_FAILED) { + fprintf(stderr, "Unable to reserve guest address space\n"); + exit(1); + } + guest_base = HOST_PAGE_ALIGN((unsigned long)p); + } + qemu_log("Reserved 0x%lx bytes of guest address space\n", reserved_va); + } +#endif /* CONFIG_USE_GUEST_BASE */ + /* * Read in mmap_min_addr kernel parameter. This value is used * When loading the ELF image to determine whether guest_base - * is needed. - * - * When user has explicitly set the quest base, we skip this - * test. + * is needed. It is also used in mmap_find_vma. */ - if (!have_guest_base) { + { FILE *fp; if ((fp = fopen("/proc/sys/vm/mmap_min_addr", "r")) != NULL) { @@ -2740,7 +3014,6 @@ fclose(fp); } } -#endif /* CONFIG_USE_GUEST_BASE */ /* * Prepare copy of argv vector for target. @@ -2765,7 +3038,7 @@ } target_argv[target_argc] = NULL; - memset(ts, 0, sizeof(TaskState)); + ts = qemu_mallocz (sizeof(TaskState)); init_task_state(ts); /* build Task State */ ts->info = info; @@ -2814,6 +3087,13 @@ syscall_init(); signal_init(); +#if defined(CONFIG_USE_GUEST_BASE) + /* Now that we've loaded the binary, GUEST_BASE is fixed. Delay + generating the prologue until now so that the prologue can take + the real value of GUEST_BASE into account. */ + tcg_prologue_init(&tcg_ctx); +#endif + #if defined(TARGET_I386) cpu_x86_set_cpl(env, 3); @@ -3030,7 +3310,10 @@ for(i = 0; i < 32; i++) { env->active_tc.gpr[i] = regs->regs[i]; } - env->active_tc.PC = regs->cp0_epc; + env->active_tc.PC = regs->cp0_epc & ~(target_ulong)1; + if (regs->cp0_epc & 1) { + env->hflags |= MIPS_HFLAG_M16; + } } #elif defined(TARGET_SH4) { @@ -3048,10 +3331,8 @@ for(i = 0; i < 28; i++) { env->ir[i] = ((abi_ulong *)regs)[i]; } - env->ipr[IPR_USP] = regs->usp; - env->ir[30] = regs->usp; + env->ir[IR_SP] = regs->usp; env->pc = regs->pc; - env->unique = regs->unique; } #elif defined(TARGET_CRIS) { diff -Nru qemu-kvm-0.12.5+noroms/linux-user/mips/syscall_nr.h qemu-kvm-0.14.1/linux-user/mips/syscall_nr.h --- qemu-kvm-0.12.5+noroms/linux-user/mips/syscall_nr.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/linux-user/mips/syscall_nr.h 2011-05-11 13:29:46.000000000 +0000 @@ -18,15 +18,15 @@ #define TARGET_NR_time (TARGET_NR_Linux + 13) #define TARGET_NR_mknod (TARGET_NR_Linux + 14) #define TARGET_NR_chmod (TARGET_NR_Linux + 15) -#define TARGET_NR_lchown32 (TARGET_NR_Linux + 16) +#define TARGET_NR_lchown (TARGET_NR_Linux + 16) #define TARGET_NR_break (TARGET_NR_Linux + 17) #define TARGET_NR_unused18 (TARGET_NR_Linux + 18) #define TARGET_NR_lseek (TARGET_NR_Linux + 19) #define TARGET_NR_getpid (TARGET_NR_Linux + 20) #define TARGET_NR_mount (TARGET_NR_Linux + 21) #define TARGET_NR_umount (TARGET_NR_Linux + 22) -#define TARGET_NR_setuid32 (TARGET_NR_Linux + 23) -#define TARGET_NR_getuid32 (TARGET_NR_Linux + 24) +#define TARGET_NR_setuid (TARGET_NR_Linux + 23) +#define TARGET_NR_getuid (TARGET_NR_Linux + 24) #define TARGET_NR_stime (TARGET_NR_Linux + 25) #define TARGET_NR_ptrace (TARGET_NR_Linux + 26) #define TARGET_NR_alarm (TARGET_NR_Linux + 27) @@ -48,11 +48,11 @@ #define TARGET_NR_times (TARGET_NR_Linux + 43) #define TARGET_NR_prof (TARGET_NR_Linux + 44) #define TARGET_NR_brk (TARGET_NR_Linux + 45) -#define TARGET_NR_setgid32 (TARGET_NR_Linux + 46) -#define TARGET_NR_getgid32 (TARGET_NR_Linux + 47) +#define TARGET_NR_setgid (TARGET_NR_Linux + 46) +#define TARGET_NR_getgid (TARGET_NR_Linux + 47) #define TARGET_NR_signal (TARGET_NR_Linux + 48) -#define TARGET_NR_geteuid32 (TARGET_NR_Linux + 49) -#define TARGET_NR_getegid32 (TARGET_NR_Linux + 50) +#define TARGET_NR_geteuid (TARGET_NR_Linux + 49) +#define TARGET_NR_getegid (TARGET_NR_Linux + 50) #define TARGET_NR_acct (TARGET_NR_Linux + 51) #define TARGET_NR_umount2 (TARGET_NR_Linux + 52) #define TARGET_NR_lock (TARGET_NR_Linux + 53) @@ -72,8 +72,8 @@ #define TARGET_NR_sigaction (TARGET_NR_Linux + 67) #define TARGET_NR_sgetmask (TARGET_NR_Linux + 68) #define TARGET_NR_ssetmask (TARGET_NR_Linux + 69) -#define TARGET_NR_setreuid32 (TARGET_NR_Linux + 70) -#define TARGET_NR_setregid32 (TARGET_NR_Linux + 71) +#define TARGET_NR_setreuid (TARGET_NR_Linux + 70) +#define TARGET_NR_setregid (TARGET_NR_Linux + 71) #define TARGET_NR_sigsuspend (TARGET_NR_Linux + 72) #define TARGET_NR_sigpending (TARGET_NR_Linux + 73) #define TARGET_NR_sethostname (TARGET_NR_Linux + 74) @@ -82,8 +82,8 @@ #define TARGET_NR_getrusage (TARGET_NR_Linux + 77) #define TARGET_NR_gettimeofday (TARGET_NR_Linux + 78) #define TARGET_NR_settimeofday (TARGET_NR_Linux + 79) -#define TARGET_NR_getgroups32 (TARGET_NR_Linux + 80) -#define TARGET_NR_setgroups32 (TARGET_NR_Linux + 81) +#define TARGET_NR_getgroups (TARGET_NR_Linux + 80) +#define TARGET_NR_setgroups (TARGET_NR_Linux + 81) #define TARGET_NR_reserved82 (TARGET_NR_Linux + 82) #define TARGET_NR_symlink (TARGET_NR_Linux + 83) #define TARGET_NR_unused84 (TARGET_NR_Linux + 84) @@ -97,7 +97,7 @@ #define TARGET_NR_truncate (TARGET_NR_Linux + 92) #define TARGET_NR_ftruncate (TARGET_NR_Linux + 93) #define TARGET_NR_fchmod (TARGET_NR_Linux + 94) -#define TARGET_NR_fchown32 (TARGET_NR_Linux + 95) +#define TARGET_NR_fchown (TARGET_NR_Linux + 95) #define TARGET_NR_getpriority (TARGET_NR_Linux + 96) #define TARGET_NR_setpriority (TARGET_NR_Linux + 97) #define TARGET_NR_profil (TARGET_NR_Linux + 98) @@ -140,8 +140,8 @@ #define TARGET_NR_sysfs (TARGET_NR_Linux + 135) #define TARGET_NR_personality (TARGET_NR_Linux + 136) #define TARGET_NR_afs_syscall (TARGET_NR_Linux + 137) /* Syscall for Andrew File System */ -#define TARGET_NR_setfsuid32 (TARGET_NR_Linux + 138) -#define TARGET_NR_setfsgid32 (TARGET_NR_Linux + 139) +#define TARGET_NR_setfsuid (TARGET_NR_Linux + 138) +#define TARGET_NR_setfsgid (TARGET_NR_Linux + 139) #define TARGET_NR__llseek (TARGET_NR_Linux + 140) #define TARGET_NR_getdents (TARGET_NR_Linux + 141) #define TARGET_NR__newselect (TARGET_NR_Linux + 142) @@ -187,13 +187,13 @@ #define TARGET_NR_shutdown (TARGET_NR_Linux + 182) #define TARGET_NR_socket (TARGET_NR_Linux + 183) #define TARGET_NR_socketpair (TARGET_NR_Linux + 184) -#define TARGET_NR_setresuid32 (TARGET_NR_Linux + 185) -#define TARGET_NR_getresuid32 (TARGET_NR_Linux + 186) +#define TARGET_NR_setresuid (TARGET_NR_Linux + 185) +#define TARGET_NR_getresuid (TARGET_NR_Linux + 186) #define TARGET_NR_query_module (TARGET_NR_Linux + 187) #define TARGET_NR_poll (TARGET_NR_Linux + 188) #define TARGET_NR_nfsservctl (TARGET_NR_Linux + 189) -#define TARGET_NR_setresgid32 (TARGET_NR_Linux + 190) -#define TARGET_NR_getresgid32 (TARGET_NR_Linux + 191) +#define TARGET_NR_setresgid (TARGET_NR_Linux + 190) +#define TARGET_NR_getresgid (TARGET_NR_Linux + 191) #define TARGET_NR_prctl (TARGET_NR_Linux + 192) #define TARGET_NR_rt_sigreturn (TARGET_NR_Linux + 193) #define TARGET_NR_rt_sigaction (TARGET_NR_Linux + 194) @@ -204,7 +204,7 @@ #define TARGET_NR_rt_sigsuspend (TARGET_NR_Linux + 199) #define TARGET_NR_pread64 (TARGET_NR_Linux + 200) #define TARGET_NR_pwrite64 (TARGET_NR_Linux + 201) -#define TARGET_NR_chown32 (TARGET_NR_Linux + 202) +#define TARGET_NR_chown (TARGET_NR_Linux + 202) #define TARGET_NR_getcwd (TARGET_NR_Linux + 203) #define TARGET_NR_capget (TARGET_NR_Linux + 204) #define TARGET_NR_capset (TARGET_NR_Linux + 205) diff -Nru qemu-kvm-0.12.5+noroms/linux-user/mmap.c qemu-kvm-0.14.1/linux-user/mmap.c --- qemu-kvm-0.12.5+noroms/linux-user/mmap.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/linux-user/mmap.c 2011-05-11 13:29:46.000000000 +0000 @@ -77,67 +77,6 @@ } #endif -void *qemu_vmalloc(size_t size) -{ - void *p; - unsigned long addr; - mmap_lock(); - /* Use map and mark the pages as used. */ - p = mmap(NULL, size, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - - addr = (unsigned long)p; - if (addr == (target_ulong) addr) { - /* Allocated region overlaps guest address space. - This may recurse. */ - page_set_flags(addr & TARGET_PAGE_MASK, TARGET_PAGE_ALIGN(addr + size), - PAGE_RESERVED); - } - - mmap_unlock(); - return p; -} - -void *qemu_malloc(size_t size) -{ - char * p; - size += 16; - p = qemu_vmalloc(size); - *(size_t *)p = size; - return p + 16; -} - -/* We use map, which is always zero initialized. */ -void * qemu_mallocz(size_t size) -{ - return qemu_malloc(size); -} - -void qemu_free(void *ptr) -{ - /* FIXME: We should unmark the reserved pages here. However this gets - complicated when one target page spans multiple host pages, so we - don't bother. */ - size_t *p; - p = (size_t *)((char *)ptr - 16); - munmap(p, *p); -} - -void *qemu_realloc(void *ptr, size_t size) -{ - size_t old_size, copy; - void *new_ptr; - - if (!ptr) - return qemu_malloc(size); - old_size = *(size_t *)((char *)ptr - 16); - copy = old_size < size ? old_size : size; - new_ptr = qemu_malloc(size); - memcpy(new_ptr, ptr, copy); - qemu_free(ptr); - return new_ptr; -} - /* NOTE: all the constants are the HOST ones, but addresses are target. */ int target_mprotect(abi_ulong start, abi_ulong len, int prot) { @@ -243,14 +182,15 @@ possible while it is a shared mapping */ if ((flags & MAP_TYPE) == MAP_SHARED && (prot & PROT_WRITE)) - return -EINVAL; + return -1; /* adjust protection to be able to read */ if (!(prot1 & PROT_WRITE)) mprotect(host_start, qemu_host_page_size, prot1 | PROT_WRITE); /* read the corresponding file data */ - pread(fd, g2h(start), end - start, offset); + if (pread(fd, g2h(start), end - start, offset) == -1) + return -1; /* put final protection */ if (prot_new != (prot1 | PROT_WRITE)) @@ -264,15 +204,52 @@ return 0; } -#if defined(__CYGWIN__) +#if HOST_LONG_BITS == 64 && TARGET_ABI_BITS == 64 +# define TASK_UNMAPPED_BASE (1ul << 38) +#elif defined(__CYGWIN__) /* Cygwin doesn't have a whole lot of address space. */ -static abi_ulong mmap_next_start = 0x18000000; +# define TASK_UNMAPPED_BASE 0x18000000 #else -static abi_ulong mmap_next_start = 0x40000000; +# define TASK_UNMAPPED_BASE 0x40000000 #endif +static abi_ulong mmap_next_start = TASK_UNMAPPED_BASE; unsigned long last_brk; +/* Subroutine of mmap_find_vma, used when we have pre-allocated a chunk + of guest address space. */ +static abi_ulong mmap_find_vma_reserved(abi_ulong start, abi_ulong size) +{ + abi_ulong addr; + abi_ulong last_addr; + int prot; + int looped = 0; + + if (size > RESERVED_VA) { + return (abi_ulong)-1; + } + + last_addr = start; + for (addr = start; last_addr + size != addr; addr += qemu_host_page_size) { + if (last_addr + size >= RESERVED_VA + || (abi_ulong)(last_addr + size) < last_addr) { + if (looped) { + return (abi_ulong)-1; + } + last_addr = qemu_host_page_size; + addr = 0; + looped = 1; + continue; + } + prot = page_get_flags(addr); + if (prot) { + last_addr = addr + qemu_host_page_size; + } + } + mmap_next_start = addr; + return last_addr; +} + /* * Find and reserve a free memory area of size 'size'. The search * starts at 'start'. @@ -281,19 +258,28 @@ */ abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size) { - void *ptr; + void *ptr, *prev; abi_ulong addr; - - size = HOST_PAGE_ALIGN(size); - start &= qemu_host_page_mask; + int wrapped, repeat; /* If 'start' == 0, then a default start address is used. */ - if (start == 0) + if (start == 0) { start = mmap_next_start; + } else { + start &= qemu_host_page_mask; + } + + size = HOST_PAGE_ALIGN(size); + + if (RESERVED_VA) { + return mmap_find_vma_reserved(start, size); + } addr = start; + wrapped = repeat = 0; + prev = 0; - for(;;) { + for (;; prev = ptr) { /* * Reserve needed memory area to avoid a race. * It should be discarded using: @@ -301,31 +287,77 @@ * - mremap() with MREMAP_FIXED flag * - shmat() with SHM_REMAP flag */ - ptr = mmap((void *)(unsigned long)addr, size, PROT_NONE, + ptr = mmap(g2h(addr), size, PROT_NONE, MAP_ANONYMOUS|MAP_PRIVATE|MAP_NORESERVE, -1, 0); /* ENOMEM, if host address space has no memory */ - if (ptr == MAP_FAILED) + if (ptr == MAP_FAILED) { return (abi_ulong)-1; + } - /* If address fits target address space we've found what we need */ - if ((unsigned long)ptr + size - 1 <= (abi_ulong)-1) - break; + /* Count the number of sequential returns of the same address. + This is used to modify the search algorithm below. */ + repeat = (ptr == prev ? repeat + 1 : 0); + + if (h2g_valid(ptr + size - 1)) { + addr = h2g(ptr); + + if ((addr & ~TARGET_PAGE_MASK) == 0) { + /* Success. */ + if (start == mmap_next_start && addr >= TASK_UNMAPPED_BASE) { + mmap_next_start = addr + size; + } + return addr; + } + + /* The address is not properly aligned for the target. */ + switch (repeat) { + case 0: + /* Assume the result that the kernel gave us is the + first with enough free space, so start again at the + next higher target page. */ + addr = TARGET_PAGE_ALIGN(addr); + break; + case 1: + /* Sometimes the kernel decides to perform the allocation + at the top end of memory instead. */ + addr &= TARGET_PAGE_MASK; + break; + case 2: + /* Start over at low memory. */ + addr = 0; + break; + default: + /* Fail. This unaligned block must the last. */ + addr = -1; + break; + } + } else { + /* Since the result the kernel gave didn't fit, start + again at low memory. If any repetition, fail. */ + addr = (repeat ? -1 : 0); + } - /* Unmap and try again with new page */ + /* Unmap and try again. */ munmap(ptr, size); - addr += qemu_host_page_size; - /* ENOMEM if we check whole of target address space */ - if (addr == start) + /* ENOMEM if we checked the whole of the target address space. */ + if (addr == (abi_ulong)-1) { + return (abi_ulong)-1; + } else if (addr == 0) { + if (wrapped) { + return (abi_ulong)-1; + } + wrapped = 1; + /* Don't actually use 0 when wrapping, instead indicate + that we'd truely like an allocation in low memory. */ + addr = (mmap_min_addr > TARGET_PAGE_SIZE + ? TARGET_PAGE_ALIGN(mmap_min_addr) + : TARGET_PAGE_SIZE); + } else if (wrapped && addr >= start) { return (abi_ulong)-1; + } } - - /* Update default start address */ - if (start == mmap_next_start) - mmap_next_start = (unsigned long)ptr + size; - - return h2g(ptr); } /* NOTE: all the constants are the HOST ones */ @@ -430,9 +462,6 @@ } start = h2g(host_start); } else { - int flg; - target_ulong addr; - if (start & ~TARGET_PAGE_MASK) { errno = EINVAL; goto fail; @@ -450,14 +479,6 @@ goto fail; } - for(addr = real_start; addr < real_end; addr += TARGET_PAGE_SIZE) { - flg = page_get_flags(addr); - if (flg & PAGE_RESERVED) { - errno = ENXIO; - goto fail; - } - } - /* worst case: we cannot map the file because the offset is not aligned, so we read it */ if (!(flags & MAP_ANONYMOUS) && @@ -474,7 +495,8 @@ -1, 0); if (retaddr == -1) goto fail; - pread(fd, g2h(start), len, offset); + if (pread(fd, g2h(start), len, offset) == -1) + goto fail; if (!(prot & PROT_WRITE)) { ret = target_mprotect(start, len, prot); if (ret != 0) { @@ -541,6 +563,47 @@ return -1; } +static void mmap_reserve(abi_ulong start, abi_ulong size) +{ + abi_ulong real_start; + abi_ulong real_end; + abi_ulong addr; + abi_ulong end; + int prot; + + real_start = start & qemu_host_page_mask; + real_end = HOST_PAGE_ALIGN(start + size); + end = start + size; + if (start > real_start) { + /* handle host page containing start */ + prot = 0; + for (addr = real_start; addr < start; addr += TARGET_PAGE_SIZE) { + prot |= page_get_flags(addr); + } + if (real_end == real_start + qemu_host_page_size) { + for (addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) { + prot |= page_get_flags(addr); + } + end = real_end; + } + if (prot != 0) + real_start += qemu_host_page_size; + } + if (end < real_end) { + prot = 0; + for (addr = end; addr < real_end; addr += TARGET_PAGE_SIZE) { + prot |= page_get_flags(addr); + } + if (prot != 0) + real_end -= qemu_host_page_size; + } + if (real_start != real_end) { + mmap(g2h(real_start), real_end - real_start, PROT_NONE, + MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE, + -1, 0); + } +} + int target_munmap(abi_ulong start, abi_ulong len) { abi_ulong end, real_start, real_end, addr; @@ -588,7 +651,11 @@ ret = 0; /* unmap what we can */ if (real_start < real_end) { - ret = munmap(g2h(real_start), real_end - real_start); + if (RESERVED_VA) { + mmap_reserve(real_start, real_end - real_start); + } else { + ret = munmap(g2h(real_start), real_end - real_start); + } } if (ret == 0) @@ -606,12 +673,18 @@ mmap_lock(); - if (flags & MREMAP_FIXED) + if (flags & MREMAP_FIXED) { host_addr = (void *) syscall(__NR_mremap, g2h(old_addr), old_size, new_size, flags, - new_addr); - else if (flags & MREMAP_MAYMOVE) { + g2h(new_addr)); + + if (RESERVED_VA && host_addr != MAP_FAILED) { + /* If new and old addresses overlap then the above mremap will + already have failed with EINVAL. */ + mmap_reserve(old_addr, old_size); + } + } else if (flags & MREMAP_MAYMOVE) { abi_ulong mmap_start; mmap_start = mmap_find_vma(0, new_size); @@ -619,13 +692,34 @@ if (mmap_start == -1) { errno = ENOMEM; host_addr = MAP_FAILED; - } else + } else { host_addr = (void *) syscall(__NR_mremap, g2h(old_addr), old_size, new_size, flags | MREMAP_FIXED, g2h(mmap_start)); + if ( RESERVED_VA ) { + mmap_reserve(old_addr, old_size); + } + } } else { - host_addr = mremap(g2h(old_addr), old_size, new_size, flags); + int prot = 0; + if (RESERVED_VA && old_size < new_size) { + abi_ulong addr; + for (addr = old_addr + old_size; + addr < old_addr + new_size; + addr++) { + prot |= page_get_flags(addr); + } + } + if (prot == 0) { + host_addr = mremap(g2h(old_addr), old_size, new_size, flags); + if (host_addr != MAP_FAILED && RESERVED_VA && old_size > new_size) { + mmap_reserve(old_addr + old_size, new_size - old_size); + } + } else { + errno = ENOMEM; + host_addr = MAP_FAILED; + } /* Check if address fits target address space */ if ((unsigned long)host_addr + new_size > (abi_ulong)-1) { /* Revert mremap() changes */ diff -Nru qemu-kvm-0.12.5+noroms/linux-user/ppc/syscall_nr.h qemu-kvm-0.14.1/linux-user/ppc/syscall_nr.h --- qemu-kvm-0.12.5+noroms/linux-user/ppc/syscall_nr.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/linux-user/ppc/syscall_nr.h 2011-05-11 13:29:46.000000000 +0000 @@ -17,15 +17,15 @@ #define TARGET_NR_time 13 #define TARGET_NR_mknod 14 #define TARGET_NR_chmod 15 -#define TARGET_NR_lchown32 16 +#define TARGET_NR_lchown 16 #define TARGET_NR_break 17 #define TARGET_NR_oldstat 18 #define TARGET_NR_lseek 19 #define TARGET_NR_getpid 20 #define TARGET_NR_mount 21 #define TARGET_NR_umount 22 -#define TARGET_NR_setuid32 23 -#define TARGET_NR_getuid32 24 +#define TARGET_NR_setuid 23 +#define TARGET_NR_getuid 24 #define TARGET_NR_stime 25 #define TARGET_NR_ptrace 26 #define TARGET_NR_alarm 27 @@ -47,11 +47,11 @@ #define TARGET_NR_times 43 #define TARGET_NR_prof 44 #define TARGET_NR_brk 45 -#define TARGET_NR_setgid32 46 -#define TARGET_NR_getgid32 47 +#define TARGET_NR_setgid 46 +#define TARGET_NR_getgid 47 #define TARGET_NR_signal 48 -#define TARGET_NR_geteuid32 49 -#define TARGET_NR_getegid32 50 +#define TARGET_NR_geteuid 49 +#define TARGET_NR_getegid 50 #define TARGET_NR_acct 51 #define TARGET_NR_umount2 52 #define TARGET_NR_lock 53 @@ -71,8 +71,8 @@ #define TARGET_NR_sigaction 67 #define TARGET_NR_sgetmask 68 #define TARGET_NR_ssetmask 69 -#define TARGET_NR_setreuid32 70 -#define TARGET_NR_setregid32 71 +#define TARGET_NR_setreuid 70 +#define TARGET_NR_setregid 71 #define TARGET_NR_sigsuspend 72 #define TARGET_NR_sigpending 73 #define TARGET_NR_sethostname 74 @@ -81,8 +81,8 @@ #define TARGET_NR_getrusage 77 #define TARGET_NR_gettimeofday 78 #define TARGET_NR_settimeofday 79 -#define TARGET_NR_getgroups32 80 -#define TARGET_NR_setgroups32 81 +#define TARGET_NR_getgroups 80 +#define TARGET_NR_setgroups 81 #define TARGET_NR_select 82 #define TARGET_NR_symlink 83 #define TARGET_NR_oldlstat 84 @@ -96,7 +96,7 @@ #define TARGET_NR_truncate 92 #define TARGET_NR_ftruncate 93 #define TARGET_NR_fchmod 94 -#define TARGET_NR_fchown32 95 +#define TARGET_NR_fchown 95 #define TARGET_NR_getpriority 96 #define TARGET_NR_setpriority 97 #define TARGET_NR_profil 98 @@ -139,8 +139,8 @@ #define TARGET_NR_sysfs 135 #define TARGET_NR_personality 136 #define TARGET_NR_afs_syscall 137 /* Syscall for Andrew File System */ -#define TARGET_NR_setfsuid32 138 -#define TARGET_NR_setfsgid32 139 +#define TARGET_NR_setfsuid 138 +#define TARGET_NR_setfsgid 139 #define TARGET_NR__llseek 140 #define TARGET_NR_getdents 141 #define TARGET_NR__newselect 142 @@ -182,7 +182,7 @@ #define TARGET_NR_rt_sigsuspend 178 #define TARGET_NR_pread64 179 #define TARGET_NR_pwrite64 180 -#define TARGET_NR_chown32 181 +#define TARGET_NR_chown 181 #define TARGET_NR_getcwd 182 #define TARGET_NR_capget 183 #define TARGET_NR_capset 184 diff -Nru qemu-kvm-0.12.5+noroms/linux-user/qemu.h qemu-kvm-0.14.1/linux-user/qemu.h --- qemu-kvm-0.12.5+noroms/linux-user/qemu.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/linux-user/qemu.h 2011-05-11 13:29:46.000000000 +0000 @@ -31,6 +31,7 @@ * task_struct fields in the kernel */ struct image_info { + abi_ulong load_bias; abi_ulong load_addr; abi_ulong start_code; abi_ulong end_code; @@ -42,13 +43,13 @@ abi_ulong mmap; abi_ulong rss; abi_ulong start_stack; + abi_ulong stack_limit; abi_ulong entry; abi_ulong code_offset; abi_ulong data_offset; abi_ulong saved_auxv; abi_ulong arg_start; abi_ulong arg_end; - char **host_argv; int personality; }; @@ -124,8 +125,6 @@ struct sigqueue sigqueue_table[MAX_SIGQUEUE_SIZE]; /* siginfo queue */ struct sigqueue *first_free; /* first free siginfo queue entry */ int signal_pending; /* non zero if a signal may be pending */ - - uint8_t stack[0]; } __attribute__((aligned(16))) TaskState; extern char *exec_path; @@ -133,9 +132,7 @@ void task_settid(TaskState *); void stop_all_tasks(void); extern const char *qemu_uname_release; -#if defined(CONFIG_USE_GUEST_BASE) extern unsigned long mmap_min_addr; -#endif /* ??? See if we can avoid exposing so much of the loader internals. */ /* @@ -145,12 +142,16 @@ */ #define MAX_ARG_PAGES 33 +/* Read a good amount of data initially, to hopefully get all the + program headers loaded. */ +#define BPRM_BUF_SIZE 1024 + /* * This structure is used to hold the arguments that are * used when loading binaries. */ struct linux_binprm { - char buf[128]; + char buf[BPRM_BUF_SIZE] __attribute__((aligned)); void *page[MAX_ARG_PAGES]; abi_ulong p; int fd; @@ -173,11 +174,6 @@ struct image_info * info); int load_flt_binary(struct linux_binprm * bprm, struct target_pt_regs * regs, struct image_info * info); -#ifdef TARGET_HAS_ELFLOAD32 -int load_elf_binary_multi(struct linux_binprm *bprm, - struct target_pt_regs *regs, - struct image_info *info); -#endif abi_long memcpy_to_target(abi_ulong dest, const void *src, unsigned long len); @@ -187,7 +183,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, abi_long arg2, abi_long arg3, abi_long arg4, abi_long arg5, abi_long arg6); -void gemu_log(const char *fmt, ...) __attribute__((format(printf,1,2))); +void gemu_log(const char *fmt, ...) GCC_FMT_ATTR(1, 2); extern THREAD CPUState *thread_env; void cpu_loop(CPUState *env); char *target_strerror(int err); @@ -248,7 +244,7 @@ #endif /* main.c */ -extern unsigned long x86_stack_size; +extern unsigned long guest_stack_size; /* user access */ @@ -267,8 +263,7 @@ */ #define __put_user(x, hptr)\ ({\ - int size = sizeof(*hptr);\ - switch(size) {\ + switch(sizeof(*hptr)) {\ case 1:\ *(uint8_t *)(hptr) = (uint8_t)(typeof(*hptr))(x);\ break;\ @@ -289,8 +284,7 @@ #define __get_user(x, hptr) \ ({\ - int size = sizeof(*hptr);\ - switch(size) {\ + switch(sizeof(*hptr)) {\ case 1:\ x = (typeof(*hptr))*(uint8_t *)(hptr);\ break;\ diff -Nru qemu-kvm-0.12.5+noroms/linux-user/signal.c qemu-kvm-0.14.1/linux-user/signal.c --- qemu-kvm-0.12.5+noroms/linux-user/signal.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/linux-user/signal.c 2011-05-11 13:29:46.000000000 +0000 @@ -366,19 +366,19 @@ } /* abort execution with signal */ -static void QEMU_NORETURN force_sig(int sig) +static void QEMU_NORETURN force_sig(int target_sig) { TaskState *ts = (TaskState *)thread_env->opaque; int host_sig, core_dumped = 0; struct sigaction act; - host_sig = target_to_host_signal(sig); - gdb_signalled(thread_env, sig); + host_sig = target_to_host_signal(target_sig); + gdb_signalled(thread_env, target_sig); /* dump core if supported by target binary format */ - if (core_dump_signal(sig) && (ts->bprm->core_dump != NULL)) { + if (core_dump_signal(target_sig) && (ts->bprm->core_dump != NULL)) { stop_all_tasks(); core_dumped = - ((*ts->bprm->core_dump)(sig, thread_env) == 0); + ((*ts->bprm->core_dump)(target_sig, thread_env) == 0); } if (core_dumped) { /* we already dumped the core of target process, we don't want @@ -388,7 +388,7 @@ nodump.rlim_cur=0; setrlimit(RLIMIT_CORE, &nodump); (void) fprintf(stderr, "qemu: uncaught target signal %d (%s) - %s\n", - sig, strsignal(host_sig), "core dumped" ); + target_sig, strsignal(host_sig), "core dumped" ); } /* The proper exit code for dieing from an uncaught signal is @@ -411,8 +411,7 @@ sigsuspend(&act.sa_mask); /* unreachable */ - assert(0); - + abort(); } /* queue a signal so that it will be send to the virtual CPU as soon @@ -1109,10 +1108,44 @@ target_stack_t tuc_stack; struct target_sigcontext tuc_mcontext; target_sigset_t tuc_sigmask; /* mask last for extensibility */ - char __unused[128 - sizeof(sigset_t)]; + char __unused[128 - sizeof(target_sigset_t)]; abi_ulong tuc_regspace[128] __attribute__((__aligned__(8))); }; +struct target_user_vfp { + uint64_t fpregs[32]; + abi_ulong fpscr; +}; + +struct target_user_vfp_exc { + abi_ulong fpexc; + abi_ulong fpinst; + abi_ulong fpinst2; +}; + +struct target_vfp_sigframe { + abi_ulong magic; + abi_ulong size; + struct target_user_vfp ufp; + struct target_user_vfp_exc ufp_exc; +} __attribute__((__aligned__(8))); + +struct target_iwmmxt_sigframe { + abi_ulong magic; + abi_ulong size; + uint64_t regs[16]; + /* Note that not all the coprocessor control registers are stored here */ + uint32_t wcssf; + uint32_t wcasf; + uint32_t wcgr0; + uint32_t wcgr1; + uint32_t wcgr2; + uint32_t wcgr3; +} __attribute__((__aligned__(8))); + +#define TARGET_VFP_MAGIC 0x56465001 +#define TARGET_IWMMXT_MAGIC 0x12ef842a + struct sigframe_v1 { struct target_sigcontext sc; @@ -1223,6 +1256,14 @@ abi_ulong handler = ka->_sa_handler; abi_ulong retcode; int thumb = handler & 1; + uint32_t cpsr = cpsr_read(env); + + cpsr &= ~CPSR_IT; + if (thumb) { + cpsr |= CPSR_T; + } else { + cpsr &= ~CPSR_T; + } if (ka->sa_flags & TARGET_SA_RESTORER) { retcode = ka->sa_restorer; @@ -1245,22 +1286,53 @@ env->regs[13] = frame_addr; env->regs[14] = retcode; env->regs[15] = handler & (thumb ? ~1 : ~3); - env->thumb = thumb; - -#if 0 -#ifdef TARGET_CONFIG_CPU_32 - env->cpsr = cpsr; -#endif -#endif + cpsr_write(env, cpsr, 0xffffffff); return 0; } +static abi_ulong *setup_sigframe_v2_vfp(abi_ulong *regspace, CPUState *env) +{ + int i; + struct target_vfp_sigframe *vfpframe; + vfpframe = (struct target_vfp_sigframe *)regspace; + __put_user(TARGET_VFP_MAGIC, &vfpframe->magic); + __put_user(sizeof(*vfpframe), &vfpframe->size); + for (i = 0; i < 32; i++) { + __put_user(env->vfp.regs[i], &vfpframe->ufp.fpregs[i]); + } + __put_user(vfp_get_fpscr(env), &vfpframe->ufp.fpscr); + __put_user(env->vfp.xregs[ARM_VFP_FPEXC], &vfpframe->ufp_exc.fpexc); + __put_user(env->vfp.xregs[ARM_VFP_FPINST], &vfpframe->ufp_exc.fpinst); + __put_user(env->vfp.xregs[ARM_VFP_FPINST2], &vfpframe->ufp_exc.fpinst2); + return (abi_ulong*)(vfpframe+1); +} + +static abi_ulong *setup_sigframe_v2_iwmmxt(abi_ulong *regspace, CPUState *env) +{ + int i; + struct target_iwmmxt_sigframe *iwmmxtframe; + iwmmxtframe = (struct target_iwmmxt_sigframe *)regspace; + __put_user(TARGET_IWMMXT_MAGIC, &iwmmxtframe->magic); + __put_user(sizeof(*iwmmxtframe), &iwmmxtframe->size); + for (i = 0; i < 16; i++) { + __put_user(env->iwmmxt.regs[i], &iwmmxtframe->regs[i]); + } + __put_user(env->vfp.xregs[ARM_IWMMXT_wCSSF], &iwmmxtframe->wcssf); + __put_user(env->vfp.xregs[ARM_IWMMXT_wCASF], &iwmmxtframe->wcssf); + __put_user(env->vfp.xregs[ARM_IWMMXT_wCGR0], &iwmmxtframe->wcgr0); + __put_user(env->vfp.xregs[ARM_IWMMXT_wCGR1], &iwmmxtframe->wcgr1); + __put_user(env->vfp.xregs[ARM_IWMMXT_wCGR2], &iwmmxtframe->wcgr2); + __put_user(env->vfp.xregs[ARM_IWMMXT_wCGR3], &iwmmxtframe->wcgr3); + return (abi_ulong*)(iwmmxtframe+1); +} + static void setup_sigframe_v2(struct target_ucontext_v2 *uc, target_sigset_t *set, CPUState *env) { struct target_sigaltstack stack; int i; + abi_ulong *regspace; /* Clear all the bits of the ucontext we don't use. */ memset(uc, 0, offsetof(struct target_ucontext_v2, tuc_mcontext)); @@ -1272,7 +1344,18 @@ memcpy(&uc->tuc_stack, &stack, sizeof(stack)); setup_sigcontext(&uc->tuc_mcontext, env, set->sig[0]); - /* FIXME: Save coprocessor signal frame. */ + /* Save coprocessor signal frame. */ + regspace = uc->tuc_regspace; + if (arm_feature(env, ARM_FEATURE_VFP)) { + regspace = setup_sigframe_v2_vfp(regspace, env); + } + if (arm_feature(env, ARM_FEATURE_IWMMXT)) { + regspace = setup_sigframe_v2_iwmmxt(regspace, env); + } + + /* Write terminating magic word */ + __put_user(0, regspace); + for(i = 0; i < TARGET_NSIG_WORDS; i++) { __put_user(set->sig[i], &uc->tuc_sigmask.sig[i]); } @@ -1487,14 +1570,69 @@ badframe: unlock_user_struct(frame, frame_addr, 0); - force_sig(SIGSEGV /* , current */); + force_sig(TARGET_SIGSEGV /* , current */); return 0; } +static abi_ulong *restore_sigframe_v2_vfp(CPUState *env, abi_ulong *regspace) +{ + int i; + abi_ulong magic, sz; + uint32_t fpscr, fpexc; + struct target_vfp_sigframe *vfpframe; + vfpframe = (struct target_vfp_sigframe *)regspace; + + __get_user(magic, &vfpframe->magic); + __get_user(sz, &vfpframe->size); + if (magic != TARGET_VFP_MAGIC || sz != sizeof(*vfpframe)) { + return 0; + } + for (i = 0; i < 32; i++) { + __get_user(env->vfp.regs[i], &vfpframe->ufp.fpregs[i]); + } + __get_user(fpscr, &vfpframe->ufp.fpscr); + vfp_set_fpscr(env, fpscr); + __get_user(fpexc, &vfpframe->ufp_exc.fpexc); + /* Sanitise FPEXC: ensure VFP is enabled, FPINST2 is invalid + * and the exception flag is cleared + */ + fpexc |= (1 << 30); + fpexc &= ~((1 << 31) | (1 << 28)); + env->vfp.xregs[ARM_VFP_FPEXC] = fpexc; + __get_user(env->vfp.xregs[ARM_VFP_FPINST], &vfpframe->ufp_exc.fpinst); + __get_user(env->vfp.xregs[ARM_VFP_FPINST2], &vfpframe->ufp_exc.fpinst2); + return (abi_ulong*)(vfpframe + 1); +} + +static abi_ulong *restore_sigframe_v2_iwmmxt(CPUState *env, abi_ulong *regspace) +{ + int i; + abi_ulong magic, sz; + struct target_iwmmxt_sigframe *iwmmxtframe; + iwmmxtframe = (struct target_iwmmxt_sigframe *)regspace; + + __get_user(magic, &iwmmxtframe->magic); + __get_user(sz, &iwmmxtframe->size); + if (magic != TARGET_IWMMXT_MAGIC || sz != sizeof(*iwmmxtframe)) { + return 0; + } + for (i = 0; i < 16; i++) { + __get_user(env->iwmmxt.regs[i], &iwmmxtframe->regs[i]); + } + __get_user(env->vfp.xregs[ARM_IWMMXT_wCSSF], &iwmmxtframe->wcssf); + __get_user(env->vfp.xregs[ARM_IWMMXT_wCASF], &iwmmxtframe->wcssf); + __get_user(env->vfp.xregs[ARM_IWMMXT_wCGR0], &iwmmxtframe->wcgr0); + __get_user(env->vfp.xregs[ARM_IWMMXT_wCGR1], &iwmmxtframe->wcgr1); + __get_user(env->vfp.xregs[ARM_IWMMXT_wCGR2], &iwmmxtframe->wcgr2); + __get_user(env->vfp.xregs[ARM_IWMMXT_wCGR3], &iwmmxtframe->wcgr3); + return (abi_ulong*)(iwmmxtframe + 1); +} + static int do_sigframe_return_v2(CPUState *env, target_ulong frame_addr, struct target_ucontext_v2 *uc) { sigset_t host_set; + abi_ulong *regspace; target_to_host_sigset(&host_set, &uc->tuc_sigmask); sigprocmask(SIG_SETMASK, &host_set, NULL); @@ -1502,6 +1640,21 @@ if (restore_sigcontext(env, &uc->tuc_mcontext)) return 1; + /* Restore coprocessor signal frame */ + regspace = uc->tuc_regspace; + if (arm_feature(env, ARM_FEATURE_VFP)) { + regspace = restore_sigframe_v2_vfp(env, regspace); + if (!regspace) { + return 1; + } + } + if (arm_feature(env, ARM_FEATURE_IWMMXT)) { + regspace = restore_sigframe_v2_iwmmxt(env, regspace); + if (!regspace) { + return 1; + } + } + if (do_sigaltstack(frame_addr + offsetof(struct target_ucontext_v2, tuc_stack), 0, get_sp_from_cpustate(env)) == -EFAULT) return 1; @@ -1539,7 +1692,7 @@ badframe: unlock_user_struct(frame, frame_addr, 0); - force_sig(SIGSEGV /* , current */); + force_sig(TARGET_SIGSEGV /* , current */); return 0; } @@ -1589,7 +1742,7 @@ badframe: unlock_user_struct(frame, frame_addr, 0); - force_sig(SIGSEGV /* , current */); + force_sig(TARGET_SIGSEGV /* , current */); return 0; } @@ -1618,7 +1771,7 @@ badframe: unlock_user_struct(frame, frame_addr, 0); - force_sig(SIGSEGV /* , current */); + force_sig(TARGET_SIGSEGV /* , current */); return 0; } @@ -1664,9 +1817,10 @@ /* A Sparc stack frame */ struct sparc_stackf { abi_ulong locals[8]; - abi_ulong ins[6]; - struct sparc_stackf *fp; - abi_ulong callers_pc; + abi_ulong ins[8]; + /* It's simpler to treat fp and callers_pc as elements of ins[] + * since we never need to access them ourselves. + */ char *structptr; abi_ulong xargs[6]; abi_ulong xxargs[1]; @@ -2053,10 +2207,10 @@ } target_mcontext_t; struct target_ucontext { - struct target_ucontext *uc_link; - abi_ulong uc_flags; - target_sigset_t uc_sigmask; - target_mcontext_t uc_mcontext; + struct target_ucontext *tuc_link; + abi_ulong tuc_flags; + target_sigset_t tuc_sigmask; + target_mcontext_t tuc_mcontext; }; /* A V9 register window */ @@ -2082,7 +2236,7 @@ ucp_addr = env->regwptr[UREG_I0]; if (!lock_user_struct(VERIFY_READ, ucp, ucp_addr, 1)) goto do_sigsegv; - grp = &ucp->uc_mcontext.mc_gregs; + grp = &ucp->tuc_mcontext.mc_gregs; err = __get_user(pc, &((*grp)[MC_PC])); err |= __get_user(npc, &((*grp)[MC_NPC])); if (err || ((pc | npc) & 3)) @@ -2092,11 +2246,11 @@ sigset_t set; if (TARGET_NSIG_WORDS == 1) { - if (__get_user(target_set.sig[0], &ucp->uc_sigmask.sig[0])) + if (__get_user(target_set.sig[0], &ucp->tuc_sigmask.sig[0])) goto do_sigsegv; } else { abi_ulong *src, *dst; - src = ucp->uc_sigmask.sig; + src = ucp->tuc_sigmask.sig; dst = target_set.sig; for (i = 0; i < sizeof(target_sigset_t) / sizeof(abi_ulong); i++, dst++, src++) @@ -2112,8 +2266,8 @@ err |= __get_user(env->y, &((*grp)[MC_Y])); err |= __get_user(tstate, &((*grp)[MC_TSTATE])); env->asi = (tstate >> 24) & 0xff; - PUT_CCR(env, tstate >> 32); - PUT_CWP64(env, tstate & 0x1f); + cpu_put_ccr(env, tstate >> 32); + cpu_put_cwp64(env, tstate & 0x1f); err |= __get_user(env->gregs[1], (&(*grp)[MC_G1])); err |= __get_user(env->gregs[2], (&(*grp)[MC_G2])); err |= __get_user(env->gregs[3], (&(*grp)[MC_G3])); @@ -2130,8 +2284,8 @@ err |= __get_user(env->regwptr[UREG_I6], (&(*grp)[MC_O6])); err |= __get_user(env->regwptr[UREG_I7], (&(*grp)[MC_O7])); - err |= __get_user(fp, &(ucp->uc_mcontext.mc_fp)); - err |= __get_user(i7, &(ucp->uc_mcontext.mc_i7)); + err |= __get_user(fp, &(ucp->tuc_mcontext.mc_fp)); + err |= __get_user(i7, &(ucp->tuc_mcontext.mc_i7)); w_addr = TARGET_STACK_BIAS+env->regwptr[UREG_I6]; if (put_user(fp, w_addr + offsetof(struct target_reg_window, ins[6]), @@ -2140,27 +2294,27 @@ if (put_user(i7, w_addr + offsetof(struct target_reg_window, ins[7]), abi_ulong) != 0) goto do_sigsegv; - err |= __get_user(fenab, &(ucp->uc_mcontext.mc_fpregs.mcfpu_enab)); - err |= __get_user(env->fprs, &(ucp->uc_mcontext.mc_fpregs.mcfpu_fprs)); + err |= __get_user(fenab, &(ucp->tuc_mcontext.mc_fpregs.mcfpu_enab)); + err |= __get_user(env->fprs, &(ucp->tuc_mcontext.mc_fpregs.mcfpu_fprs)); { uint32_t *src, *dst; - src = ucp->uc_mcontext.mc_fpregs.mcfpu_fregs.sregs; + src = ucp->tuc_mcontext.mc_fpregs.mcfpu_fregs.sregs; dst = env->fpr; /* XXX: check that the CPU storage is the same as user context */ for (i = 0; i < 64; i++, dst++, src++) err |= __get_user(*dst, src); } err |= __get_user(env->fsr, - &(ucp->uc_mcontext.mc_fpregs.mcfpu_fsr)); + &(ucp->tuc_mcontext.mc_fpregs.mcfpu_fsr)); err |= __get_user(env->gsr, - &(ucp->uc_mcontext.mc_fpregs.mcfpu_gsr)); + &(ucp->tuc_mcontext.mc_fpregs.mcfpu_gsr)); if (err) goto do_sigsegv; unlock_user_struct(ucp, ucp_addr, 0); return; do_sigsegv: unlock_user_struct(ucp, ucp_addr, 0); - force_sig(SIGSEGV); + force_sig(TARGET_SIGSEGV); } void sparc64_get_context(CPUSPARCState *env) @@ -2179,7 +2333,7 @@ if (!lock_user_struct(VERIFY_WRITE, ucp, ucp_addr, 0)) goto do_sigsegv; - mcp = &ucp->uc_mcontext; + mcp = &ucp->tuc_mcontext; grp = &mcp->mc_gregs; /* Skip over the trap instruction, first. */ @@ -2192,11 +2346,11 @@ host_to_target_sigset_internal(&target_set, &set); if (TARGET_NSIG_WORDS == 1) { err |= __put_user(target_set.sig[0], - (abi_ulong *)&ucp->uc_sigmask); + (abi_ulong *)&ucp->tuc_sigmask); } else { abi_ulong *src, *dst; src = target_set.sig; - dst = ucp->uc_sigmask.sig; + dst = ucp->tuc_sigmask.sig; for (i = 0; i < sizeof(target_sigset_t) / sizeof(abi_ulong); i++, dst++, src++) err |= __put_user(*src, dst); @@ -2239,7 +2393,7 @@ { uint32_t *src, *dst; src = env->fpr; - dst = ucp->uc_mcontext.mc_fpregs.mcfpu_fregs.sregs; + dst = ucp->tuc_mcontext.mc_fpregs.mcfpu_fregs.sregs; /* XXX: check that the CPU storage is the same as user context */ for (i = 0; i < 64; i++, dst++, src++) err |= __put_user(*src, dst); @@ -2254,7 +2408,7 @@ return; do_sigsegv: unlock_user_struct(ucp, ucp_addr, 1); - force_sig(SIGSEGV); + force_sig(TARGET_SIGSEGV); } #endif #elif defined(TARGET_ABI_MIPSN64) @@ -2347,12 +2501,12 @@ }; struct target_ucontext { - target_ulong uc_flags; - target_ulong uc_link; - target_stack_t uc_stack; + target_ulong tuc_flags; + target_ulong tuc_link; + target_stack_t tuc_stack; target_ulong pad0; - struct target_sigcontext uc_mcontext; - target_sigset_t uc_sigmask; + struct target_sigcontext tuc_mcontext; + target_sigset_t tuc_sigmask; }; struct target_rt_sigframe { @@ -2664,17 +2818,17 @@ copy_siginfo_to_user(&frame->rs_info, info); - __put_user(0, &frame->rs_uc.uc_flags); - __put_user(0, &frame->rs_uc.uc_link); - __put_user(target_sigaltstack_used.ss_sp, &frame->rs_uc.uc_stack.ss_sp); - __put_user(target_sigaltstack_used.ss_size, &frame->rs_uc.uc_stack.ss_size); + __put_user(0, &frame->rs_uc.tuc_flags); + __put_user(0, &frame->rs_uc.tuc_link); + __put_user(target_sigaltstack_used.ss_sp, &frame->rs_uc.tuc_stack.ss_sp); + __put_user(target_sigaltstack_used.ss_size, &frame->rs_uc.tuc_stack.ss_size); __put_user(sas_ss_flags(get_sp_from_cpustate(env)), - &frame->rs_uc.uc_stack.ss_flags); + &frame->rs_uc.tuc_stack.ss_flags); - setup_sigcontext(env, &frame->rs_uc.uc_mcontext); + setup_sigcontext(env, &frame->rs_uc.tuc_mcontext); for(i = 0; i < TARGET_NSIG_WORDS; i++) { - __put_user(set->sig[i], &frame->rs_uc.uc_sigmask.sig[i]); + __put_user(set->sig[i], &frame->rs_uc.tuc_sigmask.sig[i]); } /* @@ -2721,14 +2875,14 @@ if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) goto badframe; - target_to_host_sigset(&blocked, &frame->rs_uc.uc_sigmask); + target_to_host_sigset(&blocked, &frame->rs_uc.tuc_sigmask); sigprocmask(SIG_SETMASK, &blocked, NULL); - if (restore_sigcontext(env, &frame->rs_uc.uc_mcontext)) + if (restore_sigcontext(env, &frame->rs_uc.tuc_mcontext)) goto badframe; if (do_sigaltstack(frame_addr + - offsetof(struct target_rt_sigframe, rs_uc.uc_stack), + offsetof(struct target_rt_sigframe, rs_uc.tuc_stack), 0, get_sp_from_cpustate(env)) == -EFAULT) goto badframe; @@ -2780,11 +2934,11 @@ struct target_ucontext { - target_ulong uc_flags; - struct target_ucontext *uc_link; - target_stack_t uc_stack; - struct target_sigcontext uc_mcontext; - target_sigset_t uc_sigmask; /* mask last for extensibility */ + target_ulong tuc_flags; + struct target_ucontext *tuc_link; + target_stack_t tuc_stack; + struct target_sigcontext tuc_mcontext; + target_sigset_t tuc_sigmask; /* mask last for extensibility */ }; struct target_rt_sigframe @@ -2812,6 +2966,7 @@ CPUState *regs, unsigned long mask) { int err = 0; + int i; #define COPY(x) err |= __put_user(regs->x, &sc->sc_##x) COPY(gregs[0]); COPY(gregs[1]); @@ -2827,7 +2982,11 @@ COPY(sr); COPY(pc); #undef COPY - /* todo: save FPU registers here */ + for (i=0; i<16; i++) { + err |= __put_user(regs->fregs[i], &sc->sc_fpregs[i]); + } + err |= __put_user(regs->fpscr, &sc->sc_fpscr); + err |= __put_user(regs->fpul, &sc->sc_fpul); /* non-iBCS2 extensions.. */ err |= __put_user(mask, &sc->oldmask); @@ -2835,10 +2994,11 @@ return err; } -static int restore_sigcontext(CPUState *regs, - struct target_sigcontext *sc) +static int restore_sigcontext(CPUState *regs, struct target_sigcontext *sc, + target_ulong *r0_p) { unsigned int err = 0; + int i; #define COPY(x) err |= __get_user(regs->x, &sc->sc_##x) COPY(gregs[1]); @@ -2854,9 +3014,14 @@ COPY(sr); COPY(pc); #undef COPY - /* todo: restore FPU registers here */ + for (i=0; i<16; i++) { + err |= __get_user(regs->fregs[i], &sc->sc_fpregs[i]); + } + err |= __get_user(regs->fpscr, &sc->sc_fpscr); + err |= __get_user(regs->fpul, &sc->sc_fpul); regs->tra = -1; /* disable syscall checks */ + err |= __get_user(*r0_p, &sc->sc_gregs[0]); return err; } @@ -2908,7 +3073,7 @@ give_sigsegv: unlock_user_struct(frame, frame_addr, 1); - force_sig(SIGSEGV); + force_sig(TARGET_SIGSEGV); } static void setup_rt_frame(int sig, struct target_sigaction *ka, @@ -2930,18 +3095,18 @@ err |= copy_siginfo_to_user(&frame->info, info); /* Create the ucontext. */ - err |= __put_user(0, &frame->uc.uc_flags); - err |= __put_user(0, (unsigned long *)&frame->uc.uc_link); + err |= __put_user(0, &frame->uc.tuc_flags); + err |= __put_user(0, (unsigned long *)&frame->uc.tuc_link); err |= __put_user((unsigned long)target_sigaltstack_used.ss_sp, - &frame->uc.uc_stack.ss_sp); + &frame->uc.tuc_stack.ss_sp); err |= __put_user(sas_ss_flags(regs->gregs[15]), - &frame->uc.uc_stack.ss_flags); + &frame->uc.tuc_stack.ss_flags); err |= __put_user(target_sigaltstack_used.ss_size, - &frame->uc.uc_stack.ss_size); - err |= setup_sigcontext(&frame->uc.uc_mcontext, + &frame->uc.tuc_stack.ss_size); + err |= setup_sigcontext(&frame->uc.tuc_mcontext, regs, set->sig[0]); for(i = 0; i < TARGET_NSIG_WORDS; i++) { - err |= __put_user(set->sig[i], &frame->uc.uc_sigmask.sig[i]); + err |= __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]); } /* Set up to return from userspace. If provided, use a stub @@ -2971,7 +3136,7 @@ give_sigsegv: unlock_user_struct(frame, frame_addr, 1); - force_sig(SIGSEGV); + force_sig(TARGET_SIGSEGV); } long do_sigreturn(CPUState *regs) @@ -2980,6 +3145,7 @@ abi_ulong frame_addr; sigset_t blocked; target_sigset_t target_set; + target_ulong r0; int i; int err = 0; @@ -3001,11 +3167,11 @@ target_to_host_sigset_internal(&blocked, &target_set); sigprocmask(SIG_SETMASK, &blocked, NULL); - if (restore_sigcontext(regs, &frame->sc)) + if (restore_sigcontext(regs, &frame->sc, &r0)) goto badframe; unlock_user_struct(frame, frame_addr, 0); - return regs->gregs[0]; + return r0; badframe: unlock_user_struct(frame, frame_addr, 0); @@ -3018,6 +3184,7 @@ struct target_rt_sigframe *frame; abi_ulong frame_addr; sigset_t blocked; + target_ulong r0; #if defined(DEBUG_SIGNAL) fprintf(stderr, "do_rt_sigreturn\n"); @@ -3026,19 +3193,19 @@ if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) goto badframe; - target_to_host_sigset(&blocked, &frame->uc.uc_sigmask); + target_to_host_sigset(&blocked, &frame->uc.tuc_sigmask); sigprocmask(SIG_SETMASK, &blocked, NULL); - if (restore_sigcontext(regs, &frame->uc.uc_mcontext)) + if (restore_sigcontext(regs, &frame->uc.tuc_mcontext, &r0)) goto badframe; if (do_sigaltstack(frame_addr + - offsetof(struct target_rt_sigframe, uc.uc_stack), + offsetof(struct target_rt_sigframe, uc.tuc_stack), 0, get_sp_from_cpustate(regs)) == -EFAULT) goto badframe; unlock_user_struct(frame, frame_addr, 0); - return regs->gregs[0]; + return r0; badframe: unlock_user_struct(frame, frame_addr, 0); @@ -3052,9 +3219,23 @@ uint32_t oldmask; }; +struct target_stack_t { + abi_ulong ss_sp; + int ss_flags; + unsigned int ss_size; +}; + +struct target_ucontext { + abi_ulong tuc_flags; + abi_ulong tuc_link; + struct target_stack_t tuc_stack; + struct target_sigcontext tuc_mcontext; + uint32_t tuc_extramask[TARGET_NSIG_WORDS - 1]; +}; + /* Signal frames. */ struct target_signal_frame { - struct target_sigcontext sc; + struct target_ucontext uc; uint32_t extramask[TARGET_NSIG_WORDS - 1]; uint32_t tramp[2]; }; @@ -3163,7 +3344,7 @@ goto badframe; /* Save the mask. */ - err |= __put_user(set->sig[0], &frame->sc.oldmask); + err |= __put_user(set->sig[0], &frame->uc.tuc_mcontext.oldmask); if (err) goto badframe; @@ -3172,7 +3353,7 @@ goto badframe; } - setup_sigcontext(&frame->sc, env); + setup_sigcontext(&frame->uc.tuc_mcontext, env); /* Set up to return from userspace. If provided, use a stub already in userspace. */ @@ -3201,7 +3382,8 @@ env->regs[1] = (unsigned long) frame; /* Signal handler args: */ env->regs[5] = sig; /* Arg 0: signum */ - env->regs[6] = (unsigned long) &frame->sc; /* arg 1: sigcontext */ + env->regs[6] = 0; + env->regs[7] = (unsigned long) &frame->uc; /* arg 1: sigcontext */ /* Offset of 4 to handle microblaze rtid r14, 0 */ env->sregs[SR_PC] = (unsigned long)ka->_sa_handler; @@ -3234,7 +3416,7 @@ goto badframe; /* Restore blocked signals */ - if (__get_user(target_set.sig[0], &frame->sc.oldmask)) + if (__get_user(target_set.sig[0], &frame->uc.tuc_mcontext.oldmask)) goto badframe; for(i = 1; i < TARGET_NSIG_WORDS; i++) { if (__get_user(target_set.sig[i], &frame->extramask[i - 1])) @@ -3243,7 +3425,7 @@ target_to_host_sigset_internal(&set, &target_set); sigprocmask(SIG_SETMASK, &set, NULL); - restore_sigcontext(&frame->sc, env); + restore_sigcontext(&frame->uc.tuc_mcontext, env); /* We got here through a sigreturn syscall, our path back is via an rtb insn so setup r14 for that. */ env->regs[14] = env->sregs[SR_PC]; @@ -3543,22 +3725,22 @@ }; struct target_ucontext { - target_ulong uc_flags; - target_ulong uc_link; /* struct ucontext __user * */ - struct target_sigaltstack uc_stack; + target_ulong tuc_flags; + target_ulong tuc_link; /* struct ucontext __user * */ + struct target_sigaltstack tuc_stack; #if !defined(TARGET_PPC64) - int32_t uc_pad[7]; - target_ulong uc_regs; /* struct mcontext __user * + int32_t tuc_pad[7]; + target_ulong tuc_regs; /* struct mcontext __user * points to uc_mcontext field */ #endif - target_sigset_t uc_sigmask; + target_sigset_t tuc_sigmask; #if defined(TARGET_PPC64) target_sigset_t unused[15]; /* Allow for uc_sigmask growth */ - struct target_sigcontext uc_mcontext; + struct target_sigcontext tuc_mcontext; #else - int32_t uc_maskext[30]; - int32_t uc_pad2[3]; - struct target_mcontext uc_mcontext; + int32_t tuc_maskext[30]; + int32_t tuc_pad2[3]; + struct target_mcontext tuc_mcontext; #endif }; @@ -3850,7 +4032,7 @@ unlock_user_struct(frame, frame_addr, 1); if (logfile) fprintf (logfile, "segfaulting from setup_frame\n"); - force_sig(SIGSEGV); + force_sig(TARGET_SIGSEGV); } static void setup_rt_frame(int sig, struct target_sigaction *ka, @@ -3871,21 +4053,21 @@ err |= copy_siginfo_to_user(&rt_sf->info, info); - err |= __put_user(0, &rt_sf->uc.uc_flags); - err |= __put_user(0, &rt_sf->uc.uc_link); + err |= __put_user(0, &rt_sf->uc.tuc_flags); + err |= __put_user(0, &rt_sf->uc.tuc_link); err |= __put_user((target_ulong)target_sigaltstack_used.ss_sp, - &rt_sf->uc.uc_stack.ss_sp); + &rt_sf->uc.tuc_stack.ss_sp); err |= __put_user(sas_ss_flags(env->gpr[1]), - &rt_sf->uc.uc_stack.ss_flags); + &rt_sf->uc.tuc_stack.ss_flags); err |= __put_user(target_sigaltstack_used.ss_size, - &rt_sf->uc.uc_stack.ss_size); - err |= __put_user(h2g (&rt_sf->uc.uc_mcontext), - &rt_sf->uc.uc_regs); + &rt_sf->uc.tuc_stack.ss_size); + err |= __put_user(h2g (&rt_sf->uc.tuc_mcontext), + &rt_sf->uc.tuc_regs); for(i = 0; i < TARGET_NSIG_WORDS; i++) { - err |= __put_user(set->sig[i], &rt_sf->uc.uc_sigmask.sig[i]); + err |= __put_user(set->sig[i], &rt_sf->uc.tuc_sigmask.sig[i]); } - frame = &rt_sf->uc.uc_mcontext; + frame = &rt_sf->uc.tuc_mcontext; err |= save_user_regs(env, frame, TARGET_NR_rt_sigreturn); /* The kernel checks for the presence of a VDSO here. We don't @@ -3919,7 +4101,7 @@ unlock_user_struct(rt_sf, rt_sf_addr, 1); if (logfile) fprintf (logfile, "segfaulting from setup_rt_frame\n"); - force_sig(SIGSEGV); + force_sig(TARGET_SIGSEGV); } @@ -3961,7 +4143,7 @@ unlock_user_struct(sc, sc_addr, 1); if (logfile) fprintf (logfile, "segfaulting from do_sigreturn\n"); - force_sig(SIGSEGV); + force_sig(TARGET_SIGSEGV); return 0; } @@ -3973,7 +4155,7 @@ sigset_t blocked; target_sigset_t set; - if (copy_from_user(&set, h2g(ucp) + offsetof(struct target_ucontext, uc_sigmask), + if (copy_from_user(&set, h2g(ucp) + offsetof(struct target_ucontext, tuc_sigmask), sizeof (set))) return 1; @@ -3981,7 +4163,7 @@ fprintf (stderr, "do_setcontext: not implemented\n"); return 0; #else - if (__get_user(mcp_addr, &ucp->uc_regs)) + if (__get_user(mcp_addr, &ucp->tuc_regs)) return 1; if (!lock_user_struct(VERIFY_READ, mcp, mcp_addr, 1)) @@ -4014,7 +4196,7 @@ goto sigsegv; do_sigaltstack(rt_sf_addr - + offsetof(struct target_rt_sigframe, uc.uc_stack), + + offsetof(struct target_rt_sigframe, uc.tuc_stack), 0, env->gpr[1]); unlock_user_struct(rt_sf, rt_sf_addr, 1); @@ -4024,7 +4206,7 @@ unlock_user_struct(rt_sf, rt_sf_addr, 1); if (logfile) fprintf (logfile, "segfaulting from do_rt_sigreturn\n"); - force_sig(SIGSEGV); + force_sig(TARGET_SIGSEGV); return 0; } @@ -4070,12 +4252,12 @@ #define TARGET_MCONTEXT_VERSION 2 struct target_ucontext { - abi_ulong uc_flags; - abi_ulong uc_link; - target_stack_t uc_stack; - struct target_mcontext uc_mcontext; - abi_long uc_filler[80]; - target_sigset_t uc_sigmask; + abi_ulong tuc_flags; + abi_ulong tuc_link; + target_stack_t tuc_stack; + struct target_mcontext tuc_mcontext; + abi_long tuc_filler[80]; + target_sigset_t tuc_sigmask; }; struct target_rt_sigframe @@ -4194,16 +4376,16 @@ give_sigsegv: unlock_user_struct(frame, frame_addr, 1); - force_sig(SIGSEGV); + force_sig(TARGET_SIGSEGV); } static inline int target_rt_setup_ucontext(struct target_ucontext *uc, CPUState *env) { - target_greg_t *gregs = uc->uc_mcontext.gregs; + target_greg_t *gregs = uc->tuc_mcontext.gregs; int err; - err = __put_user(TARGET_MCONTEXT_VERSION, &uc->uc_mcontext.version); + err = __put_user(TARGET_MCONTEXT_VERSION, &uc->tuc_mcontext.version); err |= __put_user(env->dregs[0], &gregs[0]); err |= __put_user(env->dregs[1], &gregs[1]); err |= __put_user(env->dregs[2], &gregs[2]); @@ -4232,9 +4414,9 @@ { int temp; int err; - target_greg_t *gregs = uc->uc_mcontext.gregs; + target_greg_t *gregs = uc->tuc_mcontext.gregs; - err = __get_user(temp, &uc->uc_mcontext.version); + err = __get_user(temp, &uc->tuc_mcontext.version); if (temp != TARGET_MCONTEXT_VERSION) goto badframe; @@ -4294,21 +4476,21 @@ /* Create the ucontext */ - err |= __put_user(0, &frame->uc.uc_flags); - err |= __put_user(0, &frame->uc.uc_link); + err |= __put_user(0, &frame->uc.tuc_flags); + err |= __put_user(0, &frame->uc.tuc_link); err |= __put_user(target_sigaltstack_used.ss_sp, - &frame->uc.uc_stack.ss_sp); + &frame->uc.tuc_stack.ss_sp); err |= __put_user(sas_ss_flags(env->aregs[7]), - &frame->uc.uc_stack.ss_flags); + &frame->uc.tuc_stack.ss_flags); err |= __put_user(target_sigaltstack_used.ss_size, - &frame->uc.uc_stack.ss_size); + &frame->uc.tuc_stack.ss_size); err |= target_rt_setup_ucontext(&frame->uc, env); if (err) goto give_sigsegv; for(i = 0; i < TARGET_NSIG_WORDS; i++) { - if (__put_user(set->sig[i], &frame->uc.uc_sigmask.sig[i])) + if (__put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i])) goto give_sigsegv; } @@ -4336,7 +4518,7 @@ give_sigsegv: unlock_user_struct(frame, frame_addr, 1); - force_sig(SIGSEGV); + force_sig(TARGET_SIGSEGV); } long do_sigreturn(CPUState *env) @@ -4397,7 +4579,7 @@ goto badframe; if (do_sigaltstack(frame_addr + - offsetof(struct target_rt_sigframe, uc.uc_stack), + offsetof(struct target_rt_sigframe, uc.tuc_stack), 0, get_sp_from_cpustate(env)) == -EFAULT) goto badframe; @@ -4410,6 +4592,273 @@ return 0; } +#elif defined(TARGET_ALPHA) + +struct target_sigcontext { + abi_long sc_onstack; + abi_long sc_mask; + abi_long sc_pc; + abi_long sc_ps; + abi_long sc_regs[32]; + abi_long sc_ownedfp; + abi_long sc_fpregs[32]; + abi_ulong sc_fpcr; + abi_ulong sc_fp_control; + abi_ulong sc_reserved1; + abi_ulong sc_reserved2; + abi_ulong sc_ssize; + abi_ulong sc_sbase; + abi_ulong sc_traparg_a0; + abi_ulong sc_traparg_a1; + abi_ulong sc_traparg_a2; + abi_ulong sc_fp_trap_pc; + abi_ulong sc_fp_trigger_sum; + abi_ulong sc_fp_trigger_inst; +}; + +struct target_ucontext { + abi_ulong tuc_flags; + abi_ulong tuc_link; + abi_ulong tuc_osf_sigmask; + target_stack_t tuc_stack; + struct target_sigcontext tuc_mcontext; + target_sigset_t tuc_sigmask; +}; + +struct target_sigframe { + struct target_sigcontext sc; + unsigned int retcode[3]; +}; + +struct target_rt_sigframe { + target_siginfo_t info; + struct target_ucontext uc; + unsigned int retcode[3]; +}; + +#define INSN_MOV_R30_R16 0x47fe0410 +#define INSN_LDI_R0 0x201f0000 +#define INSN_CALLSYS 0x00000083 + +static int setup_sigcontext(struct target_sigcontext *sc, CPUState *env, + abi_ulong frame_addr, target_sigset_t *set) +{ + int i, err = 0; + + err |= __put_user(on_sig_stack(frame_addr), &sc->sc_onstack); + err |= __put_user(set->sig[0], &sc->sc_mask); + err |= __put_user(env->pc, &sc->sc_pc); + err |= __put_user(8, &sc->sc_ps); + + for (i = 0; i < 31; ++i) { + err |= __put_user(env->ir[i], &sc->sc_regs[i]); + } + err |= __put_user(0, &sc->sc_regs[31]); + + for (i = 0; i < 31; ++i) { + err |= __put_user(env->fir[i], &sc->sc_fpregs[i]); + } + err |= __put_user(0, &sc->sc_fpregs[31]); + err |= __put_user(cpu_alpha_load_fpcr(env), &sc->sc_fpcr); + + err |= __put_user(0, &sc->sc_traparg_a0); /* FIXME */ + err |= __put_user(0, &sc->sc_traparg_a1); /* FIXME */ + err |= __put_user(0, &sc->sc_traparg_a2); /* FIXME */ + + return err; +} + +static int restore_sigcontext(CPUState *env, struct target_sigcontext *sc) +{ + uint64_t fpcr; + int i, err = 0; + + err |= __get_user(env->pc, &sc->sc_pc); + + for (i = 0; i < 31; ++i) { + err |= __get_user(env->ir[i], &sc->sc_regs[i]); + } + for (i = 0; i < 31; ++i) { + err |= __get_user(env->fir[i], &sc->sc_fpregs[i]); + } + + err |= __get_user(fpcr, &sc->sc_fpcr); + cpu_alpha_store_fpcr(env, fpcr); + + return err; +} + +static inline abi_ulong get_sigframe(struct target_sigaction *sa, + CPUState *env, unsigned long framesize) +{ + abi_ulong sp = env->ir[IR_SP]; + + /* This is the X/Open sanctioned signal stack switching. */ + if ((sa->sa_flags & TARGET_SA_ONSTACK) != 0 && !sas_ss_flags(sp)) { + sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size; + } + return (sp - framesize) & -32; +} + +static void setup_frame(int sig, struct target_sigaction *ka, + target_sigset_t *set, CPUState *env) +{ + abi_ulong frame_addr, r26; + struct target_sigframe *frame; + int err = 0; + + frame_addr = get_sigframe(ka, env, sizeof(*frame)); + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { + goto give_sigsegv; + } + + err |= setup_sigcontext(&frame->sc, env, frame_addr, set); + + if (ka->sa_restorer) { + r26 = ka->sa_restorer; + } else { + err |= __put_user(INSN_MOV_R30_R16, &frame->retcode[0]); + err |= __put_user(INSN_LDI_R0 + TARGET_NR_sigreturn, + &frame->retcode[1]); + err |= __put_user(INSN_CALLSYS, &frame->retcode[2]); + /* imb() */ + r26 = frame_addr; + } + + unlock_user_struct(frame, frame_addr, 1); + + if (err) { + give_sigsegv: + if (sig == TARGET_SIGSEGV) { + ka->_sa_handler = TARGET_SIG_DFL; + } + force_sig(TARGET_SIGSEGV); + } + + env->ir[IR_RA] = r26; + env->ir[IR_PV] = env->pc = ka->_sa_handler; + env->ir[IR_A0] = sig; + env->ir[IR_A1] = 0; + env->ir[IR_A2] = frame_addr + offsetof(struct target_sigframe, sc); + env->ir[IR_SP] = frame_addr; +} + +static void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUState *env) +{ + abi_ulong frame_addr, r26; + struct target_rt_sigframe *frame; + int i, err = 0; + + frame_addr = get_sigframe(ka, env, sizeof(*frame)); + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { + goto give_sigsegv; + } + + err |= copy_siginfo_to_user(&frame->info, info); + + err |= __put_user(0, &frame->uc.tuc_flags); + err |= __put_user(0, &frame->uc.tuc_link); + err |= __put_user(set->sig[0], &frame->uc.tuc_osf_sigmask); + err |= __put_user(target_sigaltstack_used.ss_sp, + &frame->uc.tuc_stack.ss_sp); + err |= __put_user(sas_ss_flags(env->ir[IR_SP]), + &frame->uc.tuc_stack.ss_flags); + err |= __put_user(target_sigaltstack_used.ss_size, + &frame->uc.tuc_stack.ss_size); + err |= setup_sigcontext(&frame->uc.tuc_mcontext, env, frame_addr, set); + for (i = 0; i < TARGET_NSIG_WORDS; ++i) { + err |= __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]); + } + + if (ka->sa_restorer) { + r26 = ka->sa_restorer; + } else { + err |= __put_user(INSN_MOV_R30_R16, &frame->retcode[0]); + err |= __put_user(INSN_LDI_R0 + TARGET_NR_rt_sigreturn, + &frame->retcode[1]); + err |= __put_user(INSN_CALLSYS, &frame->retcode[2]); + /* imb(); */ + r26 = frame_addr; + } + + if (err) { + give_sigsegv: + if (sig == TARGET_SIGSEGV) { + ka->_sa_handler = TARGET_SIG_DFL; + } + force_sig(TARGET_SIGSEGV); + } + + env->ir[IR_RA] = r26; + env->ir[IR_PV] = env->pc = ka->_sa_handler; + env->ir[IR_A0] = sig; + env->ir[IR_A1] = frame_addr + offsetof(struct target_rt_sigframe, info); + env->ir[IR_A2] = frame_addr + offsetof(struct target_rt_sigframe, uc); + env->ir[IR_SP] = frame_addr; +} + +long do_sigreturn(CPUState *env) +{ + struct target_sigcontext *sc; + abi_ulong sc_addr = env->ir[IR_A0]; + target_sigset_t target_set; + sigset_t set; + + if (!lock_user_struct(VERIFY_READ, sc, sc_addr, 1)) { + goto badframe; + } + + target_sigemptyset(&target_set); + if (__get_user(target_set.sig[0], &sc->sc_mask)) { + goto badframe; + } + + target_to_host_sigset_internal(&set, &target_set); + sigprocmask(SIG_SETMASK, &set, NULL); + + if (restore_sigcontext(env, sc)) { + goto badframe; + } + unlock_user_struct(sc, sc_addr, 0); + return env->ir[IR_V0]; + + badframe: + unlock_user_struct(sc, sc_addr, 0); + force_sig(TARGET_SIGSEGV); +} + +long do_rt_sigreturn(CPUState *env) +{ + abi_ulong frame_addr = env->ir[IR_A0]; + struct target_rt_sigframe *frame; + sigset_t set; + + if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { + goto badframe; + } + target_to_host_sigset(&set, &frame->uc.tuc_sigmask); + sigprocmask(SIG_SETMASK, &set, NULL); + + if (restore_sigcontext(env, &frame->uc.tuc_mcontext)) { + goto badframe; + } + if (do_sigaltstack(frame_addr + offsetof(struct target_rt_sigframe, + uc.tuc_stack), + 0, env->ir[IR_SP]) == -EFAULT) { + goto badframe; + } + + unlock_user_struct(frame, frame_addr, 0); + return env->ir[IR_V0]; + + + badframe: + unlock_user_struct(frame, frame_addr, 0); + force_sig(TARGET_SIGSEGV); +} + #else static void setup_frame(int sig, struct target_sigaction *ka, diff -Nru qemu-kvm-0.12.5+noroms/linux-user/strace.c qemu-kvm-0.14.1/linux-user/strace.c --- qemu-kvm-0.12.5+noroms/linux-user/strace.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/linux-user/strace.c 2011-05-11 13:29:46.000000000 +0000 @@ -385,6 +385,9 @@ FLAG_GENERIC(PROT_EXEC), FLAG_GENERIC(PROT_READ), FLAG_GENERIC(PROT_WRITE), + FLAG_TARGET(PROT_SEM), + FLAG_GENERIC(PROT_GROWSDOWN), + FLAG_GENERIC(PROT_GROWSUP), FLAG_END, }; @@ -1252,8 +1255,10 @@ int cmd = (int)tswap32(tflag); #ifdef FUTEX_PRIVATE_FLAG - if (cmd == FUTEX_PRIVATE_FLAG) + if (cmd & FUTEX_PRIVATE_FLAG) { gemu_log("FUTEX_PRIVATE_FLAG|"); + cmd &= ~FUTEX_PRIVATE_FLAG; + } #endif print_op(FUTEX_WAIT) print_op(FUTEX_WAKE) diff -Nru qemu-kvm-0.12.5+noroms/linux-user/strace.list qemu-kvm-0.14.1/linux-user/strace.list --- qemu-kvm-0.12.5+noroms/linux-user/strace.list 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/linux-user/strace.list 2011-05-11 13:29:46.000000000 +0000 @@ -1518,3 +1518,9 @@ #ifdef TARGET_NR_utimensat { TARGET_NR_utimensat, "utimensat", NULL, print_utimensat, NULL }, #endif +#ifdef TARGET_NR_sync_file_range +{ TARGET_NR_sync_file_range, "sync_file_range", NULL, NULL, NULL }, +#endif +#ifdef TARGET_NR_sync_file_range2 +{ TARGET_NR_sync_file_range2, "sync_file_range2", NULL, NULL, NULL }, +#endif diff -Nru qemu-kvm-0.12.5+noroms/linux-user/syscall.c qemu-kvm-0.14.1/linux-user/syscall.c --- qemu-kvm-0.12.5+noroms/linux-user/syscall.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/linux-user/syscall.c 2011-05-11 13:29:46.000000000 +0000 @@ -41,6 +41,10 @@ #include #include #include +#ifdef __ia64__ +int __clone2(int (*fn)(void *), void *child_stack_base, + size_t stack_size, int flags, void *arg, ...); +#endif #include #include #include @@ -79,9 +83,13 @@ #include #include #include +#if defined(CONFIG_FIEMAP) +#include +#endif #include #include #include "linux_loop.h" +#include "cpu-uname.h" #include "qemu.h" #include "qemu-common.h" @@ -203,7 +211,7 @@ _syscall3(int, sys_getdents64, uint, fd, struct linux_dirent64 *, dirp, uint, count); #endif _syscall2(int, sys_getpriority, int, which, int, who); -#if defined(TARGET_NR__llseek) && !defined (__x86_64__) +#if defined(TARGET_NR__llseek) && defined(__NR_llseek) _syscall5(int, _llseek, uint, fd, ulong, hi, ulong, lo, loff_t *, res, uint, wh); #endif @@ -505,9 +513,18 @@ return (inotify_rm_watch(fd, wd)); } #endif +#ifdef CONFIG_INOTIFY1 +#if defined(TARGET_NR_inotify_init1) && defined(__NR_inotify_init1) +static int sys_inotify_init1(int flags) +{ + return (inotify_init1(flags)); +} +#endif +#endif #else /* Userspace can usually survive runtime without inotify */ #undef TARGET_NR_inotify_init +#undef TARGET_NR_inotify_init1 #undef TARGET_NR_inotify_add_watch #undef TARGET_NR_inotify_rm_watch #endif /* CONFIG_INOTIFY */ @@ -704,9 +721,17 @@ PROT_READ|PROT_WRITE, MAP_ANON|MAP_FIXED|MAP_PRIVATE, 0, 0)); - if (!is_error(mapped_addr)) +#if defined(TARGET_ALPHA) + /* We (partially) emulate OSF/1 on Alpha, which requires we + return a proper errno, not an unchanged brk value. */ + if (is_error(mapped_addr)) { + return -TARGET_ENOMEM; + } +#endif + + if (!is_error(mapped_addr)) { target_brk = new_brk; - + } return target_brk; } @@ -817,6 +842,22 @@ return 0; } +static inline rlim_t target_to_host_rlim(target_ulong target_rlim) +{ + if (target_rlim == TARGET_RLIM_INFINITY) + return RLIM_INFINITY; + else + return tswapl(target_rlim); +} + +static inline target_ulong host_to_target_rlim(rlim_t rlim) +{ + if (rlim == RLIM_INFINITY || rlim != (target_long)rlim) + return TARGET_RLIM_INFINITY; + else + return tswapl(rlim); +} + static inline abi_long copy_from_user_timeval(struct timeval *tv, abi_ulong target_tv_addr) { @@ -957,7 +998,8 @@ #endif } -static abi_long do_pipe(void *cpu_env, abi_ulong pipedes, int flags) +static abi_long do_pipe(void *cpu_env, abi_ulong pipedes, + int flags, int is_pipe2) { int host_pipe[2]; abi_long ret; @@ -965,17 +1007,25 @@ if (is_error(ret)) return get_errno(ret); -#if defined(TARGET_MIPS) - ((CPUMIPSState*)cpu_env)->active_tc.gpr[3] = host_pipe[1]; - ret = host_pipe[0]; + + /* Several targets have special calling conventions for the original + pipe syscall, but didn't replicate this into the pipe2 syscall. */ + if (!is_pipe2) { +#if defined(TARGET_ALPHA) + ((CPUAlphaState *)cpu_env)->ir[IR_A4] = host_pipe[1]; + return host_pipe[0]; +#elif defined(TARGET_MIPS) + ((CPUMIPSState*)cpu_env)->active_tc.gpr[3] = host_pipe[1]; + return host_pipe[0]; #elif defined(TARGET_SH4) - ((CPUSH4State*)cpu_env)->gregs[1] = host_pipe[1]; - ret = host_pipe[0]; -#else + ((CPUSH4State*)cpu_env)->gregs[1] = host_pipe[1]; + return host_pipe[0]; +#endif + } + if (put_user_s32(host_pipe[0], pipedes) || put_user_s32(host_pipe[1], pipedes + sizeof(host_pipe[0]))) return -TARGET_EFAULT; -#endif return get_errno(ret); } @@ -1327,15 +1377,66 @@ switch(level) { case TARGET_SOL_SOCKET: - level = SOL_SOCKET; - switch (optname) { - case TARGET_SO_LINGER: - case TARGET_SO_RCVTIMEO: - case TARGET_SO_SNDTIMEO: - case TARGET_SO_PEERCRED: - case TARGET_SO_PEERNAME: - /* These don't just return a single integer */ - goto unimplemented; + level = SOL_SOCKET; + switch (optname) { + /* These don't just return a single integer */ + case TARGET_SO_LINGER: + case TARGET_SO_RCVTIMEO: + case TARGET_SO_SNDTIMEO: + case TARGET_SO_PEERCRED: + case TARGET_SO_PEERNAME: + goto unimplemented; + /* Options with 'int' argument. */ + case TARGET_SO_DEBUG: + optname = SO_DEBUG; + goto int_case; + case TARGET_SO_REUSEADDR: + optname = SO_REUSEADDR; + goto int_case; + case TARGET_SO_TYPE: + optname = SO_TYPE; + goto int_case; + case TARGET_SO_ERROR: + optname = SO_ERROR; + goto int_case; + case TARGET_SO_DONTROUTE: + optname = SO_DONTROUTE; + goto int_case; + case TARGET_SO_BROADCAST: + optname = SO_BROADCAST; + goto int_case; + case TARGET_SO_SNDBUF: + optname = SO_SNDBUF; + goto int_case; + case TARGET_SO_RCVBUF: + optname = SO_RCVBUF; + goto int_case; + case TARGET_SO_KEEPALIVE: + optname = SO_KEEPALIVE; + goto int_case; + case TARGET_SO_OOBINLINE: + optname = SO_OOBINLINE; + goto int_case; + case TARGET_SO_NO_CHECK: + optname = SO_NO_CHECK; + goto int_case; + case TARGET_SO_PRIORITY: + optname = SO_PRIORITY; + goto int_case; +#ifdef SO_BSDCOMPAT + case TARGET_SO_BSDCOMPAT: + optname = SO_BSDCOMPAT; + goto int_case; +#endif + case TARGET_SO_PASSCRED: + optname = SO_PASSCRED; + goto int_case; + case TARGET_SO_TIMESTAMP: + optname = SO_TIMESTAMP; + goto int_case; + case TARGET_SO_RCVLOWAT: + optname = SO_RCVLOWAT; + goto int_case; default: goto int_case; } @@ -1359,7 +1460,7 @@ } else { if (put_user_u8(val, optval_addr)) return -TARGET_EFAULT; - } + } if (put_user_u32(len, optlen)) return -TARGET_EFAULT; break; @@ -1504,8 +1605,9 @@ void *addr; abi_long ret; - if (addrlen < 0) + if ((int)addrlen < 0) { return -TARGET_EINVAL; + } addr = alloca(addrlen+1); @@ -1523,8 +1625,9 @@ void *addr; abi_long ret; - if (addrlen < 0) + if ((int)addrlen < 0) { return -TARGET_EINVAL; + } addr = alloca(addrlen); @@ -1609,8 +1712,9 @@ if (get_user_u32(addrlen, target_addrlen_addr)) return -TARGET_EINVAL; - if (addrlen < 0) + if ((int)addrlen < 0) { return -TARGET_EINVAL; + } if (!access_ok(VERIFY_WRITE, target_addr, addrlen)) return -TARGET_EINVAL; @@ -1637,8 +1741,9 @@ if (get_user_u32(addrlen, target_addrlen_addr)) return -TARGET_EFAULT; - if (addrlen < 0) + if ((int)addrlen < 0) { return -TARGET_EINVAL; + } if (!access_ok(VERIFY_WRITE, target_addr, addrlen)) return -TARGET_EFAULT; @@ -1665,8 +1770,9 @@ if (get_user_u32(addrlen, target_addrlen_addr)) return -TARGET_EFAULT; - if (addrlen < 0) + if ((int)addrlen < 0) { return -TARGET_EINVAL; + } if (!access_ok(VERIFY_WRITE, target_addr, addrlen)) return -TARGET_EFAULT; @@ -1706,8 +1812,9 @@ void *host_msg; abi_long ret; - if (addrlen < 0) + if ((int)addrlen < 0) { return -TARGET_EINVAL; + } host_msg = lock_user(VERIFY_READ, msg, len, 1); if (!host_msg) @@ -1745,7 +1852,7 @@ ret = -TARGET_EFAULT; goto fail; } - if (addrlen < 0) { + if ((int)addrlen < 0) { ret = -TARGET_EINVAL; goto fail; } @@ -2735,7 +2842,7 @@ for (i = 0; i < N_SHM_REGIONS; ++i) { if (shm_regions[i].start == shmaddr) { shm_regions[i].start = 0; - page_set_flags(shmaddr, shm_regions[i].size, 0); + page_set_flags(shmaddr, shmaddr + shm_regions[i].size, 0); break; } } @@ -2861,13 +2968,19 @@ #undef STRUCT #undef STRUCT_SPECIAL -typedef struct IOCTLEntry { +typedef struct IOCTLEntry IOCTLEntry; + +typedef abi_long do_ioctl_fn(const IOCTLEntry *ie, uint8_t *buf_temp, + int fd, abi_long cmd, abi_long arg); + +struct IOCTLEntry { unsigned int target_cmd; unsigned int host_cmd; const char *name; int access; + do_ioctl_fn *do_ioctl; const argtype arg_type[5]; -} IOCTLEntry; +}; #define IOC_R 0x0001 #define IOC_W 0x0002 @@ -2875,9 +2988,100 @@ #define MAX_STRUCT_SIZE 4096 +#ifdef CONFIG_FIEMAP +/* So fiemap access checks don't overflow on 32 bit systems. + * This is very slightly smaller than the limit imposed by + * the underlying kernel. + */ +#define FIEMAP_MAX_EXTENTS ((UINT_MAX - sizeof(struct fiemap)) \ + / sizeof(struct fiemap_extent)) + +static abi_long do_ioctl_fs_ioc_fiemap(const IOCTLEntry *ie, uint8_t *buf_temp, + int fd, abi_long cmd, abi_long arg) +{ + /* The parameter for this ioctl is a struct fiemap followed + * by an array of struct fiemap_extent whose size is set + * in fiemap->fm_extent_count. The array is filled in by the + * ioctl. + */ + int target_size_in, target_size_out; + struct fiemap *fm; + const argtype *arg_type = ie->arg_type; + const argtype extent_arg_type[] = { MK_STRUCT(STRUCT_fiemap_extent) }; + void *argptr, *p; + abi_long ret; + int i, extent_size = thunk_type_size(extent_arg_type, 0); + uint32_t outbufsz; + int free_fm = 0; + + assert(arg_type[0] == TYPE_PTR); + assert(ie->access == IOC_RW); + arg_type++; + target_size_in = thunk_type_size(arg_type, 0); + argptr = lock_user(VERIFY_READ, arg, target_size_in, 1); + if (!argptr) { + return -TARGET_EFAULT; + } + thunk_convert(buf_temp, argptr, arg_type, THUNK_HOST); + unlock_user(argptr, arg, 0); + fm = (struct fiemap *)buf_temp; + if (fm->fm_extent_count > FIEMAP_MAX_EXTENTS) { + return -TARGET_EINVAL; + } + + outbufsz = sizeof (*fm) + + (sizeof(struct fiemap_extent) * fm->fm_extent_count); + + if (outbufsz > MAX_STRUCT_SIZE) { + /* We can't fit all the extents into the fixed size buffer. + * Allocate one that is large enough and use it instead. + */ + fm = malloc(outbufsz); + if (!fm) { + return -TARGET_ENOMEM; + } + memcpy(fm, buf_temp, sizeof(struct fiemap)); + free_fm = 1; + } + ret = get_errno(ioctl(fd, ie->host_cmd, fm)); + if (!is_error(ret)) { + target_size_out = target_size_in; + /* An extent_count of 0 means we were only counting the extents + * so there are no structs to copy + */ + if (fm->fm_extent_count != 0) { + target_size_out += fm->fm_mapped_extents * extent_size; + } + argptr = lock_user(VERIFY_WRITE, arg, target_size_out, 0); + if (!argptr) { + ret = -TARGET_EFAULT; + } else { + /* Convert the struct fiemap */ + thunk_convert(argptr, fm, arg_type, THUNK_TARGET); + if (fm->fm_extent_count != 0) { + p = argptr + target_size_in; + /* ...and then all the struct fiemap_extents */ + for (i = 0; i < fm->fm_mapped_extents; i++) { + thunk_convert(p, &fm->fm_extents[i], extent_arg_type, + THUNK_TARGET); + p += extent_size; + } + } + unlock_user(argptr, arg, target_size_out); + } + } + if (free_fm) { + free(fm); + } + return ret; +} +#endif + static IOCTLEntry ioctl_entries[] = { #define IOCTL(cmd, access, ...) \ - { TARGET_ ## cmd, cmd, #cmd, access, { __VA_ARGS__ } }, + { TARGET_ ## cmd, cmd, #cmd, access, 0, { __VA_ARGS__ } }, +#define IOCTL_SPECIAL(cmd, access, dofn, ...) \ + { TARGET_ ## cmd, cmd, #cmd, access, dofn, { __VA_ARGS__ } }, #include "ioctls.h" { 0, 0, }, }; @@ -2907,6 +3111,10 @@ #if defined(DEBUG) gemu_log("ioctl: cmd=0x%04lx (%s)\n", (long)cmd, ie->name); #endif + if (ie->do_ioctl) { + return ie->do_ioctl(ie, buf_temp, fd, cmd, arg); + } + switch(arg_type[0]) { case TYPE_NULL: /* no argument */ @@ -3531,11 +3739,12 @@ { int ret; TaskState *ts; - uint8_t *new_stack; CPUState *new_env; #if defined(CONFIG_USE_NPTL) unsigned int nptl_flags; sigset_t sigmask; +#else + uint8_t *new_stack; #endif /* Emulate vfork() with fork() */ @@ -3548,9 +3757,8 @@ new_thread_info info; pthread_attr_t attr; #endif - ts = qemu_mallocz(sizeof(TaskState) + NEW_STACK_SIZE); + ts = qemu_mallocz(sizeof(TaskState)); init_task_state(ts); - new_stack = ts->stack; /* we create a new CPU instance. */ new_env = cpu_copy(env); #if defined(TARGET_I386) || defined(TARGET_SPARC) || defined(TARGET_PPC) @@ -3586,7 +3794,8 @@ info.parent_tidptr = parent_tidptr; ret = pthread_attr_init(&attr); - ret = pthread_attr_setstack(&attr, new_stack, NEW_STACK_SIZE); + ret = pthread_attr_setstacksize(&attr, NEW_STACK_SIZE); + ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); /* It is not safe to deliver signals until the child has finished initializing, so temporarily block all signals. */ sigfillset(&sigmask); @@ -3614,8 +3823,9 @@ if (flags & CLONE_NPTL_FLAGS2) return -EINVAL; /* This is probably going to die very quickly, but do it anyway. */ + new_stack = qemu_mallocz (NEW_STACK_SIZE); #ifdef __ia64__ - ret = __clone2(clone_func, new_stack + NEW_STACK_SIZE, flags, new_env); + ret = __clone2(clone_func, new_stack, NEW_STACK_SIZE, flags, new_env); #else ret = clone(clone_func, new_stack + NEW_STACK_SIZE, flags, new_env); #endif @@ -4004,7 +4214,7 @@ } else #endif { -#if TARGET_LONG_BITS == 64 +#if TARGET_ABI_BITS == 64 && !defined(TARGET_ALPHA) struct target_stat *target_st; #else struct target_stat64 *target_st; @@ -4187,7 +4397,9 @@ sys_futex(g2h(ts->child_tidptr), FUTEX_WAKE, INT_MAX, NULL, NULL, 0); } - /* TODO: Free CPU state. */ + thread_env = NULL; + qemu_free(cpu_env); + qemu_free(ts); pthread_exit(NULL); } #endif @@ -4450,13 +4662,18 @@ case TARGET_NR_lseek: ret = get_errno(lseek(arg1, arg2, arg3)); break; -#ifdef TARGET_NR_getxpid +#if defined(TARGET_NR_getxpid) && defined(TARGET_ALPHA) + /* Alpha specific */ case TARGET_NR_getxpid: -#else - case TARGET_NR_getpid: + ((CPUAlphaState *)cpu_env)->ir[IR_A4] = getppid(); + ret = get_errno(getpid()); + break; #endif +#ifdef TARGET_NR_getpid + case TARGET_NR_getpid: ret = get_errno(getpid()); break; +#endif case TARGET_NR_mount: { /* need to look at the data field */ @@ -4665,11 +4882,11 @@ ret = get_errno(dup(arg1)); break; case TARGET_NR_pipe: - ret = do_pipe(cpu_env, arg1, 0); + ret = do_pipe(cpu_env, arg1, 0, 0); break; #ifdef TARGET_NR_pipe2 case TARGET_NR_pipe2: - ret = do_pipe(cpu_env, arg1, arg2); + ret = do_pipe(cpu_env, arg1, arg2, 1); break; #endif case TARGET_NR_times: @@ -4774,20 +4991,18 @@ #ifdef TARGET_NR_sigaction case TARGET_NR_sigaction: { -#if !defined(TARGET_MIPS) +#if defined(TARGET_ALPHA) + struct target_sigaction act, oact, *pact = 0; struct target_old_sigaction *old_act; - struct target_sigaction act, oact, *pact; if (arg2) { if (!lock_user_struct(VERIFY_READ, old_act, arg2, 1)) goto efault; act._sa_handler = old_act->_sa_handler; target_siginitset(&act.sa_mask, old_act->sa_mask); act.sa_flags = old_act->sa_flags; - act.sa_restorer = old_act->sa_restorer; + act.sa_restorer = 0; unlock_user_struct(old_act, arg2, 0); pact = &act; - } else { - pact = NULL; } ret = get_errno(do_sigaction(arg1, pact, &oact)); if (!is_error(ret) && arg3) { @@ -4796,10 +5011,9 @@ old_act->_sa_handler = oact._sa_handler; old_act->sa_mask = oact.sa_mask.sig[0]; old_act->sa_flags = oact.sa_flags; - old_act->sa_restorer = oact.sa_restorer; unlock_user_struct(old_act, arg3, 1); } -#else +#elif defined(TARGET_MIPS) struct target_sigaction act, oact, *pact, *old_act; if (arg2) { @@ -4827,12 +5041,61 @@ old_act->sa_mask.sig[3] = 0; unlock_user_struct(old_act, arg3, 1); } +#else + struct target_old_sigaction *old_act; + struct target_sigaction act, oact, *pact; + if (arg2) { + if (!lock_user_struct(VERIFY_READ, old_act, arg2, 1)) + goto efault; + act._sa_handler = old_act->_sa_handler; + target_siginitset(&act.sa_mask, old_act->sa_mask); + act.sa_flags = old_act->sa_flags; + act.sa_restorer = old_act->sa_restorer; + unlock_user_struct(old_act, arg2, 0); + pact = &act; + } else { + pact = NULL; + } + ret = get_errno(do_sigaction(arg1, pact, &oact)); + if (!is_error(ret) && arg3) { + if (!lock_user_struct(VERIFY_WRITE, old_act, arg3, 0)) + goto efault; + old_act->_sa_handler = oact._sa_handler; + old_act->sa_mask = oact.sa_mask.sig[0]; + old_act->sa_flags = oact.sa_flags; + old_act->sa_restorer = oact.sa_restorer; + unlock_user_struct(old_act, arg3, 1); + } #endif } break; #endif case TARGET_NR_rt_sigaction: { +#if defined(TARGET_ALPHA) + struct target_sigaction act, oact, *pact = 0; + struct target_rt_sigaction *rt_act; + /* ??? arg4 == sizeof(sigset_t). */ + if (arg2) { + if (!lock_user_struct(VERIFY_READ, rt_act, arg2, 1)) + goto efault; + act._sa_handler = rt_act->_sa_handler; + act.sa_mask = rt_act->sa_mask; + act.sa_flags = rt_act->sa_flags; + act.sa_restorer = arg5; + unlock_user_struct(rt_act, arg2, 0); + pact = &act; + } + ret = get_errno(do_sigaction(arg1, pact, &oact)); + if (!is_error(ret) && arg3) { + if (!lock_user_struct(VERIFY_WRITE, rt_act, arg3, 0)) + goto efault; + rt_act->_sa_handler = oact._sa_handler; + rt_act->sa_mask = oact.sa_mask; + rt_act->sa_flags = oact.sa_flags; + unlock_user_struct(rt_act, arg3, 1); + } +#else struct target_sigaction *act; struct target_sigaction *oact; @@ -4854,6 +5117,7 @@ unlock_user_struct(act, arg2, 0); if (oact) unlock_user_struct(oact, arg3, 1); +#endif } break; #ifdef TARGET_NR_sgetmask /* not on alpha */ @@ -4884,11 +5148,41 @@ #ifdef TARGET_NR_sigprocmask case TARGET_NR_sigprocmask: { - int how = arg1; +#if defined(TARGET_ALPHA) + sigset_t set, oldset; + abi_ulong mask; + int how; + + switch (arg1) { + case TARGET_SIG_BLOCK: + how = SIG_BLOCK; + break; + case TARGET_SIG_UNBLOCK: + how = SIG_UNBLOCK; + break; + case TARGET_SIG_SETMASK: + how = SIG_SETMASK; + break; + default: + ret = -TARGET_EINVAL; + goto fail; + } + mask = arg2; + target_to_host_old_sigset(&set, &mask); + + ret = get_errno(sigprocmask(how, &set, &oldset)); + + if (!is_error(ret)) { + host_to_target_old_sigset(&mask, &oldset); + ret = mask; + ((CPUAlphaState *)cpu_env)->[IR_V0] = 0; /* force no error */ + } +#else sigset_t set, oldset, *set_ptr; + int how; if (arg2) { - switch(how) { + switch (arg1) { case TARGET_SIG_BLOCK: how = SIG_BLOCK; break; @@ -4911,13 +5205,14 @@ how = 0; set_ptr = NULL; } - ret = get_errno(sigprocmask(arg1, set_ptr, &oldset)); + ret = get_errno(sigprocmask(how, set_ptr, &oldset)); if (!is_error(ret) && arg3) { if (!(p = lock_user(VERIFY_WRITE, arg3, sizeof(target_sigset_t), 0))) goto efault; host_to_target_old_sigset(p, &oldset); unlock_user(p, arg3, sizeof(target_sigset_t)); } +#endif } break; #endif @@ -4989,10 +5284,15 @@ case TARGET_NR_sigsuspend: { sigset_t set; +#if defined(TARGET_ALPHA) + abi_ulong mask = arg1; + target_to_host_old_sigset(&set, &mask); +#else if (!(p = lock_user(VERIFY_READ, arg1, sizeof(target_sigset_t), 1))) goto efault; target_to_host_old_sigset(&set, p); unlock_user(p, arg1, 0); +#endif ret = get_errno(sigsuspend(&set)); } break; @@ -5060,21 +5360,19 @@ break; case TARGET_NR_setrlimit: { - /* XXX: convert resource ? */ int resource = arg1; struct target_rlimit *target_rlim; struct rlimit rlim; if (!lock_user_struct(VERIFY_READ, target_rlim, arg2, 1)) goto efault; - rlim.rlim_cur = tswapl(target_rlim->rlim_cur); - rlim.rlim_max = tswapl(target_rlim->rlim_max); + rlim.rlim_cur = target_to_host_rlim(target_rlim->rlim_cur); + rlim.rlim_max = target_to_host_rlim(target_rlim->rlim_max); unlock_user_struct(target_rlim, arg2, 0); ret = get_errno(setrlimit(resource, &rlim)); } break; case TARGET_NR_getrlimit: { - /* XXX: convert resource ? */ int resource = arg1; struct target_rlimit *target_rlim; struct rlimit rlim; @@ -5083,8 +5381,8 @@ if (!is_error(ret)) { if (!lock_user_struct(VERIFY_WRITE, target_rlim, arg2, 0)) goto efault; - target_rlim->rlim_cur = tswapl(rlim.rlim_cur); - target_rlim->rlim_max = tswapl(rlim.rlim_max); + target_rlim->rlim_cur = host_to_target_rlim(rlim.rlim_cur); + target_rlim->rlim_max = host_to_target_rlim(rlim.rlim_max); unlock_user_struct(target_rlim, arg2, 1); } } @@ -5135,6 +5433,10 @@ } break; #endif +#ifdef TARGET_NR_pselect6 + case TARGET_NR_pselect6: + goto unimplemented_nowarn; +#endif case TARGET_NR_symlink: { void *p2; @@ -5263,6 +5565,17 @@ ret = get_errno(target_munmap(arg1, arg2)); break; case TARGET_NR_mprotect: + { + TaskState *ts = ((CPUState *)cpu_env)->opaque; + /* Special hack to detect libc making the stack executable. */ + if ((arg3 & PROT_GROWSDOWN) + && arg1 >= ts->info->stack_limit + && arg1 <= ts->info->start_stack) { + arg3 &= ~PROT_GROWSDOWN; + arg2 = arg2 + arg1 - ts->info->stack_limit; + arg1 = ts->info->stack_limit; + } + } ret = get_errno(target_mprotect(arg1, arg2, arg3)); break; #ifdef TARGET_NR_mremap @@ -5704,7 +6017,7 @@ ret = get_errno(fsync(arg1)); break; case TARGET_NR_clone: -#if defined(TARGET_SH4) +#if defined(TARGET_SH4) || defined(TARGET_ALPHA) ret = get_errno(do_fork(cpu_env, arg1, arg2, arg3, arg5, arg4)); #elif defined(TARGET_CRIS) ret = get_errno(do_fork(cpu_env, arg2, arg1, arg3, arg4, arg5)); @@ -5739,7 +6052,7 @@ if (!is_error(ret)) { /* Overrite the native machine name with whatever is being emulated. */ - strcpy (buf->machine, UNAME_MACHINE); + strcpy (buf->machine, cpu_to_uname_machine(cpu_env)); /* Allow the user to override the reported release. */ if (qemu_uname_release && *qemu_uname_release) strcpy (buf->release, qemu_uname_release); @@ -5796,7 +6109,7 @@ #ifdef TARGET_NR__llseek /* Not on alpha */ case TARGET_NR__llseek: { -#if defined (__x86_64__) +#if !defined(__NR_llseek) ret = get_errno(lseek(arg1, ((uint64_t )arg2 << 32) | arg3, arg5)); if (put_user_s64(ret, arg4)) goto efault; @@ -6169,8 +6482,8 @@ struct target_rlimit *target_rlim; if (!lock_user_struct(VERIFY_WRITE, target_rlim, arg2, 0)) goto efault; - target_rlim->rlim_cur = tswapl(rlim.rlim_cur); - target_rlim->rlim_max = tswapl(rlim.rlim_max); + target_rlim->rlim_cur = host_to_target_rlim(rlim.rlim_cur); + target_rlim->rlim_max = host_to_target_rlim(rlim.rlim_max); unlock_user_struct(target_rlim, arg2, 1); } break; @@ -6390,25 +6703,142 @@ #if defined(TARGET_NR_getxuid) && defined(TARGET_ALPHA) /* Alpha specific */ case TARGET_NR_getxuid: - { - uid_t euid; - euid=geteuid(); - ((CPUAlphaState *)cpu_env)->ir[IR_A4]=euid; - } + { + uid_t euid; + euid=geteuid(); + ((CPUAlphaState *)cpu_env)->ir[IR_A4]=euid; + } ret = get_errno(getuid()); break; #endif #if defined(TARGET_NR_getxgid) && defined(TARGET_ALPHA) /* Alpha specific */ case TARGET_NR_getxgid: - { - uid_t egid; - egid=getegid(); - ((CPUAlphaState *)cpu_env)->ir[IR_A4]=egid; - } + { + uid_t egid; + egid=getegid(); + ((CPUAlphaState *)cpu_env)->ir[IR_A4]=egid; + } ret = get_errno(getgid()); break; #endif +#if defined(TARGET_NR_osf_getsysinfo) && defined(TARGET_ALPHA) + /* Alpha specific */ + case TARGET_NR_osf_getsysinfo: + ret = -TARGET_EOPNOTSUPP; + switch (arg1) { + case TARGET_GSI_IEEE_FP_CONTROL: + { + uint64_t swcr, fpcr = cpu_alpha_load_fpcr (cpu_env); + + /* Copied from linux ieee_fpcr_to_swcr. */ + swcr = (fpcr >> 35) & SWCR_STATUS_MASK; + swcr |= (fpcr >> 36) & SWCR_MAP_DMZ; + swcr |= (~fpcr >> 48) & (SWCR_TRAP_ENABLE_INV + | SWCR_TRAP_ENABLE_DZE + | SWCR_TRAP_ENABLE_OVF); + swcr |= (~fpcr >> 57) & (SWCR_TRAP_ENABLE_UNF + | SWCR_TRAP_ENABLE_INE); + swcr |= (fpcr >> 47) & SWCR_MAP_UMZ; + swcr |= (~fpcr >> 41) & SWCR_TRAP_ENABLE_DNO; + + if (put_user_u64 (swcr, arg2)) + goto efault; + ret = 0; + } + break; + + /* case GSI_IEEE_STATE_AT_SIGNAL: + -- Not implemented in linux kernel. + case GSI_UACPROC: + -- Retrieves current unaligned access state; not much used. + case GSI_PROC_TYPE: + -- Retrieves implver information; surely not used. + case GSI_GET_HWRPB: + -- Grabs a copy of the HWRPB; surely not used. + */ + } + break; +#endif +#if defined(TARGET_NR_osf_setsysinfo) && defined(TARGET_ALPHA) + /* Alpha specific */ + case TARGET_NR_osf_setsysinfo: + ret = -TARGET_EOPNOTSUPP; + switch (arg1) { + case TARGET_SSI_IEEE_FP_CONTROL: + case TARGET_SSI_IEEE_RAISE_EXCEPTION: + { + uint64_t swcr, fpcr, orig_fpcr; + + if (get_user_u64 (swcr, arg2)) + goto efault; + orig_fpcr = cpu_alpha_load_fpcr (cpu_env); + fpcr = orig_fpcr & FPCR_DYN_MASK; + + /* Copied from linux ieee_swcr_to_fpcr. */ + fpcr |= (swcr & SWCR_STATUS_MASK) << 35; + fpcr |= (swcr & SWCR_MAP_DMZ) << 36; + fpcr |= (~swcr & (SWCR_TRAP_ENABLE_INV + | SWCR_TRAP_ENABLE_DZE + | SWCR_TRAP_ENABLE_OVF)) << 48; + fpcr |= (~swcr & (SWCR_TRAP_ENABLE_UNF + | SWCR_TRAP_ENABLE_INE)) << 57; + fpcr |= (swcr & SWCR_MAP_UMZ ? FPCR_UNDZ | FPCR_UNFD : 0); + fpcr |= (~swcr & SWCR_TRAP_ENABLE_DNO) << 41; + + cpu_alpha_store_fpcr (cpu_env, fpcr); + ret = 0; + + if (arg1 == TARGET_SSI_IEEE_RAISE_EXCEPTION) { + /* Old exceptions are not signaled. */ + fpcr &= ~(orig_fpcr & FPCR_STATUS_MASK); + + /* If any exceptions set by this call, and are unmasked, + send a signal. */ + /* ??? FIXME */ + } + } + break; + + /* case SSI_NVPAIRS: + -- Used with SSIN_UACPROC to enable unaligned accesses. + case SSI_IEEE_STATE_AT_SIGNAL: + case SSI_IEEE_IGNORE_STATE_AT_SIGNAL: + -- Not implemented in linux kernel + */ + } + break; +#endif +#ifdef TARGET_NR_osf_sigprocmask + /* Alpha specific. */ + case TARGET_NR_osf_sigprocmask: + { + abi_ulong mask; + int how = arg1; + sigset_t set, oldset; + + switch(arg1) { + case TARGET_SIG_BLOCK: + how = SIG_BLOCK; + break; + case TARGET_SIG_UNBLOCK: + how = SIG_UNBLOCK; + break; + case TARGET_SIG_SETMASK: + how = SIG_SETMASK; + break; + default: + ret = -TARGET_EINVAL; + goto fail; + } + mask = arg2; + target_to_host_old_sigset(&set, &mask); + sigprocmask(arg1, &set, &oldset); + host_to_target_old_sigset(&mask, &oldset); + ret = mask; + } + break; +#endif #ifdef TARGET_NR_getgid32 case TARGET_NR_getgid32: @@ -6887,6 +7317,13 @@ ret = get_errno(sys_inotify_init()); break; #endif +#ifdef CONFIG_INOTIFY1 +#if defined(TARGET_NR_inotify_init1) && defined(__NR_inotify_init1) + case TARGET_NR_inotify_init1: + ret = get_errno(sys_inotify_init1(arg1)); + break; +#endif +#endif #if defined(TARGET_NR_inotify_add_watch) && defined(__NR_inotify_add_watch) case TARGET_NR_inotify_add_watch: p = lock_user_string(arg2); @@ -7032,6 +7469,29 @@ ret = get_errno(fallocate(arg1, arg2, arg3, arg4)); break; #endif +#if defined(CONFIG_SYNC_FILE_RANGE) +#if defined(TARGET_NR_sync_file_range) + case TARGET_NR_sync_file_range: +#if TARGET_ABI_BITS == 32 + ret = get_errno(sync_file_range(arg1, target_offset64(arg2, arg3), + target_offset64(arg4, arg5), arg6)); +#else + ret = get_errno(sync_file_range(arg1, arg2, arg3, arg4)); +#endif + break; +#endif +#if defined(TARGET_NR_sync_file_range2) + case TARGET_NR_sync_file_range2: + /* This is like sync_file_range but the arguments are reordered */ +#if TARGET_ABI_BITS == 32 + ret = get_errno(sync_file_range(arg1, target_offset64(arg3, arg4), + target_offset64(arg5, arg6), arg2)); +#else + ret = get_errno(sync_file_range(arg1, arg3, arg4, arg2)); +#endif + break; +#endif +#endif default: unimplemented: gemu_log("qemu: Unsupported syscall: %d\n", num); diff -Nru qemu-kvm-0.12.5+noroms/linux-user/syscall_defs.h qemu-kvm-0.14.1/linux-user/syscall_defs.h --- qemu-kvm-0.12.5+noroms/linux-user/syscall_defs.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/linux-user/syscall_defs.h 2011-05-11 13:29:46.000000000 +0000 @@ -49,7 +49,7 @@ #define TARGET_IOC_TYPEBITS 8 #if defined(TARGET_I386) || defined(TARGET_ARM) || defined(TARGET_SPARC) \ - || defined(TARGET_M68K) || defined(TARGET_SH4) || defined(TARGET_CRIS) + || defined(TARGET_M68K) || defined(TARGET_SH4) || defined(TARGET_CRIS) || defined(TARGET_PPC) || defined(TARGET_MIPS) /* 16 bit uid wrappers emulation */ #define USE_UID16 #endif @@ -336,6 +336,14 @@ #if !defined(TARGET_ABI_MIPSN32) && !defined(TARGET_ABI_MIPSN64) #define TARGET_SA_RESTORER 0x04000000 /* Only for O32 */ #endif +#elif defined(TARGET_ALPHA) +#define TARGET_SA_ONSTACK 0x00000001 +#define TARGET_SA_RESTART 0x00000002 +#define TARGET_SA_NOCLDSTOP 0x00000004 +#define TARGET_SA_NODEFER 0x00000008 +#define TARGET_SA_RESETHAND 0x00000010 +#define TARGET_SA_NOCLDWAIT 0x00000020 /* not supported yet */ +#define TARGET_SA_SIGINFO 0x00000040 #else #define TARGET_SA_NOCLDSTOP 0x00000001 #define TARGET_SA_NOCLDWAIT 0x00000002 /* not supported yet */ @@ -472,8 +480,28 @@ #endif -#if defined(TARGET_MIPS) +#if defined(TARGET_ALPHA) +struct target_old_sigaction { + abi_ulong _sa_handler; + abi_ulong sa_mask; + abi_ulong sa_flags; +}; + +struct target_rt_sigaction { + abi_ulong _sa_handler; + abi_ulong sa_flags; + target_sigset_t sa_mask; +}; +/* This is the struct used inside the kernel. The ka_restorer + field comes from the 5th argument to sys_rt_sigaction. */ +struct target_sigaction { + abi_ulong _sa_handler; + abi_ulong sa_flags; + target_sigset_t sa_mask; + abi_ulong sa_restorer; +}; +#elif defined(TARGET_MIPS) struct target_sigaction { uint32_t sa_flags; #if defined(TARGET_ABI_MIPSN32) @@ -483,7 +511,6 @@ #endif target_sigset_t sa_mask; }; - #else struct target_old_sigaction { abi_ulong _sa_handler; @@ -650,6 +677,14 @@ abi_ulong rlim_max; }; +#if defined(TARGET_ALPHA) +#define TARGET_RLIM_INFINITY 0x7fffffffffffffffull +#elif defined(TARGET_MIPS) || defined(TARGET_SPARC) +#define TARGET_RLIM_INFINITY 0x7fffffffUL +#else +#define TARGET_RLIM_INFINITY ((target_ulong)~0UL) +#endif + struct target_pollfd { int fd; /* file descriptor */ short events; /* requested events */ @@ -748,6 +783,7 @@ #define TARGET_BLKGETSIZE64 TARGET_IOR(0x12,114,sizeof(uint64_t)) /* return device size in bytes (u64 *arg) */ #define TARGET_FIBMAP TARGET_IO(0x00,1) /* bmap access */ #define TARGET_FIGETBSZ TARGET_IO(0x00,2) /* get the block size used for bmap */ +#define TARGET_FS_IOC_FIEMAP TARGET_IOWR('f',11,struct fiemap) /* cdrom commands */ #define TARGET_CDROMPAUSE 0x5301 /* Pause Audio Operation */ @@ -911,6 +947,12 @@ #include "termbits.h" +#if defined(TARGET_MIPS) +#define TARGET_PROT_SEM 0x10 +#else +#define TARGET_PROT_SEM 0x08 +#endif + /* Common */ #define TARGET_MAP_SHARED 0x01 /* Share changes */ #define TARGET_MAP_PRIVATE 0x02 /* Changes are private */ @@ -1241,7 +1283,10 @@ /* FIXME: Microblaze no-mmu user-space has a difference stat64 layout... */ struct __attribute__((__packed__)) target_stat64 { uint64_t st_dev; - uint64_t st_ino; +#define TARGET_STAT64_HAS_BROKEN_ST_INO 1 + uint32_t pad0; + uint32_t __st_ino; + uint32_t st_mode; uint32_t st_nlink; uint32_t st_uid; @@ -1255,13 +1300,12 @@ int64_t st_blocks; /* Number 512-byte blocks allocated. */ int target_st_atime; - unsigned int target_st_atime_nsec; + unsigned int target_st_atime_nsec; int target_st_mtime; - unsigned int target_st_mtime_nsec; + unsigned int target_st_mtime_nsec; int target_st_ctime; - unsigned int target_st_ctime_nsec; - uint32_t __unused4; - uint32_t __unused5; + unsigned int target_st_ctime_nsec; + uint64_t st_ino; }; #elif defined(TARGET_M68K) diff -Nru qemu-kvm-0.12.5+noroms/linux-user/syscall_types.h qemu-kvm-0.14.1/linux-user/syscall_types.h --- qemu-kvm-0.12.5+noroms/linux-user/syscall_types.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/linux-user/syscall_types.h 2011-05-11 13:29:46.000000000 +0000 @@ -165,3 +165,19 @@ TYPE_SHORT, /* v_active */ TYPE_SHORT, /* v_signal */ TYPE_SHORT) /* v_state */ + +STRUCT(fiemap_extent, + TYPE_ULONGLONG, /* fe_logical */ + TYPE_ULONGLONG, /* fe_physical */ + TYPE_ULONGLONG, /* fe_length */ + MK_ARRAY(TYPE_ULONGLONG, 2), /* fe_reserved64[2] */ + TYPE_INT, /* fe_flags */ + MK_ARRAY(TYPE_INT, 3)) /* fe_reserved[3] */ + +STRUCT(fiemap, + TYPE_ULONGLONG, /* fm_start */ + TYPE_ULONGLONG, /* fm_length */ + TYPE_INT, /* fm_flags */ + TYPE_INT, /* fm_mapped_extents */ + TYPE_INT, /* fm_extent_count */ + TYPE_INT) /* fm_reserved */ diff -Nru qemu-kvm-0.12.5+noroms/m68k-dis.c qemu-kvm-0.14.1/m68k-dis.c --- qemu-kvm-0.12.5+noroms/m68k-dis.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/m68k-dis.c 2011-05-11 13:29:46.000000000 +0000 @@ -572,38 +572,38 @@ #endif /* Get a 1 byte signed integer. */ -#define NEXTBYTE(p) (p += 2, FETCH_DATA (info, p), COERCE_SIGNED_CHAR(p[-1])) +#define NEXTBYTE(p) (p += 2, fetch_data(info, p), COERCE_SIGNED_CHAR(p[-1])) /* Get a 2 byte signed integer. */ #define COERCE16(x) ((int) (((x) ^ 0x8000) - 0x8000)) #define NEXTWORD(p) \ - (p += 2, FETCH_DATA (info, p), \ + (p += 2, fetch_data(info, p), \ COERCE16 ((p[-2] << 8) + p[-1])) /* Get a 4 byte signed integer. */ #define COERCE32(x) ((bfd_signed_vma) ((x) ^ 0x80000000) - 0x80000000) #define NEXTLONG(p) \ - (p += 4, FETCH_DATA (info, p), \ + (p += 4, fetch_data(info, p), \ (COERCE32 ((((((p[-4] << 8) + p[-3]) << 8) + p[-2]) << 8) + p[-1]))) /* Get a 4 byte unsigned integer. */ #define NEXTULONG(p) \ - (p += 4, FETCH_DATA (info, p), \ + (p += 4, fetch_data(info, p), \ (unsigned int) ((((((p[-4] << 8) + p[-3]) << 8) + p[-2]) << 8) + p[-1])) /* Get a single precision float. */ #define NEXTSINGLE(val, p) \ - (p += 4, FETCH_DATA (info, p), \ + (p += 4, fetch_data(info, p), \ floatformat_to_double (&floatformat_ieee_single_big, (char *) p - 4, &val)) /* Get a double precision float. */ #define NEXTDOUBLE(val, p) \ - (p += 8, FETCH_DATA (info, p), \ + (p += 8, fetch_data(info, p), \ floatformat_to_double (&floatformat_ieee_double_big, (char *) p - 8, &val)) /* Get an extended precision float. */ #define NEXTEXTEND(val, p) \ - (p += 12, FETCH_DATA (info, p), \ + (p += 12, fetch_data(info, p), \ floatformat_to_double (&floatformat_m68881_ext, (char *) p - 12, &val)) /* Need a function to convert from packed to double @@ -611,7 +611,7 @@ packed number than a double anyway, so maybe there should be a special case to handle this... */ #define NEXTPACKED(p) \ - (p += 12, FETCH_DATA (info, p), 0.0) + (p += 12, fetch_data(info, p), 0.0) /* Maximum length of an instruction. */ #define MAXLEN 22 @@ -630,12 +630,8 @@ /* Make sure that bytes from INFO->PRIVATE_DATA->BUFFER (inclusive) to ADDR (exclusive) are valid. Returns 1 for success, longjmps on error. */ -#define FETCH_DATA(info, addr) \ - ((addr) <= ((struct private *) (info->private_data))->max_fetched \ - ? 1 : fetch_data ((info), (addr))) - static int -fetch_data (struct disassemble_info *info, bfd_byte *addr) +fetch_data2(struct disassemble_info *info, bfd_byte *addr) { int status; struct private *priv = (struct private *)info->private_data; @@ -654,7 +650,17 @@ priv->max_fetched = addr; return 1; } - + +static int +fetch_data(struct disassemble_info *info, bfd_byte *addr) +{ + if (addr <= ((struct private *) (info->private_data))->max_fetched) { + return 1; + } else { + return fetch_data2(info, addr); + } +} + /* This function is used to print to the bit-bucket. */ static int dummy_printer (FILE *file ATTRIBUTE_UNUSED, @@ -728,64 +734,64 @@ break; case 'k': - FETCH_DATA (info, buffer + 3); + fetch_data(info, buffer + 3); val = (buffer[3] >> 4); break; case 'C': - FETCH_DATA (info, buffer + 3); + fetch_data(info, buffer + 3); val = buffer[3]; break; case '1': - FETCH_DATA (info, buffer + 3); + fetch_data(info, buffer + 3); val = (buffer[2] << 8) + buffer[3]; val >>= 12; break; case '2': - FETCH_DATA (info, buffer + 3); + fetch_data(info, buffer + 3); val = (buffer[2] << 8) + buffer[3]; val >>= 6; break; case '3': case 'j': - FETCH_DATA (info, buffer + 3); + fetch_data(info, buffer + 3); val = (buffer[2] << 8) + buffer[3]; break; case '4': - FETCH_DATA (info, buffer + 5); + fetch_data(info, buffer + 5); val = (buffer[4] << 8) + buffer[5]; val >>= 12; break; case '5': - FETCH_DATA (info, buffer + 5); + fetch_data(info, buffer + 5); val = (buffer[4] << 8) + buffer[5]; val >>= 6; break; case '6': - FETCH_DATA (info, buffer + 5); + fetch_data(info, buffer + 5); val = (buffer[4] << 8) + buffer[5]; break; case '7': - FETCH_DATA (info, buffer + 3); + fetch_data(info, buffer + 3); val = (buffer[2] << 8) + buffer[3]; val >>= 7; break; case '8': - FETCH_DATA (info, buffer + 3); + fetch_data(info, buffer + 3); val = (buffer[2] << 8) + buffer[3]; val >>= 10; break; case '9': - FETCH_DATA (info, buffer + 3); + fetch_data(info, buffer + 3); val = (buffer[2] << 8) + buffer[3]; val >>= 5; break; @@ -1104,7 +1110,7 @@ { static const char *const cacheFieldName[] = { "nc", "dc", "ic", "bc" }; val = fetch_arg (buffer, place, 2, info); - (*info->fprintf_func) (info->stream, cacheFieldName[val]); + (*info->fprintf_func) (info->stream, "%s", cacheFieldName[val]); break; } @@ -1199,7 +1205,7 @@ { static const char *const scalefactor_name[] = { "<<", ">>" }; val = fetch_arg (buffer, place, 1, info); - (*info->fprintf_func) (info->stream, scalefactor_name[val]); + (*info->fprintf_func) (info->stream, "%s", scalefactor_name[val]); } else { @@ -1726,7 +1732,7 @@ const char *d; bfd_byte *buffer = priv->the_buffer; - fprintf_ftype save_printer = info->fprintf_func; + fprintf_function save_printer = info->fprintf_func; void (* save_print_address) (bfd_vma, struct disassemble_info *) = info->print_address_func; @@ -1793,18 +1799,18 @@ this because we know exactly what the second word is, and we aren't going to print anything based on it. */ p = buffer + 6; - FETCH_DATA (info, p); + fetch_data(info, p); buffer[2] = buffer[4]; buffer[3] = buffer[5]; } - FETCH_DATA (info, p); + fetch_data(info, p); d = best->args; save_p = p; info->print_address_func = dummy_print_address; - info->fprintf_func = (fprintf_ftype) dummy_printer; + info->fprintf_func = dummy_printer; /* We scan the operands twice. The first time we don't print anything, but look for errors. */ @@ -1963,7 +1969,7 @@ break; } - FETCH_DATA (info, buffer + 2); + fetch_data(info, buffer + 2); major_opcode = (buffer[0] >> 4) & 15; for (i = 0; i < numopcodes[major_opcode]; i++) @@ -1977,7 +1983,7 @@ /* Only fetch the next two bytes if we need to. */ && (((0xffff & match) == 0) || - (FETCH_DATA (info, buffer + 4) + (fetch_data(info, buffer + 4) && ((0xff & buffer[2] & (match >> 8)) == (0xff & (opcode >> 8))) && ((0xff & buffer[3] & match) == (0xff & opcode))) ) diff -Nru qemu-kvm-0.12.5+noroms/m68k-semi.c qemu-kvm-0.14.1/m68k-semi.c --- qemu-kvm-0.12.5+noroms/m68k-semi.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/m68k-semi.c 2011-05-11 13:29:46.000000000 +0000 @@ -33,10 +33,10 @@ #define SEMIHOSTING_HEAP_SIZE (128 * 1024 * 1024) #else #include "qemu-common.h" -#include "sysemu.h" #include "gdbstub.h" #include "softmmu-semi.h" #endif +#include "sysemu.h" #define HOSTED_EXIT 0 #define HOSTED_INIT_SIM 1 @@ -172,6 +172,7 @@ args = env->dregs[1]; switch (nr) { case HOSTED_EXIT: + gdb_exit(env, env->dregs[0]); exit(env->dregs[0]); case HOSTED_OPEN: if (use_gdb_syscalls()) { diff -Nru qemu-kvm-0.12.5+noroms/MAINTAINERS qemu-kvm-0.14.1/MAINTAINERS --- qemu-kvm-0.12.5+noroms/MAINTAINERS 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/MAINTAINERS 2011-05-11 13:29:46.000000000 +0000 @@ -1,88 +1,489 @@ QEMU Maintainers ================ -Project leaders: ----------------- +The intention of this file is not to establish who owns what portions of the +code base, but to provide a set of names that developers can consult when they +have a question about a particular subset and also to provide a set of names +to be CC'd when submitting a patch to obtain appropriate review. -Fabrice Bellard -Paul Brook +In general, if you have a question about inclusion of a patch, you should +consult qemu-devel and not any specific individual privately. -CPU cores: ----------- +Descriptions of section entries: -x86 Fabrice Bellard -ARM Paul Brook -SPARC Blue Swirl -MIPS Thiemo Seufer -PowerPC ? -M68K Paul Brook -SH4 ? -CRIS Edgar E. Iglesias -Alpha ? -MicroBlaze Edgar E. Iglesias -S390 ? + M: Mail patches to: FullName + L: Mailing list that is relevant to this area + W: Web-page with status/info + Q: Patchwork web based patch tracking system site + T: SCM tree type and location. Type is one of: git, hg, quilt, stgit. + S: Status, one of the following: + Supported: Someone is actually paid to look after this. + Maintained: Someone actually looks after it. + Odd Fixes: It has a maintainer but they don't have time to do + much other than throw the odd patch in. See below.. + Orphan: No current maintainer [but maybe you could take the + role as you write your new code]. + Obsolete: Old code. Something tagged obsolete generally means + it has been replaced by a better system and you + should be using that. + F: Files and directories with wildcard patterns. + A trailing slash includes all files and subdirectory files. + F: drivers/net/ all files in and below drivers/net + F: drivers/net/* all files in drivers/net, but not below + F: */net/* all files in "any top level directory"/net + One pattern per line. Multiple F: lines acceptable. + X: Files and directories that are NOT maintained, same rules as F: + Files exclusions are tested before file matches. + Can be useful for excluding a specific subdirectory, for instance: + F: net/ + X: net/ipv6/ + matches all files in and below net excluding net/ipv6/ + K: Keyword perl extended regex pattern to match content in a + patch or file. For instance: + K: of_get_profile + matches patches or files that contain "of_get_profile" + K: \b(printk|pr_(info|err))\b + matches patches or files that contain one or more of the words + printk, pr_info or pr_err + One regex pattern per line. Multiple K: lines acceptable. -Machines (sorted by CPU): -------------------------- -x86 - pc.c Fabrice Bellard (new maintainer needed) +General Project Administration +------------------------------ +M: Anthony Liguori +M: Paul Brook + +Guest CPU cores (TCG): +---------------------- +Alpha +M: qemu-devel@nongnu.org +S: Orphan +F: target-alpha/ + ARM - integratorcp.c Paul Brook - versatilepb.c Paul Brook - Real View Paul Brook - spitz.c Andrzej Zaborowski - palm.c Andrzej Zaborowski - nseries.c Andrzej Zaborowski - stellaris.c Paul Brook - gumstix.c Thorsten Zitterell - mainstone.c Armin Kuster - musicpal.c Jan Kiszka -SPARC - sun4u.c Blue Swirl - sun4m.c Blue Swirl +M: Paul Brook +S: Maintained +F: target-arm/ + +CRIS +M: Edgar E. Iglesias +S: Maintained +F: target-cris/ + +M68K +M: Paul Brook +S: Maintained +F: target-m68k/ + +MicroBlaze +M: Edgar E. Iglesias +S: Maintained +F: target-microblaze/ + MIPS - mips_r4k.c Aurelien Jarno - mips_malta.c Aurelien Jarno - mips_jazz.c Hervé Poussineau - mips_mipssim.c Thiemo Seufer +M: Aurelien Jarno +S: Maintained +F: target-mips/ + PowerPC - ppc_prep.c ? - ppc_oldworld.c Fabrice Bellard - ppc_chrp.c Fabrice Bellard - ppc405_boards.c ? -M86K - mcf5208.c Paul Brook - an5206.c Paul Brook - dummy_m68k.c Paul Brook +M: Alexander Graf +S: Maintained +F: target-ppc/ + +S390 +M: Alexander Graf +S: Maintained +F: target-s390x/ + SH4 - shix.c ? - r2d.c Magnus Damm -CRIS - etraxfs.c Edgar E. Iglesias - axis_dev88.c Edgar E. Iglesias -Alpha -MicroBlaze - petalogix_s3adsp1800.c Edgar E. Iglesias +M: Aurelien Jarno +S: Maintained +F: target-sh4/ + +SPARC +M: Blue Swirl +S: Maintained +F: target-sparc/ + +X86 +M: qemu-devel@nongnu.org +S: Odd Fixes +F: target-i386/ + +Guest CPU Cores (KVM): +---------------------- + +Overall +M: Avi Kivity +M: Marcelo Tosatti +L: kvm@vger.kernel.org +S: Supported +F: kvm-* +F: */kvm.* + +PPC +M: Alexander Graf +S: Maintained +F: target-ppc/kvm.c + S390 - s390-*.c Alexander Graf +M: Alexander Graf +S: Maintained +F: target-s390x/kvm.c + +X86 +M: Avi Kivity +M: Marcelo Tosatti +L: kvm@vger.kernel.org +S: Supported +F: target-i386/kvm.c + +ARM Machines +------------ +Gumstix +M: qemu-devel@nongnu.org +S: Orphan +F: hw/gumstix.c + +Integrator CP +M: Paul Brook +S: Maintained +F: hw/integratorcp.c + +Mainstone +M: qemu-devel@nongnu.org +S: Orphan +F: hw/mainstone.c + +Musicpal +M: Jan Kiszka +S: Maintained +F: hw/musicpal.c + +nSeries +M: Andrzej Zaborowski +S: Maintained +F: hw/nseries.c + +Palm +M: Andrzej Zaborowski +S: Maintained +F: hw/palm.c + +Real View +M: Paul Brook +S: Maintained +F: hw/realview* + +Spitz +M: Andrzej Zaborowski +S: Maintained +F: hw/spitz.c + +Stellaris +M: Paul Brook +S: Maintained +F: hw/stellaris.c + +Versatile PB +M: Paul Brook +S: Maintained +F: hw/versatilepb.c + +CRIS Machines +------------- +Axis Dev88 +M: Edgar E. Iglesias +S: Maintained +F: hw/axis_dev88.c + +etraxfs +M: Edgar E. Iglesias +S: Maintained +F: hw/etraxfs.c + +M68K Machines +------------- +an5206 +M: Paul Brook +S: Maintained +F: hw/an5206.c + +dummy_m68k +M: Paul Brook +S: Maintained +F: hw/dummy_m68k.c + +mcf5208 +M: Paul Brook +S: Maintained +F: hw/mcf5208.c + +MicroBlaze Machines +------------------- +petalogix_s3adsp1800 +M: Edgar E. Iglesias +S: Maintained +F: hw/petalogix_s3adsp1800.c + +MIPS Machines +------------- +Jazz +M: Hervé Poussineau +S: Maintained +F: hw/mips_jazz.c + +Malta +M: Aurelien Jarno +S: Maintained +F: hw/mips_malta.c + +Mipssim +M: qemu-devel@nongnu.org +S: Orphan +F: hw/mips_mipssim.c + +R4000 +M: Aurelien Jarno +S: Maintained +F: hw/mips_r4k.c + +PowerPC Machines +---------------- +405 +M: Alexander Graf +S: Maintained +F: hw/ppc405_boards.c + +New World +M: Alexander Graf +S: Maintained +F: hw/ppc_newworld.c + +Old World +M: Alexander Graf +S: Maintained +F: hw/ppc_oldworld.c + +Prep +M: qemu-devel@nongnu.org +S: Orphan +F: hw/ppc_prep.c + +SH4 Machines +------------ +R2D +M: Magnus Damm +S: Maintained +F: hw/r2d.c + +Shix +M: Magnus Damm +S: Orphan +F: hw/shix.c + +SPARC Machines +-------------- +Sun4m +M: Blue Swirl +S: Maintained +F: hw/sun4m.c + +Sun4u +M: Blue Swirl +S: Maintained +F: hw/sun4u.c + +S390 Machines +------------- +S390 Virtio +M: Alexander Graf +S: Maintained +F: hw/s390-*.c + +X86 Machines +------------ +PC +M: Anthony Liguori +S: Supported +F: hw/pc.[ch] hw/pc_piix.c + +Devices +------- +IDE +M: Kevin Wolf +S: Odd Fixes +F: hw/ide/ + +PCI +M: Michael S. Tsirkin +S: Supported +F: hw/pci* +F: hw/piix* + +SCSI +M: Paul Brook +M: Kevin Wolf +S: Odd Fixes +F: hw/lsi53c895a.c +F: hw/scsi* + +USB +M: Gerd Hoffmann +S: Maintained +F: hw/usb* + +vhost +M: Michael S. Tsirkin +S: Supported +F: hw/vhost* + +virtio +M: Anthony Liguori +S: Supported +F: hw/virtio* + +virtio-9p +M: Venkateswararao Jujjuri (JV) +S: Supported +F: hw/virtio-9p* + +virtio-blk +M: Kevin Wolf +S: Supported +F: hw/virtio-blk* + +virtio-serial +M: Amit Shah +S: Supported +F: hw/virtio-serial* +F: hw/virtio-console* + +Subsystems +---------- +Audio +M: Vassili Karpov (malc) +S: Maintained +F: audio/ + +Block +M: Kevin Wolf +S: Supported +F: block* +F: block/ + +Character Devices +M: Anthony Liguori +S: Maintained +F: qemu-char.c + +GDB stub +M: qemu-devel@nongnu.org +S: Odd Fixes +F: gdbstub* +F: gdb-xml/ + +SPICE +M: Gerd Hoffmann +S: Supported +F: ui/qemu-spice.h +F: ui/spice-*.c +F: audio/spiceaudio.c +F: hw/qxl* + +Graphics +M: Anthony Liguori +S: Maintained +F: ui/ + +Main loop +M: Anthony Liguori +S: Supported +F: vl.c + +Monitor (QMP/HMP) +M: Luiz Capitulino +M: Markus Armbruster +S: Supported +F: monitor.c + +Network device layer +M: Anthony Liguori +M: Mark McLoughlin +S: Maintained +F: net/ + +SLIRP +M: qemu-devel@nongnu.org +S: Orphan +F: slirp/ + +Usermode Emulation +------------------ +BSD user +M: Blue Swirl +S: Maintained +F: bsd-user/ + +Darwin user +M: qemu-devel@nongnu.org +S: Orphan +F: darwin-user/ + +Linux user +M: Riku Voipio +S: Maintained +F: linux-user/ + +Tiny Code Generator (TCG) +------------------------- +Common code +M: qemu-devel@nongnu.org +S: Maintained +F: tcg/ + +ARM target +M: Andrzej Zaborowski +S: Maintained +F: tcg/arm/ + +HPPA target +M: Richard Henderson +S: Maintained +F: tcg/hppa/ + +i386 target +M: qemu-devel@nongnu.org +S: Maintained +F: tcg/i386/ + +IA64 target +M: Aurelien Jarno +S: Maintained +F: tcg/ia64/ + +MIPS target +M: Aurelien Jarno +S: Maintained +F: tcg/mips/ + +PPC +M: Vassili Karpov (malc) +S: Maintained +F: tcg/ppc/ + +PPC64 target +M: Vassili Karpov (malc) +S: Maintained +F: tcg/ppc64/ -Generic Subsystems: -------------------- +S390 target +M: Alexander Graf +M: Richard Henderson +S: Maintained +F: tcg/s390/ -Dynamic translator Fabrice Bellard -Main loop Fabrice Bellard (new maintainer needed) -TCG Fabrice Bellard -IDE device ? -SCSI device Paul Brook -PCI layer ? -USB layer ? -Block layer ? -Graphic layer ? -Audio device layer Vassili Karpov (malc) -Character device layer ? -Network device layer ? -GDB stub ? -Linux user ? -Darwin user ? -SLIRP ? +SPARC target +M: Blue Swirl +S: Maintained +F: tcg/sparc/ diff -Nru qemu-kvm-0.12.5+noroms/Makefile qemu-kvm-0.14.1/Makefile --- qemu-kvm-0.12.5+noroms/Makefile 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/Makefile 2011-05-11 13:29:46.000000000 +0000 @@ -1,14 +1,16 @@ # Makefile for QEMU. -# This needs to be defined before rules.mak -GENERATED_HEADERS = config-host.h +GENERATED_HEADERS = config-host.h trace.h qemu-options.def +ifeq ($(TRACE_BACKEND),dtrace) +GENERATED_HEADERS += trace-dtrace.h +endif ifneq ($(wildcard config-host.mak),) # Put the all: rule here so that config-host.mak can contain dependencies. all: build-all include config-host.mak include $(SRC_PATH)/rules.mak -config-host.mak: configure +config-host.mak: $(SRC_PATH)/configure @echo $@ is out-of-date, running configure @sed -n "/.*Configured with/s/[^:]*: //p" $@ | sh else @@ -23,32 +25,45 @@ configure: ; .PHONY: all clean cscope distclean dvi html info install install-doc \ - recurse-all speed tar tarbin test build-all + pdf recurse-all speed tar tarbin test build-all -VPATH=$(SRC_PATH):$(SRC_PATH)/hw +$(call set-vpath, $(SRC_PATH):$(SRC_PATH)/hw) LIBS+=-lz $(LIBS_TOOLS) ifdef BUILD_DOCS -DOCS=qemu-doc.html qemu-tech.html qemu.1 qemu-img.1 qemu-nbd.8 +DOCS=qemu-doc.html qemu-tech.html qemu.1 qemu-img.1 qemu-nbd.8 QMP/qmp-commands.txt else DOCS= endif SUBDIR_MAKEFLAGS=$(if $(V),,--no-print-directory) SUBDIR_DEVICES_MAK=$(patsubst %, %/config-devices.mak, $(TARGET_DIRS)) +SUBDIR_DEVICES_MAK_DEP=$(patsubst %, %/config-devices.mak.d, $(TARGET_DIRS)) config-all-devices.mak: $(SUBDIR_DEVICES_MAK) $(call quiet-command,cat $(SUBDIR_DEVICES_MAK) | grep =y | sort -u > $@," GEN $@") +-include $(SUBDIR_DEVICES_MAK_DEP) + %/config-devices.mak: default-configs/%.mak - $(call quiet-command,cat $< > $@.tmp, " GEN $@") - @if test -f $@ ; then \ - echo "WARNING: $@ out of date." ;\ - echo "Run \"make defconfig\" to regenerate." ; \ - rm $@.tmp ; \ + $(call quiet-command,$(SHELL) $(SRC_PATH)/scripts/make_device_config.sh $@ $<, " GEN $@") + @if test -f $@; then \ + if cmp -s $@.old $@; then \ + mv $@.tmp $@; \ + cp -p $@ $@.old; \ + else \ + if test -f $@.old; then \ + echo "WARNING: $@ (user modified) out of date.";\ + else \ + echo "WARNING: $@ out of date.";\ + fi; \ + echo "Run \"make defconfig\" to regenerate."; \ + rm $@.tmp; \ + fi; \ else \ - mv $@.tmp $@ ; \ + mv $@.tmp $@; \ + cp -p $@ $@.old; \ fi defconfig: @@ -60,30 +75,22 @@ config-host.h: config-host.h-timestamp config-host.h-timestamp: config-host.mak +qemu-options.def: $(SRC_PATH)/qemu-options.hx + $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@," GEN $@") SUBDIR_RULES=$(patsubst %,subdir-%, $(TARGET_DIRS)) -ifeq ($(KVM_KMOD),yes) - -.PHONEY: kvm-kmod - -all: kvm-kmod - -kvm-kmod: - $(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C kvm/kernel V="$(V)" ) - - -endif - subdir-%: $(GENERATED_HEADERS) $(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C $* V="$(V)" TARGET_DIR="$*/" all,) -$(filter %-softmmu,$(SUBDIR_RULES)): libqemu_common.a +ifneq ($(wildcard config-host.mak),) +include $(SRC_PATH)/Makefile.objs +endif -$(filter %-user,$(SUBDIR_RULES)): libuser.a +$(common-obj-y): $(GENERATED_HEADERS) +$(filter %-softmmu,$(SUBDIR_RULES)): $(trace-obj-y) $(common-obj-y) subdir-libdis -libuser.a: $(GENERATED_HEADERS) - $(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C libuser V="$(V)" TARGET_DIR="libuser/" all,) +$(filter %-user,$(SUBDIR_RULES)): $(GENERATED_HEADERS) $(trace-obj-y) subdir-libdis-user subdir-libuser ROMSUBDIR_RULES=$(patsubst %,romsubdir-%, $(ROMS)) romsubdir-%: @@ -93,194 +100,102 @@ recurse-all: $(SUBDIR_RULES) $(ROMSUBDIR_RULES) -####################################################################### -# QObject -qobject-obj-y = qint.o qstring.o qdict.o qlist.o qfloat.o qbool.o -qobject-obj-y += qjson.o json-lexer.o json-streamer.o json-parser.o -qobject-obj-y += qerror.o - -####################################################################### -# block-obj-y is code used by both qemu system emulation and qemu-img - -block-obj-y = cutils.o cache-utils.o qemu-malloc.o qemu-option.o module.o -block-obj-y += nbd.o block.o aio.o aes.o osdep.o -block-obj-$(CONFIG_POSIX) += posix-aio-compat.o -block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o -block-obj-$(CONFIG_POSIX) += compatfd.o - -block-nested-y += cow.o qcow.o vdi.o vmdk.o cloop.o dmg.o bochs.o vpc.o vvfat.o -block-nested-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o -block-nested-y += parallels.o nbd.o -block-nested-$(CONFIG_WIN32) += raw-win32.o -block-nested-$(CONFIG_POSIX) += raw-posix.o -block-nested-$(CONFIG_CURL) += curl.o - -block-obj-y += $(addprefix block/, $(block-nested-y)) - -net-obj-y = net.o -net-nested-y = queue.o checksum.o util.o -net-nested-y += socket.o -net-nested-y += dump.o -net-nested-$(CONFIG_POSIX) += tap.o -net-nested-$(CONFIG_LINUX) += tap-linux.o -net-nested-$(CONFIG_WIN32) += tap-win32.o -net-nested-$(CONFIG_BSD) += tap-bsd.o -net-nested-$(CONFIG_SOLARIS) += tap-solaris.o -net-nested-$(CONFIG_AIX) += tap-aix.o -net-nested-$(CONFIG_SLIRP) += slirp.o -net-nested-$(CONFIG_VDE) += vde.o -net-obj-y += $(addprefix net/, $(net-nested-y)) - -###################################################################### -# libqemu_common.a: Target independent part of system emulation. The -# long term path is to suppress *all* target specific code in case of -# system emulation, i.e. a single QEMU executable should support all -# CPUs and machines. - -obj-y = $(block-obj-y) -obj-y += $(net-obj-y) -obj-y += $(qobject-obj-y) -obj-y += readline.o console.o - -obj-y += tcg-runtime.o host-utils.o -obj-y += irq.o ioport.o -obj-$(CONFIG_PTIMER) += ptimer.o -obj-$(CONFIG_MAX7310) += max7310.o -obj-$(CONFIG_WM8750) += wm8750.o -obj-$(CONFIG_TWL92230) += twl92230.o -obj-$(CONFIG_TSC2005) += tsc2005.o -obj-$(CONFIG_LM832X) += lm832x.o -obj-$(CONFIG_TMP105) += tmp105.o -obj-$(CONFIG_STELLARIS_INPUT) += stellaris_input.o -obj-$(CONFIG_SSD0303) += ssd0303.o -obj-$(CONFIG_SSD0323) += ssd0323.o -obj-$(CONFIG_ADS7846) += ads7846.o -obj-$(CONFIG_MAX111X) += max111x.o -obj-$(CONFIG_DS1338) += ds1338.o -obj-y += i2c.o smbus.o smbus_eeprom.o -obj-y += eeprom93xx.o -obj-y += scsi-disk.o cdrom.o -obj-y += scsi-generic.o scsi-bus.o -obj-y += usb.o usb-hub.o usb-$(HOST_USB).o usb-hid.o usb-msd.o usb-wacom.o -obj-y += usb-serial.o usb-net.o usb-bus.o -obj-$(CONFIG_SSI) += ssi.o -obj-$(CONFIG_SSI_SD) += ssi-sd.o -obj-$(CONFIG_SD) += sd.o -obj-y += bt.o bt-host.o bt-vhci.o bt-l2cap.o bt-sdp.o bt-hci.o bt-hid.o usb-bt.o -obj-y += bt-hci-csr.o -obj-y += buffered_file.o migration.o migration-tcp.o qemu-sockets.o -obj-y += qemu-char.o aio.o savevm.o -obj-y += msmouse.o ps2.o -obj-y += qdev.o qdev-properties.o -obj-y += qemu-config.o block-migration.o - -obj-$(CONFIG_BRLAPI) += baum.o -obj-$(CONFIG_POSIX) += migration-exec.o migration-unix.o migration-fd.o - audio/audio.o audio/fmodaudio.o: QEMU_CFLAGS += $(FMOD_CFLAGS) -audio-obj-y = audio.o noaudio.o wavaudio.o mixeng.o -audio-obj-$(CONFIG_SDL) += sdlaudio.o -audio-obj-$(CONFIG_OSS) += ossaudio.o -audio-obj-$(CONFIG_COREAUDIO) += coreaudio.o -audio-obj-$(CONFIG_ALSA) += alsaaudio.o -audio-obj-$(CONFIG_DSOUND) += dsoundaudio.o -audio-obj-$(CONFIG_FMOD) += fmodaudio.o -audio-obj-$(CONFIG_ESD) += esdaudio.o -audio-obj-$(CONFIG_PA) += paaudio.o -audio-obj-$(CONFIG_WINWAVE) += winwaveaudio.o -audio-obj-$(CONFIG_AUDIO_PT_INT) += audio_pt_int.o -audio-obj-$(CONFIG_AUDIO_WIN_INT) += audio_win_int.o -audio-obj-y += wavcapture.o -obj-y += $(addprefix audio/, $(audio-obj-y)) - -obj-y += keymaps.o -obj-$(CONFIG_SDL) += sdl.o sdl_zoom.o x_keymap.o -obj-$(CONFIG_CURSES) += curses.o -obj-y += vnc.o acl.o d3des.o -obj-$(CONFIG_VNC_TLS) += vnc-tls.o vnc-auth-vencrypt.o -obj-$(CONFIG_VNC_SASL) += vnc-auth-sasl.o -obj-$(CONFIG_COCOA) += cocoa.o -obj-$(CONFIG_IOTHREAD) += qemu-thread.o - -slirp-obj-y = cksum.o if.o ip_icmp.o ip_input.o ip_output.o -slirp-obj-y += slirp.o mbuf.o misc.o sbuf.o socket.o tcp_input.o tcp_output.o -slirp-obj-y += tcp_subr.o tcp_timer.o udp.o bootp.o tftp.o -obj-$(CONFIG_SLIRP) += $(addprefix slirp/, $(slirp-obj-y)) - -# xen backend driver support -obj-$(CONFIG_XEN) += xen_backend.o xen_devconfig.o -obj-$(CONFIG_XEN) += xen_console.o xenfb.o xen_disk.o xen_nic.o - QEMU_CFLAGS+=$(CURL_CFLAGS) -cocoa.o: cocoa.m - -keymaps.o: keymaps.c keymaps.h - -sdl_zoom.o: sdl_zoom.c sdl_zoom.h sdl_zoom_template.h +ui/cocoa.o: ui/cocoa.m -sdl.o: sdl.c keymaps.h sdl_keysym.h sdl_zoom.h +ui/sdl.o audio/sdlaudio.o ui/sdl_zoom.o baum.o: QEMU_CFLAGS += $(SDL_CFLAGS) -sdl.o audio/sdlaudio.o sdl_zoom.o baum.o: QEMU_CFLAGS += $(SDL_CFLAGS) +ui/vnc.o: QEMU_CFLAGS += $(VNC_TLS_CFLAGS) -acl.o: acl.h acl.c - -vnc.h: vnc-tls.h vnc-auth-vencrypt.h vnc-auth-sasl.h keymaps.h +bt-host.o: QEMU_CFLAGS += $(BLUEZ_CFLAGS) -vnc.o: vnc.c vnc.h vnc_keysym.h vnchextile.h d3des.c d3des.h acl.h +ifeq ($(TRACE_BACKEND),dtrace) +trace.h: trace.h-timestamp trace-dtrace.h +else +trace.h: trace.h-timestamp +endif +trace.h-timestamp: $(SRC_PATH)/trace-events config-host.mak + $(call quiet-command,sh $(SRC_PATH)/scripts/tracetool --$(TRACE_BACKEND) -h < $< > $@," GEN trace.h") + @cmp -s $@ trace.h || cp $@ trace.h -vnc.o: QEMU_CFLAGS += $(VNC_TLS_CFLAGS) +trace.c: trace.c-timestamp +trace.c-timestamp: $(SRC_PATH)/trace-events config-host.mak + $(call quiet-command,sh $(SRC_PATH)/scripts/tracetool --$(TRACE_BACKEND) -c < $< > $@," GEN trace.c") + @cmp -s $@ trace.c || cp $@ trace.c -vnc-tls.o: vnc-tls.c vnc.h +trace.o: trace.c $(GENERATED_HEADERS) -vnc-auth-vencrypt.o: vnc-auth-vencrypt.c vnc.h +trace-dtrace.h: trace-dtrace.dtrace + $(call quiet-command,dtrace -o $@ -h -s $<, " GEN trace-dtrace.h") -vnc-auth-sasl.o: vnc-auth-sasl.c vnc.h +# Normal practice is to name DTrace probe file with a '.d' extension +# but that gets picked up by QEMU's Makefile as an external dependancy +# rule file. So we use '.dtrace' instead +trace-dtrace.dtrace: trace-dtrace.dtrace-timestamp +trace-dtrace.dtrace-timestamp: $(SRC_PATH)/trace-events config-host.mak + $(call quiet-command,sh $(SRC_PATH)/scripts/tracetool --$(TRACE_BACKEND) -d < $< > $@," GEN trace-dtrace.dtrace") + @cmp -s $@ trace-dtrace.dtrace || cp $@ trace-dtrace.dtrace -curses.o: curses.c keymaps.h curses_keys.h +trace-dtrace.o: trace-dtrace.dtrace $(GENERATED_HEADERS) + $(call quiet-command,dtrace -o $@ -G -s $<, " GEN trace-dtrace.o") -bt-host.o: QEMU_CFLAGS += $(BLUEZ_CFLAGS) +simpletrace.o: simpletrace.c $(GENERATED_HEADERS) -libqemu_common.a: $(obj-y) +version.o: $(SRC_PATH)/version.rc config-host.mak + $(call quiet-command,$(WINDRES) -I. -o $@ $<," RC $(TARGET_DIR)$@") +version-obj-$(CONFIG_WIN32) += version.o ###################################################################### qemu-img.o: qemu-img-cmds.h +qemu-img.o qemu-tool.o qemu-nbd.o qemu-io.o cmd.o: $(GENERATED_HEADERS) -qemu-img$(EXESUF): qemu-img.o qemu-tool.o $(block-obj-y) $(qobject-obj-y) +qemu-img$(EXESUF): qemu-img.o qemu-tool.o qemu-error.o $(oslib-obj-y) $(trace-obj-y) $(block-obj-y) $(qobject-obj-y) $(version-obj-y) qemu-timer-common.o -qemu-nbd$(EXESUF): qemu-nbd.o qemu-tool.o $(block-obj-y) $(qobject-obj-y) +qemu-nbd$(EXESUF): qemu-nbd.o qemu-tool.o qemu-error.o $(oslib-obj-y) $(trace-obj-y) $(block-obj-y) $(qobject-obj-y) $(version-obj-y) qemu-timer-common.o -qemu-io$(EXESUF): qemu-io.o qemu-tool.o cmd.o $(block-obj-y) $(qobject-obj-y) +qemu-io$(EXESUF): qemu-io.o cmd.o qemu-tool.o qemu-error.o $(oslib-obj-y) $(trace-obj-y) $(block-obj-y) $(qobject-obj-y) $(version-obj-y) qemu-timer-common.o qemu-img-cmds.h: $(SRC_PATH)/qemu-img-cmds.hx - $(call quiet-command,sh $(SRC_PATH)/hxtool -h < $< > $@," GEN $@") + $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@," GEN $@") -check-qint: check-qint.o qint.o qemu-malloc.o -check-qstring: check-qstring.o qstring.o qemu-malloc.o -check-qdict: check-qdict.o qdict.o qint.o qstring.o qbool.o qemu-malloc.o qlist.o -check-qlist: check-qlist.o qlist.o qint.o qemu-malloc.o -check-qfloat: check-qfloat.o qfloat.o qemu-malloc.o -check-qjson: check-qjson.o qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o qjson.o json-streamer.o json-lexer.o json-parser.o qemu-malloc.o +check-qint.o check-qstring.o check-qdict.o check-qlist.o check-qfloat.o check-qjson.o: $(GENERATED_HEADERS) + +CHECK_PROG_DEPS = qemu-malloc.o $(oslib-obj-y) $(trace-obj-y) + +check-qint: check-qint.o qint.o $(CHECK_PROG_DEPS) +check-qstring: check-qstring.o qstring.o $(CHECK_PROG_DEPS) +check-qdict: check-qdict.o qdict.o qfloat.o qint.o qstring.o qbool.o qlist.o $(CHECK_PROG_DEPS) +check-qlist: check-qlist.o qlist.o qint.o $(CHECK_PROG_DEPS) +check-qfloat: check-qfloat.o qfloat.o $(CHECK_PROG_DEPS) +check-qjson: check-qjson.o qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o qjson.o json-streamer.o json-lexer.o json-parser.o $(CHECK_PROG_DEPS) clean: # avoid old build problems by removing potentially incorrect old files rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h + rm -f qemu-options.def rm -f *.o *.d *.a $(TOOLS) TAGS cscope.* *.pod *~ */*~ - rm -f slirp/*.o slirp/*.d audio/*.o audio/*.d block/*.o block/*.d net/*.o net/*.d + rm -f slirp/*.o slirp/*.d audio/*.o audio/*.d block/*.o block/*.d net/*.o net/*.d fsdev/*.o fsdev/*.d ui/*.o ui/*.d rm -f qemu-img-cmds.h + rm -f trace.c trace.h trace.c-timestamp trace.h-timestamp + rm -f trace-dtrace.dtrace trace-dtrace.dtrace-timestamp + rm -f trace-dtrace.h trace-dtrace.h-timestamp $(MAKE) -C tests clean - for d in $(ALL_SUBDIRS) libhw32 libhw64 libuser; do \ + for d in $(ALL_SUBDIRS) libhw32 libhw64 libuser libdis libdis-user; do \ if test -d $$d; then $(MAKE) -C $$d $@ || exit 1; fi; \ + rm -f $$d/qemu-options.def; \ done distclean: clean rm -f config-host.mak config-host.h* config-host.ld $(DOCS) qemu-options.texi qemu-img-cmds.texi qemu-monitor.texi rm -f config-all-devices.mak rm -f roms/seabios/config.mak roms/vgabios/config.mak - rm -f qemu-{doc,tech}.{info,aux,cp,dvi,fn,info,ky,log,pg,toc,tp,vr} - for d in $(TARGET_DIRS) libhw32 libhw64 libuser; do \ + rm -f qemu-doc.info qemu-doc.aux qemu-doc.cp qemu-doc.dvi qemu-doc.fn qemu-doc.info qemu-doc.ky qemu-doc.log qemu-doc.pdf qemu-doc.pg qemu-doc.toc qemu-doc.tp qemu-doc.vr + rm -f qemu-tech.info qemu-tech.aux qemu-tech.cp qemu-tech.dvi qemu-tech.fn qemu-tech.info qemu-tech.ky qemu-tech.log qemu-tech.pdf qemu-tech.pg qemu-tech.toc qemu-tech.tp qemu-tech.vr + for d in $(TARGET_DIRS) libhw32 libhw64 libuser libdis libdis-user; do \ rm -rf $$d || exit 1 ; \ done @@ -289,13 +204,16 @@ common de-ch es fo fr-ca hu ja mk nl-be pt sl tr ifdef INSTALL_BLOBS -BLOBS=bios.bin vgabios.bin vgabios-cirrus.bin ppc_rom.bin \ -video.x openbios-sparc32 openbios-sparc64 openbios-ppc \ -pxe-e1000.bin pxe-i82559er.bin \ +BLOBS=bios.bin vgabios.bin vgabios-cirrus.bin \ +vgabios-stdvga.bin vgabios-vmware.bin vgabios-qxl.bin \ +ppc_rom.bin openbios-sparc32 openbios-sparc64 openbios-ppc \ +gpxe-eepro100-80861209.rom \ +pxe-e1000.bin \ pxe-ne2k_pci.bin pxe-pcnet.bin \ pxe-rtl8139.bin pxe-virtio.bin \ bamboo.dtb petalogix-s3adsp1800.dtb \ -multiboot.bin linuxboot.bin +multiboot.bin linuxboot.bin \ +s390-zipl.rom BLOBS += extboot.bin BLOBS += vapic.bin else @@ -312,7 +230,11 @@ $(INSTALL_DATA) qemu-nbd.8 "$(DESTDIR)$(mandir)/man8" endif -install: all $(if $(BUILD_DOCS),install-doc) +install-sysconfig: + $(INSTALL_DIR) "$(DESTDIR)$(sysconfdir)/qemu" + $(INSTALL_DATA) $(SRC_PATH)/sysconfigs/target/target-x86_64.conf "$(DESTDIR)$(sysconfdir)/qemu" + +install: all $(if $(BUILD_DOCS),install-doc) install-sysconfig $(INSTALL_DIR) "$(DESTDIR)$(bindir)" ifneq ($(TOOLS),) $(INSTALL_PROG) $(STRIP_OPT) $(TOOLS) "$(DESTDIR)$(bindir)" @@ -335,9 +257,6 @@ for d in $(TARGET_DIRS); do \ $(MAKE) -C $$d $@ || exit 1 ; \ done -ifeq ($(KVM_KMOD),yes) - $(MAKE) -C kvm/kernel $@ -endif # various test targets test speed: all @@ -353,49 +272,60 @@ cscope -b # documentation +MAKEINFO=makeinfo +MAKEINFOFLAGS=--no-headers --no-split --number-sections +TEXIFLAG=$(if $(V),,--quiet) +%.dvi: %.texi + $(call quiet-command,texi2dvi $(TEXIFLAG) -I . $<," GEN $@") + %.html: %.texi - $(call quiet-command,texi2html -I=. -monolithic -number $<," GEN $@") + $(call quiet-command,$(MAKEINFO) $(MAKEINFOFLAGS) --html $< -o $@, \ + " GEN $@") %.info: %.texi - $(call quiet-command,makeinfo -I . $< -o $@," GEN $@") + $(call quiet-command,$(MAKEINFO) $< -o $@," GEN $@") -%.dvi: %.texi - $(call quiet-command,texi2dvi -I . $<," GEN $@") +%.pdf: %.texi + $(call quiet-command,texi2pdf $(TEXIFLAG) -I . $<," GEN $@") qemu-options.texi: $(SRC_PATH)/qemu-options.hx - $(call quiet-command,sh $(SRC_PATH)/hxtool -t < $< > $@," GEN $@") + $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@," GEN $@") -qemu-monitor.texi: $(SRC_PATH)/qemu-monitor.hx - $(call quiet-command,sh $(SRC_PATH)/hxtool -t < $< > $@," GEN $@") +qemu-monitor.texi: $(SRC_PATH)/hmp-commands.hx + $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@," GEN $@") + +QMP/qmp-commands.txt: $(SRC_PATH)/qmp-commands.hx + $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -q < $< > $@," GEN $@") qemu-img-cmds.texi: $(SRC_PATH)/qemu-img-cmds.hx - $(call quiet-command,sh $(SRC_PATH)/hxtool -t < $< > $@," GEN $@") + $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@," GEN $@") qemu.1: qemu-doc.texi qemu-options.texi qemu-monitor.texi $(call quiet-command, \ - perl -Ww -- $(SRC_PATH)/texi2pod.pl $< qemu.pod && \ + perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< qemu.pod && \ pod2man --section=1 --center=" " --release=" " qemu.pod > $@, \ " GEN $@") qemu-img.1: qemu-img.texi qemu-img-cmds.texi $(call quiet-command, \ - perl -Ww -- $(SRC_PATH)/texi2pod.pl $< qemu-img.pod && \ + perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< qemu-img.pod && \ pod2man --section=1 --center=" " --release=" " qemu-img.pod > $@, \ " GEN $@") qemu-nbd.8: qemu-nbd.texi $(call quiet-command, \ - perl -Ww -- $(SRC_PATH)/texi2pod.pl $< qemu-nbd.pod && \ + perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< qemu-nbd.pod && \ pod2man --section=8 --center=" " --release=" " qemu-nbd.pod > $@, \ " GEN $@") -info: qemu-doc.info qemu-tech.info - dvi: qemu-doc.dvi qemu-tech.dvi - html: qemu-doc.html qemu-tech.html +info: qemu-doc.info qemu-tech.info +pdf: qemu-doc.pdf qemu-tech.pdf -qemu-doc.dvi qemu-doc.html qemu-doc.info: qemu-img.texi qemu-nbd.texi qemu-options.texi qemu-monitor.texi qemu-img-cmds.texi +qemu-doc.dvi qemu-doc.html qemu-doc.info qemu-doc.pdf: \ + qemu-img.texi qemu-nbd.texi qemu-options.texi \ + qemu-monitor.texi qemu-img-cmds.texi VERSION ?= $(shell cat VERSION) FILE = qemu-$(VERSION) @@ -407,50 +337,28 @@ cd /tmp && tar zcvf ~/$(FILE).tar.gz $(FILE) --exclude CVS --exclude .git --exclude .svn rm -rf /tmp/$(FILE) +SYSTEM_TARGETS=$(filter %-softmmu,$(TARGET_DIRS)) +SYSTEM_PROGS=$(patsubst qemu-system-i386,qemu, \ + $(patsubst %-softmmu,qemu-system-%, \ + $(SYSTEM_TARGETS))) + +USER_TARGETS=$(filter %-user,$(TARGET_DIRS)) +USER_PROGS=$(patsubst %-bsd-user,qemu-%, \ + $(patsubst %-darwin-user,qemu-%, \ + $(patsubst %-linux-user,qemu-%, \ + $(USER_TARGETS)))) + # generate a binary distribution tarbin: cd / && tar zcvf ~/qemu-$(VERSION)-$(ARCH).tar.gz \ - $(bindir)/qemu \ - $(bindir)/qemu-system-x86_64 \ - $(bindir)/qemu-system-arm \ - $(bindir)/qemu-system-cris \ - $(bindir)/qemu-system-m68k \ - $(bindir)/qemu-system-microblaze \ - $(bindir)/qemu-system-mips \ - $(bindir)/qemu-system-mipsel \ - $(bindir)/qemu-system-mips64 \ - $(bindir)/qemu-system-mips64el \ - $(bindir)/qemu-system-ppc \ - $(bindir)/qemu-system-ppcemb \ - $(bindir)/qemu-system-ppc64 \ - $(bindir)/qemu-system-sh4 \ - $(bindir)/qemu-system-sh4eb \ - $(bindir)/qemu-system-sparc \ - $(bindir)/qemu-i386 \ - $(bindir)/qemu-x86_64 \ - $(bindir)/qemu-alpha \ - $(bindir)/qemu-arm \ - $(bindir)/qemu-armeb \ - $(bindir)/qemu-cris \ - $(bindir)/qemu-m68k \ - $(bindir)/qemu-microblaze \ - $(bindir)/qemu-mips \ - $(bindir)/qemu-mipsel \ - $(bindir)/qemu-ppc \ - $(bindir)/qemu-ppc64 \ - $(bindir)/qemu-ppc64abi32 \ - $(bindir)/qemu-sh4 \ - $(bindir)/qemu-sh4eb \ - $(bindir)/qemu-sparc \ - $(bindir)/qemu-sparc64 \ - $(bindir)/qemu-sparc32plus \ + $(patsubst %,$(bindir)/%, $(SYSTEM_PROGS)) \ + $(patsubst %,$(bindir)/%, $(USER_PROGS)) \ $(bindir)/qemu-img \ $(bindir)/qemu-nbd \ $(datadir)/bios.bin \ $(datadir)/vgabios.bin \ $(datadir)/vgabios-cirrus.bin \ $(datadir)/ppc_rom.bin \ - $(datadir)/video.x \ $(datadir)/openbios-sparc32 \ $(datadir)/openbios-sparc64 \ $(datadir)/openbios-ppc \ @@ -466,4 +374,4 @@ $(mandir)/man8/qemu-nbd.8 # Include automatically generated dependency files --include $(wildcard *.d audio/*.d slirp/*.d block/*.d net/*.d) +-include $(wildcard *.d audio/*.d slirp/*.d block/*.d net/*.d ui/*.d) diff -Nru qemu-kvm-0.12.5+noroms/Makefile.dis qemu-kvm-0.14.1/Makefile.dis --- qemu-kvm-0.12.5+noroms/Makefile.dis 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/Makefile.dis 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,23 @@ +# Makefile for disassemblers. + +include ../config-host.mak +include config.mak +include $(SRC_PATH)/rules.mak + +.PHONY: all + +$(call set-vpath, $(SRC_PATH)) + +QEMU_CFLAGS+=-I.. + +include $(SRC_PATH)/Makefile.objs + +all: $(libdis-y) +# Dummy command so that make thinks it has done something + @true + +clean: + rm -f *.o *.d *.a *~ + +# Include automatically generated dependency files +-include $(wildcard *.d */*.d) diff -Nru qemu-kvm-0.12.5+noroms/Makefile.hw qemu-kvm-0.14.1/Makefile.hw --- qemu-kvm-0.12.5+noroms/Makefile.hw 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/Makefile.hw 2011-05-11 13:29:46.000000000 +0000 @@ -7,49 +7,18 @@ .PHONY: all -VPATH=$(SRC_PATH):$(SRC_PATH)/hw +$(call set-vpath, $(SRC_PATH):$(SRC_PATH)/hw) QEMU_CFLAGS+=-I.. -I$(SRC_PATH)/fpu -obj-y = -obj-y += loader.o -obj-y += virtio.o -obj-y += fw_cfg.o -obj-y += watchdog.o -obj-$(CONFIG_ECC) += ecc.o -obj-$(CONFIG_NAND) += nand.o - -obj-$(CONFIG_M48T59) += m48t59.o -obj-$(CONFIG_ESCC) += escc.o - -# PCI watchdog devices -obj-y += wdt_i6300esb.o - -# MSI-X depends on kvm for interrupt injection, -# so moved it from Makefile.hw to Makefile.target for now -# obj-y += msix.o - -# PCI network cards -obj-y += ne2000.o - -obj-$(CONFIG_SMC91C111) += smc91c111.o -obj-$(CONFIG_LAN9118) += lan9118.o - -# SCSI layer -obj-y += lsi53c895a.o -obj-$(CONFIG_ESP) += esp.o +include $(SRC_PATH)/Makefile.objs -obj-y += dma-helpers.o sysbus.o isa-bus.o -obj-$(CONFIG_QDEV_ADDR) += qdev-addr.o - -all: $(HWLIB) +all: $(hw-obj-y) # Dummy command so that make thinks it has done something @true -$(HWLIB): $(obj-y) - clean: - rm -f *.o *.d *.a *~ + rm -f *.o */*.o *.d */*.d *.a */*.a *~ */*~ # Include automatically generated dependency files -include $(wildcard *.d */*.d) diff -Nru qemu-kvm-0.12.5+noroms/Makefile.objs qemu-kvm-0.14.1/Makefile.objs --- qemu-kvm-0.12.5+noroms/Makefile.objs 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/Makefile.objs 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,322 @@ +####################################################################### +# QObject +qobject-obj-y = qint.o qstring.o qdict.o qlist.o qfloat.o qbool.o +qobject-obj-y += qjson.o json-lexer.o json-streamer.o json-parser.o +qobject-obj-y += qerror.o + +####################################################################### +# oslib-obj-y is code depending on the OS (win32 vs posix) +oslib-obj-y = osdep.o +oslib-obj-$(CONFIG_WIN32) += oslib-win32.o +oslib-obj-$(CONFIG_POSIX) += oslib-posix.o + +####################################################################### +# block-obj-y is code used by both qemu system emulation and qemu-img + +block-obj-y = cutils.o cache-utils.o qemu-malloc.o qemu-option.o module.o +block-obj-y += nbd.o block.o aio.o aes.o qemu-config.o +block-obj-$(CONFIG_POSIX) += posix-aio-compat.o +block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o +block-obj-$(CONFIG_POSIX) += compatfd.o + +block-nested-y += raw.o cow.o qcow.o vdi.o vmdk.o cloop.o dmg.o bochs.o vpc.o vvfat.o +block-nested-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-cache.o +block-nested-y += qed.o qed-gencb.o qed-l2-cache.o qed-table.o qed-cluster.o +block-nested-y += qed-check.o +block-nested-y += parallels.o nbd.o blkdebug.o sheepdog.o blkverify.o +block-nested-$(CONFIG_WIN32) += raw-win32.o +block-nested-$(CONFIG_POSIX) += raw-posix.o +block-nested-$(CONFIG_CURL) += curl.o +block-nested-$(CONFIG_RBD) += rbd.o + +block-obj-y += $(addprefix block/, $(block-nested-y)) + +net-obj-y = net.o +net-nested-y = queue.o checksum.o util.o +net-nested-y += socket.o +net-nested-y += dump.o +net-nested-$(CONFIG_POSIX) += tap.o +net-nested-$(CONFIG_LINUX) += tap-linux.o +net-nested-$(CONFIG_WIN32) += tap-win32.o +net-nested-$(CONFIG_BSD) += tap-bsd.o +net-nested-$(CONFIG_SOLARIS) += tap-solaris.o +net-nested-$(CONFIG_AIX) += tap-aix.o +net-nested-$(CONFIG_HAIKU) += tap-haiku.o +net-nested-$(CONFIG_SLIRP) += slirp.o +net-nested-$(CONFIG_VDE) += vde.o +net-obj-y += $(addprefix net/, $(net-nested-y)) + +ifeq ($(CONFIG_VIRTIO)$(CONFIG_VIRTFS),yy) +# Lots of the fsdev/9pcode is pulled in by vl.c via qemu_fsdev_add. +# only pull in the actual virtio-9p device if we also enabled virtio. +CONFIG_REALLY_VIRTFS=y +endif +fsdev-nested-$(CONFIG_VIRTFS) = qemu-fsdev.o +fsdev-obj-$(CONFIG_VIRTFS) += $(addprefix fsdev/, $(fsdev-nested-y)) + +###################################################################### +# libqemu_common.a: Target independent part of system emulation. The +# long term path is to suppress *all* target specific code in case of +# system emulation, i.e. a single QEMU executable should support all +# CPUs and machines. + +common-obj-y = $(block-obj-y) blockdev.o +common-obj-y += $(net-obj-y) +common-obj-y += $(qobject-obj-y) +common-obj-$(CONFIG_LINUX) += $(fsdev-obj-$(CONFIG_LINUX)) +common-obj-y += readline.o console.o cursor.o async.o qemu-error.o +common-obj-y += $(oslib-obj-y) +common-obj-$(CONFIG_WIN32) += os-win32.o +common-obj-$(CONFIG_POSIX) += os-posix.o + +common-obj-y += tcg-runtime.o host-utils.o +common-obj-y += irq.o ioport.o input.o +common-obj-$(CONFIG_PTIMER) += ptimer.o +common-obj-$(CONFIG_MAX7310) += max7310.o +common-obj-$(CONFIG_WM8750) += wm8750.o +common-obj-$(CONFIG_TWL92230) += twl92230.o +common-obj-$(CONFIG_TSC2005) += tsc2005.o +common-obj-$(CONFIG_LM832X) += lm832x.o +common-obj-$(CONFIG_TMP105) += tmp105.o +common-obj-$(CONFIG_STELLARIS_INPUT) += stellaris_input.o +common-obj-$(CONFIG_SSD0303) += ssd0303.o +common-obj-$(CONFIG_SSD0323) += ssd0323.o +common-obj-$(CONFIG_ADS7846) += ads7846.o +common-obj-$(CONFIG_MAX111X) += max111x.o +common-obj-$(CONFIG_DS1338) += ds1338.o +common-obj-y += i2c.o smbus.o smbus_eeprom.o +common-obj-y += eeprom93xx.o +common-obj-y += scsi-disk.o cdrom.o +common-obj-y += scsi-generic.o scsi-bus.o +common-obj-y += usb.o usb-hub.o usb-$(HOST_USB).o usb-hid.o usb-msd.o usb-wacom.o +common-obj-y += usb-serial.o usb-net.o usb-bus.o usb-desc.o +common-obj-$(CONFIG_SSI) += ssi.o +common-obj-$(CONFIG_SSI_SD) += ssi-sd.o +common-obj-$(CONFIG_SD) += sd.o +common-obj-y += bt.o bt-host.o bt-vhci.o bt-l2cap.o bt-sdp.o bt-hci.o bt-hid.o usb-bt.o +common-obj-y += bt-hci-csr.o +common-obj-y += buffered_file.o migration.o migration-tcp.o qemu-sockets.o +common-obj-y += qemu-char.o savevm.o #aio.o +common-obj-y += msmouse.o ps2.o +common-obj-y += qdev.o qdev-properties.o +common-obj-y += block-migration.o +common-obj-y += pflib.o + +common-obj-$(CONFIG_BRLAPI) += baum.o +common-obj-$(CONFIG_POSIX) += migration-exec.o migration-unix.o migration-fd.o +common-obj-$(CONFIG_WIN32) += version.o + +common-obj-$(CONFIG_SPICE) += ui/spice-core.o ui/spice-input.o ui/spice-display.o spice-qemu-char.o + +audio-obj-y = audio.o noaudio.o wavaudio.o mixeng.o +audio-obj-$(CONFIG_SDL) += sdlaudio.o +audio-obj-$(CONFIG_OSS) += ossaudio.o +audio-obj-$(CONFIG_SPICE) += spiceaudio.o +audio-obj-$(CONFIG_COREAUDIO) += coreaudio.o +audio-obj-$(CONFIG_ALSA) += alsaaudio.o +audio-obj-$(CONFIG_DSOUND) += dsoundaudio.o +audio-obj-$(CONFIG_FMOD) += fmodaudio.o +audio-obj-$(CONFIG_ESD) += esdaudio.o +audio-obj-$(CONFIG_PA) += paaudio.o +audio-obj-$(CONFIG_WINWAVE) += winwaveaudio.o +audio-obj-$(CONFIG_AUDIO_PT_INT) += audio_pt_int.o +audio-obj-$(CONFIG_AUDIO_WIN_INT) += audio_win_int.o +audio-obj-y += wavcapture.o +common-obj-y += $(addprefix audio/, $(audio-obj-y)) + +ui-obj-y += keymaps.o +ui-obj-$(CONFIG_SDL) += sdl.o sdl_zoom.o x_keymap.o +ui-obj-$(CONFIG_CURSES) += curses.o +ui-obj-y += vnc.o d3des.o +ui-obj-y += vnc-enc-zlib.o vnc-enc-hextile.o +ui-obj-y += vnc-enc-tight.o vnc-palette.o +ui-obj-$(CONFIG_VNC_TLS) += vnc-tls.o vnc-auth-vencrypt.o +ui-obj-$(CONFIG_VNC_SASL) += vnc-auth-sasl.o +ui-obj-$(CONFIG_COCOA) += cocoa.o +ifdef CONFIG_VNC_THREAD +ui-obj-y += vnc-jobs-async.o +else +ui-obj-y += vnc-jobs-sync.o +endif +common-obj-y += $(addprefix ui/, $(ui-obj-y)) + +common-obj-y += iov.o acl.o +common-obj-$(CONFIG_THREAD) += qemu-thread.o +common-obj-$(CONFIG_IOTHREAD) += compatfd.o +common-obj-y += notify.o event_notifier.o +common-obj-y += qemu-timer.o qemu-timer-common.o + +slirp-obj-y = cksum.o if.o ip_icmp.o ip_input.o ip_output.o +slirp-obj-y += slirp.o mbuf.o misc.o sbuf.o socket.o tcp_input.o tcp_output.o +slirp-obj-y += tcp_subr.o tcp_timer.o udp.o bootp.o tftp.o +common-obj-$(CONFIG_SLIRP) += $(addprefix slirp/, $(slirp-obj-y)) + +# xen backend driver support +common-obj-$(CONFIG_XEN) += xen_backend.o xen_devconfig.o +common-obj-$(CONFIG_XEN) += xen_console.o xenfb.o xen_disk.o xen_nic.o + +###################################################################### +# libuser + +user-obj-y = +user-obj-y += envlist.o path.o +user-obj-y += tcg-runtime.o host-utils.o +user-obj-y += cutils.o cache-utils.o + +###################################################################### +# libhw + +hw-obj-y = +hw-obj-y += loader.o +hw-obj-$(CONFIG_VIRTIO) += virtio.o virtio-console.o +hw-obj-y += fw_cfg.o +hw-obj-$(CONFIG_PCI) += pci_bridge.o +hw-obj-$(CONFIG_PCI) += msix.o msi.o +hw-obj-$(CONFIG_PCI) += pci_host.o pcie_host.o +hw-obj-$(CONFIG_PCI) += ioh3420.o xio3130_upstream.o xio3130_downstream.o +hw-obj-y += watchdog.o +hw-obj-$(CONFIG_ISA_MMIO) += isa_mmio.o +hw-obj-$(CONFIG_ECC) += ecc.o +hw-obj-$(CONFIG_NAND) += nand.o +hw-obj-$(CONFIG_PFLASH_CFI01) += pflash_cfi01.o +hw-obj-$(CONFIG_PFLASH_CFI02) += pflash_cfi02.o + +hw-obj-$(CONFIG_M48T59) += m48t59.o +hw-obj-$(CONFIG_ESCC) += escc.o +hw-obj-$(CONFIG_EMPTY_SLOT) += empty_slot.o + +hw-obj-$(CONFIG_SERIAL) += serial.o +hw-obj-$(CONFIG_PARALLEL) += parallel.o +# Moved back to Makefile.target due to #include qemu-kvm.h: +#hw-obj-$(CONFIG_I8254) += i8254.o +#hw-obj-$(CONFIG_PCSPK) += pcspk.o +hw-obj-$(CONFIG_PCKBD) += pckbd.o +hw-obj-$(CONFIG_USB_UHCI) += usb-uhci.o +hw-obj-$(CONFIG_USB_OHCI) += usb-ohci.o +hw-obj-$(CONFIG_FDC) += fdc.o +# needs fixes for cpu hotplug, so moved to Makefile.target: +# hw-obj-$(CONFIG_ACPI) += acpi.o acpi_piix4.o +hw-obj-$(CONFIG_APM) += pm_smbus.o apm.o +hw-obj-$(CONFIG_DMA) += dma.o + +# PPC devices +hw-obj-$(CONFIG_OPENPIC) += openpic.o +hw-obj-$(CONFIG_PREP_PCI) += prep_pci.o +# Mac shared devices +hw-obj-$(CONFIG_MACIO) += macio.o +hw-obj-$(CONFIG_CUDA) += cuda.o +hw-obj-$(CONFIG_ADB) += adb.o +hw-obj-$(CONFIG_MAC_NVRAM) += mac_nvram.o +hw-obj-$(CONFIG_MAC_DBDMA) += mac_dbdma.o +# OldWorld PowerMac +hw-obj-$(CONFIG_HEATHROW_PIC) += heathrow_pic.o +hw-obj-$(CONFIG_GRACKLE_PCI) += grackle_pci.o +# NewWorld PowerMac +hw-obj-$(CONFIG_UNIN_PCI) += unin_pci.o +hw-obj-$(CONFIG_DEC_PCI) += dec_pci.o +# PowerPC E500 boards +hw-obj-$(CONFIG_PPCE500_PCI) += ppce500_pci.o + +# MIPS devices +hw-obj-$(CONFIG_PIIX4) += piix4.o + +# PCI watchdog devices +hw-obj-$(CONFIG_PCI) += wdt_i6300esb.o + +hw-obj-$(CONFIG_PCI) += pcie.o pcie_aer.o pcie_port.o + +# PCI network cards +hw-obj-$(CONFIG_NE2000_PCI) += ne2000.o +hw-obj-$(CONFIG_EEPRO100_PCI) += eepro100.o +hw-obj-$(CONFIG_PCNET_PCI) += pcnet-pci.o +hw-obj-$(CONFIG_PCNET_COMMON) += pcnet.o +hw-obj-$(CONFIG_E1000_PCI) += e1000.o +hw-obj-$(CONFIG_RTL8139_PCI) += rtl8139.o + +hw-obj-$(CONFIG_SMC91C111) += smc91c111.o +hw-obj-$(CONFIG_LAN9118) += lan9118.o +hw-obj-$(CONFIG_NE2000_ISA) += ne2000-isa.o + +# IDE +hw-obj-$(CONFIG_IDE_CORE) += ide/core.o +hw-obj-$(CONFIG_IDE_QDEV) += ide/qdev.o +hw-obj-$(CONFIG_IDE_PCI) += ide/pci.o +hw-obj-$(CONFIG_IDE_ISA) += ide/isa.o +hw-obj-$(CONFIG_IDE_PIIX) += ide/piix.o +hw-obj-$(CONFIG_IDE_CMD646) += ide/cmd646.o +hw-obj-$(CONFIG_IDE_MACIO) += ide/macio.o +hw-obj-$(CONFIG_IDE_VIA) += ide/via.o +hw-obj-$(CONFIG_AHCI) += ide/ahci.o +hw-obj-$(CONFIG_AHCI) += ide/ich.o + +# SCSI layer +hw-obj-$(CONFIG_LSI_SCSI_PCI) += lsi53c895a.o +hw-obj-$(CONFIG_ESP) += esp.o + +hw-obj-y += dma-helpers.o sysbus.o isa-bus.o +hw-obj-y += qdev-addr.o + +# VGA +hw-obj-$(CONFIG_VGA_PCI) += vga-pci.o +hw-obj-$(CONFIG_VGA_ISA) += vga-isa.o +hw-obj-$(CONFIG_VGA_ISA_MM) += vga-isa-mm.o +hw-obj-$(CONFIG_VMWARE_VGA) += vmware_vga.o + +hw-obj-$(CONFIG_RC4030) += rc4030.o +hw-obj-$(CONFIG_DP8393X) += dp8393x.o +hw-obj-$(CONFIG_DS1225Y) += ds1225y.o +hw-obj-$(CONFIG_MIPSNET) += mipsnet.o + +# Sound +sound-obj-y = +sound-obj-$(CONFIG_SB16) += sb16.o +sound-obj-$(CONFIG_ES1370) += es1370.o +sound-obj-$(CONFIG_AC97) += ac97.o +sound-obj-$(CONFIG_ADLIB) += fmopl.o adlib.o +sound-obj-$(CONFIG_GUS) += gus.o gusemu_hal.o gusemu_mixer.o +sound-obj-$(CONFIG_CS4231A) += cs4231a.o +sound-obj-$(CONFIG_HDA) += intel-hda.o hda-audio.o + +adlib.o fmopl.o: QEMU_CFLAGS += -DBUILD_Y8950=0 +hw-obj-$(CONFIG_SOUND) += $(sound-obj-y) + +hw-obj-$(CONFIG_REALLY_VIRTFS) += virtio-9p-debug.o +hw-obj-$(CONFIG_VIRTFS) += virtio-9p-local.o virtio-9p-xattr.o +hw-obj-$(CONFIG_VIRTFS) += virtio-9p-xattr-user.o virtio-9p-posix-acl.o + +###################################################################### +# libdis +# NOTE: the disassembler code is only needed for debugging + +libdis-y = +libdis-$(CONFIG_ALPHA_DIS) += alpha-dis.o +libdis-$(CONFIG_ARM_DIS) += arm-dis.o +libdis-$(CONFIG_CRIS_DIS) += cris-dis.o +libdis-$(CONFIG_HPPA_DIS) += hppa-dis.o +libdis-$(CONFIG_I386_DIS) += i386-dis.o +libdis-$(CONFIG_IA64_DIS) += ia64-dis.o +libdis-$(CONFIG_M68K_DIS) += m68k-dis.o +libdis-$(CONFIG_MICROBLAZE_DIS) += microblaze-dis.o +libdis-$(CONFIG_MIPS_DIS) += mips-dis.o +libdis-$(CONFIG_PPC_DIS) += ppc-dis.o +libdis-$(CONFIG_S390_DIS) += s390-dis.o +libdis-$(CONFIG_SH4_DIS) += sh4-dis.o +libdis-$(CONFIG_SPARC_DIS) += sparc-dis.o + +###################################################################### +# trace + +ifeq ($(TRACE_BACKEND),dtrace) +trace-obj-y = trace-dtrace.o +else +trace-obj-y = trace.o +ifeq ($(TRACE_BACKEND),simple) +trace-obj-y += simpletrace.o +user-obj-y += qemu-timer-common.o +endif +endif + +vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS) + +vl.o: QEMU_CFLAGS+=$(SDL_CFLAGS) + diff -Nru qemu-kvm-0.12.5+noroms/Makefile.target qemu-kvm-0.14.1/Makefile.target --- qemu-kvm-0.12.5+noroms/Makefile.target 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/Makefile.target 2011-05-11 13:29:46.000000000 +0000 @@ -1,17 +1,23 @@ # -*- Mode: makefile -*- -# This needs to be defined before rules.mak GENERATED_HEADERS = config-target.h +CONFIG_NO_PCI = $(if $(subst n,,$(CONFIG_PCI)),n,y) +CONFIG_NO_KVM = $(if $(subst n,,$(CONFIG_KVM)),n,y) include ../config-host.mak include config-devices.mak include config-target.mak include $(SRC_PATH)/rules.mak +ifneq ($(HWDIR),) +include $(HWDIR)/config.mak +endif TARGET_PATH=$(SRC_PATH)/target-$(TARGET_BASE_ARCH) -VPATH=$(SRC_PATH):$(TARGET_PATH):$(SRC_PATH)/hw +$(call set-vpath, $(SRC_PATH):$(TARGET_PATH):$(SRC_PATH)/hw) QEMU_CFLAGS+= -I.. -I$(TARGET_PATH) -DNEED_CPU_H +include $(SRC_PATH)/Makefile.objs + ifdef CONFIG_USER_ONLY # user emulator name QEMU_PROG=qemu-$(TARGET_ARCH2) @@ -25,17 +31,40 @@ endif PROGS=$(QEMU_PROG) +STPFILES= +ifndef CONFIG_HAIKU LIBS+=-lm +endif -kvm.o kvm-all.o: QEMU_CFLAGS+=$(KVM_CFLAGS) +kvm.o kvm-all.o vhost.o vhost_net.o: QEMU_CFLAGS+=$(KVM_CFLAGS) CFLAGS += $(KVM_CFLAGS) config-target.h: config-target.h-timestamp config-target.h-timestamp: config-target.mak -all: $(PROGS) +ifdef CONFIG_SYSTEMTAP_TRACE +stap: $(QEMU_PROG).stp + +ifdef CONFIG_USER_ONLY +TARGET_TYPE=user +else +TARGET_TYPE=system +endif + +$(QEMU_PROG).stp: + $(call quiet-command,sh $(SRC_PATH)/scripts/tracetool \ + --$(TRACE_BACKEND) \ + --binary $(bindir)/$(QEMU_PROG) \ + --target-arch $(TARGET_ARCH) \ + --target-type $(TARGET_TYPE) \ + --stap < $(SRC_PATH)/trace-events > $(QEMU_PROG).stp," GEN $(QEMU_PROG).stp") +else +stap: +endif + +all: $(PROGS) stap # Dummy command so that make thinks it has done something @true @@ -49,32 +78,20 @@ libobj-$(CONFIG_SOFTFLOAT) += fpu/softfloat.o libobj-$(CONFIG_NOSOFTFLOAT) += fpu/softfloat-native.o libobj-y += op_helper.o helper.o +ifeq ($(TARGET_BASE_ARCH), i386) +libobj-y += cpuid.o +endif libobj-$(CONFIG_NEED_MMU) += mmu.o libobj-$(CONFIG_KVM) += kvm-tpr-opt.o -libobj-$(CONFIG_KVM) += qemu-kvm-helper.o libobj-$(TARGET_ARM) += neon_helper.o iwmmxt_helper.o -libobj-$(TARGET_ALPHA) += alpha_palcode.o -# NOTE: the disassembler code is only needed for debugging libobj-y += disas.o -libobj-$(CONFIG_ALPHA_DIS) += alpha-dis.o -libobj-$(CONFIG_ARM_DIS) += arm-dis.o -libobj-$(CONFIG_CRIS_DIS) += cris-dis.o -libobj-$(CONFIG_HPPA_DIS) += hppa-dis.o -libobj-$(CONFIG_I386_DIS) += i386-dis.o -libobj-$(CONFIG_M68K_DIS) += m68k-dis.o -libobj-$(CONFIG_MICROBLAZE_DIS) += microblaze-dis.o -libobj-$(CONFIG_MIPS_DIS) += mips-dis.o -libobj-$(CONFIG_PPC_DIS) += ppc-dis.o -libobj-$(CONFIG_S390_DIS) += s390-dis.o -libobj-$(CONFIG_SH4_DIS) += sh4-dis.o -libobj-$(CONFIG_SPARC_DIS) += sparc-dis.o -# libqemu +$(libobj-y): $(GENERATED_HEADERS) -libqemu.a: $(libobj-y) +# libqemu translate.o: translate.c cpu.h @@ -97,13 +114,14 @@ ifdef CONFIG_LINUX_USER -VPATH+=:$(SRC_PATH)/linux-user:$(SRC_PATH)/linux-user/$(TARGET_ABI_DIR) +$(call set-vpath, $(SRC_PATH)/linux-user:$(SRC_PATH)/linux-user/$(TARGET_ABI_DIR)) + QEMU_CFLAGS+=-I$(SRC_PATH)/linux-user -I$(SRC_PATH)/linux-user/$(TARGET_ABI_DIR) obj-y = main.o syscall.o strace.o mmap.o signal.o thunk.o \ - elfload.o linuxload.o uaccess.o gdbstub.o + elfload.o linuxload.o uaccess.o gdbstub.o cpu-uname.o \ + qemu-malloc.o $(oslib-obj-y) obj-$(TARGET_HAS_BFLT) += flatload.o -obj-$(TARGET_HAS_ELFLOAD32) += elfload32.o obj-$(TARGET_I386) += vm86.o @@ -116,7 +134,11 @@ obj-m68k-y += m68k-sim.o m68k-semi.o -ARLIBS=../libuser/libuser.a libqemu.a +$(obj-y) $(obj-$(TARGET_BASE_ARCH)-y): $(GENERATED_HEADERS) + +obj-y += $(addprefix ../libuser/, $(user-obj-y)) +obj-y += $(addprefix ../libdis-user/, $(libdis-y)) +obj-y += $(libobj-y) endif #CONFIG_LINUX_USER @@ -125,7 +147,8 @@ ifdef CONFIG_DARWIN_USER -VPATH+=:$(SRC_PATH)/darwin-user +$(call set-vpath, $(SRC_PATH)/darwin-user) + QEMU_CFLAGS+=-I$(SRC_PATH)/darwin-user -I$(SRC_PATH)/darwin-user/$(TARGET_ARCH) # Leave some space for the regular program loading zone @@ -138,7 +161,11 @@ obj-i386-y += ioport-user.o -ARLIBS=../libuser/libuser.a libqemu.a +$(obj-y) $(obj-$(TARGET_BASE_ARCH)-y): $(GENERATED_HEADERS) + +obj-y += $(addprefix ../libuser/, $(user-obj-y)) +obj-y += $(addprefix ../libdis-user/, $(libdis-y)) +obj-y += $(libobj-y) endif #CONFIG_DARWIN_USER @@ -147,7 +174,8 @@ ifdef CONFIG_BSD_USER -VPATH+=:$(SRC_PATH)/bsd-user +$(call set-vpath, $(SRC_PATH)/bsd-user) + QEMU_CFLAGS+=-I$(SRC_PATH)/bsd-user -I$(SRC_PATH)/bsd-user/$(TARGET_ARCH) obj-y = main.o bsdload.o elfload.o mmap.o signal.o strace.o syscall.o \ @@ -155,7 +183,11 @@ obj-i386-y += ioport-user.o -ARLIBS=../libuser/libuser.a libqemu.a +$(obj-y) $(obj-$(TARGET_BASE_ARCH)-y): $(GENERATED_HEADERS) + +obj-y += $(addprefix ../libuser/, $(user-obj-y)) +obj-y += $(addprefix ../libdis-user/, $(libdis-y)) +obj-y += $(libobj-y) endif #CONFIG_BSD_USER @@ -163,55 +195,47 @@ # System emulator target ifdef CONFIG_SOFTMMU -obj-y = vl.o async.o monitor.o pci.o pci_host.o pcie_host.o machine.o gdbstub.o +obj-y = arch_init.o cpus.o monitor.o machine.o gdbstub.o vl.o balloon.o # virtio has to be here due to weird dependency between PCI and virtio-net. # need to fix this properly -obj-y += virtio-blk.o virtio-balloon.o virtio-net.o virtio-console.o virtio-pci.o +obj-$(CONFIG_NO_PCI) += pci-stub.o +obj-$(CONFIG_PCI) += pci.o +obj-$(CONFIG_VIRTIO) += virtio-blk.o virtio-balloon.o virtio-net.o virtio-serial-bus.o +obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o +obj-y += vhost_net.o +obj-$(CONFIG_VHOST_NET) += vhost.o +obj-$(CONFIG_REALLY_VIRTFS) += virtio-9p.o +obj-y += rwhandler.o obj-$(CONFIG_KVM) += kvm.o kvm-all.o -# MSI-X depends on kvm for interrupt injection, -# so moved it from Makefile.hw to Makefile.target for now -obj-y += msix.o +obj-$(CONFIG_NO_KVM) += kvm-stub.o -obj-$(CONFIG_ISA_MMIO) += isa_mmio.o LIBS+=-lz -sound-obj-y = -sound-obj-$(CONFIG_SB16) += sb16.o -sound-obj-$(CONFIG_ES1370) += es1370.o -sound-obj-$(CONFIG_AC97) += ac97.o -sound-obj-$(CONFIG_ADLIB) += fmopl.o adlib.o -sound-obj-$(CONFIG_GUS) += gus.o gusemu_hal.o gusemu_mixer.o -sound-obj-$(CONFIG_CS4231A) += cs4231a.o - -adlib.o fmopl.o: QEMU_CFLAGS += -DBUILD_Y8950=0 - QEMU_CFLAGS += $(VNC_TLS_CFLAGS) QEMU_CFLAGS += $(VNC_SASL_CFLAGS) +QEMU_CFLAGS += $(VNC_JPEG_CFLAGS) +QEMU_CFLAGS += $(VNC_PNG_CFLAGS) # xen backend driver support obj-$(CONFIG_XEN) += xen_machine_pv.o xen_domainbuild.o -# USB layer -obj-$(CONFIG_USB_OHCI) += usb-ohci.o - -# PCI network cards -obj-y += eepro100.o -obj-y += pcnet.o -obj-y += rtl8139.o -obj-y += e1000.o +# Inter-VM PCI shared memory +obj-$(CONFIG_KVM) += ivshmem.o # Hardware support -obj-i386-y = ide/core.o ide/qdev.o ide/isa.o ide/pci.o ide/piix.o -obj-i386-y += pckbd.o $(sound-obj-y) dma.o -obj-i386-y += vga.o vga-pci.o vga-isa.o -obj-i386-y += fdc.o mc146818rtc.o serial.o i8259.o i8254.o pcspk.o pc.o -obj-i386-y += cirrus_vga.o apic.o ioapic.o parallel.o acpi.o piix_pci.o -obj-i386-y += usb-uhci.o vmmouse.o vmport.o vmware_vga.o hpet.o +obj-i386-y += vga.o +obj-i386-y += mc146818rtc.o i8259.o pc.o +obj-i386-y += cirrus_vga.o apic.o ioapic.o piix_pci.o +obj-i386-y += vmmouse.o vmport.o hpet.o applesmc.o obj-i386-y += device-hotplug.o pci-hotplug.o smbios.o wdt_ib700.o obj-i386-y += extboot.o -obj-i386-y += ne2000-isa.o +obj-i386-y += debugcon.o multiboot.o +obj-i386-y += pc_piix.o +obj-i386-$(CONFIG_SPICE) += qxl.o qxl-logger.o qxl-render.o obj-i386-y += testdev.o +obj-i386-y += acpi.o acpi_piix4.o +obj-i386-y += pcspk.o i8254.o obj-i386-$(CONFIG_KVM_PIT) += i8254-kvm.o obj-i386-$(CONFIG_KVM_DEVICE_ASSIGNMENT) += device-assignment.o @@ -223,37 +247,40 @@ obj-ia64-$(CONFIG_KVM_DEVICE_ASSIGNMENT) += device-assignment.o # shared objects -obj-ppc-y = ppc.o ide/core.o ide/qdev.o ide/isa.o ide/pci.o ide/macio.o -obj-ppc-y += ide/cmd646.o -obj-ppc-y += vga.o vga-pci.o $(sound-obj-y) dma.o openpic.o -obj-ppc-y += cirrus_vga.o +obj-ppc-y = ppc.o +obj-ppc-y += vga.o # PREP target -obj-ppc-y += pckbd.o serial.o i8259.o i8254.o fdc.o mc146818rtc.o -obj-ppc-y += prep_pci.o ppc_prep.o ne2000-isa.o -# Mac shared devices -obj-ppc-y += macio.o cuda.o adb.o mac_nvram.o mac_dbdma.o +obj-ppc-y += i8259.o mc146818rtc.o +obj-ppc-y += ppc_prep.o # OldWorld PowerMac -obj-ppc-y += heathrow_pic.o grackle_pci.o ppc_oldworld.o +obj-ppc-y += ppc_oldworld.o # NewWorld PowerMac -obj-ppc-y += unin_pci.o ppc_newworld.o +obj-ppc-y += ppc_newworld.o # PowerPC 4xx boards -obj-ppc-y += pflash_cfi02.o ppc4xx_devs.o ppc4xx_pci.o ppc405_uc.o ppc405_boards.o +obj-ppc-y += ppc4xx_devs.o ppc4xx_pci.o ppc405_uc.o ppc405_boards.o obj-ppc-y += ppc440.o ppc440_bamboo.o # PowerPC E500 boards -obj-ppc-y += ppce500_pci.o ppce500_mpc8544ds.o +obj-ppc-y += ppce500_mpc8544ds.o +# PowerPC 440 Xilinx ML507 reference board. +obj-ppc-y += virtex_ml507.o obj-ppc-$(CONFIG_KVM) += kvm_ppc.o obj-ppc-$(CONFIG_FDT) += device_tree.o +# Xilinx PPC peripherals +obj-ppc-y += xilinx_intc.o +obj-ppc-y += xilinx_timer.o +obj-ppc-y += xilinx_uartlite.o +obj-ppc-y += xilinx_ethlite.o + obj-mips-y = mips_r4k.o mips_jazz.o mips_malta.o mips_mipssim.o -obj-mips-y += mips_timer.o mips_int.o dma.o vga.o serial.o i8254.o i8259.o rc4030.o -obj-mips-y += vga-pci.o vga-isa.o vga-isa-mm.o -obj-mips-y += g364fb.o jazz_led.o dp8393x.o -obj-mips-y += ide/core.o ide/qdev.o ide/isa.o ide/pci.o ide/piix.o -obj-mips-y += gt64xxx.o pckbd.o fdc.o mc146818rtc.o usb-uhci.o acpi.o ds1225y.o -obj-mips-y += piix4.o parallel.o cirrus_vga.o pcspk.o $(sound-obj-y) -obj-mips-y += mipsnet.o ne2000-isa.o -obj-mips-y += pflash_cfi01.o -obj-mips-y += vmware_vga.o +obj-mips-y += pcspk.o i8254.o +obj-mips-y += acpi.o acpi_piix4.o +obj-mips-y += mips_addr.o mips_timer.o mips_int.o +obj-mips-y += vga.o i8259.o +obj-mips-y += g364fb.o jazz_led.o +obj-mips-y += gt64xxx.o mc146818rtc.o +obj-mips-y += cirrus_vga.o +obj-mips-$(CONFIG_FULONG) += bonito.o vt82c686.o mips_fulong2e.o obj-microblaze-y = petalogix_s3adsp1800_mmu.o @@ -263,12 +290,13 @@ obj-microblaze-y += xilinx_uartlite.o obj-microblaze-y += xilinx_ethlite.o -obj-microblaze-y += pflash_cfi02.o - obj-microblaze-$(CONFIG_FDT) += device_tree.o # Boards -obj-cris-y = cris_pic_cpu.o etraxfs.o axis_dev88.o +obj-cris-y = cris_pic_cpu.o +obj-cris-y += cris-boot.o +obj-cris-y += etraxfs.o axis_dev88.o +obj-cris-y += axis_dev88.o # IO blocks obj-cris-y += etraxfs_dma.o @@ -277,18 +305,18 @@ obj-cris-y += etraxfs_timer.o obj-cris-y += etraxfs_ser.o -obj-cris-y += pflash_cfi02.o - ifeq ($(TARGET_ARCH), sparc64) -obj-sparc-y = sun4u.o pckbd.o apb_pci.o -obj-sparc-y += ide/core.o ide/qdev.o ide/pci.o ide/cmd646.o -obj-sparc-y += vga.o vga-pci.o -obj-sparc-y += fdc.o mc146818rtc.o serial.o -obj-sparc-y += cirrus_vga.o parallel.o +obj-sparc-y = sun4u.o apb_pci.o +obj-sparc-y += vga.o +obj-sparc-y += mc146818rtc.o +obj-sparc-y += cirrus_vga.o else -obj-sparc-y = sun4m.o lance.o tcx.o iommu.o slavio_intctl.o -obj-sparc-y += slavio_timer.o slavio_misc.o fdc.o sparc32_dma.o -obj-sparc-y += cs4231.o eccmemctl.o sbi.o sun4c_intctl.o +obj-sparc-y = sun4m.o lance.o tcx.o sun4m_iommu.o slavio_intctl.o +obj-sparc-y += slavio_timer.o slavio_misc.o sparc32_dma.o +obj-sparc-y += cs4231.o eccmemctl.o sbi.o sun4c_intctl.o leon3.o + +# GRLIB +obj-sparc-y += grlib_gptimer.o grlib_irqmp.o grlib_apbuart.o endif obj-arm-y = integratorcp.o versatilepb.o arm_pic.o arm_timer.o @@ -300,68 +328,84 @@ obj-arm-y += arm-semi.o obj-arm-y += pxa2xx.o pxa2xx_pic.o pxa2xx_gpio.o pxa2xx_timer.o pxa2xx_dma.o obj-arm-y += pxa2xx_lcd.o pxa2xx_mmci.o pxa2xx_pcmcia.o pxa2xx_keypad.o -obj-arm-y += pflash_cfi01.o gumstix.o -obj-arm-y += zaurus.o ide/core.o ide/microdrive.o serial.o spitz.o tosa.o tc6393xb.o -obj-arm-y += omap1.o omap_lcdc.o omap_dma.o omap_clk.o omap_mmc.o omap_i2c.o -obj-arm-y += omap2.o omap_dss.o soc_dma.o +obj-arm-y += gumstix.o +obj-arm-y += zaurus.o ide/microdrive.o spitz.o tosa.o tc6393xb.o +obj-arm-y += omap1.o omap_lcdc.o omap_dma.o omap_clk.o omap_mmc.o omap_i2c.o \ + omap_gpio.o omap_intc.o omap_uart.o +obj-arm-y += omap2.o omap_dss.o soc_dma.o omap_gptimer.o omap_synctimer.o \ + omap_gpmc.o omap_sdrc.o omap_spi.o omap_tap.o omap_l4.o obj-arm-y += omap_sx1.o palm.o tsc210x.o obj-arm-y += nseries.o blizzard.o onenand.o vga.o cbus.o tusb6010.o usb-musb.o obj-arm-y += mst_fpga.o mainstone.o -obj-arm-y += musicpal.o pflash_cfi02.o bitbang_i2c.o marvell_88w8618_audio.o +obj-arm-y += musicpal.o bitbang_i2c.o marvell_88w8618_audio.o obj-arm-y += framebuffer.o obj-arm-y += syborg.o syborg_fb.o syborg_interrupt.o syborg_keyboard.o obj-arm-y += syborg_serial.o syborg_timer.o syborg_pointer.o syborg_rtc.o obj-arm-y += syborg_virtio.o obj-sh4-y = shix.o r2d.o sh7750.o sh7750_regnames.o tc58128.o -obj-sh4-y += sh_timer.o sh_serial.o sh_intc.o sh_pci.o sm501.o serial.o -obj-sh4-y += ide/core.o ide/mmio.o +obj-sh4-y += sh_timer.o sh_serial.o sh_intc.o sh_pci.o sm501.o +obj-sh4-y += ide/mmio.o obj-m68k-y = an5206.o mcf5206.o mcf_uart.o mcf_intc.o mcf5208.o mcf_fec.o obj-m68k-y += m68k-semi.o dummy_m68k.o obj-s390x-y = s390-virtio-bus.o s390-virtio.o +obj-alpha-y = alpha_palcode.o + ifeq ($(TARGET_ARCH), ia64) firmware.o: firmware.c $(CC) $(HELPER_CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) -c -o $@ $< endif -main.o vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS) +main.o: QEMU_CFLAGS+=$(GPROF_CFLAGS) -vl.o: QEMU_CFLAGS+=$(SDL_CFLAGS) +monitor.o: hmp-commands.h qmp-commands.h -vl.o: qemu-options.h +$(obj-y) $(obj-$(TARGET_BASE_ARCH)-y): $(GENERATED_HEADERS) -monitor.o: qemu-monitor.h - -ARLIBS=../libqemu_common.a libqemu.a $(HWLIB) +obj-y += $(addprefix ../, $(common-obj-y)) +obj-y += $(addprefix ../libdis/, $(libdis-y)) +obj-y += $(libobj-y) +obj-y += $(addprefix $(HWDIR)/, $(hw-obj-y)) endif # CONFIG_SOFTMMU +obj-y += $(addprefix ../, $(trace-obj-y)) obj-$(CONFIG_GDBSTUB_XML) += gdbstub-xml.o -$(QEMU_PROG): $(obj-y) $(obj-$(TARGET_BASE_ARCH)-y) $(ARLIBS) +$(QEMU_PROG): $(obj-y) $(obj-$(TARGET_BASE_ARCH)-y) $(call LINK,$(obj-y) $(obj-$(TARGET_BASE_ARCH)-y)) -gdbstub-xml.c: $(TARGET_XML_FILES) feature_to_c.sh - $(call quiet-command,rm -f $@ && $(SHELL) $(SRC_PATH)/feature_to_c.sh $@ $(TARGET_XML_FILES)," GEN $(TARGET_DIR)$@") +gdbstub-xml.c: $(TARGET_XML_FILES) $(SRC_PATH)/scripts/feature_to_c.sh + $(call quiet-command,rm -f $@ && $(SHELL) $(SRC_PATH)/scripts/feature_to_c.sh $@ $(TARGET_XML_FILES)," GEN $(TARGET_DIR)$@") -qemu-options.h: $(SRC_PATH)/qemu-options.hx - $(call quiet-command,sh $(SRC_PATH)/hxtool -h < $< > $@," GEN $(TARGET_DIR)$@") +hmp-commands.h: $(SRC_PATH)/hmp-commands.hx + $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@," GEN $(TARGET_DIR)$@") -qemu-monitor.h: $(SRC_PATH)/qemu-monitor.hx - $(call quiet-command,sh $(SRC_PATH)/hxtool -h < $< > $@," GEN $(TARGET_DIR)$@") +qmp-commands.h: $(SRC_PATH)/qmp-commands.hx + $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@," GEN $(TARGET_DIR)$@") clean: rm -f *.o *.a *~ $(PROGS) nwfpe/*.o fpu/*.o rm -f *.d */*.d tcg/*.o ide/*.o - rm -f qemu-options.h qemu-monitor.h gdbstub-xml.c + rm -f hmp-commands.h qmp-commands.h gdbstub-xml.c +ifdef CONFIG_SYSTEMTAP_TRACE + rm -f *.stp +endif install: all ifneq ($(PROGS),) - $(INSTALL) -m 755 $(STRIP_OPT) $(PROGS) "$(DESTDIR)$(bindir)" + $(INSTALL) -m 755 $(PROGS) "$(DESTDIR)$(bindir)" +ifneq ($(STRIP),) + $(STRIP) $(patsubst %,"$(DESTDIR)$(bindir)/%",$(PROGS)) +endif +endif +ifdef CONFIG_SYSTEMTAP_TRACE + $(INSTALL_DIR) "$(DESTDIR)$(datadir)/../systemtap/tapset" + $(INSTALL_DATA) $(QEMU_PROG).stp "$(DESTDIR)$(datadir)/../systemtap/tapset" endif # Include automatically generated dependency files diff -Nru qemu-kvm-0.12.5+noroms/Makefile.user qemu-kvm-0.14.1/Makefile.user --- qemu-kvm-0.12.5+noroms/Makefile.user 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/Makefile.user 2011-05-11 13:29:46.000000000 +0000 @@ -6,25 +6,16 @@ .PHONY: all -# Do not take %.o from $(SRC_PATH), only %.c and %.h -# All %.o for user targets should be built with -fpie, when -# configured with --enable-user-pie, so we don't want to -# take %.o from $(SRC_PATH), since they built without -fpie -vpath %.c %.h $(SRC_PATH) +$(call set-vpath, $(SRC_PATH)) QEMU_CFLAGS+=-I.. -obj-y = -obj-y += envlist.o path.o -obj-y += tcg-runtime.o host-utils.o -obj-y += cutils.o cache-utils.o +include $(SRC_PATH)/Makefile.objs -all: libuser.a +all: $(user-obj-y) # Dummy command so that make thinks it has done something @true -libuser.a: $(obj-y) - clean: rm -f *.o *.d *.a *~ diff -Nru qemu-kvm-0.12.5+noroms/microblaze-dis.c qemu-kvm-0.14.1/microblaze-dis.c --- qemu-kvm-0.12.5+noroms/microblaze-dis.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/microblaze-dis.c 2011-05-11 13:29:46.000000000 +0000 @@ -12,7 +12,7 @@ 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, see . */ +along with this program; if not, see . */ /* * Copyright (c) 2001 Xilinx, Inc. All rights reserved. @@ -37,6 +37,9 @@ #define STATIC_TABLE #define DEFINE_TABLE +#define TRUE 1 +#define FALSE 0 + #ifndef MICROBLAZE_OPC #define MICROBLAZE_OPC /* Assembler instructions for Xilinx's microblaze processor @@ -99,17 +102,33 @@ enum microblaze_instr { add, rsub, addc, rsubc, addk, rsubk, addkc, rsubkc, cmp, cmpu, - addi, rsubi, addic, rsubic, addik, rsubik, addikc, rsubikc, mul, + addi, rsubi, addic, rsubic, addik, rsubik, addikc, rsubikc, mul, mulh, mulhu, mulhsu, idiv, idivu, bsll, bsra, bsrl, get, put, nget, nput, cget, cput, ncget, ncput, muli, bslli, bsrai, bsrli, mului, or, and, xor, - andn, pcmpbf, pcmpbc, pcmpeq, pcmpne, sra, src, srl, sext8, sext16, wic, wdc, mts, mfs, br, brd, + andn, pcmpbf, pcmpbc, pcmpeq, pcmpne, sra, src, srl, sext8, sext16, wic, wdc, wdcclear, wdcflush, mts, mfs, br, brd, brld, bra, brad, brald, microblaze_brk, beq, beqd, bne, bned, blt, bltd, ble, bled, bgt, bgtd, bge, bged, ori, andi, xori, andni, imm, rtsd, rtid, rtbd, rted, bri, brid, brlid, brai, braid, bralid, brki, beqi, beqid, bnei, bneid, blti, bltid, blei, bleid, bgti, - bgtid, bgei, bgeid, lbu, lhu, lw, sb, sh, sw, lbui, lhui, lwi, + bgtid, bgei, bgeid, lbu, lhu, lw, lwx, sb, sh, sw, swx, lbui, lhui, lwi, sbi, shi, swi, msrset, msrclr, tuqula, fadd, frsub, fmul, fdiv, - fcmp_lt, fcmp_eq, fcmp_le, fcmp_gt, fcmp_ne, fcmp_ge, fcmp_un, invalid_inst } ; + fcmp_lt, fcmp_eq, fcmp_le, fcmp_gt, fcmp_ne, fcmp_ge, fcmp_un, flt, fint, fsqrt, + tget, tcget, tnget, tncget, tput, tcput, tnput, tncput, + eget, ecget, neget, necget, eput, ecput, neput, necput, + teget, tecget, tneget, tnecget, teput, tecput, tneput, tnecput, + aget, caget, naget, ncaget, aput, caput, naput, ncaput, + taget, tcaget, tnaget, tncaget, taput, tcaput, tnaput, tncaput, + eaget, ecaget, neaget, necaget, eaput, ecaput, neaput, necaput, + teaget, tecaget, tneaget, tnecaget, teaput, tecaput, tneaput, tnecaput, + getd, tgetd, cgetd, tcgetd, ngetd, tngetd, ncgetd, tncgetd, + putd, tputd, cputd, tcputd, nputd, tnputd, ncputd, tncputd, + egetd, tegetd, ecgetd, tecgetd, negetd, tnegetd, necgetd, tnecgetd, + eputd, teputd, ecputd, tecputd, neputd, tneputd, necputd, tnecputd, + agetd, tagetd, cagetd, tcagetd, nagetd, tnagetd, ncagetd, tncagetd, + aputd, taputd, caputd, tcaputd, naputd, tnaputd, ncaputd, tncaputd, + eagetd, teagetd, ecagetd, tecagetd, neagetd, tneagetd, necagetd, tnecagetd, + eaputd, teaputd, ecaputd, tecaputd, neaputd, tneaputd, necaputd, tnecaputd, + invalid_inst } ; enum microblaze_instr_type { arithmetic_inst, logical_inst, mult_inst, div_inst, branch_inst, @@ -126,15 +145,38 @@ #define REG_EAR_MASK 0x8003 #define REG_ESR_MASK 0x8005 #define REG_FSR_MASK 0x8007 +#define REG_BTR_MASK 0x800b +#define REG_EDR_MASK 0x800d +#define REG_PVR_MASK 0xa000 + +#define REG_PID_MASK 0x9000 +#define REG_ZPR_MASK 0x9001 +#define REG_TLBX_MASK 0x9002 +#define REG_TLBLO_MASK 0x9003 +#define REG_TLBHI_MASK 0x9004 +#define REG_TLBSX_MASK 0x9005 #define MIN_REGNUM 0 #define MAX_REGNUM 31 +#define MIN_PVR_REGNUM 0 +#define MAX_PVR_REGNUM 15 + #define REG_PC 32 /* PC */ #define REG_MSR 33 /* machine status reg */ #define REG_EAR 35 /* Exception reg */ #define REG_ESR 37 /* Exception reg */ #define REG_FSR 39 /* FPU Status reg */ +#define REG_BTR 43 /* Branch Target reg */ +#define REG_EDR 45 /* Exception reg */ +#define REG_PVR 40960 /* Program Verification reg */ + +#define REG_PID 36864 /* MMU: Process ID reg */ +#define REG_ZPR 36865 /* MMU: Zone Protect reg */ +#define REG_TLBX 36866 /* MMU: TLB Index reg */ +#define REG_TLBLO 36867 /* MMU: TLB Low reg */ +#define REG_TLBHI 36868 /* MMU: TLB High reg */ +#define REG_TLBSX 36869 /* MMU: TLB Search Index reg */ /* alternate names for gen purpose regs */ #define REG_SP 1 /* stack pointer */ @@ -159,11 +201,11 @@ #define IMM5_MASK 0x0000001F -// imm mask for get, put instructions -#define IMM12_MASK 0x00000FFF +// FSL imm mask for get, put instructions +#define RFSL_MASK 0x000000F // imm mask for msrset, msrclr instructions -#define IMM14_MASK 0x00003FFF +#define IMM15_MASK 0x00007FFF #endif /* MICROBLAZE-OPCM */ @@ -182,18 +224,21 @@ #define INST_TYPE_R1 12 // new instn type for barrel shift imms #define INST_TYPE_RD_R1_IMM5 13 -#define INST_TYPE_RD_IMM12 14 -#define INST_TYPE_R1_IMM12 15 +#define INST_TYPE_RD_RFSL 14 +#define INST_TYPE_R1_RFSL 15 // new insn type for insn cache #define INST_TYPE_RD_R1_SPECIAL 16 // new insn type for msrclr, msrset insns. -#define INST_TYPE_RD_IMM14 17 +#define INST_TYPE_RD_IMM15 17 // new insn type for tuqula rd - addik rd, r0, 42 #define INST_TYPE_RD 18 +// new insn type for t*put +#define INST_TYPE_RFSL 19 + #define INST_TYPE_NONE 25 @@ -210,24 +255,25 @@ #define OPCODE_MASK_H2 0xFC1F0000 /* High 6 and bits 20-16 */ #define OPCODE_MASK_H12 0xFFFF0000 /* High 16 */ #define OPCODE_MASK_H4 0xFC0007FF /* High 6 and low 11 bits */ -#define OPCODE_MASK_H13S 0xFFE0FFF0 /* High 11 and 15:1 bits and last nibble of last byte for spr */ -#define OPCODE_MASK_H23S 0xFC1FFFF0 /* High 6, 20-16 and 15:1 bits and last nibble of last byte for spr */ +#define OPCODE_MASK_H13S 0xFFE0EFF0 /* High 11 and 15:1 bits and last nibble of last byte for spr */ +#define OPCODE_MASK_H23S 0xFC1FC000 /* High 6, 20-16 and 15:1 bits and last nibble of last byte for spr */ #define OPCODE_MASK_H34 0xFC00FFFF /* High 6 and low 16 bits */ #define OPCODE_MASK_H14 0xFFE007FF /* High 11 and low 11 bits */ #define OPCODE_MASK_H24 0xFC1F07FF /* High 6, bits 20-16 and low 11 bits */ #define OPCODE_MASK_H124 0xFFFF07FF /* High 16, and low 11 bits */ #define OPCODE_MASK_H1234 0xFFFFFFFF /* All 32 bits */ #define OPCODE_MASK_H3 0xFC000600 /* High 6 bits and bits 21, 22 */ -#define OPCODE_MASK_H32 0xFC00F000 /* High 6 bits and bit 16, 17, 18 and 19*/ +#define OPCODE_MASK_H32 0xFC00FC00 /* High 6 bits and bit 16-21 */ #define OPCODE_MASK_H34B 0xFC0000FF /* High 6 bits and low 8 bits */ +#define OPCODE_MASK_H34C 0xFC0007E0 /* High 6 bits and bits 21-26 */ // New Mask for msrset, msrclr insns. -#define OPCODE_MASK_H23N 0xFC1FC000 /* High 6 and bits 12 - 18 */ +#define OPCODE_MASK_H23N 0xFC1F8000 /* High 6 and bits 11 - 16 */ #define DELAY_SLOT 1 #define NO_DELAY_SLOT 0 -#define MAX_OPCODES 149 +#define MAX_OPCODES 280 struct op_code_struct { const char *name; @@ -262,19 +308,22 @@ {"addikc",INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x38000000, OPCODE_MASK_H, addikc, arithmetic_inst }, {"rsubikc",INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x3C000000, OPCODE_MASK_H, rsubikc, arithmetic_inst }, {"mul", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x40000000, OPCODE_MASK_H4, mul, mult_inst }, + {"mulh", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x40000001, OPCODE_MASK_H4, mulh, mult_inst }, + {"mulhu", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x40000003, OPCODE_MASK_H4, mulhu, mult_inst }, + {"mulhsu",INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x40000002, OPCODE_MASK_H4, mulhsu, mult_inst }, {"idiv", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x48000000, OPCODE_MASK_H4, idiv, div_inst }, {"idivu", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x48000002, OPCODE_MASK_H4, idivu, div_inst }, {"bsll", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x44000400, OPCODE_MASK_H3, bsll, barrel_shift_inst }, {"bsra", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x44000200, OPCODE_MASK_H3, bsra, barrel_shift_inst }, {"bsrl", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x44000000, OPCODE_MASK_H3, bsrl, barrel_shift_inst }, - {"get", INST_TYPE_RD_IMM12, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C000000, OPCODE_MASK_H32, get, anyware_inst }, - {"put", INST_TYPE_R1_IMM12, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C008000, OPCODE_MASK_H32, put, anyware_inst }, - {"nget", INST_TYPE_RD_IMM12, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C004000, OPCODE_MASK_H32, nget, anyware_inst }, - {"nput", INST_TYPE_R1_IMM12, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00C000, OPCODE_MASK_H32, nput, anyware_inst }, - {"cget", INST_TYPE_RD_IMM12, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C002000, OPCODE_MASK_H32, cget, anyware_inst }, - {"cput", INST_TYPE_R1_IMM12, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00A000, OPCODE_MASK_H32, cput, anyware_inst }, - {"ncget", INST_TYPE_RD_IMM12, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C006000, OPCODE_MASK_H32, ncget, anyware_inst }, - {"ncput", INST_TYPE_R1_IMM12, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00E000, OPCODE_MASK_H32, ncput, anyware_inst }, + {"get", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C000000, OPCODE_MASK_H32, get, anyware_inst }, + {"put", INST_TYPE_R1_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C008000, OPCODE_MASK_H32, put, anyware_inst }, + {"nget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C004000, OPCODE_MASK_H32, nget, anyware_inst }, + {"nput", INST_TYPE_R1_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00C000, OPCODE_MASK_H32, nput, anyware_inst }, + {"cget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C002000, OPCODE_MASK_H32, cget, anyware_inst }, + {"cput", INST_TYPE_R1_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00A000, OPCODE_MASK_H32, cput, anyware_inst }, + {"ncget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C006000, OPCODE_MASK_H32, ncget, anyware_inst }, + {"ncput", INST_TYPE_R1_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00E000, OPCODE_MASK_H32, ncput, anyware_inst }, {"muli", INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x60000000, OPCODE_MASK_H, muli, mult_inst }, {"bslli", INST_TYPE_RD_R1_IMM5, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x64000400, OPCODE_MASK_H3, bslli, barrel_shift_inst }, {"bsrai", INST_TYPE_RD_R1_IMM5, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x64000200, OPCODE_MASK_H3, bsrai, barrel_shift_inst }, @@ -294,6 +343,8 @@ {"sext16",INST_TYPE_RD_R1, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x90000061, OPCODE_MASK_H34, sext16, logical_inst }, {"wic", INST_TYPE_RD_R1_SPECIAL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x90000068, OPCODE_MASK_H34B, wic, special_inst }, {"wdc", INST_TYPE_RD_R1_SPECIAL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x90000064, OPCODE_MASK_H34B, wdc, special_inst }, + {"wdc.clear", INST_TYPE_RD_R1_SPECIAL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x90000066, OPCODE_MASK_H34B, wdcclear, special_inst }, + {"wdc.flush", INST_TYPE_RD_R1_SPECIAL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x90000074, OPCODE_MASK_H34B, wdcflush, special_inst }, {"mts", INST_TYPE_SPECIAL_R1, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_MTS, 0x9400C000, OPCODE_MASK_H13S, mts, special_inst }, {"mfs", INST_TYPE_RD_SPECIAL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_MFS, 0x94008000, OPCODE_MASK_H23S, mfs, special_inst }, {"br", INST_TYPE_R2, INST_PC_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x98000000, OPCODE_MASK_H124, br, branch_inst }, @@ -346,9 +397,11 @@ {"lbu", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xC0000000, OPCODE_MASK_H4, lbu, memory_load_inst }, {"lhu", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xC4000000, OPCODE_MASK_H4, lhu, memory_load_inst }, {"lw", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xC8000000, OPCODE_MASK_H4, lw, memory_load_inst }, + {"lwx", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xC8000400, OPCODE_MASK_H4, lwx, memory_load_inst }, {"sb", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xD0000000, OPCODE_MASK_H4, sb, memory_store_inst }, {"sh", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xD4000000, OPCODE_MASK_H4, sh, memory_store_inst }, {"sw", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xD8000000, OPCODE_MASK_H4, sw, memory_store_inst }, + {"swx", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xD8000400, OPCODE_MASK_H4, swx, memory_store_inst }, {"lbui", INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xE0000000, OPCODE_MASK_H, lbui, memory_load_inst }, {"lhui", INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xE4000000, OPCODE_MASK_H, lhui, memory_load_inst }, {"lwi", INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xE8000000, OPCODE_MASK_H, lwi, memory_load_inst }, @@ -364,8 +417,8 @@ {"sub", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x04000000, OPCODE_MASK_H, invalid_inst, arithmetic_inst }, /* sub translates to rsub rd, rb, ra */ {"lmi", INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xE8000000, OPCODE_MASK_H, invalid_inst, memory_load_inst }, {"smi", INST_TYPE_RD_R1_IMM, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0xF8000000, OPCODE_MASK_H, invalid_inst, memory_store_inst }, - {"msrset",INST_TYPE_RD_IMM14, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x94100000, OPCODE_MASK_H23N, msrset, special_inst }, - {"msrclr",INST_TYPE_RD_IMM14, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x94110000, OPCODE_MASK_H23N, msrclr, special_inst }, + {"msrset",INST_TYPE_RD_IMM15, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x94100000, OPCODE_MASK_H23N, msrset, special_inst }, + {"msrclr",INST_TYPE_RD_IMM15, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x94110000, OPCODE_MASK_H23N, msrclr, special_inst }, {"fadd", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x58000000, OPCODE_MASK_H4, fadd, arithmetic_inst }, {"frsub", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x58000080, OPCODE_MASK_H4, frsub, arithmetic_inst }, {"fmul", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x58000100, OPCODE_MASK_H4, fmul, arithmetic_inst }, @@ -377,24 +430,155 @@ {"fcmp.ne", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x58000250, OPCODE_MASK_H4, fcmp_ne, arithmetic_inst }, {"fcmp.ge", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x58000260, OPCODE_MASK_H4, fcmp_ge, arithmetic_inst }, {"fcmp.un", INST_TYPE_RD_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x58000200, OPCODE_MASK_H4, fcmp_un, arithmetic_inst }, - {""} + {"flt", INST_TYPE_RD_R1, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x58000280, OPCODE_MASK_H4, flt, arithmetic_inst }, + {"fint", INST_TYPE_RD_R1, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x58000300, OPCODE_MASK_H4, fint, arithmetic_inst }, + {"fsqrt", INST_TYPE_RD_R1, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x58000380, OPCODE_MASK_H4, fsqrt, arithmetic_inst }, + {"tget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C001000, OPCODE_MASK_H32, tget, anyware_inst }, + {"tcget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C003000, OPCODE_MASK_H32, tcget, anyware_inst }, + {"tnget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C005000, OPCODE_MASK_H32, tnget, anyware_inst }, + {"tncget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C007000, OPCODE_MASK_H32, tncget, anyware_inst }, + {"tput", INST_TYPE_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C009000, OPCODE_MASK_H32, tput, anyware_inst }, + {"tcput", INST_TYPE_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00B000, OPCODE_MASK_H32, tcput, anyware_inst }, + {"tnput", INST_TYPE_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00D000, OPCODE_MASK_H32, tnput, anyware_inst }, + {"tncput", INST_TYPE_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00F000, OPCODE_MASK_H32, tncput, anyware_inst }, + + {"eget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C000400, OPCODE_MASK_H32, eget, anyware_inst }, + {"ecget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C002400, OPCODE_MASK_H32, ecget, anyware_inst }, + {"neget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C004400, OPCODE_MASK_H32, neget, anyware_inst }, + {"necget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C006400, OPCODE_MASK_H32, necget, anyware_inst }, + {"eput", INST_TYPE_R1_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C008400, OPCODE_MASK_H32, eput, anyware_inst }, + {"ecput", INST_TYPE_R1_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00A400, OPCODE_MASK_H32, ecput, anyware_inst }, + {"neput", INST_TYPE_R1_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00C400, OPCODE_MASK_H32, neput, anyware_inst }, + {"necput", INST_TYPE_R1_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00E400, OPCODE_MASK_H32, necput, anyware_inst }, + + {"teget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C001400, OPCODE_MASK_H32, teget, anyware_inst }, + {"tecget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C003400, OPCODE_MASK_H32, tecget, anyware_inst }, + {"tneget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C005400, OPCODE_MASK_H32, tneget, anyware_inst }, + {"tnecget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C007400, OPCODE_MASK_H32, tnecget, anyware_inst }, + {"teput", INST_TYPE_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C009400, OPCODE_MASK_H32, teput, anyware_inst }, + {"tecput", INST_TYPE_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00B400, OPCODE_MASK_H32, tecput, anyware_inst }, + {"tneput", INST_TYPE_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00D400, OPCODE_MASK_H32, tneput, anyware_inst }, + {"tnecput", INST_TYPE_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00F400, OPCODE_MASK_H32, tnecput, anyware_inst }, + + {"aget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C000800, OPCODE_MASK_H32, aget, anyware_inst }, + {"caget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C002800, OPCODE_MASK_H32, caget, anyware_inst }, + {"naget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C004800, OPCODE_MASK_H32, naget, anyware_inst }, + {"ncaget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C006800, OPCODE_MASK_H32, ncaget, anyware_inst }, + {"aput", INST_TYPE_R1_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C008800, OPCODE_MASK_H32, aput, anyware_inst }, + {"caput", INST_TYPE_R1_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00A800, OPCODE_MASK_H32, caput, anyware_inst }, + {"naput", INST_TYPE_R1_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00C800, OPCODE_MASK_H32, naput, anyware_inst }, + {"ncaput", INST_TYPE_R1_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00E800, OPCODE_MASK_H32, ncaput, anyware_inst }, + + {"taget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C001800, OPCODE_MASK_H32, taget, anyware_inst }, + {"tcaget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C003800, OPCODE_MASK_H32, tcaget, anyware_inst }, + {"tnaget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C005800, OPCODE_MASK_H32, tnaget, anyware_inst }, + {"tncaget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C007800, OPCODE_MASK_H32, tncaget, anyware_inst }, + {"taput", INST_TYPE_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C009800, OPCODE_MASK_H32, taput, anyware_inst }, + {"tcaput", INST_TYPE_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00B800, OPCODE_MASK_H32, tcaput, anyware_inst }, + {"tnaput", INST_TYPE_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00D800, OPCODE_MASK_H32, tnaput, anyware_inst }, + {"tncaput", INST_TYPE_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00F800, OPCODE_MASK_H32, tncaput, anyware_inst }, + + {"eaget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C000C00, OPCODE_MASK_H32, eget, anyware_inst }, + {"ecaget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C002C00, OPCODE_MASK_H32, ecget, anyware_inst }, + {"neaget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C004C00, OPCODE_MASK_H32, neget, anyware_inst }, + {"necaget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C006C00, OPCODE_MASK_H32, necget, anyware_inst }, + {"eaput", INST_TYPE_R1_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C008C00, OPCODE_MASK_H32, eput, anyware_inst }, + {"ecaput", INST_TYPE_R1_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00AC00, OPCODE_MASK_H32, ecput, anyware_inst }, + {"neaput", INST_TYPE_R1_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00CC00, OPCODE_MASK_H32, neput, anyware_inst }, + {"necaput", INST_TYPE_R1_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00EC00, OPCODE_MASK_H32, necput, anyware_inst }, + + {"teaget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C001C00, OPCODE_MASK_H32, teaget, anyware_inst }, + {"tecaget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C003C00, OPCODE_MASK_H32, tecaget, anyware_inst }, + {"tneaget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C005C00, OPCODE_MASK_H32, tneaget, anyware_inst }, + {"tnecaget", INST_TYPE_RD_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C007C00, OPCODE_MASK_H32, tnecaget, anyware_inst }, + {"teaput", INST_TYPE_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C009C00, OPCODE_MASK_H32, teaput, anyware_inst }, + {"tecaput", INST_TYPE_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00BC00, OPCODE_MASK_H32, tecaput, anyware_inst }, + {"tneaput", INST_TYPE_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00DC00, OPCODE_MASK_H32, tneaput, anyware_inst }, + {"tnecaput", INST_TYPE_RFSL, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x6C00FC00, OPCODE_MASK_H32, tnecaput, anyware_inst }, + + {"getd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000000, OPCODE_MASK_H34C, getd, anyware_inst }, + {"tgetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000080, OPCODE_MASK_H34C, tgetd, anyware_inst }, + {"cgetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000100, OPCODE_MASK_H34C, cgetd, anyware_inst }, + {"tcgetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000180, OPCODE_MASK_H34C, tcgetd, anyware_inst }, + {"ngetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000200, OPCODE_MASK_H34C, ngetd, anyware_inst }, + {"tngetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000280, OPCODE_MASK_H34C, tngetd, anyware_inst }, + {"ncgetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000300, OPCODE_MASK_H34C, ncgetd, anyware_inst }, + {"tncgetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000380, OPCODE_MASK_H34C, tncgetd, anyware_inst }, + {"putd", INST_TYPE_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000400, OPCODE_MASK_H34C, putd, anyware_inst }, + {"tputd", INST_TYPE_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000480, OPCODE_MASK_H34C, tputd, anyware_inst }, + {"cputd", INST_TYPE_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000500, OPCODE_MASK_H34C, cputd, anyware_inst }, + {"tcputd", INST_TYPE_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000580, OPCODE_MASK_H34C, tcputd, anyware_inst }, + {"nputd", INST_TYPE_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000600, OPCODE_MASK_H34C, nputd, anyware_inst }, + {"tnputd", INST_TYPE_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000680, OPCODE_MASK_H34C, tnputd, anyware_inst }, + {"ncputd", INST_TYPE_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000700, OPCODE_MASK_H34C, ncputd, anyware_inst }, + {"tncputd", INST_TYPE_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000780, OPCODE_MASK_H34C, tncputd, anyware_inst }, + + {"egetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000020, OPCODE_MASK_H34C, egetd, anyware_inst }, + {"tegetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0000A0, OPCODE_MASK_H34C, tegetd, anyware_inst }, + {"ecgetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000120, OPCODE_MASK_H34C, ecgetd, anyware_inst }, + {"tecgetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0001A0, OPCODE_MASK_H34C, tecgetd, anyware_inst }, + {"negetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000220, OPCODE_MASK_H34C, negetd, anyware_inst }, + {"tnegetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0002A0, OPCODE_MASK_H34C, tnegetd, anyware_inst }, + {"necgetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000320, OPCODE_MASK_H34C, necgetd, anyware_inst }, + {"tnecgetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0003A0, OPCODE_MASK_H34C, tnecgetd, anyware_inst }, + {"eputd", INST_TYPE_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000420, OPCODE_MASK_H34C, eputd, anyware_inst }, + {"teputd", INST_TYPE_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0004A0, OPCODE_MASK_H34C, teputd, anyware_inst }, + {"ecputd", INST_TYPE_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000520, OPCODE_MASK_H34C, ecputd, anyware_inst }, + {"tecputd", INST_TYPE_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0005A0, OPCODE_MASK_H34C, tecputd, anyware_inst }, + {"neputd", INST_TYPE_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000620, OPCODE_MASK_H34C, neputd, anyware_inst }, + {"tneputd", INST_TYPE_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0006A0, OPCODE_MASK_H34C, tneputd, anyware_inst }, + {"necputd", INST_TYPE_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000720, OPCODE_MASK_H34C, necputd, anyware_inst }, + {"tnecputd", INST_TYPE_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0007A0, OPCODE_MASK_H34C, tnecputd, anyware_inst }, + + {"agetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000040, OPCODE_MASK_H34C, agetd, anyware_inst }, + {"tagetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0000C0, OPCODE_MASK_H34C, tagetd, anyware_inst }, + {"cagetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000140, OPCODE_MASK_H34C, cagetd, anyware_inst }, + {"tcagetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0001C0, OPCODE_MASK_H34C, tcagetd, anyware_inst }, + {"nagetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000240, OPCODE_MASK_H34C, nagetd, anyware_inst }, + {"tnagetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0002C0, OPCODE_MASK_H34C, tnagetd, anyware_inst }, + {"ncagetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000340, OPCODE_MASK_H34C, ncagetd, anyware_inst }, + {"tncagetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0003C0, OPCODE_MASK_H34C, tncagetd, anyware_inst }, + {"aputd", INST_TYPE_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000440, OPCODE_MASK_H34C, aputd, anyware_inst }, + {"taputd", INST_TYPE_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0004C0, OPCODE_MASK_H34C, taputd, anyware_inst }, + {"caputd", INST_TYPE_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000540, OPCODE_MASK_H34C, caputd, anyware_inst }, + {"tcaputd", INST_TYPE_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0005C0, OPCODE_MASK_H34C, tcaputd, anyware_inst }, + {"naputd", INST_TYPE_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000640, OPCODE_MASK_H34C, naputd, anyware_inst }, + {"tnaputd", INST_TYPE_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0006C0, OPCODE_MASK_H34C, tnaputd, anyware_inst }, + {"ncaputd", INST_TYPE_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000740, OPCODE_MASK_H34C, ncaputd, anyware_inst }, + {"tncaputd", INST_TYPE_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0007C0, OPCODE_MASK_H34C, tncaputd, anyware_inst }, + + {"eagetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000060, OPCODE_MASK_H34C, eagetd, anyware_inst }, + {"teagetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0000E0, OPCODE_MASK_H34C, teagetd, anyware_inst }, + {"ecagetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000160, OPCODE_MASK_H34C, ecagetd, anyware_inst }, + {"tecagetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0001E0, OPCODE_MASK_H34C, tecagetd, anyware_inst }, + {"neagetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000260, OPCODE_MASK_H34C, neagetd, anyware_inst }, + {"tneagetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0002E0, OPCODE_MASK_H34C, tneagetd, anyware_inst }, + {"necagetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000360, OPCODE_MASK_H34C, necagetd, anyware_inst }, + {"tnecagetd", INST_TYPE_RD_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0003E0, OPCODE_MASK_H34C, tnecagetd, anyware_inst }, + {"eaputd", INST_TYPE_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000460, OPCODE_MASK_H34C, eaputd, anyware_inst }, + {"teaputd", INST_TYPE_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0004E0, OPCODE_MASK_H34C, teaputd, anyware_inst }, + {"ecaputd", INST_TYPE_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000560, OPCODE_MASK_H34C, ecaputd, anyware_inst }, + {"tecaputd", INST_TYPE_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0005E0, OPCODE_MASK_H34C, tecaputd, anyware_inst }, + {"neaputd", INST_TYPE_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000660, OPCODE_MASK_H34C, neaputd, anyware_inst }, + {"tneaputd", INST_TYPE_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0006E0, OPCODE_MASK_H34C, tneaputd, anyware_inst }, + {"necaputd", INST_TYPE_R1_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C000760, OPCODE_MASK_H34C, necaputd, anyware_inst }, + {"tnecaputd", INST_TYPE_R2, INST_NO_OFFSET, NO_DELAY_SLOT, IMMVAL_MASK_NON_SPECIAL, 0x4C0007E0, OPCODE_MASK_H34C, tnecaputd, anyware_inst }, + {"", 0, 0, 0, 0, 0, 0, 0, 0}, }; /* prefix for register names */ char register_prefix[] = "r"; char special_register_prefix[] = "spr"; char fsl_register_prefix[] = "rfsl"; +char pvr_register_prefix[] = "rpvr"; /* #defines for valid immediate range */ -#define MIN_IMM 0x80000000 -#define MAX_IMM 0x7fffffff - -#define MIN_IMM12 0x000 -#define MAX_IMM12 0x7ff +#define MIN_IMM ((int) 0x80000000) +#define MAX_IMM ((int) 0x7fffffff) -#define MIN_IMM14 0x0000 -#define MAX_IMM14 0x1fff +#define MIN_IMM15 ((int) 0x0000) +#define MAX_IMM15 ((int) 0x7fff) #endif /* MICROBLAZE_OPC */ @@ -407,8 +591,42 @@ #define get_int_field_imm(instr) ((instr & IMM_MASK) >> IMM_LOW) #define get_int_field_r1(instr) ((instr & RA_MASK) >> RA_LOW) +/* Local function prototypes. */ + +static char * get_field (long instr, long mask, unsigned short low); +static char * get_field_imm (long instr); +static char * get_field_imm5 (long instr); +static char * get_field_rfsl (long instr); +static char * get_field_imm15 (long instr); +#if 0 +static char * get_field_unsigned_imm (long instr); +#endif +char * get_field_special (long instr, struct op_code_struct * op); +unsigned long read_insn_microblaze (bfd_vma memaddr, + struct disassemble_info *info, + struct op_code_struct **opr); +enum microblaze_instr get_insn_microblaze (long inst, + bfd_boolean *isunsignedimm, + enum microblaze_instr_type *insn_type, + short *delay_slots); +short get_delay_slots_microblaze (long inst); +enum microblaze_instr microblaze_decode_insn (long insn, + int *rd, + int *ra, + int *rb, + int *imm); +unsigned long +microblaze_get_target_address (long inst, + bfd_boolean immfound, + int immval, + long pcval, + long r1val, + long r2val, + bfd_boolean *targetvalid, + bfd_boolean *unconditionalbranch); + static char * -get_field (long instr, long mask, unsigned short low) +get_field (long instr, long mask, unsigned short low) { char tmpstr[25]; sprintf(tmpstr, "%s%d", register_prefix, (int)((instr & mask) >> low)); @@ -416,7 +634,7 @@ } static char * -get_field_imm (long instr) +get_field_imm (long instr) { char tmpstr[25]; sprintf(tmpstr, "%d", (short)((instr & IMM_MASK) >> IMM_LOW)); @@ -424,7 +642,7 @@ } static char * -get_field_imm5 (long instr) +get_field_imm5 (long instr) { char tmpstr[25]; sprintf(tmpstr, "%d", (short)((instr & IMM5_MASK) >> IMM_LOW)); @@ -432,24 +650,24 @@ } static char * -get_field_imm12 (long instr) +get_field_rfsl (long instr) { char tmpstr[25]; - sprintf(tmpstr, "%s%d", fsl_register_prefix, (short)((instr & IMM12_MASK) >> IMM_LOW)); + sprintf(tmpstr, "%s%d", fsl_register_prefix, (short)((instr & RFSL_MASK) >> IMM_LOW)); return(strdup(tmpstr)); } static char * -get_field_imm14 (long instr) +get_field_imm15 (long instr) { char tmpstr[25]; - sprintf(tmpstr, "%d", (short)((instr & IMM14_MASK) >> IMM_LOW)); + sprintf(tmpstr, "%d", (short)((instr & IMM15_MASK) >> IMM_LOW)); return(strdup(tmpstr)); } #if 0 static char * -get_field_unsigned_imm (long instr) +get_field_unsigned_imm (long instr) { char tmpstr[25]; sprintf(tmpstr, "%d", (int)((instr & IMM_MASK) >> IMM_LOW)); @@ -470,13 +688,14 @@ } */ -static char * -get_field_special (long instr, struct op_code_struct * op) +char * +get_field_special (long instr, struct op_code_struct * op) { char tmpstr[25]; - char spr[5]; + char spr[6]; switch ( (((instr & IMM_MASK) >> IMM_LOW) ^ op->immval_mask) ) { + case REG_MSR_MASK : strcpy(spr, "msr"); break; @@ -491,19 +710,51 @@ break; case REG_FSR_MASK : strcpy(spr, "fsr"); + break; + case REG_BTR_MASK : + strcpy(spr, "btr"); break; - default : - strcpy(spr, "pc"); + case REG_EDR_MASK : + strcpy(spr, "edr"); + break; + case REG_PID_MASK : + strcpy(spr, "pid"); + break; + case REG_ZPR_MASK : + strcpy(spr, "zpr"); + break; + case REG_TLBX_MASK : + strcpy(spr, "tlbx"); + break; + case REG_TLBLO_MASK : + strcpy(spr, "tlblo"); + break; + case REG_TLBHI_MASK : + strcpy(spr, "tlbhi"); break; + case REG_TLBSX_MASK : + strcpy(spr, "tlbsx"); + break; + default : + { + if ( ((((instr & IMM_MASK) >> IMM_LOW) ^ op->immval_mask) & 0xE000) == REG_PVR_MASK) { + sprintf(tmpstr, "%spvr%d", register_prefix, (unsigned short)(((instr & IMM_MASK) >> IMM_LOW) ^ op->immval_mask) ^ REG_PVR_MASK); + return(strdup(tmpstr)); + } else { + strcpy(spr, "pc"); + } + } + break; } sprintf(tmpstr, "%s%s", register_prefix, spr); return(strdup(tmpstr)); } -static unsigned long -read_insn_microblaze(bfd_vma memaddr, struct disassemble_info *info, - struct op_code_struct ** opr) +unsigned long +read_insn_microblaze (bfd_vma memaddr, + struct disassemble_info *info, + struct op_code_struct **opr) { unsigned char ibytes[4]; int status; @@ -538,12 +789,12 @@ int print_insn_microblaze (bfd_vma memaddr, struct disassemble_info * info) { - fprintf_ftype fprintf = info->fprintf_func; + fprintf_function fprintf_func = info->fprintf_func; void * stream = info->stream; unsigned long inst, prev_inst; struct op_code_struct * op, *pop; int immval = 0; - bfd_boolean immfound = false; + bfd_boolean immfound = FALSE; static bfd_vma prev_insn_addr = -1; /*init the prev insn addr */ static int prev_insn_vma = -1; /*init the prev insn vma */ int curr_insn_vma = info->buffer_vma; @@ -551,8 +802,9 @@ info->bytes_per_chunk = 4; inst = read_insn_microblaze (memaddr, info, &op); - if (inst == 0) + if (inst == 0) { return -1; + } if (prev_insn_vma == curr_insn_vma) { if (memaddr-(info->bytes_per_chunk) == prev_insn_addr) { @@ -561,11 +813,11 @@ return -1; if (pop->instr == imm) { immval = (get_int_field_imm(prev_inst) << 16) & 0xffff0000; - immfound = true; + immfound = TRUE; } else { immval = 0; - immfound = false; + immfound = FALSE; } } } @@ -573,19 +825,20 @@ prev_insn_addr = memaddr; prev_insn_vma = curr_insn_vma; - if (op->name == 0) - fprintf (stream, ".short 0x%04x", inst); + if (op->name == 0) { + fprintf_func (stream, ".short 0x%04lx", inst); + } else { - fprintf (stream, "%s", op->name); + fprintf_func (stream, "%s", op->name); switch (op->inst_type) { case INST_TYPE_RD_R1_R2: - fprintf(stream, "\t%s, %s, %s", get_field_rd(inst), get_field_r1(inst), get_field_r2(inst)); + fprintf_func(stream, "\t%s, %s, %s", get_field_rd(inst), get_field_r1(inst), get_field_r2(inst)); break; case INST_TYPE_RD_R1_IMM: - fprintf(stream, "\t%s, %s, %s", get_field_rd(inst), get_field_r1(inst), get_field_imm(inst)); + fprintf_func(stream, "\t%s, %s, %s", get_field_rd(inst), get_field_r1(inst), get_field_imm(inst)); if (info->print_address_func && get_int_field_r1(inst) == 0 && info->symbol_at_address_func) { if (immfound) immval |= (get_int_field_imm(inst) & 0x0000ffff); @@ -595,34 +848,34 @@ immval |= 0xFFFF0000; } if (immval > 0 && info->symbol_at_address_func(immval, info)) { - fprintf (stream, "\t// "); + fprintf_func (stream, "\t// "); info->print_address_func (immval, info); } } break; case INST_TYPE_RD_R1_IMM5: - fprintf(stream, "\t%s, %s, %s", get_field_rd(inst), get_field_r1(inst), get_field_imm5(inst)); + fprintf_func(stream, "\t%s, %s, %s", get_field_rd(inst), get_field_r1(inst), get_field_imm5(inst)); break; - case INST_TYPE_RD_IMM12: - fprintf(stream, "\t%s, %s", get_field_rd(inst), get_field_imm12(inst)); + case INST_TYPE_RD_RFSL: + fprintf_func(stream, "\t%s, %s", get_field_rd(inst), get_field_rfsl(inst)); break; - case INST_TYPE_R1_IMM12: - fprintf(stream, "\t%s, %s", get_field_r1(inst), get_field_imm12(inst)); + case INST_TYPE_R1_RFSL: + fprintf_func(stream, "\t%s, %s", get_field_r1(inst), get_field_rfsl(inst)); break; case INST_TYPE_RD_SPECIAL: - fprintf(stream, "\t%s, %s", get_field_rd(inst), get_field_special(inst, op)); + fprintf_func(stream, "\t%s, %s", get_field_rd(inst), get_field_special(inst, op)); break; case INST_TYPE_SPECIAL_R1: - fprintf(stream, "\t%s, %s", get_field_special(inst, op), get_field_r1(inst)); + fprintf_func(stream, "\t%s, %s", get_field_special(inst, op), get_field_r1(inst)); break; case INST_TYPE_RD_R1: - fprintf(stream, "\t%s, %s", get_field_rd(inst), get_field_r1(inst)); + fprintf_func(stream, "\t%s, %s", get_field_rd(inst), get_field_r1(inst)); break; case INST_TYPE_R1_R2: - fprintf(stream, "\t%s, %s", get_field_r1(inst), get_field_r2(inst)); + fprintf_func(stream, "\t%s, %s", get_field_r1(inst), get_field_r2(inst)); break; case INST_TYPE_R1_IMM: - fprintf(stream, "\t%s, %s", get_field_r1(inst), get_field_imm(inst)); + fprintf_func(stream, "\t%s, %s", get_field_r1(inst), get_field_imm(inst)); /* The non-pc relative instructions are returns, which shouldn't have a label printed */ if (info->print_address_func && op->inst_offset_type == INST_PC_OFFSET && info->symbol_at_address_func) { @@ -635,16 +888,16 @@ } immval += memaddr; if (immval > 0 && info->symbol_at_address_func(immval, info)) { - fprintf (stream, "\t// "); + fprintf_func (stream, "\t// "); info->print_address_func (immval, info); } else { - fprintf (stream, "\t\t// "); - fprintf (stream, "%x", immval); + fprintf_func (stream, "\t\t// "); + fprintf_func (stream, "%x", immval); } } break; case INST_TYPE_RD_IMM: - fprintf(stream, "\t%s, %s", get_field_rd(inst), get_field_imm(inst)); + fprintf_func(stream, "\t%s, %s", get_field_rd(inst), get_field_imm(inst)); if (info->print_address_func && info->symbol_at_address_func) { if (immfound) immval |= (get_int_field_imm(inst) & 0x0000ffff); @@ -656,13 +909,13 @@ if (op->inst_offset_type == INST_PC_OFFSET) immval += (int) memaddr; if (info->symbol_at_address_func(immval, info)) { - fprintf (stream, "\t// "); + fprintf_func (stream, "\t// "); info->print_address_func (immval, info); } } break; case INST_TYPE_IMM: - fprintf(stream, "\t%s", get_field_imm(inst)); + fprintf_func(stream, "\t%s", get_field_imm(inst)); if (info->print_address_func && info->symbol_at_address_func && op->instr != imm) { if (immfound) immval |= (get_int_field_imm(inst) & 0x0000ffff); @@ -674,37 +927,39 @@ if (op->inst_offset_type == INST_PC_OFFSET) immval += (int) memaddr; if (immval > 0 && info->symbol_at_address_func(immval, info)) { - fprintf (stream, "\t// "); + fprintf_func (stream, "\t// "); info->print_address_func (immval, info); } else if (op->inst_offset_type == INST_PC_OFFSET) { - fprintf (stream, "\t\t// "); - fprintf (stream, "%x", immval); + fprintf_func (stream, "\t\t// "); + fprintf_func (stream, "%x", immval); } } break; case INST_TYPE_RD_R2: - fprintf(stream, "\t%s, %s", get_field_rd(inst), get_field_r2(inst)); + fprintf_func(stream, "\t%s, %s", get_field_rd(inst), get_field_r2(inst)); break; case INST_TYPE_R2: - fprintf(stream, "\t%s", get_field_r2(inst)); + fprintf_func(stream, "\t%s", get_field_r2(inst)); break; case INST_TYPE_R1: - fprintf(stream, "\t%s", get_field_r1(inst)); + fprintf_func(stream, "\t%s", get_field_r1(inst)); break; case INST_TYPE_RD_R1_SPECIAL: - fprintf(stream, "\t%s, %s", get_field_rd(inst), get_field_r2(inst)); + fprintf_func(stream, "\t%s, %s", get_field_rd(inst), get_field_r2(inst)); break; - case INST_TYPE_RD_IMM14: - fprintf(stream, "\t%s, %s", get_field_rd(inst), get_field_imm14(inst)); + case INST_TYPE_RD_IMM15: + fprintf_func(stream, "\t%s, %s", get_field_rd(inst), get_field_imm15(inst)); break; /* For tuqula instruction */ case INST_TYPE_RD: - fprintf(stream, "\t%s", get_field_rd(inst)); + fprintf_func(stream, "\t%s", get_field_rd(inst)); + break; + case INST_TYPE_RFSL: + fprintf_func(stream, "\t%s", get_field_rfsl(inst)); break; - default: /* if the disassembler lags the instruction set */ - fprintf (stream, "\tundecoded operands, inst is 0x%04x", inst); + fprintf_func (stream, "\tundecoded operands, inst is 0x%04lx", inst); break; } } @@ -713,14 +968,14 @@ return 4; } -#if 0 -static enum microblaze_instr -get_insn_microblaze (long inst, boolean *isunsignedimm, - enum microblaze_instr_type *insn_type, - short *delay_slots ) +enum microblaze_instr +get_insn_microblaze (long inst, + bfd_boolean *isunsignedimm, + enum microblaze_instr_type *insn_type, + short *delay_slots) { struct op_code_struct * op; - *isunsignedimm = false; + *isunsignedimm = FALSE; /* Just a linear search of the table. */ for (op = opcodes; op->name != 0; op ++) @@ -736,13 +991,11 @@ return op->instr; } } -#endif -#if 0 -static short -get_delay_slots_microblaze ( long inst ) +short +get_delay_slots_microblaze (long inst) { - boolean isunsignedimm; + bfd_boolean isunsignedimm; enum microblaze_instr_type insn_type; enum microblaze_instr op; short delay_slots; @@ -753,14 +1006,16 @@ else return delay_slots; } -#endif -#if 0 -static enum microblaze_instr -microblaze_decode_insn (long insn, int *rd, int *ra, int *rb, int *imm) +enum microblaze_instr +microblaze_decode_insn (long insn, + int *rd, + int *ra, + int *rb, + int *imm) { enum microblaze_instr op; - boolean t1; + bfd_boolean t1; enum microblaze_instr_type t2; short t3; @@ -772,40 +1027,42 @@ *imm = (int) t3; return (op); } -#endif -#if 0 -static unsigned long -microblaze_get_target_address (long inst, boolean immfound, int immval, - long pcval, long r1val, long r2val, - boolean *targetvalid, - boolean *unconditionalbranch) +unsigned long +microblaze_get_target_address (long inst, + bfd_boolean immfound, + int immval, + long pcval, + long r1val, + long r2val, + bfd_boolean *targetvalid, + bfd_boolean *unconditionalbranch) { struct op_code_struct * op; long targetaddr = 0; - *unconditionalbranch = false; + *unconditionalbranch = FALSE; /* Just a linear search of the table. */ for (op = opcodes; op->name != 0; op ++) if (op->bit_sequence == (inst & op->opcode_mask)) break; if (op->name == 0) { - *targetvalid = false; + *targetvalid = FALSE; } else if (op->instr_type == branch_inst) { switch (op->inst_type) { case INST_TYPE_R2: - *unconditionalbranch = true; + *unconditionalbranch = TRUE; /* fallthru */ case INST_TYPE_RD_R2: case INST_TYPE_R1_R2: targetaddr = r2val; - *targetvalid = true; + *targetvalid = TRUE; if (op->inst_offset_type == INST_PC_OFFSET) targetaddr += pcval; break; case INST_TYPE_IMM: - *unconditionalbranch = true; + *unconditionalbranch = TRUE; /* fallthru */ case INST_TYPE_RD_IMM: case INST_TYPE_R1_IMM: @@ -819,10 +1076,10 @@ } if (op->inst_offset_type == INST_PC_OFFSET) targetaddr += pcval; - *targetvalid = true; + *targetvalid = TRUE; break; default: - *targetvalid = false; + *targetvalid = FALSE; break; } } else if (op->instr_type == return_inst) { @@ -835,10 +1092,9 @@ targetaddr |= 0xFFFF0000; } targetaddr += r1val; - *targetvalid = true; + *targetvalid = TRUE; } else { - *targetvalid = false; + *targetvalid = FALSE; } return targetaddr; } -#endif diff -Nru qemu-kvm-0.12.5+noroms/migration.c qemu-kvm-0.14.1/migration.c --- qemu-kvm-0.12.5+noroms/migration.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/migration.c 2011-05-11 13:29:46.000000000 +0000 @@ -24,113 +24,137 @@ //#define DEBUG_MIGRATION #ifdef DEBUG_MIGRATION -#define dprintf(fmt, ...) \ +#define DPRINTF(fmt, ...) \ do { printf("migration: " fmt, ## __VA_ARGS__); } while (0) #else -#define dprintf(fmt, ...) \ +#define DPRINTF(fmt, ...) \ do { } while (0) #endif /* Migration speed throttling */ -static uint32_t max_throttle = (32 << 20); +static int64_t max_throttle = (32 << 20); static MigrationState *current_migration; -void qemu_start_incoming_migration(const char *uri) +static NotifierList migration_state_notifiers = + NOTIFIER_LIST_INITIALIZER(migration_state_notifiers); + +int qemu_start_incoming_migration(const char *uri) { const char *p; + int ret; if (strstart(uri, "tcp:", &p)) - tcp_start_incoming_migration(p); + ret = tcp_start_incoming_migration(p); #if !defined(WIN32) else if (strstart(uri, "exec:", &p)) - exec_start_incoming_migration(p); + ret = exec_start_incoming_migration(p); else if (strstart(uri, "unix:", &p)) - unix_start_incoming_migration(p); + ret = unix_start_incoming_migration(p); else if (strstart(uri, "fd:", &p)) - fd_start_incoming_migration(p); + ret = fd_start_incoming_migration(p); #endif - else + else { fprintf(stderr, "unknown migration protocol: %s\n", uri); + ret = -EPROTONOSUPPORT; + } + return ret; +} + +void process_incoming_migration(QEMUFile *f) +{ + if (qemu_loadvm_state(f) < 0) { + fprintf(stderr, "load of migration failed\n"); + exit(0); + } + qemu_announce_self(); + DPRINTF("successfully loaded vm state\n"); + + incoming_expected = false; + + if (autostart) + vm_start(); } -void do_migrate(Monitor *mon, const QDict *qdict, QObject **ret_data) +int do_migrate(Monitor *mon, const QDict *qdict, QObject **ret_data) { MigrationState *s = NULL; const char *p; - int detach = qdict_get_int(qdict, "detach"); + int detach = qdict_get_try_bool(qdict, "detach", 0); + int blk = qdict_get_try_bool(qdict, "blk", 0); + int inc = qdict_get_try_bool(qdict, "inc", 0); const char *uri = qdict_get_str(qdict, "uri"); if (current_migration && current_migration->get_status(current_migration) == MIG_STATE_ACTIVE) { monitor_printf(mon, "migration already in progress\n"); - return; + return -1; } - if (strstart(uri, "tcp:", &p)) + if (qemu_savevm_state_blocked(mon)) { + return -1; + } + + if (strstart(uri, "tcp:", &p)) { s = tcp_start_outgoing_migration(mon, p, max_throttle, detach, - (int)qdict_get_int(qdict, "blk"), - (int)qdict_get_int(qdict, "inc")); + blk, inc); #if !defined(WIN32) - else if (strstart(uri, "exec:", &p)) + } else if (strstart(uri, "exec:", &p)) { s = exec_start_outgoing_migration(mon, p, max_throttle, detach, - (int)qdict_get_int(qdict, "blk"), - (int)qdict_get_int(qdict, "inc")); - else if (strstart(uri, "unix:", &p)) + blk, inc); + } else if (strstart(uri, "unix:", &p)) { s = unix_start_outgoing_migration(mon, p, max_throttle, detach, - (int)qdict_get_int(qdict, "blk"), - (int)qdict_get_int(qdict, "inc")); - else if (strstart(uri, "fd:", &p)) + blk, inc); + } else if (strstart(uri, "fd:", &p)) { s = fd_start_outgoing_migration(mon, p, max_throttle, detach, - (int)qdict_get_int(qdict, "blk"), - (int)qdict_get_int(qdict, "inc")); + blk, inc); #endif - else + } else { monitor_printf(mon, "unknown migration protocol: %s\n", uri); + return -1; + } - if (s == NULL) + if (s == NULL) { monitor_printf(mon, "migration failed\n"); - else { - if (current_migration) - current_migration->release(current_migration); + return -1; + } - current_migration = s; + if (current_migration) { + current_migration->release(current_migration); } + + current_migration = s; + notifier_list_notify(&migration_state_notifiers); + return 0; } -void do_migrate_cancel(Monitor *mon, const QDict *qdict, QObject **ret_data) +int do_migrate_cancel(Monitor *mon, const QDict *qdict, QObject **ret_data) { MigrationState *s = current_migration; if (s) s->cancel(s); + + return 0; } -void do_migrate_set_speed(Monitor *mon, const QDict *qdict) +int do_migrate_set_speed(Monitor *mon, const QDict *qdict, QObject **ret_data) { - double d; - char *ptr; + int64_t d; FdMigrationState *s; - const char *value = qdict_get_str(qdict, "value"); - d = strtod(value, &ptr); - switch (*ptr) { - case 'G': case 'g': - d *= 1024; - case 'M': case 'm': - d *= 1024; - case 'K': case 'k': - d *= 1024; - default: - break; + d = qdict_get_int(qdict, "value"); + if (d < 0) { + d = 0; } - - max_throttle = (uint32_t)d; + max_throttle = d; s = migrate_to_fms(current_migration); if (s && s->file) { qemu_file_set_rate_limit(s->file, max_throttle); } + + return 0; } /* amount of nanoseconds we are willing to wait for migration to be down. @@ -144,24 +168,16 @@ return max_downtime; } -void do_migrate_set_downtime(Monitor *mon, const QDict *qdict) +int do_migrate_set_downtime(Monitor *mon, const QDict *qdict, + QObject **ret_data) { - char *ptr; double d; - const char *value = qdict_get_str(qdict, "value"); - - d = strtod(value, &ptr); - if (!strcmp(ptr,"ms")) { - d *= 1000000; - } else if (!strcmp(ptr,"us")) { - d *= 1000; - } else if (!strcmp(ptr,"ns")) { - } else { - /* all else considered to be seconds */ - d *= 1000000000; - } + d = qdict_get_double(qdict, "value") * 1e9; + d = MAX(0, MIN(UINT64_MAX, d)); max_downtime = (uint64_t)d; + + return 0; } static void migrate_print_status(Monitor *mon, const char *name, @@ -205,49 +221,9 @@ obj = qobject_from_jsonf("{ 'transferred': %" PRId64 ", " "'remaining': %" PRId64 ", " "'total': %" PRId64 " }", trans, rem, total); - assert(obj != NULL); - qdict_put_obj(qdict, name, obj); } -/** - * do_info_migrate(): Migration status - * - * Return a QDict. If migration is active there will be another - * QDict with RAM migration status and if block migration is active - * another one with block migration status. - * - * The main QDict contains the following: - * - * - "status": migration status - * - "ram": only present if "status" is "active", it is a QDict with the - * following RAM information (in bytes): - * - "transferred": amount transferred - * - "remaining": amount remaining - * - "total": total - * - "disk": only present if "status" is "active" and it is a block migration, - * it is a QDict with the following disk information (in bytes): - * - "transferred": amount transferred - * - "remaining": amount remaining - * - "total": total - * - * Examples: - * - * 1. Migration is "completed": - * - * { "status": "completed" } - * - * 2. Migration is "active" and it is not a block migration: - * - * { "status": "active", - * "ram": { "transferred": 123, "remaining": 123, "total": 246 } } - * - * 3. Migration is "active" and it is a block migration: - * - * { "status": "active", - * "ram": { "total": 1057024, "remaining": 1053304, "transferred": 3720 }, - * "disk": { "total": 20971520, "remaining": 20880384, "transferred": 91136 }} - */ void do_info_migrate(Monitor *mon, QObject **ret_data) { QDict *qdict; @@ -280,7 +256,6 @@ *ret_data = qobject_from_jsonf("{ 'status': 'cancelled' }"); break; } - assert(*ret_data != NULL); } } @@ -290,7 +265,7 @@ { s->mon = mon; if (monitor_suspend(mon) == 0) { - dprintf("suspending monitor\n"); + DPRINTF("suspending monitor\n"); } else { monitor_printf(mon, "terminal does not allow synchronous " "migration, continuing detached\n"); @@ -299,18 +274,23 @@ void migrate_fd_error(FdMigrationState *s) { - dprintf("setting error state\n"); + DPRINTF("setting error state\n"); s->state = MIG_STATE_ERROR; + notifier_list_notify(&migration_state_notifiers); migrate_fd_cleanup(s); } -void migrate_fd_cleanup(FdMigrationState *s) +int migrate_fd_cleanup(FdMigrationState *s) { + int ret = 0; + qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL); if (s->file) { - dprintf("closing file\n"); - qemu_fclose(s->file); + DPRINTF("closing file\n"); + if (qemu_fclose(s->file) != 0) { + ret = -1; + } s->file = NULL; } @@ -323,6 +303,8 @@ } s->fd = -1; + + return ret; } void migrate_fd_put_notify(void *opaque) @@ -345,8 +327,15 @@ if (ret == -1) ret = -(s->get_error(s)); - if (ret == -EAGAIN) + if (ret == -EAGAIN) { qemu_set_fd_handler2(s->fd, NULL, NULL, migrate_fd_put_notify, s); + } else if (ret < 0) { + if (s->mon) { + monitor_resume(s->mon); + } + s->state = MIG_STATE_ERROR; + notifier_list_notify(&migration_state_notifiers); + } return ret; } @@ -362,11 +351,11 @@ migrate_fd_wait_for_unfreeze, migrate_fd_close); - dprintf("beginning savevm\n"); + DPRINTF("beginning savevm\n"); ret = qemu_savevm_state_begin(s->mon, s->file, s->mig_state.blk, s->mig_state.shared); if (ret < 0) { - dprintf("failed, %d\n", ret); + DPRINTF("failed, %d\n", ret); migrate_fd_error(s); return; } @@ -379,20 +368,18 @@ FdMigrationState *s = opaque; if (s->state != MIG_STATE_ACTIVE) { - dprintf("put_ready returning because of non-active state\n"); + DPRINTF("put_ready returning because of non-active state\n"); return; } - dprintf("iterate\n"); + DPRINTF("iterate\n"); if (qemu_savevm_state_iterate(s->mon, s->file) == 1) { int state; int old_vm_running = vm_running; - dprintf("done iterating\n"); + DPRINTF("done iterating\n"); vm_stop(0); - qemu_aio_flush(); - bdrv_flush_all(); if ((qemu_savevm_state_complete(s->mon, s->file)) < 0) { if (old_vm_running) { vm_start(); @@ -401,8 +388,14 @@ } else { state = MIG_STATE_COMPLETED; } - migrate_fd_cleanup(s); + if (migrate_fd_cleanup(s) < 0) { + if (old_vm_running) { + vm_start(); + } + state = MIG_STATE_ERROR; + } s->state = state; + notifier_list_notify(&migration_state_notifiers); } } @@ -419,9 +412,10 @@ if (s->state != MIG_STATE_ACTIVE) return; - dprintf("cancelling migration\n"); + DPRINTF("cancelling migration\n"); s->state = MIG_STATE_CANCELLED; + notifier_list_notify(&migration_state_notifiers); qemu_savevm_state_cancel(s->mon, s->file); migrate_fd_cleanup(s); @@ -431,13 +425,14 @@ { FdMigrationState *s = migrate_to_fms(mig_state); - dprintf("releasing state\n"); + DPRINTF("releasing state\n"); if (s->state == MIG_STATE_ACTIVE) { s->state = MIG_STATE_CANCELLED; + notifier_list_notify(&migration_state_notifiers); migrate_fd_cleanup(s); } - free(s); + qemu_free(s); } void migrate_fd_wait_for_unfreeze(void *opaque) @@ -445,7 +440,7 @@ FdMigrationState *s = opaque; int ret; - dprintf("wait for unfreeze\n"); + DPRINTF("wait for unfreeze\n"); if (s->state != MIG_STATE_ACTIVE) return; @@ -466,3 +461,22 @@ qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL); return s->close(s); } + +void add_migration_state_change_notifier(Notifier *notify) +{ + notifier_list_add(&migration_state_notifiers, notify); +} + +void remove_migration_state_change_notifier(Notifier *notify) +{ + notifier_list_remove(&migration_state_notifiers, notify); +} + +int get_migration_state(void) +{ + if (current_migration) { + return migrate_fd_get_status(current_migration); + } else { + return MIG_STATE_ERROR; + } +} diff -Nru qemu-kvm-0.12.5+noroms/migration-exec.c qemu-kvm-0.14.1/migration-exec.c --- qemu-kvm-0.12.5+noroms/migration-exec.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/migration-exec.c 2011-05-11 13:29:46.000000000 +0000 @@ -20,14 +20,16 @@ #include "sysemu.h" #include "buffered_file.h" #include "block.h" +#include +#include //#define DEBUG_MIGRATION_EXEC #ifdef DEBUG_MIGRATION_EXEC -#define dprintf(fmt, ...) \ +#define DPRINTF(fmt, ...) \ do { printf("migration-exec: " fmt, ## __VA_ARGS__); } while (0) #else -#define dprintf(fmt, ...) \ +#define DPRINTF(fmt, ...) \ do { } while (0) #endif @@ -43,13 +45,21 @@ static int exec_close(FdMigrationState *s) { - dprintf("exec_close\n"); + int ret = 0; + DPRINTF("exec_close\n"); if (s->opaque) { - qemu_fclose(s->opaque); + ret = qemu_fclose(s->opaque); s->opaque = NULL; s->fd = -1; + if (ret != -1 && + WIFEXITED(ret) + && WEXITSTATUS(ret) == 0) { + ret = 0; + } else { + ret = -1; + } } - return 0; + return ret; } MigrationState *exec_start_outgoing_migration(Monitor *mon, @@ -66,13 +76,13 @@ f = popen(command, "w"); if (f == NULL) { - dprintf("Unable to popen exec target\n"); + DPRINTF("Unable to popen exec target\n"); goto err_after_alloc; } s->fd = fileno(f); if (s->fd == -1) { - dprintf("Unable to retrieve file descriptor for popen'd handle\n"); + DPRINTF("Unable to retrieve file descriptor for popen'd handle\n"); goto err_after_open; } @@ -111,21 +121,9 @@ static void exec_accept_incoming_migration(void *opaque) { QEMUFile *f = opaque; - int ret; - ret = qemu_loadvm_state(f); - if (ret < 0) { - fprintf(stderr, "load of migration failed\n"); - goto err; - } - qemu_announce_self(); - dprintf("successfully loaded vm state\n"); - /* we've successfully migrated, close the fd */ + process_incoming_migration(f); qemu_set_fd_handler2(qemu_stdio_fd(f), NULL, NULL, NULL, NULL); - if (autostart) - vm_start(); - -err: qemu_fclose(f); } @@ -133,16 +131,15 @@ { QEMUFile *f; - dprintf("Attempting to start an incoming migration\n"); + DPRINTF("Attempting to start an incoming migration\n"); f = qemu_popen_cmd(command, "r"); if(f == NULL) { - dprintf("Unable to apply qemu wrapper to popen file\n"); + DPRINTF("Unable to apply qemu wrapper to popen file\n"); return -errno; } qemu_set_fd_handler2(qemu_stdio_fd(f), NULL, - exec_accept_incoming_migration, NULL, - (void *)(unsigned long)f); + exec_accept_incoming_migration, NULL, f); return 0; } diff -Nru qemu-kvm-0.12.5+noroms/migration-fd.c qemu-kvm-0.14.1/migration-fd.c --- qemu-kvm-0.12.5+noroms/migration-fd.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/migration-fd.c 2011-05-11 13:29:46.000000000 +0000 @@ -24,10 +24,10 @@ //#define DEBUG_MIGRATION_FD #ifdef DEBUG_MIGRATION_FD -#define dprintf(fmt, ...) \ +#define DPRINTF(fmt, ...) \ do { printf("migration-fd: " fmt, ## __VA_ARGS__); } while (0) #else -#define dprintf(fmt, ...) \ +#define DPRINTF(fmt, ...) \ do { } while (0) #endif @@ -43,7 +43,7 @@ static int fd_close(FdMigrationState *s) { - dprintf("fd_close\n"); + DPRINTF("fd_close\n"); if (s->fd != -1) { close(s->fd); s->fd = -1; @@ -64,12 +64,12 @@ s->fd = monitor_get_fd(mon, fdname); if (s->fd == -1) { - dprintf("fd_migration: invalid file descriptor identifier\n"); + DPRINTF("fd_migration: invalid file descriptor identifier\n"); goto err_after_alloc; } if (fcntl(s->fd, F_SETFL, O_NONBLOCK) == -1) { - dprintf("Unable to set nonblocking mode on file descriptor\n"); + DPRINTF("Unable to set nonblocking mode on file descriptor\n"); goto err_after_open; } @@ -104,21 +104,9 @@ static void fd_accept_incoming_migration(void *opaque) { QEMUFile *f = opaque; - int ret; - ret = qemu_loadvm_state(f); - if (ret < 0) { - fprintf(stderr, "load of migration failed\n"); - goto err; - } - qemu_announce_self(); - dprintf("successfully loaded vm state\n"); - /* we've successfully migrated, close the fd */ + process_incoming_migration(f); qemu_set_fd_handler2(qemu_stdio_fd(f), NULL, NULL, NULL, NULL); - if (autostart) - vm_start(); - -err: qemu_fclose(f); } @@ -127,17 +115,16 @@ int fd; QEMUFile *f; - dprintf("Attempting to start an incoming migration via fd\n"); + DPRINTF("Attempting to start an incoming migration via fd\n"); fd = strtol(infd, NULL, 0); f = qemu_fdopen(fd, "rb"); if(f == NULL) { - dprintf("Unable to apply qemu wrapper to file descriptor\n"); + DPRINTF("Unable to apply qemu wrapper to file descriptor\n"); return -errno; } - qemu_set_fd_handler2(fd, NULL, fd_accept_incoming_migration, NULL, - (void *)(unsigned long)f); + qemu_set_fd_handler2(fd, NULL, fd_accept_incoming_migration, NULL, f); return 0; } diff -Nru qemu-kvm-0.12.5+noroms/migration.h qemu-kvm-0.14.1/migration.h --- qemu-kvm-0.12.5+noroms/migration.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/migration.h 2011-05-11 13:29:46.000000000 +0000 @@ -16,6 +16,7 @@ #include "qdict.h" #include "qemu-common.h" +#include "notify.h" #define MIG_STATE_ERROR -1 #define MIG_STATE_COMPLETED 0 @@ -50,17 +51,20 @@ void *opaque; }; -void qemu_start_incoming_migration(const char *uri); +void process_incoming_migration(QEMUFile *f); -void do_migrate(Monitor *mon, const QDict *qdict, QObject **ret_data); +int qemu_start_incoming_migration(const char *uri); -void do_migrate_cancel(Monitor *mon, const QDict *qdict, QObject **ret_data); +int do_migrate(Monitor *mon, const QDict *qdict, QObject **ret_data); -void do_migrate_set_speed(Monitor *mon, const QDict *qdict); +int do_migrate_cancel(Monitor *mon, const QDict *qdict, QObject **ret_data); + +int do_migrate_set_speed(Monitor *mon, const QDict *qdict, QObject **ret_data); uint64_t migrate_max_downtime(void); -void do_migrate_set_downtime(Monitor *mon, const QDict *qdict); +int do_migrate_set_downtime(Monitor *mon, const QDict *qdict, + QObject **ret_data); void do_info_migrate_print(Monitor *mon, const QObject *data); @@ -106,7 +110,7 @@ void migrate_fd_error(FdMigrationState *s); -void migrate_fd_cleanup(FdMigrationState *s); +int migrate_fd_cleanup(FdMigrationState *s); void migrate_fd_put_notify(void *opaque); @@ -131,4 +135,8 @@ return container_of(mig_state, FdMigrationState, mig_state); } +void add_migration_state_change_notifier(Notifier *notify); +void remove_migration_state_change_notifier(Notifier *notify); +int get_migration_state(void); + #endif diff -Nru qemu-kvm-0.12.5+noroms/migration-tcp.c qemu-kvm-0.14.1/migration-tcp.c --- qemu-kvm-0.12.5+noroms/migration-tcp.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/migration-tcp.c 2011-05-11 13:29:46.000000000 +0000 @@ -22,10 +22,10 @@ //#define DEBUG_MIGRATION_TCP #ifdef DEBUG_MIGRATION_TCP -#define dprintf(fmt, ...) \ +#define DPRINTF(fmt, ...) \ do { printf("migration-tcp: " fmt, ## __VA_ARGS__); } while (0) #else -#define dprintf(fmt, ...) \ +#define DPRINTF(fmt, ...) \ do { } while (0) #endif @@ -41,7 +41,7 @@ static int tcp_close(FdMigrationState *s) { - dprintf("tcp_close\n"); + DPRINTF("tcp_close\n"); if (s->fd != -1) { close(s->fd); s->fd = -1; @@ -56,7 +56,7 @@ int val, ret; socklen_t valsize = sizeof(val); - dprintf("connect completed\n"); + DPRINTF("connect completed\n"); do { ret = getsockopt(s->fd, SOL_SOCKET, SO_ERROR, (void *) &val, &valsize); } while (ret == -1 && (s->get_error(s)) == EINTR); @@ -71,7 +71,7 @@ if (val == 0) migrate_fd_connect(s); else { - dprintf("error connecting %d\n", val); + DPRINTF("error connecting %d\n", val); migrate_fd_error(s); } } @@ -127,10 +127,8 @@ } while (ret == -EINTR); if (ret < 0 && ret != -EINPROGRESS && ret != -EWOULDBLOCK) { - dprintf("connect failed\n"); - close(s->fd); - qemu_free(s); - return NULL; + DPRINTF("connect failed\n"); + migrate_fd_error(s); } else if (ret >= 0) migrate_fd_connect(s); @@ -143,17 +141,17 @@ socklen_t addrlen = sizeof(addr); int s = (unsigned long)opaque; QEMUFile *f; - int c, ret; + int c; do { c = qemu_accept(s, (struct sockaddr *)&addr, &addrlen); } while (c == -1 && socket_error() == EINTR); - dprintf("accepted migration\n"); + DPRINTF("accepted migration\n"); if (c == -1) { fprintf(stderr, "could not accept migration connection\n"); - return; + goto out2; } f = qemu_fopen_socket(c); @@ -162,24 +160,13 @@ goto out; } - ret = qemu_loadvm_state(f); - if (ret < 0) { - fprintf(stderr, "load of migration failed\n"); - goto out_fopen; - } - qemu_announce_self(); - dprintf("successfully loaded vm state\n"); - - /* we've successfully migrated, close the server socket */ - qemu_set_fd_handler2(s, NULL, NULL, NULL, NULL); - close(s); - if (autostart) - vm_start(); - -out_fopen: + process_incoming_migration(f); qemu_fclose(f); out: close(c); +out2: + qemu_set_fd_handler2(s, NULL, NULL, NULL, NULL); + close(s); } int tcp_start_incoming_migration(const char *host_port) diff -Nru qemu-kvm-0.12.5+noroms/migration-unix.c qemu-kvm-0.14.1/migration-unix.c --- qemu-kvm-0.12.5+noroms/migration-unix.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/migration-unix.c 2011-05-11 13:29:46.000000000 +0000 @@ -22,10 +22,10 @@ //#define DEBUG_MIGRATION_UNIX #ifdef DEBUG_MIGRATION_UNIX -#define dprintf(fmt, ...) \ +#define DPRINTF(fmt, ...) \ do { printf("migration-unix: " fmt, ## __VA_ARGS__); } while (0) #else -#define dprintf(fmt, ...) \ +#define DPRINTF(fmt, ...) \ do { } while (0) #endif @@ -41,7 +41,7 @@ static int unix_close(FdMigrationState *s) { - dprintf("unix_close\n"); + DPRINTF("unix_close\n"); if (s->fd != -1) { close(s->fd); s->fd = -1; @@ -55,7 +55,7 @@ int val, ret; socklen_t valsize = sizeof(val); - dprintf("connect completed\n"); + DPRINTF("connect completed\n"); do { ret = getsockopt(s->fd, SOL_SOCKET, SO_ERROR, (void *) &val, &valsize); } while (ret == -1 && (s->get_error(s)) == EINTR); @@ -70,7 +70,7 @@ if (val == 0) migrate_fd_connect(s); else { - dprintf("error connecting %d\n", val); + DPRINTF("error connecting %d\n", val); migrate_fd_error(s); } } @@ -106,7 +106,7 @@ s->bandwidth_limit = bandwidth_limit; s->fd = qemu_socket(PF_UNIX, SOCK_STREAM, 0); if (s->fd < 0) { - dprintf("Unable to open socket"); + DPRINTF("Unable to open socket"); goto err_after_alloc; } @@ -122,7 +122,7 @@ } while (ret == -EINTR); if (ret < 0 && ret != -EINPROGRESS && ret != -EWOULDBLOCK) { - dprintf("connect failed\n"); + DPRINTF("connect failed\n"); goto err_after_open; } @@ -149,13 +149,13 @@ socklen_t addrlen = sizeof(addr); int s = (unsigned long)opaque; QEMUFile *f; - int c, ret; + int c; do { c = qemu_accept(s, (struct sockaddr *)&addr, &addrlen); } while (c == -1 && socket_error() == EINTR); - dprintf("accepted migration\n"); + DPRINTF("accepted migration\n"); if (c == -1) { fprintf(stderr, "could not accept migration connection\n"); @@ -168,21 +168,11 @@ goto out; } - ret = qemu_loadvm_state(f); - if (ret < 0) { - fprintf(stderr, "load of migration failed\n"); - goto out_fopen; - } - qemu_announce_self(); - dprintf("successfully loaded vm state\n"); - - /* we've successfully migrated, close the server socket */ - qemu_set_fd_handler2(s, NULL, NULL, NULL, NULL); - close(s); - -out_fopen: + process_incoming_migration(f); qemu_fclose(f); out: + qemu_set_fd_handler2(s, NULL, NULL, NULL, NULL); + close(s); close(c); } @@ -191,7 +181,7 @@ struct sockaddr_un un; int sock; - dprintf("Attempting to start an incoming migration\n"); + DPRINTF("Attempting to start an incoming migration\n"); sock = qemu_socket(PF_UNIX, SOCK_STREAM, 0); if (sock < 0) { diff -Nru qemu-kvm-0.12.5+noroms/mips-dis.c qemu-kvm-0.14.1/mips-dis.c --- qemu-kvm-0.12.5+noroms/mips-dis.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/mips-dis.c 2011-05-11 13:29:46.000000000 +0000 @@ -565,6 +565,11 @@ /* DSP R2 ASE */ #define INSN_DSPR2 0x20000000 +/* ST Microelectronics Loongson 2E. */ +#define INSN_LOONGSON_2E 0x40000000 +/* ST Microelectronics Loongson 2F. */ +#define INSN_LOONGSON_2F 0x80000000 + /* MIPS ISA defines, use instead of hardcoding ISA level. */ #define ISA_UNKNOWN 0 /* Gas internal use. */ @@ -1129,6 +1134,9 @@ /* MIPS64 MDMX ASE support. */ #define MX INSN_MDMX +#define IL2E (INSN_LOONGSON_2E) +#define IL2F (INSN_LOONGSON_2F) + #define P3 INSN_4650 #define L1 INSN_4010 #define V1 (INSN_4100 | INSN_4111 | INSN_4120) @@ -2719,6 +2727,31 @@ {"bc0fl", "p", 0x41020000, 0xffff0000, CBL|RD_CC, 0, I2|T3 }, {"bc0t", "p", 0x41010000, 0xffff0000, CBD|RD_CC, 0, I1 }, {"bc0tl", "p", 0x41030000, 0xffff0000, CBL|RD_CC, 0, I2|T3 }, +/* ST Microelectronics Loongson-2E and -2F. */ +{"mult.g", "d,s,t", 0x7c000018, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2E }, +{"mult.g", "d,s,t", 0x70000010, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2F }, +{"multu.g", "d,s,t", 0x7c000019, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2E }, +{"multu.g", "d,s,t", 0x70000012, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2F }, +{"dmult.g", "d,s,t", 0x7c00001c, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2E }, +{"dmult.g", "d,s,t", 0x70000011, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2F }, +{"dmultu.g", "d,s,t", 0x7c00001d, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2E }, +{"dmultu.g", "d,s,t", 0x70000013, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2F }, +{"div.g", "d,s,t", 0x7c00001a, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2E }, +{"div.g", "d,s,t", 0x70000014, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2F }, +{"divu.g", "d,s,t", 0x7c00001b, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2E }, +{"divu.g", "d,s,t", 0x70000016, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2F }, +{"ddiv.g", "d,s,t", 0x7c00001e, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2E }, +{"ddiv.g", "d,s,t", 0x70000015, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2F }, +{"ddivu.g", "d,s,t", 0x7c00001f, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2E }, +{"ddivu.g", "d,s,t", 0x70000017, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2F }, +{"mod.g", "d,s,t", 0x7c000022, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2E }, +{"mod.g", "d,s,t", 0x7000001c, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2F }, +{"modu.g", "d,s,t", 0x7c000023, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2E }, +{"modu.g", "d,s,t", 0x7000001e, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2F }, +{"dmod.g", "d,s,t", 0x7c000026, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2E }, +{"dmod.g", "d,s,t", 0x7000001d, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2F }, +{"dmodu.g", "d,s,t", 0x7c000027, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2E }, +{"dmodu.g", "d,s,t", 0x7000001f, 0xfc0007ff, RD_s|RD_t|WR_d, 0, IL2F }, }; #define MIPS_NUM_OPCODES \ @@ -3035,7 +3068,7 @@ const char * const *fpr_names; }; -struct mips_abi_choice mips_abi_choices[] = +static struct mips_abi_choice mips_abi_choices[] = { { "numeric", mips_gpr_names_numeric, mips_fpr_names_numeric }, { "32", mips_gpr_names_oldabi, mips_fpr_names_32 }, @@ -3084,9 +3117,7 @@ #define bfd_mach_mipsisa64 64 #define bfd_mach_mipsisa64r2 65 -#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) - -const struct mips_arch_choice mips_arch_choices[] = +static const struct mips_arch_choice mips_arch_choices[] = { { "numeric", 0, 0, 0, 0, mips_cp0_names_numeric, NULL, 0, mips_hwr_names_numeric }, diff -Nru qemu-kvm-0.12.5+noroms/monitor.c qemu-kvm-0.14.1/monitor.c --- qemu-kvm-0.12.5+noroms/monitor.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/monitor.c 2011-05-11 13:29:46.000000000 +0000 @@ -34,11 +34,12 @@ #include "net.h" #include "net/slirp.h" #include "qemu-char.h" +#include "ui/qemu-spice.h" #include "sysemu.h" #include "monitor.h" #include "readline.h" #include "console.h" -#include "block.h" +#include "blockdev.h" #include "audio/audio.h" #include "disas.h" #include "balloon.h" @@ -47,18 +48,19 @@ #include "kvm.h" #include "acl.h" #include "qint.h" +#include "qfloat.h" #include "qlist.h" -#include "qdict.h" #include "qbool.h" #include "qstring.h" -#include "qerror.h" #include "qjson.h" #include "json-streamer.h" #include "json-parser.h" #include "osdep.h" #include "exec-all.h" - -#include "qemu-kvm.h" +#ifdef CONFIG_SIMPLE_TRACE +#include "trace.h" +#endif +#include "ui/qemu-spice.h" //#define DEBUG //#define DEBUG_COMPLETION @@ -69,16 +71,39 @@ * 'F' filename * 'B' block device name * 's' string (accept optional quote) + * 'O' option string of the form NAME=VALUE,... + * parsed according to QemuOptsList given by its name + * Example: 'device:O' uses qemu_device_opts. + * Restriction: only lists with empty desc are supported + * TODO lift the restriction * 'i' 32 bit integer * 'l' target long (32 or 64 bit) + * 'M' just like 'l', except in user mode the value is + * multiplied by 2^20 (think Mebibyte) + * 'o' octets (aka bytes) + * user mode accepts an optional T, t, G, g, M, m, K, k + * suffix, which multiplies the value by 2^40 for + * suffixes T and t, 2^30 for suffixes G and g, 2^20 for + * M and m, 2^10 for K and k + * 'T' double + * user mode accepts an optional ms, us, ns suffix, + * which divides the value by 1e3, 1e6, 1e9, respectively * '/' optional gdb-like print format (like "/10x") * * '?' optional type (for all types, except '/') * '.' other form of optional type (for 'i' and 'l') + * 'b' boolean + * user mode accepts "on" or "off" * '-' optional parameter (eg. '-f') * */ +typedef struct MonitorCompletionData MonitorCompletionData; +struct MonitorCompletionData { + Monitor *mon; + void (*user_print)(Monitor *mon, const QObject *data); +}; + typedef struct mon_cmd_t { const char *name; const char *args_type; @@ -88,9 +113,13 @@ union { void (*info)(Monitor *mon); void (*info_new)(Monitor *mon, QObject **ret_data); + int (*info_async)(Monitor *mon, MonitorCompletion *cb, void *opaque); void (*cmd)(Monitor *mon, const QDict *qdict); - void (*cmd_new)(Monitor *mon, const QDict *params, QObject **ret_data); + int (*cmd_new)(Monitor *mon, const QDict *params, QObject **ret_data); + int (*cmd_async)(Monitor *mon, const QDict *params, + MonitorCompletion *cb, void *opaque); } mhandler; + int flags; } mon_cmd_t; /* file descriptors passed via SCM_RIGHTS */ @@ -103,8 +132,8 @@ typedef struct MonitorControl { QObject *id; - int print_enabled; JSONMessageParser parser; + int command_mode; } MonitorControl; struct Monitor { @@ -120,27 +149,75 @@ CPUState *mon_cpu; BlockDriverCompletionFunc *password_completion_cb; void *password_opaque; +#ifdef CONFIG_DEBUG_MONITOR + int print_calls_nr; +#endif QError *error; QLIST_HEAD(,mon_fd_t) fds; QLIST_ENTRY(Monitor) entry; }; +#ifdef CONFIG_DEBUG_MONITOR +#define MON_DEBUG(fmt, ...) do { \ + fprintf(stderr, "Monitor: "); \ + fprintf(stderr, fmt, ## __VA_ARGS__); } while (0) + +static inline void mon_print_count_inc(Monitor *mon) +{ + mon->print_calls_nr++; +} + +static inline void mon_print_count_init(Monitor *mon) +{ + mon->print_calls_nr = 0; +} + +static inline int mon_print_count_get(const Monitor *mon) +{ + return mon->print_calls_nr; +} + +#else /* !CONFIG_DEBUG_MONITOR */ +#define MON_DEBUG(fmt, ...) do { } while (0) +static inline void mon_print_count_inc(Monitor *mon) { } +static inline void mon_print_count_init(Monitor *mon) { } +static inline int mon_print_count_get(const Monitor *mon) { return 0; } +#endif /* CONFIG_DEBUG_MONITOR */ + +/* QMP checker flags */ +#define QMP_ACCEPT_UNKNOWNS 1 + static QLIST_HEAD(mon_list, Monitor) mon_list; static const mon_cmd_t mon_cmds[]; static const mon_cmd_t info_cmds[]; -Monitor *cur_mon = NULL; +static const mon_cmd_t qmp_cmds[]; +static const mon_cmd_t qmp_query_cmds[]; + +Monitor *cur_mon; +Monitor *default_mon; static void monitor_command_cb(Monitor *mon, const char *cmdline, void *opaque); +static inline int qmp_cmd_mode(const Monitor *mon) +{ + return (mon->mc ? mon->mc->command_mode : 0); +} + /* Return true if in control mode, false otherwise */ static inline int monitor_ctrl_mode(const Monitor *mon) { return (mon->flags & MONITOR_USE_CONTROL); } +/* Return non-zero iff we have a current monitor, and it is in QMP mode. */ +int monitor_cur_is_qmp(void) +{ + return cur_mon && monitor_ctrl_mode(cur_mon); +} + static void monitor_read_command(Monitor *mon, int show_prompt) { if (!mon->rs) @@ -155,7 +232,7 @@ void *opaque) { if (monitor_ctrl_mode(mon)) { - qemu_error_new(QERR_MISSING_PARAMETER, "password"); + qerror_report(QERR_MISSING_PARAMETER, "password"); return -EINVAL; } else if (mon->rs) { readline_start(mon->rs, "Password: ", 1, readline_func, opaque); @@ -195,16 +272,19 @@ void monitor_vprintf(Monitor *mon, const char *fmt, va_list ap) { + char buf[4096]; + if (!mon) return; - if (mon->mc && !mon->mc->print_enabled) { - qemu_error_new(QERR_UNDEFINED_ERROR); - } else { - char buf[4096]; - vsnprintf(buf, sizeof(buf), fmt, ap); - monitor_puts(mon, buf); + mon_print_count_inc(mon); + + if (monitor_ctrl_mode(mon)) { + return; } + + vsnprintf(buf, sizeof(buf), fmt, ap); + monitor_puts(mon, buf); } void monitor_printf(Monitor *mon, const char *fmt, ...) @@ -242,7 +322,8 @@ } } -static int monitor_fprintf(FILE *stream, const char *fmt, ...) +static int GCC_FMT_ATTR(2, 3) monitor_fprintf(FILE *stream, + const char *fmt, ...) { va_list ap; va_start(ap, fmt); @@ -253,11 +334,16 @@ static void monitor_user_noop(Monitor *mon, const QObject *data) { } -static inline int monitor_handler_ported(const mon_cmd_t *cmd) +static inline int handler_is_qobject(const mon_cmd_t *cmd) { return cmd->user_print != NULL; } +static inline bool handler_is_async(const mon_cmd_t *cmd) +{ + return cmd->flags & MONITOR_CMD_ASYNC; +} + static inline int monitor_has_error(const Monitor *mon) { return mon->error != NULL; @@ -267,12 +353,12 @@ { QString *json; - json = qobject_to_json(data); + json = mon->flags & MONITOR_USE_PRETTY ? qobject_to_json_pretty(data) : + qobject_to_json(data); assert(json != NULL); - mon->mc->print_enabled = 1; - monitor_printf(mon, "%s\n", qstring_get_str(json)); - mon->mc->print_enabled = 0; + qstring_append_chr(json, '\n'); + monitor_puts(mon, qstring_get_str(json)); QDECREF(json); } @@ -323,8 +409,6 @@ obj = qobject_from_jsonf("{ 'seconds': %" PRId64 ", " "'microseconds': %" PRId64 " }", (int64_t) tv.tv_sec, (int64_t) tv.tv_usec); - assert(obj != NULL); - qdict_put_obj(qdict, "timestamp", obj); } @@ -342,9 +426,6 @@ assert(event < QEVENT_MAX); switch (event) { - case QEVENT_DEBUG: - event_name = "DEBUG"; - break; case QEVENT_SHUTDOWN: event_name = "SHUTDOWN"; break; @@ -357,6 +438,36 @@ case QEVENT_STOP: event_name = "STOP"; break; + case QEVENT_RESUME: + event_name = "RESUME"; + break; + case QEVENT_VNC_CONNECTED: + event_name = "VNC_CONNECTED"; + break; + case QEVENT_VNC_INITIALIZED: + event_name = "VNC_INITIALIZED"; + break; + case QEVENT_VNC_DISCONNECTED: + event_name = "VNC_DISCONNECTED"; + break; + case QEVENT_BLOCK_IO_ERROR: + event_name = "BLOCK_IO_ERROR"; + break; + case QEVENT_RTC_CHANGE: + event_name = "RTC_CHANGE"; + break; + case QEVENT_WATCHDOG: + event_name = "WATCHDOG"; + break; + case QEVENT_SPICE_CONNECTED: + event_name = "SPICE_CONNECTED"; + break; + case QEVENT_SPICE_INITIALIZED: + event_name = "SPICE_INITIALIZED"; + break; + case QEVENT_SPICE_DISCONNECTED: + event_name = "SPICE_DISCONNECTED"; + break; default: abort(); break; @@ -371,13 +482,62 @@ } QLIST_FOREACH(mon, &mon_list, entry) { - if (monitor_ctrl_mode(mon)) { + if (monitor_ctrl_mode(mon) && qmp_cmd_mode(mon)) { monitor_json_emitter(mon, QOBJECT(qmp)); } } QDECREF(qmp); } +static int do_qmp_capabilities(Monitor *mon, const QDict *params, + QObject **ret_data) +{ + /* Will setup QMP capabilities in the future */ + if (monitor_ctrl_mode(mon)) { + mon->mc->command_mode = 1; + } + + return 0; +} + +static int mon_set_cpu(int cpu_index); +static void handle_user_command(Monitor *mon, const char *cmdline); + +static int do_hmp_passthrough(Monitor *mon, const QDict *params, + QObject **ret_data) +{ + int ret = 0; + Monitor *old_mon, hmp; + CharDriverState mchar; + + memset(&hmp, 0, sizeof(hmp)); + qemu_chr_init_mem(&mchar); + hmp.chr = &mchar; + + old_mon = cur_mon; + cur_mon = &hmp; + + if (qdict_haskey(params, "cpu-index")) { + ret = mon_set_cpu(qdict_get_int(params, "cpu-index")); + if (ret < 0) { + cur_mon = old_mon; + qerror_report(QERR_INVALID_PARAMETER_VALUE, "cpu-index", "a CPU number"); + goto out; + } + } + + handle_user_command(&hmp, qdict_get_str(params, "command-line")); + cur_mon = old_mon; + + if (qemu_chr_mem_osize(hmp.chr) > 0) { + *ret_data = QOBJECT(qemu_chr_mem_to_qs(hmp.chr)); + } + +out: + qemu_chr_close_mem(hmp.chr); + return ret; +} + static int compare_cmd(const char *name, const char *list) { const char *p, *pstart; @@ -432,28 +592,107 @@ help_cmd(mon, qdict_get_try_str(qdict, "name")); } -static void do_commit(Monitor *mon, const QDict *qdict) +#ifdef CONFIG_SIMPLE_TRACE +static void do_change_trace_event_state(Monitor *mon, const QDict *qdict) { - int all_devices; - DriveInfo *dinfo; - const char *device = qdict_get_str(qdict, "device"); + const char *tp_name = qdict_get_str(qdict, "name"); + bool new_state = qdict_get_bool(qdict, "option"); + int ret = st_change_trace_event_state(tp_name, new_state); - all_devices = !strcmp(device, "all"); - QTAILQ_FOREACH(dinfo, &drives, next) { - if (!all_devices) - if (strcmp(bdrv_get_device_name(dinfo->bdrv), device)) - continue; - bdrv_commit(dinfo->bdrv); + if (!ret) { + monitor_printf(mon, "unknown event name \"%s\"\n", tp_name); + } +} + +static void do_trace_file(Monitor *mon, const QDict *qdict) +{ + const char *op = qdict_get_try_str(qdict, "op"); + const char *arg = qdict_get_try_str(qdict, "arg"); + + if (!op) { + st_print_trace_file_status((FILE *)mon, &monitor_fprintf); + } else if (!strcmp(op, "on")) { + st_set_trace_file_enabled(true); + } else if (!strcmp(op, "off")) { + st_set_trace_file_enabled(false); + } else if (!strcmp(op, "flush")) { + st_flush_trace_buffer(); + } else if (!strcmp(op, "set")) { + if (arg) { + st_set_trace_file(arg); + } + } else { + monitor_printf(mon, "unexpected argument \"%s\"\n", op); + help_cmd(mon, "trace-file"); + } +} +#endif + +static void user_monitor_complete(void *opaque, QObject *ret_data) +{ + MonitorCompletionData *data = (MonitorCompletionData *)opaque; + + if (ret_data) { + data->user_print(data->mon, ret_data); + } + monitor_resume(data->mon); + qemu_free(data); +} + +static void qmp_monitor_complete(void *opaque, QObject *ret_data) +{ + monitor_protocol_emitter(opaque, ret_data); +} + +static int qmp_async_cmd_handler(Monitor *mon, const mon_cmd_t *cmd, + const QDict *params) +{ + return cmd->mhandler.cmd_async(mon, params, qmp_monitor_complete, mon); +} + +static void qmp_async_info_handler(Monitor *mon, const mon_cmd_t *cmd) +{ + cmd->mhandler.info_async(mon, qmp_monitor_complete, mon); +} + +static void user_async_cmd_handler(Monitor *mon, const mon_cmd_t *cmd, + const QDict *params) +{ + int ret; + + MonitorCompletionData *cb_data = qemu_malloc(sizeof(*cb_data)); + cb_data->mon = mon; + cb_data->user_print = cmd->user_print; + monitor_suspend(mon); + ret = cmd->mhandler.cmd_async(mon, params, + user_monitor_complete, cb_data); + if (ret < 0) { + monitor_resume(mon); + qemu_free(cb_data); + } +} + +static void user_async_info_handler(Monitor *mon, const mon_cmd_t *cmd) +{ + int ret; + + MonitorCompletionData *cb_data = qemu_malloc(sizeof(*cb_data)); + cb_data->mon = mon; + cb_data->user_print = cmd->user_print; + monitor_suspend(mon); + ret = cmd->mhandler.info_async(mon, user_monitor_complete, cb_data); + if (ret < 0) { + monitor_resume(mon); + qemu_free(cb_data); } } -static void do_info(Monitor *mon, const QDict *qdict, QObject **ret_data) +static void do_info(Monitor *mon, const QDict *qdict) { const mon_cmd_t *cmd; const char *item = qdict_get_try_str(qdict, "item"); if (!item) { - assert(monitor_ctrl_mode(mon) == 0); goto help; } @@ -463,31 +702,21 @@ } if (cmd->name == NULL) { - if (monitor_ctrl_mode(mon)) { - qemu_error_new(QERR_COMMAND_NOT_FOUND, item); - return; - } goto help; } - if (monitor_handler_ported(cmd)) { - cmd->mhandler.info_new(mon, ret_data); - - if (!monitor_ctrl_mode(mon)) { - /* - * User Protocol function is called here, Monitor Protocol is - * handled by monitor_call_handler() - */ - if (*ret_data) - cmd->user_print(mon, *ret_data); + if (handler_is_async(cmd)) { + user_async_info_handler(mon, cmd); + } else if (handler_is_qobject(cmd)) { + QObject *info_data = NULL; + + cmd->mhandler.info_new(mon, &info_data); + if (info_data) { + cmd->user_print(mon, info_data); + qobject_decref(info_data); } } else { - if (monitor_ctrl_mode(mon)) { - /* handler not converted yet */ - qemu_error_new(QERR_COMMAND_NOT_FOUND, item); - } else { - cmd->mhandler.info(mon); - } + cmd->mhandler.info(mon); } return; @@ -499,29 +728,32 @@ static void do_info_version_print(Monitor *mon, const QObject *data) { QDict *qdict; + QDict *qemu; qdict = qobject_to_qdict(data); + qemu = qdict_get_qdict(qdict, "qemu"); - monitor_printf(mon, "%s%s\n", qdict_get_str(qdict, "qemu"), - qdict_get_str(qdict, "package")); + monitor_printf(mon, "%" PRId64 ".%" PRId64 ".%" PRId64 "%s\n", + qdict_get_int(qemu, "major"), + qdict_get_int(qemu, "minor"), + qdict_get_int(qemu, "micro"), + qdict_get_str(qdict, "package")); } -/** - * do_info_version(): Show QEMU version - * - * Return a QDict with the following information: - * - * - "qemu": QEMU's version - * - "package": package's version - * - * Example: - * - * { "qemu": "0.11.50", "package": "" } - */ static void do_info_version(Monitor *mon, QObject **ret_data) { - *ret_data = qobject_from_jsonf("{ 'qemu': %s, 'package': %s }", - QEMU_VERSION, QEMU_PKGVERSION); + const char *version = QEMU_VERSION; + int major = 0, minor = 0, micro = 0; + char *tmp; + + major = strtol(version, &tmp, 10); + tmp++; + minor = strtol(tmp, &tmp, 10); + tmp++; + micro = strtol(tmp, &tmp, 10); + + *ret_data = qobject_from_jsonf("{ 'qemu': { 'major': %d, 'minor': %d, \ + 'micro': %d }, 'package': %s }", major, minor, micro, QEMU_PKGVERSION); } static void do_info_name_print(Monitor *mon, const QObject *data) @@ -536,17 +768,6 @@ monitor_printf(mon, "%s\n", qdict_get_str(qdict, "name")); } -/** - * do_info_name(): Show VM name - * - * Return a QDict with the following information: - * - * - "name": VM's name (optional) - * - * Example: - * - * { "name": "qemu-name" } - */ static void do_info_name(Monitor *mon, QObject **ret_data) { *ret_data = qemu_name ? qobject_from_jsonf("{'name': %s }", qemu_name) : @@ -568,20 +789,6 @@ return qobject_from_jsonf("{ 'name': %s }", p); } -/** - * do_info_commands(): List QMP available commands - * - * Each command is represented by a QDict, the returned QObject is a QList - * of all commands. - * - * The QDict contains: - * - * - "name": command's name - * - * Example: - * - * { [ { "name": "query-balloon" }, { "name": "system_powerdown" } ] } - */ static void do_info_commands(Monitor *mon, QObject **ret_data) { QList *cmd_list; @@ -589,64 +796,24 @@ cmd_list = qlist_new(); - for (cmd = mon_cmds; cmd->name != NULL; cmd++) { - if (monitor_handler_ported(cmd) && !compare_cmd(cmd->name, "info")) { - qlist_append_obj(cmd_list, get_cmd_dict(cmd->name)); - } + for (cmd = qmp_cmds; cmd->name != NULL; cmd++) { + qlist_append_obj(cmd_list, get_cmd_dict(cmd->name)); } - for (cmd = info_cmds; cmd->name != NULL; cmd++) { - if (monitor_handler_ported(cmd)) { - char buf[128]; - snprintf(buf, sizeof(buf), "query-%s", cmd->name); - qlist_append_obj(cmd_list, get_cmd_dict(buf)); - } + for (cmd = qmp_query_cmds; cmd->name != NULL; cmd++) { + char buf[128]; + snprintf(buf, sizeof(buf), "query-%s", cmd->name); + qlist_append_obj(cmd_list, get_cmd_dict(buf)); } *ret_data = QOBJECT(cmd_list); } -#if defined(TARGET_I386) -static void do_info_hpet_print(Monitor *mon, const QObject *data) -{ - monitor_printf(mon, "HPET is %s by QEMU\n", - qdict_get_bool(qobject_to_qdict(data), "enabled") ? - "enabled" : "disabled"); -} - -/** - * do_info_hpet(): Show HPET state - * - * Return a QDict with the following information: - * - * - "enabled": true if hpet if enabled, false otherwise - * - * Example: - * - * { "enabled": true } - */ -static void do_info_hpet(Monitor *mon, QObject **ret_data) -{ - *ret_data = qobject_from_jsonf("{ 'enabled': %i }", !no_hpet); -} -#endif - static void do_info_uuid_print(Monitor *mon, const QObject *data) { monitor_printf(mon, "%s\n", qdict_get_str(qobject_to_qdict(data), "UUID")); } -/** - * do_info_uuid(): Show VM UUID - * - * Return a QDict with the following information: - * - * - "UUID": Universally Unique Identifier - * - * Example: - * - * { "UUID": "550e8400-e29b-41d4-a716-446655440000" } - */ static void do_info_uuid(Monitor *mon, QObject **ret_data) { char uuid[64]; @@ -686,8 +853,6 @@ { CPUState *env; env = mon_get_cpu(); - if (!env) - return; #ifdef TARGET_I386 cpu_dump_state(env, (FILE *)mon, monitor_fprintf, X86_DUMP_FPU); @@ -747,25 +912,6 @@ qlist_iter(cpu_list, print_cpu_iter, mon); } -/** - * do_info_cpus(): Show CPU information - * - * Return a QList. Each CPU is represented by a QDict, which contains: - * - * - "cpu": CPU index - * - "current": true if this is the current CPU, false otherwise - * - "halted": true if the cpu is halted, false otherwise - * - Current program counter. The key's name depends on the architecture: - * "pc": i386/x86)64 - * "nip": PPC - * "pc" and "npc": sparc - * "PC": mips - * - * Example: - * - * [ { "CPU": 0, "current": true, "halted": false, "pc": 3227107138 }, - * { "CPU": 1, "current": false, "halted": true, "pc": 7108165 } ] - */ static void do_info_cpus(Monitor *mon, QObject **ret_data) { CPUState *env; @@ -785,7 +931,6 @@ obj = qobject_from_jsonf("{ 'CPU': %d, 'current': %i, 'halted': %i }", env->cpu_index, env == mon->mon_cpu, env->halted); - assert(obj != NULL); cpu = qobject_to_qdict(obj); @@ -807,11 +952,15 @@ *ret_data = QOBJECT(cpu_list); } -static void do_cpu_set(Monitor *mon, const QDict *qdict) +static int do_cpu_set(Monitor *mon, const QDict *qdict, QObject **ret_data) { int index = qdict_get_int(qdict, "index"); - if (mon_set_cpu(index) < 0) - monitor_printf(mon, "Invalid CPU index\n"); + if (mon_set_cpu(index) < 0) { + qerror_report(QERR_INVALID_PARAMETER_VALUE, "index", + "a CPU number"); + return -1; + } + return 0; } static void do_cpu_set_nr(Monitor *mon, const QDict *qdict) @@ -868,92 +1017,46 @@ } #endif -/** - * do_quit(): Quit QEMU execution - */ -static void do_quit(Monitor *mon, const QDict *qdict, QObject **ret_data) -{ - exit(0); -} - -static int eject_device(Monitor *mon, BlockDriverState *bs, int force) +#if defined(CONFIG_SIMPLE_TRACE) +static void do_info_trace(Monitor *mon) { - if (bdrv_is_inserted(bs)) { - if (!force) { - if (!bdrv_is_removable(bs)) { - qemu_error_new(QERR_DEVICE_NOT_REMOVABLE, - bdrv_get_device_name(bs)); - return -1; - } - if (bdrv_is_locked(bs)) { - qemu_error_new(QERR_DEVICE_LOCKED, bdrv_get_device_name(bs)); - return -1; - } - } - bdrv_close(bs); - } - return 0; + st_print_trace((FILE *)mon, &monitor_fprintf); } -static void do_eject(Monitor *mon, const QDict *qdict, QObject **ret_data) +static void do_info_trace_events(Monitor *mon) { - BlockDriverState *bs; - int force = qdict_get_int(qdict, "force"); - const char *filename = qdict_get_str(qdict, "device"); - - bs = bdrv_find(filename); - if (!bs) { - qemu_error_new(QERR_DEVICE_NOT_FOUND, filename); - return; - } - eject_device(mon, bs, force); + st_print_trace_events((FILE *)mon, &monitor_fprintf); } +#endif -static void do_block_set_passwd(Monitor *mon, const QDict *qdict, - QObject **ret_data) +/** + * do_quit(): Quit QEMU execution + */ +static int do_quit(Monitor *mon, const QDict *qdict, QObject **ret_data) { - BlockDriverState *bs; - - bs = bdrv_find(qdict_get_str(qdict, "device")); - if (!bs) { - qemu_error_new(QERR_DEVICE_NOT_FOUND, qdict_get_str(qdict, "device")); - return; - } + monitor_suspend(mon); + no_shutdown = 0; + qemu_system_shutdown_request(); - if (bdrv_set_key(bs, qdict_get_str(qdict, "password")) < 0) { - qemu_error_new(QERR_INVALID_PASSWORD); - } + return 0; } -static void do_change_block(Monitor *mon, const char *device, - const char *filename, const char *fmt) +static int change_vnc_password(const char *password) { - BlockDriverState *bs; - BlockDriver *drv = NULL; - - bs = bdrv_find(device); - if (!bs) { - qemu_error_new(QERR_DEVICE_NOT_FOUND, device); - return; - } - if (fmt) { - drv = bdrv_find_whitelisted_format(fmt); - if (!drv) { - qemu_error_new(QERR_INVALID_BLOCK_FORMAT, fmt); - return; + if (!password || !password[0]) { + if (vnc_display_disable_login(NULL)) { + qerror_report(QERR_SET_PASSWD_FAILED); + return -1; } + return 0; } - if (eject_device(mon, bs, 0) < 0) - return; - bdrv_open2(bs, filename, 0, drv); - monitor_read_bdrv_key_start(mon, bs, NULL, NULL); -} -static void change_vnc_password(const char *password) -{ - if (vnc_display_password(NULL, password) < 0) - qemu_error_new(QERR_SET_PASSWD_FAILED); + if (vnc_display_password(NULL, password) < 0) { + qerror_report(QERR_SET_PASSWD_FAILED); + return -1; + } + return 0; } static void change_vnc_password_cb(Monitor *mon, const char *password, @@ -963,7 +1066,7 @@ monitor_read_command(mon, 1); } -static void do_change_vnc(Monitor *mon, const char *target, const char *arg) +static int do_change_vnc(Monitor *mon, const char *target, const char *arg) { if (strcmp(target, "passwd") == 0 || strcmp(target, "password") == 0) { @@ -971,77 +1074,215 @@ char password[9]; strncpy(password, arg, sizeof(password)); password[sizeof(password) - 1] = '\0'; - change_vnc_password(password); + return change_vnc_password(password); } else { - monitor_read_password(mon, change_vnc_password_cb, NULL); + return monitor_read_password(mon, change_vnc_password_cb, NULL); } } else { - if (vnc_display_open(NULL, target) < 0) - qemu_error_new(QERR_VNC_SERVER_FAILED, target); + if (vnc_display_open(NULL, target) < 0) { + qerror_report(QERR_VNC_SERVER_FAILED, target); + return -1; + } } + + return 0; } /** * do_change(): Change a removable medium, or VNC configuration */ -static void do_change(Monitor *mon, const QDict *qdict, QObject **ret_data) +static int do_change(Monitor *mon, const QDict *qdict, QObject **ret_data) { const char *device = qdict_get_str(qdict, "device"); const char *target = qdict_get_str(qdict, "target"); const char *arg = qdict_get_try_str(qdict, "arg"); + int ret; + if (strcmp(device, "vnc") == 0) { - do_change_vnc(mon, target, arg); + ret = do_change_vnc(mon, target, arg); } else { - do_change_block(mon, device, target, arg); + ret = do_change_block(mon, device, target, arg); } -} -static void do_screen_dump(Monitor *mon, const QDict *qdict) -{ - vga_hw_screen_dump(qdict_get_str(qdict, "filename")); + return ret; } -static void do_logfile(Monitor *mon, const QDict *qdict) +static int set_password(Monitor *mon, const QDict *qdict, QObject **ret_data) { - cpu_set_log_filename(qdict_get_str(qdict, "filename")); -} + const char *protocol = qdict_get_str(qdict, "protocol"); + const char *password = qdict_get_str(qdict, "password"); + const char *connected = qdict_get_try_str(qdict, "connected"); + int disconnect_if_connected = 0; + int fail_if_connected = 0; + int rc; -static void do_log(Monitor *mon, const QDict *qdict) -{ - int mask; - const char *items = qdict_get_str(qdict, "items"); + if (connected) { + if (strcmp(connected, "fail") == 0) { + fail_if_connected = 1; + } else if (strcmp(connected, "disconnect") == 0) { + disconnect_if_connected = 1; + } else if (strcmp(connected, "keep") == 0) { + /* nothing */ + } else { + qerror_report(QERR_INVALID_PARAMETER, "connected"); + return -1; + } + } - if (!strcmp(items, "none")) { - mask = 0; - } else { - mask = cpu_str_to_log_mask(items); - if (!mask) { - help_cmd(mon, "log"); - return; + if (strcmp(protocol, "spice") == 0) { + if (!using_spice) { + /* correct one? spice isn't a device ,,, */ + qerror_report(QERR_DEVICE_NOT_ACTIVE, "spice"); + return -1; + } + rc = qemu_spice_set_passwd(password, fail_if_connected, + disconnect_if_connected); + if (rc != 0) { + qerror_report(QERR_SET_PASSWD_FAILED); + return -1; } + return 0; } - cpu_set_log(mask); -} -static void do_singlestep(Monitor *mon, const QDict *qdict) -{ - const char *option = qdict_get_try_str(qdict, "option"); - if (!option || !strcmp(option, "on")) { - singlestep = 1; - } else if (!strcmp(option, "off")) { - singlestep = 0; - } else { - monitor_printf(mon, "unexpected option %s\n", option); + if (strcmp(protocol, "vnc") == 0) { + if (fail_if_connected || disconnect_if_connected) { + /* vnc supports "connected=keep" only */ + qerror_report(QERR_INVALID_PARAMETER, "connected"); + return -1; + } + /* Note that setting an empty password will not disable login through + * this interface. */ + rc = vnc_display_password(NULL, password); + if (rc != 0) { + qerror_report(QERR_SET_PASSWD_FAILED); + return -1; + } + return 0; } + + qerror_report(QERR_INVALID_PARAMETER, "protocol"); + return -1; } -/** - * do_stop(): Stop VM execution - */ -static void do_stop(Monitor *mon, const QDict *qdict, QObject **ret_data) +static int expire_password(Monitor *mon, const QDict *qdict, QObject **ret_data) { - vm_stop(EXCP_INTERRUPT); -} + const char *protocol = qdict_get_str(qdict, "protocol"); + const char *whenstr = qdict_get_str(qdict, "time"); + time_t when; + int rc; + + if (strcmp(whenstr, "now") == 0) { + when = 0; + } else if (strcmp(whenstr, "never") == 0) { + when = TIME_MAX; + } else if (whenstr[0] == '+') { + when = time(NULL) + strtoull(whenstr+1, NULL, 10); + } else { + when = strtoull(whenstr, NULL, 10); + } + + if (strcmp(protocol, "spice") == 0) { + if (!using_spice) { + /* correct one? spice isn't a device ,,, */ + qerror_report(QERR_DEVICE_NOT_ACTIVE, "spice"); + return -1; + } + rc = qemu_spice_set_pw_expire(when); + if (rc != 0) { + qerror_report(QERR_SET_PASSWD_FAILED); + return -1; + } + return 0; + } + + if (strcmp(protocol, "vnc") == 0) { + rc = vnc_display_pw_expire(NULL, when); + if (rc != 0) { + qerror_report(QERR_SET_PASSWD_FAILED); + return -1; + } + return 0; + } + + qerror_report(QERR_INVALID_PARAMETER, "protocol"); + return -1; +} + +static int client_migrate_info(Monitor *mon, const QDict *qdict, QObject **ret_data) +{ + const char *protocol = qdict_get_str(qdict, "protocol"); + const char *hostname = qdict_get_str(qdict, "hostname"); + const char *subject = qdict_get_try_str(qdict, "cert-subject"); + int port = qdict_get_try_int(qdict, "port", -1); + int tls_port = qdict_get_try_int(qdict, "tls-port", -1); + int ret; + + if (strcmp(protocol, "spice") == 0) { + if (!using_spice) { + qerror_report(QERR_DEVICE_NOT_ACTIVE, "spice"); + return -1; + } + + ret = qemu_spice_migrate_info(hostname, port, tls_port, subject); + if (ret != 0) { + qerror_report(QERR_UNDEFINED_ERROR); + return -1; + } + return 0; + } + + qerror_report(QERR_INVALID_PARAMETER, "protocol"); + return -1; +} + +static int do_screen_dump(Monitor *mon, const QDict *qdict, QObject **ret_data) +{ + vga_hw_screen_dump(qdict_get_str(qdict, "filename")); + return 0; +} + +static void do_logfile(Monitor *mon, const QDict *qdict) +{ + cpu_set_log_filename(qdict_get_str(qdict, "filename")); +} + +static void do_log(Monitor *mon, const QDict *qdict) +{ + int mask; + const char *items = qdict_get_str(qdict, "items"); + + if (!strcmp(items, "none")) { + mask = 0; + } else { + mask = cpu_str_to_log_mask(items); + if (!mask) { + help_cmd(mon, "log"); + return; + } + } + cpu_set_log(mask); +} + +static void do_singlestep(Monitor *mon, const QDict *qdict) +{ + const char *option = qdict_get_try_str(qdict, "option"); + if (!option || !strcmp(option, "on")) { + singlestep = 1; + } else if (!strcmp(option, "off")) { + singlestep = 0; + } else { + monitor_printf(mon, "unexpected option %s\n", option); + } +} + +/** + * do_stop(): Stop VM execution + */ +static int do_stop(Monitor *mon, const QDict *qdict, QObject **ret_data) +{ + vm_stop(EXCP_INTERRUPT); + return 0; +} static void encrypted_bdrv_it(void *opaque, BlockDriverState *bs); @@ -1053,14 +1294,22 @@ /** * do_cont(): Resume emulation. */ -static void do_cont(Monitor *mon, const QDict *qdict, QObject **ret_data) +static int do_cont(Monitor *mon, const QDict *qdict, QObject **ret_data) { struct bdrv_iterate_context context = { mon, 0 }; + if (incoming_expected) { + qerror_report(QERR_MIGRATION_EXPECTED); + return -1; + } bdrv_iterate(encrypted_bdrv_it, &context); /* only resume the vm if all keys are set and valid */ - if (!context.err) + if (!context.err) { vm_start(); + return 0; + } else { + return -1; + } } static void bdrv_key_cb(void *opaque, int err) @@ -1138,7 +1387,7 @@ target_phys_addr_t addr, int is_physical) { CPUState *env; - int nb_per_line, l, line_size, i, max_digits, len; + int l, line_size, i, max_digits, len; uint8_t buf[16]; uint64_t v; @@ -1146,8 +1395,6 @@ int flags; flags = 0; env = mon_get_cpu(); - if (!env && !is_physical) - return; #ifdef TARGET_I386 if (wsize == 2) { flags = 1; @@ -1177,7 +1424,6 @@ line_size = 8; else line_size = 16; - nb_per_line = line_size / wsize; max_digits = 0; switch(format) { @@ -1209,8 +1455,6 @@ cpu_physical_memory_rw(addr, buf, l, 0); } else { env = mon_get_cpu(); - if (!env) - break; if (cpu_memory_rw_debug(env, addr, buf, l, 0) < 0) { monitor_printf(mon, " Cannot access memory\n"); break; @@ -1326,7 +1570,7 @@ monitor_printf(mon, "\n"); } -static void do_memory_save(Monitor *mon, const QDict *qdict, QObject **ret_data) +static int do_memory_save(Monitor *mon, const QDict *qdict, QObject **ret_data) { FILE *f; uint32_t size = qdict_get_int(qdict, "size"); @@ -1335,29 +1579,36 @@ uint32_t l; CPUState *env; uint8_t buf[1024]; + int ret = -1; env = mon_get_cpu(); - if (!env) - return; f = fopen(filename, "wb"); if (!f) { - monitor_printf(mon, "could not open '%s'\n", filename); - return; + qerror_report(QERR_OPEN_FILE_FAILED, filename); + return -1; } while (size != 0) { l = sizeof(buf); if (l > size) l = size; cpu_memory_rw_debug(env, addr, buf, l, 0); - fwrite(buf, 1, l, f); + if (fwrite(buf, 1, l, f) != l) { + monitor_printf(mon, "fwrite() error in do_memory_save\n"); + goto exit; + } addr += l; size -= l; } + + ret = 0; + +exit: fclose(f); + return ret; } -static void do_physical_memory_save(Monitor *mon, const QDict *qdict, +static int do_physical_memory_save(Monitor *mon, const QDict *qdict, QObject **ret_data) { FILE *f; @@ -1366,23 +1617,32 @@ uint32_t size = qdict_get_int(qdict, "size"); const char *filename = qdict_get_str(qdict, "filename"); target_phys_addr_t addr = qdict_get_int(qdict, "val"); + int ret = -1; f = fopen(filename, "wb"); if (!f) { - monitor_printf(mon, "could not open '%s'\n", filename); - return; + qerror_report(QERR_OPEN_FILE_FAILED, filename); + return -1; } while (size != 0) { l = sizeof(buf); if (l > size) l = size; cpu_physical_memory_rw(addr, buf, l, 0); - fwrite(buf, 1, l, f); + if (fwrite(buf, 1, l, f) != l) { + monitor_printf(mon, "fwrite() error in do_physical_memory_save\n"); + goto exit; + } fflush(f); addr += l; size -= l; } + + ret = 0; + +exit: fclose(f); + return ret; } static void do_sum(Monitor *mon, const QDict *qdict) @@ -1448,7 +1708,8 @@ { 0x17, "i" }, { 0x18, "o" }, { 0x19, "p" }, - + { 0x1a, "bracket_left" }, + { 0x1b, "bracket_right" }, { 0x1c, "ret" }, { 0x1e, "a" }, @@ -1460,7 +1721,11 @@ { 0x24, "j" }, { 0x25, "k" }, { 0x26, "l" }, + { 0x27, "semicolon" }, + { 0x28, "apostrophe" }, + { 0x29, "grave_accent" }, + { 0x2b, "backslash" }, { 0x2c, "z" }, { 0x2d, "x" }, { 0x2e, "c" }, @@ -1735,27 +2000,38 @@ /** * do_system_reset(): Issue a machine reset */ -static void do_system_reset(Monitor *mon, const QDict *qdict, - QObject **ret_data) +static int do_system_reset(Monitor *mon, const QDict *qdict, + QObject **ret_data) { qemu_system_reset_request(); + return 0; } /** * do_system_powerdown(): Issue a machine powerdown */ -static void do_system_powerdown(Monitor *mon, const QDict *qdict, - QObject **ret_data) +static int do_system_powerdown(Monitor *mon, const QDict *qdict, + QObject **ret_data) { qemu_system_powerdown_request(); + return 0; } #if defined(TARGET_I386) -static void print_pte(Monitor *mon, uint32_t addr, uint32_t pte, uint32_t mask) +static void print_pte(Monitor *mon, target_phys_addr_t addr, + target_phys_addr_t pte, + target_phys_addr_t mask) { - monitor_printf(mon, "%08x: %08x %c%c%c%c%c%c%c%c\n", +#ifdef TARGET_X86_64 + if (addr & (1ULL << 47)) { + addr |= -1LL << 48; + } +#endif + monitor_printf(mon, TARGET_FMT_plx ": " TARGET_FMT_plx + " %c%c%c%c%c%c%c%c%c\n", addr, pte & mask, + pte & PG_NX_MASK ? 'X' : '-', pte & PG_GLOBAL_MASK ? 'G' : '-', pte & PG_PSE_MASK ? 'P' : '-', pte & PG_DIRTY_MASK ? 'D' : '-', @@ -1766,27 +2042,19 @@ pte & PG_RW_MASK ? 'W' : '-'); } -static void tlb_info(Monitor *mon) +static void tlb_info_32(Monitor *mon, CPUState *env) { - CPUState *env; int l1, l2; uint32_t pgd, pde, pte; - env = mon_get_cpu(); - if (!env) - return; - - if (!(env->cr[0] & CR0_PG_MASK)) { - monitor_printf(mon, "PG disabled\n"); - return; - } pgd = env->cr[3] & ~0xfff; for(l1 = 0; l1 < 1024; l1++) { cpu_physical_memory_read(pgd + l1 * 4, (uint8_t *)&pde, 4); pde = le32_to_cpu(pde); if (pde & PG_PRESENT_MASK) { if ((pde & PG_PSE_MASK) && (env->cr[4] & CR4_PSE_MASK)) { - print_pte(mon, (l1 << 22), pde, ~((1 << 20) - 1)); + /* 4M pages */ + print_pte(mon, (l1 << 22), pde, ~((1 << 21) - 1)); } else { for(l2 = 0; l2 < 1024; l2++) { cpu_physical_memory_read((pde & ~0xfff) + l2 * 4, @@ -1803,14 +2071,142 @@ } } -static void mem_print(Monitor *mon, uint32_t *pstart, int *plast_prot, - uint32_t end, int prot) +static void tlb_info_pae32(Monitor *mon, CPUState *env) +{ + int l1, l2, l3; + uint64_t pdpe, pde, pte; + uint64_t pdp_addr, pd_addr, pt_addr; + + pdp_addr = env->cr[3] & ~0x1f; + for (l1 = 0; l1 < 4; l1++) { + cpu_physical_memory_read(pdp_addr + l1 * 8, (uint8_t *)&pdpe, 8); + pdpe = le64_to_cpu(pdpe); + if (pdpe & PG_PRESENT_MASK) { + pd_addr = pdpe & 0x3fffffffff000ULL; + for (l2 = 0; l2 < 512; l2++) { + cpu_physical_memory_read(pd_addr + l2 * 8, + (uint8_t *)&pde, 8); + pde = le64_to_cpu(pde); + if (pde & PG_PRESENT_MASK) { + if (pde & PG_PSE_MASK) { + /* 2M pages with PAE, CR4.PSE is ignored */ + print_pte(mon, (l1 << 30 ) + (l2 << 21), pde, + ~((target_phys_addr_t)(1 << 20) - 1)); + } else { + pt_addr = pde & 0x3fffffffff000ULL; + for (l3 = 0; l3 < 512; l3++) { + cpu_physical_memory_read(pt_addr + l3 * 8, + (uint8_t *)&pte, 8); + pte = le64_to_cpu(pte); + if (pte & PG_PRESENT_MASK) { + print_pte(mon, (l1 << 30 ) + (l2 << 21) + + (l3 << 12), + pte & ~PG_PSE_MASK, + ~(target_phys_addr_t)0xfff); + } + } + } + } + } + } + } +} + +#ifdef TARGET_X86_64 +static void tlb_info_64(Monitor *mon, CPUState *env) +{ + uint64_t l1, l2, l3, l4; + uint64_t pml4e, pdpe, pde, pte; + uint64_t pml4_addr, pdp_addr, pd_addr, pt_addr; + + pml4_addr = env->cr[3] & 0x3fffffffff000ULL; + for (l1 = 0; l1 < 512; l1++) { + cpu_physical_memory_read(pml4_addr + l1 * 8, (uint8_t *)&pml4e, 8); + pml4e = le64_to_cpu(pml4e); + if (pml4e & PG_PRESENT_MASK) { + pdp_addr = pml4e & 0x3fffffffff000ULL; + for (l2 = 0; l2 < 512; l2++) { + cpu_physical_memory_read(pdp_addr + l2 * 8, (uint8_t *)&pdpe, + 8); + pdpe = le64_to_cpu(pdpe); + if (pdpe & PG_PRESENT_MASK) { + if (pdpe & PG_PSE_MASK) { + /* 1G pages, CR4.PSE is ignored */ + print_pte(mon, (l1 << 39) + (l2 << 30), pdpe, + 0x3ffffc0000000ULL); + } else { + pd_addr = pdpe & 0x3fffffffff000ULL; + for (l3 = 0; l3 < 512; l3++) { + cpu_physical_memory_read(pd_addr + l3 * 8, + (uint8_t *)&pde, 8); + pde = le64_to_cpu(pde); + if (pde & PG_PRESENT_MASK) { + if (pde & PG_PSE_MASK) { + /* 2M pages, CR4.PSE is ignored */ + print_pte(mon, (l1 << 39) + (l2 << 30) + + (l3 << 21), pde, + 0x3ffffffe00000ULL); + } else { + pt_addr = pde & 0x3fffffffff000ULL; + for (l4 = 0; l4 < 512; l4++) { + cpu_physical_memory_read(pt_addr + + l4 * 8, + (uint8_t *)&pte, + 8); + pte = le64_to_cpu(pte); + if (pte & PG_PRESENT_MASK) { + print_pte(mon, (l1 << 39) + + (l2 << 30) + + (l3 << 21) + (l4 << 12), + pte & ~PG_PSE_MASK, + 0x3fffffffff000ULL); + } + } + } + } + } + } + } + } + } + } +} +#endif + +static void tlb_info(Monitor *mon) +{ + CPUState *env; + + env = mon_get_cpu(); + + if (!(env->cr[0] & CR0_PG_MASK)) { + monitor_printf(mon, "PG disabled\n"); + return; + } + if (env->cr[4] & CR4_PAE_MASK) { +#ifdef TARGET_X86_64 + if (env->hflags & HF_LMA_MASK) { + tlb_info_64(mon, env); + } else +#endif + { + tlb_info_pae32(mon, env); + } + } else { + tlb_info_32(mon, env); + } +} + +static void mem_print(Monitor *mon, target_phys_addr_t *pstart, + int *plast_prot, + target_phys_addr_t end, int prot) { int prot1; prot1 = *plast_prot; if (prot != prot1) { if (*pstart != -1) { - monitor_printf(mon, "%08x-%08x %08x %c%c%c\n", + monitor_printf(mon, TARGET_FMT_plx "-" TARGET_FMT_plx " " + TARGET_FMT_plx " %c%c%c\n", *pstart, end, end - *pstart, prot1 & PG_USER_MASK ? 'u' : '-', 'r', @@ -1824,20 +2220,12 @@ } } -static void mem_info(Monitor *mon) +static void mem_info_32(Monitor *mon, CPUState *env) { - CPUState *env; int l1, l2, prot, last_prot; - uint32_t pgd, pde, pte, start, end; - - env = mon_get_cpu(); - if (!env) - return; + uint32_t pgd, pde, pte; + target_phys_addr_t start, end; - if (!(env->cr[0] & CR0_PG_MASK)) { - monitor_printf(mon, "PG disabled\n"); - return; - } pgd = env->cr[3] & ~0xfff; last_prot = 0; start = -1; @@ -1869,6 +2257,162 @@ } } } + +static void mem_info_pae32(Monitor *mon, CPUState *env) +{ + int l1, l2, l3, prot, last_prot; + uint64_t pdpe, pde, pte; + uint64_t pdp_addr, pd_addr, pt_addr; + target_phys_addr_t start, end; + + pdp_addr = env->cr[3] & ~0x1f; + last_prot = 0; + start = -1; + for (l1 = 0; l1 < 4; l1++) { + cpu_physical_memory_read(pdp_addr + l1 * 8, (uint8_t *)&pdpe, 8); + pdpe = le64_to_cpu(pdpe); + end = l1 << 30; + if (pdpe & PG_PRESENT_MASK) { + pd_addr = pdpe & 0x3fffffffff000ULL; + for (l2 = 0; l2 < 512; l2++) { + cpu_physical_memory_read(pd_addr + l2 * 8, + (uint8_t *)&pde, 8); + pde = le64_to_cpu(pde); + end = (l1 << 30) + (l2 << 21); + if (pde & PG_PRESENT_MASK) { + if (pde & PG_PSE_MASK) { + prot = pde & (PG_USER_MASK | PG_RW_MASK | + PG_PRESENT_MASK); + mem_print(mon, &start, &last_prot, end, prot); + } else { + pt_addr = pde & 0x3fffffffff000ULL; + for (l3 = 0; l3 < 512; l3++) { + cpu_physical_memory_read(pt_addr + l3 * 8, + (uint8_t *)&pte, 8); + pte = le64_to_cpu(pte); + end = (l1 << 30) + (l2 << 21) + (l3 << 12); + if (pte & PG_PRESENT_MASK) { + prot = pte & (PG_USER_MASK | PG_RW_MASK | + PG_PRESENT_MASK); + } else { + prot = 0; + } + mem_print(mon, &start, &last_prot, end, prot); + } + } + } else { + prot = 0; + mem_print(mon, &start, &last_prot, end, prot); + } + } + } else { + prot = 0; + mem_print(mon, &start, &last_prot, end, prot); + } + } +} + + +#ifdef TARGET_X86_64 +static void mem_info_64(Monitor *mon, CPUState *env) +{ + int prot, last_prot; + uint64_t l1, l2, l3, l4; + uint64_t pml4e, pdpe, pde, pte; + uint64_t pml4_addr, pdp_addr, pd_addr, pt_addr, start, end; + + pml4_addr = env->cr[3] & 0x3fffffffff000ULL; + last_prot = 0; + start = -1; + for (l1 = 0; l1 < 512; l1++) { + cpu_physical_memory_read(pml4_addr + l1 * 8, (uint8_t *)&pml4e, 8); + pml4e = le64_to_cpu(pml4e); + end = l1 << 39; + if (pml4e & PG_PRESENT_MASK) { + pdp_addr = pml4e & 0x3fffffffff000ULL; + for (l2 = 0; l2 < 512; l2++) { + cpu_physical_memory_read(pdp_addr + l2 * 8, (uint8_t *)&pdpe, + 8); + pdpe = le64_to_cpu(pdpe); + end = (l1 << 39) + (l2 << 30); + if (pdpe & PG_PRESENT_MASK) { + if (pdpe & PG_PSE_MASK) { + prot = pdpe & (PG_USER_MASK | PG_RW_MASK | + PG_PRESENT_MASK); + mem_print(mon, &start, &last_prot, end, prot); + } else { + pd_addr = pdpe & 0x3fffffffff000ULL; + for (l3 = 0; l3 < 512; l3++) { + cpu_physical_memory_read(pd_addr + l3 * 8, + (uint8_t *)&pde, 8); + pde = le64_to_cpu(pde); + end = (l1 << 39) + (l2 << 30) + (l3 << 21); + if (pde & PG_PRESENT_MASK) { + if (pde & PG_PSE_MASK) { + prot = pde & (PG_USER_MASK | PG_RW_MASK | + PG_PRESENT_MASK); + mem_print(mon, &start, &last_prot, end, prot); + } else { + pt_addr = pde & 0x3fffffffff000ULL; + for (l4 = 0; l4 < 512; l4++) { + cpu_physical_memory_read(pt_addr + + l4 * 8, + (uint8_t *)&pte, + 8); + pte = le64_to_cpu(pte); + end = (l1 << 39) + (l2 << 30) + + (l3 << 21) + (l4 << 12); + if (pte & PG_PRESENT_MASK) { + prot = pte & (PG_USER_MASK | PG_RW_MASK | + PG_PRESENT_MASK); + } else { + prot = 0; + } + mem_print(mon, &start, &last_prot, end, prot); + } + } + } else { + prot = 0; + mem_print(mon, &start, &last_prot, end, prot); + } + } + } + } else { + prot = 0; + mem_print(mon, &start, &last_prot, end, prot); + } + } + } else { + prot = 0; + mem_print(mon, &start, &last_prot, end, prot); + } + } +} +#endif + +static void mem_info(Monitor *mon) +{ + CPUState *env; + + env = mon_get_cpu(); + + if (!(env->cr[0] & CR0_PG_MASK)) { + monitor_printf(mon, "PG disabled\n"); + return; + } + if (env->cr[4] & CR4_PAE_MASK) { +#ifdef TARGET_X86_64 + if (env->hflags & HF_LMA_MASK) { + mem_info_64(mon, env); + } else +#endif + { + mem_info_pae32(mon, env); + } + } else { + mem_info_32(mon, env); + } +} #endif #if defined(TARGET_SH4) @@ -1900,6 +2444,15 @@ #endif +#if defined(TARGET_SPARC) +static void tlb_info(Monitor *mon) +{ + CPUState *env1 = mon_get_cpu(); + + dump_mmu((FILE*)mon, (fprintf_function)monitor_printf, env1); +} +#endif + static void do_info_kvm_print(Monitor *mon, const QObject *data) { QDict *qdict; @@ -1915,18 +2468,6 @@ } } -/** - * do_info_kvm(): Show KVM information - * - * Return a QDict with the following information: - * - * - "enabled": true if KVM support is enabled, false otherwise - * - "present": true if QEMU has KVM support, false otherwise - * - * Example: - * - * { "enabled": true, "present": true } - */ static void do_info_kvm(Monitor *mon, QObject **ret_data) { #ifdef CONFIG_KVM @@ -2030,8 +2571,9 @@ nchannels = has_channels ? nchannels : 2; if (wav_start_capture (s, path, freq, bits, nchannels)) { - monitor_printf(mon, "Faied to add wave capture\n"); + monitor_printf(mon, "Failed to add wave capture\n"); qemu_free (s); + return; } QLIST_INSERT_HEAD (&capture_head, s, entries); } @@ -2073,101 +2615,28 @@ monitor_printf(mon, "\n"); } -/** - * do_info_status(): VM status - * - * Return a QDict with the following information: - * - * - "running": true if the VM is running, or false if it is paused - * - "singlestep": true if the VM is in single step mode, false otherwise - * - * Example: - * - * { "running": true, "singlestep": false } - */ static void do_info_status(Monitor *mon, QObject **ret_data) { *ret_data = qobject_from_jsonf("{ 'running': %i, 'singlestep': %i }", vm_running, singlestep); } -static ram_addr_t balloon_get_value(void) +static qemu_acl *find_acl(Monitor *mon, const char *name) { - ram_addr_t actual; - - if (kvm_enabled() && !kvm_has_sync_mmu()) { - qemu_error_new(QERR_KVM_MISSING_CAP, "synchronous MMU", "balloon"); - return 0; - } - - actual = qemu_balloon_status(); - if (actual == 0) { - qemu_error_new(QERR_DEVICE_NOT_ACTIVE, "balloon"); - return 0; - } - - return actual; -} + qemu_acl *acl = qemu_acl_find(name); -/** - * do_balloon(): Request VM to change its memory allocation - */ -static void do_balloon(Monitor *mon, const QDict *qdict, QObject **ret_data) -{ - if (balloon_get_value()) { - /* ballooning is active */ - qemu_balloon(qdict_get_int(qdict, "value")); + if (!acl) { + monitor_printf(mon, "acl: unknown list '%s'\n", name); } + return acl; } -static void monitor_print_balloon(Monitor *mon, const QObject *data) +static void do_acl_show(Monitor *mon, const QDict *qdict) { - QDict *qdict; - - qdict = qobject_to_qdict(data); - - monitor_printf(mon, "balloon: actual=%" PRId64 "\n", - qdict_get_int(qdict, "balloon") >> 20); -} - -/** - * do_info_balloon(): Balloon information - * - * Return a QDict with the following information: - * - * - "balloon": current balloon value in bytes - * - * Example: - * - * { "balloon": 1073741824 } - */ -static void do_info_balloon(Monitor *mon, QObject **ret_data) -{ - ram_addr_t actual; - - actual = balloon_get_value(); - if (actual != 0) { - *ret_data = qobject_from_jsonf("{ 'balloon': %" PRId64 "}", - (int64_t) actual); - } -} - -static qemu_acl *find_acl(Monitor *mon, const char *name) -{ - qemu_acl *acl = qemu_acl_find(name); - - if (!acl) { - monitor_printf(mon, "acl: unknown list '%s'\n", name); - } - return acl; -} - -static void do_acl_show(Monitor *mon, const QDict *qdict) -{ - const char *aclname = qdict_get_str(qdict, "aclname"); - qemu_acl *acl = find_acl(mon, aclname); - qemu_acl_entry *entry; - int i = 0; + const char *aclname = qdict_get_str(qdict, "aclname"); + qemu_acl *acl = find_acl(mon, aclname); + qemu_acl_entry *entry; + int i = 0; if (acl) { monitor_printf(mon, "policy: %s\n", @@ -2268,16 +2737,19 @@ uint64_t mcg_status = qdict_get_int(qdict, "mcg_status"); uint64_t addr = qdict_get_int(qdict, "addr"); uint64_t misc = qdict_get_int(qdict, "misc"); + int broadcast = qdict_get_try_bool(qdict, "broadcast", 0); - for (cenv = first_cpu; cenv != NULL; cenv = cenv->next_cpu) + for (cenv = first_cpu; cenv != NULL; cenv = cenv->next_cpu) { if (cenv->cpu_index == cpu_index && cenv->mcg_cap) { - cpu_inject_x86_mce(cenv, bank, status, mcg_status, addr, misc); + cpu_inject_x86_mce(cenv, bank, status, mcg_status, addr, misc, + broadcast); break; } + } } #endif -static void do_getfd(Monitor *mon, const QDict *qdict, QObject **ret_data) +static int do_getfd(Monitor *mon, const QDict *qdict, QObject **ret_data) { const char *fdname = qdict_get_str(qdict, "fdname"); mon_fd_t *monfd; @@ -2285,22 +2757,14 @@ fd = qemu_chr_get_msgfd(mon->chr); if (fd == -1) { - qemu_error_new(QERR_FD_NOT_SUPPLIED); - return; + qerror_report(QERR_FD_NOT_SUPPLIED); + return -1; } if (qemu_isdigit(fdname[0])) { - qemu_error_new(QERR_INVALID_PARAMETER, "fdname"); - return; - } - - fd = dup(fd); - if (fd == -1) { - if (errno == EMFILE) - qemu_error_new(QERR_TOO_MANY_FILES); - else - qemu_error_new(QERR_UNDEFINED_ERROR); - return; + qerror_report(QERR_INVALID_PARAMETER_VALUE, "fdname", + "a name not starting with a digit"); + return -1; } QLIST_FOREACH(monfd, &mon->fds, next) { @@ -2310,7 +2774,7 @@ close(monfd->fd); monfd->fd = fd; - return; + return 0; } monfd = qemu_mallocz(sizeof(mon_fd_t)); @@ -2318,9 +2782,10 @@ monfd->fd = fd; QLIST_INSERT_HEAD(&mon->fds, monfd, next); + return 0; } -static void do_closefd(Monitor *mon, const QDict *qdict, QObject **ret_data) +static int do_closefd(Monitor *mon, const QDict *qdict, QObject **ret_data) { const char *fdname = qdict_get_str(qdict, "fdname"); mon_fd_t *monfd; @@ -2334,10 +2799,11 @@ close(monfd->fd); qemu_free(monfd->name); qemu_free(monfd); - return; + return 0; } - qemu_error_new(QERR_FD_NOT_FOUND, fdname); + qerror_report(QERR_FD_NOT_FOUND, fdname); + return -1; } static void do_loadvm(Monitor *mon, const QDict *qdict) @@ -2347,8 +2813,9 @@ vm_stop(0); - if (load_vmstate(mon, name) >= 0 && saved_vm_running) + if (load_vmstate(name) == 0 && saved_vm_running) { vm_start(); + } } int monitor_get_fd(Monitor *mon, const char *fdname) @@ -2376,11 +2843,11 @@ } static const mon_cmd_t mon_cmds[] = { -#include "qemu-monitor.h" +#include "hmp-commands.h" { NULL, NULL, }, }; -/* Please update qemu-monitor.hx when adding or changing commands */ +/* Please update hmp-commands.hx when adding or changing commands */ static const mon_cmd_t info_cmds[] = { { .name = "version", @@ -2391,14 +2858,6 @@ .mhandler.info_new = do_info_version, }, { - .name = "commands", - .args_type = "", - .params = "", - .help = "list QMP available commands", - .user_print = monitor_user_noop, - .mhandler.info_new = do_info_commands, - }, - { .name = "network", .args_type = "", .params = "", @@ -2470,9 +2929,10 @@ .args_type = "", .params = "", .help = "show PCI info", - .mhandler.info = pci_info, + .user_print = do_pci_info_print, + .mhandler.info_new = do_pci_info, }, -#if defined(TARGET_I386) || defined(TARGET_SH4) +#if defined(TARGET_I386) || defined(TARGET_SH4) || defined(TARGET_SPARC) { .name = "tlb", .args_type = "", @@ -2489,14 +2949,6 @@ .help = "show the active virtual memory mappings", .mhandler.info = mem_info, }, - { - .name = "hpet", - .args_type = "", - .params = "", - .help = "show state of HPET", - .user_print = do_info_hpet_print, - .mhandler.info_new = do_info_hpet, - }, #endif { .name = "jit", @@ -2586,6 +3038,16 @@ .user_print = do_info_vnc_print, .mhandler.info_new = do_info_vnc, }, +#if defined(CONFIG_SPICE) + { + .name = "spice", + .args_type = "", + .params = "", + .help = "show the spice server status", + .user_print = do_info_spice_print, + .mhandler.info_new = do_info_spice, + }, +#endif { .name = "name", .args_type = "", @@ -2634,7 +3096,8 @@ .params = "", .help = "show balloon information", .user_print = monitor_print_balloon, - .mhandler.info_new = do_info_balloon, + .mhandler.info_async = do_info_balloon, + .flags = MONITOR_CMD_ASYNC, }, { .name = "qtree", @@ -2657,11 +3120,167 @@ .help = "show roms", .mhandler.info = do_info_roms, }, +#if defined(CONFIG_SIMPLE_TRACE) + { + .name = "trace", + .args_type = "", + .params = "", + .help = "show current contents of trace buffer", + .mhandler.info = do_info_trace, + }, + { + .name = "trace-events", + .args_type = "", + .params = "", + .help = "show available trace-events & their state", + .mhandler.info = do_info_trace_events, + }, +#endif { .name = NULL, }, }; +static const mon_cmd_t qmp_cmds[] = { +#include "qmp-commands.h" + { /* NULL */ }, +}; + +static const mon_cmd_t qmp_query_cmds[] = { + { + .name = "version", + .args_type = "", + .params = "", + .help = "show the version of QEMU", + .user_print = do_info_version_print, + .mhandler.info_new = do_info_version, + }, + { + .name = "commands", + .args_type = "", + .params = "", + .help = "list QMP available commands", + .user_print = monitor_user_noop, + .mhandler.info_new = do_info_commands, + }, + { + .name = "chardev", + .args_type = "", + .params = "", + .help = "show the character devices", + .user_print = qemu_chr_info_print, + .mhandler.info_new = qemu_chr_info, + }, + { + .name = "block", + .args_type = "", + .params = "", + .help = "show the block devices", + .user_print = bdrv_info_print, + .mhandler.info_new = bdrv_info, + }, + { + .name = "blockstats", + .args_type = "", + .params = "", + .help = "show block device statistics", + .user_print = bdrv_stats_print, + .mhandler.info_new = bdrv_info_stats, + }, + { + .name = "cpus", + .args_type = "", + .params = "", + .help = "show infos for each CPU", + .user_print = monitor_print_cpus, + .mhandler.info_new = do_info_cpus, + }, + { + .name = "pci", + .args_type = "", + .params = "", + .help = "show PCI info", + .user_print = do_pci_info_print, + .mhandler.info_new = do_pci_info, + }, + { + .name = "kvm", + .args_type = "", + .params = "", + .help = "show KVM information", + .user_print = do_info_kvm_print, + .mhandler.info_new = do_info_kvm, + }, + { + .name = "status", + .args_type = "", + .params = "", + .help = "show the current VM status (running|paused)", + .user_print = do_info_status_print, + .mhandler.info_new = do_info_status, + }, + { + .name = "mice", + .args_type = "", + .params = "", + .help = "show which guest mouse is receiving events", + .user_print = do_info_mice_print, + .mhandler.info_new = do_info_mice, + }, + { + .name = "vnc", + .args_type = "", + .params = "", + .help = "show the vnc server status", + .user_print = do_info_vnc_print, + .mhandler.info_new = do_info_vnc, + }, +#if defined(CONFIG_SPICE) + { + .name = "spice", + .args_type = "", + .params = "", + .help = "show the spice server status", + .user_print = do_info_spice_print, + .mhandler.info_new = do_info_spice, + }, +#endif + { + .name = "name", + .args_type = "", + .params = "", + .help = "show the current VM name", + .user_print = do_info_name_print, + .mhandler.info_new = do_info_name, + }, + { + .name = "uuid", + .args_type = "", + .params = "", + .help = "show the current VM UUID", + .user_print = do_info_uuid_print, + .mhandler.info_new = do_info_uuid, + }, + { + .name = "migrate", + .args_type = "", + .params = "", + .help = "show migration status", + .user_print = do_info_migrate_print, + .mhandler.info_new = do_info_migrate, + }, + { + .name = "balloon", + .args_type = "", + .params = "", + .help = "show balloon information", + .user_print = monitor_print_balloon, + .mhandler.info_async = do_info_balloon, + .flags = MONITOR_CMD_ASYNC, + }, + { /* NULL */ }, +}; + /*******************************************************************/ static const char *pch; @@ -2681,8 +3300,6 @@ static target_long monitor_get_pc (const struct MonitorDef *md, int val) { CPUState *env = mon_get_cpu(); - if (!env) - return 0; return env->eip + env->segs[R_CS].base; } #endif @@ -2694,9 +3311,6 @@ unsigned int u; int i; - if (!env) - return 0; - u = 0; for (i = 0; i < 8; i++) u |= env->crf[i] << (32 - (4 * i)); @@ -2707,40 +3321,30 @@ static target_long monitor_get_msr (const struct MonitorDef *md, int val) { CPUState *env = mon_get_cpu(); - if (!env) - return 0; return env->msr; } static target_long monitor_get_xer (const struct MonitorDef *md, int val) { CPUState *env = mon_get_cpu(); - if (!env) - return 0; return env->xer; } static target_long monitor_get_decr (const struct MonitorDef *md, int val) { CPUState *env = mon_get_cpu(); - if (!env) - return 0; return cpu_ppc_load_decr(env); } static target_long monitor_get_tbu (const struct MonitorDef *md, int val) { CPUState *env = mon_get_cpu(); - if (!env) - return 0; return cpu_ppc_load_tbu(env); } static target_long monitor_get_tbl (const struct MonitorDef *md, int val) { CPUState *env = mon_get_cpu(); - if (!env) - return 0; return cpu_ppc_load_tbl(env); } #endif @@ -2750,17 +3354,14 @@ static target_long monitor_get_psr (const struct MonitorDef *md, int val) { CPUState *env = mon_get_cpu(); - if (!env) - return 0; - return GET_PSR(env); + + return cpu_get_psr(env); } #endif static target_long monitor_get_reg(const struct MonitorDef *md, int val) { CPUState *env = mon_get_cpu(); - if (!env) - return 0; return env->regwptr[val]; } #endif @@ -3012,7 +3613,7 @@ longjmp(expr_env, 1); } -/* return 0 if OK, -1 if not found, -2 if no CPU defined */ +/* return 0 if OK, -1 if not found */ static int get_monitor_def(target_long *pval, const char *name) { const MonitorDef *md; @@ -3024,8 +3625,6 @@ *pval = md->get_value(md, md->offset); } else { CPUState *env = mon_get_cpu(); - if (!env) - return -2; ptr = (uint8_t *)env + md->offset; switch(md->type) { case MD_I32: @@ -3112,10 +3711,8 @@ pch++; *q = 0; ret = get_monitor_def(®, buf); - if (ret == -1) + if (ret < 0) expr_error(mon, "unknown register"); - else if (ret == -2) - expr_error(mon, "no cpu defined"); n = reg; } break; @@ -3234,6 +3831,27 @@ return 0; } +static int get_double(Monitor *mon, double *pval, const char **pp) +{ + const char *p = *pp; + char *tailp; + double d; + + d = strtod(p, &tailp); + if (tailp == p) { + monitor_printf(mon, "Number expected\n"); + return -1; + } + if (d != d || d - d != 0) { + /* NaN or infinity */ + monitor_printf(mon, "Bad number\n"); + return -1; + } + *pval = d; + *pp = tailp; + return 0; +} + static int get_str(char *buf, int buf_size, const char **pp) { const char *p; @@ -3369,11 +3987,12 @@ return (typestr != NULL); } -static const mon_cmd_t *monitor_find_command(const char *cmdname) +static const mon_cmd_t *search_dispatch_table(const mon_cmd_t *disp_table, + const char *cmdname) { const mon_cmd_t *cmd; - for (cmd = mon_cmds; cmd->name != NULL; cmd++) { + for (cmd = disp_table; cmd->name != NULL; cmd++) { if (compare_cmd(cmdname, cmd->name)) { return cmd; } @@ -3382,6 +4001,21 @@ return NULL; } +static const mon_cmd_t *monitor_find_command(const char *cmdname) +{ + return search_dispatch_table(mon_cmds, cmdname); +} + +static const mon_cmd_t *qmp_find_query_cmd(const char *info_item) +{ + return search_dispatch_table(qmp_query_cmds, info_item); +} + +static const mon_cmd_t *qmp_find_cmd(const char *cmdname) +{ + return search_dispatch_table(qmp_cmds, cmdname); +} + static const mon_cmd_t *monitor_parse_command(Monitor *mon, const char *cmdline, QDict *qdict) @@ -3452,6 +4086,31 @@ qdict_put(qdict, key, qstring_from_str(buf)); } break; + case 'O': + { + QemuOptsList *opts_list; + QemuOpts *opts; + + opts_list = qemu_find_opts(key); + if (!opts_list || opts_list->desc->name) { + goto bad_type; + } + while (qemu_isspace(*p)) { + p++; + } + if (!*p) + break; + if (get_str(buf, sizeof(buf), &p) < 0) { + goto fail; + } + opts = qemu_opts_parse(opts_list, buf, 1); + if (!opts) { + goto fail; + } + qemu_opts_to_qdict(opts, qdict); + qemu_opts_del(opts); + } + break; case '/': { int count, format, size; @@ -3570,10 +4229,88 @@ qdict_put(qdict, key, qint_from_int(val)); } break; + case 'o': + { + int64_t val; + char *end; + + while (qemu_isspace(*p)) { + p++; + } + if (*typestr == '?') { + typestr++; + if (*p == '\0') { + break; + } + } + val = strtosz(p, &end); + if (val < 0) { + monitor_printf(mon, "invalid size\n"); + goto fail; + } + qdict_put(qdict, key, qint_from_int(val)); + p = end; + } + break; + case 'T': + { + double val; + + while (qemu_isspace(*p)) + p++; + if (*typestr == '?') { + typestr++; + if (*p == '\0') { + break; + } + } + if (get_double(mon, &val, &p) < 0) { + goto fail; + } + if (p[0] && p[1] == 's') { + switch (*p) { + case 'm': + val /= 1e3; p += 2; break; + case 'u': + val /= 1e6; p += 2; break; + case 'n': + val /= 1e9; p += 2; break; + } + } + if (*p && !qemu_isspace(*p)) { + monitor_printf(mon, "Unknown unit suffix\n"); + goto fail; + } + qdict_put(qdict, key, qfloat_from_double(val)); + } + break; + case 'b': + { + const char *beg; + int val; + + while (qemu_isspace(*p)) { + p++; + } + beg = p; + while (qemu_isgraph(*p)) { + p++; + } + if (p - beg == 2 && !memcmp(beg, "on", p - beg)) { + val = 1; + } else if (p - beg == 3 && !memcmp(beg, "off", p - beg)) { + val = 0; + } else { + monitor_printf(mon, "Expected 'on' or 'off'\n"); + goto fail; + } + qdict_put(qdict, key, qbool_from_int(val)); + } + break; case '-': { const char *tmp = p; - int has_option, skip_key = 0; + int skip_key = 0; /* option */ c = *typestr++; @@ -3581,7 +4318,6 @@ goto bad_type; while (qemu_isspace(*p)) p++; - has_option = 0; if (*p == '-') { p++; if(c != *p) { @@ -3597,11 +4333,11 @@ if(skip_key) { p = tmp; } else { + /* has option */ p++; - has_option = 1; + qdict_put(qdict, key, qbool_from_int(1)); } } - qdict_put(qdict, key, qint_from_int(has_option)); } break; default: @@ -3628,30 +4364,57 @@ return NULL; } -static void monitor_print_error(Monitor *mon) +void monitor_set_error(Monitor *mon, QError *qerror) { - qerror_print(mon->error); - QDECREF(mon->error); - mon->error = NULL; + /* report only the first error */ + if (!mon->error) { + mon->error = qerror; + } else { + MON_DEBUG("Additional error report at %s:%d\n", + qerror->file, qerror->linenr); + QDECREF(qerror); + } } -static void monitor_call_handler(Monitor *mon, const mon_cmd_t *cmd, - const QDict *params) +static void handler_audit(Monitor *mon, const mon_cmd_t *cmd, int ret) { - QObject *data = NULL; - - cmd->mhandler.cmd_new(mon, params, &data); - - if (monitor_ctrl_mode(mon)) { - /* Monitor Protocol */ - monitor_protocol_emitter(mon, data); - } else { - /* User Protocol */ - if (data) - cmd->user_print(mon, data); + if (ret && !monitor_has_error(mon)) { + /* + * If it returns failure, it must have passed on error. + * + * Action: Report an internal error to the client if in QMP. + */ + qerror_report(QERR_UNDEFINED_ERROR); + MON_DEBUG("command '%s' returned failure but did not pass an error\n", + cmd->name); + } + +#ifdef CONFIG_DEBUG_MONITOR + if (!ret && monitor_has_error(mon)) { + /* + * If it returns success, it must not have passed an error. + * + * Action: Report the passed error to the client. + */ + MON_DEBUG("command '%s' returned success but passed an error\n", + cmd->name); + } + + if (mon_print_count_get(mon) > 0 && strcmp(cmd->name, "info") != 0) { + /* + * Handlers should not call Monitor print functions. + * + * Action: Ignore them in QMP. + * + * (XXX: we don't check any 'info' or 'query' command here + * because the user print function _is_ called by do_info(), hence + * we will trigger this check. This problem will go away when we + * make 'query' commands real and kill do_info()) + */ + MON_DEBUG("command '%s' called print functions %d time(s)\n", + cmd->name, mon_print_count_get(mon)); } - - qobject_decref(data); +#endif } static void handle_user_command(Monitor *mon, const char *cmdline) @@ -3665,19 +4428,22 @@ if (!cmd) goto out; - qemu_errors_to_mon(mon); - - if (monitor_handler_ported(cmd)) { - monitor_call_handler(mon, cmd, qdict); + if (handler_is_async(cmd)) { + user_async_cmd_handler(mon, cmd, qdict); + } else if (handler_is_qobject(cmd)) { + QObject *data = NULL; + + /* XXX: ignores the error code */ + cmd->mhandler.cmd_new(mon, qdict, &data); + assert(!monitor_has_error(mon)); + if (data) { + cmd->user_print(mon, data); + qobject_decref(data); + } } else { cmd->mhandler.cmd(mon, qdict); } - if (monitor_has_error(mon)) - monitor_print_error(mon); - - qemu_errors_to_previous(); - out: QDECREF(qdict); } @@ -3742,6 +4508,11 @@ d = readdir(ffs); if (!d) break; + + if (strcmp(d->d_name, ".") == 0 || strcmp(d->d_name, "..") == 0) { + continue; + } + if (strstart(d->d_name, file_prefix, NULL)) { memcpy(file, input, input_path_len); if (input_path_len < sizeof(file)) @@ -3822,8 +4593,9 @@ next arg */ len = strlen(cmdline); if (len > 0 && qemu_isspace(cmdline[len - 1])) { - if (nb_args >= MAX_ARGS) - return; + if (nb_args >= MAX_ARGS) { + goto cleanup; + } args[nb_args++] = qemu_strdup(""); } if (nb_args <= 1) { @@ -3838,12 +4610,15 @@ } } else { /* find the command */ - for(cmd = mon_cmds; cmd->name != NULL; cmd++) { - if (compare_cmd(args[0], cmd->name)) - goto found; + for (cmd = mon_cmds; cmd->name != NULL; cmd++) { + if (compare_cmd(args[0], cmd->name)) { + break; + } } - return; - found: + if (!cmd->name) { + goto cleanup; + } + ptype = next_arg_type(cmd->args_type); for(i = 0; i < nb_args - 2; i++) { if (*ptype != '\0') { @@ -3854,7 +4629,7 @@ } str = args[nb_args - 1]; if (*ptype == '-' && ptype[1] != '\0') { - ptype += 2; + ptype = next_arg_type(ptype); } switch(*ptype) { case 'F': @@ -3893,8 +4668,11 @@ break; } } - for(i = 0; i < nb_args; i++) + +cleanup: + for (i = 0; i < nb_args; i++) { qemu_free(args[i]); + } } static int monitor_can_read(void *opaque) @@ -3904,156 +4682,286 @@ return (mon->suspend_cnt == 0) ? 1 : 0; } -typedef struct CmdArgs { - QString *name; - int type; - int flag; - int optional; -} CmdArgs; - -static int check_opt(const CmdArgs *cmd_args, const char *name, QDict *args) +static int invalid_qmp_mode(const Monitor *mon, const char *cmd_name) { - if (!cmd_args->optional) { - qemu_error_new(QERR_MISSING_PARAMETER, name); - return -1; - } - - if (cmd_args->type == '-') { - /* handlers expect a value, they need to be changed */ - qdict_put(args, name, qint_from_int(0)); - } - - return 0; + int is_cap = compare_cmd(cmd_name, "qmp_capabilities"); + return (qmp_cmd_mode(mon) ? is_cap : !is_cap); } -static int check_arg(const CmdArgs *cmd_args, QDict *args) +/* + * Argument validation rules: + * + * 1. The argument must exist in cmd_args qdict + * 2. The argument type must be the expected one + * + * Special case: If the argument doesn't exist in cmd_args and + * the QMP_ACCEPT_UNKNOWNS flag is set, then the + * checking is skipped for it. + */ +static int check_client_args_type(const QDict *client_args, + const QDict *cmd_args, int flags) { - QObject *value; - const char *name; + const QDictEntry *ent; - name = qstring_get_str(cmd_args->name); - - if (!args) { - return check_opt(cmd_args, name, args); - } + for (ent = qdict_first(client_args); ent;ent = qdict_next(client_args,ent)){ + QObject *obj; + QString *arg_type; + const QObject *client_arg = qdict_entry_value(ent); + const char *client_arg_name = qdict_entry_key(ent); + + obj = qdict_get(cmd_args, client_arg_name); + if (!obj) { + if (flags & QMP_ACCEPT_UNKNOWNS) { + /* handler accepts unknowns */ + continue; + } + /* client arg doesn't exist */ + qerror_report(QERR_INVALID_PARAMETER, client_arg_name); + return -1; + } - value = qdict_get(args, name); - if (!value) { - return check_opt(cmd_args, name, args); - } + arg_type = qobject_to_qstring(obj); + assert(arg_type != NULL); - switch (cmd_args->type) { + /* check if argument's type is correct */ + switch (qstring_get_str(arg_type)[0]) { case 'F': case 'B': case 's': - if (qobject_type(value) != QTYPE_QSTRING) { - qemu_error_new(QERR_INVALID_PARAMETER_TYPE, name, "string"); + if (qobject_type(client_arg) != QTYPE_QSTRING) { + qerror_report(QERR_INVALID_PARAMETER_TYPE, client_arg_name, + "string"); return -1; } - break; - case '/': { - int i; - const char *keys[] = { "count", "format", "size", NULL }; - - for (i = 0; keys[i]; i++) { - QObject *obj = qdict_get(args, keys[i]); - if (!obj) { - qemu_error_new(QERR_MISSING_PARAMETER, name); - return -1; - } - if (qobject_type(obj) != QTYPE_QINT) { - qemu_error_new(QERR_INVALID_PARAMETER_TYPE, name, "int"); - return -1; - } - } - break; - } + break; case 'i': case 'l': case 'M': - if (qobject_type(value) != QTYPE_QINT) { - qemu_error_new(QERR_INVALID_PARAMETER_TYPE, name, "int"); - return -1; + case 'o': + if (qobject_type(client_arg) != QTYPE_QINT) { + qerror_report(QERR_INVALID_PARAMETER_TYPE, client_arg_name, + "int"); + return -1; } break; - case '-': - if (qobject_type(value) != QTYPE_QINT && - qobject_type(value) != QTYPE_QBOOL) { - qemu_error_new(QERR_INVALID_PARAMETER_TYPE, name, "bool"); - return -1; + case 'T': + if (qobject_type(client_arg) != QTYPE_QINT && + qobject_type(client_arg) != QTYPE_QFLOAT) { + qerror_report(QERR_INVALID_PARAMETER_TYPE, client_arg_name, + "number"); + return -1; } - if (qobject_type(value) == QTYPE_QBOOL) { - /* handlers expect a QInt, they need to be changed */ - qdict_put(args, name, - qint_from_int(qbool_get_int(qobject_to_qbool(value)))); + break; + case 'b': + case '-': + if (qobject_type(client_arg) != QTYPE_QBOOL) { + qerror_report(QERR_INVALID_PARAMETER_TYPE, client_arg_name, + "bool"); + return -1; } break; + case 'O': + assert(flags & QMP_ACCEPT_UNKNOWNS); + break; + case '/': + case '.': + /* + * These types are not supported by QMP and thus are not + * handled here. Fall through. + */ default: - /* impossible */ abort(); + } } return 0; } -static void cmd_args_init(CmdArgs *cmd_args) +/* + * - Check if the client has passed all mandatory args + * - Set special flags for argument validation + */ +static int check_mandatory_args(const QDict *cmd_args, + const QDict *client_args, int *flags) { - cmd_args->name = qstring_new(); - cmd_args->type = cmd_args->flag = cmd_args->optional = 0; + const QDictEntry *ent; + + for (ent = qdict_first(cmd_args); ent; ent = qdict_next(cmd_args, ent)) { + const char *cmd_arg_name = qdict_entry_key(ent); + QString *type = qobject_to_qstring(qdict_entry_value(ent)); + assert(type != NULL); + + if (qstring_get_str(type)[0] == 'O') { + assert((*flags & QMP_ACCEPT_UNKNOWNS) == 0); + *flags |= QMP_ACCEPT_UNKNOWNS; + } else if (qstring_get_str(type)[0] != '-' && + qstring_get_str(type)[1] != '?' && + !qdict_haskey(client_args, cmd_arg_name)) { + qerror_report(QERR_MISSING_PARAMETER, cmd_arg_name); + return -1; + } + } + + return 0; +} + +static QDict *qdict_from_args_type(const char *args_type) +{ + int i; + QDict *qdict; + QString *key, *type, *cur_qs; + + assert(args_type != NULL); + + qdict = qdict_new(); + + if (args_type == NULL || args_type[0] == '\0') { + /* no args, empty qdict */ + goto out; + } + + key = qstring_new(); + type = qstring_new(); + + cur_qs = key; + + for (i = 0;; i++) { + switch (args_type[i]) { + case ',': + case '\0': + qdict_put(qdict, qstring_get_str(key), type); + QDECREF(key); + if (args_type[i] == '\0') { + goto out; + } + type = qstring_new(); /* qdict has ref */ + cur_qs = key = qstring_new(); + break; + case ':': + cur_qs = type; + break; + default: + qstring_append_chr(cur_qs, args_type[i]); + break; + } + } + +out: + return qdict; } /* - * This is not trivial, we have to parse Monitor command's argument - * type syntax to be able to check the arguments provided by clients. + * Client argument checking rules: * - * In the near future we will be using an array for that and will be - * able to drop all this parsing... + * 1. Client must provide all mandatory arguments + * 2. Each argument provided by the client must be expected + * 3. Each argument provided by the client must have the type expected + * by the command */ -static int monitor_check_qmp_args(const mon_cmd_t *cmd, QDict *args) +static int qmp_check_client_args(const mon_cmd_t *cmd, QDict *client_args) { - int err; - const char *p; - CmdArgs cmd_args; + int flags, err; + QDict *cmd_args; + + cmd_args = qdict_from_args_type(cmd->args_type); - if (cmd->args_type == NULL) { - return (qdict_size(args) == 0 ? 0 : -1); + flags = 0; + err = check_mandatory_args(cmd_args, client_args, &flags); + if (err) { + goto out; } - err = 0; - cmd_args_init(&cmd_args); + err = check_client_args_type(client_args, cmd_args, flags); - for (p = cmd->args_type;; p++) { - if (*p == ':') { - cmd_args.type = *++p; - p++; - if (cmd_args.type == '-') { - cmd_args.flag = *p++; - cmd_args.optional = 1; - } else if (*p == '?') { - cmd_args.optional = 1; - p++; - } +out: + QDECREF(cmd_args); + return err; +} + +/* + * Input object checking rules + * + * 1. Input object must be a dict + * 2. The "execute" key must exist + * 3. The "execute" key must be a string + * 4. If the "arguments" key exists, it must be a dict + * 5. If the "id" key exists, it can be anything (ie. json-value) + * 6. Any argument not listed above is considered invalid + */ +static QDict *qmp_check_input_obj(QObject *input_obj) +{ + const QDictEntry *ent; + int has_exec_key = 0; + QDict *input_dict; - assert(*p == ',' || *p == '\0'); - err = check_arg(&cmd_args, args); + if (qobject_type(input_obj) != QTYPE_QDICT) { + qerror_report(QERR_QMP_BAD_INPUT_OBJECT, "object"); + return NULL; + } - QDECREF(cmd_args.name); - cmd_args_init(&cmd_args); + input_dict = qobject_to_qdict(input_obj); - if (err < 0) { - break; + for (ent = qdict_first(input_dict); ent; ent = qdict_next(input_dict, ent)){ + const char *arg_name = qdict_entry_key(ent); + const QObject *arg_obj = qdict_entry_value(ent); + + if (!strcmp(arg_name, "execute")) { + if (qobject_type(arg_obj) != QTYPE_QSTRING) { + qerror_report(QERR_QMP_BAD_INPUT_OBJECT_MEMBER, "execute", + "string"); + return NULL; } + has_exec_key = 1; + } else if (!strcmp(arg_name, "arguments")) { + if (qobject_type(arg_obj) != QTYPE_QDICT) { + qerror_report(QERR_QMP_BAD_INPUT_OBJECT_MEMBER, "arguments", + "object"); + return NULL; + } + } else if (!strcmp(arg_name, "id")) { + /* FIXME: check duplicated IDs for async commands */ } else { - qstring_append_chr(cmd_args.name, *p); + qerror_report(QERR_QMP_EXTRA_MEMBER, arg_name); + return NULL; } + } - if (*p == '\0') { - break; + if (!has_exec_key) { + qerror_report(QERR_QMP_BAD_INPUT_OBJECT, "execute"); + return NULL; + } + + return input_dict; +} + +static void qmp_call_query_cmd(Monitor *mon, const mon_cmd_t *cmd) +{ + QObject *ret_data = NULL; + + if (handler_is_async(cmd)) { + qmp_async_info_handler(mon, cmd); + if (monitor_has_error(mon)) { + monitor_protocol_emitter(mon, NULL); } + } else { + cmd->mhandler.info_new(mon, &ret_data); + monitor_protocol_emitter(mon, ret_data); + qobject_decref(ret_data); } +} - QDECREF(cmd_args.name); - return err; +static void qmp_call_cmd(Monitor *mon, const mon_cmd_t *cmd, + const QDict *params) +{ + int ret; + QObject *data = NULL; + + mon_print_count_init(mon); + + ret = cmd->mhandler.cmd_new(mon, params, &data); + handler_audit(mon, cmd, ret); + monitor_protocol_emitter(mon, data); + qobject_decref(data); } static void handle_qmp_command(JSONMessageParser *parser, QList *tokens) @@ -4063,55 +4971,42 @@ QDict *input, *args; const mon_cmd_t *cmd; Monitor *mon = cur_mon; - const char *cmd_name, *info_item; + const char *cmd_name, *query_cmd; - args = NULL; - qemu_errors_to_mon(mon); + query_cmd = NULL; + args = input = NULL; obj = json_parser_parse(tokens, NULL); if (!obj) { // FIXME: should be triggered in json_parser_parse() - qemu_error_new(QERR_JSON_PARSING); + qerror_report(QERR_JSON_PARSING); goto err_out; - } else if (qobject_type(obj) != QTYPE_QDICT) { - qemu_error_new(QERR_QMP_BAD_INPUT_OBJECT, "object"); + } + + input = qmp_check_input_obj(obj); + if (!input) { qobject_decref(obj); goto err_out; } - input = qobject_to_qdict(obj); - mon->mc->id = qdict_get(input, "id"); qobject_incref(mon->mc->id); - obj = qdict_get(input, "execute"); - if (!obj) { - qemu_error_new(QERR_QMP_BAD_INPUT_OBJECT, "execute"); - goto err_input; - } else if (qobject_type(obj) != QTYPE_QSTRING) { - qemu_error_new(QERR_QMP_BAD_INPUT_OBJECT, "string"); - goto err_input; - } - - cmd_name = qstring_get_str(qobject_to_qstring(obj)); - - /* - * XXX: We need this special case until we get info handlers - * converted into 'query-' commands - */ - if (compare_cmd(cmd_name, "info")) { - qemu_error_new(QERR_COMMAND_NOT_FOUND, cmd_name); - goto err_input; - } else if (strstart(cmd_name, "query-", &info_item)) { - cmd = monitor_find_command("info"); - qdict_put_obj(input, "arguments", - qobject_from_jsonf("{ 'item': %s }", info_item)); + cmd_name = qdict_get_str(input, "execute"); + if (invalid_qmp_mode(mon, cmd_name)) { + qerror_report(QERR_COMMAND_NOT_FOUND, cmd_name); + goto err_out; + } + + if (strstart(cmd_name, "query-", &query_cmd)) { + cmd = qmp_find_query_cmd(query_cmd); } else { - cmd = monitor_find_command(cmd_name); - if (!cmd || !monitor_handler_ported(cmd)) { - qemu_error_new(QERR_COMMAND_NOT_FOUND, cmd_name); - goto err_input; - } + cmd = qmp_find_cmd(cmd_name); + } + + if (!cmd) { + qerror_report(QERR_COMMAND_NOT_FOUND, cmd_name); + goto err_out; } obj = qdict_get(input, "arguments"); @@ -4122,23 +5017,30 @@ QINCREF(args); } - QDECREF(input); - - err = monitor_check_qmp_args(cmd, args); + err = qmp_check_client_args(cmd, args); if (err < 0) { goto err_out; } - monitor_call_handler(mon, cmd, args); + if (query_cmd) { + qmp_call_query_cmd(mon, cmd); + } else if (handler_is_async(cmd)) { + err = qmp_async_cmd_handler(mon, cmd, args); + if (err) { + /* emit the error response */ + goto err_out; + } + } else { + qmp_call_cmd(mon, cmd, args); + } + goto out; -err_input: - QDECREF(input); err_out: monitor_protocol_emitter(mon, NULL); out: + QDECREF(input); QDECREF(args); - qemu_errors_to_previous(); } /** @@ -4198,22 +5100,33 @@ readline_show_prompt(mon->rs); } +static QObject *get_qmp_greeting(void) +{ + QObject *ver; + + do_info_version(NULL, &ver); + return qobject_from_jsonf("{'QMP':{'version': %p,'capabilities': []}}",ver); +} + /** * monitor_control_event(): Print QMP gretting */ static void monitor_control_event(void *opaque, int event) { - if (event == CHR_EVENT_OPENED) { - QObject *data; - Monitor *mon = opaque; + QObject *data; + Monitor *mon = opaque; + switch (event) { + case CHR_EVENT_OPENED: + mon->mc->command_mode = 0; json_message_parser_init(&mon->mc->parser, handle_qmp_command); - - data = qobject_from_jsonf("{ 'QMP': { 'capabilities': [] } }"); - assert(data != NULL); - + data = get_qmp_greeting(); monitor_json_emitter(mon, data); qobject_decref(data); + break; + case CHR_EVENT_CLOSED: + json_message_parser_destroy(&mon->mc->parser); + break; } } @@ -4290,14 +5203,15 @@ /* Control mode requires special handlers */ qemu_chr_add_handlers(chr, monitor_can_read, monitor_control_read, monitor_control_event, mon); + qemu_chr_set_echo(chr, true); } else { qemu_chr_add_handlers(chr, monitor_can_read, monitor_read, monitor_event, mon); } QLIST_INSERT_HEAD(&mon_list, mon, entry); - if (!cur_mon || (flags & MONITOR_IS_DEFAULT)) - cur_mon = mon; + if (!default_mon || (flags & MONITOR_IS_DEFAULT)) + default_mon = mon; } static void bdrv_password_cb(Monitor *mon, const char *password, void *opaque) @@ -4315,21 +5229,21 @@ monitor_read_command(mon, 1); } -void monitor_read_bdrv_key_start(Monitor *mon, BlockDriverState *bs, - BlockDriverCompletionFunc *completion_cb, - void *opaque) +int monitor_read_bdrv_key_start(Monitor *mon, BlockDriverState *bs, + BlockDriverCompletionFunc *completion_cb, + void *opaque) { int err; if (!bdrv_key_required(bs)) { if (completion_cb) completion_cb(opaque, 0); - return; + return 0; } if (monitor_ctrl_mode(mon)) { - qemu_error_new(QERR_DEVICE_ENCRYPTED, bdrv_get_device_name(bs)); - return; + qerror_report(QERR_DEVICE_ENCRYPTED, bdrv_get_device_name(bs)); + return -1; } monitor_printf(mon, "%s (%s) is encrypted.\n", bdrv_get_device_name(bs), @@ -4342,94 +5256,6 @@ if (err && completion_cb) completion_cb(opaque, err); -} -typedef struct QemuErrorSink QemuErrorSink; -struct QemuErrorSink { - enum { - ERR_SINK_FILE, - ERR_SINK_MONITOR, - } dest; - union { - FILE *fp; - Monitor *mon; - }; - QemuErrorSink *previous; -}; - -static QemuErrorSink *qemu_error_sink; - -void qemu_errors_to_file(FILE *fp) -{ - QemuErrorSink *sink; - - sink = qemu_mallocz(sizeof(*sink)); - sink->dest = ERR_SINK_FILE; - sink->fp = fp; - sink->previous = qemu_error_sink; - qemu_error_sink = sink; -} - -void qemu_errors_to_mon(Monitor *mon) -{ - QemuErrorSink *sink; - - sink = qemu_mallocz(sizeof(*sink)); - sink->dest = ERR_SINK_MONITOR; - sink->mon = mon; - sink->previous = qemu_error_sink; - qemu_error_sink = sink; -} - -void qemu_errors_to_previous(void) -{ - QemuErrorSink *sink; - - assert(qemu_error_sink != NULL); - sink = qemu_error_sink; - qemu_error_sink = sink->previous; - qemu_free(sink); -} - -void qemu_error(const char *fmt, ...) -{ - va_list args; - - assert(qemu_error_sink != NULL); - switch (qemu_error_sink->dest) { - case ERR_SINK_FILE: - va_start(args, fmt); - vfprintf(qemu_error_sink->fp, fmt, args); - va_end(args); - break; - case ERR_SINK_MONITOR: - va_start(args, fmt); - monitor_vprintf(qemu_error_sink->mon, fmt, args); - va_end(args); - break; - } -} - -void qemu_error_internal(const char *file, int linenr, const char *func, - const char *fmt, ...) -{ - va_list va; - QError *qerror; - - assert(qemu_error_sink != NULL); - - va_start(va, fmt); - qerror = qerror_from_info(file, linenr, func, fmt, &va); - va_end(va); - - switch (qemu_error_sink->dest) { - case ERR_SINK_FILE: - qerror_print(qerror); - QDECREF(qerror); - break; - case ERR_SINK_MONITOR: - assert(qemu_error_sink->mon->error == NULL); - qemu_error_sink->mon->error = qerror; - break; - } + return err; } diff -Nru qemu-kvm-0.12.5+noroms/monitor.h qemu-kvm-0.14.1/monitor.h --- qemu-kvm-0.12.5+noroms/monitor.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/monitor.h 2011-05-11 13:29:46.000000000 +0000 @@ -3,42 +3,63 @@ #include "qemu-common.h" #include "qemu-char.h" +#include "qerror.h" #include "qdict.h" #include "block.h" extern Monitor *cur_mon; +extern Monitor *default_mon; /* flags for monitor_init */ #define MONITOR_IS_DEFAULT 0x01 #define MONITOR_USE_READLINE 0x02 #define MONITOR_USE_CONTROL 0x04 +#define MONITOR_USE_PRETTY 0x08 + +/* flags for monitor commands */ +#define MONITOR_CMD_ASYNC 0x0001 /* QMP events */ typedef enum MonitorEvent { - QEVENT_DEBUG, QEVENT_SHUTDOWN, QEVENT_RESET, QEVENT_POWERDOWN, QEVENT_STOP, + QEVENT_RESUME, + QEVENT_VNC_CONNECTED, + QEVENT_VNC_INITIALIZED, + QEVENT_VNC_DISCONNECTED, + QEVENT_BLOCK_IO_ERROR, + QEVENT_RTC_CHANGE, + QEVENT_WATCHDOG, + QEVENT_SPICE_CONNECTED, + QEVENT_SPICE_INITIALIZED, + QEVENT_SPICE_DISCONNECTED, QEVENT_MAX, } MonitorEvent; +int monitor_cur_is_qmp(void); + void monitor_protocol_event(MonitorEvent event, QObject *data); void monitor_init(CharDriverState *chr, int flags); int monitor_suspend(Monitor *mon); void monitor_resume(Monitor *mon); -void monitor_read_bdrv_key_start(Monitor *mon, BlockDriverState *bs, - BlockDriverCompletionFunc *completion_cb, - void *opaque); +int monitor_read_bdrv_key_start(Monitor *mon, BlockDriverState *bs, + BlockDriverCompletionFunc *completion_cb, + void *opaque); int monitor_get_fd(Monitor *mon, const char *fdname); -void monitor_vprintf(Monitor *mon, const char *fmt, va_list ap); -void monitor_printf(Monitor *mon, const char *fmt, ...) - __attribute__ ((__format__ (__printf__, 2, 3))); +void monitor_vprintf(Monitor *mon, const char *fmt, va_list ap) + GCC_FMT_ATTR(2, 0); +void monitor_printf(Monitor *mon, const char *fmt, ...) GCC_FMT_ATTR(2, 3); void monitor_print_filename(Monitor *mon, const char *filename); void monitor_flush(Monitor *mon); +typedef void (MonitorCompletion)(void *opaque, QObject *ret_data); + +void monitor_set_error(Monitor *mon, QError *qerror); + #endif /* !MONITOR_H */ diff -Nru qemu-kvm-0.12.5+noroms/nbd.c qemu-kvm-0.14.1/nbd.c --- qemu-kvm-0.12.5+noroms/nbd.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/nbd.c 2011-05-11 13:29:46.000000000 +0000 @@ -23,7 +23,7 @@ #ifndef _WIN32 #include #endif -#ifdef __sun__ +#if defined(__sun__) || defined(__HAIKU__) #include #endif #include @@ -49,6 +49,7 @@ /* This is all part of the "official" NBD API */ +#define NBD_REPLY_SIZE (4 + 4 + 8) #define NBD_REQUEST_MAGIC 0x25609513 #define NBD_REPLY_MAGIC 0x67446698 @@ -62,6 +63,8 @@ #define NBD_SET_SIZE_BLOCKS _IO(0xab, 7) #define NBD_DISCONNECT _IO(0xab, 8) +#define NBD_OPT_EXPORT_NAME (1 << 0) + /* That's all folks */ #define read_sync(fd, buffer, size) nbd_wr_sync(fd, buffer, size, true) @@ -296,22 +299,27 @@ return 0; } -int nbd_receive_negotiate(int csock, off_t *size, size_t *blocksize) +int nbd_receive_negotiate(int csock, const char *name, uint32_t *flags, + off_t *size, size_t *blocksize) { - char buf[8 + 8 + 8 + 128]; - uint64_t magic; + char buf[256]; + uint64_t magic, s; + uint16_t tmp; TRACE("Receiving negotation."); - if (read_sync(csock, buf, sizeof(buf)) != sizeof(buf)) { + if (read_sync(csock, buf, 8) != 8) { LOG("read failed"); errno = EINVAL; return -1; } - magic = be64_to_cpup((uint64_t*)(buf + 8)); - *size = be64_to_cpup((uint64_t*)(buf + 16)); - *blocksize = 1024; + buf[8] = '\0'; + if (strlen(buf) == 0) { + LOG("server connection closed"); + errno = EINVAL; + return -1; + } TRACE("Magic is %c%c%c%c%c%c%c%c", qemu_isprint(buf[0]) ? buf[0] : '.', @@ -322,8 +330,6 @@ qemu_isprint(buf[5]) ? buf[5] : '.', qemu_isprint(buf[6]) ? buf[6] : '.', qemu_isprint(buf[7]) ? buf[7] : '.'); - TRACE("Magic is 0x%" PRIx64, magic); - TRACE("Size is %" PRIu64, *size); if (memcmp(buf, "NBDMAGIC", 8) != 0) { LOG("Invalid magic received"); @@ -331,10 +337,99 @@ return -1; } - TRACE("Checking magic"); + if (read_sync(csock, &magic, sizeof(magic)) != sizeof(magic)) { + LOG("read failed"); + errno = EINVAL; + return -1; + } + magic = be64_to_cpu(magic); + TRACE("Magic is 0x%" PRIx64, magic); + + if (name) { + uint32_t reserved = 0; + uint32_t opt; + uint32_t namesize; + + TRACE("Checking magic (opts_magic)"); + if (magic != 0x49484156454F5054LL) { + LOG("Bad magic received"); + errno = EINVAL; + return -1; + } + if (read_sync(csock, &tmp, sizeof(tmp)) != sizeof(tmp)) { + LOG("flags read failed"); + errno = EINVAL; + return -1; + } + *flags = be16_to_cpu(tmp) << 16; + /* reserved for future use */ + if (write_sync(csock, &reserved, sizeof(reserved)) != + sizeof(reserved)) { + LOG("write failed (reserved)"); + errno = EINVAL; + return -1; + } + /* write the export name */ + magic = cpu_to_be64(magic); + if (write_sync(csock, &magic, sizeof(magic)) != sizeof(magic)) { + LOG("write failed (magic)"); + errno = EINVAL; + return -1; + } + opt = cpu_to_be32(NBD_OPT_EXPORT_NAME); + if (write_sync(csock, &opt, sizeof(opt)) != sizeof(opt)) { + LOG("write failed (opt)"); + errno = EINVAL; + return -1; + } + namesize = cpu_to_be32(strlen(name)); + if (write_sync(csock, &namesize, sizeof(namesize)) != + sizeof(namesize)) { + LOG("write failed (namesize)"); + errno = EINVAL; + return -1; + } + if (write_sync(csock, (char*)name, strlen(name)) != strlen(name)) { + LOG("write failed (name)"); + errno = EINVAL; + return -1; + } + } else { + TRACE("Checking magic (cli_magic)"); + + if (magic != 0x00420281861253LL) { + LOG("Bad magic received"); + errno = EINVAL; + return -1; + } + } + + if (read_sync(csock, &s, sizeof(s)) != sizeof(s)) { + LOG("read failed"); + errno = EINVAL; + return -1; + } + *size = be64_to_cpu(s); + *blocksize = 1024; + TRACE("Size is %" PRIu64, *size); - if (magic != 0x00420281861253LL) { - LOG("Bad magic received"); + if (!name) { + if (read_sync(csock, flags, sizeof(*flags)) != sizeof(*flags)) { + LOG("read failed (flags)"); + errno = EINVAL; + return -1; + } + *flags = be32_to_cpup(flags); + } else { + if (read_sync(csock, &tmp, sizeof(tmp)) != sizeof(tmp)) { + LOG("read failed (tmp)"); + errno = EINVAL; + return -1; + } + *flags |= be32_to_cpu(tmp); + } + if (read_sync(csock, &buf, 124) != 124) { + LOG("read failed (buf)"); errno = EINVAL; return -1; } @@ -353,8 +448,7 @@ return -1; } - TRACE("Setting size to %llu block(s)", - (unsigned long long)(size / blocksize)); + TRACE("Setting size to %zd block(s)", (size_t)(size / blocksize)); if (ioctl(fd, NBD_SET_SIZE_BLOCKS, size / blocksize) == -1) { int serrno = errno; @@ -394,7 +488,7 @@ return 0; } -int nbd_client(int fd, int csock) +int nbd_client(int fd) { int ret; int serrno; @@ -428,7 +522,7 @@ return -1; } -int nbd_client(int fd, int csock) +int nbd_client(int fd) { errno = ENOTSUP; return -1; @@ -495,7 +589,7 @@ int nbd_receive_reply(int csock, struct nbd_reply *reply) { - uint8_t buf[4 + 4 + 8]; + uint8_t buf[NBD_REPLY_SIZE]; uint32_t magic; memset(buf, 0xAA, sizeof(buf)); @@ -562,9 +656,9 @@ if (nbd_receive_request(csock, &request) == -1) return -1; - if (request.len > data_size) { + if (request.len + NBD_REPLY_SIZE > data_size) { LOG("len (%u) is larger than max len (%u)", - request.len, data_size); + request.len + NBD_REPLY_SIZE, data_size); errno = EINVAL; return -1; } @@ -594,7 +688,8 @@ case NBD_CMD_READ: TRACE("Request type is READ"); - if (bdrv_read(bs, (request.from + dev_offset) / 512, data, + if (bdrv_read(bs, (request.from + dev_offset) / 512, + data + NBD_REPLY_SIZE, request.len / 512) == -1) { LOG("reading from file failed"); errno = EINVAL; @@ -604,12 +699,21 @@ TRACE("Read %u byte(s)", request.len); - if (nbd_send_reply(csock, &reply) == -1) - return -1; + /* Reply + [ 0 .. 3] magic (NBD_REPLY_MAGIC) + [ 4 .. 7] error (0 == no error) + [ 7 .. 15] handle + */ + + cpu_to_be32w((uint32_t*)data, NBD_REPLY_MAGIC); + cpu_to_be32w((uint32_t*)(data + 4), reply.error); + cpu_to_be64w((uint64_t*)(data + 8), reply.handle); TRACE("Sending data to client"); - if (write_sync(csock, data, request.len) != request.len) { + if (write_sync(csock, data, + request.len + NBD_REPLY_SIZE) != + request.len + NBD_REPLY_SIZE) { LOG("writing to socket failed"); errno = EINVAL; return -1; diff -Nru qemu-kvm-0.12.5+noroms/nbd.h qemu-kvm-0.14.1/nbd.h --- qemu-kvm-0.12.5+noroms/nbd.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/nbd.h 2011-05-11 13:29:46.000000000 +0000 @@ -20,7 +20,6 @@ #define NBD_H #include -#include #include #include "block_int.h" @@ -43,6 +42,8 @@ NBD_CMD_DISC = 2 }; +#define NBD_DEFAULT_PORT 10809 + size_t nbd_wr_sync(int fd, void *buffer, size_t size, bool do_read); int tcp_socket_outgoing(const char *address, uint16_t port); int tcp_socket_incoming(const char *address, uint16_t port); @@ -50,13 +51,14 @@ int unix_socket_incoming(const char *path); int nbd_negotiate(int csock, off_t size); -int nbd_receive_negotiate(int csock, off_t *size, size_t *blocksize); +int nbd_receive_negotiate(int csock, const char *name, uint32_t *flags, + off_t *size, size_t *blocksize); int nbd_init(int fd, int csock, off_t size, size_t blocksize); int nbd_send_request(int csock, struct nbd_request *request); int nbd_receive_reply(int csock, struct nbd_reply *reply); int nbd_trip(BlockDriverState *bs, int csock, off_t size, uint64_t dev_offset, off_t *offset, bool readonly, uint8_t *data, int data_size); -int nbd_client(int fd, int csock); +int nbd_client(int fd); int nbd_disconnect(int fd); #endif diff -Nru qemu-kvm-0.12.5+noroms/net/dump.c qemu-kvm-0.14.1/net/dump.c --- qemu-kvm-0.12.5+noroms/net/dump.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/net/dump.c 2011-05-11 13:29:46.000000000 +0000 @@ -25,6 +25,7 @@ #include "dump.h" #include "qemu-common.h" #include "sysemu.h" +#include "qemu-error.h" #include "qemu-log.h" typedef struct DumpState { @@ -107,7 +108,7 @@ fd = open(filename, O_CREAT | O_WRONLY | O_BINARY, 0644); if (fd < 0) { - qemu_error("-net dump: can't open %s\n", filename); + error_report("-net dump: can't open %s", filename); return -1; } @@ -120,7 +121,7 @@ hdr.linktype = 1; if (write(fd, &hdr, sizeof(hdr)) < sizeof(hdr)) { - qemu_error("-net dump write error: %s\n", strerror(errno)); + error_report("-net dump write error: %s", strerror(errno)); close(fd); return -1; } diff -Nru qemu-kvm-0.12.5+noroms/net/slirp.c qemu-kvm-0.14.1/net/slirp.c --- qemu-kvm-0.12.5+noroms/net/slirp.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/net/slirp.c 2011-05-11 13:29:46.000000000 +0000 @@ -25,6 +25,9 @@ #include "config-host.h" +#ifndef _WIN32 +#include +#endif #include "net.h" #include "monitor.h" #include "sysemu.h" @@ -410,14 +413,14 @@ if (slirp_add_hostfwd(s->slirp, is_udp, host_addr, host_port, guest_addr, guest_port) < 0) { - qemu_error("could not set up host forwarding rule '%s'\n", - redir_str); + error_report("could not set up host forwarding rule '%s'", + redir_str); return -1; } return 0; fail_syntax: - qemu_error("invalid host forwarding rule '%s'\n", redir_str); + error_report("invalid host forwarding rule '%s'", redir_str); return -1; } @@ -464,10 +467,17 @@ static void slirp_smb_cleanup(SlirpState *s) { char cmd[128]; + int ret; if (s->smb_dir[0] != '\0') { snprintf(cmd, sizeof(cmd), "rm -rf %s", s->smb_dir); - system(cmd); + ret = system(cmd); + if (ret == -1 || !WIFEXITED(ret)) { + error_report("'%s' failed.", cmd); + } else if (WEXITSTATUS(ret)) { + error_report("'%s' failed. Error code: %d", + cmd, WEXITSTATUS(ret)); + } s->smb_dir[0] = '\0'; } } @@ -483,7 +493,7 @@ snprintf(s->smb_dir, sizeof(s->smb_dir), "/tmp/qemu-smb.%ld-%d", (long)getpid(), instance++); if (mkdir(s->smb_dir, 0700) < 0) { - qemu_error("could not create samba server dir '%s'\n", s->smb_dir); + error_report("could not create samba server dir '%s'", s->smb_dir); return -1; } snprintf(smb_conf, sizeof(smb_conf), "%s/%s", s->smb_dir, "smb.conf"); @@ -491,8 +501,8 @@ f = fopen(smb_conf, "w"); if (!f) { slirp_smb_cleanup(s); - qemu_error("could not create samba server configuration file '%s'\n", - smb_conf); + error_report("could not create samba server configuration file '%s'", + smb_conf); return -1; } fprintf(f, @@ -523,7 +533,7 @@ if (slirp_add_exec(s->slirp, 0, smb_cmdline, &vserver_addr, 139) < 0) { slirp_smb_cleanup(s); - qemu_error("conflicting/invalid smbserver address\n"); + error_report("conflicting/invalid smbserver address"); return -1; } return 0; @@ -608,14 +618,14 @@ snprintf(buf, sizeof(buf), "guestfwd.tcp:%d", port); fwd->hd = qemu_chr_open(buf, p, NULL); if (!fwd->hd) { - qemu_error("could not open guest forwarding device '%s'\n", buf); + error_report("could not open guest forwarding device '%s'", buf); qemu_free(fwd); return -1; } if (slirp_add_exec(s->slirp, 3, fwd->hd, &server, port) < 0) { - qemu_error("conflicting/invalid host:port in guest forwarding " - "rule '%s'\n", config_str); + error_report("conflicting/invalid host:port in guest forwarding " + "rule '%s'", config_str); qemu_free(fwd); return -1; } @@ -628,7 +638,7 @@ return 0; fail_syntax: - qemu_error("invalid guest forwarding rule '%s'\n", config_str); + error_report("invalid guest forwarding rule '%s'", config_str); return -1; } diff -Nru qemu-kvm-0.12.5+noroms/net/socket.c qemu-kvm-0.14.1/net/socket.c --- qemu-kvm-0.12.5+noroms/net/socket.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/net/socket.c 2011-05-11 13:29:46.000000000 +0000 @@ -28,9 +28,9 @@ #include "net.h" #include "qemu-char.h" #include "qemu-common.h" +#include "qemu-error.h" #include "qemu-option.h" #include "qemu_socket.h" -#include "sysemu.h" typedef struct NetSocketState { VLANClientState nc; @@ -149,7 +149,7 @@ qemu_send_packet(&s->nc, s->buf, size); } -static int net_socket_mcast_create(struct sockaddr_in *mcastaddr) +static int net_socket_mcast_create(struct sockaddr_in *mcastaddr, struct in_addr *localaddr) { struct ip_mreq imr; int fd; @@ -183,7 +183,11 @@ /* Add host to multicast group */ imr.imr_multiaddr = mcastaddr->sin_addr; - imr.imr_interface.s_addr = htonl(INADDR_ANY); + if (localaddr) { + imr.imr_interface = *localaddr; + } else { + imr.imr_interface.s_addr = htonl(INADDR_ANY); + } ret = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const char *)&imr, sizeof(struct ip_mreq)); @@ -201,6 +205,16 @@ goto fail; } + /* If a bind address is given, only send packets from that address */ + if (localaddr != NULL) { + ret = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, + (const char *)localaddr, sizeof(*localaddr)); + if (ret < 0) { + perror("setsockopt(IP_MULTICAST_IF)"); + goto fail; + } + } + socket_set_nonblock(fd); return fd; fail: @@ -248,7 +262,7 @@ return NULL; } /* clone dgram socket */ - newfd = net_socket_mcast_create(&saddr); + newfd = net_socket_mcast_create(&saddr, NULL); if (newfd < 0) { /* error already reported by net_socket_mcast_create() */ close(fd); @@ -468,17 +482,26 @@ static int net_socket_mcast_init(VLANState *vlan, const char *model, const char *name, - const char *host_str) + const char *host_str, + const char *localaddr_str) { NetSocketState *s; int fd; struct sockaddr_in saddr; + struct in_addr localaddr, *param_localaddr; if (parse_host_port(&saddr, host_str) < 0) return -1; + if (localaddr_str != NULL) { + if (inet_aton(localaddr_str, &localaddr) == 0) + return -1; + param_localaddr = &localaddr; + } else { + param_localaddr = NULL; + } - fd = net_socket_mcast_create(&saddr); + fd = net_socket_mcast_create(&saddr, param_localaddr); if (fd < 0) return -1; @@ -505,8 +528,9 @@ if (qemu_opt_get(opts, "listen") || qemu_opt_get(opts, "connect") || - qemu_opt_get(opts, "mcast")) { - qemu_error("listen=, connect= and mcast= is invalid with fd=\n"); + qemu_opt_get(opts, "mcast") || + qemu_opt_get(opts, "localaddr")) { + error_report("listen=, connect=, mcast= and localaddr= is invalid with fd=\n"); return -1; } @@ -524,8 +548,9 @@ if (qemu_opt_get(opts, "fd") || qemu_opt_get(opts, "connect") || - qemu_opt_get(opts, "mcast")) { - qemu_error("fd=, connect= and mcast= is invalid with listen=\n"); + qemu_opt_get(opts, "mcast") || + qemu_opt_get(opts, "localaddr")) { + error_report("fd=, connect=, mcast= and localaddr= is invalid with listen=\n"); return -1; } @@ -539,8 +564,9 @@ if (qemu_opt_get(opts, "fd") || qemu_opt_get(opts, "listen") || - qemu_opt_get(opts, "mcast")) { - qemu_error("fd=, listen= and mcast= is invalid with connect=\n"); + qemu_opt_get(opts, "mcast") || + qemu_opt_get(opts, "localaddr")) { + error_report("fd=, listen=, mcast= and localaddr= is invalid with connect=\n"); return -1; } @@ -550,22 +576,23 @@ return -1; } } else if (qemu_opt_get(opts, "mcast")) { - const char *mcast; + const char *mcast, *localaddr; if (qemu_opt_get(opts, "fd") || qemu_opt_get(opts, "connect") || qemu_opt_get(opts, "listen")) { - qemu_error("fd=, connect= and listen= is invalid with mcast=\n"); + error_report("fd=, connect= and listen= is invalid with mcast="); return -1; } mcast = qemu_opt_get(opts, "mcast"); + localaddr = qemu_opt_get(opts, "localaddr"); - if (net_socket_mcast_init(vlan, "socket", name, mcast) == -1) { + if (net_socket_mcast_init(vlan, "socket", name, mcast, localaddr) == -1) { return -1; } } else { - qemu_error("-socket requires fd=, listen=, connect= or mcast=\n"); + error_report("-socket requires fd=, listen=, connect= or mcast="); return -1; } diff -Nru qemu-kvm-0.12.5+noroms/net/tap-aix.c qemu-kvm-0.14.1/net/tap-aix.c --- qemu-kvm-0.12.5+noroms/net/tap-aix.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/net/tap-aix.c 2011-05-11 13:29:46.000000000 +0000 @@ -46,6 +46,15 @@ return 0; } +int tap_probe_vnet_hdr_len(int fd, int len) +{ + return 0; +} + +void tap_fd_set_vnet_hdr_len(int fd, int len) +{ +} + void tap_fd_set_offload(int fd, int csum, int tso4, int tso6, int ecn, int ufo) { diff -Nru qemu-kvm-0.12.5+noroms/net/tap-bsd.c qemu-kvm-0.14.1/net/tap-bsd.c --- qemu-kvm-0.12.5+noroms/net/tap-bsd.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/net/tap-bsd.c 2011-05-11 13:29:46.000000000 +0000 @@ -25,6 +25,7 @@ #include "net/tap.h" #include "qemu-common.h" #include "sysemu.h" +#include "qemu-error.h" #ifdef __NetBSD__ #include @@ -36,18 +37,14 @@ #include #endif -#if defined(__OpenBSD__) -#include -#endif - int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required) { int fd; char *dev; struct stat s; -#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) - /* if no ifname is given, always start the search from tap0. */ +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__) + /* if no ifname is given, always start the search from tap0/tun0. */ int i; char dname[100]; @@ -55,7 +52,11 @@ if (*ifname) { snprintf(dname, sizeof dname, "/dev/%s", ifname); } else { +#if defined(__OpenBSD__) + snprintf(dname, sizeof dname, "/dev/tun%d", i); +#else snprintf(dname, sizeof dname, "/dev/tap%d", i); +#endif } TFR(fd = open(dname, O_RDWR)); if (fd >= 0) { @@ -69,7 +70,8 @@ } } if (fd < 0) { - qemu_error("warning: could not open %s (%s): no virtual network emulation\n", dname, strerror(errno)); + error_report("warning: could not open %s (%s): no virtual network emulation", + dname, strerror(errno)); return -1; } #else @@ -89,8 +91,8 @@ *vnet_hdr = 0; if (vnet_hdr_required && !*vnet_hdr) { - qemu_error("vnet_hdr=1 requested, but no kernel " - "support for IFF_VNET_HDR available"); + error_report("vnet_hdr=1 requested, but no kernel " + "support for IFF_VNET_HDR available"); close(fd); return -1; } @@ -114,6 +116,15 @@ return 0; } +int tap_probe_vnet_hdr_len(int fd, int len) +{ + return 0; +} + +void tap_fd_set_vnet_hdr_len(int fd, int len) +{ +} + void tap_fd_set_offload(int fd, int csum, int tso4, int tso6, int ecn, int ufo) { diff -Nru qemu-kvm-0.12.5+noroms/net/tap.c qemu-kvm-0.14.1/net/tap.c --- qemu-kvm-0.12.5+noroms/net/tap.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/net/tap.c 2011-05-11 13:29:46.000000000 +0000 @@ -38,9 +38,12 @@ #include "sysemu.h" #include "qemu-char.h" #include "qemu-common.h" +#include "qemu-error.h" #include "net/tap-linux.h" +#include "hw/vhost_net.h" + /* Maximum GSO packet size (64k) plus plenty of room for * the ethernet and virtio_net headers */ @@ -54,9 +57,10 @@ uint8_t buf[TAP_BUFSIZE]; unsigned int read_poll : 1; unsigned int write_poll : 1; - unsigned int has_vnet_hdr : 1; unsigned int using_vnet_hdr : 1; unsigned int has_ufo: 1; + VHostNetState *vhost_net; + unsigned host_vnet_hdr_len; } TAPState; static int launch_script(const char *setup_script, const char *ifname, int fd); @@ -117,11 +121,11 @@ TAPState *s = DO_UPCAST(TAPState, nc, nc); const struct iovec *iovp = iov; struct iovec iov_copy[iovcnt + 1]; - struct virtio_net_hdr hdr = { 0, }; + struct virtio_net_hdr_mrg_rxbuf hdr = { }; - if (s->has_vnet_hdr && !s->using_vnet_hdr) { + if (s->host_vnet_hdr_len && !s->using_vnet_hdr) { iov_copy[0].iov_base = &hdr; - iov_copy[0].iov_len = sizeof(hdr); + iov_copy[0].iov_len = s->host_vnet_hdr_len; memcpy(&iov_copy[1], iov, iovcnt * sizeof(*iov)); iovp = iov_copy; iovcnt++; @@ -135,11 +139,11 @@ TAPState *s = DO_UPCAST(TAPState, nc, nc); struct iovec iov[2]; int iovcnt = 0; - struct virtio_net_hdr hdr = { 0, }; + struct virtio_net_hdr_mrg_rxbuf hdr = { }; - if (s->has_vnet_hdr) { + if (s->host_vnet_hdr_len) { iov[iovcnt].iov_base = &hdr; - iov[iovcnt].iov_len = sizeof(hdr); + iov[iovcnt].iov_len = s->host_vnet_hdr_len; iovcnt++; } @@ -155,7 +159,7 @@ TAPState *s = DO_UPCAST(TAPState, nc, nc); struct iovec iov[1]; - if (s->has_vnet_hdr && !s->using_vnet_hdr) { + if (s->host_vnet_hdr_len && !s->using_vnet_hdr) { return tap_receive_raw(nc, buf, size); } @@ -198,9 +202,9 @@ break; } - if (s->has_vnet_hdr && !s->using_vnet_hdr) { - buf += sizeof(struct virtio_net_hdr); - size -= sizeof(struct virtio_net_hdr); + if (s->host_vnet_hdr_len && !s->using_vnet_hdr) { + buf += s->host_vnet_hdr_len; + size -= s->host_vnet_hdr_len; } size = qemu_send_packet_async(&s->nc, buf, size, tap_send_completed); @@ -225,7 +229,28 @@ assert(nc->info->type == NET_CLIENT_TYPE_TAP); - return s->has_vnet_hdr; + return !!s->host_vnet_hdr_len; +} + +int tap_has_vnet_hdr_len(VLANClientState *nc, int len) +{ + TAPState *s = DO_UPCAST(TAPState, nc, nc); + + assert(nc->info->type == NET_CLIENT_TYPE_TAP); + + return tap_probe_vnet_hdr_len(s->fd, len); +} + +void tap_set_vnet_hdr_len(VLANClientState *nc, int len) +{ + TAPState *s = DO_UPCAST(TAPState, nc, nc); + + assert(nc->info->type == NET_CLIENT_TYPE_TAP); + assert(len == sizeof(struct virtio_net_hdr_mrg_rxbuf) || + len == sizeof(struct virtio_net_hdr)); + + tap_fd_set_vnet_hdr_len(s->fd, len); + s->host_vnet_hdr_len = len; } void tap_using_vnet_hdr(VLANClientState *nc, int using_vnet_hdr) @@ -235,7 +260,7 @@ using_vnet_hdr = using_vnet_hdr != 0; assert(nc->info->type == NET_CLIENT_TYPE_TAP); - assert(s->has_vnet_hdr == using_vnet_hdr); + assert(!!s->host_vnet_hdr_len == using_vnet_hdr); s->using_vnet_hdr = using_vnet_hdr; } @@ -244,14 +269,22 @@ int tso6, int ecn, int ufo) { TAPState *s = DO_UPCAST(TAPState, nc, nc); + if (s->fd < 0) { + return; + } - return tap_fd_set_offload(s->fd, csum, tso4, tso6, ecn, ufo); + tap_fd_set_offload(s->fd, csum, tso4, tso6, ecn, ufo); } static void tap_cleanup(VLANClientState *nc) { TAPState *s = DO_UPCAST(TAPState, nc, nc); + if (s->vhost_net) { + vhost_net_cleanup(s->vhost_net); + s->vhost_net = NULL; + } + qemu_purge_queued_packets(nc); if (s->down_script[0]) @@ -260,6 +293,21 @@ tap_read_poll(s, 0); tap_write_poll(s, 0); close(s->fd); + s->fd = -1; +} + +static void tap_poll(VLANClientState *nc, bool enable) +{ + TAPState *s = DO_UPCAST(TAPState, nc, nc); + tap_read_poll(s, enable); + tap_write_poll(s, enable); +} + +int tap_get_fd(VLANClientState *nc) +{ + TAPState *s = DO_UPCAST(TAPState, nc, nc); + assert(nc->info->type == NET_CLIENT_TYPE_TAP); + return s->fd; } /* fd support */ @@ -270,6 +318,7 @@ .receive = tap_receive, .receive_raw = tap_receive_raw, .receive_iov = tap_receive_iov, + .poll = tap_poll, .cleanup = tap_cleanup, }; @@ -287,11 +336,12 @@ s = DO_UPCAST(TAPState, nc, nc); s->fd = fd; - s->has_vnet_hdr = vnet_hdr != 0; + s->host_vnet_hdr_len = vnet_hdr ? sizeof(struct virtio_net_hdr) : 0; s->using_vnet_hdr = 0; s->has_ufo = tap_probe_has_ufo(s->fd); tap_set_offload(&s->nc, 0, 0, 0, 0, 0); tap_read_poll(s, 1); + s->vhost_net = NULL; return s; } @@ -322,7 +372,7 @@ parg = args; *parg++ = (char *)setup_script; *parg++ = (char *)ifname; - *parg++ = NULL; + *parg = NULL; execv(setup_script, args); _exit(1); } else if (pid > 0) { @@ -385,7 +435,7 @@ qemu_opt_get(opts, "script") || qemu_opt_get(opts, "downscript") || qemu_opt_get(opts, "vnet_hdr")) { - qemu_error("ifname=, script=, downscript= and vnet_hdr= is invalid with fd=\n"); + error_report("ifname=, script=, downscript= and vnet_hdr= is invalid with fd="); return -1; } @@ -441,5 +491,35 @@ } } + if (qemu_opt_get_bool(opts, "vhost", !!qemu_opt_get(opts, "vhostfd") || + qemu_opt_get_bool(opts, "vhostforce", false))) { + int vhostfd, r; + bool force = qemu_opt_get_bool(opts, "vhostforce", false); + if (qemu_opt_get(opts, "vhostfd")) { + r = net_handle_fd_param(mon, qemu_opt_get(opts, "vhostfd")); + if (r == -1) { + return -1; + } + vhostfd = r; + } else { + vhostfd = -1; + } + s->vhost_net = vhost_net_init(&s->nc, vhostfd, force); + if (!s->vhost_net) { + error_report("vhost-net requested but could not be initialized"); + return -1; + } + } else if (qemu_opt_get(opts, "vhostfd")) { + error_report("vhostfd= is not valid without vhost"); + return -1; + } + return 0; } + +VHostNetState *tap_get_vhost_net(VLANClientState *nc) +{ + TAPState *s = DO_UPCAST(TAPState, nc, nc); + assert(nc->info->type == NET_CLIENT_TYPE_TAP); + return s->vhost_net; +} diff -Nru qemu-kvm-0.12.5+noroms/net/tap.h qemu-kvm-0.14.1/net/tap.h --- qemu-kvm-0.12.5+noroms/net/tap.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/net/tap.h 2011-05-11 13:29:46.000000000 +0000 @@ -40,12 +40,21 @@ int tap_has_ufo(VLANClientState *vc); int tap_has_vnet_hdr(VLANClientState *vc); +int tap_has_vnet_hdr_len(VLANClientState *vc, int len); void tap_using_vnet_hdr(VLANClientState *vc, int using_vnet_hdr); void tap_set_offload(VLANClientState *vc, int csum, int tso4, int tso6, int ecn, int ufo); +void tap_set_vnet_hdr_len(VLANClientState *vc, int len); int tap_set_sndbuf(int fd, QemuOpts *opts); int tap_probe_vnet_hdr(int fd); +int tap_probe_vnet_hdr_len(int fd, int len); int tap_probe_has_ufo(int fd); void tap_fd_set_offload(int fd, int csum, int tso4, int tso6, int ecn, int ufo); +void tap_fd_set_vnet_hdr_len(int fd, int len); + +int tap_get_fd(VLANClientState *vc); + +struct vhost_net; +struct vhost_net *tap_get_vhost_net(VLANClientState *vc); #endif /* QEMU_NET_TAP_H */ diff -Nru qemu-kvm-0.12.5+noroms/net/tap-haiku.c qemu-kvm-0.14.1/net/tap-haiku.c --- qemu-kvm-0.12.5+noroms/net/tap-haiku.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/net/tap-haiku.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,61 @@ +/* + * 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 "net/tap.h" +#include + +int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required) +{ + fprintf(stderr, "no tap on Haiku\n"); + return -1; +} + +int tap_set_sndbuf(int fd, QemuOpts *opts) +{ + return 0; +} + +int tap_probe_vnet_hdr(int fd) +{ + return 0; +} + +int tap_probe_has_ufo(int fd) +{ + return 0; +} + +int tap_probe_vnet_hdr_len(int fd, int len) +{ + return 0; +} + +void tap_fd_set_vnet_hdr_len(int fd, int len) +{ +} + +void tap_fd_set_offload(int fd, int csum, int tso4, + int tso6, int ecn, int ufo) +{ +} diff -Nru qemu-kvm-0.12.5+noroms/net/tap-linux.c qemu-kvm-0.14.1/net/tap-linux.c --- qemu-kvm-0.12.5+noroms/net/tap-linux.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/net/tap-linux.c 2011-05-11 13:29:46.000000000 +0000 @@ -31,15 +31,18 @@ #include "sysemu.h" #include "qemu-common.h" +#include "qemu-error.h" + +#define PATH_NET_TUN "/dev/net/tun" int tap_open(char *ifname, int ifname_size, int *vnet_hdr, int vnet_hdr_required) { struct ifreq ifr; int fd, ret; - TFR(fd = open("/dev/net/tun", O_RDWR)); + TFR(fd = open(PATH_NET_TUN, O_RDWR)); if (fd < 0) { - fprintf(stderr, "warning: could not open /dev/net/tun: no virtual network emulation\n"); + error_report("could not open %s: %m", PATH_NET_TUN); return -1; } memset(&ifr, 0, sizeof(ifr)); @@ -57,8 +60,8 @@ } if (vnet_hdr_required && !*vnet_hdr) { - qemu_error("vnet_hdr=1 requested, but no kernel " - "support for IFF_VNET_HDR available"); + error_report("vnet_hdr=1 requested, but no kernel " + "support for IFF_VNET_HDR available"); close(fd); return -1; } @@ -70,7 +73,7 @@ pstrcpy(ifr.ifr_name, IFNAMSIZ, "tap%d"); ret = ioctl(fd, TUNSETIFF, (void *) &ifr); if (ret != 0) { - fprintf(stderr, "warning: could not configure /dev/net/tun: no virtual network emulation\n"); + error_report("could not configure %s (%s): %m", PATH_NET_TUN, ifr.ifr_name); close(fd); return -1; } @@ -79,12 +82,17 @@ return fd; } -/* sndbuf should be set to a value lower than the tx queue - * capacity of any destination network interface. +/* sndbuf implements a kind of flow control for tap. + * Unfortunately when it's enabled, and packets are sent + * to other guests on the same host, the receiver + * can lock up the transmitter indefinitely. + * + * To avoid packet loss, sndbuf should be set to a value lower than the tx + * queue capacity of any destination network interface. * Ethernet NICs generally have txqueuelen=1000, so 1Mb is - * a good default, given a 1500 byte MTU. + * a good value, given a 1500 byte MTU. */ -#define TAP_DEFAULT_SNDBUF 1024*1024 +#define TAP_DEFAULT_SNDBUF 0 int tap_set_sndbuf(int fd, QemuOpts *opts) { @@ -96,7 +104,7 @@ } if (ioctl(fd, TUNSETSNDBUF, &sndbuf) == -1 && qemu_opt_get(opts, "sndbuf")) { - qemu_error("TUNSETSNDBUF ioctl failed: %s\n", strerror(errno)); + error_report("TUNSETSNDBUF ioctl failed: %s", strerror(errno)); return -1; } return 0; @@ -107,7 +115,7 @@ struct ifreq ifr; if (ioctl(fd, TUNGETIFF, &ifr) != 0) { - qemu_error("TUNGETIFF ioctl() failed: %s\n", strerror(errno)); + error_report("TUNGETIFF ioctl() failed: %s", strerror(errno)); return 0; } @@ -126,6 +134,35 @@ return 1; } +/* Verify that we can assign given length */ +int tap_probe_vnet_hdr_len(int fd, int len) +{ + int orig; + if (ioctl(fd, TUNGETVNETHDRSZ, &orig) == -1) { + return 0; + } + if (ioctl(fd, TUNSETVNETHDRSZ, &len) == -1) { + return 0; + } + /* Restore original length: we can't handle failure. */ + if (ioctl(fd, TUNSETVNETHDRSZ, &orig) == -1) { + fprintf(stderr, "TUNGETVNETHDRSZ ioctl() failed: %s. Exiting.\n", + strerror(errno)); + assert(0); + return -errno; + } + return 1; +} + +void tap_fd_set_vnet_hdr_len(int fd, int len) +{ + if (ioctl(fd, TUNSETVNETHDRSZ, &len) == -1) { + fprintf(stderr, "TUNSETVNETHDRSZ ioctl() failed: %s. Exiting.\n", + strerror(errno)); + assert(0); + } +} + void tap_fd_set_offload(int fd, int csum, int tso4, int tso6, int ecn, int ufo) { diff -Nru qemu-kvm-0.12.5+noroms/net/tap-linux.h qemu-kvm-0.14.1/net/tap-linux.h --- qemu-kvm-0.12.5+noroms/net/tap-linux.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/net/tap-linux.h 2011-05-11 13:29:46.000000000 +0000 @@ -27,6 +27,8 @@ #define TUNSETOFFLOAD _IOW('T', 208, unsigned int) #define TUNGETIFF _IOR('T', 210, unsigned int) #define TUNSETSNDBUF _IOW('T', 212, int) +#define TUNGETVNETHDRSZ _IOR('T', 215, int) +#define TUNSETVNETHDRSZ _IOW('T', 216, int) #endif @@ -52,4 +54,10 @@ uint16_t csum_offset; }; +struct virtio_net_hdr_mrg_rxbuf +{ + struct virtio_net_hdr hdr; + uint16_t num_buffers; /* Number of merged rx buffers */ +}; + #endif /* QEMU_TAP_H */ diff -Nru qemu-kvm-0.12.5+noroms/net/tap-solaris.c qemu-kvm-0.14.1/net/tap-solaris.c --- qemu-kvm-0.12.5+noroms/net/tap-solaris.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/net/tap-solaris.c 2011-05-11 13:29:46.000000000 +0000 @@ -23,6 +23,7 @@ */ #include "net/tap.h" +#include "sysemu.h" #include #include @@ -37,6 +38,7 @@ #include #include #include +#include "qemu-error.h" ssize_t tap_read_packet(int tapfd, uint8_t *buf, int maxlen) { @@ -185,8 +187,8 @@ *vnet_hdr = 0; if (vnet_hdr_required && !*vnet_hdr) { - qemu_error("vnet_hdr=1 requested, but no kernel " - "support for IFF_VNET_HDR available"); + error_report("vnet_hdr=1 requested, but no kernel " + "support for IFF_VNET_HDR available"); close(fd); return -1; } @@ -210,6 +212,15 @@ return 0; } +int tap_probe_vnet_hdr_len(int fd, int len) +{ + return 0; +} + +void tap_fd_set_vnet_hdr_len(int fd, int len) +{ +} + void tap_fd_set_offload(int fd, int csum, int tso4, int tso6, int ecn, int ufo) { diff -Nru qemu-kvm-0.12.5+noroms/net/tap-win32.c qemu-kvm-0.14.1/net/tap-win32.c --- qemu-kvm-0.12.5+noroms/net/tap-win32.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/net/tap-win32.c 2011-05-11 13:29:46.000000000 +0000 @@ -31,6 +31,7 @@ #include "qemu-common.h" #include "net.h" #include "sysemu.h" +#include "qemu-error.h" #include #include #include @@ -578,7 +579,6 @@ } version; DWORD version_len; DWORD idThread; - HANDLE hThread; if (prefered_name != NULL) snprintf(name_buffer, sizeof(name_buffer), "%s", prefered_name); @@ -622,8 +622,8 @@ *phandle = &tap_overlapped; - hThread = CreateThread(NULL, 0, tap_win32_thread_entry, - (LPVOID)&tap_overlapped, 0, &idThread); + CreateThread(NULL, 0, tap_win32_thread_entry, + (LPVOID)&tap_overlapped, 0, &idThread); return 0; } @@ -706,7 +706,7 @@ ifname = qemu_opt_get(opts, "ifname"); if (!ifname) { - qemu_error("tap: no interface name\n"); + error_report("tap: no interface name"); return -1; } @@ -727,6 +727,15 @@ return 0; } +int tap_probe_vnet_hdr_len(int fd, int len) +{ + return 0; +} + +void tap_fd_set_vnet_hdr_len(int fd, int len) +{ +} + void tap_using_vnet_hdr(VLANClientState *vc, int using_vnet_hdr) { } @@ -735,3 +744,8 @@ int tso6, int ecn, int ufo) { } + +struct vhost_net *tap_get_vhost_net(VLANClientState *nc) +{ + return NULL; +} diff -Nru qemu-kvm-0.12.5+noroms/net.c qemu-kvm-0.14.1/net.c --- qemu-kvm-0.12.5+noroms/net.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/net.c 2011-05-11 13:29:46.000000000 +0000 @@ -35,6 +35,7 @@ #include "sysemu.h" #include "qemu-common.h" #include "qemu_socket.h" +#include "hw/qdev.h" static QTAILQ_HEAD(, VLANState) vlans; static QTAILQ_HEAD(, VLANClientState) non_vlan_clients; @@ -245,6 +246,7 @@ QTAILQ_INSERT_TAIL(&vc->vlan->clients, vc, next); } else { if (peer) { + assert(!peer->peer); vc->peer = peer; peer->peer = vc; } @@ -279,29 +281,64 @@ return nic; } -void qemu_del_vlan_client(VLANClientState *vc) +static void qemu_cleanup_vlan_client(VLANClientState *vc) { if (vc->vlan) { QTAILQ_REMOVE(&vc->vlan->clients, vc, next); } else { - if (vc->send_queue) { - qemu_del_net_queue(vc->send_queue); - } QTAILQ_REMOVE(&non_vlan_clients, vc, next); - if (vc->peer) { - vc->peer->peer = NULL; - } } if (vc->info->cleanup) { vc->info->cleanup(vc); } +} +static void qemu_free_vlan_client(VLANClientState *vc) +{ + if (!vc->vlan) { + if (vc->send_queue) { + qemu_del_net_queue(vc->send_queue); + } + if (vc->peer) { + vc->peer->peer = NULL; + } + } qemu_free(vc->name); qemu_free(vc->model); qemu_free(vc); } +void qemu_del_vlan_client(VLANClientState *vc) +{ + /* If there is a peer NIC, delete and cleanup client, but do not free. */ + if (!vc->vlan && vc->peer && vc->peer->info->type == NET_CLIENT_TYPE_NIC) { + NICState *nic = DO_UPCAST(NICState, nc, vc->peer); + if (nic->peer_deleted) { + return; + } + nic->peer_deleted = true; + /* Let NIC know peer is gone. */ + vc->peer->link_down = true; + if (vc->peer->info->link_status_changed) { + vc->peer->info->link_status_changed(vc->peer); + } + qemu_cleanup_vlan_client(vc); + return; + } + + /* If this is a peer NIC and peer has already been deleted, free it now. */ + if (!vc->vlan && vc->peer && vc->info->type == NET_CLIENT_TYPE_NIC) { + NICState *nic = DO_UPCAST(NICState, nc, vc); + if (nic->peer_deleted) { + qemu_free_vlan_client(vc->peer); + } + } + + qemu_cleanup_vlan_client(vc); + qemu_free_vlan_client(vc); +} + VLANClientState * qemu_find_vlan_client_by_name(Monitor *mon, int vlan_id, const char *client_str) @@ -731,25 +768,31 @@ return i; } - qemu_error("qemu: Unsupported NIC model: %s\n", nd->model); + error_report("qemu: Unsupported NIC model: %s", nd->model); return -1; } int net_handle_fd_param(Monitor *mon, const char *param) { - if (!qemu_isdigit(param[0])) { - int fd; + int fd; + + if (!qemu_isdigit(param[0]) && mon) { fd = monitor_get_fd(mon, param); if (fd == -1) { - qemu_error("No file descriptor named %s found", param); + error_report("No file descriptor named %s found", param); return -1; } - - return fd; } else { - return strtol(param, NULL, 0); + char *endptr = NULL; + + fd = strtol(param, &endptr, 10); + if (*endptr || (fd == 0 && param == endptr)) { + return -1; + } } + + return fd; } static int net_init_nic(QemuOpts *opts, @@ -763,7 +806,7 @@ idx = nic_get_free_idx(); if (idx == -1 || nb_nics >= MAX_NICS) { - qemu_error("Too Many NICs\n"); + error_report("Too Many NICs"); return -1; } @@ -774,7 +817,7 @@ if ((netdev = qemu_opt_get(opts, "netdev"))) { nd->netdev = qemu_find_netdev(netdev); if (!nd->netdev) { - qemu_error("netdev '%s' not found\n", netdev); + error_report("netdev '%s' not found", netdev); return -1; } } else { @@ -800,14 +843,15 @@ if (qemu_opt_get(opts, "macaddr") && net_parse_macaddr(nd->macaddr, qemu_opt_get(opts, "macaddr")) < 0) { - qemu_error("invalid syntax for ethernet address\n"); + error_report("invalid syntax for ethernet address"); return -1; } - nd->nvectors = qemu_opt_get_number(opts, "vectors", NIC_NVECTORS_UNSPECIFIED); - if (nd->nvectors != NIC_NVECTORS_UNSPECIFIED && + nd->nvectors = qemu_opt_get_number(opts, "vectors", + DEV_NVECTORS_UNSPECIFIED); + if (nd->nvectors != DEV_NVECTORS_UNSPECIFIED && (nd->nvectors < 0 || nd->nvectors > 0x7ffffff)) { - qemu_error("invalid # of vectors: %d\n", nd->nvectors); + error_report("invalid # of vectors: %d", nd->nvectors); return -1; } @@ -840,7 +884,7 @@ /* magic number, but compiler will warn if too small */ #define NET_MAX_DESC 20 -static struct { +static const struct { const char *type; net_client_init_func init; QemuOptDesc desc[NET_MAX_DESC]; @@ -973,7 +1017,19 @@ .name = "vnet_hdr", .type = QEMU_OPT_BOOL, .help = "enable the IFF_VNET_HDR flag on the tap interface" - }, + }, { + .name = "vhost", + .type = QEMU_OPT_BOOL, + .help = "enable vhost-net network accelerator", + }, { + .name = "vhostfd", + .type = QEMU_OPT_STRING, + .help = "file descriptor of an already opened vhost net device", + }, { + .name = "vhostforce", + .type = QEMU_OPT_BOOL, + .help = "force vhost on for non-MSIX virtio guests", + }, #endif /* _WIN32 */ { /* end of list */ } }, @@ -998,6 +1054,10 @@ .name = "mcast", .type = QEMU_OPT_STRING, .help = "UDP multicast address and port number", + }, { + .name = "localaddr", + .type = QEMU_OPT_STRING, + .help = "source address for multicast packets", }, { /* end of list */ } }, @@ -1054,18 +1114,12 @@ int i; type = qemu_opt_get(opts, "type"); + if (!type) { + qerror_report(QERR_MISSING_PARAMETER, "type"); + return -1; + } - if (!is_netdev) { - if (!type) { - qemu_error("No type specified for -net\n"); - return -1; - } - } else { - if (!type) { - qemu_error("No type specified for -netdev\n"); - return -1; - } - + if (is_netdev) { if (strcmp(type, "tap") != 0 && #ifdef CONFIG_SLIRP strcmp(type, "user") != 0 && @@ -1074,21 +1128,21 @@ strcmp(type, "vde") != 0 && #endif strcmp(type, "socket") != 0) { - qemu_error("The '%s' network backend type is not valid with -netdev\n", - type); + qerror_report(QERR_INVALID_PARAMETER_VALUE, "type", + "a netdev backend type"); return -1; } if (qemu_opt_get(opts, "vlan")) { - qemu_error("The 'vlan' parameter is not valid with -netdev\n"); + qerror_report(QERR_INVALID_PARAMETER, "vlan"); return -1; } if (qemu_opt_get(opts, "name")) { - qemu_error("The 'name' parameter is not valid with -netdev\n"); + qerror_report(QERR_INVALID_PARAMETER, "name"); return -1; } if (!qemu_opts_id(opts)) { - qemu_error("The id= parameter is required with -netdev\n"); + qerror_report(QERR_MISSING_PARAMETER, "id"); return -1; } } @@ -1101,6 +1155,7 @@ for (i = 0; net_client_types[i].type != NULL; i++) { if (!strcmp(net_client_types[i].type, type)) { VLANState *vlan = NULL; + int ret; if (qemu_opts_validate(opts, &net_client_types[i].desc[0]) == -1) { return -1; @@ -1113,15 +1168,21 @@ vlan = qemu_find_vlan(qemu_opt_get_number(opts, "vlan", 0), 1); } + ret = 0; if (net_client_types[i].init) { - return net_client_types[i].init(opts, mon, name, vlan); - } else { - return 0; + ret = net_client_types[i].init(opts, mon, name, vlan); + if (ret < 0) { + /* TODO push error reporting into init() methods */ + qerror_report(QERR_DEVICE_INIT_FAILED, type); + return -1; + } } + return ret; } } - qemu_error("Invalid -net type '%s'\n", type); + qerror_report(QERR_INVALID_PARAMETER_VALUE, "type", + "a network client type"); return -1; } @@ -1156,10 +1217,8 @@ return; } - opts = qemu_opts_parse(&qemu_net_opts, opts_str ? opts_str : "", NULL); + opts = qemu_opts_parse(qemu_find_opts("net"), opts_str ? opts_str : "", 0); if (!opts) { - monitor_printf(mon, "parsing network options '%s' failed\n", - opts_str ? opts_str : ""); return; } @@ -1187,6 +1246,39 @@ qemu_del_vlan_client(vc); } +int do_netdev_add(Monitor *mon, const QDict *qdict, QObject **ret_data) +{ + QemuOpts *opts; + int res; + + opts = qemu_opts_from_qdict(qemu_find_opts("netdev"), qdict); + if (!opts) { + return -1; + } + + res = net_client_init(mon, opts, 1); + if (res < 0) { + qemu_opts_del(opts); + } + + return res; +} + +int do_netdev_del(Monitor *mon, const QDict *qdict, QObject **ret_data) +{ + const char *id = qdict_get_str(qdict, "id"); + VLANClientState *vc; + + vc = qemu_find_netdev(id); + if (!vc || vc->info->type == NET_CLIENT_TYPE_NIC) { + qerror_report(QERR_DEVICE_NOT_FOUND, id); + return -1; + } + qemu_del_vlan_client(vc); + qemu_opts_del(qemu_opts_find(qemu_find_opts("netdev"), id)); + return 0; +} + void do_info_network(Monitor *mon) { VLANState *vlan; @@ -1209,12 +1301,12 @@ } } -void do_set_link(Monitor *mon, const QDict *qdict) +int do_set_link(Monitor *mon, const QDict *qdict, QObject **ret_data) { VLANState *vlan; VLANClientState *vc = NULL; const char *name = qdict_get_str(qdict, "name"); - const char *up_or_down = qdict_get_str(qdict, "up_or_down"); + int up = qdict_get_bool(qdict, "up"); QTAILQ_FOREACH(vlan, &vlans, next) { QTAILQ_FOREACH(vc, &vlan->clients, next) { @@ -1227,21 +1319,16 @@ done: if (!vc) { - monitor_printf(mon, "could not find network device '%s'\n", name); - return; + qerror_report(QERR_DEVICE_NOT_FOUND, name); + return -1; } - if (strcmp(up_or_down, "up") == 0) - vc->link_down = 0; - else if (strcmp(up_or_down, "down") == 0) - vc->link_down = 1; - else - monitor_printf(mon, "invalid link status '%s'; only 'up' or 'down' " - "valid\n", up_or_down); + vc->link_down = !up; if (vc->info->link_status_changed) { vc->info->link_status_changed(vc); } + return 0; } void net_cleanup(void) @@ -1264,7 +1351,7 @@ { VLANState *vlan; VLANClientState *vc; - int has_nic, has_host_dev; + int has_nic = 0, has_host_dev = 0; QTAILQ_FOREACH(vlan, &vlans, next) { QTAILQ_FOREACH(vc, &vlan->clients, next) { @@ -1311,21 +1398,23 @@ int net_init_clients(void) { + QemuOptsList *net = qemu_find_opts("net"); + if (default_net) { /* if no clients, we use a default config */ - qemu_opts_set(&qemu_net_opts, NULL, "type", "nic"); + qemu_opts_set(net, NULL, "type", "nic"); #ifdef CONFIG_SLIRP - qemu_opts_set(&qemu_net_opts, NULL, "type", "user"); + qemu_opts_set(net, NULL, "type", "user"); #endif } QTAILQ_INIT(&vlans); QTAILQ_INIT(&non_vlan_clients); - if (qemu_opts_foreach(&qemu_netdev_opts, net_init_netdev, NULL, 1) == -1) + if (qemu_opts_foreach(qemu_find_opts("netdev"), net_init_netdev, NULL, 1) == -1) return -1; - if (qemu_opts_foreach(&qemu_net_opts, net_init_client, NULL, 1) == -1) { + if (qemu_opts_foreach(net, net_init_client, NULL, 1) == -1) { return -1; } @@ -1341,7 +1430,7 @@ } #endif - if (!qemu_opts_parse(opts_list, optarg, "type")) { + if (!qemu_opts_parse(opts_list, optarg, 1)) { return -1; } diff -Nru qemu-kvm-0.12.5+noroms/net.h qemu-kvm-0.14.1/net.h --- qemu-kvm-0.12.5+noroms/net.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/net.h 2011-05-11 13:29:46.000000000 +0000 @@ -17,12 +17,14 @@ MACAddr macaddr; VLANState *vlan; VLANClientState *peer; + int32_t bootindex; } NICConf; #define DEFINE_NIC_PROPERTIES(_state, _conf) \ DEFINE_PROP_MACADDR("mac", _state, _conf.macaddr), \ DEFINE_PROP_VLAN("vlan", _state, _conf.vlan), \ - DEFINE_PROP_NETDEV("netdev", _state, _conf.peer) + DEFINE_PROP_NETDEV("netdev", _state, _conf.peer), \ + DEFINE_PROP_INT32("bootindex", _state, _conf.bootindex, -1) /* VLANs support */ @@ -36,6 +38,7 @@ NET_CLIENT_TYPE_DUMP } net_client_type; +typedef void (NetPoll)(VLANClientState *, bool enable); typedef int (NetCanReceive)(VLANClientState *); typedef ssize_t (NetReceive)(VLANClientState *, const uint8_t *, size_t); typedef ssize_t (NetReceiveIOV)(VLANClientState *, const struct iovec *, int); @@ -51,6 +54,7 @@ NetCanReceive *can_receive; NetCleanup *cleanup; LinkStatusChanged *link_status_changed; + NetPoll *poll; } NetClientInfo; struct VLANClientState { @@ -70,6 +74,7 @@ VLANClientState nc; NICConf *conf; void *opaque; + bool peer_deleted; } NICState; struct VLANState { @@ -115,14 +120,11 @@ const char *default_model); void do_info_network(Monitor *mon); -void do_set_link(Monitor *mon, const QDict *qdict); +int do_set_link(Monitor *mon, const QDict *qdict, QObject **ret_data); /* NIC info */ #define MAX_NICS 8 -enum { - NIC_NVECTORS_UNSPECIFIED = -1 -}; struct NICInfo { uint8_t macaddr[6]; @@ -132,7 +134,6 @@ VLANState *vlan; VLANClientState *netdev; int used; - int bootable; int nvectors; }; @@ -165,6 +166,8 @@ void net_cleanup(void); void net_host_device_add(Monitor *mon, const QDict *qdict); void net_host_device_remove(Monitor *mon, const QDict *qdict); +int do_netdev_add(Monitor *mon, const QDict *qdict, QObject **ret_data); +int do_netdev_del(Monitor *mon, const QDict *qdict, QObject **ret_data); #define DEFAULT_NETWORK_SCRIPT "/etc/qemu-ifup" #define DEFAULT_NETWORK_DOWN_SCRIPT "/etc/qemu-ifdown" diff -Nru qemu-kvm-0.12.5+noroms/notify.c qemu-kvm-0.14.1/notify.c --- qemu-kvm-0.12.5+noroms/notify.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/notify.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,39 @@ +/* + * Notifier lists + * + * Copyright IBM, Corp. 2010 + * + * Authors: + * Anthony Liguori + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ + +#include "qemu-common.h" +#include "notify.h" + +void notifier_list_init(NotifierList *list) +{ + QTAILQ_INIT(&list->notifiers); +} + +void notifier_list_add(NotifierList *list, Notifier *notifier) +{ + QTAILQ_INSERT_HEAD(&list->notifiers, notifier, node); +} + +void notifier_list_remove(NotifierList *list, Notifier *notifier) +{ + QTAILQ_REMOVE(&list->notifiers, notifier, node); +} + +void notifier_list_notify(NotifierList *list) +{ + Notifier *notifier, *next; + + QTAILQ_FOREACH_SAFE(notifier, &list->notifiers, node, next) { + notifier->notify(notifier); + } +} diff -Nru qemu-kvm-0.12.5+noroms/notify.h qemu-kvm-0.14.1/notify.h --- qemu-kvm-0.12.5+noroms/notify.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/notify.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,43 @@ +/* + * Notifier lists + * + * Copyright IBM, Corp. 2010 + * + * Authors: + * Anthony Liguori + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ + +#ifndef QEMU_NOTIFY_H +#define QEMU_NOTIFY_H + +#include "qemu-queue.h" + +typedef struct Notifier Notifier; + +struct Notifier +{ + void (*notify)(Notifier *notifier); + QTAILQ_ENTRY(Notifier) node; +}; + +typedef struct NotifierList +{ + QTAILQ_HEAD(, Notifier) notifiers; +} NotifierList; + +#define NOTIFIER_LIST_INITIALIZER(head) \ + { QTAILQ_HEAD_INITIALIZER((head).notifiers) } + +void notifier_list_init(NotifierList *list); + +void notifier_list_add(NotifierList *list, Notifier *notifier); + +void notifier_list_remove(NotifierList *list, Notifier *notifier); + +void notifier_list_notify(NotifierList *list); + +#endif diff -Nru qemu-kvm-0.12.5+noroms/osdep.c qemu-kvm-0.14.1/osdep.c --- qemu-kvm-0.12.5+noroms/osdep.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/osdep.c 2011-05-11 13:29:46.000000000 +0000 @@ -28,205 +28,43 @@ #include #include #include -#ifdef CONFIG_SOLARIS -#include -#include -#endif /* Needed early for CONFIG_BSD etc. */ #include "config-host.h" -#ifdef _WIN32 -#include -#elif defined(CONFIG_BSD) -#include -#else -#include +#if defined(CONFIG_MADVISE) || defined(CONFIG_POSIX_MADVISE) +#include +#endif + +#ifdef CONFIG_SOLARIS +#include +#include +/* See MySQL bug #7156 (http://bugs.mysql.com/bug.php?id=7156) for + discussion about Solaris header problems */ +extern int madvise(caddr_t, size_t, int); #endif #include "qemu-common.h" +#include "trace.h" #include "sysemu.h" #include "qemu_socket.h" -#if !defined(_POSIX_C_SOURCE) || defined(_WIN32) || defined(__sun__) -static void *oom_check(void *ptr) -{ - if (ptr == NULL) { - abort(); - } - return ptr; -} -#endif - -#if defined(_WIN32) -void *qemu_memalign(size_t alignment, size_t size) -{ - if (!size) { - abort(); - } - return oom_check(VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE)); -} - -void *qemu_vmalloc(size_t size) -{ - /* FIXME: this is not exactly optimal solution since VirtualAlloc - has 64Kb granularity, but at least it guarantees us that the - memory is page aligned. */ - if (!size) { - abort(); - } - return oom_check(VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE)); -} - -void qemu_vfree(void *ptr) +int qemu_madvise(void *addr, size_t len, int advice) { - VirtualFree(ptr, 0, MEM_RELEASE); -} - -#else - -void *qemu_memalign(size_t alignment, size_t size) -{ -#if defined(_POSIX_C_SOURCE) && !defined(__sun__) - int ret; - void *ptr; - ret = posix_memalign(&ptr, alignment, size); - if (ret != 0) - abort(); - return ptr; -#elif defined(CONFIG_BSD) - return oom_check(valloc(size)); -#else - return oom_check(memalign(alignment, size)); -#endif -} - -/* alloc shared memory pages */ -void *qemu_vmalloc(size_t size) -{ -#ifndef __ia64__ - return qemu_memalign(getpagesize(), size); -#else - return qemu_memalign(65536, size); -#endif -} - -void qemu_vfree(void *ptr) -{ - free(ptr); -} - -#endif - -int qemu_create_pidfile(const char *filename) -{ - char buffer[128]; - int len; -#ifndef _WIN32 - int fd; - - fd = qemu_open(filename, O_RDWR | O_CREAT, 0600); - if (fd == -1) + if (advice == QEMU_MADV_INVALID) { + errno = EINVAL; return -1; - - if (lockf(fd, F_TLOCK, 0) == -1) - return -1; - - len = snprintf(buffer, sizeof(buffer), "%ld\n", (long)getpid()); - if (write(fd, buffer, len) != len) - return -1; -#else - HANDLE file; - DWORD flags; - OVERLAPPED overlap; - BOOL ret; - - /* Open for writing with no sharing. */ - file = CreateFile(filename, GENERIC_WRITE, 0, NULL, - OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - - if (file == INVALID_HANDLE_VALUE) - return -1; - - flags = LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY; - overlap.hEvent = 0; - /* Lock 1 byte. */ - ret = LockFileEx(file, flags, 0, 0, 1, &overlap); - if (ret == 0) - return -1; - - /* Write PID to file. */ - len = snprintf(buffer, sizeof(buffer), "%ld\n", (long)getpid()); - ret = WriteFileEx(file, (LPCVOID)buffer, (DWORD)len, - &overlap, NULL); - if (ret == 0) - return -1; -#endif - return 0; -} - -#ifdef _WIN32 - -/* Offset between 1/1/1601 and 1/1/1970 in 100 nanosec units */ -#define _W32_FT_OFFSET (116444736000000000ULL) - -int qemu_gettimeofday(qemu_timeval *tp) -{ - union { - unsigned long long ns100; /*time since 1 Jan 1601 in 100ns units */ - FILETIME ft; - } _now; - - if(tp) - { - GetSystemTimeAsFileTime (&_now.ft); - tp->tv_usec=(long)((_now.ns100 / 10ULL) % 1000000ULL ); - tp->tv_sec= (long)((_now.ns100 - _W32_FT_OFFSET) / 10000000ULL); } - /* Always return 0 as per Open Group Base Specifications Issue 6. - Do not set errno on error. */ - return 0; -} -#endif /* _WIN32 */ - - -#ifdef _WIN32 -void socket_set_nonblock(int fd) -{ - unsigned long opt = 1; - ioctlsocket(fd, FIONBIO, &opt); -} - -int inet_aton(const char *cp, struct in_addr *ia) -{ - uint32_t addr = inet_addr(cp); - if (addr == 0xffffffff) - return 0; - ia->s_addr = addr; - return 1; -} - -void qemu_set_cloexec(int fd) -{ -} - +#if defined(CONFIG_MADVISE) + return madvise(addr, len, advice); +#elif defined(CONFIG_POSIX_MADVISE) + return posix_madvise(addr, len, advice); #else - -void socket_set_nonblock(int fd) -{ - int f; - f = fcntl(fd, F_GETFL); - fcntl(fd, F_SETFL, f | O_NONBLOCK); -} - -void qemu_set_cloexec(int fd) -{ - int f; - f = fcntl(fd, F_GETFD); - fcntl(fd, F_SETFD, f | FD_CLOEXEC); + errno = EINVAL; + return -1; +#endif } -#endif /* * Opens a file with FD_CLOEXEC set @@ -256,29 +94,37 @@ return ret; } -#ifndef _WIN32 /* - * Creates a pipe with FD_CLOEXEC set on both file descriptors + * A variant of write(2) which handles partial write. + * + * Return the number of bytes transferred. + * Set errno if fewer than `count' bytes are written. + * + * This function don't work with non-blocking fd's. + * Any of the possibilities with non-bloking fd's is bad: + * - return a short write (then name is wrong) + * - busy wait adding (errno == EAGAIN) to the loop */ -int qemu_pipe(int pipefd[2]) +ssize_t qemu_write_full(int fd, const void *buf, size_t count) { - int ret; + ssize_t ret = 0; + ssize_t total = 0; -#ifdef CONFIG_PIPE2 - ret = pipe2(pipefd, O_CLOEXEC); - if (ret != -1 || errno != ENOSYS) { - return ret; - } -#endif - ret = pipe(pipefd); - if (ret == 0) { - qemu_set_cloexec(pipefd[0]); - qemu_set_cloexec(pipefd[1]); + while (count) { + ret = write(fd, buf, count); + if (ret < 0) { + if (errno == EINTR) + continue; + break; + } + + count -= ret; + buf += ret; + total += ret; } - return ret; + return total; } -#endif /* * Opens a socket with FD_CLOEXEC set diff -Nru qemu-kvm-0.12.5+noroms/osdep.h qemu-kvm-0.14.1/osdep.h --- qemu-kvm-0.12.5+noroms/osdep.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/osdep.h 2011-05-11 13:29:46.000000000 +0000 @@ -90,19 +90,41 @@ void *qemu_vmalloc(size_t size); void qemu_vfree(void *ptr); -int qemu_create_pidfile(const char *filename); +#define QEMU_MADV_INVALID -1 -#ifdef _WIN32 -int ffs(int i); +#if defined(CONFIG_MADVISE) -typedef struct { - long tv_sec; - long tv_usec; -} qemu_timeval; -int qemu_gettimeofday(qemu_timeval *tp); +#define QEMU_MADV_WILLNEED MADV_WILLNEED +#define QEMU_MADV_DONTNEED MADV_DONTNEED +#ifdef MADV_DONTFORK +#define QEMU_MADV_DONTFORK MADV_DONTFORK +#else +#define QEMU_MADV_DONTFORK QEMU_MADV_INVALID +#endif +#ifdef MADV_MERGEABLE +#define QEMU_MADV_MERGEABLE MADV_MERGEABLE #else -typedef struct timeval qemu_timeval; -#define qemu_gettimeofday(tp) gettimeofday(tp, NULL); -#endif /* !_WIN32 */ +#define QEMU_MADV_MERGEABLE QEMU_MADV_INVALID +#endif + +#elif defined(CONFIG_POSIX_MADVISE) + +#define QEMU_MADV_WILLNEED POSIX_MADV_WILLNEED +#define QEMU_MADV_DONTNEED POSIX_MADV_DONTNEED +#define QEMU_MADV_DONTFORK QEMU_MADV_INVALID +#define QEMU_MADV_MERGEABLE QEMU_MADV_INVALID + +#else /* no-op */ + +#define QEMU_MADV_WILLNEED QEMU_MADV_INVALID +#define QEMU_MADV_DONTNEED QEMU_MADV_INVALID +#define QEMU_MADV_DONTFORK QEMU_MADV_INVALID +#define QEMU_MADV_MERGEABLE QEMU_MADV_INVALID + +#endif + +int qemu_madvise(void *addr, size_t len, int advice); + +int qemu_create_pidfile(const char *filename); #endif diff -Nru qemu-kvm-0.12.5+noroms/oslib-posix.c qemu-kvm-0.14.1/oslib-posix.c --- qemu-kvm-0.12.5+noroms/oslib-posix.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/oslib-posix.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,161 @@ +/* + * os-posix-lib.c + * + * Copyright (c) 2003-2008 Fabrice Bellard + * Copyright (c) 2010 Red Hat, Inc. + * + * QEMU library functions on POSIX which are shared between QEMU and + * the QEMU tools. + * + * 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 "config-host.h" +#include "sysemu.h" +#include "trace.h" +#include "qemu_socket.h" + +void *qemu_oom_check(void *ptr) +{ + if (ptr == NULL) { + fprintf(stderr, "Failed to allocate memory: %s\n", strerror(errno)); + abort(); + } + return ptr; +} + +void *qemu_memalign(size_t alignment, size_t size) +{ + void *ptr; +#if defined(_POSIX_C_SOURCE) && !defined(__sun__) + int ret; + ret = posix_memalign(&ptr, alignment, size); + if (ret != 0) { + fprintf(stderr, "Failed to allocate %zu B: %s\n", + size, strerror(ret)); + abort(); + } +#elif defined(CONFIG_BSD) + ptr = qemu_oom_check(valloc(size)); +#else + ptr = qemu_oom_check(memalign(alignment, size)); +#endif + trace_qemu_memalign(alignment, size, ptr); + return ptr; +} + +/* alloc shared memory pages */ +void *qemu_vmalloc(size_t size) +{ +#ifndef __ia64__ + return qemu_memalign(getpagesize(), size); +#else + return qemu_memalign(65536, size); +#endif +} + +void qemu_vfree(void *ptr) +{ + trace_qemu_vfree(ptr); + free(ptr); +} + +void socket_set_nonblock(int fd) +{ + int f; + f = fcntl(fd, F_GETFL); + fcntl(fd, F_SETFL, f | O_NONBLOCK); +} + +void qemu_set_cloexec(int fd) +{ + int f; + f = fcntl(fd, F_GETFD); + fcntl(fd, F_SETFD, f | FD_CLOEXEC); +} + +/* + * Creates a pipe with FD_CLOEXEC set on both file descriptors + */ +int qemu_pipe(int pipefd[2]) +{ + int ret; + +#ifdef CONFIG_PIPE2 + ret = pipe2(pipefd, O_CLOEXEC); + if (ret != -1 || errno != ENOSYS) { + return ret; + } +#endif + ret = pipe(pipefd); + if (ret == 0) { + qemu_set_cloexec(pipefd[0]); + qemu_set_cloexec(pipefd[1]); + } + + return ret; +} + +int qemu_utimensat(int dirfd, const char *path, const struct timespec *times, + int flags) +{ + struct timeval tv[2], tv_now; + struct stat st; + int i; +#ifdef CONFIG_UTIMENSAT + int ret; + + ret = utimensat(dirfd, path, times, flags); + if (ret != -1 || errno != ENOSYS) { + return ret; + } +#endif + /* Fallback: use utimes() instead of utimensat() */ + + /* happy if special cases */ + if (times[0].tv_nsec == UTIME_OMIT && times[1].tv_nsec == UTIME_OMIT) { + return 0; + } + if (times[0].tv_nsec == UTIME_NOW && times[1].tv_nsec == UTIME_NOW) { + return utimes(path, NULL); + } + + /* prepare for hard cases */ + if (times[0].tv_nsec == UTIME_NOW || times[1].tv_nsec == UTIME_NOW) { + gettimeofday(&tv_now, NULL); + } + if (times[0].tv_nsec == UTIME_OMIT || times[1].tv_nsec == UTIME_OMIT) { + stat(path, &st); + } + + for (i = 0; i < 2; i++) { + if (times[i].tv_nsec == UTIME_NOW) { + tv[i].tv_sec = tv_now.tv_sec; + tv[i].tv_usec = tv_now.tv_usec; + } else if (times[i].tv_nsec == UTIME_OMIT) { + tv[i].tv_sec = (i == 0) ? st.st_atime : st.st_mtime; + tv[i].tv_usec = 0; + } else { + tv[i].tv_sec = times[i].tv_sec; + tv[i].tv_usec = times[i].tv_nsec / 1000; + } + } + + return utimes(path, &tv[0]); +} diff -Nru qemu-kvm-0.12.5+noroms/oslib-win32.c qemu-kvm-0.14.1/oslib-win32.c --- qemu-kvm-0.12.5+noroms/oslib-win32.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/oslib-win32.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,121 @@ +/* + * os-win32.c + * + * Copyright (c) 2003-2008 Fabrice Bellard + * Copyright (c) 2010 Red Hat, Inc. + * + * QEMU library functions for win32 which are shared between QEMU and + * the QEMU tools. + * + * 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 +#include "config-host.h" +#include "sysemu.h" +#include "trace.h" +#include "qemu_socket.h" + +void *qemu_oom_check(void *ptr) +{ + if (ptr == NULL) { + fprintf(stderr, "Failed to allocate memory: %lu\n", GetLastError()); + abort(); + } + return ptr; +} + +void *qemu_memalign(size_t alignment, size_t size) +{ + void *ptr; + + if (!size) { + abort(); + } + ptr = qemu_oom_check(VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE)); + trace_qemu_memalign(alignment, size, ptr); + return ptr; +} + +void *qemu_vmalloc(size_t size) +{ + void *ptr; + + /* FIXME: this is not exactly optimal solution since VirtualAlloc + has 64Kb granularity, but at least it guarantees us that the + memory is page aligned. */ + if (!size) { + abort(); + } + ptr = qemu_oom_check(VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE)); + trace_qemu_vmalloc(size, ptr); + return ptr; +} + +void qemu_vfree(void *ptr) +{ + trace_qemu_vfree(ptr); + VirtualFree(ptr, 0, MEM_RELEASE); +} + +void socket_set_nonblock(int fd) +{ + unsigned long opt = 1; + ioctlsocket(fd, FIONBIO, &opt); +} + +int inet_aton(const char *cp, struct in_addr *ia) +{ + uint32_t addr = inet_addr(cp); + if (addr == 0xffffffff) { + return 0; + } + ia->s_addr = addr; + return 1; +} + +void qemu_set_cloexec(int fd) +{ +} + +/* mingw32 needs ffs for compilations without optimization. */ +int ffs(int i) +{ + /* Use gcc's builtin ffs. */ + return __builtin_ffs(i); +} + +/* Offset between 1/1/1601 and 1/1/1970 in 100 nanosec units */ +#define _W32_FT_OFFSET (116444736000000000ULL) + +int qemu_gettimeofday(qemu_timeval *tp) +{ + union { + unsigned long long ns100; /*time since 1 Jan 1601 in 100ns units */ + FILETIME ft; + } _now; + + if(tp) { + GetSystemTimeAsFileTime (&_now.ft); + tp->tv_usec=(long)((_now.ns100 / 10ULL) % 1000000ULL ); + tp->tv_sec= (long)((_now.ns100 - _W32_FT_OFFSET) / 10000000ULL); + } + /* Always return 0 as per Open Group Base Specifications Issue 6. + Do not set errno on error. */ + return 0; +} diff -Nru qemu-kvm-0.12.5+noroms/os-posix.c qemu-kvm-0.14.1/os-posix.c --- qemu-kvm-0.12.5+noroms/os-posix.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/os-posix.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,384 @@ +/* + * os-posix.c + * + * Copyright (c) 2003-2008 Fabrice Bellard + * Copyright (c) 2010 Red Hat, Inc. + * + * 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 +#include +#include +#include +#include +/*needed for MAP_POPULATE before including qemu-options.h */ +#include +#include +#include + +/* Needed early for CONFIG_BSD etc. */ +#include "config-host.h" +#include "sysemu.h" +#include "net/slirp.h" +#include "qemu-options.h" + +#ifdef CONFIG_LINUX +#include +#endif + +#ifdef CONFIG_EVENTFD +#include +#endif + +static struct passwd *user_pwd; +static const char *chroot_dir; +static int daemonize; +static int fds[2]; + +void os_setup_early_signal_handling(void) +{ + struct sigaction act; + sigfillset(&act.sa_mask); + act.sa_flags = 0; + act.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &act, NULL); +} + +static void termsig_handler(int signal) +{ + qemu_system_shutdown_request(); +} + +static void sigchld_handler(int signal) +{ + waitpid(-1, NULL, WNOHANG); +} + +void os_setup_signal_handling(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); +} + +/* Find a likely location for support files using the location of the binary. + For installed binaries this will be "$bindir/../share/qemu". When + running from the build tree this will be "$bindir/../pc-bios". */ +#define SHARE_SUFFIX "/share/qemu" +#define BUILD_SUFFIX "/pc-bios" +char *os_find_datadir(const char *argv0) +{ + char *dir; + char *p = NULL; + char *res; + char buf[PATH_MAX]; + size_t max_len; + +#if defined(__linux__) + { + int len; + len = readlink("/proc/self/exe", buf, sizeof(buf) - 1); + if (len > 0) { + buf[len] = 0; + p = buf; + } + } +#elif defined(__FreeBSD__) + { + static int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1}; + size_t len = sizeof(buf) - 1; + + *buf = '\0'; + if (!sysctl(mib, ARRAY_SIZE(mib), buf, &len, NULL, 0) && + *buf) { + buf[sizeof(buf) - 1] = '\0'; + p = buf; + } + } +#endif + /* If we don't have any way of figuring out the actual executable + location then try argv[0]. */ + if (!p) { + p = realpath(argv0, buf); + if (!p) { + return NULL; + } + } + dir = dirname(p); + dir = dirname(dir); + + max_len = strlen(dir) + + MAX(strlen(SHARE_SUFFIX), strlen(BUILD_SUFFIX)) + 1; + res = qemu_mallocz(max_len); + snprintf(res, max_len, "%s%s", dir, SHARE_SUFFIX); + if (access(res, R_OK)) { + snprintf(res, max_len, "%s%s", dir, BUILD_SUFFIX); + if (access(res, R_OK)) { + qemu_free(res); + res = NULL; + } + } + + return res; +} +#undef SHARE_SUFFIX +#undef BUILD_SUFFIX + +void os_set_proc_name(const char *s) +{ +#if defined(PR_SET_NAME) + char name[16]; + if (!s) + return; + name[sizeof(name) - 1] = 0; + strncpy(name, s, sizeof(name)); + /* Could rewrite argv[0] too, but that's a bit more complicated. + This simple way is enough for `top'. */ + if (prctl(PR_SET_NAME, name)) { + perror("unable to change process name"); + exit(1); + } +#else + fprintf(stderr, "Change of process name not supported by your OS\n"); + exit(1); +#endif +} + +/* + * Parse OS specific command line options. + * return 0 if option handled, -1 otherwise + */ +void os_parse_cmd_args(int index, const char *optarg) +{ + switch (index) { +#ifdef CONFIG_SLIRP + case QEMU_OPTION_smb: + if (net_slirp_smb(optarg) < 0) + exit(1); + break; +#endif + case QEMU_OPTION_runas: + user_pwd = getpwnam(optarg); + if (!user_pwd) { + fprintf(stderr, "User \"%s\" doesn't exist\n", optarg); + exit(1); + } + break; + case QEMU_OPTION_chroot: + chroot_dir = optarg; + break; + case QEMU_OPTION_daemonize: + daemonize = 1; + break; + } + return; +} + +static void change_process_uid(void) +{ + if (user_pwd) { + if (setgid(user_pwd->pw_gid) < 0) { + fprintf(stderr, "Failed to setgid(%d)\n", user_pwd->pw_gid); + exit(1); + } + if (setuid(user_pwd->pw_uid) < 0) { + fprintf(stderr, "Failed to setuid(%d)\n", user_pwd->pw_uid); + exit(1); + } + if (setuid(0) != -1) { + fprintf(stderr, "Dropping privileges failed\n"); + exit(1); + } + } +} + +static void change_root(void) +{ + if (chroot_dir) { + if (chroot(chroot_dir) < 0) { + fprintf(stderr, "chroot failed\n"); + exit(1); + } + if (chdir("/")) { + perror("not able to chdir to /"); + exit(1); + } + } + +} + +void os_daemonize(void) +{ + if (daemonize) { + pid_t pid; + + if (pipe(fds) == -1) + exit(1); + + pid = fork(); + if (pid > 0) { + uint8_t status; + ssize_t len; + + close(fds[1]); + + again: + len = read(fds[0], &status, 1); + if (len == -1 && (errno == EINTR)) + goto again; + + if (len != 1) + exit(1); + else if (status == 1) { + fprintf(stderr, "Could not acquire pidfile: %s\n", strerror(errno)); + exit(1); + } else + exit(0); + } else if (pid < 0) + exit(1); + + close(fds[0]); + qemu_set_cloexec(fds[1]); + + setsid(); + + pid = fork(); + if (pid > 0) + exit(0); + else if (pid < 0) + exit(1); + + umask(027); + + signal(SIGTSTP, SIG_IGN); + signal(SIGTTOU, SIG_IGN); + signal(SIGTTIN, SIG_IGN); + } +} + +void os_setup_post(void) +{ + int fd = 0; + + if (daemonize) { + uint8_t status = 0; + ssize_t len; + + again1: + len = write(fds[1], &status, 1); + if (len == -1 && (errno == EINTR)) + goto again1; + + if (len != 1) + exit(1); + + if (chdir("/")) { + perror("not able to chdir to /"); + exit(1); + } + TFR(fd = qemu_open("/dev/null", O_RDWR)); + if (fd == -1) + exit(1); + } + + change_root(); + change_process_uid(); + + if (daemonize) { + dup2(fd, 0); + dup2(fd, 1); + dup2(fd, 2); + + close(fd); + } +} + +void os_pidfile_error(void) +{ + if (daemonize) { + uint8_t status = 1; + if (write(fds[1], &status, 1) != 1) { + perror("daemonize. Writing to pipe\n"); + } + } else + fprintf(stderr, "Could not acquire pid file: %s\n", strerror(errno)); +} + +void os_set_line_buffering(void) +{ + setvbuf(stdout, NULL, _IOLBF, 0); +} + +/* + * Creates an eventfd that looks like a pipe and has EFD_CLOEXEC set. + */ +int qemu_eventfd(int fds[2]) +{ +#ifdef CONFIG_EVENTFD + int ret; + + ret = eventfd(0, 0); + if (ret >= 0) { + fds[0] = ret; + qemu_set_cloexec(ret); + if ((fds[1] = dup(ret)) == -1) { + close(ret); + return -1; + } + qemu_set_cloexec(fds[1]); + return 0; + } + + if (errno != ENOSYS) { + return -1; + } +#endif + + return qemu_pipe(fds); +} + +int qemu_create_pidfile(const char *filename) +{ + char buffer[128]; + int len; + int fd; + + fd = qemu_open(filename, O_RDWR | O_CREAT, 0600); + if (fd == -1) { + return -1; + } + if (lockf(fd, F_TLOCK, 0) == -1) { + return -1; + } + len = snprintf(buffer, sizeof(buffer), "%ld\n", (long)getpid()); + if (write(fd, buffer, len) != len) { + return -1; + } + + return 0; +} diff -Nru qemu-kvm-0.12.5+noroms/os-win32.c qemu-kvm-0.14.1/os-win32.c --- qemu-kvm-0.12.5+noroms/os-win32.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/os-win32.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,266 @@ +/* + * os-win32.c + * + * Copyright (c) 2003-2008 Fabrice Bellard + * Copyright (c) 2010 Red Hat, Inc. + * + * 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 +#include +#include +#include +#include +#include +#include +#include "config-host.h" +#include "sysemu.h" +#include "qemu-options.h" + +/***********************************************************/ +/* Functions missing in mingw */ + +int setenv(const char *name, const char *value, int overwrite) +{ + int result = 0; + if (overwrite || !getenv(name)) { + size_t length = strlen(name) + strlen(value) + 2; + char *string = qemu_malloc(length); + snprintf(string, length, "%s=%s", name, value); + result = putenv(string); + } + return result; +} + +/***********************************************************/ +/* 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)); + 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; + } + } +} + +/***********************************************************/ +/* Wait objects support */ +typedef struct WaitObjects { + int num; + HANDLE events[MAXIMUM_WAIT_OBJECTS + 1]; + WaitObjectFunc *func[MAXIMUM_WAIT_OBJECTS + 1]; + void *opaque[MAXIMUM_WAIT_OBJECTS + 1]; +} WaitObjects; + +static WaitObjects wait_objects = {0}; + +int qemu_add_wait_object(HANDLE handle, WaitObjectFunc *func, void *opaque) +{ + WaitObjects *w = &wait_objects; + + if (w->num >= MAXIMUM_WAIT_OBJECTS) + return -1; + w->events[w->num] = handle; + w->func[w->num] = func; + w->opaque[w->num] = opaque; + w->num++; + return 0; +} + +void qemu_del_wait_object(HANDLE handle, WaitObjectFunc *func, void *opaque) +{ + int i, found; + WaitObjects *w = &wait_objects; + + found = 0; + for (i = 0; i < w->num; i++) { + if (w->events[i] == handle) + found = 1; + if (found) { + w->events[i] = w->events[i + 1]; + w->func[i] = w->func[i + 1]; + w->opaque[i] = w->opaque[i + 1]; + } + } + if (found) + w->num--; +} + +void os_host_main_loop_wait(int *timeout) +{ + int ret, ret2, i; + 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); + } + if (ret == 0) { + int err; + WaitObjects *w = &wait_objects; + + ret = WaitForMultipleObjects(w->num, w->events, FALSE, *timeout); + if (WAIT_OBJECT_0 + 0 <= ret && ret <= WAIT_OBJECT_0 + w->num - 1) { + if (w->func[ret - WAIT_OBJECT_0]) + w->func[ret - WAIT_OBJECT_0](w->opaque[ret - WAIT_OBJECT_0]); + + /* Check for additional signaled events */ + for(i = (ret - WAIT_OBJECT_0 + 1); i < w->num; i++) { + + /* Check if event is signaled */ + ret2 = WaitForSingleObject(w->events[i], 0); + if(ret2 == WAIT_OBJECT_0) { + if (w->func[i]) + w->func[i](w->opaque[i]); + } else if (ret2 == WAIT_TIMEOUT) { + } else { + err = GetLastError(); + fprintf(stderr, "WaitForSingleObject error %d %d\n", i, err); + } + } + } else if (ret == WAIT_TIMEOUT) { + } else { + err = GetLastError(); + fprintf(stderr, "WaitForMultipleObjects error %d %d\n", ret, err); + } + } + + *timeout = 0; +} + +static BOOL WINAPI qemu_ctrl_handler(DWORD type) +{ + exit(STATUS_CONTROL_C_EXIT); + return TRUE; +} + +void os_setup_early_signal_handling(void) +{ + /* 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; + + SetConsoleCtrlHandler(qemu_ctrl_handler, TRUE); + + 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); + } + } +} + +/* Look for support files in the same directory as the executable. */ +char *os_find_datadir(const char *argv0) +{ + char *p; + char buf[MAX_PATH]; + DWORD len; + + len = GetModuleFileName(NULL, buf, sizeof(buf) - 1); + if (len == 0) { + return NULL; + } + + buf[len] = 0; + p = buf + len - 1; + while (p != buf && *p != '\\') + p--; + *p = 0; + if (access(buf, R_OK) == 0) { + return qemu_strdup(buf); + } + return NULL; +} + +void os_set_line_buffering(void) +{ + setbuf(stdout, NULL); + setbuf(stderr, NULL); +} + +/* + * Parse OS specific command line options. + * return 0 if option handled, -1 otherwise + */ +void os_parse_cmd_args(int index, const char *optarg) +{ + return; +} + +void os_pidfile_error(void) +{ + fprintf(stderr, "Could not acquire pid file: %s\n", strerror(errno)); +} + +int qemu_create_pidfile(const char *filename) +{ + char buffer[128]; + int len; + HANDLE file; + OVERLAPPED overlap; + BOOL ret; + memset(&overlap, 0, sizeof(overlap)); + + file = CreateFile(filename, GENERIC_WRITE, FILE_SHARE_READ, NULL, + OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + + if (file == INVALID_HANDLE_VALUE) { + return -1; + } + len = snprintf(buffer, sizeof(buffer), "%ld\n", (long)getpid()); + ret = WriteFileEx(file, (LPCVOID)buffer, (DWORD)len, + &overlap, NULL); + if (ret == 0) { + return -1; + } + return 0; +} diff -Nru qemu-kvm-0.12.5+noroms/path.c qemu-kvm-0.14.1/path.c --- qemu-kvm-0.12.5+noroms/path.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/path.c 2011-05-11 13:29:46.000000000 +0000 @@ -46,7 +46,10 @@ { struct pathelem *new = malloc(sizeof(*new)); new->name = strdup(name); - asprintf(&new->pathname, "%s/%s", root, name); + if (asprintf(&new->pathname, "%s/%s", root, name) == -1) { + printf("Cannot allocate memory\n"); + exit(1); + } new->num_entries = 0; return new; } Binary files /tmp/TI7SNDltVQ/qemu-kvm-0.12.5+noroms/pc-bios/bios.bin and /tmp/j1a6VI3o1Z/qemu-kvm-0.14.1/pc-bios/bios.bin differ Binary files /tmp/TI7SNDltVQ/qemu-kvm-0.12.5+noroms/pc-bios/gpxe-eepro100-80861209.rom and /tmp/j1a6VI3o1Z/qemu-kvm-0.14.1/pc-bios/gpxe-eepro100-80861209.rom differ diff -Nru qemu-kvm-0.12.5+noroms/pc-bios/keymaps/bepo qemu-kvm-0.14.1/pc-bios/keymaps/bepo --- qemu-kvm-0.12.5+noroms/pc-bios/keymaps/bepo 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/pc-bios/keymaps/bepo 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,333 @@ +include common + +# Bépo : Improved ergonomic french keymap using Dvorak method. +# Built by community on 'Dvorak Fr / Bépo' : +# see http://www.clavier-dvorak.org/wiki/ to join and help. +# +# Bépo layout (1.0rc2 version) for a pc105 keyboard (french) : +# ┌────┠+# │ S A│ S = Shift, A = AltGr + Shift +# │ s a│ s = normal, a = AltGr +# └────┘ +# +# ┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┲â”â”â”â”â”â”â”â”â”┓ +# │ # ¶ │ 1 „ │ 2 “ │ 3 †│ 4 ≤ │ 5 ≥ │ 6 │ 7 ¬ │ 8 ¼ │ 9 ½ │ 0 ¾ │ ° ′ │ ` ″ ┃ ⌫ Retour┃ +# │ $ – │ " — │ « < │ » > │ ( [ │ ) ] │ @ ^ │ + ± │ - − │ / ÷ │ * × │ = ≠ │ % ‰ ┃ arrière┃ +# ┢â”â”â”â”â”â”·â”┱───┴─┬───┴─┬───┴─┬───┴─┬───┴─┬───┴─┬───┴─┬───┴─┬───┴─┬───┴─┬───┴─┬───┺â”┳â”â”â”â”â”â”â”┫ +# ┃ ┃ B ¦ │ É Ë â”‚ P § │ O Å’ │ È ` │ ! │ V │ D à │ L │ J IJ │ Z Æ â”‚ W ┃Entrée ┃ +# ┃Tab ↹ ┃ b | │ é ËŠ │ p & │ o Å“ │ è ` │ ˆ ¡ │ v ˇ │ d ð │ l / │ j ij │ z É™ │ w ̆ ┃ ⎠┃ +# ┣â”â”â”â”â”â”â”┻┱────┴┬────┴┬────┴┬────┴┬────┴┬────┴┬────┴┬────┴┬────┴┬────┴┬────┴┬────┺┓ ┃ +# ┃ ┃ A Æ │ U Ù │ I Ë™ │ E ¤ │ ; Ì› │ C Å¿ │ T Þ │ S ẞ │ R â„¢ │ N │ M º │ Ç , ┃ ┃ +# ┃Maj ⇬ ┃ a æ │ u ù │ i ̈ │ e € │ , ’ │ c © │ t þ │ s ß │ r ® │ n Ëœ │ m ¯ │ ç ¸ ┃ ┃ +# ┣â”â”â”â”â”â”â”┳┹────┬┴────┬┴────┬┴────┬┴────┬┴────┬┴────┬┴────┬┴────┬┴────┬┴────┲┷â”â”â”â”â”â”»â”â”â”â”â”â”┫ +# ┃ ┃ Ê │ À │ Y ‘ │ X ’ │ : · │ K │ ? ̉ │ Q Ì£ │ G │ H ‡ │ F ª ┃ ┃ +# ┃Shift ⇧┃ ê / │ à \ │ y { │ x } │ . … │ k ~ │ ' ¿ │ q Ëš │ g µ │ h † │ f Ë› ┃Shift ⇧ ┃ +# ┣â”â”â”â”â”â”â”â•‹â”â”â”â”â”â”·â”┳â”â”â”â”·â”â”â”┱─┴─────┴─────┴─────┴─────┴─────┴───┲â”â”·â”â”â”â”â”╈â”â”â”â”â”â”»â”┳â”â”â”â”â”â”â”┳â”â”â”â”› +# ┃ ┃ ┃ ┃ Espace inséc. Espace inséc. fin ┃ ┃ ┃ ┃ +# ┃Ctrl ┃Meta ┃Alt ┃ ⣠(Espace) _ ⣠┃AltGr ⇮┃Menu ┃Ctrl ┃ +# â”—â”â”â”â”â”â”â”â”»â”â”â”â”â”â”â”â”»â”â”â”â”â”â”â”┹───────────────────────────────────┺â”â”â”â”â”â”â”â”»â”â”â”â”â”â”â”â”»â”â”â”â”â”â”â”â”› + + +# First row +## keycode 41 = dollar numbersign U+2013 U+00b6 +dollar 0x29 +numbersign 0x29 shift +U2013 0x29 altgr +U00b6 0x29 shift altgr + +## keycode 2 = +quotedbl +one U+2014 U+201e +quotedbl 0x2 +one 0x2 shift +U2014 0x2 altgr +U201e 0x2 shift altgr + +## keycode 3 = +guillemotleft +two less U+201c +guillemotleft 0x3 +two 0x3 shift +less 0x3 altgr +U201c 0x3 shift altgr + +## keycode 4 = +guillemotright +three greater U+201d +guillemotright 0x4 +three 0x4 shift +greater 0x4 altgr +U201d 0x4 shift altgr + +## keycode 5 = +parenleft +four bracketleft U+2264 +parenleft 0x5 +four 0x5 shift +bracketleft 0x5 altgr +U2264 0x5 shift altgr + +## keycode 6 = +parenright +five bracketright U+2265 +parenright 0x6 +five 0x6 shift +bracketright 0x6 altgr +U2265 0x6 shift altgr + +## keycode 7 = +at +six asciicircum +at 0x7 +six 0x7 shift +asciicircum 0x7 altgr + +## keycode 8 = +plus +seven U+00b1 U+00ac +plus 0x8 +seven 0x8 shift +U00b1 0x8 altgr +U00ac 0x8 shift altgr + +## keycode 9 = +minus +eight U+2212 U+00bc +minus 0x9 +eight 0x9 shift +U2212 0x9 altgr +U00bc 0x9 shift altgr + +## keycode 10 = +slash +nine U+00f7 U+00bd +slash 0xa +nine 0xa shift +U00f7 0xa altgr +U00bd 0xa shift altgr + +## keycode 11 = +asterisk +zero U+00d7 U+00be +asterisk 0xb +zero 0xb shift +U00d7 0xb altgr +U00be 0xb shift altgr + +## keycode 12 = equal U+00b0 U+2260 U+2032 +equal 0xc +U00b0 0xc shift +U2260 0xc altgr +U2032 0xc shift altgr + +## keycode 13 = percent grave U+2030 U+2033 +percent 0xd +grave 0xd shift +U2030 0xd altgr +U2033 0xd shift altgr + + +# Second row + +# simplified letter definitions notation : +## keycode 16 = b +b 0x10 addupper +## keycode 18 = p +p 0x12 addupper +## keycode 19 = o +o 0x13 addupper +## keycode 22 = v +v 0x16 addupper +## keycode 23 = d +d 0x17 addupper +## keycode 24 = l +l 0x18 addupper +## keycode 25 = j +j 0x19 addupper +## keycode 26 = z +z 0x1a addupper +## keycode 27 = w +w 0x1b addupper + +# then, add specific definitions +## AltGr keycode 16 = bar +bar 0x10 altgr +## Shift AltGr keycode 16 = brokenbar +brokenbar 0x10 shift altgr + +## keycode 17 = +eacute +Eacute dead_acute +eacute 0x11 +Eacute 0x11 shift +dead_acute 0x11 altgr + +## AltGr keycode 18 = ampersand +ampersand 0x12 altgr +## Shift AltGr keycode 18 = U+00a7 +U00a7 0x12 shift altgr + +## AltGr keycode 19 = +U+0153 +U+0153 0x13 altgr +## Shift AltGr keycode 19 = +U+0152 +U+0152 0x13 shift altgr + +## keycode 20 = +egrave +Egrave dead_grave grave # no Meta ! +egrave 0x14 +Egrave 0x14 shift +dead_grave 0x14 altgr + +## keycode 21 = dead_circumflex exclam exclamdown +dead_circumflex 0x15 +exclam 0x15 shift +exclamdown 0x15 altgr + +## AltGr keycode 22 = dead_caron +dead_caron 0x16 altgr + +## AltGr keycode 23 = eth +eth 0x17 altgr +## Shift AltGr keycode 23 = ETH +ETH 0x17 shift altgr + +## AltGr keycode 25 = +U+0133 +U+0133 0x19 altgr +## Shift AltGr keycode 25 = +U+0132 +U+0132 0x19 shift altgr + +## AltGr keycode 26 = +U+0259 +U+0259 0x1a altgr +## Shift AltGr keycode 26 = +U+018f +U+018f 0x1a shift altgr + + + +# Third row + +# simplified letter definitions notation : +## keycode 30 = a +a 0x1e addupper +## keycode 31 = u +u 0x1f addupper +## keycode 32 = i +i 0x20 addupper +## keycode 33 = e +e 0x21 addupper +## keycode 35 = c +c 0x23 addupper +## keycode 36 = t +t 0x24 addupper +## keycode 37 = s +s 0x25 addupper +## keycode 38 = r +r 0x26 addupper +## keycode 39 = n +n 0x27 addupper +## keycode 40 = m +m 0x28 addupper + +# then, add specific definitions +## AltGr keycode 30 = +ae +ae 0x1e altgr +## Shift AltGr keycode 30 = +AE +AE 0x1e shift altgr + +## AltGr keycode 31 = +ugrave +ugrave 0x1f altgr +## Shift AltGr keycode 31 = +Ugrave +Ugrave 0x1f shift altgr + +## AltGr keycode 32 = dead_diaeresis +dead_diaeresis 0x20 altgr + + +## AltGr keycode 33 = U+20ac +U20ac 0x21 altgr + +## keycode 34 = comma semicolon U+2019 +U+031b +comma 0x22 +semicolon 0x22 shift +U2019 0x22 altgr +U+031b 0x22 shift altgr + +## AltGr keycode 35 = copyright +copyright 0x23 altgr +## Shift AltGr keycode 35 = U+017f +U017f 0x23 shift altgr + +## AltGr keycode 36 = +thorn +thorn 0x24 altgr +## Shift AltGr keycode 36 = +THORN +THORN 0x24 shift altgr + +## AltGr keycode 37 = +ssharp +ssharp 0x25 altgr +## Shift AltGr keycode 37 = U+1e9e +U1e9e 0x25 shift altgr + +## AltGr keycode 38 = registered +registered 0x26 altgr +## Shift AltGr keycode 38 = U+2122 +U2122 0x26 shift altgr + +## AltGr keycode 39 = dead_tilde +dead_tilde 0x27 altgr + +## Shift AltGr keycode 40 = masculine +masculine 0x28 shift altgr + +## keycode 43 = +ccedilla +Ccedilla dead_cedilla +ccedilla 0x2b +Ccedilla 0x2b shift +dead_cedilla 0x2b altgr + + +# Fourth row + +# simplified letter definitions notation : +## keycode 45 = y +y 0x2d addupper +## keycode 46 = x +x 0x2e addupper +## keycode 48 = k +k 0x30 addupper +## keycode 50 = q +q 0x32 addupper +## keycode 51 = g +g 0x33 addupper +## keycode 52 = h +h 0x34 addupper +## keycode 53 = f +f 0x35 addupper + +# then, add specific definitions +## keycode 86 = +ecircumflex +Ecircumflex slash slash +ecircumflex 0x56 +Ecircumflex 0x56 shift + +## keycode 44 = +agrave +Agrave backslash +agrave 0x2c +Agrave 0x2c shift +backslash 0x2c altgr + +## AltGr keycode 45 = braceleft +braceleft 0x2d altgr +## Shift AltGr keycode 45 = U+2018 +U2018 0x2d shift altgr + +## AltGr keycode 46 = braceright +braceright 0x2e altgr + +## keycode 47 = period colon U+2026 periodcentered +period 0x2f +colon 0x2f shift +U2026 0x2f altgr +periodcentered 0x2f shift altgr + +## AltGr keycode 48 = asciitilde +asciitilde 0x30 altgr +## Shift AltGr keycode 48 = U+2328 +U2328 0x30 shift altgr + +## keycode 49 = apostrophe question questiondown +U+0309 +apostrophe 0x31 +question 0x31 shift +questiondown 0x31 altgr +U+0309 0x31 shift altgr + +## AltGr keycode 51 = mu +mu 0x33 altgr + +## AltGr keycode 52 = U+2020 +U2020 0x34 altgr +## Shift AltGr keycode 52 = U+2021 +U2021 0x34 shift altgr + +## Shift AltGr keycode 53 = ordfeminine +ordfeminine 0x35 shift altgr + + + +## keycode 57 = space nobreakspace underscore U+202f +space 0x39 +nobreakspace 0x39 shift +underscore 0x39 altgr +U202f 0x39 shift altgr Binary files /tmp/TI7SNDltVQ/qemu-kvm-0.12.5+noroms/pc-bios/openbios-ppc and /tmp/j1a6VI3o1Z/qemu-kvm-0.14.1/pc-bios/openbios-ppc differ Binary files /tmp/TI7SNDltVQ/qemu-kvm-0.12.5+noroms/pc-bios/openbios-sparc32 and /tmp/j1a6VI3o1Z/qemu-kvm-0.14.1/pc-bios/openbios-sparc32 differ Binary files /tmp/TI7SNDltVQ/qemu-kvm-0.12.5+noroms/pc-bios/openbios-sparc64 and /tmp/j1a6VI3o1Z/qemu-kvm-0.14.1/pc-bios/openbios-sparc64 differ diff -Nru qemu-kvm-0.12.5+noroms/pc-bios/optionrom/extboot.S qemu-kvm-0.14.1/pc-bios/optionrom/extboot.S --- qemu-kvm-0.12.5+noroms/pc-bios/optionrom/extboot.S 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/pc-bios/optionrom/extboot.S 2011-05-11 13:29:46.000000000 +0000 @@ -19,6 +19,9 @@ * Authors: Anthony Liguori */ +#define OLD_INT19 (0x80 * 4) /* re-use INT 0x80 BASIC vector */ +#define OLD_INT13 (0x81 * 4) /* re-use INT 0x81 BASIC vector */ + .code16 .text .global _start @@ -37,7 +40,7 @@ /* save old int 19 */ mov (0x19*4), %eax - mov %eax, %cs:old_int19 + mov %eax, (OLD_INT19) /* install out int 19 handler */ movw $int19_handler, (0x19*4) @@ -48,6 +51,7 @@ lret int19_handler: + push %eax /* reserve space for lret */ push %eax push %bx push %cx @@ -58,47 +62,30 @@ xor %ax, %ax mov %ax, %ds - movw $0x404, %dx - inb %dx, %al - cmp $1, %al - je 1f - cmp $2, %al - je 2f - jmp 3f - -1: /* hook int13: intb(0x404) == 1 */ /* save old int 13 to int 2c */ mov (0x13*4), %eax - mov %eax, %cs:old_int13 + mov %eax, (OLD_INT13) /* install our int 13 handler */ movw $int13_handler, (0x13*4) mov %cs, (0x13*4+2) - jmp 3f -2: /* linux boot: intb(0x404) == 2 */ - cli - cld - mov $0x9000, %ax - mov %ax, %ds - mov %ax, %es - mov %ax, %fs - mov %ax, %gs - mov %ax, %ss - mov $0x8ffe, %sp - ljmp $0x9000 + 0x20, $0 - -3: /* fall through: inb(0x404) == 0 */ /* restore previous int $0x19 handler */ - mov %cs:old_int19,%eax + mov (OLD_INT19),%eax mov %eax,(0x19*4) - + + /* write old handler as return address onto stack */ + push %bp + mov %sp, %bp + mov %eax, 14(%bp) + pop %bp + pop %ds pop %dx pop %cx pop %bx pop %eax - ljmpw *%cs:old_int19 + lret #define FLAGS_CF 0x01 @@ -626,7 +613,21 @@ int13_handler: cmp $0x80, %dl je 1f - ljmpw *%cs:old_int13 + + /* write old handler as return address onto stack */ + push %eax + push %eax + push %ds + push %bp + mov %sp, %bp + xor %ax, %ax + mov %ax, %ds + mov (OLD_INT13), %eax + mov %eax, 8(%bp) + pop %bp + pop %ds + pop %eax + lret 1: cmp $0x0, %ah jne 1f @@ -686,10 +687,5 @@ int $0x18 /* boot failed */ iret -/* Variables */ -.align 4, 0 -old_int13: .long 0 -old_int19: .long 0 - .align 512, 0 _end: diff -Nru qemu-kvm-0.12.5+noroms/pc-bios/optionrom/Makefile qemu-kvm-0.14.1/pc-bios/optionrom/Makefile --- qemu-kvm-0.12.5+noroms/pc-bios/optionrom/Makefile 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/pc-bios/optionrom/Makefile 2011-05-11 13:29:46.000000000 +0000 @@ -5,7 +5,8 @@ include ../../config-host.mak include $(SRC_PATH)/rules.mak -VPATH=$(SRC_PATH)/pc-bios/optionrom +$(call set-vpath, $(SRC_PATH)/pc-bios/optionrom) + .PHONY : all clean build-all CFLAGS := -Wall -Wstrict-prototypes -Werror -fomit-frame-pointer -fno-builtin @@ -24,7 +25,7 @@ $(call quiet-command,$(OBJCOPY) -O binary -j .text $< $@," Building $(TARGET_DIR)$@") %.bin: %.raw - $(call quiet-command,$(SHELL) $(SRC_PATH)/pc-bios/optionrom/signrom.sh $< $@," Signing $(TARGET_DIR)$@") + $(call quiet-command,$(SHELL) $(SRC_PATH)/scripts/signrom.sh $< $@," Signing $(TARGET_DIR)$@") clean: rm -f *.o *.d *.raw *.img *.bin *~ diff -Nru qemu-kvm-0.12.5+noroms/pc-bios/optionrom/signrom.sh qemu-kvm-0.14.1/pc-bios/optionrom/signrom.sh --- qemu-kvm-0.12.5+noroms/pc-bios/optionrom/signrom.sh 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/pc-bios/optionrom/signrom.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,46 +0,0 @@ -#!/bin/sh - -# Option ROM Signing utility -# -# 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, see . -# -# Copyright Novell Inc, 2009 -# Authors: Alexander Graf -# -# Syntax: signrom.sh - -# did we get proper arguments? -test "$1" -a "$2" || exit 1 - -sum=0 - -# find out the file size -x=`dd if="$1" bs=1 count=1 skip=2 2>/dev/null | od -t u1 -A n` -#size=`expr $x \* 512 - 1` -size=$(( $x * 512 - 1 )) - -# now get the checksum -nums=`od -A n -t u1 -v "$1"` -for i in ${nums}; do - # add each byte's value to sum - sum=`expr $sum + $i` -done - -sum=$(( $sum % 256 )) -sum=$(( 256 - $sum )) -sum_octal=$( printf "%o" $sum ) - -# and write the output file -cp "$1" "$2" -printf "\\$sum_octal" | dd of="$2" bs=1 count=1 seek=$size conv=notrunc 2>/dev/null diff -Nru qemu-kvm-0.12.5+noroms/pc-bios/optionrom/vapic.S qemu-kvm-0.14.1/pc-bios/optionrom/vapic.S --- qemu-kvm-0.12.5+noroms/pc-bios/optionrom/vapic.S 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/pc-bios/optionrom/vapic.S 2011-05-11 13:29:46.000000000 +0000 @@ -4,6 +4,17 @@ _start: .short 0xaa55 .byte (_end - _start) / 512 + # clear vapic area: firmware load using rep insb may cause + # stale tpr/isr/irr data to corrupt the vapic area. + push %es + push %cs + pop %es + xor %ax, %ax + mov $vapic_size/2, %cx + lea vapic, %di + cld + rep stosw + pop %es mov $vapic_base, %ax out %ax, $0x7e lret @@ -312,4 +323,8 @@ vapic: . = . + vapic_size + +.byte 0 # reserve space for signature +.align 512, 0 + _end: Binary files /tmp/TI7SNDltVQ/qemu-kvm-0.12.5+noroms/pc-bios/pxe-i82559er.bin and /tmp/j1a6VI3o1Z/qemu-kvm-0.14.1/pc-bios/pxe-i82559er.bin differ diff -Nru qemu-kvm-0.12.5+noroms/pc-bios/README qemu-kvm-0.14.1/pc-bios/README --- qemu-kvm-0.12.5+noroms/pc-bios/README 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/pc-bios/README 2011-05-11 13:29:46.000000000 +0000 @@ -7,23 +7,23 @@ - The PowerPC Open Hack'Ware Open Firmware Compatible BIOS is available at http://perso.magic.fr/l_indien/OpenHackWare/index.htm. -- video.x is a PowerMac NDRV compatible driver for a VGA frame - buffer. It comes from the Mac-on-Linux project - (http://www.maconlinux.org/). - - OpenBIOS (http://www.openbios.org/) is a free (GPL v2) portable firmware implementation. The goal is to implement a 100% IEEE 1275-1994 (referred to as Open Firmware) compliant firmware. - The included images for Sparc32, Sparc64 and PowerPC (for 32 and 64 bit - PPC CPUs) are built from OpenBIOS SVN revision 569. + The included image for PowerPC (for 32 and 64 bit PPC CPUs), Sparc32 + and Sparc64 are built from OpenBIOS SVN revision 1018. - The PXE roms come from Rom-o-Matic gPXE 0.9.9 with BANNER_TIMEOUT=0 e1000 8086:100E - pcnet32 1022:2000 + eepro100 8086:1209 (also used for 8086:1229 and 8086:2449) ns8390 1050:0940 + pcnet32 1022:2000 rtl8139 10ec:8139 - eepro100 8086:1209 virtio 1af4:1000 http://rom-o-matic.net/ + +- The S390 zipl loader is an addition to the official IBM s390-tools + package. That fork is maintained in its own git repository at: + git://repo.or.cz/s390-tools.git Binary files /tmp/TI7SNDltVQ/qemu-kvm-0.12.5+noroms/pc-bios/s390-zipl.rom and /tmp/j1a6VI3o1Z/qemu-kvm-0.14.1/pc-bios/s390-zipl.rom differ Binary files /tmp/TI7SNDltVQ/qemu-kvm-0.12.5+noroms/pc-bios/vgabios.bin and /tmp/j1a6VI3o1Z/qemu-kvm-0.14.1/pc-bios/vgabios.bin differ Binary files /tmp/TI7SNDltVQ/qemu-kvm-0.12.5+noroms/pc-bios/vgabios-cirrus.bin and /tmp/j1a6VI3o1Z/qemu-kvm-0.14.1/pc-bios/vgabios-cirrus.bin differ Binary files /tmp/TI7SNDltVQ/qemu-kvm-0.12.5+noroms/pc-bios/vgabios-qxl.bin and /tmp/j1a6VI3o1Z/qemu-kvm-0.14.1/pc-bios/vgabios-qxl.bin differ Binary files /tmp/TI7SNDltVQ/qemu-kvm-0.12.5+noroms/pc-bios/vgabios-stdvga.bin and /tmp/j1a6VI3o1Z/qemu-kvm-0.14.1/pc-bios/vgabios-stdvga.bin differ Binary files /tmp/TI7SNDltVQ/qemu-kvm-0.12.5+noroms/pc-bios/vgabios-vmware.bin and /tmp/j1a6VI3o1Z/qemu-kvm-0.14.1/pc-bios/vgabios-vmware.bin differ Binary files /tmp/TI7SNDltVQ/qemu-kvm-0.12.5+noroms/pc-bios/video.x and /tmp/j1a6VI3o1Z/qemu-kvm-0.14.1/pc-bios/video.x differ diff -Nru qemu-kvm-0.12.5+noroms/pflib.c qemu-kvm-0.14.1/pflib.c --- qemu-kvm-0.12.5+noroms/pflib.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/pflib.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,213 @@ +/* + * PixelFormat conversion library. + * + * Author: Gerd Hoffmann + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ +#include "qemu-common.h" +#include "console.h" +#include "pflib.h" + +typedef struct QemuPixel QemuPixel; + +typedef void (*pf_convert)(QemuPfConv *conv, + void *dst, void *src, uint32_t cnt); +typedef void (*pf_convert_from)(PixelFormat *pf, + QemuPixel *dst, void *src, uint32_t cnt); +typedef void (*pf_convert_to)(PixelFormat *pf, + void *dst, QemuPixel *src, uint32_t cnt); + +struct QemuPfConv { + pf_convert convert; + PixelFormat src; + PixelFormat dst; + + /* for copy_generic() */ + pf_convert_from conv_from; + pf_convert_to conv_to; + QemuPixel *conv_buf; + uint32_t conv_cnt; +}; + +struct QemuPixel { + uint8_t red; + uint8_t green; + uint8_t blue; + uint8_t alpha; +}; + +/* ----------------------------------------------------------------------- */ +/* PixelFormat -> QemuPixel conversions */ + +static void conv_16_to_pixel(PixelFormat *pf, + QemuPixel *dst, void *src, uint32_t cnt) +{ + uint16_t *src16 = src; + + while (cnt > 0) { + dst->red = ((*src16 & pf->rmask) >> pf->rshift) << (8 - pf->rbits); + dst->green = ((*src16 & pf->gmask) >> pf->gshift) << (8 - pf->gbits); + dst->blue = ((*src16 & pf->bmask) >> pf->bshift) << (8 - pf->bbits); + dst->alpha = ((*src16 & pf->amask) >> pf->ashift) << (8 - pf->abits); + dst++, src16++, cnt--; + } +} + +/* assumes pf->{r,g,b,a}bits == 8 */ +static void conv_32_to_pixel_fast(PixelFormat *pf, + QemuPixel *dst, void *src, uint32_t cnt) +{ + uint32_t *src32 = src; + + while (cnt > 0) { + dst->red = (*src32 & pf->rmask) >> pf->rshift; + dst->green = (*src32 & pf->gmask) >> pf->gshift; + dst->blue = (*src32 & pf->bmask) >> pf->bshift; + dst->alpha = (*src32 & pf->amask) >> pf->ashift; + dst++, src32++, cnt--; + } +} + +static void conv_32_to_pixel_generic(PixelFormat *pf, + QemuPixel *dst, void *src, uint32_t cnt) +{ + uint32_t *src32 = src; + + while (cnt > 0) { + if (pf->rbits < 8) { + dst->red = ((*src32 & pf->rmask) >> pf->rshift) << (8 - pf->rbits); + } else { + dst->red = ((*src32 & pf->rmask) >> pf->rshift) >> (pf->rbits - 8); + } + if (pf->gbits < 8) { + dst->green = ((*src32 & pf->gmask) >> pf->gshift) << (8 - pf->gbits); + } else { + dst->green = ((*src32 & pf->gmask) >> pf->gshift) >> (pf->gbits - 8); + } + if (pf->bbits < 8) { + dst->blue = ((*src32 & pf->bmask) >> pf->bshift) << (8 - pf->bbits); + } else { + dst->blue = ((*src32 & pf->bmask) >> pf->bshift) >> (pf->bbits - 8); + } + if (pf->abits < 8) { + dst->alpha = ((*src32 & pf->amask) >> pf->ashift) << (8 - pf->abits); + } else { + dst->alpha = ((*src32 & pf->amask) >> pf->ashift) >> (pf->abits - 8); + } + dst++, src32++, cnt--; + } +} + +/* ----------------------------------------------------------------------- */ +/* QemuPixel -> PixelFormat conversions */ + +static void conv_pixel_to_16(PixelFormat *pf, + void *dst, QemuPixel *src, uint32_t cnt) +{ + uint16_t *dst16 = dst; + + while (cnt > 0) { + *dst16 = ((uint16_t)src->red >> (8 - pf->rbits)) << pf->rshift; + *dst16 |= ((uint16_t)src->green >> (8 - pf->gbits)) << pf->gshift; + *dst16 |= ((uint16_t)src->blue >> (8 - pf->bbits)) << pf->bshift; + *dst16 |= ((uint16_t)src->alpha >> (8 - pf->abits)) << pf->ashift; + dst16++, src++, cnt--; + } +} + +static void conv_pixel_to_32(PixelFormat *pf, + void *dst, QemuPixel *src, uint32_t cnt) +{ + uint32_t *dst32 = dst; + + while (cnt > 0) { + *dst32 = ((uint32_t)src->red >> (8 - pf->rbits)) << pf->rshift; + *dst32 |= ((uint32_t)src->green >> (8 - pf->gbits)) << pf->gshift; + *dst32 |= ((uint32_t)src->blue >> (8 - pf->bbits)) << pf->bshift; + *dst32 |= ((uint32_t)src->alpha >> (8 - pf->abits)) << pf->ashift; + dst32++, src++, cnt--; + } +} + +/* ----------------------------------------------------------------------- */ +/* PixelFormat -> PixelFormat conversions */ + +static void convert_copy(QemuPfConv *conv, void *dst, void *src, uint32_t cnt) +{ + uint32_t bytes = cnt * conv->src.bytes_per_pixel; + memcpy(dst, src, bytes); +} + +static void convert_generic(QemuPfConv *conv, void *dst, void *src, uint32_t cnt) +{ + if (conv->conv_cnt < cnt) { + conv->conv_cnt = cnt; + conv->conv_buf = qemu_realloc(conv->conv_buf, sizeof(QemuPixel) * conv->conv_cnt); + } + conv->conv_from(&conv->src, conv->conv_buf, src, cnt); + conv->conv_to(&conv->dst, dst, conv->conv_buf, cnt); +} + +/* ----------------------------------------------------------------------- */ +/* public interface */ + +QemuPfConv *qemu_pf_conv_get(PixelFormat *dst, PixelFormat *src) +{ + QemuPfConv *conv = qemu_mallocz(sizeof(QemuPfConv)); + + conv->src = *src; + conv->dst = *dst; + + if (memcmp(&conv->src, &conv->dst, sizeof(PixelFormat)) == 0) { + /* formats identical, can simply copy */ + conv->convert = convert_copy; + } else { + /* generic two-step conversion: src -> QemuPixel -> dst */ + switch (conv->src.bytes_per_pixel) { + case 2: + conv->conv_from = conv_16_to_pixel; + break; + case 4: + if (conv->src.rbits == 8 && conv->src.gbits == 8 && conv->src.bbits == 8) { + conv->conv_from = conv_32_to_pixel_fast; + } else { + conv->conv_from = conv_32_to_pixel_generic; + } + break; + default: + goto err; + } + switch (conv->dst.bytes_per_pixel) { + case 2: + conv->conv_to = conv_pixel_to_16; + break; + case 4: + conv->conv_to = conv_pixel_to_32; + break; + default: + goto err; + } + conv->convert = convert_generic; + } + return conv; + +err: + qemu_free(conv); + return NULL; +} + +void qemu_pf_conv_run(QemuPfConv *conv, void *dst, void *src, uint32_t cnt) +{ + conv->convert(conv, dst, src, cnt); +} + +void qemu_pf_conv_put(QemuPfConv *conv) +{ + if (conv) { + qemu_free(conv->conv_buf); + qemu_free(conv); + } +} diff -Nru qemu-kvm-0.12.5+noroms/pflib.h qemu-kvm-0.14.1/pflib.h --- qemu-kvm-0.12.5+noroms/pflib.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/pflib.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,20 @@ +#ifndef __QEMU_PFLIB_H +#define __QEMU_PFLIB_H + +/* + * PixelFormat conversion library. + * + * Author: Gerd Hoffmann + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ + +typedef struct QemuPfConv QemuPfConv; + +QemuPfConv *qemu_pf_conv_get(PixelFormat *dst, PixelFormat *src); +void qemu_pf_conv_run(QemuPfConv *conv, void *dst, void *src, uint32_t cnt); +void qemu_pf_conv_put(QemuPfConv *conv); + +#endif diff -Nru qemu-kvm-0.12.5+noroms/poison.h qemu-kvm-0.14.1/poison.h --- qemu-kvm-0.12.5+noroms/poison.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/poison.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,50 @@ +/* Poison identifiers that should not be used when building + target independent device code. */ + +#ifndef HW_POISON_H +#define HW_POISON_H +#ifdef __GNUC__ + +#pragma GCC poison TARGET_I386 +#pragma GCC poison TARGET_X86_64 +#pragma GCC poison TARGET_ALPHA +#pragma GCC poison TARGET_ARM +#pragma GCC poison TARGET_CRIS +#pragma GCC poison TARGET_M68K +#pragma GCC poison TARGET_MIPS +#pragma GCC poison TARGET_MIPS64 +#pragma GCC poison TARGET_PPC +#pragma GCC poison TARGET_PPCEMB +#pragma GCC poison TARGET_PPC64 +#pragma GCC poison TARGET_ABI32 +#pragma GCC poison TARGET_SH4 +#pragma GCC poison TARGET_SPARC +#pragma GCC poison TARGET_SPARC64 + +#pragma GCC poison TARGET_WORDS_BIGENDIAN +#pragma GCC poison BSWAP_NEEDED + +#pragma GCC poison TARGET_LONG_BITS +#pragma GCC poison TARGET_FMT_lx +#pragma GCC poison TARGET_FMT_ld + +#pragma GCC poison TARGET_PAGE_SIZE +#pragma GCC poison TARGET_PAGE_MASK +#pragma GCC poison TARGET_PAGE_BITS +#pragma GCC poison TARGET_PAGE_ALIGN + +#pragma GCC poison CPUState +#pragma GCC poison env + +#pragma GCC poison CPU_INTERRUPT_HARD +#pragma GCC poison CPU_INTERRUPT_EXITTB +#pragma GCC poison CPU_INTERRUPT_TIMER +#pragma GCC poison CPU_INTERRUPT_FIQ +#pragma GCC poison CPU_INTERRUPT_HALT +#pragma GCC poison CPU_INTERRUPT_SMI +#pragma GCC poison CPU_INTERRUPT_DEBUG +#pragma GCC poison CPU_INTERRUPT_VIRQ +#pragma GCC poison CPU_INTERRUPT_NMI + +#endif +#endif diff -Nru qemu-kvm-0.12.5+noroms/posix-aio-compat.c qemu-kvm-0.14.1/posix-aio-compat.c --- qemu-kvm-0.12.5+noroms/posix-aio-compat.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/posix-aio-compat.c 2011-05-11 13:29:46.000000000 +0000 @@ -24,7 +24,9 @@ #include "qemu-queue.h" #include "osdep.h" +#include "sysemu.h" #include "qemu-common.h" +#include "trace.h" #include "block_int.h" #include "compatfd.h" @@ -36,7 +38,7 @@ int aio_fildes; union { struct iovec *aio_iov; - void *aio_ioctl_buf; + void *aio_ioctl_buf; }; int aio_niov; size_t aio_nbytes; @@ -120,21 +122,21 @@ static ssize_t handle_aiocb_ioctl(struct qemu_paiocb *aiocb) { - int ret; + int ret; + + ret = ioctl(aiocb->aio_fildes, aiocb->aio_ioctl_cmd, aiocb->aio_ioctl_buf); + if (ret == -1) + return -errno; - ret = ioctl(aiocb->aio_fildes, aiocb->aio_ioctl_cmd, aiocb->aio_ioctl_buf); - if (ret == -1) - return -errno; - - /* - * This looks weird, but the aio code only consideres a request - * successfull if it has written the number full number of bytes. - * - * Now we overload aio_nbytes as aio_ioctl_cmd for the ioctl command, - * so in fact we return the ioctl command here to make posix_aio_read() - * happy.. - */ - return aiocb->aio_nbytes; + /* + * This looks weird, but the aio code only consideres a request + * successful if it has written the number full number of bytes. + * + * Now we overload aio_nbytes as aio_ioctl_cmd for the ioctl command, + * so in fact we return the ioctl command here to make posix_aio_read() + * happy.. + */ + return aiocb->aio_nbytes; } static ssize_t handle_aiocb_flush(struct qemu_paiocb *aiocb) @@ -250,10 +252,10 @@ * Try preadv/pwritev first and fall back to linearizing the * buffer if it's not supported. */ - if (preadv_present) { + if (preadv_present) { nbytes = handle_aiocb_rw_vector(aiocb); if (nbytes == aiocb->aio_nbytes) - return nbytes; + return nbytes; if (nbytes < 0 && nbytes != -ENOSYS) return nbytes; preadv_present = 0; @@ -270,7 +272,7 @@ * Ok, we have to do it the hard way, copy all segments into * a single aligned buffer. */ - buf = qemu_memalign(512, aiocb->aio_nbytes); + buf = qemu_blockalign(aiocb->common.bs, aiocb->aio_nbytes); if (aiocb->aio_type & QEMU_AIO_WRITE) { char *p = buf; int i; @@ -336,19 +338,19 @@ switch (aiocb->aio_type & QEMU_AIO_TYPE_MASK) { case QEMU_AIO_READ: case QEMU_AIO_WRITE: - ret = handle_aiocb_rw(aiocb); - break; + ret = handle_aiocb_rw(aiocb); + break; case QEMU_AIO_FLUSH: - ret = handle_aiocb_flush(aiocb); - break; + ret = handle_aiocb_flush(aiocb); + break; case QEMU_AIO_IOCTL: - ret = handle_aiocb_ioctl(aiocb); - break; - default: - fprintf(stderr, "invalid aio request (0x%x)\n", aiocb->aio_type); - ret = -EINVAL; - break; - } + ret = handle_aiocb_ioctl(aiocb); + break; + default: + fprintf(stderr, "invalid aio request (0x%x)\n", aiocb->aio_type); + ret = -EINVAL; + break; + } mutex_lock(&lock); aiocb->ret = ret; @@ -581,6 +583,7 @@ acb->next = posix_aio_state->first_aio; posix_aio_state->first_aio = acb; + trace_paio_submit(acb, opaque, sector_num, nb_sectors, type); qemu_paio_submit(acb); return &acb->common; } @@ -597,6 +600,7 @@ acb->aio_type = QEMU_AIO_IOCTL; acb->aio_fildes = fd; acb->ev_signo = SIGUSR2; + acb->async_context_id = get_async_context_id(); acb->aio_offset = 0; acb->aio_ioctl_buf = buf; acb->aio_ioctl_cmd = req; diff -Nru qemu-kvm-0.12.5+noroms/qbool.c qemu-kvm-0.14.1/qbool.c --- qemu-kvm-0.12.5+noroms/qbool.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/qbool.c 2011-05-11 13:29:46.000000000 +0000 @@ -1,14 +1,6 @@ /* * QBool Module * - * Copyright (C) 2009 Red Hat Inc. - * - * Authors: - * Luiz Capitulino - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * * Copyright IBM, Corp. 2009 * * Authors: diff -Nru qemu-kvm-0.12.5+noroms/qdict.c qemu-kvm-0.14.1/qdict.c --- qemu-kvm-0.12.5+noroms/qdict.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/qdict.c 2011-05-11 13:29:46.000000000 +0000 @@ -1,16 +1,17 @@ /* - * QDict data type. + * QDict Module * * Copyright (C) 2009 Red Hat Inc. * * Authors: * Luiz Capitulino * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. */ #include "qint.h" +#include "qfloat.h" #include "qdict.h" #include "qbool.h" #include "qstring.h" @@ -82,14 +83,35 @@ } /** + * qdict_entry_value(): Return qdict entry value + * + * Return weak reference. + */ +QObject *qdict_entry_value(const QDictEntry *entry) +{ + return entry->value; +} + +/** + * qdict_entry_key(): Return qdict entry key + * + * Return a *pointer* to the string, it has to be duplicated before being + * stored. + */ +const char *qdict_entry_key(const QDictEntry *entry) +{ + return entry->key; +} + +/** * qdict_find(): List lookup function */ static QDictEntry *qdict_find(const QDict *qdict, - const char *key, unsigned int hash) + const char *key, unsigned int bucket) { QDictEntry *entry; - QLIST_FOREACH(entry, &qdict->table[hash], next) + QLIST_FOREACH(entry, &qdict->table[bucket], next) if (!strcmp(entry->key, key)) return entry; @@ -109,11 +131,11 @@ */ void qdict_put_obj(QDict *qdict, const char *key, QObject *value) { - unsigned int hash; + unsigned int bucket; QDictEntry *entry; - hash = tdb_hash(key) % QDICT_HASH_SIZE; - entry = qdict_find(qdict, key, hash); + bucket = tdb_hash(key) % QDICT_BUCKET_MAX; + entry = qdict_find(qdict, key, bucket); if (entry) { /* replace key's value */ qobject_decref(entry->value); @@ -121,7 +143,7 @@ } else { /* allocate a new entry */ entry = alloc_entry(key, value); - QLIST_INSERT_HEAD(&qdict->table[hash], entry, next); + QLIST_INSERT_HEAD(&qdict->table[bucket], entry, next); qdict->size++; } } @@ -136,7 +158,7 @@ { QDictEntry *entry; - entry = qdict_find(qdict, key, tdb_hash(key) % QDICT_HASH_SIZE); + entry = qdict_find(qdict, key, tdb_hash(key) % QDICT_BUCKET_MAX); return (entry == NULL ? NULL : entry->value); } @@ -147,8 +169,8 @@ */ int qdict_haskey(const QDict *qdict, const char *key) { - unsigned int hash = tdb_hash(key) % QDICT_HASH_SIZE; - return (qdict_find(qdict, key, hash) == NULL ? 0 : 1); + unsigned int bucket = tdb_hash(key) % QDICT_BUCKET_MAX; + return (qdict_find(qdict, key, bucket) == NULL ? 0 : 1); } /** @@ -175,6 +197,29 @@ } /** + * qdict_get_double(): Get an number mapped by 'key' + * + * This function assumes that 'key' exists and it stores a + * QFloat or QInt object. + * + * Return number mapped by 'key'. + */ +double qdict_get_double(const QDict *qdict, const char *key) +{ + QObject *obj = qdict_get(qdict, key); + + assert(obj); + switch (qobject_type(obj)) { + case QTYPE_QFLOAT: + return qfloat_get_double(qobject_to_qfloat(obj)); + case QTYPE_QINT: + return qint_get_int(qobject_to_qint(obj)); + default: + abort(); + } +} + +/** * qdict_get_int(): Get an integer mapped by 'key' * * This function assumes that 'key' exists and it stores a @@ -216,6 +261,19 @@ } /** + * qdict_get_qdict(): Get the QDict mapped by 'key' + * + * This function assumes that 'key' exists and it stores a + * QDict object. + * + * Return QDict mapped by 'key'. + */ +QDict *qdict_get_qdict(const QDict *qdict, const char *key) +{ + return qobject_to_qdict(qdict_get_obj(qdict, key, QTYPE_QDICT)); +} + +/** * qdict_get_str(): Get a pointer to the stored string mapped * by 'key' * @@ -235,21 +293,39 @@ * * Return integer mapped by 'key', if it is not present in * the dictionary or if the stored object is not of QInt type - * 'err_value' will be returned. + * 'def_value' will be returned. */ int64_t qdict_get_try_int(const QDict *qdict, const char *key, - int64_t err_value) + int64_t def_value) { QObject *obj; obj = qdict_get(qdict, key); if (!obj || qobject_type(obj) != QTYPE_QINT) - return err_value; + return def_value; return qint_get_int(qobject_to_qint(obj)); } /** + * qdict_get_try_bool(): Try to get a bool mapped by 'key' + * + * Return bool mapped by 'key', if it is not present in the + * dictionary or if the stored object is not of QBool type + * 'def_value' will be returned. + */ +int qdict_get_try_bool(const QDict *qdict, const char *key, int def_value) +{ + QObject *obj; + + obj = qdict_get(qdict, key); + if (!obj || qobject_type(obj) != QTYPE_QBOOL) + return def_value; + + return qbool_get_int(qobject_to_qbool(obj)); +} + +/** * qdict_get_try_str(): Try to get a pointer to the stored string * mapped by 'key' * @@ -281,12 +357,49 @@ int i; QDictEntry *entry; - for (i = 0; i < QDICT_HASH_SIZE; i++) { + for (i = 0; i < QDICT_BUCKET_MAX; i++) { QLIST_FOREACH(entry, &qdict->table[i], next) iter(entry->key, entry->value, opaque); } } +static QDictEntry *qdict_next_entry(const QDict *qdict, int first_bucket) +{ + int i; + + for (i = first_bucket; i < QDICT_BUCKET_MAX; i++) { + if (!QLIST_EMPTY(&qdict->table[i])) { + return QLIST_FIRST(&qdict->table[i]); + } + } + + return NULL; +} + +/** + * qdict_first(): Return first qdict entry for iteration. + */ +const QDictEntry *qdict_first(const QDict *qdict) +{ + return qdict_next_entry(qdict, 0); +} + +/** + * qdict_next(): Return next qdict entry in an iteration. + */ +const QDictEntry *qdict_next(const QDict *qdict, const QDictEntry *entry) +{ + QDictEntry *ret; + + ret = QLIST_NEXT(entry, next); + if (!ret) { + unsigned int bucket = tdb_hash(entry->key) % QDICT_BUCKET_MAX; + ret = qdict_next_entry(qdict, bucket + 1); + } + + return ret; +} + /** * qentry_destroy(): Free all the memory allocated by a QDictEntry */ @@ -310,7 +423,7 @@ { QDictEntry *entry; - entry = qdict_find(qdict, key, tdb_hash(key) % QDICT_HASH_SIZE); + entry = qdict_find(qdict, key, tdb_hash(key) % QDICT_BUCKET_MAX); if (entry) { QLIST_REMOVE(entry, next); qentry_destroy(entry); @@ -329,7 +442,7 @@ assert(obj != NULL); qdict = qobject_to_qdict(obj); - for (i = 0; i < QDICT_HASH_SIZE; i++) { + for (i = 0; i < QDICT_BUCKET_MAX; i++) { QDictEntry *entry = QLIST_FIRST(&qdict->table[i]); while (entry) { QDictEntry *tmp = QLIST_NEXT(entry, next); diff -Nru qemu-kvm-0.12.5+noroms/qdict.h qemu-kvm-0.14.1/qdict.h --- qemu-kvm-0.12.5+noroms/qdict.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/qdict.h 2011-05-11 13:29:46.000000000 +0000 @@ -1,3 +1,15 @@ +/* + * QDict Module + * + * Copyright (C) 2009 Red Hat Inc. + * + * Authors: + * Luiz Capitulino + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + */ + #ifndef QDICT_H #define QDICT_H @@ -6,7 +18,7 @@ #include "qemu-queue.h" #include -#define QDICT_HASH_SIZE 512 +#define QDICT_BUCKET_MAX 512 typedef struct QDictEntry { char *key; @@ -17,11 +29,13 @@ typedef struct QDict { QObject_HEAD; size_t size; - QLIST_HEAD(,QDictEntry) table[QDICT_HASH_SIZE]; + QLIST_HEAD(,QDictEntry) table[QDICT_BUCKET_MAX]; } QDict; /* Object API */ QDict *qdict_new(void); +const char *qdict_entry_key(const QDictEntry *entry); +QObject *qdict_entry_value(const QDictEntry *entry); size_t qdict_size(const QDict *qdict); void qdict_put_obj(QDict *qdict, const char *key, QObject *value); void qdict_del(QDict *qdict, const char *key); @@ -31,18 +45,23 @@ void qdict_iter(const QDict *qdict, void (*iter)(const char *key, QObject *obj, void *opaque), void *opaque); +const QDictEntry *qdict_first(const QDict *qdict); +const QDictEntry *qdict_next(const QDict *qdict, const QDictEntry *entry); /* Helper to qdict_put_obj(), accepts any object */ #define qdict_put(qdict, key, obj) \ qdict_put_obj(qdict, key, QOBJECT(obj)) /* High level helpers */ +double qdict_get_double(const QDict *qdict, const char *key); int64_t qdict_get_int(const QDict *qdict, const char *key); int qdict_get_bool(const QDict *qdict, const char *key); QList *qdict_get_qlist(const QDict *qdict, const char *key); +QDict *qdict_get_qdict(const QDict *qdict, const char *key); const char *qdict_get_str(const QDict *qdict, const char *key); int64_t qdict_get_try_int(const QDict *qdict, const char *key, - int64_t err_value); + int64_t def_value); +int qdict_get_try_bool(const QDict *qdict, const char *key, int def_value); const char *qdict_get_try_str(const QDict *qdict, const char *key); #endif /* QDICT_H */ Binary files /tmp/TI7SNDltVQ/qemu-kvm-0.12.5+noroms/qemu/pc-bios/extboot.bin and /tmp/j1a6VI3o1Z/qemu-kvm-0.14.1/qemu/pc-bios/extboot.bin differ diff -Nru qemu-kvm-0.12.5+noroms/qemu-barrier.h qemu-kvm-0.14.1/qemu-barrier.h --- qemu-kvm-0.12.5+noroms/qemu-barrier.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/qemu-barrier.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,10 @@ +#ifndef __QEMU_BARRIER_H +#define __QEMU_BARRIER_H 1 + +/* FIXME: arch dependant, x86 version */ +#define smp_wmb() asm volatile("" ::: "memory") + +/* Compiler barrier */ +#define barrier() asm volatile("" ::: "memory") + +#endif diff -Nru qemu-kvm-0.12.5+noroms/qemu-binfmt-conf.sh qemu-kvm-0.14.1/qemu-binfmt-conf.sh --- qemu-kvm-0.12.5+noroms/qemu-binfmt-conf.sh 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/qemu-binfmt-conf.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,59 +0,0 @@ -#!/bin/sh -# enable automatic i386/ARM/M68K/MIPS/SPARC/PPC program execution by the kernel - -# load the binfmt_misc module -if [ ! -d /proc/sys/fs/binfmt_misc ]; then - /sbin/modprobe binfmt_misc -fi -if [ ! -f /proc/sys/fs/binfmt_misc/register ]; then - mount binfmt_misc -t binfmt_misc /proc/sys/fs/binfmt_misc -fi - -# probe cpu type -cpu=`uname -m` -case "$cpu" in - i386|i486|i586|i686|i86pc|BePC) - cpu="i386" - ;; - m68k) - cpu="m68k" - ;; - mips*) - cpu="mips" - ;; - "Power Macintosh"|ppc|ppc64) - cpu="ppc" - ;; - armv4l) - cpu="arm" - ;; -esac - -# register the interpreter for each cpu except for the native one -if [ $cpu != "i386" ] ; then - echo ':i386:M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x03\x00:\xff\xff\xff\xff\xff\xfe\xfe\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/local/bin/qemu-i386:' > /proc/sys/fs/binfmt_misc/register - echo ':i486:M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x06\x00:\xff\xff\xff\xff\xff\xfe\xfe\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/local/bin/qemu-i386:' > /proc/sys/fs/binfmt_misc/register -fi -if [ $cpu != "arm" ] ; then - echo ':arm:M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/local/bin/qemu-arm:' > /proc/sys/fs/binfmt_misc/register - echo ':armeb:M::\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:/usr/local/bin/qemu-armeb:' > /proc/sys/fs/binfmt_misc/register -fi -if [ $cpu != "sparc" ] ; then - echo ':sparc:M::\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x02:\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:/usr/local/bin/qemu-sparc:' > /proc/sys/fs/binfmt_misc/register -fi -if [ $cpu != "ppc" ] ; then - echo ':ppc:M::\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x14:\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:/usr/local/bin/qemu-ppc:' > /proc/sys/fs/binfmt_misc/register -fi -if [ $cpu != "m68k" ] ; then - echo 'Please check cpu value and header information for m68k!' - echo ':m68k:M::\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x04:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:/usr/local/bin/qemu-m68k:' > /proc/sys/fs/binfmt_misc/register -fi -if [ $cpu != "mips" ] ; then - # FIXME: We could use the other endianness on a MIPS host. - echo ':mips:M::\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:/usr/local/bin/qemu-mips:' > /proc/sys/fs/binfmt_misc/register - echo ':mipsel:M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/local/bin/qemu-mipsel:' > /proc/sys/fs/binfmt_misc/register - echo ':mipsn32:M::\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:/usr/local/bin/qemu-mipsn32:' > /proc/sys/fs/binfmt_misc/register - echo ':mipsn32el:M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/local/bin/qemu-mipsn32el:' > /proc/sys/fs/binfmt_misc/register - echo ':mips64:M::\x7fELF\x02\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:/usr/local/bin/qemu-mips64:' > /proc/sys/fs/binfmt_misc/register - echo ':mips64el:M::\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/local/bin/qemu-mips64el:' > /proc/sys/fs/binfmt_misc/register -fi diff -Nru qemu-kvm-0.12.5+noroms/qemu-char.c qemu-kvm-0.14.1/qemu-char.c --- qemu-kvm-0.12.5+noroms/qemu-char.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/qemu-char.c 2011-05-11 13:29:46.000000000 +0000 @@ -28,7 +28,6 @@ #include "sysemu.h" #include "qemu-timer.h" #include "qemu-char.h" -#include "block.h" #include "hw/usb.h" #include "hw/baum.h" #include "hw/msmouse.h" @@ -98,6 +97,7 @@ #endif #include "qemu_socket.h" +#include "ui/qemu-spice.h" #define READ_BUF_LEN 4096 @@ -109,6 +109,16 @@ static void qemu_chr_event(CharDriverState *s, int event) { + /* Keep track if the char device is open */ + switch (event) { + case CHR_EVENT_OPENED: + s->opened = 1; + break; + case CHR_EVENT_CLOSED: + s->opened = 0; + break; + } + if (!s->chr_event) return; s->chr_event(s->handler_opaque, event); @@ -182,7 +192,7 @@ } void qemu_chr_add_handlers(CharDriverState *s, - IOCanRWHandler *fd_can_read, + IOCanReadHandler *fd_can_read, IOReadHandler *fd_read, IOEventHandler *fd_event, void *opaque) @@ -193,6 +203,12 @@ s->handler_opaque = opaque; if (s->chr_update_read_handler) s->chr_update_read_handler(s); + + /* We're connecting to an already opened device, so let's make sure we + also get the open event */ + if (s->opened) { + qemu_chr_generic_open(s); + } } static int null_chr_write(CharDriverState *chr, const uint8_t *buf, int len) @@ -214,7 +230,7 @@ #define MUX_BUFFER_SIZE 32 /* Must be a power of 2. */ #define MUX_BUFFER_MASK (MUX_BUFFER_SIZE - 1) typedef struct { - IOCanRWHandler *chr_can_read[MAX_MUX]; + IOCanReadHandler *chr_can_read[MAX_MUX]; IOReadHandler *chr_read[MAX_MUX]; IOEventHandler *chr_event[MAX_MUX]; void *ext_opaque[MAX_MUX]; @@ -336,12 +352,7 @@ break; } case 's': - { - DriveInfo *dinfo; - QTAILQ_FOREACH(dinfo, &drives, next) { - bdrv_commit(dinfo->bdrv); - } - } + bdrv_commit_all(); break; case 'b': qemu_chr_event(chr, CHR_EVENT_BREAK); @@ -465,6 +476,10 @@ chr->chr_write = mux_chr_write; chr->chr_update_read_handler = mux_chr_update_read_handler; chr->chr_accept_input = mux_chr_accept_input; + + /* Muxes are always open on creation */ + qemu_chr_generic_open(chr); + return chr; } @@ -494,9 +509,10 @@ #else -static int unix_write(int fd, const uint8_t *buf, int len1) +int send_all(int fd, const void *_buf, int len1) { int ret, len; + const uint8_t *buf = _buf; len = len1; while (len > 0) { @@ -513,11 +529,6 @@ } return len1 - len; } - -int send_all(int fd, const void *buf, int len1) -{ - return unix_write(fd, buf, len1); -} #endif /* !_WIN32 */ #ifndef _WIN32 @@ -705,7 +716,7 @@ /* init terminal so that we can grab keys */ static struct termios oldtty; static int old_fd0_flags; -static int term_atexit_done; +static bool stdio_allow_signal; static void term_exit(void) { @@ -713,32 +724,26 @@ fcntl(0, F_SETFL, old_fd0_flags); } -static void term_init(QemuOpts *opts) +static void qemu_chr_set_echo_stdio(CharDriverState *chr, bool echo) { struct termios tty; - tcgetattr (0, &tty); - oldtty = tty; - old_fd0_flags = fcntl(0, F_GETFL); - - tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP + tty = oldtty; + if (!echo) { + tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP |INLCR|IGNCR|ICRNL|IXON); - tty.c_oflag |= OPOST; - tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN); + tty.c_oflag |= OPOST; + tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN); + tty.c_cflag &= ~(CSIZE|PARENB); + tty.c_cflag |= CS8; + tty.c_cc[VMIN] = 1; + tty.c_cc[VTIME] = 0; + } /* if graphical mode, we allow Ctrl-C handling */ - if (!qemu_opt_get_bool(opts, "signal", display_type != DT_NOGRAPHIC)) + if (!stdio_allow_signal) tty.c_lflag &= ~ISIG; - tty.c_cflag &= ~(CSIZE|PARENB); - tty.c_cflag |= CS8; - tty.c_cc[VMIN] = 1; - tty.c_cc[VTIME] = 0; tcsetattr (0, TCSANOW, &tty); - - if (!term_atexit_done++) - atexit(term_exit); - - fcntl(0, F_SETFL, O_NONBLOCK); } static void qemu_chr_close_stdio(struct CharDriverState *chr) @@ -755,11 +760,21 @@ if (stdio_nb_clients >= STDIO_MAX_CLIENTS) return NULL; + if (stdio_nb_clients == 0) { + old_fd0_flags = fcntl(0, F_GETFL); + tcgetattr (0, &oldtty); + fcntl(0, F_SETFL, O_NONBLOCK); + atexit(term_exit); + } + chr = qemu_chr_open_fd(0, 1); chr->chr_close = qemu_chr_close_stdio; + chr->chr_set_echo = qemu_chr_set_echo_stdio; qemu_set_fd_handler2(0, stdio_read_poll, stdio_read, NULL, chr); stdio_nb_clients++; - term_init(opts); + stdio_allow_signal = qemu_opt_get_bool(opts, "signal", + display_type != DT_NOGRAPHIC); + qemu_chr_set_echo(chr, false); return chr; } @@ -1173,6 +1188,22 @@ return 0; } +static void qemu_chr_close_tty(CharDriverState *chr) +{ + FDCharDriver *s = chr->opaque; + int fd = -1; + + if (s) { + fd = s->fd_in; + } + + fd_chr_close(chr); + + if (fd >= 0) { + close(fd); + } +} + static CharDriverState *qemu_chr_open_tty(QemuOpts *opts) { const char *filename = qemu_opt_get(opts, "path"); @@ -1190,11 +1221,11 @@ return NULL; } chr->chr_ioctl = tty_serial_ioctl; - qemu_chr_generic_open(chr); + chr->chr_close = qemu_chr_close_tty; return chr; } #else /* ! __linux__ && ! __sun__ */ -static CharDriverState *qemu_chr_open_pty(void) +static CharDriverState *qemu_chr_open_pty(QemuOpts *opts) { return NULL; } @@ -1345,7 +1376,7 @@ #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) static int pp_ioctl(CharDriverState *chr, int cmd, void *arg) { - int fd = (int)chr->opaque; + int fd = (int)(long)chr->opaque; uint8_t b; switch(cmd) { @@ -1391,7 +1422,7 @@ return NULL; chr = qemu_mallocz(sizeof(CharDriverState)); - chr->opaque = (void *)fd; + chr->opaque = (void *)(long)fd; chr->chr_write = null_chr_write; chr->chr_ioctl = pp_ioctl; return chr; @@ -1954,8 +1985,9 @@ static int tcp_get_msgfd(CharDriverState *chr) { TCPCharDriver *s = chr->opaque; - - return s->msgfd; + int fd = s->msgfd; + s->msgfd = -1; + return fd; } #ifndef _WIN32 @@ -2043,13 +2075,16 @@ tcp_chr_process_IAC_bytes(chr, s, buf, &size); if (size > 0) qemu_chr_read(chr, buf, size); - if (s->msgfd != -1) { - close(s->msgfd); - s->msgfd = -1; - } } } +#ifndef _WIN32 +CharDriverState *qemu_chr_open_eventfd(int eventfd) +{ + return qemu_chr_open_fd(eventfd, eventfd); +} +#endif + static void tcp_chr_connect(void *opaque) { CharDriverState *chr = opaque; @@ -2235,6 +2270,70 @@ return NULL; } +/***********************************************************/ +/* Memory chardev */ +typedef struct { + size_t outbuf_size; + size_t outbuf_capacity; + uint8_t *outbuf; +} MemoryDriver; + +static int mem_chr_write(CharDriverState *chr, const uint8_t *buf, int len) +{ + MemoryDriver *d = chr->opaque; + + /* TODO: the QString implementation has the same code, we should + * introduce a generic way to do this in cutils.c */ + if (d->outbuf_capacity < d->outbuf_size + len) { + /* grow outbuf */ + d->outbuf_capacity += len; + d->outbuf_capacity *= 2; + d->outbuf = qemu_realloc(d->outbuf, d->outbuf_capacity); + } + + memcpy(d->outbuf + d->outbuf_size, buf, len); + d->outbuf_size += len; + + return len; +} + +void qemu_chr_init_mem(CharDriverState *chr) +{ + MemoryDriver *d; + + d = qemu_malloc(sizeof(*d)); + d->outbuf_size = 0; + d->outbuf_capacity = 4096; + d->outbuf = qemu_mallocz(d->outbuf_capacity); + + memset(chr, 0, sizeof(*chr)); + chr->opaque = d; + chr->chr_write = mem_chr_write; +} + +QString *qemu_chr_mem_to_qs(CharDriverState *chr) +{ + MemoryDriver *d = chr->opaque; + return qstring_from_substr((char *) d->outbuf, 0, d->outbuf_size - 1); +} + +/* NOTE: this driver can not be closed with qemu_chr_close()! */ +void qemu_chr_close_mem(CharDriverState *chr) +{ + MemoryDriver *d = chr->opaque; + + qemu_free(d->outbuf); + qemu_free(chr->opaque); + chr->opaque = NULL; + chr->chr_write = NULL; +} + +size_t qemu_chr_mem_osize(const CharDriverState *chr) +{ + const MemoryDriver *d = chr->opaque; + return d->outbuf_size; +} + QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename) { char host[65], port[33], width[8], height[8]; @@ -2242,7 +2341,7 @@ const char *p; QemuOpts *opts; - opts = qemu_opts_create(&qemu_chardev_opts, label, 1); + opts = qemu_opts_create(qemu_find_opts("chardev"), label, 1); if (NULL == opts) return NULL; @@ -2391,6 +2490,9 @@ || defined(__FreeBSD_kernel__) { .name = "parport", .open = qemu_chr_open_pp }, #endif +#ifdef CONFIG_SPICE + { .name = "spicevmc", .open = qemu_chr_open_spice }, +#endif }; CharDriverState *qemu_chr_open_opts(QemuOpts *opts, @@ -2404,6 +2506,11 @@ return NULL; } + if (qemu_opt_get(opts, "backend") == NULL) { + fprintf(stderr, "chardev: \"%s\" missing backend\n", + qemu_opts_id(opts)); + return NULL; + } for (i = 0; i < ARRAY_SIZE(backend_table); i++) { if (strcmp(backend_table[i].name, qemu_opt_get(opts, "backend")) == 0) break; @@ -2457,9 +2564,17 @@ if (chr && qemu_opt_get_bool(opts, "mux", 0)) { monitor_init(chr, MONITOR_USE_READLINE); } + qemu_opts_del(opts); return chr; } +void qemu_chr_set_echo(struct CharDriverState *chr, bool echo) +{ + if (chr->chr_set_echo) { + chr->chr_set_echo(chr, echo); + } +} + void qemu_chr_close(CharDriverState *chr) { QTAILQ_REMOVE(&chardevs, chr, next); @@ -2485,22 +2600,6 @@ qlist_iter(qobject_to_qlist(ret_data), qemu_chr_qlist_iter, mon); } -/** - * qemu_chr_info(): Character devices information - * - * Each device is represented by a QDict. The returned QObject is a QList - * of all devices. - * - * The QDict contains the following: - * - * - "label": device's label - * - "filename": device's file - * - * Example: - * - * [ { "label": "monitor", "filename", "stdio" }, - * { "label": "serial0", "filename": "vc" } ] - */ void qemu_chr_info(Monitor *mon, QObject **ret_data) { QList *chr_list; diff -Nru qemu-kvm-0.12.5+noroms/qemu-char.h qemu-kvm-0.14.1/qemu-char.h --- qemu-kvm-0.12.5+noroms/qemu-char.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/qemu-char.h 2011-05-11 13:29:46.000000000 +0000 @@ -6,6 +6,7 @@ #include "qemu-option.h" #include "qemu-config.h" #include "qobject.h" +#include "qstring.h" /* character device */ @@ -57,16 +58,18 @@ int (*chr_ioctl)(struct CharDriverState *s, int cmd, void *arg); int (*get_msgfd)(struct CharDriverState *s); IOEventHandler *chr_event; - IOCanRWHandler *chr_can_read; + IOCanReadHandler *chr_can_read; IOReadHandler *chr_read; void *handler_opaque; void (*chr_send_event)(struct CharDriverState *chr, int event); void (*chr_close)(struct CharDriverState *chr); void (*chr_accept_input)(struct CharDriverState *chr); + void (*chr_set_echo)(struct CharDriverState *chr, bool echo); void *opaque; QEMUBH *bh; char *label; char *filename; + int opened; QTAILQ_ENTRY(CharDriverState) next; }; @@ -74,12 +77,14 @@ CharDriverState *qemu_chr_open_opts(QemuOpts *opts, void (*init)(struct CharDriverState *s)); CharDriverState *qemu_chr_open(const char *label, const char *filename, void (*init)(struct CharDriverState *s)); +void qemu_chr_set_echo(struct CharDriverState *chr, bool echo); void qemu_chr_close(CharDriverState *chr); -void qemu_chr_printf(CharDriverState *s, const char *fmt, ...); +void qemu_chr_printf(CharDriverState *s, const char *fmt, ...) + GCC_FMT_ATTR(2, 3); int qemu_chr_write(CharDriverState *s, const uint8_t *buf, int len); void qemu_chr_send_event(CharDriverState *s, int event); void qemu_chr_add_handlers(CharDriverState *s, - IOCanRWHandler *fd_can_read, + IOCanReadHandler *fd_can_read, IOReadHandler *fd_read, IOEventHandler *fd_event, void *opaque); @@ -93,12 +98,21 @@ void qemu_chr_info(Monitor *mon, QObject **ret_data); CharDriverState *qemu_chr_find(const char *name); +/* add an eventfd to the qemu devices that are polled */ +CharDriverState *qemu_chr_open_eventfd(int eventfd); + extern int term_escape_char; +/* memory chardev */ +void qemu_chr_init_mem(CharDriverState *chr); +void qemu_chr_close_mem(CharDriverState *chr); +QString *qemu_chr_mem_to_qs(CharDriverState *chr); +size_t qemu_chr_mem_osize(const CharDriverState *chr); + /* async I/O support */ int qemu_set_fd_handler2(int fd, - IOCanRWHandler *fd_read_poll, + IOCanReadHandler *fd_read_poll, IOHandler *fd_read, IOHandler *fd_write, void *opaque); @@ -106,5 +120,4 @@ IOHandler *fd_read, IOHandler *fd_write, void *opaque); - #endif diff -Nru qemu-kvm-0.12.5+noroms/qemu-common.h qemu-kvm-0.14.1/qemu-common.h --- qemu-kvm-0.12.5+noroms/qemu-common.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/qemu-common.h 2011-05-11 13:29:46.000000000 +0000 @@ -11,15 +11,18 @@ #define QEMU_WARN_UNUSED_RESULT #endif -/* Hack around the mess dyngen-exec.h causes: We need QEMU_NORETURN in files that - cannot include the following headers without conflicts. This condition has - to be removed once dyngen is gone. */ -#ifndef __DYNGEN_EXEC_H__ +#define QEMU_BUILD_BUG_ON(x) typedef char __build_bug_on__##__LINE__[(x)?-1:1]; + +typedef struct QEMUTimer QEMUTimer; +typedef struct QEMUFile QEMUFile; +typedef struct QEMUBH QEMUBH; +typedef struct DeviceState DeviceState; /* we put basic includes here to avoid repeating them in device drivers */ #include #include #include +#include #include #include #include @@ -47,6 +50,9 @@ #if !defined(ENOTSUP) #define ENOTSUP 4096 #endif +#ifndef TIME_MAX +#define TIME_MAX LONG_MAX +#endif #ifndef CONFIG_IOVEC #define CONFIG_IOVEC @@ -62,10 +68,29 @@ #include #endif +#if defined __GNUC__ +# if (__GNUC__ < 4) || \ + defined(__GNUC_MINOR__) && (__GNUC__ == 4) && (__GNUC_MINOR__ < 4) + /* gcc versions before 4.4.x don't support gnu_printf, so use printf. */ +# define GCC_ATTR __attribute__((__unused__, format(printf, 1, 2))) +# define GCC_FMT_ATTR(n, m) __attribute__((format(printf, n, m))) +# else + /* Use gnu_printf when supported (qemu uses standard format strings). */ +# define GCC_ATTR __attribute__((__unused__, format(gnu_printf, 1, 2))) +# define GCC_FMT_ATTR(n, m) __attribute__((format(gnu_printf, n, m))) +# endif +#else +#define GCC_ATTR /**/ +#define GCC_FMT_ATTR(n, m) +#endif + +typedef int (*fprintf_function)(FILE *f, const char *fmt, ...) + GCC_FMT_ATTR(2, 3); + #ifdef _WIN32 #define fsync _commit #define lseek _lseeki64 -extern int qemu_ftruncate64(int, int64_t); +int qemu_ftruncate64(int, int64_t); #define ftruncate qemu_ftruncate64 static inline char *realpath(const char *path, char *resolved_path) @@ -94,8 +119,6 @@ #endif /* !defined(NEED_CPU_H) */ /* bottom halves */ -typedef struct QEMUBH QEMUBH; - typedef void QEMUBHFunc(void *opaque); void async_context_push(void); @@ -116,8 +139,6 @@ int qemu_bh_poll(void); void qemu_bh_update_timeout(int *timeout); -uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c); - void qemu_get_timedate(struct tm *tm, int offset); int qemu_timedate_diff(struct tm *tm); @@ -130,6 +151,22 @@ time_t mktimegm(struct tm *tm); int qemu_fls(int i); int qemu_fdatasync(int fd); +int fcntl_setfl(int fd, int flag); + +/* + * strtosz() suffixes used to specify the default treatment of an + * argument passed to strtosz() without an explicit suffix. + * These should be defined using upper case characters in the range + * A-Z, as strtosz() will use qemu_toupper() on the given argument + * prior to comparison. + */ +#define STRTOSZ_DEFSUFFIX_TB 'T' +#define STRTOSZ_DEFSUFFIX_GB 'G' +#define STRTOSZ_DEFSUFFIX_MB 'M' +#define STRTOSZ_DEFSUFFIX_KB 'K' +#define STRTOSZ_DEFSUFFIX_B 'B' +int64_t strtosz(const char *nptr, char **end); +int64_t strtosz_suffix(const char *nptr, char **end, const char default_suffix); /* path.c */ void init_paths(const char *prefix); @@ -151,6 +188,12 @@ #define qemu_isascii(c) isascii((unsigned char)(c)) #define qemu_toascii(c) toascii((unsigned char)(c)) +#ifdef _WIN32 +/* ffs() in oslib-win32.c for WIN32, strings.h for the rest of the world */ +int ffs(int i); +#endif + +void *qemu_oom_check(void *ptr); void *qemu_malloc(size_t size); void *qemu_realloc(void *ptr, size_t size); void *qemu_mallocz(size_t size); @@ -158,27 +201,26 @@ char *qemu_strdup(const char *str); char *qemu_strndup(const char *str, size_t size); -void *get_mmap_addr(unsigned long size); - - void qemu_mutex_lock_iothread(void); void qemu_mutex_unlock_iothread(void); int qemu_open(const char *name, int flags, ...); +ssize_t qemu_write_full(int fd, const void *buf, size_t count) + QEMU_WARN_UNUSED_RESULT; void qemu_set_cloexec(int fd); #ifndef _WIN32 +int qemu_eventfd(int pipefd[2]); int qemu_pipe(int pipefd[2]); #endif /* Error handling. */ -void QEMU_NORETURN hw_error(const char *fmt, ...) - __attribute__ ((__format__ (__printf__, 1, 2))); +void QEMU_NORETURN hw_error(const char *fmt, ...) GCC_FMT_ATTR(1, 2); /* IO callbacks. */ typedef void IOReadHandler(void *opaque, const uint8_t *buf, int size); -typedef int IOCanRWHandler(void *opaque); +typedef int IOCanReadHandler(void *opaque); typedef void IOHandler(void *opaque); struct ParallelIOArg { @@ -194,6 +236,7 @@ typedef struct HCIInfo HCIInfo; typedef struct AudioState AudioState; typedef struct BlockDriverState BlockDriverState; +typedef struct DriveInfo DriveInfo; typedef struct DisplayState DisplayState; typedef struct DisplayChangeListener DisplayChangeListener; typedef struct DisplaySurface DisplaySurface; @@ -205,23 +248,33 @@ typedef struct MACAddr MACAddr; typedef struct VLANState VLANState; typedef struct VLANClientState VLANClientState; -typedef struct QEMUFile QEMUFile; typedef struct i2c_bus i2c_bus; typedef struct i2c_slave i2c_slave; typedef struct SMBusDevice SMBusDevice; -typedef struct QEMUTimer QEMUTimer; typedef struct PCIHostState PCIHostState; typedef struct PCIExpressHost PCIExpressHost; typedef struct PCIBus PCIBus; typedef struct PCIDevice PCIDevice; +typedef struct PCIExpressDevice PCIExpressDevice; +typedef struct PCIBridge PCIBridge; +typedef struct PCIEAERMsg PCIEAERMsg; +typedef struct PCIEAERLog PCIEAERLog; +typedef struct PCIEAERErr PCIEAERErr; +typedef struct PCIEPort PCIEPort; +typedef struct PCIESlot PCIESlot; typedef struct SerialState SerialState; typedef struct IRQState *qemu_irq; typedef struct PCMCIACardState PCMCIACardState; typedef struct MouseTransformInfo MouseTransformInfo; typedef struct uWireSlave uWireSlave; typedef struct I2SCodec I2SCodec; -typedef struct DeviceState DeviceState; typedef struct SSIBus SSIBus; +typedef struct EventNotifier EventNotifier; +typedef struct VirtIODevice VirtIODevice; + +typedef uint64_t pcibus_t; + +void cpu_exec_init_all(unsigned long tb_size); /* CPU save/load. */ void cpu_save(QEMUFile *f, void *opaque); @@ -230,6 +283,13 @@ /* Force QEMU to stop what it's doing and service IO */ void qemu_service_io(void); +/* Force QEMU to process pending events */ +void qemu_notify_event(void); + +/* Unblock cpu */ +void qemu_cpu_kick(void *env); +int qemu_cpu_self(void *env); + /* work queue */ struct qemu_work_item { struct qemu_work_item *next; @@ -238,13 +298,6 @@ int done; }; -/* Force QEMU to process pending events */ -void qemu_notify_event(void); - -/* Unblock cpu */ -void qemu_cpu_kick(void *env); -int qemu_cpu_self(void *env); - #ifdef CONFIG_USER_ONLY #define qemu_init_vcpu(env) do { } while (0) #else @@ -261,11 +314,16 @@ void qemu_iovec_init(QEMUIOVector *qiov, int alloc_hint); void qemu_iovec_init_external(QEMUIOVector *qiov, struct iovec *iov, int niov); void qemu_iovec_add(QEMUIOVector *qiov, void *base, size_t len); +void qemu_iovec_copy(QEMUIOVector *dst, QEMUIOVector *src, uint64_t skip, + size_t size); void qemu_iovec_concat(QEMUIOVector *dst, QEMUIOVector *src, size_t size); void qemu_iovec_destroy(QEMUIOVector *qiov); void qemu_iovec_reset(QEMUIOVector *qiov); void qemu_iovec_to_buffer(QEMUIOVector *qiov, void *buf); void qemu_iovec_from_buffer(QEMUIOVector *qiov, const void *buf, size_t count); +void qemu_iovec_memset(QEMUIOVector *qiov, int c, size_t count); +void qemu_iovec_memset_skip(QEMUIOVector *qiov, int c, size_t count, + size_t skip); struct Monitor; typedef struct Monitor Monitor; @@ -281,8 +339,30 @@ return ((val >> 4) * 10) + (val & 0x0f); } -#include "module.h" +/* compute with 96 bit intermediate result: (a*b)/c */ +static inline 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; +} -#endif /* dyngen-exec.h hack */ +#include "module.h" #endif diff -Nru qemu-kvm-0.12.5+noroms/qemu-config.c qemu-kvm-0.14.1/qemu-config.c --- qemu-kvm-0.12.5+noroms/qemu-config.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/qemu-config.c 2011-05-11 13:29:46.000000000 +0000 @@ -1,10 +1,11 @@ #include "qemu-common.h" +#include "qemu-error.h" #include "qemu-option.h" #include "qemu-config.h" #include "sysemu.h" #include "hw/qdev.h" -QemuOptsList qemu_drive_opts = { +static QemuOptsList qemu_drive_opts = { .name = "drive", .head = QTAILQ_HEAD_INITIALIZER(qemu_drive_opts.head), .desc = { @@ -53,7 +54,7 @@ },{ .name = "cache", .type = QEMU_OPT_STRING, - .help = "host cache usage (none, writeback, writethrough)", + .help = "host cache usage (none, writeback, writethrough, unsafe)", },{ .name = "aio", .type = QEMU_OPT_STRING, @@ -83,12 +84,13 @@ .type = QEMU_OPT_BOOL, .help = "make this a boot drive", }, - { /* end if list */ } + { /* end of list */ } }, }; -QemuOptsList qemu_chardev_opts = { +static QemuOptsList qemu_chardev_opts = { .name = "chardev", + .implied_opt_name = "backend", .head = QTAILQ_HEAD_INITIALIZER(qemu_chardev_opts.head), .desc = { { @@ -148,13 +150,62 @@ },{ .name = "signal", .type = QEMU_OPT_BOOL, + },{ + .name = "name", + .type = QEMU_OPT_STRING, + },{ + .name = "debug", + .type = QEMU_OPT_NUMBER, }, - { /* end if list */ } + { /* end of list */ } + }, +}; + +QemuOptsList qemu_fsdev_opts = { + .name = "fsdev", + .implied_opt_name = "fstype", + .head = QTAILQ_HEAD_INITIALIZER(qemu_fsdev_opts.head), + .desc = { + { + .name = "fstype", + .type = QEMU_OPT_STRING, + }, { + .name = "path", + .type = QEMU_OPT_STRING, + }, { + .name = "security_model", + .type = QEMU_OPT_STRING, + }, + { /*End of list */ } + }, +}; + +QemuOptsList qemu_virtfs_opts = { + .name = "virtfs", + .implied_opt_name = "fstype", + .head = QTAILQ_HEAD_INITIALIZER(qemu_virtfs_opts.head), + .desc = { + { + .name = "fstype", + .type = QEMU_OPT_STRING, + }, { + .name = "path", + .type = QEMU_OPT_STRING, + }, { + .name = "mount_tag", + .type = QEMU_OPT_STRING, + }, { + .name = "security_model", + .type = QEMU_OPT_STRING, + }, + + { /*End of list */ } }, }; -QemuOptsList qemu_device_opts = { +static QemuOptsList qemu_device_opts = { .name = "device", + .implied_opt_name = "driver", .head = QTAILQ_HEAD_INITIALIZER(qemu_device_opts.head), .desc = { /* @@ -162,12 +213,13 @@ * sanity checking will happen later * when setting device properties */ - { /* end if list */ } + { /* end of list */ } }, }; -QemuOptsList qemu_netdev_opts = { +static QemuOptsList qemu_netdev_opts = { .name = "netdev", + .implied_opt_name = "type", .head = QTAILQ_HEAD_INITIALIZER(qemu_netdev_opts.head), .desc = { /* @@ -178,8 +230,9 @@ }, }; -QemuOptsList qemu_net_opts = { +static QemuOptsList qemu_net_opts = { .name = "net", + .implied_opt_name = "type", .head = QTAILQ_HEAD_INITIALIZER(qemu_net_opts.head), .desc = { /* @@ -190,7 +243,7 @@ }, }; -QemuOptsList qemu_rtc_opts = { +static QemuOptsList qemu_rtc_opts = { .name = "rtc", .head = QTAILQ_HEAD_INITIALIZER(qemu_rtc_opts.head), .desc = { @@ -200,17 +253,15 @@ },{ .name = "clock", .type = QEMU_OPT_STRING, -#ifdef TARGET_I386 },{ .name = "driftfix", .type = QEMU_OPT_STRING, -#endif }, - { /* end if list */ } + { /* end of list */ } }, }; -QemuOptsList qemu_global_opts = { +static QemuOptsList qemu_global_opts = { .name = "global", .head = QTAILQ_HEAD_INITIALIZER(qemu_global_opts.head), .desc = { @@ -224,12 +275,13 @@ .name = "value", .type = QEMU_OPT_STRING, }, - { /* end if list */ } + { /* end of list */ } }, }; -QemuOptsList qemu_mon_opts = { +static QemuOptsList qemu_mon_opts = { .name = "mon", + .implied_opt_name = "chardev", .head = QTAILQ_HEAD_INITIALIZER(qemu_mon_opts.head), .desc = { { @@ -241,12 +293,169 @@ },{ .name = "default", .type = QEMU_OPT_BOOL, + },{ + .name = "pretty", + .type = QEMU_OPT_BOOL, + }, + { /* end of list */ } + }, +}; + +#ifdef CONFIG_SIMPLE_TRACE +static QemuOptsList qemu_trace_opts = { + .name = "trace", + .implied_opt_name = "trace", + .head = QTAILQ_HEAD_INITIALIZER(qemu_trace_opts.head), + .desc = { + { + .name = "file", + .type = QEMU_OPT_STRING, + }, + { /* end if list */ } + }, +}; +#endif + +static QemuOptsList qemu_cpudef_opts = { + .name = "cpudef", + .head = QTAILQ_HEAD_INITIALIZER(qemu_cpudef_opts.head), + .desc = { + { + .name = "name", + .type = QEMU_OPT_STRING, + },{ + .name = "level", + .type = QEMU_OPT_NUMBER, + },{ + .name = "vendor", + .type = QEMU_OPT_STRING, + },{ + .name = "family", + .type = QEMU_OPT_NUMBER, + },{ + .name = "model", + .type = QEMU_OPT_NUMBER, + },{ + .name = "stepping", + .type = QEMU_OPT_NUMBER, + },{ + .name = "feature_edx", /* cpuid 0000_0001.edx */ + .type = QEMU_OPT_STRING, + },{ + .name = "feature_ecx", /* cpuid 0000_0001.ecx */ + .type = QEMU_OPT_STRING, + },{ + .name = "extfeature_edx", /* cpuid 8000_0001.edx */ + .type = QEMU_OPT_STRING, + },{ + .name = "extfeature_ecx", /* cpuid 8000_0001.ecx */ + .type = QEMU_OPT_STRING, + },{ + .name = "xlevel", + .type = QEMU_OPT_NUMBER, + },{ + .name = "model_id", + .type = QEMU_OPT_STRING, + },{ + .name = "vendor_override", + .type = QEMU_OPT_NUMBER, + }, + { /* end of list */ } + }, +}; + +QemuOptsList qemu_spice_opts = { + .name = "spice", + .head = QTAILQ_HEAD_INITIALIZER(qemu_spice_opts.head), + .desc = { + { + .name = "port", + .type = QEMU_OPT_NUMBER, + },{ + .name = "tls-port", + .type = QEMU_OPT_NUMBER, + },{ + .name = "addr", + .type = QEMU_OPT_STRING, + },{ + .name = "ipv4", + .type = QEMU_OPT_BOOL, + },{ + .name = "ipv6", + .type = QEMU_OPT_BOOL, + },{ + .name = "password", + .type = QEMU_OPT_STRING, + },{ + .name = "disable-ticketing", + .type = QEMU_OPT_BOOL, + },{ + .name = "x509-dir", + .type = QEMU_OPT_STRING, + },{ + .name = "x509-key-file", + .type = QEMU_OPT_STRING, + },{ + .name = "x509-key-password", + .type = QEMU_OPT_STRING, + },{ + .name = "x509-cert-file", + .type = QEMU_OPT_STRING, + },{ + .name = "x509-cacert-file", + .type = QEMU_OPT_STRING, + },{ + .name = "x509-dh-key-file", + .type = QEMU_OPT_STRING, + },{ + .name = "tls-ciphers", + .type = QEMU_OPT_STRING, + },{ + .name = "tls-channel", + .type = QEMU_OPT_STRING, + },{ + .name = "plaintext-channel", + .type = QEMU_OPT_STRING, + },{ + .name = "image-compression", + .type = QEMU_OPT_STRING, + },{ + .name = "jpeg-wan-compression", + .type = QEMU_OPT_STRING, + },{ + .name = "zlib-glz-wan-compression", + .type = QEMU_OPT_STRING, + },{ + .name = "streaming-video", + .type = QEMU_OPT_STRING, + },{ + .name = "agent-mouse", + .type = QEMU_OPT_BOOL, + },{ + .name = "playback-compression", + .type = QEMU_OPT_BOOL, + }, + { /* end if list */ } + }, +}; + +QemuOptsList qemu_option_rom_opts = { + .name = "option-rom", + .implied_opt_name = "romfile", + .head = QTAILQ_HEAD_INITIALIZER(qemu_option_rom_opts.head), + .desc = { + { + .name = "bootindex", + .type = QEMU_OPT_NUMBER, + }, { + .name = "romfile", + .type = QEMU_OPT_STRING, }, { /* end if list */ } }, }; -static QemuOptsList *lists[] = { +static QemuOptsList *vm_config_groups[32] = { &qemu_drive_opts, &qemu_chardev_opts, &qemu_device_opts, @@ -255,10 +464,15 @@ &qemu_rtc_opts, &qemu_global_opts, &qemu_mon_opts, + &qemu_cpudef_opts, +#ifdef CONFIG_SIMPLE_TRACE + &qemu_trace_opts, +#endif + &qemu_option_rom_opts, NULL, }; -static QemuOptsList *find_list(const char *group) +static QemuOptsList *find_list(QemuOptsList **lists, const char *group) { int i; @@ -267,11 +481,32 @@ break; } if (lists[i] == NULL) { - qemu_error("there is no option group \"%s\"\n", group); + error_report("there is no option group \"%s\"", group); } return lists[i]; } +QemuOptsList *qemu_find_opts(const char *group) +{ + return find_list(vm_config_groups, group); +} + +void qemu_add_opts(QemuOptsList *list) +{ + int entries, i; + + entries = ARRAY_SIZE(vm_config_groups); + entries--; /* keep list NULL terminated */ + for (i = 0; i < entries; i++) { + if (vm_config_groups[i] == NULL) { + vm_config_groups[i] = list; + return; + } + } + fprintf(stderr, "ran out of space in vm_config_groups"); + abort(); +} + int qemu_set_option(const char *str) { char group[64], id[64], arg[64]; @@ -281,19 +516,19 @@ rc = sscanf(str, "%63[^.].%63[^.].%63[^=]%n", group, id, arg, &offset); if (rc < 3 || str[offset] != '=') { - qemu_error("can't parse: \"%s\"\n", str); + error_report("can't parse: \"%s\"", str); return -1; } - list = find_list(group); + list = qemu_find_opts(group); if (list == NULL) { return -1; } opts = qemu_opts_find(list, id); if (!opts) { - qemu_error("there is no %s \"%s\" defined\n", - list->name, id); + error_report("there is no %s \"%s\" defined", + list->name, id); return -1; } @@ -311,7 +546,7 @@ rc = sscanf(str, "%63[^.].%63[^=]%n", driver, property, &offset); if (rc < 2 || str[offset] != '=') { - qemu_error("can't parse: \"%s\"\n", str); + error_report("can't parse: \"%s\"", str); return -1; } @@ -322,23 +557,6 @@ return 0; } -static int qemu_add_one_global(QemuOpts *opts, void *opaque) -{ - GlobalProperty *g; - - g = qemu_mallocz(sizeof(*g)); - g->driver = qemu_opt_get(opts, "driver"); - g->property = qemu_opt_get(opts, "property"); - g->value = qemu_opt_get(opts, "value"); - qdev_prop_register_global(g); - return 0; -} - -void qemu_add_globals(void) -{ - qemu_opts_foreach(&qemu_global_opts, qemu_add_one_global, NULL, 0); -} - struct ConfigWriteData { QemuOptsList *list; FILE *fp; @@ -370,6 +588,7 @@ void qemu_config_write(FILE *fp) { struct ConfigWriteData data = { .fp = fp }; + QemuOptsList **lists = vm_config_groups; int i; fprintf(fp, "# qemu config file\n\n"); @@ -379,13 +598,17 @@ } } -int qemu_config_parse(FILE *fp) +int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname) { char line[1024], group[64], id[64], arg[64], value[1024]; + Location loc; QemuOptsList *list = NULL; QemuOpts *opts = NULL; + int res = -1, lno = 0; + loc_push_none(&loc); while (fgets(line, sizeof(line), fp) != NULL) { + loc_set_file(fname, ++lno); if (line[0] == '\n') { /* skip empty lines */ continue; @@ -396,35 +619,59 @@ } if (sscanf(line, "[%63s \"%63[^\"]\"]", group, id) == 2) { /* group with id */ - list = find_list(group); + list = find_list(lists, group); if (list == NULL) - return -1; + goto out; opts = qemu_opts_create(list, id, 1); continue; } if (sscanf(line, "[%63[^]]]", group) == 1) { /* group without id */ - list = find_list(group); + list = find_list(lists, group); if (list == NULL) - return -1; + goto out; opts = qemu_opts_create(list, NULL, 0); continue; } if (sscanf(line, " %63s = \"%1023[^\"]\"", arg, value) == 2) { /* arg = value */ if (opts == NULL) { - fprintf(stderr, "no group defined\n"); - return -1; + error_report("no group defined"); + goto out; } if (qemu_opt_set(opts, arg, value) != 0) { - fprintf(stderr, "failed to set \"%s\" for %s\n", - arg, group); - return -1; + goto out; } continue; } - fprintf(stderr, "parse error: %s\n", line); - return -1; + error_report("parse error"); + goto out; + } + if (ferror(fp)) { + error_report("error reading file"); + goto out; + } + res = 0; +out: + loc_pop(&loc); + return res; +} + +int qemu_read_config_file(const char *filename) +{ + FILE *f = fopen(filename, "r"); + int ret; + + if (f == NULL) { + return -errno; + } + + ret = qemu_config_parse(f, vm_config_groups, filename); + fclose(f); + + if (ret == 0) { + return 0; + } else { + return -EINVAL; } - return 0; } diff -Nru qemu-kvm-0.12.5+noroms/qemu-config.h qemu-kvm-0.14.1/qemu-config.h --- qemu-kvm-0.12.5+noroms/qemu-config.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/qemu-config.h 2011-05-11 13:29:46.000000000 +0000 @@ -1,20 +1,19 @@ #ifndef QEMU_CONFIG_H #define QEMU_CONFIG_H -extern QemuOptsList qemu_drive_opts; -extern QemuOptsList qemu_chardev_opts; -extern QemuOptsList qemu_device_opts; -extern QemuOptsList qemu_netdev_opts; -extern QemuOptsList qemu_net_opts; -extern QemuOptsList qemu_rtc_opts; -extern QemuOptsList qemu_global_opts; -extern QemuOptsList qemu_mon_opts; +extern QemuOptsList qemu_fsdev_opts; +extern QemuOptsList qemu_virtfs_opts; +extern QemuOptsList qemu_spice_opts; +QemuOptsList *qemu_find_opts(const char *group); +void qemu_add_opts(QemuOptsList *list); int qemu_set_option(const char *str); int qemu_global_option(const char *str); void qemu_add_globals(void); void qemu_config_write(FILE *fp); -int qemu_config_parse(FILE *fp); +int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname); + +int qemu_read_config_file(const char *filename); #endif /* QEMU_CONFIG_H */ diff -Nru qemu-kvm-0.12.5+noroms/qemu-doc.texi qemu-kvm-0.14.1/qemu-doc.texi --- qemu-kvm-0.12.5+noroms/qemu-doc.texi 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/qemu-doc.texi 2011-05-11 13:29:46.000000000 +0000 @@ -1,11 +1,21 @@ \input texinfo @c -*- texinfo -*- @c %**start of header @setfilename qemu-doc.info + +@documentlanguage en +@documentencoding UTF-8 + @settitle QEMU Emulator User Documentation @exampleindent 0 @paragraphindent 0 @c %**end of header +@ifinfo +@direntry +* QEMU: (qemu-doc). The QEMU Emulator User Documentation. +@end direntry +@end ifinfo + @iftex @titlepage @sp 7 @@ -27,6 +37,7 @@ * QEMU System emulator for non PC targets:: * QEMU User space emulator:: * compilation:: Compilation from the sources +* License:: * Index:: @end menu @end ifnottex @@ -48,15 +59,18 @@ QEMU has two operating modes: -@itemize @minus +@itemize +@cindex operating modes @item +@cindex system emulation Full system emulation. In this mode, QEMU emulates a full system (for example a PC), including one or several processors and various peripherals. It can be used to launch different Operating Systems without rebooting the PC or to debug system code. @item +@cindex user mode emulation User mode emulation. In this mode, QEMU can launch processes compiled for one CPU on another CPU. It can be used to launch the Wine Windows API emulator (@url{http://www.winehq.org}) or @@ -69,6 +83,8 @@ For system emulation, the following hardware targets are supported: @itemize +@cindex emulated target systems +@cindex supported target systems @item PC (x86 or x86_64 processor) @item ISA PC (old style PC without PCI bus) @item PREP (PowerPC processor) @@ -96,7 +112,10 @@ @item Petalogix Spartan 3aDSP1800 MMU ref design (MicroBlaze). @end itemize -For user emulation, x86, PowerPC, ARM, 32-bit MIPS, Sparc32/64, ColdFire(m68k), CRISv32 and MicroBlaze CPUs are supported. +@cindex supported user mode targets +For user emulation, x86 (32 and 64 bit), PowerPC (32 and 64 bit), +ARM, MIPS (32 bit only), Sparc (32 and 64 bit), +Alpha, ColdFire(m68k), CRISv32 and MicroBlaze CPUs are supported. @node Installation @chapter Installation @@ -111,24 +130,29 @@ @node install_linux @section Linux +@cindex installation (Linux) If a precompiled package is available for your distribution - you just have to install it. Otherwise, see @ref{compilation}. @node install_windows @section Windows +@cindex installation (Windows) Download the experimental binary installer at @url{http://www.free.oszoo.org/@/download.html}. +TODO (no longer available) @node install_mac @section Mac OS X Download the experimental binary installer at @url{http://www.free.oszoo.org/@/download.html}. +TODO (no longer available) @node QEMU PC System emulator @chapter QEMU PC System emulator +@cindex system emulation (PC) @menu * pcsys_introduction:: Introduction @@ -138,6 +162,7 @@ * pcsys_monitor:: QEMU Monitor * disk_images:: Disk Images * pcsys_network:: Network emulation +* pcsys_other_devs:: Other Devices * direct_linux_boot:: Direct Linux Boot * pcsys_usb:: USB emulation * vnc_security:: VNC security @@ -176,7 +201,9 @@ @item Intel 82801AA AC97 Audio compatible sound card @item -Adlib(OPL2) - Yamaha YM3812 compatible chip +Intel HD Audio Controller and HDA codec +@item +Adlib (OPL2) - Yamaha YM3812 compatible chip @item Gravis Ultrasound GF1 sound card @item @@ -196,7 +223,7 @@ QEMU uses YM3812 emulation by Tatsuyuki Satoh. -QEMU uses GUS emulation(GUSEMU32 @url{http://www.deinmeister.de/gusemu/}) +QEMU uses GUS emulation (GUSEMU32 @url{http://www.deinmeister.de/gusemu/}) by Tibor "TS" Schütz. Not that, by default, GUS shares IRQ(7) with parallel ports and so @@ -219,6 +246,7 @@ @node pcsys_quickstart @section Quick Start +@cindex quick start Download and uncompress the linux image (@file{linux.img}) and type: @@ -253,12 +281,15 @@ During the graphical emulation, you can use the following keys: @table @key @item Ctrl-Alt-f +@kindex Ctrl-Alt-f Toggle full screen @item Ctrl-Alt-u +@kindex Ctrl-Alt-u Restore the screen's un-scaled dimensions @item Ctrl-Alt-n +@kindex Ctrl-Alt-n Switch to virtual console 'n'. Standard console mappings are: @table @emph @item 1 @@ -270,30 +301,44 @@ @end table @item Ctrl-Alt +@kindex Ctrl-Alt Toggle mouse and keyboard grab. @end table +@kindex Ctrl-Up +@kindex Ctrl-Down +@kindex Ctrl-PageUp +@kindex Ctrl-PageDown In the virtual consoles, you can use @key{Ctrl-Up}, @key{Ctrl-Down}, @key{Ctrl-PageUp} and @key{Ctrl-PageDown} to move in the back log. +@kindex Ctrl-a h During emulation, if you are using the @option{-nographic} option, use @key{Ctrl-a h} to get terminal commands: @table @key @item Ctrl-a h +@kindex Ctrl-a h @item Ctrl-a ? +@kindex Ctrl-a ? Print this help @item Ctrl-a x +@kindex Ctrl-a x Exit emulator @item Ctrl-a s +@kindex Ctrl-a s Save disk data back to file (if -snapshot) @item Ctrl-a t +@kindex Ctrl-a t Toggle console timestamps @item Ctrl-a b +@kindex Ctrl-a b Send break (magic sysrq in Linux) @item Ctrl-a c +@kindex Ctrl-a c Switch between console and monitor @item Ctrl-a Ctrl-a +@kindex Ctrl-a a Send Ctrl-a @end table @c man end @@ -313,6 +358,7 @@ @node pcsys_monitor @section QEMU Monitor +@cindex QEMU monitor The QEMU monitor is used to give complex commands to the QEMU emulator. You can use it to: @@ -361,6 +407,7 @@ * host_drives:: Using host drives * disk_images_fat_images:: Virtual FAT disk images * disk_images_nbd:: NBD access +* disk_images_sheepdog:: Sheepdog disk images @end menu @node disk_images_quickstart @@ -577,6 +624,64 @@ qemu linux2.img -hdb nbd:unix:/tmp/my_socket @end example +If the nbd-server uses named exports (since NBD 2.9.18), you must use the +"exportname" option: +@example +qemu -cdrom nbd:localhost:exportname=debian-500-ppc-netinst +qemu -cdrom nbd:localhost:exportname=openSUSE-11.1-ppc-netinst +@end example + +@node disk_images_sheepdog +@subsection Sheepdog disk images + +Sheepdog is a distributed storage system for QEMU. It provides highly +available block level storage volumes that can be attached to +QEMU-based virtual machines. + +You can create a Sheepdog disk image with the command: +@example +qemu-img create sheepdog:@var{image} @var{size} +@end example +where @var{image} is the Sheepdog image name and @var{size} is its +size. + +To import the existing @var{filename} to Sheepdog, you can use a +convert command. +@example +qemu-img convert @var{filename} sheepdog:@var{image} +@end example + +You can boot from the Sheepdog disk image with the command: +@example +qemu sheepdog:@var{image} +@end example + +You can also create a snapshot of the Sheepdog image like qcow2. +@example +qemu-img snapshot -c @var{tag} sheepdog:@var{image} +@end example +where @var{tag} is a tag name of the newly created snapshot. + +To boot from the Sheepdog snapshot, specify the tag name of the +snapshot. +@example +qemu sheepdog:@var{image}:@var{tag} +@end example + +You can create a cloned image from the existing snapshot. +@example +qemu-img create -b sheepdog:@var{base}:@var{tag} sheepdog:@var{image} +@end example +where @var{base} is a image name of the source snapshot and @var{tag} +is its tag name. + +If the Sheepdog daemon doesn't run on the local host, you need to +specify one of the Sheepdog servers to connect to. +@example +qemu-img create sheepdog:@var{hostname}:@var{port}:@var{image} @var{size} +qemu sheepdog:@var{hostname}:@var{port}:@var{image} +@end example + @node pcsys_network @section Network emulation @@ -663,6 +768,50 @@ that span several QEMU instances. See @ref{sec_invocation} to have a basic example. +@node pcsys_other_devs +@section Other Devices + +@subsection Inter-VM Shared Memory device + +With KVM enabled on a Linux host, a shared memory device is available. Guests +map a POSIX shared memory region into the guest as a PCI device that enables +zero-copy communication to the application level of the guests. The basic +syntax is: + +@example +qemu -device ivshmem,size=[,shm=] +@end example + +If desired, interrupts can be sent between guest VMs accessing the same shared +memory region. Interrupt support requires using a shared memory server and +using a chardev socket to connect to it. The code for the shared memory server +is qemu.git/contrib/ivshmem-server. An example syntax when using the shared +memory server is: + +@example +qemu -device ivshmem,size=[,chardev=] + [,msi=on][,ioeventfd=on][,vectors=n][,role=peer|master] +qemu -chardev socket,path=,id= +@end example + +When using the server, the guest will be assigned a VM ID (>=0) that allows guests +using the same server to communicate via interrupts. Guests can read their +VM ID from a device register (see example code). Since receiving the shared +memory region from the server is asynchronous, there is a (small) chance the +guest may boot before the shared memory is attached. To allow an application +to ensure shared memory is attached, the VM ID register will return -1 (an +invalid VM ID) until the memory is attached. Once the shared memory is +attached, the VM ID will return the guest's valid VM ID. With these semantics, +the guest application can check to ensure the shared memory is attached to the +guest before proceeding. + +The @option{role} argument can be set to either master or peer and will affect +how the shared memory is migrated. With @option{role=master}, the guest will +copy the shared memory on migration to the destination host. With +@option{role=peer}, the guest will not be able to migrate with the device attached. +With the @option{peer} case, the device should be detached and then reattached +after migration using the PCI hotplug support. + @node direct_linux_boot @section Direct Linux Boot @@ -737,7 +886,7 @@ Serial converter. This emulates an FTDI FT232BM chip connected to host character device @var{dev}. The available character devices are the same as for the @code{-serial} option. The @code{vendorid} and @code{productid} options can be -used to override the default 0403:6001. For instance, +used to override the default 0403:6001. For instance, @example usb_add serial:productid=FA00:tcp:192.168.0.2:4444 @end example @@ -952,7 +1101,7 @@ The GNU TLS packages provides a command called @code{certtool} which can be used to generate certificates and keys in PEM format. At a minimum it -is neccessary to setup a certificate authority, and issue certificates to +is necessary to setup a certificate authority, and issue certificates to each server. If using certificates for authentication, then each client will also need to be issued a certificate. The recommendation is for the server to keep its certificates in either @code{/etc/pki/qemu} or for @@ -1095,7 +1244,7 @@ For this to work the administrator of your KDC must generate a Kerberos principal for the server, with a name of 'qemu/somehost.example.com@@EXAMPLE.COM' replacing 'somehost.example.com' with the fully qualified host name of the -machine running QEMU, and 'EXAMPLE.COM' with the Keberos Realm. +machine running QEMU, and 'EXAMPLE.COM' with the Kerberos Realm. Other configurations will be left as an exercise for the reader. It should be noted that only Digest-MD5 and GSSAPI provides a SSF layer for data @@ -1276,16 +1425,20 @@ differences are mentioned in the following sections. @menu -* QEMU PowerPC System emulator:: +* PowerPC System emulator:: * Sparc32 System emulator:: * Sparc64 System emulator:: * MIPS System emulator:: * ARM System emulator:: * ColdFire System emulator:: +* Cris System emulator:: +* Microblaze System emulator:: +* SH4 System emulator:: @end menu -@node QEMU PowerPC System emulator -@section QEMU PowerPC System emulator +@node PowerPC System emulator +@section PowerPC System emulator +@cindex system emulation (PowerPC) Use the executable @file{qemu-system-ppc} to simulate a complete PREP or PowerMac PowerPC system. @@ -1368,6 +1521,7 @@ @node Sparc32 System emulator @section Sparc32 System emulator +@cindex system emulation (Sparc32) Use the executable @file{qemu-system-sparc} to simulate the following Sun4m architecture machines: @@ -1456,7 +1610,7 @@ -prom-env 'boot-device=sd(0,2,0):d' -prom-env 'boot-args=linux single' @end example -@item -M [SS-4|SS-5|SS-10|SS-20|SS-600MP|LX|Voyager|SPARCClassic|SPARCbook|SS-2|SS-1000|SS-2000] +@item -M [SS-4|SS-5|SS-10|SS-20|SS-600MP|LX|Voyager|SPARCClassic] [|SPARCbook|SS-2|SS-1000|SS-2000] Set the emulated machine type. Default is SS-5. @@ -1466,6 +1620,7 @@ @node Sparc64 System emulator @section Sparc64 System emulator +@cindex system emulation (Sparc64) Use the executable @file{qemu-system-sparc64} to simulate a Sun4u (UltraSPARC PC-like machine), Sun4v (T1 PC-like machine), or generic @@ -1515,6 +1670,7 @@ @node MIPS System emulator @section MIPS System emulator +@cindex system emulation (MIPS) Four executables cover simulation of 32 and 64-bit MIPS systems in both endian options, @file{qemu-system-mips}, @file{qemu-system-mipsel} @@ -1610,6 +1766,7 @@ @node ARM System emulator @section ARM System emulator +@cindex system emulation (ARM) Use the executable @file{qemu-system-arm} to simulate a ARM machine. The ARM Integrator/CP board is emulated with the following @@ -1659,8 +1816,17 @@ PL181 MultiMedia Card Interface with SD card. @end itemize -The ARM RealView Emulation/Platform baseboard is emulated with the following -devices: +Several variants of the ARM RealView baseboard are emulated, +including the EB, PB-A8 and PBX-A9. Due to interactions with the +bootloader, only certain Linux kernel configurations work out +of the box on these boards. + +Kernels for the PB-A8 board should have CONFIG_REALVIEW_HIGH_PHYS_OFFSET +enabled in the kernel, and expect 512M RAM. Kernels for The PBX-A9 board +should have CONFIG_SPARSEMEM enabled, CONFIG_REALVIEW_HIGH_PHYS_OFFSET +disabled and expect 1024M RAM. + +The following devices are emulated: @itemize @minus @item @@ -1760,7 +1926,7 @@ @item Three OMAP on-chip UARTs and on-chip STI debugging console @item -A Bluetooth(R) transciever and HCI connected to an UART +A Bluetooth(R) transceiver and HCI connected to an UART @item Mentor Graphics "Inventra" dual-role USB controller embedded in a TI TUSB6010 chip - only USB host mode is supported @@ -1816,13 +1982,13 @@ @item MV88W8618 audio controller, WM8750 CODEC and mixer @item -128×64 display with brightness control +128×64 display with brightness control @item 2 buttons, 2 navigation wheels with button function @end itemize The Siemens SX1 models v1 and v2 (default) basic emulation. -The emulaton includes the following elements: +The emulation includes the following elements: @itemize @minus @item @@ -1886,6 +2052,8 @@ @node ColdFire System emulator @section ColdFire System emulator +@cindex system emulation (ColdFire) +@cindex system emulation (M68K) Use the executable @file{qemu-system-m68k} to simulate a ColdFire machine. The emulator is able to boot a uClinux kernel. @@ -1912,7 +2080,7 @@ @c man begin OPTIONS -The following options are specific to the ARM emulation: +The following options are specific to the ColdFire emulation: @table @option @@ -1926,6 +2094,24 @@ @end table +@node Cris System emulator +@section Cris System emulator +@cindex system emulation (Cris) + +TODO + +@node Microblaze System emulator +@section Microblaze System emulator +@cindex system emulation (Microblaze) + +TODO + +@node SH4 System emulator +@section SH4 System emulator +@cindex system emulation (SH4) + +TODO + @node QEMU User space emulator @chapter QEMU User space emulator @@ -1998,7 +2184,7 @@ @example qemu-i386 tests/i386/ls @end example -You can look at @file{qemu-binfmt-conf.sh} so that +You can look at @file{scripts/qemu-binfmt-conf.sh} so that QEMU is automatically launched by the Linux kernel when you try to launch x86 executables. It requires the @code{binfmt_misc} module in the Linux kernel. @@ -2044,7 +2230,7 @@ @subsection Command line options @example -usage: qemu-i386 [-h] [-d] [-L path] [-s size] [-cpu model] [-g port] [-B offset] program [arguments...] +usage: qemu-i386 [-h] [-d] [-L path] [-s size] [-cpu model] [-g port] [-B offset] [-R size] program [arguments...] @end example @table @option @@ -2056,10 +2242,20 @@ Set the x86 stack size in bytes (default=524288) @item -cpu model Select CPU model (-cpu ? for list and additional feature selection) +@item -ignore-environment +Start with an empty environment. Without this option, +the initial environment is a copy of the caller's environment. +@item -E @var{var}=@var{value} +Set environment @var{var} to @var{value}. +@item -U @var{var} +Remove @var{var} from the environment. @item -B offset Offset guest address by the specified number of bytes. This is useful when -the address region rewuired by guest applications is reserved on the host. -Ths option is currently only supported on some hosts. +the address region required by guest applications is reserved on the host. +This option is currently only supported on some hosts. +@item -R size +Pre-allocate a guest virtual address space of the given size (in bytes). +"G", "M", and "k" suffixes may be used when specifying the size. @end table Debug options: @@ -2090,16 +2286,49 @@ @node Other binaries @subsection Other binaries +@cindex user mode (Alpha) +@command{qemu-alpha} TODO. + +@cindex user mode (ARM) +@command{qemu-armeb} TODO. + +@cindex user mode (ARM) @command{qemu-arm} is also capable of running ARM "Angel" semihosted ELF binaries (as implemented by the arm-elf and arm-eabi Newlib/GDB configurations), and arm-uclinux bFLT format binaries. +@cindex user mode (ColdFire) +@cindex user mode (M68K) @command{qemu-m68k} is capable of running semihosted binaries using the BDM (m5xxx-ram-hosted.ld) or m68k-sim (sim.ld) syscall interfaces, and coldfire uClinux bFLT format binaries. The binary format is detected automatically. +@cindex user mode (Cris) +@command{qemu-cris} TODO. + +@cindex user mode (i386) +@command{qemu-i386} TODO. +@command{qemu-x86_64} TODO. + +@cindex user mode (Microblaze) +@command{qemu-microblaze} TODO. + +@cindex user mode (MIPS) +@command{qemu-mips} TODO. +@command{qemu-mipsel} TODO. + +@cindex user mode (PowerPC) +@command{qemu-ppc64abi32} TODO. +@command{qemu-ppc64} TODO. +@command{qemu-ppc} TODO. + +@cindex user mode (SH4) +@command{qemu-sh4eb} TODO. +@command{qemu-sh4} TODO. + +@cindex user mode (SPARC) @command{qemu-sparc} can execute Sparc32 binaries (Sparc32 CPU, 32 bit ABI). @command{qemu-sparc32plus} can execute Sparc32 and SPARC32PLUS binaries @@ -2243,6 +2472,13 @@ Set the library root path (default=/) @item -s size Set the stack size in bytes (default=524288) +@item -ignore-environment +Start with an empty environment. Without this option, +the initial environment is a copy of the caller's environment. +@item -E @var{var}=@var{value} +Set environment @var{var} to @var{value}. +@item -U @var{var} +Remove @var{var} from the environment. @item -bsd type Set the type of the emulated BSD Operating system. Valid values are FreeBSD, NetBSD and OpenBSD (default). @@ -2267,6 +2503,7 @@ * Windows:: * Cross compilation for Windows with Linux:: * Mac OS X:: +* Make targets:: @end menu @node Linux/Unix @@ -2304,11 +2541,14 @@ @item Download the MinGW development library of SDL 1.2.x (@file{SDL-devel-1.2.x-@/mingw32.tar.gz}) from -@url{http://www.libsdl.org}. Unpack it in a temporary place, and -unpack the archive @file{i386-mingw32msvc.tar.gz} in the MinGW tool -directory. Edit the @file{sdl-config} script so that it gives the +@url{http://www.libsdl.org}. Unpack it in a temporary place and +edit the @file{sdl-config} script so that it gives the correct SDL directory when invoked. +@item Install the MinGW version of zlib and make sure +@file{zlib.h} and @file{libz.dll.a} are in +MinGW's default header and linker search paths. + @item Extract the current version of QEMU. @item Start the MSYS shell (file @file{msys.bat}). @@ -2331,29 +2571,43 @@ Install the MinGW cross compilation tools available at @url{http://www.mingw.org/}. -@item -Install the Win32 version of SDL (@url{http://www.libsdl.org}) by -unpacking @file{i386-mingw32msvc.tar.gz}. Set up the PATH environment -variable so that @file{i386-mingw32msvc-sdl-config} can be launched by +@item Download +the MinGW development library of SDL 1.2.x +(@file{SDL-devel-1.2.x-@/mingw32.tar.gz}) from +@url{http://www.libsdl.org}. Unpack it in a temporary place and +edit the @file{sdl-config} script so that it gives the +correct SDL directory when invoked. Set up the @code{PATH} environment +variable so that @file{sdl-config} can be launched by the QEMU configuration script. +@item Install the MinGW version of zlib and make sure +@file{zlib.h} and @file{libz.dll.a} are in +MinGW's default header and linker search paths. + @item Configure QEMU for Windows cross compilation: @example -./configure --enable-mingw32 +PATH=/usr/i686-pc-mingw32/sys-root/mingw/bin:$PATH ./configure --cross-prefix='i686-pc-mingw32-' +@end example +The example assumes @file{sdl-config} is installed under @file{/usr/i686-pc-mingw32/sys-root/mingw/bin} and +MinGW cross compilation tools have names like @file{i686-pc-mingw32-gcc} and @file{i686-pc-mingw32-strip}. +We set the @code{PATH} environment variable to ensure the MinGW version of @file{sdl-config} is used and +use --cross-prefix to specify the name of the cross compiler. +You can also use --prefix to set the Win32 install path which defaults to @file{c:/Program Files/Qemu}. + +Under Fedora Linux, you can run: +@example +yum -y install mingw32-gcc mingw32-SDL mingw32-zlib @end example -If necessary, you can change the cross-prefix according to the prefix -chosen for the MinGW tools with --cross-prefix. You can also use ---prefix to set the Win32 install path. +to get a suitable cross compilation environment. @item You can install QEMU in the installation directory by typing -@file{make install}. Don't forget to copy @file{SDL.dll} in the +@code{make install}. Don't forget to copy @file{SDL.dll} and @file{zlib1.dll} into the installation directory. @end itemize -Note: Currently, Wine does not seem able to launch -QEMU for Win32. +Wine can be used to launch the resulting qemu.exe compiled for Win32. @node Mac OS X @section Mac OS X @@ -2362,8 +2616,98 @@ at the QEMU mailing list archive to have all the necessary information. +@node Make targets +@section Make targets + +@table @code + +@item make +@item make all +Make everything which is typically needed. + +@item install +TODO + +@item install-doc +TODO + +@item make clean +Remove most files which were built during make. + +@item make distclean +Remove everything which was built during make. + +@item make dvi +@item make html +@item make info +@item make pdf +Create documentation in dvi, html, info or pdf format. + +@item make cscope +TODO + +@item make defconfig +(Re-)create some build configuration files. +User made changes will be overwritten. + +@item tar +@item tarbin +TODO + +@end table + +@node License +@appendix License + +QEMU is a trademark of Fabrice Bellard. + +QEMU is released under the GNU General Public License (TODO: add link). +Parts of QEMU have specific licenses, see file LICENSE. + +TODO (refer to file LICENSE, include it, include the GPL?) + @node Index -@chapter Index +@appendix Index +@menu +* Concept Index:: +* Function Index:: +* Keystroke Index:: +* Program Index:: +* Data Type Index:: +* Variable Index:: +@end menu + +@node Concept Index +@section Concept Index +This is the main index. Should we combine all keywords in one index? TODO @printindex cp +@node Function Index +@section Function Index +This index could be used for command line options and monitor functions. +@printindex fn + +@node Keystroke Index +@section Keystroke Index + +This is a list of all keystrokes which have a special function +in system emulation. + +@printindex ky + +@node Program Index +@section Program Index +@printindex pg + +@node Data Type Index +@section Data Type Index + +This index could be used for qdev device names and options. + +@printindex tp + +@node Variable Index +@section Variable Index +@printindex vr + @bye diff -Nru qemu-kvm-0.12.5+noroms/qemu-error.c qemu-kvm-0.14.1/qemu-error.c --- qemu-kvm-0.12.5+noroms/qemu-error.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/qemu-error.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,209 @@ +/* + * Error reporting + * + * Copyright (C) 2010 Red Hat Inc. + * + * Authors: + * Markus Armbruster , + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include +#include "monitor.h" +#include "sysemu.h" + +/* + * Print to current monitor if we have one, else to stderr. + * TODO should return int, so callers can calculate width, but that + * requires surgery to monitor_vprintf(). Left for another day. + */ +void error_vprintf(const char *fmt, va_list ap) +{ + if (cur_mon) { + monitor_vprintf(cur_mon, fmt, ap); + } else { + vfprintf(stderr, fmt, ap); + } +} + +/* + * Print to current monitor if we have one, else to stderr. + * TODO just like error_vprintf() + */ +void error_printf(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + error_vprintf(fmt, ap); + va_end(ap); +} + +void error_printf_unless_qmp(const char *fmt, ...) +{ + va_list ap; + + if (!monitor_cur_is_qmp()) { + va_start(ap, fmt); + error_vprintf(fmt, ap); + va_end(ap); + } +} + +static Location std_loc = { + .kind = LOC_NONE +}; +static Location *cur_loc = &std_loc; + +/* + * Push location saved in LOC onto the location stack, return it. + * The top of that stack is the current location. + * Needs a matching loc_pop(). + */ +Location *loc_push_restore(Location *loc) +{ + assert(!loc->prev); + loc->prev = cur_loc; + cur_loc = loc; + return loc; +} + +/* + * Initialize *LOC to "nowhere", push it onto the location stack. + * The top of that stack is the current location. + * Needs a matching loc_pop(). + * Return LOC. + */ +Location *loc_push_none(Location *loc) +{ + loc->kind = LOC_NONE; + loc->prev = NULL; + return loc_push_restore(loc); +} + +/* + * Pop the location stack. + * LOC must be the current location, i.e. the top of the stack. + */ +Location *loc_pop(Location *loc) +{ + assert(cur_loc == loc && loc->prev); + cur_loc = loc->prev; + loc->prev = NULL; + return loc; +} + +/* + * Save the current location in LOC, return LOC. + */ +Location *loc_save(Location *loc) +{ + *loc = *cur_loc; + loc->prev = NULL; + return loc; +} + +/* + * Change the current location to the one saved in LOC. + */ +void loc_restore(Location *loc) +{ + Location *prev = cur_loc->prev; + assert(!loc->prev); + *cur_loc = *loc; + cur_loc->prev = prev; +} + +/* + * Change the current location to "nowhere in particular". + */ +void loc_set_none(void) +{ + cur_loc->kind = LOC_NONE; +} + +/* + * Change the current location to argument ARGV[IDX..IDX+CNT-1]. + */ +void loc_set_cmdline(char **argv, int idx, int cnt) +{ + cur_loc->kind = LOC_CMDLINE; + cur_loc->num = cnt; + cur_loc->ptr = argv + idx; +} + +/* + * Change the current location to file FNAME, line LNO. + */ +void loc_set_file(const char *fname, int lno) +{ + assert (fname || cur_loc->kind == LOC_FILE); + cur_loc->kind = LOC_FILE; + cur_loc->num = lno; + if (fname) { + cur_loc->ptr = fname; + } +} + +static const char *progname; + +/* + * Set the program name for error_print_loc(). + */ +void error_set_progname(const char *argv0) +{ + const char *p = strrchr(argv0, '/'); + progname = p ? p + 1 : argv0; +} + +/* + * Print current location to current monitor if we have one, else to stderr. + */ +void error_print_loc(void) +{ + const char *sep = ""; + int i; + const char *const *argp; + + if (!cur_mon && progname) { + fprintf(stderr, "%s:", progname); + sep = " "; + } + switch (cur_loc->kind) { + case LOC_CMDLINE: + argp = cur_loc->ptr; + for (i = 0; i < cur_loc->num; i++) { + error_printf("%s%s", sep, argp[i]); + sep = " "; + } + error_printf(": "); + break; + case LOC_FILE: + error_printf("%s:", (const char *)cur_loc->ptr); + if (cur_loc->num) { + error_printf("%d:", cur_loc->num); + } + error_printf(" "); + break; + default: + error_printf("%s", sep); + } +} + +/* + * Print an error message to current monitor if we have one, else to stderr. + * Prepend the current location and append a newline. + * It's wrong to call this in a QMP monitor. Use qerror_report() there. + */ +void error_report(const char *fmt, ...) +{ + va_list ap; + + error_print_loc(); + va_start(ap, fmt); + error_vprintf(fmt, ap); + va_end(ap); + error_printf("\n"); +} diff -Nru qemu-kvm-0.12.5+noroms/qemu-error.h qemu-kvm-0.14.1/qemu-error.h --- qemu-kvm-0.12.5+noroms/qemu-error.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/qemu-error.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,40 @@ +/* + * Error reporting + * + * Copyright (C) 2010 Red Hat Inc. + * + * Authors: + * Markus Armbruster , + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef QEMU_ERROR_H +#define QEMU_ERROR_H + +typedef struct Location { + /* all members are private to qemu-error.c */ + enum { LOC_NONE, LOC_CMDLINE, LOC_FILE } kind; + int num; + const void *ptr; + struct Location *prev; +} Location; + +Location *loc_push_restore(Location *loc); +Location *loc_push_none(Location *loc); +Location *loc_pop(Location *loc); +Location *loc_save(Location *loc); +void loc_restore(Location *loc); +void loc_set_none(void); +void loc_set_cmdline(char **argv, int idx, int cnt); +void loc_set_file(const char *fname, int lno); + +void error_vprintf(const char *fmt, va_list ap) GCC_FMT_ATTR(1, 0); +void error_printf(const char *fmt, ...) GCC_FMT_ATTR(1, 2); +void error_printf_unless_qmp(const char *fmt, ...) GCC_FMT_ATTR(1, 2); +void error_print_loc(void); +void error_set_progname(const char *argv0); +void error_report(const char *fmt, ...) GCC_FMT_ATTR(1, 2); + +#endif diff -Nru qemu-kvm-0.12.5+noroms/qemu-img.c qemu-kvm-0.14.1/qemu-img.c --- qemu-kvm-0.12.5+noroms/qemu-img.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/qemu-img.c 2011-05-11 13:29:46.000000000 +0000 @@ -23,7 +23,9 @@ */ #include "qemu-common.h" #include "qemu-option.h" +#include "qemu-error.h" #include "osdep.h" +#include "sysemu.h" #include "block_int.h" #include @@ -37,18 +39,7 @@ } img_cmd_t; /* Default to cache=writeback as data integrity is not important for qemu-tcg. */ -#define BRDV_O_FLAGS BDRV_O_CACHE_WB - -static void QEMU_NORETURN error(const char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - fprintf(stderr, "qemu-img: "); - vfprintf(stderr, fmt, ap); - fprintf(stderr, "\n"); - exit(1); - va_end(ap); -} +#define BDRV_O_FLAGS BDRV_O_CACHE_WB static void format_print(void *opaque, const char *name) { @@ -58,7 +49,8 @@ /* Please keep in synch with qemu-img.texi */ static void help(void) { - printf("qemu-img version " QEMU_VERSION ", Copyright (c) 2004-2008 Fabrice Bellard\n" + const char *help_msg = + "qemu-img version " QEMU_VERSION ", Copyright (c) 2004-2008 Fabrice Bellard\n" "usage: qemu-img command [command options]\n" "QEMU disk image utility\n" "\n" @@ -81,6 +73,9 @@ " name=value format. Use -o ? for an overview of the options supported by the\n" " used format\n" " '-c' indicates that target image must be compressed (qcow format only)\n" + " '-u' enables unsafe rebasing. It is assumed that old and new backing file\n" + " match exactly. The image doesn't need a working backing file before\n" + " rebasing in this case (useful for renaming the backing file)\n" " '-h' with or without a command shows this help and lists the supported formats\n" "\n" "Parameters to snapshot subcommand:\n" @@ -88,9 +83,9 @@ " '-a' applies a snapshot (revert disk to saved state)\n" " '-c' creates a snapshot\n" " '-d' deletes a snapshot\n" - " '-l' lists all snapshots in the given image\n" - ); - printf("\nSupported formats:"); + " '-l' lists all snapshots in the given image\n"; + + printf("%s\nSupported formats:", help_msg); bdrv_iterate_format(format_print, NULL); printf("\n"); exit(1); @@ -184,79 +179,117 @@ } #endif +static int print_block_option_help(const char *filename, const char *fmt) +{ + BlockDriver *drv, *proto_drv; + QEMUOptionParameter *create_options = NULL; + + /* Find driver and parse its options */ + drv = bdrv_find_format(fmt); + if (!drv) { + error_report("Unknown file format '%s'", fmt); + return 1; + } + + proto_drv = bdrv_find_protocol(filename); + if (!proto_drv) { + error_report("Unknown protocol '%s'", filename); + return 1; + } + + create_options = append_option_parameters(create_options, + drv->create_options); + create_options = append_option_parameters(create_options, + proto_drv->create_options); + print_option_help(create_options); + free_option_parameters(create_options); + return 0; +} + static BlockDriverState *bdrv_new_open(const char *filename, - const char *fmt) + const char *fmt, + int flags) { BlockDriverState *bs; BlockDriver *drv; char password[256]; + int ret; + + bs = bdrv_new("image"); - bs = bdrv_new(""); - if (!bs) - error("Not enough memory"); if (fmt) { drv = bdrv_find_format(fmt); - if (!drv) - error("Unknown file format '%s'", fmt); + if (!drv) { + error_report("Unknown file format '%s'", fmt); + goto fail; + } } else { drv = NULL; } - if (bdrv_open2(bs, filename, BRDV_O_FLAGS, drv) < 0) { - error("Could not open '%s'", filename); + + ret = bdrv_open(bs, filename, flags, drv); + if (ret < 0) { + error_report("Could not open '%s': %s", filename, strerror(-ret)); + goto fail; } + if (bdrv_is_encrypted(bs)) { printf("Disk image '%s' is encrypted.\n", filename); - if (read_password(password, sizeof(password)) < 0) - error("No password given"); - if (bdrv_set_key(bs, password) < 0) - error("invalid password"); + if (read_password(password, sizeof(password)) < 0) { + error_report("No password given"); + goto fail; + } + if (bdrv_set_key(bs, password) < 0) { + error_report("invalid password"); + goto fail; + } } return bs; +fail: + if (bs) { + bdrv_delete(bs); + } + return NULL; } -static void add_old_style_options(const char *fmt, QEMUOptionParameter *list, - int flags, const char *base_filename, const char *base_fmt) +static int add_old_style_options(const char *fmt, QEMUOptionParameter *list, + const char *base_filename, + const char *base_fmt) { - if (flags & BLOCK_FLAG_ENCRYPT) { - if (set_option_parameter(list, BLOCK_OPT_ENCRYPT, "on")) { - error("Encryption not supported for file format '%s'", fmt); - } - } - if (flags & BLOCK_FLAG_COMPAT6) { - if (set_option_parameter(list, BLOCK_OPT_COMPAT6, "on")) { - error("VMDK version 6 not supported for file format '%s'", fmt); - } - } - if (base_filename) { if (set_option_parameter(list, BLOCK_OPT_BACKING_FILE, base_filename)) { - error("Backing file not supported for file format '%s'", fmt); + error_report("Backing file not supported for file format '%s'", + fmt); + return -1; } } if (base_fmt) { if (set_option_parameter(list, BLOCK_OPT_BACKING_FMT, base_fmt)) { - error("Backing file format not supported for file format '%s'", fmt); + error_report("Backing file format not supported for file " + "format '%s'", fmt); + return -1; } } + return 0; } static int img_create(int argc, char **argv) { - int c, ret, flags; + int c, ret = 0; + uint64_t img_size = -1; const char *fmt = "raw"; const char *base_fmt = NULL; const char *filename; const char *base_filename = NULL; - BlockDriver *drv; - QEMUOptionParameter *param = NULL; char *options = NULL; - flags = 0; for(;;) { c = getopt(argc, argv, "F:b:f:he6o:"); - if (c == -1) + if (c == -1) { break; + } switch(c) { + case '?': case 'h': help(); break; @@ -270,120 +303,76 @@ fmt = optarg; break; case 'e': - flags |= BLOCK_FLAG_ENCRYPT; - break; + error_report("qemu-img: option -e is deprecated, please use \'-o " + "encryption\' instead!"); + return 1; case '6': - flags |= BLOCK_FLAG_COMPAT6; - break; + error_report("qemu-img: option -6 is deprecated, please use \'-o " + "compat6\' instead!"); + return 1; case 'o': options = optarg; break; } } - /* Find driver and parse its options */ - drv = bdrv_find_format(fmt); - if (!drv) - error("Unknown file format '%s'", fmt); - - if (options && !strcmp(options, "?")) { - print_option_help(drv->create_options); - return 0; - } - - /* Create parameter list with default values */ - param = parse_option_parameters("", drv->create_options, param); - set_option_parameter_int(param, BLOCK_OPT_SIZE, -1); - - /* Parse -o options */ - if (options) { - param = parse_option_parameters(options, drv->create_options, param); - if (param == NULL) { - error("Invalid options for file format '%s'.", fmt); - } - } - /* Get the filename */ - if (optind >= argc) + if (optind >= argc) { help(); + } filename = argv[optind++]; - /* Add size to parameters */ + /* Get image size, if specified */ if (optind < argc) { - set_option_parameter(param, BLOCK_OPT_SIZE, argv[optind++]); - } - - /* Add old-style options to parameters */ - add_old_style_options(fmt, param, flags, base_filename, base_fmt); - - // The size for the image must always be specified, with one exception: - // If we are using a backing file, we can obtain the size from there - if (get_option_parameter(param, BLOCK_OPT_SIZE)->value.n == -1) { - - QEMUOptionParameter *backing_file = - get_option_parameter(param, BLOCK_OPT_BACKING_FILE); - QEMUOptionParameter *backing_fmt = - get_option_parameter(param, BLOCK_OPT_BACKING_FMT); - - if (backing_file && backing_file->value.s) { - BlockDriverState *bs; - uint64_t size; - const char *fmt = NULL; - char buf[32]; - - if (backing_fmt && backing_fmt->value.s) { - if (bdrv_find_format(backing_fmt->value.s)) { - fmt = backing_fmt->value.s; - } else { - error("Unknown backing file format '%s'", - backing_fmt->value.s); - } - } - - bs = bdrv_new_open(backing_file->value.s, fmt); - bdrv_get_geometry(bs, &size); - size *= 512; - bdrv_delete(bs); - - snprintf(buf, sizeof(buf), "%" PRId64, size); - set_option_parameter(param, BLOCK_OPT_SIZE, buf); - } else { - error("Image creation needs a size parameter"); + int64_t sval; + sval = strtosz_suffix(argv[optind++], NULL, STRTOSZ_DEFSUFFIX_B); + if (sval < 0) { + error_report("Invalid image size specified! You may use k, M, G or " + "T suffixes for "); + error_report("kilobytes, megabytes, gigabytes and terabytes."); + ret = -1; + goto out; } + img_size = (uint64_t)sval; } - printf("Formatting '%s', fmt=%s ", filename, fmt); - print_option_parameters(param); - puts(""); - - ret = bdrv_create(drv, filename, param); - free_option_parameters(param); + if (options && !strcmp(options, "?")) { + ret = print_block_option_help(filename, fmt); + goto out; + } - if (ret < 0) { - if (ret == -ENOTSUP) { - error("Formatting or formatting option not supported for file format '%s'", fmt); - } else if (ret == -EFBIG) { - error("The image size is too large for file format '%s'", fmt); - } else { - error("Error while formatting"); - } + ret = bdrv_img_create(filename, fmt, base_filename, base_fmt, + options, img_size, BDRV_O_FLAGS); +out: + if (ret) { + return 1; } return 0; } +/* + * Checks an image for consistency. Exit codes: + * + * 0 - Check completed, image is good + * 1 - Check not completed because of internal errors + * 2 - Check completed, image is corrupted + * 3 - Check completed, image has leaked clusters, but is good otherwise + */ static int img_check(int argc, char **argv) { int c, ret; const char *filename, *fmt; - BlockDriver *drv; BlockDriverState *bs; + BdrvCheckResult result; fmt = NULL; for(;;) { c = getopt(argc, argv, "f:h"); - if (c == -1) + if (c == -1) { break; + } switch(c) { + case '?': case 'h': help(); break; @@ -392,57 +381,77 @@ break; } } - if (optind >= argc) + if (optind >= argc) { help(); + } filename = argv[optind++]; - bs = bdrv_new(""); - if (!bs) - error("Not enough memory"); - if (fmt) { - drv = bdrv_find_format(fmt); - if (!drv) - error("Unknown file format '%s'", fmt); - } else { - drv = NULL; + bs = bdrv_new_open(filename, fmt, BDRV_O_FLAGS); + if (!bs) { + return 1; } - if (bdrv_open2(bs, filename, BRDV_O_FLAGS, drv) < 0) { - error("Could not open '%s'", filename); + ret = bdrv_check(bs, &result); + + if (ret == -ENOTSUP) { + error_report("This image format does not support checks"); + bdrv_delete(bs); + return 1; } - ret = bdrv_check(bs); - switch(ret) { - case 0: + + if (!(result.corruptions || result.leaks || result.check_errors)) { printf("No errors were found on the image.\n"); - break; - case -ENOTSUP: - error("This image format does not support checks"); - break; - default: - if (ret < 0) { - error("An error occurred during the check"); - } else { - printf("%d errors were found on the image.\n", ret); + } else { + if (result.corruptions) { + printf("\n%d errors were found on the image.\n" + "Data may be corrupted, or further writes to the image " + "may corrupt it.\n", + result.corruptions); + } + + if (result.leaks) { + printf("\n%d leaked clusters were found on the image.\n" + "This means waste of disk space, but no harm to data.\n", + result.leaks); + } + + if (result.check_errors) { + printf("\n%d internal errors have occurred during the check.\n", + result.check_errors); } - break; } bdrv_delete(bs); - return 0; + + if (ret < 0 || result.check_errors) { + printf("\nAn error has occurred during the check: %s\n" + "The check is not complete and may have missed error.\n", + strerror(-ret)); + return 1; + } + + if (result.corruptions) { + return 2; + } else if (result.leaks) { + return 3; + } else { + return 0; + } } static int img_commit(int argc, char **argv) { int c, ret; const char *filename, *fmt; - BlockDriver *drv; BlockDriverState *bs; fmt = NULL; for(;;) { c = getopt(argc, argv, "f:h"); - if (c == -1) + if (c == -1) { break; + } switch(c) { + case '?': case 'h': help(); break; @@ -451,22 +460,14 @@ break; } } - if (optind >= argc) + if (optind >= argc) { help(); + } filename = argv[optind++]; - bs = bdrv_new(""); - if (!bs) - error("Not enough memory"); - if (fmt) { - drv = bdrv_find_format(fmt); - if (!drv) - error("Unknown file format '%s'", fmt); - } else { - drv = NULL; - } - if (bdrv_open2(bs, filename, BRDV_O_FLAGS, drv) < 0) { - error("Could not open '%s'", filename); + bs = bdrv_new_open(filename, fmt, BDRV_O_FLAGS | BDRV_O_RDWR); + if (!bs) { + return 1; } ret = bdrv_commit(bs); switch(ret) { @@ -474,20 +475,23 @@ printf("Image committed.\n"); break; case -ENOENT: - error("No disk inserted"); + error_report("No disk inserted"); break; case -EACCES: - error("Image is read-only"); + error_report("Image is read-only"); break; case -ENOTSUP: - error("Image is already committed"); + error_report("Image is already committed"); break; default: - error("Error while committing image"); + error_report("Error while committing image"); break; } bdrv_delete(bs); + if (ret) { + return 1; + } return 0; } @@ -527,31 +531,66 @@ return v; } +/* + * Compares two buffers sector by sector. Returns 0 if the first sector of both + * buffers matches, non-zero otherwise. + * + * pnum is set to the number of sectors (including and immediately following + * the first one) that are known to have the same comparison result + */ +static int compare_sectors(const uint8_t *buf1, const uint8_t *buf2, int n, + int *pnum) +{ + int res, i; + + if (n <= 0) { + *pnum = 0; + return 0; + } + + res = !!memcmp(buf1, buf2, 512); + for(i = 1; i < n; i++) { + buf1 += 512; + buf2 += 512; + + if (!!memcmp(buf1, buf2, 512) != res) { + break; + } + } + + *pnum = i; + return res; +} + #define IO_BUF_SIZE (2 * 1024 * 1024) static int img_convert(int argc, char **argv) { - int c, ret, n, n1, bs_n, bs_i, flags, cluster_size, cluster_sectors; + int c, ret = 0, n, n1, bs_n, bs_i, compress, cluster_size, cluster_sectors; const char *fmt, *out_fmt, *out_baseimg, *out_filename; - BlockDriver *drv; - BlockDriverState **bs, *out_bs; + BlockDriver *drv, *proto_drv; + BlockDriverState **bs = NULL, *out_bs = NULL; int64_t total_sectors, nb_sectors, sector_num, bs_offset; uint64_t bs_sectors; - uint8_t * buf; + uint8_t * buf = NULL; const uint8_t *buf1; BlockDriverInfo bdi; - QEMUOptionParameter *param = NULL; + QEMUOptionParameter *param = NULL, *create_options = NULL; + QEMUOptionParameter *out_baseimg_param; char *options = NULL; + const char *snapshot_name = NULL; fmt = NULL; out_fmt = "raw"; out_baseimg = NULL; - flags = 0; + compress = 0; for(;;) { - c = getopt(argc, argv, "f:O:B:hce6o:"); - if (c == -1) + c = getopt(argc, argv, "f:O:B:s:hce6o:"); + if (c == -1) { break; + } switch(c) { + case '?': case 'h': help(); break; @@ -565,105 +604,173 @@ out_baseimg = optarg; break; case 'c': - flags |= BLOCK_FLAG_COMPRESS; + compress = 1; break; case 'e': - flags |= BLOCK_FLAG_ENCRYPT; - break; + error_report("qemu-img: option -e is deprecated, please use \'-o " + "encryption\' instead!"); + return 1; case '6': - flags |= BLOCK_FLAG_COMPAT6; - break; + error_report("qemu-img: option -6 is deprecated, please use \'-o " + "compat6\' instead!"); + return 1; case 'o': options = optarg; break; + case 's': + snapshot_name = optarg; + break; } } bs_n = argc - optind - 1; - if (bs_n < 1) help(); + if (bs_n < 1) { + help(); + } out_filename = argv[argc - 1]; - if (bs_n > 1 && out_baseimg) - error("-B makes no sense when concatenating multiple input images"); + if (options && !strcmp(options, "?")) { + ret = print_block_option_help(out_filename, out_fmt); + goto out; + } + + if (bs_n > 1 && out_baseimg) { + error_report("-B makes no sense when concatenating multiple input " + "images"); + ret = -1; + goto out; + } - bs = calloc(bs_n, sizeof(BlockDriverState *)); - if (!bs) - error("Out of memory"); + bs = qemu_mallocz(bs_n * sizeof(BlockDriverState *)); total_sectors = 0; for (bs_i = 0; bs_i < bs_n; bs_i++) { - bs[bs_i] = bdrv_new_open(argv[optind + bs_i], fmt); - if (!bs[bs_i]) - error("Could not open '%s'", argv[optind + bs_i]); + bs[bs_i] = bdrv_new_open(argv[optind + bs_i], fmt, BDRV_O_FLAGS); + if (!bs[bs_i]) { + error_report("Could not open '%s'", argv[optind + bs_i]); + ret = -1; + goto out; + } bdrv_get_geometry(bs[bs_i], &bs_sectors); total_sectors += bs_sectors; } + if (snapshot_name != NULL) { + if (bs_n > 1) { + error_report("No support for concatenating multiple snapshot\n"); + ret = -1; + goto out; + } + if (bdrv_snapshot_load_tmp(bs[0], snapshot_name) < 0) { + error_report("Failed to load snapshot\n"); + ret = -1; + goto out; + } + } + /* Find driver and parse its options */ drv = bdrv_find_format(out_fmt); - if (!drv) - error("Unknown file format '%s'", out_fmt); - - if (options && !strcmp(options, "?")) { - print_option_help(drv->create_options); - free(bs); - return 0; - } + if (!drv) { + error_report("Unknown file format '%s'", out_fmt); + ret = -1; + goto out; + } + + proto_drv = bdrv_find_protocol(out_filename); + if (!proto_drv) { + error_report("Unknown protocol '%s'", out_filename); + ret = -1; + goto out; + } + + create_options = append_option_parameters(create_options, + drv->create_options); + create_options = append_option_parameters(create_options, + proto_drv->create_options); if (options) { - param = parse_option_parameters(options, drv->create_options, param); + param = parse_option_parameters(options, create_options, param); if (param == NULL) { - error("Invalid options for file format '%s'.", out_fmt); + error_report("Invalid options for file format '%s'.", out_fmt); + ret = -1; + goto out; } } else { - param = parse_option_parameters("", drv->create_options, param); + param = parse_option_parameters("", create_options, param); } set_option_parameter_int(param, BLOCK_OPT_SIZE, total_sectors * 512); - add_old_style_options(out_fmt, param, flags, out_baseimg, NULL); + ret = add_old_style_options(out_fmt, param, out_baseimg, NULL); + if (ret < 0) { + goto out; + } + + /* Get backing file name if -o backing_file was used */ + out_baseimg_param = get_option_parameter(param, BLOCK_OPT_BACKING_FILE); + if (out_baseimg_param) { + out_baseimg = out_baseimg_param->value.s; + } /* Check if compression is supported */ - if (flags & BLOCK_FLAG_COMPRESS) { + if (compress) { QEMUOptionParameter *encryption = get_option_parameter(param, BLOCK_OPT_ENCRYPT); if (!drv->bdrv_write_compressed) { - error("Compression not supported for this file format"); + error_report("Compression not supported for this file format"); + ret = -1; + goto out; } if (encryption && encryption->value.n) { - error("Compression and encryption not supported at the same time"); + error_report("Compression and encryption not supported at " + "the same time"); + ret = -1; + goto out; } } /* Create the new image */ ret = bdrv_create(drv, out_filename, param); - free_option_parameters(param); - if (ret < 0) { if (ret == -ENOTSUP) { - error("Formatting not supported for file format '%s'", out_fmt); + error_report("Formatting not supported for file format '%s'", + out_fmt); } else if (ret == -EFBIG) { - error("The image size is too large for file format '%s'", out_fmt); + error_report("The image size is too large for file format '%s'", + out_fmt); } else { - error("Error while formatting '%s'", out_filename); + error_report("%s: error while converting %s: %s", + out_filename, out_fmt, strerror(-ret)); } + goto out; } - out_bs = bdrv_new_open(out_filename, out_fmt); + out_bs = bdrv_new_open(out_filename, out_fmt, + BDRV_O_FLAGS | BDRV_O_RDWR | BDRV_O_NO_FLUSH); + if (!out_bs) { + ret = -1; + goto out; + } bs_i = 0; bs_offset = 0; bdrv_get_geometry(bs[0], &bs_sectors); buf = qemu_malloc(IO_BUF_SIZE); - if (flags & BLOCK_FLAG_COMPRESS) { - if (bdrv_get_info(out_bs, &bdi) < 0) - error("could not get block driver info"); + if (compress) { + ret = bdrv_get_info(out_bs, &bdi); + if (ret < 0) { + error_report("could not get block driver info"); + goto out; + } cluster_size = bdi.cluster_size; - if (cluster_size <= 0 || cluster_size > IO_BUF_SIZE) - error("invalid cluster size"); + if (cluster_size <= 0 || cluster_size > IO_BUF_SIZE) { + error_report("invalid cluster size"); + ret = -1; + goto out; + } cluster_sectors = cluster_size >> 9; sector_num = 0; for(;;) { @@ -691,16 +798,19 @@ bs_offset += bs_sectors; bdrv_get_geometry(bs[bs_i], &bs_sectors); bs_num = 0; - /* printf("changing part: sector_num=%lld, " - "bs_i=%d, bs_offset=%lld, bs_sectors=%lld\n", - sector_num, bs_i, bs_offset, bs_sectors); */ + /* printf("changing part: sector_num=%" PRId64 ", " + "bs_i=%d, bs_offset=%" PRId64 ", bs_sectors=%" PRId64 + "\n", sector_num, bs_i, bs_offset, bs_sectors); */ } assert (bs_num < bs_sectors); nlow = (remainder > bs_sectors - bs_num) ? bs_sectors - bs_num : remainder; - if (bdrv_read(bs[bs_i], bs_num, buf2, nlow) < 0) - error("error while reading"); + ret = bdrv_read(bs[bs_i], bs_num, buf2, nlow); + if (ret < 0) { + error_report("error while reading"); + goto out; + } buf2 += nlow * 512; bs_num += nlow; @@ -709,43 +819,52 @@ } assert (remainder == 0); - if (n < cluster_sectors) + if (n < cluster_sectors) { memset(buf + n * 512, 0, cluster_size - n * 512); + } if (is_not_zero(buf, cluster_size)) { - if (bdrv_write_compressed(out_bs, sector_num, buf, - cluster_sectors) != 0) - error("error while compressing sector %" PRId64, + ret = bdrv_write_compressed(out_bs, sector_num, buf, + cluster_sectors); + if (ret != 0) { + error_report("error while compressing sector %" PRId64, sector_num); + goto out; + } } sector_num += n; } /* signal EOF to align */ bdrv_write_compressed(out_bs, 0, NULL, 0); } else { + int has_zero_init = bdrv_has_zero_init(out_bs); + sector_num = 0; // total number of sectors converted so far for(;;) { nb_sectors = total_sectors - sector_num; - if (nb_sectors <= 0) + if (nb_sectors <= 0) { break; - if (nb_sectors >= (IO_BUF_SIZE / 512)) + } + if (nb_sectors >= (IO_BUF_SIZE / 512)) { n = (IO_BUF_SIZE / 512); - else + } else { n = nb_sectors; + } while (sector_num - bs_offset >= bs_sectors) { bs_i ++; assert (bs_i < bs_n); bs_offset += bs_sectors; bdrv_get_geometry(bs[bs_i], &bs_sectors); - /* printf("changing part: sector_num=%lld, bs_i=%d, " - "bs_offset=%lld, bs_sectors=%lld\n", + /* printf("changing part: sector_num=%" PRId64 ", bs_i=%d, " + "bs_offset=%" PRId64 ", bs_sectors=%" PRId64 "\n", sector_num, bs_i, bs_offset, bs_sectors); */ } - if (n > bs_offset + bs_sectors - sector_num) + if (n > bs_offset + bs_sectors - sector_num) { n = bs_offset + bs_sectors - sector_num; + } - if (!drv->no_zero_init) { + if (has_zero_init) { /* If the output image is being created as a copy on write image, assume that sectors which are unallocated in the input image are present in both the output's and input's base images (no @@ -764,8 +883,11 @@ n1 = n; } - if (bdrv_read(bs[bs_i], sector_num - bs_offset, buf, n) < 0) - error("error while reading"); + ret = bdrv_read(bs[bs_i], sector_num - bs_offset, buf, n); + if (ret < 0) { + error_report("error while reading"); + goto out; + } /* NOTE: at the same time we convert, we do not write zero sectors to have a chance to compress the image. Ideally, we should add a specific call to have the info to go faster */ @@ -778,10 +900,13 @@ If the output is to a host device, we also write out sectors that are entirely 0, since whatever data was already there is garbage, not 0s. */ - if (drv->no_zero_init || out_baseimg || + if (!has_zero_init || out_baseimg || is_allocated_sectors(buf1, n, &n1)) { - if (bdrv_write(out_bs, sector_num, buf1, n1) < 0) - error("error while writing"); + ret = bdrv_write(out_bs, sector_num, buf1, n1); + if (ret < 0) { + error_report("error while writing"); + goto out; + } } sector_num += n1; n -= n1; @@ -789,11 +914,24 @@ } } } +out: + free_option_parameters(create_options); + free_option_parameters(param); qemu_free(buf); - bdrv_delete(out_bs); - for (bs_i = 0; bs_i < bs_n; bs_i++) - bdrv_delete(bs[bs_i]); - free(bs); + if (out_bs) { + bdrv_delete(out_bs); + } + if (bs) { + for (bs_i = 0; bs_i < bs_n; bs_i++) { + if (bs[bs_i]) { + bdrv_delete(bs[bs_i]); + } + } + qemu_free(bs); + } + if (ret) { + return 1; + } return 0; } @@ -849,7 +987,6 @@ { int c; const char *filename, *fmt; - BlockDriver *drv; BlockDriverState *bs; char fmt_name[128], size_buf[128], dsize_buf[128]; uint64_t total_sectors; @@ -861,9 +998,11 @@ fmt = NULL; for(;;) { c = getopt(argc, argv, "f:h"); - if (c == -1) + if (c == -1) { break; + } switch(c) { + case '?': case 'h': help(); break; @@ -872,32 +1011,25 @@ break; } } - if (optind >= argc) + if (optind >= argc) { help(); + } filename = argv[optind++]; - bs = bdrv_new(""); - if (!bs) - error("Not enough memory"); - if (fmt) { - drv = bdrv_find_format(fmt); - if (!drv) - error("Unknown file format '%s'", fmt); - } else { - drv = NULL; - } - if (bdrv_open2(bs, filename, BRDV_O_FLAGS, drv) < 0) { - error("Could not open '%s'", filename); + bs = bdrv_new_open(filename, fmt, BDRV_O_FLAGS | BDRV_O_NO_BACKING); + if (!bs) { + return 1; } bdrv_get_format(bs, fmt_name, sizeof(fmt_name)); bdrv_get_geometry(bs, &total_sectors); get_human_readable_size(size_buf, sizeof(size_buf), total_sectors * 512); allocated_size = get_allocated_file_size(filename); - if (allocated_size < 0) + if (allocated_size < 0) { snprintf(dsize_buf, sizeof(dsize_buf), "unavailable"); - else + } else { get_human_readable_size(dsize_buf, sizeof(dsize_buf), allocated_size); + } printf("image: %s\n" "file format: %s\n" "virtual size: %s (%" PRId64 " bytes)\n" @@ -905,11 +1037,13 @@ filename, fmt_name, size_buf, (total_sectors * 512), dsize_buf); - if (bdrv_is_encrypted(bs)) + if (bdrv_is_encrypted(bs)) { printf("encrypted: yes\n"); + } if (bdrv_get_info(bs, &bdi) >= 0) { - if (bdi.cluster_size != 0) + if (bdi.cluster_size != 0) { printf("cluster_size: %d\n", bdi.cluster_size); + } } bdrv_get_backing_filename(bs, backing_filename, sizeof(backing_filename)); if (backing_filename[0] != '\0') { @@ -934,16 +1068,19 @@ BlockDriverState *bs; QEMUSnapshotInfo sn; char *filename, *snapshot_name = NULL; - int c, ret; + int c, ret = 0, bdrv_oflags; int action = 0; qemu_timeval tv; + bdrv_oflags = BDRV_O_FLAGS | BDRV_O_RDWR; /* Parse commandline parameters */ for(;;) { c = getopt(argc, argv, "la:c:d:h"); - if (c == -1) + if (c == -1) { break; + } switch(c) { + case '?': case 'h': help(); return 0; @@ -953,6 +1090,7 @@ return 0; } action = SNAPSHOT_LIST; + bdrv_oflags &= ~BDRV_O_RDWR; /* no need for RW */ break; case 'a': if (action) { @@ -981,17 +1119,15 @@ } } - if (optind >= argc) + if (optind >= argc) { help(); + } filename = argv[optind++]; /* Open the image */ - bs = bdrv_new(""); - if (!bs) - error("Not enough memory"); - - if (bdrv_open2(bs, filename, 0, NULL) < 0) { - error("Could not open '%s'", filename); + bs = bdrv_new_open(filename, NULL, bdrv_oflags); + if (!bs) { + return 1; } /* Perform the requested action */ @@ -1009,29 +1145,351 @@ sn.date_nsec = tv.tv_usec * 1000; ret = bdrv_snapshot_create(bs, &sn); - if (ret) - error("Could not create snapshot '%s': %d (%s)", + if (ret) { + error_report("Could not create snapshot '%s': %d (%s)", snapshot_name, ret, strerror(-ret)); + } break; case SNAPSHOT_APPLY: ret = bdrv_snapshot_goto(bs, snapshot_name); - if (ret) - error("Could not apply snapshot '%s': %d (%s)", + if (ret) { + error_report("Could not apply snapshot '%s': %d (%s)", snapshot_name, ret, strerror(-ret)); + } break; case SNAPSHOT_DELETE: ret = bdrv_snapshot_delete(bs, snapshot_name); - if (ret) - error("Could not delete snapshot '%s': %d (%s)", + if (ret) { + error_report("Could not delete snapshot '%s': %d (%s)", snapshot_name, ret, strerror(-ret)); + } break; } /* Cleanup */ bdrv_delete(bs); + if (ret) { + return 1; + } + return 0; +} + +static int img_rebase(int argc, char **argv) +{ + BlockDriverState *bs, *bs_old_backing = NULL, *bs_new_backing = NULL; + BlockDriver *old_backing_drv, *new_backing_drv; + char *filename; + const char *fmt, *out_basefmt, *out_baseimg; + int c, flags, ret; + int unsafe = 0; + + /* Parse commandline parameters */ + fmt = NULL; + out_baseimg = NULL; + out_basefmt = NULL; + + for(;;) { + c = getopt(argc, argv, "uhf:F:b:"); + if (c == -1) { + break; + } + switch(c) { + case '?': + case 'h': + help(); + return 0; + case 'f': + fmt = optarg; + break; + case 'F': + out_basefmt = optarg; + break; + case 'b': + out_baseimg = optarg; + break; + case 'u': + unsafe = 1; + break; + } + } + + if ((optind >= argc) || !out_baseimg) { + help(); + } + filename = argv[optind++]; + + /* + * Open the images. + * + * Ignore the old backing file for unsafe rebase in case we want to correct + * the reference to a renamed or moved backing file. + */ + flags = BDRV_O_FLAGS | BDRV_O_RDWR | (unsafe ? BDRV_O_NO_BACKING : 0); + bs = bdrv_new_open(filename, fmt, flags); + if (!bs) { + return 1; + } + + /* Find the right drivers for the backing files */ + old_backing_drv = NULL; + new_backing_drv = NULL; + + if (!unsafe && bs->backing_format[0] != '\0') { + old_backing_drv = bdrv_find_format(bs->backing_format); + if (old_backing_drv == NULL) { + error_report("Invalid format name: '%s'", bs->backing_format); + ret = -1; + goto out; + } + } + + if (out_basefmt != NULL) { + new_backing_drv = bdrv_find_format(out_basefmt); + if (new_backing_drv == NULL) { + error_report("Invalid format name: '%s'", out_basefmt); + ret = -1; + goto out; + } + } + + /* For safe rebasing we need to compare old and new backing file */ + if (unsafe) { + /* Make the compiler happy */ + bs_old_backing = NULL; + bs_new_backing = NULL; + } else { + char backing_name[1024]; + + bs_old_backing = bdrv_new("old_backing"); + bdrv_get_backing_filename(bs, backing_name, sizeof(backing_name)); + ret = bdrv_open(bs_old_backing, backing_name, BDRV_O_FLAGS, + old_backing_drv); + if (ret) { + error_report("Could not open old backing file '%s'", backing_name); + goto out; + } + + bs_new_backing = bdrv_new("new_backing"); + ret = bdrv_open(bs_new_backing, out_baseimg, BDRV_O_FLAGS, + new_backing_drv); + if (ret) { + error_report("Could not open new backing file '%s'", out_baseimg); + goto out; + } + } + + /* + * Check each unallocated cluster in the COW file. If it is unallocated, + * accesses go to the backing file. We must therefore compare this cluster + * in the old and new backing file, and if they differ we need to copy it + * from the old backing file into the COW file. + * + * If qemu-img crashes during this step, no harm is done. The content of + * the image is the same as the original one at any time. + */ + if (!unsafe) { + uint64_t num_sectors; + uint64_t sector; + int n; + uint8_t * buf_old; + uint8_t * buf_new; + + buf_old = qemu_malloc(IO_BUF_SIZE); + buf_new = qemu_malloc(IO_BUF_SIZE); + + bdrv_get_geometry(bs, &num_sectors); + + for (sector = 0; sector < num_sectors; sector += n) { + + /* How many sectors can we handle with the next read? */ + if (sector + (IO_BUF_SIZE / 512) <= num_sectors) { + n = (IO_BUF_SIZE / 512); + } else { + n = num_sectors - sector; + } + + /* If the cluster is allocated, we don't need to take action */ + ret = bdrv_is_allocated(bs, sector, n, &n); + if (ret) { + continue; + } + + /* Read old and new backing file */ + ret = bdrv_read(bs_old_backing, sector, buf_old, n); + if (ret < 0) { + error_report("error while reading from old backing file"); + goto out; + } + ret = bdrv_read(bs_new_backing, sector, buf_new, n); + if (ret < 0) { + error_report("error while reading from new backing file"); + goto out; + } + + /* If they differ, we need to write to the COW file */ + uint64_t written = 0; + + while (written < n) { + int pnum; + + if (compare_sectors(buf_old + written * 512, + buf_new + written * 512, n - written, &pnum)) + { + ret = bdrv_write(bs, sector + written, + buf_old + written * 512, pnum); + if (ret < 0) { + error_report("Error while writing to COW image: %s", + strerror(-ret)); + goto out; + } + } + + written += pnum; + } + } + + qemu_free(buf_old); + qemu_free(buf_new); + } + + /* + * Change the backing file. All clusters that are different from the old + * backing file are overwritten in the COW file now, so the visible content + * doesn't change when we switch the backing file. + */ + ret = bdrv_change_backing_file(bs, out_baseimg, out_basefmt); + if (ret == -ENOSPC) { + error_report("Could not change the backing file to '%s': No " + "space left in the file header", out_baseimg); + } else if (ret < 0) { + error_report("Could not change the backing file to '%s': %s", + out_baseimg, strerror(-ret)); + } + + /* + * TODO At this point it is possible to check if any clusters that are + * allocated in the COW file are the same in the backing file. If so, they + * could be dropped from the COW file. Don't do this before switching the + * backing file, in case of a crash this would lead to corruption. + */ +out: + /* Cleanup */ + if (!unsafe) { + bdrv_delete(bs_old_backing); + bdrv_delete(bs_new_backing); + } + + bdrv_delete(bs); + if (ret) { + return 1; + } + return 0; +} + +static int img_resize(int argc, char **argv) +{ + int c, ret, relative; + const char *filename, *fmt, *size; + int64_t n, total_size; + BlockDriverState *bs = NULL; + QEMUOptionParameter *param; + QEMUOptionParameter resize_options[] = { + { + .name = BLOCK_OPT_SIZE, + .type = OPT_SIZE, + .help = "Virtual disk size" + }, + { NULL } + }; + + fmt = NULL; + for(;;) { + c = getopt(argc, argv, "f:h"); + if (c == -1) { + break; + } + switch(c) { + case '?': + case 'h': + help(); + break; + case 'f': + fmt = optarg; + break; + } + } + if (optind + 1 >= argc) { + help(); + } + filename = argv[optind++]; + size = argv[optind++]; + + /* Choose grow, shrink, or absolute resize mode */ + switch (size[0]) { + case '+': + relative = 1; + size++; + break; + case '-': + relative = -1; + size++; + break; + default: + relative = 0; + break; + } + + /* Parse size */ + param = parse_option_parameters("", resize_options, NULL); + if (set_option_parameter(param, BLOCK_OPT_SIZE, size)) { + /* Error message already printed when size parsing fails */ + ret = -1; + goto out; + } + n = get_option_parameter(param, BLOCK_OPT_SIZE)->value.n; + free_option_parameters(param); + + bs = bdrv_new_open(filename, fmt, BDRV_O_FLAGS | BDRV_O_RDWR); + if (!bs) { + ret = -1; + goto out; + } + + if (relative) { + total_size = bdrv_getlength(bs) + n * relative; + } else { + total_size = n; + } + if (total_size <= 0) { + error_report("New image size must be positive"); + ret = -1; + goto out; + } + ret = bdrv_truncate(bs, total_size); + switch (ret) { + case 0: + printf("Image resized.\n"); + break; + case -ENOTSUP: + error_report("This image format does not support resize"); + break; + case -EACCES: + error_report("Image is read-only"); + break; + default: + error_report("Error resizing image (%d)", -ret); + break; + } +out: + if (bs) { + bdrv_delete(bs); + } + if (ret) { + return 1; + } return 0; } @@ -1049,6 +1507,8 @@ const img_cmd_t *cmd; const char *cmdname; + error_set_progname(argv[0]); + bdrv_init(); if (argc < 2) help(); diff -Nru qemu-kvm-0.12.5+noroms/qemu-img-cmds.hx qemu-kvm-0.14.1/qemu-img-cmds.hx --- qemu-kvm-0.12.5+noroms/qemu-img-cmds.hx 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/qemu-img-cmds.hx 2011-05-11 13:29:46.000000000 +0000 @@ -7,7 +7,7 @@ STEXI @table @option -STEXI +ETEXI DEF("check", img_check, "check [-f fmt] filename") @@ -28,9 +28,9 @@ ETEXI DEF("convert", img_convert, - "convert [-c] [-f fmt] [-O output_fmt] [-o options] filename [filename2 [...]] output_filename") + "convert [-c] [-f fmt] [-O output_fmt] [-o options] [-s snapshot_name] filename [filename2 [...]] output_filename") STEXI -@item convert [-c] [-f @var{fmt}] [-O @var{output_fmt}] [-o @var{options}] @var{filename} [@var{filename2} [...]] @var{output_filename} +@item convert [-c] [-f @var{fmt}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_name}] @var{filename} [@var{filename2} [...]] @var{output_filename} ETEXI DEF("info", img_info, @@ -43,5 +43,17 @@ "snapshot [-l | -a snapshot | -c snapshot | -d snapshot] filename") STEXI @item snapshot [-l | -a @var{snapshot} | -c @var{snapshot} | -d @var{snapshot}] @var{filename} +ETEXI + +DEF("rebase", img_rebase, + "rebase [-f fmt] [-u] -b backing_file [-F backing_fmt] filename") +STEXI +@item rebase [-f @var{fmt}] [-u] -b @var{backing_file} [-F @var{backing_fmt}] @var{filename} +ETEXI + +DEF("resize", img_resize, + "resize filename [+ | -]size") +STEXI +@item resize @var{filename} [+ | -]@var{size} @end table ETEXI diff -Nru qemu-kvm-0.12.5+noroms/qemu-img.texi qemu-kvm-0.14.1/qemu-img.texi --- qemu-kvm-0.12.5+noroms/qemu-img.texi 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/qemu-img.texi 2011-05-11 13:29:46.000000000 +0000 @@ -59,6 +59,13 @@ Command description: @table @option +@item check [-f @var{fmt}] @var{filename} + +Perform a consistency check on the disk image @var{filename}. + +Only the formats @code{qcow2}, @code{qed} and @code{vdi} support +consistency checks. + @item create [-f @var{fmt}] [-o @var{options}] @var{filename} [@var{size}] Create the new disk image @var{filename} of size @var{size} and format @@ -77,9 +84,9 @@ Commit the changes recorded in @var{filename} in its base image. -@item convert [-c] [-f @var{fmt}] [-O @var{output_fmt}] [-o @var{options}] @var{filename} [@var{filename2} [...]] @var{output_filename} +@item convert [-c] [-f @var{fmt}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_name}] @var{filename} [@var{filename2} [...]] @var{output_filename} -Convert the disk image @var{filename} to disk image @var{output_filename} +Convert the disk image @var{filename} or a snapshot @var{snapshot_name} to disk image @var{output_filename} using format @var{output_fmt}. It can be optionally compressed (@code{-c} option) or use any format specific options like encryption (@code{-o} option). @@ -106,6 +113,52 @@ @item snapshot [-l | -a @var{snapshot} | -c @var{snapshot} | -d @var{snapshot} ] @var{filename} List, apply, create or delete snapshots in image @var{filename}. + +@item rebase [-f @var{fmt}] [-u] -b @var{backing_file} [-F @var{backing_fmt}] @var{filename} + +Changes the backing file of an image. Only the formats @code{qcow2} and +@code{qed} support changing the backing file. + +The backing file is changed to @var{backing_file} and (if the image format of +@var{filename} supports this) the backing file format is changed to +@var{backing_fmt}. + +There are two different modes in which @code{rebase} can operate: +@table @option +@item Safe mode +This is the default mode and performs a real rebase operation. The new backing +file may differ from the old one and qemu-img rebase will take care of keeping +the guest-visible content of @var{filename} unchanged. + +In order to achieve this, any clusters that differ between @var{backing_file} +and the old backing file of @var{filename} are merged into @var{filename} +before actually changing the backing file. + +Note that the safe mode is an expensive operation, comparable to converting +an image. It only works if the old backing file still exists. + +@item Unsafe mode +qemu-img uses the unsafe mode if @code{-u} is specified. In this mode, only the +backing file name and format of @var{filename} is changed without any checks +on the file contents. The user must take care of specifying the correct new +backing file, or the guest-visible content of the image will be corrupted. + +This mode is useful for renaming or moving the backing file to somewhere else. +It can be used without an accessible old backing file, i.e. you can use it to +fix an image whose backing file has already been moved/renamed. +@end table + +@item resize @var{filename} [+ | -]@var{size} + +Change the disk image as if it had been created with @var{size}. + +Before using this command to shrink a disk image, you MUST use file system and +partitioning tools inside the VM to reduce allocated file systems and partition +sizes accordingly. Failure to do so will result in data loss! + +After using this command to grow a disk image, you must use file system and +partitioning tools inside the VM to actually begin using the new space on the +device. @end table Supported image file formats: diff -Nru qemu-kvm-0.12.5+noroms/qemu-io.c qemu-kvm-0.14.1/qemu-io.c --- qemu-kvm-0.12.5+noroms/qemu-io.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/qemu-io.c 2011-05-11 13:29:46.000000000 +0000 @@ -61,7 +61,7 @@ if (misalign) len += MISALIGN_OFFSET; - buf = qemu_memalign(512, len); + buf = qemu_blockalign(bs, len); memset(buf, pattern, len); if (misalign) buf += MISALIGN_OFFSET; @@ -84,7 +84,7 @@ for (i = 0, p = buffer; i < len; i += 16) { const uint8_t *s = p; - printf("%08llx: ", (unsigned long long)offset + i); + printf("%08" PRIx64 ": ", offset + i); for (j = 0; j < 16 && i + j < len; j++, p++) printf("%02x ", *p); printf(" "); @@ -108,8 +108,8 @@ if (!Cflag) { cvtstr((double)total, s1, sizeof(s1)); cvtstr(tdiv((double)total, *t), s2, sizeof(s2)); - printf("%s %d/%d bytes at offset %lld\n", - op, total, count, (long long)offset); + printf("%s %d/%d bytes at offset %" PRId64 "\n", + op, total, count, offset); printf("%s, %d ops; %s (%s/sec and %.4f ops/sec)\n", s1, cnt, ts, s2, tdiv((double)cnt, *t)); } else {/* bytes,ops,time,bytes/sec,ops/sec */ @@ -135,7 +135,7 @@ for (i = 0; i < nr_iov; i++) { char *arg = argv[i]; - long long len; + int64_t len; len = cvtnum(arg); if (len < 0) { @@ -144,14 +144,14 @@ } /* should be SIZE_T_MAX, but that doesn't exist */ - if (len > UINT_MAX) { + if (len > INT_MAX) { printf("too large length argument -- %s\n", arg); goto fail; } if (len & 0x1ff) { - printf("length argument %lld is not sector aligned\n", - len); + printf("length argument %" PRId64 + " is not sector aligned\n", len); goto fail; } @@ -267,6 +267,47 @@ return async_ret < 0 ? async_ret : 1; } +struct multiwrite_async_ret { + int num_done; + int error; +}; + +static void multiwrite_cb(void *opaque, int ret) +{ + struct multiwrite_async_ret *async_ret = opaque; + + async_ret->num_done++; + if (ret < 0) { + async_ret->error = ret; + } +} + +static int do_aio_multiwrite(BlockRequest* reqs, int num_reqs, int *total) +{ + int i, ret; + struct multiwrite_async_ret async_ret = { + .num_done = 0, + .error = 0, + }; + + *total = 0; + for (i = 0; i < num_reqs; i++) { + reqs[i].cb = multiwrite_cb; + reqs[i].opaque = &async_ret; + *total += reqs[i].qiov->size; + } + + ret = bdrv_aio_multiwrite(bs, reqs, num_reqs); + if (ret < 0) { + return ret; + } + + while (async_ret.num_done < num_reqs) { + qemu_aio_wait(); + } + + return async_ret.error < 0 ? async_ret.error : 1; +} static void read_help(void) @@ -285,7 +326,7 @@ " -l, -- length for pattern verification (only with -P)\n" " -p, -- use bdrv_pread to read the file\n" " -P, -- use a pattern to verify read data\n" -" -q, -- quite mode, do not show I/O statistics\n" +" -q, -- quiet mode, do not show I/O statistics\n" " -s, -- start offset for pattern verification (only with -P)\n" " -v, -- dump buffer to standard output\n" "\n"); @@ -398,8 +439,8 @@ if (!pflag) if (offset & 0x1ff) { - printf("offset %lld is not sector aligned\n", - (long long)offset); + printf("offset %" PRId64 " is not sector aligned\n", + offset); return 0; if (count & 0x1ff) { @@ -429,9 +470,9 @@ void* cmp_buf = malloc(pattern_count); memset(cmp_buf, pattern, pattern_count); if (memcmp(buf + pattern_offset, cmp_buf, pattern_count)) { - printf("Pattern verification failed at offset %lld, " - "%d bytes\n", - (long long) offset + pattern_offset, pattern_count); + printf("Pattern verification failed at offset %" + PRId64 ", %d bytes\n", + offset + pattern_offset, pattern_count); } free(cmp_buf); } @@ -468,7 +509,7 @@ " -C, -- report statistics in a machine parsable format\n" " -P, -- use a pattern to verify read data\n" " -v, -- dump buffer to standard output\n" -" -q, -- quite mode, do not show I/O statistics\n" +" -q, -- quiet mode, do not show I/O statistics\n" "\n"); } @@ -492,7 +533,8 @@ int c, cnt; char *buf; int64_t offset; - int total; + /* Some compilers get confused and warn if this is not initialized. */ + int total = 0; int nr_iov; QEMUIOVector qiov; int pattern = 0; @@ -532,8 +574,8 @@ optind++; if (offset & 0x1ff) { - printf("offset %lld is not sector aligned\n", - (long long)offset); + printf("offset %" PRId64 " is not sector aligned\n", + offset); return 0; } @@ -553,9 +595,9 @@ void* cmp_buf = malloc(qiov.size); memset(cmp_buf, pattern, qiov.size); if (memcmp(buf, cmp_buf, qiov.size)) { - printf("Pattern verification failed at offset %lld, " - "%zd bytes\n", - (long long) offset, qiov.size); + printf("Pattern verification failed at offset %" + PRId64 ", %zd bytes\n", + offset, qiov.size); } free(cmp_buf); } @@ -591,7 +633,7 @@ " -p, -- use bdrv_pwrite to write the file\n" " -P, -- use different pattern to fill file\n" " -C, -- report statistics in a machine parsable format\n" -" -q, -- quite mode, do not show I/O statistics\n" +" -q, -- quiet mode, do not show I/O statistics\n" "\n"); } @@ -668,8 +710,8 @@ if (!pflag) { if (offset & 0x1ff) { - printf("offset %lld is not sector aligned\n", - (long long)offset); + printf("offset %" PRId64 " is not sector aligned\n", + offset); return 0; } @@ -723,7 +765,7 @@ " filled with a set pattern (0xcdcdcdcd).\n" " -P, -- use different pattern to fill file\n" " -C, -- report statistics in a machine parsable format\n" -" -q, -- quite mode, do not show I/O statistics\n" +" -q, -- quiet mode, do not show I/O statistics\n" "\n"); } @@ -747,7 +789,8 @@ int c, cnt; char *buf; int64_t offset; - int total; + /* Some compilers get confused and warn if this is not initialized. */ + int total = 0; int nr_iov; int pattern = 0xcd; QEMUIOVector qiov; @@ -781,8 +824,8 @@ optind++; if (offset & 0x1ff) { - printf("offset %lld is not sector aligned\n", - (long long)offset); + printf("offset %" PRId64 " is not sector aligned\n", + offset); return 0; } @@ -809,6 +852,156 @@ return 0; } +static void +multiwrite_help(void) +{ + printf( +"\n" +" writes a range of bytes from the given offset source from multiple buffers,\n" +" in a batch of requests that may be merged by qemu\n" +"\n" +" Example:\n" +" 'multiwrite 512 1k 1k ; 4k 1k' \n" +" writes 2 kB at 512 bytes and 1 kB at 4 kB into the open file\n" +"\n" +" Writes into a segment of the currently open file, using a buffer\n" +" filled with a set pattern (0xcdcdcdcd). The pattern byte is increased\n" +" by one for each request contained in the multiwrite command.\n" +" -P, -- use different pattern to fill file\n" +" -C, -- report statistics in a machine parsable format\n" +" -q, -- quiet mode, do not show I/O statistics\n" +"\n"); +} + +static int multiwrite_f(int argc, char **argv); + +static const cmdinfo_t multiwrite_cmd = { + .name = "multiwrite", + .cfunc = multiwrite_f, + .argmin = 2, + .argmax = -1, + .args = "[-Cq] [-P pattern ] off len [len..] [; off len [len..]..]", + .oneline = "issues multiple write requests at once", + .help = multiwrite_help, +}; + +static int +multiwrite_f(int argc, char **argv) +{ + struct timeval t1, t2; + int Cflag = 0, qflag = 0; + int c, cnt; + char **buf; + int64_t offset, first_offset = 0; + /* Some compilers get confused and warn if this is not initialized. */ + int total = 0; + int nr_iov; + int nr_reqs; + int pattern = 0xcd; + QEMUIOVector *qiovs; + int i; + BlockRequest *reqs; + + while ((c = getopt(argc, argv, "CqP:")) != EOF) { + switch (c) { + case 'C': + Cflag = 1; + break; + case 'q': + qflag = 1; + break; + case 'P': + pattern = parse_pattern(optarg); + if (pattern < 0) + return 0; + break; + default: + return command_usage(&writev_cmd); + } + } + + if (optind > argc - 2) + return command_usage(&writev_cmd); + + nr_reqs = 1; + for (i = optind; i < argc; i++) { + if (!strcmp(argv[i], ";")) { + nr_reqs++; + } + } + + reqs = qemu_malloc(nr_reqs * sizeof(*reqs)); + buf = qemu_malloc(nr_reqs * sizeof(*buf)); + qiovs = qemu_malloc(nr_reqs * sizeof(*qiovs)); + + for (i = 0; i < nr_reqs; i++) { + int j; + + /* Read the offset of the request */ + offset = cvtnum(argv[optind]); + if (offset < 0) { + printf("non-numeric offset argument -- %s\n", argv[optind]); + return 0; + } + optind++; + + if (offset & 0x1ff) { + printf("offset %lld is not sector aligned\n", + (long long)offset); + return 0; + } + + if (i == 0) { + first_offset = offset; + } + + /* Read lengths for qiov entries */ + for (j = optind; j < argc; j++) { + if (!strcmp(argv[j], ";")) { + break; + } + } + + nr_iov = j - optind; + + /* Build request */ + reqs[i].qiov = &qiovs[i]; + buf[i] = create_iovec(reqs[i].qiov, &argv[optind], nr_iov, pattern); + reqs[i].sector = offset >> 9; + reqs[i].nb_sectors = reqs[i].qiov->size >> 9; + + optind = j + 1; + + offset += reqs[i].qiov->size; + pattern++; + } + + gettimeofday(&t1, NULL); + cnt = do_aio_multiwrite(reqs, nr_reqs, &total); + gettimeofday(&t2, NULL); + + if (cnt < 0) { + printf("aio_multiwrite failed: %s\n", strerror(-cnt)); + goto out; + } + + if (qflag) + goto out; + + /* Finally, report back -- -C gives a parsable format */ + t2 = tsub(t2, t1); + print_report("wrote", &t2, first_offset, total, total, cnt, Cflag); +out: + for (i = 0; i < nr_reqs; i++) { + qemu_io_free(buf[i]); + qemu_iovec_destroy(&qiovs[i]); + } + qemu_free(buf); + qemu_free(reqs); + qemu_free(qiovs); + return 0; +} + struct aio_ctx { QEMUIOVector qiov; int64_t offset; @@ -866,9 +1059,9 @@ memset(cmp_buf, ctx->pattern, ctx->qiov.size); if (memcmp(ctx->buf, cmp_buf, ctx->qiov.size)) { - printf("Pattern verification failed at offset %lld, " - "%zd bytes\n", - (long long) ctx->offset, ctx->qiov.size); + printf("Pattern verification failed at offset %" + PRId64 ", %zd bytes\n", + ctx->offset, ctx->qiov.size); } free(cmp_buf); } @@ -902,12 +1095,12 @@ "\n" " Reads a segment of the currently open file, optionally dumping it to the\n" " standard output stream (with -v option) for subsequent inspection.\n" -" The read is performed asynchronously and should the aio_flush command \n" -" should be used to ensure all outstanding aio requests have been completed\n" +" The read is performed asynchronously and the aio_flush command must be\n" +" used to ensure all outstanding aio requests have been completed\n" " -C, -- report statistics in a machine parsable format\n" " -P, -- use a pattern to verify read data\n" " -v, -- dump buffer to standard output\n" -" -q, -- quite mode, do not show I/O statistics\n" +" -q, -- quiet mode, do not show I/O statistics\n" "\n"); } @@ -938,8 +1131,10 @@ case 'P': ctx->Pflag = 1; ctx->pattern = parse_pattern(optarg); - if (ctx->pattern < 0) + if (ctx->pattern < 0) { + free(ctx); return 0; + } break; case 'q': ctx->qflag = 1; @@ -967,8 +1162,8 @@ optind++; if (ctx->offset & 0x1ff) { - printf("offset %lld is not sector aligned\n", - (long long)ctx->offset); + printf("offset %" PRId64 " is not sector aligned\n", + ctx->offset); free(ctx); return 0; } @@ -1001,11 +1196,11 @@ "\n" " Writes into a segment of the currently open file, using a buffer\n" " filled with a set pattern (0xcdcdcdcd).\n" -" The write is performed asynchronously and should the aio_flush command \n" -" should be used to ensure all outstanding aio requests have been completed\n" +" The write is performed asynchronously and the aio_flush command must be\n" +" used to ensure all outstanding aio requests have been completed\n" " -P, -- use different pattern to fill file\n" " -C, -- report statistics in a machine parsable format\n" -" -q, -- quite mode, do not show I/O statistics\n" +" -q, -- quiet mode, do not show I/O statistics\n" "\n"); } @@ -1062,8 +1257,8 @@ optind++; if (ctx->offset & 0x1ff) { - printf("offset %lld is not sector aligned\n", - (long long)ctx->offset); + printf("offset %" PRId64 " is not sector aligned\n", + ctx->offset); free(ctx); return 0; } @@ -1093,7 +1288,7 @@ static const cmdinfo_t aio_flush_cmd = { .name = "aio_flush", .cfunc = aio_flush_f, - .oneline = "completes all outstanding aio requets" + .oneline = "completes all outstanding aio requests" }; static int @@ -1124,7 +1319,7 @@ ret = bdrv_truncate(bs, offset); if (ret < 0) { - printf("truncate: %s", strerror(ret)); + printf("truncate: %s\n", strerror(-ret)); return 0; } @@ -1149,7 +1344,7 @@ size = bdrv_getlength(bs); if (size < 0) { - printf("getlength: %s", strerror(size)); + printf("getlength: %s\n", strerror(-size)); return 0; } @@ -1201,6 +1396,93 @@ .oneline = "prints information about the current file", }; +static void +discard_help(void) +{ + printf( +"\n" +" discards a range of bytes from the given offset\n" +"\n" +" Example:\n" +" 'discard 512 1k' - discards 1 kilobyte from 512 bytes into the file\n" +"\n" +" Discards a segment of the currently open file.\n" +" -C, -- report statistics in a machine parsable format\n" +" -q, -- quiet mode, do not show I/O statistics\n" +"\n"); +} + +static int discard_f(int argc, char **argv); + +static const cmdinfo_t discard_cmd = { + .name = "discard", + .altname = "d", + .cfunc = discard_f, + .argmin = 2, + .argmax = -1, + .args = "[-Cq] off len", + .oneline = "discards a number of bytes at a specified offset", + .help = discard_help, +}; + +static int +discard_f(int argc, char **argv) +{ + struct timeval t1, t2; + int Cflag = 0, qflag = 0; + int c, ret; + int64_t offset; + int count; + + while ((c = getopt(argc, argv, "Cq")) != EOF) { + switch (c) { + case 'C': + Cflag = 1; + break; + case 'q': + qflag = 1; + break; + default: + return command_usage(&discard_cmd); + } + } + + if (optind != argc - 2) { + return command_usage(&discard_cmd); + } + + offset = cvtnum(argv[optind]); + if (offset < 0) { + printf("non-numeric length argument -- %s\n", argv[optind]); + return 0; + } + + optind++; + count = cvtnum(argv[optind]); + if (count < 0) { + printf("non-numeric length argument -- %s\n", argv[optind]); + return 0; + } + + gettimeofday(&t1, NULL); + ret = bdrv_discard(bs, offset >> BDRV_SECTOR_BITS, count >> BDRV_SECTOR_BITS); + gettimeofday(&t2, NULL); + + if (ret < 0) { + printf("discard failed: %s\n", strerror(-ret)); + goto out; + } + + /* Finally, report back -- -C gives a parsable format */ + if (!qflag) { + t2 = tsub(t2, t1); + print_report("discard", &t2, offset, count, count, 1, Cflag); + } + +out: + return 0; +} + static int alloc_f(int argc, char **argv) { @@ -1212,8 +1494,8 @@ offset = cvtnum(argv[1]); if (offset & 0x1ff) { - printf("offset %lld is not sector aligned\n", - (long long)offset); + printf("offset %" PRId64 " is not sector aligned\n", + offset); return 0; } @@ -1234,11 +1516,8 @@ cvtstr(offset, s1, sizeof(s1)); - if (nb_sectors == 1) - printf("sector allocated at offset %s\n", s1); - else - printf("%d/%d sectors allocated at offset %s\n", - sum_alloc, nb_sectors, s1); + printf("%d/%d sectors allocated at offset %s\n", + sum_alloc, nb_sectors, s1); return 0; } @@ -1253,6 +1532,44 @@ }; static int +map_f(int argc, char **argv) +{ + int64_t offset; + int64_t nb_sectors; + char s1[64]; + int num, num_checked; + int ret; + const char *retstr; + + offset = 0; + nb_sectors = bs->total_sectors; + + do { + num_checked = MIN(nb_sectors, INT_MAX); + ret = bdrv_is_allocated(bs, offset, num_checked, &num); + retstr = ret ? " allocated" : "not allocated"; + cvtstr(offset << 9ULL, s1, sizeof(s1)); + printf("[% 24" PRId64 "] % 8d/% 8d sectors %s at offset %s (%d)\n", + offset << 9ULL, num, num_checked, retstr, s1, ret); + + offset += num; + nb_sectors -= num; + } while(offset < bs->total_sectors); + + return 0; +} + +static const cmdinfo_t map_cmd = { + .name = "map", + .argmin = 0, + .argmax = 0, + .cfunc = map_f, + .args = "", + .oneline = "prints the allocated areas of a file", +}; + + +static int close_f(int argc, char **argv) { bdrv_close(bs); @@ -1274,23 +1591,21 @@ return 1; } - bs = bdrv_new("hda"); - if (!bs) - return 1; - if (growable) { - flags |= BDRV_O_FILE; - } + if (bdrv_file_open(&bs, name, flags)) { + fprintf(stderr, "%s: can't open device %s\n", progname, name); + return 1; + } + } else { + bs = bdrv_new("hda"); - if (bdrv_open(bs, name, flags) == -1) { - fprintf(stderr, "%s: can't open device %s\n", progname, name); - bs = NULL; - return 1; + if (bdrv_open(bs, name, flags, NULL) < 0) { + fprintf(stderr, "%s: can't open device %s\n", progname, name); + bs = NULL; + return 1; + } } - if (growable) { - bs->growable = 1; - } return 0; } @@ -1305,7 +1620,6 @@ " 'open -Cn /tmp/data' - creates/opens data file read-write and uncached\n" "\n" " Opens a file for subsequent use by all of the other qemu-io commands.\n" -" -C, -- create new file if it doesn't exist\n" " -r, -- open file read-only\n" " -s, -- use snapshot file\n" " -n, -- disable host cache\n" @@ -1335,7 +1649,7 @@ int growable = 0; int c; - while ((c = getopt(argc, argv, "snCrg")) != EOF) { + while ((c = getopt(argc, argv, "snrg")) != EOF) { switch (c) { case 's': flags |= BDRV_O_SNAPSHOT; @@ -1343,9 +1657,6 @@ case 'n': flags |= BDRV_O_NOCACHE; break; - case 'C': - flags |= BDRV_O_CREAT; - break; case 'r': readonly = 1; break; @@ -1357,10 +1668,9 @@ } } - if (readonly) - flags |= BDRV_O_RDONLY; - else - flags |= BDRV_O_RDWR; + if (!readonly) { + flags |= BDRV_O_RDWR; + } if (optind != argc - 1) return command_usage(&open_cmd); @@ -1394,10 +1704,9 @@ static void usage(const char *name) { printf( -"Usage: %s [-h] [-V] [-Crsnm] [-c cmd] ... [file]\n" +"Usage: %s [-h] [-V] [-rsnm] [-c cmd] ... [file]\n" "QEMU Disk exerciser\n" "\n" -" -C, --create create new file if it doesn't exist\n" " -c, --cmd command to execute\n" " -r, --read-only export read-only\n" " -s, --snapshot use snapshot file\n" @@ -1416,13 +1725,12 @@ { int readonly = 0; int growable = 0; - const char *sopt = "hVc:Crsnmgk"; - struct option lopt[] = { + const char *sopt = "hVc:rsnmgk"; + const struct option lopt[] = { { "help", 0, NULL, 'h' }, { "version", 0, NULL, 'V' }, { "offset", 1, NULL, 'o' }, { "cmd", 1, NULL, 'c' }, - { "create", 0, NULL, 'C' }, { "read-only", 0, NULL, 'r' }, { "snapshot", 0, NULL, 's' }, { "nocache", 0, NULL, 'n' }, @@ -1448,9 +1756,6 @@ case 'c': add_user_command(optarg); break; - case 'C': - flags |= BDRV_O_CREAT; - break; case 'r': readonly = 1; break; @@ -1491,6 +1796,7 @@ add_command(&readv_cmd); add_command(&write_cmd); add_command(&writev_cmd); + add_command(&multiwrite_cmd); add_command(&aio_read_cmd); add_command(&aio_write_cmd); add_command(&aio_flush_cmd); @@ -1498,16 +1804,17 @@ add_command(&truncate_cmd); add_command(&length_cmd); add_command(&info_cmd); + add_command(&discard_cmd); add_command(&alloc_cmd); + add_command(&map_cmd); add_args_command(init_args_command); add_check_command(init_check_command); /* open the device */ - if (readonly) - flags |= BDRV_O_RDONLY; - else - flags |= BDRV_O_RDWR; + if (!readonly) { + flags |= BDRV_O_RDWR; + } if ((argc - optind) == 1) openfile(argv[optind], flags, growable); diff -Nru qemu-kvm-0.12.5+noroms/qemu-kvm.c qemu-kvm-0.14.1/qemu-kvm.c --- qemu-kvm-0.12.5+noroms/qemu-kvm.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/qemu-kvm.c 2011-05-11 13:29:46.000000000 +0000 @@ -17,6 +17,7 @@ #include "block.h" #include "compatfd.h" #include "gdbstub.h" +#include "monitor.h" #include "qemu-kvm.h" #include "libkvm.h" @@ -49,7 +50,6 @@ #error libkvm: userspace and kernel version mismatch #endif -int kvm_allowed = 1; int kvm_irqchip = 1; int kvm_pit = 1; int kvm_pit_reinject = 1; @@ -71,26 +71,15 @@ #define SIG_IPI (SIGRTMIN+4) pthread_t io_thread; -static int io_thread_fd = -1; static int io_thread_sigfd = -1; static CPUState *kvm_debug_cpu_requested; -static uint64_t phys_ram_size; - #ifdef CONFIG_KVM_DEVICE_ASSIGNMENT /* The list of ioperm_data */ static QLIST_HEAD(, ioperm_data) ioperm_head; #endif -//#define DEBUG_MEMREG -#ifdef DEBUG_MEMREG -#define DPRINTF(fmt, args...) \ - do { fprintf(stderr, "%s:%d " fmt , __func__, __LINE__, ##args); } while (0) -#else -#define DPRINTF(fmt, args...) do {} while (0) -#endif - #define ALIGN(x, y) (((x)+(y)-1) & ~((y)-1)) int kvm_abi = EXPECTED_KVM_API_VERSION; @@ -116,6 +105,24 @@ return -EINVAL; } +#define VMX_INVALID_GUEST_STATE 0x80000021 + +static int handle_failed_vmentry(uint64_t reason) +{ + fprintf(stderr, "kvm: vm entry failed with error 0x%" PRIx64 "\n\n", reason); + + /* Perhaps we will need to check if this machine is intel since exit reason 0x21 + has a different interpretation on SVM */ + if (reason == VMX_INVALID_GUEST_STATE) { + fprintf(stderr, "If you're runnning a guest on an Intel machine without\n"); + fprintf(stderr, "unrestricted mode support, the failure can be most likely\n"); + fprintf(stderr, "due to the guest entering an invalid state for Intel VT.\n"); + fprintf(stderr, "For example, the guest maybe running in big real mode\n"); + fprintf(stderr, "which is not supported on less recent Intel processors.\n\n"); + } + + return -EINVAL; +} static inline void set_gsi(kvm_context_t kvm, unsigned int gsi) { @@ -137,218 +144,9 @@ DPRINTF("Invalid GSI %u\n", gsi); } -struct slot_info { - unsigned long phys_addr; - unsigned long len; - unsigned long userspace_addr; - unsigned flags; - int logging_count; -}; - -struct slot_info slots[KVM_MAX_NUM_MEM_REGIONS]; - -static void init_slots(void) -{ - int i; - - for (i = 0; i < KVM_MAX_NUM_MEM_REGIONS; ++i) - slots[i].len = 0; -} - -static int get_free_slot(kvm_context_t kvm) -{ - int i; - int tss_ext; - -#if defined(KVM_CAP_SET_TSS_ADDR) && !defined(__s390__) - tss_ext = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, KVM_CAP_SET_TSS_ADDR); -#else - tss_ext = 0; -#endif - - /* - * on older kernels where the set tss ioctl is not supprted we must save - * slot 0 to hold the extended memory, as the vmx will use the last 3 - * pages of this slot. - */ - if (tss_ext > 0) - i = 0; - else - i = 1; - - for (; i < KVM_MAX_NUM_MEM_REGIONS; ++i) - if (!slots[i].len) - return i; - return -1; -} - -static void register_slot(int slot, unsigned long phys_addr, - unsigned long len, unsigned long userspace_addr, - unsigned flags) -{ - slots[slot].phys_addr = phys_addr; - slots[slot].len = len; - slots[slot].userspace_addr = userspace_addr; - slots[slot].flags = flags; -} - -static void free_slot(int slot) -{ - slots[slot].len = 0; - slots[slot].logging_count = 0; -} - -static int get_slot(unsigned long phys_addr) -{ - int i; - - for (i = 0; i < KVM_MAX_NUM_MEM_REGIONS; ++i) { - if (slots[i].len && slots[i].phys_addr <= phys_addr && - (slots[i].phys_addr + slots[i].len - 1) >= phys_addr) - return i; - } - return -1; -} - -/* Returns -1 if this slot is not totally contained on any other, - * and the number of the slot otherwise */ -static int get_container_slot(uint64_t phys_addr, unsigned long size) -{ - int i; - - for (i = 0; i < KVM_MAX_NUM_MEM_REGIONS; ++i) - if (slots[i].len && slots[i].phys_addr <= phys_addr && - (slots[i].phys_addr + slots[i].len) >= phys_addr + size) - return i; - return -1; -} - -int kvm_is_containing_region(kvm_context_t kvm, unsigned long phys_addr, - unsigned long size) -{ - int slot = get_container_slot(phys_addr, size); - if (slot == -1) - return 0; - return 1; -} - -/* - * dirty pages logging control - */ -static int kvm_dirty_pages_log_change(kvm_context_t kvm, - unsigned long phys_addr, unsigned flags, - unsigned mask) -{ - int r = -1; - int slot = get_slot(phys_addr); - - if (slot == -1) { - fprintf(stderr, "BUG: %s: invalid parameters\n", __FUNCTION__); - return 1; - } - - flags = (slots[slot].flags & ~mask) | flags; - if (flags == slots[slot].flags) - return 0; - slots[slot].flags = flags; - - { - struct kvm_userspace_memory_region mem = { - .slot = slot, - .memory_size = slots[slot].len, - .guest_phys_addr = slots[slot].phys_addr, - .userspace_addr = slots[slot].userspace_addr, - .flags = slots[slot].flags, - }; - - - DPRINTF("slot %d start %llx len %llx flags %x\n", - mem.slot, mem.guest_phys_addr, mem.memory_size, mem.flags); - r = kvm_vm_ioctl(kvm_state, KVM_SET_USER_MEMORY_REGION, &mem); - if (r < 0) - fprintf(stderr, "%s: %m\n", __FUNCTION__); - } - return r; -} - -static int kvm_dirty_pages_log_change_all(kvm_context_t kvm, - int (*change)(kvm_context_t kvm, - uint64_t start, - uint64_t len)) -{ - int i, r; - - for (i = r = 0; i < KVM_MAX_NUM_MEM_REGIONS && r == 0; i++) { - if (slots[i].len) - r = change(kvm, slots[i].phys_addr, slots[i].len); - } - return r; -} - -int kvm_dirty_pages_log_enable_slot(kvm_context_t kvm, uint64_t phys_addr, - uint64_t len) -{ - int slot = get_slot(phys_addr); - - DPRINTF("start %" PRIx64 " len %" PRIx64 "\n", phys_addr, len); - if (slot == -1) { - fprintf(stderr, "BUG: %s: invalid parameters\n", __func__); - return -EINVAL; - } - - if (slots[slot].logging_count++) - return 0; - - return kvm_dirty_pages_log_change(kvm, slots[slot].phys_addr, - KVM_MEM_LOG_DIRTY_PAGES, - KVM_MEM_LOG_DIRTY_PAGES); -} - -int kvm_dirty_pages_log_disable_slot(kvm_context_t kvm, uint64_t phys_addr, - uint64_t len) -{ - int slot = get_slot(phys_addr); - - if (slot == -1) { - fprintf(stderr, "BUG: %s: invalid parameters\n", __func__); - return -EINVAL; - } - - if (--slots[slot].logging_count) - return 0; - - return kvm_dirty_pages_log_change(kvm, slots[slot].phys_addr, 0, - KVM_MEM_LOG_DIRTY_PAGES); -} - -/** - * Enable dirty page logging for all memory regions - */ -int kvm_dirty_pages_log_enable_all(kvm_context_t kvm) -{ - if (kvm->dirty_pages_log_all) - return 0; - kvm->dirty_pages_log_all = 1; - return kvm_dirty_pages_log_change_all(kvm, kvm_dirty_pages_log_enable_slot); -} - -/** - * Enable dirty page logging only for memory regions that were created with - * dirty logging enabled (disable for all other memory regions). - */ -int kvm_dirty_pages_log_reset(kvm_context_t kvm) -{ - if (!kvm->dirty_pages_log_all) - return 0; - kvm->dirty_pages_log_all = 0; - return kvm_dirty_pages_log_change_all(kvm, - kvm_dirty_pages_log_disable_slot); -} - - static int kvm_create_context(void); -int kvm_init(int smp_cpus) +int kvm_init(void) { int fd; int r, gsi_count; @@ -402,10 +200,13 @@ kvm_context->max_gsi = gsi_bits; /* Mark any over-allocated bits as already in use */ - for (i = gsi_count; i < gsi_bits; i++) + for (i = gsi_count; i < gsi_bits; i++) { set_gsi(kvm_context, i); + } } + kvm_cpu_register_phys_memory_client(); + pthread_mutex_lock(&qemu_mutex); return kvm_create_context(); @@ -436,10 +237,18 @@ kvm->no_pit_creation = 1; } +static void kvm_reset_vcpu(void *opaque) +{ + CPUState *env = opaque; + + kvm_arch_cpu_reset(env); +} + static void kvm_create_vcpu(CPUState *env, int id) { long mmap_size; int r; + KVMState *s = kvm_state; r = kvm_vm_ioctl(kvm_state, KVM_CREATE_VCPU, id); if (r < 0) { @@ -464,6 +273,17 @@ goto err_fd; } +#ifdef KVM_CAP_COALESCED_MMIO + if (s->coalesced_mmio && !s->coalesced_mmio_ring) + s->coalesced_mmio_ring = (void *) env->kvm_run + + s->coalesced_mmio * PAGE_SIZE; +#endif + + r = kvm_arch_init_vcpu(env); + if (r == 0) { + qemu_register_reset(kvm_reset_vcpu, env); + } + return; err_fd: close(env->kvm_fd); @@ -476,8 +296,9 @@ { #ifdef KVM_CAP_SET_BOOT_CPU_ID int r = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, KVM_CAP_SET_BOOT_CPU_ID); - if (r > 0) + if (r > 0) { return kvm_vm_ioctl(kvm_state, KVM_SET_BOOT_CPU_ID, id); + } return -ENOSYS; #else return -ENOSYS; @@ -532,8 +353,9 @@ #if defined(KVM_CAP_IRQ_INJECT_STATUS) && defined(KVM_IRQ_LINE_STATUS) r = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, KVM_CAP_IRQ_INJECT_STATUS); - if (r > 0) + if (r > 0) { kvm->irqchip_inject_ioctl = KVM_IRQ_LINE_STATUS; + } #endif kvm->irqchip_in_kernel = 1; } else @@ -546,157 +368,27 @@ int kvm_create(kvm_context_t kvm, unsigned long phys_mem_bytes, void **vm_mem) { - int r; + int r, i; r = kvm_create_vm(kvm); - if (r < 0) + if (r < 0) { return r; + } r = kvm_arch_create(kvm, phys_mem_bytes, vm_mem); - if (r < 0) - return r; - init_slots(); - r = kvm_create_default_phys_mem(kvm, phys_mem_bytes, vm_mem); - if (r < 0) - return r; - kvm_create_irqchip(kvm); - - return 0; -} - - -int kvm_register_phys_mem(kvm_context_t kvm, - unsigned long phys_start, void *userspace_addr, - unsigned long len, int log) -{ - - struct kvm_userspace_memory_region memory = { - .memory_size = len, - .guest_phys_addr = phys_start, - .userspace_addr = (unsigned long) (uintptr_t) userspace_addr, - .flags = log ? KVM_MEM_LOG_DIRTY_PAGES : 0, - }; - int r; - - memory.slot = get_free_slot(kvm); - DPRINTF - ("memory: gpa: %llx, size: %llx, uaddr: %llx, slot: %x, flags: %x\n", - memory.guest_phys_addr, memory.memory_size, memory.userspace_addr, - memory.slot, memory.flags); - r = kvm_vm_ioctl(kvm_state, KVM_SET_USER_MEMORY_REGION, &memory); if (r < 0) { - fprintf(stderr, "create_userspace_phys_mem: %s\n", strerror(-r)); - return -1; - } - register_slot(memory.slot, memory.guest_phys_addr, memory.memory_size, - memory.userspace_addr, memory.flags); - return 0; -} - - -/* destroy/free a whole slot. - * phys_start, len and slot are the params passed to kvm_create_phys_mem() - */ -void kvm_destroy_phys_mem(kvm_context_t kvm, unsigned long phys_start, - unsigned long len) -{ - int slot; - int r; - struct kvm_userspace_memory_region memory = { - .memory_size = 0, - .guest_phys_addr = phys_start, - .userspace_addr = 0, - .flags = 0, - }; - - slot = get_slot(phys_start); - - if ((slot >= KVM_MAX_NUM_MEM_REGIONS) || (slot == -1)) { - fprintf(stderr, "BUG: %s: invalid parameters (slot=%d)\n", __FUNCTION__, - slot); - return; + return r; } - if (phys_start != slots[slot].phys_addr) { - fprintf(stderr, - "WARNING: %s: phys_start is 0x%lx expecting 0x%lx\n", - __FUNCTION__, phys_start, slots[slot].phys_addr); - phys_start = slots[slot].phys_addr; + for (i = 0; i < ARRAY_SIZE(kvm_state->slots); i++) { + kvm_state->slots[i].slot = i; } - memory.slot = slot; - DPRINTF("slot %d start %llx len %llx flags %x\n", - memory.slot, memory.guest_phys_addr, memory.memory_size, - memory.flags); - r = kvm_vm_ioctl(kvm_state, KVM_SET_USER_MEMORY_REGION, &memory); + r = kvm_create_default_phys_mem(kvm, phys_mem_bytes, vm_mem); if (r < 0) { - fprintf(stderr, "destroy_userspace_phys_mem: %s", strerror(-r)); - return; - } - - free_slot(memory.slot); -} - -void kvm_unregister_memory_area(kvm_context_t kvm, uint64_t phys_addr, - unsigned long size) -{ - - int slot = get_container_slot(phys_addr, size); - - if (slot != -1) { - DPRINTF("Unregistering memory region %" PRIx64 " (%lx)\n", phys_addr, size); - kvm_destroy_phys_mem(kvm, phys_addr, size); - return; - } -} - -static int kvm_get_map(kvm_context_t kvm, int ioctl_num, int slot, void *buf) -{ - int r; - struct kvm_dirty_log log = { - .slot = slot, - }; - - log.dirty_bitmap = buf; - - r = kvm_vm_ioctl(kvm_state, ioctl_num, &log); - if (r < 0) return r; - return 0; -} - -int kvm_get_dirty_pages(kvm_context_t kvm, unsigned long phys_addr, void *buf) -{ - int slot; - - slot = get_slot(phys_addr); - return kvm_get_map(kvm, KVM_GET_DIRTY_LOG, slot, buf); -} + } -int kvm_get_dirty_pages_range(kvm_context_t kvm, unsigned long phys_addr, - unsigned long len, void *opaque, - int (*cb)(unsigned long start, - unsigned long len, void *bitmap, - void *opaque)) -{ - int i; - int r; - unsigned long end_addr = phys_addr + len; - void *buf; + kvm_create_irqchip(kvm); - for (i = 0; i < KVM_MAX_NUM_MEM_REGIONS; ++i) { - if ((slots[i].len && (uint64_t) slots[i].phys_addr >= phys_addr) - && ((uint64_t) slots[i].phys_addr + slots[i].len <= end_addr)) { - buf = qemu_malloc(BITMAP_SIZE(slots[i].len)); - r = kvm_get_map(kvm, KVM_GET_DIRTY_LOG, i, buf); - if (r) { - qemu_free(buf); - return r; - } - r = cb(slots[i].phys_addr, slots[i].len, buf, opaque); - qemu_free(buf); - if (r) - return r; - } - } return 0; } @@ -707,13 +399,15 @@ struct kvm_irq_level event; int r; - if (!kvm->irqchip_in_kernel) + if (!kvm->irqchip_in_kernel) { return 0; + } event.level = level; event.irq = irq; r = kvm_vm_ioctl(kvm_state, kvm->irqchip_inject_ioctl, &event); - if (r < 0) + if (r < 0) { perror("kvm_set_irq_level"); + } if (status) { #ifdef KVM_CAP_IRQ_INJECT_STATUS @@ -731,8 +425,9 @@ { int r; - if (!kvm->irqchip_in_kernel) + if (!kvm->irqchip_in_kernel) { return 0; + } r = kvm_vm_ioctl(kvm_state, KVM_GET_IRQCHIP, chip); if (r < 0) { perror("kvm_get_irqchip\n"); @@ -744,8 +439,9 @@ { int r; - if (!kvm->irqchip_in_kernel) + if (!kvm->irqchip_in_kernel) { return 0; + } r = kvm_vm_ioctl(kvm_state, KVM_SET_IRQCHIP, chip); if (r < 0) { perror("kvm_set_irqchip\n"); @@ -776,34 +472,15 @@ return kvm_vcpu_ioctl(env, KVM_SET_REGS, regs); } -int kvm_get_fpu(CPUState *env, struct kvm_fpu *fpu) -{ - return kvm_vcpu_ioctl(env, KVM_GET_FPU, fpu); -} - -int kvm_set_fpu(CPUState *env, struct kvm_fpu *fpu) -{ - return kvm_vcpu_ioctl(env, KVM_SET_FPU, fpu); -} - -int kvm_get_sregs(CPUState *env, struct kvm_sregs *sregs) -{ - return kvm_vcpu_ioctl(env, KVM_GET_SREGS, sregs); -} - -int kvm_set_sregs(CPUState *env, struct kvm_sregs *sregs) -{ - return kvm_vcpu_ioctl(env, KVM_SET_SREGS, sregs); -} - #ifdef KVM_CAP_MP_STATE int kvm_get_mpstate(CPUState *env, struct kvm_mp_state *mp_state) { int r; r = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, KVM_CAP_MP_STATE); - if (r > 0) + if (r > 0) { return kvm_vcpu_ioctl(env, KVM_GET_MP_STATE, mp_state); + } return -ENOSYS; } @@ -812,8 +489,9 @@ int r; r = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, KVM_CAP_MP_STATE); - if (r > 0) + if (r > 0) { return kvm_vcpu_ioctl(env, KVM_SET_MP_STATE, mp_state); + } return -ENOSYS; } #endif @@ -825,8 +503,9 @@ void *data = kvm_run->mmio.data; /* hack: Red Hat 7.1 generates these weird accesses. */ - if ((addr > 0xa0000 - 4 && addr <= 0xa0000) && kvm_run->mmio.len == 3) + if ((addr > 0xa0000 - 4 && addr <= 0xa0000) && kvm_run->mmio.len == 3) { return 0; + } cpu_physical_memory_rw(addr, data, kvm_run->mmio.len, kvm_run->mmio.is_write); return 0; @@ -864,11 +543,6 @@ { kvm_arch_pre_run(env, env->kvm_run); - if (env->kvm_cpu_state.regs_modified) { - kvm_arch_put_registers(env); - env->kvm_cpu_state.regs_modified = 0; - } - pthread_mutex_unlock(&qemu_mutex); return 0; } @@ -878,29 +552,6 @@ return env->kvm_run->ready_for_interrupt_injection; } -static int kvm_handle_internal_error(kvm_context_t kvm, - CPUState *env, - struct kvm_run *run) -{ - fprintf(stderr, "KVM internal error. Suberror: %d\n", - run->internal.suberror); -#ifdef KVM_CAP_INTERNAL_ERROR_DATA - if (kvm_check_extension(kvm_state, KVM_CAP_INTERNAL_ERROR_DATA)) { - int i; - - for (i = 0; i < run->internal.ndata; ++i) { - fprintf(stderr, "extra data[%d]: %"PRIx64"\n", - i, (uint64_t)run->internal.data[i]); - } - } -#endif - kvm_show_regs(env); - if (run->internal.suberror == KVM_INTERNAL_ERROR_EMULATION) - fprintf(stderr, "emulation failure, check dmesg for details\n"); - vm_stop(0); - return 1; -} - int kvm_run(CPUState *env) { int r; @@ -909,15 +560,25 @@ int fd = env->kvm_fd; again: + if (env->kvm_vcpu_dirty) { + kvm_arch_load_regs(env, KVM_PUT_RUNTIME_STATE); + env->kvm_vcpu_dirty = 0; + } push_nmi(kvm); #if !defined(__s390__) - if (!kvm->irqchip_in_kernel) + if (!kvm->irqchip_in_kernel) { run->request_interrupt_window = kvm_arch_try_push_interrupts(env); + } #endif r = pre_kvm_run(kvm, env); - if (r) + if (r) { return r; + } + if (env->exit_request) { + env->exit_request = 0; + pthread_kill(env->kvm_cpu_state.thread, SIG_IPI); + } r = ioctl(fd, KVM_RUN, 0); if (r == -1 && errno != EINTR && errno != EAGAIN) { @@ -929,19 +590,7 @@ post_kvm_run(kvm, env); -#if defined(KVM_CAP_COALESCED_MMIO) - if (kvm_state->coalesced_mmio) { - struct kvm_coalesced_mmio_ring *ring = - (void *) run + kvm_state->coalesced_mmio * PAGE_SIZE; - while (ring->first != ring->last) { - cpu_physical_memory_rw(ring->coalesced_mmio[ring->first].phys_addr, - &ring->coalesced_mmio[ring->first].data[0], - ring->coalesced_mmio[ring->first].len, 1); - smp_wmb(); - ring->first = (ring->first + 1) % KVM_COALESCED_MMIO_MAX; - } - } -#endif + kvm_flush_coalesced_mmio_buffer(); #if !defined(__s390__) if (r == -1) { @@ -955,7 +604,7 @@ r = handle_unhandled(run->hw.hardware_exit_reason); break; case KVM_EXIT_FAIL_ENTRY: - r = handle_unhandled(run->fail_entry.hardware_entry_failure_reason); + r = handle_failed_vmentry(run->fail_entry.hardware_entry_failure_reason); break; case KVM_EXIT_EXCEPTION: fprintf(stderr, "exception %d (%x)\n", run->ex.exception, @@ -995,7 +644,8 @@ break; #endif case KVM_EXIT_INTERNAL_ERROR: - r = kvm_handle_internal_error(kvm, env, run); + kvm_handle_internal_error(env, run); + r = 1; break; default: if (kvm_arch_run(env)) { @@ -1006,9 +656,10 @@ break; } } - more: - if (!r) +more: + if (!r) { goto again; + } return r; } @@ -1020,35 +671,6 @@ return kvm_vcpu_ioctl(env, KVM_INTERRUPT, &intr); } -#ifdef KVM_CAP_SET_GUEST_DEBUG -int kvm_set_guest_debug(CPUState *env, struct kvm_guest_debug *dbg) -{ - return kvm_vcpu_ioctl(env, KVM_SET_GUEST_DEBUG, dbg); -} -#endif - -int kvm_set_signal_mask(CPUState *env, const sigset_t *sigset) -{ - struct kvm_signal_mask *sigmask; - int r; - - if (!sigset) { - return kvm_vcpu_ioctl(env, KVM_SET_SIGNAL_MASK, NULL); - } - sigmask = qemu_malloc(sizeof(*sigmask) + sizeof(*sigset)); - - sigmask->len = 8; - memcpy(sigmask->sigset, sigset, sizeof(*sigset)); - r = kvm_vcpu_ioctl(env, KVM_SET_SIGNAL_MASK, sigmask); - free(sigmask); - return r; -} - -int kvm_pit_in_kernel(kvm_context_t kvm) -{ - return kvm->pit_in_kernel; -} - int kvm_inject_nmi(CPUState *env) { #ifdef KVM_CAP_USER_NMI @@ -1118,20 +740,6 @@ } #endif -int kvm_destroy_memory_region_works(kvm_context_t kvm) -{ - int ret = 0; - -#ifdef KVM_CAP_DESTROY_MEMORY_REGION_WORKS - ret = - kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, - KVM_CAP_DESTROY_MEMORY_REGION_WORKS); - if (ret <= 0) - ret = 0; -#endif - return ret; -} - int kvm_reinject_control(kvm_context_t kvm, int pit_reinject) { #ifdef KVM_CAP_REINJECT_CONTROL @@ -1148,7 +756,7 @@ return -ENOSYS; } -int kvm_has_gsi_routing(kvm_context_t kvm) +int kvm_has_gsi_routing(void) { int r = 0; @@ -1167,9 +775,11 @@ #endif } -int kvm_clear_gsi_routes(kvm_context_t kvm) +int kvm_clear_gsi_routes(void) { #ifdef KVM_CAP_IRQ_ROUTING + kvm_context_t kvm = kvm_context; + kvm->irq_routes->nr = 0; return 0; #else @@ -1177,23 +787,25 @@ #endif } -int kvm_add_routing_entry(kvm_context_t kvm, - struct kvm_irq_routing_entry *entry) +int kvm_add_routing_entry(struct kvm_irq_routing_entry *entry) { #ifdef KVM_CAP_IRQ_ROUTING + kvm_context_t kvm = kvm_context; struct kvm_irq_routing *z; struct kvm_irq_routing_entry *new; int n, size; if (kvm->irq_routes->nr == kvm->nr_allocated_irq_routes) { n = kvm->nr_allocated_irq_routes * 2; - if (n < 64) + if (n < 64) { n = 64; + } size = sizeof(struct kvm_irq_routing); size += n * sizeof(*new); z = realloc(kvm->irq_routes, size); - if (!z) + if (!z) { return -ENOMEM; + } kvm->nr_allocated_irq_routes = n; kvm->irq_routes = z; } @@ -1213,7 +825,7 @@ #endif } -int kvm_add_irq_route(kvm_context_t kvm, int gsi, int irqchip, int pin) +int kvm_add_irq_route(int gsi, int irqchip, int pin) { #ifdef KVM_CAP_IRQ_ROUTING struct kvm_irq_routing_entry e; @@ -1223,16 +835,16 @@ e.flags = 0; e.u.irqchip.irqchip = irqchip; e.u.irqchip.pin = pin; - return kvm_add_routing_entry(kvm, &e); + return kvm_add_routing_entry(&e); #else return -ENOSYS; #endif } -int kvm_del_routing_entry(kvm_context_t kvm, - struct kvm_irq_routing_entry *entry) +int kvm_del_routing_entry(struct kvm_irq_routing_entry *entry) { #ifdef KVM_CAP_IRQ_ROUTING + kvm_context_t kvm = kvm_context; struct kvm_irq_routing_entry *e, *p; int i, gsi, found = 0; @@ -1275,8 +887,9 @@ if (e->gsi == gsi) break; } - if (i == kvm->irq_routes->nr) + if (i == kvm->irq_routes->nr) { clear_gsi(kvm, gsi); + } return 0; } @@ -1288,11 +901,11 @@ #endif } -int kvm_update_routing_entry(kvm_context_t kvm, - struct kvm_irq_routing_entry *entry, +int kvm_update_routing_entry(struct kvm_irq_routing_entry *entry, struct kvm_irq_routing_entry *newentry) { #ifdef KVM_CAP_IRQ_ROUTING + kvm_context_t kvm = kvm_context; struct kvm_irq_routing_entry *e; int i; @@ -1332,7 +945,7 @@ #endif } -int kvm_del_irq_route(kvm_context_t kvm, int gsi, int irqchip, int pin) +int kvm_del_irq_route(int gsi, int irqchip, int pin) { #ifdef KVM_CAP_IRQ_ROUTING struct kvm_irq_routing_entry e; @@ -1342,15 +955,17 @@ e.flags = 0; e.u.irqchip.irqchip = irqchip; e.u.irqchip.pin = pin; - return kvm_del_routing_entry(kvm, &e); + return kvm_del_routing_entry(&e); #else return -ENOSYS; #endif } -int kvm_commit_irq_routes(kvm_context_t kvm) +int kvm_commit_irq_routes(void) { #ifdef KVM_CAP_IRQ_ROUTING + kvm_context_t kvm = kvm_context; + kvm->irq_routes->flags = 0; return kvm_vm_ioctl(kvm_state, KVM_SET_GSI_ROUTING, kvm->irq_routes); #else @@ -1358,16 +973,18 @@ #endif } -int kvm_get_irq_route_gsi(kvm_context_t kvm) +int kvm_get_irq_route_gsi(void) { + kvm_context_t kvm = kvm_context; int i, bit; uint32_t *buf = kvm->used_gsi_bitmap; /* Return the lowest unused GSI in the bitmap */ for (i = 0; i < kvm->max_gsi / 32; i++) { bit = ffs(~buf[i]); - if (!bit) + if (!bit) { continue; + } return bit - 1 + i * 32; } @@ -1375,21 +992,65 @@ return -ENOSPC; } -#ifdef KVM_CAP_DEVICE_MSIX -int kvm_assign_set_msix_nr(kvm_context_t kvm, - struct kvm_assigned_msix_nr *msix_nr) +static void kvm_msix_routing_entry(struct kvm_irq_routing_entry *e, + uint32_t gsi, uint32_t addr_lo, + uint32_t addr_hi, uint32_t data) + { - return kvm_vm_ioctl(kvm_state, KVM_ASSIGN_SET_MSIX_NR, msix_nr); + e->gsi = gsi; + e->type = KVM_IRQ_ROUTING_MSI; + e->flags = 0; + e->u.msi.address_lo = addr_lo; + e->u.msi.address_hi = addr_hi; + e->u.msi.data = data; } -int kvm_assign_set_msix_entry(kvm_context_t kvm, - struct kvm_assigned_msix_entry *entry) +int kvm_add_msix(uint32_t gsi, uint32_t addr_lo, + uint32_t addr_hi, uint32_t data) { - return kvm_vm_ioctl(kvm_state, KVM_ASSIGN_SET_MSIX_ENTRY, entry); + struct kvm_irq_routing_entry e; + + kvm_msix_routing_entry(&e, gsi, addr_lo, addr_hi, data); + return kvm_add_routing_entry(&e); } -#endif -#if defined(KVM_CAP_IRQFD) && defined(CONFIG_EVENTFD) +int kvm_del_msix(uint32_t gsi, uint32_t addr_lo, + uint32_t addr_hi, uint32_t data) +{ + struct kvm_irq_routing_entry e; + + kvm_msix_routing_entry(&e, gsi, addr_lo, addr_hi, data); + return kvm_del_routing_entry(&e); +} + +int kvm_update_msix(uint32_t old_gsi, uint32_t old_addr_lo, + uint32_t old_addr_hi, uint32_t old_data, + uint32_t new_gsi, uint32_t new_addr_lo, + uint32_t new_addr_hi, uint32_t new_data) +{ + struct kvm_irq_routing_entry e1, e2; + + kvm_msix_routing_entry(&e1, old_gsi, old_addr_lo, old_addr_hi, old_data); + kvm_msix_routing_entry(&e2, new_gsi, new_addr_lo, new_addr_hi, new_data); + return kvm_update_routing_entry(&e1, &e2); +} + + +#ifdef KVM_CAP_DEVICE_MSIX +int kvm_assign_set_msix_nr(kvm_context_t kvm, + struct kvm_assigned_msix_nr *msix_nr) +{ + return kvm_vm_ioctl(kvm_state, KVM_ASSIGN_SET_MSIX_NR, msix_nr); +} + +int kvm_assign_set_msix_entry(kvm_context_t kvm, + struct kvm_assigned_msix_entry *entry) +{ + return kvm_vm_ioctl(kvm_state, KVM_ASSIGN_SET_MSIX_ENTRY, entry); +} +#endif + +#if defined(KVM_CAP_IRQFD) && defined(CONFIG_EVENTFD) #include @@ -1413,8 +1074,9 @@ return -ENOENT; fd = eventfd(0, 0); - if (fd < 0) + if (fd < 0) { return -errno; + } r = _kvm_irqfd(kvm, fd, gsi, 0); if (r < 0) { @@ -1433,7 +1095,7 @@ } #endif /* KVM_CAP_IRQFD */ -static inline unsigned long kvm_get_thread_id(void) +unsigned long kvm_get_thread_id(void) { return syscall(SYS_gettid); } @@ -1450,12 +1112,6 @@ { } -static void hardware_memory_error(void) -{ - fprintf(stderr, "Hardware memory error!\n"); - exit(1); -} - static void sigbus_reraise(void) { sigset_t set; @@ -1476,43 +1132,11 @@ static void sigbus_handler(int n, struct qemu_signalfd_siginfo *siginfo, void *ctx) { -#if defined(KVM_CAP_MCE) && defined(TARGET_I386) - if (first_cpu->mcg_cap && siginfo->ssi_addr - && siginfo->ssi_code == BUS_MCEERR_AO) { - uint64_t status; - unsigned long paddr; - CPUState *cenv; - - /* Hope we are lucky for AO MCE */ - if (do_qemu_ram_addr_from_host((void *)(intptr_t)siginfo->ssi_addr, - &paddr)) { - fprintf(stderr, "Hardware memory error for memory used by " - "QEMU itself instead of guest system!: %llx\n", - (unsigned long long)siginfo->ssi_addr); - return; - } - status = MCI_STATUS_VAL | MCI_STATUS_UC | MCI_STATUS_EN - | MCI_STATUS_MISCV | MCI_STATUS_ADDRV | MCI_STATUS_S - | 0xc0; - kvm_inject_x86_mce(first_cpu, 9, status, - MCG_STATUS_MCIP | MCG_STATUS_RIPV, paddr, - (MCM_ADDR_PHYS << 6) | 0xc, 1); - for (cenv = first_cpu->next_cpu; cenv != NULL; cenv = cenv->next_cpu) - kvm_inject_x86_mce(cenv, 1, MCI_STATUS_VAL | MCI_STATUS_UC, - MCG_STATUS_MCIP | MCG_STATUS_RIPV, 0, 0, 1); - } else -#endif - { - if (siginfo->ssi_code == BUS_MCEERR_AO) - return; - else if (siginfo->ssi_code == BUS_MCEERR_AR) - hardware_memory_error(); - else - sigbus_reraise(); - } + if (kvm_on_sigbus(siginfo->ssi_code, (void *)(intptr_t)siginfo->ssi_addr)) + sigbus_reraise(); } -static void on_vcpu(CPUState *env, void (*func)(void *data), void *data) +void on_vcpu(CPUState *env, void (*func)(void *data), void *data) { struct qemu_work_item wi; @@ -1523,37 +1147,48 @@ wi.func = func; wi.data = data; - if (!env->kvm_cpu_state.queued_work_first) + if (!env->kvm_cpu_state.queued_work_first) { env->kvm_cpu_state.queued_work_first = &wi; - else + } else { env->kvm_cpu_state.queued_work_last->next = &wi; + } env->kvm_cpu_state.queued_work_last = &wi; wi.next = NULL; wi.done = false; pthread_kill(env->kvm_cpu_state.thread, SIG_IPI); - while (!wi.done) + while (!wi.done) { qemu_cond_wait(&qemu_work_cond); -} - -void kvm_arch_get_registers(CPUState *env) -{ - kvm_arch_save_regs(env); + } } static void do_kvm_cpu_synchronize_state(void *_env) { CPUState *env = _env; - if (!env->kvm_cpu_state.regs_modified) { - kvm_arch_get_registers(env); - env->kvm_cpu_state.regs_modified = 1; + + if (!env->kvm_vcpu_dirty) { + kvm_arch_save_regs(env); + env->kvm_vcpu_dirty = 1; } } void kvm_cpu_synchronize_state(CPUState *env) { - if (!env->kvm_cpu_state.regs_modified) + if (!env->kvm_vcpu_dirty) { on_vcpu(env, do_kvm_cpu_synchronize_state, env); + } +} + +void kvm_cpu_synchronize_post_reset(CPUState *env) +{ + kvm_arch_load_regs(env, KVM_PUT_RESET_STATE); + env->kvm_vcpu_dirty = 0; +} + +void kvm_cpu_synchronize_post_init(CPUState *env) +{ + kvm_arch_load_regs(env, KVM_PUT_FULL_STATE); + env->kvm_vcpu_dirty = 0; } static void inject_interrupt(void *data) @@ -1571,79 +1206,26 @@ int signal = 0; if (env) { - if (!current_env || !current_env->created) + if (!current_env || !current_env->created) { signal = 1; + } /* * Testing for created here is really redundant */ if (current_env && current_env->created && - env != current_env && !env->kvm_cpu_state.signalled) + env != current_env && !env->kvm_cpu_state.signalled) { signal = 1; + } if (signal) { env->kvm_cpu_state.signalled = 1; - if (env->kvm_cpu_state.thread) + if (env->kvm_cpu_state.thread) { pthread_kill(env->kvm_cpu_state.thread, SIG_IPI); + } } } } -static void kvm_do_load_registers(void *_env) -{ - CPUState *env = _env; - - kvm_arch_load_regs(env); -} - -void kvm_load_registers(CPUState *env) -{ - if (kvm_enabled() && qemu_system_ready) - on_vcpu(env, kvm_do_load_registers, env); -} - -static void kvm_do_save_registers(void *_env) -{ - CPUState *env = _env; - - kvm_arch_save_regs(env); -} - -void kvm_save_registers(CPUState *env) -{ - if (kvm_enabled()) - on_vcpu(env, kvm_do_save_registers, env); -} - -static void kvm_do_load_mpstate(void *_env) -{ - CPUState *env = _env; - - kvm_arch_load_mpstate(env); -} - -void kvm_load_mpstate(CPUState *env) -{ - if (kvm_enabled() && qemu_system_ready && kvm_vcpu_inited(env)) - on_vcpu(env, kvm_do_load_mpstate, env); -} - -static void kvm_do_save_mpstate(void *_env) -{ - CPUState *env = _env; - - kvm_arch_save_mpstate(env); -#ifdef KVM_CAP_MP_STATE - if (kvm_irqchip_in_kernel()) - env->halted = (env->mp_state == KVM_MP_STATE_HALTED); -#endif -} - -void kvm_save_mpstate(CPUState *env) -{ - if (kvm_enabled()) - on_vcpu(env, kvm_do_save_mpstate, env); -} - int kvm_cpu_exec(CPUState *env) { int r; @@ -1657,7 +1239,7 @@ return 0; } -static int is_cpu_stopped(CPUState *env) +int kvm_cpu_is_stopped(CPUState *env) { return !vm_running || env->stopped; } @@ -1666,8 +1248,9 @@ { struct qemu_work_item *wi; - if (!env->kvm_cpu_state.queued_work_first) + if (!env->kvm_cpu_state.queued_work_first) { return; + } while ((wi = env->kvm_cpu_state.queued_work_first)) { env->kvm_cpu_state.queued_work_first = wi->next; @@ -1678,60 +1261,6 @@ pthread_cond_broadcast(&qemu_work_cond); } -static void kvm_on_sigbus(CPUState *env, siginfo_t *siginfo) -{ -#if defined(KVM_CAP_MCE) && defined(TARGET_I386) - struct kvm_x86_mce mce = { - .bank = 9, - }; - unsigned long paddr; - int r; - - if (env->mcg_cap && siginfo->si_addr - && (siginfo->si_code == BUS_MCEERR_AR - || siginfo->si_code == BUS_MCEERR_AO)) { - if (siginfo->si_code == BUS_MCEERR_AR) { - /* Fake an Intel architectural Data Load SRAR UCR */ - mce.status = MCI_STATUS_VAL | MCI_STATUS_UC | MCI_STATUS_EN - | MCI_STATUS_MISCV | MCI_STATUS_ADDRV | MCI_STATUS_S - | MCI_STATUS_AR | 0x134; - mce.misc = (MCM_ADDR_PHYS << 6) | 0xc; - mce.mcg_status = MCG_STATUS_MCIP | MCG_STATUS_EIPV; - } else { - /* Fake an Intel architectural Memory scrubbing UCR */ - mce.status = MCI_STATUS_VAL | MCI_STATUS_UC | MCI_STATUS_EN - | MCI_STATUS_MISCV | MCI_STATUS_ADDRV | MCI_STATUS_S - | 0xc0; - mce.misc = (MCM_ADDR_PHYS << 6) | 0xc; - mce.mcg_status = MCG_STATUS_MCIP | MCG_STATUS_RIPV; - } - if (do_qemu_ram_addr_from_host((void *)siginfo->si_addr, &paddr)) { - fprintf(stderr, "Hardware memory error for memory used by " - "QEMU itself instaed of guest system!\n"); - /* Hope we are lucky for AO MCE */ - if (siginfo->si_code == BUS_MCEERR_AO) - return; - else - hardware_memory_error(); - } - mce.addr = paddr; - r = kvm_set_mce(env, &mce); - if (r < 0) { - fprintf(stderr, "kvm_set_mce: %s\n", strerror(errno)); - abort(); - } - } else -#endif - { - if (siginfo->si_code == BUS_MCEERR_AO) - return; - else if (siginfo->si_code == BUS_MCEERR_AR) - hardware_memory_error(); - else - sigbus_reraise(); - } -} - static void kvm_main_loop_wait(CPUState *env, int timeout) { struct timespec ts; @@ -1761,7 +1290,8 @@ switch (r) { case SIGBUS: - kvm_on_sigbus(env, &siginfo); + if (kvm_on_sigbus_vcpu(env, siginfo.si_code, siginfo.si_addr)) + sigbus_reraise(); break; default: break; @@ -1791,8 +1321,9 @@ CPUState *penv = first_cpu; while (penv) { - if (penv->stop) + if (penv->stop) { return 0; + } penv = (CPUState *) penv->next_cpu; } @@ -1815,8 +1346,9 @@ penv = (CPUState *) penv->next_cpu; } - while (!all_threads_paused()) + while (!all_threads_paused()) { qemu_cond_wait(&qemu_pause_cond); + } } static void resume_all_threads(void) @@ -1835,10 +1367,11 @@ static void kvm_vm_state_change_handler(void *context, int running, int reason) { - if (running) + if (running) { resume_all_threads(); - else + } else { pause_all_threads(); + } } static void setup_kernel_sigmask(CPUState *env) @@ -1860,17 +1393,10 @@ static void qemu_kvm_system_reset(void) { - CPUState *penv = first_cpu; - pause_all_threads(); qemu_system_reset(); - while (penv) { - kvm_arch_cpu_reset(penv); - penv = (CPUState *) penv->next_cpu; - } - resume_all_threads(); } @@ -1884,7 +1410,7 @@ static int kvm_main_loop_cpu(CPUState *env) { while (1) { - int run_cpu = !is_cpu_stopped(env); + int run_cpu = !kvm_cpu_is_stopped(env); if (run_cpu && !kvm_irqchip_in_kernel()) { process_irqchip_events(env); run_cpu = !env->halted; @@ -1912,7 +1438,6 @@ env->thread_id = kvm_get_thread_id(); sigfillset(&signals); sigprocmask(SIG_BLOCK, &signals, NULL); - kvm_create_vcpu(env, env->cpu_index); #ifdef CONFIG_KVM_DEVICE_ASSIGNMENT /* do ioperm for io ports of assigned devices */ @@ -1920,22 +1445,20 @@ on_vcpu(env, kvm_arch_do_ioperm, data); #endif - setup_kernel_sigmask(env); - pthread_mutex_lock(&qemu_mutex); cpu_single_env = env; - kvm_arch_init_vcpu(env); - - kvm_arch_load_regs(env); + kvm_create_vcpu(env, env->cpu_index); + setup_kernel_sigmask(env); /* signal VCPU creation */ current_env->created = 1; pthread_cond_signal(&qemu_vcpu_cond); /* and wait for machine initialization */ - while (!qemu_system_ready) + while (!qemu_system_ready) { qemu_cond_wait(&qemu_system_cond); + } /* re-initialize cpu_single_env after re-acquiring qemu_mutex */ cpu_single_env = env; @@ -1944,12 +1467,15 @@ return NULL; } -void kvm_init_vcpu(CPUState *env) +int kvm_init_vcpu(CPUState *env) { pthread_create(&env->kvm_cpu_state.thread, NULL, ap_main_loop, env); - while (env->created == 0) + while (env->created == 0) { qemu_cond_wait(&qemu_vcpu_cond); + } + + return 0; } int kvm_vcpu_inited(CPUState *env) @@ -1989,41 +1515,10 @@ action.sa_flags = SA_SIGINFO; action.sa_sigaction = (void (*)(int, siginfo_t*, void*))sigbus_handler; sigaction(SIGBUS, &action, NULL); - prctl(PR_MCE_KILL, 1, 1); + prctl(PR_MCE_KILL, 1, 1, 0, 0); return 0; } -void qemu_kvm_notify_work(void) -{ - uint64_t value = 1; - char buffer[8]; - size_t offset = 0; - - if (io_thread_fd == -1) - return; - - memcpy(buffer, &value, sizeof(value)); - - while (offset < 8) { - ssize_t len; - - len = write(io_thread_fd, buffer + offset, 8 - offset); - if (len == -1 && errno == EINTR) - continue; - - /* In case we have a pipe, there is not reason to insist writing - * 8 bytes - */ - if (len == -1 && errno == EAGAIN) - break; - - if (len <= 0) - break; - - offset += len; - } -} - /* If we have signalfd, we mask out the signals we want to handle and then * use signalfd to listen for them. We rely on whatever the current signal * handler is to dispatch the signals when we receive them. @@ -2041,8 +1536,9 @@ len = read(fd, &info, sizeof(info)); } while (len == -1 && errno == EINTR); - if (len == -1 && errno == EAGAIN) + if (len == -1 && errno == EAGAIN) { break; + } if (len != sizeof(info)) { printf("read from sigfd returned %zd: %m\n", len); @@ -2050,56 +1546,23 @@ } sigaction(info.ssi_signo, NULL, &action); - if ((action.sa_flags & SA_SIGINFO) && action.sa_sigaction) + if ((action.sa_flags & SA_SIGINFO) && action.sa_sigaction) { action.sa_sigaction(info.ssi_signo, (siginfo_t *)&info, NULL); - else if (action.sa_handler) + } else if (action.sa_handler) { action.sa_handler(info.ssi_signo); - - } -} - -/* Used to break IO thread out of select */ -static void io_thread_wakeup(void *opaque) -{ - int fd = (unsigned long) opaque; - char buffer[4096]; - - /* Drain the pipe/(eventfd) */ - while (1) { - ssize_t len; - - len = read(fd, buffer, sizeof(buffer)); - if (len == -1 && errno == EINTR) - continue; - - if (len <= 0) - break; + } } } int kvm_main_loop(void) { - int fds[2]; sigset_t mask; int sigfd; io_thread = pthread_self(); qemu_system_ready = 1; - if (qemu_eventfd(fds) == -1) { - fprintf(stderr, "failed to create eventfd\n"); - return -errno; - } - - fcntl(fds[0], F_SETFL, O_NONBLOCK); - fcntl(fds[1], F_SETFL, O_NONBLOCK); - - qemu_set_fd_handler2(fds[0], NULL, io_thread_wakeup, NULL, - (void *)(unsigned long) fds[0]); - - io_thread_fd = fds[1]; - sigemptyset(&mask); sigaddset(&mask, SIGIO); sigaddset(&mask, SIGALRM); @@ -2123,34 +1586,33 @@ cpu_single_env = NULL; while (1) { - main_loop_wait(1000); + main_loop_wait(0); if (qemu_shutdown_requested()) { + monitor_protocol_event(QEVENT_SHUTDOWN, NULL); if (qemu_no_shutdown()) { vm_stop(0); - } else + } else { break; - } else if (qemu_powerdown_requested()) + } + } else if (qemu_powerdown_requested()) { + monitor_protocol_event(QEVENT_POWERDOWN, NULL); qemu_irq_raise(qemu_system_powerdown); - else if (qemu_reset_requested()) + } else if (qemu_reset_requested()) { qemu_kvm_system_reset(); - else if (kvm_debug_cpu_requested) { + } else if (kvm_debug_cpu_requested) { gdb_set_stop_cpu(kvm_debug_cpu_requested); vm_stop(EXCP_DEBUG); kvm_debug_cpu_requested = NULL; } } + bdrv_close_all(); pause_all_threads(); pthread_mutex_unlock(&qemu_mutex); return 0; } -#ifdef TARGET_I386 -static int destroy_region_works = 0; -#endif - - #if !defined(TARGET_I386) int kvm_arch_init_irq_routing(void) { @@ -2162,6 +1624,10 @@ static int kvm_create_context(void) { + static const char upgrade_note[] = + "Please upgrade to at least kernel 2.6.29 or recent kvm-kmod\n" + "(see http://sourceforge.net/projects/kvm).\n"; + int r; if (!kvm_irqchip) { @@ -2175,17 +1641,26 @@ return -1; } r = kvm_arch_qemu_create_context(); - if (r < 0) + if (r < 0) { kvm_finalize(kvm_state); + return -1; + } if (kvm_pit && !kvm_pit_reinject) { if (kvm_reinject_control(kvm_context, 0)) { fprintf(stderr, "failure to disable in-kernel PIT reinjection\n"); return -1; } } -#ifdef TARGET_I386 - destroy_region_works = kvm_destroy_memory_region_works(kvm_context); -#endif + + /* There was a nasty bug in < kvm-80 that prevents memory slots from being + * destroyed properly. Since we rely on this capability, refuse to work + * with any kernel without this capability. */ + if (!kvm_check_extension(kvm_state, KVM_CAP_DESTROY_MEMORY_REGION_WORKS)) { + fprintf(stderr, + "KVM kernel module broken (DESTROY_MEMORY_REGION).\n%s", + upgrade_note); + return -EINVAL; + } r = kvm_arch_init_irq_routing(); if (r < 0) { @@ -2197,6 +1672,23 @@ kvm_state->vcpu_events = kvm_check_extension(kvm_state, KVM_CAP_VCPU_EVENTS); #endif + kvm_state->debugregs = 0; +#ifdef KVM_CAP_DEBUGREGS + kvm_state->debugregs = kvm_check_extension(kvm_state, KVM_CAP_DEBUGREGS); +#endif + + kvm_state->xsave = 0; +#ifdef KVM_CAP_XSAVE + kvm_state->xsave = kvm_check_extension(kvm_state, KVM_CAP_XSAVE); +#endif + + kvm_state->xcrs = 0; +#ifdef KVM_CAP_XCRS + kvm_state->xcrs = kvm_check_extension(kvm_state, KVM_CAP_XCRS); +#endif + + kvm_state->many_ioeventfds = kvm_check_many_ioeventfds(); + kvm_init_ap(); if (kvm_irqchip) { if (!qemu_kvm_has_gsi_routing()) { @@ -2218,287 +1710,6 @@ return 0; } -#ifdef TARGET_I386 -static int must_use_aliases_source(target_phys_addr_t addr) -{ - if (destroy_region_works) - return false; - if (addr == 0xa0000 || addr == 0xa8000) - return true; - return false; -} - -static int must_use_aliases_target(target_phys_addr_t addr) -{ - if (destroy_region_works) - return false; - if (addr >= 0xe0000000 && addr < 0x100000000ull) - return true; - return false; -} - -static struct mapping { - target_phys_addr_t phys; - ram_addr_t ram; - ram_addr_t len; -} mappings[50]; -static int nr_mappings; - -static struct mapping *find_ram_mapping(ram_addr_t ram_addr) -{ - struct mapping *p; - - for (p = mappings; p < mappings + nr_mappings; ++p) { - if (p->ram <= ram_addr && ram_addr < p->ram + p->len) { - return p; - } - } - return NULL; -} - -static struct mapping *find_mapping(target_phys_addr_t start_addr) -{ - struct mapping *p; - - for (p = mappings; p < mappings + nr_mappings; ++p) { - if (p->phys <= start_addr && start_addr < p->phys + p->len) { - return p; - } - } - return NULL; -} - -static void drop_mapping(target_phys_addr_t start_addr) -{ - struct mapping *p = find_mapping(start_addr); - - if (p) - *p = mappings[--nr_mappings]; -} -#endif - -void kvm_set_phys_mem(target_phys_addr_t start_addr, ram_addr_t size, - ram_addr_t phys_offset) -{ - int r = 0; - unsigned long area_flags; -#ifdef TARGET_I386 - struct mapping *p; -#endif - - if (start_addr + size > phys_ram_size) { - phys_ram_size = start_addr + size; - } - - phys_offset &= ~IO_MEM_ROM; - area_flags = phys_offset & ~TARGET_PAGE_MASK; - - if (area_flags != IO_MEM_RAM) { -#ifdef TARGET_I386 - if (must_use_aliases_source(start_addr)) { - kvm_destroy_memory_alias(kvm_context, start_addr); - return; - } - if (must_use_aliases_target(start_addr)) - return; -#endif - while (size > 0) { - p = find_mapping(start_addr); - if (p) { - kvm_unregister_memory_area(kvm_context, p->phys, p->len); - drop_mapping(p->phys); - } - start_addr += TARGET_PAGE_SIZE; - if (size > TARGET_PAGE_SIZE) { - size -= TARGET_PAGE_SIZE; - } else { - size = 0; - } - } - return; - } - - r = kvm_is_containing_region(kvm_context, start_addr, size); - if (r) - return; - - if (area_flags >= TLB_MMIO) - return; - -#ifdef TARGET_I386 - if (must_use_aliases_source(start_addr)) { - p = find_ram_mapping(phys_offset); - if (p) { - kvm_create_memory_alias(kvm_context, start_addr, size, - p->phys + (phys_offset - p->ram)); - } - return; - } -#endif - - r = kvm_register_phys_mem(kvm_context, start_addr, - qemu_get_ram_ptr(phys_offset), size, 0); - if (r < 0) { - printf("kvm_cpu_register_physical_memory: failed\n"); - exit(1); - } -#ifdef TARGET_I386 - drop_mapping(start_addr); - p = &mappings[nr_mappings++]; - p->phys = start_addr; - p->ram = phys_offset; - p->len = size; -#endif - - return; -} - -int kvm_setup_guest_memory(void *area, unsigned long size) -{ - int ret = 0; - -#ifdef MADV_DONTFORK - if (kvm_enabled() && !kvm_has_sync_mmu()) - ret = madvise(area, size, MADV_DONTFORK); -#endif - - if (ret) - perror("madvise"); - - return ret; -} - -#ifdef KVM_CAP_SET_GUEST_DEBUG - -struct kvm_set_guest_debug_data { - struct kvm_guest_debug dbg; - int err; -}; - -static void kvm_invoke_set_guest_debug(void *data) -{ - struct kvm_set_guest_debug_data *dbg_data = data; - - if (cpu_single_env->kvm_cpu_state.regs_modified) { - kvm_arch_put_registers(cpu_single_env); - cpu_single_env->kvm_cpu_state.regs_modified = 0; - } - dbg_data->err = - kvm_set_guest_debug(cpu_single_env, - &dbg_data->dbg); -} - -int kvm_update_guest_debug(CPUState *env, unsigned long reinject_trap) -{ - struct kvm_set_guest_debug_data data; - - data.dbg.control = 0; - if (env->singlestep_enabled) - data.dbg.control = KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_SINGLESTEP; - - kvm_arch_update_guest_debug(env, &data.dbg); - data.dbg.control |= reinject_trap; - - on_vcpu(env, kvm_invoke_set_guest_debug, &data); - return data.err; -} - -#endif - -/* - * dirty pages logging - */ -/* FIXME: use unsigned long pointer instead of unsigned char */ -unsigned char *kvm_dirty_bitmap = NULL; -int kvm_physical_memory_set_dirty_tracking(int enable) -{ - int r = 0; - - if (!kvm_enabled()) - return 0; - - if (enable) { - if (!kvm_dirty_bitmap) { - unsigned bitmap_size = BITMAP_SIZE(phys_ram_size); - kvm_dirty_bitmap = qemu_malloc(bitmap_size); - r = kvm_dirty_pages_log_enable_all(kvm_context); - } - } else { - if (kvm_dirty_bitmap) { - r = kvm_dirty_pages_log_reset(kvm_context); - qemu_free(kvm_dirty_bitmap); - kvm_dirty_bitmap = NULL; - } - } - return r; -} - -/* get kvm's dirty pages bitmap and update qemu's */ -static int kvm_get_dirty_pages_log_range(unsigned long start_addr, - unsigned char *bitmap, - unsigned long offset, - unsigned long mem_size) -{ - unsigned int i, j, n = 0; - unsigned char c; - unsigned long page_number, addr, addr1; - ram_addr_t ram_addr; - unsigned int len = ((mem_size / TARGET_PAGE_SIZE) + 7) / 8; - - /* - * bitmap-traveling is faster than memory-traveling (for addr...) - * especially when most of the memory is not dirty. - */ - for (i = 0; i < len; i++) { - c = bitmap[i]; - while (c > 0) { - j = ffsl(c) - 1; - c &= ~(1u << j); - page_number = i * 8 + j; - addr1 = page_number * TARGET_PAGE_SIZE; - addr = offset + addr1; - ram_addr = cpu_get_physical_page_desc(addr); - cpu_physical_memory_set_dirty(ram_addr); - n++; - } - } - return 0; -} - -static int kvm_get_dirty_bitmap_cb(unsigned long start, unsigned long len, - void *bitmap, void *opaque) -{ - return kvm_get_dirty_pages_log_range(start, bitmap, start, len); -} - -/* - * get kvm's dirty pages bitmap and update qemu's - * we only care about physical ram, which resides in slots 0 and 3 - */ -int kvm_update_dirty_pages_log(void) -{ - int r = 0; - - - r = kvm_get_dirty_pages_range(kvm_context, 0, -1UL, NULL, - kvm_get_dirty_bitmap_cb); - return r; -} - -void kvm_qemu_log_memory(target_phys_addr_t start, target_phys_addr_t size, - int log) -{ - if (log) - kvm_dirty_pages_log_enable_slot(kvm_context, start, size); - else { -#ifdef TARGET_I386 - if (must_use_aliases_target(start)) - return; -#endif - kvm_dirty_pages_log_disable_slot(kvm_context, start, size); - } -} - #ifdef KVM_CAP_IRQCHIP int kvm_set_irq(int irq, int level, int *status) @@ -2508,18 +1719,13 @@ #endif -int qemu_kvm_get_dirty_pages(unsigned long phys_addr, void *buf) -{ - return kvm_get_dirty_pages(kvm_context, phys_addr, buf); -} - -void kvm_mutex_unlock(void) +static void kvm_mutex_unlock(void) { assert(!cpu_single_env); pthread_mutex_unlock(&qemu_mutex); } -void kvm_mutex_lock(void) +static void kvm_mutex_lock(void) { pthread_mutex_lock(&qemu_mutex); cpu_single_env = NULL; @@ -2527,14 +1733,16 @@ void qemu_mutex_unlock_iothread(void) { - if (kvm_enabled()) + if (kvm_enabled()) { kvm_mutex_unlock(); + } } void qemu_mutex_lock_iothread(void) { - if (kvm_enabled()) + if (kvm_enabled()) { kvm_mutex_lock(); + } } #ifdef CONFIG_KVM_DEVICE_ASSIGNMENT @@ -2562,108 +1770,15 @@ void kvm_ioperm(CPUState *env, void *data) { - if (kvm_enabled() && qemu_system_ready) + if (kvm_enabled() && qemu_system_ready) { on_vcpu(env, kvm_arch_do_ioperm, data); + } } #endif -int kvm_physical_sync_dirty_bitmap(target_phys_addr_t start_addr, - target_phys_addr_t end_addr) -{ -#ifndef TARGET_IA64 - -#ifdef TARGET_I386 - if (must_use_aliases_source(start_addr)) - return 0; -#endif - - kvm_get_dirty_pages_range(kvm_context, start_addr, - end_addr - start_addr, NULL, - kvm_get_dirty_bitmap_cb); -#endif - return 0; -} - -int kvm_log_start(target_phys_addr_t phys_addr, target_phys_addr_t len) -{ -#ifdef TARGET_I386 - if (must_use_aliases_source(phys_addr)) - return 0; -#endif - -#ifndef TARGET_IA64 - kvm_qemu_log_memory(phys_addr, len, 1); -#endif - return 0; -} - -int kvm_log_stop(target_phys_addr_t phys_addr, target_phys_addr_t len) -{ -#ifdef TARGET_I386 - if (must_use_aliases_source(phys_addr)) - return 0; -#endif - -#ifndef TARGET_IA64 - kvm_qemu_log_memory(phys_addr, len, 0); -#endif - return 0; -} - int kvm_set_boot_cpu_id(uint32_t id) { return kvm_set_boot_vcpu_id(kvm_context, id); } -#ifdef TARGET_I386 -#ifdef KVM_CAP_MCE -struct kvm_x86_mce_data { - CPUState *env; - struct kvm_x86_mce *mce; - int abort_on_error; -}; - -static void kvm_do_inject_x86_mce(void *_data) -{ - struct kvm_x86_mce_data *data = _data; - int r; - - r = kvm_set_mce(data->env, data->mce); - if (r < 0) { - perror("kvm_set_mce FAILED"); - if (data->abort_on_error) - abort(); - } -} -#endif - -void kvm_inject_x86_mce(CPUState *cenv, int bank, uint64_t status, - uint64_t mcg_status, uint64_t addr, uint64_t misc, - int abort_on_error) -{ -#ifdef KVM_CAP_MCE - struct kvm_x86_mce mce = { - .bank = bank, - .status = status, - .mcg_status = mcg_status, - .addr = addr, - .misc = misc, - }; - struct kvm_x86_mce_data data = { - .env = cenv, - .mce = &mce, - .abort_on_error = abort_on_error, - }; - - if (!cenv->mcg_cap) { - fprintf(stderr, "MCE support is not enabled!\n"); - return; - } - on_vcpu(cenv, kvm_do_inject_x86_mce, &data); -#else - if (abort_on_error) - abort(); -#endif -} -#endif diff -Nru qemu-kvm-0.12.5+noroms/qemu-kvm.h qemu-kvm-0.14.1/qemu-kvm.h --- qemu-kvm-0.12.5+noroms/qemu-kvm.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/qemu-kvm.h 2011-05-11 13:29:46.000000000 +0000 @@ -8,9 +8,7 @@ #ifndef THE_ORIGINAL_AND_TRUE_QEMU_KVM_H #define THE_ORIGINAL_AND_TRUE_QEMU_KVM_H -#ifndef QEMU_KVM_NO_CPU #include "cpu.h" -#endif #include #include @@ -66,8 +64,6 @@ int irqchip_inject_ioctl; /// do not create in-kernel pit if set int no_pit_creation; - /// in-kernel pit status - int pit_in_kernel; #ifdef KVM_CAP_IRQ_ROUTING struct kvm_irq_routing *irq_routes; int nr_allocated_irq_routes; @@ -94,8 +90,6 @@ int handle_halt(CPUState *env); -#ifndef QEMU_KVM_NO_CPU - int handle_shutdown(kvm_context_t kvm, CPUState *env); void post_kvm_run(kvm_context_t kvm, CPUState *env); int pre_kvm_run(kvm_context_t kvm, CPUState *env); @@ -103,31 +97,10 @@ int try_push_interrupts(kvm_context_t kvm); #if defined(__x86_64__) || defined(__i386__) -struct kvm_msr_list *kvm_get_msr_list(kvm_context_t); -int kvm_get_msrs(CPUState *env, struct kvm_msr_entry *msrs, int n); -int kvm_set_msrs(CPUState *env, struct kvm_msr_entry *msrs, int n); -int kvm_get_mce_cap_supported(kvm_context_t, uint64_t *mce_cap, - int *max_banks); -int kvm_setup_mce(CPUState *env, uint64_t *mcg_cap); struct kvm_x86_mce; -int kvm_set_mce(CPUState *env, struct kvm_x86_mce *mce); -#endif - #endif /*! - * \brief Create new KVM context - * - * This creates a new kvm_context. A KVM context is a small area of data that - * holds information about the KVM instance that gets created by this call.\n - * This should always be your first call to KVM. - * - * \param opaque Not used - * \return NULL on failure - */ -int kvm_init(int smp_cpus); - -/*! * \brief Disable the in-kernel IRQCHIP creation * * In-kernel irqchip is enabled by default. If userspace irqchip is to be used, @@ -231,69 +204,6 @@ * \return 0 on success */ int kvm_set_regs(CPUState *env, struct kvm_regs *regs); -/*! - * \brief Read VCPU fpu registers - * - * This gets the FPU registers from the VCPU and outputs them - * into a kvm_fpu structure - * - * \note This function returns a \b copy of the VCPUs registers.\n - * If you wish to modify the VCPU FPU registers, you should call kvm_set_fpu() - * - * \param kvm Pointer to the current kvm_context - * \param vcpu Which virtual CPU should get dumped - * \param fpu Pointer to a kvm_fpu which will be populated with the VCPUs - * fpu registers values - * \return 0 on success - */ -int kvm_get_fpu(CPUState *env, struct kvm_fpu *fpu); - -/*! - * \brief Write VCPU fpu registers - * - * This sets the FPU registers on the VCPU from a kvm_fpu structure - * - * \note When this function returns, the fpu pointer and the data it points to - * can be discarded - * \param kvm Pointer to the current kvm_context - * \param vcpu Which virtual CPU should get dumped - * \param fpu Pointer to a kvm_fpu which holds the new vcpu fpu state - * \return 0 on success - */ -int kvm_set_fpu(CPUState *env, struct kvm_fpu *fpu); - -/*! - * \brief Read VCPU system registers - * - * This gets the non-GP registers from the VCPU and outputs them - * into a kvm_sregs structure - * - * \note This function returns a \b copy of the VCPUs registers.\n - * If you wish to modify the VCPUs non-GP registers, you should call - * kvm_set_sregs() - * - * \param kvm Pointer to the current kvm_context - * \param vcpu Which virtual CPU should get dumped - * \param regs Pointer to a kvm_sregs which will be populated with the VCPUs - * registers values - * \return 0 on success - */ -int kvm_get_sregs(CPUState *env, struct kvm_sregs *regs); - -/*! - * \brief Write VCPU system registers - * - * This sets the non-GP registers on the VCPU from a kvm_sregs structure - * - * \note When this function returns, the regs pointer and the data it points to - * can be discarded - * \param kvm Pointer to the current kvm_context - * \param vcpu Which virtual CPU should get dumped - * \param regs Pointer to a kvm_sregs which will be populated with the VCPUs - * registers values - * \return 0 on success - */ -int kvm_set_sregs(CPUState *env, struct kvm_sregs *regs); #ifdef KVM_CAP_MP_STATE /*! @@ -307,18 +217,9 @@ * */ int kvm_set_mpstate(CPUState *env, struct kvm_mp_state *mp_state); -/*! - * * \brief Reset VCPU MP state - * - */ -static inline int kvm_reset_mpstate(CPUState *env) -{ - struct kvm_mp_state mp_state = {.mp_state = KVM_MP_STATE_UNINITIALIZED - }; - return kvm_set_mpstate(env, &mp_state); -} #endif +#if defined(__i386__) || defined(__x86_64__) /*! * \brief Simulate an external vectored interrupt * @@ -331,40 +232,6 @@ */ int kvm_inject_irq(CPUState *env, unsigned irq); -#ifdef KVM_CAP_SET_GUEST_DEBUG -int kvm_set_guest_debug(CPUState *env, struct kvm_guest_debug *dbg); -#endif - -#if defined(__i386__) || defined(__x86_64__) -/*! - * \brief Setup a vcpu's cpuid instruction emulation - * - * Set up a table of cpuid function to cpuid outputs.\n - * - * \param kvm Pointer to the current kvm_context - * \param vcpu Which virtual CPU should be initialized - * \param nent number of entries to be installed - * \param entries cpuid function entries table - * \return 0 on success, or -errno on error - */ -int kvm_setup_cpuid(CPUState *env, int nent, - struct kvm_cpuid_entry *entries); - -/*! - * \brief Setup a vcpu's cpuid instruction emulation - * - * Set up a table of cpuid function to cpuid outputs. - * This call replaces the older kvm_setup_cpuid interface by adding a few - * parameters to support cpuid functions that have sub-leaf values. - * - * \param kvm Pointer to the current kvm_context - * \param vcpu Which virtual CPU should be initialized - * \param nent number of entries to be installed - * \param entries cpuid function entries table - * \return 0 on success, or -errno on error - */ -int kvm_setup_cpuid2(CPUState *env, int nent, - struct kvm_cpuid_entry2 *entries); /*! * \brief Setting the number of shadow pages to be allocated to the vm @@ -385,21 +252,6 @@ #endif /*! - * \brief Set a vcpu's signal mask for guest mode - * - * A vcpu can have different signals blocked in guest mode and user mode. - * This allows guest execution to be interrupted on a signal, without requiring - * that the signal be delivered to a signal handler (the signal can be - * dequeued using sigwait(2). - * - * \param kvm Pointer to the current kvm_context - * \param vcpu Which virtual CPU should be initialized - * \param sigset signal mask for guest mode - * \return 0 on success, or -errno on error - */ -int kvm_set_signal_mask(CPUState *env, const sigset_t *sigset); - -/*! * \brief Dump VCPU registers * * This dumps some of the information that KVM has about a virtual CPU, namely: @@ -418,14 +270,11 @@ unsigned long len, int log, int writable); void kvm_destroy_phys_mem(kvm_context_t, unsigned long phys_start, unsigned long len); -void kvm_unregister_memory_area(kvm_context_t, uint64_t phys_start, - unsigned long len); int kvm_is_containing_region(kvm_context_t kvm, unsigned long phys_start, unsigned long size); int kvm_register_phys_mem(kvm_context_t kvm, unsigned long phys_start, void *userspace_addr, unsigned long len, int log); -int kvm_get_dirty_pages(kvm_context_t, unsigned long phys_addr, void *buf); int kvm_get_dirty_pages_range(kvm_context_t kvm, unsigned long phys_addr, unsigned long end_addr, void *opaque, int (*cb)(unsigned long start, @@ -437,23 +286,6 @@ uint32_t size); /*! - * \brief Create a memory alias - * - * Aliases a portion of physical memory to another portion. If the guest - * accesses the alias region, it will behave exactly as if it accessed - * the target memory. - */ -int kvm_create_memory_alias(kvm_context_t, uint64_t phys_start, uint64_t len, - uint64_t target_phys); - -/*! - * \brief Destroy a memory alias - * - * Removes an alias created with kvm_create_memory_alias(). - */ -int kvm_destroy_memory_alias(kvm_context_t, uint64_t phys_start); - -/*! * \brief Get a bitmap of guest ram pages which are allocated to the guest. * * \param kvm Pointer to the current kvm_context @@ -552,30 +384,6 @@ #endif /*! - * \brief Simulate an x86 MCE - * - * This allows you to simulate a x86 MCE. - * - * \param cenv Which virtual CPU should get MCE injected - * \param bank Bank number - * \param status MSR_MCI_STATUS - * \param mcg_status MSR_MCG_STATUS - * \param addr MSR_MCI_ADDR - * \param misc MSR_MCI_MISC - * \param abort_on_error abort on error - */ -void kvm_inject_x86_mce(CPUState *cenv, int bank, uint64_t status, - uint64_t mcg_status, uint64_t addr, uint64_t misc, - int abort_on_error); - -/*! - * \brief Query wheather in kernel pit is used - * - * \param kvm Pointer to the current kvm_context - */ -int kvm_pit_in_kernel(kvm_context_t kvm); - -/*! * \brief Initialize coalesced MMIO * * Check for coalesced MMIO capability and store in context @@ -645,27 +453,6 @@ #ifdef KVM_CAP_VAPIC -/*! - * \brief Enable kernel tpr access reporting - * - * When tpr access reporting is enabled, the kernel will call the - * ->tpr_access() callback every time the guest vcpu accesses the tpr. - * - * \param kvm Pointer to the current kvm_context - * \param vcpu vcpu to enable tpr access reporting on - */ -int kvm_enable_tpr_access_reporting(CPUState *env); - -/*! - * \brief Disable kernel tpr access reporting - * - * Undoes the effect of kvm_enable_tpr_access_reporting(). - * - * \param kvm Pointer to the current kvm_context - * \param vcpu vcpu to disable tpr access reporting on - */ -int kvm_disable_tpr_access_reporting(CPUState *env); - int kvm_enable_vapic(CPUState *env, uint64_t vapic); #endif @@ -716,15 +503,6 @@ #endif #endif -/*! - * \brief Determines whether destroying memory regions is allowed - * - * KVM before 2.6.29 had a bug when destroying memory regions. - * - * \param kvm Pointer to the current kvm_context - */ -int kvm_destroy_memory_region_works(kvm_context_t kvm); - #ifdef KVM_CAP_DEVICE_DEASSIGNMENT /*! * \brief Notifies host kernel about a PCI device to be deassigned from a guest @@ -740,16 +518,6 @@ #endif /*! - * \brief Checks whether the generic irq routing capability is present - * - * Checks whether kvm can reroute interrupts among the various interrupt - * controllers. - * - * \param kvm Pointer to the current kvm_context - */ -int kvm_has_gsi_routing(kvm_context_t kvm); - -/*! * \brief Determines the number of gsis that can be routed * * Returns the number of distinct gsis that can be routed by kvm. This is @@ -766,29 +534,24 @@ * Clears the temporary irq routing table. Nothing is committed to the * running VM. * - * \param kvm Pointer to the current kvm_context */ -int kvm_clear_gsi_routes(kvm_context_t kvm); +int kvm_clear_gsi_routes(void); /*! * \brief Adds an irq route to the temporary irq routing table * * Adds an irq route to the temporary irq routing table. Nothing is * committed to the running VM. - * - * \param kvm Pointer to the current kvm_context */ -int kvm_add_irq_route(kvm_context_t kvm, int gsi, int irqchip, int pin); +int kvm_add_irq_route(int gsi, int irqchip, int pin); /*! * \brief Removes an irq route from the temporary irq routing table * * Adds an irq route to the temporary irq routing table. Nothing is * committed to the running VM. - * - * \param kvm Pointer to the current kvm_context */ -int kvm_del_irq_route(kvm_context_t kvm, int gsi, int irqchip, int pin); +int kvm_del_irq_route(int gsi, int irqchip, int pin); struct kvm_irq_routing_entry; /*! @@ -796,22 +559,16 @@ * * Adds a filled routing entry to the temporary irq routing table. Nothing is * committed to the running VM. - * - * \param kvm Pointer to the current kvm_context */ -int kvm_add_routing_entry(kvm_context_t kvm, - struct kvm_irq_routing_entry *entry); +int kvm_add_routing_entry(struct kvm_irq_routing_entry *entry); /*! * \brief Removes a routing from the temporary irq routing table * * Remove a routing to the temporary irq routing table. Nothing is * committed to the running VM. - * - * \param kvm Pointer to the current kvm_context */ -int kvm_del_routing_entry(kvm_context_t kvm, - struct kvm_irq_routing_entry *entry); +int kvm_del_routing_entry(struct kvm_irq_routing_entry *entry); /*! * \brief Updates a routing in the temporary irq routing table @@ -819,30 +576,10 @@ * Update a routing in the temporary irq routing table * with a new value. entry type and GSI can not be changed. * Nothing is committed to the running VM. - * - * \param kvm Pointer to the current kvm_context */ -int kvm_update_routing_entry(kvm_context_t kvm, - struct kvm_irq_routing_entry *entry, +int kvm_update_routing_entry(struct kvm_irq_routing_entry *entry, struct kvm_irq_routing_entry *newentry); -/*! - * \brief Commit the temporary irq routing table - * - * Commit the temporary irq routing table to the running VM. - * - * \param kvm Pointer to the current kvm_context - */ -int kvm_commit_irq_routes(kvm_context_t kvm); - -/*! - * \brief Get unused GSI number for irq routing table - * - * Get unused GSI number for irq routing table - * - * \param kvm Pointer to the current kvm_context - */ -int kvm_get_irq_route_gsi(kvm_context_t kvm); /*! * \brief Create a file descriptor for injecting interrupts @@ -865,8 +602,6 @@ struct kvm_assigned_msix_entry *entry); #endif -uint32_t kvm_get_supported_cpuid(kvm_context_t kvm, uint32_t function, int reg); - #else /* !CONFIG_KVM */ typedef struct kvm_context *kvm_context_t; @@ -875,65 +610,39 @@ struct kvm_pit_state { }; -static inline int kvm_init(int smp_cpus) -{ - return 0; -} - -#ifndef QEMU_KVM_NO_CPU - -static inline void kvm_inject_x86_mce(CPUState *cenv, int bank, - uint64_t status, uint64_t mcg_status, - uint64_t addr, uint64_t misc, - int abort_on_error) -{ - if (abort_on_error) - abort(); -} - -#endif - -extern int kvm_allowed; - #endif /* !CONFIG_KVM */ +/*! + * \brief Create new KVM context + * + * This creates a new kvm_context. A KVM context is a small area of data that + * holds information about the KVM instance that gets created by this call.\n + * This should always be your first call to KVM. + * + * \param opaque Not used + * \return NULL on failure + */ +int kvm_init(void); + int kvm_main_loop(void); int kvm_init_ap(void); -#ifndef QEMU_KVM_NO_CPU int kvm_vcpu_inited(CPUState *env); -void kvm_load_registers(CPUState *env); -void kvm_save_registers(CPUState *env); -void kvm_load_mpstate(CPUState *env); -void kvm_save_mpstate(CPUState *env); -int kvm_cpu_exec(CPUState *env); -int kvm_insert_breakpoint(CPUState * current_env, target_ulong addr, - target_ulong len, int type); -int kvm_remove_breakpoint(CPUState * current_env, target_ulong addr, - target_ulong len, int type); -void kvm_remove_all_breakpoints(CPUState * current_env); -int kvm_update_guest_debug(CPUState *env, unsigned long reinject_trap); -void kvm_apic_init(CPUState *env); -/* called from vcpu initialization */ -void qemu_kvm_load_lapic(CPUState *env); -#endif +void kvm_save_lapic(CPUState *env); +void kvm_load_lapic(CPUState *env); void kvm_hpet_enable_kpit(void); void kvm_hpet_disable_kpit(void); -int kvm_set_irq(int irq, int level, int *status); int kvm_physical_memory_set_dirty_tracking(int enable); -int kvm_update_dirty_pages_log(void); -#ifndef QEMU_KVM_NO_CPU +void on_vcpu(CPUState *env, void (*func)(void *data), void *data); void qemu_kvm_call_with_env(void (*func)(void *), void *data, CPUState *env); void qemu_kvm_cpuid_on_env(CPUState *env); void kvm_inject_interrupt(CPUState *env, int mask); void kvm_update_after_sipi(CPUState *env); void kvm_update_interrupt_request(CPUState *env); -#endif -void kvm_set_phys_mem(target_phys_addr_t start_addr, ram_addr_t size, - ram_addr_t phys_offset); +#ifndef CONFIG_USER_ONLY void *kvm_cpu_create_phys_mem(target_phys_addr_t start_addr, unsigned long size, int log, int writable); @@ -941,21 +650,15 @@ unsigned long size); void kvm_qemu_log_memory(target_phys_addr_t start, target_phys_addr_t size, int log); -int kvm_setup_guest_memory(void *area, unsigned long size); +#endif int kvm_qemu_create_memory_alias(uint64_t phys_start, uint64_t len, uint64_t target_phys); int kvm_qemu_destroy_memory_alias(uint64_t phys_start); int kvm_arch_qemu_create_context(void); -#ifndef QEMU_KVM_NO_CPU void kvm_arch_save_regs(CPUState *env); -void kvm_arch_load_regs(CPUState *env); -void kvm_arch_load_mpstate(CPUState *env); -void kvm_arch_save_mpstate(CPUState *env); -int kvm_arch_init_vcpu(CPUState *cenv); -int kvm_arch_pre_run(CPUState *env, struct kvm_run *run); -int kvm_arch_post_run(CPUState *env, struct kvm_run *run); +void kvm_arch_load_regs(CPUState *env, int level); int kvm_arch_has_work(CPUState *env); void kvm_arch_process_irqchip_events(CPUState *env); int kvm_arch_try_push_interrupts(void *opaque); @@ -963,50 +666,11 @@ void kvm_arch_cpu_reset(CPUState *env); int kvm_set_boot_cpu_id(uint32_t id); -struct kvm_guest_debug; -struct kvm_debug_exit_arch; - -struct kvm_sw_breakpoint { - target_ulong pc; - target_ulong saved_insn; - int use_count; - QTAILQ_ENTRY(kvm_sw_breakpoint) entry; -}; - -QTAILQ_HEAD(kvm_sw_breakpoint_head, kvm_sw_breakpoint); - -int kvm_arch_debug(struct kvm_debug_exit_arch *arch_info); -int kvm_sw_breakpoints_active(CPUState *env); -struct kvm_sw_breakpoint *kvm_find_sw_breakpoint(CPUState *env, - target_ulong pc); -int kvm_arch_insert_sw_breakpoint(CPUState * current_env, - struct kvm_sw_breakpoint *bp); -int kvm_arch_remove_sw_breakpoint(CPUState * current_env, - struct kvm_sw_breakpoint *bp); -int kvm_arch_insert_hw_breakpoint(target_ulong addr, target_ulong len, - int type); -int kvm_arch_remove_hw_breakpoint(target_ulong addr, target_ulong len, - int type); -void kvm_arch_remove_all_hw_breakpoints(void); -void kvm_arch_update_guest_debug(CPUState *env, struct kvm_guest_debug *dbg); - -#endif - void qemu_kvm_aio_wait_start(void); void qemu_kvm_aio_wait(void); void qemu_kvm_aio_wait_end(void); -void qemu_kvm_notify_work(void); - -#ifndef QEMU_KVM_NO_CPU -void kvm_tpr_opt_setup(void); void kvm_tpr_access_report(CPUState *env, uint64_t rip, int is_write); -void kvm_tpr_vcpu_start(CPUState *env); -#endif - -int qemu_kvm_get_dirty_pages(unsigned long phys_addr, void *buf); -int kvm_coalesce_mmio_region(target_phys_addr_t start, ram_addr_t size); -int kvm_uncoalesce_mmio_region(target_phys_addr_t start, ram_addr_t size); int kvm_arch_init_irq_routing(void); @@ -1023,14 +687,11 @@ #endif #define ALIGN(x, y) (((x)+(y)-1) & ~((y)-1)) -#ifndef QEMU_KVM_NO_CPU #define BITMAP_SIZE(m) (ALIGN(((m)>>TARGET_PAGE_BITS), HOST_LONG_BITS) / 8) -#endif #ifdef CONFIG_KVM #include "qemu-queue.h" -extern int kvm_allowed; extern int kvm_irqchip; extern int kvm_pit; extern int kvm_pit_reinject; @@ -1048,85 +709,20 @@ int kvm_arch_halt(CPUState *env); int handle_tpr_access(void *opaque, CPUState *env, uint64_t rip, int is_write); -int kvm_has_sync_mmu(void); -#define kvm_enabled() (kvm_allowed) -#define qemu_kvm_pit_in_kernel() kvm_pit_in_kernel(kvm_context) -#define qemu_kvm_has_gsi_routing() kvm_has_gsi_routing(kvm_context) +#define qemu_kvm_has_gsi_routing() kvm_has_gsi_routing() #ifdef TARGET_I386 #define qemu_kvm_has_pit_state2() kvm_has_pit_state2(kvm_context) #endif -void kvm_init_vcpu(CPUState *env); -void kvm_load_tsc(CPUState *env); #else -#define kvm_has_sync_mmu() (0) -#define kvm_enabled() (0) #define kvm_nested 0 -#define qemu_kvm_pit_in_kernel() (0) #define qemu_kvm_has_gsi_routing() (0) -#ifndef QEMU_KVM_NO_CPU #ifdef TARGET_I386 #define qemu_kvm_has_pit_state2() (0) #endif -#define kvm_load_registers(env) do {} while(0) -#define kvm_save_registers(env) do {} while(0) -#define kvm_save_mpstate(env) do {} while(0) #define qemu_kvm_cpu_stop(env) do {} while(0) -static inline void kvm_init_vcpu(CPUState *env) -{ -} - -static inline void kvm_load_tsc(CPUState *env) -{ -} -#endif -#endif - -void kvm_mutex_unlock(void); -void kvm_mutex_lock(void); - -int kvm_physical_sync_dirty_bitmap(target_phys_addr_t start_addr, - target_phys_addr_t end_addr); - -int kvm_log_start(target_phys_addr_t phys_addr, target_phys_addr_t len); -int kvm_log_stop(target_phys_addr_t phys_addr, target_phys_addr_t len); - - -static inline int kvm_sync_vcpus(void) -{ - return 0; -} - -#ifndef QEMU_KVM_NO_CPU -void kvm_arch_get_registers(CPUState *env); - -static inline void kvm_arch_put_registers(CPUState *env) -{ - kvm_load_registers(env); -} - -void kvm_cpu_synchronize_state(CPUState *env); - -static inline void cpu_synchronize_state(CPUState *env) -{ - if (kvm_enabled()) { - kvm_cpu_synchronize_state(env); - } -} - -uint32_t kvm_arch_get_supported_cpuid(CPUState *env, uint32_t function, - int reg); - - #endif -static inline int kvm_set_migration_log(int enable) -{ - return kvm_physical_memory_set_dirty_tracking(enable); -} - - -int kvm_irqchip_in_kernel(void); #ifdef CONFIG_KVM typedef struct KVMSlot { @@ -1139,31 +735,37 @@ typedef struct kvm_dirty_log KVMDirtyLog; -typedef struct KVMState { +struct KVMState { KVMSlot slots[32]; int fd; int vmfd; int coalesced_mmio; +#ifdef KVM_CAP_COALESCED_MMIO + struct kvm_coalesced_mmio_ring *coalesced_mmio_ring; +#endif int broken_set_mem_region; int migration_log; int vcpu_events; + int robust_singlestep; + int debugregs; #ifdef KVM_CAP_SET_GUEST_DEBUG QTAILQ_HEAD(, kvm_sw_breakpoint) kvm_sw_breakpoints; #endif int irqchip_in_kernel; + int pit_in_kernel; + int xsave, xcrs; + int many_ioeventfds; struct kvm_context kvm_context; -} KVMState; - -extern KVMState *kvm_state; +}; -int kvm_ioctl(KVMState *s, int type, ...); -int kvm_vm_ioctl(KVMState *s, int type, ...); -int kvm_vcpu_ioctl(CPUState *env, int type, ...); -int kvm_check_extension(KVMState *s, unsigned int ext); +extern struct KVMState *kvm_state; int kvm_tpr_enable_vapic(CPUState *env); +unsigned long kvm_get_thread_id(void); +int kvm_cpu_is_stopped(CPUState *env); + #endif #endif diff -Nru qemu-kvm-0.12.5+noroms/qemu-kvm-helper.c qemu-kvm-0.14.1/qemu-kvm-helper.c --- qemu-kvm-0.12.5+noroms/qemu-kvm-helper.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/qemu-kvm-helper.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,40 +0,0 @@ - -#include "config.h" -#include "config-host.h" - -#include "exec.h" - -#include "qemu-kvm.h" - -void qemu_kvm_call_with_env(void (*func)(void *), void *data, CPUState *newenv) -{ - CPUState *oldenv; -#define DECLARE_HOST_REGS -#include "hostregs_helper.h" - - oldenv = newenv; - -#define SAVE_HOST_REGS -#include "hostregs_helper.h" - - env = newenv; - - env_to_regs(); - func(data); - regs_to_env(); - - env = oldenv; - -#include "hostregs_helper.h" -} - -static void call_helper_cpuid(void *junk) -{ - helper_cpuid(); -} - -void qemu_kvm_cpuid_on_env(CPUState *env) -{ - qemu_kvm_call_with_env(call_helper_cpuid, NULL, env); -} - diff -Nru qemu-kvm-0.12.5+noroms/qemu-kvm-ia64.c qemu-kvm-0.14.1/qemu-kvm-ia64.c --- qemu-kvm-0.12.5+noroms/qemu-kvm-ia64.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/qemu-kvm-ia64.c 2011-05-11 13:29:46.000000000 +0000 @@ -16,7 +16,7 @@ return 0; } -void kvm_arch_load_regs(CPUState *env) +void kvm_arch_load_regs(CPUState *env, int level) { } @@ -124,7 +124,9 @@ { if (kvm_irqchip_in_kernel(kvm_context)) { #ifdef KVM_CAP_MP_STATE - kvm_reset_mpstate(env->kvm_cpu_state.vcpu_ctx); + struct kvm_mp_state mp_state = {.mp_state = KVM_MP_STATE_UNINITIALIZED + }; + kvm_set_mpstate(env, &mp_state); #endif } else { env->interrupt_request &= ~CPU_INTERRUPT_HARD; diff -Nru qemu-kvm-0.12.5+noroms/qemu-kvm-x86.c qemu-kvm-0.14.1/qemu-kvm-x86.c --- qemu-kvm-0.12.5+noroms/qemu-kvm-x86.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/qemu-kvm-x86.c 2011-05-11 13:29:46.000000000 +0000 @@ -22,287 +22,221 @@ #include #include "kvm.h" -#include "hw/pc.h" +#include "hw/apic.h" -#define MSR_IA32_TSC 0x10 +#define MSR_IA32_TSC 0x10 static struct kvm_msr_list *kvm_msr_list; extern unsigned int kvm_shadow_memory; -static int kvm_has_msr_star; -static int kvm_has_vm_hsave_pa; - -static int lm_capable_kernel; int kvm_set_tss_addr(kvm_context_t kvm, unsigned long addr) { -#ifdef KVM_CAP_SET_TSS_ADDR - int r; + int r; - r = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, KVM_CAP_SET_TSS_ADDR); - if (r > 0) { - r = kvm_vm_ioctl(kvm_state, KVM_SET_TSS_ADDR, addr); - if (r < 0) { - fprintf(stderr, "kvm_set_tss_addr: %m\n"); - return r; - } - return 0; - } -#endif - return -ENOSYS; + r = kvm_vm_ioctl(kvm_state, KVM_SET_TSS_ADDR, addr); + if (r < 0) { + fprintf(stderr, "kvm_set_tss_addr: %m\n"); + return r; + } + return 0; } static int kvm_init_tss(kvm_context_t kvm) { -#ifdef KVM_CAP_SET_TSS_ADDR - int r; - - r = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, KVM_CAP_SET_TSS_ADDR); - if (r > 0) { - /* - * this address is 3 pages before the bios, and the bios should present - * as unavaible memory - */ - r = kvm_set_tss_addr(kvm, 0xfeffd000); - if (r < 0) { - fprintf(stderr, "kvm_init_tss: unable to set tss addr\n"); - return r; - } + int r; - } -#endif - return 0; + r = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, KVM_CAP_SET_TSS_ADDR); + if (r > 0) { + /* + * this address is 3 pages before the bios, and the bios should present + * as unavaible memory + */ + r = kvm_set_tss_addr(kvm, 0xfeffd000); + if (r < 0) { + fprintf(stderr, "kvm_init_tss: unable to set tss addr\n"); + return r; + } + } else { + fprintf(stderr, "kvm does not support KVM_CAP_SET_TSS_ADDR\n"); + } + return 0; } static int kvm_set_identity_map_addr(kvm_context_t kvm, uint64_t addr) { #ifdef KVM_CAP_SET_IDENTITY_MAP_ADDR - int r; + int r; - r = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, KVM_CAP_SET_IDENTITY_MAP_ADDR); - if (r > 0) { - r = kvm_vm_ioctl(kvm_state, KVM_SET_IDENTITY_MAP_ADDR, &addr); - if (r == -1) { - fprintf(stderr, "kvm_set_identity_map_addr: %m\n"); - return -errno; - } - return 0; - } + r = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, KVM_CAP_SET_IDENTITY_MAP_ADDR); + if (r > 0) { + r = kvm_vm_ioctl(kvm_state, KVM_SET_IDENTITY_MAP_ADDR, &addr); + if (r == -1) { + fprintf(stderr, "kvm_set_identity_map_addr: %m\n"); + return -errno; + } + return 0; + } #endif - return -ENOSYS; + return -ENOSYS; } static int kvm_init_identity_map_page(kvm_context_t kvm) { #ifdef KVM_CAP_SET_IDENTITY_MAP_ADDR - int r; - - r = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, KVM_CAP_SET_IDENTITY_MAP_ADDR); - if (r > 0) { - /* - * this address is 4 pages before the bios, and the bios should present - * as unavaible memory - */ - r = kvm_set_identity_map_addr(kvm, 0xfeffc000); - if (r < 0) { - fprintf(stderr, "kvm_init_identity_map_page: " - "unable to set identity mapping addr\n"); - return r; - } + int r; - } + r = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, KVM_CAP_SET_IDENTITY_MAP_ADDR); + if (r > 0) { + /* + * this address is 4 pages before the bios, and the bios should present + * as unavaible memory + */ + r = kvm_set_identity_map_addr(kvm, 0xfeffc000); + if (r < 0) { + fprintf(stderr, "kvm_init_identity_map_page: " + "unable to set identity mapping addr\n"); + return r; + } + } #endif - return 0; + return 0; } static int kvm_create_pit(kvm_context_t kvm) { #ifdef KVM_CAP_PIT - int r; + int r; - kvm->pit_in_kernel = 0; - if (!kvm->no_pit_creation) { - r = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, KVM_CAP_PIT); - if (r > 0) { - r = kvm_vm_ioctl(kvm_state, KVM_CREATE_PIT); - if (r >= 0) - kvm->pit_in_kernel = 1; - else { - fprintf(stderr, "Create kernel PIC irqchip failed\n"); - return r; - } - } - } + kvm_state->pit_in_kernel = 0; + if (!kvm->no_pit_creation) { + r = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, KVM_CAP_PIT); + if (r > 0) { + r = kvm_vm_ioctl(kvm_state, KVM_CREATE_PIT); + if (r >= 0) { + kvm_state->pit_in_kernel = 1; + } else { + fprintf(stderr, "Create kernel PIC irqchip failed\n"); + return r; + } + } + } #endif - return 0; + return 0; } int kvm_arch_create(kvm_context_t kvm, unsigned long phys_mem_bytes, - void **vm_mem) + void **vm_mem) { - int r = 0; + int r = 0; - r = kvm_init_tss(kvm); - if (r < 0) - return r; + r = kvm_init_tss(kvm); + if (r < 0) { + return r; + } - r = kvm_init_identity_map_page(kvm); - if (r < 0) - return r; + r = kvm_init_identity_map_page(kvm); + if (r < 0) { + return r; + } - r = kvm_create_pit(kvm); - if (r < 0) - return r; + /* + * Tell fw_cfg to notify the BIOS to reserve the range. + */ + if (e820_add_entry(0xfeffc000, 0x4000, E820_RESERVED) < 0) { + perror("e820_add_entry() table is full"); + exit(1); + } - r = kvm_init_coalesced_mmio(kvm); - if (r < 0) - return r; + r = kvm_create_pit(kvm); + if (r < 0) { + return r; + } -#ifdef KVM_EXIT_TPR_ACCESS - kvm_tpr_opt_setup(); -#endif + r = kvm_init_coalesced_mmio(kvm); + if (r < 0) { + return r; + } - return 0; + return 0; } #ifdef KVM_EXIT_TPR_ACCESS static int kvm_handle_tpr_access(CPUState *env) { - struct kvm_run *run = env->kvm_run; - kvm_tpr_access_report(env, - run->tpr_access.rip, - run->tpr_access.is_write); + struct kvm_run *run = env->kvm_run; + kvm_tpr_access_report(env, + run->tpr_access.rip, + run->tpr_access.is_write); return 0; } int kvm_enable_vapic(CPUState *env, uint64_t vapic) { - struct kvm_vapic_addr va = { - .vapic_addr = vapic, - }; + struct kvm_vapic_addr va = { + .vapic_addr = vapic, + }; - return kvm_vcpu_ioctl(env, KVM_SET_VAPIC_ADDR, &va); + return kvm_vcpu_ioctl(env, KVM_SET_VAPIC_ADDR, &va); } #endif int kvm_arch_run(CPUState *env) { - int r = 0; - struct kvm_run *run = env->kvm_run; + int r = 0; + struct kvm_run *run = env->kvm_run; - - switch (run->exit_reason) { + switch (run->exit_reason) { #ifdef KVM_EXIT_SET_TPR - case KVM_EXIT_SET_TPR: - break; + case KVM_EXIT_SET_TPR: + break; #endif #ifdef KVM_EXIT_TPR_ACCESS - case KVM_EXIT_TPR_ACCESS: - r = kvm_handle_tpr_access(env); - break; + case KVM_EXIT_TPR_ACCESS: + r = kvm_handle_tpr_access(env); + break; #endif - default: - r = 1; - break; - } - - return r; -} - -#define MAX_ALIAS_SLOTS 4 -static struct { - uint64_t start; - uint64_t len; -} kvm_aliases[MAX_ALIAS_SLOTS]; - -static int get_alias_slot(uint64_t start) -{ - int i; - - for (i=0; ipit_in_kernel) - return 0; - return kvm_vm_ioctl(kvm_state, KVM_GET_PIT, s); + if (!kvm_pit_in_kernel()) { + return 0; + } + return kvm_vm_ioctl(kvm_state, KVM_GET_PIT, s); } int kvm_set_pit(kvm_context_t kvm, struct kvm_pit_state *s) { - if (!kvm->pit_in_kernel) - return 0; - return kvm_vm_ioctl(kvm_state, KVM_SET_PIT, s); + if (!kvm_pit_in_kernel()) { + return 0; + } + return kvm_vm_ioctl(kvm_state, KVM_SET_PIT, s); } #ifdef KVM_CAP_PIT_STATE2 int kvm_get_pit2(kvm_context_t kvm, struct kvm_pit_state2 *ps2) { - if (!kvm->pit_in_kernel) - return 0; - return kvm_vm_ioctl(kvm_state, KVM_GET_PIT2, ps2); + if (!kvm_pit_in_kernel()) { + return 0; + } + return kvm_vm_ioctl(kvm_state, KVM_GET_PIT2, ps2); } int kvm_set_pit2(kvm_context_t kvm, struct kvm_pit_state2 *ps2) { - if (!kvm->pit_in_kernel) - return 0; - return kvm_vm_ioctl(kvm_state, KVM_SET_PIT2, ps2); + if (!kvm_pit_in_kernel()) { + return 0; + } + return kvm_vm_ioctl(kvm_state, KVM_SET_PIT2, ps2); } #endif @@ -343,401 +281,190 @@ int kvm_has_pit_state2(kvm_context_t kvm) { - int r = 0; + int r = 0; #ifdef KVM_CAP_PIT_STATE2 - r = kvm_check_extension(kvm_state, KVM_CAP_PIT_STATE2); + r = kvm_check_extension(kvm_state, KVM_CAP_PIT_STATE2); #endif - return r; + return r; } void kvm_show_code(CPUState *env) { #define SHOW_CODE_LEN 50 - struct kvm_regs regs; - struct kvm_sregs sregs; - int r, n; - int back_offset; - unsigned char code; - char code_str[SHOW_CODE_LEN * 3 + 1]; - unsigned long rip; - - r = kvm_vcpu_ioctl(env, KVM_GET_SREGS, &sregs); - if (r < 0 ) { - perror("KVM_GET_SREGS"); - return; - } - r = kvm_vcpu_ioctl(env, KVM_GET_REGS, ®s); - if (r < 0) { - perror("KVM_GET_REGS"); - return; - } - rip = sregs.cs.base + regs.rip; - back_offset = regs.rip; - if (back_offset > 20) - back_offset = 20; - *code_str = 0; - for (n = -back_offset; n < SHOW_CODE_LEN-back_offset; ++n) { - if (n == 0) - strcat(code_str, " -->"); - cpu_physical_memory_rw(rip + n, &code, 1, 1); - sprintf(code_str + strlen(code_str), " %02x", code); - } - fprintf(stderr, "code:%s\n", code_str); + struct kvm_regs regs; + struct kvm_sregs sregs; + int r, n; + int back_offset; + unsigned char code; + char code_str[SHOW_CODE_LEN * 3 + 1]; + unsigned long rip; + + r = kvm_vcpu_ioctl(env, KVM_GET_SREGS, &sregs); + if (r < 0 ) { + perror("KVM_GET_SREGS"); + return; + } + r = kvm_vcpu_ioctl(env, KVM_GET_REGS, ®s); + if (r < 0) { + perror("KVM_GET_REGS"); + return; + } + rip = sregs.cs.base + regs.rip; + back_offset = regs.rip; + if (back_offset > 20) { + back_offset = 20; + } + *code_str = 0; + for (n = -back_offset; n < SHOW_CODE_LEN-back_offset; ++n) { + if (n == 0) { + strcat(code_str, " -->"); + } + cpu_physical_memory_rw(rip + n, &code, 1, 1); + sprintf(code_str + strlen(code_str), " %02x", code); + } + fprintf(stderr, "code:%s\n", code_str); } /* * Returns available msr list. User must free. */ -struct kvm_msr_list *kvm_get_msr_list(kvm_context_t kvm) -{ - struct kvm_msr_list sizer, *msrs; - int r; - - sizer.nmsrs = 0; - r = kvm_ioctl(kvm_state, KVM_GET_MSR_INDEX_LIST, &sizer); - if (r < 0 && r != -E2BIG) - return NULL; - /* Old kernel modules had a bug and could write beyond the provided - memory. Allocate at least a safe amount of 1K. */ - msrs = qemu_malloc(MAX(1024, sizeof(*msrs) + - sizer.nmsrs * sizeof(*msrs->indices))); - - msrs->nmsrs = sizer.nmsrs; - r = kvm_ioctl(kvm_state, KVM_GET_MSR_INDEX_LIST, msrs); - if (r < 0) { - free(msrs); - errno = r; - return NULL; - } - return msrs; -} - -int kvm_get_msrs(CPUState *env, struct kvm_msr_entry *msrs, int n) -{ - struct kvm_msrs *kmsrs = qemu_malloc(sizeof *kmsrs + n * sizeof *msrs); - int r; - - kmsrs->nmsrs = n; - memcpy(kmsrs->entries, msrs, n * sizeof *msrs); - r = kvm_vcpu_ioctl(env, KVM_GET_MSRS, kmsrs); - memcpy(msrs, kmsrs->entries, n * sizeof *msrs); - free(kmsrs); - return r; -} - -int kvm_set_msrs(CPUState *env, struct kvm_msr_entry *msrs, int n) +static struct kvm_msr_list *kvm_get_msr_list(void) { - struct kvm_msrs *kmsrs = qemu_malloc(sizeof *kmsrs + n * sizeof *msrs); + struct kvm_msr_list sizer, *msrs; int r; - kmsrs->nmsrs = n; - memcpy(kmsrs->entries, msrs, n * sizeof *msrs); - r = kvm_vcpu_ioctl(env, KVM_SET_MSRS, kmsrs); - free(kmsrs); - return r; -} - -int kvm_get_mce_cap_supported(kvm_context_t kvm, uint64_t *mce_cap, - int *max_banks) -{ -#ifdef KVM_CAP_MCE - int r; - - r = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, KVM_CAP_MCE); - if (r > 0) { - *max_banks = r; - return kvm_ioctl(kvm_state, KVM_X86_GET_MCE_CAP_SUPPORTED, mce_cap); + sizer.nmsrs = 0; + r = kvm_ioctl(kvm_state, KVM_GET_MSR_INDEX_LIST, &sizer); + if (r < 0 && r != -E2BIG) { + return NULL; + } + /* Old kernel modules had a bug and could write beyond the provided + memory. Allocate at least a safe amount of 1K. */ + msrs = qemu_malloc(MAX(1024, sizeof(*msrs) + + sizer.nmsrs * sizeof(*msrs->indices))); + + msrs->nmsrs = sizer.nmsrs; + r = kvm_ioctl(kvm_state, KVM_GET_MSR_INDEX_LIST, msrs); + if (r < 0) { + free(msrs); + errno = r; + return NULL; } -#endif - return -ENOSYS; -} - -int kvm_setup_mce(CPUState *env, uint64_t *mcg_cap) -{ -#ifdef KVM_CAP_MCE - return kvm_vcpu_ioctl(env, KVM_X86_SETUP_MCE, mcg_cap); -#else - return -ENOSYS; -#endif -} - -int kvm_set_mce(CPUState *env, struct kvm_x86_mce *m) -{ -#ifdef KVM_CAP_MCE - return kvm_vcpu_ioctl(env, KVM_X86_SET_MCE, m); -#else - return -ENOSYS; -#endif + return msrs; } static void print_seg(FILE *file, const char *name, struct kvm_segment *seg) { - fprintf(stderr, - "%s %04x (%08llx/%08x p %d dpl %d db %d s %d type %x l %d" - " g %d avl %d)\n", - name, seg->selector, seg->base, seg->limit, seg->present, - seg->dpl, seg->db, seg->s, seg->type, seg->l, seg->g, - seg->avl); + fprintf(stderr, + "%s %04x (%08llx/%08x p %d dpl %d db %d s %d type %x l %d" + " g %d avl %d)\n", + name, seg->selector, seg->base, seg->limit, seg->present, + seg->dpl, seg->db, seg->s, seg->type, seg->l, seg->g, + seg->avl); } static void print_dt(FILE *file, const char *name, struct kvm_dtable *dt) { - fprintf(stderr, "%s %llx/%x\n", name, dt->base, dt->limit); + fprintf(stderr, "%s %llx/%x\n", name, dt->base, dt->limit); } void kvm_show_regs(CPUState *env) { - struct kvm_regs regs; - struct kvm_sregs sregs; - int r; - - r = kvm_vcpu_ioctl(env, KVM_GET_REGS, ®s); - if (r < 0) { - perror("KVM_GET_REGS"); - return; - } - fprintf(stderr, - "rax %016llx rbx %016llx rcx %016llx rdx %016llx\n" - "rsi %016llx rdi %016llx rsp %016llx rbp %016llx\n" - "r8 %016llx r9 %016llx r10 %016llx r11 %016llx\n" - "r12 %016llx r13 %016llx r14 %016llx r15 %016llx\n" - "rip %016llx rflags %08llx\n", - regs.rax, regs.rbx, regs.rcx, regs.rdx, - regs.rsi, regs.rdi, regs.rsp, regs.rbp, - regs.r8, regs.r9, regs.r10, regs.r11, - regs.r12, regs.r13, regs.r14, regs.r15, - regs.rip, regs.rflags); - r = kvm_vcpu_ioctl(env, KVM_GET_SREGS, &sregs); - if (r < 0) { - perror("KVM_GET_SREGS"); - return; - } - print_seg(stderr, "cs", &sregs.cs); - print_seg(stderr, "ds", &sregs.ds); - print_seg(stderr, "es", &sregs.es); - print_seg(stderr, "ss", &sregs.ss); - print_seg(stderr, "fs", &sregs.fs); - print_seg(stderr, "gs", &sregs.gs); - print_seg(stderr, "tr", &sregs.tr); - print_seg(stderr, "ldt", &sregs.ldt); - print_dt(stderr, "gdt", &sregs.gdt); - print_dt(stderr, "idt", &sregs.idt); - fprintf(stderr, "cr0 %llx cr2 %llx cr3 %llx cr4 %llx cr8 %llx" - " efer %llx\n", - sregs.cr0, sregs.cr2, sregs.cr3, sregs.cr4, sregs.cr8, - sregs.efer); -} - -static void kvm_set_cr8(CPUState *env, uint64_t cr8) -{ - env->kvm_run->cr8 = cr8; -} - -int kvm_setup_cpuid(CPUState *env, int nent, - struct kvm_cpuid_entry *entries) -{ - struct kvm_cpuid *cpuid; - int r; - - cpuid = qemu_malloc(sizeof(*cpuid) + nent * sizeof(*entries)); - - cpuid->nent = nent; - memcpy(cpuid->entries, entries, nent * sizeof(*entries)); - r = kvm_vcpu_ioctl(env, KVM_SET_CPUID, cpuid); + struct kvm_regs regs; + struct kvm_sregs sregs; + int r; - free(cpuid); - return r; + r = kvm_vcpu_ioctl(env, KVM_GET_REGS, ®s); + if (r < 0) { + perror("KVM_GET_REGS"); + return; + } + fprintf(stderr, + "rax %016llx rbx %016llx rcx %016llx rdx %016llx\n" + "rsi %016llx rdi %016llx rsp %016llx rbp %016llx\n" + "r8 %016llx r9 %016llx r10 %016llx r11 %016llx\n" + "r12 %016llx r13 %016llx r14 %016llx r15 %016llx\n" + "rip %016llx rflags %08llx\n", + regs.rax, regs.rbx, regs.rcx, regs.rdx, + regs.rsi, regs.rdi, regs.rsp, regs.rbp, + regs.r8, regs.r9, regs.r10, regs.r11, + regs.r12, regs.r13, regs.r14, regs.r15, + regs.rip, regs.rflags); + r = kvm_vcpu_ioctl(env, KVM_GET_SREGS, &sregs); + if (r < 0) { + perror("KVM_GET_SREGS"); + return; + } + print_seg(stderr, "cs", &sregs.cs); + print_seg(stderr, "ds", &sregs.ds); + print_seg(stderr, "es", &sregs.es); + print_seg(stderr, "ss", &sregs.ss); + print_seg(stderr, "fs", &sregs.fs); + print_seg(stderr, "gs", &sregs.gs); + print_seg(stderr, "tr", &sregs.tr); + print_seg(stderr, "ldt", &sregs.ldt); + print_dt(stderr, "gdt", &sregs.gdt); + print_dt(stderr, "idt", &sregs.idt); + fprintf(stderr, "cr0 %llx cr2 %llx cr3 %llx cr4 %llx cr8 %llx" + " efer %llx\n", + sregs.cr0, sregs.cr2, sregs.cr3, sregs.cr4, sregs.cr8, + sregs.efer); } -int kvm_setup_cpuid2(CPUState *env, int nent, - struct kvm_cpuid_entry2 *entries) +static void kvm_set_cr8(CPUState *env, uint64_t cr8) { - struct kvm_cpuid2 *cpuid; - int r; - - cpuid = qemu_malloc(sizeof(*cpuid) + nent * sizeof(*entries)); - - cpuid->nent = nent; - memcpy(cpuid->entries, entries, nent * sizeof(*entries)); - r = kvm_vcpu_ioctl(env, KVM_SET_CPUID2, cpuid); - free(cpuid); - return r; + env->kvm_run->cr8 = cr8; } int kvm_set_shadow_pages(kvm_context_t kvm, unsigned int nrshadow_pages) { #ifdef KVM_CAP_MMU_SHADOW_CACHE_CONTROL - int r; + int r; - r = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, - KVM_CAP_MMU_SHADOW_CACHE_CONTROL); - if (r > 0) { - r = kvm_vm_ioctl(kvm_state, KVM_SET_NR_MMU_PAGES, nrshadow_pages); - if (r < 0) { - fprintf(stderr, "kvm_set_shadow_pages: %m\n"); - return r; - } - return 0; - } + r = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, + KVM_CAP_MMU_SHADOW_CACHE_CONTROL); + if (r > 0) { + r = kvm_vm_ioctl(kvm_state, KVM_SET_NR_MMU_PAGES, nrshadow_pages); + if (r < 0) { + fprintf(stderr, "kvm_set_shadow_pages: %m\n"); + return r; + } + return 0; + } #endif - return -1; + return -1; } int kvm_get_shadow_pages(kvm_context_t kvm, unsigned int *nrshadow_pages) { #ifdef KVM_CAP_MMU_SHADOW_CACHE_CONTROL - int r; + int r; - r = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, - KVM_CAP_MMU_SHADOW_CACHE_CONTROL); - if (r > 0) { - *nrshadow_pages = kvm_vm_ioctl(kvm_state, KVM_GET_NR_MMU_PAGES); - return 0; - } + r = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, + KVM_CAP_MMU_SHADOW_CACHE_CONTROL); + if (r > 0) { + *nrshadow_pages = kvm_vm_ioctl(kvm_state, KVM_GET_NR_MMU_PAGES); + return 0; + } #endif - return -1; + return -1; } #ifdef KVM_CAP_VAPIC - -static int tpr_access_reporting(CPUState *env, int enabled) -{ - int r; - struct kvm_tpr_access_ctl tac = { - .enabled = enabled, - }; - - r = kvm_ioctl(kvm_state, KVM_CHECK_EXTENSION, KVM_CAP_VAPIC); - if (r <= 0) - return -ENOSYS; - return kvm_vcpu_ioctl(env, KVM_TPR_ACCESS_REPORTING, &tac); -} - -int kvm_enable_tpr_access_reporting(CPUState *env) -{ - return tpr_access_reporting(env, 1); -} - -int kvm_disable_tpr_access_reporting(CPUState *env) +static int kvm_enable_tpr_access_reporting(CPUState *env) { - return tpr_access_reporting(env, 0); -} - -#endif - -#ifdef KVM_CAP_EXT_CPUID - -static struct kvm_cpuid2 *try_get_cpuid(kvm_context_t kvm, int max) -{ - struct kvm_cpuid2 *cpuid; - int r, size; - - size = sizeof(*cpuid) + max * sizeof(*cpuid->entries); - cpuid = qemu_malloc(size); - cpuid->nent = max; - r = kvm_ioctl(kvm_state, KVM_GET_SUPPORTED_CPUID, cpuid); - if (r == 0 && cpuid->nent >= max) - r = -E2BIG; - if (r < 0) { - if (r == -E2BIG) { - free(cpuid); - return NULL; - } else { - fprintf(stderr, "KVM_GET_SUPPORTED_CPUID failed: %s\n", - strerror(-r)); - exit(1); - } - } - return cpuid; -} - -#define R_EAX 0 -#define R_ECX 1 -#define R_EDX 2 -#define R_EBX 3 -#define R_ESP 4 -#define R_EBP 5 -#define R_ESI 6 -#define R_EDI 7 - -uint32_t kvm_get_supported_cpuid(kvm_context_t kvm, uint32_t function, int reg) -{ - struct kvm_cpuid2 *cpuid; - int i, max; - uint32_t ret = 0; - uint32_t cpuid_1_edx; - - if (!kvm_check_extension(kvm_state, KVM_CAP_EXT_CPUID)) { - return -1U; - } - - max = 1; - while ((cpuid = try_get_cpuid(kvm, max)) == NULL) { - max *= 2; - } - - for (i = 0; i < cpuid->nent; ++i) { - if (cpuid->entries[i].function == function) { - switch (reg) { - case R_EAX: - ret = cpuid->entries[i].eax; - break; - case R_EBX: - ret = cpuid->entries[i].ebx; - break; - case R_ECX: - ret = cpuid->entries[i].ecx; - break; - case R_EDX: - ret = cpuid->entries[i].edx; - if (function == 1) { - /* kvm misreports the following features - */ - ret |= 1 << 12; /* MTRR */ - ret |= 1 << 16; /* PAT */ - ret |= 1 << 7; /* MCE */ - ret |= 1 << 14; /* MCA */ - } - - /* On Intel, kvm returns cpuid according to - * the Intel spec, so add missing bits - * according to the AMD spec: - */ - if (function == 0x80000001) { - cpuid_1_edx = kvm_get_supported_cpuid(kvm, 1, R_EDX); - ret |= cpuid_1_edx & 0xdfeff7ff; - } - break; - } - } - } - - free(cpuid); - - return ret; -} - -#else + int r; + struct kvm_tpr_access_ctl tac = { .enabled = 1 }; -uint32_t kvm_get_supported_cpuid(kvm_context_t kvm, uint32_t function, int reg) -{ - return -1U; + r = kvm_ioctl(env->kvm_state, KVM_CHECK_EXTENSION, KVM_CAP_VAPIC); + if (r <= 0) { + return -ENOSYS; + } + return kvm_vcpu_ioctl(env, KVM_TPR_ACCESS_REPORTING, &tac); } - #endif -int kvm_qemu_create_memory_alias(uint64_t phys_start, - uint64_t len, - uint64_t target_phys) -{ - return kvm_create_memory_alias(kvm_context, phys_start, len, target_phys); -} - -int kvm_qemu_destroy_memory_alias(uint64_t phys_start) -{ - return kvm_destroy_memory_alias(kvm_context, phys_start); -} #ifdef KVM_CAP_ADJUST_CLOCK static struct kvm_clock_data kvmclock_data; @@ -772,619 +499,150 @@ int kvm_arch_qemu_create_context(void) { - int i; + int r; struct utsname utsname; uname(&utsname); lm_capable_kernel = strcmp(utsname.machine, "x86_64") == 0; - if (kvm_shadow_memory) + if (kvm_shadow_memory) { kvm_set_shadow_pages(kvm_context, kvm_shadow_memory); - - kvm_msr_list = kvm_get_msr_list(kvm_context); - if (!kvm_msr_list) - return -1; - for (i = 0; i < kvm_msr_list->nmsrs; ++i) { - if (kvm_msr_list->indices[i] == MSR_STAR) - kvm_has_msr_star = 1; - if (kvm_msr_list->indices[i] == MSR_VM_HSAVE_PA) - kvm_has_vm_hsave_pa = 1; } -#ifdef KVM_CAP_ADJUST_CLOCK - if (kvm_check_extension(kvm_state, KVM_CAP_ADJUST_CLOCK)) - vmstate_register(0, &vmstate_kvmclock, &kvmclock_data); -#endif - return 0; -} - -static void set_msr_entry(struct kvm_msr_entry *entry, uint32_t index, - uint64_t data) -{ - entry->index = index; - entry->data = data; -} - -/* returns 0 on success, non-0 on failure */ -static int get_msr_entry(struct kvm_msr_entry *entry, CPUState *env) -{ - switch (entry->index) { - case MSR_IA32_SYSENTER_CS: - env->sysenter_cs = entry->data; - break; - case MSR_IA32_SYSENTER_ESP: - env->sysenter_esp = entry->data; - break; - case MSR_IA32_SYSENTER_EIP: - env->sysenter_eip = entry->data; - break; - case MSR_STAR: - env->star = entry->data; - break; -#ifdef TARGET_X86_64 - case MSR_CSTAR: - env->cstar = entry->data; - break; - case MSR_KERNELGSBASE: - env->kernelgsbase = entry->data; - break; - case MSR_FMASK: - env->fmask = entry->data; - break; - case MSR_LSTAR: - env->lstar = entry->data; - break; -#endif - case MSR_IA32_TSC: - env->tsc = entry->data; - break; - case MSR_VM_HSAVE_PA: - env->vm_hsave = entry->data; - break; - case MSR_KVM_SYSTEM_TIME: - env->system_time_msr = entry->data; - break; - case MSR_KVM_WALL_CLOCK: - env->wall_clock_msr = entry->data; - break; - default: - printf("Warning unknown msr index 0x%x\n", entry->index); - return 1; - } - return 0; -} - -static void set_v8086_seg(struct kvm_segment *lhs, const SegmentCache *rhs) -{ - lhs->selector = rhs->selector; - lhs->base = rhs->base; - lhs->limit = rhs->limit; - lhs->type = 3; - lhs->present = 1; - lhs->dpl = 3; - lhs->db = 0; - lhs->s = 1; - lhs->l = 0; - lhs->g = 0; - lhs->avl = 0; - lhs->unusable = 0; -} - -static void set_seg(struct kvm_segment *lhs, const SegmentCache *rhs) -{ - unsigned flags = rhs->flags; - lhs->selector = rhs->selector; - lhs->base = rhs->base; - lhs->limit = rhs->limit; - lhs->type = (flags >> DESC_TYPE_SHIFT) & 15; - lhs->present = (flags & DESC_P_MASK) != 0; - lhs->dpl = rhs->selector & 3; - lhs->db = (flags >> DESC_B_SHIFT) & 1; - lhs->s = (flags & DESC_S_MASK) != 0; - lhs->l = (flags >> DESC_L_SHIFT) & 1; - lhs->g = (flags & DESC_G_MASK) != 0; - lhs->avl = (flags & DESC_AVL_MASK) != 0; - lhs->unusable = 0; -} - -static void get_seg(SegmentCache *lhs, const struct kvm_segment *rhs) -{ - lhs->selector = rhs->selector; - lhs->base = rhs->base; - lhs->limit = rhs->limit; - lhs->flags = - (rhs->type << DESC_TYPE_SHIFT) - | (rhs->present * DESC_P_MASK) - | (rhs->dpl << DESC_DPL_SHIFT) - | (rhs->db << DESC_B_SHIFT) - | (rhs->s * DESC_S_MASK) - | (rhs->l << DESC_L_SHIFT) - | (rhs->g * DESC_G_MASK) - | (rhs->avl * DESC_AVL_MASK); -} - -void kvm_arch_load_regs(CPUState *env) -{ - struct kvm_regs regs; - struct kvm_fpu fpu; - struct kvm_sregs sregs; - struct kvm_msr_entry msrs[100]; - int rc, n, i; - - regs.rax = env->regs[R_EAX]; - regs.rbx = env->regs[R_EBX]; - regs.rcx = env->regs[R_ECX]; - regs.rdx = env->regs[R_EDX]; - regs.rsi = env->regs[R_ESI]; - regs.rdi = env->regs[R_EDI]; - regs.rsp = env->regs[R_ESP]; - regs.rbp = env->regs[R_EBP]; -#ifdef TARGET_X86_64 - regs.r8 = env->regs[8]; - regs.r9 = env->regs[9]; - regs.r10 = env->regs[10]; - regs.r11 = env->regs[11]; - regs.r12 = env->regs[12]; - regs.r13 = env->regs[13]; - regs.r14 = env->regs[14]; - regs.r15 = env->regs[15]; -#endif - - regs.rflags = env->eflags; - regs.rip = env->eip; + /* initialize has_msr_star/has_msr_hsave_pa */ + r = kvm_get_supported_msrs(kvm_state); + if (r < 0) { + return r; + } - kvm_set_regs(env, ®s); + kvm_msr_list = kvm_get_msr_list(); + if (!kvm_msr_list) { + return -1; + } - memset(&fpu, 0, sizeof fpu); - fpu.fsw = env->fpus & ~(7 << 11); - fpu.fsw |= (env->fpstt & 7) << 11; - fpu.fcw = env->fpuc; - for (i = 0; i < 8; ++i) - fpu.ftwx |= (!env->fptags[i]) << i; - memcpy(fpu.fpr, env->fpregs, sizeof env->fpregs); - memcpy(fpu.xmm, env->xmm_regs, sizeof env->xmm_regs); - fpu.mxcsr = env->mxcsr; - kvm_set_fpu(env, &fpu); - - memset(sregs.interrupt_bitmap, 0, sizeof(sregs.interrupt_bitmap)); - if (env->interrupt_injected >= 0) { - sregs.interrupt_bitmap[env->interrupt_injected / 64] |= - (uint64_t)1 << (env->interrupt_injected % 64); - } - - if ((env->eflags & VM_MASK)) { - set_v8086_seg(&sregs.cs, &env->segs[R_CS]); - set_v8086_seg(&sregs.ds, &env->segs[R_DS]); - set_v8086_seg(&sregs.es, &env->segs[R_ES]); - set_v8086_seg(&sregs.fs, &env->segs[R_FS]); - set_v8086_seg(&sregs.gs, &env->segs[R_GS]); - set_v8086_seg(&sregs.ss, &env->segs[R_SS]); - } else { - set_seg(&sregs.cs, &env->segs[R_CS]); - set_seg(&sregs.ds, &env->segs[R_DS]); - set_seg(&sregs.es, &env->segs[R_ES]); - set_seg(&sregs.fs, &env->segs[R_FS]); - set_seg(&sregs.gs, &env->segs[R_GS]); - set_seg(&sregs.ss, &env->segs[R_SS]); - - if (env->cr[0] & CR0_PE_MASK) { - /* force ss cpl to cs cpl */ - sregs.ss.selector = (sregs.ss.selector & ~3) | - (sregs.cs.selector & 3); - sregs.ss.dpl = sregs.ss.selector & 3; - } - } - - set_seg(&sregs.tr, &env->tr); - set_seg(&sregs.ldt, &env->ldt); - - sregs.idt.limit = env->idt.limit; - sregs.idt.base = env->idt.base; - sregs.gdt.limit = env->gdt.limit; - sregs.gdt.base = env->gdt.base; - - sregs.cr0 = env->cr[0]; - sregs.cr2 = env->cr[2]; - sregs.cr3 = env->cr[3]; - sregs.cr4 = env->cr[4]; - - sregs.cr8 = cpu_get_apic_tpr(env); - sregs.apic_base = cpu_get_apic_base(env); - - sregs.efer = env->efer; - - kvm_set_sregs(env, &sregs); - - /* msrs */ - n = 0; - /* Remember to increase msrs size if you add new registers below */ - set_msr_entry(&msrs[n++], MSR_IA32_SYSENTER_CS, env->sysenter_cs); - set_msr_entry(&msrs[n++], MSR_IA32_SYSENTER_ESP, env->sysenter_esp); - set_msr_entry(&msrs[n++], MSR_IA32_SYSENTER_EIP, env->sysenter_eip); - if (kvm_has_msr_star) - set_msr_entry(&msrs[n++], MSR_STAR, env->star); - if (kvm_has_vm_hsave_pa) - set_msr_entry(&msrs[n++], MSR_VM_HSAVE_PA, env->vm_hsave); -#ifdef TARGET_X86_64 - if (lm_capable_kernel) { - set_msr_entry(&msrs[n++], MSR_CSTAR, env->cstar); - set_msr_entry(&msrs[n++], MSR_KERNELGSBASE, env->kernelgsbase); - set_msr_entry(&msrs[n++], MSR_FMASK, env->fmask); - set_msr_entry(&msrs[n++], MSR_LSTAR , env->lstar); +#ifdef KVM_CAP_ADJUST_CLOCK + if (kvm_check_extension(kvm_state, KVM_CAP_ADJUST_CLOCK)) { + vmstate_register(NULL, 0, &vmstate_kvmclock, &kvmclock_data); } #endif - set_msr_entry(&msrs[n++], MSR_KVM_SYSTEM_TIME, env->system_time_msr); - set_msr_entry(&msrs[n++], MSR_KVM_WALL_CLOCK, env->wall_clock_msr); - - rc = kvm_set_msrs(env, msrs, n); - if (rc == -1) - perror("kvm_set_msrs FAILED"); -} - -void kvm_load_tsc(CPUState *env) -{ - int rc; - struct kvm_msr_entry msr; - set_msr_entry(&msr, MSR_IA32_TSC, env->tsc); + r = kvm_set_boot_cpu_id(0); + if (r < 0 && r != -ENOSYS) { + return r; + } - rc = kvm_set_msrs(env, &msr, 1); - if (rc == -1) - perror("kvm_set_tsc FAILED.\n"); + return 0; } -void kvm_arch_save_mpstate(CPUState *env) +static void kvm_arch_save_mpstate(CPUState *env) { #ifdef KVM_CAP_MP_STATE int r; struct kvm_mp_state mp_state; r = kvm_get_mpstate(env, &mp_state); - if (r < 0) + if (r < 0) { env->mp_state = -1; - else + } else { env->mp_state = mp_state.mp_state; + if (kvm_irqchip_in_kernel()) { + env->halted = (env->mp_state == KVM_MP_STATE_HALTED); + } + } #else env->mp_state = -1; #endif } -void kvm_arch_load_mpstate(CPUState *env) +static void kvm_arch_load_mpstate(CPUState *env) { #ifdef KVM_CAP_MP_STATE - struct kvm_mp_state mp_state = { .mp_state = env->mp_state }; + struct kvm_mp_state mp_state; /* * -1 indicates that the host did not support GET_MP_STATE ioctl, * so don't touch it. */ - if (env->mp_state != -1) + if (env->mp_state != -1) { + mp_state.mp_state = env->mp_state; kvm_set_mpstate(env, &mp_state); + } #endif } -void kvm_arch_save_regs(CPUState *env) -{ - struct kvm_regs regs; - struct kvm_fpu fpu; - struct kvm_sregs sregs; - struct kvm_msr_entry msrs[100]; - uint32_t hflags; - uint32_t i, n, rc, bit; - - kvm_get_regs(env, ®s); - - env->regs[R_EAX] = regs.rax; - env->regs[R_EBX] = regs.rbx; - env->regs[R_ECX] = regs.rcx; - env->regs[R_EDX] = regs.rdx; - env->regs[R_ESI] = regs.rsi; - env->regs[R_EDI] = regs.rdi; - env->regs[R_ESP] = regs.rsp; - env->regs[R_EBP] = regs.rbp; -#ifdef TARGET_X86_64 - env->regs[8] = regs.r8; - env->regs[9] = regs.r9; - env->regs[10] = regs.r10; - env->regs[11] = regs.r11; - env->regs[12] = regs.r12; - env->regs[13] = regs.r13; - env->regs[14] = regs.r14; - env->regs[15] = regs.r15; -#endif - - env->eflags = regs.rflags; - env->eip = regs.rip; - - kvm_get_fpu(env, &fpu); - env->fpstt = (fpu.fsw >> 11) & 7; - env->fpus = fpu.fsw; - env->fpuc = fpu.fcw; - for (i = 0; i < 8; ++i) - env->fptags[i] = !((fpu.ftwx >> i) & 1); - memcpy(env->fpregs, fpu.fpr, sizeof env->fpregs); - memcpy(env->xmm_regs, fpu.xmm, sizeof env->xmm_regs); - env->mxcsr = fpu.mxcsr; - - kvm_get_sregs(env, &sregs); - - /* There can only be one pending IRQ set in the bitmap at a time, so try - to find it and save its number instead (-1 for none). */ - env->interrupt_injected = -1; - for (i = 0; i < ARRAY_SIZE(sregs.interrupt_bitmap); i++) { - if (sregs.interrupt_bitmap[i]) { - bit = ctz64(sregs.interrupt_bitmap[i]); - env->interrupt_injected = i * 64 + bit; - break; - } - } - - get_seg(&env->segs[R_CS], &sregs.cs); - get_seg(&env->segs[R_DS], &sregs.ds); - get_seg(&env->segs[R_ES], &sregs.es); - get_seg(&env->segs[R_FS], &sregs.fs); - get_seg(&env->segs[R_GS], &sregs.gs); - get_seg(&env->segs[R_SS], &sregs.ss); - - get_seg(&env->tr, &sregs.tr); - get_seg(&env->ldt, &sregs.ldt); - - env->idt.limit = sregs.idt.limit; - env->idt.base = sregs.idt.base; - env->gdt.limit = sregs.gdt.limit; - env->gdt.base = sregs.gdt.base; - - env->cr[0] = sregs.cr0; - env->cr[2] = sregs.cr2; - env->cr[3] = sregs.cr3; - env->cr[4] = sregs.cr4; - - cpu_set_apic_base(env, sregs.apic_base); - - env->efer = sregs.efer; - //cpu_set_apic_tpr(env, sregs.cr8); - -#define HFLAG_COPY_MASK ~( \ - HF_CPL_MASK | HF_PE_MASK | HF_MP_MASK | HF_EM_MASK | \ - HF_TS_MASK | HF_TF_MASK | HF_VM_MASK | HF_IOPL_MASK | \ - HF_OSFXSR_MASK | HF_LMA_MASK | HF_CS32_MASK | \ - HF_SS32_MASK | HF_CS64_MASK | HF_ADDSEG_MASK) +#define XSAVE_CWD_RIP 2 +#define XSAVE_CWD_RDP 4 +#define XSAVE_MXCSR 6 +#define XSAVE_ST_SPACE 8 +#define XSAVE_XMM_SPACE 40 +#define XSAVE_XSTATE_BV 128 +#define XSAVE_YMMH_SPACE 144 +void kvm_arch_load_regs(CPUState *env, int level) +{ + int rc; + assert(kvm_cpu_is_stopped(env) || env->thread_id == kvm_get_thread_id()); - hflags = (env->segs[R_CS].flags >> DESC_DPL_SHIFT) & HF_CPL_MASK; - hflags |= (env->cr[0] & CR0_PE_MASK) << (HF_PE_SHIFT - CR0_PE_SHIFT); - hflags |= (env->cr[0] << (HF_MP_SHIFT - CR0_MP_SHIFT)) & - (HF_MP_MASK | HF_EM_MASK | HF_TS_MASK); - hflags |= (env->eflags & (HF_TF_MASK | HF_VM_MASK | HF_IOPL_MASK)); - hflags |= (env->cr[4] & CR4_OSFXSR_MASK) << - (HF_OSFXSR_SHIFT - CR4_OSFXSR_SHIFT); + kvm_getput_regs(env, 1); - if (env->efer & MSR_EFER_LMA) { - hflags |= HF_LMA_MASK; - } + kvm_put_xsave(env); + kvm_put_xcrs(env); - if ((hflags & HF_LMA_MASK) && (env->segs[R_CS].flags & DESC_L_MASK)) { - hflags |= HF_CS32_MASK | HF_SS32_MASK | HF_CS64_MASK; - } else { - hflags |= (env->segs[R_CS].flags & DESC_B_MASK) >> - (DESC_B_SHIFT - HF_CS32_SHIFT); - hflags |= (env->segs[R_SS].flags & DESC_B_MASK) >> - (DESC_B_SHIFT - HF_SS32_SHIFT); - if (!(env->cr[0] & CR0_PE_MASK) || - (env->eflags & VM_MASK) || - !(hflags & HF_CS32_MASK)) { - hflags |= HF_ADDSEG_MASK; - } else { - hflags |= ((env->segs[R_DS].base | - env->segs[R_ES].base | - env->segs[R_SS].base) != 0) << - HF_ADDSEG_SHIFT; - } - } - env->hflags = (env->hflags & HFLAG_COPY_MASK) | hflags; + kvm_put_sregs(env); - /* msrs */ - n = 0; - /* Remember to increase msrs size if you add new registers below */ - msrs[n++].index = MSR_IA32_SYSENTER_CS; - msrs[n++].index = MSR_IA32_SYSENTER_ESP; - msrs[n++].index = MSR_IA32_SYSENTER_EIP; - if (kvm_has_msr_star) - msrs[n++].index = MSR_STAR; - msrs[n++].index = MSR_IA32_TSC; - if (kvm_has_vm_hsave_pa) - msrs[n++].index = MSR_VM_HSAVE_PA; -#ifdef TARGET_X86_64 - if (lm_capable_kernel) { - msrs[n++].index = MSR_CSTAR; - msrs[n++].index = MSR_KERNELGSBASE; - msrs[n++].index = MSR_FMASK; - msrs[n++].index = MSR_LSTAR; + rc = kvm_put_msrs(env, level); + if (rc < 0) { + perror("kvm__msrs FAILED"); } -#endif - msrs[n++].index = MSR_KVM_SYSTEM_TIME; - msrs[n++].index = MSR_KVM_WALL_CLOCK; - rc = kvm_get_msrs(env, msrs, n); - if (rc == -1) { - perror("kvm_get_msrs FAILED"); + if (level >= KVM_PUT_RESET_STATE) { + kvm_arch_load_mpstate(env); + kvm_load_lapic(env); } - else { - n = rc; /* actual number of MSRs */ - for (i=0 ; ikvm_vcpu_update_vapic) { + kvm_tpr_enable_vapic(env); } } - kvm_arch_save_mpstate(env); + + kvm_put_vcpu_events(env, level); + kvm_put_debugregs(env); + + /* must be last */ + kvm_guest_debug_workarounds(env); } -static void do_cpuid_ent(struct kvm_cpuid_entry2 *e, uint32_t function, - uint32_t count, CPUState *env) +void kvm_arch_save_regs(CPUState *env) { - env->regs[R_EAX] = function; - env->regs[R_ECX] = count; - qemu_kvm_cpuid_on_env(env); - e->function = function; - e->flags = 0; - e->index = 0; - e->eax = env->regs[R_EAX]; - e->ebx = env->regs[R_EBX]; - e->ecx = env->regs[R_ECX]; - e->edx = env->regs[R_EDX]; -} - -struct kvm_para_features { - int cap; - int feature; -} para_features[] = { -#ifdef KVM_CAP_CLOCKSOURCE - { KVM_CAP_CLOCKSOURCE, KVM_FEATURE_CLOCKSOURCE }, -#endif -#ifdef KVM_CAP_NOP_IO_DELAY - { KVM_CAP_NOP_IO_DELAY, KVM_FEATURE_NOP_IO_DELAY }, -#endif -#ifdef KVM_CAP_PV_MMU - { KVM_CAP_PV_MMU, KVM_FEATURE_MMU_OP }, -#endif -#ifdef KVM_CAP_CR3_CACHE - { KVM_CAP_CR3_CACHE, KVM_FEATURE_CR3_CACHE }, -#endif - { -1, -1 } -}; + int rc; -static int get_para_features(kvm_context_t kvm_context) -{ - int i, features = 0; + assert(kvm_cpu_is_stopped(env) || env->thread_id == kvm_get_thread_id()); - for (i = 0; i < ARRAY_SIZE(para_features)-1; i++) { - if (kvm_check_extension(kvm_state, para_features[i].cap)) - features |= (1 << para_features[i].feature); - } + kvm_getput_regs(env, 0); - return features; -} + kvm_get_xsave(env); + kvm_get_xcrs(env); -static void kvm_trim_features(uint32_t *features, uint32_t supported) -{ - int i; - uint32_t mask; + kvm_get_sregs(env); - for (i = 0; i < 32; ++i) { - mask = 1U << i; - if ((*features & mask) && !(supported & mask)) { - *features &= ~mask; - } + rc = kvm_get_msrs(env); + if (rc < 0) { + perror("kvm_get_msrs FAILED"); } + + kvm_arch_save_mpstate(env); + kvm_save_lapic(env); + kvm_get_vcpu_events(env); + kvm_get_debugregs(env); } -int kvm_arch_init_vcpu(CPUState *cenv) +static int _kvm_arch_init_vcpu(CPUState *env) { - struct kvm_cpuid_entry2 cpuid_ent[100]; -#ifdef KVM_CPUID_SIGNATURE - struct kvm_cpuid_entry2 *pv_ent; - uint32_t signature[3]; -#endif - int cpuid_nent = 0; - CPUState copy; - uint32_t i, j, limit; - - qemu_kvm_load_lapic(cenv); - - cenv->interrupt_injected = -1; - -#ifdef KVM_CPUID_SIGNATURE - /* Paravirtualization CPUIDs */ - memcpy(signature, "KVMKVMKVM\0\0\0", 12); - pv_ent = &cpuid_ent[cpuid_nent++]; - memset(pv_ent, 0, sizeof(*pv_ent)); - pv_ent->function = KVM_CPUID_SIGNATURE; - pv_ent->eax = 0; - pv_ent->ebx = signature[0]; - pv_ent->ecx = signature[1]; - pv_ent->edx = signature[2]; - - pv_ent = &cpuid_ent[cpuid_nent++]; - memset(pv_ent, 0, sizeof(*pv_ent)); - pv_ent->function = KVM_CPUID_FEATURES; - pv_ent->eax = get_para_features(kvm_context); -#endif - - kvm_trim_features(&cenv->cpuid_features, - kvm_arch_get_supported_cpuid(cenv, 1, R_EDX)); - - /* prevent the hypervisor bit from being cleared by the kernel */ - i = cenv->cpuid_ext_features & CPUID_EXT_HYPERVISOR; - kvm_trim_features(&cenv->cpuid_ext_features, - kvm_arch_get_supported_cpuid(cenv, 1, R_ECX)); - cenv->cpuid_ext_features |= i; - - kvm_trim_features(&cenv->cpuid_ext2_features, - kvm_arch_get_supported_cpuid(cenv, 0x80000001, R_EDX)); - kvm_trim_features(&cenv->cpuid_ext3_features, - kvm_arch_get_supported_cpuid(cenv, 0x80000001, R_ECX)); - - copy = *cenv; - - copy.regs[R_EAX] = 0; - qemu_kvm_cpuid_on_env(©); - limit = copy.regs[R_EAX]; - - for (i = 0; i <= limit; ++i) { - if (i == 4 || i == 0xb || i == 0xd) { - for (j = 0; ; ++j) { - do_cpuid_ent(&cpuid_ent[cpuid_nent], i, j, ©); - - cpuid_ent[cpuid_nent].flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX; - cpuid_ent[cpuid_nent].index = j; - - cpuid_nent++; - - if (i == 4 && copy.regs[R_EAX] == 0) - break; - if (i == 0xb && !(copy.regs[R_ECX] & 0xff00)) - break; - if (i == 0xd && copy.regs[R_EAX] == 0) - break; - } - } else - do_cpuid_ent(&cpuid_ent[cpuid_nent++], i, 0, ©); - } - - copy.regs[R_EAX] = 0x80000000; - qemu_kvm_cpuid_on_env(©); - limit = copy.regs[R_EAX]; - - for (i = 0x80000000; i <= limit; ++i) - do_cpuid_ent(&cpuid_ent[cpuid_nent++], i, 0, ©); - - kvm_setup_cpuid2(cenv, cpuid_nent, cpuid_ent); - -#ifdef KVM_CAP_MCE - if (((cenv->cpuid_version >> 8)&0xF) >= 6 - && (cenv->cpuid_features&(CPUID_MCE|CPUID_MCA)) == (CPUID_MCE|CPUID_MCA) - && kvm_check_extension(kvm_state, KVM_CAP_MCE) > 0) { - uint64_t mcg_cap; - int banks; - - if (kvm_get_mce_cap_supported(kvm_context, &mcg_cap, &banks)) - perror("kvm_get_mce_cap_supported FAILED"); - else { - if (banks > MCE_BANKS_DEF) - banks = MCE_BANKS_DEF; - mcg_cap &= MCE_CAP_DEF; - mcg_cap |= banks; - if (kvm_setup_mce(cenv, &mcg_cap)) - perror("kvm_setup_mce FAILED"); - else - cenv->mcg_cap = mcg_cap; - } - } -#endif + kvm_arch_reset_vcpu(env); #ifdef KVM_EXIT_TPR_ACCESS - kvm_tpr_vcpu_start(cenv); + kvm_enable_tpr_access_reporting(env); #endif return 0; } @@ -1393,29 +651,28 @@ { if (!((env->interrupt_request & CPU_INTERRUPT_HARD) && - (env->eflags & IF_MASK)) && - !(env->interrupt_request & CPU_INTERRUPT_NMI)) { - env->halted = 1; + (env->eflags & IF_MASK)) && + !(env->interrupt_request & CPU_INTERRUPT_NMI)) { + env->halted = 1; } return 1; } int kvm_arch_pre_run(CPUState *env, struct kvm_run *run) { - if (env->update_vapic) { - kvm_tpr_enable_vapic(env); + if (!kvm_irqchip_in_kernel()) { + kvm_set_cr8(env, cpu_get_apic_tpr(env->apic_state)); } - if (!kvm_irqchip_in_kernel()) - kvm_set_cr8(env, cpu_get_apic_tpr(env)); return 0; } int kvm_arch_has_work(CPUState *env) { if (((env->interrupt_request & CPU_INTERRUPT_HARD) && - (env->eflags & IF_MASK)) || - (env->interrupt_request & CPU_INTERRUPT_NMI)) - return 1; + (env->eflags & IF_MASK)) || + (env->interrupt_request & CPU_INTERRUPT_NMI)) { + return 1; + } return 0; } @@ -1427,13 +684,14 @@ if (kvm_is_ready_for_interrupt_injection(env) && (env->interrupt_request & CPU_INTERRUPT_HARD) && (env->eflags & IF_MASK)) { - env->interrupt_request &= ~CPU_INTERRUPT_HARD; - irq = cpu_get_pic_interrupt(env); - if (irq >= 0) { - r = kvm_inject_irq(env, irq); - if (r < 0) - printf("cpu %d fail inject %x\n", env->cpu_index, irq); - } + env->interrupt_request &= ~CPU_INTERRUPT_HARD; + irq = cpu_get_pic_interrupt(env); + if (irq >= 0) { + r = kvm_inject_irq(env, irq); + if (r < 0) { + printf("cpu %d fail inject %x\n", env->cpu_index, irq); + } + } } return (env->interrupt_request & CPU_INTERRUPT_HARD) != 0; @@ -1445,203 +703,56 @@ CPUState *env = cpu_single_env; int r; - if (likely(!(env->interrupt_request & CPU_INTERRUPT_NMI))) + if (likely(!(env->interrupt_request & CPU_INTERRUPT_NMI))) { return; + } env->interrupt_request &= ~CPU_INTERRUPT_NMI; r = kvm_inject_nmi(env); - if (r < 0) + if (r < 0) { printf("cpu %d fail inject NMI\n", env->cpu_index); -} -#endif /* KVM_CAP_USER_NMI */ - -void kvm_arch_cpu_reset(CPUState *env) -{ - kvm_arch_reset_vcpu(env); - kvm_arch_load_regs(env); - kvm_put_vcpu_events(env); - if (!cpu_is_bsp(env)) { - if (kvm_irqchip_in_kernel()) { -#ifdef KVM_CAP_MP_STATE - kvm_reset_mpstate(env); -#endif - } else { - env->interrupt_request &= ~CPU_INTERRUPT_HARD; - env->halted = 1; - } } } +#endif /* KVM_CAP_USER_NMI */ -int kvm_arch_insert_sw_breakpoint(CPUState *env, struct kvm_sw_breakpoint *bp) -{ - uint8_t int3 = 0xcc; - - if (cpu_memory_rw_debug(env, bp->pc, (uint8_t *)&bp->saved_insn, 1, 0) || - cpu_memory_rw_debug(env, bp->pc, &int3, 1, 1)) - return -EINVAL; - return 0; -} - -int kvm_arch_remove_sw_breakpoint(CPUState *env, struct kvm_sw_breakpoint *bp) -{ - uint8_t int3; - - if (cpu_memory_rw_debug(env, bp->pc, &int3, 1, 0) || int3 != 0xcc || - cpu_memory_rw_debug(env, bp->pc, (uint8_t *)&bp->saved_insn, 1, 1)) - return -EINVAL; - return 0; -} - -#ifdef KVM_CAP_SET_GUEST_DEBUG -static struct { - target_ulong addr; - int len; - int type; -} hw_breakpoint[4]; - -static int nb_hw_breakpoint; - -static int find_hw_breakpoint(target_ulong addr, int len, int type) +static int kvm_reset_msrs(CPUState *env) { + struct { + struct kvm_msrs info; + struct kvm_msr_entry entries[100]; + } msr_data; int n; + struct kvm_msr_entry *msrs = msr_data.entries; + uint32_t index; + uint64_t data; - for (n = 0; n < nb_hw_breakpoint; n++) - if (hw_breakpoint[n].addr == addr && hw_breakpoint[n].type == type && - (hw_breakpoint[n].len == len || len == -1)) - return n; - return -1; -} - -int kvm_arch_insert_hw_breakpoint(target_ulong addr, - target_ulong len, int type) -{ - switch (type) { - case GDB_BREAKPOINT_HW: - len = 1; - break; - case GDB_WATCHPOINT_WRITE: - case GDB_WATCHPOINT_ACCESS: - switch (len) { - case 1: - break; - case 2: - case 4: - case 8: - if (addr & (len - 1)) - return -EINVAL; - break; - default: - return -EINVAL; - } - break; - default: - return -ENOSYS; + if (!kvm_msr_list) { + return -1; } - if (nb_hw_breakpoint == 4) - return -ENOBUFS; - - if (find_hw_breakpoint(addr, len, type) >= 0) - return -EEXIST; - - hw_breakpoint[nb_hw_breakpoint].addr = addr; - hw_breakpoint[nb_hw_breakpoint].len = len; - hw_breakpoint[nb_hw_breakpoint].type = type; - nb_hw_breakpoint++; - - return 0; -} - -int kvm_arch_remove_hw_breakpoint(target_ulong addr, - target_ulong len, int type) -{ - int n; - - n = find_hw_breakpoint(addr, (type == GDB_BREAKPOINT_HW) ? 1 : len, type); - if (n < 0) - return -ENOENT; - - nb_hw_breakpoint--; - hw_breakpoint[n] = hw_breakpoint[nb_hw_breakpoint]; + for (n = 0; n < kvm_msr_list->nmsrs; n++) { + index = kvm_msr_list->indices[n]; + switch (index) { + case MSR_PAT: + data = 0x0007040600070406ULL; + break; + default: + data = 0; + } + kvm_msr_entry_set(&msrs[n], kvm_msr_list->indices[n], data); + } - return 0; -} + msr_data.info.nmsrs = n; -void kvm_arch_remove_all_hw_breakpoints(void) -{ - nb_hw_breakpoint = 0; + return kvm_vcpu_ioctl(env, KVM_SET_MSRS, &msr_data); } -static CPUWatchpoint hw_watchpoint; -int kvm_arch_debug(struct kvm_debug_exit_arch *arch_info) +void kvm_arch_cpu_reset(CPUState *env) { - int handle = 0; - int n; - - if (arch_info->exception == 1) { - if (arch_info->dr6 & (1 << 14)) { - if (cpu_single_env->singlestep_enabled) - handle = 1; - } else { - for (n = 0; n < 4; n++) - if (arch_info->dr6 & (1 << n)) - switch ((arch_info->dr7 >> (16 + n*4)) & 0x3) { - case 0x0: - handle = 1; - break; - case 0x1: - handle = 1; - cpu_single_env->watchpoint_hit = &hw_watchpoint; - hw_watchpoint.vaddr = hw_breakpoint[n].addr; - hw_watchpoint.flags = BP_MEM_WRITE; - break; - case 0x3: - handle = 1; - cpu_single_env->watchpoint_hit = &hw_watchpoint; - hw_watchpoint.vaddr = hw_breakpoint[n].addr; - hw_watchpoint.flags = BP_MEM_ACCESS; - break; - } - } - } else if (kvm_find_sw_breakpoint(cpu_single_env, arch_info->pc)) - handle = 1; - - if (!handle) - kvm_update_guest_debug(cpu_single_env, - (arch_info->exception == 1) ? - KVM_GUESTDBG_INJECT_DB : KVM_GUESTDBG_INJECT_BP); - - return handle; -} - -void kvm_arch_update_guest_debug(CPUState *env, struct kvm_guest_debug *dbg) -{ - const uint8_t type_code[] = { - [GDB_BREAKPOINT_HW] = 0x0, - [GDB_WATCHPOINT_WRITE] = 0x1, - [GDB_WATCHPOINT_ACCESS] = 0x3 - }; - const uint8_t len_code[] = { - [1] = 0x0, [2] = 0x1, [4] = 0x3, [8] = 0x2 - }; - int n; - - if (kvm_sw_breakpoints_active(env)) - dbg->control |= KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_SW_BP; - - if (nb_hw_breakpoint > 0) { - dbg->control |= KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_HW_BP; - dbg->arch.debugreg[7] = 0x0600; - for (n = 0; n < nb_hw_breakpoint; n++) { - dbg->arch.debugreg[n] = hw_breakpoint[n].addr; - dbg->arch.debugreg[7] |= (2 << (n * 2)) | - (type_code[hw_breakpoint[n].type] << (16 + n*4)) | - (len_code[hw_breakpoint[n].len] << (18 + n*4)); - } - } + kvm_reset_msrs(env); + kvm_arch_reset_vcpu(env); } -#endif #ifdef CONFIG_KVM_DEVICE_ASSIGNMENT void kvm_arch_do_ioperm(void *_data) @@ -1658,40 +769,38 @@ { int i, r; - if (kvm_irqchip && kvm_has_gsi_routing(kvm_context)) { - kvm_clear_gsi_routes(kvm_context); + if (kvm_irqchip && kvm_has_gsi_routing()) { + kvm_clear_gsi_routes(); for (i = 0; i < 8; ++i) { - if (i == 2) + if (i == 2) { continue; - r = kvm_add_irq_route(kvm_context, i, KVM_IRQCHIP_PIC_MASTER, i); - if (r < 0) + } + r = kvm_add_irq_route(i, KVM_IRQCHIP_PIC_MASTER, i); + if (r < 0) { return r; + } } for (i = 8; i < 16; ++i) { - r = kvm_add_irq_route(kvm_context, i, KVM_IRQCHIP_PIC_SLAVE, i - 8); - if (r < 0) + r = kvm_add_irq_route(i, KVM_IRQCHIP_PIC_SLAVE, i - 8); + if (r < 0) { return r; + } } for (i = 0; i < 24; ++i) { - if (i == 0) { - r = kvm_add_irq_route(kvm_context, i, KVM_IRQCHIP_IOAPIC, 2); - } else if (i != 2) { - r = kvm_add_irq_route(kvm_context, i, KVM_IRQCHIP_IOAPIC, i); + if (i == 0 && irq0override) { + r = kvm_add_irq_route(i, KVM_IRQCHIP_IOAPIC, 2); + } else if (i != 2 || !irq0override) { + r = kvm_add_irq_route(i, KVM_IRQCHIP_IOAPIC, i); } - if (r < 0) + if (r < 0) { return r; + } } - kvm_commit_irq_routes(kvm_context); + kvm_commit_irq_routes(); } return 0; } -uint32_t kvm_arch_get_supported_cpuid(CPUState *env, uint32_t function, - int reg) -{ - return kvm_get_supported_cpuid(kvm_context, function, reg); -} - void kvm_arch_process_irqchip_events(CPUState *env) { if (env->interrupt_request & CPU_INTERRUPT_INIT) { diff -Nru qemu-kvm-0.12.5+noroms/qemu-lock.h qemu-kvm-0.14.1/qemu-lock.h --- qemu-kvm-0.12.5+noroms/qemu-lock.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/qemu-lock.h 2011-05-11 13:29:46.000000000 +0000 @@ -224,11 +224,6 @@ { resetlock(lock); } - -static inline int spin_trylock(spinlock_t *lock) -{ - return !testandset(lock); -} #else static inline void spin_lock(spinlock_t *lock) { @@ -237,11 +232,6 @@ static inline void spin_unlock(spinlock_t *lock) { } - -static inline int spin_trylock(spinlock_t *lock) -{ - return 1; -} #endif #endif diff -Nru qemu-kvm-0.12.5+noroms/qemu-malloc.c qemu-kvm-0.14.1/qemu-malloc.c --- qemu-kvm-0.12.5+noroms/qemu-malloc.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/qemu-malloc.c 2011-05-11 13:29:46.000000000 +0000 @@ -22,23 +22,12 @@ * THE SOFTWARE. */ #include "qemu-common.h" +#include "trace.h" #include -static void *oom_check(void *ptr) -{ - if (ptr == NULL) { - abort(); - } - return ptr; -} - -void *get_mmap_addr(unsigned long size) -{ - return NULL; -} - void qemu_free(void *ptr) { + trace_qemu_free(ptr); free(ptr); } @@ -53,27 +42,34 @@ void *qemu_malloc(size_t size) { + void *ptr; if (!size && !allow_zero_malloc()) { abort(); } - return oom_check(malloc(size ? size : 1)); + ptr = qemu_oom_check(malloc(size ? size : 1)); + trace_qemu_malloc(size, ptr); + return ptr; } void *qemu_realloc(void *ptr, size_t size) { - if (size) { - return oom_check(realloc(ptr, size)); - } else if (allow_zero_malloc()) { - return oom_check(realloc(ptr, size ? size : 1)); + void *newptr; + if (!size && !allow_zero_malloc()) { + abort(); } - abort(); + newptr = qemu_oom_check(realloc(ptr, size ? size : 1)); + trace_qemu_realloc(ptr, size, newptr); + return newptr; } void *qemu_mallocz(size_t size) { void *ptr; - ptr = qemu_malloc(size); - memset(ptr, 0, size); + if (!size && !allow_zero_malloc()) { + abort(); + } + ptr = qemu_oom_check(calloc(1, size ? size : 1)); + trace_qemu_malloc(size, ptr); return ptr; } diff -Nru qemu-kvm-0.12.5+noroms/qemu-monitor.hx qemu-kvm-0.14.1/qemu-monitor.hx --- qemu-kvm-0.12.5+noroms/qemu-monitor.hx 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/qemu-monitor.hx 1970-01-01 00:00:00.000000000 +0000 @@ -1,1080 +0,0 @@ -HXCOMM Use DEFHEADING() to define headings in both help text and texi -HXCOMM Text between STEXI and ETEXI are copied to texi version and -HXCOMM discarded from C version -HXCOMM DEF(command, args, callback, arg_string, help) is used to construct -HXCOMM monitor commands -HXCOMM HXCOMM can be used for comments, discarded from both texi and C - -STEXI -@table @option -ETEXI - - { - .name = "help|?", - .args_type = "name:s?", - .params = "[cmd]", - .help = "show the help", - .mhandler.cmd = do_help_cmd, - }, - -STEXI -@item help or ? [@var{cmd}] -Show the help for all commands or just for command @var{cmd}. -ETEXI - - { - .name = "commit", - .args_type = "device:B", - .params = "device|all", - .help = "commit changes to the disk images (if -snapshot is used) or backing files", - .mhandler.cmd = do_commit, - }, - -STEXI -@item commit -Commit changes to the disk images (if -snapshot is used) or backing files. -ETEXI - - { - .name = "info", - .args_type = "item:s?", - .params = "[subcommand]", - .help = "show various information about the system state", - .user_print = monitor_user_noop, - .mhandler.cmd_new = do_info, - }, - -STEXI -@item info @var{subcommand} -Show various information about the system state. - -@table @option -@item info version -show the version of QEMU -@item info network -show the various VLANs and the associated devices -@item info chardev -show the character devices -@item info block -show the block devices -@item info block -show block device statistics -@item info registers -show the cpu registers -@item info cpus -show infos for each CPU -@item info history -show the command line history -@item info irq -show the interrupts statistics (if available) -@item info pic -show i8259 (PIC) state -@item info pci -show emulated PCI device info -@item info tlb -show virtual to physical memory mappings (i386 only) -@item info mem -show the active virtual memory mappings (i386 only) -@item info hpet -show state of HPET (i386 only) -@item info kvm -show KVM information -@item info usb -show USB devices plugged on the virtual USB hub -@item info usbhost -show all USB host devices -@item info profile -show profiling information -@item info capture -show information about active capturing -@item info snapshots -show list of VM snapshots -@item info status -show the current VM status (running|paused) -@item info pcmcia -show guest PCMCIA status -@item info mice -show which guest mouse is receiving events -@item info vnc -show the vnc server status -@item info name -show the current VM name -@item info uuid -show the current VM UUID -@item info cpustats -show CPU statistics -@item info usernet -show user network stack connection states -@item info migrate -show migration status -@item info balloon -show balloon information -@item info qtree -show device tree -@end table -ETEXI - - { - .name = "q|quit", - .args_type = "", - .params = "", - .help = "quit the emulator", - .user_print = monitor_user_noop, - .mhandler.cmd_new = do_quit, - }, - -STEXI -@item q or quit -Quit the emulator. -ETEXI - - { - .name = "eject", - .args_type = "force:-f,device:B", - .params = "[-f] device", - .help = "eject a removable medium (use -f to force it)", - .user_print = monitor_user_noop, - .mhandler.cmd_new = do_eject, - }, - -STEXI -@item eject [-f] @var{device} -Eject a removable medium (use -f to force it). -ETEXI - - { - .name = "change", - .args_type = "device:B,target:F,arg:s?", - .params = "device filename [format]", - .help = "change a removable medium, optional format", - .user_print = monitor_user_noop, - .mhandler.cmd_new = do_change, - }, - -STEXI -@item change @var{device} @var{setting} - -Change the configuration of a device. - -@table @option -@item change @var{diskdevice} @var{filename} [@var{format}] -Change the medium for a removable disk device to point to @var{filename}. eg - -@example -(qemu) change ide1-cd0 /path/to/some.iso -@end example - -@var{format} is optional. - -@item change vnc @var{display},@var{options} -Change the configuration of the VNC server. The valid syntax for @var{display} -and @var{options} are described at @ref{sec_invocation}. eg - -@example -(qemu) change vnc localhost:1 -@end example - -@item change vnc password [@var{password}] - -Change the password associated with the VNC server. If the new password is not -supplied, the monitor will prompt for it to be entered. VNC passwords are only -significant up to 8 letters. eg - -@example -(qemu) change vnc password -Password: ******** -@end example - -@end table -ETEXI - - { - .name = "screendump", - .args_type = "filename:F", - .params = "filename", - .help = "save screen into PPM image 'filename'", - .mhandler.cmd = do_screen_dump, - }, - -STEXI -@item screendump @var{filename} -Save screen into PPM image @var{filename}. -ETEXI - - { - .name = "logfile", - .args_type = "filename:F", - .params = "filename", - .help = "output logs to 'filename'", - .mhandler.cmd = do_logfile, - }, - -STEXI -@item logfile @var{filename} -Output logs to @var{filename}. -ETEXI - - { - .name = "log", - .args_type = "items:s", - .params = "item1[,...]", - .help = "activate logging of the specified items to '/tmp/qemu.log'", - .mhandler.cmd = do_log, - }, - -STEXI -@item log @var{item1}[,...] -Activate logging of the specified items to @file{/tmp/qemu.log}. -ETEXI - - { - .name = "savevm", - .args_type = "name:s?", - .params = "[tag|id]", - .help = "save a VM snapshot. If no tag or id are provided, a new snapshot is created", - .mhandler.cmd = do_savevm, - }, - -STEXI -@item savevm [@var{tag}|@var{id}] -Create a snapshot of the whole virtual machine. If @var{tag} is -provided, it is used as human readable identifier. If there is already -a snapshot with the same tag or ID, it is replaced. More info at -@ref{vm_snapshots}. -ETEXI - - { - .name = "loadvm", - .args_type = "name:s", - .params = "tag|id", - .help = "restore a VM snapshot from its tag or id", - .mhandler.cmd = do_loadvm, - }, - -STEXI -@item loadvm @var{tag}|@var{id} -Set the whole virtual machine to the snapshot identified by the tag -@var{tag} or the unique snapshot ID @var{id}. -ETEXI - - { - .name = "delvm", - .args_type = "name:s", - .params = "tag|id", - .help = "delete a VM snapshot from its tag or id", - .mhandler.cmd = do_delvm, - }, - -STEXI -@item delvm @var{tag}|@var{id} -Delete the snapshot identified by @var{tag} or @var{id}. -ETEXI - - { - .name = "singlestep", - .args_type = "option:s?", - .params = "[on|off]", - .help = "run emulation in singlestep mode or switch to normal mode", - .mhandler.cmd = do_singlestep, - }, - -STEXI -@item singlestep [off] -Run the emulation in single step mode. -If called with option off, the emulation returns to normal mode. -ETEXI - - { - .name = "stop", - .args_type = "", - .params = "", - .help = "stop emulation", - .user_print = monitor_user_noop, - .mhandler.cmd_new = do_stop, - }, - -STEXI -@item stop -Stop emulation. -ETEXI - - { - .name = "c|cont", - .args_type = "", - .params = "", - .help = "resume emulation", - .user_print = monitor_user_noop, - .mhandler.cmd_new = do_cont, - }, - -STEXI -@item c or cont -Resume emulation. -ETEXI - - { - .name = "gdbserver", - .args_type = "device:s?", - .params = "[device]", - .help = "start gdbserver on given device (default 'tcp::1234'), stop with 'none'", - .mhandler.cmd = do_gdbserver, - }, - -STEXI -@item gdbserver [@var{port}] -Start gdbserver session (default @var{port}=1234) -ETEXI - - { - .name = "x", - .args_type = "fmt:/,addr:l", - .params = "/fmt addr", - .help = "virtual memory dump starting at 'addr'", - .mhandler.cmd = do_memory_dump, - }, - -STEXI -@item x/fmt @var{addr} -Virtual memory dump starting at @var{addr}. -ETEXI - - { - .name = "xp", - .args_type = "fmt:/,addr:l", - .params = "/fmt addr", - .help = "physical memory dump starting at 'addr'", - .mhandler.cmd = do_physical_memory_dump, - }, - -STEXI -@item xp /@var{fmt} @var{addr} -Physical memory dump starting at @var{addr}. - -@var{fmt} is a format which tells the command how to format the -data. Its syntax is: @option{/@{count@}@{format@}@{size@}} - -@table @var -@item count -is the number of items to be dumped. - -@item format -can be x (hex), d (signed decimal), u (unsigned decimal), o (octal), -c (char) or i (asm instruction). - -@item size -can be b (8 bits), h (16 bits), w (32 bits) or g (64 bits). On x86, -@code{h} or @code{w} can be specified with the @code{i} format to -respectively select 16 or 32 bit code instruction size. - -@end table - -Examples: -@itemize -@item -Dump 10 instructions at the current instruction pointer: -@example -(qemu) x/10i $eip -0x90107063: ret -0x90107064: sti -0x90107065: lea 0x0(%esi,1),%esi -0x90107069: lea 0x0(%edi,1),%edi -0x90107070: ret -0x90107071: jmp 0x90107080 -0x90107073: nop -0x90107074: nop -0x90107075: nop -0x90107076: nop -@end example - -@item -Dump 80 16 bit values at the start of the video memory. -@smallexample -(qemu) xp/80hx 0xb8000 -0x000b8000: 0x0b50 0x0b6c 0x0b65 0x0b78 0x0b38 0x0b36 0x0b2f 0x0b42 -0x000b8010: 0x0b6f 0x0b63 0x0b68 0x0b73 0x0b20 0x0b56 0x0b47 0x0b41 -0x000b8020: 0x0b42 0x0b69 0x0b6f 0x0b73 0x0b20 0x0b63 0x0b75 0x0b72 -0x000b8030: 0x0b72 0x0b65 0x0b6e 0x0b74 0x0b2d 0x0b63 0x0b76 0x0b73 -0x000b8040: 0x0b20 0x0b30 0x0b35 0x0b20 0x0b4e 0x0b6f 0x0b76 0x0b20 -0x000b8050: 0x0b32 0x0b30 0x0b30 0x0b33 0x0720 0x0720 0x0720 0x0720 -0x000b8060: 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 -0x000b8070: 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 -0x000b8080: 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 -0x000b8090: 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 0x0720 -@end smallexample -@end itemize -ETEXI - - { - .name = "p|print", - .args_type = "fmt:/,val:l", - .params = "/fmt expr", - .help = "print expression value (use $reg for CPU register access)", - .mhandler.cmd = do_print, - }, - -STEXI -@item p or print/@var{fmt} @var{expr} - -Print expression value. Only the @var{format} part of @var{fmt} is -used. -ETEXI - - { - .name = "i", - .args_type = "fmt:/,addr:i,index:i.", - .params = "/fmt addr", - .help = "I/O port read", - .mhandler.cmd = do_ioport_read, - }, - -STEXI -Read I/O port. -ETEXI - - { - .name = "o", - .args_type = "fmt:/,addr:i,val:i", - .params = "/fmt addr value", - .help = "I/O port write", - .mhandler.cmd = do_ioport_write, - }, - -STEXI -Write to I/O port. -ETEXI - - { - .name = "sendkey", - .args_type = "string:s,hold_time:i?", - .params = "keys [hold_ms]", - .help = "send keys to the VM (e.g. 'sendkey ctrl-alt-f1', default hold time=100 ms)", - .mhandler.cmd = do_sendkey, - }, - -STEXI -@item sendkey @var{keys} - -Send @var{keys} to the emulator. @var{keys} could be the name of the -key or @code{#} followed by the raw value in either decimal or hexadecimal -format. Use @code{-} to press several keys simultaneously. Example: -@example -sendkey ctrl-alt-f1 -@end example - -This command is useful to send keys that your graphical user interface -intercepts at low level, such as @code{ctrl-alt-f1} in X Window. -ETEXI - - { - .name = "system_reset", - .args_type = "", - .params = "", - .help = "reset the system", - .user_print = monitor_user_noop, - .mhandler.cmd_new = do_system_reset, - }, - -STEXI -@item system_reset - -Reset the system. -ETEXI - - { - .name = "system_powerdown", - .args_type = "", - .params = "", - .help = "send system power down event", - .user_print = monitor_user_noop, - .mhandler.cmd_new = do_system_powerdown, - }, - -STEXI -@item system_powerdown - -Power down the system (if supported). -ETEXI - - { - .name = "sum", - .args_type = "start:i,size:i", - .params = "addr size", - .help = "compute the checksum of a memory region", - .mhandler.cmd = do_sum, - }, - -STEXI -@item sum @var{addr} @var{size} - -Compute the checksum of a memory region. -ETEXI - - { - .name = "usb_add", - .args_type = "devname:s", - .params = "device", - .help = "add USB device (e.g. 'host:bus.addr' or 'host:vendor_id:product_id')", - .mhandler.cmd = do_usb_add, - }, - -STEXI -@item usb_add @var{devname} - -Add the USB device @var{devname}. For details of available devices see -@ref{usb_devices} -ETEXI - - { - .name = "usb_del", - .args_type = "devname:s", - .params = "device", - .help = "remove USB device 'bus.addr'", - .mhandler.cmd = do_usb_del, - }, - -STEXI -@item usb_del @var{devname} - -Remove the USB device @var{devname} from the QEMU virtual USB -hub. @var{devname} has the syntax @code{bus.addr}. Use the monitor -command @code{info usb} to see the devices you can remove. -ETEXI - - { - .name = "device_add", - .args_type = "config:s", - .params = "device", - .help = "add device, like -device on the command line", - .mhandler.cmd = do_device_add, - }, - -STEXI -@item device_add @var{config} - -Add device. -ETEXI - - { - .name = "device_del", - .args_type = "id:s", - .params = "device", - .help = "remove device", - .mhandler.cmd = do_device_del, - }, - -STEXI -@item device_del @var{id} - -Remove device @var{id}. -ETEXI - - { - .name = "cpu", - .args_type = "index:i", - .params = "index", - .help = "set the default CPU", - .mhandler.cmd = do_cpu_set, - }, - -STEXI -Set the default CPU. -ETEXI - - { - .name = "mouse_move", - .args_type = "dx_str:s,dy_str:s,dz_str:s?", - .params = "dx dy [dz]", - .help = "send mouse move events", - .mhandler.cmd = do_mouse_move, - }, - -STEXI -@item mouse_move @var{dx} @var{dy} [@var{dz}] -Move the active mouse to the specified coordinates @var{dx} @var{dy} -with optional scroll axis @var{dz}. -ETEXI - - { - .name = "mouse_button", - .args_type = "button_state:i", - .params = "state", - .help = "change mouse button state (1=L, 2=M, 4=R)", - .mhandler.cmd = do_mouse_button, - }, - -STEXI -@item mouse_button @var{val} -Change the active mouse button state @var{val} (1=L, 2=M, 4=R). -ETEXI - - { - .name = "mouse_set", - .args_type = "index:i", - .params = "index", - .help = "set which mouse device receives events", - .mhandler.cmd = do_mouse_set, - }, - -STEXI -@item mouse_set @var{index} -Set which mouse device receives events at given @var{index}, index -can be obtained with -@example -info mice -@end example -ETEXI - -#ifdef HAS_AUDIO - { - .name = "wavcapture", - .args_type = "path:F,freq:i?,bits:i?,nchannels:i?", - .params = "path [frequency [bits [channels]]]", - .help = "capture audio to a wave file (default frequency=44100 bits=16 channels=2)", - .mhandler.cmd = do_wav_capture, - }, -#endif -STEXI -@item wavcapture @var{filename} [@var{frequency} [@var{bits} [@var{channels}]]] -Capture audio into @var{filename}. Using sample rate @var{frequency} -bits per sample @var{bits} and number of channels @var{channels}. - -Defaults: -@itemize @minus -@item Sample rate = 44100 Hz - CD quality -@item Bits = 16 -@item Number of channels = 2 - Stereo -@end itemize -ETEXI - -#ifdef HAS_AUDIO - { - .name = "stopcapture", - .args_type = "n:i", - .params = "capture index", - .help = "stop capture", - .mhandler.cmd = do_stop_capture, - }, -#endif -STEXI -@item stopcapture @var{index} -Stop capture with a given @var{index}, index can be obtained with -@example -info capture -@end example -ETEXI - - { - .name = "memsave", - .args_type = "val:l,size:i,filename:s", - .params = "addr size file", - .help = "save to disk virtual memory dump starting at 'addr' of size 'size'", - .user_print = monitor_user_noop, - .mhandler.cmd_new = do_memory_save, - }, - -STEXI -@item memsave @var{addr} @var{size} @var{file} -save to disk virtual memory dump starting at @var{addr} of size @var{size}. -ETEXI - - { - .name = "pmemsave", - .args_type = "val:l,size:i,filename:s", - .params = "addr size file", - .help = "save to disk physical memory dump starting at 'addr' of size 'size'", - .user_print = monitor_user_noop, - .mhandler.cmd_new = do_physical_memory_save, - }, - -STEXI -@item pmemsave @var{addr} @var{size} @var{file} -save to disk physical memory dump starting at @var{addr} of size @var{size}. -ETEXI - - { - .name = "boot_set", - .args_type = "bootdevice:s", - .params = "bootdevice", - .help = "define new values for the boot device list", - .mhandler.cmd = do_boot_set, - }, - -STEXI -@item boot_set @var{bootdevicelist} - -Define new values for the boot device list. Those values will override -the values specified on the command line through the @code{-boot} option. - -The values that can be specified here depend on the machine type, but are -the same that can be specified in the @code{-boot} command line option. -ETEXI - -#if defined(TARGET_I386) - { - .name = "nmi", - .args_type = "cpu_index:i", - .params = "cpu", - .help = "inject an NMI on the given CPU", - .mhandler.cmd = do_inject_nmi, - }, -#endif -STEXI -@item nmi @var{cpu} -Inject an NMI on the given CPU (x86 only). -ETEXI - - { - .name = "migrate", - .args_type = "detach:-d,blk:-b,inc:-i,uri:s", - .params = "[-d] [-b] [-i] uri", - .help = "migrate to URI (using -d to not wait for completion)" - "\n\t\t\t -b for migration without shared storage with" - " full copy of disk\n\t\t\t -i for migration without " - "shared storage with incremental copy of disk " - "(base image shared between src and destination)", - .user_print = monitor_user_noop, - .mhandler.cmd_new = do_migrate, - }, - - -STEXI -@item migrate [-d] [-b] [-i] @var{uri} -Migrate to @var{uri} (using -d to not wait for completion). - -b for migration with full copy of disk - -i for migration with incremental copy of disk (base image is shared) -ETEXI - - { - .name = "migrate_cancel", - .args_type = "", - .params = "", - .help = "cancel the current VM migration", - .user_print = monitor_user_noop, - .mhandler.cmd_new = do_migrate_cancel, - }, - -STEXI -@item migrate_cancel -Cancel the current VM migration. -ETEXI - - { - .name = "migrate_set_speed", - .args_type = "value:s", - .params = "value", - .help = "set maximum speed (in bytes) for migrations", - .mhandler.cmd = do_migrate_set_speed, - }, - -STEXI -@item migrate_set_speed @var{value} -Set maximum speed to @var{value} (in bytes) for migrations. -ETEXI - - { - .name = "migrate_set_downtime", - .args_type = "value:s", - .params = "value", - .help = "set maximum tolerated downtime (in seconds) for migrations", - .mhandler.cmd = do_migrate_set_downtime, - }, - -STEXI -@item migrate_set_downtime @var{second} -Set maximum tolerated downtime (in seconds) for migration. -ETEXI - -#if defined(TARGET_I386) - { - .name = "drive_add", - .args_type = "pci_addr:s,opts:s", - .params = "[[:]:]\n" - "[file=file][,if=type][,bus=n]\n" - "[,unit=m][,media=d][index=i]\n" - "[,cyls=c,heads=h,secs=s[,trans=t]]\n" - "[snapshot=on|off][,cache=on|off]", - .help = "add drive to PCI storage controller", - .mhandler.cmd = drive_hot_add, - }, -#endif - -STEXI -@item drive_add -Add drive to PCI storage controller. -ETEXI - -#if defined(TARGET_I386) - { - .name = "pci_add", - .args_type = "pci_addr:s,type:s,opts:s?", - .params = "auto|[[:]:] nic|storage|host [[vlan=n][,macaddr=addr][,model=type]] [file=file][,if=type][,bus=nr]... [host=02:00.0[,name=string][,dma=none]", - .help = "hot-add PCI device", - .user_print = pci_device_hot_add_print, - .mhandler.cmd_new = pci_device_hot_add, - }, -#endif - -STEXI -@item pci_add -Hot-add PCI device. -ETEXI - -#if defined(TARGET_I386) - { - .name = "pci_del", - .args_type = "pci_addr:s", - .params = "[[:]:]", - .help = "hot remove PCI device", - .user_print = monitor_user_noop, - .mhandler.cmd_new = do_pci_device_hot_remove, - }, -#endif - -STEXI -@item pci_del -Hot remove PCI device. -ETEXI - - { - .name = "host_net_add", - .args_type = "device:s,opts:s?", - .params = "tap|user|socket|vde|dump [options]", - .help = "add host VLAN client", - .mhandler.cmd = net_host_device_add, - }, - -STEXI -@item host_net_add -Add host VLAN client. -ETEXI - - { - .name = "host_net_remove", - .args_type = "vlan_id:i,device:s", - .params = "vlan_id name", - .help = "remove host VLAN client", - .mhandler.cmd = net_host_device_remove, - }, - -STEXI -@item host_net_remove -Remove host VLAN client. -ETEXI - -#ifdef CONFIG_SLIRP - { - .name = "hostfwd_add", - .args_type = "arg1:s,arg2:s?,arg3:s?", - .params = "[vlan_id name] [tcp|udp]:[hostaddr]:hostport-[guestaddr]:guestport", - .help = "redirect TCP or UDP connections from host to guest (requires -net user)", - .mhandler.cmd = net_slirp_hostfwd_add, - }, - - { - .name = "hostfwd_remove", - .args_type = "arg1:s,arg2:s?,arg3:s?", - .params = "[vlan_id name] [tcp|udp]:[hostaddr]:hostport", - .help = "remove host-to-guest TCP or UDP redirection", - .mhandler.cmd = net_slirp_hostfwd_remove, - }, - -#endif -STEXI -@item host_net_redir -Redirect TCP or UDP connections from host to guest (requires -net user). -ETEXI - - { - .name = "balloon", - .args_type = "value:M", - .params = "target", - .help = "request VM to change its memory allocation (in MB)", - .user_print = monitor_user_noop, - .mhandler.cmd_new = do_balloon, - }, - -STEXI -@item balloon @var{value} -Request VM to change its memory allocation to @var{value} (in MB). -ETEXI - - { - .name = "set_link", - .args_type = "name:s,up_or_down:s", - .params = "name up|down", - .help = "change the link status of a network adapter", - .mhandler.cmd = do_set_link, - }, - -STEXI -@item set_link @var{name} [up|down] -Set link @var{name} up or down. -ETEXI - - { - .name = "watchdog_action", - .args_type = "action:s", - .params = "[reset|shutdown|poweroff|pause|debug|none]", - .help = "change watchdog action", - .mhandler.cmd = do_watchdog_action, - }, - -STEXI -@item watchdog_action -Change watchdog action. -ETEXI - - { - .name = "acl_show", - .args_type = "aclname:s", - .params = "aclname", - .help = "list rules in the access control list", - .mhandler.cmd = do_acl_show, - }, - -STEXI -@item acl_show @var{aclname} -List all the matching rules in the access control list, and the default -policy. There are currently two named access control lists, -@var{vnc.x509dname} and @var{vnc.username} matching on the x509 client -certificate distinguished name, and SASL username respectively. -ETEXI - - { - .name = "acl_policy", - .args_type = "aclname:s,policy:s", - .params = "aclname allow|deny", - .help = "set default access control list policy", - .mhandler.cmd = do_acl_policy, - }, - -STEXI -@item acl_policy @var{aclname} @code{allow|deny} -Set the default access control list policy, used in the event that -none of the explicit rules match. The default policy at startup is -always @code{deny}. -ETEXI - - { - .name = "acl_add", - .args_type = "aclname:s,match:s,policy:s,index:i?", - .params = "aclname match allow|deny [index]", - .help = "add a match rule to the access control list", - .mhandler.cmd = do_acl_add, - }, - -STEXI -@item acl_allow @var{aclname} @var{match} @code{allow|deny} [@var{index}] -Add a match rule to the access control list, allowing or denying access. -The match will normally be an exact username or x509 distinguished name, -but can optionally include wildcard globs. eg @code{*@@EXAMPLE.COM} to -allow all users in the @code{EXAMPLE.COM} kerberos realm. The match will -normally be appended to the end of the ACL, but can be inserted -earlier in the list if the optional @var{index} parameter is supplied. -ETEXI - - { - .name = "acl_remove", - .args_type = "aclname:s,match:s", - .params = "aclname match", - .help = "remove a match rule from the access control list", - .mhandler.cmd = do_acl_remove, - }, - -STEXI -@item acl_remove @var{aclname} @var{match} -Remove the specified match rule from the access control list. -ETEXI - - { - .name = "acl_reset", - .args_type = "aclname:s", - .params = "aclname", - .help = "reset the access control list", - .mhandler.cmd = do_acl_reset, - }, - -STEXI -@item acl_remove @var{aclname} @var{match} -Remove all matches from the access control list, and set the default -policy back to @code{deny}. -ETEXI - -#if defined(TARGET_I386) - - { - .name = "mce", - .args_type = "cpu_index:i,bank:i,status:l,mcg_status:l,addr:l,misc:l", - .params = "cpu bank status mcgstatus addr misc", - .help = "inject a MCE on the given CPU", - .mhandler.cmd = do_inject_mce, - }, - -#endif -STEXI -@item mce @var{cpu} @var{bank} @var{status} @var{mcgstatus} @var{addr} @var{misc} -Inject an MCE on the given CPU (x86 only). -ETEXI - - { - .name = "getfd", - .args_type = "fdname:s", - .params = "getfd name", - .help = "receive a file descriptor via SCM rights and assign it a name", - .user_print = monitor_user_noop, - .mhandler.cmd_new = do_getfd, - }, - -STEXI -@item getfd @var{fdname} -If a file descriptor is passed alongside this command using the SCM_RIGHTS -mechanism on unix sockets, it is stored using the name @var{fdname} for -later use by other monitor commands. -ETEXI - - { - .name = "closefd", - .args_type = "fdname:s", - .params = "closefd name", - .help = "close a file descriptor previously passed via SCM rights", - .user_print = monitor_user_noop, - .mhandler.cmd_new = do_closefd, - }, - -STEXI -@item closefd @var{fdname} -Close the file descriptor previously assigned to @var{fdname} using the -@code{getfd} command. This is only needed if the file descriptor was never -used by another monitor command. -ETEXI - - { - .name = "block_passwd", - .args_type = "device:B,password:s", - .params = "block_passwd device password", - .help = "set the password of encrypted block devices", - .user_print = monitor_user_noop, - .mhandler.cmd_new = do_block_set_passwd, - }, - -STEXI -@item block_passwd @var{device} @var{password} -Set the encrypted device @var{device} password to @var{password} -ETEXI - - { - .name = "cpu_set", - .args_type = "cpu:i,state:s", - .params = "cpu [online|offline]", - .help = "change cpu state", - .mhandler.cmd = do_cpu_set_nr, - }, - -STEXI -@item cpu_set @var{cpu} [online|offline] -Set CPU @var{cpu} online or offline. -ETEXI - -STEXI -@end table -ETEXI diff -Nru qemu-kvm-0.12.5+noroms/qemu-nbd.c qemu-kvm-0.14.1/qemu-nbd.c --- qemu-kvm-0.12.5+noroms/qemu-nbd.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/qemu-nbd.c 2011-05-11 13:29:46.000000000 +0000 @@ -30,6 +30,7 @@ #include #include #include +#include #define SOCKET_PATH "/var/lock/qemu-nbd-%s" @@ -43,7 +44,7 @@ "Usage: %s [OPTIONS] FILE\n" "QEMU Disk Network Block Device Server\n" "\n" -" -p, --port=PORT port to listen on (default `1024')\n" +" -p, --port=PORT port to listen on (default `%d')\n" " -o, --offset=OFFSET offset into the image\n" " -b, --bind=IFACE interface to bind to (default `0.0.0.0')\n" " -k, --socket=PATH path to the unix socket\n" @@ -61,7 +62,7 @@ " -V, --version output version information and exit\n" "\n" "Report bugs to \n" - , name, "DEVICE"); + , name, NBD_DEFAULT_PORT, "DEVICE"); } static void version(const char *name) @@ -111,9 +112,12 @@ uint8_t data[512]; int i; int ext_partnum = 4; + int ret; - if (bdrv_read(bs, 0, data, 1)) - errx(EINVAL, "error while reading"); + if ((ret = bdrv_read(bs, 0, data, 1)) < 0) { + errno = -ret; + err(EXIT_FAILURE, "error while reading"); + } if (data[510] != 0x55 || data[511] != 0xaa) { errno = -EINVAL; @@ -131,8 +135,10 @@ uint8_t data1[512]; int j; - if (bdrv_read(bs, mbr[i].start_sector_abs, data1, 1)) - errx(EINVAL, "error while reading"); + if ((ret = bdrv_read(bs, mbr[i].start_sector_abs, data1, 1)) < 0) { + errno = -ret; + err(EXIT_FAILURE, "error while reading"); + } for (j = 0; j < 4; j++) { read_partition(&data1[446 + 16 * j], &ext[j]); @@ -182,7 +188,7 @@ bool readonly = false; bool disconnect = false; const char *bindto = "0.0.0.0"; - int port = 1024; + int port = NBD_DEFAULT_PORT; struct sockaddr_in addr; socklen_t addr_len = sizeof(addr); off_t fd_size; @@ -212,7 +218,7 @@ int opt_ind = 0; int li; char *end; - int flags = 0; + int flags = BDRV_O_RDWR; int partition = -1; int ret; int shared = 1; @@ -224,6 +230,7 @@ int nb_fds = 0; int max_fd; int persistent = 0; + uint32_t nbdflags; while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) { switch (ch) { @@ -239,36 +246,37 @@ case 'p': li = strtol(optarg, &end, 0); if (*end) { - errx(EINVAL, "Invalid port `%s'", optarg); + errx(EXIT_FAILURE, "Invalid port `%s'", optarg); } if (li < 1 || li > 65535) { - errx(EINVAL, "Port out of range `%s'", optarg); + errx(EXIT_FAILURE, "Port out of range `%s'", optarg); } port = (uint16_t)li; break; case 'o': dev_offset = strtoll (optarg, &end, 0); if (*end) { - errx(EINVAL, "Invalid offset `%s'", optarg); + errx(EXIT_FAILURE, "Invalid offset `%s'", optarg); } if (dev_offset < 0) { - errx(EINVAL, "Offset must be positive `%s'", optarg); + errx(EXIT_FAILURE, "Offset must be positive `%s'", optarg); } break; case 'r': readonly = true; + flags &= ~BDRV_O_RDWR; break; case 'P': partition = strtol(optarg, &end, 0); if (*end) - errx(EINVAL, "Invalid partition `%s'", optarg); + errx(EXIT_FAILURE, "Invalid partition `%s'", optarg); if (partition < 1 || partition > 8) - errx(EINVAL, "Invalid partition %d", partition); + errx(EXIT_FAILURE, "Invalid partition %d", partition); break; case 'k': socket = optarg; if (socket[0] != '/') - errx(EINVAL, "socket path must be absolute\n"); + errx(EXIT_FAILURE, "socket path must be absolute\n"); break; case 'd': disconnect = true; @@ -279,10 +287,10 @@ case 'e': shared = strtol(optarg, &end, 0); if (*end) { - errx(EINVAL, "Invalid shared device number '%s'", optarg); + errx(EXIT_FAILURE, "Invalid shared device number '%s'", optarg); } if (shared < 1) { - errx(EINVAL, "Shared device number must be greater than 0\n"); + errx(EXIT_FAILURE, "Shared device number must be greater than 0\n"); } break; case 't': @@ -300,13 +308,13 @@ exit(0); break; case '?': - errx(EINVAL, "Try `%s --help' for more information.", + errx(EXIT_FAILURE, "Try `%s --help' for more information.", argv[0]); } } if ((argc - optind) != 1) { - errx(EINVAL, "Invalid number of argument.\n" + errx(EXIT_FAILURE, "Invalid number of argument.\n" "Try `%s --help' for more information.", argv[0]); } @@ -314,7 +322,7 @@ if (disconnect) { fd = open(argv[optind], O_RDWR); if (fd == -1) - errx(errno, "Cannot open %s", argv[optind]); + err(EXIT_FAILURE, "Cannot open %s", argv[optind]); nbd_disconnect(fd); @@ -328,31 +336,37 @@ bdrv_init(); bs = bdrv_new("hda"); - if (bs == NULL) - return 1; - if (bdrv_open(bs, argv[optind], flags) == -1) - return 1; + if ((ret = bdrv_open(bs, argv[optind], flags, NULL)) < 0) { + errno = -ret; + err(EXIT_FAILURE, "Failed to bdrv_open '%s'", argv[optind]); + } fd_size = bs->total_sectors * 512; if (partition != -1 && find_partition(bs, partition, &dev_offset, &fd_size)) - errx(errno, "Could not find partition %d", partition); + err(EXIT_FAILURE, "Could not find partition %d", partition); if (device) { pid_t pid; int sock; + /* want to fail before daemonizing */ + if (access(device, R_OK|W_OK) == -1) { + err(EXIT_FAILURE, "Could not access '%s'", device); + } + if (!verbose) { /* detach client and server */ if (daemon(0, 0) == -1) { - errx(errno, "Failed to daemonize"); + err(EXIT_FAILURE, "Failed to daemonize"); } } if (socket == NULL) { - sprintf(sockpath, SOCKET_PATH, basename(device)); + snprintf(sockpath, sizeof(sockpath), SOCKET_PATH, + basename(device)); socket = sockpath; } @@ -369,8 +383,10 @@ do { sock = unix_socket_outgoing(socket); if (sock == -1) { - if (errno != ENOENT && errno != ECONNREFUSED) + if (errno != ENOENT && errno != ECONNREFUSED) { + ret = 1; goto out; + } sleep(1); /* wait children */ } } while (sock == -1); @@ -381,7 +397,8 @@ goto out; } - ret = nbd_receive_negotiate(sock, &size, &blocksize); + ret = nbd_receive_negotiate(sock, NULL, &nbdflags, + &size, &blocksize); if (ret == -1) { ret = 1; goto out; @@ -400,7 +417,10 @@ show_parts(device); - nbd_client(fd, sock); + ret = nbd_client(fd); + if (ret) { + ret = 1; + } close(fd); out: kill(pid, SIGTERM); @@ -424,9 +444,9 @@ max_fd = sharing_fds[0]; nb_fds++; - data = qemu_memalign(512, NBD_BUFFER_SIZE); + data = qemu_blockalign(bs, NBD_BUFFER_SIZE); if (data == NULL) - errx(ENOMEM, "Cannot allocate data buffer"); + errx(EXIT_FAILURE, "Cannot allocate data buffer"); do { diff -Nru qemu-kvm-0.12.5+noroms/qemu-nbd.texi qemu-kvm-0.14.1/qemu-nbd.texi --- qemu-kvm-0.12.5+noroms/qemu-nbd.texi 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/qemu-nbd.texi 2011-05-11 13:29:46.000000000 +0000 @@ -30,8 +30,8 @@ use snapshot file @item -n, --nocache disable host cache -@item -c, --connect - connect FILE to NBD device DEV +@item -c, --connect=@var{dev} + connect @var{filename} to NBD device @var{dev} @item -d, --disconnect disconnect the specified device @item -e, --shared=@var{num} diff -Nru qemu-kvm-0.12.5+noroms/qemu-objects.h qemu-kvm-0.14.1/qemu-objects.h --- qemu-kvm-0.12.5+noroms/qemu-objects.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/qemu-objects.h 2011-05-11 13:29:46.000000000 +0000 @@ -6,9 +6,10 @@ * Authors: * Luiz Capitulino * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. */ + #ifndef QEMU_OBJECTS_H #define QEMU_OBJECTS_H diff -Nru qemu-kvm-0.12.5+noroms/qemu-option.c qemu-kvm-0.14.1/qemu-option.c --- qemu-kvm-0.12.5+noroms/qemu-option.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/qemu-option.c 2011-05-11 13:29:46.000000000 +0000 @@ -27,7 +27,10 @@ #include #include "qemu-common.h" +#include "qemu-error.h" +#include "qemu-objects.h" #include "qemu-option.h" +#include "qerror.h" /* * Extracts the name of an option from the parameter string (p points at the @@ -173,7 +176,7 @@ } else if (!strcmp(value, "off")) { *ret = 0; } else { - fprintf(stderr, "Option '%s': Use 'on' or 'off'\n", name); + qerror_report(QERR_INVALID_PARAMETER_VALUE, name, "'on' or 'off'"); return -1; } } else { @@ -190,12 +193,12 @@ if (value != NULL) { number = strtoull(value, &postfix, 0); if (*postfix != '\0') { - fprintf(stderr, "Option '%s' needs a number as parameter\n", name); + qerror_report(QERR_INVALID_PARAMETER_VALUE, name, "a number"); return -1; } *ret = number; } else { - fprintf(stderr, "Option '%s' needs a parameter\n", name); + qerror_report(QERR_INVALID_PARAMETER_VALUE, name, "a number"); return -1; } return 0; @@ -223,13 +226,13 @@ *ret = (uint64_t) sizef; break; default: - fprintf(stderr, "Option '%s' needs size as parameter\n", name); - fprintf(stderr, "You may use k, M, G or T suffixes for " + qerror_report(QERR_INVALID_PARAMETER_VALUE, name, "a size"); + error_printf_unless_qmp("You may use k, M, G or T suffixes for " "kilobytes, megabytes, gigabytes and terabytes.\n"); return -1; } } else { - fprintf(stderr, "Option '%s' needs a parameter\n", name); + qerror_report(QERR_INVALID_PARAMETER_VALUE, name, "a size"); return -1; } return 0; @@ -343,10 +346,56 @@ } /* + * Count valid options in list + */ +static size_t count_option_parameters(QEMUOptionParameter *list) +{ + size_t num_options = 0; + + while (list && list->name) { + num_options++; + list++; + } + + return num_options; +} + +/* + * Append an option list (list) to an option list (dest). + * + * If dest is NULL, a new copy of list is created. + * + * Returns a pointer to the first element of dest (or the newly allocated copy) + */ +QEMUOptionParameter *append_option_parameters(QEMUOptionParameter *dest, + QEMUOptionParameter *list) +{ + size_t num_options, num_dest_options; + + num_options = count_option_parameters(dest); + num_dest_options = num_options; + + num_options += count_option_parameters(list); + + dest = qemu_realloc(dest, (num_options + 1) * sizeof(QEMUOptionParameter)); + dest[num_dest_options].name = NULL; + + while (list && list->name) { + if (get_option_parameter(dest, list->name) == NULL) { + dest[num_dest_options++] = *list; + dest[num_dest_options].name = NULL; + } + list++; + } + + return dest; +} + +/* * Parses a parameter string (param) into an option list (dest). * - * list is the templace is. If dest is NULL, a new copy of list is created for - * it. If list is NULL, this function fails. + * list is the template option list. If dest is NULL, a new copy of list is + * created. If list is NULL, this function fails. * * A parameter string consists of one or more parameters, separated by commas. * Each parameter consists of its name and possibly of a value. In the latter @@ -362,31 +411,18 @@ QEMUOptionParameter *parse_option_parameters(const char *param, QEMUOptionParameter *list, QEMUOptionParameter *dest) { - QEMUOptionParameter *cur; QEMUOptionParameter *allocated = NULL; char name[256]; char value[256]; char *param_delim, *value_delim; char next_delim; - size_t num_options; if (list == NULL) { return NULL; } if (dest == NULL) { - // Count valid options - num_options = 0; - cur = list; - while (cur->name) { - num_options++; - cur++; - } - - // Create a copy of the option list to fill in values - dest = qemu_mallocz((num_options + 1) * sizeof(QEMUOptionParameter)); - allocated = dest; - memcpy(dest, list, (num_options + 1) * sizeof(QEMUOptionParameter)); + dest = allocated = append_option_parameters(NULL, list); } while (*param) { @@ -470,7 +506,7 @@ const char *name; const char *str; - QemuOptDesc *desc; + const QemuOptDesc *desc; union { int boolean; uint64_t uint; @@ -483,6 +519,7 @@ struct QemuOpts { char *id; QemuOptsList *list; + Location loc; QTAILQ_HEAD(QemuOptHead, QemuOpt) head; QTAILQ_ENTRY(QemuOpts) next; }; @@ -565,7 +602,7 @@ int qemu_opt_set(QemuOpts *opts, const char *name, const char *value) { QemuOpt *opt; - QemuOptDesc *desc = opts->list->desc; + const QemuOptDesc *desc = opts->list->desc; int i; for (i = 0; desc[i].name != NULL; i++) { @@ -577,8 +614,7 @@ if (i == 0) { /* empty list -> allow any */; } else { - fprintf(stderr, "option \"%s\" is not valid for %s\n", - name, opts->list->name); + qerror_report(QERR_INVALID_PARAMETER, name); return -1; } } @@ -594,8 +630,6 @@ opt->str = qemu_strdup(value); } if (qemu_opt_parse(opt) < 0) { - fprintf(stderr, "Failed to parse \"%s\" for \"%s.%s\"\n", opt->str, - opts->list->name, opt->name); qemu_opt_del(opt); return -1; } @@ -632,16 +666,35 @@ return NULL; } +static int id_wellformed(const char *id) +{ + int i; + + if (!qemu_isalpha(id[0])) { + return 0; + } + for (i = 1; id[i]; i++) { + if (!qemu_isalnum(id[i]) && !strchr("-._", id[i])) { + return 0; + } + } + return 1; +} + QemuOpts *qemu_opts_create(QemuOptsList *list, const char *id, int fail_if_exists) { QemuOpts *opts = NULL; if (id) { + if (!id_wellformed(id)) { + qerror_report(QERR_INVALID_PARAMETER_VALUE, "id", "an identifier"); + error_printf_unless_qmp("Identifiers consist of letters, digits, '-', '.', '_', starting with a letter.\n"); + return NULL; + } opts = qemu_opts_find(list, id); if (opts != NULL) { if (fail_if_exists) { - fprintf(stderr, "tried to create id \"%s\" twice for \"%s\"\n", - id, list->name); + qerror_report(QERR_DUPLICATE_ID, id, list->name); return NULL; } else { return opts; @@ -653,11 +706,26 @@ opts->id = qemu_strdup(id); } opts->list = list; + loc_save(&opts->loc); QTAILQ_INIT(&opts->head); QTAILQ_INSERT_TAIL(&list->head, opts, next); return opts; } +void qemu_opts_reset(QemuOptsList *list) +{ + QemuOpts *opts, *next_opts; + + QTAILQ_FOREACH_SAFE(opts, &list->head, next, next_opts) { + qemu_opts_del(opts); + } +} + +void qemu_opts_loc_restore(QemuOpts *opts) +{ + loc_restore(&opts->loc); +} + int qemu_opts_set(QemuOptsList *list, const char *id, const char *name, const char *value) { @@ -749,18 +817,23 @@ return 0; } -QemuOpts *qemu_opts_parse(QemuOptsList *list, const char *params, const char *firstname) +QemuOpts *qemu_opts_parse(QemuOptsList *list, const char *params, + int permit_abbrev) { + const char *firstname; char value[1024], *id = NULL; const char *p; QemuOpts *opts; + assert(!permit_abbrev || list->implied_opt_name); + firstname = permit_abbrev ? list->implied_opt_name : NULL; + if (strncmp(params, "id=", 3) == 0) { get_opt_value(value, sizeof(value), params+3); - id = qemu_strdup(value); + id = value; } else if ((p = strstr(params, ",id=")) != NULL) { get_opt_value(value, sizeof(value), p+4); - id = qemu_strdup(value); + id = value; } opts = qemu_opts_create(list, id, 1); if (opts == NULL) @@ -774,10 +847,89 @@ return opts; } +static void qemu_opts_from_qdict_1(const char *key, QObject *obj, void *opaque) +{ + char buf[32]; + const char *value; + int n; + + if (!strcmp(key, "id")) { + return; + } + + switch (qobject_type(obj)) { + case QTYPE_QSTRING: + value = qstring_get_str(qobject_to_qstring(obj)); + break; + case QTYPE_QINT: + n = snprintf(buf, sizeof(buf), "%" PRId64, + qint_get_int(qobject_to_qint(obj))); + assert(n < sizeof(buf)); + value = buf; + break; + case QTYPE_QFLOAT: + n = snprintf(buf, sizeof(buf), "%.17g", + qfloat_get_double(qobject_to_qfloat(obj))); + assert(n < sizeof(buf)); + value = buf; + break; + case QTYPE_QBOOL: + pstrcpy(buf, sizeof(buf), + qbool_get_int(qobject_to_qbool(obj)) ? "on" : "off"); + value = buf; + break; + default: + return; + } + qemu_opt_set(opaque, key, value); +} + +/* + * Create QemuOpts from a QDict. + * Use value of key "id" as ID if it exists and is a QString. + * Only QStrings, QInts, QFloats and QBools are copied. Entries with + * other types are silently ignored. + */ +QemuOpts *qemu_opts_from_qdict(QemuOptsList *list, const QDict *qdict) +{ + QemuOpts *opts; + + opts = qemu_opts_create(list, qdict_get_try_str(qdict, "id"), 1); + if (opts == NULL) + return NULL; + + qdict_iter(qdict, qemu_opts_from_qdict_1, opts); + return opts; +} + +/* + * Convert from QemuOpts to QDict. + * The QDict values are of type QString. + * TODO We'll want to use types appropriate for opt->desc->type, but + * this is enough for now. + */ +QDict *qemu_opts_to_qdict(QemuOpts *opts, QDict *qdict) +{ + QemuOpt *opt; + QObject *val; + + if (!qdict) { + qdict = qdict_new(); + } + if (opts->id) { + qdict_put(qdict, "id", qstring_from_str(opts->id)); + } + QTAILQ_FOREACH(opt, &opts->head, next) { + val = QOBJECT(qstring_from_str(opt->str)); + qdict_put_obj(qdict, opt->name, val); + } + return qdict; +} + /* Validate parsed opts against descriptions where no * descriptions were provided in the QemuOptsList. */ -int qemu_opts_validate(QemuOpts *opts, QemuOptDesc *desc) +int qemu_opts_validate(QemuOpts *opts, const QemuOptDesc *desc) { QemuOpt *opt; @@ -792,8 +944,7 @@ } } if (desc[i].name == NULL) { - fprintf(stderr, "option \"%s\" is not valid for %s\n", - opt->name, opts->list->name); + qerror_report(QERR_INVALID_PARAMETER, opt->name); return -1; } @@ -810,13 +961,17 @@ int qemu_opts_foreach(QemuOptsList *list, qemu_opts_loopfunc func, void *opaque, int abort_on_failure) { + Location loc; QemuOpts *opts; int rc = 0; + loc_push_none(&loc); QTAILQ_FOREACH(opts, &list->head, next) { - rc = func(opts, opaque); + loc_restore(&opts->loc); + rc |= func(opts, opaque); if (abort_on_failure && rc != 0) break; } + loc_pop(&loc); return rc; } diff -Nru qemu-kvm-0.12.5+noroms/qemu-option.h qemu-kvm-0.14.1/qemu-option.h --- qemu-kvm-0.12.5+noroms/qemu-option.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/qemu-option.h 2011-05-11 13:29:46.000000000 +0000 @@ -28,6 +28,7 @@ #include #include "qemu-queue.h" +#include "qdict.h" enum QEMUOptionParType { OPT_FLAG, @@ -69,6 +70,8 @@ const char *value); int set_option_parameter_int(QEMUOptionParameter *list, const char *name, uint64_t value); +QEMUOptionParameter *append_option_parameters(QEMUOptionParameter *dest, + QEMUOptionParameter *list); QEMUOptionParameter *parse_option_parameters(const char *param, QEMUOptionParameter *list, QEMUOptionParameter *dest); void free_option_parameters(QEMUOptionParameter *list); @@ -96,6 +99,7 @@ struct QemuOptsList { const char *name; + const char *implied_opt_name; QTAILQ_HEAD(, QemuOpts) head; QemuOptDesc desc[]; }; @@ -111,13 +115,17 @@ QemuOpts *qemu_opts_find(QemuOptsList *list, const char *id); QemuOpts *qemu_opts_create(QemuOptsList *list, const char *id, int fail_if_exists); +void qemu_opts_reset(QemuOptsList *list); +void qemu_opts_loc_restore(QemuOpts *opts); int qemu_opts_set(QemuOptsList *list, const char *id, const char *name, const char *value); const char *qemu_opts_id(QemuOpts *opts); void qemu_opts_del(QemuOpts *opts); -int qemu_opts_validate(QemuOpts *opts, QemuOptDesc *desc); +int qemu_opts_validate(QemuOpts *opts, const QemuOptDesc *desc); int qemu_opts_do_parse(QemuOpts *opts, const char *params, const char *firstname); -QemuOpts *qemu_opts_parse(QemuOptsList *list, const char *params, const char *firstname); +QemuOpts *qemu_opts_parse(QemuOptsList *list, const char *params, int permit_abbrev); +QemuOpts *qemu_opts_from_qdict(QemuOptsList *list, const QDict *qdict); +QDict *qemu_opts_to_qdict(QemuOpts *opts, QDict *qdict); typedef int (*qemu_opts_loopfunc)(QemuOpts *opts, void *opaque); int qemu_opts_print(QemuOpts *opts, void *dummy); diff -Nru qemu-kvm-0.12.5+noroms/qemu-options.h qemu-kvm-0.14.1/qemu-options.h --- qemu-kvm-0.12.5+noroms/qemu-options.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/qemu-options.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,41 @@ +/* + * qemu-options.h + * + * Defines needed for command line argument processing. + * + * Copyright (c) 2003-2008 Fabrice Bellard + * Copyright (c) 2010 Jes Sorensen + * + * 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. + */ + +#ifndef _QEMU_OPTIONS_H_ +#define _QEMU_OPTIONS_H_ + +enum { +#define DEF(option, opt_arg, opt_enum, opt_help, arch_mask) \ + opt_enum, +#define DEFHEADING(text) +#include "qemu-options.def" +#undef DEF +#undef DEFHEADING +#undef GEN_DOCS +}; + +#endif diff -Nru qemu-kvm-0.12.5+noroms/qemu-options.hx qemu-kvm-0.14.1/qemu-options.hx --- qemu-kvm-0.12.5+noroms/qemu-options.hx 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/qemu-options.hx 2011-05-11 13:29:46.000000000 +0000 @@ -1,8 +1,9 @@ HXCOMM Use DEFHEADING() to define headings in both help text and texi HXCOMM Text between STEXI and ETEXI are copied to texi version and HXCOMM discarded from C version -HXCOMM DEF(option, HAS_ARG/0, opt_enum, opt_help) is used to construct -HXCOMM option structures, enums and help message. +HXCOMM DEF(option, HAS_ARG/0, opt_enum, opt_help, arch_mask) is used to +HXCOMM construct option structures, enums and help message for specified +HXCOMM architectures. HXCOMM HXCOMM can be used for comments, discarded from both texi and C DEFHEADING(Standard options:) @@ -11,30 +12,34 @@ ETEXI DEF("help", 0, QEMU_OPTION_h, - "-h or -help display this help and exit\n") + "-h or -help display this help and exit\n", QEMU_ARCH_ALL) STEXI @item -h +@findex -h Display help and exit ETEXI DEF("version", 0, QEMU_OPTION_version, - "-version display version information and exit\n") + "-version display version information and exit\n", QEMU_ARCH_ALL) STEXI @item -version +@findex -version Display version information and exit ETEXI DEF("M", HAS_ARG, QEMU_OPTION_M, - "-M machine select emulated machine (-M ? for list)\n") + "-M machine select emulated machine (-M ? for list)\n", QEMU_ARCH_ALL) STEXI @item -M @var{machine} +@findex -M Select the emulated @var{machine} (@code{-M ?} for list) ETEXI DEF("cpu", HAS_ARG, QEMU_OPTION_cpu, - "-cpu cpu select CPU (-cpu ? for list)\n") + "-cpu cpu select CPU (-cpu ? for list)\n", QEMU_ARCH_ALL) STEXI @item -cpu @var{model} +@findex -cpu Select CPU model (-cpu ? for list and additional feature selection) ETEXI @@ -42,12 +47,14 @@ "-smp n[,maxcpus=cpus][,cores=cores][,threads=threads][,sockets=sockets]\n" " set the number of CPUs to 'n' [default=1]\n" " maxcpus= maximum number of total cpus, including\n" - " offline CPUs for hotplug etc.\n" + " offline CPUs for hotplug, etc\n" " cores= number of CPU cores on one socket\n" " threads= number of threads on one CPU core\n" - " sockets= number of discrete sockets in the system\n") + " sockets= number of discrete sockets in the system\n", + QEMU_ARCH_ALL) STEXI @item -smp @var{n}[,cores=@var{cores}][,threads=@var{threads}][,sockets=@var{sockets}][,maxcpus=@var{maxcpus}] +@findex -smp Simulate an SMP system with @var{n} CPUs. On the PC target, up to 255 CPUs are supported. On Sparc32 target, Linux limits the number of usable CPUs to 4. @@ -59,41 +66,50 @@ ETEXI DEF("numa", HAS_ARG, QEMU_OPTION_numa, - "-numa node[,mem=size][,cpus=cpu[-cpu]][,nodeid=node]\n") + "-numa node[,mem=size][,cpus=cpu[-cpu]][,nodeid=node]\n", QEMU_ARCH_ALL) STEXI @item -numa @var{opts} +@findex -numa Simulate a multi node NUMA system. If mem and cpus are omitted, resources are split equally. ETEXI DEF("fda", HAS_ARG, QEMU_OPTION_fda, - "-fda/-fdb file use 'file' as floppy disk 0/1 image\n") -DEF("fdb", HAS_ARG, QEMU_OPTION_fdb, "") + "-fda/-fdb file use 'file' as floppy disk 0/1 image\n", QEMU_ARCH_ALL) +DEF("fdb", HAS_ARG, QEMU_OPTION_fdb, "", QEMU_ARCH_ALL) STEXI @item -fda @var{file} @item -fdb @var{file} +@findex -fda +@findex -fdb Use @var{file} as floppy disk 0/1 image (@pxref{disk_images}). You can use the host floppy by using @file{/dev/fd0} as filename (@pxref{host_drives}). ETEXI DEF("hda", HAS_ARG, QEMU_OPTION_hda, - "-hda/-hdb file use 'file' as IDE hard disk 0/1 image\n") -DEF("hdb", HAS_ARG, QEMU_OPTION_hdb, "") + "-hda/-hdb file use 'file' as IDE hard disk 0/1 image\n", QEMU_ARCH_ALL) +DEF("hdb", HAS_ARG, QEMU_OPTION_hdb, "", QEMU_ARCH_ALL) DEF("hdc", HAS_ARG, QEMU_OPTION_hdc, - "-hdc/-hdd file use 'file' as IDE hard disk 2/3 image\n") -DEF("hdd", HAS_ARG, QEMU_OPTION_hdd, "") + "-hdc/-hdd file use 'file' as IDE hard disk 2/3 image\n", QEMU_ARCH_ALL) +DEF("hdd", HAS_ARG, QEMU_OPTION_hdd, "", QEMU_ARCH_ALL) STEXI @item -hda @var{file} @item -hdb @var{file} @item -hdc @var{file} @item -hdd @var{file} +@findex -hda +@findex -hdb +@findex -hdc +@findex -hdd Use @var{file} as hard disk 0, 1, 2 or 3 image (@pxref{disk_images}). ETEXI DEF("cdrom", HAS_ARG, QEMU_OPTION_cdrom, - "-cdrom file use 'file' as IDE cdrom image (cdrom is ide1 master)\n") + "-cdrom file use 'file' as IDE cdrom image (cdrom is ide1 master)\n", + QEMU_ARCH_ALL) STEXI @item -cdrom @var{file} +@findex -cdrom Use @var{file} as CD-ROM image (you cannot use @option{-hdc} and @option{-cdrom} at the same time). You can use the host CD-ROM by using @file{/dev/cdrom} as filename (@pxref{host_drives}). @@ -102,19 +118,13 @@ DEF("drive", HAS_ARG, QEMU_OPTION_drive, "-drive [file=file][,if=type][,bus=n][,unit=m][,media=d][,index=i]\n" " [,cyls=c,heads=h,secs=s[,trans=t]][,snapshot=on|off]\n" - " [,cache=writethrough|writeback|none][,format=f][,serial=s]\n" - " [,addr=A][,id=name][,aio=threads|native]\n" - " [,boot=on|off]\n" - " use 'file' as a drive image\n") -DEF("set", HAS_ARG, QEMU_OPTION_set, - "-set group.id.arg=value\n" - " set parameter for item of type \n" - " i.e. -set drive.$id.file=/path/to/image\n") -DEF("global", HAS_ARG, QEMU_OPTION_global, - "-global driver.property=value\n" - " set a global default for a driver property\n") + " [,cache=writethrough|writeback|none|unsafe][,format=f]\n" + " [,serial=s][,addr=A][,id=name][,aio=threads|native]\n" + " [,readonly=on|off][,boot=on|off]\n" + " use 'file' as a drive image\n", QEMU_ARCH_ALL) STEXI @item -drive @var{option}[,@var{option}[,@var{option}[,...]]] +@findex -drive Define a new drive. Valid options are: @@ -139,7 +149,7 @@ @item snapshot=@var{snapshot} @var{snapshot} is "on" or "off" and allows to enable snapshot for given drive (see @option{-snapshot}). @item cache=@var{cache} -@var{cache} is "none", "writeback", or "writethrough" and controls how the host cache is used to access block data. +@var{cache} is "none", "writeback", "unsafe", or "writethrough" and controls how the host cache is used to access block data. @item aio=@var{aio} @var{aio} is "threads", or "native" and selects between pthread based disk I/O and native Linux AIO. @item format=@var{format} @@ -150,6 +160,8 @@ This option specifies the serial number to assign to the device. @item addr=@var{addr} Specify the controller's PCI address (if=virtio only). +@item boot=@var{boot} +@var{boot} is "on" or "off" and allows for booting from non-traditional interfaces, such as virtio. @end table By default, writethrough caching is used for all block device. This means that @@ -160,8 +172,7 @@ Writeback caching will report data writes as completed as soon as the data is present in the host page cache. This is safe as long as you trust your host. If your host crashes or loses power, then the guest may experience data -corruption. When using the @option{-snapshot} option, writeback caching is -used by default. +corruption. The host page cache can be avoided entirely with @option{cache=none}. This will attempt to do disk IO directly to the guests memory. QEMU may still perform @@ -171,6 +182,13 @@ qcow2. If performance is more important than correctness, @option{cache=writeback} should be used with qcow2. +In case you don't care about data integrity over host failures, use +cache=unsafe. This option tells qemu that it never needs to write any data +to the disk but can instead keeps things in cache. If anything goes wrong, +like your host losing power, the disk storage getting disconnected accidently, +etc. you're image will most probably be rendered unusable. When using +the @option{-snapshot} option, unsafe caching is always used. + Instead of @option{-cdrom} you can use: @example qemu -drive file=file,index=2,media=cdrom @@ -217,34 +235,58 @@ @end example ETEXI -DEF("mtdblock", HAS_ARG, QEMU_OPTION_mtdblock, - "-mtdblock file use 'file' as on-board Flash memory image\n") +DEF("set", HAS_ARG, QEMU_OPTION_set, + "-set group.id.arg=value\n" + " set parameter for item of type \n" + " i.e. -set drive.$id.file=/path/to/image\n", QEMU_ARCH_ALL) STEXI +@item -set +@findex -set +TODO +ETEXI +DEF("global", HAS_ARG, QEMU_OPTION_global, + "-global driver.property=value\n" + " set a global default for a driver property\n", + QEMU_ARCH_ALL) +STEXI +@item -global +@findex -global +TODO +ETEXI + +DEF("mtdblock", HAS_ARG, QEMU_OPTION_mtdblock, + "-mtdblock file use 'file' as on-board Flash memory image\n", + QEMU_ARCH_ALL) +STEXI @item -mtdblock @var{file} +@findex -mtdblock Use @var{file} as on-board Flash memory image. ETEXI DEF("sd", HAS_ARG, QEMU_OPTION_sd, - "-sd file use 'file' as SecureDigital card image\n") + "-sd file use 'file' as SecureDigital card image\n", QEMU_ARCH_ALL) STEXI @item -sd @var{file} +@findex -sd Use @var{file} as SecureDigital card image. ETEXI DEF("pflash", HAS_ARG, QEMU_OPTION_pflash, - "-pflash file use 'file' as a parallel flash image\n") + "-pflash file use 'file' as a parallel flash image\n", QEMU_ARCH_ALL) STEXI @item -pflash @var{file} +@findex -pflash Use @var{file} as a parallel flash image. ETEXI DEF("boot", HAS_ARG, QEMU_OPTION_boot, "-boot [order=drives][,once=drives][,menu=on|off]\n" - " 'drives': floppy (a), hard disk (c), CD-ROM (d), network (n)\n") + " 'drives': floppy (a), hard disk (c), CD-ROM (d), network (n)\n", + QEMU_ARCH_ALL) STEXI @item -boot [order=@var{drives}][,once=@var{drives}][,menu=on|off] - +@findex -boot Specify boot order @var{drives} as a string of drive letters. Valid drive letters depend on the target achitecture. The x86 PC uses: a, b (floppy 1 and 2), c (first hard disk), d (first CD-ROM), n-p (Etherboot @@ -267,28 +309,50 @@ ETEXI DEF("snapshot", 0, QEMU_OPTION_snapshot, - "-snapshot write to temporary files instead of disk image files\n") + "-snapshot write to temporary files instead of disk image files\n", + QEMU_ARCH_ALL) STEXI @item -snapshot +@findex -snapshot Write to temporary files instead of disk image files. In this case, the raw disk image you use is not written back. You can however force the write back by pressing @key{C-a s} (@pxref{disk_images}). ETEXI DEF("m", HAS_ARG, QEMU_OPTION_m, - "-m megs set virtual RAM size to megs MB [default=%d]\n") + "-m megs set virtual RAM size to megs MB [default=" + stringify(DEFAULT_RAM_SIZE) "]\n", QEMU_ARCH_ALL) STEXI @item -m @var{megs} +@findex -m Set virtual RAM size to @var{megs} megabytes. Default is 128 MiB. Optionally, a suffix of ``M'' or ``G'' can be used to signify a value in megabytes or gigabytes respectively. ETEXI +DEF("mem-path", HAS_ARG, QEMU_OPTION_mempath, + "-mem-path FILE provide backing storage for guest RAM\n", QEMU_ARCH_ALL) +STEXI +@item -mem-path @var{path} +Allocate guest RAM from a temporarily created file in @var{path}. +ETEXI + +#ifdef MAP_POPULATE +DEF("mem-prealloc", 0, QEMU_OPTION_mem_prealloc, + "-mem-prealloc preallocate guest memory (use with -mem-path)\n", + QEMU_ARCH_ALL) +STEXI +@item -mem-prealloc +Preallocate memory when using -mem-path. +ETEXI +#endif + DEF("k", HAS_ARG, QEMU_OPTION_k, - "-k language use keyboard layout (for example 'fr' for French)\n") + "-k language use keyboard layout (for example 'fr' for French)\n", + QEMU_ARCH_ALL) STEXI @item -k @var{language} - +@findex -k Use keyboard layout @var{language} (for example @code{fr} for French). This option is only needed where it is not easy to get raw PC keycodes (e.g. on Macs, with some X11 servers or with a VNC @@ -306,27 +370,24 @@ ETEXI -#ifdef HAS_AUDIO DEF("audio-help", 0, QEMU_OPTION_audio_help, - "-audio-help print list of audio drivers and their options\n") -#endif + "-audio-help print list of audio drivers and their options\n", + QEMU_ARCH_ALL) STEXI @item -audio-help - +@findex -audio-help Will show the audio subsystem help: list of drivers, tunable parameters. ETEXI -#ifdef HAS_AUDIO DEF("soundhw", HAS_ARG, QEMU_OPTION_soundhw, "-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 + " use -soundhw all to enable all of them\n", QEMU_ARCH_ALL) STEXI @item -soundhw @var{card1}[,@var{card2},...] or -soundhw all - +@findex -soundhw Enable audio and selected sound hardware. Use ? to print all available sound hardware. @@ -334,6 +395,7 @@ qemu -soundhw sb16,adlib disk.img qemu -soundhw es1370 disk.img qemu -soundhw ac97 disk.img +qemu -soundhw hda disk.img qemu -soundhw all disk.img qemu -soundhw ? @end example @@ -351,20 +413,24 @@ ETEXI DEF("usb", 0, QEMU_OPTION_usb, - "-usb enable the USB driver (will be the default soon)\n") + "-usb enable the USB driver (will be the default soon)\n", + QEMU_ARCH_ALL) STEXI USB options: @table @option @item -usb +@findex -usb Enable the USB driver (will be the default soon) ETEXI DEF("usbdevice", HAS_ARG, QEMU_OPTION_usbdevice, - "-usbdevice name add the host or guest USB device 'name'\n") + "-usbdevice name add the host or guest USB device 'name'\n", + QEMU_ARCH_ALL) STEXI @item -usbdevice @var{devname} +@findex -usbdevice Add the USB device @var{devname}. @xref{usb_devices}. @table @option @@ -404,18 +470,101 @@ ETEXI DEF("device", HAS_ARG, QEMU_OPTION_device, - "-device driver[,options] add device\n") + "-device driver[,prop[=value][,...]]\n" + " add device (based on driver)\n" + " prop=value,... sets driver properties\n" + " use -device ? to print all possible drivers\n" + " use -device driver,? to print all possible properties\n", + QEMU_ARCH_ALL) +STEXI +@item -device @var{driver}[,@var{prop}[=@var{value}][,...]] +@findex -device +Add device @var{driver}. @var{prop}=@var{value} sets driver +properties. Valid properties depend on the driver. To get help on +possible drivers and properties, use @code{-device ?} and +@code{-device @var{driver},?}. +ETEXI + +DEFHEADING(File system options:) + +DEF("fsdev", HAS_ARG, QEMU_OPTION_fsdev, + "-fsdev local,id=id,path=path,security_model=[mapped|passthrough|none]\n", + QEMU_ARCH_ALL) + STEXI -@item -device @var{driver}[,@var{option}[,...]] -Add device @var{driver}. Depending on the device type, -@var{option} (typically @var{key}=@var{value}) may be useful. + +The general form of a File system device option is: +@table @option + +@item -fsdev @var{fstype} ,id=@var{id} [,@var{options}] +@findex -fsdev +Fstype is one of: +@option{local}, +The specific Fstype will determine the applicable options. + +Options to each backend are described below. + +@item -fsdev local ,id=@var{id} ,path=@var{path} ,security_model=@var{security_model} + +Create a file-system-"device" for local-filesystem. + +@option{local} is only available on Linux. + +@option{path} specifies the path to be exported. @option{path} is required. + +@option{security_model} specifies the security model to be followed. +@option{security_model} is required. + +@end table +ETEXI + +DEFHEADING(Virtual File system pass-through options:) + +DEF("virtfs", HAS_ARG, QEMU_OPTION_virtfs, + "-virtfs local,path=path,mount_tag=tag,security_model=[mapped|passthrough|none]\n", + QEMU_ARCH_ALL) + +STEXI + +The general form of a Virtual File system pass-through option is: +@table @option + +@item -virtfs @var{fstype} [,@var{options}] +@findex -virtfs +Fstype is one of: +@option{local}, +The specific Fstype will determine the applicable options. + +Options to each backend are described below. + +@item -virtfs local ,path=@var{path} ,mount_tag=@var{mount_tag} ,security_model=@var{security_model} + +Create a Virtual file-system-pass through for local-filesystem. + +@option{local} is only available on Linux. + +@option{path} specifies the path to be exported. @option{path} is required. + +@option{security_model} specifies the security model to be followed. +@option{security_model} is required. + + +@option{mount_tag} specifies the tag with which the exported file is mounted. +@option{mount_tag} is required. + +@end table ETEXI +DEFHEADING() + DEF("name", HAS_ARG, QEMU_OPTION_name, - "-name string1[,process=string2] set the name of the guest\n" - " string1 sets the window title and string2 the process name (on Linux)\n") + "-name string1[,process=string2]\n" + " set the name of the guest\n" + " string1 sets the window title and string2 the process name (on Linux)\n", + QEMU_ARCH_ALL) STEXI @item -name @var{name} +@findex -name Sets the @var{name} of the guest. This name will be displayed in the SDL window caption. The @var{name} will also be used for the VNC server. @@ -423,10 +572,11 @@ ETEXI DEF("uuid", HAS_ARG, QEMU_OPTION_uuid, - "-uuid %%08x-%%04x-%%04x-%%04x-%%012x\n" - " specify machine UUID\n") + "-uuid %08x-%04x-%04x-%04x-%012x\n" + " specify machine UUID\n", QEMU_ARCH_ALL) STEXI @item -uuid @var{uuid} +@findex -uuid Set system UUID. ETEXI @@ -443,10 +593,11 @@ ETEXI DEF("nographic", 0, QEMU_OPTION_nographic, - "-nographic disable graphical output and redirect serial I/Os to console\n") + "-nographic disable graphical output and redirect serial I/Os to console\n", + QEMU_ARCH_ALL) STEXI @item -nographic - +@findex -nographic Normally, QEMU uses SDL to display the VGA output. With this option, you can totally disable graphical output so that QEMU is a simple command line application. The emulated serial port is redirected on @@ -456,11 +607,12 @@ #ifdef CONFIG_CURSES DEF("curses", 0, QEMU_OPTION_curses, - "-curses use a curses/ncurses interface instead of SDL\n") + "-curses use a curses/ncurses interface instead of SDL\n", + QEMU_ARCH_ALL) #endif STEXI @item -curses - +@findex curses Normally, QEMU uses SDL to display the VGA output. With this option, QEMU can display the VGA output when in text mode using a curses/ncurses interface. Nothing is displayed in graphical mode. @@ -468,11 +620,12 @@ #ifdef CONFIG_SDL DEF("no-frame", 0, QEMU_OPTION_no_frame, - "-no-frame open SDL window without a frame and window decorations\n") + "-no-frame open SDL window without a frame and window decorations\n", + QEMU_ARCH_ALL) #endif STEXI @item -no-frame - +@findex -no-frame Do not use decorations for SDL windows and start them using the whole available screen space. This makes the using QEMU in a dedicated desktop workspace more convenient. @@ -480,57 +633,131 @@ #ifdef CONFIG_SDL DEF("alt-grab", 0, QEMU_OPTION_alt_grab, - "-alt-grab use Ctrl-Alt-Shift to grab mouse (instead of Ctrl-Alt)\n") + "-alt-grab use Ctrl-Alt-Shift to grab mouse (instead of Ctrl-Alt)\n", + QEMU_ARCH_ALL) #endif STEXI @item -alt-grab - +@findex -alt-grab Use Ctrl-Alt-Shift to grab mouse (instead of Ctrl-Alt). ETEXI #ifdef CONFIG_SDL DEF("ctrl-grab", 0, QEMU_OPTION_ctrl_grab, - "-ctrl-grab use Right-Ctrl to grab mouse (instead of Ctrl-Alt)\n") + "-ctrl-grab use Right-Ctrl to grab mouse (instead of Ctrl-Alt)\n", + QEMU_ARCH_ALL) #endif STEXI @item -ctrl-grab - +@findex -ctrl-grab Use Right-Ctrl to grab mouse (instead of Ctrl-Alt). ETEXI #ifdef CONFIG_SDL DEF("no-quit", 0, QEMU_OPTION_no_quit, - "-no-quit disable SDL window close capability\n") + "-no-quit disable SDL window close capability\n", QEMU_ARCH_ALL) #endif STEXI @item -no-quit - +@findex -no-quit Disable SDL window close capability. ETEXI #ifdef CONFIG_SDL DEF("sdl", 0, QEMU_OPTION_sdl, - "-sdl enable SDL\n") + "-sdl enable SDL\n", QEMU_ARCH_ALL) #endif STEXI @item -sdl - +@findex -sdl Enable SDL. ETEXI +DEF("spice", HAS_ARG, QEMU_OPTION_spice, + "-spice enable spice\n", QEMU_ARCH_ALL) +STEXI +@item -spice @var{option}[,@var{option}[,...]] +@findex -spice +Enable the spice remote desktop protocol. Valid options are + +@table @option + +@item port= +Set the TCP port spice is listening on for plaintext channels. + +@item addr= +Set the IP address spice is listening on. Default is any address. + +@item ipv4 +@item ipv6 +Force using the specified IP version. + +@item password= +Set the password you need to authenticate. + +@item disable-ticketing +Allow client connects without authentication. + +@item tls-port= +Set the TCP port spice is listening on for encrypted channels. + +@item x509-dir= +Set the x509 file directory. Expects same filenames as -vnc $display,x509=$dir + +@item x509-key-file= +@item x509-key-password= +@item x509-cert-file= +@item x509-cacert-file= +@item x509-dh-key-file= +The x509 file names can also be configured individually. + +@item tls-ciphers= +Specify which ciphers to use. + +@item tls-channel=[main|display|inputs|record|playback|tunnel] +@item plaintext-channel=[main|display|inputs|record|playback|tunnel] +Force specific channel to be used with or without TLS encryption. The +options can be specified multiple times to configure multiple +channels. The special name "default" can be used to set the default +mode. For channels which are not explicitly forced into one mode the +spice client is allowed to pick tls/plaintext as he pleases. + +@item image-compression=[auto_glz|auto_lz|quic|glz|lz|off] +Configure image compression (lossless). +Default is auto_glz. + +@item jpeg-wan-compression=[auto|never|always] +@item zlib-glz-wan-compression=[auto|never|always] +Configure wan image compression (lossy for slow links). +Default is auto. + +@item streaming-video=[off|all|filter] +Configure video stream detection. Default is filter. + +@item agent-mouse=[on|off] +Enable/disable passing mouse events via vdagent. Default is on. + +@item playback-compression=[on|off] +Enable/disable audio stream compression (using celt 0.5.1). Default is on. + +@end table +ETEXI + DEF("portrait", 0, QEMU_OPTION_portrait, - "-portrait rotate graphical output 90 deg left (only PXA LCD)\n") + "-portrait rotate graphical output 90 deg left (only PXA LCD)\n", + QEMU_ARCH_ALL) STEXI @item -portrait - +@findex -portrait Rotate graphical output 90 deg left (only PXA LCD). ETEXI DEF("vga", HAS_ARG, QEMU_OPTION_vga, - "-vga [std|cirrus|vmware|xenfb|none]\n" - " select video card type\n") + "-vga [std|cirrus|vmware|qxl|xenfb|none]\n" + " select video card type\n", QEMU_ARCH_ALL) STEXI @item -vga @var{type} +@findex -vga Select type of VGA card to emulate. Valid values for @var{type} are @table @option @item cirrus @@ -547,30 +774,37 @@ VMWare SVGA-II compatible adapter. Use it if you have sufficiently recent XFree86/XOrg server or Windows guest with a driver for this card. +@item qxl +QXL paravirtual graphic card. It is VGA compatible (including VESA +2.0 VBE support). Works best with qxl guest drivers installed though. +Recommended choice when using the spice protocol. @item none Disable VGA card. @end table ETEXI DEF("full-screen", 0, QEMU_OPTION_full_screen, - "-full-screen start in full screen\n") + "-full-screen start in full screen\n", QEMU_ARCH_ALL) STEXI @item -full-screen +@findex -full-screen Start in full screen. ETEXI -#if defined(TARGET_PPC) || defined(TARGET_SPARC) DEF("g", 1, QEMU_OPTION_g , - "-g WxH[xDEPTH] Set the initial graphical resolution and depth\n") -#endif + "-g WxH[xDEPTH] Set the initial graphical resolution and depth\n", + QEMU_ARCH_PPC | QEMU_ARCH_SPARC) STEXI +@item -g @var{width}x@var{height}[x@var{depth}] +@findex -g +Set the initial graphical resolution and depth (PPC, SPARC only). ETEXI DEF("vnc", HAS_ARG, QEMU_OPTION_vnc , - "-vnc display start a VNC server on display\n") + "-vnc display start a VNC server on display\n", QEMU_ARCH_ALL) STEXI @item -vnc @var{display}[,@var{option}[,@var{option}[,...]]] - +@findex -vnc Normally, QEMU uses SDL to display the VGA output. With this option, you can have QEMU listen on VNC display @var{display} and redirect the VGA display over the VNC session. It is very useful to enable the usb @@ -674,6 +908,13 @@ use the VNC server until the ACLs have been loaded. This can be achieved using the @code{acl} monitor command. +@item lossy + +Enable lossy compression methods (gradient, JPEG, ...). If this +option is set, VNC client may receive lossy framebuffer updates +depending on its encoding settings. Enabling this option can save +a lot of bandwidth at the expense of quality. + @end table ETEXI @@ -683,107 +924,98 @@ DEFHEADING() -#ifdef TARGET_I386 DEFHEADING(i386 target only:) -#endif STEXI @table @option ETEXI -#ifdef TARGET_I386 DEF("win2k-hack", 0, QEMU_OPTION_win2k_hack, - "-win2k-hack use it when installing Windows 2000 to avoid a disk full bug\n") -#endif + "-win2k-hack use it when installing Windows 2000 to avoid a disk full bug\n", + QEMU_ARCH_I386) STEXI @item -win2k-hack +@findex -win2k-hack Use it when installing Windows 2000 to avoid a disk full bug. After Windows 2000 is installed, you no longer need this option (this option slows down the IDE transfers). ETEXI -#ifdef TARGET_I386 HXCOMM Deprecated by -rtc -DEF("rtc-td-hack", 0, QEMU_OPTION_rtc_td_hack, "") -#endif +DEF("rtc-td-hack", 0, QEMU_OPTION_rtc_td_hack, "", QEMU_ARCH_I386) -#ifdef TARGET_I386 DEF("no-fd-bootchk", 0, QEMU_OPTION_no_fd_bootchk, - "-no-fd-bootchk disable boot signature checking for floppy disks\n") -#endif + "-no-fd-bootchk disable boot signature checking for floppy disks\n", + QEMU_ARCH_I386) STEXI @item -no-fd-bootchk +@findex -no-fd-bootchk Disable boot signature checking for floppy disks in Bochs BIOS. It may be needed to boot from old floppy disks. +TODO: check reference to Bochs BIOS. ETEXI -#ifdef TARGET_I386 DEF("no-acpi", 0, QEMU_OPTION_no_acpi, - "-no-acpi disable ACPI\n") -#endif + "-no-acpi disable ACPI\n", QEMU_ARCH_I386) STEXI @item -no-acpi +@findex -no-acpi Disable ACPI (Advanced Configuration and Power Interface) support. Use it if your guest OS complains about ACPI problems (PC target machine only). ETEXI -#ifdef TARGET_I386 DEF("no-hpet", 0, QEMU_OPTION_no_hpet, - "-no-hpet disable HPET\n") -#endif + "-no-hpet disable HPET\n", QEMU_ARCH_I386) STEXI @item -no-hpet +@findex -no-hpet Disable HPET support. ETEXI -#ifdef TARGET_I386 DEF("balloon", HAS_ARG, QEMU_OPTION_balloon, "-balloon none disable balloon device\n" "-balloon virtio[,addr=str]\n" - " enable virtio balloon device (default)\n") -#endif + " enable virtio balloon device (default)\n", QEMU_ARCH_ALL) STEXI @item -balloon none +@findex -balloon Disable balloon device. @item -balloon virtio[,addr=@var{addr}] Enable virtio balloon device (default), optionally with PCI address @var{addr}. ETEXI -#ifdef TARGET_I386 DEF("acpitable", HAS_ARG, QEMU_OPTION_acpitable, "-acpitable [sig=str][,rev=n][,oem_id=str][,oem_table_id=str][,oem_rev=n][,asl_compiler_id=str][,asl_compiler_rev=n][,data=file1[:file2]...]\n" - " ACPI table description\n") -#endif + " ACPI table description\n", QEMU_ARCH_I386) STEXI @item -acpitable [sig=@var{str}][,rev=@var{n}][,oem_id=@var{str}][,oem_table_id=@var{str}][,oem_rev=@var{n}] [,asl_compiler_id=@var{str}][,asl_compiler_rev=@var{n}][,data=@var{file1}[:@var{file2}]...] +@findex -acpitable Add ACPI table with specified header fields and context from specified files. ETEXI -#ifdef TARGET_I386 DEF("smbios", HAS_ARG, QEMU_OPTION_smbios, "-smbios file=binary\n" - " Load SMBIOS entry from binary file\n" - "-smbios type=0[,vendor=str][,version=str][,date=str][,release=%%d.%%d]\n" - " Specify SMBIOS type 0 fields\n" + " load SMBIOS entry from binary file\n" + "-smbios type=0[,vendor=str][,version=str][,date=str][,release=%d.%d]\n" + " specify SMBIOS type 0 fields\n" "-smbios type=1[,manufacturer=str][,product=str][,version=str][,serial=str]\n" " [,uuid=uuid][,sku=str][,family=str]\n" - " Specify SMBIOS type 1 fields\n") -#endif + " specify SMBIOS type 1 fields\n", QEMU_ARCH_I386) STEXI @item -smbios file=@var{binary} +@findex -smbios Load SMBIOS entry from binary file. @item -smbios type=0[,vendor=@var{str}][,version=@var{str}][,date=@var{str}][,release=@var{%d.%d}] +@findex -smbios Specify SMBIOS type 0 fields -@item -smbios type=1[,manufacturer=@var{str}][,product=@var{str}][,version=@var{str}][,serial=@var{str}][,uuid=@var{uuid}][,sku=@var{str}][,family=@var{str}] +@item -smbios type=1[,manufacturer=@var{str}][,product=@var{str}] [,version=@var{str}][,serial=@var{str}][,uuid=@var{uuid}][,sku=@var{str}] [,family=@var{str}] Specify SMBIOS type 1 fields ETEXI -#ifdef TARGET_I386 DEFHEADING() -#endif STEXI @end table ETEXI @@ -795,11 +1027,11 @@ HXCOMM Legacy slirp options (now moved to -net user): #ifdef CONFIG_SLIRP -DEF("tftp", HAS_ARG, QEMU_OPTION_tftp, "") -DEF("bootp", HAS_ARG, QEMU_OPTION_bootp, "") -DEF("redir", HAS_ARG, QEMU_OPTION_redir, "") +DEF("tftp", HAS_ARG, QEMU_OPTION_tftp, "", QEMU_ARCH_ALL) +DEF("bootp", HAS_ARG, QEMU_OPTION_bootp, "", QEMU_ARCH_ALL) +DEF("redir", HAS_ARG, QEMU_OPTION_redir, "", QEMU_ARCH_ALL) #ifndef _WIN32 -DEF("smb", HAS_ARG, QEMU_OPTION_smb, "") +DEF("smb", HAS_ARG, QEMU_OPTION_smb, "", QEMU_ARCH_ALL) #endif #endif @@ -820,21 +1052,26 @@ "-net tap[,vlan=n][,name=str],ifname=name\n" " connect the host TAP network interface to VLAN 'n'\n" #else - "-net tap[,vlan=n][,name=str][,fd=h][,ifname=name][,script=file][,downscript=dfile][,sndbuf=nbytes][,vnet_hdr=on|off]\n" + "-net tap[,vlan=n][,name=str][,fd=h][,ifname=name][,script=file][,downscript=dfile][,sndbuf=nbytes][,vnet_hdr=on|off][,vhost=on|off][,vhostfd=h][,vhostforce=on|off]\n" " connect the host TAP network interface to VLAN 'n' and use the\n" - " network scripts 'file' (default=%s)\n" - " and 'dfile' (default=%s);\n" - " use '[down]script=no' to disable script execution;\n" + " network scripts 'file' (default=" DEFAULT_NETWORK_SCRIPT ")\n" + " and 'dfile' (default=" DEFAULT_NETWORK_DOWN_SCRIPT ")\n" + " use '[down]script=no' to disable script execution\n" " use 'fd=h' to connect to an already opened TAP interface\n" - " use 'sndbuf=nbytes' to limit the size of the send buffer; the\n" - " default of 'sndbuf=1048576' can be disabled using 'sndbuf=0'\n" - " use vnet_hdr=off to avoid enabling the IFF_VNET_HDR tap flag; use\n" - " vnet_hdr=on to make the lack of IFF_VNET_HDR support an error condition\n" + " use 'sndbuf=nbytes' to limit the size of the send buffer (the\n" + " default is disabled 'sndbuf=0' to enable flow control set 'sndbuf=1048576')\n" + " use vnet_hdr=off to avoid enabling the IFF_VNET_HDR tap flag\n" + " use vnet_hdr=on to make the lack of IFF_VNET_HDR support an error condition\n" + " use vhost=on to enable experimental in kernel accelerator\n" + " (only has effect for virtio guests which use MSIX)\n" + " use vhostforce=on to force vhost on for non-MSIX virtio guests\n" + " use 'vhostfd=h' to connect to an already opened vhost net device\n" #endif "-net socket[,vlan=n][,name=str][,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][,name=str][,fd=h][,mcast=maddr:port]\n" + "-net socket[,vlan=n][,name=str][,fd=h][,mcast=maddr:port[,localaddr=addr]]\n" " connect the vlan 'n' to multicast maddr and port\n" + " use 'localaddr=addr' to specify the host address to send packets from\n" #ifdef CONFIG_VDE "-net vde[,vlan=n][,name=str][,sock=socketpath][,port=n][,group=groupname][,mode=octalmode]\n" " connect the vlan 'n' to port 'n' of a vde switch running\n" @@ -844,8 +1081,8 @@ #endif "-net dump[,vlan=n][,file=f][,len=n]\n" " dump traffic on vlan 'n' to file 'f' (max n bytes per packet)\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") + "-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", QEMU_ARCH_ALL) DEF("netdev", HAS_ARG, QEMU_OPTION_netdev, "-netdev [" #ifdef CONFIG_SLIRP @@ -855,9 +1092,10 @@ #ifdef CONFIG_VDE "vde|" #endif - "socket],id=str[,option][,option][,...]\n") + "socket],id=str[,option][,option][,...]\n", QEMU_ARCH_ALL) STEXI -@item -net nic[,vlan=@var{n}][,macaddr=@var{mac}][,model=@var{type}][,name=@var{name}][,addr=@var{addr}][,vectors=@var{v}] +@item -net nic[,vlan=@var{n}][,macaddr=@var{mac}][,model=@var{type}] [,name=@var{name}][,addr=@var{addr}][,vectors=@var{v}] +@findex -net Create a new Network Interface Card and connect it to VLAN @var{n} (@var{n} = 0 is the default). The NIC is an e1000 by default on the PC target. Optionally, the MAC address can be changed to @var{mac}, the @@ -987,7 +1225,7 @@ syntax gives undefined results. Their use for new applications is discouraged as they will be removed from future versions. -@item -net tap[,vlan=@var{n}][,name=@var{name}][,fd=@var{h}][,ifname=@var{name}][,script=@var{file}][,downscript=@var{dfile}] +@item -net tap[,vlan=@var{n}][,name=@var{name}][,fd=@var{h}][,ifname=@var{name}] [,script=@var{file}][,downscript=@var{dfile}] Connect the host TAP network interface @var{name} to VLAN @var{n}, use the network script @var{file} to configure it and the network script @var{dfile} to deconfigure it. If @var{name} is not provided, the OS @@ -1007,7 +1245,7 @@ -net nic,vlan=1 -net tap,vlan=1,ifname=tap1 @end example -@item -net socket[,vlan=@var{n}][,name=@var{name}][,fd=@var{h}][,listen=[@var{host}]:@var{port}][,connect=@var{host}:@var{port}] +@item -net socket[,vlan=@var{n}][,name=@var{name}][,fd=@var{h}] [,listen=[@var{host}]:@var{port}][,connect=@var{host}:@var{port}] Connect the VLAN @var{n} to a remote VLAN in another QEMU virtual machine using a TCP socket connection. If @option{listen} is @@ -1027,7 +1265,7 @@ -net socket,connect=127.0.0.1:1234 @end example -@item -net socket[,vlan=@var{n}][,name=@var{name}][,fd=@var{h}][,mcast=@var{maddr}:@var{port}] +@item -net socket[,vlan=@var{n}][,name=@var{name}][,fd=@var{h}][,mcast=@var{maddr}:@var{port}[,localaddr=@var{addr}]] Create a VLAN @var{n} shared with another QEMU virtual machines using a UDP multicast socket, effectively making a bus for @@ -1067,7 +1305,13 @@ /path/to/linux ubd0=/path/to/root_fs eth0=mcast @end example -@item -net vde[,vlan=@var{n}][,name=@var{name}][,sock=@var{socketpath}][,port=@var{n}][,group=@var{groupname}][,mode=@var{octalmode}] +Example (send packets from host's 1.2.3.4): +@example +qemu linux.img -net nic,macaddr=52:54:00:12:34:56 \ + -net socket,mcast=239.192.168.1:1102,localaddr=1.2.3.4 +@end example + +@item -net vde[,vlan=@var{n}][,name=@var{name}][,sock=@var{socketpath}] [,port=@var{n}][,group=@var{groupname}][,mode=@var{octalmode}] Connect VLAN @var{n} to PORT @var{n} of a vde switch running on host and listening for incoming connections on @var{socketpath}. Use GROUP @var{groupname} and MODE @var{octalmode} to change default ownership and permissions for @@ -1100,33 +1344,38 @@ DEFHEADING(Character device options:) DEF("chardev", HAS_ARG, QEMU_OPTION_chardev, - "-chardev null,id=id\n" + "-chardev null,id=id[,mux=on|off]\n" "-chardev socket,id=id[,host=host],port=host[,to=to][,ipv4][,ipv6][,nodelay]\n" - " [,server][,nowait][,telnet] (tcp)\n" - "-chardev socket,id=id,path=path[,server][,nowait][,telnet] (unix)\n" + " [,server][,nowait][,telnet][,mux=on|off] (tcp)\n" + "-chardev socket,id=id,path=path[,server][,nowait][,telnet],[mux=on|off] (unix)\n" "-chardev udp,id=id[,host=host],port=port[,localaddr=localaddr]\n" - " [,localport=localport][,ipv4][,ipv6]\n" - "-chardev msmouse,id=id\n" + " [,localport=localport][,ipv4][,ipv6][,mux=on|off]\n" + "-chardev msmouse,id=id[,mux=on|off]\n" "-chardev vc,id=id[[,width=width][,height=height]][[,cols=cols][,rows=rows]]\n" - "-chardev file,id=id,path=path\n" - "-chardev pipe,id=id,path=path\n" + " [,mux=on|off]\n" + "-chardev file,id=id,path=path[,mux=on|off]\n" + "-chardev pipe,id=id,path=path[,mux=on|off]\n" #ifdef _WIN32 - "-chardev console,id=id\n" - "-chardev serial,id=id,path=path\n" + "-chardev console,id=id[,mux=on|off]\n" + "-chardev serial,id=id,path=path[,mux=on|off]\n" #else - "-chardev pty,id=id\n" - "-chardev stdio,id=id,[,signal=on|off]\n" + "-chardev pty,id=id[,mux=on|off]\n" + "-chardev stdio,id=id[,mux=on|off][,signal=on|off]\n" #endif #ifdef CONFIG_BRLAPI - "-chardev braille,id=id\n" + "-chardev braille,id=id[,mux=on|off]\n" #endif #if defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \ || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) - "-chardev tty,id=id,path=path\n" + "-chardev tty,id=id,path=path[,mux=on|off]\n" #endif #if defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly__) - "-chardev parport,id=id,path=path\n" + "-chardev parport,id=id,path=path[,mux=on|off]\n" +#endif +#if defined(CONFIG_SPICE) + "-chardev spicevmc,id=id,name=name[,debug=debug]\n" #endif + , QEMU_ARCH_ALL ) STEXI @@ -1134,8 +1383,8 @@ The general form of a character device option is: @table @option -@item -chardev @var{backend} ,id=@var{id} [,@var{options}] - +@item -chardev @var{backend} ,id=@var{id} [,mux=on|off] [,@var{options}] +@findex -chardev Backend is one of: @option{null}, @option{socket}, @@ -1150,12 +1399,17 @@ @option{stdio}, @option{braille}, @option{tty}, -@option{parport}. +@option{parport}, +@option{spicevmc}. The specific backend will determine the applicable options. All devices must have an id, which can be any string up to 127 characters long. It is used to uniquely identify this device in other command line directives. +A character device may be used in multiplexing mode by multiple front-ends. +The key sequence of @key{Control-a} and @key{c} will rotate the input focus +between attached front-ends. Specify @option{mux=on} to enable this mode. + Options to each backend are described below. @item -chardev null ,id=@var{id} @@ -1180,7 +1434,7 @@ @table @option -@item TCP options: port=@var{host} [,host=@var{host}] [,to=@var{to}] [,ipv4] [,ipv6] [,nodelay] +@item TCP options: port=@var{port} [,host=@var{host}] [,to=@var{to}] [,ipv4] [,ipv6] [,nodelay] @option{host} for a listening socket specifies the local address to be bound. For a connecting socket species the remote host to connect to. @option{host} is @@ -1322,6 +1576,16 @@ @option{path} specifies the path to the parallel port device. @option{path} is required. +#if defined(CONFIG_SPICE) +@item -chardev spicevmc ,id=@var{id} ,debug=@var{debug}, name=@var{name} + +@option{debug} debug level for spicevmc + +@option{name} name of spice channel to connect to + +Connect to a spice virtual machine channel, such as vdiport. +#endif + @end table ETEXI @@ -1338,11 +1602,13 @@ "-bt vhci[,vlan=n]\n" \ " add host computer to virtual scatternet 'n' using VHCI\n" \ "-bt device:dev[,vlan=n]\n" \ - " emulate a bluetooth device 'dev' in scatternet 'n'\n") + " emulate a bluetooth device 'dev' in scatternet 'n'\n", + QEMU_ARCH_ALL) STEXI @table @option @item -bt hci[...] +@findex -bt Defines the function of the corresponding Bluetooth HCI. -bt options are matched with the HCIs present in the chosen machine type. For example when emulating a machine with only one HCI built into it, only @@ -1408,24 +1674,27 @@ ETEXI DEF("kernel", HAS_ARG, QEMU_OPTION_kernel, \ - "-kernel bzImage use 'bzImage' as kernel image\n") + "-kernel bzImage use 'bzImage' as kernel image\n", QEMU_ARCH_ALL) STEXI @item -kernel @var{bzImage} +@findex -kernel Use @var{bzImage} as kernel image. The kernel can be either a Linux kernel or in multiboot format. ETEXI DEF("append", HAS_ARG, QEMU_OPTION_append, \ - "-append cmdline use 'cmdline' as kernel command line\n") + "-append cmdline use 'cmdline' as kernel command line\n", QEMU_ARCH_ALL) STEXI @item -append @var{cmdline} +@findex -append Use @var{cmdline} as kernel command line ETEXI DEF("initrd", HAS_ARG, QEMU_OPTION_initrd, \ - "-initrd file use 'file' as initial ram disk\n") + "-initrd file use 'file' as initial ram disk\n", QEMU_ARCH_ALL) STEXI @item -initrd @var{file} +@findex -initrd Use @var{file} as initial ram disk. @item -initrd "@var{file1} arg=foo,@var{file2}" @@ -1449,9 +1718,11 @@ ETEXI DEF("serial", HAS_ARG, QEMU_OPTION_serial, \ - "-serial dev redirect the serial port to char device 'dev'\n") + "-serial dev redirect the serial port to char device 'dev'\n", + QEMU_ARCH_ALL) STEXI @item -serial @var{dev} +@findex -serial Redirect the virtual serial port to host character device @var{dev}. The default device is @code{vc} in graphical mode and @code{stdio} in non graphical mode. @@ -1576,9 +1847,11 @@ ETEXI DEF("parallel", HAS_ARG, QEMU_OPTION_parallel, \ - "-parallel dev redirect the parallel port to char device 'dev'\n") + "-parallel dev redirect the parallel port to char device 'dev'\n", + QEMU_ARCH_ALL) STEXI @item -parallel @var{dev} +@findex -parallel Redirect the virtual parallel port to host device @var{dev} (same devices as the serial port). On Linux hosts, @file{/dev/parportN} can be used to use hardware devices connected on the corresponding host @@ -1591,50 +1864,77 @@ ETEXI DEF("monitor", HAS_ARG, QEMU_OPTION_monitor, \ - "-monitor dev redirect the monitor to char device 'dev'\n") + "-monitor dev redirect the monitor to char device 'dev'\n", + QEMU_ARCH_ALL) STEXI @item -monitor @var{dev} +@findex -monitor Redirect the monitor to host device @var{dev} (same devices as the serial port). The default device is @code{vc} in graphical mode and @code{stdio} in non graphical mode. ETEXI DEF("qmp", HAS_ARG, QEMU_OPTION_qmp, \ - "-qmp dev like -monitor but opens in 'control' mode.\n") + "-qmp dev like -monitor but opens in 'control' mode\n", + QEMU_ARCH_ALL) +STEXI +@item -qmp @var{dev} +@findex -qmp +Like -monitor but opens in 'control' mode. +ETEXI DEF("mon", HAS_ARG, QEMU_OPTION_mon, \ - "-mon chardev=[name][,mode=readline|control][,default]\n") + "-mon chardev=[name][,mode=readline|control][,default]\n", QEMU_ARCH_ALL) STEXI @item -mon chardev=[name][,mode=readline|control][,default] +@findex -mon Setup monitor on chardev @var{name}. ETEXI +DEF("debugcon", HAS_ARG, QEMU_OPTION_debugcon, \ + "-debugcon dev redirect the debug console to char device 'dev'\n", + QEMU_ARCH_ALL) +STEXI +@item -debugcon @var{dev} +@findex -debugcon +Redirect the debug console to host device @var{dev} (same devices as the +serial port). The debug console is an I/O port which is typically port +0xe9; writing to that I/O port sends output to this device. +The default device is @code{vc} in graphical mode and @code{stdio} in +non graphical mode. +ETEXI + DEF("pidfile", HAS_ARG, QEMU_OPTION_pidfile, \ - "-pidfile file write PID to 'file'\n") + "-pidfile file write PID to 'file'\n", QEMU_ARCH_ALL) STEXI @item -pidfile @var{file} +@findex -pidfile Store the QEMU process PID in @var{file}. It is useful if you launch QEMU from a script. ETEXI DEF("singlestep", 0, QEMU_OPTION_singlestep, \ - "-singlestep always run in singlestep mode\n") + "-singlestep always run in singlestep mode\n", QEMU_ARCH_ALL) STEXI @item -singlestep +@findex -singlestep Run the emulation in single step mode. ETEXI DEF("S", 0, QEMU_OPTION_S, \ - "-S freeze CPU at startup (use 'c' to start execution)\n") + "-S freeze CPU at startup (use 'c' to start execution)\n", + QEMU_ARCH_ALL) STEXI @item -S +@findex -S Do not start CPU at startup (you must type 'c' in the monitor). ETEXI DEF("gdb", HAS_ARG, QEMU_OPTION_gdb, \ - "-gdb dev wait for gdb connection on 'dev'\n") + "-gdb dev wait for gdb connection on 'dev'\n", QEMU_ARCH_ALL) STEXI @item -gdb @var{dev} +@findex -gdb Wait for gdb connection on device @var{dev} (@pxref{gdb_usage}). Typical connections will likely be TCP-based, but also UDP, pseudo TTY, or even stdio are reasonable use case. The latter is allowing to start qemu from @@ -1645,26 +1945,32 @@ ETEXI DEF("s", 0, QEMU_OPTION_s, \ - "-s shorthand for -gdb tcp::%s\n") + "-s shorthand for -gdb tcp::" DEFAULT_GDBSTUB_PORT "\n", + QEMU_ARCH_ALL) STEXI @item -s +@findex -s Shorthand for -gdb tcp::1234, i.e. open a gdbserver on TCP port 1234 (@pxref{gdb_usage}). ETEXI DEF("d", HAS_ARG, QEMU_OPTION_d, \ - "-d item1,... output log to %s (use -d ? for a list of log items)\n") + "-d item1,... output log to /tmp/qemu.log (use -d ? for a list of log items)\n", + QEMU_ARCH_ALL) STEXI @item -d +@findex -d Output log in /tmp/qemu.log ETEXI DEF("hdachs", HAS_ARG, QEMU_OPTION_hdachs, \ "-hdachs c,h,s[,t]\n" \ " force hard disk 0 physical geometry and the optional BIOS\n" \ - " translation (t=none or lba) (usually qemu can guess them)\n") + " translation (t=none or lba) (usually qemu can guess them)\n", + QEMU_ARCH_ALL) STEXI @item -hdachs @var{c},@var{h},@var{s},[,@var{t}] +@findex -hdachs Force hard disk 0 physical geometry (1 <= @var{c} <= 16383, 1 <= @var{h} <= 16, 1 <= @var{s} <= 63) and optionally force the BIOS translation mode (@var{t}=none, lba or auto). Usually QEMU can guess @@ -1673,51 +1979,68 @@ ETEXI DEF("L", HAS_ARG, QEMU_OPTION_L, \ - "-L path set the directory for the BIOS, VGA BIOS and keymaps\n") + "-L path set the directory for the BIOS, VGA BIOS and keymaps\n", + QEMU_ARCH_ALL) STEXI @item -L @var{path} +@findex -L Set the directory for the BIOS, VGA BIOS and keymaps. ETEXI DEF("bios", HAS_ARG, QEMU_OPTION_bios, \ - "-bios file set the filename for the BIOS\n") + "-bios file set the filename for the BIOS\n", QEMU_ARCH_ALL) STEXI @item -bios @var{file} +@findex -bios Set the filename for the BIOS. ETEXI -#ifdef CONFIG_KVM DEF("enable-kvm", 0, QEMU_OPTION_enable_kvm, \ - "-enable-kvm enable KVM full virtualization support\n") -#endif + "-enable-kvm enable KVM full virtualization support\n", QEMU_ARCH_ALL) STEXI @item -enable-kvm +@findex -enable-kvm Enable KVM full virtualization support. This option is only available if KVM support is enabled when compiling. ETEXI -#ifdef CONFIG_XEN DEF("xen-domid", HAS_ARG, QEMU_OPTION_xen_domid, - "-xen-domid id specify xen guest domain id\n") + "-xen-domid id specify xen guest domain id\n", QEMU_ARCH_ALL) DEF("xen-create", 0, QEMU_OPTION_xen_create, "-xen-create create domain using xen hypercalls, bypassing xend\n" - " warning: should not be used when xend is in use\n") + " warning: should not be used when xend is in use\n", + QEMU_ARCH_ALL) DEF("xen-attach", 0, QEMU_OPTION_xen_attach, "-xen-attach attach to existing xen domain\n" - " xend will use this when starting qemu\n") -#endif + " xend will use this when starting qemu\n", + QEMU_ARCH_ALL) +STEXI +@item -xen-domid @var{id} +@findex -xen-domid +Specify xen guest domain @var{id} (XEN only). +@item -xen-create +@findex -xen-create +Create domain using xen hypercalls, bypassing xend. +Warning: should not be used when xend is in use (XEN only). +@item -xen-attach +@findex -xen-attach +Attach to existing xen domain. +xend will use this when starting qemu (XEN only). +ETEXI DEF("no-reboot", 0, QEMU_OPTION_no_reboot, \ - "-no-reboot exit instead of rebooting\n") + "-no-reboot exit instead of rebooting\n", QEMU_ARCH_ALL) STEXI @item -no-reboot +@findex -no-reboot Exit instead of rebooting. ETEXI DEF("no-shutdown", 0, QEMU_OPTION_no_shutdown, \ - "-no-shutdown stop before shutdown\n") + "-no-shutdown stop before shutdown\n", QEMU_ARCH_ALL) STEXI @item -no-shutdown +@findex -no-shutdown Don't exit QEMU on guest shutdown, but instead only stop the emulation. This allows for instance switching to monitor to commit changes to the disk image. @@ -1725,18 +2048,21 @@ DEF("loadvm", HAS_ARG, QEMU_OPTION_loadvm, \ "-loadvm [tag|id]\n" \ - " start right away with a saved state (loadvm in monitor)\n") + " start right away with a saved state (loadvm in monitor)\n", + QEMU_ARCH_ALL) STEXI @item -loadvm @var{file} +@findex -loadvm Start right away with a saved state (@code{loadvm} in monitor) ETEXI #ifndef _WIN32 DEF("daemonize", 0, QEMU_OPTION_daemonize, \ - "-daemonize daemonize QEMU after initializing\n") + "-daemonize daemonize QEMU after initializing\n", QEMU_ARCH_ALL) #endif STEXI @item -daemonize +@findex -daemonize Daemonize the QEMU process after initialization. QEMU will not detach from standard IO until it is ready to receive connections on any of its devices. This option is a useful way for external programs to launch QEMU without having @@ -1744,39 +2070,39 @@ ETEXI DEF("option-rom", HAS_ARG, QEMU_OPTION_option_rom, \ - "-option-rom rom load a file, rom, into the option ROM space\n") + "-option-rom rom load a file, rom, into the option ROM space\n", + QEMU_ARCH_ALL) STEXI @item -option-rom @var{file} +@findex -option-rom Load the contents of @var{file} as an option ROM. This option is useful to load things like EtherBoot. ETEXI DEF("clock", HAS_ARG, QEMU_OPTION_clock, \ "-clock force the use of the given methods for timer alarm.\n" \ - " To see what timers are available use -clock ?\n") + " To see what timers are available use -clock ?\n", + QEMU_ARCH_ALL) STEXI @item -clock @var{method} +@findex -clock Force the use of the given methods for timer alarm. To see what timers are available use -clock ?. ETEXI HXCOMM Options deprecated by -rtc -DEF("localtime", 0, QEMU_OPTION_localtime, "") -DEF("startdate", HAS_ARG, QEMU_OPTION_startdate, "") +DEF("localtime", 0, QEMU_OPTION_localtime, "", QEMU_ARCH_ALL) +DEF("startdate", HAS_ARG, QEMU_OPTION_startdate, "", QEMU_ARCH_ALL) -#ifdef TARGET_I386 DEF("rtc", HAS_ARG, QEMU_OPTION_rtc, \ "-rtc [base=utc|localtime|date][,clock=host|vm][,driftfix=none|slew]\n" \ - " set the RTC base and clock, enable drift fix for clock ticks\n") -#else -DEF("rtc", HAS_ARG, QEMU_OPTION_rtc, \ - "-rtc [base=utc|localtime|date][,clock=host|vm]\n" \ - " set the RTC base and clock\n") -#endif + " set the RTC base and clock, enable drift fix for clock ticks (x86 only)\n", + QEMU_ARCH_ALL) STEXI @item -rtc [base=utc|localtime|@var{date}][,clock=host|vm][,driftfix=none|slew] +@findex -rtc Specify @option{base} as @code{utc} or @code{localtime} to let the RTC start at the current UTC or local time, respectively. @code{localtime} is required for correct date in MS-DOS or Windows. To start at a specific point in time, provide @var{date} in the @@ -1797,9 +2123,10 @@ DEF("icount", HAS_ARG, QEMU_OPTION_icount, \ "-icount [N|auto]\n" \ " enable virtual instruction counter with 2^N clock ticks per\n" \ - " instruction\n") + " instruction\n", QEMU_ARCH_ALL) STEXI @item -icount [@var{N}|auto] +@findex -icount Enable virtual instruction counter. The virtual cpu will execute one instruction every 2^@var{N} ns of virtual time. If @code{auto} is specified then the virtual cpu speed will be automatically adjusted to keep virtual @@ -1813,9 +2140,11 @@ DEF("watchdog", HAS_ARG, QEMU_OPTION_watchdog, \ "-watchdog i6300esb|ib700\n" \ - " enable virtual hardware watchdog [default=none]\n") + " enable virtual hardware watchdog [default=none]\n", + QEMU_ARCH_ALL) STEXI @item -watchdog @var{model} +@findex -watchdog Create a virtual hardware watchdog device. Once enabled (by a guest action), the watchdog must be periodically polled by an agent inside the guest or else the guest will be restarted. @@ -1832,7 +2161,8 @@ DEF("watchdog-action", HAS_ARG, QEMU_OPTION_watchdog_action, \ "-watchdog-action reset|shutdown|poweroff|pause|debug|none\n" \ - " action when watchdog fires [default=reset]\n") + " action when watchdog fires [default=reset]\n", + QEMU_ARCH_ALL) STEXI @item -watchdog-action @var{action} @@ -1861,10 +2191,12 @@ ETEXI DEF("echr", HAS_ARG, QEMU_OPTION_echr, \ - "-echr chr set terminal escape character instead of ctrl-a\n") + "-echr chr set terminal escape character instead of ctrl-a\n", + QEMU_ARCH_ALL) STEXI @item -echr @var{numeric_ascii_value} +@findex -echr Change the escape character used for switching to the monitor when using monitor and serial sharing. The default is @code{0x01} when using the @code{-nographic} option. @code{0x01} is equal to pressing @@ -1880,111 +2212,159 @@ DEF("virtioconsole", HAS_ARG, QEMU_OPTION_virtiocon, \ "-virtioconsole c\n" \ - " set virtio console\n") + " set virtio console\n", QEMU_ARCH_ALL) STEXI @item -virtioconsole @var{c} +@findex -virtioconsole Set virtio console. + +This option is maintained for backward compatibility. + +Please use @code{-device virtconsole} for the new way of invocation. ETEXI DEF("show-cursor", 0, QEMU_OPTION_show_cursor, \ - "-show-cursor show cursor\n") + "-show-cursor show cursor\n", QEMU_ARCH_ALL) STEXI +@item -show-cursor +@findex -show-cursor +Show cursor. ETEXI DEF("tb-size", HAS_ARG, QEMU_OPTION_tb_size, \ - "-tb-size n set TB size\n") + "-tb-size n set TB size\n", QEMU_ARCH_ALL) STEXI +@item -tb-size @var{n} +@findex -tb-size +Set TB size. ETEXI DEF("incoming", HAS_ARG, QEMU_OPTION_incoming, \ - "-incoming p prepare for incoming migration, listen on port p\n") + "-incoming p prepare for incoming migration, listen on port p\n", + QEMU_ARCH_ALL) STEXI +@item -incoming @var{port} +@findex -incoming +Prepare for incoming migration, listen on @var{port}. ETEXI DEF("nodefaults", 0, QEMU_OPTION_nodefaults, \ - "-nodefaults don't create default devices.\n") + "-nodefaults don't create default devices\n", QEMU_ARCH_ALL) STEXI @item -nodefaults +@findex -nodefaults Don't create default devices. ETEXI #ifndef _WIN32 DEF("chroot", HAS_ARG, QEMU_OPTION_chroot, \ - "-chroot dir Chroot to dir just before starting the VM.\n") + "-chroot dir chroot to dir just before starting the VM\n", + QEMU_ARCH_ALL) #endif STEXI @item -chroot @var{dir} +@findex -chroot Immediately before starting guest execution, chroot to the specified directory. Especially useful in combination with -runas. ETEXI #ifndef _WIN32 DEF("runas", HAS_ARG, QEMU_OPTION_runas, \ - "-runas user Change to user id user just before starting the VM.\n") + "-runas user change to user id user just before starting the VM\n", + QEMU_ARCH_ALL) #endif STEXI @item -runas @var{user} +@findex -runas Immediately before starting guest execution, drop root privileges, switching to the specified user. ETEXI -#if defined(TARGET_SPARC) || defined(TARGET_PPC) DEF("prom-env", HAS_ARG, QEMU_OPTION_prom_env, "-prom-env variable=value\n" - " set OpenBIOS nvram variables\n") -#endif -#if defined(TARGET_ARM) || defined(TARGET_M68K) + " set OpenBIOS nvram variables\n", + QEMU_ARCH_PPC | QEMU_ARCH_SPARC) +STEXI +@item -prom-env @var{variable}=@var{value} +@findex -prom-env +Set OpenBIOS nvram @var{variable} to given @var{value} (PPC, SPARC only). +ETEXI DEF("semihosting", 0, QEMU_OPTION_semihosting, - "-semihosting semihosting mode\n") -#endif -#if defined(TARGET_ARM) + "-semihosting semihosting mode\n", QEMU_ARCH_ARM | QEMU_ARCH_M68K) +STEXI +@item -semihosting +@findex -semihosting +Semihosting mode (ARM, M68K only). +ETEXI DEF("old-param", 0, QEMU_OPTION_old_param, - "-old-param old param mode\n") -#endif + "-old-param old param mode\n", QEMU_ARCH_ARM) +STEXI +@item -old-param +@findex -old-param (ARM) +Old param mode (ARM only). +ETEXI + DEF("readconfig", HAS_ARG, QEMU_OPTION_readconfig, - "-readconfig \n") + "-readconfig \n", QEMU_ARCH_ALL) STEXI @item -readconfig @var{file} +@findex -readconfig Read device configuration from @var{file}. ETEXI DEF("writeconfig", HAS_ARG, QEMU_OPTION_writeconfig, "-writeconfig \n" - " read/write config file\n") + " read/write config file\n", QEMU_ARCH_ALL) STEXI @item -writeconfig @var{file} +@findex -writeconfig Write device configuration to @var{file}. ETEXI +DEF("nodefconfig", 0, QEMU_OPTION_nodefconfig, + "-nodefconfig\n" + " do not load default config files at startup\n", + QEMU_ARCH_ALL) +STEXI +@item -nodefconfig +@findex -nodefconfig +Normally QEMU loads a configuration file from @var{sysconfdir}/qemu.conf and +@var{sysconfdir}/target-@var{ARCH}.conf on startup. The @code{-nodefconfig} +option will prevent QEMU from loading these configuration files at startup. +ETEXI +#ifdef CONFIG_SIMPLE_TRACE +DEF("trace", HAS_ARG, QEMU_OPTION_trace, + "-trace\n" + " Specify a trace file to log traces to\n", + QEMU_ARCH_ALL) +STEXI +@item -trace +@findex -trace +Specify a trace file to log output traces to. +ETEXI +#endif DEF("no-kvm", 0, QEMU_OPTION_no_kvm, - "-no-kvm disable KVM hardware virtualization\n") + "-no-kvm disable KVM hardware virtualization\n", + QEMU_ARCH_ALL) DEF("no-kvm-irqchip", 0, QEMU_OPTION_no_kvm_irqchip, - "-no-kvm-irqchip disable KVM kernel mode PIC/IOAPIC/LAPIC\n") + "-no-kvm-irqchip disable KVM kernel mode PIC/IOAPIC/LAPIC\n", + QEMU_ARCH_I386) DEF("no-kvm-pit", 0, QEMU_OPTION_no_kvm_pit, - "-no-kvm-pit disable KVM kernel mode PIT\n") + "-no-kvm-pit disable KVM kernel mode PIT\n", + QEMU_ARCH_I386) DEF("no-kvm-pit-reinjection", 0, QEMU_OPTION_no_kvm_pit_reinjection, - "-no-kvm-pit-reinjection disable KVM kernel mode PIT interrupt reinjection\n") -#if defined(TARGET_I386) || defined(TARGET_X86_64) || defined(TARGET_IA64) || defined(__linux__) -DEF("pcidevice", HAS_ARG, QEMU_OPTION_pcidevice, - "-pcidevice host=bus:dev.func[,dma=none][,name=string]\n" - " expose a PCI device to the guest OS.\n" - " dma=none: don't perform any dma translations (default is to use an iommu)\n" - " 'string' is used in log output.\n") -#endif + "-no-kvm-pit-reinjection\n" + " disable KVM kernel mode PIT interrupt reinjection\n", + QEMU_ARCH_I386) DEF("enable-nesting", 0, QEMU_OPTION_enable_nesting, - "-enable-nesting enable support for running a VM inside the VM (AMD only)\n") + "-enable-nesting enable support for running a VM inside the VM (AMD only)\n", QEMU_ARCH_I386) DEF("nvram", HAS_ARG, QEMU_OPTION_nvram, - "-nvram FILE provide ia64 nvram contents\n") + "-nvram FILE provide ia64 nvram contents\n", QEMU_ARCH_ALL) DEF("tdf", 0, QEMU_OPTION_tdf, - "-tdf enable guest time drift compensation\n") + "-tdf enable guest time drift compensation\n", QEMU_ARCH_ALL) DEF("kvm-shadow-memory", HAS_ARG, QEMU_OPTION_kvm_shadow_memory, "-kvm-shadow-memory MEGABYTES\n" - " allocate MEGABYTES for kvm mmu shadowing\n") -DEF("mem-path", HAS_ARG, QEMU_OPTION_mempath, - "-mem-path FILE provide backing storage for guest RAM\n") -#ifdef MAP_POPULATE -DEF("mem-prealloc", 0, QEMU_OPTION_mem_prealloc, - "-mem-prealloc preallocate guest memory (use with -mempath)\n") -#endif + " allocate MEGABYTES for kvm mmu shadowing\n", + QEMU_ARCH_I386) HXCOMM This is the last statement. Insert new options before this line! STEXI diff -Nru qemu-kvm-0.12.5+noroms/qemu-os-posix.h qemu-kvm-0.14.1/qemu-os-posix.h --- qemu-kvm-0.12.5+noroms/qemu-os-posix.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/qemu-os-posix.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * posix specific declarations + * + * Copyright (c) 2003-2008 Fabrice Bellard + * Copyright (c) 2010 Jes Sorensen + * + * 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. + */ + +#ifndef QEMU_OS_POSIX_H +#define QEMU_OS_POSIX_H + +static inline void os_host_main_loop_wait(int *timeout) +{ +} + +void os_set_line_buffering(void); +void os_set_proc_name(const char *s); +void os_setup_signal_handling(void); +void os_daemonize(void); +void os_setup_post(void); + +typedef struct timeval qemu_timeval; +#define qemu_gettimeofday(tp) gettimeofday(tp, NULL) + +#ifndef CONFIG_UTIMENSAT +#ifndef UTIME_NOW +# define UTIME_NOW ((1l << 30) - 1l) +#endif +#ifndef UTIME_OMIT +# define UTIME_OMIT ((1l << 30) - 2l) +#endif +#endif +typedef struct timespec qemu_timespec; +int qemu_utimensat(int dirfd, const char *path, const qemu_timespec *times, + int flags); + +#endif diff -Nru qemu-kvm-0.12.5+noroms/qemu-os-win32.h qemu-kvm-0.14.1/qemu-os-win32.h --- qemu-kvm-0.12.5+noroms/qemu-os-win32.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/qemu-os-win32.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,63 @@ +/* + * win32 specific declarations + * + * Copyright (c) 2003-2008 Fabrice Bellard + * Copyright (c) 2010 Jes Sorensen + * + * 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. + */ + +#ifndef QEMU_OS_WIN32_H +#define QEMU_OS_WIN32_H + +/* Polling handling */ + +/* return TRUE if no sleep should be done afterwards */ +typedef int PollingFunc(void *opaque); + +int qemu_add_polling_cb(PollingFunc *func, void *opaque); +void qemu_del_polling_cb(PollingFunc *func, void *opaque); + +/* Wait objects handling */ +typedef void WaitObjectFunc(void *opaque); + +int qemu_add_wait_object(HANDLE handle, WaitObjectFunc *func, void *opaque); +void qemu_del_wait_object(HANDLE handle, WaitObjectFunc *func, void *opaque); + +void os_host_main_loop_wait(int *timeout); + +static inline void os_setup_signal_handling(void) {} +static inline void os_daemonize(void) {} +static inline void os_setup_post(void) {} +void os_set_line_buffering(void); +static inline void os_set_proc_name(const char *dummy) {} + +#if !defined(EPROTONOSUPPORT) +# define EPROTONOSUPPORT EINVAL +#endif + +int setenv(const char *name, const char *value, int overwrite); + +typedef struct { + long tv_sec; + long tv_usec; +} qemu_timeval; +int qemu_gettimeofday(qemu_timeval *tp); + +#endif diff -Nru qemu-kvm-0.12.5+noroms/qemu_socket.h qemu-kvm-0.14.1/qemu_socket.h --- qemu-kvm-0.12.5+noroms/qemu_socket.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/qemu_socket.h 2011-05-11 13:29:46.000000000 +0000 @@ -17,6 +17,7 @@ #else +#include #include #include #include @@ -44,6 +45,7 @@ int inet_connect_opts(QemuOpts *opts); int inet_connect(const char *str, int socktype); int inet_dgram_opts(QemuOpts *opts); +const char *inet_strfamily(int family); int unix_listen_opts(QemuOpts *opts); int unix_listen(const char *path, char *ostr, int olen); @@ -55,5 +57,6 @@ int parse_host_src_port(struct sockaddr_in *haddr, struct sockaddr_in *saddr, const char *str); +int socket_init(void); #endif /* QEMU_SOCKET_H */ diff -Nru qemu-kvm-0.12.5+noroms/qemu-sockets.c qemu-kvm-0.14.1/qemu-sockets.c --- qemu-kvm-0.12.5+noroms/qemu-sockets.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/qemu-sockets.c 2011-05-11 13:29:46.000000000 +0000 @@ -91,14 +91,14 @@ } } -static const char *inet_strfamily(int family) +const char *inet_strfamily(int family) { switch (family) { case PF_INET6: return "ipv6"; case PF_INET: return "ipv4"; case PF_UNIX: return "unix"; } - return "????"; + return "unknown"; } static void inet_print_addrinfo(const char *tag, struct addrinfo *res) @@ -649,3 +649,27 @@ } #endif + +#ifdef _WIN32 +static void socket_cleanup(void) +{ + WSACleanup(); +} +#endif + +int socket_init(void) +{ +#ifdef _WIN32 + WSADATA Data; + int ret, err; + + ret = WSAStartup(MAKEWORD(2,2), &Data); + if (ret != 0) { + err = WSAGetLastError(); + fprintf(stderr, "WSAStartup: %d\n", err); + return -1; + } + atexit(socket_cleanup); +#endif + return 0; +} diff -Nru qemu-kvm-0.12.5+noroms/qemu-tech.texi qemu-kvm-0.14.1/qemu-tech.texi --- qemu-kvm-0.12.5+noroms/qemu-tech.texi 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/qemu-tech.texi 2011-05-11 13:29:46.000000000 +0000 @@ -1,11 +1,21 @@ \input texinfo @c -*- texinfo -*- @c %**start of header @setfilename qemu-tech.info + +@documentlanguage en +@documentencoding UTF-8 + @settitle QEMU Internals @exampleindent 0 @paragraphindent 0 @c %**end of header +@ifinfo +@direntry +* QEMU Internals: (qemu-tech). The QEMU Emulator Internals. +@end direntry +@end ifinfo + @iftex @titlepage @sp 7 @@ -506,7 +516,7 @@ @section Hardware interrupts In order to be faster, QEMU does not check at every basic block if an -hardware interrupt is pending. Instead, the user must asynchrously +hardware interrupt is pending. Instead, the user must asynchronously call a specific function to tell that an interrupt is pending. This function resets the chaining of the currently executing basic block. It ensures that the execution will return soon in the main loop @@ -538,7 +548,7 @@ from the virtual signal handler. Some signals (such as SIGALRM) directly come from the host. Other -signals are synthetized from the virtual CPU exceptions such as SIGFPE +signals are synthesized from the virtual CPU exceptions such as SIGFPE when a division by zero is done (see @code{main.c:cpu_loop()}). The blocked signal mask is still handled by the host Linux kernel so diff -Nru qemu-kvm-0.12.5+noroms/qemu-thread.c qemu-kvm-0.14.1/qemu-thread.c --- qemu-kvm-0.12.5+noroms/qemu-thread.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/qemu-thread.c 2011-05-11 13:29:46.000000000 +0000 @@ -34,6 +34,15 @@ error_exit(err, __func__); } +void qemu_mutex_destroy(QemuMutex *mutex) +{ + int err; + + err = pthread_mutex_destroy(&mutex->lock); + if (err) + error_exit(err, __func__); +} + void qemu_mutex_lock(QemuMutex *mutex) { int err; @@ -90,6 +99,15 @@ error_exit(err, __func__); } +void qemu_cond_destroy(QemuCond *cond) +{ + int err; + + err = pthread_cond_destroy(&cond->cond); + if (err) + error_exit(err, __func__); +} + void qemu_cond_signal(QemuCond *cond) { int err; @@ -137,9 +155,16 @@ { int err; + /* Leave signal handling to the iothread. */ + sigset_t set, oldset; + + sigfillset(&set); + pthread_sigmask(SIG_SETMASK, &set, &oldset); err = pthread_create(&thread->thread, NULL, start_routine, arg); if (err) error_exit(err, __func__); + + pthread_sigmask(SIG_SETMASK, &oldset, NULL); } void qemu_thread_signal(QemuThread *thread, int sig) @@ -161,3 +186,7 @@ return pthread_equal(thread1->thread, thread2->thread); } +void qemu_thread_exit(void *retval) +{ + pthread_exit(retval); +} diff -Nru qemu-kvm-0.12.5+noroms/qemu-thread.h qemu-kvm-0.14.1/qemu-thread.h --- qemu-kvm-0.12.5+noroms/qemu-thread.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/qemu-thread.h 2011-05-11 13:29:46.000000000 +0000 @@ -20,12 +20,14 @@ typedef struct QemuThread QemuThread; void qemu_mutex_init(QemuMutex *mutex); +void qemu_mutex_destroy(QemuMutex *mutex); void qemu_mutex_lock(QemuMutex *mutex); int qemu_mutex_trylock(QemuMutex *mutex); int qemu_mutex_timedlock(QemuMutex *mutex, uint64_t msecs); void qemu_mutex_unlock(QemuMutex *mutex); void qemu_cond_init(QemuCond *cond); +void qemu_cond_destroy(QemuCond *cond); void qemu_cond_signal(QemuCond *cond); void qemu_cond_broadcast(QemuCond *cond); void qemu_cond_wait(QemuCond *cond, QemuMutex *mutex); @@ -37,4 +39,6 @@ void qemu_thread_signal(QemuThread *thread, int sig); void qemu_thread_self(QemuThread *thread); int qemu_thread_equal(QemuThread *thread1, QemuThread *thread2); +void qemu_thread_exit(void *retval); + #endif diff -Nru qemu-kvm-0.12.5+noroms/qemu-timer.c qemu-kvm-0.14.1/qemu-timer.c --- qemu-kvm-0.12.5+noroms/qemu-timer.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/qemu-timer.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,1122 @@ +/* + * 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 "sysemu.h" +#include "net.h" +#include "monitor.h" +#include "console.h" + +#include "hw/hw.h" + +#include +#include +#include +#include +#include +#include +#ifdef __FreeBSD__ +#include +#endif + +#ifdef __linux__ +#include +#include +/* For the benefit of older linux systems which don't supply it, + we use a local copy of hpet.h. */ +/* #include */ +#include "hpet.h" +#endif + +#ifdef _WIN32 +#include +#include +#endif + +#include "qemu-timer.h" + +/* Conversion factor from emulated instructions to virtual clock ticks. */ +int icount_time_shift; +/* Arbitrarily pick 1MIPS as the minimum allowable speed. */ +#define MAX_ICOUNT_SHIFT 10 +/* Compensate for varying guest execution speed. */ +int64_t qemu_icount_bias; +static QEMUTimer *icount_rt_timer; +static QEMUTimer *icount_vm_timer; + +/***********************************************************/ +/* guest cycle counter */ + +typedef struct TimersState { + int64_t cpu_ticks_prev; + int64_t cpu_ticks_offset; + int64_t cpu_clock_offset; + int32_t cpu_ticks_enabled; + int64_t dummy; +} TimersState; + +TimersState timers_state; + +/* return the host CPU cycle counter and handle stop/restart */ +int64_t cpu_get_ticks(void) +{ + if (use_icount) { + return cpu_get_icount(); + } + if (!timers_state.cpu_ticks_enabled) { + return timers_state.cpu_ticks_offset; + } else { + int64_t ticks; + ticks = cpu_get_real_ticks(); + if (timers_state.cpu_ticks_prev > ticks) { + /* Note: non increasing ticks may happen if the host uses + software suspend */ + timers_state.cpu_ticks_offset += timers_state.cpu_ticks_prev - ticks; + } + timers_state.cpu_ticks_prev = ticks; + return ticks + timers_state.cpu_ticks_offset; + } +} + +/* return the host CPU monotonic timer and handle stop/restart */ +static int64_t cpu_get_clock(void) +{ + int64_t ti; + if (!timers_state.cpu_ticks_enabled) { + return timers_state.cpu_clock_offset; + } else { + ti = get_clock(); + return ti + timers_state.cpu_clock_offset; + } +} + +static int64_t qemu_icount_delta(void) +{ + if (!use_icount) { + return 5000 * (int64_t) 1000000; + } else if (use_icount == 1) { + /* When not using an adaptive execution frequency + we tend to get badly out of sync with real time, + so just delay for a reasonable amount of time. */ + return 0; + } else { + return cpu_get_icount() - cpu_get_clock(); + } +} + +/* enable cpu_get_ticks() */ +void cpu_enable_ticks(void) +{ + if (!timers_state.cpu_ticks_enabled) { + timers_state.cpu_ticks_offset -= cpu_get_real_ticks(); + timers_state.cpu_clock_offset -= get_clock(); + timers_state.cpu_ticks_enabled = 1; + } +} + +/* disable cpu_get_ticks() : the clock is stopped. You must not call + cpu_get_ticks() after that. */ +void cpu_disable_ticks(void) +{ + if (timers_state.cpu_ticks_enabled) { + timers_state.cpu_ticks_offset = cpu_get_ticks(); + timers_state.cpu_clock_offset = cpu_get_clock(); + timers_state.cpu_ticks_enabled = 0; + } +} + +/***********************************************************/ +/* timers */ + +#define QEMU_CLOCK_REALTIME 0 +#define QEMU_CLOCK_VIRTUAL 1 +#define QEMU_CLOCK_HOST 2 + +struct QEMUClock { + int type; + int enabled; + /* XXX: add frequency */ +}; + +struct QEMUTimer { + QEMUClock *clock; + int64_t expire_time; + QEMUTimerCB *cb; + void *opaque; + struct QEMUTimer *next; +}; + +struct qemu_alarm_timer { + char const *name; + int (*start)(struct qemu_alarm_timer *t); + void (*stop)(struct qemu_alarm_timer *t); + void (*rearm)(struct qemu_alarm_timer *t); + void *priv; + + char expired; + char pending; +}; + +static struct qemu_alarm_timer *alarm_timer; + +int qemu_alarm_pending(void) +{ + return alarm_timer->pending; +} + +static inline int alarm_has_dynticks(struct qemu_alarm_timer *t) +{ + return !!t->rearm; +} + +static void qemu_rearm_alarm_timer(struct qemu_alarm_timer *t) +{ + if (!alarm_has_dynticks(t)) + return; + + t->rearm(t); +} + +/* TODO: MIN_TIMER_REARM_NS should be optimized */ +#define MIN_TIMER_REARM_NS 250000 + +#ifdef _WIN32 + +struct qemu_alarm_win32 { + MMRESULT timerId; + unsigned int period; +} alarm_win32_data = {0, 0}; + +static int win32_start_timer(struct qemu_alarm_timer *t); +static void win32_stop_timer(struct qemu_alarm_timer *t); +static void win32_rearm_timer(struct qemu_alarm_timer *t); + +#else + +static int unix_start_timer(struct qemu_alarm_timer *t); +static void unix_stop_timer(struct qemu_alarm_timer *t); + +#ifdef __linux__ + +static int dynticks_start_timer(struct qemu_alarm_timer *t); +static void dynticks_stop_timer(struct qemu_alarm_timer *t); +static void dynticks_rearm_timer(struct qemu_alarm_timer *t); + +static int hpet_start_timer(struct qemu_alarm_timer *t); +static void hpet_stop_timer(struct qemu_alarm_timer *t); + +static int rtc_start_timer(struct qemu_alarm_timer *t); +static void rtc_stop_timer(struct qemu_alarm_timer *t); + +#endif /* __linux__ */ + +#endif /* _WIN32 */ + +/* Correlation between real and virtual time is always going to be + fairly approximate, so ignore small variation. + When the guest is idle real and virtual time will be aligned in + the IO wait loop. */ +#define ICOUNT_WOBBLE (get_ticks_per_sec() / 10) + +static void icount_adjust(void) +{ + int64_t cur_time; + int64_t cur_icount; + int64_t delta; + static int64_t last_delta; + /* If the VM is not running, then do nothing. */ + if (!vm_running) + return; + + cur_time = cpu_get_clock(); + cur_icount = qemu_get_clock(vm_clock); + delta = cur_icount - cur_time; + /* FIXME: This is a very crude algorithm, somewhat prone to oscillation. */ + if (delta > 0 + && last_delta + ICOUNT_WOBBLE < delta * 2 + && icount_time_shift > 0) { + /* The guest is getting too far ahead. Slow time down. */ + icount_time_shift--; + } + if (delta < 0 + && last_delta - ICOUNT_WOBBLE > delta * 2 + && icount_time_shift < MAX_ICOUNT_SHIFT) { + /* The guest is getting too far behind. Speed time up. */ + icount_time_shift++; + } + last_delta = delta; + qemu_icount_bias = cur_icount - (qemu_icount << icount_time_shift); +} + +static void icount_adjust_rt(void * opaque) +{ + qemu_mod_timer(icount_rt_timer, + qemu_get_clock(rt_clock) + 1000); + icount_adjust(); +} + +static void icount_adjust_vm(void * opaque) +{ + qemu_mod_timer(icount_vm_timer, + qemu_get_clock(vm_clock) + get_ticks_per_sec() / 10); + icount_adjust(); +} + +int64_t qemu_icount_round(int64_t count) +{ + return (count + (1 << icount_time_shift) - 1) >> icount_time_shift; +} + +static struct qemu_alarm_timer alarm_timers[] = { +#ifndef _WIN32 +#ifdef __linux__ + {"dynticks", dynticks_start_timer, + dynticks_stop_timer, dynticks_rearm_timer, NULL}, + /* HPET - if available - is preferred */ + {"hpet", hpet_start_timer, hpet_stop_timer, NULL, NULL}, + /* ...otherwise try RTC */ + {"rtc", rtc_start_timer, rtc_stop_timer, NULL, NULL}, +#endif + {"unix", unix_start_timer, unix_stop_timer, NULL, NULL}, +#else + {"dynticks", win32_start_timer, + win32_stop_timer, win32_rearm_timer, &alarm_win32_data}, + {"win32", win32_start_timer, + win32_stop_timer, NULL, &alarm_win32_data}, +#endif + {NULL, } +}; + +static void show_available_alarms(void) +{ + int i; + + printf("Available alarm timers, in order of precedence:\n"); + for (i = 0; alarm_timers[i].name; i++) + printf("%s\n", alarm_timers[i].name); +} + +void configure_alarms(char const *opt) +{ + int i; + int cur = 0; + int count = ARRAY_SIZE(alarm_timers) - 1; + char *arg; + char *name; + struct qemu_alarm_timer tmp; + + if (!strcmp(opt, "?")) { + show_available_alarms(); + exit(0); + } + + arg = qemu_strdup(opt); + + /* Reorder the array */ + name = strtok(arg, ","); + while (name) { + for (i = 0; i < count && alarm_timers[i].name; i++) { + if (!strcmp(alarm_timers[i].name, name)) + break; + } + + if (i == count) { + fprintf(stderr, "Unknown clock %s\n", name); + goto next; + } + + if (i < cur) + /* Ignore */ + goto next; + + /* Swap */ + tmp = alarm_timers[i]; + alarm_timers[i] = alarm_timers[cur]; + alarm_timers[cur] = tmp; + + cur++; +next: + name = strtok(NULL, ","); + } + + qemu_free(arg); + + if (cur) { + /* Disable remaining timers */ + for (i = cur; i < count; i++) + alarm_timers[i].name = NULL; + } else { + show_available_alarms(); + exit(1); + } +} + +#define QEMU_NUM_CLOCKS 3 + +QEMUClock *rt_clock; +QEMUClock *vm_clock; +QEMUClock *host_clock; + +static QEMUTimer *active_timers[QEMU_NUM_CLOCKS]; + +static QEMUClock *qemu_new_clock(int type) +{ + QEMUClock *clock; + clock = qemu_mallocz(sizeof(QEMUClock)); + clock->type = type; + clock->enabled = 1; + return clock; +} + +void qemu_clock_enable(QEMUClock *clock, int enabled) +{ + clock->enabled = enabled; +} + +QEMUTimer *qemu_new_timer(QEMUClock *clock, QEMUTimerCB *cb, void *opaque) +{ + QEMUTimer *ts; + + ts = qemu_mallocz(sizeof(QEMUTimer)); + ts->clock = clock; + ts->cb = cb; + ts->opaque = opaque; + return ts; +} + +void qemu_free_timer(QEMUTimer *ts) +{ + qemu_free(ts); +} + +/* stop a timer, but do not dealloc it */ +void qemu_del_timer(QEMUTimer *ts) +{ + QEMUTimer **pt, *t; + + /* NOTE: this code must be signal safe because + qemu_timer_expired() can be called from a signal. */ + pt = &active_timers[ts->clock->type]; + for(;;) { + t = *pt; + if (!t) + break; + if (t == ts) { + *pt = t->next; + break; + } + pt = &t->next; + } +} + +/* modify the current timer so that it will be fired when current_time + >= expire_time. The corresponding callback will be called. */ +void qemu_mod_timer(QEMUTimer *ts, int64_t expire_time) +{ + QEMUTimer **pt, *t; + + qemu_del_timer(ts); + + /* add the timer in the sorted list */ + /* NOTE: this code must be signal safe because + qemu_timer_expired() can be called from a signal. */ + pt = &active_timers[ts->clock->type]; + for(;;) { + t = *pt; + if (!t) + break; + if (t->expire_time > expire_time) + break; + pt = &t->next; + } + ts->expire_time = expire_time; + ts->next = *pt; + *pt = ts; + + /* Rearm if necessary */ + if (pt == &active_timers[ts->clock->type]) { + if (!alarm_timer->pending) { + qemu_rearm_alarm_timer(alarm_timer); + } + /* Interrupt execution to force deadline recalculation. */ + if (use_icount) + qemu_notify_event(); + } +} + +int qemu_timer_pending(QEMUTimer *ts) +{ + QEMUTimer *t; + for(t = active_timers[ts->clock->type]; t != NULL; t = t->next) { + if (t == ts) + return 1; + } + return 0; +} + +int qemu_timer_expired(QEMUTimer *timer_head, int64_t current_time) +{ + if (!timer_head) + return 0; + return (timer_head->expire_time <= current_time); +} + +static void qemu_run_timers(QEMUClock *clock) +{ + QEMUTimer **ptimer_head, *ts; + int64_t current_time; + + if (!clock->enabled) + return; + + current_time = qemu_get_clock (clock); + ptimer_head = &active_timers[clock->type]; + for(;;) { + ts = *ptimer_head; + if (!ts || ts->expire_time > current_time) + break; + /* remove timer from the list before calling the callback */ + *ptimer_head = ts->next; + ts->next = NULL; + + /* run the callback (the timer list can be modified) */ + ts->cb(ts->opaque); + } +} + +int64_t qemu_get_clock(QEMUClock *clock) +{ + switch(clock->type) { + case QEMU_CLOCK_REALTIME: + return get_clock() / 1000000; + default: + case QEMU_CLOCK_VIRTUAL: + if (use_icount) { + return cpu_get_icount(); + } else { + return cpu_get_clock(); + } + case QEMU_CLOCK_HOST: + return get_clock_realtime(); + } +} + +int64_t qemu_get_clock_ns(QEMUClock *clock) +{ + switch(clock->type) { + case QEMU_CLOCK_REALTIME: + return get_clock(); + default: + case QEMU_CLOCK_VIRTUAL: + if (use_icount) { + return cpu_get_icount(); + } else { + return cpu_get_clock(); + } + case QEMU_CLOCK_HOST: + return get_clock_realtime(); + } +} + +void init_clocks(void) +{ + rt_clock = qemu_new_clock(QEMU_CLOCK_REALTIME); + vm_clock = qemu_new_clock(QEMU_CLOCK_VIRTUAL); + host_clock = qemu_new_clock(QEMU_CLOCK_HOST); + + rtc_clock = host_clock; +} + +/* save a timer */ +void qemu_put_timer(QEMUFile *f, QEMUTimer *ts) +{ + uint64_t expire_time; + + if (qemu_timer_pending(ts)) { + expire_time = ts->expire_time; + } else { + expire_time = -1; + } + qemu_put_be64(f, expire_time); +} + +void qemu_get_timer(QEMUFile *f, QEMUTimer *ts) +{ + uint64_t expire_time; + + expire_time = qemu_get_be64(f); + if (expire_time != -1) { + qemu_mod_timer(ts, expire_time); + } else { + qemu_del_timer(ts); + } +} + +static const VMStateDescription vmstate_timers = { + .name = "timer", + .version_id = 2, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField []) { + VMSTATE_INT64(cpu_ticks_offset, TimersState), + VMSTATE_INT64(dummy, TimersState), + VMSTATE_INT64_V(cpu_clock_offset, TimersState, 2), + VMSTATE_END_OF_LIST() + } +}; + +void configure_icount(const char *option) +{ + vmstate_register(NULL, 0, &vmstate_timers, &timers_state); + if (!option) + return; + + if (strcmp(option, "auto") != 0) { + icount_time_shift = strtol(option, NULL, 0); + use_icount = 1; + return; + } + + use_icount = 2; + + /* 125MIPS seems a reasonable initial guess at the guest speed. + It will be corrected fairly quickly anyway. */ + icount_time_shift = 3; + + /* Have both realtime and virtual time triggers for speed adjustment. + The realtime trigger catches emulated time passing too slowly, + the virtual time trigger catches emulated time passing too fast. + Realtime triggers occur even when idle, so use them less frequently + than VM triggers. */ + icount_rt_timer = qemu_new_timer(rt_clock, icount_adjust_rt, NULL); + qemu_mod_timer(icount_rt_timer, + qemu_get_clock(rt_clock) + 1000); + icount_vm_timer = qemu_new_timer(vm_clock, icount_adjust_vm, NULL); + qemu_mod_timer(icount_vm_timer, + qemu_get_clock(vm_clock) + get_ticks_per_sec() / 10); +} + +void qemu_run_all_timers(void) +{ + alarm_timer->pending = 0; + + /* rearm timer, if not periodic */ + if (alarm_timer->expired) { + alarm_timer->expired = 0; + qemu_rearm_alarm_timer(alarm_timer); + } + + /* vm time timers */ + if (vm_running) { + qemu_run_timers(vm_clock); + } + + qemu_run_timers(rt_clock); + qemu_run_timers(host_clock); +} + +static int64_t qemu_next_alarm_deadline(void); + +#ifdef _WIN32 +static void CALLBACK host_alarm_handler(UINT uTimerID, UINT uMsg, + DWORD_PTR dwUser, DWORD_PTR dw1, + DWORD_PTR dw2) +#else +static void host_alarm_handler(int host_signum) +#endif +{ + struct qemu_alarm_timer *t = alarm_timer; + if (!t) + return; + +#if 0 +#define DISP_FREQ 1000 + { + static int64_t delta_min = INT64_MAX; + static int64_t delta_max, delta_cum, last_clock, delta, ti; + static int count; + ti = qemu_get_clock(vm_clock); + if (last_clock != 0) { + delta = ti - last_clock; + if (delta < delta_min) + delta_min = delta; + if (delta > delta_max) + delta_max = delta; + delta_cum += delta; + if (++count == DISP_FREQ) { + printf("timer: min=%" PRId64 " us max=%" PRId64 " us avg=%" PRId64 " us avg_freq=%0.3f Hz\n", + muldiv64(delta_min, 1000000, get_ticks_per_sec()), + muldiv64(delta_max, 1000000, get_ticks_per_sec()), + muldiv64(delta_cum, 1000000 / DISP_FREQ, get_ticks_per_sec()), + (double)get_ticks_per_sec() / ((double)delta_cum / DISP_FREQ)); + count = 0; + delta_min = INT64_MAX; + delta_max = 0; + delta_cum = 0; + } + } + last_clock = ti; + } +#endif + if (alarm_has_dynticks(t) || + qemu_next_alarm_deadline () <= 0) { + t->expired = alarm_has_dynticks(t); + t->pending = 1; + qemu_notify_event(); + } +} + +int64_t qemu_next_deadline(void) +{ + /* To avoid problems with overflow limit this to 2^32. */ + int64_t delta = INT32_MAX; + + if (active_timers[QEMU_CLOCK_VIRTUAL]) { + delta = active_timers[QEMU_CLOCK_VIRTUAL]->expire_time - + qemu_get_clock_ns(vm_clock); + } + if (active_timers[QEMU_CLOCK_HOST]) { + int64_t hdelta = active_timers[QEMU_CLOCK_HOST]->expire_time - + qemu_get_clock_ns(host_clock); + if (hdelta < delta) + delta = hdelta; + } + + if (delta < 0) + delta = 0; + + return delta; +} + +static int64_t qemu_next_alarm_deadline(void) +{ + int64_t delta; + int64_t rtdelta; + + if (!use_icount && active_timers[QEMU_CLOCK_VIRTUAL]) { + delta = active_timers[QEMU_CLOCK_VIRTUAL]->expire_time - + qemu_get_clock(vm_clock); + } else { + delta = INT32_MAX; + } + if (active_timers[QEMU_CLOCK_HOST]) { + int64_t hdelta = active_timers[QEMU_CLOCK_HOST]->expire_time - + qemu_get_clock_ns(host_clock); + if (hdelta < delta) + delta = hdelta; + } + if (active_timers[QEMU_CLOCK_REALTIME]) { + rtdelta = (active_timers[QEMU_CLOCK_REALTIME]->expire_time * 1000000 - + qemu_get_clock_ns(rt_clock)); + if (rtdelta < delta) + delta = rtdelta; + } + + return delta; +} + +#if defined(__linux__) + +#define RTC_FREQ 1024 + +static void enable_sigio_timer(int fd) +{ + struct sigaction act; + + /* timer signal */ + sigfillset(&act.sa_mask); + act.sa_flags = 0; + act.sa_handler = host_alarm_handler; + + sigaction(SIGIO, &act, NULL); + fcntl_setfl(fd, O_ASYNC); + fcntl(fd, F_SETOWN, getpid()); +} + +static int hpet_start_timer(struct qemu_alarm_timer *t) +{ + struct hpet_info info; + int r, fd; + + fd = qemu_open("/dev/hpet", O_RDONLY); + if (fd < 0) + return -1; + + /* Set frequency */ + r = ioctl(fd, HPET_IRQFREQ, RTC_FREQ); + if (r < 0) { + fprintf(stderr, "Could not configure '/dev/hpet' to have a 1024Hz timer. This is not a fatal\n" + "error, but for better emulation accuracy type:\n" + "'echo 1024 > /proc/sys/dev/hpet/max-user-freq' as root.\n"); + goto fail; + } + + /* Check capabilities */ + r = ioctl(fd, HPET_INFO, &info); + if (r < 0) + goto fail; + + /* Enable periodic mode */ + r = ioctl(fd, HPET_EPI, 0); + if (info.hi_flags && (r < 0)) + goto fail; + + /* Enable interrupt */ + r = ioctl(fd, HPET_IE_ON, 0); + if (r < 0) + goto fail; + + enable_sigio_timer(fd); + t->priv = (void *)(long)fd; + + return 0; +fail: + close(fd); + return -1; +} + +static void hpet_stop_timer(struct qemu_alarm_timer *t) +{ + int fd = (long)t->priv; + + close(fd); +} + +static int rtc_start_timer(struct qemu_alarm_timer *t) +{ + int rtc_fd; + unsigned long current_rtc_freq = 0; + + TFR(rtc_fd = qemu_open("/dev/rtc", O_RDONLY)); + if (rtc_fd < 0) + return -1; + ioctl(rtc_fd, RTC_IRQP_READ, ¤t_rtc_freq); + if (current_rtc_freq != RTC_FREQ && + ioctl(rtc_fd, RTC_IRQP_SET, RTC_FREQ) < 0) { + fprintf(stderr, "Could not configure '/dev/rtc' to have a 1024 Hz timer. This is not a fatal\n" + "error, but for better emulation accuracy either use a 2.6 host Linux kernel or\n" + "type 'echo 1024 > /proc/sys/dev/rtc/max-user-freq' as root.\n"); + goto fail; + } + if (ioctl(rtc_fd, RTC_PIE_ON, 0) < 0) { + fail: + close(rtc_fd); + return -1; + } + + enable_sigio_timer(rtc_fd); + + t->priv = (void *)(long)rtc_fd; + + return 0; +} + +static void rtc_stop_timer(struct qemu_alarm_timer *t) +{ + int rtc_fd = (long)t->priv; + + close(rtc_fd); +} + +static int dynticks_start_timer(struct qemu_alarm_timer *t) +{ + struct sigevent ev; + timer_t host_timer; + struct sigaction act; + + sigfillset(&act.sa_mask); + act.sa_flags = 0; + act.sa_handler = host_alarm_handler; + + sigaction(SIGALRM, &act, NULL); + + /* + * Initialize ev struct to 0 to avoid valgrind complaining + * about uninitialized data in timer_create call + */ + memset(&ev, 0, sizeof(ev)); + ev.sigev_value.sival_int = 0; + ev.sigev_notify = SIGEV_SIGNAL; + ev.sigev_signo = SIGALRM; + + if (timer_create(CLOCK_REALTIME, &ev, &host_timer)) { + perror("timer_create"); + + /* disable dynticks */ + fprintf(stderr, "Dynamic Ticks disabled\n"); + + return -1; + } + + t->priv = (void *)(long)host_timer; + + return 0; +} + +static void dynticks_stop_timer(struct qemu_alarm_timer *t) +{ + timer_t host_timer = (timer_t)(long)t->priv; + + timer_delete(host_timer); +} + +static void dynticks_rearm_timer(struct qemu_alarm_timer *t) +{ + timer_t host_timer = (timer_t)(long)t->priv; + struct itimerspec timeout; + int64_t nearest_delta_ns = INT64_MAX; + int64_t current_ns; + + assert(alarm_has_dynticks(t)); + if (!active_timers[QEMU_CLOCK_REALTIME] && + !active_timers[QEMU_CLOCK_VIRTUAL] && + !active_timers[QEMU_CLOCK_HOST]) + return; + + nearest_delta_ns = qemu_next_alarm_deadline(); + if (nearest_delta_ns < MIN_TIMER_REARM_NS) + nearest_delta_ns = MIN_TIMER_REARM_NS; + + /* check whether a timer is already running */ + if (timer_gettime(host_timer, &timeout)) { + perror("gettime"); + fprintf(stderr, "Internal timer error: aborting\n"); + exit(1); + } + current_ns = timeout.it_value.tv_sec * 1000000000LL + timeout.it_value.tv_nsec; + if (current_ns && current_ns <= nearest_delta_ns) + return; + + timeout.it_interval.tv_sec = 0; + timeout.it_interval.tv_nsec = 0; /* 0 for one-shot timer */ + timeout.it_value.tv_sec = nearest_delta_ns / 1000000000; + timeout.it_value.tv_nsec = nearest_delta_ns % 1000000000; + if (timer_settime(host_timer, 0 /* RELATIVE */, &timeout, NULL)) { + perror("settime"); + fprintf(stderr, "Internal timer error: aborting\n"); + exit(1); + } +} + +#endif /* defined(__linux__) */ + +#if !defined(_WIN32) + +static int unix_start_timer(struct qemu_alarm_timer *t) +{ + struct sigaction act; + struct itimerval itv; + int err; + + /* timer signal */ + sigfillset(&act.sa_mask); + act.sa_flags = 0; + act.sa_handler = host_alarm_handler; + + sigaction(SIGALRM, &act, NULL); + + itv.it_interval.tv_sec = 0; + /* for i386 kernel 2.6 to get 1 ms */ + itv.it_interval.tv_usec = 999; + itv.it_value.tv_sec = 0; + itv.it_value.tv_usec = 10 * 1000; + + err = setitimer(ITIMER_REAL, &itv, NULL); + if (err) + return -1; + + return 0; +} + +static void unix_stop_timer(struct qemu_alarm_timer *t) +{ + struct itimerval itv; + + memset(&itv, 0, sizeof(itv)); + setitimer(ITIMER_REAL, &itv, NULL); +} + +#endif /* !defined(_WIN32) */ + + +#ifdef _WIN32 + +static int win32_start_timer(struct qemu_alarm_timer *t) +{ + TIMECAPS tc; + struct qemu_alarm_win32 *data = t->priv; + UINT flags; + + memset(&tc, 0, sizeof(tc)); + timeGetDevCaps(&tc, sizeof(tc)); + + data->period = tc.wPeriodMin; + timeBeginPeriod(data->period); + + flags = TIME_CALLBACK_FUNCTION; + if (alarm_has_dynticks(t)) + flags |= TIME_ONESHOT; + else + flags |= TIME_PERIODIC; + + data->timerId = timeSetEvent(1, // interval (ms) + data->period, // resolution + host_alarm_handler, // function + (DWORD)t, // parameter + flags); + + if (!data->timerId) { + fprintf(stderr, "Failed to initialize win32 alarm timer: %ld\n", + GetLastError()); + timeEndPeriod(data->period); + return -1; + } + + return 0; +} + +static void win32_stop_timer(struct qemu_alarm_timer *t) +{ + struct qemu_alarm_win32 *data = t->priv; + + timeKillEvent(data->timerId); + timeEndPeriod(data->period); +} + +static void win32_rearm_timer(struct qemu_alarm_timer *t) +{ + struct qemu_alarm_win32 *data = t->priv; + + assert(alarm_has_dynticks(t)); + if (!active_timers[QEMU_CLOCK_REALTIME] && + !active_timers[QEMU_CLOCK_VIRTUAL] && + !active_timers[QEMU_CLOCK_HOST]) + return; + + timeKillEvent(data->timerId); + + data->timerId = timeSetEvent(1, + data->period, + host_alarm_handler, + (DWORD)t, + TIME_ONESHOT | TIME_CALLBACK_FUNCTION); + + if (!data->timerId) { + fprintf(stderr, "Failed to re-arm win32 alarm timer %ld\n", + GetLastError()); + + timeEndPeriod(data->period); + exit(1); + } +} + +#endif /* _WIN32 */ + +static void alarm_timer_on_change_state_rearm(void *opaque, int running, int reason) +{ + if (running) + qemu_rearm_alarm_timer((struct qemu_alarm_timer *) opaque); +} + +int init_timer_alarm(void) +{ + struct qemu_alarm_timer *t = NULL; + int i, err = -1; + + for (i = 0; alarm_timers[i].name; i++) { + t = &alarm_timers[i]; + + err = t->start(t); + if (!err) + break; + } + + if (err) { + err = -ENOENT; + goto fail; + } + + /* first event is at time 0 */ + t->pending = 1; + alarm_timer = t; + qemu_add_vm_change_state_handler(alarm_timer_on_change_state_rearm, t); + + return 0; + +fail: + return err; +} + +void quit_timers(void) +{ + struct qemu_alarm_timer *t = alarm_timer; + alarm_timer = NULL; + t->stop(t); +} + +int qemu_calculate_timeout(void) +{ + int timeout; + +#define QEMUKVM 1 +#if defined (CONFIG_IOTHREAD) || defined (QEMUKVM) + /* When using icount, making forward progress with qemu_icount when the + guest CPU is idle is critical. We only use the static io-thread timeout + for non icount runs. */ + if (!use_icount) { + return 1000; + } +#endif + + if (!vm_running) + timeout = 5000; + else { + /* XXX: use timeout computed from timers */ + int64_t add; + int64_t delta; + /* Advance virtual time to the next event. */ + delta = qemu_icount_delta(); + if (delta > 0) { + /* If virtual time is ahead of real time then just + wait for IO. */ + timeout = (delta + 999999) / 1000000; + } else { + /* Wait for either IO to occur or the next + timer event. */ + add = qemu_next_deadline(); + /* We advance the timer before checking for IO. + Limit the amount we advance so that early IO + activity won't get the guest too far ahead. */ + if (add > 10000000) + add = 10000000; + delta += add; + qemu_icount += qemu_icount_round (add); + timeout = delta / 1000000; + if (timeout < 0) + timeout = 0; + } + } + + return timeout; +} + diff -Nru qemu-kvm-0.12.5+noroms/qemu-timer-common.c qemu-kvm-0.14.1/qemu-timer-common.c --- qemu-kvm-0.12.5+noroms/qemu-timer-common.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/qemu-timer-common.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,63 @@ +/* + * 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 "qemu-timer.h" + +/***********************************************************/ +/* real time host monotonic timer */ + +#ifdef _WIN32 + +int64_t clock_freq; + +static void __attribute__((constructor)) init_get_clock(void) +{ + LARGE_INTEGER freq; + int ret; + ret = QueryPerformanceFrequency(&freq); + if (ret == 0) { + fprintf(stderr, "Could not calibrate ticks\n"); + exit(1); + } + clock_freq = freq.QuadPart; +} + +#else + +int use_rt_clock; + +static void __attribute__((constructor)) init_get_clock(void) +{ + use_rt_clock = 0; +#if defined(__linux__) || (defined(__FreeBSD__) && __FreeBSD_version >= 500000) \ + || defined(__DragonFly__) || defined(__FreeBSD_kernel__) \ + || defined(__OpenBSD__) + { + struct timespec ts; + if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) { + use_rt_clock = 1; + } + } +#endif +} +#endif diff -Nru qemu-kvm-0.12.5+noroms/qemu-timer.h qemu-kvm-0.14.1/qemu-timer.h --- qemu-kvm-0.12.5+noroms/qemu-timer.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/qemu-timer.h 2011-05-11 13:29:46.000000000 +0000 @@ -1,6 +1,15 @@ #ifndef QEMU_TIMER_H #define QEMU_TIMER_H +#include "qemu-common.h" +#include +#include + +#ifdef _WIN32 +#include +#include +#endif + /* timers */ typedef struct QEMUClock QEMUClock; @@ -25,6 +34,8 @@ extern QEMUClock *host_clock; int64_t qemu_get_clock(QEMUClock *clock); +int64_t qemu_get_clock_ns(QEMUClock *clock); +void qemu_clock_enable(QEMUClock *clock, int enabled); QEMUTimer *qemu_new_timer(QEMUClock *clock, QEMUTimerCB *cb, void *opaque); void qemu_free_timer(QEMUTimer *ts); @@ -33,11 +44,65 @@ int qemu_timer_pending(QEMUTimer *ts); int qemu_timer_expired(QEMUTimer *timer_head, int64_t current_time); +void qemu_run_all_timers(void); +int qemu_alarm_pending(void); +int64_t qemu_next_deadline(void); +void configure_alarms(char const *opt); +void configure_icount(const char *option); +int qemu_calculate_timeout(void); +void init_clocks(void); +int init_timer_alarm(void); +void quit_timers(void); + static inline int64_t get_ticks_per_sec(void) { return 1000000000LL; } +/* real time host monotonic timer */ +static inline int64_t get_clock_realtime(void) +{ + struct timeval tv; + + gettimeofday(&tv, NULL); + return tv.tv_sec * 1000000000LL + (tv.tv_usec * 1000); +} + +/* Warning: don't insert tracepoints into these functions, they are + also used by simpletrace backend and tracepoints would cause + an infinite recursion! */ +#ifdef _WIN32 +extern int64_t clock_freq; + +static inline int64_t get_clock(void) +{ + LARGE_INTEGER ti; + QueryPerformanceCounter(&ti); + return muldiv64(ti.QuadPart, get_ticks_per_sec(), clock_freq); +} + +#else + +extern int use_rt_clock; + +static inline int64_t get_clock(void) +{ +#if defined(__linux__) || (defined(__FreeBSD__) && __FreeBSD_version >= 500000) \ + || defined(__DragonFly__) || defined(__FreeBSD_kernel__) + if (use_rt_clock) { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return ts.tv_sec * 1000000000LL + ts.tv_nsec; + } else +#endif + { + /* XXX: using gettimeofday leads to problems if the date + changes, so it should be avoided. */ + return get_clock_realtime(); + } +} +#endif + void qemu_get_timer(QEMUFile *f, QEMUTimer *ts); void qemu_put_timer(QEMUFile *f, QEMUTimer *ts); @@ -56,4 +121,193 @@ void qemu_put_ptimer(QEMUFile *f, ptimer_state *s); void qemu_get_ptimer(QEMUFile *f, ptimer_state *s); +/* icount */ +int64_t qemu_icount_round(int64_t count); +extern int64_t qemu_icount; +extern int use_icount; +extern int icount_time_shift; +extern int64_t qemu_icount_bias; +int64_t cpu_get_icount(void); + +/*******************************************/ +/* host CPU ticks (if available) */ + +#if defined(_ARCH_PPC) + +static inline int64_t cpu_get_real_ticks(void) +{ + int64_t retval; +#ifdef _ARCH_PPC64 + /* This reads timebase in one 64bit go and includes Cell workaround from: + http://ozlabs.org/pipermail/linuxppc-dev/2006-October/027052.html + */ + __asm__ __volatile__ ("mftb %0\n\t" + "cmpwi %0,0\n\t" + "beq- $-8" + : "=r" (retval)); +#else + /* http://ozlabs.org/pipermail/linuxppc-dev/1999-October/003889.html */ + unsigned long junk; + __asm__ __volatile__ ("mfspr %1,269\n\t" /* mftbu */ + "mfspr %L0,268\n\t" /* mftb */ + "mfspr %0,269\n\t" /* mftbu */ + "cmpw %0,%1\n\t" + "bne $-16" + : "=r" (retval), "=r" (junk)); +#endif + return retval; +} + +#elif defined(__i386__) + +static inline int64_t cpu_get_real_ticks(void) +{ + int64_t val; + asm volatile ("rdtsc" : "=A" (val)); + return val; +} + +#elif defined(__x86_64__) + +static inline int64_t cpu_get_real_ticks(void) +{ + uint32_t low,high; + int64_t val; + asm volatile("rdtsc" : "=a" (low), "=d" (high)); + val = high; + val <<= 32; + val |= low; + return val; +} + +#elif defined(__hppa__) + +static inline int64_t cpu_get_real_ticks(void) +{ + int val; + asm volatile ("mfctl %%cr16, %0" : "=r"(val)); + return val; +} + +#elif defined(__ia64) + +static inline int64_t cpu_get_real_ticks(void) +{ + int64_t val; + asm volatile ("mov %0 = ar.itc" : "=r"(val) :: "memory"); + return val; +} + +#elif defined(__s390__) + +static inline int64_t cpu_get_real_ticks(void) +{ + int64_t val; + asm volatile("stck 0(%1)" : "=m" (val) : "a" (&val) : "cc"); + return val; +} + +#elif defined(__sparc_v8plus__) || defined(__sparc_v8plusa__) || defined(__sparc_v9__) + +static inline int64_t cpu_get_real_ticks (void) +{ +#if defined(_LP64) + uint64_t rval; + asm volatile("rd %%tick,%0" : "=r"(rval)); + return rval; +#else + union { + uint64_t i64; + struct { + uint32_t high; + uint32_t low; + } i32; + } rval; + asm volatile("rd %%tick,%1; srlx %1,32,%0" + : "=r"(rval.i32.high), "=r"(rval.i32.low)); + return rval.i64; +#endif +} + +#elif defined(__mips__) && \ + ((defined(__mips_isa_rev) && __mips_isa_rev >= 2) || defined(__linux__)) +/* + * binutils wants to use rdhwr only on mips32r2 + * but as linux kernel emulate it, it's fine + * to use it. + * + */ +#define MIPS_RDHWR(rd, value) { \ + __asm__ __volatile__ (".set push\n\t" \ + ".set mips32r2\n\t" \ + "rdhwr %0, "rd"\n\t" \ + ".set pop" \ + : "=r" (value)); \ + } + +static inline int64_t cpu_get_real_ticks(void) +{ + /* On kernels >= 2.6.25 rdhwr , $2 and $3 are emulated */ + uint32_t count; + static uint32_t cyc_per_count = 0; + + if (!cyc_per_count) { + MIPS_RDHWR("$3", cyc_per_count); + } + + MIPS_RDHWR("$2", count); + return (int64_t)(count * cyc_per_count); +} + +#elif defined(__alpha__) + +static inline int64_t cpu_get_real_ticks(void) +{ + uint64_t cc; + uint32_t cur, ofs; + + asm volatile("rpcc %0" : "=r"(cc)); + cur = cc; + ofs = cc >> 32; + return cur - ofs; +} + +#else +/* The host CPU doesn't have an easily accessible cycle counter. + Just return a monotonically increasing value. This will be + totally wrong, but hopefully better than nothing. */ +static inline int64_t cpu_get_real_ticks (void) +{ + static int64_t ticks = 0; + return ticks++; +} +#endif + +#ifdef NEED_CPU_H +/* Deterministic execution requires that IO only be performed on the last + instruction of a TB so that interrupts take effect immediately. */ +static inline int can_do_io(CPUState *env) +{ + if (!use_icount) + return 1; + + /* If not executing code then assume we are ok. */ + if (!env->current_tb) + return 1; + + return env->can_do_io != 0; +} +#endif + +#ifdef CONFIG_PROFILER +static inline int64_t profile_getclock(void) +{ + return cpu_get_real_ticks(); +} + +extern int64_t qemu_time, qemu_time_start; +extern int64_t tlb_flush_time; +extern int64_t dev_time; +#endif + #endif diff -Nru qemu-kvm-0.12.5+noroms/qemu-tool.c qemu-kvm-0.14.1/qemu-tool.c --- qemu-kvm-0.12.5+noroms/qemu-tool.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/qemu-tool.c 2011-05-11 13:29:46.000000000 +0000 @@ -13,9 +13,9 @@ #include "qemu-common.h" #include "monitor.h" -#include "sysemu.h" #include "qemu-timer.h" #include "qemu-log.h" +#include "sysemu.h" #include @@ -35,6 +35,19 @@ Monitor *cur_mon; +int monitor_cur_is_qmp(void) +{ + return 0; +} + +void monitor_set_error(Monitor *mon, QError *qerror) +{ +} + +void monitor_vprintf(Monitor *mon, const char *fmt, va_list ap) +{ +} + void monitor_printf(Monitor *mon, const char *fmt, ...) { } @@ -91,26 +104,10 @@ } int qemu_set_fd_handler2(int fd, - IOCanRWHandler *fd_read_poll, + IOCanReadHandler *fd_read_poll, IOHandler *fd_read, IOHandler *fd_write, void *opaque) { return 0; } - -int64_t qemu_get_clock(QEMUClock *clock) -{ - qemu_timeval tv; - qemu_gettimeofday(&tv); - return (tv.tv_sec * 1000000000LL + (tv.tv_usec * 1000)) / 1000000; -} - -void qemu_error(const char *fmt, ...) -{ - va_list args; - - va_start(args, fmt); - vfprintf(stderr, fmt, args); - va_end(args); -} diff -Nru qemu-kvm-0.12.5+noroms/qemu-x509.h qemu-kvm-0.14.1/qemu-x509.h --- qemu-kvm-0.12.5+noroms/qemu-x509.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/qemu-x509.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,9 @@ +#ifndef QEMU_X509_H +#define QEMU_X509_H + +#define X509_CA_CERT_FILE "ca-cert.pem" +#define X509_CA_CRL_FILE "ca-crl.pem" +#define X509_SERVER_KEY_FILE "server-key.pem" +#define X509_SERVER_CERT_FILE "server-cert.pem" + +#endif /* QEMU_X509_H */ diff -Nru qemu-kvm-0.12.5+noroms/qerror.c qemu-kvm-0.14.1/qerror.c --- qemu-kvm-0.12.5+noroms/qerror.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/qerror.c 2011-05-11 13:29:46.000000000 +0000 @@ -1,5 +1,5 @@ /* - * QError: QEMU Error data-type. + * QError Module * * Copyright (C) 2009 Red Hat Inc. * @@ -9,10 +9,10 @@ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. * See the COPYING.LIB file in the top-level directory. */ + +#include "monitor.h" #include "qjson.h" #include "qerror.h" -#include "qstring.h" -#include "sysemu.h" #include "qemu-common.h" static void qerror_destroy_obj(QObject *obj); @@ -38,35 +38,79 @@ * for example: * * "running out of foo: %(foo)%%" + * + * Please keep the entries in alphabetical order. + * Use "sed -n '/^static.*qerror_table\[\]/,/^};/s/QERR_/&/gp' qerror.c | sort -c" + * to check. */ static const QErrorStringTable qerror_table[] = { { + .error_fmt = QERR_BAD_BUS_FOR_DEVICE, + .desc = "Device '%(device)' can't go on a %(bad_bus_type) bus", + }, + { + .error_fmt = QERR_BUS_NOT_FOUND, + .desc = "Bus '%(bus)' not found", + }, + { + .error_fmt = QERR_BUS_NO_HOTPLUG, + .desc = "Bus '%(bus)' does not support hotplugging", + }, + { .error_fmt = QERR_COMMAND_NOT_FOUND, .desc = "The command %(name) has not been found", }, { .error_fmt = QERR_DEVICE_ENCRYPTED, - .desc = "The %(device) is encrypted", + .desc = "Device '%(device)' is encrypted", + }, + { + .error_fmt = QERR_DEVICE_INIT_FAILED, + .desc = "Device '%(device)' could not be initialized", + }, + { + .error_fmt = QERR_DEVICE_IN_USE, + .desc = "Device '%(device)' is in use", }, { .error_fmt = QERR_DEVICE_LOCKED, - .desc = "Device %(device) is locked", + .desc = "Device '%(device)' is locked", + }, + { + .error_fmt = QERR_DEVICE_MULTIPLE_BUSSES, + .desc = "Device '%(device)' has multiple child busses", }, { .error_fmt = QERR_DEVICE_NOT_ACTIVE, - .desc = "The %(device) device has not been activated by the guest", + .desc = "Device '%(device)' has not been activated", + }, + { + .error_fmt = QERR_DEVICE_NOT_ENCRYPTED, + .desc = "Device '%(device)' is not encrypted", }, { .error_fmt = QERR_DEVICE_NOT_FOUND, - .desc = "The %(device) device has not been found", + .desc = "Device '%(device)' not found", }, { .error_fmt = QERR_DEVICE_NOT_REMOVABLE, - .desc = "Device %(device) is not removable", + .desc = "Device '%(device)' is not removable", + }, + { + .error_fmt = QERR_DEVICE_NO_BUS, + .desc = "Device '%(device)' has no child bus", + }, + { + .error_fmt = QERR_DEVICE_NO_HOTPLUG, + .desc = "Device '%(device)' does not support hotplugging", + }, + { + .error_fmt = QERR_DUPLICATE_ID, + .desc = "Duplicate ID '%(id)' for %(object)", }, { .error_fmt = QERR_FD_NOT_FOUND, - .desc = "Failed to find file descriptor named %(name)", + .desc = "File descriptor named '%(name)' not found", }, { .error_fmt = QERR_FD_NOT_SUPPLIED, @@ -74,19 +118,23 @@ }, { .error_fmt = QERR_INVALID_BLOCK_FORMAT, - .desc = "Invalid block format %(name)", + .desc = "Invalid block format '%(name)'", }, { .error_fmt = QERR_INVALID_PARAMETER, - .desc = "Invalid parameter %(name)", + .desc = "Invalid parameter '%(name)'", }, { .error_fmt = QERR_INVALID_PARAMETER_TYPE, .desc = "Invalid parameter type, expected: %(expected)", }, { + .error_fmt = QERR_INVALID_PARAMETER_VALUE, + .desc = "Parameter '%(name)' expects %(expected)", + }, + { .error_fmt = QERR_INVALID_PASSWORD, - .desc = "The entered password is invalid", + .desc = "Password incorrect", }, { .error_fmt = QERR_JSON_PARSING, @@ -97,12 +145,48 @@ .desc = "Using KVM without %(capability), %(feature) unavailable", }, { + .error_fmt = QERR_MIGRATION_EXPECTED, + .desc = "An incoming migration is expected before this command can be executed", + }, + { .error_fmt = QERR_MISSING_PARAMETER, - .desc = "Parameter %(name) is missing", + .desc = "Parameter '%(name)' is missing", + }, + { + .error_fmt = QERR_NO_BUS_FOR_DEVICE, + .desc = "No '%(bus)' bus found for device '%(device)'", + }, + { + .error_fmt = QERR_OPEN_FILE_FAILED, + .desc = "Could not open '%(filename)'", + }, + { + .error_fmt = QERR_PROPERTY_NOT_FOUND, + .desc = "Property '%(device).%(property)' not found", + }, + { + .error_fmt = QERR_PROPERTY_VALUE_BAD, + .desc = "Property '%(device).%(property)' doesn't take value '%(value)'", + }, + { + .error_fmt = QERR_PROPERTY_VALUE_IN_USE, + .desc = "Property '%(device).%(property)' can't take value '%(value)', it's in use", + }, + { + .error_fmt = QERR_PROPERTY_VALUE_NOT_FOUND, + .desc = "Property '%(device).%(property)' can't find value '%(value)'", }, { .error_fmt = QERR_QMP_BAD_INPUT_OBJECT, - .desc = "Bad QMP input object", + .desc = "Expected '%(expected)' in QMP input", + }, + { + .error_fmt = QERR_QMP_BAD_INPUT_OBJECT_MEMBER, + .desc = "QMP input object member '%(member)' expects '%(expected)'", + }, + { + .error_fmt = QERR_QMP_EXTRA_MEMBER, + .desc = "QMP input object member '%(member)' is unexpected", }, { .error_fmt = QERR_SET_PASSWD_FAILED, @@ -117,6 +201,11 @@ .desc = "An undefined error has ocurred", }, { + .error_fmt = QERR_UNKNOWN_BLOCK_FORMAT_FEATURE, + .desc = "'%(device)' uses a %(format) feature which is not " + "supported by this qemu version: %(feature)", + }, + { .error_fmt = QERR_VNC_SERVER_FAILED, .desc = "Could not start VNC server on %(target)", }, @@ -138,7 +227,8 @@ return qerr; } -static void qerror_abort(const QError *qerr, const char *fmt, ...) +static void GCC_FMT_ATTR(2, 3) qerror_abort(const QError *qerr, + const char *fmt, ...) { va_list ap; @@ -153,7 +243,8 @@ abort(); } -static void qerror_set_data(QError *qerr, const char *fmt, va_list *va) +static void GCC_FMT_ATTR(2, 0) qerror_set_data(QError *qerr, + const char *fmt, va_list *va) { QObject *obj; @@ -220,6 +311,7 @@ QError *qerr; qerr = qerror_new(); + loc_save(&qerr->loc); qerr->linenr = linenr; qerr->file = file; qerr->func = func; @@ -314,16 +406,36 @@ * qerror_print(): Print QError data * * This function will print the member 'desc' of the specified QError object, - * it uses qemu_error() for this, so that the output is routed to the right + * it uses error_report() for this, so that the output is routed to the right * place (ie. stderr or Monitor's device). */ -void qerror_print(const QError *qerror) +void qerror_print(QError *qerror) { QString *qstring = qerror_human(qerror); - qemu_error("%s\n", qstring_get_str(qstring)); + loc_push_restore(&qerror->loc); + error_report("%s", qstring_get_str(qstring)); + loc_pop(&qerror->loc); QDECREF(qstring); } +void qerror_report_internal(const char *file, int linenr, const char *func, + const char *fmt, ...) +{ + va_list va; + QError *qerror; + + va_start(va, fmt); + qerror = qerror_from_info(file, linenr, func, fmt, &va); + va_end(va); + + if (monitor_cur_is_qmp()) { + monitor_set_error(cur_mon, qerror); + } else { + qerror_print(qerror); + QDECREF(qerror); + } +} + /** * qobject_to_qerror(): Convert a QObject into a QError */ diff -Nru qemu-kvm-0.12.5+noroms/qerror.h qemu-kvm-0.14.1/qerror.h --- qemu-kvm-0.12.5+noroms/qerror.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/qerror.h 2011-05-11 13:29:46.000000000 +0000 @@ -1,5 +1,5 @@ /* - * QError header file. + * QError Module * * Copyright (C) 2009 Red Hat Inc. * @@ -14,6 +14,7 @@ #include "qdict.h" #include "qstring.h" +#include "qemu-error.h" #include typedef struct QErrorStringTable { @@ -24,6 +25,7 @@ typedef struct QError { QObject_HEAD; QDict *error; + Location loc; int linenr; const char *file; const char *func; @@ -32,32 +34,68 @@ QError *qerror_new(void); QError *qerror_from_info(const char *file, int linenr, const char *func, - const char *fmt, va_list *va); + const char *fmt, va_list *va) GCC_FMT_ATTR(4, 0); QString *qerror_human(const QError *qerror); -void qerror_print(const QError *qerror); +void qerror_print(QError *qerror); +void qerror_report_internal(const char *file, int linenr, const char *func, + const char *fmt, ...) GCC_FMT_ATTR(4, 5); +#define qerror_report(fmt, ...) \ + qerror_report_internal(__FILE__, __LINE__, __func__, fmt, ## __VA_ARGS__) QError *qobject_to_qerror(const QObject *obj); /* * QError class list + * Please keep the definitions in alphabetical order. + * Use "grep '^#define QERR_' qerror.h | sort -c" to check. */ +#define QERR_BAD_BUS_FOR_DEVICE \ + "{ 'class': 'BadBusForDevice', 'data': { 'device': %s, 'bad_bus_type': %s } }" + +#define QERR_BUS_NOT_FOUND \ + "{ 'class': 'BusNotFound', 'data': { 'bus': %s } }" + +#define QERR_BUS_NO_HOTPLUG \ + "{ 'class': 'BusNoHotplug', 'data': { 'bus': %s } }" + #define QERR_COMMAND_NOT_FOUND \ "{ 'class': 'CommandNotFound', 'data': { 'name': %s } }" #define QERR_DEVICE_ENCRYPTED \ "{ 'class': 'DeviceEncrypted', 'data': { 'device': %s } }" -#define QERR_DEVICE_LOCKED \ +#define QERR_DEVICE_INIT_FAILED \ + "{ 'class': 'DeviceInitFailed', 'data': { 'device': %s } }" + +#define QERR_DEVICE_IN_USE \ + "{ 'class': 'DeviceInUse', 'data': { 'device': %s } }" + +#define QERR_DEVICE_LOCKED \ "{ 'class': 'DeviceLocked', 'data': { 'device': %s } }" +#define QERR_DEVICE_MULTIPLE_BUSSES \ + "{ 'class': 'DeviceMultipleBusses', 'data': { 'device': %s } }" + #define QERR_DEVICE_NOT_ACTIVE \ "{ 'class': 'DeviceNotActive', 'data': { 'device': %s } }" +#define QERR_DEVICE_NOT_ENCRYPTED \ + "{ 'class': 'DeviceNotEncrypted', 'data': { 'device': %s } }" + #define QERR_DEVICE_NOT_FOUND \ "{ 'class': 'DeviceNotFound', 'data': { 'device': %s } }" #define QERR_DEVICE_NOT_REMOVABLE \ "{ 'class': 'DeviceNotRemovable', 'data': { 'device': %s } }" +#define QERR_DEVICE_NO_BUS \ + "{ 'class': 'DeviceNoBus', 'data': { 'device': %s } }" + +#define QERR_DEVICE_NO_HOTPLUG \ + "{ 'class': 'DeviceNoHotplug', 'data': { 'device': %s } }" + +#define QERR_DUPLICATE_ID \ + "{ 'class': 'DuplicateId', 'data': { 'id': %s, 'object': %s } }" + #define QERR_FD_NOT_FOUND \ "{ 'class': 'FdNotFound', 'data': { 'name': %s } }" @@ -73,6 +111,9 @@ #define QERR_INVALID_PARAMETER_TYPE \ "{ 'class': 'InvalidParameterType', 'data': { 'name': %s,'expected': %s } }" +#define QERR_INVALID_PARAMETER_VALUE \ + "{ 'class': 'InvalidParameterValue', 'data': { 'name': %s, 'expected': %s } }" + #define QERR_INVALID_PASSWORD \ "{ 'class': 'InvalidPassword', 'data': {} }" @@ -82,20 +123,50 @@ #define QERR_KVM_MISSING_CAP \ "{ 'class': 'KVMMissingCap', 'data': { 'capability': %s, 'feature': %s } }" +#define QERR_MIGRATION_EXPECTED \ + "{ 'class': 'MigrationExpected', 'data': {} }" + #define QERR_MISSING_PARAMETER \ "{ 'class': 'MissingParameter', 'data': { 'name': %s } }" +#define QERR_NO_BUS_FOR_DEVICE \ + "{ 'class': 'NoBusForDevice', 'data': { 'device': %s, 'bus': %s } }" + +#define QERR_OPEN_FILE_FAILED \ + "{ 'class': 'OpenFileFailed', 'data': { 'filename': %s } }" + +#define QERR_PROPERTY_NOT_FOUND \ + "{ 'class': 'PropertyNotFound', 'data': { 'device': %s, 'property': %s } }" + +#define QERR_PROPERTY_VALUE_BAD \ + "{ 'class': 'PropertyValueBad', 'data': { 'device': %s, 'property': %s, 'value': %s } }" + +#define QERR_PROPERTY_VALUE_IN_USE \ + "{ 'class': 'PropertyValueInUse', 'data': { 'device': %s, 'property': %s, 'value': %s } }" + +#define QERR_PROPERTY_VALUE_NOT_FOUND \ + "{ 'class': 'PropertyValueNotFound', 'data': { 'device': %s, 'property': %s, 'value': %s } }" + #define QERR_QMP_BAD_INPUT_OBJECT \ "{ 'class': 'QMPBadInputObject', 'data': { 'expected': %s } }" +#define QERR_QMP_BAD_INPUT_OBJECT_MEMBER \ + "{ 'class': 'QMPBadInputObjectMember', 'data': { 'member': %s, 'expected': %s } }" + +#define QERR_QMP_EXTRA_MEMBER \ + "{ 'class': 'QMPExtraInputObjectMember', 'data': { 'member': %s } }" + #define QERR_SET_PASSWD_FAILED \ "{ 'class': 'SetPasswdFailed', 'data': {} }" +#define QERR_TOO_MANY_FILES \ + "{ 'class': 'TooManyFiles', 'data': {} }" + #define QERR_UNDEFINED_ERROR \ "{ 'class': 'UndefinedError', 'data': {} }" -#define QERR_TOO_MANY_FILES \ - "{ 'class': 'TooManyFiles', 'data': {} }" +#define QERR_UNKNOWN_BLOCK_FORMAT_FEATURE \ + "{ 'class': 'UnknownBlockFormatFeature', 'data': { 'device': %s, 'format': %s, 'feature': %s } }" #define QERR_VNC_SERVER_FAILED \ "{ 'class': 'VNCServerFailed', 'data': { 'target': %s } }" diff -Nru qemu-kvm-0.12.5+noroms/qfloat.c qemu-kvm-0.14.1/qfloat.c --- qemu-kvm-0.12.5+noroms/qfloat.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/qfloat.c 2011-05-11 13:29:46.000000000 +0000 @@ -1,14 +1,6 @@ /* * QFloat Module * - * Copyright (C) 2009 Red Hat Inc. - * - * Authors: - * Luiz Capitulino - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * * Copyright IBM, Corp. 2009 * * Authors: diff -Nru qemu-kvm-0.12.5+noroms/qint.c qemu-kvm-0.14.1/qint.c --- qemu-kvm-0.12.5+noroms/qint.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/qint.c 2011-05-11 13:29:46.000000000 +0000 @@ -1,14 +1,15 @@ /* - * QInt data type. + * QInt Module * * Copyright (C) 2009 Red Hat Inc. * * Authors: * Luiz Capitulino * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. */ + #include "qint.h" #include "qobject.h" #include "qemu-common.h" diff -Nru qemu-kvm-0.12.5+noroms/qint.h qemu-kvm-0.14.1/qint.h --- qemu-kvm-0.12.5+noroms/qint.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/qint.h 2011-05-11 13:29:46.000000000 +0000 @@ -1,3 +1,15 @@ +/* + * QInt Module + * + * Copyright (C) 2009 Red Hat Inc. + * + * Authors: + * Luiz Capitulino + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + */ + #ifndef QINT_H #define QINT_H diff -Nru qemu-kvm-0.12.5+noroms/qjson.c qemu-kvm-0.14.1/qjson.c --- qemu-kvm-0.12.5+noroms/qjson.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/qjson.c 2011-05-11 13:29:46.000000000 +0000 @@ -53,6 +53,10 @@ return qobject_from_jsonv(string, NULL); } +/* + * IMPORTANT: This function aborts on error, thus it must not + * be used with untrusted arguments. + */ QObject *qobject_from_jsonf(const char *string, ...) { QObject *obj; @@ -62,48 +66,63 @@ obj = qobject_from_jsonv(string, &ap); va_end(ap); + assert(obj != NULL); return obj; } typedef struct ToJsonIterState { + int indent; + int pretty; int count; QString *str; } ToJsonIterState; -static void to_json(const QObject *obj, QString *str); +static void to_json(const QObject *obj, QString *str, int pretty, int indent); static void to_json_dict_iter(const char *key, QObject *obj, void *opaque) { ToJsonIterState *s = opaque; QString *qkey; + int j; - if (s->count) { + if (s->count) qstring_append(s->str, ", "); + + if (s->pretty) { + qstring_append(s->str, "\n"); + for (j = 0 ; j < s->indent ; j++) + qstring_append(s->str, " "); } qkey = qstring_from_str(key); - to_json(QOBJECT(qkey), s->str); + to_json(QOBJECT(qkey), s->str, s->pretty, s->indent); QDECREF(qkey); qstring_append(s->str, ": "); - to_json(obj, s->str); + to_json(obj, s->str, s->pretty, s->indent); s->count++; } static void to_json_list_iter(QObject *obj, void *opaque) { ToJsonIterState *s = opaque; + int j; - if (s->count) { + if (s->count) qstring_append(s->str, ", "); + + if (s->pretty) { + qstring_append(s->str, "\n"); + for (j = 0 ; j < s->indent ; j++) + qstring_append(s->str, " "); } - to_json(obj, s->str); + to_json(obj, s->str, s->pretty, s->indent); s->count++; } -static void to_json(const QObject *obj, QString *str) +static void to_json(const QObject *obj, QString *str, int pretty, int indent) { switch (qobject_type(obj)) { case QTYPE_QINT: { @@ -153,6 +172,9 @@ case '\b': qstring_append(str, "\\b"); break; + case '\f': + qstring_append(str, "\\f"); + break; case '\n': qstring_append(str, "\\n"); break; @@ -163,8 +185,14 @@ qstring_append(str, "\\t"); break; default: { - char buf[2] = { ptr[0], 0 }; - qstring_append(str, buf); + if (ptr[0] <= 0x1F) { + char escape[7]; + snprintf(escape, sizeof(escape), "\\u%04X", ptr[0]); + qstring_append(str, escape); + } else { + char buf[2] = { ptr[0], 0 }; + qstring_append(str, buf); + } break; } } @@ -179,8 +207,16 @@ s.count = 0; s.str = str; + s.indent = indent + 1; + s.pretty = pretty; qstring_append(str, "{"); qdict_iter(val, to_json_dict_iter, &s); + if (pretty) { + int j; + qstring_append(str, "\n"); + for (j = 0 ; j < indent ; j++) + qstring_append(str, " "); + } qstring_append(str, "}"); break; } @@ -190,8 +226,16 @@ s.count = 0; s.str = str; + s.indent = indent + 1; + s.pretty = pretty; qstring_append(str, "["); qlist_iter(val, (void *)to_json_list_iter, &s); + if (pretty) { + int j; + qstring_append(str, "\n"); + for (j = 0 ; j < indent ; j++) + qstring_append(str, " "); + } qstring_append(str, "]"); break; } @@ -235,7 +279,16 @@ { QString *str = qstring_new(); - to_json(obj, str); + to_json(obj, str, 0, 0); + + return str; +} + +QString *qobject_to_json_pretty(const QObject *obj) +{ + QString *str = qstring_new(); + + to_json(obj, str, 1, 0); return str; } diff -Nru qemu-kvm-0.12.5+noroms/qjson.h qemu-kvm-0.14.1/qjson.h --- qemu-kvm-0.12.5+noroms/qjson.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/qjson.h 2011-05-11 13:29:46.000000000 +0000 @@ -18,11 +18,11 @@ #include "qobject.h" #include "qstring.h" -QObject *qobject_from_json(const char *string); -QObject *qobject_from_jsonf(const char *string, ...) - __attribute__((__format__ (__printf__, 1, 2))); -QObject *qobject_from_jsonv(const char *string, va_list *ap); +QObject *qobject_from_json(const char *string) GCC_FMT_ATTR(1, 0); +QObject *qobject_from_jsonf(const char *string, ...) GCC_FMT_ATTR(1, 2); +QObject *qobject_from_jsonv(const char *string, va_list *ap) GCC_FMT_ATTR(1, 0); QString *qobject_to_json(const QObject *obj); +QString *qobject_to_json_pretty(const QObject *obj); #endif /* QJSON_H */ diff -Nru qemu-kvm-0.12.5+noroms/qlist.c qemu-kvm-0.14.1/qlist.c --- qemu-kvm-0.12.5+noroms/qlist.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/qlist.c 2011-05-11 13:29:46.000000000 +0000 @@ -1,14 +1,15 @@ /* - * QList data type. + * QList Module * * Copyright (C) 2009 Red Hat Inc. * * Authors: * Luiz Capitulino * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. */ + #include "qlist.h" #include "qobject.h" #include "qemu-queue.h" diff -Nru qemu-kvm-0.12.5+noroms/qlist.h qemu-kvm-0.14.1/qlist.h --- qemu-kvm-0.12.5+noroms/qlist.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/qlist.h 2011-05-11 13:29:46.000000000 +0000 @@ -1,14 +1,15 @@ /* - * QList data type header. + * QList Module * * Copyright (C) 2009 Red Hat Inc. * * Authors: * Luiz Capitulino * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. */ + #ifndef QLIST_H #define QLIST_H @@ -29,6 +30,16 @@ #define qlist_append(qlist, obj) \ qlist_append_obj(qlist, QOBJECT(obj)) +#define QLIST_FOREACH_ENTRY(qlist, var) \ + for ((var) = ((qlist)->head.tqh_first); \ + (var); \ + (var) = ((var)->next.tqe_next)) + +static inline QObject *qlist_entry_obj(const QListEntry *entry) +{ + return entry->value; +} + QList *qlist_new(void); QList *qlist_copy(QList *src); void qlist_append_obj(QList *qlist, QObject *obj); diff -Nru qemu-kvm-0.12.5+noroms/QMP/qmp-events.txt qemu-kvm-0.14.1/QMP/qmp-events.txt --- qemu-kvm-0.12.5+noroms/QMP/qmp-events.txt 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/QMP/qmp-events.txt 2011-05-11 13:29:46.000000000 +0000 @@ -1,26 +1,266 @@ - QEMU Monitor Protocol: Events - ============================= + QEMU Monitor Protocol Events + ============================ -1 SHUTDOWN ------------ +BLOCK_IO_ERROR +-------------- -Description: Issued when the Virtual Machine is powered down. -Data: None. +Emitted when a disk I/O error occurs. + +Data: + +- "device": device name (json-string) +- "operation": I/O operation (json-string, "read" or "write") +- "action": action that has been taken, it's one of the following (json-string): + "ignore": error has been ignored + "report": error has been reported to the device + "stop": error caused VM to be stopped + +Example: + +{ "event": "BLOCK_IO_ERROR", + "data": { "device": "ide0-hd1", + "operation": "write", + "action": "stop" }, + "timestamp": { "seconds": 1265044230, "microseconds": 450486 } } -2 RESET -------- +Note: If action is "stop", a STOP event will eventually follow the +BLOCK_IO_ERROR event. + +RESET +----- + +Emitted when the Virtual Machine is reseted. -Description: Issued when the Virtual Machine is reseted. Data: None. -3 STOP +Example: + +{ "event": "RESET", + "timestamp": { "seconds": 1267041653, "microseconds": 9518 } } + +RESUME ------ -Description: Issued when the Virtual Machine is stopped. +Emitted when the Virtual Machine resumes execution. + Data: None. -4 DEBUG -------- +Example: + +{ "event": "RESUME", + "timestamp": { "seconds": 1271770767, "microseconds": 582542 } } + +RTC_CHANGE +---------- + +Emitted when the guest changes the RTC time. + +Data: + +- "offset": delta against the host UTC in seconds (json-number) + +Example: + +{ "event": "RTC_CHANGE", + "data": { "offset": 78 }, + "timestamp": { "seconds": 1267020223, "microseconds": 435656 } } + +SHUTDOWN +-------- + +Emitted when the Virtual Machine is powered down. -Description: Issued when the Virtual Machine enters debug mode. Data: None. + +Example: + +{ "event": "SHUTDOWN", + "timestamp": { "seconds": 1267040730, "microseconds": 682951 } } + +Note: If the command-line option "-no-shutdown" has been specified, a STOP +event will eventually follow the SHUTDOWN event. + +STOP +---- + +Emitted when the Virtual Machine is stopped. + +Data: None. + +Example: + +{ "event": "STOP", + "timestamp": { "seconds": 1267041730, "microseconds": 281295 } } + +VNC_CONNECTED +------------- + +Emitted when a VNC client establishes a connection. + +Data: + +- "server": Server information (json-object) + - "host": IP address (json-string) + - "service": port number (json-string) + - "family": address family (json-string, "ipv4" or "ipv6") + - "auth": authentication method (json-string, optional) +- "client": Client information (json-object) + - "host": IP address (json-string) + - "service": port number (json-string) + - "family": address family (json-string, "ipv4" or "ipv6") + +Example: + +{ "event": "VNC_CONNECTED", + "data": { + "server": { "auth": "sasl", "family": "ipv4", + "service": "5901", "host": "0.0.0.0" }, + "client": { "family": "ipv4", "service": "58425", + "host": "127.0.0.1" } }, + "timestamp": { "seconds": 1262976601, "microseconds": 975795 } } + + +Note: This event is emitted before any authentication takes place, thus +the authentication ID is not provided. + +VNC_DISCONNECTED +---------------- + +Emitted when the conection is closed. + +Data: + +- "server": Server information (json-object) + - "host": IP address (json-string) + - "service": port number (json-string) + - "family": address family (json-string, "ipv4" or "ipv6") + - "auth": authentication method (json-string, optional) +- "client": Client information (json-object) + - "host": IP address (json-string) + - "service": port number (json-string) + - "family": address family (json-string, "ipv4" or "ipv6") + - "x509_dname": TLS dname (json-string, optional) + - "sasl_username": SASL username (json-string, optional) + +Example: + +{ "event": "VNC_DISCONNECTED", + "data": { + "server": { "auth": "sasl", "family": "ipv4", + "service": "5901", "host": "0.0.0.0" }, + "client": { "family": "ipv4", "service": "58425", + "host": "127.0.0.1", "sasl_username": "luiz" } }, + "timestamp": { "seconds": 1262976601, "microseconds": 975795 } } + +VNC_INITIALIZED +--------------- + +Emitted after authentication takes place (if any) and the VNC session is +made active. + +Data: + +- "server": Server information (json-object) + - "host": IP address (json-string) + - "service": port number (json-string) + - "family": address family (json-string, "ipv4" or "ipv6") + - "auth": authentication method (json-string, optional) +- "client": Client information (json-object) + - "host": IP address (json-string) + - "service": port number (json-string) + - "family": address family (json-string, "ipv4" or "ipv6") + - "x509_dname": TLS dname (json-string, optional) + - "sasl_username": SASL username (json-string, optional) + +Example: + +{ "event": "VNC_INITIALIZED", + "data": { + "server": { "auth": "sasl", "family": "ipv4", + "service": "5901", "host": "0.0.0.0"}, + "client": { "family": "ipv4", "service": "46089", + "host": "127.0.0.1", "sasl_username": "luiz" } }, + "timestamp": { "seconds": 1263475302, "microseconds": 150772 } } + +SPICE_CONNECTED, SPICE_DISCONNECTED +----------------------------------- + +Emitted when a SPICE client connects or disconnects. + +Data: + +- "server": Server information (json-object) + - "host": IP address (json-string) + - "port": port number (json-string) + - "family": address family (json-string, "ipv4" or "ipv6") +- "client": Client information (json-object) + - "host": IP address (json-string) + - "port": port number (json-string) + - "family": address family (json-string, "ipv4" or "ipv6") + +Example: + +{ "timestamp": {"seconds": 1290688046, "microseconds": 388707}, + "event": "SPICE_CONNECTED", + "data": { + "server": { "port": "5920", "family": "ipv4", "host": "127.0.0.1"}, + "client": {"port": "52873", "family": "ipv4", "host": "127.0.0.1"} +}} + + +SPICE_INITIALIZED +----------------- + +Emitted after initial handshake and authentication takes place (if any) +and the SPICE channel is up'n'running + +Data: + +- "server": Server information (json-object) + - "host": IP address (json-string) + - "port": port number (json-string) + - "family": address family (json-string, "ipv4" or "ipv6") + - "auth": authentication method (json-string, optional) +- "client": Client information (json-object) + - "host": IP address (json-string) + - "port": port number (json-string) + - "family": address family (json-string, "ipv4" or "ipv6") + - "connection-id": spice connection id. All channels with the same id + belong to the same spice session (json-int) + - "channel-type": channel type. "1" is the main control channel, filter for + this one if you want track spice sessions only (json-int) + - "channel-id": channel id. Usually "0", might be different needed when + multiple channels of the same type exist, such as multiple + display channels in a multihead setup (json-int) + - "tls": whevener the channel is encrypted (json-bool) + +Example: + +{ "timestamp": {"seconds": 1290688046, "microseconds": 417172}, + "event": "SPICE_INITIALIZED", + "data": {"server": {"auth": "spice", "port": "5921", + "family": "ipv4", "host": "127.0.0.1"}, + "client": {"port": "49004", "family": "ipv4", "channel-type": 3, + "connection-id": 1804289383, "host": "127.0.0.1", + "channel-id": 0, "tls": true} +}} + + +WATCHDOG +-------- + +Emitted when the watchdog device's timer is expired. + +Data: + +- "action": Action that has been taken, it's one of the following (json-string): + "reset", "shutdown", "poweroff", "pause", "debug", or "none" + +Example: + +{ "event": "WATCHDOG", + "data": { "action": "reset" }, + "timestamp": { "seconds": 1267061043, "microseconds": 959568 } } + +Note: If action is "reset", "shutdown", or "pause" the WATCHDOG event is +followed respectively by the RESET, SHUTDOWN, or STOP events. diff -Nru qemu-kvm-0.12.5+noroms/QMP/qmp.py qemu-kvm-0.14.1/QMP/qmp.py --- qemu-kvm-0.12.5+noroms/QMP/qmp.py 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/QMP/qmp.py 2011-05-11 13:29:46.000000000 +0000 @@ -1,6 +1,6 @@ # QEMU Monitor Protocol Python class # -# Copyright (C) 2009 Red Hat Inc. +# Copyright (C) 2009, 2010 Red Hat Inc. # # Authors: # Luiz Capitulino @@ -8,7 +8,9 @@ # This work is licensed under the terms of the GNU GPL, version 2. See # the COPYING file in the top-level directory. -import socket, json +import json +import errno +import socket class QMPError(Exception): pass @@ -16,57 +18,114 @@ class QMPConnectError(QMPError): pass +class QMPCapabilitiesError(QMPError): + pass + class QEMUMonitorProtocol: - def connect(self): - self.sock.connect(self.filename) - data = self.__json_read() - if data == None: - raise QMPConnectError - if not data.has_key('QMP'): - raise QMPConnectError - return data['QMP']['capabilities'] + def __init__(self, address): + """ + Create a QEMUMonitorProtocol class. + + @param address: QEMU address, can be either a unix socket path (string) + or a tuple in the form ( address, port ) for a TCP + connection + @note No connection is established, this is done by the connect() method + """ + self.__events = [] + self.__address = address + self.__sock = self.__get_sock() + self.__sockfile = self.__sock.makefile() + + def __get_sock(self): + if isinstance(self.__address, tuple): + family = socket.AF_INET + else: + family = socket.AF_UNIX + return socket.socket(family, socket.SOCK_STREAM) - def close(self): - self.sock.close() + def __json_read(self): + while True: + data = self.__sockfile.readline() + if not data: + return + resp = json.loads(data) + if 'event' in resp: + self.__events.append(resp) + continue + return resp - def send_raw(self, line): - self.sock.send(str(line)) - return self.__json_read() + error = socket.error - def send(self, cmdline): - cmd = self.__build_cmd(cmdline) - self.__json_send(cmd) - resp = self.__json_read() - if resp == None: - return - elif resp.has_key('error'): - return resp['error'] - else: - return resp['return'] + def connect(self): + """ + Connect to the QMP Monitor and perform capabilities negotiation. - def __build_cmd(self, cmdline): - cmdargs = cmdline.split() - qmpcmd = { 'execute': cmdargs[0], 'arguments': {} } - for arg in cmdargs[1:]: - opt = arg.split('=') - try: - value = int(opt[1]) - except ValueError: - value = opt[1] - qmpcmd['arguments'][opt[0]] = value - return qmpcmd - - def __json_send(self, cmd): - # XXX: We have to send any additional char, otherwise - # the Server won't read our input - self.sock.send(json.dumps(cmd) + ' ') + @return QMP greeting dict + @raise socket.error on socket connection errors + @raise QMPConnectError if the greeting is not received + @raise QMPCapabilitiesError if fails to negotiate capabilities + """ + self.__sock.connect(self.__address) + greeting = self.__json_read() + if greeting is None or not greeting.has_key('QMP'): + raise QMPConnectError + # Greeting seems ok, negotiate capabilities + resp = self.cmd('qmp_capabilities') + if "return" in resp: + return greeting + raise QMPCapabilitiesError + + def cmd_obj(self, qmp_cmd): + """ + Send a QMP command to the QMP Monitor. + + @param qmp_cmd: QMP command to be sent as a Python dict + @return QMP response as a Python dict or None if the connection has + been closed + """ + try: + self.__sock.sendall(json.dumps(qmp_cmd)) + except socket.error, err: + if err[0] == errno.EPIPE: + return + raise socket.error(err) + return self.__json_read() - def __json_read(self): + def cmd(self, name, args=None, id=None): + """ + Build a QMP command and send it to the QMP Monitor. + + @param name: command name (string) + @param args: command arguments (dict) + @param id: command id (dict, list, string or int) + """ + qmp_cmd = { 'execute': name } + if args: + qmp_cmd['arguments'] = args + if id: + qmp_cmd['id'] = id + return self.cmd_obj(qmp_cmd) + + def get_events(self): + """ + Get a list of available QMP events. + """ + self.__sock.setblocking(0) try: - return json.loads(self.sock.recv(1024)) - except ValueError: - return - - def __init__(self, filename): - self.filename = filename - self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + self.__json_read() + except socket.error, err: + if err[0] == errno.EAGAIN: + # No data available + pass + self.__sock.setblocking(1) + return self.__events + + def clear_events(self): + """ + Clear current list of pending events. + """ + self.__events = [] + + def close(self): + self.__sock.close() + self.__sockfile.close() diff -Nru qemu-kvm-0.12.5+noroms/QMP/qmp-shell qemu-kvm-0.14.1/QMP/qmp-shell --- qemu-kvm-0.12.5+noroms/QMP/qmp-shell 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/QMP/qmp-shell 2011-05-11 13:29:46.000000000 +0000 @@ -1,8 +1,8 @@ #!/usr/bin/python # -# Simple QEMU shell on top of QMP +# Low-level QEMU shell on top of QMP. # -# Copyright (C) 2009 Red Hat Inc. +# Copyright (C) 2009, 2010 Red Hat Inc. # # Authors: # Luiz Capitulino @@ -14,59 +14,246 @@ # # Start QEMU with: # -# $ qemu [...] -monitor control,unix:./qmp,server +# # qemu [...] -qmp unix:./qmp-sock,server # # Run the shell: # -# $ qmp-shell ./qmp +# $ qmp-shell ./qmp-sock # # Commands have the following format: # -# < command-name > [ arg-name1=arg1 ] ... [ arg-nameN=argN ] +# < command-name > [ arg-name1=arg1 ] ... [ arg-nameN=argN ] # # For example: # -# (QEMU) info item=network +# (QEMU) device_add driver=e1000 id=net1 +# {u'return': {}} +# (QEMU) import qmp import readline -from sys import argv,exit +import sys -def shell_help(): - print 'bye exit from the shell' +class QMPCompleter(list): + def complete(self, text, state): + for cmd in self: + if cmd.startswith(text): + if not state: + return cmd + else: + state -= 1 -def main(): - if len(argv) != 2: - print 'qemu-shell ' - exit(1) +class QMPShellError(Exception): + pass + +class QMPShellBadPort(QMPShellError): + pass + +# TODO: QMPShell's interface is a bit ugly (eg. _fill_completion() and +# _execute_cmd()). Let's design a better one. +class QMPShell(qmp.QEMUMonitorProtocol): + def __init__(self, address): + qmp.QEMUMonitorProtocol.__init__(self, self.__get_address(address)) + self._greeting = None + self._completer = None + + def __get_address(self, arg): + """ + Figure out if the argument is in the port:host form, if it's not it's + probably a file path. + """ + addr = arg.split(':') + if len(addr) == 2: + try: + port = int(addr[1]) + except ValueError: + raise QMPShellBadPort + return ( addr[0], port ) + # socket path + return arg + + def _fill_completion(self): + for cmd in self.cmd('query-commands')['return']: + self._completer.append(cmd['name']) + + def __completer_setup(self): + self._completer = QMPCompleter() + self._fill_completion() + readline.set_completer(self._completer.complete) + readline.parse_and_bind("tab: complete") + # XXX: default delimiters conflict with some command names (eg. query-), + # clearing everything as it doesn't seem to matter + readline.set_completer_delims('') - qemu = qmp.QEMUMonitorProtocol(argv[1]) - qemu.connect() + def __build_cmd(self, cmdline): + """ + Build a QMP input object from a user provided command-line in the + following format: + + < command-name > [ arg-name1=arg1 ] ... [ arg-nameN=argN ] + """ + cmdargs = cmdline.split() + qmpcmd = { 'execute': cmdargs[0], 'arguments': {} } + for arg in cmdargs[1:]: + opt = arg.split('=') + try: + value = int(opt[1]) + except ValueError: + value = opt[1] + qmpcmd['arguments'][opt[0]] = value + return qmpcmd + + def _execute_cmd(self, cmdline): + try: + qmpcmd = self.__build_cmd(cmdline) + except: + print 'command format: ', + print '[arg-name1=arg1] ... [arg-nameN=argN]' + return True + resp = self.cmd_obj(qmpcmd) + if resp is None: + print 'Disconnected' + return False + print resp + return True + + def connect(self): + self._greeting = qmp.QEMUMonitorProtocol.connect(self) + self.__completer_setup() - print 'Connected!' + def show_banner(self, msg='Welcome to the QMP low-level shell!'): + print msg + version = self._greeting['QMP']['version']['qemu'] + print 'Connected to QEMU %d.%d.%d\n' % (version['major'],version['minor'],version['micro']) - while True: + def read_exec_command(self, prompt): + """ + Read and execute a command. + + @return True if execution was ok, return False if disconnected. + """ try: - cmd = raw_input('(QEMU) ') + cmdline = raw_input(prompt) except EOFError: print - break - if cmd == '': - continue - elif cmd == 'bye': - break - elif cmd == 'help': - shell_help() + return False + if cmdline == '': + for ev in self.get_events(): + print ev + self.clear_events() + return True else: + return self._execute_cmd(cmdline) + +class HMPShell(QMPShell): + def __init__(self, address): + QMPShell.__init__(self, address) + self.__cpu_index = 0 + + def __cmd_completion(self): + for cmd in self.__cmd_passthrough('help')['return'].split('\r\n'): + if cmd and cmd[0] != '[' and cmd[0] != '\t': + name = cmd.split()[0] # drop help text + if name == 'info': + continue + if name.find('|') != -1: + # Command in the form 'foobar|f' or 'f|foobar', take the + # full name + opt = name.split('|') + if len(opt[0]) == 1: + name = opt[1] + else: + name = opt[0] + self._completer.append(name) + self._completer.append('help ' + name) # help completion + + def __info_completion(self): + for cmd in self.__cmd_passthrough('info')['return'].split('\r\n'): + if cmd: + self._completer.append('info ' + cmd.split()[1]) + + def __other_completion(self): + # special cases + self._completer.append('help info') + + def _fill_completion(self): + self.__cmd_completion() + self.__info_completion() + self.__other_completion() + + def __cmd_passthrough(self, cmdline, cpu_index = 0): + return self.cmd_obj({ 'execute': 'human-monitor-command', 'arguments': + { 'command-line': cmdline, + 'cpu-index': cpu_index } }) + + def _execute_cmd(self, cmdline): + if cmdline.split()[0] == "cpu": + # trap the cpu command, it requires special setting try: - resp = qemu.send(cmd) - if resp == None: - print 'Disconnected' - break - print resp - except IndexError: - print '-> command format: ', - print '[arg-name1=arg1] ... [arg-nameN=argN]' + idx = int(cmdline.split()[1]) + if not 'return' in self.__cmd_passthrough('info version', idx): + print 'bad CPU index' + return True + self.__cpu_index = idx + except ValueError: + print 'cpu command takes an integer argument' + return True + resp = self.__cmd_passthrough(cmdline, self.__cpu_index) + if resp is None: + print 'Disconnected' + return False + assert 'return' in resp or 'error' in resp + if 'return' in resp: + # Success + if len(resp['return']) > 0: + print resp['return'], + else: + # Error + print '%s: %s' % (resp['error']['class'], resp['error']['desc']) + return True + + def show_banner(self): + QMPShell.show_banner(self, msg='Welcome to the HMP shell!') + +def die(msg): + sys.stderr.write('ERROR: %s\n' % msg) + sys.exit(1) + +def fail_cmdline(option=None): + if option: + sys.stderr.write('ERROR: bad command-line option \'%s\'\n' % option) + sys.stderr.write('qemu-shell [ -H ] < UNIX socket path> | < TCP address:port >\n') + sys.exit(1) + +def main(): + addr = '' + try: + if len(sys.argv) == 2: + qemu = QMPShell(sys.argv[1]) + addr = sys.argv[1] + elif len(sys.argv) == 3: + if sys.argv[1] != '-H': + fail_cmdline(sys.argv[1]) + qemu = HMPShell(sys.argv[2]) + addr = sys.argv[2] + else: + fail_cmdline() + except QMPShellBadPort: + die('bad port number in command-line') + + try: + qemu.connect() + except qmp.QMPConnectError: + die('Didn\'t get QMP greeting message') + except qmp.QMPCapabilitiesError: + die('Could not negotiate capabilities') + except qemu.error: + die('Could not connect to %s' % addr) + + qemu.show_banner() + while qemu.read_exec_command('(QEMU) '): + pass + qemu.close() if __name__ == '__main__': main() diff -Nru qemu-kvm-0.12.5+noroms/QMP/qmp-spec.txt qemu-kvm-0.14.1/QMP/qmp-spec.txt --- qemu-kvm-0.12.5+noroms/QMP/qmp-spec.txt 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/QMP/qmp-spec.txt 2011-05-11 13:29:46.000000000 +0000 @@ -44,14 +44,17 @@ Right when connected the Server will issue a greeting message, which signals that the connection has been successfully established and that the Server is -waiting for commands. +ready for capabilities negotiation (for more information refer to section +'4. Capabilities Negotiation'). The format is: -{ "QMP": { "capabilities": json-array } } +{ "QMP": { "version": json-object, "capabilities": json-array } } Where, +- The "version" member contains the Server's version information (the format + is the same of the 'query-version' command) - The "capabilities" member specify the availability of features beyond the baseline specification @@ -152,7 +155,7 @@ 3.1 Server greeting ------------------- -S: {"QMP": {"capabilities": []}} +S: {"QMP": {"version": {"qemu": "0.12.50", "package": ""}, "capabilities": []}} 3.2 Simple 'stop' execution --------------------------- @@ -179,25 +182,91 @@ S: {"timestamp": {"seconds": 1258551470, "microseconds": 802384}, "event": "POWERDOWN"} -4. Compatibility Considerations --------------------------------- +4. Capabilities Negotiation +---------------------------- -In order to achieve maximum compatibility between versions, Clients must not -assume any particular: +When a Client successfully establishes a connection, the Server is in +Capabilities Negotiation mode. + +In this mode only the 'qmp_capabilities' command is allowed to run, all +other commands will return the CommandNotFound error. Asynchronous messages +are not delivered either. + +Clients should use the 'qmp_capabilities' command to enable capabilities +advertised in the Server's greeting (section '2.2 Server Greeting') they +support. + +When the 'qmp_capabilities' command is issued, and if it does not return an +error, the Server enters in Command mode where capabilities changes take +effect, all commands (except 'qmp_capabilities') are allowed and asynchronous +messages are delivered. + +5 Compatibility Considerations +------------------------------ + +All protocol changes or new features which modify the protocol format in an +incompatible way are disabled by default and will be advertised by the +capabilities array (section '2.2 Server Greeting'). Thus, Clients can check +that array and enable the capabilities they support. + +Additionally, Clients must not assume any particular: - Size of json-objects or length of json-arrays - Order of json-object members or json-array elements - Amount of errors generated by a command, that is, new errors can be added to any existing command in newer versions of the Server -Additionally, Clients should always: - -- Check the capabilities json-array at connection time -- Check the availability of commands with 'query-commands' before issuing them - -5. Recommendations to Client implementors ------------------------------------------ +6. Downstream extension of QMP +------------------------------ -5.1 The Server should be always started in pause mode, thus the Client is - able to perform any setup procedure without the risk of race conditions - and related problems +We recommend that downstream consumers of QEMU do *not* modify QMP. +Management tools should be able to support both upstream and downstream +versions of QMP without special logic, and downstream extensions are +inherently at odds with that. + +However, we recognize that it is sometimes impossible for downstreams to +avoid modifying QMP. Both upstream and downstream need to take care to +preserve long-term compatibility and interoperability. + +To help with that, QMP reserves JSON object member names beginning with +'__' (double underscore) for downstream use ("downstream names"). This +means upstream will never use any downstream names for its commands, +arguments, errors, asynchronous events, and so forth. + +Any new names downstream wishes to add must begin with '__'. To +ensure compatibility with other downstreams, it is strongly +recommended that you prefix your downstram names with '__RFQDN_' where +RFQDN is a valid, reverse fully qualified domain name which you +control. For example, a qemu-kvm specific monitor command would be: + + (qemu) __org.linux-kvm_enable_irqchip + +Downstream must not change the server greeting (section 2.2) other than +to offer additional capabilities. But see below for why even that is +discouraged. + +Section '5 Compatibility Considerations' applies to downstream as well +as to upstream, obviously. It follows that downstream must behave +exactly like upstream for any input not containing members with +downstream names ("downstream members"), except it may add members +with downstream names to its output. + +Thus, a client should not be able to distinguish downstream from +upstream as long as it doesn't send input with downstream members, and +properly ignores any downstream members in the output it receives. + +Advice on downstream modifications: + +1. Introducing new commands is okay. If you want to extend an existing + command, consider introducing a new one with the new behaviour + instead. + +2. Introducing new asynchronous messages is okay. If you want to extend + an existing message, consider adding a new one instead. + +3. Introducing new errors for use in new commands is okay. Adding new + errors to existing commands counts as extension, so 1. applies. + +4. New capabilities are strongly discouraged. Capabilities are for + evolving the basic protocol, and multiple diverging basic protocol + dialects are most undesirable. diff -Nru qemu-kvm-0.12.5+noroms/QMP/README qemu-kvm-0.14.1/QMP/README --- qemu-kvm-0.12.5+noroms/QMP/README 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/QMP/README 2011-05-11 13:29:46.000000000 +0000 @@ -7,57 +7,82 @@ The QEMU Monitor Protocol (QMP) allows applications to communicate with QEMU's Monitor. -QMP is JSON[1] based and has the following features: +QMP is JSON[1] based and currently has the following features: - Lightweight, text-based, easy to parse data format -- Asynchronous events support -- Stability +- Asynchronous messages support (ie. events) +- Capabilities Negotiation -For more information, please, refer to the following files: +For detailed information on QMP's usage, please, refer to the following files: -o qmp-spec.txt QEMU Monitor Protocol current specification -o qmp-events.txt List of available asynchronous events +o qmp-spec.txt QEMU Monitor Protocol current specification +o qmp-commands.txt QMP supported commands (auto-generated at build-time) +o qmp-events.txt List of available asynchronous events -There are also two simple Python scripts available: +There is also a simple Python script called 'qmp-shell' available. + +IMPORTANT: It's strongly recommended to read the 'Stability Considerations' +section in the qmp-commands.txt file before making any serious use of QMP. -o qmp-shell A shell -o vm-info Show some information about the Virtual Machine [1] http://www.json.org Usage ----- -To enable QMP, QEMU has to be started in "control mode". There are -two ways of doing this, the simplest one is using the the '-qmp' -command-line option. +To enable QMP, you need a QEMU monitor instance in "control mode". There are +two ways of doing this. + +The simplest one is using the '-qmp' command-line option. The following +example makes QMP available on localhost port 4444: -For example: + $ qemu [...] -qmp tcp:localhost:4444,server -$ qemu [...] -qmp tcp:localhost:4444,server +However, in order to have more complex combinations, like multiple monitors, +the '-mon' command-line option should be used along with the '-chardev' one. +For instance, the following example creates one user monitor on stdio and one +QMP monitor on localhost port 4444. -Will start QEMU in control mode, waiting for a client TCP connection -on localhost port 4444. + $ qemu [...] -chardev stdio,id=mon0 -mon chardev=mon0,mode=readline \ + -chardev socket,id=mon1,host=localhost,port=4444,server \ + -mon chardev=mon1,mode=control -It is also possible to use the '-mon' command-line option to have -more complex combinations. Please, refer to the QEMU's manpage for -more information. +Please, refer to QEMU's manpage for more information. Simple Testing -------------- -To manually test QMP one can connect with telnet and issue commands: +To manually test QMP one can connect with telnet and issue commands by hand: $ telnet localhost 4444 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. -{"QMP": {"capabilities": []}} +{"QMP": {"version": {"qemu": {"micro": 50, "minor": 13, "major": 0}, "package": ""}, "capabilities": []}} +{ "execute": "qmp_capabilities" } +{"return": {}} { "execute": "query-version" } -{"return": {"qemu": "0.11.50", "package": ""}} +{"return": {"qemu": {"micro": 50, "minor": 13, "major": 0}, "package": ""}} + +Development Process +------------------- + +When changing QMP's interface (by adding new commands, events or modifying +existing ones) it's mandatory to update the relevant documentation, which is +one (or more) of the files listed in the 'Introduction' section*. + +Also, it's strongly recommended to send the documentation patch first, before +doing any code change. This is so because: + + 1. Avoids the code dictating the interface + + 2. Review can improve your interface. Letting that happen before + you implement it can save you work. + +* The qmp-commands.txt file is generated from the qmp-commands.hx one, which + is the file that should be edited. -Contact -------- +Homepage +-------- -http://www.linux-kvm.org/page/MonitorProtocol -Luiz Fernando N. Capitulino +http://wiki.qemu.org/QMP diff -Nru qemu-kvm-0.12.5+noroms/QMP/vm-info qemu-kvm-0.14.1/QMP/vm-info --- qemu-kvm-0.12.5+noroms/QMP/vm-info 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/QMP/vm-info 1970-01-01 00:00:00.000000000 +0000 @@ -1,32 +0,0 @@ -#!/usr/bin/python -# -# Print Virtual Machine information -# -# Usage: -# -# Start QEMU with: -# -# $ qemu [...] -monitor control,unix:./qmp,server -# -# Run vm-info: -# -# $ vm-info ./qmp -# -# Luiz Capitulino - -import qmp -from sys import argv,exit - -def main(): - if len(argv) != 2: - print 'vm-info ' - exit(1) - - qemu = qmp.QEMUMonitorProtocol(argv[1]) - qemu.connect() - - for cmd in [ 'version', 'hpet', 'kvm', 'status', 'uuid', 'balloon' ]: - print cmd + ': ' + str(qemu.send('query-' + cmd)) - -if __name__ == '__main__': - main() diff -Nru qemu-kvm-0.12.5+noroms/qmp-commands.hx qemu-kvm-0.14.1/qmp-commands.hx --- qemu-kvm-0.12.5+noroms/qmp-commands.hx 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/qmp-commands.hx 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,1776 @@ +HXCOMM QMP dispatch table and documentation +HXCOMM Text between SQMP and EQMP is copied to the QMP documention file and +HXCOMM does not show up in the other formats. + +SQMP + QMP Supported Commands + ---------------------- + +This document describes all commands currently supported by QMP. + +Most of the time their usage is exactly the same as in the user Monitor, this +means that any other document which also describe commands (the manpage, +QEMU's manual, etc) can and should be consulted. + +QMP has two types of commands: regular and query commands. Regular commands +usually change the Virtual Machine's state someway, while query commands just +return information. The sections below are divided accordingly. + +It's important to observe that all communication examples are formatted in +a reader-friendly way, so that they're easier to understand. However, in real +protocol usage, they're emitted as a single line. + +Also, the following notation is used to denote data flow: + +-> data issued by the Client +<- Server data response + +Please, refer to the QMP specification (QMP/qmp-spec.txt) for detailed +information on the Server command and response formats. + +NOTE: This document is temporary and will be replaced soon. + +1. Stability Considerations +=========================== + +The current QMP command set (described in this file) may be useful for a +number of use cases, however it's limited and several commands have bad +defined semantics, specially with regard to command completion. + +These problems are going to be solved incrementally in the next QEMU releases +and we're going to establish a deprecation policy for badly defined commands. + +If you're planning to adopt QMP, please observe the following: + + 1. The deprecation policy will take efect and be documented soon, please + check the documentation of each used command as soon as a new release of + QEMU is available + + 2. DO NOT rely on anything which is not explicit documented + + 3. Errors, in special, are not documented. Applications should NOT check + for specific errors classes or data (it's strongly recommended to only + check for the "error" key) + +2. Regular Commands +=================== + +Server's responses in the examples below are always a success response, please +refer to the QMP specification for more details on error responses. + +EQMP + + { + .name = "quit", + .args_type = "", + .params = "", + .help = "quit the emulator", + .user_print = monitor_user_noop, + .mhandler.cmd_new = do_quit, + }, + +SQMP +quit +---- + +Quit the emulator. + +Arguments: None. + +Example: + +-> { "execute": "quit" } +<- { "return": {} } + +EQMP + + { + .name = "eject", + .args_type = "force:-f,device:B", + .params = "[-f] device", + .help = "eject a removable medium (use -f to force it)", + .user_print = monitor_user_noop, + .mhandler.cmd_new = do_eject, + }, + +SQMP +eject +----- + +Eject a removable medium. + +Arguments: + +- force: force ejection (json-bool, optional) +- device: device name (json-string) + +Example: + +-> { "execute": "eject", "arguments": { "device": "ide1-cd0" } } +<- { "return": {} } + +Note: The "force" argument defaults to false. + +EQMP + + { + .name = "change", + .args_type = "device:B,target:F,arg:s?", + .params = "device filename [format]", + .help = "change a removable medium, optional format", + .user_print = monitor_user_noop, + .mhandler.cmd_new = do_change, + }, + +SQMP +change +------ + +Change a removable medium or VNC configuration. + +Arguments: + +- "device": device name (json-string) +- "target": filename or item (json-string) +- "arg": additional argument (json-string, optional) + +Examples: + +1. Change a removable medium + +-> { "execute": "change", + "arguments": { "device": "ide1-cd0", + "target": "/srv/images/Fedora-12-x86_64-DVD.iso" } } +<- { "return": {} } + +2. Change VNC password + +-> { "execute": "change", + "arguments": { "device": "vnc", "target": "password", + "arg": "foobar1" } } +<- { "return": {} } + +EQMP + + { + .name = "screendump", + .args_type = "filename:F", + .params = "filename", + .help = "save screen into PPM image 'filename'", + .user_print = monitor_user_noop, + .mhandler.cmd_new = do_screen_dump, + }, + +SQMP +screendump +---------- + +Save screen into PPM image. + +Arguments: + +- "filename": file path (json-string) + +Example: + +-> { "execute": "screendump", "arguments": { "filename": "/tmp/image" } } +<- { "return": {} } + +EQMP + + { + .name = "stop", + .args_type = "", + .params = "", + .help = "stop emulation", + .user_print = monitor_user_noop, + .mhandler.cmd_new = do_stop, + }, + +SQMP +stop +---- + +Stop the emulator. + +Arguments: None. + +Example: + +-> { "execute": "stop" } +<- { "return": {} } + +EQMP + + { + .name = "cont", + .args_type = "", + .params = "", + .help = "resume emulation", + .user_print = monitor_user_noop, + .mhandler.cmd_new = do_cont, + }, + +SQMP +cont +---- + +Resume emulation. + +Arguments: None. + +Example: + +-> { "execute": "cont" } +<- { "return": {} } + +EQMP + + { + .name = "system_reset", + .args_type = "", + .params = "", + .help = "reset the system", + .user_print = monitor_user_noop, + .mhandler.cmd_new = do_system_reset, + }, + +SQMP +system_reset +------------ + +Reset the system. + +Arguments: None. + +Example: + +-> { "execute": "system_reset" } +<- { "return": {} } + +EQMP + + { + .name = "system_powerdown", + .args_type = "", + .params = "", + .help = "send system power down event", + .user_print = monitor_user_noop, + .mhandler.cmd_new = do_system_powerdown, + }, + +SQMP +system_powerdown +---------------- + +Send system power down event. + +Arguments: None. + +Example: + +-> { "execute": "system_powerdown" } +<- { "return": {} } + +EQMP + + { + .name = "device_add", + .args_type = "device:O", + .params = "driver[,prop=value][,...]", + .help = "add device, like -device on the command line", + .user_print = monitor_user_noop, + .mhandler.cmd_new = do_device_add, + }, + +SQMP +device_add +---------- + +Add a device. + +Arguments: + +- "driver": the name of the new device's driver (json-string) +- "bus": the device's parent bus (device tree path, json-string, optional) +- "id": the device's ID, must be unique (json-string) +- device properties + +Example: + +-> { "execute": "device_add", "arguments": { "driver": "e1000", "id": "net1" } } +<- { "return": {} } + +Notes: + +(1) For detailed information about this command, please refer to the + 'docs/qdev-device-use.txt' file. + +(2) It's possible to list device properties by running QEMU with the + "-device DEVICE,\?" command-line argument, where DEVICE is the device's name + +EQMP + + { + .name = "device_del", + .args_type = "id:s", + .params = "device", + .help = "remove device", + .user_print = monitor_user_noop, + .mhandler.cmd_new = do_device_del, + }, + +SQMP +device_del +---------- + +Remove a device. + +Arguments: + +- "id": the device's ID (json-string) + +Example: + +-> { "execute": "device_del", "arguments": { "id": "net1" } } +<- { "return": {} } + +EQMP + + { + .name = "cpu", + .args_type = "index:i", + .params = "index", + .help = "set the default CPU", + .user_print = monitor_user_noop, + .mhandler.cmd_new = do_cpu_set, + }, + +SQMP +cpu +--- + +Set the default CPU. + +Arguments: + +- "index": the CPU's index (json-int) + +Example: + +-> { "execute": "cpu", "arguments": { "index": 0 } } +<- { "return": {} } + +Note: CPUs' indexes are obtained with the 'query-cpus' command. + +EQMP + + { + .name = "memsave", + .args_type = "val:l,size:i,filename:s", + .params = "addr size file", + .help = "save to disk virtual memory dump starting at 'addr' of size 'size'", + .user_print = monitor_user_noop, + .mhandler.cmd_new = do_memory_save, + }, + +SQMP +memsave +------- + +Save to disk virtual memory dump starting at 'val' of size 'size'. + +Arguments: + +- "val": the starting address (json-int) +- "size": the memory size, in bytes (json-int) +- "filename": file path (json-string) + +Example: + +-> { "execute": "memsave", + "arguments": { "val": 10, + "size": 100, + "filename": "/tmp/virtual-mem-dump" } } +<- { "return": {} } + +Note: Depends on the current CPU. + +EQMP + + { + .name = "pmemsave", + .args_type = "val:l,size:i,filename:s", + .params = "addr size file", + .help = "save to disk physical memory dump starting at 'addr' of size 'size'", + .user_print = monitor_user_noop, + .mhandler.cmd_new = do_physical_memory_save, + }, + +SQMP +pmemsave +-------- + +Save to disk physical memory dump starting at 'val' of size 'size'. + +Arguments: + +- "val": the starting address (json-int) +- "size": the memory size, in bytes (json-int) +- "filename": file path (json-string) + +Example: + +-> { "execute": "pmemsave", + "arguments": { "val": 10, + "size": 100, + "filename": "/tmp/physical-mem-dump" } } +<- { "return": {} } + +EQMP + + { + .name = "migrate", + .args_type = "detach:-d,blk:-b,inc:-i,uri:s", + .params = "[-d] [-b] [-i] uri", + .help = "migrate to URI (using -d to not wait for completion)" + "\n\t\t\t -b for migration without shared storage with" + " full copy of disk\n\t\t\t -i for migration without " + "shared storage with incremental copy of disk " + "(base image shared between src and destination)", + .user_print = monitor_user_noop, + .mhandler.cmd_new = do_migrate, + }, + +SQMP +migrate +------- + +Migrate to URI. + +Arguments: + +- "blk": block migration, full disk copy (json-bool, optional) +- "inc": incremental disk copy (json-bool, optional) +- "uri": Destination URI (json-string) + +Example: + +-> { "execute": "migrate", "arguments": { "uri": "tcp:0:4446" } } +<- { "return": {} } + +Notes: + +(1) The 'query-migrate' command should be used to check migration's progress + and final result (this information is provided by the 'status' member) +(2) All boolean arguments default to false +(3) The user Monitor's "detach" argument is invalid in QMP and should not + be used + +EQMP + + { + .name = "migrate_cancel", + .args_type = "", + .params = "", + .help = "cancel the current VM migration", + .user_print = monitor_user_noop, + .mhandler.cmd_new = do_migrate_cancel, + }, + +SQMP +migrate_cancel +-------------- + +Cancel the current migration. + +Arguments: None. + +Example: + +-> { "execute": "migrate_cancel" } +<- { "return": {} } + +EQMP + + { + .name = "migrate_set_speed", + .args_type = "value:o", + .params = "value", + .help = "set maximum speed (in bytes) for migrations", + .user_print = monitor_user_noop, + .mhandler.cmd_new = do_migrate_set_speed, + }, + +SQMP +client_migrate_info +------------------ + +Set the spice/vnc connection info for the migration target. The spice/vnc +server will ask the spice/vnc client to automatically reconnect using the +new parameters (if specified) once the vm migration finished successfully. + +Arguments: + +- "protocol": protocol: "spice" or "vnc" (json-string) +- "hostname": migration target hostname (json-string) +- "port": spice/vnc tcp port for plaintext channels (json-int, optional) +- "tls-port": spice tcp port for tls-secured channels (json-int, optional) +- "cert-subject": server certificate subject (json-string, optional) + +Example: + +-> { "execute": "client_migrate_info", + "arguments": { "protocol": "spice", + "hostname": "virt42.lab.kraxel.org", + "port": 1234 } } +<- { "return": {} } + +EQMP + + { + .name = "client_migrate_info", + .args_type = "protocol:s,hostname:s,port:i?,tls-port:i?,cert-subject:s?", + .params = "protocol hostname port tls-port cert-subject", + .help = "send migration info to spice/vnc client", + .user_print = monitor_user_noop, + .mhandler.cmd_new = client_migrate_info, + }, + +SQMP +migrate_set_speed +----------------- + +Set maximum speed for migrations. + +Arguments: + +- "value": maximum speed, in bytes per second (json-int) + +Example: + +-> { "execute": "migrate_set_speed", "arguments": { "value": 1024 } } +<- { "return": {} } + +EQMP + + { + .name = "migrate_set_downtime", + .args_type = "value:T", + .params = "value", + .help = "set maximum tolerated downtime (in seconds) for migrations", + .user_print = monitor_user_noop, + .mhandler.cmd_new = do_migrate_set_downtime, + }, + +SQMP +migrate_set_downtime +-------------------- + +Set maximum tolerated downtime (in seconds) for migrations. + +Arguments: + +- "value": maximum downtime (json-number) + +Example: + +-> { "execute": "migrate_set_downtime", "arguments": { "value": 0.1 } } +<- { "return": {} } + +EQMP + + { + .name = "netdev_add", + .args_type = "netdev:O", + .params = "[user|tap|socket],id=str[,prop=value][,...]", + .help = "add host network device", + .user_print = monitor_user_noop, + .mhandler.cmd_new = do_netdev_add, + }, + +SQMP +netdev_add +---------- + +Add host network device. + +Arguments: + +- "type": the device type, "tap", "user", ... (json-string) +- "id": the device's ID, must be unique (json-string) +- device options + +Example: + +-> { "execute": "netdev_add", "arguments": { "type": "user", "id": "netdev1" } } +<- { "return": {} } + +Note: The supported device options are the same ones supported by the '-net' + command-line argument, which are listed in the '-help' output or QEMU's + manual + +EQMP + + { + .name = "netdev_del", + .args_type = "id:s", + .params = "id", + .help = "remove host network device", + .user_print = monitor_user_noop, + .mhandler.cmd_new = do_netdev_del, + }, + +SQMP +netdev_del +---------- + +Remove host network device. + +Arguments: + +- "id": the device's ID, must be unique (json-string) + +Example: + +-> { "execute": "netdev_del", "arguments": { "id": "netdev1" } } +<- { "return": {} } + + +EQMP + + { + .name = "block_resize", + .args_type = "device:B,size:o", + .params = "device size", + .help = "resize a block image", + .user_print = monitor_user_noop, + .mhandler.cmd_new = do_block_resize, + }, + +SQMP +block_resize +------------ + +Resize a block image while a guest is running. + +Arguments: + +- "device": the device's ID, must be unique (json-string) +- "size": new size + +Example: + +-> { "execute": "block_resize", "arguments": { "device": "scratch", "size": 1073741824 } } +<- { "return": {} } + +EQMP + + { + .name = "balloon", + .args_type = "value:M", + .params = "target", + .help = "request VM to change its memory allocation (in MB)", + .user_print = monitor_user_noop, + .mhandler.cmd_async = do_balloon, + .flags = MONITOR_CMD_ASYNC, + }, + +SQMP +balloon +------- + +Request VM to change its memory allocation (in bytes). + +Arguments: + +- "value": New memory allocation (json-int) + +Example: + +-> { "execute": "balloon", "arguments": { "value": 536870912 } } +<- { "return": {} } + +EQMP + + { + .name = "set_link", + .args_type = "name:s,up:b", + .params = "name on|off", + .help = "change the link status of a network adapter", + .user_print = monitor_user_noop, + .mhandler.cmd_new = do_set_link, + }, + +SQMP +set_link +-------- + +Change the link status of a network adapter. + +Arguments: + +- "name": network device name (json-string) +- "up": status is up (json-bool) + +Example: + +-> { "execute": "set_link", "arguments": { "name": "e1000.0", "up": false } } +<- { "return": {} } + +EQMP + + { + .name = "getfd", + .args_type = "fdname:s", + .params = "getfd name", + .help = "receive a file descriptor via SCM rights and assign it a name", + .user_print = monitor_user_noop, + .mhandler.cmd_new = do_getfd, + }, + +SQMP +getfd +----- + +Receive a file descriptor via SCM rights and assign it a name. + +Arguments: + +- "fdname": file descriptor name (json-string) + +Example: + +-> { "execute": "getfd", "arguments": { "fdname": "fd1" } } +<- { "return": {} } + +EQMP + + { + .name = "closefd", + .args_type = "fdname:s", + .params = "closefd name", + .help = "close a file descriptor previously passed via SCM rights", + .user_print = monitor_user_noop, + .mhandler.cmd_new = do_closefd, + }, + +SQMP +closefd +------- + +Close a file descriptor previously passed via SCM rights. + +Arguments: + +- "fdname": file descriptor name (json-string) + +Example: + +-> { "execute": "closefd", "arguments": { "fdname": "fd1" } } +<- { "return": {} } + +EQMP + + { + .name = "block_passwd", + .args_type = "device:B,password:s", + .params = "block_passwd device password", + .help = "set the password of encrypted block devices", + .user_print = monitor_user_noop, + .mhandler.cmd_new = do_block_set_passwd, + }, + +SQMP +block_passwd +------------ + +Set the password of encrypted block devices. + +Arguments: + +- "device": device name (json-string) +- "password": password (json-string) + +Example: + +-> { "execute": "block_passwd", "arguments": { "device": "ide0-hd0", + "password": "12345" } } +<- { "return": {} } + +EQMP + + { + .name = "set_password", + .args_type = "protocol:s,password:s,connected:s?", + .params = "protocol password action-if-connected", + .help = "set spice/vnc password", + .user_print = monitor_user_noop, + .mhandler.cmd_new = set_password, + }, + +SQMP +set_password +------------ + +Set the password for vnc/spice protocols. + +Arguments: + +- "protocol": protocol name (json-string) +- "password": password (json-string) +- "connected": [ keep | disconnect | fail ] (josn-string, optional) + +Example: + +-> { "execute": "set_password", "arguments": { "protocol": "vnc", + "password": "secret" } } +<- { "return": {} } + +EQMP + + { + .name = "expire_password", + .args_type = "protocol:s,time:s", + .params = "protocol time", + .help = "set spice/vnc password expire-time", + .user_print = monitor_user_noop, + .mhandler.cmd_new = expire_password, + }, + +SQMP +expire_password +--------------- + +Set the password expire time for vnc/spice protocols. + +Arguments: + +- "protocol": protocol name (json-string) +- "time": [ now | never | +secs | secs ] (json-string) + +Example: + +-> { "execute": "expire_password", "arguments": { "protocol": "vnc", + "time": "+60" } } +<- { "return": {} } + +EQMP + + { + .name = "qmp_capabilities", + .args_type = "", + .params = "", + .help = "enable QMP capabilities", + .user_print = monitor_user_noop, + .mhandler.cmd_new = do_qmp_capabilities, + }, + +SQMP +qmp_capabilities +---------------- + +Enable QMP capabilities. + +Arguments: None. + +Example: + +-> { "execute": "qmp_capabilities" } +<- { "return": {} } + +Note: This command must be issued before issuing any other command. + +EQMP + + { + .name = "human-monitor-command", + .args_type = "command-line:s,cpu-index:i?", + .params = "", + .help = "", + .user_print = monitor_user_noop, + .mhandler.cmd_new = do_hmp_passthrough, + }, + +SQMP +human-monitor-command +--------------------- + +Execute a Human Monitor command. + +Arguments: + +- command-line: the command name and its arguments, just like the + Human Monitor's shell (json-string) +- cpu-index: select the CPU number to be used by commands which access CPU + data, like 'info registers'. The Monitor selects CPU 0 if this + argument is not provided (json-int, optional) + +Example: + +-> { "execute": "human-monitor-command", "arguments": { "command-line": "info kvm" } } +<- { "return": "kvm support: enabled\r\n" } + +Notes: + +(1) The Human Monitor is NOT an stable interface, this means that command + names, arguments and responses can change or be removed at ANY time. + Applications that rely on long term stability guarantees should NOT + use this command + +(2) Limitations: + + o This command is stateless, this means that commands that depend + on state information (such as getfd) might not work + + o Commands that prompt the user for data (eg. 'cont' when the block + device is encrypted) don't currently work + +3. Query Commands +================= + +HXCOMM Each query command below is inside a SQMP/EQMP section, do NOT change +HXCOMM this! We will possibly move query commands definitions inside those +HXCOMM sections, just like regular commands. + +EQMP + +SQMP +query-version +------------- + +Show QEMU version. + +Return a json-object with the following information: + +- "qemu": A json-object containing three integer values: + - "major": QEMU's major version (json-int) + - "minor": QEMU's minor version (json-int) + - "micro": QEMU's micro version (json-int) +- "package": package's version (json-string) + +Example: + +-> { "execute": "query-version" } +<- { + "return":{ + "qemu":{ + "major":0, + "minor":11, + "micro":5 + }, + "package":"" + } + } + +EQMP + +SQMP +query-commands +-------------- + +List QMP available commands. + +Each command is represented by a json-object, the returned value is a json-array +of all commands. + +Each json-object contain: + +- "name": command's name (json-string) + +Example: + +-> { "execute": "query-commands" } +<- { + "return":[ + { + "name":"query-balloon" + }, + { + "name":"system_powerdown" + } + ] + } + +Note: This example has been shortened as the real response is too long. + +EQMP + +SQMP +query-chardev +------------- + +Each device is represented by a json-object. The returned value is a json-array +of all devices. + +Each json-object contain the following: + +- "label": device's label (json-string) +- "filename": device's file (json-string) + +Example: + +-> { "execute": "query-chardev" } +<- { + "return":[ + { + "label":"monitor", + "filename":"stdio" + }, + { + "label":"serial0", + "filename":"vc" + } + ] + } + +EQMP + +SQMP +query-block +----------- + +Show the block devices. + +Each block device information is stored in a json-object and the returned value +is a json-array of all devices. + +Each json-object contain the following: + +- "device": device name (json-string) +- "type": device type (json-string) + - Possible values: "hd", "cdrom", "floppy", "unknown" +- "removable": true if the device is removable, false otherwise (json-bool) +- "locked": true if the device is locked, false otherwise (json-bool) +- "inserted": only present if the device is inserted, it is a json-object + containing the following: + - "file": device file name (json-string) + - "ro": true if read-only, false otherwise (json-bool) + - "drv": driver format name (json-string) + - Possible values: "blkdebug", "bochs", "cloop", "cow", "dmg", + "file", "file", "ftp", "ftps", "host_cdrom", + "host_device", "host_floppy", "http", "https", + "nbd", "parallels", "qcow", "qcow2", "raw", + "tftp", "vdi", "vmdk", "vpc", "vvfat" + - "backing_file": backing file name (json-string, optional) + - "encrypted": true if encrypted, false otherwise (json-bool) + +Example: + +-> { "execute": "query-block" } +<- { + "return":[ + { + "device":"ide0-hd0", + "locked":false, + "removable":false, + "inserted":{ + "ro":false, + "drv":"qcow2", + "encrypted":false, + "file":"disks/test.img" + }, + "type":"hd" + }, + { + "device":"ide1-cd0", + "locked":false, + "removable":true, + "type":"cdrom" + }, + { + "device":"floppy0", + "locked":false, + "removable":true, + "type": "floppy" + }, + { + "device":"sd0", + "locked":false, + "removable":true, + "type":"floppy" + } + ] + } + +EQMP + +SQMP +query-blockstats +---------------- + +Show block device statistics. + +Each device statistic information is stored in a json-object and the returned +value is a json-array of all devices. + +Each json-object contain the following: + +- "device": device name (json-string) +- "stats": A json-object with the statistics information, it contains: + - "rd_bytes": bytes read (json-int) + - "wr_bytes": bytes written (json-int) + - "rd_operations": read operations (json-int) + - "wr_operations": write operations (json-int) + - "wr_highest_offset": Highest offset of a sector written since the + BlockDriverState has been opened (json-int) +- "parent": Contains recursively the statistics of the underlying + protocol (e.g. the host file for a qcow2 image). If there is + no underlying protocol, this field is omitted + (json-object, optional) + +Example: + +-> { "execute": "query-blockstats" } +<- { + "return":[ + { + "device":"ide0-hd0", + "parent":{ + "stats":{ + "wr_highest_offset":3686448128, + "wr_bytes":9786368, + "wr_operations":751, + "rd_bytes":122567168, + "rd_operations":36772 + } + }, + "stats":{ + "wr_highest_offset":2821110784, + "wr_bytes":9786368, + "wr_operations":692, + "rd_bytes":122739200, + "rd_operations":36604 + } + }, + { + "device":"ide1-cd0", + "stats":{ + "wr_highest_offset":0, + "wr_bytes":0, + "wr_operations":0, + "rd_bytes":0, + "rd_operations":0 + } + }, + { + "device":"floppy0", + "stats":{ + "wr_highest_offset":0, + "wr_bytes":0, + "wr_operations":0, + "rd_bytes":0, + "rd_operations":0 + } + }, + { + "device":"sd0", + "stats":{ + "wr_highest_offset":0, + "wr_bytes":0, + "wr_operations":0, + "rd_bytes":0, + "rd_operations":0 + } + } + ] + } + +EQMP + +SQMP +query-cpus +---------- + +Show CPU information. + +Return a json-array. Each CPU is represented by a json-object, which contains: + +- "CPU": CPU index (json-int) +- "current": true if this is the current CPU, false otherwise (json-bool) +- "halted": true if the cpu is halted, false otherwise (json-bool) +- Current program counter. The key's name depends on the architecture: + "pc": i386/x86_64 (json-int) + "nip": PPC (json-int) + "pc" and "npc": sparc (json-int) + "PC": mips (json-int) + +Example: + +-> { "execute": "query-cpus" } +<- { + "return":[ + { + "CPU":0, + "current":true, + "halted":false, + "pc":3227107138 + }, + { + "CPU":1, + "current":false, + "halted":true, + "pc":7108165 + } + ] + } + +EQMP + +SQMP +query-pci +--------- + +PCI buses and devices information. + +The returned value is a json-array of all buses. Each bus is represented by +a json-object, which has a key with a json-array of all PCI devices attached +to it. Each device is represented by a json-object. + +The bus json-object contains the following: + +- "bus": bus number (json-int) +- "devices": a json-array of json-objects, each json-object represents a + PCI device + +The PCI device json-object contains the following: + +- "bus": identical to the parent's bus number (json-int) +- "slot": slot number (json-int) +- "function": function number (json-int) +- "class_info": a json-object containing: + - "desc": device class description (json-string, optional) + - "class": device class number (json-int) +- "id": a json-object containing: + - "device": device ID (json-int) + - "vendor": vendor ID (json-int) +- "irq": device's IRQ if assigned (json-int, optional) +- "qdev_id": qdev id string (json-string) +- "pci_bridge": It's a json-object, only present if this device is a + PCI bridge, contains: + - "bus": bus number (json-int) + - "secondary": secondary bus number (json-int) + - "subordinate": subordinate bus number (json-int) + - "io_range": I/O memory range information, a json-object with the + following members: + - "base": base address, in bytes (json-int) + - "limit": limit address, in bytes (json-int) + - "memory_range": memory range information, a json-object with the + following members: + - "base": base address, in bytes (json-int) + - "limit": limit address, in bytes (json-int) + - "prefetchable_range": Prefetchable memory range information, a + json-object with the following members: + - "base": base address, in bytes (json-int) + - "limit": limit address, in bytes (json-int) + - "devices": a json-array of PCI devices if there's any attached, each + each element is represented by a json-object, which contains + the same members of the 'PCI device json-object' described + above (optional) +- "regions": a json-array of json-objects, each json-object represents a + memory region of this device + +The memory range json-object contains the following: + +- "base": base memory address (json-int) +- "limit": limit value (json-int) + +The region json-object can be an I/O region or a memory region, an I/O region +json-object contains the following: + +- "type": "io" (json-string, fixed) +- "bar": BAR number (json-int) +- "address": memory address (json-int) +- "size": memory size (json-int) + +A memory region json-object contains the following: + +- "type": "memory" (json-string, fixed) +- "bar": BAR number (json-int) +- "address": memory address (json-int) +- "size": memory size (json-int) +- "mem_type_64": true or false (json-bool) +- "prefetch": true or false (json-bool) + +Example: + +-> { "execute": "query-pci" } +<- { + "return":[ + { + "bus":0, + "devices":[ + { + "bus":0, + "qdev_id":"", + "slot":0, + "class_info":{ + "class":1536, + "desc":"Host bridge" + }, + "id":{ + "device":32902, + "vendor":4663 + }, + "function":0, + "regions":[ + + ] + }, + { + "bus":0, + "qdev_id":"", + "slot":1, + "class_info":{ + "class":1537, + "desc":"ISA bridge" + }, + "id":{ + "device":32902, + "vendor":28672 + }, + "function":0, + "regions":[ + + ] + }, + { + "bus":0, + "qdev_id":"", + "slot":1, + "class_info":{ + "class":257, + "desc":"IDE controller" + }, + "id":{ + "device":32902, + "vendor":28688 + }, + "function":1, + "regions":[ + { + "bar":4, + "size":16, + "address":49152, + "type":"io" + } + ] + }, + { + "bus":0, + "qdev_id":"", + "slot":2, + "class_info":{ + "class":768, + "desc":"VGA controller" + }, + "id":{ + "device":4115, + "vendor":184 + }, + "function":0, + "regions":[ + { + "prefetch":true, + "mem_type_64":false, + "bar":0, + "size":33554432, + "address":4026531840, + "type":"memory" + }, + { + "prefetch":false, + "mem_type_64":false, + "bar":1, + "size":4096, + "address":4060086272, + "type":"memory" + }, + { + "prefetch":false, + "mem_type_64":false, + "bar":6, + "size":65536, + "address":-1, + "type":"memory" + } + ] + }, + { + "bus":0, + "qdev_id":"", + "irq":11, + "slot":4, + "class_info":{ + "class":1280, + "desc":"RAM controller" + }, + "id":{ + "device":6900, + "vendor":4098 + }, + "function":0, + "regions":[ + { + "bar":0, + "size":32, + "address":49280, + "type":"io" + } + ] + } + ] + } + ] + } + +Note: This example has been shortened as the real response is too long. + +EQMP + +SQMP +query-kvm +--------- + +Show KVM information. + +Return a json-object with the following information: + +- "enabled": true if KVM support is enabled, false otherwise (json-bool) +- "present": true if QEMU has KVM support, false otherwise (json-bool) + +Example: + +-> { "execute": "query-kvm" } +<- { "return": { "enabled": true, "present": true } } + +EQMP + +SQMP +query-status +------------ + +Return a json-object with the following information: + +- "running": true if the VM is running, or false if it is paused (json-bool) +- "singlestep": true if the VM is in single step mode, + false otherwise (json-bool) + +Example: + +-> { "execute": "query-status" } +<- { "return": { "running": true, "singlestep": false } } + +EQMP + +SQMP +query-mice +---------- + +Show VM mice information. + +Each mouse is represented by a json-object, the returned value is a json-array +of all mice. + +The mouse json-object contains the following: + +- "name": mouse's name (json-string) +- "index": mouse's index (json-int) +- "current": true if this mouse is receiving events, false otherwise (json-bool) +- "absolute": true if the mouse generates absolute input events (json-bool) + +Example: + +-> { "execute": "query-mice" } +<- { + "return":[ + { + "name":"QEMU Microsoft Mouse", + "index":0, + "current":false, + "absolute":false + }, + { + "name":"QEMU PS/2 Mouse", + "index":1, + "current":true, + "absolute":true + } + ] + } + +EQMP + +SQMP +query-vnc +--------- + +Show VNC server information. + +Return a json-object with server information. Connected clients are returned +as a json-array of json-objects. + +The main json-object contains the following: + +- "enabled": true or false (json-bool) +- "host": server's IP address (json-string) +- "family": address family (json-string) + - Possible values: "ipv4", "ipv6", "unix", "unknown" +- "service": server's port number (json-string) +- "auth": authentication method (json-string) + - Possible values: "invalid", "none", "ra2", "ra2ne", "sasl", "tight", + "tls", "ultra", "unknown", "vencrypt", "vencrypt", + "vencrypt+plain", "vencrypt+tls+none", + "vencrypt+tls+plain", "vencrypt+tls+sasl", + "vencrypt+tls+vnc", "vencrypt+x509+none", + "vencrypt+x509+plain", "vencrypt+x509+sasl", + "vencrypt+x509+vnc", "vnc" +- "clients": a json-array of all connected clients + +Clients are described by a json-object, each one contain the following: + +- "host": client's IP address (json-string) +- "family": address family (json-string) + - Possible values: "ipv4", "ipv6", "unix", "unknown" +- "service": client's port number (json-string) +- "x509_dname": TLS dname (json-string, optional) +- "sasl_username": SASL username (json-string, optional) + +Example: + +-> { "execute": "query-vnc" } +<- { + "return":{ + "enabled":true, + "host":"0.0.0.0", + "service":"50402", + "auth":"vnc", + "family":"ipv4", + "clients":[ + { + "host":"127.0.0.1", + "service":"50401", + "family":"ipv4" + } + ] + } + } + +EQMP + +SQMP +query-spice +----------- + +Show SPICE server information. + +Return a json-object with server information. Connected clients are returned +as a json-array of json-objects. + +The main json-object contains the following: + +- "enabled": true or false (json-bool) +- "host": server's IP address (json-string) +- "port": server's port number (json-int, optional) +- "tls-port": server's port number (json-int, optional) +- "auth": authentication method (json-string) + - Possible values: "none", "spice" +- "channels": a json-array of all active channels clients + +Channels are described by a json-object, each one contain the following: + +- "host": client's IP address (json-string) +- "family": address family (json-string) + - Possible values: "ipv4", "ipv6", "unix", "unknown" +- "port": client's port number (json-string) +- "connection-id": spice connection id. All channels with the same id + belong to the same spice session (json-int) +- "channel-type": channel type. "1" is the main control channel, filter for + this one if you want track spice sessions only (json-int) +- "channel-id": channel id. Usually "0", might be different needed when + multiple channels of the same type exist, such as multiple + display channels in a multihead setup (json-int) +- "tls": whevener the channel is encrypted (json-bool) + +Example: + +-> { "execute": "query-spice" } +<- { + "return": { + "enabled": true, + "auth": "spice", + "port": 5920, + "tls-port": 5921, + "host": "0.0.0.0", + "channels": [ + { + "port": "54924", + "family": "ipv4", + "channel-type": 1, + "connection-id": 1804289383, + "host": "127.0.0.1", + "channel-id": 0, + "tls": true + }, + { + "port": "36710", + "family": "ipv4", + "channel-type": 4, + "connection-id": 1804289383, + "host": "127.0.0.1", + "channel-id": 0, + "tls": false + }, + [ ... more channels follow ... ] + ] + } + } + +EQMP + +SQMP +query-name +---------- + +Show VM name. + +Return a json-object with the following information: + +- "name": VM's name (json-string, optional) + +Example: + +-> { "execute": "query-name" } +<- { "return": { "name": "qemu-name" } } + +EQMP + +SQMP +query-uuid +---------- + +Show VM UUID. + +Return a json-object with the following information: + +- "UUID": Universally Unique Identifier (json-string) + +Example: + +-> { "execute": "query-uuid" } +<- { "return": { "UUID": "550e8400-e29b-41d4-a716-446655440000" } } + +EQMP + +SQMP +query-migrate +------------- + +Migration status. + +Return a json-object. If migration is active there will be another json-object +with RAM migration status and if block migration is active another one with +block migration status. + +The main json-object contains the following: + +- "status": migration status (json-string) + - Possible values: "active", "completed", "failed", "cancelled" +- "ram": only present if "status" is "active", it is a json-object with the + following RAM information (in bytes): + - "transferred": amount transferred (json-int) + - "remaining": amount remaining (json-int) + - "total": total (json-int) +- "disk": only present if "status" is "active" and it is a block migration, + it is a json-object with the following disk information (in bytes): + - "transferred": amount transferred (json-int) + - "remaining": amount remaining (json-int) + - "total": total (json-int) + +Examples: + +1. Before the first migration + +-> { "execute": "query-migrate" } +<- { "return": {} } + +2. Migration is done and has succeeded + +-> { "execute": "query-migrate" } +<- { "return": { "status": "completed" } } + +3. Migration is done and has failed + +-> { "execute": "query-migrate" } +<- { "return": { "status": "failed" } } + +4. Migration is being performed and is not a block migration: + +-> { "execute": "query-migrate" } +<- { + "return":{ + "status":"active", + "ram":{ + "transferred":123, + "remaining":123, + "total":246 + } + } + } + +5. Migration is being performed and is a block migration: + +-> { "execute": "query-migrate" } +<- { + "return":{ + "status":"active", + "ram":{ + "total":1057024, + "remaining":1053304, + "transferred":3720 + }, + "disk":{ + "total":20971520, + "remaining":20880384, + "transferred":91136 + } + } + } + +EQMP + +SQMP +query-balloon +------------- + +Show balloon information. + +Make an asynchronous request for balloon info. When the request completes a +json-object will be returned containing the following data: + +- "actual": current balloon value in bytes (json-int) +- "mem_swapped_in": Amount of memory swapped in bytes (json-int, optional) +- "mem_swapped_out": Amount of memory swapped out in bytes (json-int, optional) +- "major_page_faults": Number of major faults (json-int, optional) +- "minor_page_faults": Number of minor faults (json-int, optional) +- "free_mem": Total amount of free and unused memory in + bytes (json-int, optional) +- "total_mem": Total amount of available memory in bytes (json-int, optional) + +Example: + +-> { "execute": "query-balloon" } +<- { + "return":{ + "actual":1073741824, + "mem_swapped_in":0, + "mem_swapped_out":0, + "major_page_faults":142, + "minor_page_faults":239245, + "free_mem":1014185984, + "total_mem":1044668416 + } + } + +EQMP + diff -Nru qemu-kvm-0.12.5+noroms/qobject.h qemu-kvm-0.14.1/qobject.h --- qemu-kvm-0.12.5+noroms/qobject.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/qobject.h 2011-05-11 13:29:46.000000000 +0000 @@ -8,8 +8,8 @@ * Authors: * Luiz Capitulino * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. * * QObject Reference Counts Terminology * ------------------------------------ diff -Nru qemu-kvm-0.12.5+noroms/qstring.c qemu-kvm-0.14.1/qstring.c --- qemu-kvm-0.12.5+noroms/qstring.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/qstring.c 2011-05-11 13:29:46.000000000 +0000 @@ -1,14 +1,15 @@ /* - * QString data type. + * QString Module * * Copyright (C) 2009 Red Hat Inc. * * Authors: * Luiz Capitulino * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. */ + #include "qobject.h" #include "qstring.h" #include "qemu-common.h" diff -Nru qemu-kvm-0.12.5+noroms/qstring.h qemu-kvm-0.14.1/qstring.h --- qemu-kvm-0.12.5+noroms/qstring.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/qstring.h 2011-05-11 13:29:46.000000000 +0000 @@ -1,3 +1,15 @@ +/* + * QString Module + * + * Copyright (C) 2009 Red Hat Inc. + * + * Authors: + * Luiz Capitulino + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + */ + #ifndef QSTRING_H #define QSTRING_H diff -Nru qemu-kvm-0.12.5+noroms/range.h qemu-kvm-0.14.1/range.h --- qemu-kvm-0.12.5+noroms/range.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/range.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,29 @@ +#ifndef QEMU_RANGE_H +#define QEMU_RANGE_H + +/* Get last byte of a range from offset + length. + * Undefined for ranges that wrap around 0. */ +static inline uint64_t range_get_last(uint64_t offset, uint64_t len) +{ + return offset + len - 1; +} + +/* Check whether a given range covers a given byte. */ +static inline int range_covers_byte(uint64_t offset, uint64_t len, + uint64_t byte) +{ + return offset <= byte && byte <= range_get_last(offset, len); +} + +/* Check whether 2 given ranges overlap. + * Undefined if ranges that wrap around 0. */ +static inline int ranges_overlap(uint64_t first1, uint64_t len1, + uint64_t first2, uint64_t len2) +{ + uint64_t last1 = range_get_last(first1, len1); + uint64_t last2 = range_get_last(first2, len2); + + return !(last2 < first1 || last1 < first2); +} + +#endif diff -Nru qemu-kvm-0.12.5+noroms/readline.c qemu-kvm-0.14.1/readline.c --- qemu-kvm-0.12.5+noroms/readline.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/readline.c 2011-05-11 13:29:46.000000000 +0000 @@ -28,6 +28,7 @@ #define IS_ESC 1 #define IS_CSI 2 +#undef printf #define printf do_not_use_printf void readline_show_prompt(ReadLineState *rs) diff -Nru qemu-kvm-0.12.5+noroms/rules.mak qemu-kvm-0.14.1/rules.mak --- qemu-kvm-0.12.5+noroms/rules.mak 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/rules.mak 2011-05-11 13:29:46.000000000 +0000 @@ -12,18 +12,18 @@ %.mak: # Flags for dependency generation -QEMU_DGFLAGS += -MMD -MP -MT $@ +QEMU_DGFLAGS += -MMD -MP -MT $@ -MF $(*D)/$(*F).d -%.o: %.c $(GENERATED_HEADERS) - $(call quiet-command,$(CC) $(QEMU_CFLAGS) $(QEMU_DGFLAGS) $(CFLAGS) -c -o $@ $<," CC $(TARGET_DIR)$@") +%.o: %.c + $(call quiet-command,$(CC) $(QEMU_CFLAGS) $(QEMU_INCLUDES) $(QEMU_DGFLAGS) $(CFLAGS) -c -o $@ $<," CC $(TARGET_DIR)$@") %.o: %.S - $(call quiet-command,$(CC) $(QEMU_CFLAGS) $(QEMU_DGFLAGS) $(CFLAGS) -c -o $@ $<," AS $(TARGET_DIR)$@") + $(call quiet-command,$(CC) $(QEMU_CFLAGS) $(QEMU_INCLUDES) $(QEMU_DGFLAGS) $(CFLAGS) -c -o $@ $<," AS $(TARGET_DIR)$@") %.o: %.m - $(call quiet-command,$(CC) $(QEMU_CFLAGS) $(QEMU_DGFLAGS) $(CFLAGS) -c -o $@ $<," OBJC $(TARGET_DIR)$@") + $(call quiet-command,$(CC) $(QEMU_CFLAGS) $(QEMU_INCLUDES) $(QEMU_DGFLAGS) $(CFLAGS) -c -o $@ $<," OBJC $(TARGET_DIR)$@") -LINK = $(call quiet-command,$(CC) $(QEMU_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ $(1) $(ARLIBS_BEGIN) $(ARLIBS) $(ARLIBS_END) $(LIBS)," LINK $(TARGET_DIR)$@") +LINK = $(call quiet-command,$(CC) $(QEMU_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ $(1) $(LIBS)," LINK $(TARGET_DIR)$@") %$(EXESUF): %.o $(call LINK,$^) @@ -39,13 +39,25 @@ cc-option = $(if $(shell $(CC) $1 $2 -S -o /dev/null -xc /dev/null \ >/dev/null 2>&1 && echo OK), $2, $3) +VPATH_SUFFIXES = %.c %.h %.S %.m %.mak %.texi +set-vpath = $(if $1,$(foreach PATTERN,$(VPATH_SUFFIXES),$(eval vpath $(PATTERN) $1))) + +# find-in-path +# Usage: $(call find-in-path, prog) +# Looks in the PATH if the argument contains no slash, else only considers one +# specific directory. Returns an # empty string if the program doesn't exist +# there. +find-in-path = $(if $(find-string /, $1), \ + $(wildcard $1), \ + $(wildcard $(patsubst %, %/$1, $(subst :, ,$(PATH))))) + # Generate timestamp files for .h include files %.h: %.h-timestamp @test -f $@ || cp $< $@ %.h-timestamp: %.mak - $(call quiet-command, sh $(SRC_PATH)/create_config < $< > $@, " GEN $*.h") + $(call quiet-command, sh $(SRC_PATH)/scripts/create_config < $< > $@, " GEN $*.h") @cmp $@ $*.h >/dev/null 2>&1 || cp $@ $*.h # will delete the target of a rule if commands exit with a nonzero exit status diff -Nru qemu-kvm-0.12.5+noroms/rwhandler.c qemu-kvm-0.14.1/rwhandler.c --- qemu-kvm-0.12.5+noroms/rwhandler.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/rwhandler.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,87 @@ +#include "rwhandler.h" +#include "ioport.h" +#include "cpu-all.h" + +#define RWHANDLER_WRITE(name, len, type) \ +static void name(void *opaque, type addr, uint32_t value) \ +{\ + struct ReadWriteHandler *handler = opaque;\ + handler->write(handler, addr, value, len);\ +} + +#define RWHANDLER_READ(name, len, type) \ +static uint32_t name(void *opaque, type addr) \ +{ \ + struct ReadWriteHandler *handler = opaque; \ + return handler->read(handler, addr, len); \ +} + +RWHANDLER_WRITE(cpu_io_memory_simple_writeb, 1, target_phys_addr_t); +RWHANDLER_READ(cpu_io_memory_simple_readb, 1, target_phys_addr_t); +RWHANDLER_WRITE(cpu_io_memory_simple_writew, 2, target_phys_addr_t); +RWHANDLER_READ(cpu_io_memory_simple_readw, 2, target_phys_addr_t); +RWHANDLER_WRITE(cpu_io_memory_simple_writel, 4, target_phys_addr_t); +RWHANDLER_READ(cpu_io_memory_simple_readl, 4, target_phys_addr_t); + +static CPUWriteMemoryFunc * const cpu_io_memory_simple_write[] = { + &cpu_io_memory_simple_writeb, + &cpu_io_memory_simple_writew, + &cpu_io_memory_simple_writel, +}; + +static CPUReadMemoryFunc * const cpu_io_memory_simple_read[] = { + &cpu_io_memory_simple_readb, + &cpu_io_memory_simple_readw, + &cpu_io_memory_simple_readl, +}; + +int cpu_register_io_memory_simple(struct ReadWriteHandler *handler, int endian) +{ + if (!handler->read || !handler->write) { + return -1; + } + return cpu_register_io_memory(cpu_io_memory_simple_read, + cpu_io_memory_simple_write, + handler, endian); +} + +RWHANDLER_WRITE(ioport_simple_writeb, 1, uint32_t); +RWHANDLER_READ(ioport_simple_readb, 1, uint32_t); +RWHANDLER_WRITE(ioport_simple_writew, 2, uint32_t); +RWHANDLER_READ(ioport_simple_readw, 2, uint32_t); +RWHANDLER_WRITE(ioport_simple_writel, 4, uint32_t); +RWHANDLER_READ(ioport_simple_readl, 4, uint32_t); + +int register_ioport_simple(ReadWriteHandler* handler, + pio_addr_t start, int length, int size) +{ + IOPortWriteFunc *write; + IOPortReadFunc *read; + int r; + switch (size) { + case 1: + write = ioport_simple_writeb; + read = ioport_simple_readb; + break; + case 2: + write = ioport_simple_writew; + read = ioport_simple_readw; + break; + default: + write = ioport_simple_writel; + read = ioport_simple_readl; + } + if (handler->write) { + r = register_ioport_write(start, length, size, write, handler); + if (r < 0) { + return r; + } + } + if (handler->read) { + r = register_ioport_read(start, length, size, read, handler); + if (r < 0) { + return r; + } + } + return 0; +} diff -Nru qemu-kvm-0.12.5+noroms/rwhandler.h qemu-kvm-0.14.1/rwhandler.h --- qemu-kvm-0.12.5+noroms/rwhandler.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/rwhandler.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,27 @@ +#ifndef READ_WRITE_HANDLER_H +#define READ_WRITE_HANDLER_H + +#include "qemu-common.h" +#include "ioport.h" + +typedef struct ReadWriteHandler ReadWriteHandler; + +/* len is guaranteed to be one of 1, 2 or 4, addr is guaranteed to fit in an + * appropriate type (io/memory/etc). They do not need to be range checked. */ +typedef void WriteHandlerFunc(ReadWriteHandler *, pcibus_t addr, + uint32_t value, int len); +typedef uint32_t ReadHandlerFunc(ReadWriteHandler *, pcibus_t addr, int len); + +struct ReadWriteHandler { + WriteHandlerFunc *write; + ReadHandlerFunc *read; +}; + +/* Helpers for when we want to use a single routine with length. */ +/* CPU memory handler: both read and write must be present. */ +int cpu_register_io_memory_simple(ReadWriteHandler *, int endian); +/* io port handler: can supply only read or write handlers. */ +int register_ioport_simple(ReadWriteHandler *, + pio_addr_t start, int length, int size); + +#endif diff -Nru qemu-kvm-0.12.5+noroms/s390-dis.c qemu-kvm-0.14.1/s390-dis.c --- qemu-kvm-0.12.5+noroms/s390-dis.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/s390-dis.c 2011-05-11 13:29:46.000000000 +0000 @@ -1,3 +1,4 @@ +/* opcodes/s390-dis.c revision 1.12 */ /* s390-dis.c -- Disassemble S390 instructions Copyright 2000, 2001, 2002, 2003, 2005 Free Software Foundation, Inc. Contributed by Martin Schwidefsky (schwidefsky@de.ibm.com). @@ -15,11 +16,14 @@ 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, see . */ + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ -#include +#include "qemu-common.h" #include "dis-asm.h" +/* include/opcode/s390.h revision 1.9 */ /* s390.h -- Header file for S390 opcode table Copyright 2000, 2001, 2003 Free Software Foundation, Inc. Contributed by Martin Schwidefsky (schwidefsky@de.ibm.com). @@ -37,7 +41,9 @@ 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, see . */ + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ #ifndef S390_H #define S390_H @@ -57,7 +63,8 @@ S390_OPCODE_Z900, S390_OPCODE_Z990, S390_OPCODE_Z9_109, - S390_OPCODE_Z9_EC + S390_OPCODE_Z9_EC, + S390_OPCODE_Z10 }; /* The opcode table is an array of struct s390_opcode. */ @@ -95,12 +102,13 @@ /* The table itself is sorted by major opcode number, and is otherwise in the order in which the disassembler should consider instructions. */ -extern const struct s390_opcode s390_opcodes[]; -extern const int s390_num_opcodes; +/* QEMU: Mark these static. */ +static const struct s390_opcode s390_opcodes[]; +static const int s390_num_opcodes; /* A opcode format table for the .insn pseudo mnemonic. */ -extern const struct s390_opcode s390_opformats[]; -extern const int s390_num_opformats; +static const struct s390_opcode s390_opformats[]; +static const int s390_num_opformats; /* Values defined for the flags field of a struct powerpc_opcode. */ @@ -121,7 +129,7 @@ /* Elements in the table are retrieved by indexing with values from the operands field of the powerpc_opcodes table. */ -extern const struct s390_operand s390_operands[]; +static const struct s390_operand s390_operands[]; /* Values defined for the flags field of a struct s390_operand. */ @@ -164,12 +172,38 @@ the instruction may be optional. */ #define S390_OPERAND_OPTIONAL 0x400 - #endif /* S390_H */ +/* QEMU-ADD */ +/* ??? Not quite the format the assembler takes, but easy to implement + without recourse to the table generator. */ +#define S390_OPERAND_CCODE 0x800 + +static const char s390_ccode_name[16][4] = { + "n", /* 0000 */ + "o", /* 0001 */ + "h", /* 0010 */ + "nle", /* 0011 */ + "l", /* 0100 */ + "nhe", /* 0101 */ + "lh", /* 0110 */ + "ne", /* 0111 */ + "e", /* 1000 */ + "nlh", /* 1001 */ + "he", /* 1010 */ + "nl", /* 1011 */ + "le", /* 1100 */ + "nh", /* 1101 */ + "no", /* 1110 */ + "a" /* 1111 */ +}; +/* QEMU-END */ +#endif /* S390_H */ static int init_flag = 0; static int opc_index[256]; -static int current_arch_mask = 0; + +/* QEMU: We've disabled the architecture check below. */ +/* static int current_arch_mask = 0; */ /* Set up index table for first opcode byte. */ @@ -188,17 +222,21 @@ (opcode[1].opcode[0] == opcode->opcode[0])) opcode++; } -// switch (info->mach) -// { -// case bfd_mach_s390_31: + +#ifdef QEMU_DISABLE + switch (info->mach) + { + case bfd_mach_s390_31: current_arch_mask = 1 << S390_OPCODE_ESA; -// break; -// case bfd_mach_s390_64: -// current_arch_mask = 1 << S390_OPCODE_ZARCH; -// break; -// default: -// abort (); -// } + break; + case bfd_mach_s390_64: + current_arch_mask = 1 << S390_OPCODE_ZARCH; + break; + default: + abort (); + } +#endif /* QEMU_DISABLE */ + init_flag = 1; } @@ -297,9 +335,12 @@ const struct s390_operand *operand; const unsigned char *opindex; +#ifdef QEMU_DISABLE /* Check architecture. */ if (!(opcode->modes & current_arch_mask)) continue; +#endif /* QEMU_DISABLE */ + /* Check signature of the opcode. */ if ((buffer[1] & opcode->mask[1]) != opcode->opcode[1] || (buffer[2] & opcode->mask[2]) != opcode->opcode[2] @@ -309,13 +350,16 @@ continue; /* The instruction is valid. */ - if (opcode->operands[0] != 0) - (*info->fprintf_func) (info->stream, "%s\t", opcode->name); - else - (*info->fprintf_func) (info->stream, "%s", opcode->name); +/* QEMU-MOD */ + (*info->fprintf_func) (info->stream, "%s", opcode->name); + + if (s390_operands[opcode->operands[0]].flags & S390_OPERAND_CCODE) + separator = 0; + else + separator = '\t'; +/* QEMU-END */ /* Extract the operands. */ - separator = 0; for (opindex = opcode->operands; *opindex != 0; opindex++) { unsigned int value; @@ -347,6 +391,15 @@ (*info->print_address_func) (memaddr + (int) value, info); else if (operand->flags & S390_OPERAND_SIGNED) (*info->fprintf_func) (info->stream, "%i", (int) value); +/* QEMU-ADD */ + else if (operand->flags & S390_OPERAND_CCODE) + { + (*info->fprintf_func) (info->stream, "%s", + s390_ccode_name[(int) value]); + separator = '\t'; + continue; + } +/* QEMU-END */ else (*info->fprintf_func) (info->stream, "%u", value); @@ -392,6 +445,8 @@ return 1; } } + +/* opcodes/s390-opc.c revision 1.16 */ /* s390-opc.c -- S390 opcode list Copyright 2000, 2001, 2003 Free Software Foundation, Inc. Contributed by Martin Schwidefsky (schwidefsky@de.ibm.com). @@ -409,9 +464,9 @@ 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, see . */ - -#include + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ /* This file holds the S390 opcode table. The opcode table includes almost all of the extended instruction mnemonics. This @@ -427,7 +482,7 @@ /* The operands table. The fields are bits, shift, insert, extract, flags. */ -const struct s390_operand s390_operands[] = +static const struct s390_operand s390_operands[] = { #define UNUSED 0 { 0, 0, 0 }, /* Indicates the end of the operand list */ @@ -525,8 +580,16 @@ #define M_16 42 /* 4 bit optional mask starting at 16 */ { 4, 16, S390_OPERAND_OPTIONAL }, #define RO_28 43 /* optional GPR starting at position 28 */ - { 4, 28, (S390_OPERAND_GPR | S390_OPERAND_OPTIONAL) } + { 4, 28, (S390_OPERAND_GPR | S390_OPERAND_OPTIONAL) }, +/* QEMU-ADD: */ +#define M4_12 44 /* 4-bit condition-code starting at 12 */ + { 4, 12, S390_OPERAND_CCODE }, +#define M4_32 45 /* 4-bit condition-code starting at 32 */ + { 4, 32, S390_OPERAND_CCODE }, +#define I8_32 46 /* 8 bit signed value starting at 32 */ + { 8, 32, S390_OPERAND_SIGNED }, +/* QEMU-END */ }; @@ -737,9 +800,17 @@ #define MASK_S_RD { 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 } #define MASK_SSF_RRDRD { 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00 } +/* QEMU-ADD: */ +#define INSTR_RIE_MRRP 6, { M4_32,R_8,R_12,J16_16,0,0 } /* e.g. crj */ +#define MASK_RIE_MRRP { 0xff, 0x00, 0x00, 0x00, 0x0f, 0xff } + +#define INSTR_RIE_MRIP 6, { M4_12,R_8,I8_32,J16_16,0,0 } /* e.g. cij */ +#define MASK_RIE_MRIP { 0xff, 0x00, 0x00, 0x00, 0x00, 0xff } +/* QEMU-END */ + /* The opcode formats table (blueprints for .insn pseudo mnemonic). */ -const struct s390_opcode s390_opformats[] = +static const struct s390_opcode s390_opformats[] = { { "e", OP8(0x00LL), MASK_E, INSTR_E, 3, 0 }, { "ri", OP8(0x00LL), MASK_RI_RI, INSTR_RI_RI, 3, 0 }, @@ -765,9 +836,10 @@ { "ssf", OP8(0x00LL), MASK_SSF_RRDRD, INSTR_SSF_RRDRD,3, 0 }, }; -const int s390_num_opformats = +static const int s390_num_opformats = sizeof (s390_opformats) / sizeof (s390_opformats[0]); +/* include "s390-opc.tab" generated from opcodes/s390-opc.txt rev 1.17 */ /* The opcode table. This file was generated by s390-mkopc. The format of the opcode table is: @@ -783,7 +855,7 @@ The disassembler reads the table in order and prints the first instruction which matches. */ -const struct s390_opcode s390_opcodes[] = +static const struct s390_opcode s390_opcodes[] = { { "dp", OP8(0xfdLL), MASK_SS_LLRDRD, INSTR_SS_LLRDRD, 3, 0}, { "mp", OP8(0xfcLL), MASK_SS_LLRDRD, INSTR_SS_LLRDRD, 3, 0}, @@ -1073,6 +1145,10 @@ { "agfi", OP16(0xc208LL), MASK_RIL_RI, INSTR_RIL_RI, 2, 4}, { "slfi", OP16(0xc205LL), MASK_RIL_RU, INSTR_RIL_RU, 2, 4}, { "slgfi", OP16(0xc204LL), MASK_RIL_RU, INSTR_RIL_RU, 2, 4}, +/* QEMU-ADD: */ + { "msfi", OP16(0xc201ll), MASK_RIL_RI, INSTR_RIL_RI, 3, 6}, + { "msgfi", OP16(0xc200ll), MASK_RIL_RI, INSTR_RIL_RI, 3, 6}, +/* QEMU-END */ { "jg", OP16(0xc0f4LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2}, { "jgno", OP16(0xc0e4LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2}, { "jgnh", OP16(0xc0d4LL), MASK_RIL_0P, INSTR_RIL_0P, 3, 2}, @@ -1697,8 +1773,24 @@ { "pfpo", OP16(0x010aLL), MASK_E, INSTR_E, 2, 5}, { "sckpf", OP16(0x0107LL), MASK_E, INSTR_E, 3, 0}, { "upt", OP16(0x0102LL), MASK_E, INSTR_E, 3, 0}, - { "pr", OP16(0x0101LL), MASK_E, INSTR_E, 3, 0} + { "pr", OP16(0x0101LL), MASK_E, INSTR_E, 3, 0}, + +/* QEMU-ADD: */ + { "crj", OP48(0xec0000000076LL), MASK_RIE_MRRP, INSTR_RIE_MRRP, 3, 6}, + { "cgrj", OP48(0xec0000000064LL), MASK_RIE_MRRP, INSTR_RIE_MRRP, 3, 6}, + { "clrj", OP48(0xec0000000077LL), MASK_RIE_MRRP, INSTR_RIE_MRRP, 3, 6}, + { "clgrj", OP48(0xec0000000065LL), MASK_RIE_MRRP, INSTR_RIE_MRRP, 3, 6}, + + { "cij", OP48(0xec000000007eLL), MASK_RIE_MRIP, INSTR_RIE_MRIP, 3, 6}, + { "cgij", OP48(0xec000000007cLL), MASK_RIE_MRIP, INSTR_RIE_MRIP, 3, 6}, + { "clij", OP48(0xec000000007fLL), MASK_RIE_MRIP, INSTR_RIE_MRIP, 3, 6}, + { "clgij", OP48(0xec000000007dLL), MASK_RIE_MRIP, INSTR_RIE_MRIP, 3, 6}, + + { "lrl", OP16(0xc40dll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, + { "lgrl", OP16(0xc408ll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, + { "lgfrl", OP16(0xc40cll), MASK_RIL_RP, INSTR_RIL_RP, 3, 6}, +/* QEMU-END */ }; -const int s390_num_opcodes = +static const int s390_num_opcodes = sizeof (s390_opcodes) / sizeof (s390_opcodes[0]); diff -Nru qemu-kvm-0.12.5+noroms/savevm.c qemu-kvm-0.14.1/savevm.c --- qemu-kvm-0.12.5+noroms/savevm.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/savevm.c 2011-05-11 13:29:46.000000000 +0000 @@ -72,24 +72,21 @@ #include "qemu-common.h" #include "hw/hw.h" +#include "hw/qdev.h" #include "net.h" #include "monitor.h" #include "sysemu.h" #include "qemu-timer.h" #include "qemu-char.h" -#include "block.h" #include "audio/audio.h" #include "migration.h" #include "qemu_socket.h" #include "qemu-queue.h" -/* point to the block driver where the snapshots are managed */ -static BlockDriverState *bs_snapshots; - #define SELF_ANNOUNCE_ROUNDS 5 #ifndef ETH_P_RARP -#define ETH_P_RARP 0x0835 +#define ETH_P_RARP 0x8035 #endif #define ARP_HTYPE_ETH 0x0001 #define ARP_PTYPE_IP 0x0800 @@ -235,9 +232,10 @@ static int stdio_pclose(void *opaque) { QEMUFileStdio *s = opaque; - pclose(s->stdio_file); + int ret; + ret = pclose(s->stdio_file); qemu_free(s); - return 0; + return ret; } static int stdio_fclose(void *opaque) @@ -339,8 +337,7 @@ { QEMUFileStdio *s = opaque; fseek(s->stdio_file, pos, SEEK_SET); - fwrite(buf, 1, size, s->stdio_file); - return size; + return fwrite(buf, 1, size, s->stdio_file); } static int file_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size) @@ -357,7 +354,7 @@ if (mode == NULL || (mode[0] != 'r' && mode[0] != 'w') || mode[1] != 'b' || mode[2] != 0) { - fprintf(stderr, "qemu_fdopen: Argument validity check failed\n"); + fprintf(stderr, "qemu_fopen: Argument validity check failed\n"); return NULL; } @@ -553,6 +550,19 @@ return size1 - size; } +static int qemu_peek_byte(QEMUFile *f) +{ + if (f->is_write) + abort(); + + if (f->buf_index >= f->buf_size) { + qemu_fill_buffer(f); + if (f->buf_index >= f->buf_size) + return 0; + } + return f->buf[f->buf_index]; +} + int qemu_get_byte(QEMUFile *f) { if (f->is_write) @@ -600,7 +610,7 @@ return 0; } -size_t qemu_file_get_rate_limit(QEMUFile *f) +int64_t qemu_file_get_rate_limit(QEMUFile *f) { if (f->get_rate_limit) return f->get_rate_limit(f->opaque); @@ -608,7 +618,7 @@ return 0; } -size_t qemu_file_set_rate_limit(QEMUFile *f, size_t new_rate) +int64_t qemu_file_set_rate_limit(QEMUFile *f, int64_t new_rate) { /* any failed or completed migration keeps its state to allow probing of * migration data, but has no associated file anymore */ @@ -664,6 +674,27 @@ return v; } +/* bool */ + +static int get_bool(QEMUFile *f, void *pv, size_t size) +{ + bool *v = pv; + *v = qemu_get_byte(f); + return 0; +} + +static void put_bool(QEMUFile *f, void *pv, size_t size) +{ + bool *v = pv; + qemu_put_byte(f, *v); +} + +const VMStateInfo vmstate_info_bool = { + .name = "bool", + .get = get_bool, + .put = put_bool, +}; + /* 8 bit int */ static int get_int8(QEMUFile *f, void *pv, size_t size) @@ -1011,10 +1042,16 @@ .put = put_unused_buffer, }; +typedef struct CompatEntry { + char idstr[256]; + int instance_id; +} CompatEntry; + typedef struct SaveStateEntry { QTAILQ_ENTRY(SaveStateEntry) entry; char idstr[256]; int instance_id; + int alias_id; int version_id; int section_id; SaveSetParamsHandler *set_params; @@ -1023,6 +1060,8 @@ LoadStateHandler *load_state; const VMStateDescription *vmsd; void *opaque; + CompatEntry *compat; + int no_migrate; } SaveStateEntry; @@ -1044,11 +1083,29 @@ return instance_id; } +static int calculate_compat_instance_id(const char *idstr) +{ + SaveStateEntry *se; + int instance_id = 0; + + QTAILQ_FOREACH(se, &savevm_handlers, entry) { + if (!se->compat) + continue; + + if (strcmp(idstr, se->compat->idstr) == 0 + && instance_id <= se->compat->instance_id) { + instance_id = se->compat->instance_id + 1; + } + } + return instance_id; +} + /* TODO: Individual devices generally have very little idea about the rest of the system, so instance_id should be removed/replaced. Meanwhile pass -1 as instance_id if you do not already have a clearly distinguishing id for all instances of your device class. */ -int register_savevm_live(const char *idstr, +int register_savevm_live(DeviceState *dev, + const char *idstr, int instance_id, int version_id, SaveSetParamsHandler *set_params, @@ -1060,7 +1117,6 @@ SaveStateEntry *se; se = qemu_mallocz(sizeof(SaveStateEntry)); - pstrcpy(se->idstr, sizeof(se->idstr), idstr); se->version_id = version_id; se->section_id = global_section_id++; se->set_params = set_params; @@ -1069,47 +1125,109 @@ se->load_state = load_state; se->opaque = opaque; se->vmsd = NULL; + se->no_migrate = 0; + + if (dev && dev->parent_bus && dev->parent_bus->info->get_dev_path) { + char *id = dev->parent_bus->info->get_dev_path(dev); + if (id) { + pstrcpy(se->idstr, sizeof(se->idstr), id); + pstrcat(se->idstr, sizeof(se->idstr), "/"); + qemu_free(id); + + se->compat = qemu_mallocz(sizeof(CompatEntry)); + pstrcpy(se->compat->idstr, sizeof(se->compat->idstr), idstr); + se->compat->instance_id = instance_id == -1 ? + calculate_compat_instance_id(idstr) : instance_id; + instance_id = -1; + } + } + pstrcat(se->idstr, sizeof(se->idstr), idstr); if (instance_id == -1) { - se->instance_id = calculate_new_instance_id(idstr); + se->instance_id = calculate_new_instance_id(se->idstr); } else { se->instance_id = instance_id; } + assert(!se->compat || se->instance_id == 0); /* add at the end of list */ QTAILQ_INSERT_TAIL(&savevm_handlers, se, entry); return 0; } -int register_savevm(const char *idstr, +int register_savevm(DeviceState *dev, + const char *idstr, int instance_id, int version_id, SaveStateHandler *save_state, LoadStateHandler *load_state, void *opaque) { - return register_savevm_live(idstr, instance_id, version_id, + return register_savevm_live(dev, idstr, instance_id, version_id, NULL, NULL, save_state, load_state, opaque); } -void unregister_savevm(const char *idstr, void *opaque) +void unregister_savevm(DeviceState *dev, const char *idstr, void *opaque) { SaveStateEntry *se, *new_se; + char id[256] = ""; + + if (dev && dev->parent_bus && dev->parent_bus->info->get_dev_path) { + char *path = dev->parent_bus->info->get_dev_path(dev); + if (path) { + pstrcpy(id, sizeof(id), path); + pstrcat(id, sizeof(id), "/"); + qemu_free(path); + } + } + pstrcat(id, sizeof(id), idstr); QTAILQ_FOREACH_SAFE(se, &savevm_handlers, entry, new_se) { - if (strcmp(se->idstr, idstr) == 0 && se->opaque == opaque) { + if (strcmp(se->idstr, id) == 0 && se->opaque == opaque) { QTAILQ_REMOVE(&savevm_handlers, se, entry); + if (se->compat) { + qemu_free(se->compat); + } qemu_free(se); } } } -int vmstate_register(int instance_id, const VMStateDescription *vmsd, - void *opaque) +/* mark a device as not to be migrated, that is the device should be + unplugged before migration */ +void register_device_unmigratable(DeviceState *dev, const char *idstr, + void *opaque) { SaveStateEntry *se; + char id[256] = ""; + + if (dev && dev->parent_bus && dev->parent_bus->info->get_dev_path) { + char *path = dev->parent_bus->info->get_dev_path(dev); + if (path) { + pstrcpy(id, sizeof(id), path); + pstrcat(id, sizeof(id), "/"); + qemu_free(path); + } + } + pstrcat(id, sizeof(id), idstr); + + QTAILQ_FOREACH(se, &savevm_handlers, entry) { + if (strcmp(se->idstr, id) == 0 && se->opaque == opaque) { + se->no_migrate = 1; + } + } +} + +int vmstate_register_with_alias_id(DeviceState *dev, int instance_id, + const VMStateDescription *vmsd, + void *opaque, int alias_id, + int required_for_version) +{ + SaveStateEntry *se; + + /* If this triggers, alias support can be dropped for the vmsd. */ + assert(alias_id == -1 || required_for_version >= vmsd->minimum_version_id); se = qemu_mallocz(sizeof(SaveStateEntry)); - pstrcpy(se->idstr, sizeof(se->idstr), vmsd->name); se->version_id = vmsd->version_id; se->section_id = global_section_id++; se->save_live_state = NULL; @@ -1117,33 +1235,68 @@ se->load_state = NULL; se->opaque = opaque; se->vmsd = vmsd; + se->alias_id = alias_id; + + if (dev && dev->parent_bus && dev->parent_bus->info->get_dev_path) { + char *id = dev->parent_bus->info->get_dev_path(dev); + if (id) { + pstrcpy(se->idstr, sizeof(se->idstr), id); + pstrcat(se->idstr, sizeof(se->idstr), "/"); + qemu_free(id); + + se->compat = qemu_mallocz(sizeof(CompatEntry)); + pstrcpy(se->compat->idstr, sizeof(se->compat->idstr), vmsd->name); + se->compat->instance_id = instance_id == -1 ? + calculate_compat_instance_id(vmsd->name) : instance_id; + instance_id = -1; + } + } + pstrcat(se->idstr, sizeof(se->idstr), vmsd->name); if (instance_id == -1) { - se->instance_id = calculate_new_instance_id(vmsd->name); + se->instance_id = calculate_new_instance_id(se->idstr); } else { se->instance_id = instance_id; } + assert(!se->compat || se->instance_id == 0); /* add at the end of list */ QTAILQ_INSERT_TAIL(&savevm_handlers, se, entry); return 0; } -void vmstate_unregister(const VMStateDescription *vmsd, void *opaque) +int vmstate_register(DeviceState *dev, int instance_id, + const VMStateDescription *vmsd, void *opaque) +{ + return vmstate_register_with_alias_id(dev, instance_id, vmsd, + opaque, -1, 0); +} + +void vmstate_unregister(DeviceState *dev, const VMStateDescription *vmsd, + void *opaque) { SaveStateEntry *se, *new_se; QTAILQ_FOREACH_SAFE(se, &savevm_handlers, entry, new_se) { if (se->vmsd == vmsd && se->opaque == opaque) { QTAILQ_REMOVE(&savevm_handlers, se, entry); + if (se->compat) { + qemu_free(se->compat); + } qemu_free(se); } } } +static void vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd, + void *opaque); +static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd, + void *opaque); + int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, void *opaque, int version_id) { VMStateField *field = vmsd->fields; + int ret; if (version_id > vmsd->version_id) { return -EINVAL; @@ -1165,7 +1318,7 @@ (!field->field_exists && field->version_id <= version_id)) { void *base_addr = opaque + field->offset; - int ret, i, n_elems = 1; + int i, n_elems = 1; int size = field->size; if (field->flags & VMS_VBUFFER) { @@ -1203,6 +1356,10 @@ } field++; } + ret = vmstate_subsection_load(f, vmsd, opaque); + if (ret != 0) { + return ret; + } if (vmsd->post_load) { return vmsd->post_load(opaque, version_id); } @@ -1255,9 +1412,7 @@ } field++; } - if (vmsd->post_save) { - vmsd->post_save(opaque); - } + vmstate_subsection_save(f, vmsd, opaque); } static int vmstate_load(QEMUFile *f, SaveStateEntry *se, int version_id) @@ -1286,6 +1441,21 @@ #define QEMU_VM_SECTION_PART 0x02 #define QEMU_VM_SECTION_END 0x03 #define QEMU_VM_SECTION_FULL 0x04 +#define QEMU_VM_SUBSECTION 0x05 + +bool qemu_savevm_state_blocked(Monitor *mon) +{ + SaveStateEntry *se; + + QTAILQ_FOREACH(se, &savevm_handlers, entry) { + if (se->no_migrate) { + monitor_printf(mon, "state blocked by non-migratable device '%s'\n", + se->idstr); + return true; + } + } + return false; +} int qemu_savevm_state_begin(Monitor *mon, QEMUFile *f, int blk_enable, int shared) @@ -1369,6 +1539,8 @@ { SaveStateEntry *se; + cpu_synchronize_all_states(); + QTAILQ_FOREACH(se, &savevm_handlers, entry) { if (se->save_live_state == NULL) continue; @@ -1428,7 +1600,10 @@ saved_vm_running = vm_running; vm_stop(0); - bdrv_flush_all(); + if (qemu_savevm_state_blocked(mon)) { + ret = -EINVAL; + goto out; + } ret = qemu_savevm_state_begin(mon, f, 0, 0); if (ret < 0) @@ -1458,12 +1633,87 @@ QTAILQ_FOREACH(se, &savevm_handlers, entry) { if (!strcmp(se->idstr, idstr) && - instance_id == se->instance_id) + (instance_id == se->instance_id || + instance_id == se->alias_id)) return se; + /* Migrating from an older version? */ + if (strstr(se->idstr, idstr) && se->compat) { + if (!strcmp(se->compat->idstr, idstr) && + (instance_id == se->compat->instance_id || + instance_id == se->alias_id)) + return se; + } + } + return NULL; +} + +static const VMStateDescription *vmstate_get_subsection(const VMStateSubsection *sub, char *idstr) +{ + while(sub && sub->needed) { + if (strcmp(idstr, sub->vmsd->name) == 0) { + return sub->vmsd; + } + sub++; } return NULL; } +static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd, + void *opaque) +{ + const VMStateSubsection *sub = vmsd->subsections; + + if (!sub || !sub->needed) { + return 0; + } + + while (qemu_peek_byte(f) == QEMU_VM_SUBSECTION) { + char idstr[256]; + int ret; + uint8_t version_id, len; + const VMStateDescription *sub_vmsd; + + qemu_get_byte(f); /* subsection */ + len = qemu_get_byte(f); + qemu_get_buffer(f, (uint8_t *)idstr, len); + idstr[len] = 0; + version_id = qemu_get_be32(f); + + sub_vmsd = vmstate_get_subsection(sub, idstr); + if (sub_vmsd == NULL) { + return -ENOENT; + } + assert(!sub_vmsd->subsections); + ret = vmstate_load_state(f, sub_vmsd, opaque, version_id); + if (ret) { + return ret; + } + } + return 0; +} + +static void vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd, + void *opaque) +{ + const VMStateSubsection *sub = vmsd->subsections; + + while (sub && sub->needed) { + if (sub->needed(opaque)) { + const VMStateDescription *vmsd = sub->vmsd; + uint8_t len; + + qemu_put_byte(f, QEMU_VM_SUBSECTION); + len = strlen(vmsd->name); + qemu_put_byte(f, len); + qemu_put_buffer(f, (uint8_t *)vmsd->name, len); + qemu_put_be32(f, vmsd->version_id); + assert(!vmsd->subsections); + vmstate_save_state(f, vmsd, opaque); + } + sub++; + } +} + typedef struct LoadStateEntry { QLIST_ENTRY(LoadStateEntry) entry; SaveStateEntry *se; @@ -1480,6 +1730,10 @@ unsigned int v; int ret; + if (qemu_savevm_state_blocked(default_mon)) { + return -EINVAL; + } + v = qemu_get_be32(f); if (v != QEMU_VM_FILE_MAGIC) return -EINVAL; @@ -1569,6 +1823,8 @@ } } + cpu_synchronize_all_post_init(); + ret = 0; out: @@ -1583,40 +1839,6 @@ return ret; } -/* device can contain snapshots */ -static int bdrv_can_snapshot(BlockDriverState *bs) -{ - return (bs && - !bdrv_is_removable(bs) && - !bdrv_is_read_only(bs)); -} - -/* device must be snapshots in order to have a reliable snapshot */ -static int bdrv_has_snapshot(BlockDriverState *bs) -{ - return (bs && - !bdrv_is_removable(bs) && - !bdrv_is_read_only(bs)); -} - -static BlockDriverState *get_bs_snapshots(void) -{ - BlockDriverState *bs; - DriveInfo *dinfo; - - if (bs_snapshots) - return bs_snapshots; - QTAILQ_FOREACH(dinfo, &drives, next) { - bs = dinfo->bdrv; - if (bdrv_can_snapshot(bs)) - goto ok; - } - return NULL; - ok: - bs_snapshots = bs; - return bs; -} - static int bdrv_snapshot_find(BlockDriverState *bs, QEMUSnapshotInfo *sn_info, const char *name) { @@ -1645,12 +1867,11 @@ static int del_existing_snapshots(Monitor *mon, const char *name) { BlockDriverState *bs; - DriveInfo *dinfo; QEMUSnapshotInfo sn1, *snapshot = &sn1; int ret; - QTAILQ_FOREACH(dinfo, &drives, next) { - bs = dinfo->bdrv; + bs = NULL; + while ((bs = bdrv_next(bs))) { if (bdrv_can_snapshot(bs) && bdrv_snapshot_find(bs, snapshot, name) >= 0) { @@ -1669,7 +1890,6 @@ void do_savevm(Monitor *mon, const QDict *qdict) { - DriveInfo *dinfo; BlockDriverState *bs, *bs1; QEMUSnapshotInfo sn1, *sn = &sn1, old_sn1, *old_sn = &old_sn1; int ret; @@ -1678,33 +1898,38 @@ uint32_t vm_state_size; #ifdef _WIN32 struct _timeb tb; + struct tm *ptm; #else struct timeval tv; + struct tm tm; #endif const char *name = qdict_get_try_str(qdict, "name"); - bs = get_bs_snapshots(); + /* Verify if there is a device that doesn't support snapshots and is writable */ + bs = NULL; + while ((bs = bdrv_next(bs))) { + + if (bdrv_is_removable(bs) || bdrv_is_read_only(bs)) { + continue; + } + + if (!bdrv_can_snapshot(bs)) { + monitor_printf(mon, "Device '%s' is writable but does not support snapshots.\n", + bdrv_get_device_name(bs)); + return; + } + } + + bs = bdrv_snapshots(); if (!bs) { monitor_printf(mon, "No block device can accept snapshots\n"); return; } - /* ??? Should this occur after vm_stop? */ - qemu_aio_flush(); - saved_vm_running = vm_running; vm_stop(0); memset(sn, 0, sizeof(*sn)); - if (name) { - ret = bdrv_snapshot_find(bs, old_sn, name); - if (ret >= 0) { - pstrcpy(sn->name, sizeof(sn->name), old_sn->name); - pstrcpy(sn->id_str, sizeof(sn->id_str), old_sn->id_str); - } else { - pstrcpy(sn->name, sizeof(sn->name), name); - } - } /* fill auxiliary fields */ #ifdef _WIN32 @@ -1718,6 +1943,25 @@ #endif sn->vm_clock_nsec = qemu_get_clock(vm_clock); + if (name) { + ret = bdrv_snapshot_find(bs, old_sn, name); + if (ret >= 0) { + pstrcpy(sn->name, sizeof(sn->name), old_sn->name); + pstrcpy(sn->id_str, sizeof(sn->id_str), old_sn->id_str); + } else { + pstrcpy(sn->name, sizeof(sn->name), name); + } + } else { +#ifdef _WIN32 + ptm = localtime(&tb.time); + strftime(sn->name, sizeof(sn->name), "vm-%Y%m%d%H%M%S", ptm); +#else + /* cast below needed for OpenBSD where tv_sec is still 'long' */ + localtime_r((const time_t *)&tv.tv_sec, &tm); + strftime(sn->name, sizeof(sn->name), "vm-%Y%m%d%H%M%S", &tm); +#endif + } + /* Delete old snapshots of the same name */ if (name && del_existing_snapshots(mon, name) < 0) { goto the_end; @@ -1739,9 +1983,9 @@ /* create the snapshots */ - QTAILQ_FOREACH(dinfo, &drives, next) { - bs1 = dinfo->bdrv; - if (bdrv_has_snapshot(bs1)) { + bs1 = NULL; + while ((bs1 = bdrv_next(bs1))) { + if (bdrv_can_snapshot(bs1)) { /* Write VM state size only to the image that contains the state */ sn->vm_state_size = (bs == bs1 ? vm_state_size : 0); ret = bdrv_snapshot_create(bs1, sn); @@ -1757,89 +2001,98 @@ vm_start(); } -int load_vmstate(Monitor *mon, const char *name) +int load_vmstate(const char *name) { - DriveInfo *dinfo; - BlockDriverState *bs, *bs1; + BlockDriverState *bs, *bs_vm_state; QEMUSnapshotInfo sn; QEMUFile *f; int ret; - bs = get_bs_snapshots(); - if (!bs) { - monitor_printf(mon, "No block device supports snapshots\n"); + bs_vm_state = bdrv_snapshots(); + if (!bs_vm_state) { + error_report("No block device supports snapshots"); + return -ENOTSUP; + } + + /* Don't even try to load empty VM states */ + ret = bdrv_snapshot_find(bs_vm_state, &sn, name); + if (ret < 0) { + return ret; + } else if (sn.vm_state_size == 0) { return -EINVAL; } + /* Verify if there is any device that doesn't support snapshots and is + writable and check if the requested snapshot is available too. */ + bs = NULL; + while ((bs = bdrv_next(bs))) { + + if (bdrv_is_removable(bs) || bdrv_is_read_only(bs)) { + continue; + } + + if (!bdrv_can_snapshot(bs)) { + error_report("Device '%s' is writable but does not support snapshots.", + bdrv_get_device_name(bs)); + return -ENOTSUP; + } + + ret = bdrv_snapshot_find(bs, &sn, name); + if (ret < 0) { + error_report("Device '%s' does not have the requested snapshot '%s'", + bdrv_get_device_name(bs), name); + return ret; + } + } + /* Flush all IO requests so they don't interfere with the new state. */ qemu_aio_flush(); - QTAILQ_FOREACH(dinfo, &drives, next) { - bs1 = dinfo->bdrv; - if (bdrv_has_snapshot(bs1)) { - ret = bdrv_snapshot_goto(bs1, name); + bs = NULL; + while ((bs = bdrv_next(bs))) { + if (bdrv_can_snapshot(bs)) { + ret = bdrv_snapshot_goto(bs, name); if (ret < 0) { - if (bs != bs1) - monitor_printf(mon, "Warning: "); - switch(ret) { - case -ENOTSUP: - monitor_printf(mon, - "Snapshots not supported on device '%s'\n", - bdrv_get_device_name(bs1)); - break; - case -ENOENT: - monitor_printf(mon, "Could not find snapshot '%s' on " - "device '%s'\n", - name, bdrv_get_device_name(bs1)); - break; - default: - monitor_printf(mon, "Error %d while activating snapshot on" - " '%s'\n", ret, bdrv_get_device_name(bs1)); - break; - } - /* fatal on snapshot block device */ - if (bs == bs1) - return 0; + error_report("Error %d while activating snapshot '%s' on '%s'", + ret, name, bdrv_get_device_name(bs)); + return ret; } } } - /* Don't even try to load empty VM states */ - ret = bdrv_snapshot_find(bs, &sn, name); - if ((ret >= 0) && (sn.vm_state_size == 0)) - return -EINVAL; - /* restore the VM state */ - f = qemu_fopen_bdrv(bs, 0); + f = qemu_fopen_bdrv(bs_vm_state, 0); if (!f) { - monitor_printf(mon, "Could not open VM state file\n"); + error_report("Could not open VM state file"); return -EINVAL; } + ret = qemu_loadvm_state(f); + qemu_fclose(f); if (ret < 0) { - monitor_printf(mon, "Error %d while loading VM state\n", ret); + error_report("Error %d while loading VM state", ret); return ret; } + return 0; } void do_delvm(Monitor *mon, const QDict *qdict) { - DriveInfo *dinfo; BlockDriverState *bs, *bs1; int ret; const char *name = qdict_get_str(qdict, "name"); - bs = get_bs_snapshots(); + bs = bdrv_snapshots(); if (!bs) { monitor_printf(mon, "No block device supports snapshots\n"); return; } - QTAILQ_FOREACH(dinfo, &drives, next) { - bs1 = dinfo->bdrv; - if (bdrv_has_snapshot(bs1)) { + bs1 = NULL; + while ((bs1 = bdrv_next(bs1))) { + if (bdrv_can_snapshot(bs1)) { ret = bdrv_snapshot_delete(bs1, name); if (ret < 0) { if (ret == -ENOTSUP) @@ -1856,38 +2109,64 @@ void do_info_snapshots(Monitor *mon) { - DriveInfo *dinfo; BlockDriverState *bs, *bs1; - QEMUSnapshotInfo *sn_tab, *sn; - int nb_sns, i; + QEMUSnapshotInfo *sn_tab, *sn, s, *sn_info = &s; + int nb_sns, i, ret, available; + int total; + int *available_snapshots; char buf[256]; - bs = get_bs_snapshots(); + bs = bdrv_snapshots(); if (!bs) { monitor_printf(mon, "No available block device supports snapshots\n"); return; } - monitor_printf(mon, "Snapshot devices:"); - QTAILQ_FOREACH(dinfo, &drives, next) { - bs1 = dinfo->bdrv; - if (bdrv_has_snapshot(bs1)) { - if (bs == bs1) - monitor_printf(mon, " %s", bdrv_get_device_name(bs1)); - } - } - monitor_printf(mon, "\n"); nb_sns = bdrv_snapshot_list(bs, &sn_tab); if (nb_sns < 0) { monitor_printf(mon, "bdrv_snapshot_list: error %d\n", nb_sns); return; } - monitor_printf(mon, "Snapshot list (from %s):\n", - bdrv_get_device_name(bs)); - monitor_printf(mon, "%s\n", bdrv_snapshot_dump(buf, sizeof(buf), NULL)); - for(i = 0; i < nb_sns; i++) { + + if (nb_sns == 0) { + monitor_printf(mon, "There is no snapshot available.\n"); + return; + } + + available_snapshots = qemu_mallocz(sizeof(int) * nb_sns); + total = 0; + for (i = 0; i < nb_sns; i++) { sn = &sn_tab[i]; - monitor_printf(mon, "%s\n", bdrv_snapshot_dump(buf, sizeof(buf), sn)); + available = 1; + bs1 = NULL; + + while ((bs1 = bdrv_next(bs1))) { + if (bdrv_can_snapshot(bs1) && bs1 != bs) { + ret = bdrv_snapshot_find(bs1, sn_info, sn->id_str); + if (ret < 0) { + available = 0; + break; + } + } + } + + if (available) { + available_snapshots[total] = i; + total++; + } } + + if (total > 0) { + monitor_printf(mon, "%s\n", bdrv_snapshot_dump(buf, sizeof(buf), NULL)); + for (i = 0; i < total; i++) { + sn = &sn_tab[available_snapshots[i]]; + monitor_printf(mon, "%s\n", bdrv_snapshot_dump(buf, sizeof(buf), sn)); + } + } else { + monitor_printf(mon, "There is no suitable snapshot available\n"); + } + qemu_free(sn_tab); + qemu_free(available_snapshots); + } diff -Nru qemu-kvm-0.12.5+noroms/scripts/checkpatch.pl qemu-kvm-0.14.1/scripts/checkpatch.pl --- qemu-kvm-0.12.5+noroms/scripts/checkpatch.pl 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/scripts/checkpatch.pl 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,2910 @@ +#!/usr/bin/perl -w +# (c) 2001, Dave Jones. (the file handling bit) +# (c) 2005, Joel Schopp (the ugly bit) +# (c) 2007,2008, Andy Whitcroft (new conditions, test suite) +# (c) 2008-2010 Andy Whitcroft +# Licensed under the terms of the GNU GPL License version 2 + +use strict; + +my $P = $0; +$P =~ s@.*/@@g; + +my $V = '0.31'; + +use Getopt::Long qw(:config no_auto_abbrev); + +my $quiet = 0; +my $tree = 1; +my $chk_signoff = 1; +my $chk_patch = 1; +my $tst_only; +my $emacs = 0; +my $terse = 0; +my $file = 0; +my $check = 0; +my $summary = 1; +my $mailback = 0; +my $summary_file = 0; +my $root; +my %debug; +my $help = 0; + +sub help { + my ($exitcode) = @_; + + print << "EOM"; +Usage: $P [OPTION]... [FILE]... +Version: $V + +Options: + -q, --quiet quiet + --no-tree run without a kernel tree + --no-signoff do not check for 'Signed-off-by' line + --patch treat FILE as patchfile (default) + --emacs emacs compile window format + --terse one line per report + -f, --file treat FILE as regular source file + --subjective, --strict enable more subjective tests + --root=PATH PATH to the kernel tree root + --no-summary suppress the per-file summary + --mailback only produce a report in case of warnings/errors + --summary-file include the filename in summary + --debug KEY=[0|1] turn on/off debugging of KEY, where KEY is one of + 'values', 'possible', 'type', and 'attr' (default + is all off) + --test-only=WORD report only warnings/errors containing WORD + literally + -h, --help, --version display this help and exit + +When FILE is - read standard input. +EOM + + exit($exitcode); +} + +GetOptions( + 'q|quiet+' => \$quiet, + 'tree!' => \$tree, + 'signoff!' => \$chk_signoff, + 'patch!' => \$chk_patch, + 'emacs!' => \$emacs, + 'terse!' => \$terse, + 'f|file!' => \$file, + 'subjective!' => \$check, + 'strict!' => \$check, + 'root=s' => \$root, + 'summary!' => \$summary, + 'mailback!' => \$mailback, + 'summary-file!' => \$summary_file, + + 'debug=s' => \%debug, + 'test-only=s' => \$tst_only, + 'h|help' => \$help, + 'version' => \$help +) or help(1); + +help(0) if ($help); + +my $exit = 0; + +if ($#ARGV < 0) { + print "$P: no input files\n"; + exit(1); +} + +my $dbg_values = 0; +my $dbg_possible = 0; +my $dbg_type = 0; +my $dbg_attr = 0; +for my $key (keys %debug) { + ## no critic + eval "\${dbg_$key} = '$debug{$key}';"; + die "$@" if ($@); +} + +my $rpt_cleaners = 0; + +if ($terse) { + $emacs = 1; + $quiet++; +} + +if ($tree) { + if (defined $root) { + if (!top_of_kernel_tree($root)) { + die "$P: $root: --root does not point at a valid tree\n"; + } + } else { + if (top_of_kernel_tree('.')) { + $root = '.'; + } elsif ($0 =~ m@(.*)/scripts/[^/]*$@ && + top_of_kernel_tree($1)) { + $root = $1; + } + } + + if (!defined $root) { + print "Must be run from the top-level dir. of a kernel tree\n"; + exit(2); + } +} + +my $emitted_corrupt = 0; + +our $Ident = qr{ + [A-Za-z_][A-Za-z\d_]* + (?:\s*\#\#\s*[A-Za-z_][A-Za-z\d_]*)* + }x; +our $Storage = qr{extern|static|asmlinkage}; +our $Sparse = qr{ + __user| + __kernel| + __force| + __iomem| + __must_check| + __init_refok| + __kprobes| + __ref + }x; + +# Notes to $Attribute: +# We need \b after 'init' otherwise 'initconst' will cause a false positive in a check +our $Attribute = qr{ + const| + __percpu| + __nocast| + __safe| + __bitwise__| + __packed__| + __packed2__| + __naked| + __maybe_unused| + __always_unused| + __noreturn| + __used| + __cold| + __noclone| + __deprecated| + __read_mostly| + __kprobes| + __(?:mem|cpu|dev|)(?:initdata|initconst|init\b)| + ____cacheline_aligned| + ____cacheline_aligned_in_smp| + ____cacheline_internodealigned_in_smp| + __weak + }x; +our $Modifier; +our $Inline = qr{inline|__always_inline|noinline}; +our $Member = qr{->$Ident|\.$Ident|\[[^]]*\]}; +our $Lval = qr{$Ident(?:$Member)*}; + +our $Constant = qr{(?:[0-9]+|0x[0-9a-fA-F]+)[UL]*}; +our $Assignment = qr{(?:\*\=|/=|%=|\+=|-=|<<=|>>=|&=|\^=|\|=|=)}; +our $Compare = qr{<=|>=|==|!=|<|>}; +our $Operators = qr{ + <=|>=|==|!=| + =>|->|<<|>>|<|>|!|~| + &&|\|\||,|\^|\+\+|--|&|\||\+|-|\*|\/|% + }x; + +our $NonptrType; +our $Type; +our $Declare; + +our $UTF8 = qr { + [\x09\x0A\x0D\x20-\x7E] # ASCII + | [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte + | \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs + | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte + | \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates + | \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3 + | [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15 + | \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16 +}x; + +our $typeTypedefs = qr{(?x: + (?:__)?(?:u|s|be|le)(?:8|16|32|64)| + atomic_t +)}; + +our $logFunctions = qr{(?x: + printk| + pr_(debug|dbg|vdbg|devel|info|warning|err|notice|alert|crit|emerg|cont)| + (dev|netdev|netif)_(printk|dbg|vdbg|info|warn|err|notice|alert|crit|emerg|WARN)| + WARN| + panic +)}; + +our @typeList = ( + qr{void}, + qr{(?:unsigned\s+)?char}, + qr{(?:unsigned\s+)?short}, + qr{(?:unsigned\s+)?int}, + qr{(?:unsigned\s+)?long}, + qr{(?:unsigned\s+)?long\s+int}, + qr{(?:unsigned\s+)?long\s+long}, + qr{(?:unsigned\s+)?long\s+long\s+int}, + qr{unsigned}, + qr{float}, + qr{double}, + qr{bool}, + qr{struct\s+$Ident}, + qr{union\s+$Ident}, + qr{enum\s+$Ident}, + qr{${Ident}_t}, + qr{${Ident}_handler}, + qr{${Ident}_handler_fn}, +); +our @modifierList = ( + qr{fastcall}, +); + +our $allowed_asm_includes = qr{(?x: + irq| + memory +)}; +# memory.h: ARM has a custom one + +sub build_types { + my $mods = "(?x: \n" . join("|\n ", @modifierList) . "\n)"; + my $all = "(?x: \n" . join("|\n ", @typeList) . "\n)"; + $Modifier = qr{(?:$Attribute|$Sparse|$mods)}; + $NonptrType = qr{ + (?:$Modifier\s+|const\s+)* + (?: + (?:typeof|__typeof__)\s*\(\s*\**\s*$Ident\s*\)| + (?:$typeTypedefs\b)| + (?:${all}\b) + ) + (?:\s+$Modifier|\s+const)* + }x; + $Type = qr{ + $NonptrType + (?:[\s\*]+\s*const|[\s\*]+|(?:\s*\[\s*\])+)? + (?:\s+$Inline|\s+$Modifier)* + }x; + $Declare = qr{(?:$Storage\s+)?$Type}; +} +build_types(); + +$chk_signoff = 0 if ($file); + +my @dep_includes = (); +my @dep_functions = (); +my $removal = "Documentation/feature-removal-schedule.txt"; +if ($tree && -f "$root/$removal") { + open(my $REMOVE, '<', "$root/$removal") || + die "$P: $removal: open failed - $!\n"; + while (<$REMOVE>) { + if (/^Check:\s+(.*\S)/) { + for my $entry (split(/[, ]+/, $1)) { + if ($entry =~ m@include/(.*)@) { + push(@dep_includes, $1); + + } elsif ($entry !~ m@/@) { + push(@dep_functions, $entry); + } + } + } + } + close($REMOVE); +} + +my @rawlines = (); +my @lines = (); +my $vname; +for my $filename (@ARGV) { + my $FILE; + if ($file) { + open($FILE, '-|', "diff -u /dev/null $filename") || + die "$P: $filename: diff failed - $!\n"; + } elsif ($filename eq '-') { + open($FILE, '<&STDIN'); + } else { + open($FILE, '<', "$filename") || + die "$P: $filename: open failed - $!\n"; + } + if ($filename eq '-') { + $vname = 'Your patch'; + } else { + $vname = $filename; + } + while (<$FILE>) { + chomp; + push(@rawlines, $_); + } + close($FILE); + if (!process($filename)) { + $exit = 1; + } + @rawlines = (); + @lines = (); +} + +exit($exit); + +sub top_of_kernel_tree { + my ($root) = @_; + + my @tree_check = ( + "COPYING", "MAINTAINERS", "Makefile", + "README", "docs", "VERSION", + "vl.c" + ); + + foreach my $check (@tree_check) { + if (! -e $root . '/' . $check) { + return 0; + } + } + return 1; +} + +sub expand_tabs { + my ($str) = @_; + + my $res = ''; + my $n = 0; + for my $c (split(//, $str)) { + if ($c eq "\t") { + $res .= ' '; + $n++; + for (; ($n % 8) != 0; $n++) { + $res .= ' '; + } + next; + } + $res .= $c; + $n++; + } + + return $res; +} +sub copy_spacing { + (my $res = shift) =~ tr/\t/ /c; + return $res; +} + +sub line_stats { + my ($line) = @_; + + # Drop the diff line leader and expand tabs + $line =~ s/^.//; + $line = expand_tabs($line); + + # Pick the indent from the front of the line. + my ($white) = ($line =~ /^(\s*)/); + + return (length($line), length($white)); +} + +my $sanitise_quote = ''; + +sub sanitise_line_reset { + my ($in_comment) = @_; + + if ($in_comment) { + $sanitise_quote = '*/'; + } else { + $sanitise_quote = ''; + } +} +sub sanitise_line { + my ($line) = @_; + + my $res = ''; + my $l = ''; + + my $qlen = 0; + my $off = 0; + my $c; + + # Always copy over the diff marker. + $res = substr($line, 0, 1); + + for ($off = 1; $off < length($line); $off++) { + $c = substr($line, $off, 1); + + # Comments we are wacking completly including the begin + # and end, all to $;. + if ($sanitise_quote eq '' && substr($line, $off, 2) eq '/*') { + $sanitise_quote = '*/'; + + substr($res, $off, 2, "$;$;"); + $off++; + next; + } + if ($sanitise_quote eq '*/' && substr($line, $off, 2) eq '*/') { + $sanitise_quote = ''; + substr($res, $off, 2, "$;$;"); + $off++; + next; + } + if ($sanitise_quote eq '' && substr($line, $off, 2) eq '//') { + $sanitise_quote = '//'; + + substr($res, $off, 2, $sanitise_quote); + $off++; + next; + } + + # A \ in a string means ignore the next character. + if (($sanitise_quote eq "'" || $sanitise_quote eq '"') && + $c eq "\\") { + substr($res, $off, 2, 'XX'); + $off++; + next; + } + # Regular quotes. + if ($c eq "'" || $c eq '"') { + if ($sanitise_quote eq '') { + $sanitise_quote = $c; + + substr($res, $off, 1, $c); + next; + } elsif ($sanitise_quote eq $c) { + $sanitise_quote = ''; + } + } + + #print "c<$c> SQ<$sanitise_quote>\n"; + if ($off != 0 && $sanitise_quote eq '*/' && $c ne "\t") { + substr($res, $off, 1, $;); + } elsif ($off != 0 && $sanitise_quote eq '//' && $c ne "\t") { + substr($res, $off, 1, $;); + } elsif ($off != 0 && $sanitise_quote && $c ne "\t") { + substr($res, $off, 1, 'X'); + } else { + substr($res, $off, 1, $c); + } + } + + if ($sanitise_quote eq '//') { + $sanitise_quote = ''; + } + + # The pathname on a #include may be surrounded by '<' and '>'. + if ($res =~ /^.\s*\#\s*include\s+\<(.*)\>/) { + my $clean = 'X' x length($1); + $res =~ s@\<.*\>@<$clean>@; + + # The whole of a #error is a string. + } elsif ($res =~ /^.\s*\#\s*(?:error|warning)\s+(.*)\b/) { + my $clean = 'X' x length($1); + $res =~ s@(\#\s*(?:error|warning)\s+).*@$1$clean@; + } + + return $res; +} + +sub ctx_statement_block { + my ($linenr, $remain, $off) = @_; + my $line = $linenr - 1; + my $blk = ''; + my $soff = $off; + my $coff = $off - 1; + my $coff_set = 0; + + my $loff = 0; + + my $type = ''; + my $level = 0; + my @stack = (); + my $p; + my $c; + my $len = 0; + + my $remainder; + while (1) { + @stack = (['', 0]) if ($#stack == -1); + + #warn "CSB: blk<$blk> remain<$remain>\n"; + # If we are about to drop off the end, pull in more + # context. + if ($off >= $len) { + for (; $remain > 0; $line++) { + last if (!defined $lines[$line]); + next if ($lines[$line] =~ /^-/); + $remain--; + $loff = $len; + $blk .= $lines[$line] . "\n"; + $len = length($blk); + $line++; + last; + } + # Bail if there is no further context. + #warn "CSB: blk<$blk> off<$off> len<$len>\n"; + if ($off >= $len) { + last; + } + } + $p = $c; + $c = substr($blk, $off, 1); + $remainder = substr($blk, $off); + + #warn "CSB: c<$c> type<$type> level<$level> remainder<$remainder> coff_set<$coff_set>\n"; + + # Handle nested #if/#else. + if ($remainder =~ /^#\s*(?:ifndef|ifdef|if)\s/) { + push(@stack, [ $type, $level ]); + } elsif ($remainder =~ /^#\s*(?:else|elif)\b/) { + ($type, $level) = @{$stack[$#stack - 1]}; + } elsif ($remainder =~ /^#\s*endif\b/) { + ($type, $level) = @{pop(@stack)}; + } + + # Statement ends at the ';' or a close '}' at the + # outermost level. + if ($level == 0 && $c eq ';') { + last; + } + + # An else is really a conditional as long as its not else if + if ($level == 0 && $coff_set == 0 && + (!defined($p) || $p =~ /(?:\s|\}|\+)/) && + $remainder =~ /^(else)(?:\s|{)/ && + $remainder !~ /^else\s+if\b/) { + $coff = $off + length($1) - 1; + $coff_set = 1; + #warn "CSB: mark coff<$coff> soff<$soff> 1<$1>\n"; + #warn "[" . substr($blk, $soff, $coff - $soff + 1) . "]\n"; + } + + if (($type eq '' || $type eq '(') && $c eq '(') { + $level++; + $type = '('; + } + if ($type eq '(' && $c eq ')') { + $level--; + $type = ($level != 0)? '(' : ''; + + if ($level == 0 && $coff < $soff) { + $coff = $off; + $coff_set = 1; + #warn "CSB: mark coff<$coff>\n"; + } + } + if (($type eq '' || $type eq '{') && $c eq '{') { + $level++; + $type = '{'; + } + if ($type eq '{' && $c eq '}') { + $level--; + $type = ($level != 0)? '{' : ''; + + if ($level == 0) { + if (substr($blk, $off + 1, 1) eq ';') { + $off++; + } + last; + } + } + $off++; + } + # We are truly at the end, so shuffle to the next line. + if ($off == $len) { + $loff = $len + 1; + $line++; + $remain--; + } + + my $statement = substr($blk, $soff, $off - $soff + 1); + my $condition = substr($blk, $soff, $coff - $soff + 1); + + #warn "STATEMENT<$statement>\n"; + #warn "CONDITION<$condition>\n"; + + #print "coff<$coff> soff<$off> loff<$loff>\n"; + + return ($statement, $condition, + $line, $remain + 1, $off - $loff + 1, $level); +} + +sub statement_lines { + my ($stmt) = @_; + + # Strip the diff line prefixes and rip blank lines at start and end. + $stmt =~ s/(^|\n)./$1/g; + $stmt =~ s/^\s*//; + $stmt =~ s/\s*$//; + + my @stmt_lines = ($stmt =~ /\n/g); + + return $#stmt_lines + 2; +} + +sub statement_rawlines { + my ($stmt) = @_; + + my @stmt_lines = ($stmt =~ /\n/g); + + return $#stmt_lines + 2; +} + +sub statement_block_size { + my ($stmt) = @_; + + $stmt =~ s/(^|\n)./$1/g; + $stmt =~ s/^\s*{//; + $stmt =~ s/}\s*$//; + $stmt =~ s/^\s*//; + $stmt =~ s/\s*$//; + + my @stmt_lines = ($stmt =~ /\n/g); + my @stmt_statements = ($stmt =~ /;/g); + + my $stmt_lines = $#stmt_lines + 2; + my $stmt_statements = $#stmt_statements + 1; + + if ($stmt_lines > $stmt_statements) { + return $stmt_lines; + } else { + return $stmt_statements; + } +} + +sub ctx_statement_full { + my ($linenr, $remain, $off) = @_; + my ($statement, $condition, $level); + + my (@chunks); + + # Grab the first conditional/block pair. + ($statement, $condition, $linenr, $remain, $off, $level) = + ctx_statement_block($linenr, $remain, $off); + #print "F: c<$condition> s<$statement> remain<$remain>\n"; + push(@chunks, [ $condition, $statement ]); + if (!($remain > 0 && $condition =~ /^\s*(?:\n[+-])?\s*(?:if|else|do)\b/s)) { + return ($level, $linenr, @chunks); + } + + # Pull in the following conditional/block pairs and see if they + # could continue the statement. + for (;;) { + ($statement, $condition, $linenr, $remain, $off, $level) = + ctx_statement_block($linenr, $remain, $off); + #print "C: c<$condition> s<$statement> remain<$remain>\n"; + last if (!($remain > 0 && $condition =~ /^(?:\s*\n[+-])*\s*(?:else|do)\b/s)); + #print "C: push\n"; + push(@chunks, [ $condition, $statement ]); + } + + return ($level, $linenr, @chunks); +} + +sub ctx_block_get { + my ($linenr, $remain, $outer, $open, $close, $off) = @_; + my $line; + my $start = $linenr - 1; + my $blk = ''; + my @o; + my @c; + my @res = (); + + my $level = 0; + my @stack = ($level); + for ($line = $start; $remain > 0; $line++) { + next if ($rawlines[$line] =~ /^-/); + $remain--; + + $blk .= $rawlines[$line]; + + # Handle nested #if/#else. + if ($lines[$line] =~ /^.\s*#\s*(?:ifndef|ifdef|if)\s/) { + push(@stack, $level); + } elsif ($lines[$line] =~ /^.\s*#\s*(?:else|elif)\b/) { + $level = $stack[$#stack - 1]; + } elsif ($lines[$line] =~ /^.\s*#\s*endif\b/) { + $level = pop(@stack); + } + + foreach my $c (split(//, $lines[$line])) { + ##print "C<$c>L<$level><$open$close>O<$off>\n"; + if ($off > 0) { + $off--; + next; + } + + if ($c eq $close && $level > 0) { + $level--; + last if ($level == 0); + } elsif ($c eq $open) { + $level++; + } + } + + if (!$outer || $level <= 1) { + push(@res, $rawlines[$line]); + } + + last if ($level == 0); + } + + return ($level, @res); +} +sub ctx_block_outer { + my ($linenr, $remain) = @_; + + my ($level, @r) = ctx_block_get($linenr, $remain, 1, '{', '}', 0); + return @r; +} +sub ctx_block { + my ($linenr, $remain) = @_; + + my ($level, @r) = ctx_block_get($linenr, $remain, 0, '{', '}', 0); + return @r; +} +sub ctx_statement { + my ($linenr, $remain, $off) = @_; + + my ($level, @r) = ctx_block_get($linenr, $remain, 0, '(', ')', $off); + return @r; +} +sub ctx_block_level { + my ($linenr, $remain) = @_; + + return ctx_block_get($linenr, $remain, 0, '{', '}', 0); +} +sub ctx_statement_level { + my ($linenr, $remain, $off) = @_; + + return ctx_block_get($linenr, $remain, 0, '(', ')', $off); +} + +sub ctx_locate_comment { + my ($first_line, $end_line) = @_; + + # Catch a comment on the end of the line itself. + my ($current_comment) = ($rawlines[$end_line - 1] =~ m@.*(/\*.*\*/)\s*(?:\\\s*)?$@); + return $current_comment if (defined $current_comment); + + # Look through the context and try and figure out if there is a + # comment. + my $in_comment = 0; + $current_comment = ''; + for (my $linenr = $first_line; $linenr < $end_line; $linenr++) { + my $line = $rawlines[$linenr - 1]; + #warn " $line\n"; + if ($linenr == $first_line and $line =~ m@^.\s*\*@) { + $in_comment = 1; + } + if ($line =~ m@/\*@) { + $in_comment = 1; + } + if (!$in_comment && $current_comment ne '') { + $current_comment = ''; + } + $current_comment .= $line . "\n" if ($in_comment); + if ($line =~ m@\*/@) { + $in_comment = 0; + } + } + + chomp($current_comment); + return($current_comment); +} +sub ctx_has_comment { + my ($first_line, $end_line) = @_; + my $cmt = ctx_locate_comment($first_line, $end_line); + + ##print "LINE: $rawlines[$end_line - 1 ]\n"; + ##print "CMMT: $cmt\n"; + + return ($cmt ne ''); +} + +sub raw_line { + my ($linenr, $cnt) = @_; + + my $offset = $linenr - 1; + $cnt++; + + my $line; + while ($cnt) { + $line = $rawlines[$offset++]; + next if (defined($line) && $line =~ /^-/); + $cnt--; + } + + return $line; +} + +sub cat_vet { + my ($vet) = @_; + my ($res, $coded); + + $res = ''; + while ($vet =~ /([^[:cntrl:]]*)([[:cntrl:]]|$)/g) { + $res .= $1; + if ($2 ne '') { + $coded = sprintf("^%c", unpack('C', $2) + 64); + $res .= $coded; + } + } + $res =~ s/$/\$/; + + return $res; +} + +my $av_preprocessor = 0; +my $av_pending; +my @av_paren_type; +my $av_pend_colon; + +sub annotate_reset { + $av_preprocessor = 0; + $av_pending = '_'; + @av_paren_type = ('E'); + $av_pend_colon = 'O'; +} + +sub annotate_values { + my ($stream, $type) = @_; + + my $res; + my $var = '_' x length($stream); + my $cur = $stream; + + print "$stream\n" if ($dbg_values > 1); + + while (length($cur)) { + @av_paren_type = ('E') if ($#av_paren_type < 0); + print " <" . join('', @av_paren_type) . + "> <$type> <$av_pending>" if ($dbg_values > 1); + if ($cur =~ /^(\s+)/o) { + print "WS($1)\n" if ($dbg_values > 1); + if ($1 =~ /\n/ && $av_preprocessor) { + $type = pop(@av_paren_type); + $av_preprocessor = 0; + } + + } elsif ($cur =~ /^(\(\s*$Type\s*)\)/) { + print "CAST($1)\n" if ($dbg_values > 1); + push(@av_paren_type, $type); + $type = 'C'; + + } elsif ($cur =~ /^($Type)\s*(?:$Ident|,|\)|\(|\s*$)/) { + print "DECLARE($1)\n" if ($dbg_values > 1); + $type = 'T'; + + } elsif ($cur =~ /^($Modifier)\s*/) { + print "MODIFIER($1)\n" if ($dbg_values > 1); + $type = 'T'; + + } elsif ($cur =~ /^(\#\s*define\s*$Ident)(\(?)/o) { + print "DEFINE($1,$2)\n" if ($dbg_values > 1); + $av_preprocessor = 1; + push(@av_paren_type, $type); + if ($2 ne '') { + $av_pending = 'N'; + } + $type = 'E'; + + } elsif ($cur =~ /^(\#\s*(?:undef\s*$Ident|include\b))/o) { + print "UNDEF($1)\n" if ($dbg_values > 1); + $av_preprocessor = 1; + push(@av_paren_type, $type); + + } elsif ($cur =~ /^(\#\s*(?:ifdef|ifndef|if))/o) { + print "PRE_START($1)\n" if ($dbg_values > 1); + $av_preprocessor = 1; + + push(@av_paren_type, $type); + push(@av_paren_type, $type); + $type = 'E'; + + } elsif ($cur =~ /^(\#\s*(?:else|elif))/o) { + print "PRE_RESTART($1)\n" if ($dbg_values > 1); + $av_preprocessor = 1; + + push(@av_paren_type, $av_paren_type[$#av_paren_type]); + + $type = 'E'; + + } elsif ($cur =~ /^(\#\s*(?:endif))/o) { + print "PRE_END($1)\n" if ($dbg_values > 1); + + $av_preprocessor = 1; + + # Assume all arms of the conditional end as this + # one does, and continue as if the #endif was not here. + pop(@av_paren_type); + push(@av_paren_type, $type); + $type = 'E'; + + } elsif ($cur =~ /^(\\\n)/o) { + print "PRECONT($1)\n" if ($dbg_values > 1); + + } elsif ($cur =~ /^(__attribute__)\s*\(?/o) { + print "ATTR($1)\n" if ($dbg_values > 1); + $av_pending = $type; + $type = 'N'; + + } elsif ($cur =~ /^(sizeof)\s*(\()?/o) { + print "SIZEOF($1)\n" if ($dbg_values > 1); + if (defined $2) { + $av_pending = 'V'; + } + $type = 'N'; + + } elsif ($cur =~ /^(if|while|for)\b/o) { + print "COND($1)\n" if ($dbg_values > 1); + $av_pending = 'E'; + $type = 'N'; + + } elsif ($cur =~/^(case)/o) { + print "CASE($1)\n" if ($dbg_values > 1); + $av_pend_colon = 'C'; + $type = 'N'; + + } elsif ($cur =~/^(return|else|goto|typeof|__typeof__)\b/o) { + print "KEYWORD($1)\n" if ($dbg_values > 1); + $type = 'N'; + + } elsif ($cur =~ /^(\()/o) { + print "PAREN('$1')\n" if ($dbg_values > 1); + push(@av_paren_type, $av_pending); + $av_pending = '_'; + $type = 'N'; + + } elsif ($cur =~ /^(\))/o) { + my $new_type = pop(@av_paren_type); + if ($new_type ne '_') { + $type = $new_type; + print "PAREN('$1') -> $type\n" + if ($dbg_values > 1); + } else { + print "PAREN('$1')\n" if ($dbg_values > 1); + } + + } elsif ($cur =~ /^($Ident)\s*\(/o) { + print "FUNC($1)\n" if ($dbg_values > 1); + $type = 'V'; + $av_pending = 'V'; + + } elsif ($cur =~ /^($Ident\s*):(?:\s*\d+\s*(,|=|;))?/) { + if (defined $2 && $type eq 'C' || $type eq 'T') { + $av_pend_colon = 'B'; + } elsif ($type eq 'E') { + $av_pend_colon = 'L'; + } + print "IDENT_COLON($1,$type>$av_pend_colon)\n" if ($dbg_values > 1); + $type = 'V'; + + } elsif ($cur =~ /^($Ident|$Constant)/o) { + print "IDENT($1)\n" if ($dbg_values > 1); + $type = 'V'; + + } elsif ($cur =~ /^($Assignment)/o) { + print "ASSIGN($1)\n" if ($dbg_values > 1); + $type = 'N'; + + } elsif ($cur =~/^(;|{|})/) { + print "END($1)\n" if ($dbg_values > 1); + $type = 'E'; + $av_pend_colon = 'O'; + + } elsif ($cur =~/^(,)/) { + print "COMMA($1)\n" if ($dbg_values > 1); + $type = 'C'; + + } elsif ($cur =~ /^(\?)/o) { + print "QUESTION($1)\n" if ($dbg_values > 1); + $type = 'N'; + + } elsif ($cur =~ /^(:)/o) { + print "COLON($1,$av_pend_colon)\n" if ($dbg_values > 1); + + substr($var, length($res), 1, $av_pend_colon); + if ($av_pend_colon eq 'C' || $av_pend_colon eq 'L') { + $type = 'E'; + } else { + $type = 'N'; + } + $av_pend_colon = 'O'; + + } elsif ($cur =~ /^(\[)/o) { + print "CLOSE($1)\n" if ($dbg_values > 1); + $type = 'N'; + + } elsif ($cur =~ /^(-(?![->])|\+(?!\+)|\*|\&\&|\&)/o) { + my $variant; + + print "OPV($1)\n" if ($dbg_values > 1); + if ($type eq 'V') { + $variant = 'B'; + } else { + $variant = 'U'; + } + + substr($var, length($res), 1, $variant); + $type = 'N'; + + } elsif ($cur =~ /^($Operators)/o) { + print "OP($1)\n" if ($dbg_values > 1); + if ($1 ne '++' && $1 ne '--') { + $type = 'N'; + } + + } elsif ($cur =~ /(^.)/o) { + print "C($1)\n" if ($dbg_values > 1); + } + if (defined $1) { + $cur = substr($cur, length($1)); + $res .= $type x length($1); + } + } + + return ($res, $var); +} + +sub possible { + my ($possible, $line) = @_; + my $notPermitted = qr{(?: + ^(?: + $Modifier| + $Storage| + $Type| + DEFINE_\S+ + )$| + ^(?: + goto| + return| + case| + else| + asm|__asm__| + do + )(?:\s|$)| + ^(?:typedef|struct|enum)\b + )}x; + warn "CHECK<$possible> ($line)\n" if ($dbg_possible > 2); + if ($possible !~ $notPermitted) { + # Check for modifiers. + $possible =~ s/\s*$Storage\s*//g; + $possible =~ s/\s*$Sparse\s*//g; + if ($possible =~ /^\s*$/) { + + } elsif ($possible =~ /\s/) { + $possible =~ s/\s*$Type\s*//g; + for my $modifier (split(' ', $possible)) { + if ($modifier !~ $notPermitted) { + warn "MODIFIER: $modifier ($possible) ($line)\n" if ($dbg_possible); + push(@modifierList, $modifier); + } + } + + } else { + warn "POSSIBLE: $possible ($line)\n" if ($dbg_possible); + push(@typeList, $possible); + } + build_types(); + } else { + warn "NOTPOSS: $possible ($line)\n" if ($dbg_possible > 1); + } +} + +my $prefix = ''; + +sub report { + if (defined $tst_only && $_[0] !~ /\Q$tst_only\E/) { + return 0; + } + my $line = $prefix . $_[0]; + + $line = (split('\n', $line))[0] . "\n" if ($terse); + + push(our @report, $line); + + return 1; +} +sub report_dump { + our @report; +} +sub ERROR { + if (report("ERROR: $_[0]\n")) { + our $clean = 0; + our $cnt_error++; + } +} +sub WARN { + if (report("WARNING: $_[0]\n")) { + our $clean = 0; + our $cnt_warn++; + } +} +sub CHK { + if ($check && report("CHECK: $_[0]\n")) { + our $clean = 0; + our $cnt_chk++; + } +} + +sub check_absolute_file { + my ($absolute, $herecurr) = @_; + my $file = $absolute; + + ##print "absolute<$absolute>\n"; + + # See if any suffix of this path is a path within the tree. + while ($file =~ s@^[^/]*/@@) { + if (-f "$root/$file") { + ##print "file<$file>\n"; + last; + } + } + if (! -f _) { + return 0; + } + + # It is, so see if the prefix is acceptable. + my $prefix = $absolute; + substr($prefix, -length($file)) = ''; + + ##print "prefix<$prefix>\n"; + if ($prefix ne ".../") { + WARN("use relative pathname instead of absolute in changelog text\n" . $herecurr); + } +} + +sub process { + my $filename = shift; + + my $linenr=0; + my $prevline=""; + my $prevrawline=""; + my $stashline=""; + my $stashrawline=""; + + my $length; + my $indent; + my $previndent=0; + my $stashindent=0; + + our $clean = 1; + my $signoff = 0; + my $is_patch = 0; + + our @report = (); + our $cnt_lines = 0; + our $cnt_error = 0; + our $cnt_warn = 0; + our $cnt_chk = 0; + + # Trace the real file/line as we go. + my $realfile = ''; + my $realline = 0; + my $realcnt = 0; + my $here = ''; + my $in_comment = 0; + my $comment_edge = 0; + my $first_line = 0; + my $p1_prefix = ''; + + my $prev_values = 'E'; + + # suppression flags + my %suppress_ifbraces; + my %suppress_whiletrailers; + my %suppress_export; + + # Pre-scan the patch sanitizing the lines. + # Pre-scan the patch looking for any __setup documentation. + # + my @setup_docs = (); + my $setup_docs = 0; + + sanitise_line_reset(); + my $line; + foreach my $rawline (@rawlines) { + $linenr++; + $line = $rawline; + + if ($rawline=~/^\+\+\+\s+(\S+)/) { + $setup_docs = 0; + if ($1 =~ m@Documentation/kernel-parameters.txt$@) { + $setup_docs = 1; + } + #next; + } + if ($rawline=~/^\@\@ -\d+(?:,\d+)? \+(\d+)(,(\d+))? \@\@/) { + $realline=$1-1; + if (defined $2) { + $realcnt=$3+1; + } else { + $realcnt=1+1; + } + $in_comment = 0; + + # Guestimate if this is a continuing comment. Run + # the context looking for a comment "edge". If this + # edge is a close comment then we must be in a comment + # at context start. + my $edge; + my $cnt = $realcnt; + for (my $ln = $linenr + 1; $cnt > 0; $ln++) { + next if (defined $rawlines[$ln - 1] && + $rawlines[$ln - 1] =~ /^-/); + $cnt--; + #print "RAW<$rawlines[$ln - 1]>\n"; + last if (!defined $rawlines[$ln - 1]); + if ($rawlines[$ln - 1] =~ m@(/\*|\*/)@ && + $rawlines[$ln - 1] !~ m@"[^"]*(?:/\*|\*/)[^"]*"@) { + ($edge) = $1; + last; + } + } + if (defined $edge && $edge eq '*/') { + $in_comment = 1; + } + + # Guestimate if this is a continuing comment. If this + # is the start of a diff block and this line starts + # ' *' then it is very likely a comment. + if (!defined $edge && + $rawlines[$linenr] =~ m@^.\s*(?:\*\*+| \*)(?:\s|$)@) + { + $in_comment = 1; + } + + ##print "COMMENT:$in_comment edge<$edge> $rawline\n"; + sanitise_line_reset($in_comment); + + } elsif ($realcnt && $rawline =~ /^(?:\+| |$)/) { + # Standardise the strings and chars within the input to + # simplify matching -- only bother with positive lines. + $line = sanitise_line($rawline); + } + push(@lines, $line); + + if ($realcnt > 1) { + $realcnt-- if ($line =~ /^(?:\+| |$)/); + } else { + $realcnt = 0; + } + + #print "==>$rawline\n"; + #print "-->$line\n"; + + if ($setup_docs && $line =~ /^\+/) { + push(@setup_docs, $line); + } + } + + $prefix = ''; + + $realcnt = 0; + $linenr = 0; + foreach my $line (@lines) { + $linenr++; + + my $rawline = $rawlines[$linenr - 1]; + +#extract the line range in the file after the patch is applied + if ($line=~/^\@\@ -\d+(?:,\d+)? \+(\d+)(,(\d+))? \@\@/) { + $is_patch = 1; + $first_line = $linenr + 1; + $realline=$1-1; + if (defined $2) { + $realcnt=$3+1; + } else { + $realcnt=1+1; + } + annotate_reset(); + $prev_values = 'E'; + + %suppress_ifbraces = (); + %suppress_whiletrailers = (); + %suppress_export = (); + next; + +# track the line number as we move through the hunk, note that +# new versions of GNU diff omit the leading space on completely +# blank context lines so we need to count that too. + } elsif ($line =~ /^( |\+|$)/) { + $realline++; + $realcnt-- if ($realcnt != 0); + + # Measure the line length and indent. + ($length, $indent) = line_stats($rawline); + + # Track the previous line. + ($prevline, $stashline) = ($stashline, $line); + ($previndent, $stashindent) = ($stashindent, $indent); + ($prevrawline, $stashrawline) = ($stashrawline, $rawline); + + #warn "line<$line>\n"; + + } elsif ($realcnt == 1) { + $realcnt--; + } + + my $hunk_line = ($realcnt != 0); + +#make up the handle for any error we report on this line + $prefix = "$filename:$realline: " if ($emacs && $file); + $prefix = "$filename:$linenr: " if ($emacs && !$file); + + $here = "#$linenr: " if (!$file); + $here = "#$realline: " if ($file); + + # extract the filename as it passes + if ($line =~ /^diff --git.*?(\S+)$/) { + $realfile = $1; + $realfile =~ s@^([^/]*)/@@; + + } elsif ($line =~ /^\+\+\+\s+(\S+)/) { + $realfile = $1; + $realfile =~ s@^([^/]*)/@@; + + $p1_prefix = $1; + if (!$file && $tree && $p1_prefix ne '' && + -e "$root/$p1_prefix") { + WARN("patch prefix '$p1_prefix' exists, appears to be a -p0 patch\n"); + } + + if ($realfile =~ m@^include/asm/@) { + ERROR("do not modify files in include/asm, change architecture specific files in include/asm-\n" . "$here$rawline\n"); + } + next; + } + + $here .= "FILE: $realfile:$realline:" if ($realcnt != 0); + + my $hereline = "$here\n$rawline\n"; + my $herecurr = "$here\n$rawline\n"; + my $hereprev = "$here\n$prevrawline\n$rawline\n"; + + $cnt_lines++ if ($realcnt != 0); + +# Check for incorrect file permissions + if ($line =~ /^new (file )?mode.*[7531]\d{0,2}$/) { + my $permhere = $here . "FILE: $realfile\n"; + if ($realfile =~ /(Makefile|Kconfig|\.c|\.h|\.S|\.tmpl)$/) { + ERROR("do not set execute permissions for source files\n" . $permhere); + } + } + +#check the patch for a signoff: + if ($line =~ /^\s*signed-off-by:/i) { + # This is a signoff, if ugly, so do not double report. + $signoff++; + if (!($line =~ /^\s*Signed-off-by:/)) { + WARN("Signed-off-by: is the preferred form\n" . + $herecurr); + } + if ($line =~ /^\s*signed-off-by:\S/i) { + WARN("space required after Signed-off-by:\n" . + $herecurr); + } + } + +# Check for wrappage within a valid hunk of the file + if ($realcnt != 0 && $line !~ m{^(?:\+|-| |\\ No newline|$)}) { + ERROR("patch seems to be corrupt (line wrapped?)\n" . + $herecurr) if (!$emitted_corrupt++); + } + +# Check for absolute kernel paths. + if ($tree) { + while ($line =~ m{(?:^|\s)(/\S*)}g) { + my $file = $1; + + if ($file =~ m{^(.*?)(?::\d+)+:?$} && + check_absolute_file($1, $herecurr)) { + # + } else { + check_absolute_file($file, $herecurr); + } + } + } + +# UTF-8 regex found at http://www.w3.org/International/questions/qa-forms-utf-8.en.php + if (($realfile =~ /^$/ || $line =~ /^\+/) && + $rawline !~ m/^$UTF8*$/) { + my ($utf8_prefix) = ($rawline =~ /^($UTF8*)/); + + my $blank = copy_spacing($rawline); + my $ptr = substr($blank, 0, length($utf8_prefix)) . "^"; + my $hereptr = "$hereline$ptr\n"; + + ERROR("Invalid UTF-8, patch and commit message should be encoded in UTF-8\n" . $hereptr); + } + +# ignore non-hunk lines and lines being removed + next if (!$hunk_line || $line =~ /^-/); + +#trailing whitespace + if ($line =~ /^\+.*\015/) { + my $herevet = "$here\n" . cat_vet($rawline) . "\n"; + ERROR("DOS line endings\n" . $herevet); + + } elsif ($rawline =~ /^\+.*\S\s+$/ || $rawline =~ /^\+\s+$/) { + my $herevet = "$here\n" . cat_vet($rawline) . "\n"; + ERROR("trailing whitespace\n" . $herevet); + $rpt_cleaners = 1; + } + +# check for Kconfig help text having a real description +# Only applies when adding the entry originally, after that we do not have +# sufficient context to determine whether it is indeed long enough. + if ($realfile =~ /Kconfig/ && + $line =~ /\+\s*(?:---)?help(?:---)?$/) { + my $length = 0; + my $cnt = $realcnt; + my $ln = $linenr + 1; + my $f; + my $is_end = 0; + while ($cnt > 0 && defined $lines[$ln - 1]) { + $f = $lines[$ln - 1]; + $cnt-- if ($lines[$ln - 1] !~ /^-/); + $is_end = $lines[$ln - 1] =~ /^\+/; + $ln++; + + next if ($f =~ /^-/); + $f =~ s/^.//; + $f =~ s/#.*//; + $f =~ s/^\s+//; + next if ($f =~ /^$/); + if ($f =~ /^\s*config\s/) { + $is_end = 1; + last; + } + $length++; + } + WARN("please write a paragraph that describes the config symbol fully\n" . $herecurr) if ($is_end && $length < 4); + #print "is_end<$is_end> length<$length>\n"; + } + +# check we are in a valid source file if not then ignore this hunk + next if ($realfile !~ /\.(h|c|s|S|pl|sh)$/); + +#80 column limit + if ($line =~ /^\+/ && $prevrawline !~ /\/\*\*/ && + $rawline !~ /^.\s*\*\s*\@$Ident\s/ && + !($line =~ /^\+\s*$logFunctions\s*\(\s*(?:(KERN_\S+\s*|[^"]*))?"[X\t]*"\s*(?:,|\)\s*;)\s*$/ || + $line =~ /^\+\s*"[^"]*"\s*(?:\s*|,|\)\s*;)\s*$/) && + $length > 80) + { + WARN("line over 80 characters\n" . $herecurr); + } + +# check for spaces before a quoted newline + if ($rawline =~ /^.*\".*\s\\n/) { + WARN("unnecessary whitespace before a quoted newline\n" . $herecurr); + } + +# check for adding lines without a newline. + if ($line =~ /^\+/ && defined $lines[$linenr] && $lines[$linenr] =~ /^\\ No newline at end of file/) { + WARN("adding a line without newline at end of file\n" . $herecurr); + } + +# Blackfin: use hi/lo macros + if ($realfile =~ m@arch/blackfin/.*\.S$@) { + if ($line =~ /\.[lL][[:space:]]*=.*&[[:space:]]*0x[fF][fF][fF][fF]/) { + my $herevet = "$here\n" . cat_vet($line) . "\n"; + ERROR("use the LO() macro, not (... & 0xFFFF)\n" . $herevet); + } + if ($line =~ /\.[hH][[:space:]]*=.*>>[[:space:]]*16/) { + my $herevet = "$here\n" . cat_vet($line) . "\n"; + ERROR("use the HI() macro, not (... >> 16)\n" . $herevet); + } + } + +# check we are in a valid source file C or perl if not then ignore this hunk + next if ($realfile !~ /\.(h|c|pl)$/); + +# in QEMU, no tabs are allowed + if ($rawline =~ /\t/) { + my $herevet = "$here\n" . cat_vet($rawline) . "\n"; + ERROR("code indent should never use tabs\n" . $herevet); + $rpt_cleaners = 1; + } + +# check we are in a valid C source file if not then ignore this hunk + next if ($realfile !~ /\.(h|c)$/); + +# check for RCS/CVS revision markers + if ($rawline =~ /^\+.*\$(Revision|Log|Id)(?:\$|)/) { + WARN("CVS style keyword markers, these will _not_ be updated\n". $herecurr); + } + +# Blackfin: don't use __builtin_bfin_[cs]sync + if ($line =~ /__builtin_bfin_csync/) { + my $herevet = "$here\n" . cat_vet($line) . "\n"; + ERROR("use the CSYNC() macro in asm/blackfin.h\n" . $herevet); + } + if ($line =~ /__builtin_bfin_ssync/) { + my $herevet = "$here\n" . cat_vet($line) . "\n"; + ERROR("use the SSYNC() macro in asm/blackfin.h\n" . $herevet); + } + +# Check for potential 'bare' types + my ($stat, $cond, $line_nr_next, $remain_next, $off_next, + $realline_next); + if ($realcnt && $line =~ /.\s*\S/) { + ($stat, $cond, $line_nr_next, $remain_next, $off_next) = + ctx_statement_block($linenr, $realcnt, 0); + $stat =~ s/\n./\n /g; + $cond =~ s/\n./\n /g; + + # Find the real next line. + $realline_next = $line_nr_next; + if (defined $realline_next && + (!defined $lines[$realline_next - 1] || + substr($lines[$realline_next - 1], $off_next) =~ /^\s*$/)) { + $realline_next++; + } + + my $s = $stat; + $s =~ s/{.*$//s; + + # Ignore goto labels. + if ($s =~ /$Ident:\*$/s) { + + # Ignore functions being called + } elsif ($s =~ /^.\s*$Ident\s*\(/s) { + + } elsif ($s =~ /^.\s*else\b/s) { + + # declarations always start with types + } elsif ($prev_values eq 'E' && $s =~ /^.\s*(?:$Storage\s+)?(?:$Inline\s+)?(?:const\s+)?((?:\s*$Ident)+?)\b(?:\s+$Sparse)?\s*\**\s*(?:$Ident|\(\*[^\)]*\))(?:\s*$Modifier)?\s*(?:;|=|,|\()/s) { + my $type = $1; + $type =~ s/\s+/ /g; + possible($type, "A:" . $s); + + # definitions in global scope can only start with types + } elsif ($s =~ /^.(?:$Storage\s+)?(?:$Inline\s+)?(?:const\s+)?($Ident)\b\s*(?!:)/s) { + possible($1, "B:" . $s); + } + + # any (foo ... *) is a pointer cast, and foo is a type + while ($s =~ /\(($Ident)(?:\s+$Sparse)*[\s\*]+\s*\)/sg) { + possible($1, "C:" . $s); + } + + # Check for any sort of function declaration. + # int foo(something bar, other baz); + # void (*store_gdt)(x86_descr_ptr *); + if ($prev_values eq 'E' && $s =~ /^(.(?:typedef\s*)?(?:(?:$Storage|$Inline)\s*)*\s*$Type\s*(?:\b$Ident|\(\*\s*$Ident\))\s*)\(/s) { + my ($name_len) = length($1); + + my $ctx = $s; + substr($ctx, 0, $name_len + 1, ''); + $ctx =~ s/\)[^\)]*$//; + + for my $arg (split(/\s*,\s*/, $ctx)) { + if ($arg =~ /^(?:const\s+)?($Ident)(?:\s+$Sparse)*\s*\**\s*(:?\b$Ident)?$/s || $arg =~ /^($Ident)$/s) { + + possible($1, "D:" . $s); + } + } + } + + } + +# +# Checks which may be anchored in the context. +# + +# Check for switch () and associated case and default +# statements should be at the same indent. + if ($line=~/\bswitch\s*\(.*\)/) { + my $err = ''; + my $sep = ''; + my @ctx = ctx_block_outer($linenr, $realcnt); + shift(@ctx); + for my $ctx (@ctx) { + my ($clen, $cindent) = line_stats($ctx); + if ($ctx =~ /^\+\s*(case\s+|default:)/ && + $indent != $cindent) { + $err .= "$sep$ctx\n"; + $sep = ''; + } else { + $sep = "[...]\n"; + } + } + if ($err ne '') { + ERROR("switch and case should be at the same indent\n$hereline$err"); + } + } + +# if/while/etc brace do not go on next line, unless defining a do while loop, +# or if that brace on the next line is for something else + if ($line =~ /(.*)\b((?:if|while|for|switch)\s*\(|do\b|else\b)/ && $line !~ /^.\s*\#/) { + my $pre_ctx = "$1$2"; + + my ($level, @ctx) = ctx_statement_level($linenr, $realcnt, 0); + my $ctx_cnt = $realcnt - $#ctx - 1; + my $ctx = join("\n", @ctx); + + my $ctx_ln = $linenr; + my $ctx_skip = $realcnt; + + while ($ctx_skip > $ctx_cnt || ($ctx_skip == $ctx_cnt && + defined $lines[$ctx_ln - 1] && + $lines[$ctx_ln - 1] =~ /^-/)) { + ##print "SKIP<$ctx_skip> CNT<$ctx_cnt>\n"; + $ctx_skip-- if (!defined $lines[$ctx_ln - 1] || $lines[$ctx_ln - 1] !~ /^-/); + $ctx_ln++; + } + + #print "realcnt<$realcnt> ctx_cnt<$ctx_cnt>\n"; + #print "pre<$pre_ctx>\nline<$line>\nctx<$ctx>\nnext<$lines[$ctx_ln - 1]>\n"; + + if ($ctx !~ /{\s*/ && defined($lines[$ctx_ln -1]) && $lines[$ctx_ln - 1] =~ /^\+\s*{/) { + ERROR("that open brace { should be on the previous line\n" . + "$here\n$ctx\n$rawlines[$ctx_ln - 1]\n"); + } + if ($level == 0 && $pre_ctx !~ /}\s*while\s*\($/ && + $ctx =~ /\)\s*\;\s*$/ && + defined $lines[$ctx_ln - 1]) + { + my ($nlength, $nindent) = line_stats($lines[$ctx_ln - 1]); + if ($nindent > $indent) { + WARN("trailing semicolon indicates no statements, indent implies otherwise\n" . + "$here\n$ctx\n$rawlines[$ctx_ln - 1]\n"); + } + } + } + +# Check relative indent for conditionals and blocks. + if ($line =~ /\b(?:(?:if|while|for)\s*\(|do\b)/ && $line !~ /^.\s*#/ && $line !~ /\}\s*while\s*/) { + my ($s, $c) = ($stat, $cond); + + substr($s, 0, length($c), ''); + + # Make sure we remove the line prefixes as we have + # none on the first line, and are going to readd them + # where necessary. + $s =~ s/\n./\n/gs; + + # Find out how long the conditional actually is. + my @newlines = ($c =~ /\n/gs); + my $cond_lines = 1 + $#newlines; + + # We want to check the first line inside the block + # starting at the end of the conditional, so remove: + # 1) any blank line termination + # 2) any opening brace { on end of the line + # 3) any do (...) { + my $continuation = 0; + my $check = 0; + $s =~ s/^.*\bdo\b//; + $s =~ s/^\s*{//; + if ($s =~ s/^\s*\\//) { + $continuation = 1; + } + if ($s =~ s/^\s*?\n//) { + $check = 1; + $cond_lines++; + } + + # Also ignore a loop construct at the end of a + # preprocessor statement. + if (($prevline =~ /^.\s*#\s*define\s/ || + $prevline =~ /\\\s*$/) && $continuation == 0) { + $check = 0; + } + + my $cond_ptr = -1; + $continuation = 0; + while ($cond_ptr != $cond_lines) { + $cond_ptr = $cond_lines; + + # If we see an #else/#elif then the code + # is not linear. + if ($s =~ /^\s*\#\s*(?:else|elif)/) { + $check = 0; + } + + # Ignore: + # 1) blank lines, they should be at 0, + # 2) preprocessor lines, and + # 3) labels. + if ($continuation || + $s =~ /^\s*?\n/ || + $s =~ /^\s*#\s*?/ || + $s =~ /^\s*$Ident\s*:/) { + $continuation = ($s =~ /^.*?\\\n/) ? 1 : 0; + if ($s =~ s/^.*?\n//) { + $cond_lines++; + } + } + } + + my (undef, $sindent) = line_stats("+" . $s); + my $stat_real = raw_line($linenr, $cond_lines); + + # Check if either of these lines are modified, else + # this is not this patch's fault. + if (!defined($stat_real) || + $stat !~ /^\+/ && $stat_real !~ /^\+/) { + $check = 0; + } + if (defined($stat_real) && $cond_lines > 1) { + $stat_real = "[...]\n$stat_real"; + } + + #print "line<$line> prevline<$prevline> indent<$indent> sindent<$sindent> check<$check> continuation<$continuation> s<$s> cond_lines<$cond_lines> stat_real<$stat_real> stat<$stat>\n"; + + if ($check && (($sindent % 4) != 0 || + ($sindent <= $indent && $s ne ''))) { + WARN("suspect code indent for conditional statements ($indent, $sindent)\n" . $herecurr . "$stat_real\n"); + } + } + + # Track the 'values' across context and added lines. + my $opline = $line; $opline =~ s/^./ /; + my ($curr_values, $curr_vars) = + annotate_values($opline . "\n", $prev_values); + $curr_values = $prev_values . $curr_values; + if ($dbg_values) { + my $outline = $opline; $outline =~ s/\t/ /g; + print "$linenr > .$outline\n"; + print "$linenr > $curr_values\n"; + print "$linenr > $curr_vars\n"; + } + $prev_values = substr($curr_values, -1); + +#ignore lines not being added + if ($line=~/^[^\+]/) {next;} + +# TEST: allow direct testing of the type matcher. + if ($dbg_type) { + if ($line =~ /^.\s*$Declare\s*$/) { + ERROR("TEST: is type\n" . $herecurr); + } elsif ($dbg_type > 1 && $line =~ /^.+($Declare)/) { + ERROR("TEST: is not type ($1 is)\n". $herecurr); + } + next; + } +# TEST: allow direct testing of the attribute matcher. + if ($dbg_attr) { + if ($line =~ /^.\s*$Modifier\s*$/) { + ERROR("TEST: is attr\n" . $herecurr); + } elsif ($dbg_attr > 1 && $line =~ /^.+($Modifier)/) { + ERROR("TEST: is not attr ($1 is)\n". $herecurr); + } + next; + } + +# check for initialisation to aggregates open brace on the next line + if ($line =~ /^.\s*{/ && + $prevline =~ /(?:^|[^=])=\s*$/) { + ERROR("that open brace { should be on the previous line\n" . $hereprev); + } + +# +# Checks which are anchored on the added line. +# + +# check for malformed paths in #include statements (uses RAW line) + if ($rawline =~ m{^.\s*\#\s*include\s+[<"](.*)[">]}) { + my $path = $1; + if ($path =~ m{//}) { + ERROR("malformed #include filename\n" . + $herecurr); + } + } + +# no C99 // comments + if ($line =~ m{//}) { + ERROR("do not use C99 // comments\n" . $herecurr); + } + # Remove C99 comments. + $line =~ s@//.*@@; + $opline =~ s@//.*@@; + +# EXPORT_SYMBOL should immediately follow the thing it is exporting, consider +# the whole statement. +#print "APW <$lines[$realline_next - 1]>\n"; + if (defined $realline_next && + exists $lines[$realline_next - 1] && + !defined $suppress_export{$realline_next} && + ($lines[$realline_next - 1] =~ /EXPORT_SYMBOL.*\((.*)\)/ || + $lines[$realline_next - 1] =~ /EXPORT_UNUSED_SYMBOL.*\((.*)\)/)) { + # Handle definitions which produce identifiers with + # a prefix: + # XXX(foo); + # EXPORT_SYMBOL(something_foo); + my $name = $1; + if ($stat =~ /^.([A-Z_]+)\s*\(\s*($Ident)/ && + $name =~ /^${Ident}_$2/) { +#print "FOO C name<$name>\n"; + $suppress_export{$realline_next} = 1; + + } elsif ($stat !~ /(?: + \n.}\s*$| + ^.DEFINE_$Ident\(\Q$name\E\)| + ^.DECLARE_$Ident\(\Q$name\E\)| + ^.LIST_HEAD\(\Q$name\E\)| + ^.(?:$Storage\s+)?$Type\s*\(\s*\*\s*\Q$name\E\s*\)\s*\(| + \b\Q$name\E(?:\s+$Attribute)*\s*(?:;|=|\[|\() + )/x) { +#print "FOO A<$lines[$realline_next - 1]> stat<$stat> name<$name>\n"; + $suppress_export{$realline_next} = 2; + } else { + $suppress_export{$realline_next} = 1; + } + } + if (!defined $suppress_export{$linenr} && + $prevline =~ /^.\s*$/ && + ($line =~ /EXPORT_SYMBOL.*\((.*)\)/ || + $line =~ /EXPORT_UNUSED_SYMBOL.*\((.*)\)/)) { +#print "FOO B <$lines[$linenr - 1]>\n"; + $suppress_export{$linenr} = 2; + } + if (defined $suppress_export{$linenr} && + $suppress_export{$linenr} == 2) { + WARN("EXPORT_SYMBOL(foo); should immediately follow its function/variable\n" . $herecurr); + } + +# check for global initialisers. + if ($line =~ /^.$Type\s*$Ident\s*(?:\s+$Modifier)*\s*=\s*(0|NULL|false)\s*;/) { + ERROR("do not initialise globals to 0 or NULL\n" . + $herecurr); + } +# check for static initialisers. + if ($line =~ /\bstatic\s.*=\s*(0|NULL|false)\s*;/) { + ERROR("do not initialise statics to 0 or NULL\n" . + $herecurr); + } + +# * goes on variable not on type + # (char*[ const]) + if ($line =~ m{\($NonptrType(\s*(?:$Modifier\b\s*|\*\s*)+)\)}) { + my ($from, $to) = ($1, $1); + + # Should start with a space. + $to =~ s/^(\S)/ $1/; + # Should not end with a space. + $to =~ s/\s+$//; + # '*'s should not have spaces between. + while ($to =~ s/\*\s+\*/\*\*/) { + } + + #print "from<$from> to<$to>\n"; + if ($from ne $to) { + ERROR("\"(foo$from)\" should be \"(foo$to)\"\n" . $herecurr); + } + } elsif ($line =~ m{\b$NonptrType(\s*(?:$Modifier\b\s*|\*\s*)+)($Ident)}) { + my ($from, $to, $ident) = ($1, $1, $2); + + # Should start with a space. + $to =~ s/^(\S)/ $1/; + # Should not end with a space. + $to =~ s/\s+$//; + # '*'s should not have spaces between. + while ($to =~ s/\*\s+\*/\*\*/) { + } + # Modifiers should have spaces. + $to =~ s/(\b$Modifier$)/$1 /; + + #print "from<$from> to<$to> ident<$ident>\n"; + if ($from ne $to && $ident !~ /^$Modifier$/) { + ERROR("\"foo${from}bar\" should be \"foo${to}bar\"\n" . $herecurr); + } + } + +# # no BUG() or BUG_ON() +# if ($line =~ /\b(BUG|BUG_ON)\b/) { +# print "Try to use WARN_ON & Recovery code rather than BUG() or BUG_ON()\n"; +# print "$herecurr"; +# $clean = 0; +# } + + if ($line =~ /\bLINUX_VERSION_CODE\b/) { + WARN("LINUX_VERSION_CODE should be avoided, code should be for the version to which it is merged\n" . $herecurr); + } + +# printk should use KERN_* levels. Note that follow on printk's on the +# same line do not need a level, so we use the current block context +# to try and find and validate the current printk. In summary the current +# printk includes all preceeding printk's which have no newline on the end. +# we assume the first bad printk is the one to report. + if ($line =~ /\bprintk\((?!KERN_)\s*"/) { + my $ok = 0; + for (my $ln = $linenr - 1; $ln >= $first_line; $ln--) { + #print "CHECK<$lines[$ln - 1]\n"; + # we have a preceeding printk if it ends + # with "\n" ignore it, else it is to blame + if ($lines[$ln - 1] =~ m{\bprintk\(}) { + if ($rawlines[$ln - 1] !~ m{\\n"}) { + $ok = 1; + } + last; + } + } + if ($ok == 0) { + WARN("printk() should include KERN_ facility level\n" . $herecurr); + } + } + +# function brace can't be on same line, except for #defines of do while, +# or if closed on same line + if (($line=~/$Type\s*$Ident\(.*\).*\s{/) and + !($line=~/\#\s*define.*do\s{/) and !($line=~/}/)) { + ERROR("open brace '{' following function declarations go on the next line\n" . $herecurr); + } + +# open braces for enum, union and struct go on the same line. + if ($line =~ /^.\s*{/ && + $prevline =~ /^.\s*(?:typedef\s+)?(enum|union|struct)(?:\s+$Ident)?\s*$/) { + ERROR("open brace '{' following $1 go on the same line\n" . $hereprev); + } + +# missing space after union, struct or enum definition + if ($line =~ /^.\s*(?:typedef\s+)?(enum|union|struct)(?:\s+$Ident)?(?:\s+$Ident)?[=\{]/) { + WARN("missing space after $1 definition\n" . $herecurr); + } + +# check for spacing round square brackets; allowed: +# 1. with a type on the left -- int [] a; +# 2. at the beginning of a line for slice initialisers -- [0...10] = 5, +# 3. inside a curly brace -- = { [0...10] = 5 } + while ($line =~ /(.*?\s)\[/g) { + my ($where, $prefix) = ($-[1], $1); + if ($prefix !~ /$Type\s+$/ && + ($where != 0 || $prefix !~ /^.\s+$/) && + $prefix !~ /{\s+$/) { + ERROR("space prohibited before open square bracket '['\n" . $herecurr); + } + } + +# check for spaces between functions and their parentheses. + while ($line =~ /($Ident)\s+\(/g) { + my $name = $1; + my $ctx_before = substr($line, 0, $-[1]); + my $ctx = "$ctx_before$name"; + + # Ignore those directives where spaces _are_ permitted. + if ($name =~ /^(?: + if|for|while|switch|return|case| + volatile|__volatile__| + __attribute__|format|__extension__| + asm|__asm__)$/x) + { + + # cpp #define statements have non-optional spaces, ie + # if there is a space between the name and the open + # parenthesis it is simply not a parameter group. + } elsif ($ctx_before =~ /^.\s*\#\s*define\s*$/) { + + # cpp #elif statement condition may start with a ( + } elsif ($ctx =~ /^.\s*\#\s*elif\s*$/) { + + # If this whole things ends with a type its most + # likely a typedef for a function. + } elsif ($ctx =~ /$Type$/) { + + } else { + WARN("space prohibited between function name and open parenthesis '('\n" . $herecurr); + } + } +# Check operator spacing. + if (!($line=~/\#\s*include/)) { + my $ops = qr{ + <<=|>>=|<=|>=|==|!=| + \+=|-=|\*=|\/=|%=|\^=|\|=|&=| + =>|->|<<|>>|<|>|=|!|~| + &&|\|\||,|\^|\+\+|--|&|\||\+|-|\*|\/|%| + \?|: + }x; + my @elements = split(/($ops|;)/, $opline); + my $off = 0; + + my $blank = copy_spacing($opline); + + for (my $n = 0; $n < $#elements; $n += 2) { + $off += length($elements[$n]); + + # Pick up the preceeding and succeeding characters. + my $ca = substr($opline, 0, $off); + my $cc = ''; + if (length($opline) >= ($off + length($elements[$n + 1]))) { + $cc = substr($opline, $off + length($elements[$n + 1])); + } + my $cb = "$ca$;$cc"; + + my $a = ''; + $a = 'V' if ($elements[$n] ne ''); + $a = 'W' if ($elements[$n] =~ /\s$/); + $a = 'C' if ($elements[$n] =~ /$;$/); + $a = 'B' if ($elements[$n] =~ /(\[|\()$/); + $a = 'O' if ($elements[$n] eq ''); + $a = 'E' if ($ca =~ /^\s*$/); + + my $op = $elements[$n + 1]; + + my $c = ''; + if (defined $elements[$n + 2]) { + $c = 'V' if ($elements[$n + 2] ne ''); + $c = 'W' if ($elements[$n + 2] =~ /^\s/); + $c = 'C' if ($elements[$n + 2] =~ /^$;/); + $c = 'B' if ($elements[$n + 2] =~ /^(\)|\]|;)/); + $c = 'O' if ($elements[$n + 2] eq ''); + $c = 'E' if ($elements[$n + 2] =~ /^\s*\\$/); + } else { + $c = 'E'; + } + + my $ctx = "${a}x${c}"; + + my $at = "(ctx:$ctx)"; + + my $ptr = substr($blank, 0, $off) . "^"; + my $hereptr = "$hereline$ptr\n"; + + # Pull out the value of this operator. + my $op_type = substr($curr_values, $off + 1, 1); + + # Get the full operator variant. + my $opv = $op . substr($curr_vars, $off, 1); + + # Ignore operators passed as parameters. + if ($op_type ne 'V' && + $ca =~ /\s$/ && $cc =~ /^\s*,/) { + +# # Ignore comments +# } elsif ($op =~ /^$;+$/) { + + # ; should have either the end of line or a space or \ after it + } elsif ($op eq ';') { + if ($ctx !~ /.x[WEBC]/ && + $cc !~ /^\\/ && $cc !~ /^;/) { + ERROR("space required after that '$op' $at\n" . $hereptr); + } + + # // is a comment + } elsif ($op eq '//') { + + # No spaces for: + # -> + # : when part of a bitfield + } elsif ($op eq '->' || $opv eq ':B') { + if ($ctx =~ /Wx.|.xW/) { + ERROR("spaces prohibited around that '$op' $at\n" . $hereptr); + } + + # , must have a space on the right. + } elsif ($op eq ',') { + if ($ctx !~ /.x[WEC]/ && $cc !~ /^}/) { + ERROR("space required after that '$op' $at\n" . $hereptr); + } + + # '*' as part of a type definition -- reported already. + } elsif ($opv eq '*_') { + #warn "'*' is part of type\n"; + + # unary operators should have a space before and + # none after. May be left adjacent to another + # unary operator, or a cast + } elsif ($op eq '!' || $op eq '~' || + $opv eq '*U' || $opv eq '-U' || + $opv eq '&U' || $opv eq '&&U') { + if ($ctx !~ /[WEBC]x./ && $ca !~ /(?:\)|!|~|\*|-|\&|\||\+\+|\-\-|\{)$/) { + ERROR("space required before that '$op' $at\n" . $hereptr); + } + if ($op eq '*' && $cc =~/\s*$Modifier\b/) { + # A unary '*' may be const + + } elsif ($ctx =~ /.xW/) { + ERROR("space prohibited after that '$op' $at\n" . $hereptr); + } + + # unary ++ and unary -- are allowed no space on one side. + } elsif ($op eq '++' or $op eq '--') { + if ($ctx !~ /[WEOBC]x[^W]/ && $ctx !~ /[^W]x[WOBEC]/) { + ERROR("space required one side of that '$op' $at\n" . $hereptr); + } + if ($ctx =~ /Wx[BE]/ || + ($ctx =~ /Wx./ && $cc =~ /^;/)) { + ERROR("space prohibited before that '$op' $at\n" . $hereptr); + } + if ($ctx =~ /ExW/) { + ERROR("space prohibited after that '$op' $at\n" . $hereptr); + } + + + # << and >> may either have or not have spaces both sides + } elsif ($op eq '<<' or $op eq '>>' or + $op eq '&' or $op eq '^' or $op eq '|' or + $op eq '+' or $op eq '-' or + $op eq '*' or $op eq '/' or + $op eq '%') + { + if ($ctx =~ /Wx[^WCE]|[^WCE]xW/) { + ERROR("need consistent spacing around '$op' $at\n" . + $hereptr); + } + + # A colon needs no spaces before when it is + # terminating a case value or a label. + } elsif ($opv eq ':C' || $opv eq ':L') { + if ($ctx =~ /Wx./) { + ERROR("space prohibited before that '$op' $at\n" . $hereptr); + } + + # All the others need spaces both sides. + } elsif ($ctx !~ /[EWC]x[CWE]/) { + my $ok = 0; + + # Ignore email addresses + if (($op eq '<' && + $cc =~ /^\S+\@\S+>/) || + ($op eq '>' && + $ca =~ /<\S+\@\S+$/)) + { + $ok = 1; + } + + # Ignore ?: + if (($opv eq ':O' && $ca =~ /\?$/) || + ($op eq '?' && $cc =~ /^:/)) { + $ok = 1; + } + + if ($ok == 0) { + ERROR("spaces required around that '$op' $at\n" . $hereptr); + } + } + $off += length($elements[$n + 1]); + } + } + +# check for multiple assignments + if ($line =~ /^.\s*$Lval\s*=\s*$Lval\s*=(?!=)/) { + CHK("multiple assignments should be avoided\n" . $herecurr); + } + +## # check for multiple declarations, allowing for a function declaration +## # continuation. +## if ($line =~ /^.\s*$Type\s+$Ident(?:\s*=[^,{]*)?\s*,\s*$Ident.*/ && +## $line !~ /^.\s*$Type\s+$Ident(?:\s*=[^,{]*)?\s*,\s*$Type\s*$Ident.*/) { +## +## # Remove any bracketed sections to ensure we do not +## # falsly report the parameters of functions. +## my $ln = $line; +## while ($ln =~ s/\([^\(\)]*\)//g) { +## } +## if ($ln =~ /,/) { +## WARN("declaring multiple variables together should be avoided\n" . $herecurr); +## } +## } + +#need space before brace following if, while, etc + if (($line =~ /\(.*\){/ && $line !~ /\($Type\){/) || + $line =~ /do{/) { + ERROR("space required before the open brace '{'\n" . $herecurr); + } + +# closing brace should have a space following it when it has anything +# on the line + if ($line =~ /}(?!(?:,|;|\)))\S/) { + ERROR("space required after that close brace '}'\n" . $herecurr); + } + +# check spacing on square brackets + if ($line =~ /\[\s/ && $line !~ /\[\s*$/) { + ERROR("space prohibited after that open square bracket '['\n" . $herecurr); + } + if ($line =~ /\s\]/) { + ERROR("space prohibited before that close square bracket ']'\n" . $herecurr); + } + +# check spacing on parentheses + if ($line =~ /\(\s/ && $line !~ /\(\s*(?:\\)?$/ && + $line !~ /for\s*\(\s+;/) { + ERROR("space prohibited after that open parenthesis '('\n" . $herecurr); + } + if ($line =~ /(\s+)\)/ && $line !~ /^.\s*\)/ && + $line !~ /for\s*\(.*;\s+\)/ && + $line !~ /:\s+\)/) { + ERROR("space prohibited before that close parenthesis ')'\n" . $herecurr); + } + +#goto labels aren't indented, allow a single space however + if ($line=~/^.\s+[A-Za-z\d_]+:(?![0-9]+)/ and + !($line=~/^. [A-Za-z\d_]+:/) and !($line=~/^.\s+default:/)) { + WARN("labels should not be indented\n" . $herecurr); + } + +# Return is not a function. + if (defined($stat) && $stat =~ /^.\s*return(\s*)(\(.*);/s) { + my $spacing = $1; + my $value = $2; + + # Flatten any parentheses + $value =~ s/\(/ \(/g; + $value =~ s/\)/\) /g; + while ($value =~ s/\[[^\{\}]*\]/1/ || + $value !~ /(?:$Ident|-?$Constant)\s* + $Compare\s* + (?:$Ident|-?$Constant)/x && + $value =~ s/\([^\(\)]*\)/1/) { + } +#print "value<$value>\n"; + if ($value =~ /^\s*(?:$Ident|-?$Constant)\s*$/) { + ERROR("return is not a function, parentheses are not required\n" . $herecurr); + + } elsif ($spacing !~ /\s+/) { + ERROR("space required before the open parenthesis '('\n" . $herecurr); + } + } +# Return of what appears to be an errno should normally be -'ve + if ($line =~ /^.\s*return\s*(E[A-Z]*)\s*;/) { + my $name = $1; + if ($name ne 'EOF' && $name ne 'ERROR') { + CHK("return of an errno should typically be -ve (return -$1)\n" . $herecurr); + } + } + +# Need a space before open parenthesis after if, while etc + if ($line=~/\b(if|while|for|switch)\(/) { + ERROR("space required before the open parenthesis '('\n" . $herecurr); + } + +# Check for illegal assignment in if conditional -- and check for trailing +# statements after the conditional. + if ($line =~ /do\s*(?!{)/) { + my ($stat_next) = ctx_statement_block($line_nr_next, + $remain_next, $off_next); + $stat_next =~ s/\n./\n /g; + ##print "stat<$stat> stat_next<$stat_next>\n"; + + if ($stat_next =~ /^\s*while\b/) { + # If the statement carries leading newlines, + # then count those as offsets. + my ($whitespace) = + ($stat_next =~ /^((?:\s*\n[+-])*\s*)/s); + my $offset = + statement_rawlines($whitespace) - 1; + + $suppress_whiletrailers{$line_nr_next + + $offset} = 1; + } + } + if (!defined $suppress_whiletrailers{$linenr} && + $line =~ /\b(?:if|while|for)\s*\(/ && $line !~ /^.\s*#/) { + my ($s, $c) = ($stat, $cond); + + if ($c =~ /\bif\s*\(.*[^<>!=]=[^=].*/s) { + ERROR("do not use assignment in if condition\n" . $herecurr); + } + + # Find out what is on the end of the line after the + # conditional. + substr($s, 0, length($c), ''); + $s =~ s/\n.*//g; + $s =~ s/$;//g; # Remove any comments + if (length($c) && $s !~ /^\s*{?\s*\\*\s*$/ && + $c !~ /}\s*while\s*/) + { + # Find out how long the conditional actually is. + my @newlines = ($c =~ /\n/gs); + my $cond_lines = 1 + $#newlines; + my $stat_real = ''; + + $stat_real = raw_line($linenr, $cond_lines) + . "\n" if ($cond_lines); + if (defined($stat_real) && $cond_lines > 1) { + $stat_real = "[...]\n$stat_real"; + } + + ERROR("trailing statements should be on next line\n" . $herecurr . $stat_real); + } + } + +# Check for bitwise tests written as boolean + if ($line =~ / + (?: + (?:\[|\(|\&\&|\|\|) + \s*0[xX][0-9]+\s* + (?:\&\&|\|\|) + | + (?:\&\&|\|\|) + \s*0[xX][0-9]+\s* + (?:\&\&|\|\||\)|\]) + )/x) + { + WARN("boolean test with hexadecimal, perhaps just 1 \& or \|?\n" . $herecurr); + } + +# if and else should not have general statements after it + if ($line =~ /^.\s*(?:}\s*)?else\b(.*)/) { + my $s = $1; + $s =~ s/$;//g; # Remove any comments + if ($s !~ /^\s*(?:\sif|(?:{|)\s*\\?\s*$)/) { + ERROR("trailing statements should be on next line\n" . $herecurr); + } + } +# if should not continue a brace + if ($line =~ /}\s*if\b/) { + ERROR("trailing statements should be on next line\n" . + $herecurr); + } +# case and default should not have general statements after them + if ($line =~ /^.\s*(?:case\s*.*|default\s*):/g && + $line !~ /\G(?: + (?:\s*$;*)(?:\s*{)?(?:\s*$;*)(?:\s*\\)?\s*$| + \s*return\s+ + )/xg) + { + ERROR("trailing statements should be on next line\n" . $herecurr); + } + + # Check for }else {, these must be at the same + # indent level to be relevant to each other. + if ($prevline=~/}\s*$/ and $line=~/^.\s*else\s*/ and + $previndent == $indent) { + ERROR("else should follow close brace '}'\n" . $hereprev); + } + + if ($prevline=~/}\s*$/ and $line=~/^.\s*while\s*/ and + $previndent == $indent) { + my ($s, $c) = ctx_statement_block($linenr, $realcnt, 0); + + # Find out what is on the end of the line after the + # conditional. + substr($s, 0, length($c), ''); + $s =~ s/\n.*//g; + + if ($s =~ /^\s*;/) { + ERROR("while should follow close brace '}'\n" . $hereprev); + } + } + +#studly caps, commented out until figure out how to distinguish between use of existing and adding new +# if (($line=~/[\w_][a-z\d]+[A-Z]/) and !($line=~/print/)) { +# print "No studly caps, use _\n"; +# print "$herecurr"; +# $clean = 0; +# } + +#no spaces allowed after \ in define + if ($line=~/\#\s*define.*\\\s$/) { + WARN("Whitepspace after \\ makes next lines useless\n" . $herecurr); + } + +#warn if is #included and is available (uses RAW line) + if ($tree && $rawline =~ m{^.\s*\#\s*include\s*\}) { + my $file = "$1.h"; + my $checkfile = "include/linux/$file"; + if (-f "$root/$checkfile" && + $realfile ne $checkfile && + $1 !~ /$allowed_asm_includes/) + { + if ($realfile =~ m{^arch/}) { + CHK("Consider using #include instead of \n" . $herecurr); + } else { + WARN("Use #include instead of \n" . $herecurr); + } + } + } + +# multi-statement macros should be enclosed in a do while loop, grab the +# first statement and ensure its the whole macro if its not enclosed +# in a known good container + if ($realfile !~ m@/vmlinux.lds.h$@ && + $line =~ /^.\s*\#\s*define\s*$Ident(\()?/) { + my $ln = $linenr; + my $cnt = $realcnt; + my ($off, $dstat, $dcond, $rest); + my $ctx = ''; + + my $args = defined($1); + + # Find the end of the macro and limit our statement + # search to that. + while ($cnt > 0 && defined $lines[$ln - 1] && + $lines[$ln - 1] =~ /^(?:-|..*\\$)/) + { + $ctx .= $rawlines[$ln - 1] . "\n"; + $cnt-- if ($lines[$ln - 1] !~ /^-/); + $ln++; + } + $ctx .= $rawlines[$ln - 1]; + + ($dstat, $dcond, $ln, $cnt, $off) = + ctx_statement_block($linenr, $ln - $linenr + 1, 0); + #print "dstat<$dstat> dcond<$dcond> cnt<$cnt> off<$off>\n"; + #print "LINE<$lines[$ln-1]> len<" . length($lines[$ln-1]) . "\n"; + + # Extract the remainder of the define (if any) and + # rip off surrounding spaces, and trailing \'s. + $rest = ''; + while ($off != 0 || ($cnt > 0 && $rest =~ /\\\s*$/)) { + #print "ADDING cnt<$cnt> $off <" . substr($lines[$ln - 1], $off) . "> rest<$rest>\n"; + if ($off != 0 || $lines[$ln - 1] !~ /^-/) { + $rest .= substr($lines[$ln - 1], $off) . "\n"; + $cnt--; + } + $ln++; + $off = 0; + } + $rest =~ s/\\\n.//g; + $rest =~ s/^\s*//s; + $rest =~ s/\s*$//s; + + # Clean up the original statement. + if ($args) { + substr($dstat, 0, length($dcond), ''); + } else { + $dstat =~ s/^.\s*\#\s*define\s+$Ident\s*//; + } + $dstat =~ s/$;//g; + $dstat =~ s/\\\n.//g; + $dstat =~ s/^\s*//s; + $dstat =~ s/\s*$//s; + + # Flatten any parentheses and braces + while ($dstat =~ s/\([^\(\)]*\)/1/ || + $dstat =~ s/\{[^\{\}]*\}/1/ || + $dstat =~ s/\[[^\{\}]*\]/1/) + { + } + + my $exceptions = qr{ + $Declare| + module_param_named| + MODULE_PARAM_DESC| + DECLARE_PER_CPU| + DEFINE_PER_CPU| + __typeof__\(| + union| + struct| + \.$Ident\s*=\s*| + ^\"|\"$ + }x; + #print "REST<$rest> dstat<$dstat> ctx<$ctx>\n"; + if ($rest ne '' && $rest ne ',') { + if ($rest !~ /while\s*\(/ && + $dstat !~ /$exceptions/) + { + ERROR("Macros with multiple statements should be enclosed in a do - while loop\n" . "$here\n$ctx\n"); + } + + } elsif ($ctx !~ /;/) { + if ($dstat ne '' && + $dstat !~ /^(?:$Ident|-?$Constant)$/ && + $dstat !~ /$exceptions/ && + $dstat !~ /^\.$Ident\s*=/ && + $dstat =~ /$Operators/) + { + ERROR("Macros with complex values should be enclosed in parenthesis\n" . "$here\n$ctx\n"); + } + } + } + +# make sure symbols are always wrapped with VMLINUX_SYMBOL() ... +# all assignments may have only one of the following with an assignment: +# . +# ALIGN(...) +# VMLINUX_SYMBOL(...) + if ($realfile eq 'vmlinux.lds.h' && $line =~ /(?:(?:^|\s)$Ident\s*=|=\s*$Ident(?:\s|$))/) { + WARN("vmlinux.lds.h needs VMLINUX_SYMBOL() around C-visible symbols\n" . $herecurr); + } + +# check for missing bracing round if etc + if ($line =~ /(^.*)\bif\b/ && $line !~ /\#\s*if/) { + my ($level, $endln, @chunks) = + ctx_statement_full($linenr, $realcnt, 1); + #print "chunks<$#chunks> linenr<$linenr> endln<$endln> level<$level>\n"; + #print "APW: <<$chunks[1][0]>><<$chunks[1][1]>>\n"; + if ($#chunks >= 0 && $level == 0) { + my $allowed = 0; + my $seen = 0; + my $herectx = $here . "\n"; + my $ln = $linenr - 1; + for my $chunk (@chunks) { + my ($cond, $block) = @{$chunk}; + + # If the condition carries leading newlines, then count those as offsets. + my ($whitespace) = ($cond =~ /^((?:\s*\n[+-])*\s*)/s); + my $offset = statement_rawlines($whitespace) - 1; + + #print "COND<$cond> whitespace<$whitespace> offset<$offset>\n"; + + # We have looked at and allowed this specific line. + $suppress_ifbraces{$ln + $offset} = 1; + + $herectx .= "$rawlines[$ln + $offset]\n[...]\n"; + $ln += statement_rawlines($block) - 1; + + substr($block, 0, length($cond), ''); + + $seen++ if ($block =~ /^\s*{/); + + #print "cond<$cond> block<$block> allowed<$allowed>\n"; + if (statement_lines($cond) > 1) { + #print "APW: ALLOWED: cond<$cond>\n"; + $allowed = 1; + } + if ($block =~/\b(?:if|for|while)\b/) { + #print "APW: ALLOWED: block<$block>\n"; + $allowed = 1; + } + if (statement_block_size($block) > 1) { + #print "APW: ALLOWED: lines block<$block>\n"; + $allowed = 1; + } + } + if (!$seen) { + WARN("braces {} are necessary for all arms of this statement\n" . $herectx); + } + } + } + if (!defined $suppress_ifbraces{$linenr - 1} && + $line =~ /\b(if|while|for|else)\b/ && + $line !~ /\#\s*else/) { + my $allowed = 0; + + # Check the pre-context. + if (substr($line, 0, $-[0]) =~ /(\}\s*)$/) { + #print "APW: ALLOWED: pre<$1>\n"; + $allowed = 1; + } + + my ($level, $endln, @chunks) = + ctx_statement_full($linenr, $realcnt, $-[0]); + + # Check the condition. + my ($cond, $block) = @{$chunks[0]}; + #print "CHECKING<$linenr> cond<$cond> block<$block>\n"; + if (defined $cond) { + substr($block, 0, length($cond), ''); + } + if (statement_lines($cond) > 1) { + #print "APW: ALLOWED: cond<$cond>\n"; + $allowed = 1; + } + if ($block =~/\b(?:if|for|while)\b/) { + #print "APW: ALLOWED: block<$block>\n"; + $allowed = 1; + } + if (statement_block_size($block) > 1) { + #print "APW: ALLOWED: lines block<$block>\n"; + $allowed = 1; + } + # Check the post-context. + if (defined $chunks[1]) { + my ($cond, $block) = @{$chunks[1]}; + if (defined $cond) { + substr($block, 0, length($cond), ''); + } + if ($block =~ /^\s*\{/) { + #print "APW: ALLOWED: chunk-1 block<$block>\n"; + $allowed = 1; + } + } + if ($level == 0 && $block !~ /^\s*\{/ && !$allowed) { + my $herectx = $here . "\n";; + my $cnt = statement_rawlines($block); + + for (my $n = 0; $n < $cnt; $n++) { + $herectx .= raw_line($linenr, $n) . "\n";; + } + + WARN("braces {} are necessary even for single statement blocks\n" . $herectx); + } + } + +# don't include deprecated include files (uses RAW line) + for my $inc (@dep_includes) { + if ($rawline =~ m@^.\s*\#\s*include\s*\<$inc>@) { + ERROR("Don't use <$inc>: see Documentation/feature-removal-schedule.txt\n" . $herecurr); + } + } + +# don't use deprecated functions + for my $func (@dep_functions) { + if ($line =~ /\b$func\b/) { + ERROR("Don't use $func(): see Documentation/feature-removal-schedule.txt\n" . $herecurr); + } + } + +# no volatiles please + my $asm_volatile = qr{\b(__asm__|asm)\s+(__volatile__|volatile)\b}; + if ($line =~ /\bvolatile\b/ && $line !~ /$asm_volatile/) { + WARN("Use of volatile is usually wrong: see Documentation/volatile-considered-harmful.txt\n" . $herecurr); + } + +# SPIN_LOCK_UNLOCKED & RW_LOCK_UNLOCKED are deprecated + if ($line =~ /\b(SPIN_LOCK_UNLOCKED|RW_LOCK_UNLOCKED)/) { + ERROR("Use of $1 is deprecated: see Documentation/spinlocks.txt\n" . $herecurr); + } + +# warn about #if 0 + if ($line =~ /^.\s*\#\s*if\s+0\b/) { + CHK("if this code is redundant consider removing it\n" . + $herecurr); + } + +# check for needless kfree() checks + if ($prevline =~ /\bif\s*\(([^\)]*)\)/) { + my $expr = $1; + if ($line =~ /\bkfree\(\Q$expr\E\);/) { + WARN("kfree(NULL) is safe this check is probably not required\n" . $hereprev); + } + } +# check for needless usb_free_urb() checks + if ($prevline =~ /\bif\s*\(([^\)]*)\)/) { + my $expr = $1; + if ($line =~ /\busb_free_urb\(\Q$expr\E\);/) { + WARN("usb_free_urb(NULL) is safe this check is probably not required\n" . $hereprev); + } + } + +# prefer usleep_range over udelay + if ($line =~ /\budelay\s*\(\s*(\w+)\s*\)/) { + # ignore udelay's < 10, however + if (! (($1 =~ /(\d+)/) && ($1 < 10)) ) { + CHK("usleep_range is preferred over udelay; see Documentation/timers/timers-howto.txt\n" . $line); + } + } + +# warn about unexpectedly long msleep's + if ($line =~ /\bmsleep\s*\((\d+)\);/) { + if ($1 < 20) { + WARN("msleep < 20ms can sleep for up to 20ms; see Documentation/timers/timers-howto.txt\n" . $line); + } + } + +# warn about #ifdefs in C files +# if ($line =~ /^.\s*\#\s*if(|n)def/ && ($realfile =~ /\.c$/)) { +# print "#ifdef in C files should be avoided\n"; +# print "$herecurr"; +# $clean = 0; +# } + +# warn about spacing in #ifdefs + if ($line =~ /^.\s*\#\s*(ifdef|ifndef|elif)\s\s+/) { + ERROR("exactly one space required after that #$1\n" . $herecurr); + } + +# check for spinlock_t definitions without a comment. + if ($line =~ /^.\s*(struct\s+mutex|spinlock_t)\s+\S+;/ || + $line =~ /^.\s*(DEFINE_MUTEX)\s*\(/) { + my $which = $1; + if (!ctx_has_comment($first_line, $linenr)) { + CHK("$1 definition without comment\n" . $herecurr); + } + } +# check for memory barriers without a comment. + if ($line =~ /\b(mb|rmb|wmb|read_barrier_depends|smp_mb|smp_rmb|smp_wmb|smp_read_barrier_depends)\(/) { + if (!ctx_has_comment($first_line, $linenr)) { + CHK("memory barrier without comment\n" . $herecurr); + } + } +# check of hardware specific defines + if ($line =~ m@^.\s*\#\s*if.*\b(__i386__|__powerpc64__|__sun__|__s390x__)\b@ && $realfile !~ m@include/asm-@) { + CHK("architecture specific defines should be avoided\n" . $herecurr); + } + +# Check that the storage class is at the beginning of a declaration + if ($line =~ /\b$Storage\b/ && $line !~ /^.\s*$Storage\b/) { + WARN("storage class should be at the beginning of the declaration\n" . $herecurr) + } + +# check the location of the inline attribute, that it is between +# storage class and type. + if ($line =~ /\b$Type\s+$Inline\b/ || + $line =~ /\b$Inline\s+$Storage\b/) { + ERROR("inline keyword should sit between storage class and type\n" . $herecurr); + } + +# Check for __inline__ and __inline, prefer inline + if ($line =~ /\b(__inline__|__inline)\b/) { + WARN("plain inline is preferred over $1\n" . $herecurr); + } + +# check for sizeof(&) + if ($line =~ /\bsizeof\s*\(\s*\&/) { + WARN("sizeof(& should be avoided\n" . $herecurr); + } + +# check for new externs in .c files. + if ($realfile =~ /\.c$/ && defined $stat && + $stat =~ /^.\s*(?:extern\s+)?$Type\s+($Ident)(\s*)\(/s) + { + my $function_name = $1; + my $paren_space = $2; + + my $s = $stat; + if (defined $cond) { + substr($s, 0, length($cond), ''); + } + if ($s =~ /^\s*;/ && + $function_name ne 'uninitialized_var') + { + WARN("externs should be avoided in .c files\n" . $herecurr); + } + + if ($paren_space =~ /\n/) { + WARN("arguments for function declarations should follow identifier\n" . $herecurr); + } + + } elsif ($realfile =~ /\.c$/ && defined $stat && + $stat =~ /^.\s*extern\s+/) + { + WARN("externs should be avoided in .c files\n" . $herecurr); + } + +# checks for new __setup's + if ($rawline =~ /\b__setup\("([^"]*)"/) { + my $name = $1; + + if (!grep(/$name/, @setup_docs)) { + CHK("__setup appears un-documented -- check Documentation/kernel-parameters.txt\n" . $herecurr); + } + } + +# check for pointless casting of kmalloc return + if ($line =~ /\*\s*\)\s*k[czm]alloc\b/) { + WARN("unnecessary cast may hide bugs, see http://c-faq.com/malloc/mallocnocast.html\n" . $herecurr); + } + +# check for gcc specific __FUNCTION__ + if ($line =~ /__FUNCTION__/) { + WARN("__func__ should be used instead of gcc specific __FUNCTION__\n" . $herecurr); + } + +# check for semaphores used as mutexes + if ($line =~ /^.\s*(DECLARE_MUTEX|init_MUTEX)\s*\(/) { + WARN("mutexes are preferred for single holder semaphores\n" . $herecurr); + } +# check for semaphores used as mutexes + if ($line =~ /^.\s*init_MUTEX_LOCKED\s*\(/) { + WARN("consider using a completion\n" . $herecurr); + + } +# recommend strict_strto* over simple_strto* + if ($line =~ /\bsimple_(strto.*?)\s*\(/) { + WARN("consider using strict_$1 in preference to simple_$1\n" . $herecurr); + } +# check for __initcall(), use device_initcall() explicitly please + if ($line =~ /^.\s*__initcall\s*\(/) { + WARN("please use device_initcall() instead of __initcall()\n" . $herecurr); + } +# check for various ops structs, ensure they are const. + my $struct_ops = qr{acpi_dock_ops| + address_space_operations| + backlight_ops| + block_device_operations| + dentry_operations| + dev_pm_ops| + dma_map_ops| + extent_io_ops| + file_lock_operations| + file_operations| + hv_ops| + ide_dma_ops| + intel_dvo_dev_ops| + item_operations| + iwl_ops| + kgdb_arch| + kgdb_io| + kset_uevent_ops| + lock_manager_operations| + microcode_ops| + mtrr_ops| + neigh_ops| + nlmsvc_binding| + pci_raw_ops| + pipe_buf_operations| + platform_hibernation_ops| + platform_suspend_ops| + proto_ops| + rpc_pipe_ops| + seq_operations| + snd_ac97_build_ops| + soc_pcmcia_socket_ops| + stacktrace_ops| + sysfs_ops| + tty_operations| + usb_mon_operations| + wd_ops}x; + if ($line !~ /\bconst\b/ && + $line =~ /\bstruct\s+($struct_ops)\b/) { + WARN("struct $1 should normally be const\n" . + $herecurr); + } + +# use of NR_CPUS is usually wrong +# ignore definitions of NR_CPUS and usage to define arrays as likely right + if ($line =~ /\bNR_CPUS\b/ && + $line !~ /^.\s*\s*#\s*if\b.*\bNR_CPUS\b/ && + $line !~ /^.\s*\s*#\s*define\b.*\bNR_CPUS\b/ && + $line !~ /^.\s*$Declare\s.*\[[^\]]*NR_CPUS[^\]]*\]/ && + $line !~ /\[[^\]]*\.\.\.[^\]]*NR_CPUS[^\]]*\]/ && + $line !~ /\[[^\]]*NR_CPUS[^\]]*\.\.\.[^\]]*\]/) + { + WARN("usage of NR_CPUS is often wrong - consider using cpu_possible(), num_possible_cpus(), for_each_possible_cpu(), etc\n" . $herecurr); + } + +# check for %L{u,d,i} in strings + my $string; + while ($line =~ /(?:^|")([X\t]*)(?:"|$)/g) { + $string = substr($rawline, $-[1], $+[1] - $-[1]); + $string =~ s/%%/__/g; + if ($string =~ /(?mutex.\n" . $herecurr); + } + } + } + + # If we have no input at all, then there is nothing to report on + # so just keep quiet. + if ($#rawlines == -1) { + exit(0); + } + + # In mailback mode only produce a report in the negative, for + # things that appear to be patches. + if ($mailback && ($clean == 1 || !$is_patch)) { + exit(0); + } + + # This is not a patch, and we are are in 'no-patch' mode so + # just keep quiet. + if (!$chk_patch && !$is_patch) { + exit(0); + } + + if (!$is_patch) { + ERROR("Does not appear to be a unified-diff format patch\n"); + } + if ($is_patch && $chk_signoff && $signoff == 0) { + ERROR("Missing Signed-off-by: line(s)\n"); + } + + print report_dump(); + if ($summary && !($clean == 1 && $quiet == 1)) { + print "$filename " if ($summary_file); + print "total: $cnt_error errors, $cnt_warn warnings, " . + (($check)? "$cnt_chk checks, " : "") . + "$cnt_lines lines checked\n"; + print "\n" if ($quiet == 0); + } + + if ($quiet == 0) { + # If there were whitespace errors which cleanpatch can fix + # then suggest that. +# if ($rpt_cleaners) { +# print "NOTE: whitespace errors detected, you may wish to use scripts/cleanpatch or\n"; +# print " scripts/cleanfile\n\n"; +# } + } + + if ($clean == 1 && $quiet == 0) { + print "$vname has no obvious style problems and is ready for submission.\n" + } + if ($clean == 0 && $quiet == 0) { + print "$vname has style problems, please review. If any of these errors\n"; + print "are false positives report them to the maintainer, see\n"; + print "CHECKPATCH in MAINTAINERS.\n"; + } + + return $clean; +} diff -Nru qemu-kvm-0.12.5+noroms/scripts/create_config qemu-kvm-0.14.1/scripts/create_config --- qemu-kvm-0.12.5+noroms/scripts/create_config 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/scripts/create_config 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,103 @@ +#!/bin/sh + +echo "/* Automatically generated by create_config - do not modify */" + +while read line; do + +case $line in + VERSION=*) # configuration + version=${line#*=} + echo "#define QEMU_VERSION \"$version\"" + ;; + PKGVERSION=*) # configuration + pkgversion=${line#*=} + echo "#define QEMU_PKGVERSION \"$pkgversion\"" + ;; + prefix=* | [a-z]*dir=*) # directory configuration + name=${line%=*} + value=${line#*=} + define_name=`echo $name | tr '[:lower:]' '[:upper:]'` + eval "define_value=\"$value\"" + echo "#define CONFIG_QEMU_$define_name \"$define_value\"" + # save for the next definitions + eval "$name=\$define_value" + ;; + CONFIG_AUDIO_DRIVERS=*) + drivers=${line#*=} + echo "#define CONFIG_AUDIO_DRIVERS \\" + for drv in $drivers; do + echo " &${drv}_audio_driver,\\" + done + echo "" + ;; + CONFIG_BDRV_WHITELIST=*) + echo "#define CONFIG_BDRV_WHITELIST \\" + for drv in ${line#*=}; do + echo " \"${drv}\",\\" + done + echo " NULL" + ;; + CONFIG_*=y) # configuration + name=${line%=*} + echo "#define $name 1" + ;; + CONFIG_*=*) # configuration + name=${line%=*} + value=${line#*=} + echo "#define $name $value" + ;; + ARCH=*) # configuration + arch=${line#*=} + arch_name=`echo $arch | tr '[:lower:]' '[:upper:]'` + echo "#define HOST_$arch_name 1" + ;; + HOST_USB=*) + # do nothing + ;; + HOST_CC=*) + # do nothing + ;; + HOST_*=y) # configuration + name=${line%=*} + echo "#define $name 1" + ;; + HOST_*=*) # configuration + name=${line%=*} + value=${line#*=} + echo "#define $name $value" + ;; + TARGET_ARCH=*) # configuration + target_arch=${line#*=} + echo "#define TARGET_ARCH \"$target_arch\"" + ;; + TARGET_BASE_ARCH=*) # configuration + target_base_arch=${line#*=} + if [ "$target_base_arch" != "$target_arch" ]; then + base_arch_name=`echo $target_base_arch | tr '[:lower:]' '[:upper:]'` + echo "#define TARGET_$base_arch_name 1" + fi + ;; + TARGET_XML_FILES=*) + # do nothing + ;; + TARGET_ABI_DIR=*) + # do nothing + ;; + TARGET_ARCH2=*) + # do nothing + ;; + TARGET_DIRS=*) + # do nothing + ;; + TARGET_*=y) # configuration + name=${line%=*} + echo "#define $name 1" + ;; + TARGET_*=*) # configuration + name=${line%=*} + value=${line#*=} + echo "#define $name $value" + ;; +esac + +done # read diff -Nru qemu-kvm-0.12.5+noroms/scripts/feature_to_c.sh qemu-kvm-0.14.1/scripts/feature_to_c.sh --- qemu-kvm-0.12.5+noroms/scripts/feature_to_c.sh 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/scripts/feature_to_c.sh 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,78 @@ +#!/bin/sh + +# Convert text files to compilable C arrays. +# +# Copyright (C) 2007 Free Software Foundation, Inc. +# +# This file is part of GDB. +# +# 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, see . + +output=$1 +shift + +if test -z "$output" || test -z "$1"; then + echo "Usage: $0 OUTPUTFILE INPUTFILE..." + exit 1 +fi + +if test -e "$output"; then + echo "Output file \"$output\" already exists; refusing to overwrite." + exit 1 +fi + +for input; do + arrayname=xml_feature_`echo $input | sed 's,.*/,,; s/[-.]/_/g'` + + ${AWK:-awk} 'BEGIN { n = 0 + printf "#include \"config.h\"\n" + printf "#include \"qemu-common.h\"\n" + printf "#include \"gdbstub.h\"\n" + print "static const char '$arrayname'[] = {" + for (i = 0; i < 255; i++) + _ord_[sprintf("%c", i)] = i + } { + split($0, line, ""); + printf " " + for (i = 1; i <= length($0); i++) { + c = line[i] + if (c == "'\''") { + printf "'\''\\'\'''\'', " + } else if (c == "\\") { + printf "'\''\\\\'\'', " + } else if (_ord_[c] >= 32 && _ord_[c] < 127) { + printf "'\''%s'\'', ", c + } else { + printf "'\''\\%03o'\'', ", _ord_[c] + } + if (i % 10 == 0) + printf "\n " + } + printf "'\''\\n'\'', \n" + } END { + print " 0 };" + }' < $input >> $output +done + +echo >> $output +echo "const char *const xml_builtin[][2] = {" >> $output + +for input; do + basename=`echo $input | sed 's,.*/,,'` + arrayname=xml_feature_`echo $input | sed 's,.*/,,; s/[-.]/_/g'` + echo " { \"$basename\", $arrayname }," >> $output +done + +echo " { (char *)0, (char *)0 }" >> $output +echo "};" >> $output diff -Nru qemu-kvm-0.12.5+noroms/scripts/hxtool qemu-kvm-0.14.1/scripts/hxtool --- qemu-kvm-0.12.5+noroms/scripts/hxtool 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/scripts/hxtool 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,102 @@ +#!/bin/sh + +hxtoh() +{ + flag=1 + while read -r str; do + case $str in + HXCOMM*) + ;; + STEXI*|ETEXI*|SQMP*|EQMP*) flag=$(($flag^1)) + ;; + *) + test $flag -eq 1 && printf "%s\n" "$str" + ;; + esac + done +} + +hxtotexi() +{ + flag=0 + line=1 + while read -r str; do + case "$str" in + HXCOMM*) + ;; + STEXI*) + if test $flag -eq 1 ; then + echo "line $line: syntax error: expected ETEXI, found $str" >&2 + exit 1 + fi + flag=1 + ;; + ETEXI*) + if test $flag -ne 1 ; then + echo "line $line: syntax error: expected STEXI, found $str" >&2 + exit 1 + fi + flag=0 + ;; + SQMP*|EQMP*) + if test $flag -eq 1 ; then + echo "line $line: syntax error: expected ETEXI, found $str" >&2 + exit 1 + fi + ;; + DEFHEADING*) + echo "$(expr "$str" : "DEFHEADING(\(.*\))")" + ;; + *) + test $flag -eq 1 && echo "$str" + ;; + esac + line=$((line+1)) + done +} + +hxtoqmp() +{ + IFS= + flag=0 + line=1 + while read -r str; do + case "$str" in + HXCOMM*) + ;; + SQMP*) + if test $flag -eq 1 ; then + echo "line $line: syntax error: expected EQMP, found $str" >&2 + exit 1 + fi + flag=1 + ;; + EQMP*) + if test $flag -ne 1 ; then + echo "line $line: syntax error: expected SQMP, found $str" >&2 + exit 1 + fi + flag=0 + ;; + STEXI*|ETEXI*) + if test $flag -eq 1 ; then + echo "line $line: syntax error: expected EQMP, found $str" >&2 + exit 1 + fi + ;; + *) + test $flag -eq 1 && echo "$str" + ;; + esac + line=$((line+1)) + done +} + +case "$1" in +"-h") hxtoh ;; +"-t") hxtotexi ;; +"-q") hxtoqmp ;; +*) exit 1 ;; +esac + +exit 0 diff -Nru qemu-kvm-0.12.5+noroms/scripts/make_device_config.sh qemu-kvm-0.14.1/scripts/make_device_config.sh --- qemu-kvm-0.12.5+noroms/scripts/make_device_config.sh 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/scripts/make_device_config.sh 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,28 @@ +#! /bin/sh +# Construct a target device config file from a default, pulling in any +# files from include directives. + +dest=$1.tmp +dep=$1.d +src=$2 +src_dir=`dirname $src` +all_includes= + +process_includes () { + cat $1 | grep '^include' | \ + while read include file ; do + all_includes="$all_includes $src_dir/$file" + process_includes $src_dir/$file + done +} + +f=$src +while [ -n "$f" ] ; do + f=`tr -d '\r' < $f | awk '/^include / {printf "'$src_dir'/%s", $2}'` + [ $? = 0 ] || exit 1 + all_includes="$all_includes $f" +done +process_includes $src > $dest + +cat $src $all_includes | grep -v '^include' > $dest +echo "$1: $all_includes" > $dep diff -Nru qemu-kvm-0.12.5+noroms/scripts/qemu-binfmt-conf.sh qemu-kvm-0.14.1/scripts/qemu-binfmt-conf.sh --- qemu-kvm-0.12.5+noroms/scripts/qemu-binfmt-conf.sh 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/scripts/qemu-binfmt-conf.sh 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,66 @@ +#!/bin/sh +# enable automatic i386/ARM/M68K/MIPS/SPARC/PPC program execution by the kernel + +# load the binfmt_misc module +if [ ! -d /proc/sys/fs/binfmt_misc ]; then + /sbin/modprobe binfmt_misc +fi +if [ ! -f /proc/sys/fs/binfmt_misc/register ]; then + mount binfmt_misc -t binfmt_misc /proc/sys/fs/binfmt_misc +fi + +# probe cpu type +cpu=`uname -m` +case "$cpu" in + i386|i486|i586|i686|i86pc|BePC|x86_64) + cpu="i386" + ;; + m68k) + cpu="m68k" + ;; + mips*) + cpu="mips" + ;; + "Power Macintosh"|ppc|ppc64) + cpu="ppc" + ;; + armv[4-9]*) + cpu="arm" + ;; +esac + +# register the interpreter for each cpu except for the native one +if [ $cpu != "i386" ] ; then + echo ':i386:M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x03\x00:\xff\xff\xff\xff\xff\xfe\xfe\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/local/bin/qemu-i386:' > /proc/sys/fs/binfmt_misc/register + echo ':i486:M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x06\x00:\xff\xff\xff\xff\xff\xfe\xfe\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/local/bin/qemu-i386:' > /proc/sys/fs/binfmt_misc/register +fi +if [ $cpu != "alpha" ] ; then + echo ':alpha:M::\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x26\x90:\xff\xff\xff\xff\xff\xfe\xfe\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/local/bin/qemu-alpha:' > /proc/sys/fs/binfmt_misc/register +fi +if [ $cpu != "arm" ] ; then + echo ':arm:M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/local/bin/qemu-arm:' > /proc/sys/fs/binfmt_misc/register + echo ':armeb:M::\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:/usr/local/bin/qemu-armeb:' > /proc/sys/fs/binfmt_misc/register +fi +if [ $cpu != "sparc" ] ; then + echo ':sparc:M::\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x02:\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:/usr/local/bin/qemu-sparc:' > /proc/sys/fs/binfmt_misc/register +fi +if [ $cpu != "ppc" ] ; then + echo ':ppc:M::\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x14:\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:/usr/local/bin/qemu-ppc:' > /proc/sys/fs/binfmt_misc/register +fi +if [ $cpu != "m68k" ] ; then + echo 'Please check cpu value and header information for m68k!' + echo ':m68k:M::\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x04:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:/usr/local/bin/qemu-m68k:' > /proc/sys/fs/binfmt_misc/register +fi +if [ $cpu != "mips" ] ; then + # FIXME: We could use the other endianness on a MIPS host. + echo ':mips:M::\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:/usr/local/bin/qemu-mips:' > /proc/sys/fs/binfmt_misc/register + echo ':mipsel:M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/local/bin/qemu-mipsel:' > /proc/sys/fs/binfmt_misc/register + echo ':mipsn32:M::\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:/usr/local/bin/qemu-mipsn32:' > /proc/sys/fs/binfmt_misc/register + echo ':mipsn32el:M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/local/bin/qemu-mipsn32el:' > /proc/sys/fs/binfmt_misc/register + echo ':mips64:M::\x7fELF\x02\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:/usr/local/bin/qemu-mips64:' > /proc/sys/fs/binfmt_misc/register + echo ':mips64el:M::\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x08\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/local/bin/qemu-mips64el:' > /proc/sys/fs/binfmt_misc/register +fi +if [ $cpu != "sh" ] ; then + echo ':sh4:M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x2a\x00:\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/local/bin/qemu-sh4:' > /proc/sys/fs/binfmt_misc/register + echo ':sh4eb:M::\x7fELF\x01\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x2a:\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff:/usr/local/bin/qemu-sh4eb:' > /proc/sys/fs/binfmt_misc/register +fi diff -Nru qemu-kvm-0.12.5+noroms/scripts/signrom.sh qemu-kvm-0.14.1/scripts/signrom.sh --- qemu-kvm-0.12.5+noroms/scripts/signrom.sh 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/scripts/signrom.sh 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,45 @@ +#!/bin/sh + +# Option ROM Signing utility +# +# 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, see . +# +# Copyright Novell Inc, 2009 +# Authors: Alexander Graf +# +# Syntax: signrom.sh + +# did we get proper arguments? +test "$1" -a "$2" || exit 1 + +sum=0 + +# find out the file size +x=`dd if="$1" bs=1 count=1 skip=2 2>/dev/null | od -t u1 -A n` +#size=`expr $x \* 512 - 1` +size=$(( $x * 512 - 1 )) + +# now get the checksum +nums=`od -A n -t u1 -v -N $size "$1"` +for i in ${nums}; do + # add each byte's value to sum + sum=`expr \( $sum + $i \) % 256` +done + +sum=$(( (256 - $sum) % 256 )) +sum_octal=$( printf "%o" $sum ) + +# and write the output file +cp "$1" "$2" +printf "\\$sum_octal" | dd of="$2" bs=1 count=1 seek=$size conv=notrunc 2>/dev/null diff -Nru qemu-kvm-0.12.5+noroms/scripts/simpletrace.py qemu-kvm-0.14.1/scripts/simpletrace.py --- qemu-kvm-0.12.5+noroms/scripts/simpletrace.py 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/scripts/simpletrace.py 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,93 @@ +#!/usr/bin/env python +# +# Pretty-printer for simple trace backend binary trace files +# +# Copyright IBM, Corp. 2010 +# +# This work is licensed under the terms of the GNU GPL, version 2. See +# the COPYING file in the top-level directory. +# +# For help see docs/tracing.txt + +import sys +import struct +import re + +header_event_id = 0xffffffffffffffff +header_magic = 0xf2b177cb0aa429b4 +header_version = 0 + +trace_fmt = '=QQQQQQQQ' +trace_len = struct.calcsize(trace_fmt) +event_re = re.compile(r'(disable\s+)?([a-zA-Z0-9_]+)\(([^)]*)\).*') + +def err(msg): + sys.stderr.write(msg + '\n') + sys.exit(1) + +def parse_events(fobj): + """Parse a trace-events file.""" + + def get_argnames(args): + """Extract argument names from a parameter list.""" + return tuple(arg.split()[-1].lstrip('*') for arg in args.split(',')) + + events = {} + event_num = 0 + for line in fobj: + m = event_re.match(line.strip()) + if m is None: + continue + + disable, name, args = m.groups() + events[event_num] = (name,) + get_argnames(args) + event_num += 1 + return events + +def read_record(fobj): + """Deserialize a trace record from a file.""" + s = fobj.read(trace_len) + if len(s) != trace_len: + return None + return struct.unpack(trace_fmt, s) + +def read_trace_file(fobj): + """Deserialize trace records from a file.""" + header = read_record(fobj) + if header is None or \ + header[0] != header_event_id or \ + header[1] != header_magic or \ + header[2] != header_version: + err('not a trace file or incompatible version') + + while True: + rec = read_record(fobj) + if rec is None: + break + + yield rec + +class Formatter(object): + def __init__(self, events): + self.events = events + self.last_timestamp = None + + def format_record(self, rec): + if self.last_timestamp is None: + self.last_timestamp = rec[1] + delta_ns = rec[1] - self.last_timestamp + self.last_timestamp = rec[1] + + event = self.events[rec[0]] + fields = [event[0], '%0.3f' % (delta_ns / 1000.0)] + for i in xrange(1, len(event)): + fields.append('%s=0x%x' % (event[i], rec[i + 1])) + return ' '.join(fields) + +if len(sys.argv) != 3: + err('usage: %s ' % sys.argv[0]) + +events = parse_events(open(sys.argv[1], 'r')) +formatter = Formatter(events) +for rec in read_trace_file(open(sys.argv[2], 'rb')): + print formatter.format_record(rec) diff -Nru qemu-kvm-0.12.5+noroms/scripts/texi2pod.pl qemu-kvm-0.14.1/scripts/texi2pod.pl --- qemu-kvm-0.12.5+noroms/scripts/texi2pod.pl 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/scripts/texi2pod.pl 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,477 @@ +#! /usr/bin/perl -w + +# Copyright (C) 1999, 2000, 2001, 2003 Free Software Foundation, Inc. + +# This file is part of GCC. + +# GCC 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, or (at your option) +# any later version. + +# GCC 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 GCC; see the file COPYING. If not, +# see . + +# This does trivial (and I mean _trivial_) conversion of Texinfo +# markup to Perl POD format. It's intended to be used to extract +# something suitable for a manpage from a Texinfo document. + +$output = 0; +$skipping = 0; +%sects = (); +$section = ""; +@icstack = (); +@endwstack = (); +@skstack = (); +@instack = (); +$shift = ""; +%defs = (); +$fnno = 1; +$inf = ""; +$ibase = ""; +@ipath = (); + +while ($_ = shift) { + if (/^-D(.*)$/) { + if ($1 ne "") { + $flag = $1; + } else { + $flag = shift; + } + $value = ""; + ($flag, $value) = ($flag =~ /^([^=]+)(?:=(.+))?/); + die "no flag specified for -D\n" + unless $flag ne ""; + die "flags may only contain letters, digits, hyphens, dashes and underscores\n" + unless $flag =~ /^[a-zA-Z0-9_-]+$/; + $defs{$flag} = $value; + } elsif (/^-I(.*)$/) { + if ($1 ne "") { + $flag = $1; + } else { + $flag = shift; + } + push (@ipath, $flag); + } elsif (/^-/) { + usage(); + } else { + $in = $_, next unless defined $in; + $out = $_, next unless defined $out; + usage(); + } +} + +if (defined $in) { + $inf = gensym(); + open($inf, "<$in") or die "opening \"$in\": $!\n"; + $ibase = $1 if $in =~ m|^(.+)/[^/]+$|; +} else { + $inf = \*STDIN; +} + +if (defined $out) { + open(STDOUT, ">$out") or die "opening \"$out\": $!\n"; +} + +while(defined $inf) { +while(<$inf>) { + # Certain commands are discarded without further processing. + /^\@(?: + [a-z]+index # @*index: useful only in complete manual + |need # @need: useful only in printed manual + |(?:end\s+)?group # @group .. @end group: ditto + |page # @page: ditto + |node # @node: useful only in .info file + |(?:end\s+)?ifnottex # @ifnottex .. @end ifnottex: use contents + )\b/x and next; + + chomp; + + # Look for filename and title markers. + /^\@setfilename\s+([^.]+)/ and $fn = $1, next; + /^\@settitle\s+([^.]+)/ and $tl = postprocess($1), next; + + # Identify a man title but keep only the one we are interested in. + /^\@c\s+man\s+title\s+([A-Za-z0-9-]+)\s+(.+)/ and do { + if (exists $defs{$1}) { + $fn = $1; + $tl = postprocess($2); + } + next; + }; + + # Look for blocks surrounded by @c man begin SECTION ... @c man end. + # This really oughta be @ifman ... @end ifman and the like, but such + # would require rev'ing all other Texinfo translators. + /^\@c\s+man\s+begin\s+([A-Z]+)\s+([A-Za-z0-9-]+)/ and do { + $output = 1 if exists $defs{$2}; + $sect = $1; + next; + }; + /^\@c\s+man\s+begin\s+([A-Z]+)/ and $sect = $1, $output = 1, next; + /^\@c\s+man\s+end/ and do { + $sects{$sect} = "" unless exists $sects{$sect}; + $sects{$sect} .= postprocess($section); + $section = ""; + $output = 0; + next; + }; + + # handle variables + /^\@set\s+([a-zA-Z0-9_-]+)\s*(.*)$/ and do { + $defs{$1} = $2; + next; + }; + /^\@clear\s+([a-zA-Z0-9_-]+)/ and do { + delete $defs{$1}; + next; + }; + + next unless $output; + + # Discard comments. (Can't do it above, because then we'd never see + # @c man lines.) + /^\@c\b/ and next; + + # End-block handler goes up here because it needs to operate even + # if we are skipping. + /^\@end\s+([a-z]+)/ and do { + # Ignore @end foo, where foo is not an operation which may + # cause us to skip, if we are presently skipping. + my $ended = $1; + next if $skipping && $ended !~ /^(?:ifset|ifclear|ignore|menu|iftex|copying)$/; + + die "\@end $ended without \@$ended at line $.\n" unless defined $endw; + die "\@$endw ended by \@end $ended at line $.\n" unless $ended eq $endw; + + $endw = pop @endwstack; + + if ($ended =~ /^(?:ifset|ifclear|ignore|menu|iftex)$/) { + $skipping = pop @skstack; + next; + } elsif ($ended =~ /^(?:example|smallexample|display)$/) { + $shift = ""; + $_ = ""; # need a paragraph break + } elsif ($ended =~ /^(?:itemize|enumerate|[fv]?table)$/) { + $_ = "\n=back\n"; + $ic = pop @icstack; + } elsif ($ended eq "multitable") { + $_ = "\n=back\n"; + } else { + die "unknown command \@end $ended at line $.\n"; + } + }; + + # We must handle commands which can cause skipping even while we + # are skipping, otherwise we will not process nested conditionals + # correctly. + /^\@ifset\s+([a-zA-Z0-9_-]+)/ and do { + push @endwstack, $endw; + push @skstack, $skipping; + $endw = "ifset"; + $skipping = 1 unless exists $defs{$1}; + next; + }; + + /^\@ifclear\s+([a-zA-Z0-9_-]+)/ and do { + push @endwstack, $endw; + push @skstack, $skipping; + $endw = "ifclear"; + $skipping = 1 if exists $defs{$1}; + next; + }; + + /^\@(ignore|menu|iftex|copying)\b/ and do { + push @endwstack, $endw; + push @skstack, $skipping; + $endw = $1; + $skipping = 1; + next; + }; + + next if $skipping; + + # Character entities. First the ones that can be replaced by raw text + # or discarded outright: + s/\@copyright\{\}/(c)/g; + s/\@dots\{\}/.../g; + s/\@enddots\{\}/..../g; + s/\@([.!? ])/$1/g; + s/\@[:-]//g; + s/\@bullet(?:\{\})?/*/g; + s/\@TeX\{\}/TeX/g; + s/\@pounds\{\}/\#/g; + s/\@minus(?:\{\})?/-/g; + s/\\,/,/g; + + # Now the ones that have to be replaced by special escapes + # (which will be turned back into text by unmunge()) + s/&/&/g; + s/\@\{/{/g; + s/\@\}/}/g; + s/\@\@/&at;/g; + + # Inside a verbatim block, handle @var specially. + if ($shift ne "") { + s/\@var\{([^\}]*)\}/<$1>/g; + } + + # POD doesn't interpret E<> inside a verbatim block. + if ($shift eq "") { + s//>/g; + } else { + s//>/g; + } + + # Single line command handlers. + + /^\@include\s+(.+)$/ and do { + push @instack, $inf; + $inf = gensym(); + $file = postprocess($1); + + # Try cwd and $ibase, then explicit -I paths. + $done = 0; + foreach $path ("", $ibase, @ipath) { + $mypath = $file; + $mypath = $path . "/" . $mypath if ($path ne ""); + open($inf, "<" . $mypath) and ($done = 1, last); + } + die "cannot find $file" if !$done; + next; + }; + + /^\@(?:section|unnumbered|unnumberedsec|center)\s+(.+)$/ + and $_ = "\n=head2 $1\n"; + /^\@subsection\s+(.+)$/ + and $_ = "\n=head3 $1\n"; + /^\@subsubsection\s+(.+)$/ + and $_ = "\n=head4 $1\n"; + + # Block command handlers: + /^\@itemize(?:\s+(\@[a-z]+|\*|-))?/ and do { + push @endwstack, $endw; + push @icstack, $ic; + if (defined $1) { + $ic = $1; + } else { + $ic = '*'; + } + $_ = "\n=over 4\n"; + $endw = "itemize"; + }; + + /^\@enumerate(?:\s+([a-zA-Z0-9]+))?/ and do { + push @endwstack, $endw; + push @icstack, $ic; + if (defined $1) { + $ic = $1 . "."; + } else { + $ic = "1."; + } + $_ = "\n=over 4\n"; + $endw = "enumerate"; + }; + + /^\@multitable\s.*/ and do { + push @endwstack, $endw; + $endw = "multitable"; + $_ = "\n=over 4\n"; + }; + + /^\@([fv]?table)\s+(\@[a-z]+)/ and do { + push @endwstack, $endw; + push @icstack, $ic; + $endw = $1; + $ic = $2; + $ic =~ s/\@(?:samp|strong|key|gcctabopt|option|env)/B/; + $ic =~ s/\@(?:code|kbd)/C/; + $ic =~ s/\@(?:dfn|var|emph|cite|i)/I/; + $ic =~ s/\@(?:file)/F/; + $_ = "\n=over 4\n"; + }; + + /^\@((?:small)?example|display)/ and do { + push @endwstack, $endw; + $endw = $1; + $shift = "\t"; + $_ = ""; # need a paragraph break + }; + + /^\@item\s+(.*\S)\s*$/ and $endw eq "multitable" and do { + @columns = (); + for $column (split (/\s*\@tab\s*/, $1)) { + # @strong{...} is used a @headitem work-alike + $column =~ s/^\@strong{(.*)}$/$1/; + push @columns, $column; + } + $_ = "\n=item ".join (" : ", @columns)."\n"; + }; + + /^\@itemx?\s*(.+)?$/ and do { + if (defined $1) { + # Entity escapes prevent munging by the <> processing below. + $_ = "\n=item $ic\<$1\>\n"; + } else { + $_ = "\n=item $ic\n"; + $ic =~ y/A-Ya-y/B-Zb-z/; + $ic =~ s/(\d+)/$1 + 1/eg; + } + }; + + $section .= $shift.$_."\n"; +} +# End of current file. +close($inf); +$inf = pop @instack; +} + +die "No filename or title\n" unless defined $fn && defined $tl; + +$sects{NAME} = "$fn \- $tl\n"; +$sects{FOOTNOTES} .= "=back\n" if exists $sects{FOOTNOTES}; + +for $sect (qw(NAME SYNOPSIS DESCRIPTION OPTIONS ENVIRONMENT FILES + BUGS NOTES FOOTNOTES SEEALSO AUTHOR COPYRIGHT)) { + if(exists $sects{$sect}) { + $head = $sect; + $head =~ s/SEEALSO/SEE ALSO/; + print "=head1 $head\n\n"; + print scalar unmunge ($sects{$sect}); + print "\n"; + } +} + +sub usage +{ + die "usage: $0 [-D toggle...] [infile [outfile]]\n"; +} + +sub postprocess +{ + local $_ = $_[0]; + + # @value{foo} is replaced by whatever 'foo' is defined as. + while (m/(\@value\{([a-zA-Z0-9_-]+)\})/g) { + if (! exists $defs{$2}) { + print STDERR "Option $2 not defined\n"; + s/\Q$1\E//; + } else { + $value = $defs{$2}; + s/\Q$1\E/$value/; + } + } + + # Formatting commands. + # Temporary escape for @r. + s/\@r\{([^\}]*)\}/R<$1>/g; + s/\@(?:dfn|var|emph|cite|i)\{([^\}]*)\}/I<$1>/g; + s/\@(?:code|kbd)\{([^\}]*)\}/C<$1>/g; + s/\@(?:gccoptlist|samp|strong|key|option|env|command|b)\{([^\}]*)\}/B<$1>/g; + s/\@sc\{([^\}]*)\}/\U$1/g; + s/\@file\{([^\}]*)\}/F<$1>/g; + s/\@w\{([^\}]*)\}/S<$1>/g; + s/\@(?:dmn|math)\{([^\}]*)\}/$1/g; + + # keep references of the form @ref{...}, print them bold + s/\@(?:ref)\{([^\}]*)\}/B<$1>/g; + + # Change double single quotes to double quotes. + s/''/"/g; + s/``/"/g; + + # Cross references are thrown away, as are @noindent and @refill. + # (@noindent is impossible in .pod, and @refill is unnecessary.) + # @* is also impossible in .pod; we discard it and any newline that + # follows it. Similarly, our macro @gol must be discarded. + + s/\(?\@xref\{(?:[^\}]*)\}(?:[^.<]|(?:<[^<>]*>))*\.\)?//g; + s/\s+\(\@pxref\{(?:[^\}]*)\}\)//g; + s/;\s+\@pxref\{(?:[^\}]*)\}//g; + s/\@noindent\s*//g; + s/\@refill//g; + s/\@gol//g; + s/\@\*\s*\n?//g; + + # Anchors are thrown away + s/\@anchor\{(?:[^\}]*)\}//g; + + # @uref can take one, two, or three arguments, with different + # semantics each time. @url and @email are just like @uref with + # one argument, for our purposes. + s/\@(?:uref|url|email)\{([^\},]*)\}/<B<$1>>/g; + s/\@uref\{([^\},]*),([^\},]*)\}/$2 (C<$1>)/g; + s/\@uref\{([^\},]*),([^\},]*),([^\},]*)\}/$3/g; + + # Un-escape <> at this point. + s/<//g; + + # Now un-nest all B<>, I<>, R<>. Theoretically we could have + # indefinitely deep nesting; in practice, one level suffices. + 1 while s/([BIR])<([^<>]*)([BIR])<([^<>]*)>/$1<$2>$3<$4>$1 with bare ...; eliminate empty markup, B<>; + # shift white space at the ends of [BI]<...> expressions outside + # the expression. + s/R<([^<>]*)>/$1/g; + s/[BI]<>//g; + s/([BI])<(\s+)([^>]+)>/$2$1<$3>/g; + s/([BI])<([^>]+?)(\s+)>/$1<$2>$3/g; + + # Extract footnotes. This has to be done after all other + # processing because otherwise the regexp will choke on formatting + # inside @footnote. + while (/\@footnote/g) { + s/\@footnote\{([^\}]+)\}/[$fnno]/; + add_footnote($1, $fnno); + $fnno++; + } + + return $_; +} + +sub unmunge +{ + # Replace escaped symbols with their equivalents. + local $_ = $_[0]; + + s/</E/g; + s/>/E/g; + s/{/\{/g; + s/}/\}/g; + s/&at;/\@/g; + s/&/&/g; + return $_; +} + +sub add_footnote +{ + unless (exists $sects{FOOTNOTES}) { + $sects{FOOTNOTES} = "\n=over 4\n\n"; + } + + $sects{FOOTNOTES} .= "=item $fnno.\n\n"; $fnno++; + $sects{FOOTNOTES} .= $_[0]; + $sects{FOOTNOTES} .= "\n\n"; +} + +# stolen from Symbol.pm +{ + my $genseq = 0; + sub gensym + { + my $name = "GEN" . $genseq++; + my $ref = \*{$name}; + delete $::{$name}; + return $ref; + } +} diff -Nru qemu-kvm-0.12.5+noroms/scripts/tracetool qemu-kvm-0.14.1/scripts/tracetool --- qemu-kvm-0.12.5+noroms/scripts/tracetool 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/scripts/tracetool 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,629 @@ +#!/bin/sh +# +# Code generator for trace events +# +# Copyright IBM, Corp. 2010 +# +# This work is licensed under the terms of the GNU GPL, version 2. See +# the COPYING file in the top-level directory. + +# Disable pathname expansion, makes processing text with '*' characters simpler +set -f + +usage() +{ + cat >&2 < +EOF +} + +linetoh_stderr() +{ + local name args argnames argc fmt + name=$(get_name "$1") + args=$(get_args "$1") + argnames=$(get_argnames "$1" ",") + argc=$(get_argc "$1") + fmt=$(get_fmt "$1") + + if [ "$argc" -gt 0 ]; then + argnames=", $argnames" + fi + + cat <" + ust_clean_namespace +} + +linetoh_ust() +{ + local name args argnames + name=$(get_name "$1") + args=$(get_args "$1") + argnames=$(get_argnames "$1", ",") + + cat < +$(ust_clean_namespace) +#include "trace.h" +EOF +} + +linetoc_ust() +{ + local name args argnames fmt + name=$(get_name "$1") + args=$(get_args "$1") + argnames=$(get_argnames "$1", ",") + fmt=$(get_fmt "$1") + + cat < -#include - -#ifndef _WIN32 -#include -#endif - -#include "qemu-common.h" -#include "console.h" -#include "sysemu.h" -#include "x_keymap.h" -#include "sdl_zoom.h" - -static DisplayChangeListener *dcl; -static SDL_Surface *real_screen; -static SDL_Surface *guest_screen = NULL; -static int gui_grab; /* if true, all keyboard/mouse events are grabbed */ -static int last_vm_running; -static int gui_saved_grab; -static int gui_fullscreen; -static int gui_noframe; -static int gui_key_modifier_pressed; -static int gui_keysym; -static int gui_fullscreen_initial_grab; -static int gui_grab_code = KMOD_LALT | KMOD_LCTRL; -static uint8_t modifiers_state[256]; -static int width, height; -static SDL_Cursor *sdl_cursor_normal; -static SDL_Cursor *sdl_cursor_hidden; -static int absolute_enabled = 0; -static int guest_cursor = 0; -static int guest_x, guest_y; -static SDL_Cursor *guest_sprite = NULL; -static uint8_t allocator; -static SDL_PixelFormat host_format; -static int scaling_active = 0; - -static void sdl_update(DisplayState *ds, int x, int y, int w, int h) -{ - // printf("updating x=%d y=%d w=%d h=%d\n", x, y, w, h); - SDL_Rect rec; - rec.x = x; - rec.y = y; - rec.w = w; - rec.h = h; - - if (guest_screen) { - if (!scaling_active) { - SDL_BlitSurface(guest_screen, &rec, real_screen, &rec); - } else { - if (sdl_zoom_blit(guest_screen, real_screen, SMOOTHING_ON, &rec) < 0) { - fprintf(stderr, "Zoom blit failed\n"); - exit(1); - } - } - } - SDL_UpdateRect(real_screen, rec.x, rec.y, rec.w, rec.h); -} - -static void sdl_setdata(DisplayState *ds) -{ - SDL_Rect rec; - rec.x = 0; - rec.y = 0; - rec.w = real_screen->w; - rec.h = real_screen->h; - - if (guest_screen != NULL) SDL_FreeSurface(guest_screen); - - guest_screen = SDL_CreateRGBSurfaceFrom(ds_get_data(ds), ds_get_width(ds), ds_get_height(ds), - ds_get_bits_per_pixel(ds), ds_get_linesize(ds), - ds->surface->pf.rmask, ds->surface->pf.gmask, - ds->surface->pf.bmask, ds->surface->pf.amask); -} - -static void do_sdl_resize(int new_width, int new_height, int bpp) -{ - int flags; - - // printf("resizing to %d %d\n", w, h); - - flags = SDL_HWSURFACE|SDL_ASYNCBLIT|SDL_HWACCEL|SDL_RESIZABLE; - if (gui_fullscreen) - flags |= SDL_FULLSCREEN; - if (gui_noframe) - flags |= SDL_NOFRAME; - - width = new_width; - height = new_height; - real_screen = SDL_SetVideoMode(width, height, bpp, flags); - if (!real_screen) { - fprintf(stderr, "Could not open SDL display\n"); - exit(1); - } -} - -static void sdl_resize(DisplayState *ds) -{ - if (!allocator) { - if (!scaling_active) - do_sdl_resize(ds_get_width(ds), ds_get_height(ds), 0); - else if (real_screen->format->BitsPerPixel != ds_get_bits_per_pixel(ds)) - do_sdl_resize(real_screen->w, real_screen->h, ds_get_bits_per_pixel(ds)); - sdl_setdata(ds); - } else { - if (guest_screen != NULL) { - SDL_FreeSurface(guest_screen); - guest_screen = NULL; - } - } -} - -static PixelFormat sdl_to_qemu_pixelformat(SDL_PixelFormat *sdl_pf) -{ - PixelFormat qemu_pf; - - memset(&qemu_pf, 0x00, sizeof(PixelFormat)); - - qemu_pf.bits_per_pixel = sdl_pf->BitsPerPixel; - qemu_pf.bytes_per_pixel = sdl_pf->BytesPerPixel; - qemu_pf.depth = (qemu_pf.bits_per_pixel) == 32 ? 24 : (qemu_pf.bits_per_pixel); - - qemu_pf.rmask = sdl_pf->Rmask; - qemu_pf.gmask = sdl_pf->Gmask; - qemu_pf.bmask = sdl_pf->Bmask; - qemu_pf.amask = sdl_pf->Amask; - - qemu_pf.rshift = sdl_pf->Rshift; - qemu_pf.gshift = sdl_pf->Gshift; - qemu_pf.bshift = sdl_pf->Bshift; - qemu_pf.ashift = sdl_pf->Ashift; - - qemu_pf.rbits = 8 - sdl_pf->Rloss; - qemu_pf.gbits = 8 - sdl_pf->Gloss; - qemu_pf.bbits = 8 - sdl_pf->Bloss; - qemu_pf.abits = 8 - sdl_pf->Aloss; - - qemu_pf.rmax = ((1 << qemu_pf.rbits) - 1); - qemu_pf.gmax = ((1 << qemu_pf.gbits) - 1); - qemu_pf.bmax = ((1 << qemu_pf.bbits) - 1); - qemu_pf.amax = ((1 << qemu_pf.abits) - 1); - - return qemu_pf; -} - -static DisplaySurface* sdl_create_displaysurface(int width, int height) -{ - DisplaySurface *surface = (DisplaySurface*) qemu_mallocz(sizeof(DisplaySurface)); - if (surface == NULL) { - fprintf(stderr, "sdl_create_displaysurface: malloc failed\n"); - exit(1); - } - - surface->width = width; - surface->height = height; - - if (scaling_active) { - if (host_format.BytesPerPixel != 2 && host_format.BytesPerPixel != 4) { - surface->linesize = width * 4; - surface->pf = qemu_default_pixelformat(32); - } else { - surface->linesize = width * host_format.BytesPerPixel; - surface->pf = sdl_to_qemu_pixelformat(&host_format); - } -#ifdef HOST_WORDS_BIGENDIAN - surface->flags = QEMU_ALLOCATED_FLAG | QEMU_BIG_ENDIAN_FLAG; -#else - surface->flags = QEMU_ALLOCATED_FLAG; -#endif - surface->data = (uint8_t*) qemu_mallocz(surface->linesize * surface->height); - - return surface; - } - - if (host_format.BitsPerPixel == 16) - do_sdl_resize(width, height, 16); - else - do_sdl_resize(width, height, 32); - - surface->pf = sdl_to_qemu_pixelformat(real_screen->format); - surface->linesize = real_screen->pitch; - surface->data = real_screen->pixels; - -#ifdef HOST_WORDS_BIGENDIAN - surface->flags = QEMU_REALPIXELS_FLAG | QEMU_BIG_ENDIAN_FLAG; -#else - surface->flags = QEMU_REALPIXELS_FLAG; -#endif - allocator = 1; - - return surface; -} - -static void sdl_free_displaysurface(DisplaySurface *surface) -{ - allocator = 0; - if (surface == NULL) - return; - - if (surface->flags & QEMU_ALLOCATED_FLAG) - qemu_free(surface->data); - qemu_free(surface); -} - -static DisplaySurface* sdl_resize_displaysurface(DisplaySurface *surface, int width, int height) -{ - sdl_free_displaysurface(surface); - return sdl_create_displaysurface(width, height); -} - -/* generic keyboard conversion */ - -#include "sdl_keysym.h" - -static kbd_layout_t *kbd_layout = NULL; - -static uint8_t sdl_keyevent_to_keycode_generic(const SDL_KeyboardEvent *ev) -{ - int keysym; - /* workaround for X11+SDL bug with AltGR */ - keysym = ev->keysym.sym; - if (keysym == 0 && ev->keysym.scancode == 113) - keysym = SDLK_MODE; - /* For Japanese key '\' and '|' */ - if (keysym == 92 && ev->keysym.scancode == 133) { - keysym = 0xa5; - } - return keysym2scancode(kbd_layout, keysym); -} - -/* specific keyboard conversions from scan codes */ - -#if defined(_WIN32) - -static uint8_t sdl_keyevent_to_keycode(const SDL_KeyboardEvent *ev) -{ - return ev->keysym.scancode; -} - -#else - -#if defined(SDL_VIDEO_DRIVER_X11) -#include - -static int check_for_evdev(void) -{ - SDL_SysWMinfo info; - XkbDescPtr desc = NULL; - int has_evdev = 0; - char *keycodes = NULL; - - SDL_VERSION(&info.version); - if (!SDL_GetWMInfo(&info)) { - return 0; - } - desc = XkbGetKeyboard(info.info.x11.display, - XkbGBN_AllComponentsMask, - XkbUseCoreKbd); - if (desc && desc->names) { - keycodes = XGetAtomName(info.info.x11.display, desc->names->keycodes); - if (keycodes == NULL) { - fprintf(stderr, "could not lookup keycode name\n"); - } else if (strstart(keycodes, "evdev", NULL)) { - has_evdev = 1; - } else if (!strstart(keycodes, "xfree86", NULL)) { - fprintf(stderr, "unknown keycodes `%s', please report to " - "qemu-devel@nongnu.org\n", keycodes); - } - } - - if (desc) { - XkbFreeKeyboard(desc, XkbGBN_AllComponentsMask, True); - } - if (keycodes) { - XFree(keycodes); - } - return has_evdev; -} -#else -static int check_for_evdev(void) -{ - return 0; -} -#endif - -static uint8_t sdl_keyevent_to_keycode(const SDL_KeyboardEvent *ev) -{ - int keycode; - static int has_evdev = -1; - - if (has_evdev == -1) - has_evdev = check_for_evdev(); - - keycode = ev->keysym.scancode; - - if (keycode < 9) { - keycode = 0; - } else if (keycode < 97) { - keycode -= 8; /* just an offset */ - } else if (keycode < 158) { - /* use conversion table */ - if (has_evdev) - keycode = translate_evdev_keycode(keycode - 97); - else - keycode = translate_xfree86_keycode(keycode - 97); - } else if (keycode == 208) { /* Hiragana_Katakana */ - keycode = 0x70; - } else if (keycode == 211) { /* backslash */ - keycode = 0x73; - } else { - keycode = 0; - } - return keycode; -} - -#endif - -static void reset_keys(void) -{ - int i; - for(i = 0; i < 256; i++) { - if (modifiers_state[i]) { - if (i & 0x80) - kbd_put_keycode(0xe0); - kbd_put_keycode(i | 0x80); - modifiers_state[i] = 0; - } - } -} - -static void sdl_process_key(SDL_KeyboardEvent *ev) -{ - int keycode, v; - - if (ev->keysym.sym == SDLK_PAUSE) { - /* specific case */ - v = 0; - if (ev->type == SDL_KEYUP) - v |= 0x80; - kbd_put_keycode(0xe1); - kbd_put_keycode(0x1d | v); - kbd_put_keycode(0x45 | v); - return; - } - - if (kbd_layout) { - keycode = sdl_keyevent_to_keycode_generic(ev); - } else { - keycode = sdl_keyevent_to_keycode(ev); - } - - switch(keycode) { - case 0x00: - /* sent when leaving window: reset the modifiers state */ - reset_keys(); - return; - case 0x2a: /* Left Shift */ - case 0x36: /* Right Shift */ - case 0x1d: /* Left CTRL */ - case 0x9d: /* Right CTRL */ - case 0x38: /* Left ALT */ - case 0xb8: /* Right ALT */ - if (ev->type == SDL_KEYUP) - modifiers_state[keycode] = 0; - else - modifiers_state[keycode] = 1; - break; - case 0x45: /* num lock */ - case 0x3a: /* caps lock */ - /* SDL does not send the key up event, so we generate it */ - kbd_put_keycode(keycode); - kbd_put_keycode(keycode | 0x80); - return; - } - - /* now send the key code */ - if (keycode & 0x80) - kbd_put_keycode(0xe0); - if (ev->type == SDL_KEYUP) - kbd_put_keycode(keycode | 0x80); - else - kbd_put_keycode(keycode & 0x7f); -} - -static void sdl_update_caption(void) -{ - char win_title[1024]; - char icon_title[1024]; - const char *status = ""; - - if (!vm_running) - status = " [Stopped]"; - else if (gui_grab) { - if (alt_grab) - status = " - Press Ctrl-Alt-Shift to exit grab"; - else if (ctrl_grab) - status = " - Press Right-Ctrl to exit grab"; - else - status = " - Press Ctrl-Alt to exit grab"; - } - - if (qemu_name) { - snprintf(win_title, sizeof(win_title), "QEMU (%s)%s", qemu_name, status); - snprintf(icon_title, sizeof(icon_title), "QEMU (%s)", qemu_name); - } else { - snprintf(win_title, sizeof(win_title), "QEMU%s", status); - snprintf(icon_title, sizeof(icon_title), "QEMU"); - } - - SDL_WM_SetCaption(win_title, icon_title); -} - -static void sdl_hide_cursor(void) -{ - if (!cursor_hide) - return; - - if (kbd_mouse_is_absolute()) { - SDL_ShowCursor(1); - SDL_SetCursor(sdl_cursor_hidden); - } else { - SDL_ShowCursor(0); - } -} - -static void sdl_show_cursor(void) -{ - if (!cursor_hide) - return; - - if (!kbd_mouse_is_absolute()) { - SDL_ShowCursor(1); - if (guest_cursor && - (gui_grab || kbd_mouse_is_absolute() || absolute_enabled)) - SDL_SetCursor(guest_sprite); - else - SDL_SetCursor(sdl_cursor_normal); - } -} - -static void sdl_grab_start(void) -{ - if (guest_cursor) { - SDL_SetCursor(guest_sprite); - if (!kbd_mouse_is_absolute() && !absolute_enabled) - SDL_WarpMouse(guest_x, guest_y); - } else - sdl_hide_cursor(); - - if (SDL_WM_GrabInput(SDL_GRAB_ON) == SDL_GRAB_ON) { - gui_grab = 1; - sdl_update_caption(); - } else - sdl_show_cursor(); -} - -static void sdl_grab_end(void) -{ - SDL_WM_GrabInput(SDL_GRAB_OFF); - gui_grab = 0; - sdl_show_cursor(); - sdl_update_caption(); -} - -static void sdl_send_mouse_event(int dx, int dy, int dz, int x, int y, int state) -{ - int buttons; - buttons = 0; - if (state & SDL_BUTTON(SDL_BUTTON_LEFT)) - buttons |= MOUSE_EVENT_LBUTTON; - if (state & SDL_BUTTON(SDL_BUTTON_RIGHT)) - buttons |= MOUSE_EVENT_RBUTTON; - if (state & SDL_BUTTON(SDL_BUTTON_MIDDLE)) - buttons |= MOUSE_EVENT_MBUTTON; - - if (kbd_mouse_is_absolute()) { - if (!absolute_enabled) { - sdl_hide_cursor(); - if (gui_grab) { - sdl_grab_end(); - } - absolute_enabled = 1; - } - - dx = x * 0x7FFF / (width - 1); - dy = y * 0x7FFF / (height - 1); - } else if (absolute_enabled) { - sdl_show_cursor(); - absolute_enabled = 0; - } else if (guest_cursor) { - x -= guest_x; - y -= guest_y; - guest_x += x; - guest_y += y; - dx = x; - dy = y; - } - - kbd_mouse_event(dx, dy, dz, buttons); -} - -static void toggle_full_screen(DisplayState *ds) -{ - gui_fullscreen = !gui_fullscreen; - do_sdl_resize(real_screen->w, real_screen->h, real_screen->format->BitsPerPixel); - if (gui_fullscreen) { - scaling_active = 0; - gui_saved_grab = gui_grab; - sdl_grab_start(); - } else { - if (!gui_saved_grab) - sdl_grab_end(); - } - vga_hw_invalidate(); - vga_hw_update(); -} - -static void sdl_refresh(DisplayState *ds) -{ - SDL_Event ev1, *ev = &ev1; - int mod_state; - int buttonstate = SDL_GetMouseState(NULL, NULL); - - if (last_vm_running != vm_running) { - last_vm_running = vm_running; - sdl_update_caption(); - } - - vga_hw_update(); - SDL_EnableUNICODE(!is_graphic_console()); - - while (SDL_PollEvent(ev)) { - switch (ev->type) { - case SDL_VIDEOEXPOSE: - sdl_update(ds, 0, 0, real_screen->w, real_screen->h); - break; - case SDL_KEYDOWN: - case SDL_KEYUP: - if (ev->type == SDL_KEYDOWN) { - if (alt_grab) { - mod_state = (SDL_GetModState() & (gui_grab_code | KMOD_LSHIFT)) == - (gui_grab_code | KMOD_LSHIFT); - } else if (ctrl_grab) { - mod_state = (SDL_GetModState() & KMOD_RCTRL) == KMOD_RCTRL; - } else { - mod_state = (SDL_GetModState() & gui_grab_code) == - gui_grab_code; - } - gui_key_modifier_pressed = mod_state; - if (gui_key_modifier_pressed) { - int keycode; - keycode = sdl_keyevent_to_keycode(&ev->key); - switch(keycode) { - case 0x21: /* 'f' key on US keyboard */ - toggle_full_screen(ds); - gui_keysym = 1; - break; - case 0x16: /* 'u' key on US keyboard */ - scaling_active = 0; - sdl_resize(ds); - vga_hw_invalidate(); - vga_hw_update(); - break; - case 0x02 ... 0x0a: /* '1' to '9' keys */ - /* Reset the modifiers sent to the current console */ - reset_keys(); - console_select(keycode - 0x02); - if (!is_graphic_console()) { - /* display grab if going to a text console */ - if (gui_grab) - sdl_grab_end(); - } - gui_keysym = 1; - break; - default: - break; - } - } else if (!is_graphic_console()) { - int keysym; - keysym = 0; - if (ev->key.keysym.mod & (KMOD_LCTRL | KMOD_RCTRL)) { - switch(ev->key.keysym.sym) { - case SDLK_UP: keysym = QEMU_KEY_CTRL_UP; break; - case SDLK_DOWN: keysym = QEMU_KEY_CTRL_DOWN; break; - case SDLK_LEFT: keysym = QEMU_KEY_CTRL_LEFT; break; - case SDLK_RIGHT: keysym = QEMU_KEY_CTRL_RIGHT; break; - case SDLK_HOME: keysym = QEMU_KEY_CTRL_HOME; break; - case SDLK_END: keysym = QEMU_KEY_CTRL_END; break; - case SDLK_PAGEUP: keysym = QEMU_KEY_CTRL_PAGEUP; break; - case SDLK_PAGEDOWN: keysym = QEMU_KEY_CTRL_PAGEDOWN; break; - default: break; - } - } else { - switch(ev->key.keysym.sym) { - case SDLK_UP: keysym = QEMU_KEY_UP; break; - case SDLK_DOWN: keysym = QEMU_KEY_DOWN; break; - case SDLK_LEFT: keysym = QEMU_KEY_LEFT; break; - case SDLK_RIGHT: keysym = QEMU_KEY_RIGHT; break; - case SDLK_HOME: keysym = QEMU_KEY_HOME; break; - case SDLK_END: keysym = QEMU_KEY_END; break; - case SDLK_PAGEUP: keysym = QEMU_KEY_PAGEUP; break; - case SDLK_PAGEDOWN: keysym = QEMU_KEY_PAGEDOWN; break; - case SDLK_BACKSPACE: keysym = QEMU_KEY_BACKSPACE; break; - case SDLK_DELETE: keysym = QEMU_KEY_DELETE; break; - default: break; - } - } - if (keysym) { - kbd_put_keysym(keysym); - } else if (ev->key.keysym.unicode != 0) { - kbd_put_keysym(ev->key.keysym.unicode); - } - } - } else if (ev->type == SDL_KEYUP) { - if (!alt_grab) { - mod_state = (ev->key.keysym.mod & gui_grab_code); - } else { - mod_state = (ev->key.keysym.mod & - (gui_grab_code | KMOD_LSHIFT)); - } - if (!mod_state) { - if (gui_key_modifier_pressed) { - gui_key_modifier_pressed = 0; - if (gui_keysym == 0) { - /* exit/enter grab if pressing Ctrl-Alt */ - if (!gui_grab) { - /* if the application is not active, - do not try to enter grab state. It - prevents - 'SDL_WM_GrabInput(SDL_GRAB_ON)' - from blocking all the application - (SDL bug). */ - if (SDL_GetAppState() & SDL_APPACTIVE) - sdl_grab_start(); - } else { - sdl_grab_end(); - } - /* SDL does not send back all the - modifiers key, so we must correct it */ - reset_keys(); - break; - } - gui_keysym = 0; - } - } - } - if (is_graphic_console() && !gui_keysym) - sdl_process_key(&ev->key); - break; - case SDL_QUIT: - if (!no_quit) - qemu_system_shutdown_request(); - break; - case SDL_MOUSEMOTION: - if (gui_grab || kbd_mouse_is_absolute() || - absolute_enabled) { - sdl_send_mouse_event(ev->motion.xrel, ev->motion.yrel, 0, - ev->motion.x, ev->motion.y, ev->motion.state); - } - break; - case SDL_MOUSEBUTTONDOWN: - case SDL_MOUSEBUTTONUP: - { - SDL_MouseButtonEvent *bev = &ev->button; - if (!gui_grab && !kbd_mouse_is_absolute()) { - if (ev->type == SDL_MOUSEBUTTONDOWN && - (bev->button == SDL_BUTTON_LEFT)) { - /* start grabbing all events */ - sdl_grab_start(); - } - } else { - int dz; - dz = 0; - if (ev->type == SDL_MOUSEBUTTONDOWN) { - buttonstate |= SDL_BUTTON(bev->button); - } else { - buttonstate &= ~SDL_BUTTON(bev->button); - } -#ifdef SDL_BUTTON_WHEELUP - if (bev->button == SDL_BUTTON_WHEELUP && ev->type == SDL_MOUSEBUTTONDOWN) { - dz = -1; - } else if (bev->button == SDL_BUTTON_WHEELDOWN && ev->type == SDL_MOUSEBUTTONDOWN) { - dz = 1; - } -#endif - sdl_send_mouse_event(0, 0, dz, bev->x, bev->y, buttonstate); - } - } - break; - case SDL_ACTIVEEVENT: - if (gui_grab && ev->active.state == SDL_APPINPUTFOCUS && - !ev->active.gain && !gui_fullscreen_initial_grab) { - sdl_grab_end(); - } - if (ev->active.state & SDL_APPACTIVE) { - if (ev->active.gain) { - /* Back to default interval */ - dcl->gui_timer_interval = 0; - dcl->idle = 0; - } else { - /* Sleeping interval */ - dcl->gui_timer_interval = 500; - dcl->idle = 1; - } - } - break; - case SDL_VIDEORESIZE: - { - SDL_ResizeEvent *rev = &ev->resize; - int bpp = real_screen->format->BitsPerPixel; - if (bpp != 16 && bpp != 32) - bpp = 32; - do_sdl_resize(rev->w, rev->h, bpp); - scaling_active = 1; - if (!is_buffer_shared(ds->surface)) { - ds->surface = qemu_resize_displaysurface(ds, ds_get_width(ds), ds_get_height(ds)); - dpy_resize(ds); - } - vga_hw_invalidate(); - vga_hw_update(); - break; - } - default: - break; - } - } -} - -static void sdl_fill(DisplayState *ds, int x, int y, int w, int h, uint32_t c) -{ - SDL_Rect dst = { x, y, w, h }; - SDL_FillRect(real_screen, &dst, c); -} - -static void sdl_mouse_warp(int x, int y, int on) -{ - if (on) { - if (!guest_cursor) - sdl_show_cursor(); - if (gui_grab || kbd_mouse_is_absolute() || absolute_enabled) { - SDL_SetCursor(guest_sprite); - if (!kbd_mouse_is_absolute() && !absolute_enabled) - SDL_WarpMouse(x, y); - } - } else if (gui_grab) - sdl_hide_cursor(); - guest_cursor = on; - guest_x = x, guest_y = y; -} - -static void sdl_mouse_define(int width, int height, int bpp, - int hot_x, int hot_y, - uint8_t *image, uint8_t *mask) -{ - uint8_t sprite[256], *line; - int x, y, dst, bypl, src = 0; - if (guest_sprite) - SDL_FreeCursor(guest_sprite); - - memset(sprite, 0, 256); - bypl = ((width * bpp + 31) >> 5) << 2; - for (y = 0, dst = 0; y < height; y ++, image += bypl) { - line = image; - for (x = 0; x < width; x ++, dst ++) { - switch (bpp) { - case 32: - src = *(line ++); src |= *(line ++); src |= *(line ++); line++; - break; - case 24: - src = *(line ++); src |= *(line ++); src |= *(line ++); - break; - case 16: - case 15: - src = *(line ++); src |= *(line ++); - break; - case 8: - src = *(line ++); - break; - case 4: - src = 0xf & (line[x >> 1] >> ((x & 1)) << 2); - break; - case 2: - src = 3 & (line[x >> 2] >> ((x & 3)) << 1); - break; - case 1: - src = 1 & (line[x >> 3] >> (x & 7)); - break; - } - if (!src) - sprite[dst >> 3] |= (1 << (~dst & 7)) & mask[dst >> 3]; - } - } - guest_sprite = SDL_CreateCursor(sprite, mask, width, height, hot_x, hot_y); - - if (guest_cursor && - (gui_grab || kbd_mouse_is_absolute() || absolute_enabled)) - SDL_SetCursor(guest_sprite); -} - -static void sdl_cleanup(void) -{ - if (guest_sprite) - SDL_FreeCursor(guest_sprite); - SDL_QuitSubSystem(SDL_INIT_VIDEO); -} - -void sdl_display_init(DisplayState *ds, int full_screen, int no_frame) -{ - int flags; - uint8_t data = 0; - DisplayAllocator *da; - const SDL_VideoInfo *vi; - -#if defined(__APPLE__) - /* always use generic keymaps */ - if (!keyboard_layout) - keyboard_layout = "en-us"; -#endif - if(keyboard_layout) { - kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout); - if (!kbd_layout) - exit(1); - } - - if (no_frame) - gui_noframe = 1; - - flags = SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE; - if (SDL_Init (flags)) { - fprintf(stderr, "Could not initialize SDL - exiting\n"); - exit(1); - } - vi = SDL_GetVideoInfo(); - host_format = *(vi->vfmt); - - dcl = qemu_mallocz(sizeof(DisplayChangeListener)); - dcl->dpy_update = sdl_update; - dcl->dpy_resize = sdl_resize; - dcl->dpy_refresh = sdl_refresh; - dcl->dpy_setdata = sdl_setdata; - dcl->dpy_fill = sdl_fill; - ds->mouse_set = sdl_mouse_warp; - ds->cursor_define = sdl_mouse_define; - register_displaychangelistener(ds, dcl); - - da = qemu_mallocz(sizeof(DisplayAllocator)); - da->create_displaysurface = sdl_create_displaysurface; - da->resize_displaysurface = sdl_resize_displaysurface; - da->free_displaysurface = sdl_free_displaysurface; - if (register_displayallocator(ds, da) == da) { - DisplaySurface *surf; - surf = sdl_create_displaysurface(ds_get_width(ds), ds_get_height(ds)); - defaultallocator_free_displaysurface(ds->surface); - ds->surface = surf; - dpy_resize(ds); - } - - sdl_update_caption(); - SDL_EnableKeyRepeat(250, 50); - gui_grab = 0; - - sdl_cursor_hidden = SDL_CreateCursor(&data, &data, 8, 1, 0, 0); - sdl_cursor_normal = SDL_GetCursor(); - - atexit(sdl_cleanup); - if (full_screen) { - gui_fullscreen = 1; - gui_fullscreen_initial_grab = 1; - sdl_grab_start(); - } -} diff -Nru qemu-kvm-0.12.5+noroms/sdl_keysym.h qemu-kvm-0.14.1/sdl_keysym.h --- qemu-kvm-0.12.5+noroms/sdl_keysym.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/sdl_keysym.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,277 +0,0 @@ - -#include "keymaps.h" - -static const name2keysym_t name2keysym[]={ -/* ascii */ - { "space", 0x020}, - { "exclam", 0x021}, - { "quotedbl", 0x022}, - { "numbersign", 0x023}, - { "dollar", 0x024}, - { "percent", 0x025}, - { "ampersand", 0x026}, - { "apostrophe", 0x027}, - { "parenleft", 0x028}, - { "parenright", 0x029}, - { "asterisk", 0x02a}, - { "plus", 0x02b}, - { "comma", 0x02c}, - { "minus", 0x02d}, - { "period", 0x02e}, - { "slash", 0x02f}, - { "0", 0x030}, - { "1", 0x031}, - { "2", 0x032}, - { "3", 0x033}, - { "4", 0x034}, - { "5", 0x035}, - { "6", 0x036}, - { "7", 0x037}, - { "8", 0x038}, - { "9", 0x039}, - { "colon", 0x03a}, - { "semicolon", 0x03b}, - { "less", 0x03c}, - { "equal", 0x03d}, - { "greater", 0x03e}, - { "question", 0x03f}, - { "at", 0x040}, - { "A", 0x041}, - { "B", 0x042}, - { "C", 0x043}, - { "D", 0x044}, - { "E", 0x045}, - { "F", 0x046}, - { "G", 0x047}, - { "H", 0x048}, - { "I", 0x049}, - { "J", 0x04a}, - { "K", 0x04b}, - { "L", 0x04c}, - { "M", 0x04d}, - { "N", 0x04e}, - { "O", 0x04f}, - { "P", 0x050}, - { "Q", 0x051}, - { "R", 0x052}, - { "S", 0x053}, - { "T", 0x054}, - { "U", 0x055}, - { "V", 0x056}, - { "W", 0x057}, - { "X", 0x058}, - { "Y", 0x059}, - { "Z", 0x05a}, - { "bracketleft", 0x05b}, - { "backslash", 0x05c}, - { "bracketright", 0x05d}, - { "asciicircum", 0x05e}, - { "underscore", 0x05f}, - { "grave", 0x060}, - { "a", 0x061}, - { "b", 0x062}, - { "c", 0x063}, - { "d", 0x064}, - { "e", 0x065}, - { "f", 0x066}, - { "g", 0x067}, - { "h", 0x068}, - { "i", 0x069}, - { "j", 0x06a}, - { "k", 0x06b}, - { "l", 0x06c}, - { "m", 0x06d}, - { "n", 0x06e}, - { "o", 0x06f}, - { "p", 0x070}, - { "q", 0x071}, - { "r", 0x072}, - { "s", 0x073}, - { "t", 0x074}, - { "u", 0x075}, - { "v", 0x076}, - { "w", 0x077}, - { "x", 0x078}, - { "y", 0x079}, - { "z", 0x07a}, - { "braceleft", 0x07b}, - { "bar", 0x07c}, - { "braceright", 0x07d}, - { "asciitilde", 0x07e}, - -/* latin 1 extensions */ -{ "nobreakspace", 0x0a0}, -{ "exclamdown", 0x0a1}, -{ "cent", 0x0a2}, -{ "sterling", 0x0a3}, -{ "currency", 0x0a4}, -{ "yen", 0x0a5}, -{ "brokenbar", 0x0a6}, -{ "section", 0x0a7}, -{ "diaeresis", 0x0a8}, -{ "copyright", 0x0a9}, -{ "ordfeminine", 0x0aa}, -{ "guillemotleft", 0x0ab}, -{ "notsign", 0x0ac}, -{ "hyphen", 0x0ad}, -{ "registered", 0x0ae}, -{ "macron", 0x0af}, -{ "degree", 0x0b0}, -{ "plusminus", 0x0b1}, -{ "twosuperior", 0x0b2}, -{ "threesuperior", 0x0b3}, -{ "acute", 0x0b4}, -{ "mu", 0x0b5}, -{ "paragraph", 0x0b6}, -{ "periodcentered", 0x0b7}, -{ "cedilla", 0x0b8}, -{ "onesuperior", 0x0b9}, -{ "masculine", 0x0ba}, -{ "guillemotright", 0x0bb}, -{ "onequarter", 0x0bc}, -{ "onehalf", 0x0bd}, -{ "threequarters", 0x0be}, -{ "questiondown", 0x0bf}, -{ "Agrave", 0x0c0}, -{ "Aacute", 0x0c1}, -{ "Acircumflex", 0x0c2}, -{ "Atilde", 0x0c3}, -{ "Adiaeresis", 0x0c4}, -{ "Aring", 0x0c5}, -{ "AE", 0x0c6}, -{ "Ccedilla", 0x0c7}, -{ "Egrave", 0x0c8}, -{ "Eacute", 0x0c9}, -{ "Ecircumflex", 0x0ca}, -{ "Ediaeresis", 0x0cb}, -{ "Igrave", 0x0cc}, -{ "Iacute", 0x0cd}, -{ "Icircumflex", 0x0ce}, -{ "Idiaeresis", 0x0cf}, -{ "ETH", 0x0d0}, -{ "Eth", 0x0d0}, -{ "Ntilde", 0x0d1}, -{ "Ograve", 0x0d2}, -{ "Oacute", 0x0d3}, -{ "Ocircumflex", 0x0d4}, -{ "Otilde", 0x0d5}, -{ "Odiaeresis", 0x0d6}, -{ "multiply", 0x0d7}, -{ "Ooblique", 0x0d8}, -{ "Oslash", 0x0d8}, -{ "Ugrave", 0x0d9}, -{ "Uacute", 0x0da}, -{ "Ucircumflex", 0x0db}, -{ "Udiaeresis", 0x0dc}, -{ "Yacute", 0x0dd}, -{ "THORN", 0x0de}, -{ "Thorn", 0x0de}, -{ "ssharp", 0x0df}, -{ "agrave", 0x0e0}, -{ "aacute", 0x0e1}, -{ "acircumflex", 0x0e2}, -{ "atilde", 0x0e3}, -{ "adiaeresis", 0x0e4}, -{ "aring", 0x0e5}, -{ "ae", 0x0e6}, -{ "ccedilla", 0x0e7}, -{ "egrave", 0x0e8}, -{ "eacute", 0x0e9}, -{ "ecircumflex", 0x0ea}, -{ "ediaeresis", 0x0eb}, -{ "igrave", 0x0ec}, -{ "iacute", 0x0ed}, -{ "icircumflex", 0x0ee}, -{ "idiaeresis", 0x0ef}, -{ "eth", 0x0f0}, -{ "ntilde", 0x0f1}, -{ "ograve", 0x0f2}, -{ "oacute", 0x0f3}, -{ "ocircumflex", 0x0f4}, -{ "otilde", 0x0f5}, -{ "odiaeresis", 0x0f6}, -{ "division", 0x0f7}, -{ "oslash", 0x0f8}, -{ "ooblique", 0x0f8}, -{ "ugrave", 0x0f9}, -{ "uacute", 0x0fa}, -{ "ucircumflex", 0x0fb}, -{ "udiaeresis", 0x0fc}, -{ "yacute", 0x0fd}, -{ "thorn", 0x0fe}, -{ "ydiaeresis", 0x0ff}, -{"EuroSign", SDLK_EURO}, - - /* modifiers */ -{"Control_L", SDLK_LCTRL}, -{"Control_R", SDLK_RCTRL}, -{"Alt_L", SDLK_LALT}, -{"Alt_R", SDLK_RALT}, -{"Caps_Lock", SDLK_CAPSLOCK}, -{"Meta_L", SDLK_LMETA}, -{"Meta_R", SDLK_RMETA}, -{"Shift_L", SDLK_LSHIFT}, -{"Shift_R", SDLK_RSHIFT}, -{"Super_L", SDLK_LSUPER}, -{"Super_R", SDLK_RSUPER}, - - /* special keys */ -{"BackSpace", SDLK_BACKSPACE}, -{"Tab", SDLK_TAB}, -{"Return", SDLK_RETURN}, -{"Right", SDLK_RIGHT}, -{"Left", SDLK_LEFT}, -{"Up", SDLK_UP}, -{"Down", SDLK_DOWN}, -{"Page_Down", SDLK_PAGEDOWN}, -{"Page_Up", SDLK_PAGEUP}, -{"Insert", SDLK_INSERT}, -{"Delete", SDLK_DELETE}, -{"Home", SDLK_HOME}, -{"End", SDLK_END}, -{"Scroll_Lock", SDLK_SCROLLOCK}, -{"F1", SDLK_F1}, -{"F2", SDLK_F2}, -{"F3", SDLK_F3}, -{"F4", SDLK_F4}, -{"F5", SDLK_F5}, -{"F6", SDLK_F6}, -{"F7", SDLK_F7}, -{"F8", SDLK_F8}, -{"F9", SDLK_F9}, -{"F10", SDLK_F10}, -{"F11", SDLK_F11}, -{"F12", SDLK_F12}, -{"F13", SDLK_F13}, -{"F14", SDLK_F14}, -{"F15", SDLK_F15}, -{"Sys_Req", SDLK_SYSREQ}, -{"KP_0", SDLK_KP0}, -{"KP_1", SDLK_KP1}, -{"KP_2", SDLK_KP2}, -{"KP_3", SDLK_KP3}, -{"KP_4", SDLK_KP4}, -{"KP_5", SDLK_KP5}, -{"KP_6", SDLK_KP6}, -{"KP_7", SDLK_KP7}, -{"KP_8", SDLK_KP8}, -{"KP_9", SDLK_KP9}, -{"KP_Add", SDLK_KP_PLUS}, -{"KP_Decimal", SDLK_KP_PERIOD}, -{"KP_Divide", SDLK_KP_DIVIDE}, -{"KP_Enter", SDLK_KP_ENTER}, -{"KP_Equal", SDLK_KP_EQUALS}, -{"KP_Multiply", SDLK_KP_MULTIPLY}, -{"KP_Subtract", SDLK_KP_MINUS}, -{"help", SDLK_HELP}, -{"Menu", SDLK_MENU}, -{"Power", SDLK_POWER}, -{"Print", SDLK_PRINT}, -{"Mode_switch", SDLK_MODE}, -{"Multi_Key", SDLK_COMPOSE}, -{"Num_Lock", SDLK_NUMLOCK}, -{"Pause", SDLK_PAUSE}, -{"Escape", SDLK_ESCAPE}, - -{NULL, 0}, -}; diff -Nru qemu-kvm-0.12.5+noroms/sdl_zoom.c qemu-kvm-0.14.1/sdl_zoom.c --- qemu-kvm-0.12.5+noroms/sdl_zoom.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/sdl_zoom.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,95 +0,0 @@ -/* - * SDL_zoom - surface scaling - * - * Copyright (c) 2009 Citrix Systems, Inc. - * - * Derived from: SDL_rotozoom, LGPL (c) A. Schiffler from the SDL_gfx library. - * Modifications by Stefano Stabellini. - * - * This work is licensed under the terms of the GNU GPL version 2. - * See the COPYING file in the top-level directory. - * - */ - -#include "sdl_zoom.h" -#include "osdep.h" -#include -#include - -static int sdl_zoom_rgb16(SDL_Surface *src, SDL_Surface *dst, int smooth, - SDL_Rect *dst_rect); -static int sdl_zoom_rgb32(SDL_Surface *src, SDL_Surface *dst, int smooth, - SDL_Rect *dst_rect); - -#define BPP 32 -#include "sdl_zoom_template.h" -#undef BPP -#define BPP 16 -#include "sdl_zoom_template.h" -#undef BPP - -int sdl_zoom_blit(SDL_Surface *src_sfc, SDL_Surface *dst_sfc, int smooth, - SDL_Rect *in_rect) -{ - SDL_Rect zoom, src_rect; - int extra; - - /* Grow the size of the modified rectangle to avoid edge artefacts */ - src_rect.x = (in_rect->x > 0) ? (in_rect->x - 1) : 0; - src_rect.y = (in_rect->y > 0) ? (in_rect->y - 1) : 0; - - src_rect.w = in_rect->w + 1; - if (src_rect.x + src_rect.w > src_sfc->w) - src_rect.w = src_sfc->w - src_rect.x; - - src_rect.h = in_rect->h + 1; - if (src_rect.y + src_rect.h > src_sfc->h) - src_rect.h = src_sfc->h - src_rect.y; - - /* (x,y) : round down */ - zoom.x = (int)(((float)(src_rect.x * dst_sfc->w)) / (float)(src_sfc->w)); - zoom.y = (int)(((float)(src_rect.y * dst_sfc->h)) / (float)(src_sfc->h)); - - /* (w,h) : round up */ - zoom.w = (int)( ((double)((src_rect.w * dst_sfc->w) + (src_sfc->w - 1))) / - (double)(src_sfc->w)); - - zoom.h = (int)( ((double)((src_rect.h * dst_sfc->h) + (src_sfc->h - 1))) / - (double)(src_sfc->h)); - - /* Account for any (x,y) rounding by adding one-source-pixel's worth - * of destination pixels and then edge checking. - */ - - extra = ((dst_sfc->w-1) / src_sfc->w) + 1; - - if ((zoom.x + zoom.w) < (dst_sfc->w - extra)) - zoom.w += extra; - else - zoom.w = dst_sfc->w - zoom.x; - - extra = ((dst_sfc->h-1) / src_sfc->h) + 1; - - if ((zoom.y + zoom.h) < (dst_sfc->h - extra)) - zoom.h += extra; - else - zoom.h = dst_sfc->h - zoom.y; - - /* The rectangle (zoom.x, zoom.y, zoom.w, zoom.h) is the area on the - * destination surface that needs to be updated. - */ - if (src_sfc->format->BitsPerPixel == 32) - sdl_zoom_rgb32(src_sfc, dst_sfc, smooth, &zoom); - else if (src_sfc->format->BitsPerPixel == 16) - sdl_zoom_rgb16(src_sfc, dst_sfc, smooth, &zoom); - else { - fprintf(stderr, "pixel format not supported\n"); - return -1; - } - - /* Return the rectangle of the update to the caller */ - *in_rect = zoom; - - return 0; -} - diff -Nru qemu-kvm-0.12.5+noroms/sdl_zoom.h qemu-kvm-0.14.1/sdl_zoom.h --- qemu-kvm-0.12.5+noroms/sdl_zoom.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/sdl_zoom.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,25 +0,0 @@ -/* - * SDL_zoom - surface scaling - * - * Copyright (c) 2009 Citrix Systems, Inc. - * - * Derived from: SDL_rotozoom, LGPL (c) A. Schiffler from the SDL_gfx library. - * Modifications by Stefano Stabellini. - * - * This work is licensed under the terms of the GNU GPL version 2. - * See the COPYING file in the top-level directory. - * - */ - -#ifndef SDL_zoom_h -#define SDL_zoom_h - -#include - -#define SMOOTHING_OFF 0 -#define SMOOTHING_ON 1 - -int sdl_zoom_blit(SDL_Surface *src_sfc, SDL_Surface *dst_sfc, - int smooth, SDL_Rect *src_rect); - -#endif /* SDL_zoom_h */ diff -Nru qemu-kvm-0.12.5+noroms/sdl_zoom_template.h qemu-kvm-0.14.1/sdl_zoom_template.h --- qemu-kvm-0.12.5+noroms/sdl_zoom_template.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/sdl_zoom_template.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,225 +0,0 @@ -/* - * SDL_zoom_template - surface scaling - * - * Copyright (c) 2009 Citrix Systems, Inc. - * - * Derived from: SDL_rotozoom, LGPL (c) A. Schiffler from the SDL_gfx library. - * Modifications by Stefano Stabellini. - * - * This work is licensed under the terms of the GNU GPL version 2. - * See the COPYING file in the top-level directory. - * - */ - -#if BPP == 16 -#define SDL_TYPE Uint16 -#elif BPP == 32 -#define SDL_TYPE Uint32 -#else -#error unsupport depth -#endif - -/* - * Simple helper functions to make the code looks nicer - * - * Assume spf = source SDL_PixelFormat - * dpf = dest SDL_PixelFormat - * - */ -#define getRed(color) (((color) & spf->Rmask) >> spf->Rshift) -#define getGreen(color) (((color) & spf->Gmask) >> spf->Gshift) -#define getBlue(color) (((color) & spf->Bmask) >> spf->Bshift) -#define getAlpha(color) (((color) & spf->Amask) >> spf->Ashift) - -#define setRed(r, pcolor) do { \ - *pcolor = ((*pcolor) & (~(dpf->Rmask))) + \ - (((r) & (dpf->Rmask >> dpf->Rshift)) << dpf->Rshift); \ -} while (0); - -#define setGreen(g, pcolor) do { \ - *pcolor = ((*pcolor) & (~(dpf->Gmask))) + \ - (((g) & (dpf->Gmask >> dpf->Gshift)) << dpf->Gshift); \ -} while (0); - -#define setBlue(b, pcolor) do { \ - *pcolor = ((*pcolor) & (~(dpf->Bmask))) + \ - (((b) & (dpf->Bmask >> dpf->Bshift)) << dpf->Bshift); \ -} while (0); - -#define setAlpha(a, pcolor) do { \ - *pcolor = ((*pcolor) & (~(dpf->Amask))) + \ - (((a) & (dpf->Amask >> dpf->Ashift)) << dpf->Ashift); \ -} while (0); - -static int glue(sdl_zoom_rgb, BPP)(SDL_Surface *src, SDL_Surface *dst, int smooth, - SDL_Rect *dst_rect) -{ - int x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy, ex, ey, t1, t2, sstep, sstep_jump; - SDL_TYPE *c00, *c01, *c10, *c11, *sp, *csp, *dp; - int d_gap; - SDL_PixelFormat *spf = src->format; - SDL_PixelFormat *dpf = dst->format; - - if (smooth) { - /* For interpolation: assume source dimension is one pixel. - * Smaller here to avoid overflow on right and bottom edge. - */ - sx = (int) (65536.0 * (float) (src->w - 1) / (float) dst->w); - sy = (int) (65536.0 * (float) (src->h - 1) / (float) dst->h); - } else { - sx = (int) (65536.0 * (float) src->w / (float) dst->w); - sy = (int) (65536.0 * (float) src->h / (float) dst->h); - } - - if ((sax = (int *) malloc((dst->w + 1) * sizeof(Uint32))) == NULL) { - return (-1); - } - if ((say = (int *) malloc((dst->h + 1) * sizeof(Uint32))) == NULL) { - free(sax); - return (-1); - } - - sp = csp = (SDL_TYPE *) src->pixels; - dp = (SDL_TYPE *) (dst->pixels + dst_rect->y * dst->pitch + - dst_rect->x * dst->format->BytesPerPixel); - - csx = 0; - csax = sax; - for (x = 0; x <= dst->w; x++) { - *csax = csx; - csax++; - csx &= 0xffff; - csx += sx; - } - csy = 0; - csay = say; - for (y = 0; y <= dst->h; y++) { - *csay = csy; - csay++; - csy &= 0xffff; - csy += sy; - } - - d_gap = dst->pitch - dst_rect->w * dst->format->BytesPerPixel; - - if (smooth) { - csay = say; - for (y = 0; y < dst_rect->y; y++) { - csay++; - sstep = (*csay >> 16) * src->pitch; - csp = (SDL_TYPE *) ((Uint8 *) csp + sstep); - } - - /* Calculate sstep_jump */ - csax = sax; - sstep_jump = 0; - for (x = 0; x < dst_rect->x; x++) { - csax++; - sstep = (*csax >> 16); - sstep_jump += sstep; - } - - for (y = 0; y < dst_rect->h ; y++) { - /* Setup colour source pointers */ - c00 = csp + sstep_jump; - c01 = c00 + 1; - c10 = (SDL_TYPE *) ((Uint8 *) csp + src->pitch) + sstep_jump; - c11 = c10 + 1; - csax = sax + dst_rect->x; - - for (x = 0; x < dst_rect->w; x++) { - - /* Interpolate colours */ - ex = (*csax & 0xffff); - ey = (*csay & 0xffff); - t1 = ((((getRed(*c01) - getRed(*c00)) * ex) >> 16) + - getRed(*c00)) & (dpf->Rmask >> dpf->Rshift); - t2 = ((((getRed(*c11) - getRed(*c10)) * ex) >> 16) + - getRed(*c10)) & (dpf->Rmask >> dpf->Rshift); - setRed((((t2 - t1) * ey) >> 16) + t1, dp); - t1 = ((((getGreen(*c01) - getGreen(*c00)) * ex) >> 16) + - getGreen(*c00)) & (dpf->Gmask >> dpf->Gshift); - t2 = ((((getGreen(*c11) - getGreen(*c10)) * ex) >> 16) + - getGreen(*c10)) & (dpf->Gmask >> dpf->Gshift); - setGreen((((t2 - t1) * ey) >> 16) + t1, dp); - t1 = ((((getBlue(*c01) - getBlue(*c00)) * ex) >> 16) + - getBlue(*c00)) & (dpf->Bmask >> dpf->Bshift); - t2 = ((((getBlue(*c11) - getBlue(*c10)) * ex) >> 16) + - getBlue(*c10)) & (dpf->Bmask >> dpf->Bshift); - setBlue((((t2 - t1) * ey) >> 16) + t1, dp); - t1 = ((((getAlpha(*c01) - getAlpha(*c00)) * ex) >> 16) + - getAlpha(*c00)) & (dpf->Amask >> dpf->Ashift); - t2 = ((((getAlpha(*c11) - getAlpha(*c10)) * ex) >> 16) + - getAlpha(*c10)) & (dpf->Amask >> dpf->Ashift); - setAlpha((((t2 - t1) * ey) >> 16) + t1, dp); - - /* Advance source pointers */ - csax++; - sstep = (*csax >> 16); - c00 += sstep; - c01 += sstep; - c10 += sstep; - c11 += sstep; - /* Advance destination pointer */ - dp++; - } - /* Advance source pointer */ - csay++; - csp = (SDL_TYPE *) ((Uint8 *) csp + (*csay >> 16) * src->pitch); - /* Advance destination pointers */ - dp = (SDL_TYPE *) ((Uint8 *) dp + d_gap); - } - - - } else { - csay = say; - - for (y = 0; y < dst_rect->y; y++) { - csay++; - sstep = (*csay >> 16) * src->pitch; - csp = (SDL_TYPE *) ((Uint8 *) csp + sstep); - } - - /* Calculate sstep_jump */ - csax = sax; - sstep_jump = 0; - for (x = 0; x < dst_rect->x; x++) { - csax++; - sstep = (*csax >> 16); - sstep_jump += sstep; - } - - for (y = 0 ; y < dst_rect->h ; y++) { - sp = csp + sstep_jump; - csax = sax + dst_rect->x; - - for (x = 0; x < dst_rect->w; x++) { - - /* Draw */ - *dp = *sp; - - /* Advance source pointers */ - csax++; - sstep = (*csax >> 16); - sp += sstep; - - /* Advance destination pointer */ - dp++; - } - /* Advance source pointers */ - csay++; - sstep = (*csay >> 16) * src->pitch; - csp = (SDL_TYPE *) ((Uint8 *) csp + sstep); - - /* Advance destination pointer */ - dp = (SDL_TYPE *) ((Uint8 *) dp + d_gap); - } - } - - free(sax); - free(say); - return (0); -} - -#undef SDL_TYPE - diff -Nru qemu-kvm-0.12.5+noroms/sh4-dis.c qemu-kvm-0.14.1/sh4-dis.c --- qemu-kvm-0.12.5+noroms/sh4-dis.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/sh4-dis.c 2011-05-11 13:29:46.000000000 +0000 @@ -1163,15 +1163,9 @@ #define INCLUDE_SHMEDIA #endif -static void print_movxy - (const sh_opcode_info *, int, int, fprintf_ftype, void *); -static void print_insn_ddt (int, struct disassemble_info *); -static void print_dsp_reg (int, fprintf_ftype, void *); -static void print_insn_ppi (int, struct disassemble_info *); - static void print_movxy (const sh_opcode_info *op, int rn, int rm, - fprintf_ftype fprintf_fn, void *stream) + fprintf_function fprintf_fn, void *stream) { int n; @@ -1247,7 +1241,7 @@ static void print_insn_ddt (int insn, struct disassemble_info *info) { - fprintf_ftype fprintf_fn = info->fprintf_func; + fprintf_function fprintf_fn = info->fprintf_func; void *stream = info->stream; /* If this is just a nop, make sure to emit something. */ @@ -1332,7 +1326,7 @@ } static void -print_dsp_reg (int rm, fprintf_ftype fprintf_fn, void *stream) +print_dsp_reg (int rm, fprintf_function fprintf_fn, void *stream) { switch (rm) { @@ -1377,7 +1371,7 @@ { static const char *sx_tab[] = { "x0", "x1", "a0", "a1" }; static const char *sy_tab[] = { "y0", "y1", "m0", "m1" }; - fprintf_ftype fprintf_fn = info->fprintf_func; + fprintf_function fprintf_fn = info->fprintf_func; void *stream = info->stream; unsigned int nib1, nib2, nib3; unsigned int altnib1, nib4; @@ -1493,10 +1487,10 @@ print_dsp_reg (field_b & 0xf, fprintf_fn, stream); break; case DSP_REG_X: - fprintf_fn (stream, sx_tab[(field_b >> 6) & 3]); + fprintf_fn (stream, "%s", sx_tab[(field_b >> 6) & 3]); break; case DSP_REG_Y: - fprintf_fn (stream, sy_tab[(field_b >> 4) & 3]); + fprintf_fn (stream, "%s", sy_tab[(field_b >> 4) & 3]); break; case A_MACH: fprintf_fn (stream, "mach"); @@ -1520,7 +1514,7 @@ int print_insn_sh (bfd_vma memaddr, struct disassemble_info *info) { - fprintf_ftype fprintf_fn = info->fprintf_func; + fprintf_function fprintf_fn = info->fprintf_func; void *stream = info->stream; unsigned char insn[4]; unsigned char nibs[8]; diff -Nru qemu-kvm-0.12.5+noroms/simpletrace.c qemu-kvm-0.14.1/simpletrace.c --- qemu-kvm-0.12.5+noroms/simpletrace.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/simpletrace.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,255 @@ +/* + * Simple trace backend + * + * Copyright IBM, Corp. 2010 + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ + +#include +#include +#include +#include +#include "qemu-timer.h" +#include "trace.h" + +/** Trace file header event ID */ +#define HEADER_EVENT_ID (~(uint64_t)0) /* avoids conflicting with TraceEventIDs */ + +/** Trace file magic number */ +#define HEADER_MAGIC 0xf2b177cb0aa429b4ULL + +/** Trace file version number, bump if format changes */ +#define HEADER_VERSION 0 + +/** Trace buffer entry */ +typedef struct { + uint64_t event; + uint64_t timestamp_ns; + uint64_t x1; + uint64_t x2; + uint64_t x3; + uint64_t x4; + uint64_t x5; + uint64_t x6; +} TraceRecord; + +enum { + TRACE_BUF_LEN = 64 * 1024 / sizeof(TraceRecord), +}; + +static TraceRecord trace_buf[TRACE_BUF_LEN]; +static unsigned int trace_idx; +static FILE *trace_fp; +static char *trace_file_name = NULL; +static bool trace_file_enabled = false; + +void st_print_trace_file_status(FILE *stream, int (*stream_printf)(FILE *stream, const char *fmt, ...)) +{ + stream_printf(stream, "Trace file \"%s\" %s.\n", + trace_file_name, trace_file_enabled ? "on" : "off"); +} + +static bool write_header(FILE *fp) +{ + static const TraceRecord header = { + .event = HEADER_EVENT_ID, + .timestamp_ns = HEADER_MAGIC, + .x1 = HEADER_VERSION, + }; + + return fwrite(&header, sizeof header, 1, fp) == 1; +} + +/** + * set_trace_file : To set the name of a trace file. + * @file : pointer to the name to be set. + * If NULL, set to the default name- set at config time. + */ +bool st_set_trace_file(const char *file) +{ + st_set_trace_file_enabled(false); + + free(trace_file_name); + + if (!file) { + if (asprintf(&trace_file_name, CONFIG_TRACE_FILE, getpid()) < 0) { + trace_file_name = NULL; + return false; + } + } else { + if (asprintf(&trace_file_name, "%s", file) < 0) { + trace_file_name = NULL; + return false; + } + } + + st_set_trace_file_enabled(true); + return true; +} + +static void flush_trace_file(void) +{ + /* If the trace file is not open yet, open it now */ + if (!trace_fp) { + trace_fp = fopen(trace_file_name, "w"); + if (!trace_fp) { + /* Avoid repeatedly trying to open file on failure */ + trace_file_enabled = false; + return; + } + write_header(trace_fp); + } + + if (trace_fp) { + size_t unused; /* for when fwrite(3) is declared warn_unused_result */ + unused = fwrite(trace_buf, trace_idx * sizeof(trace_buf[0]), 1, trace_fp); + } +} + +void st_flush_trace_buffer(void) +{ + if (trace_file_enabled) { + flush_trace_file(); + } + + /* Discard written trace records */ + trace_idx = 0; +} + +void st_set_trace_file_enabled(bool enable) +{ + if (enable == trace_file_enabled) { + return; /* no change */ + } + + /* Flush/discard trace buffer */ + st_flush_trace_buffer(); + + /* To disable, close trace file */ + if (!enable) { + fclose(trace_fp); + trace_fp = NULL; + } + + trace_file_enabled = enable; +} + +static void trace(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3, + uint64_t x4, uint64_t x5, uint64_t x6) +{ + TraceRecord *rec = &trace_buf[trace_idx]; + + if (!trace_list[event].state) { + return; + } + + rec->event = event; + rec->timestamp_ns = get_clock(); + rec->x1 = x1; + rec->x2 = x2; + rec->x3 = x3; + rec->x4 = x4; + rec->x5 = x5; + rec->x6 = x6; + + if (++trace_idx == TRACE_BUF_LEN) { + st_flush_trace_buffer(); + } +} + +void trace0(TraceEventID event) +{ + trace(event, 0, 0, 0, 0, 0, 0); +} + +void trace1(TraceEventID event, uint64_t x1) +{ + trace(event, x1, 0, 0, 0, 0, 0); +} + +void trace2(TraceEventID event, uint64_t x1, uint64_t x2) +{ + trace(event, x1, x2, 0, 0, 0, 0); +} + +void trace3(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3) +{ + trace(event, x1, x2, x3, 0, 0, 0); +} + +void trace4(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4) +{ + trace(event, x1, x2, x3, x4, 0, 0); +} + +void trace5(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, uint64_t x5) +{ + trace(event, x1, x2, x3, x4, x5, 0); +} + +void trace6(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, uint64_t x5, uint64_t x6) +{ + trace(event, x1, x2, x3, x4, x5, x6); +} + +/** + * Flush the trace buffer on exit + */ +static void __attribute__((constructor)) st_init(void) +{ + atexit(st_flush_trace_buffer); +} + +void st_print_trace(FILE *stream, int (*stream_printf)(FILE *stream, const char *fmt, ...)) +{ + unsigned int i; + + for (i = 0; i < trace_idx; i++) { + stream_printf(stream, "Event %" PRIu64 " : %" PRIx64 " %" PRIx64 + " %" PRIx64 " %" PRIx64 " %" PRIx64 " %" PRIx64 "\n", + trace_buf[i].event, trace_buf[i].x1, trace_buf[i].x2, + trace_buf[i].x3, trace_buf[i].x4, trace_buf[i].x5, + trace_buf[i].x6); + } +} + +void st_print_trace_events(FILE *stream, int (*stream_printf)(FILE *stream, const char *fmt, ...)) +{ + unsigned int i; + + for (i = 0; i < NR_TRACE_EVENTS; i++) { + stream_printf(stream, "%s [Event ID %u] : state %u\n", + trace_list[i].tp_name, i, trace_list[i].state); + } +} + +static TraceEvent* find_trace_event_by_name(const char *tname) +{ + unsigned int i; + + if (!tname) { + return NULL; + } + + for (i = 0; i < NR_TRACE_EVENTS; i++) { + if (!strcmp(trace_list[i].tp_name, tname)) { + return &trace_list[i]; + } + } + return NULL; /* indicates end of list reached without a match */ +} + +bool st_change_trace_event_state(const char *tname, bool tstate) +{ + TraceEvent *tp; + + tp = find_trace_event_by_name(tname); + if (tp) { + tp->state = tstate; + return true; + } + return false; +} diff -Nru qemu-kvm-0.12.5+noroms/simpletrace.h qemu-kvm-0.14.1/simpletrace.h --- qemu-kvm-0.12.5+noroms/simpletrace.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/simpletrace.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,40 @@ +/* + * Simple trace backend + * + * Copyright IBM, Corp. 2010 + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ + +#ifndef SIMPLETRACE_H +#define SIMPLETRACE_H + +#include +#include +#include + +typedef uint64_t TraceEventID; + +typedef struct { + const char *tp_name; + bool state; +} TraceEvent; + +void trace0(TraceEventID event); +void trace1(TraceEventID event, uint64_t x1); +void trace2(TraceEventID event, uint64_t x1, uint64_t x2); +void trace3(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3); +void trace4(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4); +void trace5(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, uint64_t x5); +void trace6(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, uint64_t x5, uint64_t x6); +void st_print_trace(FILE *stream, fprintf_function stream_printf); +void st_print_trace_events(FILE *stream, fprintf_function stream_printf); +bool st_change_trace_event_state(const char *tname, bool tstate); +void st_print_trace_file_status(FILE *stream, fprintf_function stream_printf); +void st_set_trace_file_enabled(bool enable); +bool st_set_trace_file(const char *file); +void st_flush_trace_buffer(void); + +#endif /* SIMPLETRACE_H */ diff -Nru qemu-kvm-0.12.5+noroms/slirp/bootp.c qemu-kvm-0.14.1/slirp/bootp.c --- qemu-kvm-0.12.5+noroms/slirp/bootp.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/slirp/bootp.c 2011-05-11 13:29:46.000000000 +0000 @@ -30,10 +30,10 @@ static const uint8_t rfc1533_cookie[] = { RFC1533_COOKIE }; #ifdef DEBUG -#define dprintf(fmt, ...) \ +#define DPRINTF(fmt, ...) \ do if (slirp_debug & DBG_CALL) { fprintf(dfd, fmt, ## __VA_ARGS__); fflush(dfd); } while (0) #else -#define dprintf(fmt, ...) +#define DPRINTF(fmt, ...) do{}while(0) #endif static BOOTPClient *get_new_addr(Slirp *slirp, struct in_addr *paddr, @@ -92,13 +92,13 @@ } static void dhcp_decode(const struct bootp_t *bp, int *pmsg_type, - const struct in_addr **preq_addr) + struct in_addr *preq_addr) { const uint8_t *p, *p_end; int len, tag; *pmsg_type = 0; - *preq_addr = NULL; + preq_addr->s_addr = htonl(0L); p = bp->bp_vend; p_end = p + DHCP_OPT_LEN; @@ -116,7 +116,7 @@ if (p >= p_end) break; len = *p++; - dprintf("dhcp: tag=%d len=%d\n", tag, len); + DPRINTF("dhcp: tag=%d len=%d\n", tag, len); switch(tag) { case RFC2132_MSG_TYPE: @@ -124,8 +124,9 @@ *pmsg_type = p[0]; break; case RFC2132_REQ_ADDR: - if (len >= 4) - *preq_addr = (struct in_addr *)p; + if (len >= 4) { + memcpy(&(preq_addr->s_addr), p, 4); + } break; default: break; @@ -133,8 +134,9 @@ p += len; } } - if (*pmsg_type == DHCPREQUEST && !*preq_addr && bp->bp_ciaddr.s_addr) { - *preq_addr = &bp->bp_ciaddr; + if (*pmsg_type == DHCPREQUEST && preq_addr->s_addr == htonl(0L) && + bp->bp_ciaddr.s_addr) { + memcpy(&(preq_addr->s_addr), &bp->bp_ciaddr, 4); } } @@ -144,17 +146,17 @@ struct mbuf *m; struct bootp_t *rbp; struct sockaddr_in saddr, daddr; - const struct in_addr *preq_addr; + struct in_addr preq_addr; int dhcp_msg_type, val; uint8_t *q; /* extract exact DHCP msg type */ dhcp_decode(bp, &dhcp_msg_type, &preq_addr); - dprintf("bootp packet op=%d msgtype=%d", bp->bp_op, dhcp_msg_type); - if (preq_addr) - dprintf(" req_addr=%08x\n", ntohl(preq_addr->s_addr)); + DPRINTF("bootp packet op=%d msgtype=%d", bp->bp_op, dhcp_msg_type); + if (preq_addr.s_addr != htonl(0L)) + DPRINTF(" req_addr=%08x\n", ntohl(preq_addr.s_addr)); else - dprintf("\n"); + DPRINTF("\n"); if (dhcp_msg_type == 0) dhcp_msg_type = DHCPREQUEST; /* Force reply for old BOOTP clients */ @@ -175,25 +177,25 @@ memset(rbp, 0, sizeof(struct bootp_t)); if (dhcp_msg_type == DHCPDISCOVER) { - if (preq_addr) { - bc = request_addr(slirp, preq_addr, slirp->client_ethaddr); + if (preq_addr.s_addr != htonl(0L)) { + bc = request_addr(slirp, &preq_addr, slirp->client_ethaddr); if (bc) { - daddr.sin_addr = *preq_addr; + daddr.sin_addr = preq_addr; } } if (!bc) { new_addr: bc = get_new_addr(slirp, &daddr.sin_addr, slirp->client_ethaddr); if (!bc) { - dprintf("no address left\n"); + DPRINTF("no address left\n"); return; } } memcpy(bc->macaddr, slirp->client_ethaddr, 6); - } else if (preq_addr) { - bc = request_addr(slirp, preq_addr, slirp->client_ethaddr); + } else if (preq_addr.s_addr != htonl(0L)) { + bc = request_addr(slirp, &preq_addr, slirp->client_ethaddr); if (bc) { - daddr.sin_addr = *preq_addr; + daddr.sin_addr = preq_addr; memcpy(bc->macaddr, slirp->client_ethaddr, 6); } else { daddr.sin_addr.s_addr = 0; @@ -226,7 +228,7 @@ q += 4; if (bc) { - dprintf("%s addr=%08x\n", + DPRINTF("%s addr=%08x\n", (dhcp_msg_type == DHCPDISCOVER) ? "offered" : "ack'ed", ntohl(daddr.sin_addr.s_addr)); @@ -282,7 +284,7 @@ } else { static const char nak_msg[] = "requested address not available"; - dprintf("nak'ed addr=%08x\n", ntohl(preq_addr->s_addr)); + DPRINTF("nak'ed addr=%08x\n", ntohl(preq_addr->s_addr)); *q++ = RFC2132_MSG_TYPE; *q++ = 1; @@ -293,7 +295,7 @@ memcpy(q, nak_msg, sizeof(nak_msg) - 1); q += sizeof(nak_msg) - 1; } - *q++ = RFC1533_END; + *q = RFC1533_END; daddr.sin_addr.s_addr = 0xffffffffu; diff -Nru qemu-kvm-0.12.5+noroms/slirp/cksum.c qemu-kvm-0.14.1/slirp/cksum.c --- qemu-kvm-0.12.5+noroms/slirp/cksum.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/slirp/cksum.c 2011-05-11 13:29:46.000000000 +0000 @@ -42,41 +42,44 @@ */ #define ADDCARRY(x) (x > 65535 ? x -= 65535 : x) -#define REDUCE {l_util.l = sum; sum = l_util.s[0] + l_util.s[1]; ADDCARRY(sum);} +#define REDUCE {l_util.l = sum; sum = l_util.s[0] + l_util.s[1]; \ + (void)ADDCARRY(sum);} int cksum(struct mbuf *m, int len) { - register u_int16_t *w; + register uint16_t *w; register int sum = 0; register int mlen = 0; int byte_swapped = 0; union { - u_int8_t c[2]; - u_int16_t s; + uint8_t c[2]; + uint16_t s; } s_util; union { - u_int16_t s[2]; - u_int32_t l; + uint16_t s[2]; + uint32_t l; } l_util; if (m->m_len == 0) goto cont; - w = mtod(m, u_int16_t *); + w = mtod(m, uint16_t *); mlen = m->m_len; if (len < mlen) mlen = len; +#ifdef DEBUG len -= mlen; +#endif /* * Force to even boundary. */ if ((1 & (long) w) && (mlen > 0)) { REDUCE; sum <<= 8; - s_util.c[0] = *(u_int8_t *)w; - w = (u_int16_t *)((int8_t *)w + 1); + s_util.c[0] = *(uint8_t *)w; + w = (uint16_t *)((int8_t *)w + 1); mlen--; byte_swapped = 1; } @@ -107,16 +110,15 @@ if (byte_swapped) { REDUCE; sum <<= 8; - byte_swapped = 0; if (mlen == -1) { - s_util.c[1] = *(u_int8_t *)w; + s_util.c[1] = *(uint8_t *)w; sum += s_util.s; mlen = 0; } else mlen = -1; } else if (mlen == -1) - s_util.c[0] = *(u_int8_t *)w; + s_util.c[0] = *(uint8_t *)w; cont: #ifdef DEBUG diff -Nru qemu-kvm-0.12.5+noroms/slirp/ip.h qemu-kvm-0.14.1/slirp/ip.h --- qemu-kvm-0.12.5+noroms/slirp/ip.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/slirp/ip.h 2011-05-11 13:29:46.000000000 +0000 @@ -51,17 +51,17 @@ # define NTOHL(d) ((d) = ntohl((d))) # endif # ifndef NTOHS -# define NTOHS(d) ((d) = ntohs((u_int16_t)(d))) +# define NTOHS(d) ((d) = ntohs((uint16_t)(d))) # endif # ifndef HTONL # define HTONL(d) ((d) = htonl((d))) # endif # ifndef HTONS -# define HTONS(d) ((d) = htons((u_int16_t)(d))) +# define HTONS(d) ((d) = htons((uint16_t)(d))) # endif #endif -typedef u_int32_t n_long; /* long as received from the net */ +typedef uint32_t n_long; /* long as received from the net */ /* * Definitions for internet protocol version 4. @@ -80,18 +80,18 @@ u_int ip_hl:4, /* header length */ ip_v:4; /* version */ #endif - u_int8_t ip_tos; /* type of service */ - u_int16_t ip_len; /* total length */ - u_int16_t ip_id; /* identification */ - u_int16_t ip_off; /* fragment offset field */ + uint8_t ip_tos; /* type of service */ + uint16_t ip_len; /* total length */ + uint16_t ip_id; /* identification */ + uint16_t ip_off; /* fragment offset field */ #define IP_DF 0x4000 /* don't fragment flag */ #define IP_MF 0x2000 /* more fragments flag */ #define IP_OFFMASK 0x1fff /* mask for fragmenting bits */ - u_int8_t ip_ttl; /* time to live */ - u_int8_t ip_p; /* protocol */ - u_int16_t ip_sum; /* checksum */ + uint8_t ip_ttl; /* time to live */ + uint8_t ip_p; /* protocol */ + uint16_t ip_sum; /* checksum */ struct in_addr ip_src,ip_dst; /* source and dest address */ -}; +} __attribute__((packed)); #define IP_MAXPACKET 65535 /* maximum packet size */ @@ -136,9 +136,9 @@ * Time stamp option structure. */ struct ip_timestamp { - u_int8_t ipt_code; /* IPOPT_TS */ - u_int8_t ipt_len; /* size of structure (variable) */ - u_int8_t ipt_ptr; /* index of current entry */ + uint8_t ipt_code; /* IPOPT_TS */ + uint8_t ipt_len; /* size of structure (variable) */ + uint8_t ipt_ptr; /* index of current entry */ #ifdef HOST_WORDS_BIGENDIAN u_int ipt_oflw:4, /* overflow counter */ ipt_flg:4; /* flags, see below */ @@ -153,7 +153,7 @@ n_long ipt_time; } ipt_ta[1]; } ipt_timestamp; -}; +} __attribute__((packed)); /* flag bits for ipt_flg */ #define IPOPT_TS_TSONLY 0 /* timestamps only */ @@ -183,11 +183,11 @@ struct mbuf_ptr { struct mbuf *mptr; uint32_t dummy; -}; +} __attribute__((packed)); #else struct mbuf_ptr { struct mbuf *mptr; -}; +} __attribute__((packed)); #endif struct qlink { void *next, *prev; @@ -198,9 +198,9 @@ */ struct ipovly { struct mbuf_ptr ih_mbuf; /* backpointer to mbuf */ - u_int8_t ih_x1; /* (unused) */ - u_int8_t ih_pr; /* protocol */ - u_int16_t ih_len; /* protocol length */ + uint8_t ih_x1; /* (unused) */ + uint8_t ih_pr; /* protocol */ + uint16_t ih_len; /* protocol length */ struct in_addr ih_src; /* source internet address */ struct in_addr ih_dst; /* destination internet address */ } __attribute__((packed)); @@ -215,11 +215,11 @@ struct ipq { struct qlink frag_link; /* to ip headers of fragments */ struct qlink ip_link; /* to other reass headers */ - u_int8_t ipq_ttl; /* time for reass q to live */ - u_int8_t ipq_p; /* protocol of this fragment */ - u_int16_t ipq_id; /* sequence id for reassembly */ + uint8_t ipq_ttl; /* time for reass q to live */ + uint8_t ipq_p; /* protocol of this fragment */ + uint16_t ipq_id; /* sequence id for reassembly */ struct in_addr ipq_src,ipq_dst; -}; +} __attribute__((packed)); /* * Ip header, when holding a fragment. @@ -229,13 +229,13 @@ struct ipasfrag { struct qlink ipf_link; struct ip ipf_ip; -}; +} __attribute__((packed)); #define ipf_off ipf_ip.ip_off #define ipf_tos ipf_ip.ip_tos #define ipf_len ipf_ip.ip_len #define ipf_next ipf_link.next -#define ipf_prev ipf_link.prev +#define ipf_prev ipf_link.prev /* * Structure stored in mbuf in inpcb.ip_options @@ -248,6 +248,6 @@ struct ipoption { struct in_addr ipopt_dst; /* first-hop dst if source routed */ int8_t ipopt_list[MAX_IPOPTLEN]; /* options proper */ -}; +} __attribute__((packed)); #endif diff -Nru qemu-kvm-0.12.5+noroms/slirp/ip_icmp.h qemu-kvm-0.14.1/slirp/ip_icmp.h --- qemu-kvm-0.12.5+noroms/slirp/ip_icmp.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/slirp/ip_icmp.h 2011-05-11 13:29:46.000000000 +0000 @@ -38,7 +38,7 @@ * Per RFC 792, September 1981. */ -typedef u_int32_t n_time; +typedef uint32_t n_time; /* * Structure of an icmp header. diff -Nru qemu-kvm-0.12.5+noroms/slirp/ip_input.c qemu-kvm-0.14.1/slirp/ip_input.c --- qemu-kvm-0.12.5+noroms/slirp/ip_input.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/slirp/ip_input.c 2011-05-11 13:29:46.000000000 +0000 @@ -144,7 +144,7 @@ m_adj(m, ip->ip_len - m->m_len); /* check ip_ttl for a correct ICMP reply */ - if(ip->ip_ttl==0 || ip->ip_ttl==1) { + if(ip->ip_ttl==0) { icmp_error(m, ICMP_TIMXCEED,ICMP_TIMXCEED_INTRANS, 0,"ttl"); goto bad; } @@ -477,7 +477,7 @@ register struct in_ifaddr *ia; int opt, optlen, cnt, off, code, type, forward = 0; struct in_addr *sin, dst; -typedef u_int32_t n_time; +typedef uint32_t n_time; n_time ntime; dst = ip->ip_dst; diff -Nru qemu-kvm-0.12.5+noroms/slirp/ip_output.c qemu-kvm-0.14.1/slirp/ip_output.c --- qemu-kvm-0.12.5+noroms/slirp/ip_output.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/slirp/ip_output.c 2011-05-11 13:29:46.000000000 +0000 @@ -75,9 +75,9 @@ /* * If small enough for interface, can just send directly. */ - if ((u_int16_t)ip->ip_len <= IF_MTU) { - ip->ip_len = htons((u_int16_t)ip->ip_len); - ip->ip_off = htons((u_int16_t)ip->ip_off); + if ((uint16_t)ip->ip_len <= IF_MTU) { + ip->ip_len = htons((uint16_t)ip->ip_len); + ip->ip_off = htons((uint16_t)ip->ip_off); ip->ip_sum = 0; ip->ip_sum = cksum(m, hlen); @@ -110,7 +110,7 @@ */ m0 = m; mhlen = sizeof (struct ip); - for (off = hlen + len; off < (u_int16_t)ip->ip_len; off += len) { + for (off = hlen + len; off < (uint16_t)ip->ip_len; off += len) { register struct ip *mhip; m = m_get(slirp); if (m == NULL) { @@ -125,18 +125,18 @@ mhip->ip_off = ((off - hlen) >> 3) + (ip->ip_off & ~IP_MF); if (ip->ip_off & IP_MF) mhip->ip_off |= IP_MF; - if (off + len >= (u_int16_t)ip->ip_len) - len = (u_int16_t)ip->ip_len - off; + if (off + len >= (uint16_t)ip->ip_len) + len = (uint16_t)ip->ip_len - off; else mhip->ip_off |= IP_MF; - mhip->ip_len = htons((u_int16_t)(len + mhlen)); + mhip->ip_len = htons((uint16_t)(len + mhlen)); if (m_copy(m, m0, off, len) < 0) { error = -1; goto sendorfree; } - mhip->ip_off = htons((u_int16_t)mhip->ip_off); + mhip->ip_off = htons((uint16_t)mhip->ip_off); mhip->ip_sum = 0; mhip->ip_sum = cksum(m, mhlen); *mnext = m; @@ -147,9 +147,9 @@ * and updating header, then send each fragment (in order). */ m = m0; - m_adj(m, hlen + firstlen - (u_int16_t)ip->ip_len); - ip->ip_len = htons((u_int16_t)m->m_len); - ip->ip_off = htons((u_int16_t)(ip->ip_off | IP_MF)); + m_adj(m, hlen + firstlen - (uint16_t)ip->ip_len); + ip->ip_len = htons((uint16_t)m->m_len); + ip->ip_off = htons((uint16_t)(ip->ip_off | IP_MF)); ip->ip_sum = 0; ip->ip_sum = cksum(m, hlen); sendorfree: diff -Nru qemu-kvm-0.12.5+noroms/slirp/main.h qemu-kvm-0.14.1/slirp/main.h --- qemu-kvm-0.12.5+noroms/slirp/main.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/slirp/main.h 2011-05-11 13:29:46.000000000 +0000 @@ -14,7 +14,7 @@ extern int slirp_socket; extern int slirp_socket_unit; extern int slirp_socket_port; -extern u_int32_t slirp_socket_addr; +extern uint32_t slirp_socket_addr; extern char *slirp_socket_passwd; extern int ctty_closed; diff -Nru qemu-kvm-0.12.5+noroms/slirp/mbuf.c qemu-kvm-0.14.1/slirp/mbuf.c --- qemu-kvm-0.12.5+noroms/slirp/mbuf.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/slirp/mbuf.c 2011-05-11 13:29:46.000000000 +0000 @@ -23,7 +23,7 @@ * Find a nice value for msize * XXX if_maxlinkhdr already in mtu */ -#define SLIRP_MSIZE (IF_MTU + IF_MAXLINKHDR + sizeof(struct m_hdr ) + 6) +#define SLIRP_MSIZE (IF_MTU + IF_MAXLINKHDR + offsetof(struct mbuf, m_dat) + 6) void m_init(Slirp *slirp) @@ -65,7 +65,7 @@ m->m_flags = (flags | M_USEDLIST); /* Initialise it */ - m->m_size = SLIRP_MSIZE - sizeof(struct m_hdr); + m->m_size = SLIRP_MSIZE - offsetof(struct mbuf, m_dat); m->m_data = m->m_dat; m->m_len = 0; m->m_nextpkt = NULL; diff -Nru qemu-kvm-0.12.5+noroms/slirp/misc.c qemu-kvm-0.14.1/slirp/misc.c --- qemu-kvm-0.12.5+noroms/slirp/misc.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/slirp/misc.c 2011-05-11 13:29:46.000000000 +0000 @@ -200,14 +200,8 @@ execvp(argv[0], (char **)argv); /* Ooops, failed, let's tell the user why */ - { - char buff[256]; - - snprintf(buff, sizeof(buff), - "Error: execvp of %s failed: %s\n", - argv[0], strerror(errno)); - write(2, buff, strlen(buff)+1); - } + fprintf(stderr, "Error: execvp of %s failed: %s\n", + argv[0], strerror(errno)); close(0); close(1); close(2); /* XXX */ exit(1); @@ -266,52 +260,10 @@ va_list args; va_start(args, format); - monitor_vprintf(cur_mon, format, args); + monitor_vprintf(default_mon, format, args); va_end(args); } -#ifdef BAD_SPRINTF - -#undef vsprintf -#undef sprintf - -/* - * Some BSD-derived systems have a sprintf which returns char * - */ - -int -vsprintf_len(string, format, args) - char *string; - const char *format; - va_list args; -{ - vsprintf(string, format, args); - return strlen(string); -} - -int -#ifdef __STDC__ -sprintf_len(char *string, const char *format, ...) -#else -sprintf_len(va_alist) va_dcl -#endif -{ - va_list args; -#ifdef __STDC__ - va_start(args, format); -#else - char *string; - char *format; - va_start(args); - string = va_arg(args, char *); - format = va_arg(args, char *); -#endif - vsprintf(string, format, args); - return strlen(string); -} - -#endif - void u_sleep(int usec) { diff -Nru qemu-kvm-0.12.5+noroms/slirp/misc.h qemu-kvm-0.14.1/slirp/misc.h --- qemu-kvm-0.12.5+noroms/slirp/misc.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/slirp/misc.h 2011-05-11 13:29:46.000000000 +0000 @@ -37,24 +37,24 @@ #define EMU_NOCONNECT 0x10 /* Don't connect */ struct tos_t { - u_int16_t lport; - u_int16_t fport; - u_int8_t tos; - u_int8_t emu; + uint16_t lport; + uint16_t fport; + uint8_t tos; + uint8_t emu; }; struct emu_t { - u_int16_t lport; - u_int16_t fport; - u_int8_t tos; - u_int8_t emu; - struct emu_t *next; + uint16_t lport; + uint16_t fport; + uint8_t tos; + uint8_t emu; + struct emu_t *next; }; extern int x_port, x_server, x_display; int show_x(char *, struct socket *); -void redir_x(u_int32_t, int, int, int); +void redir_x(uint32_t, int, int, int); void slirp_insque(void *, void *); void slirp_remque(void *); int add_exec(struct ex_list **, int, char *, struct in_addr, int); diff -Nru qemu-kvm-0.12.5+noroms/slirp/slirp.c qemu-kvm-0.14.1/slirp/slirp.c --- qemu-kvm-0.12.5+noroms/slirp/slirp.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/slirp/slirp.c 2011-05-11 13:29:46.000000000 +0000 @@ -232,7 +232,8 @@ slirp->opaque = opaque; - register_savevm("slirp", 0, 3, slirp_state_save, slirp_state_load, slirp); + register_savevm(NULL, "slirp", 0, 3, + slirp_state_save, slirp_state_load, slirp); QTAILQ_INSERT_TAIL(&slirp_instances, slirp, entry); @@ -243,7 +244,7 @@ { QTAILQ_REMOVE(&slirp_instances, slirp, entry); - unregister_savevm("slirp", slirp); + unregister_savevm(NULL, "slirp", slirp); qemu_free(slirp->tftp_prefix); qemu_free(slirp->bootp_filename); @@ -598,7 +599,7 @@ { struct ethhdr *eh = (struct ethhdr *)pkt; struct arphdr *ah = (struct arphdr *)(pkt + ETH_HLEN); - uint8_t arp_reply[ETH_HLEN + sizeof(struct arphdr)]; + uint8_t arp_reply[max(ETH_HLEN + sizeof(struct arphdr), 64)]; struct ethhdr *reh = (struct ethhdr *)arp_reply; struct arphdr *rah = (struct arphdr *)(arp_reply + ETH_HLEN); int ar_op; @@ -618,6 +619,7 @@ } return; arp_ok: + memset(arp_reply, 0, sizeof(arp_reply)); /* XXX: make an ARP request to have the client address */ memcpy(slirp->client_ethaddr, eh->h_source, ETH_ALEN); @@ -1069,9 +1071,8 @@ { Slirp *slirp = opaque; struct ex_list *ex_ptr; - int r; - while ((r = qemu_get_byte(f))) { + while (qemu_get_byte(f)) { int ret; struct socket *so = socreate(slirp); diff -Nru qemu-kvm-0.12.5+noroms/slirp/slirp_config.h qemu-kvm-0.14.1/slirp/slirp_config.h --- qemu-kvm-0.12.5+noroms/slirp/slirp_config.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/slirp/slirp_config.h 2011-05-11 13:29:46.000000000 +0000 @@ -85,9 +85,6 @@ /* Define if the machine is big endian */ //#undef HOST_WORDS_BIGENDIAN -/* Define if your sprintf returns char * instead of int */ -#undef BAD_SPRINTF - /* Define if you have readv */ #undef HAVE_READV @@ -97,9 +94,6 @@ #define DECLARE_IOVEC #endif -/* Define if a declaration of sprintf/fprintf is needed */ -#undef DECLARE_SPRINTF - /* Define if you have a POSIX.1 sys/wait.h */ #undef HAVE_SYS_WAIT_H @@ -133,12 +127,6 @@ /* Define if your compiler doesn't like prototypes */ #undef NO_PROTOTYPES -/* Define if you don't have u_int32_t etc. typedef'd */ -#undef NEED_TYPEDEFS -#ifdef __sun__ -#define NEED_TYPEDEFS -#endif - /* Define to sizeof(char) */ #define SIZEOF_CHAR 1 diff -Nru qemu-kvm-0.12.5+noroms/slirp/slirp.h qemu-kvm-0.14.1/slirp/slirp.h --- qemu-kvm-0.12.5+noroms/slirp/slirp.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/slirp/slirp.h 2011-05-11 13:29:46.000000000 +0000 @@ -7,10 +7,6 @@ #ifdef _WIN32 # include -typedef uint8_t u_int8_t; -typedef uint16_t u_int16_t; -typedef uint32_t u_int32_t; -typedef uint64_t u_int64_t; typedef char *caddr_t; # include @@ -28,7 +24,9 @@ #else # define ioctlsocket ioctl # define closesocket(s) close(s) -# define O_BINARY 0 +# if !defined(__HAIKU__) +# define O_BINARY 0 +# endif #endif #include @@ -38,35 +36,6 @@ #include -#ifdef NEED_TYPEDEFS -typedef char int8_t; -typedef unsigned char u_int8_t; - -# if SIZEOF_SHORT == 2 - typedef short int16_t; - typedef unsigned short u_int16_t; -# else -# if SIZEOF_INT == 2 - typedef int int16_t; - typedef unsigned int u_int16_t; -# else - #error Cannot find a type with sizeof() == 2 -# endif -# endif - -# if SIZEOF_SHORT == 4 - typedef short int32_t; - typedef unsigned short u_int32_t; -# else -# if SIZEOF_INT == 4 - typedef int int32_t; - typedef unsigned int u_int32_t; -# else - #error Cannot find a type with sizeof() == 4 -# endif -# endif -#endif /* NEED_TYPEDEFS */ - #ifdef HAVE_UNISTD_H # include #endif @@ -233,7 +202,7 @@ /* ip states */ struct ipq ipq; /* ip reass. queue */ - u_int16_t ip_id; /* ip packet ctr, for ids */ + uint16_t ip_id; /* ip packet ctr, for ids */ /* bootp/dhcp states */ BOOTPClient bootp_clients[NB_BOOTP_CLIENTS]; @@ -243,7 +212,7 @@ struct socket tcb; struct socket *tcp_last_so; tcp_seq tcp_iss; /* tcp initial send seq # */ - u_int32_t tcp_now; /* for RFC 1323 timestamps */ + uint32_t tcp_now; /* for RFC 1323 timestamps */ /* udp states */ struct socket udb; @@ -268,22 +237,8 @@ void if_start(struct ttys *); #endif -#ifdef BAD_SPRINTF -# define vsprintf vsprintf_len -# define sprintf sprintf_len - extern int vsprintf_len(char *, const char *, va_list); - extern int sprintf_len(char *, const char *, ...); -#endif - -#ifdef DECLARE_SPRINTF -# ifndef BAD_SPRINTF - extern int vsprintf(char *, const char *, va_list); -# endif - extern int vfprintf(FILE *, const char *, va_list); -#endif - #ifndef HAVE_STRERROR - extern char *strerror(int error); + char *strerror(int error); #endif #ifndef HAVE_INDEX @@ -294,7 +249,7 @@ long gethostid(void); #endif -void lprint(const char *, ...); +void lprint(const char *, ...) GCC_FMT_ATTR(1, 2); #ifndef _WIN32 #include @@ -339,7 +294,7 @@ int tcp_fconnect(struct socket *); void tcp_connect(struct socket *); int tcp_attach(struct socket *); -u_int8_t tcp_tos(struct socket *); +uint8_t tcp_tos(struct socket *); int tcp_emu(struct socket *, struct mbuf *); int tcp_ctl(struct socket *); struct tcpcb *tcp_drop(struct tcpcb *tp, int err); diff -Nru qemu-kvm-0.12.5+noroms/slirp/socket.c qemu-kvm-0.14.1/slirp/socket.c --- qemu-kvm-0.12.5+noroms/slirp/socket.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/slirp/socket.c 2011-05-11 13:29:46.000000000 +0000 @@ -90,8 +90,6 @@ DEBUG_CALL("sopreprbuf"); DEBUG_ARG("so = %lx", (long )so); - len = sb->sb_datalen - sb->sb_cc; - if (len <= 0) return 0; @@ -363,8 +361,6 @@ * sowrite wouldn't have been called otherwise */ - len = sb->sb_cc; - iov[0].iov_base = sb->sb_rptr; iov[1].iov_base = NULL; iov[1].iov_len = 0; @@ -584,13 +580,14 @@ * Listen for incoming TCP connections */ struct socket * -tcp_listen(Slirp *slirp, u_int32_t haddr, u_int hport, u_int32_t laddr, +tcp_listen(Slirp *slirp, uint32_t haddr, u_int hport, uint32_t laddr, u_int lport, int flags) { struct sockaddr_in addr; struct socket *so; int s, opt = 1; socklen_t addrlen = sizeof(addr); + memset(&addr, 0, addrlen); DEBUG_CALL("tcp_listen"); DEBUG_ARG("haddr = %x", haddr); diff -Nru qemu-kvm-0.12.5+noroms/slirp/socket.h qemu-kvm-0.14.1/slirp/socket.h --- qemu-kvm-0.12.5+noroms/slirp/socket.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/slirp/socket.h 2011-05-11 13:29:46.000000000 +0000 @@ -31,11 +31,11 @@ int so_urgc; struct in_addr so_faddr; /* foreign host table entry */ struct in_addr so_laddr; /* local host table entry */ - u_int16_t so_fport; /* foreign port */ - u_int16_t so_lport; /* local port */ + uint16_t so_fport; /* foreign port */ + uint16_t so_lport; /* local port */ - u_int8_t so_iptos; /* Type of service */ - u_int8_t so_emu; /* Is the socket emulated? */ + uint8_t so_iptos; /* Type of service */ + uint8_t so_emu; /* Is the socket emulated? */ u_char so_type; /* Type of socket, UDP or TCP */ int so_state; /* internal state flags SS_*, below */ @@ -83,7 +83,7 @@ int sowrite(struct socket *); void sorecvfrom(struct socket *); int sosendto(struct socket *, struct mbuf *); -struct socket * tcp_listen(Slirp *, u_int32_t, u_int, u_int32_t, u_int, +struct socket * tcp_listen(Slirp *, uint32_t, u_int, uint32_t, u_int, int); void soisfconnecting(register struct socket *); void soisfconnected(register struct socket *); diff -Nru qemu-kvm-0.12.5+noroms/slirp/tcp.h qemu-kvm-0.14.1/slirp/tcp.h --- qemu-kvm-0.12.5+noroms/slirp/tcp.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/slirp/tcp.h 2011-05-11 13:29:46.000000000 +0000 @@ -33,7 +33,7 @@ #ifndef _TCP_H_ #define _TCP_H_ -typedef u_int32_t tcp_seq; +typedef uint32_t tcp_seq; #define PR_SLOWHZ 2 /* 2 slow timeouts per second (approx) */ #define PR_FASTHZ 5 /* 5 fast timeouts per second (not important) */ @@ -46,8 +46,8 @@ * Per RFC 793, September, 1981. */ struct tcphdr { - u_int16_t th_sport; /* source port */ - u_int16_t th_dport; /* destination port */ + uint16_t th_sport; /* source port */ + uint16_t th_dport; /* destination port */ tcp_seq th_seq; /* sequence number */ tcp_seq th_ack; /* acknowledgement number */ #ifdef HOST_WORDS_BIGENDIAN @@ -57,16 +57,16 @@ u_int th_x2:4, /* (unused) */ th_off:4; /* data offset */ #endif - u_int8_t th_flags; + uint8_t th_flags; #define TH_FIN 0x01 #define TH_SYN 0x02 #define TH_RST 0x04 #define TH_PUSH 0x08 #define TH_ACK 0x10 #define TH_URG 0x20 - u_int16_t th_win; /* window */ - u_int16_t th_sum; /* checksum */ - u_int16_t th_urp; /* urgent pointer */ + uint16_t th_win; /* window */ + uint16_t th_sum; /* checksum */ + uint16_t th_urp; /* urgent pointer */ }; #include "tcp_var.h" diff -Nru qemu-kvm-0.12.5+noroms/slirp/tcp_input.c qemu-kvm-0.14.1/slirp/tcp_input.c --- qemu-kvm-0.12.5+noroms/slirp/tcp_input.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/slirp/tcp_input.c 2011-05-11 13:29:46.000000000 +0000 @@ -280,7 +280,7 @@ tcpiphdr2qlink(ti)->next = tcpiphdr2qlink(ti)->prev = NULL; memset(&ti->ti_i.ih_mbuf, 0 , sizeof(struct mbuf_ptr)); ti->ti_x1 = 0; - ti->ti_len = htons((u_int16_t)tlen); + ti->ti_len = htons((uint16_t)tlen); len = sizeof(struct ip ) + tlen; if(cksum(m, len)) { goto drop; @@ -597,7 +597,7 @@ *ip=save_ip; icmp_error(m, ICMP_UNREACH,code, 0,strerror(errno)); } - tp = tcp_close(tp); + tcp_close(tp); m_free(m); } else { /* @@ -660,8 +660,9 @@ goto dropwithreset; if (tiflags & TH_RST) { - if (tiflags & TH_ACK) - tp = tcp_drop(tp,0); /* XXX Check t_softerror! */ + if (tiflags & TH_ACK) { + tcp_drop(tp, 0); /* XXX Check t_softerror! */ + } goto drop; } @@ -821,13 +822,13 @@ case TCPS_FIN_WAIT_2: case TCPS_CLOSE_WAIT: tp->t_state = TCPS_CLOSED; - tp = tcp_close(tp); + tcp_close(tp); goto drop; case TCPS_CLOSING: case TCPS_LAST_ACK: case TCPS_TIME_WAIT: - tp = tcp_close(tp); + tcp_close(tp); goto drop; } @@ -1074,7 +1075,7 @@ */ case TCPS_LAST_ACK: if (ourfinisacked) { - tp = tcp_close(tp); + tcp_close(tp); goto drop; } break; @@ -1165,12 +1166,6 @@ if ((ti->ti_len || (tiflags&TH_FIN)) && TCPS_HAVERCVDFIN(tp->t_state) == 0) { TCP_REASS(tp, ti, m, so, tiflags); - /* - * Note the amount of data that peer has sent into - * our window, in order to estimate the sender's - * buffer size. - */ - len = so->so_rcv.sb_datalen - (tp->rcv_adv - tp->rcv_nxt); } else { m_free(m); tiflags &= ~TH_FIN; @@ -1294,7 +1289,7 @@ static void tcp_dooptions(struct tcpcb *tp, u_char *cp, int cnt, struct tcpiphdr *ti) { - u_int16_t mss; + uint16_t mss; int opt, optlen; DEBUG_CALL("tcp_dooptions"); diff -Nru qemu-kvm-0.12.5+noroms/slirp/tcp_output.c qemu-kvm-0.14.1/slirp/tcp_output.c --- qemu-kvm-0.12.5+noroms/slirp/tcp_output.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/slirp/tcp_output.c 2011-05-11 13:29:46.000000000 +0000 @@ -263,11 +263,11 @@ if (flags & TH_SYN) { tp->snd_nxt = tp->iss; if ((tp->t_flags & TF_NOOPT) == 0) { - u_int16_t mss; + uint16_t mss; opt[0] = TCPOPT_MAXSEG; opt[1] = 4; - mss = htons((u_int16_t) tcp_mss(tp, 0)); + mss = htons((uint16_t) tcp_mss(tp, 0)); memcpy((caddr_t)(opt + 2), (caddr_t)&mss, sizeof(mss)); optlen = 4; } @@ -364,10 +364,10 @@ win = (long)TCP_MAXWIN << tp->rcv_scale; if (win < (long)(tp->rcv_adv - tp->rcv_nxt)) win = (long)(tp->rcv_adv - tp->rcv_nxt); - ti->ti_win = htons((u_int16_t) (win>>tp->rcv_scale)); + ti->ti_win = htons((uint16_t) (win>>tp->rcv_scale)); if (SEQ_GT(tp->snd_up, tp->snd_una)) { - ti->ti_urp = htons((u_int16_t)(tp->snd_up - ntohl(ti->ti_seq))); + ti->ti_urp = htons((uint16_t)(tp->snd_up - ntohl(ti->ti_seq))); ti->ti_flags |= TH_URG; } else /* @@ -383,7 +383,7 @@ * checksum extended header and data. */ if (len + optlen) - ti->ti_len = htons((u_int16_t)(sizeof (struct tcphdr) + + ti->ti_len = htons((uint16_t)(sizeof (struct tcphdr) + optlen + len)); ti->ti_sum = cksum(m, (int)(hdrlen + len)); diff -Nru qemu-kvm-0.12.5+noroms/slirp/tcp_subr.c qemu-kvm-0.14.1/slirp/tcp_subr.c --- qemu-kvm-0.12.5+noroms/slirp/tcp_subr.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/slirp/tcp_subr.c 2011-05-11 13:29:46.000000000 +0000 @@ -134,8 +134,8 @@ m->m_len = sizeof (struct tcpiphdr); tlen = 0; #define xchg(a,b,type) { type t; t=a; a=b; b=t; } - xchg(ti->ti_dst.s_addr, ti->ti_src.s_addr, u_int32_t); - xchg(ti->ti_dport, ti->ti_sport, u_int16_t); + xchg(ti->ti_dst.s_addr, ti->ti_src.s_addr, uint32_t); + xchg(ti->ti_dport, ti->ti_sport, uint16_t); #undef xchg } ti->ti_len = htons((u_short)(sizeof (struct tcphdr) + tlen)); @@ -150,9 +150,9 @@ ti->ti_off = sizeof (struct tcphdr) >> 2; ti->ti_flags = flags; if (tp) - ti->ti_win = htons((u_int16_t) (win >> tp->rcv_scale)); + ti->ti_win = htons((uint16_t) (win >> tp->rcv_scale)); else - ti->ti_win = htons((u_int16_t)win); + ti->ti_win = htons((uint16_t)win); ti->ti_urp = 0; ti->ti_sum = 0; ti->ti_sum = cksum(m, tlen); @@ -491,7 +491,7 @@ /* * Return TOS according to the above table */ -u_int8_t +uint8_t tcp_tos(struct socket *so) { int i = 0; @@ -548,7 +548,7 @@ Slirp *slirp = so->slirp; u_int n1, n2, n3, n4, n5, n6; char buff[257]; - u_int32_t laddr; + uint32_t laddr; u_int lport; char *bptr; @@ -857,7 +857,7 @@ if (p == 7071) p = 0; *(u_char *)bptr++ = (p >> 8) & 0xff; - *(u_char *)bptr++ = p & 0xff; + *(u_char *)bptr = p & 0xff; ra = 0; return 1; /* port redirected, we're done */ break; diff -Nru qemu-kvm-0.12.5+noroms/slirp/tcp_var.h qemu-kvm-0.14.1/slirp/tcp_var.h --- qemu-kvm-0.12.5+noroms/slirp/tcp_var.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/slirp/tcp_var.h 2011-05-11 13:29:46.000000000 +0000 @@ -75,9 +75,9 @@ tcp_seq snd_wl1; /* window update seg seq number */ tcp_seq snd_wl2; /* window update seg ack number */ tcp_seq iss; /* initial send sequence number */ - u_int32_t snd_wnd; /* send window */ + uint32_t snd_wnd; /* send window */ /* receive sequence variables */ - u_int32_t rcv_wnd; /* receive window */ + uint32_t rcv_wnd; /* receive window */ tcp_seq rcv_nxt; /* receive next */ tcp_seq rcv_up; /* receive urgent pointer */ tcp_seq irs; /* initial receive sequence number */ @@ -91,8 +91,8 @@ * used to recognize retransmits */ /* congestion control (for slow start, source quench, retransmit after loss) */ - u_int32_t snd_cwnd; /* congestion-controlled window */ - u_int32_t snd_ssthresh; /* snd_cwnd size threshold for + uint32_t snd_cwnd; /* congestion-controlled window */ + uint32_t snd_ssthresh; /* snd_cwnd size threshold for * for slow start exponential to * linear switch */ @@ -106,7 +106,7 @@ short t_srtt; /* smoothed round-trip time */ short t_rttvar; /* variance in round-trip time */ u_short t_rttmin; /* minimum rtt allowed */ - u_int32_t max_sndwnd; /* largest window peer has offered */ + uint32_t max_sndwnd; /* largest window peer has offered */ /* out-of-band data */ char t_oobflags; /* have some */ @@ -120,8 +120,8 @@ u_char rcv_scale; /* window scaling for recv window */ u_char request_r_scale; /* pending window scaling */ u_char requested_s_scale; - u_int32_t ts_recent; /* timestamp echo data */ - u_int32_t ts_recent_age; /* when last updated */ + uint32_t ts_recent; /* timestamp echo data */ + uint32_t ts_recent_age; /* when last updated */ tcp_seq last_ack_sent; }; diff -Nru qemu-kvm-0.12.5+noroms/slirp/tftp.c qemu-kvm-0.14.1/slirp/tftp.c --- qemu-kvm-0.12.5+noroms/slirp/tftp.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/slirp/tftp.c 2011-05-11 13:29:46.000000000 +0000 @@ -92,8 +92,8 @@ return -1; } -static int tftp_read_data(struct tftp_session *spt, u_int16_t block_nr, - u_int8_t *buf, int len) +static int tftp_read_data(struct tftp_session *spt, uint16_t block_nr, + uint8_t *buf, int len) { int fd; int bytes_read = 0; @@ -155,13 +155,12 @@ } static void tftp_send_error(struct tftp_session *spt, - u_int16_t errorcode, const char *msg, + uint16_t errorcode, const char *msg, struct tftp_t *recv_tp) { struct sockaddr_in saddr, daddr; struct mbuf *m; struct tftp_t *tp; - int nobytes; m = m_get(spt->slirp); @@ -185,8 +184,6 @@ daddr.sin_addr = spt->client_ip; daddr.sin_port = spt->client_port; - nobytes = 2; - m->m_len = sizeof(struct tftp_t) - 514 + 3 + strlen(msg) - sizeof(struct ip) - sizeof(struct udphdr); @@ -197,7 +194,7 @@ } static int tftp_send_data(struct tftp_session *spt, - u_int16_t block_nr, + uint16_t block_nr, struct tftp_t *recv_tp) { struct sockaddr_in saddr, daddr; @@ -314,7 +311,7 @@ return; } - if (memcmp(&tp->x.tp_buf[k], "octet\0", 6) != 0) { + if (strcasecmp((const char *)&tp->x.tp_buf[k], "octet") != 0) { tftp_send_error(spt, 4, "Unsupported transfer mode", tp); return; } @@ -354,7 +351,7 @@ value = (const char *)&tp->x.tp_buf[k]; k += strlen(value) + 1; - if (strcmp(key, "tsize") == 0) { + if (strcasecmp(key, "tsize") == 0) { int tsize = atoi(value); struct stat stat_p; diff -Nru qemu-kvm-0.12.5+noroms/slirp/tftp.h qemu-kvm-0.14.1/slirp/tftp.h --- qemu-kvm-0.12.5+noroms/slirp/tftp.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/slirp/tftp.h 2011-05-11 13:29:46.000000000 +0000 @@ -16,17 +16,17 @@ struct tftp_t { struct ip ip; struct udphdr udp; - u_int16_t tp_op; + uint16_t tp_op; union { struct { - u_int16_t tp_block_nr; - u_int8_t tp_buf[512]; + uint16_t tp_block_nr; + uint8_t tp_buf[512]; } tp_data; struct { - u_int16_t tp_error_code; - u_int8_t tp_msg[512]; + uint16_t tp_error_code; + uint8_t tp_msg[512]; } tp_error; - u_int8_t tp_buf[512 + 2]; + uint8_t tp_buf[512 + 2]; } x; }; @@ -35,7 +35,7 @@ char *filename; struct in_addr client_ip; - u_int16_t client_port; + uint16_t client_port; int timestamp; }; diff -Nru qemu-kvm-0.12.5+noroms/slirp/udp.c qemu-kvm-0.14.1/slirp/udp.c --- qemu-kvm-0.12.5+noroms/slirp/udp.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/slirp/udp.c 2011-05-11 13:29:46.000000000 +0000 @@ -41,7 +41,7 @@ #include #include "ip_icmp.h" -static u_int8_t udp_tos(struct socket *so); +static uint8_t udp_tos(struct socket *so); void udp_init(Slirp *slirp) @@ -88,7 +88,7 @@ * Make mbuf data length reflect UDP length. * If not enough data to reflect UDP length, drop. */ - len = ntohs((u_int16_t)uh->uh_ulen); + len = ntohs((uint16_t)uh->uh_ulen); if (ip->ip_len != len) { if (len > ip->ip_len) { @@ -321,7 +321,7 @@ {0, 0, 0, 0} }; -static u_int8_t +static uint8_t udp_tos(struct socket *so) { int i = 0; @@ -339,7 +339,7 @@ } struct socket * -udp_listen(Slirp *slirp, u_int32_t haddr, u_int hport, u_int32_t laddr, +udp_listen(Slirp *slirp, uint32_t haddr, u_int hport, uint32_t laddr, u_int lport, int flags) { struct sockaddr_in addr; diff -Nru qemu-kvm-0.12.5+noroms/slirp/udp.h qemu-kvm-0.14.1/slirp/udp.h --- qemu-kvm-0.12.5+noroms/slirp/udp.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/slirp/udp.h 2011-05-11 13:29:46.000000000 +0000 @@ -41,10 +41,10 @@ * Per RFC 768, September, 1981. */ struct udphdr { - u_int16_t uh_sport; /* source port */ - u_int16_t uh_dport; /* destination port */ - int16_t uh_ulen; /* udp length */ - u_int16_t uh_sum; /* udp checksum */ + uint16_t uh_sport; /* source port */ + uint16_t uh_dport; /* destination port */ + int16_t uh_ulen; /* udp length */ + uint16_t uh_sum; /* udp checksum */ }; /* @@ -78,7 +78,7 @@ int udp_output(struct socket *, struct mbuf *, struct sockaddr_in *); int udp_attach(struct socket *); void udp_detach(struct socket *); -struct socket * udp_listen(Slirp *, u_int32_t, u_int, u_int32_t, u_int, +struct socket * udp_listen(Slirp *, uint32_t, u_int, uint32_t, u_int, int); int udp_output2(struct socket *so, struct mbuf *m, struct sockaddr_in *saddr, struct sockaddr_in *daddr, diff -Nru qemu-kvm-0.12.5+noroms/softmmu_exec.h qemu-kvm-0.14.1/softmmu_exec.h --- qemu-kvm-0.12.5+noroms/softmmu_exec.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/softmmu_exec.h 2011-05-11 13:29:46.000000000 +0000 @@ -100,9 +100,28 @@ #undef MEMSUFFIX #endif /* (NB_MMU_MODES >= 5) */ -#if (NB_MMU_MODES > 5) -#error "NB_MMU_MODES > 5 is not supported for now" -#endif /* (NB_MMU_MODES > 5) */ +#if (NB_MMU_MODES >= 6) + +#define ACCESS_TYPE 5 +#define MEMSUFFIX MMU_MODE5_SUFFIX +#define DATA_SIZE 1 +#include "softmmu_header.h" + +#define DATA_SIZE 2 +#include "softmmu_header.h" + +#define DATA_SIZE 4 +#include "softmmu_header.h" + +#define DATA_SIZE 8 +#include "softmmu_header.h" +#undef ACCESS_TYPE +#undef MEMSUFFIX +#endif /* (NB_MMU_MODES >= 6) */ + +#if (NB_MMU_MODES > 6) +#error "NB_MMU_MODES > 6 is not supported for now" +#endif /* (NB_MMU_MODES > 6) */ /* these access are slower, they must be as rare as possible */ #define ACCESS_TYPE (NB_MMU_MODES) diff -Nru qemu-kvm-0.12.5+noroms/softmmu_header.h qemu-kvm-0.14.1/softmmu_header.h --- qemu-kvm-0.12.5+noroms/softmmu_header.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/softmmu_header.h 2011-05-11 13:29:46.000000000 +0000 @@ -60,7 +60,7 @@ #if DATA_SIZE == 8 #define RES_TYPE uint64_t #else -#define RES_TYPE int +#define RES_TYPE uint32_t #endif #if ACCESS_TYPE == (NB_MMU_MODES + 1) diff -Nru qemu-kvm-0.12.5+noroms/softmmu_template.h qemu-kvm-0.14.1/softmmu_template.h --- qemu-kvm-0.12.5+noroms/softmmu_template.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/softmmu_template.h 2011-05-11 13:29:46.000000000 +0000 @@ -16,6 +16,8 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ +#include "qemu-timer.h" + #define DATA_SIZE (1 << SHIFT) #if DATA_SIZE == 8 @@ -85,7 +87,8 @@ DATA_TYPE res; int index; target_ulong tlb_addr; - target_phys_addr_t addend; + target_phys_addr_t ioaddr; + unsigned long addend; void *retaddr; /* test if there is match for unaligned or IO access */ @@ -99,8 +102,8 @@ if ((addr & (DATA_SIZE - 1)) != 0) goto do_unaligned_access; retaddr = GETPC(); - addend = env->iotlb[mmu_idx][index]; - res = glue(io_read, SUFFIX)(addend, addr, retaddr); + ioaddr = env->iotlb[mmu_idx][index]; + res = glue(io_read, SUFFIX)(ioaddr, addr, retaddr); } else if (((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1) >= TARGET_PAGE_SIZE) { /* slow unaligned access (it spans two pages or IO) */ do_unaligned_access: @@ -141,7 +144,8 @@ { DATA_TYPE res, res1, res2; int index, shift; - target_phys_addr_t addend; + target_phys_addr_t ioaddr; + unsigned long addend; target_ulong tlb_addr, addr1, addr2; index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); @@ -152,9 +156,8 @@ /* IO access */ if ((addr & (DATA_SIZE - 1)) != 0) goto do_unaligned_access; - retaddr = GETPC(); - addend = env->iotlb[mmu_idx][index]; - res = glue(io_read, SUFFIX)(addend, addr, retaddr); + ioaddr = env->iotlb[mmu_idx][index]; + res = glue(io_read, SUFFIX)(ioaddr, addr, retaddr); } else if (((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1) >= TARGET_PAGE_SIZE) { do_unaligned_access: /* slow unaligned access (it spans two pages) */ @@ -223,7 +226,8 @@ DATA_TYPE val, int mmu_idx) { - target_phys_addr_t addend; + target_phys_addr_t ioaddr; + unsigned long addend; target_ulong tlb_addr; void *retaddr; int index; @@ -237,8 +241,8 @@ if ((addr & (DATA_SIZE - 1)) != 0) goto do_unaligned_access; retaddr = GETPC(); - addend = env->iotlb[mmu_idx][index]; - glue(io_write, SUFFIX)(addend, val, addr, retaddr); + ioaddr = env->iotlb[mmu_idx][index]; + glue(io_write, SUFFIX)(ioaddr, val, addr, retaddr); } else if (((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1) >= TARGET_PAGE_SIZE) { do_unaligned_access: retaddr = GETPC(); @@ -276,7 +280,8 @@ int mmu_idx, void *retaddr) { - target_phys_addr_t addend; + target_phys_addr_t ioaddr; + unsigned long addend; target_ulong tlb_addr; int index, i; @@ -288,8 +293,8 @@ /* IO access */ if ((addr & (DATA_SIZE - 1)) != 0) goto do_unaligned_access; - addend = env->iotlb[mmu_idx][index]; - glue(io_write, SUFFIX)(addend, val, addr, retaddr); + ioaddr = env->iotlb[mmu_idx][index]; + glue(io_write, SUFFIX)(ioaddr, val, addr, retaddr); } else if (((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1) >= TARGET_PAGE_SIZE) { do_unaligned_access: /* XXX: not efficient, but simple */ diff -Nru qemu-kvm-0.12.5+noroms/sparc-dis.c qemu-kvm-0.14.1/sparc-dis.c --- qemu-kvm-0.12.5+noroms/sparc-dis.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/sparc-dis.c 2011-05-11 13:29:46.000000000 +0000 @@ -2153,6 +2153,28 @@ { 0x8a, "#ASI_PRIMARY_NOFAULT_LITTLE" }, { 0x8b, "#ASI_SECONDARY_NOFAULT_LITTLE" }, /* These are UltraSPARC extensions. */ + { 0x14, "#ASI_PHYS_USE_EC"}, + { 0x15, "#ASI_PHYS_BYPASS_EC_WITH_EBIT"}, + { 0x45, "#ASI_LSU_CONTROL_REG"}, + { 0x47, "#ASI_DCACHE_TAG"}, + { 0x4a, "#ASI_UPA_CONFIG_REG"}, + { 0x50, "#ASI_IMMU" }, + { 0x51, "#ASI_IMMU_TSB_8KB_PTR_REG" }, + { 0x52, "#ASI_IMMU_TSB_64KB_PTR_REG" }, + /*{ 0x53, "#reserved?" },*/ + { 0x54, "#ASI_ITLB_DATA_IN_REG" }, + { 0x55, "#ASI_ITLB_DATA_ACCESS_REG" }, + { 0x56, "#ASI_ITLB_TAG_READ_REG" }, + { 0x57, "#ASI_IMMU_DEMAP" }, + { 0x58, "#ASI_DMMU" }, + { 0x59, "#ASI_DMMU_TSB_8KB_PTR_REG" }, + { 0x5a, "#ASI_DMMU_TSB_64KB_PTR_REG" }, + { 0x5b, "#ASI_DMMU_TSB_DIRECT_PTR_REG" }, + { 0x5c, "#ASI_DTLB_DATA_IN_REG" }, + { 0x5d, "#ASI_DTLB_DATA_ACCESS_REG" }, + { 0x5e, "#ASI_DTLB_TAG_READ_REG" }, + { 0x5f, "#ASI_DMMU_DEMAP" }, + { 0x67, "#ASI_IC_TAG"}, /* FIXME: There are dozens of them. Not sure we want them all. Most are for kernel building but some are for vis type stuff. */ { 0, NULL } @@ -2760,7 +2782,7 @@ int found_plus = 0; /* Nonzero means we have an annulled branch. */ - int is_annulled = 0; + /* int is_annulled = 0; */ /* see FIXME below */ /* Do we have an `add' or `or' instruction combining an immediate with rs1? */ @@ -2778,7 +2800,7 @@ /* Can't do simple format if source and dest are different. */ continue; - (*info->fprintf_func) (stream, opcode->name); + (*info->fprintf_func) (stream, "%s", opcode->name); { const char *s; @@ -2796,7 +2818,7 @@ { case 'a': (*info->fprintf_func) (stream, "a"); - is_annulled = 1; + /* is_annulled = 1; */ /* see FIXME below */ ++s; continue; case 'N': diff -Nru qemu-kvm-0.12.5+noroms/spice-qemu-char.c qemu-kvm-0.14.1/spice-qemu-char.c --- qemu-kvm-0.12.5+noroms/spice-qemu-char.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/spice-qemu-char.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,190 @@ +#include "config-host.h" +#include "trace.h" +#include "ui/qemu-spice.h" +#include +#include + +#include "osdep.h" + +#define dprintf(_scd, _level, _fmt, ...) \ + do { \ + static unsigned __dprintf_counter = 0; \ + if (_scd->debug >= _level) { \ + fprintf(stderr, "scd: %3d: " _fmt, ++__dprintf_counter, ## __VA_ARGS__);\ + } \ + } while (0) + +#define VMC_MAX_HOST_WRITE 2048 + +typedef struct SpiceCharDriver { + CharDriverState* chr; + SpiceCharDeviceInstance sin; + char *subtype; + bool active; + uint8_t *buffer; + uint8_t *datapos; + ssize_t bufsize, datalen; + uint32_t debug; +} SpiceCharDriver; + +static int vmc_write(SpiceCharDeviceInstance *sin, const uint8_t *buf, int len) +{ + SpiceCharDriver *scd = container_of(sin, SpiceCharDriver, sin); + ssize_t out = 0; + ssize_t last_out; + uint8_t* p = (uint8_t*)buf; + + while (len > 0) { + last_out = MIN(len, VMC_MAX_HOST_WRITE); + qemu_chr_read(scd->chr, p, last_out); + if (last_out > 0) { + out += last_out; + len -= last_out; + p += last_out; + } else { + break; + } + } + + dprintf(scd, 3, "%s: %lu/%zd\n", __func__, out, len + out); + trace_spice_vmc_write(out, len + out); + return out; +} + +static int vmc_read(SpiceCharDeviceInstance *sin, uint8_t *buf, int len) +{ + SpiceCharDriver *scd = container_of(sin, SpiceCharDriver, sin); + int bytes = MIN(len, scd->datalen); + + dprintf(scd, 2, "%s: %p %d/%d/%zd\n", __func__, scd->datapos, len, bytes, scd->datalen); + if (bytes > 0) { + memcpy(buf, scd->datapos, bytes); + scd->datapos += bytes; + scd->datalen -= bytes; + assert(scd->datalen >= 0); + if (scd->datalen == 0) { + scd->datapos = 0; + } + } + trace_spice_vmc_read(bytes, len); + return bytes; +} + +static SpiceCharDeviceInterface vmc_interface = { + .base.type = SPICE_INTERFACE_CHAR_DEVICE, + .base.description = "spice virtual channel char device", + .base.major_version = SPICE_INTERFACE_CHAR_DEVICE_MAJOR, + .base.minor_version = SPICE_INTERFACE_CHAR_DEVICE_MINOR, + .write = vmc_write, + .read = vmc_read, +}; + + +static void vmc_register_interface(SpiceCharDriver *scd) +{ + if (scd->active) { + return; + } + dprintf(scd, 1, "%s\n", __func__); + scd->sin.base.sif = &vmc_interface.base; + qemu_spice_add_interface(&scd->sin.base); + scd->active = true; + trace_spice_vmc_register_interface(scd); +} + +static void vmc_unregister_interface(SpiceCharDriver *scd) +{ + if (!scd->active) { + return; + } + dprintf(scd, 1, "%s\n", __func__); + spice_server_remove_interface(&scd->sin.base); + scd->active = false; + trace_spice_vmc_unregister_interface(scd); +} + + +static int spice_chr_write(CharDriverState *chr, const uint8_t *buf, int len) +{ + SpiceCharDriver *s = chr->opaque; + + dprintf(s, 2, "%s: %d\n", __func__, len); + vmc_register_interface(s); + assert(s->datalen == 0); + if (s->bufsize < len) { + s->bufsize = len; + s->buffer = qemu_realloc(s->buffer, s->bufsize); + } + memcpy(s->buffer, buf, len); + s->datapos = s->buffer; + s->datalen = len; + spice_server_char_device_wakeup(&s->sin); + return len; +} + +static void spice_chr_close(struct CharDriverState *chr) +{ + SpiceCharDriver *s = chr->opaque; + + printf("%s\n", __func__); + vmc_unregister_interface(s); + qemu_free(s); +} + +static void print_allowed_subtypes(void) +{ + const char** psubtype; + int i; + + fprintf(stderr, "allowed names: "); + for(i=0, psubtype = spice_server_char_device_recognized_subtypes(); + *psubtype != NULL; ++psubtype, ++i) { + if (i == 0) { + fprintf(stderr, "%s", *psubtype); + } else { + fprintf(stderr, ", %s", *psubtype); + } + } + fprintf(stderr, "\n"); +} + +CharDriverState *qemu_chr_open_spice(QemuOpts *opts) +{ + CharDriverState *chr; + SpiceCharDriver *s; + const char* name = qemu_opt_get(opts, "name"); + uint32_t debug = qemu_opt_get_number(opts, "debug", 0); + const char** psubtype = spice_server_char_device_recognized_subtypes(); + const char *subtype = NULL; + + if (name == NULL) { + fprintf(stderr, "spice-qemu-char: missing name parameter\n"); + print_allowed_subtypes(); + return NULL; + } + for(;*psubtype != NULL; ++psubtype) { + if (strcmp(name, *psubtype) == 0) { + subtype = *psubtype; + break; + } + } + if (subtype == NULL) { + fprintf(stderr, "spice-qemu-char: unsupported name\n"); + print_allowed_subtypes(); + return NULL; + } + + chr = qemu_mallocz(sizeof(CharDriverState)); + s = qemu_mallocz(sizeof(SpiceCharDriver)); + s->chr = chr; + s->debug = debug; + s->active = false; + s->sin.subtype = subtype; + chr->opaque = s; + chr->chr_write = spice_chr_write; + chr->chr_close = spice_chr_close; + + qemu_chr_generic_open(chr); + + return chr; +} diff -Nru qemu-kvm-0.12.5+noroms/sysconfigs/target/target-x86_64.conf qemu-kvm-0.14.1/sysconfigs/target/target-x86_64.conf --- qemu-kvm-0.12.5+noroms/sysconfigs/target/target-x86_64.conf 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/sysconfigs/target/target-x86_64.conf 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,86 @@ +# x86 CPU MODELS + +[cpudef] + name = "Conroe" + level = "2" + vendor = "GenuineIntel" + family = "6" + model = "2" + stepping = "3" + feature_edx = "sse2 sse fxsr mmx pat cmov pge sep apic cx8 mce pae msr tsc pse de fpu mtrr clflush mca pse36" + feature_ecx = "sse3 ssse3" + extfeature_edx = "fxsr mmx pat cmov pge apic cx8 mce pae msr tsc pse de fpu lm syscall nx" + extfeature_ecx = "lahf_lm" + xlevel = "0x8000000A" + model_id = "Intel Celeron_4x0 (Conroe/Merom Class Core 2)" + +[cpudef] + name = "Penryn" + level = "2" + vendor = "GenuineIntel" + family = "6" + model = "2" + stepping = "3" + feature_edx = "sse2 sse fxsr mmx pat cmov pge sep apic cx8 mce pae msr tsc pse de fpu mtrr clflush mca pse36" + feature_ecx = "sse3 cx16 ssse3 sse4.1" + extfeature_edx = "fxsr mmx pat cmov pge apic cx8 mce pae msr tsc pse de fpu lm syscall nx" + extfeature_ecx = "lahf_lm" + xlevel = "0x8000000A" + model_id = "Intel Core 2 Duo P9xxx (Penryn Class Core 2)" + +[cpudef] + name = "Nehalem" + level = "2" + vendor = "GenuineIntel" + family = "6" + model = "2" + stepping = "3" + feature_edx = "sse2 sse fxsr mmx pat cmov pge sep apic cx8 mce pae msr tsc pse de fpu mtrr clflush mca pse36" + feature_ecx = "sse3 cx16 ssse3 sse4.1 sse4.2 popcnt" + extfeature_edx = "fxsr mmx pat cmov pge apic cx8 mce pae msr tsc pse de fpu lm syscall nx" + extfeature_ecx = "lahf_lm" + xlevel = "0x8000000A" + model_id = "Intel Core i7 9xx (Nehalem Class Core i7)" + +[cpudef] + name = "Opteron_G1" + level = "5" + vendor = "AuthenticAMD" + family = "15" + model = "6" + stepping = "1" + feature_edx = "sse2 sse fxsr mmx pat cmov pge sep apic cx8 mce pae msr tsc pse de fpu mtrr clflush mca pse36" + feature_ecx = "sse3" + extfeature_edx = "fxsr mmx pat cmov pge apic cx8 mce pae msr tsc pse de fpu lm syscall nx" +# extfeature_ecx = "" + xlevel = "0x80000008" + model_id = "AMD Opteron 240 (Gen 1 Class Opteron)" + +[cpudef] + name = "Opteron_G2" + level = "5" + vendor = "AuthenticAMD" + family = "15" + model = "6" + stepping = "1" + feature_edx = "sse2 sse fxsr mmx pat cmov pge sep apic cx8 mce pae msr tsc pse de fpu mtrr clflush mca pse36" + feature_ecx = "sse3 cx16" + extfeature_edx = "fxsr mmx pat cmov pge apic cx8 mce pae msr tsc pse de fpu lm syscall nx rdtscp" + extfeature_ecx = "svm lahf_lm" + xlevel = "0x80000008" + model_id = "AMD Opteron 22xx (Gen 2 Class Opteron)" + +[cpudef] + name = "Opteron_G3" + level = "5" + vendor = "AuthenticAMD" + family = "15" + model = "6" + stepping = "1" + feature_edx = "sse2 sse fxsr mmx pat cmov pge sep apic cx8 mce pae msr tsc pse de fpu mtrr clflush mca pse36" + feature_ecx = "sse3 cx16 monitor popcnt" + extfeature_edx = "fxsr mmx pat cmov pge apic cx8 mce pae msr tsc pse de fpu lm syscall nx rdtscp" + extfeature_ecx = "svm sse4a abm misalignsse lahf_lm" + xlevel = "0x80000008" + model_id = "AMD Opteron 23xx (Gen 3 Class Opteron)" + diff -Nru qemu-kvm-0.12.5+noroms/sysemu.h qemu-kvm-0.14.1/sysemu.h --- qemu-kvm-0.12.5+noroms/sysemu.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/sysemu.h 2011-05-11 13:29:46.000000000 +0000 @@ -6,11 +6,15 @@ #include "qemu-option.h" #include "qemu-queue.h" #include "qemu-timer.h" -#include "qdict.h" -#include "qerror.h" +#include "notify.h" #ifdef _WIN32 #include +#include "qemu-os-win32.h" +#endif + +#ifdef CONFIG_POSIX +#include "qemu-os-posix.h" #endif /* vl.c */ @@ -54,15 +58,25 @@ extern qemu_irq qemu_system_powerdown; void qemu_system_reset(void); +void qemu_add_exit_notifier(Notifier *notify); +void qemu_remove_exit_notifier(Notifier *notify); + +void qemu_add_machine_init_done_notifier(Notifier *notify); + void do_savevm(Monitor *mon, const QDict *qdict); -int load_vmstate(Monitor *mon, const char *name); +int load_vmstate(const char *name); void do_delvm(Monitor *mon, const QDict *qdict); void do_info_snapshots(Monitor *mon); +void cpu_synchronize_all_states(void); +void cpu_synchronize_all_post_reset(void); +void cpu_synchronize_all_post_init(void); + void qemu_announce_self(void); -void main_loop_wait(int timeout); +void main_loop_wait(int nonblocking); +bool qemu_savevm_state_blocked(Monitor *mon); int qemu_savevm_state_begin(Monitor *mon, QEMUFile *f, int blk_enable, int shared); int qemu_savevm_state_iterate(Monitor *mon, QEMUFile *f); @@ -70,50 +84,29 @@ void qemu_savevm_state_cancel(Monitor *mon, QEMUFile *f); int qemu_loadvm_state(QEMUFile *f); -void qemu_errors_to_file(FILE *fp); -void qemu_errors_to_mon(Monitor *mon); -void qemu_errors_to_previous(void); -void qemu_error(const char *fmt, ...) __attribute__ ((format(printf, 1, 2))); -void qemu_error_internal(const char *file, int linenr, const char *func, - const char *fmt, ...) - __attribute__ ((format(printf, 4, 5))); - -#define qemu_error_new(fmt, ...) \ - qemu_error_internal(__FILE__, __LINE__, __func__, fmt, ## __VA_ARGS__) - -#ifdef _WIN32 -/* Polling handling */ - -/* return TRUE if no sleep should be done afterwards */ -typedef int PollingFunc(void *opaque); - -int qemu_add_polling_cb(PollingFunc *func, void *opaque); -void qemu_del_polling_cb(PollingFunc *func, void *opaque); - -/* Wait objects handling */ -typedef void WaitObjectFunc(void *opaque); - -int qemu_add_wait_object(HANDLE handle, WaitObjectFunc *func, void *opaque); -void qemu_del_wait_object(HANDLE handle, WaitObjectFunc *func, void *opaque); -#endif - /* SLIRP */ void do_info_slirp(Monitor *mon); +/* OS specific functions */ +void os_setup_early_signal_handling(void); +char *os_find_datadir(const char *argv0); +void os_parse_cmd_args(int index, const char *optarg); +void os_pidfile_error(void); + typedef enum DisplayType { DT_DEFAULT, DT_CURSES, DT_SDL, - DT_VNC, DT_NOGRAPHIC, } DisplayType; extern int autostart; +extern int incoming_expected; extern int bios_size; typedef enum { - VGA_NONE, VGA_STD, VGA_CIRRUS, VGA_VMWARE, VGA_XENFB + VGA_NONE, VGA_STD, VGA_CIRRUS, VGA_VMWARE, VGA_XENFB, VGA_QXL, } VGAInterfaceType; extern int vga_interface_type; @@ -121,6 +114,7 @@ #define std_vga_enabled (vga_interface_type == VGA_STD) #define xenfb_enabled (vga_interface_type == VGA_XENFB) #define vmsvga_enabled (vga_interface_type == VGA_VMWARE) +#define qxl_enabled (vga_interface_type == VGA_QXL) extern int graphic_width; extern int graphic_height; @@ -138,6 +132,7 @@ extern int cursor_hide; extern int graphic_rotate; extern int no_quit; +extern int no_shutdown; extern int semihosting_enabled; extern int old_param; extern int boot_menu; @@ -150,80 +145,29 @@ extern uint64_t node_cpumask[MAX_NODES]; #define MAX_OPTION_ROMS 16 -extern const char *option_rom[MAX_OPTION_ROMS]; +typedef struct QEMUOptionRom { + const char *name; + int32_t bootindex; +} QEMUOptionRom; +extern QEMUOptionRom option_rom[MAX_OPTION_ROMS]; extern int nb_option_roms; -#ifdef NEED_CPU_H -#if defined(TARGET_SPARC) || defined(TARGET_PPC) #define MAX_PROM_ENVS 128 extern const char *prom_envs[MAX_PROM_ENVS]; extern unsigned int nb_prom_envs; -#endif -#endif - -typedef enum { - IF_NONE, - IF_IDE, IF_SCSI, IF_FLOPPY, IF_PFLASH, IF_MTD, IF_SD, IF_VIRTIO, IF_XEN, - IF_COUNT -} BlockInterfaceType; - -typedef enum { - BLOCK_ERR_REPORT, BLOCK_ERR_IGNORE, BLOCK_ERR_STOP_ENOSPC, - BLOCK_ERR_STOP_ANY -} BlockInterfaceErrorAction; - -#define BLOCK_SERIAL_STRLEN 20 - -typedef struct DriveInfo { - BlockDriverState *bdrv; - char *id; - const char *devaddr; - BlockInterfaceType type; - int bus; - int unit; - QemuOpts *opts; - BlockInterfaceErrorAction on_read_error; - BlockInterfaceErrorAction on_write_error; - char serial[BLOCK_SERIAL_STRLEN + 1]; - QTAILQ_ENTRY(DriveInfo) next; -} DriveInfo; - -#define MAX_IDE_DEVS 2 -#define MAX_SCSI_DEVS 7 -#define MAX_DRIVES 32 - -extern QTAILQ_HEAD(drivelist, DriveInfo) drives; -extern QTAILQ_HEAD(driveoptlist, DriveOpt) driveopts; -extern DriveInfo *extboot_drive; - -extern DriveInfo *drive_get(BlockInterfaceType type, int bus, int unit); -extern DriveInfo *drive_get_by_id(const char *id); -extern int drive_get_max_bus(BlockInterfaceType type); -extern void drive_uninit(DriveInfo *dinfo); -extern const char *drive_get_serial(BlockDriverState *bdrv); - -extern BlockInterfaceErrorAction drive_get_on_error( - BlockDriverState *bdrv, int is_read); - -BlockDriverState *qdev_init_bdrv(DeviceState *dev, BlockInterfaceType type); - -extern QemuOpts *drive_add(const char *file, const char *fmt, ...); -extern DriveInfo *drive_init(QemuOpts *arg, void *machine, int *fatal_error); /* acpi */ void qemu_system_cpu_hot_add(int cpu, int state); -/* device-hotplug */ - -DriveInfo *add_init_drive(const char *opts); - /* pci-hotplug */ -void pci_device_hot_add_print(Monitor *mon, const QObject *data); -void pci_device_hot_add(Monitor *mon, const QDict *qdict, QObject **ret_data); +void pci_device_hot_add(Monitor *mon, const QDict *qdict); void drive_hot_add(Monitor *mon, const QDict *qdict); -void pci_device_hot_remove(Monitor *mon, const char *pci_addr); -void do_pci_device_hot_remove(Monitor *mon, const QDict *qdict, - QObject **ret_data); +void do_pci_device_hot_remove(Monitor *mon, const QDict *qdict); + +/* pcie aer error injection */ +void pcie_aer_inject_error_print(Monitor *mon, const QObject *data); +int do_pcie_aer_inejct_error(Monitor *mon, + const QDict *qdict, QObject **ret_data); /* serial ports */ @@ -237,33 +181,17 @@ extern CharDriverState *parallel_hds[MAX_PARALLEL_PORTS]; -/* virtio consoles */ - -#define MAX_VIRTIO_CONSOLES 1 - -extern CharDriverState *virtcon_hds[MAX_VIRTIO_CONSOLES]; - #define TFR(expr) do { if ((expr) != -1) break; } while (errno == EINTR) -#ifdef HAS_AUDIO -struct soundhw { - const char *name; - const char *descr; - int enabled; - int isa; - union { - int (*init_isa) (qemu_irq *pic); - int (*init_pci) (PCIBus *bus); - } init; -}; - -extern struct soundhw soundhw[]; -#endif - void do_usb_add(Monitor *mon, const QDict *qdict); void do_usb_del(Monitor *mon, const QDict *qdict); void usb_info(Monitor *mon); +void rtc_change_mon_event(struct tm *tm); + void register_devices(void); +void add_boot_device_path(int32_t bootindex, DeviceState *dev, + const char *suffix); +char *get_boot_devices_list(uint32_t *size); #endif diff -Nru qemu-kvm-0.12.5+noroms/target-alpha/cpu.h qemu-kvm-0.14.1/target-alpha/cpu.h --- qemu-kvm-0.12.5+noroms/target-alpha/cpu.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-alpha/cpu.h 2011-05-11 13:29:46.000000000 +0000 @@ -41,7 +41,9 @@ #define TARGET_PAGE_BITS 13 -#define VA_BITS 43 +/* ??? EV4 has 34 phys addr bits, EV5 has 40, EV6 has 44. */ +#define TARGET_PHYS_ADDR_SPACE_BITS 44 +#define TARGET_VIRT_ADDR_SPACE_BITS (30 + TARGET_PAGE_BITS) /* Alpha major type */ enum { @@ -139,9 +141,65 @@ FP_ROUND_DYNAMIC = 0x3, }; +/* FPCR bits */ +#define FPCR_SUM (1ULL << 63) +#define FPCR_INED (1ULL << 62) +#define FPCR_UNFD (1ULL << 61) +#define FPCR_UNDZ (1ULL << 60) +#define FPCR_DYN_SHIFT 58 +#define FPCR_DYN_CHOPPED (0ULL << FPCR_DYN_SHIFT) +#define FPCR_DYN_MINUS (1ULL << FPCR_DYN_SHIFT) +#define FPCR_DYN_NORMAL (2ULL << FPCR_DYN_SHIFT) +#define FPCR_DYN_PLUS (3ULL << FPCR_DYN_SHIFT) +#define FPCR_DYN_MASK (3ULL << FPCR_DYN_SHIFT) +#define FPCR_IOV (1ULL << 57) +#define FPCR_INE (1ULL << 56) +#define FPCR_UNF (1ULL << 55) +#define FPCR_OVF (1ULL << 54) +#define FPCR_DZE (1ULL << 53) +#define FPCR_INV (1ULL << 52) +#define FPCR_OVFD (1ULL << 51) +#define FPCR_DZED (1ULL << 50) +#define FPCR_INVD (1ULL << 49) +#define FPCR_DNZ (1ULL << 48) +#define FPCR_DNOD (1ULL << 47) +#define FPCR_STATUS_MASK (FPCR_IOV | FPCR_INE | FPCR_UNF \ + | FPCR_OVF | FPCR_DZE | FPCR_INV) + +/* The silly software trap enables implemented by the kernel emulation. + These are more or less architecturally required, since the real hardware + has read-as-zero bits in the FPCR when the features aren't implemented. + For the purposes of QEMU, we pretend the FPCR can hold everything. */ +#define SWCR_TRAP_ENABLE_INV (1ULL << 1) +#define SWCR_TRAP_ENABLE_DZE (1ULL << 2) +#define SWCR_TRAP_ENABLE_OVF (1ULL << 3) +#define SWCR_TRAP_ENABLE_UNF (1ULL << 4) +#define SWCR_TRAP_ENABLE_INE (1ULL << 5) +#define SWCR_TRAP_ENABLE_DNO (1ULL << 6) +#define SWCR_TRAP_ENABLE_MASK ((1ULL << 7) - (1ULL << 1)) + +#define SWCR_MAP_DMZ (1ULL << 12) +#define SWCR_MAP_UMZ (1ULL << 13) +#define SWCR_MAP_MASK (SWCR_MAP_DMZ | SWCR_MAP_UMZ) + +#define SWCR_STATUS_INV (1ULL << 17) +#define SWCR_STATUS_DZE (1ULL << 18) +#define SWCR_STATUS_OVF (1ULL << 19) +#define SWCR_STATUS_UNF (1ULL << 20) +#define SWCR_STATUS_INE (1ULL << 21) +#define SWCR_STATUS_DNO (1ULL << 22) +#define SWCR_STATUS_MASK ((1ULL << 23) - (1ULL << 17)) + +#define SWCR_MASK (SWCR_TRAP_ENABLE_MASK | SWCR_MAP_MASK | SWCR_STATUS_MASK) + /* Internal processor registers */ /* XXX: TOFIX: most of those registers are implementation dependant */ enum { +#if defined(CONFIG_USER_ONLY) + IPR_EXC_ADDR, + IPR_EXC_SUM, + IPR_EXC_MASK, +#else /* Ebox IPRs */ IPR_CC = 0xC0, /* 21264 */ IPR_CC_CTL = 0xC1, /* 21264 */ @@ -255,6 +313,7 @@ IPR_VPTB, IPR_WHAMI, IPR_ALT_MODE, +#endif IPR_LAST, }; @@ -294,17 +353,28 @@ struct CPUAlphaState { uint64_t ir[31]; - float64 fir[31]; - float_status fp_status; - uint64_t fpcr; + float64 fir[31]; uint64_t pc; - uint64_t lock; - uint32_t pcc[2]; uint64_t ipr[IPR_LAST]; uint64_t ps; uint64_t unique; - int saved_mode; /* Used for HW_LD / HW_ST */ - int intr_flag; /* For RC and RS */ + uint64_t lock_addr; + uint64_t lock_st_addr; + uint64_t lock_value; + float_status fp_status; + /* The following fields make up the FPCR, but in FP_STATUS format. */ + uint8_t fpcr_exc_status; + uint8_t fpcr_exc_mask; + uint8_t fpcr_dyn_round; + uint8_t fpcr_flush_to_zero; + uint8_t fpcr_dnz; + uint8_t fpcr_dnod; + uint8_t fpcr_undz; + + /* Used for HW_LD / HW_ST */ + uint8_t saved_mode; + /* For RC and RS */ + uint8_t intr_flag; #if TARGET_LONG_BITS > HOST_LONG_BITS /* temporary fixed-point registers @@ -342,17 +412,7 @@ return (env->ps >> 3) & 3; } -#if defined(CONFIG_USER_ONLY) -static inline void cpu_clone_regs(CPUState *env, target_ulong newsp) -{ - if (newsp) - env->ir[30] = newsp; - /* FIXME: Zero syscall return value. */ -} -#endif - #include "cpu-all.h" -#include "exec-all.h" enum { FEATURE_ASN = 0x00000001, @@ -380,12 +440,18 @@ /* Pseudo exception for console */ EXCP_CONSOLE_DISPATCH = 0x4001, EXCP_CONSOLE_FIXUP = 0x4002, + EXCP_STL_C = 0x4003, + EXCP_STQ_C = 0x4004, }; /* Arithmetic exception */ -enum { - EXCP_ARITH_OVERFLOW, -}; +#define EXC_M_IOV (1<<16) /* Integer Overflow */ +#define EXC_M_INE (1<<15) /* Inexact result */ +#define EXC_M_UNF (1<<14) /* Underflow */ +#define EXC_M_FOV (1<<13) /* Overflow */ +#define EXC_M_DZE (1<<12) /* Division by zero */ +#define EXC_M_INV (1<<11) /* Invalid operation */ +#define EXC_M_SWC (1<<10) /* Software completion */ enum { IR_V0 = 0, @@ -404,7 +470,7 @@ IR_S4 = 13, IR_S5 = 14, IR_S6 = 15, -#define IR_FP IR_S6 + IR_FP = IR_S6, IR_A0 = 16, IR_A1 = 17, IR_A2 = 18, @@ -417,7 +483,7 @@ IR_T11 = 25, IR_RA = 26, IR_T12 = 27, -#define IR_PV IR_T12 + IR_PV = IR_T12, IR_AT = 28, IR_GP = 29, IR_SP = 30, @@ -436,20 +502,15 @@ #define cpu_handle_mmu_fault cpu_alpha_handle_mmu_fault void do_interrupt (CPUState *env); +uint64_t cpu_alpha_load_fpcr (CPUState *env); +void cpu_alpha_store_fpcr (CPUState *env, uint64_t val); int cpu_alpha_mfpr (CPUState *env, int iprn, uint64_t *valp); int cpu_alpha_mtpr (CPUState *env, int iprn, uint64_t val, uint64_t *oldvalp); -void pal_init (CPUState *env); #if !defined (CONFIG_USER_ONLY) +void pal_init (CPUState *env); void call_pal (CPUState *env); -#else -void call_pal (CPUState *env, int palcode); #endif -static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb) -{ - env->pc = tb->pc; -} - static inline void cpu_get_tb_cpu_state(CPUState *env, target_ulong *pc, target_ulong *cs_base, int *flags) { @@ -458,4 +519,20 @@ *flags = env->ps; } +#if defined(CONFIG_USER_ONLY) +static inline void cpu_clone_regs(CPUState *env, target_ulong newsp) +{ + if (newsp) { + env->ir[IR_SP] = newsp; + } + env->ir[IR_V0] = 0; + env->ir[IR_A3] = 0; +} + +static inline void cpu_set_tls(CPUState *env, target_ulong newtls) +{ + env->unique = newtls; +} +#endif + #endif /* !defined (__CPU_ALPHA_H__) */ diff -Nru qemu-kvm-0.12.5+noroms/target-alpha/exec.h qemu-kvm-0.14.1/target-alpha/exec.h --- qemu-kvm-0.12.5+noroms/target-alpha/exec.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-alpha/exec.h 2011-05-11 13:29:46.000000000 +0000 @@ -28,8 +28,6 @@ register struct CPUAlphaState *env asm(AREG0); -#define PARAM(n) ((uint64_t)PARAM##n) -#define SPARAM(n) ((int32_t)PARAM##n) #define FP_STATUS (env->fp_status) #include "cpu.h" @@ -39,14 +37,6 @@ #include "softmmu_exec.h" #endif /* !defined(CONFIG_USER_ONLY) */ -static inline void env_to_regs(void) -{ -} - -static inline void regs_to_env(void) -{ -} - static inline int cpu_has_work(CPUState *env) { return (env->interrupt_request & CPU_INTERRUPT_HARD); @@ -63,4 +53,9 @@ return EXCP_HALTED; } +static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb) +{ + env->pc = tb->pc; +} + #endif /* !defined (__ALPHA_EXEC_H__) */ diff -Nru qemu-kvm-0.12.5+noroms/target-alpha/helper.c qemu-kvm-0.14.1/target-alpha/helper.c --- qemu-kvm-0.12.5+noroms/target-alpha/helper.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-alpha/helper.c 2011-05-11 13:29:46.000000000 +0000 @@ -23,6 +23,141 @@ #include "cpu.h" #include "exec-all.h" +#include "softfloat.h" + +uint64_t cpu_alpha_load_fpcr (CPUState *env) +{ + uint64_t r = 0; + uint8_t t; + + t = env->fpcr_exc_status; + if (t) { + r = FPCR_SUM; + if (t & float_flag_invalid) { + r |= FPCR_INV; + } + if (t & float_flag_divbyzero) { + r |= FPCR_DZE; + } + if (t & float_flag_overflow) { + r |= FPCR_OVF; + } + if (t & float_flag_underflow) { + r |= FPCR_UNF; + } + if (t & float_flag_inexact) { + r |= FPCR_INE; + } + } + + t = env->fpcr_exc_mask; + if (t & float_flag_invalid) { + r |= FPCR_INVD; + } + if (t & float_flag_divbyzero) { + r |= FPCR_DZED; + } + if (t & float_flag_overflow) { + r |= FPCR_OVFD; + } + if (t & float_flag_underflow) { + r |= FPCR_UNFD; + } + if (t & float_flag_inexact) { + r |= FPCR_INED; + } + + switch (env->fpcr_dyn_round) { + case float_round_nearest_even: + r |= FPCR_DYN_NORMAL; + break; + case float_round_down: + r |= FPCR_DYN_MINUS; + break; + case float_round_up: + r |= FPCR_DYN_PLUS; + break; + case float_round_to_zero: + r |= FPCR_DYN_CHOPPED; + break; + } + + if (env->fpcr_dnz) { + r |= FPCR_DNZ; + } + if (env->fpcr_dnod) { + r |= FPCR_DNOD; + } + if (env->fpcr_undz) { + r |= FPCR_UNDZ; + } + + return r; +} + +void cpu_alpha_store_fpcr (CPUState *env, uint64_t val) +{ + uint8_t t; + + t = 0; + if (val & FPCR_INV) { + t |= float_flag_invalid; + } + if (val & FPCR_DZE) { + t |= float_flag_divbyzero; + } + if (val & FPCR_OVF) { + t |= float_flag_overflow; + } + if (val & FPCR_UNF) { + t |= float_flag_underflow; + } + if (val & FPCR_INE) { + t |= float_flag_inexact; + } + env->fpcr_exc_status = t; + + t = 0; + if (val & FPCR_INVD) { + t |= float_flag_invalid; + } + if (val & FPCR_DZED) { + t |= float_flag_divbyzero; + } + if (val & FPCR_OVFD) { + t |= float_flag_overflow; + } + if (val & FPCR_UNFD) { + t |= float_flag_underflow; + } + if (val & FPCR_INED) { + t |= float_flag_inexact; + } + env->fpcr_exc_mask = t; + + switch (val & FPCR_DYN_MASK) { + case FPCR_DYN_CHOPPED: + t = float_round_to_zero; + break; + case FPCR_DYN_MINUS: + t = float_round_down; + break; + case FPCR_DYN_NORMAL: + t = float_round_nearest_even; + break; + case FPCR_DYN_PLUS: + t = float_round_up; + break; + } + env->fpcr_dyn_round = t; + + env->fpcr_flush_to_zero + = (val & (FPCR_UNDZ|FPCR_UNFD)) == (FPCR_UNDZ|FPCR_UNFD); + + env->fpcr_dnz = (val & FPCR_DNZ) != 0; + env->fpcr_dnod = (val & FPCR_DNOD) != 0; + env->fpcr_undz = (val & FPCR_UNDZ) != 0; +} #if defined(CONFIG_USER_ONLY) @@ -38,11 +173,6 @@ return 1; } -target_phys_addr_t cpu_get_phys_page_debug (CPUState *env, target_ulong addr) -{ - return addr; -} - void do_interrupt (CPUState *env) { env->exception_index = -1; @@ -336,6 +466,7 @@ env->ipr[IPR_SYSPTBR] = val; else ret = -1; + break; case IPR_TBCHK: /* Read-only */ ret = -1; @@ -389,7 +520,7 @@ env->ipr[IPR_EXC_ADDR] = env->pc | 1; excp = env->exception_index; - env->exception_index = 0; + env->exception_index = -1; env->error_code = 0; /* XXX: disable interrupts and memory mapping */ if (env->ipr[IPR_PAL_BASE] != -1ULL) { @@ -406,8 +537,7 @@ } #endif -void cpu_dump_state (CPUState *env, FILE *f, - int (*cpu_fprintf)(FILE *f, const char *fmt, ...), +void cpu_dump_state (CPUState *env, FILE *f, fprintf_function cpu_fprintf, int flags) { static const char *linux_reg_names[] = { @@ -426,12 +556,15 @@ if ((i % 3) == 2) cpu_fprintf(f, "\n"); } - cpu_fprintf(f, "\n"); + + cpu_fprintf(f, "lock_a " TARGET_FMT_lx " lock_v " TARGET_FMT_lx "\n", + env->lock_addr, env->lock_value); + for (i = 0; i < 31; i++) { cpu_fprintf(f, "FIR%02d " TARGET_FMT_lx " ", i, *((uint64_t *)(&env->fir[i]))); if ((i % 3) == 2) cpu_fprintf(f, "\n"); } - cpu_fprintf(f, "\nlock " TARGET_FMT_lx "\n", env->lock); + cpu_fprintf(f, "\n"); } diff -Nru qemu-kvm-0.12.5+noroms/target-alpha/helper.h qemu-kvm-0.14.1/target-alpha/helper.h --- qemu-kvm-0.12.5+noroms/target-alpha/helper.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-alpha/helper.h 2011-05-11 13:29:46.000000000 +0000 @@ -1,11 +1,7 @@ #include "def-helper.h" -DEF_HELPER_0(tb_flush, void) - DEF_HELPER_2(excp, void, int, int) -DEF_HELPER_0(load_pcc, i64) -DEF_HELPER_0(rc, i64) -DEF_HELPER_0(rs, i64) +DEF_HELPER_FLAGS_0(load_pcc, TCG_CALL_CONST | TCG_CALL_PURE, i64) DEF_HELPER_2(addqv, i64, i64, i64) DEF_HELPER_2(addlv, i64, i64, i64) @@ -13,96 +9,95 @@ DEF_HELPER_2(sublv, i64, i64, i64) DEF_HELPER_2(mullv, i64, i64, i64) DEF_HELPER_2(mulqv, i64, i64, i64) -DEF_HELPER_2(umulh, i64, i64, i64) +DEF_HELPER_FLAGS_2(umulh, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64) -DEF_HELPER_1(ctpop, i64, i64) -DEF_HELPER_1(ctlz, i64, i64) -DEF_HELPER_1(cttz, i64, i64) - -DEF_HELPER_2(mskbl, i64, i64, i64) -DEF_HELPER_2(insbl, i64, i64, i64) -DEF_HELPER_2(mskwl, i64, i64, i64) -DEF_HELPER_2(inswl, i64, i64, i64) -DEF_HELPER_2(mskll, i64, i64, i64) -DEF_HELPER_2(insll, i64, i64, i64) -DEF_HELPER_2(zap, i64, i64, i64) -DEF_HELPER_2(zapnot, i64, i64, i64) -DEF_HELPER_2(mskql, i64, i64, i64) -DEF_HELPER_2(insql, i64, i64, i64) -DEF_HELPER_2(mskwh, i64, i64, i64) -DEF_HELPER_2(inswh, i64, i64, i64) -DEF_HELPER_2(msklh, i64, i64, i64) -DEF_HELPER_2(inslh, i64, i64, i64) -DEF_HELPER_2(mskqh, i64, i64, i64) -DEF_HELPER_2(insqh, i64, i64, i64) - -DEF_HELPER_2(cmpbge, i64, i64, i64) - -DEF_HELPER_0(load_fpcr, i64) -DEF_HELPER_1(store_fpcr, void, i64) - -DEF_HELPER_1(f_to_memory, i32, i64) -DEF_HELPER_1(memory_to_f, i64, i32) -DEF_HELPER_2(addf, i64, i64, i64) -DEF_HELPER_2(subf, i64, i64, i64) -DEF_HELPER_2(mulf, i64, i64, i64) -DEF_HELPER_2(divf, i64, i64, i64) -DEF_HELPER_1(sqrtf, i64, i64) - -DEF_HELPER_1(g_to_memory, i64, i64) -DEF_HELPER_1(memory_to_g, i64, i64) -DEF_HELPER_2(addg, i64, i64, i64) -DEF_HELPER_2(subg, i64, i64, i64) -DEF_HELPER_2(mulg, i64, i64, i64) -DEF_HELPER_2(divg, i64, i64, i64) -DEF_HELPER_1(sqrtg, i64, i64) - -DEF_HELPER_1(s_to_memory, i32, i64) -DEF_HELPER_1(memory_to_s, i64, i32) -DEF_HELPER_2(adds, i64, i64, i64) -DEF_HELPER_2(subs, i64, i64, i64) -DEF_HELPER_2(muls, i64, i64, i64) -DEF_HELPER_2(divs, i64, i64, i64) -DEF_HELPER_1(sqrts, i64, i64) - -DEF_HELPER_2(addt, i64, i64, i64) -DEF_HELPER_2(subt, i64, i64, i64) -DEF_HELPER_2(mult, i64, i64, i64) -DEF_HELPER_2(divt, i64, i64, i64) -DEF_HELPER_1(sqrtt, i64, i64) - -DEF_HELPER_2(cmptun, i64, i64, i64) -DEF_HELPER_2(cmpteq, i64, i64, i64) -DEF_HELPER_2(cmptle, i64, i64, i64) -DEF_HELPER_2(cmptlt, i64, i64, i64) -DEF_HELPER_2(cmpgeq, i64, i64, i64) -DEF_HELPER_2(cmpgle, i64, i64, i64) -DEF_HELPER_2(cmpglt, i64, i64, i64) - -DEF_HELPER_1(cmpfeq, i64, i64) -DEF_HELPER_1(cmpfne, i64, i64) -DEF_HELPER_1(cmpflt, i64, i64) -DEF_HELPER_1(cmpfle, i64, i64) -DEF_HELPER_1(cmpfgt, i64, i64) -DEF_HELPER_1(cmpfge, i64, i64) - -DEF_HELPER_2(cpys, i64, i64, i64) -DEF_HELPER_2(cpysn, i64, i64, i64) -DEF_HELPER_2(cpyse, i64, i64, i64) - -DEF_HELPER_1(cvtts, i64, i64) -DEF_HELPER_1(cvtst, i64, i64) -DEF_HELPER_1(cvttq, i64, i64) -DEF_HELPER_1(cvtqs, i64, i64) -DEF_HELPER_1(cvtqt, i64, i64) -DEF_HELPER_1(cvtqf, i64, i64) -DEF_HELPER_1(cvtgf, i64, i64) -DEF_HELPER_1(cvtgq, i64, i64) -DEF_HELPER_1(cvtqg, i64, i64) -DEF_HELPER_1(cvtlq, i64, i64) -DEF_HELPER_1(cvtql, i64, i64) -DEF_HELPER_1(cvtqlv, i64, i64) -DEF_HELPER_1(cvtqlsv, i64, i64) +DEF_HELPER_FLAGS_1(ctpop, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64) +DEF_HELPER_FLAGS_1(ctlz, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64) +DEF_HELPER_FLAGS_1(cttz, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64) + +DEF_HELPER_FLAGS_2(zap, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64) +DEF_HELPER_FLAGS_2(zapnot, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64) + +DEF_HELPER_FLAGS_2(cmpbge, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64) + +DEF_HELPER_FLAGS_2(minub8, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64) +DEF_HELPER_FLAGS_2(minsb8, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64) +DEF_HELPER_FLAGS_2(minuw4, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64) +DEF_HELPER_FLAGS_2(minsw4, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64) +DEF_HELPER_FLAGS_2(maxub8, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64) +DEF_HELPER_FLAGS_2(maxsb8, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64) +DEF_HELPER_FLAGS_2(maxuw4, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64) +DEF_HELPER_FLAGS_2(maxsw4, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64) +DEF_HELPER_FLAGS_2(perr, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64) +DEF_HELPER_FLAGS_1(pklb, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64) +DEF_HELPER_FLAGS_1(pkwb, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64) +DEF_HELPER_FLAGS_1(unpkbl, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64) +DEF_HELPER_FLAGS_1(unpkbw, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64) + +DEF_HELPER_FLAGS_0(load_fpcr, TCG_CALL_CONST | TCG_CALL_PURE, i64) +DEF_HELPER_FLAGS_1(store_fpcr, TCG_CALL_CONST, void, i64) + +DEF_HELPER_FLAGS_1(f_to_memory, TCG_CALL_CONST | TCG_CALL_PURE, i32, i64) +DEF_HELPER_FLAGS_1(memory_to_f, TCG_CALL_CONST | TCG_CALL_PURE, i64, i32) +DEF_HELPER_FLAGS_2(addf, TCG_CALL_CONST, i64, i64, i64) +DEF_HELPER_FLAGS_2(subf, TCG_CALL_CONST, i64, i64, i64) +DEF_HELPER_FLAGS_2(mulf, TCG_CALL_CONST, i64, i64, i64) +DEF_HELPER_FLAGS_2(divf, TCG_CALL_CONST, i64, i64, i64) +DEF_HELPER_FLAGS_1(sqrtf, TCG_CALL_CONST, i64, i64) + +DEF_HELPER_FLAGS_1(g_to_memory, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64) +DEF_HELPER_FLAGS_1(memory_to_g, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64) +DEF_HELPER_FLAGS_2(addg, TCG_CALL_CONST, i64, i64, i64) +DEF_HELPER_FLAGS_2(subg, TCG_CALL_CONST, i64, i64, i64) +DEF_HELPER_FLAGS_2(mulg, TCG_CALL_CONST, i64, i64, i64) +DEF_HELPER_FLAGS_2(divg, TCG_CALL_CONST, i64, i64, i64) +DEF_HELPER_FLAGS_1(sqrtg, TCG_CALL_CONST, i64, i64) + +DEF_HELPER_FLAGS_1(s_to_memory, TCG_CALL_CONST | TCG_CALL_PURE, i32, i64) +DEF_HELPER_FLAGS_1(memory_to_s, TCG_CALL_CONST | TCG_CALL_PURE, i64, i32) +DEF_HELPER_FLAGS_2(adds, TCG_CALL_CONST, i64, i64, i64) +DEF_HELPER_FLAGS_2(subs, TCG_CALL_CONST, i64, i64, i64) +DEF_HELPER_FLAGS_2(muls, TCG_CALL_CONST, i64, i64, i64) +DEF_HELPER_FLAGS_2(divs, TCG_CALL_CONST, i64, i64, i64) +DEF_HELPER_FLAGS_1(sqrts, TCG_CALL_CONST, i64, i64) + +DEF_HELPER_FLAGS_2(addt, TCG_CALL_CONST, i64, i64, i64) +DEF_HELPER_FLAGS_2(subt, TCG_CALL_CONST, i64, i64, i64) +DEF_HELPER_FLAGS_2(mult, TCG_CALL_CONST, i64, i64, i64) +DEF_HELPER_FLAGS_2(divt, TCG_CALL_CONST, i64, i64, i64) +DEF_HELPER_FLAGS_1(sqrtt, TCG_CALL_CONST, i64, i64) + +DEF_HELPER_FLAGS_2(cmptun, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64) +DEF_HELPER_FLAGS_2(cmpteq, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64) +DEF_HELPER_FLAGS_2(cmptle, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64) +DEF_HELPER_FLAGS_2(cmptlt, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64) +DEF_HELPER_FLAGS_2(cmpgeq, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64) +DEF_HELPER_FLAGS_2(cmpgle, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64) +DEF_HELPER_FLAGS_2(cmpglt, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64) + +DEF_HELPER_FLAGS_1(cvtts, TCG_CALL_CONST, i64, i64) +DEF_HELPER_FLAGS_1(cvtst, TCG_CALL_CONST, i64, i64) +DEF_HELPER_FLAGS_1(cvtqs, TCG_CALL_CONST, i64, i64) +DEF_HELPER_FLAGS_1(cvtqt, TCG_CALL_CONST, i64, i64) +DEF_HELPER_FLAGS_1(cvtqf, TCG_CALL_CONST, i64, i64) +DEF_HELPER_FLAGS_1(cvtgf, TCG_CALL_CONST, i64, i64) +DEF_HELPER_FLAGS_1(cvtgq, TCG_CALL_CONST, i64, i64) +DEF_HELPER_FLAGS_1(cvtqg, TCG_CALL_CONST, i64, i64) + +DEF_HELPER_FLAGS_1(cvttq, TCG_CALL_CONST, i64, i64) +DEF_HELPER_FLAGS_1(cvttq_c, TCG_CALL_CONST, i64, i64) +DEF_HELPER_FLAGS_1(cvttq_svic, TCG_CALL_CONST, i64, i64) + +DEF_HELPER_FLAGS_1(setroundmode, TCG_CALL_CONST, void, i32) +DEF_HELPER_FLAGS_1(setflushzero, TCG_CALL_CONST, void, i32) +DEF_HELPER_FLAGS_0(fp_exc_clear, TCG_CALL_CONST, void) +DEF_HELPER_FLAGS_0(fp_exc_get, TCG_CALL_CONST | TCG_CALL_PURE, i32) +DEF_HELPER_2(fp_exc_raise, void, i32, i32) +DEF_HELPER_2(fp_exc_raise_s, void, i32, i32) + +DEF_HELPER_1(ieee_input, i64, i64) +DEF_HELPER_1(ieee_input_cmp, i64, i64) +DEF_HELPER_1(ieee_input_s, i64, i64) #if !defined (CONFIG_USER_ONLY) DEF_HELPER_0(hw_rei, void) diff -Nru qemu-kvm-0.12.5+noroms/target-alpha/op_helper.c qemu-kvm-0.14.1/target-alpha/op_helper.c --- qemu-kvm-0.12.5+noroms/target-alpha/op_helper.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-alpha/op_helper.c 2011-05-11 13:29:46.000000000 +0000 @@ -21,15 +21,11 @@ #include "host-utils.h" #include "softfloat.h" #include "helper.h" - -void helper_tb_flush (void) -{ - tb_flush(env); -} +#include "qemu-timer.h" /*****************************************************************************/ /* Exceptions processing helpers */ -void helper_excp (int excp, int error) +void QEMU_NORETURN helper_excp (int excp, int error) { env->exception_index = excp; env->error_code = error; @@ -38,81 +34,18 @@ uint64_t helper_load_pcc (void) { - /* XXX: TODO */ - return 0; + /* ??? This isn't a timer for which we have any rate info. */ + return (uint32_t)cpu_get_real_ticks(); } uint64_t helper_load_fpcr (void) { - uint64_t ret = 0; -#ifdef CONFIG_SOFTFLOAT - ret |= (uint64_t)env->fp_status.float_exception_flags << 52; - if (env->fp_status.float_exception_flags) - ret |= 1ULL << 63; - env->ipr[IPR_EXC_SUM] &= ~0x3E; - env->ipr[IPR_EXC_SUM] |= env->fp_status.float_exception_flags << 1; -#endif - switch (env->fp_status.float_rounding_mode) { - case float_round_nearest_even: - ret |= 2ULL << 58; - break; - case float_round_down: - ret |= 1ULL << 58; - break; - case float_round_up: - ret |= 3ULL << 58; - break; - case float_round_to_zero: - break; - } - return ret; + return cpu_alpha_load_fpcr (env); } void helper_store_fpcr (uint64_t val) { -#ifdef CONFIG_SOFTFLOAT - set_float_exception_flags((val >> 52) & 0x3F, &FP_STATUS); -#endif - switch ((val >> 58) & 3) { - case 0: - set_float_rounding_mode(float_round_to_zero, &FP_STATUS); - break; - case 1: - set_float_rounding_mode(float_round_down, &FP_STATUS); - break; - case 2: - set_float_rounding_mode(float_round_nearest_even, &FP_STATUS); - break; - case 3: - set_float_rounding_mode(float_round_up, &FP_STATUS); - break; - } -} - -static spinlock_t intr_cpu_lock = SPIN_LOCK_UNLOCKED; - -uint64_t helper_rs(void) -{ - uint64_t tmp; - - spin_lock(&intr_cpu_lock); - tmp = env->intr_flag; - env->intr_flag = 1; - spin_unlock(&intr_cpu_lock); - - return tmp; -} - -uint64_t helper_rc(void) -{ - uint64_t tmp; - - spin_lock(&intr_cpu_lock); - tmp = env->intr_flag; - env->intr_flag = 0; - spin_unlock(&intr_cpu_lock); - - return tmp; + cpu_alpha_store_fpcr (env, val); } uint64_t helper_addqv (uint64_t op1, uint64_t op2) @@ -120,7 +53,7 @@ uint64_t tmp = op1; op1 += op2; if (unlikely((tmp ^ op2 ^ (-1ULL)) & (tmp ^ op1) & (1ULL << 63))) { - helper_excp(EXCP_ARITH, EXCP_ARITH_OVERFLOW); + helper_excp(EXCP_ARITH, EXC_M_IOV); } return op1; } @@ -130,7 +63,7 @@ uint64_t tmp = op1; op1 = (uint32_t)(op1 + op2); if (unlikely((tmp ^ op2 ^ (-1UL)) & (tmp ^ op1) & (1UL << 31))) { - helper_excp(EXCP_ARITH, EXCP_ARITH_OVERFLOW); + helper_excp(EXCP_ARITH, EXC_M_IOV); } return op1; } @@ -140,7 +73,7 @@ uint64_t res; res = op1 - op2; if (unlikely((op1 ^ op2) & (res ^ op1) & (1ULL << 63))) { - helper_excp(EXCP_ARITH, EXCP_ARITH_OVERFLOW); + helper_excp(EXCP_ARITH, EXC_M_IOV); } return res; } @@ -150,7 +83,7 @@ uint32_t res; res = op1 - op2; if (unlikely((op1 ^ op2) & (res ^ op1) & (1UL << 31))) { - helper_excp(EXCP_ARITH, EXCP_ARITH_OVERFLOW); + helper_excp(EXCP_ARITH, EXC_M_IOV); } return res; } @@ -160,7 +93,7 @@ int64_t res = (int64_t)op1 * (int64_t)op2; if (unlikely((int32_t)res != res)) { - helper_excp(EXCP_ARITH, EXCP_ARITH_OVERFLOW); + helper_excp(EXCP_ARITH, EXC_M_IOV); } return (int64_t)((int32_t)res); } @@ -172,7 +105,7 @@ muls64(&tl, &th, op1, op2); /* If th != 0 && th != -1, then we had an overflow */ if (unlikely((th + 1) > 1)) { - helper_excp(EXCP_ARITH, EXCP_ARITH_OVERFLOW); + helper_excp(EXCP_ARITH, EXC_M_IOV); } return tl; } @@ -217,109 +150,324 @@ return op & ~mask; } -uint64_t helper_mskbl(uint64_t val, uint64_t mask) +uint64_t helper_zap(uint64_t val, uint64_t mask) { - return byte_zap(val, 0x01 << (mask & 7)); + return byte_zap(val, mask); } -uint64_t helper_insbl(uint64_t val, uint64_t mask) +uint64_t helper_zapnot(uint64_t val, uint64_t mask) { - val <<= (mask & 7) * 8; - return byte_zap(val, ~(0x01 << (mask & 7))); + return byte_zap(val, ~mask); } -uint64_t helper_mskwl(uint64_t val, uint64_t mask) +uint64_t helper_cmpbge (uint64_t op1, uint64_t op2) { - return byte_zap(val, 0x03 << (mask & 7)); + uint8_t opa, opb, res; + int i; + + res = 0; + for (i = 0; i < 8; i++) { + opa = op1 >> (i * 8); + opb = op2 >> (i * 8); + if (opa >= opb) + res |= 1 << i; + } + return res; } -uint64_t helper_inswl(uint64_t val, uint64_t mask) +uint64_t helper_minub8 (uint64_t op1, uint64_t op2) { - val <<= (mask & 7) * 8; - return byte_zap(val, ~(0x03 << (mask & 7))); + uint64_t res = 0; + uint8_t opa, opb, opr; + int i; + + for (i = 0; i < 8; ++i) { + opa = op1 >> (i * 8); + opb = op2 >> (i * 8); + opr = opa < opb ? opa : opb; + res |= (uint64_t)opr << (i * 8); + } + return res; } -uint64_t helper_mskll(uint64_t val, uint64_t mask) +uint64_t helper_minsb8 (uint64_t op1, uint64_t op2) { - return byte_zap(val, 0x0F << (mask & 7)); + uint64_t res = 0; + int8_t opa, opb; + uint8_t opr; + int i; + + for (i = 0; i < 8; ++i) { + opa = op1 >> (i * 8); + opb = op2 >> (i * 8); + opr = opa < opb ? opa : opb; + res |= (uint64_t)opr << (i * 8); + } + return res; } -uint64_t helper_insll(uint64_t val, uint64_t mask) +uint64_t helper_minuw4 (uint64_t op1, uint64_t op2) { - val <<= (mask & 7) * 8; - return byte_zap(val, ~(0x0F << (mask & 7))); + uint64_t res = 0; + uint16_t opa, opb, opr; + int i; + + for (i = 0; i < 4; ++i) { + opa = op1 >> (i * 16); + opb = op2 >> (i * 16); + opr = opa < opb ? opa : opb; + res |= (uint64_t)opr << (i * 16); + } + return res; } -uint64_t helper_zap(uint64_t val, uint64_t mask) +uint64_t helper_minsw4 (uint64_t op1, uint64_t op2) { - return byte_zap(val, mask); + uint64_t res = 0; + int16_t opa, opb; + uint16_t opr; + int i; + + for (i = 0; i < 4; ++i) { + opa = op1 >> (i * 16); + opb = op2 >> (i * 16); + opr = opa < opb ? opa : opb; + res |= (uint64_t)opr << (i * 16); + } + return res; } -uint64_t helper_zapnot(uint64_t val, uint64_t mask) +uint64_t helper_maxub8 (uint64_t op1, uint64_t op2) { - return byte_zap(val, ~mask); + uint64_t res = 0; + uint8_t opa, opb, opr; + int i; + + for (i = 0; i < 8; ++i) { + opa = op1 >> (i * 8); + opb = op2 >> (i * 8); + opr = opa > opb ? opa : opb; + res |= (uint64_t)opr << (i * 8); + } + return res; } -uint64_t helper_mskql(uint64_t val, uint64_t mask) +uint64_t helper_maxsb8 (uint64_t op1, uint64_t op2) { - return byte_zap(val, 0xFF << (mask & 7)); + uint64_t res = 0; + int8_t opa, opb; + uint8_t opr; + int i; + + for (i = 0; i < 8; ++i) { + opa = op1 >> (i * 8); + opb = op2 >> (i * 8); + opr = opa > opb ? opa : opb; + res |= (uint64_t)opr << (i * 8); + } + return res; } -uint64_t helper_insql(uint64_t val, uint64_t mask) +uint64_t helper_maxuw4 (uint64_t op1, uint64_t op2) { - val <<= (mask & 7) * 8; - return byte_zap(val, ~(0xFF << (mask & 7))); + uint64_t res = 0; + uint16_t opa, opb, opr; + int i; + + for (i = 0; i < 4; ++i) { + opa = op1 >> (i * 16); + opb = op2 >> (i * 16); + opr = opa > opb ? opa : opb; + res |= (uint64_t)opr << (i * 16); + } + return res; } -uint64_t helper_mskwh(uint64_t val, uint64_t mask) +uint64_t helper_maxsw4 (uint64_t op1, uint64_t op2) { - return byte_zap(val, (0x03 << (mask & 7)) >> 8); + uint64_t res = 0; + int16_t opa, opb; + uint16_t opr; + int i; + + for (i = 0; i < 4; ++i) { + opa = op1 >> (i * 16); + opb = op2 >> (i * 16); + opr = opa > opb ? opa : opb; + res |= (uint64_t)opr << (i * 16); + } + return res; } -uint64_t helper_inswh(uint64_t val, uint64_t mask) +uint64_t helper_perr (uint64_t op1, uint64_t op2) { - val >>= 64 - ((mask & 7) * 8); - return byte_zap(val, ~((0x03 << (mask & 7)) >> 8)); + uint64_t res = 0; + uint8_t opa, opb, opr; + int i; + + for (i = 0; i < 8; ++i) { + opa = op1 >> (i * 8); + opb = op2 >> (i * 8); + if (opa >= opb) + opr = opa - opb; + else + opr = opb - opa; + res += opr; + } + return res; } -uint64_t helper_msklh(uint64_t val, uint64_t mask) +uint64_t helper_pklb (uint64_t op1) { - return byte_zap(val, (0x0F << (mask & 7)) >> 8); + return (op1 & 0xff) | ((op1 >> 24) & 0xff00); } -uint64_t helper_inslh(uint64_t val, uint64_t mask) +uint64_t helper_pkwb (uint64_t op1) { - val >>= 64 - ((mask & 7) * 8); - return byte_zap(val, ~((0x0F << (mask & 7)) >> 8)); + return ((op1 & 0xff) + | ((op1 >> 8) & 0xff00) + | ((op1 >> 16) & 0xff0000) + | ((op1 >> 24) & 0xff000000)); } -uint64_t helper_mskqh(uint64_t val, uint64_t mask) +uint64_t helper_unpkbl (uint64_t op1) { - return byte_zap(val, (0xFF << (mask & 7)) >> 8); + return (op1 & 0xff) | ((op1 & 0xff00) << 24); } -uint64_t helper_insqh(uint64_t val, uint64_t mask) +uint64_t helper_unpkbw (uint64_t op1) { - val >>= 64 - ((mask & 7) * 8); - return byte_zap(val, ~((0xFF << (mask & 7)) >> 8)); + return ((op1 & 0xff) + | ((op1 & 0xff00) << 8) + | ((op1 & 0xff0000) << 16) + | ((op1 & 0xff000000) << 24)); } -uint64_t helper_cmpbge (uint64_t op1, uint64_t op2) +/* Floating point helpers */ + +void helper_setroundmode (uint32_t val) { - uint8_t opa, opb, res; - int i; + set_float_rounding_mode(val, &FP_STATUS); +} - res = 0; - for (i = 0; i < 8; i++) { - opa = op1 >> (i * 8); - opb = op2 >> (i * 8); - if (opa >= opb) - res |= 1 << i; +void helper_setflushzero (uint32_t val) +{ + set_flush_to_zero(val, &FP_STATUS); +} + +void helper_fp_exc_clear (void) +{ + set_float_exception_flags(0, &FP_STATUS); +} + +uint32_t helper_fp_exc_get (void) +{ + return get_float_exception_flags(&FP_STATUS); +} + +/* Raise exceptions for ieee fp insns without software completion. + In that case there are no exceptions that don't trap; the mask + doesn't apply. */ +void helper_fp_exc_raise(uint32_t exc, uint32_t regno) +{ + if (exc) { + uint32_t hw_exc = 0; + + env->ipr[IPR_EXC_MASK] |= 1ull << regno; + + if (exc & float_flag_invalid) { + hw_exc |= EXC_M_INV; + } + if (exc & float_flag_divbyzero) { + hw_exc |= EXC_M_DZE; + } + if (exc & float_flag_overflow) { + hw_exc |= EXC_M_FOV; + } + if (exc & float_flag_underflow) { + hw_exc |= EXC_M_UNF; + } + if (exc & float_flag_inexact) { + hw_exc |= EXC_M_INE; + } + helper_excp(EXCP_ARITH, hw_exc); } - return res; } -/* Floating point helpers */ +/* Raise exceptions for ieee fp insns with software completion. */ +void helper_fp_exc_raise_s(uint32_t exc, uint32_t regno) +{ + if (exc) { + env->fpcr_exc_status |= exc; + + exc &= ~env->fpcr_exc_mask; + if (exc) { + helper_fp_exc_raise(exc, regno); + } + } +} + +/* Input remapping without software completion. Handle denormal-map-to-zero + and trap for all other non-finite numbers. */ +uint64_t helper_ieee_input(uint64_t val) +{ + uint32_t exp = (uint32_t)(val >> 52) & 0x7ff; + uint64_t frac = val & 0xfffffffffffffull; + + if (exp == 0) { + if (frac != 0) { + /* If DNZ is set flush denormals to zero on input. */ + if (env->fpcr_dnz) { + val &= 1ull << 63; + } else { + helper_excp(EXCP_ARITH, EXC_M_UNF); + } + } + } else if (exp == 0x7ff) { + /* Infinity or NaN. */ + /* ??? I'm not sure these exception bit flags are correct. I do + know that the Linux kernel, at least, doesn't rely on them and + just emulates the insn to figure out what exception to use. */ + helper_excp(EXCP_ARITH, frac ? EXC_M_INV : EXC_M_FOV); + } + return val; +} + +/* Similar, but does not trap for infinities. Used for comparisons. */ +uint64_t helper_ieee_input_cmp(uint64_t val) +{ + uint32_t exp = (uint32_t)(val >> 52) & 0x7ff; + uint64_t frac = val & 0xfffffffffffffull; + + if (exp == 0) { + if (frac != 0) { + /* If DNZ is set flush denormals to zero on input. */ + if (env->fpcr_dnz) { + val &= 1ull << 63; + } else { + helper_excp(EXCP_ARITH, EXC_M_UNF); + } + } + } else if (exp == 0x7ff && frac) { + /* NaN. */ + helper_excp(EXCP_ARITH, EXC_M_INV); + } + return val; +} + +/* Input remapping with software completion enabled. All we have to do + is handle denormal-map-to-zero; all other inputs get exceptions as + needed from the actual operation. */ +uint64_t helper_ieee_input_s(uint64_t val) +{ + if (env->fpcr_dnz) { + uint32_t exp = (uint32_t)(val >> 52) & 0x7ff; + if (exp == 0) { + val &= 1ull << 63; + } + } + return val; +} /* F floating (VAX) */ static inline uint64_t float32_to_f(float32 fa) @@ -398,6 +546,9 @@ return r; } +/* ??? Emulating VAX arithmetic with IEEE arithmetic is wrong. We should + either implement VAX arithmetic properly or just signal invalid opcode. */ + uint64_t helper_addf (uint64_t a, uint64_t b) { float32 fa, fb, fr; @@ -576,37 +727,57 @@ /* S floating (single) */ + +/* Taken from linux/arch/alpha/kernel/traps.c, s_mem_to_reg. */ +static inline uint64_t float32_to_s_int(uint32_t fi) +{ + uint32_t frac = fi & 0x7fffff; + uint32_t sign = fi >> 31; + uint32_t exp_msb = (fi >> 30) & 1; + uint32_t exp_low = (fi >> 23) & 0x7f; + uint32_t exp; + + exp = (exp_msb << 10) | exp_low; + if (exp_msb) { + if (exp_low == 0x7f) + exp = 0x7ff; + } else { + if (exp_low != 0x00) + exp |= 0x380; + } + + return (((uint64_t)sign << 63) + | ((uint64_t)exp << 52) + | ((uint64_t)frac << 29)); +} + static inline uint64_t float32_to_s(float32 fa) { CPU_FloatU a; - uint64_t r; - a.f = fa; + return float32_to_s_int(a.l); +} - r = (((uint64_t)(a.l & 0xc0000000)) << 32) | (((uint64_t)(a.l & 0x3fffffff)) << 29); - if (((a.l & 0x7f800000) != 0x7f800000) && (!(a.l & 0x40000000))) - r |= 0x7ll << 59; - return r; +static inline uint32_t s_to_float32_int(uint64_t a) +{ + return ((a >> 32) & 0xc0000000) | ((a >> 29) & 0x3fffffff); } static inline float32 s_to_float32(uint64_t a) { CPU_FloatU r; - r.l = ((a >> 32) & 0xc0000000) | ((a >> 29) & 0x3fffffff); + r.l = s_to_float32_int(a); return r.f; } uint32_t helper_s_to_memory (uint64_t a) { - /* Memory format is the same as float32 */ - float32 fa = s_to_float32(a); - return *(uint32_t*)(&fa); + return s_to_float32_int(a); } uint64_t helper_memory_to_s (uint32_t a) { - /* Memory format is the same as float32 */ - return float32_to_s(*(float32*)(&a)); + return float32_to_s_int(a); } uint64_t helper_adds (uint64_t a, uint64_t b) @@ -725,24 +896,6 @@ return float64_to_t(fr); } - -/* Sign copy */ -uint64_t helper_cpys(uint64_t a, uint64_t b) -{ - return (a & 0x8000000000000000ULL) | (b & ~0x8000000000000000ULL); -} - -uint64_t helper_cpysn(uint64_t a, uint64_t b) -{ - return ((~a) & 0x8000000000000000ULL) | (b & ~0x8000000000000000ULL); -} - -uint64_t helper_cpyse(uint64_t a, uint64_t b) -{ - return (a & 0xFFF0000000000000ULL) | (b & ~0xFFF0000000000000ULL); -} - - /* Comparisons */ uint64_t helper_cmptun (uint64_t a, uint64_t b) { @@ -751,7 +904,7 @@ fa = t_to_float64(a); fb = t_to_float64(b); - if (float64_is_nan(fa) || float64_is_nan(fb)) + if (float64_is_quiet_nan(fa) || float64_is_quiet_nan(fb)) return 0x4000000000000000ULL; else return 0; @@ -835,37 +988,6 @@ return 0; } -uint64_t helper_cmpfeq (uint64_t a) -{ - return !(a & 0x7FFFFFFFFFFFFFFFULL); -} - -uint64_t helper_cmpfne (uint64_t a) -{ - return (a & 0x7FFFFFFFFFFFFFFFULL); -} - -uint64_t helper_cmpflt (uint64_t a) -{ - return (a & 0x8000000000000000ULL) && (a & 0x7FFFFFFFFFFFFFFFULL); -} - -uint64_t helper_cmpfle (uint64_t a) -{ - return (a & 0x8000000000000000ULL) || !(a & 0x7FFFFFFFFFFFFFFFULL); -} - -uint64_t helper_cmpfgt (uint64_t a) -{ - return !(a & 0x8000000000000000ULL) && (a & 0x7FFFFFFFFFFFFFFFULL); -} - -uint64_t helper_cmpfge (uint64_t a) -{ - return !(a & 0x8000000000000000ULL) || !(a & 0x7FFFFFFFFFFFFFFFULL); -} - - /* Floating point format conversion */ uint64_t helper_cvtts (uint64_t a) { @@ -893,10 +1015,107 @@ return float32_to_s(fr); } -uint64_t helper_cvttq (uint64_t a) +/* Implement float64 to uint64 conversion without saturation -- we must + supply the truncated result. This behaviour is used by the compiler + to get unsigned conversion for free with the same instruction. + + The VI flag is set when overflow or inexact exceptions should be raised. */ + +static inline uint64_t helper_cvttq_internal(uint64_t a, int roundmode, int VI) { - float64 fa = t_to_float64(a); - return float64_to_int64_round_to_zero(fa, &FP_STATUS); + uint64_t frac, ret = 0; + uint32_t exp, sign, exc = 0; + int shift; + + sign = (a >> 63); + exp = (uint32_t)(a >> 52) & 0x7ff; + frac = a & 0xfffffffffffffull; + + if (exp == 0) { + if (unlikely(frac != 0)) { + goto do_underflow; + } + } else if (exp == 0x7ff) { + exc = (frac ? float_flag_invalid : VI ? float_flag_overflow : 0); + } else { + /* Restore implicit bit. */ + frac |= 0x10000000000000ull; + + shift = exp - 1023 - 52; + if (shift >= 0) { + /* In this case the number is so large that we must shift + the fraction left. There is no rounding to do. */ + if (shift < 63) { + ret = frac << shift; + if (VI && (ret >> shift) != frac) { + exc = float_flag_overflow; + } + } + } else { + uint64_t round; + + /* In this case the number is smaller than the fraction as + represented by the 52 bit number. Here we must think + about rounding the result. Handle this by shifting the + fractional part of the number into the high bits of ROUND. + This will let us efficiently handle round-to-nearest. */ + shift = -shift; + if (shift < 63) { + ret = frac >> shift; + round = frac << (64 - shift); + } else { + /* The exponent is so small we shift out everything. + Leave a sticky bit for proper rounding below. */ + do_underflow: + round = 1; + } + + if (round) { + exc = (VI ? float_flag_inexact : 0); + switch (roundmode) { + case float_round_nearest_even: + if (round == (1ull << 63)) { + /* Fraction is exactly 0.5; round to even. */ + ret += (ret & 1); + } else if (round > (1ull << 63)) { + ret += 1; + } + break; + case float_round_to_zero: + break; + case float_round_up: + ret += 1 - sign; + break; + case float_round_down: + ret += sign; + break; + } + } + } + if (sign) { + ret = -ret; + } + } + if (unlikely(exc)) { + float_raise(exc, &FP_STATUS); + } + + return ret; +} + +uint64_t helper_cvttq(uint64_t a) +{ + return helper_cvttq_internal(a, FP_STATUS.float_rounding_mode, 1); +} + +uint64_t helper_cvttq_c(uint64_t a) +{ + return helper_cvttq_internal(a, float_round_to_zero, 0); +} + +uint64_t helper_cvttq_svic(uint64_t a) +{ + return helper_cvttq_internal(a, float_round_to_zero, 1); } uint64_t helper_cvtqt (uint64_t a) @@ -934,48 +1153,14 @@ return float64_to_g(fr); } -uint64_t helper_cvtlq (uint64_t a) -{ - return (int64_t)((int32_t)((a >> 32) | ((a >> 29) & 0x3FFFFFFF))); -} - -static inline uint64_t __helper_cvtql(uint64_t a, int s, int v) -{ - uint64_t r; - - r = ((uint64_t)(a & 0xC0000000)) << 32; - r |= ((uint64_t)(a & 0x7FFFFFFF)) << 29; - - if (v && (int64_t)((int32_t)r) != (int64_t)r) { - helper_excp(EXCP_ARITH, EXCP_ARITH_OVERFLOW); - } - if (s) { - /* TODO */ - } - return r; -} - -uint64_t helper_cvtql (uint64_t a) -{ - return __helper_cvtql(a, 0, 0); -} - -uint64_t helper_cvtqlv (uint64_t a) -{ - return __helper_cvtql(a, 0, 1); -} - -uint64_t helper_cvtqlsv (uint64_t a) -{ - return __helper_cvtql(a, 1, 1); -} - /* PALcode support special instructions */ #if !defined (CONFIG_USER_ONLY) void helper_hw_rei (void) { env->pc = env->ipr[IPR_EXC_ADDR] & ~3; env->ipr[IPR_EXC_ADDR] = env->ipr[IPR_EXC_ADDR] & 1; + env->intr_flag = 0; + env->lock_addr = -1; /* XXX: re-enable interrupts and memory mapping */ } @@ -983,6 +1168,8 @@ { env->pc = a & ~3; env->ipr[IPR_EXC_ADDR] = a & 1; + env->intr_flag = 0; + env->lock_addr = -1; /* XXX: re-enable interrupts and memory mapping */ } diff -Nru qemu-kvm-0.12.5+noroms/target-alpha/translate.c qemu-kvm-0.14.1/target-alpha/translate.c --- qemu-kvm-0.12.5+noroms/target-alpha/translate.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-alpha/translate.c 2011-05-11 13:29:46.000000000 +0000 @@ -32,34 +32,66 @@ #define GEN_HELPER 1 #include "helper.h" -/* #define DO_SINGLE_STEP */ -#define ALPHA_DEBUG_DISAS -/* #define DO_TB_FLUSH */ - +#undef ALPHA_DEBUG_DISAS +#define CONFIG_SOFTFLOAT_INLINE #ifdef ALPHA_DEBUG_DISAS -# define LOG_DISAS(...) qemu_log(__VA_ARGS__) +# define LOG_DISAS(...) qemu_log_mask(CPU_LOG_TB_IN_ASM, ## __VA_ARGS__) #else # define LOG_DISAS(...) do { } while (0) #endif typedef struct DisasContext DisasContext; struct DisasContext { + struct TranslationBlock *tb; + CPUAlphaState *env; uint64_t pc; int mem_idx; #if !defined (CONFIG_USER_ONLY) int pal_mode; #endif - CPUAlphaState *env; uint32_t amask; + + /* Current rounding mode for this TB. */ + int tb_rm; + /* Current flush-to-zero setting for this TB. */ + int tb_ftz; }; +/* Return values from translate_one, indicating the state of the TB. + Note that zero indicates that we are not exiting the TB. */ + +typedef enum { + NO_EXIT, + + /* We have emitted one or more goto_tb. No fixup required. */ + EXIT_GOTO_TB, + + /* We are not using a goto_tb (for whatever reason), but have updated + the PC (for whatever reason), so there's no need to do it again on + exiting the TB. */ + EXIT_PC_UPDATED, + + /* We are exiting the TB, but have neither emitted a goto_tb, nor + updated the PC for the next instruction to be executed. */ + EXIT_PC_STALE, + + /* We are ending the TB with a noreturn function call, e.g. longjmp. + No following code will be executed. */ + EXIT_NORETURN, +} ExitStatus; + /* global register indexes */ static TCGv_ptr cpu_env; static TCGv cpu_ir[31]; static TCGv cpu_fir[31]; static TCGv cpu_pc; -static TCGv cpu_lock; +static TCGv cpu_lock_addr; +static TCGv cpu_lock_st_addr; +static TCGv cpu_lock_value; +#ifdef CONFIG_USER_ONLY +static TCGv cpu_uniq; +#endif /* register names */ static char cpu_reg_names[10*4+21*5 + 10*5+21*6]; @@ -93,8 +125,20 @@ cpu_pc = tcg_global_mem_new_i64(TCG_AREG0, offsetof(CPUState, pc), "pc"); - cpu_lock = tcg_global_mem_new_i64(TCG_AREG0, - offsetof(CPUState, lock), "lock"); + cpu_lock_addr = tcg_global_mem_new_i64(TCG_AREG0, + offsetof(CPUState, lock_addr), + "lock_addr"); + cpu_lock_st_addr = tcg_global_mem_new_i64(TCG_AREG0, + offsetof(CPUState, lock_st_addr), + "lock_st_addr"); + cpu_lock_value = tcg_global_mem_new_i64(TCG_AREG0, + offsetof(CPUState, lock_value), + "lock_value"); + +#ifdef CONFIG_USER_ONLY + cpu_uniq = tcg_global_mem_new_i64(TCG_AREG0, + offsetof(CPUState, unique), "uniq"); +#endif /* register helpers */ #define GEN_HELPER 2 @@ -103,7 +147,7 @@ done_init = 1; } -static inline void gen_excp(DisasContext *ctx, int exception, int error_code) +static ExitStatus gen_excp(DisasContext *ctx, int exception, int error_code) { TCGv_i32 tmp1, tmp2; @@ -113,11 +157,13 @@ gen_helper_excp(tmp1, tmp2); tcg_temp_free_i32(tmp2); tcg_temp_free_i32(tmp1); + + return EXIT_NORETURN; } -static inline void gen_invalid(DisasContext *ctx) +static inline ExitStatus gen_invalid(DisasContext *ctx) { - gen_excp(ctx, EXCP_OPCDEC, 0); + return gen_excp(ctx, EXCP_OPCDEC, 0); } static inline void gen_qemu_ldf(TCGv t0, TCGv t1, int flags) @@ -152,14 +198,16 @@ static inline void gen_qemu_ldl_l(TCGv t0, TCGv t1, int flags) { - tcg_gen_mov_i64(cpu_lock, t1); tcg_gen_qemu_ld32s(t0, t1, flags); + tcg_gen_mov_i64(cpu_lock_addr, t1); + tcg_gen_mov_i64(cpu_lock_value, t0); } static inline void gen_qemu_ldq_l(TCGv t0, TCGv t1, int flags) { - tcg_gen_mov_i64(cpu_lock, t1); tcg_gen_qemu_ld64(t0, t1, flags); + tcg_gen_mov_i64(cpu_lock_addr, t1); + tcg_gen_mov_i64(cpu_lock_value, t0); } static inline void gen_load_mem(DisasContext *ctx, @@ -168,25 +216,31 @@ int ra, int rb, int32_t disp16, int fp, int clear) { - TCGv addr; + TCGv addr, va; - if (unlikely(ra == 31)) + /* LDQ_U with ra $31 is UNOP. Other various loads are forms of + prefetches, which we can treat as nops. No worries about + missed exceptions here. */ + if (unlikely(ra == 31)) { return; + } addr = tcg_temp_new(); if (rb != 31) { tcg_gen_addi_i64(addr, cpu_ir[rb], disp16); - if (clear) + if (clear) { tcg_gen_andi_i64(addr, addr, ~0x7); + } } else { - if (clear) + if (clear) { disp16 &= ~0x7; + } tcg_gen_movi_i64(addr, disp16); } - if (fp) - tcg_gen_qemu_load(cpu_fir[ra], addr, ctx->mem_idx); - else - tcg_gen_qemu_load(cpu_ir[ra], addr, ctx->mem_idx); + + va = (fp ? cpu_fir[ra] : cpu_ir[ra]); + tcg_gen_qemu_load(va, addr, ctx->mem_idx); + tcg_temp_free(addr); } @@ -220,152 +274,254 @@ tcg_temp_free_i32(tmp32); } -static inline void gen_qemu_stl_c(TCGv t0, TCGv t1, int flags) -{ - int l1, l2; - - l1 = gen_new_label(); - l2 = gen_new_label(); - tcg_gen_brcond_i64(TCG_COND_NE, cpu_lock, t1, l1); - tcg_gen_qemu_st32(t0, t1, flags); - tcg_gen_movi_i64(t0, 1); - tcg_gen_br(l2); - gen_set_label(l1); - tcg_gen_movi_i64(t0, 0); - gen_set_label(l2); - tcg_gen_movi_i64(cpu_lock, -1); -} - -static inline void gen_qemu_stq_c(TCGv t0, TCGv t1, int flags) -{ - int l1, l2; - - l1 = gen_new_label(); - l2 = gen_new_label(); - tcg_gen_brcond_i64(TCG_COND_NE, cpu_lock, t1, l1); - tcg_gen_qemu_st64(t0, t1, flags); - tcg_gen_movi_i64(t0, 1); - tcg_gen_br(l2); - gen_set_label(l1); - tcg_gen_movi_i64(t0, 0); - gen_set_label(l2); - tcg_gen_movi_i64(cpu_lock, -1); -} - static inline void gen_store_mem(DisasContext *ctx, void (*tcg_gen_qemu_store)(TCGv t0, TCGv t1, int flags), int ra, int rb, int32_t disp16, int fp, - int clear, int local) + int clear) { - TCGv addr; - if (local) - addr = tcg_temp_local_new(); - else - addr = tcg_temp_new(); + TCGv addr, va; + + addr = tcg_temp_new(); if (rb != 31) { tcg_gen_addi_i64(addr, cpu_ir[rb], disp16); - if (clear) + if (clear) { tcg_gen_andi_i64(addr, addr, ~0x7); + } } else { - if (clear) + if (clear) { disp16 &= ~0x7; + } tcg_gen_movi_i64(addr, disp16); } - if (ra != 31) { - if (fp) - tcg_gen_qemu_store(cpu_fir[ra], addr, ctx->mem_idx); - else - tcg_gen_qemu_store(cpu_ir[ra], addr, ctx->mem_idx); - } else { - TCGv zero; - if (local) - zero = tcg_const_local_i64(0); - else - zero = tcg_const_i64(0); - tcg_gen_qemu_store(zero, addr, ctx->mem_idx); - tcg_temp_free(zero); + + if (ra == 31) { + va = tcg_const_i64(0); + } else { + va = (fp ? cpu_fir[ra] : cpu_ir[ra]); } + tcg_gen_qemu_store(va, addr, ctx->mem_idx); + tcg_temp_free(addr); + if (ra == 31) { + tcg_temp_free(va); + } } -static inline void gen_bcond(DisasContext *ctx, TCGCond cond, int ra, - int32_t disp, int mask) +static ExitStatus gen_store_conditional(DisasContext *ctx, int ra, int rb, + int32_t disp16, int quad) { - int l1, l2; + TCGv addr; - l1 = gen_new_label(); - l2 = gen_new_label(); - if (likely(ra != 31)) { - if (mask) { - TCGv tmp = tcg_temp_new(); - tcg_gen_andi_i64(tmp, cpu_ir[ra], 1); - tcg_gen_brcondi_i64(cond, tmp, 0, l1); - tcg_temp_free(tmp); - } else - tcg_gen_brcondi_i64(cond, cpu_ir[ra], 0, l1); + if (ra == 31) { + /* ??? Don't bother storing anything. The user can't tell + the difference, since the zero register always reads zero. */ + return NO_EXIT; + } + +#if defined(CONFIG_USER_ONLY) + addr = cpu_lock_st_addr; +#else + addr = tcg_local_new(); +#endif + + if (rb != 31) { + tcg_gen_addi_i64(addr, cpu_ir[rb], disp16); } else { - /* Very uncommon case - Do not bother to optimize. */ - TCGv tmp = tcg_const_i64(0); - tcg_gen_brcondi_i64(cond, tmp, 0, l1); - tcg_temp_free(tmp); + tcg_gen_movi_i64(addr, disp16); } - tcg_gen_movi_i64(cpu_pc, ctx->pc); - tcg_gen_br(l2); - gen_set_label(l1); - tcg_gen_movi_i64(cpu_pc, ctx->pc + (int64_t)(disp << 2)); - gen_set_label(l2); + +#if defined(CONFIG_USER_ONLY) + /* ??? This is handled via a complicated version of compare-and-swap + in the cpu_loop. Hopefully one day we'll have a real CAS opcode + in TCG so that this isn't necessary. */ + return gen_excp(ctx, quad ? EXCP_STQ_C : EXCP_STL_C, ra); +#else + /* ??? In system mode we are never multi-threaded, so CAS can be + implemented via a non-atomic load-compare-store sequence. */ + { + int lab_fail, lab_done; + TCGv val; + + lab_fail = gen_new_label(); + lab_done = gen_new_label(); + tcg_gen_brcond(TCG_COND_NE, addr, cpu_lock_addr, lab_fail); + + val = tcg_temp_new(); + if (quad) { + tcg_gen_qemu_ld64(val, addr, ctx->mem_idx); + } else { + tcg_gen_qemu_ld32s(val, addr, ctx->mem_idx); + } + tcg_gen_brcond(TCG_COND_NE, val, cpu_lock_value, lab_fail); + + if (quad) { + tcg_gen_qemu_st64(cpu_ir[ra], addr, ctx->mem_idx); + } else { + tcg_gen_qemu_st32(cpu_ir[ra], addr, ctx->mem_idx); + } + tcg_gen_movi_i64(cpu_ir[ra], 1); + tcg_gen_br(lab_done); + + gen_set_label(lab_fail); + tcg_gen_movi_i64(cpu_ir[ra], 0); + + gen_set_label(lab_done); + tcg_gen_movi_i64(cpu_lock_addr, -1); + + tcg_temp_free(addr); + return NO_EXIT; + } +#endif +} + +static int use_goto_tb(DisasContext *ctx, uint64_t dest) +{ + /* Check for the dest on the same page as the start of the TB. We + also want to suppress goto_tb in the case of single-steping and IO. */ + return (((ctx->tb->pc ^ dest) & TARGET_PAGE_MASK) == 0 + && !ctx->env->singlestep_enabled + && !(ctx->tb->cflags & CF_LAST_IO)); } -static inline void gen_fbcond(DisasContext *ctx, int opc, int ra, - int32_t disp16) +static ExitStatus gen_bdirect(DisasContext *ctx, int ra, int32_t disp) { - int l1, l2; - TCGv tmp; - TCGv src; + uint64_t dest = ctx->pc + (disp << 2); - l1 = gen_new_label(); - l2 = gen_new_label(); if (ra != 31) { - tmp = tcg_temp_new(); - src = cpu_fir[ra]; - } else { - tmp = tcg_const_i64(0); - src = tmp; + tcg_gen_movi_i64(cpu_ir[ra], ctx->pc); } - switch (opc) { - case 0x31: /* FBEQ */ - gen_helper_cmpfeq(tmp, src); - break; - case 0x32: /* FBLT */ - gen_helper_cmpflt(tmp, src); - break; - case 0x33: /* FBLE */ - gen_helper_cmpfle(tmp, src); - break; - case 0x35: /* FBNE */ - gen_helper_cmpfne(tmp, src); + + /* Notice branch-to-next; used to initialize RA with the PC. */ + if (disp == 0) { + return 0; + } else if (use_goto_tb(ctx, dest)) { + tcg_gen_goto_tb(0); + tcg_gen_movi_i64(cpu_pc, dest); + tcg_gen_exit_tb((long)ctx->tb); + return EXIT_GOTO_TB; + } else { + tcg_gen_movi_i64(cpu_pc, dest); + return EXIT_PC_UPDATED; + } +} + +static ExitStatus gen_bcond_internal(DisasContext *ctx, TCGCond cond, + TCGv cmp, int32_t disp) +{ + uint64_t dest = ctx->pc + (disp << 2); + int lab_true = gen_new_label(); + + if (use_goto_tb(ctx, dest)) { + tcg_gen_brcondi_i64(cond, cmp, 0, lab_true); + + tcg_gen_goto_tb(0); + tcg_gen_movi_i64(cpu_pc, ctx->pc); + tcg_gen_exit_tb((long)ctx->tb); + + gen_set_label(lab_true); + tcg_gen_goto_tb(1); + tcg_gen_movi_i64(cpu_pc, dest); + tcg_gen_exit_tb((long)ctx->tb + 1); + + return EXIT_GOTO_TB; + } else { + int lab_over = gen_new_label(); + + /* ??? Consider using either + movi pc, next + addi tmp, pc, disp + movcond pc, cond, 0, tmp, pc + or + setcond tmp, cond, 0 + movi pc, next + neg tmp, tmp + andi tmp, tmp, disp + add pc, pc, tmp + The current diamond subgraph surely isn't efficient. */ + + tcg_gen_brcondi_i64(cond, cmp, 0, lab_true); + tcg_gen_movi_i64(cpu_pc, ctx->pc); + tcg_gen_br(lab_over); + gen_set_label(lab_true); + tcg_gen_movi_i64(cpu_pc, dest); + gen_set_label(lab_over); + + return EXIT_PC_UPDATED; + } +} + +static ExitStatus gen_bcond(DisasContext *ctx, TCGCond cond, int ra, + int32_t disp, int mask) +{ + TCGv cmp_tmp; + + if (unlikely(ra == 31)) { + cmp_tmp = tcg_const_i64(0); + } else { + cmp_tmp = tcg_temp_new(); + if (mask) { + tcg_gen_andi_i64(cmp_tmp, cpu_ir[ra], 1); + } else { + tcg_gen_mov_i64(cmp_tmp, cpu_ir[ra]); + } + } + + return gen_bcond_internal(ctx, cond, cmp_tmp, disp); +} + +/* Fold -0.0 for comparison with COND. */ + +static void gen_fold_mzero(TCGCond cond, TCGv dest, TCGv src) +{ + uint64_t mzero = 1ull << 63; + + switch (cond) { + case TCG_COND_LE: + case TCG_COND_GT: + /* For <= or >, the -0.0 value directly compares the way we want. */ + tcg_gen_mov_i64(dest, src); break; - case 0x36: /* FBGE */ - gen_helper_cmpfge(tmp, src); + + case TCG_COND_EQ: + case TCG_COND_NE: + /* For == or !=, we can simply mask off the sign bit and compare. */ + tcg_gen_andi_i64(dest, src, mzero - 1); break; - case 0x37: /* FBGT */ - gen_helper_cmpfgt(tmp, src); + + case TCG_COND_GE: + case TCG_COND_LT: + /* For >= or <, map -0.0 to +0.0 via comparison and mask. */ + tcg_gen_setcondi_i64(TCG_COND_NE, dest, src, mzero); + tcg_gen_neg_i64(dest, dest); + tcg_gen_and_i64(dest, dest, src); break; + default: abort(); } - tcg_gen_brcondi_i64(TCG_COND_NE, tmp, 0, l1); - tcg_gen_movi_i64(cpu_pc, ctx->pc); - tcg_gen_br(l2); - gen_set_label(l1); - tcg_gen_movi_i64(cpu_pc, ctx->pc + (int64_t)(disp16 << 2)); - gen_set_label(l2); } -static inline void gen_cmov(TCGCond inv_cond, int ra, int rb, int rc, - int islit, uint8_t lit, int mask) +static ExitStatus gen_fbcond(DisasContext *ctx, TCGCond cond, int ra, + int32_t disp) { + TCGv cmp_tmp; + + if (unlikely(ra == 31)) { + /* Very uncommon case, but easier to optimize it to an integer + comparison than continuing with the floating point comparison. */ + return gen_bcond(ctx, cond, ra, disp, 0); + } + + cmp_tmp = tcg_temp_new(); + gen_fold_mzero(cond, cmp_tmp, cpu_fir[ra]); + return gen_bcond_internal(ctx, cond, cmp_tmp, disp); +} + +static void gen_cmov(TCGCond cond, int ra, int rb, int rc, + int islit, uint8_t lit, int mask) +{ + TCGCond inv_cond = tcg_invert_cond(cond); int l1; if (unlikely(rc == 31)) @@ -395,62 +551,484 @@ gen_set_label(l1); } -#define FARITH2(name) \ -static inline void glue(gen_f, name)(int rb, int rc) \ -{ \ - if (unlikely(rc == 31)) \ - return; \ - \ - if (rb != 31) \ - gen_helper_ ## name (cpu_fir[rc], cpu_fir[rb]); \ - else { \ - TCGv tmp = tcg_const_i64(0); \ - gen_helper_ ## name (cpu_fir[rc], tmp); \ - tcg_temp_free(tmp); \ - } \ +static void gen_fcmov(TCGCond cond, int ra, int rb, int rc) +{ + TCGv cmp_tmp; + int l1; + + if (unlikely(rc == 31)) { + return; + } + + cmp_tmp = tcg_temp_new(); + if (unlikely(ra == 31)) { + tcg_gen_movi_i64(cmp_tmp, 0); + } else { + gen_fold_mzero(cond, cmp_tmp, cpu_fir[ra]); + } + + l1 = gen_new_label(); + tcg_gen_brcondi_i64(tcg_invert_cond(cond), cmp_tmp, 0, l1); + tcg_temp_free(cmp_tmp); + + if (rb != 31) + tcg_gen_mov_i64(cpu_fir[rc], cpu_fir[rb]); + else + tcg_gen_movi_i64(cpu_fir[rc], 0); + gen_set_label(l1); } -FARITH2(sqrts) + +#define QUAL_RM_N 0x080 /* Round mode nearest even */ +#define QUAL_RM_C 0x000 /* Round mode chopped */ +#define QUAL_RM_M 0x040 /* Round mode minus infinity */ +#define QUAL_RM_D 0x0c0 /* Round mode dynamic */ +#define QUAL_RM_MASK 0x0c0 + +#define QUAL_U 0x100 /* Underflow enable (fp output) */ +#define QUAL_V 0x100 /* Overflow enable (int output) */ +#define QUAL_S 0x400 /* Software completion enable */ +#define QUAL_I 0x200 /* Inexact detection enable */ + +static void gen_qual_roundmode(DisasContext *ctx, int fn11) +{ + TCGv_i32 tmp; + + fn11 &= QUAL_RM_MASK; + if (fn11 == ctx->tb_rm) { + return; + } + ctx->tb_rm = fn11; + + tmp = tcg_temp_new_i32(); + switch (fn11) { + case QUAL_RM_N: + tcg_gen_movi_i32(tmp, float_round_nearest_even); + break; + case QUAL_RM_C: + tcg_gen_movi_i32(tmp, float_round_to_zero); + break; + case QUAL_RM_M: + tcg_gen_movi_i32(tmp, float_round_down); + break; + case QUAL_RM_D: + tcg_gen_ld8u_i32(tmp, cpu_env, offsetof(CPUState, fpcr_dyn_round)); + break; + } + +#if defined(CONFIG_SOFTFLOAT_INLINE) + /* ??? The "softfloat.h" interface is to call set_float_rounding_mode. + With CONFIG_SOFTFLOAT that expands to an out-of-line call that just + sets the one field. */ + tcg_gen_st8_i32(tmp, cpu_env, + offsetof(CPUState, fp_status.float_rounding_mode)); +#else + gen_helper_setroundmode(tmp); +#endif + + tcg_temp_free_i32(tmp); +} + +static void gen_qual_flushzero(DisasContext *ctx, int fn11) +{ + TCGv_i32 tmp; + + fn11 &= QUAL_U; + if (fn11 == ctx->tb_ftz) { + return; + } + ctx->tb_ftz = fn11; + + tmp = tcg_temp_new_i32(); + if (fn11) { + /* Underflow is enabled, use the FPCR setting. */ + tcg_gen_ld8u_i32(tmp, cpu_env, offsetof(CPUState, fpcr_flush_to_zero)); + } else { + /* Underflow is disabled, force flush-to-zero. */ + tcg_gen_movi_i32(tmp, 1); + } + +#if defined(CONFIG_SOFTFLOAT_INLINE) + tcg_gen_st8_i32(tmp, cpu_env, + offsetof(CPUState, fp_status.flush_to_zero)); +#else + gen_helper_setflushzero(tmp); +#endif + + tcg_temp_free_i32(tmp); +} + +static TCGv gen_ieee_input(int reg, int fn11, int is_cmp) +{ + TCGv val = tcg_temp_new(); + if (reg == 31) { + tcg_gen_movi_i64(val, 0); + } else if (fn11 & QUAL_S) { + gen_helper_ieee_input_s(val, cpu_fir[reg]); + } else if (is_cmp) { + gen_helper_ieee_input_cmp(val, cpu_fir[reg]); + } else { + gen_helper_ieee_input(val, cpu_fir[reg]); + } + return val; +} + +static void gen_fp_exc_clear(void) +{ +#if defined(CONFIG_SOFTFLOAT_INLINE) + TCGv_i32 zero = tcg_const_i32(0); + tcg_gen_st8_i32(zero, cpu_env, + offsetof(CPUState, fp_status.float_exception_flags)); + tcg_temp_free_i32(zero); +#else + gen_helper_fp_exc_clear(); +#endif +} + +static void gen_fp_exc_raise_ignore(int rc, int fn11, int ignore) +{ + /* ??? We ought to be able to do something with imprecise exceptions. + E.g. notice we're still in the trap shadow of something within the + TB and do not generate the code to signal the exception; end the TB + when an exception is forced to arrive, either by consumption of a + register value or TRAPB or EXCB. */ + TCGv_i32 exc = tcg_temp_new_i32(); + TCGv_i32 reg; + +#if defined(CONFIG_SOFTFLOAT_INLINE) + tcg_gen_ld8u_i32(exc, cpu_env, + offsetof(CPUState, fp_status.float_exception_flags)); +#else + gen_helper_fp_exc_get(exc); +#endif + + if (ignore) { + tcg_gen_andi_i32(exc, exc, ~ignore); + } + + /* ??? Pass in the regno of the destination so that the helper can + set EXC_MASK, which contains a bitmask of destination registers + that have caused arithmetic traps. A simple userspace emulation + does not require this. We do need it for a guest kernel's entArith, + or if we were to do something clever with imprecise exceptions. */ + reg = tcg_const_i32(rc + 32); + + if (fn11 & QUAL_S) { + gen_helper_fp_exc_raise_s(exc, reg); + } else { + gen_helper_fp_exc_raise(exc, reg); + } + + tcg_temp_free_i32(reg); + tcg_temp_free_i32(exc); +} + +static inline void gen_fp_exc_raise(int rc, int fn11) +{ + gen_fp_exc_raise_ignore(rc, fn11, fn11 & QUAL_I ? 0 : float_flag_inexact); +} + +static void gen_fcvtlq(int rb, int rc) +{ + if (unlikely(rc == 31)) { + return; + } + if (unlikely(rb == 31)) { + tcg_gen_movi_i64(cpu_fir[rc], 0); + } else { + TCGv tmp = tcg_temp_new(); + + /* The arithmetic right shift here, plus the sign-extended mask below + yields a sign-extended result without an explicit ext32s_i64. */ + tcg_gen_sari_i64(tmp, cpu_fir[rb], 32); + tcg_gen_shri_i64(cpu_fir[rc], cpu_fir[rb], 29); + tcg_gen_andi_i64(tmp, tmp, (int32_t)0xc0000000); + tcg_gen_andi_i64(cpu_fir[rc], cpu_fir[rc], 0x3fffffff); + tcg_gen_or_i64(cpu_fir[rc], cpu_fir[rc], tmp); + + tcg_temp_free(tmp); + } +} + +static void gen_fcvtql(int rb, int rc) +{ + if (unlikely(rc == 31)) { + return; + } + if (unlikely(rb == 31)) { + tcg_gen_movi_i64(cpu_fir[rc], 0); + } else { + TCGv tmp = tcg_temp_new(); + + tcg_gen_andi_i64(tmp, cpu_fir[rb], 0xC0000000); + tcg_gen_andi_i64(cpu_fir[rc], cpu_fir[rb], 0x3FFFFFFF); + tcg_gen_shli_i64(tmp, tmp, 32); + tcg_gen_shli_i64(cpu_fir[rc], cpu_fir[rc], 29); + tcg_gen_or_i64(cpu_fir[rc], cpu_fir[rc], tmp); + + tcg_temp_free(tmp); + } +} + +static void gen_fcvtql_v(DisasContext *ctx, int rb, int rc) +{ + if (rb != 31) { + int lab = gen_new_label(); + TCGv tmp = tcg_temp_new(); + + tcg_gen_ext32s_i64(tmp, cpu_fir[rb]); + tcg_gen_brcond_i64(TCG_COND_EQ, tmp, cpu_fir[rb], lab); + gen_excp(ctx, EXCP_ARITH, EXC_M_IOV); + + gen_set_label(lab); + } + gen_fcvtql(rb, rc); +} + +#define FARITH2(name) \ +static inline void glue(gen_f, name)(int rb, int rc) \ +{ \ + if (unlikely(rc == 31)) { \ + return; \ + } \ + if (rb != 31) { \ + gen_helper_ ## name (cpu_fir[rc], cpu_fir[rb]); \ + } else { \ + TCGv tmp = tcg_const_i64(0); \ + gen_helper_ ## name (cpu_fir[rc], tmp); \ + tcg_temp_free(tmp); \ + } \ +} + +/* ??? VAX instruction qualifiers ignored. */ FARITH2(sqrtf) FARITH2(sqrtg) -FARITH2(sqrtt) FARITH2(cvtgf) FARITH2(cvtgq) FARITH2(cvtqf) FARITH2(cvtqg) -FARITH2(cvtst) -FARITH2(cvtts) -FARITH2(cvttq) -FARITH2(cvtqs) -FARITH2(cvtqt) -FARITH2(cvtlq) -FARITH2(cvtql) -FARITH2(cvtqlv) -FARITH2(cvtqlsv) - -#define FARITH3(name) \ -static inline void glue(gen_f, name)(int ra, int rb, int rc) \ -{ \ - if (unlikely(rc == 31)) \ - return; \ - \ - if (ra != 31) { \ - if (rb != 31) \ - gen_helper_ ## name (cpu_fir[rc], cpu_fir[ra], cpu_fir[rb]); \ - else { \ - TCGv tmp = tcg_const_i64(0); \ - gen_helper_ ## name (cpu_fir[rc], cpu_fir[ra], tmp); \ - tcg_temp_free(tmp); \ - } \ - } else { \ - TCGv tmp = tcg_const_i64(0); \ - if (rb != 31) \ - gen_helper_ ## name (cpu_fir[rc], tmp, cpu_fir[rb]); \ - else \ - gen_helper_ ## name (cpu_fir[rc], tmp, tmp); \ - tcg_temp_free(tmp); \ - } \ + +static void gen_ieee_arith2(DisasContext *ctx, void (*helper)(TCGv, TCGv), + int rb, int rc, int fn11) +{ + TCGv vb; + + /* ??? This is wrong: the instruction is not a nop, it still may + raise exceptions. */ + if (unlikely(rc == 31)) { + return; + } + + gen_qual_roundmode(ctx, fn11); + gen_qual_flushzero(ctx, fn11); + gen_fp_exc_clear(); + + vb = gen_ieee_input(rb, fn11, 0); + helper(cpu_fir[rc], vb); + tcg_temp_free(vb); + + gen_fp_exc_raise(rc, fn11); +} + +#define IEEE_ARITH2(name) \ +static inline void glue(gen_f, name)(DisasContext *ctx, \ + int rb, int rc, int fn11) \ +{ \ + gen_ieee_arith2(ctx, gen_helper_##name, rb, rc, fn11); \ +} +IEEE_ARITH2(sqrts) +IEEE_ARITH2(sqrtt) +IEEE_ARITH2(cvtst) +IEEE_ARITH2(cvtts) + +static void gen_fcvttq(DisasContext *ctx, int rb, int rc, int fn11) +{ + TCGv vb; + int ignore = 0; + + /* ??? This is wrong: the instruction is not a nop, it still may + raise exceptions. */ + if (unlikely(rc == 31)) { + return; + } + + /* No need to set flushzero, since we have an integer output. */ + gen_fp_exc_clear(); + vb = gen_ieee_input(rb, fn11, 0); + + /* Almost all integer conversions use cropped rounding, and most + also do not have integer overflow enabled. Special case that. */ + switch (fn11) { + case QUAL_RM_C: + gen_helper_cvttq_c(cpu_fir[rc], vb); + break; + case QUAL_V | QUAL_RM_C: + case QUAL_S | QUAL_V | QUAL_RM_C: + ignore = float_flag_inexact; + /* FALLTHRU */ + case QUAL_S | QUAL_V | QUAL_I | QUAL_RM_C: + gen_helper_cvttq_svic(cpu_fir[rc], vb); + break; + default: + gen_qual_roundmode(ctx, fn11); + gen_helper_cvttq(cpu_fir[rc], vb); + ignore |= (fn11 & QUAL_V ? 0 : float_flag_overflow); + ignore |= (fn11 & QUAL_I ? 0 : float_flag_inexact); + break; + } + tcg_temp_free(vb); + + gen_fp_exc_raise_ignore(rc, fn11, ignore); +} + +static void gen_ieee_intcvt(DisasContext *ctx, void (*helper)(TCGv, TCGv), + int rb, int rc, int fn11) +{ + TCGv vb; + + /* ??? This is wrong: the instruction is not a nop, it still may + raise exceptions. */ + if (unlikely(rc == 31)) { + return; + } + + gen_qual_roundmode(ctx, fn11); + + if (rb == 31) { + vb = tcg_const_i64(0); + } else { + vb = cpu_fir[rb]; + } + + /* The only exception that can be raised by integer conversion + is inexact. Thus we only need to worry about exceptions when + inexact handling is requested. */ + if (fn11 & QUAL_I) { + gen_fp_exc_clear(); + helper(cpu_fir[rc], vb); + gen_fp_exc_raise(rc, fn11); + } else { + helper(cpu_fir[rc], vb); + } + + if (rb == 31) { + tcg_temp_free(vb); + } +} + +#define IEEE_INTCVT(name) \ +static inline void glue(gen_f, name)(DisasContext *ctx, \ + int rb, int rc, int fn11) \ +{ \ + gen_ieee_intcvt(ctx, gen_helper_##name, rb, rc, fn11); \ +} +IEEE_INTCVT(cvtqs) +IEEE_INTCVT(cvtqt) + +static void gen_cpys_internal(int ra, int rb, int rc, int inv_a, uint64_t mask) +{ + TCGv va, vb, vmask; + int za = 0, zb = 0; + + if (unlikely(rc == 31)) { + return; + } + + vmask = tcg_const_i64(mask); + + TCGV_UNUSED_I64(va); + if (ra == 31) { + if (inv_a) { + va = vmask; + } else { + za = 1; + } + } else { + va = tcg_temp_new_i64(); + tcg_gen_mov_i64(va, cpu_fir[ra]); + if (inv_a) { + tcg_gen_andc_i64(va, vmask, va); + } else { + tcg_gen_and_i64(va, va, vmask); + } + } + + TCGV_UNUSED_I64(vb); + if (rb == 31) { + zb = 1; + } else { + vb = tcg_temp_new_i64(); + tcg_gen_andc_i64(vb, cpu_fir[rb], vmask); + } + + switch (za << 1 | zb) { + case 0 | 0: + tcg_gen_or_i64(cpu_fir[rc], va, vb); + break; + case 0 | 1: + tcg_gen_mov_i64(cpu_fir[rc], va); + break; + case 2 | 0: + tcg_gen_mov_i64(cpu_fir[rc], vb); + break; + case 2 | 1: + tcg_gen_movi_i64(cpu_fir[rc], 0); + break; + } + + tcg_temp_free(vmask); + if (ra != 31) { + tcg_temp_free(va); + } + if (rb != 31) { + tcg_temp_free(vb); + } +} + +static inline void gen_fcpys(int ra, int rb, int rc) +{ + gen_cpys_internal(ra, rb, rc, 0, 0x8000000000000000ULL); } +static inline void gen_fcpysn(int ra, int rb, int rc) +{ + gen_cpys_internal(ra, rb, rc, 1, 0x8000000000000000ULL); +} + +static inline void gen_fcpyse(int ra, int rb, int rc) +{ + gen_cpys_internal(ra, rb, rc, 0, 0xFFF0000000000000ULL); +} + +#define FARITH3(name) \ +static inline void glue(gen_f, name)(int ra, int rb, int rc) \ +{ \ + TCGv va, vb; \ + \ + if (unlikely(rc == 31)) { \ + return; \ + } \ + if (ra == 31) { \ + va = tcg_const_i64(0); \ + } else { \ + va = cpu_fir[ra]; \ + } \ + if (rb == 31) { \ + vb = tcg_const_i64(0); \ + } else { \ + vb = cpu_fir[rb]; \ + } \ + \ + gen_helper_ ## name (cpu_fir[rc], va, vb); \ + \ + if (ra == 31) { \ + tcg_temp_free(va); \ + } \ + if (rb == 31) { \ + tcg_temp_free(vb); \ + } \ +} + +/* ??? VAX instruction qualifiers ignored. */ FARITH3(addf) FARITH3(subf) FARITH3(mulf) @@ -462,95 +1040,181 @@ FARITH3(cmpgeq) FARITH3(cmpglt) FARITH3(cmpgle) -FARITH3(adds) -FARITH3(subs) -FARITH3(muls) -FARITH3(divs) -FARITH3(addt) -FARITH3(subt) -FARITH3(mult) -FARITH3(divt) -FARITH3(cmptun) -FARITH3(cmpteq) -FARITH3(cmptlt) -FARITH3(cmptle) -FARITH3(cpys) -FARITH3(cpysn) -FARITH3(cpyse) -#define FCMOV(name) \ -static inline void glue(gen_f, name)(int ra, int rb, int rc) \ -{ \ - int l1; \ - TCGv tmp; \ - \ - if (unlikely(rc == 31)) \ - return; \ - \ - l1 = gen_new_label(); \ - tmp = tcg_temp_new(); \ - if (ra != 31) { \ - tmp = tcg_temp_new(); \ - gen_helper_ ## name (tmp, cpu_fir[ra]); \ - } else { \ - tmp = tcg_const_i64(0); \ - gen_helper_ ## name (tmp, tmp); \ - } \ - tcg_gen_brcondi_i64(TCG_COND_EQ, tmp, 0, l1); \ - if (rb != 31) \ - tcg_gen_mov_i64(cpu_fir[rc], cpu_fir[ra]); \ - else \ - tcg_gen_movi_i64(cpu_fir[rc], 0); \ - gen_set_label(l1); \ -} -FCMOV(cmpfeq) -FCMOV(cmpfne) -FCMOV(cmpflt) -FCMOV(cmpfge) -FCMOV(cmpfle) -FCMOV(cmpfgt) - -/* EXTWH, EXTWH, EXTLH, EXTQH */ -static inline void gen_ext_h(void(*tcg_gen_ext_i64)(TCGv t0, TCGv t1), - int ra, int rb, int rc, int islit, uint8_t lit) +static void gen_ieee_arith3(DisasContext *ctx, + void (*helper)(TCGv, TCGv, TCGv), + int ra, int rb, int rc, int fn11) +{ + TCGv va, vb; + + /* ??? This is wrong: the instruction is not a nop, it still may + raise exceptions. */ + if (unlikely(rc == 31)) { + return; + } + + gen_qual_roundmode(ctx, fn11); + gen_qual_flushzero(ctx, fn11); + gen_fp_exc_clear(); + + va = gen_ieee_input(ra, fn11, 0); + vb = gen_ieee_input(rb, fn11, 0); + helper(cpu_fir[rc], va, vb); + tcg_temp_free(va); + tcg_temp_free(vb); + + gen_fp_exc_raise(rc, fn11); +} + +#define IEEE_ARITH3(name) \ +static inline void glue(gen_f, name)(DisasContext *ctx, \ + int ra, int rb, int rc, int fn11) \ +{ \ + gen_ieee_arith3(ctx, gen_helper_##name, ra, rb, rc, fn11); \ +} +IEEE_ARITH3(adds) +IEEE_ARITH3(subs) +IEEE_ARITH3(muls) +IEEE_ARITH3(divs) +IEEE_ARITH3(addt) +IEEE_ARITH3(subt) +IEEE_ARITH3(mult) +IEEE_ARITH3(divt) + +static void gen_ieee_compare(DisasContext *ctx, + void (*helper)(TCGv, TCGv, TCGv), + int ra, int rb, int rc, int fn11) +{ + TCGv va, vb; + + /* ??? This is wrong: the instruction is not a nop, it still may + raise exceptions. */ + if (unlikely(rc == 31)) { + return; + } + + gen_fp_exc_clear(); + + va = gen_ieee_input(ra, fn11, 1); + vb = gen_ieee_input(rb, fn11, 1); + helper(cpu_fir[rc], va, vb); + tcg_temp_free(va); + tcg_temp_free(vb); + + gen_fp_exc_raise(rc, fn11); +} + +#define IEEE_CMP3(name) \ +static inline void glue(gen_f, name)(DisasContext *ctx, \ + int ra, int rb, int rc, int fn11) \ +{ \ + gen_ieee_compare(ctx, gen_helper_##name, ra, rb, rc, fn11); \ +} +IEEE_CMP3(cmptun) +IEEE_CMP3(cmpteq) +IEEE_CMP3(cmptlt) +IEEE_CMP3(cmptle) + +static inline uint64_t zapnot_mask(uint8_t lit) +{ + uint64_t mask = 0; + int i; + + for (i = 0; i < 8; ++i) { + if ((lit >> i) & 1) + mask |= 0xffull << (i * 8); + } + return mask; +} + +/* Implement zapnot with an immediate operand, which expands to some + form of immediate AND. This is a basic building block in the + definition of many of the other byte manipulation instructions. */ +static void gen_zapnoti(TCGv dest, TCGv src, uint8_t lit) +{ + switch (lit) { + case 0x00: + tcg_gen_movi_i64(dest, 0); + break; + case 0x01: + tcg_gen_ext8u_i64(dest, src); + break; + case 0x03: + tcg_gen_ext16u_i64(dest, src); + break; + case 0x0f: + tcg_gen_ext32u_i64(dest, src); + break; + case 0xff: + tcg_gen_mov_i64(dest, src); + break; + default: + tcg_gen_andi_i64 (dest, src, zapnot_mask (lit)); + break; + } +} + +static inline void gen_zapnot(int ra, int rb, int rc, int islit, uint8_t lit) { if (unlikely(rc == 31)) return; + else if (unlikely(ra == 31)) + tcg_gen_movi_i64(cpu_ir[rc], 0); + else if (islit) + gen_zapnoti(cpu_ir[rc], cpu_ir[ra], lit); + else + gen_helper_zapnot (cpu_ir[rc], cpu_ir[ra], cpu_ir[rb]); +} - if (ra != 31) { +static inline void gen_zap(int ra, int rb, int rc, int islit, uint8_t lit) +{ + if (unlikely(rc == 31)) + return; + else if (unlikely(ra == 31)) + tcg_gen_movi_i64(cpu_ir[rc], 0); + else if (islit) + gen_zapnoti(cpu_ir[rc], cpu_ir[ra], ~lit); + else + gen_helper_zap (cpu_ir[rc], cpu_ir[ra], cpu_ir[rb]); +} + + +/* EXTWH, EXTLH, EXTQH */ +static void gen_ext_h(int ra, int rb, int rc, int islit, + uint8_t lit, uint8_t byte_mask) +{ + if (unlikely(rc == 31)) + return; + else if (unlikely(ra == 31)) + tcg_gen_movi_i64(cpu_ir[rc], 0); + else { if (islit) { - if (lit != 0) - tcg_gen_shli_i64(cpu_ir[rc], cpu_ir[ra], 64 - ((lit & 7) * 8)); - else - tcg_gen_mov_i64(cpu_ir[rc], cpu_ir[ra]); + lit = (64 - (lit & 7) * 8) & 0x3f; + tcg_gen_shli_i64(cpu_ir[rc], cpu_ir[ra], lit); } else { - TCGv tmp1; - tmp1 = tcg_temp_new(); - + TCGv tmp1 = tcg_temp_new(); tcg_gen_andi_i64(tmp1, cpu_ir[rb], 7); tcg_gen_shli_i64(tmp1, tmp1, 3); tcg_gen_neg_i64(tmp1, tmp1); tcg_gen_andi_i64(tmp1, tmp1, 0x3f); tcg_gen_shl_i64(cpu_ir[rc], cpu_ir[ra], tmp1); - tcg_temp_free(tmp1); } - if (tcg_gen_ext_i64) - tcg_gen_ext_i64(cpu_ir[rc], cpu_ir[rc]); - } else - tcg_gen_movi_i64(cpu_ir[rc], 0); + gen_zapnoti(cpu_ir[rc], cpu_ir[rc], byte_mask); + } } -/* EXTBL, EXTWL, EXTWL, EXTLL, EXTQL */ -static inline void gen_ext_l(void(*tcg_gen_ext_i64)(TCGv t0, TCGv t1), - int ra, int rb, int rc, int islit, uint8_t lit) +/* EXTBL, EXTWL, EXTLL, EXTQL */ +static void gen_ext_l(int ra, int rb, int rc, int islit, + uint8_t lit, uint8_t byte_mask) { if (unlikely(rc == 31)) return; - - if (ra != 31) { + else if (unlikely(ra == 31)) + tcg_gen_movi_i64(cpu_ir[rc], 0); + else { if (islit) { - tcg_gen_shri_i64(cpu_ir[rc], cpu_ir[ra], (lit & 7) * 8); + tcg_gen_shri_i64(cpu_ir[rc], cpu_ir[ra], (lit & 7) * 8); } else { TCGv tmp = tcg_temp_new(); tcg_gen_andi_i64(tmp, cpu_ir[rb], 7); @@ -558,10 +1222,144 @@ tcg_gen_shr_i64(cpu_ir[rc], cpu_ir[ra], tmp); tcg_temp_free(tmp); } - if (tcg_gen_ext_i64) - tcg_gen_ext_i64(cpu_ir[rc], cpu_ir[rc]); - } else + gen_zapnoti(cpu_ir[rc], cpu_ir[rc], byte_mask); + } +} + +/* INSWH, INSLH, INSQH */ +static void gen_ins_h(int ra, int rb, int rc, int islit, + uint8_t lit, uint8_t byte_mask) +{ + if (unlikely(rc == 31)) + return; + else if (unlikely(ra == 31) || (islit && (lit & 7) == 0)) + tcg_gen_movi_i64(cpu_ir[rc], 0); + else { + TCGv tmp = tcg_temp_new(); + + /* The instruction description has us left-shift the byte mask + and extract bits <15:8> and apply that zap at the end. This + is equivalent to simply performing the zap first and shifting + afterward. */ + gen_zapnoti (tmp, cpu_ir[ra], byte_mask); + + if (islit) { + /* Note that we have handled the lit==0 case above. */ + tcg_gen_shri_i64 (cpu_ir[rc], tmp, 64 - (lit & 7) * 8); + } else { + TCGv shift = tcg_temp_new(); + + /* If (B & 7) == 0, we need to shift by 64 and leave a zero. + Do this portably by splitting the shift into two parts: + shift_count-1 and 1. Arrange for the -1 by using + ones-complement instead of twos-complement in the negation: + ~((B & 7) * 8) & 63. */ + + tcg_gen_andi_i64(shift, cpu_ir[rb], 7); + tcg_gen_shli_i64(shift, shift, 3); + tcg_gen_not_i64(shift, shift); + tcg_gen_andi_i64(shift, shift, 0x3f); + + tcg_gen_shr_i64(cpu_ir[rc], tmp, shift); + tcg_gen_shri_i64(cpu_ir[rc], cpu_ir[rc], 1); + tcg_temp_free(shift); + } + tcg_temp_free(tmp); + } +} + +/* INSBL, INSWL, INSLL, INSQL */ +static void gen_ins_l(int ra, int rb, int rc, int islit, + uint8_t lit, uint8_t byte_mask) +{ + if (unlikely(rc == 31)) + return; + else if (unlikely(ra == 31)) + tcg_gen_movi_i64(cpu_ir[rc], 0); + else { + TCGv tmp = tcg_temp_new(); + + /* The instruction description has us left-shift the byte mask + the same number of byte slots as the data and apply the zap + at the end. This is equivalent to simply performing the zap + first and shifting afterward. */ + gen_zapnoti (tmp, cpu_ir[ra], byte_mask); + + if (islit) { + tcg_gen_shli_i64(cpu_ir[rc], tmp, (lit & 7) * 8); + } else { + TCGv shift = tcg_temp_new(); + tcg_gen_andi_i64(shift, cpu_ir[rb], 7); + tcg_gen_shli_i64(shift, shift, 3); + tcg_gen_shl_i64(cpu_ir[rc], tmp, shift); + tcg_temp_free(shift); + } + tcg_temp_free(tmp); + } +} + +/* MSKWH, MSKLH, MSKQH */ +static void gen_msk_h(int ra, int rb, int rc, int islit, + uint8_t lit, uint8_t byte_mask) +{ + if (unlikely(rc == 31)) + return; + else if (unlikely(ra == 31)) + tcg_gen_movi_i64(cpu_ir[rc], 0); + else if (islit) { + gen_zapnoti (cpu_ir[rc], cpu_ir[ra], ~((byte_mask << (lit & 7)) >> 8)); + } else { + TCGv shift = tcg_temp_new(); + TCGv mask = tcg_temp_new(); + + /* The instruction description is as above, where the byte_mask + is shifted left, and then we extract bits <15:8>. This can be + emulated with a right-shift on the expanded byte mask. This + requires extra care because for an input <2:0> == 0 we need a + shift of 64 bits in order to generate a zero. This is done by + splitting the shift into two parts, the variable shift - 1 + followed by a constant 1 shift. The code we expand below is + equivalent to ~((B & 7) * 8) & 63. */ + + tcg_gen_andi_i64(shift, cpu_ir[rb], 7); + tcg_gen_shli_i64(shift, shift, 3); + tcg_gen_not_i64(shift, shift); + tcg_gen_andi_i64(shift, shift, 0x3f); + tcg_gen_movi_i64(mask, zapnot_mask (byte_mask)); + tcg_gen_shr_i64(mask, mask, shift); + tcg_gen_shri_i64(mask, mask, 1); + + tcg_gen_andc_i64(cpu_ir[rc], cpu_ir[ra], mask); + + tcg_temp_free(mask); + tcg_temp_free(shift); + } +} + +/* MSKBL, MSKWL, MSKLL, MSKQL */ +static void gen_msk_l(int ra, int rb, int rc, int islit, + uint8_t lit, uint8_t byte_mask) +{ + if (unlikely(rc == 31)) + return; + else if (unlikely(ra == 31)) tcg_gen_movi_i64(cpu_ir[rc], 0); + else if (islit) { + gen_zapnoti (cpu_ir[rc], cpu_ir[ra], ~(byte_mask << (lit & 7))); + } else { + TCGv shift = tcg_temp_new(); + TCGv mask = tcg_temp_new(); + + tcg_gen_andi_i64(shift, cpu_ir[rb], 7); + tcg_gen_shli_i64(shift, shift, 3); + tcg_gen_movi_i64(mask, zapnot_mask (byte_mask)); + tcg_gen_shl_i64(mask, mask, shift); + + tcg_gen_andc_i64(cpu_ir[rc], cpu_ir[ra], mask); + + tcg_temp_free(mask); + tcg_temp_free(shift); + } } /* Code to call arith3 helpers */ @@ -595,71 +1393,92 @@ ARITH3(sublv) ARITH3(addqv) ARITH3(subqv) -ARITH3(mskbl) -ARITH3(insbl) -ARITH3(mskwl) -ARITH3(inswl) -ARITH3(mskll) -ARITH3(insll) -ARITH3(zap) -ARITH3(zapnot) -ARITH3(mskql) -ARITH3(insql) -ARITH3(mskwh) -ARITH3(inswh) -ARITH3(msklh) -ARITH3(inslh) -ARITH3(mskqh) -ARITH3(insqh) ARITH3(umulh) ARITH3(mullv) ARITH3(mulqv) +ARITH3(minub8) +ARITH3(minsb8) +ARITH3(minuw4) +ARITH3(minsw4) +ARITH3(maxub8) +ARITH3(maxsb8) +ARITH3(maxuw4) +ARITH3(maxsw4) +ARITH3(perr) + +#define MVIOP2(name) \ +static inline void glue(gen_, name)(int rb, int rc) \ +{ \ + if (unlikely(rc == 31)) \ + return; \ + if (unlikely(rb == 31)) \ + tcg_gen_movi_i64(cpu_ir[rc], 0); \ + else \ + gen_helper_ ## name (cpu_ir[rc], cpu_ir[rb]); \ +} +MVIOP2(pklb) +MVIOP2(pkwb) +MVIOP2(unpkbl) +MVIOP2(unpkbw) -static inline void gen_cmp(TCGCond cond, int ra, int rb, int rc, int islit, - uint8_t lit) +static void gen_cmp(TCGCond cond, int ra, int rb, int rc, + int islit, uint8_t lit) { - int l1, l2; - TCGv tmp; + TCGv va, vb; - if (unlikely(rc == 31)) - return; + if (unlikely(rc == 31)) { + return; + } - l1 = gen_new_label(); - l2 = gen_new_label(); + if (ra == 31) { + va = tcg_const_i64(0); + } else { + va = cpu_ir[ra]; + } + if (islit) { + vb = tcg_const_i64(lit); + } else { + vb = cpu_ir[rb]; + } + + tcg_gen_setcond_i64(cond, cpu_ir[rc], va, vb); + + if (ra == 31) { + tcg_temp_free(va); + } + if (islit) { + tcg_temp_free(vb); + } +} + +static void gen_rx(int ra, int set) +{ + TCGv_i32 tmp; if (ra != 31) { - tmp = tcg_temp_new(); - tcg_gen_mov_i64(tmp, cpu_ir[ra]); - } else - tmp = tcg_const_i64(0); - if (islit) - tcg_gen_brcondi_i64(cond, tmp, lit, l1); - else - tcg_gen_brcond_i64(cond, tmp, cpu_ir[rb], l1); + tcg_gen_ld8u_i64(cpu_ir[ra], cpu_env, offsetof(CPUState, intr_flag)); + } - tcg_gen_movi_i64(cpu_ir[rc], 0); - tcg_gen_br(l2); - gen_set_label(l1); - tcg_gen_movi_i64(cpu_ir[rc], 1); - gen_set_label(l2); + tmp = tcg_const_i32(set); + tcg_gen_st8_i32(tmp, cpu_env, offsetof(CPUState, intr_flag)); + tcg_temp_free_i32(tmp); } -static inline int translate_one(DisasContext *ctx, uint32_t insn) +static ExitStatus translate_one(DisasContext *ctx, uint32_t insn) { uint32_t palcode; int32_t disp21, disp16, disp12; - uint16_t fn11, fn16; - uint8_t opc, ra, rb, rc, sbz, fpfn, fn7, fn2, islit; + uint16_t fn11; + uint8_t opc, ra, rb, rc, fpfn, fn7, fn2, islit, real_islit; uint8_t lit; - int ret; + ExitStatus ret; /* Decode all instruction fields */ opc = insn >> 26; ra = (insn >> 21) & 0x1F; rb = (insn >> 16) & 0x1F; rc = insn & 0x1F; - sbz = (insn >> 13) & 0x07; - islit = (insn >> 12) & 1; + real_islit = islit = (insn >> 12) & 1; if (rb == 31 && !islit) { islit = 1; lit = 0; @@ -669,34 +1488,43 @@ disp21 = ((int32_t)((insn & 0x001FFFFF) << 11)) >> 11; disp16 = (int16_t)(insn & 0x0000FFFF); disp12 = (int32_t)((insn & 0x00000FFF) << 20) >> 20; - fn16 = insn & 0x0000FFFF; fn11 = (insn >> 5) & 0x000007FF; fpfn = fn11 & 0x3F; fn7 = (insn >> 5) & 0x0000007F; fn2 = (insn >> 5) & 0x00000003; - ret = 0; - LOG_DISAS("opc %02x ra %d rb %d rc %d disp16 %04x\n", + LOG_DISAS("opc %02x ra %2d rb %2d rc %2d disp16 %6d\n", opc, ra, rb, rc, disp16); + + ret = NO_EXIT; switch (opc) { case 0x00: /* CALL_PAL */ +#ifdef CONFIG_USER_ONLY + if (palcode == 0x9E) { + /* RDUNIQUE */ + tcg_gen_mov_i64(cpu_ir[IR_V0], cpu_uniq); + break; + } else if (palcode == 0x9F) { + /* WRUNIQUE */ + tcg_gen_mov_i64(cpu_uniq, cpu_ir[IR_A0]); + break; + } +#endif if (palcode >= 0x80 && palcode < 0xC0) { /* Unprivileged PAL call */ - gen_excp(ctx, EXCP_CALL_PAL + ((palcode & 0x3F) << 6), 0); -#if !defined (CONFIG_USER_ONLY) - } else if (palcode < 0x40) { + ret = gen_excp(ctx, EXCP_CALL_PAL + ((palcode & 0x3F) << 6), 0); + break; + } +#ifndef CONFIG_USER_ONLY + if (palcode < 0x40) { /* Privileged PAL code */ if (ctx->mem_idx & 1) goto invalid_opc; - else - gen_excp(ctx, EXCP_CALL_PALP + ((palcode & 0x3F) << 6), 0); -#endif - } else { - /* Invalid PAL call */ - goto invalid_opc; + ret = gen_excp(ctx, EXCP_CALL_PALP + ((palcode & 0x3F) << 6), 0); } - ret = 3; - break; +#endif + /* Invalid PAL call */ + goto invalid_opc; case 0x01: /* OPC01 */ goto invalid_opc; @@ -754,15 +1582,15 @@ break; case 0x0D: /* STW */ - gen_store_mem(ctx, &tcg_gen_qemu_st16, ra, rb, disp16, 0, 0, 0); + gen_store_mem(ctx, &tcg_gen_qemu_st16, ra, rb, disp16, 0, 0); break; case 0x0E: /* STB */ - gen_store_mem(ctx, &tcg_gen_qemu_st8, ra, rb, disp16, 0, 0, 0); + gen_store_mem(ctx, &tcg_gen_qemu_st8, ra, rb, disp16, 0, 0); break; case 0x0F: /* STQ_U */ - gen_store_mem(ctx, &tcg_gen_qemu_st64, ra, rb, disp16, 0, 1, 0); + gen_store_mem(ctx, &tcg_gen_qemu_st64, ra, rb, disp16, 0, 1); break; case 0x10: switch (fn7) { @@ -1066,11 +1894,11 @@ break; case 0x14: /* CMOVLBS */ - gen_cmov(TCG_COND_EQ, ra, rb, rc, islit, lit, 1); + gen_cmov(TCG_COND_NE, ra, rb, rc, islit, lit, 1); break; case 0x16: /* CMOVLBC */ - gen_cmov(TCG_COND_NE, ra, rb, rc, islit, lit, 1); + gen_cmov(TCG_COND_EQ, ra, rb, rc, islit, lit, 1); break; case 0x20: /* BIS */ @@ -1090,11 +1918,11 @@ break; case 0x24: /* CMOVEQ */ - gen_cmov(TCG_COND_NE, ra, rb, rc, islit, lit, 0); + gen_cmov(TCG_COND_EQ, ra, rb, rc, islit, lit, 0); break; case 0x26: /* CMOVNE */ - gen_cmov(TCG_COND_EQ, ra, rb, rc, islit, lit, 0); + gen_cmov(TCG_COND_NE, ra, rb, rc, islit, lit, 0); break; case 0x28: /* ORNOT */ @@ -1130,11 +1958,11 @@ break; case 0x44: /* CMOVLT */ - gen_cmov(TCG_COND_GE, ra, rb, rc, islit, lit, 0); + gen_cmov(TCG_COND_LT, ra, rb, rc, islit, lit, 0); break; case 0x46: /* CMOVGE */ - gen_cmov(TCG_COND_LT, ra, rb, rc, islit, lit, 0); + gen_cmov(TCG_COND_GE, ra, rb, rc, islit, lit, 0); break; case 0x48: /* EQV */ @@ -1174,11 +2002,11 @@ break; case 0x64: /* CMOVLE */ - gen_cmov(TCG_COND_GT, ra, rb, rc, islit, lit, 0); + gen_cmov(TCG_COND_LE, ra, rb, rc, islit, lit, 0); break; case 0x66: /* CMOVGT */ - gen_cmov(TCG_COND_LE, ra, rb, rc, islit, lit, 0); + gen_cmov(TCG_COND_GT, ra, rb, rc, islit, lit, 0); break; case 0x6C: /* IMPLVER */ @@ -1193,39 +2021,39 @@ switch (fn7) { case 0x02: /* MSKBL */ - gen_mskbl(ra, rb, rc, islit, lit); + gen_msk_l(ra, rb, rc, islit, lit, 0x01); break; case 0x06: /* EXTBL */ - gen_ext_l(&tcg_gen_ext8u_i64, ra, rb, rc, islit, lit); + gen_ext_l(ra, rb, rc, islit, lit, 0x01); break; case 0x0B: /* INSBL */ - gen_insbl(ra, rb, rc, islit, lit); + gen_ins_l(ra, rb, rc, islit, lit, 0x01); break; case 0x12: /* MSKWL */ - gen_mskwl(ra, rb, rc, islit, lit); + gen_msk_l(ra, rb, rc, islit, lit, 0x03); break; case 0x16: /* EXTWL */ - gen_ext_l(&tcg_gen_ext16u_i64, ra, rb, rc, islit, lit); + gen_ext_l(ra, rb, rc, islit, lit, 0x03); break; case 0x1B: /* INSWL */ - gen_inswl(ra, rb, rc, islit, lit); + gen_ins_l(ra, rb, rc, islit, lit, 0x03); break; case 0x22: /* MSKLL */ - gen_mskll(ra, rb, rc, islit, lit); + gen_msk_l(ra, rb, rc, islit, lit, 0x0f); break; case 0x26: /* EXTLL */ - gen_ext_l(&tcg_gen_ext32u_i64, ra, rb, rc, islit, lit); + gen_ext_l(ra, rb, rc, islit, lit, 0x0f); break; case 0x2B: /* INSLL */ - gen_insll(ra, rb, rc, islit, lit); + gen_ins_l(ra, rb, rc, islit, lit, 0x0f); break; case 0x30: /* ZAP */ @@ -1237,7 +2065,7 @@ break; case 0x32: /* MSKQL */ - gen_mskql(ra, rb, rc, islit, lit); + gen_msk_l(ra, rb, rc, islit, lit, 0xff); break; case 0x34: /* SRL */ @@ -1257,7 +2085,7 @@ break; case 0x36: /* EXTQL */ - gen_ext_l(NULL, ra, rb, rc, islit, lit); + gen_ext_l(ra, rb, rc, islit, lit, 0xff); break; case 0x39: /* SLL */ @@ -1277,7 +2105,7 @@ break; case 0x3B: /* INSQL */ - gen_insql(ra, rb, rc, islit, lit); + gen_ins_l(ra, rb, rc, islit, lit, 0xff); break; case 0x3C: /* SRA */ @@ -1297,39 +2125,39 @@ break; case 0x52: /* MSKWH */ - gen_mskwh(ra, rb, rc, islit, lit); + gen_msk_h(ra, rb, rc, islit, lit, 0x03); break; case 0x57: /* INSWH */ - gen_inswh(ra, rb, rc, islit, lit); + gen_ins_h(ra, rb, rc, islit, lit, 0x03); break; case 0x5A: /* EXTWH */ - gen_ext_h(&tcg_gen_ext16u_i64, ra, rb, rc, islit, lit); + gen_ext_h(ra, rb, rc, islit, lit, 0x03); break; case 0x62: /* MSKLH */ - gen_msklh(ra, rb, rc, islit, lit); + gen_msk_h(ra, rb, rc, islit, lit, 0x0f); break; case 0x67: /* INSLH */ - gen_inslh(ra, rb, rc, islit, lit); + gen_ins_h(ra, rb, rc, islit, lit, 0x0f); break; case 0x6A: /* EXTLH */ - gen_ext_h(&tcg_gen_ext32u_i64, ra, rb, rc, islit, lit); + gen_ext_h(ra, rb, rc, islit, lit, 0x0f); break; case 0x72: /* MSKQH */ - gen_mskqh(ra, rb, rc, islit, lit); + gen_msk_h(ra, rb, rc, islit, lit, 0xff); break; case 0x77: /* INSQH */ - gen_insqh(ra, rb, rc, islit, lit); + gen_ins_h(ra, rb, rc, islit, lit, 0xff); break; case 0x7A: /* EXTQH */ - gen_ext_h(NULL, ra, rb, rc, islit, lit); + gen_ext_h(ra, rb, rc, islit, lit, 0xff); break; default: goto invalid_opc; @@ -1379,7 +2207,7 @@ } break; case 0x14: - switch (fpfn) { /* f11 & 0x3F */ + switch (fpfn) { /* fn11 & 0x3F */ case 0x04: /* ITOFS */ if (!(ctx->amask & AMASK_FIX)) @@ -1404,7 +2232,7 @@ /* SQRTS */ if (!(ctx->amask & AMASK_FIX)) goto invalid_opc; - gen_fsqrts(rb, rc); + gen_fsqrts(ctx, rb, rc, fn11); break; case 0x14: /* ITOFF */ @@ -1441,7 +2269,7 @@ /* SQRTT */ if (!(ctx->amask & AMASK_FIX)) goto invalid_opc; - gen_fsqrtt(rb, rc); + gen_fsqrtt(ctx, rb, rc, fn11); break; default: goto invalid_opc; @@ -1450,7 +2278,7 @@ case 0x15: /* VAX floating point */ /* XXX: rounding mode and trap are ignored (!) */ - switch (fpfn) { /* f11 & 0x3F */ + switch (fpfn) { /* fn11 & 0x3F */ case 0x00: /* ADDF */ gen_faddf(ra, rb, rc); @@ -1533,77 +2361,75 @@ break; case 0x16: /* IEEE floating-point */ - /* XXX: rounding mode and traps are ignored (!) */ - switch (fpfn) { /* f11 & 0x3F */ + switch (fpfn) { /* fn11 & 0x3F */ case 0x00: /* ADDS */ - gen_fadds(ra, rb, rc); + gen_fadds(ctx, ra, rb, rc, fn11); break; case 0x01: /* SUBS */ - gen_fsubs(ra, rb, rc); + gen_fsubs(ctx, ra, rb, rc, fn11); break; case 0x02: /* MULS */ - gen_fmuls(ra, rb, rc); + gen_fmuls(ctx, ra, rb, rc, fn11); break; case 0x03: /* DIVS */ - gen_fdivs(ra, rb, rc); + gen_fdivs(ctx, ra, rb, rc, fn11); break; case 0x20: /* ADDT */ - gen_faddt(ra, rb, rc); + gen_faddt(ctx, ra, rb, rc, fn11); break; case 0x21: /* SUBT */ - gen_fsubt(ra, rb, rc); + gen_fsubt(ctx, ra, rb, rc, fn11); break; case 0x22: /* MULT */ - gen_fmult(ra, rb, rc); + gen_fmult(ctx, ra, rb, rc, fn11); break; case 0x23: /* DIVT */ - gen_fdivt(ra, rb, rc); + gen_fdivt(ctx, ra, rb, rc, fn11); break; case 0x24: /* CMPTUN */ - gen_fcmptun(ra, rb, rc); + gen_fcmptun(ctx, ra, rb, rc, fn11); break; case 0x25: /* CMPTEQ */ - gen_fcmpteq(ra, rb, rc); + gen_fcmpteq(ctx, ra, rb, rc, fn11); break; case 0x26: /* CMPTLT */ - gen_fcmptlt(ra, rb, rc); + gen_fcmptlt(ctx, ra, rb, rc, fn11); break; case 0x27: /* CMPTLE */ - gen_fcmptle(ra, rb, rc); + gen_fcmptle(ctx, ra, rb, rc, fn11); break; case 0x2C: - /* XXX: incorrect */ if (fn11 == 0x2AC || fn11 == 0x6AC) { /* CVTST */ - gen_fcvtst(rb, rc); + gen_fcvtst(ctx, rb, rc, fn11); } else { /* CVTTS */ - gen_fcvtts(rb, rc); + gen_fcvtts(ctx, rb, rc, fn11); } break; case 0x2F: /* CVTTQ */ - gen_fcvttq(rb, rc); + gen_fcvttq(ctx, rb, rc, fn11); break; case 0x3C: /* CVTQS */ - gen_fcvtqs(rb, rc); + gen_fcvtqs(ctx, rb, rc, fn11); break; case 0x3E: /* CVTQT */ - gen_fcvtqt(rb, rc); + gen_fcvtqt(ctx, rb, rc, fn11); break; default: goto invalid_opc; @@ -1617,12 +2443,16 @@ break; case 0x020: if (likely(rc != 31)) { - if (ra == rb) + if (ra == rb) { /* FMOV */ - tcg_gen_mov_i64(cpu_fir[rc], cpu_fir[ra]); - else + if (ra == 31) + tcg_gen_movi_i64(cpu_fir[rc], 0); + else + tcg_gen_mov_i64(cpu_fir[rc], cpu_fir[ra]); + } else { /* CPYS */ gen_fcpys(ra, rb, rc); + } } break; case 0x021: @@ -1650,27 +2480,27 @@ break; case 0x02A: /* FCMOVEQ */ - gen_fcmpfeq(ra, rb, rc); + gen_fcmov(TCG_COND_EQ, ra, rb, rc); break; case 0x02B: /* FCMOVNE */ - gen_fcmpfne(ra, rb, rc); + gen_fcmov(TCG_COND_NE, ra, rb, rc); break; case 0x02C: /* FCMOVLT */ - gen_fcmpflt(ra, rb, rc); + gen_fcmov(TCG_COND_LT, ra, rb, rc); break; case 0x02D: /* FCMOVGE */ - gen_fcmpfge(ra, rb, rc); + gen_fcmov(TCG_COND_GE, ra, rb, rc); break; case 0x02E: /* FCMOVLE */ - gen_fcmpfle(ra, rb, rc); + gen_fcmov(TCG_COND_LE, ra, rb, rc); break; case 0x02F: /* FCMOVGT */ - gen_fcmpfgt(ra, rb, rc); + gen_fcmov(TCG_COND_GT, ra, rb, rc); break; case 0x030: /* CVTQL */ @@ -1678,11 +2508,12 @@ break; case 0x130: /* CVTQL/V */ - gen_fcvtqlv(rb, rc); - break; case 0x530: /* CVTQL/SV */ - gen_fcvtqlsv(rb, rc); + /* ??? I'm pretty sure there's nothing that /sv needs to do that + /v doesn't do. The only thing I can think is that /sv is a + valid instruction merely for completeness in the ISA. */ + gen_fcvtql_v(ctx, rb, rc); break; default: goto invalid_opc; @@ -1692,13 +2523,11 @@ switch ((uint16_t)disp16) { case 0x0000: /* TRAPB */ - /* No-op. Just exit from the current tb */ - ret = 2; + /* No-op. */ break; case 0x0400: /* EXCB */ - /* No-op. Just exit from the current tb */ - ret = 2; + /* No-op. */ break; case 0x4000: /* MB */ @@ -1723,16 +2552,14 @@ break; case 0xE000: /* RC */ - if (ra != 31) - gen_helper_rc(cpu_ir[ra]); + gen_rx(ra, 0); break; case 0xE800: /* ECB */ break; case 0xF000: /* RS */ - if (ra != 31) - gen_helper_rs(cpu_ir[ra]); + gen_rx(ra, 1); break; case 0xF800: /* WH64 */ @@ -1757,28 +2584,17 @@ break; #endif case 0x1A: - if (rb != 31) + /* JMP, JSR, RET, JSR_COROUTINE. These only differ by the branch + prediction stack action, which of course we don't implement. */ + if (rb != 31) { tcg_gen_andi_i64(cpu_pc, cpu_ir[rb], ~3); - else + } else { tcg_gen_movi_i64(cpu_pc, 0); - if (ra != 31) + } + if (ra != 31) { tcg_gen_movi_i64(cpu_ir[ra], ctx->pc); - /* Those four jumps only differ by the branch prediction hint */ - switch (fn2) { - case 0x0: - /* JMP */ - break; - case 0x1: - /* JSR */ - break; - case 0x2: - /* RET */ - break; - case 0x3: - /* JSR_COROUTINE */ - break; } - ret = 1; + ret = EXIT_PC_UPDATED; break; case 0x1B: /* HW_LD (PALcode) */ @@ -1916,8 +2732,7 @@ /* PERR */ if (!(ctx->amask & AMASK_MVI)) goto invalid_opc; - /* XXX: TODO */ - goto invalid_opc; + gen_perr(ra, rb, rc, islit, lit); break; case 0x32: /* CTLZ */ @@ -1945,85 +2760,81 @@ /* UNPKBW */ if (!(ctx->amask & AMASK_MVI)) goto invalid_opc; - /* XXX: TODO */ - goto invalid_opc; + if (real_islit || ra != 31) + goto invalid_opc; + gen_unpkbw (rb, rc); break; case 0x35: - /* UNPKWL */ + /* UNPKBL */ if (!(ctx->amask & AMASK_MVI)) goto invalid_opc; - /* XXX: TODO */ - goto invalid_opc; + if (real_islit || ra != 31) + goto invalid_opc; + gen_unpkbl (rb, rc); break; case 0x36: /* PKWB */ if (!(ctx->amask & AMASK_MVI)) goto invalid_opc; - /* XXX: TODO */ - goto invalid_opc; + if (real_islit || ra != 31) + goto invalid_opc; + gen_pkwb (rb, rc); break; case 0x37: /* PKLB */ if (!(ctx->amask & AMASK_MVI)) goto invalid_opc; - /* XXX: TODO */ - goto invalid_opc; + if (real_islit || ra != 31) + goto invalid_opc; + gen_pklb (rb, rc); break; case 0x38: /* MINSB8 */ if (!(ctx->amask & AMASK_MVI)) goto invalid_opc; - /* XXX: TODO */ - goto invalid_opc; + gen_minsb8 (ra, rb, rc, islit, lit); break; case 0x39: /* MINSW4 */ if (!(ctx->amask & AMASK_MVI)) goto invalid_opc; - /* XXX: TODO */ - goto invalid_opc; + gen_minsw4 (ra, rb, rc, islit, lit); break; case 0x3A: /* MINUB8 */ if (!(ctx->amask & AMASK_MVI)) goto invalid_opc; - /* XXX: TODO */ - goto invalid_opc; + gen_minub8 (ra, rb, rc, islit, lit); break; case 0x3B: /* MINUW4 */ if (!(ctx->amask & AMASK_MVI)) goto invalid_opc; - /* XXX: TODO */ - goto invalid_opc; + gen_minuw4 (ra, rb, rc, islit, lit); break; case 0x3C: /* MAXUB8 */ if (!(ctx->amask & AMASK_MVI)) goto invalid_opc; - /* XXX: TODO */ - goto invalid_opc; + gen_maxub8 (ra, rb, rc, islit, lit); break; case 0x3D: /* MAXUW4 */ if (!(ctx->amask & AMASK_MVI)) goto invalid_opc; - /* XXX: TODO */ - goto invalid_opc; + gen_maxuw4 (ra, rb, rc, islit, lit); break; case 0x3E: /* MAXSB8 */ if (!(ctx->amask & AMASK_MVI)) goto invalid_opc; - /* XXX: TODO */ - goto invalid_opc; + gen_maxsb8 (ra, rb, rc, islit, lit); break; case 0x3F: /* MAXSW4 */ if (!(ctx->amask & AMASK_MVI)) goto invalid_opc; - /* XXX: TODO */ - goto invalid_opc; + gen_maxsw4 (ra, rb, rc, islit, lit); break; case 0x70: /* FTOIT */ @@ -2074,7 +2885,7 @@ tcg_temp_free(tmp2); } tcg_temp_free(tmp1); - ret = 2; + ret = EXIT_PC_STALE; } break; #endif @@ -2099,7 +2910,7 @@ gen_helper_hw_ret(tmp); tcg_temp_free(tmp); } - ret = 2; + ret = EXIT_PC_UPDATED; break; #endif case 0x1F: @@ -2212,19 +3023,19 @@ break; case 0x24: /* STF */ - gen_store_mem(ctx, &gen_qemu_stf, ra, rb, disp16, 1, 0, 0); + gen_store_mem(ctx, &gen_qemu_stf, ra, rb, disp16, 1, 0); break; case 0x25: /* STG */ - gen_store_mem(ctx, &gen_qemu_stg, ra, rb, disp16, 1, 0, 0); + gen_store_mem(ctx, &gen_qemu_stg, ra, rb, disp16, 1, 0); break; case 0x26: /* STS */ - gen_store_mem(ctx, &gen_qemu_sts, ra, rb, disp16, 1, 0, 0); + gen_store_mem(ctx, &gen_qemu_sts, ra, rb, disp16, 1, 0); break; case 0x27: /* STT */ - gen_store_mem(ctx, &tcg_gen_qemu_st64, ra, rb, disp16, 1, 0, 0); + gen_store_mem(ctx, &tcg_gen_qemu_st64, ra, rb, disp16, 1, 0); break; case 0x28: /* LDL */ @@ -2244,89 +3055,80 @@ break; case 0x2C: /* STL */ - gen_store_mem(ctx, &tcg_gen_qemu_st32, ra, rb, disp16, 0, 0, 0); + gen_store_mem(ctx, &tcg_gen_qemu_st32, ra, rb, disp16, 0, 0); break; case 0x2D: /* STQ */ - gen_store_mem(ctx, &tcg_gen_qemu_st64, ra, rb, disp16, 0, 0, 0); + gen_store_mem(ctx, &tcg_gen_qemu_st64, ra, rb, disp16, 0, 0); break; case 0x2E: /* STL_C */ - gen_store_mem(ctx, &gen_qemu_stl_c, ra, rb, disp16, 0, 0, 1); + ret = gen_store_conditional(ctx, ra, rb, disp16, 0); break; case 0x2F: /* STQ_C */ - gen_store_mem(ctx, &gen_qemu_stq_c, ra, rb, disp16, 0, 0, 1); + ret = gen_store_conditional(ctx, ra, rb, disp16, 1); break; case 0x30: /* BR */ - if (ra != 31) - tcg_gen_movi_i64(cpu_ir[ra], ctx->pc); - tcg_gen_movi_i64(cpu_pc, ctx->pc + (int64_t)(disp21 << 2)); - ret = 1; + ret = gen_bdirect(ctx, ra, disp21); break; case 0x31: /* FBEQ */ + ret = gen_fbcond(ctx, TCG_COND_EQ, ra, disp21); + break; case 0x32: /* FBLT */ + ret = gen_fbcond(ctx, TCG_COND_LT, ra, disp21); + break; case 0x33: /* FBLE */ - gen_fbcond(ctx, opc, ra, disp16); - ret = 1; + ret = gen_fbcond(ctx, TCG_COND_LE, ra, disp21); break; case 0x34: /* BSR */ - if (ra != 31) - tcg_gen_movi_i64(cpu_ir[ra], ctx->pc); - tcg_gen_movi_i64(cpu_pc, ctx->pc + (int64_t)(disp21 << 2)); - ret = 1; + ret = gen_bdirect(ctx, ra, disp21); break; case 0x35: /* FBNE */ + ret = gen_fbcond(ctx, TCG_COND_NE, ra, disp21); + break; case 0x36: /* FBGE */ + ret = gen_fbcond(ctx, TCG_COND_GE, ra, disp21); + break; case 0x37: /* FBGT */ - gen_fbcond(ctx, opc, ra, disp16); - ret = 1; + ret = gen_fbcond(ctx, TCG_COND_GT, ra, disp21); break; case 0x38: /* BLBC */ - gen_bcond(ctx, TCG_COND_EQ, ra, disp21, 1); - ret = 1; + ret = gen_bcond(ctx, TCG_COND_EQ, ra, disp21, 1); break; case 0x39: /* BEQ */ - gen_bcond(ctx, TCG_COND_EQ, ra, disp21, 0); - ret = 1; + ret = gen_bcond(ctx, TCG_COND_EQ, ra, disp21, 0); break; case 0x3A: /* BLT */ - gen_bcond(ctx, TCG_COND_LT, ra, disp21, 0); - ret = 1; + ret = gen_bcond(ctx, TCG_COND_LT, ra, disp21, 0); break; case 0x3B: /* BLE */ - gen_bcond(ctx, TCG_COND_LE, ra, disp21, 0); - ret = 1; + ret = gen_bcond(ctx, TCG_COND_LE, ra, disp21, 0); break; case 0x3C: /* BLBS */ - gen_bcond(ctx, TCG_COND_NE, ra, disp21, 1); - ret = 1; + ret = gen_bcond(ctx, TCG_COND_NE, ra, disp21, 1); break; case 0x3D: /* BNE */ - gen_bcond(ctx, TCG_COND_NE, ra, disp21, 0); - ret = 1; + ret = gen_bcond(ctx, TCG_COND_NE, ra, disp21, 0); break; case 0x3E: /* BGE */ - gen_bcond(ctx, TCG_COND_GE, ra, disp21, 0); - ret = 1; + ret = gen_bcond(ctx, TCG_COND_GE, ra, disp21, 0); break; case 0x3F: /* BGT */ - gen_bcond(ctx, TCG_COND_GT, ra, disp21, 0); - ret = 1; + ret = gen_bcond(ctx, TCG_COND_GT, ra, disp21, 0); break; invalid_opc: - gen_invalid(ctx); - ret = 3; + ret = gen_invalid(ctx); break; } @@ -2337,37 +3139,47 @@ TranslationBlock *tb, int search_pc) { -#if defined ALPHA_DEBUG_DISAS - static int insn_count; -#endif DisasContext ctx, *ctxp = &ctx; target_ulong pc_start; uint32_t insn; uint16_t *gen_opc_end; CPUBreakpoint *bp; int j, lj = -1; - int ret; + ExitStatus ret; int num_insns; int max_insns; pc_start = tb->pc; gen_opc_end = gen_opc_buf + OPC_MAX_SIZE; + + ctx.tb = tb; + ctx.env = env; ctx.pc = pc_start; ctx.amask = env->amask; - ctx.env = env; #if defined (CONFIG_USER_ONLY) ctx.mem_idx = 0; #else ctx.mem_idx = ((env->ps >> 3) & 3); ctx.pal_mode = env->ipr[IPR_EXC_ADDR] & 1; #endif + + /* ??? Every TB begins with unset rounding mode, to be initialized on + the first fp insn of the TB. Alternately we could define a proper + default for every TB (e.g. QUAL_RM_N or QUAL_RM_D) and make sure + to reset the FP_STATUS to that default at the end of any TB that + changes the default. We could even (gasp) dynamiclly figure out + what default would be most efficient given the running program. */ + ctx.tb_rm = -1; + /* Similarly for flush-to-zero. */ + ctx.tb_ftz = -1; + num_insns = 0; max_insns = tb->cflags & CF_COUNT_MASK; if (max_insns == 0) max_insns = CF_COUNT_MASK; gen_icount_start(); - for (ret = 0; ret == 0;) { + do { if (unlikely(!QTAILQ_EMPTY(&env->breakpoints))) { QTAILQ_FOREACH(bp, &env->breakpoints, entry) { if (bp->pc == ctx.pc) { @@ -2389,52 +3201,49 @@ } if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO)) gen_io_start(); -#if defined ALPHA_DEBUG_DISAS - insn_count++; - LOG_DISAS("pc " TARGET_FMT_lx " mem_idx %d\n", - ctx.pc, ctx.mem_idx); -#endif insn = ldl_code(ctx.pc); -#if defined ALPHA_DEBUG_DISAS - insn_count++; - LOG_DISAS("opcode %08x %d\n", insn, insn_count); -#endif num_insns++; - ctx.pc += 4; - ret = translate_one(ctxp, insn); - if (ret != 0) - break; - /* if we reach a page boundary or are single stepping, stop - * generation - */ - if (env->singlestep_enabled) { - gen_excp(&ctx, EXCP_DEBUG, 0); - break; - } - - if ((ctx.pc & (TARGET_PAGE_SIZE - 1)) == 0) - break; - if (gen_opc_ptr >= gen_opc_end) - break; + if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP))) { + tcg_gen_debug_insn_start(ctx.pc); + } - if (num_insns >= max_insns) - break; + ctx.pc += 4; + ret = translate_one(ctxp, insn); - if (singlestep) { - break; + if (ret == NO_EXIT) { + /* If we reach a page boundary, are single stepping, + or exhaust instruction count, stop generation. */ + if (env->singlestep_enabled) { + gen_excp(&ctx, EXCP_DEBUG, 0); + ret = EXIT_PC_UPDATED; + } else if ((ctx.pc & (TARGET_PAGE_SIZE - 1)) == 0 + || gen_opc_ptr >= gen_opc_end + || num_insns >= max_insns + || singlestep) { + ret = EXIT_PC_STALE; + } } + } while (ret == NO_EXIT); + + if (tb->cflags & CF_LAST_IO) { + gen_io_end(); } - if (ret != 1 && ret != 3) { + + switch (ret) { + case EXIT_GOTO_TB: + case EXIT_NORETURN: + break; + case EXIT_PC_STALE: tcg_gen_movi_i64(cpu_pc, ctx.pc); + /* FALLTHRU */ + case EXIT_PC_UPDATED: + tcg_gen_exit_tb(0); + break; + default: + abort(); } -#if defined (DO_TB_FLUSH) - gen_helper_tb_flush(); -#endif - if (tb->cflags & CF_LAST_IO) - gen_io_end(); - /* Generate the return instruction */ - tcg_gen_exit_tb(0); + gen_icount_end(tb, num_insns); *gen_opc_ptr = INDEX_op_end; if (search_pc) { @@ -2446,8 +3255,8 @@ tb->size = ctx.pc - pc_start; tb->icount = num_insns; } -#if defined ALPHA_DEBUG_DISAS - log_cpu_state_mask(CPU_LOG_TB_CPU, env, 0); + +#ifdef DEBUG_DISAS if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { qemu_log("IN: %s\n", lookup_symbol(pc_start)); log_target_disas(pc_start, ctx.pc - pc_start, 1); @@ -2466,40 +3275,93 @@ gen_intermediate_code_internal(env, tb, 1); } +struct cpu_def_t { + const char *name; + int implver, amask; +}; + +static const struct cpu_def_t cpu_defs[] = { + { "ev4", IMPLVER_2106x, 0 }, + { "ev5", IMPLVER_21164, 0 }, + { "ev56", IMPLVER_21164, AMASK_BWX }, + { "pca56", IMPLVER_21164, AMASK_BWX | AMASK_MVI }, + { "ev6", IMPLVER_21264, AMASK_BWX | AMASK_FIX | AMASK_MVI | AMASK_TRAP }, + { "ev67", IMPLVER_21264, (AMASK_BWX | AMASK_FIX | AMASK_CIX + | AMASK_MVI | AMASK_TRAP | AMASK_PREFETCH), }, + { "ev68", IMPLVER_21264, (AMASK_BWX | AMASK_FIX | AMASK_CIX + | AMASK_MVI | AMASK_TRAP | AMASK_PREFETCH), }, + { "21064", IMPLVER_2106x, 0 }, + { "21164", IMPLVER_21164, 0 }, + { "21164a", IMPLVER_21164, AMASK_BWX }, + { "21164pc", IMPLVER_21164, AMASK_BWX | AMASK_MVI }, + { "21264", IMPLVER_21264, AMASK_BWX | AMASK_FIX | AMASK_MVI | AMASK_TRAP }, + { "21264a", IMPLVER_21264, (AMASK_BWX | AMASK_FIX | AMASK_CIX + | AMASK_MVI | AMASK_TRAP | AMASK_PREFETCH), } +}; + CPUAlphaState * cpu_alpha_init (const char *cpu_model) { CPUAlphaState *env; - uint64_t hwpcb; + int implver, amask, i, max; env = qemu_mallocz(sizeof(CPUAlphaState)); cpu_exec_init(env); alpha_translate_init(); tlb_flush(env, 1); - /* XXX: should not be hardcoded */ - env->implver = IMPLVER_2106x; + + /* Default to ev67; no reason not to emulate insns by default. */ + implver = IMPLVER_21264; + amask = (AMASK_BWX | AMASK_FIX | AMASK_CIX | AMASK_MVI + | AMASK_TRAP | AMASK_PREFETCH); + + max = ARRAY_SIZE(cpu_defs); + for (i = 0; i < max; i++) { + if (strcmp (cpu_model, cpu_defs[i].name) == 0) { + implver = cpu_defs[i].implver; + amask = cpu_defs[i].amask; + break; + } + } + env->implver = implver; + env->amask = amask; + env->ps = 0x1F00; #if defined (CONFIG_USER_ONLY) env->ps |= 1 << 3; -#endif + cpu_alpha_store_fpcr(env, (FPCR_INVD | FPCR_DZED | FPCR_OVFD + | FPCR_UNFD | FPCR_INED | FPCR_DNOD)); +#else pal_init(env); +#endif + env->lock_addr = -1; + /* Initialize IPR */ - hwpcb = env->ipr[IPR_PCBB]; - env->ipr[IPR_ASN] = 0; - env->ipr[IPR_ASTEN] = 0; - env->ipr[IPR_ASTSR] = 0; - env->ipr[IPR_DATFX] = 0; - /* XXX: fix this */ - // env->ipr[IPR_ESP] = ldq_raw(hwpcb + 8); - // env->ipr[IPR_KSP] = ldq_raw(hwpcb + 0); - // env->ipr[IPR_SSP] = ldq_raw(hwpcb + 16); - // env->ipr[IPR_USP] = ldq_raw(hwpcb + 24); - env->ipr[IPR_FEN] = 0; - env->ipr[IPR_IPL] = 31; - env->ipr[IPR_MCES] = 0; - env->ipr[IPR_PERFMON] = 0; /* Implementation specific */ - // env->ipr[IPR_PTBR] = ldq_raw(hwpcb + 32); - env->ipr[IPR_SISR] = 0; - env->ipr[IPR_VIRBND] = -1ULL; +#if defined (CONFIG_USER_ONLY) + env->ipr[IPR_EXC_ADDR] = 0; + env->ipr[IPR_EXC_SUM] = 0; + env->ipr[IPR_EXC_MASK] = 0; +#else + { + // uint64_t hwpcb; + // hwpcb = env->ipr[IPR_PCBB]; + env->ipr[IPR_ASN] = 0; + env->ipr[IPR_ASTEN] = 0; + env->ipr[IPR_ASTSR] = 0; + env->ipr[IPR_DATFX] = 0; + /* XXX: fix this */ + // env->ipr[IPR_ESP] = ldq_raw(hwpcb + 8); + // env->ipr[IPR_KSP] = ldq_raw(hwpcb + 0); + // env->ipr[IPR_SSP] = ldq_raw(hwpcb + 16); + // env->ipr[IPR_USP] = ldq_raw(hwpcb + 24); + env->ipr[IPR_FEN] = 0; + env->ipr[IPR_IPL] = 31; + env->ipr[IPR_MCES] = 0; + env->ipr[IPR_PERFMON] = 0; /* Implementation specific */ + // env->ipr[IPR_PTBR] = ldq_raw(hwpcb + 32); + env->ipr[IPR_SISR] = 0; + env->ipr[IPR_VIRBND] = -1ULL; + } +#endif qemu_init_vcpu(env); return env; diff -Nru qemu-kvm-0.12.5+noroms/target-arm/cpu.h qemu-kvm-0.14.1/target-arm/cpu.h --- qemu-kvm-0.12.5+noroms/target-arm/cpu.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-arm/cpu.h 2011-05-11 13:29:46.000000000 +0000 @@ -25,6 +25,8 @@ #define CPUState struct CPUARMState +#include "config.h" +#include "qemu-common.h" #include "cpu-defs.h" #include "softfloat.h" @@ -146,16 +148,8 @@ int current_sp; int exception; int pending_exception; - void *nvic; } v7m; - /* Coprocessor IO used by peripherals */ - struct { - ARMReadCPFunc *cp_read; - ARMWriteCPFunc *cp_write; - void *opaque; - } cp[15]; - /* Thumb-2 EE state. */ uint32_t teecr; uint32_t teehbr; @@ -179,7 +173,20 @@ /* scratch space when Tn are not sufficient. */ uint32_t scratch[8]; + /* fp_status is the "normal" fp status. standard_fp_status retains + * values corresponding to the ARM "Standard FPSCR Value", ie + * default-NaN, flush-to-zero, round-to-nearest and is used by + * any operations (generally Neon) which the architecture defines + * as controlled by the standard FPSCR value rather than the FPSCR. + * + * To avoid having to transfer exception bits around, we simply + * say that the FPSCR cumulative exception flags are the logical + * OR of the flags in the two fp statuses. This relies on the + * only thing which needs to read the exception flags being + * an explicit FPSCR read. + */ float_status fp_status; + float_status standard_fp_status; } vfp; uint32_t exclusive_addr; uint32_t exclusive_val; @@ -205,6 +212,14 @@ CPU_COMMON /* These fields after the common ones so they are preserved on reset. */ + + /* Coprocessor IO used by peripherals */ + struct { + ARMReadCPFunc *cp_read; + ARMWriteCPFunc *cp_write; + void *opaque; + } cp[15]; + void *nvic; struct arm_boot_info *boot_info; } CPUARMState; @@ -225,8 +240,6 @@ int mmu_idx, int is_softmuu); #define cpu_handle_mmu_fault cpu_arm_handle_mmu_fault -void cpu_lock(void); -void cpu_unlock(void); static inline void cpu_set_tls(CPUARMState *env, target_ulong newtls) { env->cp15.c13_tls2 = newtls; @@ -300,6 +313,10 @@ } } +/* Return the current FPSCR value. */ +uint32_t vfp_get_fpscr(CPUARMState *env); +void vfp_set_fpscr(CPUARMState *env, uint32_t val); + enum arm_cpu_mode { ARM_CPU_MODE_USR = 0x10, ARM_CPU_MODE_FIQ = 0x11, @@ -353,7 +370,7 @@ return (env->features & (1u << feature)) != 0; } -void arm_cpu_list(FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...)); +void arm_cpu_list(FILE *f, fprintf_function cpu_fprintf); /* Interface between CPU and Interrupt controller. */ void armv7m_nvic_set_pending(void *opaque, int irq); @@ -405,6 +422,9 @@ #define TARGET_PAGE_BITS 10 #endif +#define TARGET_PHYS_ADDR_SPACE_BITS 32 +#define TARGET_VIRT_ADDR_SPACE_BITS 32 + #define cpu_init cpu_arm_init #define cpu_exec cpu_arm_exec #define cpu_gen_code cpu_arm_gen_code @@ -432,24 +452,57 @@ #endif #include "cpu-all.h" -#include "exec-all.h" -static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb) -{ - env->regs[15] = tb->pc; -} +/* Bit usage in the TB flags field: */ +#define ARM_TBFLAG_THUMB_SHIFT 0 +#define ARM_TBFLAG_THUMB_MASK (1 << ARM_TBFLAG_THUMB_SHIFT) +#define ARM_TBFLAG_VECLEN_SHIFT 1 +#define ARM_TBFLAG_VECLEN_MASK (0x7 << ARM_TBFLAG_VECLEN_SHIFT) +#define ARM_TBFLAG_VECSTRIDE_SHIFT 4 +#define ARM_TBFLAG_VECSTRIDE_MASK (0x3 << ARM_TBFLAG_VECSTRIDE_SHIFT) +#define ARM_TBFLAG_PRIV_SHIFT 6 +#define ARM_TBFLAG_PRIV_MASK (1 << ARM_TBFLAG_PRIV_SHIFT) +#define ARM_TBFLAG_VFPEN_SHIFT 7 +#define ARM_TBFLAG_VFPEN_MASK (1 << ARM_TBFLAG_VFPEN_SHIFT) +#define ARM_TBFLAG_CONDEXEC_SHIFT 8 +#define ARM_TBFLAG_CONDEXEC_MASK (0xff << ARM_TBFLAG_CONDEXEC_SHIFT) +/* Bits 31..16 are currently unused. */ + +/* some convenience accessor macros */ +#define ARM_TBFLAG_THUMB(F) \ + (((F) & ARM_TBFLAG_THUMB_MASK) >> ARM_TBFLAG_THUMB_SHIFT) +#define ARM_TBFLAG_VECLEN(F) \ + (((F) & ARM_TBFLAG_VECLEN_MASK) >> ARM_TBFLAG_VECLEN_SHIFT) +#define ARM_TBFLAG_VECSTRIDE(F) \ + (((F) & ARM_TBFLAG_VECSTRIDE_MASK) >> ARM_TBFLAG_VECSTRIDE_SHIFT) +#define ARM_TBFLAG_PRIV(F) \ + (((F) & ARM_TBFLAG_PRIV_MASK) >> ARM_TBFLAG_PRIV_SHIFT) +#define ARM_TBFLAG_VFPEN(F) \ + (((F) & ARM_TBFLAG_VFPEN_MASK) >> ARM_TBFLAG_VFPEN_SHIFT) +#define ARM_TBFLAG_CONDEXEC(F) \ + (((F) & ARM_TBFLAG_CONDEXEC_MASK) >> ARM_TBFLAG_CONDEXEC_SHIFT) static inline void cpu_get_tb_cpu_state(CPUState *env, target_ulong *pc, target_ulong *cs_base, int *flags) { + int privmode; *pc = env->regs[15]; *cs_base = 0; - *flags = env->thumb | (env->vfp.vec_len << 1) - | (env->vfp.vec_stride << 4) | (env->condexec_bits << 8); - if ((env->uncached_cpsr & CPSR_M) != ARM_CPU_MODE_USR) - *flags |= (1 << 6); - if (env->vfp.xregs[ARM_VFP_FPEXC] & (1 << 30)) - *flags |= (1 << 7); + *flags = (env->thumb << ARM_TBFLAG_THUMB_SHIFT) + | (env->vfp.vec_len << ARM_TBFLAG_VECLEN_SHIFT) + | (env->vfp.vec_stride << ARM_TBFLAG_VECSTRIDE_SHIFT) + | (env->condexec_bits << ARM_TBFLAG_CONDEXEC_SHIFT); + if (arm_feature(env, ARM_FEATURE_M)) { + privmode = !((env->v7m.exception == 0) && (env->v7m.control & 1)); + } else { + privmode = (env->uncached_cpsr & CPSR_M) != ARM_CPU_MODE_USR; + } + if (privmode) { + *flags |= ARM_TBFLAG_PRIV_MASK; + } + if (env->vfp.xregs[ARM_VFP_FPEXC] & (1 << 30)) { + *flags |= ARM_TBFLAG_VFPEN_MASK; + } } #endif diff -Nru qemu-kvm-0.12.5+noroms/target-arm/exec.h qemu-kvm-0.14.1/target-arm/exec.h --- qemu-kvm-0.12.5+noroms/target-arm/exec.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-arm/exec.h 2011-05-11 13:29:46.000000000 +0000 @@ -26,14 +26,6 @@ #include "cpu.h" #include "exec-all.h" -static inline void env_to_regs(void) -{ -} - -static inline void regs_to_env(void) -{ -} - static inline int cpu_has_work(CPUState *env) { return (env->interrupt_request & @@ -58,3 +50,9 @@ #endif void raise_exception(int); + +static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb) +{ + env->regs[15] = tb->pc; +} + diff -Nru qemu-kvm-0.12.5+noroms/target-arm/helper.c qemu-kvm-0.14.1/target-arm/helper.c --- qemu-kvm-0.12.5+noroms/target-arm/helper.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-arm/helper.c 2011-05-11 13:29:46.000000000 +0000 @@ -8,6 +8,9 @@ #include "helpers.h" #include "qemu-common.h" #include "host-utils.h" +#if !defined(CONFIG_USER_ONLY) +#include "hw/loader.h" +#endif static uint32_t cortexa9_cp15_c0_c1[8] = { 0x1031, 0x11, 0x000, 0, 0x00100103, 0x20000000, 0x01230000, 0x00002111 }; @@ -73,6 +76,7 @@ memcpy(env->cp15.c0_c1, arm1136_cp15_c0_c1, 8 * sizeof(uint32_t)); memcpy(env->cp15.c0_c2, arm1136_cp15_c0_c2, 8 * sizeof(uint32_t)); env->cp15.c0_cachetype = 0x1dd20d2; + env->cp15.c1_sys = 0x00050078; break; case ARM_CPUID_ARM11MPCORE: set_feature(env, ARM_FEATURE_V6); @@ -106,6 +110,7 @@ env->cp15.c0_ccsid[0] = 0xe007e01a; /* 16k L1 dcache. */ env->cp15.c0_ccsid[1] = 0x2007e01a; /* 16k L1 icache. */ env->cp15.c0_ccsid[2] = 0xf0000000; /* No L2 icache. */ + env->cp15.c1_sys = 0x00c50078; break; case ARM_CPUID_CORTEXA9: set_feature(env, ARM_FEATURE_V6); @@ -127,6 +132,7 @@ env->cp15.c0_clid = (1 << 27) | (1 << 24) | 3; env->cp15.c0_ccsid[0] = 0xe00fe015; /* 16k L1 dcache. */ env->cp15.c0_ccsid[1] = 0x200fe015; /* 16k L1 icache. */ + env->cp15.c1_sys = 0x00c50078; break; case ARM_CPUID_CORTEXM3: set_feature(env, ARM_FEATURE_V6); @@ -200,18 +206,40 @@ cpu_reset_model_id(env, id); #if defined (CONFIG_USER_ONLY) env->uncached_cpsr = ARM_CPU_MODE_USR; + /* For user mode we must enable access to coprocessors */ env->vfp.xregs[ARM_VFP_FPEXC] = 1 << 30; + if (arm_feature(env, ARM_FEATURE_IWMMXT)) { + env->cp15.c15_cpar = 3; + } else if (arm_feature(env, ARM_FEATURE_XSCALE)) { + env->cp15.c15_cpar = 1; + } #else /* SVC mode with interrupts disabled. */ env->uncached_cpsr = ARM_CPU_MODE_SVC | CPSR_A | CPSR_F | CPSR_I; /* On ARMv7-M the CPSR_I is the value of the PRIMASK register, and is - clear at reset. */ - if (IS_M(env)) + clear at reset. Initial SP and PC are loaded from ROM. */ + if (IS_M(env)) { + uint32_t pc; + uint8_t *rom; env->uncached_cpsr &= ~CPSR_I; + rom = rom_ptr(0); + if (rom) { + /* We should really use ldl_phys here, in case the guest + modified flash and reset itself. However images + loaded via -kenrel have not been copied yet, so load the + values directly from there. */ + env->regs[13] = ldl_p(rom); + pc = ldl_p(rom + 4); + env->thumb = pc & 1; + env->regs[15] = pc & ~1; + } + } env->vfp.xregs[ARM_VFP_FPEXC] = 0; env->cp15.c2_base_mask = 0xffffc000u; #endif - env->regs[15] = 0; + set_flush_to_zero(1, &env->vfp.standard_fp_status); + set_flush_inputs_to_zero(1, &env->vfp.standard_fp_status); + set_default_nan_mode(1, &env->vfp.standard_fp_status); tlb_flush(env, 1); } @@ -332,7 +360,7 @@ { 0, NULL} }; -void arm_cpu_list(FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...)) +void arm_cpu_list(FILE *f, fprintf_function cpu_fprintf) { int i; @@ -483,11 +511,6 @@ return 1; } -target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr) -{ - return addr; -} - /* These should probably raise undefined insn exceptions. */ void HELPER(set_cp)(CPUState *env, uint32_t insn, uint32_t val) { @@ -511,7 +534,6 @@ uint32_t HELPER(get_cp15)(CPUState *env, uint32_t insn) { cpu_abort(env, "cp15 insn %08x\n", insn); - return 0; } /* These should probably raise undefined insn exceptions. */ @@ -630,7 +652,7 @@ type = env->regs[15]; if (env->v7m.exception != 0) - armv7m_nvic_complete_irq(env->v7m.nvic, env->v7m.exception); + armv7m_nvic_complete_irq(env->nvic, env->v7m.exception); /* Switch to the target stack. */ switch_v7m_sp(env, (type & 4) != 0); @@ -672,15 +694,15 @@ one we're raising. */ switch (env->exception_index) { case EXCP_UDEF: - armv7m_nvic_set_pending(env->v7m.nvic, ARMV7M_EXCP_USAGE); + armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE); return; case EXCP_SWI: env->regs[15] += 2; - armv7m_nvic_set_pending(env->v7m.nvic, ARMV7M_EXCP_SVC); + armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_SVC); return; case EXCP_PREFETCH_ABORT: case EXCP_DATA_ABORT: - armv7m_nvic_set_pending(env->v7m.nvic, ARMV7M_EXCP_MEM); + armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_MEM); return; case EXCP_BKPT: if (semihosting_enabled) { @@ -692,10 +714,10 @@ return; } } - armv7m_nvic_set_pending(env->v7m.nvic, ARMV7M_EXCP_DEBUG); + armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_DEBUG); return; case EXCP_IRQ: - env->v7m.exception = armv7m_nvic_acknowledge_irq(env->v7m.nvic); + env->v7m.exception = armv7m_nvic_acknowledge_irq(env->nvic); break; case EXCP_EXCEPTION_EXIT: do_v7m_exception_exit(env); @@ -825,11 +847,10 @@ env->spsr = cpsr_read(env); /* Clear IT bits. */ env->condexec_bits = 0; - /* Switch to the new mode, and switch to Arm mode. */ - /* ??? Thumb interrupt handlers not implemented. */ + /* Switch to the new mode, and to the correct instruction set. */ env->uncached_cpsr = (env->uncached_cpsr & ~CPSR_M) | new_mode; env->uncached_cpsr |= mask; - env->thumb = 0; + env->thumb = (env->cp15.c1_sys & (1 << 30)) != 0; env->regs[14] = env->regs[15] + offset; env->regs[15] = addr; env->interrupt_request |= CPU_INTERRUPT_EXITTB; @@ -901,7 +922,8 @@ } static int get_phys_addr_v5(CPUState *env, uint32_t address, int access_type, - int is_user, uint32_t *phys_ptr, int *prot) + int is_user, uint32_t *phys_ptr, int *prot, + target_ulong *page_size) { int code; uint32_t table; @@ -934,6 +956,7 @@ phys_addr = (desc & 0xfff00000) | (address & 0x000fffff); ap = (desc >> 10) & 3; code = 13; + *page_size = 1024 * 1024; } else { /* Lookup l2 entry. */ if (type == 1) { @@ -951,10 +974,12 @@ case 1: /* 64k page. */ phys_addr = (desc & 0xffff0000) | (address & 0xffff); ap = (desc >> (4 + ((address >> 13) & 6))) & 3; + *page_size = 0x10000; break; case 2: /* 4k page. */ phys_addr = (desc & 0xfffff000) | (address & 0xfff); ap = (desc >> (4 + ((address >> 13) & 6))) & 3; + *page_size = 0x1000; break; case 3: /* 1k page. */ if (type == 1) { @@ -969,6 +994,7 @@ phys_addr = (desc & 0xfffffc00) | (address & 0x3ff); } ap = (desc >> 4) & 3; + *page_size = 0x400; break; default: /* Never happens, but compiler isn't smart enough to tell. */ @@ -981,6 +1007,7 @@ /* Access permission fault. */ goto do_fault; } + *prot |= PAGE_EXEC; *phys_ptr = phys_addr; return 0; do_fault: @@ -988,7 +1015,8 @@ } static int get_phys_addr_v6(CPUState *env, uint32_t address, int access_type, - int is_user, uint32_t *phys_ptr, int *prot) + int is_user, uint32_t *phys_ptr, int *prot, + target_ulong *page_size) { int code; uint32_t table; @@ -1028,9 +1056,11 @@ if (desc & (1 << 18)) { /* Supersection. */ phys_addr = (desc & 0xff000000) | (address & 0x00ffffff); + *page_size = 0x1000000; } else { /* Section. */ phys_addr = (desc & 0xfff00000) | (address & 0x000fffff); + *page_size = 0x100000; } ap = ((desc >> 10) & 3) | ((desc >> 13) & 4); xn = desc & (1 << 4); @@ -1047,10 +1077,12 @@ case 1: /* 64k page. */ phys_addr = (desc & 0xffff0000) | (address & 0xffff); xn = desc & (1 << 15); + *page_size = 0x10000; break; case 2: case 3: /* 4k page. */ phys_addr = (desc & 0xfffff000) | (address & 0xfff); xn = desc & 1; + *page_size = 0x1000; break; default: /* Never happens, but compiler isn't smart enough to tell. */ @@ -1058,19 +1090,26 @@ } code = 15; } - if (xn && access_type == 2) - goto do_fault; + if (domain == 3) { + *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + } else { + if (xn && access_type == 2) + goto do_fault; - /* The simplified model uses AP[0] as an access control bit. */ - if ((env->cp15.c1_sys & (1 << 29)) && (ap & 1) == 0) { - /* Access flag fault. */ - code = (code == 15) ? 6 : 3; - goto do_fault; - } - *prot = check_ap(env, ap, domain, access_type, is_user); - if (!*prot) { - /* Access permission fault. */ - goto do_fault; + /* The simplified model uses AP[0] as an access control bit. */ + if ((env->cp15.c1_sys & (1 << 29)) && (ap & 1) == 0) { + /* Access flag fault. */ + code = (code == 15) ? 6 : 3; + goto do_fault; + } + *prot = check_ap(env, ap, domain, access_type, is_user); + if (!*prot) { + /* Access permission fault. */ + goto do_fault; + } + if (!xn) { + *prot |= PAGE_EXEC; + } } *phys_ptr = phys_addr; return 0; @@ -1134,12 +1173,14 @@ /* Bad permission. */ return 1; } + *prot |= PAGE_EXEC; return 0; } static inline int get_phys_addr(CPUState *env, uint32_t address, int access_type, int is_user, - uint32_t *phys_ptr, int *prot) + uint32_t *phys_ptr, int *prot, + target_ulong *page_size) { /* Fast Context Switch Extension. */ if (address < 0x02000000) @@ -1148,17 +1189,19 @@ if ((env->cp15.c1_sys & 1) == 0) { /* MMU/MPU disabled. */ *phys_ptr = address; - *prot = PAGE_READ | PAGE_WRITE; + *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + *page_size = TARGET_PAGE_SIZE; return 0; } else if (arm_feature(env, ARM_FEATURE_MPU)) { + *page_size = TARGET_PAGE_SIZE; return get_phys_addr_mpu(env, address, access_type, is_user, phys_ptr, prot); } else if (env->cp15.c1_sys & (1 << 23)) { return get_phys_addr_v6(env, address, access_type, is_user, phys_ptr, - prot); + prot, page_size); } else { return get_phys_addr_v5(env, address, access_type, is_user, phys_ptr, - prot); + prot, page_size); } } @@ -1166,17 +1209,19 @@ int access_type, int mmu_idx, int is_softmmu) { uint32_t phys_addr; + target_ulong page_size; int prot; int ret, is_user; is_user = mmu_idx == MMU_USER_IDX; - ret = get_phys_addr(env, address, access_type, is_user, &phys_addr, &prot); + ret = get_phys_addr(env, address, access_type, is_user, &phys_addr, &prot, + &page_size); if (ret == 0) { /* Map a single [sub]page. */ phys_addr &= ~(uint32_t)0x3ff; address &= ~(uint32_t)0x3ff; - return tlb_set_page (env, address, phys_addr, prot, mmu_idx, - is_softmmu); + tlb_set_page (env, address, phys_addr, prot, mmu_idx, page_size); + return 0; } if (access_type == 2) { @@ -1196,10 +1241,11 @@ target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr) { uint32_t phys_addr; + target_ulong page_size; int prot; int ret; - ret = get_phys_addr(env, addr, 0, 0, &phys_addr, &prot); + ret = get_phys_addr(env, addr, 0, 0, &phys_addr, &prot, &page_size); if (ret != 0) return -1; @@ -1413,18 +1459,7 @@ tlb_flush(env, 0); break; case 1: /* Invalidate single TLB entry. */ -#if 0 - /* ??? This is wrong for large pages and sections. */ - /* As an ugly hack to make linux work we always flush a 4K - pages. */ - val &= 0xfffff000; - tlb_flush_page(env, val); - tlb_flush_page(env, val + 0x400); - tlb_flush_page(env, val + 0x800); - tlb_flush_page(env, val + 0xc00); -#else - tlb_flush(env, 1); -#endif + tlb_flush_page(env, val & TARGET_PAGE_MASK); break; case 2: /* Invalidate on ASID. */ tlb_flush(env, val == 0); @@ -1491,15 +1526,6 @@ tlb_flush(env, 0); env->cp15.c13_context = val; break; - case 2: - env->cp15.c13_tls1 = val; - break; - case 3: - env->cp15.c13_tls2 = val; - break; - case 4: - env->cp15.c13_tls3 = val; - break; default: goto bad_reg; } @@ -1779,12 +1805,6 @@ return env->cp15.c13_fcse; case 1: return env->cp15.c13_context; - case 2: - return env->cp15.c13_tls1; - case 3: - return env->cp15.c13_tls2; - case 4: - return env->cp15.c13_tls3; default: goto bad_reg; } @@ -1829,12 +1849,20 @@ void HELPER(set_r13_banked)(CPUState *env, uint32_t mode, uint32_t val) { - env->banked_r13[bank_number(mode)] = val; + if ((env->uncached_cpsr & CPSR_M) == mode) { + env->regs[13] = val; + } else { + env->banked_r13[bank_number(mode)] = val; + } } uint32_t HELPER(get_r13_banked)(CPUState *env, uint32_t mode) { - return env->banked_r13[bank_number(mode)]; + if ((env->uncached_cpsr & CPSR_M) == mode) { + return env->regs[13]; + } else { + return env->banked_r13[bank_number(mode)]; + } } uint32_t HELPER(v7m_mrs)(CPUState *env, uint32_t reg) @@ -2225,6 +2253,8 @@ target_bits |= 8; if (host_bits & float_flag_inexact) target_bits |= 0x10; + if (host_bits & float_flag_input_denormal) + target_bits |= 0x80; return target_bits; } @@ -2237,10 +2267,16 @@ | (env->vfp.vec_len << 16) | (env->vfp.vec_stride << 20); i = get_float_exception_flags(&env->vfp.fp_status); + i |= get_float_exception_flags(&env->vfp.standard_fp_status); fpscr |= vfp_exceptbits_from_host(i); return fpscr; } +uint32_t vfp_get_fpscr(CPUState *env) +{ + return HELPER(vfp_get_fpscr)(env); +} + /* Convert vfp exception flags to target form. */ static inline int vfp_exceptbits_to_host(int target_bits) { @@ -2256,6 +2292,8 @@ host_bits |= float_flag_underflow; if (target_bits & 0x10) host_bits |= float_flag_inexact; + if (target_bits & 0x80) + host_bits |= float_flag_input_denormal; return host_bits; } @@ -2288,13 +2326,21 @@ } set_float_rounding_mode(i, &env->vfp.fp_status); } - if (changed & (1 << 24)) + if (changed & (1 << 24)) { set_flush_to_zero((val & (1 << 24)) != 0, &env->vfp.fp_status); + set_flush_inputs_to_zero((val & (1 << 24)) != 0, &env->vfp.fp_status); + } if (changed & (1 << 25)) set_default_nan_mode((val & (1 << 25)) != 0, &env->vfp.fp_status); - i = vfp_exceptbits_to_host((val >> 8) & 0x1f); + i = vfp_exceptbits_to_host(val); set_float_exception_flags(i, &env->vfp.fp_status); + set_float_exception_flags(0, &env->vfp.standard_fp_status); +} + +void vfp_set_fpscr(CPUState *env, uint32_t val) +{ + HELPER(vfp_set_fpscr)(env, val); } #define VFP_HELPER(name, p) HELPER(glue(glue(vfp_,name),p)) @@ -2443,53 +2489,85 @@ /* Float to integer conversion. */ float32 VFP_HELPER(toui, s)(float32 x, CPUState *env) { + if (float32_is_any_nan(x)) { + return float32_zero; + } return vfp_itos(float32_to_uint32(x, &env->vfp.fp_status)); } float32 VFP_HELPER(toui, d)(float64 x, CPUState *env) { + if (float64_is_any_nan(x)) { + return float32_zero; + } return vfp_itos(float64_to_uint32(x, &env->vfp.fp_status)); } float32 VFP_HELPER(tosi, s)(float32 x, CPUState *env) { + if (float32_is_any_nan(x)) { + return float32_zero; + } return vfp_itos(float32_to_int32(x, &env->vfp.fp_status)); } float32 VFP_HELPER(tosi, d)(float64 x, CPUState *env) { + if (float64_is_any_nan(x)) { + return float32_zero; + } return vfp_itos(float64_to_int32(x, &env->vfp.fp_status)); } float32 VFP_HELPER(touiz, s)(float32 x, CPUState *env) { + if (float32_is_any_nan(x)) { + return float32_zero; + } return vfp_itos(float32_to_uint32_round_to_zero(x, &env->vfp.fp_status)); } float32 VFP_HELPER(touiz, d)(float64 x, CPUState *env) { + if (float64_is_any_nan(x)) { + return float32_zero; + } return vfp_itos(float64_to_uint32_round_to_zero(x, &env->vfp.fp_status)); } float32 VFP_HELPER(tosiz, s)(float32 x, CPUState *env) { + if (float32_is_any_nan(x)) { + return float32_zero; + } return vfp_itos(float32_to_int32_round_to_zero(x, &env->vfp.fp_status)); } float32 VFP_HELPER(tosiz, d)(float64 x, CPUState *env) { + if (float64_is_any_nan(x)) { + return float32_zero; + } return vfp_itos(float64_to_int32_round_to_zero(x, &env->vfp.fp_status)); } /* floating point conversion */ float64 VFP_HELPER(fcvtd, s)(float32 x, CPUState *env) { - return float32_to_float64(x, &env->vfp.fp_status); + float64 r = float32_to_float64(x, &env->vfp.fp_status); + /* ARM requires that S<->D conversion of any kind of NaN generates + * a quiet NaN by forcing the most significant frac bit to 1. + */ + return float64_maybe_silence_nan(r); } float32 VFP_HELPER(fcvts, d)(float64 x, CPUState *env) { - return float64_to_float32(x, &env->vfp.fp_status); + float32 r = float64_to_float32(x, &env->vfp.fp_status); + /* ARM requires that S<->D conversion of any kind of NaN generates + * a quiet NaN by forcing the most significant frac bit to 1. + */ + return float32_maybe_silence_nan(r); } /* VFP3 fixed point conversion. */ @@ -2497,15 +2575,18 @@ ftype VFP_HELPER(name##to, p)(ftype x, uint32_t shift, CPUState *env) \ { \ ftype tmp; \ - tmp = sign##int32_to_##ftype ((itype)vfp_##p##toi(x), \ + tmp = sign##int32_to_##ftype ((itype##_t)vfp_##p##toi(x), \ &env->vfp.fp_status); \ return ftype##_scalbn(tmp, -(int)shift, &env->vfp.fp_status); \ } \ ftype VFP_HELPER(to##name, p)(ftype x, uint32_t shift, CPUState *env) \ { \ ftype tmp; \ + if (ftype##_is_any_nan(x)) { \ + return ftype##_zero; \ + } \ tmp = ftype##_scalbn(x, shift, &env->vfp.fp_status); \ - return vfp_ito##p((itype)ftype##_to_##sign##int32_round_to_zero(tmp, \ + return vfp_ito##p(ftype##_to_##itype##_round_to_zero(tmp, \ &env->vfp.fp_status)); \ } @@ -2543,9 +2624,17 @@ float32 HELPER(rsqrts_f32)(float32 a, float32 b, CPUState *env) { - float_status *s = &env->vfp.fp_status; + float_status *s = &env->vfp.standard_fp_status; + float32 two = int32_to_float32(2, s); float32 three = int32_to_float32(3, s); - return float32_sub(three, float32_mul(a, b, s), s); + float32 product; + if ((float32_is_infinity(a) && float32_is_zero_or_denormal(b)) || + (float32_is_infinity(b) && float32_is_zero_or_denormal(a))) { + product = float32_zero; + } else { + product = float32_mul(a, b, s); + } + return float32_div(float32_sub(three, product, s), two, s); } /* NEON helpers. */ diff -Nru qemu-kvm-0.12.5+noroms/target-arm/helpers.h qemu-kvm-0.14.1/target-arm/helpers.h --- qemu-kvm-0.12.5+noroms/target-arm/helpers.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-arm/helpers.h 2011-05-11 13:29:46.000000000 +0000 @@ -137,10 +137,6 @@ DEF_HELPER_2(recpe_u32, i32, i32, env) DEF_HELPER_2(rsqrte_u32, i32, i32, env) DEF_HELPER_4(neon_tbl, i32, i32, i32, i32, i32) -DEF_HELPER_2(neon_add_saturate_u64, i64, i64, i64) -DEF_HELPER_2(neon_add_saturate_s64, i64, i64, i64) -DEF_HELPER_2(neon_sub_saturate_u64, i64, i64, i64) -DEF_HELPER_2(neon_sub_saturate_s64, i64, i64, i64) DEF_HELPER_2(add_cc, i32, i32, i32) DEF_HELPER_2(adc_cc, i32, i32, i32) @@ -160,10 +156,18 @@ DEF_HELPER_3(neon_qadd_s8, i32, env, i32, i32) DEF_HELPER_3(neon_qadd_u16, i32, env, i32, i32) DEF_HELPER_3(neon_qadd_s16, i32, env, i32, i32) +DEF_HELPER_3(neon_qadd_u32, i32, env, i32, i32) +DEF_HELPER_3(neon_qadd_s32, i32, env, i32, i32) DEF_HELPER_3(neon_qsub_u8, i32, env, i32, i32) DEF_HELPER_3(neon_qsub_s8, i32, env, i32, i32) DEF_HELPER_3(neon_qsub_u16, i32, env, i32, i32) DEF_HELPER_3(neon_qsub_s16, i32, env, i32, i32) +DEF_HELPER_3(neon_qsub_u32, i32, env, i32, i32) +DEF_HELPER_3(neon_qsub_s32, i32, env, i32, i32) +DEF_HELPER_3(neon_qadd_u64, i64, env, i64, i64) +DEF_HELPER_3(neon_qadd_s64, i64, env, i64, i64) +DEF_HELPER_3(neon_qsub_u64, i64, env, i64, i64) +DEF_HELPER_3(neon_qsub_s64, i64, env, i64, i64) DEF_HELPER_2(neon_hadd_s8, i32, i32, i32) DEF_HELPER_2(neon_hadd_u8, i32, i32, i32) @@ -249,6 +253,10 @@ DEF_HELPER_3(neon_qshl_s32, i32, env, i32, i32) DEF_HELPER_3(neon_qshl_u64, i64, env, i64, i64) DEF_HELPER_3(neon_qshl_s64, i64, env, i64, i64) +DEF_HELPER_3(neon_qshlu_s8, i32, env, i32, i32); +DEF_HELPER_3(neon_qshlu_s16, i32, env, i32, i32); +DEF_HELPER_3(neon_qshlu_s32, i32, env, i32, i32); +DEF_HELPER_3(neon_qshlu_s64, i64, env, i64, i64); DEF_HELPER_3(neon_qrshl_u8, i32, env, i32, i32) DEF_HELPER_3(neon_qrshl_s8, i32, env, i32, i32) DEF_HELPER_3(neon_qrshl_u16, i32, env, i32, i32) diff -Nru qemu-kvm-0.12.5+noroms/target-arm/neon_helper.c qemu-kvm-0.14.1/target-arm/neon_helper.c --- qemu-kvm-0.12.5+noroms/target-arm/neon_helper.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-arm/neon_helper.c 2011-05-11 13:29:46.000000000 +0000 @@ -198,6 +198,28 @@ #undef NEON_FN #undef NEON_USAT +uint32_t HELPER(neon_qadd_u32)(CPUState *env, uint32_t a, uint32_t b) +{ + uint32_t res = a + b; + if (res < a) { + SET_QC(); + res = ~0; + } + return res; +} + +uint64_t HELPER(neon_qadd_u64)(CPUState *env, uint64_t src1, uint64_t src2) +{ + uint64_t res; + + res = src1 + src2; + if (res < src1) { + SET_QC(); + res = ~(uint64_t)0; + } + return res; +} + #define NEON_SSAT(dest, src1, src2, type) do { \ int32_t tmp = (uint32_t)src1 + (uint32_t)src2; \ if (tmp != (type)tmp) { \ @@ -218,6 +240,28 @@ #undef NEON_FN #undef NEON_SSAT +uint32_t HELPER(neon_qadd_s32)(CPUState *env, uint32_t a, uint32_t b) +{ + uint32_t res = a + b; + if (((res ^ a) & SIGNBIT) && !((a ^ b) & SIGNBIT)) { + SET_QC(); + res = ~(((int32_t)a >> 31) ^ SIGNBIT); + } + return res; +} + +uint64_t HELPER(neon_qadd_s64)(CPUState *env, uint64_t src1, uint64_t src2) +{ + uint64_t res; + + res = src1 + src2; + if (((res ^ src1) & SIGNBIT64) && !((src1 ^ src2) & SIGNBIT64)) { + SET_QC(); + res = ((int64_t)src1 >> 63) ^ ~SIGNBIT64; + } + return res; +} + #define NEON_USAT(dest, src1, src2, type) do { \ uint32_t tmp = (uint32_t)src1 - (uint32_t)src2; \ if (tmp != (type)tmp) { \ @@ -234,6 +278,29 @@ #undef NEON_FN #undef NEON_USAT +uint32_t HELPER(neon_qsub_u32)(CPUState *env, uint32_t a, uint32_t b) +{ + uint32_t res = a - b; + if (res > a) { + SET_QC(); + res = 0; + } + return res; +} + +uint64_t HELPER(neon_qsub_u64)(CPUState *env, uint64_t src1, uint64_t src2) +{ + uint64_t res; + + if (src1 < src2) { + SET_QC(); + res = 0; + } else { + res = src1 - src2; + } + return res; +} + #define NEON_SSAT(dest, src1, src2, type) do { \ int32_t tmp = (uint32_t)src1 - (uint32_t)src2; \ if (tmp != (type)tmp) { \ @@ -254,6 +321,28 @@ #undef NEON_FN #undef NEON_SSAT +uint32_t HELPER(neon_qsub_s32)(CPUState *env, uint32_t a, uint32_t b) +{ + uint32_t res = a - b; + if (((res ^ a) & SIGNBIT) && ((a ^ b) & SIGNBIT)) { + SET_QC(); + res = ~(((int32_t)a >> 31) ^ SIGNBIT); + } + return res; +} + +uint64_t HELPER(neon_qsub_s64)(CPUState *env, uint64_t src1, uint64_t src2) +{ + uint64_t res; + + res = src1 - src2; + if (((res ^ src1) & SIGNBIT64) && ((src1 ^ src2) & SIGNBIT64)) { + SET_QC(); + res = ((int64_t)src1 >> 63) ^ ~SIGNBIT64; + } + return res; +} + #define NEON_FN(dest, src1, src2) dest = (src1 + src2) >> 1 NEON_VOP(hadd_s8, neon_s8, 4) NEON_VOP(hadd_u8, neon_u8, 4) @@ -560,8 +649,6 @@ if (val) { val = ~(uint64_t)0; SET_QC(); - } else { - val = 0; } } else if (shift <= -64) { val = 0; @@ -582,9 +669,15 @@ int8_t tmp; \ tmp = (int8_t)src2; \ if (tmp >= (ssize_t)sizeof(src1) * 8) { \ - if (src1) \ + if (src1) { \ SET_QC(); \ - dest = src1 >> 31; \ + dest = (uint32_t)(1 << (sizeof(src1) * 8 - 1)); \ + if (src1 > 0) { \ + dest--; \ + } \ + } else { \ + dest = src1; \ + } \ } else if (tmp <= -(ssize_t)sizeof(src1) * 8) { \ dest = src1 >> 31; \ } else if (tmp < 0) { \ @@ -593,7 +686,10 @@ dest = src1 << tmp; \ if ((dest >> tmp) != src1) { \ SET_QC(); \ - dest = src2 >> 31; \ + dest = (uint32_t)(1 << (sizeof(src1) * 8 - 1)); \ + if (src1 > 0) { \ + dest--; \ + } \ } \ }} while (0) NEON_VOP_ENV(qshl_s8, neon_s8, 4) @@ -608,9 +704,9 @@ if (shift >= 64) { if (val) { SET_QC(); - val = (val >> 63) & ~SIGNBIT64; + val = (val >> 63) ^ ~SIGNBIT64; } - } else if (shift <= 64) { + } else if (shift <= -64) { val >>= 63; } else if (shift < 0) { val >>= -shift; @@ -625,6 +721,53 @@ return val; } +#define NEON_FN(dest, src1, src2) do { \ + if (src1 & (1 << (sizeof(src1) * 8 - 1))) { \ + SET_QC(); \ + dest = 0; \ + } else { \ + int8_t tmp; \ + tmp = (int8_t)src2; \ + if (tmp >= (ssize_t)sizeof(src1) * 8) { \ + if (src1) { \ + SET_QC(); \ + dest = ~0; \ + } else { \ + dest = 0; \ + } \ + } else if (tmp <= -(ssize_t)sizeof(src1) * 8) { \ + dest = 0; \ + } else if (tmp < 0) { \ + dest = src1 >> -tmp; \ + } else { \ + dest = src1 << tmp; \ + if ((dest >> tmp) != src1) { \ + SET_QC(); \ + dest = ~0; \ + } \ + } \ + }} while (0) +NEON_VOP_ENV(qshlu_s8, neon_u8, 4) +NEON_VOP_ENV(qshlu_s16, neon_u16, 2) +#undef NEON_FN + +uint32_t HELPER(neon_qshlu_s32)(CPUState *env, uint32_t valop, uint32_t shiftop) +{ + if ((int32_t)valop < 0) { + SET_QC(); + return 0; + } + return helper_neon_qshl_u32(env, valop, shiftop); +} + +uint64_t HELPER(neon_qshlu_s64)(CPUState *env, uint64_t valop, uint64_t shiftop) +{ + if ((int64_t)valop < 0) { + SET_QC(); + return 0; + } + return helper_neon_qshl_u64(env, valop, shiftop); +} /* FIXME: This is wrong. */ #define NEON_FN(dest, src1, src2) do { \ @@ -826,8 +969,9 @@ if ((tmp ^ (tmp << 1)) & SIGNBIT) { \ SET_QC(); \ tmp = (tmp >> 31) ^ ~SIGNBIT; \ + } else { \ + tmp <<= 1; \ } \ - tmp <<= 1; \ if (round) { \ int32_t old = tmp; \ tmp += 1 << 15; \ diff -Nru qemu-kvm-0.12.5+noroms/target-arm/op_helper.c qemu-kvm-0.14.1/target-arm/op_helper.c --- qemu-kvm-0.12.5+noroms/target-arm/op_helper.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-arm/op_helper.c 2011-05-11 13:29:46.000000000 +0000 @@ -28,20 +28,6 @@ cpu_loop_exit(); } -/* thread support */ - -static spinlock_t global_cpu_lock = SPIN_LOCK_UNLOCKED; - -void cpu_lock(void) -{ - spin_lock(&global_cpu_lock); -} - -void cpu_unlock(void) -{ - spin_unlock(&global_cpu_lock); -} - uint32_t HELPER(neon_tbl)(uint32_t ireg, uint32_t def, uint32_t rn, uint32_t maxindex) { @@ -438,52 +424,3 @@ return ((uint32_t)x >> shift) | (x << (32 - shift)); } } - -uint64_t HELPER(neon_add_saturate_s64)(uint64_t src1, uint64_t src2) -{ - uint64_t res; - - res = src1 + src2; - if (((res ^ src1) & SIGNBIT64) && !((src1 ^ src2) & SIGNBIT64)) { - env->QF = 1; - res = ((int64_t)src1 >> 63) ^ ~SIGNBIT64; - } - return res; -} - -uint64_t HELPER(neon_add_saturate_u64)(uint64_t src1, uint64_t src2) -{ - uint64_t res; - - res = src1 + src2; - if (res < src1) { - env->QF = 1; - res = ~(uint64_t)0; - } - return res; -} - -uint64_t HELPER(neon_sub_saturate_s64)(uint64_t src1, uint64_t src2) -{ - uint64_t res; - - res = src1 - src2; - if (((res ^ src1) & SIGNBIT64) && ((src1 ^ src2) & SIGNBIT64)) { - env->QF = 1; - res = ((int64_t)src1 >> 63) ^ ~SIGNBIT64; - } - return res; -} - -uint64_t HELPER(neon_sub_saturate_u64)(uint64_t src1, uint64_t src2) -{ - uint64_t res; - - if (src1 < src2) { - env->QF = 1; - res = 0; - } else { - res = src1 - src2; - } - return res; -} diff -Nru qemu-kvm-0.12.5+noroms/target-arm/translate.c qemu-kvm-0.14.1/target-arm/translate.c --- qemu-kvm-0.12.5+noroms/target-arm/translate.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-arm/translate.c 2011-05-11 13:29:46.000000000 +0000 @@ -59,8 +59,13 @@ #if !defined(CONFIG_USER_ONLY) int user; #endif + int vfp_enabled; + int vec_len; + int vec_stride; } DisasContext; +static uint32_t gen_opc_condexec_bits[OPC_BUF_SIZE]; + #if defined(CONFIG_USER_ONLY) #define IS_USER(s) 1 #else @@ -250,13 +255,9 @@ /* Byteswap low halfword and sign extend. */ static void gen_revsh(TCGv var) { - TCGv tmp = new_tmp(); - tcg_gen_shri_i32(tmp, var, 8); - tcg_gen_andi_i32(tmp, tmp, 0x00ff); - tcg_gen_shli_i32(var, var, 8); - tcg_gen_ext8s_i32(var, var); - tcg_gen_or_i32(var, var, tmp); - dead_tmp(tmp); + tcg_gen_ext16u_i32(var, var); + tcg_gen_bswap16_i32(var, var); + tcg_gen_ext16s_i32(var, var); } /* Unsigned bitfield extract. */ @@ -291,11 +292,32 @@ tcg_gen_or_i32(dest, base, val); } -/* Round the top 32 bits of a 64-bit value. */ -static void gen_roundqd(TCGv a, TCGv b) +/* Return (b << 32) + a. Mark inputs as dead */ +static TCGv_i64 gen_addq_msw(TCGv_i64 a, TCGv b) +{ + TCGv_i64 tmp64 = tcg_temp_new_i64(); + + tcg_gen_extu_i32_i64(tmp64, b); + dead_tmp(b); + tcg_gen_shli_i64(tmp64, tmp64, 32); + tcg_gen_add_i64(a, tmp64, a); + + tcg_temp_free_i64(tmp64); + return a; +} + +/* Return (b << 32) - a. Mark inputs as dead. */ +static TCGv_i64 gen_subq_msw(TCGv_i64 a, TCGv b) { - tcg_gen_shri_i32(a, a, 31); - tcg_gen_add_i32(a, a, b); + TCGv_i64 tmp64 = tcg_temp_new_i64(); + + tcg_gen_extu_i32_i64(tmp64, b); + dead_tmp(b); + tcg_gen_shli_i64(tmp64, tmp64, 32); + tcg_gen_sub_i64(a, tmp64, a); + + tcg_temp_free_i64(tmp64); + return a; } /* FIXME: Most targets have native widening multiplication. @@ -329,22 +351,6 @@ return tmp1; } -/* Signed 32x32->64 multiply. */ -static void gen_imull(TCGv a, TCGv b) -{ - TCGv_i64 tmp1 = tcg_temp_new_i64(); - TCGv_i64 tmp2 = tcg_temp_new_i64(); - - tcg_gen_ext_i32_i64(tmp1, a); - tcg_gen_ext_i32_i64(tmp2, b); - tcg_gen_mul_i64(tmp1, tmp1, tmp2); - tcg_temp_free_i64(tmp2); - tcg_gen_trunc_i64_i32(a, tmp1); - tcg_gen_shri_i64(tmp1, tmp1, 32); - tcg_gen_trunc_i64_i32(b, tmp1); - tcg_temp_free_i64(tmp1); -} - /* Swap low and high halfwords. */ static void gen_swap_half(TCGv var) { @@ -1131,6 +1137,7 @@ static inline void iwmmxt_store_creg(int reg, TCGv var) { tcg_gen_st_i32(var, cpu_env, offsetof(CPUState, iwmmxt.cregs[reg])); + dead_tmp(var); } static inline void gen_op_iwmmxt_movq_wRn_M0(int rn) @@ -1415,6 +1422,7 @@ } } } + dead_tmp(addr); return 0; } @@ -2455,6 +2463,55 @@ return 0; } +static int cp15_tls_load_store(CPUState *env, DisasContext *s, uint32_t insn, uint32_t rd) +{ + TCGv tmp; + int cpn = (insn >> 16) & 0xf; + int cpm = insn & 0xf; + int op = ((insn >> 5) & 7) | ((insn >> 18) & 0x38); + + if (!arm_feature(env, ARM_FEATURE_V6K)) + return 0; + + if (!(cpn == 13 && cpm == 0)) + return 0; + + if (insn & ARM_CP_RW_BIT) { + switch (op) { + case 2: + tmp = load_cpu_field(cp15.c13_tls1); + break; + case 3: + tmp = load_cpu_field(cp15.c13_tls2); + break; + case 4: + tmp = load_cpu_field(cp15.c13_tls3); + break; + default: + return 0; + } + store_reg(s, rd, tmp); + + } else { + tmp = load_reg(s, rd); + switch (op) { + case 2: + store_cpu_field(tmp, cp15.c13_tls1); + break; + case 3: + store_cpu_field(tmp, cp15.c13_tls2); + break; + case 4: + store_cpu_field(tmp, cp15.c13_tls3); + break; + default: + dead_tmp(tmp); + return 0; + } + } + return 1; +} + /* Disassemble system coprocessor (cp15) instruction. Return nonzero if instruction is not defined. */ static int disas_cp15_insn(CPUState *env, DisasContext *s, uint32_t insn) @@ -2489,6 +2546,10 @@ return 0; } rd = (insn >> 12) & 0xf; + + if (cp15_tls_load_store(env, s, insn, rd)) + return 0; + tmp2 = tcg_const_i32(insn); if (insn & ARM_CP_RW_BIT) { tmp = new_tmp(); @@ -2547,12 +2608,6 @@ dead_tmp(tmp); } -static inline int -vfp_enabled(CPUState * env) -{ - return ((env->vfp.xregs[ARM_VFP_FPEXC] & (1 << 30)) != 0); -} - static void gen_neon_dup_u8(TCGv var, int shift) { TCGv tmp = new_tmp(); @@ -2597,7 +2652,7 @@ if (!arm_feature(env, ARM_FEATURE_VFP)) return 1; - if (!vfp_enabled(env)) { + if (!s->vfp_enabled) { /* VFP disabled. Only allow fmxr/fmrx to/from some control regs. */ if ((insn & 0x0fe00fff) != 0x0ee00a10) return 1; @@ -2815,16 +2870,18 @@ VFP_DREG_N(rn, insn); } - if (op == 15 && (rn == 15 || rn > 17)) { + if (op == 15 && (rn == 15 || ((rn & 0x1c) == 0x18))) { /* Integer or single precision destination. */ rd = VFP_SREG_D(insn); } else { VFP_DREG_D(rd, insn); } - - if (op == 15 && (rn == 16 || rn == 17)) { - /* Integer source. */ - rm = ((insn << 1) & 0x1e) | ((insn >> 5) & 1); + if (op == 15 && + (((rn & 0x1c) == 0x10) || ((rn & 0x14) == 0x14))) { + /* VCVT from int is always from S reg regardless of dp bit. + * VCVT with immediate frac_bits has same format as SREG_M + */ + rm = VFP_SREG_M(insn); } else { VFP_DREG_M(rm, insn); } @@ -2836,10 +2893,13 @@ } else { rd = VFP_SREG_D(insn); } + /* NB that we implicitly rely on the encoding for the frac_bits + * in VCVT of fixed to float being the same as that of an SREG_M + */ rm = VFP_SREG_M(insn); } - veclen = env->vfp.vec_len; + veclen = s->vec_len; if (op == 15 && rn > 3) veclen = 0; @@ -2860,9 +2920,9 @@ veclen = 0; } else { if (dp) - delta_d = (env->vfp.vec_stride >> 1) + 1; + delta_d = (s->vec_stride >> 1) + 1; else - delta_d = env->vfp.vec_stride + 1; + delta_d = s->vec_stride + 1; if ((rm & bank_mask) == 0) { /* mixed scalar/vector */ @@ -3124,8 +3184,8 @@ /* Write back the result. */ if (op == 15 && (rn >= 8 && rn <= 11)) ; /* Comparison, do nothing. */ - else if (op == 15 && rn > 17) - /* Integer result. */ + else if (op == 15 && dp && ((rn & 0x1c) == 0x18)) + /* VCVT double to int: always integer result. */ gen_mov_vreg_F0(0, rd); else if (op == 15 && rn == 15) /* conversion */ @@ -3427,6 +3487,14 @@ } } +static void gen_exception_insn(DisasContext *s, int offset, int excp) +{ + gen_set_condexec(s); + gen_set_pc_im(s->pc - offset); + gen_exception(excp); + s->is_jmp = DISAS_JUMP; +} + static void gen_nop_hint(DisasContext *s, int val) { switch (val) { @@ -3471,12 +3539,6 @@ #define gen_helper_neon_pmin_s32 gen_helper_neon_min_s32 #define gen_helper_neon_pmin_u32 gen_helper_neon_min_u32 -/* FIXME: This is wrong. They set the wrong overflow bit. */ -#define gen_helper_neon_qadd_s32(a, e, b, c) gen_helper_add_saturate(a, b, c) -#define gen_helper_neon_qadd_u32(a, e, b, c) gen_helper_add_usaturate(a, b, c) -#define gen_helper_neon_qsub_s32(a, e, b, c) gen_helper_sub_saturate(a, b, c) -#define gen_helper_neon_qsub_u32(a, e, b, c) gen_helper_sub_usaturate(a, b, c) - #define GEN_NEON_INTEGER_OP_ENV(name) do { \ switch ((size << 1) | u) { \ case 0: \ @@ -3540,14 +3602,14 @@ { TCGv tmp; if (size == 1) { - tmp = neon_load_reg(reg >> 1, reg & 1); - } else { - tmp = neon_load_reg(reg >> 2, (reg >> 1) & 1); - if (reg & 1) { - gen_neon_dup_low16(tmp); - } else { + tmp = neon_load_reg(reg & 7, reg >> 4); + if (reg & 8) { gen_neon_dup_high16(tmp); + } else { + gen_neon_dup_low16(tmp); } + } else { + tmp = neon_load_reg(reg & 15, reg >> 4); } return tmp; } @@ -3743,7 +3805,7 @@ TCGv tmp2; TCGv_i64 tmp64; - if (!vfp_enabled(env)) + if (!s->vfp_enabled) return 1; VFP_DREG_D(rd, insn); rn = (insn >> 16) & 0xf; @@ -3799,7 +3861,8 @@ tcg_gen_addi_i32(addr, addr, stride); tmp2 = gen_ld16u(addr, IS_USER(s)); tcg_gen_addi_i32(addr, addr, stride); - gen_bfi(tmp, tmp, tmp2, 16, 0xffff); + tcg_gen_shli_i32(tmp2, tmp2, 16); + tcg_gen_or_i32(tmp, tmp, tmp2); dead_tmp(tmp2); neon_store_reg(rd, pass, tmp); } else { @@ -3820,7 +3883,8 @@ if (n == 0) { tmp2 = tmp; } else { - gen_bfi(tmp2, tmp2, tmp, n * 8, 0xff); + tcg_gen_shli_i32(tmp, tmp, n * 8); + tcg_gen_or_i32(tmp2, tmp2, tmp); dead_tmp(tmp); } } @@ -4113,6 +4177,13 @@ break; default: abort(); } + + /* gen_helper_neon_mull_[su]{8|16} do not free their parameters. + Don't forget to clean them now. */ + if (size < 2) { + dead_tmp(a); + dead_tmp(b); + } } /* Translate a NEON data processing instruction. Return nonzero if the @@ -4136,7 +4207,7 @@ TCGv tmp, tmp2, tmp3, tmp4, tmp5; TCGv_i64 tmp64; - if (!vfp_enabled(env)) + if (!s->vfp_enabled) return 1; q = (insn & (1 << 6)) != 0; u = (insn >> 24) & 1; @@ -4156,16 +4227,20 @@ switch (op) { case 1: /* VQADD */ if (u) { - gen_helper_neon_add_saturate_u64(CPU_V001); + gen_helper_neon_qadd_u64(cpu_V0, cpu_env, + cpu_V0, cpu_V1); } else { - gen_helper_neon_add_saturate_s64(CPU_V001); + gen_helper_neon_qadd_s64(cpu_V0, cpu_env, + cpu_V0, cpu_V1); } break; case 5: /* VQSUB */ if (u) { - gen_helper_neon_sub_saturate_u64(CPU_V001); + gen_helper_neon_qsub_u64(cpu_V0, cpu_env, + cpu_V0, cpu_V1); } else { - gen_helper_neon_sub_saturate_s64(CPU_V001); + gen_helper_neon_qsub_s64(cpu_V0, cpu_env, + cpu_V0, cpu_V1); } break; case 8: /* VSHL */ @@ -4178,9 +4253,9 @@ case 9: /* VQSHL */ if (u) { gen_helper_neon_qshl_u64(cpu_V0, cpu_env, - cpu_V0, cpu_V0); + cpu_V1, cpu_V0); } else { - gen_helper_neon_qshl_s64(cpu_V1, cpu_env, + gen_helper_neon_qshl_s64(cpu_V0, cpu_env, cpu_V1, cpu_V0); } break; @@ -4589,19 +4664,27 @@ case 5: /* VSHL, VSLI */ gen_helper_neon_shl_u64(cpu_V0, cpu_V0, cpu_V1); break; - case 6: /* VQSHL */ - if (u) - gen_helper_neon_qshl_u64(cpu_V0, cpu_env, cpu_V0, cpu_V1); - else - gen_helper_neon_qshl_s64(cpu_V0, cpu_env, cpu_V0, cpu_V1); + case 6: /* VQSHLU */ + if (u) { + gen_helper_neon_qshlu_s64(cpu_V0, cpu_env, + cpu_V0, cpu_V1); + } else { + return 1; + } break; - case 7: /* VQSHLU */ - gen_helper_neon_qshl_u64(cpu_V0, cpu_env, cpu_V0, cpu_V1); + case 7: /* VQSHL */ + if (u) { + gen_helper_neon_qshl_u64(cpu_V0, cpu_env, + cpu_V0, cpu_V1); + } else { + gen_helper_neon_qshl_s64(cpu_V0, cpu_env, + cpu_V0, cpu_V1); + } break; } if (op == 1 || op == 3) { /* Accumulate. */ - neon_load_reg64(cpu_V0, rd + pass); + neon_load_reg64(cpu_V1, rd + pass); tcg_gen_add_i64(cpu_V0, cpu_V0, cpu_V1); } else if (op == 4 || (op == 5 && u)) { /* Insert */ @@ -4635,24 +4718,37 @@ default: return 1; } break; - case 6: /* VQSHL */ - GEN_NEON_INTEGER_OP_ENV(qshl); - break; - case 7: /* VQSHLU */ + case 6: /* VQSHLU */ + if (!u) { + return 1; + } switch (size) { - case 0: gen_helper_neon_qshl_u8(tmp, cpu_env, tmp, tmp2); break; - case 1: gen_helper_neon_qshl_u16(tmp, cpu_env, tmp, tmp2); break; - case 2: gen_helper_neon_qshl_u32(tmp, cpu_env, tmp, tmp2); break; - default: return 1; + case 0: + gen_helper_neon_qshlu_s8(tmp, cpu_env, + tmp, tmp2); + break; + case 1: + gen_helper_neon_qshlu_s16(tmp, cpu_env, + tmp, tmp2); + break; + case 2: + gen_helper_neon_qshlu_s32(tmp, cpu_env, + tmp, tmp2); + break; + default: + return 1; } break; + case 7: /* VQSHL */ + GEN_NEON_INTEGER_OP_ENV(qshl); + break; } dead_tmp(tmp2); if (op == 1 || op == 3) { /* Accumulate. */ tmp2 = neon_load_reg(rd, pass); - gen_neon_add(size, tmp2, tmp); + gen_neon_add(size, tmp, tmp2); dead_tmp(tmp2); } else if (op == 4 || (op == 5 && u)) { /* Insert */ @@ -4756,7 +4852,7 @@ if (size == 3) { tcg_temp_free_i64(tmp64); } else { - dead_tmp(tmp2); + tcg_temp_free_i32(tmp2); } } else if (op == 10) { /* VSHLL */ @@ -4788,11 +4884,15 @@ } neon_store_reg64(cpu_V0, rd + pass); } - } else if (op == 15 || op == 16) { + } else if (op >= 14) { /* VCVT fixed-point. */ + /* We have already masked out the must-be-1 top bit of imm6, + * hence this 32-shift where the ARM ARM has 64-imm6. + */ + shift = 32 - shift; for (pass = 0; pass < (q ? 4 : 2); pass++) { tcg_gen_ld_f32(cpu_F0s, cpu_env, neon_reg_offset(rm, pass)); - if (op & 1) { + if (!(op & 1)) { if (u) gen_vfp_ulto(0, shift); else @@ -4835,7 +4935,7 @@ imm = (imm << 8) | (imm << 24); break; case 12: - imm = (imm < 8) | 0xff; + imm = (imm << 8) | 0xff; break; case 13: imm = (imm << 16) | 0xffff; @@ -4957,7 +5057,7 @@ case 0: case 1: case 4: /* VADDL, VADDW, VADDHN, VRADDHN */ gen_neon_addl(size); break; - case 2: case 3: case 6: /* VSUBL, VSUBW, VSUBHL, VRSUBHL */ + case 2: case 3: case 6: /* VSUBL, VSUBW, VSUBHN, VRSUBHN */ gen_neon_subl(size); break; case 5: case 7: /* VABAL, VABDL */ @@ -4988,8 +5088,6 @@ case 8: case 9: case 10: case 11: case 12: case 13: /* VMLAL, VQDMLAL, VMLSL, VQDMLSL, VMULL, VQDMULL */ gen_neon_mull(cpu_V0, tmp, tmp2, size, u); - dead_tmp(tmp2); - dead_tmp(tmp); break; case 14: /* Polynomial VMULL */ cpu_abort(env, "Polynomial VMULL not implemented"); @@ -5026,7 +5124,7 @@ } else if (op == 4 || op == 6) { /* Narrowing operation. */ tmp = new_tmp(); - if (u) { + if (!u) { switch (size) { case 0: gen_helper_neon_narrow_high_u8(tmp, cpu_V0); @@ -5140,6 +5238,10 @@ return 1; tmp2 = neon_get_scalar(size, rm); + /* We need a copy of tmp2 because gen_neon_mull + * deletes it during pass 0. */ + tmp4 = new_tmp(); + tcg_gen_mov_i32(tmp4, tmp2); tmp3 = neon_load_reg(rn, 1); for (pass = 0; pass < 2; pass++) { @@ -5147,9 +5249,9 @@ tmp = neon_load_reg(rn, 0); } else { tmp = tmp3; + tmp2 = tmp4; } gen_neon_mull(cpu_V0, tmp, tmp2, size, u); - dead_tmp(tmp); if (op == 6 || op == 7) { gen_neon_negl(cpu_V0, size); } @@ -5176,7 +5278,6 @@ neon_store_reg64(cpu_V0, rd + pass); } - dead_tmp(tmp2); break; default: /* 14 and 15 are RESERVED */ @@ -5187,7 +5288,6 @@ if (!u) { /* Extract. */ imm = (insn >> 8) & 0xf; - count = q + 1; if (imm > 7 && !q) return 1; @@ -5385,6 +5485,7 @@ if (pass == 1) tmp = tmp2; gen_neon_widen(cpu_V0, tmp, size, 1); + tcg_gen_shli_i64(cpu_V0, cpu_V0, 8 << size); neon_store_reg64(cpu_V0, rd + pass); } break; @@ -5598,16 +5699,16 @@ gen_helper_rsqrte_f32(cpu_F0s, cpu_F0s, cpu_env); break; case 60: /* VCVT.F32.S32 */ - gen_vfp_tosiz(0); + gen_vfp_sito(0); break; case 61: /* VCVT.F32.U32 */ - gen_vfp_touiz(0); + gen_vfp_uito(0); break; case 62: /* VCVT.S32.F32 */ - gen_vfp_sito(0); + gen_vfp_tosiz(0); break; case 63: /* VCVT.U32.F32 */ - gen_vfp_uito(0); + gen_vfp_touiz(0); break; default: /* Reserved: 21, 29, 39-56 */ @@ -5869,8 +5970,10 @@ tcg_gen_mov_i32(cpu_exclusive_val, tmp); store_reg(s, rt, tmp); if (size == 3) { - tcg_gen_addi_i32(addr, addr, 4); - tmp = gen_ld32(addr, IS_USER(s)); + TCGv tmp2 = new_tmp(); + tcg_gen_addi_i32(tmp2, addr, 4); + tmp = gen_ld32(tmp2, IS_USER(s)); + dead_tmp(tmp2); tcg_gen_mov_i32(cpu_exclusive_high, tmp); store_reg(s, rt2, tmp); } @@ -5889,10 +5992,7 @@ tcg_gen_mov_i32(cpu_exclusive_test, addr); tcg_gen_movi_i32(cpu_exclusive_info, size | (rd << 4) | (rt << 8) | (rt2 << 12)); - gen_set_condexec(s); - gen_set_pc_im(s->pc - 4); - gen_exception(EXCP_STREX); - s->is_jmp = DISAS_JUMP; + gen_exception_insn(s, 4, EXCP_STREX); } #else static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2, @@ -5930,7 +6030,7 @@ if (size == 3) { TCGv tmp2 = new_tmp(); tcg_gen_addi_i32(tmp2, addr, 4); - tmp = gen_ld32(addr, IS_USER(s)); + tmp = gen_ld32(tmp2, IS_USER(s)); dead_tmp(tmp2); tcg_gen_brcond_i32(TCG_COND_NE, tmp, cpu_exclusive_high, fail_label); dead_tmp(tmp); @@ -6032,14 +6132,10 @@ goto illegal_op; ARCH(6); op1 = (insn & 0x1f); - if (op1 == (env->uncached_cpsr & CPSR_M)) { - addr = load_reg(s, 13); - } else { - addr = new_tmp(); - tmp = tcg_const_i32(op1); - gen_helper_get_r13_banked(addr, cpu_env, tmp); - tcg_temp_free_i32(tmp); - } + addr = new_tmp(); + tmp = tcg_const_i32(op1); + gen_helper_get_r13_banked(addr, cpu_env, tmp); + tcg_temp_free_i32(tmp); i = (insn >> 23) & 3; switch (i) { case 0: offset = -4; break; /* DA */ @@ -6066,18 +6162,15 @@ } if (offset) tcg_gen_addi_i32(addr, addr, offset); - if (op1 == (env->uncached_cpsr & CPSR_M)) { - store_reg(s, 13, addr); - } else { - tmp = tcg_const_i32(op1); - gen_helper_set_r13_banked(cpu_env, tmp, addr); - tcg_temp_free_i32(tmp); - dead_tmp(addr); - } + tmp = tcg_const_i32(op1); + gen_helper_set_r13_banked(cpu_env, tmp, addr); + tcg_temp_free_i32(tmp); + dead_tmp(addr); } else { dead_tmp(addr); } - } else if ((insn & 0x0e5fffe0) == 0x081d0a00) { + return; + } else if ((insn & 0x0e50ffe0) == 0x08100a00) { /* rfe */ int32_t offset; if (IS_USER(s)) @@ -6288,11 +6381,15 @@ dead_tmp(tmp2); store_reg(s, rd, tmp); break; - case 7: /* bkpt */ - gen_set_condexec(s); - gen_set_pc_im(s->pc - 4); - gen_exception(EXCP_BKPT); - s->is_jmp = DISAS_JUMP; + case 7: + /* SMC instruction (op1 == 3) + and undefined instructions (op1 == 0 || op1 == 2) + will trap */ + if (op1 != 1) { + goto illegal_op; + } + /* bkpt */ + gen_exception_insn(s, 4, EXCP_BKPT); break; case 0x8: /* signed multiply */ case 0xa: @@ -6565,26 +6662,38 @@ gen_logic_CC(tmp); store_reg(s, rd, tmp); break; - default: - /* 64 bit mul */ + case 4: + /* 64 bit mul double accumulate (UMAAL) */ + ARCH(6); tmp = load_reg(s, rs); tmp2 = load_reg(s, rm); - if (insn & (1 << 22)) + tmp64 = gen_mulu_i64_i32(tmp, tmp2); + gen_addq_lo(s, tmp64, rn); + gen_addq_lo(s, tmp64, rd); + gen_storeq_reg(s, rn, rd, tmp64); + tcg_temp_free_i64(tmp64); + break; + case 8: case 9: case 10: case 11: + case 12: case 13: case 14: case 15: + /* 64 bit mul: UMULL, UMLAL, SMULL, SMLAL. */ + tmp = load_reg(s, rs); + tmp2 = load_reg(s, rm); + if (insn & (1 << 22)) { tmp64 = gen_muls_i64_i32(tmp, tmp2); - else + } else { tmp64 = gen_mulu_i64_i32(tmp, tmp2); - if (insn & (1 << 21)) /* mult accumulate */ + } + if (insn & (1 << 21)) { /* mult accumulate */ gen_addq(s, tmp64, rn, rd); - if (!(insn & (1 << 23))) { /* double accumulate */ - ARCH(6); - gen_addq_lo(s, tmp64, rn); - gen_addq_lo(s, tmp64, rd); } - if (insn & (1 << 20)) + if (insn & (1 << 20)) { gen_logicq_cc(tmp64); + } gen_storeq_reg(s, rn, rd, tmp64); tcg_temp_free_i64(tmp64); break; + default: + goto illegal_op; } } else { rn = (insn >> 16) & 0xf; @@ -6622,7 +6731,7 @@ gen_store_exclusive(s, rd, rm, 15, addr, 2); break; case 1: /* strexd */ - gen_store_exclusive(s, rd, rm, rm + 1, addr, 2); + gen_store_exclusive(s, rd, rm, rm + 1, addr, 3); break; case 2: /* strexb */ gen_store_exclusive(s, rd, rm, 15, addr, 0); @@ -6785,27 +6894,23 @@ tcg_gen_shli_i32(tmp, tmp, shift); } sh = (insn >> 16) & 0x1f; - if (sh != 0) { - tmp2 = tcg_const_i32(sh); - if (insn & (1 << 22)) - gen_helper_usat(tmp, tmp, tmp2); - else - gen_helper_ssat(tmp, tmp, tmp2); - tcg_temp_free_i32(tmp2); - } + tmp2 = tcg_const_i32(sh); + if (insn & (1 << 22)) + gen_helper_usat(tmp, tmp, tmp2); + else + gen_helper_ssat(tmp, tmp, tmp2); + tcg_temp_free_i32(tmp2); store_reg(s, rd, tmp); } else if ((insn & 0x00300fe0) == 0x00200f20) { /* [us]sat16 */ tmp = load_reg(s, rm); sh = (insn >> 16) & 0x1f; - if (sh != 0) { - tmp2 = tcg_const_i32(sh); - if (insn & (1 << 22)) - gen_helper_usat16(tmp, tmp, tmp2); - else - gen_helper_ssat16(tmp, tmp, tmp2); - tcg_temp_free_i32(tmp2); - } + tmp2 = tcg_const_i32(sh); + if (insn & (1 << 22)) + gen_helper_usat16(tmp, tmp, tmp2); + else + gen_helper_ssat16(tmp, tmp, tmp2); + tcg_temp_free_i32(tmp2); store_reg(s, rd, tmp); } else if ((insn & 0x00700fe0) == 0x00000fa0) { /* Select bytes. */ @@ -6869,23 +6974,25 @@ tmp = load_reg(s, rm); tmp2 = load_reg(s, rs); if (insn & (1 << 20)) { - /* Signed multiply most significant [accumulate]. */ + /* Signed multiply most significant [accumulate]. + (SMMUL, SMMLA, SMMLS) */ tmp64 = gen_muls_i64_i32(tmp, tmp2); - if (insn & (1 << 5)) - tcg_gen_addi_i64(tmp64, tmp64, 0x80000000u); - tcg_gen_shri_i64(tmp64, tmp64, 32); - tmp = new_tmp(); - tcg_gen_trunc_i64_i32(tmp, tmp64); - tcg_temp_free_i64(tmp64); + if (rd != 15) { - tmp2 = load_reg(s, rd); + tmp = load_reg(s, rd); if (insn & (1 << 6)) { - tcg_gen_sub_i32(tmp, tmp, tmp2); + tmp64 = gen_subq_msw(tmp64, tmp); } else { - tcg_gen_add_i32(tmp, tmp, tmp2); + tmp64 = gen_addq_msw(tmp64, tmp); } - dead_tmp(tmp2); } + if (insn & (1 << 5)) { + tcg_gen_addi_i64(tmp64, tmp64, 0x80000000u); + } + tcg_gen_shri_i64(tmp64, tmp64, 32); + tmp = new_tmp(); + tcg_gen_trunc_i64_i32(tmp, tmp64); + tcg_temp_free_i64(tmp64); store_reg(s, rn, tmp); } else { if (insn & (1 << 5)) @@ -7177,10 +7284,7 @@ break; default: illegal_op: - gen_set_condexec(s); - gen_set_pc_im(s->pc - 4); - gen_exception(EXCP_UDEF); - s->is_jmp = DISAS_JUMP; + gen_exception_insn(s, 4, EXCP_UDEF); break; } } @@ -7463,14 +7567,10 @@ } else { /* srs */ op = (insn & 0x1f); - if (op == (env->uncached_cpsr & CPSR_M)) { - addr = load_reg(s, 13); - } else { - addr = new_tmp(); - tmp = tcg_const_i32(op); - gen_helper_get_r13_banked(addr, cpu_env, tmp); - tcg_temp_free_i32(tmp); - } + addr = new_tmp(); + tmp = tcg_const_i32(op); + gen_helper_get_r13_banked(addr, cpu_env, tmp); + tcg_temp_free_i32(tmp); if ((insn & (1 << 24)) == 0) { tcg_gen_addi_i32(addr, addr, -8); } @@ -7486,13 +7586,9 @@ } else { tcg_gen_addi_i32(addr, addr, 4); } - if (op == (env->uncached_cpsr & CPSR_M)) { - store_reg(s, 13, addr); - } else { - tmp = tcg_const_i32(op); - gen_helper_set_r13_banked(cpu_env, tmp, addr); - tcg_temp_free_i32(tmp); - } + tmp = tcg_const_i32(op); + gen_helper_set_r13_banked(cpu_env, tmp, addr); + tcg_temp_free_i32(tmp); } else { dead_tmp(addr); } @@ -7543,27 +7639,54 @@ } } break; - case 5: /* Data processing register constant shift. */ - if (rn == 15) { - tmp = new_tmp(); - tcg_gen_movi_i32(tmp, 0); - } else { - tmp = load_reg(s, rn); - } - tmp2 = load_reg(s, rm); + case 5: + op = (insn >> 21) & 0xf; - shiftop = (insn >> 4) & 3; - shift = ((insn >> 6) & 3) | ((insn >> 10) & 0x1c); - conds = (insn & (1 << 20)) != 0; - logic_cc = (conds && thumb2_logic_op(op)); - gen_arm_shift_im(tmp2, shiftop, shift, logic_cc); - if (gen_thumb2_data_op(s, op, conds, 0, tmp, tmp2)) - goto illegal_op; - dead_tmp(tmp2); - if (rd != 15) { + if (op == 6) { + /* Halfword pack. */ + tmp = load_reg(s, rn); + tmp2 = load_reg(s, rm); + shift = ((insn >> 10) & 0x1c) | ((insn >> 6) & 0x3); + if (insn & (1 << 5)) { + /* pkhtb */ + if (shift == 0) + shift = 31; + tcg_gen_sari_i32(tmp2, tmp2, shift); + tcg_gen_andi_i32(tmp, tmp, 0xffff0000); + tcg_gen_ext16u_i32(tmp2, tmp2); + } else { + /* pkhbt */ + if (shift) + tcg_gen_shli_i32(tmp2, tmp2, shift); + tcg_gen_ext16u_i32(tmp, tmp); + tcg_gen_andi_i32(tmp2, tmp2, 0xffff0000); + } + tcg_gen_or_i32(tmp, tmp, tmp2); + dead_tmp(tmp2); store_reg(s, rd, tmp); } else { - dead_tmp(tmp); + /* Data processing register constant shift. */ + if (rn == 15) { + tmp = new_tmp(); + tcg_gen_movi_i32(tmp, 0); + } else { + tmp = load_reg(s, rn); + } + tmp2 = load_reg(s, rm); + + shiftop = (insn >> 4) & 3; + shift = ((insn >> 6) & 3) | ((insn >> 10) & 0x1c); + conds = (insn & (1 << 20)) != 0; + logic_cc = (conds && thumb2_logic_op(op)); + gen_arm_shift_im(tmp2, shiftop, shift, logic_cc); + if (gen_thumb2_data_op(s, op, conds, 0, tmp, tmp2)) + goto illegal_op; + dead_tmp(tmp2); + if (rd != 15) { + store_reg(s, rd, tmp); + } else { + dead_tmp(tmp); + } } break; case 13: /* Misc data processing. */ @@ -7628,9 +7751,9 @@ /* Saturating add/subtract. */ tmp = load_reg(s, rn); tmp2 = load_reg(s, rm); - if (op & 2) - gen_helper_double_saturate(tmp, tmp); if (op & 1) + gen_helper_double_saturate(tmp, tmp); + if (op & 2) gen_helper_sub_saturate(tmp, tmp2, tmp); else gen_helper_add_saturate(tmp, tmp, tmp2); @@ -7729,24 +7852,23 @@ dead_tmp(tmp2); } break; - case 5: case 6: /* 32 * 32 -> 32msb */ - gen_imull(tmp, tmp2); - if (insn & (1 << 5)) { - gen_roundqd(tmp, tmp2); - dead_tmp(tmp2); - } else { - dead_tmp(tmp); - tmp = tmp2; - } + case 5: case 6: /* 32 * 32 -> 32msb (SMMUL, SMMLA, SMMLS) */ + tmp64 = gen_muls_i64_i32(tmp, tmp2); if (rs != 15) { - tmp2 = load_reg(s, rs); - if (insn & (1 << 21)) { - tcg_gen_add_i32(tmp, tmp, tmp2); + tmp = load_reg(s, rs); + if (insn & (1 << 20)) { + tmp64 = gen_addq_msw(tmp64, tmp); } else { - tcg_gen_sub_i32(tmp, tmp2, tmp); + tmp64 = gen_subq_msw(tmp64, tmp); } - dead_tmp(tmp2); } + if (insn & (1 << 4)) { + tcg_gen_addi_i64(tmp64, tmp64, 0x80000000u); + } + tcg_gen_shri_i64(tmp64, tmp64, 32); + tmp = new_tmp(); + tcg_gen_trunc_i64_i32(tmp, tmp64); + tcg_temp_free_i64(tmp64); break; case 7: /* Unsigned sum of absolute differences. */ gen_helper_usad8(tmp, tmp, tmp2); @@ -7946,8 +8068,16 @@ gen_bx(s, tmp); break; case 5: /* Exception return. */ - /* Unpredictable in user mode. */ - goto illegal_op; + if (IS_USER(s)) { + goto illegal_op; + } + if (rn != 14 || rd != 15) { + goto illegal_op; + } + tmp = load_reg(s, rn); + tcg_gen_subi_i32(tmp, tmp, insn & 0xff); + gen_exception_return(s, tmp); + break; case 6: /* mrs cpsr. */ tmp = new_tmp(); if (IS_M(env)) { @@ -8273,9 +8403,11 @@ if (s->condexec_mask) { cond = s->condexec_cond; - s->condlabel = gen_new_label(); - gen_test_cc(cond ^ 1, s->condlabel); - s->condjmp = 1; + if (cond != 0x0e) { /* Skip conditional when condition is AL. */ + s->condlabel = gen_new_label(); + gen_test_cc(cond ^ 1, s->condlabel); + s->condjmp = 1; + } } insn = lduw_code(s->pc); @@ -8798,10 +8930,7 @@ break; case 0xe: /* bkpt */ - gen_set_condexec(s); - gen_set_pc_im(s->pc - 2); - gen_exception(EXCP_BKPT); - s->is_jmp = DISAS_JUMP; + gen_exception_insn(s, 2, EXCP_BKPT); break; case 0xa: /* rev */ @@ -8843,7 +8972,7 @@ shift = CPSR_A | CPSR_I | CPSR_F; else shift = 0; - gen_set_psr_im(s, shift, 0, ((insn & 7) << 6) & shift); + gen_set_psr_im(s, ((insn & 7) << 6), 0, shift); } break; @@ -8887,7 +9016,6 @@ if (cond == 0xf) { /* swi */ - gen_set_condexec(s); gen_set_pc_im(s->pc); s->is_jmp = DISAS_SWI; break; @@ -8924,17 +9052,11 @@ } return; undef32: - gen_set_condexec(s); - gen_set_pc_im(s->pc - 4); - gen_exception(EXCP_UDEF); - s->is_jmp = DISAS_JUMP; + gen_exception_insn(s, 4, EXCP_UDEF); return; illegal_op: undef: - gen_set_condexec(s); - gen_set_pc_im(s->pc - 2); - gen_exception(EXCP_UDEF); - s->is_jmp = DISAS_JUMP; + gen_exception_insn(s, 2, EXCP_UDEF); } /* generate intermediate code in gen_opc_buf and gen_opparam_buf for @@ -8966,16 +9088,15 @@ dc->pc = pc_start; dc->singlestep_enabled = env->singlestep_enabled; dc->condjmp = 0; - dc->thumb = env->thumb; - dc->condexec_mask = (env->condexec_bits & 0xf) << 1; - dc->condexec_cond = env->condexec_bits >> 4; + dc->thumb = ARM_TBFLAG_THUMB(tb->flags); + dc->condexec_mask = (ARM_TBFLAG_CONDEXEC(tb->flags) & 0xf) << 1; + dc->condexec_cond = ARM_TBFLAG_CONDEXEC(tb->flags) >> 4; #if !defined(CONFIG_USER_ONLY) - if (IS_M(env)) { - dc->user = ((env->v7m.exception == 0) && (env->v7m.control & 1)); - } else { - dc->user = (env->uncached_cpsr & 0x1f) == ARM_CPU_MODE_USR; - } + dc->user = (ARM_TBFLAG_PRIV(tb->flags) == 0); #endif + dc->vfp_enabled = ARM_TBFLAG_VFPEN(tb->flags); + dc->vec_len = ARM_TBFLAG_VECLEN(tb->flags); + dc->vec_stride = ARM_TBFLAG_VECSTRIDE(tb->flags); cpu_F0s = tcg_temp_new_i32(); cpu_F1s = tcg_temp_new_i32(); cpu_F0d = tcg_temp_new_i64(); @@ -8992,9 +9113,41 @@ max_insns = CF_COUNT_MASK; gen_icount_start(); + + /* A note on handling of the condexec (IT) bits: + * + * We want to avoid the overhead of having to write the updated condexec + * bits back to the CPUState for every instruction in an IT block. So: + * (1) if the condexec bits are not already zero then we write + * zero back into the CPUState now. This avoids complications trying + * to do it at the end of the block. (For example if we don't do this + * it's hard to identify whether we can safely skip writing condexec + * at the end of the TB, which we definitely want to do for the case + * where a TB doesn't do anything with the IT state at all.) + * (2) if we are going to leave the TB then we call gen_set_condexec() + * which will write the correct value into CPUState if zero is wrong. + * This is done both for leaving the TB at the end, and for leaving + * it because of an exception we know will happen, which is done in + * gen_exception_insn(). The latter is necessary because we need to + * leave the TB with the PC/IT state just prior to execution of the + * instruction which caused the exception. + * (3) if we leave the TB unexpectedly (eg a data abort on a load) + * then the CPUState will be wrong and we need to reset it. + * This is handled in the same way as restoration of the + * PC in these situations: we will be called again with search_pc=1 + * and generate a mapping of the condexec bits for each PC in + * gen_opc_condexec_bits[]. gen_pc_load[] then uses this to restore + * the condexec bits. + * + * Note that there are no instructions which can read the condexec + * bits, and none which can write non-static values to them, so + * we don't need to care about whether CPUState is correct in the + * middle of a TB. + */ + /* Reset the conditional execution bits immediately. This avoids complications trying to do it at the end of the block. */ - if (env->condexec_bits) + if (dc->condexec_mask || dc->condexec_cond) { TCGv tmp = new_tmp(); tcg_gen_movi_i32(tmp, 0); @@ -9023,10 +9176,7 @@ if (unlikely(!QTAILQ_EMPTY(&env->breakpoints))) { QTAILQ_FOREACH(bp, &env->breakpoints, entry) { if (bp->pc == dc->pc) { - gen_set_condexec(dc); - gen_set_pc_im(dc->pc); - gen_exception(EXCP_DEBUG); - dc->is_jmp = DISAS_JUMP; + gen_exception_insn(dc, 0, EXCP_DEBUG); /* Advance PC so that clearing the breakpoint will invalidate this TB. */ dc->pc += 2; @@ -9043,6 +9193,7 @@ gen_opc_instr_start[lj++] = 0; } gen_opc_pc[lj] = dc->pc; + gen_opc_condexec_bits[lj] = (dc->condexec_cond << 4) | (dc->condexec_mask >> 1); gen_opc_instr_start[lj] = 1; gen_opc_icount[lj] = num_insns; } @@ -9050,7 +9201,11 @@ if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO)) gen_io_start(); - if (env->thumb) { + if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP))) { + tcg_gen_debug_insn_start(dc->pc); + } + + if (dc->thumb) { disas_thumb_insn(env, dc); if (dc->condexec_mask) { dc->condexec_cond = (dc->condexec_cond & 0xe) @@ -9164,7 +9319,7 @@ if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { qemu_log("----------------\n"); qemu_log("IN: %s\n", lookup_symbol(pc_start)); - log_target_disas(pc_start, dc->pc - pc_start, env->thumb); + log_target_disas(pc_start, dc->pc - pc_start, dc->thumb); qemu_log("\n"); } #endif @@ -9194,8 +9349,7 @@ "???", "???", "???", "und", "???", "???", "???", "sys" }; -void cpu_dump_state(CPUState *env, FILE *f, - int (*cpu_fprintf)(FILE *f, const char *fmt, ...), +void cpu_dump_state(CPUState *env, FILE *f, fprintf_function cpu_fprintf, int flags) { int i; @@ -9251,4 +9405,5 @@ unsigned long searched_pc, int pc_pos, void *puc) { env->regs[15] = gen_opc_pc[pc_pos]; + env->condexec_bits = gen_opc_condexec_bits[pc_pos]; } diff -Nru qemu-kvm-0.12.5+noroms/target-cris/cpu.h qemu-kvm-0.14.1/target-cris/cpu.h --- qemu-kvm-0.12.5+noroms/target-cris/cpu.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-cris/cpu.h 2011-05-11 13:29:46.000000000 +0000 @@ -49,6 +49,7 @@ #define PR_WZ 4 #define PR_EXS 5 #define PR_EDA 6 +#define PR_PREFIX 6 /* On CRISv10 P6 is reserved, we use it as prefix. */ #define PR_MOF 7 #define PR_DZ 8 #define PR_EBP 9 @@ -62,6 +63,7 @@ /* CPU flags. */ #define Q_FLAG 0x80000000 #define M_FLAG 0x40000000 +#define PFIX_FLAG 0x800 /* CRISv10 Only. */ #define S_FLAG 0x200 #define R_FLAG 0x100 #define P_FLAG 0x80 @@ -121,6 +123,8 @@ /* X flag at the time of cc snapshot. */ int cc_x; + /* CRIS has certain insns that lockout interrupts. */ + int locked_irq; int interrupt_vector; int fault_vector; int trap_vector; @@ -152,6 +156,9 @@ } tlbsets[2][4][16]; CPU_COMMON + + /* Members after CPU_COMMON are preserved across resets. */ + void *load_info; } CPUCRISState; CPUCRISState *cpu_cris_init(const char *cpu_model); @@ -180,6 +187,7 @@ CC_OP_MULS, CC_OP_MULU, CC_OP_DSTEP, + CC_OP_MSTEP, CC_OP_BOUND, CC_OP_OR, @@ -195,6 +203,9 @@ #define TARGET_PAGE_BITS 13 #define MMAP_SHIFT TARGET_PAGE_BITS +#define TARGET_PHYS_ADDR_SPACE_BITS 32 +#define TARGET_VIRT_ADDR_SPACE_BITS 32 + #define cpu_init cpu_cris_init #define cpu_exec cpu_cris_exec #define cpu_gen_code cpu_cris_gen_code @@ -240,12 +251,6 @@ #define SFR_RW_MM_TLB_HI env->pregs[PR_SRS]][6 #include "cpu-all.h" -#include "exec-all.h" - -static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb) -{ - env->pc = tb->pc; -} static inline void cpu_get_tb_cpu_state(CPUState *env, target_ulong *pc, target_ulong *cs_base, int *flags) @@ -253,7 +258,11 @@ *pc = env->pc; *cs_base = 0; *flags = env->dslot | - (env->pregs[PR_CCS] & (S_FLAG | P_FLAG | U_FLAG | X_FLAG)); + (env->pregs[PR_CCS] & (S_FLAG | P_FLAG | U_FLAG + | X_FLAG | PFIX_FLAG)); } +#define cpu_list cris_cpu_list +void cris_cpu_list(FILE *f, fprintf_function cpu_fprintf); + #endif diff -Nru qemu-kvm-0.12.5+noroms/target-cris/crisv10-decode.h qemu-kvm-0.14.1/target-cris/crisv10-decode.h --- qemu-kvm-0.12.5+noroms/target-cris/crisv10-decode.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/target-cris/crisv10-decode.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,107 @@ +/* + * CRISv10 insn decoding macros. + * + * Copyright (c) 2010 AXIS Communications AB + * Written by Edgar E. Iglesias. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#define CRISV10_MODE_QIMMEDIATE 0 +#define CRISV10_MODE_REG 1 +#define CRISV10_MODE_INDIRECT 2 +#define CRISV10_MODE_AUTOINC 3 + +/* Quick Immediate. */ +#define CRISV10_QIMM_BCC_R0 0 +#define CRISV10_QIMM_BCC_R1 1 +#define CRISV10_QIMM_BCC_R2 2 +#define CRISV10_QIMM_BCC_R3 3 + +#define CRISV10_QIMM_BDAP_R0 4 +#define CRISV10_QIMM_BDAP_R1 5 +#define CRISV10_QIMM_BDAP_R2 6 +#define CRISV10_QIMM_BDAP_R3 7 + +#define CRISV10_QIMM_ADDQ 8 +#define CRISV10_QIMM_MOVEQ 9 +#define CRISV10_QIMM_SUBQ 10 +#define CRISV10_QIMM_CMPQ 11 +#define CRISV10_QIMM_ANDQ 12 +#define CRISV10_QIMM_ORQ 13 +#define CRISV10_QIMM_ASHQ 14 +#define CRISV10_QIMM_LSHQ 15 + + +#define CRISV10_REG_ADDX 0 +#define CRISV10_REG_MOVX 1 +#define CRISV10_REG_SUBX 2 +#define CRISV10_REG_LSL 3 +#define CRISV10_REG_ADDI 4 +#define CRISV10_REG_BIAP 5 +#define CRISV10_REG_NEG 6 +#define CRISV10_REG_BOUND 7 +#define CRISV10_REG_ADD 8 +#define CRISV10_REG_MOVE_R 9 +#define CRISV10_REG_MOVE_SPR_R 9 +#define CRISV10_REG_MOVE_R_SPR 8 +#define CRISV10_REG_SUB 10 +#define CRISV10_REG_CMP 11 +#define CRISV10_REG_AND 12 +#define CRISV10_REG_OR 13 +#define CRISV10_REG_ASR 14 +#define CRISV10_REG_LSR 15 + +#define CRISV10_REG_BTST 3 +#define CRISV10_REG_SCC 4 +#define CRISV10_REG_SETF 6 +#define CRISV10_REG_CLEARF 7 +#define CRISV10_REG_BIAP 5 +#define CRISV10_REG_ABS 10 +#define CRISV10_REG_DSTEP 11 +#define CRISV10_REG_LZ 12 +#define CRISV10_REG_NOT 13 +#define CRISV10_REG_SWAP 13 +#define CRISV10_REG_XOR 14 +#define CRISV10_REG_MSTEP 15 + +/* Indirect, var size. */ +#define CRISV10_IND_TEST 14 +#define CRISV10_IND_MUL 4 +#define CRISV10_IND_BDAP_M 5 +#define CRISV10_IND_ADD 8 +#define CRISV10_IND_MOVE_M_R 9 + + +/* indirect fixed size. */ +#define CRISV10_IND_ADDX 0 +#define CRISV10_IND_MOVX 1 +#define CRISV10_IND_SUBX 2 +#define CRISV10_IND_CMPX 3 +#define CRISV10_IND_JUMP_M 4 +#define CRISV10_IND_DIP 5 +#define CRISV10_IND_JUMP_R 6 +#define CRISV10_IND_BOUND 7 +#define CRISV10_IND_BCC_M 7 +#define CRISV10_IND_MOVE_M_SPR 8 +#define CRISV10_IND_MOVE_SPR_M 9 +#define CRISV10_IND_SUB 10 +#define CRISV10_IND_CMP 11 +#define CRISV10_IND_AND 12 +#define CRISV10_IND_OR 13 +#define CRISV10_IND_MOVE_R_M 15 + +#define CRISV10_IND_MOVEM_M_R 14 +#define CRISV10_IND_MOVEM_R_M 15 + diff -Nru qemu-kvm-0.12.5+noroms/target-cris/exec.h qemu-kvm-0.14.1/target-cris/exec.h --- qemu-kvm-0.12.5+noroms/target-cris/exec.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-cris/exec.h 2011-05-11 13:29:46.000000000 +0000 @@ -24,21 +24,10 @@ #include "cpu.h" #include "exec-all.h" -static inline void env_to_regs(void) -{ -} - -static inline void regs_to_env(void) -{ -} - #if !defined(CONFIG_USER_ONLY) #include "softmmu_exec.h" #endif -void cpu_cris_flush_flags(CPUCRISState *env, int cc_op); -void helper_movec(CPUCRISState *env, int reg, uint32_t val); - static inline int cpu_has_work(CPUState *env) { return (env->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI)); @@ -56,3 +45,9 @@ } return EXCP_HALTED; } + +static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb) +{ + env->pc = tb->pc; +} + diff -Nru qemu-kvm-0.12.5+noroms/target-cris/helper.c qemu-kvm-0.14.1/target-cris/helper.c --- qemu-kvm-0.12.5+noroms/target-cris/helper.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-cris/helper.c 2011-05-11 13:29:46.000000000 +0000 @@ -56,11 +56,6 @@ return 1; } -target_phys_addr_t cpu_get_phys_page_debug(CPUState * env, target_ulong addr) -{ - return addr; -} - #else /* !CONFIG_USER_ONLY */ @@ -83,14 +78,14 @@ D(printf ("%s addr=%x pc=%x rw=%x\n", __func__, address, env->pc, rw)); miss = cris_mmu_translate(&res, env, address & TARGET_PAGE_MASK, - rw, mmu_idx); + rw, mmu_idx, 0); if (miss) { if (env->exception_index == EXCP_BUSFAULT) - cpu_abort(env, + cpu_abort(env, "CRIS: Illegal recursive bus fault." - "addr=%x rw=%d\n", - address, rw); + "addr=%x rw=%d\n", + address, rw); env->pregs[PR_EDA] = address; env->exception_index = EXCP_BUSFAULT; @@ -105,8 +100,9 @@ */ phy = res.phy & ~0x80000000; prot = res.prot; - r = tlb_set_page(env, address & TARGET_PAGE_MASK, - phy, prot, mmu_idx, is_softmmu); + tlb_set_page(env, address & TARGET_PAGE_MASK, phy, + prot, mmu_idx, TARGET_PAGE_SIZE); + r = 0; } if (r > 0) D_LOG("%s returns %d irqreq=%x addr=%x" @@ -116,10 +112,68 @@ return r; } +static void do_interruptv10(CPUState *env) +{ + int ex_vec = -1; + + D_LOG( "exception index=%d interrupt_req=%d\n", + env->exception_index, + env->interrupt_request); + + assert(!(env->pregs[PR_CCS] & PFIX_FLAG)); + switch (env->exception_index) + { + case EXCP_BREAK: + /* These exceptions are genereated by the core itself. + ERP should point to the insn following the brk. */ + ex_vec = env->trap_vector; + env->pregs[PR_ERP] = env->pc; + break; + + case EXCP_NMI: + /* NMI is hardwired to vector zero. */ + ex_vec = 0; + env->pregs[PR_CCS] &= ~M_FLAG; + env->pregs[PR_NRP] = env->pc; + break; + + case EXCP_BUSFAULT: + cpu_abort(env, "Unhandled busfault"); + break; + + default: + /* The interrupt controller gives us the vector. */ + ex_vec = env->interrupt_vector; + /* Normal interrupts are taken between + TB's. env->pc is valid here. */ + env->pregs[PR_ERP] = env->pc; + break; + } + + if (env->pregs[PR_CCS] & U_FLAG) { + /* Swap stack pointers. */ + env->pregs[PR_USP] = env->regs[R_SP]; + env->regs[R_SP] = env->ksp; + } + + /* Now that we are in kernel mode, load the handlers address. */ + env->pc = ldl_code(env->pregs[PR_EBP] + ex_vec * 4); + env->locked_irq = 1; + + qemu_log_mask(CPU_LOG_INT, "%s isr=%x vec=%x ccs=%x pid=%d erp=%x\n", + __func__, env->pc, ex_vec, + env->pregs[PR_CCS], + env->pregs[PR_PID], + env->pregs[PR_ERP]); +} + void do_interrupt(CPUState *env) { int ex_vec = -1; + if (env->pregs[PR_VR] < 32) + return do_interruptv10(env); + D_LOG( "exception index=%d interrupt_req=%d\n", env->exception_index, env->interrupt_request); @@ -181,11 +235,17 @@ /* Apply the CRIS CCS shift. Clears U if set. */ cris_shift_ccs(env); - /* Now that we are in kernel mode, load the handlers address. */ + /* Now that we are in kernel mode, load the handlers address. + This load may not fault, real hw leaves that behaviour as + undefined. */ env->pc = ldl_code(env->pregs[PR_EBP] + ex_vec * 4); - D_LOG("%s isr=%x vec=%x ccs=%x pid=%d erp=%x\n", - __func__, env->pc, ex_vec, + /* Clear the excption_index to avoid spurios hw_aborts for recursive + bus faults. */ + env->exception_index = -1; + + D_LOG("%s isr=%x vec=%x ccs=%x pid=%d erp=%x\n", + __func__, env->pc, ex_vec, env->pregs[PR_CCS], env->pregs[PR_PID], env->pregs[PR_ERP]); @@ -196,7 +256,13 @@ uint32_t phy = addr; struct cris_mmu_result res; int miss; - miss = cris_mmu_translate(&res, env, addr, 0, 0); + + miss = cris_mmu_translate(&res, env, addr, 0, 0, 1); + /* If D TLB misses, try I TLB. */ + if (miss) { + miss = cris_mmu_translate(&res, env, addr, 2, 0, 1); + } + if (!miss) phy = res.phy; D(fprintf(stderr, "%s %x -> %x\n", __func__, addr, phy)); diff -Nru qemu-kvm-0.12.5+noroms/target-cris/mmu.c qemu-kvm-0.14.1/target-cris/mmu.c --- qemu-kvm-0.12.5+noroms/target-cris/mmu.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-cris/mmu.c 2011-05-11 13:29:46.000000000 +0000 @@ -31,9 +31,9 @@ #ifdef DEBUG #define D(x) x -#define D_LOG(...) qemu_log(__VA__ARGS__) +#define D_LOG(...) qemu_log(__VA_ARGS__) #else -#define D(x) +#define D(x) do { } while (0) #define D_LOG(...) do { } while (0) #endif @@ -55,6 +55,17 @@ return f; } +static void cris_mmu_update_rand_lfsr(CPUState *env) +{ + unsigned int f; + + /* Update lfsr at every fault. */ + f = compute_polynom(env->mmu_rand_lfsr); + env->mmu_rand_lfsr >>= 1; + env->mmu_rand_lfsr |= (f << 15); + env->mmu_rand_lfsr &= 0xffff; +} + static inline int cris_mmu_enabled(uint32_t rw_gc_cfg) { return (rw_gc_cfg & 12) != 0; @@ -124,7 +135,7 @@ /* rw 0 = read, 1 = write, 2 = exec. */ static int cris_mmu_translate_page(struct cris_mmu_result *res, CPUState *env, uint32_t vaddr, - int rw, int usermode) + int rw, int usermode, int debug) { unsigned int vpage; unsigned int idx; @@ -240,7 +251,7 @@ res->prot |= PAGE_READ; if (tlb_w) res->prot |= PAGE_WRITE; - if (tlb_x) + if (mmu == 0 && (cfg_x || tlb_x)) res->prot |= PAGE_EXEC; } else @@ -250,15 +261,9 @@ set = env->mmu_rand_lfsr & 3; } - if (!match) { - unsigned int f; + if (!match && !debug) { + cris_mmu_update_rand_lfsr(env); - /* Update lfsr at every fault. */ - f = compute_polynom(env->mmu_rand_lfsr); - env->mmu_rand_lfsr >>= 1; - env->mmu_rand_lfsr |= (f << 15); - env->mmu_rand_lfsr &= 0xffff; - /* Compute index. */ idx = vpage & 15; @@ -325,9 +330,8 @@ int cris_mmu_translate(struct cris_mmu_result *res, CPUState *env, uint32_t vaddr, - int rw, int mmu_idx) + int rw, int mmu_idx, int debug) { - uint32_t phy = vaddr; int seg; int miss = 0; int is_user = mmu_idx == MMU_USER_IDX; @@ -351,12 +355,12 @@ miss = 0; base = cris_mmu_translate_seg(env, seg); - phy = base | (0x0fffffff & vaddr); - res->phy = phy; + res->phy = base | (0x0fffffff & vaddr); res->prot = PAGE_BITS; + } else { + miss = cris_mmu_translate_page(res, env, vaddr, rw, + is_user, debug); } - else - miss = cris_mmu_translate_page(res, env, vaddr, rw, is_user); done: env->pregs[PR_SRS] = old_srs; return miss; diff -Nru qemu-kvm-0.12.5+noroms/target-cris/mmu.h qemu-kvm-0.14.1/target-cris/mmu.h --- qemu-kvm-0.12.5+noroms/target-cris/mmu.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-cris/mmu.h 2011-05-11 13:29:46.000000000 +0000 @@ -14,4 +14,4 @@ void cris_mmu_flush_pid(CPUState *env, uint32_t pid); int cris_mmu_translate(struct cris_mmu_result *res, CPUState *env, uint32_t vaddr, - int rw, int mmu_idx); + int rw, int mmu_idx, int debug); diff -Nru qemu-kvm-0.12.5+noroms/target-cris/op_helper.c qemu-kvm-0.14.1/target-cris/op_helper.c --- qemu-kvm-0.12.5+noroms/target-cris/op_helper.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-cris/op_helper.c 2011-05-11 13:29:46.000000000 +0000 @@ -164,7 +164,9 @@ D_LOG("tlb flush vaddr=%x v=%d pc=%x\n", vaddr, tlb_v, env->pc); - tlb_flush_page(env, vaddr); + if (tlb_v) { + tlb_flush_page(env, vaddr); + } } } #endif @@ -276,6 +278,8 @@ /* Clear the X, N and Z flags. */ ccs = ccs & ~(X_FLAG | N_FLAG | Z_FLAG); + if (env->pregs[PR_VR] < 32) + ccs &= ~(V_FLAG | C_FLAG); /* Set the N and Z flags accordingly. */ ccs |= (bset << 3) | (fz << 2); return ccs; diff -Nru qemu-kvm-0.12.5+noroms/target-cris/translate.c qemu-kvm-0.14.1/target-cris/translate.c --- qemu-kvm-0.12.5+noroms/target-cris/translate.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-cris/translate.c 2011-05-11 13:29:46.000000000 +0000 @@ -86,6 +86,7 @@ target_ulong pc, ppc; /* Decoder. */ + unsigned int (*decoder)(struct DisasContext *dc); uint32_t ir; uint32_t opcode; unsigned int op1; @@ -94,6 +95,11 @@ unsigned int mode; unsigned int postinc; + unsigned int size; + unsigned int src; + unsigned int dst; + unsigned int cond; + int update_cc; int cc_op; int cc_size; @@ -108,13 +114,16 @@ int flags_x; int clear_x; /* Clear x after this insn? */ + int clear_prefix; /* Clear prefix after this insn? */ + int clear_locked_irq; /* Clear the irq lockout. */ int cpustate_changed; unsigned int tb_flags; /* tb dependent flags. */ int is_jmp; -#define JMP_NOJMP 0 -#define JMP_DIRECT 1 -#define JMP_INDIRECT 2 +#define JMP_NOJMP 0 +#define JMP_DIRECT 1 +#define JMP_DIRECT_CC 2 +#define JMP_INDIRECT 3 int jmp; /* 0=nojmp, 1=direct, 2=indirect. */ uint32_t jmp_pc; @@ -219,6 +228,61 @@ } } +/* Sign extend at translation time. */ +static int sign_extend(unsigned int val, unsigned int width) +{ + int sval; + + /* LSL. */ + val <<= 31 - width; + sval = val; + /* ASR. */ + sval >>= 31 - width; + return sval; +} + +static int cris_fetch(DisasContext *dc, uint32_t addr, + unsigned int size, unsigned int sign) +{ + int r; + + switch (size) { + case 4: + { + r = ldl_code(addr); + break; + } + case 2: + { + if (sign) { + r = ldsw_code(addr); + } else { + r = lduw_code(addr); + } + break; + } + case 1: + { + if (sign) { + r = ldsb_code(addr); + } else { + r = ldub_code(addr); + } + break; + } + default: + cpu_abort(dc->env, "Invalid fetch size %d\n", size); + break; + } + return r; +} + +static void cris_lock_irq(DisasContext *dc) +{ + dc->clear_locked_irq = 0; + t_gen_mov_env_TN(locked_irq, tcg_const_tl(1)); +} + static inline void t_gen_raise_exception(uint32_t index) { TCGv_i32 tmp = tcg_const_i32(index); @@ -332,6 +396,24 @@ gen_set_label(l1); } +static void t_gen_cris_mstep(TCGv d, TCGv a, TCGv b, TCGv ccs) +{ + TCGv t; + + /* + * d <<= 1 + * if (n) + * d += s; + */ + t = tcg_temp_new(); + tcg_gen_shli_tl(d, a, 1); + tcg_gen_shli_tl(t, ccs, 31 - 3); + tcg_gen_sari_tl(t, t, 31); + tcg_gen_and_tl(t, t, b); + tcg_gen_add_tl(d, d, t); + tcg_temp_free(t); +} + /* Extended arithmetics on CRIS. */ static inline void t_gen_add_flag(TCGv d, int flag) { @@ -496,20 +578,15 @@ static void t_gen_cc_jmp(TCGv pc_true, TCGv pc_false) { - TCGv btaken; int l1; l1 = gen_new_label(); - btaken = tcg_temp_new(); /* Conditional jmp. */ - tcg_gen_mov_tl(btaken, env_btaken); tcg_gen_mov_tl(env_pc, pc_false); - tcg_gen_brcondi_tl(TCG_COND_EQ, btaken, 0, l1); + tcg_gen_brcondi_tl(TCG_COND_EQ, env_btaken, 0, l1); tcg_gen_mov_tl(env_pc, pc_true); gen_set_label(l1); - - tcg_temp_free(btaken); } static void gen_goto_tb(DisasContext *dc, int n, target_ulong dest) @@ -526,19 +603,6 @@ } } -/* Sign extend at translation time. */ -static int sign_extend(unsigned int val, unsigned int width) -{ - int sval; - - /* LSL. */ - val <<= 31 - width; - sval = val; - /* ASR. */ - sval >>= 31 - width; - return sval; -} - static inline void cris_clear_x_flag(DisasContext *dc) { if (dc->flagx_known && dc->flags_x) @@ -634,7 +698,7 @@ if (dc->flags_x) tcg_gen_ori_tl(cpu_PR[PR_CCS], cpu_PR[PR_CCS], X_FLAG); - else + else if (dc->cc_op == CC_OP_FLAGS) tcg_gen_andi_tl(cpu_PR[PR_CCS], cpu_PR[PR_CCS], ~X_FLAG); } @@ -774,6 +838,9 @@ case CC_OP_DSTEP: t_gen_cris_dstep(dst, a, b); break; + case CC_OP_MSTEP: + t_gen_cris_mstep(dst, a, b, cpu_PR[PR_CCS]); + break; case CC_OP_BOUND: { int l1; @@ -878,16 +945,10 @@ move_opt = (dc->cc_op == CC_OP_MOVE); switch (cond) { case CC_EQ: - if (arith_opt || move_opt) { - /* If cc_result is zero, T0 should be - non-zero otherwise T0 should be zero. */ - int l1; - l1 = gen_new_label(); - tcg_gen_movi_tl(cc, 0); - tcg_gen_brcondi_tl(TCG_COND_NE, cc_result, - 0, l1); - tcg_gen_movi_tl(cc, 1); - gen_set_label(l1); + if ((arith_opt || move_opt) + && dc->cc_x_uptodate != (2 | X_FLAG)) { + tcg_gen_setcond_tl(TCG_COND_EQ, cc, + cc_result, tcg_const_tl(0)); } else { cris_evaluate_flags(dc); @@ -896,9 +957,10 @@ } break; case CC_NE: - if (arith_opt || move_opt) + if ((arith_opt || move_opt) + && dc->cc_x_uptodate != (2 | X_FLAG)) { tcg_gen_mov_tl(cc, cc_result); - else { + } else { cris_evaluate_flags(dc); tcg_gen_xori_tl(cc, cpu_PR[PR_CCS], Z_FLAG); @@ -1059,9 +1121,12 @@ static void cris_store_direct_jmp(DisasContext *dc) { /* Store the direct jmp state into the cpu-state. */ - if (dc->jmp == JMP_DIRECT) { + if (dc->jmp == JMP_DIRECT || dc->jmp == JMP_DIRECT_CC) { + if (dc->jmp == JMP_DIRECT) { + tcg_gen_movi_tl(env_btaken, 1); + } tcg_gen_movi_tl(env_btarget, dc->jmp_pc); - tcg_gen_movi_tl(env_btaken, 1); + dc->jmp = JMP_INDIRECT; } } @@ -1071,17 +1136,11 @@ /* This helps us re-schedule the micro-code to insns in delay-slots before the actual jump. */ dc->delayed_branch = 2; + dc->jmp = JMP_DIRECT_CC; dc->jmp_pc = dc->pc + offset; - if (cond != CC_A) - { - dc->jmp = JMP_INDIRECT; - gen_tst_cc (dc, env_btaken, cond); - tcg_gen_movi_tl(env_btarget, dc->jmp_pc); - } else { - /* Allow chaining. */ - dc->jmp = JMP_DIRECT; - } + gen_tst_cc (dc, env_btaken, cond); + tcg_gen_movi_tl(env_btarget, dc->jmp_pc); } @@ -1093,8 +1152,9 @@ before the actual jump. */ dc->delayed_branch = 2; dc->jmp = type; - if (type == JMP_INDIRECT) + if (type == JMP_INDIRECT) { tcg_gen_movi_tl(env_btaken, 1); + } } static void gen_load64(DisasContext *dc, TCGv_i64 dst, TCGv addr) @@ -1255,13 +1315,12 @@ static int dec_prep_move_m(DisasContext *dc, int s_ext, int memsize, TCGv dst) { - unsigned int rs, rd; + unsigned int rs; uint32_t imm; int is_imm; int insn_len = 2; rs = dc->op1; - rd = dc->op2; is_imm = rs == 15 && dc->postinc; /* Load [$rs] onto T1. */ @@ -1270,21 +1329,7 @@ if (memsize == 1) insn_len++; - if (memsize != 4) { - if (s_ext) { - if (memsize == 1) - imm = ldsb_code(dc->pc + 2); - else - imm = ldsw_code(dc->pc + 2); - } else { - if (memsize == 1) - imm = ldub_code(dc->pc + 2); - else - imm = lduw_code(dc->pc + 2); - } - } else - imm = ldl_code(dc->pc + 2); - + imm = cris_fetch(dc, dc->pc + 2, memsize, s_ext); tcg_gen_movi_tl(dst, imm); dc->postinc = 0; } else { @@ -1325,19 +1370,17 @@ /* Start of insn decoders. */ -static unsigned int dec_bccq(DisasContext *dc) +static int dec_bccq(DisasContext *dc) { int32_t offset; int sign; uint32_t cond = dc->op2; - int tmp; offset = EXTRACT_FIELD (dc->ir, 1, 7); sign = EXTRACT_FIELD(dc->ir, 0, 0); offset *= 2; offset |= sign << 8; - tmp = offset; offset = sign_extend(offset, 8); LOG_DIS("b%s %x\n", cc_name(cond), dc->pc + offset); @@ -1347,7 +1390,7 @@ cris_prepare_cc_branch (dc, offset, cond); return 2; } -static unsigned int dec_addoq(DisasContext *dc) +static int dec_addoq(DisasContext *dc) { int32_t imm; @@ -1361,7 +1404,7 @@ return 2; } -static unsigned int dec_addq(DisasContext *dc) +static int dec_addq(DisasContext *dc) { LOG_DIS("addq %u, $r%u\n", dc->op1, dc->op2); @@ -1373,7 +1416,7 @@ cpu_R[dc->op2], cpu_R[dc->op2], tcg_const_tl(dc->op1), 4); return 2; } -static unsigned int dec_moveq(DisasContext *dc) +static int dec_moveq(DisasContext *dc) { uint32_t imm; @@ -1381,10 +1424,10 @@ imm = sign_extend(dc->op1, 5); LOG_DIS("moveq %d, $r%u\n", imm, dc->op2); - tcg_gen_mov_tl(cpu_R[dc->op2], tcg_const_tl(imm)); + tcg_gen_movi_tl(cpu_R[dc->op2], imm); return 2; } -static unsigned int dec_subq(DisasContext *dc) +static int dec_subq(DisasContext *dc) { dc->op1 = EXTRACT_FIELD(dc->ir, 0, 5); @@ -1395,7 +1438,7 @@ cpu_R[dc->op2], cpu_R[dc->op2], tcg_const_tl(dc->op1), 4); return 2; } -static unsigned int dec_cmpq(DisasContext *dc) +static int dec_cmpq(DisasContext *dc) { uint32_t imm; dc->op1 = EXTRACT_FIELD(dc->ir, 0, 5); @@ -1408,7 +1451,7 @@ cpu_R[dc->op2], cpu_R[dc->op2], tcg_const_tl(imm), 4); return 2; } -static unsigned int dec_andq(DisasContext *dc) +static int dec_andq(DisasContext *dc) { uint32_t imm; dc->op1 = EXTRACT_FIELD(dc->ir, 0, 5); @@ -1421,7 +1464,7 @@ cpu_R[dc->op2], cpu_R[dc->op2], tcg_const_tl(imm), 4); return 2; } -static unsigned int dec_orq(DisasContext *dc) +static int dec_orq(DisasContext *dc) { uint32_t imm; dc->op1 = EXTRACT_FIELD(dc->ir, 0, 5); @@ -1433,7 +1476,7 @@ cpu_R[dc->op2], cpu_R[dc->op2], tcg_const_tl(imm), 4); return 2; } -static unsigned int dec_btstq(DisasContext *dc) +static int dec_btstq(DisasContext *dc) { dc->op1 = EXTRACT_FIELD(dc->ir, 0, 4); LOG_DIS("btstq %u, $r%d\n", dc->op1, dc->op2); @@ -1448,7 +1491,7 @@ dc->flags_uptodate = 1; return 2; } -static unsigned int dec_asrq(DisasContext *dc) +static int dec_asrq(DisasContext *dc) { dc->op1 = EXTRACT_FIELD(dc->ir, 0, 4); LOG_DIS("asrq %u, $r%d\n", dc->op1, dc->op2); @@ -1460,7 +1503,7 @@ cpu_R[dc->op2], cpu_R[dc->op2], 4); return 2; } -static unsigned int dec_lslq(DisasContext *dc) +static int dec_lslq(DisasContext *dc) { dc->op1 = EXTRACT_FIELD(dc->ir, 0, 4); LOG_DIS("lslq %u, $r%d\n", dc->op1, dc->op2); @@ -1474,7 +1517,7 @@ cpu_R[dc->op2], cpu_R[dc->op2], 4); return 2; } -static unsigned int dec_lsrq(DisasContext *dc) +static int dec_lsrq(DisasContext *dc) { dc->op1 = EXTRACT_FIELD(dc->ir, 0, 4); LOG_DIS("lsrq %u, $r%d\n", dc->op1, dc->op2); @@ -1488,7 +1531,7 @@ return 2; } -static unsigned int dec_move_r(DisasContext *dc) +static int dec_move_r(DisasContext *dc) { int size = memsize_zz(dc); @@ -1516,7 +1559,7 @@ return 2; } -static unsigned int dec_scc_r(DisasContext *dc) +static int dec_scc_r(DisasContext *dc) { int cond = dc->op2; @@ -1559,7 +1602,7 @@ } } -static unsigned int dec_and_r(DisasContext *dc) +static int dec_and_r(DisasContext *dc) { TCGv t[2]; int size = memsize_zz(dc); @@ -1576,7 +1619,7 @@ return 2; } -static unsigned int dec_lz_r(DisasContext *dc) +static int dec_lz_r(DisasContext *dc) { TCGv t0; LOG_DIS("lz $r%u, $r%u\n", @@ -1589,7 +1632,7 @@ return 2; } -static unsigned int dec_lsl_r(DisasContext *dc) +static int dec_lsl_r(DisasContext *dc) { TCGv t[2]; int size = memsize_zz(dc); @@ -1606,7 +1649,7 @@ return 2; } -static unsigned int dec_lsr_r(DisasContext *dc) +static int dec_lsr_r(DisasContext *dc) { TCGv t[2]; int size = memsize_zz(dc); @@ -1623,7 +1666,7 @@ return 2; } -static unsigned int dec_asr_r(DisasContext *dc) +static int dec_asr_r(DisasContext *dc) { TCGv t[2]; int size = memsize_zz(dc); @@ -1640,7 +1683,7 @@ return 2; } -static unsigned int dec_muls_r(DisasContext *dc) +static int dec_muls_r(DisasContext *dc) { TCGv t[2]; int size = memsize_zz(dc); @@ -1656,7 +1699,7 @@ return 2; } -static unsigned int dec_mulu_r(DisasContext *dc) +static int dec_mulu_r(DisasContext *dc) { TCGv t[2]; int size = memsize_zz(dc); @@ -1673,7 +1716,7 @@ } -static unsigned int dec_dstep_r(DisasContext *dc) +static int dec_dstep_r(DisasContext *dc) { LOG_DIS("dstep $r%u, $r%u\n", dc->op1, dc->op2); cris_cc_mask(dc, CC_MASK_NZ); @@ -1682,7 +1725,7 @@ return 2; } -static unsigned int dec_xor_r(DisasContext *dc) +static int dec_xor_r(DisasContext *dc) { TCGv t[2]; int size = memsize_zz(dc); @@ -1698,7 +1741,7 @@ return 2; } -static unsigned int dec_bound_r(DisasContext *dc) +static int dec_bound_r(DisasContext *dc) { TCGv l0; int size = memsize_zz(dc); @@ -1712,7 +1755,7 @@ return 2; } -static unsigned int dec_cmp_r(DisasContext *dc) +static int dec_cmp_r(DisasContext *dc) { TCGv t[2]; int size = memsize_zz(dc); @@ -1727,7 +1770,7 @@ return 2; } -static unsigned int dec_abs_r(DisasContext *dc) +static int dec_abs_r(DisasContext *dc) { TCGv t0; @@ -1746,7 +1789,7 @@ return 2; } -static unsigned int dec_add_r(DisasContext *dc) +static int dec_add_r(DisasContext *dc) { TCGv t[2]; int size = memsize_zz(dc); @@ -1761,7 +1804,7 @@ return 2; } -static unsigned int dec_addc_r(DisasContext *dc) +static int dec_addc_r(DisasContext *dc) { LOG_DIS("addc $r%u, $r%u\n", dc->op1, dc->op2); @@ -1776,7 +1819,7 @@ return 2; } -static unsigned int dec_mcp_r(DisasContext *dc) +static int dec_mcp_r(DisasContext *dc) { LOG_DIS("mcp $p%u, $r%u\n", dc->op2, dc->op1); @@ -1803,7 +1846,7 @@ } #endif -static unsigned int dec_swap_r(DisasContext *dc) +static int dec_swap_r(DisasContext *dc) { TCGv t0; #if DISAS_CRIS @@ -1829,7 +1872,7 @@ return 2; } -static unsigned int dec_or_r(DisasContext *dc) +static int dec_or_r(DisasContext *dc) { TCGv t[2]; int size = memsize_zz(dc); @@ -1843,7 +1886,7 @@ return 2; } -static unsigned int dec_addi_r(DisasContext *dc) +static int dec_addi_r(DisasContext *dc) { TCGv t0; LOG_DIS("addi.%c $r%u, $r%u\n", @@ -1856,7 +1899,7 @@ return 2; } -static unsigned int dec_addi_acr(DisasContext *dc) +static int dec_addi_acr(DisasContext *dc) { TCGv t0; LOG_DIS("addi.%c $r%u, $r%u, $acr\n", @@ -1869,7 +1912,7 @@ return 2; } -static unsigned int dec_neg_r(DisasContext *dc) +static int dec_neg_r(DisasContext *dc) { TCGv t[2]; int size = memsize_zz(dc); @@ -1884,7 +1927,7 @@ return 2; } -static unsigned int dec_btst_r(DisasContext *dc) +static int dec_btst_r(DisasContext *dc) { LOG_DIS("btst $r%u, $r%u\n", dc->op1, dc->op2); @@ -1899,7 +1942,7 @@ return 2; } -static unsigned int dec_sub_r(DisasContext *dc) +static int dec_sub_r(DisasContext *dc) { TCGv t[2]; int size = memsize_zz(dc); @@ -1914,7 +1957,7 @@ } /* Zero extension. From size to dword. */ -static unsigned int dec_movu_r(DisasContext *dc) +static int dec_movu_r(DisasContext *dc) { TCGv t0; int size = memsize_z(dc); @@ -1931,7 +1974,7 @@ } /* Sign extension. From size to dword. */ -static unsigned int dec_movs_r(DisasContext *dc) +static int dec_movs_r(DisasContext *dc) { TCGv t0; int size = memsize_z(dc); @@ -1950,7 +1993,7 @@ } /* zero extension. From size to dword. */ -static unsigned int dec_addu_r(DisasContext *dc) +static int dec_addu_r(DisasContext *dc) { TCGv t0; int size = memsize_z(dc); @@ -1969,7 +2012,7 @@ } /* Sign extension. From size to dword. */ -static unsigned int dec_adds_r(DisasContext *dc) +static int dec_adds_r(DisasContext *dc) { TCGv t0; int size = memsize_z(dc); @@ -1988,7 +2031,7 @@ } /* Zero extension. From size to dword. */ -static unsigned int dec_subu_r(DisasContext *dc) +static int dec_subu_r(DisasContext *dc) { TCGv t0; int size = memsize_z(dc); @@ -2007,7 +2050,7 @@ } /* Sign extension. From size to dword. */ -static unsigned int dec_subs_r(DisasContext *dc) +static int dec_subs_r(DisasContext *dc) { TCGv t0; int size = memsize_z(dc); @@ -2025,7 +2068,7 @@ return 2; } -static unsigned int dec_setclrf(DisasContext *dc) +static int dec_setclrf(DisasContext *dc) { uint32_t flags; int set = (~dc->opcode >> 2) & 1; @@ -2058,16 +2101,17 @@ dc->flags_x = 0; } - /* Break the TB if the P flag changes. */ - if (flags & P_FLAG) { - if ((set && !(dc->tb_flags & P_FLAG)) - || (!set && (dc->tb_flags & P_FLAG))) { - tcg_gen_movi_tl(env_pc, dc->pc + 2); - dc->is_jmp = DISAS_UPDATE; - dc->cpustate_changed = 1; - } + /* Break the TB if any of the SPI flag changes. */ + if (flags & (P_FLAG | S_FLAG)) { + tcg_gen_movi_tl(env_pc, dc->pc + 2); + dc->is_jmp = DISAS_UPDATE; + dc->cpustate_changed = 1; } - if (flags & S_FLAG) { + + /* For the I flag, only act on posedge. */ + if ((flags & I_FLAG)) { + tcg_gen_movi_tl(env_pc, dc->pc + 2); + dc->is_jmp = DISAS_UPDATE; dc->cpustate_changed = 1; } @@ -2095,14 +2139,14 @@ return 2; } -static unsigned int dec_move_rs(DisasContext *dc) +static int dec_move_rs(DisasContext *dc) { LOG_DIS("move $r%u, $s%u\n", dc->op1, dc->op2); cris_cc_mask(dc, 0); gen_helper_movl_sreg_reg(tcg_const_tl(dc->op2), tcg_const_tl(dc->op1)); return 2; } -static unsigned int dec_move_sr(DisasContext *dc) +static int dec_move_sr(DisasContext *dc) { LOG_DIS("move $s%u, $r%u\n", dc->op2, dc->op1); cris_cc_mask(dc, 0); @@ -2110,7 +2154,7 @@ return 2; } -static unsigned int dec_move_rp(DisasContext *dc) +static int dec_move_rp(DisasContext *dc) { TCGv t[2]; LOG_DIS("move $r%u, $p%u\n", dc->op1, dc->op2); @@ -2140,24 +2184,29 @@ tcg_temp_free(t[0]); return 2; } -static unsigned int dec_move_pr(DisasContext *dc) +static int dec_move_pr(DisasContext *dc) { TCGv t0; - LOG_DIS("move $p%u, $r%u\n", dc->op1, dc->op2); + LOG_DIS("move $p%u, $r%u\n", dc->op2, dc->op1); cris_cc_mask(dc, 0); if (dc->op2 == PR_CCS) cris_evaluate_flags(dc); - t0 = tcg_temp_new(); - t_gen_mov_TN_preg(t0, dc->op2); - cris_alu(dc, CC_OP_MOVE, - cpu_R[dc->op1], cpu_R[dc->op1], t0, preg_sizes[dc->op2]); - tcg_temp_free(t0); + if (dc->op2 == PR_DZ) { + tcg_gen_movi_tl(cpu_R[dc->op1], 0); + } else { + t0 = tcg_temp_new(); + t_gen_mov_TN_preg(t0, dc->op2); + cris_alu(dc, CC_OP_MOVE, + cpu_R[dc->op1], cpu_R[dc->op1], t0, + preg_sizes[dc->op2]); + tcg_temp_free(t0); + } return 2; } -static unsigned int dec_move_mr(DisasContext *dc) +static int dec_move_mr(DisasContext *dc) { int memsize = memsize_zz(dc); int insn_len; @@ -2199,7 +2248,7 @@ tcg_temp_free(t[1]); } -static unsigned int dec_movs_m(DisasContext *dc) +static int dec_movs_m(DisasContext *dc) { TCGv t[2]; int memsize = memsize_z(dc); @@ -2220,7 +2269,7 @@ return insn_len; } -static unsigned int dec_addu_m(DisasContext *dc) +static int dec_addu_m(DisasContext *dc) { TCGv t[2]; int memsize = memsize_z(dc); @@ -2241,7 +2290,7 @@ return insn_len; } -static unsigned int dec_adds_m(DisasContext *dc) +static int dec_adds_m(DisasContext *dc) { TCGv t[2]; int memsize = memsize_z(dc); @@ -2261,7 +2310,7 @@ return insn_len; } -static unsigned int dec_subu_m(DisasContext *dc) +static int dec_subu_m(DisasContext *dc) { TCGv t[2]; int memsize = memsize_z(dc); @@ -2281,7 +2330,7 @@ return insn_len; } -static unsigned int dec_subs_m(DisasContext *dc) +static int dec_subs_m(DisasContext *dc) { TCGv t[2]; int memsize = memsize_z(dc); @@ -2301,7 +2350,7 @@ return insn_len; } -static unsigned int dec_movu_m(DisasContext *dc) +static int dec_movu_m(DisasContext *dc) { TCGv t[2]; int memsize = memsize_z(dc); @@ -2321,7 +2370,7 @@ return insn_len; } -static unsigned int dec_cmpu_m(DisasContext *dc) +static int dec_cmpu_m(DisasContext *dc) { TCGv t[2]; int memsize = memsize_z(dc); @@ -2340,7 +2389,7 @@ return insn_len; } -static unsigned int dec_cmps_m(DisasContext *dc) +static int dec_cmps_m(DisasContext *dc) { TCGv t[2]; int memsize = memsize_z(dc); @@ -2361,7 +2410,7 @@ return insn_len; } -static unsigned int dec_cmp_m(DisasContext *dc) +static int dec_cmp_m(DisasContext *dc) { TCGv t[2]; int memsize = memsize_zz(dc); @@ -2382,7 +2431,7 @@ return insn_len; } -static unsigned int dec_test_m(DisasContext *dc) +static int dec_test_m(DisasContext *dc) { TCGv t[2]; int memsize = memsize_zz(dc); @@ -2406,7 +2455,7 @@ return insn_len; } -static unsigned int dec_and_m(DisasContext *dc) +static int dec_and_m(DisasContext *dc) { TCGv t[2]; int memsize = memsize_zz(dc); @@ -2425,7 +2474,7 @@ return insn_len; } -static unsigned int dec_add_m(DisasContext *dc) +static int dec_add_m(DisasContext *dc) { TCGv t[2]; int memsize = memsize_zz(dc); @@ -2445,7 +2494,7 @@ return insn_len; } -static unsigned int dec_addo_m(DisasContext *dc) +static int dec_addo_m(DisasContext *dc) { TCGv t[2]; int memsize = memsize_zz(dc); @@ -2464,7 +2513,7 @@ return insn_len; } -static unsigned int dec_bound_m(DisasContext *dc) +static int dec_bound_m(DisasContext *dc) { TCGv l[2]; int memsize = memsize_zz(dc); @@ -2485,7 +2534,7 @@ return insn_len; } -static unsigned int dec_addc_mr(DisasContext *dc) +static int dec_addc_mr(DisasContext *dc) { TCGv t[2]; int insn_len = 2; @@ -2508,7 +2557,7 @@ return insn_len; } -static unsigned int dec_sub_m(DisasContext *dc) +static int dec_sub_m(DisasContext *dc) { TCGv t[2]; int memsize = memsize_zz(dc); @@ -2527,7 +2576,7 @@ return insn_len; } -static unsigned int dec_or_m(DisasContext *dc) +static int dec_or_m(DisasContext *dc) { TCGv t[2]; int memsize = memsize_zz(dc); @@ -2547,7 +2596,7 @@ return insn_len; } -static unsigned int dec_move_mp(DisasContext *dc) +static int dec_move_mp(DisasContext *dc) { TCGv t[2]; int memsize = memsize_zz(dc); @@ -2579,7 +2628,7 @@ return insn_len; } -static unsigned int dec_move_pm(DisasContext *dc) +static int dec_move_pm(DisasContext *dc) { TCGv t0; int memsize; @@ -2605,7 +2654,7 @@ return 2; } -static unsigned int dec_movem_mr(DisasContext *dc) +static int dec_movem_mr(DisasContext *dc) { TCGv_i64 tmp[16]; TCGv tmp32; @@ -2652,7 +2701,7 @@ return 2; } -static unsigned int dec_movem_rm(DisasContext *dc) +static int dec_movem_rm(DisasContext *dc) { TCGv tmp; TCGv addr; @@ -2681,7 +2730,7 @@ return 2; } -static unsigned int dec_move_rm(DisasContext *dc) +static int dec_move_rm(DisasContext *dc) { int memsize; @@ -2700,7 +2749,7 @@ return 2; } -static unsigned int dec_lapcq(DisasContext *dc) +static int dec_lapcq(DisasContext *dc) { LOG_DIS("lapcq %x, $r%u\n", dc->pc + dc->op1*2, dc->op2); @@ -2709,7 +2758,7 @@ return 2; } -static unsigned int dec_lapc_im(DisasContext *dc) +static int dec_lapc_im(DisasContext *dc) { unsigned int rd; int32_t imm; @@ -2718,17 +2767,17 @@ rd = dc->op2; cris_cc_mask(dc, 0); - imm = ldl_code(dc->pc + 2); + imm = cris_fetch(dc, dc->pc + 2, 4, 0); LOG_DIS("lapc 0x%x, $r%u\n", imm + dc->pc, dc->op2); pc = dc->pc; pc += imm; - t_gen_mov_reg_TN(rd, tcg_const_tl(pc)); + tcg_gen_movi_tl(cpu_R[rd], pc); return 6; } /* Jump to special reg. */ -static unsigned int dec_jump_p(DisasContext *dc) +static int dec_jump_p(DisasContext *dc) { LOG_DIS("jump $p%u\n", dc->op2); @@ -2743,7 +2792,7 @@ } /* Jump and save. */ -static unsigned int dec_jas_r(DisasContext *dc) +static int dec_jas_r(DisasContext *dc) { LOG_DIS("jas $r%u, $p%u\n", dc->op1, dc->op2); cris_cc_mask(dc, 0); @@ -2757,11 +2806,11 @@ return 2; } -static unsigned int dec_jas_im(DisasContext *dc) +static int dec_jas_im(DisasContext *dc) { uint32_t imm; - imm = ldl_code(dc->pc + 2); + imm = cris_fetch(dc, dc->pc + 2, 4, 0); LOG_DIS("jas 0x%x\n", imm); cris_cc_mask(dc, 0); @@ -2773,11 +2822,11 @@ return 6; } -static unsigned int dec_jasc_im(DisasContext *dc) +static int dec_jasc_im(DisasContext *dc) { uint32_t imm; - imm = ldl_code(dc->pc + 2); + imm = cris_fetch(dc, dc->pc + 2, 4, 0); LOG_DIS("jasc 0x%x\n", imm); cris_cc_mask(dc, 0); @@ -2789,7 +2838,7 @@ return 6; } -static unsigned int dec_jasc_r(DisasContext *dc) +static int dec_jasc_r(DisasContext *dc) { LOG_DIS("jasc_r $r%u, $p%u\n", dc->op1, dc->op2); cris_cc_mask(dc, 0); @@ -2800,12 +2849,12 @@ return 2; } -static unsigned int dec_bcc_im(DisasContext *dc) +static int dec_bcc_im(DisasContext *dc) { int32_t offset; uint32_t cond = dc->op2; - offset = ldsw_code(dc->pc + 2); + offset = cris_fetch(dc, dc->pc + 2, 2, 1); LOG_DIS("b%s %d pc=%x dst=%x\n", cc_name(cond), offset, @@ -2817,12 +2866,12 @@ return 4; } -static unsigned int dec_bas_im(DisasContext *dc) +static int dec_bas_im(DisasContext *dc) { int32_t simm; - simm = ldl_code(dc->pc + 2); + simm = cris_fetch(dc, dc->pc + 2, 4, 0); LOG_DIS("bas 0x%x, $p%u\n", dc->pc + simm, dc->op2); cris_cc_mask(dc, 0); @@ -2834,10 +2883,10 @@ return 6; } -static unsigned int dec_basc_im(DisasContext *dc) +static int dec_basc_im(DisasContext *dc) { int32_t simm; - simm = ldl_code(dc->pc + 2); + simm = cris_fetch(dc, dc->pc + 2, 4, 0); LOG_DIS("basc 0x%x, $p%u\n", dc->pc + simm, dc->op2); cris_cc_mask(dc, 0); @@ -2849,7 +2898,7 @@ return 6; } -static unsigned int dec_rfe_etc(DisasContext *dc) +static int dec_rfe_etc(DisasContext *dc) { cris_cc_mask(dc, 0); @@ -2896,17 +2945,17 @@ return 2; } -static unsigned int dec_ftag_fidx_d_m(DisasContext *dc) +static int dec_ftag_fidx_d_m(DisasContext *dc) { return 2; } -static unsigned int dec_ftag_fidx_i_m(DisasContext *dc) +static int dec_ftag_fidx_i_m(DisasContext *dc) { return 2; } -static unsigned int dec_null(DisasContext *dc) +static int dec_null(DisasContext *dc) { printf ("unknown insn pc=%x opc=%x op1=%x op2=%x\n", dc->pc, dc->opcode, dc->op1, dc->op2); @@ -2920,7 +2969,7 @@ uint32_t bits; uint32_t mask; }; - unsigned int (*dec)(DisasContext *dc); + int (*dec)(DisasContext *dc); } decinfo[] = { /* Order matters here. */ {DEC_MOVEQ, dec_moveq}, @@ -3026,17 +3075,16 @@ {{0, 0}, dec_null} }; -static inline unsigned int -cris_decoder(DisasContext *dc) +static unsigned int crisv32_decoder(DisasContext *dc) { - unsigned int insn_len = 2; + int insn_len = 2; int i; if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP))) tcg_gen_debug_insn_start(dc->pc); /* Load a halfword onto the instruction register. */ - dc->ir = lduw_code(dc->pc); + dc->ir = cris_fetch(dc, dc->pc, 2, 0); /* Now decode it. */ dc->opcode = EXTRACT_FIELD(dc->ir, 4, 11); @@ -3090,6 +3138,7 @@ } } +#include "translate_v10.c" /* * Delay slots on QEMU/CRIS. @@ -3143,6 +3192,14 @@ qemu_log_try_set_file(stderr); + if (env->pregs[PR_VR] == 32) { + dc->decoder = crisv32_decoder; + dc->clear_locked_irq = 0; + } else { + dc->decoder = crisv10_decoder; + dc->clear_locked_irq = 1; + } + /* Odd PC indicates that branch is rexecuting due to exception in the * delayslot, like in real hw. */ @@ -3162,12 +3219,14 @@ dc->cc_x_uptodate = 0; dc->cc_mask = 0; dc->update_cc = 0; + dc->clear_prefix = 0; cris_update_cc_op(dc, CC_OP_FLAGS, 4); dc->cc_size_uptodate = -1; /* Decode TB flags. */ - dc->tb_flags = tb->flags & (S_FLAG | P_FLAG | U_FLAG | X_FLAG); + dc->tb_flags = tb->flags & (S_FLAG | P_FLAG | U_FLAG \ + | X_FLAG | PFIX_FLAG); dc->delayed_branch = !!(tb->flags & 7); if (dc->delayed_branch) dc->jmp = JMP_INDIRECT; @@ -3178,14 +3237,14 @@ if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { qemu_log( - "srch=%d pc=%x %x flg=%llx bt=%x ds=%u ccs=%x\n" + "srch=%d pc=%x %x flg=%" PRIx64 " bt=%x ds=%u ccs=%x\n" "pid=%x usp=%x\n" "%x.%x.%x.%x\n" "%x.%x.%x.%x\n" "%x.%x.%x.%x\n" "%x.%x.%x.%x\n", search_pc, dc->pc, dc->ppc, - (unsigned long long)tb->flags, + (uint64_t)tb->flags, env->btarget, (unsigned)tb->flags & 7, env->pregs[PR_CCS], env->pregs[PR_PID], env->pregs[PR_USP], @@ -3233,7 +3292,7 @@ gen_io_start(); dc->clear_x = 1; - insn_len = cris_decoder(dc); + insn_len = dc->decoder(dc); dc->ppc = dc->pc; dc->pc += insn_len; if (dc->clear_x) @@ -3250,8 +3309,36 @@ if (tb->flags & 7) t_gen_mov_env_TN(dslot, tcg_const_tl(0)); - if (dc->jmp == JMP_DIRECT) { - dc->is_jmp = DISAS_NEXT; + if (dc->cpustate_changed || !dc->flagx_known + || (dc->flags_x != (tb->flags & X_FLAG))) { + cris_store_direct_jmp(dc); + } + + if (dc->clear_locked_irq) { + dc->clear_locked_irq = 0; + t_gen_mov_env_TN(locked_irq, + tcg_const_tl(0)); + } + + if (dc->jmp == JMP_DIRECT_CC) { + int l1; + + l1 = gen_new_label(); + cris_evaluate_flags(dc); + + /* Conditional jmp. */ + tcg_gen_brcondi_tl(TCG_COND_EQ, + env_btaken, 0, l1); + gen_goto_tb(dc, 1, dc->jmp_pc); + gen_set_label(l1); + gen_goto_tb(dc, 0, dc->pc); + dc->is_jmp = DISAS_TB_JUMP; + dc->jmp = JMP_NOJMP; + } else if (dc->jmp == JMP_DIRECT) { + cris_evaluate_flags(dc); + gen_goto_tb(dc, 0, dc->jmp_pc); + dc->is_jmp = DISAS_TB_JUMP; + dc->jmp = JMP_NOJMP; } else { t_gen_cc_jmp(env_btarget, tcg_const_tl(dc->pc)); @@ -3271,9 +3358,10 @@ && (dc->pc < next_page_start) && num_insns < max_insns); + if (dc->clear_locked_irq) + t_gen_mov_env_TN(locked_irq, tcg_const_tl(0)); + npc = dc->pc; - if (dc->jmp == JMP_DIRECT && !dc->delayed_branch) - npc = dc->jmp_pc; if (tb->cflags & CF_LAST_IO) gen_io_end(); @@ -3330,8 +3418,9 @@ #ifdef DEBUG_DISAS #if !DISAS_CRIS if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { - log_target_disas(pc_start, dc->pc - pc_start, 0); - qemu_log("\nisize=%d osize=%zd\n", + log_target_disas(pc_start, dc->pc - pc_start, + dc->env->pregs[PR_VR]); + qemu_log("\nisize=%d osize=%td\n", dc->pc - pc_start, gen_opc_ptr - gen_opc_buf); } #endif @@ -3348,8 +3437,7 @@ gen_intermediate_code_internal(env, tb, 1); } -void cpu_dump_state (CPUState *env, FILE *f, - int (*cpu_fprintf)(FILE *f, const char *fmt, ...), +void cpu_dump_state (CPUState *env, FILE *f, fprintf_function cpu_fprintf, int flags) { int i; @@ -3366,13 +3454,13 @@ for (i = 0; i < 16; i++) { - cpu_fprintf(f, "r%2.2d=%8.8x ", i, env->regs[i]); + cpu_fprintf(f, "%s=%8.8x ",regnames[i], env->regs[i]); if ((i + 1) % 4 == 0) cpu_fprintf(f, "\n"); } cpu_fprintf(f, "\nspecial regs:\n"); for (i = 0; i < 16; i++) { - cpu_fprintf(f, "p%2.2d=%8.8x ", i, env->pregs[i]); + cpu_fprintf(f, "%s=%8.8x ", pregnames[i], env->pregs[i]); if ((i + 1) % 4 == 0) cpu_fprintf(f, "\n"); } @@ -3390,6 +3478,39 @@ } +struct +{ + uint32_t vr; + const char *name; +} cris_cores[] = { + {8, "crisv8"}, + {9, "crisv9"}, + {10, "crisv10"}, + {11, "crisv11"}, + {32, "crisv32"}, +}; + +void cris_cpu_list(FILE *f, fprintf_function cpu_fprintf) +{ + unsigned int i; + + (*cpu_fprintf)(f, "Available CPUs:\n"); + for (i = 0; i < ARRAY_SIZE(cris_cores); i++) { + (*cpu_fprintf)(f, " %s\n", cris_cores[i].name); + } +} + +static uint32_t vr_by_name(const char *name) +{ + unsigned int i; + for (i = 0; i < ARRAY_SIZE(cris_cores); i++) { + if (strcmp(name, cris_cores[i].name) == 0) { + return cris_cores[i].vr; + } + } + return 32; +} + CPUCRISState *cpu_cris_init (const char *cpu_model) { CPUCRISState *env; @@ -3398,6 +3519,7 @@ env = qemu_mallocz(sizeof(CPUCRISState)); + env->pregs[PR_VR] = vr_by_name(cpu_model); cpu_exec_init(env); cpu_reset(env); qemu_init_vcpu(env); @@ -3407,6 +3529,15 @@ tcg_initialized = 1; +#define GEN_HELPER 2 +#include "helper.h" + + if (env->pregs[PR_VR] < 32) { + cpu_crisv10_init(env); + return env; + } + + cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env"); cc_x = tcg_global_mem_new(TCG_AREG0, offsetof(CPUState, cc_x), "cc_x"); @@ -3447,26 +3578,26 @@ pregnames[i]); } -#define GEN_HELPER 2 -#include "helper.h" - return env; } void cpu_reset (CPUCRISState *env) { + uint32_t vr; + if (qemu_loglevel_mask(CPU_LOG_RESET)) { qemu_log("CPU Reset (CPU %d)\n", env->cpu_index); log_cpu_state(env, 0); } + vr = env->pregs[PR_VR]; memset(env, 0, offsetof(CPUCRISState, breakpoints)); + env->pregs[PR_VR] = vr; tlb_flush(env, 1); - env->pregs[PR_VR] = 32; #if defined(CONFIG_USER_ONLY) /* start in user mode with interrupts enabled. */ - env->pregs[PR_CCS] |= U_FLAG | I_FLAG; + env->pregs[PR_CCS] |= U_FLAG | I_FLAG | P_FLAG; #else cris_mmu_init(env); env->pregs[PR_CCS] = 0; diff -Nru qemu-kvm-0.12.5+noroms/target-cris/translate_v10.c qemu-kvm-0.14.1/target-cris/translate_v10.c --- qemu-kvm-0.12.5+noroms/target-cris/translate_v10.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/target-cris/translate_v10.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,1245 @@ +/* + * CRISv10 emulation for qemu: main translation routines. + * + * Copyright (c) 2010 AXIS Communications AB + * Written by Edgar E. Iglesias. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include "crisv10-decode.h" + +static const char *regnames_v10[] = +{ + "$r0", "$r1", "$r2", "$r3", + "$r4", "$r5", "$r6", "$r7", + "$r8", "$r9", "$r10", "$r11", + "$r12", "$r13", "$sp", "$pc", +}; + +static const char *pregnames_v10[] = +{ + "$bz", "$vr", "$p2", "$p3", + "$wz", "$ccr", "$p6-prefix", "$mof", + "$dz", "$ibr", "$irp", "$srp", + "$bar", "$dccr", "$brp", "$usp", +}; + +/* We need this table to handle preg-moves with implicit width. */ +static int preg_sizes_v10[] = { + 1, /* bz. */ + 1, /* vr. */ + 1, /* pid. */ + 1, /* srs. */ + 2, /* wz. */ + 2, 2, 4, + 4, 4, 4, 4, + 4, 4, 4, 4, +}; + +static inline int dec10_size(unsigned int size) +{ + size++; + if (size == 3) + size++; + return size; +} + +static inline void cris_illegal_insn(DisasContext *dc) +{ + qemu_log("illegal insn at pc=%x\n", dc->pc); + t_gen_raise_exception(EXCP_BREAK); +} + +/* Prefix flag and register are used to handle the more complex + addressing modes. */ +static void cris_set_prefix(DisasContext *dc) +{ + dc->clear_prefix = 0; + dc->tb_flags |= PFIX_FLAG; + tcg_gen_ori_tl(cpu_PR[PR_CCS], cpu_PR[PR_CCS], PFIX_FLAG); + + /* prefix insns dont clear the x flag. */ + dc->clear_x = 0; + cris_lock_irq(dc); +} + +static void crisv10_prepare_memaddr(DisasContext *dc, + TCGv addr, unsigned int size) +{ + if (dc->tb_flags & PFIX_FLAG) { + tcg_gen_mov_tl(addr, cpu_PR[PR_PREFIX]); + } else { + tcg_gen_mov_tl(addr, cpu_R[dc->src]); + } +} + +static unsigned int crisv10_post_memaddr(DisasContext *dc, unsigned int size) +{ + unsigned int insn_len = 0; + + if (dc->tb_flags & PFIX_FLAG) { + if (dc->mode == CRISV10_MODE_AUTOINC) { + tcg_gen_mov_tl(cpu_R[dc->src], cpu_PR[PR_PREFIX]); + } + } else { + if (dc->mode == CRISV10_MODE_AUTOINC) { + if (dc->src == 15) { + insn_len += size & ~1; + } else { + tcg_gen_addi_tl(cpu_R[dc->src], cpu_R[dc->src], size); + } + } + } + return insn_len; +} + +static int dec10_prep_move_m(DisasContext *dc, int s_ext, int memsize, + TCGv dst) +{ + unsigned int rs; + uint32_t imm; + int is_imm; + int insn_len = 0; + + rs = dc->src; + is_imm = rs == 15 && !(dc->tb_flags & PFIX_FLAG); + LOG_DIS("rs=%d rd=%d is_imm=%d mode=%d pfix=%d\n", + rs, dc->dst, is_imm, dc->mode, dc->tb_flags & PFIX_FLAG); + + /* Load [$rs] onto T1. */ + if (is_imm) { + if (memsize != 4) { + if (s_ext) { + if (memsize == 1) + imm = ldsb_code(dc->pc + 2); + else + imm = ldsw_code(dc->pc + 2); + } else { + if (memsize == 1) + imm = ldub_code(dc->pc + 2); + else + imm = lduw_code(dc->pc + 2); + } + } else + imm = ldl_code(dc->pc + 2); + + tcg_gen_movi_tl(dst, imm); + + if (dc->mode == CRISV10_MODE_AUTOINC) { + insn_len += memsize; + if (memsize == 1) + insn_len++; + tcg_gen_addi_tl(cpu_R[15], cpu_R[15], insn_len); + } + } else { + TCGv addr; + + addr = tcg_temp_new(); + cris_flush_cc_state(dc); + crisv10_prepare_memaddr(dc, addr, memsize); + gen_load(dc, dst, addr, memsize, 0); + if (s_ext) + t_gen_sext(dst, dst, memsize); + else + t_gen_zext(dst, dst, memsize); + insn_len += crisv10_post_memaddr(dc, memsize); + tcg_temp_free(addr); + } + + if (dc->mode == CRISV10_MODE_INDIRECT && (dc->tb_flags & PFIX_FLAG)) { + dc->dst = dc->src; + } + return insn_len; +} + +static unsigned int dec10_quick_imm(DisasContext *dc) +{ + int32_t imm, simm; + int op; + + /* sign extend. */ + imm = dc->ir & ((1 << 6) - 1); + simm = (int8_t) (imm << 2); + simm >>= 2; + switch (dc->opcode) { + case CRISV10_QIMM_BDAP_R0: + case CRISV10_QIMM_BDAP_R1: + case CRISV10_QIMM_BDAP_R2: + case CRISV10_QIMM_BDAP_R3: + simm = (int8_t)dc->ir; + LOG_DIS("bdap %d $r%d\n", simm, dc->dst); + LOG_DIS("pc=%x mode=%x quickimm %d r%d r%d\n", + dc->pc, dc->mode, dc->opcode, dc->src, dc->dst); + cris_set_prefix(dc); + if (dc->dst == 15) { + tcg_gen_movi_tl(cpu_PR[PR_PREFIX], dc->pc + 2 + simm); + } else { + tcg_gen_addi_tl(cpu_PR[PR_PREFIX], cpu_R[dc->dst], simm); + } + break; + + case CRISV10_QIMM_MOVEQ: + LOG_DIS("moveq %d, $r%d\n", simm, dc->dst); + + cris_cc_mask(dc, CC_MASK_NZVC); + cris_alu(dc, CC_OP_MOVE, cpu_R[dc->dst], + cpu_R[dc->dst], tcg_const_tl(simm), 4); + break; + case CRISV10_QIMM_CMPQ: + LOG_DIS("cmpq %d, $r%d\n", simm, dc->dst); + + cris_cc_mask(dc, CC_MASK_NZVC); + cris_alu(dc, CC_OP_CMP, cpu_R[dc->dst], + cpu_R[dc->dst], tcg_const_tl(simm), 4); + break; + case CRISV10_QIMM_ADDQ: + LOG_DIS("addq %d, $r%d\n", imm, dc->dst); + + cris_cc_mask(dc, CC_MASK_NZVC); + cris_alu(dc, CC_OP_ADD, cpu_R[dc->dst], + cpu_R[dc->dst], tcg_const_tl(imm), 4); + break; + case CRISV10_QIMM_ANDQ: + LOG_DIS("andq %d, $r%d\n", simm, dc->dst); + + cris_cc_mask(dc, CC_MASK_NZVC); + cris_alu(dc, CC_OP_AND, cpu_R[dc->dst], + cpu_R[dc->dst], tcg_const_tl(simm), 4); + break; + case CRISV10_QIMM_ASHQ: + LOG_DIS("ashq %d, $r%d\n", simm, dc->dst); + + cris_cc_mask(dc, CC_MASK_NZVC); + op = imm & (1 << 5); + imm &= 0x1f; + if (op) { + cris_alu(dc, CC_OP_ASR, cpu_R[dc->dst], + cpu_R[dc->dst], tcg_const_tl(imm), 4); + } else { + /* BTST */ + cris_update_cc_op(dc, CC_OP_FLAGS, 4); + gen_helper_btst(cpu_PR[PR_CCS], cpu_R[dc->dst], + tcg_const_tl(imm), cpu_PR[PR_CCS]); + } + break; + case CRISV10_QIMM_LSHQ: + LOG_DIS("lshq %d, $r%d\n", simm, dc->dst); + + op = CC_OP_LSL; + if (imm & (1 << 5)) { + op = CC_OP_LSR; + } + imm &= 0x1f; + cris_cc_mask(dc, CC_MASK_NZVC); + cris_alu(dc, op, cpu_R[dc->dst], + cpu_R[dc->dst], tcg_const_tl(imm), 4); + break; + case CRISV10_QIMM_SUBQ: + LOG_DIS("subq %d, $r%d\n", imm, dc->dst); + + cris_cc_mask(dc, CC_MASK_NZVC); + cris_alu(dc, CC_OP_SUB, cpu_R[dc->dst], + cpu_R[dc->dst], tcg_const_tl(imm), 4); + break; + case CRISV10_QIMM_ORQ: + LOG_DIS("andq %d, $r%d\n", simm, dc->dst); + + cris_cc_mask(dc, CC_MASK_NZVC); + cris_alu(dc, CC_OP_OR, cpu_R[dc->dst], + cpu_R[dc->dst], tcg_const_tl(simm), 4); + break; + + case CRISV10_QIMM_BCC_R0: + if (!dc->ir) { + cpu_abort(dc->env, "opcode zero\n"); + } + case CRISV10_QIMM_BCC_R1: + case CRISV10_QIMM_BCC_R2: + case CRISV10_QIMM_BCC_R3: + imm = dc->ir & 0xff; + /* bit 0 is a sign bit. */ + if (imm & 1) { + imm |= 0xffffff00; /* sign extend. */ + imm &= ~1; /* get rid of the sign bit. */ + } + imm += 2; + LOG_DIS("b%s %d\n", cc_name(dc->cond), imm); + + cris_cc_mask(dc, 0); + cris_prepare_cc_branch(dc, imm, dc->cond); + break; + + default: + LOG_DIS("pc=%x mode=%x quickimm %d r%d r%d\n", + dc->pc, dc->mode, dc->opcode, dc->src, dc->dst); + cpu_abort(dc->env, "Unhandled quickimm\n"); + break; + } + return 2; +} + +static unsigned int dec10_setclrf(DisasContext *dc) +{ + uint32_t flags; + unsigned int set = ~dc->opcode & 1; + + flags = EXTRACT_FIELD(dc->ir, 0, 3) + | (EXTRACT_FIELD(dc->ir, 12, 15) << 4); + LOG_DIS("%s set=%d flags=%x\n", __func__, set, flags); + + + if (flags & X_FLAG) { + dc->flagx_known = 1; + if (set) + dc->flags_x = X_FLAG; + else + dc->flags_x = 0; + } + + cris_evaluate_flags (dc); + cris_update_cc_op(dc, CC_OP_FLAGS, 4); + cris_update_cc_x(dc); + tcg_gen_movi_tl(cc_op, dc->cc_op); + + if (set) { + tcg_gen_ori_tl(cpu_PR[PR_CCS], cpu_PR[PR_CCS], flags); + } else { + tcg_gen_andi_tl(cpu_PR[PR_CCS], cpu_PR[PR_CCS], ~flags); + } + + dc->flags_uptodate = 1; + dc->clear_x = 0; + cris_lock_irq(dc); + return 2; +} + +static inline void dec10_reg_prep_sext(DisasContext *dc, int size, int sext, + TCGv dd, TCGv ds, TCGv sd, TCGv ss) +{ + if (sext) { + t_gen_sext(dd, sd, size); + t_gen_sext(ds, ss, size); + } else { + t_gen_zext(dd, sd, size); + t_gen_zext(ds, ss, size); + } +} + +static void dec10_reg_alu(DisasContext *dc, int op, int size, int sext) +{ + TCGv t[2]; + + t[0] = tcg_temp_new(); + t[1] = tcg_temp_new(); + dec10_reg_prep_sext(dc, size, sext, + t[0], t[1], cpu_R[dc->dst], cpu_R[dc->src]); + + if (op == CC_OP_LSL || op == CC_OP_LSR || op == CC_OP_ASR) { + tcg_gen_andi_tl(t[1], t[1], 63); + } + + assert(dc->dst != 15); + cris_alu(dc, op, cpu_R[dc->dst], t[0], t[1], size); + tcg_temp_free(t[0]); + tcg_temp_free(t[1]); +} + +static void dec10_reg_bound(DisasContext *dc, int size) +{ + TCGv t; + + t = tcg_temp_local_new(); + t_gen_zext(t, cpu_R[dc->src], size); + cris_alu(dc, CC_OP_BOUND, cpu_R[dc->dst], cpu_R[dc->dst], t, 4); + tcg_temp_free(t); +} + +static void dec10_reg_mul(DisasContext *dc, int size, int sext) +{ + int op = sext ? CC_OP_MULS : CC_OP_MULU; + TCGv t[2]; + + t[0] = tcg_temp_new(); + t[1] = tcg_temp_new(); + dec10_reg_prep_sext(dc, size, sext, + t[0], t[1], cpu_R[dc->dst], cpu_R[dc->src]); + + cris_alu(dc, op, cpu_R[dc->dst], t[0], t[1], 4); + + tcg_temp_free(t[0]); + tcg_temp_free(t[1]); +} + + +static void dec10_reg_movs(DisasContext *dc) +{ + int size = (dc->size & 1) + 1; + TCGv t; + + LOG_DIS("movx.%d $r%d, $r%d\n", size, dc->src, dc->dst); + cris_cc_mask(dc, CC_MASK_NZVC); + + t = tcg_temp_new(); + if (dc->ir & 32) + t_gen_sext(t, cpu_R[dc->src], size); + else + t_gen_zext(t, cpu_R[dc->src], size); + + cris_alu(dc, CC_OP_MOVE, cpu_R[dc->dst], cpu_R[dc->dst], t, 4); + tcg_temp_free(t); +} + +static void dec10_reg_alux(DisasContext *dc, int op) +{ + int size = (dc->size & 1) + 1; + TCGv t; + + LOG_DIS("movx.%d $r%d, $r%d\n", size, dc->src, dc->dst); + cris_cc_mask(dc, CC_MASK_NZVC); + + t = tcg_temp_new(); + if (dc->ir & 32) + t_gen_sext(t, cpu_R[dc->src], size); + else + t_gen_zext(t, cpu_R[dc->src], size); + + cris_alu(dc, op, cpu_R[dc->dst], cpu_R[dc->dst], t, 4); + tcg_temp_free(t); +} + +static void dec10_reg_mov_pr(DisasContext *dc) +{ + LOG_DIS("move p%d r%d sz=%d\n", dc->dst, dc->src, preg_sizes_v10[dc->dst]); + cris_lock_irq(dc); + if (dc->src == 15) { + tcg_gen_mov_tl(env_btarget, cpu_PR[dc->dst]); + cris_prepare_jmp(dc, JMP_INDIRECT); + return; + } + if (dc->dst == PR_CCS) { + cris_evaluate_flags(dc); + } + cris_alu(dc, CC_OP_MOVE, cpu_R[dc->src], + cpu_R[dc->src], cpu_PR[dc->dst], preg_sizes_v10[dc->dst]); +} + +static void dec10_reg_abs(DisasContext *dc) +{ + TCGv t0; + + LOG_DIS("abs $r%u, $r%u\n", dc->src, dc->dst); + + assert(dc->dst != 15); + t0 = tcg_temp_new(); + tcg_gen_sari_tl(t0, cpu_R[dc->src], 31); + tcg_gen_xor_tl(cpu_R[dc->dst], cpu_R[dc->src], t0); + tcg_gen_sub_tl(t0, cpu_R[dc->dst], t0); + + cris_alu(dc, CC_OP_MOVE, cpu_R[dc->dst], cpu_R[dc->dst], t0, 4); + tcg_temp_free(t0); +} + +static void dec10_reg_swap(DisasContext *dc) +{ + TCGv t0; + + LOG_DIS("not $r%d, $r%d\n", dc->src, dc->dst); + + cris_cc_mask(dc, CC_MASK_NZVC); + t0 = tcg_temp_new(); + t_gen_mov_TN_reg(t0, dc->src); + if (dc->dst & 8) + tcg_gen_not_tl(t0, t0); + if (dc->dst & 4) + t_gen_swapw(t0, t0); + if (dc->dst & 2) + t_gen_swapb(t0, t0); + if (dc->dst & 1) + t_gen_swapr(t0, t0); + cris_alu(dc, CC_OP_MOVE, cpu_R[dc->src], cpu_R[dc->src], t0, 4); + tcg_temp_free(t0); +} + +static void dec10_reg_scc(DisasContext *dc) +{ + int cond = dc->dst; + + LOG_DIS("s%s $r%u\n", cc_name(cond), dc->src); + + if (cond != CC_A) + { + int l1; + + gen_tst_cc (dc, cpu_R[dc->src], cond); + l1 = gen_new_label(); + tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_R[dc->src], 0, l1); + tcg_gen_movi_tl(cpu_R[dc->src], 1); + gen_set_label(l1); + } else { + tcg_gen_movi_tl(cpu_R[dc->src], 1); + } + + cris_cc_mask(dc, 0); +} + +static unsigned int dec10_reg(DisasContext *dc) +{ + TCGv t; + unsigned int insn_len = 2; + unsigned int size = dec10_size(dc->size); + unsigned int tmp; + + if (dc->size != 3) { + switch (dc->opcode) { + case CRISV10_REG_MOVE_R: + LOG_DIS("move.%d $r%d, $r%d\n", dc->size, dc->src, dc->dst); + cris_cc_mask(dc, CC_MASK_NZVC); + dec10_reg_alu(dc, CC_OP_MOVE, size, 0); + if (dc->dst == 15) { + tcg_gen_mov_tl(env_btarget, cpu_R[dc->dst]); + cris_prepare_jmp(dc, JMP_INDIRECT); + dc->delayed_branch = 1; + } + break; + case CRISV10_REG_MOVX: + cris_cc_mask(dc, CC_MASK_NZVC); + dec10_reg_movs(dc); + break; + case CRISV10_REG_ADDX: + cris_cc_mask(dc, CC_MASK_NZVC); + dec10_reg_alux(dc, CC_OP_ADD); + break; + case CRISV10_REG_SUBX: + cris_cc_mask(dc, CC_MASK_NZVC); + dec10_reg_alux(dc, CC_OP_SUB); + break; + case CRISV10_REG_ADD: + LOG_DIS("add $r%d, $r%d sz=%d\n", dc->src, dc->dst, size); + cris_cc_mask(dc, CC_MASK_NZVC); + dec10_reg_alu(dc, CC_OP_ADD, size, 0); + break; + case CRISV10_REG_SUB: + LOG_DIS("sub $r%d, $r%d sz=%d\n", dc->src, dc->dst, size); + cris_cc_mask(dc, CC_MASK_NZVC); + dec10_reg_alu(dc, CC_OP_SUB, size, 0); + break; + case CRISV10_REG_CMP: + LOG_DIS("cmp $r%d, $r%d sz=%d\n", dc->src, dc->dst, size); + cris_cc_mask(dc, CC_MASK_NZVC); + dec10_reg_alu(dc, CC_OP_CMP, size, 0); + break; + case CRISV10_REG_BOUND: + LOG_DIS("bound $r%d, $r%d sz=%d\n", dc->src, dc->dst, size); + cris_cc_mask(dc, CC_MASK_NZVC); + dec10_reg_bound(dc, size); + break; + case CRISV10_REG_AND: + LOG_DIS("and $r%d, $r%d sz=%d\n", dc->src, dc->dst, size); + cris_cc_mask(dc, CC_MASK_NZVC); + dec10_reg_alu(dc, CC_OP_AND, size, 0); + break; + case CRISV10_REG_ADDI: + if (dc->src == 15) { + /* nop. */ + return 2; + } + t = tcg_temp_new(); + LOG_DIS("addi r%d r%d size=%d\n", dc->src, dc->dst, dc->size); + tcg_gen_shli_tl(t, cpu_R[dc->dst], dc->size & 3); + tcg_gen_add_tl(cpu_R[dc->src], cpu_R[dc->src], t); + tcg_temp_free(t); + break; + case CRISV10_REG_LSL: + LOG_DIS("lsl $r%d, $r%d sz=%d\n", dc->src, dc->dst, size); + cris_cc_mask(dc, CC_MASK_NZVC); + dec10_reg_alu(dc, CC_OP_LSL, size, 0); + break; + case CRISV10_REG_LSR: + LOG_DIS("lsr $r%d, $r%d sz=%d\n", dc->src, dc->dst, size); + cris_cc_mask(dc, CC_MASK_NZVC); + dec10_reg_alu(dc, CC_OP_LSR, size, 0); + break; + case CRISV10_REG_ASR: + LOG_DIS("asr $r%d, $r%d sz=%d\n", dc->src, dc->dst, size); + cris_cc_mask(dc, CC_MASK_NZVC); + dec10_reg_alu(dc, CC_OP_ASR, size, 1); + break; + case CRISV10_REG_OR: + LOG_DIS("or $r%d, $r%d sz=%d\n", dc->src, dc->dst, size); + cris_cc_mask(dc, CC_MASK_NZVC); + dec10_reg_alu(dc, CC_OP_OR, size, 0); + break; + case CRISV10_REG_NEG: + LOG_DIS("neg $r%d, $r%d sz=%d\n", dc->src, dc->dst, size); + cris_cc_mask(dc, CC_MASK_NZVC); + dec10_reg_alu(dc, CC_OP_NEG, size, 0); + break; + case CRISV10_REG_BIAP: + LOG_DIS("BIAP pc=%x reg %d r%d r%d size=%d\n", dc->pc, + dc->opcode, dc->src, dc->dst, size); + switch (size) { + case 4: tmp = 2; break; + case 2: tmp = 1; break; + case 1: tmp = 0; break; + default: + cpu_abort(dc->env, "Unhandled BIAP"); + break; + } + + t = tcg_temp_new(); + tcg_gen_shli_tl(t, cpu_R[dc->dst], tmp); + if (dc->src == 15) { + tcg_gen_addi_tl(cpu_PR[PR_PREFIX], t, ((dc->pc +2)| 1) + 1); + } else { + tcg_gen_add_tl(cpu_PR[PR_PREFIX], cpu_R[dc->src], t); + } + tcg_temp_free(t); + cris_set_prefix(dc); + break; + + default: + LOG_DIS("pc=%x reg %d r%d r%d\n", dc->pc, + dc->opcode, dc->src, dc->dst); + cpu_abort(dc->env, "Unhandled opcode"); + break; + } + } else { + switch (dc->opcode) { + case CRISV10_REG_MOVX: + cris_cc_mask(dc, CC_MASK_NZVC); + dec10_reg_movs(dc); + break; + case CRISV10_REG_ADDX: + cris_cc_mask(dc, CC_MASK_NZVC); + dec10_reg_alux(dc, CC_OP_ADD); + break; + case CRISV10_REG_SUBX: + cris_cc_mask(dc, CC_MASK_NZVC); + dec10_reg_alux(dc, CC_OP_SUB); + break; + case CRISV10_REG_MOVE_SPR_R: + cris_evaluate_flags(dc); + cris_cc_mask(dc, 0); + dec10_reg_mov_pr(dc); + break; + case CRISV10_REG_MOVE_R_SPR: + LOG_DIS("move r%d p%d\n", dc->src, dc->dst); + cris_evaluate_flags(dc); + if (dc->src != 11) /* fast for srp. */ + dc->cpustate_changed = 1; + t_gen_mov_preg_TN(dc, dc->dst, cpu_R[dc->src]); + break; + case CRISV10_REG_SETF: + case CRISV10_REG_CLEARF: + dec10_setclrf(dc); + break; + case CRISV10_REG_SWAP: + dec10_reg_swap(dc); + break; + case CRISV10_REG_ABS: + cris_cc_mask(dc, CC_MASK_NZVC); + dec10_reg_abs(dc); + break; + case CRISV10_REG_LZ: + LOG_DIS("lz $r%d, $r%d sz=%d\n", dc->src, dc->dst, size); + cris_cc_mask(dc, CC_MASK_NZVC); + dec10_reg_alu(dc, CC_OP_LZ, 4, 0); + break; + case CRISV10_REG_XOR: + LOG_DIS("xor $r%d, $r%d sz=%d\n", dc->src, dc->dst, size); + cris_cc_mask(dc, CC_MASK_NZVC); + dec10_reg_alu(dc, CC_OP_XOR, 4, 0); + break; + case CRISV10_REG_BTST: + LOG_DIS("btst $r%d, $r%d sz=%d\n", dc->src, dc->dst, size); + cris_cc_mask(dc, CC_MASK_NZVC); + cris_update_cc_op(dc, CC_OP_FLAGS, 4); + gen_helper_btst(cpu_PR[PR_CCS], cpu_R[dc->dst], + cpu_R[dc->src], cpu_PR[PR_CCS]); + break; + case CRISV10_REG_DSTEP: + LOG_DIS("dstep $r%d, $r%d sz=%d\n", dc->src, dc->dst, size); + cris_cc_mask(dc, CC_MASK_NZVC); + cris_alu(dc, CC_OP_DSTEP, cpu_R[dc->dst], + cpu_R[dc->dst], cpu_R[dc->src], 4); + break; + case CRISV10_REG_MSTEP: + LOG_DIS("mstep $r%d, $r%d sz=%d\n", dc->src, dc->dst, size); + cris_evaluate_flags(dc); + cris_cc_mask(dc, CC_MASK_NZVC); + cris_alu(dc, CC_OP_MSTEP, cpu_R[dc->dst], + cpu_R[dc->dst], cpu_R[dc->src], 4); + break; + case CRISV10_REG_SCC: + dec10_reg_scc(dc); + break; + default: + LOG_DIS("pc=%x reg %d r%d r%d\n", dc->pc, + dc->opcode, dc->src, dc->dst); + cpu_abort(dc->env, "Unhandled opcode"); + break; + } + } + return insn_len; +} + +static unsigned int dec10_ind_move_m_r(DisasContext *dc, unsigned int size) +{ + unsigned int insn_len = 2; + TCGv t; + + LOG_DIS("%s: move.%d [$r%d], $r%d\n", __func__, + size, dc->src, dc->dst); + + cris_cc_mask(dc, CC_MASK_NZVC); + t = tcg_temp_new(); + insn_len += dec10_prep_move_m(dc, 0, size, t); + cris_alu(dc, CC_OP_MOVE, cpu_R[dc->dst], cpu_R[dc->dst], t, size); + if (dc->dst == 15) { + tcg_gen_mov_tl(env_btarget, cpu_R[dc->dst]); + cris_prepare_jmp(dc, JMP_INDIRECT); + dc->delayed_branch = 1; + return insn_len; + } + + tcg_temp_free(t); + return insn_len; +} + +static unsigned int dec10_ind_move_r_m(DisasContext *dc, unsigned int size) +{ + unsigned int insn_len = 2; + TCGv addr; + + LOG_DIS("move.%d $r%d, [$r%d]\n", dc->size, dc->src, dc->dst); + addr = tcg_temp_new(); + crisv10_prepare_memaddr(dc, addr, size); + gen_store(dc, addr, cpu_R[dc->dst], size); + insn_len += crisv10_post_memaddr(dc, size); + + return insn_len; +} + +static unsigned int dec10_ind_move_m_pr(DisasContext *dc) +{ + unsigned int insn_len = 2, rd = dc->dst; + TCGv t, addr; + + LOG_DIS("move.%d $p%d, [$r%d]\n", dc->size, dc->dst, dc->src); + cris_lock_irq(dc); + + addr = tcg_temp_new(); + t = tcg_temp_new(); + insn_len += dec10_prep_move_m(dc, 0, 4, t); + if (rd == 15) { + tcg_gen_mov_tl(env_btarget, t); + cris_prepare_jmp(dc, JMP_INDIRECT); + dc->delayed_branch = 1; + return insn_len; + } + + tcg_gen_mov_tl(cpu_PR[rd], t); + dc->cpustate_changed = 1; + tcg_temp_free(addr); + tcg_temp_free(t); + return insn_len; +} + +static unsigned int dec10_ind_move_pr_m(DisasContext *dc) +{ + unsigned int insn_len = 2, size = preg_sizes_v10[dc->dst]; + TCGv addr, t0; + + LOG_DIS("move.%d $p%d, [$r%d]\n", dc->size, dc->dst, dc->src); + + addr = tcg_temp_new(); + crisv10_prepare_memaddr(dc, addr, size); + if (dc->dst == PR_CCS) { + t0 = tcg_temp_new(); + cris_evaluate_flags(dc); + tcg_gen_andi_tl(t0, cpu_PR[PR_CCS], ~PFIX_FLAG); + gen_store(dc, addr, t0, size); + tcg_temp_free(t0); + } else { + gen_store(dc, addr, cpu_PR[dc->dst], size); + } + t0 = tcg_temp_new(); + insn_len += crisv10_post_memaddr(dc, size); + cris_lock_irq(dc); + + return insn_len; +} + +static void dec10_movem_r_m(DisasContext *dc) +{ + int i, pfix = dc->tb_flags & PFIX_FLAG; + TCGv addr, t0; + + LOG_DIS("%s r%d, [r%d] pi=%d ir=%x\n", __func__, + dc->dst, dc->src, dc->postinc, dc->ir); + + addr = tcg_temp_new(); + t0 = tcg_temp_new(); + crisv10_prepare_memaddr(dc, addr, 4); + tcg_gen_mov_tl(t0, addr); + for (i = dc->dst; i >= 0; i--) { + if ((pfix && dc->mode == CRISV10_MODE_AUTOINC) && dc->src == i) { + gen_store(dc, addr, t0, 4); + } else { + gen_store(dc, addr, cpu_R[i], 4); + } + tcg_gen_addi_tl(addr, addr, 4); + } + + if (pfix && dc->mode == CRISV10_MODE_AUTOINC) { + tcg_gen_mov_tl(cpu_R[dc->src], t0); + } + + if (!pfix && dc->mode == CRISV10_MODE_AUTOINC) { + tcg_gen_mov_tl(cpu_R[dc->src], addr); + } + tcg_temp_free(addr); + tcg_temp_free(t0); +} + +static void dec10_movem_m_r(DisasContext *dc) +{ + int i, pfix = dc->tb_flags & PFIX_FLAG; + TCGv addr, t0; + + LOG_DIS("%s [r%d], r%d pi=%d ir=%x\n", __func__, + dc->src, dc->dst, dc->postinc, dc->ir); + + addr = tcg_temp_new(); + t0 = tcg_temp_new(); + crisv10_prepare_memaddr(dc, addr, 4); + tcg_gen_mov_tl(t0, addr); + for (i = dc->dst; i >= 0; i--) { + gen_load(dc, cpu_R[i], addr, 4, 0); + tcg_gen_addi_tl(addr, addr, 4); + } + + if (pfix && dc->mode == CRISV10_MODE_AUTOINC) { + tcg_gen_mov_tl(cpu_R[dc->src], t0); + } + + if (!pfix && dc->mode == CRISV10_MODE_AUTOINC) { + tcg_gen_mov_tl(cpu_R[dc->src], addr); + } + tcg_temp_free(addr); + tcg_temp_free(t0); +} + +static int dec10_ind_alu(DisasContext *dc, int op, unsigned int size) +{ + int insn_len = 0; + int rd = dc->dst; + TCGv t[2]; + + cris_alu_m_alloc_temps(t); + insn_len += dec10_prep_move_m(dc, 0, size, t[0]); + cris_alu(dc, op, cpu_R[dc->dst], cpu_R[rd], t[0], size); + if (dc->dst == 15) { + tcg_gen_mov_tl(env_btarget, cpu_R[dc->dst]); + cris_prepare_jmp(dc, JMP_INDIRECT); + dc->delayed_branch = 1; + return insn_len; + } + + cris_alu_m_free_temps(t); + + return insn_len; +} + +static int dec10_ind_bound(DisasContext *dc, unsigned int size) +{ + int insn_len = 0; + int rd = dc->dst; + TCGv t; + + t = tcg_temp_local_new(); + insn_len += dec10_prep_move_m(dc, 0, size, t); + cris_alu(dc, CC_OP_BOUND, cpu_R[dc->dst], cpu_R[rd], t, 4); + if (dc->dst == 15) { + tcg_gen_mov_tl(env_btarget, cpu_R[dc->dst]); + cris_prepare_jmp(dc, JMP_INDIRECT); + dc->delayed_branch = 1; + return insn_len; + } + + tcg_temp_free(t); + return insn_len; +} + +static int dec10_alux_m(DisasContext *dc, int op) +{ + unsigned int size = (dc->size & 1) ? 2 : 1; + unsigned int sx = !!(dc->size & 2); + int insn_len = 2; + int rd = dc->dst; + TCGv t; + + LOG_DIS("addx size=%d sx=%d op=%d %d\n", size, sx, dc->src, dc->dst); + + t = tcg_temp_new(); + + cris_cc_mask(dc, CC_MASK_NZVC); + insn_len += dec10_prep_move_m(dc, sx, size, t); + cris_alu(dc, op, cpu_R[dc->dst], cpu_R[rd], t, 4); + if (dc->dst == 15) { + tcg_gen_mov_tl(env_btarget, cpu_R[dc->dst]); + cris_prepare_jmp(dc, JMP_INDIRECT); + dc->delayed_branch = 1; + return insn_len; + } + + tcg_temp_free(t); + return insn_len; +} + +static int dec10_dip(DisasContext *dc) +{ + int insn_len = 2; + uint32_t imm; + + LOG_DIS("dip pc=%x opcode=%d r%d r%d\n", + dc->pc, dc->opcode, dc->src, dc->dst); + if (dc->src == 15) { + imm = ldl_code(dc->pc + 2); + tcg_gen_movi_tl(cpu_PR[PR_PREFIX], imm); + if (dc->postinc) + insn_len += 4; + tcg_gen_addi_tl(cpu_R[15], cpu_R[15], insn_len - 2); + } else { + gen_load(dc, cpu_PR[PR_PREFIX], cpu_R[dc->src], 4, 0); + if (dc->postinc) + tcg_gen_addi_tl(cpu_R[dc->src], cpu_R[dc->src], 4); + } + + cris_set_prefix(dc); + return insn_len; +} + +static int dec10_bdap_m(DisasContext *dc, int size) +{ + int insn_len = 2; + int rd = dc->dst; + + LOG_DIS("bdap_m pc=%x opcode=%d r%d r%d sz=%d\n", + dc->pc, dc->opcode, dc->src, dc->dst, size); + + assert(dc->dst != 15); +#if 0 + /* 8bit embedded offset? */ + if (!dc->postinc && (dc->ir & (1 << 11))) { + int simm = dc->ir & 0xff; + + /* cpu_abort(dc->env, "Unhandled opcode"); */ + /* sign extended. */ + simm = (int8_t)simm; + + tcg_gen_addi_tl(cpu_PR[PR_PREFIX], cpu_R[dc->dst], simm); + + cris_set_prefix(dc); + return insn_len; + } +#endif + /* Now the rest of the modes are truely indirect. */ + insn_len += dec10_prep_move_m(dc, 1, size, cpu_PR[PR_PREFIX]); + tcg_gen_add_tl(cpu_PR[PR_PREFIX], cpu_PR[PR_PREFIX], cpu_R[rd]); + cris_set_prefix(dc); + return insn_len; +} + +static unsigned int dec10_ind(DisasContext *dc) +{ + unsigned int insn_len = 2; + unsigned int size = dec10_size(dc->size); + uint32_t imm; + int32_t simm; + TCGv t[2]; + + if (dc->size != 3) { + switch (dc->opcode) { + case CRISV10_IND_MOVE_M_R: + return dec10_ind_move_m_r(dc, size); + break; + case CRISV10_IND_MOVE_R_M: + return dec10_ind_move_r_m(dc, size); + break; + case CRISV10_IND_CMP: + LOG_DIS("cmp size=%d op=%d %d\n", size, dc->src, dc->dst); + cris_cc_mask(dc, CC_MASK_NZVC); + insn_len += dec10_ind_alu(dc, CC_OP_CMP, size); + break; + case CRISV10_IND_TEST: + LOG_DIS("test size=%d op=%d %d\n", size, dc->src, dc->dst); + + cris_evaluate_flags(dc); + cris_cc_mask(dc, CC_MASK_NZVC); + cris_alu_m_alloc_temps(t); + insn_len += dec10_prep_move_m(dc, 0, size, t[0]); + tcg_gen_andi_tl(cpu_PR[PR_CCS], cpu_PR[PR_CCS], ~3); + cris_alu(dc, CC_OP_CMP, cpu_R[dc->dst], + t[0], tcg_const_tl(0), size); + cris_alu_m_free_temps(t); + break; + case CRISV10_IND_ADD: + LOG_DIS("add size=%d op=%d %d\n", size, dc->src, dc->dst); + cris_cc_mask(dc, CC_MASK_NZVC); + insn_len += dec10_ind_alu(dc, CC_OP_ADD, size); + break; + case CRISV10_IND_SUB: + LOG_DIS("sub size=%d op=%d %d\n", size, dc->src, dc->dst); + cris_cc_mask(dc, CC_MASK_NZVC); + insn_len += dec10_ind_alu(dc, CC_OP_SUB, size); + break; + case CRISV10_IND_BOUND: + LOG_DIS("bound size=%d op=%d %d\n", size, dc->src, dc->dst); + cris_cc_mask(dc, CC_MASK_NZVC); + insn_len += dec10_ind_bound(dc, size); + break; + case CRISV10_IND_AND: + LOG_DIS("and size=%d op=%d %d\n", size, dc->src, dc->dst); + cris_cc_mask(dc, CC_MASK_NZVC); + insn_len += dec10_ind_alu(dc, CC_OP_AND, size); + break; + case CRISV10_IND_OR: + LOG_DIS("or size=%d op=%d %d\n", size, dc->src, dc->dst); + cris_cc_mask(dc, CC_MASK_NZVC); + insn_len += dec10_ind_alu(dc, CC_OP_OR, size); + break; + case CRISV10_IND_MOVX: + insn_len = dec10_alux_m(dc, CC_OP_MOVE); + break; + case CRISV10_IND_ADDX: + insn_len = dec10_alux_m(dc, CC_OP_ADD); + break; + case CRISV10_IND_SUBX: + insn_len = dec10_alux_m(dc, CC_OP_SUB); + break; + case CRISV10_IND_CMPX: + insn_len = dec10_alux_m(dc, CC_OP_CMP); + break; + case CRISV10_IND_MUL: + /* This is a reg insn coded in the mem indir space. */ + LOG_DIS("mul pc=%x opcode=%d\n", dc->pc, dc->opcode); + cris_cc_mask(dc, CC_MASK_NZVC); + dec10_reg_mul(dc, size, dc->ir & (1 << 10)); + break; + case CRISV10_IND_BDAP_M: + insn_len = dec10_bdap_m(dc, size); + break; + default: + LOG_DIS("pc=%x var-ind.%d %d r%d r%d\n", + dc->pc, size, dc->opcode, dc->src, dc->dst); + cpu_abort(dc->env, "Unhandled opcode"); + break; + } + return insn_len; + } + + switch (dc->opcode) { + case CRISV10_IND_MOVE_M_SPR: + insn_len = dec10_ind_move_m_pr(dc); + break; + case CRISV10_IND_MOVE_SPR_M: + insn_len = dec10_ind_move_pr_m(dc); + break; + case CRISV10_IND_JUMP_M: + if (dc->src == 15) { + LOG_DIS("jump.%d %d r%d r%d direct\n", size, + dc->opcode, dc->src, dc->dst); + imm = ldl_code(dc->pc + 2); + if (dc->mode == CRISV10_MODE_AUTOINC) + insn_len += size; + + t_gen_mov_preg_TN(dc, dc->dst, tcg_const_tl(dc->pc + insn_len)); + dc->jmp_pc = imm; + cris_prepare_jmp(dc, JMP_DIRECT); + dc->delayed_branch--; /* v10 has no dslot here. */ + } else { + if (dc->dst == 14) { + LOG_DIS("break %d\n", dc->src); + cris_evaluate_flags(dc); + tcg_gen_movi_tl(env_pc, dc->pc + 2); + t_gen_raise_exception(EXCP_BREAK); + dc->is_jmp = DISAS_UPDATE; + return insn_len; + } + LOG_DIS("%d: jump.%d %d r%d r%d\n", __LINE__, size, + dc->opcode, dc->src, dc->dst); + t[0] = tcg_temp_new(); + t_gen_mov_preg_TN(dc, dc->dst, tcg_const_tl(dc->pc + insn_len)); + crisv10_prepare_memaddr(dc, t[0], size); + gen_load(dc, env_btarget, t[0], 4, 0); + insn_len += crisv10_post_memaddr(dc, size); + cris_prepare_jmp(dc, JMP_INDIRECT); + dc->delayed_branch--; /* v10 has no dslot here. */ + tcg_temp_free(t[0]); + } + break; + + case CRISV10_IND_MOVEM_R_M: + LOG_DIS("movem_r_m pc=%x opcode=%d r%d r%d\n", + dc->pc, dc->opcode, dc->dst, dc->src); + dec10_movem_r_m(dc); + break; + case CRISV10_IND_MOVEM_M_R: + LOG_DIS("movem_m_r pc=%x opcode=%d\n", dc->pc, dc->opcode); + dec10_movem_m_r(dc); + break; + case CRISV10_IND_JUMP_R: + LOG_DIS("jmp pc=%x opcode=%d r%d r%d\n", + dc->pc, dc->opcode, dc->dst, dc->src); + tcg_gen_mov_tl(env_btarget, cpu_R[dc->src]); + t_gen_mov_preg_TN(dc, dc->dst, tcg_const_tl(dc->pc + insn_len)); + cris_prepare_jmp(dc, JMP_INDIRECT); + dc->delayed_branch--; /* v10 has no dslot here. */ + break; + case CRISV10_IND_MOVX: + insn_len = dec10_alux_m(dc, CC_OP_MOVE); + break; + case CRISV10_IND_ADDX: + insn_len = dec10_alux_m(dc, CC_OP_ADD); + break; + case CRISV10_IND_SUBX: + insn_len = dec10_alux_m(dc, CC_OP_SUB); + break; + case CRISV10_IND_CMPX: + insn_len = dec10_alux_m(dc, CC_OP_CMP); + break; + case CRISV10_IND_DIP: + insn_len = dec10_dip(dc); + break; + case CRISV10_IND_BCC_M: + + cris_cc_mask(dc, 0); + imm = ldsw_code(dc->pc + 2); + simm = (int16_t)imm; + simm += 4; + + LOG_DIS("bcc_m: b%s %x\n", cc_name(dc->cond), dc->pc + simm); + cris_prepare_cc_branch(dc, simm, dc->cond); + insn_len = 4; + break; + default: + LOG_DIS("ERROR pc=%x opcode=%d\n", dc->pc, dc->opcode); + cpu_abort(dc->env, "Unhandled opcode"); + break; + } + + return insn_len; +} + +static unsigned int crisv10_decoder(DisasContext *dc) +{ + unsigned int insn_len = 2; + + if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP))) + tcg_gen_debug_insn_start(dc->pc); + + /* Load a halfword onto the instruction register. */ + dc->ir = lduw_code(dc->pc); + + /* Now decode it. */ + dc->opcode = EXTRACT_FIELD(dc->ir, 6, 9); + dc->mode = EXTRACT_FIELD(dc->ir, 10, 11); + dc->src = EXTRACT_FIELD(dc->ir, 0, 3); + dc->size = EXTRACT_FIELD(dc->ir, 4, 5); + dc->cond = dc->dst = EXTRACT_FIELD(dc->ir, 12, 15); + dc->postinc = EXTRACT_FIELD(dc->ir, 10, 10); + + dc->clear_prefix = 1; + + /* FIXME: What if this insn insn't 2 in length?? */ + if (dc->src == 15 || dc->dst == 15) + tcg_gen_movi_tl(cpu_R[15], dc->pc + 2); + + switch (dc->mode) { + case CRISV10_MODE_QIMMEDIATE: + insn_len = dec10_quick_imm(dc); + break; + case CRISV10_MODE_REG: + insn_len = dec10_reg(dc); + break; + case CRISV10_MODE_AUTOINC: + case CRISV10_MODE_INDIRECT: + insn_len = dec10_ind(dc); + break; + } + + if (dc->clear_prefix && dc->tb_flags & PFIX_FLAG) { + dc->tb_flags &= ~PFIX_FLAG; + tcg_gen_andi_tl(cpu_PR[PR_CCS], cpu_PR[PR_CCS], ~PFIX_FLAG); + if (dc->tb_flags != dc->tb->flags) { + dc->cpustate_changed = 1; + } + } + + /* CRISv10 locks out interrupts on dslots. */ + if (dc->delayed_branch == 2) { + cris_lock_irq(dc); + } + return insn_len; +} + +static CPUCRISState *cpu_crisv10_init (CPUState *env) +{ + int i; + + cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env"); + cc_x = tcg_global_mem_new(TCG_AREG0, + offsetof(CPUState, cc_x), "cc_x"); + cc_src = tcg_global_mem_new(TCG_AREG0, + offsetof(CPUState, cc_src), "cc_src"); + cc_dest = tcg_global_mem_new(TCG_AREG0, + offsetof(CPUState, cc_dest), + "cc_dest"); + cc_result = tcg_global_mem_new(TCG_AREG0, + offsetof(CPUState, cc_result), + "cc_result"); + cc_op = tcg_global_mem_new(TCG_AREG0, + offsetof(CPUState, cc_op), "cc_op"); + cc_size = tcg_global_mem_new(TCG_AREG0, + offsetof(CPUState, cc_size), + "cc_size"); + cc_mask = tcg_global_mem_new(TCG_AREG0, + offsetof(CPUState, cc_mask), + "cc_mask"); + + env_pc = tcg_global_mem_new(TCG_AREG0, + offsetof(CPUState, pc), + "pc"); + env_btarget = tcg_global_mem_new(TCG_AREG0, + offsetof(CPUState, btarget), + "btarget"); + env_btaken = tcg_global_mem_new(TCG_AREG0, + offsetof(CPUState, btaken), + "btaken"); + for (i = 0; i < 16; i++) { + cpu_R[i] = tcg_global_mem_new(TCG_AREG0, + offsetof(CPUState, regs[i]), + regnames_v10[i]); + } + for (i = 0; i < 16; i++) { + cpu_PR[i] = tcg_global_mem_new(TCG_AREG0, + offsetof(CPUState, pregs[i]), + pregnames_v10[i]); + } + + return env; +} + diff -Nru qemu-kvm-0.12.5+noroms/target-i386/cpu.h qemu-kvm-0.14.1/target-i386/cpu.h --- qemu-kvm-0.12.5+noroms/target-i386/cpu.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-i386/cpu.h 2011-05-11 13:29:46.000000000 +0000 @@ -20,6 +20,7 @@ #define CPU_I386_H #include "config.h" +#include "qemu-common.h" #ifdef TARGET_X86_64 #define TARGET_LONG_BITS 64 @@ -421,6 +422,17 @@ #define CPUID_EXT3_IBS (1 << 10) #define CPUID_EXT3_SKINIT (1 << 12) +#define CPUID_SVM_NPT (1 << 0) +#define CPUID_SVM_LBRV (1 << 1) +#define CPUID_SVM_SVMLOCK (1 << 2) +#define CPUID_SVM_NRIPSAVE (1 << 3) +#define CPUID_SVM_TSCSCALE (1 << 4) +#define CPUID_SVM_VMCBCLEAN (1 << 5) +#define CPUID_SVM_FLUSHASID (1 << 6) +#define CPUID_SVM_DECODEASSIST (1 << 7) +#define CPUID_SVM_PAUSEFILTER (1 << 10) +#define CPUID_SVM_PFTHRESHOLD (1 << 12) + #define CPUID_VENDOR_INTEL_1 0x756e6547 /* "Genu" */ #define CPUID_VENDOR_INTEL_2 0x49656e69 /* "ineI" */ #define CPUID_VENDOR_INTEL_3 0x6c65746e /* "ntel" */ @@ -669,6 +681,7 @@ #endif uint64_t system_time_msr; uint64_t wall_clock_msr; + uint64_t async_pf_en_msr; uint64_t tsc; @@ -686,6 +699,10 @@ uint32_t smbase; int old_exception; /* exception in flight */ + /* KVM states, automatically cleared on reset */ + uint8_t nmi_injected; + uint8_t nmi_pending; + CPU_COMMON /* processor features (e.g. for CPUID insn) */ @@ -713,19 +730,19 @@ int32_t exception_injected; int32_t interrupt_injected; uint8_t soft_interrupt; - uint8_t nmi_injected; - uint8_t nmi_pending; uint8_t has_error_code; uint32_t sipi_vector; - + uint32_t cpuid_kvm_features; + uint32_t cpuid_svm_features; + /* in order to simplify APIC support, we leave this pointer to the user */ - struct APICState *apic_state; + struct DeviceState *apic_state; - uint64 mcg_cap; - uint64 mcg_status; - uint64 mcg_ctl; - uint64 mce_banks[MCE_BANKS_DEF*4]; + uint64_t mcg_cap; + uint64_t mcg_status; + uint64_t mcg_ctl; + uint64_t mce_banks[MCE_BANKS_DEF*4]; uint64_t tsc_aux; @@ -734,14 +751,21 @@ uint16_t fptag_vmstate; uint16_t fpregs_format_vmstate; - int update_vapic; + int kvm_vcpu_update_vapic; + + uint64_t xstate_bv; + XMMReg ymmh_regs[CPU_NB_REGS]; + + uint64_t xcr0; } CPUX86State; CPUX86State *cpu_x86_init(const char *cpu_model); int cpu_x86_exec(CPUX86State *s); void cpu_x86_close(CPUX86State *s); -void x86_cpu_list (FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, - ...)); +void x86_cpu_list (FILE *f, fprintf_function cpu_fprintf, const char *optarg); +void x86_cpudef_setup(void); +int cpu_x86_support_mca_broadcast(CPUState *env); + int cpu_get_pic_interrupt(CPUX86State *s); /* MSDOS compatibility mode FPU exception support */ void cpu_set_ferr(CPUX86State *s); @@ -805,6 +829,17 @@ } } +static inline void cpu_x86_load_seg_cache_sipi(CPUX86State *env, + int sipi_vector) +{ + env->eip = 0; + cpu_x86_load_seg_cache(env, R_CS, sipi_vector << 8, + sipi_vector << 12, + env->segs[R_CS].limit, + env->segs[R_CS].flags); + env->halted = 0; +} + int cpu_x86_get_descr_debug(CPUX86State *env, unsigned int selector, target_ulong *base, unsigned int *limit, unsigned int *flags); @@ -837,14 +872,20 @@ int cpu_x86_signal_handler(int host_signum, void *pinfo, void *puc); +/* cpuid.c */ +void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, + uint32_t *eax, uint32_t *ebx, + uint32_t *ecx, uint32_t *edx); +int cpu_x86_register (CPUX86State *env, const char *cpu_model); +void cpu_clear_apic_feature(CPUX86State *env); +void host_cpuid(uint32_t function, uint32_t count, + uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx); + /* helper.c */ int cpu_x86_handle_mmu_fault(CPUX86State *env, target_ulong addr, int is_write, int mmu_idx, int is_softmmu); #define cpu_handle_mmu_fault cpu_x86_handle_mmu_fault void cpu_x86_set_a20(CPUX86State *env, int a20_state); -void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, - uint32_t *eax, uint32_t *ebx, - uint32_t *ecx, uint32_t *edx); static inline int hw_breakpoint_enabled(unsigned long dr7, int index) { @@ -871,17 +912,10 @@ void cpu_x86_update_cr3(CPUX86State *env, target_ulong new_cr3); void cpu_x86_update_cr4(CPUX86State *env, uint32_t new_cr4); -/* hw/apic.c */ -void cpu_set_apic_base(CPUX86State *env, uint64_t val); -uint64_t cpu_get_apic_base(CPUX86State *env); -void cpu_set_apic_tpr(CPUX86State *env, uint8_t val); -#ifndef NO_CPU_IO_DEFS -uint8_t cpu_get_apic_tpr(CPUX86State *env); -#endif - /* hw/pc.c */ void cpu_smm_update(CPUX86State *env); uint64_t cpu_get_tsc(CPUX86State *env); +CPUState *pc_new_cpu(const char *cpu_model); /* used to debug */ #define X86_DUMP_FPU 0x0001 /* dump FPU state too */ @@ -889,11 +923,23 @@ #define TARGET_PAGE_BITS 12 +#ifdef TARGET_X86_64 +#define TARGET_PHYS_ADDR_SPACE_BITS 52 +/* ??? This is really 48 bits, sign-extended, but the only thing + accessible to userland with bit 48 set is the VSYSCALL, and that + is handled via other mechanisms. */ +#define TARGET_VIRT_ADDR_SPACE_BITS 47 +#else +#define TARGET_PHYS_ADDR_SPACE_BITS 36 +#define TARGET_VIRT_ADDR_SPACE_BITS 32 +#endif + #define cpu_init cpu_x86_init #define cpu_exec cpu_x86_exec #define cpu_gen_code cpu_x86_gen_code #define cpu_signal_handler cpu_x86_signal_handler -#define cpu_list x86_cpu_list +#define cpu_list_id x86_cpu_list +#define cpudef_setup x86_cpudef_setup #define CPU_SAVE_VERSION 12 @@ -924,14 +970,11 @@ #endif #include "cpu-all.h" -#include "exec-all.h" - #include "svm.h" -static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb) -{ - env->eip = tb->pc - tb->cs_base; -} +#if !defined(CONFIG_USER_ONLY) +#include "hw/apic.h" +#endif static inline void cpu_get_tb_cpu_state(CPUState *env, target_ulong *pc, target_ulong *cs_base, int *flags) @@ -942,8 +985,6 @@ (env->eflags & (IOPL_MASK | TF_MASK | RF_MASK | VM_MASK)); } -void apic_init_reset(CPUState *env); -void apic_sipi(CPUState *env); void do_cpu_init(CPUState *env); void do_cpu_sipi(CPUState *env); #endif /* CPU_I386_H */ diff -Nru qemu-kvm-0.12.5+noroms/target-i386/cpuid.c qemu-kvm-0.14.1/target-i386/cpuid.c --- qemu-kvm-0.12.5+noroms/target-i386/cpuid.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/target-i386/cpuid.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,1266 @@ +/* + * i386 CPUID helper functions + * + * Copyright (c) 2003 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ +#include +#include +#include +#include + +#include "cpu.h" +#include "kvm.h" + +#include "qemu-option.h" +#include "qemu-config.h" + +/* feature flags taken from "Intel Processor Identification and the CPUID + * Instruction" and AMD's "CPUID Specification". In cases of disagreement + * between feature naming conventions, aliases may be added. + */ +static const char *feature_name[] = { + "fpu", "vme", "de", "pse", + "tsc", "msr", "pae", "mce", + "cx8", "apic", NULL, "sep", + "mtrr", "pge", "mca", "cmov", + "pat", "pse36", "pn" /* Intel psn */, "clflush" /* Intel clfsh */, + NULL, "ds" /* Intel dts */, "acpi", "mmx", + "fxsr", "sse", "sse2", "ss", + "ht" /* Intel htt */, "tm", "ia64", "pbe", +}; +static const char *ext_feature_name[] = { + "pni|sse3" /* Intel,AMD sse3 */, "pclmuldq", "dtes64", "monitor", + "ds_cpl", "vmx", "smx", "est", + "tm2", "ssse3", "cid", NULL, + "fma", "cx16", "xtpr", "pdcm", + NULL, NULL, "dca", "sse4.1|sse4_1", + "sse4.2|sse4_2", "x2apic", "movbe", "popcnt", + NULL, "aes", "xsave", "osxsave", + "avx", NULL, NULL, "hypervisor", +}; +static const char *ext2_feature_name[] = { + "fpu", "vme", "de", "pse", + "tsc", "msr", "pae", "mce", + "cx8" /* AMD CMPXCHG8B */, "apic", NULL, "syscall", + "mtrr", "pge", "mca", "cmov", + "pat", "pse36", NULL, NULL /* Linux mp */, + "nx" /* Intel xd */, NULL, "mmxext", "mmx", + "fxsr", "fxsr_opt" /* AMD ffxsr */, "pdpe1gb" /* AMD Page1GB */, "rdtscp", + NULL, "lm" /* Intel 64 */, "3dnowext", "3dnow", +}; +static const char *ext3_feature_name[] = { + "lahf_lm" /* AMD LahfSahf */, "cmp_legacy", "svm", "extapic" /* AMD ExtApicSpace */, + "cr8legacy" /* AMD AltMovCr8 */, "abm", "sse4a", "misalignsse", + "3dnowprefetch", "osvw", "ibs", "xop", + "skinit", "wdt", NULL, NULL, + "fma4", NULL, "cvt16", "nodeid_msr", + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, +}; + +static const char *kvm_feature_name[] = { + "kvmclock", "kvm_nopiodelay", "kvm_mmu", NULL, "kvm_asyncpf", NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +}; + +static const char *svm_feature_name[] = { + "npt", "lbrv", "svm_lock", "nrip_save", + "tsc_scale", "vmcb_clean", "flushbyasid", "decodeassists", + NULL, NULL, "pause_filter", NULL, + "pfthreshold", NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, +}; + +/* collects per-function cpuid data + */ +typedef struct model_features_t { + uint32_t *guest_feat; + uint32_t *host_feat; + uint32_t check_feat; + const char **flag_names; + uint32_t cpuid; + } model_features_t; + +int check_cpuid = 0; +int enforce_cpuid = 0; + +void host_cpuid(uint32_t function, uint32_t count, + uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx) +{ +#if defined(CONFIG_KVM) + uint32_t vec[4]; + +#ifdef __x86_64__ + asm volatile("cpuid" + : "=a"(vec[0]), "=b"(vec[1]), + "=c"(vec[2]), "=d"(vec[3]) + : "0"(function), "c"(count) : "cc"); +#else + asm volatile("pusha \n\t" + "cpuid \n\t" + "mov %%eax, 0(%2) \n\t" + "mov %%ebx, 4(%2) \n\t" + "mov %%ecx, 8(%2) \n\t" + "mov %%edx, 12(%2) \n\t" + "popa" + : : "a"(function), "c"(count), "S"(vec) + : "memory", "cc"); +#endif + + if (eax) + *eax = vec[0]; + if (ebx) + *ebx = vec[1]; + if (ecx) + *ecx = vec[2]; + if (edx) + *edx = vec[3]; +#endif +} + +#define iswhite(c) ((c) && ((c) <= ' ' || '~' < (c))) + +/* general substring compare of *[s1..e1) and *[s2..e2). sx is start of + * a substring. ex if !NULL points to the first char after a substring, + * otherwise the string is assumed to sized by a terminating nul. + * Return lexical ordering of *s1:*s2. + */ +static int sstrcmp(const char *s1, const char *e1, const char *s2, + const char *e2) +{ + for (;;) { + if (!*s1 || !*s2 || *s1 != *s2) + return (*s1 - *s2); + ++s1, ++s2; + if (s1 == e1 && s2 == e2) + return (0); + else if (s1 == e1) + return (*s2); + else if (s2 == e2) + return (*s1); + } +} + +/* compare *[s..e) to *altstr. *altstr may be a simple string or multiple + * '|' delimited (possibly empty) strings in which case search for a match + * within the alternatives proceeds left to right. Return 0 for success, + * non-zero otherwise. + */ +static int altcmp(const char *s, const char *e, const char *altstr) +{ + const char *p, *q; + + for (q = p = altstr; ; ) { + while (*p && *p != '|') + ++p; + if ((q == p && !*s) || (q != p && !sstrcmp(s, e, q, p))) + return (0); + if (!*p) + return (1); + else + q = ++p; + } +} + +/* search featureset for flag *[s..e), if found set corresponding bit in + * *pval and return success, otherwise return zero + */ +static int lookup_feature(uint32_t *pval, const char *s, const char *e, + const char **featureset) +{ + uint32_t mask; + const char **ppc; + + for (mask = 1, ppc = featureset; mask; mask <<= 1, ++ppc) + if (*ppc && !altcmp(s, e, *ppc)) { + *pval |= mask; + break; + } + return (mask ? 1 : 0); +} + +static void add_flagname_to_bitmaps(const char *flagname, uint32_t *features, + uint32_t *ext_features, + uint32_t *ext2_features, + uint32_t *ext3_features, + uint32_t *kvm_features, + uint32_t *svm_features) +{ + if (!lookup_feature(features, flagname, NULL, feature_name) && + !lookup_feature(ext_features, flagname, NULL, ext_feature_name) && + !lookup_feature(ext2_features, flagname, NULL, ext2_feature_name) && + !lookup_feature(ext3_features, flagname, NULL, ext3_feature_name) && + !lookup_feature(kvm_features, flagname, NULL, kvm_feature_name) && + !lookup_feature(svm_features, flagname, NULL, svm_feature_name)) + fprintf(stderr, "CPU feature %s not found\n", flagname); +} + +typedef struct x86_def_t { + struct x86_def_t *next; + const char *name; + uint32_t level; + uint32_t vendor1, vendor2, vendor3; + int family; + int model; + int stepping; + uint32_t features, ext_features, ext2_features, ext3_features; + uint32_t kvm_features, svm_features; + uint32_t xlevel; + char model_id[48]; + int vendor_override; + uint32_t flags; +} x86_def_t; + +#define I486_FEATURES (CPUID_FP87 | CPUID_VME | CPUID_PSE) +#define PENTIUM_FEATURES (I486_FEATURES | CPUID_DE | CPUID_TSC | \ + CPUID_MSR | CPUID_MCE | CPUID_CX8 | CPUID_MMX | CPUID_APIC) +#define PENTIUM2_FEATURES (PENTIUM_FEATURES | CPUID_PAE | CPUID_SEP | \ + CPUID_MTRR | CPUID_PGE | CPUID_MCA | CPUID_CMOV | CPUID_PAT | \ + CPUID_PSE36 | CPUID_FXSR) +#define PENTIUM3_FEATURES (PENTIUM2_FEATURES | CPUID_SSE) +#define PPRO_FEATURES (CPUID_FP87 | CPUID_DE | CPUID_PSE | CPUID_TSC | \ + CPUID_MSR | CPUID_MCE | CPUID_CX8 | CPUID_PGE | CPUID_CMOV | \ + CPUID_PAT | CPUID_FXSR | CPUID_MMX | CPUID_SSE | CPUID_SSE2 | \ + CPUID_PAE | CPUID_SEP | CPUID_APIC) +#define EXT2_FEATURE_MASK 0x0183F3FF + +#define TCG_FEATURES (CPUID_FP87 | CPUID_PSE | CPUID_TSC | CPUID_MSR | \ + CPUID_PAE | CPUID_MCE | CPUID_CX8 | CPUID_APIC | CPUID_SEP | \ + CPUID_MTRR | CPUID_PGE | CPUID_MCA | CPUID_CMOV | CPUID_PAT | \ + CPUID_PSE36 | CPUID_CLFLUSH | CPUID_ACPI | CPUID_MMX | \ + CPUID_FXSR | CPUID_SSE | CPUID_SSE2 | CPUID_SS) + /* partly implemented: + CPUID_MTRR, CPUID_MCA, CPUID_CLFLUSH (needed for Win64) + CPUID_PSE36 (needed for Solaris) */ + /* missing: + CPUID_VME, CPUID_DTS, CPUID_SS, CPUID_HT, CPUID_TM, CPUID_PBE */ +#define TCG_EXT_FEATURES (CPUID_EXT_SSE3 | CPUID_EXT_MONITOR | \ + CPUID_EXT_CX16 | CPUID_EXT_POPCNT | \ + CPUID_EXT_HYPERVISOR) + /* missing: + CPUID_EXT_DTES64, CPUID_EXT_DSCPL, CPUID_EXT_VMX, CPUID_EXT_EST, + CPUID_EXT_TM2, CPUID_EXT_XTPR, CPUID_EXT_PDCM, CPUID_EXT_XSAVE */ +#define TCG_EXT2_FEATURES ((TCG_FEATURES & EXT2_FEATURE_MASK) | \ + CPUID_EXT2_NX | CPUID_EXT2_MMXEXT | CPUID_EXT2_RDTSCP | \ + CPUID_EXT2_3DNOW | CPUID_EXT2_3DNOWEXT) + /* missing: + CPUID_EXT2_PDPE1GB */ +#define TCG_EXT3_FEATURES (CPUID_EXT3_LAHF_LM | CPUID_EXT3_SVM | \ + CPUID_EXT3_CR8LEG | CPUID_EXT3_ABM | CPUID_EXT3_SSE4A) +#define TCG_SVM_FEATURES 0 + +/* maintains list of cpu model definitions + */ +static x86_def_t *x86_defs = {NULL}; + +/* built-in cpu model definitions (deprecated) + */ +static x86_def_t builtin_x86_defs[] = { + { + .name = "qemu64", + .level = 4, + .vendor1 = CPUID_VENDOR_AMD_1, + .vendor2 = CPUID_VENDOR_AMD_2, + .vendor3 = CPUID_VENDOR_AMD_3, + .family = 6, + .model = 2, + .stepping = 3, + .features = PPRO_FEATURES | + CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA | + CPUID_PSE36, + .ext_features = CPUID_EXT_SSE3 | CPUID_EXT_CX16 | CPUID_EXT_POPCNT, + .ext2_features = (PPRO_FEATURES & EXT2_FEATURE_MASK) | + CPUID_EXT2_LM | CPUID_EXT2_SYSCALL | CPUID_EXT2_NX, + .ext3_features = CPUID_EXT3_LAHF_LM | CPUID_EXT3_SVM | + CPUID_EXT3_ABM | CPUID_EXT3_SSE4A, + .xlevel = 0x8000000A, + .model_id = "QEMU Virtual CPU version " QEMU_VERSION, + }, + { + .name = "phenom", + .level = 5, + .vendor1 = CPUID_VENDOR_AMD_1, + .vendor2 = CPUID_VENDOR_AMD_2, + .vendor3 = CPUID_VENDOR_AMD_3, + .family = 16, + .model = 2, + .stepping = 3, + .features = PPRO_FEATURES | + CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA | + CPUID_PSE36 | CPUID_VME | CPUID_HT, + .ext_features = CPUID_EXT_SSE3 | CPUID_EXT_MONITOR | CPUID_EXT_CX16 | + CPUID_EXT_POPCNT, + .ext2_features = (PPRO_FEATURES & EXT2_FEATURE_MASK) | + CPUID_EXT2_LM | CPUID_EXT2_SYSCALL | CPUID_EXT2_NX | + CPUID_EXT2_3DNOW | CPUID_EXT2_3DNOWEXT | CPUID_EXT2_MMXEXT | + CPUID_EXT2_FFXSR | CPUID_EXT2_PDPE1GB | CPUID_EXT2_RDTSCP, + /* Missing: CPUID_EXT3_CMP_LEG, CPUID_EXT3_EXTAPIC, + CPUID_EXT3_CR8LEG, + CPUID_EXT3_MISALIGNSSE, CPUID_EXT3_3DNOWPREFETCH, + CPUID_EXT3_OSVW, CPUID_EXT3_IBS */ + .ext3_features = CPUID_EXT3_LAHF_LM | CPUID_EXT3_SVM | + CPUID_EXT3_ABM | CPUID_EXT3_SSE4A, + .svm_features = CPUID_SVM_NPT | CPUID_SVM_LBRV, + .xlevel = 0x8000001A, + .model_id = "AMD Phenom(tm) 9550 Quad-Core Processor" + }, + { + .name = "core2duo", + .level = 10, + .family = 6, + .model = 15, + .stepping = 11, + .features = PPRO_FEATURES | + CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA | + CPUID_PSE36 | CPUID_VME | CPUID_DTS | CPUID_ACPI | CPUID_SS | + CPUID_HT | CPUID_TM | CPUID_PBE, + .ext_features = CPUID_EXT_SSE3 | CPUID_EXT_MONITOR | CPUID_EXT_SSSE3 | + CPUID_EXT_DTES64 | CPUID_EXT_DSCPL | CPUID_EXT_VMX | CPUID_EXT_EST | + CPUID_EXT_TM2 | CPUID_EXT_CX16 | CPUID_EXT_XTPR | CPUID_EXT_PDCM, + .ext2_features = CPUID_EXT2_LM | CPUID_EXT2_SYSCALL | CPUID_EXT2_NX, + .ext3_features = CPUID_EXT3_LAHF_LM, + .xlevel = 0x80000008, + .model_id = "Intel(R) Core(TM)2 Duo CPU T7700 @ 2.40GHz", + }, + { + .name = "kvm64", + .level = 5, + .vendor1 = CPUID_VENDOR_INTEL_1, + .vendor2 = CPUID_VENDOR_INTEL_2, + .vendor3 = CPUID_VENDOR_INTEL_3, + .family = 15, + .model = 6, + .stepping = 1, + /* Missing: CPUID_VME, CPUID_HT */ + .features = PPRO_FEATURES | + CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA | + CPUID_PSE36, + /* Missing: CPUID_EXT_POPCNT, CPUID_EXT_MONITOR */ + .ext_features = CPUID_EXT_SSE3 | CPUID_EXT_CX16, + /* Missing: CPUID_EXT2_PDPE1GB, CPUID_EXT2_RDTSCP */ + .ext2_features = (PPRO_FEATURES & EXT2_FEATURE_MASK) | + CPUID_EXT2_LM | CPUID_EXT2_SYSCALL | CPUID_EXT2_NX, + /* Missing: CPUID_EXT3_LAHF_LM, CPUID_EXT3_CMP_LEG, CPUID_EXT3_EXTAPIC, + CPUID_EXT3_CR8LEG, CPUID_EXT3_ABM, CPUID_EXT3_SSE4A, + CPUID_EXT3_MISALIGNSSE, CPUID_EXT3_3DNOWPREFETCH, + CPUID_EXT3_OSVW, CPUID_EXT3_IBS, CPUID_EXT3_SVM */ + .ext3_features = 0, + .xlevel = 0x80000008, + .model_id = "Common KVM processor" + }, + { + .name = "qemu32", + .level = 4, + .family = 6, + .model = 3, + .stepping = 3, + .features = PPRO_FEATURES, + .ext_features = CPUID_EXT_SSE3 | CPUID_EXT_POPCNT, + .xlevel = 0x80000004, + .model_id = "QEMU Virtual CPU version " QEMU_VERSION, + }, + { + .name = "kvm32", + .level = 5, + .family = 15, + .model = 6, + .stepping = 1, + .features = PPRO_FEATURES | + CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA | CPUID_PSE36, + .ext_features = CPUID_EXT_SSE3, + .ext2_features = PPRO_FEATURES & EXT2_FEATURE_MASK, + .ext3_features = 0, + .xlevel = 0x80000008, + .model_id = "Common 32-bit KVM processor" + }, + { + .name = "coreduo", + .level = 10, + .family = 6, + .model = 14, + .stepping = 8, + .features = PPRO_FEATURES | CPUID_VME | + CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA | CPUID_DTS | CPUID_ACPI | + CPUID_SS | CPUID_HT | CPUID_TM | CPUID_PBE, + .ext_features = CPUID_EXT_SSE3 | CPUID_EXT_MONITOR | CPUID_EXT_VMX | + CPUID_EXT_EST | CPUID_EXT_TM2 | CPUID_EXT_XTPR | CPUID_EXT_PDCM, + .ext2_features = CPUID_EXT2_NX, + .xlevel = 0x80000008, + .model_id = "Genuine Intel(R) CPU T2600 @ 2.16GHz", + }, + { + .name = "486", + .level = 1, + .family = 4, + .model = 0, + .stepping = 0, + .features = I486_FEATURES, + .xlevel = 0, + }, + { + .name = "pentium", + .level = 1, + .family = 5, + .model = 4, + .stepping = 3, + .features = PENTIUM_FEATURES, + .xlevel = 0, + }, + { + .name = "pentium2", + .level = 2, + .family = 6, + .model = 5, + .stepping = 2, + .features = PENTIUM2_FEATURES, + .xlevel = 0, + }, + { + .name = "pentium3", + .level = 2, + .family = 6, + .model = 7, + .stepping = 3, + .features = PENTIUM3_FEATURES, + .xlevel = 0, + }, + { + .name = "athlon", + .level = 2, + .vendor1 = CPUID_VENDOR_AMD_1, + .vendor2 = CPUID_VENDOR_AMD_2, + .vendor3 = CPUID_VENDOR_AMD_3, + .family = 6, + .model = 2, + .stepping = 3, + .features = PPRO_FEATURES | CPUID_PSE36 | CPUID_VME | CPUID_MTRR | CPUID_MCA, + .ext2_features = (PPRO_FEATURES & EXT2_FEATURE_MASK) | CPUID_EXT2_MMXEXT | CPUID_EXT2_3DNOW | CPUID_EXT2_3DNOWEXT, + .xlevel = 0x80000008, + /* XXX: put another string ? */ + .model_id = "QEMU Virtual CPU version " QEMU_VERSION, + }, + { + .name = "n270", + /* original is on level 10 */ + .level = 5, + .family = 6, + .model = 28, + .stepping = 2, + .features = PPRO_FEATURES | + CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA | CPUID_VME | CPUID_DTS | + CPUID_ACPI | CPUID_SS | CPUID_HT | CPUID_TM | CPUID_PBE, + /* Some CPUs got no CPUID_SEP */ + .ext_features = CPUID_EXT_SSE3 | CPUID_EXT_MONITOR | CPUID_EXT_SSSE3 | + CPUID_EXT_DSCPL | CPUID_EXT_EST | CPUID_EXT_TM2 | CPUID_EXT_XTPR, + .ext2_features = (PPRO_FEATURES & EXT2_FEATURE_MASK) | CPUID_EXT2_NX, + .ext3_features = CPUID_EXT3_LAHF_LM, + .xlevel = 0x8000000A, + .model_id = "Intel(R) Atom(TM) CPU N270 @ 1.60GHz", + }, +}; + +static int cpu_x86_fill_model_id(char *str) +{ + uint32_t eax = 0, ebx = 0, ecx = 0, edx = 0; + int i; + + for (i = 0; i < 3; i++) { + host_cpuid(0x80000002 + i, 0, &eax, &ebx, &ecx, &edx); + memcpy(str + i * 16 + 0, &eax, 4); + memcpy(str + i * 16 + 4, &ebx, 4); + memcpy(str + i * 16 + 8, &ecx, 4); + memcpy(str + i * 16 + 12, &edx, 4); + } + return 0; +} + +static int cpu_x86_fill_host(x86_def_t *x86_cpu_def) +{ + uint32_t eax = 0, ebx = 0, ecx = 0, edx = 0; + + x86_cpu_def->name = "host"; + host_cpuid(0x0, 0, &eax, &ebx, &ecx, &edx); + x86_cpu_def->level = eax; + x86_cpu_def->vendor1 = ebx; + x86_cpu_def->vendor2 = edx; + x86_cpu_def->vendor3 = ecx; + + host_cpuid(0x1, 0, &eax, &ebx, &ecx, &edx); + x86_cpu_def->family = ((eax >> 8) & 0x0F) + ((eax >> 20) & 0xFF); + x86_cpu_def->model = ((eax >> 4) & 0x0F) | ((eax & 0xF0000) >> 12); + x86_cpu_def->stepping = eax & 0x0F; + x86_cpu_def->ext_features = ecx; + x86_cpu_def->features = edx; + + host_cpuid(0x80000000, 0, &eax, &ebx, &ecx, &edx); + x86_cpu_def->xlevel = eax; + + host_cpuid(0x80000001, 0, &eax, &ebx, &ecx, &edx); + x86_cpu_def->ext2_features = edx; + x86_cpu_def->ext3_features = ecx; + cpu_x86_fill_model_id(x86_cpu_def->model_id); + x86_cpu_def->vendor_override = 0; + + + /* + * Every SVM feature requires emulation support in KVM - so we can't just + * read the host features here. KVM might even support SVM features not + * available on the host hardware. Just set all bits and mask out the + * unsupported ones later. + */ + x86_cpu_def->svm_features = -1; + + return 0; +} + +static int unavailable_host_feature(struct model_features_t *f, uint32_t mask) +{ + int i; + + for (i = 0; i < 32; ++i) + if (1 << i & mask) { + fprintf(stderr, "warning: host cpuid %04x_%04x lacks requested" + " flag '%s' [0x%08x]\n", + f->cpuid >> 16, f->cpuid & 0xffff, + f->flag_names[i] ? f->flag_names[i] : "[reserved]", mask); + break; + } + return 0; +} + +/* best effort attempt to inform user requested cpu flags aren't making + * their way to the guest. Note: ft[].check_feat ideally should be + * specified via a guest_def field to suppress report of extraneous flags. + */ +static int check_features_against_host(x86_def_t *guest_def) +{ + x86_def_t host_def; + uint32_t mask; + int rv, i; + struct model_features_t ft[] = { + {&guest_def->features, &host_def.features, + ~0, feature_name, 0x00000000}, + {&guest_def->ext_features, &host_def.ext_features, + ~CPUID_EXT_HYPERVISOR, ext_feature_name, 0x00000001}, + {&guest_def->ext2_features, &host_def.ext2_features, + ~PPRO_FEATURES, ext2_feature_name, 0x80000000}, + {&guest_def->ext3_features, &host_def.ext3_features, + ~CPUID_EXT3_SVM, ext3_feature_name, 0x80000001}}; + + cpu_x86_fill_host(&host_def); + for (rv = 0, i = 0; i < ARRAY_SIZE(ft); ++i) + for (mask = 1; mask; mask <<= 1) + if (ft[i].check_feat & mask && *ft[i].guest_feat & mask && + !(*ft[i].host_feat & mask)) { + unavailable_host_feature(&ft[i], mask); + rv = 1; + } + return rv; +} + +static int cpu_x86_find_by_name(x86_def_t *x86_cpu_def, const char *cpu_model) +{ + unsigned int i; + x86_def_t *def; + + char *s = strdup(cpu_model); + char *featurestr, *name = strtok(s, ","); + /* Features to be added*/ + uint32_t plus_features = 0, plus_ext_features = 0; + uint32_t plus_ext2_features = 0, plus_ext3_features = 0; + uint32_t plus_kvm_features = 0, plus_svm_features = 0; + /* Features to be removed */ + uint32_t minus_features = 0, minus_ext_features = 0; + uint32_t minus_ext2_features = 0, minus_ext3_features = 0; + uint32_t minus_kvm_features = 0, minus_svm_features = 0; + uint32_t numvalue; + + for (def = x86_defs; def; def = def->next) + if (!strcmp(name, def->name)) + break; + if (kvm_enabled() && strcmp(name, "host") == 0) { + cpu_x86_fill_host(x86_cpu_def); + } else if (!def) { + goto error; + } else { + memcpy(x86_cpu_def, def, sizeof(*def)); + } + + plus_kvm_features = ~0; /* not supported bits will be filtered out later */ + + add_flagname_to_bitmaps("hypervisor", &plus_features, + &plus_ext_features, &plus_ext2_features, &plus_ext3_features, + &plus_kvm_features, &plus_svm_features); + + featurestr = strtok(NULL, ","); + + while (featurestr) { + char *val; + if (featurestr[0] == '+') { + add_flagname_to_bitmaps(featurestr + 1, &plus_features, + &plus_ext_features, &plus_ext2_features, + &plus_ext3_features, &plus_kvm_features, + &plus_svm_features); + } else if (featurestr[0] == '-') { + add_flagname_to_bitmaps(featurestr + 1, &minus_features, + &minus_ext_features, &minus_ext2_features, + &minus_ext3_features, &minus_kvm_features, + &minus_svm_features); + } else if ((val = strchr(featurestr, '='))) { + *val = 0; val++; + if (!strcmp(featurestr, "family")) { + char *err; + numvalue = strtoul(val, &err, 0); + if (!*val || *err) { + fprintf(stderr, "bad numerical value %s\n", val); + goto error; + } + x86_cpu_def->family = numvalue; + } else if (!strcmp(featurestr, "model")) { + char *err; + numvalue = strtoul(val, &err, 0); + if (!*val || *err || numvalue > 0xff) { + fprintf(stderr, "bad numerical value %s\n", val); + goto error; + } + x86_cpu_def->model = numvalue; + } else if (!strcmp(featurestr, "stepping")) { + char *err; + numvalue = strtoul(val, &err, 0); + if (!*val || *err || numvalue > 0xf) { + fprintf(stderr, "bad numerical value %s\n", val); + goto error; + } + x86_cpu_def->stepping = numvalue ; + } else if (!strcmp(featurestr, "level")) { + char *err; + numvalue = strtoul(val, &err, 0); + if (!*val || *err) { + fprintf(stderr, "bad numerical value %s\n", val); + goto error; + } + x86_cpu_def->level = numvalue; + } else if (!strcmp(featurestr, "xlevel")) { + char *err; + numvalue = strtoul(val, &err, 0); + if (!*val || *err) { + fprintf(stderr, "bad numerical value %s\n", val); + goto error; + } + if (numvalue < 0x80000000) { + numvalue += 0x80000000; + } + x86_cpu_def->xlevel = numvalue; + } else if (!strcmp(featurestr, "vendor")) { + if (strlen(val) != 12) { + fprintf(stderr, "vendor string must be 12 chars long\n"); + goto error; + } + x86_cpu_def->vendor1 = 0; + x86_cpu_def->vendor2 = 0; + x86_cpu_def->vendor3 = 0; + for(i = 0; i < 4; i++) { + x86_cpu_def->vendor1 |= ((uint8_t)val[i ]) << (8 * i); + x86_cpu_def->vendor2 |= ((uint8_t)val[i + 4]) << (8 * i); + x86_cpu_def->vendor3 |= ((uint8_t)val[i + 8]) << (8 * i); + } + x86_cpu_def->vendor_override = 1; + } else if (!strcmp(featurestr, "model_id")) { + pstrcpy(x86_cpu_def->model_id, sizeof(x86_cpu_def->model_id), + val); + } else { + fprintf(stderr, "unrecognized feature %s\n", featurestr); + goto error; + } + } else if (!strcmp(featurestr, "check")) { + check_cpuid = 1; + } else if (!strcmp(featurestr, "enforce")) { + check_cpuid = enforce_cpuid = 1; + } else { + fprintf(stderr, "feature string `%s' not in format (+feature|-feature|feature=xyz)\n", featurestr); + goto error; + } + featurestr = strtok(NULL, ","); + } + x86_cpu_def->features |= plus_features; + x86_cpu_def->ext_features |= plus_ext_features; + x86_cpu_def->ext2_features |= plus_ext2_features; + x86_cpu_def->ext3_features |= plus_ext3_features; + x86_cpu_def->kvm_features |= plus_kvm_features; + x86_cpu_def->svm_features |= plus_svm_features; + x86_cpu_def->features &= ~minus_features; + x86_cpu_def->ext_features &= ~minus_ext_features; + x86_cpu_def->ext2_features &= ~minus_ext2_features; + x86_cpu_def->ext3_features &= ~minus_ext3_features; + x86_cpu_def->kvm_features &= ~minus_kvm_features; + x86_cpu_def->svm_features &= ~minus_svm_features; + if (check_cpuid) { + if (check_features_against_host(x86_cpu_def) && enforce_cpuid) + goto error; + } + free(s); + return 0; + +error: + free(s); + return -1; +} + +/* generate a composite string into buf of all cpuid names in featureset + * selected by fbits. indicate truncation at bufsize in the event of overflow. + * if flags, suppress names undefined in featureset. + */ +static void listflags(char *buf, int bufsize, uint32_t fbits, + const char **featureset, uint32_t flags) +{ + const char **p = &featureset[31]; + char *q, *b, bit; + int nc; + + b = 4 <= bufsize ? buf + (bufsize -= 3) - 1 : NULL; + *buf = '\0'; + for (q = buf, bit = 31; fbits && bufsize; --p, fbits &= ~(1 << bit), --bit) + if (fbits & 1 << bit && (*p || !flags)) { + if (*p) + nc = snprintf(q, bufsize, "%s%s", q == buf ? "" : " ", *p); + else + nc = snprintf(q, bufsize, "%s[%d]", q == buf ? "" : " ", bit); + if (bufsize <= nc) { + if (b) { + memcpy(b, "...", sizeof("...")); + } + return; + } + q += nc; + bufsize -= nc; + } +} + +/* generate CPU information: + * -? list model names + * -?model list model names/IDs + * -?dump output all model (x86_def_t) data + * -?cpuid list all recognized cpuid flag names + */ +void x86_cpu_list(FILE *f, fprintf_function cpu_fprintf, const char *optarg) +{ + unsigned char model = !strcmp("?model", optarg); + unsigned char dump = !strcmp("?dump", optarg); + unsigned char cpuid = !strcmp("?cpuid", optarg); + x86_def_t *def; + char buf[256]; + + if (cpuid) { + (*cpu_fprintf)(f, "Recognized CPUID flags:\n"); + listflags(buf, sizeof (buf), (uint32_t)~0, feature_name, 1); + (*cpu_fprintf)(f, " f_edx: %s\n", buf); + listflags(buf, sizeof (buf), (uint32_t)~0, ext_feature_name, 1); + (*cpu_fprintf)(f, " f_ecx: %s\n", buf); + listflags(buf, sizeof (buf), (uint32_t)~0, ext2_feature_name, 1); + (*cpu_fprintf)(f, " extf_edx: %s\n", buf); + listflags(buf, sizeof (buf), (uint32_t)~0, ext3_feature_name, 1); + (*cpu_fprintf)(f, " extf_ecx: %s\n", buf); + return; + } + for (def = x86_defs; def; def = def->next) { + snprintf(buf, sizeof (buf), def->flags ? "[%s]": "%s", def->name); + if (model || dump) { + (*cpu_fprintf)(f, "x86 %16s %-48s\n", buf, def->model_id); + } else { + (*cpu_fprintf)(f, "x86 %16s\n", buf); + } + if (dump) { + memcpy(buf, &def->vendor1, sizeof (def->vendor1)); + memcpy(buf + 4, &def->vendor2, sizeof (def->vendor2)); + memcpy(buf + 8, &def->vendor3, sizeof (def->vendor3)); + buf[12] = '\0'; + (*cpu_fprintf)(f, + " family %d model %d stepping %d level %d xlevel 0x%x" + " vendor \"%s\"\n", + def->family, def->model, def->stepping, def->level, + def->xlevel, buf); + listflags(buf, sizeof (buf), def->features, feature_name, 0); + (*cpu_fprintf)(f, " feature_edx %08x (%s)\n", def->features, + buf); + listflags(buf, sizeof (buf), def->ext_features, ext_feature_name, + 0); + (*cpu_fprintf)(f, " feature_ecx %08x (%s)\n", def->ext_features, + buf); + listflags(buf, sizeof (buf), def->ext2_features, ext2_feature_name, + 0); + (*cpu_fprintf)(f, " extfeature_edx %08x (%s)\n", + def->ext2_features, buf); + listflags(buf, sizeof (buf), def->ext3_features, ext3_feature_name, + 0); + (*cpu_fprintf)(f, " extfeature_ecx %08x (%s)\n", + def->ext3_features, buf); + (*cpu_fprintf)(f, "\n"); + } + } + if (kvm_enabled()) { + (*cpu_fprintf)(f, "x86 %16s\n", "[host]"); + } +} + +int cpu_x86_register (CPUX86State *env, const char *cpu_model) +{ + x86_def_t def1, *def = &def1; + + memset(def, 0, sizeof(*def)); + + if (cpu_x86_find_by_name(def, cpu_model) < 0) + return -1; + if (def->vendor1) { + env->cpuid_vendor1 = def->vendor1; + env->cpuid_vendor2 = def->vendor2; + env->cpuid_vendor3 = def->vendor3; + } else { + env->cpuid_vendor1 = CPUID_VENDOR_INTEL_1; + env->cpuid_vendor2 = CPUID_VENDOR_INTEL_2; + env->cpuid_vendor3 = CPUID_VENDOR_INTEL_3; + } + env->cpuid_vendor_override = def->vendor_override; + env->cpuid_level = def->level; + if (def->family > 0x0f) + env->cpuid_version = 0xf00 | ((def->family - 0x0f) << 20); + else + env->cpuid_version = def->family << 8; + env->cpuid_version |= ((def->model & 0xf) << 4) | ((def->model >> 4) << 16); + env->cpuid_version |= def->stepping; + env->cpuid_features = def->features; + env->pat = 0x0007040600070406ULL; + env->cpuid_ext_features = def->ext_features; + env->cpuid_ext2_features = def->ext2_features; + env->cpuid_ext3_features = def->ext3_features; + env->cpuid_xlevel = def->xlevel; + env->cpuid_kvm_features = def->kvm_features; + env->cpuid_svm_features = def->svm_features; + if (!kvm_enabled()) { + env->cpuid_features &= TCG_FEATURES; + env->cpuid_ext_features &= TCG_EXT_FEATURES; + env->cpuid_ext2_features &= (TCG_EXT2_FEATURES +#ifdef TARGET_X86_64 + | CPUID_EXT2_SYSCALL | CPUID_EXT2_LM +#endif + ); + env->cpuid_ext3_features &= TCG_EXT3_FEATURES; + env->cpuid_svm_features &= TCG_SVM_FEATURES; + } + { + const char *model_id = def->model_id; + int c, len, i; + if (!model_id) + model_id = ""; + len = strlen(model_id); + for(i = 0; i < 48; i++) { + if (i >= len) + c = '\0'; + else + c = (uint8_t)model_id[i]; + env->cpuid_model[i >> 2] |= c << (8 * (i & 3)); + } + } + return 0; +} + +#if !defined(CONFIG_USER_ONLY) +/* copy vendor id string to 32 bit register, nul pad as needed + */ +static void cpyid(const char *s, uint32_t *id) +{ + char *d = (char *)id; + char i; + + for (i = sizeof (*id); i--; ) + *d++ = *s ? *s++ : '\0'; +} + +/* interpret radix and convert from string to arbitrary scalar, + * otherwise flag failure + */ +#define setscalar(pval, str, perr) \ +{ \ + char *pend; \ + unsigned long ul; \ + \ + ul = strtoul(str, &pend, 0); \ + *str && !*pend ? (*pval = ul) : (*perr = 1); \ +} + +/* map cpuid options to feature bits, otherwise return failure + * (option tags in *str are delimited by whitespace) + */ +static void setfeatures(uint32_t *pval, const char *str, + const char **featureset, int *perr) +{ + const char *p, *q; + + for (q = p = str; *p || *q; q = p) { + while (iswhite(*p)) + q = ++p; + while (*p && !iswhite(*p)) + ++p; + if (!*q && !*p) + return; + if (!lookup_feature(pval, q, p, featureset)) { + fprintf(stderr, "error: feature \"%.*s\" not available in set\n", + (int)(p - q), q); + *perr = 1; + return; + } + } +} + +/* map config file options to x86_def_t form + */ +static int cpudef_setfield(const char *name, const char *str, void *opaque) +{ + x86_def_t *def = opaque; + int err = 0; + + if (!strcmp(name, "name")) { + def->name = strdup(str); + } else if (!strcmp(name, "model_id")) { + strncpy(def->model_id, str, sizeof (def->model_id)); + } else if (!strcmp(name, "level")) { + setscalar(&def->level, str, &err) + } else if (!strcmp(name, "vendor")) { + cpyid(&str[0], &def->vendor1); + cpyid(&str[4], &def->vendor2); + cpyid(&str[8], &def->vendor3); + } else if (!strcmp(name, "family")) { + setscalar(&def->family, str, &err) + } else if (!strcmp(name, "model")) { + setscalar(&def->model, str, &err) + } else if (!strcmp(name, "stepping")) { + setscalar(&def->stepping, str, &err) + } else if (!strcmp(name, "feature_edx")) { + setfeatures(&def->features, str, feature_name, &err); + } else if (!strcmp(name, "feature_ecx")) { + setfeatures(&def->ext_features, str, ext_feature_name, &err); + } else if (!strcmp(name, "extfeature_edx")) { + setfeatures(&def->ext2_features, str, ext2_feature_name, &err); + } else if (!strcmp(name, "extfeature_ecx")) { + setfeatures(&def->ext3_features, str, ext3_feature_name, &err); + } else if (!strcmp(name, "xlevel")) { + setscalar(&def->xlevel, str, &err) + } else { + fprintf(stderr, "error: unknown option [%s = %s]\n", name, str); + return (1); + } + if (err) { + fprintf(stderr, "error: bad option value [%s = %s]\n", name, str); + return (1); + } + return (0); +} + +/* register config file entry as x86_def_t + */ +static int cpudef_register(QemuOpts *opts, void *opaque) +{ + x86_def_t *def = qemu_mallocz(sizeof (x86_def_t)); + + qemu_opt_foreach(opts, cpudef_setfield, def, 1); + def->next = x86_defs; + x86_defs = def; + return (0); +} + +void cpu_clear_apic_feature(CPUX86State *env) +{ + env->cpuid_features &= ~CPUID_APIC; +} + +#endif /* !CONFIG_USER_ONLY */ + +/* register "cpudef" models defined in configuration file. Here we first + * preload any built-in definitions + */ +void x86_cpudef_setup(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(builtin_x86_defs); ++i) { + builtin_x86_defs[i].next = x86_defs; + builtin_x86_defs[i].flags = 1; + x86_defs = &builtin_x86_defs[i]; + } +#if !defined(CONFIG_USER_ONLY) + qemu_opts_foreach(qemu_find_opts("cpudef"), cpudef_register, NULL, 0); +#endif +} + +static void get_cpuid_vendor(CPUX86State *env, uint32_t *ebx, + uint32_t *ecx, uint32_t *edx) +{ + *ebx = env->cpuid_vendor1; + *edx = env->cpuid_vendor2; + *ecx = env->cpuid_vendor3; + + /* sysenter isn't supported on compatibility mode on AMD, syscall + * isn't supported in compatibility mode on Intel. + * Normally we advertise the actual cpu vendor, but you can override + * this if you want to use KVM's sysenter/syscall emulation + * in compatibility mode and when doing cross vendor migration + */ + if (kvm_enabled() && ! env->cpuid_vendor_override) { + host_cpuid(0, 0, NULL, ebx, ecx, edx); + } +} + +void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, + uint32_t *eax, uint32_t *ebx, + uint32_t *ecx, uint32_t *edx) +{ + /* test if maximum index reached */ + if (index & 0x80000000) { + if (index > env->cpuid_xlevel) + index = env->cpuid_level; + } else { + if (index > env->cpuid_level) + index = env->cpuid_level; + } + + switch(index) { + case 0: + *eax = env->cpuid_level; + get_cpuid_vendor(env, ebx, ecx, edx); + break; + case 1: + *eax = env->cpuid_version; + *ebx = (env->cpuid_apic_id << 24) | 8 << 8; /* CLFLUSH size in quad words, Linux wants it. */ + *ecx = env->cpuid_ext_features; + *edx = env->cpuid_features; + if (env->nr_cores * env->nr_threads > 1) { + *ebx |= (env->nr_cores * env->nr_threads) << 16; + *edx |= 1 << 28; /* HTT bit */ + } + break; + case 2: + /* cache info: needed for Pentium Pro compatibility */ + *eax = 1; + *ebx = 0; + *ecx = 0; + *edx = 0x2c307d; + break; + case 4: + /* cache info: needed for Core compatibility */ + if (env->nr_cores > 1) { + *eax = (env->nr_cores - 1) << 26; + } else { + *eax = 0; + } + switch (count) { + case 0: /* L1 dcache info */ + *eax |= 0x0000121; + *ebx = 0x1c0003f; + *ecx = 0x000003f; + *edx = 0x0000001; + break; + case 1: /* L1 icache info */ + *eax |= 0x0000122; + *ebx = 0x1c0003f; + *ecx = 0x000003f; + *edx = 0x0000001; + break; + case 2: /* L2 cache info */ + *eax |= 0x0000143; + if (env->nr_threads > 1) { + *eax |= (env->nr_threads - 1) << 14; + } + *ebx = 0x3c0003f; + *ecx = 0x0000fff; + *edx = 0x0000001; + break; + default: /* end of info */ + *eax = 0; + *ebx = 0; + *ecx = 0; + *edx = 0; + break; + } + break; + case 5: + /* mwait info: needed for Core compatibility */ + *eax = 0; /* Smallest monitor-line size in bytes */ + *ebx = 0; /* Largest monitor-line size in bytes */ + *ecx = CPUID_MWAIT_EMX | CPUID_MWAIT_IBE; + *edx = 0; + break; + case 6: + /* Thermal and Power Leaf */ + *eax = 0; + *ebx = 0; + *ecx = 0; + *edx = 0; + break; + case 9: + /* Direct Cache Access Information Leaf */ + *eax = 0; /* Bits 0-31 in DCA_CAP MSR */ + *ebx = 0; + *ecx = 0; + *edx = 0; + break; + case 0xA: + /* Architectural Performance Monitoring Leaf */ + *eax = 0; + *ebx = 0; + *ecx = 0; + *edx = 0; + break; + case 0xD: + /* Processor Extended State */ + if (!(env->cpuid_ext_features & CPUID_EXT_XSAVE)) { + *eax = 0; + *ebx = 0; + *ecx = 0; + *edx = 0; + break; + } + if (kvm_enabled()) { + *eax = kvm_arch_get_supported_cpuid(env, 0xd, count, R_EAX); + *ebx = kvm_arch_get_supported_cpuid(env, 0xd, count, R_EBX); + *ecx = kvm_arch_get_supported_cpuid(env, 0xd, count, R_ECX); + *edx = kvm_arch_get_supported_cpuid(env, 0xd, count, R_EDX); + } else { + *eax = 0; + *ebx = 0; + *ecx = 0; + *edx = 0; + } + break; + case 0x80000000: + *eax = env->cpuid_xlevel; + *ebx = env->cpuid_vendor1; + *edx = env->cpuid_vendor2; + *ecx = env->cpuid_vendor3; + break; + case 0x80000001: + *eax = env->cpuid_version; + *ebx = 0; + *ecx = env->cpuid_ext3_features; + *edx = env->cpuid_ext2_features; + + /* The Linux kernel checks for the CMPLegacy bit and + * discards multiple thread information if it is set. + * So dont set it here for Intel to make Linux guests happy. + */ + if (env->nr_cores * env->nr_threads > 1) { + uint32_t tebx, tecx, tedx; + get_cpuid_vendor(env, &tebx, &tecx, &tedx); + if (tebx != CPUID_VENDOR_INTEL_1 || + tedx != CPUID_VENDOR_INTEL_2 || + tecx != CPUID_VENDOR_INTEL_3) { + *ecx |= 1 << 1; /* CmpLegacy bit */ + } + } + if (kvm_enabled()) { + uint32_t h_eax, h_edx; + + host_cpuid(index, 0, &h_eax, NULL, NULL, &h_edx); + + /* disable CPU features that the host does not support */ + + /* long mode */ + if ((h_edx & 0x20000000) == 0 /* || !lm_capable_kernel */) + *edx &= ~0x20000000; + /* syscall */ + if ((h_edx & 0x00000800) == 0) + *edx &= ~0x00000800; + /* nx */ + if ((h_edx & 0x00100000) == 0) + *edx &= ~0x00100000; + + /* disable CPU features that KVM cannot support */ + + /* svm */ + if (!kvm_nested) + *ecx &= ~CPUID_EXT3_SVM; + /* 3dnow */ + *edx &= ~0xc0000000; + } + break; + case 0x80000002: + case 0x80000003: + case 0x80000004: + *eax = env->cpuid_model[(index - 0x80000002) * 4 + 0]; + *ebx = env->cpuid_model[(index - 0x80000002) * 4 + 1]; + *ecx = env->cpuid_model[(index - 0x80000002) * 4 + 2]; + *edx = env->cpuid_model[(index - 0x80000002) * 4 + 3]; + break; + case 0x80000005: + /* cache info (L1 cache) */ + *eax = 0x01ff01ff; + *ebx = 0x01ff01ff; + *ecx = 0x40020140; + *edx = 0x40020140; + break; + case 0x80000006: + /* cache info (L2 cache) */ + *eax = 0; + *ebx = 0x42004200; + *ecx = 0x02008140; + *edx = 0; + break; + case 0x80000008: + /* virtual & phys address size in low 2 bytes. */ +/* XXX: This value must match the one used in the MMU code. */ + if (env->cpuid_ext2_features & CPUID_EXT2_LM) { + /* 64 bit processor */ +/* XXX: The physical address space is limited to 42 bits in exec.c. */ + *eax = 0x00003028; /* 48 bits virtual, 40 bits physical */ + } else { + if (env->cpuid_features & CPUID_PSE36) + *eax = 0x00000024; /* 36 bits physical */ + else + *eax = 0x00000020; /* 32 bits physical */ + } + *ebx = 0; + *ecx = 0; + *edx = 0; + if (env->nr_cores * env->nr_threads > 1) { + *ecx |= (env->nr_cores * env->nr_threads) - 1; + } + break; + case 0x8000000A: + if (env->cpuid_ext3_features & CPUID_EXT3_SVM) { + *eax = 0x00000001; /* SVM Revision */ + *ebx = 0x00000010; /* nr of ASIDs */ + *ecx = 0; + *edx = env->cpuid_svm_features; /* optional features */ + } else { + *eax = 0; + *ebx = 0; + *ecx = 0; + *edx = 0; + } + break; + default: + /* reserved values: zero */ + *eax = 0; + *ebx = 0; + *ecx = 0; + *edx = 0; + break; + } +} diff -Nru qemu-kvm-0.12.5+noroms/target-i386/exec.h qemu-kvm-0.14.1/target-i386/exec.h --- qemu-kvm-0.12.5+noroms/target-i386/exec.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-i386/exec.h 2011-05-11 13:29:46.000000000 +0000 @@ -73,6 +73,7 @@ target_ulong next_eip); void QEMU_NORETURN raise_exception_err(int exception_index, int error_code); void QEMU_NORETURN raise_exception(int exception_index); +void QEMU_NORETURN raise_exception_env(int exception_index, CPUState *nenv); void do_smm_enter(void); /* n must be a constant to be efficient */ @@ -290,62 +291,6 @@ (eflags & update_mask) | 0x2; } -static inline void env_to_regs(void) -{ -#ifdef reg_EAX - EAX = env->regs[R_EAX]; -#endif -#ifdef reg_ECX - ECX = env->regs[R_ECX]; -#endif -#ifdef reg_EDX - EDX = env->regs[R_EDX]; -#endif -#ifdef reg_EBX - EBX = env->regs[R_EBX]; -#endif -#ifdef reg_ESP - ESP = env->regs[R_ESP]; -#endif -#ifdef reg_EBP - EBP = env->regs[R_EBP]; -#endif -#ifdef reg_ESI - ESI = env->regs[R_ESI]; -#endif -#ifdef reg_EDI - EDI = env->regs[R_EDI]; -#endif -} - -static inline void regs_to_env(void) -{ -#ifdef reg_EAX - env->regs[R_EAX] = EAX; -#endif -#ifdef reg_ECX - env->regs[R_ECX] = ECX; -#endif -#ifdef reg_EDX - env->regs[R_EDX] = EDX; -#endif -#ifdef reg_EBX - env->regs[R_EBX] = EBX; -#endif -#ifdef reg_ESP - env->regs[R_ESP] = ESP; -#endif -#ifdef reg_EBP - env->regs[R_EBP] = EBP; -#endif -#ifdef reg_ESI - env->regs[R_ESI] = ESI; -#endif -#ifdef reg_EDI - env->regs[R_EDI] = EDI; -#endif -} - static inline int cpu_has_work(CPUState *env) { int work; @@ -382,3 +327,9 @@ if (env->efer & MSR_EFER_SVME) env->hflags |= HF_SVME_MASK; } + +static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb) +{ + env->eip = tb->pc - tb->cs_base; +} + diff -Nru qemu-kvm-0.12.5+noroms/target-i386/fake-exec.c qemu-kvm-0.14.1/target-i386/fake-exec.c --- qemu-kvm-0.12.5+noroms/target-i386/fake-exec.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-i386/fake-exec.c 2011-05-11 13:29:46.000000000 +0000 @@ -12,22 +12,20 @@ */ #include "exec.h" #include "cpu.h" +#include "tcg.h" int code_copy_enabled = 0; CCTable cc_table[CC_OP_NB]; +TCGContext tcg_ctx; + void cpu_dump_statistics (CPUState *env, FILE*f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...), int flags) { } -unsigned long code_gen_max_block_size(void) -{ - return 32; -} - void cpu_gen_init(void) { } @@ -48,3 +46,7 @@ void optimize_flags_init(void) { } + +void tcg_prologue_init(TCGContext *ctx) +{ +} diff -Nru qemu-kvm-0.12.5+noroms/target-i386/helper.c qemu-kvm-0.14.1/target-i386/helper.c --- qemu-kvm-0.12.5+noroms/target-i386/helper.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-i386/helper.c 2011-05-11 13:29:46.000000000 +0000 @@ -27,528 +27,12 @@ #include "exec-all.h" #include "qemu-common.h" #include "kvm.h" +#include "kvm_x86.h" #include "qemu-kvm.h" //#define DEBUG_MMU -/* feature flags taken from "Intel Processor Identification and the CPUID - * Instruction" and AMD's "CPUID Specification". In cases of disagreement - * about feature names, the Linux name is used. */ -static const char *feature_name[] = { - "fpu", "vme", "de", "pse", "tsc", "msr", "pae", "mce", - "cx8", "apic", NULL, "sep", "mtrr", "pge", "mca", "cmov", - "pat", "pse36", "pn" /* Intel psn */, "clflush" /* Intel clfsh */, NULL, "ds" /* Intel dts */, "acpi", "mmx", - "fxsr", "sse", "sse2", "ss", "ht" /* Intel htt */, "tm", "ia64", "pbe", -}; -static const char *ext_feature_name[] = { - "pni" /* Intel,AMD sse3 */, NULL, NULL, "monitor", "ds_cpl", "vmx", NULL /* Linux smx */, "est", - "tm2", "ssse3", "cid", NULL, NULL, "cx16", "xtpr", NULL, - NULL, NULL, "dca", NULL, NULL, "x2apic", NULL, "popcnt", - NULL, NULL, NULL, NULL, NULL, NULL, NULL, "hypervisor", -}; -static const char *ext2_feature_name[] = { - "fpu", "vme", "de", "pse", "tsc", "msr", "pae", "mce", - "cx8" /* AMD CMPXCHG8B */, "apic", NULL, "syscall", "mtrr", "pge", "mca", "cmov", - "pat", "pse36", NULL, NULL /* Linux mp */, "nx" /* Intel xd */, NULL, "mmxext", "mmx", - "fxsr", "fxsr_opt" /* AMD ffxsr */, "pdpe1gb" /* AMD Page1GB */, "rdtscp", NULL, "lm" /* Intel 64 */, "3dnowext", "3dnow", -}; -static const char *ext3_feature_name[] = { - "lahf_lm" /* AMD LahfSahf */, "cmp_legacy", "svm", "extapic" /* AMD ExtApicSpace */, "cr8legacy" /* AMD AltMovCr8 */, "abm", "sse4a", "misalignsse", - "3dnowprefetch", "osvw", NULL /* Linux ibs */, NULL, "skinit", "wdt", NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, -}; - -static void add_flagname_to_bitmaps(const char *flagname, uint32_t *features, - uint32_t *ext_features, - uint32_t *ext2_features, - uint32_t *ext3_features) -{ - int i; - int found = 0; - - for ( i = 0 ; i < 32 ; i++ ) - if (feature_name[i] && !strcmp (flagname, feature_name[i])) { - *features |= 1 << i; - found = 1; - } - for ( i = 0 ; i < 32 ; i++ ) - if (ext_feature_name[i] && !strcmp (flagname, ext_feature_name[i])) { - *ext_features |= 1 << i; - found = 1; - } - for ( i = 0 ; i < 32 ; i++ ) - if (ext2_feature_name[i] && !strcmp (flagname, ext2_feature_name[i])) { - *ext2_features |= 1 << i; - found = 1; - } - for ( i = 0 ; i < 32 ; i++ ) - if (ext3_feature_name[i] && !strcmp (flagname, ext3_feature_name[i])) { - *ext3_features |= 1 << i; - found = 1; - } - if (!found) { - fprintf(stderr, "CPU feature %s not found\n", flagname); - } -} - -typedef struct x86_def_t { - const char *name; - uint32_t level; - uint32_t vendor1, vendor2, vendor3; - int family; - int model; - int stepping; - uint32_t features, ext_features, ext2_features, ext3_features; - uint32_t xlevel; - char model_id[48]; - int vendor_override; -} x86_def_t; - -#define I486_FEATURES (CPUID_FP87 | CPUID_VME | CPUID_PSE) -#define PENTIUM_FEATURES (I486_FEATURES | CPUID_DE | CPUID_TSC | \ - CPUID_MSR | CPUID_MCE | CPUID_CX8 | CPUID_MMX) -#define PENTIUM2_FEATURES (PENTIUM_FEATURES | CPUID_PAE | CPUID_SEP | \ - CPUID_MTRR | CPUID_PGE | CPUID_MCA | CPUID_CMOV | CPUID_PAT | \ - CPUID_PSE36 | CPUID_FXSR) -#define PENTIUM3_FEATURES (PENTIUM2_FEATURES | CPUID_SSE) -#define PPRO_FEATURES (CPUID_FP87 | CPUID_DE | CPUID_PSE | CPUID_TSC | \ - CPUID_MSR | CPUID_MCE | CPUID_CX8 | CPUID_PGE | CPUID_CMOV | \ - CPUID_PAT | CPUID_FXSR | CPUID_MMX | CPUID_SSE | CPUID_SSE2 | \ - CPUID_PAE | CPUID_SEP | CPUID_APIC) -static x86_def_t x86_defs[] = { -#ifdef TARGET_X86_64 - { - .name = "qemu64", - .level = 4, - .vendor1 = CPUID_VENDOR_AMD_1, - .vendor2 = CPUID_VENDOR_AMD_2, - .vendor3 = CPUID_VENDOR_AMD_3, - .family = 6, - .model = 2, - .stepping = 3, - .features = PPRO_FEATURES | - /* these features are needed for Win64 and aren't fully implemented */ - CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA | - /* this feature is needed for Solaris and isn't fully implemented */ - CPUID_PSE36, - .ext_features = CPUID_EXT_SSE3 | CPUID_EXT_CX16 | CPUID_EXT_POPCNT, - .ext2_features = (PPRO_FEATURES & 0x0183F3FF) | - CPUID_EXT2_LM | CPUID_EXT2_SYSCALL | CPUID_EXT2_NX, - .ext3_features = CPUID_EXT3_LAHF_LM | CPUID_EXT3_SVM | - CPUID_EXT3_ABM | CPUID_EXT3_SSE4A, - .xlevel = 0x8000000A, - .model_id = "QEMU Virtual CPU version " QEMU_VERSION, - }, - { - .name = "phenom", - .level = 5, - .vendor1 = CPUID_VENDOR_AMD_1, - .vendor2 = CPUID_VENDOR_AMD_2, - .vendor3 = CPUID_VENDOR_AMD_3, - .family = 16, - .model = 2, - .stepping = 3, - /* Missing: CPUID_VME, CPUID_HT */ - .features = PPRO_FEATURES | - CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA | - CPUID_PSE36, - .ext_features = CPUID_EXT_SSE3 | CPUID_EXT_MONITOR | CPUID_EXT_CX16 | - CPUID_EXT_POPCNT, - /* Missing: CPUID_EXT2_PDPE1GB, CPUID_EXT2_RDTSCP */ - .ext2_features = (PPRO_FEATURES & 0x0183F3FF) | - CPUID_EXT2_LM | CPUID_EXT2_SYSCALL | CPUID_EXT2_NX | - CPUID_EXT2_3DNOW | CPUID_EXT2_3DNOWEXT | CPUID_EXT2_MMXEXT | - CPUID_EXT2_FFXSR, - /* Missing: CPUID_EXT3_CMP_LEG, CPUID_EXT3_EXTAPIC, - CPUID_EXT3_CR8LEG, - CPUID_EXT3_MISALIGNSSE, CPUID_EXT3_3DNOWPREFETCH, - CPUID_EXT3_OSVW, CPUID_EXT3_IBS */ - .ext3_features = CPUID_EXT3_LAHF_LM | CPUID_EXT3_SVM | - CPUID_EXT3_ABM | CPUID_EXT3_SSE4A, - .xlevel = 0x8000001A, - .model_id = "AMD Phenom(tm) 9550 Quad-Core Processor" - }, - { - .name = "core2duo", - .level = 10, - .family = 6, - .model = 15, - .stepping = 11, - /* The original CPU also implements these features: - CPUID_VME, CPUID_DTS, CPUID_ACPI, CPUID_SS, CPUID_HT, - CPUID_TM, CPUID_PBE */ - .features = PPRO_FEATURES | - CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA | - CPUID_PSE36, - /* The original CPU also implements these ext features: - CPUID_EXT_DTES64, CPUID_EXT_DSCPL, CPUID_EXT_VMX, CPUID_EXT_EST, - CPUID_EXT_TM2, CPUID_EXT_CX16, CPUID_EXT_XTPR, CPUID_EXT_PDCM */ - .ext_features = CPUID_EXT_SSE3 | CPUID_EXT_MONITOR | CPUID_EXT_SSSE3, - .ext2_features = CPUID_EXT2_LM | CPUID_EXT2_SYSCALL | CPUID_EXT2_NX, - .ext3_features = CPUID_EXT3_LAHF_LM, - .xlevel = 0x80000008, - .model_id = "Intel(R) Core(TM)2 Duo CPU T7700 @ 2.40GHz", - }, - { - .name = "kvm64", - .level = 5, - .vendor1 = CPUID_VENDOR_INTEL_1, - .vendor2 = CPUID_VENDOR_INTEL_2, - .vendor3 = CPUID_VENDOR_INTEL_3, - .family = 15, - .model = 6, - .stepping = 1, - /* Missing: CPUID_VME, CPUID_HT */ - .features = PPRO_FEATURES | - CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA | - CPUID_PSE36, - /* Missing: CPUID_EXT_POPCNT, CPUID_EXT_MONITOR */ - .ext_features = CPUID_EXT_SSE3 | CPUID_EXT_CX16, - /* Missing: CPUID_EXT2_PDPE1GB, CPUID_EXT2_RDTSCP */ - .ext2_features = (PPRO_FEATURES & 0x0183F3FF) | - CPUID_EXT2_LM | CPUID_EXT2_SYSCALL | CPUID_EXT2_NX, - /* Missing: CPUID_EXT3_LAHF_LM, CPUID_EXT3_CMP_LEG, CPUID_EXT3_EXTAPIC, - CPUID_EXT3_CR8LEG, CPUID_EXT3_ABM, CPUID_EXT3_SSE4A, - CPUID_EXT3_MISALIGNSSE, CPUID_EXT3_3DNOWPREFETCH, - CPUID_EXT3_OSVW, CPUID_EXT3_IBS, CPUID_EXT3_SVM */ - .ext3_features = 0, - .xlevel = 0x80000008, - .model_id = "Common KVM processor" - }, -#endif - { - .name = "qemu32", - .level = 4, - .family = 6, - .model = 3, - .stepping = 3, - .features = PPRO_FEATURES, - .ext_features = CPUID_EXT_SSE3 | CPUID_EXT_POPCNT, - .xlevel = 0, - .model_id = "QEMU Virtual CPU version " QEMU_VERSION, - }, - { - .name = "coreduo", - .level = 10, - .family = 6, - .model = 14, - .stepping = 8, - /* The original CPU also implements these features: - CPUID_DTS, CPUID_ACPI, CPUID_SS, CPUID_HT, - CPUID_TM, CPUID_PBE */ - .features = PPRO_FEATURES | CPUID_VME | - CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA, - /* The original CPU also implements these ext features: - CPUID_EXT_VMX, CPUID_EXT_EST, CPUID_EXT_TM2, CPUID_EXT_XTPR, - CPUID_EXT_PDCM */ - .ext_features = CPUID_EXT_SSE3 | CPUID_EXT_MONITOR, - .ext2_features = CPUID_EXT2_NX, - .xlevel = 0x80000008, - .model_id = "Genuine Intel(R) CPU T2600 @ 2.16GHz", - }, - { - .name = "486", - .level = 0, - .family = 4, - .model = 0, - .stepping = 0, - .features = I486_FEATURES, - .xlevel = 0, - }, - { - .name = "pentium", - .level = 1, - .family = 5, - .model = 4, - .stepping = 3, - .features = PENTIUM_FEATURES, - .xlevel = 0, - }, - { - .name = "pentium2", - .level = 2, - .family = 6, - .model = 5, - .stepping = 2, - .features = PENTIUM2_FEATURES, - .xlevel = 0, - }, - { - .name = "pentium3", - .level = 2, - .family = 6, - .model = 7, - .stepping = 3, - .features = PENTIUM3_FEATURES, - .xlevel = 0, - }, - { - .name = "athlon", - .level = 2, - .vendor1 = CPUID_VENDOR_AMD_1, - .vendor2 = CPUID_VENDOR_AMD_2, - .vendor3 = CPUID_VENDOR_AMD_3, - .family = 6, - .model = 2, - .stepping = 3, - .features = PPRO_FEATURES | CPUID_PSE36 | CPUID_VME | CPUID_MTRR | CPUID_MCA, - .ext2_features = (PPRO_FEATURES & 0x0183F3FF) | CPUID_EXT2_MMXEXT | CPUID_EXT2_3DNOW | CPUID_EXT2_3DNOWEXT, - .xlevel = 0x80000008, - /* XXX: put another string ? */ - .model_id = "QEMU Virtual CPU version " QEMU_VERSION, - }, - { - .name = "n270", - /* original is on level 10 */ - .level = 5, - .family = 6, - .model = 28, - .stepping = 2, - .features = PPRO_FEATURES | - CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA | CPUID_VME, - /* Missing: CPUID_DTS | CPUID_ACPI | CPUID_SS | - * CPUID_HT | CPUID_TM | CPUID_PBE */ - /* Some CPUs got no CPUID_SEP */ - .ext_features = CPUID_EXT_MONITOR | - CPUID_EXT_SSE3 /* PNI */ | CPUID_EXT_SSSE3, - /* Missing: CPUID_EXT_DSCPL | CPUID_EXT_EST | - * CPUID_EXT_TM2 | CPUID_EXT_XTPR */ - .ext2_features = (PPRO_FEATURES & 0x0183F3FF) | CPUID_EXT2_NX, - /* Missing: .ext3_features = CPUID_EXT3_LAHF_LM */ - .xlevel = 0x8000000A, - .model_id = "Intel(R) Atom(TM) CPU N270 @ 1.60GHz", - }, -}; - -static void host_cpuid(uint32_t function, uint32_t count, uint32_t *eax, - uint32_t *ebx, uint32_t *ecx, uint32_t *edx); - -static int cpu_x86_fill_model_id(char *str) -{ - uint32_t eax = 0, ebx = 0, ecx = 0, edx = 0; - int i; - - for (i = 0; i < 3; i++) { - host_cpuid(0x80000002 + i, 0, &eax, &ebx, &ecx, &edx); - memcpy(str + i * 16 + 0, &eax, 4); - memcpy(str + i * 16 + 4, &ebx, 4); - memcpy(str + i * 16 + 8, &ecx, 4); - memcpy(str + i * 16 + 12, &edx, 4); - } - return 0; -} - -static int cpu_x86_fill_host(x86_def_t *x86_cpu_def) -{ - uint32_t eax = 0, ebx = 0, ecx = 0, edx = 0; - - x86_cpu_def->name = "host"; - host_cpuid(0x0, 0, &eax, &ebx, &ecx, &edx); - x86_cpu_def->level = eax; - x86_cpu_def->vendor1 = ebx; - x86_cpu_def->vendor2 = edx; - x86_cpu_def->vendor3 = ecx; - - host_cpuid(0x1, 0, &eax, &ebx, &ecx, &edx); - x86_cpu_def->family = ((eax >> 8) & 0x0F) + ((eax >> 20) & 0xFF); - x86_cpu_def->model = ((eax >> 4) & 0x0F) | ((eax & 0xF0000) >> 12); - x86_cpu_def->stepping = eax & 0x0F; - x86_cpu_def->ext_features = ecx; - x86_cpu_def->features = edx; - - host_cpuid(0x80000000, 0, &eax, &ebx, &ecx, &edx); - x86_cpu_def->xlevel = eax; - - host_cpuid(0x80000001, 0, &eax, &ebx, &ecx, &edx); - x86_cpu_def->ext2_features = edx; - x86_cpu_def->ext3_features = ecx; - cpu_x86_fill_model_id(x86_cpu_def->model_id); - x86_cpu_def->vendor_override = 0; - - return 0; -} - -static int cpu_x86_find_by_name(x86_def_t *x86_cpu_def, const char *cpu_model) -{ - unsigned int i; - x86_def_t *def; - - char *s = strdup(cpu_model); - char *featurestr, *name = strtok(s, ","); - uint32_t plus_features = 0, plus_ext_features = 0, plus_ext2_features = 0, plus_ext3_features = 0; - uint32_t minus_features = 0, minus_ext_features = 0, minus_ext2_features = 0, minus_ext3_features = 0; - uint32_t numvalue; - - def = NULL; - for (i = 0; i < ARRAY_SIZE(x86_defs); i++) { - if (strcmp(name, x86_defs[i].name) == 0) { - def = &x86_defs[i]; - break; - } - } - if (kvm_enabled() && strcmp(name, "host") == 0) { - cpu_x86_fill_host(x86_cpu_def); - } else if (!def) { - goto error; - } else { - memcpy(x86_cpu_def, def, sizeof(*def)); - } - - add_flagname_to_bitmaps("hypervisor", &plus_features, - &plus_ext_features, &plus_ext2_features, &plus_ext3_features); - - featurestr = strtok(NULL, ","); - - while (featurestr) { - char *val; - if (featurestr[0] == '+') { - add_flagname_to_bitmaps(featurestr + 1, &plus_features, &plus_ext_features, &plus_ext2_features, &plus_ext3_features); - } else if (featurestr[0] == '-') { - add_flagname_to_bitmaps(featurestr + 1, &minus_features, &minus_ext_features, &minus_ext2_features, &minus_ext3_features); - } else if ((val = strchr(featurestr, '='))) { - *val = 0; val++; - if (!strcmp(featurestr, "family")) { - char *err; - numvalue = strtoul(val, &err, 0); - if (!*val || *err) { - fprintf(stderr, "bad numerical value %s\n", val); - goto error; - } - x86_cpu_def->family = numvalue; - } else if (!strcmp(featurestr, "model")) { - char *err; - numvalue = strtoul(val, &err, 0); - if (!*val || *err || numvalue > 0xff) { - fprintf(stderr, "bad numerical value %s\n", val); - goto error; - } - x86_cpu_def->model = numvalue; - } else if (!strcmp(featurestr, "stepping")) { - char *err; - numvalue = strtoul(val, &err, 0); - if (!*val || *err || numvalue > 0xf) { - fprintf(stderr, "bad numerical value %s\n", val); - goto error; - } - x86_cpu_def->stepping = numvalue ; - } else if (!strcmp(featurestr, "level")) { - char *err; - numvalue = strtoul(val, &err, 0); - if (!*val || *err) { - fprintf(stderr, "bad numerical value %s\n", val); - goto error; - } - x86_cpu_def->level = numvalue; - } else if (!strcmp(featurestr, "xlevel")) { - char *err; - numvalue = strtoul(val, &err, 0); - if (!*val || *err) { - fprintf(stderr, "bad numerical value %s\n", val); - goto error; - } - if (numvalue < 0x80000000) { - numvalue += 0x80000000; - } - x86_cpu_def->xlevel = numvalue; - } else if (!strcmp(featurestr, "vendor")) { - if (strlen(val) != 12) { - fprintf(stderr, "vendor string must be 12 chars long\n"); - goto error; - } - x86_cpu_def->vendor1 = 0; - x86_cpu_def->vendor2 = 0; - x86_cpu_def->vendor3 = 0; - for(i = 0; i < 4; i++) { - x86_cpu_def->vendor1 |= ((uint8_t)val[i ]) << (8 * i); - x86_cpu_def->vendor2 |= ((uint8_t)val[i + 4]) << (8 * i); - x86_cpu_def->vendor3 |= ((uint8_t)val[i + 8]) << (8 * i); - } - x86_cpu_def->vendor_override = 1; - } else if (!strcmp(featurestr, "model_id")) { - pstrcpy(x86_cpu_def->model_id, sizeof(x86_cpu_def->model_id), - val); - } else { - fprintf(stderr, "unrecognized feature %s\n", featurestr); - goto error; - } - } else { - fprintf(stderr, "feature string `%s' not in format (+feature|-feature|feature=xyz)\n", featurestr); - goto error; - } - featurestr = strtok(NULL, ","); - } - x86_cpu_def->features |= plus_features; - x86_cpu_def->ext_features |= plus_ext_features; - x86_cpu_def->ext2_features |= plus_ext2_features; - x86_cpu_def->ext3_features |= plus_ext3_features; - x86_cpu_def->features &= ~minus_features; - x86_cpu_def->ext_features &= ~minus_ext_features; - x86_cpu_def->ext2_features &= ~minus_ext2_features; - x86_cpu_def->ext3_features &= ~minus_ext3_features; - free(s); - return 0; - -error: - free(s); - return -1; -} - -void x86_cpu_list (FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...)) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(x86_defs); i++) - (*cpu_fprintf)(f, "x86 %16s\n", x86_defs[i].name); -} - -static int cpu_x86_register (CPUX86State *env, const char *cpu_model) -{ - x86_def_t def1, *def = &def1; - - if (cpu_x86_find_by_name(def, cpu_model) < 0) - return -1; - if (def->vendor1) { - env->cpuid_vendor1 = def->vendor1; - env->cpuid_vendor2 = def->vendor2; - env->cpuid_vendor3 = def->vendor3; - } else { - env->cpuid_vendor1 = CPUID_VENDOR_INTEL_1; - env->cpuid_vendor2 = CPUID_VENDOR_INTEL_2; - env->cpuid_vendor3 = CPUID_VENDOR_INTEL_3; - } - env->cpuid_vendor_override = def->vendor_override || kvm_enabled(); - env->cpuid_level = def->level; - if (def->family > 0x0f) - env->cpuid_version = 0xf00 | ((def->family - 0x0f) << 20); - else - env->cpuid_version = def->family << 8; - env->cpuid_version |= ((def->model & 0xf) << 4) | ((def->model >> 4) << 16); - env->cpuid_version |= def->stepping; - env->cpuid_features = def->features; - env->pat = 0x0007040600070406ULL; - env->cpuid_ext_features = def->ext_features; - env->cpuid_ext2_features = def->ext2_features; - env->cpuid_xlevel = def->xlevel; - env->cpuid_ext3_features = def->ext3_features; - { - const char *model_id = def->model_id; - int c, len, i; - if (!model_id) - model_id = ""; - len = strlen(model_id); - for(i = 0; i < 48; i++) { - if (i >= len) - c = '\0'; - else - c = (uint8_t)model_id[i]; - env->cpuid_model[i >> 2] |= c << (8 * (i & 3)); - } - } - return 0; -} - /* NOTE: must be called outside the CPU execute loop */ void cpu_reset(CPUX86State *env) { @@ -628,6 +112,32 @@ qemu_free(env); } +static void cpu_x86_version(CPUState *env, int *family, int *model) +{ + int cpuver = env->cpuid_version; + + if (family == NULL || model == NULL) { + return; + } + + *family = (cpuver >> 8) & 0x0f; + *model = ((cpuver >> 12) & 0xf0) + ((cpuver >> 4) & 0x0f); +} + +/* Broadcast MCA signal for processor version 06H_EH and above */ +int cpu_x86_support_mca_broadcast(CPUState *env) +{ + int family = 0; + int model = 0; + + cpu_x86_version(env, &family, &model); + if ((family == 6 && model >= 14) || family > 6) { + return 1; + } + + return 0; +} + /***********************************************************/ /* x86 debug */ @@ -687,19 +197,18 @@ }; static void -cpu_x86_dump_seg_cache(CPUState *env, FILE *f, - int (*cpu_fprintf)(FILE *f, const char *fmt, ...), +cpu_x86_dump_seg_cache(CPUState *env, FILE *f, fprintf_function cpu_fprintf, const char *name, struct SegmentCache *sc) { #ifdef TARGET_X86_64 if (env->hflags & HF_CS64_MASK) { cpu_fprintf(f, "%-3s=%04x %016" PRIx64 " %08x %08x", name, - sc->selector, sc->base, sc->limit, sc->flags); + sc->selector, sc->base, sc->limit, sc->flags & 0x00ffff00); } else #endif { cpu_fprintf(f, "%-3s=%04x %08x %08x %08x", name, sc->selector, - (uint32_t)sc->base, sc->limit, sc->flags); + (uint32_t)sc->base, sc->limit, sc->flags & 0x00ffff00); } if (!(env->hflags & HF_PE_MASK) || !(sc->flags & DESC_P_MASK)) @@ -733,16 +242,19 @@ "Reserved", "IntGate64", "TrapGate64" } }; - cpu_fprintf(f, sys_type_name[(env->hflags & HF_LMA_MASK) ? 1 : 0] - [(sc->flags & DESC_TYPE_MASK) - >> DESC_TYPE_SHIFT]); + cpu_fprintf(f, "%s", + sys_type_name[(env->hflags & HF_LMA_MASK) ? 1 : 0] + [(sc->flags & DESC_TYPE_MASK) + >> DESC_TYPE_SHIFT]); } done: cpu_fprintf(f, "\n"); } -void cpu_dump_state(CPUState *env, FILE *f, - int (*cpu_fprintf)(FILE *f, const char *fmt, ...), +#define DUMP_CODE_BYTES_TOTAL 50 +#define DUMP_CODE_BYTES_BACKWARD 20 + +void cpu_dump_state(CPUState *env, FILE *f, fprintf_function cpu_fprintf, int flags) { int eflags, i, nb; @@ -852,9 +364,11 @@ (uint32_t)env->cr[2], (uint32_t)env->cr[3], (uint32_t)env->cr[4]); - for(i = 0; i < 4; i++) - cpu_fprintf(f, "DR%d=%08x ", i, env->dr[i]); - cpu_fprintf(f, "\nDR6=%08x DR7=%08x\n", env->dr[6], env->dr[7]); + for(i = 0; i < 4; i++) { + cpu_fprintf(f, "DR%d=" TARGET_FMT_lx " ", i, env->dr[i]); + } + cpu_fprintf(f, "\nDR6=" TARGET_FMT_lx " DR7=" TARGET_FMT_lx "\n", + env->dr[6], env->dr[7]); } if (flags & X86_DUMP_CCOP) { if ((unsigned)env->cc_op < CC_OP_NB) @@ -874,6 +388,7 @@ cc_op_name); } } + cpu_fprintf(f, "EFER=%016" PRIx64 "\n", env->efer); if (flags & X86_DUMP_FPU) { int fptag; fptag = 0; @@ -924,6 +439,24 @@ cpu_fprintf(f, " "); } } + if (flags & CPU_DUMP_CODE) { + target_ulong base = env->segs[R_CS].base + env->eip; + target_ulong offs = MIN(env->eip, DUMP_CODE_BYTES_BACKWARD); + uint8_t code; + char codestr[3]; + + cpu_fprintf(f, "Code="); + for (i = 0; i < DUMP_CODE_BYTES_TOTAL; i++) { + if (cpu_memory_rw_debug(env, base - offs + i, &code, 1, 0) == 0) { + snprintf(codestr, sizeof(codestr), "%02x", code); + } else { + snprintf(codestr, sizeof(codestr), "??"); + } + cpu_fprintf(f, "%s%s%s%s", i > 0 ? " " : "", + i == offs ? "<" : "", codestr, i == offs ? ">" : ""); + } + cpu_fprintf(f, "\n"); + } } /***********************************************************/ @@ -1036,11 +569,6 @@ return 1; } -target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr) -{ - return addr; -} - #else /* XXX: This value should match the one returned by CPUID @@ -1055,14 +583,13 @@ -1 = cannot handle fault 0 = nothing more to do 1 = generate PF fault - 2 = soft MMU activation required for this block */ int cpu_x86_handle_mmu_fault(CPUX86State *env, target_ulong addr, int is_write1, int mmu_idx, int is_softmmu) { uint64_t ptep, pte; target_ulong pde_addr, pte_addr; - int error_code, is_dirty, prot, page_size, ret, is_write, is_user; + int error_code, is_dirty, prot, page_size, is_write, is_user; target_phys_addr_t paddr; uint32_t page_offset; target_ulong vaddr, virt_addr; @@ -1323,8 +850,8 @@ paddr = (pte & TARGET_PAGE_MASK) + page_offset; vaddr = virt_addr + page_offset; - ret = tlb_set_page_exec(env, vaddr, paddr, prot, mmu_idx, is_softmmu); - return ret; + tlb_set_page(env, vaddr, paddr, prot, mmu_idx, page_size); + return 0; do_fault_protect: error_code = PG_ERROR_P_MASK; do_fault: @@ -1512,7 +1039,7 @@ static CPUDebugExcpHandler *prev_debug_excp_handler; -void raise_exception(int exception_index); +void raise_exception_env(int exception_index, CPUState *env); static void breakpoint_handler(CPUState *env) { @@ -1522,7 +1049,7 @@ if (env->watchpoint_hit->flags & BP_CPU) { env->watchpoint_hit = NULL; if (check_hw_breakpoints(env, 0)) - raise_exception(EXCP01_DB); + raise_exception_env(EXCP01_DB, env); else cpu_resume_from_signal(env, NULL); } @@ -1531,7 +1058,7 @@ if (bp->pc == env->eip) { if (bp->flags & BP_CPU) { check_hw_breakpoints(env, 1); - raise_exception(EXCP01_DB); + raise_exception_env(EXCP01_DB, env); } break; } @@ -1543,21 +1070,12 @@ /* This should come from sysemu.h - if we could include it here... */ void qemu_system_reset_request(void); -void cpu_inject_x86_mce(CPUState *cenv, int bank, uint64_t status, +static void qemu_inject_x86_mce(CPUState *cenv, int bank, uint64_t status, uint64_t mcg_status, uint64_t addr, uint64_t misc) { uint64_t mcg_cap = cenv->mcg_cap; - unsigned bank_num = mcg_cap & 0xff; uint64_t *banks = cenv->mce_banks; - if (kvm_enabled()) { - kvm_inject_x86_mce(cenv, bank, status, mcg_status, addr, misc, 0); - return; - } - - if (bank >= bank_num || !(status & MCI_STATUS_VAL)) - return; - /* * if MSR_MCG_CTL is not all 1s, the uncorrected error * reporting is disabled @@ -1598,6 +1116,45 @@ } else banks[1] |= MCI_STATUS_OVER; } + +void cpu_inject_x86_mce(CPUState *cenv, int bank, uint64_t status, + uint64_t mcg_status, uint64_t addr, uint64_t misc, + int broadcast) +{ + unsigned bank_num = cenv->mcg_cap & 0xff; + CPUState *env; + int flag = 0; + + if (bank >= bank_num || !(status & MCI_STATUS_VAL)) { + return; + } + + if (broadcast) { + if (!cpu_x86_support_mca_broadcast(cenv)) { + fprintf(stderr, "Current CPU does not support broadcast\n"); + return; + } + } + + if (kvm_enabled()) { + if (broadcast) { + flag |= MCE_BROADCAST; + } + + kvm_inject_x86_mce(cenv, bank, status, mcg_status, addr, misc, flag); + } else { + qemu_inject_x86_mce(cenv, bank, status, mcg_status, addr, misc); + if (broadcast) { + for (env = first_cpu; env != NULL; env = env->next_cpu) { + if (cenv == env) { + continue; + } + qemu_inject_x86_mce(env, 1, MCI_STATUS_VAL | MCI_STATUS_UC, + MCG_STATUS_MCIP | MCG_STATUS_RIPV, 0, 0); + } + } + } +} #endif /* !CONFIG_USER_ONLY */ static void mce_init(CPUX86State *cenv) @@ -1614,273 +1171,6 @@ } } -static void host_cpuid(uint32_t function, uint32_t count, - uint32_t *eax, uint32_t *ebx, - uint32_t *ecx, uint32_t *edx) -{ -#if defined(CONFIG_KVM) || defined(USE_KVM) - uint32_t vec[4]; - -#ifdef __x86_64__ - asm volatile("cpuid" - : "=a"(vec[0]), "=b"(vec[1]), - "=c"(vec[2]), "=d"(vec[3]) - : "0"(function), "c"(count) : "cc"); -#else - asm volatile("pusha \n\t" - "cpuid \n\t" - "mov %%eax, 0(%2) \n\t" - "mov %%ebx, 4(%2) \n\t" - "mov %%ecx, 8(%2) \n\t" - "mov %%edx, 12(%2) \n\t" - "popa" - : : "a"(function), "c"(count), "S"(vec) - : "memory", "cc"); -#endif - - if (eax) - *eax = vec[0]; - if (ebx) - *ebx = vec[1]; - if (ecx) - *ecx = vec[2]; - if (edx) - *edx = vec[3]; -#endif -} - -static void get_cpuid_vendor(CPUX86State *env, uint32_t *ebx, - uint32_t *ecx, uint32_t *edx) -{ - *ebx = env->cpuid_vendor1; - *edx = env->cpuid_vendor2; - *ecx = env->cpuid_vendor3; - - /* sysenter isn't supported on compatibility mode on AMD, syscall - * isn't supported in compatibility mode on Intel. - * Normally we advertise the actual cpu vendor, but you can override - * this if you want to use KVM's sysenter/syscall emulation - * in compatibility mode and when doing cross vendor migration - */ - if (kvm_enabled() && env->cpuid_vendor_override) { - host_cpuid(0, 0, NULL, ebx, ecx, edx); - } -} - -void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, - uint32_t *eax, uint32_t *ebx, - uint32_t *ecx, uint32_t *edx) -{ - /* test if maximum index reached */ - if (index & 0x80000000) { - if (index > env->cpuid_xlevel) - index = env->cpuid_level; - } else { - if (index > env->cpuid_level) - index = env->cpuid_level; - } - - switch(index) { - case 0: - *eax = env->cpuid_level; - get_cpuid_vendor(env, ebx, ecx, edx); - break; - case 1: - *eax = env->cpuid_version; - *ebx = (env->cpuid_apic_id << 24) | 8 << 8; /* CLFLUSH size in quad words, Linux wants it. */ - *ecx = env->cpuid_ext_features; - *edx = env->cpuid_features; - if (env->nr_cores * env->nr_threads > 1) { - *ebx |= (env->nr_cores * env->nr_threads) << 16; - *edx |= 1 << 28; /* HTT bit */ - } - break; - case 2: - /* cache info: needed for Pentium Pro compatibility */ - *eax = 1; - *ebx = 0; - *ecx = 0; - *edx = 0x2c307d; - break; - case 4: - /* cache info: needed for Core compatibility */ - if (env->nr_cores > 1) { - *eax = (env->nr_cores - 1) << 26; - } else { - *eax = 0; - } - switch (count) { - case 0: /* L1 dcache info */ - *eax |= 0x0000121; - *ebx = 0x1c0003f; - *ecx = 0x000003f; - *edx = 0x0000001; - break; - case 1: /* L1 icache info */ - *eax |= 0x0000122; - *ebx = 0x1c0003f; - *ecx = 0x000003f; - *edx = 0x0000001; - break; - case 2: /* L2 cache info */ - *eax |= 0x0000143; - if (env->nr_threads > 1) { - *eax |= (env->nr_threads - 1) << 14; - } - *ebx = 0x3c0003f; - *ecx = 0x0000fff; - *edx = 0x0000001; - break; - default: /* end of info */ - *eax = 0; - *ebx = 0; - *ecx = 0; - *edx = 0; - break; - } - break; - case 5: - /* mwait info: needed for Core compatibility */ - *eax = 0; /* Smallest monitor-line size in bytes */ - *ebx = 0; /* Largest monitor-line size in bytes */ - *ecx = CPUID_MWAIT_EMX | CPUID_MWAIT_IBE; - *edx = 0; - break; - case 6: - /* Thermal and Power Leaf */ - *eax = 0; - *ebx = 0; - *ecx = 0; - *edx = 0; - break; - case 9: - /* Direct Cache Access Information Leaf */ - *eax = 0; /* Bits 0-31 in DCA_CAP MSR */ - *ebx = 0; - *ecx = 0; - *edx = 0; - break; - case 0xA: - /* Architectural Performance Monitoring Leaf */ - *eax = 0; - *ebx = 0; - *ecx = 0; - *edx = 0; - break; - case 0x80000000: - *eax = env->cpuid_xlevel; - *ebx = env->cpuid_vendor1; - *edx = env->cpuid_vendor2; - *ecx = env->cpuid_vendor3; - break; - case 0x80000001: - *eax = env->cpuid_version; - *ebx = 0; - *ecx = env->cpuid_ext3_features; - *edx = env->cpuid_ext2_features; - - /* The Linux kernel checks for the CMPLegacy bit and - * discards multiple thread information if it is set. - * So dont set it here for Intel to make Linux guests happy. - */ - if (env->nr_cores * env->nr_threads > 1) { - uint32_t tebx, tecx, tedx; - get_cpuid_vendor(env, &tebx, &tecx, &tedx); - if (tebx != CPUID_VENDOR_INTEL_1 || - tedx != CPUID_VENDOR_INTEL_2 || - tecx != CPUID_VENDOR_INTEL_3) { - *ecx |= 1 << 1; /* CmpLegacy bit */ - } - } - - if (kvm_enabled()) { - uint32_t h_eax, h_edx; - - host_cpuid(index, 0, &h_eax, NULL, NULL, &h_edx); - - /* disable CPU features that the host does not support */ - - /* long mode */ - if ((h_edx & 0x20000000) == 0 /* || !lm_capable_kernel */) - *edx &= ~0x20000000; - /* syscall */ - if ((h_edx & 0x00000800) == 0) - *edx &= ~0x00000800; - /* nx */ - if ((h_edx & 0x00100000) == 0) - *edx &= ~0x00100000; - - /* disable CPU features that KVM cannot support */ - - /* svm */ - if (!kvm_nested) - *ecx &= ~CPUID_EXT3_SVM; - /* 3dnow */ - *edx &= ~0xc0000000; - } else { - /* AMD 3DNow! is not supported in QEMU */ - *edx &= ~(CPUID_EXT2_3DNOW | CPUID_EXT2_3DNOWEXT); - } - break; - case 0x80000002: - case 0x80000003: - case 0x80000004: - *eax = env->cpuid_model[(index - 0x80000002) * 4 + 0]; - *ebx = env->cpuid_model[(index - 0x80000002) * 4 + 1]; - *ecx = env->cpuid_model[(index - 0x80000002) * 4 + 2]; - *edx = env->cpuid_model[(index - 0x80000002) * 4 + 3]; - break; - case 0x80000005: - /* cache info (L1 cache) */ - *eax = 0x01ff01ff; - *ebx = 0x01ff01ff; - *ecx = 0x40020140; - *edx = 0x40020140; - break; - case 0x80000006: - /* cache info (L2 cache) */ - *eax = 0; - *ebx = 0x42004200; - *ecx = 0x02008140; - *edx = 0; - break; - case 0x80000008: - /* virtual & phys address size in low 2 bytes. */ -/* XXX: This value must match the one used in the MMU code. */ - if (env->cpuid_ext2_features & CPUID_EXT2_LM) { - /* 64 bit processor */ -/* XXX: The physical address space is limited to 42 bits in exec.c. */ - *eax = 0x00003028; /* 48 bits virtual, 40 bits physical */ - } else { - if (env->cpuid_features & CPUID_PSE36) - *eax = 0x00000024; /* 36 bits physical */ - else - *eax = 0x00000020; /* 32 bits physical */ - } - *ebx = 0; - *ecx = 0; - *edx = 0; - if (env->nr_cores * env->nr_threads > 1) { - *ecx |= (env->nr_cores * env->nr_threads) - 1; - } - break; - case 0x8000000A: - *eax = 0x00000001; /* SVM Revision */ - *ebx = 0x00000010; /* nr of ASIDs */ - *ecx = 0; - *edx = 0; /* optional features */ - break; - default: - /* reserved values: zero */ - *eax = 0; - *ebx = 0; - *ecx = 0; - *edx = 0; - break; - } -} - - int cpu_x86_get_descr_debug(CPUX86State *env, unsigned int selector, target_ulong *base, unsigned int *limit, unsigned int *flags) @@ -1934,6 +1224,8 @@ } mce_init(env); + qemu_init_vcpu(env); + return env; } @@ -1943,12 +1235,13 @@ int sipi = env->interrupt_request & CPU_INTERRUPT_SIPI; cpu_reset(env); env->interrupt_request = sipi; - apic_init_reset(env); + apic_init_reset(env->apic_state); + env->halted = !cpu_is_bsp(env); } void do_cpu_sipi(CPUState *env) { - apic_sipi(env); + apic_sipi(env->apic_state); } #else void do_cpu_init(CPUState *env) diff -Nru qemu-kvm-0.12.5+noroms/target-i386/kvm.c qemu-kvm-0.14.1/target-i386/kvm.c --- qemu-kvm-0.12.5+noroms/target-i386/kvm.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-i386/kvm.c 2011-05-11 13:29:46.000000000 +0000 @@ -15,6 +15,7 @@ #include #include #include +#include #include @@ -24,22 +25,50 @@ #include "cpu.h" #include "gdbstub.h" #include "host-utils.h" +#include "hw/pc.h" +#include "hw/apic.h" +#include "ioport.h" +#include "kvm_x86.h" -#ifdef KVM_UPSTREAM +#ifdef CONFIG_KVM_PARA +#include +#endif +// //#define DEBUG_KVM #ifdef DEBUG_KVM -#define dprintf(fmt, ...) \ +#define DPRINTF(fmt, ...) \ do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0) #else -#define dprintf(fmt, ...) \ +#define DPRINTF(fmt, ...) \ do { } while (0) #endif #define MSR_KVM_WALL_CLOCK 0x11 #define MSR_KVM_SYSTEM_TIME 0x12 -#ifdef KVM_CAP_EXT_CPUID +#ifndef BUS_MCEERR_AR +#define BUS_MCEERR_AR 4 +#endif +#ifndef BUS_MCEERR_AO +#define BUS_MCEERR_AO 5 +#endif + +#ifdef OBSOLETE_KVM_IMPL +const KVMCapabilityInfo kvm_arch_required_capabilities[] = { + KVM_CAP_INFO(SET_TSS_ADDR), + KVM_CAP_INFO(EXT_CPUID), + KVM_CAP_INFO(MP_STATE), + KVM_CAP_LAST_INFO +}; +#endif + +static bool has_msr_star; +static bool has_msr_hsave_pa; +#if defined(CONFIG_KVM_PARA) && defined(KVM_CAP_ASYNC_PF) +static bool has_msr_async_pf_en; +#endif +static int lm_capable_kernel; static struct kvm_cpuid2 *try_get_cpuid(KVMState *s, int max) { @@ -66,24 +95,22 @@ return cpuid; } -uint32_t kvm_arch_get_supported_cpuid(CPUState *env, uint32_t function, int reg) +uint32_t kvm_arch_get_supported_cpuid(CPUState *env, uint32_t function, + uint32_t index, int reg) { struct kvm_cpuid2 *cpuid; int i, max; uint32_t ret = 0; uint32_t cpuid_1_edx; - if (!kvm_check_extension(env->kvm_state, KVM_CAP_EXT_CPUID)) { - return -1U; - } - max = 1; while ((cpuid = try_get_cpuid(env->kvm_state, max)) == NULL) { max *= 2; } for (i = 0; i < cpuid->nent; ++i) { - if (cpuid->entries[i].function == function) { + if (cpuid->entries[i].function == function && + cpuid->entries[i].index == index) { switch (reg) { case R_EAX: ret = cpuid->entries[i].eax; @@ -96,12 +123,18 @@ break; case R_EDX: ret = cpuid->entries[i].edx; - if (function == 0x80000001) { + switch (function) { + case 1: + /* KVM before 2.6.30 misreports the following features */ + ret |= CPUID_MTRR | CPUID_PAT | CPUID_MCE | CPUID_MCA; + break; + case 0x80000001: /* On Intel, kvm returns cpuid according to the Intel spec, * so add missing bits according to the AMD spec: */ - cpuid_1_edx = kvm_arch_get_supported_cpuid(env, 1, R_EDX); - ret |= cpuid_1_edx & 0xdfeff7ff; + cpuid_1_edx = kvm_arch_get_supported_cpuid(env, 1, 0, R_EDX); + ret |= cpuid_1_edx & 0x183f7ff; + break; } break; } @@ -113,58 +146,221 @@ return ret; } -#else +#ifdef CONFIG_KVM_PARA +struct kvm_para_features { + int cap; + int feature; +} para_features[] = { + { KVM_CAP_CLOCKSOURCE, KVM_FEATURE_CLOCKSOURCE }, + { KVM_CAP_NOP_IO_DELAY, KVM_FEATURE_NOP_IO_DELAY }, + { KVM_CAP_PV_MMU, KVM_FEATURE_MMU_OP }, +#ifdef KVM_CAP_ASYNC_PF + { KVM_CAP_ASYNC_PF, KVM_FEATURE_ASYNC_PF }, +#endif + { -1, -1 } +}; -uint32_t kvm_arch_get_supported_cpuid(CPUState *env, uint32_t function, int reg) +static int get_para_features(CPUState *env) { - return -1U; -} + int i, features = 0; + for (i = 0; i < ARRAY_SIZE(para_features) - 1; i++) { + if (kvm_check_extension(env->kvm_state, para_features[i].cap)) { + features |= (1 << para_features[i].feature); + } + } +#ifdef KVM_CAP_ASYNC_PF + has_msr_async_pf_en = features & (1 << KVM_FEATURE_ASYNC_PF); +#endif + return features; +} #endif -static void kvm_trim_features(uint32_t *features, uint32_t supported) +#ifdef KVM_CAP_MCE +static int kvm_get_mce_cap_supported(KVMState *s, uint64_t *mce_cap, + int *max_banks) { - int i; - uint32_t mask; + int r; + + r = kvm_check_extension(s, KVM_CAP_MCE); + if (r > 0) { + *max_banks = r; + return kvm_ioctl(s, KVM_X86_GET_MCE_CAP_SUPPORTED, mce_cap); + } + return -ENOSYS; +} + +static int kvm_setup_mce(CPUState *env, uint64_t *mcg_cap) +{ + return kvm_vcpu_ioctl(env, KVM_X86_SETUP_MCE, mcg_cap); +} + +static int kvm_set_mce(CPUState *env, struct kvm_x86_mce *m) +{ + return kvm_vcpu_ioctl(env, KVM_X86_SET_MCE, m); +} + +static int kvm_get_msr(CPUState *env, struct kvm_msr_entry *msrs, int n) +{ + struct kvm_msrs *kmsrs = qemu_malloc(sizeof *kmsrs + n * sizeof *msrs); + int r; + + kmsrs->nmsrs = n; + memcpy(kmsrs->entries, msrs, n * sizeof *msrs); + r = kvm_vcpu_ioctl(env, KVM_GET_MSRS, kmsrs); + memcpy(msrs, kmsrs->entries, n * sizeof *msrs); + free(kmsrs); + return r; +} + +/* FIXME: kill this and kvm_get_msr, use env->mcg_status instead */ +static int kvm_mce_in_progress(CPUState *env) +{ + struct kvm_msr_entry msr_mcg_status = { + .index = MSR_MCG_STATUS, + }; + int r; + + r = kvm_get_msr(env, &msr_mcg_status, 1); + if (r == -1 || r == 0) { + fprintf(stderr, "Failed to get MCE status\n"); + return 0; + } + return !!(msr_mcg_status.data & MCG_STATUS_MCIP); +} + +struct kvm_x86_mce_data +{ + CPUState *env; + struct kvm_x86_mce *mce; + int abort_on_error; +}; - for (i = 0; i < 32; ++i) { - mask = 1U << i; - if ((*features & mask) && !(supported & mask)) { - *features &= ~mask; +static void kvm_do_inject_x86_mce(void *_data) +{ + struct kvm_x86_mce_data *data = _data; + int r; + + /* If there is an MCE exception being processed, ignore this SRAO MCE */ + if ((data->env->mcg_cap & MCG_SER_P) && + !(data->mce->status & MCI_STATUS_AR)) { + if (kvm_mce_in_progress(data->env)) { + return; } } + + r = kvm_set_mce(data->env, data->mce); + if (r < 0) { + perror("kvm_set_mce FAILED"); + if (data->abort_on_error) { + abort(); + } + } +} + +static void kvm_inject_x86_mce_on(CPUState *env, struct kvm_x86_mce *mce, + int flag) +{ + struct kvm_x86_mce_data data = { + .env = env, + .mce = mce, + .abort_on_error = (flag & ABORT_ON_ERROR), + }; + + if (!env->mcg_cap) { + fprintf(stderr, "MCE support is not enabled!\n"); + return; + } + + on_vcpu(env, kvm_do_inject_x86_mce, &data); } +static void kvm_mce_broadcast_rest(CPUState *env); +#endif + +void kvm_inject_x86_mce(CPUState *cenv, int bank, uint64_t status, + uint64_t mcg_status, uint64_t addr, uint64_t misc, + int flag) +{ +#ifdef KVM_CAP_MCE + struct kvm_x86_mce mce = { + .bank = bank, + .status = status, + .mcg_status = mcg_status, + .addr = addr, + .misc = misc, + }; + + if (flag & MCE_BROADCAST) { + kvm_mce_broadcast_rest(cenv); + } + + kvm_inject_x86_mce_on(cenv, &mce, flag); +#else + if (flag & ABORT_ON_ERROR) { + abort(); + } +#endif +} + +static int _kvm_arch_init_vcpu(CPUState *env); + int kvm_arch_init_vcpu(CPUState *env) { + int r; struct { struct kvm_cpuid2 cpuid; struct kvm_cpuid_entry2 entries[100]; } __attribute__((packed)) cpuid_data; uint32_t limit, i, j, cpuid_i; uint32_t unused; + struct kvm_cpuid_entry2 *c; +#ifdef CONFIG_KVM_PARA + uint32_t signature[3]; +#endif - env->mp_state = KVM_MP_STATE_RUNNABLE; + r = _kvm_arch_init_vcpu(env); + if (r < 0) { + return r; + } - kvm_trim_features(&env->cpuid_features, - kvm_arch_get_supported_cpuid(env, 1, R_EDX)); + env->cpuid_features &= kvm_arch_get_supported_cpuid(env, 1, 0, R_EDX); i = env->cpuid_ext_features & CPUID_EXT_HYPERVISOR; - kvm_trim_features(&env->cpuid_ext_features, - kvm_arch_get_supported_cpuid(env, 1, R_ECX)); + env->cpuid_ext_features &= kvm_arch_get_supported_cpuid(env, 1, 0, R_ECX); env->cpuid_ext_features |= i; - kvm_trim_features(&env->cpuid_ext2_features, - kvm_arch_get_supported_cpuid(env, 0x80000001, R_EDX)); - kvm_trim_features(&env->cpuid_ext3_features, - kvm_arch_get_supported_cpuid(env, 0x80000001, R_ECX)); + env->cpuid_ext2_features &= kvm_arch_get_supported_cpuid(env, 0x80000001, + 0, R_EDX); + env->cpuid_ext3_features &= kvm_arch_get_supported_cpuid(env, 0x80000001, + 0, R_ECX); + env->cpuid_svm_features &= kvm_arch_get_supported_cpuid(env, 0x8000000A, + 0, R_EDX); + cpuid_i = 0; +#ifdef CONFIG_KVM_PARA + /* Paravirtualization CPUIDs */ + memcpy(signature, "KVMKVMKVM\0\0\0", 12); + c = &cpuid_data.entries[cpuid_i++]; + memset(c, 0, sizeof(*c)); + c->function = KVM_CPUID_SIGNATURE; + c->eax = 0; + c->ebx = signature[0]; + c->ecx = signature[1]; + c->edx = signature[2]; + + c = &cpuid_data.entries[cpuid_i++]; + memset(c, 0, sizeof(*c)); + c->function = KVM_CPUID_FEATURES; + c->eax = env->cpuid_kvm_features & get_para_features(env); +#endif + cpu_x86_cpuid(env, 0, 0, &limit, &unused, &unused, &unused); for (i = 0; i <= limit; i++) { - struct kvm_cpuid_entry2 *c = &cpuid_data.entries[cpuid_i++]; + c = &cpuid_data.entries[cpuid_i++]; switch (i) { case 2: { @@ -194,13 +390,15 @@ c->index = j; cpu_x86_cpuid(env, i, j, &c->eax, &c->ebx, &c->ecx, &c->edx); - if (i == 4 && c->eax == 0) + if (i == 4 && c->eax == 0) { break; - if (i == 0xb && !(c->ecx & 0xff00)) + } + if (i == 0xb && !(c->ecx & 0xff00)) { break; - if (i == 0xd && c->eax == 0) + } + if (i == 0xd && c->eax == 0) { break; - + } c = &cpuid_data.entries[cpuid_i++]; } break; @@ -214,7 +412,7 @@ cpu_x86_cpuid(env, 0x80000000, 0, &limit, &unused, &unused, &unused); for (i = 0x80000000; i <= limit; i++) { - struct kvm_cpuid_entry2 *c = &cpuid_data.entries[cpuid_i++]; + c = &cpuid_data.entries[cpuid_i++]; c->function = i; c->flags = 0; @@ -223,36 +421,75 @@ cpuid_data.cpuid.nent = cpuid_i; +#ifdef KVM_CAP_MCE + if (((env->cpuid_version >> 8)&0xF) >= 6 + && (env->cpuid_features&(CPUID_MCE|CPUID_MCA)) == (CPUID_MCE|CPUID_MCA) + && kvm_check_extension(env->kvm_state, KVM_CAP_MCE) > 0) { + uint64_t mcg_cap; + int banks; + + if (kvm_get_mce_cap_supported(env->kvm_state, &mcg_cap, &banks)) { + perror("kvm_get_mce_cap_supported FAILED"); + } else { + if (banks > MCE_BANKS_DEF) + banks = MCE_BANKS_DEF; + mcg_cap &= MCE_CAP_DEF; + mcg_cap |= banks; + if (kvm_setup_mce(env, &mcg_cap)) { + perror("kvm_setup_mce FAILED"); + } else { + env->mcg_cap = mcg_cap; + } + } + } +#endif + return kvm_vcpu_ioctl(env, KVM_SET_CPUID2, &cpuid_data); } +static void kvm_clear_vapic(CPUState *env) +{ +#ifdef KVM_SET_VAPIC_ADDR + struct kvm_vapic_addr va = { + .vapic_addr = 0, + }; + + kvm_vcpu_ioctl(env, KVM_SET_VAPIC_ADDR, &va); #endif +} + void kvm_arch_reset_vcpu(CPUState *env) { + kvm_clear_vapic(env); env->exception_injected = -1; env->interrupt_injected = -1; - env->nmi_injected = 0; - env->nmi_pending = 0; + env->xcr0 = 1; + if (kvm_irqchip_in_kernel()) { + env->mp_state = cpu_is_bsp(env) ? KVM_MP_STATE_RUNNABLE : + KVM_MP_STATE_UNINITIALIZED; + } else { + env->mp_state = KVM_MP_STATE_RUNNABLE; + } } -#ifdef KVM_UPSTREAM -static int kvm_has_msr_star(CPUState *env) + +static int kvm_get_supported_msrs(KVMState *s) { - static int has_msr_star; - int ret; + static int kvm_supported_msrs; + int ret = 0; /* first time */ - if (has_msr_star == 0) { + if (kvm_supported_msrs == 0) { struct kvm_msr_list msr_list, *kvm_msr_list; - has_msr_star = -1; + kvm_supported_msrs = -1; /* Obtain MSR list from KVM. These are the MSRs that we must * save/restore */ msr_list.nmsrs = 0; - ret = kvm_ioctl(env->kvm_state, KVM_GET_MSR_INDEX_LIST, &msr_list); + ret = kvm_ioctl(s, KVM_GET_MSR_INDEX_LIST, &msr_list); if (ret < 0 && ret != -E2BIG) { - return 0; + return ret; } /* Old kernel modules had a bug and could write beyond the provided memory. Allocate at least a safe amount of 1K. */ @@ -261,14 +498,18 @@ sizeof(msr_list.indices[0]))); kvm_msr_list->nmsrs = msr_list.nmsrs; - ret = kvm_ioctl(env->kvm_state, KVM_GET_MSR_INDEX_LIST, kvm_msr_list); + ret = kvm_ioctl(s, KVM_GET_MSR_INDEX_LIST, kvm_msr_list); if (ret >= 0) { int i; for (i = 0; i < kvm_msr_list->nmsrs; i++) { if (kvm_msr_list->indices[i] == MSR_STAR) { - has_msr_star = 1; - break; + has_msr_star = true; + continue; + } + if (kvm_msr_list->indices[i] == MSR_VM_HSAVE_PA) { + has_msr_hsave_pa = true; + continue; } } } @@ -276,34 +517,65 @@ free(kvm_msr_list); } - if (has_msr_star == 1) - return 1; - return 0; + return ret; } -int kvm_arch_init(KVMState *s, int smp_cpus) +#ifdef OBSOLETE_KVM_IMPL + +int kvm_arch_init(KVMState *s) { + uint64_t identity_base = 0xfffbc000; int ret; + struct utsname utsname; - /* create vm86 tss. KVM uses vm86 mode to emulate 16-bit code - * directly. In order to use vm86 mode, a TSS is needed. Since this - * must be part of guest physical memory, we need to allocate it. Older - * versions of KVM just assumed that it would be at the end of physical - * memory but that doesn't work with more than 4GB of memory. We simply - * refuse to work with those older versions of KVM. */ - ret = kvm_ioctl(s, KVM_CHECK_EXTENSION, KVM_CAP_SET_TSS_ADDR); - if (ret <= 0) { - fprintf(stderr, "kvm does not support KVM_CAP_SET_TSS_ADDR\n"); + ret = kvm_get_supported_msrs(s); + if (ret < 0) { return ret; } - /* this address is 3 pages before the bios, and the bios should present - * as unavaible memory. FIXME, need to ensure the e820 map deals with - * this? + uname(&utsname); + lm_capable_kernel = strcmp(utsname.machine, "x86_64") == 0; + + /* + * On older Intel CPUs, KVM uses vm86 mode to emulate 16-bit code directly. + * In order to use vm86 mode, an EPT identity map and a TSS are needed. + * Since these must be part of guest physical memory, we need to allocate + * them, both by setting their start addresses in the kernel and by + * creating a corresponding e820 entry. We need 4 pages before the BIOS. + * + * Older KVM versions may not support setting the identity map base. In + * that case we need to stick with the default, i.e. a 256K maximum BIOS + * size. */ - return kvm_vm_ioctl(s, KVM_SET_TSS_ADDR, 0xfffbd000); +#ifdef KVM_CAP_SET_IDENTITY_MAP_ADDR + if (kvm_check_extension(s, KVM_CAP_SET_IDENTITY_MAP_ADDR)) { + /* Allows up to 16M BIOSes. */ + identity_base = 0xfeffc000; + + ret = kvm_vm_ioctl(s, KVM_SET_IDENTITY_MAP_ADDR, &identity_base); + if (ret < 0) { + return ret; + } + } +#endif + /* Set TSS base one page after EPT identity map. */ + ret = kvm_vm_ioctl(s, KVM_SET_TSS_ADDR, identity_base + 0x1000); + if (ret < 0) { + return ret; + } + + /* Tell fw_cfg to notify the BIOS to reserve the range. */ + ret = e820_add_entry(identity_base, 0x4000, E820_RESERVED); + if (ret < 0) { + fprintf(stderr, "e820_add_entry() table is full\n"); + return ret; + } + + return 0; } - + +#endif + static void set_v8086_seg(struct kvm_segment *lhs, const SegmentCache *rhs) { lhs->selector = rhs->selector; @@ -328,7 +600,7 @@ lhs->limit = rhs->limit; lhs->type = (flags >> DESC_TYPE_SHIFT) & 15; lhs->present = (flags & DESC_P_MASK) != 0; - lhs->dpl = rhs->selector & 3; + lhs->dpl = (flags >> DESC_DPL_SHIFT) & 3; lhs->db = (flags >> DESC_B_SHIFT) & 1; lhs->s = (flags & DESC_S_MASK) != 0; lhs->l = (flags >> DESC_L_SHIFT) & 1; @@ -342,23 +614,24 @@ lhs->selector = rhs->selector; lhs->base = rhs->base; lhs->limit = rhs->limit; - lhs->flags = - (rhs->type << DESC_TYPE_SHIFT) - | (rhs->present * DESC_P_MASK) - | (rhs->dpl << DESC_DPL_SHIFT) - | (rhs->db << DESC_B_SHIFT) - | (rhs->s * DESC_S_MASK) - | (rhs->l << DESC_L_SHIFT) - | (rhs->g * DESC_G_MASK) - | (rhs->avl * DESC_AVL_MASK); + lhs->flags = (rhs->type << DESC_TYPE_SHIFT) | + (rhs->present * DESC_P_MASK) | + (rhs->dpl << DESC_DPL_SHIFT) | + (rhs->db << DESC_B_SHIFT) | + (rhs->s * DESC_S_MASK) | + (rhs->l << DESC_L_SHIFT) | + (rhs->g * DESC_G_MASK) | + (rhs->avl * DESC_AVL_MASK); } + static void kvm_getput_reg(__u64 *kvm_reg, target_ulong *qemu_reg, int set) { - if (set) + if (set) { *kvm_reg = *qemu_reg; - else + } else { *qemu_reg = *kvm_reg; + } } static int kvm_getput_regs(CPUState *env, int set) @@ -368,8 +641,9 @@ if (!set) { ret = kvm_vcpu_ioctl(env, KVM_GET_REGS, ®s); - if (ret < 0) + if (ret < 0) { return ret; + } } kvm_getput_reg(®s.rax, &env->regs[R_EAX], set); @@ -394,8 +668,9 @@ kvm_getput_reg(®s.rflags, &env->eflags, set); kvm_getput_reg(®s.rip, &env->eip, set); - if (set) + if (set) { ret = kvm_vcpu_ioctl(env, KVM_SET_REGS, ®s); + } return ret; } @@ -409,8 +684,9 @@ fpu.fsw = env->fpus & ~(7 << 11); fpu.fsw |= (env->fpstt & 7) << 11; fpu.fcw = env->fpuc; - for (i = 0; i < 8; ++i) - fpu.ftwx |= (!env->fptags[i]) << i; + for (i = 0; i < 8; ++i) { + fpu.ftwx |= (!env->fptags[i]) << i; + } memcpy(fpu.fpr, env->fpregs, sizeof env->fpregs); memcpy(fpu.xmm, env->xmm_regs, sizeof env->xmm_regs); fpu.mxcsr = env->mxcsr; @@ -418,6 +694,73 @@ return kvm_vcpu_ioctl(env, KVM_SET_FPU, &fpu); } +#ifdef KVM_CAP_XSAVE +#define XSAVE_CWD_RIP 2 +#define XSAVE_CWD_RDP 4 +#define XSAVE_MXCSR 6 +#define XSAVE_ST_SPACE 8 +#define XSAVE_XMM_SPACE 40 +#define XSAVE_XSTATE_BV 128 +#define XSAVE_YMMH_SPACE 144 +#endif + +static int kvm_put_xsave(CPUState *env) +{ +#ifdef KVM_CAP_XSAVE + int i, r; + struct kvm_xsave* xsave; + uint16_t cwd, swd, twd, fop; + + if (!kvm_has_xsave()) { + return kvm_put_fpu(env); + } + + xsave = qemu_memalign(4096, sizeof(struct kvm_xsave)); + memset(xsave, 0, sizeof(struct kvm_xsave)); + cwd = swd = twd = fop = 0; + swd = env->fpus & ~(7 << 11); + swd |= (env->fpstt & 7) << 11; + cwd = env->fpuc; + for (i = 0; i < 8; ++i) { + twd |= (!env->fptags[i]) << i; + } + xsave->region[0] = (uint32_t)(swd << 16) + cwd; + xsave->region[1] = (uint32_t)(fop << 16) + twd; + memcpy(&xsave->region[XSAVE_ST_SPACE], env->fpregs, + sizeof env->fpregs); + memcpy(&xsave->region[XSAVE_XMM_SPACE], env->xmm_regs, + sizeof env->xmm_regs); + xsave->region[XSAVE_MXCSR] = env->mxcsr; + *(uint64_t *)&xsave->region[XSAVE_XSTATE_BV] = env->xstate_bv; + memcpy(&xsave->region[XSAVE_YMMH_SPACE], env->ymmh_regs, + sizeof env->ymmh_regs); + r = kvm_vcpu_ioctl(env, KVM_SET_XSAVE, xsave); + qemu_free(xsave); + return r; +#else + return kvm_put_fpu(env); +#endif +} + +static int kvm_put_xcrs(CPUState *env) +{ +#ifdef KVM_CAP_XCRS + struct kvm_xcrs xcrs; + + if (!kvm_has_xcrs()) { + return 0; + } + + xcrs.nr_xcrs = 1; + xcrs.flags = 0; + xcrs.xcrs[0].xcr = 0; + xcrs.xcrs[0].value = env->xcr0; + return kvm_vcpu_ioctl(env, KVM_SET_XCRS, &xcrs); +#else + return 0; +#endif +} + static int kvm_put_sregs(CPUState *env) { struct kvm_sregs sregs; @@ -429,26 +772,19 @@ } if ((env->eflags & VM_MASK)) { - set_v8086_seg(&sregs.cs, &env->segs[R_CS]); - set_v8086_seg(&sregs.ds, &env->segs[R_DS]); - set_v8086_seg(&sregs.es, &env->segs[R_ES]); - set_v8086_seg(&sregs.fs, &env->segs[R_FS]); - set_v8086_seg(&sregs.gs, &env->segs[R_GS]); - set_v8086_seg(&sregs.ss, &env->segs[R_SS]); + set_v8086_seg(&sregs.cs, &env->segs[R_CS]); + set_v8086_seg(&sregs.ds, &env->segs[R_DS]); + set_v8086_seg(&sregs.es, &env->segs[R_ES]); + set_v8086_seg(&sregs.fs, &env->segs[R_FS]); + set_v8086_seg(&sregs.gs, &env->segs[R_GS]); + set_v8086_seg(&sregs.ss, &env->segs[R_SS]); } else { - set_seg(&sregs.cs, &env->segs[R_CS]); - set_seg(&sregs.ds, &env->segs[R_DS]); - set_seg(&sregs.es, &env->segs[R_ES]); - set_seg(&sregs.fs, &env->segs[R_FS]); - set_seg(&sregs.gs, &env->segs[R_GS]); - set_seg(&sregs.ss, &env->segs[R_SS]); - - if (env->cr[0] & CR0_PE_MASK) { - /* force ss cpl to cs cpl */ - sregs.ss.selector = (sregs.ss.selector & ~3) | - (sregs.cs.selector & 3); - sregs.ss.dpl = sregs.ss.selector & 3; - } + set_seg(&sregs.cs, &env->segs[R_CS]); + set_seg(&sregs.ds, &env->segs[R_DS]); + set_seg(&sregs.es, &env->segs[R_ES]); + set_seg(&sregs.fs, &env->segs[R_FS]); + set_seg(&sregs.gs, &env->segs[R_GS]); + set_seg(&sregs.ss, &env->segs[R_SS]); } set_seg(&sregs.tr, &env->tr); @@ -464,8 +800,8 @@ sregs.cr3 = env->cr[3]; sregs.cr4 = env->cr[4]; - sregs.cr8 = cpu_get_apic_tpr(env); - sregs.apic_base = cpu_get_apic_base(env); + sregs.cr8 = cpu_get_apic_tpr(env->apic_state); + sregs.apic_base = cpu_get_apic_base(env->apic_state); sregs.efer = env->efer; @@ -479,7 +815,7 @@ entry->data = value; } -static int kvm_put_msrs(CPUState *env) +static int kvm_put_msrs(CPUState *env, int level) { struct { struct kvm_msrs info; @@ -491,19 +827,62 @@ kvm_msr_entry_set(&msrs[n++], MSR_IA32_SYSENTER_CS, env->sysenter_cs); kvm_msr_entry_set(&msrs[n++], MSR_IA32_SYSENTER_ESP, env->sysenter_esp); kvm_msr_entry_set(&msrs[n++], MSR_IA32_SYSENTER_EIP, env->sysenter_eip); - if (kvm_has_msr_star(env)) - kvm_msr_entry_set(&msrs[n++], MSR_STAR, env->star); - kvm_msr_entry_set(&msrs[n++], MSR_IA32_TSC, env->tsc); - kvm_msr_entry_set(&msrs[n++], MSR_VM_HSAVE_PA, env->vm_hsave); + if (has_msr_star) { + kvm_msr_entry_set(&msrs[n++], MSR_STAR, env->star); + } + if (has_msr_hsave_pa) { + kvm_msr_entry_set(&msrs[n++], MSR_VM_HSAVE_PA, env->vm_hsave); + } #ifdef TARGET_X86_64 - /* FIXME if lm capable */ - kvm_msr_entry_set(&msrs[n++], MSR_CSTAR, env->cstar); - kvm_msr_entry_set(&msrs[n++], MSR_KERNELGSBASE, env->kernelgsbase); - kvm_msr_entry_set(&msrs[n++], MSR_FMASK, env->fmask); - kvm_msr_entry_set(&msrs[n++], MSR_LSTAR, env->lstar); + if (lm_capable_kernel) { + kvm_msr_entry_set(&msrs[n++], MSR_CSTAR, env->cstar); + kvm_msr_entry_set(&msrs[n++], MSR_KERNELGSBASE, env->kernelgsbase); + kvm_msr_entry_set(&msrs[n++], MSR_FMASK, env->fmask); + kvm_msr_entry_set(&msrs[n++], MSR_LSTAR, env->lstar); + } +#endif + if (level == KVM_PUT_FULL_STATE) { + /* + * KVM is yet unable to synchronize TSC values of multiple VCPUs on + * writeback. Until this is fixed, we only write the offset to SMP + * guests after migration, desynchronizing the VCPUs, but avoiding + * huge jump-backs that would occur without any writeback at all. + */ + if (smp_cpus == 1 || env->tsc != 0) { + kvm_msr_entry_set(&msrs[n++], MSR_IA32_TSC, env->tsc); + } + } + /* + * The following paravirtual MSRs have side effects on the guest or are + * too heavy for normal writeback. Limit them to reset or full state + * updates. + */ + if (level >= KVM_PUT_RESET_STATE) { + kvm_msr_entry_set(&msrs[n++], MSR_KVM_SYSTEM_TIME, + env->system_time_msr); + kvm_msr_entry_set(&msrs[n++], MSR_KVM_WALL_CLOCK, env->wall_clock_msr); +#if defined(CONFIG_KVM_PARA) && defined(KVM_CAP_ASYNC_PF) + if (has_msr_async_pf_en) { + kvm_msr_entry_set(&msrs[n++], MSR_KVM_ASYNC_PF_EN, + env->async_pf_en_msr); + } +#endif + } +#ifdef KVM_CAP_MCE + if (env->mcg_cap) { + int i; + + if (level == KVM_PUT_RESET_STATE) { + kvm_msr_entry_set(&msrs[n++], MSR_MCG_STATUS, env->mcg_status); + } else if (level == KVM_PUT_FULL_STATE) { + kvm_msr_entry_set(&msrs[n++], MSR_MCG_STATUS, env->mcg_status); + kvm_msr_entry_set(&msrs[n++], MSR_MCG_CTL, env->mcg_ctl); + for (i = 0; i < (env->mcg_cap & 0xff) * 4; i++) { + kvm_msr_entry_set(&msrs[n++], MSR_MC0_CTL + i, env->mce_banks[i]); + } + } + } #endif - kvm_msr_entry_set(&msrs[n++], MSR_KVM_SYSTEM_TIME, env->system_time_msr); - kvm_msr_entry_set(&msrs[n++], MSR_KVM_WALL_CLOCK, env->wall_clock_msr); msr_data.info.nmsrs = n; @@ -511,21 +890,22 @@ } - static int kvm_get_fpu(CPUState *env) { struct kvm_fpu fpu; int i, ret; ret = kvm_vcpu_ioctl(env, KVM_GET_FPU, &fpu); - if (ret < 0) + if (ret < 0) { return ret; + } env->fpstt = (fpu.fsw >> 11) & 7; env->fpus = fpu.fsw; env->fpuc = fpu.fcw; - for (i = 0; i < 8; ++i) - env->fptags[i] = !((fpu.ftwx >> i) & 1); + for (i = 0; i < 8; ++i) { + env->fptags[i] = !((fpu.ftwx >> i) & 1); + } memcpy(env->fpregs, fpu.fpr, sizeof env->fpregs); memcpy(env->xmm_regs, fpu.xmm, sizeof env->xmm_regs); env->mxcsr = fpu.mxcsr; @@ -533,6 +913,77 @@ return 0; } +static int kvm_get_xsave(CPUState *env) +{ +#ifdef KVM_CAP_XSAVE + struct kvm_xsave* xsave; + int ret, i; + uint16_t cwd, swd, twd, fop; + + if (!kvm_has_xsave()) { + return kvm_get_fpu(env); + } + + xsave = qemu_memalign(4096, sizeof(struct kvm_xsave)); + ret = kvm_vcpu_ioctl(env, KVM_GET_XSAVE, xsave); + if (ret < 0) { + qemu_free(xsave); + return ret; + } + + cwd = (uint16_t)xsave->region[0]; + swd = (uint16_t)(xsave->region[0] >> 16); + twd = (uint16_t)xsave->region[1]; + fop = (uint16_t)(xsave->region[1] >> 16); + env->fpstt = (swd >> 11) & 7; + env->fpus = swd; + env->fpuc = cwd; + for (i = 0; i < 8; ++i) { + env->fptags[i] = !((twd >> i) & 1); + } + env->mxcsr = xsave->region[XSAVE_MXCSR]; + memcpy(env->fpregs, &xsave->region[XSAVE_ST_SPACE], + sizeof env->fpregs); + memcpy(env->xmm_regs, &xsave->region[XSAVE_XMM_SPACE], + sizeof env->xmm_regs); + env->xstate_bv = *(uint64_t *)&xsave->region[XSAVE_XSTATE_BV]; + memcpy(env->ymmh_regs, &xsave->region[XSAVE_YMMH_SPACE], + sizeof env->ymmh_regs); + qemu_free(xsave); + return 0; +#else + return kvm_get_fpu(env); +#endif +} + +static int kvm_get_xcrs(CPUState *env) +{ +#ifdef KVM_CAP_XCRS + int i, ret; + struct kvm_xcrs xcrs; + + if (!kvm_has_xcrs()) { + return 0; + } + + ret = kvm_vcpu_ioctl(env, KVM_GET_XCRS, &xcrs); + if (ret < 0) { + return ret; + } + + for (i = 0; i < xcrs.nr_xcrs; i++) { + /* Only support xcr0 now */ + if (xcrs.xcrs[0].xcr == 0) { + env->xcr0 = xcrs.xcrs[0].value; + break; + } + } + return 0; +#else + return 0; +#endif +} + static int kvm_get_sregs(CPUState *env) { struct kvm_sregs sregs; @@ -540,8 +991,9 @@ int bit, i, ret; ret = kvm_vcpu_ioctl(env, KVM_GET_SREGS, &sregs); - if (ret < 0) + if (ret < 0) { return ret; + } /* There can only be one pending IRQ set in the bitmap at a time, so try to find it and save its number instead (-1 for none). */ @@ -574,26 +1026,24 @@ env->cr[3] = sregs.cr3; env->cr[4] = sregs.cr4; - cpu_set_apic_base(env, sregs.apic_base); + cpu_set_apic_base(env->apic_state, sregs.apic_base); env->efer = sregs.efer; - //cpu_set_apic_tpr(env, sregs.cr8); - -#define HFLAG_COPY_MASK ~( \ - HF_CPL_MASK | HF_PE_MASK | HF_MP_MASK | HF_EM_MASK | \ - HF_TS_MASK | HF_TF_MASK | HF_VM_MASK | HF_IOPL_MASK | \ - HF_OSFXSR_MASK | HF_LMA_MASK | HF_CS32_MASK | \ - HF_SS32_MASK | HF_CS64_MASK | HF_ADDSEG_MASK) - + //cpu_set_apic_tpr(env->apic_state, sregs.cr8); +#define HFLAG_COPY_MASK \ + ~( HF_CPL_MASK | HF_PE_MASK | HF_MP_MASK | HF_EM_MASK | \ + HF_TS_MASK | HF_TF_MASK | HF_VM_MASK | HF_IOPL_MASK | \ + HF_OSFXSR_MASK | HF_LMA_MASK | HF_CS32_MASK | \ + HF_SS32_MASK | HF_CS64_MASK | HF_ADDSEG_MASK) hflags = (env->segs[R_CS].flags >> DESC_DPL_SHIFT) & HF_CPL_MASK; hflags |= (env->cr[0] & CR0_PE_MASK) << (HF_PE_SHIFT - CR0_PE_SHIFT); hflags |= (env->cr[0] << (HF_MP_SHIFT - CR0_MP_SHIFT)) & - (HF_MP_MASK | HF_EM_MASK | HF_TS_MASK); + (HF_MP_MASK | HF_EM_MASK | HF_TS_MASK); hflags |= (env->eflags & (HF_TF_MASK | HF_VM_MASK | HF_IOPL_MASK)); hflags |= (env->cr[4] & CR4_OSFXSR_MASK) << - (HF_OSFXSR_SHIFT - CR4_OSFXSR_SHIFT); + (HF_OSFXSR_SHIFT - CR4_OSFXSR_SHIFT); if (env->efer & MSR_EFER_LMA) { hflags |= HF_LMA_MASK; @@ -603,19 +1053,16 @@ hflags |= HF_CS32_MASK | HF_SS32_MASK | HF_CS64_MASK; } else { hflags |= (env->segs[R_CS].flags & DESC_B_MASK) >> - (DESC_B_SHIFT - HF_CS32_SHIFT); + (DESC_B_SHIFT - HF_CS32_SHIFT); hflags |= (env->segs[R_SS].flags & DESC_B_MASK) >> - (DESC_B_SHIFT - HF_SS32_SHIFT); - if (!(env->cr[0] & CR0_PE_MASK) || - (env->eflags & VM_MASK) || - !(hflags & HF_CS32_MASK)) { - hflags |= HF_ADDSEG_MASK; - } else { - hflags |= ((env->segs[R_DS].base | - env->segs[R_ES].base | - env->segs[R_SS].base) != 0) << - HF_ADDSEG_SHIFT; - } + (DESC_B_SHIFT - HF_SS32_SHIFT); + if (!(env->cr[0] & CR0_PE_MASK) || (env->eflags & VM_MASK) || + !(hflags & HF_CS32_MASK)) { + hflags |= HF_ADDSEG_MASK; + } else { + hflags |= ((env->segs[R_DS].base | env->segs[R_ES].base | + env->segs[R_SS].base) != 0) << HF_ADDSEG_SHIFT; + } } env->hflags = (env->hflags & HFLAG_COPY_MASK) | hflags; @@ -635,24 +1082,44 @@ msrs[n++].index = MSR_IA32_SYSENTER_CS; msrs[n++].index = MSR_IA32_SYSENTER_ESP; msrs[n++].index = MSR_IA32_SYSENTER_EIP; - if (kvm_has_msr_star(env)) - msrs[n++].index = MSR_STAR; + if (has_msr_star) { + msrs[n++].index = MSR_STAR; + } + if (has_msr_hsave_pa) { + msrs[n++].index = MSR_VM_HSAVE_PA; + } msrs[n++].index = MSR_IA32_TSC; - msrs[n++].index = MSR_VM_HSAVE_PA; #ifdef TARGET_X86_64 - /* FIXME lm_capable_kernel */ - msrs[n++].index = MSR_CSTAR; - msrs[n++].index = MSR_KERNELGSBASE; - msrs[n++].index = MSR_FMASK; - msrs[n++].index = MSR_LSTAR; + if (lm_capable_kernel) { + msrs[n++].index = MSR_CSTAR; + msrs[n++].index = MSR_KERNELGSBASE; + msrs[n++].index = MSR_FMASK; + msrs[n++].index = MSR_LSTAR; + } #endif msrs[n++].index = MSR_KVM_SYSTEM_TIME; msrs[n++].index = MSR_KVM_WALL_CLOCK; +#if defined(CONFIG_KVM_PARA) && defined(KVM_CAP_ASYNC_PF) + if (has_msr_async_pf_en) { + msrs[n++].index = MSR_KVM_ASYNC_PF_EN; + } +#endif + +#ifdef KVM_CAP_MCE + if (env->mcg_cap) { + msrs[n++].index = MSR_MCG_STATUS; + msrs[n++].index = MSR_MCG_CTL; + for (i = 0; i < (env->mcg_cap & 0xff) * 4; i++) { + msrs[n++].index = MSR_MC0_CTL + i; + } + } +#endif msr_data.info.nmsrs = n; ret = kvm_vcpu_ioctl(env, KVM_GET_MSRS, &msr_data); - if (ret < 0) + if (ret < 0) { return ret; + } for (i = 0; i < ret; i++) { switch (msrs[i].index) { @@ -685,21 +1152,43 @@ case MSR_IA32_TSC: env->tsc = msrs[i].data; break; + case MSR_VM_HSAVE_PA: + env->vm_hsave = msrs[i].data; + break; case MSR_KVM_SYSTEM_TIME: env->system_time_msr = msrs[i].data; break; case MSR_KVM_WALL_CLOCK: env->wall_clock_msr = msrs[i].data; break; - case MSR_VM_HSAVE_PA: - env->vm_hsave = msrs[i].data; +#ifdef KVM_CAP_MCE + case MSR_MCG_STATUS: + env->mcg_status = msrs[i].data; + break; + case MSR_MCG_CTL: + env->mcg_ctl = msrs[i].data; + break; +#endif + default: +#ifdef KVM_CAP_MCE + if (msrs[i].index >= MSR_MC0_CTL && + msrs[i].index < MSR_MC0_CTL + (env->mcg_cap & 0xff) * 4) { + env->mce_banks[msrs[i].index - MSR_MC0_CTL] = msrs[i].data; + } +#endif break; +#if defined(CONFIG_KVM_PARA) && defined(KVM_CAP_ASYNC_PF) + case MSR_KVM_ASYNC_PF_EN: + env->async_pf_en_msr = msrs[i].data; + break; +#endif } } return 0; } +#ifdef OBSOLETE_KVM_IMPL static int kvm_put_mp_state(CPUState *env) { struct kvm_mp_state mp_state = { .mp_state = env->mp_state }; @@ -717,11 +1206,14 @@ return ret; } env->mp_state = mp_state.mp_state; + if (kvm_irqchip_in_kernel()) { + env->halted = (mp_state.mp_state == KVM_MP_STATE_HALTED); + } return 0; } #endif -int kvm_put_vcpu_events(CPUState *env) +static int kvm_put_vcpu_events(CPUState *env, int level) { #ifdef KVM_CAP_VCPU_EVENTS struct kvm_vcpu_events events; @@ -745,8 +1237,11 @@ events.sipi_vector = env->sipi_vector; - events.flags = - KVM_VCPUEVENT_VALID_NMI_PENDING | KVM_VCPUEVENT_VALID_SIPI_VECTOR; + events.flags = 0; + if (level >= KVM_PUT_RESET_STATE) { + events.flags |= + KVM_VCPUEVENT_VALID_NMI_PENDING | KVM_VCPUEVENT_VALID_SIPI_VECTOR; + } return kvm_vcpu_ioctl(env, KVM_SET_VCPU_EVENTS, &events); #else @@ -754,7 +1249,7 @@ #endif } -int kvm_get_vcpu_events(CPUState *env) +static int kvm_get_vcpu_events(CPUState *env) { #ifdef KVM_CAP_VCPU_EVENTS struct kvm_vcpu_events events; @@ -791,35 +1286,130 @@ return 0; } -#ifdef KVM_UPSTREAM -int kvm_arch_put_registers(CPUState *env) +static int kvm_guest_debug_workarounds(CPUState *env) +{ + int ret = 0; +#ifdef KVM_CAP_SET_GUEST_DEBUG + unsigned long reinject_trap = 0; + + if (!kvm_has_vcpu_events()) { + if (env->exception_injected == 1) { + reinject_trap = KVM_GUESTDBG_INJECT_DB; + } else if (env->exception_injected == 3) { + reinject_trap = KVM_GUESTDBG_INJECT_BP; + } + env->exception_injected = -1; + } + + /* + * Kernels before KVM_CAP_X86_ROBUST_SINGLESTEP overwrote flags.TF + * injected via SET_GUEST_DEBUG while updating GP regs. Work around this + * by updating the debug state once again if single-stepping is on. + * Another reason to call kvm_update_guest_debug here is a pending debug + * trap raise by the guest. On kernels without SET_VCPU_EVENTS we have to + * reinject them via SET_GUEST_DEBUG. + */ + if (reinject_trap || + (!kvm_has_robust_singlestep() && env->singlestep_enabled)) { + ret = kvm_update_guest_debug(env, reinject_trap); + } +#endif /* KVM_CAP_SET_GUEST_DEBUG */ + return ret; +} + +static int kvm_put_debugregs(CPUState *env) +{ +#ifdef KVM_CAP_DEBUGREGS + struct kvm_debugregs dbgregs; + int i; + + if (!kvm_has_debugregs()) { + return 0; + } + + for (i = 0; i < 4; i++) { + dbgregs.db[i] = env->dr[i]; + } + dbgregs.dr6 = env->dr[6]; + dbgregs.dr7 = env->dr[7]; + dbgregs.flags = 0; + + return kvm_vcpu_ioctl(env, KVM_SET_DEBUGREGS, &dbgregs); +#else + return 0; +#endif +} + +static int kvm_get_debugregs(CPUState *env) +{ +#ifdef KVM_CAP_DEBUGREGS + struct kvm_debugregs dbgregs; + int i, ret; + + if (!kvm_has_debugregs()) { + return 0; + } + + ret = kvm_vcpu_ioctl(env, KVM_GET_DEBUGREGS, &dbgregs); + if (ret < 0) { + return ret; + } + for (i = 0; i < 4; i++) { + env->dr[i] = dbgregs.db[i]; + } + env->dr[4] = env->dr[6] = dbgregs.dr6; + env->dr[5] = env->dr[7] = dbgregs.dr7; +#endif + + return 0; +} + +#ifdef OBSOLETE_KVM_IMPL +int kvm_arch_put_registers(CPUState *env, int level) { int ret; + assert(cpu_is_stopped(env) || qemu_cpu_self(env)); + ret = kvm_getput_regs(env, 1); - if (ret < 0) + if (ret < 0) { return ret; - - ret = kvm_put_fpu(env); - if (ret < 0) + } + ret = kvm_put_xsave(env); + if (ret < 0) { return ret; - + } + ret = kvm_put_xcrs(env); + if (ret < 0) { + return ret; + } ret = kvm_put_sregs(env); - if (ret < 0) + if (ret < 0) { return ret; - - ret = kvm_put_msrs(env); - if (ret < 0) + } + ret = kvm_put_msrs(env, level); + if (ret < 0) { return ret; - - ret = kvm_put_mp_state(env); - if (ret < 0) + } + if (level >= KVM_PUT_RESET_STATE) { + ret = kvm_put_mp_state(env); + if (ret < 0) { + return ret; + } + } + ret = kvm_put_vcpu_events(env, level); + if (ret < 0) { return ret; - - ret = kvm_put_vcpu_events(env); - if (ret < 0) + } + ret = kvm_put_debugregs(env); + if (ret < 0) { return ret; - + } + /* must be last */ + ret = kvm_guest_debug_workarounds(env); + if (ret < 0) { + return ret; + } return 0; } @@ -827,35 +1417,52 @@ { int ret; + assert(cpu_is_stopped(env) || qemu_cpu_self(env)); + ret = kvm_getput_regs(env, 0); - if (ret < 0) + if (ret < 0) { return ret; - - ret = kvm_get_fpu(env); - if (ret < 0) + } + ret = kvm_get_xsave(env); + if (ret < 0) { return ret; - + } + ret = kvm_get_xcrs(env); + if (ret < 0) { + return ret; + } ret = kvm_get_sregs(env); - if (ret < 0) + if (ret < 0) { return ret; - + } ret = kvm_get_msrs(env); - if (ret < 0) + if (ret < 0) { return ret; - + } ret = kvm_get_mp_state(env); - if (ret < 0) + if (ret < 0) { return ret; - + } ret = kvm_get_vcpu_events(env); - if (ret < 0) + if (ret < 0) { return ret; - + } + ret = kvm_get_debugregs(env); + if (ret < 0) { + return ret; + } return 0; } int kvm_arch_pre_run(CPUState *env, struct kvm_run *run) { + /* Inject NMI */ + if (env->interrupt_request & CPU_INTERRUPT_NMI) { + env->interrupt_request &= ~CPU_INTERRUPT_NMI; + DPRINTF("injected NMI\n"); + kvm_vcpu_ioctl(env, KVM_NMI); + } + /* Try to inject an interrupt if the guest can accept it */ if (run->ready_for_interrupt_injection && (env->interrupt_request & CPU_INTERRUPT_HARD) && @@ -868,7 +1475,7 @@ struct kvm_interrupt intr; intr.irq = irq; /* FIXME: errors */ - dprintf("injected interrupt %d\n", irq); + DPRINTF("injected interrupt %d\n", irq); kvm_vcpu_ioctl(env, KVM_INTERRUPT, &intr); } } @@ -877,13 +1484,14 @@ * interrupt, request an interrupt window exit. This will * cause a return to userspace as soon as the guest is ready to * receive interrupts. */ - if ((env->interrupt_request & CPU_INTERRUPT_HARD)) + if ((env->interrupt_request & CPU_INTERRUPT_HARD)) { run->request_interrupt_window = 1; - else + } else { run->request_interrupt_window = 0; + } - dprintf("setting tpr\n"); - run->cr8 = cpu_get_apic_tpr(env); + DPRINTF("setting tpr\n"); + run->cr8 = cpu_get_apic_tpr(env->apic_state); return 0; } @@ -891,18 +1499,35 @@ int kvm_arch_post_run(CPUState *env, struct kvm_run *run) { - if (run->if_flag) + if (run->if_flag) { env->eflags |= IF_MASK; - else + } else { env->eflags &= ~IF_MASK; - - cpu_set_apic_tpr(env, run->cr8); - cpu_set_apic_base(env, run->apic_base); + } + cpu_set_apic_tpr(env->apic_state, run->cr8); + cpu_set_apic_base(env->apic_state, run->apic_base); return 0; } -#ifdef KVM_UPSTREAM +#ifdef OBSOLETE_KVM_IMPL + +int kvm_arch_process_irqchip_events(CPUState *env) +{ + if (env->interrupt_request & CPU_INTERRUPT_INIT) { + kvm_cpu_synchronize_state(env); + do_cpu_init(env); + env->exception_index = EXCP_HALTED; + } + + if (env->interrupt_request & CPU_INTERRUPT_SIPI) { + kvm_cpu_synchronize_state(env); + do_cpu_sipi(env); + } + + return env->halted; +} + static int kvm_handle_halt(CPUState *env) { if (!((env->interrupt_request & CPU_INTERRUPT_HARD) && @@ -916,19 +1541,60 @@ return 1; } +static bool host_supports_vmx(void) +{ + uint32_t ecx, unused; + + host_cpuid(1, 0, &unused, &unused, &ecx, &unused); + return ecx & CPUID_EXT_VMX; +} + +#define VMX_INVALID_GUEST_STATE 0x80000021 + int kvm_arch_handle_exit(CPUState *env, struct kvm_run *run) { + uint64_t code; int ret = 0; switch (run->exit_reason) { case KVM_EXIT_HLT: - dprintf("handle_hlt\n"); + DPRINTF("handle_hlt\n"); ret = kvm_handle_halt(env); break; + case KVM_EXIT_SET_TPR: + ret = 1; + break; + case KVM_EXIT_FAIL_ENTRY: + code = run->fail_entry.hardware_entry_failure_reason; + fprintf(stderr, "KVM: entry failed, hardware error 0x%" PRIx64 "\n", + code); + if (host_supports_vmx() && code == VMX_INVALID_GUEST_STATE) { + fprintf(stderr, + "\nIf you're runnning a guest on an Intel machine without " + "unrestricted mode\n" + "support, the failure can be most likely due to the guest " + "entering an invalid\n" + "state for Intel VT. For example, the guest maybe running " + "in big real mode\n" + "which is not supported on less recent Intel processors." + "\n\n"); + } + ret = -1; + break; + case KVM_EXIT_EXCEPTION: + fprintf(stderr, "KVM: exception %d exit (error code 0x%x)\n", + run->ex.exception, run->ex.error_code); + ret = -1; + break; + default: + fprintf(stderr, "KVM: unknown exit reason %d\n", run->exit_reason); + ret = -1; + break; } return ret; } +#endif #ifdef KVM_CAP_SET_GUEST_DEBUG int kvm_arch_insert_sw_breakpoint(CPUState *env, struct kvm_sw_breakpoint *bp) @@ -936,8 +1602,9 @@ static const uint8_t int3 = 0xcc; if (cpu_memory_rw_debug(env, bp->pc, (uint8_t *)&bp->saved_insn, 1, 0) || - cpu_memory_rw_debug(env, bp->pc, (uint8_t *)&int3, 1, 1)) + cpu_memory_rw_debug(env, bp->pc, (uint8_t *)&int3, 1, 1)) { return -EINVAL; + } return 0; } @@ -946,8 +1613,9 @@ uint8_t int3; if (cpu_memory_rw_debug(env, bp->pc, &int3, 1, 0) || int3 != 0xcc || - cpu_memory_rw_debug(env, bp->pc, (uint8_t *)&bp->saved_insn, 1, 1)) + cpu_memory_rw_debug(env, bp->pc, (uint8_t *)&bp->saved_insn, 1, 1)) { return -EINVAL; + } return 0; } @@ -963,10 +1631,12 @@ { int n; - for (n = 0; n < nb_hw_breakpoint; n++) + for (n = 0; n < nb_hw_breakpoint; n++) { if (hw_breakpoint[n].addr == addr && hw_breakpoint[n].type == type && - (hw_breakpoint[n].len == len || len == -1)) + (hw_breakpoint[n].len == len || len == -1)) { return n; + } + } return -1; } @@ -985,8 +1655,9 @@ case 2: case 4: case 8: - if (addr & (len - 1)) + if (addr & (len - 1)) { return -EINVAL; + } break; default: return -EINVAL; @@ -996,12 +1667,12 @@ return -ENOSYS; } - if (nb_hw_breakpoint == 4) + if (nb_hw_breakpoint == 4) { return -ENOBUFS; - - if (find_hw_breakpoint(addr, len, type) >= 0) + } + if (find_hw_breakpoint(addr, len, type) >= 0) { return -EEXIST; - + } hw_breakpoint[nb_hw_breakpoint].addr = addr; hw_breakpoint[nb_hw_breakpoint].len = len; hw_breakpoint[nb_hw_breakpoint].type = type; @@ -1016,9 +1687,9 @@ int n; n = find_hw_breakpoint(addr, (type == GDB_BREAKPOINT_HW) ? 1 : len, type); - if (n < 0) + if (n < 0) { return -ENOENT; - + } nb_hw_breakpoint--; hw_breakpoint[n] = hw_breakpoint[nb_hw_breakpoint]; @@ -1039,11 +1710,12 @@ if (arch_info->exception == 1) { if (arch_info->dr6 & (1 << 14)) { - if (cpu_single_env->singlestep_enabled) + if (cpu_single_env->singlestep_enabled) { handle = 1; + } } else { - for (n = 0; n < 4; n++) - if (arch_info->dr6 & (1 << n)) + for (n = 0; n < 4; n++) { + if (arch_info->dr6 & (1 << n)) { switch ((arch_info->dr7 >> (16 + n*4)) & 0x3) { case 0x0: handle = 1; @@ -1061,14 +1733,19 @@ hw_watchpoint.flags = BP_MEM_ACCESS; break; } + } + } } - } else if (kvm_find_sw_breakpoint(cpu_single_env, arch_info->pc)) + } else if (kvm_find_sw_breakpoint(cpu_single_env, arch_info->pc)) { handle = 1; + } + if (!handle) { + cpu_synchronize_state(cpu_single_env); + assert(cpu_single_env->exception_injected == -1); - if (!handle) - kvm_update_guest_debug(cpu_single_env, - (arch_info->exception == 1) ? - KVM_GUESTDBG_INJECT_DB : KVM_GUESTDBG_INJECT_BP); + cpu_single_env->exception_injected = arch_info->exception; + cpu_single_env->has_error_code = 0; + } return handle; } @@ -1085,9 +1762,9 @@ }; int n; - if (kvm_sw_breakpoints_active(env)) + if (kvm_sw_breakpoints_active(env)) { dbg->control |= KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_SW_BP; - + } if (nb_hw_breakpoint > 0) { dbg->control |= KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_HW_BP; dbg->arch.debugreg[7] = 0x0600; @@ -1095,11 +1772,186 @@ dbg->arch.debugreg[n] = hw_breakpoint[n].addr; dbg->arch.debugreg[7] |= (2 << (n * 2)) | (type_code[hw_breakpoint[n].type] << (16 + n*4)) | - (len_code[hw_breakpoint[n].len] << (18 + n*4)); + ((uint32_t)len_code[hw_breakpoint[n].len] << (18 + n*4)); } } } #endif /* KVM_CAP_SET_GUEST_DEBUG */ + +bool kvm_arch_stop_on_emulation_error(CPUState *env) +{ + return !(env->cr[0] & CR0_PE_MASK) || + ((env->segs[R_CS].selector & 3) != 3); +} + +static void hardware_memory_error(void) +{ + fprintf(stderr, "Hardware memory error!\n"); + exit(1); +} + +#ifdef KVM_CAP_MCE +static void kvm_mce_broadcast_rest(CPUState *env) +{ + struct kvm_x86_mce mce = { + .bank = 1, + .status = MCI_STATUS_VAL | MCI_STATUS_UC, + .mcg_status = MCG_STATUS_MCIP | MCG_STATUS_RIPV, + .addr = 0, + .misc = 0, + }; + CPUState *cenv; + + /* Broadcast MCA signal for processor version 06H_EH and above */ + if (cpu_x86_support_mca_broadcast(env)) { + for (cenv = first_cpu; cenv != NULL; cenv = cenv->next_cpu) { + if (cenv == env) { + continue; + } + kvm_inject_x86_mce_on(cenv, &mce, ABORT_ON_ERROR); + } + } +} + +static void kvm_mce_inj_srar_dataload(CPUState *env, target_phys_addr_t paddr) +{ + struct kvm_x86_mce mce = { + .bank = 9, + .status = MCI_STATUS_VAL | MCI_STATUS_UC | MCI_STATUS_EN + | MCI_STATUS_MISCV | MCI_STATUS_ADDRV | MCI_STATUS_S + | MCI_STATUS_AR | 0x134, + .mcg_status = MCG_STATUS_MCIP | MCG_STATUS_EIPV, + .addr = paddr, + .misc = (MCM_ADDR_PHYS << 6) | 0xc, + }; + int r; + + r = kvm_set_mce(env, &mce); + if (r < 0) { + fprintf(stderr, "kvm_set_mce: %s\n", strerror(errno)); + abort(); + } + kvm_mce_broadcast_rest(env); +} + +static void kvm_mce_inj_srao_memscrub(CPUState *env, target_phys_addr_t paddr) +{ + struct kvm_x86_mce mce = { + .bank = 9, + .status = MCI_STATUS_VAL | MCI_STATUS_UC | MCI_STATUS_EN + | MCI_STATUS_MISCV | MCI_STATUS_ADDRV | MCI_STATUS_S + | 0xc0, + .mcg_status = MCG_STATUS_MCIP | MCG_STATUS_RIPV, + .addr = paddr, + .misc = (MCM_ADDR_PHYS << 6) | 0xc, + }; + int r; + + r = kvm_set_mce(env, &mce); + if (r < 0) { + fprintf(stderr, "kvm_set_mce: %s\n", strerror(errno)); + abort(); + } + kvm_mce_broadcast_rest(env); +} + +static void kvm_mce_inj_srao_memscrub2(CPUState *env, target_phys_addr_t paddr) +{ + struct kvm_x86_mce mce = { + .bank = 9, + .status = MCI_STATUS_VAL | MCI_STATUS_UC | MCI_STATUS_EN + | MCI_STATUS_MISCV | MCI_STATUS_ADDRV | MCI_STATUS_S + | 0xc0, + .mcg_status = MCG_STATUS_MCIP | MCG_STATUS_RIPV, + .addr = paddr, + .misc = (MCM_ADDR_PHYS << 6) | 0xc, + }; + + kvm_inject_x86_mce_on(env, &mce, ABORT_ON_ERROR); + kvm_mce_broadcast_rest(env); +} + #endif +int kvm_on_sigbus_vcpu(CPUState *env, int code, void *addr) +{ +#if defined(KVM_CAP_MCE) + void *vaddr; + ram_addr_t ram_addr; + target_phys_addr_t paddr; + + if ((env->mcg_cap & MCG_SER_P) && addr + && (code == BUS_MCEERR_AR + || code == BUS_MCEERR_AO)) { + vaddr = (void *)addr; + if (qemu_ram_addr_from_host(vaddr, &ram_addr) || + !kvm_physical_memory_addr_from_ram(env->kvm_state, ram_addr, &paddr)) { + fprintf(stderr, "Hardware memory error for memory used by " + "QEMU itself instead of guest system!\n"); + /* Hope we are lucky for AO MCE */ + if (code == BUS_MCEERR_AO) { + return 0; + } else { + hardware_memory_error(); + } + } + + if (code == BUS_MCEERR_AR) { + /* Fake an Intel architectural Data Load SRAR UCR */ + kvm_mce_inj_srar_dataload(env, paddr); + } else { + /* + * If there is an MCE excpetion being processed, ignore + * this SRAO MCE + */ + if (!kvm_mce_in_progress(env)) { + /* Fake an Intel architectural Memory scrubbing UCR */ + kvm_mce_inj_srao_memscrub(env, paddr); + } + } + } else +#endif + { + if (code == BUS_MCEERR_AO) { + return 0; + } else if (code == BUS_MCEERR_AR) { + hardware_memory_error(); + } else { + return 1; + } + } + return 0; +} + +int kvm_on_sigbus(int code, void *addr) +{ +#if defined(KVM_CAP_MCE) + if ((first_cpu->mcg_cap & MCG_SER_P) && addr && code == BUS_MCEERR_AO) { + void *vaddr; + ram_addr_t ram_addr; + target_phys_addr_t paddr; + + /* Hope we are lucky for AO MCE */ + vaddr = addr; + if (qemu_ram_addr_from_host(vaddr, &ram_addr) || + !kvm_physical_memory_addr_from_ram(first_cpu->kvm_state, ram_addr, &paddr)) { + fprintf(stderr, "Hardware memory error for memory used by " + "QEMU itself instead of guest system!: %p\n", addr); + return 0; + } + kvm_mce_inj_srao_memscrub2(first_cpu, paddr); + } else +#endif + { + if (code == BUS_MCEERR_AO) { + return 0; + } else if (code == BUS_MCEERR_AR) { + hardware_memory_error(); + } else { + return 1; + } + } + return 0; +} + #include "qemu-kvm-x86.c" diff -Nru qemu-kvm-0.12.5+noroms/target-i386/kvm_x86.h qemu-kvm-0.14.1/target-i386/kvm_x86.h --- qemu-kvm-0.12.5+noroms/target-i386/kvm_x86.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/target-i386/kvm_x86.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,25 @@ +/* + * QEMU KVM support + * + * Copyright (C) 2009 Red Hat Inc. + * Copyright IBM, Corp. 2008 + * + * Authors: + * Anthony Liguori + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#ifndef __KVM_X86_H__ +#define __KVM_X86_H__ + +#define ABORT_ON_ERROR 0x01 +#define MCE_BROADCAST 0x02 + +void kvm_inject_x86_mce(CPUState *cenv, int bank, uint64_t status, + uint64_t mcg_status, uint64_t addr, uint64_t misc, + int flag); + +#endif diff -Nru qemu-kvm-0.12.5+noroms/target-i386/machine.c qemu-kvm-0.14.1/target-i386/machine.c --- qemu-kvm-0.12.5+noroms/target-i386/machine.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-i386/machine.c 2011-05-11 13:29:46.000000000 +0000 @@ -5,7 +5,6 @@ #include "exec-all.h" #include "kvm.h" -#include "qemu-kvm.h" static const VMStateDescription vmstate_segment = { .name = "segment", @@ -48,6 +47,22 @@ #define VMSTATE_XMM_REGS(_field, _state, _n) \ VMSTATE_STRUCT_ARRAY(_field, _state, _n, 0, vmstate_xmm_reg, XMMReg) +/* YMMH format is the same as XMM */ +static const VMStateDescription vmstate_ymmh_reg = { + .name = "ymmh_reg", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField []) { + VMSTATE_UINT64(XMM_Q(0), XMMReg), + VMSTATE_UINT64(XMM_Q(1), XMMReg), + VMSTATE_END_OF_LIST() + } +}; + +#define VMSTATE_YMMH_REGS_VARS(_field, _state, _n, _v) \ + VMSTATE_STRUCT_ARRAY(_field, _state, _n, _v, vmstate_ymmh_reg, XMMReg) + static const VMStateDescription vmstate_mtrr_var = { .name = "mtrr_var", .version_id = 1, @@ -322,12 +337,6 @@ CPUState *env = opaque; int i; - cpu_synchronize_state(env); - if (kvm_enabled()) { - kvm_save_mpstate(env); - kvm_get_vcpu_events(env); - } - /* FPU */ env->fpus_vmstate = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11; env->fptag_vmstate = 0; @@ -342,14 +351,6 @@ #endif } -static int cpu_pre_load(void *opaque) -{ - CPUState *env = opaque; - - cpu_synchronize_state(env); - return 0; -} - static int cpu_post_load(void *opaque, int version_id) { CPUState *env = opaque; @@ -369,27 +370,33 @@ hw_breakpoint_insert(env, i); tlb_flush(env, 1); + return 0; +} - if (kvm_enabled()) { - /* when in-kernel irqchip is used, env->halted causes deadlock - because no userspace IRQs will ever clear this flag */ - env->halted = 0; - - kvm_load_tsc(env); - kvm_load_mpstate(env); - kvm_put_vcpu_events(env); - } +static bool async_pf_msr_needed(void *opaque) +{ + CPUState *cpu = opaque; - return 0; + return cpu->async_pf_en_msr != 0; } +static const VMStateDescription vmstate_async_pf_msr = { + .name = "cpu/async_pf_msr", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField []) { + VMSTATE_UINT64(async_pf_en_msr, CPUState), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_cpu = { .name = "cpu", .version_id = CPU_SAVE_VERSION, .minimum_version_id = 3, .minimum_version_id_old = 3, .pre_save = cpu_pre_save, - .pre_load = cpu_pre_load, .post_load = cpu_post_load, .fields = (VMStateField []) { VMSTATE_UINTTL_ARRAY(regs, CPUState, CPU_NB_REGS), @@ -480,8 +487,21 @@ /* KVM pvclock msr */ VMSTATE_UINT64_V(system_time_msr, CPUState, 11), VMSTATE_UINT64_V(wall_clock_msr, CPUState, 11), + + /* XSAVE related fields */ + VMSTATE_UINT64_V(xcr0, CPUState, 12), + VMSTATE_UINT64_V(xstate_bv, CPUState, 12), + VMSTATE_YMMH_REGS_VARS(ymmh_regs, CPUState, CPU_NB_REGS, 12), VMSTATE_END_OF_LIST() /* The above list is not sorted /wrt version numbers, watch out! */ + }, + .subsections = (VMStateSubsection []) { + { + .vmsd = &vmstate_async_pf_msr, + .needed = async_pf_msr_needed, + } , { + /* empty */ + } } }; diff -Nru qemu-kvm-0.12.5+noroms/target-i386/op_helper.c qemu-kvm-0.14.1/target-i386/op_helper.c --- qemu-kvm-0.12.5+noroms/target-i386/op_helper.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-i386/op_helper.c 2011-05-11 13:29:46.000000000 +0000 @@ -16,10 +16,11 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ -#define CPU_NO_GLOBAL_REGS + #include "exec.h" #include "exec-all.h" #include "host-utils.h" +#include "ioport.h" //#define DEBUG_PCALL @@ -348,6 +349,10 @@ new_segs[R_GS] = 0; new_trap = 0; } + /* XXX: avoid a compiler warning, see + http://support.amd.com/us/Processor_TechDocs/24593.pdf + chapters 12.2.5 and 13.2.4 on how to implement TSS Trap bit */ + (void)new_trap; /* NOTE: we must avoid memory exceptions during the task switch, so we make dummy accesses before */ @@ -1230,7 +1235,7 @@ #if 0 { int i; - uint8_t *ptr; + target_ulong ptr; qemu_log(" code="); ptr = env->segs[R_CS].base + env->eip; for(i = 0; i < 16; i++) { @@ -1351,6 +1356,11 @@ raise_interrupt(exception_index, 0, 0, 0); } +void raise_exception_env(int exception_index, CPUState *nenv) +{ + env = nenv; + raise_exception(exception_index); +} /* SMM support */ #if defined(CONFIG_USER_ONLY) @@ -2882,7 +2892,7 @@ break; case 8: if (!(env->hflags2 & HF2_VINTR_MASK)) { - val = cpu_get_apic_tpr(env); + val = cpu_get_apic_tpr(env->apic_state); } else { val = env->v_tpr; } @@ -2906,7 +2916,7 @@ break; case 8: if (!(env->hflags2 & HF2_VINTR_MASK)) { - cpu_set_apic_tpr(env, t0); + cpu_set_apic_tpr(env->apic_state, t0); } env->v_tpr = t0 & 0x0f; break; @@ -3014,7 +3024,7 @@ env->sysenter_eip = val; break; case MSR_IA32_APICBASE: - cpu_set_apic_base(env, val); + cpu_set_apic_base(env->apic_state, val); break; case MSR_EFER: { @@ -3147,7 +3157,7 @@ val = env->sysenter_eip; break; case MSR_IA32_APICBASE: - val = cpu_get_apic_base(env); + val = cpu_get_apic_base(env->apic_state); break; case MSR_EFER: val = env->efer; @@ -5231,7 +5241,7 @@ switch((uint32_t)ECX) { case 0 ... 0x1fff: t0 = (ECX * 2) % 8; - t1 = ECX / 8; + t1 = (ECX * 2) / 8; break; case 0xc0000000 ... 0xc0001fff: t0 = (8192 + ECX - 0xc0000000) * 2; @@ -5382,6 +5392,7 @@ ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.event_inj))); stl_phys(env->vm_vmcb + offsetof(struct vmcb, control.exit_int_info_err), ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.event_inj_err))); + stl_phys(env->vm_vmcb + offsetof(struct vmcb, control.event_inj), 0); env->hflags2 &= ~HF2_GIF_MASK; /* FIXME: Resets the current ASID register to zero (host ASID). */ diff -Nru qemu-kvm-0.12.5+noroms/target-i386/ops_sse_header.h qemu-kvm-0.14.1/target-i386/ops_sse_header.h --- qemu-kvm-0.12.5+noroms/target-i386/ops_sse_header.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-i386/ops_sse_header.h 2011-05-11 13:29:46.000000000 +0000 @@ -30,6 +30,9 @@ #define dh_ctype_Reg Reg * #define dh_ctype_XMMReg XMMReg * #define dh_ctype_MMXReg MMXReg * +#define dh_is_signed_Reg dh_is_signed_ptr +#define dh_is_signed_XMMReg dh_is_signed_ptr +#define dh_is_signed_MMXReg dh_is_signed_ptr DEF_HELPER_2(glue(psrlw, SUFFIX), void, Reg, Reg) DEF_HELPER_2(glue(psraw, SUFFIX), void, Reg, Reg) diff -Nru qemu-kvm-0.12.5+noroms/target-i386/translate.c qemu-kvm-0.14.1/target-i386/translate.c --- qemu-kvm-0.12.5+noroms/target-i386/translate.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-i386/translate.c 2011-05-11 13:29:46.000000000 +0000 @@ -72,6 +72,8 @@ static TCGv_i64 cpu_tmp1_i64; static TCGv cpu_tmp5; +static uint8_t gen_opc_cc_op[OPC_BUF_SIZE]; + #include "gen-icount.h" #ifdef TARGET_X86_64 @@ -272,28 +274,16 @@ static inline void gen_op_mov_reg_v(int ot, int reg, TCGv t0) { - TCGv tmp; - switch(ot) { case OT_BYTE: - tmp = tcg_temp_new(); - tcg_gen_ext8u_tl(tmp, t0); if (reg < 4 X86_64_DEF( || reg >= 8 || x86_64_hregs)) { - tcg_gen_andi_tl(cpu_regs[reg], cpu_regs[reg], ~0xff); - tcg_gen_or_tl(cpu_regs[reg], cpu_regs[reg], tmp); + tcg_gen_deposit_tl(cpu_regs[reg], cpu_regs[reg], t0, 0, 8); } else { - tcg_gen_shli_tl(tmp, tmp, 8); - tcg_gen_andi_tl(cpu_regs[reg - 4], cpu_regs[reg - 4], ~0xff00); - tcg_gen_or_tl(cpu_regs[reg - 4], cpu_regs[reg - 4], tmp); + tcg_gen_deposit_tl(cpu_regs[reg - 4], cpu_regs[reg - 4], t0, 8, 8); } - tcg_temp_free(tmp); break; case OT_WORD: - tmp = tcg_temp_new(); - tcg_gen_ext16u_tl(tmp, t0); - tcg_gen_andi_tl(cpu_regs[reg], cpu_regs[reg], ~0xffff); - tcg_gen_or_tl(cpu_regs[reg], cpu_regs[reg], tmp); - tcg_temp_free(tmp); + tcg_gen_deposit_tl(cpu_regs[reg], cpu_regs[reg], t0, 0, 16); break; default: /* XXX this shouldn't be reached; abort? */ case OT_LONG: @@ -321,15 +311,9 @@ static inline void gen_op_mov_reg_A0(int size, int reg) { - TCGv tmp; - switch(size) { case 0: - tmp = tcg_temp_new(); - tcg_gen_ext16u_tl(tmp, cpu_A0); - tcg_gen_andi_tl(cpu_regs[reg], cpu_regs[reg], ~0xffff); - tcg_gen_or_tl(cpu_regs[reg], cpu_regs[reg], tmp); - tcg_temp_free(tmp); + tcg_gen_deposit_tl(cpu_regs[reg], cpu_regs[reg], cpu_A0, 0, 16); break; default: /* XXX this shouldn't be reached; abort? */ case 1: @@ -413,9 +397,7 @@ switch(size) { case 0: tcg_gen_addi_tl(cpu_tmp0, cpu_regs[reg], val); - tcg_gen_ext16u_tl(cpu_tmp0, cpu_tmp0); - tcg_gen_andi_tl(cpu_regs[reg], cpu_regs[reg], ~0xffff); - tcg_gen_or_tl(cpu_regs[reg], cpu_regs[reg], cpu_tmp0); + tcg_gen_deposit_tl(cpu_regs[reg], cpu_regs[reg], cpu_tmp0, 0, 16); break; case 1: tcg_gen_addi_tl(cpu_tmp0, cpu_regs[reg], val); @@ -437,9 +419,7 @@ switch(size) { case 0: tcg_gen_add_tl(cpu_tmp0, cpu_regs[reg], cpu_T[0]); - tcg_gen_ext16u_tl(cpu_tmp0, cpu_tmp0); - tcg_gen_andi_tl(cpu_regs[reg], cpu_regs[reg], ~0xffff); - tcg_gen_or_tl(cpu_regs[reg], cpu_regs[reg], cpu_tmp0); + tcg_gen_deposit_tl(cpu_regs[reg], cpu_regs[reg], cpu_tmp0, 0, 16); break; case 1: tcg_gen_add_tl(cpu_tmp0, cpu_regs[reg], cpu_T[0]); @@ -761,7 +741,6 @@ if (s->cc_op != CC_OP_DYNAMIC) gen_op_set_cc_op(s->cc_op); gen_jmp_im(cur_eip); - state_saved = 1; } svm_flags |= (1 << (4 + ot)); next_eip = s->pc - s->cs_base; @@ -2015,7 +1994,7 @@ break; default: case 2: - disp = ldl_code(s->pc); + disp = (int32_t)ldl_code(s->pc); s->pc += 4; break; } @@ -2207,8 +2186,6 @@ if (s->override >= 0) { override = s->override; must_add_seg = 1; - } else { - override = R_DS; } if (must_add_seg) { #ifdef TARGET_X86_64 @@ -2311,10 +2288,7 @@ int l1, l2, cc_op; cc_op = s->cc_op; - if (s->cc_op != CC_OP_DYNAMIC) { - gen_op_set_cc_op(s->cc_op); - s->cc_op = CC_OP_DYNAMIC; - } + gen_update_cc_op(s); if (s->jmp_opt) { l1 = gen_new_label(); gen_jcc1(s, cc_op, b, l1); @@ -2323,7 +2297,7 @@ gen_set_label(l1); gen_goto_tb(s, 1, val); - s->is_jmp = 3; + s->is_jmp = DISAS_TB_JUMP; } else { l1 = gen_new_label(); @@ -2401,11 +2375,11 @@ stop as a special handling must be done to disable hardware interrupts for the next instruction */ if (seg_reg == R_SS || (s->code32 && seg_reg < R_FS)) - s->is_jmp = 3; + s->is_jmp = DISAS_TB_JUMP; } else { gen_op_movl_seg_T0_vm(seg_reg); if (seg_reg == R_SS) - s->is_jmp = 3; + s->is_jmp = DISAS_TB_JUMP; } } @@ -2673,7 +2647,7 @@ gen_op_set_cc_op(s->cc_op); gen_jmp_im(cur_eip); gen_helper_raise_exception(tcg_const_i32(trapno)); - s->is_jmp = 3; + s->is_jmp = DISAS_TB_JUMP; } /* an interrupt is different from an exception because of the @@ -2686,7 +2660,7 @@ gen_jmp_im(cur_eip); gen_helper_raise_interrupt(tcg_const_i32(intno), tcg_const_i32(next_eip - cur_eip)); - s->is_jmp = 3; + s->is_jmp = DISAS_TB_JUMP; } static void gen_debug(DisasContext *s, target_ulong cur_eip) @@ -2695,7 +2669,7 @@ gen_op_set_cc_op(s->cc_op); gen_jmp_im(cur_eip); gen_helper_debug(); - s->is_jmp = 3; + s->is_jmp = DISAS_TB_JUMP; } /* generate a generic end of block. Trace exception is also generated @@ -2717,7 +2691,7 @@ } else { tcg_gen_exit_tb(0); } - s->is_jmp = 3; + s->is_jmp = DISAS_TB_JUMP; } /* generate a jump to eip. No segment change must happen before as a @@ -2725,12 +2699,9 @@ static void gen_jmp_tb(DisasContext *s, target_ulong eip, int tb_num) { if (s->jmp_opt) { - if (s->cc_op != CC_OP_DYNAMIC) { - gen_op_set_cc_op(s->cc_op); - s->cc_op = CC_OP_DYNAMIC; - } + gen_update_cc_op(s); gen_goto_tb(s, tb_num, eip); - s->is_jmp = 3; + s->is_jmp = DISAS_TB_JUMP; } else { gen_jmp_im(eip); gen_eob(s); @@ -3473,6 +3444,9 @@ case 0x171: /* shift xmm, im */ case 0x172: case 0x173: + if (b1 >= 2) { + goto illegal_op; + } val = ldub_code(s->pc++); if (is_xmm) { gen_op_movl_T0_im(val); @@ -3700,6 +3674,9 @@ rm = modrm & 7; reg = ((modrm >> 3) & 7) | rex_r; mod = (modrm >> 6) & 3; + if (b1 >= 2) { + goto illegal_op; + } sse_op2 = sse_op_table6[b].op[b1]; if (!sse_op2) @@ -3799,6 +3776,9 @@ rm = modrm & 7; reg = ((modrm >> 3) & 7) | rex_r; mod = (modrm >> 6) & 3; + if (b1 >= 2) { + goto illegal_op; + } sse_op2 = sse_op_table7[b].op[b1]; if (!sse_op2) @@ -4697,8 +4677,6 @@ ot = dflag + OT_WORD; modrm = ldub_code(s->pc++); - mod = (modrm >> 6) & 3; - rm = (modrm & 7) | REX_B(s); reg = ((modrm >> 3) & 7) | rex_r; gen_ldst_modrm(s, modrm, ot, OR_TMP0, 0); @@ -6895,10 +6873,7 @@ if (!s->pe) { gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); } else { - if (s->cc_op != CC_OP_DYNAMIC) { - gen_op_set_cc_op(s->cc_op); - s->cc_op = CC_OP_DYNAMIC; - } + gen_update_cc_op(s); gen_jmp_im(pc_start - s->cs_base); gen_helper_sysenter(); gen_eob(s); @@ -6911,10 +6886,7 @@ if (!s->pe) { gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); } else { - if (s->cc_op != CC_OP_DYNAMIC) { - gen_op_set_cc_op(s->cc_op); - s->cc_op = CC_OP_DYNAMIC; - } + gen_update_cc_op(s); gen_jmp_im(pc_start - s->cs_base); gen_helper_sysexit(tcg_const_i32(dflag)); gen_eob(s); @@ -6923,10 +6895,7 @@ #ifdef TARGET_X86_64 case 0x105: /* syscall */ /* XXX: is it usable in real mode ? */ - if (s->cc_op != CC_OP_DYNAMIC) { - gen_op_set_cc_op(s->cc_op); - s->cc_op = CC_OP_DYNAMIC; - } + gen_update_cc_op(s); gen_jmp_im(pc_start - s->cs_base); gen_helper_syscall(tcg_const_i32(s->pc - pc_start)); gen_eob(s); @@ -6935,10 +6904,7 @@ if (!s->pe) { gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); } else { - if (s->cc_op != CC_OP_DYNAMIC) { - gen_op_set_cc_op(s->cc_op); - s->cc_op = CC_OP_DYNAMIC; - } + gen_update_cc_op(s); gen_jmp_im(pc_start - s->cs_base); gen_helper_sysret(tcg_const_i32(s->dflag)); /* condition codes are modified only in long mode */ @@ -6962,7 +6928,7 @@ gen_op_set_cc_op(s->cc_op); gen_jmp_im(pc_start - s->cs_base); gen_helper_hlt(tcg_const_i32(s->pc - pc_start)); - s->is_jmp = 3; + s->is_jmp = DISAS_TB_JUMP; } break; case 0x100: @@ -7079,10 +7045,7 @@ if (!(s->cpuid_ext_features & CPUID_EXT_MONITOR) || s->cpl != 0) goto illegal_op; - if (s->cc_op != CC_OP_DYNAMIC) { - gen_op_set_cc_op(s->cc_op); - s->cc_op = CC_OP_DYNAMIC; - } + gen_update_cc_op(s); gen_jmp_im(pc_start - s->cs_base); gen_helper_mwait(tcg_const_i32(s->pc - pc_start)); gen_eob(s); @@ -7119,7 +7082,7 @@ gen_helper_vmrun(tcg_const_i32(s->aflag), tcg_const_i32(s->pc - pc_start)); tcg_gen_exit_tb(0); - s->is_jmp = 3; + s->is_jmp = DISAS_TB_JUMP; } break; case 1: /* VMMCALL */ @@ -7607,10 +7570,7 @@ gen_svm_check_intercept(s, pc_start, SVM_EXIT_RSM); if (!(s->flags & HF_SMM_MASK)) goto illegal_op; - if (s->cc_op != CC_OP_DYNAMIC) { - gen_op_set_cc_op(s->cc_op); - s->cc_op = CC_OP_DYNAMIC; - } + gen_update_cc_op(s); gen_jmp_im(s->pc - s->cs_base); gen_helper_rsm(); gen_eob(s); @@ -7751,7 +7711,7 @@ target_ulong pc_ptr; uint16_t *gen_opc_end; CPUBreakpoint *bp; - int j, lj, cflags; + int j, lj; uint64_t flags; target_ulong pc_start; target_ulong cs_base; @@ -7762,7 +7722,6 @@ pc_start = tb->pc; cs_base = tb->cs_base; flags = tb->flags; - cflags = tb->cflags; dc->pe = (flags >> HF_PE_SHIFT) & 1; dc->code32 = (flags >> HF_CS32_SHIFT) & 1; @@ -7900,7 +7859,6 @@ } #ifdef DEBUG_DISAS - log_cpu_state_mask(CPU_LOG_TB_CPU, env, X86_DUMP_CCOP); if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { int disas_flags; qemu_log("----------------\n"); diff -Nru qemu-kvm-0.12.5+noroms/target-ia64/machine.c qemu-kvm-0.14.1/target-ia64/machine.c --- qemu-kvm-0.12.5+noroms/target-ia64/machine.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-ia64/machine.c 2011-05-11 13:29:46.000000000 +0000 @@ -4,12 +4,14 @@ #include "exec-all.h" #include "qemu-kvm.h" +void kvm_arch_save_mpstate(CPUState *env); +void kvm_arch_load_mpstate(CPUState *env); + void cpu_save(QEMUFile *f, void *opaque) { CPUState *env = opaque; if (kvm_enabled()) { - kvm_save_registers(env); kvm_arch_save_mpstate(env); } } @@ -19,7 +21,6 @@ CPUState *env = opaque; if (kvm_enabled()) { - kvm_load_registers(env); kvm_arch_load_mpstate(env); } return 0; diff -Nru qemu-kvm-0.12.5+noroms/target-m68k/cpu.h qemu-kvm-0.14.1/target-m68k/cpu.h --- qemu-kvm-0.12.5+noroms/target-m68k/cpu.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-m68k/cpu.h 2011-05-11 13:29:46.000000000 +0000 @@ -24,6 +24,7 @@ #define CPUState struct CPUM68KState +#include "qemu-common.h" #include "cpu-defs.h" #include "softfloat.h" @@ -198,7 +199,7 @@ return (env->features & (1u << feature)) != 0; } -void m68k_cpu_list(FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...)); +void m68k_cpu_list(FILE *f, fprintf_function cpu_fprintf); void register_m68k_insns (CPUM68KState *env); @@ -210,6 +211,9 @@ #define TARGET_PAGE_BITS 10 #endif +#define TARGET_PHYS_ADDR_SPACE_BITS 32 +#define TARGET_VIRT_ADDR_SPACE_BITS 32 + #define cpu_init cpu_m68k_init #define cpu_exec cpu_m68k_exec #define cpu_gen_code cpu_m68k_gen_code @@ -239,12 +243,6 @@ #endif #include "cpu-all.h" -#include "exec-all.h" - -static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb) -{ - env->pc = tb->pc; -} static inline void cpu_get_tb_cpu_state(CPUState *env, target_ulong *pc, target_ulong *cs_base, int *flags) diff -Nru qemu-kvm-0.12.5+noroms/target-m68k/exec.h qemu-kvm-0.14.1/target-m68k/exec.h --- qemu-kvm-0.12.5+noroms/target-m68k/exec.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-m68k/exec.h 2011-05-11 13:29:46.000000000 +0000 @@ -20,22 +20,10 @@ #include "dyngen-exec.h" register struct CPUM68KState *env asm(AREG0); -/* This is only used for tb lookup. */ -register uint32_t T0 asm(AREG1); -/* ??? We don't use T1, but common code expects it to exist */ -#define T1 env->t1 #include "cpu.h" #include "exec-all.h" -static inline void env_to_regs(void) -{ -} - -static inline void regs_to_env(void) -{ -} - #if !defined(CONFIG_USER_ONLY) #include "softmmu_exec.h" #endif @@ -54,3 +42,9 @@ } return EXCP_HALTED; } + +static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb) +{ + env->pc = tb->pc; +} + diff -Nru qemu-kvm-0.12.5+noroms/target-m68k/helper.c qemu-kvm-0.14.1/target-m68k/helper.c --- qemu-kvm-0.12.5+noroms/target-m68k/helper.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-m68k/helper.c 2011-05-11 13:29:46.000000000 +0000 @@ -53,7 +53,7 @@ {NULL, 0}, }; -void m68k_cpu_list(FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...)) +void m68k_cpu_list(FILE *f, fprintf_function cpu_fprintf) { unsigned int i; @@ -342,14 +342,6 @@ env->current_sp = new_sp; } -/* MMU */ - -/* TODO: This will need fixing once the MMU is implemented. */ -target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr) -{ - return addr; -} - #if defined(CONFIG_USER_ONLY) int cpu_m68k_handle_mmu_fault (CPUState *env, target_ulong address, int rw, @@ -362,14 +354,23 @@ #else +/* MMU */ + +/* TODO: This will need fixing once the MMU is implemented. */ +target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr) +{ + return addr; +} + int cpu_m68k_handle_mmu_fault (CPUState *env, target_ulong address, int rw, int mmu_idx, int is_softmmu) { int prot; address &= TARGET_PAGE_MASK; - prot = PAGE_READ | PAGE_WRITE; - return tlb_set_page(env, address, address, prot, mmu_idx, is_softmmu); + prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + tlb_set_page(env, address, address, prot, mmu_idx, TARGET_PAGE_SIZE); + return 0; } /* Notify CPU of a pending interrupt. Prioritization and vectoring should @@ -613,10 +614,10 @@ /* ??? Should flush denormals to zero. */ float64 res; res = float64_sub(a, b, &env->fp_status); - if (float64_is_nan(res)) { + if (float64_is_quiet_nan(res)) { /* +/-inf compares equal against itself, but sub returns nan. */ - if (!float64_is_nan(a) - && !float64_is_nan(b)) { + if (!float64_is_quiet_nan(a) + && !float64_is_quiet_nan(b)) { res = float64_zero; if (float64_lt_quiet(a, res, &env->fp_status)) res = float64_chs(res); @@ -767,10 +768,11 @@ { uint64_t val; val = env->macc[acc]; - if (val == 0) + if (val == 0) { env->macsr |= MACSR_Z; - else if (val & (1ull << 47)); + } else if (val & (1ull << 47)) { env->macsr |= MACSR_N; + } if (env->macsr & (MACSR_PAV0 << acc)) { env->macsr |= MACSR_V; } diff -Nru qemu-kvm-0.12.5+noroms/target-m68k/translate.c qemu-kvm-0.14.1/target-m68k/translate.c --- qemu-kvm-0.12.5+noroms/target-m68k/translate.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-m68k/translate.c 2011-05-11 13:29:46.000000000 +0000 @@ -541,7 +541,6 @@ offset = read_im32(s); return gen_im32(offset); case 2: /* pc displacement */ - tmp = tcg_temp_new(); offset = s->pc; offset += ldsw_code(s->pc); s->pc += 2; @@ -2969,7 +2968,6 @@ int j, lj; target_ulong pc_start; int pc_offset; - int last_cc_op; int num_insns; int max_insns; @@ -3023,7 +3021,6 @@ } if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO)) gen_io_start(); - last_cc_op = dc->cc_op; dc->insn_pc = dc->pc; disas_m68k_insn(env, dc); num_insns++; @@ -3095,8 +3092,7 @@ gen_intermediate_code_internal(env, tb, 1); } -void cpu_dump_state(CPUState *env, FILE *f, - int (*cpu_fprintf)(FILE *f, const char *fmt, ...), +void cpu_dump_state(CPUState *env, FILE *f, fprintf_function cpu_fprintf, int flags) { int i; diff -Nru qemu-kvm-0.12.5+noroms/target-microblaze/cpu.h qemu-kvm-0.14.1/target-microblaze/cpu.h --- qemu-kvm-0.12.5+noroms/target-microblaze/cpu.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-microblaze/cpu.h 2011-05-11 13:29:46.000000000 +0000 @@ -24,6 +24,7 @@ #define CPUState struct CPUMBState #include "cpu-defs.h" +#include "softfloat.h" struct CPUMBState; #if !defined(CONFIG_USER_ONLY) #include "mmu.h" @@ -31,7 +32,7 @@ #define TARGET_HAS_ICE 1 -#define ELF_MACHINE EM_XILINX_MICROBLAZE +#define ELF_MACHINE EM_MICROBLAZE #define EXCP_NMI 1 #define EXCP_MMU 2 @@ -91,6 +92,13 @@ #define ESR_EC_DATA_TLB 10 #define ESR_EC_INSN_TLB 11 +/* Floating Point Status Register (FSR) Bits */ +#define FSR_IO (1<<4) /* Invalid operation */ +#define FSR_DZ (1<<3) /* Divide-by-zero */ +#define FSR_OF (1<<2) /* Overflow */ +#define FSR_UF (1<<1) /* Underflow */ +#define FSR_DO (1<<0) /* Denormalized operand error */ + /* Version reg. */ /* Basic PVR mask */ #define PVR0_PVR_FULL_MASK 0x80000000 @@ -208,6 +216,7 @@ uint32_t imm; uint32_t regs[33]; uint32_t sregs[24]; + float_status fp_status; /* Internal flags. */ #define IMM_FLAG 4 @@ -217,8 +226,7 @@ #define DRTB_FLAG (1 << 18) #define D_FLAG (1 << 19) /* Bit in ESR. */ /* TB dependant CPUState. */ -#define IFLAGS_TB_MASK (D_FLAG | IMM_FLAG | DRTI_FLAG \ - | DRTE_FLAG | DRTB_FLAG | MSR_EE_FLAG) +#define IFLAGS_TB_MASK (D_FLAG | IMM_FLAG | DRTI_FLAG | DRTE_FLAG | DRTB_FLAG) uint32_t iflags; struct { @@ -253,6 +261,9 @@ #define TARGET_PAGE_BITS 12 #define MMAP_SHIFT TARGET_PAGE_BITS +#define TARGET_PHYS_ADDR_SPACE_BITS 32 +#define TARGET_VIRT_ADDR_SPACE_BITS 32 + #define cpu_init cpu_mb_init #define cpu_exec cpu_mb_exec #define cpu_gen_code cpu_mb_gen_code @@ -303,12 +314,6 @@ } #include "cpu-all.h" -#include "exec-all.h" - -static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb) -{ - env->sregs[SR_PC] = tb->pc; -} static inline target_ulong cpu_get_pc(CPUState *env) { @@ -320,10 +325,12 @@ { *pc = env->sregs[SR_PC]; *cs_base = 0; - env->iflags |= env->sregs[SR_MSR] & MSR_EE; - *flags = env->iflags & IFLAGS_TB_MASK; + *flags = (env->iflags & IFLAGS_TB_MASK) | + (env->sregs[SR_MSR] & (MSR_UM | MSR_VM | MSR_EE)); } +#if !defined(CONFIG_USER_ONLY) void do_unassigned_access(target_phys_addr_t addr, int is_write, int is_exec, int is_asi, int size); #endif +#endif diff -Nru qemu-kvm-0.12.5+noroms/target-microblaze/exec.h qemu-kvm-0.14.1/target-microblaze/exec.h --- qemu-kvm-0.12.5+noroms/target-microblaze/exec.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-microblaze/exec.h 2011-05-11 13:29:46.000000000 +0000 @@ -23,20 +23,10 @@ #include "cpu.h" #include "exec-all.h" -static inline void env_to_regs(void) -{ -} - -static inline void regs_to_env(void) -{ -} - #if !defined(CONFIG_USER_ONLY) #include "softmmu_exec.h" #endif -void cpu_mb_flush_flags(CPUMBState *env, int cc_op); - static inline int cpu_has_work(CPUState *env) { return (env->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI)); @@ -54,3 +44,9 @@ } return EXCP_HALTED; } + +static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb) +{ + env->sregs[SR_PC] = tb->pc; +} + diff -Nru qemu-kvm-0.12.5+noroms/target-microblaze/helper.c qemu-kvm-0.14.1/target-microblaze/helper.c --- qemu-kvm-0.12.5+noroms/target-microblaze/helper.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-microblaze/helper.c 2011-05-11 13:29:46.000000000 +0000 @@ -45,11 +45,6 @@ return 1; } -target_phys_addr_t cpu_get_phys_page_debug(CPUState * env, target_ulong addr) -{ - return addr; -} - #else /* !CONFIG_USER_ONLY */ int cpu_mb_handle_mmu_fault (CPUState *env, target_ulong address, int rw, @@ -81,8 +76,8 @@ DMMU(qemu_log("MMU map mmu=%d v=%x p=%x prot=%x\n", mmu_idx, vaddr, paddr, lu.prot)); - r = tlb_set_page(env, vaddr, - paddr, lu.prot, mmu_idx, is_softmmu); + tlb_set_page(env, vaddr, paddr, lu.prot, mmu_idx, TARGET_PAGE_SIZE); + r = 0; } else { env->sregs[SR_EAR] = address; DMMU(qemu_log("mmu=%d miss v=%x\n", mmu_idx, address)); @@ -112,7 +107,8 @@ /* MMU disabled or not available. */ address &= TARGET_PAGE_MASK; prot = PAGE_BITS; - r = tlb_set_page(env, address, address, prot, mmu_idx, is_softmmu); + tlb_set_page(env, address, address, prot, mmu_idx, TARGET_PAGE_SIZE); + r = 0; } return r; } diff -Nru qemu-kvm-0.12.5+noroms/target-microblaze/helper.h qemu-kvm-0.14.1/target-microblaze/helper.h --- qemu-kvm-0.12.5+noroms/target-microblaze/helper.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-microblaze/helper.h 2011-05-11 13:29:46.000000000 +0000 @@ -2,14 +2,29 @@ DEF_HELPER_1(raise_exception, void, i32) DEF_HELPER_0(debug, void) -DEF_HELPER_4(addkc, i32, i32, i32, i32, i32) -DEF_HELPER_4(subkc, i32, i32, i32, i32, i32) +DEF_HELPER_FLAGS_3(carry, TCG_CALL_PURE | TCG_CALL_CONST, i32, i32, i32, i32) DEF_HELPER_2(cmp, i32, i32, i32) DEF_HELPER_2(cmpu, i32, i32, i32) DEF_HELPER_2(divs, i32, i32, i32) DEF_HELPER_2(divu, i32, i32, i32) +DEF_HELPER_2(fadd, i32, i32, i32) +DEF_HELPER_2(frsub, i32, i32, i32) +DEF_HELPER_2(fmul, i32, i32, i32) +DEF_HELPER_2(fdiv, i32, i32, i32) +DEF_HELPER_1(flt, i32, i32) +DEF_HELPER_1(fint, i32, i32) +DEF_HELPER_1(fsqrt, i32, i32) + +DEF_HELPER_2(fcmp_un, i32, i32, i32) +DEF_HELPER_2(fcmp_lt, i32, i32, i32) +DEF_HELPER_2(fcmp_eq, i32, i32, i32) +DEF_HELPER_2(fcmp_le, i32, i32, i32) +DEF_HELPER_2(fcmp_gt, i32, i32, i32) +DEF_HELPER_2(fcmp_ne, i32, i32, i32) +DEF_HELPER_2(fcmp_ge, i32, i32, i32) + DEF_HELPER_FLAGS_2(pcmpbf, TCG_CALL_PURE | TCG_CALL_CONST, i32, i32, i32) #if !defined(CONFIG_USER_ONLY) DEF_HELPER_1(mmu_read, i32, i32) diff -Nru qemu-kvm-0.12.5+noroms/target-microblaze/mmu.c qemu-kvm-0.14.1/target-microblaze/mmu.c --- qemu-kvm-0.12.5+noroms/target-microblaze/mmu.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-microblaze/mmu.c 2011-05-11 13:29:46.000000000 +0000 @@ -60,8 +60,7 @@ { struct microblaze_mmu *mmu = &env->mmu; unsigned int i; - unsigned int tlb_size; - uint32_t tlb_tag, mask, t; + uint32_t t; if (newpid & ~0xff) qemu_log("Illegal rpid=%x\n", newpid); @@ -70,10 +69,6 @@ /* Lookup and decode. */ t = mmu->rams[RAM_TAG][i]; if (t & TLB_VALID) { - tlb_size = tlb_decode_size((t & TLB_PAGESZ_MASK) >> 7); - mask = ~(tlb_size - 1); - - tlb_tag = t & TLB_EPN_MASK; if (mmu->tids[i] && ((mmu->regs[MMU_R_PID] & 0xff) == mmu->tids[i])) mmu_flush_idx(env, i); } diff -Nru qemu-kvm-0.12.5+noroms/target-microblaze/op_helper.c qemu-kvm-0.14.1/target-microblaze/op_helper.c --- qemu-kvm-0.12.5+noroms/target-microblaze/op_helper.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-microblaze/op_helper.c 2011-05-11 13:29:46.000000000 +0000 @@ -80,6 +80,15 @@ int i; qemu_log("PC=%8.8x\n", env->sregs[SR_PC]); + qemu_log("rmsr=%x resr=%x rear=%x debug[%x] imm=%x iflags=%x\n", + env->sregs[SR_MSR], env->sregs[SR_ESR], env->sregs[SR_EAR], + env->debug, env->imm, env->iflags); + qemu_log("btaken=%d btarget=%x mode=%s(saved=%s) eip=%d ie=%d\n", + env->btaken, env->btarget, + (env->sregs[SR_MSR] & MSR_UM) ? "user" : "kernel", + (env->sregs[SR_MSR] & MSR_UMS) ? "user" : "kernel", + (env->sregs[SR_MSR] & MSR_EIP), + (env->sregs[SR_MSR] & MSR_IE)); for (i = 0; i < 32; i++) { qemu_log("r%2.2d=%8.8x ", i, env->regs[i]); if ((i + 1) % 4 == 0) @@ -119,48 +128,11 @@ return t; } -uint32_t helper_addkc(uint32_t a, uint32_t b, uint32_t k, uint32_t c) +uint32_t helper_carry(uint32_t a, uint32_t b, uint32_t cf) { - uint32_t d, cf = 0, ncf; - - if (c) - cf = env->sregs[SR_MSR] >> 31; - assert(cf == 0 || cf == 1); - d = a + b + cf; - - if (!k) { - ncf = compute_carry(a, b, cf); - assert(ncf == 0 || ncf == 1); - if (ncf) - env->sregs[SR_MSR] |= MSR_C | MSR_CC; - else - env->sregs[SR_MSR] &= ~(MSR_C | MSR_CC); - } - D(qemu_log("%x = %x + %x cf=%d ncf=%d k=%d c=%d\n", - d, a, b, cf, ncf, k, c)); - return d; -} - -uint32_t helper_subkc(uint32_t a, uint32_t b, uint32_t k, uint32_t c) -{ - uint32_t d, cf = 1, ncf; - - if (c) - cf = env->sregs[SR_MSR] >> 31; - assert(cf == 0 || cf == 1); - d = b + ~a + cf; - - if (!k) { - ncf = compute_carry(b, ~a, cf); - assert(ncf == 0 || ncf == 1); - if (ncf) - env->sregs[SR_MSR] |= MSR_C | MSR_CC; - else - env->sregs[SR_MSR] &= ~(MSR_C | MSR_CC); - } - D(qemu_log("%x = %x + %x cf=%d ncf=%d k=%d c=%d\n", - d, a, b, cf, ncf, k, c)); - return d; + uint32_t ncf; + ncf = compute_carry(a, b, cf); + return ncf; } static inline int div_prepare(uint32_t a, uint32_t b) @@ -193,6 +165,250 @@ return a / b; } +/* raise FPU exception. */ +static void raise_fpu_exception(void) +{ + env->sregs[SR_ESR] = ESR_EC_FPU; + helper_raise_exception(EXCP_HW_EXCP); +} + +static void update_fpu_flags(int flags) +{ + int raise = 0; + + if (flags & float_flag_invalid) { + env->sregs[SR_FSR] |= FSR_IO; + raise = 1; + } + if (flags & float_flag_divbyzero) { + env->sregs[SR_FSR] |= FSR_DZ; + raise = 1; + } + if (flags & float_flag_overflow) { + env->sregs[SR_FSR] |= FSR_OF; + raise = 1; + } + if (flags & float_flag_underflow) { + env->sregs[SR_FSR] |= FSR_UF; + raise = 1; + } + if (raise + && (env->pvr.regs[2] & PVR2_FPU_EXC_MASK) + && (env->sregs[SR_MSR] & MSR_EE)) { + raise_fpu_exception(); + } +} + +uint32_t helper_fadd(uint32_t a, uint32_t b) +{ + CPU_FloatU fd, fa, fb; + int flags; + + set_float_exception_flags(0, &env->fp_status); + fa.l = a; + fb.l = b; + fd.f = float32_add(fa.f, fb.f, &env->fp_status); + + flags = get_float_exception_flags(&env->fp_status); + update_fpu_flags(flags); + return fd.l; +} + +uint32_t helper_frsub(uint32_t a, uint32_t b) +{ + CPU_FloatU fd, fa, fb; + int flags; + + set_float_exception_flags(0, &env->fp_status); + fa.l = a; + fb.l = b; + fd.f = float32_sub(fb.f, fa.f, &env->fp_status); + flags = get_float_exception_flags(&env->fp_status); + update_fpu_flags(flags); + return fd.l; +} + +uint32_t helper_fmul(uint32_t a, uint32_t b) +{ + CPU_FloatU fd, fa, fb; + int flags; + + set_float_exception_flags(0, &env->fp_status); + fa.l = a; + fb.l = b; + fd.f = float32_mul(fa.f, fb.f, &env->fp_status); + flags = get_float_exception_flags(&env->fp_status); + update_fpu_flags(flags); + + return fd.l; +} + +uint32_t helper_fdiv(uint32_t a, uint32_t b) +{ + CPU_FloatU fd, fa, fb; + int flags; + + set_float_exception_flags(0, &env->fp_status); + fa.l = a; + fb.l = b; + fd.f = float32_div(fb.f, fa.f, &env->fp_status); + flags = get_float_exception_flags(&env->fp_status); + update_fpu_flags(flags); + + return fd.l; +} + +uint32_t helper_fcmp_un(uint32_t a, uint32_t b) +{ + CPU_FloatU fa, fb; + uint32_t r = 0; + + fa.l = a; + fb.l = b; + + if (float32_is_signaling_nan(fa.f) || float32_is_signaling_nan(fb.f)) { + update_fpu_flags(float_flag_invalid); + r = 1; + } + + if (float32_is_quiet_nan(fa.f) || float32_is_quiet_nan(fb.f)) { + r = 1; + } + + return r; +} + +uint32_t helper_fcmp_lt(uint32_t a, uint32_t b) +{ + CPU_FloatU fa, fb; + int r; + int flags; + + set_float_exception_flags(0, &env->fp_status); + fa.l = a; + fb.l = b; + r = float32_lt(fb.f, fa.f, &env->fp_status); + flags = get_float_exception_flags(&env->fp_status); + update_fpu_flags(flags & float_flag_invalid); + + return r; +} + +uint32_t helper_fcmp_eq(uint32_t a, uint32_t b) +{ + CPU_FloatU fa, fb; + int flags; + int r; + + set_float_exception_flags(0, &env->fp_status); + fa.l = a; + fb.l = b; + r = float32_eq(fa.f, fb.f, &env->fp_status); + flags = get_float_exception_flags(&env->fp_status); + update_fpu_flags(flags & float_flag_invalid); + + return r; +} + +uint32_t helper_fcmp_le(uint32_t a, uint32_t b) +{ + CPU_FloatU fa, fb; + int flags; + int r; + + fa.l = a; + fb.l = b; + set_float_exception_flags(0, &env->fp_status); + r = float32_le(fa.f, fb.f, &env->fp_status); + flags = get_float_exception_flags(&env->fp_status); + update_fpu_flags(flags & float_flag_invalid); + + + return r; +} + +uint32_t helper_fcmp_gt(uint32_t a, uint32_t b) +{ + CPU_FloatU fa, fb; + int flags, r; + + fa.l = a; + fb.l = b; + set_float_exception_flags(0, &env->fp_status); + r = float32_lt(fa.f, fb.f, &env->fp_status); + flags = get_float_exception_flags(&env->fp_status); + update_fpu_flags(flags & float_flag_invalid); + return r; +} + +uint32_t helper_fcmp_ne(uint32_t a, uint32_t b) +{ + CPU_FloatU fa, fb; + int flags, r; + + fa.l = a; + fb.l = b; + set_float_exception_flags(0, &env->fp_status); + r = !float32_eq(fa.f, fb.f, &env->fp_status); + flags = get_float_exception_flags(&env->fp_status); + update_fpu_flags(flags & float_flag_invalid); + + return r; +} + +uint32_t helper_fcmp_ge(uint32_t a, uint32_t b) +{ + CPU_FloatU fa, fb; + int flags, r; + + fa.l = a; + fb.l = b; + set_float_exception_flags(0, &env->fp_status); + r = !float32_lt(fa.f, fb.f, &env->fp_status); + flags = get_float_exception_flags(&env->fp_status); + update_fpu_flags(flags & float_flag_invalid); + + return r; +} + +uint32_t helper_flt(uint32_t a) +{ + CPU_FloatU fd, fa; + + fa.l = a; + fd.f = int32_to_float32(fa.l, &env->fp_status); + return fd.l; +} + +uint32_t helper_fint(uint32_t a) +{ + CPU_FloatU fa; + uint32_t r; + int flags; + + set_float_exception_flags(0, &env->fp_status); + fa.l = a; + r = float32_to_int32(fa.f, &env->fp_status); + flags = get_float_exception_flags(&env->fp_status); + update_fpu_flags(flags); + + return r; +} + +uint32_t helper_fsqrt(uint32_t a) +{ + CPU_FloatU fd, fa; + int flags; + + set_float_exception_flags(0, &env->fp_status); + fa.l = a; + fd.l = float32_sqrt(fa.f, &env->fp_status); + flags = get_float_exception_flags(&env->fp_status); + update_fpu_flags(flags); + + return fd.l; +} + uint32_t helper_pcmpbf(uint32_t a, uint32_t b) { unsigned int i; @@ -236,12 +452,17 @@ { mmu_write(env, rn, v); } -#endif void do_unassigned_access(target_phys_addr_t addr, int is_write, int is_exec, int is_asi, int size) { CPUState *saved_env; + + if (!cpu_single_env) { + /* XXX: ??? */ + return; + } + /* XXX: hack to restore env in all cases, even if not called from generated code */ saved_env = env; @@ -267,3 +488,4 @@ } env = saved_env; } +#endif diff -Nru qemu-kvm-0.12.5+noroms/target-microblaze/translate.c qemu-kvm-0.14.1/target-microblaze/translate.c --- qemu-kvm-0.12.5+noroms/target-microblaze/translate.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-microblaze/translate.c 2011-05-11 13:29:46.000000000 +0000 @@ -63,8 +63,7 @@ /* This is the state at translation time. */ typedef struct DisasContext { CPUState *env; - target_ulong pc, ppc; - target_ulong cache_pc; + target_ulong pc; /* Decoder. */ int type_b; @@ -79,9 +78,10 @@ unsigned int clear_imm; int is_jmp; -#define JMP_NOJMP 0 -#define JMP_DIRECT 1 -#define JMP_INDIRECT 2 +#define JMP_NOJMP 0 +#define JMP_DIRECT 1 +#define JMP_DIRECT_CC 2 +#define JMP_INDIRECT 3 unsigned int jmp; uint32_t jmp_pc; @@ -153,6 +153,31 @@ } } +static void read_carry(DisasContext *dc, TCGv d) +{ + tcg_gen_shri_tl(d, cpu_SR[SR_MSR], 31); +} + +static void write_carry(DisasContext *dc, TCGv v) +{ + TCGv t0 = tcg_temp_new(); + tcg_gen_shli_tl(t0, v, 31); + tcg_gen_sari_tl(t0, t0, 31); + tcg_gen_andi_tl(t0, t0, (MSR_C | MSR_CC)); + tcg_gen_andi_tl(cpu_SR[SR_MSR], cpu_SR[SR_MSR], + ~(MSR_C | MSR_CC)); + tcg_gen_or_tl(cpu_SR[SR_MSR], cpu_SR[SR_MSR], t0); + tcg_temp_free(t0); +} + +/* True if ALU operand b is a small immediate that may deserve + faster treatment. */ +static inline int dec_alu_op_b_is_small_imm(DisasContext *dc) +{ + /* Immediate insn without the imm prefix ? */ + return dc->type_b && !(dc->tb_flags & IMM_FLAG); +} + static inline TCGv *dec_alu_op_b(DisasContext *dc) { if (dc->type_b) { @@ -168,6 +193,7 @@ static void dec_add(DisasContext *dc) { unsigned int k, c; + TCGv cf; k = dc->opcode & 4; c = dc->opcode & 2; @@ -176,22 +202,52 @@ dc->type_b ? "i" : "", k ? "k" : "", c ? "c" : "", dc->rd, dc->ra, dc->rb); - if (k && !c && dc->rd) + /* Take care of the easy cases first. */ + if (k) { + /* k - keep carry, no need to update MSR. */ + /* If rd == r0, it's a nop. */ + if (dc->rd) { + tcg_gen_add_tl(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); + + if (c) { + /* c - Add carry into the result. */ + cf = tcg_temp_new(); + + read_carry(dc, cf); + tcg_gen_add_tl(cpu_R[dc->rd], cpu_R[dc->rd], cf); + tcg_temp_free(cf); + } + } + return; + } + + /* From now on, we can assume k is zero. So we need to update MSR. */ + /* Extract carry. */ + cf = tcg_temp_new(); + if (c) { + read_carry(dc, cf); + } else { + tcg_gen_movi_tl(cf, 0); + } + + if (dc->rd) { + TCGv ncf = tcg_temp_new(); + gen_helper_carry(ncf, cpu_R[dc->ra], *(dec_alu_op_b(dc)), cf); tcg_gen_add_tl(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); - else if (dc->rd) - gen_helper_addkc(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc)), - tcg_const_tl(k), tcg_const_tl(c)); - else { - TCGv d = tcg_temp_new(); - gen_helper_addkc(d, cpu_R[dc->ra], *(dec_alu_op_b(dc)), - tcg_const_tl(k), tcg_const_tl(c)); - tcg_temp_free(d); + tcg_gen_add_tl(cpu_R[dc->rd], cpu_R[dc->rd], cf); + write_carry(dc, ncf); + tcg_temp_free(ncf); + } else { + gen_helper_carry(cf, cpu_R[dc->ra], *(dec_alu_op_b(dc)), cf); + write_carry(dc, cf); } + tcg_temp_free(cf); } static void dec_sub(DisasContext *dc) { unsigned int u, cmp, k, c; + TCGv cf, na; u = dc->imm & 2; k = dc->opcode & 4; @@ -206,24 +262,57 @@ else gen_helper_cmp(cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]); } - } else { - LOG_DIS("sub%s%s r%d, r%d r%d\n", - k ? "k" : "", c ? "c" : "", dc->rd, dc->ra, dc->rb); + return; + } - if (!k || c) { - TCGv t; - t = tcg_temp_new(); - if (dc->rd) - gen_helper_subkc(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc)), - tcg_const_tl(k), tcg_const_tl(c)); - else - gen_helper_subkc(t, cpu_R[dc->ra], *(dec_alu_op_b(dc)), - tcg_const_tl(k), tcg_const_tl(c)); - tcg_temp_free(t); - } - else if (dc->rd) + LOG_DIS("sub%s%s r%d, r%d r%d\n", + k ? "k" : "", c ? "c" : "", dc->rd, dc->ra, dc->rb); + + /* Take care of the easy cases first. */ + if (k) { + /* k - keep carry, no need to update MSR. */ + /* If rd == r0, it's a nop. */ + if (dc->rd) { tcg_gen_sub_tl(cpu_R[dc->rd], *(dec_alu_op_b(dc)), cpu_R[dc->ra]); + + if (c) { + /* c - Add carry into the result. */ + cf = tcg_temp_new(); + + read_carry(dc, cf); + tcg_gen_add_tl(cpu_R[dc->rd], cpu_R[dc->rd], cf); + tcg_temp_free(cf); + } + } + return; } + + /* From now on, we can assume k is zero. So we need to update MSR. */ + /* Extract carry. And complement a into na. */ + cf = tcg_temp_new(); + na = tcg_temp_new(); + if (c) { + read_carry(dc, cf); + } else { + tcg_gen_movi_tl(cf, 1); + } + + /* d = b + ~a + c. carry defaults to 1. */ + tcg_gen_not_tl(na, cpu_R[dc->ra]); + + if (dc->rd) { + TCGv ncf = tcg_temp_new(); + gen_helper_carry(ncf, na, *(dec_alu_op_b(dc)), cf); + tcg_gen_add_tl(cpu_R[dc->rd], na, *(dec_alu_op_b(dc))); + tcg_gen_add_tl(cpu_R[dc->rd], cpu_R[dc->rd], cf); + write_carry(dc, ncf); + tcg_temp_free(ncf); + } else { + gen_helper_carry(cf, na, *(dec_alu_op_b(dc)), cf); + write_carry(dc, cf); + } + tcg_temp_free(cf); + tcg_temp_free(na); } static void dec_pattern(DisasContext *dc) @@ -329,25 +418,6 @@ tcg_gen_xor_tl(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); } -static void read_carry(DisasContext *dc, TCGv d) -{ - tcg_gen_shri_tl(d, cpu_SR[SR_MSR], 31); -} - -static void write_carry(DisasContext *dc, TCGv v) -{ - TCGv t0 = tcg_temp_new(); - tcg_gen_shli_tl(t0, v, 31); - tcg_gen_sari_tl(t0, t0, 31); - tcg_gen_mov_tl(env_debug, t0); - tcg_gen_andi_tl(t0, t0, (MSR_C | MSR_CC)); - tcg_gen_andi_tl(cpu_SR[SR_MSR], cpu_SR[SR_MSR], - ~(MSR_C | MSR_CC)); - tcg_gen_or_tl(cpu_SR[SR_MSR], cpu_SR[SR_MSR], t0); - tcg_temp_free(t0); -} - - static inline void msr_read(DisasContext *dc, TCGv d) { tcg_gen_mov_tl(d, cpu_SR[SR_MSR]); @@ -450,7 +520,7 @@ tcg_gen_mov_tl(cpu_SR[SR_ESR], cpu_R[dc->ra]); break; case 0x7: - /* Ignored at the moment. */ + tcg_gen_andi_tl(cpu_SR[SR_FSR], cpu_R[dc->ra], 31); break; default: cpu_abort(dc->env, "unknown mts reg %x\n", sr); @@ -473,7 +543,7 @@ tcg_gen_mov_tl(cpu_R[dc->rd], cpu_SR[SR_ESR]); break; case 0x7: - tcg_gen_movi_tl(cpu_R[dc->rd], 0); + tcg_gen_mov_tl(cpu_R[dc->rd], cpu_SR[SR_FSR]); break; case 0xb: tcg_gen_mov_tl(cpu_R[dc->rd], cpu_SR[SR_BTR]); @@ -713,6 +783,9 @@ tcg_gen_ext16s_i32(cpu_R[dc->rd], cpu_R[dc->ra]); break; case 0x64: + case 0x66: + case 0x74: + case 0x76: /* wdc. */ LOG_DIS("wdc r%d\n", dc->ra); if ((dc->tb_flags & MSR_EE_FLAG) @@ -741,10 +814,12 @@ static inline void sync_jmpstate(DisasContext *dc) { - if (dc->jmp == JMP_DIRECT) { - dc->jmp = JMP_INDIRECT; + if (dc->jmp == JMP_DIRECT || dc->jmp == JMP_DIRECT_CC) { + if (dc->jmp == JMP_DIRECT) { tcg_gen_movi_tl(env_btaken, 1); - tcg_gen_movi_tl(env_btarget, dc->jmp_pc); + } + dc->jmp = JMP_INDIRECT; + tcg_gen_movi_tl(env_btarget, dc->jmp_pc); } } @@ -775,8 +850,15 @@ { unsigned int extimm = dc->tb_flags & IMM_FLAG; - /* Treat the fast cases first. */ + /* Treat the common cases first. */ if (!dc->type_b) { + /* If any of the regs is r0, return a ptr to the other. */ + if (dc->ra == 0) { + return &cpu_R[dc->rb]; + } else if (dc->rb == 0) { + return &cpu_R[dc->ra]; + } + *t = tcg_temp_new(); tcg_gen_add_tl(*t, cpu_R[dc->ra], cpu_R[dc->rb]); return t; @@ -797,12 +879,35 @@ return t; } +static inline void dec_byteswap(DisasContext *dc, TCGv dst, TCGv src, int size) +{ + if (size == 4) { + tcg_gen_bswap32_tl(dst, src); + } else if (size == 2) { + TCGv t = tcg_temp_new(); + + /* bswap16 assumes the high bits are zero. */ + tcg_gen_andi_tl(t, src, 0xffff); + tcg_gen_bswap16_tl(dst, t); + tcg_temp_free(t); + } else { + /* Ignore. + cpu_abort(dc->env, "Invalid ldst byteswap size %d\n", size); + */ + } +} + static void dec_load(DisasContext *dc) { TCGv t, *addr; - unsigned int size; + unsigned int size, rev = 0; size = 1 << (dc->opcode & 3); + + if (!dc->type_b) { + rev = (dc->ir >> 9) & 1; + } + if (size > 4 && (dc->tb_flags & MSR_EE_FLAG) && (dc->env->pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK)) { tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); @@ -810,10 +915,62 @@ return; } - LOG_DIS("l %x %d\n", dc->opcode, size); + LOG_DIS("l%d%s%s\n", size, dc->type_b ? "i" : "", rev ? "r" : ""); + t_sync_flags(dc); addr = compute_ldst_addr(dc, &t); + /* + * When doing reverse accesses we need to do two things. + * + * 1. Reverse the address wrt endianess. + * 2. Byteswap the data lanes on the way back into the CPU core. + */ + if (rev && size != 4) { + /* Endian reverse the address. t is addr. */ + switch (size) { + case 1: + { + /* 00 -> 11 + 01 -> 10 + 10 -> 10 + 11 -> 00 */ + TCGv low = tcg_temp_new(); + + /* Force addr into the temp. */ + if (addr != &t) { + t = tcg_temp_new(); + tcg_gen_mov_tl(t, *addr); + addr = &t; + } + + tcg_gen_andi_tl(low, t, 3); + tcg_gen_sub_tl(low, tcg_const_tl(3), low); + tcg_gen_andi_tl(t, t, ~3); + tcg_gen_or_tl(t, t, low); + tcg_gen_mov_tl(env_imm, t); + tcg_temp_free(low); + break; + } + + case 2: + /* 00 -> 10 + 10 -> 00. */ + /* Force addr into the temp. */ + if (addr != &t) { + t = tcg_temp_new(); + tcg_gen_xori_tl(t, *addr, 2); + addr = &t; + } else { + tcg_gen_xori_tl(t, t, 2); + } + break; + default: + cpu_abort(dc->env, "Invalid reverse size\n"); + break; + } + } + /* If we get a fault on a dslot, the jmpstate better be in sync. */ sync_jmpstate(dc); @@ -832,13 +989,22 @@ tcg_gen_movi_tl(cpu_SR[SR_PC], dc->pc); gen_helper_memalign(*addr, tcg_const_tl(dc->rd), tcg_const_tl(0), tcg_const_tl(size - 1)); - if (dc->rd) - tcg_gen_mov_tl(cpu_R[dc->rd], v); + if (dc->rd) { + if (rev) { + dec_byteswap(dc, cpu_R[dc->rd], v, size); + } else { + tcg_gen_mov_tl(cpu_R[dc->rd], v); + } + } tcg_temp_free(v); } else { if (dc->rd) { gen_load(dc, cpu_R[dc->rd], *addr, size); + if (rev) { + dec_byteswap(dc, cpu_R[dc->rd], cpu_R[dc->rd], size); + } } else { + /* We are loading into r0, no need to reverse. */ gen_load(dc, env_imm, *addr, size); } } @@ -865,9 +1031,12 @@ static void dec_store(DisasContext *dc) { TCGv t, *addr; - unsigned int size; + unsigned int size, rev = 0; size = 1 << (dc->opcode & 3); + if (!dc->type_b) { + rev = (dc->ir >> 9) & 1; + } if (size > 4 && (dc->tb_flags & MSR_EE_FLAG) && (dc->env->pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK)) { @@ -876,19 +1045,83 @@ return; } - LOG_DIS("s%d%s\n", size, dc->type_b ? "i" : ""); + LOG_DIS("s%d%s%s\n", size, dc->type_b ? "i" : "", rev ? "r" : ""); t_sync_flags(dc); /* If we get a fault on a dslot, the jmpstate better be in sync. */ sync_jmpstate(dc); addr = compute_ldst_addr(dc, &t); - gen_store(dc, *addr, cpu_R[dc->rd], size); + if (rev && size != 4) { + /* Endian reverse the address. t is addr. */ + switch (size) { + case 1: + { + /* 00 -> 11 + 01 -> 10 + 10 -> 10 + 11 -> 00 */ + TCGv low = tcg_temp_new(); + + /* Force addr into the temp. */ + if (addr != &t) { + t = tcg_temp_new(); + tcg_gen_mov_tl(t, *addr); + addr = &t; + } + + tcg_gen_andi_tl(low, t, 3); + tcg_gen_sub_tl(low, tcg_const_tl(3), low); + tcg_gen_andi_tl(t, t, ~3); + tcg_gen_or_tl(t, t, low); + tcg_gen_mov_tl(env_imm, t); + tcg_temp_free(low); + break; + } + + case 2: + /* 00 -> 10 + 10 -> 00. */ + /* Force addr into the temp. */ + if (addr != &t) { + t = tcg_temp_new(); + tcg_gen_xori_tl(t, *addr, 2); + addr = &t; + } else { + tcg_gen_xori_tl(t, t, 2); + } + break; + default: + cpu_abort(dc->env, "Invalid reverse size\n"); + break; + } + + if (size != 1) { + TCGv bs_data = tcg_temp_new(); + dec_byteswap(dc, bs_data, cpu_R[dc->rd], size); + gen_store(dc, *addr, bs_data, size); + tcg_temp_free(bs_data); + } else { + gen_store(dc, *addr, cpu_R[dc->rd], size); + } + } else { + if (rev) { + TCGv bs_data = tcg_temp_new(); + dec_byteswap(dc, bs_data, cpu_R[dc->rd], size); + gen_store(dc, *addr, bs_data, size); + tcg_temp_free(bs_data); + } else { + gen_store(dc, *addr, cpu_R[dc->rd], size); + } + } /* Verify alignment if needed. */ if ((dc->env->pvr.regs[2] & PVR2_UNALIGNED_EXC_MASK) && size > 1) { tcg_gen_movi_tl(cpu_SR[SR_PC], dc->pc); /* FIXME: if the alignment is wrong, we should restore the value - * in memory. + * in memory. One possible way to acheive this is to probe + * the MMU prior to the memaccess, thay way we could put + * the alignment checks in between the probe and the mem + * access. */ gen_helper_memalign(*addr, tcg_const_tl(dc->rd), tcg_const_tl(1), tcg_const_tl(size - 1)); @@ -901,50 +1134,24 @@ static inline void eval_cc(DisasContext *dc, unsigned int cc, TCGv d, TCGv a, TCGv b) { - int l1; - switch (cc) { case CC_EQ: - l1 = gen_new_label(); - tcg_gen_movi_tl(env_btaken, 1); - tcg_gen_brcond_tl(TCG_COND_EQ, a, b, l1); - tcg_gen_movi_tl(env_btaken, 0); - gen_set_label(l1); + tcg_gen_setcond_tl(TCG_COND_EQ, d, a, b); break; case CC_NE: - l1 = gen_new_label(); - tcg_gen_movi_tl(env_btaken, 1); - tcg_gen_brcond_tl(TCG_COND_NE, a, b, l1); - tcg_gen_movi_tl(env_btaken, 0); - gen_set_label(l1); + tcg_gen_setcond_tl(TCG_COND_NE, d, a, b); break; case CC_LT: - l1 = gen_new_label(); - tcg_gen_movi_tl(env_btaken, 1); - tcg_gen_brcond_tl(TCG_COND_LT, a, b, l1); - tcg_gen_movi_tl(env_btaken, 0); - gen_set_label(l1); + tcg_gen_setcond_tl(TCG_COND_LT, d, a, b); break; case CC_LE: - l1 = gen_new_label(); - tcg_gen_movi_tl(env_btaken, 1); - tcg_gen_brcond_tl(TCG_COND_LE, a, b, l1); - tcg_gen_movi_tl(env_btaken, 0); - gen_set_label(l1); + tcg_gen_setcond_tl(TCG_COND_LE, d, a, b); break; case CC_GE: - l1 = gen_new_label(); - tcg_gen_movi_tl(env_btaken, 1); - tcg_gen_brcond_tl(TCG_COND_GE, a, b, l1); - tcg_gen_movi_tl(env_btaken, 0); - gen_set_label(l1); + tcg_gen_setcond_tl(TCG_COND_GE, d, a, b); break; case CC_GT: - l1 = gen_new_label(); - tcg_gen_movi_tl(env_btaken, 1); - tcg_gen_brcond_tl(TCG_COND_GT, a, b, l1); - tcg_gen_movi_tl(env_btaken, 0); - gen_set_label(l1); + tcg_gen_setcond_tl(TCG_COND_GT, d, a, b); break; default: cpu_abort(dc->env, "Unknown condition code %x.\n", cc); @@ -981,15 +1188,24 @@ cpu_env, offsetof(CPUState, bimm)); } - tcg_gen_movi_tl(env_btarget, dc->pc); - tcg_gen_add_tl(env_btarget, env_btarget, *(dec_alu_op_b(dc))); + if (dec_alu_op_b_is_small_imm(dc)) { + int32_t offset = (int32_t)((int16_t)dc->imm); /* sign-extend. */ + + tcg_gen_movi_tl(env_btarget, dc->pc + offset); + dc->jmp = JMP_DIRECT_CC; + dc->jmp_pc = dc->pc + offset; + } else { + dc->jmp = JMP_INDIRECT; + tcg_gen_movi_tl(env_btarget, dc->pc); + tcg_gen_add_tl(env_btarget, env_btarget, *(dec_alu_op_b(dc))); + } eval_cc(dc, cc, env_btaken, cpu_R[dc->ra], tcg_const_tl(0)); - dc->jmp = JMP_INDIRECT; } static void dec_br(DisasContext *dc) { unsigned int dslot, link, abs; + int mem_index = cpu_mmu_index(dc->env); dslot = dc->ir & (1 << 20); abs = dc->ir & (1 << 19); @@ -1013,19 +1229,27 @@ if (abs) { tcg_gen_movi_tl(env_btaken, 1); tcg_gen_mov_tl(env_btarget, *(dec_alu_op_b(dc))); - if (link && !(dc->tb_flags & IMM_FLAG) - && (dc->imm == 8 || dc->imm == 0x18)) - t_gen_raise_exception(dc, EXCP_BREAK); - if (dc->imm == 0) - t_gen_raise_exception(dc, EXCP_DEBUG); + if (link && !dslot) { + if (!(dc->tb_flags & IMM_FLAG) && (dc->imm == 8 || dc->imm == 0x18)) + t_gen_raise_exception(dc, EXCP_BREAK); + if (dc->imm == 0) { + if ((dc->tb_flags & MSR_EE_FLAG) && mem_index == MMU_USER_IDX) { + tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); + t_gen_raise_exception(dc, EXCP_HW_EXCP); + return; + } + + t_gen_raise_exception(dc, EXCP_DEBUG); + } + } } else { - if (dc->tb_flags & IMM_FLAG) { + if (dec_alu_op_b_is_small_imm(dc)) { + dc->jmp = JMP_DIRECT; + dc->jmp_pc = dc->pc + (int32_t)((int16_t)dc->imm); + } else { tcg_gen_movi_tl(env_btaken, 1); tcg_gen_movi_tl(env_btarget, dc->pc); tcg_gen_add_tl(env_btarget, env_btarget, *(dec_alu_op_b(dc))); - } else { - dc->jmp = JMP_DIRECT; - dc->jmp_pc = dc->pc + (int32_t)((int16_t)dc->imm); } } } @@ -1124,22 +1348,120 @@ } else LOG_DIS("rts ir=%x\n", dc->ir); + dc->jmp = JMP_INDIRECT; tcg_gen_movi_tl(env_btaken, 1); tcg_gen_add_tl(env_btarget, cpu_R[dc->ra], *(dec_alu_op_b(dc))); } +static int dec_check_fpuv2(DisasContext *dc) +{ + int r; + + r = dc->env->pvr.regs[2] & PVR2_USE_FPU2_MASK; + + if (!r && (dc->tb_flags & MSR_EE_FLAG)) { + tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_FPU); + t_gen_raise_exception(dc, EXCP_HW_EXCP); + } + return r; +} + static void dec_fpu(DisasContext *dc) { + unsigned int fpu_insn; + if ((dc->tb_flags & MSR_EE_FLAG) && (dc->env->pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK) && !((dc->env->pvr.regs[2] & PVR2_USE_FPU_MASK))) { - tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_FPU); + tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); t_gen_raise_exception(dc, EXCP_HW_EXCP); return; } - qemu_log ("unimplemented FPU insn pc=%x opc=%x\n", dc->pc, dc->opcode); - dc->abort_at_next_insn = 1; + fpu_insn = (dc->ir >> 7) & 7; + + switch (fpu_insn) { + case 0: + gen_helper_fadd(cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]); + break; + + case 1: + gen_helper_frsub(cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]); + break; + + case 2: + gen_helper_fmul(cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]); + break; + + case 3: + gen_helper_fdiv(cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]); + break; + + case 4: + switch ((dc->ir >> 4) & 7) { + case 0: + gen_helper_fcmp_un(cpu_R[dc->rd], + cpu_R[dc->ra], cpu_R[dc->rb]); + break; + case 1: + gen_helper_fcmp_lt(cpu_R[dc->rd], + cpu_R[dc->ra], cpu_R[dc->rb]); + break; + case 2: + gen_helper_fcmp_eq(cpu_R[dc->rd], + cpu_R[dc->ra], cpu_R[dc->rb]); + break; + case 3: + gen_helper_fcmp_le(cpu_R[dc->rd], + cpu_R[dc->ra], cpu_R[dc->rb]); + break; + case 4: + gen_helper_fcmp_gt(cpu_R[dc->rd], + cpu_R[dc->ra], cpu_R[dc->rb]); + break; + case 5: + gen_helper_fcmp_ne(cpu_R[dc->rd], + cpu_R[dc->ra], cpu_R[dc->rb]); + break; + case 6: + gen_helper_fcmp_ge(cpu_R[dc->rd], + cpu_R[dc->ra], cpu_R[dc->rb]); + break; + default: + qemu_log ("unimplemented fcmp fpu_insn=%x pc=%x opc=%x\n", + fpu_insn, dc->pc, dc->opcode); + dc->abort_at_next_insn = 1; + break; + } + break; + + case 5: + if (!dec_check_fpuv2(dc)) { + return; + } + gen_helper_flt(cpu_R[dc->rd], cpu_R[dc->ra]); + break; + + case 6: + if (!dec_check_fpuv2(dc)) { + return; + } + gen_helper_fint(cpu_R[dc->rd], cpu_R[dc->ra]); + break; + + case 7: + if (!dec_check_fpuv2(dc)) { + return; + } + gen_helper_fsqrt(cpu_R[dc->rd], cpu_R[dc->ra]); + break; + + default: + qemu_log ("unimplemented FPU insn fpu_insn=%x pc=%x opc=%x\n", + fpu_insn, dc->pc, dc->opcode); + dc->abort_at_next_insn = 1; + break; + } } static void dec_null(DisasContext *dc) @@ -1267,9 +1589,10 @@ dc->is_jmp = DISAS_NEXT; dc->jmp = 0; dc->delayed_branch = !!(dc->tb_flags & D_FLAG); - dc->ppc = pc_start; + if (dc->delayed_branch) { + dc->jmp = JMP_INDIRECT; + } dc->pc = pc_start; - dc->cache_pc = -1; dc->singlestep_enabled = env->singlestep_enabled; dc->cpustate_changed = 0; dc->abort_at_next_insn = 0; @@ -1325,7 +1648,6 @@ decode(dc); if (dc->clear_imm) dc->tb_flags &= ~IMM_FLAG; - dc->ppc = dc->pc; dc->pc += 4; num_insns++; @@ -1341,9 +1663,25 @@ /* Clear the delay slot flag. */ dc->tb_flags &= ~D_FLAG; /* If it is a direct jump, try direct chaining. */ - if (dc->jmp != JMP_DIRECT) { + if (dc->jmp == JMP_INDIRECT) { eval_cond_jmp(dc, env_btarget, tcg_const_tl(dc->pc)); dc->is_jmp = DISAS_JUMP; + } else if (dc->jmp == JMP_DIRECT) { + t_sync_flags(dc); + gen_goto_tb(dc, 0, dc->jmp_pc); + dc->is_jmp = DISAS_TB_JUMP; + } else if (dc->jmp == JMP_DIRECT_CC) { + int l1; + + t_sync_flags(dc); + l1 = gen_new_label(); + /* Conditional jmp. */ + tcg_gen_brcondi_tl(TCG_COND_NE, env_btaken, 0, l1); + gen_goto_tb(dc, 1, dc->pc); + gen_set_label(l1); + gen_goto_tb(dc, 0, dc->jmp_pc); + + dc->is_jmp = DISAS_TB_JUMP; } break; } @@ -1357,7 +1695,7 @@ && num_insns < max_insns); npc = dc->pc; - if (dc->jmp == JMP_DIRECT) { + if (dc->jmp == JMP_DIRECT || dc->jmp == JMP_DIRECT_CC) { if (dc->tb_flags & D_FLAG) { dc->is_jmp = DISAS_UPDATE; tcg_gen_movi_tl(cpu_SR[SR_PC], npc); @@ -1416,7 +1754,7 @@ #if DISAS_GNU log_target_disas(pc_start, dc->pc - pc_start, 0); #endif - qemu_log("\nisize=%d osize=%zd\n", + qemu_log("\nisize=%d osize=%td\n", dc->pc - pc_start, gen_opc_ptr - gen_opc_buf); } #endif @@ -1434,8 +1772,7 @@ gen_intermediate_code_internal(env, tb, 1); } -void cpu_dump_state (CPUState *env, FILE *f, - int (*cpu_fprintf)(FILE *f, const char *fmt, ...), +void cpu_dump_state (CPUState *env, FILE *f, fprintf_function cpu_fprintf, int flags) { int i; @@ -1445,13 +1782,16 @@ cpu_fprintf(f, "IN: PC=%x %s\n", env->sregs[SR_PC], lookup_symbol(env->sregs[SR_PC])); - cpu_fprintf(f, "rmsr=%x resr=%x debug[%x] imm=%x iflags=%x\n", - env->sregs[SR_MSR], env->sregs[SR_ESR], - env->debug, env->imm, env->iflags); - cpu_fprintf(f, "btaken=%d btarget=%x mode=%s(saved=%s)\n", + cpu_fprintf(f, "rmsr=%x resr=%x rear=%x debug=%x imm=%x iflags=%x fsr=%x\n", + env->sregs[SR_MSR], env->sregs[SR_ESR], env->sregs[SR_EAR], + env->debug, env->imm, env->iflags, env->sregs[SR_FSR]); + cpu_fprintf(f, "btaken=%d btarget=%x mode=%s(saved=%s) eip=%d ie=%d\n", env->btaken, env->btarget, (env->sregs[SR_MSR] & MSR_UM) ? "user" : "kernel", - (env->sregs[SR_MSR] & MSR_UMS) ? "user" : "kernel"); + (env->sregs[SR_MSR] & MSR_UMS) ? "user" : "kernel", + (env->sregs[SR_MSR] & MSR_EIP), + (env->sregs[SR_MSR] & MSR_IE)); + for (i = 0; i < 32; i++) { cpu_fprintf(f, "r%2.2d=%8.8x ", i, env->regs[i]); if ((i + 1) % 4 == 0) @@ -1470,7 +1810,7 @@ cpu_exec_init(env); cpu_reset(env); - + set_float_rounding_mode(float_round_nearest_even, &env->fp_status); if (tcg_initialized) return env; @@ -1539,15 +1879,19 @@ | PVR2_USE_DIV_MASK \ | PVR2_USE_HW_MUL_MASK \ | PVR2_USE_MUL64_MASK \ + | PVR2_USE_FPU_MASK \ + | PVR2_USE_FPU2_MASK \ + | PVR2_FPU_EXC_MASK \ | 0; env->pvr.regs[10] = 0x0c000000; /* Default to spartan 3a dsp family. */ env->pvr.regs[11] = PVR11_USE_MMU | (16 << 17); - env->sregs[SR_MSR] = 0; #if defined(CONFIG_USER_ONLY) /* start in user mode with interrupts enabled. */ + env->sregs[SR_MSR] = MSR_EE | MSR_IE | MSR_VM | MSR_UM; env->pvr.regs[10] = 0x0c000000; /* Spartan 3a dsp. */ #else + env->sregs[SR_MSR] = 0; mmu_init(&env->mmu); env->mmu.c_mmu = 3; env->mmu.c_mmu_tlb_access = 3; diff -Nru qemu-kvm-0.12.5+noroms/target-mips/cpu.h qemu-kvm-0.14.1/target-mips/cpu.h --- qemu-kvm-0.12.5+noroms/target-mips/cpu.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-mips/cpu.h 2011-05-11 13:29:46.000000000 +0000 @@ -8,6 +8,7 @@ #define CPUState struct CPUMIPSState #include "config.h" +#include "qemu-common.h" #include "mips-defs.h" #include "cpu-defs.h" #include "softfloat.h" @@ -36,6 +37,7 @@ target_ulong PFN[2]; }; +#if !defined(CONFIG_USER_ONLY) typedef struct CPUMIPSTLBContext CPUMIPSTLBContext; struct CPUMIPSTLBContext { uint32_t nb_tlb; @@ -51,6 +53,7 @@ } r4k; } mmu; }; +#endif typedef union fpr_t fpr_t; union fpr_t { @@ -361,6 +364,7 @@ #define CP0C2_SA 0 int32_t CP0_Config3; #define CP0C3_M 31 +#define CP0C3_ISA_ON_EXC 16 #define CP0C3_DSPP 10 #define CP0C3_LPA 7 #define CP0C3_VEIC 6 @@ -416,35 +420,44 @@ int error_code; uint32_t hflags; /* CPU State */ /* TMASK defines different execution modes */ -#define MIPS_HFLAG_TMASK 0x03FF -#define MIPS_HFLAG_MODE 0x0007 /* execution modes */ +#define MIPS_HFLAG_TMASK 0x007FF +#define MIPS_HFLAG_MODE 0x00007 /* execution modes */ /* The KSU flags must be the lowest bits in hflags. The flag order must be the same as defined for CP0 Status. This allows to use the bits as the value of mmu_idx. */ -#define MIPS_HFLAG_KSU 0x0003 /* kernel/supervisor/user mode mask */ -#define MIPS_HFLAG_UM 0x0002 /* user mode flag */ -#define MIPS_HFLAG_SM 0x0001 /* supervisor mode flag */ -#define MIPS_HFLAG_KM 0x0000 /* kernel mode flag */ -#define MIPS_HFLAG_DM 0x0004 /* Debug mode */ -#define MIPS_HFLAG_64 0x0008 /* 64-bit instructions enabled */ -#define MIPS_HFLAG_CP0 0x0010 /* CP0 enabled */ -#define MIPS_HFLAG_FPU 0x0020 /* FPU enabled */ -#define MIPS_HFLAG_F64 0x0040 /* 64-bit FPU enabled */ +#define MIPS_HFLAG_KSU 0x00003 /* kernel/supervisor/user mode mask */ +#define MIPS_HFLAG_UM 0x00002 /* user mode flag */ +#define MIPS_HFLAG_SM 0x00001 /* supervisor mode flag */ +#define MIPS_HFLAG_KM 0x00000 /* kernel mode flag */ +#define MIPS_HFLAG_DM 0x00004 /* Debug mode */ +#define MIPS_HFLAG_64 0x00008 /* 64-bit instructions enabled */ +#define MIPS_HFLAG_CP0 0x00010 /* CP0 enabled */ +#define MIPS_HFLAG_FPU 0x00020 /* FPU enabled */ +#define MIPS_HFLAG_F64 0x00040 /* 64-bit FPU enabled */ /* True if the MIPS IV COP1X instructions can be used. This also controls the non-COP1X instructions RECIP.S, RECIP.D, RSQRT.S and RSQRT.D. */ -#define MIPS_HFLAG_COP1X 0x0080 /* COP1X instructions enabled */ -#define MIPS_HFLAG_RE 0x0100 /* Reversed endianness */ -#define MIPS_HFLAG_UX 0x0200 /* 64-bit user mode */ +#define MIPS_HFLAG_COP1X 0x00080 /* COP1X instructions enabled */ +#define MIPS_HFLAG_RE 0x00100 /* Reversed endianness */ +#define MIPS_HFLAG_UX 0x00200 /* 64-bit user mode */ +#define MIPS_HFLAG_M16 0x00400 /* MIPS16 mode flag */ +#define MIPS_HFLAG_M16_SHIFT 10 /* If translation is interrupted between the branch instruction and * the delay slot, record what type of branch it is so that we can * resume translation properly. It might be possible to reduce * this from three bits to two. */ -#define MIPS_HFLAG_BMASK 0x1C00 -#define MIPS_HFLAG_B 0x0400 /* Unconditional branch */ -#define MIPS_HFLAG_BC 0x0800 /* Conditional branch */ -#define MIPS_HFLAG_BL 0x0C00 /* Likely branch */ -#define MIPS_HFLAG_BR 0x1000 /* branch to register (can't link TB) */ +#define MIPS_HFLAG_BMASK_BASE 0x03800 +#define MIPS_HFLAG_B 0x00800 /* Unconditional branch */ +#define MIPS_HFLAG_BC 0x01000 /* Conditional branch */ +#define MIPS_HFLAG_BL 0x01800 /* Likely branch */ +#define MIPS_HFLAG_BR 0x02000 /* branch to register (can't link TB) */ + /* Extra flags about the current pending branch. */ +#define MIPS_HFLAG_BMASK_EXT 0x3C000 +#define MIPS_HFLAG_B16 0x04000 /* branch instruction was 16 bits */ +#define MIPS_HFLAG_BDS16 0x08000 /* branch requires 16-bit delay slot */ +#define MIPS_HFLAG_BDS32 0x10000 /* branch requires 32-bit delay slot */ +#define MIPS_HFLAG_BX 0x20000 /* branch exchanges execution mode */ +#define MIPS_HFLAG_BMASK (MIPS_HFLAG_BMASK_BASE | MIPS_HFLAG_BMASK_EXT) target_ulong btarget; /* Jump / branch target */ target_ulong bcond; /* Branch condition (if needed) */ @@ -459,13 +472,16 @@ CPU_COMMON CPUMIPSMVPContext *mvp; +#if !defined(CONFIG_USER_ONLY) CPUMIPSTLBContext *tlb; +#endif const mips_def_t *cpu_model; void *irq[8]; struct QEMUTimer *timer; /* Internal timer */ }; +#if !defined(CONFIG_USER_ONLY) int no_mmu_map_address (CPUMIPSState *env, target_phys_addr_t *physical, int *prot, target_ulong address, int rw, int access_type); int fixed_mmu_map_address (CPUMIPSState *env, target_phys_addr_t *physical, int *prot, @@ -476,10 +492,12 @@ void r4k_helper_tlbwr (void); void r4k_helper_tlbp (void); void r4k_helper_tlbr (void); -void mips_cpu_list (FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...)); void do_unassigned_access(target_phys_addr_t addr, int is_write, int is_exec, int unused, int size); +#endif + +void mips_cpu_list (FILE *f, fprintf_function cpu_fprintf); #define cpu_init cpu_mips_init #define cpu_exec cpu_mips_exec @@ -508,8 +526,38 @@ env->active_tc.gpr[2] = 0; } +static inline int cpu_mips_hw_interrupts_pending(CPUState *env) +{ + int32_t pending; + int32_t status; + int r; + + if (!(env->CP0_Status & (1 << CP0St_IE)) || + (env->CP0_Status & (1 << CP0St_EXL)) || + (env->CP0_Status & (1 << CP0St_ERL)) || + (env->hflags & MIPS_HFLAG_DM)) { + /* Interrupts are disabled */ + return 0; + } + + pending = env->CP0_Cause & CP0Ca_IP_mask; + status = env->CP0_Status & CP0Ca_IP_mask; + + if (env->CP0_Config3 & (1 << CP0C3_VEIC)) { + /* A MIPS configured with a vectorizing external interrupt controller + will feed a vector into the Cause pending lines. The core treats + the status lines as a vector level, not as indiviual masks. */ + r = pending > status; + } else { + /* A MIPS configured with compatibility or VInt (Vectored Interrupts) + treats the pending lines as individual interrupt lines, the status + lines are individual masks. */ + r = pending & status; + } + return r; +} + #include "cpu-all.h" -#include "exec-all.h" /* Memory access type : * may be needed for precise access rights control and precise exceptions. @@ -582,23 +630,18 @@ void cpu_mips_stop_count(CPUState *env); /* mips_int.c */ -void cpu_mips_update_irq (CPUState *env); +void cpu_mips_soft_irq(CPUState *env, int irq, int level); /* helper.c */ int cpu_mips_handle_mmu_fault (CPUState *env, target_ulong address, int rw, int mmu_idx, int is_softmmu); #define cpu_handle_mmu_fault cpu_mips_handle_mmu_fault void do_interrupt (CPUState *env); +#if !defined(CONFIG_USER_ONLY) void r4k_invalidate_tlb (CPUState *env, int idx, int use_extra); target_phys_addr_t cpu_mips_translate_address (CPUState *env, target_ulong address, int rw); - -static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb) -{ - env->active_tc.PC = tb->pc; - env->hflags &= ~MIPS_HFLAG_BMASK; - env->hflags |= tb->flags & MIPS_HFLAG_BMASK; -} +#endif static inline void cpu_get_tb_cpu_state(CPUState *env, target_ulong *pc, target_ulong *cs_base, int *flags) diff -Nru qemu-kvm-0.12.5+noroms/target-mips/exec.h qemu-kvm-0.14.1/target-mips/exec.h --- qemu-kvm-0.12.5+noroms/target-mips/exec.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-mips/exec.h 2011-05-11 13:29:46.000000000 +0000 @@ -17,28 +17,24 @@ #include "softmmu_exec.h" #endif /* !defined(CONFIG_USER_ONLY) */ -void dump_fpu(CPUState *env); -void fpu_dump_state(CPUState *env, FILE *f, - int (*fpu_fprintf)(FILE *f, const char *fmt, ...), - int flags); - -void cpu_mips_clock_init (CPUState *env); -void cpu_mips_tlb_flush (CPUState *env, int flush_global); - -static inline void env_to_regs(void) +static inline int cpu_has_work(CPUState *env) { -} + int has_work = 0; -static inline void regs_to_env(void) -{ -} + /* It is implementation dependent if non-enabled interrupts + wake-up the CPU, however most of the implementations only + check for interrupts that can be taken. */ + if ((env->interrupt_request & CPU_INTERRUPT_HARD) && + cpu_mips_hw_interrupts_pending(env)) { + has_work = 1; + } -static inline int cpu_has_work(CPUState *env) -{ - return (env->interrupt_request & - (CPU_INTERRUPT_HARD | CPU_INTERRUPT_TIMER)); -} + if (env->interrupt_request & CPU_INTERRUPT_TIMER) { + has_work = 1; + } + return has_work; +} static inline int cpu_halted(CPUState *env) { @@ -92,4 +88,11 @@ } } +static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb) +{ + env->active_tc.PC = tb->pc; + env->hflags &= ~MIPS_HFLAG_BMASK; + env->hflags |= tb->flags & MIPS_HFLAG_BMASK; +} + #endif /* !defined(__QEMU_MIPS_EXEC_H__) */ diff -Nru qemu-kvm-0.12.5+noroms/target-mips/helper.c qemu-kvm-0.14.1/target-mips/helper.c --- qemu-kvm-0.12.5+noroms/target-mips/helper.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-mips/helper.c 2011-05-11 13:29:46.000000000 +0000 @@ -34,6 +34,8 @@ TLBRET_MATCH = 0 }; +#if !defined(CONFIG_USER_ONLY) + /* no MMU emulation */ int no_mmu_map_address (CPUState *env, target_phys_addr_t *physical, int *prot, target_ulong address, int rw, int access_type) @@ -98,7 +100,6 @@ return TLBRET_NOMATCH; } -#if !defined(CONFIG_USER_ONLY) static int get_physical_address (CPUState *env, target_phys_addr_t *physical, int *prot, target_ulong address, int rw, int access_type) @@ -253,19 +254,17 @@ env->error_code = error_code; } +#if !defined(CONFIG_USER_ONLY) target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr) { -#if defined(CONFIG_USER_ONLY) - return addr; -#else target_phys_addr_t phys_addr; int prot; if (get_physical_address(env, &phys_addr, &prot, addr, 0, ACCESS_INT) != 0) return -1; return phys_addr; -#endif } +#endif int cpu_mips_handle_mmu_fault (CPUState *env, target_ulong address, int rw, int mmu_idx, int is_softmmu) @@ -297,9 +296,10 @@ qemu_log("%s address=" TARGET_FMT_lx " ret %d physical " TARGET_FMT_plx " prot %d\n", __func__, address, ret, physical, prot); if (ret == TLBRET_MATCH) { - ret = tlb_set_page(env, address & TARGET_PAGE_MASK, - physical & TARGET_PAGE_MASK, prot, - mmu_idx, is_softmmu); + tlb_set_page(env, address & TARGET_PAGE_MASK, + physical & TARGET_PAGE_MASK, prot | PAGE_EXEC, + mmu_idx, TARGET_PAGE_SIZE); + ret = 0; } else if (ret < 0) #endif { @@ -369,6 +369,36 @@ [EXCP_CACHE] = "cache error", }; +#if !defined(CONFIG_USER_ONLY) +static target_ulong exception_resume_pc (CPUState *env) +{ + target_ulong bad_pc; + target_ulong isa_mode; + + isa_mode = !!(env->hflags & MIPS_HFLAG_M16); + bad_pc = env->active_tc.PC | isa_mode; + if (env->hflags & MIPS_HFLAG_BMASK) { + /* If the exception was raised from a delay slot, come back to + the jump. */ + bad_pc -= (env->hflags & MIPS_HFLAG_B16 ? 2 : 4); + } + + return bad_pc; +} + +static void set_hflags_for_handler (CPUState *env) +{ + /* Exception handlers are entered in 32-bit mode. */ + env->hflags &= ~(MIPS_HFLAG_M16); + /* ...except that microMIPS lets you choose. */ + if (env->insn_flags & ASE_MICROMIPS) { + env->hflags |= (!!(env->CP0_Config3 + & (1 << CP0C3_ISA_ON_EXC)) + << MIPS_HFLAG_M16_SHIFT); + } +} +#endif + void do_interrupt (CPUState *env) { #if !defined(CONFIG_USER_ONLY) @@ -396,7 +426,7 @@ resume will always occur on the next instruction (but we assume the pc has always been updated during code translation). */ - env->CP0_DEPC = env->active_tc.PC; + env->CP0_DEPC = env->active_tc.PC | !!(env->hflags & MIPS_HFLAG_M16); goto enter_debug_mode; case EXCP_DINT: env->CP0_Debug |= 1 << CP0DB_DINT; @@ -413,14 +443,8 @@ case EXCP_DDBL: env->CP0_Debug |= 1 << CP0DB_DDBL; set_DEPC: - if (env->hflags & MIPS_HFLAG_BMASK) { - /* If the exception was raised from a delay slot, - come back to the jump. */ - env->CP0_DEPC = env->active_tc.PC - 4; - env->hflags &= ~MIPS_HFLAG_BMASK; - } else { - env->CP0_DEPC = env->active_tc.PC; - } + env->CP0_DEPC = exception_resume_pc(env); + env->hflags &= ~MIPS_HFLAG_BMASK; enter_debug_mode: env->hflags |= MIPS_HFLAG_DM | MIPS_HFLAG_64 | MIPS_HFLAG_CP0; env->hflags &= ~(MIPS_HFLAG_KSU); @@ -428,6 +452,7 @@ if (!(env->CP0_Status & (1 << CP0St_EXL))) env->CP0_Cause &= ~(1 << CP0Ca_BD); env->active_tc.PC = (int32_t)0xBFC00480; + set_hflags_for_handler(env); break; case EXCP_RESET: cpu_reset(env); @@ -439,25 +464,47 @@ case EXCP_NMI: env->CP0_Status |= (1 << CP0St_NMI); set_error_EPC: - if (env->hflags & MIPS_HFLAG_BMASK) { - /* If the exception was raised from a delay slot, - come back to the jump. */ - env->CP0_ErrorEPC = env->active_tc.PC - 4; - env->hflags &= ~MIPS_HFLAG_BMASK; - } else { - env->CP0_ErrorEPC = env->active_tc.PC; - } + env->CP0_ErrorEPC = exception_resume_pc(env); + env->hflags &= ~MIPS_HFLAG_BMASK; env->CP0_Status |= (1 << CP0St_ERL) | (1 << CP0St_BEV); env->hflags |= MIPS_HFLAG_64 | MIPS_HFLAG_CP0; env->hflags &= ~(MIPS_HFLAG_KSU); if (!(env->CP0_Status & (1 << CP0St_EXL))) env->CP0_Cause &= ~(1 << CP0Ca_BD); env->active_tc.PC = (int32_t)0xBFC00000; + set_hflags_for_handler(env); break; case EXCP_EXT_INTERRUPT: cause = 0; if (env->CP0_Cause & (1 << CP0Ca_IV)) offset = 0x200; + + if (env->CP0_Config3 & ((1 << CP0C3_VInt) | (1 << CP0C3_VEIC))) { + /* Vectored Interrupts. */ + unsigned int spacing; + unsigned int vector; + unsigned int pending = (env->CP0_Cause & CP0Ca_IP_mask) >> 8; + + /* Compute the Vector Spacing. */ + spacing = (env->CP0_IntCtl >> CP0IntCtl_VS) & ((1 << 6) - 1); + spacing <<= 5; + + if (env->CP0_Config3 & (1 << CP0C3_VInt)) { + /* For VInt mode, the MIPS computes the vector internally. */ + for (vector = 0; vector < 8; vector++) { + if (pending & 1) { + /* Found it. */ + break; + } + pending >>= 1; + } + } else { + /* For VEIC mode, the external interrupt controller feeds the + vector throught the CP0Cause IP lines. */ + vector = pending; + } + offset = 0x200 + vector * spacing; + } goto set_EPC; case EXCP_LTLBL: cause = 1; @@ -471,7 +518,8 @@ int SX = (env->CP0_Status & (1 << CP0St_SX)) != 0; int KX = (env->CP0_Status & (1 << CP0St_KX)) != 0; - if ((R == 0 && UX) || (R == 1 && SX) || (R == 3 && KX)) + if (((R == 0 && UX) || (R == 1 && SX) || (R == 3 && KX)) && + (!(env->insn_flags & (INSN_LOONGSON2E | INSN_LOONGSON2F)))) offset = 0x080; else #endif @@ -487,7 +535,8 @@ int SX = (env->CP0_Status & (1 << CP0St_SX)) != 0; int KX = (env->CP0_Status & (1 << CP0St_KX)) != 0; - if ((R == 0 && UX) || (R == 1 && SX) || (R == 3 && KX)) + if (((R == 0 && UX) || (R == 1 && SX) || (R == 3 && KX)) && + (!(env->insn_flags & (INSN_LOONGSON2E | INSN_LOONGSON2F)))) offset = 0x080; else #endif @@ -554,13 +603,10 @@ } set_EPC: if (!(env->CP0_Status & (1 << CP0St_EXL))) { + env->CP0_EPC = exception_resume_pc(env); if (env->hflags & MIPS_HFLAG_BMASK) { - /* If the exception was raised from a delay slot, - come back to the jump. */ - env->CP0_EPC = env->active_tc.PC - 4; env->CP0_Cause |= (1 << CP0Ca_BD); } else { - env->CP0_EPC = env->active_tc.PC; env->CP0_Cause &= ~(1 << CP0Ca_BD); } env->CP0_Status |= (1 << CP0St_EXL); @@ -574,6 +620,7 @@ env->active_tc.PC = (int32_t)(env->CP0_EBase & ~0x3ff); } env->active_tc.PC += offset; + set_hflags_for_handler(env); env->CP0_Cause = (env->CP0_Cause & ~(0x1f << CP0Ca_EC)) | (cause << CP0Ca_EC); break; default: @@ -592,6 +639,7 @@ env->exception_index = EXCP_NONE; } +#if !defined(CONFIG_USER_ONLY) void r4k_invalidate_tlb (CPUState *env, int idx, int use_extra) { r4k_tlb_t *tlb; @@ -645,3 +693,4 @@ } } } +#endif diff -Nru qemu-kvm-0.12.5+noroms/target-mips/helper.h qemu-kvm-0.14.1/target-mips/helper.h --- qemu-kvm-0.12.5+noroms/target-mips/helper.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-mips/helper.h 2011-05-11 13:29:46.000000000 +0000 @@ -2,7 +2,6 @@ DEF_HELPER_2(raise_exception_err, void, i32, int) DEF_HELPER_1(raise_exception, void, i32) -DEF_HELPER_0(interrupt_restart, void) #ifdef TARGET_MIPS64 DEF_HELPER_3(ldl, tl, tl, tl, int) @@ -155,11 +154,20 @@ DEF_HELPER_2(mtthi, void, tl, i32) DEF_HELPER_2(mttacx, void, tl, i32) DEF_HELPER_1(mttdsp, void, tl) -DEF_HELPER_1(dmt, tl, tl) -DEF_HELPER_1(emt, tl, tl) -DEF_HELPER_1(dvpe, tl, tl) -DEF_HELPER_1(evpe, tl, tl) +DEF_HELPER_0(dmt, tl) +DEF_HELPER_0(emt, tl) +DEF_HELPER_0(dvpe, tl) +DEF_HELPER_0(evpe, tl) #endif /* !CONFIG_USER_ONLY */ + +/* microMIPS functions */ +DEF_HELPER_3(lwm, void, tl, tl, i32); +DEF_HELPER_3(swm, void, tl, tl, i32); +#ifdef TARGET_MIPS64 +DEF_HELPER_3(ldm, void, tl, tl, i32); +DEF_HELPER_3(sdm, void, tl, tl, i32); +#endif + DEF_HELPER_2(fork, void, tl, tl) DEF_HELPER_1(yield, tl, tl) diff -Nru qemu-kvm-0.12.5+noroms/target-mips/mips-defs.h qemu-kvm-0.14.1/target-mips/mips-defs.h --- qemu-kvm-0.12.5+noroms/target-mips/mips-defs.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-mips/mips-defs.h 2011-05-11 13:29:46.000000000 +0000 @@ -10,8 +10,12 @@ #if defined(TARGET_MIPS64) #define TARGET_LONG_BITS 64 +#define TARGET_PHYS_ADDR_SPACE_BITS 36 +#define TARGET_VIRT_ADDR_SPACE_BITS 42 #else #define TARGET_LONG_BITS 32 +#define TARGET_PHYS_ADDR_SPACE_BITS 36 +#define TARGET_VIRT_ADDR_SPACE_BITS 32 #endif /* Masks used to mark instructions to indicate which ISA level they @@ -34,8 +38,11 @@ #define ASE_DSPR2 0x00010000 #define ASE_MT 0x00020000 #define ASE_SMARTMIPS 0x00040000 +#define ASE_MICROMIPS 0x00080000 /* Chip specific instructions. */ +#define INSN_LOONGSON2E 0x20000000 +#define INSN_LOONGSON2F 0x40000000 #define INSN_VR54XX 0x80000000 /* MIPS CPU defines. */ @@ -44,6 +51,8 @@ #define CPU_MIPS3 (CPU_MIPS2 | ISA_MIPS3) #define CPU_MIPS4 (CPU_MIPS3 | ISA_MIPS4) #define CPU_VR54XX (CPU_MIPS4 | INSN_VR54XX) +#define CPU_LOONGSON2E (CPU_MIPS3 | INSN_LOONGSON2E) +#define CPU_LOONGSON2F (CPU_MIPS3 | INSN_LOONGSON2F) #define CPU_MIPS5 (CPU_MIPS4 | ISA_MIPS5) diff -Nru qemu-kvm-0.12.5+noroms/target-mips/op_helper.c qemu-kvm-0.14.1/target-mips/op_helper.c --- qemu-kvm-0.12.5+noroms/target-mips/op_helper.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-mips/op_helper.c 2011-05-11 13:29:46.000000000 +0000 @@ -22,6 +22,11 @@ #include "host-utils.h" #include "helper.h" + +#ifndef CONFIG_USER_ONLY +static inline void cpu_mips_tlb_flush (CPUState *env, int flush_global); +#endif + /*****************************************************************************/ /* Exceptions processing helpers */ @@ -41,18 +46,6 @@ helper_raise_exception_err(exception, 0); } -void helper_interrupt_restart (void) -{ - if (!(env->CP0_Status & (1 << CP0St_EXL)) && - !(env->CP0_Status & (1 << CP0St_ERL)) && - !(env->hflags & MIPS_HFLAG_DM) && - (env->CP0_Status & (1 << CP0St_IE)) && - (env->CP0_Status & env->CP0_Cause & CP0Ca_IP_mask)) { - env->CP0_Cause &= ~(0x1f << CP0Ca_EC); - helper_raise_exception(EXCP_EXT_INTERRUPT); - } -} - #if !defined(CONFIG_USER_ONLY) static void do_restore_state (void *pc_ptr) { @@ -565,6 +558,142 @@ } #endif /* TARGET_MIPS64 */ +static const int multiple_regs[] = { 16, 17, 18, 19, 20, 21, 22, 23, 30 }; + +void helper_lwm (target_ulong addr, target_ulong reglist, uint32_t mem_idx) +{ + target_ulong base_reglist = reglist & 0xf; + target_ulong do_r31 = reglist & 0x10; +#ifdef CONFIG_USER_ONLY +#undef ldfun +#define ldfun ldl_raw +#else + uint32_t (*ldfun)(target_ulong); + + switch (mem_idx) + { + case 0: ldfun = ldl_kernel; break; + case 1: ldfun = ldl_super; break; + default: + case 2: ldfun = ldl_user; break; + } +#endif + + if (base_reglist > 0 && base_reglist <= ARRAY_SIZE (multiple_regs)) { + target_ulong i; + + for (i = 0; i < base_reglist; i++) { + env->active_tc.gpr[multiple_regs[i]] = (target_long) ldfun(addr); + addr += 4; + } + } + + if (do_r31) { + env->active_tc.gpr[31] = (target_long) ldfun(addr); + } +} + +void helper_swm (target_ulong addr, target_ulong reglist, uint32_t mem_idx) +{ + target_ulong base_reglist = reglist & 0xf; + target_ulong do_r31 = reglist & 0x10; +#ifdef CONFIG_USER_ONLY +#undef stfun +#define stfun stl_raw +#else + void (*stfun)(target_ulong, uint32_t); + + switch (mem_idx) + { + case 0: stfun = stl_kernel; break; + case 1: stfun = stl_super; break; + default: + case 2: stfun = stl_user; break; + } +#endif + + if (base_reglist > 0 && base_reglist <= ARRAY_SIZE (multiple_regs)) { + target_ulong i; + + for (i = 0; i < base_reglist; i++) { + stfun(addr, env->active_tc.gpr[multiple_regs[i]]); + addr += 4; + } + } + + if (do_r31) { + stfun(addr, env->active_tc.gpr[31]); + } +} + +#if defined(TARGET_MIPS64) +void helper_ldm (target_ulong addr, target_ulong reglist, uint32_t mem_idx) +{ + target_ulong base_reglist = reglist & 0xf; + target_ulong do_r31 = reglist & 0x10; +#ifdef CONFIG_USER_ONLY +#undef ldfun +#define ldfun ldq_raw +#else + uint64_t (*ldfun)(target_ulong); + + switch (mem_idx) + { + case 0: ldfun = ldq_kernel; break; + case 1: ldfun = ldq_super; break; + default: + case 2: ldfun = ldq_user; break; + } +#endif + + if (base_reglist > 0 && base_reglist <= ARRAY_SIZE (multiple_regs)) { + target_ulong i; + + for (i = 0; i < base_reglist; i++) { + env->active_tc.gpr[multiple_regs[i]] = ldfun(addr); + addr += 8; + } + } + + if (do_r31) { + env->active_tc.gpr[31] = ldfun(addr); + } +} + +void helper_sdm (target_ulong addr, target_ulong reglist, uint32_t mem_idx) +{ + target_ulong base_reglist = reglist & 0xf; + target_ulong do_r31 = reglist & 0x10; +#ifdef CONFIG_USER_ONLY +#undef stfun +#define stfun stq_raw +#else + void (*stfun)(target_ulong, uint64_t); + + switch (mem_idx) + { + case 0: stfun = stq_kernel; break; + case 1: stfun = stq_super; break; + default: + case 2: stfun = stq_user; break; + } +#endif + + if (base_reglist > 0 && base_reglist <= ARRAY_SIZE (multiple_regs)) { + target_ulong i; + + for (i = 0; i < base_reglist; i++) { + stfun(addr, env->active_tc.gpr[multiple_regs[i]]); + addr += 8; + } + } + + if (do_r31) { + stfun(addr, env->active_tc.gpr[31]); + } +} +#endif + #ifndef CONFIG_USER_ONLY /* CP0 helpers */ target_ulong helper_mfc0_mvpcontrol (void) @@ -1172,7 +1301,6 @@ default: cpu_abort(env, "Invalid MMU mode!\n"); break; } } - cpu_mips_update_irq(env); } void helper_mttc0_status(target_ulong arg1) @@ -1206,6 +1334,7 @@ { uint32_t mask = 0x00C00300; uint32_t old = env->CP0_Cause; + int i; if (env->insn_flags & ISA_MIPS32R2) mask |= 1 << CP0Ca_DC; @@ -1219,18 +1348,18 @@ cpu_mips_start_count(env); } - /* Handle the software interrupt as an hardware one, as they - are very similar */ - if (arg1 & CP0Ca_IP_mask) { - cpu_mips_update_irq(env); + /* Set/reset software interrupts */ + for (i = 0 ; i < 2 ; i++) { + if ((old ^ env->CP0_Cause) & (1 << (CP0Ca_IP + i))) { + cpu_mips_soft_irq(env, i, env->CP0_Cause & (1 << (CP0Ca_IP + i))); + } } } void helper_mtc0_ebase (target_ulong arg1) { /* vectored interrupts not implemented */ - /* Multi-CPU not implemented */ - env->CP0_EBase = 0x80000000 | (arg1 & 0x3FFFF000); + env->CP0_EBase = (env->CP0_EBase & ~0x3FFFF000) | (arg1 & 0x3FFFF000); } void helper_mtc0_config0 (target_ulong arg1) @@ -1425,40 +1554,28 @@ } /* MIPS MT functions */ -target_ulong helper_dmt(target_ulong arg1) +target_ulong helper_dmt(void) { // TODO - arg1 = 0; - // rt = arg1 - - return arg1; + return 0; } -target_ulong helper_emt(target_ulong arg1) +target_ulong helper_emt(void) { // TODO - arg1 = 0; - // rt = arg1 - - return arg1; + return 0; } -target_ulong helper_dvpe(target_ulong arg1) +target_ulong helper_dvpe(void) { // TODO - arg1 = 0; - // rt = arg1 - - return arg1; + return 0; } -target_ulong helper_evpe(target_ulong arg1) +target_ulong helper_evpe(void) { // TODO - arg1 = 0; - // rt = arg1 - - return arg1; + return 0; } #endif /* !CONFIG_USER_ONLY */ @@ -1469,8 +1586,10 @@ // TODO: store to TC register } -target_ulong helper_yield(target_ulong arg1) +target_ulong helper_yield(target_ulong arg) { + target_long arg1 = arg; + if (arg1 < 0) { /* No scheduling policy implemented. */ if (arg1 != -2) { @@ -1499,7 +1618,7 @@ #ifndef CONFIG_USER_ONLY /* TLB management */ -void cpu_mips_tlb_flush (CPUState *env, int flush_global) +static void cpu_mips_tlb_flush (CPUState *env, int flush_global) { /* Flush qemu's TLB and discard all shadowed entries. */ tlb_flush (env, flush_global); @@ -1652,8 +1771,6 @@ target_ulong t0 = env->CP0_Status; env->CP0_Status = t0 & ~(1 << CP0St_IE); - cpu_mips_update_irq(env); - return t0; } @@ -1662,8 +1779,6 @@ target_ulong t0 = env->CP0_Status; env->CP0_Status = t0 | (1 << CP0St_IE); - cpu_mips_update_irq(env); - return t0; } @@ -1698,14 +1813,24 @@ } } +static void set_pc (target_ulong error_pc) +{ + env->active_tc.PC = error_pc & ~(target_ulong)1; + if (error_pc & 1) { + env->hflags |= MIPS_HFLAG_M16; + } else { + env->hflags &= ~(MIPS_HFLAG_M16); + } +} + void helper_eret (void) { debug_pre_eret(); if (env->CP0_Status & (1 << CP0St_ERL)) { - env->active_tc.PC = env->CP0_ErrorEPC; + set_pc(env->CP0_ErrorEPC); env->CP0_Status &= ~(1 << CP0St_ERL); } else { - env->active_tc.PC = env->CP0_EPC; + set_pc(env->CP0_EPC); env->CP0_Status &= ~(1 << CP0St_EXL); } compute_hflags(env); @@ -1716,7 +1841,8 @@ void helper_deret (void) { debug_pre_eret(); - env->active_tc.PC = env->CP0_DEPC; + set_pc(env->CP0_DEPC); + env->hflags &= MIPS_HFLAG_DM; compute_hflags(env); debug_post_eret(); @@ -2751,10 +2877,10 @@ { if (float64_is_signaling_nan(a) || float64_is_signaling_nan(b) || - (sig && (float64_is_nan(a) || float64_is_nan(b)))) { + (sig && (float64_is_quiet_nan(a) || float64_is_quiet_nan(b)))) { float_raise(float_flag_invalid, status); return 1; - } else if (float64_is_nan(a) || float64_is_nan(b)) { + } else if (float64_is_quiet_nan(a) || float64_is_quiet_nan(b)) { return 1; } else { return 0; @@ -2809,10 +2935,10 @@ { if (float32_is_signaling_nan(a) || float32_is_signaling_nan(b) || - (sig && (float32_is_nan(a) || float32_is_nan(b)))) { + (sig && (float32_is_quiet_nan(a) || float32_is_quiet_nan(b)))) { float_raise(float_flag_invalid, status); return 1; - } else if (float32_is_nan(a) || float32_is_nan(b)) { + } else if (float32_is_quiet_nan(a) || float32_is_quiet_nan(b)) { return 1; } else { return 0; diff -Nru qemu-kvm-0.12.5+noroms/target-mips/TODO qemu-kvm-0.14.1/target-mips/TODO --- qemu-kvm-0.12.5+noroms/target-mips/TODO 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-mips/TODO 2011-05-11 13:29:46.000000000 +0000 @@ -4,7 +4,6 @@ General ------- - Unimplemented ASEs: - - MIPS16 - MDMX - SmartMIPS - DSP r1 diff -Nru qemu-kvm-0.12.5+noroms/target-mips/translate.c qemu-kvm-0.14.1/target-mips/translate.c --- qemu-kvm-0.12.5+noroms/target-mips/translate.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-mips/translate.c 2011-05-11 13:29:46.000000000 +0000 @@ -4,6 +4,7 @@ * Copyright (c) 2004-2005 Jocelyn Mayer * Copyright (c) 2006 Marius Groeger (FPU operations) * Copyright (c) 2006 Thiemo Seufer (MIPS32R2 support) + * Copyright (c) 2009 CodeSourcery (MIPS16 and microMIPS support) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -67,6 +68,7 @@ /* Jump and branches */ OPC_J = (0x02 << 26), OPC_JAL = (0x03 << 26), + OPC_JALS = OPC_JAL | 0x5, OPC_BEQ = (0x04 << 26), /* Unconditional if rs = rt = 0 (B) */ OPC_BEQL = (0x14 << 26), OPC_BNE = (0x05 << 26), @@ -76,6 +78,7 @@ OPC_BGTZ = (0x07 << 26), OPC_BGTZL = (0x17 << 26), OPC_JALX = (0x1D << 26), /* MIPS 16 only */ + OPC_JALXS = OPC_JALX | 0x5, /* Load and stores */ OPC_LDL = (0x1A << 26), OPC_LDR = (0x1B << 26), @@ -83,6 +86,7 @@ OPC_LH = (0x21 << 26), OPC_LWL = (0x22 << 26), OPC_LW = (0x23 << 26), + OPC_LWPC = OPC_LW | 0x5, OPC_LBU = (0x24 << 26), OPC_LHU = (0x25 << 26), OPC_LWR = (0x26 << 26), @@ -97,6 +101,7 @@ OPC_LL = (0x30 << 26), OPC_LLD = (0x34 << 26), OPC_LD = (0x37 << 26), + OPC_LDPC = OPC_LD | 0x5, OPC_SC = (0x38 << 26), OPC_SCD = (0x3C << 26), OPC_SD = (0x3F << 26), @@ -128,18 +133,23 @@ /* SSNOP is SLL r0, r0, 1 */ /* EHB is SLL r0, r0, 3 */ OPC_SRL = 0x02 | OPC_SPECIAL, /* also ROTR */ + OPC_ROTR = OPC_SRL | (1 << 21), OPC_SRA = 0x03 | OPC_SPECIAL, OPC_SLLV = 0x04 | OPC_SPECIAL, OPC_SRLV = 0x06 | OPC_SPECIAL, /* also ROTRV */ + OPC_ROTRV = OPC_SRLV | (1 << 6), OPC_SRAV = 0x07 | OPC_SPECIAL, OPC_DSLLV = 0x14 | OPC_SPECIAL, OPC_DSRLV = 0x16 | OPC_SPECIAL, /* also DROTRV */ + OPC_DROTRV = OPC_DSRLV | (1 << 6), OPC_DSRAV = 0x17 | OPC_SPECIAL, OPC_DSLL = 0x38 | OPC_SPECIAL, OPC_DSRL = 0x3A | OPC_SPECIAL, /* also DROTR */ + OPC_DROTR = OPC_DSRL | (1 << 21), OPC_DSRA = 0x3B | OPC_SPECIAL, OPC_DSLL32 = 0x3C | OPC_SPECIAL, OPC_DSRL32 = 0x3E | OPC_SPECIAL, /* also DROTR32 */ + OPC_DROTR32 = OPC_DSRL32 | (1 << 21), OPC_DSRA32 = 0x3F | OPC_SPECIAL, /* Multiplication / division */ OPC_MULT = 0x18 | OPC_SPECIAL, @@ -168,6 +178,8 @@ /* Jumps */ OPC_JR = 0x08 | OPC_SPECIAL, /* Also JR.HB */ OPC_JALR = 0x09 | OPC_SPECIAL, /* Also JALR.HB */ + OPC_JALRC = OPC_JALR | (0x5 << 6), + OPC_JALRS = 0x10 | OPC_SPECIAL | (0x5 << 6), /* Traps */ OPC_TGE = 0x30 | OPC_SPECIAL, OPC_TGEU = 0x31 | OPC_SPECIAL, @@ -231,8 +243,10 @@ OPC_BGEZ = (0x01 << 16) | OPC_REGIMM, OPC_BGEZL = (0x03 << 16) | OPC_REGIMM, OPC_BLTZAL = (0x10 << 16) | OPC_REGIMM, + OPC_BLTZALS = OPC_BLTZAL | 0x5, /* microMIPS */ OPC_BLTZALL = (0x12 << 16) | OPC_REGIMM, OPC_BGEZAL = (0x11 << 16) | OPC_REGIMM, + OPC_BGEZALS = OPC_BGEZAL | 0x5, /* microMIPS */ OPC_BGEZALL = (0x13 << 16) | OPC_REGIMM, OPC_TGEI = (0x08 << 16) | OPC_REGIMM, OPC_TGEIU = (0x09 << 16) | OPC_REGIMM, @@ -253,6 +267,19 @@ OPC_MUL = 0x02 | OPC_SPECIAL2, OPC_MSUB = 0x04 | OPC_SPECIAL2, OPC_MSUBU = 0x05 | OPC_SPECIAL2, + /* Loongson 2F */ + OPC_MULT_G_2F = 0x10 | OPC_SPECIAL2, + OPC_DMULT_G_2F = 0x11 | OPC_SPECIAL2, + OPC_MULTU_G_2F = 0x12 | OPC_SPECIAL2, + OPC_DMULTU_G_2F = 0x13 | OPC_SPECIAL2, + OPC_DIV_G_2F = 0x14 | OPC_SPECIAL2, + OPC_DDIV_G_2F = 0x15 | OPC_SPECIAL2, + OPC_DIVU_G_2F = 0x16 | OPC_SPECIAL2, + OPC_DDIVU_G_2F = 0x17 | OPC_SPECIAL2, + OPC_MOD_G_2F = 0x1c | OPC_SPECIAL2, + OPC_DMOD_G_2F = 0x1d | OPC_SPECIAL2, + OPC_MODU_G_2F = 0x1e | OPC_SPECIAL2, + OPC_DMODU_G_2F = 0x1f | OPC_SPECIAL2, /* Misc */ OPC_CLZ = 0x20 | OPC_SPECIAL2, OPC_CLO = 0x21 | OPC_SPECIAL2, @@ -279,6 +306,20 @@ OPC_BSHFL = 0x20 | OPC_SPECIAL3, OPC_DBSHFL = 0x24 | OPC_SPECIAL3, OPC_RDHWR = 0x3B | OPC_SPECIAL3, + + /* Loongson 2E */ + OPC_MULT_G_2E = 0x18 | OPC_SPECIAL3, + OPC_MULTU_G_2E = 0x19 | OPC_SPECIAL3, + OPC_DIV_G_2E = 0x1A | OPC_SPECIAL3, + OPC_DIVU_G_2E = 0x1B | OPC_SPECIAL3, + OPC_DMULT_G_2E = 0x1C | OPC_SPECIAL3, + OPC_DMULTU_G_2E = 0x1D | OPC_SPECIAL3, + OPC_DDIV_G_2E = 0x1E | OPC_SPECIAL3, + OPC_DDIVU_G_2E = 0x1F | OPC_SPECIAL3, + OPC_MOD_G_2E = 0x22 | OPC_SPECIAL3, + OPC_MODU_G_2E = 0x23 | OPC_SPECIAL3, + OPC_DMOD_G_2E = 0x26 | OPC_SPECIAL3, + OPC_DMODU_G_2E = 0x27 | OPC_SPECIAL3, }; /* BSHFL opcodes */ @@ -345,6 +386,19 @@ /* Coprocessor 1 (rs field) */ #define MASK_CP1(op) MASK_OP_MAJOR(op) | (op & (0x1F << 21)) +/* Values for the fmt field in FP instructions */ +enum { + /* 0 - 15 are reserved */ + FMT_S = 16, /* single fp */ + FMT_D = 17, /* double fp */ + FMT_E = 18, /* extended fp */ + FMT_Q = 19, /* quad fp */ + FMT_W = 20, /* 32-bit fixed */ + FMT_L = 21, /* 64-bit fixed */ + FMT_PS = 22, /* paired single fp */ + /* 23 - 31 are reserved */ +}; + enum { OPC_MFC1 = (0x00 << 21) | OPC_CP1, OPC_DMFC1 = (0x01 << 21) | OPC_CP1, @@ -357,13 +411,13 @@ OPC_BC1 = (0x08 << 21) | OPC_CP1, /* bc */ OPC_BC1ANY2 = (0x09 << 21) | OPC_CP1, OPC_BC1ANY4 = (0x0A << 21) | OPC_CP1, - OPC_S_FMT = (0x10 << 21) | OPC_CP1, /* 16: fmt=single fp */ - OPC_D_FMT = (0x11 << 21) | OPC_CP1, /* 17: fmt=double fp */ - OPC_E_FMT = (0x12 << 21) | OPC_CP1, /* 18: fmt=extended fp */ - OPC_Q_FMT = (0x13 << 21) | OPC_CP1, /* 19: fmt=quad fp */ - OPC_W_FMT = (0x14 << 21) | OPC_CP1, /* 20: fmt=32bit fixed */ - OPC_L_FMT = (0x15 << 21) | OPC_CP1, /* 21: fmt=64bit fixed */ - OPC_PS_FMT = (0x16 << 21) | OPC_CP1, /* 22: fmt=paired single fp */ + OPC_S_FMT = (FMT_S << 21) | OPC_CP1, + OPC_D_FMT = (FMT_D << 21) | OPC_CP1, + OPC_E_FMT = (FMT_E << 21) | OPC_CP1, + OPC_Q_FMT = (FMT_Q << 21) | OPC_CP1, + OPC_W_FMT = (FMT_W << 21) | OPC_CP1, + OPC_L_FMT = (FMT_L << 21) | OPC_CP1, + OPC_PS_FMT = (FMT_PS << 21) | OPC_CP1, }; #define MASK_CP1_FUNC(op) MASK_CP1(op) | (op & 0x3F) @@ -433,6 +487,8 @@ static TCGv_i32 hflags; static TCGv_i32 fpu_fcr0, fpu_fcr31; +static uint32_t gen_opc_hflags[OPC_BUF_SIZE]; + #include "gen-icount.h" #define gen_helper_0i(name, arg) do { \ @@ -652,97 +708,7 @@ return 23; } -#define FOP_CONDS(type, fmt, bits) \ -static inline void gen_cmp ## type ## _ ## fmt(int n, TCGv_i##bits a, \ - TCGv_i##bits b, int cc) \ -{ \ - switch (n) { \ - case 0: gen_helper_2i(cmp ## type ## _ ## fmt ## _f, a, b, cc); break;\ - case 1: gen_helper_2i(cmp ## type ## _ ## fmt ## _un, a, b, cc); break;\ - case 2: gen_helper_2i(cmp ## type ## _ ## fmt ## _eq, a, b, cc); break;\ - case 3: gen_helper_2i(cmp ## type ## _ ## fmt ## _ueq, a, b, cc); break;\ - case 4: gen_helper_2i(cmp ## type ## _ ## fmt ## _olt, a, b, cc); break;\ - case 5: gen_helper_2i(cmp ## type ## _ ## fmt ## _ult, a, b, cc); break;\ - case 6: gen_helper_2i(cmp ## type ## _ ## fmt ## _ole, a, b, cc); break;\ - case 7: gen_helper_2i(cmp ## type ## _ ## fmt ## _ule, a, b, cc); break;\ - case 8: gen_helper_2i(cmp ## type ## _ ## fmt ## _sf, a, b, cc); break;\ - case 9: gen_helper_2i(cmp ## type ## _ ## fmt ## _ngle, a, b, cc); break;\ - case 10: gen_helper_2i(cmp ## type ## _ ## fmt ## _seq, a, b, cc); break;\ - case 11: gen_helper_2i(cmp ## type ## _ ## fmt ## _ngl, a, b, cc); break;\ - case 12: gen_helper_2i(cmp ## type ## _ ## fmt ## _lt, a, b, cc); break;\ - case 13: gen_helper_2i(cmp ## type ## _ ## fmt ## _nge, a, b, cc); break;\ - case 14: gen_helper_2i(cmp ## type ## _ ## fmt ## _le, a, b, cc); break;\ - case 15: gen_helper_2i(cmp ## type ## _ ## fmt ## _ngt, a, b, cc); break;\ - default: abort(); \ - } \ -} - -FOP_CONDS(, d, 64) -FOP_CONDS(abs, d, 64) -FOP_CONDS(, s, 32) -FOP_CONDS(abs, s, 32) -FOP_CONDS(, ps, 64) -FOP_CONDS(abs, ps, 64) -#undef FOP_CONDS - /* Tests */ -#define OP_COND(name, cond) \ -static inline void glue(gen_op_, name) (TCGv ret, TCGv t0, TCGv t1) \ -{ \ - int l1 = gen_new_label(); \ - int l2 = gen_new_label(); \ - \ - tcg_gen_brcond_tl(cond, t0, t1, l1); \ - tcg_gen_movi_tl(ret, 0); \ - tcg_gen_br(l2); \ - gen_set_label(l1); \ - tcg_gen_movi_tl(ret, 1); \ - gen_set_label(l2); \ -} -OP_COND(eq, TCG_COND_EQ); -OP_COND(ne, TCG_COND_NE); -OP_COND(ge, TCG_COND_GE); -OP_COND(geu, TCG_COND_GEU); -OP_COND(lt, TCG_COND_LT); -OP_COND(ltu, TCG_COND_LTU); -#undef OP_COND - -#define OP_CONDI(name, cond) \ -static inline void glue(gen_op_, name) (TCGv ret, TCGv t0, target_ulong val) \ -{ \ - int l1 = gen_new_label(); \ - int l2 = gen_new_label(); \ - \ - tcg_gen_brcondi_tl(cond, t0, val, l1); \ - tcg_gen_movi_tl(ret, 0); \ - tcg_gen_br(l2); \ - gen_set_label(l1); \ - tcg_gen_movi_tl(ret, 1); \ - gen_set_label(l2); \ -} -OP_CONDI(lti, TCG_COND_LT); -OP_CONDI(ltiu, TCG_COND_LTU); -#undef OP_CONDI - -#define OP_CONDZ(name, cond) \ -static inline void glue(gen_op_, name) (TCGv ret, TCGv t0) \ -{ \ - int l1 = gen_new_label(); \ - int l2 = gen_new_label(); \ - \ - tcg_gen_brcondi_tl(cond, t0, 0, l1); \ - tcg_gen_movi_tl(ret, 0); \ - tcg_gen_br(l2); \ - gen_set_label(l1); \ - tcg_gen_movi_tl(ret, 1); \ - gen_set_label(l2); \ -} -OP_CONDZ(gez, TCG_COND_GE); -OP_CONDZ(gtz, TCG_COND_GT); -OP_CONDZ(lez, TCG_COND_LE); -OP_CONDZ(ltz, TCG_COND_LT); -#undef OP_CONDZ - static inline void gen_save_pc(target_ulong pc) { tcg_gen_movi_tl(cpu_PC, pc); @@ -758,7 +724,7 @@ if (ctx->hflags != ctx->saved_hflags) { tcg_gen_movi_i32(hflags, ctx->hflags); ctx->saved_hflags = ctx->hflags; - switch (ctx->hflags & MIPS_HFLAG_BMASK) { + switch (ctx->hflags & MIPS_HFLAG_BMASK_BASE) { case MIPS_HFLAG_BR: break; case MIPS_HFLAG_BC: @@ -773,7 +739,7 @@ static inline void restore_cpu_state (CPUState *env, DisasContext *ctx) { ctx->saved_hflags = ctx->hflags; - switch (ctx->hflags & MIPS_HFLAG_BMASK) { + switch (ctx->hflags & MIPS_HFLAG_BMASK_BASE) { case MIPS_HFLAG_BR: break; case MIPS_HFLAG_BC: @@ -882,9 +848,72 @@ generate_exception(ctx, EXCP_RI); } +/* Define small wrappers for gen_load_fpr* so that we have a uniform + calling interface for 32 and 64-bit FPRs. No sense in changing + all callers for gen_load_fpr32 when we need the CTX parameter for + this one use. */ +#define gen_ldcmp_fpr32(ctx, x, y) gen_load_fpr32(x, y) +#define gen_ldcmp_fpr64(ctx, x, y) gen_load_fpr64(ctx, x, y) +#define FOP_CONDS(type, abs, fmt, ifmt, bits) \ +static inline void gen_cmp ## type ## _ ## fmt(DisasContext *ctx, int n, \ + int ft, int fs, int cc) \ +{ \ + TCGv_i##bits fp0 = tcg_temp_new_i##bits (); \ + TCGv_i##bits fp1 = tcg_temp_new_i##bits (); \ + switch (ifmt) { \ + case FMT_PS: \ + check_cp1_64bitmode(ctx); \ + break; \ + case FMT_D: \ + if (abs) { \ + check_cop1x(ctx); \ + } \ + check_cp1_registers(ctx, fs | ft); \ + break; \ + case FMT_S: \ + if (abs) { \ + check_cop1x(ctx); \ + } \ + break; \ + } \ + gen_ldcmp_fpr##bits (ctx, fp0, fs); \ + gen_ldcmp_fpr##bits (ctx, fp1, ft); \ + switch (n) { \ + case 0: gen_helper_2i(cmp ## type ## _ ## fmt ## _f, fp0, fp1, cc); break;\ + case 1: gen_helper_2i(cmp ## type ## _ ## fmt ## _un, fp0, fp1, cc); break;\ + case 2: gen_helper_2i(cmp ## type ## _ ## fmt ## _eq, fp0, fp1, cc); break;\ + case 3: gen_helper_2i(cmp ## type ## _ ## fmt ## _ueq, fp0, fp1, cc); break;\ + case 4: gen_helper_2i(cmp ## type ## _ ## fmt ## _olt, fp0, fp1, cc); break;\ + case 5: gen_helper_2i(cmp ## type ## _ ## fmt ## _ult, fp0, fp1, cc); break;\ + case 6: gen_helper_2i(cmp ## type ## _ ## fmt ## _ole, fp0, fp1, cc); break;\ + case 7: gen_helper_2i(cmp ## type ## _ ## fmt ## _ule, fp0, fp1, cc); break;\ + case 8: gen_helper_2i(cmp ## type ## _ ## fmt ## _sf, fp0, fp1, cc); break;\ + case 9: gen_helper_2i(cmp ## type ## _ ## fmt ## _ngle, fp0, fp1, cc); break;\ + case 10: gen_helper_2i(cmp ## type ## _ ## fmt ## _seq, fp0, fp1, cc); break;\ + case 11: gen_helper_2i(cmp ## type ## _ ## fmt ## _ngl, fp0, fp1, cc); break;\ + case 12: gen_helper_2i(cmp ## type ## _ ## fmt ## _lt, fp0, fp1, cc); break;\ + case 13: gen_helper_2i(cmp ## type ## _ ## fmt ## _nge, fp0, fp1, cc); break;\ + case 14: gen_helper_2i(cmp ## type ## _ ## fmt ## _le, fp0, fp1, cc); break;\ + case 15: gen_helper_2i(cmp ## type ## _ ## fmt ## _ngt, fp0, fp1, cc); break;\ + default: abort(); \ + } \ + tcg_temp_free_i##bits (fp0); \ + tcg_temp_free_i##bits (fp1); \ +} + +FOP_CONDS(, 0, d, FMT_D, 64) +FOP_CONDS(abs, 1, d, FMT_D, 64) +FOP_CONDS(, 0, s, FMT_S, 32) +FOP_CONDS(abs, 1, s, FMT_S, 32) +FOP_CONDS(, 0, ps, FMT_PS, 64) +FOP_CONDS(abs, 1, ps, FMT_PS, 64) +#undef FOP_CONDS +#undef gen_ldcmp_fpr32 +#undef gen_ldcmp_fpr64 + /* load/store instructions. */ #define OP_LD(insn,fname) \ -static inline void op_ldst_##insn(TCGv ret, TCGv arg1, DisasContext *ctx) \ +static inline void op_ld_##insn(TCGv ret, TCGv arg1, DisasContext *ctx) \ { \ tcg_gen_qemu_##fname(ret, arg1, ctx->mem_idx); \ } @@ -900,7 +929,7 @@ #undef OP_LD #define OP_ST(insn,fname) \ -static inline void op_ldst_##insn(TCGv arg1, TCGv arg2, DisasContext *ctx) \ +static inline void op_st_##insn(TCGv arg1, TCGv arg2, DisasContext *ctx) \ { \ tcg_gen_qemu_##fname(arg1, arg2, ctx->mem_idx); \ } @@ -914,7 +943,7 @@ #ifdef CONFIG_USER_ONLY #define OP_LD_ATOMIC(insn,fname) \ -static inline void op_ldst_##insn(TCGv ret, TCGv arg1, DisasContext *ctx) \ +static inline void op_ld_##insn(TCGv ret, TCGv arg1, DisasContext *ctx) \ { \ TCGv t0 = tcg_temp_new(); \ tcg_gen_mov_tl(t0, arg1); \ @@ -925,7 +954,7 @@ } #else #define OP_LD_ATOMIC(insn,fname) \ -static inline void op_ldst_##insn(TCGv ret, TCGv arg1, DisasContext *ctx) \ +static inline void op_ld_##insn(TCGv ret, TCGv arg1, DisasContext *ctx) \ { \ gen_helper_2i(insn, ret, arg1, ctx->mem_idx); \ } @@ -938,7 +967,7 @@ #ifdef CONFIG_USER_ONLY #define OP_ST_ATOMIC(insn,fname,ldname,almask) \ -static inline void op_ldst_##insn(TCGv arg1, TCGv arg2, int rt, DisasContext *ctx) \ +static inline void op_st_##insn(TCGv arg1, TCGv arg2, int rt, DisasContext *ctx) \ { \ TCGv t0 = tcg_temp_new(); \ int l1 = gen_new_label(); \ @@ -962,7 +991,7 @@ } #else #define OP_ST_ATOMIC(insn,fname,ldname,almask) \ -static inline void op_ldst_##insn(TCGv arg1, TCGv arg2, int rt, DisasContext *ctx) \ +static inline void op_st_##insn(TCGv arg1, TCGv arg2, int rt, DisasContext *ctx) \ { \ TCGv t0 = tcg_temp_new(); \ gen_helper_3i(insn, t0, arg1, arg2, ctx->mem_idx); \ @@ -976,50 +1005,72 @@ #endif #undef OP_ST_ATOMIC -/* Load and store */ -static void gen_ldst (DisasContext *ctx, uint32_t opc, int rt, - int base, int16_t offset) +static void gen_base_offset_addr (DisasContext *ctx, TCGv addr, + int base, int16_t offset) { - const char *opn = "ldst"; - TCGv t0 = tcg_temp_new(); - TCGv t1 = tcg_temp_new(); - if (base == 0) { - tcg_gen_movi_tl(t0, offset); + tcg_gen_movi_tl(addr, offset); } else if (offset == 0) { - gen_load_gpr(t0, base); + gen_load_gpr(addr, base); } else { - tcg_gen_movi_tl(t0, offset); - gen_op_addr_add(ctx, t0, cpu_gpr[base], t0); + tcg_gen_movi_tl(addr, offset); + gen_op_addr_add(ctx, addr, cpu_gpr[base], addr); } - /* Don't do NOP if destination is zero: we must perform the actual - memory access. */ +} + +static target_ulong pc_relative_pc (DisasContext *ctx) +{ + target_ulong pc = ctx->pc; + + if (ctx->hflags & MIPS_HFLAG_BMASK) { + int branch_bytes = ctx->hflags & MIPS_HFLAG_BDS16 ? 2 : 4; + + pc -= branch_bytes; + } + + pc &= ~(target_ulong)3; + return pc; +} + +/* Load */ +static void gen_ld (CPUState *env, DisasContext *ctx, uint32_t opc, + int rt, int base, int16_t offset) +{ + const char *opn = "ld"; + TCGv t0, t1; + + if (rt == 0 && env->insn_flags & (INSN_LOONGSON2E | INSN_LOONGSON2F)) { + /* Loongson CPU uses a load to zero register for prefetch. + We emulate it as a NOP. On other CPU we must perform the + actual memory access. */ + MIPS_DEBUG("NOP"); + return; + } + + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); + gen_base_offset_addr(ctx, t0, base, offset); + switch (opc) { #if defined(TARGET_MIPS64) case OPC_LWU: save_cpu_state(ctx, 0); - op_ldst_lwu(t0, t0, ctx); + op_ld_lwu(t0, t0, ctx); gen_store_gpr(t0, rt); opn = "lwu"; break; case OPC_LD: save_cpu_state(ctx, 0); - op_ldst_ld(t0, t0, ctx); + op_ld_ld(t0, t0, ctx); gen_store_gpr(t0, rt); opn = "ld"; break; case OPC_LLD: - save_cpu_state(ctx, 0); - op_ldst_lld(t0, t0, ctx); + save_cpu_state(ctx, 1); + op_ld_lld(t0, t0, ctx); gen_store_gpr(t0, rt); opn = "lld"; break; - case OPC_SD: - save_cpu_state(ctx, 0); - gen_load_gpr(t1, rt); - op_ldst_sd(t1, t0, ctx); - opn = "sd"; - break; case OPC_LDL: save_cpu_state(ctx, 1); gen_load_gpr(t1, rt); @@ -1027,12 +1078,6 @@ gen_store_gpr(t1, rt); opn = "ldl"; break; - case OPC_SDL: - save_cpu_state(ctx, 1); - gen_load_gpr(t1, rt); - gen_helper_2i(sdl, t1, t0, ctx->mem_idx); - opn = "sdl"; - break; case OPC_LDR: save_cpu_state(ctx, 1); gen_load_gpr(t1, rt); @@ -1040,58 +1085,50 @@ gen_store_gpr(t1, rt); opn = "ldr"; break; - case OPC_SDR: - save_cpu_state(ctx, 1); - gen_load_gpr(t1, rt); - gen_helper_2i(sdr, t1, t0, ctx->mem_idx); - opn = "sdr"; + case OPC_LDPC: + save_cpu_state(ctx, 0); + tcg_gen_movi_tl(t1, pc_relative_pc(ctx)); + gen_op_addr_add(ctx, t0, t0, t1); + op_ld_ld(t0, t0, ctx); + gen_store_gpr(t0, rt); + opn = "ldpc"; break; #endif - case OPC_LW: + case OPC_LWPC: save_cpu_state(ctx, 0); - op_ldst_lw(t0, t0, ctx); + tcg_gen_movi_tl(t1, pc_relative_pc(ctx)); + gen_op_addr_add(ctx, t0, t0, t1); + op_ld_lw(t0, t0, ctx); gen_store_gpr(t0, rt); - opn = "lw"; + opn = "lwpc"; break; - case OPC_SW: + case OPC_LW: save_cpu_state(ctx, 0); - gen_load_gpr(t1, rt); - op_ldst_sw(t1, t0, ctx); - opn = "sw"; + op_ld_lw(t0, t0, ctx); + gen_store_gpr(t0, rt); + opn = "lw"; break; case OPC_LH: save_cpu_state(ctx, 0); - op_ldst_lh(t0, t0, ctx); + op_ld_lh(t0, t0, ctx); gen_store_gpr(t0, rt); opn = "lh"; break; - case OPC_SH: - save_cpu_state(ctx, 0); - gen_load_gpr(t1, rt); - op_ldst_sh(t1, t0, ctx); - opn = "sh"; - break; case OPC_LHU: save_cpu_state(ctx, 0); - op_ldst_lhu(t0, t0, ctx); + op_ld_lhu(t0, t0, ctx); gen_store_gpr(t0, rt); opn = "lhu"; break; case OPC_LB: save_cpu_state(ctx, 0); - op_ldst_lb(t0, t0, ctx); + op_ld_lb(t0, t0, ctx); gen_store_gpr(t0, rt); opn = "lb"; break; - case OPC_SB: - save_cpu_state(ctx, 0); - gen_load_gpr(t1, rt); - op_ldst_sb(t1, t0, ctx); - opn = "sb"; - break; case OPC_LBU: save_cpu_state(ctx, 0); - op_ldst_lbu(t0, t0, ctx); + op_ld_lbu(t0, t0, ctx); gen_store_gpr(t0, rt); opn = "lbu"; break; @@ -1102,12 +1139,6 @@ gen_store_gpr(t1, rt); opn = "lwl"; break; - case OPC_SWL: - save_cpu_state(ctx, 1); - gen_load_gpr(t1, rt); - gen_helper_2i(swl, t1, t0, ctx->mem_idx); - opn = "swr"; - break; case OPC_LWR: save_cpu_state(ctx, 1); gen_load_gpr(t1, rt); @@ -1115,24 +1146,80 @@ gen_store_gpr(t1, rt); opn = "lwr"; break; - case OPC_SWR: - save_cpu_state(ctx, 1); - gen_load_gpr(t1, rt); - gen_helper_2i(swr, t1, t0, ctx->mem_idx); - opn = "swr"; - break; case OPC_LL: save_cpu_state(ctx, 1); - op_ldst_ll(t0, t0, ctx); + op_ld_ll(t0, t0, ctx); gen_store_gpr(t0, rt); opn = "ll"; break; } + (void)opn; /* avoid a compiler warning */ + MIPS_DEBUG("%s %s, %d(%s)", opn, regnames[rt], offset, regnames[base]); + tcg_temp_free(t0); + tcg_temp_free(t1); +} + +/* Store */ +static void gen_st (DisasContext *ctx, uint32_t opc, int rt, + int base, int16_t offset) +{ + const char *opn = "st"; + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + + gen_base_offset_addr(ctx, t0, base, offset); + gen_load_gpr(t1, rt); + switch (opc) { +#if defined(TARGET_MIPS64) + case OPC_SD: + save_cpu_state(ctx, 0); + op_st_sd(t1, t0, ctx); + opn = "sd"; + break; + case OPC_SDL: + save_cpu_state(ctx, 1); + gen_helper_2i(sdl, t1, t0, ctx->mem_idx); + opn = "sdl"; + break; + case OPC_SDR: + save_cpu_state(ctx, 1); + gen_helper_2i(sdr, t1, t0, ctx->mem_idx); + opn = "sdr"; + break; +#endif + case OPC_SW: + save_cpu_state(ctx, 0); + op_st_sw(t1, t0, ctx); + opn = "sw"; + break; + case OPC_SH: + save_cpu_state(ctx, 0); + op_st_sh(t1, t0, ctx); + opn = "sh"; + break; + case OPC_SB: + save_cpu_state(ctx, 0); + op_st_sb(t1, t0, ctx); + opn = "sb"; + break; + case OPC_SWL: + save_cpu_state(ctx, 1); + gen_helper_2i(swl, t1, t0, ctx->mem_idx); + opn = "swl"; + break; + case OPC_SWR: + save_cpu_state(ctx, 1); + gen_helper_2i(swr, t1, t0, ctx->mem_idx); + opn = "swr"; + break; + } + (void)opn; /* avoid a compiler warning */ MIPS_DEBUG("%s %s, %d(%s)", opn, regnames[rt], offset, regnames[base]); tcg_temp_free(t0); tcg_temp_free(t1); } + /* Store conditional */ static void gen_st_cond (DisasContext *ctx, uint32_t opc, int rt, int base, int16_t offset) @@ -1142,14 +1229,7 @@ t0 = tcg_temp_local_new(); - if (base == 0) { - tcg_gen_movi_tl(t0, offset); - } else if (offset == 0) { - gen_load_gpr(t0, base); - } else { - tcg_gen_movi_tl(t0, offset); - gen_op_addr_add(ctx, t0, cpu_gpr[base], t0); - } + gen_base_offset_addr(ctx, t0, base, offset); /* Don't do NOP if destination is zero: we must perform the actual memory access. */ @@ -1158,17 +1238,18 @@ switch (opc) { #if defined(TARGET_MIPS64) case OPC_SCD: - save_cpu_state(ctx, 0); - op_ldst_scd(t1, t0, rt, ctx); + save_cpu_state(ctx, 1); + op_st_scd(t1, t0, rt, ctx); opn = "scd"; break; #endif case OPC_SC: save_cpu_state(ctx, 1); - op_ldst_sc(t1, t0, rt, ctx); + op_st_sc(t1, t0, rt, ctx); opn = "sc"; break; } + (void)opn; /* avoid a compiler warning */ MIPS_DEBUG("%s %s, %d(%s)", opn, regnames[rt], offset, regnames[base]); tcg_temp_free(t1); tcg_temp_free(t0); @@ -1181,14 +1262,7 @@ const char *opn = "flt_ldst"; TCGv t0 = tcg_temp_new(); - if (base == 0) { - tcg_gen_movi_tl(t0, offset); - } else if (offset == 0) { - gen_load_gpr(t0, base); - } else { - tcg_gen_movi_tl(t0, offset); - gen_op_addr_add(ctx, t0, cpu_gpr[base], t0); - } + gen_base_offset_addr(ctx, t0, base, offset); /* Don't do NOP if destination is zero: we must perform the actual memory access. */ switch (opc) { @@ -1241,11 +1315,23 @@ generate_exception(ctx, EXCP_RI); goto out; } + (void)opn; /* avoid a compiler warning */ MIPS_DEBUG("%s %s, %d(%s)", opn, fregnames[ft], offset, regnames[base]); out: tcg_temp_free(t0); } +static void gen_cop1_ldst(CPUState *env, DisasContext *ctx, + uint32_t op, int rt, int rs, int16_t imm) +{ + if (env->CP0_Config1 & (1 << CP0C1_FP)) { + check_cp1_enabled(ctx); + gen_flt_ldst(ctx, op, rt, rs, imm); + } else { + generate_exception_err(ctx, EXCP_CpU, 1); + } +} + /* Arithmetic with immediate operand */ static void gen_arith_imm (CPUState *env, DisasContext *ctx, uint32_t opc, int rt, int rs, int16_t imm) @@ -1330,6 +1416,7 @@ break; #endif } + (void)opn; /* avoid a compiler warning */ MIPS_DEBUG("%s %s, %s, " TARGET_FMT_lx, opn, regnames[rt], regnames[rs], uimm); } @@ -1372,6 +1459,7 @@ opn = "lui"; break; } + (void)opn; /* avoid a compiler warning */ MIPS_DEBUG("%s %s, %s, " TARGET_FMT_lx, opn, regnames[rt], regnames[rs], uimm); } @@ -1391,14 +1479,15 @@ gen_load_gpr(t0, rs); switch (opc) { case OPC_SLTI: - gen_op_lti(cpu_gpr[rt], t0, uimm); + tcg_gen_setcondi_tl(TCG_COND_LT, cpu_gpr[rt], t0, uimm); opn = "slti"; break; case OPC_SLTIU: - gen_op_ltiu(cpu_gpr[rt], t0, uimm); + tcg_gen_setcondi_tl(TCG_COND_LTU, cpu_gpr[rt], t0, uimm); opn = "sltiu"; break; } + (void)opn; /* avoid a compiler warning */ MIPS_DEBUG("%s %s, %s, " TARGET_FMT_lx, opn, regnames[rt], regnames[rs], uimm); tcg_temp_free(t0); } @@ -1426,50 +1515,30 @@ opn = "sll"; break; case OPC_SRA: - tcg_gen_ext32s_tl(t0, t0); tcg_gen_sari_tl(cpu_gpr[rt], t0, uimm); opn = "sra"; break; case OPC_SRL: - switch ((ctx->opcode >> 21) & 0x1f) { - case 0: - if (uimm != 0) { - tcg_gen_ext32u_tl(t0, t0); - tcg_gen_shri_tl(cpu_gpr[rt], t0, uimm); - } else { - tcg_gen_ext32s_tl(cpu_gpr[rt], t0); - } - opn = "srl"; - break; - case 1: - /* rotr is decoded as srl on non-R2 CPUs */ - if (env->insn_flags & ISA_MIPS32R2) { - if (uimm != 0) { - TCGv_i32 t1 = tcg_temp_new_i32(); + if (uimm != 0) { + tcg_gen_ext32u_tl(t0, t0); + tcg_gen_shri_tl(cpu_gpr[rt], t0, uimm); + } else { + tcg_gen_ext32s_tl(cpu_gpr[rt], t0); + } + opn = "srl"; + break; + case OPC_ROTR: + if (uimm != 0) { + TCGv_i32 t1 = tcg_temp_new_i32(); - tcg_gen_trunc_tl_i32(t1, t0); - tcg_gen_rotri_i32(t1, t1, uimm); - tcg_gen_ext_i32_tl(cpu_gpr[rt], t1); - tcg_temp_free_i32(t1); - } else { - tcg_gen_ext32s_tl(cpu_gpr[rt], t0); - } - opn = "rotr"; - } else { - if (uimm != 0) { - tcg_gen_ext32u_tl(t0, t0); - tcg_gen_shri_tl(cpu_gpr[rt], t0, uimm); - } else { - tcg_gen_ext32s_tl(cpu_gpr[rt], t0); - } - opn = "srl"; - } - break; - default: - MIPS_INVAL("invalid srl flag"); - generate_exception(ctx, EXCP_RI); - break; + tcg_gen_trunc_tl_i32(t1, t0); + tcg_gen_rotri_i32(t1, t1, uimm); + tcg_gen_ext_i32_tl(cpu_gpr[rt], t1); + tcg_temp_free_i32(t1); + } else { + tcg_gen_ext32s_tl(cpu_gpr[rt], t0); } + opn = "rotr"; break; #if defined(TARGET_MIPS64) case OPC_DSLL: @@ -1481,30 +1550,16 @@ opn = "dsra"; break; case OPC_DSRL: - switch ((ctx->opcode >> 21) & 0x1f) { - case 0: - tcg_gen_shri_tl(cpu_gpr[rt], t0, uimm); - opn = "dsrl"; - break; - case 1: - /* drotr is decoded as dsrl on non-R2 CPUs */ - if (env->insn_flags & ISA_MIPS32R2) { - if (uimm != 0) { - tcg_gen_rotri_tl(cpu_gpr[rt], t0, uimm); - } else { - tcg_gen_mov_tl(cpu_gpr[rt], t0); - } - opn = "drotr"; - } else { - tcg_gen_shri_tl(cpu_gpr[rt], t0, uimm); - opn = "dsrl"; - } - break; - default: - MIPS_INVAL("invalid dsrl flag"); - generate_exception(ctx, EXCP_RI); - break; + tcg_gen_shri_tl(cpu_gpr[rt], t0, uimm); + opn = "dsrl"; + break; + case OPC_DROTR: + if (uimm != 0) { + tcg_gen_rotri_tl(cpu_gpr[rt], t0, uimm); + } else { + tcg_gen_mov_tl(cpu_gpr[rt], t0); } + opn = "drotr"; break; case OPC_DSLL32: tcg_gen_shli_tl(cpu_gpr[rt], t0, uimm + 32); @@ -1515,29 +1570,16 @@ opn = "dsra32"; break; case OPC_DSRL32: - switch ((ctx->opcode >> 21) & 0x1f) { - case 0: - tcg_gen_shri_tl(cpu_gpr[rt], t0, uimm + 32); - opn = "dsrl32"; - break; - case 1: - /* drotr32 is decoded as dsrl32 on non-R2 CPUs */ - if (env->insn_flags & ISA_MIPS32R2) { - tcg_gen_rotri_tl(cpu_gpr[rt], t0, uimm + 32); - opn = "drotr32"; - } else { - tcg_gen_shri_tl(cpu_gpr[rt], t0, uimm + 32); - opn = "dsrl32"; - } - break; - default: - MIPS_INVAL("invalid dsrl32 flag"); - generate_exception(ctx, EXCP_RI); - break; - } + tcg_gen_shri_tl(cpu_gpr[rt], t0, uimm + 32); + opn = "dsrl32"; + break; + case OPC_DROTR32: + tcg_gen_rotri_tl(cpu_gpr[rt], t0, uimm + 32); + opn = "drotr32"; break; #endif } + (void)opn; /* avoid a compiler warning */ MIPS_DEBUG("%s %s, %s, " TARGET_FMT_lx, opn, regnames[rt], regnames[rs], uimm); tcg_temp_free(t0); } @@ -1569,9 +1611,8 @@ tcg_gen_add_tl(t0, t1, t2); tcg_gen_ext32s_tl(t0, t0); tcg_gen_xor_tl(t1, t1, t2); - tcg_gen_not_tl(t1, t1); tcg_gen_xor_tl(t2, t0, t2); - tcg_gen_and_tl(t1, t1, t2); + tcg_gen_andc_tl(t1, t2, t1); tcg_temp_free(t2); tcg_gen_brcondi_tl(TCG_COND_GE, t1, 0, l1); tcg_temp_free(t1); @@ -1647,9 +1688,8 @@ gen_load_gpr(t2, rt); tcg_gen_add_tl(t0, t1, t2); tcg_gen_xor_tl(t1, t1, t2); - tcg_gen_not_tl(t1, t1); tcg_gen_xor_tl(t2, t0, t2); - tcg_gen_and_tl(t1, t1, t2); + tcg_gen_andc_tl(t1, t2, t1); tcg_temp_free(t2); tcg_gen_brcondi_tl(TCG_COND_GE, t1, 0, l1); tcg_temp_free(t1); @@ -1720,6 +1760,7 @@ opn = "mul"; break; } + (void)opn; /* avoid a compiler warning */ MIPS_DEBUG("%s %s, %s, %s", opn, regnames[rd], regnames[rs], regnames[rt]); } @@ -1757,6 +1798,7 @@ tcg_gen_movi_tl(cpu_gpr[rd], 0); gen_set_label(l1); + (void)opn; /* avoid a compiler warning */ MIPS_DEBUG("%s %s, %s, %s", opn, regnames[rd], regnames[rs], regnames[rt]); } @@ -1817,6 +1859,7 @@ opn = "xor"; break; } + (void)opn; /* avoid a compiler warning */ MIPS_DEBUG("%s %s, %s, %s", opn, regnames[rd], regnames[rs], regnames[rt]); } @@ -1838,14 +1881,15 @@ gen_load_gpr(t1, rt); switch (opc) { case OPC_SLT: - gen_op_lt(cpu_gpr[rd], t0, t1); + tcg_gen_setcond_tl(TCG_COND_LT, cpu_gpr[rd], t0, t1); opn = "slt"; break; case OPC_SLTU: - gen_op_ltu(cpu_gpr[rd], t0, t1); + tcg_gen_setcond_tl(TCG_COND_LTU, cpu_gpr[rd], t0, t1); opn = "sltu"; break; } + (void)opn; /* avoid a compiler warning */ MIPS_DEBUG("%s %s, %s, %s", opn, regnames[rd], regnames[rs], regnames[rt]); tcg_temp_free(t0); tcg_temp_free(t1); @@ -1877,46 +1921,30 @@ opn = "sllv"; break; case OPC_SRAV: - tcg_gen_ext32s_tl(t1, t1); tcg_gen_andi_tl(t0, t0, 0x1f); tcg_gen_sar_tl(cpu_gpr[rd], t1, t0); opn = "srav"; break; case OPC_SRLV: - switch ((ctx->opcode >> 6) & 0x1f) { - case 0: - tcg_gen_ext32u_tl(t1, t1); - tcg_gen_andi_tl(t0, t0, 0x1f); - tcg_gen_shr_tl(t0, t1, t0); - tcg_gen_ext32s_tl(cpu_gpr[rd], t0); - opn = "srlv"; - break; - case 1: - /* rotrv is decoded as srlv on non-R2 CPUs */ - if (env->insn_flags & ISA_MIPS32R2) { - TCGv_i32 t2 = tcg_temp_new_i32(); - TCGv_i32 t3 = tcg_temp_new_i32(); - - tcg_gen_trunc_tl_i32(t2, t0); - tcg_gen_trunc_tl_i32(t3, t1); - tcg_gen_andi_i32(t2, t2, 0x1f); - tcg_gen_rotr_i32(t2, t3, t2); - tcg_gen_ext_i32_tl(cpu_gpr[rd], t2); - tcg_temp_free_i32(t2); - tcg_temp_free_i32(t3); - opn = "rotrv"; - } else { - tcg_gen_ext32u_tl(t1, t1); - tcg_gen_andi_tl(t0, t0, 0x1f); - tcg_gen_shr_tl(t0, t1, t0); - tcg_gen_ext32s_tl(cpu_gpr[rd], t0); - opn = "srlv"; - } - break; - default: - MIPS_INVAL("invalid srlv flag"); - generate_exception(ctx, EXCP_RI); - break; + tcg_gen_ext32u_tl(t1, t1); + tcg_gen_andi_tl(t0, t0, 0x1f); + tcg_gen_shr_tl(t0, t1, t0); + tcg_gen_ext32s_tl(cpu_gpr[rd], t0); + opn = "srlv"; + break; + case OPC_ROTRV: + { + TCGv_i32 t2 = tcg_temp_new_i32(); + TCGv_i32 t3 = tcg_temp_new_i32(); + + tcg_gen_trunc_tl_i32(t2, t0); + tcg_gen_trunc_tl_i32(t3, t1); + tcg_gen_andi_i32(t2, t2, 0x1f); + tcg_gen_rotr_i32(t2, t3, t2); + tcg_gen_ext_i32_tl(cpu_gpr[rd], t2); + tcg_temp_free_i32(t2); + tcg_temp_free_i32(t3); + opn = "rotrv"; } break; #if defined(TARGET_MIPS64) @@ -1931,32 +1959,18 @@ opn = "dsrav"; break; case OPC_DSRLV: - switch ((ctx->opcode >> 6) & 0x1f) { - case 0: - tcg_gen_andi_tl(t0, t0, 0x3f); - tcg_gen_shr_tl(cpu_gpr[rd], t1, t0); - opn = "dsrlv"; - break; - case 1: - /* drotrv is decoded as dsrlv on non-R2 CPUs */ - if (env->insn_flags & ISA_MIPS32R2) { - tcg_gen_andi_tl(t0, t0, 0x3f); - tcg_gen_rotr_tl(cpu_gpr[rd], t1, t0); - opn = "drotrv"; - } else { - tcg_gen_andi_tl(t0, t0, 0x3f); - tcg_gen_shr_tl(t0, t1, t0); - opn = "dsrlv"; - } - break; - default: - MIPS_INVAL("invalid dsrlv flag"); - generate_exception(ctx, EXCP_RI); - break; - } + tcg_gen_andi_tl(t0, t0, 0x3f); + tcg_gen_shr_tl(cpu_gpr[rd], t1, t0); + opn = "dsrlv"; + break; + case OPC_DROTRV: + tcg_gen_andi_tl(t0, t0, 0x3f); + tcg_gen_rotr_tl(cpu_gpr[rd], t1, t0); + opn = "drotrv"; break; #endif } + (void)opn; /* avoid a compiler warning */ MIPS_DEBUG("%s %s, %s, %s", opn, regnames[rd], regnames[rs], regnames[rt]); tcg_temp_free(t0); tcg_temp_free(t1); @@ -1996,6 +2010,7 @@ opn = "mtlo"; break; } + (void)opn; /* avoid a compiler warning */ MIPS_DEBUG("%s %s", opn, regnames[reg]); } @@ -2228,6 +2243,7 @@ generate_exception(ctx, EXCP_RI); goto out; } + (void)opn; /* avoid a compiler warning */ MIPS_DEBUG("%s %s %s", opn, regnames[rs], regnames[rt]); out: tcg_temp_free(t0); @@ -2307,6 +2323,7 @@ goto out; } gen_store_gpr(t0, rd); + (void)opn; /* avoid a compiler warning */ MIPS_DEBUG("%s %s, %s, %s", opn, regnames[rd], regnames[rs], regnames[rt]); out: @@ -2347,8 +2364,224 @@ break; #endif } + (void)opn; /* avoid a compiler warning */ + MIPS_DEBUG("%s %s, %s", opn, regnames[rd], regnames[rs]); + tcg_temp_free(t0); +} + +/* Godson integer instructions */ +static void gen_loongson_integer (DisasContext *ctx, uint32_t opc, + int rd, int rs, int rt) +{ + const char *opn = "loongson"; + TCGv t0, t1; + + if (rd == 0) { + /* Treat as NOP. */ + MIPS_DEBUG("NOP"); + return; + } + + switch (opc) { + case OPC_MULT_G_2E: + case OPC_MULT_G_2F: + case OPC_MULTU_G_2E: + case OPC_MULTU_G_2F: +#if defined(TARGET_MIPS64) + case OPC_DMULT_G_2E: + case OPC_DMULT_G_2F: + case OPC_DMULTU_G_2E: + case OPC_DMULTU_G_2F: +#endif + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); + break; + default: + t0 = tcg_temp_local_new(); + t1 = tcg_temp_local_new(); + break; + } + + gen_load_gpr(t0, rs); + gen_load_gpr(t1, rt); + + switch (opc) { + case OPC_MULT_G_2E: + case OPC_MULT_G_2F: + tcg_gen_mul_tl(cpu_gpr[rd], t0, t1); + tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]); + opn = "mult.g"; + break; + case OPC_MULTU_G_2E: + case OPC_MULTU_G_2F: + tcg_gen_ext32u_tl(t0, t0); + tcg_gen_ext32u_tl(t1, t1); + tcg_gen_mul_tl(cpu_gpr[rd], t0, t1); + tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]); + opn = "multu.g"; + break; + case OPC_DIV_G_2E: + case OPC_DIV_G_2F: + { + int l1 = gen_new_label(); + int l2 = gen_new_label(); + int l3 = gen_new_label(); + tcg_gen_ext32s_tl(t0, t0); + tcg_gen_ext32s_tl(t1, t1); + tcg_gen_brcondi_tl(TCG_COND_NE, t1, 0, l1); + tcg_gen_movi_tl(cpu_gpr[rd], 0); + tcg_gen_br(l3); + gen_set_label(l1); + tcg_gen_brcondi_tl(TCG_COND_NE, t0, INT_MIN, l2); + tcg_gen_brcondi_tl(TCG_COND_NE, t1, -1, l2); + tcg_gen_mov_tl(cpu_gpr[rd], t0); + tcg_gen_br(l3); + gen_set_label(l2); + tcg_gen_div_tl(cpu_gpr[rd], t0, t1); + tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]); + gen_set_label(l3); + } + opn = "div.g"; + break; + case OPC_DIVU_G_2E: + case OPC_DIVU_G_2F: + { + int l1 = gen_new_label(); + int l2 = gen_new_label(); + tcg_gen_ext32u_tl(t0, t0); + tcg_gen_ext32u_tl(t1, t1); + tcg_gen_brcondi_tl(TCG_COND_NE, t1, 0, l1); + tcg_gen_movi_tl(cpu_gpr[rd], 0); + tcg_gen_br(l2); + gen_set_label(l1); + tcg_gen_divu_tl(cpu_gpr[rd], t0, t1); + tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]); + gen_set_label(l2); + } + opn = "divu.g"; + break; + case OPC_MOD_G_2E: + case OPC_MOD_G_2F: + { + int l1 = gen_new_label(); + int l2 = gen_new_label(); + int l3 = gen_new_label(); + tcg_gen_ext32u_tl(t0, t0); + tcg_gen_ext32u_tl(t1, t1); + tcg_gen_brcondi_tl(TCG_COND_EQ, t1, 0, l1); + tcg_gen_brcondi_tl(TCG_COND_NE, t0, INT_MIN, l2); + tcg_gen_brcondi_tl(TCG_COND_NE, t1, -1, l2); + gen_set_label(l1); + tcg_gen_movi_tl(cpu_gpr[rd], 0); + tcg_gen_br(l3); + gen_set_label(l2); + tcg_gen_rem_tl(cpu_gpr[rd], t0, t1); + tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]); + gen_set_label(l3); + } + opn = "mod.g"; + break; + case OPC_MODU_G_2E: + case OPC_MODU_G_2F: + { + int l1 = gen_new_label(); + int l2 = gen_new_label(); + tcg_gen_ext32u_tl(t0, t0); + tcg_gen_ext32u_tl(t1, t1); + tcg_gen_brcondi_tl(TCG_COND_NE, t1, 0, l1); + tcg_gen_movi_tl(cpu_gpr[rd], 0); + tcg_gen_br(l2); + gen_set_label(l1); + tcg_gen_remu_tl(cpu_gpr[rd], t0, t1); + tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]); + gen_set_label(l2); + } + opn = "modu.g"; + break; +#if defined(TARGET_MIPS64) + case OPC_DMULT_G_2E: + case OPC_DMULT_G_2F: + tcg_gen_mul_tl(cpu_gpr[rd], t0, t1); + opn = "dmult.g"; + break; + case OPC_DMULTU_G_2E: + case OPC_DMULTU_G_2F: + tcg_gen_mul_tl(cpu_gpr[rd], t0, t1); + opn = "dmultu.g"; + break; + case OPC_DDIV_G_2E: + case OPC_DDIV_G_2F: + { + int l1 = gen_new_label(); + int l2 = gen_new_label(); + int l3 = gen_new_label(); + tcg_gen_brcondi_tl(TCG_COND_NE, t1, 0, l1); + tcg_gen_movi_tl(cpu_gpr[rd], 0); + tcg_gen_br(l3); + gen_set_label(l1); + tcg_gen_brcondi_tl(TCG_COND_NE, t0, -1LL << 63, l2); + tcg_gen_brcondi_tl(TCG_COND_NE, t1, -1LL, l2); + tcg_gen_mov_tl(cpu_gpr[rd], t0); + tcg_gen_br(l3); + gen_set_label(l2); + tcg_gen_div_tl(cpu_gpr[rd], t0, t1); + gen_set_label(l3); + } + opn = "ddiv.g"; + break; + case OPC_DDIVU_G_2E: + case OPC_DDIVU_G_2F: + { + int l1 = gen_new_label(); + int l2 = gen_new_label(); + tcg_gen_brcondi_tl(TCG_COND_NE, t1, 0, l1); + tcg_gen_movi_tl(cpu_gpr[rd], 0); + tcg_gen_br(l2); + gen_set_label(l1); + tcg_gen_divu_tl(cpu_gpr[rd], t0, t1); + gen_set_label(l2); + } + opn = "ddivu.g"; + break; + case OPC_DMOD_G_2E: + case OPC_DMOD_G_2F: + { + int l1 = gen_new_label(); + int l2 = gen_new_label(); + int l3 = gen_new_label(); + tcg_gen_brcondi_tl(TCG_COND_EQ, t1, 0, l1); + tcg_gen_brcondi_tl(TCG_COND_NE, t0, -1LL << 63, l2); + tcg_gen_brcondi_tl(TCG_COND_NE, t1, -1LL, l2); + gen_set_label(l1); + tcg_gen_movi_tl(cpu_gpr[rd], 0); + tcg_gen_br(l3); + gen_set_label(l2); + tcg_gen_rem_tl(cpu_gpr[rd], t0, t1); + gen_set_label(l3); + } + opn = "dmod.g"; + break; + case OPC_DMODU_G_2E: + case OPC_DMODU_G_2F: + { + int l1 = gen_new_label(); + int l2 = gen_new_label(); + tcg_gen_brcondi_tl(TCG_COND_NE, t1, 0, l1); + tcg_gen_movi_tl(cpu_gpr[rd], 0); + tcg_gen_br(l2); + gen_set_label(l1); + tcg_gen_remu_tl(cpu_gpr[rd], t0, t1); + gen_set_label(l2); + } + opn = "dmodu.g"; + break; +#endif + } + + (void)opn; /* avoid a compiler warning */ MIPS_DEBUG("%s %s, %s", opn, regnames[rd], regnames[rs]); tcg_temp_free(t0); + tcg_temp_free(t1); } /* Traps */ @@ -2466,6 +2699,7 @@ /* Branches (before delay slot) */ static void gen_compute_branch (DisasContext *ctx, uint32_t opc, + int insn_bytes, int rs, int rt, int32_t offset) { target_ulong btgt = -1; @@ -2494,10 +2728,11 @@ gen_load_gpr(t1, rt); bcond_compute = 1; } - btgt = ctx->pc + 4 + offset; + btgt = ctx->pc + insn_bytes + offset; break; case OPC_BGEZ: case OPC_BGEZAL: + case OPC_BGEZALS: case OPC_BGEZALL: case OPC_BGEZL: case OPC_BGTZ: @@ -2506,6 +2741,7 @@ case OPC_BLEZL: case OPC_BLTZ: case OPC_BLTZAL: + case OPC_BLTZALS: case OPC_BLTZALL: case OPC_BLTZL: /* Compare to zero */ @@ -2513,15 +2749,20 @@ gen_load_gpr(t0, rs); bcond_compute = 1; } - btgt = ctx->pc + 4 + offset; + btgt = ctx->pc + insn_bytes + offset; break; case OPC_J: case OPC_JAL: + case OPC_JALX: + case OPC_JALS: + case OPC_JALXS: /* Jump to immediate */ - btgt = ((ctx->pc + 4) & (int32_t)0xF0000000) | (uint32_t)offset; + btgt = ((ctx->pc + insn_bytes) & (int32_t)0xF0000000) | (uint32_t)offset; break; case OPC_JR: case OPC_JALR: + case OPC_JALRC: + case OPC_JALRS: /* Jump to register */ if (offset != 0 && offset != 16) { /* Hint = 0 is JR/JALR, hint 16 is JR.HB/JALR.HB, the @@ -2550,8 +2791,12 @@ ctx->hflags |= MIPS_HFLAG_B; MIPS_DEBUG("balways"); break; + case OPC_BGEZALS: case OPC_BGEZAL: /* 0 >= 0 */ case OPC_BGEZALL: /* 0 >= 0 likely */ + ctx->hflags |= (opc == OPC_BGEZALS + ? MIPS_HFLAG_BDS16 + : MIPS_HFLAG_BDS32); /* Always take and link */ blink = 31; ctx->hflags |= MIPS_HFLAG_B; @@ -2563,10 +2808,18 @@ /* Treat as NOP. */ MIPS_DEBUG("bnever (NOP)"); goto out; + case OPC_BLTZALS: case OPC_BLTZAL: /* 0 < 0 */ - tcg_gen_movi_tl(cpu_gpr[31], ctx->pc + 8); + ctx->hflags |= (opc == OPC_BLTZALS + ? MIPS_HFLAG_BDS16 + : MIPS_HFLAG_BDS32); + /* Handle as an unconditional branch to get correct delay + slot checking. */ + blink = 31; + btgt = ctx->pc + (opc == OPC_BLTZALS ? 6 : 8); + ctx->hflags |= MIPS_HFLAG_B; MIPS_DEBUG("bnever and link"); - goto out; + break; case OPC_BLTZALL: /* 0 < 0 likely */ tcg_gen_movi_tl(cpu_gpr[31], ctx->pc + 8); /* Skip the instruction in the delay slot */ @@ -2584,18 +2837,33 @@ ctx->hflags |= MIPS_HFLAG_B; MIPS_DEBUG("j " TARGET_FMT_lx, btgt); break; + case OPC_JALXS: + case OPC_JALX: + ctx->hflags |= MIPS_HFLAG_BX; + /* Fallthrough */ + case OPC_JALS: case OPC_JAL: blink = 31; ctx->hflags |= MIPS_HFLAG_B; + ctx->hflags |= ((opc == OPC_JALS || opc == OPC_JALXS) + ? MIPS_HFLAG_BDS16 + : MIPS_HFLAG_BDS32); MIPS_DEBUG("jal " TARGET_FMT_lx, btgt); break; case OPC_JR: ctx->hflags |= MIPS_HFLAG_BR; + if (insn_bytes == 4) + ctx->hflags |= MIPS_HFLAG_BDS32; MIPS_DEBUG("jr %s", regnames[rs]); break; + case OPC_JALRS: case OPC_JALR: + case OPC_JALRC: blink = rt; ctx->hflags |= MIPS_HFLAG_BR; + ctx->hflags |= (opc == OPC_JALRS + ? MIPS_HFLAG_BDS16 + : MIPS_HFLAG_BDS32); MIPS_DEBUG("jalr %s, %s", regnames[rt], regnames[rs]); break; default: @@ -2606,76 +2874,84 @@ } else { switch (opc) { case OPC_BEQ: - gen_op_eq(bcond, t0, t1); + tcg_gen_setcond_tl(TCG_COND_EQ, bcond, t0, t1); MIPS_DEBUG("beq %s, %s, " TARGET_FMT_lx, regnames[rs], regnames[rt], btgt); goto not_likely; case OPC_BEQL: - gen_op_eq(bcond, t0, t1); + tcg_gen_setcond_tl(TCG_COND_EQ, bcond, t0, t1); MIPS_DEBUG("beql %s, %s, " TARGET_FMT_lx, regnames[rs], regnames[rt], btgt); goto likely; case OPC_BNE: - gen_op_ne(bcond, t0, t1); + tcg_gen_setcond_tl(TCG_COND_NE, bcond, t0, t1); MIPS_DEBUG("bne %s, %s, " TARGET_FMT_lx, regnames[rs], regnames[rt], btgt); goto not_likely; case OPC_BNEL: - gen_op_ne(bcond, t0, t1); + tcg_gen_setcond_tl(TCG_COND_NE, bcond, t0, t1); MIPS_DEBUG("bnel %s, %s, " TARGET_FMT_lx, regnames[rs], regnames[rt], btgt); goto likely; case OPC_BGEZ: - gen_op_gez(bcond, t0); + tcg_gen_setcondi_tl(TCG_COND_GE, bcond, t0, 0); MIPS_DEBUG("bgez %s, " TARGET_FMT_lx, regnames[rs], btgt); goto not_likely; case OPC_BGEZL: - gen_op_gez(bcond, t0); + tcg_gen_setcondi_tl(TCG_COND_GE, bcond, t0, 0); MIPS_DEBUG("bgezl %s, " TARGET_FMT_lx, regnames[rs], btgt); goto likely; + case OPC_BGEZALS: case OPC_BGEZAL: - gen_op_gez(bcond, t0); + ctx->hflags |= (opc == OPC_BGEZALS + ? MIPS_HFLAG_BDS16 + : MIPS_HFLAG_BDS32); + tcg_gen_setcondi_tl(TCG_COND_GE, bcond, t0, 0); MIPS_DEBUG("bgezal %s, " TARGET_FMT_lx, regnames[rs], btgt); blink = 31; goto not_likely; case OPC_BGEZALL: - gen_op_gez(bcond, t0); + tcg_gen_setcondi_tl(TCG_COND_GE, bcond, t0, 0); blink = 31; MIPS_DEBUG("bgezall %s, " TARGET_FMT_lx, regnames[rs], btgt); goto likely; case OPC_BGTZ: - gen_op_gtz(bcond, t0); + tcg_gen_setcondi_tl(TCG_COND_GT, bcond, t0, 0); MIPS_DEBUG("bgtz %s, " TARGET_FMT_lx, regnames[rs], btgt); goto not_likely; case OPC_BGTZL: - gen_op_gtz(bcond, t0); + tcg_gen_setcondi_tl(TCG_COND_GT, bcond, t0, 0); MIPS_DEBUG("bgtzl %s, " TARGET_FMT_lx, regnames[rs], btgt); goto likely; case OPC_BLEZ: - gen_op_lez(bcond, t0); + tcg_gen_setcondi_tl(TCG_COND_LE, bcond, t0, 0); MIPS_DEBUG("blez %s, " TARGET_FMT_lx, regnames[rs], btgt); goto not_likely; case OPC_BLEZL: - gen_op_lez(bcond, t0); + tcg_gen_setcondi_tl(TCG_COND_LE, bcond, t0, 0); MIPS_DEBUG("blezl %s, " TARGET_FMT_lx, regnames[rs], btgt); goto likely; case OPC_BLTZ: - gen_op_ltz(bcond, t0); + tcg_gen_setcondi_tl(TCG_COND_LT, bcond, t0, 0); MIPS_DEBUG("bltz %s, " TARGET_FMT_lx, regnames[rs], btgt); goto not_likely; case OPC_BLTZL: - gen_op_ltz(bcond, t0); + tcg_gen_setcondi_tl(TCG_COND_LT, bcond, t0, 0); MIPS_DEBUG("bltzl %s, " TARGET_FMT_lx, regnames[rs], btgt); goto likely; + case OPC_BLTZALS: case OPC_BLTZAL: - gen_op_ltz(bcond, t0); + ctx->hflags |= (opc == OPC_BLTZALS + ? MIPS_HFLAG_BDS16 + : MIPS_HFLAG_BDS32); + tcg_gen_setcondi_tl(TCG_COND_LT, bcond, t0, 0); blink = 31; MIPS_DEBUG("bltzal %s, " TARGET_FMT_lx, regnames[rs], btgt); not_likely: ctx->hflags |= MIPS_HFLAG_BC; break; case OPC_BLTZALL: - gen_op_ltz(bcond, t0); + tcg_gen_setcondi_tl(TCG_COND_LT, bcond, t0, 0); blink = 31; MIPS_DEBUG("bltzall %s, " TARGET_FMT_lx, regnames[rs], btgt); likely: @@ -2692,10 +2968,18 @@ ctx->btarget = btgt; if (blink > 0) { - tcg_gen_movi_tl(cpu_gpr[blink], ctx->pc + 8); + int post_delay = insn_bytes; + int lowbit = !!(ctx->hflags & MIPS_HFLAG_M16); + + if (opc != OPC_JALRC) + post_delay += ((ctx->hflags & MIPS_HFLAG_BDS16) ? 2 : 4); + + tcg_gen_movi_tl(cpu_gpr[blink], ctx->pc + post_delay + lowbit); } out: + if (insn_bytes == 2) + ctx->hflags |= MIPS_HFLAG_B16; tcg_temp_free(t0); tcg_temp_free(t1); } @@ -3126,8 +3410,10 @@ gen_helper_mfc0_count(arg); if (use_icount) { gen_io_end(); - ctx->bstate = BS_STOP; } + /* Break the TB to be able to take timer interrupts immediately + after reading count. */ + ctx->bstate = BS_STOP; rn = "Count"; break; /* 6,7 are implementation dependent */ @@ -3464,6 +3750,7 @@ default: goto die; } + (void)rn; /* avoid a compiler warning */ LOG_DISAS("mfc0 %s (reg %d sel %d)\n", rn, reg, sel); return; @@ -4054,6 +4341,7 @@ default: goto die; } + (void)rn; /* avoid a compiler warning */ LOG_DISAS("mtc0 %s (reg %d sel %d)\n", rn, reg, sel); /* For simplicity assume that all writes can cause interrupts. */ if (use_icount) { @@ -4295,8 +4583,10 @@ gen_helper_mfc0_count(arg); if (use_icount) { gen_io_end(); - ctx->bstate = BS_STOP; } + /* Break the TB to be able to take timer interrupts immediately + after reading count. */ + ctx->bstate = BS_STOP; rn = "Count"; break; /* 6,7 are implementation dependent */ @@ -4626,6 +4916,7 @@ default: goto die; } + (void)rn; /* avoid a compiler warning */ LOG_DISAS("dmfc0 %s (reg %d sel %d)\n", rn, reg, sel); return; @@ -4924,7 +5215,17 @@ switch (sel) { case 0: save_cpu_state(ctx, 1); + /* Mark as an IO operation because we may trigger a software + interrupt. */ + if (use_icount) { + gen_io_start(); + } gen_helper_mtc0_cause(arg); + if (use_icount) { + gen_io_end(); + } + /* Stop translation as we may have triggered an intetrupt */ + ctx->bstate = BS_STOP; rn = "Cause"; break; default: @@ -5207,6 +5508,7 @@ default: goto die; } + (void)rn; /* avoid a compiler warning */ LOG_DISAS("dmtc0 %s (reg %d sel %d)\n", rn, reg, sel); /* For simplicity assume that all writes can cause interrupts. */ if (use_icount) { @@ -5667,6 +5969,7 @@ generate_exception(ctx, EXCP_RI); return; } + (void)opn; /* avoid a compiler warning */ MIPS_DEBUG("%s %s %d", opn, regnames[rt], rd); } #endif /* !CONFIG_USER_ONLY */ @@ -5718,9 +6021,8 @@ TCGv_i32 t1 = tcg_temp_new_i32(); tcg_gen_shri_i32(t0, fpu_fcr31, get_fp_bit(cc)); tcg_gen_shri_i32(t1, fpu_fcr31, get_fp_bit(cc+1)); - tcg_gen_or_i32(t0, t0, t1); + tcg_gen_nor_i32(t0, t0, t1); tcg_temp_free_i32(t1); - tcg_gen_not_i32(t0, t0); tcg_gen_andi_i32(t0, t0, 1); tcg_gen_extu_i32_tl(bcond, t0); } @@ -5747,9 +6049,8 @@ tcg_gen_shri_i32(t1, fpu_fcr31, get_fp_bit(cc+2)); tcg_gen_or_i32(t0, t0, t1); tcg_gen_shri_i32(t1, fpu_fcr31, get_fp_bit(cc+3)); - tcg_gen_or_i32(t0, t0, t1); + tcg_gen_nor_i32(t0, t0, t1); tcg_temp_free_i32(t1); - tcg_gen_not_i32(t0, t0); tcg_gen_andi_i32(t0, t0, 1); tcg_gen_extu_i32_tl(bcond, t0); } @@ -5778,6 +6079,7 @@ generate_exception (ctx, EXCP_RI); goto out; } + (void)opn; /* avoid a compiler warning */ MIPS_DEBUG("%s: cond %02x target " TARGET_FMT_lx, opn, ctx->hflags, btarget); ctx->btarget = btarget; @@ -5790,6 +6092,146 @@ #define FOP(func, fmt) (((fmt) << 21) | (func)) +enum fopcode { + OPC_ADD_S = FOP(0, FMT_S), + OPC_SUB_S = FOP(1, FMT_S), + OPC_MUL_S = FOP(2, FMT_S), + OPC_DIV_S = FOP(3, FMT_S), + OPC_SQRT_S = FOP(4, FMT_S), + OPC_ABS_S = FOP(5, FMT_S), + OPC_MOV_S = FOP(6, FMT_S), + OPC_NEG_S = FOP(7, FMT_S), + OPC_ROUND_L_S = FOP(8, FMT_S), + OPC_TRUNC_L_S = FOP(9, FMT_S), + OPC_CEIL_L_S = FOP(10, FMT_S), + OPC_FLOOR_L_S = FOP(11, FMT_S), + OPC_ROUND_W_S = FOP(12, FMT_S), + OPC_TRUNC_W_S = FOP(13, FMT_S), + OPC_CEIL_W_S = FOP(14, FMT_S), + OPC_FLOOR_W_S = FOP(15, FMT_S), + OPC_MOVCF_S = FOP(17, FMT_S), + OPC_MOVZ_S = FOP(18, FMT_S), + OPC_MOVN_S = FOP(19, FMT_S), + OPC_RECIP_S = FOP(21, FMT_S), + OPC_RSQRT_S = FOP(22, FMT_S), + OPC_RECIP2_S = FOP(28, FMT_S), + OPC_RECIP1_S = FOP(29, FMT_S), + OPC_RSQRT1_S = FOP(30, FMT_S), + OPC_RSQRT2_S = FOP(31, FMT_S), + OPC_CVT_D_S = FOP(33, FMT_S), + OPC_CVT_W_S = FOP(36, FMT_S), + OPC_CVT_L_S = FOP(37, FMT_S), + OPC_CVT_PS_S = FOP(38, FMT_S), + OPC_CMP_F_S = FOP (48, FMT_S), + OPC_CMP_UN_S = FOP (49, FMT_S), + OPC_CMP_EQ_S = FOP (50, FMT_S), + OPC_CMP_UEQ_S = FOP (51, FMT_S), + OPC_CMP_OLT_S = FOP (52, FMT_S), + OPC_CMP_ULT_S = FOP (53, FMT_S), + OPC_CMP_OLE_S = FOP (54, FMT_S), + OPC_CMP_ULE_S = FOP (55, FMT_S), + OPC_CMP_SF_S = FOP (56, FMT_S), + OPC_CMP_NGLE_S = FOP (57, FMT_S), + OPC_CMP_SEQ_S = FOP (58, FMT_S), + OPC_CMP_NGL_S = FOP (59, FMT_S), + OPC_CMP_LT_S = FOP (60, FMT_S), + OPC_CMP_NGE_S = FOP (61, FMT_S), + OPC_CMP_LE_S = FOP (62, FMT_S), + OPC_CMP_NGT_S = FOP (63, FMT_S), + + OPC_ADD_D = FOP(0, FMT_D), + OPC_SUB_D = FOP(1, FMT_D), + OPC_MUL_D = FOP(2, FMT_D), + OPC_DIV_D = FOP(3, FMT_D), + OPC_SQRT_D = FOP(4, FMT_D), + OPC_ABS_D = FOP(5, FMT_D), + OPC_MOV_D = FOP(6, FMT_D), + OPC_NEG_D = FOP(7, FMT_D), + OPC_ROUND_L_D = FOP(8, FMT_D), + OPC_TRUNC_L_D = FOP(9, FMT_D), + OPC_CEIL_L_D = FOP(10, FMT_D), + OPC_FLOOR_L_D = FOP(11, FMT_D), + OPC_ROUND_W_D = FOP(12, FMT_D), + OPC_TRUNC_W_D = FOP(13, FMT_D), + OPC_CEIL_W_D = FOP(14, FMT_D), + OPC_FLOOR_W_D = FOP(15, FMT_D), + OPC_MOVCF_D = FOP(17, FMT_D), + OPC_MOVZ_D = FOP(18, FMT_D), + OPC_MOVN_D = FOP(19, FMT_D), + OPC_RECIP_D = FOP(21, FMT_D), + OPC_RSQRT_D = FOP(22, FMT_D), + OPC_RECIP2_D = FOP(28, FMT_D), + OPC_RECIP1_D = FOP(29, FMT_D), + OPC_RSQRT1_D = FOP(30, FMT_D), + OPC_RSQRT2_D = FOP(31, FMT_D), + OPC_CVT_S_D = FOP(32, FMT_D), + OPC_CVT_W_D = FOP(36, FMT_D), + OPC_CVT_L_D = FOP(37, FMT_D), + OPC_CMP_F_D = FOP (48, FMT_D), + OPC_CMP_UN_D = FOP (49, FMT_D), + OPC_CMP_EQ_D = FOP (50, FMT_D), + OPC_CMP_UEQ_D = FOP (51, FMT_D), + OPC_CMP_OLT_D = FOP (52, FMT_D), + OPC_CMP_ULT_D = FOP (53, FMT_D), + OPC_CMP_OLE_D = FOP (54, FMT_D), + OPC_CMP_ULE_D = FOP (55, FMT_D), + OPC_CMP_SF_D = FOP (56, FMT_D), + OPC_CMP_NGLE_D = FOP (57, FMT_D), + OPC_CMP_SEQ_D = FOP (58, FMT_D), + OPC_CMP_NGL_D = FOP (59, FMT_D), + OPC_CMP_LT_D = FOP (60, FMT_D), + OPC_CMP_NGE_D = FOP (61, FMT_D), + OPC_CMP_LE_D = FOP (62, FMT_D), + OPC_CMP_NGT_D = FOP (63, FMT_D), + + OPC_CVT_S_W = FOP(32, FMT_W), + OPC_CVT_D_W = FOP(33, FMT_W), + OPC_CVT_S_L = FOP(32, FMT_L), + OPC_CVT_D_L = FOP(33, FMT_L), + OPC_CVT_PS_PW = FOP(38, FMT_W), + + OPC_ADD_PS = FOP(0, FMT_PS), + OPC_SUB_PS = FOP(1, FMT_PS), + OPC_MUL_PS = FOP(2, FMT_PS), + OPC_DIV_PS = FOP(3, FMT_PS), + OPC_ABS_PS = FOP(5, FMT_PS), + OPC_MOV_PS = FOP(6, FMT_PS), + OPC_NEG_PS = FOP(7, FMT_PS), + OPC_MOVCF_PS = FOP(17, FMT_PS), + OPC_MOVZ_PS = FOP(18, FMT_PS), + OPC_MOVN_PS = FOP(19, FMT_PS), + OPC_ADDR_PS = FOP(24, FMT_PS), + OPC_MULR_PS = FOP(26, FMT_PS), + OPC_RECIP2_PS = FOP(28, FMT_PS), + OPC_RECIP1_PS = FOP(29, FMT_PS), + OPC_RSQRT1_PS = FOP(30, FMT_PS), + OPC_RSQRT2_PS = FOP(31, FMT_PS), + + OPC_CVT_S_PU = FOP(32, FMT_PS), + OPC_CVT_PW_PS = FOP(36, FMT_PS), + OPC_CVT_S_PL = FOP(40, FMT_PS), + OPC_PLL_PS = FOP(44, FMT_PS), + OPC_PLU_PS = FOP(45, FMT_PS), + OPC_PUL_PS = FOP(46, FMT_PS), + OPC_PUU_PS = FOP(47, FMT_PS), + OPC_CMP_F_PS = FOP (48, FMT_PS), + OPC_CMP_UN_PS = FOP (49, FMT_PS), + OPC_CMP_EQ_PS = FOP (50, FMT_PS), + OPC_CMP_UEQ_PS = FOP (51, FMT_PS), + OPC_CMP_OLT_PS = FOP (52, FMT_PS), + OPC_CMP_ULT_PS = FOP (53, FMT_PS), + OPC_CMP_OLE_PS = FOP (54, FMT_PS), + OPC_CMP_ULE_PS = FOP (55, FMT_PS), + OPC_CMP_SF_PS = FOP (56, FMT_PS), + OPC_CMP_NGLE_PS = FOP (57, FMT_PS), + OPC_CMP_SEQ_PS = FOP (58, FMT_PS), + OPC_CMP_NGL_PS = FOP (59, FMT_PS), + OPC_CMP_LT_PS = FOP (60, FMT_PS), + OPC_CMP_NGE_PS = FOP (61, FMT_PS), + OPC_CMP_LE_PS = FOP (62, FMT_PS), + OPC_CMP_NGT_PS = FOP (63, FMT_PS), +}; + static void gen_cp1 (DisasContext *ctx, uint32_t opc, int rt, int fs) { const char *opn = "cp1 move"; @@ -5867,6 +6309,7 @@ generate_exception (ctx, EXCP_RI); goto out; } + (void)opn; /* avoid a compiler warning */ MIPS_DEBUG("%s %s %s", opn, regnames[rt], fregnames[fs]); out: @@ -5970,7 +6413,7 @@ } -static void gen_farith (DisasContext *ctx, uint32_t op1, +static void gen_farith (DisasContext *ctx, enum fopcode op1, int ft, int fs, int fd, int cc) { const char *opn = "farith"; @@ -6013,8 +6456,8 @@ enum { BINOP, CMPOP, OTHEROP } optype = OTHEROP; uint32_t func = ctx->opcode & 0x3f; - switch (ctx->opcode & FOP(0x3f, 0x1f)) { - case FOP(0, 16): + switch (op1) { + case OPC_ADD_S: { TCGv_i32 fp0 = tcg_temp_new_i32(); TCGv_i32 fp1 = tcg_temp_new_i32(); @@ -6029,7 +6472,7 @@ opn = "add.s"; optype = BINOP; break; - case FOP(1, 16): + case OPC_SUB_S: { TCGv_i32 fp0 = tcg_temp_new_i32(); TCGv_i32 fp1 = tcg_temp_new_i32(); @@ -6044,7 +6487,7 @@ opn = "sub.s"; optype = BINOP; break; - case FOP(2, 16): + case OPC_MUL_S: { TCGv_i32 fp0 = tcg_temp_new_i32(); TCGv_i32 fp1 = tcg_temp_new_i32(); @@ -6059,7 +6502,7 @@ opn = "mul.s"; optype = BINOP; break; - case FOP(3, 16): + case OPC_DIV_S: { TCGv_i32 fp0 = tcg_temp_new_i32(); TCGv_i32 fp1 = tcg_temp_new_i32(); @@ -6074,7 +6517,7 @@ opn = "div.s"; optype = BINOP; break; - case FOP(4, 16): + case OPC_SQRT_S: { TCGv_i32 fp0 = tcg_temp_new_i32(); @@ -6085,7 +6528,7 @@ } opn = "sqrt.s"; break; - case FOP(5, 16): + case OPC_ABS_S: { TCGv_i32 fp0 = tcg_temp_new_i32(); @@ -6096,7 +6539,7 @@ } opn = "abs.s"; break; - case FOP(6, 16): + case OPC_MOV_S: { TCGv_i32 fp0 = tcg_temp_new_i32(); @@ -6106,7 +6549,7 @@ } opn = "mov.s"; break; - case FOP(7, 16): + case OPC_NEG_S: { TCGv_i32 fp0 = tcg_temp_new_i32(); @@ -6117,7 +6560,7 @@ } opn = "neg.s"; break; - case FOP(8, 16): + case OPC_ROUND_L_S: check_cp1_64bitmode(ctx); { TCGv_i32 fp32 = tcg_temp_new_i32(); @@ -6131,7 +6574,7 @@ } opn = "round.l.s"; break; - case FOP(9, 16): + case OPC_TRUNC_L_S: check_cp1_64bitmode(ctx); { TCGv_i32 fp32 = tcg_temp_new_i32(); @@ -6145,7 +6588,7 @@ } opn = "trunc.l.s"; break; - case FOP(10, 16): + case OPC_CEIL_L_S: check_cp1_64bitmode(ctx); { TCGv_i32 fp32 = tcg_temp_new_i32(); @@ -6159,7 +6602,7 @@ } opn = "ceil.l.s"; break; - case FOP(11, 16): + case OPC_FLOOR_L_S: check_cp1_64bitmode(ctx); { TCGv_i32 fp32 = tcg_temp_new_i32(); @@ -6173,7 +6616,7 @@ } opn = "floor.l.s"; break; - case FOP(12, 16): + case OPC_ROUND_W_S: { TCGv_i32 fp0 = tcg_temp_new_i32(); @@ -6184,7 +6627,7 @@ } opn = "round.w.s"; break; - case FOP(13, 16): + case OPC_TRUNC_W_S: { TCGv_i32 fp0 = tcg_temp_new_i32(); @@ -6195,7 +6638,7 @@ } opn = "trunc.w.s"; break; - case FOP(14, 16): + case OPC_CEIL_W_S: { TCGv_i32 fp0 = tcg_temp_new_i32(); @@ -6206,7 +6649,7 @@ } opn = "ceil.w.s"; break; - case FOP(15, 16): + case OPC_FLOOR_W_S: { TCGv_i32 fp0 = tcg_temp_new_i32(); @@ -6217,11 +6660,11 @@ } opn = "floor.w.s"; break; - case FOP(17, 16): + case OPC_MOVCF_S: gen_movcf_s(fs, fd, (ft >> 2) & 0x7, ft & 0x1); opn = "movcf.s"; break; - case FOP(18, 16): + case OPC_MOVZ_S: { int l1 = gen_new_label(); TCGv_i32 fp0; @@ -6237,7 +6680,7 @@ } opn = "movz.s"; break; - case FOP(19, 16): + case OPC_MOVN_S: { int l1 = gen_new_label(); TCGv_i32 fp0; @@ -6253,7 +6696,7 @@ } opn = "movn.s"; break; - case FOP(21, 16): + case OPC_RECIP_S: check_cop1x(ctx); { TCGv_i32 fp0 = tcg_temp_new_i32(); @@ -6265,7 +6708,7 @@ } opn = "recip.s"; break; - case FOP(22, 16): + case OPC_RSQRT_S: check_cop1x(ctx); { TCGv_i32 fp0 = tcg_temp_new_i32(); @@ -6277,7 +6720,7 @@ } opn = "rsqrt.s"; break; - case FOP(28, 16): + case OPC_RECIP2_S: check_cp1_64bitmode(ctx); { TCGv_i32 fp0 = tcg_temp_new_i32(); @@ -6292,7 +6735,7 @@ } opn = "recip2.s"; break; - case FOP(29, 16): + case OPC_RECIP1_S: check_cp1_64bitmode(ctx); { TCGv_i32 fp0 = tcg_temp_new_i32(); @@ -6304,7 +6747,7 @@ } opn = "recip1.s"; break; - case FOP(30, 16): + case OPC_RSQRT1_S: check_cp1_64bitmode(ctx); { TCGv_i32 fp0 = tcg_temp_new_i32(); @@ -6316,7 +6759,7 @@ } opn = "rsqrt1.s"; break; - case FOP(31, 16): + case OPC_RSQRT2_S: check_cp1_64bitmode(ctx); { TCGv_i32 fp0 = tcg_temp_new_i32(); @@ -6331,7 +6774,7 @@ } opn = "rsqrt2.s"; break; - case FOP(33, 16): + case OPC_CVT_D_S: check_cp1_registers(ctx, fd); { TCGv_i32 fp32 = tcg_temp_new_i32(); @@ -6345,7 +6788,7 @@ } opn = "cvt.d.s"; break; - case FOP(36, 16): + case OPC_CVT_W_S: { TCGv_i32 fp0 = tcg_temp_new_i32(); @@ -6356,7 +6799,7 @@ } opn = "cvt.w.s"; break; - case FOP(37, 16): + case OPC_CVT_L_S: check_cp1_64bitmode(ctx); { TCGv_i32 fp32 = tcg_temp_new_i32(); @@ -6370,7 +6813,7 @@ } opn = "cvt.l.s"; break; - case FOP(38, 16): + case OPC_CVT_PS_S: check_cp1_64bitmode(ctx); { TCGv_i64 fp64 = tcg_temp_new_i64(); @@ -6387,41 +6830,31 @@ } opn = "cvt.ps.s"; break; - case FOP(48, 16): - case FOP(49, 16): - case FOP(50, 16): - case FOP(51, 16): - case FOP(52, 16): - case FOP(53, 16): - case FOP(54, 16): - case FOP(55, 16): - case FOP(56, 16): - case FOP(57, 16): - case FOP(58, 16): - case FOP(59, 16): - case FOP(60, 16): - case FOP(61, 16): - case FOP(62, 16): - case FOP(63, 16): - { - TCGv_i32 fp0 = tcg_temp_new_i32(); - TCGv_i32 fp1 = tcg_temp_new_i32(); - - gen_load_fpr32(fp0, fs); - gen_load_fpr32(fp1, ft); - if (ctx->opcode & (1 << 6)) { - check_cop1x(ctx); - gen_cmpabs_s(func-48, fp0, fp1, cc); - opn = condnames_abs[func-48]; - } else { - gen_cmp_s(func-48, fp0, fp1, cc); - opn = condnames[func-48]; - } - tcg_temp_free_i32(fp0); - tcg_temp_free_i32(fp1); + case OPC_CMP_F_S: + case OPC_CMP_UN_S: + case OPC_CMP_EQ_S: + case OPC_CMP_UEQ_S: + case OPC_CMP_OLT_S: + case OPC_CMP_ULT_S: + case OPC_CMP_OLE_S: + case OPC_CMP_ULE_S: + case OPC_CMP_SF_S: + case OPC_CMP_NGLE_S: + case OPC_CMP_SEQ_S: + case OPC_CMP_NGL_S: + case OPC_CMP_LT_S: + case OPC_CMP_NGE_S: + case OPC_CMP_LE_S: + case OPC_CMP_NGT_S: + if (ctx->opcode & (1 << 6)) { + gen_cmpabs_s(ctx, func-48, ft, fs, cc); + opn = condnames_abs[func-48]; + } else { + gen_cmp_s(ctx, func-48, ft, fs, cc); + opn = condnames[func-48]; } break; - case FOP(0, 17): + case OPC_ADD_D: check_cp1_registers(ctx, fs | ft | fd); { TCGv_i64 fp0 = tcg_temp_new_i64(); @@ -6437,7 +6870,7 @@ opn = "add.d"; optype = BINOP; break; - case FOP(1, 17): + case OPC_SUB_D: check_cp1_registers(ctx, fs | ft | fd); { TCGv_i64 fp0 = tcg_temp_new_i64(); @@ -6453,7 +6886,7 @@ opn = "sub.d"; optype = BINOP; break; - case FOP(2, 17): + case OPC_MUL_D: check_cp1_registers(ctx, fs | ft | fd); { TCGv_i64 fp0 = tcg_temp_new_i64(); @@ -6469,7 +6902,7 @@ opn = "mul.d"; optype = BINOP; break; - case FOP(3, 17): + case OPC_DIV_D: check_cp1_registers(ctx, fs | ft | fd); { TCGv_i64 fp0 = tcg_temp_new_i64(); @@ -6485,7 +6918,7 @@ opn = "div.d"; optype = BINOP; break; - case FOP(4, 17): + case OPC_SQRT_D: check_cp1_registers(ctx, fs | fd); { TCGv_i64 fp0 = tcg_temp_new_i64(); @@ -6497,7 +6930,7 @@ } opn = "sqrt.d"; break; - case FOP(5, 17): + case OPC_ABS_D: check_cp1_registers(ctx, fs | fd); { TCGv_i64 fp0 = tcg_temp_new_i64(); @@ -6509,7 +6942,7 @@ } opn = "abs.d"; break; - case FOP(6, 17): + case OPC_MOV_D: check_cp1_registers(ctx, fs | fd); { TCGv_i64 fp0 = tcg_temp_new_i64(); @@ -6520,7 +6953,7 @@ } opn = "mov.d"; break; - case FOP(7, 17): + case OPC_NEG_D: check_cp1_registers(ctx, fs | fd); { TCGv_i64 fp0 = tcg_temp_new_i64(); @@ -6532,7 +6965,7 @@ } opn = "neg.d"; break; - case FOP(8, 17): + case OPC_ROUND_L_D: check_cp1_64bitmode(ctx); { TCGv_i64 fp0 = tcg_temp_new_i64(); @@ -6544,7 +6977,7 @@ } opn = "round.l.d"; break; - case FOP(9, 17): + case OPC_TRUNC_L_D: check_cp1_64bitmode(ctx); { TCGv_i64 fp0 = tcg_temp_new_i64(); @@ -6556,7 +6989,7 @@ } opn = "trunc.l.d"; break; - case FOP(10, 17): + case OPC_CEIL_L_D: check_cp1_64bitmode(ctx); { TCGv_i64 fp0 = tcg_temp_new_i64(); @@ -6568,7 +7001,7 @@ } opn = "ceil.l.d"; break; - case FOP(11, 17): + case OPC_FLOOR_L_D: check_cp1_64bitmode(ctx); { TCGv_i64 fp0 = tcg_temp_new_i64(); @@ -6580,7 +7013,7 @@ } opn = "floor.l.d"; break; - case FOP(12, 17): + case OPC_ROUND_W_D: check_cp1_registers(ctx, fs); { TCGv_i32 fp32 = tcg_temp_new_i32(); @@ -6594,7 +7027,7 @@ } opn = "round.w.d"; break; - case FOP(13, 17): + case OPC_TRUNC_W_D: check_cp1_registers(ctx, fs); { TCGv_i32 fp32 = tcg_temp_new_i32(); @@ -6608,7 +7041,7 @@ } opn = "trunc.w.d"; break; - case FOP(14, 17): + case OPC_CEIL_W_D: check_cp1_registers(ctx, fs); { TCGv_i32 fp32 = tcg_temp_new_i32(); @@ -6622,7 +7055,7 @@ } opn = "ceil.w.d"; break; - case FOP(15, 17): + case OPC_FLOOR_W_D: check_cp1_registers(ctx, fs); { TCGv_i32 fp32 = tcg_temp_new_i32(); @@ -6636,11 +7069,11 @@ } opn = "floor.w.d"; break; - case FOP(17, 17): + case OPC_MOVCF_D: gen_movcf_d(ctx, fs, fd, (ft >> 2) & 0x7, ft & 0x1); opn = "movcf.d"; break; - case FOP(18, 17): + case OPC_MOVZ_D: { int l1 = gen_new_label(); TCGv_i64 fp0; @@ -6656,7 +7089,7 @@ } opn = "movz.d"; break; - case FOP(19, 17): + case OPC_MOVN_D: { int l1 = gen_new_label(); TCGv_i64 fp0; @@ -6672,7 +7105,7 @@ } opn = "movn.d"; break; - case FOP(21, 17): + case OPC_RECIP_D: check_cp1_64bitmode(ctx); { TCGv_i64 fp0 = tcg_temp_new_i64(); @@ -6684,7 +7117,7 @@ } opn = "recip.d"; break; - case FOP(22, 17): + case OPC_RSQRT_D: check_cp1_64bitmode(ctx); { TCGv_i64 fp0 = tcg_temp_new_i64(); @@ -6696,7 +7129,7 @@ } opn = "rsqrt.d"; break; - case FOP(28, 17): + case OPC_RECIP2_D: check_cp1_64bitmode(ctx); { TCGv_i64 fp0 = tcg_temp_new_i64(); @@ -6711,7 +7144,7 @@ } opn = "recip2.d"; break; - case FOP(29, 17): + case OPC_RECIP1_D: check_cp1_64bitmode(ctx); { TCGv_i64 fp0 = tcg_temp_new_i64(); @@ -6723,7 +7156,7 @@ } opn = "recip1.d"; break; - case FOP(30, 17): + case OPC_RSQRT1_D: check_cp1_64bitmode(ctx); { TCGv_i64 fp0 = tcg_temp_new_i64(); @@ -6735,7 +7168,7 @@ } opn = "rsqrt1.d"; break; - case FOP(31, 17): + case OPC_RSQRT2_D: check_cp1_64bitmode(ctx); { TCGv_i64 fp0 = tcg_temp_new_i64(); @@ -6750,43 +7183,31 @@ } opn = "rsqrt2.d"; break; - case FOP(48, 17): - case FOP(49, 17): - case FOP(50, 17): - case FOP(51, 17): - case FOP(52, 17): - case FOP(53, 17): - case FOP(54, 17): - case FOP(55, 17): - case FOP(56, 17): - case FOP(57, 17): - case FOP(58, 17): - case FOP(59, 17): - case FOP(60, 17): - case FOP(61, 17): - case FOP(62, 17): - case FOP(63, 17): - { - TCGv_i64 fp0 = tcg_temp_new_i64(); - TCGv_i64 fp1 = tcg_temp_new_i64(); - - gen_load_fpr64(ctx, fp0, fs); - gen_load_fpr64(ctx, fp1, ft); - if (ctx->opcode & (1 << 6)) { - check_cop1x(ctx); - check_cp1_registers(ctx, fs | ft); - gen_cmpabs_d(func-48, fp0, fp1, cc); - opn = condnames_abs[func-48]; - } else { - check_cp1_registers(ctx, fs | ft); - gen_cmp_d(func-48, fp0, fp1, cc); - opn = condnames[func-48]; - } - tcg_temp_free_i64(fp0); - tcg_temp_free_i64(fp1); + case OPC_CMP_F_D: + case OPC_CMP_UN_D: + case OPC_CMP_EQ_D: + case OPC_CMP_UEQ_D: + case OPC_CMP_OLT_D: + case OPC_CMP_ULT_D: + case OPC_CMP_OLE_D: + case OPC_CMP_ULE_D: + case OPC_CMP_SF_D: + case OPC_CMP_NGLE_D: + case OPC_CMP_SEQ_D: + case OPC_CMP_NGL_D: + case OPC_CMP_LT_D: + case OPC_CMP_NGE_D: + case OPC_CMP_LE_D: + case OPC_CMP_NGT_D: + if (ctx->opcode & (1 << 6)) { + gen_cmpabs_d(ctx, func-48, ft, fs, cc); + opn = condnames_abs[func-48]; + } else { + gen_cmp_d(ctx, func-48, ft, fs, cc); + opn = condnames[func-48]; } break; - case FOP(32, 17): + case OPC_CVT_S_D: check_cp1_registers(ctx, fs); { TCGv_i32 fp32 = tcg_temp_new_i32(); @@ -6800,7 +7221,7 @@ } opn = "cvt.s.d"; break; - case FOP(36, 17): + case OPC_CVT_W_D: check_cp1_registers(ctx, fs); { TCGv_i32 fp32 = tcg_temp_new_i32(); @@ -6814,7 +7235,7 @@ } opn = "cvt.w.d"; break; - case FOP(37, 17): + case OPC_CVT_L_D: check_cp1_64bitmode(ctx); { TCGv_i64 fp0 = tcg_temp_new_i64(); @@ -6826,7 +7247,7 @@ } opn = "cvt.l.d"; break; - case FOP(32, 20): + case OPC_CVT_S_W: { TCGv_i32 fp0 = tcg_temp_new_i32(); @@ -6837,7 +7258,7 @@ } opn = "cvt.s.w"; break; - case FOP(33, 20): + case OPC_CVT_D_W: check_cp1_registers(ctx, fd); { TCGv_i32 fp32 = tcg_temp_new_i32(); @@ -6851,7 +7272,7 @@ } opn = "cvt.d.w"; break; - case FOP(32, 21): + case OPC_CVT_S_L: check_cp1_64bitmode(ctx); { TCGv_i32 fp32 = tcg_temp_new_i32(); @@ -6865,7 +7286,7 @@ } opn = "cvt.s.l"; break; - case FOP(33, 21): + case OPC_CVT_D_L: check_cp1_64bitmode(ctx); { TCGv_i64 fp0 = tcg_temp_new_i64(); @@ -6877,7 +7298,7 @@ } opn = "cvt.d.l"; break; - case FOP(38, 20): + case OPC_CVT_PS_PW: check_cp1_64bitmode(ctx); { TCGv_i64 fp0 = tcg_temp_new_i64(); @@ -6889,7 +7310,7 @@ } opn = "cvt.ps.pw"; break; - case FOP(0, 22): + case OPC_ADD_PS: check_cp1_64bitmode(ctx); { TCGv_i64 fp0 = tcg_temp_new_i64(); @@ -6904,7 +7325,7 @@ } opn = "add.ps"; break; - case FOP(1, 22): + case OPC_SUB_PS: check_cp1_64bitmode(ctx); { TCGv_i64 fp0 = tcg_temp_new_i64(); @@ -6919,7 +7340,7 @@ } opn = "sub.ps"; break; - case FOP(2, 22): + case OPC_MUL_PS: check_cp1_64bitmode(ctx); { TCGv_i64 fp0 = tcg_temp_new_i64(); @@ -6934,7 +7355,7 @@ } opn = "mul.ps"; break; - case FOP(5, 22): + case OPC_ABS_PS: check_cp1_64bitmode(ctx); { TCGv_i64 fp0 = tcg_temp_new_i64(); @@ -6946,7 +7367,7 @@ } opn = "abs.ps"; break; - case FOP(6, 22): + case OPC_MOV_PS: check_cp1_64bitmode(ctx); { TCGv_i64 fp0 = tcg_temp_new_i64(); @@ -6957,7 +7378,7 @@ } opn = "mov.ps"; break; - case FOP(7, 22): + case OPC_NEG_PS: check_cp1_64bitmode(ctx); { TCGv_i64 fp0 = tcg_temp_new_i64(); @@ -6969,12 +7390,12 @@ } opn = "neg.ps"; break; - case FOP(17, 22): + case OPC_MOVCF_PS: check_cp1_64bitmode(ctx); gen_movcf_ps(fs, fd, (ft >> 2) & 0x7, ft & 0x1); opn = "movcf.ps"; break; - case FOP(18, 22): + case OPC_MOVZ_PS: check_cp1_64bitmode(ctx); { int l1 = gen_new_label(); @@ -6990,7 +7411,7 @@ } opn = "movz.ps"; break; - case FOP(19, 22): + case OPC_MOVN_PS: check_cp1_64bitmode(ctx); { int l1 = gen_new_label(); @@ -7007,7 +7428,7 @@ } opn = "movn.ps"; break; - case FOP(24, 22): + case OPC_ADDR_PS: check_cp1_64bitmode(ctx); { TCGv_i64 fp0 = tcg_temp_new_i64(); @@ -7022,7 +7443,7 @@ } opn = "addr.ps"; break; - case FOP(26, 22): + case OPC_MULR_PS: check_cp1_64bitmode(ctx); { TCGv_i64 fp0 = tcg_temp_new_i64(); @@ -7037,7 +7458,7 @@ } opn = "mulr.ps"; break; - case FOP(28, 22): + case OPC_RECIP2_PS: check_cp1_64bitmode(ctx); { TCGv_i64 fp0 = tcg_temp_new_i64(); @@ -7052,7 +7473,7 @@ } opn = "recip2.ps"; break; - case FOP(29, 22): + case OPC_RECIP1_PS: check_cp1_64bitmode(ctx); { TCGv_i64 fp0 = tcg_temp_new_i64(); @@ -7064,7 +7485,7 @@ } opn = "recip1.ps"; break; - case FOP(30, 22): + case OPC_RSQRT1_PS: check_cp1_64bitmode(ctx); { TCGv_i64 fp0 = tcg_temp_new_i64(); @@ -7076,7 +7497,7 @@ } opn = "rsqrt1.ps"; break; - case FOP(31, 22): + case OPC_RSQRT2_PS: check_cp1_64bitmode(ctx); { TCGv_i64 fp0 = tcg_temp_new_i64(); @@ -7091,7 +7512,7 @@ } opn = "rsqrt2.ps"; break; - case FOP(32, 22): + case OPC_CVT_S_PU: check_cp1_64bitmode(ctx); { TCGv_i32 fp0 = tcg_temp_new_i32(); @@ -7103,7 +7524,7 @@ } opn = "cvt.s.pu"; break; - case FOP(36, 22): + case OPC_CVT_PW_PS: check_cp1_64bitmode(ctx); { TCGv_i64 fp0 = tcg_temp_new_i64(); @@ -7115,7 +7536,7 @@ } opn = "cvt.pw.ps"; break; - case FOP(40, 22): + case OPC_CVT_S_PL: check_cp1_64bitmode(ctx); { TCGv_i32 fp0 = tcg_temp_new_i32(); @@ -7127,7 +7548,7 @@ } opn = "cvt.s.pl"; break; - case FOP(44, 22): + case OPC_PLL_PS: check_cp1_64bitmode(ctx); { TCGv_i32 fp0 = tcg_temp_new_i32(); @@ -7142,7 +7563,7 @@ } opn = "pll.ps"; break; - case FOP(45, 22): + case OPC_PLU_PS: check_cp1_64bitmode(ctx); { TCGv_i32 fp0 = tcg_temp_new_i32(); @@ -7157,7 +7578,7 @@ } opn = "plu.ps"; break; - case FOP(46, 22): + case OPC_PUL_PS: check_cp1_64bitmode(ctx); { TCGv_i32 fp0 = tcg_temp_new_i32(); @@ -7172,7 +7593,7 @@ } opn = "pul.ps"; break; - case FOP(47, 22): + case OPC_PUU_PS: check_cp1_64bitmode(ctx); { TCGv_i32 fp0 = tcg_temp_new_i32(); @@ -7187,38 +7608,28 @@ } opn = "puu.ps"; break; - case FOP(48, 22): - case FOP(49, 22): - case FOP(50, 22): - case FOP(51, 22): - case FOP(52, 22): - case FOP(53, 22): - case FOP(54, 22): - case FOP(55, 22): - case FOP(56, 22): - case FOP(57, 22): - case FOP(58, 22): - case FOP(59, 22): - case FOP(60, 22): - case FOP(61, 22): - case FOP(62, 22): - case FOP(63, 22): - check_cp1_64bitmode(ctx); - { - TCGv_i64 fp0 = tcg_temp_new_i64(); - TCGv_i64 fp1 = tcg_temp_new_i64(); - - gen_load_fpr64(ctx, fp0, fs); - gen_load_fpr64(ctx, fp1, ft); - if (ctx->opcode & (1 << 6)) { - gen_cmpabs_ps(func-48, fp0, fp1, cc); - opn = condnames_abs[func-48]; - } else { - gen_cmp_ps(func-48, fp0, fp1, cc); - opn = condnames[func-48]; - } - tcg_temp_free_i64(fp0); - tcg_temp_free_i64(fp1); + case OPC_CMP_F_PS: + case OPC_CMP_UN_PS: + case OPC_CMP_EQ_PS: + case OPC_CMP_UEQ_PS: + case OPC_CMP_OLT_PS: + case OPC_CMP_ULT_PS: + case OPC_CMP_OLE_PS: + case OPC_CMP_ULE_PS: + case OPC_CMP_SF_PS: + case OPC_CMP_NGLE_PS: + case OPC_CMP_SEQ_PS: + case OPC_CMP_NGL_PS: + case OPC_CMP_LT_PS: + case OPC_CMP_NGE_PS: + case OPC_CMP_LE_PS: + case OPC_CMP_NGT_PS: + if (ctx->opcode & (1 << 6)) { + gen_cmpabs_ps(ctx, func-48, ft, fs, cc); + opn = condnames_abs[func-48]; + } else { + gen_cmp_ps(ctx, func-48, ft, fs, cc); + opn = condnames[func-48]; } break; default: @@ -7226,6 +7637,7 @@ generate_exception (ctx, EXCP_RI); return; } + (void)opn; /* avoid a compiler warning */ switch (optype) { case BINOP: MIPS_DEBUG("%s %s, %s, %s", opn, fregnames[fd], fregnames[fs], fregnames[ft]); @@ -7338,6 +7750,7 @@ break; } tcg_temp_free(t0); + (void)opn; (void)store; /* avoid compiler warnings */ MIPS_DEBUG("%s %s, %s(%s)", opn, fregnames[store ? fs : fd], regnames[index], regnames[base]); } @@ -7611,36 +8024,3602 @@ generate_exception (ctx, EXCP_RI); return; } + (void)opn; /* avoid a compiler warning */ MIPS_DEBUG("%s %s, %s, %s, %s", opn, fregnames[fd], fregnames[fr], fregnames[fs], fregnames[ft]); } -/* ISA extensions (ASEs) */ -/* MIPS16 extension to MIPS32 */ -/* SmartMIPS extension to MIPS32 */ - -#if defined(TARGET_MIPS64) +static void +gen_rdhwr (CPUState *env, DisasContext *ctx, int rt, int rd) +{ + TCGv t0; -/* MDMX extension to MIPS64 */ + check_insn(env, ctx, ISA_MIPS32R2); + t0 = tcg_temp_new(); + switch (rd) { + case 0: + save_cpu_state(ctx, 1); + gen_helper_rdhwr_cpunum(t0); + gen_store_gpr(t0, rt); + break; + case 1: + save_cpu_state(ctx, 1); + gen_helper_rdhwr_synci_step(t0); + gen_store_gpr(t0, rt); + break; + case 2: + save_cpu_state(ctx, 1); + gen_helper_rdhwr_cc(t0); + gen_store_gpr(t0, rt); + break; + case 3: + save_cpu_state(ctx, 1); + gen_helper_rdhwr_ccres(t0); + gen_store_gpr(t0, rt); + break; + case 29: +#if defined(CONFIG_USER_ONLY) + tcg_gen_ld_tl(t0, cpu_env, offsetof(CPUState, tls_value)); + gen_store_gpr(t0, rt); + break; +#else + /* XXX: Some CPUs implement this in hardware. + Not supported yet. */ #endif + default: /* Invalid */ + MIPS_INVAL("rdhwr"); + generate_exception(ctx, EXCP_RI); + break; + } + tcg_temp_free(t0); +} -static void decode_opc (CPUState *env, DisasContext *ctx) +static void handle_delay_slot (CPUState *env, DisasContext *ctx, + int insn_bytes) { - int32_t offset; - int rs, rt, rd, sa; - uint32_t op, op1, op2; - int16_t imm; + if (ctx->hflags & MIPS_HFLAG_BMASK) { + int proc_hflags = ctx->hflags & MIPS_HFLAG_BMASK; + /* Branches completion */ + ctx->hflags &= ~MIPS_HFLAG_BMASK; + ctx->bstate = BS_BRANCH; + save_cpu_state(ctx, 0); + /* FIXME: Need to clear can_do_io. */ + switch (proc_hflags & MIPS_HFLAG_BMASK_BASE) { + case MIPS_HFLAG_B: + /* unconditional branch */ + MIPS_DEBUG("unconditional branch"); + if (proc_hflags & MIPS_HFLAG_BX) { + tcg_gen_xori_i32(hflags, hflags, MIPS_HFLAG_M16); + } + gen_goto_tb(ctx, 0, ctx->btarget); + break; + case MIPS_HFLAG_BL: + /* blikely taken case */ + MIPS_DEBUG("blikely branch taken"); + gen_goto_tb(ctx, 0, ctx->btarget); + break; + case MIPS_HFLAG_BC: + /* Conditional branch */ + MIPS_DEBUG("conditional branch"); + { + int l1 = gen_new_label(); - /* make sure instructions are on a word boundary */ - if (ctx->pc & 0x3) { - env->CP0_BadVAddr = ctx->pc; + tcg_gen_brcondi_tl(TCG_COND_NE, bcond, 0, l1); + gen_goto_tb(ctx, 1, ctx->pc + insn_bytes); + gen_set_label(l1); + gen_goto_tb(ctx, 0, ctx->btarget); + } + break; + case MIPS_HFLAG_BR: + /* unconditional branch to register */ + MIPS_DEBUG("branch to register"); + if (env->insn_flags & (ASE_MIPS16 | ASE_MICROMIPS)) { + TCGv t0 = tcg_temp_new(); + TCGv_i32 t1 = tcg_temp_new_i32(); + + tcg_gen_andi_tl(t0, btarget, 0x1); + tcg_gen_trunc_tl_i32(t1, t0); + tcg_temp_free(t0); + tcg_gen_andi_i32(hflags, hflags, ~(uint32_t)MIPS_HFLAG_M16); + tcg_gen_shli_i32(t1, t1, MIPS_HFLAG_M16_SHIFT); + tcg_gen_or_i32(hflags, hflags, t1); + tcg_temp_free_i32(t1); + + tcg_gen_andi_tl(cpu_PC, btarget, ~(target_ulong)0x1); + } else { + tcg_gen_mov_tl(cpu_PC, btarget); + } + if (ctx->singlestep_enabled) { + save_cpu_state(ctx, 0); + gen_helper_0i(raise_exception, EXCP_DEBUG); + } + tcg_gen_exit_tb(0); + break; + default: + MIPS_DEBUG("unknown branch"); + break; + } + } +} + +/* ISA extensions (ASEs) */ +/* MIPS16 extension to MIPS32 */ + +/* MIPS16 major opcodes */ +enum { + M16_OPC_ADDIUSP = 0x00, + M16_OPC_ADDIUPC = 0x01, + M16_OPC_B = 0x02, + M16_OPC_JAL = 0x03, + M16_OPC_BEQZ = 0x04, + M16_OPC_BNEQZ = 0x05, + M16_OPC_SHIFT = 0x06, + M16_OPC_LD = 0x07, + M16_OPC_RRIA = 0x08, + M16_OPC_ADDIU8 = 0x09, + M16_OPC_SLTI = 0x0a, + M16_OPC_SLTIU = 0x0b, + M16_OPC_I8 = 0x0c, + M16_OPC_LI = 0x0d, + M16_OPC_CMPI = 0x0e, + M16_OPC_SD = 0x0f, + M16_OPC_LB = 0x10, + M16_OPC_LH = 0x11, + M16_OPC_LWSP = 0x12, + M16_OPC_LW = 0x13, + M16_OPC_LBU = 0x14, + M16_OPC_LHU = 0x15, + M16_OPC_LWPC = 0x16, + M16_OPC_LWU = 0x17, + M16_OPC_SB = 0x18, + M16_OPC_SH = 0x19, + M16_OPC_SWSP = 0x1a, + M16_OPC_SW = 0x1b, + M16_OPC_RRR = 0x1c, + M16_OPC_RR = 0x1d, + M16_OPC_EXTEND = 0x1e, + M16_OPC_I64 = 0x1f +}; + +/* I8 funct field */ +enum { + I8_BTEQZ = 0x0, + I8_BTNEZ = 0x1, + I8_SWRASP = 0x2, + I8_ADJSP = 0x3, + I8_SVRS = 0x4, + I8_MOV32R = 0x5, + I8_MOVR32 = 0x7 +}; + +/* RRR f field */ +enum { + RRR_DADDU = 0x0, + RRR_ADDU = 0x1, + RRR_DSUBU = 0x2, + RRR_SUBU = 0x3 +}; + +/* RR funct field */ +enum { + RR_JR = 0x00, + RR_SDBBP = 0x01, + RR_SLT = 0x02, + RR_SLTU = 0x03, + RR_SLLV = 0x04, + RR_BREAK = 0x05, + RR_SRLV = 0x06, + RR_SRAV = 0x07, + RR_DSRL = 0x08, + RR_CMP = 0x0a, + RR_NEG = 0x0b, + RR_AND = 0x0c, + RR_OR = 0x0d, + RR_XOR = 0x0e, + RR_NOT = 0x0f, + RR_MFHI = 0x10, + RR_CNVT = 0x11, + RR_MFLO = 0x12, + RR_DSRA = 0x13, + RR_DSLLV = 0x14, + RR_DSRLV = 0x16, + RR_DSRAV = 0x17, + RR_MULT = 0x18, + RR_MULTU = 0x19, + RR_DIV = 0x1a, + RR_DIVU = 0x1b, + RR_DMULT = 0x1c, + RR_DMULTU = 0x1d, + RR_DDIV = 0x1e, + RR_DDIVU = 0x1f +}; + +/* I64 funct field */ +enum { + I64_LDSP = 0x0, + I64_SDSP = 0x1, + I64_SDRASP = 0x2, + I64_DADJSP = 0x3, + I64_LDPC = 0x4, + I64_DADDIU5 = 0x5, + I64_DADDIUPC = 0x6, + I64_DADDIUSP = 0x7 +}; + +/* RR ry field for CNVT */ +enum { + RR_RY_CNVT_ZEB = 0x0, + RR_RY_CNVT_ZEH = 0x1, + RR_RY_CNVT_ZEW = 0x2, + RR_RY_CNVT_SEB = 0x4, + RR_RY_CNVT_SEH = 0x5, + RR_RY_CNVT_SEW = 0x6, +}; + +static int xlat (int r) +{ + static int map[] = { 16, 17, 2, 3, 4, 5, 6, 7 }; + + return map[r]; +} + +static void gen_mips16_save (DisasContext *ctx, + int xsregs, int aregs, + int do_ra, int do_s0, int do_s1, + int framesize) +{ + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + int args, astatic; + + switch (aregs) { + case 0: + case 1: + case 2: + case 3: + case 11: + args = 0; + break; + case 4: + case 5: + case 6: + case 7: + args = 1; + break; + case 8: + case 9: + case 10: + args = 2; + break; + case 12: + case 13: + args = 3; + break; + case 14: + args = 4; + break; + default: + generate_exception(ctx, EXCP_RI); + return; + } + + switch (args) { + case 4: + gen_base_offset_addr(ctx, t0, 29, 12); + gen_load_gpr(t1, 7); + op_st_sw(t1, t0, ctx); + /* Fall through */ + case 3: + gen_base_offset_addr(ctx, t0, 29, 8); + gen_load_gpr(t1, 6); + op_st_sw(t1, t0, ctx); + /* Fall through */ + case 2: + gen_base_offset_addr(ctx, t0, 29, 4); + gen_load_gpr(t1, 5); + op_st_sw(t1, t0, ctx); + /* Fall through */ + case 1: + gen_base_offset_addr(ctx, t0, 29, 0); + gen_load_gpr(t1, 4); + op_st_sw(t1, t0, ctx); + } + + gen_load_gpr(t0, 29); + +#define DECR_AND_STORE(reg) do { \ + tcg_gen_subi_tl(t0, t0, 4); \ + gen_load_gpr(t1, reg); \ + op_st_sw(t1, t0, ctx); \ + } while (0) + + if (do_ra) { + DECR_AND_STORE(31); + } + + switch (xsregs) { + case 7: + DECR_AND_STORE(30); + /* Fall through */ + case 6: + DECR_AND_STORE(23); + /* Fall through */ + case 5: + DECR_AND_STORE(22); + /* Fall through */ + case 4: + DECR_AND_STORE(21); + /* Fall through */ + case 3: + DECR_AND_STORE(20); + /* Fall through */ + case 2: + DECR_AND_STORE(19); + /* Fall through */ + case 1: + DECR_AND_STORE(18); + } + + if (do_s1) { + DECR_AND_STORE(17); + } + if (do_s0) { + DECR_AND_STORE(16); + } + + switch (aregs) { + case 0: + case 4: + case 8: + case 12: + case 14: + astatic = 0; + break; + case 1: + case 5: + case 9: + case 13: + astatic = 1; + break; + case 2: + case 6: + case 10: + astatic = 2; + break; + case 3: + case 7: + astatic = 3; + break; + case 11: + astatic = 4; + break; + default: + generate_exception(ctx, EXCP_RI); + return; + } + + if (astatic > 0) { + DECR_AND_STORE(7); + if (astatic > 1) { + DECR_AND_STORE(6); + if (astatic > 2) { + DECR_AND_STORE(5); + if (astatic > 3) { + DECR_AND_STORE(4); + } + } + } + } +#undef DECR_AND_STORE + + tcg_gen_subi_tl(cpu_gpr[29], cpu_gpr[29], framesize); + tcg_temp_free(t0); + tcg_temp_free(t1); +} + +static void gen_mips16_restore (DisasContext *ctx, + int xsregs, int aregs, + int do_ra, int do_s0, int do_s1, + int framesize) +{ + int astatic; + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + + tcg_gen_addi_tl(t0, cpu_gpr[29], framesize); + +#define DECR_AND_LOAD(reg) do { \ + tcg_gen_subi_tl(t0, t0, 4); \ + op_ld_lw(t1, t0, ctx); \ + gen_store_gpr(t1, reg); \ + } while (0) + + if (do_ra) { + DECR_AND_LOAD(31); + } + + switch (xsregs) { + case 7: + DECR_AND_LOAD(30); + /* Fall through */ + case 6: + DECR_AND_LOAD(23); + /* Fall through */ + case 5: + DECR_AND_LOAD(22); + /* Fall through */ + case 4: + DECR_AND_LOAD(21); + /* Fall through */ + case 3: + DECR_AND_LOAD(20); + /* Fall through */ + case 2: + DECR_AND_LOAD(19); + /* Fall through */ + case 1: + DECR_AND_LOAD(18); + } + + if (do_s1) { + DECR_AND_LOAD(17); + } + if (do_s0) { + DECR_AND_LOAD(16); + } + + switch (aregs) { + case 0: + case 4: + case 8: + case 12: + case 14: + astatic = 0; + break; + case 1: + case 5: + case 9: + case 13: + astatic = 1; + break; + case 2: + case 6: + case 10: + astatic = 2; + break; + case 3: + case 7: + astatic = 3; + break; + case 11: + astatic = 4; + break; + default: + generate_exception(ctx, EXCP_RI); + return; + } + + if (astatic > 0) { + DECR_AND_LOAD(7); + if (astatic > 1) { + DECR_AND_LOAD(6); + if (astatic > 2) { + DECR_AND_LOAD(5); + if (astatic > 3) { + DECR_AND_LOAD(4); + } + } + } + } +#undef DECR_AND_LOAD + + tcg_gen_addi_tl(cpu_gpr[29], cpu_gpr[29], framesize); + tcg_temp_free(t0); + tcg_temp_free(t1); +} + +static void gen_addiupc (DisasContext *ctx, int rx, int imm, + int is_64_bit, int extended) +{ + TCGv t0; + + if (extended && (ctx->hflags & MIPS_HFLAG_BMASK)) { + generate_exception(ctx, EXCP_RI); + return; + } + + t0 = tcg_temp_new(); + + tcg_gen_movi_tl(t0, pc_relative_pc(ctx)); + tcg_gen_addi_tl(cpu_gpr[rx], t0, imm); + if (!is_64_bit) { + tcg_gen_ext32s_tl(cpu_gpr[rx], cpu_gpr[rx]); + } + + tcg_temp_free(t0); +} + +#if defined(TARGET_MIPS64) +static void decode_i64_mips16 (CPUState *env, DisasContext *ctx, + int ry, int funct, int16_t offset, + int extended) +{ + switch (funct) { + case I64_LDSP: + check_mips_64(ctx); + offset = extended ? offset : offset << 3; + gen_ld(env, ctx, OPC_LD, ry, 29, offset); + break; + case I64_SDSP: + check_mips_64(ctx); + offset = extended ? offset : offset << 3; + gen_st(ctx, OPC_SD, ry, 29, offset); + break; + case I64_SDRASP: + check_mips_64(ctx); + offset = extended ? offset : (ctx->opcode & 0xff) << 3; + gen_st(ctx, OPC_SD, 31, 29, offset); + break; + case I64_DADJSP: + check_mips_64(ctx); + offset = extended ? offset : ((int8_t)ctx->opcode) << 3; + gen_arith_imm(env, ctx, OPC_DADDIU, 29, 29, offset); + break; + case I64_LDPC: + if (extended && (ctx->hflags & MIPS_HFLAG_BMASK)) { + generate_exception(ctx, EXCP_RI); + } else { + offset = extended ? offset : offset << 3; + gen_ld(env, ctx, OPC_LDPC, ry, 0, offset); + } + break; + case I64_DADDIU5: + check_mips_64(ctx); + offset = extended ? offset : ((int8_t)(offset << 3)) >> 3; + gen_arith_imm(env, ctx, OPC_DADDIU, ry, ry, offset); + break; + case I64_DADDIUPC: + check_mips_64(ctx); + offset = extended ? offset : offset << 2; + gen_addiupc(ctx, ry, offset, 1, extended); + break; + case I64_DADDIUSP: + check_mips_64(ctx); + offset = extended ? offset : offset << 2; + gen_arith_imm(env, ctx, OPC_DADDIU, ry, 29, offset); + break; + } +} +#endif + +static int decode_extended_mips16_opc (CPUState *env, DisasContext *ctx, + int *is_branch) +{ + int extend = lduw_code(ctx->pc + 2); + int op, rx, ry, funct, sa; + int16_t imm, offset; + + ctx->opcode = (ctx->opcode << 16) | extend; + op = (ctx->opcode >> 11) & 0x1f; + sa = (ctx->opcode >> 22) & 0x1f; + funct = (ctx->opcode >> 8) & 0x7; + rx = xlat((ctx->opcode >> 8) & 0x7); + ry = xlat((ctx->opcode >> 5) & 0x7); + offset = imm = (int16_t) (((ctx->opcode >> 16) & 0x1f) << 11 + | ((ctx->opcode >> 21) & 0x3f) << 5 + | (ctx->opcode & 0x1f)); + + /* The extended opcodes cleverly reuse the opcodes from their 16-bit + counterparts. */ + switch (op) { + case M16_OPC_ADDIUSP: + gen_arith_imm(env, ctx, OPC_ADDIU, rx, 29, imm); + break; + case M16_OPC_ADDIUPC: + gen_addiupc(ctx, rx, imm, 0, 1); + break; + case M16_OPC_B: + gen_compute_branch(ctx, OPC_BEQ, 4, 0, 0, offset << 1); + /* No delay slot, so just process as a normal instruction */ + break; + case M16_OPC_BEQZ: + gen_compute_branch(ctx, OPC_BEQ, 4, rx, 0, offset << 1); + /* No delay slot, so just process as a normal instruction */ + break; + case M16_OPC_BNEQZ: + gen_compute_branch(ctx, OPC_BNE, 4, rx, 0, offset << 1); + /* No delay slot, so just process as a normal instruction */ + break; + case M16_OPC_SHIFT: + switch (ctx->opcode & 0x3) { + case 0x0: + gen_shift_imm(env, ctx, OPC_SLL, rx, ry, sa); + break; + case 0x1: +#if defined(TARGET_MIPS64) + check_mips_64(ctx); + gen_shift_imm(env, ctx, OPC_DSLL, rx, ry, sa); +#else + generate_exception(ctx, EXCP_RI); +#endif + break; + case 0x2: + gen_shift_imm(env, ctx, OPC_SRL, rx, ry, sa); + break; + case 0x3: + gen_shift_imm(env, ctx, OPC_SRA, rx, ry, sa); + break; + } + break; +#if defined(TARGET_MIPS64) + case M16_OPC_LD: + check_mips_64(ctx); + gen_ld(env, ctx, OPC_LD, ry, rx, offset); + break; +#endif + case M16_OPC_RRIA: + imm = ctx->opcode & 0xf; + imm = imm | ((ctx->opcode >> 20) & 0x7f) << 4; + imm = imm | ((ctx->opcode >> 16) & 0xf) << 11; + imm = (int16_t) (imm << 1) >> 1; + if ((ctx->opcode >> 4) & 0x1) { +#if defined(TARGET_MIPS64) + check_mips_64(ctx); + gen_arith_imm(env, ctx, OPC_DADDIU, ry, rx, imm); +#else + generate_exception(ctx, EXCP_RI); +#endif + } else { + gen_arith_imm(env, ctx, OPC_ADDIU, ry, rx, imm); + } + break; + case M16_OPC_ADDIU8: + gen_arith_imm(env, ctx, OPC_ADDIU, rx, rx, imm); + break; + case M16_OPC_SLTI: + gen_slt_imm(env, OPC_SLTI, 24, rx, imm); + break; + case M16_OPC_SLTIU: + gen_slt_imm(env, OPC_SLTIU, 24, rx, imm); + break; + case M16_OPC_I8: + switch (funct) { + case I8_BTEQZ: + gen_compute_branch(ctx, OPC_BEQ, 4, 24, 0, offset << 1); + break; + case I8_BTNEZ: + gen_compute_branch(ctx, OPC_BNE, 4, 24, 0, offset << 1); + break; + case I8_SWRASP: + gen_st(ctx, OPC_SW, 31, 29, imm); + break; + case I8_ADJSP: + gen_arith_imm(env, ctx, OPC_ADDIU, 29, 29, imm); + break; + case I8_SVRS: + { + int xsregs = (ctx->opcode >> 24) & 0x7; + int aregs = (ctx->opcode >> 16) & 0xf; + int do_ra = (ctx->opcode >> 6) & 0x1; + int do_s0 = (ctx->opcode >> 5) & 0x1; + int do_s1 = (ctx->opcode >> 4) & 0x1; + int framesize = (((ctx->opcode >> 20) & 0xf) << 4 + | (ctx->opcode & 0xf)) << 3; + + if (ctx->opcode & (1 << 7)) { + gen_mips16_save(ctx, xsregs, aregs, + do_ra, do_s0, do_s1, + framesize); + } else { + gen_mips16_restore(ctx, xsregs, aregs, + do_ra, do_s0, do_s1, + framesize); + } + } + break; + default: + generate_exception(ctx, EXCP_RI); + break; + } + break; + case M16_OPC_LI: + tcg_gen_movi_tl(cpu_gpr[rx], (uint16_t) imm); + break; + case M16_OPC_CMPI: + tcg_gen_xori_tl(cpu_gpr[24], cpu_gpr[rx], (uint16_t) imm); + break; +#if defined(TARGET_MIPS64) + case M16_OPC_SD: + gen_st(ctx, OPC_SD, ry, rx, offset); + break; +#endif + case M16_OPC_LB: + gen_ld(env, ctx, OPC_LB, ry, rx, offset); + break; + case M16_OPC_LH: + gen_ld(env, ctx, OPC_LH, ry, rx, offset); + break; + case M16_OPC_LWSP: + gen_ld(env, ctx, OPC_LW, rx, 29, offset); + break; + case M16_OPC_LW: + gen_ld(env, ctx, OPC_LW, ry, rx, offset); + break; + case M16_OPC_LBU: + gen_ld(env, ctx, OPC_LBU, ry, rx, offset); + break; + case M16_OPC_LHU: + gen_ld(env, ctx, OPC_LHU, ry, rx, offset); + break; + case M16_OPC_LWPC: + gen_ld(env, ctx, OPC_LWPC, rx, 0, offset); + break; +#if defined(TARGET_MIPS64) + case M16_OPC_LWU: + gen_ld(env, ctx, OPC_LWU, ry, rx, offset); + break; +#endif + case M16_OPC_SB: + gen_st(ctx, OPC_SB, ry, rx, offset); + break; + case M16_OPC_SH: + gen_st(ctx, OPC_SH, ry, rx, offset); + break; + case M16_OPC_SWSP: + gen_st(ctx, OPC_SW, rx, 29, offset); + break; + case M16_OPC_SW: + gen_st(ctx, OPC_SW, ry, rx, offset); + break; +#if defined(TARGET_MIPS64) + case M16_OPC_I64: + decode_i64_mips16(env, ctx, ry, funct, offset, 1); + break; +#endif + default: + generate_exception(ctx, EXCP_RI); + break; + } + + return 4; +} + +static int decode_mips16_opc (CPUState *env, DisasContext *ctx, + int *is_branch) +{ + int rx, ry; + int sa; + int op, cnvt_op, op1, offset; + int funct; + int n_bytes; + + op = (ctx->opcode >> 11) & 0x1f; + sa = (ctx->opcode >> 2) & 0x7; + sa = sa == 0 ? 8 : sa; + rx = xlat((ctx->opcode >> 8) & 0x7); + cnvt_op = (ctx->opcode >> 5) & 0x7; + ry = xlat((ctx->opcode >> 5) & 0x7); + op1 = offset = ctx->opcode & 0x1f; + + n_bytes = 2; + + switch (op) { + case M16_OPC_ADDIUSP: + { + int16_t imm = ((uint8_t) ctx->opcode) << 2; + + gen_arith_imm(env, ctx, OPC_ADDIU, rx, 29, imm); + } + break; + case M16_OPC_ADDIUPC: + gen_addiupc(ctx, rx, ((uint8_t) ctx->opcode) << 2, 0, 0); + break; + case M16_OPC_B: + offset = (ctx->opcode & 0x7ff) << 1; + offset = (int16_t)(offset << 4) >> 4; + gen_compute_branch(ctx, OPC_BEQ, 2, 0, 0, offset); + /* No delay slot, so just process as a normal instruction */ + break; + case M16_OPC_JAL: + offset = lduw_code(ctx->pc + 2); + offset = (((ctx->opcode & 0x1f) << 21) + | ((ctx->opcode >> 5) & 0x1f) << 16 + | offset) << 2; + op = ((ctx->opcode >> 10) & 0x1) ? OPC_JALXS : OPC_JALS; + gen_compute_branch(ctx, op, 4, rx, ry, offset); + n_bytes = 4; + *is_branch = 1; + break; + case M16_OPC_BEQZ: + gen_compute_branch(ctx, OPC_BEQ, 2, rx, 0, ((int8_t)ctx->opcode) << 1); + /* No delay slot, so just process as a normal instruction */ + break; + case M16_OPC_BNEQZ: + gen_compute_branch(ctx, OPC_BNE, 2, rx, 0, ((int8_t)ctx->opcode) << 1); + /* No delay slot, so just process as a normal instruction */ + break; + case M16_OPC_SHIFT: + switch (ctx->opcode & 0x3) { + case 0x0: + gen_shift_imm(env, ctx, OPC_SLL, rx, ry, sa); + break; + case 0x1: +#if defined(TARGET_MIPS64) + check_mips_64(ctx); + gen_shift_imm(env, ctx, OPC_DSLL, rx, ry, sa); +#else + generate_exception(ctx, EXCP_RI); +#endif + break; + case 0x2: + gen_shift_imm(env, ctx, OPC_SRL, rx, ry, sa); + break; + case 0x3: + gen_shift_imm(env, ctx, OPC_SRA, rx, ry, sa); + break; + } + break; +#if defined(TARGET_MIPS64) + case M16_OPC_LD: + check_mips_64(ctx); + gen_ld(env, ctx, OPC_LD, ry, rx, offset << 3); + break; +#endif + case M16_OPC_RRIA: + { + int16_t imm = (int8_t)((ctx->opcode & 0xf) << 4) >> 4; + + if ((ctx->opcode >> 4) & 1) { +#if defined(TARGET_MIPS64) + check_mips_64(ctx); + gen_arith_imm(env, ctx, OPC_DADDIU, ry, rx, imm); +#else + generate_exception(ctx, EXCP_RI); +#endif + } else { + gen_arith_imm(env, ctx, OPC_ADDIU, ry, rx, imm); + } + } + break; + case M16_OPC_ADDIU8: + { + int16_t imm = (int8_t) ctx->opcode; + + gen_arith_imm(env, ctx, OPC_ADDIU, rx, rx, imm); + } + break; + case M16_OPC_SLTI: + { + int16_t imm = (uint8_t) ctx->opcode; + + gen_slt_imm(env, OPC_SLTI, 24, rx, imm); + } + break; + case M16_OPC_SLTIU: + { + int16_t imm = (uint8_t) ctx->opcode; + + gen_slt_imm(env, OPC_SLTIU, 24, rx, imm); + } + break; + case M16_OPC_I8: + { + int reg32; + + funct = (ctx->opcode >> 8) & 0x7; + switch (funct) { + case I8_BTEQZ: + gen_compute_branch(ctx, OPC_BEQ, 2, 24, 0, + ((int8_t)ctx->opcode) << 1); + break; + case I8_BTNEZ: + gen_compute_branch(ctx, OPC_BNE, 2, 24, 0, + ((int8_t)ctx->opcode) << 1); + break; + case I8_SWRASP: + gen_st(ctx, OPC_SW, 31, 29, (ctx->opcode & 0xff) << 2); + break; + case I8_ADJSP: + gen_arith_imm(env, ctx, OPC_ADDIU, 29, 29, + ((int8_t)ctx->opcode) << 3); + break; + case I8_SVRS: + { + int do_ra = ctx->opcode & (1 << 6); + int do_s0 = ctx->opcode & (1 << 5); + int do_s1 = ctx->opcode & (1 << 4); + int framesize = ctx->opcode & 0xf; + + if (framesize == 0) { + framesize = 128; + } else { + framesize = framesize << 3; + } + + if (ctx->opcode & (1 << 7)) { + gen_mips16_save(ctx, 0, 0, + do_ra, do_s0, do_s1, framesize); + } else { + gen_mips16_restore(ctx, 0, 0, + do_ra, do_s0, do_s1, framesize); + } + } + break; + case I8_MOV32R: + { + int rz = xlat(ctx->opcode & 0x7); + + reg32 = (((ctx->opcode >> 3) & 0x3) << 3) | + ((ctx->opcode >> 5) & 0x7); + gen_arith(env, ctx, OPC_ADDU, reg32, rz, 0); + } + break; + case I8_MOVR32: + reg32 = ctx->opcode & 0x1f; + gen_arith(env, ctx, OPC_ADDU, ry, reg32, 0); + break; + default: + generate_exception(ctx, EXCP_RI); + break; + } + } + break; + case M16_OPC_LI: + { + int16_t imm = (uint8_t) ctx->opcode; + + gen_arith_imm(env, ctx, OPC_ADDIU, rx, 0, imm); + } + break; + case M16_OPC_CMPI: + { + int16_t imm = (uint8_t) ctx->opcode; + + gen_logic_imm(env, OPC_XORI, 24, rx, imm); + } + break; +#if defined(TARGET_MIPS64) + case M16_OPC_SD: + check_mips_64(ctx); + gen_st(ctx, OPC_SD, ry, rx, offset << 3); + break; +#endif + case M16_OPC_LB: + gen_ld(env, ctx, OPC_LB, ry, rx, offset); + break; + case M16_OPC_LH: + gen_ld(env, ctx, OPC_LH, ry, rx, offset << 1); + break; + case M16_OPC_LWSP: + gen_ld(env, ctx, OPC_LW, rx, 29, ((uint8_t)ctx->opcode) << 2); + break; + case M16_OPC_LW: + gen_ld(env, ctx, OPC_LW, ry, rx, offset << 2); + break; + case M16_OPC_LBU: + gen_ld(env, ctx, OPC_LBU, ry, rx, offset); + break; + case M16_OPC_LHU: + gen_ld(env, ctx, OPC_LHU, ry, rx, offset << 1); + break; + case M16_OPC_LWPC: + gen_ld(env, ctx, OPC_LWPC, rx, 0, ((uint8_t)ctx->opcode) << 2); + break; +#if defined (TARGET_MIPS64) + case M16_OPC_LWU: + check_mips_64(ctx); + gen_ld(env, ctx, OPC_LWU, ry, rx, offset << 2); + break; +#endif + case M16_OPC_SB: + gen_st(ctx, OPC_SB, ry, rx, offset); + break; + case M16_OPC_SH: + gen_st(ctx, OPC_SH, ry, rx, offset << 1); + break; + case M16_OPC_SWSP: + gen_st(ctx, OPC_SW, rx, 29, ((uint8_t)ctx->opcode) << 2); + break; + case M16_OPC_SW: + gen_st(ctx, OPC_SW, ry, rx, offset << 2); + break; + case M16_OPC_RRR: + { + int rz = xlat((ctx->opcode >> 2) & 0x7); + int mips32_op; + + switch (ctx->opcode & 0x3) { + case RRR_ADDU: + mips32_op = OPC_ADDU; + break; + case RRR_SUBU: + mips32_op = OPC_SUBU; + break; +#if defined(TARGET_MIPS64) + case RRR_DADDU: + mips32_op = OPC_DADDU; + check_mips_64(ctx); + break; + case RRR_DSUBU: + mips32_op = OPC_DSUBU; + check_mips_64(ctx); + break; +#endif + default: + generate_exception(ctx, EXCP_RI); + goto done; + } + + gen_arith(env, ctx, mips32_op, rz, rx, ry); + done: + ; + } + break; + case M16_OPC_RR: + switch (op1) { + case RR_JR: + { + int nd = (ctx->opcode >> 7) & 0x1; + int link = (ctx->opcode >> 6) & 0x1; + int ra = (ctx->opcode >> 5) & 0x1; + + if (link) { + op = nd ? OPC_JALRC : OPC_JALRS; + } else { + op = OPC_JR; + } + + gen_compute_branch(ctx, op, 2, ra ? 31 : rx, 31, 0); + if (!nd) { + *is_branch = 1; + } + } + break; + case RR_SDBBP: + /* XXX: not clear which exception should be raised + * when in debug mode... + */ + check_insn(env, ctx, ISA_MIPS32); + if (!(ctx->hflags & MIPS_HFLAG_DM)) { + generate_exception(ctx, EXCP_DBp); + } else { + generate_exception(ctx, EXCP_DBp); + } + break; + case RR_SLT: + gen_slt(env, OPC_SLT, 24, rx, ry); + break; + case RR_SLTU: + gen_slt(env, OPC_SLTU, 24, rx, ry); + break; + case RR_BREAK: + generate_exception(ctx, EXCP_BREAK); + break; + case RR_SLLV: + gen_shift(env, ctx, OPC_SLLV, ry, rx, ry); + break; + case RR_SRLV: + gen_shift(env, ctx, OPC_SRLV, ry, rx, ry); + break; + case RR_SRAV: + gen_shift(env, ctx, OPC_SRAV, ry, rx, ry); + break; +#if defined (TARGET_MIPS64) + case RR_DSRL: + check_mips_64(ctx); + gen_shift_imm(env, ctx, OPC_DSRL, ry, ry, sa); + break; +#endif + case RR_CMP: + gen_logic(env, OPC_XOR, 24, rx, ry); + break; + case RR_NEG: + gen_arith(env, ctx, OPC_SUBU, rx, 0, ry); + break; + case RR_AND: + gen_logic(env, OPC_AND, rx, rx, ry); + break; + case RR_OR: + gen_logic(env, OPC_OR, rx, rx, ry); + break; + case RR_XOR: + gen_logic(env, OPC_XOR, rx, rx, ry); + break; + case RR_NOT: + gen_logic(env, OPC_NOR, rx, ry, 0); + break; + case RR_MFHI: + gen_HILO(ctx, OPC_MFHI, rx); + break; + case RR_CNVT: + switch (cnvt_op) { + case RR_RY_CNVT_ZEB: + tcg_gen_ext8u_tl(cpu_gpr[rx], cpu_gpr[rx]); + break; + case RR_RY_CNVT_ZEH: + tcg_gen_ext16u_tl(cpu_gpr[rx], cpu_gpr[rx]); + break; + case RR_RY_CNVT_SEB: + tcg_gen_ext8s_tl(cpu_gpr[rx], cpu_gpr[rx]); + break; + case RR_RY_CNVT_SEH: + tcg_gen_ext16s_tl(cpu_gpr[rx], cpu_gpr[rx]); + break; +#if defined (TARGET_MIPS64) + case RR_RY_CNVT_ZEW: + check_mips_64(ctx); + tcg_gen_ext32u_tl(cpu_gpr[rx], cpu_gpr[rx]); + break; + case RR_RY_CNVT_SEW: + check_mips_64(ctx); + tcg_gen_ext32s_tl(cpu_gpr[rx], cpu_gpr[rx]); + break; +#endif + default: + generate_exception(ctx, EXCP_RI); + break; + } + break; + case RR_MFLO: + gen_HILO(ctx, OPC_MFLO, rx); + break; +#if defined (TARGET_MIPS64) + case RR_DSRA: + check_mips_64(ctx); + gen_shift_imm(env, ctx, OPC_DSRA, ry, ry, sa); + break; + case RR_DSLLV: + check_mips_64(ctx); + gen_shift(env, ctx, OPC_DSLLV, ry, rx, ry); + break; + case RR_DSRLV: + check_mips_64(ctx); + gen_shift(env, ctx, OPC_DSRLV, ry, rx, ry); + break; + case RR_DSRAV: + check_mips_64(ctx); + gen_shift(env, ctx, OPC_DSRAV, ry, rx, ry); + break; +#endif + case RR_MULT: + gen_muldiv(ctx, OPC_MULT, rx, ry); + break; + case RR_MULTU: + gen_muldiv(ctx, OPC_MULTU, rx, ry); + break; + case RR_DIV: + gen_muldiv(ctx, OPC_DIV, rx, ry); + break; + case RR_DIVU: + gen_muldiv(ctx, OPC_DIVU, rx, ry); + break; +#if defined (TARGET_MIPS64) + case RR_DMULT: + check_mips_64(ctx); + gen_muldiv(ctx, OPC_DMULT, rx, ry); + break; + case RR_DMULTU: + check_mips_64(ctx); + gen_muldiv(ctx, OPC_DMULTU, rx, ry); + break; + case RR_DDIV: + check_mips_64(ctx); + gen_muldiv(ctx, OPC_DDIV, rx, ry); + break; + case RR_DDIVU: + check_mips_64(ctx); + gen_muldiv(ctx, OPC_DDIVU, rx, ry); + break; +#endif + default: + generate_exception(ctx, EXCP_RI); + break; + } + break; + case M16_OPC_EXTEND: + decode_extended_mips16_opc(env, ctx, is_branch); + n_bytes = 4; + break; +#if defined(TARGET_MIPS64) + case M16_OPC_I64: + funct = (ctx->opcode >> 8) & 0x7; + decode_i64_mips16(env, ctx, ry, funct, offset, 0); + break; +#endif + default: + generate_exception(ctx, EXCP_RI); + break; + } + + return n_bytes; +} + +/* microMIPS extension to MIPS32 */ + +/* microMIPS32 major opcodes */ + +enum { + POOL32A = 0x00, + POOL16A = 0x01, + LBU16 = 0x02, + MOVE16 = 0x03, + ADDI32 = 0x04, + LBU32 = 0x05, + SB32 = 0x06, + LB32 = 0x07, + + POOL32B = 0x08, + POOL16B = 0x09, + LHU16 = 0x0a, + ANDI16 = 0x0b, + ADDIU32 = 0x0c, + LHU32 = 0x0d, + SH32 = 0x0e, + LH32 = 0x0f, + + POOL32I = 0x10, + POOL16C = 0x11, + LWSP16 = 0x12, + POOL16D = 0x13, + ORI32 = 0x14, + POOL32F = 0x15, + POOL32S = 0x16, + DADDIU32 = 0x17, + + POOL32C = 0x18, + LWGP16 = 0x19, + LW16 = 0x1a, + POOL16E = 0x1b, + XORI32 = 0x1c, + JALS32 = 0x1d, + ADDIUPC = 0x1e, + POOL48A = 0x1f, + + /* 0x20 is reserved */ + RES_20 = 0x20, + POOL16F = 0x21, + SB16 = 0x22, + BEQZ16 = 0x23, + SLTI32 = 0x24, + BEQ32 = 0x25, + SWC132 = 0x26, + LWC132 = 0x27, + + /* 0x28 and 0x29 are reserved */ + RES_28 = 0x28, + RES_29 = 0x29, + SH16 = 0x2a, + BNEZ16 = 0x2b, + SLTIU32 = 0x2c, + BNE32 = 0x2d, + SDC132 = 0x2e, + LDC132 = 0x2f, + + /* 0x30 and 0x31 are reserved */ + RES_30 = 0x30, + RES_31 = 0x31, + SWSP16 = 0x32, + B16 = 0x33, + ANDI32 = 0x34, + J32 = 0x35, + SD32 = 0x36, + LD32 = 0x37, + + /* 0x38 and 0x39 are reserved */ + RES_38 = 0x38, + RES_39 = 0x39, + SW16 = 0x3a, + LI16 = 0x3b, + JALX32 = 0x3c, + JAL32 = 0x3d, + SW32 = 0x3e, + LW32 = 0x3f +}; + +/* POOL32A encoding of minor opcode field */ + +enum { + /* These opcodes are distinguished only by bits 9..6; those bits are + * what are recorded below. */ + SLL32 = 0x0, + SRL32 = 0x1, + SRA = 0x2, + ROTR = 0x3, + + SLLV = 0x0, + SRLV = 0x1, + SRAV = 0x2, + ROTRV = 0x3, + ADD = 0x4, + ADDU32 = 0x5, + SUB = 0x6, + SUBU32 = 0x7, + MUL = 0x8, + AND = 0x9, + OR32 = 0xa, + NOR = 0xb, + XOR32 = 0xc, + SLT = 0xd, + SLTU = 0xe, + + MOVN = 0x0, + MOVZ = 0x1, + LWXS = 0x4, + + /* The following can be distinguished by their lower 6 bits. */ + INS = 0x0c, + EXT = 0x2c, + POOL32AXF = 0x3c +}; + +/* POOL32AXF encoding of minor opcode field extension */ + +enum { + /* bits 11..6 */ + TEQ = 0x00, + TGE = 0x08, + TGEU = 0x10, + TLT = 0x20, + TLTU = 0x28, + TNE = 0x30, + + MFC0 = 0x03, + MTC0 = 0x0b, + + /* bits 13..12 for 0x01 */ + MFHI_ACC = 0x0, + MFLO_ACC = 0x1, + MTHI_ACC = 0x2, + MTLO_ACC = 0x3, + + /* bits 13..12 for 0x2a */ + MADD_ACC = 0x0, + MADDU_ACC = 0x1, + MSUB_ACC = 0x2, + MSUBU_ACC = 0x3, + + /* bits 13..12 for 0x32 */ + MULT_ACC = 0x0, + MULTU_ACC = 0x0, + + /* bits 15..12 for 0x2c */ + SEB = 0x2, + SEH = 0x3, + CLO = 0x4, + CLZ = 0x5, + RDHWR = 0x6, + WSBH = 0x7, + MULT = 0x8, + MULTU = 0x9, + DIV = 0xa, + DIVU = 0xb, + MADD = 0xc, + MADDU = 0xd, + MSUB = 0xe, + MSUBU = 0xf, + + /* bits 15..12 for 0x34 */ + MFC2 = 0x4, + MTC2 = 0x5, + MFHC2 = 0x8, + MTHC2 = 0x9, + CFC2 = 0xc, + CTC2 = 0xd, + + /* bits 15..12 for 0x3c */ + JALR = 0x0, + JR = 0x0, /* alias */ + JALR_HB = 0x1, + JALRS = 0x4, + JALRS_HB = 0x5, + + /* bits 15..12 for 0x05 */ + RDPGPR = 0xe, + WRPGPR = 0xf, + + /* bits 15..12 for 0x0d */ + TLBP = 0x0, + TLBR = 0x1, + TLBWI = 0x2, + TLBWR = 0x3, + WAIT = 0x9, + IRET = 0xd, + DERET = 0xe, + ERET = 0xf, + + /* bits 15..12 for 0x15 */ + DMT = 0x0, + DVPE = 0x1, + EMT = 0x2, + EVPE = 0x3, + + /* bits 15..12 for 0x1d */ + DI = 0x4, + EI = 0x5, + + /* bits 15..12 for 0x2d */ + SYNC = 0x6, + SYSCALL = 0x8, + SDBBP = 0xd, + + /* bits 15..12 for 0x35 */ + MFHI32 = 0x0, + MFLO32 = 0x1, + MTHI32 = 0x2, + MTLO32 = 0x3, +}; + +/* POOL32B encoding of minor opcode field (bits 15..12) */ + +enum { + LWC2 = 0x0, + LWP = 0x1, + LDP = 0x4, + LWM32 = 0x5, + CACHE = 0x6, + LDM = 0x7, + SWC2 = 0x8, + SWP = 0x9, + SDP = 0xc, + SWM32 = 0xd, + SDM = 0xf +}; + +/* POOL32C encoding of minor opcode field (bits 15..12) */ + +enum { + LWL = 0x0, + SWL = 0x8, + LWR = 0x1, + SWR = 0x9, + PREF = 0x2, + /* 0xa is reserved */ + LL = 0x3, + SC = 0xb, + LDL = 0x4, + SDL = 0xc, + LDR = 0x5, + SDR = 0xd, + /* 0x6 is reserved */ + LWU = 0xe, + LLD = 0x7, + SCD = 0xf +}; + +/* POOL32F encoding of minor opcode field (bits 5..0) */ + +enum { + /* These are the bit 7..6 values */ + ADD_FMT = 0x0, + MOVN_FMT = 0x0, + + SUB_FMT = 0x1, + MOVZ_FMT = 0x1, + + MUL_FMT = 0x2, + + DIV_FMT = 0x3, + + /* These are the bit 8..6 values */ + RSQRT2_FMT = 0x0, + MOVF_FMT = 0x0, + + LWXC1 = 0x1, + MOVT_FMT = 0x1, + + PLL_PS = 0x2, + SWXC1 = 0x2, + + PLU_PS = 0x3, + LDXC1 = 0x3, + + PUL_PS = 0x4, + SDXC1 = 0x4, + RECIP2_FMT = 0x4, + + PUU_PS = 0x5, + LUXC1 = 0x5, + + CVT_PS_S = 0x6, + SUXC1 = 0x6, + ADDR_PS = 0x6, + PREFX = 0x6, + + MULR_PS = 0x7, + + MADD_S = 0x01, + MADD_D = 0x09, + MADD_PS = 0x11, + ALNV_PS = 0x19, + MSUB_S = 0x21, + MSUB_D = 0x29, + MSUB_PS = 0x31, + + NMADD_S = 0x02, + NMADD_D = 0x0a, + NMADD_PS = 0x12, + NMSUB_S = 0x22, + NMSUB_D = 0x2a, + NMSUB_PS = 0x32, + + POOL32FXF = 0x3b, + + CABS_COND_FMT = 0x1c, /* MIPS3D */ + C_COND_FMT = 0x3c +}; + +/* POOL32Fxf encoding of minor opcode extension field */ + +enum { + CVT_L = 0x04, + RSQRT_FMT = 0x08, + FLOOR_L = 0x0c, + CVT_PW_PS = 0x1c, + CVT_W = 0x24, + SQRT_FMT = 0x28, + FLOOR_W = 0x2c, + CVT_PS_PW = 0x3c, + CFC1 = 0x40, + RECIP_FMT = 0x48, + CEIL_L = 0x4c, + CTC1 = 0x60, + CEIL_W = 0x6c, + MFC1 = 0x80, + CVT_S_PL = 0x84, + TRUNC_L = 0x8c, + MTC1 = 0xa0, + CVT_S_PU = 0xa4, + TRUNC_W = 0xac, + MFHC1 = 0xc0, + ROUND_L = 0xcc, + MTHC1 = 0xe0, + ROUND_W = 0xec, + + MOV_FMT = 0x01, + MOVF = 0x05, + ABS_FMT = 0x0d, + RSQRT1_FMT = 0x1d, + MOVT = 0x25, + NEG_FMT = 0x2d, + CVT_D = 0x4d, + RECIP1_FMT = 0x5d, + CVT_S = 0x6d +}; + +/* POOL32I encoding of minor opcode field (bits 25..21) */ + +enum { + BLTZ = 0x00, + BLTZAL = 0x01, + BGEZ = 0x02, + BGEZAL = 0x03, + BLEZ = 0x04, + BNEZC = 0x05, + BGTZ = 0x06, + BEQZC = 0x07, + TLTI = 0x08, + TGEI = 0x09, + TLTIU = 0x0a, + TGEIU = 0x0b, + TNEI = 0x0c, + LUI = 0x0d, + TEQI = 0x0e, + SYNCI = 0x10, + BLTZALS = 0x11, + BGEZALS = 0x13, + BC2F = 0x14, + BC2T = 0x15, + BPOSGE64 = 0x1a, + BPOSGE32 = 0x1b, + /* These overlap and are distinguished by bit16 of the instruction */ + BC1F = 0x1c, + BC1T = 0x1d, + BC1ANY2F = 0x1c, + BC1ANY2T = 0x1d, + BC1ANY4F = 0x1e, + BC1ANY4T = 0x1f +}; + +/* POOL16A encoding of minor opcode field */ + +enum { + ADDU16 = 0x0, + SUBU16 = 0x1 +}; + +/* POOL16B encoding of minor opcode field */ + +enum { + SLL16 = 0x0, + SRL16 = 0x1 +}; + +/* POOL16C encoding of minor opcode field */ + +enum { + NOT16 = 0x00, + XOR16 = 0x04, + AND16 = 0x08, + OR16 = 0x0c, + LWM16 = 0x10, + SWM16 = 0x14, + JR16 = 0x18, + JRC16 = 0x1a, + JALR16 = 0x1c, + JALR16S = 0x1e, + MFHI16 = 0x20, + MFLO16 = 0x24, + BREAK16 = 0x28, + SDBBP16 = 0x2c, + JRADDIUSP = 0x30 +}; + +/* POOL16D encoding of minor opcode field */ + +enum { + ADDIUS5 = 0x0, + ADDIUSP = 0x1 +}; + +/* POOL16E encoding of minor opcode field */ + +enum { + ADDIUR2 = 0x0, + ADDIUR1SP = 0x1 +}; + +static int mmreg (int r) +{ + static const int map[] = { 16, 17, 2, 3, 4, 5, 6, 7 }; + + return map[r]; +} + +/* Used for 16-bit store instructions. */ +static int mmreg2 (int r) +{ + static const int map[] = { 0, 17, 2, 3, 4, 5, 6, 7 }; + + return map[r]; +} + +#define uMIPS_RD(op) ((op >> 7) & 0x7) +#define uMIPS_RS(op) ((op >> 4) & 0x7) +#define uMIPS_RS2(op) uMIPS_RS(op) +#define uMIPS_RS1(op) ((op >> 1) & 0x7) +#define uMIPS_RD5(op) ((op >> 5) & 0x1f) +#define uMIPS_RS5(op) (op & 0x1f) + +/* Signed immediate */ +#define SIMM(op, start, width) \ + ((int32_t)(((op >> start) & ((~0U) >> (32-width))) \ + << (32-width)) \ + >> (32-width)) +/* Zero-extended immediate */ +#define ZIMM(op, start, width) ((op >> start) & ((~0U) >> (32-width))) + +static void gen_addiur1sp (CPUState *env, DisasContext *ctx) +{ + int rd = mmreg(uMIPS_RD(ctx->opcode)); + + gen_arith_imm(env, ctx, OPC_ADDIU, rd, 29, ((ctx->opcode >> 1) & 0x3f) << 2); +} + +static void gen_addiur2 (CPUState *env, DisasContext *ctx) +{ + static const int decoded_imm[] = { 1, 4, 8, 12, 16, 20, 24, -1 }; + int rd = mmreg(uMIPS_RD(ctx->opcode)); + int rs = mmreg(uMIPS_RS(ctx->opcode)); + + gen_arith_imm(env, ctx, OPC_ADDIU, rd, rs, decoded_imm[ZIMM(ctx->opcode, 1, 3)]); +} + +static void gen_addiusp (CPUState *env, DisasContext *ctx) +{ + int encoded = ZIMM(ctx->opcode, 1, 9); + int decoded; + + if (encoded <= 1) { + decoded = 256 + encoded; + } else if (encoded <= 255) { + decoded = encoded; + } else if (encoded <= 509) { + decoded = encoded - 512; + } else { + decoded = encoded - 768; + } + + gen_arith_imm(env, ctx, OPC_ADDIU, 29, 29, decoded << 2); +} + +static void gen_addius5 (CPUState *env, DisasContext *ctx) +{ + int imm = SIMM(ctx->opcode, 1, 4); + int rd = (ctx->opcode >> 5) & 0x1f; + + gen_arith_imm(env, ctx, OPC_ADDIU, rd, rd, imm); +} + +static void gen_andi16 (CPUState *env, DisasContext *ctx) +{ + static const int decoded_imm[] = { 128, 1, 2, 3, 4, 7, 8, 15, 16, + 31, 32, 63, 64, 255, 32768, 65535 }; + int rd = mmreg(uMIPS_RD(ctx->opcode)); + int rs = mmreg(uMIPS_RS(ctx->opcode)); + int encoded = ZIMM(ctx->opcode, 0, 4); + + gen_logic_imm(env, OPC_ANDI, rd, rs, decoded_imm[encoded]); +} + +static void gen_ldst_multiple (DisasContext *ctx, uint32_t opc, int reglist, + int base, int16_t offset) +{ + TCGv t0, t1; + TCGv_i32 t2; + + if (ctx->hflags & MIPS_HFLAG_BMASK) { + generate_exception(ctx, EXCP_RI); + return; + } + + t0 = tcg_temp_new(); + + gen_base_offset_addr(ctx, t0, base, offset); + + t1 = tcg_const_tl(reglist); + t2 = tcg_const_i32(ctx->mem_idx); + + save_cpu_state(ctx, 1); + switch (opc) { + case LWM32: + gen_helper_lwm(t0, t1, t2); + break; + case SWM32: + gen_helper_swm(t0, t1, t2); + break; +#ifdef TARGET_MIPS64 + case LDM: + gen_helper_ldm(t0, t1, t2); + break; + case SDM: + gen_helper_sdm(t0, t1, t2); + break; +#endif + } + MIPS_DEBUG("%s, %x, %d(%s)", opn, reglist, offset, regnames[base]); + tcg_temp_free(t0); + tcg_temp_free(t1); + tcg_temp_free_i32(t2); +} + + +static void gen_pool16c_insn (CPUState *env, DisasContext *ctx, int *is_branch) +{ + int rd = mmreg((ctx->opcode >> 3) & 0x7); + int rs = mmreg(ctx->opcode & 0x7); + int opc; + + switch (((ctx->opcode) >> 4) & 0x3f) { + case NOT16 + 0: + case NOT16 + 1: + case NOT16 + 2: + case NOT16 + 3: + gen_logic(env, OPC_NOR, rd, rs, 0); + break; + case XOR16 + 0: + case XOR16 + 1: + case XOR16 + 2: + case XOR16 + 3: + gen_logic(env, OPC_XOR, rd, rd, rs); + break; + case AND16 + 0: + case AND16 + 1: + case AND16 + 2: + case AND16 + 3: + gen_logic(env, OPC_AND, rd, rd, rs); + break; + case OR16 + 0: + case OR16 + 1: + case OR16 + 2: + case OR16 + 3: + gen_logic(env, OPC_OR, rd, rd, rs); + break; + case LWM16 + 0: + case LWM16 + 1: + case LWM16 + 2: + case LWM16 + 3: + { + static const int lwm_convert[] = { 0x11, 0x12, 0x13, 0x14 }; + int offset = ZIMM(ctx->opcode, 0, 4); + + gen_ldst_multiple(ctx, LWM32, lwm_convert[(ctx->opcode >> 4) & 0x3], + 29, offset << 2); + } + break; + case SWM16 + 0: + case SWM16 + 1: + case SWM16 + 2: + case SWM16 + 3: + { + static const int swm_convert[] = { 0x11, 0x12, 0x13, 0x14 }; + int offset = ZIMM(ctx->opcode, 0, 4); + + gen_ldst_multiple(ctx, SWM32, swm_convert[(ctx->opcode >> 4) & 0x3], + 29, offset << 2); + } + break; + case JR16 + 0: + case JR16 + 1: + { + int reg = ctx->opcode & 0x1f; + + gen_compute_branch(ctx, OPC_JR, 2, reg, 0, 0); + } + *is_branch = 1; + break; + case JRC16 + 0: + case JRC16 + 1: + { + int reg = ctx->opcode & 0x1f; + + gen_compute_branch(ctx, OPC_JR, 2, reg, 0, 0); + /* Let normal delay slot handling in our caller take us + to the branch target. */ + } + break; + case JALR16 + 0: + case JALR16 + 1: + opc = OPC_JALR; + goto do_jalr; + case JALR16S + 0: + case JALR16S + 1: + opc = OPC_JALRS; + do_jalr: + { + int reg = ctx->opcode & 0x1f; + + gen_compute_branch(ctx, opc, 2, reg, 31, 0); + } + *is_branch = 1; + break; + case MFHI16 + 0: + case MFHI16 + 1: + gen_HILO(ctx, OPC_MFHI, uMIPS_RS5(ctx->opcode)); + break; + case MFLO16 + 0: + case MFLO16 + 1: + gen_HILO(ctx, OPC_MFLO, uMIPS_RS5(ctx->opcode)); + break; + case BREAK16: + generate_exception(ctx, EXCP_BREAK); + break; + case SDBBP16: + /* XXX: not clear which exception should be raised + * when in debug mode... + */ + check_insn(env, ctx, ISA_MIPS32); + if (!(ctx->hflags & MIPS_HFLAG_DM)) { + generate_exception(ctx, EXCP_DBp); + } else { + generate_exception(ctx, EXCP_DBp); + } + break; + case JRADDIUSP + 0: + case JRADDIUSP + 1: + { + int imm = ZIMM(ctx->opcode, 0, 5); + + gen_compute_branch(ctx, OPC_JR, 2, 31, 0, 0); + gen_arith_imm(env, ctx, OPC_ADDIU, 29, 29, imm << 2); + /* Let normal delay slot handling in our caller take us + to the branch target. */ + } + break; + default: + generate_exception(ctx, EXCP_RI); + break; + } +} + +static void gen_ldxs (DisasContext *ctx, int base, int index, int rd) +{ + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + + gen_load_gpr(t0, base); + + if (index != 0) { + gen_load_gpr(t1, index); + tcg_gen_shli_tl(t1, t1, 2); + gen_op_addr_add(ctx, t0, t1, t0); + } + + save_cpu_state(ctx, 0); + op_ld_lw(t1, t0, ctx); + gen_store_gpr(t1, rd); + + tcg_temp_free(t0); + tcg_temp_free(t1); +} + +static void gen_ldst_pair (DisasContext *ctx, uint32_t opc, int rd, + int base, int16_t offset) +{ + const char *opn = "ldst_pair"; + TCGv t0, t1; + + if (ctx->hflags & MIPS_HFLAG_BMASK || rd == 31 || rd == base) { + generate_exception(ctx, EXCP_RI); + return; + } + + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); + + gen_base_offset_addr(ctx, t0, base, offset); + + switch (opc) { + case LWP: + save_cpu_state(ctx, 0); + op_ld_lw(t1, t0, ctx); + gen_store_gpr(t1, rd); + tcg_gen_movi_tl(t1, 4); + gen_op_addr_add(ctx, t0, t0, t1); + op_ld_lw(t1, t0, ctx); + gen_store_gpr(t1, rd+1); + opn = "lwp"; + break; + case SWP: + save_cpu_state(ctx, 0); + gen_load_gpr(t1, rd); + op_st_sw(t1, t0, ctx); + tcg_gen_movi_tl(t1, 4); + gen_op_addr_add(ctx, t0, t0, t1); + gen_load_gpr(t1, rd+1); + op_st_sw(t1, t0, ctx); + opn = "swp"; + break; +#ifdef TARGET_MIPS64 + case LDP: + save_cpu_state(ctx, 0); + op_ld_ld(t1, t0, ctx); + gen_store_gpr(t1, rd); + tcg_gen_movi_tl(t1, 8); + gen_op_addr_add(ctx, t0, t0, t1); + op_ld_ld(t1, t0, ctx); + gen_store_gpr(t1, rd+1); + opn = "ldp"; + break; + case SDP: + save_cpu_state(ctx, 0); + gen_load_gpr(t1, rd); + op_st_sd(t1, t0, ctx); + tcg_gen_movi_tl(t1, 8); + gen_op_addr_add(ctx, t0, t0, t1); + gen_load_gpr(t1, rd+1); + op_st_sd(t1, t0, ctx); + opn = "sdp"; + break; +#endif + } + (void)opn; /* avoid a compiler warning */ + MIPS_DEBUG("%s, %s, %d(%s)", opn, regnames[rd], offset, regnames[base]); + tcg_temp_free(t0); + tcg_temp_free(t1); +} + +static void gen_pool32axf (CPUState *env, DisasContext *ctx, int rt, int rs, + int *is_branch) +{ + int extension = (ctx->opcode >> 6) & 0x3f; + int minor = (ctx->opcode >> 12) & 0xf; + uint32_t mips32_op; + + switch (extension) { + case TEQ: + mips32_op = OPC_TEQ; + goto do_trap; + case TGE: + mips32_op = OPC_TGE; + goto do_trap; + case TGEU: + mips32_op = OPC_TGEU; + goto do_trap; + case TLT: + mips32_op = OPC_TLT; + goto do_trap; + case TLTU: + mips32_op = OPC_TLTU; + goto do_trap; + case TNE: + mips32_op = OPC_TNE; + do_trap: + gen_trap(ctx, mips32_op, rs, rt, -1); + break; +#ifndef CONFIG_USER_ONLY + case MFC0: + case MFC0 + 32: + if (rt == 0) { + /* Treat as NOP. */ + break; + } + gen_mfc0(env, ctx, cpu_gpr[rt], rs, (ctx->opcode >> 11) & 0x7); + break; + case MTC0: + case MTC0 + 32: + { + TCGv t0 = tcg_temp_new(); + + gen_load_gpr(t0, rt); + gen_mtc0(env, ctx, t0, rs, (ctx->opcode >> 11) & 0x7); + tcg_temp_free(t0); + } + break; +#endif + case 0x2c: + switch (minor) { + case SEB: + gen_bshfl(ctx, OPC_SEB, rs, rt); + break; + case SEH: + gen_bshfl(ctx, OPC_SEH, rs, rt); + break; + case CLO: + mips32_op = OPC_CLO; + goto do_cl; + case CLZ: + mips32_op = OPC_CLZ; + do_cl: + check_insn(env, ctx, ISA_MIPS32); + gen_cl(ctx, mips32_op, rt, rs); + break; + case RDHWR: + gen_rdhwr(env, ctx, rt, rs); + break; + case WSBH: + gen_bshfl(ctx, OPC_WSBH, rs, rt); + break; + case MULT: + mips32_op = OPC_MULT; + goto do_muldiv; + case MULTU: + mips32_op = OPC_MULTU; + goto do_muldiv; + case DIV: + mips32_op = OPC_DIV; + goto do_muldiv; + case DIVU: + mips32_op = OPC_DIVU; + goto do_muldiv; + case MADD: + mips32_op = OPC_MADD; + goto do_muldiv; + case MADDU: + mips32_op = OPC_MADDU; + goto do_muldiv; + case MSUB: + mips32_op = OPC_MSUB; + goto do_muldiv; + case MSUBU: + mips32_op = OPC_MSUBU; + do_muldiv: + check_insn(env, ctx, ISA_MIPS32); + gen_muldiv(ctx, mips32_op, rs, rt); + break; + default: + goto pool32axf_invalid; + } + break; + case 0x34: + switch (minor) { + case MFC2: + case MTC2: + case MFHC2: + case MTHC2: + case CFC2: + case CTC2: + generate_exception_err(ctx, EXCP_CpU, 2); + break; + default: + goto pool32axf_invalid; + } + break; + case 0x3c: + switch (minor) { + case JALR: + case JALR_HB: + gen_compute_branch (ctx, OPC_JALR, 4, rs, rt, 0); + *is_branch = 1; + break; + case JALRS: + case JALRS_HB: + gen_compute_branch (ctx, OPC_JALRS, 4, rs, rt, 0); + *is_branch = 1; + break; + default: + goto pool32axf_invalid; + } + break; + case 0x05: + switch (minor) { + case RDPGPR: + check_insn(env, ctx, ISA_MIPS32R2); + gen_load_srsgpr(rt, rs); + break; + case WRPGPR: + check_insn(env, ctx, ISA_MIPS32R2); + gen_store_srsgpr(rt, rs); + break; + default: + goto pool32axf_invalid; + } + break; +#ifndef CONFIG_USER_ONLY + case 0x0d: + switch (minor) { + case TLBP: + mips32_op = OPC_TLBP; + goto do_cp0; + case TLBR: + mips32_op = OPC_TLBR; + goto do_cp0; + case TLBWI: + mips32_op = OPC_TLBWI; + goto do_cp0; + case TLBWR: + mips32_op = OPC_TLBWR; + goto do_cp0; + case WAIT: + mips32_op = OPC_WAIT; + goto do_cp0; + case DERET: + mips32_op = OPC_DERET; + goto do_cp0; + case ERET: + mips32_op = OPC_ERET; + do_cp0: + gen_cp0(env, ctx, mips32_op, rt, rs); + break; + default: + goto pool32axf_invalid; + } + break; + case 0x1d: + switch (minor) { + case DI: + { + TCGv t0 = tcg_temp_new(); + + save_cpu_state(ctx, 1); + gen_helper_di(t0); + gen_store_gpr(t0, rs); + /* Stop translation as we may have switched the execution mode */ + ctx->bstate = BS_STOP; + tcg_temp_free(t0); + } + break; + case EI: + { + TCGv t0 = tcg_temp_new(); + + save_cpu_state(ctx, 1); + gen_helper_ei(t0); + gen_store_gpr(t0, rs); + /* Stop translation as we may have switched the execution mode */ + ctx->bstate = BS_STOP; + tcg_temp_free(t0); + } + break; + default: + goto pool32axf_invalid; + } + break; +#endif + case 0x2d: + switch (minor) { + case SYNC: + /* NOP */ + break; + case SYSCALL: + generate_exception(ctx, EXCP_SYSCALL); + ctx->bstate = BS_STOP; + break; + case SDBBP: + check_insn(env, ctx, ISA_MIPS32); + if (!(ctx->hflags & MIPS_HFLAG_DM)) { + generate_exception(ctx, EXCP_DBp); + } else { + generate_exception(ctx, EXCP_DBp); + } + break; + default: + goto pool32axf_invalid; + } + break; + case 0x35: + switch (minor) { + case MFHI32: + gen_HILO(ctx, OPC_MFHI, rs); + break; + case MFLO32: + gen_HILO(ctx, OPC_MFLO, rs); + break; + case MTHI32: + gen_HILO(ctx, OPC_MTHI, rs); + break; + case MTLO32: + gen_HILO(ctx, OPC_MTLO, rs); + break; + default: + goto pool32axf_invalid; + } + break; + default: + pool32axf_invalid: + MIPS_INVAL("pool32axf"); + generate_exception(ctx, EXCP_RI); + break; + } +} + +/* Values for microMIPS fmt field. Variable-width, depending on which + formats the instruction supports. */ + +enum { + FMT_SD_S = 0, + FMT_SD_D = 1, + + FMT_SDPS_S = 0, + FMT_SDPS_D = 1, + FMT_SDPS_PS = 2, + + FMT_SWL_S = 0, + FMT_SWL_W = 1, + FMT_SWL_L = 2, + + FMT_DWL_D = 0, + FMT_DWL_W = 1, + FMT_DWL_L = 2 +}; + +static void gen_pool32fxf (CPUState *env, DisasContext *ctx, int rt, int rs) +{ + int extension = (ctx->opcode >> 6) & 0x3ff; + uint32_t mips32_op; + +#define FLOAT_1BIT_FMT(opc, fmt) (fmt << 8) | opc +#define FLOAT_2BIT_FMT(opc, fmt) (fmt << 7) | opc +#define COND_FLOAT_MOV(opc, cond) (cond << 7) | opc + + switch (extension) { + case FLOAT_1BIT_FMT(CFC1, 0): + mips32_op = OPC_CFC1; + goto do_cp1; + case FLOAT_1BIT_FMT(CTC1, 0): + mips32_op = OPC_CTC1; + goto do_cp1; + case FLOAT_1BIT_FMT(MFC1, 0): + mips32_op = OPC_MFC1; + goto do_cp1; + case FLOAT_1BIT_FMT(MTC1, 0): + mips32_op = OPC_MTC1; + goto do_cp1; + case FLOAT_1BIT_FMT(MFHC1, 0): + mips32_op = OPC_MFHC1; + goto do_cp1; + case FLOAT_1BIT_FMT(MTHC1, 0): + mips32_op = OPC_MTHC1; + do_cp1: + gen_cp1(ctx, mips32_op, rt, rs); + break; + + /* Reciprocal square root */ + case FLOAT_1BIT_FMT(RSQRT_FMT, FMT_SD_S): + mips32_op = OPC_RSQRT_S; + goto do_unaryfp; + case FLOAT_1BIT_FMT(RSQRT_FMT, FMT_SD_D): + mips32_op = OPC_RSQRT_D; + goto do_unaryfp; + + /* Square root */ + case FLOAT_1BIT_FMT(SQRT_FMT, FMT_SD_S): + mips32_op = OPC_SQRT_S; + goto do_unaryfp; + case FLOAT_1BIT_FMT(SQRT_FMT, FMT_SD_D): + mips32_op = OPC_SQRT_D; + goto do_unaryfp; + + /* Reciprocal */ + case FLOAT_1BIT_FMT(RECIP_FMT, FMT_SD_S): + mips32_op = OPC_RECIP_S; + goto do_unaryfp; + case FLOAT_1BIT_FMT(RECIP_FMT, FMT_SD_D): + mips32_op = OPC_RECIP_D; + goto do_unaryfp; + + /* Floor */ + case FLOAT_1BIT_FMT(FLOOR_L, FMT_SD_S): + mips32_op = OPC_FLOOR_L_S; + goto do_unaryfp; + case FLOAT_1BIT_FMT(FLOOR_L, FMT_SD_D): + mips32_op = OPC_FLOOR_L_D; + goto do_unaryfp; + case FLOAT_1BIT_FMT(FLOOR_W, FMT_SD_S): + mips32_op = OPC_FLOOR_W_S; + goto do_unaryfp; + case FLOAT_1BIT_FMT(FLOOR_W, FMT_SD_D): + mips32_op = OPC_FLOOR_W_D; + goto do_unaryfp; + + /* Ceiling */ + case FLOAT_1BIT_FMT(CEIL_L, FMT_SD_S): + mips32_op = OPC_CEIL_L_S; + goto do_unaryfp; + case FLOAT_1BIT_FMT(CEIL_L, FMT_SD_D): + mips32_op = OPC_CEIL_L_D; + goto do_unaryfp; + case FLOAT_1BIT_FMT(CEIL_W, FMT_SD_S): + mips32_op = OPC_CEIL_W_S; + goto do_unaryfp; + case FLOAT_1BIT_FMT(CEIL_W, FMT_SD_D): + mips32_op = OPC_CEIL_W_D; + goto do_unaryfp; + + /* Truncation */ + case FLOAT_1BIT_FMT(TRUNC_L, FMT_SD_S): + mips32_op = OPC_TRUNC_L_S; + goto do_unaryfp; + case FLOAT_1BIT_FMT(TRUNC_L, FMT_SD_D): + mips32_op = OPC_TRUNC_L_D; + goto do_unaryfp; + case FLOAT_1BIT_FMT(TRUNC_W, FMT_SD_S): + mips32_op = OPC_TRUNC_W_S; + goto do_unaryfp; + case FLOAT_1BIT_FMT(TRUNC_W, FMT_SD_D): + mips32_op = OPC_TRUNC_W_D; + goto do_unaryfp; + + /* Round */ + case FLOAT_1BIT_FMT(ROUND_L, FMT_SD_S): + mips32_op = OPC_ROUND_L_S; + goto do_unaryfp; + case FLOAT_1BIT_FMT(ROUND_L, FMT_SD_D): + mips32_op = OPC_ROUND_L_D; + goto do_unaryfp; + case FLOAT_1BIT_FMT(ROUND_W, FMT_SD_S): + mips32_op = OPC_ROUND_W_S; + goto do_unaryfp; + case FLOAT_1BIT_FMT(ROUND_W, FMT_SD_D): + mips32_op = OPC_ROUND_W_D; + goto do_unaryfp; + + /* Integer to floating-point conversion */ + case FLOAT_1BIT_FMT(CVT_L, FMT_SD_S): + mips32_op = OPC_CVT_L_S; + goto do_unaryfp; + case FLOAT_1BIT_FMT(CVT_L, FMT_SD_D): + mips32_op = OPC_CVT_L_D; + goto do_unaryfp; + case FLOAT_1BIT_FMT(CVT_W, FMT_SD_S): + mips32_op = OPC_CVT_W_S; + goto do_unaryfp; + case FLOAT_1BIT_FMT(CVT_W, FMT_SD_D): + mips32_op = OPC_CVT_W_D; + goto do_unaryfp; + + /* Paired-foo conversions */ + case FLOAT_1BIT_FMT(CVT_S_PL, 0): + mips32_op = OPC_CVT_S_PL; + goto do_unaryfp; + case FLOAT_1BIT_FMT(CVT_S_PU, 0): + mips32_op = OPC_CVT_S_PU; + goto do_unaryfp; + case FLOAT_1BIT_FMT(CVT_PW_PS, 0): + mips32_op = OPC_CVT_PW_PS; + goto do_unaryfp; + case FLOAT_1BIT_FMT(CVT_PS_PW, 0): + mips32_op = OPC_CVT_PS_PW; + goto do_unaryfp; + + /* Floating-point moves */ + case FLOAT_2BIT_FMT(MOV_FMT, FMT_SDPS_S): + mips32_op = OPC_MOV_S; + goto do_unaryfp; + case FLOAT_2BIT_FMT(MOV_FMT, FMT_SDPS_D): + mips32_op = OPC_MOV_D; + goto do_unaryfp; + case FLOAT_2BIT_FMT(MOV_FMT, FMT_SDPS_PS): + mips32_op = OPC_MOV_PS; + goto do_unaryfp; + + /* Absolute value */ + case FLOAT_2BIT_FMT(ABS_FMT, FMT_SDPS_S): + mips32_op = OPC_ABS_S; + goto do_unaryfp; + case FLOAT_2BIT_FMT(ABS_FMT, FMT_SDPS_D): + mips32_op = OPC_ABS_D; + goto do_unaryfp; + case FLOAT_2BIT_FMT(ABS_FMT, FMT_SDPS_PS): + mips32_op = OPC_ABS_PS; + goto do_unaryfp; + + /* Negation */ + case FLOAT_2BIT_FMT(NEG_FMT, FMT_SDPS_S): + mips32_op = OPC_NEG_S; + goto do_unaryfp; + case FLOAT_2BIT_FMT(NEG_FMT, FMT_SDPS_D): + mips32_op = OPC_NEG_D; + goto do_unaryfp; + case FLOAT_2BIT_FMT(NEG_FMT, FMT_SDPS_PS): + mips32_op = OPC_NEG_PS; + goto do_unaryfp; + + /* Reciprocal square root step */ + case FLOAT_2BIT_FMT(RSQRT1_FMT, FMT_SDPS_S): + mips32_op = OPC_RSQRT1_S; + goto do_unaryfp; + case FLOAT_2BIT_FMT(RSQRT1_FMT, FMT_SDPS_D): + mips32_op = OPC_RSQRT1_D; + goto do_unaryfp; + case FLOAT_2BIT_FMT(RSQRT1_FMT, FMT_SDPS_PS): + mips32_op = OPC_RSQRT1_PS; + goto do_unaryfp; + + /* Reciprocal step */ + case FLOAT_2BIT_FMT(RECIP1_FMT, FMT_SDPS_S): + mips32_op = OPC_RECIP1_S; + goto do_unaryfp; + case FLOAT_2BIT_FMT(RECIP1_FMT, FMT_SDPS_D): + mips32_op = OPC_RECIP1_S; + goto do_unaryfp; + case FLOAT_2BIT_FMT(RECIP1_FMT, FMT_SDPS_PS): + mips32_op = OPC_RECIP1_PS; + goto do_unaryfp; + + /* Conversions from double */ + case FLOAT_2BIT_FMT(CVT_D, FMT_SWL_S): + mips32_op = OPC_CVT_D_S; + goto do_unaryfp; + case FLOAT_2BIT_FMT(CVT_D, FMT_SWL_W): + mips32_op = OPC_CVT_D_W; + goto do_unaryfp; + case FLOAT_2BIT_FMT(CVT_D, FMT_SWL_L): + mips32_op = OPC_CVT_D_L; + goto do_unaryfp; + + /* Conversions from single */ + case FLOAT_2BIT_FMT(CVT_S, FMT_DWL_D): + mips32_op = OPC_CVT_S_D; + goto do_unaryfp; + case FLOAT_2BIT_FMT(CVT_S, FMT_DWL_W): + mips32_op = OPC_CVT_S_W; + goto do_unaryfp; + case FLOAT_2BIT_FMT(CVT_S, FMT_DWL_L): + mips32_op = OPC_CVT_S_L; + do_unaryfp: + gen_farith(ctx, mips32_op, -1, rs, rt, 0); + break; + + /* Conditional moves on floating-point codes */ + case COND_FLOAT_MOV(MOVT, 0): + case COND_FLOAT_MOV(MOVT, 1): + case COND_FLOAT_MOV(MOVT, 2): + case COND_FLOAT_MOV(MOVT, 3): + case COND_FLOAT_MOV(MOVT, 4): + case COND_FLOAT_MOV(MOVT, 5): + case COND_FLOAT_MOV(MOVT, 6): + case COND_FLOAT_MOV(MOVT, 7): + gen_movci(ctx, rt, rs, (ctx->opcode >> 13) & 0x7, 1); + break; + case COND_FLOAT_MOV(MOVF, 0): + case COND_FLOAT_MOV(MOVF, 1): + case COND_FLOAT_MOV(MOVF, 2): + case COND_FLOAT_MOV(MOVF, 3): + case COND_FLOAT_MOV(MOVF, 4): + case COND_FLOAT_MOV(MOVF, 5): + case COND_FLOAT_MOV(MOVF, 6): + case COND_FLOAT_MOV(MOVF, 7): + gen_movci(ctx, rt, rs, (ctx->opcode >> 13) & 0x7, 0); + break; + default: + MIPS_INVAL("pool32fxf"); + generate_exception(ctx, EXCP_RI); + break; + } +} + +static void decode_micromips32_opc (CPUState *env, DisasContext *ctx, + uint16_t insn_hw1, int *is_branch) +{ + int32_t offset; + uint16_t insn; + int rt, rs, rd, rr; + int16_t imm; + uint32_t op, minor, mips32_op; + uint32_t cond, fmt, cc; + + insn = lduw_code(ctx->pc + 2); + ctx->opcode = (ctx->opcode << 16) | insn; + + rt = (ctx->opcode >> 21) & 0x1f; + rs = (ctx->opcode >> 16) & 0x1f; + rd = (ctx->opcode >> 11) & 0x1f; + rr = (ctx->opcode >> 6) & 0x1f; + imm = (int16_t) ctx->opcode; + + op = (ctx->opcode >> 26) & 0x3f; + switch (op) { + case POOL32A: + minor = ctx->opcode & 0x3f; + switch (minor) { + case 0x00: + minor = (ctx->opcode >> 6) & 0xf; + switch (minor) { + case SLL32: + mips32_op = OPC_SLL; + goto do_shifti; + case SRA: + mips32_op = OPC_SRA; + goto do_shifti; + case SRL32: + mips32_op = OPC_SRL; + goto do_shifti; + case ROTR: + mips32_op = OPC_ROTR; + do_shifti: + gen_shift_imm(env, ctx, mips32_op, rt, rs, rd); + break; + default: + goto pool32a_invalid; + } + break; + case 0x10: + minor = (ctx->opcode >> 6) & 0xf; + switch (minor) { + /* Arithmetic */ + case ADD: + mips32_op = OPC_ADD; + goto do_arith; + case ADDU32: + mips32_op = OPC_ADDU; + goto do_arith; + case SUB: + mips32_op = OPC_SUB; + goto do_arith; + case SUBU32: + mips32_op = OPC_SUBU; + goto do_arith; + case MUL: + mips32_op = OPC_MUL; + do_arith: + gen_arith(env, ctx, mips32_op, rd, rs, rt); + break; + /* Shifts */ + case SLLV: + mips32_op = OPC_SLLV; + goto do_shift; + case SRLV: + mips32_op = OPC_SRLV; + goto do_shift; + case SRAV: + mips32_op = OPC_SRAV; + goto do_shift; + case ROTRV: + mips32_op = OPC_ROTRV; + do_shift: + gen_shift(env, ctx, mips32_op, rd, rs, rt); + break; + /* Logical operations */ + case AND: + mips32_op = OPC_AND; + goto do_logic; + case OR32: + mips32_op = OPC_OR; + goto do_logic; + case NOR: + mips32_op = OPC_NOR; + goto do_logic; + case XOR32: + mips32_op = OPC_XOR; + do_logic: + gen_logic(env, mips32_op, rd, rs, rt); + break; + /* Set less than */ + case SLT: + mips32_op = OPC_SLT; + goto do_slt; + case SLTU: + mips32_op = OPC_SLTU; + do_slt: + gen_slt(env, mips32_op, rd, rs, rt); + break; + default: + goto pool32a_invalid; + } + break; + case 0x18: + minor = (ctx->opcode >> 6) & 0xf; + switch (minor) { + /* Conditional moves */ + case MOVN: + mips32_op = OPC_MOVN; + goto do_cmov; + case MOVZ: + mips32_op = OPC_MOVZ; + do_cmov: + gen_cond_move(env, mips32_op, rd, rs, rt); + break; + case LWXS: + gen_ldxs(ctx, rs, rt, rd); + break; + default: + goto pool32a_invalid; + } + break; + case INS: + gen_bitops(ctx, OPC_INS, rt, rs, rr, rd); + return; + case EXT: + gen_bitops(ctx, OPC_EXT, rt, rs, rr, rd); + return; + case POOL32AXF: + gen_pool32axf(env, ctx, rt, rs, is_branch); + break; + case 0x07: + generate_exception(ctx, EXCP_BREAK); + break; + default: + pool32a_invalid: + MIPS_INVAL("pool32a"); + generate_exception(ctx, EXCP_RI); + break; + } + break; + case POOL32B: + minor = (ctx->opcode >> 12) & 0xf; + switch (minor) { + case CACHE: + /* Treat as no-op. */ + break; + case LWC2: + case SWC2: + /* COP2: Not implemented. */ + generate_exception_err(ctx, EXCP_CpU, 2); + break; + case LWP: + case SWP: +#ifdef TARGET_MIPS64 + case LDP: + case SDP: +#endif + gen_ldst_pair(ctx, minor, rt, rs, SIMM(ctx->opcode, 0, 12)); + break; + case LWM32: + case SWM32: +#ifdef TARGET_MIPS64 + case LDM: + case SDM: +#endif + gen_ldst_multiple(ctx, minor, rt, rs, SIMM(ctx->opcode, 0, 12)); + break; + default: + MIPS_INVAL("pool32b"); + generate_exception(ctx, EXCP_RI); + break; + } + break; + case POOL32F: + if (env->CP0_Config1 & (1 << CP0C1_FP)) { + minor = ctx->opcode & 0x3f; + check_cp1_enabled(ctx); + switch (minor) { + case ALNV_PS: + mips32_op = OPC_ALNV_PS; + goto do_madd; + case MADD_S: + mips32_op = OPC_MADD_S; + goto do_madd; + case MADD_D: + mips32_op = OPC_MADD_D; + goto do_madd; + case MADD_PS: + mips32_op = OPC_MADD_PS; + goto do_madd; + case MSUB_S: + mips32_op = OPC_MSUB_S; + goto do_madd; + case MSUB_D: + mips32_op = OPC_MSUB_D; + goto do_madd; + case MSUB_PS: + mips32_op = OPC_MSUB_PS; + goto do_madd; + case NMADD_S: + mips32_op = OPC_NMADD_S; + goto do_madd; + case NMADD_D: + mips32_op = OPC_NMADD_D; + goto do_madd; + case NMADD_PS: + mips32_op = OPC_NMADD_PS; + goto do_madd; + case NMSUB_S: + mips32_op = OPC_NMSUB_S; + goto do_madd; + case NMSUB_D: + mips32_op = OPC_NMSUB_D; + goto do_madd; + case NMSUB_PS: + mips32_op = OPC_NMSUB_PS; + do_madd: + gen_flt3_arith(ctx, mips32_op, rd, rr, rs, rt); + break; + case CABS_COND_FMT: + cond = (ctx->opcode >> 6) & 0xf; + cc = (ctx->opcode >> 13) & 0x7; + fmt = (ctx->opcode >> 10) & 0x3; + switch (fmt) { + case 0x0: + gen_cmpabs_s(ctx, cond, rt, rs, cc); + break; + case 0x1: + gen_cmpabs_d(ctx, cond, rt, rs, cc); + break; + case 0x2: + gen_cmpabs_ps(ctx, cond, rt, rs, cc); + break; + default: + goto pool32f_invalid; + } + break; + case C_COND_FMT: + cond = (ctx->opcode >> 6) & 0xf; + cc = (ctx->opcode >> 13) & 0x7; + fmt = (ctx->opcode >> 10) & 0x3; + switch (fmt) { + case 0x0: + gen_cmp_s(ctx, cond, rt, rs, cc); + break; + case 0x1: + gen_cmp_d(ctx, cond, rt, rs, cc); + break; + case 0x2: + gen_cmp_ps(ctx, cond, rt, rs, cc); + break; + default: + goto pool32f_invalid; + } + break; + case POOL32FXF: + gen_pool32fxf(env, ctx, rt, rs); + break; + case 0x00: + /* PLL foo */ + switch ((ctx->opcode >> 6) & 0x7) { + case PLL_PS: + mips32_op = OPC_PLL_PS; + goto do_ps; + case PLU_PS: + mips32_op = OPC_PLU_PS; + goto do_ps; + case PUL_PS: + mips32_op = OPC_PUL_PS; + goto do_ps; + case PUU_PS: + mips32_op = OPC_PUU_PS; + goto do_ps; + case CVT_PS_S: + mips32_op = OPC_CVT_PS_S; + do_ps: + gen_farith(ctx, mips32_op, rt, rs, rd, 0); + break; + default: + goto pool32f_invalid; + } + break; + case 0x08: + /* [LS][WDU]XC1 */ + switch ((ctx->opcode >> 6) & 0x7) { + case LWXC1: + mips32_op = OPC_LWXC1; + goto do_ldst_cp1; + case SWXC1: + mips32_op = OPC_SWXC1; + goto do_ldst_cp1; + case LDXC1: + mips32_op = OPC_LDXC1; + goto do_ldst_cp1; + case SDXC1: + mips32_op = OPC_SDXC1; + goto do_ldst_cp1; + case LUXC1: + mips32_op = OPC_LUXC1; + goto do_ldst_cp1; + case SUXC1: + mips32_op = OPC_SUXC1; + do_ldst_cp1: + gen_flt3_ldst(ctx, mips32_op, rd, rd, rt, rs); + break; + default: + goto pool32f_invalid; + } + break; + case 0x18: + /* 3D insns */ + fmt = (ctx->opcode >> 9) & 0x3; + switch ((ctx->opcode >> 6) & 0x7) { + case RSQRT2_FMT: + switch (fmt) { + case FMT_SDPS_S: + mips32_op = OPC_RSQRT2_S; + goto do_3d; + case FMT_SDPS_D: + mips32_op = OPC_RSQRT2_D; + goto do_3d; + case FMT_SDPS_PS: + mips32_op = OPC_RSQRT2_PS; + goto do_3d; + default: + goto pool32f_invalid; + } + break; + case RECIP2_FMT: + switch (fmt) { + case FMT_SDPS_S: + mips32_op = OPC_RECIP2_S; + goto do_3d; + case FMT_SDPS_D: + mips32_op = OPC_RECIP2_D; + goto do_3d; + case FMT_SDPS_PS: + mips32_op = OPC_RECIP2_PS; + goto do_3d; + default: + goto pool32f_invalid; + } + break; + case ADDR_PS: + mips32_op = OPC_ADDR_PS; + goto do_3d; + case MULR_PS: + mips32_op = OPC_MULR_PS; + do_3d: + gen_farith(ctx, mips32_op, rt, rs, rd, 0); + break; + default: + goto pool32f_invalid; + } + break; + case 0x20: + /* MOV[FT].fmt and PREFX */ + cc = (ctx->opcode >> 13) & 0x7; + fmt = (ctx->opcode >> 9) & 0x3; + switch ((ctx->opcode >> 6) & 0x7) { + case MOVF_FMT: + switch (fmt) { + case FMT_SDPS_S: + gen_movcf_s(rs, rt, cc, 0); + break; + case FMT_SDPS_D: + gen_movcf_d(ctx, rs, rt, cc, 0); + break; + case FMT_SDPS_PS: + gen_movcf_ps(rs, rt, cc, 0); + break; + default: + goto pool32f_invalid; + } + break; + case MOVT_FMT: + switch (fmt) { + case FMT_SDPS_S: + gen_movcf_s(rs, rt, cc, 1); + break; + case FMT_SDPS_D: + gen_movcf_d(ctx, rs, rt, cc, 1); + break; + case FMT_SDPS_PS: + gen_movcf_ps(rs, rt, cc, 1); + break; + default: + goto pool32f_invalid; + } + break; + case PREFX: + break; + default: + goto pool32f_invalid; + } + break; +#define FINSN_3ARG_SDPS(prfx) \ + switch ((ctx->opcode >> 8) & 0x3) { \ + case FMT_SDPS_S: \ + mips32_op = OPC_##prfx##_S; \ + goto do_fpop; \ + case FMT_SDPS_D: \ + mips32_op = OPC_##prfx##_D; \ + goto do_fpop; \ + case FMT_SDPS_PS: \ + mips32_op = OPC_##prfx##_PS; \ + goto do_fpop; \ + default: \ + goto pool32f_invalid; \ + } + case 0x30: + /* regular FP ops */ + switch ((ctx->opcode >> 6) & 0x3) { + case ADD_FMT: + FINSN_3ARG_SDPS(ADD); + break; + case SUB_FMT: + FINSN_3ARG_SDPS(SUB); + break; + case MUL_FMT: + FINSN_3ARG_SDPS(MUL); + break; + case DIV_FMT: + fmt = (ctx->opcode >> 8) & 0x3; + if (fmt == 1) { + mips32_op = OPC_DIV_D; + } else if (fmt == 0) { + mips32_op = OPC_DIV_S; + } else { + goto pool32f_invalid; + } + goto do_fpop; + default: + goto pool32f_invalid; + } + break; + case 0x38: + /* cmovs */ + switch ((ctx->opcode >> 6) & 0x3) { + case MOVN_FMT: + FINSN_3ARG_SDPS(MOVN); + break; + case MOVZ_FMT: + FINSN_3ARG_SDPS(MOVZ); + break; + default: + goto pool32f_invalid; + } + break; + do_fpop: + gen_farith(ctx, mips32_op, rt, rs, rd, 0); + break; + default: + pool32f_invalid: + MIPS_INVAL("pool32f"); + generate_exception(ctx, EXCP_RI); + break; + } + } else { + generate_exception_err(ctx, EXCP_CpU, 1); + } + break; + case POOL32I: + minor = (ctx->opcode >> 21) & 0x1f; + switch (minor) { + case BLTZ: + mips32_op = OPC_BLTZ; + goto do_branch; + case BLTZAL: + mips32_op = OPC_BLTZAL; + goto do_branch; + case BLTZALS: + mips32_op = OPC_BLTZALS; + goto do_branch; + case BGEZ: + mips32_op = OPC_BGEZ; + goto do_branch; + case BGEZAL: + mips32_op = OPC_BGEZAL; + goto do_branch; + case BGEZALS: + mips32_op = OPC_BGEZALS; + goto do_branch; + case BLEZ: + mips32_op = OPC_BLEZ; + goto do_branch; + case BGTZ: + mips32_op = OPC_BGTZ; + do_branch: + gen_compute_branch(ctx, mips32_op, 4, rs, -1, imm << 1); + *is_branch = 1; + break; + + /* Traps */ + case TLTI: + mips32_op = OPC_TLTI; + goto do_trapi; + case TGEI: + mips32_op = OPC_TGEI; + goto do_trapi; + case TLTIU: + mips32_op = OPC_TLTIU; + goto do_trapi; + case TGEIU: + mips32_op = OPC_TGEIU; + goto do_trapi; + case TNEI: + mips32_op = OPC_TNEI; + goto do_trapi; + case TEQI: + mips32_op = OPC_TEQI; + do_trapi: + gen_trap(ctx, mips32_op, rs, -1, imm); + break; + + case BNEZC: + case BEQZC: + gen_compute_branch(ctx, minor == BNEZC ? OPC_BNE : OPC_BEQ, + 4, rs, 0, imm << 1); + /* Compact branches don't have a delay slot, so just let + the normal delay slot handling take us to the branch + target. */ + break; + case LUI: + gen_logic_imm(env, OPC_LUI, rs, -1, imm); + break; + case SYNCI: + break; + case BC2F: + case BC2T: + /* COP2: Not implemented. */ + generate_exception_err(ctx, EXCP_CpU, 2); + break; + case BC1F: + mips32_op = (ctx->opcode & (1 << 16)) ? OPC_BC1FANY2 : OPC_BC1F; + goto do_cp1branch; + case BC1T: + mips32_op = (ctx->opcode & (1 << 16)) ? OPC_BC1TANY2 : OPC_BC1T; + goto do_cp1branch; + case BC1ANY4F: + mips32_op = OPC_BC1FANY4; + goto do_cp1mips3d; + case BC1ANY4T: + mips32_op = OPC_BC1TANY4; + do_cp1mips3d: + check_cop1x(ctx); + check_insn(env, ctx, ASE_MIPS3D); + /* Fall through */ + do_cp1branch: + gen_compute_branch1(env, ctx, mips32_op, + (ctx->opcode >> 18) & 0x7, imm << 1); + *is_branch = 1; + break; + case BPOSGE64: + case BPOSGE32: + /* MIPS DSP: not implemented */ + /* Fall through */ + default: + MIPS_INVAL("pool32i"); + generate_exception(ctx, EXCP_RI); + break; + } + break; + case POOL32C: + minor = (ctx->opcode >> 12) & 0xf; + switch (minor) { + case LWL: + mips32_op = OPC_LWL; + goto do_ld_lr; + case SWL: + mips32_op = OPC_SWL; + goto do_st_lr; + case LWR: + mips32_op = OPC_LWR; + goto do_ld_lr; + case SWR: + mips32_op = OPC_SWR; + goto do_st_lr; +#if defined(TARGET_MIPS64) + case LDL: + mips32_op = OPC_LDL; + goto do_ld_lr; + case SDL: + mips32_op = OPC_SDL; + goto do_st_lr; + case LDR: + mips32_op = OPC_LDR; + goto do_ld_lr; + case SDR: + mips32_op = OPC_SDR; + goto do_st_lr; + case LWU: + mips32_op = OPC_LWU; + goto do_ld_lr; + case LLD: + mips32_op = OPC_LLD; + goto do_ld_lr; +#endif + case LL: + mips32_op = OPC_LL; + goto do_ld_lr; + do_ld_lr: + gen_ld(env, ctx, mips32_op, rt, rs, SIMM(ctx->opcode, 0, 12)); + break; + do_st_lr: + gen_st(ctx, mips32_op, rt, rs, SIMM(ctx->opcode, 0, 12)); + break; + case SC: + gen_st_cond(ctx, OPC_SC, rt, rs, SIMM(ctx->opcode, 0, 12)); + break; +#if defined(TARGET_MIPS64) + case SCD: + gen_st_cond(ctx, OPC_SCD, rt, rs, SIMM(ctx->opcode, 0, 12)); + break; +#endif + case PREF: + /* Treat as no-op */ + break; + default: + MIPS_INVAL("pool32c"); + generate_exception(ctx, EXCP_RI); + break; + } + break; + case ADDI32: + mips32_op = OPC_ADDI; + goto do_addi; + case ADDIU32: + mips32_op = OPC_ADDIU; + do_addi: + gen_arith_imm(env, ctx, mips32_op, rt, rs, imm); + break; + + /* Logical operations */ + case ORI32: + mips32_op = OPC_ORI; + goto do_logici; + case XORI32: + mips32_op = OPC_XORI; + goto do_logici; + case ANDI32: + mips32_op = OPC_ANDI; + do_logici: + gen_logic_imm(env, mips32_op, rt, rs, imm); + break; + + /* Set less than immediate */ + case SLTI32: + mips32_op = OPC_SLTI; + goto do_slti; + case SLTIU32: + mips32_op = OPC_SLTIU; + do_slti: + gen_slt_imm(env, mips32_op, rt, rs, imm); + break; + case JALX32: + offset = (int32_t)(ctx->opcode & 0x3FFFFFF) << 2; + gen_compute_branch(ctx, OPC_JALX, 4, rt, rs, offset); + *is_branch = 1; + break; + case JALS32: + offset = (int32_t)(ctx->opcode & 0x3FFFFFF) << 1; + gen_compute_branch(ctx, OPC_JALS, 4, rt, rs, offset); + *is_branch = 1; + break; + case BEQ32: + gen_compute_branch(ctx, OPC_BEQ, 4, rt, rs, imm << 1); + *is_branch = 1; + break; + case BNE32: + gen_compute_branch(ctx, OPC_BNE, 4, rt, rs, imm << 1); + *is_branch = 1; + break; + case J32: + gen_compute_branch(ctx, OPC_J, 4, rt, rs, + (int32_t)(ctx->opcode & 0x3FFFFFF) << 1); + *is_branch = 1; + break; + case JAL32: + gen_compute_branch(ctx, OPC_JAL, 4, rt, rs, + (int32_t)(ctx->opcode & 0x3FFFFFF) << 1); + *is_branch = 1; + break; + /* Floating point (COP1) */ + case LWC132: + mips32_op = OPC_LWC1; + goto do_cop1; + case LDC132: + mips32_op = OPC_LDC1; + goto do_cop1; + case SWC132: + mips32_op = OPC_SWC1; + goto do_cop1; + case SDC132: + mips32_op = OPC_SDC1; + do_cop1: + gen_cop1_ldst(env, ctx, mips32_op, rt, rs, imm); + break; + case ADDIUPC: + { + int reg = mmreg(ZIMM(ctx->opcode, 23, 3)); + int offset = SIMM(ctx->opcode, 0, 23) << 2; + + gen_addiupc(ctx, reg, offset, 0, 0); + } + break; + /* Loads and stores */ + case LB32: + mips32_op = OPC_LB; + goto do_ld; + case LBU32: + mips32_op = OPC_LBU; + goto do_ld; + case LH32: + mips32_op = OPC_LH; + goto do_ld; + case LHU32: + mips32_op = OPC_LHU; + goto do_ld; + case LW32: + mips32_op = OPC_LW; + goto do_ld; +#ifdef TARGET_MIPS64 + case LD32: + mips32_op = OPC_LD; + goto do_ld; + case SD32: + mips32_op = OPC_SD; + goto do_st; +#endif + case SB32: + mips32_op = OPC_SB; + goto do_st; + case SH32: + mips32_op = OPC_SH; + goto do_st; + case SW32: + mips32_op = OPC_SW; + goto do_st; + do_ld: + gen_ld(env, ctx, mips32_op, rt, rs, imm); + break; + do_st: + gen_st(ctx, mips32_op, rt, rs, imm); + break; + default: + generate_exception(ctx, EXCP_RI); + break; + } +} + +static int decode_micromips_opc (CPUState *env, DisasContext *ctx, int *is_branch) +{ + uint32_t op; + + /* make sure instructions are on a halfword boundary */ + if (ctx->pc & 0x1) { + env->CP0_BadVAddr = ctx->pc; + generate_exception(ctx, EXCP_AdEL); + ctx->bstate = BS_STOP; + return 2; + } + + op = (ctx->opcode >> 10) & 0x3f; + /* Enforce properly-sized instructions in a delay slot */ + if (ctx->hflags & MIPS_HFLAG_BMASK) { + int bits = ctx->hflags & MIPS_HFLAG_BMASK_EXT; + + switch (op) { + case POOL32A: + case POOL32B: + case POOL32I: + case POOL32C: + case ADDI32: + case ADDIU32: + case ORI32: + case XORI32: + case SLTI32: + case SLTIU32: + case ANDI32: + case JALX32: + case LBU32: + case LHU32: + case POOL32F: + case JALS32: + case BEQ32: + case BNE32: + case J32: + case JAL32: + case SB32: + case SH32: + case POOL32S: + case ADDIUPC: + case SWC132: + case SDC132: + case SD32: + case SW32: + case LB32: + case LH32: + case DADDIU32: + case POOL48A: /* ??? */ + case LWC132: + case LDC132: + case LD32: + case LW32: + if (bits & MIPS_HFLAG_BDS16) { + generate_exception(ctx, EXCP_RI); + /* Just stop translation; the user is confused. */ + ctx->bstate = BS_STOP; + return 2; + } + break; + case POOL16A: + case POOL16B: + case POOL16C: + case LWGP16: + case POOL16F: + case LBU16: + case LHU16: + case LWSP16: + case LW16: + case SB16: + case SH16: + case SWSP16: + case SW16: + case MOVE16: + case ANDI16: + case POOL16D: + case POOL16E: + case BEQZ16: + case BNEZ16: + case B16: + case LI16: + if (bits & MIPS_HFLAG_BDS32) { + generate_exception(ctx, EXCP_RI); + /* Just stop translation; the user is confused. */ + ctx->bstate = BS_STOP; + return 2; + } + break; + default: + break; + } + } + switch (op) { + case POOL16A: + { + int rd = mmreg(uMIPS_RD(ctx->opcode)); + int rs1 = mmreg(uMIPS_RS1(ctx->opcode)); + int rs2 = mmreg(uMIPS_RS2(ctx->opcode)); + uint32_t opc = 0; + + switch (ctx->opcode & 0x1) { + case ADDU16: + opc = OPC_ADDU; + break; + case SUBU16: + opc = OPC_SUBU; + break; + } + + gen_arith(env, ctx, opc, rd, rs1, rs2); + } + break; + case POOL16B: + { + int rd = mmreg(uMIPS_RD(ctx->opcode)); + int rs = mmreg(uMIPS_RS(ctx->opcode)); + int amount = (ctx->opcode >> 1) & 0x7; + uint32_t opc = 0; + amount = amount == 0 ? 8 : amount; + + switch (ctx->opcode & 0x1) { + case SLL16: + opc = OPC_SLL; + break; + case SRL16: + opc = OPC_SRL; + break; + } + + gen_shift_imm(env, ctx, opc, rd, rs, amount); + } + break; + case POOL16C: + gen_pool16c_insn(env, ctx, is_branch); + break; + case LWGP16: + { + int rd = mmreg(uMIPS_RD(ctx->opcode)); + int rb = 28; /* GP */ + int16_t offset = SIMM(ctx->opcode, 0, 7) << 2; + + gen_ld(env, ctx, OPC_LW, rd, rb, offset); + } + break; + case POOL16F: + if (ctx->opcode & 1) { + generate_exception(ctx, EXCP_RI); + } else { + /* MOVEP */ + int enc_dest = uMIPS_RD(ctx->opcode); + int enc_rt = uMIPS_RS2(ctx->opcode); + int enc_rs = uMIPS_RS1(ctx->opcode); + int rd, rs, re, rt; + static const int rd_enc[] = { 5, 5, 6, 4, 4, 4, 4, 4 }; + static const int re_enc[] = { 6, 7, 7, 21, 22, 5, 6, 7 }; + static const int rs_rt_enc[] = { 0, 17, 2, 3, 16, 18, 19, 20 }; + + rd = rd_enc[enc_dest]; + re = re_enc[enc_dest]; + rs = rs_rt_enc[enc_rs]; + rt = rs_rt_enc[enc_rt]; + + gen_arith_imm(env, ctx, OPC_ADDIU, rd, rs, 0); + gen_arith_imm(env, ctx, OPC_ADDIU, re, rt, 0); + } + break; + case LBU16: + { + int rd = mmreg(uMIPS_RD(ctx->opcode)); + int rb = mmreg(uMIPS_RS(ctx->opcode)); + int16_t offset = ZIMM(ctx->opcode, 0, 4); + offset = (offset == 0xf ? -1 : offset); + + gen_ld(env, ctx, OPC_LBU, rd, rb, offset); + } + break; + case LHU16: + { + int rd = mmreg(uMIPS_RD(ctx->opcode)); + int rb = mmreg(uMIPS_RS(ctx->opcode)); + int16_t offset = ZIMM(ctx->opcode, 0, 4) << 1; + + gen_ld(env, ctx, OPC_LHU, rd, rb, offset); + } + break; + case LWSP16: + { + int rd = (ctx->opcode >> 5) & 0x1f; + int rb = 29; /* SP */ + int16_t offset = ZIMM(ctx->opcode, 0, 5) << 2; + + gen_ld(env, ctx, OPC_LW, rd, rb, offset); + } + break; + case LW16: + { + int rd = mmreg(uMIPS_RD(ctx->opcode)); + int rb = mmreg(uMIPS_RS(ctx->opcode)); + int16_t offset = ZIMM(ctx->opcode, 0, 4) << 2; + + gen_ld(env, ctx, OPC_LW, rd, rb, offset); + } + break; + case SB16: + { + int rd = mmreg2(uMIPS_RD(ctx->opcode)); + int rb = mmreg(uMIPS_RS(ctx->opcode)); + int16_t offset = ZIMM(ctx->opcode, 0, 4); + + gen_st(ctx, OPC_SB, rd, rb, offset); + } + break; + case SH16: + { + int rd = mmreg2(uMIPS_RD(ctx->opcode)); + int rb = mmreg(uMIPS_RS(ctx->opcode)); + int16_t offset = ZIMM(ctx->opcode, 0, 4) << 1; + + gen_st(ctx, OPC_SH, rd, rb, offset); + } + break; + case SWSP16: + { + int rd = (ctx->opcode >> 5) & 0x1f; + int rb = 29; /* SP */ + int16_t offset = ZIMM(ctx->opcode, 0, 5) << 2; + + gen_st(ctx, OPC_SW, rd, rb, offset); + } + break; + case SW16: + { + int rd = mmreg2(uMIPS_RD(ctx->opcode)); + int rb = mmreg(uMIPS_RS(ctx->opcode)); + int16_t offset = ZIMM(ctx->opcode, 0, 4) << 2; + + gen_st(ctx, OPC_SW, rd, rb, offset); + } + break; + case MOVE16: + { + int rd = uMIPS_RD5(ctx->opcode); + int rs = uMIPS_RS5(ctx->opcode); + + gen_arith_imm(env, ctx, OPC_ADDIU, rd, rs, 0); + } + break; + case ANDI16: + gen_andi16(env, ctx); + break; + case POOL16D: + switch (ctx->opcode & 0x1) { + case ADDIUS5: + gen_addius5(env, ctx); + break; + case ADDIUSP: + gen_addiusp(env, ctx); + break; + } + break; + case POOL16E: + switch (ctx->opcode & 0x1) { + case ADDIUR2: + gen_addiur2(env, ctx); + break; + case ADDIUR1SP: + gen_addiur1sp(env, ctx); + break; + } + break; + case B16: + gen_compute_branch(ctx, OPC_BEQ, 2, 0, 0, + SIMM(ctx->opcode, 0, 10) << 1); + *is_branch = 1; + break; + case BNEZ16: + case BEQZ16: + gen_compute_branch(ctx, op == BNEZ16 ? OPC_BNE : OPC_BEQ, 2, + mmreg(uMIPS_RD(ctx->opcode)), + 0, SIMM(ctx->opcode, 0, 7) << 1); + *is_branch = 1; + break; + case LI16: + { + int reg = mmreg(uMIPS_RD(ctx->opcode)); + int imm = ZIMM(ctx->opcode, 0, 7); + + imm = (imm == 0x7f ? -1 : imm); + tcg_gen_movi_tl(cpu_gpr[reg], imm); + } + break; + case RES_20: + case RES_28: + case RES_29: + case RES_30: + case RES_31: + case RES_38: + case RES_39: + generate_exception(ctx, EXCP_RI); + break; + default: + decode_micromips32_opc (env, ctx, op, is_branch); + return 4; + } + + return 2; +} + +/* SmartMIPS extension to MIPS32 */ + +#if defined(TARGET_MIPS64) + +/* MDMX extension to MIPS64 */ + +#endif + +static void decode_opc (CPUState *env, DisasContext *ctx, int *is_branch) +{ + int32_t offset; + int rs, rt, rd, sa; + uint32_t op, op1, op2; + int16_t imm; + + /* make sure instructions are on a word boundary */ + if (ctx->pc & 0x3) { + env->CP0_BadVAddr = ctx->pc; generate_exception(ctx, EXCP_AdEL); return; } /* Handle blikely not taken case */ - if ((ctx->hflags & MIPS_HFLAG_BMASK) == MIPS_HFLAG_BL) { + if ((ctx->hflags & MIPS_HFLAG_BMASK_BASE) == MIPS_HFLAG_BL) { int l1 = gen_new_label(); MIPS_DEBUG("blikely condition (" TARGET_FMT_lx ")", ctx->pc + 4); @@ -7665,22 +11644,53 @@ switch (op1) { case OPC_SLL: /* Shift with immediate */ case OPC_SRA: - case OPC_SRL: gen_shift_imm(env, ctx, op1, rd, rt, sa); break; + case OPC_SRL: + switch ((ctx->opcode >> 21) & 0x1f) { + case 1: + /* rotr is decoded as srl on non-R2 CPUs */ + if (env->insn_flags & ISA_MIPS32R2) { + op1 = OPC_ROTR; + } + /* Fallthrough */ + case 0: + gen_shift_imm(env, ctx, op1, rd, rt, sa); + break; + default: + generate_exception(ctx, EXCP_RI); + break; + } + break; case OPC_MOVN: /* Conditional move */ case OPC_MOVZ: - check_insn(env, ctx, ISA_MIPS4 | ISA_MIPS32); + check_insn(env, ctx, ISA_MIPS4 | ISA_MIPS32 | + INSN_LOONGSON2E | INSN_LOONGSON2F); gen_cond_move(env, op1, rd, rs, rt); break; case OPC_ADD ... OPC_SUBU: gen_arith(env, ctx, op1, rd, rs, rt); break; case OPC_SLLV: /* Shifts */ - case OPC_SRLV: case OPC_SRAV: gen_shift(env, ctx, op1, rd, rs, rt); break; + case OPC_SRLV: + switch ((ctx->opcode >> 6) & 0x1f) { + case 1: + /* rotrv is decoded as srlv on non-R2 CPUs */ + if (env->insn_flags & ISA_MIPS32R2) { + op1 = OPC_ROTRV; + } + /* Fallthrough */ + case 0: + gen_shift(env, ctx, op1, rd, rs, rt); + break; + default: + generate_exception(ctx, EXCP_RI); + break; + } + break; case OPC_SLT: /* Set on less than */ case OPC_SLTU: gen_slt(env, op1, rd, rs, rt); @@ -7700,8 +11710,9 @@ gen_muldiv(ctx, op1, rs, rt); break; case OPC_JR ... OPC_JALR: - gen_compute_branch(ctx, op1, rs, rd, sa); - return; + gen_compute_branch(ctx, op1, 4, rs, rd, sa); + *is_branch = 1; + break; case OPC_TGE ... OPC_TEQ: /* Traps */ case OPC_TNE: gen_trap(ctx, op1, rs, rt, -1); @@ -7758,14 +11769,48 @@ /* MIPS64 specific opcodes */ case OPC_DSLL: case OPC_DSRA: - case OPC_DSRL: case OPC_DSLL32: case OPC_DSRA32: - case OPC_DSRL32: check_insn(env, ctx, ISA_MIPS3); check_mips_64(ctx); gen_shift_imm(env, ctx, op1, rd, rt, sa); break; + case OPC_DSRL: + switch ((ctx->opcode >> 21) & 0x1f) { + case 1: + /* drotr is decoded as dsrl on non-R2 CPUs */ + if (env->insn_flags & ISA_MIPS32R2) { + op1 = OPC_DROTR; + } + /* Fallthrough */ + case 0: + check_insn(env, ctx, ISA_MIPS3); + check_mips_64(ctx); + gen_shift_imm(env, ctx, op1, rd, rt, sa); + break; + default: + generate_exception(ctx, EXCP_RI); + break; + } + break; + case OPC_DSRL32: + switch ((ctx->opcode >> 21) & 0x1f) { + case 1: + /* drotr32 is decoded as dsrl32 on non-R2 CPUs */ + if (env->insn_flags & ISA_MIPS32R2) { + op1 = OPC_DROTR32; + } + /* Fallthrough */ + case 0: + check_insn(env, ctx, ISA_MIPS3); + check_mips_64(ctx); + gen_shift_imm(env, ctx, op1, rd, rt, sa); + break; + default: + generate_exception(ctx, EXCP_RI); + break; + } + break; case OPC_DADD ... OPC_DSUBU: check_insn(env, ctx, ISA_MIPS3); check_mips_64(ctx); @@ -7773,11 +11818,28 @@ break; case OPC_DSLLV: case OPC_DSRAV: - case OPC_DSRLV: check_insn(env, ctx, ISA_MIPS3); check_mips_64(ctx); gen_shift(env, ctx, op1, rd, rs, rt); break; + case OPC_DSRLV: + switch ((ctx->opcode >> 6) & 0x1f) { + case 1: + /* drotrv is decoded as dsrlv on non-R2 CPUs */ + if (env->insn_flags & ISA_MIPS32R2) { + op1 = OPC_DROTRV; + } + /* Fallthrough */ + case 0: + check_insn(env, ctx, ISA_MIPS3); + check_mips_64(ctx); + gen_shift(env, ctx, op1, rd, rs, rt); + break; + default: + generate_exception(ctx, EXCP_RI); + break; + } + break; case OPC_DMULT ... OPC_DDIVU: check_insn(env, ctx, ISA_MIPS3); check_mips_64(ctx); @@ -7818,6 +11880,15 @@ } /* Treat as NOP. */ break; + case OPC_DIV_G_2F: + case OPC_DIVU_G_2F: + case OPC_MULT_G_2F: + case OPC_MULTU_G_2F: + case OPC_MOD_G_2F: + case OPC_MODU_G_2F: + check_insn(env, ctx, INSN_LOONGSON2F); + gen_loongson_integer(ctx, op1, rd, rs, rt); + break; #if defined(TARGET_MIPS64) case OPC_DCLO: case OPC_DCLZ: @@ -7825,6 +11896,15 @@ check_mips_64(ctx); gen_cl(ctx, op1, rd, rs); break; + case OPC_DMULT_G_2F: + case OPC_DMULTU_G_2F: + case OPC_DDIV_G_2F: + case OPC_DDIVU_G_2F: + case OPC_DMOD_G_2F: + case OPC_DMODU_G_2F: + check_insn(env, ctx, INSN_LOONGSON2F); + gen_loongson_integer(ctx, op1, rd, rs, rt); + break; #endif default: /* Invalid */ MIPS_INVAL("special2"); @@ -7846,47 +11926,7 @@ gen_bshfl(ctx, op2, rt, rd); break; case OPC_RDHWR: - check_insn(env, ctx, ISA_MIPS32R2); - { - TCGv t0 = tcg_temp_new(); - - switch (rd) { - case 0: - save_cpu_state(ctx, 1); - gen_helper_rdhwr_cpunum(t0); - gen_store_gpr(t0, rt); - break; - case 1: - save_cpu_state(ctx, 1); - gen_helper_rdhwr_synci_step(t0); - gen_store_gpr(t0, rt); - break; - case 2: - save_cpu_state(ctx, 1); - gen_helper_rdhwr_cc(t0); - gen_store_gpr(t0, rt); - break; - case 3: - save_cpu_state(ctx, 1); - gen_helper_rdhwr_ccres(t0); - gen_store_gpr(t0, rt); - break; - case 29: -#if defined(CONFIG_USER_ONLY) - tcg_gen_ld_tl(t0, cpu_env, offsetof(CPUState, tls_value)); - gen_store_gpr(t0, rt); - break; -#else - /* XXX: Some CPUs implement this in hardware. - Not supported yet. */ -#endif - default: /* Invalid */ - MIPS_INVAL("rdhwr"); - generate_exception(ctx, EXCP_RI); - break; - } - tcg_temp_free(t0); - } + gen_rdhwr(env, ctx, rt, rd); break; case OPC_FORK: check_insn(env, ctx, ASE_MT); @@ -7913,6 +11953,12 @@ tcg_temp_free(t0); } break; + case OPC_DIV_G_2E ... OPC_DIVU_G_2E: + case OPC_MULT_G_2E ... OPC_MULTU_G_2E: + case OPC_MOD_G_2E ... OPC_MODU_G_2E: + check_insn(env, ctx, INSN_LOONGSON2E); + gen_loongson_integer(ctx, op1, rd, rs, rt); + break; #if defined(TARGET_MIPS64) case OPC_DEXTM ... OPC_DEXT: case OPC_DINSM ... OPC_DINS: @@ -7926,6 +11972,12 @@ op2 = MASK_DBSHFL(ctx->opcode); gen_bshfl(ctx, op2, rt, rd); break; + case OPC_DDIV_G_2E ... OPC_DDIVU_G_2E: + case OPC_DMULT_G_2E ... OPC_DMULTU_G_2E: + case OPC_DMOD_G_2E ... OPC_DMODU_G_2E: + check_insn(env, ctx, INSN_LOONGSON2E); + gen_loongson_integer(ctx, op1, rd, rs, rt); + break; #endif default: /* Invalid */ MIPS_INVAL("special3"); @@ -7938,8 +11990,9 @@ switch (op1) { case OPC_BLTZ ... OPC_BGEZL: /* REGIMM branches */ case OPC_BLTZAL ... OPC_BGEZALL: - gen_compute_branch(ctx, op1, rs, -1, imm << 2); - return; + gen_compute_branch(ctx, op1, 4, rs, -1, imm << 2); + *is_branch = 1; + break; case OPC_TGEI ... OPC_TEQI: /* REGIMM traps */ case OPC_TNEI: gen_trap(ctx, op1, rs, -1, imm); @@ -7984,22 +12037,22 @@ switch (op2) { case OPC_DMT: check_insn(env, ctx, ASE_MT); - gen_helper_dmt(t0, t0); + gen_helper_dmt(t0); gen_store_gpr(t0, rt); break; case OPC_EMT: check_insn(env, ctx, ASE_MT); - gen_helper_emt(t0, t0); + gen_helper_emt(t0); gen_store_gpr(t0, rt); break; case OPC_DVPE: check_insn(env, ctx, ASE_MT); - gen_helper_dvpe(t0, t0); + gen_helper_dvpe(t0); gen_store_gpr(t0, rt); break; case OPC_EVPE: check_insn(env, ctx, ASE_MT); - gen_helper_evpe(t0, t0); + gen_helper_evpe(t0); gen_store_gpr(t0, rt); break; case OPC_DI: @@ -8057,17 +12110,21 @@ break; case OPC_J ... OPC_JAL: /* Jump */ offset = (int32_t)(ctx->opcode & 0x3FFFFFF) << 2; - gen_compute_branch(ctx, op, rs, rt, offset); - return; + gen_compute_branch(ctx, op, 4, rs, rt, offset); + *is_branch = 1; + break; case OPC_BEQ ... OPC_BGTZ: /* Branch */ case OPC_BEQL ... OPC_BGTZL: - gen_compute_branch(ctx, op, rs, rt, imm << 2); - return; + gen_compute_branch(ctx, op, 4, rs, rt, imm << 2); + *is_branch = 1; + break; case OPC_LB ... OPC_LWR: /* Load and stores */ + case OPC_LL: + gen_ld(env, ctx, op, rt, rs, imm); + break; case OPC_SB ... OPC_SW: case OPC_SWR: - case OPC_LL: - gen_ldst(ctx, op, rt, rs, imm); + gen_st(ctx, op, rt, rs, imm); break; case OPC_SC: gen_st_cond(ctx, op, rt, rs, imm); @@ -8086,12 +12143,7 @@ case OPC_LDC1: case OPC_SWC1: case OPC_SDC1: - if (env->CP0_Config1 & (1 << CP0C1_FP)) { - check_cp1_enabled(ctx); - gen_flt_ldst(ctx, op, rt, rs, imm); - } else { - generate_exception_err(ctx, EXCP_CpU, 1); - } + gen_cop1_ldst(env, ctx, op, rt, rs, imm); break; case OPC_CP1: @@ -8123,13 +12175,14 @@ case OPC_BC1: gen_compute_branch1(env, ctx, MASK_BC1(ctx->opcode), (rt >> 2) & 0x7, imm << 2); - return; + *is_branch = 1; + break; case OPC_S_FMT: case OPC_D_FMT: case OPC_W_FMT: case OPC_L_FMT: case OPC_PS_FMT: - gen_farith(ctx, MASK_CP1_FUNC(ctx->opcode), rt, rd, sa, + gen_farith(ctx, ctx->opcode & FOP(0x3f, 0x1f), rt, rd, sa, (imm >> 8) & 0x7); break; default: @@ -8197,13 +12250,17 @@ /* MIPS64 opcodes */ case OPC_LWU: case OPC_LDL ... OPC_LDR: - case OPC_SDL ... OPC_SDR: case OPC_LLD: case OPC_LD: + check_insn(env, ctx, ISA_MIPS3); + check_mips_64(ctx); + gen_ld(env, ctx, op, rt, rs, imm); + break; + case OPC_SDL ... OPC_SDR: case OPC_SD: check_insn(env, ctx, ISA_MIPS3); check_mips_64(ctx); - gen_ldst(ctx, op, rt, rs, imm); + gen_st(ctx, op, rt, rs, imm); break; case OPC_SCD: check_insn(env, ctx, ISA_MIPS3); @@ -8218,8 +12275,11 @@ break; #endif case OPC_JALX: - check_insn(env, ctx, ASE_MIPS16); - /* MIPS16: Not implemented. */ + check_insn(env, ctx, ASE_MIPS16 | ASE_MICROMIPS); + offset = (int32_t)(ctx->opcode & 0x3FFFFFF) << 2; + gen_compute_branch(ctx, op, 4, rs, rt, offset); + *is_branch = 1; + break; case OPC_MDMX: check_insn(env, ctx, ASE_MDMX); /* MDMX: Not implemented. */ @@ -8228,51 +12288,6 @@ generate_exception(ctx, EXCP_RI); break; } - if (ctx->hflags & MIPS_HFLAG_BMASK) { - int hflags = ctx->hflags & MIPS_HFLAG_BMASK; - /* Branches completion */ - ctx->hflags &= ~MIPS_HFLAG_BMASK; - ctx->bstate = BS_BRANCH; - save_cpu_state(ctx, 0); - /* FIXME: Need to clear can_do_io. */ - switch (hflags) { - case MIPS_HFLAG_B: - /* unconditional branch */ - MIPS_DEBUG("unconditional branch"); - gen_goto_tb(ctx, 0, ctx->btarget); - break; - case MIPS_HFLAG_BL: - /* blikely taken case */ - MIPS_DEBUG("blikely branch taken"); - gen_goto_tb(ctx, 0, ctx->btarget); - break; - case MIPS_HFLAG_BC: - /* Conditional branch */ - MIPS_DEBUG("conditional branch"); - { - int l1 = gen_new_label(); - - tcg_gen_brcondi_tl(TCG_COND_NE, bcond, 0, l1); - gen_goto_tb(ctx, 1, ctx->pc + 4); - gen_set_label(l1); - gen_goto_tb(ctx, 0, ctx->btarget); - } - break; - case MIPS_HFLAG_BR: - /* unconditional branch to register */ - MIPS_DEBUG("branch to register"); - tcg_gen_mov_tl(cpu_PC, btarget); - if (ctx->singlestep_enabled) { - save_cpu_state(ctx, 0); - gen_helper_0i(raise_exception, EXCP_DEBUG); - } - tcg_gen_exit_tb(0); - break; - default: - MIPS_DEBUG("unknown branch"); - break; - } - } } static inline void @@ -8286,6 +12301,8 @@ int j, lj = -1; int num_insns; int max_insns; + int insn_bytes; + int is_branch; if (search_pc) qemu_log("search pc %d\n", search_pc); @@ -8309,11 +12326,6 @@ max_insns = tb->cflags & CF_COUNT_MASK; if (max_insns == 0) max_insns = CF_COUNT_MASK; -#ifdef DEBUG_DISAS - qemu_log_mask(CPU_LOG_TB_CPU, "------------------------------------------------\n"); - /* FIXME: This may print out stale hflags from env... */ - log_cpu_state_mask(CPU_LOG_TB_CPU, env, 0); -#endif LOG_DISAS("\ntb %p idx %d hflags %04x\n", tb, ctx.mem_idx, ctx.hflags); gen_icount_start(); while (ctx.bstate == BS_NONE) { @@ -8345,9 +12357,28 @@ } if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO)) gen_io_start(); - ctx.opcode = ldl_code(ctx.pc); - decode_opc(env, &ctx); - ctx.pc += 4; + + is_branch = 0; + if (!(ctx.hflags & MIPS_HFLAG_M16)) { + ctx.opcode = ldl_code(ctx.pc); + insn_bytes = 4; + decode_opc(env, &ctx, &is_branch); + } else if (env->insn_flags & ASE_MICROMIPS) { + ctx.opcode = lduw_code(ctx.pc); + insn_bytes = decode_micromips_opc(env, &ctx, &is_branch); + } else if (env->insn_flags & ASE_MIPS16) { + ctx.opcode = lduw_code(ctx.pc); + insn_bytes = decode_mips16_opc(env, &ctx, &is_branch); + } else { + generate_exception(&ctx, EXCP_RI); + ctx.bstate = BS_STOP; + break; + } + if (!is_branch) { + handle_delay_slot(env, &ctx, insn_bytes); + } + ctx.pc += insn_bytes; + num_insns++; /* Execute a branch and its delay slot as a single instruction. @@ -8377,7 +12408,6 @@ } else { switch (ctx.bstate) { case BS_STOP: - gen_helper_interrupt_restart(); gen_goto_tb(&ctx, 0, ctx.pc); break; case BS_NONE: @@ -8385,7 +12415,6 @@ gen_goto_tb(&ctx, 0, ctx.pc); break; case BS_EXCP: - gen_helper_interrupt_restart(); tcg_gen_exit_tb(0); break; case BS_BRANCH: @@ -8412,7 +12441,6 @@ log_target_disas(pc_start, ctx.pc - pc_start, 0); qemu_log("\n"); } - qemu_log_mask(CPU_LOG_TB_CPU, "---------------- %d %08x\n", ctx.bstate, ctx.hflags); #endif } @@ -8426,32 +12454,37 @@ gen_intermediate_code_internal(env, tb, 1); } -static void fpu_dump_state(CPUState *env, FILE *f, - int (*fpu_fprintf)(FILE *f, const char *fmt, ...), +static void fpu_dump_state(CPUState *env, FILE *f, fprintf_function fpu_fprintf, int flags) { int i; int is_fpu64 = !!(env->hflags & MIPS_HFLAG_F64); -#define printfpr(fp) \ - do { \ - if (is_fpu64) \ - fpu_fprintf(f, "w:%08x d:%016lx fd:%13g fs:%13g psu: %13g\n", \ - (fp)->w[FP_ENDIAN_IDX], (fp)->d, (fp)->fd, \ - (fp)->fs[FP_ENDIAN_IDX], (fp)->fs[!FP_ENDIAN_IDX]); \ - else { \ - fpr_t tmp; \ - tmp.w[FP_ENDIAN_IDX] = (fp)->w[FP_ENDIAN_IDX]; \ - tmp.w[!FP_ENDIAN_IDX] = ((fp) + 1)->w[FP_ENDIAN_IDX]; \ - fpu_fprintf(f, "w:%08x d:%016lx fd:%13g fs:%13g psu:%13g\n", \ - tmp.w[FP_ENDIAN_IDX], tmp.d, tmp.fd, \ - tmp.fs[FP_ENDIAN_IDX], tmp.fs[!FP_ENDIAN_IDX]); \ - } \ +#define printfpr(fp) \ + do { \ + if (is_fpu64) \ + fpu_fprintf(f, "w:%08x d:%016" PRIx64 \ + " fd:%13g fs:%13g psu: %13g\n", \ + (fp)->w[FP_ENDIAN_IDX], (fp)->d, \ + (double)(fp)->fd, \ + (double)(fp)->fs[FP_ENDIAN_IDX], \ + (double)(fp)->fs[!FP_ENDIAN_IDX]); \ + else { \ + fpr_t tmp; \ + tmp.w[FP_ENDIAN_IDX] = (fp)->w[FP_ENDIAN_IDX]; \ + tmp.w[!FP_ENDIAN_IDX] = ((fp) + 1)->w[FP_ENDIAN_IDX]; \ + fpu_fprintf(f, "w:%08x d:%016" PRIx64 \ + " fd:%13g fs:%13g psu:%13g\n", \ + tmp.w[FP_ENDIAN_IDX], tmp.d, \ + (double)tmp.fd, \ + (double)tmp.fs[FP_ENDIAN_IDX], \ + (double)tmp.fs[!FP_ENDIAN_IDX]); \ + } \ } while(0) - fpu_fprintf(f, "CP1 FCR0 0x%08x FCR31 0x%08x SR.FR %d fp_status 0x%08x(0x%02x)\n", - env->active_fpu.fcr0, env->active_fpu.fcr31, is_fpu64, env->active_fpu.fp_status, + fpu_fprintf(f, "CP1 FCR0 0x%08x FCR31 0x%08x SR.FR %d fp_status 0x%02x\n", + env->active_fpu.fcr0, env->active_fpu.fcr31, is_fpu64, get_float_exception_flags(&env->active_fpu.fp_status)); for (i = 0; i < 32; (is_fpu64) ? i++ : (i += 2)) { fpu_fprintf(f, "%3s: ", fregnames[i]); @@ -8469,7 +12502,7 @@ static void cpu_mips_check_sign_extensions (CPUState *env, FILE *f, - int (*cpu_fprintf)(FILE *f, const char *fmt, ...), + fprintf_function cpu_fprintf, int flags) { int i; @@ -8495,13 +12528,14 @@ } #endif -void cpu_dump_state (CPUState *env, FILE *f, - int (*cpu_fprintf)(FILE *f, const char *fmt, ...), +void cpu_dump_state (CPUState *env, FILE *f, fprintf_function cpu_fprintf, int flags) { int i; - cpu_fprintf(f, "pc=0x" TARGET_FMT_lx " HI=0x" TARGET_FMT_lx " LO=0x" TARGET_FMT_lx " ds %04x " TARGET_FMT_lx " %d\n", + cpu_fprintf(f, "pc=0x" TARGET_FMT_lx " HI=0x" TARGET_FMT_lx + " LO=0x" TARGET_FMT_lx " ds %04x " + TARGET_FMT_lx " " TARGET_FMT_ld "\n", env->active_tc.PC, env->active_tc.HI[0], env->active_tc.LO[0], env->hflags, env->btarget, env->bcond); for (i = 0; i < 32; i++) { @@ -8676,8 +12710,7 @@ env->CP0_Random = env->tlb->nb_tlb - 1; env->tlb->tlb_in_use = env->tlb->nb_tlb; env->CP0_Wired = 0; - /* SMP not implemented */ - env->CP0_EBase = 0x80000000; + env->CP0_EBase = 0x80000000 | (env->cpu_index & 0x3FF); env->CP0_Status = (1 << CP0St_BEV) | (1 << CP0St_ERL); /* vectored interrupts not implemented, timer on int 7, no performance counters. */ diff -Nru qemu-kvm-0.12.5+noroms/target-mips/translate_init.c qemu-kvm-0.14.1/target-mips/translate_init.c --- qemu-kvm-0.12.5+noroms/target-mips/translate_init.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-mips/translate_init.c 2011-05-11 13:29:46.000000000 +0000 @@ -45,10 +45,6 @@ (0 << CP0C3_VEIC) | (0 << CP0C3_VInt) | (0 << CP0C3_SP) | \ (0 << CP0C3_SM) | (0 << CP0C3_TL)) -/* Define a implementation number of 1. - Define a major version 1, minor version 0. */ -#define MIPS_FCR0 ((0 << FCR0_S) | (0x1 << FCR0_PRID) | (0x10 << FCR0_REV)) - /* MMU types, the first four entries have the same layout as the CP0C0_MT field. */ enum mips_mmu_types { @@ -104,7 +100,8 @@ .CP0_Config0 = MIPS_CONFIG0 | (MMU_TYPE_R4000 << CP0C0_MT), .CP0_Config1 = MIPS_CONFIG1 | (15 << CP0C1_MMU) | (0 << CP0C1_IS) | (3 << CP0C1_IL) | (1 << CP0C1_IA) | - (0 << CP0C1_DS) | (3 << CP0C1_DL) | (1 << CP0C1_DA), + (0 << CP0C1_DS) | (3 << CP0C1_DL) | (1 << CP0C1_DA) | + (0 << CP0C1_CA), .CP0_Config2 = MIPS_CONFIG2, .CP0_Config3 = MIPS_CONFIG3, .CP0_LLAddr_rw_bitmask = 0, @@ -114,7 +111,7 @@ .CP0_Status_rw_bitmask = 0x1278FF17, .SEGBITS = 32, .PABITS = 32, - .insn_flags = CPU_MIPS32 | ASE_MIPS16, + .insn_flags = CPU_MIPS32, .mmu_type = MMU_TYPE_R4000, }, { @@ -125,7 +122,8 @@ .CP0_Config0 = MIPS_CONFIG0 | (MMU_TYPE_FMT << CP0C0_MT), .CP0_Config1 = MIPS_CONFIG1 | (0 << CP0C1_IS) | (3 << CP0C1_IL) | (1 << CP0C1_IA) | - (0 << CP0C1_DS) | (3 << CP0C1_DL) | (1 << CP0C1_DA), + (0 << CP0C1_DS) | (3 << CP0C1_DL) | (1 << CP0C1_DA) | + (1 << CP0C1_CA), .CP0_Config2 = MIPS_CONFIG2, .CP0_Config3 = MIPS_CONFIG3, .CP0_LLAddr_rw_bitmask = 0, @@ -144,7 +142,8 @@ .CP0_Config0 = MIPS_CONFIG0 | (MMU_TYPE_R4000 << CP0C0_MT), .CP0_Config1 = MIPS_CONFIG1 | (15 << CP0C1_MMU) | (0 << CP0C1_IS) | (3 << CP0C1_IL) | (1 << CP0C1_IA) | - (0 << CP0C1_DS) | (3 << CP0C1_DL) | (1 << CP0C1_DA), + (0 << CP0C1_DS) | (3 << CP0C1_DL) | (1 << CP0C1_DA) | + (0 << CP0C1_CA), .CP0_Config2 = MIPS_CONFIG2, .CP0_Config3 = MIPS_CONFIG3, .CP0_LLAddr_rw_bitmask = 0, @@ -154,7 +153,7 @@ .CP0_Status_rw_bitmask = 0x1278FF17, .SEGBITS = 32, .PABITS = 32, - .insn_flags = CPU_MIPS32 | ASE_MIPS16, + .insn_flags = CPU_MIPS32, .mmu_type = MMU_TYPE_R4000, }, { @@ -163,7 +162,8 @@ .CP0_Config0 = MIPS_CONFIG0 | (MMU_TYPE_FMT << CP0C0_MT), .CP0_Config1 = MIPS_CONFIG1 | (0 << CP0C1_IS) | (3 << CP0C1_IL) | (1 << CP0C1_IA) | - (0 << CP0C1_DS) | (3 << CP0C1_DL) | (1 << CP0C1_DA), + (0 << CP0C1_DS) | (3 << CP0C1_DL) | (1 << CP0C1_DA) | + (1 << CP0C1_CA), .CP0_Config2 = MIPS_CONFIG2, .CP0_Config3 = MIPS_CONFIG3, .CP0_LLAddr_rw_bitmask = 0, @@ -183,7 +183,8 @@ (MMU_TYPE_R4000 << CP0C0_MT), .CP0_Config1 = MIPS_CONFIG1 | (15 << CP0C1_MMU) | (0 << CP0C1_IS) | (3 << CP0C1_IL) | (1 << CP0C1_IA) | - (0 << CP0C1_DS) | (3 << CP0C1_DL) | (1 << CP0C1_DA), + (0 << CP0C1_DS) | (3 << CP0C1_DL) | (1 << CP0C1_DA) | + (0 << CP0C1_CA), .CP0_Config2 = MIPS_CONFIG2, .CP0_Config3 = MIPS_CONFIG3 | (0 << CP0C3_VInt), .CP0_LLAddr_rw_bitmask = 0, @@ -193,7 +194,7 @@ .CP0_Status_rw_bitmask = 0x1278FF17, .SEGBITS = 32, .PABITS = 32, - .insn_flags = CPU_MIPS32R2 | ASE_MIPS16, + .insn_flags = CPU_MIPS32R2, .mmu_type = MMU_TYPE_R4000, }, { @@ -203,7 +204,8 @@ (MMU_TYPE_FMT << CP0C0_MT), .CP0_Config1 = MIPS_CONFIG1 | (0 << CP0C1_IS) | (3 << CP0C1_IL) | (1 << CP0C1_IA) | - (0 << CP0C1_DS) | (3 << CP0C1_DL) | (1 << CP0C1_DA), + (0 << CP0C1_DS) | (3 << CP0C1_DL) | (1 << CP0C1_DA) | + (1 << CP0C1_CA), .CP0_Config2 = MIPS_CONFIG2, .CP0_Config3 = MIPS_CONFIG3, .CP0_LLAddr_rw_bitmask = 0, @@ -223,7 +225,8 @@ (MMU_TYPE_R4000 << CP0C0_MT), .CP0_Config1 = MIPS_CONFIG1 | (15 << CP0C1_MMU) | (0 << CP0C1_IS) | (3 << CP0C1_IL) | (1 << CP0C1_IA) | - (0 << CP0C1_DS) | (3 << CP0C1_DL) | (1 << CP0C1_DA), + (0 << CP0C1_DS) | (3 << CP0C1_DL) | (1 << CP0C1_DA) | + (1 << CP0C1_CA), .CP0_Config2 = MIPS_CONFIG2, .CP0_Config3 = MIPS_CONFIG3 | (0 << CP0C3_VInt), .CP0_LLAddr_rw_bitmask = 0, @@ -244,7 +247,8 @@ (MMU_TYPE_R4000 << CP0C0_MT), .CP0_Config1 = MIPS_CONFIG1 | (1 << CP0C1_FP) | (15 << CP0C1_MMU) | (0 << CP0C1_IS) | (3 << CP0C1_IL) | (1 << CP0C1_IA) | - (0 << CP0C1_DS) | (3 << CP0C1_DL) | (1 << CP0C1_DA), + (0 << CP0C1_DS) | (3 << CP0C1_DL) | (1 << CP0C1_DA) | + (1 << CP0C1_CA), .CP0_Config2 = MIPS_CONFIG2, .CP0_Config3 = MIPS_CONFIG3 | (0 << CP0C3_VInt), .CP0_LLAddr_rw_bitmask = 0, @@ -267,7 +271,8 @@ (MMU_TYPE_R4000 << CP0C0_MT), .CP0_Config1 = MIPS_CONFIG1 | (1 << CP0C1_FP) | (15 << CP0C1_MMU) | (0 << CP0C1_IS) | (3 << CP0C1_IL) | (1 << CP0C1_IA) | - (0 << CP0C1_DS) | (3 << CP0C1_DL) | (1 << CP0C1_DA), + (0 << CP0C1_DS) | (3 << CP0C1_DL) | (1 << CP0C1_DA) | + (1 << CP0C1_CA), .CP0_Config2 = MIPS_CONFIG2, .CP0_Config3 = MIPS_CONFIG3 | (0 << CP0C3_VInt) | (1 << CP0C3_MT), .CP0_LLAddr_rw_bitmask = 0, @@ -445,6 +450,41 @@ .insn_flags = CPU_MIPS64R2 | ASE_MIPS3D, .mmu_type = MMU_TYPE_R4000, }, + { + .name = "Loongson-2E", + .CP0_PRid = 0x6302, + /*64KB I-cache and d-cache. 4 way with 32 bit cache line size*/ + .CP0_Config0 = (0x1<<17) | (0x1<<16) | (0x1<<11) | (0x1<<8) | (0x1<<5) | + (0x1<<4) | (0x1<<1), + /* Note: Config1 is only used internally, Loongson-2E has only Config0. */ + .CP0_Config1 = (1 << CP0C1_FP) | (47 << CP0C1_MMU), + .SYNCI_Step = 16, + .CCRes = 2, + .CP0_Status_rw_bitmask = 0x35D0FFFF, + .CP1_fcr0 = (0x5 << FCR0_PRID) | (0x1 << FCR0_REV), + .SEGBITS = 40, + .PABITS = 40, + .insn_flags = CPU_LOONGSON2E, + .mmu_type = MMU_TYPE_R4000, + }, + { + .name = "Loongson-2F", + .CP0_PRid = 0x6303, + /*64KB I-cache and d-cache. 4 way with 32 bit cache line size*/ + .CP0_Config0 = (0x1<<17) | (0x1<<16) | (0x1<<11) | (0x1<<8) | (0x1<<5) | + (0x1<<4) | (0x1<<1), + /* Note: Config1 is only used internally, Loongson-2F has only Config0. */ + .CP0_Config1 = (1 << CP0C1_FP) | (47 << CP0C1_MMU), + .SYNCI_Step = 16, + .CCRes = 2, + .CP0_Status_rw_bitmask = 0xF5D0FF1F, /*bit5:7 not writeable*/ + .CP1_fcr0 = (0x5 << FCR0_PRID) | (0x1 << FCR0_REV), + .SEGBITS = 40, + .PABITS = 40, + .insn_flags = CPU_LOONGSON2F, + .mmu_type = MMU_TYPE_R4000, + }, + #endif }; @@ -460,7 +500,7 @@ return NULL; } -void mips_cpu_list (FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...)) +void mips_cpu_list (FILE *f, fprintf_function cpu_fprintf) { int i; diff -Nru qemu-kvm-0.12.5+noroms/target-ppc/cpu.h qemu-kvm-0.14.1/target-ppc/cpu.h --- qemu-kvm-0.12.5+noroms/target-ppc/cpu.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-ppc/cpu.h 2011-05-11 13:29:46.000000000 +0000 @@ -20,7 +20,7 @@ #define __CPU_PPC_H__ #include "config.h" -#include +#include "qemu-common.h" //#define PPC_EMULATE_32BITS_HYPV @@ -29,6 +29,20 @@ #define TARGET_LONG_BITS 64 #define TARGET_PAGE_BITS 12 +/* Note that the official physical address space bits is 62-M where M + is implementation dependent. I've not looked up M for the set of + cpus we emulate at the system level. */ +#define TARGET_PHYS_ADDR_SPACE_BITS 62 + +/* Note that the PPC environment architecture talks about 80 bit virtual + addresses, with segmentation. Obviously that's not all visible to a + single process, which is all we're concerned with here. */ +#ifdef TARGET_ABI32 +# define TARGET_VIRT_ADDR_SPACE_BITS 32 +#else +# define TARGET_VIRT_ADDR_SPACE_BITS 64 +#endif + #else /* defined (TARGET_PPC64) */ /* PowerPC 32 definitions */ #define TARGET_LONG_BITS 32 @@ -51,6 +65,9 @@ #define TARGET_PAGE_BITS 12 #endif /* defined(TARGET_PPCEMB) */ +#define TARGET_PHYS_ADDR_SPACE_BITS 32 +#define TARGET_VIRT_ADDR_SPACE_BITS 32 + #endif /* defined (TARGET_PPC64) */ #define CPUState struct CPUPPCState @@ -316,6 +333,7 @@ uint64_t u64[2]; }; +#if !defined(CONFIG_USER_ONLY) /* Software TLB cache */ typedef struct ppc6xx_tlb_t ppc6xx_tlb_t; struct ppc6xx_tlb_t { @@ -338,6 +356,7 @@ ppc6xx_tlb_t tlb6; ppcemb_tlb_t tlbe; }; +#endif typedef struct ppc_slb_t ppc_slb_t; struct ppc_slb_t { @@ -435,6 +454,9 @@ #endif #endif +/* Exception state register bits definition */ +#define ESR_ST 23 /* Exception was caused by a store type access. */ + enum { POWERPC_FLAG_NONE = 0x00000000, /* Flag for MSR bit 25 signification (VRE/SPE) */ @@ -679,10 +701,12 @@ int power_mode; int (*check_pow)(CPUPPCState *env); - /* temporary hack to handle OSI calls (only used if non NULL) */ - int (*osi_call)(struct CPUPPCState *env); +#if !defined(CONFIG_USER_ONLY) + void *load_info; /* Holds boot loading state. */ +#endif }; +#if !defined(CONFIG_USER_ONLY) /* Context used internally during MMU translations */ typedef struct mmu_ctx_t mmu_ctx_t; struct mmu_ctx_t { @@ -694,6 +718,7 @@ int key; /* Access key */ int nx; /* Non-execute area */ }; +#endif /*****************************************************************************/ CPUPPCState *cpu_ppc_init (const char *cpu_model); @@ -708,8 +733,10 @@ int cpu_ppc_handle_mmu_fault (CPUPPCState *env, target_ulong address, int rw, int mmu_idx, int is_softmmu); #define cpu_handle_mmu_fault cpu_ppc_handle_mmu_fault +#if !defined(CONFIG_USER_ONLY) int get_physical_address (CPUPPCState *env, mmu_ctx_t *ctx, target_ulong vaddr, int rw, int access_type); +#endif void do_interrupt (CPUPPCState *env); void ppc_hw_interrupt (CPUPPCState *env); @@ -735,18 +762,18 @@ #endif /* !defined(CONFIG_USER_ONLY) */ void ppc_store_msr (CPUPPCState *env, target_ulong value); -void ppc_cpu_list (FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...)); +void ppc_cpu_list (FILE *f, fprintf_function cpu_fprintf); const ppc_def_t *cpu_ppc_find_by_name (const char *name); int cpu_ppc_register_internal (CPUPPCState *env, const ppc_def_t *def); /* Time-base and decrementer management */ #ifndef NO_CPU_IO_DEFS -uint32_t cpu_ppc_load_tbl (CPUPPCState *env); +uint64_t cpu_ppc_load_tbl (CPUPPCState *env); uint32_t cpu_ppc_load_tbu (CPUPPCState *env); void cpu_ppc_store_tbu (CPUPPCState *env, uint32_t value); void cpu_ppc_store_tbl (CPUPPCState *env, uint32_t value); -uint32_t cpu_ppc_load_atbl (CPUPPCState *env); +uint64_t cpu_ppc_load_atbl (CPUPPCState *env); uint32_t cpu_ppc_load_atbu (CPUPPCState *env); void cpu_ppc_store_atbl (CPUPPCState *env, uint32_t value); void cpu_ppc_store_atbu (CPUPPCState *env, uint32_t value); @@ -796,8 +823,8 @@ } /* Device control registers */ -int ppc_dcr_read (ppc_dcr_t *dcr_env, int dcrn, target_ulong *valp); -int ppc_dcr_write (ppc_dcr_t *dcr_env, int dcrn, target_ulong val); +int ppc_dcr_read (ppc_dcr_t *dcr_env, int dcrn, uint32_t *valp); +int ppc_dcr_write (ppc_dcr_t *dcr_env, int dcrn, uint32_t val); #define cpu_init cpu_ppc_init #define cpu_exec cpu_ppc_exec @@ -827,7 +854,6 @@ #endif #include "cpu-all.h" -#include "exec-all.h" /*****************************************************************************/ /* CRF definitions */ @@ -835,10 +861,10 @@ #define CRF_GT 2 #define CRF_EQ 1 #define CRF_SO 0 -#define CRF_CH (1 << 4) -#define CRF_CL (1 << 3) -#define CRF_CH_OR_CL (1 << 2) -#define CRF_CH_AND_CL (1 << 1) +#define CRF_CH (1 << CRF_LT) +#define CRF_CL (1 << CRF_GT) +#define CRF_CH_OR_CL (1 << CRF_EQ) +#define CRF_CH_AND_CL (1 << CRF_SO) /* XER definitions */ #define XER_SO 31 @@ -1579,11 +1605,6 @@ /*****************************************************************************/ -static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb) -{ - env->nip = tb->pc; -} - static inline void cpu_get_tb_cpu_state(CPUState *env, target_ulong *pc, target_ulong *cs_base, int *flags) { diff -Nru qemu-kvm-0.12.5+noroms/target-ppc/exec.h qemu-kvm-0.14.1/target-ppc/exec.h --- qemu-kvm-0.12.5+noroms/target-ppc/exec.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-ppc/exec.h 2011-05-11 13:29:46.000000000 +0000 @@ -26,23 +26,12 @@ #include "cpu.h" #include "exec-all.h" -/* Precise emulation is needed to correctly emulate exception flags */ -#define USE_PRECISE_EMULATION 1 - register struct CPUPPCState *env asm(AREG0); #if !defined(CONFIG_USER_ONLY) #include "softmmu_exec.h" #endif /* !defined(CONFIG_USER_ONLY) */ -static inline void env_to_regs(void) -{ -} - -static inline void regs_to_env(void) -{ -} - static inline int cpu_has_work(CPUState *env) { return (msr_ee && (env->interrupt_request & CPU_INTERRUPT_HARD)); @@ -60,4 +49,9 @@ return EXCP_HALTED; } +static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb) +{ + env->nip = tb->pc; +} + #endif /* !defined (__PPC_H__) */ diff -Nru qemu-kvm-0.12.5+noroms/target-ppc/helper.c qemu-kvm-0.14.1/target-ppc/helper.c --- qemu-kvm-0.12.5+noroms/target-ppc/helper.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-ppc/helper.c 2011-05-11 13:29:46.000000000 +0000 @@ -98,11 +98,6 @@ return 1; } -target_phys_addr_t cpu_get_phys_page_debug (CPUState *env, target_ulong addr) -{ - return addr; -} - #else /* Common routines used by software and hardware TLBs emulation */ static inline int pte_is_valid(target_ulong pte0) @@ -204,7 +199,6 @@ target_ulong ptem, mmask; int access, ret, pteh, ptev, pp; - access = 0; ret = -1; /* Check validity and table match */ #if defined(TARGET_PPC64) @@ -499,7 +493,7 @@ int rw, int type) { target_ulong *BATlt, *BATut, *BATu, *BATl; - target_ulong base, BEPIl, BEPIu, bl; + target_ulong BEPIl, BEPIu, bl; int i, valid, prot; int ret = -1; @@ -515,7 +509,6 @@ BATut = env->DBAT[0]; break; } - base = virtual & 0xFFFC0000; for (i = 0; i < env->nb_BATs; i++) { BATu = &BATut[i]; BATl = &BATlt[i]; @@ -737,14 +730,13 @@ PRIx32 "\n", __func__, n, slb->tmp64, slb->tmp); if (slb_is_valid(slb)) { /* SLB entry is valid */ + mask = 0xFFFFFFFFF0000000ULL; if (slb->tmp & 0x8) { - /* 1 TB Segment */ - mask = 0xFFFF000000000000ULL; + /* 16 MB PTEs */ if (target_page_bits) - *target_page_bits = 24; // XXX 16M pages? + *target_page_bits = 24; } else { - /* 256MB Segment */ - mask = 0xFFFFFFFFF0000000ULL; + /* 4 KB PTEs */ if (target_page_bits) *target_page_bits = TARGET_PAGE_BITS; } @@ -1059,7 +1051,6 @@ /* Check valid flag */ if (!(tlb->prot & PAGE_VALID)) { - qemu_log("%s: TLB %d not valid\n", __func__, i); return -1; } mask = ~(tlb->size - 1); @@ -1156,7 +1147,7 @@ env->spr[SPR_40x_PID], 0, i) < 0) continue; zsel = (tlb->attr >> 4) & 0xF; - zpr = (env->spr[SPR_40x_ZPR] >> (28 - (2 * zsel))) & 0x3; + zpr = (env->spr[SPR_40x_ZPR] >> (30 - (2 * zsel))) & 0x3; LOG_SWTLB("%s: TLB %d zsel %d zpr %d rw %d attr %08x\n", __func__, i, zsel, zpr, rw, tlb->attr); /* Check execute enable bit */ @@ -1172,6 +1163,8 @@ break; case 0x0: if (pr != 0) { + /* Raise Zone protection fault. */ + env->spr[SPR_40x_ESR] = 1 << 22; ctx->prot = 0; ret = -2; break; @@ -1180,10 +1173,10 @@ case 0x1: check_perms: /* Check from TLB entry */ - /* XXX: there is a problem here or in the TLB fill code... */ ctx->prot = tlb->prot; - ctx->prot |= PAGE_EXEC; ret = check_prot(ctx->prot, rw, access_type); + if (ret == -2) + env->spr[SPR_40x_ESR] = 0; break; } if (ret >= 0) { @@ -1331,8 +1324,15 @@ #endif if ((access_type == ACCESS_CODE && msr_ir == 0) || (access_type != ACCESS_CODE && msr_dr == 0)) { - /* No address translation */ - ret = check_physical(env, ctx, eaddr, rw); + if (env->mmu_model == POWERPC_MMU_BOOKE) { + /* The BookE MMU always performs address translation. The + IS and DS bits only affect the address space. */ + ret = mmubooke_get_physical_address(env, ctx, eaddr, + rw, access_type); + } else { + /* No address translation. */ + ret = check_physical(env, ctx, eaddr, rw); + } } else { ret = -1; switch (env->mmu_model) { @@ -1413,9 +1413,10 @@ } ret = get_physical_address(env, &ctx, address, rw, access_type); if (ret == 0) { - ret = tlb_set_page_exec(env, address & TARGET_PAGE_MASK, - ctx.raddr & TARGET_PAGE_MASK, ctx.prot, - mmu_idx, is_softmmu); + tlb_set_page(env, address & TARGET_PAGE_MASK, + ctx.raddr & TARGET_PAGE_MASK, ctx.prot, + mmu_idx, TARGET_PAGE_SIZE); + ret = 0; } else if (ret < 0) { LOG_MMU_STATE(env); if (access_type == ACCESS_CODE) { @@ -1449,8 +1450,9 @@ env->error_code = 0x40000000; break; case POWERPC_MMU_BOOKE: - /* XXX: TODO */ - cpu_abort(env, "BookE MMU model is not implemented\n"); + env->exception_index = POWERPC_EXCP_ITLB; + env->error_code = 0; + env->spr[SPR_BOOKE_DEAR] = address; return -1; case POWERPC_MMU_BOOKE_FSL: /* XXX: TODO */ @@ -1476,6 +1478,9 @@ break; case -3: /* No execute protection violation */ + if (env->mmu_model == POWERPC_MMU_BOOKE) { + env->spr[SPR_BOOKE_ESR] = 0x00000000; + } env->exception_index = POWERPC_EXCP_ISI; env->error_code = 0x10000000; break; @@ -1561,8 +1566,10 @@ cpu_abort(env, "MPC8xx MMU model is not implemented\n"); break; case POWERPC_MMU_BOOKE: - /* XXX: TODO */ - cpu_abort(env, "BookE MMU model is not implemented\n"); + env->exception_index = POWERPC_EXCP_DTLB; + env->error_code = 0; + env->spr[SPR_BOOKE_DEAR] = address; + env->spr[SPR_BOOKE_ESR] = rw ? 1 << ESR_ST : 0; return -1; case POWERPC_MMU_BOOKE_FSL: /* XXX: TODO */ @@ -1581,11 +1588,23 @@ /* Access rights violation */ env->exception_index = POWERPC_EXCP_DSI; env->error_code = 0; - env->spr[SPR_DAR] = address; - if (rw == 1) - env->spr[SPR_DSISR] = 0x0A000000; - else - env->spr[SPR_DSISR] = 0x08000000; + if (env->mmu_model == POWERPC_MMU_SOFT_4xx + || env->mmu_model == POWERPC_MMU_SOFT_4xx_Z) { + env->spr[SPR_40x_DEAR] = address; + if (rw) { + env->spr[SPR_40x_ESR] |= 0x00800000; + } + } else if (env->mmu_model == POWERPC_MMU_BOOKE) { + env->spr[SPR_BOOKE_DEAR] = address; + env->spr[SPR_BOOKE_ESR] = rw ? 1 << ESR_ST : 0; + } else { + env->spr[SPR_DAR] = address; + if (rw == 1) { + env->spr[SPR_DSISR] = 0x0A000000; + } else { + env->spr[SPR_DSISR] = 0x08000000; + } + } break; case -4: /* Direct store exception */ @@ -1748,11 +1767,15 @@ void ppc_store_ibatu_601 (CPUPPCState *env, int nr, target_ulong value) { target_ulong mask; +#if defined(FLUSH_ALL_TLBS) int do_inval; +#endif dump_store_bat(env, 'I', 0, nr, value); if (env->IBAT[0][nr] != value) { +#if defined(FLUSH_ALL_TLBS) do_inval = 0; +#endif mask = (env->IBAT[1][nr] << 17) & 0x0FFE0000UL; if (env->IBAT[1][nr] & 0x40) { /* Invalidate BAT only if it is valid */ @@ -1785,11 +1808,15 @@ void ppc_store_ibatl_601 (CPUPPCState *env, int nr, target_ulong value) { target_ulong mask; +#if defined(FLUSH_ALL_TLBS) int do_inval; +#endif dump_store_bat(env, 'I', 1, nr, value); if (env->IBAT[1][nr] != value) { +#if defined(FLUSH_ALL_TLBS) do_inval = 0; +#endif if (env->IBAT[1][nr] & 0x40) { #if !defined(FLUSH_ALL_TLBS) mask = (env->IBAT[1][nr] << 17) & 0x0FFE0000UL; @@ -1836,8 +1863,7 @@ cpu_abort(env, "MPC8xx MMU model is not implemented\n"); break; case POWERPC_MMU_BOOKE: - /* XXX: TODO */ - cpu_abort(env, "BookE MMU model is not implemented\n"); + tlb_flush(env, 1); break; case POWERPC_MMU_BOOKE_FSL: /* XXX: TODO */ @@ -2061,18 +2087,24 @@ qemu_log_mask(CPU_LOG_INT, "Raise exception at " TARGET_FMT_lx " => %08x (%02x)\n", env->nip, excp, env->error_code); - msr = env->msr; - new_msr = msr; + + /* new srr1 value excluding must-be-zero bits */ + msr = env->msr & ~0x783f0000ULL; + + /* new interrupt handler msr */ + new_msr = env->msr & ((target_ulong)1 << MSR_ME); + + /* target registers */ srr0 = SPR_SRR0; srr1 = SPR_SRR1; asrr0 = -1; asrr1 = -1; + switch (excp) { case POWERPC_EXCP_NONE: /* Should never happen */ return; case POWERPC_EXCP_CRITICAL: /* Critical input */ - new_msr &= ~((target_ulong)1 << MSR_RI); /* XXX: check this */ switch (excp_model) { case POWERPC_EXCP_40x: srr0 = SPR_40x_SRR2; @@ -2103,12 +2135,14 @@ env->halted = 1; env->interrupt_request |= CPU_INTERRUPT_EXITTB; } - new_msr &= ~((target_ulong)1 << MSR_RI); - new_msr &= ~((target_ulong)1 << MSR_ME); if (0) { /* XXX: find a suitable condition to enable the hypervisor mode */ new_msr |= (target_ulong)MSR_HVB; } + + /* machine check exceptions don't have ME set */ + new_msr &= ~((target_ulong)1 << MSR_ME); + /* XXX: should also have something loaded in DAR / DSISR */ switch (excp_model) { case POWERPC_EXCP_40x: @@ -2128,25 +2162,21 @@ case POWERPC_EXCP_DSI: /* Data storage exception */ LOG_EXCP("DSI exception: DSISR=" TARGET_FMT_lx" DAR=" TARGET_FMT_lx "\n", env->spr[SPR_DSISR], env->spr[SPR_DAR]); - new_msr &= ~((target_ulong)1 << MSR_RI); if (lpes1 == 0) new_msr |= (target_ulong)MSR_HVB; goto store_next; case POWERPC_EXCP_ISI: /* Instruction storage exception */ LOG_EXCP("ISI exception: msr=" TARGET_FMT_lx ", nip=" TARGET_FMT_lx "\n", msr, env->nip); - new_msr &= ~((target_ulong)1 << MSR_RI); if (lpes1 == 0) new_msr |= (target_ulong)MSR_HVB; msr |= env->error_code; goto store_next; case POWERPC_EXCP_EXTERNAL: /* External input */ - new_msr &= ~((target_ulong)1 << MSR_RI); if (lpes0 == 1) new_msr |= (target_ulong)MSR_HVB; goto store_next; case POWERPC_EXCP_ALIGN: /* Alignment exception */ - new_msr &= ~((target_ulong)1 << MSR_RI); if (lpes1 == 0) new_msr |= (target_ulong)MSR_HVB; /* XXX: this is false */ @@ -2162,7 +2192,6 @@ env->error_code = 0; return; } - new_msr &= ~((target_ulong)1 << MSR_RI); if (lpes1 == 0) new_msr |= (target_ulong)MSR_HVB; msr |= 0x00100000; @@ -2172,19 +2201,16 @@ break; case POWERPC_EXCP_INVAL: LOG_EXCP("Invalid instruction at " TARGET_FMT_lx "\n", env->nip); - new_msr &= ~((target_ulong)1 << MSR_RI); if (lpes1 == 0) new_msr |= (target_ulong)MSR_HVB; msr |= 0x00080000; break; case POWERPC_EXCP_PRIV: - new_msr &= ~((target_ulong)1 << MSR_RI); if (lpes1 == 0) new_msr |= (target_ulong)MSR_HVB; msr |= 0x00040000; break; case POWERPC_EXCP_TRAP: - new_msr &= ~((target_ulong)1 << MSR_RI); if (lpes1 == 0) new_msr |= (target_ulong)MSR_HVB; msr |= 0x00020000; @@ -2197,40 +2223,24 @@ } goto store_current; case POWERPC_EXCP_FPU: /* Floating-point unavailable exception */ - new_msr &= ~((target_ulong)1 << MSR_RI); if (lpes1 == 0) new_msr |= (target_ulong)MSR_HVB; goto store_current; case POWERPC_EXCP_SYSCALL: /* System call exception */ - /* NOTE: this is a temporary hack to support graphics OSI - calls from the MOL driver */ - /* XXX: To be removed */ - if (env->gpr[3] == 0x113724fa && env->gpr[4] == 0x77810f9b && - env->osi_call) { - if (env->osi_call(env) != 0) { - env->exception_index = POWERPC_EXCP_NONE; - env->error_code = 0; - return; - } - } dump_syscall(env); - new_msr &= ~((target_ulong)1 << MSR_RI); lev = env->error_code; if (lev == 1 || (lpes0 == 0 && lpes1 == 0)) new_msr |= (target_ulong)MSR_HVB; goto store_next; case POWERPC_EXCP_APU: /* Auxiliary processor unavailable */ - new_msr &= ~((target_ulong)1 << MSR_RI); goto store_current; case POWERPC_EXCP_DECR: /* Decrementer exception */ - new_msr &= ~((target_ulong)1 << MSR_RI); if (lpes1 == 0) new_msr |= (target_ulong)MSR_HVB; goto store_next; case POWERPC_EXCP_FIT: /* Fixed-interval timer interrupt */ /* FIT on 4xx */ LOG_EXCP("FIT exception\n"); - new_msr &= ~((target_ulong)1 << MSR_RI); /* XXX: check this */ goto store_next; case POWERPC_EXCP_WDT: /* Watchdog timer interrupt */ LOG_EXCP("WDT exception\n"); @@ -2242,13 +2252,10 @@ default: break; } - new_msr &= ~((target_ulong)1 << MSR_RI); /* XXX: check this */ goto store_next; case POWERPC_EXCP_DTLB: /* Data TLB error */ - new_msr &= ~((target_ulong)1 << MSR_RI); /* XXX: check this */ goto store_next; case POWERPC_EXCP_ITLB: /* Instruction TLB error */ - new_msr &= ~((target_ulong)1 << MSR_RI); /* XXX: check this */ goto store_next; case POWERPC_EXCP_DEBUG: /* Debug interrupt */ switch (excp_model) { @@ -2265,7 +2272,6 @@ cpu_abort(env, "Debug exception is not implemented yet !\n"); goto store_next; case POWERPC_EXCP_SPEU: /* SPE/embedded floating-point unavailable */ - new_msr &= ~((target_ulong)1 << MSR_RI); /* XXX: check this */ goto store_current; case POWERPC_EXCP_EFPDI: /* Embedded floating-point data interrupt */ /* XXX: TODO */ @@ -2278,7 +2284,6 @@ "is not implemented yet !\n"); goto store_next; case POWERPC_EXCP_EPERFM: /* Embedded performance monitor interrupt */ - new_msr &= ~((target_ulong)1 << MSR_RI); /* XXX: TODO */ cpu_abort(env, "Performance counter exception is not implemented yet !\n"); @@ -2302,19 +2307,23 @@ "is not implemented yet !\n"); goto store_next; case POWERPC_EXCP_RESET: /* System reset exception */ - new_msr &= ~((target_ulong)1 << MSR_RI); + if (msr_pow) { + /* indicate that we resumed from power save mode */ + msr |= 0x10000; + } else { + new_msr &= ~((target_ulong)1 << MSR_ME); + } + if (0) { /* XXX: find a suitable condition to enable the hypervisor mode */ new_msr |= (target_ulong)MSR_HVB; } goto store_next; case POWERPC_EXCP_DSEG: /* Data segment exception */ - new_msr &= ~((target_ulong)1 << MSR_RI); if (lpes1 == 0) new_msr |= (target_ulong)MSR_HVB; goto store_next; case POWERPC_EXCP_ISEG: /* Instruction segment exception */ - new_msr &= ~((target_ulong)1 << MSR_RI); if (lpes1 == 0) new_msr |= (target_ulong)MSR_HVB; goto store_next; @@ -2322,9 +2331,9 @@ srr0 = SPR_HSRR0; srr1 = SPR_HSRR1; new_msr |= (target_ulong)MSR_HVB; + new_msr |= env->msr & ((target_ulong)1 << MSR_RI); goto store_next; case POWERPC_EXCP_TRACE: /* Trace exception */ - new_msr &= ~((target_ulong)1 << MSR_RI); if (lpes1 == 0) new_msr |= (target_ulong)MSR_HVB; goto store_next; @@ -2332,30 +2341,32 @@ srr0 = SPR_HSRR0; srr1 = SPR_HSRR1; new_msr |= (target_ulong)MSR_HVB; + new_msr |= env->msr & ((target_ulong)1 << MSR_RI); goto store_next; case POWERPC_EXCP_HISI: /* Hypervisor instruction storage exception */ srr0 = SPR_HSRR0; srr1 = SPR_HSRR1; new_msr |= (target_ulong)MSR_HVB; + new_msr |= env->msr & ((target_ulong)1 << MSR_RI); goto store_next; case POWERPC_EXCP_HDSEG: /* Hypervisor data segment exception */ srr0 = SPR_HSRR0; srr1 = SPR_HSRR1; new_msr |= (target_ulong)MSR_HVB; + new_msr |= env->msr & ((target_ulong)1 << MSR_RI); goto store_next; case POWERPC_EXCP_HISEG: /* Hypervisor instruction segment exception */ srr0 = SPR_HSRR0; srr1 = SPR_HSRR1; new_msr |= (target_ulong)MSR_HVB; + new_msr |= env->msr & ((target_ulong)1 << MSR_RI); goto store_next; case POWERPC_EXCP_VPU: /* Vector unavailable exception */ - new_msr &= ~((target_ulong)1 << MSR_RI); if (lpes1 == 0) new_msr |= (target_ulong)MSR_HVB; goto store_current; case POWERPC_EXCP_PIT: /* Programmable interval timer interrupt */ LOG_EXCP("PIT exception\n"); - new_msr &= ~((target_ulong)1 << MSR_RI); /* XXX: check this */ goto store_next; case POWERPC_EXCP_IO: /* IO error exception */ /* XXX: TODO */ @@ -2371,7 +2382,6 @@ "is not implemented yet !\n"); goto store_next; case POWERPC_EXCP_IFTLB: /* Instruction fetch TLB error */ - new_msr &= ~((target_ulong)1 << MSR_RI); /* XXX: check this */ if (lpes1 == 0) /* XXX: check this */ new_msr |= (target_ulong)MSR_HVB; switch (excp_model) { @@ -2390,7 +2400,6 @@ } break; case POWERPC_EXCP_DLTLB: /* Data load TLB miss */ - new_msr &= ~((target_ulong)1 << MSR_RI); /* XXX: check this */ if (lpes1 == 0) /* XXX: check this */ new_msr |= (target_ulong)MSR_HVB; switch (excp_model) { @@ -2409,7 +2418,6 @@ } break; case POWERPC_EXCP_DSTLB: /* Data store TLB miss */ - new_msr &= ~((target_ulong)1 << MSR_RI); /* XXX: check this */ if (lpes1 == 0) /* XXX: check this */ new_msr |= (target_ulong)MSR_HVB; switch (excp_model) { @@ -2513,7 +2521,6 @@ "is not implemented yet !\n"); goto store_next; case POWERPC_EXCP_PERFM: /* Embedded performance monitor interrupt */ - new_msr &= ~((target_ulong)1 << MSR_RI); if (lpes1 == 0) new_msr |= (target_ulong)MSR_HVB; /* XXX: TODO */ @@ -2567,24 +2574,11 @@ /* If we disactivated any translation, flush TLBs */ if (new_msr & ((1 << MSR_IR) | (1 << MSR_DR))) tlb_flush(env, 1); - /* reload MSR with correct bits */ - new_msr &= ~((target_ulong)1 << MSR_EE); - new_msr &= ~((target_ulong)1 << MSR_PR); - new_msr &= ~((target_ulong)1 << MSR_FP); - new_msr &= ~((target_ulong)1 << MSR_FE0); - new_msr &= ~((target_ulong)1 << MSR_SE); - new_msr &= ~((target_ulong)1 << MSR_BE); - new_msr &= ~((target_ulong)1 << MSR_FE1); - new_msr &= ~((target_ulong)1 << MSR_IR); - new_msr &= ~((target_ulong)1 << MSR_DR); -#if 0 /* Fix this: not on all targets */ - new_msr &= ~((target_ulong)1 << MSR_PMM); -#endif - new_msr &= ~((target_ulong)1 << MSR_LE); - if (msr_ile) + + if (msr_ile) { new_msr |= (target_ulong)1 << MSR_LE; - else - new_msr &= ~((target_ulong)1 << MSR_LE); + } + /* Jump to handler */ vector = env->excp_vectors[excp]; if (vector == (target_ulong)-1ULL) { @@ -2595,14 +2589,12 @@ #if defined(TARGET_PPC64) if (excp_model == POWERPC_EXCP_BOOKE) { if (!msr_icm) { - new_msr &= ~((target_ulong)1 << MSR_CM); vector = (uint32_t)vector; } else { new_msr |= (target_ulong)1 << MSR_CM; } } else { if (!msr_isf && !(env->mmu_model & POWERPC_MMU_64)) { - new_msr &= ~((target_ulong)1 << MSR_SF); vector = (uint32_t)vector; } else { new_msr |= (target_ulong)1 << MSR_SF; @@ -2618,6 +2610,13 @@ /* Reset exception state */ env->exception_index = POWERPC_EXCP_NONE; env->error_code = 0; + + if (env->mmu_model == POWERPC_MMU_BOOKE) { + /* XXX: The BookE changes address space when switching modes, + we should probably implement that as different MMU indexes, + but for the moment we do it the slow way and flush all. */ + tlb_flush(env, 1); + } } void do_interrupt (CPUState *env) diff -Nru qemu-kvm-0.12.5+noroms/target-ppc/helper.h qemu-kvm-0.14.1/target-ppc/helper.h --- qemu-kvm-0.12.5+noroms/target-ppc/helper.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-ppc/helper.h 2011-05-11 13:29:46.000000000 +0000 @@ -95,6 +95,7 @@ #define dh_alias_avr ptr #define dh_ctype_avr ppc_avr_t * +#define dh_is_signed_avr dh_is_signed_ptr DEF_HELPER_3(vaddubm, void, avr, avr, avr) DEF_HELPER_3(vadduhm, void, avr, avr, avr) @@ -245,6 +246,7 @@ DEF_HELPER_2(vrsqrtefp, void, avr, avr) DEF_HELPER_4(vmaddfp, void, avr, avr, avr, avr) DEF_HELPER_4(vnmsubfp, void, avr, avr, avr, avr) +DEF_HELPER_2(vexptefp, void, avr, avr) DEF_HELPER_2(vlogefp, void, avr, avr) DEF_HELPER_2(vrfim, void, avr, avr) DEF_HELPER_2(vrfin, void, avr, avr) diff -Nru qemu-kvm-0.12.5+noroms/target-ppc/kvm.c qemu-kvm-0.14.1/target-ppc/kvm.c --- qemu-kvm-0.12.5+noroms/target-ppc/kvm.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-ppc/kvm.c 2011-05-11 13:29:46.000000000 +0000 @@ -37,8 +37,43 @@ do { } while (0) #endif -int kvm_arch_init(KVMState *s, int smp_cpus) +const KVMCapabilityInfo kvm_arch_required_capabilities[] = { + KVM_CAP_LAST_INFO +}; + +static int cap_interrupt_unset = false; +static int cap_interrupt_level = false; + +/* XXX We have a race condition where we actually have a level triggered + * interrupt, but the infrastructure can't expose that yet, so the guest + * takes but ignores it, goes to sleep and never gets notified that there's + * still an interrupt pending. + * + * As a quick workaround, let's just wake up again 20 ms after we injected + * an interrupt. That way we can assure that we're always reinjecting + * interrupts in case the guest swallowed them. + */ +static QEMUTimer *idle_timer; + +static void kvm_kick_env(void *env) +{ + qemu_cpu_kick(env); +} + +int kvm_arch_init(KVMState *s) { +#ifdef KVM_CAP_PPC_UNSET_IRQ + cap_interrupt_unset = kvm_check_extension(s, KVM_CAP_PPC_UNSET_IRQ); +#endif +#ifdef KVM_CAP_PPC_IRQ_LEVEL + cap_interrupt_level = kvm_check_extension(s, KVM_CAP_PPC_IRQ_LEVEL); +#endif + + if (!cap_interrupt_level) { + fprintf(stderr, "KVM: Couldn't find level irq capability. Expect the " + "VM to stall at times!\n"); + } + return 0; } @@ -50,6 +85,8 @@ sregs.pvr = cenv->spr[SPR_PVR]; ret = kvm_vcpu_ioctl(cenv, KVM_SET_SREGS, &sregs); + idle_timer = qemu_new_timer(vm_clock, kvm_kick_env, cenv); + return ret; } @@ -57,7 +94,7 @@ { } -int kvm_arch_put_registers(CPUState *env) +int kvm_arch_put_registers(CPUState *env, int level) { struct kvm_regs regs; int ret; @@ -99,7 +136,7 @@ { struct kvm_regs regs; struct kvm_sregs sregs; - uint32_t i, ret; + int i, ret; ret = kvm_vcpu_ioctl(env, KVM_GET_REGS, ®s); if (ret < 0) @@ -160,6 +197,23 @@ return 0; } +int kvmppc_set_interrupt(CPUState *env, int irq, int level) +{ + unsigned virq = level ? KVM_INTERRUPT_SET_LEVEL : KVM_INTERRUPT_UNSET; + + if (irq != PPC_INTERRUPT_EXT) { + return 0; + } + + if (!kvm_enabled() || !cap_interrupt_unset || !cap_interrupt_level) { + return 0; + } + + kvm_vcpu_ioctl(env, KVM_INTERRUPT, &virq); + + return 0; +} + #if defined(TARGET_PPCEMB) #define PPC_INPUT_INT PPC40x_INPUT_INT #elif defined(TARGET_PPC64) @@ -175,7 +229,8 @@ /* PowerPC Qemu tracks the various core input pins (interrupt, critical * interrupt, reset, etc) in PPC-specific env->irq_input_state. */ - if (run->ready_for_interrupt_injection && + if (!cap_interrupt_level && + run->ready_for_interrupt_injection && (env->interrupt_request & CPU_INTERRUPT_HARD) && (env->irq_input_state & (1<cpu_index, irq); + + /* Always wake up soon in case the interrupt was level based */ + qemu_mod_timer(idle_timer, qemu_get_clock(vm_clock) + + (get_ticks_per_sec() / 50)); } /* We don't know if there are more interrupts pending after this. However, @@ -202,6 +261,11 @@ return 0; } +int kvm_arch_process_irqchip_events(CPUState *env) +{ + return 0; +} + static int kvmppc_handle_halt(CPUState *env) { if (!(env->interrupt_request & CPU_INTERRUPT_HARD) && (msr_ee)) { @@ -247,8 +311,96 @@ dprintf("handle halt\n"); ret = kvmppc_handle_halt(env); break; + default: + fprintf(stderr, "KVM: unknown exit reason %d\n", run->exit_reason); + ret = -1; + break; + } + + return ret; +} + +static int read_cpuinfo(const char *field, char *value, int len) +{ + FILE *f; + int ret = -1; + int field_len = strlen(field); + char line[512]; + + f = fopen("/proc/cpuinfo", "r"); + if (!f) { + return -1; } + do { + if(!fgets(line, sizeof(line), f)) { + break; + } + if (!strncmp(line, field, field_len)) { + strncpy(value, line, len); + ret = 0; + break; + } + } while(*line); + + fclose(f); + return ret; } +uint32_t kvmppc_get_tbfreq(void) +{ + char line[512]; + char *ns; + uint32_t retval = get_ticks_per_sec(); + + if (read_cpuinfo("timebase", line, sizeof(line))) { + return retval; + } + + if (!(ns = strchr(line, ':'))) { + return retval; + } + + ns++; + + retval = atoi(ns); + return retval; +} + +int kvmppc_get_hypercall(CPUState *env, uint8_t *buf, int buf_len) +{ + uint32_t *hc = (uint32_t*)buf; + +#ifdef KVM_CAP_PPC_GET_PVINFO + struct kvm_ppc_pvinfo pvinfo; + + if (kvm_check_extension(env->kvm_state, KVM_CAP_PPC_GET_PVINFO) && + !kvm_vm_ioctl(env->kvm_state, KVM_PPC_GET_PVINFO, &pvinfo)) { + memcpy(buf, pvinfo.hcall, buf_len); + + return 0; + } +#endif + + /* + * Fallback to always fail hypercalls: + * + * li r3, -1 + * nop + * nop + * nop + */ + + hc[0] = 0x3860ffff; + hc[1] = 0x60000000; + hc[2] = 0x60000000; + hc[3] = 0x60000000; + + return 0; +} + +bool kvm_arch_stop_on_emulation_error(CPUState *env) +{ + return true; +} diff -Nru qemu-kvm-0.12.5+noroms/target-ppc/kvm_ppc.h qemu-kvm-0.14.1/target-ppc/kvm_ppc.h --- qemu-kvm-0.12.5+noroms/target-ppc/kvm_ppc.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-ppc/kvm_ppc.h 2011-05-11 13:29:46.000000000 +0000 @@ -14,4 +14,20 @@ int kvmppc_read_host_property(const char *node_path, const char *prop, void *val, size_t len); +uint32_t kvmppc_get_tbfreq(void); +int kvmppc_get_hypercall(CPUState *env, uint8_t *buf, int buf_len); +int kvmppc_set_interrupt(CPUState *env, int irq, int level); + +#ifndef KVM_INTERRUPT_SET +#define KVM_INTERRUPT_SET -1 +#endif + +#ifndef KVM_INTERRUPT_UNSET +#define KVM_INTERRUPT_UNSET -2 +#endif + +#ifndef KVM_INTERRUPT_SET_LEVEL +#define KVM_INTERRUPT_SET_LEVEL -3 +#endif + #endif /* __KVM_PPC_H__ */ diff -Nru qemu-kvm-0.12.5+noroms/target-ppc/machine.c qemu-kvm-0.14.1/target-ppc/machine.c --- qemu-kvm-0.12.5+noroms/target-ppc/machine.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-ppc/machine.c 2011-05-11 13:29:46.000000000 +0000 @@ -8,8 +8,6 @@ CPUState *env = (CPUState *)opaque; unsigned int i, j; - cpu_synchronize_state(env); - for (i = 0; i < 32; i++) qemu_put_betls(f, &env->gpr[i]); #if !defined(TARGET_PPC64) @@ -97,8 +95,6 @@ CPUState *env = (CPUState *)opaque; unsigned int i, j; - cpu_synchronize_state(env); - for (i = 0; i < 32; i++) qemu_get_betls(f, &env->gpr[i]); #if !defined(TARGET_PPC64) diff -Nru qemu-kvm-0.12.5+noroms/target-ppc/op_helper.c qemu-kvm-0.14.1/target-ppc/op_helper.c --- qemu-kvm-0.12.5+noroms/target-ppc/op_helper.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-ppc/op_helper.c 2011-05-11 13:29:46.000000000 +0000 @@ -68,7 +68,7 @@ target_ulong helper_load_tbl (void) { - return cpu_ppc_load_tbl(env); + return (target_ulong)cpu_ppc_load_tbl(env); } target_ulong helper_load_tbu (void) @@ -78,7 +78,7 @@ target_ulong helper_load_atbl (void) { - return cpu_ppc_load_atbl(env); + return (target_ulong)cpu_ppc_load_atbl(env); } target_ulong helper_load_atbu (void) @@ -348,15 +348,13 @@ void helper_icbi(target_ulong addr) { - uint32_t tmp; - addr &= ~(env->dcache_line_size - 1); /* Invalidate one cache line : * PowerPC specification says this is to be treated like a load * (not a fetch) by the MMU. To be sure it will be so, * do the load "by hand". */ - tmp = ldl(addr); + ldl(addr); tb_invalidate_page_range(addr, addr + env->icache_line_size); } @@ -548,7 +546,7 @@ int ret; farg.ll = arg; isneg = float64_is_neg(farg.d); - if (unlikely(float64_is_nan(farg.d))) { + if (unlikely(float64_is_any_nan(farg.d))) { if (float64_is_signaling_nan(farg.d)) { /* Signaling NaN: flags are undefined */ ret = 0x00; @@ -645,7 +643,7 @@ env->fpscr &= ~((1 << FPSCR_FR) | (1 << FPSCR_FI)); if (ve == 0) { /* Set the result to quiet NaN */ - ret = 0xFFF8000000000000ULL; + ret = 0x7FF8000000000000ULL; env->fpscr &= ~(0xF << FPSCR_FPCC); env->fpscr |= 0x11 << FPSCR_FPCC; } @@ -656,7 +654,7 @@ env->fpscr &= ~((1 << FPSCR_FR) | (1 << FPSCR_FI)); if (ve == 0) { /* Set the result to quiet NaN */ - ret = 0xFFF8000000000000ULL; + ret = 0x7FF8000000000000ULL; env->fpscr &= ~(0xF << FPSCR_FPCC); env->fpscr |= 0x11 << FPSCR_FPCC; } @@ -976,21 +974,20 @@ farg1.ll = arg1; farg2.ll = arg2; -#if USE_PRECISE_EMULATION - if (unlikely(float64_is_signaling_nan(farg1.d) || - float64_is_signaling_nan(farg2.d))) { - /* sNaN addition */ - farg1.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); - } else if (unlikely(float64_is_infinity(farg1.d) && float64_is_infinity(farg2.d) && - float64_is_neg(farg1.d) != float64_is_neg(farg2.d))) { + + if (unlikely(float64_is_infinity(farg1.d) && float64_is_infinity(farg2.d) && + float64_is_neg(farg1.d) != float64_is_neg(farg2.d))) { /* Magnitude subtraction of infinities */ farg1.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXISI); } else { + if (unlikely(float64_is_signaling_nan(farg1.d) || + float64_is_signaling_nan(farg2.d))) { + /* sNaN addition */ + fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); + } farg1.d = float64_add(farg1.d, farg2.d, &env->fp_status); } -#else - farg1.d = float64_add(farg1.d, farg2.d, &env->fp_status); -#endif + return farg1.ll; } @@ -1001,23 +998,20 @@ farg1.ll = arg1; farg2.ll = arg2; -#if USE_PRECISE_EMULATION -{ - if (unlikely(float64_is_signaling_nan(farg1.d) || - float64_is_signaling_nan(farg2.d))) { - /* sNaN subtraction */ - farg1.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); - } else if (unlikely(float64_is_infinity(farg1.d) && float64_is_infinity(farg2.d) && - float64_is_neg(farg1.d) == float64_is_neg(farg2.d))) { + + if (unlikely(float64_is_infinity(farg1.d) && float64_is_infinity(farg2.d) && + float64_is_neg(farg1.d) == float64_is_neg(farg2.d))) { /* Magnitude subtraction of infinities */ farg1.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXISI); } else { + if (unlikely(float64_is_signaling_nan(farg1.d) || + float64_is_signaling_nan(farg2.d))) { + /* sNaN subtraction */ + fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); + } farg1.d = float64_sub(farg1.d, farg2.d, &env->fp_status); } -} -#else - farg1.d = float64_sub(farg1.d, farg2.d, &env->fp_status); -#endif + return farg1.ll; } @@ -1028,21 +1022,20 @@ farg1.ll = arg1; farg2.ll = arg2; -#if USE_PRECISE_EMULATION - if (unlikely(float64_is_signaling_nan(farg1.d) || - float64_is_signaling_nan(farg2.d))) { - /* sNaN multiplication */ - farg1.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); - } else if (unlikely((float64_is_infinity(farg1.d) && float64_is_zero(farg2.d)) || - (float64_is_zero(farg1.d) && float64_is_infinity(farg2.d)))) { + + if (unlikely((float64_is_infinity(farg1.d) && float64_is_zero(farg2.d)) || + (float64_is_zero(farg1.d) && float64_is_infinity(farg2.d)))) { /* Multiplication of zero by infinity */ farg1.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXIMZ); } else { + if (unlikely(float64_is_signaling_nan(farg1.d) || + float64_is_signaling_nan(farg2.d))) { + /* sNaN multiplication */ + fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); + } farg1.d = float64_mul(farg1.d, farg2.d, &env->fp_status); } -#else - farg1.d = float64_mul(farg1.d, farg2.d, &env->fp_status); -#endif + return farg1.ll; } @@ -1053,23 +1046,22 @@ farg1.ll = arg1; farg2.ll = arg2; -#if USE_PRECISE_EMULATION - if (unlikely(float64_is_signaling_nan(farg1.d) || - float64_is_signaling_nan(farg2.d))) { - /* sNaN division */ - farg1.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); - } else if (unlikely(float64_is_infinity(farg1.d) && float64_is_infinity(farg2.d))) { + + if (unlikely(float64_is_infinity(farg1.d) && float64_is_infinity(farg2.d))) { /* Division of infinity by infinity */ farg1.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXIDI); } else if (unlikely(float64_is_zero(farg1.d) && float64_is_zero(farg2.d))) { /* Division of zero by zero */ farg1.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXZDZ); } else { + if (unlikely(float64_is_signaling_nan(farg1.d) || + float64_is_signaling_nan(farg2.d))) { + /* sNaN division */ + fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); + } farg1.d = float64_div(farg1.d, farg2.d, &env->fp_status); } -#else - farg1.d = float64_div(farg1.d, farg2.d, &env->fp_status); -#endif + return farg1.ll; } @@ -1113,17 +1105,15 @@ if (unlikely(float64_is_signaling_nan(farg.d))) { /* sNaN conversion */ farg.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN | POWERPC_EXCP_FP_VXCVI); - } else if (unlikely(float64_is_nan(farg.d) || float64_is_infinity(farg.d))) { + } else if (unlikely(float64_is_quiet_nan(farg.d) || float64_is_infinity(farg.d))) { /* qNan / infinity conversion */ farg.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXCVI); } else { farg.ll = float64_to_int32(farg.d, &env->fp_status); -#if USE_PRECISE_EMULATION /* XXX: higher bits are not supposed to be significant. * to make tests easier, return the same as a real PowerPC 750 */ farg.ll |= 0xFFF80000ULL << 32; -#endif } return farg.ll; } @@ -1137,17 +1127,15 @@ if (unlikely(float64_is_signaling_nan(farg.d))) { /* sNaN conversion */ farg.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN | POWERPC_EXCP_FP_VXCVI); - } else if (unlikely(float64_is_nan(farg.d) || float64_is_infinity(farg.d))) { + } else if (unlikely(float64_is_quiet_nan(farg.d) || float64_is_infinity(farg.d))) { /* qNan / infinity conversion */ farg.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXCVI); } else { farg.ll = float64_to_int32_round_to_zero(farg.d, &env->fp_status); -#if USE_PRECISE_EMULATION /* XXX: higher bits are not supposed to be significant. * to make tests easier, return the same as a real PowerPC 750 */ farg.ll |= 0xFFF80000ULL << 32; -#endif } return farg.ll; } @@ -1170,7 +1158,7 @@ if (unlikely(float64_is_signaling_nan(farg.d))) { /* sNaN conversion */ farg.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN | POWERPC_EXCP_FP_VXCVI); - } else if (unlikely(float64_is_nan(farg.d) || float64_is_infinity(farg.d))) { + } else if (unlikely(float64_is_quiet_nan(farg.d) || float64_is_infinity(farg.d))) { /* qNan / infinity conversion */ farg.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXCVI); } else { @@ -1188,7 +1176,7 @@ if (unlikely(float64_is_signaling_nan(farg.d))) { /* sNaN conversion */ farg.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN | POWERPC_EXCP_FP_VXCVI); - } else if (unlikely(float64_is_nan(farg.d) || float64_is_infinity(farg.d))) { + } else if (unlikely(float64_is_quiet_nan(farg.d) || float64_is_infinity(farg.d))) { /* qNan / infinity conversion */ farg.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXCVI); } else { @@ -1207,7 +1195,7 @@ if (unlikely(float64_is_signaling_nan(farg.d))) { /* sNaN round */ farg.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN | POWERPC_EXCP_FP_VXCVI); - } else if (unlikely(float64_is_nan(farg.d) || float64_is_infinity(farg.d))) { + } else if (unlikely(float64_is_quiet_nan(farg.d) || float64_is_infinity(farg.d))) { /* qNan / infinity round */ farg.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXCVI); } else { @@ -1247,17 +1235,18 @@ farg1.ll = arg1; farg2.ll = arg2; farg3.ll = arg3; -#if USE_PRECISE_EMULATION - if (unlikely(float64_is_signaling_nan(farg1.d) || - float64_is_signaling_nan(farg2.d) || - float64_is_signaling_nan(farg3.d))) { - /* sNaN operation */ - farg1.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); - } else if (unlikely((float64_is_infinity(farg1.d) && float64_is_zero(farg2.d)) || - (float64_is_zero(farg1.d) && float64_is_infinity(farg2.d)))) { + + if (unlikely((float64_is_infinity(farg1.d) && float64_is_zero(farg2.d)) || + (float64_is_zero(farg1.d) && float64_is_infinity(farg2.d)))) { /* Multiplication of zero by infinity */ farg1.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXIMZ); } else { + if (unlikely(float64_is_signaling_nan(farg1.d) || + float64_is_signaling_nan(farg2.d) || + float64_is_signaling_nan(farg3.d))) { + /* sNaN operation */ + fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); + } #ifdef FLOAT128 /* This is the way the PowerPC specification defines it */ float128 ft0_128, ft1_128; @@ -1279,10 +1268,7 @@ farg1.d = (farg1.d * farg2.d) + farg3.d; #endif } -#else - farg1.d = float64_mul(farg1.d, farg2.d, &env->fp_status); - farg1.d = float64_add(farg1.d, farg3.d, &env->fp_status); -#endif + return farg1.ll; } @@ -1294,17 +1280,18 @@ farg1.ll = arg1; farg2.ll = arg2; farg3.ll = arg3; -#if USE_PRECISE_EMULATION - if (unlikely(float64_is_signaling_nan(farg1.d) || - float64_is_signaling_nan(farg2.d) || - float64_is_signaling_nan(farg3.d))) { - /* sNaN operation */ - farg1.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); - } else if (unlikely((float64_is_infinity(farg1.d) && float64_is_zero(farg2.d)) || + + if (unlikely((float64_is_infinity(farg1.d) && float64_is_zero(farg2.d)) || (float64_is_zero(farg1.d) && float64_is_infinity(farg2.d)))) { /* Multiplication of zero by infinity */ farg1.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXIMZ); } else { + if (unlikely(float64_is_signaling_nan(farg1.d) || + float64_is_signaling_nan(farg2.d) || + float64_is_signaling_nan(farg3.d))) { + /* sNaN operation */ + fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); + } #ifdef FLOAT128 /* This is the way the PowerPC specification defines it */ float128 ft0_128, ft1_128; @@ -1326,10 +1313,6 @@ farg1.d = (farg1.d * farg2.d) - farg3.d; #endif } -#else - farg1.d = float64_mul(farg1.d, farg2.d, &env->fp_status); - farg1.d = float64_sub(farg1.d, farg3.d, &env->fp_status); -#endif return farg1.ll; } @@ -1342,17 +1325,17 @@ farg2.ll = arg2; farg3.ll = arg3; - if (unlikely(float64_is_signaling_nan(farg1.d) || - float64_is_signaling_nan(farg2.d) || - float64_is_signaling_nan(farg3.d))) { - /* sNaN operation */ - farg1.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); - } else if (unlikely((float64_is_infinity(farg1.d) && float64_is_zero(farg2.d)) || - (float64_is_zero(farg1.d) && float64_is_infinity(farg2.d)))) { + if (unlikely((float64_is_infinity(farg1.d) && float64_is_zero(farg2.d)) || + (float64_is_zero(farg1.d) && float64_is_infinity(farg2.d)))) { /* Multiplication of zero by infinity */ farg1.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXIMZ); } else { -#if USE_PRECISE_EMULATION + if (unlikely(float64_is_signaling_nan(farg1.d) || + float64_is_signaling_nan(farg2.d) || + float64_is_signaling_nan(farg3.d))) { + /* sNaN operation */ + fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); + } #ifdef FLOAT128 /* This is the way the PowerPC specification defines it */ float128 ft0_128, ft1_128; @@ -1373,12 +1356,9 @@ /* This is OK on x86 hosts */ farg1.d = (farg1.d * farg2.d) + farg3.d; #endif -#else - farg1.d = float64_mul(farg1.d, farg2.d, &env->fp_status); - farg1.d = float64_add(farg1.d, farg3.d, &env->fp_status); -#endif - if (likely(!float64_is_nan(farg1.d))) + if (likely(!float64_is_any_nan(farg1.d))) { farg1.d = float64_chs(farg1.d); + } } return farg1.ll; } @@ -1392,17 +1372,17 @@ farg2.ll = arg2; farg3.ll = arg3; - if (unlikely(float64_is_signaling_nan(farg1.d) || - float64_is_signaling_nan(farg2.d) || - float64_is_signaling_nan(farg3.d))) { - /* sNaN operation */ - farg1.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); - } else if (unlikely((float64_is_infinity(farg1.d) && float64_is_zero(farg2.d)) || + if (unlikely((float64_is_infinity(farg1.d) && float64_is_zero(farg2.d)) || (float64_is_zero(farg1.d) && float64_is_infinity(farg2.d)))) { /* Multiplication of zero by infinity */ farg1.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXIMZ); } else { -#if USE_PRECISE_EMULATION + if (unlikely(float64_is_signaling_nan(farg1.d) || + float64_is_signaling_nan(farg2.d) || + float64_is_signaling_nan(farg3.d))) { + /* sNaN operation */ + fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); + } #ifdef FLOAT128 /* This is the way the PowerPC specification defines it */ float128 ft0_128, ft1_128; @@ -1423,12 +1403,9 @@ /* This is OK on x86 hosts */ farg1.d = (farg1.d * farg2.d) - farg3.d; #endif -#else - farg1.d = float64_mul(farg1.d, farg2.d, &env->fp_status); - farg1.d = float64_sub(farg1.d, farg3.d, &env->fp_status); -#endif - if (likely(!float64_is_nan(farg1.d))) + if (likely(!float64_is_any_nan(farg1.d))) { farg1.d = float64_chs(farg1.d); + } } return farg1.ll; } @@ -1440,18 +1417,13 @@ float32 f32; farg.ll = arg; -#if USE_PRECISE_EMULATION if (unlikely(float64_is_signaling_nan(farg.d))) { /* sNaN square root */ - farg.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); - } else { - f32 = float64_to_float32(farg.d, &env->fp_status); - farg.d = float32_to_float64(f32, &env->fp_status); + fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); } -#else f32 = float64_to_float32(farg.d, &env->fp_status); farg.d = float32_to_float64(f32, &env->fp_status); -#endif + return farg.ll; } @@ -1461,13 +1433,14 @@ CPU_DoubleU farg; farg.ll = arg; - if (unlikely(float64_is_signaling_nan(farg.d))) { - /* sNaN square root */ - farg.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); - } else if (unlikely(float64_is_neg(farg.d) && !float64_is_zero(farg.d))) { + if (unlikely(float64_is_neg(farg.d) && !float64_is_zero(farg.d))) { /* Square root of a negative nonzero number */ farg.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXSQRT); } else { + if (unlikely(float64_is_signaling_nan(farg.d))) { + /* sNaN square root */ + fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); + } farg.d = float64_sqrt(farg.d, &env->fp_status); } return farg.ll; @@ -1481,10 +1454,9 @@ if (unlikely(float64_is_signaling_nan(farg.d))) { /* sNaN reciprocal */ - farg.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); - } else { - farg.d = float64_div(float64_one, farg.d, &env->fp_status); + fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); } + farg.d = float64_div(float64_one, farg.d, &env->fp_status); return farg.d; } @@ -1497,12 +1469,12 @@ if (unlikely(float64_is_signaling_nan(farg.d))) { /* sNaN reciprocal */ - farg.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); - } else { - farg.d = float64_div(float64_one, farg.d, &env->fp_status); - f32 = float64_to_float32(farg.d, &env->fp_status); - farg.d = float32_to_float64(f32, &env->fp_status); + fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); } + farg.d = float64_div(float64_one, farg.d, &env->fp_status); + f32 = float64_to_float32(farg.d, &env->fp_status); + farg.d = float32_to_float64(f32, &env->fp_status); + return farg.ll; } @@ -1513,13 +1485,14 @@ float32 f32; farg.ll = arg; - if (unlikely(float64_is_signaling_nan(farg.d))) { - /* sNaN reciprocal square root */ - farg.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); - } else if (unlikely(float64_is_neg(farg.d) && !float64_is_zero(farg.d))) { + if (unlikely(float64_is_neg(farg.d) && !float64_is_zero(farg.d))) { /* Reciprocal square root of a negative nonzero number */ farg.ll = fload_invalid_op_excp(POWERPC_EXCP_FP_VXSQRT); } else { + if (unlikely(float64_is_signaling_nan(farg.d))) { + /* sNaN reciprocal square root */ + fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN); + } farg.d = float64_sqrt(farg.d, &env->fp_status); farg.d = float64_div(float64_one, farg.d, &env->fp_status); f32 = float64_to_float32(farg.d, &env->fp_status); @@ -1535,10 +1508,11 @@ farg1.ll = arg1; - if ((!float64_is_neg(farg1.d) || float64_is_zero(farg1.d)) && !float64_is_nan(farg1.d)) + if ((!float64_is_neg(farg1.d) || float64_is_zero(farg1.d)) && !float64_is_any_nan(farg1.d)) { return arg2; - else + } else { return arg3; + } } void helper_fcmpu (uint64_t arg1, uint64_t arg2, uint32_t crfD) @@ -1548,8 +1522,8 @@ farg1.ll = arg1; farg2.ll = arg2; - if (unlikely(float64_is_nan(farg1.d) || - float64_is_nan(farg2.d))) { + if (unlikely(float64_is_any_nan(farg1.d) || + float64_is_any_nan(farg2.d))) { ret = 0x01UL; } else if (float64_lt(farg1.d, farg2.d, &env->fp_status)) { ret = 0x08UL; @@ -1577,8 +1551,8 @@ farg1.ll = arg1; farg2.ll = arg2; - if (unlikely(float64_is_nan(farg1.d) || - float64_is_nan(farg2.d))) { + if (unlikely(float64_is_any_nan(farg1.d) || + float64_is_any_nan(farg2.d))) { ret = 0x01UL; } else if (float64_lt(farg1.d, farg2.d, &env->fp_status)) { ret = 0x08UL; @@ -1830,14 +1804,14 @@ /* XXX: to be improved to check access rights when in user-mode */ target_ulong helper_load_dcr (target_ulong dcrn) { - target_ulong val = 0; + uint32_t val = 0; if (unlikely(env->dcr_env == NULL)) { qemu_log("No DCR environment\n"); helper_raise_exception_err(POWERPC_EXCP_PROGRAM, POWERPC_EXCP_INVAL | POWERPC_EXCP_INVAL_INVAL); - } else if (unlikely(ppc_dcr_read(env->dcr_env, dcrn, &val) != 0)) { - qemu_log("DCR read error %d %03x\n", (int)dcrn, (int)dcrn); + } else if (unlikely(ppc_dcr_read(env->dcr_env, (uint32_t)dcrn, &val) != 0)) { + qemu_log("DCR read error %d %03x\n", (uint32_t)dcrn, (uint32_t)dcrn); helper_raise_exception_err(POWERPC_EXCP_PROGRAM, POWERPC_EXCP_INVAL | POWERPC_EXCP_PRIV_REG); } @@ -1850,8 +1824,8 @@ qemu_log("No DCR environment\n"); helper_raise_exception_err(POWERPC_EXCP_PROGRAM, POWERPC_EXCP_INVAL | POWERPC_EXCP_INVAL_INVAL); - } else if (unlikely(ppc_dcr_write(env->dcr_env, dcrn, val) != 0)) { - qemu_log("DCR write error %d %03x\n", (int)dcrn, (int)dcrn); + } else if (unlikely(ppc_dcr_write(env->dcr_env, (uint32_t)dcrn, (uint32_t)val) != 0)) { + qemu_log("DCR write error %d %03x\n", (uint32_t)dcrn, (uint32_t)dcrn); helper_raise_exception_err(POWERPC_EXCP_PROGRAM, POWERPC_EXCP_INVAL | POWERPC_EXCP_PRIV_REG); } @@ -1940,7 +1914,7 @@ /* If X is a NaN, store the corresponding QNaN into RESULT. Otherwise, * execute the following block. */ #define DO_HANDLE_NAN(result, x) \ - if (float32_is_nan(x) || float32_is_signaling_nan(x)) { \ + if (float32_is_any_nan(x)) { \ CPU_FloatU __f; \ __f.f = x; \ __f.l = __f.l | (1 << 22); /* Set QNaN bit. */ \ @@ -1955,14 +1929,14 @@ DO_HANDLE_NAN(result, x) DO_HANDLE_NAN(result, y) DO_HANDLE_NAN(result, z) /* Saturating arithmetic helpers. */ -#define SATCVT(from, to, from_type, to_type, min, max, use_min, use_max) \ +#define SATCVT(from, to, from_type, to_type, min, max) \ static inline to_type cvt##from##to(from_type x, int *sat) \ { \ to_type r; \ - if (use_min && x < min) { \ + if (x < (from_type)min) { \ r = min; \ *sat = 1; \ - } else if (use_max && x > max) { \ + } else if (x > (from_type)max) { \ r = max; \ *sat = 1; \ } else { \ @@ -1970,30 +1944,30 @@ } \ return r; \ } -SATCVT(sh, sb, int16_t, int8_t, INT8_MIN, INT8_MAX, 1, 1) -SATCVT(sw, sh, int32_t, int16_t, INT16_MIN, INT16_MAX, 1, 1) -SATCVT(sd, sw, int64_t, int32_t, INT32_MIN, INT32_MAX, 1, 1) - -/* Work around gcc problems with the macro version */ -static inline uint8_t cvtuhub(uint16_t x, int *sat) -{ - uint8_t r; - - if (x > UINT8_MAX) { - r = UINT8_MAX; - *sat = 1; - } else { - r = x; +#define SATCVTU(from, to, from_type, to_type, min, max) \ + static inline to_type cvt##from##to(from_type x, int *sat) \ + { \ + to_type r; \ + if (x > (from_type)max) { \ + r = max; \ + *sat = 1; \ + } else { \ + r = x; \ + } \ + return r; \ } - return r; -} -//SATCVT(uh, ub, uint16_t, uint8_t, 0, UINT8_MAX, 0, 1) -SATCVT(uw, uh, uint32_t, uint16_t, 0, UINT16_MAX, 0, 1) -SATCVT(ud, uw, uint64_t, uint32_t, 0, UINT32_MAX, 0, 1) -SATCVT(sh, ub, int16_t, uint8_t, 0, UINT8_MAX, 1, 1) -SATCVT(sw, uh, int32_t, uint16_t, 0, UINT16_MAX, 1, 1) -SATCVT(sd, uw, int64_t, uint32_t, 0, UINT32_MAX, 1, 1) +SATCVT(sh, sb, int16_t, int8_t, INT8_MIN, INT8_MAX) +SATCVT(sw, sh, int32_t, int16_t, INT16_MIN, INT16_MAX) +SATCVT(sd, sw, int64_t, int32_t, INT32_MIN, INT32_MAX) + +SATCVTU(uh, ub, uint16_t, uint8_t, 0, UINT8_MAX) +SATCVTU(uw, uh, uint32_t, uint16_t, 0, UINT16_MAX) +SATCVTU(ud, uw, uint64_t, uint32_t, 0, UINT32_MAX) +SATCVT(sh, ub, int16_t, uint8_t, 0, UINT8_MAX) +SATCVT(sw, uh, int32_t, uint16_t, 0, UINT16_MAX) +SATCVT(sd, uw, int64_t, uint32_t, 0, UINT32_MAX) #undef SATCVT +#undef SATCVTU #define LVE(name, access, swap, element) \ void helper_##name (ppc_avr_t *r, target_ulong addr) \ @@ -2285,8 +2259,7 @@ float_status s = env->vec_status; \ set_float_rounding_mode(float_round_to_zero, &s); \ for (i = 0; i < ARRAY_SIZE(r->f); i++) { \ - if (float32_is_nan(b->f[i]) || \ - float32_is_signaling_nan(b->f[i])) { \ + if (float32_is_any_nan(b->f[i])) { \ r->element[i] = 0; \ } else { \ float64 t = float32_to_float64(b->f[i], &s); \ @@ -2713,6 +2686,16 @@ r->u64[1] = (a->u64[1] & ~c->u64[1]) | (b->u64[1] & c->u64[1]); } +void helper_vexptefp (ppc_avr_t *r, ppc_avr_t *b) +{ + int i; + for (i = 0; i < ARRAY_SIZE(r->f); i++) { + HANDLE_NAN1(r->f[i], b->f[i]) { + r->f[i] = float32_exp2(b->f[i], &env->vec_status); + } + } +} + void helper_vlogefp (ppc_avr_t *r, ppc_avr_t *b) { int i; @@ -3124,7 +3107,7 @@ u.l = val; /* NaN are not treated the same way IEEE 754 does */ - if (unlikely(float32_is_nan(u.f))) + if (unlikely(float32_is_quiet_nan(u.f))) return 0; return float32_to_int32(u.f, &env->vec_status); @@ -3136,7 +3119,7 @@ u.l = val; /* NaN are not treated the same way IEEE 754 does */ - if (unlikely(float32_is_nan(u.f))) + if (unlikely(float32_is_quiet_nan(u.f))) return 0; return float32_to_uint32(u.f, &env->vec_status); @@ -3148,7 +3131,7 @@ u.l = val; /* NaN are not treated the same way IEEE 754 does */ - if (unlikely(float32_is_nan(u.f))) + if (unlikely(float32_is_quiet_nan(u.f))) return 0; return float32_to_int32_round_to_zero(u.f, &env->vec_status); @@ -3160,7 +3143,7 @@ u.l = val; /* NaN are not treated the same way IEEE 754 does */ - if (unlikely(float32_is_nan(u.f))) + if (unlikely(float32_is_quiet_nan(u.f))) return 0; return float32_to_uint32_round_to_zero(u.f, &env->vec_status); @@ -3197,7 +3180,7 @@ u.l = val; /* NaN are not treated the same way IEEE 754 does */ - if (unlikely(float32_is_nan(u.f))) + if (unlikely(float32_is_quiet_nan(u.f))) return 0; tmp = uint64_to_float32(1ULL << 32, &env->vec_status); u.f = float32_mul(u.f, tmp, &env->vec_status); @@ -3212,7 +3195,7 @@ u.l = val; /* NaN are not treated the same way IEEE 754 does */ - if (unlikely(float32_is_nan(u.f))) + if (unlikely(float32_is_quiet_nan(u.f))) return 0; tmp = uint64_to_float32(1ULL << 32, &env->vec_status); u.f = float32_mul(u.f, tmp, &env->vec_status); @@ -3466,8 +3449,9 @@ u.ll = val; /* NaN are not treated the same way IEEE 754 does */ - if (unlikely(float64_is_nan(u.d))) + if (unlikely(float64_is_any_nan(u.d))) { return 0; + } return float64_to_int32(u.d, &env->vec_status); } @@ -3478,8 +3462,9 @@ u.ll = val; /* NaN are not treated the same way IEEE 754 does */ - if (unlikely(float64_is_nan(u.d))) + if (unlikely(float64_is_any_nan(u.d))) { return 0; + } return float64_to_uint32(u.d, &env->vec_status); } @@ -3490,8 +3475,9 @@ u.ll = val; /* NaN are not treated the same way IEEE 754 does */ - if (unlikely(float64_is_nan(u.d))) + if (unlikely(float64_is_any_nan(u.d))) { return 0; + } return float64_to_int32_round_to_zero(u.d, &env->vec_status); } @@ -3502,8 +3488,9 @@ u.ll = val; /* NaN are not treated the same way IEEE 754 does */ - if (unlikely(float64_is_nan(u.d))) + if (unlikely(float64_is_any_nan(u.d))) { return 0; + } return float64_to_int64_round_to_zero(u.d, &env->vec_status); } @@ -3514,8 +3501,9 @@ u.ll = val; /* NaN are not treated the same way IEEE 754 does */ - if (unlikely(float64_is_nan(u.d))) + if (unlikely(float64_is_any_nan(u.d))) { return 0; + } return float64_to_uint32_round_to_zero(u.d, &env->vec_status); } @@ -3526,8 +3514,9 @@ u.ll = val; /* NaN are not treated the same way IEEE 754 does */ - if (unlikely(float64_is_nan(u.d))) + if (unlikely(float64_is_any_nan(u.d))) { return 0; + } return float64_to_uint64_round_to_zero(u.d, &env->vec_status); } @@ -3563,8 +3552,9 @@ u.ll = val; /* NaN are not treated the same way IEEE 754 does */ - if (unlikely(float64_is_nan(u.d))) + if (unlikely(float64_is_any_nan(u.d))) { return 0; + } tmp = uint64_to_float64(1ULL << 32, &env->vec_status); u.d = float64_mul(u.d, tmp, &env->vec_status); @@ -3578,8 +3568,9 @@ u.ll = val; /* NaN are not treated the same way IEEE 754 does */ - if (unlikely(float64_is_nan(u.d))) + if (unlikely(float64_is_any_nan(u.d))) { return 0; + } tmp = uint64_to_float64(1ULL << 32, &env->vec_status); u.d = float64_mul(u.d, tmp, &env->vec_status); @@ -3804,6 +3795,7 @@ EPN = env->spr[SPR_DMISS]; } way = (env->spr[SPR_SRR1] >> 17) & 1; + (void)EPN; /* avoid a compiler warning */ LOG_SWTLB("%s: EPN " TARGET_FMT_lx " " TARGET_FMT_lx " PTE0 " TARGET_FMT_lx " PTE1 " TARGET_FMT_lx " way %d\n", __func__, new_EPN, EPN, CMP, RPN, way); @@ -3832,6 +3824,7 @@ CMP = env->spr[SPR_PTEHI]; EPN = env->spr[SPR_TLBMISS] & ~0x3; way = env->spr[SPR_TLBMISS] & 0x3; + (void)EPN; /* avoid a compiler warning */ LOG_SWTLB("%s: EPN " TARGET_FMT_lx " " TARGET_FMT_lx " PTE0 " TARGET_FMT_lx " PTE1 " TARGET_FMT_lx " way %d\n", __func__, new_EPN, EPN, CMP, RPN, way); @@ -3919,37 +3912,56 @@ } /* Helpers for 4xx TLB management */ -target_ulong helper_4xx_tlbre_lo (target_ulong entry) +#define PPC4XX_TLB_ENTRY_MASK 0x0000003f /* Mask for 64 TLB entries */ + +#define PPC4XX_TLBHI_V 0x00000040 +#define PPC4XX_TLBHI_E 0x00000020 +#define PPC4XX_TLBHI_SIZE_MIN 0 +#define PPC4XX_TLBHI_SIZE_MAX 7 +#define PPC4XX_TLBHI_SIZE_DEFAULT 1 +#define PPC4XX_TLBHI_SIZE_SHIFT 7 +#define PPC4XX_TLBHI_SIZE_MASK 0x00000007 + +#define PPC4XX_TLBLO_EX 0x00000200 +#define PPC4XX_TLBLO_WR 0x00000100 +#define PPC4XX_TLBLO_ATTR_MASK 0x000000FF +#define PPC4XX_TLBLO_RPN_MASK 0xFFFFFC00 + +target_ulong helper_4xx_tlbre_hi (target_ulong entry) { ppcemb_tlb_t *tlb; target_ulong ret; int size; - entry &= 0x3F; + entry &= PPC4XX_TLB_ENTRY_MASK; tlb = &env->tlb[entry].tlbe; ret = tlb->EPN; - if (tlb->prot & PAGE_VALID) - ret |= 0x400; + if (tlb->prot & PAGE_VALID) { + ret |= PPC4XX_TLBHI_V; + } size = booke_page_size_to_tlb(tlb->size); - if (size < 0 || size > 0x7) - size = 1; - ret |= size << 7; + if (size < PPC4XX_TLBHI_SIZE_MIN || size > PPC4XX_TLBHI_SIZE_MAX) { + size = PPC4XX_TLBHI_SIZE_DEFAULT; + } + ret |= size << PPC4XX_TLBHI_SIZE_SHIFT; env->spr[SPR_40x_PID] = tlb->PID; return ret; } -target_ulong helper_4xx_tlbre_hi (target_ulong entry) +target_ulong helper_4xx_tlbre_lo (target_ulong entry) { ppcemb_tlb_t *tlb; target_ulong ret; - entry &= 0x3F; + entry &= PPC4XX_TLB_ENTRY_MASK; tlb = &env->tlb[entry].tlbe; ret = tlb->RPN; - if (tlb->prot & PAGE_EXEC) - ret |= 0x200; - if (tlb->prot & PAGE_WRITE) - ret |= 0x100; + if (tlb->prot & PAGE_EXEC) { + ret |= PPC4XX_TLBLO_EX; + } + if (tlb->prot & PAGE_WRITE) { + ret |= PPC4XX_TLBLO_WR; + } return ret; } @@ -3960,37 +3972,40 @@ LOG_SWTLB("%s entry %d val " TARGET_FMT_lx "\n", __func__, (int)entry, val); - entry &= 0x3F; + entry &= PPC4XX_TLB_ENTRY_MASK; tlb = &env->tlb[entry].tlbe; /* Invalidate previous TLB (if it's valid) */ if (tlb->prot & PAGE_VALID) { end = tlb->EPN + tlb->size; LOG_SWTLB("%s: invalidate old TLB %d start " TARGET_FMT_lx " end " TARGET_FMT_lx "\n", __func__, (int)entry, tlb->EPN, end); - for (page = tlb->EPN; page < end; page += TARGET_PAGE_SIZE) + for (page = tlb->EPN; page < end; page += TARGET_PAGE_SIZE) { tlb_flush_page(env, page); + } } - tlb->size = booke_tlb_to_page_size((val >> 7) & 0x7); + tlb->size = booke_tlb_to_page_size((val >> PPC4XX_TLBHI_SIZE_SHIFT) + & PPC4XX_TLBHI_SIZE_MASK); /* We cannot handle TLB size < TARGET_PAGE_SIZE. * If this ever occurs, one should use the ppcemb target instead * of the ppc or ppc64 one */ - if ((val & 0x40) && tlb->size < TARGET_PAGE_SIZE) { + if ((val & PPC4XX_TLBHI_V) && tlb->size < TARGET_PAGE_SIZE) { cpu_abort(env, "TLB size " TARGET_FMT_lu " < %u " "are not supported (%d)\n", tlb->size, TARGET_PAGE_SIZE, (int)((val >> 7) & 0x7)); } tlb->EPN = val & ~(tlb->size - 1); - if (val & 0x40) + if (val & PPC4XX_TLBHI_V) { tlb->prot |= PAGE_VALID; - else + if (val & PPC4XX_TLBHI_E) { + /* XXX: TO BE FIXED */ + cpu_abort(env, + "Little-endian TLB entries are not supported by now\n"); + } + } else { tlb->prot &= ~PAGE_VALID; - if (val & 0x20) { - /* XXX: TO BE FIXED */ - cpu_abort(env, "Little-endian TLB entries are not supported by now\n"); } tlb->PID = env->spr[SPR_40x_PID]; /* PID */ - tlb->attr = val & 0xFF; LOG_SWTLB("%s: set up TLB %d RPN " TARGET_FMT_plx " EPN " TARGET_FMT_lx " size " TARGET_FMT_lx " prot %c%c%c%c PID %d\n", __func__, (int)entry, tlb->RPN, tlb->EPN, tlb->size, @@ -4003,8 +4018,9 @@ end = tlb->EPN + tlb->size; LOG_SWTLB("%s: invalidate TLB %d start " TARGET_FMT_lx " end " TARGET_FMT_lx "\n", __func__, (int)entry, tlb->EPN, end); - for (page = tlb->EPN; page < end; page += TARGET_PAGE_SIZE) + for (page = tlb->EPN; page < end; page += TARGET_PAGE_SIZE) { tlb_flush_page(env, page); + } } } @@ -4014,14 +4030,17 @@ LOG_SWTLB("%s entry %i val " TARGET_FMT_lx "\n", __func__, (int)entry, val); - entry &= 0x3F; + entry &= PPC4XX_TLB_ENTRY_MASK; tlb = &env->tlb[entry].tlbe; - tlb->RPN = val & 0xFFFFFC00; + tlb->attr = val & PPC4XX_TLBLO_ATTR_MASK; + tlb->RPN = val & PPC4XX_TLBLO_RPN_MASK; tlb->prot = PAGE_READ; - if (val & 0x200) + if (val & PPC4XX_TLBLO_EX) { tlb->prot |= PAGE_EXEC; - if (val & 0x100) + } + if (val & PPC4XX_TLBLO_WR) { tlb->prot |= PAGE_WRITE; + } LOG_SWTLB("%s: set up TLB %d RPN " TARGET_FMT_plx " EPN " TARGET_FMT_lx " size " TARGET_FMT_lx " prot %c%c%c%c PID %d\n", __func__, (int)entry, tlb->RPN, tlb->EPN, tlb->size, diff -Nru qemu-kvm-0.12.5+noroms/target-ppc/translate.c qemu-kvm-0.14.1/target-ppc/translate.c --- qemu-kvm-0.12.5+noroms/target-ppc/translate.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-ppc/translate.c 2011-05-11 13:29:46.000000000 +0000 @@ -3398,7 +3398,7 @@ static inline void gen_bcond(DisasContext *ctx, int type) { uint32_t bo = BO(ctx->opcode); - int l1 = gen_new_label(); + int l1; TCGv target; ctx->exception = POWERPC_EXCP_BRANCH; @@ -3719,16 +3719,14 @@ #endif } -#if 1 -#define SPR_NOACCESS ((void *)(-1UL)) -#else -static void spr_noaccess (void *opaque, int sprn) +static void spr_noaccess(void *opaque, int gprn, int sprn) { +#if 0 sprn = ((sprn >> 5) & 0x1F) | ((sprn & 0x1F) << 5); printf("ERROR: try to access SPR %d !\n", sprn); +#endif } #define SPR_NOACCESS (&spr_noaccess) -#endif /* mfspr */ static inline void gen_op_mfspr(DisasContext *ctx) @@ -5876,7 +5874,7 @@ case 2: { TCGv_i32 t0 = tcg_const_i32(rB(ctx->opcode)); - gen_helper_440_tlbwe(t0, cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]); + gen_helper_440_tlbre(cpu_gpr[rD(ctx->opcode)], t0, cpu_gpr[rA(ctx->opcode)]); tcg_temp_free_i32(t0); } break; @@ -6384,6 +6382,7 @@ GEN_VXFORM_NOA(vupklpx, 7, 15); GEN_VXFORM_NOA(vrefp, 5, 4); GEN_VXFORM_NOA(vrsqrtefp, 5, 5); +GEN_VXFORM_NOA(vexptefp, 5, 6); GEN_VXFORM_NOA(vlogefp, 5, 7); GEN_VXFORM_NOA(vrfim, 5, 8); GEN_VXFORM_NOA(vrfin, 5, 9); @@ -6733,7 +6732,7 @@ tcg_gen_br(l2); gen_set_label(l1); tcg_gen_movi_i32(ret, 0); - tcg_gen_br(l2); + gen_set_label(l2); tcg_temp_free_i32(t0); } GEN_SPEOP_ARITH2(evsrwu, gen_op_evsrwu); @@ -6752,7 +6751,7 @@ tcg_gen_br(l2); gen_set_label(l1); tcg_gen_movi_i32(ret, 0); - tcg_gen_br(l2); + gen_set_label(l2); tcg_temp_free_i32(t0); } GEN_SPEOP_ARITH2(evsrws, gen_op_evsrws); @@ -6771,7 +6770,7 @@ tcg_gen_br(l2); gen_set_label(l1); tcg_gen_movi_i32(ret, 0); - tcg_gen_br(l2); + gen_set_label(l2); tcg_temp_free_i32(t0); } GEN_SPEOP_ARITH2(evslw, gen_op_evslw); @@ -7001,7 +7000,7 @@ } static inline void gen_evsplati(DisasContext *ctx) { - uint64_t imm = ((int32_t)(rA(ctx->opcode) << 11)) >> 27; + uint64_t imm = ((int32_t)(rA(ctx->opcode) << 27)) >> 27; #if defined(TARGET_PPC64) tcg_gen_movi_tl(cpu_gpr[rD(ctx->opcode)], (imm << 32) | imm); @@ -7012,7 +7011,7 @@ } static inline void gen_evsplatfi(DisasContext *ctx) { - uint64_t imm = rA(ctx->opcode) << 11; + uint64_t imm = rA(ctx->opcode) << 27; #if defined(TARGET_PPC64) tcg_gen_movi_tl(cpu_gpr[rD(ctx->opcode)], (imm << 32) | imm); @@ -7752,10 +7751,10 @@ return; } #if defined(TARGET_PPC64) - tcg_gen_andi_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], ~0x8000000080000000LL); + tcg_gen_andi_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], ~0x8000000080000000LL); #else - tcg_gen_andi_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], ~0x80000000); - tcg_gen_andi_tl(cpu_gprh[rA(ctx->opcode)], cpu_gprh[rA(ctx->opcode)], ~0x80000000); + tcg_gen_andi_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], ~0x80000000); + tcg_gen_andi_tl(cpu_gprh[rD(ctx->opcode)], cpu_gprh[rA(ctx->opcode)], ~0x80000000); #endif } static inline void gen_evfsnabs(DisasContext *ctx) @@ -7765,10 +7764,10 @@ return; } #if defined(TARGET_PPC64) - tcg_gen_ori_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], 0x8000000080000000LL); + tcg_gen_ori_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], 0x8000000080000000LL); #else - tcg_gen_ori_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], 0x80000000); - tcg_gen_ori_tl(cpu_gprh[rA(ctx->opcode)], cpu_gprh[rA(ctx->opcode)], 0x80000000); + tcg_gen_ori_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], 0x80000000); + tcg_gen_ori_tl(cpu_gprh[rD(ctx->opcode)], cpu_gprh[rA(ctx->opcode)], 0x80000000); #endif } static inline void gen_evfsneg(DisasContext *ctx) @@ -7778,10 +7777,10 @@ return; } #if defined(TARGET_PPC64) - tcg_gen_xori_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], 0x8000000080000000LL); + tcg_gen_xori_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], 0x8000000080000000LL); #else - tcg_gen_xori_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], 0x80000000); - tcg_gen_xori_tl(cpu_gprh[rA(ctx->opcode)], cpu_gprh[rA(ctx->opcode)], 0x80000000); + tcg_gen_xori_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], 0x80000000); + tcg_gen_xori_tl(cpu_gprh[rD(ctx->opcode)], cpu_gprh[rA(ctx->opcode)], 0x80000000); #endif } @@ -7833,7 +7832,7 @@ gen_exception(ctx, POWERPC_EXCP_APU); return; } - tcg_gen_andi_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], (target_long)~0x80000000LL); + tcg_gen_andi_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], (target_long)~0x80000000LL); } static inline void gen_efsnabs(DisasContext *ctx) { @@ -7841,7 +7840,7 @@ gen_exception(ctx, POWERPC_EXCP_APU); return; } - tcg_gen_ori_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], 0x80000000); + tcg_gen_ori_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], 0x80000000); } static inline void gen_efsneg(DisasContext *ctx) { @@ -7849,7 +7848,7 @@ gen_exception(ctx, POWERPC_EXCP_APU); return; } - tcg_gen_xori_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], 0x80000000); + tcg_gen_xori_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], 0x80000000); } /* Conversion */ @@ -7902,9 +7901,10 @@ return; } #if defined(TARGET_PPC64) - tcg_gen_andi_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], ~0x8000000000000000LL); + tcg_gen_andi_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], ~0x8000000000000000LL); #else - tcg_gen_andi_tl(cpu_gprh[rA(ctx->opcode)], cpu_gprh[rA(ctx->opcode)], ~0x80000000); + tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]); + tcg_gen_andi_tl(cpu_gprh[rD(ctx->opcode)], cpu_gprh[rA(ctx->opcode)], ~0x80000000); #endif } static inline void gen_efdnabs(DisasContext *ctx) @@ -7914,9 +7914,10 @@ return; } #if defined(TARGET_PPC64) - tcg_gen_ori_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], 0x8000000000000000LL); + tcg_gen_ori_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], 0x8000000000000000LL); #else - tcg_gen_ori_tl(cpu_gprh[rA(ctx->opcode)], cpu_gprh[rA(ctx->opcode)], 0x80000000); + tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]); + tcg_gen_ori_tl(cpu_gprh[rD(ctx->opcode)], cpu_gprh[rA(ctx->opcode)], 0x80000000); #endif } static inline void gen_efdneg(DisasContext *ctx) @@ -7926,9 +7927,10 @@ return; } #if defined(TARGET_PPC64) - tcg_gen_xori_tl(cpu_gpr[rA(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], 0x8000000000000000LL); + tcg_gen_xori_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], 0x8000000000000000LL); #else - tcg_gen_xori_tl(cpu_gprh[rA(ctx->opcode)], cpu_gprh[rA(ctx->opcode)], 0x80000000); + tcg_gen_mov_tl(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]); + tcg_gen_xori_tl(cpu_gprh[rD(ctx->opcode)], cpu_gprh[rA(ctx->opcode)], 0x80000000); #endif } @@ -8049,10 +8051,10 @@ GEN_HANDLER(stswx, 0x1F, 0x15, 0x14, 0x00000001, PPC_STRING), GEN_HANDLER(eieio, 0x1F, 0x16, 0x1A, 0x03FFF801, PPC_MEM_EIEIO), GEN_HANDLER(isync, 0x13, 0x16, 0x04, 0x03FFF801, PPC_MEM), -GEN_HANDLER(lwarx, 0x1F, 0x14, 0x00, 0x00000001, PPC_RES), +GEN_HANDLER(lwarx, 0x1F, 0x14, 0x00, 0x00000000, PPC_RES), GEN_HANDLER2(stwcx_, "stwcx.", 0x1F, 0x16, 0x04, 0x00000000, PPC_RES), #if defined(TARGET_PPC64) -GEN_HANDLER(ldarx, 0x1F, 0x14, 0x02, 0x00000001, PPC_64B), +GEN_HANDLER(ldarx, 0x1F, 0x14, 0x02, 0x00000000, PPC_64B), GEN_HANDLER2(stdcx_, "stdcx.", 0x1F, 0x16, 0x06, 0x00000000, PPC_64B), #endif GEN_HANDLER(sync, 0x1F, 0x16, 0x12, 0x039FF801, PPC_MEM_SYNC), @@ -8698,6 +8700,7 @@ GEN_VXFORM_NOA(vupklpx, 7, 15), GEN_VXFORM_NOA(vrefp, 5, 4), GEN_VXFORM_NOA(vrsqrtefp, 5, 5), +GEN_VXFORM_NOA(vexptefp, 5, 6), GEN_VXFORM_NOA(vlogefp, 5, 7), GEN_VXFORM_NOA(vrfim, 5, 8), GEN_VXFORM_NOA(vrfin, 5, 9), @@ -8830,8 +8833,7 @@ /*****************************************************************************/ /* Misc PowerPC helpers */ -void cpu_dump_state (CPUState *env, FILE *f, - int (*cpu_fprintf)(FILE *f, const char *fmt, ...), +void cpu_dump_state (CPUState *env, FILE *f, fprintf_function cpu_fprintf, int flags) { #define RGPL 4 @@ -8840,15 +8842,15 @@ int i; cpu_fprintf(f, "NIP " TARGET_FMT_lx " LR " TARGET_FMT_lx " CTR " - TARGET_FMT_lx " XER %08x\n", env->nip, env->lr, env->ctr, - env->xer); + TARGET_FMT_lx " XER " TARGET_FMT_lx "\n", + env->nip, env->lr, env->ctr, env->xer); cpu_fprintf(f, "MSR " TARGET_FMT_lx " HID0 " TARGET_FMT_lx " HF " TARGET_FMT_lx " idx %d\n", env->msr, env->spr[SPR_HID0], env->hflags, env->mmu_idx); #if !defined(NO_TIMER_DUMP) - cpu_fprintf(f, "TB %08x %08x " + cpu_fprintf(f, "TB %08" PRIu32 " %08" PRIu64 #if !defined(CONFIG_USER_ONLY) - "DECR %08x" + " DECR %08" PRIu32 #endif "\n", cpu_ppc_load_tbu(env), cpu_ppc_load_tbl(env) @@ -8898,8 +8900,7 @@ #undef RFPL } -void cpu_dump_statistics (CPUState *env, FILE*f, - int (*cpu_fprintf)(FILE *f, const char *fmt, ...), +void cpu_dump_statistics (CPUState *env, FILE*f, fprintf_function cpu_fprintf, int flags) { #if defined(DO_PPC_STATISTICS) @@ -8920,7 +8921,7 @@ if (handler->count == 0) continue; cpu_fprintf(f, "%02x %02x %02x (%02x %04d) %16s: " - "%016llx %lld\n", + "%016" PRIx64 " %" PRId64 "\n", op1, op2, op3, op1, (op3 << 5) | op2, handler->oname, handler->count, handler->count); @@ -8929,7 +8930,7 @@ if (handler->count == 0) continue; cpu_fprintf(f, "%02x %02x (%02x %04d) %16s: " - "%016llx %lld\n", + "%016" PRIx64 " %" PRId64 "\n", op1, op2, op1, op2, handler->oname, handler->count, handler->count); } @@ -8937,7 +8938,8 @@ } else { if (handler->count == 0) continue; - cpu_fprintf(f, "%02x (%02x ) %16s: %016llx %lld\n", + cpu_fprintf(f, "%02x (%02x ) %16s: %016" PRIx64 + " %" PRId64 "\n", op1, op1, handler->oname, handler->count, handler->count); } @@ -9053,11 +9055,6 @@ "%02x - %02x - %02x (%08x) " TARGET_FMT_lx " %d\n", opc1(ctx.opcode), opc2(ctx.opcode), opc3(ctx.opcode), ctx.opcode, ctx.nip - 4, (int)msr_ir); - } else { - printf("invalid/unsupported opcode: " - "%02x - %02x - %02x (%08x) " TARGET_FMT_lx " %d\n", - opc1(ctx.opcode), opc2(ctx.opcode), - opc3(ctx.opcode), ctx.opcode, ctx.nip - 4, (int)msr_ir); } } else { if (unlikely((ctx.opcode & handler->inval) != 0)) { @@ -9067,12 +9064,6 @@ ctx.opcode & handler->inval, opc1(ctx.opcode), opc2(ctx.opcode), opc3(ctx.opcode), ctx.opcode, ctx.nip - 4); - } else { - printf("invalid bits: %08x for opcode: " - "%02x - %02x - %02x (%08x) " TARGET_FMT_lx "\n", - ctx.opcode & handler->inval, opc1(ctx.opcode), - opc2(ctx.opcode), opc3(ctx.opcode), - ctx.opcode, ctx.nip - 4); } gen_inval_exception(ctxp, POWERPC_EXCP_INVAL_INVAL); break; @@ -9122,8 +9113,6 @@ tb->icount = num_insns; } #if defined(DEBUG_DISAS) - qemu_log_mask(CPU_LOG_TB_CPU, "---------------- excp: %04x\n", ctx.exception); - log_cpu_state_mask(CPU_LOG_TB_CPU, env, 0); if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { int flags; flags = env->bfd_mach; diff -Nru qemu-kvm-0.12.5+noroms/target-ppc/translate_init.c qemu-kvm-0.14.1/target-ppc/translate_init.c --- qemu-kvm-0.12.5+noroms/target-ppc/translate_init.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-ppc/translate_init.c 2011-05-11 13:29:46.000000000 +0000 @@ -777,16 +777,16 @@ &spr_read_tbl, SPR_NOACCESS, 0x00000000); spr_register(env, SPR_TBL, "TBL", - SPR_NOACCESS, SPR_NOACCESS, - SPR_NOACCESS, &spr_write_tbl, + &spr_read_tbl, SPR_NOACCESS, + &spr_read_tbl, &spr_write_tbl, 0x00000000); spr_register(env, SPR_VTBU, "TBU", &spr_read_tbu, SPR_NOACCESS, &spr_read_tbu, SPR_NOACCESS, 0x00000000); spr_register(env, SPR_TBU, "TBU", - SPR_NOACCESS, SPR_NOACCESS, - SPR_NOACCESS, &spr_write_tbu, + &spr_read_tbu, SPR_NOACCESS, + &spr_read_tbu, &spr_write_tbu, 0x00000000); } @@ -3596,7 +3596,6 @@ POWERPC_FLAG_DE | POWERPC_FLAG_BUS_CLK) #define check_pow_440x5 check_pow_nocheck -__attribute__ (( unused )) static void init_proc_440x5 (CPUPPCState *env) { /* Time base */ @@ -3656,7 +3655,7 @@ init_excp_BookE(env); env->dcache_line_size = 32; env->icache_line_size = 32; - /* XXX: TODO: allocate internal IRQ controller */ + ppc40x_irq_init(env); } /* PowerPC 460 (guessed) */ @@ -5237,7 +5236,7 @@ #define POWERPC_FLAG_7400 (POWERPC_FLAG_VRE | POWERPC_FLAG_SE | \ POWERPC_FLAG_BE | POWERPC_FLAG_PMM | \ POWERPC_FLAG_BUS_CLK) -#define check_pow_7400 check_pow_hid0_74xx +#define check_pow_7400 check_pow_hid0 static void init_proc_7400 (CPUPPCState *env) { @@ -5289,7 +5288,7 @@ #define POWERPC_FLAG_7410 (POWERPC_FLAG_VRE | POWERPC_FLAG_SE | \ POWERPC_FLAG_BE | POWERPC_FLAG_PMM | \ POWERPC_FLAG_BUS_CLK) -#define check_pow_7410 check_pow_hid0_74xx +#define check_pow_7410 check_pow_hid0 static void init_proc_7410 (CPUPPCState *env) { @@ -6536,6 +6535,7 @@ #if 0 CPU_POWERPC_440A4 = xxx, #endif + CPU_POWERPC_440_XILINX = 0x7ff21910, #if 0 CPU_POWERPC_440A5 = xxx, #endif @@ -7464,6 +7464,8 @@ /* PowerPC 440 A4 */ POWERPC_DEF("440A4", CPU_POWERPC_440A4, 440x4), #endif + /* PowerPC 440 Xilinx 5 */ + POWERPC_DEF("440-Xilinx", CPU_POWERPC_440_XILINX, 440x5), #if defined (TODO) /* PowerPC 440 A5 */ POWERPC_DEF("440A5", CPU_POWERPC_440A5, 440x5), @@ -9754,7 +9756,7 @@ return ret; } -void ppc_cpu_list (FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...)) +void ppc_cpu_list (FILE *f, fprintf_function cpu_fprintf) { int i, max; diff -Nru qemu-kvm-0.12.5+noroms/target-s390x/cpu.h qemu-kvm-0.14.1/target-s390x/cpu.h --- qemu-kvm-0.12.5+noroms/target-s390x/cpu.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-s390x/cpu.h 2011-05-11 13:29:46.000000000 +0000 @@ -14,8 +14,7 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA + * License along with this library; if not, see . */ #ifndef CPU_S390X_H #define CPU_S390X_H @@ -100,10 +99,15 @@ #define TARGET_PAGE_BITS 12 +/* ??? This is certainly wrong for 64-bit s390x, but given that only KVM + emulation actually works, this is good enough for a placeholder. */ +#define TARGET_PHYS_ADDR_SPACE_BITS 32 +#define TARGET_VIRT_ADDR_SPACE_BITS 32 + #ifndef CONFIG_USER_ONLY -extern int s390_virtio_hypercall(CPUState *env); -extern void kvm_s390_virtio_irq(CPUState *env, int config_change, uint64_t token); -extern CPUState *s390_cpu_addr2state(uint16_t cpu_addr); +int s390_virtio_hypercall(CPUState *env); +void kvm_s390_virtio_irq(CPUState *env, int config_change, uint64_t token); +CPUState *s390_cpu_addr2state(uint16_t cpu_addr); #endif @@ -112,18 +116,12 @@ #define cpu_gen_code cpu_s390x_gen_code #include "cpu-all.h" -#include "exec-all.h" #define EXCP_OPEX 1 /* operation exception (sigill) */ #define EXCP_SVC 2 /* supervisor call (syscall) */ #define EXCP_ADDR 5 /* addressing exception */ #define EXCP_EXECUTE_SVC 0xff00000 /* supervisor call via execute insn */ -static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock* tb) -{ - env->psw.addr = tb->pc; -} - static inline void cpu_get_tb_cpu_state(CPUState* env, target_ulong *pc, target_ulong *cs_base, int *flags) { diff -Nru qemu-kvm-0.12.5+noroms/target-s390x/exec.h qemu-kvm-0.14.1/target-s390x/exec.h --- qemu-kvm-0.12.5+noroms/target-s390x/exec.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-s390x/exec.h 2011-05-11 13:29:46.000000000 +0000 @@ -14,8 +14,7 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA + * License along with this library; if not, see . */ #include "dyngen-exec.h" @@ -35,14 +34,6 @@ return env->interrupt_request & CPU_INTERRUPT_HARD; // guess } -static inline void regs_to_env(void) -{ -} - -static inline void env_to_regs(void) -{ -} - static inline int cpu_halted(CPUState *env) { if (!env->halted) { @@ -54,3 +45,9 @@ } return EXCP_HALTED; } + +static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock* tb) +{ + env->psw.addr = tb->pc; +} + diff -Nru qemu-kvm-0.12.5+noroms/target-s390x/helper.c qemu-kvm-0.14.1/target-s390x/helper.c --- qemu-kvm-0.12.5+noroms/target-s390x/helper.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-s390x/helper.c 2011-05-11 13:29:46.000000000 +0000 @@ -14,8 +14,7 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA + * License along with this library; if not, see . */ #include @@ -47,11 +46,6 @@ return env; } -target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr) -{ - return addr; -} - void cpu_reset(CPUS390XState *env) { if (qemu_loglevel_mask(CPU_LOG_RESET)) { @@ -64,6 +58,11 @@ tlb_flush(env, 1); } +target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr) +{ + return 0; +} + #ifndef CONFIG_USER_ONLY int cpu_s390x_handle_mmu_fault (CPUState *env, target_ulong address, int rw, @@ -75,10 +74,11 @@ /* XXX: implement mmu */ phys = address; - prot = PAGE_READ | PAGE_WRITE; + prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; - return tlb_set_page(env, address & TARGET_PAGE_MASK, - phys & TARGET_PAGE_MASK, prot, - mmu_idx, is_softmmu); + tlb_set_page(env, address & TARGET_PAGE_MASK, + phys & TARGET_PAGE_MASK, prot, + mmu_idx, TARGET_PAGE_SIZE); + return 0; } #endif /* CONFIG_USER_ONLY */ diff -Nru qemu-kvm-0.12.5+noroms/target-s390x/kvm.c qemu-kvm-0.14.1/target-s390x/kvm.c --- qemu-kvm-0.12.5+noroms/target-s390x/kvm.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-s390x/kvm.c 2011-05-11 13:29:46.000000000 +0000 @@ -70,7 +70,11 @@ #define SCLP_CMDW_READ_SCP_INFO 0x00020001 #define SCLP_CMDW_READ_SCP_INFO_FORCED 0x00120001 -int kvm_arch_init(KVMState *s, int smp_cpus) +const KVMCapabilityInfo kvm_arch_required_capabilities[] = { + KVM_CAP_LAST_INFO +}; + +int kvm_arch_init(KVMState *s) { return 0; } @@ -91,7 +95,7 @@ /* FIXME: add code to reset vcpu. */ } -int kvm_arch_put_registers(CPUState *env) +int kvm_arch_put_registers(CPUState *env, int level) { struct kvm_regs regs; int ret; @@ -119,7 +123,7 @@ int kvm_arch_get_registers(CPUState *env) { - uint32_t ret; + int ret; struct kvm_regs regs; int i; @@ -175,6 +179,11 @@ return 0; } +int kvm_arch_process_irqchip_events(CPUState *env) +{ + return 0; +} + static void kvm_s390_interrupt_internal(CPUState *env, int type, uint32_t parm, uint64_t parm64, int vm) { @@ -186,7 +195,7 @@ } env->halted = 0; - env->exception_index = 0; + env->exception_index = -1; kvmint.type = type; kvmint.parm = parm; @@ -296,7 +305,6 @@ cpu_synchronize_state(env); r = s390_virtio_hypercall(env); - kvm_arch_put_registers(env); return r; } @@ -325,7 +333,7 @@ { kvm_s390_interrupt(env, KVM_S390_RESTART, 0); env->halted = 0; - env->exception_index = 0; + env->exception_index = -1; qemu_cpu_kick(env); dprintf("DONE: SIGP cpu restart: %p\n", env); return 0; @@ -340,9 +348,20 @@ static int s390_cpu_initial_reset(CPUState *env) { - /* XXX */ - fprintf(stderr, "XXX SIGP init\n"); - return -1; + int i; + + if (kvm_vcpu_ioctl(env, KVM_S390_INITIAL_RESET, NULL) < 0) { + perror("cannot init reset vcpu"); + } + + /* Manually zero out all registers */ + cpu_synchronize_state(env); + for (i = 0; i < 16; i++) { + env->regs[i] = 0; + } + + dprintf("DONE: SIGP initial reset: %p\n", env); + return 0; } static int handle_sigp(CPUState *env, struct kvm_run *run, uint8_t ipa1) @@ -481,3 +500,8 @@ return ret; } + +bool kvm_arch_stop_on_emulation_error(CPUState *env) +{ + return true; +} diff -Nru qemu-kvm-0.12.5+noroms/target-s390x/op_helper.c qemu-kvm-0.14.1/target-s390x/op_helper.c --- qemu-kvm-0.12.5+noroms/target-s390x/op_helper.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-s390x/op_helper.c 2011-05-11 13:29:46.000000000 +0000 @@ -14,8 +14,7 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA + * License along with this library; if not, see . */ #include "exec.h" diff -Nru qemu-kvm-0.12.5+noroms/target-s390x/translate.c qemu-kvm-0.14.1/target-s390x/translate.c --- qemu-kvm-0.12.5+noroms/target-s390x/translate.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-s390x/translate.c 2011-05-11 13:29:46.000000000 +0000 @@ -14,8 +14,7 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA + * License along with this library; if not, see . */ #include "cpu.h" @@ -24,8 +23,7 @@ #include "tcg-op.h" #include "qemu-log.h" -void cpu_dump_state(CPUState *env, FILE *f, - int (*cpu_fprintf)(FILE *f, const char *fmt, ...), +void cpu_dump_state(CPUState *env, FILE *f, fprintf_function cpu_fprintf, int flags) { int i; @@ -38,7 +36,7 @@ } } for (i = 0; i < 16; i++) { - cpu_fprintf(f, "F%02d=%016lx", i, env->fregs[i]); + cpu_fprintf(f, "F%02d=%016lx", i, (long)env->fregs[i].i); if ((i % 4) == 3) { cpu_fprintf(f, "\n"); } else { diff -Nru qemu-kvm-0.12.5+noroms/target-sh4/cpu.h qemu-kvm-0.14.1/target-sh4/cpu.h --- qemu-kvm-0.12.5+noroms/target-sh4/cpu.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-sh4/cpu.h 2011-05-11 13:29:46.000000000 +0000 @@ -20,6 +20,7 @@ #define _CPU_SH4_H #include "config.h" +#include "qemu-common.h" #define TARGET_LONG_BITS 32 #define TARGET_HAS_ICE 1 @@ -44,6 +45,9 @@ #define TARGET_PAGE_BITS 12 /* 4k XXXXX */ +#define TARGET_PHYS_ADDR_SPACE_BITS 32 +#define TARGET_VIRT_ADDR_SPACE_BITS 32 + #define SR_MD (1 << 30) #define SR_RB (1 << 29) #define SR_BL (1 << 28) @@ -57,10 +61,37 @@ #define SR_S (1 << 1) #define SR_T (1 << 0) -#define FPSCR_FR (1 << 21) -#define FPSCR_SZ (1 << 20) -#define FPSCR_PR (1 << 19) -#define FPSCR_DN (1 << 18) +#define FPSCR_MASK (0x003fffff) +#define FPSCR_FR (1 << 21) +#define FPSCR_SZ (1 << 20) +#define FPSCR_PR (1 << 19) +#define FPSCR_DN (1 << 18) +#define FPSCR_CAUSE_MASK (0x3f << 12) +#define FPSCR_CAUSE_SHIFT (12) +#define FPSCR_CAUSE_E (1 << 17) +#define FPSCR_CAUSE_V (1 << 16) +#define FPSCR_CAUSE_Z (1 << 15) +#define FPSCR_CAUSE_O (1 << 14) +#define FPSCR_CAUSE_U (1 << 13) +#define FPSCR_CAUSE_I (1 << 12) +#define FPSCR_ENABLE_MASK (0x1f << 7) +#define FPSCR_ENABLE_SHIFT (7) +#define FPSCR_ENABLE_V (1 << 11) +#define FPSCR_ENABLE_Z (1 << 10) +#define FPSCR_ENABLE_O (1 << 9) +#define FPSCR_ENABLE_U (1 << 8) +#define FPSCR_ENABLE_I (1 << 7) +#define FPSCR_FLAG_MASK (0x1f << 2) +#define FPSCR_FLAG_SHIFT (2) +#define FPSCR_FLAG_V (1 << 6) +#define FPSCR_FLAG_Z (1 << 5) +#define FPSCR_FLAG_O (1 << 4) +#define FPSCR_FLAG_U (1 << 3) +#define FPSCR_FLAG_I (1 << 2) +#define FPSCR_RM_MASK (0x03 << 0) +#define FPSCR_RM_NEAREST (0 << 0) +#define FPSCR_RM_ZERO (1 << 0) + #define DELAY_SLOT (1 << 0) #define DELAY_SLOT_CONDITIONAL (1 << 1) #define DELAY_SLOT_TRUE (1 << 2) @@ -72,21 +103,20 @@ * The use of DELAY_SLOT_TRUE flag makes us accept such SR_T modification. */ -/* XXXXX The structure could be made more compact */ typedef struct tlb_t { - uint8_t asid; /* address space identifier */ uint32_t vpn; /* virtual page number */ - uint8_t v; /* validity */ uint32_t ppn; /* physical page number */ - uint8_t sz; /* page size */ - uint32_t size; /* cached page size in bytes */ - uint8_t sh; /* share status */ - uint8_t c; /* cacheability */ - uint8_t pr; /* protection key */ - uint8_t d; /* dirty */ - uint8_t wt; /* write through */ - uint8_t sa; /* space attribute (PCMCIA) */ - uint8_t tc; /* timing control */ + uint32_t size; /* mapped page size in bytes */ + uint8_t asid; /* address space identifier */ + uint8_t v:1; /* validity */ + uint8_t sz:2; /* page size */ + uint8_t sh:1; /* share status */ + uint8_t c:1; /* cacheability */ + uint8_t pr:2; /* protection key */ + uint8_t d:1; /* dirty */ + uint8_t wt:1; /* write through */ + uint8_t sa:3; /* space attribute (PCMCIA) */ + uint8_t tc:1; /* timing control */ } tlb_t; #define UTLB_SIZE 64 @@ -106,8 +136,6 @@ } memory_content; typedef struct CPUSH4State { - int id; /* CPU model */ - uint32_t flags; /* general execution flags */ uint32_t gregs[24]; /* general registers */ float32 fregs[32]; /* floating point registers */ @@ -143,14 +171,18 @@ uint32_t expevt; /* exception event register */ uint32_t intevt; /* interrupt event register */ + tlb_t itlb[ITLB_SIZE]; /* instruction translation table */ + tlb_t utlb[UTLB_SIZE]; /* unified translation table */ + + uint32_t ldst; + + CPU_COMMON + + int id; /* CPU model */ uint32_t pvr; /* Processor Version Register */ uint32_t prr; /* Processor Revision Register */ uint32_t cvr; /* Cache Version Register */ - uint32_t ldst; - - CPU_COMMON tlb_t utlb[UTLB_SIZE]; /* unified translation table */ - tlb_t itlb[ITLB_SIZE]; /* instruction translation table */ void *intc_handle; int intr_at_halt; /* SR_BL ignored during sleep */ memory_content *movcal_backup; @@ -166,10 +198,26 @@ #define cpu_handle_mmu_fault cpu_sh4_handle_mmu_fault void do_interrupt(CPUSH4State * env); -void sh4_cpu_list(FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...)); +void sh4_cpu_list(FILE *f, fprintf_function cpu_fprintf); +#if !defined(CONFIG_USER_ONLY) void cpu_sh4_invalidate_tlb(CPUSH4State *s); +uint32_t cpu_sh4_read_mmaped_itlb_addr(CPUSH4State *s, + target_phys_addr_t addr); +void cpu_sh4_write_mmaped_itlb_addr(CPUSH4State *s, target_phys_addr_t addr, + uint32_t mem_value); +uint32_t cpu_sh4_read_mmaped_itlb_data(CPUSH4State *s, + target_phys_addr_t addr); +void cpu_sh4_write_mmaped_itlb_data(CPUSH4State *s, target_phys_addr_t addr, + uint32_t mem_value); +uint32_t cpu_sh4_read_mmaped_utlb_addr(CPUSH4State *s, + target_phys_addr_t addr); void cpu_sh4_write_mmaped_utlb_addr(CPUSH4State *s, target_phys_addr_t addr, - uint32_t mem_value); + uint32_t mem_value); +uint32_t cpu_sh4_read_mmaped_utlb_data(CPUSH4State *s, + target_phys_addr_t addr); +void cpu_sh4_write_mmaped_utlb_data(CPUSH4State *s, target_phys_addr_t addr, + uint32_t mem_value); +#endif int cpu_sh4_is_cached(CPUSH4State * env, target_ulong addr); @@ -207,7 +255,6 @@ #endif #include "cpu-all.h" -#include "exec-all.h" /* Memory access type */ enum { @@ -299,12 +346,6 @@ #define PTEA_TC (1 << 3) #define cpu_ptea_tc(ptea) (((ptea) & PTEA_TC) >> 3) -static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb) -{ - env->pc = tb->pc; - env->flags = tb->flags; -} - #define TB_FLAG_PENDING_MOVCA (1 << 4) static inline void cpu_get_tb_cpu_state(CPUState *env, target_ulong *pc, diff -Nru qemu-kvm-0.12.5+noroms/target-sh4/exec.h qemu-kvm-0.14.1/target-sh4/exec.h --- qemu-kvm-0.12.5+noroms/target-sh4/exec.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-sh4/exec.h 2011-05-11 13:29:46.000000000 +0000 @@ -47,14 +47,10 @@ #include "softmmu_exec.h" #endif -static inline void regs_to_env(void) +static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb) { - /* XXXXX */ -} - -static inline void env_to_regs(void) -{ - /* XXXXX */ + env->pc = tb->pc; + env->flags = tb->flags; } #endif /* _EXEC_SH4_H */ diff -Nru qemu-kvm-0.12.5+noroms/target-sh4/helper.c qemu-kvm-0.14.1/target-sh4/helper.c --- qemu-kvm-0.12.5+noroms/target-sh4/helper.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-sh4/helper.c 2011-05-11 13:29:46.000000000 +0000 @@ -38,7 +38,7 @@ int mmu_idx, int is_softmmu) { env->tea = address; - env->exception_index = 0; + env->exception_index = -1; switch (rw) { case 0: env->exception_index = 0x0a0; @@ -53,11 +53,6 @@ return 1; } -target_phys_addr_t cpu_get_phys_page_debug(CPUState * env, target_ulong addr) -{ - return addr; -} - int cpu_sh4_is_cached(CPUSH4State * env, target_ulong addr) { /* For user mode, only U0 area is cachable. */ @@ -240,7 +235,7 @@ return 2; if ((env->mmucr & 0x2c000000) == 0x00000000) return 3; - assert(0); + cpu_abort(env, "Unhandled itlb_replacement"); } /* Find the corresponding entry in the right TLB @@ -261,24 +256,6 @@ continue; /* Invalid entry */ if (!entries[i].sh && use_asid && entries[i].asid != asid) continue; /* Bad ASID */ -#if 0 - switch (entries[i].sz) { - case 0: - size = 1024; /* 1kB */ - break; - case 1: - size = 4 * 1024; /* 4kB */ - break; - case 2: - size = 64 * 1024; /* 64kB */ - break; - case 3: - size = 1024 * 1024; /* 1MB */ - break; - default: - assert(0); - } -#endif start = (entries[i].vpn << 10) & ~(entries[i].size - 1); end = start + entries[i].size - 1; if (address >= start && address <= end) { /* Match */ @@ -290,16 +267,6 @@ return match; } -static int same_tlb_entry_exists(const tlb_t * haystack, uint8_t nbtlb, - const tlb_t * needle) -{ - int i; - for (i = 0; i < nbtlb; i++) - if (!memcmp(&haystack[i], needle, sizeof(tlb_t))) - return 1; - return 0; -} - static void increment_urc(CPUState * env) { uint8_t urb, urc; @@ -313,36 +280,40 @@ env->mmucr = (env->mmucr & 0xffff03ff) | (urc << 10); } -/* Find itlb entry - update itlb from utlb if necessary and asked for +/* Copy and utlb entry into itlb + Return entry +*/ +static int copy_utlb_entry_itlb(CPUState *env, int utlb) +{ + int itlb; + + tlb_t * ientry; + itlb = itlb_replacement(env); + ientry = &env->itlb[itlb]; + if (ientry->v) { + tlb_flush_page(env, ientry->vpn << 10); + } + *ientry = env->utlb[utlb]; + update_itlb_use(env, itlb); + return itlb; +} + +/* Find itlb entry Return entry, MMU_ITLB_MISS, MMU_ITLB_MULTIPLE or MMU_DTLB_MULTIPLE - Update the itlb from utlb if update is not 0 */ static int find_itlb_entry(CPUState * env, target_ulong address, - int use_asid, int update) + int use_asid) { - int e, n; + int e; e = find_tlb_entry(env, address, env->itlb, ITLB_SIZE, use_asid); - if (e == MMU_DTLB_MULTIPLE) + if (e == MMU_DTLB_MULTIPLE) { e = MMU_ITLB_MULTIPLE; - else if (e == MMU_DTLB_MISS && update) { - e = find_tlb_entry(env, address, env->utlb, UTLB_SIZE, use_asid); - if (e >= 0) { - tlb_t * ientry; - n = itlb_replacement(env); - ientry = &env->itlb[n]; - if (ientry->v) { - if (!same_tlb_entry_exists(env->utlb, UTLB_SIZE, ientry)) - tlb_flush_page(env, ientry->vpn << 10); - } - *ientry = env->utlb[e]; - e = n; - } else if (e == MMU_DTLB_MISS) - e = MMU_ITLB_MISS; - } else if (e == MMU_DTLB_MISS) + } else if (e == MMU_DTLB_MISS) { e = MMU_ITLB_MISS; - if (e >= 0) + } else if (e >= 0) { update_itlb_use(env, e); + } return e; } @@ -374,50 +345,58 @@ use_asid = (env->mmucr & MMUCR_SV) == 0 || (env->sr & SR_MD) == 0; if (rw == 2) { - n = find_itlb_entry(env, address, use_asid, 1); + n = find_itlb_entry(env, address, use_asid); if (n >= 0) { matching = &env->itlb[n]; if (!(env->sr & SR_MD) && !(matching->pr & 2)) n = MMU_ITLB_VIOLATION; else - *prot = PAGE_READ; + *prot = PAGE_EXEC; + } else { + n = find_utlb_entry(env, address, use_asid); + if (n >= 0) { + n = copy_utlb_entry_itlb(env, n); + matching = &env->itlb[n]; + if (!(env->sr & SR_MD) && !(matching->pr & 2)) { + n = MMU_ITLB_VIOLATION; + } else { + *prot = PAGE_READ | PAGE_EXEC; + if ((matching->pr & 1) && matching->d) { + *prot |= PAGE_WRITE; + } + } + } else if (n == MMU_DTLB_MULTIPLE) { + n = MMU_ITLB_MULTIPLE; + } else if (n == MMU_DTLB_MISS) { + n = MMU_ITLB_MISS; + } } } else { n = find_utlb_entry(env, address, use_asid); if (n >= 0) { matching = &env->utlb[n]; - switch ((matching->pr << 1) | ((env->sr & SR_MD) ? 1 : 0)) { - case 0: /* 000 */ - case 2: /* 010 */ - n = (rw == 1) ? MMU_DTLB_VIOLATION_WRITE : - MMU_DTLB_VIOLATION_READ; - break; - case 1: /* 001 */ - case 4: /* 100 */ - case 5: /* 101 */ - if (rw == 1) - n = MMU_DTLB_VIOLATION_WRITE; - else - *prot = PAGE_READ; - break; - case 3: /* 011 */ - case 6: /* 110 */ - case 7: /* 111 */ - *prot = (rw == 1)? PAGE_WRITE : PAGE_READ; - break; - } + if (!(env->sr & SR_MD) && !(matching->pr & 2)) { + n = (rw == 1) ? MMU_DTLB_VIOLATION_WRITE : + MMU_DTLB_VIOLATION_READ; + } else if ((rw == 1) && !(matching->pr & 1)) { + n = MMU_DTLB_VIOLATION_WRITE; + } else if ((rw == 1) && !matching->d) { + n = MMU_DTLB_INITIAL_WRITE; + } else { + *prot = PAGE_READ; + if ((matching->pr & 1) && matching->d) { + *prot |= PAGE_WRITE; + } + } } else if (n == MMU_DTLB_MISS) { n = (rw == 1) ? MMU_DTLB_MISS_WRITE : MMU_DTLB_MISS_READ; } } if (n >= 0) { + n = MMU_OK; *physical = ((matching->ppn << 10) & ~(matching->size - 1)) | (address & (matching->size - 1)); - if ((rw == 1) & !matching->d) - n = MMU_DTLB_INITIAL_WRITE; - else - n = MMU_OK; } return n; } @@ -446,14 +425,14 @@ } else { *physical = address; } - *prot = PAGE_READ | PAGE_WRITE; + *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; return MMU_OK; } /* If MMU is disabled, return the corresponding physical page */ - if (!env->mmucr & MMUCR_AT) { + if (!(env->mmucr & MMUCR_AT)) { *physical = address & 0x1FFFFFFF; - *prot = PAGE_READ | PAGE_WRITE; + *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; return MMU_OK; } @@ -464,7 +443,7 @@ int cpu_sh4_handle_mmu_fault(CPUState * env, target_ulong address, int rw, int mmu_idx, int is_softmmu) { - target_ulong physical, page_offset, page_size; + target_ulong physical; int prot, ret, access_type; access_type = ACCESS_INT; @@ -474,6 +453,10 @@ if (ret != MMU_OK) { env->tea = address; + if (ret != MMU_DTLB_MULTIPLE && ret != MMU_ITLB_MULTIPLE) { + env->pteh = (env->pteh & PTEH_ASID_MASK) | + (address & PTEH_VPN_MASK); + } switch (ret) { case MMU_ITLB_MISS: case MMU_DTLB_MISS_READ: @@ -500,24 +483,22 @@ break; case MMU_IADDR_ERROR: case MMU_DADDR_ERROR_READ: - env->exception_index = 0x0c0; + env->exception_index = 0x0e0; break; case MMU_DADDR_ERROR_WRITE: env->exception_index = 0x100; break; default: - assert(0); + cpu_abort(env, "Unhandled MMU fault"); } return 1; } - page_size = TARGET_PAGE_SIZE; - page_offset = - (address - (address & TARGET_PAGE_MASK)) & ~(page_size - 1); - address = (address & TARGET_PAGE_MASK) + page_offset; - physical = (physical & TARGET_PAGE_MASK) + page_offset; + address &= TARGET_PAGE_MASK; + physical &= TARGET_PAGE_MASK; - return tlb_set_page(env, address, physical, prot, mmu_idx, is_softmmu); + tlb_set_page(env, address, physical, prot, mmu_idx, TARGET_PAGE_SIZE); + return 0; } target_phys_addr_t cpu_get_phys_page_debug(CPUState * env, target_ulong addr) @@ -537,9 +518,7 @@ if (entry->v) { /* Overwriting valid entry in utlb. */ target_ulong address = entry->vpn << 10; - if (!same_tlb_entry_exists(env->itlb, ITLB_SIZE, entry)) { - tlb_flush_page(env, address); - } + tlb_flush_page(env, address); } /* Take values into cpu status from registers. */ @@ -562,7 +541,7 @@ entry->size = 1024 * 1024; /* 1M */ break; default: - assert(0); + cpu_abort(env, "Unhandled load_tlb"); break; } entry->sh = (uint8_t)cpu_ptel_sh(env->ptel); @@ -584,14 +563,108 @@ entry->v = 0; } /* ITLB */ - for (i = 0; i < UTLB_SIZE; i++) { - tlb_t * entry = &s->utlb[i]; + for (i = 0; i < ITLB_SIZE; i++) { + tlb_t * entry = &s->itlb[i]; entry->v = 0; } tlb_flush(s, 1); } +uint32_t cpu_sh4_read_mmaped_itlb_addr(CPUSH4State *s, + target_phys_addr_t addr) +{ + int index = (addr & 0x00000300) >> 8; + tlb_t * entry = &s->itlb[index]; + + return (entry->vpn << 10) | + (entry->v << 8) | + (entry->asid); +} + +void cpu_sh4_write_mmaped_itlb_addr(CPUSH4State *s, target_phys_addr_t addr, + uint32_t mem_value) +{ + uint32_t vpn = (mem_value & 0xfffffc00) >> 10; + uint8_t v = (uint8_t)((mem_value & 0x00000100) >> 8); + uint8_t asid = (uint8_t)(mem_value & 0x000000ff); + + int index = (addr & 0x00000300) >> 8; + tlb_t * entry = &s->itlb[index]; + if (entry->v) { + /* Overwriting valid entry in itlb. */ + target_ulong address = entry->vpn << 10; + tlb_flush_page(s, address); + } + entry->asid = asid; + entry->vpn = vpn; + entry->v = v; +} + +uint32_t cpu_sh4_read_mmaped_itlb_data(CPUSH4State *s, + target_phys_addr_t addr) +{ + int array = (addr & 0x00800000) >> 23; + int index = (addr & 0x00000300) >> 8; + tlb_t * entry = &s->itlb[index]; + + if (array == 0) { + /* ITLB Data Array 1 */ + return (entry->ppn << 10) | + (entry->v << 8) | + (entry->pr << 5) | + ((entry->sz & 1) << 6) | + ((entry->sz & 2) << 4) | + (entry->c << 3) | + (entry->sh << 1); + } else { + /* ITLB Data Array 2 */ + return (entry->tc << 1) | + (entry->sa); + } +} + +void cpu_sh4_write_mmaped_itlb_data(CPUSH4State *s, target_phys_addr_t addr, + uint32_t mem_value) +{ + int array = (addr & 0x00800000) >> 23; + int index = (addr & 0x00000300) >> 8; + tlb_t * entry = &s->itlb[index]; + + if (array == 0) { + /* ITLB Data Array 1 */ + if (entry->v) { + /* Overwriting valid entry in utlb. */ + target_ulong address = entry->vpn << 10; + tlb_flush_page(s, address); + } + entry->ppn = (mem_value & 0x1ffffc00) >> 10; + entry->v = (mem_value & 0x00000100) >> 8; + entry->sz = (mem_value & 0x00000080) >> 6 | + (mem_value & 0x00000010) >> 4; + entry->pr = (mem_value & 0x00000040) >> 5; + entry->c = (mem_value & 0x00000008) >> 3; + entry->sh = (mem_value & 0x00000002) >> 1; + } else { + /* ITLB Data Array 2 */ + entry->tc = (mem_value & 0x00000008) >> 3; + entry->sa = (mem_value & 0x00000007); + } +} + +uint32_t cpu_sh4_read_mmaped_utlb_addr(CPUSH4State *s, + target_phys_addr_t addr) +{ + int index = (addr & 0x00003f00) >> 8; + tlb_t * entry = &s->utlb[index]; + + increment_urc(s); /* per utlb access */ + + return (entry->vpn << 10) | + (entry->v << 8) | + (entry->asid); +} + void cpu_sh4_write_mmaped_utlb_addr(CPUSH4State *s, target_phys_addr_t addr, uint32_t mem_value) { @@ -654,9 +727,7 @@ if (entry->v) { /* Overwriting valid entry in utlb. */ target_ulong address = entry->vpn << 10; - if (!same_tlb_entry_exists(s->itlb, ITLB_SIZE, entry)) { - tlb_flush_page(s, address); - } + tlb_flush_page(s, address); } entry->asid = asid; entry->vpn = vpn; @@ -666,6 +737,65 @@ } } +uint32_t cpu_sh4_read_mmaped_utlb_data(CPUSH4State *s, + target_phys_addr_t addr) +{ + int array = (addr & 0x00800000) >> 23; + int index = (addr & 0x00003f00) >> 8; + tlb_t * entry = &s->utlb[index]; + + increment_urc(s); /* per utlb access */ + + if (array == 0) { + /* ITLB Data Array 1 */ + return (entry->ppn << 10) | + (entry->v << 8) | + (entry->pr << 5) | + ((entry->sz & 1) << 6) | + ((entry->sz & 2) << 4) | + (entry->c << 3) | + (entry->d << 2) | + (entry->sh << 1) | + (entry->wt); + } else { + /* ITLB Data Array 2 */ + return (entry->tc << 1) | + (entry->sa); + } +} + +void cpu_sh4_write_mmaped_utlb_data(CPUSH4State *s, target_phys_addr_t addr, + uint32_t mem_value) +{ + int array = (addr & 0x00800000) >> 23; + int index = (addr & 0x00003f00) >> 8; + tlb_t * entry = &s->utlb[index]; + + increment_urc(s); /* per utlb access */ + + if (array == 0) { + /* UTLB Data Array 1 */ + if (entry->v) { + /* Overwriting valid entry in utlb. */ + target_ulong address = entry->vpn << 10; + tlb_flush_page(s, address); + } + entry->ppn = (mem_value & 0x1ffffc00) >> 10; + entry->v = (mem_value & 0x00000100) >> 8; + entry->sz = (mem_value & 0x00000080) >> 6 | + (mem_value & 0x00000010) >> 4; + entry->pr = (mem_value & 0x00000060) >> 5; + entry->c = (mem_value & 0x00000008) >> 3; + entry->d = (mem_value & 0x00000004) >> 2; + entry->sh = (mem_value & 0x00000002) >> 1; + entry->wt = (mem_value & 0x00000001); + } else { + /* UTLB Data Array 2 */ + entry->tc = (mem_value & 0x00000008) >> 3; + entry->sa = (mem_value & 0x00000007); + } +} + int cpu_sh4_is_cached(CPUSH4State * env, target_ulong addr) { int n; diff -Nru qemu-kvm-0.12.5+noroms/target-sh4/helper.h qemu-kvm-0.14.1/target-sh4/helper.h --- qemu-kvm-0.12.5+noroms/target-sh4/helper.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-sh4/helper.h 2011-05-11 13:29:46.000000000 +0000 @@ -17,7 +17,6 @@ DEF_HELPER_2(addc, i32, i32, i32) DEF_HELPER_2(subv, i32, i32, i32) DEF_HELPER_2(subc, i32, i32, i32) -DEF_HELPER_1(negc, i32, i32) DEF_HELPER_2(div1, i32, i32, i32) DEF_HELPER_2(macl, void, i32, i32) DEF_HELPER_2(macw, void, i32, i32) @@ -49,5 +48,7 @@ DEF_HELPER_1(fsqrt_DT, i64, i64) DEF_HELPER_1(ftrc_FT, i32, i32) DEF_HELPER_1(ftrc_DT, i32, i64) +DEF_HELPER_2(fipr, void, i32, i32) +DEF_HELPER_1(ftrv, void, i32) #include "def-helper.h" diff -Nru qemu-kvm-0.12.5+noroms/target-sh4/op_helper.c qemu-kvm-0.14.1/target-sh4/op_helper.c --- qemu-kvm-0.12.5+noroms/target-sh4/op_helper.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-sh4/op_helper.c 2011-05-11 13:29:46.000000000 +0000 @@ -21,6 +21,22 @@ #include "exec.h" #include "helper.h" +static void cpu_restore_state_from_retaddr(void *retaddr) +{ + TranslationBlock *tb; + unsigned long pc; + + if (retaddr) { + pc = (unsigned long) retaddr; + tb = tb_find_pc(pc); + if (tb) { + /* the PC is inside the translated code. It means that we have + a virtual CPU fault */ + cpu_restore_state(tb, env, pc, NULL); + } + } +} + #ifndef CONFIG_USER_ONLY #define MMUSUFFIX _mmu @@ -39,9 +55,7 @@ void tlb_fill(target_ulong addr, int is_write, int mmu_idx, void *retaddr) { - TranslationBlock *tb; CPUState *saved_env; - unsigned long pc; int ret; /* XXX: hack to restore env in all cases, even if not called from @@ -50,16 +64,8 @@ env = cpu_single_env; ret = cpu_sh4_handle_mmu_fault(env, addr, is_write, mmu_idx, 1); if (ret) { - if (retaddr) { - /* now we have a real cpu fault */ - pc = (unsigned long) retaddr; - tb = tb_find_pc(pc); - if (tb) { - /* the PC is inside the translated code. It means that we have - a virtual CPU fault */ - cpu_restore_state(tb, env, pc, NULL); - } - } + /* now we have a real cpu fault */ + cpu_restore_state_from_retaddr(retaddr); cpu_loop_exit(); } env = saved_env; @@ -71,34 +77,37 @@ { #ifdef CONFIG_USER_ONLY /* XXXXX */ - assert(0); + cpu_abort(env, "Unhandled ldtlb"); #else cpu_load_tlb(env); #endif } -void helper_raise_illegal_instruction(void) +static inline void raise_exception(int index, void *retaddr) { - env->exception_index = 0x180; + env->exception_index = index; + cpu_restore_state_from_retaddr(retaddr); cpu_loop_exit(); } +void helper_raise_illegal_instruction(void) +{ + raise_exception(0x180, GETPC()); +} + void helper_raise_slot_illegal_instruction(void) { - env->exception_index = 0x1a0; - cpu_loop_exit(); + raise_exception(0x1a0, GETPC()); } void helper_raise_fpu_disable(void) { - env->exception_index = 0x800; - cpu_loop_exit(); + raise_exception(0x800, GETPC()); } void helper_raise_slot_fpu_disable(void) { - env->exception_index = 0x820; - cpu_loop_exit(); + raise_exception(0x820, GETPC()); } void helper_debug(void) @@ -118,8 +127,7 @@ void helper_trapa(uint32_t tra) { env->tra = tra << 2; - env->exception_index = 0x160; - cpu_loop_exit(); + raise_exception(0x160, GETPC()); } void helper_movcal(uint32_t address, uint32_t value) @@ -371,21 +379,6 @@ } } -uint32_t helper_negc(uint32_t arg) -{ - uint32_t temp; - - temp = -arg; - arg = temp - (env->sr & SR_T); - if (0 < temp) - env->sr |= SR_T; - else - env->sr &= ~SR_T; - if (temp < arg) - env->sr |= SR_T; - return arg; -} - uint32_t helper_subc(uint32_t arg0, uint32_t arg1) { uint32_t tmp0, tmp1; @@ -443,11 +436,54 @@ void helper_ld_fpscr(uint32_t val) { - env->fpscr = val & 0x003fffff; - if (val & 0x01) + env->fpscr = val & FPSCR_MASK; + if ((val & FPSCR_RM_MASK) == FPSCR_RM_ZERO) { set_float_rounding_mode(float_round_to_zero, &env->fp_status); - else + } else { set_float_rounding_mode(float_round_nearest_even, &env->fp_status); + } + set_flush_to_zero((val & FPSCR_DN) != 0, &env->fp_status); +} + +static void update_fpscr(void *retaddr) +{ + int xcpt, cause, enable; + + xcpt = get_float_exception_flags(&env->fp_status); + + /* Clear the flag entries */ + env->fpscr &= ~FPSCR_FLAG_MASK; + + if (unlikely(xcpt)) { + if (xcpt & float_flag_invalid) { + env->fpscr |= FPSCR_FLAG_V; + } + if (xcpt & float_flag_divbyzero) { + env->fpscr |= FPSCR_FLAG_Z; + } + if (xcpt & float_flag_overflow) { + env->fpscr |= FPSCR_FLAG_O; + } + if (xcpt & float_flag_underflow) { + env->fpscr |= FPSCR_FLAG_U; + } + if (xcpt & float_flag_inexact) { + env->fpscr |= FPSCR_FLAG_I; + } + + /* Accumulate in cause entries */ + env->fpscr |= (env->fpscr & FPSCR_FLAG_MASK) + << (FPSCR_CAUSE_SHIFT - FPSCR_FLAG_SHIFT); + + /* Generate an exception if enabled */ + cause = (env->fpscr & FPSCR_CAUSE_MASK) >> FPSCR_CAUSE_SHIFT; + enable = (env->fpscr & FPSCR_ENABLE_MASK) >> FPSCR_ENABLE_SHIFT; + if (cause & enable) { + cpu_restore_state_from_retaddr(retaddr); + env->exception_index = 0x120; + cpu_loop_exit(); + } + } } uint32_t helper_fabs_FT(uint32_t t0) @@ -471,7 +507,9 @@ CPU_FloatU f0, f1; f0.l = t0; f1.l = t1; + set_float_exception_flags(0, &env->fp_status); f0.f = float32_add(f0.f, f1.f, &env->fp_status); + update_fpscr(GETPC()); return f0.l; } @@ -480,56 +518,82 @@ CPU_DoubleU d0, d1; d0.ll = t0; d1.ll = t1; + set_float_exception_flags(0, &env->fp_status); d0.d = float64_add(d0.d, d1.d, &env->fp_status); + update_fpscr(GETPC()); return d0.ll; } void helper_fcmp_eq_FT(uint32_t t0, uint32_t t1) { CPU_FloatU f0, f1; + int relation; f0.l = t0; f1.l = t1; - if (float32_compare(f0.f, f1.f, &env->fp_status) == 0) + set_float_exception_flags(0, &env->fp_status); + relation = float32_compare(f0.f, f1.f, &env->fp_status); + if (unlikely(relation == float_relation_unordered)) { + update_fpscr(GETPC()); + } else if (relation == float_relation_equal) { set_t(); - else + } else { clr_t(); + } } void helper_fcmp_eq_DT(uint64_t t0, uint64_t t1) { CPU_DoubleU d0, d1; + int relation; d0.ll = t0; d1.ll = t1; - if (float64_compare(d0.d, d1.d, &env->fp_status) == 0) + set_float_exception_flags(0, &env->fp_status); + relation = float64_compare(d0.d, d1.d, &env->fp_status); + if (unlikely(relation == float_relation_unordered)) { + update_fpscr(GETPC()); + } else if (relation == float_relation_equal) { set_t(); - else + } else { clr_t(); + } } void helper_fcmp_gt_FT(uint32_t t0, uint32_t t1) { CPU_FloatU f0, f1; + int relation; f0.l = t0; f1.l = t1; - if (float32_compare(f0.f, f1.f, &env->fp_status) == 1) + set_float_exception_flags(0, &env->fp_status); + relation = float32_compare(f0.f, f1.f, &env->fp_status); + if (unlikely(relation == float_relation_unordered)) { + update_fpscr(GETPC()); + } else if (relation == float_relation_greater) { set_t(); - else + } else { clr_t(); + } } void helper_fcmp_gt_DT(uint64_t t0, uint64_t t1) { CPU_DoubleU d0, d1; + int relation; d0.ll = t0; d1.ll = t1; - if (float64_compare(d0.d, d1.d, &env->fp_status) == 1) + set_float_exception_flags(0, &env->fp_status); + relation = float64_compare(d0.d, d1.d, &env->fp_status); + if (unlikely(relation == float_relation_unordered)) { + update_fpscr(GETPC()); + } else if (relation == float_relation_greater) { set_t(); - else + } else { clr_t(); + } } uint64_t helper_fcnvsd_FT_DT(uint32_t t0) @@ -537,7 +601,9 @@ CPU_DoubleU d; CPU_FloatU f; f.l = t0; + set_float_exception_flags(0, &env->fp_status); d.d = float32_to_float64(f.f, &env->fp_status); + update_fpscr(GETPC()); return d.ll; } @@ -546,7 +612,9 @@ CPU_DoubleU d; CPU_FloatU f; d.ll = t0; + set_float_exception_flags(0, &env->fp_status); f.f = float64_to_float32(d.d, &env->fp_status); + update_fpscr(GETPC()); return f.l; } @@ -555,7 +623,9 @@ CPU_FloatU f0, f1; f0.l = t0; f1.l = t1; + set_float_exception_flags(0, &env->fp_status); f0.f = float32_div(f0.f, f1.f, &env->fp_status); + update_fpscr(GETPC()); return f0.l; } @@ -564,21 +634,29 @@ CPU_DoubleU d0, d1; d0.ll = t0; d1.ll = t1; + set_float_exception_flags(0, &env->fp_status); d0.d = float64_div(d0.d, d1.d, &env->fp_status); + update_fpscr(GETPC()); return d0.ll; } uint32_t helper_float_FT(uint32_t t0) { CPU_FloatU f; + + set_float_exception_flags(0, &env->fp_status); f.f = int32_to_float32(t0, &env->fp_status); + update_fpscr(GETPC()); + return f.l; } uint64_t helper_float_DT(uint32_t t0) { CPU_DoubleU d; + set_float_exception_flags(0, &env->fp_status); d.d = int32_to_float64(t0, &env->fp_status); + update_fpscr(GETPC()); return d.ll; } @@ -588,8 +666,11 @@ f0.l = t0; f1.l = t1; f2.l = t2; + set_float_exception_flags(0, &env->fp_status); f0.f = float32_mul(f0.f, f1.f, &env->fp_status); f0.f = float32_add(f0.f, f2.f, &env->fp_status); + update_fpscr(GETPC()); + return f0.l; } @@ -598,7 +679,9 @@ CPU_FloatU f0, f1; f0.l = t0; f1.l = t1; + set_float_exception_flags(0, &env->fp_status); f0.f = float32_mul(f0.f, f1.f, &env->fp_status); + update_fpscr(GETPC()); return f0.l; } @@ -607,7 +690,10 @@ CPU_DoubleU d0, d1; d0.ll = t0; d1.ll = t1; + set_float_exception_flags(0, &env->fp_status); d0.d = float64_mul(d0.d, d1.d, &env->fp_status); + update_fpscr(GETPC()); + return d0.ll; } @@ -623,7 +709,9 @@ { CPU_FloatU f; f.l = t0; + set_float_exception_flags(0, &env->fp_status); f.f = float32_sqrt(f.f, &env->fp_status); + update_fpscr(GETPC()); return f.l; } @@ -631,7 +719,9 @@ { CPU_DoubleU d; d.ll = t0; + set_float_exception_flags(0, &env->fp_status); d.d = float64_sqrt(d.d, &env->fp_status); + update_fpscr(GETPC()); return d.ll; } @@ -640,29 +730,88 @@ CPU_FloatU f0, f1; f0.l = t0; f1.l = t1; + set_float_exception_flags(0, &env->fp_status); f0.f = float32_sub(f0.f, f1.f, &env->fp_status); + update_fpscr(GETPC()); return f0.l; } uint64_t helper_fsub_DT(uint64_t t0, uint64_t t1) { CPU_DoubleU d0, d1; + d0.ll = t0; d1.ll = t1; + set_float_exception_flags(0, &env->fp_status); d0.d = float64_sub(d0.d, d1.d, &env->fp_status); + update_fpscr(GETPC()); return d0.ll; } uint32_t helper_ftrc_FT(uint32_t t0) { CPU_FloatU f; + uint32_t ret; f.l = t0; - return float32_to_int32_round_to_zero(f.f, &env->fp_status); + set_float_exception_flags(0, &env->fp_status); + ret = float32_to_int32_round_to_zero(f.f, &env->fp_status); + update_fpscr(GETPC()); + return ret; } uint32_t helper_ftrc_DT(uint64_t t0) { CPU_DoubleU d; + uint32_t ret; d.ll = t0; - return float64_to_int32_round_to_zero(d.d, &env->fp_status); + set_float_exception_flags(0, &env->fp_status); + ret = float64_to_int32_round_to_zero(d.d, &env->fp_status); + update_fpscr(GETPC()); + return ret; +} + +void helper_fipr(uint32_t m, uint32_t n) +{ + int bank, i; + float32 r, p; + + bank = (env->sr & FPSCR_FR) ? 16 : 0; + r = float32_zero; + set_float_exception_flags(0, &env->fp_status); + + for (i = 0 ; i < 4 ; i++) { + p = float32_mul(env->fregs[bank + m + i], + env->fregs[bank + n + i], + &env->fp_status); + r = float32_add(r, p, &env->fp_status); + } + update_fpscr(GETPC()); + + env->fregs[bank + n + 3] = r; +} + +void helper_ftrv(uint32_t n) +{ + int bank_matrix, bank_vector; + int i, j; + float32 r[4]; + float32 p; + + bank_matrix = (env->sr & FPSCR_FR) ? 0 : 16; + bank_vector = (env->sr & FPSCR_FR) ? 16 : 0; + set_float_exception_flags(0, &env->fp_status); + for (i = 0 ; i < 4 ; i++) { + r[i] = float32_zero; + for (j = 0 ; j < 4 ; j++) { + p = float32_mul(env->fregs[bank_matrix + 4 * j + i], + env->fregs[bank_vector + j], + &env->fp_status); + r[i] = float32_add(r[i], p, &env->fp_status); + } + } + update_fpscr(GETPC()); + + for (i = 0 ; i < 4 ; i++) { + env->fregs[bank_vector + i] = r[i]; + } } diff -Nru qemu-kvm-0.12.5+noroms/target-sh4/README.sh4 qemu-kvm-0.14.1/target-sh4/README.sh4 --- qemu-kvm-0.12.5+noroms/target-sh4/README.sh4 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-sh4/README.sh4 2011-05-11 13:29:46.000000000 +0000 @@ -6,7 +6,7 @@ file describes the current state of implementation. Most places requiring attention and/or modification can be detected by -looking for "XXXXX" or "assert (0)". +looking for "XXXXX" or "abort()". The sh4 core is located in target-sh4/*, while the 7750 peripheral features (IO ports for example) are located in hw/sh7750.[ch]. The diff -Nru qemu-kvm-0.12.5+noroms/target-sh4/translate.c qemu-kvm-0.14.1/target-sh4/translate.c --- qemu-kvm-0.12.5+noroms/target-sh4/translate.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-sh4/translate.c 2011-05-11 13:29:46.000000000 +0000 @@ -77,6 +77,8 @@ /* internal register indexes */ static TCGv cpu_flags, cpu_delayed_pc; +static uint32_t gen_opc_hflags[OPC_BUF_SIZE]; + #include "gen-icount.h" static void sh4_translate_init(void) @@ -183,28 +185,27 @@ } } -static void cpu_sh4_reset(CPUSH4State * env) +void cpu_reset(CPUSH4State * env) { if (qemu_loglevel_mask(CPU_LOG_RESET)) { qemu_log("CPU Reset (CPU %d)\n", env->cpu_index); log_cpu_state(env, 0); } -#if defined(CONFIG_USER_ONLY) - env->sr = 0; -#else - env->sr = SR_MD | SR_RB | SR_BL | SR_I3 | SR_I2 | SR_I1 | SR_I0; -#endif - env->vbr = 0; + memset(env, 0, offsetof(CPUSH4State, breakpoints)); + tlb_flush(env, 1); + env->pc = 0xA0000000; #if defined(CONFIG_USER_ONLY) env->fpscr = FPSCR_PR; /* value for userspace according to the kernel */ set_float_rounding_mode(float_round_nearest_even, &env->fp_status); /* ?! */ #else - env->fpscr = 0x00040001; /* CPU reset value according to SH4 manual */ + env->sr = SR_MD | SR_RB | SR_BL | SR_I3 | SR_I2 | SR_I1 | SR_I0; + env->fpscr = FPSCR_DN | FPSCR_RM_ZERO; /* CPU reset value according to SH4 manual */ set_float_rounding_mode(float_round_to_zero, &env->fp_status); + set_flush_to_zero(1, &env->fp_status); #endif - env->mmucr = 0; + set_default_nan_mode(1, &env->fp_status); } typedef struct { @@ -255,7 +256,7 @@ return NULL; } -void sh4_cpu_list(FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...)) +void sh4_cpu_list(FILE *f, fprintf_function cpu_fprintf) { int i; @@ -263,7 +264,7 @@ (*cpu_fprintf)(f, "%s\n", sh4_defs[i].name); } -static void cpu_sh4_register(CPUSH4State *env, const sh4_def_t *def) +static void cpu_register(CPUSH4State *env, const sh4_def_t *def) { env->pvr = def->pvr; env->prr = def->prr; @@ -285,9 +286,8 @@ env->movcal_backup_tail = &(env->movcal_backup); sh4_translate_init(); env->cpu_model_str = cpu_model; - cpu_sh4_reset(env); - cpu_sh4_register(env, def); - tlb_flush(env, 1); + cpu_reset(env); + cpu_register(env, def); qemu_init_vcpu(env); return env; } @@ -332,7 +332,7 @@ tcg_gen_movi_i32(cpu_delayed_pc, delayed_pc); sr = tcg_temp_new(); tcg_gen_andi_i32(sr, cpu_sr, SR_T); - tcg_gen_brcondi_i32(TCG_COND_NE, sr, t ? SR_T : 0, label); + tcg_gen_brcondi_i32(t ? TCG_COND_EQ:TCG_COND_NE, sr, 0, label); tcg_gen_ori_i32(cpu_flags, cpu_flags, DELAY_SLOT_TRUE); gen_set_label(label); } @@ -347,7 +347,7 @@ l1 = gen_new_label(); sr = tcg_temp_new(); tcg_gen_andi_i32(sr, cpu_sr, SR_T); - tcg_gen_brcondi_i32(TCG_COND_EQ, sr, SR_T, l1); + tcg_gen_brcondi_i32(TCG_COND_NE, sr, 0, l1); gen_goto_tb(ctx, 0, ifnott); gen_set_label(l1); gen_goto_tb(ctx, 1, ift); @@ -362,7 +362,7 @@ l1 = gen_new_label(); ds = tcg_temp_new(); tcg_gen_andi_i32(ds, cpu_flags, DELAY_SLOT_TRUE); - tcg_gen_brcondi_i32(TCG_COND_EQ, ds, DELAY_SLOT_TRUE, l1); + tcg_gen_brcondi_i32(TCG_COND_NE, ds, 0, l1); gen_goto_tb(ctx, 1, ctx->pc + 2); gen_set_label(l1); tcg_gen_andi_i32(cpu_flags, cpu_flags, ~DELAY_SLOT_TRUE); @@ -381,26 +381,26 @@ static inline void gen_cmp(int cond, TCGv t0, TCGv t1) { - int label1 = gen_new_label(); - int label2 = gen_new_label(); - tcg_gen_brcond_i32(cond, t1, t0, label1); - gen_clr_t(); - tcg_gen_br(label2); - gen_set_label(label1); - gen_set_t(); - gen_set_label(label2); + TCGv t; + + t = tcg_temp_new(); + tcg_gen_setcond_i32(cond, t, t1, t0); + tcg_gen_andi_i32(cpu_sr, cpu_sr, ~SR_T); + tcg_gen_or_i32(cpu_sr, cpu_sr, t); + + tcg_temp_free(t); } static inline void gen_cmp_imm(int cond, TCGv t0, int32_t imm) { - int label1 = gen_new_label(); - int label2 = gen_new_label(); - tcg_gen_brcondi_i32(cond, t0, imm, label1); - gen_clr_t(); - tcg_gen_br(label2); - gen_set_label(label1); - gen_set_t(); - gen_set_label(label2); + TCGv t; + + t = tcg_temp_new(); + tcg_gen_setcondi_i32(cond, t, t0, imm); + tcg_gen_andi_i32(cpu_sr, cpu_sr, ~SR_T); + tcg_gen_or_i32(cpu_sr, cpu_sr, t); + + tcg_temp_free(t); } static inline void gen_store_flags(uint32_t flags) @@ -467,27 +467,27 @@ #define CHECK_NOT_DELAY_SLOT \ if (ctx->flags & (DELAY_SLOT | DELAY_SLOT_CONDITIONAL)) \ { \ - tcg_gen_movi_i32(cpu_pc, ctx->pc-2); \ gen_helper_raise_slot_illegal_instruction(); \ ctx->bstate = BS_EXCP; \ return; \ } -#define CHECK_PRIVILEGED \ - if (IS_USER(ctx)) { \ - tcg_gen_movi_i32(cpu_pc, ctx->pc); \ - gen_helper_raise_illegal_instruction(); \ - ctx->bstate = BS_EXCP; \ - return; \ +#define CHECK_PRIVILEGED \ + if (IS_USER(ctx)) { \ + if (ctx->flags & (DELAY_SLOT | DELAY_SLOT_CONDITIONAL)) { \ + gen_helper_raise_slot_illegal_instruction(); \ + } else { \ + gen_helper_raise_illegal_instruction(); \ + } \ + ctx->bstate = BS_EXCP; \ + return; \ } #define CHECK_FPU_ENABLED \ if (ctx->flags & SR_FD) { \ if (ctx->flags & (DELAY_SLOT | DELAY_SLOT_CONDITIONAL)) { \ - tcg_gen_movi_i32(cpu_pc, ctx->pc-2); \ gen_helper_raise_slot_fpu_disable(); \ } else { \ - tcg_gen_movi_i32(cpu_pc, ctx->pc); \ gen_helper_raise_fpu_disable(); \ } \ ctx->bstate = BS_EXCP; \ @@ -664,7 +664,7 @@ TCGv addr = tcg_temp_new(); tcg_gen_subi_i32(addr, REG(B11_8), 1); tcg_gen_qemu_st8(REG(B7_4), addr, ctx->memidx); /* might cause re-execution */ - tcg_gen_subi_i32(REG(B11_8), REG(B11_8), 1); /* modify register status */ + tcg_gen_mov_i32(REG(B11_8), addr); /* modify register status */ tcg_temp_free(addr); } return; @@ -673,7 +673,7 @@ TCGv addr = tcg_temp_new(); tcg_gen_subi_i32(addr, REG(B11_8), 2); tcg_gen_qemu_st16(REG(B7_4), addr, ctx->memidx); - tcg_gen_subi_i32(REG(B11_8), REG(B11_8), 2); + tcg_gen_mov_i32(REG(B11_8), addr); tcg_temp_free(addr); } return; @@ -682,7 +682,7 @@ TCGv addr = tcg_temp_new(); tcg_gen_subi_i32(addr, REG(B11_8), 4); tcg_gen_qemu_st32(REG(B7_4), addr, ctx->memidx); - tcg_gen_subi_i32(REG(B11_8), REG(B11_8), 4); + tcg_gen_mov_i32(REG(B11_8), addr); } return; case 0x6004: /* mov.b @Rm+,Rn */ @@ -750,17 +750,13 @@ return; case 0x6008: /* swap.b Rm,Rn */ { - TCGv highw, high, low; - highw = tcg_temp_new(); - tcg_gen_andi_i32(highw, REG(B7_4), 0xffff0000); + TCGv high, low; high = tcg_temp_new(); - tcg_gen_ext8u_i32(high, REG(B7_4)); - tcg_gen_shli_i32(high, high, 8); + tcg_gen_andi_i32(high, REG(B7_4), 0xffff0000); low = tcg_temp_new(); - tcg_gen_shri_i32(low, REG(B7_4), 8); - tcg_gen_ext8u_i32(low, low); + tcg_gen_ext16u_i32(low, REG(B7_4)); + tcg_gen_bswap16_i32(low, low); tcg_gen_or_i32(REG(B11_8), high, low); - tcg_gen_or_i32(REG(B11_8), REG(B11_8), highw); tcg_temp_free(low); tcg_temp_free(high); } @@ -769,8 +765,7 @@ { TCGv high, low; high = tcg_temp_new(); - tcg_gen_ext16u_i32(high, REG(B7_4)); - tcg_gen_shli_i32(high, high, 16); + tcg_gen_shli_i32(high, REG(B7_4), 16); low = tcg_temp_new(); tcg_gen_shri_i32(low, REG(B7_4), 16); tcg_gen_ext16u_i32(low, low); @@ -783,8 +778,7 @@ { TCGv high, low; high = tcg_temp_new(); - tcg_gen_ext16u_i32(high, REG(B7_4)); - tcg_gen_shli_i32(high, high, 16); + tcg_gen_shli_i32(high, REG(B7_4), 16); low = tcg_temp_new(); tcg_gen_shri_i32(low, REG(B11_8), 16); tcg_gen_ext16u_i32(low, low); @@ -822,24 +816,22 @@ return; case 0x200c: /* cmp/str Rm,Rn */ { - int label1 = gen_new_label(); - int label2 = gen_new_label(); - TCGv cmp1 = tcg_temp_local_new(); - TCGv cmp2 = tcg_temp_local_new(); + TCGv cmp1 = tcg_temp_new(); + TCGv cmp2 = tcg_temp_new(); + tcg_gen_andi_i32(cpu_sr, cpu_sr, ~SR_T); tcg_gen_xor_i32(cmp1, REG(B7_4), REG(B11_8)); tcg_gen_andi_i32(cmp2, cmp1, 0xff000000); - tcg_gen_brcondi_i32(TCG_COND_EQ, cmp2, 0, label1); + tcg_gen_setcondi_i32(TCG_COND_EQ, cmp2, cmp2, 0); + tcg_gen_or_i32(cpu_sr, cpu_sr, cmp2); tcg_gen_andi_i32(cmp2, cmp1, 0x00ff0000); - tcg_gen_brcondi_i32(TCG_COND_EQ, cmp2, 0, label1); + tcg_gen_setcondi_i32(TCG_COND_EQ, cmp2, cmp2, 0); + tcg_gen_or_i32(cpu_sr, cpu_sr, cmp2); tcg_gen_andi_i32(cmp2, cmp1, 0x0000ff00); - tcg_gen_brcondi_i32(TCG_COND_EQ, cmp2, 0, label1); + tcg_gen_setcondi_i32(TCG_COND_EQ, cmp2, cmp2, 0); + tcg_gen_or_i32(cpu_sr, cpu_sr, cmp2); tcg_gen_andi_i32(cmp2, cmp1, 0x000000ff); - tcg_gen_brcondi_i32(TCG_COND_EQ, cmp2, 0, label1); - tcg_gen_andi_i32(cpu_sr, cpu_sr, ~SR_T); - tcg_gen_br(label2); - gen_set_label(label1); - tcg_gen_ori_i32(cpu_sr, cpu_sr, SR_T); - gen_set_label(label2); + tcg_gen_setcondi_i32(TCG_COND_EQ, cmp2, cmp2, 0); + tcg_gen_or_i32(cpu_sr, cpu_sr, cmp2); tcg_temp_free(cmp2); tcg_temp_free(cmp1); } @@ -960,7 +952,21 @@ tcg_gen_neg_i32(REG(B11_8), REG(B7_4)); return; case 0x600a: /* negc Rm,Rn */ - gen_helper_negc(REG(B11_8), REG(B7_4)); + { + TCGv t0, t1; + t0 = tcg_temp_new(); + tcg_gen_neg_i32(t0, REG(B7_4)); + t1 = tcg_temp_new(); + tcg_gen_andi_i32(t1, cpu_sr, SR_T); + tcg_gen_sub_i32(REG(B11_8), t0, t1); + tcg_gen_andi_i32(cpu_sr, cpu_sr, ~SR_T); + tcg_gen_setcondi_i32(TCG_COND_GTU, t1, t0, 0); + tcg_gen_or_i32(cpu_sr, cpu_sr, t1); + tcg_gen_setcond_i32(TCG_COND_GTU, t1, REG(B11_8), t0); + tcg_gen_or_i32(cpu_sr, cpu_sr, t1); + tcg_temp_free(t0); + tcg_temp_free(t1); + } return; case 0x6007: /* not Rm,Rn */ tcg_gen_not_i32(REG(B11_8), REG(B7_4)); @@ -974,20 +980,24 @@ int label2 = gen_new_label(); int label3 = gen_new_label(); int label4 = gen_new_label(); - TCGv shift = tcg_temp_local_new(); + TCGv shift; tcg_gen_brcondi_i32(TCG_COND_LT, REG(B7_4), 0, label1); /* Rm positive, shift to the left */ + shift = tcg_temp_new(); tcg_gen_andi_i32(shift, REG(B7_4), 0x1f); tcg_gen_shl_i32(REG(B11_8), REG(B11_8), shift); + tcg_temp_free(shift); tcg_gen_br(label4); /* Rm negative, shift to the right */ gen_set_label(label1); + shift = tcg_temp_new(); tcg_gen_andi_i32(shift, REG(B7_4), 0x1f); tcg_gen_brcondi_i32(TCG_COND_EQ, shift, 0, label2); tcg_gen_not_i32(shift, REG(B7_4)); tcg_gen_andi_i32(shift, shift, 0x1f); tcg_gen_addi_i32(shift, shift, 1); tcg_gen_sar_i32(REG(B11_8), REG(B11_8), shift); + tcg_temp_free(shift); tcg_gen_br(label4); /* Rm = -32 */ gen_set_label(label2); @@ -997,7 +1007,6 @@ gen_set_label(label3); tcg_gen_movi_i32(REG(B11_8), 0xffffffff); gen_set_label(label4); - tcg_temp_free(shift); } return; case 0x400d: /* shld Rm,Rn */ @@ -1005,26 +1014,29 @@ int label1 = gen_new_label(); int label2 = gen_new_label(); int label3 = gen_new_label(); - TCGv shift = tcg_temp_local_new(); + TCGv shift; tcg_gen_brcondi_i32(TCG_COND_LT, REG(B7_4), 0, label1); /* Rm positive, shift to the left */ + shift = tcg_temp_new(); tcg_gen_andi_i32(shift, REG(B7_4), 0x1f); tcg_gen_shl_i32(REG(B11_8), REG(B11_8), shift); + tcg_temp_free(shift); tcg_gen_br(label3); /* Rm negative, shift to the right */ gen_set_label(label1); + shift = tcg_temp_new(); tcg_gen_andi_i32(shift, REG(B7_4), 0x1f); tcg_gen_brcondi_i32(TCG_COND_EQ, shift, 0, label2); tcg_gen_not_i32(shift, REG(B7_4)); tcg_gen_andi_i32(shift, shift, 0x1f); tcg_gen_addi_i32(shift, shift, 1); tcg_gen_shr_i32(REG(B11_8), REG(B11_8), shift); + tcg_temp_free(shift); tcg_gen_br(label3); /* Rm = -32 */ gen_set_label(label2); tcg_gen_movi_i32(REG(B11_8), 0); gen_set_label(label3); - tcg_temp_free(shift); } return; case 0x3008: /* sub Rm,Rn */ @@ -1106,7 +1118,7 @@ int fr = XREG(B7_4); tcg_gen_subi_i32(addr, REG(B11_8), 4); tcg_gen_qemu_st32(cpu_fregs[fr+1], addr, ctx->memidx); - tcg_gen_subi_i32(addr, REG(B11_8), 8); + tcg_gen_subi_i32(addr, addr, 4); tcg_gen_qemu_st32(cpu_fregs[fr ], addr, ctx->memidx); tcg_gen_mov_i32(REG(B11_8), addr); tcg_temp_free(addr); @@ -1115,8 +1127,8 @@ addr = tcg_temp_new_i32(); tcg_gen_subi_i32(addr, REG(B11_8), 4); tcg_gen_qemu_st32(cpu_fregs[FREG(B7_4)], addr, ctx->memidx); + tcg_gen_mov_i32(REG(B11_8), addr); tcg_temp_free(addr); - tcg_gen_subi_i32(REG(B11_8), REG(B11_8), 4); } return; case 0xf006: /* fmov @(R0,Rm),{F,D,X}Rm - FPSCR: Nothing */ @@ -1373,7 +1385,6 @@ { TCGv imm; CHECK_NOT_DELAY_SLOT - tcg_gen_movi_i32(cpu_pc, ctx->pc); imm = tcg_const_i32(B7_0); gen_helper_trapa(imm); tcg_temp_free(imm); @@ -1436,8 +1447,8 @@ TCGv addr = tcg_temp_new(); tcg_gen_subi_i32(addr, REG(B11_8), 4); tcg_gen_qemu_st32(ALTREG(B6_4), addr, ctx->memidx); + tcg_gen_mov_i32(REG(B11_8), addr); tcg_temp_free(addr); - tcg_gen_subi_i32(REG(B11_8), REG(B11_8), 4); } return; } @@ -1505,11 +1516,11 @@ TCGv addr = tcg_temp_new(); tcg_gen_subi_i32(addr, REG(B11_8), 4); tcg_gen_qemu_st32(cpu_sr, addr, ctx->memidx); + tcg_gen_mov_i32(REG(B11_8), addr); tcg_temp_free(addr); - tcg_gen_subi_i32(REG(B11_8), REG(B11_8), 4); } return; -#define LDST(reg,ldnum,ldpnum,stnum,stpnum,prechk) \ +#define LD(reg,ldnum,ldpnum,prechk) \ case ldnum: \ prechk \ tcg_gen_mov_i32 (cpu_##reg, REG(B11_8)); \ @@ -1518,7 +1529,8 @@ prechk \ tcg_gen_qemu_ld32s (cpu_##reg, REG(B11_8), ctx->memidx); \ tcg_gen_addi_i32(REG(B11_8), REG(B11_8), 4); \ - return; \ + return; +#define ST(reg,stnum,stpnum,prechk) \ case stnum: \ prechk \ tcg_gen_mov_i32 (REG(B11_8), cpu_##reg); \ @@ -1526,17 +1538,22 @@ case stpnum: \ prechk \ { \ - TCGv addr = tcg_temp_new(); \ + TCGv addr = tcg_temp_new(); \ tcg_gen_subi_i32(addr, REG(B11_8), 4); \ tcg_gen_qemu_st32 (cpu_##reg, addr, ctx->memidx); \ + tcg_gen_mov_i32(REG(B11_8), addr); \ tcg_temp_free(addr); \ - tcg_gen_subi_i32(REG(B11_8), REG(B11_8), 4); \ } \ return; +#define LDST(reg,ldnum,ldpnum,stnum,stpnum,prechk) \ + LD(reg,ldnum,ldpnum,prechk) \ + ST(reg,stnum,stpnum,prechk) LDST(gbr, 0x401e, 0x4017, 0x0012, 0x4013, {}) LDST(vbr, 0x402e, 0x4027, 0x0022, 0x4023, CHECK_PRIVILEGED) LDST(ssr, 0x403e, 0x4037, 0x0032, 0x4033, CHECK_PRIVILEGED) LDST(spc, 0x404e, 0x4047, 0x0042, 0x4043, CHECK_PRIVILEGED) + ST(sgr, 0x003a, 0x4032, CHECK_PRIVILEGED) + LD(sgr, 0x403a, 0x4036, CHECK_PRIVILEGED if (!(ctx->features & SH_FEATURE_SH4A)) break;) LDST(dbr, 0x40fa, 0x40f6, 0x00fa, 0x40f2, CHECK_PRIVILEGED) LDST(mach, 0x400a, 0x4006, 0x000a, 0x4002, {}) LDST(macl, 0x401a, 0x4016, 0x001a, 0x4012, {}) @@ -1571,9 +1588,9 @@ addr = tcg_temp_new(); tcg_gen_subi_i32(addr, REG(B11_8), 4); tcg_gen_qemu_st32(val, addr, ctx->memidx); + tcg_gen_mov_i32(REG(B11_8), addr); tcg_temp_free(addr); tcg_temp_free(val); - tcg_gen_subi_i32(REG(B11_8), REG(B11_8), 4); } return; case 0x00c3: /* movca.l R0,@Rm */ @@ -1687,14 +1704,12 @@ } return; case 0x4004: /* rotl Rn */ - gen_copy_bit_i32(cpu_sr, 0, REG(B11_8), 31); - tcg_gen_shli_i32(REG(B11_8), REG(B11_8), 1); - gen_copy_bit_i32(REG(B11_8), 0, cpu_sr, 0); + tcg_gen_rotli_i32(REG(B11_8), REG(B11_8), 1); + gen_copy_bit_i32(cpu_sr, 0, REG(B11_8), 0); return; case 0x4005: /* rotr Rn */ gen_copy_bit_i32(cpu_sr, 0, REG(B11_8), 0); - tcg_gen_shri_i32(REG(B11_8), REG(B11_8), 1); - gen_copy_bit_i32(REG(B11_8), 31, cpu_sr, 0); + tcg_gen_rotri_i32(REG(B11_8), REG(B11_8), 1); return; case 0x4000: /* shll Rn */ case 0x4020: /* shal Rn */ @@ -1846,13 +1861,40 @@ tcg_temp_free_i64(fp); } return; + case 0xf0ed: /* fipr FVm,FVn */ + CHECK_FPU_ENABLED + if ((ctx->fpscr & FPSCR_PR) == 0) { + TCGv m, n; + m = tcg_const_i32((ctx->opcode >> 16) & 3); + n = tcg_const_i32((ctx->opcode >> 18) & 3); + gen_helper_fipr(m, n); + tcg_temp_free(m); + tcg_temp_free(n); + return; + } + break; + case 0xf0fd: /* ftrv XMTRX,FVn */ + CHECK_FPU_ENABLED + if ((ctx->opcode & 0x0300) == 0x0100 && + (ctx->fpscr & FPSCR_PR) == 0) { + TCGv n; + n = tcg_const_i32((ctx->opcode >> 18) & 3); + gen_helper_ftrv(n); + tcg_temp_free(n); + return; + } + break; } #if 0 fprintf(stderr, "unknown instruction 0x%04x at pc 0x%08x\n", ctx->opcode, ctx->pc); fflush(stderr); #endif - gen_helper_raise_illegal_instruction(); + if (ctx->flags & (DELAY_SLOT | DELAY_SLOT_CONDITIONAL)) { + gen_helper_raise_slot_illegal_instruction(); + } else { + gen_helper_raise_illegal_instruction(); + } ctx->bstate = BS_EXCP; } @@ -1860,6 +1902,10 @@ { uint32_t old_flags = ctx->flags; + if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP))) { + tcg_gen_debug_insn_start(ctx->pc); + } + _decode_opc(ctx); if (old_flags & (DELAY_SLOT | DELAY_SLOT_CONDITIONAL)) { @@ -1914,12 +1960,6 @@ ctx.features = env->features; ctx.has_movcal = (tb->flags & TB_FLAG_PENDING_MOVCA); -#ifdef DEBUG_DISAS - qemu_log_mask(CPU_LOG_TB_CPU, - "------------------------------------------------\n"); - log_cpu_state_mask(CPU_LOG_TB_CPU, env, 0); -#endif - ii = -1; num_insns = 0; max_insns = tb->cflags & CF_COUNT_MASK; diff -Nru qemu-kvm-0.12.5+noroms/target-sparc/cpu.h qemu-kvm-0.14.1/target-sparc/cpu.h --- qemu-kvm-0.12.5+noroms/target-sparc/cpu.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-sparc/cpu.h 2011-05-11 13:29:46.000000000 +0000 @@ -2,15 +2,24 @@ #define CPU_SPARC_H #include "config.h" +#include "qemu-common.h" #if !defined(TARGET_SPARC64) #define TARGET_LONG_BITS 32 #define TARGET_FPREGS 32 #define TARGET_PAGE_BITS 12 /* 4k */ +#define TARGET_PHYS_ADDR_SPACE_BITS 36 +#define TARGET_VIRT_ADDR_SPACE_BITS 32 #else #define TARGET_LONG_BITS 64 #define TARGET_FPREGS 64 #define TARGET_PAGE_BITS 13 /* 8k */ +#define TARGET_PHYS_ADDR_SPACE_BITS 41 +# ifdef TARGET_ABI32 +# define TARGET_VIRT_ADDR_SPACE_BITS 32 +# else +# define TARGET_VIRT_ADDR_SPACE_BITS 44 +# endif #endif #define CPUState struct CPUSPARCState @@ -71,7 +80,7 @@ #define TT_DPROT 0x6c #define TT_SPILL 0x80 #define TT_FILL 0xc0 -#define TT_WOTHER 0x10 +#define TT_WOTHER (1 << 5) #define TT_TRAP 0x100 #endif @@ -84,12 +93,14 @@ #define PSR_CARRY_SHIFT 20 #define PSR_CARRY (1 << PSR_CARRY_SHIFT) #define PSR_ICC (PSR_NEG|PSR_ZERO|PSR_OVF|PSR_CARRY) +#if !defined(TARGET_SPARC64) #define PSR_EF (1<<12) #define PSR_PIL 0xf00 #define PSR_S (1<<7) #define PSR_PS (1<<6) #define PSR_ET (1<<5) #define PSR_CWP 0x1f +#endif #define CC_SRC (env->cc_src) #define CC_SRC2 (env->cc_src2) @@ -216,7 +227,7 @@ #if !defined(TARGET_SPARC64) #define NB_MMU_MODES 2 #else -#define NB_MMU_MODES 3 +#define NB_MMU_MODES 6 typedef struct trap_state { uint64_t tpc; uint64_t tnpc; @@ -241,20 +252,24 @@ uint32_t maxtl; } sparc_def_t; -#define CPU_FEATURE_FLOAT (1 << 0) -#define CPU_FEATURE_FLOAT128 (1 << 1) -#define CPU_FEATURE_SWAP (1 << 2) -#define CPU_FEATURE_MUL (1 << 3) -#define CPU_FEATURE_DIV (1 << 4) -#define CPU_FEATURE_FLUSH (1 << 5) -#define CPU_FEATURE_FSQRT (1 << 6) -#define CPU_FEATURE_FMUL (1 << 7) -#define CPU_FEATURE_VIS1 (1 << 8) -#define CPU_FEATURE_VIS2 (1 << 9) -#define CPU_FEATURE_FSMULD (1 << 10) -#define CPU_FEATURE_HYPV (1 << 11) -#define CPU_FEATURE_CMT (1 << 12) -#define CPU_FEATURE_GL (1 << 13) +#define CPU_FEATURE_FLOAT (1 << 0) +#define CPU_FEATURE_FLOAT128 (1 << 1) +#define CPU_FEATURE_SWAP (1 << 2) +#define CPU_FEATURE_MUL (1 << 3) +#define CPU_FEATURE_DIV (1 << 4) +#define CPU_FEATURE_FLUSH (1 << 5) +#define CPU_FEATURE_FSQRT (1 << 6) +#define CPU_FEATURE_FMUL (1 << 7) +#define CPU_FEATURE_VIS1 (1 << 8) +#define CPU_FEATURE_VIS2 (1 << 9) +#define CPU_FEATURE_FSMULD (1 << 10) +#define CPU_FEATURE_HYPV (1 << 11) +#define CPU_FEATURE_CMT (1 << 12) +#define CPU_FEATURE_GL (1 << 13) +#define CPU_FEATURE_TA0_SHUTDOWN (1 << 14) /* Shutdown on "ta 0x0" */ +#define CPU_FEATURE_ASR17 (1 << 15) +#define CPU_FEATURE_CACHE_CTRL (1 << 16) + #ifndef TARGET_SPARC64 #define CPU_DEFAULT_FEATURES (CPU_FEATURE_FLOAT | CPU_FEATURE_SWAP | \ CPU_FEATURE_MUL | CPU_FEATURE_DIV | \ @@ -292,6 +307,22 @@ uint64_t tte; } SparcTLBEntry; +struct CPUTimer +{ + const char *name; + uint32_t frequency; + uint32_t disabled; + uint64_t disabled_mask; + int64_t clock_offset; + struct QEMUTimer *qtimer; +}; + +typedef struct CPUTimer CPUTimer; + +struct QEMUFile; +void cpu_put_timer(struct QEMUFile *f, CPUTimer *s); +void cpu_get_timer(struct QEMUFile *f, CPUTimer *s); + typedef struct CPUSPARCState { target_ulong gregs[8]; /* general registers */ target_ulong *regwptr; /* pointer to current register window */ @@ -317,14 +348,16 @@ uint32_t wim; /* window invalid mask */ #endif target_ulong tbr; /* trap base register */ +#if !defined(TARGET_SPARC64) int psrs; /* supervisor mode (extracted from PSR) */ int psrps; /* previous supervisor mode */ -#if !defined(TARGET_SPARC64) int psret; /* enable traps */ #endif uint32_t psrpil; /* interrupt blocking level */ uint32_t pil_in; /* incoming interrupt level bitmap */ +#if !defined(TARGET_SPARC64) int psref; /* enable fpu */ +#endif target_ulong version; int interrupt_index; uint32_t nwindows; @@ -393,31 +426,39 @@ uint64_t mgregs[8]; /* mmu general registers */ uint64_t fprs; uint64_t tick_cmpr, stick_cmpr; - void *tick, *stick; + CPUTimer *tick, *stick; +#define TICK_NPT_MASK 0x8000000000000000ULL +#define TICK_INT_DIS 0x8000000000000000ULL uint64_t gsr; uint32_t gl; // UA2005 /* UA 2005 hyperprivileged registers */ uint64_t hpstate, htstate[MAXTL_MAX], hintp, htba, hver, hstick_cmpr, ssr; - void *hstick; // UA 2005 + CPUTimer *hstick; // UA 2005 uint32_t softint; #define SOFTINT_TIMER 1 #define SOFTINT_STIMER (1 << 16) +#define SOFTINT_INTRMASK (0xFFFE) +#define SOFTINT_REG_MASK (SOFTINT_STIMER|SOFTINT_INTRMASK|SOFTINT_TIMER) #endif sparc_def_t *def; + + void *irq_manager; + void (*qemu_irq_ack) (void *irq_manager, int intno); + + /* Leon3 cache control */ + uint32_t cache_control; } CPUSPARCState; +#ifndef NO_CPU_IO_DEFS /* helper.c */ CPUSPARCState *cpu_sparc_init(const char *cpu_model); void cpu_sparc_set_id(CPUSPARCState *env, unsigned int cpu); -void sparc_cpu_list (FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, - ...)); -void cpu_lock(void); -void cpu_unlock(void); +void sparc_cpu_list(FILE *f, fprintf_function cpu_fprintf); int cpu_sparc_handle_mmu_fault(CPUSPARCState *env1, target_ulong address, int rw, int mmu_idx, int is_softmmu); #define cpu_handle_mmu_fault cpu_sparc_handle_mmu_fault target_ulong mmu_probe(CPUSPARCState *env, target_ulong address, int mmulev); -void dump_mmu(CPUSPARCState *env); +void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUState *env); /* translate.c */ void gen_intermediate_code_init(CPUSPARCState *env); @@ -425,105 +466,53 @@ /* cpu-exec.c */ int cpu_sparc_exec(CPUSPARCState *s); -#if !defined (TARGET_SPARC64) -#define GET_PSR(env) (env->version | (env->psr & PSR_ICC) | \ - (env->psref? PSR_EF : 0) | \ - (env->psrpil << 8) | \ - (env->psrs? PSR_S : 0) | \ - (env->psrps? PSR_PS : 0) | \ - (env->psret? PSR_ET : 0) | env->cwp) -#else -#define GET_PSR(env) (env->version | (env->psr & PSR_ICC) | \ - (env->psref? PSR_EF : 0) | \ - (env->psrpil << 8) | \ - (env->psrs? PSR_S : 0) | \ - (env->psrps? PSR_PS : 0) | \ - env->cwp) -#endif - -#ifndef NO_CPU_IO_DEFS +/* op_helper.c */ +target_ulong cpu_get_psr(CPUState *env1); +void cpu_put_psr(CPUState *env1, target_ulong val); +#ifdef TARGET_SPARC64 +target_ulong cpu_get_ccr(CPUState *env1); +void cpu_put_ccr(CPUState *env1, target_ulong val); +target_ulong cpu_get_cwp64(CPUState *env1); +void cpu_put_cwp64(CPUState *env1, int cwp); +#endif +int cpu_cwp_inc(CPUState *env1, int cwp); +int cpu_cwp_dec(CPUState *env1, int cwp); +void cpu_set_cwp(CPUState *env1, int new_cwp); +void leon3_irq_manager(void *irq_manager, int intno); -static inline int cpu_cwp_inc(CPUSPARCState *env1, int cwp) -{ - if (unlikely(cwp >= env1->nwindows)) - cwp -= env1->nwindows; - return cwp; -} +/* sun4m.c, sun4u.c */ +void cpu_check_irqs(CPUSPARCState *env); -static inline int cpu_cwp_dec(CPUSPARCState *env1, int cwp) -{ - if (unlikely(cwp < 0)) - cwp += env1->nwindows; - return cwp; -} -#endif +/* leon3.c */ +void leon3_irq_ack(void *irq_manager, int intno); -static inline void memcpy32(target_ulong *dst, const target_ulong *src) -{ - dst[0] = src[0]; - dst[1] = src[1]; - dst[2] = src[2]; - dst[3] = src[3]; - dst[4] = src[4]; - dst[5] = src[5]; - dst[6] = src[6]; - dst[7] = src[7]; -} +#if defined (TARGET_SPARC64) -static inline void cpu_set_cwp(CPUSPARCState *env1, int new_cwp) +static inline int compare_masked(uint64_t x, uint64_t y, uint64_t mask) { - /* put the modified wrap registers at their proper location */ - if (env1->cwp == env1->nwindows - 1) - memcpy32(env1->regbase, env1->regbase + env1->nwindows * 16); - env1->cwp = new_cwp; - /* put the wrap registers at their temporary location */ - if (new_cwp == env1->nwindows - 1) - memcpy32(env1->regbase + env1->nwindows * 16, env1->regbase); - env1->regwptr = env1->regbase + (new_cwp * 16); + return (x & mask) == (y & mask); } -/* sun4m.c, sun4u.c */ -void cpu_check_irqs(CPUSPARCState *env); +#define MMU_CONTEXT_BITS 13 +#define MMU_CONTEXT_MASK ((1 << MMU_CONTEXT_BITS) - 1) -static inline void PUT_PSR(CPUSPARCState *env1, target_ulong val) +static inline int tlb_compare_context(const SparcTLBEntry *tlb, + uint64_t context) { - env1->psr = val & PSR_ICC; - env1->psref = (val & PSR_EF)? 1 : 0; - env1->psrpil = (val & PSR_PIL) >> 8; -#if ((!defined (TARGET_SPARC64)) && !defined(CONFIG_USER_ONLY)) - cpu_check_irqs(env1); -#endif - env1->psrs = (val & PSR_S)? 1 : 0; - env1->psrps = (val & PSR_PS)? 1 : 0; -#if !defined (TARGET_SPARC64) - env1->psret = (val & PSR_ET)? 1 : 0; -#endif - cpu_set_cwp(env1, val & PSR_CWP); - env1->cc_op = CC_OP_FLAGS; + return compare_masked(context, tlb->tag, MMU_CONTEXT_MASK); } -#ifdef TARGET_SPARC64 -#define GET_CCR(env) (((env->xcc >> 20) << 4) | ((env->psr & PSR_ICC) >> 20)) -#define PUT_CCR(env, val) do { int _tmp = val; \ - env->xcc = (_tmp >> 4) << 20; \ - env->psr = (_tmp & 0xf) << 20; \ - CC_OP = CC_OP_FLAGS; \ - } while (0) -#define GET_CWP64(env) (env->nwindows - 1 - (env)->cwp) - -#ifndef NO_CPU_IO_DEFS -static inline void PUT_CWP64(CPUSPARCState *env1, int cwp) -{ - if (unlikely(cwp >= env1->nwindows || cwp < 0)) - cwp = 0; - cpu_set_cwp(env1, env1->nwindows - 1 - cwp); -} #endif #endif /* cpu-exec.c */ +#if !defined(CONFIG_USER_ONLY) void do_unassigned_access(target_phys_addr_t addr, int is_write, int is_exec, int is_asi, int size); +target_phys_addr_t cpu_get_phys_page_nofault(CPUState *env, target_ulong addr, + int mmu_idx); + +#endif int cpu_sparc_signal_handler(int host_signum, void *pinfo, void *puc); #define cpu_init cpu_sparc_init @@ -532,17 +521,45 @@ #define cpu_signal_handler cpu_sparc_signal_handler #define cpu_list sparc_cpu_list -#define CPU_SAVE_VERSION 5 +#define CPU_SAVE_VERSION 6 /* MMU modes definitions */ +#if defined (TARGET_SPARC64) +#define MMU_USER_IDX 0 #define MMU_MODE0_SUFFIX _user -#define MMU_MODE1_SUFFIX _kernel -#ifdef TARGET_SPARC64 -#define MMU_MODE2_SUFFIX _hypv -#endif +#define MMU_USER_SECONDARY_IDX 1 +#define MMU_MODE1_SUFFIX _user_secondary +#define MMU_KERNEL_IDX 2 +#define MMU_MODE2_SUFFIX _kernel +#define MMU_KERNEL_SECONDARY_IDX 3 +#define MMU_MODE3_SUFFIX _kernel_secondary +#define MMU_NUCLEUS_IDX 4 +#define MMU_MODE4_SUFFIX _nucleus +#define MMU_HYPV_IDX 5 +#define MMU_MODE5_SUFFIX _hypv +#else #define MMU_USER_IDX 0 +#define MMU_MODE0_SUFFIX _user #define MMU_KERNEL_IDX 1 -#define MMU_HYPV_IDX 2 +#define MMU_MODE1_SUFFIX _kernel +#endif + +#if defined (TARGET_SPARC64) +static inline int cpu_has_hypervisor(CPUState *env1) +{ + return env1->def->features & CPU_FEATURE_HYPV; +} + +static inline int cpu_hypervisor_mode(CPUState *env1) +{ + return cpu_has_hypervisor(env1) && (env1->hpstate & HS_PRIV); +} + +static inline int cpu_supervisor_mode(CPUState *env1) +{ + return env1->pstate & PS_PRIV; +} +#endif static inline int cpu_mmu_index(CPUState *env1) { @@ -551,12 +568,38 @@ #elif !defined(TARGET_SPARC64) return env1->psrs; #else - if (!env1->psrs) - return MMU_USER_IDX; - else if ((env1->hpstate & HS_PRIV) == 0) - return MMU_KERNEL_IDX; - else + if (env1->tl > 0) { + return MMU_NUCLEUS_IDX; + } else if (cpu_hypervisor_mode(env1)) { return MMU_HYPV_IDX; + } else if (cpu_supervisor_mode(env1)) { + return MMU_KERNEL_IDX; + } else { + return MMU_USER_IDX; + } +#endif +} + +static inline int cpu_interrupts_enabled(CPUState *env1) +{ +#if !defined (TARGET_SPARC64) + if (env1->psret != 0) + return 1; +#else + if (env1->pstate & PS_IE) + return 1; +#endif + + return 0; +} + +static inline int cpu_pil_allowed(CPUState *env1, int pil) +{ +#if !defined(TARGET_SPARC64) + /* level 15 is non-maskable on sparc v8 */ + return pil == 15 || pil > env1->psrpil; +#else + return pil > env1->psrpil; #endif } @@ -584,22 +627,15 @@ #endif #include "cpu-all.h" -#include "exec-all.h" #ifdef TARGET_SPARC64 /* sun4u.c */ -void cpu_tick_set_count(void *opaque, uint64_t count); -uint64_t cpu_tick_get_count(void *opaque); -void cpu_tick_set_limit(void *opaque, uint64_t limit); +void cpu_tick_set_count(CPUTimer *timer, uint64_t count); +uint64_t cpu_tick_get_count(CPUTimer *timer); +void cpu_tick_set_limit(CPUTimer *timer, uint64_t limit); trap_state* cpu_tsptr(CPUState* env); #endif -static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb) -{ - env->pc = tb->pc; - env->npc = tb->cs_base; -} - static inline void cpu_get_tb_cpu_state(CPUState *env, target_ulong *pc, target_ulong *cs_base, int *flags) { @@ -607,9 +643,13 @@ *cs_base = env->npc; #ifdef TARGET_SPARC64 // AM . Combined FPU enable bits . PRIV . DMMU enabled . IMMU enabled - *flags = ((env->pstate & PS_AM) << 2) - | (((env->pstate & PS_PEF) >> 1) | ((env->fprs & FPRS_FEF) << 2)) - | (env->pstate & PS_PRIV) | ((env->lsu & (DMMU_E | IMMU_E)) >> 2); + *flags = ((env->pstate & PS_AM) << 2) /* 5 */ + | (((env->pstate & PS_PEF) >> 1) /* 3 */ + | ((env->fprs & FPRS_FEF) << 2)) /* 4 */ + | (env->pstate & PS_PRIV) /* 2 */ + | ((env->lsu & (DMMU_E | IMMU_E)) >> 2) /* 1, 0 */ + | ((env->tl & 0xff) << 8) + | (env->dmmu.mmu_primary_context << 16); /* 16... */ #else // FPU enable . Supervisor *flags = (env->psref << 4) | env->psrs; diff -Nru qemu-kvm-0.12.5+noroms/target-sparc/exec.h qemu-kvm-0.14.1/target-sparc/exec.h --- qemu-kvm-0.12.5+noroms/target-sparc/exec.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-sparc/exec.h 2011-05-11 13:29:46.000000000 +0000 @@ -5,38 +5,16 @@ register struct CPUSPARCState *env asm(AREG0); -#define DT0 (env->dt0) -#define DT1 (env->dt1) -#define QT0 (env->qt0) -#define QT1 (env->qt1) - #include "cpu.h" #include "exec-all.h" -static inline void env_to_regs(void) -{ -} - -static inline void regs_to_env(void) -{ -} +#if !defined(CONFIG_USER_ONLY) +#include "softmmu_exec.h" +#endif /* !defined(CONFIG_USER_ONLY) */ /* op_helper.c */ void do_interrupt(CPUState *env); -static inline int cpu_interrupts_enabled(CPUState *env1) -{ -#if !defined (TARGET_SPARC64) - if (env1->psret != 0) - return 1; -#else - if (env1->pstate & PS_IE) - return 1; -#endif - - return 0; -} - static inline int cpu_has_work(CPUState *env1) { return (env1->interrupt_request & CPU_INTERRUPT_HARD) && @@ -54,4 +32,10 @@ return EXCP_HALTED; } +static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb) +{ + env->pc = tb->pc; + env->npc = tb->cs_base; +} + #endif diff -Nru qemu-kvm-0.12.5+noroms/target-sparc/helper.c qemu-kvm-0.14.1/target-sparc/helper.c --- qemu-kvm-0.12.5+noroms/target-sparc/helper.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-sparc/helper.c 2011-05-11 13:29:46.000000000 +0000 @@ -30,24 +30,17 @@ //#define DEBUG_MMU //#define DEBUG_FEATURES +#ifdef DEBUG_MMU +#define DPRINTF_MMU(fmt, ...) \ + do { printf("MMU: " fmt , ## __VA_ARGS__); } while (0) +#else +#define DPRINTF_MMU(fmt, ...) do {} while (0) +#endif + static int cpu_sparc_find_by_name(sparc_def_t *cpu_def, const char *cpu_model); /* Sparc MMU emulation */ -/* thread support */ - -static spinlock_t global_cpu_lock = SPIN_LOCK_UNLOCKED; - -void cpu_lock(void) -{ - spin_lock(&global_cpu_lock); -} - -void cpu_unlock(void) -{ - spin_unlock(&global_cpu_lock); -} - #if defined(CONFIG_USER_ONLY) int cpu_sparc_handle_mmu_fault(CPUState *env1, target_ulong address, int rw, @@ -102,19 +95,19 @@ static int get_physical_address(CPUState *env, target_phys_addr_t *physical, int *prot, int *access_index, - target_ulong address, int rw, int mmu_idx) + target_ulong address, int rw, int mmu_idx, + target_ulong *page_size) { int access_perms = 0; target_phys_addr_t pde_ptr; uint32_t pde; - target_ulong virt_addr; int error_code = 0, is_dirty, is_user; unsigned long page_offset; is_user = mmu_idx == MMU_USER_IDX; - virt_addr = address & TARGET_PAGE_MASK; if ((env->mmuregs[0] & MMU_E) == 0) { /* MMU disabled */ + *page_size = TARGET_PAGE_SIZE; // Boot mode: instruction fetches are taken from PROM if (rw == 2 && (env->mmuregs[0] & env->def->mmu_bm)) { *physical = env->prom_addr | (address & 0x7ffffULL); @@ -174,22 +167,28 @@ case 3: /* Reserved */ return (3 << 8) | (4 << 2); case 2: /* L3 PTE */ - virt_addr = address & TARGET_PAGE_MASK; page_offset = (address & TARGET_PAGE_MASK) & (TARGET_PAGE_SIZE - 1); } + *page_size = TARGET_PAGE_SIZE; break; case 2: /* L2 PTE */ - virt_addr = address & ~0x3ffff; page_offset = address & 0x3ffff; + *page_size = 0x40000; } break; case 2: /* L1 PTE */ - virt_addr = address & ~0xffffff; page_offset = address & 0xffffff; + *page_size = 0x1000000; } } + /* check access */ + access_perms = (pde & PTE_ACCESS_MASK) >> PTE_ACCESS_SHIFT; + error_code = access_table[*access_index][access_perms]; + if (error_code && !((env->mmuregs[0] & MMU_NF) && is_user)) + return error_code; + /* update page modified and dirty bits */ is_dirty = (rw & 1) && !(pde & PG_MODIFIED_MASK); if (!(pde & PG_ACCESSED_MASK) || is_dirty) { @@ -198,11 +197,6 @@ pde |= PG_MODIFIED_MASK; stl_phys_notdirty(pde_ptr, pde); } - /* check access */ - access_perms = (pde & PTE_ACCESS_MASK) >> PTE_ACCESS_SHIFT; - error_code = access_table[*access_index][access_perms]; - if (error_code && !((env->mmuregs[0] & MMU_NF) && is_user)) - return error_code; /* the page can be put in the TLB */ *prot = perm_table[is_user][access_perms]; @@ -224,10 +218,11 @@ { target_phys_addr_t paddr; target_ulong vaddr; - int error_code = 0, prot, ret = 0, access_index; + target_ulong page_size; + int error_code = 0, prot, access_index; error_code = get_physical_address(env, &paddr, &prot, &access_index, - address, rw, mmu_idx); + address, rw, mmu_idx, &page_size); if (error_code == 0) { vaddr = address & TARGET_PAGE_MASK; paddr &= TARGET_PAGE_MASK; @@ -235,8 +230,8 @@ printf("Translate at " TARGET_FMT_lx " -> " TARGET_FMT_plx ", vaddr " TARGET_FMT_lx "\n", address, paddr, vaddr); #endif - ret = tlb_set_page_exec(env, vaddr, paddr, prot, mmu_idx, is_softmmu); - return ret; + tlb_set_page(env, vaddr, paddr, prot, mmu_idx, page_size); + return 0; } if (env->mmuregs[3]) /* Fault status register */ @@ -251,8 +246,8 @@ // switching to normal mode. vaddr = address & TARGET_PAGE_MASK; prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; - ret = tlb_set_page_exec(env, vaddr, paddr, prot, mmu_idx, is_softmmu); - return ret; + tlb_set_page(env, vaddr, paddr, prot, mmu_idx, TARGET_PAGE_SIZE); + return 0; } else { if (rw & 2) env->exception_index = TT_TFAULT; @@ -325,47 +320,45 @@ return 0; } -#ifdef DEBUG_MMU -void dump_mmu(CPUState *env) +void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUState *env) { target_ulong va, va1, va2; unsigned int n, m, o; target_phys_addr_t pde_ptr, pa; uint32_t pde; - printf("MMU dump:\n"); pde_ptr = (env->mmuregs[1] << 4) + (env->mmuregs[2] << 2); pde = ldl_phys(pde_ptr); - printf("Root ptr: " TARGET_FMT_plx ", ctx: %d\n", - (target_phys_addr_t)env->mmuregs[1] << 4, env->mmuregs[2]); + (*cpu_fprintf)(f, "Root ptr: " TARGET_FMT_plx ", ctx: %d\n", + (target_phys_addr_t)env->mmuregs[1] << 4, env->mmuregs[2]); for (n = 0, va = 0; n < 256; n++, va += 16 * 1024 * 1024) { pde = mmu_probe(env, va, 2); if (pde) { pa = cpu_get_phys_page_debug(env, va); - printf("VA: " TARGET_FMT_lx ", PA: " TARGET_FMT_plx - " PDE: " TARGET_FMT_lx "\n", va, pa, pde); + (*cpu_fprintf)(f, "VA: " TARGET_FMT_lx ", PA: " TARGET_FMT_plx + " PDE: " TARGET_FMT_lx "\n", va, pa, pde); for (m = 0, va1 = va; m < 64; m++, va1 += 256 * 1024) { pde = mmu_probe(env, va1, 1); if (pde) { pa = cpu_get_phys_page_debug(env, va1); - printf(" VA: " TARGET_FMT_lx ", PA: " TARGET_FMT_plx - " PDE: " TARGET_FMT_lx "\n", va1, pa, pde); + (*cpu_fprintf)(f, " VA: " TARGET_FMT_lx ", PA: " + TARGET_FMT_plx " PDE: " TARGET_FMT_lx "\n", + va1, pa, pde); for (o = 0, va2 = va1; o < 64; o++, va2 += 4 * 1024) { pde = mmu_probe(env, va2, 0); if (pde) { pa = cpu_get_phys_page_debug(env, va2); - printf(" VA: " TARGET_FMT_lx ", PA: " - TARGET_FMT_plx " PTE: " TARGET_FMT_lx "\n", - va2, pa, pde); + (*cpu_fprintf)(f, " VA: " TARGET_FMT_lx ", PA: " + TARGET_FMT_plx " PTE: " + TARGET_FMT_lx "\n", + va2, pa, pde); } } } } } } - printf("MMU dump ends\n"); } -#endif /* DEBUG_MMU */ #else /* !TARGET_SPARC64 */ @@ -379,17 +372,11 @@ * UltraSparc IIi I/DMMUs */ -static inline int compare_masked(uint64_t x, uint64_t y, uint64_t mask) -{ - return (x & mask) == (y & mask); -} - // Returns true if TTE tag is valid and matches virtual address value in context // requires virtual address mask value calculated from TTE entry size static inline int ultrasparc_tag_match(SparcTLBEntry *tlb, uint64_t address, uint64_t context, - target_phys_addr_t *physical, - int is_nucleus) + target_phys_addr_t *physical) { uint64_t mask; @@ -411,8 +398,7 @@ // valid, context match, virtual address match? if (TTE_IS_VALID(tlb->tte) && - ((is_nucleus && compare_masked(0, tlb->tag, 0x1fff)) - || TTE_IS_GLOBAL(tlb->tte) || compare_masked(context, tlb->tag, 0x1fff)) + (TTE_IS_GLOBAL(tlb->tte) || tlb_compare_context(tlb, context)) && compare_masked(address, tlb->tag, mask)) { // decode physical address @@ -425,11 +411,13 @@ static int get_physical_address_data(CPUState *env, target_phys_addr_t *physical, int *prot, - target_ulong address, int rw, int is_user) + target_ulong address, int rw, int mmu_idx) { unsigned int i; uint64_t context; - int is_nucleus; + + int is_user = (mmu_idx == MMU_USER_IDX || + mmu_idx == MMU_USER_SECONDARY_IDX); if ((env->lsu & DMMU_E) == 0) { /* DMMU disabled */ *physical = ultrasparc_truncate_physical(address); @@ -437,48 +425,70 @@ return 0; } - context = env->dmmu.mmu_primary_context & 0x1fff; - is_nucleus = env->tl > 0; + switch(mmu_idx) { + case MMU_USER_IDX: + case MMU_KERNEL_IDX: + context = env->dmmu.mmu_primary_context & 0x1fff; + break; + case MMU_USER_SECONDARY_IDX: + case MMU_KERNEL_SECONDARY_IDX: + context = env->dmmu.mmu_secondary_context & 0x1fff; + break; + case MMU_NUCLEUS_IDX: + default: + context = 0; + break; + } for (i = 0; i < 64; i++) { // ctx match, vaddr match, valid? - if (ultrasparc_tag_match(&env->dtlb[i], - address, context, physical, - is_nucleus)) { + if (ultrasparc_tag_match(&env->dtlb[i], address, context, physical)) { + + uint8_t fault_type = 0; + // access ok? - if (((env->dtlb[i].tte & 0x4) && is_user) || - (!(env->dtlb[i].tte & 0x2) && (rw == 1))) { - uint8_t fault_type = 0; + if ((env->dtlb[i].tte & 0x4) && is_user) { + fault_type |= 1; /* privilege violation */ + env->exception_index = TT_DFAULT; - if ((env->dtlb[i].tte & 0x4) && is_user) { - fault_type |= 1; /* privilege violation */ - } + DPRINTF_MMU("DFAULT at %" PRIx64 " context %" PRIx64 + " mmu_idx=%d tl=%d\n", + address, context, mmu_idx, env->tl); + } else if (!(env->dtlb[i].tte & 0x2) && (rw == 1)) { + env->exception_index = TT_DPROT; + + DPRINTF_MMU("DPROT at %" PRIx64 " context %" PRIx64 + " mmu_idx=%d tl=%d\n", + address, context, mmu_idx, env->tl); + } else { + *prot = PAGE_READ; + if (env->dtlb[i].tte & 0x2) + *prot |= PAGE_WRITE; + + TTE_SET_USED(env->dtlb[i].tte); - if (env->dmmu.sfsr & 1) /* Fault status register */ - env->dmmu.sfsr = 2; /* overflow (not read before + return 0; + } + + if (env->dmmu.sfsr & 1) /* Fault status register */ + env->dmmu.sfsr = 2; /* overflow (not read before another fault) */ - env->dmmu.sfsr |= (is_user << 3) | ((rw == 1) << 2) | 1; + env->dmmu.sfsr |= (is_user << 3) | ((rw == 1) << 2) | 1; - env->dmmu.sfsr |= (fault_type << 7); + env->dmmu.sfsr |= (fault_type << 7); - env->dmmu.sfar = address; /* Fault address register */ - env->exception_index = TT_DFAULT; -#ifdef DEBUG_MMU - printf("DFAULT at 0x%" PRIx64 "\n", address); -#endif - return 1; - } - *prot = PAGE_READ; - if (env->dtlb[i].tte & 0x2) - *prot |= PAGE_WRITE; - TTE_SET_USED(env->dtlb[i].tte); - return 0; + env->dmmu.sfar = address; /* Fault address register */ + + env->dmmu.tag_access = (address & ~0x1fffULL) | context; + + return 1; } } -#ifdef DEBUG_MMU - printf("DMISS at 0x%" PRIx64 "\n", address); -#endif + + DPRINTF_MMU("DMISS at %" PRIx64 " context %" PRIx64 "\n", + address, context); + env->dmmu.tag_access = (address & ~0x1fffULL) | context; env->exception_index = TT_DMISS; return 1; @@ -486,11 +496,13 @@ static int get_physical_address_code(CPUState *env, target_phys_addr_t *physical, int *prot, - target_ulong address, int is_user) + target_ulong address, int mmu_idx) { unsigned int i; uint64_t context; - int is_nucleus; + + int is_user = (mmu_idx == MMU_USER_IDX || + mmu_idx == MMU_USER_SECONDARY_IDX); if ((env->lsu & IMMU_E) == 0 || (env->pstate & PS_RED) != 0) { /* IMMU disabled */ @@ -499,14 +511,18 @@ return 0; } - context = env->dmmu.mmu_primary_context & 0x1fff; - is_nucleus = env->tl > 0; + if (env->tl == 0) { + /* PRIMARY context */ + context = env->dmmu.mmu_primary_context & 0x1fff; + } else { + /* NUCLEUS context */ + context = 0; + } for (i = 0; i < 64; i++) { // ctx match, vaddr match, valid? if (ultrasparc_tag_match(&env->itlb[i], - address, context, physical, - is_nucleus)) { + address, context, physical)) { // access ok? if ((env->itlb[i].tte & 0x4) && is_user) { if (env->immu.sfsr) /* Fault status register */ @@ -514,9 +530,12 @@ another fault) */ env->immu.sfsr |= (is_user << 3) | 1; env->exception_index = TT_TFAULT; -#ifdef DEBUG_MMU - printf("TFAULT at 0x%" PRIx64 "\n", address); -#endif + + env->immu.tag_access = (address & ~0x1fffULL) | context; + + DPRINTF_MMU("TFAULT at %" PRIx64 " context %" PRIx64 "\n", + address, context); + return 1; } *prot = PAGE_EXEC; @@ -524,9 +543,10 @@ return 0; } } -#ifdef DEBUG_MMU - printf("TMISS at 0x%" PRIx64 "\n", address); -#endif + + DPRINTF_MMU("TMISS at %" PRIx64 " context %" PRIx64 "\n", + address, context); + /* Context is stored in DMMU (dmmuregs[1]) also for IMMU */ env->immu.tag_access = (address & ~0x1fffULL) | context; env->exception_index = TT_TMISS; @@ -535,16 +555,35 @@ static int get_physical_address(CPUState *env, target_phys_addr_t *physical, int *prot, int *access_index, - target_ulong address, int rw, int mmu_idx) + target_ulong address, int rw, int mmu_idx, + target_ulong *page_size) { - int is_user = mmu_idx == MMU_USER_IDX; + /* ??? We treat everything as a small page, then explicitly flush + everything when an entry is evicted. */ + *page_size = TARGET_PAGE_SIZE; + +#if defined (DEBUG_MMU) + /* safety net to catch wrong softmmu index use from dynamic code */ + if (env->tl > 0 && mmu_idx != MMU_NUCLEUS_IDX) { + DPRINTF_MMU("get_physical_address %s tl=%d mmu_idx=%d" + " primary context=%" PRIx64 + " secondary context=%" PRIx64 + " address=%" PRIx64 + "\n", + (rw == 2 ? "CODE" : "DATA"), + env->tl, mmu_idx, + env->dmmu.mmu_primary_context, + env->dmmu.mmu_secondary_context, + address); + } +#endif if (rw == 2) return get_physical_address_code(env, physical, prot, address, - is_user); + mmu_idx); else return get_physical_address_data(env, physical, prot, address, rw, - is_user); + mmu_idx); } /* Perform address translation */ @@ -553,37 +592,47 @@ { target_ulong virt_addr, vaddr; target_phys_addr_t paddr; - int error_code = 0, prot, ret = 0, access_index; + target_ulong page_size; + int error_code = 0, prot, access_index; error_code = get_physical_address(env, &paddr, &prot, &access_index, - address, rw, mmu_idx); + address, rw, mmu_idx, &page_size); if (error_code == 0) { virt_addr = address & TARGET_PAGE_MASK; vaddr = virt_addr + ((address & TARGET_PAGE_MASK) & (TARGET_PAGE_SIZE - 1)); -#ifdef DEBUG_MMU - printf("Translate at 0x%" PRIx64 " -> 0x%" PRIx64 ", vaddr 0x%" PRIx64 - "\n", address, paddr, vaddr); -#endif - ret = tlb_set_page_exec(env, vaddr, paddr, prot, mmu_idx, is_softmmu); - return ret; + + DPRINTF_MMU("Translate at %" PRIx64 " -> %" PRIx64 "," + " vaddr %" PRIx64 + " mmu_idx=%d" + " tl=%d" + " primary context=%" PRIx64 + " secondary context=%" PRIx64 + "\n", + address, paddr, vaddr, mmu_idx, env->tl, + env->dmmu.mmu_primary_context, + env->dmmu.mmu_secondary_context); + + tlb_set_page(env, vaddr, paddr, prot, mmu_idx, page_size); + return 0; } // XXX return 1; } -#ifdef DEBUG_MMU -void dump_mmu(CPUState *env) +void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUState *env) { unsigned int i; const char *mask; - printf("MMU contexts: Primary: %" PRId64 ", Secondary: %" PRId64 "\n", - env->dmmu.mmu_primary_context, env->dmmu.mmu_secondary_context); + (*cpu_fprintf)(f, "MMU contexts: Primary: %" PRId64 ", Secondary: %" + PRId64 "\n", + env->dmmu.mmu_primary_context, + env->dmmu.mmu_secondary_context); if ((env->lsu & DMMU_E) == 0) { - printf("DMMU disabled\n"); + (*cpu_fprintf)(f, "DMMU disabled\n"); } else { - printf("DMMU dump:\n"); + (*cpu_fprintf)(f, "DMMU dump\n"); for (i = 0; i < 64; i++) { switch ((env->dtlb[i].tte >> 61) & 3) { default: @@ -601,24 +650,25 @@ break; } if ((env->dtlb[i].tte & 0x8000000000000000ULL) != 0) { - printf("[%02u] VA: %" PRIx64 ", PA: %" PRIx64 - ", %s, %s, %s, %s, ctx %" PRId64 " %s\n", - i, - env->dtlb[i].tag & (uint64_t)~0x1fffULL, - env->dtlb[i].tte & (uint64_t)0x1ffffffe000ULL, - mask, - env->dtlb[i].tte & 0x4? "priv": "user", - env->dtlb[i].tte & 0x2? "RW": "RO", - env->dtlb[i].tte & 0x40? "locked": "unlocked", - env->dtlb[i].tag & (uint64_t)0x1fffULL, - TTE_IS_GLOBAL(env->dtlb[i].tag)? "global" : "local"); + (*cpu_fprintf)(f, "[%02u] VA: %" PRIx64 ", PA: %" PRIx64 + ", %s, %s, %s, %s, ctx %" PRId64 " %s\n", + i, + env->dtlb[i].tag & (uint64_t)~0x1fffULL, + env->dtlb[i].tte & (uint64_t)0x1ffffffe000ULL, + mask, + env->dtlb[i].tte & 0x4? "priv": "user", + env->dtlb[i].tte & 0x2? "RW": "RO", + env->dtlb[i].tte & 0x40? "locked": "unlocked", + env->dtlb[i].tag & (uint64_t)0x1fffULL, + TTE_IS_GLOBAL(env->dtlb[i].tte)? + "global" : "local"); } } } if ((env->lsu & IMMU_E) == 0) { - printf("IMMU disabled\n"); + (*cpu_fprintf)(f, "IMMU disabled\n"); } else { - printf("IMMU dump:\n"); + (*cpu_fprintf)(f, "IMMU dump\n"); for (i = 0; i < 64; i++) { switch ((env->itlb[i].tte >> 61) & 3) { default: @@ -636,47 +686,48 @@ break; } if ((env->itlb[i].tte & 0x8000000000000000ULL) != 0) { - printf("[%02u] VA: %" PRIx64 ", PA: %" PRIx64 - ", %s, %s, %s, ctx %" PRId64 " %s\n", - i, - env->itlb[i].tag & (uint64_t)~0x1fffULL, - env->itlb[i].tte & (uint64_t)0x1ffffffe000ULL, - mask, - env->itlb[i].tte & 0x4? "priv": "user", - env->itlb[i].tte & 0x40? "locked": "unlocked", - env->itlb[i].tag & (uint64_t)0x1fffULL, - TTE_IS_GLOBAL(env->itlb[i].tag)? "global" : "local"); + (*cpu_fprintf)(f, "[%02u] VA: %" PRIx64 ", PA: %" PRIx64 + ", %s, %s, %s, ctx %" PRId64 " %s\n", + i, + env->itlb[i].tag & (uint64_t)~0x1fffULL, + env->itlb[i].tte & (uint64_t)0x1ffffffe000ULL, + mask, + env->itlb[i].tte & 0x4? "priv": "user", + env->itlb[i].tte & 0x40? "locked": "unlocked", + env->itlb[i].tag & (uint64_t)0x1fffULL, + TTE_IS_GLOBAL(env->itlb[i].tte)? + "global" : "local"); } } } } -#endif /* DEBUG_MMU */ #endif /* TARGET_SPARC64 */ #endif /* !CONFIG_USER_ONLY */ -#if defined(CONFIG_USER_ONLY) -target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr) -{ - return addr; -} - -#else -target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr) +#if !defined(CONFIG_USER_ONLY) +target_phys_addr_t cpu_get_phys_page_nofault(CPUState *env, target_ulong addr, + int mmu_idx) { target_phys_addr_t phys_addr; + target_ulong page_size; int prot, access_index; if (get_physical_address(env, &phys_addr, &prot, &access_index, addr, 2, - MMU_KERNEL_IDX) != 0) + mmu_idx, &page_size) != 0) if (get_physical_address(env, &phys_addr, &prot, &access_index, addr, - 0, MMU_KERNEL_IDX) != 0) + 0, mmu_idx, &page_size) != 0) return -1; if (cpu_get_physical_page_desc(phys_addr) == IO_MEM_UNASSIGNED) return -1; return phys_addr; } + +target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr) +{ + return cpu_get_phys_page_nofault(env, addr, cpu_mmu_index(env)); +} #endif void cpu_reset(CPUSPARCState *env) @@ -703,12 +754,12 @@ #else #if !defined(TARGET_SPARC64) env->psret = 0; -#endif env->psrs = 1; env->psrps = 1; +#endif #ifdef TARGET_SPARC64 env->pstate = PS_PRIV|PS_RED|PS_PEF|PS_AG; - env->hpstate = HS_PRIV; + env->hpstate = cpu_has_hypervisor(env) ? HS_PRIV : 0; env->tl = env->maxtl; cpu_tsptr(env)->tt = TT_POWER_ON_RESET; env->lsu = 0; @@ -719,6 +770,7 @@ env->pc = 0; env->npc = env->pc + 4; #endif + env->cache_control = 0; } static int cpu_sparc_register(CPUSPARCState *env, const char *cpu_model) @@ -1223,20 +1275,21 @@ .mmu_sfsr_mask = 0xffffffff, .mmu_trcr_mask = 0xffffffff, .nwindows = 8, - .features = CPU_DEFAULT_FEATURES, + .features = CPU_DEFAULT_FEATURES | CPU_FEATURE_TA0_SHUTDOWN, }, { .name = "LEON3", .iu_version = 0xf3000000, .fpu_version = 4 << 17, /* FPU version 4 (Meiko) */ .mmu_version = 0xf3000000, - .mmu_bm = 0x00004000, + .mmu_bm = 0x00000000, .mmu_ctpr_mask = 0x007ffff0, .mmu_cxr_mask = 0x0000003f, .mmu_sfsr_mask = 0xffffffff, .mmu_trcr_mask = 0xffffffff, .nwindows = 8, - .features = CPU_DEFAULT_FEATURES, + .features = CPU_DEFAULT_FEATURES | CPU_FEATURE_TA0_SHUTDOWN | + CPU_FEATURE_ASR17 | CPU_FEATURE_CACHE_CTRL, }, #endif }; @@ -1258,8 +1311,7 @@ "gl", }; -static void print_features(FILE *f, - int (*cpu_fprintf)(FILE *f, const char *fmt, ...), +static void print_features(FILE *f, fprintf_function cpu_fprintf, uint32_t features, const char *prefix) { unsigned int i; @@ -1292,7 +1344,7 @@ char *featurestr, *name = strtok(s, ","); uint32_t plus_features = 0; uint32_t minus_features = 0; - long long iu_version; + uint64_t iu_version; uint32_t fpu_version, mmu_version, nwindows; for (i = 0; i < ARRAY_SIZE(sparc_defs); i++) { @@ -1324,7 +1376,7 @@ } cpu_def->iu_version = iu_version; #ifdef DEBUG_FEATURES - fprintf(stderr, "iu_version %llx\n", iu_version); + fprintf(stderr, "iu_version %" PRIx64 "\n", iu_version); #endif } else if (!strcmp(featurestr, "fpu_version")) { char *err; @@ -1387,7 +1439,7 @@ return -1; } -void sparc_cpu_list(FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...)) +void sparc_cpu_list(FILE *f, fprintf_function cpu_fprintf) { unsigned int i; @@ -1414,8 +1466,21 @@ "fpu_version mmu_version nwindows\n"); } -void cpu_dump_state(CPUState *env, FILE *f, - int (*cpu_fprintf)(FILE *f, const char *fmt, ...), +static void cpu_print_cc(FILE *f, fprintf_function cpu_fprintf, + uint32_t cc) +{ + cpu_fprintf(f, "%c%c%c%c", cc & PSR_NEG? 'N' : '-', + cc & PSR_ZERO? 'Z' : '-', cc & PSR_OVF? 'V' : '-', + cc & PSR_CARRY? 'C' : '-'); +} + +#ifdef TARGET_SPARC64 +#define REGS_PER_LINE 4 +#else +#define REGS_PER_LINE 8 +#endif + +void cpu_dump_state(CPUState *env, FILE *f, fprintf_function cpu_fprintf, int flags) { int i, x; @@ -1423,26 +1488,32 @@ cpu_fprintf(f, "pc: " TARGET_FMT_lx " npc: " TARGET_FMT_lx "\n", env->pc, env->npc); cpu_fprintf(f, "General Registers:\n"); - for (i = 0; i < 4; i++) - cpu_fprintf(f, "%%g%c: " TARGET_FMT_lx "\t", i + '0', env->gregs[i]); - cpu_fprintf(f, "\n"); - for (; i < 8; i++) - cpu_fprintf(f, "%%g%c: " TARGET_FMT_lx "\t", i + '0', env->gregs[i]); + + for (i = 0; i < 8; i++) { + if (i % REGS_PER_LINE == 0) { + cpu_fprintf(f, "%%g%d-%d:", i, i + REGS_PER_LINE - 1); + } + cpu_fprintf(f, " " TARGET_FMT_lx, env->gregs[i]); + if (i % REGS_PER_LINE == REGS_PER_LINE - 1) { + cpu_fprintf(f, "\n"); + } + } cpu_fprintf(f, "\nCurrent Register Window:\n"); for (x = 0; x < 3; x++) { - for (i = 0; i < 4; i++) - cpu_fprintf(f, "%%%c%d: " TARGET_FMT_lx "\t", - (x == 0 ? 'o' : (x == 1 ? 'l' : 'i')), i, - env->regwptr[i + x * 8]); - cpu_fprintf(f, "\n"); - for (; i < 8; i++) - cpu_fprintf(f, "%%%c%d: " TARGET_FMT_lx "\t", - (x == 0 ? 'o' : x == 1 ? 'l' : 'i'), i, - env->regwptr[i + x * 8]); - cpu_fprintf(f, "\n"); + for (i = 0; i < 8; i++) { + if (i % REGS_PER_LINE == 0) { + cpu_fprintf(f, "%%%c%d-%d: ", + x == 0 ? 'o' : (x == 1 ? 'l' : 'i'), + i, i + REGS_PER_LINE - 1); + } + cpu_fprintf(f, TARGET_FMT_lx " ", env->regwptr[i + x * 8]); + if (i % REGS_PER_LINE == REGS_PER_LINE - 1) { + cpu_fprintf(f, "\n"); + } + } } cpu_fprintf(f, "\nFloating Point Registers:\n"); - for (i = 0; i < 32; i++) { + for (i = 0; i < TARGET_FPREGS; i++) { if ((i & 3) == 0) cpu_fprintf(f, "%%f%02d:", i); cpu_fprintf(f, " %016f", *(float *)&env->fpr[i]); @@ -1450,21 +1521,26 @@ cpu_fprintf(f, "\n"); } #ifdef TARGET_SPARC64 - cpu_fprintf(f, "pstate: 0x%08x ccr: 0x%02x asi: 0x%02x tl: %d fprs: %d\n", - env->pstate, GET_CCR(env), env->asi, env->tl, env->fprs); - cpu_fprintf(f, "cansave: %d canrestore: %d otherwin: %d wstate %d " - "cleanwin %d cwp %d\n", + cpu_fprintf(f, "pstate: %08x ccr: %02x (icc: ", env->pstate, + (unsigned)cpu_get_ccr(env)); + cpu_print_cc(f, cpu_fprintf, cpu_get_ccr(env) << PSR_CARRY_SHIFT); + cpu_fprintf(f, " xcc: "); + cpu_print_cc(f, cpu_fprintf, cpu_get_ccr(env) << (PSR_CARRY_SHIFT - 4)); + cpu_fprintf(f, ") asi: %02x tl: %d pil: %x\n", env->asi, env->tl, + env->psrpil); + cpu_fprintf(f, "cansave: %d canrestore: %d otherwin: %d wstate: %d " + "cleanwin: %d cwp: %d\n", env->cansave, env->canrestore, env->otherwin, env->wstate, env->cleanwin, env->nwindows - 1 - env->cwp); + cpu_fprintf(f, "fsr: " TARGET_FMT_lx " y: " TARGET_FMT_lx " fprs: " + TARGET_FMT_lx "\n", env->fsr, env->y, env->fprs); #else - -#define GET_FLAG(a,b) ((env->psr & a)?b:'-') - - cpu_fprintf(f, "psr: 0x%08x -> %c%c%c%c %c%c%c wim: 0x%08x\n", - GET_PSR(env), GET_FLAG(PSR_ZERO, 'Z'), GET_FLAG(PSR_OVF, 'V'), - GET_FLAG(PSR_NEG, 'N'), GET_FLAG(PSR_CARRY, 'C'), - env->psrs?'S':'-', env->psrps?'P':'-', - env->psret?'E':'-', env->wim); + cpu_fprintf(f, "psr: %08x (icc: ", cpu_get_psr(env)); + cpu_print_cc(f, cpu_fprintf, cpu_get_psr(env)); + cpu_fprintf(f, " SPE: %c%c%c) wim: %08x\n", env->psrs? 'S' : '-', + env->psrps? 'P' : '-', env->psret? 'E' : '-', + env->wim); + cpu_fprintf(f, "fsr: " TARGET_FMT_lx " y: " TARGET_FMT_lx "\n", + env->fsr, env->y); #endif - cpu_fprintf(f, "fsr: 0x%08x\n", env->fsr); } diff -Nru qemu-kvm-0.12.5+noroms/target-sparc/helper.h qemu-kvm-0.14.1/target-sparc/helper.h --- qemu-kvm-0.12.5+noroms/target-sparc/helper.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-sparc/helper.h 2011-05-11 13:29:46.000000000 +0000 @@ -5,6 +5,7 @@ DEF_HELPER_1(wrpsr, void, tl) DEF_HELPER_0(rdpsr, tl) #else +DEF_HELPER_1(wrpil, void, tl) DEF_HELPER_1(wrpstate, void, tl) DEF_HELPER_0(done, void) DEF_HELPER_0(retry, void) @@ -36,7 +37,9 @@ DEF_HELPER_0(restore, void) DEF_HELPER_1(flush, void, tl) DEF_HELPER_2(udiv, tl, tl, tl) +DEF_HELPER_2(udiv_cc, tl, tl, tl) DEF_HELPER_2(sdiv, tl, tl, tl) +DEF_HELPER_2(sdiv_cc, tl, tl, tl) DEF_HELPER_2(stdf, void, tl, int) DEF_HELPER_2(lddf, void, tl, int) DEF_HELPER_2(ldqf, void, tl, int) @@ -82,6 +85,7 @@ DEF_HELPER_0(fcmpeq_fcc3, void) #endif DEF_HELPER_1(raise_exception, void, int) +DEF_HELPER_0(shutdown, void) #define F_HELPER_0_0(name) DEF_HELPER_0(f ## name, void) #define F_HELPER_DQ_0_0(name) \ F_HELPER_0_0(name ## d); \ diff -Nru qemu-kvm-0.12.5+noroms/target-sparc/machine.c qemu-kvm-0.14.1/target-sparc/machine.c --- qemu-kvm-0.12.5+noroms/target-sparc/machine.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-sparc/machine.c 2011-05-11 13:29:46.000000000 +0000 @@ -33,7 +33,7 @@ qemu_put_betls(f, &env->pc); qemu_put_betls(f, &env->npc); qemu_put_betls(f, &env->y); - tmp = GET_PSR(env); + tmp = cpu_get_psr(env); qemu_put_be32(f, tmp); qemu_put_betls(f, &env->fsr); qemu_put_betls(f, &env->tbr); @@ -84,8 +84,8 @@ qemu_put_be64s(f, &env->fprs); qemu_put_be64s(f, &env->tick_cmpr); qemu_put_be64s(f, &env->stick_cmpr); - qemu_put_ptimer(f, env->tick); - qemu_put_ptimer(f, env->stick); + cpu_put_timer(f, env->tick); + cpu_put_timer(f, env->stick); qemu_put_be64s(f, &env->gsr); qemu_put_be32s(f, &env->gl); qemu_put_be64s(f, &env->hpstate); @@ -96,7 +96,7 @@ qemu_put_be64s(f, &env->hver); qemu_put_be64s(f, &env->hstick_cmpr); qemu_put_be64s(f, &env->ssr); - qemu_put_ptimer(f, env->hstick); + cpu_put_timer(f, env->hstick); #endif } @@ -106,7 +106,7 @@ int i; uint32_t tmp; - if (version_id != 5) + if (version_id < 6) return -EINVAL; for(i = 0; i < 8; i++) qemu_get_betls(f, &env->gregs[i]); @@ -130,7 +130,7 @@ tmp = qemu_get_be32(f); env->cwp = 0; /* needed to ensure that the wrapping registers are correctly updated */ - PUT_PSR(env, tmp); + cpu_put_psr(env, tmp); qemu_get_betls(f, &env->fsr); qemu_get_betls(f, &env->tbr); tmp = qemu_get_be32(f); @@ -180,8 +180,8 @@ qemu_get_be64s(f, &env->fprs); qemu_get_be64s(f, &env->tick_cmpr); qemu_get_be64s(f, &env->stick_cmpr); - qemu_get_ptimer(f, env->tick); - qemu_get_ptimer(f, env->stick); + cpu_get_timer(f, env->tick); + cpu_get_timer(f, env->stick); qemu_get_be64s(f, &env->gsr); qemu_get_be32s(f, &env->gl); qemu_get_be64s(f, &env->hpstate); @@ -192,7 +192,7 @@ qemu_get_be64s(f, &env->hver); qemu_get_be64s(f, &env->hstick_cmpr); qemu_get_be64s(f, &env->ssr); - qemu_get_ptimer(f, env->hstick); + cpu_get_timer(f, env->hstick); #endif tlb_flush(env, 1); return 0; diff -Nru qemu-kvm-0.12.5+noroms/target-sparc/op_helper.c qemu-kvm-0.14.1/target-sparc/op_helper.c --- qemu-kvm-0.12.5+noroms/target-sparc/op_helper.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-sparc/op_helper.c 2011-05-11 13:29:46.000000000 +0000 @@ -1,9 +1,7 @@ #include "exec.h" #include "host-utils.h" #include "helper.h" -#if !defined(CONFIG_USER_ONLY) -#include "softmmu_exec.h" -#endif /* !defined(CONFIG_USER_ONLY) */ +#include "sysemu.h" //#define DEBUG_MMU //#define DEBUG_MXCC @@ -11,6 +9,8 @@ //#define DEBUG_UNASSIGNED //#define DEBUG_ASI //#define DEBUG_PCALL +//#define DEBUG_PSTATE +//#define DEBUG_CACHE_CONTROL #ifdef DEBUG_MMU #define DPRINTF_MMU(fmt, ...) \ @@ -31,6 +31,20 @@ do { printf("ASI: " fmt , ## __VA_ARGS__); } while (0) #endif +#ifdef DEBUG_PSTATE +#define DPRINTF_PSTATE(fmt, ...) \ + do { printf("PSTATE: " fmt , ## __VA_ARGS__); } while (0) +#else +#define DPRINTF_PSTATE(fmt, ...) do {} while (0) +#endif + +#ifdef DEBUG_CACHE_CONTROL +#define DPRINTF_CACHE_CONTROL(fmt, ...) \ + do { printf("CACHE_CONTROL: " fmt , ## __VA_ARGS__); } while (0) +#else +#define DPRINTF_CACHE_CONTROL(fmt, ...) do {} while (0) +#endif + #ifdef TARGET_SPARC64 #ifndef TARGET_ABI32 #define AM_CHECK(env1) ((env1)->pstate & PS_AM) @@ -39,6 +53,37 @@ #endif #endif +#define DT0 (env->dt0) +#define DT1 (env->dt1) +#define QT0 (env->qt0) +#define QT1 (env->qt1) + +/* Leon3 cache control */ + +/* Cache control: emulate the behavior of cache control registers but without + any effect on the emulated */ + +#define CACHE_STATE_MASK 0x3 +#define CACHE_DISABLED 0x0 +#define CACHE_FROZEN 0x1 +#define CACHE_ENABLED 0x3 + +/* Cache Control register fields */ + +#define CACHE_CTRL_IF (1 << 4) /* Instruction Cache Freeze on Interrupt */ +#define CACHE_CTRL_DF (1 << 5) /* Data Cache Freeze on Interrupt */ +#define CACHE_CTRL_DP (1 << 14) /* Data cache flush pending */ +#define CACHE_CTRL_IP (1 << 15) /* Instruction cache flush pending */ +#define CACHE_CTRL_IB (1 << 16) /* Instruction burst fetch */ +#define CACHE_CTRL_FI (1 << 21) /* Flush Instruction cache (Write only) */ +#define CACHE_CTRL_FD (1 << 22) /* Flush Data cache (Write only) */ +#define CACHE_CTRL_DS (1 << 23) /* Data cache snoop enable */ + +#if defined(CONFIG_USER_ONLY) && defined(TARGET_SPARC64) +static void do_unassigned_access(target_ulong addr, int is_write, int is_exec, + int is_asi, int size); +#endif + #if defined(TARGET_SPARC64) && !defined(CONFIG_USER_ONLY) // Calculates TSB pointer value for fault page size 8k or 64k static uint64_t ultrasparc_tsb_pointer(uint64_t tsb_register, @@ -116,24 +161,59 @@ { unsigned int i; target_ulong mask; + uint64_t context; + + int is_demap_context = (demap_addr >> 6) & 1; + + // demap context + switch ((demap_addr >> 4) & 3) { + case 0: // primary + context = env1->dmmu.mmu_primary_context; + break; + case 1: // secondary + context = env1->dmmu.mmu_secondary_context; + break; + case 2: // nucleus + context = 0; + break; + case 3: // reserved + default: + return; + } for (i = 0; i < 64; i++) { if (TTE_IS_VALID(tlb[i].tte)) { - mask = 0xffffffffffffe000ULL; - mask <<= 3 * ((tlb[i].tte >> 61) & 3); + if (is_demap_context) { + // will remove non-global entries matching context value + if (TTE_IS_GLOBAL(tlb[i].tte) || + !tlb_compare_context(&tlb[i], context)) { + continue; + } + } else { + // demap page + // will remove any entry matching VA + mask = 0xffffffffffffe000ULL; + mask <<= 3 * ((tlb[i].tte >> 61) & 3); + + if (!compare_masked(demap_addr, tlb[i].tag, mask)) { + continue; + } + + // entry should be global or matching context value + if (!TTE_IS_GLOBAL(tlb[i].tte) && + !tlb_compare_context(&tlb[i], context)) { + continue; + } + } - if ((demap_addr & mask) == (tlb[i].tag & mask)) { - replace_tlb_entry(&tlb[i], 0, 0, env1); + replace_tlb_entry(&tlb[i], 0, 0, env1); #ifdef DEBUG_MMU - DPRINTF_MMU("%s demap invalidated entry [%02u]\n", strmmu, i); - dump_mmu(env1); + DPRINTF_MMU("%s demap invalidated entry [%02u]\n", strmmu, i); + dump_mmu(stdout, fprintf, env1); #endif - } - //return; } } - } static void replace_tlb_1bit_lru(SparcTLBEntry *tlb, @@ -148,7 +228,7 @@ replace_tlb_entry(&tlb[i], tlb_tag, tlb_tte, env1); #ifdef DEBUG_MMU DPRINTF_MMU("%s lru replaced invalid entry [%i]\n", strmmu, i); - dump_mmu(env1); + dump_mmu(stdout, fprintf, env1); #endif return; } @@ -167,7 +247,7 @@ #ifdef DEBUG_MMU DPRINTF_MMU("%s lru replaced unlocked %s entry [%i]\n", strmmu, (replace_used?"used":"unused"), i); - dump_mmu(env1); + dump_mmu(stdout, fprintf, env1); #endif return; } @@ -188,14 +268,51 @@ #endif -static inline void address_mask(CPUState *env1, target_ulong *addr) +static inline target_ulong address_mask(CPUState *env1, target_ulong addr) { #ifdef TARGET_SPARC64 if (AM_CHECK(env1)) - *addr &= 0xffffffffULL; + addr &= 0xffffffffULL; +#endif + return addr; +} + +/* returns true if access using this ASI is to have address translated by MMU + otherwise access is to raw physical address */ +static inline int is_translating_asi(int asi) +{ +#ifdef TARGET_SPARC64 + /* Ultrasparc IIi translating asi + - note this list is defined by cpu implementation + */ + switch (asi) { + case 0x04 ... 0x11: + case 0x18 ... 0x19: + case 0x24 ... 0x2C: + case 0x70 ... 0x73: + case 0x78 ... 0x79: + case 0x80 ... 0xFF: + return 1; + + default: + return 0; + } +#else + /* TODO: check sparc32 bits */ + return 0; #endif } +static inline target_ulong asi_address_mask(CPUState *env1, + int asi, target_ulong addr) +{ + if (is_translating_asi(asi)) { + return address_mask(env, addr); + } else { + return addr; + } +} + static void raise_exception(int tt) { env->exception_index = tt; @@ -207,9 +324,11 @@ raise_exception(tt); } -static inline void set_cwp(int new_cwp) +void helper_shutdown(void) { - cpu_set_cwp(env, new_cwp); +#if !defined(CONFIG_USER_ONLY) + qemu_system_shutdown_request(); +#endif } void helper_check_align(target_ulong addr, uint32_t align) @@ -775,65 +894,77 @@ QT0 = float128_sqrt(QT1, &env->fp_status); } -#define GEN_FCMP(name, size, reg1, reg2, FS, TRAP) \ +#define GEN_FCMP(name, size, reg1, reg2, FS, E) \ void glue(helper_, name) (void) \ { \ - target_ulong new_fsr; \ - \ - env->fsr &= ~((FSR_FCC1 | FSR_FCC0) << FS); \ + env->fsr &= FSR_FTT_NMASK; \ + if (E && (glue(size, _is_any_nan)(reg1) || \ + glue(size, _is_any_nan)(reg2)) && \ + (env->fsr & FSR_NVM)) { \ + env->fsr |= FSR_NVC; \ + env->fsr |= FSR_FTT_IEEE_EXCP; \ + raise_exception(TT_FP_EXCP); \ + } \ switch (glue(size, _compare) (reg1, reg2, &env->fp_status)) { \ case float_relation_unordered: \ - new_fsr = (FSR_FCC1 | FSR_FCC0) << FS; \ - if ((env->fsr & FSR_NVM) || TRAP) { \ - env->fsr |= new_fsr; \ + if ((env->fsr & FSR_NVM)) { \ env->fsr |= FSR_NVC; \ env->fsr |= FSR_FTT_IEEE_EXCP; \ raise_exception(TT_FP_EXCP); \ } else { \ + env->fsr &= ~((FSR_FCC1 | FSR_FCC0) << FS); \ + env->fsr |= (FSR_FCC1 | FSR_FCC0) << FS; \ env->fsr |= FSR_NVA; \ } \ break; \ case float_relation_less: \ - new_fsr = FSR_FCC0 << FS; \ + env->fsr &= ~((FSR_FCC1 | FSR_FCC0) << FS); \ + env->fsr |= FSR_FCC0 << FS; \ break; \ case float_relation_greater: \ - new_fsr = FSR_FCC1 << FS; \ + env->fsr &= ~((FSR_FCC1 | FSR_FCC0) << FS); \ + env->fsr |= FSR_FCC1 << FS; \ break; \ default: \ - new_fsr = 0; \ + env->fsr &= ~((FSR_FCC1 | FSR_FCC0) << FS); \ break; \ } \ - env->fsr |= new_fsr; \ } -#define GEN_FCMPS(name, size, FS, TRAP) \ +#define GEN_FCMPS(name, size, FS, E) \ void glue(helper_, name)(float32 src1, float32 src2) \ { \ - target_ulong new_fsr; \ - \ - env->fsr &= ~((FSR_FCC1 | FSR_FCC0) << FS); \ + env->fsr &= FSR_FTT_NMASK; \ + if (E && (glue(size, _is_any_nan)(src1) || \ + glue(size, _is_any_nan)(src2)) && \ + (env->fsr & FSR_NVM)) { \ + env->fsr |= FSR_NVC; \ + env->fsr |= FSR_FTT_IEEE_EXCP; \ + raise_exception(TT_FP_EXCP); \ + } \ switch (glue(size, _compare) (src1, src2, &env->fp_status)) { \ case float_relation_unordered: \ - new_fsr = (FSR_FCC1 | FSR_FCC0) << FS; \ - if ((env->fsr & FSR_NVM) || TRAP) { \ - env->fsr |= new_fsr; \ + if ((env->fsr & FSR_NVM)) { \ env->fsr |= FSR_NVC; \ env->fsr |= FSR_FTT_IEEE_EXCP; \ raise_exception(TT_FP_EXCP); \ } else { \ + env->fsr &= ~((FSR_FCC1 | FSR_FCC0) << FS); \ + env->fsr |= (FSR_FCC1 | FSR_FCC0) << FS; \ env->fsr |= FSR_NVA; \ } \ break; \ case float_relation_less: \ - new_fsr = FSR_FCC0 << FS; \ + env->fsr &= ~((FSR_FCC1 | FSR_FCC0) << FS); \ + env->fsr |= FSR_FCC0 << FS; \ break; \ case float_relation_greater: \ - new_fsr = FSR_FCC1 << FS; \ + env->fsr &= ~((FSR_FCC1 | FSR_FCC0) << FS); \ + env->fsr |= FSR_FCC1 << FS; \ break; \ default: \ - new_fsr = 0; \ + env->fsr &= ~((FSR_FCC1 | FSR_FCC0) << FS); \ break; \ } \ - env->fsr |= new_fsr; \ } GEN_FCMPS(fcmps, float32, 0, 0); @@ -855,14 +986,15 @@ return env->psr & PSR_CARRY; } -static inline uint32_t get_NZ_icc(target_ulong dst) +static inline uint32_t get_NZ_icc(int32_t dst) { uint32_t ret = 0; - if (!(dst & 0xffffffffULL)) - ret |= PSR_ZERO; - if ((int32_t) (dst & 0xffffffffULL) < 0) - ret |= PSR_NEG; + if (dst == 0) { + ret = PSR_ZERO; + } else if (dst < 0) { + ret = PSR_NEG; + } return ret; } @@ -877,14 +1009,15 @@ return env->xcc & PSR_CARRY; } -static inline uint32_t get_NZ_xcc(target_ulong dst) +static inline uint32_t get_NZ_xcc(target_long dst) { uint32_t ret = 0; - if (!dst) - ret |= PSR_ZERO; - if ((int64_t)dst < 0) - ret |= PSR_NEG; + if (!dst) { + ret = PSR_ZERO; + } else if (dst < 0) { + ret = PSR_NEG; + } return ret; } #endif @@ -893,8 +1026,9 @@ { uint32_t ret = 0; - if (src2 != 0) - ret |= PSR_OVF; + if (src2 != 0) { + ret = PSR_OVF; + } return ret; } @@ -912,26 +1046,35 @@ return 0; } -/* carry = (src1[31] & src2[31]) | ( ~dst[31] & (src1[31] | src2[31])) */ -static inline uint32_t get_C_add_icc(target_ulong dst, target_ulong src1, - target_ulong src2) +static inline uint32_t get_C_add_icc(uint32_t dst, uint32_t src1) { uint32_t ret = 0; - if (((src1 & (1ULL << 31)) & (src2 & (1ULL << 31))) - | ((~(dst & (1ULL << 31))) - & ((src1 & (1ULL << 31)) | (src2 & (1ULL << 31))))) - ret |= PSR_CARRY; + if (dst < src1) { + ret = PSR_CARRY; + } return ret; } -static inline uint32_t get_V_add_icc(target_ulong dst, target_ulong src1, - target_ulong src2) +static inline uint32_t get_C_addx_icc(uint32_t dst, uint32_t src1, + uint32_t src2) +{ + uint32_t ret = 0; + + if (((src1 & src2) | (~dst & (src1 | src2))) & (1U << 31)) { + ret = PSR_CARRY; + } + return ret; +} + +static inline uint32_t get_V_add_icc(uint32_t dst, uint32_t src1, + uint32_t src2) { uint32_t ret = 0; - if (((src1 ^ src2 ^ -1) & (src1 ^ dst)) & (1ULL << 31)) - ret |= PSR_OVF; + if (((src1 ^ src2 ^ -1) & (src1 ^ dst)) & (1U << 31)) { + ret = PSR_OVF; + } return ret; } @@ -940,8 +1083,20 @@ { uint32_t ret = 0; - if (dst < src1) - ret |= PSR_CARRY; + if (dst < src1) { + ret = PSR_CARRY; + } + return ret; +} + +static inline uint32_t get_C_addx_xcc(target_ulong dst, target_ulong src1, + target_ulong src2) +{ + uint32_t ret = 0; + + if (((src1 & src2) | (~dst & (src1 | src2))) & (1ULL << 63)) { + ret = PSR_CARRY; + } return ret; } @@ -950,8 +1105,9 @@ { uint32_t ret = 0; - if (((src1 ^ src2 ^ -1) & (src1 ^ dst)) & (1ULL << 63)) - ret |= PSR_OVF; + if (((src1 ^ src2 ^ -1) & (src1 ^ dst)) & (1ULL << 63)) { + ret = PSR_OVF; + } return ret; } @@ -976,14 +1132,14 @@ uint32_t ret; ret = get_NZ_icc(CC_DST); - ret |= get_C_add_icc(CC_DST, CC_SRC, CC_SRC2); + ret |= get_C_add_icc(CC_DST, CC_SRC); ret |= get_V_add_icc(CC_DST, CC_SRC, CC_SRC2); return ret; } static uint32_t compute_C_add(void) { - return get_C_add_icc(CC_DST, CC_SRC, CC_SRC2); + return get_C_add_icc(CC_DST, CC_SRC); } #ifdef TARGET_SPARC64 @@ -992,8 +1148,7 @@ uint32_t ret; ret = get_NZ_xcc(CC_DST); - ret |= get_C_add_xcc(CC_DST - CC_SRC2, CC_SRC); - ret |= get_C_add_xcc(CC_DST, CC_SRC); + ret |= get_C_addx_xcc(CC_DST, CC_SRC, CC_SRC2); ret |= get_V_add_xcc(CC_DST, CC_SRC, CC_SRC2); return ret; } @@ -1002,18 +1157,36 @@ { uint32_t ret; - ret = get_C_add_xcc(CC_DST - CC_SRC2, CC_SRC); - ret |= get_C_add_xcc(CC_DST, CC_SRC); + ret = get_C_addx_xcc(CC_DST, CC_SRC, CC_SRC2); return ret; } #endif +static uint32_t compute_all_addx(void) +{ + uint32_t ret; + + ret = get_NZ_icc(CC_DST); + ret |= get_C_addx_icc(CC_DST, CC_SRC, CC_SRC2); + ret |= get_V_add_icc(CC_DST, CC_SRC, CC_SRC2); + return ret; +} + +static uint32_t compute_C_addx(void) +{ + uint32_t ret; + + ret = get_C_addx_icc(CC_DST, CC_SRC, CC_SRC2); + return ret; +} + static inline uint32_t get_V_tag_icc(target_ulong src1, target_ulong src2) { uint32_t ret = 0; - if ((src1 | src2) & 0x3) - ret |= PSR_OVF; + if ((src1 | src2) & 0x3) { + ret = PSR_OVF; + } return ret; } @@ -1022,51 +1195,50 @@ uint32_t ret; ret = get_NZ_icc(CC_DST); - ret |= get_C_add_icc(CC_DST, CC_SRC, CC_SRC2); + ret |= get_C_add_icc(CC_DST, CC_SRC); ret |= get_V_add_icc(CC_DST, CC_SRC, CC_SRC2); ret |= get_V_tag_icc(CC_SRC, CC_SRC2); return ret; } -static uint32_t compute_C_tadd(void) -{ - return get_C_add_icc(CC_DST, CC_SRC, CC_SRC2); -} - static uint32_t compute_all_taddtv(void) { uint32_t ret; ret = get_NZ_icc(CC_DST); - ret |= get_C_add_icc(CC_DST, CC_SRC, CC_SRC2); + ret |= get_C_add_icc(CC_DST, CC_SRC); return ret; } -static uint32_t compute_C_taddtv(void) +static inline uint32_t get_C_sub_icc(uint32_t src1, uint32_t src2) { - return get_C_add_icc(CC_DST, CC_SRC, CC_SRC2); + uint32_t ret = 0; + + if (src1 < src2) { + ret = PSR_CARRY; + } + return ret; } -/* carry = (~src1[31] & src2[31]) | ( dst[31] & (~src1[31] | src2[31])) */ -static inline uint32_t get_C_sub_icc(target_ulong dst, target_ulong src1, - target_ulong src2) +static inline uint32_t get_C_subx_icc(uint32_t dst, uint32_t src1, + uint32_t src2) { uint32_t ret = 0; - if (((~(src1 & (1ULL << 31))) & (src2 & (1ULL << 31))) - | ((dst & (1ULL << 31)) & (( ~(src1 & (1ULL << 31))) - | (src2 & (1ULL << 31))))) - ret |= PSR_CARRY; + if (((~src1 & src2) | (dst & (~src1 | src2))) & (1U << 31)) { + ret = PSR_CARRY; + } return ret; } -static inline uint32_t get_V_sub_icc(target_ulong dst, target_ulong src1, - target_ulong src2) +static inline uint32_t get_V_sub_icc(uint32_t dst, uint32_t src1, + uint32_t src2) { uint32_t ret = 0; - if (((src1 ^ src2) & (src1 ^ dst)) & (1ULL << 31)) - ret |= PSR_OVF; + if (((src1 ^ src2) & (src1 ^ dst)) & (1U << 31)) { + ret = PSR_OVF; + } return ret; } @@ -1076,8 +1248,20 @@ { uint32_t ret = 0; - if (src1 < src2) - ret |= PSR_CARRY; + if (src1 < src2) { + ret = PSR_CARRY; + } + return ret; +} + +static inline uint32_t get_C_subx_xcc(target_ulong dst, target_ulong src1, + target_ulong src2) +{ + uint32_t ret = 0; + + if (((~src1 & src2) | (dst & (~src1 | src2))) & (1ULL << 63)) { + ret = PSR_CARRY; + } return ret; } @@ -1086,8 +1270,9 @@ { uint32_t ret = 0; - if (((src1 ^ src2) & (src1 ^ dst)) & (1ULL << 63)) - ret |= PSR_OVF; + if (((src1 ^ src2) & (src1 ^ dst)) & (1ULL << 63)) { + ret = PSR_OVF; + } return ret; } @@ -1112,14 +1297,14 @@ uint32_t ret; ret = get_NZ_icc(CC_DST); - ret |= get_C_sub_icc(CC_DST, CC_SRC, CC_SRC2); + ret |= get_C_sub_icc(CC_SRC, CC_SRC2); ret |= get_V_sub_icc(CC_DST, CC_SRC, CC_SRC2); return ret; } static uint32_t compute_C_sub(void) { - return get_C_sub_icc(CC_DST, CC_SRC, CC_SRC2); + return get_C_sub_icc(CC_SRC, CC_SRC2); } #ifdef TARGET_SPARC64 @@ -1128,8 +1313,7 @@ uint32_t ret; ret = get_NZ_xcc(CC_DST); - ret |= get_C_sub_xcc(CC_DST - CC_SRC2, CC_SRC); - ret |= get_C_sub_xcc(CC_DST, CC_SRC2); + ret |= get_C_subx_xcc(CC_DST, CC_SRC, CC_SRC2); ret |= get_V_sub_xcc(CC_DST, CC_SRC, CC_SRC2); return ret; } @@ -1138,40 +1322,47 @@ { uint32_t ret; - ret = get_C_sub_xcc(CC_DST - CC_SRC2, CC_SRC); - ret |= get_C_sub_xcc(CC_DST, CC_SRC2); + ret = get_C_subx_xcc(CC_DST, CC_SRC, CC_SRC2); return ret; } #endif -static uint32_t compute_all_tsub(void) +static uint32_t compute_all_subx(void) { uint32_t ret; ret = get_NZ_icc(CC_DST); - ret |= get_C_sub_icc(CC_DST, CC_SRC, CC_SRC2); + ret |= get_C_subx_icc(CC_DST, CC_SRC, CC_SRC2); ret |= get_V_sub_icc(CC_DST, CC_SRC, CC_SRC2); - ret |= get_V_tag_icc(CC_SRC, CC_SRC2); return ret; } -static uint32_t compute_C_tsub(void) +static uint32_t compute_C_subx(void) { - return get_C_sub_icc(CC_DST, CC_SRC, CC_SRC2); + uint32_t ret; + + ret = get_C_subx_icc(CC_DST, CC_SRC, CC_SRC2); + return ret; } -static uint32_t compute_all_tsubtv(void) +static uint32_t compute_all_tsub(void) { uint32_t ret; ret = get_NZ_icc(CC_DST); - ret |= get_C_sub_icc(CC_DST, CC_SRC, CC_SRC2); + ret |= get_C_sub_icc(CC_SRC, CC_SRC2); + ret |= get_V_sub_icc(CC_DST, CC_SRC, CC_SRC2); + ret |= get_V_tag_icc(CC_SRC, CC_SRC2); return ret; } -static uint32_t compute_C_tsubtv(void) +static uint32_t compute_all_tsubtv(void) { - return get_C_sub_icc(CC_DST, CC_SRC, CC_SRC2); + uint32_t ret; + + ret = get_NZ_icc(CC_DST); + ret |= get_C_sub_icc(CC_SRC, CC_SRC2); + return ret; } static uint32_t compute_all_logic(void) @@ -1201,13 +1392,13 @@ [CC_OP_FLAGS] = { compute_all_flags, compute_C_flags }, [CC_OP_DIV] = { compute_all_div, compute_C_div }, [CC_OP_ADD] = { compute_all_add, compute_C_add }, - [CC_OP_ADDX] = { compute_all_add, compute_C_add }, - [CC_OP_TADD] = { compute_all_tadd, compute_C_tadd }, - [CC_OP_TADDTV] = { compute_all_taddtv, compute_C_taddtv }, + [CC_OP_ADDX] = { compute_all_addx, compute_C_addx }, + [CC_OP_TADD] = { compute_all_tadd, compute_C_add }, + [CC_OP_TADDTV] = { compute_all_taddtv, compute_C_add }, [CC_OP_SUB] = { compute_all_sub, compute_C_sub }, - [CC_OP_SUBX] = { compute_all_sub, compute_C_sub }, - [CC_OP_TSUB] = { compute_all_tsub, compute_C_tsub }, - [CC_OP_TSUBTV] = { compute_all_tsubtv, compute_C_tsubtv }, + [CC_OP_SUBX] = { compute_all_subx, compute_C_subx }, + [CC_OP_TSUB] = { compute_all_tsub, compute_C_sub }, + [CC_OP_TSUBTV] = { compute_all_tsubtv, compute_C_sub }, [CC_OP_LOGIC] = { compute_all_logic, compute_C_logic }, }; @@ -1249,6 +1440,140 @@ return ret; } +static inline void memcpy32(target_ulong *dst, const target_ulong *src) +{ + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + dst[3] = src[3]; + dst[4] = src[4]; + dst[5] = src[5]; + dst[6] = src[6]; + dst[7] = src[7]; +} + +static void set_cwp(int new_cwp) +{ + /* put the modified wrap registers at their proper location */ + if (env->cwp == env->nwindows - 1) { + memcpy32(env->regbase, env->regbase + env->nwindows * 16); + } + env->cwp = new_cwp; + + /* put the wrap registers at their temporary location */ + if (new_cwp == env->nwindows - 1) { + memcpy32(env->regbase + env->nwindows * 16, env->regbase); + } + env->regwptr = env->regbase + (new_cwp * 16); +} + +void cpu_set_cwp(CPUState *env1, int new_cwp) +{ + CPUState *saved_env; + + saved_env = env; + env = env1; + set_cwp(new_cwp); + env = saved_env; +} + +static target_ulong get_psr(void) +{ + helper_compute_psr(); + +#if !defined (TARGET_SPARC64) + return env->version | (env->psr & PSR_ICC) | + (env->psref? PSR_EF : 0) | + (env->psrpil << 8) | + (env->psrs? PSR_S : 0) | + (env->psrps? PSR_PS : 0) | + (env->psret? PSR_ET : 0) | env->cwp; +#else + return env->psr & PSR_ICC; +#endif +} + +target_ulong cpu_get_psr(CPUState *env1) +{ + CPUState *saved_env; + target_ulong ret; + + saved_env = env; + env = env1; + ret = get_psr(); + env = saved_env; + return ret; +} + +static void put_psr(target_ulong val) +{ + env->psr = val & PSR_ICC; +#if !defined (TARGET_SPARC64) + env->psref = (val & PSR_EF)? 1 : 0; + env->psrpil = (val & PSR_PIL) >> 8; +#endif +#if ((!defined (TARGET_SPARC64)) && !defined(CONFIG_USER_ONLY)) + cpu_check_irqs(env); +#endif +#if !defined (TARGET_SPARC64) + env->psrs = (val & PSR_S)? 1 : 0; + env->psrps = (val & PSR_PS)? 1 : 0; + env->psret = (val & PSR_ET)? 1 : 0; + set_cwp(val & PSR_CWP); +#endif + env->cc_op = CC_OP_FLAGS; +} + +void cpu_put_psr(CPUState *env1, target_ulong val) +{ + CPUState *saved_env; + + saved_env = env; + env = env1; + put_psr(val); + env = saved_env; +} + +static int cwp_inc(int cwp) +{ + if (unlikely(cwp >= env->nwindows)) { + cwp -= env->nwindows; + } + return cwp; +} + +int cpu_cwp_inc(CPUState *env1, int cwp) +{ + CPUState *saved_env; + target_ulong ret; + + saved_env = env; + env = env1; + ret = cwp_inc(cwp); + env = saved_env; + return ret; +} + +static int cwp_dec(int cwp) +{ + if (unlikely(cwp < 0)) { + cwp += env->nwindows; + } + return cwp; +} + +int cpu_cwp_dec(CPUState *env1, int cwp) +{ + CPUState *saved_env; + target_ulong ret; + + saved_env = env; + env = env1; + ret = cwp_dec(cwp); + env = saved_env; + return ret; +} + #ifdef TARGET_SPARC64 GEN_FCMPS(fcmps_fcc1, float32, 22, 0); GEN_FCMP(fcmpd_fcc1, float64, DT0, DT1, 22, 0); @@ -1324,6 +1649,109 @@ #ifndef TARGET_SPARC64 #ifndef CONFIG_USER_ONLY + + +/* Leon3 cache control */ + +static void leon3_cache_control_int(void) +{ + uint32_t state = 0; + + if (env->cache_control & CACHE_CTRL_IF) { + /* Instruction cache state */ + state = env->cache_control & CACHE_STATE_MASK; + if (state == CACHE_ENABLED) { + state = CACHE_FROZEN; + DPRINTF_CACHE_CONTROL("Instruction cache: freeze\n"); + } + + env->cache_control &= ~CACHE_STATE_MASK; + env->cache_control |= state; + } + + if (env->cache_control & CACHE_CTRL_DF) { + /* Data cache state */ + state = (env->cache_control >> 2) & CACHE_STATE_MASK; + if (state == CACHE_ENABLED) { + state = CACHE_FROZEN; + DPRINTF_CACHE_CONTROL("Data cache: freeze\n"); + } + + env->cache_control &= ~(CACHE_STATE_MASK << 2); + env->cache_control |= (state << 2); + } +} + +static void leon3_cache_control_st(target_ulong addr, uint64_t val, int size) +{ + DPRINTF_CACHE_CONTROL("st addr:%08x, val:%" PRIx64 ", size:%d\n", + addr, val, size); + + if (size != 4) { + DPRINTF_CACHE_CONTROL("32bits only\n"); + return; + } + + switch (addr) { + case 0x00: /* Cache control */ + + /* These values must always be read as zeros */ + val &= ~CACHE_CTRL_FD; + val &= ~CACHE_CTRL_FI; + val &= ~CACHE_CTRL_IB; + val &= ~CACHE_CTRL_IP; + val &= ~CACHE_CTRL_DP; + + env->cache_control = val; + break; + case 0x04: /* Instruction cache configuration */ + case 0x08: /* Data cache configuration */ + /* Read Only */ + break; + default: + DPRINTF_CACHE_CONTROL("write unknown register %08x\n", addr); + break; + }; +} + +static uint64_t leon3_cache_control_ld(target_ulong addr, int size) +{ + uint64_t ret = 0; + + if (size != 4) { + DPRINTF_CACHE_CONTROL("32bits only\n"); + return 0; + } + + switch (addr) { + case 0x00: /* Cache control */ + ret = env->cache_control; + break; + + /* Configuration registers are read and only always keep those + predefined values */ + + case 0x04: /* Instruction cache configuration */ + ret = 0x10220000; + break; + case 0x08: /* Data cache configuration */ + ret = 0x18220000; + break; + default: + DPRINTF_CACHE_CONTROL("read unknown register %08x\n", addr); + break; + }; + DPRINTF_CACHE_CONTROL("ld addr:%08x, ret:0x%" PRIx64 ", size:%d\n", + addr, ret, size); + return ret; +} + +void leon3_irq_manager(void *irq_manager, int intno) +{ + leon3_irq_ack(irq_manager, intno); + leon3_cache_control_int(); +} + uint64_t helper_ld_asi(target_ulong addr, int asi, int size, int sign) { uint64_t ret = 0; @@ -1333,8 +1761,15 @@ helper_check_align(addr, size - 1); switch (asi) { - case 2: /* SuperSparc MXCC registers */ + case 2: /* SuperSparc MXCC registers and Leon3 cache control */ switch (addr) { + case 0x00: /* Leon3 Cache Control */ + case 0x08: /* Leon3 Instruction Cache config */ + case 0x0C: /* Leon3 Date Cache config */ + if (env->def->features & CPU_FEATURE_CACHE_CTRL) { + ret = leon3_cache_control_ld(addr, size); + } + break; case 0x01c00a00: /* MXCC control register */ if (size == 8) ret = env->mxccregs[3]; @@ -1505,6 +1940,7 @@ case 0x31: // Turbosparc RAM snoop case 0x32: // Turbosparc page table descriptor diagnostic case 0x39: /* data cache diagnostic register */ + case 0x4c: /* SuperSPARC MMU Breakpoint Action register */ ret = 0; break; case 0x38: /* SuperSPARC MMU Breakpoint Control Registers */ @@ -1561,8 +1997,16 @@ { helper_check_align(addr, size - 1); switch(asi) { - case 2: /* SuperSparc MXCC registers */ + case 2: /* SuperSparc MXCC registers and Leon3 cache control */ switch (addr) { + case 0x00: /* Leon3 Cache Control */ + case 0x08: /* Leon3 Instruction Cache config */ + case 0x0C: /* Leon3 Date Cache config */ + if (env->def->features & CPU_FEATURE_CACHE_CTRL) { + leon3_cache_control_st(addr, val, size); + } + break; + case 0x01c00000: /* MXCC stream data register 0 */ if (size == 8) env->mxccdata[0] = val; @@ -1682,7 +2126,7 @@ break; } #ifdef DEBUG_MMU - dump_mmu(env); + dump_mmu(stdout, fprintf, env); #endif } break; @@ -1734,7 +2178,7 @@ reg, oldreg, env->mmuregs[reg]); } #ifdef DEBUG_MMU - dump_mmu(env); + dump_mmu(stdout, fprintf, env); #endif } break; @@ -1910,7 +2354,7 @@ raise_exception(TT_PRIV_ACT); helper_check_align(addr, size - 1); - address_mask(env, &addr); + addr = asi_address_mask(env, asi, addr); switch (asi) { case 0x82: // Primary no-fault @@ -2013,7 +2457,7 @@ raise_exception(TT_PRIV_ACT); helper_check_align(addr, size - 1); - address_mask(env, &addr); + addr = asi_address_mask(env, asi, addr); /* Convert to little endian */ switch (asi) { @@ -2084,31 +2528,44 @@ asi &= 0xff; if ((asi < 0x80 && (env->pstate & PS_PRIV) == 0) - || ((env->def->features & CPU_FEATURE_HYPV) + || (cpu_has_hypervisor(env) && asi >= 0x30 && asi < 0x80 && !(env->hpstate & HS_PRIV))) raise_exception(TT_PRIV_ACT); helper_check_align(addr, size - 1); + addr = asi_address_mask(env, asi, addr); + switch (asi) { case 0x82: // Primary no-fault case 0x8a: // Primary no-fault LE - if (cpu_get_phys_page_debug(env, addr) == -1ULL) { + case 0x83: // Secondary no-fault + case 0x8b: // Secondary no-fault LE + { + /* secondary space access has lowest asi bit equal to 1 */ + int access_mmu_idx = ( asi & 1 ) ? MMU_KERNEL_IDX + : MMU_KERNEL_SECONDARY_IDX; + + if (cpu_get_phys_page_nofault(env, addr, access_mmu_idx) == -1ULL) { #ifdef DEBUG_ASI - dump_asi("read ", last_addr, asi, size, ret); + dump_asi("read ", last_addr, asi, size, ret); #endif - return 0; + return 0; + } } // Fall through case 0x10: // As if user primary + case 0x11: // As if user secondary case 0x18: // As if user primary LE + case 0x19: // As if user secondary LE case 0x80: // Primary + case 0x81: // Secondary case 0x88: // Primary LE + case 0x89: // Secondary LE case 0xe2: // UA2007 Primary block init case 0xe3: // UA2007 Secondary block init if ((asi & 0x80) && (env->pstate & PS_PRIV)) { - if ((env->def->features & CPU_FEATURE_HYPV) - && env->hpstate & HS_PRIV) { + if (cpu_hypervisor_mode(env)) { switch(size) { case 1: ret = ldub_hypv(addr); @@ -2125,37 +2582,75 @@ break; } } else { + /* secondary space access has lowest asi bit equal to 1 */ + if (asi & 1) { + switch(size) { + case 1: + ret = ldub_kernel_secondary(addr); + break; + case 2: + ret = lduw_kernel_secondary(addr); + break; + case 4: + ret = ldl_kernel_secondary(addr); + break; + default: + case 8: + ret = ldq_kernel_secondary(addr); + break; + } + } else { + switch(size) { + case 1: + ret = ldub_kernel(addr); + break; + case 2: + ret = lduw_kernel(addr); + break; + case 4: + ret = ldl_kernel(addr); + break; + default: + case 8: + ret = ldq_kernel(addr); + break; + } + } + } + } else { + /* secondary space access has lowest asi bit equal to 1 */ + if (asi & 1) { switch(size) { case 1: - ret = ldub_kernel(addr); + ret = ldub_user_secondary(addr); break; case 2: - ret = lduw_kernel(addr); + ret = lduw_user_secondary(addr); break; case 4: - ret = ldl_kernel(addr); + ret = ldl_user_secondary(addr); break; default: case 8: - ret = ldq_kernel(addr); + ret = ldq_user_secondary(addr); + break; + } + } else { + switch(size) { + case 1: + ret = ldub_user(addr); + break; + case 2: + ret = lduw_user(addr); + break; + case 4: + ret = ldl_user(addr); + break; + default: + case 8: + ret = ldq_user(addr); break; } - } - } else { - switch(size) { - case 1: - ret = ldub_user(addr); - break; - case 2: - ret = lduw_user(addr); - break; - case 4: - ret = ldl_user(addr); - break; - default: - case 8: - ret = ldq_user(addr); - break; } } break; @@ -2186,22 +2681,27 @@ // Only ldda allowed raise_exception(TT_ILL_INSN); return 0; - case 0x83: // Secondary no-fault - case 0x8b: // Secondary no-fault LE - if (cpu_get_phys_page_debug(env, addr) == -1ULL) { -#ifdef DEBUG_ASI - dump_asi("read ", last_addr, asi, size, ret); -#endif - return 0; - } - // Fall through case 0x04: // Nucleus case 0x0c: // Nucleus Little Endian (LE) - case 0x11: // As if user secondary - case 0x19: // As if user secondary LE + { + switch(size) { + case 1: + ret = ldub_nucleus(addr); + break; + case 2: + ret = lduw_nucleus(addr); + break; + case 4: + ret = ldl_nucleus(addr); + break; + default: + case 8: + ret = ldq_nucleus(addr); + break; + } + break; + } case 0x4a: // UPA config - case 0x81: // Secondary - case 0x89: // Secondary LE // XXX break; case 0x45: // LSU @@ -2381,12 +2881,14 @@ asi &= 0xff; if ((asi < 0x80 && (env->pstate & PS_PRIV) == 0) - || ((env->def->features & CPU_FEATURE_HYPV) + || (cpu_has_hypervisor(env) && asi >= 0x30 && asi < 0x80 && !(env->hpstate & HS_PRIV))) raise_exception(TT_PRIV_ACT); helper_check_align(addr, size - 1); + addr = asi_address_mask(env, asi, addr); + /* Convert to little endian */ switch (asi) { case 0x0c: // Nucleus Little Endian (LE) @@ -2415,14 +2917,17 @@ switch(asi) { case 0x10: // As if user primary + case 0x11: // As if user secondary case 0x18: // As if user primary LE + case 0x19: // As if user secondary LE case 0x80: // Primary + case 0x81: // Secondary case 0x88: // Primary LE + case 0x89: // Secondary LE case 0xe2: // UA2007 Primary block init case 0xe3: // UA2007 Secondary block init if ((asi & 0x80) && (env->pstate & PS_PRIV)) { - if ((env->def->features & CPU_FEATURE_HYPV) - && env->hpstate & HS_PRIV) { + if (cpu_hypervisor_mode(env)) { switch(size) { case 1: stb_hypv(addr, val); @@ -2439,37 +2944,75 @@ break; } } else { + /* secondary space access has lowest asi bit equal to 1 */ + if (asi & 1) { + switch(size) { + case 1: + stb_kernel_secondary(addr, val); + break; + case 2: + stw_kernel_secondary(addr, val); + break; + case 4: + stl_kernel_secondary(addr, val); + break; + case 8: + default: + stq_kernel_secondary(addr, val); + break; + } + } else { + switch(size) { + case 1: + stb_kernel(addr, val); + break; + case 2: + stw_kernel(addr, val); + break; + case 4: + stl_kernel(addr, val); + break; + case 8: + default: + stq_kernel(addr, val); + break; + } + } + } + } else { + /* secondary space access has lowest asi bit equal to 1 */ + if (asi & 1) { switch(size) { case 1: - stb_kernel(addr, val); + stb_user_secondary(addr, val); break; case 2: - stw_kernel(addr, val); + stw_user_secondary(addr, val); break; case 4: - stl_kernel(addr, val); + stl_user_secondary(addr, val); break; case 8: default: - stq_kernel(addr, val); + stq_user_secondary(addr, val); + break; + } + } else { + switch(size) { + case 1: + stb_user(addr, val); + break; + case 2: + stw_user(addr, val); + break; + case 4: + stl_user(addr, val); + break; + case 8: + default: + stq_user(addr, val); break; } - } - } else { - switch(size) { - case 1: - stb_user(addr, val); - break; - case 2: - stw_user(addr, val); - break; - case 4: - stl_user(addr, val); - break; - case 8: - default: - stq_user(addr, val); - break; } } break; @@ -2502,11 +3045,26 @@ return; case 0x04: // Nucleus case 0x0c: // Nucleus Little Endian (LE) - case 0x11: // As if user secondary - case 0x19: // As if user secondary LE + { + switch(size) { + case 1: + stb_nucleus(addr, val); + break; + case 2: + stw_nucleus(addr, val); + break; + case 4: + stl_nucleus(addr, val); + break; + default: + case 8: + stq_nucleus(addr, val); + break; + } + break; + } + case 0x4a: // UPA config - case 0x81: // Secondary - case 0x89: // Secondary LE // XXX return; case 0x45: // LSU @@ -2521,7 +3079,7 @@ DPRINTF_MMU("LSU change: 0x%" PRIx64 " -> 0x%" PRIx64 "\n", oldreg, env->lsu); #ifdef DEBUG_MMU - dump_mmu(env); + dump_mmu(stdout, fprintf, env1); #endif tlb_flush(env, 1); } @@ -2566,7 +3124,7 @@ PRIx64 "\n", reg, oldreg, env->immuregs[reg]); } #ifdef DEBUG_MMU - dump_mmu(env); + dump_mmu(stdout, fprintf, env); #endif return; } @@ -2583,12 +3141,12 @@ #ifdef DEBUG_MMU DPRINTF_MMU("immu data access replaced entry [%i]\n", i); - dump_mmu(env); + dump_mmu(stdout, fprintf, env); #endif return; } case 0x57: // I-MMU demap - demap_tlb(env->itlb, val, "immu", env); + demap_tlb(env->itlb, addr, "immu", env); return; case 0x58: // D-MMU regs { @@ -2609,9 +3167,15 @@ break; case 1: // Primary context env->dmmu.mmu_primary_context = val; + /* can be optimized to only flush MMU_USER_IDX + and MMU_KERNEL_IDX entries */ + tlb_flush(env, 1); break; case 2: // Secondary context env->dmmu.mmu_secondary_context = val; + /* can be optimized to only flush MMU_USER_SECONDARY_IDX + and MMU_KERNEL_SECONDARY_IDX entries */ + tlb_flush(env, 1); break; case 5: // TSB access DPRINTF_MMU("dmmu TSB write: 0x%016" PRIx64 " -> 0x%016" @@ -2633,7 +3197,7 @@ PRIx64 "\n", reg, oldreg, env->dmmuregs[reg]); } #ifdef DEBUG_MMU - dump_mmu(env); + dump_mmu(stdout, fprintf, env); #endif return; } @@ -2648,12 +3212,12 @@ #ifdef DEBUG_MMU DPRINTF_MMU("dmmu data access replaced entry [%i]\n", i); - dump_mmu(env); + dump_mmu(stdout, fprintf, env); #endif return; } case 0x5f: // D-MMU demap - demap_tlb(env->dtlb, val, "dmmu", env); + demap_tlb(env->dtlb, addr, "dmmu", env); return; case 0x49: // Interrupt data receive // XXX @@ -2694,35 +3258,39 @@ void helper_ldda_asi(target_ulong addr, int asi, int rd) { if ((asi < 0x80 && (env->pstate & PS_PRIV) == 0) - || ((env->def->features & CPU_FEATURE_HYPV) + || (cpu_has_hypervisor(env) && asi >= 0x30 && asi < 0x80 && !(env->hpstate & HS_PRIV))) raise_exception(TT_PRIV_ACT); + addr = asi_address_mask(env, asi, addr); + switch (asi) { +#if !defined(CONFIG_USER_ONLY) case 0x24: // Nucleus quad LDD 128 bit atomic case 0x2c: // Nucleus quad LDD 128 bit atomic LE helper_check_align(addr, 0xf); if (rd == 0) { - env->gregs[1] = ldq_kernel(addr + 8); + env->gregs[1] = ldq_nucleus(addr + 8); if (asi == 0x2c) bswap64s(&env->gregs[1]); } else if (rd < 8) { - env->gregs[rd] = ldq_kernel(addr); - env->gregs[rd + 1] = ldq_kernel(addr + 8); + env->gregs[rd] = ldq_nucleus(addr); + env->gregs[rd + 1] = ldq_nucleus(addr + 8); if (asi == 0x2c) { bswap64s(&env->gregs[rd]); bswap64s(&env->gregs[rd + 1]); } } else { - env->regwptr[rd] = ldq_kernel(addr); - env->regwptr[rd + 1] = ldq_kernel(addr + 8); + env->regwptr[rd] = ldq_nucleus(addr); + env->regwptr[rd + 1] = ldq_nucleus(addr + 8); if (asi == 0x2c) { bswap64s(&env->regwptr[rd]); bswap64s(&env->regwptr[rd + 1]); } } break; +#endif default: helper_check_align(addr, 0x3); if (rd == 0) @@ -2744,6 +3312,8 @@ target_ulong val; helper_check_align(addr, 3); + addr = asi_address_mask(env, asi, addr); + switch (asi) { case 0xf0: // Block load primary case 0xf1: // Block load secondary @@ -2761,6 +3331,20 @@ } return; + case 0x70: // Block load primary, user privilege + case 0x71: // Block load secondary, user privilege + if (rd & 7) { + raise_exception(TT_ILL_INSN); + return; + } + helper_check_align(addr, 0x3f); + for (i = 0; i < 16; i++) { + *(uint32_t *)&env->fpr[rd++] = helper_ld_asi(addr, asi & 0x1f, 4, + 0); + addr += 4; + } + + return; default: break; } @@ -2786,6 +3370,8 @@ target_ulong val = 0; helper_check_align(addr, 3); + addr = asi_address_mask(env, asi, addr); + switch (asi) { case 0xe0: // UA2007 Block commit store primary (cache flush) case 0xe1: // UA2007 Block commit store secondary (cache flush) @@ -2805,6 +3391,20 @@ } return; + case 0x70: // Block store primary, user privilege + case 0x71: // Block store secondary, user privilege + if (rd & 7) { + raise_exception(TT_ILL_INSN); + return; + } + helper_check_align(addr, 0x3f); + for (i = 0; i < 16; i++) { + val = *(uint32_t *)&env->fpr[rd++]; + helper_st_asi(addr, val, asi & 0x1f, 4); + addr += 4; + } + + return; default: break; } @@ -2858,7 +3458,7 @@ raise_exception(TT_ILL_INSN); env->psret = 1; - cwp = cpu_cwp_inc(env, env->cwp + 1) ; + cwp = cwp_inc(env->cwp + 1) ; if (env->wim & (1 << cwp)) { raise_exception(TT_WIN_UNF); } @@ -2867,13 +3467,14 @@ } #endif -target_ulong helper_udiv(target_ulong a, target_ulong b) +static target_ulong helper_udiv_common(target_ulong a, target_ulong b, int cc) { + int overflow = 0; uint64_t x0; uint32_t x1; x0 = (a & 0xffffffff) | ((int64_t) (env->y) << 32); - x1 = b; + x1 = (b & 0xffffffff); if (x1 == 0) { raise_exception(TT_DIV_ZERO); @@ -2881,21 +3482,36 @@ x0 = x0 / x1; if (x0 > 0xffffffff) { - env->cc_src2 = 1; - return 0xffffffff; - } else { - env->cc_src2 = 0; - return x0; + x0 = 0xffffffff; + overflow = 1; + } + + if (cc) { + env->cc_dst = x0; + env->cc_src2 = overflow; + env->cc_op = CC_OP_DIV; } + return x0; } -target_ulong helper_sdiv(target_ulong a, target_ulong b) +target_ulong helper_udiv(target_ulong a, target_ulong b) +{ + return helper_udiv_common(a, b, 0); +} + +target_ulong helper_udiv_cc(target_ulong a, target_ulong b) { + return helper_udiv_common(a, b, 1); +} + +static target_ulong helper_sdiv_common(target_ulong a, target_ulong b, int cc) +{ + int overflow = 0; int64_t x0; int32_t x1; x0 = (a & 0xffffffff) | ((int64_t) (env->y) << 32); - x1 = b; + x1 = (b & 0xffffffff); if (x1 == 0) { raise_exception(TT_DIV_ZERO); @@ -2903,12 +3519,26 @@ x0 = x0 / x1; if ((int32_t) x0 != x0) { - env->cc_src2 = 1; - return x0 < 0? 0x80000000: 0x7fffffff; - } else { - env->cc_src2 = 0; - return x0; + x0 = x0 < 0 ? 0x80000000: 0x7fffffff; + overflow = 1; } + + if (cc) { + env->cc_dst = x0; + env->cc_src2 = overflow; + env->cc_op = CC_OP_DIV; + } + return x0; +} + +target_ulong helper_sdiv(target_ulong a, target_ulong b) +{ + return helper_sdiv_common(a, b, 0); +} + +target_ulong helper_sdiv_cc(target_ulong a, target_ulong b) +{ + return helper_sdiv_common(a, b, 1); } void helper_stdf(target_ulong addr, int mem_idx) @@ -2916,23 +3546,23 @@ helper_check_align(addr, 7); #if !defined(CONFIG_USER_ONLY) switch (mem_idx) { - case 0: + case MMU_USER_IDX: stfq_user(addr, DT0); break; - case 1: + case MMU_KERNEL_IDX: stfq_kernel(addr, DT0); break; #ifdef TARGET_SPARC64 - case 2: + case MMU_HYPV_IDX: stfq_hypv(addr, DT0); break; #endif default: + DPRINTF_MMU("helper_stdf: need to check MMU idx %d\n", mem_idx); break; } #else - address_mask(env, &addr); - stfq_raw(addr, DT0); + stfq_raw(address_mask(env, addr), DT0); #endif } @@ -2941,23 +3571,23 @@ helper_check_align(addr, 7); #if !defined(CONFIG_USER_ONLY) switch (mem_idx) { - case 0: + case MMU_USER_IDX: DT0 = ldfq_user(addr); break; - case 1: + case MMU_KERNEL_IDX: DT0 = ldfq_kernel(addr); break; #ifdef TARGET_SPARC64 - case 2: + case MMU_HYPV_IDX: DT0 = ldfq_hypv(addr); break; #endif default: + DPRINTF_MMU("helper_lddf: need to check MMU idx %d\n", mem_idx); break; } #else - address_mask(env, &addr); - DT0 = ldfq_raw(addr); + DT0 = ldfq_raw(address_mask(env, addr)); #endif } @@ -2969,30 +3599,30 @@ helper_check_align(addr, 7); #if !defined(CONFIG_USER_ONLY) switch (mem_idx) { - case 0: + case MMU_USER_IDX: u.ll.upper = ldq_user(addr); u.ll.lower = ldq_user(addr + 8); QT0 = u.q; break; - case 1: + case MMU_KERNEL_IDX: u.ll.upper = ldq_kernel(addr); u.ll.lower = ldq_kernel(addr + 8); QT0 = u.q; break; #ifdef TARGET_SPARC64 - case 2: + case MMU_HYPV_IDX: u.ll.upper = ldq_hypv(addr); u.ll.lower = ldq_hypv(addr + 8); QT0 = u.q; break; #endif default: + DPRINTF_MMU("helper_ldqf: need to check MMU idx %d\n", mem_idx); break; } #else - address_mask(env, &addr); - u.ll.upper = ldq_raw(addr); - u.ll.lower = ldq_raw((addr + 8) & 0xffffffffULL); + u.ll.upper = ldq_raw(address_mask(env, addr)); + u.ll.lower = ldq_raw(address_mask(env, addr + 8)); QT0 = u.q; #endif } @@ -3005,31 +3635,31 @@ helper_check_align(addr, 7); #if !defined(CONFIG_USER_ONLY) switch (mem_idx) { - case 0: + case MMU_USER_IDX: u.q = QT0; stq_user(addr, u.ll.upper); stq_user(addr + 8, u.ll.lower); break; - case 1: + case MMU_KERNEL_IDX: u.q = QT0; stq_kernel(addr, u.ll.upper); stq_kernel(addr + 8, u.ll.lower); break; #ifdef TARGET_SPARC64 - case 2: + case MMU_HYPV_IDX: u.q = QT0; stq_hypv(addr, u.ll.upper); stq_hypv(addr + 8, u.ll.lower); break; #endif default: + DPRINTF_MMU("helper_stqf: need to check MMU idx %d\n", mem_idx); break; } #else u.q = QT0; - address_mask(env, &addr); - stq_raw(addr, u.ll.upper); - stq_raw((addr + 8) & 0xffffffffULL, u.ll.lower); + stq_raw(address_mask(env, addr), u.ll.upper); + stq_raw(address_mask(env, addr + 8), u.ll.lower); #endif } @@ -3082,7 +3712,7 @@ { uint32_t cwp; - cwp = cpu_cwp_dec(env, env->cwp - 1); + cwp = cwp_dec(env->cwp - 1); if (env->wim & (1 << cwp)) { raise_exception(TT_WIN_OVF); } @@ -3093,7 +3723,7 @@ { uint32_t cwp; - cwp = cpu_cwp_inc(env, env->cwp + 1); + cwp = cwp_inc(env->cwp + 1); if (env->wim & (1 << cwp)) { raise_exception(TT_WIN_UNF); } @@ -3102,15 +3732,16 @@ void helper_wrpsr(target_ulong new_psr) { - if ((new_psr & PSR_CWP) >= env->nwindows) + if ((new_psr & PSR_CWP) >= env->nwindows) { raise_exception(TT_ILL_INSN); - else - PUT_PSR(env, new_psr); + } else { + cpu_put_psr(env, new_psr); + } } target_ulong helper_rdpsr(void) { - return GET_PSR(env); + return get_psr(); } #else @@ -3120,7 +3751,7 @@ { uint32_t cwp; - cwp = cpu_cwp_dec(env, env->cwp - 1); + cwp = cwp_dec(env->cwp - 1); if (env->cansave == 0) { raise_exception(TT_SPILL | (env->otherwin != 0 ? (TT_WOTHER | ((env->wstate & 0x38) >> 1)): @@ -3141,7 +3772,7 @@ { uint32_t cwp; - cwp = cpu_cwp_inc(env, env->cwp + 1); + cwp = cwp_inc(env->cwp + 1); if (env->canrestore == 0) { raise_exception(TT_FILL | (env->otherwin != 0 ? (TT_WOTHER | ((env->wstate & 0x38) >> 1)): @@ -3182,26 +3813,101 @@ env->otherwin--; } +static target_ulong get_ccr(void) +{ + target_ulong psr; + + psr = get_psr(); + + return ((env->xcc >> 20) << 4) | ((psr & PSR_ICC) >> 20); +} + +target_ulong cpu_get_ccr(CPUState *env1) +{ + CPUState *saved_env; + target_ulong ret; + + saved_env = env; + env = env1; + ret = get_ccr(); + env = saved_env; + return ret; +} + +static void put_ccr(target_ulong val) +{ + target_ulong tmp = val; + + env->xcc = (tmp >> 4) << 20; + env->psr = (tmp & 0xf) << 20; + CC_OP = CC_OP_FLAGS; +} + +void cpu_put_ccr(CPUState *env1, target_ulong val) +{ + CPUState *saved_env; + + saved_env = env; + env = env1; + put_ccr(val); + env = saved_env; +} + +static target_ulong get_cwp64(void) +{ + return env->nwindows - 1 - env->cwp; +} + +target_ulong cpu_get_cwp64(CPUState *env1) +{ + CPUState *saved_env; + target_ulong ret; + + saved_env = env; + env = env1; + ret = get_cwp64(); + env = saved_env; + return ret; +} + +static void put_cwp64(int cwp) +{ + if (unlikely(cwp >= env->nwindows || cwp < 0)) { + cwp %= env->nwindows; + } + set_cwp(env->nwindows - 1 - cwp); +} + +void cpu_put_cwp64(CPUState *env1, int cwp) +{ + CPUState *saved_env; + + saved_env = env; + env = env1; + put_cwp64(cwp); + env = saved_env; +} + target_ulong helper_rdccr(void) { - return GET_CCR(env); + return get_ccr(); } void helper_wrccr(target_ulong new_ccr) { - PUT_CCR(env, new_ccr); + put_ccr(new_ccr); } // CWP handling is reversed in V9, but we still use the V8 register // order. target_ulong helper_rdcwp(void) { - return GET_CWP64(env); + return get_cwp64(); } void helper_wrcwp(target_ulong new_cwp) { - PUT_CWP64(env, new_cwp); + put_cwp64(new_cwp); } // This function uses non-native bit order @@ -3240,10 +3946,16 @@ return ctpop64(val); } -static inline uint64_t *get_gregset(uint64_t pstate) +static inline uint64_t *get_gregset(uint32_t pstate) { switch (pstate) { default: + DPRINTF_PSTATE("ERROR in get_gregset: active pstate bits=%x%s%s%s\n", + pstate, + (pstate & PS_IG) ? " IG" : "", + (pstate & PS_MG) ? " MG" : "", + (pstate & PS_AG) ? " AG" : ""); + /* pass through to normal set of global registers */ case 0: return env->bgregs; case PS_AG: @@ -3255,9 +3967,9 @@ } } -static inline void change_pstate(uint64_t new_pstate) +static inline void change_pstate(uint32_t new_pstate) { - uint64_t pstate_regs, new_pstate_regs; + uint32_t pstate_regs, new_pstate_regs; uint64_t *src, *dst; if (env->def->features & CPU_FEATURE_GL) { @@ -3269,18 +3981,44 @@ new_pstate_regs = new_pstate & 0xc01; if (new_pstate_regs != pstate_regs) { + DPRINTF_PSTATE("change_pstate: switching regs old=%x new=%x\n", + pstate_regs, new_pstate_regs); // Switch global register bank src = get_gregset(new_pstate_regs); dst = get_gregset(pstate_regs); memcpy32(dst, env->gregs); memcpy32(env->gregs, src); } + else { + DPRINTF_PSTATE("change_pstate: regs new=%x (unchanged)\n", + new_pstate_regs); + } env->pstate = new_pstate; } void helper_wrpstate(target_ulong new_state) { change_pstate(new_state & 0xf3f); + +#if !defined(CONFIG_USER_ONLY) + if (cpu_interrupts_enabled(env)) { + cpu_check_irqs(env); + } +#endif +} + +void helper_wrpil(target_ulong new_pil) +{ +#if !defined(CONFIG_USER_ONLY) + DPRINTF_PSTATE("helper_wrpil old=%x new=%x\n", + env->psrpil, (uint32_t)new_pil); + + env->psrpil = new_pil; + + if (cpu_interrupts_enabled(env)) { + cpu_check_irqs(env); + } +#endif } void helper_done(void) @@ -3289,11 +4027,19 @@ env->pc = tsptr->tnpc; env->npc = tsptr->tnpc + 4; - PUT_CCR(env, tsptr->tstate >> 32); + put_ccr(tsptr->tstate >> 32); env->asi = (tsptr->tstate >> 24) & 0xff; change_pstate((tsptr->tstate >> 8) & 0xf3f); - PUT_CWP64(env, tsptr->tstate & 0xff); + put_cwp64(tsptr->tstate & 0xff); env->tl--; + + DPRINTF_PSTATE("... helper_done tl=%d\n", env->tl); + +#if !defined(CONFIG_USER_ONLY) + if (cpu_interrupts_enabled(env)) { + cpu_check_irqs(env); + } +#endif } void helper_retry(void) @@ -3302,26 +4048,47 @@ env->pc = tsptr->tpc; env->npc = tsptr->tnpc; - PUT_CCR(env, tsptr->tstate >> 32); + put_ccr(tsptr->tstate >> 32); env->asi = (tsptr->tstate >> 24) & 0xff; change_pstate((tsptr->tstate >> 8) & 0xf3f); - PUT_CWP64(env, tsptr->tstate & 0xff); + put_cwp64(tsptr->tstate & 0xff); env->tl--; + + DPRINTF_PSTATE("... helper_retry tl=%d\n", env->tl); + +#if !defined(CONFIG_USER_ONLY) + if (cpu_interrupts_enabled(env)) { + cpu_check_irqs(env); + } +#endif +} + +static void do_modify_softint(const char* operation, uint32_t value) +{ + if (env->softint != value) { + env->softint = value; + DPRINTF_PSTATE(": %s new %08x\n", operation, env->softint); +#if !defined(CONFIG_USER_ONLY) + if (cpu_interrupts_enabled(env)) { + cpu_check_irqs(env); + } +#endif + } } void helper_set_softint(uint64_t value) { - env->softint |= (uint32_t)value; + do_modify_softint("helper_set_softint", env->softint | (uint32_t)value); } void helper_clear_softint(uint64_t value) { - env->softint &= (uint32_t)~value; + do_modify_softint("helper_clear_softint", env->softint & (uint32_t)~value); } void helper_write_softint(uint64_t value) { - env->softint = (uint32_t)value; + do_modify_softint("helper_write_softint", (uint32_t)value); } #endif @@ -3435,9 +4202,9 @@ } tsptr = cpu_tsptr(env); - tsptr->tstate = ((uint64_t)GET_CCR(env) << 32) | + tsptr->tstate = (get_ccr() << 32) | ((env->asi & 0xff) << 24) | ((env->pstate & 0xf3f) << 8) | - GET_CWP64(env); + get_cwp64(); tsptr->tpc = env->pc; tsptr->tnpc = env->npc; tsptr->tt = intno; @@ -3447,10 +4214,10 @@ change_pstate(PS_PEF | PS_PRIV | PS_IG); break; case TT_TFAULT: - case TT_TMISS: case TT_DFAULT: - case TT_DMISS: - case TT_DPROT: + case TT_TMISS ... TT_TMISS + 3: + case TT_DMISS ... TT_DMISS + 3: + case TT_DPROT ... TT_DPROT + 3: change_pstate(PS_PEF | PS_PRIV | PS_MG); break; default: @@ -3458,17 +4225,18 @@ break; } - if (intno == TT_CLRWIN) - cpu_set_cwp(env, cpu_cwp_dec(env, env->cwp - 1)); - else if ((intno & 0x1c0) == TT_SPILL) - cpu_set_cwp(env, cpu_cwp_dec(env, env->cwp - env->cansave - 2)); - else if ((intno & 0x1c0) == TT_FILL) - cpu_set_cwp(env, cpu_cwp_inc(env, env->cwp + 1)); + if (intno == TT_CLRWIN) { + set_cwp(cwp_dec(env->cwp - 1)); + } else if ((intno & 0x1c0) == TT_SPILL) { + set_cwp(cwp_dec(env->cwp - env->cansave - 2)); + } else if ((intno & 0x1c0) == TT_FILL) { + set_cwp(cwp_inc(env->cwp + 1)); + } env->tbr &= ~0x7fffULL; env->tbr |= ((env->tl > 1) ? 1 << 14 : 0) | (intno << 5); env->pc = env->tbr; env->npc = env->pc + 4; - env->exception_index = 0; + env->exception_index = -1; } #else #ifdef DEBUG_PCALL @@ -3554,8 +4322,8 @@ } #endif env->psret = 0; - cwp = cpu_cwp_dec(env, env->cwp - 1); - cpu_set_cwp(env, cwp); + cwp = cwp_dec(env->cwp - 1); + set_cwp(cwp); env->regwptr[9] = env->pc; env->regwptr[10] = env->npc; env->psrps = env->psrs; @@ -3563,7 +4331,14 @@ env->tbr = (env->tbr & TBR_BASE_MASK) | (intno << 4); env->pc = env->tbr; env->npc = env->pc + 4; - env->exception_index = 0; + env->exception_index = -1; + +#if !defined(CONFIG_USER_ONLY) + /* IRQ acknowledgment */ + if ((intno & ~15) == TT_EXTINT && env->qemu_irq_ack != NULL) { + env->qemu_irq_ack(env->irq_manager, intno); + } +#endif } #endif @@ -3638,13 +4413,15 @@ env = saved_env; } -#endif +#endif /* !CONFIG_USER_ONLY */ #ifndef TARGET_SPARC64 +#if !defined(CONFIG_USER_ONLY) void do_unassigned_access(target_phys_addr_t addr, int is_write, int is_exec, int is_asi, int size) { CPUState *saved_env; + int fault_type; /* XXX: hack to restore env in all cases, even if not called from generated code */ @@ -3662,48 +4439,76 @@ is_exec ? "exec" : is_write ? "write" : "read", size, size == 1 ? "" : "s", addr, env->pc); #endif - if (env->mmuregs[3]) /* Fault status register */ - env->mmuregs[3] = 1; /* overflow (not read before another fault) */ - if (is_asi) - env->mmuregs[3] |= 1 << 16; - if (env->psrs) - env->mmuregs[3] |= 1 << 5; - if (is_exec) - env->mmuregs[3] |= 1 << 6; - if (is_write) - env->mmuregs[3] |= 1 << 7; - env->mmuregs[3] |= (5 << 2) | 2; - env->mmuregs[4] = addr; /* Fault address register */ + /* Don't overwrite translation and access faults */ + fault_type = (env->mmuregs[3] & 0x1c) >> 2; + if ((fault_type > 4) || (fault_type == 0)) { + env->mmuregs[3] = 0; /* Fault status register */ + if (is_asi) + env->mmuregs[3] |= 1 << 16; + if (env->psrs) + env->mmuregs[3] |= 1 << 5; + if (is_exec) + env->mmuregs[3] |= 1 << 6; + if (is_write) + env->mmuregs[3] |= 1 << 7; + env->mmuregs[3] |= (5 << 2) | 2; + /* SuperSPARC will never place instruction fault addresses in the FAR */ + if (!is_exec) { + env->mmuregs[4] = addr; /* Fault address register */ + } + } + /* overflow (same type fault was not read before another fault) */ + if (fault_type == ((env->mmuregs[3] & 0x1c)) >> 2) { + env->mmuregs[3] |= 1; + } + if ((env->mmuregs[0] & MMU_E) && !(env->mmuregs[0] & MMU_NF)) { if (is_exec) raise_exception(TT_CODE_ACCESS); else raise_exception(TT_DATA_ACCESS); } + + /* flush neverland mappings created during no-fault mode, + so the sequential MMU faults report proper fault types */ + if (env->mmuregs[0] & MMU_NF) { + tlb_flush(env, 1); + } + env = saved_env; } +#endif +#else +#if defined(CONFIG_USER_ONLY) +static void do_unassigned_access(target_ulong addr, int is_write, int is_exec, + int is_asi, int size) #else void do_unassigned_access(target_phys_addr_t addr, int is_write, int is_exec, int is_asi, int size) +#endif { -#ifdef DEBUG_UNASSIGNED CPUState *saved_env; /* XXX: hack to restore env in all cases, even if not called from generated code */ saved_env = env; env = cpu_single_env; + +#ifdef DEBUG_UNASSIGNED printf("Unassigned mem access to " TARGET_FMT_plx " from " TARGET_FMT_lx "\n", addr, env->pc); - env = saved_env; #endif + if (is_exec) raise_exception(TT_CODE_ACCESS); else raise_exception(TT_DATA_ACCESS); + + env = saved_env; } #endif + #ifdef TARGET_SPARC64 void helper_tick_set_count(void *opaque, uint64_t count) { diff -Nru qemu-kvm-0.12.5+noroms/target-sparc/translate.c qemu-kvm-0.14.1/target-sparc/translate.c --- qemu-kvm-0.12.5+noroms/target-sparc/translate.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/target-sparc/translate.c 2011-05-11 13:29:46.000000000 +0000 @@ -49,7 +49,7 @@ #ifndef CONFIG_USER_ONLY static TCGv cpu_tbr; #endif -static TCGv cpu_cond, cpu_src1, cpu_src2, cpu_dst, cpu_addr, cpu_val; +static TCGv cpu_cond, cpu_dst, cpu_addr, cpu_val; #ifdef TARGET_SPARC64 static TCGv_i32 cpu_xcc, cpu_asi, cpu_fprs; static TCGv cpu_gsr; @@ -66,6 +66,9 @@ /* Floating point registers */ static TCGv_i32 cpu_fpr[TARGET_FPREGS]; +static target_ulong gen_opc_npc[OPC_BUF_SIZE]; +static target_ulong gen_opc_jump_pc[2]; + #include "gen-icount.h" typedef struct DisasContext { @@ -76,6 +79,7 @@ int mem_idx; int fpu_enabled; int address_mask_32bit; + int singlestep; uint32_t cc_op; /* current CC operation */ struct TranslationBlock *tb; sparc_def_t *def; @@ -179,9 +183,9 @@ #define hypervisor(dc) 0 #endif #else -#define supervisor(dc) (dc->mem_idx >= 1) +#define supervisor(dc) (dc->mem_idx >= MMU_KERNEL_IDX) #ifdef TARGET_SPARC64 -#define hypervisor(dc) (dc->mem_idx == 2) +#define hypervisor(dc) (dc->mem_idx == MMU_HYPV_IDX) #else #endif #endif @@ -231,7 +235,8 @@ tb = s->tb; if ((pc & TARGET_PAGE_MASK) == (tb->pc & TARGET_PAGE_MASK) && - (npc & TARGET_PAGE_MASK) == (tb->pc & TARGET_PAGE_MASK)) { + (npc & TARGET_PAGE_MASK) == (tb->pc & TARGET_PAGE_MASK) && + !s->singlestep) { /* jump to same page: we can use a direct jump */ tcg_gen_goto_tb(tb_num); tcg_gen_movi_tl(cpu_pc, pc); @@ -327,24 +332,132 @@ tcg_gen_mov_tl(dst, cpu_cc_dst); } -static inline void gen_op_addxi_cc(TCGv dst, TCGv src1, target_long src2) +static TCGv_i32 gen_add32_carry32(void) { - tcg_gen_mov_tl(cpu_cc_src, src1); - tcg_gen_movi_tl(cpu_cc_src2, src2); - gen_mov_reg_C(cpu_tmp0, cpu_psr); - tcg_gen_add_tl(cpu_cc_dst, cpu_cc_src, cpu_tmp0); - tcg_gen_addi_tl(cpu_cc_dst, cpu_cc_dst, src2); - tcg_gen_mov_tl(dst, cpu_cc_dst); + TCGv_i32 carry_32, cc_src1_32, cc_src2_32; + + /* Carry is computed from a previous add: (dst < src) */ +#if TARGET_LONG_BITS == 64 + cc_src1_32 = tcg_temp_new_i32(); + cc_src2_32 = tcg_temp_new_i32(); + tcg_gen_trunc_i64_i32(cc_src1_32, cpu_cc_dst); + tcg_gen_trunc_i64_i32(cc_src2_32, cpu_cc_src); +#else + cc_src1_32 = cpu_cc_dst; + cc_src2_32 = cpu_cc_src; +#endif + + carry_32 = tcg_temp_new_i32(); + tcg_gen_setcond_i32(TCG_COND_LTU, carry_32, cc_src1_32, cc_src2_32); + +#if TARGET_LONG_BITS == 64 + tcg_temp_free_i32(cc_src1_32); + tcg_temp_free_i32(cc_src2_32); +#endif + + return carry_32; } -static inline void gen_op_addx_cc(TCGv dst, TCGv src1, TCGv src2) +static TCGv_i32 gen_sub32_carry32(void) { - tcg_gen_mov_tl(cpu_cc_src, src1); - tcg_gen_mov_tl(cpu_cc_src2, src2); - gen_mov_reg_C(cpu_tmp0, cpu_psr); - tcg_gen_add_tl(cpu_cc_dst, cpu_cc_src, cpu_tmp0); - tcg_gen_add_tl(cpu_cc_dst, cpu_cc_dst, cpu_cc_src2); - tcg_gen_mov_tl(dst, cpu_cc_dst); + TCGv_i32 carry_32, cc_src1_32, cc_src2_32; + + /* Carry is computed from a previous borrow: (src1 < src2) */ +#if TARGET_LONG_BITS == 64 + cc_src1_32 = tcg_temp_new_i32(); + cc_src2_32 = tcg_temp_new_i32(); + tcg_gen_trunc_i64_i32(cc_src1_32, cpu_cc_src); + tcg_gen_trunc_i64_i32(cc_src2_32, cpu_cc_src2); +#else + cc_src1_32 = cpu_cc_src; + cc_src2_32 = cpu_cc_src2; +#endif + + carry_32 = tcg_temp_new_i32(); + tcg_gen_setcond_i32(TCG_COND_LTU, carry_32, cc_src1_32, cc_src2_32); + +#if TARGET_LONG_BITS == 64 + tcg_temp_free_i32(cc_src1_32); + tcg_temp_free_i32(cc_src2_32); +#endif + + return carry_32; +} + +static void gen_op_addx_int(DisasContext *dc, TCGv dst, TCGv src1, + TCGv src2, int update_cc) +{ + TCGv_i32 carry_32; + TCGv carry; + + switch (dc->cc_op) { + case CC_OP_DIV: + case CC_OP_LOGIC: + /* Carry is known to be zero. Fall back to plain ADD. */ + if (update_cc) { + gen_op_add_cc(dst, src1, src2); + } else { + tcg_gen_add_tl(dst, src1, src2); + } + return; + + case CC_OP_ADD: + case CC_OP_TADD: + case CC_OP_TADDTV: +#if TCG_TARGET_REG_BITS == 32 && TARGET_LONG_BITS == 32 + { + /* For 32-bit hosts, we can re-use the host's hardware carry + generation by using an ADD2 opcode. We discard the low + part of the output. Ideally we'd combine this operation + with the add that generated the carry in the first place. */ + TCGv dst_low = tcg_temp_new(); + tcg_gen_op6_i32(INDEX_op_add2_i32, dst_low, dst, + cpu_cc_src, src1, cpu_cc_src2, src2); + tcg_temp_free(dst_low); + goto add_done; + } +#endif + carry_32 = gen_add32_carry32(); + break; + + case CC_OP_SUB: + case CC_OP_TSUB: + case CC_OP_TSUBTV: + carry_32 = gen_sub32_carry32(); + break; + + default: + /* We need external help to produce the carry. */ + carry_32 = tcg_temp_new_i32(); + gen_helper_compute_C_icc(carry_32); + break; + } + +#if TARGET_LONG_BITS == 64 + carry = tcg_temp_new(); + tcg_gen_extu_i32_i64(carry, carry_32); +#else + carry = carry_32; +#endif + + tcg_gen_add_tl(dst, src1, src2); + tcg_gen_add_tl(dst, dst, carry); + + tcg_temp_free_i32(carry_32); +#if TARGET_LONG_BITS == 64 + tcg_temp_free(carry); +#endif + +#if TCG_TARGET_REG_BITS == 32 && TARGET_LONG_BITS == 32 + add_done: +#endif + if (update_cc) { + tcg_gen_mov_tl(cpu_cc_src, src1); + tcg_gen_mov_tl(cpu_cc_src2, src2); + tcg_gen_mov_tl(cpu_cc_dst, dst); + tcg_gen_movi_i32(cpu_cc_op, CC_OP_ADDX); + dc->cc_op = CC_OP_ADDX; + } } static inline void gen_op_tadd_cc(TCGv dst, TCGv src1, TCGv src2) @@ -410,24 +523,80 @@ tcg_gen_mov_tl(dst, cpu_cc_dst); } -static inline void gen_op_subxi_cc(TCGv dst, TCGv src1, target_long src2) +static void gen_op_subx_int(DisasContext *dc, TCGv dst, TCGv src1, + TCGv src2, int update_cc) { - tcg_gen_mov_tl(cpu_cc_src, src1); - tcg_gen_movi_tl(cpu_cc_src2, src2); - gen_mov_reg_C(cpu_tmp0, cpu_psr); - tcg_gen_sub_tl(cpu_cc_dst, cpu_cc_src, cpu_tmp0); - tcg_gen_subi_tl(cpu_cc_dst, cpu_cc_dst, src2); - tcg_gen_mov_tl(dst, cpu_cc_dst); -} + TCGv_i32 carry_32; + TCGv carry; -static inline void gen_op_subx_cc(TCGv dst, TCGv src1, TCGv src2) -{ - tcg_gen_mov_tl(cpu_cc_src, src1); - tcg_gen_mov_tl(cpu_cc_src2, src2); - gen_mov_reg_C(cpu_tmp0, cpu_psr); - tcg_gen_sub_tl(cpu_cc_dst, cpu_cc_src, cpu_tmp0); - tcg_gen_sub_tl(cpu_cc_dst, cpu_cc_dst, cpu_cc_src2); - tcg_gen_mov_tl(dst, cpu_cc_dst); + switch (dc->cc_op) { + case CC_OP_DIV: + case CC_OP_LOGIC: + /* Carry is known to be zero. Fall back to plain SUB. */ + if (update_cc) { + gen_op_sub_cc(dst, src1, src2); + } else { + tcg_gen_sub_tl(dst, src1, src2); + } + return; + + case CC_OP_ADD: + case CC_OP_TADD: + case CC_OP_TADDTV: + carry_32 = gen_add32_carry32(); + break; + + case CC_OP_SUB: + case CC_OP_TSUB: + case CC_OP_TSUBTV: +#if TCG_TARGET_REG_BITS == 32 && TARGET_LONG_BITS == 32 + { + /* For 32-bit hosts, we can re-use the host's hardware carry + generation by using a SUB2 opcode. We discard the low + part of the output. Ideally we'd combine this operation + with the add that generated the carry in the first place. */ + TCGv dst_low = tcg_temp_new(); + tcg_gen_op6_i32(INDEX_op_sub2_i32, dst_low, dst, + cpu_cc_src, src1, cpu_cc_src2, src2); + tcg_temp_free(dst_low); + goto sub_done; + } +#endif + carry_32 = gen_sub32_carry32(); + break; + + default: + /* We need external help to produce the carry. */ + carry_32 = tcg_temp_new_i32(); + gen_helper_compute_C_icc(carry_32); + break; + } + +#if TARGET_LONG_BITS == 64 + carry = tcg_temp_new(); + tcg_gen_extu_i32_i64(carry, carry_32); +#else + carry = carry_32; +#endif + + tcg_gen_sub_tl(dst, src1, src2); + tcg_gen_sub_tl(dst, dst, carry); + + tcg_temp_free_i32(carry_32); +#if TARGET_LONG_BITS == 64 + tcg_temp_free(carry); +#endif + +#if TCG_TARGET_REG_BITS == 32 && TARGET_LONG_BITS == 32 + sub_done: +#endif + if (update_cc) { + tcg_gen_mov_tl(cpu_cc_src, src1); + tcg_gen_mov_tl(cpu_cc_src2, src2); + tcg_gen_mov_tl(cpu_cc_dst, dst); + tcg_gen_movi_i32(cpu_cc_op, CC_OP_SUBX); + dc->cc_op = CC_OP_SUBX; + } } static inline void gen_op_tsub_cc(TCGv dst, TCGv src1, TCGv src2) @@ -493,50 +662,53 @@ tcg_gen_mov_tl(dst, cpu_cc_dst); } -static inline void gen_op_umul(TCGv dst, TCGv src1, TCGv src2) +static inline void gen_op_multiply(TCGv dst, TCGv src1, TCGv src2, int sign_ext) { + TCGv_i32 r_src1, r_src2; TCGv_i64 r_temp, r_temp2; + r_src1 = tcg_temp_new_i32(); + r_src2 = tcg_temp_new_i32(); + + tcg_gen_trunc_tl_i32(r_src1, src1); + tcg_gen_trunc_tl_i32(r_src2, src2); + r_temp = tcg_temp_new_i64(); r_temp2 = tcg_temp_new_i64(); - tcg_gen_extu_tl_i64(r_temp, src2); - tcg_gen_extu_tl_i64(r_temp2, src1); + if (sign_ext) { + tcg_gen_ext_i32_i64(r_temp, r_src2); + tcg_gen_ext_i32_i64(r_temp2, r_src1); + } else { + tcg_gen_extu_i32_i64(r_temp, r_src2); + tcg_gen_extu_i32_i64(r_temp2, r_src1); + } + tcg_gen_mul_i64(r_temp2, r_temp, r_temp2); tcg_gen_shri_i64(r_temp, r_temp2, 32); tcg_gen_trunc_i64_tl(cpu_tmp0, r_temp); tcg_temp_free_i64(r_temp); tcg_gen_andi_tl(cpu_y, cpu_tmp0, 0xffffffff); -#ifdef TARGET_SPARC64 - tcg_gen_mov_i64(dst, r_temp2); -#else + tcg_gen_trunc_i64_tl(dst, r_temp2); -#endif + tcg_temp_free_i64(r_temp2); + + tcg_temp_free_i32(r_src1); + tcg_temp_free_i32(r_src2); } -static inline void gen_op_smul(TCGv dst, TCGv src1, TCGv src2) +static inline void gen_op_umul(TCGv dst, TCGv src1, TCGv src2) { - TCGv_i64 r_temp, r_temp2; - - r_temp = tcg_temp_new_i64(); - r_temp2 = tcg_temp_new_i64(); - - tcg_gen_ext_tl_i64(r_temp, src2); - tcg_gen_ext_tl_i64(r_temp2, src1); - tcg_gen_mul_i64(r_temp2, r_temp, r_temp2); + /* zero-extend truncated operands before multiplication */ + gen_op_multiply(dst, src1, src2, 0); +} - tcg_gen_shri_i64(r_temp, r_temp2, 32); - tcg_gen_trunc_i64_tl(cpu_tmp0, r_temp); - tcg_temp_free_i64(r_temp); - tcg_gen_andi_tl(cpu_y, cpu_tmp0, 0xffffffff); -#ifdef TARGET_SPARC64 - tcg_gen_mov_i64(dst, r_temp2); -#else - tcg_gen_trunc_i64_tl(dst, r_temp2); -#endif - tcg_temp_free_i64(r_temp2); +static inline void gen_op_smul(TCGv dst, TCGv src1, TCGv src2) +{ + /* sign-extend truncated operands before multiplication */ + gen_op_multiply(dst, src1, src2, 1); } #ifdef TARGET_SPARC64 @@ -1628,12 +1800,13 @@ unsigned int rs1; rs1 = GET_FIELD(insn, 13, 17); - if (rs1 == 0) - r_rs1 = tcg_const_tl(0); // XXX how to free? - else if (rs1 < 8) + if (rs1 == 0) { + tcg_gen_movi_tl(def, 0); + } else if (rs1 < 8) { r_rs1 = cpu_gregs[rs1]; - else + } else { tcg_gen_ld_tl(def, cpu_regwptr, (rs1 - 8) * sizeof(target_ulong)); + } return r_rs1; } @@ -1642,20 +1815,17 @@ TCGv r_rs2 = def; if (IS_IMM) { /* immediate */ - target_long simm; - - simm = GET_FIELDs(insn, 19, 31); - r_rs2 = tcg_const_tl(simm); // XXX how to free? + target_long simm = GET_FIELDs(insn, 19, 31); + tcg_gen_movi_tl(def, simm); } else { /* register */ - unsigned int rs2; - - rs2 = GET_FIELD(insn, 27, 31); - if (rs2 == 0) - r_rs2 = tcg_const_tl(0); // XXX how to free? - else if (rs2 < 8) + unsigned int rs2 = GET_FIELD(insn, 27, 31); + if (rs2 == 0) { + tcg_gen_movi_tl(def, 0); + } else if (rs2 < 8) { r_rs2 = cpu_gregs[rs2]; - else + } else { tcg_gen_ld_tl(def, cpu_regwptr, (rs2 - 8) * sizeof(target_ulong)); + } } return r_rs2; } @@ -1663,27 +1833,27 @@ #ifdef TARGET_SPARC64 static inline void gen_load_trap_state_at_tl(TCGv_ptr r_tsptr, TCGv_ptr cpu_env) { - TCGv r_tl = tcg_temp_new(); + TCGv_i32 r_tl = tcg_temp_new_i32(); /* load env->tl into r_tl */ - { - TCGv_i32 r_tl_tmp = tcg_temp_new_i32(); - tcg_gen_ld_i32(r_tl_tmp, cpu_env, offsetof(CPUSPARCState, tl)); - tcg_gen_ext_i32_tl(r_tl, r_tl_tmp); - tcg_temp_free_i32(r_tl_tmp); - } + tcg_gen_ld_i32(r_tl, cpu_env, offsetof(CPUSPARCState, tl)); /* tl = [0 ... MAXTL_MASK] where MAXTL_MASK must be power of 2 */ - tcg_gen_andi_tl(r_tl, r_tl, MAXTL_MASK); + tcg_gen_andi_i32(r_tl, r_tl, MAXTL_MASK); /* calculate offset to current trap state from env->ts, reuse r_tl */ - tcg_gen_muli_tl(r_tl, r_tl, sizeof (trap_state)); + tcg_gen_muli_i32(r_tl, r_tl, sizeof (trap_state)); tcg_gen_addi_ptr(r_tsptr, cpu_env, offsetof(CPUState, ts)); /* tsptr = env->ts[env->tl & MAXTL_MASK] */ - tcg_gen_add_ptr(r_tsptr, r_tsptr, r_tl); + { + TCGv_ptr r_tl_tmp = tcg_temp_new_ptr(); + tcg_gen_ext_i32_ptr(r_tl_tmp, r_tl); + tcg_gen_add_ptr(r_tsptr, r_tsptr, r_tl_tmp); + tcg_temp_free_ptr(r_tl_tmp); + } - tcg_temp_free(r_tl); + tcg_temp_free_i32(r_tl); } #endif @@ -1698,6 +1868,7 @@ static void disas_sparc_insn(DisasContext * dc) { unsigned int insn, opc, rs1, rs2, rd; + TCGv cpu_src1, cpu_src2, cpu_tmp1, cpu_tmp2; target_long simm; if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP))) @@ -1707,8 +1878,8 @@ rd = GET_FIELD(insn, 2, 6); - cpu_src1 = tcg_temp_new(); // const - cpu_src2 = tcg_temp_new(); // const + cpu_tmp1 = cpu_src1 = tcg_temp_new(); + cpu_tmp2 = cpu_src2 = tcg_temp_new(); switch (opc) { case 0: /* branches/sethi */ @@ -1826,8 +1997,9 @@ } else tcg_gen_mov_tl(cpu_dst, cpu_src1); } + cond = GET_FIELD(insn, 3, 6); - if (cond == 0x8) { + if (cond == 0x8) { /* Trap Always */ save_state(dc, cpu_cond); if ((dc->def->features & CPU_FEATURE_HYPV) && supervisor(dc)) @@ -1836,7 +2008,15 @@ tcg_gen_andi_tl(cpu_dst, cpu_dst, V8_TRAP_MASK); tcg_gen_addi_tl(cpu_dst, cpu_dst, TT_TRAP); tcg_gen_trunc_tl_i32(cpu_tmp32, cpu_dst); - gen_helper_raise_exception(cpu_tmp32); + + if (rs2 == 0 && + dc->def->features & CPU_FEATURE_TA0_SHUTDOWN) { + + gen_helper_shutdown(); + + } else { + gen_helper_raise_exception(cpu_tmp32); + } } else if (cond != 0) { TCGv r_cond = tcg_temp_new(); int l1; @@ -1887,6 +2067,17 @@ case 0x10 ... 0x1f: /* implementation-dependent in the SPARCv8 manual, rdy on the microSPARC II */ + /* Read Asr17 */ + if (rs1 == 0x11 && dc->def->features & CPU_FEATURE_ASR17) { + TCGv r_const; + + /* Read Asr17 for a Leon3 monoprocessor */ + r_const = tcg_const_tl((1 << 8) + | (dc->def->nwindows - 1)); + gen_movl_TN_reg(rd, r_const); + tcg_temp_free(r_const); + break; + } #endif gen_movl_TN_reg(rd, cpu_y); break; @@ -2152,6 +2343,7 @@ rs1 = GET_FIELD(insn, 13, 17); rs2 = GET_FIELD(insn, 27, 31); xop = GET_FIELD(insn, 18, 26); + save_state(dc, cpu_cond); switch (xop) { case 0x1: /* fmovs */ tcg_gen_mov_i32(cpu_fpr[rd], cpu_fpr[rs2]); @@ -2465,6 +2657,7 @@ rs1 = GET_FIELD(insn, 13, 17); rs2 = GET_FIELD(insn, 27, 31); xop = GET_FIELD(insn, 18, 26); + save_state(dc, cpu_cond); #ifdef TARGET_SPARC64 if ((xop & 0x11f) == 0x005) { // V9 fmovsr int l1; @@ -2944,32 +3137,8 @@ } break; case 0x8: /* addx, V9 addc */ - if (IS_IMM) { - simm = GET_FIELDs(insn, 19, 31); - if (xop & 0x10) { - gen_helper_compute_psr(); - gen_op_addxi_cc(cpu_dst, cpu_src1, simm); - tcg_gen_movi_i32(cpu_cc_op, CC_OP_ADDX); - dc->cc_op = CC_OP_ADDX; - } else { - gen_helper_compute_psr(); - gen_mov_reg_C(cpu_tmp0, cpu_psr); - tcg_gen_addi_tl(cpu_tmp0, cpu_tmp0, simm); - tcg_gen_add_tl(cpu_dst, cpu_src1, cpu_tmp0); - } - } else { - if (xop & 0x10) { - gen_helper_compute_psr(); - gen_op_addx_cc(cpu_dst, cpu_src1, cpu_src2); - tcg_gen_movi_i32(cpu_cc_op, CC_OP_ADDX); - dc->cc_op = CC_OP_ADDX; - } else { - gen_helper_compute_psr(); - gen_mov_reg_C(cpu_tmp0, cpu_psr); - tcg_gen_add_tl(cpu_tmp0, cpu_src2, cpu_tmp0); - tcg_gen_add_tl(cpu_dst, cpu_src1, cpu_tmp0); - } - } + gen_op_addx_int(dc, cpu_dst, cpu_src1, cpu_src2, + (xop & 0x10)); break; #ifdef TARGET_SPARC64 case 0x9: /* V9 mulx */ @@ -3000,32 +3169,8 @@ } break; case 0xc: /* subx, V9 subc */ - if (IS_IMM) { - simm = GET_FIELDs(insn, 19, 31); - if (xop & 0x10) { - gen_helper_compute_psr(); - gen_op_subxi_cc(cpu_dst, cpu_src1, simm); - tcg_gen_movi_i32(cpu_cc_op, CC_OP_SUBX); - dc->cc_op = CC_OP_SUBX; - } else { - gen_helper_compute_psr(); - gen_mov_reg_C(cpu_tmp0, cpu_psr); - tcg_gen_addi_tl(cpu_tmp0, cpu_tmp0, simm); - tcg_gen_sub_tl(cpu_dst, cpu_src1, cpu_tmp0); - } - } else { - if (xop & 0x10) { - gen_helper_compute_psr(); - gen_op_subx_cc(cpu_dst, cpu_src1, cpu_src2); - tcg_gen_movi_i32(cpu_cc_op, CC_OP_SUBX); - dc->cc_op = CC_OP_SUBX; - } else { - gen_helper_compute_psr(); - gen_mov_reg_C(cpu_tmp0, cpu_psr); - tcg_gen_add_tl(cpu_tmp0, cpu_src2, cpu_tmp0); - tcg_gen_sub_tl(cpu_dst, cpu_src1, cpu_tmp0); - } - } + gen_op_subx_int(dc, cpu_dst, cpu_src1, cpu_src2, + (xop & 0x10)); break; #ifdef TARGET_SPARC64 case 0xd: /* V9 udivx */ @@ -3037,20 +3182,20 @@ #endif case 0xe: /* udiv */ CHECK_IU_FEATURE(dc, DIV); - gen_helper_udiv(cpu_dst, cpu_src1, cpu_src2); if (xop & 0x10) { - tcg_gen_mov_tl(cpu_cc_dst, cpu_dst); - tcg_gen_movi_i32(cpu_cc_op, CC_OP_DIV); + gen_helper_udiv_cc(cpu_dst, cpu_src1, cpu_src2); dc->cc_op = CC_OP_DIV; + } else { + gen_helper_udiv(cpu_dst, cpu_src1, cpu_src2); } break; case 0xf: /* sdiv */ CHECK_IU_FEATURE(dc, DIV); - gen_helper_sdiv(cpu_dst, cpu_src1, cpu_src2); if (xop & 0x10) { - tcg_gen_mov_tl(cpu_cc_dst, cpu_dst); - tcg_gen_movi_i32(cpu_cc_op, CC_OP_DIV); + gen_helper_sdiv_cc(cpu_dst, cpu_src1, cpu_src2); dc->cc_op = CC_OP_DIV; + } else { + gen_helper_sdiv(cpu_dst, cpu_src1, cpu_src2); } break; default: @@ -3165,8 +3310,9 @@ break; case 0xf: /* V9 sir, nop if user */ #if !defined(CONFIG_USER_ONLY) - if (supervisor(dc)) + if (supervisor(dc)) { ; // XXX + } #endif break; case 0x13: /* Graphics Status */ @@ -3361,20 +3507,17 @@ case 6: // pstate save_state(dc, cpu_cond); gen_helper_wrpstate(cpu_tmp0); - gen_op_next_insn(); - tcg_gen_exit_tb(0); - dc->is_br = 1; + dc->npc = DYNAMIC_PC; break; case 7: // tl + save_state(dc, cpu_cond); tcg_gen_trunc_tl_i32(cpu_tmp32, cpu_tmp0); tcg_gen_st_i32(cpu_tmp32, cpu_env, offsetof(CPUSPARCState, tl)); + dc->npc = DYNAMIC_PC; break; case 8: // pil - tcg_gen_trunc_tl_i32(cpu_tmp32, cpu_tmp0); - tcg_gen_st_i32(cpu_tmp32, cpu_env, - offsetof(CPUSPARCState, - psrpil)); + gen_helper_wrpil(cpu_tmp0); break; case 9: // cwp gen_helper_wrcwp(cpu_tmp0); @@ -4356,7 +4499,11 @@ if (rd == 1) { tcg_gen_qemu_ld64(cpu_tmp64, cpu_addr, dc->mem_idx); gen_helper_ldxfsr(cpu_tmp64); - } else + } else { + tcg_gen_qemu_ld32u(cpu_tmp0, cpu_addr, dc->mem_idx); + tcg_gen_trunc_tl_i32(cpu_tmp32, cpu_tmp0); + gen_helper_ldfsr(cpu_tmp32); + } #else { tcg_gen_qemu_ld32u(cpu_tmp32, cpu_addr, dc->mem_idx); @@ -4370,6 +4517,7 @@ CHECK_FPU_FEATURE(dc, FLOAT128); r_const = tcg_const_i32(dc->mem_idx); + gen_address_mask(dc, cpu_addr); gen_helper_ldqf(cpu_addr, r_const); tcg_temp_free_i32(r_const); gen_op_store_QT0_fpr(QFPREG(rd)); @@ -4380,6 +4528,7 @@ TCGv_i32 r_const; r_const = tcg_const_i32(dc->mem_idx); + gen_address_mask(dc, cpu_addr); gen_helper_lddf(cpu_addr, r_const); tcg_temp_free_i32(r_const); gen_op_store_DT0_fpr(DFPREG(rd)); @@ -4430,6 +4579,7 @@ #endif save_state(dc, cpu_cond); gen_st_asi(cpu_val, cpu_addr, insn, 4); + dc->npc = DYNAMIC_PC; break; case 0x15: /* stba, store byte alternate */ #ifndef TARGET_SPARC64 @@ -4440,6 +4590,7 @@ #endif save_state(dc, cpu_cond); gen_st_asi(cpu_val, cpu_addr, insn, 1); + dc->npc = DYNAMIC_PC; break; case 0x16: /* stha, store halfword alternate */ #ifndef TARGET_SPARC64 @@ -4450,6 +4601,7 @@ #endif save_state(dc, cpu_cond); gen_st_asi(cpu_val, cpu_addr, insn, 2); + dc->npc = DYNAMIC_PC; break; case 0x17: /* stda, store double word alternate */ #ifndef TARGET_SPARC64 @@ -4474,6 +4626,7 @@ case 0x1e: /* V9 stxa */ save_state(dc, cpu_cond); gen_st_asi(cpu_val, cpu_addr, insn, 8); + dc->npc = DYNAMIC_PC; break; #endif default: @@ -4511,6 +4664,7 @@ CHECK_FPU_FEATURE(dc, FLOAT128); gen_op_load_fpr_QT0(QFPREG(rd)); r_const = tcg_const_i32(dc->mem_idx); + gen_address_mask(dc, cpu_addr); gen_helper_stqf(cpu_addr, r_const); tcg_temp_free_i32(r_const); } @@ -4533,6 +4687,7 @@ gen_op_load_fpr_DT0(DFPREG(rd)); r_const = tcg_const_i32(dc->mem_idx); + gen_address_mask(dc, cpu_addr); gen_helper_stdf(cpu_addr, r_const); tcg_temp_free_i32(r_const); } @@ -4599,7 +4754,7 @@ dc->npc = dc->npc + 4; } jmp_insn: - return; + goto egress; illegal_insn: { TCGv_i32 r_const; @@ -4610,7 +4765,7 @@ tcg_temp_free_i32(r_const); dc->is_br = 1; } - return; + goto egress; unimp_flush: { TCGv_i32 r_const; @@ -4621,7 +4776,7 @@ tcg_temp_free_i32(r_const); dc->is_br = 1; } - return; + goto egress; #if !defined(CONFIG_USER_ONLY) priv_insn: { @@ -4633,19 +4788,19 @@ tcg_temp_free_i32(r_const); dc->is_br = 1; } - return; + goto egress; #endif nfpu_insn: save_state(dc, cpu_cond); gen_op_fpexception_im(FSR_FTT_UNIMPFPOP); dc->is_br = 1; - return; + goto egress; #if !defined(CONFIG_USER_ONLY) && !defined(TARGET_SPARC64) nfq_insn: save_state(dc, cpu_cond); gen_op_fpexception_im(FSR_FTT_SEQ_ERROR); dc->is_br = 1; - return; + goto egress; #endif #ifndef TARGET_SPARC64 ncp_insn: @@ -4658,8 +4813,11 @@ tcg_temp_free(r_const); dc->is_br = 1; } - return; + goto egress; #endif + egress: + tcg_temp_free(cpu_tmp1); + tcg_temp_free(cpu_tmp2); } static inline void gen_intermediate_code_internal(TranslationBlock * tb, @@ -4689,6 +4847,7 @@ #ifdef TARGET_SPARC64 dc->address_mask_32bit = env->pstate & PS_AM; #endif + dc->singlestep = (env->singlestep_enabled || singlestep); gen_opc_end = gen_opc_buf + OPC_MAX_SIZE; cpu_tmp0 = tcg_temp_new(); @@ -4749,9 +4908,7 @@ break; /* if single step mode, we generate only one instruction and generate an exception */ - if (env->singlestep_enabled || singlestep) { - tcg_gen_movi_tl(cpu_pc, dc->pc); - tcg_gen_exit_tb(0); + if (dc->singlestep) { break; } } while ((gen_opc_ptr < gen_opc_end) && @@ -4932,12 +5089,12 @@ if (npc == 1) { /* dynamic NPC: already stored */ } else if (npc == 2) { - target_ulong t2 = (target_ulong)(unsigned long)puc; - /* jump PC: use T2 and the jump targets of the translation */ - if (t2) + /* jump PC: use 'cond' and the jump targets of the translation */ + if (env->cond) { env->npc = gen_opc_jump_pc[0]; - else + } else { env->npc = gen_opc_jump_pc[1]; + } } else { env->npc = npc; } diff -Nru qemu-kvm-0.12.5+noroms/targphys.h qemu-kvm-0.14.1/targphys.h --- qemu-kvm-0.12.5+noroms/targphys.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/targphys.h 2011-05-11 13:29:46.000000000 +0000 @@ -5,10 +5,7 @@ #ifdef TARGET_PHYS_ADDR_BITS /* target_phys_addr_t is the type of a physical address (its size can - be different from 'target_ulong'). We have sizeof(target_phys_addr) - = max(sizeof(unsigned long), - sizeof(size_of_target_physical_address)) because we must pass a - host pointer to memory operations in some cases */ + be different from 'target_ulong'). */ #if TARGET_PHYS_ADDR_BITS == 32 typedef uint32_t target_phys_addr_t; diff -Nru qemu-kvm-0.12.5+noroms/tcg/arm/tcg-target.c qemu-kvm-0.14.1/tcg/arm/tcg-target.c --- qemu-kvm-0.12.5+noroms/tcg/arm/tcg-target.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/tcg/arm/tcg-target.c 2011-05-11 13:29:46.000000000 +0000 @@ -22,6 +22,51 @@ * THE SOFTWARE. */ +#if defined(__ARM_ARCH_7__) || \ + defined(__ARM_ARCH_7A__) || \ + defined(__ARM_ARCH_7EM__) || \ + defined(__ARM_ARCH_7M__) || \ + defined(__ARM_ARCH_7R__) +#define USE_ARMV7_INSTRUCTIONS +#endif + +#if defined(USE_ARMV7_INSTRUCTIONS) || \ + defined(__ARM_ARCH_6J__) || \ + defined(__ARM_ARCH_6K__) || \ + defined(__ARM_ARCH_6T2__) || \ + defined(__ARM_ARCH_6Z__) || \ + defined(__ARM_ARCH_6ZK__) +#define USE_ARMV6_INSTRUCTIONS +#endif + +#if defined(USE_ARMV6_INSTRUCTIONS) || \ + defined(__ARM_ARCH_5T__) || \ + defined(__ARM_ARCH_5TE__) || \ + defined(__ARM_ARCH_5TEJ__) +#define USE_ARMV5_INSTRUCTIONS +#endif + +#ifdef USE_ARMV5_INSTRUCTIONS +static const int use_armv5_instructions = 1; +#else +static const int use_armv5_instructions = 0; +#endif +#undef USE_ARMV5_INSTRUCTIONS + +#ifdef USE_ARMV6_INSTRUCTIONS +static const int use_armv6_instructions = 1; +#else +static const int use_armv6_instructions = 0; +#endif +#undef USE_ARMV6_INSTRUCTIONS + +#ifdef USE_ARMV7_INSTRUCTIONS +static const int use_armv7_instructions = 1; +#else +static const int use_armv7_instructions = 0; +#endif +#undef USE_ARMV7_INSTRUCTIONS + #ifndef NDEBUG static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { "%r0", @@ -39,14 +84,11 @@ "%r12", "%r13", "%r14", + "%pc", }; #endif static const int tcg_target_reg_alloc_order[] = { - TCG_REG_R0, - TCG_REG_R1, - TCG_REG_R2, - TCG_REG_R3, TCG_REG_R4, TCG_REG_R5, TCG_REG_R6, @@ -55,8 +97,12 @@ TCG_REG_R9, TCG_REG_R10, TCG_REG_R11, - TCG_REG_R12, TCG_REG_R13, + TCG_REG_R0, + TCG_REG_R1, + TCG_REG_R2, + TCG_REG_R3, + TCG_REG_R12, TCG_REG_R14, }; @@ -67,12 +113,25 @@ TCG_REG_R0, TCG_REG_R1 }; +static inline void reloc_abs32(void *code_ptr, tcg_target_long target) +{ + *(uint32_t *) code_ptr = target; +} + +static inline void reloc_pc24(void *code_ptr, tcg_target_long target) +{ + uint32_t offset = ((target - ((tcg_target_long) code_ptr + 8)) >> 2); + + *(uint32_t *) code_ptr = ((*(uint32_t *) code_ptr) & ~0xffffff) + | (offset & 0xffffff); +} + static void patch_reloc(uint8_t *code_ptr, int type, tcg_target_long value, tcg_target_long addend) { switch (type) { case R_ARM_ABS32: - *(uint32_t *) code_ptr = value; + reloc_abs32(code_ptr, value); break; case R_ARM_CALL: @@ -81,8 +140,7 @@ tcg_abort(); case R_ARM_PC24: - *(uint32_t *) code_ptr = ((*(uint32_t *) code_ptr) & 0xff000000) | - (((value - ((tcg_target_long) code_ptr + 8)) >> 2) & 0xffffff); + reloc_pc24(code_ptr, value); break; } } @@ -105,69 +163,54 @@ break; case 'r': -#ifndef CONFIG_SOFTMMU - case 'd': - case 'D': - case 'x': - case 'X': -#endif ct->ct |= TCG_CT_REG; tcg_regset_set32(ct->u.regs, 0, (1 << TCG_TARGET_NB_REGS) - 1); break; -#ifdef CONFIG_SOFTMMU - /* qemu_ld/st inputs (unless 'X', 'd' or 'D') */ - case 'x': + /* qemu_ld address */ + case 'l': ct->ct |= TCG_CT_REG; tcg_regset_set32(ct->u.regs, 0, (1 << TCG_TARGET_NB_REGS) - 1); +#ifdef CONFIG_SOFTMMU + /* r0 and r1 will be overwritten when reading the tlb entry, + so don't use these. */ tcg_regset_reset_reg(ct->u.regs, TCG_REG_R0); tcg_regset_reset_reg(ct->u.regs, TCG_REG_R1); +#endif break; - - /* qemu_ld64 data_reg */ - case 'd': + case 'L': ct->ct |= TCG_CT_REG; tcg_regset_set32(ct->u.regs, 0, (1 << TCG_TARGET_NB_REGS) - 1); - /* r1 is still needed to load data_reg2, so don't use it. */ +#ifdef CONFIG_SOFTMMU + /* r1 is still needed to load data_reg or data_reg2, + so don't use it. */ tcg_regset_reset_reg(ct->u.regs, TCG_REG_R1); +#endif break; - /* qemu_ld/st64 data_reg2 */ - case 'D': + /* qemu_st address & data_reg */ + case 's': ct->ct |= TCG_CT_REG; tcg_regset_set32(ct->u.regs, 0, (1 << TCG_TARGET_NB_REGS) - 1); - /* r0, r1 and optionally r2 will be overwritten by the address - * and the low word of data, so don't use these. */ + /* r0 and r1 will be overwritten when reading the tlb entry + (softmmu only) and doing the byte swapping, so don't + use these. */ tcg_regset_reset_reg(ct->u.regs, TCG_REG_R0); tcg_regset_reset_reg(ct->u.regs, TCG_REG_R1); -# if TARGET_LONG_BITS == 64 - tcg_regset_reset_reg(ct->u.regs, TCG_REG_R2); -# endif break; - -# if TARGET_LONG_BITS == 64 - /* qemu_ld/st addr_reg2 */ - case 'X': + /* qemu_st64 data_reg2 */ + case 'S': ct->ct |= TCG_CT_REG; tcg_regset_set32(ct->u.regs, 0, (1 << TCG_TARGET_NB_REGS) - 1); - /* r0 will be overwritten by the low word of base, so don't use it. */ + /* r0 and r1 will be overwritten when reading the tlb entry + (softmmu only) and doing the byte swapping, so don't + use these. */ tcg_regset_reset_reg(ct->u.regs, TCG_REG_R0); tcg_regset_reset_reg(ct->u.regs, TCG_REG_R1); - break; -# endif +#ifdef CONFIG_SOFTMMU + /* r2 is still needed to load data_reg, so don't use it. */ + tcg_regset_reset_reg(ct->u.regs, TCG_REG_R2); #endif - - case '1': - ct->ct |= TCG_CT_REG; - tcg_regset_set32(ct->u.regs, 0, (1 << TCG_TARGET_NB_REGS) - 1); - tcg_regset_reset_reg(ct->u.regs, TCG_REG_R0); - break; - - case '2': - ct->ct |= TCG_CT_REG; - tcg_regset_set32(ct->u.regs, 0, (1 << TCG_TARGET_NB_REGS) - 1); - tcg_regset_reset_reg(ct->u.regs, TCG_REG_R0); - tcg_regset_reset_reg(ct->u.regs, TCG_REG_R1); break; default: @@ -309,6 +352,9 @@ static inline void tcg_out_b_noaddr(TCGContext *s, int cond) { + /* We pay attention here to not modify the branch target by skipping + the corresponding bytes. This ensure that caches and memory are + kept coherent during retranslation. */ #ifdef HOST_WORDS_BIGENDIAN tcg_out8(s, (cond << 4) | 0x0a); s->code_ptr += 3; @@ -324,6 +370,11 @@ (((offset - 8) >> 2) & 0x00ffffff)); } +static inline void tcg_out_blx(TCGContext *s, int cond, int rn) +{ + tcg_out32(s, (cond << 28) | 0x012fff30 | rn); +} + static inline void tcg_out_dat_reg(TCGContext *s, int cond, int opc, int rd, int rn, int rm, int shift) { @@ -358,42 +409,38 @@ } static inline void tcg_out_movi32(TCGContext *s, - int cond, int rd, int32_t arg) + int cond, int rd, uint32_t arg) { - int offset = (uint32_t) arg - ((uint32_t) s->code_ptr + 8); - /* TODO: This is very suboptimal, we can easily have a constant * pool somewhere after all the instructions. */ + if ((int)arg < 0 && (int)arg >= -0x100) { + tcg_out_dat_imm(s, cond, ARITH_MVN, rd, 0, (~arg) & 0xff); + } else if (use_armv7_instructions) { + /* use movw/movt */ + /* movw */ + tcg_out32(s, (cond << 28) | 0x03000000 | (rd << 12) + | ((arg << 4) & 0x000f0000) | (arg & 0xfff)); + if (arg & 0xffff0000) { + /* movt */ + tcg_out32(s, (cond << 28) | 0x03400000 | (rd << 12) + | ((arg >> 12) & 0x000f0000) | ((arg >> 16) & 0xfff)); + } + } else { + int opc = ARITH_MOV; + int rn = 0; - if (arg < 0 && arg > -0x100) - return tcg_out_dat_imm(s, cond, ARITH_MVN, rd, 0, (~arg) & 0xff); + do { + int i, rot; - if (offset < 0x100 && offset > -0x100) - return offset >= 0 ? - tcg_out_dat_imm(s, cond, ARITH_ADD, rd, 15, offset) : - tcg_out_dat_imm(s, cond, ARITH_SUB, rd, 15, -offset); - -#ifdef __ARM_ARCH_7A__ - /* use movw/movt */ - /* movw */ - tcg_out32(s, (cond << 28) | 0x03000000 | (rd << 12) - | ((arg << 4) & 0x000f0000) | (arg & 0xfff)); - if (arg & 0xffff0000) - /* movt */ - tcg_out32(s, (cond << 28) | 0x03400000 | (rd << 12) - | ((arg >> 12) & 0x000f0000) | ((arg >> 16) & 0xfff)); -#else - tcg_out_dat_imm(s, cond, ARITH_MOV, rd, 0, arg & 0xff); - if (arg & 0x0000ff00) - tcg_out_dat_imm(s, cond, ARITH_ORR, rd, rd, - ((arg >> 8) & 0xff) | 0xc00); - if (arg & 0x00ff0000) - tcg_out_dat_imm(s, cond, ARITH_ORR, rd, rd, - ((arg >> 16) & 0xff) | 0x800); - if (arg & 0xff000000) - tcg_out_dat_imm(s, cond, ARITH_ORR, rd, rd, - ((arg >> 24) & 0xff) | 0x400); -#endif + i = ctz32(arg) & ~1; + rot = ((32 - i) << 7) & 0xf00; + tcg_out_dat_imm(s, cond, opc, rd, rn, ((arg >> i) & 0xff) | rot); + arg &= ~(0xff << i); + + opc = ARITH_ORR; + rn = rd; + } while (arg); + } } static inline void tcg_out_mul32(TCGContext *s, @@ -409,7 +456,7 @@ tcg_out32(s, (cond << 28) | ( 8 << 16) | (0 << 12) | (rs << 8) | 0x90 | rm); tcg_out_dat_reg(s, cond, ARITH_MOV, - rd, 0, 8, SHIFT_IMM_LSL(0)); + rd, 0, TCG_REG_R8, SHIFT_IMM_LSL(0)); } } @@ -447,6 +494,101 @@ } } +static inline void tcg_out_ext8s(TCGContext *s, int cond, + int rd, int rn) +{ + if (use_armv6_instructions) { + /* sxtb */ + tcg_out32(s, 0x06af0070 | (cond << 28) | (rd << 12) | rn); + } else { + tcg_out_dat_reg(s, cond, ARITH_MOV, + rd, 0, rn, SHIFT_IMM_LSL(24)); + tcg_out_dat_reg(s, cond, ARITH_MOV, + rd, 0, rd, SHIFT_IMM_ASR(24)); + } +} + +static inline void tcg_out_ext8u(TCGContext *s, int cond, + int rd, int rn) +{ + tcg_out_dat_imm(s, cond, ARITH_AND, rd, rn, 0xff); +} + +static inline void tcg_out_ext16s(TCGContext *s, int cond, + int rd, int rn) +{ + if (use_armv6_instructions) { + /* sxth */ + tcg_out32(s, 0x06bf0070 | (cond << 28) | (rd << 12) | rn); + } else { + tcg_out_dat_reg(s, cond, ARITH_MOV, + rd, 0, rn, SHIFT_IMM_LSL(16)); + tcg_out_dat_reg(s, cond, ARITH_MOV, + rd, 0, rd, SHIFT_IMM_ASR(16)); + } +} + +static inline void tcg_out_ext16u(TCGContext *s, int cond, + int rd, int rn) +{ + if (use_armv6_instructions) { + /* uxth */ + tcg_out32(s, 0x06ff0070 | (cond << 28) | (rd << 12) | rn); + } else { + tcg_out_dat_reg(s, cond, ARITH_MOV, + rd, 0, rn, SHIFT_IMM_LSL(16)); + tcg_out_dat_reg(s, cond, ARITH_MOV, + rd, 0, rd, SHIFT_IMM_LSR(16)); + } +} + +static inline void tcg_out_bswap16s(TCGContext *s, int cond, int rd, int rn) +{ + if (use_armv6_instructions) { + /* revsh */ + tcg_out32(s, 0x06ff0fb0 | (cond << 28) | (rd << 12) | rn); + } else { + tcg_out_dat_reg(s, cond, ARITH_MOV, + TCG_REG_R8, 0, rn, SHIFT_IMM_LSL(24)); + tcg_out_dat_reg(s, cond, ARITH_MOV, + TCG_REG_R8, 0, TCG_REG_R8, SHIFT_IMM_ASR(16)); + tcg_out_dat_reg(s, cond, ARITH_ORR, + rd, TCG_REG_R8, rn, SHIFT_IMM_LSR(8)); + } +} + +static inline void tcg_out_bswap16(TCGContext *s, int cond, int rd, int rn) +{ + if (use_armv6_instructions) { + /* rev16 */ + tcg_out32(s, 0x06bf0fb0 | (cond << 28) | (rd << 12) | rn); + } else { + tcg_out_dat_reg(s, cond, ARITH_MOV, + TCG_REG_R8, 0, rn, SHIFT_IMM_LSL(24)); + tcg_out_dat_reg(s, cond, ARITH_MOV, + TCG_REG_R8, 0, TCG_REG_R8, SHIFT_IMM_LSR(16)); + tcg_out_dat_reg(s, cond, ARITH_ORR, + rd, TCG_REG_R8, rn, SHIFT_IMM_LSR(8)); + } +} + +static inline void tcg_out_bswap32(TCGContext *s, int cond, int rd, int rn) +{ + if (use_armv6_instructions) { + /* rev */ + tcg_out32(s, 0x06bf0f30 | (cond << 28) | (rd << 12) | rn); + } else { + tcg_out_dat_reg(s, cond, ARITH_EOR, + TCG_REG_R8, rn, rn, SHIFT_IMM_ROR(16)); + tcg_out_dat_imm(s, cond, ARITH_BIC, + TCG_REG_R8, TCG_REG_R8, 0xff | 0x800); + tcg_out_dat_reg(s, cond, ARITH_MOV, + rd, 0, rn, SHIFT_IMM_ROR(8)); + tcg_out_dat_reg(s, cond, ARITH_EOR, + rd, rd, TCG_REG_R8, SHIFT_IMM_LSR(8)); + } +} + static inline void tcg_out_ld32_12(TCGContext *s, int cond, int rd, int rn, tcg_target_long im) { @@ -511,7 +653,7 @@ (((-im) & 0xf0) << 4) | ((-im) & 0xf)); } -static inline void tcg_out_st16u_8(TCGContext *s, int cond, +static inline void tcg_out_st16_8(TCGContext *s, int cond, int rd, int rn, tcg_target_long im) { if (im >= 0) @@ -531,7 +673,7 @@ (rn << 16) | (rd << 12) | rm); } -static inline void tcg_out_st16u_r(TCGContext *s, int cond, +static inline void tcg_out_st16_r(TCGContext *s, int cond, int rd, int rn, int rm) { tcg_out32(s, (cond << 28) | 0x018000b0 | @@ -551,19 +693,6 @@ (((-im) & 0xf0) << 4) | ((-im) & 0xf)); } -static inline void tcg_out_st16s_8(TCGContext *s, int cond, - int rd, int rn, tcg_target_long im) -{ - if (im >= 0) - tcg_out32(s, (cond << 28) | 0x01c000f0 | - (rn << 16) | (rd << 12) | - ((im & 0xf0) << 4) | (im & 0xf)); - else - tcg_out32(s, (cond << 28) | 0x014000f0 | - (rn << 16) | (rd << 12) | - (((-im) & 0xf0) << 4) | ((-im) & 0xf)); -} - static inline void tcg_out_ld16s_r(TCGContext *s, int cond, int rd, int rn, int rm) { @@ -571,13 +700,6 @@ (rn << 16) | (rd << 12) | rm); } -static inline void tcg_out_st16s_r(TCGContext *s, int cond, - int rd, int rn, int rm) -{ - tcg_out32(s, (cond << 28) | 0x018000f0 | - (rn << 16) | (rd << 12) | rm); -} - static inline void tcg_out_ld8_12(TCGContext *s, int cond, int rd, int rn, tcg_target_long im) { @@ -627,19 +749,6 @@ (((-im) & 0xf0) << 4) | ((-im) & 0xf)); } -static inline void tcg_out_st8s_8(TCGContext *s, int cond, - int rd, int rn, tcg_target_long im) -{ - if (im >= 0) - tcg_out32(s, (cond << 28) | 0x01c000d0 | - (rn << 16) | (rd << 12) | - ((im & 0xf0) << 4) | (im & 0xf)); - else - tcg_out32(s, (cond << 28) | 0x014000d0 | - (rn << 16) | (rd << 12) | - (((-im) & 0xf0) << 4) | ((-im) & 0xf)); -} - static inline void tcg_out_ld8s_r(TCGContext *s, int cond, int rd, int rn, int rm) { @@ -647,13 +756,6 @@ (rn << 16) | (rd << 12) | rm); } -static inline void tcg_out_st8s_r(TCGContext *s, int cond, - int rd, int rn, int rm) -{ - tcg_out32(s, (cond << 28) | 0x018000d0 | - (rn << 16) | (rd << 12) | rm); -} - static inline void tcg_out_ld32u(TCGContext *s, int cond, int rd, int rn, int32_t offset) { @@ -694,14 +796,14 @@ tcg_out_ld16s_8(s, cond, rd, rn, offset); } -static inline void tcg_out_st16u(TCGContext *s, int cond, +static inline void tcg_out_st16(TCGContext *s, int cond, int rd, int rn, int32_t offset) { if (offset > 0xff || offset < -0xff) { tcg_out_movi32(s, cond, TCG_REG_R8, offset); - tcg_out_st16u_r(s, cond, rd, rn, TCG_REG_R8); + tcg_out_st16_r(s, cond, rd, rn, TCG_REG_R8); } else - tcg_out_st16u_8(s, cond, rd, rn, offset); + tcg_out_st16_8(s, cond, rd, rn, offset); } static inline void tcg_out_ld8u(TCGContext *s, int cond, @@ -724,7 +826,7 @@ tcg_out_ld8s_8(s, cond, rd, rn, offset); } -static inline void tcg_out_st8u(TCGContext *s, int cond, +static inline void tcg_out_st8(TCGContext *s, int cond, int rd, int rn, int32_t offset) { if (offset > 0xfff || offset < -0xfff) { @@ -746,12 +848,13 @@ tcg_abort(); #else if (cond == COND_AL) { - tcg_out_ld32_12(s, COND_AL, 15, 15, -4); + tcg_out_ld32_12(s, COND_AL, TCG_REG_PC, TCG_REG_PC, -4); tcg_out32(s, addr); /* XXX: This is l->u.value, can we use it? */ } else { tcg_out_movi32(s, cond, TCG_REG_R8, val - 8); tcg_out_dat_reg(s, cond, ARITH_ADD, - 15, 15, TCG_REG_R8, SHIFT_IMM_LSL(0)); + TCG_REG_PC, TCG_REG_PC, + TCG_REG_R8, SHIFT_IMM_LSL(0)); } #endif } @@ -761,10 +864,6 @@ { int32_t val; -#ifdef SAVE_LR - tcg_out_dat_reg(s, cond, ARITH_MOV, TCG_REG_R8, 0, 14, SHIFT_IMM_LSL(0)); -#endif - val = addr - (tcg_target_long) s->code_ptr; if (val < 0x01fffffd && val > -0x01fffffd) tcg_out_bl(s, cond, val); @@ -773,33 +872,28 @@ tcg_abort(); #else if (cond == COND_AL) { - tcg_out_dat_imm(s, cond, ARITH_ADD, 14, 15, 4); - tcg_out_ld32_12(s, COND_AL, 15, 15, -4); + tcg_out_dat_imm(s, cond, ARITH_ADD, TCG_REG_R14, TCG_REG_PC, 4); + tcg_out_ld32_12(s, COND_AL, TCG_REG_PC, TCG_REG_PC, -4); tcg_out32(s, addr); /* XXX: This is l->u.value, can we use it? */ } else { tcg_out_movi32(s, cond, TCG_REG_R9, addr); - tcg_out_dat_imm(s, cond, ARITH_MOV, 14, 0, 15); + tcg_out_dat_reg(s, cond, ARITH_MOV, TCG_REG_R14, 0, + TCG_REG_PC, SHIFT_IMM_LSL(0)); tcg_out_bx(s, cond, TCG_REG_R9); } #endif } - -#ifdef SAVE_LR - tcg_out_dat_reg(s, cond, ARITH_MOV, 14, 0, TCG_REG_R8, SHIFT_IMM_LSL(0)); -#endif } static inline void tcg_out_callr(TCGContext *s, int cond, int arg) { -#ifdef SAVE_LR - tcg_out_dat_reg(s, cond, ARITH_MOV, TCG_REG_R8, 0, 14, SHIFT_IMM_LSL(0)); -#endif - /* TODO: on ARMv5 and ARMv6 replace with tcg_out_blx(s, cond, arg); */ - tcg_out_dat_reg(s, cond, ARITH_MOV, 14, 0, 15, SHIFT_IMM_LSL(0)); - tcg_out_bx(s, cond, arg); -#ifdef SAVE_LR - tcg_out_dat_reg(s, cond, ARITH_MOV, 14, 0, TCG_REG_R8, SHIFT_IMM_LSL(0)); -#endif + if (use_armv5_instructions) { + tcg_out_blx(s, cond, arg); + } else { + tcg_out_dat_reg(s, cond, ARITH_MOV, TCG_REG_R14, 0, + TCG_REG_PC, SHIFT_IMM_LSL(0)); + tcg_out_bx(s, cond, arg); + } } static inline void tcg_out_goto_label(TCGContext *s, int cond, int label_index) @@ -809,7 +903,7 @@ if (l->has_value) tcg_out_goto(s, cond, l->u.value); else if (cond == COND_AL) { - tcg_out_ld32_12(s, COND_AL, 15, 15, -4); + tcg_out_ld32_12(s, COND_AL, TCG_REG_PC, TCG_REG_PC, -4); tcg_out_reloc(s, s->code_ptr, R_ARM_ABS32, label_index, 31337); s->code_ptr += 4; } else { @@ -819,57 +913,6 @@ } } -static void tcg_out_div_helper(TCGContext *s, int cond, const TCGArg *args, - void *helper_div, void *helper_rem, int shift) -{ - int div_reg = args[0]; - int rem_reg = args[1]; - - /* stmdb sp!, { r0 - r3, ip, lr } */ - /* (Note that we need an even number of registers as per EABI) */ - tcg_out32(s, (cond << 28) | 0x092d500f); - - tcg_out_dat_reg(s, cond, ARITH_MOV, 0, 0, args[2], SHIFT_IMM_LSL(0)); - tcg_out_dat_reg(s, cond, ARITH_MOV, 1, 0, args[3], SHIFT_IMM_LSL(0)); - tcg_out_dat_reg(s, cond, ARITH_MOV, 2, 0, args[4], SHIFT_IMM_LSL(0)); - tcg_out_dat_reg(s, cond, ARITH_MOV, 3, 0, 2, shift); - - tcg_out_call(s, cond, (uint32_t) helper_div); - tcg_out_dat_reg(s, cond, ARITH_MOV, 8, 0, 0, SHIFT_IMM_LSL(0)); - - /* ldmia sp, { r0 - r3, fp, lr } */ - tcg_out32(s, (cond << 28) | 0x089d500f); - - tcg_out_dat_reg(s, cond, ARITH_MOV, 0, 0, args[2], SHIFT_IMM_LSL(0)); - tcg_out_dat_reg(s, cond, ARITH_MOV, 1, 0, args[3], SHIFT_IMM_LSL(0)); - tcg_out_dat_reg(s, cond, ARITH_MOV, 2, 0, args[4], SHIFT_IMM_LSL(0)); - tcg_out_dat_reg(s, cond, ARITH_MOV, 3, 0, 2, shift); - - tcg_out_call(s, cond, (uint32_t) helper_rem); - - tcg_out_dat_reg(s, cond, ARITH_MOV, rem_reg, 0, 0, SHIFT_IMM_LSL(0)); - tcg_out_dat_reg(s, cond, ARITH_MOV, div_reg, 0, 8, SHIFT_IMM_LSL(0)); - - /* ldr r0, [sp], #4 */ - if (rem_reg != 0 && div_reg != 0) - tcg_out32(s, (cond << 28) | 0x04bd0004); - /* ldr r1, [sp], #4 */ - if (rem_reg != 1 && div_reg != 1) - tcg_out32(s, (cond << 28) | 0x04bd1004); - /* ldr r2, [sp], #4 */ - if (rem_reg != 2 && div_reg != 2) - tcg_out32(s, (cond << 28) | 0x04bd2004); - /* ldr r3, [sp], #4 */ - if (rem_reg != 3 && div_reg != 3) - tcg_out32(s, (cond << 28) | 0x04bd3004); - /* ldr ip, [sp], #4 */ - if (rem_reg != 12 && div_reg != 12) - tcg_out32(s, (cond << 28) | 0x04bdc004); - /* ldr lr, [sp], #4 */ - if (rem_reg != 14 && div_reg != 14) - tcg_out32(s, (cond << 28) | 0x04bde004); -} - #ifdef CONFIG_SOFTMMU #include "../../softmmu_defs.h" @@ -891,10 +934,9 @@ #define TLB_SHIFT (CPU_TLB_ENTRY_BITS + CPU_TLB_BITS) -static inline void tcg_out_qemu_ld(TCGContext *s, int cond, - const TCGArg *args, int opc) +static inline void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, int opc) { - int addr_reg, data_reg, data_reg2; + int addr_reg, data_reg, data_reg2, bswap; #ifdef CONFIG_SOFTMMU int mem_index, s_bits; # if TARGET_LONG_BITS == 64 @@ -903,6 +945,11 @@ uint32_t *label_ptr; #endif +#ifdef TARGET_WORDS_BIGENDIAN + bswap = 1; +#else + bswap = 0; +#endif data_reg = *args++; if (opc == 3) data_reg2 = *args++; @@ -924,12 +971,12 @@ # if CPU_TLB_BITS > 8 # error # endif - tcg_out_dat_reg(s, COND_AL, ARITH_MOV, - 8, 0, addr_reg, SHIFT_IMM_LSR(TARGET_PAGE_BITS)); + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, TCG_REG_R8, + 0, addr_reg, SHIFT_IMM_LSR(TARGET_PAGE_BITS)); tcg_out_dat_imm(s, COND_AL, ARITH_AND, - 0, 8, CPU_TLB_SIZE - 1); - tcg_out_dat_reg(s, COND_AL, ARITH_ADD, - 0, TCG_AREG0, 0, SHIFT_IMM_LSL(CPU_TLB_ENTRY_BITS)); + TCG_REG_R0, TCG_REG_R8, CPU_TLB_SIZE - 1); + tcg_out_dat_reg(s, COND_AL, ARITH_ADD, TCG_REG_R0, TCG_AREG0, + TCG_REG_R0, SHIFT_IMM_LSL(CPU_TLB_ENTRY_BITS)); /* In the * ldr r1 [r0, #(offsetof(CPUState, tlb_table[mem_index][0].addr_read))] * below, the offset is likely to exceed 12 bits if mem_index != 0 and @@ -938,13 +985,13 @@ * before. */ if (mem_index) - tcg_out_dat_imm(s, COND_AL, ARITH_ADD, 0, 0, + tcg_out_dat_imm(s, COND_AL, ARITH_ADD, TCG_REG_R0, TCG_REG_R0, (mem_index << (TLB_SHIFT & 1)) | ((16 - (TLB_SHIFT >> 1)) << 8)); - tcg_out_ld32_12(s, COND_AL, 1, 0, + tcg_out_ld32_12(s, COND_AL, TCG_REG_R1, TCG_REG_R0, offsetof(CPUState, tlb_table[0][0].addr_read)); - tcg_out_dat_reg(s, COND_AL, ARITH_CMP, - 0, 1, 8, SHIFT_IMM_LSL(TARGET_PAGE_BITS)); + tcg_out_dat_reg(s, COND_AL, ARITH_CMP, 0, TCG_REG_R1, + TCG_REG_R8, SHIFT_IMM_LSL(TARGET_PAGE_BITS)); /* Check alignment. */ if (s_bits) tcg_out_dat_imm(s, COND_EQ, ARITH_TST, @@ -952,95 +999,102 @@ # if TARGET_LONG_BITS == 64 /* XXX: possibly we could use a block data load or writeback in * the first access. */ - tcg_out_ld32_12(s, COND_EQ, 1, 0, + tcg_out_ld32_12(s, COND_EQ, TCG_REG_R1, TCG_REG_R0, offsetof(CPUState, tlb_table[0][0].addr_read) + 4); - tcg_out_dat_reg(s, COND_EQ, ARITH_CMP, - 0, 1, addr_reg2, SHIFT_IMM_LSL(0)); + tcg_out_dat_reg(s, COND_EQ, ARITH_CMP, 0, + TCG_REG_R1, addr_reg2, SHIFT_IMM_LSL(0)); # endif - tcg_out_ld32_12(s, COND_EQ, 1, 0, + tcg_out_ld32_12(s, COND_EQ, TCG_REG_R1, TCG_REG_R0, offsetof(CPUState, tlb_table[0][0].addend)); switch (opc) { case 0: - tcg_out_ld8_r(s, COND_EQ, data_reg, addr_reg, 1); + tcg_out_ld8_r(s, COND_EQ, data_reg, addr_reg, TCG_REG_R1); break; case 0 | 4: - tcg_out_ld8s_r(s, COND_EQ, data_reg, addr_reg, 1); + tcg_out_ld8s_r(s, COND_EQ, data_reg, addr_reg, TCG_REG_R1); break; case 1: - tcg_out_ld16u_r(s, COND_EQ, data_reg, addr_reg, 1); + tcg_out_ld16u_r(s, COND_EQ, data_reg, addr_reg, TCG_REG_R1); + if (bswap) { + tcg_out_bswap16(s, COND_EQ, data_reg, data_reg); + } break; case 1 | 4: - tcg_out_ld16s_r(s, COND_EQ, data_reg, addr_reg, 1); + if (bswap) { + tcg_out_ld16u_r(s, COND_EQ, data_reg, addr_reg, TCG_REG_R1); + tcg_out_bswap16s(s, COND_EQ, data_reg, data_reg); + } else { + tcg_out_ld16s_r(s, COND_EQ, data_reg, addr_reg, TCG_REG_R1); + } break; case 2: default: - tcg_out_ld32_r(s, COND_EQ, data_reg, addr_reg, 1); + tcg_out_ld32_r(s, COND_EQ, data_reg, addr_reg, TCG_REG_R1); + if (bswap) { + tcg_out_bswap32(s, COND_EQ, data_reg, data_reg); + } break; case 3: - tcg_out_ld32_rwb(s, COND_EQ, data_reg, 1, addr_reg); - tcg_out_ld32_12(s, COND_EQ, data_reg2, 1, 4); + if (bswap) { + tcg_out_ld32_rwb(s, COND_EQ, data_reg2, TCG_REG_R1, addr_reg); + tcg_out_ld32_12(s, COND_EQ, data_reg, TCG_REG_R1, 4); + tcg_out_bswap32(s, COND_EQ, data_reg2, data_reg2); + tcg_out_bswap32(s, COND_EQ, data_reg, data_reg); + } else { + tcg_out_ld32_rwb(s, COND_EQ, data_reg, TCG_REG_R1, addr_reg); + tcg_out_ld32_12(s, COND_EQ, data_reg2, TCG_REG_R1, 4); + } break; } label_ptr = (void *) s->code_ptr; - tcg_out_b(s, COND_EQ, 8); - -# ifdef SAVE_LR - tcg_out_dat_reg(s, cond, ARITH_MOV, 8, 0, 14, SHIFT_IMM_LSL(0)); -# endif + tcg_out_b_noaddr(s, COND_EQ); /* TODO: move this code to where the constants pool will be */ - if (addr_reg) - tcg_out_dat_reg(s, cond, ARITH_MOV, - 0, 0, addr_reg, SHIFT_IMM_LSL(0)); + if (addr_reg != TCG_REG_R0) { + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, + TCG_REG_R0, 0, addr_reg, SHIFT_IMM_LSL(0)); + } # if TARGET_LONG_BITS == 32 - tcg_out_dat_imm(s, cond, ARITH_MOV, 1, 0, mem_index); + tcg_out_dat_imm(s, COND_AL, ARITH_MOV, TCG_REG_R1, 0, mem_index); # else - if (addr_reg2 != 1) - tcg_out_dat_reg(s, cond, ARITH_MOV, - 1, 0, addr_reg2, SHIFT_IMM_LSL(0)); - tcg_out_dat_imm(s, cond, ARITH_MOV, 2, 0, mem_index); + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, + TCG_REG_R1, 0, addr_reg2, SHIFT_IMM_LSL(0)); + tcg_out_dat_imm(s, COND_AL, ARITH_MOV, TCG_REG_R2, 0, mem_index); # endif - tcg_out_bl(s, cond, (tcg_target_long) qemu_ld_helpers[s_bits] - + tcg_out_bl(s, COND_AL, (tcg_target_long) qemu_ld_helpers[s_bits] - (tcg_target_long) s->code_ptr); switch (opc) { case 0 | 4: - tcg_out_dat_reg(s, cond, ARITH_MOV, - 0, 0, 0, SHIFT_IMM_LSL(24)); - tcg_out_dat_reg(s, cond, ARITH_MOV, - data_reg, 0, 0, SHIFT_IMM_ASR(24)); + tcg_out_ext8s(s, COND_AL, data_reg, TCG_REG_R0); break; case 1 | 4: - tcg_out_dat_reg(s, cond, ARITH_MOV, - 0, 0, 0, SHIFT_IMM_LSL(16)); - tcg_out_dat_reg(s, cond, ARITH_MOV, - data_reg, 0, 0, SHIFT_IMM_ASR(16)); + tcg_out_ext16s(s, COND_AL, data_reg, TCG_REG_R0); break; case 0: case 1: case 2: default: - if (data_reg) - tcg_out_dat_reg(s, cond, ARITH_MOV, - data_reg, 0, 0, SHIFT_IMM_LSL(0)); + if (data_reg != TCG_REG_R0) { + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, + data_reg, 0, TCG_REG_R0, SHIFT_IMM_LSL(0)); + } break; case 3: - if (data_reg != 0) - tcg_out_dat_reg(s, cond, ARITH_MOV, - data_reg, 0, 0, SHIFT_IMM_LSL(0)); - if (data_reg2 != 1) - tcg_out_dat_reg(s, cond, ARITH_MOV, - data_reg2, 0, 1, SHIFT_IMM_LSL(0)); + if (data_reg != TCG_REG_R0) { + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, + data_reg, 0, TCG_REG_R0, SHIFT_IMM_LSL(0)); + } + if (data_reg2 != TCG_REG_R1) { + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, + data_reg2, 0, TCG_REG_R1, SHIFT_IMM_LSL(0)); + } break; } -# ifdef SAVE_LR - tcg_out_dat_reg(s, cond, ARITH_MOV, 14, 0, 8, SHIFT_IMM_LSL(0)); -# endif - - *label_ptr += ((void *) s->code_ptr - (void *) label_ptr - 8) >> 2; + reloc_pc24(label_ptr, (tcg_target_long)s->code_ptr); #else /* !CONFIG_SOFTMMU */ if (GUEST_BASE) { uint32_t offset = GUEST_BASE; @@ -1051,9 +1105,9 @@ i = ctz32(offset) & ~1; rot = ((32 - i) << 7) & 0xf00; - tcg_out_dat_imm(s, COND_AL, ARITH_ADD, 8, addr_reg, + tcg_out_dat_imm(s, COND_AL, ARITH_ADD, TCG_REG_R8, addr_reg, ((offset >> i) & 0xff) | rot); - addr_reg = 8; + addr_reg = TCG_REG_R8; offset &= ~(0xff << i); } } @@ -1066,33 +1120,47 @@ break; case 1: tcg_out_ld16u_8(s, COND_AL, data_reg, addr_reg, 0); + if (bswap) { + tcg_out_bswap16(s, COND_AL, data_reg, data_reg); + } break; case 1 | 4: - tcg_out_ld16s_8(s, COND_AL, data_reg, addr_reg, 0); + if (bswap) { + tcg_out_ld16u_8(s, COND_AL, data_reg, addr_reg, 0); + tcg_out_bswap16s(s, COND_AL, data_reg, data_reg); + } else { + tcg_out_ld16s_8(s, COND_AL, data_reg, addr_reg, 0); + } break; case 2: default: tcg_out_ld32_12(s, COND_AL, data_reg, addr_reg, 0); + if (bswap) { + tcg_out_bswap32(s, COND_AL, data_reg, data_reg); + } break; case 3: /* TODO: use block load - * check that data_reg2 > data_reg or the other way */ if (data_reg == addr_reg) { - tcg_out_ld32_12(s, COND_AL, data_reg2, addr_reg, 4); - tcg_out_ld32_12(s, COND_AL, data_reg, addr_reg, 0); + tcg_out_ld32_12(s, COND_AL, data_reg2, addr_reg, bswap ? 0 : 4); + tcg_out_ld32_12(s, COND_AL, data_reg, addr_reg, bswap ? 4 : 0); } else { - tcg_out_ld32_12(s, COND_AL, data_reg, addr_reg, 0); - tcg_out_ld32_12(s, COND_AL, data_reg2, addr_reg, 4); + tcg_out_ld32_12(s, COND_AL, data_reg, addr_reg, bswap ? 4 : 0); + tcg_out_ld32_12(s, COND_AL, data_reg2, addr_reg, bswap ? 0 : 4); + } + if (bswap) { + tcg_out_bswap32(s, COND_AL, data_reg, data_reg); + tcg_out_bswap32(s, COND_AL, data_reg2, data_reg2); } break; } #endif } -static inline void tcg_out_qemu_st(TCGContext *s, int cond, - const TCGArg *args, int opc) +static inline void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, int opc) { - int addr_reg, data_reg, data_reg2; + int addr_reg, data_reg, data_reg2, bswap; #ifdef CONFIG_SOFTMMU int mem_index, s_bits; # if TARGET_LONG_BITS == 64 @@ -1101,6 +1169,11 @@ uint32_t *label_ptr; #endif +#ifdef TARGET_WORDS_BIGENDIAN + bswap = 1; +#else + bswap = 0; +#endif data_reg = *args++; if (opc == 3) data_reg2 = *args++; @@ -1120,11 +1193,11 @@ * add r0, env, r0 lsl #CPU_TLB_ENTRY_BITS */ tcg_out_dat_reg(s, COND_AL, ARITH_MOV, - 8, 0, addr_reg, SHIFT_IMM_LSR(TARGET_PAGE_BITS)); + TCG_REG_R8, 0, addr_reg, SHIFT_IMM_LSR(TARGET_PAGE_BITS)); tcg_out_dat_imm(s, COND_AL, ARITH_AND, - 0, 8, CPU_TLB_SIZE - 1); - tcg_out_dat_reg(s, COND_AL, ARITH_ADD, - 0, TCG_AREG0, 0, SHIFT_IMM_LSL(CPU_TLB_ENTRY_BITS)); + TCG_REG_R0, TCG_REG_R8, CPU_TLB_SIZE - 1); + tcg_out_dat_reg(s, COND_AL, ARITH_ADD, TCG_REG_R0, + TCG_AREG0, TCG_REG_R0, SHIFT_IMM_LSL(CPU_TLB_ENTRY_BITS)); /* In the * ldr r1 [r0, #(offsetof(CPUState, tlb_table[mem_index][0].addr_write))] * below, the offset is likely to exceed 12 bits if mem_index != 0 and @@ -1133,13 +1206,13 @@ * before. */ if (mem_index) - tcg_out_dat_imm(s, COND_AL, ARITH_ADD, 0, 0, + tcg_out_dat_imm(s, COND_AL, ARITH_ADD, TCG_REG_R0, TCG_REG_R0, (mem_index << (TLB_SHIFT & 1)) | ((16 - (TLB_SHIFT >> 1)) << 8)); - tcg_out_ld32_12(s, COND_AL, 1, 0, + tcg_out_ld32_12(s, COND_AL, TCG_REG_R1, TCG_REG_R0, offsetof(CPUState, tlb_table[0][0].addr_write)); - tcg_out_dat_reg(s, COND_AL, ARITH_CMP, - 0, 1, 8, SHIFT_IMM_LSL(TARGET_PAGE_BITS)); + tcg_out_dat_reg(s, COND_AL, ARITH_CMP, 0, TCG_REG_R1, + TCG_REG_R8, SHIFT_IMM_LSL(TARGET_PAGE_BITS)); /* Check alignment. */ if (s_bits) tcg_out_dat_imm(s, COND_EQ, ARITH_TST, @@ -1147,125 +1220,122 @@ # if TARGET_LONG_BITS == 64 /* XXX: possibly we could use a block data load or writeback in * the first access. */ - tcg_out_ld32_12(s, COND_EQ, 1, 0, - offsetof(CPUState, tlb_table[0][0].addr_write) - + 4); - tcg_out_dat_reg(s, COND_EQ, ARITH_CMP, - 0, 1, addr_reg2, SHIFT_IMM_LSL(0)); + tcg_out_ld32_12(s, COND_EQ, TCG_REG_R1, TCG_REG_R0, + offsetof(CPUState, tlb_table[0][0].addr_write) + 4); + tcg_out_dat_reg(s, COND_EQ, ARITH_CMP, 0, + TCG_REG_R1, addr_reg2, SHIFT_IMM_LSL(0)); # endif - tcg_out_ld32_12(s, COND_EQ, 1, 0, + tcg_out_ld32_12(s, COND_EQ, TCG_REG_R1, TCG_REG_R0, offsetof(CPUState, tlb_table[0][0].addend)); switch (opc) { case 0: - tcg_out_st8_r(s, COND_EQ, data_reg, addr_reg, 1); - break; - case 0 | 4: - tcg_out_st8s_r(s, COND_EQ, data_reg, addr_reg, 1); + tcg_out_st8_r(s, COND_EQ, data_reg, addr_reg, TCG_REG_R1); break; case 1: - tcg_out_st16u_r(s, COND_EQ, data_reg, addr_reg, 1); - break; - case 1 | 4: - tcg_out_st16s_r(s, COND_EQ, data_reg, addr_reg, 1); + if (bswap) { + tcg_out_bswap16(s, COND_EQ, TCG_REG_R0, data_reg); + tcg_out_st16_r(s, COND_EQ, TCG_REG_R0, addr_reg, TCG_REG_R1); + } else { + tcg_out_st16_r(s, COND_EQ, data_reg, addr_reg, TCG_REG_R1); + } break; case 2: default: - tcg_out_st32_r(s, COND_EQ, data_reg, addr_reg, 1); + if (bswap) { + tcg_out_bswap32(s, COND_EQ, TCG_REG_R0, data_reg); + tcg_out_st32_r(s, COND_EQ, TCG_REG_R0, addr_reg, TCG_REG_R1); + } else { + tcg_out_st32_r(s, COND_EQ, data_reg, addr_reg, TCG_REG_R1); + } break; case 3: - tcg_out_st32_rwb(s, COND_EQ, data_reg, 1, addr_reg); - tcg_out_st32_12(s, COND_EQ, data_reg2, 1, 4); + if (bswap) { + tcg_out_bswap32(s, COND_EQ, TCG_REG_R0, data_reg2); + tcg_out_st32_rwb(s, COND_EQ, TCG_REG_R0, TCG_REG_R1, addr_reg); + tcg_out_bswap32(s, COND_EQ, TCG_REG_R0, data_reg); + tcg_out_st32_12(s, COND_EQ, TCG_REG_R0, TCG_REG_R1, 4); + } else { + tcg_out_st32_rwb(s, COND_EQ, data_reg, TCG_REG_R1, addr_reg); + tcg_out_st32_12(s, COND_EQ, data_reg2, TCG_REG_R1, 4); + } break; } label_ptr = (void *) s->code_ptr; - tcg_out_b(s, COND_EQ, 8); + tcg_out_b_noaddr(s, COND_EQ); /* TODO: move this code to where the constants pool will be */ - if (addr_reg) - tcg_out_dat_reg(s, cond, ARITH_MOV, - 0, 0, addr_reg, SHIFT_IMM_LSL(0)); + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, + TCG_REG_R0, 0, addr_reg, SHIFT_IMM_LSL(0)); # if TARGET_LONG_BITS == 32 switch (opc) { case 0: - tcg_out_dat_imm(s, cond, ARITH_AND, 1, data_reg, 0xff); - tcg_out_dat_imm(s, cond, ARITH_MOV, 2, 0, mem_index); + tcg_out_ext8u(s, COND_AL, TCG_REG_R1, data_reg); + tcg_out_dat_imm(s, COND_AL, ARITH_MOV, TCG_REG_R2, 0, mem_index); break; case 1: - tcg_out_dat_reg(s, cond, ARITH_MOV, - 1, 0, data_reg, SHIFT_IMM_LSL(16)); - tcg_out_dat_reg(s, cond, ARITH_MOV, - 1, 0, 1, SHIFT_IMM_LSR(16)); - tcg_out_dat_imm(s, cond, ARITH_MOV, 2, 0, mem_index); + tcg_out_ext16u(s, COND_AL, TCG_REG_R1, data_reg); + tcg_out_dat_imm(s, COND_AL, ARITH_MOV, TCG_REG_R2, 0, mem_index); break; case 2: - if (data_reg != 1) - tcg_out_dat_reg(s, cond, ARITH_MOV, - 1, 0, data_reg, SHIFT_IMM_LSL(0)); - tcg_out_dat_imm(s, cond, ARITH_MOV, 2, 0, mem_index); + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, + TCG_REG_R1, 0, data_reg, SHIFT_IMM_LSL(0)); + tcg_out_dat_imm(s, COND_AL, ARITH_MOV, TCG_REG_R2, 0, mem_index); break; case 3: - if (data_reg != 1) - tcg_out_dat_reg(s, cond, ARITH_MOV, - 1, 0, data_reg, SHIFT_IMM_LSL(0)); - if (data_reg2 != 2) - tcg_out_dat_reg(s, cond, ARITH_MOV, - 2, 0, data_reg2, SHIFT_IMM_LSL(0)); - tcg_out_dat_imm(s, cond, ARITH_MOV, 3, 0, mem_index); + tcg_out_dat_imm(s, COND_AL, ARITH_MOV, TCG_REG_R8, 0, mem_index); + tcg_out32(s, (COND_AL << 28) | 0x052d8010); /* str r8, [sp, #-0x10]! */ + if (data_reg != TCG_REG_R2) { + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, + TCG_REG_R2, 0, data_reg, SHIFT_IMM_LSL(0)); + } + if (data_reg2 != TCG_REG_R3) { + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, + TCG_REG_R3, 0, data_reg2, SHIFT_IMM_LSL(0)); + } break; } # else - if (addr_reg2 != 1) - tcg_out_dat_reg(s, cond, ARITH_MOV, - 1, 0, addr_reg2, SHIFT_IMM_LSL(0)); + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, + TCG_REG_R1, 0, addr_reg2, SHIFT_IMM_LSL(0)); switch (opc) { case 0: - tcg_out_dat_imm(s, cond, ARITH_AND, 2, data_reg, 0xff); - tcg_out_dat_imm(s, cond, ARITH_MOV, 3, 0, mem_index); + tcg_out_ext8u(s, COND_AL, TCG_REG_R2, data_reg); + tcg_out_dat_imm(s, COND_AL, ARITH_MOV, TCG_REG_R3, 0, mem_index); break; case 1: - tcg_out_dat_reg(s, cond, ARITH_MOV, - 2, 0, data_reg, SHIFT_IMM_LSL(16)); - tcg_out_dat_reg(s, cond, ARITH_MOV, - 2, 0, 2, SHIFT_IMM_LSR(16)); - tcg_out_dat_imm(s, cond, ARITH_MOV, 3, 0, mem_index); + tcg_out_ext16u(s, COND_AL, TCG_REG_R2, data_reg); + tcg_out_dat_imm(s, COND_AL, ARITH_MOV, TCG_REG_R3, 0, mem_index); break; case 2: - if (data_reg != 2) - tcg_out_dat_reg(s, cond, ARITH_MOV, - 2, 0, data_reg, SHIFT_IMM_LSL(0)); - tcg_out_dat_imm(s, cond, ARITH_MOV, 3, 0, mem_index); + if (data_reg != TCG_REG_R2) { + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, + TCG_REG_R2, 0, data_reg, SHIFT_IMM_LSL(0)); + } + tcg_out_dat_imm(s, COND_AL, ARITH_MOV, TCG_REG_R3, 0, mem_index); break; case 3: - tcg_out_dat_imm(s, cond, ARITH_MOV, 8, 0, mem_index); - tcg_out32(s, (cond << 28) | 0x052d8010); /* str r8, [sp, #-0x10]! */ - if (data_reg != 2) - tcg_out_dat_reg(s, cond, ARITH_MOV, - 2, 0, data_reg, SHIFT_IMM_LSL(0)); - if (data_reg2 != 3) - tcg_out_dat_reg(s, cond, ARITH_MOV, - 3, 0, data_reg2, SHIFT_IMM_LSL(0)); + tcg_out_dat_imm(s, COND_AL, ARITH_MOV, TCG_REG_R8, 0, mem_index); + tcg_out32(s, (COND_AL << 28) | 0x052d8010); /* str r8, [sp, #-0x10]! */ + if (data_reg != TCG_REG_R2) { + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, + TCG_REG_R2, 0, data_reg, SHIFT_IMM_LSL(0)); + } + if (data_reg2 != TCG_REG_R3) { + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, + TCG_REG_R3, 0, data_reg2, SHIFT_IMM_LSL(0)); + } break; } # endif -# ifdef SAVE_LR - tcg_out_dat_reg(s, cond, ARITH_MOV, 8, 0, 14, SHIFT_IMM_LSL(0)); -# endif - - tcg_out_bl(s, cond, (tcg_target_long) qemu_st_helpers[s_bits] - + tcg_out_bl(s, COND_AL, (tcg_target_long) qemu_st_helpers[s_bits] - (tcg_target_long) s->code_ptr); -# if TARGET_LONG_BITS == 64 if (opc == 3) - tcg_out_dat_imm(s, cond, ARITH_ADD, 13, 13, 0x10); -# endif - -# ifdef SAVE_LR - tcg_out_dat_reg(s, cond, ARITH_MOV, 14, 0, 8, SHIFT_IMM_LSL(0)); -# endif + tcg_out_dat_imm(s, COND_AL, ARITH_ADD, TCG_REG_R13, TCG_REG_R13, 0x10); - *label_ptr += ((void *) s->code_ptr - (void *) label_ptr - 8) >> 2; + reloc_pc24(label_ptr, (tcg_target_long)s->code_ptr); #else /* !CONFIG_SOFTMMU */ if (GUEST_BASE) { uint32_t offset = GUEST_BASE; @@ -1276,9 +1346,9 @@ i = ctz32(offset) & ~1; rot = ((32 - i) << 7) & 0xf00; - tcg_out_dat_imm(s, COND_AL, ARITH_ADD, 8, addr_reg, + tcg_out_dat_imm(s, COND_AL, ARITH_ADD, TCG_REG_R1, addr_reg, ((offset >> i) & 0xff) | rot); - addr_reg = 8; + addr_reg = TCG_REG_R1; offset &= ~(0xff << i); } } @@ -1286,24 +1356,35 @@ case 0: tcg_out_st8_12(s, COND_AL, data_reg, addr_reg, 0); break; - case 0 | 4: - tcg_out_st8s_8(s, COND_AL, data_reg, addr_reg, 0); - break; case 1: - tcg_out_st16u_8(s, COND_AL, data_reg, addr_reg, 0); - break; - case 1 | 4: - tcg_out_st16s_8(s, COND_AL, data_reg, addr_reg, 0); + if (bswap) { + tcg_out_bswap16(s, COND_AL, TCG_REG_R0, data_reg); + tcg_out_st16_8(s, COND_AL, TCG_REG_R0, addr_reg, 0); + } else { + tcg_out_st16_8(s, COND_AL, data_reg, addr_reg, 0); + } break; case 2: default: - tcg_out_st32_12(s, COND_AL, data_reg, addr_reg, 0); + if (bswap) { + tcg_out_bswap32(s, COND_AL, TCG_REG_R0, data_reg); + tcg_out_st32_12(s, COND_AL, TCG_REG_R0, addr_reg, 0); + } else { + tcg_out_st32_12(s, COND_AL, data_reg, addr_reg, 0); + } break; case 3: /* TODO: use block store - * check that data_reg2 > data_reg or the other way */ - tcg_out_st32_12(s, COND_AL, data_reg, addr_reg, 0); - tcg_out_st32_12(s, COND_AL, data_reg2, addr_reg, 4); + if (bswap) { + tcg_out_bswap32(s, COND_AL, TCG_REG_R0, data_reg2); + tcg_out_st32_12(s, COND_AL, TCG_REG_R0, addr_reg, 0); + tcg_out_bswap32(s, COND_AL, TCG_REG_R0, data_reg); + tcg_out_st32_12(s, COND_AL, TCG_REG_R0, addr_reg, 4); + } else { + tcg_out_st32_12(s, COND_AL, data_reg, addr_reg, 0); + tcg_out_st32_12(s, COND_AL, data_reg2, addr_reg, 4); + } break; } #endif @@ -1311,44 +1392,34 @@ static uint8_t *tb_ret_addr; -static inline void tcg_out_op(TCGContext *s, int opc, +static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg *args, const int *const_args) { int c; switch (opc) { case INDEX_op_exit_tb: -#ifdef SAVE_LR - if (args[0] >> 8) - tcg_out_ld32_12(s, COND_AL, TCG_REG_R0, 15, 0); - else - tcg_out_dat_imm(s, COND_AL, ARITH_MOV, TCG_REG_R0, 0, args[0]); - tcg_out_dat_reg(s, COND_AL, ARITH_MOV, 15, 0, 14, SHIFT_IMM_LSL(0)); - if (args[0] >> 8) - tcg_out32(s, args[0]); -#else { uint8_t *ld_ptr = s->code_ptr; if (args[0] >> 8) - tcg_out_ld32_12(s, COND_AL, 0, 15, 0); + tcg_out_ld32_12(s, COND_AL, TCG_REG_R0, TCG_REG_PC, 0); else - tcg_out_dat_imm(s, COND_AL, ARITH_MOV, 0, 0, args[0]); + tcg_out_dat_imm(s, COND_AL, ARITH_MOV, TCG_REG_R0, 0, args[0]); tcg_out_goto(s, COND_AL, (tcg_target_ulong) tb_ret_addr); if (args[0] >> 8) { *ld_ptr = (uint8_t) (s->code_ptr - ld_ptr) - 8; tcg_out32(s, args[0]); } } -#endif break; case INDEX_op_goto_tb: if (s->tb_jmp_offset) { /* Direct jump method */ #if defined(USE_DIRECT_JUMP) s->tb_jmp_offset[args[0]] = s->code_ptr - s->code_buf; - tcg_out_b(s, COND_AL, 8); + tcg_out_b_noaddr(s, COND_AL); #else - tcg_out_ld32_12(s, COND_AL, 15, 15, -4); + tcg_out_ld32_12(s, COND_AL, TCG_REG_PC, TCG_REG_PC, -4); s->tb_jmp_offset[args[0]] = s->code_ptr - s->code_buf; tcg_out32(s, 0); #endif @@ -1359,12 +1430,12 @@ if (c > 0xfff || c < -0xfff) { tcg_out_movi32(s, COND_AL, TCG_REG_R0, (tcg_target_long) (s->tb_next + args[0])); - tcg_out_ld32_12(s, COND_AL, 15, TCG_REG_R0, 0); + tcg_out_ld32_12(s, COND_AL, TCG_REG_PC, TCG_REG_R0, 0); } else - tcg_out_ld32_12(s, COND_AL, 15, 15, c); + tcg_out_ld32_12(s, COND_AL, TCG_REG_PC, TCG_REG_PC, c); #else - tcg_out_ld32_12(s, COND_AL, TCG_REG_R0, 15, 0); - tcg_out_ld32_12(s, COND_AL, 15, TCG_REG_R0, 0); + tcg_out_ld32_12(s, COND_AL, TCG_REG_R0, TCG_REG_PC, 0); + tcg_out_ld32_12(s, COND_AL, TCG_REG_PC, TCG_REG_R0, 0); tcg_out32(s, (tcg_target_long) (s->tb_next + args[0])); #endif } @@ -1402,10 +1473,10 @@ tcg_out_ld32u(s, COND_AL, args[0], args[1], args[2]); break; case INDEX_op_st8_i32: - tcg_out_st8u(s, COND_AL, args[0], args[1], args[2]); + tcg_out_st8(s, COND_AL, args[0], args[1], args[2]); break; case INDEX_op_st16_i32: - tcg_out_st16u(s, COND_AL, args[0], args[1], args[2]); + tcg_out_st16(s, COND_AL, args[0], args[1], args[2]); break; case INDEX_op_st_i32: tcg_out_st32(s, COND_AL, args[0], args[1], args[2]); @@ -1427,6 +1498,9 @@ case INDEX_op_and_i32: c = ARITH_AND; goto gen_arith; + case INDEX_op_andc_i32: + c = ARITH_BIC; + goto gen_arith; case INDEX_op_or_i32: c = ARITH_ORR; goto gen_arith; @@ -1466,16 +1540,6 @@ case INDEX_op_mulu2_i32: tcg_out_umull32(s, COND_AL, args[0], args[1], args[2], args[3]); break; - case INDEX_op_div2_i32: - tcg_out_div_helper(s, COND_AL, args, - tcg_helper_div_i64, tcg_helper_rem_i64, - SHIFT_IMM_ASR(31)); - break; - case INDEX_op_divu2_i32: - tcg_out_div_helper(s, COND_AL, args, - tcg_helper_divu_i64, tcg_helper_remu_i64, - SHIFT_IMM_LSR(31)); - break; /* XXX: Perhaps args[2] & 0x1f is wrong */ case INDEX_op_shl_i32: c = const_args[2] ? @@ -1488,14 +1552,38 @@ case INDEX_op_sar_i32: c = const_args[2] ? (args[2] & 0x1f) ? SHIFT_IMM_ASR(args[2] & 0x1f) : SHIFT_IMM_LSL(0) : SHIFT_REG_ASR(args[2]); + goto gen_shift32; + case INDEX_op_rotr_i32: + c = const_args[2] ? (args[2] & 0x1f) ? SHIFT_IMM_ROR(args[2] & 0x1f) : + SHIFT_IMM_LSL(0) : SHIFT_REG_ROR(args[2]); /* Fall through. */ gen_shift32: tcg_out_dat_reg(s, COND_AL, ARITH_MOV, args[0], 0, args[1], c); break; + case INDEX_op_rotl_i32: + if (const_args[2]) { + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, args[0], 0, args[1], + ((0x20 - args[2]) & 0x1f) ? + SHIFT_IMM_ROR((0x20 - args[2]) & 0x1f) : + SHIFT_IMM_LSL(0)); + } else { + tcg_out_dat_imm(s, COND_AL, ARITH_RSB, TCG_REG_R8, args[1], 0x20); + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, args[0], 0, args[1], + SHIFT_REG_ROR(TCG_REG_R8)); + } + break; + case INDEX_op_brcond_i32: - tcg_out_dat_reg(s, COND_AL, ARITH_CMP, 0, - args[0], args[1], SHIFT_IMM_LSL(0)); + if (const_args[1]) { + int rot; + rot = encode_imm(args[1]); + tcg_out_dat_imm(s, COND_AL, ARITH_CMP, 0, + args[0], rotl(args[1], rot) | (rot << 7)); + } else { + tcg_out_dat_reg(s, COND_AL, ARITH_CMP, 0, + args[0], args[1], SHIFT_IMM_LSL(0)); + } tcg_out_goto_label(s, tcg_cond_to_arm_cond[args[2]], args[3]); break; case INDEX_op_brcond2_i32: @@ -1513,60 +1601,80 @@ args[0], args[2], SHIFT_IMM_LSL(0)); tcg_out_goto_label(s, tcg_cond_to_arm_cond[args[4]], args[5]); break; + case INDEX_op_setcond_i32: + if (const_args[2]) { + int rot; + rot = encode_imm(args[2]); + tcg_out_dat_imm(s, COND_AL, ARITH_CMP, 0, + args[1], rotl(args[2], rot) | (rot << 7)); + } else { + tcg_out_dat_reg(s, COND_AL, ARITH_CMP, 0, + args[1], args[2], SHIFT_IMM_LSL(0)); + } + tcg_out_dat_imm(s, tcg_cond_to_arm_cond[args[3]], + ARITH_MOV, args[0], 0, 1); + tcg_out_dat_imm(s, tcg_cond_to_arm_cond[tcg_invert_cond(args[3])], + ARITH_MOV, args[0], 0, 0); + break; + case INDEX_op_setcond2_i32: + /* See brcond2_i32 comment */ + tcg_out_dat_reg(s, COND_AL, ARITH_CMP, 0, + args[2], args[4], SHIFT_IMM_LSL(0)); + tcg_out_dat_reg(s, COND_EQ, ARITH_CMP, 0, + args[1], args[3], SHIFT_IMM_LSL(0)); + tcg_out_dat_imm(s, tcg_cond_to_arm_cond[args[5]], + ARITH_MOV, args[0], 0, 1); + tcg_out_dat_imm(s, tcg_cond_to_arm_cond[tcg_invert_cond(args[5])], + ARITH_MOV, args[0], 0, 0); + break; case INDEX_op_qemu_ld8u: - tcg_out_qemu_ld(s, COND_AL, args, 0); + tcg_out_qemu_ld(s, args, 0); break; case INDEX_op_qemu_ld8s: - tcg_out_qemu_ld(s, COND_AL, args, 0 | 4); + tcg_out_qemu_ld(s, args, 0 | 4); break; case INDEX_op_qemu_ld16u: - tcg_out_qemu_ld(s, COND_AL, args, 1); + tcg_out_qemu_ld(s, args, 1); break; case INDEX_op_qemu_ld16s: - tcg_out_qemu_ld(s, COND_AL, args, 1 | 4); + tcg_out_qemu_ld(s, args, 1 | 4); break; - case INDEX_op_qemu_ld32u: - tcg_out_qemu_ld(s, COND_AL, args, 2); + case INDEX_op_qemu_ld32: + tcg_out_qemu_ld(s, args, 2); break; case INDEX_op_qemu_ld64: - tcg_out_qemu_ld(s, COND_AL, args, 3); + tcg_out_qemu_ld(s, args, 3); break; case INDEX_op_qemu_st8: - tcg_out_qemu_st(s, COND_AL, args, 0); + tcg_out_qemu_st(s, args, 0); break; case INDEX_op_qemu_st16: - tcg_out_qemu_st(s, COND_AL, args, 1); + tcg_out_qemu_st(s, args, 1); break; case INDEX_op_qemu_st32: - tcg_out_qemu_st(s, COND_AL, args, 2); + tcg_out_qemu_st(s, args, 2); break; case INDEX_op_qemu_st64: - tcg_out_qemu_st(s, COND_AL, args, 3); + tcg_out_qemu_st(s, args, 3); + break; + + case INDEX_op_bswap16_i32: + tcg_out_bswap16(s, COND_AL, args[0], args[1]); + break; + case INDEX_op_bswap32_i32: + tcg_out_bswap32(s, COND_AL, args[0], args[1]); break; case INDEX_op_ext8s_i32: -#ifdef __ARM_ARCH_7A__ - /* sxtb */ - tcg_out32(s, 0xe6af0070 | (args[0] << 12) | args[1]); -#else - tcg_out_dat_reg(s, COND_AL, ARITH_MOV, - args[0], 0, args[1], SHIFT_IMM_LSL(24)); - tcg_out_dat_reg(s, COND_AL, ARITH_MOV, - args[0], 0, args[0], SHIFT_IMM_ASR(24)); -#endif + tcg_out_ext8s(s, COND_AL, args[0], args[1]); break; case INDEX_op_ext16s_i32: -#ifdef __ARM_ARCH_7A__ - /* sxth */ - tcg_out32(s, 0xe6bf0070 | (args[0] << 12) | args[1]); -#else - tcg_out_dat_reg(s, COND_AL, ARITH_MOV, - args[0], 0, args[1], SHIFT_IMM_LSL(16)); - tcg_out_dat_reg(s, COND_AL, ARITH_MOV, - args[0], 0, args[0], SHIFT_IMM_ASR(16)); -#endif + tcg_out_ext16s(s, COND_AL, args[0], args[1]); + break; + case INDEX_op_ext16u_i32: + tcg_out_ext16u(s, COND_AL, args[0], args[1]); break; default: @@ -1598,9 +1706,8 @@ { INDEX_op_sub_i32, { "r", "r", "rI" } }, { INDEX_op_mul_i32, { "r", "r", "r" } }, { INDEX_op_mulu2_i32, { "r", "r", "r", "r" } }, - { INDEX_op_div2_i32, { "r", "r", "r", "1", "2" } }, - { INDEX_op_divu2_i32, { "r", "r", "r", "1", "2" } }, { INDEX_op_and_i32, { "r", "r", "rI" } }, + { INDEX_op_andc_i32, { "r", "r", "rI" } }, { INDEX_op_or_i32, { "r", "r", "rI" } }, { INDEX_op_xor_i32, { "r", "r", "rI" } }, { INDEX_op_neg_i32, { "r", "r" } }, @@ -1609,50 +1716,75 @@ { INDEX_op_shl_i32, { "r", "r", "ri" } }, { INDEX_op_shr_i32, { "r", "r", "ri" } }, { INDEX_op_sar_i32, { "r", "r", "ri" } }, + { INDEX_op_rotl_i32, { "r", "r", "ri" } }, + { INDEX_op_rotr_i32, { "r", "r", "ri" } }, - { INDEX_op_brcond_i32, { "r", "r" } }, + { INDEX_op_brcond_i32, { "r", "rI" } }, + { INDEX_op_setcond_i32, { "r", "r", "rI" } }, /* TODO: "r", "r", "r", "r", "ri", "ri" */ { INDEX_op_add2_i32, { "r", "r", "r", "r", "r", "r" } }, { INDEX_op_sub2_i32, { "r", "r", "r", "r", "r", "r" } }, { INDEX_op_brcond2_i32, { "r", "r", "r", "r" } }, + { INDEX_op_setcond2_i32, { "r", "r", "r", "r", "r" } }, + +#if TARGET_LONG_BITS == 32 + { INDEX_op_qemu_ld8u, { "r", "l" } }, + { INDEX_op_qemu_ld8s, { "r", "l" } }, + { INDEX_op_qemu_ld16u, { "r", "l" } }, + { INDEX_op_qemu_ld16s, { "r", "l" } }, + { INDEX_op_qemu_ld32, { "r", "l" } }, + { INDEX_op_qemu_ld64, { "L", "L", "l" } }, + + { INDEX_op_qemu_st8, { "s", "s" } }, + { INDEX_op_qemu_st16, { "s", "s" } }, + { INDEX_op_qemu_st32, { "s", "s" } }, + { INDEX_op_qemu_st64, { "S", "S", "s" } }, +#else + { INDEX_op_qemu_ld8u, { "r", "l", "l" } }, + { INDEX_op_qemu_ld8s, { "r", "l", "l" } }, + { INDEX_op_qemu_ld16u, { "r", "l", "l" } }, + { INDEX_op_qemu_ld16s, { "r", "l", "l" } }, + { INDEX_op_qemu_ld32, { "r", "l", "l" } }, + { INDEX_op_qemu_ld64, { "L", "L", "l", "l" } }, + + { INDEX_op_qemu_st8, { "s", "s", "s" } }, + { INDEX_op_qemu_st16, { "s", "s", "s" } }, + { INDEX_op_qemu_st32, { "s", "s", "s" } }, + { INDEX_op_qemu_st64, { "S", "S", "s", "s" } }, +#endif - { INDEX_op_qemu_ld8u, { "r", "x", "X" } }, - { INDEX_op_qemu_ld8s, { "r", "x", "X" } }, - { INDEX_op_qemu_ld16u, { "r", "x", "X" } }, - { INDEX_op_qemu_ld16s, { "r", "x", "X" } }, - { INDEX_op_qemu_ld32u, { "r", "x", "X" } }, - { INDEX_op_qemu_ld64, { "d", "r", "x", "X" } }, - - { INDEX_op_qemu_st8, { "x", "x", "X" } }, - { INDEX_op_qemu_st16, { "x", "x", "X" } }, - { INDEX_op_qemu_st32, { "x", "x", "X" } }, - { INDEX_op_qemu_st64, { "x", "D", "x", "X" } }, + { INDEX_op_bswap16_i32, { "r", "r" } }, + { INDEX_op_bswap32_i32, { "r", "r" } }, { INDEX_op_ext8s_i32, { "r", "r" } }, { INDEX_op_ext16s_i32, { "r", "r" } }, + { INDEX_op_ext16u_i32, { "r", "r" } }, { -1 }, }; -void tcg_target_init(TCGContext *s) +static void tcg_target_init(TCGContext *s) { +#if !defined(CONFIG_USER_ONLY) /* fail safe */ if ((1 << CPU_TLB_ENTRY_BITS) != sizeof(CPUTLBEntry)) tcg_abort(); +#endif - tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0, - ((2 << TCG_REG_R14) - 1) & ~(1 << TCG_REG_R8)); + tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0, 0xffff); tcg_regset_set32(tcg_target_call_clobber_regs, 0, - ((2 << TCG_REG_R3) - 1) | - (1 << TCG_REG_R12) | (1 << TCG_REG_R14)); + (1 << TCG_REG_R0) | + (1 << TCG_REG_R1) | + (1 << TCG_REG_R2) | + (1 << TCG_REG_R3) | + (1 << TCG_REG_R12) | + (1 << TCG_REG_R14)); tcg_regset_clear(s->reserved_regs); -#ifdef SAVE_LR - tcg_regset_set_reg(s->reserved_regs, TCG_REG_R14); -#endif tcg_regset_set_reg(s->reserved_regs, TCG_REG_CALL_STACK); tcg_regset_set_reg(s->reserved_regs, TCG_REG_R8); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_PC); tcg_add_target_add_op_defs(arm_op_defs); } @@ -1684,7 +1816,7 @@ } } -static inline void tcg_out_mov(TCGContext *s, int ret, int arg) +static inline void tcg_out_mov(TCGContext *s, TCGType type, int ret, int arg) { tcg_out_dat_reg(s, COND_AL, ARITH_MOV, ret, 0, arg, SHIFT_IMM_LSL(0)); } @@ -1695,17 +1827,17 @@ tcg_out_movi32(s, COND_AL, ret, arg); } -void tcg_target_qemu_prologue(TCGContext *s) +static void tcg_target_qemu_prologue(TCGContext *s) { - /* Theoretically there is no need to save r12, but an - even number of registers to be saved as per EABI */ + /* There is no need to save r7, it is used to store the address + of the env structure and is not modified by GCC. */ - /* stmdb sp!, { r4 - r12, lr } */ - tcg_out32(s, (COND_AL << 28) | 0x092d5ff0); + /* stmdb sp!, { r4 - r6, r8 - r11, lr } */ + tcg_out32(s, (COND_AL << 28) | 0x092d4f70); tcg_out_bx(s, COND_AL, TCG_REG_R0); tb_ret_addr = s->code_ptr; - /* ldmia sp!, { r4 - r12, pc } */ - tcg_out32(s, (COND_AL << 28) | 0x08bd9ff0); + /* ldmia sp!, { r4 - r6, r8 - r11, pc } */ + tcg_out32(s, (COND_AL << 28) | 0x08bd8f70); } diff -Nru qemu-kvm-0.12.5+noroms/tcg/arm/tcg-target.h qemu-kvm-0.14.1/tcg/arm/tcg-target.h --- qemu-kvm-0.12.5+noroms/tcg/arm/tcg-target.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/tcg/arm/tcg-target.h 2011-05-11 13:29:46.000000000 +0000 @@ -26,14 +26,6 @@ #define TCG_TARGET_REG_BITS 32 #undef TCG_TARGET_WORDS_BIGENDIAN -#undef TCG_TARGET_HAS_div_i32 -#undef TCG_TARGET_HAS_div_i64 -#undef TCG_TARGET_HAS_bswap32_i32 -#define TCG_TARGET_HAS_ext8s_i32 -#define TCG_TARGET_HAS_ext16s_i32 -#define TCG_TARGET_HAS_neg_i32 -#undef TCG_TARGET_HAS_neg_i64 -#define TCG_TARGET_HAS_not_i32 #undef TCG_TARGET_STACK_GROWSUP enum { @@ -52,24 +44,40 @@ TCG_REG_R12, TCG_REG_R13, TCG_REG_R14, + TCG_REG_PC, }; -#define TCG_TARGET_NB_REGS 15 +#define TCG_TARGET_NB_REGS 16 #define TCG_CT_CONST_ARM 0x100 /* used for function call generation */ #define TCG_REG_CALL_STACK TCG_REG_R13 #define TCG_TARGET_STACK_ALIGN 8 +#define TCG_TARGET_CALL_ALIGN_ARGS 1 #define TCG_TARGET_CALL_STACK_OFFSET 0 +/* optional instructions */ +#define TCG_TARGET_HAS_ext8s_i32 +#define TCG_TARGET_HAS_ext16s_i32 +#undef TCG_TARGET_HAS_ext8u_i32 /* and r0, r1, #0xff */ +#define TCG_TARGET_HAS_ext16u_i32 +#define TCG_TARGET_HAS_bswap16_i32 +#define TCG_TARGET_HAS_bswap32_i32 +#define TCG_TARGET_HAS_not_i32 +#define TCG_TARGET_HAS_neg_i32 +#define TCG_TARGET_HAS_rot_i32 +#define TCG_TARGET_HAS_andc_i32 +// #define TCG_TARGET_HAS_orc_i32 +// #define TCG_TARGET_HAS_eqv_i32 +// #define TCG_TARGET_HAS_nand_i32 +// #define TCG_TARGET_HAS_nor_i32 + #define TCG_TARGET_HAS_GUEST_BASE enum { /* Note: must be synced with dyngen-exec.h */ TCG_AREG0 = TCG_REG_R7, - TCG_AREG1 = TCG_REG_R4, - TCG_AREG2 = TCG_REG_R5, }; static inline void flush_icache_range(unsigned long start, unsigned long stop) diff -Nru qemu-kvm-0.12.5+noroms/tcg/hppa/tcg-target.c qemu-kvm-0.14.1/tcg/hppa/tcg-target.c --- qemu-kvm-0.12.5+noroms/tcg/hppa/tcg-target.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/tcg/hppa/tcg-target.c 2011-05-11 13:29:46.000000000 +0000 @@ -24,41 +24,22 @@ #ifndef NDEBUG static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { - "%r0", - "%r1", - "%rp", - "%r3", - "%r4", - "%r5", - "%r6", - "%r7", - "%r8", - "%r9", - "%r10", - "%r11", - "%r12", - "%r13", - "%r14", - "%r15", - "%r16", - "%r17", - "%r18", - "%r19", - "%r20", - "%r21", - "%r22", - "%r23", - "%r24", - "%r25", - "%r26", - "%dp", - "%ret0", - "%ret1", - "%sp", - "%r31", + "%r0", "%r1", "%rp", "%r3", "%r4", "%r5", "%r6", "%r7", + "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15", + "%r16", "%r17", "%r18", "%r19", "%r20", "%r21", "%r22", "%r23", + "%r24", "%r25", "%r26", "%dp", "%ret0", "%ret1", "%sp", "%r31", }; #endif +/* This is an 8 byte temp slot in the stack frame. */ +#define STACK_TEMP_OFS -16 + +#ifdef CONFIG_USE_GUEST_BASE +#define TCG_GUEST_BASE_REG TCG_REG_R16 +#else +#define TCG_GUEST_BASE_REG TCG_REG_R0 +#endif + static const int tcg_target_reg_alloc_order[] = { TCG_REG_R4, TCG_REG_R5, @@ -75,6 +56,14 @@ TCG_REG_R14, TCG_REG_R15, TCG_REG_R16, + + TCG_REG_R26, + TCG_REG_R25, + TCG_REG_R24, + TCG_REG_R23, + + TCG_REG_RET0, + TCG_REG_RET1, }; static const int tcg_target_call_iarg_regs[4] = { @@ -89,16 +78,101 @@ TCG_REG_RET1, }; +/* True iff val fits a signed field of width BITS. */ +static inline int check_fit_tl(tcg_target_long val, unsigned int bits) +{ + return (val << ((sizeof(tcg_target_long) * 8 - bits)) + >> (sizeof(tcg_target_long) * 8 - bits)) == val; +} + +/* True iff depi can be used to compute (reg | MASK). + Accept a bit pattern like: + 0....01....1 + 1....10....0 + 0..01..10..0 + Copied from gcc sources. */ +static inline int or_mask_p(tcg_target_ulong mask) +{ + if (mask == 0 || mask == -1) { + return 0; + } + mask += mask & -mask; + return (mask & (mask - 1)) == 0; +} + +/* True iff depi or extru can be used to compute (reg & mask). + Accept a bit pattern like these: + 0....01....1 + 1....10....0 + 1..10..01..1 + Copied from gcc sources. */ +static inline int and_mask_p(tcg_target_ulong mask) +{ + return or_mask_p(~mask); +} + +static int low_sign_ext(int val, int len) +{ + return (((val << 1) & ~(-1u << len)) | ((val >> (len - 1)) & 1)); +} + +static int reassemble_12(int as12) +{ + return (((as12 & 0x800) >> 11) | + ((as12 & 0x400) >> 8) | + ((as12 & 0x3ff) << 3)); +} + +static int reassemble_17(int as17) +{ + return (((as17 & 0x10000) >> 16) | + ((as17 & 0x0f800) << 5) | + ((as17 & 0x00400) >> 8) | + ((as17 & 0x003ff) << 3)); +} + +static int reassemble_21(int as21) +{ + return (((as21 & 0x100000) >> 20) | + ((as21 & 0x0ffe00) >> 8) | + ((as21 & 0x000180) << 7) | + ((as21 & 0x00007c) << 14) | + ((as21 & 0x000003) << 12)); +} + +/* ??? Bizzarely, there is no PCREL12F relocation type. I guess all + such relocations are simply fully handled by the assembler. */ +#define R_PARISC_PCREL12F R_PARISC_NONE + static void patch_reloc(uint8_t *code_ptr, int type, tcg_target_long value, tcg_target_long addend) { + uint32_t *insn_ptr = (uint32_t *)code_ptr; + uint32_t insn = *insn_ptr; + tcg_target_long pcrel; + + value += addend; + pcrel = (value - ((tcg_target_long)code_ptr + 8)) >> 2; + switch (type) { + case R_PARISC_PCREL12F: + assert(check_fit_tl(pcrel, 12)); + /* ??? We assume all patches are forward. See tcg_out_brcond + re setting the NUL bit on the branch and eliding the nop. */ + assert(pcrel >= 0); + insn &= ~0x1ffdu; + insn |= reassemble_12(pcrel); + break; case R_PARISC_PCREL17F: - hppa_patch17f((uint32_t *)code_ptr, value, addend); + assert(check_fit_tl(pcrel, 17)); + insn &= ~0x1f1ffdu; + insn |= reassemble_17(pcrel); break; default: tcg_abort(); } + + *insn_ptr = insn; } /* maximum number of register used for input function arguments */ @@ -126,6 +200,24 @@ tcg_regset_reset_reg(ct->u.regs, TCG_REG_R24); tcg_regset_reset_reg(ct->u.regs, TCG_REG_R23); break; + case 'Z': + ct->ct |= TCG_CT_CONST_0; + break; + case 'I': + ct->ct |= TCG_CT_CONST_S11; + break; + case 'J': + ct->ct |= TCG_CT_CONST_S5; + break; + case 'K': + ct->ct |= TCG_CT_CONST_MS11; + break; + case 'M': + ct->ct |= TCG_CT_CONST_AND; + break; + case 'O': + ct->ct |= TCG_CT_CONST_OR; + break; default: return -1; } @@ -135,15 +227,25 @@ } /* test if a constant matches the constraint */ -static inline int tcg_target_const_match(tcg_target_long val, - const TCGArgConstraint *arg_ct) +static int tcg_target_const_match(tcg_target_long val, + const TCGArgConstraint *arg_ct) { - int ct; - - ct = arg_ct->ct; - - /* TODO */ - + int ct = arg_ct->ct; + if (ct & TCG_CT_CONST) { + return 1; + } else if (ct & TCG_CT_CONST_0) { + return val == 0; + } else if (ct & TCG_CT_CONST_S5) { + return check_fit_tl(val, 5); + } else if (ct & TCG_CT_CONST_S11) { + return check_fit_tl(val, 11); + } else if (ct & TCG_CT_CONST_MS11) { + return check_fit_tl(-val, 11); + } else if (ct & TCG_CT_CONST_AND) { + return and_mask_p(val); + } else if (ct & TCG_CT_CONST_OR) { + return or_mask_p(val); + } return 0; } @@ -163,692 +265,1197 @@ #define INSN_SHDEP_CP(x) ((31 - (x)) << 5) #define INSN_SHDEP_P(x) ((x) << 5) #define INSN_COND(x) ((x) << 13) +#define INSN_IM11(x) low_sign_ext(x, 11) +#define INSN_IM14(x) low_sign_ext(x, 14) +#define INSN_IM5(x) (low_sign_ext(x, 5) << 16) + +#define COND_NEVER 0 +#define COND_EQ 1 +#define COND_LT 2 +#define COND_LE 3 +#define COND_LTU 4 +#define COND_LEU 5 +#define COND_SV 6 +#define COND_OD 7 +#define COND_FALSE 8 + +#define INSN_ADD (INSN_OP(0x02) | INSN_EXT6(0x18)) +#define INSN_ADDC (INSN_OP(0x02) | INSN_EXT6(0x1c)) +#define INSN_ADDI (INSN_OP(0x2d)) +#define INSN_ADDIL (INSN_OP(0x0a)) +#define INSN_ADDL (INSN_OP(0x02) | INSN_EXT6(0x28)) +#define INSN_AND (INSN_OP(0x02) | INSN_EXT6(0x08)) +#define INSN_ANDCM (INSN_OP(0x02) | INSN_EXT6(0x00)) +#define INSN_COMCLR (INSN_OP(0x02) | INSN_EXT6(0x22)) +#define INSN_COMICLR (INSN_OP(0x24)) +#define INSN_DEP (INSN_OP(0x35) | INSN_EXT3SH(3)) +#define INSN_DEPI (INSN_OP(0x35) | INSN_EXT3SH(7)) +#define INSN_EXTRS (INSN_OP(0x34) | INSN_EXT3SH(7)) +#define INSN_EXTRU (INSN_OP(0x34) | INSN_EXT3SH(6)) +#define INSN_LDIL (INSN_OP(0x08)) +#define INSN_LDO (INSN_OP(0x0d)) +#define INSN_MTCTL (INSN_OP(0x00) | INSN_EXT8B(0xc2)) +#define INSN_OR (INSN_OP(0x02) | INSN_EXT6(0x09)) +#define INSN_SHD (INSN_OP(0x34) | INSN_EXT3SH(2)) +#define INSN_SUB (INSN_OP(0x02) | INSN_EXT6(0x10)) +#define INSN_SUBB (INSN_OP(0x02) | INSN_EXT6(0x14)) +#define INSN_SUBI (INSN_OP(0x25)) +#define INSN_VEXTRS (INSN_OP(0x34) | INSN_EXT3SH(5)) +#define INSN_VEXTRU (INSN_OP(0x34) | INSN_EXT3SH(4)) +#define INSN_VSHD (INSN_OP(0x34) | INSN_EXT3SH(0)) +#define INSN_XOR (INSN_OP(0x02) | INSN_EXT6(0x0a)) +#define INSN_ZDEP (INSN_OP(0x35) | INSN_EXT3SH(2)) +#define INSN_ZVDEP (INSN_OP(0x35) | INSN_EXT3SH(0)) + +#define INSN_BL (INSN_OP(0x3a) | INSN_EXT3BR(0)) +#define INSN_BL_N (INSN_OP(0x3a) | INSN_EXT3BR(0) | 2) +#define INSN_BLR (INSN_OP(0x3a) | INSN_EXT3BR(2)) +#define INSN_BV (INSN_OP(0x3a) | INSN_EXT3BR(6)) +#define INSN_BV_N (INSN_OP(0x3a) | INSN_EXT3BR(6) | 2) +#define INSN_BLE_SR4 (INSN_OP(0x39) | (1 << 13)) + +#define INSN_LDB (INSN_OP(0x10)) +#define INSN_LDH (INSN_OP(0x11)) +#define INSN_LDW (INSN_OP(0x12)) +#define INSN_LDWM (INSN_OP(0x13)) +#define INSN_FLDDS (INSN_OP(0x0b) | INSN_EXT4(0) | (1 << 12)) + +#define INSN_LDBX (INSN_OP(0x03) | INSN_EXT4(0)) +#define INSN_LDHX (INSN_OP(0x03) | INSN_EXT4(1)) +#define INSN_LDWX (INSN_OP(0x03) | INSN_EXT4(2)) + +#define INSN_STB (INSN_OP(0x18)) +#define INSN_STH (INSN_OP(0x19)) +#define INSN_STW (INSN_OP(0x1a)) +#define INSN_STWM (INSN_OP(0x1b)) +#define INSN_FSTDS (INSN_OP(0x0b) | INSN_EXT4(8) | (1 << 12)) + +#define INSN_COMBT (INSN_OP(0x20)) +#define INSN_COMBF (INSN_OP(0x22)) +#define INSN_COMIBT (INSN_OP(0x21)) +#define INSN_COMIBF (INSN_OP(0x23)) + +/* supplied by libgcc */ +extern void *__canonicalize_funcptr_for_compare(void *); + +static void tcg_out_mov(TCGContext *s, TCGType type, int ret, int arg) +{ + /* PA1.1 defines COPY as OR r,0,t; PA2.0 defines COPY as LDO 0(r),t + but hppa-dis.c is unaware of this definition */ + if (ret != arg) { + tcg_out32(s, INSN_OR | INSN_T(ret) | INSN_R1(arg) + | INSN_R2(TCG_REG_R0)); + } +} + +static void tcg_out_movi(TCGContext *s, TCGType type, + int ret, tcg_target_long arg) +{ + if (check_fit_tl(arg, 14)) { + tcg_out32(s, INSN_LDO | INSN_R1(ret) + | INSN_R2(TCG_REG_R0) | INSN_IM14(arg)); + } else { + uint32_t hi, lo; + hi = arg >> 11; + lo = arg & 0x7ff; + + tcg_out32(s, INSN_LDIL | INSN_R2(ret) | reassemble_21(hi)); + if (lo) { + tcg_out32(s, INSN_LDO | INSN_R1(ret) + | INSN_R2(ret) | INSN_IM14(lo)); + } + } +} + +static void tcg_out_ldst(TCGContext *s, int ret, int addr, + tcg_target_long offset, int op) +{ + if (!check_fit_tl(offset, 14)) { + uint32_t hi, lo, op; + + hi = offset >> 11; + lo = offset & 0x7ff; + + if (addr == TCG_REG_R0) { + op = INSN_LDIL | INSN_R2(TCG_REG_R1); + } else { + op = INSN_ADDIL | INSN_R2(addr); + } + tcg_out32(s, op | reassemble_21(hi)); -#define COND_NEVER 0 -#define COND_EQUAL 1 -#define COND_LT 2 -#define COND_LTEQ 3 -#define COND_LTU 4 -#define COND_LTUEQ 5 -#define COND_SV 6 -#define COND_OD 7 + addr = TCG_REG_R1; + offset = lo; + } + if (ret != addr || offset != 0 || op != INSN_LDO) { + tcg_out32(s, op | INSN_R1(ret) | INSN_R2(addr) | INSN_IM14(offset)); + } +} -/* Logical ADD */ -#define ARITH_ADD (INSN_OP(0x02) | INSN_EXT6(0x28)) -#define ARITH_AND (INSN_OP(0x02) | INSN_EXT6(0x08)) -#define ARITH_OR (INSN_OP(0x02) | INSN_EXT6(0x09)) -#define ARITH_XOR (INSN_OP(0x02) | INSN_EXT6(0x0a)) -#define ARITH_SUB (INSN_OP(0x02) | INSN_EXT6(0x10)) +/* This function is required by tcg.c. */ +static inline void tcg_out_ld(TCGContext *s, TCGType type, int ret, + int arg1, tcg_target_long arg2) +{ + tcg_out_ldst(s, ret, arg1, arg2, INSN_LDW); +} -#define SHD (INSN_OP(0x34) | INSN_EXT3SH(2)) -#define VSHD (INSN_OP(0x34) | INSN_EXT3SH(0)) -#define DEP (INSN_OP(0x35) | INSN_EXT3SH(3)) -#define ZDEP (INSN_OP(0x35) | INSN_EXT3SH(2)) -#define ZVDEP (INSN_OP(0x35) | INSN_EXT3SH(0)) -#define EXTRU (INSN_OP(0x34) | INSN_EXT3SH(6)) -#define EXTRS (INSN_OP(0x34) | INSN_EXT3SH(7)) -#define VEXTRS (INSN_OP(0x34) | INSN_EXT3SH(5)) +/* This function is required by tcg.c. */ +static inline void tcg_out_st(TCGContext *s, TCGType type, int ret, + int arg1, tcg_target_long arg2) +{ + tcg_out_ldst(s, ret, arg1, arg2, INSN_STW); +} -#define SUBI (INSN_OP(0x25)) -#define MTCTL (INSN_OP(0x00) | INSN_EXT8B(0xc2)) +static void tcg_out_ldst_index(TCGContext *s, int data, + int base, int index, int op) +{ + tcg_out32(s, op | INSN_T(data) | INSN_R1(index) | INSN_R2(base)); +} -#define BL (INSN_OP(0x3a) | INSN_EXT3BR(0)) -#define BLE_SR4 (INSN_OP(0x39) | (1 << 13)) -#define BV (INSN_OP(0x3a) | INSN_EXT3BR(6)) -#define BV_N (INSN_OP(0x3a) | INSN_EXT3BR(6) | 2) -#define LDIL (INSN_OP(0x08)) -#define LDO (INSN_OP(0x0d)) +static inline void tcg_out_addi2(TCGContext *s, int ret, int arg1, + tcg_target_long val) +{ + tcg_out_ldst(s, ret, arg1, val, INSN_LDO); +} -#define LDB (INSN_OP(0x10)) -#define LDH (INSN_OP(0x11)) -#define LDW (INSN_OP(0x12)) -#define LDWM (INSN_OP(0x13)) +/* This function is required by tcg.c. */ +static inline void tcg_out_addi(TCGContext *s, int reg, tcg_target_long val) +{ + tcg_out_addi2(s, reg, reg, val); +} -#define STB (INSN_OP(0x18)) -#define STH (INSN_OP(0x19)) -#define STW (INSN_OP(0x1a)) -#define STWM (INSN_OP(0x1b)) +static inline void tcg_out_arith(TCGContext *s, int t, int r1, int r2, int op) +{ + tcg_out32(s, op | INSN_T(t) | INSN_R1(r1) | INSN_R2(r2)); +} -#define COMBT (INSN_OP(0x20)) -#define COMBF (INSN_OP(0x22)) +static inline void tcg_out_arithi(TCGContext *s, int t, int r1, + tcg_target_long val, int op) +{ + assert(check_fit_tl(val, 11)); + tcg_out32(s, op | INSN_R1(t) | INSN_R2(r1) | INSN_IM11(val)); +} -static int lowsignext(uint32_t val, int start, int length) +static inline void tcg_out_nop(TCGContext *s) { - return (((val << 1) & ~(~0 << length)) | - ((val >> (length - 1)) & 1)) << start; + tcg_out_arith(s, TCG_REG_R0, TCG_REG_R0, TCG_REG_R0, INSN_OR); } -static inline void tcg_out_mov(TCGContext *s, int ret, int arg) +static inline void tcg_out_mtctl_sar(TCGContext *s, int arg) { - /* PA1.1 defines COPY as OR r,0,t */ - tcg_out32(s, ARITH_OR | INSN_T(ret) | INSN_R1(arg) | INSN_R2(TCG_REG_R0)); + tcg_out32(s, INSN_MTCTL | INSN_R2(11) | INSN_R1(arg)); +} - /* PA2.0 defines COPY as LDO 0(r),t - * but hppa-dis.c is unaware of this definition */ - /* tcg_out32(s, LDO | INSN_R1(ret) | INSN_R2(arg) | reassemble_14(0)); */ +/* Extract LEN bits at position OFS from ARG and place in RET. + Note that here the bit ordering is reversed from the PA-RISC + standard, such that the right-most bit is 0. */ +static inline void tcg_out_extr(TCGContext *s, int ret, int arg, + unsigned ofs, unsigned len, int sign) +{ + assert(ofs < 32 && len <= 32 - ofs); + tcg_out32(s, (sign ? INSN_EXTRS : INSN_EXTRU) + | INSN_R1(ret) | INSN_R2(arg) + | INSN_SHDEP_P(31 - ofs) | INSN_DEP_LEN(len)); } -static inline void tcg_out_movi(TCGContext *s, TCGType type, - int ret, tcg_target_long arg) +/* Likewise with OFS interpreted little-endian. */ +static inline void tcg_out_dep(TCGContext *s, int ret, int arg, + unsigned ofs, unsigned len) { - if (arg == (arg & 0x1fff)) { - tcg_out32(s, LDO | INSN_R1(ret) | INSN_R2(TCG_REG_R0) | - reassemble_14(arg)); + assert(ofs < 32 && len <= 32 - ofs); + tcg_out32(s, INSN_DEP | INSN_R2(ret) | INSN_R1(arg) + | INSN_SHDEP_CP(31 - ofs) | INSN_DEP_LEN(len)); +} + +static inline void tcg_out_shd(TCGContext *s, int ret, int hi, int lo, + unsigned count) +{ + assert(count < 32); + tcg_out32(s, INSN_SHD | INSN_R1(hi) | INSN_R2(lo) | INSN_T(ret) + | INSN_SHDEP_CP(count)); +} + +static void tcg_out_vshd(TCGContext *s, int ret, int hi, int lo, int creg) +{ + tcg_out_mtctl_sar(s, creg); + tcg_out32(s, INSN_VSHD | INSN_T(ret) | INSN_R1(hi) | INSN_R2(lo)); +} + +static void tcg_out_ori(TCGContext *s, int ret, int arg, tcg_target_ulong m) +{ + int bs0, bs1; + + /* Note that the argument is constrained to match or_mask_p. */ + for (bs0 = 0; bs0 < 32; bs0++) { + if ((m & (1u << bs0)) != 0) { + break; + } + } + for (bs1 = bs0; bs1 < 32; bs1++) { + if ((m & (1u << bs1)) == 0) { + break; + } + } + assert(bs1 == 32 || (1ul << bs1) > m); + + tcg_out_mov(s, TCG_TYPE_I32, ret, arg); + tcg_out32(s, INSN_DEPI | INSN_R2(ret) | INSN_IM5(-1) + | INSN_SHDEP_CP(31 - bs0) | INSN_DEP_LEN(bs1 - bs0)); +} + +static void tcg_out_andi(TCGContext *s, int ret, int arg, tcg_target_ulong m) +{ + int ls0, ls1, ms0; + + /* Note that the argument is constrained to match and_mask_p. */ + for (ls0 = 0; ls0 < 32; ls0++) { + if ((m & (1u << ls0)) == 0) { + break; + } + } + for (ls1 = ls0; ls1 < 32; ls1++) { + if ((m & (1u << ls1)) != 0) { + break; + } + } + for (ms0 = ls1; ms0 < 32; ms0++) { + if ((m & (1u << ms0)) == 0) { + break; + } + } + assert (ms0 == 32); + + if (ls1 == 32) { + tcg_out_extr(s, ret, arg, 0, ls0, 0); } else { - tcg_out32(s, LDIL | INSN_R2(ret) | - reassemble_21(lrsel((uint32_t)arg, 0))); - if (arg & 0x7ff) - tcg_out32(s, LDO | INSN_R1(ret) | INSN_R2(ret) | - reassemble_14(rrsel((uint32_t)arg, 0))); + tcg_out_mov(s, TCG_TYPE_I32, ret, arg); + tcg_out32(s, INSN_DEPI | INSN_R2(ret) | INSN_IM5(0) + | INSN_SHDEP_CP(31 - ls0) | INSN_DEP_LEN(ls1 - ls0)); } } -static inline void tcg_out_ld_raw(TCGContext *s, int ret, - tcg_target_long arg) +static inline void tcg_out_ext8s(TCGContext *s, int ret, int arg) { - tcg_out32(s, LDIL | INSN_R2(ret) | - reassemble_21(lrsel((uint32_t)arg, 0))); - tcg_out32(s, LDW | INSN_R1(ret) | INSN_R2(ret) | - reassemble_14(rrsel((uint32_t)arg, 0))); + tcg_out_extr(s, ret, arg, 0, 8, 1); } -static inline void tcg_out_ld_ptr(TCGContext *s, int ret, - tcg_target_long arg) +static inline void tcg_out_ext16s(TCGContext *s, int ret, int arg) { - tcg_out_ld_raw(s, ret, arg); + tcg_out_extr(s, ret, arg, 0, 16, 1); } -static inline void tcg_out_ldst(TCGContext *s, int ret, int addr, int offset, - int op) +static void tcg_out_shli(TCGContext *s, int ret, int arg, int count) { - if (offset == (offset & 0xfff)) - tcg_out32(s, op | INSN_R1(ret) | INSN_R2(addr) | - reassemble_14(offset)); - else { - fprintf(stderr, "unimplemented %s with offset %d\n", __func__, offset); - tcg_abort(); - } + count &= 31; + tcg_out32(s, INSN_ZDEP | INSN_R2(ret) | INSN_R1(arg) + | INSN_SHDEP_CP(31 - count) | INSN_DEP_LEN(32 - count)); } -static inline void tcg_out_ld(TCGContext *s, TCGType type, int ret, - int arg1, tcg_target_long arg2) +static void tcg_out_shl(TCGContext *s, int ret, int arg, int creg) { - fprintf(stderr, "unimplemented %s\n", __func__); - tcg_abort(); + tcg_out_arithi(s, TCG_REG_R20, creg, 31, INSN_SUBI); + tcg_out_mtctl_sar(s, TCG_REG_R20); + tcg_out32(s, INSN_ZVDEP | INSN_R2(ret) | INSN_R1(arg) | INSN_DEP_LEN(32)); } -static inline void tcg_out_st(TCGContext *s, TCGType type, int ret, - int arg1, tcg_target_long arg2) +static void tcg_out_shri(TCGContext *s, int ret, int arg, int count) { - fprintf(stderr, "unimplemented %s\n", __func__); - tcg_abort(); + count &= 31; + tcg_out_extr(s, ret, arg, count, 32 - count, 0); } -static inline void tcg_out_arith(TCGContext *s, int t, int r1, int r2, int op) +static void tcg_out_shr(TCGContext *s, int ret, int arg, int creg) { - tcg_out32(s, op | INSN_T(t) | INSN_R1(r1) | INSN_R2(r2)); + tcg_out_vshd(s, ret, TCG_REG_R0, arg, creg); } -static inline void tcg_out_arithi(TCGContext *s, int t, int r1, - tcg_target_long val, int op) +static void tcg_out_sari(TCGContext *s, int ret, int arg, int count) { - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R20, val); - tcg_out_arith(s, t, r1, TCG_REG_R20, op); + count &= 31; + tcg_out_extr(s, ret, arg, count, 32 - count, 1); } -static inline void tcg_out_addi(TCGContext *s, int reg, tcg_target_long val) +static void tcg_out_sar(TCGContext *s, int ret, int arg, int creg) { - tcg_out_arithi(s, reg, reg, val, ARITH_ADD); + tcg_out_arithi(s, TCG_REG_R20, creg, 31, INSN_SUBI); + tcg_out_mtctl_sar(s, TCG_REG_R20); + tcg_out32(s, INSN_VEXTRS | INSN_R1(ret) | INSN_R2(arg) | INSN_DEP_LEN(32)); } -static inline void tcg_out_nop(TCGContext *s) +static void tcg_out_rotli(TCGContext *s, int ret, int arg, int count) { - tcg_out32(s, ARITH_OR | INSN_T(TCG_REG_R0) | INSN_R1(TCG_REG_R0) | - INSN_R2(TCG_REG_R0)); + count &= 31; + tcg_out_shd(s, ret, arg, arg, 32 - count); } -static inline void tcg_out_ext8s(TCGContext *s, int ret, int arg) { - tcg_out32(s, EXTRS | INSN_R1(ret) | INSN_R2(arg) | - INSN_SHDEP_P(31) | INSN_DEP_LEN(8)); +static void tcg_out_rotl(TCGContext *s, int ret, int arg, int creg) +{ + tcg_out_arithi(s, TCG_REG_R20, creg, 32, INSN_SUBI); + tcg_out_vshd(s, ret, arg, arg, TCG_REG_R20); } -static inline void tcg_out_ext16s(TCGContext *s, int ret, int arg) { - tcg_out32(s, EXTRS | INSN_R1(ret) | INSN_R2(arg) | - INSN_SHDEP_P(31) | INSN_DEP_LEN(16)); +static void tcg_out_rotri(TCGContext *s, int ret, int arg, int count) +{ + count &= 31; + tcg_out_shd(s, ret, arg, arg, count); } -static inline void tcg_out_bswap16(TCGContext *s, int ret, int arg) { - if(ret != arg) - tcg_out_mov(s, ret, arg); - tcg_out32(s, DEP | INSN_R2(ret) | INSN_R1(ret) | - INSN_SHDEP_CP(15) | INSN_DEP_LEN(8)); - tcg_out32(s, SHD | INSN_T(ret) | INSN_R1(TCG_REG_R0) | - INSN_R2(ret) | INSN_SHDEP_CP(8)); +static void tcg_out_rotr(TCGContext *s, int ret, int arg, int creg) +{ + tcg_out_vshd(s, ret, arg, arg, creg); } -static inline void tcg_out_bswap32(TCGContext *s, int ret, int arg, int temp) { - tcg_out32(s, SHD | INSN_T(temp) | INSN_R1(arg) | - INSN_R2(arg) | INSN_SHDEP_CP(16)); - tcg_out32(s, DEP | INSN_R2(temp) | INSN_R1(temp) | - INSN_SHDEP_CP(15) | INSN_DEP_LEN(8)); - tcg_out32(s, SHD | INSN_T(ret) | INSN_R1(arg) | - INSN_R2(temp) | INSN_SHDEP_CP(8)); +static void tcg_out_bswap16(TCGContext *s, int ret, int arg, int sign) +{ + if (ret != arg) { + tcg_out_mov(s, TCG_TYPE_I32, ret, arg); /* arg = xxAB */ + } + tcg_out_dep(s, ret, ret, 16, 8); /* ret = xBAB */ + tcg_out_extr(s, ret, ret, 8, 16, sign); /* ret = ..BA */ } -static inline void tcg_out_call(TCGContext *s, void *func) +static void tcg_out_bswap32(TCGContext *s, int ret, int arg, int temp) { - uint32_t val = (uint32_t)__canonicalize_funcptr_for_compare(func); - tcg_out32(s, LDIL | INSN_R2(TCG_REG_R20) | - reassemble_21(lrsel(val, 0))); - tcg_out32(s, BLE_SR4 | INSN_R2(TCG_REG_R20) | - reassemble_17(rrsel(val, 0) >> 2)); - tcg_out_mov(s, TCG_REG_RP, TCG_REG_R31); + /* arg = ABCD */ + tcg_out_rotri(s, temp, arg, 16); /* temp = CDAB */ + tcg_out_dep(s, temp, temp, 16, 8); /* temp = CBAB */ + tcg_out_shd(s, ret, arg, temp, 8); /* ret = DCBA */ } -#if defined(CONFIG_SOFTMMU) +static void tcg_out_call(TCGContext *s, void *func) +{ + tcg_target_long val, hi, lo, disp; -#include "../../softmmu_defs.h" + val = (uint32_t)__canonicalize_funcptr_for_compare(func); + disp = (val - ((tcg_target_long)s->code_ptr + 8)) >> 2; -static void *qemu_ld_helpers[4] = { - __ldb_mmu, - __ldw_mmu, - __ldl_mmu, - __ldq_mmu, -}; + if (check_fit_tl(disp, 17)) { + tcg_out32(s, INSN_BL_N | INSN_R2(TCG_REG_RP) | reassemble_17(disp)); + } else { + hi = val >> 11; + lo = val & 0x7ff; -static void *qemu_st_helpers[4] = { - __stb_mmu, - __stw_mmu, - __stl_mmu, - __stq_mmu, -}; -#endif + tcg_out32(s, INSN_LDIL | INSN_R2(TCG_REG_R20) | reassemble_21(hi)); + tcg_out32(s, INSN_BLE_SR4 | INSN_R2(TCG_REG_R20) + | reassemble_17(lo >> 2)); + tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_RP, TCG_REG_R31); + } +} -static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, int opc) +static void tcg_out_xmpyu(TCGContext *s, int retl, int reth, + int arg1, int arg2) { - int addr_reg, data_reg, data_reg2, r0, r1, mem_index, s_bits, bswap; -#if defined(CONFIG_SOFTMMU) - uint32_t *label1_ptr, *label2_ptr; -#endif -#if TARGET_LONG_BITS == 64 -#if defined(CONFIG_SOFTMMU) - uint32_t *label3_ptr; -#endif - int addr_reg2; -#endif + /* Store both words into the stack for copy to the FPU. */ + tcg_out_ldst(s, arg1, TCG_REG_SP, STACK_TEMP_OFS, INSN_STW); + tcg_out_ldst(s, arg2, TCG_REG_SP, STACK_TEMP_OFS + 4, INSN_STW); + + /* Load both words into the FPU at the same time. We get away + with this because we can address the left and right half of the + FPU registers individually once loaded. */ + /* fldds stack_temp(sp),fr22 */ + tcg_out32(s, INSN_FLDDS | INSN_R2(TCG_REG_SP) + | INSN_IM5(STACK_TEMP_OFS) | INSN_T(22)); + + /* xmpyu fr22r,fr22,fr22 */ + tcg_out32(s, 0x3ad64796); + + /* Store the 64-bit result back into the stack. */ + /* fstds stack_temp(sp),fr22 */ + tcg_out32(s, INSN_FSTDS | INSN_R2(TCG_REG_SP) + | INSN_IM5(STACK_TEMP_OFS) | INSN_T(22)); + + /* Load the pieces of the result that the caller requested. */ + if (reth) { + tcg_out_ldst(s, reth, TCG_REG_SP, STACK_TEMP_OFS, INSN_LDW); + } + if (retl) { + tcg_out_ldst(s, retl, TCG_REG_SP, STACK_TEMP_OFS + 4, INSN_LDW); + } +} - data_reg = *args++; - if (opc == 3) - data_reg2 = *args++; - else - data_reg2 = 0; /* suppress warning */ - addr_reg = *args++; -#if TARGET_LONG_BITS == 64 - addr_reg2 = *args++; -#endif - mem_index = *args; - s_bits = opc & 3; +static void tcg_out_add2(TCGContext *s, int destl, int desth, + int al, int ah, int bl, int bh, int blconst) +{ + int tmp = (destl == ah || destl == bh ? TCG_REG_R20 : destl); - r0 = TCG_REG_R26; - r1 = TCG_REG_R25; + if (blconst) { + tcg_out_arithi(s, tmp, al, bl, INSN_ADDI); + } else { + tcg_out_arith(s, tmp, al, bl, INSN_ADD); + } + tcg_out_arith(s, desth, ah, bh, INSN_ADDC); -#if defined(CONFIG_SOFTMMU) - tcg_out_mov(s, r1, addr_reg); + tcg_out_mov(s, TCG_TYPE_I32, destl, tmp); +} - tcg_out_mov(s, r0, addr_reg); +static void tcg_out_sub2(TCGContext *s, int destl, int desth, int al, int ah, + int bl, int bh, int alconst, int blconst) +{ + int tmp = (destl == ah || destl == bh ? TCG_REG_R20 : destl); - tcg_out32(s, SHD | INSN_T(r1) | INSN_R1(TCG_REG_R0) | INSN_R2(r1) | - INSN_SHDEP_CP(TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS)); + if (alconst) { + if (blconst) { + tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_R20, bl); + bl = TCG_REG_R20; + } + tcg_out_arithi(s, tmp, bl, al, INSN_SUBI); + } else if (blconst) { + tcg_out_arithi(s, tmp, al, -bl, INSN_ADDI); + } else { + tcg_out_arith(s, tmp, al, bl, INSN_SUB); + } + tcg_out_arith(s, desth, ah, bh, INSN_SUBB); - tcg_out_arithi(s, r0, r0, TARGET_PAGE_MASK | ((1 << s_bits) - 1), - ARITH_AND); + tcg_out_mov(s, TCG_TYPE_I32, destl, tmp); +} - tcg_out_arithi(s, r1, r1, (CPU_TLB_SIZE - 1) << CPU_TLB_ENTRY_BITS, - ARITH_AND); +static void tcg_out_branch(TCGContext *s, int label_index, int nul) +{ + TCGLabel *l = &s->labels[label_index]; + uint32_t op = nul ? INSN_BL_N : INSN_BL; - tcg_out_arith(s, r1, r1, TCG_AREG0, ARITH_ADD); - tcg_out_arithi(s, r1, r1, - offsetof(CPUState, tlb_table[mem_index][0].addr_read), - ARITH_ADD); + if (l->has_value) { + tcg_target_long val = l->u.value; - tcg_out_ldst(s, TCG_REG_R20, r1, 0, LDW); + val -= (tcg_target_long)s->code_ptr + 8; + val >>= 2; + assert(check_fit_tl(val, 17)); -#if TARGET_LONG_BITS == 32 - /* if equal, jump to label1 */ - label1_ptr = (uint32_t *)s->code_ptr; - tcg_out32(s, COMBT | INSN_R1(TCG_REG_R20) | INSN_R2(r0) | - INSN_COND(COND_EQUAL)); - tcg_out_mov(s, r0, addr_reg); /* delay slot */ -#else - /* if not equal, jump to label3 */ - label3_ptr = (uint32_t *)s->code_ptr; - tcg_out32(s, COMBF | INSN_R1(TCG_REG_R20) | INSN_R2(r0) | - INSN_COND(COND_EQUAL)); - tcg_out_mov(s, r0, addr_reg); /* delay slot */ - - tcg_out_ldst(s, TCG_REG_R20, r1, 4, LDW); - - /* if equal, jump to label1 */ - label1_ptr = (uint32_t *)s->code_ptr; - tcg_out32(s, COMBT | INSN_R1(TCG_REG_R20) | INSN_R2(addr_reg2) | - INSN_COND(COND_EQUAL)); - tcg_out_nop(s); /* delay slot */ + tcg_out32(s, op | reassemble_17(val)); + } else { + /* We need to keep the offset unchanged for retranslation. */ + uint32_t old_insn = *(uint32_t *)s->code_ptr; - /* label3: */ - *label3_ptr |= reassemble_12((uint32_t *)s->code_ptr - label3_ptr - 2); -#endif + tcg_out_reloc(s, s->code_ptr, R_PARISC_PCREL17F, label_index, 0); + tcg_out32(s, op | (old_insn & 0x1f1ffdu)); + } +} -#if TARGET_LONG_BITS == 32 - tcg_out_mov(s, TCG_REG_R26, addr_reg); - tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_R25, mem_index); -#else - tcg_out_mov(s, TCG_REG_R26, addr_reg); - tcg_out_mov(s, TCG_REG_R25, addr_reg2); - tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_R24, mem_index); -#endif +static const uint8_t tcg_cond_to_cmp_cond[10] = +{ + [TCG_COND_EQ] = COND_EQ, + [TCG_COND_NE] = COND_EQ | COND_FALSE, + [TCG_COND_LT] = COND_LT, + [TCG_COND_GE] = COND_LT | COND_FALSE, + [TCG_COND_LE] = COND_LE, + [TCG_COND_GT] = COND_LE | COND_FALSE, + [TCG_COND_LTU] = COND_LTU, + [TCG_COND_GEU] = COND_LTU | COND_FALSE, + [TCG_COND_LEU] = COND_LEU, + [TCG_COND_GTU] = COND_LEU | COND_FALSE, +}; - tcg_out_call(s, qemu_ld_helpers[s_bits]); +static void tcg_out_brcond(TCGContext *s, int cond, TCGArg c1, + TCGArg c2, int c2const, int label_index) +{ + TCGLabel *l = &s->labels[label_index]; + int op, pacond; - switch(opc) { - case 0 | 4: - tcg_out_ext8s(s, data_reg, TCG_REG_RET0); - break; - case 1 | 4: - tcg_out_ext16s(s, data_reg, TCG_REG_RET0); - break; - case 0: - case 1: - case 2: - default: - tcg_out_mov(s, data_reg, TCG_REG_RET0); - break; - case 3: - tcg_abort(); - tcg_out_mov(s, data_reg, TCG_REG_RET0); - tcg_out_mov(s, data_reg2, TCG_REG_RET1); - break; + /* Note that COMIB operates as if the immediate is the first + operand. We model brcond with the immediate in the second + to better match what targets are likely to give us. For + consistency, model COMB with reversed operands as well. */ + pacond = tcg_cond_to_cmp_cond[tcg_swap_cond(cond)]; + + if (c2const) { + op = (pacond & COND_FALSE ? INSN_COMIBF : INSN_COMIBT); + op |= INSN_IM5(c2); + } else { + op = (pacond & COND_FALSE ? INSN_COMBF : INSN_COMBT); + op |= INSN_R1(c2); } + op |= INSN_R2(c1); + op |= INSN_COND(pacond & 7); - /* jump to label2 */ - label2_ptr = (uint32_t *)s->code_ptr; - tcg_out32(s, BL | INSN_R2(TCG_REG_R0) | 2); + if (l->has_value) { + tcg_target_long val = l->u.value; - /* label1: */ - *label1_ptr |= reassemble_12((uint32_t *)s->code_ptr - label1_ptr - 2); + val -= (tcg_target_long)s->code_ptr + 8; + val >>= 2; + assert(check_fit_tl(val, 12)); + + /* ??? Assume that all branches to defined labels are backward. + Which means that if the nul bit is set, the delay slot is + executed if the branch is taken, and not executed in fallthru. */ + tcg_out32(s, op | reassemble_12(val)); + tcg_out_nop(s); + } else { + /* We need to keep the offset unchanged for retranslation. */ + uint32_t old_insn = *(uint32_t *)s->code_ptr; - tcg_out_arithi(s, TCG_REG_R20, r1, - offsetof(CPUTLBEntry, addend) - offsetof(CPUTLBEntry, addr_read), - ARITH_ADD); - tcg_out_ldst(s, TCG_REG_R20, TCG_REG_R20, 0, LDW); - tcg_out_arith(s, r0, r0, TCG_REG_R20, ARITH_ADD); -#else - r0 = addr_reg; -#endif + tcg_out_reloc(s, s->code_ptr, R_PARISC_PCREL12F, label_index, 0); + /* ??? Assume that all branches to undefined labels are forward. + Which means that if the nul bit is set, the delay slot is + not executed if the branch is taken, which is what we want. */ + tcg_out32(s, op | 2 | (old_insn & 0x1ffdu)); + } +} -#ifdef TARGET_WORDS_BIGENDIAN - bswap = 0; -#else - bswap = 1; -#endif - switch (opc) { - case 0: - tcg_out_ldst(s, data_reg, r0, 0, LDB); - break; - case 0 | 4: - tcg_out_ldst(s, data_reg, r0, 0, LDB); - tcg_out_ext8s(s, data_reg, data_reg); - break; - case 1: - tcg_out_ldst(s, data_reg, r0, 0, LDH); - if (bswap) - tcg_out_bswap16(s, data_reg, data_reg); - break; - case 1 | 4: - tcg_out_ldst(s, data_reg, r0, 0, LDH); - if (bswap) - tcg_out_bswap16(s, data_reg, data_reg); - tcg_out_ext16s(s, data_reg, data_reg); - break; - case 2: - tcg_out_ldst(s, data_reg, r0, 0, LDW); - if (bswap) - tcg_out_bswap32(s, data_reg, data_reg, TCG_REG_R20); - break; - case 3: - tcg_abort(); - if (!bswap) { - tcg_out_ldst(s, data_reg, r0, 0, LDW); - tcg_out_ldst(s, data_reg2, r0, 4, LDW); - } else { - tcg_out_ldst(s, data_reg, r0, 4, LDW); - tcg_out_bswap32(s, data_reg, data_reg, TCG_REG_R20); - tcg_out_ldst(s, data_reg2, r0, 0, LDW); - tcg_out_bswap32(s, data_reg2, data_reg2, TCG_REG_R20); - } - break; - default: - tcg_abort(); +static void tcg_out_comclr(TCGContext *s, int cond, TCGArg ret, + TCGArg c1, TCGArg c2, int c2const) +{ + int op, pacond; + + /* Note that COMICLR operates as if the immediate is the first + operand. We model setcond with the immediate in the second + to better match what targets are likely to give us. For + consistency, model COMCLR with reversed operands as well. */ + pacond = tcg_cond_to_cmp_cond[tcg_swap_cond(cond)]; + + if (c2const) { + op = INSN_COMICLR | INSN_R2(c1) | INSN_R1(ret) | INSN_IM11(c2); + } else { + op = INSN_COMCLR | INSN_R2(c1) | INSN_R1(c2) | INSN_T(ret); } + op |= INSN_COND(pacond & 7); + op |= pacond & COND_FALSE ? 1 << 12 : 0; -#if defined(CONFIG_SOFTMMU) - /* label2: */ - *label2_ptr |= reassemble_17((uint32_t *)s->code_ptr - label2_ptr - 2); -#endif + tcg_out32(s, op); } -static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, int opc) +static void tcg_out_brcond2(TCGContext *s, int cond, TCGArg al, TCGArg ah, + TCGArg bl, int blconst, TCGArg bh, int bhconst, + int label_index) { - int addr_reg, data_reg, data_reg2, r0, r1, mem_index, s_bits, bswap; -#if defined(CONFIG_SOFTMMU) - uint32_t *label1_ptr, *label2_ptr; -#endif -#if TARGET_LONG_BITS == 64 -#if defined(CONFIG_SOFTMMU) - uint32_t *label3_ptr; -#endif - int addr_reg2; -#endif + switch (cond) { + case TCG_COND_EQ: + case TCG_COND_NE: + tcg_out_comclr(s, tcg_invert_cond(cond), TCG_REG_R0, al, bl, blconst); + tcg_out_brcond(s, cond, ah, bh, bhconst, label_index); + break; - data_reg = *args++; - if (opc == 3) - data_reg2 = *args++; - else - data_reg2 = 0; /* suppress warning */ - addr_reg = *args++; -#if TARGET_LONG_BITS == 64 - addr_reg2 = *args++; -#endif - mem_index = *args; + default: + tcg_out_brcond(s, cond, ah, bh, bhconst, label_index); + tcg_out_comclr(s, TCG_COND_NE, TCG_REG_R0, ah, bh, bhconst); + tcg_out_brcond(s, tcg_unsigned_cond(cond), + al, bl, blconst, label_index); + break; + } +} - s_bits = opc; +static void tcg_out_setcond(TCGContext *s, int cond, TCGArg ret, + TCGArg c1, TCGArg c2, int c2const) +{ + tcg_out_comclr(s, tcg_invert_cond(cond), ret, c1, c2, c2const); + tcg_out_movi(s, TCG_TYPE_I32, ret, 1); +} - r0 = TCG_REG_R26; - r1 = TCG_REG_R25; +static void tcg_out_setcond2(TCGContext *s, int cond, TCGArg ret, + TCGArg al, TCGArg ah, TCGArg bl, int blconst, + TCGArg bh, int bhconst) +{ + int scratch = TCG_REG_R20; -#if defined(CONFIG_SOFTMMU) - tcg_out_mov(s, r1, addr_reg); + if (ret != al && ret != ah + && (blconst || ret != bl) + && (bhconst || ret != bh)) { + scratch = ret; + } - tcg_out_mov(s, r0, addr_reg); + switch (cond) { + case TCG_COND_EQ: + case TCG_COND_NE: + tcg_out_setcond(s, cond, scratch, al, bl, blconst); + tcg_out_comclr(s, TCG_COND_EQ, TCG_REG_R0, ah, bh, bhconst); + tcg_out_movi(s, TCG_TYPE_I32, scratch, cond == TCG_COND_NE); + break; - tcg_out32(s, SHD | INSN_T(r1) | INSN_R1(TCG_REG_R0) | INSN_R2(r1) | - INSN_SHDEP_CP(TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS)); + default: + tcg_out_setcond(s, tcg_unsigned_cond(cond), scratch, al, bl, blconst); + tcg_out_comclr(s, TCG_COND_EQ, TCG_REG_R0, ah, bh, bhconst); + tcg_out_movi(s, TCG_TYPE_I32, scratch, 0); + tcg_out_comclr(s, cond, TCG_REG_R0, ah, bh, bhconst); + tcg_out_movi(s, TCG_TYPE_I32, scratch, 1); + break; + } - tcg_out_arithi(s, r0, r0, TARGET_PAGE_MASK | ((1 << s_bits) - 1), - ARITH_AND); + tcg_out_mov(s, TCG_TYPE_I32, ret, scratch); +} - tcg_out_arithi(s, r1, r1, (CPU_TLB_SIZE - 1) << CPU_TLB_ENTRY_BITS, - ARITH_AND); +#if defined(CONFIG_SOFTMMU) +#include "../../softmmu_defs.h" - tcg_out_arith(s, r1, r1, TCG_AREG0, ARITH_ADD); - tcg_out_arithi(s, r1, r1, - offsetof(CPUState, tlb_table[mem_index][0].addr_write), - ARITH_ADD); +static void *qemu_ld_helpers[4] = { + __ldb_mmu, + __ldw_mmu, + __ldl_mmu, + __ldq_mmu, +}; - tcg_out_ldst(s, TCG_REG_R20, r1, 0, LDW); +static void *qemu_st_helpers[4] = { + __stb_mmu, + __stw_mmu, + __stl_mmu, + __stq_mmu, +}; -#if TARGET_LONG_BITS == 32 - /* if equal, jump to label1 */ - label1_ptr = (uint32_t *)s->code_ptr; - tcg_out32(s, COMBT | INSN_R1(TCG_REG_R20) | INSN_R2(r0) | - INSN_COND(COND_EQUAL)); - tcg_out_mov(s, r0, addr_reg); /* delay slot */ -#else - /* if not equal, jump to label3 */ - label3_ptr = (uint32_t *)s->code_ptr; - tcg_out32(s, COMBF | INSN_R1(TCG_REG_R20) | INSN_R2(r0) | - INSN_COND(COND_EQUAL)); - tcg_out_mov(s, r0, addr_reg); /* delay slot */ - - tcg_out_ldst(s, TCG_REG_R20, r1, 4, LDW); - - /* if equal, jump to label1 */ - label1_ptr = (uint32_t *)s->code_ptr; - tcg_out32(s, COMBT | INSN_R1(TCG_REG_R20) | INSN_R2(addr_reg2) | - INSN_COND(COND_EQUAL)); - tcg_out_nop(s); /* delay slot */ +/* Load and compare a TLB entry, and branch if TLB miss. OFFSET is set to + the offset of the first ADDR_READ or ADDR_WRITE member of the appropriate + TLB for the memory index. The return value is the offset from ENV + contained in R1 afterward (to be used when loading ADDEND); if the + return value is 0, R1 is not used. */ + +static int tcg_out_tlb_read(TCGContext *s, int r0, int r1, int addrlo, + int addrhi, int s_bits, int lab_miss, int offset) +{ + int ret; + + /* Extracting the index into the TLB. The "normal C operation" is + r1 = addr_reg >> TARGET_PAGE_BITS; + r1 &= CPU_TLB_SIZE - 1; + r1 <<= CPU_TLB_ENTRY_BITS; + What this does is extract CPU_TLB_BITS beginning at TARGET_PAGE_BITS + and place them at CPU_TLB_ENTRY_BITS. We can combine the first two + operations with an EXTRU. Unfortunately, the current value of + CPU_TLB_ENTRY_BITS is > 3, so we can't merge that shift with the + add that follows. */ + tcg_out_extr(s, r1, addrlo, TARGET_PAGE_BITS, CPU_TLB_BITS, 0); + tcg_out_shli(s, r1, r1, CPU_TLB_ENTRY_BITS); + tcg_out_arith(s, r1, r1, TCG_AREG0, INSN_ADDL); + + /* Make sure that both the addr_{read,write} and addend can be + read with a 14-bit offset from the same base register. */ + if (check_fit_tl(offset + CPU_TLB_SIZE, 14)) { + ret = 0; + } else { + ret = (offset + 0x400) & ~0x7ff; + offset = ret - offset; + tcg_out_addi2(s, TCG_REG_R1, r1, ret); + r1 = TCG_REG_R1; + } - /* label3: */ - *label3_ptr |= reassemble_12((uint32_t *)s->code_ptr - label3_ptr - 2); -#endif + /* Load the entry from the computed slot. */ + if (TARGET_LONG_BITS == 64) { + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_R23, r1, offset); + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_R20, r1, offset + 4); + } else { + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_R20, r1, offset); + } - tcg_out_mov(s, TCG_REG_R26, addr_reg); -#if TARGET_LONG_BITS == 64 - tcg_out_mov(s, TCG_REG_R25, addr_reg2); - if (opc == 3) { - tcg_abort(); - tcg_out_mov(s, TCG_REG_R24, data_reg); - tcg_out_mov(s, TCG_REG_R23, data_reg2); - /* TODO: push mem_index */ - tcg_abort(); + /* Compute the value that ought to appear in the TLB for a hit, namely, the page + of the address. We include the low N bits of the address to catch unaligned + accesses and force them onto the slow path. Do this computation after having + issued the load from the TLB slot to give the load time to complete. */ + tcg_out_andi(s, r0, addrlo, TARGET_PAGE_MASK | ((1 << s_bits) - 1)); + + /* If not equal, jump to lab_miss. */ + if (TARGET_LONG_BITS == 64) { + tcg_out_brcond2(s, TCG_COND_NE, TCG_REG_R20, TCG_REG_R23, + r0, 0, addrhi, 0, lab_miss); } else { - switch(opc) { - case 0: - tcg_out32(s, EXTRU | INSN_R1(TCG_REG_R24) | INSN_R2(data_reg) | - INSN_SHDEP_P(31) | INSN_DEP_LEN(8)); - break; - case 1: - tcg_out32(s, EXTRU | INSN_R1(TCG_REG_R24) | INSN_R2(data_reg) | - INSN_SHDEP_P(31) | INSN_DEP_LEN(16)); - break; - case 2: - tcg_out_mov(s, TCG_REG_R24, data_reg); - break; - } - tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_R23, mem_index); + tcg_out_brcond(s, TCG_COND_NE, TCG_REG_R20, r0, 0, lab_miss); } + + return ret; +} +#endif + +static void tcg_out_qemu_ld_direct(TCGContext *s, int datalo_reg, int datahi_reg, + int addr_reg, int addend_reg, int opc) +{ +#ifdef TARGET_WORDS_BIGENDIAN + const int bswap = 0; #else - if (opc == 3) { - tcg_abort(); - tcg_out_mov(s, TCG_REG_R25, data_reg); - tcg_out_mov(s, TCG_REG_R24, data_reg2); - tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_R23, mem_index); - } else { - switch(opc) { - case 0: - tcg_out32(s, EXTRU | INSN_R1(TCG_REG_R25) | INSN_R2(data_reg) | - INSN_SHDEP_P(31) | INSN_DEP_LEN(8)); - break; - case 1: - tcg_out32(s, EXTRU | INSN_R1(TCG_REG_R25) | INSN_R2(data_reg) | - INSN_SHDEP_P(31) | INSN_DEP_LEN(16)); - break; - case 2: - tcg_out_mov(s, TCG_REG_R25, data_reg); - break; + const int bswap = 1; +#endif + + switch (opc) { + case 0: + tcg_out_ldst_index(s, datalo_reg, addr_reg, addend_reg, INSN_LDBX); + break; + case 0 | 4: + tcg_out_ldst_index(s, datalo_reg, addr_reg, addend_reg, INSN_LDBX); + tcg_out_ext8s(s, datalo_reg, datalo_reg); + break; + case 1: + tcg_out_ldst_index(s, datalo_reg, addr_reg, addend_reg, INSN_LDHX); + if (bswap) { + tcg_out_bswap16(s, datalo_reg, datalo_reg, 0); + } + break; + case 1 | 4: + tcg_out_ldst_index(s, datalo_reg, addr_reg, addend_reg, INSN_LDHX); + if (bswap) { + tcg_out_bswap16(s, datalo_reg, datalo_reg, 1); + } else { + tcg_out_ext16s(s, datalo_reg, datalo_reg); + } + break; + case 2: + tcg_out_ldst_index(s, datalo_reg, addr_reg, addend_reg, INSN_LDWX); + if (bswap) { + tcg_out_bswap32(s, datalo_reg, datalo_reg, TCG_REG_R20); + } + break; + case 3: + if (bswap) { + int t = datahi_reg; + datahi_reg = datalo_reg; + datalo_reg = t; + } + /* We can't access the low-part with a reg+reg addressing mode, + so perform the addition now and use reg_ofs addressing mode. */ + if (addend_reg != TCG_REG_R0) { + tcg_out_arith(s, TCG_REG_R20, addr_reg, addend_reg, INSN_ADD); + addr_reg = TCG_REG_R20; + } + /* Make sure not to clobber the base register. */ + if (datahi_reg == addr_reg) { + tcg_out_ldst(s, datalo_reg, addr_reg, 4, INSN_LDW); + tcg_out_ldst(s, datahi_reg, addr_reg, 0, INSN_LDW); + } else { + tcg_out_ldst(s, datahi_reg, addr_reg, 0, INSN_LDW); + tcg_out_ldst(s, datalo_reg, addr_reg, 4, INSN_LDW); + } + if (bswap) { + tcg_out_bswap32(s, datalo_reg, datalo_reg, TCG_REG_R20); + tcg_out_bswap32(s, datahi_reg, datahi_reg, TCG_REG_R20); } - tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_R24, mem_index); + break; + default: + tcg_abort(); } -#endif - tcg_out_call(s, qemu_st_helpers[s_bits]); +} - /* jump to label2 */ - label2_ptr = (uint32_t *)s->code_ptr; - tcg_out32(s, BL | INSN_R2(TCG_REG_R0) | 2); +static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, int opc) +{ + int datalo_reg = *args++; + /* Note that datahi_reg is only used for 64-bit loads. */ + int datahi_reg = (opc == 3 ? *args++ : TCG_REG_R0); + int addrlo_reg = *args++; + +#if defined(CONFIG_SOFTMMU) + /* Note that addrhi_reg is only used for 64-bit guests. */ + int addrhi_reg = (TARGET_LONG_BITS == 64 ? *args++ : TCG_REG_R0); + int mem_index = *args; + int lab1, lab2, argreg, offset; + + lab1 = gen_new_label(); + lab2 = gen_new_label(); + + offset = offsetof(CPUState, tlb_table[mem_index][0].addr_read); + offset = tcg_out_tlb_read(s, TCG_REG_R26, TCG_REG_R25, addrlo_reg, addrhi_reg, + opc & 3, lab1, offset); + + /* TLB Hit. */ + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_R20, (offset ? TCG_REG_R1 : TCG_REG_R25), + offsetof(CPUState, tlb_table[mem_index][0].addend) - offset); + tcg_out_qemu_ld_direct(s, datalo_reg, datahi_reg, addrlo_reg, TCG_REG_R20, opc); + tcg_out_branch(s, lab2, 1); + /* TLB Miss. */ /* label1: */ - *label1_ptr |= reassemble_12((uint32_t *)s->code_ptr - label1_ptr - 2); + tcg_out_label(s, lab1, (tcg_target_long)s->code_ptr); - tcg_out_arithi(s, TCG_REG_R20, r1, - offsetof(CPUTLBEntry, addend) - offsetof(CPUTLBEntry, addr_write), - ARITH_ADD); - tcg_out_ldst(s, TCG_REG_R20, TCG_REG_R20, 0, LDW); - tcg_out_arith(s, r0, r0, TCG_REG_R20, ARITH_ADD); + argreg = TCG_REG_R26; + tcg_out_mov(s, TCG_TYPE_I32, argreg--, addrlo_reg); + if (TARGET_LONG_BITS == 64) { + tcg_out_mov(s, TCG_TYPE_I32, argreg--, addrhi_reg); + } + tcg_out_movi(s, TCG_TYPE_I32, argreg, mem_index); + + tcg_out_call(s, qemu_ld_helpers[opc & 3]); + + switch (opc) { + case 0: + tcg_out_andi(s, datalo_reg, TCG_REG_RET0, 0xff); + break; + case 0 | 4: + tcg_out_ext8s(s, datalo_reg, TCG_REG_RET0); + break; + case 1: + tcg_out_andi(s, datalo_reg, TCG_REG_RET0, 0xffff); + break; + case 1 | 4: + tcg_out_ext16s(s, datalo_reg, TCG_REG_RET0); + break; + case 2: + case 2 | 4: + tcg_out_mov(s, TCG_TYPE_I32, datalo_reg, TCG_REG_RET0); + break; + case 3: + tcg_out_mov(s, TCG_TYPE_I32, datahi_reg, TCG_REG_RET0); + tcg_out_mov(s, TCG_TYPE_I32, datalo_reg, TCG_REG_RET1); + break; + default: + tcg_abort(); + } + + /* label2: */ + tcg_out_label(s, lab2, (tcg_target_long)s->code_ptr); #else - r0 = addr_reg; + tcg_out_qemu_ld_direct(s, datalo_reg, datahi_reg, addrlo_reg, + (GUEST_BASE ? TCG_GUEST_BASE_REG : TCG_REG_R0), opc); #endif +} +static void tcg_out_qemu_st_direct(TCGContext *s, int datalo_reg, int datahi_reg, + int addr_reg, int opc) +{ #ifdef TARGET_WORDS_BIGENDIAN - bswap = 0; + const int bswap = 0; #else - bswap = 1; + const int bswap = 1; #endif + switch (opc) { case 0: - tcg_out_ldst(s, data_reg, r0, 0, STB); + tcg_out_ldst(s, datalo_reg, addr_reg, 0, INSN_STB); break; case 1: if (bswap) { - tcg_out_bswap16(s, TCG_REG_R20, data_reg); - data_reg = TCG_REG_R20; + tcg_out_bswap16(s, TCG_REG_R20, datalo_reg, 0); + datalo_reg = TCG_REG_R20; } - tcg_out_ldst(s, data_reg, r0, 0, STH); + tcg_out_ldst(s, datalo_reg, addr_reg, 0, INSN_STH); break; case 2: if (bswap) { - tcg_out_bswap32(s, TCG_REG_R20, data_reg, TCG_REG_R20); - data_reg = TCG_REG_R20; + tcg_out_bswap32(s, TCG_REG_R20, datalo_reg, TCG_REG_R20); + datalo_reg = TCG_REG_R20; } - tcg_out_ldst(s, data_reg, r0, 0, STW); + tcg_out_ldst(s, datalo_reg, addr_reg, 0, INSN_STW); break; case 3: - tcg_abort(); - if (!bswap) { - tcg_out_ldst(s, data_reg, r0, 0, STW); - tcg_out_ldst(s, data_reg2, r0, 4, STW); - } else { - tcg_out_bswap32(s, TCG_REG_R20, data_reg, TCG_REG_R20); - tcg_out_ldst(s, TCG_REG_R20, r0, 4, STW); - tcg_out_bswap32(s, TCG_REG_R20, data_reg2, TCG_REG_R20); - tcg_out_ldst(s, TCG_REG_R20, r0, 0, STW); + if (bswap) { + tcg_out_bswap32(s, TCG_REG_R20, datalo_reg, TCG_REG_R20); + tcg_out_bswap32(s, TCG_REG_R23, datahi_reg, TCG_REG_R23); + datahi_reg = TCG_REG_R20; + datalo_reg = TCG_REG_R23; } + tcg_out_ldst(s, datahi_reg, addr_reg, 0, INSN_STW); + tcg_out_ldst(s, datalo_reg, addr_reg, 4, INSN_STW); break; default: tcg_abort(); } +} + +static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, int opc) +{ + int datalo_reg = *args++; + /* Note that datahi_reg is only used for 64-bit loads. */ + int datahi_reg = (opc == 3 ? *args++ : TCG_REG_R0); + int addrlo_reg = *args++; + #if defined(CONFIG_SOFTMMU) + /* Note that addrhi_reg is only used for 64-bit guests. */ + int addrhi_reg = (TARGET_LONG_BITS == 64 ? *args++ : TCG_REG_R0); + int mem_index = *args; + int lab1, lab2, argreg, offset; + + lab1 = gen_new_label(); + lab2 = gen_new_label(); + + offset = offsetof(CPUState, tlb_table[mem_index][0].addr_write); + offset = tcg_out_tlb_read(s, TCG_REG_R26, TCG_REG_R25, addrlo_reg, addrhi_reg, + opc, lab1, offset); + + /* TLB Hit. */ + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_R20, (offset ? TCG_REG_R1 : TCG_REG_R25), + offsetof(CPUState, tlb_table[mem_index][0].addend) - offset); + + /* There are no indexed stores, so we must do this addition explitly. + Careful to avoid R20, which is used for the bswaps to follow. */ + tcg_out_arith(s, TCG_REG_R31, addrlo_reg, TCG_REG_R20, INSN_ADDL); + tcg_out_qemu_st_direct(s, datalo_reg, datahi_reg, TCG_REG_R31, opc); + tcg_out_branch(s, lab2, 1); + + /* TLB Miss. */ + /* label1: */ + tcg_out_label(s, lab1, (tcg_target_long)s->code_ptr); + + argreg = TCG_REG_R26; + tcg_out_mov(s, TCG_TYPE_I32, argreg--, addrlo_reg); + if (TARGET_LONG_BITS == 64) { + tcg_out_mov(s, TCG_TYPE_I32, argreg--, addrhi_reg); + } + + switch(opc) { + case 0: + tcg_out_andi(s, argreg--, datalo_reg, 0xff); + tcg_out_movi(s, TCG_TYPE_I32, argreg, mem_index); + break; + case 1: + tcg_out_andi(s, argreg--, datalo_reg, 0xffff); + tcg_out_movi(s, TCG_TYPE_I32, argreg, mem_index); + break; + case 2: + tcg_out_mov(s, TCG_TYPE_I32, argreg--, datalo_reg); + tcg_out_movi(s, TCG_TYPE_I32, argreg, mem_index); + break; + case 3: + /* Because of the alignment required by the 64-bit data argument, + we will always use R23/R24. Also, we will always run out of + argument registers for storing mem_index, so that will have + to go on the stack. */ + if (mem_index == 0) { + argreg = TCG_REG_R0; + } else { + argreg = TCG_REG_R20; + tcg_out_movi(s, TCG_TYPE_I32, argreg, mem_index); + } + tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_R23, datahi_reg); + tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_R24, datalo_reg); + tcg_out_st(s, TCG_TYPE_I32, argreg, TCG_REG_SP, + TCG_TARGET_CALL_STACK_OFFSET - 4); + break; + default: + tcg_abort(); + } + + tcg_out_call(s, qemu_st_helpers[opc]); + /* label2: */ - *label2_ptr |= reassemble_17((uint32_t *)s->code_ptr - label2_ptr - 2); + tcg_out_label(s, lab2, (tcg_target_long)s->code_ptr); +#else + /* There are no indexed stores, so if GUEST_BASE is set we must do the add + explicitly. Careful to avoid R20, which is used for the bswaps to follow. */ + if (GUEST_BASE != 0) { + tcg_out_arith(s, TCG_REG_R31, addrlo_reg, TCG_GUEST_BASE_REG, INSN_ADDL); + addrlo_reg = TCG_REG_R31; + } + tcg_out_qemu_st_direct(s, datalo_reg, datahi_reg, addrlo_reg, opc); #endif } -static inline void tcg_out_op(TCGContext *s, int opc, const TCGArg *args, - const int *const_args) +static void tcg_out_exit_tb(TCGContext *s, TCGArg arg) +{ + if (!check_fit_tl(arg, 14)) { + uint32_t hi, lo; + hi = arg & ~0x7ff; + lo = arg & 0x7ff; + if (lo) { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_RET0, hi); + tcg_out32(s, INSN_BV | INSN_R2(TCG_REG_R18)); + tcg_out_addi(s, TCG_REG_RET0, lo); + return; + } + arg = hi; + } + tcg_out32(s, INSN_BV | INSN_R2(TCG_REG_R18)); + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_RET0, arg); +} + +static void tcg_out_goto_tb(TCGContext *s, TCGArg arg) { - int c; + if (s->tb_jmp_offset) { + /* direct jump method */ + fprintf(stderr, "goto_tb direct\n"); + tcg_abort(); + } else { + /* indirect jump method */ + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_R20, TCG_REG_R0, + (tcg_target_long)(s->tb_next + arg)); + tcg_out32(s, INSN_BV_N | INSN_R2(TCG_REG_R20)); + } + s->tb_next_offset[arg] = s->code_ptr - s->code_buf; +} +static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg *args, + const int *const_args) +{ switch (opc) { case INDEX_op_exit_tb: - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_RET0, args[0]); - tcg_out32(s, BV_N | INSN_R2(TCG_REG_R18)); + tcg_out_exit_tb(s, args[0]); break; case INDEX_op_goto_tb: - if (s->tb_jmp_offset) { - /* direct jump method */ - fprintf(stderr, "goto_tb direct\n"); - tcg_abort(); - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R20, args[0]); - tcg_out32(s, BV_N | INSN_R2(TCG_REG_R20)); - s->tb_jmp_offset[args[0]] = s->code_ptr - s->code_buf; - } else { - /* indirect jump method */ - tcg_out_ld_ptr(s, TCG_REG_R20, - (tcg_target_long)(s->tb_next + args[0])); - tcg_out32(s, BV_N | INSN_R2(TCG_REG_R20)); - } - s->tb_next_offset[args[0]] = s->code_ptr - s->code_buf; + tcg_out_goto_tb(s, args[0]); break; + case INDEX_op_call: - tcg_out32(s, BLE_SR4 | INSN_R2(args[0])); - tcg_out_mov(s, TCG_REG_RP, TCG_REG_R31); + if (const_args[0]) { + tcg_out_call(s, (void *)args[0]); + } else { + /* ??? FIXME: the value in the register in args[0] is almost + certainly a procedure descriptor, not a code address. We + probably need to use the millicode $$dyncall routine. */ + tcg_abort(); + } break; + case INDEX_op_jmp: fprintf(stderr, "unimplemented jmp\n"); tcg_abort(); break; + case INDEX_op_br: - fprintf(stderr, "unimplemented br\n"); - tcg_abort(); + tcg_out_branch(s, args[0], 1); break; + case INDEX_op_movi_i32: tcg_out_movi(s, TCG_TYPE_I32, args[0], (uint32_t)args[1]); break; case INDEX_op_ld8u_i32: - tcg_out_ldst(s, args[0], args[1], args[2], LDB); + tcg_out_ldst(s, args[0], args[1], args[2], INSN_LDB); break; case INDEX_op_ld8s_i32: - tcg_out_ldst(s, args[0], args[1], args[2], LDB); + tcg_out_ldst(s, args[0], args[1], args[2], INSN_LDB); tcg_out_ext8s(s, args[0], args[0]); break; case INDEX_op_ld16u_i32: - tcg_out_ldst(s, args[0], args[1], args[2], LDH); + tcg_out_ldst(s, args[0], args[1], args[2], INSN_LDH); break; case INDEX_op_ld16s_i32: - tcg_out_ldst(s, args[0], args[1], args[2], LDH); + tcg_out_ldst(s, args[0], args[1], args[2], INSN_LDH); tcg_out_ext16s(s, args[0], args[0]); break; case INDEX_op_ld_i32: - tcg_out_ldst(s, args[0], args[1], args[2], LDW); + tcg_out_ldst(s, args[0], args[1], args[2], INSN_LDW); break; case INDEX_op_st8_i32: - tcg_out_ldst(s, args[0], args[1], args[2], STB); + tcg_out_ldst(s, args[0], args[1], args[2], INSN_STB); break; case INDEX_op_st16_i32: - tcg_out_ldst(s, args[0], args[1], args[2], STH); + tcg_out_ldst(s, args[0], args[1], args[2], INSN_STH); break; case INDEX_op_st_i32: - tcg_out_ldst(s, args[0], args[1], args[2], STW); + tcg_out_ldst(s, args[0], args[1], args[2], INSN_STW); + break; + + case INDEX_op_add_i32: + if (const_args[2]) { + tcg_out_addi2(s, args[0], args[1], args[2]); + } else { + tcg_out_arith(s, args[0], args[1], args[2], INSN_ADDL); + } break; case INDEX_op_sub_i32: - c = ARITH_SUB; - goto gen_arith; + if (const_args[1]) { + if (const_args[2]) { + tcg_out_movi(s, TCG_TYPE_I32, args[0], args[1] - args[2]); + } else { + /* Recall that SUBI is a reversed subtract. */ + tcg_out_arithi(s, args[0], args[2], args[1], INSN_SUBI); + } + } else if (const_args[2]) { + tcg_out_addi2(s, args[0], args[1], -args[2]); + } else { + tcg_out_arith(s, args[0], args[1], args[2], INSN_SUB); + } + break; + case INDEX_op_and_i32: - c = ARITH_AND; - goto gen_arith; + if (const_args[2]) { + tcg_out_andi(s, args[0], args[1], args[2]); + } else { + tcg_out_arith(s, args[0], args[1], args[2], INSN_AND); + } + break; + case INDEX_op_or_i32: - c = ARITH_OR; - goto gen_arith; + if (const_args[2]) { + tcg_out_ori(s, args[0], args[1], args[2]); + } else { + tcg_out_arith(s, args[0], args[1], args[2], INSN_OR); + } + break; + case INDEX_op_xor_i32: - c = ARITH_XOR; - goto gen_arith; - case INDEX_op_add_i32: - c = ARITH_ADD; - goto gen_arith; + tcg_out_arith(s, args[0], args[1], args[2], INSN_XOR); + break; + + case INDEX_op_andc_i32: + if (const_args[2]) { + tcg_out_andi(s, args[0], args[1], ~args[2]); + } else { + tcg_out_arith(s, args[0], args[1], args[2], INSN_ANDCM); + } + break; case INDEX_op_shl_i32: - tcg_out32(s, SUBI | INSN_R1(TCG_REG_R20) | INSN_R2(args[2]) | - lowsignext(0x1f, 0, 11)); - tcg_out32(s, MTCTL | INSN_R2(11) | INSN_R1(TCG_REG_R20)); - tcg_out32(s, ZVDEP | INSN_R2(args[0]) | INSN_R1(args[1]) | - INSN_DEP_LEN(32)); + if (const_args[2]) { + tcg_out_shli(s, args[0], args[1], args[2]); + } else { + tcg_out_shl(s, args[0], args[1], args[2]); + } break; + case INDEX_op_shr_i32: - tcg_out32(s, MTCTL | INSN_R2(11) | INSN_R1(args[2])); - tcg_out32(s, VSHD | INSN_T(args[0]) | INSN_R1(TCG_REG_R0) | - INSN_R2(args[1])); + if (const_args[2]) { + tcg_out_shri(s, args[0], args[1], args[2]); + } else { + tcg_out_shr(s, args[0], args[1], args[2]); + } break; + case INDEX_op_sar_i32: - tcg_out32(s, SUBI | INSN_R1(TCG_REG_R20) | INSN_R2(args[2]) | - lowsignext(0x1f, 0, 11)); - tcg_out32(s, MTCTL | INSN_R2(11) | INSN_R1(TCG_REG_R20)); - tcg_out32(s, VEXTRS | INSN_R1(args[0]) | INSN_R2(args[1]) | - INSN_DEP_LEN(32)); + if (const_args[2]) { + tcg_out_sari(s, args[0], args[1], args[2]); + } else { + tcg_out_sar(s, args[0], args[1], args[2]); + } + break; + + case INDEX_op_rotl_i32: + if (const_args[2]) { + tcg_out_rotli(s, args[0], args[1], args[2]); + } else { + tcg_out_rotl(s, args[0], args[1], args[2]); + } + break; + + case INDEX_op_rotr_i32: + if (const_args[2]) { + tcg_out_rotri(s, args[0], args[1], args[2]); + } else { + tcg_out_rotr(s, args[0], args[1], args[2]); + } break; case INDEX_op_mul_i32: - fprintf(stderr, "unimplemented mul\n"); - tcg_abort(); + tcg_out_xmpyu(s, args[0], TCG_REG_R0, args[1], args[2]); break; case INDEX_op_mulu2_i32: - fprintf(stderr, "unimplemented mulu2\n"); - tcg_abort(); + tcg_out_xmpyu(s, args[0], args[1], args[2], args[3]); break; - case INDEX_op_div2_i32: - fprintf(stderr, "unimplemented div2\n"); - tcg_abort(); + + case INDEX_op_bswap16_i32: + tcg_out_bswap16(s, args[0], args[1], 0); break; - case INDEX_op_divu2_i32: - fprintf(stderr, "unimplemented divu2\n"); - tcg_abort(); + case INDEX_op_bswap32_i32: + tcg_out_bswap32(s, args[0], args[1], TCG_REG_R20); + break; + + case INDEX_op_not_i32: + tcg_out_arithi(s, args[0], args[1], -1, INSN_SUBI); + break; + case INDEX_op_ext8s_i32: + tcg_out_ext8s(s, args[0], args[1]); + break; + case INDEX_op_ext16s_i32: + tcg_out_ext16s(s, args[0], args[1]); break; case INDEX_op_brcond_i32: - fprintf(stderr, "unimplemented brcond\n"); - tcg_abort(); + tcg_out_brcond(s, args[2], args[0], args[1], const_args[1], args[3]); + break; + case INDEX_op_brcond2_i32: + tcg_out_brcond2(s, args[4], args[0], args[1], + args[2], const_args[2], + args[3], const_args[3], args[5]); + break; + + case INDEX_op_setcond_i32: + tcg_out_setcond(s, args[3], args[0], args[1], args[2], const_args[2]); + break; + case INDEX_op_setcond2_i32: + tcg_out_setcond2(s, args[5], args[0], args[1], args[2], + args[3], const_args[3], args[4], const_args[4]); + break; + + case INDEX_op_add2_i32: + tcg_out_add2(s, args[0], args[1], args[2], args[3], + args[4], args[5], const_args[4]); + break; + + case INDEX_op_sub2_i32: + tcg_out_sub2(s, args[0], args[1], args[2], args[3], + args[4], args[5], const_args[2], const_args[4]); break; case INDEX_op_qemu_ld8u: @@ -863,9 +1470,12 @@ case INDEX_op_qemu_ld16s: tcg_out_qemu_ld(s, args, 1 | 4); break; - case INDEX_op_qemu_ld32u: + case INDEX_op_qemu_ld32: tcg_out_qemu_ld(s, args, 2); break; + case INDEX_op_qemu_ld64: + tcg_out_qemu_ld(s, args, 3); + break; case INDEX_op_qemu_st8: tcg_out_qemu_st(s, args, 0); @@ -876,88 +1486,186 @@ case INDEX_op_qemu_st32: tcg_out_qemu_st(s, args, 2); break; + case INDEX_op_qemu_st64: + tcg_out_qemu_st(s, args, 3); + break; default: fprintf(stderr, "unknown opcode 0x%x\n", opc); tcg_abort(); } - return; - -gen_arith: - tcg_out_arith(s, args[0], args[1], args[2], c); } static const TCGTargetOpDef hppa_op_defs[] = { { INDEX_op_exit_tb, { } }, { INDEX_op_goto_tb, { } }, - { INDEX_op_call, { "r" } }, + { INDEX_op_call, { "ri" } }, { INDEX_op_jmp, { "r" } }, { INDEX_op_br, { } }, { INDEX_op_mov_i32, { "r", "r" } }, { INDEX_op_movi_i32, { "r" } }, + { INDEX_op_ld8u_i32, { "r", "r" } }, { INDEX_op_ld8s_i32, { "r", "r" } }, { INDEX_op_ld16u_i32, { "r", "r" } }, { INDEX_op_ld16s_i32, { "r", "r" } }, { INDEX_op_ld_i32, { "r", "r" } }, - { INDEX_op_st8_i32, { "r", "r" } }, - { INDEX_op_st16_i32, { "r", "r" } }, - { INDEX_op_st_i32, { "r", "r" } }, - - { INDEX_op_add_i32, { "r", "r", "r" } }, - { INDEX_op_sub_i32, { "r", "r", "r" } }, - { INDEX_op_and_i32, { "r", "r", "r" } }, - { INDEX_op_or_i32, { "r", "r", "r" } }, - { INDEX_op_xor_i32, { "r", "r", "r" } }, - - { INDEX_op_shl_i32, { "r", "r", "r" } }, - { INDEX_op_shr_i32, { "r", "r", "r" } }, - { INDEX_op_sar_i32, { "r", "r", "r" } }, + { INDEX_op_st8_i32, { "rZ", "r" } }, + { INDEX_op_st16_i32, { "rZ", "r" } }, + { INDEX_op_st_i32, { "rZ", "r" } }, + + { INDEX_op_add_i32, { "r", "rZ", "ri" } }, + { INDEX_op_sub_i32, { "r", "rI", "ri" } }, + { INDEX_op_and_i32, { "r", "rZ", "rM" } }, + { INDEX_op_or_i32, { "r", "rZ", "rO" } }, + { INDEX_op_xor_i32, { "r", "rZ", "rZ" } }, + /* Note that the second argument will be inverted, which means + we want a constant whose inversion matches M, and that O = ~M. + See the implementation of and_mask_p. */ + { INDEX_op_andc_i32, { "r", "rZ", "rO" } }, + + { INDEX_op_mul_i32, { "r", "r", "r" } }, + { INDEX_op_mulu2_i32, { "r", "r", "r", "r" } }, + + { INDEX_op_shl_i32, { "r", "r", "ri" } }, + { INDEX_op_shr_i32, { "r", "r", "ri" } }, + { INDEX_op_sar_i32, { "r", "r", "ri" } }, + { INDEX_op_rotl_i32, { "r", "r", "ri" } }, + { INDEX_op_rotr_i32, { "r", "r", "ri" } }, + + { INDEX_op_bswap16_i32, { "r", "r" } }, + { INDEX_op_bswap32_i32, { "r", "r" } }, + { INDEX_op_not_i32, { "r", "r" } }, + + { INDEX_op_ext8s_i32, { "r", "r" } }, + { INDEX_op_ext16s_i32, { "r", "r" } }, + + { INDEX_op_brcond_i32, { "rZ", "rJ" } }, + { INDEX_op_brcond2_i32, { "rZ", "rZ", "rJ", "rJ" } }, - { INDEX_op_brcond_i32, { "r", "r" } }, + { INDEX_op_setcond_i32, { "r", "rZ", "rI" } }, + { INDEX_op_setcond2_i32, { "r", "rZ", "rZ", "rI", "rI" } }, + + { INDEX_op_add2_i32, { "r", "r", "rZ", "rZ", "rI", "rZ" } }, + { INDEX_op_sub2_i32, { "r", "r", "rI", "rZ", "rK", "rZ" } }, #if TARGET_LONG_BITS == 32 { INDEX_op_qemu_ld8u, { "r", "L" } }, { INDEX_op_qemu_ld8s, { "r", "L" } }, { INDEX_op_qemu_ld16u, { "r", "L" } }, { INDEX_op_qemu_ld16s, { "r", "L" } }, - { INDEX_op_qemu_ld32u, { "r", "L" } }, + { INDEX_op_qemu_ld32, { "r", "L" } }, { INDEX_op_qemu_ld64, { "r", "r", "L" } }, - { INDEX_op_qemu_st8, { "L", "L" } }, - { INDEX_op_qemu_st16, { "L", "L" } }, - { INDEX_op_qemu_st32, { "L", "L" } }, - { INDEX_op_qemu_st64, { "L", "L", "L" } }, + { INDEX_op_qemu_st8, { "LZ", "L" } }, + { INDEX_op_qemu_st16, { "LZ", "L" } }, + { INDEX_op_qemu_st32, { "LZ", "L" } }, + { INDEX_op_qemu_st64, { "LZ", "LZ", "L" } }, #else { INDEX_op_qemu_ld8u, { "r", "L", "L" } }, { INDEX_op_qemu_ld8s, { "r", "L", "L" } }, { INDEX_op_qemu_ld16u, { "r", "L", "L" } }, { INDEX_op_qemu_ld16s, { "r", "L", "L" } }, - { INDEX_op_qemu_ld32u, { "r", "L", "L" } }, - { INDEX_op_qemu_ld32s, { "r", "L", "L" } }, + { INDEX_op_qemu_ld32, { "r", "L", "L" } }, { INDEX_op_qemu_ld64, { "r", "r", "L", "L" } }, - { INDEX_op_qemu_st8, { "L", "L", "L" } }, - { INDEX_op_qemu_st16, { "L", "L", "L" } }, - { INDEX_op_qemu_st32, { "L", "L", "L" } }, - { INDEX_op_qemu_st64, { "L", "L", "L", "L" } }, + { INDEX_op_qemu_st8, { "LZ", "L", "L" } }, + { INDEX_op_qemu_st16, { "LZ", "L", "L" } }, + { INDEX_op_qemu_st32, { "LZ", "L", "L" } }, + { INDEX_op_qemu_st64, { "LZ", "LZ", "L", "L" } }, #endif { -1 }, }; -void tcg_target_init(TCGContext *s) +static int tcg_target_callee_save_regs[] = { + /* R2, the return address register, is saved specially + in the caller's frame. */ + /* R3, the frame pointer, is not currently modified. */ + TCG_REG_R4, + TCG_REG_R5, + TCG_REG_R6, + TCG_REG_R7, + TCG_REG_R8, + TCG_REG_R9, + TCG_REG_R10, + TCG_REG_R11, + TCG_REG_R12, + TCG_REG_R13, + TCG_REG_R14, + TCG_REG_R15, + TCG_REG_R16, + /* R17 is the global env, so no need to save. */ + TCG_REG_R18 +}; + +static void tcg_target_qemu_prologue(TCGContext *s) +{ + int frame_size, i; + + /* Allocate space for the fixed frame marker. */ + frame_size = -TCG_TARGET_CALL_STACK_OFFSET; + frame_size += TCG_TARGET_STATIC_CALL_ARGS_SIZE; + + /* Allocate space for the saved registers. */ + frame_size += ARRAY_SIZE(tcg_target_callee_save_regs) * 4; + + /* Align the allocated space. */ + frame_size = ((frame_size + TCG_TARGET_STACK_ALIGN - 1) + & -TCG_TARGET_STACK_ALIGN); + + /* The return address is stored in the caller's frame. */ + tcg_out_st(s, TCG_TYPE_PTR, TCG_REG_RP, TCG_REG_SP, -20); + + /* Allocate stack frame, saving the first register at the same time. */ + tcg_out_ldst(s, tcg_target_callee_save_regs[0], + TCG_REG_SP, frame_size, INSN_STWM); + + /* Save all callee saved registers. */ + for (i = 1; i < ARRAY_SIZE(tcg_target_callee_save_regs); i++) { + tcg_out_st(s, TCG_TYPE_PTR, tcg_target_callee_save_regs[i], + TCG_REG_SP, -frame_size + i * 4); + } + +#ifdef CONFIG_USE_GUEST_BASE + if (GUEST_BASE != 0) { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_GUEST_BASE_REG, GUEST_BASE); + tcg_regset_set_reg(s->reserved_regs, TCG_GUEST_BASE_REG); + } +#endif + + /* Jump to TB, and adjust R18 to be the return address. */ + tcg_out32(s, INSN_BLE_SR4 | INSN_R2(TCG_REG_R26)); + tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_R18, TCG_REG_R31); + + /* Restore callee saved registers. */ + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_RP, TCG_REG_SP, -frame_size - 20); + for (i = 1; i < ARRAY_SIZE(tcg_target_callee_save_regs); i++) { + tcg_out_ld(s, TCG_TYPE_PTR, tcg_target_callee_save_regs[i], + TCG_REG_SP, -frame_size + i * 4); + } + + /* Deallocate stack frame and return. */ + tcg_out32(s, INSN_BV | INSN_R2(TCG_REG_RP)); + tcg_out_ldst(s, tcg_target_callee_save_regs[0], + TCG_REG_SP, -frame_size, INSN_LDWM); +} + +static void tcg_target_init(TCGContext *s) { tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0, 0xffffffff); - tcg_regset_set32(tcg_target_call_clobber_regs, 0, - (1 << TCG_REG_R20) | - (1 << TCG_REG_R21) | - (1 << TCG_REG_R22) | - (1 << TCG_REG_R23) | - (1 << TCG_REG_R24) | - (1 << TCG_REG_R25) | - (1 << TCG_REG_R26)); + + tcg_regset_clear(tcg_target_call_clobber_regs); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R20); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R21); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R22); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R23); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R24); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R25); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R26); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_RET0); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_RET1); tcg_regset_clear(s->reserved_regs); tcg_regset_set_reg(s->reserved_regs, TCG_REG_R0); /* hardwired to zero */ diff -Nru qemu-kvm-0.12.5+noroms/tcg/hppa/tcg-target.h qemu-kvm-0.14.1/tcg/hppa/tcg-target.h --- qemu-kvm-0.12.5+noroms/tcg/hppa/tcg-target.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/tcg/hppa/tcg-target.h 2011-05-11 13:29:46.000000000 +0000 @@ -69,135 +69,51 @@ TCG_REG_R31, }; +#define TCG_CT_CONST_0 0x0100 +#define TCG_CT_CONST_S5 0x0200 +#define TCG_CT_CONST_S11 0x0400 +#define TCG_CT_CONST_MS11 0x0800 +#define TCG_CT_CONST_AND 0x1000 +#define TCG_CT_CONST_OR 0x2000 + /* used for function call generation */ #define TCG_REG_CALL_STACK TCG_REG_SP -#define TCG_TARGET_STACK_ALIGN 16 +#define TCG_TARGET_STACK_ALIGN 64 +#define TCG_TARGET_CALL_STACK_OFFSET -48 +#define TCG_TARGET_STATIC_CALL_ARGS_SIZE 8*4 +#define TCG_TARGET_CALL_ALIGN_ARGS 1 #define TCG_TARGET_STACK_GROWSUP /* optional instructions */ -//#define TCG_TARGET_HAS_ext8s_i32 -//#define TCG_TARGET_HAS_ext16s_i32 -//#define TCG_TARGET_HAS_bswap16_i32 -//#define TCG_TARGET_HAS_bswap32_i32 +// #define TCG_TARGET_HAS_div_i32 +#define TCG_TARGET_HAS_rot_i32 +#define TCG_TARGET_HAS_ext8s_i32 +#define TCG_TARGET_HAS_ext16s_i32 +#define TCG_TARGET_HAS_bswap16_i32 +#define TCG_TARGET_HAS_bswap32_i32 +#define TCG_TARGET_HAS_not_i32 +#define TCG_TARGET_HAS_andc_i32 +// #define TCG_TARGET_HAS_orc_i32 + +/* optional instructions automatically implemented */ +#undef TCG_TARGET_HAS_neg_i32 /* sub rd, 0, rs */ +#undef TCG_TARGET_HAS_ext8u_i32 /* and rd, rs, 0xff */ +#undef TCG_TARGET_HAS_ext16u_i32 /* and rd, rs, 0xffff */ + +#define TCG_TARGET_HAS_GUEST_BASE /* Note: must be synced with dyngen-exec.h */ #define TCG_AREG0 TCG_REG_R17 -#define TCG_AREG1 TCG_REG_R14 -#define TCG_AREG2 TCG_REG_R15 static inline void flush_icache_range(unsigned long start, unsigned long stop) { start &= ~31; - while (start <= stop) - { - asm volatile ("fdc 0(%0)\n" - "sync\n" - "fic 0(%%sr4, %0)\n" - "sync\n" + while (start <= stop) { + asm volatile ("fdc 0(%0)\n\t" + "sync\n\t" + "fic 0(%%sr4, %0)\n\t" + "sync" : : "r"(start) : "memory"); start += 32; } } - -/* supplied by libgcc */ -extern void *__canonicalize_funcptr_for_compare(void *); - -/* Field selection types defined by hppa */ -#define rnd(x) (((x)+0x1000)&~0x1fff) -/* lsel: select left 21 bits */ -#define lsel(v,a) (((v)+(a))>>11) -/* rsel: select right 11 bits */ -#define rsel(v,a) (((v)+(a))&0x7ff) -/* lrsel with rounding of addend to nearest 8k */ -#define lrsel(v,a) (((v)+rnd(a))>>11) -/* rrsel with rounding of addend to nearest 8k */ -#define rrsel(v,a) ((((v)+rnd(a))&0x7ff)+((a)-rnd(a))) - -#define mask(x,sz) ((x) & ~((1<<(sz))-1)) - -static inline int reassemble_12(int as12) -{ - return (((as12 & 0x800) >> 11) | - ((as12 & 0x400) >> 8) | - ((as12 & 0x3ff) << 3)); -} - -static inline int reassemble_14(int as14) -{ - return (((as14 & 0x1fff) << 1) | - ((as14 & 0x2000) >> 13)); -} - -static inline int reassemble_17(int as17) -{ - return (((as17 & 0x10000) >> 16) | - ((as17 & 0x0f800) << 5) | - ((as17 & 0x00400) >> 8) | - ((as17 & 0x003ff) << 3)); -} - -static inline int reassemble_21(int as21) -{ - return (((as21 & 0x100000) >> 20) | - ((as21 & 0x0ffe00) >> 8) | - ((as21 & 0x000180) << 7) | - ((as21 & 0x00007c) << 14) | - ((as21 & 0x000003) << 12)); -} - -static inline void hppa_patch21l(uint32_t *insn, int val, int addend) -{ - val = lrsel(val, addend); - *insn = mask(*insn, 21) | reassemble_21(val); -} - -static inline void hppa_patch14r(uint32_t *insn, int val, int addend) -{ - val = rrsel(val, addend); - *insn = mask(*insn, 14) | reassemble_14(val); -} - -static inline void hppa_patch17r(uint32_t *insn, int val, int addend) -{ - val = rrsel(val, addend); - *insn = (*insn & ~0x1f1ffd) | reassemble_17(val); -} - - -static inline void hppa_patch21l_dprel(uint32_t *insn, int val, int addend) -{ - register unsigned int dp asm("r27"); - hppa_patch21l(insn, val - dp, addend); -} - -static inline void hppa_patch14r_dprel(uint32_t *insn, int val, int addend) -{ - register unsigned int dp asm("r27"); - hppa_patch14r(insn, val - dp, addend); -} - -static inline void hppa_patch17f(uint32_t *insn, int val, int addend) -{ - int dot = (int)insn & ~0x3; - int v = ((val + addend) - dot - 8) / 4; - if (v > (1 << 16) || v < -(1 << 16)) { - printf("cannot fit branch to offset %d [%08x->%08x]\n", v, dot, val); - abort(); - } - *insn = (*insn & ~0x1f1ffd) | reassemble_17(v); -} - -static inline void hppa_load_imm21l(uint32_t *insn, int val, int addend) -{ - /* Transform addil L'sym(%dp) to ldil L'val, %r1 */ - *insn = 0x20200000 | reassemble_21(lrsel(val, 0)); -} - -static inline void hppa_load_imm14r(uint32_t *insn, int val, int addend) -{ - /* Transform ldw R'sym(%r1), %rN to ldo R'sym(%r1), %rN */ - hppa_patch14r(insn, val, addend); - /* HACK */ - if (addend == 0) - *insn = (*insn & ~0xfc000000) | (0x0d << 26); -} diff -Nru qemu-kvm-0.12.5+noroms/tcg/i386/tcg-target.c qemu-kvm-0.14.1/tcg/i386/tcg-target.c --- qemu-kvm-0.12.5+noroms/tcg/i386/tcg-target.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/tcg/i386/tcg-target.c 2011-05-11 13:29:46.000000000 +0000 @@ -24,42 +24,83 @@ #ifndef NDEBUG static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { - "%eax", - "%ecx", - "%edx", - "%ebx", - "%esp", - "%ebp", - "%esi", - "%edi", +#if TCG_TARGET_REG_BITS == 64 + "%rax", "%rcx", "%rdx", "%rbx", "%rsp", "%rbp", "%rsi", "%rdi", + "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15", +#else + "%eax", "%ecx", "%edx", "%ebx", "%esp", "%ebp", "%esi", "%edi", +#endif }; #endif static const int tcg_target_reg_alloc_order[] = { - TCG_REG_EAX, - TCG_REG_EDX, - TCG_REG_ECX, +#if TCG_TARGET_REG_BITS == 64 + TCG_REG_RBP, + TCG_REG_RBX, + TCG_REG_R12, + TCG_REG_R13, + TCG_REG_R14, + TCG_REG_R15, + TCG_REG_R10, + TCG_REG_R11, + TCG_REG_R9, + TCG_REG_R8, + TCG_REG_RCX, + TCG_REG_RDX, + TCG_REG_RSI, + TCG_REG_RDI, + TCG_REG_RAX, +#else TCG_REG_EBX, TCG_REG_ESI, TCG_REG_EDI, TCG_REG_EBP, + TCG_REG_ECX, + TCG_REG_EDX, + TCG_REG_EAX, +#endif }; -static const int tcg_target_call_iarg_regs[3] = { TCG_REG_EAX, TCG_REG_EDX, TCG_REG_ECX }; -static const int tcg_target_call_oarg_regs[2] = { TCG_REG_EAX, TCG_REG_EDX }; +static const int tcg_target_call_iarg_regs[] = { +#if TCG_TARGET_REG_BITS == 64 + TCG_REG_RDI, + TCG_REG_RSI, + TCG_REG_RDX, + TCG_REG_RCX, + TCG_REG_R8, + TCG_REG_R9, +#else + TCG_REG_EAX, + TCG_REG_EDX, + TCG_REG_ECX +#endif +}; + +static const int tcg_target_call_oarg_regs[2] = { + TCG_REG_EAX, + TCG_REG_EDX +}; static uint8_t *tb_ret_addr; -static void patch_reloc(uint8_t *code_ptr, int type, +static void patch_reloc(uint8_t *code_ptr, int type, tcg_target_long value, tcg_target_long addend) { value += addend; switch(type) { - case R_386_32: + case R_386_PC32: + value -= (uintptr_t)code_ptr; + if (value != (int32_t)value) { + tcg_abort(); + } *(uint32_t *)code_ptr = value; break; - case R_386_PC32: - *(uint32_t *)code_ptr = value - (long)code_ptr; + case R_386_PC8: + value -= (uintptr_t)code_ptr; + if (value != (int8_t)value) { + tcg_abort(); + } + *(uint8_t *)code_ptr = value; break; default: tcg_abort(); @@ -69,6 +110,10 @@ /* maximum number of register used for input function arguments */ static inline int tcg_target_get_call_iarg_regs_count(int flags) { + if (TCG_TARGET_REG_BITS == 64) { + return 6; + } + flags &= TCG_CALL_TYPE_MASK; switch(flags) { case TCG_CALL_TYPE_STD: @@ -115,20 +160,42 @@ break; case 'q': ct->ct |= TCG_CT_REG; - tcg_regset_set32(ct->u.regs, 0, 0xf); + if (TCG_TARGET_REG_BITS == 64) { + tcg_regset_set32(ct->u.regs, 0, 0xffff); + } else { + tcg_regset_set32(ct->u.regs, 0, 0xf); + } break; case 'r': ct->ct |= TCG_CT_REG; - tcg_regset_set32(ct->u.regs, 0, 0xff); + if (TCG_TARGET_REG_BITS == 64) { + tcg_regset_set32(ct->u.regs, 0, 0xffff); + } else { + tcg_regset_set32(ct->u.regs, 0, 0xff); + } break; /* qemu_ld/st address constraint */ case 'L': ct->ct |= TCG_CT_REG; - tcg_regset_set32(ct->u.regs, 0, 0xff); - tcg_regset_reset_reg(ct->u.regs, TCG_REG_EAX); - tcg_regset_reset_reg(ct->u.regs, TCG_REG_EDX); + if (TCG_TARGET_REG_BITS == 64) { + tcg_regset_set32(ct->u.regs, 0, 0xffff); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_RSI); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_RDI); + } else { + tcg_regset_set32(ct->u.regs, 0, 0xff); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_EAX); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_EDX); + } + break; + + case 'e': + ct->ct |= TCG_CT_CONST_S32; + break; + case 'Z': + ct->ct |= TCG_CT_CONST_U32; break; + default: return -1; } @@ -141,14 +208,83 @@ static inline int tcg_target_const_match(tcg_target_long val, const TCGArgConstraint *arg_ct) { - int ct; - ct = arg_ct->ct; - if (ct & TCG_CT_CONST) + int ct = arg_ct->ct; + if (ct & TCG_CT_CONST) { return 1; - else - return 0; + } + if ((ct & TCG_CT_CONST_S32) && val == (int32_t)val) { + return 1; + } + if ((ct & TCG_CT_CONST_U32) && val == (uint32_t)val) { + return 1; + } + return 0; } +#if TCG_TARGET_REG_BITS == 64 +# define LOWREGMASK(x) ((x) & 7) +#else +# define LOWREGMASK(x) (x) +#endif + +#define P_EXT 0x100 /* 0x0f opcode prefix */ +#define P_DATA16 0x200 /* 0x66 opcode prefix */ +#if TCG_TARGET_REG_BITS == 64 +# define P_ADDR32 0x400 /* 0x67 opcode prefix */ +# define P_REXW 0x800 /* Set REX.W = 1 */ +# define P_REXB_R 0x1000 /* REG field as byte register */ +# define P_REXB_RM 0x2000 /* R/M field as byte register */ +#else +# define P_ADDR32 0 +# define P_REXW 0 +# define P_REXB_R 0 +# define P_REXB_RM 0 +#endif + +#define OPC_ARITH_EvIz (0x81) +#define OPC_ARITH_EvIb (0x83) +#define OPC_ARITH_GvEv (0x03) /* ... plus (ARITH_FOO << 3) */ +#define OPC_ADD_GvEv (OPC_ARITH_GvEv | (ARITH_ADD << 3)) +#define OPC_BSWAP (0xc8 | P_EXT) +#define OPC_CALL_Jz (0xe8) +#define OPC_CMP_GvEv (OPC_ARITH_GvEv | (ARITH_CMP << 3)) +#define OPC_DEC_r32 (0x48) +#define OPC_IMUL_GvEv (0xaf | P_EXT) +#define OPC_IMUL_GvEvIb (0x6b) +#define OPC_IMUL_GvEvIz (0x69) +#define OPC_INC_r32 (0x40) +#define OPC_JCC_long (0x80 | P_EXT) /* ... plus condition code */ +#define OPC_JCC_short (0x70) /* ... plus condition code */ +#define OPC_JMP_long (0xe9) +#define OPC_JMP_short (0xeb) +#define OPC_LEA (0x8d) +#define OPC_MOVB_EvGv (0x88) /* stores, more or less */ +#define OPC_MOVL_EvGv (0x89) /* stores, more or less */ +#define OPC_MOVL_GvEv (0x8b) /* loads, more or less */ +#define OPC_MOVL_EvIz (0xc7) +#define OPC_MOVL_Iv (0xb8) +#define OPC_MOVSBL (0xbe | P_EXT) +#define OPC_MOVSWL (0xbf | P_EXT) +#define OPC_MOVSLQ (0x63 | P_REXW) +#define OPC_MOVZBL (0xb6 | P_EXT) +#define OPC_MOVZWL (0xb7 | P_EXT) +#define OPC_POP_r32 (0x58) +#define OPC_PUSH_r32 (0x50) +#define OPC_PUSH_Iv (0x68) +#define OPC_PUSH_Ib (0x6a) +#define OPC_RET (0xc3) +#define OPC_SETCC (0x90 | P_EXT | P_REXB_RM) /* ... plus cc */ +#define OPC_SHIFT_1 (0xd1) +#define OPC_SHIFT_Ib (0xc1) +#define OPC_SHIFT_cl (0xd3) +#define OPC_TESTL (0x85) +#define OPC_XCHG_ax_r32 (0x90) + +#define OPC_GRP3_Ev (0xf7) +#define OPC_GRP5 (0xff) + +/* Group 1 opcode extensions for 0x80-0x83. + These are also used as modifiers for OPC_ARITH. */ #define ARITH_ADD 0 #define ARITH_OR 1 #define ARITH_ADC 2 @@ -158,12 +294,28 @@ #define ARITH_XOR 6 #define ARITH_CMP 7 +/* Group 2 opcode extensions for 0xc0, 0xc1, 0xd0-0xd3. */ #define SHIFT_ROL 0 #define SHIFT_ROR 1 #define SHIFT_SHL 4 #define SHIFT_SHR 5 #define SHIFT_SAR 7 +/* Group 3 opcode extensions for 0xf6, 0xf7. To be used with OPC_GRP3. */ +#define EXT3_NOT 2 +#define EXT3_NEG 3 +#define EXT3_MUL 4 +#define EXT3_IMUL 5 +#define EXT3_DIV 6 +#define EXT3_IDIV 7 + +/* Group 5 opcode extensions for 0xff. To be used with OPC_GRP5. */ +#define EXT5_INC_Ev 0 +#define EXT5_DEC_Ev 1 +#define EXT5_CALLN_Ev 2 +#define EXT5_JMPN_Ev 4 + +/* Condition codes to be added to OPC_JCC_{long,short}. */ #define JCC_JMP (-1) #define JCC_JO 0x0 #define JCC_JNO 0x1 @@ -182,8 +334,6 @@ #define JCC_JLE 0xe #define JCC_JG 0xf -#define P_EXT 0x100 /* 0x0f opcode prefix */ - static const uint8_t tcg_cond_to_jcc[10] = { [TCG_COND_EQ] = JCC_JE, [TCG_COND_NE] = JCC_JNE, @@ -197,228 +347,614 @@ [TCG_COND_GTU] = JCC_JA, }; -static inline void tcg_out_opc(TCGContext *s, int opc) +#if TCG_TARGET_REG_BITS == 64 +static void tcg_out_opc(TCGContext *s, int opc, int r, int rm, int x) { - if (opc & P_EXT) + int rex; + + if (opc & P_DATA16) { + /* We should never be asking for both 16 and 64-bit operation. */ + assert((opc & P_REXW) == 0); + tcg_out8(s, 0x66); + } + if (opc & P_ADDR32) { + tcg_out8(s, 0x67); + } + + rex = 0; + rex |= (opc & P_REXW) >> 8; /* REX.W */ + rex |= (r & 8) >> 1; /* REX.R */ + rex |= (x & 8) >> 2; /* REX.X */ + rex |= (rm & 8) >> 3; /* REX.B */ + + /* P_REXB_{R,RM} indicates that the given register is the low byte. + For %[abcd]l we need no REX prefix, but for %{si,di,bp,sp}l we do, + as otherwise the encoding indicates %[abcd]h. Note that the values + that are ORed in merely indicate that the REX byte must be present; + those bits get discarded in output. */ + rex |= opc & (r >= 4 ? P_REXB_R : 0); + rex |= opc & (rm >= 4 ? P_REXB_RM : 0); + + if (rex) { + tcg_out8(s, (uint8_t)(rex | 0x40)); + } + + if (opc & P_EXT) { tcg_out8(s, 0x0f); + } tcg_out8(s, opc); } - -static inline void tcg_out_modrm(TCGContext *s, int opc, int r, int rm) +#else +static void tcg_out_opc(TCGContext *s, int opc) { - tcg_out_opc(s, opc); - tcg_out8(s, 0xc0 | (r << 3) | rm); + if (opc & P_DATA16) { + tcg_out8(s, 0x66); + } + if (opc & P_EXT) { + tcg_out8(s, 0x0f); + } + tcg_out8(s, opc); } +/* Discard the register arguments to tcg_out_opc early, so as not to penalize + the 32-bit compilation paths. This method works with all versions of gcc, + whereas relying on optimization may not be able to exclude them. */ +#define tcg_out_opc(s, opc, r, rm, x) (tcg_out_opc)(s, opc) +#endif + +static void tcg_out_modrm(TCGContext *s, int opc, int r, int rm) +{ + tcg_out_opc(s, opc, r, rm, 0); + tcg_out8(s, 0xc0 | (LOWREGMASK(r) << 3) | LOWREGMASK(rm)); +} + +/* Output an opcode with a full "rm + (index<code_ptr + 5 + ~rm; + tcg_target_long disp = offset - pc; + if (disp == (int32_t)disp) { + tcg_out_opc(s, opc, r, 0, 0); + tcg_out8(s, (LOWREGMASK(r) << 3) | 5); + tcg_out32(s, disp); + return; + } -/* rm == -1 means no register index */ -static inline void tcg_out_modrm_offset(TCGContext *s, int opc, int r, int rm, - int32_t offset) -{ - tcg_out_opc(s, opc); - if (rm == -1) { - tcg_out8(s, 0x05 | (r << 3)); - tcg_out32(s, offset); - } else if (offset == 0 && rm != TCG_REG_EBP) { - if (rm == TCG_REG_ESP) { - tcg_out8(s, 0x04 | (r << 3)); - tcg_out8(s, 0x24); - } else { - tcg_out8(s, 0x00 | (r << 3) | rm); - } - } else if ((int8_t)offset == offset) { - if (rm == TCG_REG_ESP) { - tcg_out8(s, 0x44 | (r << 3)); - tcg_out8(s, 0x24); + /* Try for an absolute address encoding. This requires the + use of the MODRM+SIB encoding and is therefore larger than + rip-relative addressing. */ + if (offset == (int32_t)offset) { + tcg_out_opc(s, opc, r, 0, 0); + tcg_out8(s, (LOWREGMASK(r) << 3) | 4); + tcg_out8(s, (4 << 3) | 5); + tcg_out32(s, offset); + return; + } + + /* ??? The memory isn't directly addressable. */ + tcg_abort(); } else { - tcg_out8(s, 0x40 | (r << 3) | rm); + /* Absolute address. */ + tcg_out_opc(s, opc, r, 0, 0); + tcg_out8(s, (r << 3) | 5); + tcg_out32(s, offset); + return; } - tcg_out8(s, offset); + } + + /* Find the length of the immediate addend. Note that the encoding + that would be used for (%ebp) indicates absolute addressing. */ + if (rm < 0) { + mod = 0, len = 4, rm = 5; + } else if (offset == 0 && LOWREGMASK(rm) != TCG_REG_EBP) { + mod = 0, len = 0; + } else if (offset == (int8_t)offset) { + mod = 0x40, len = 1; + } else { + mod = 0x80, len = 4; + } + + /* Use a single byte MODRM format if possible. Note that the encoding + that would be used for %esp is the escape to the two byte form. */ + if (index < 0 && LOWREGMASK(rm) != TCG_REG_ESP) { + /* Single byte MODRM format. */ + tcg_out_opc(s, opc, r, rm, 0); + tcg_out8(s, mod | (LOWREGMASK(r) << 3) | LOWREGMASK(rm)); } else { - if (rm == TCG_REG_ESP) { - tcg_out8(s, 0x84 | (r << 3)); - tcg_out8(s, 0x24); + /* Two byte MODRM+SIB format. */ + + /* Note that the encoding that would place %esp into the index + field indicates no index register. In 64-bit mode, the REX.X + bit counts, so %r12 can be used as the index. */ + if (index < 0) { + index = 4; } else { - tcg_out8(s, 0x80 | (r << 3) | rm); + assert(index != TCG_REG_ESP); } + + tcg_out_opc(s, opc, r, rm, index); + tcg_out8(s, mod | (LOWREGMASK(r) << 3) | 4); + tcg_out8(s, (shift << 6) | (LOWREGMASK(index) << 3) | LOWREGMASK(rm)); + } + + if (len == 1) { + tcg_out8(s, offset); + } else if (len == 4) { tcg_out32(s, offset); } } -static inline void tcg_out_mov(TCGContext *s, int ret, int arg) +/* A simplification of the above with no index or shift. */ +static inline void tcg_out_modrm_offset(TCGContext *s, int opc, int r, + int rm, tcg_target_long offset) +{ + tcg_out_modrm_sib_offset(s, opc, r, rm, -1, 0, offset); +} + +/* Generate dest op= src. Uses the same ARITH_* codes as tgen_arithi. */ +static inline void tgen_arithr(TCGContext *s, int subop, int dest, int src) { - if (arg != ret) - tcg_out_modrm(s, 0x8b, ret, arg); + /* Propagate an opcode prefix, such as P_REXW. */ + int ext = subop & ~0x7; + subop &= 0x7; + + tcg_out_modrm(s, OPC_ARITH_GvEv + (subop << 3) + ext, dest, src); } -static inline void tcg_out_movi(TCGContext *s, TCGType type, - int ret, int32_t arg) +static inline void tcg_out_mov(TCGContext *s, TCGType type, int ret, int arg) +{ + if (arg != ret) { + int opc = OPC_MOVL_GvEv + (type == TCG_TYPE_I64 ? P_REXW : 0); + tcg_out_modrm(s, opc, ret, arg); + } +} + +static void tcg_out_movi(TCGContext *s, TCGType type, + int ret, tcg_target_long arg) { if (arg == 0) { - /* xor r0,r0 */ - tcg_out_modrm(s, 0x01 | (ARITH_XOR << 3), ret, ret); + tgen_arithr(s, ARITH_XOR, ret, ret); + return; + } else if (arg == (uint32_t)arg || type == TCG_TYPE_I32) { + tcg_out_opc(s, OPC_MOVL_Iv + LOWREGMASK(ret), 0, ret, 0); + tcg_out32(s, arg); + } else if (arg == (int32_t)arg) { + tcg_out_modrm(s, OPC_MOVL_EvIz + P_REXW, 0, ret); + tcg_out32(s, arg); } else { - tcg_out8(s, 0xb8 + ret); + tcg_out_opc(s, OPC_MOVL_Iv + P_REXW + LOWREGMASK(ret), 0, ret, 0); tcg_out32(s, arg); + tcg_out32(s, arg >> 31 >> 1); + } +} + +static inline void tcg_out_pushi(TCGContext *s, tcg_target_long val) +{ + if (val == (int8_t)val) { + tcg_out_opc(s, OPC_PUSH_Ib, 0, 0, 0); + tcg_out8(s, val); + } else if (val == (int32_t)val) { + tcg_out_opc(s, OPC_PUSH_Iv, 0, 0, 0); + tcg_out32(s, val); + } else { + tcg_abort(); } } +static inline void tcg_out_push(TCGContext *s, int reg) +{ + tcg_out_opc(s, OPC_PUSH_r32 + LOWREGMASK(reg), 0, reg, 0); +} + +static inline void tcg_out_pop(TCGContext *s, int reg) +{ + tcg_out_opc(s, OPC_POP_r32 + LOWREGMASK(reg), 0, reg, 0); +} + static inline void tcg_out_ld(TCGContext *s, TCGType type, int ret, int arg1, tcg_target_long arg2) { - /* movl */ - tcg_out_modrm_offset(s, 0x8b, ret, arg1, arg2); + int opc = OPC_MOVL_GvEv + (type == TCG_TYPE_I64 ? P_REXW : 0); + tcg_out_modrm_offset(s, opc, ret, arg1, arg2); } static inline void tcg_out_st(TCGContext *s, TCGType type, int arg, int arg1, tcg_target_long arg2) { - /* movl */ - tcg_out_modrm_offset(s, 0x89, arg, arg1, arg2); + int opc = OPC_MOVL_EvGv + (type == TCG_TYPE_I64 ? P_REXW : 0); + tcg_out_modrm_offset(s, opc, arg, arg1, arg2); } -static inline void tgen_arithi(TCGContext *s, int c, int r0, int32_t val, int cf) +static void tcg_out_shifti(TCGContext *s, int subopc, int reg, int count) { - if (!cf && ((c == ARITH_ADD && val == 1) || (c == ARITH_SUB && val == -1))) { - /* inc */ - tcg_out_opc(s, 0x40 + r0); - } else if (!cf && ((c == ARITH_ADD && val == -1) || (c == ARITH_SUB && val == 1))) { - /* dec */ - tcg_out_opc(s, 0x48 + r0); - } else if (val == (int8_t)val) { - tcg_out_modrm(s, 0x83, c, r0); - tcg_out8(s, val); - } else if (c == ARITH_AND && val == 0xffu && r0 < 4) { - /* movzbl */ - tcg_out_modrm(s, 0xb6 | P_EXT, r0, r0); - } else if (c == ARITH_AND && val == 0xffffu) { - /* movzwl */ - tcg_out_modrm(s, 0xb7 | P_EXT, r0, r0); + /* Propagate an opcode prefix, such as P_DATA16. */ + int ext = subopc & ~0x7; + subopc &= 0x7; + + if (count == 1) { + tcg_out_modrm(s, OPC_SHIFT_1 + ext, subopc, reg); } else { - tcg_out_modrm(s, 0x81, c, r0); + tcg_out_modrm(s, OPC_SHIFT_Ib + ext, subopc, reg); + tcg_out8(s, count); + } +} + +static inline void tcg_out_bswap32(TCGContext *s, int reg) +{ + tcg_out_opc(s, OPC_BSWAP + LOWREGMASK(reg), 0, reg, 0); +} + +static inline void tcg_out_rolw_8(TCGContext *s, int reg) +{ + tcg_out_shifti(s, SHIFT_ROL + P_DATA16, reg, 8); +} + +static inline void tcg_out_ext8u(TCGContext *s, int dest, int src) +{ + /* movzbl */ + assert(src < 4 || TCG_TARGET_REG_BITS == 64); + tcg_out_modrm(s, OPC_MOVZBL + P_REXB_RM, dest, src); +} + +static void tcg_out_ext8s(TCGContext *s, int dest, int src, int rexw) +{ + /* movsbl */ + assert(src < 4 || TCG_TARGET_REG_BITS == 64); + tcg_out_modrm(s, OPC_MOVSBL + P_REXB_RM + rexw, dest, src); +} + +static inline void tcg_out_ext16u(TCGContext *s, int dest, int src) +{ + /* movzwl */ + tcg_out_modrm(s, OPC_MOVZWL, dest, src); +} + +static inline void tcg_out_ext16s(TCGContext *s, int dest, int src, int rexw) +{ + /* movsw[lq] */ + tcg_out_modrm(s, OPC_MOVSWL + rexw, dest, src); +} + +static inline void tcg_out_ext32u(TCGContext *s, int dest, int src) +{ + /* 32-bit mov zero extends. */ + tcg_out_modrm(s, OPC_MOVL_GvEv, dest, src); +} + +static inline void tcg_out_ext32s(TCGContext *s, int dest, int src) +{ + tcg_out_modrm(s, OPC_MOVSLQ, dest, src); +} + +static inline void tcg_out_bswap64(TCGContext *s, int reg) +{ + tcg_out_opc(s, OPC_BSWAP + P_REXW + LOWREGMASK(reg), 0, reg, 0); +} + +static void tgen_arithi(TCGContext *s, int c, int r0, + tcg_target_long val, int cf) +{ + int rexw = 0; + + if (TCG_TARGET_REG_BITS == 64) { + rexw = c & -8; + c &= 7; + } + + /* ??? While INC is 2 bytes shorter than ADDL $1, they also induce + partial flags update stalls on Pentium4 and are not recommended + by current Intel optimization manuals. */ + if (!cf && (c == ARITH_ADD || c == ARITH_SUB) && (val == 1 || val == -1)) { + int is_inc = (c == ARITH_ADD) ^ (val < 0); + if (TCG_TARGET_REG_BITS == 64) { + /* The single-byte increment encodings are re-tasked as the + REX prefixes. Use the MODRM encoding. */ + tcg_out_modrm(s, OPC_GRP5 + rexw, + (is_inc ? EXT5_INC_Ev : EXT5_DEC_Ev), r0); + } else { + tcg_out8(s, (is_inc ? OPC_INC_r32 : OPC_DEC_r32) + r0); + } + return; + } + + if (c == ARITH_AND) { + if (TCG_TARGET_REG_BITS == 64) { + if (val == 0xffffffffu) { + tcg_out_ext32u(s, r0, r0); + return; + } + if (val == (uint32_t)val) { + /* AND with no high bits set can use a 32-bit operation. */ + rexw = 0; + } + } + if (val == 0xffu && (r0 < 4 || TCG_TARGET_REG_BITS == 64)) { + tcg_out_ext8u(s, r0, r0); + return; + } + if (val == 0xffffu) { + tcg_out_ext16u(s, r0, r0); + return; + } + } + + if (val == (int8_t)val) { + tcg_out_modrm(s, OPC_ARITH_EvIb + rexw, c, r0); + tcg_out8(s, val); + return; + } + if (rexw == 0 || val == (int32_t)val) { + tcg_out_modrm(s, OPC_ARITH_EvIz + rexw, c, r0); tcg_out32(s, val); + return; } + + tcg_abort(); } static void tcg_out_addi(TCGContext *s, int reg, tcg_target_long val) { - if (val != 0) - tgen_arithi(s, ARITH_ADD, reg, val, 0); + if (val != 0) { + tgen_arithi(s, ARITH_ADD + P_REXW, reg, val, 0); + } } -static void tcg_out_jxx(TCGContext *s, int opc, int label_index) +/* Use SMALL != 0 to force a short forward branch. */ +static void tcg_out_jxx(TCGContext *s, int opc, int label_index, int small) { int32_t val, val1; TCGLabel *l = &s->labels[label_index]; - + if (l->has_value) { val = l->u.value - (tcg_target_long)s->code_ptr; val1 = val - 2; if ((int8_t)val1 == val1) { - if (opc == -1) - tcg_out8(s, 0xeb); - else - tcg_out8(s, 0x70 + opc); + if (opc == -1) { + tcg_out8(s, OPC_JMP_short); + } else { + tcg_out8(s, OPC_JCC_short + opc); + } tcg_out8(s, val1); } else { + if (small) { + tcg_abort(); + } if (opc == -1) { - tcg_out8(s, 0xe9); + tcg_out8(s, OPC_JMP_long); tcg_out32(s, val - 5); } else { - tcg_out8(s, 0x0f); - tcg_out8(s, 0x80 + opc); + tcg_out_opc(s, OPC_JCC_long + opc, 0, 0, 0); tcg_out32(s, val - 6); } } + } else if (small) { + if (opc == -1) { + tcg_out8(s, OPC_JMP_short); + } else { + tcg_out8(s, OPC_JCC_short + opc); + } + tcg_out_reloc(s, s->code_ptr, R_386_PC8, label_index, -1); + s->code_ptr += 1; } else { if (opc == -1) { - tcg_out8(s, 0xe9); + tcg_out8(s, OPC_JMP_long); } else { - tcg_out8(s, 0x0f); - tcg_out8(s, 0x80 + opc); + tcg_out_opc(s, OPC_JCC_long + opc, 0, 0, 0); } tcg_out_reloc(s, s->code_ptr, R_386_PC32, label_index, -4); s->code_ptr += 4; } } -static void tcg_out_brcond(TCGContext *s, int cond, - TCGArg arg1, TCGArg arg2, int const_arg2, - int label_index) +static void tcg_out_cmp(TCGContext *s, TCGArg arg1, TCGArg arg2, + int const_arg2, int rexw) { if (const_arg2) { if (arg2 == 0) { /* test r, r */ - tcg_out_modrm(s, 0x85, arg1, arg1); + tcg_out_modrm(s, OPC_TESTL + rexw, arg1, arg1); } else { - tgen_arithi(s, ARITH_CMP, arg1, arg2, 0); + tgen_arithi(s, ARITH_CMP + rexw, arg1, arg2, 0); } } else { - tcg_out_modrm(s, 0x01 | (ARITH_CMP << 3), arg2, arg1); + tgen_arithr(s, ARITH_CMP + rexw, arg1, arg2); } - tcg_out_jxx(s, tcg_cond_to_jcc[cond], label_index); } +static void tcg_out_brcond32(TCGContext *s, TCGCond cond, + TCGArg arg1, TCGArg arg2, int const_arg2, + int label_index, int small) +{ + tcg_out_cmp(s, arg1, arg2, const_arg2, 0); + tcg_out_jxx(s, tcg_cond_to_jcc[cond], label_index, small); +} + +#if TCG_TARGET_REG_BITS == 64 +static void tcg_out_brcond64(TCGContext *s, TCGCond cond, + TCGArg arg1, TCGArg arg2, int const_arg2, + int label_index, int small) +{ + tcg_out_cmp(s, arg1, arg2, const_arg2, P_REXW); + tcg_out_jxx(s, tcg_cond_to_jcc[cond], label_index, small); +} +#else /* XXX: we implement it at the target level to avoid having to handle cross basic blocks temporaries */ -static void tcg_out_brcond2(TCGContext *s, - const TCGArg *args, const int *const_args) +static void tcg_out_brcond2(TCGContext *s, const TCGArg *args, + const int *const_args, int small) { int label_next; label_next = gen_new_label(); switch(args[4]) { case TCG_COND_EQ: - tcg_out_brcond(s, TCG_COND_NE, args[0], args[2], const_args[2], label_next); - tcg_out_brcond(s, TCG_COND_EQ, args[1], args[3], const_args[3], args[5]); + tcg_out_brcond32(s, TCG_COND_NE, args[0], args[2], const_args[2], + label_next, 1); + tcg_out_brcond32(s, TCG_COND_EQ, args[1], args[3], const_args[3], + args[5], small); break; case TCG_COND_NE: - tcg_out_brcond(s, TCG_COND_NE, args[0], args[2], const_args[2], args[5]); - tcg_out_brcond(s, TCG_COND_NE, args[1], args[3], const_args[3], args[5]); + tcg_out_brcond32(s, TCG_COND_NE, args[0], args[2], const_args[2], + args[5], small); + tcg_out_brcond32(s, TCG_COND_NE, args[1], args[3], const_args[3], + args[5], small); break; case TCG_COND_LT: - tcg_out_brcond(s, TCG_COND_LT, args[1], args[3], const_args[3], args[5]); - tcg_out_jxx(s, JCC_JNE, label_next); - tcg_out_brcond(s, TCG_COND_LTU, args[0], args[2], const_args[2], args[5]); + tcg_out_brcond32(s, TCG_COND_LT, args[1], args[3], const_args[3], + args[5], small); + tcg_out_jxx(s, JCC_JNE, label_next, 1); + tcg_out_brcond32(s, TCG_COND_LTU, args[0], args[2], const_args[2], + args[5], small); break; case TCG_COND_LE: - tcg_out_brcond(s, TCG_COND_LT, args[1], args[3], const_args[3], args[5]); - tcg_out_jxx(s, JCC_JNE, label_next); - tcg_out_brcond(s, TCG_COND_LEU, args[0], args[2], const_args[2], args[5]); + tcg_out_brcond32(s, TCG_COND_LT, args[1], args[3], const_args[3], + args[5], small); + tcg_out_jxx(s, JCC_JNE, label_next, 1); + tcg_out_brcond32(s, TCG_COND_LEU, args[0], args[2], const_args[2], + args[5], small); break; case TCG_COND_GT: - tcg_out_brcond(s, TCG_COND_GT, args[1], args[3], const_args[3], args[5]); - tcg_out_jxx(s, JCC_JNE, label_next); - tcg_out_brcond(s, TCG_COND_GTU, args[0], args[2], const_args[2], args[5]); + tcg_out_brcond32(s, TCG_COND_GT, args[1], args[3], const_args[3], + args[5], small); + tcg_out_jxx(s, JCC_JNE, label_next, 1); + tcg_out_brcond32(s, TCG_COND_GTU, args[0], args[2], const_args[2], + args[5], small); break; case TCG_COND_GE: - tcg_out_brcond(s, TCG_COND_GT, args[1], args[3], const_args[3], args[5]); - tcg_out_jxx(s, JCC_JNE, label_next); - tcg_out_brcond(s, TCG_COND_GEU, args[0], args[2], const_args[2], args[5]); + tcg_out_brcond32(s, TCG_COND_GT, args[1], args[3], const_args[3], + args[5], small); + tcg_out_jxx(s, JCC_JNE, label_next, 1); + tcg_out_brcond32(s, TCG_COND_GEU, args[0], args[2], const_args[2], + args[5], small); break; case TCG_COND_LTU: - tcg_out_brcond(s, TCG_COND_LTU, args[1], args[3], const_args[3], args[5]); - tcg_out_jxx(s, JCC_JNE, label_next); - tcg_out_brcond(s, TCG_COND_LTU, args[0], args[2], const_args[2], args[5]); + tcg_out_brcond32(s, TCG_COND_LTU, args[1], args[3], const_args[3], + args[5], small); + tcg_out_jxx(s, JCC_JNE, label_next, 1); + tcg_out_brcond32(s, TCG_COND_LTU, args[0], args[2], const_args[2], + args[5], small); break; case TCG_COND_LEU: - tcg_out_brcond(s, TCG_COND_LTU, args[1], args[3], const_args[3], args[5]); - tcg_out_jxx(s, JCC_JNE, label_next); - tcg_out_brcond(s, TCG_COND_LEU, args[0], args[2], const_args[2], args[5]); + tcg_out_brcond32(s, TCG_COND_LTU, args[1], args[3], const_args[3], + args[5], small); + tcg_out_jxx(s, JCC_JNE, label_next, 1); + tcg_out_brcond32(s, TCG_COND_LEU, args[0], args[2], const_args[2], + args[5], small); break; case TCG_COND_GTU: - tcg_out_brcond(s, TCG_COND_GTU, args[1], args[3], const_args[3], args[5]); - tcg_out_jxx(s, JCC_JNE, label_next); - tcg_out_brcond(s, TCG_COND_GTU, args[0], args[2], const_args[2], args[5]); + tcg_out_brcond32(s, TCG_COND_GTU, args[1], args[3], const_args[3], + args[5], small); + tcg_out_jxx(s, JCC_JNE, label_next, 1); + tcg_out_brcond32(s, TCG_COND_GTU, args[0], args[2], const_args[2], + args[5], small); break; case TCG_COND_GEU: - tcg_out_brcond(s, TCG_COND_GTU, args[1], args[3], const_args[3], args[5]); - tcg_out_jxx(s, JCC_JNE, label_next); - tcg_out_brcond(s, TCG_COND_GEU, args[0], args[2], const_args[2], args[5]); + tcg_out_brcond32(s, TCG_COND_GTU, args[1], args[3], const_args[3], + args[5], small); + tcg_out_jxx(s, JCC_JNE, label_next, 1); + tcg_out_brcond32(s, TCG_COND_GEU, args[0], args[2], const_args[2], + args[5], small); break; default: tcg_abort(); } tcg_out_label(s, label_next, (tcg_target_long)s->code_ptr); } +#endif + +static void tcg_out_setcond32(TCGContext *s, TCGCond cond, TCGArg dest, + TCGArg arg1, TCGArg arg2, int const_arg2) +{ + tcg_out_cmp(s, arg1, arg2, const_arg2, 0); + tcg_out_modrm(s, OPC_SETCC | tcg_cond_to_jcc[cond], 0, dest); + tcg_out_ext8u(s, dest, dest); +} + +#if TCG_TARGET_REG_BITS == 64 +static void tcg_out_setcond64(TCGContext *s, TCGCond cond, TCGArg dest, + TCGArg arg1, TCGArg arg2, int const_arg2) +{ + tcg_out_cmp(s, arg1, arg2, const_arg2, P_REXW); + tcg_out_modrm(s, OPC_SETCC | tcg_cond_to_jcc[cond], 0, dest); + tcg_out_ext8u(s, dest, dest); +} +#else +static void tcg_out_setcond2(TCGContext *s, const TCGArg *args, + const int *const_args) +{ + TCGArg new_args[6]; + int label_true, label_over; + + memcpy(new_args, args+1, 5*sizeof(TCGArg)); + + if (args[0] == args[1] || args[0] == args[2] + || (!const_args[3] && args[0] == args[3]) + || (!const_args[4] && args[0] == args[4])) { + /* When the destination overlaps with one of the argument + registers, don't do anything tricky. */ + label_true = gen_new_label(); + label_over = gen_new_label(); + + new_args[5] = label_true; + tcg_out_brcond2(s, new_args, const_args+1, 1); + + tcg_out_movi(s, TCG_TYPE_I32, args[0], 0); + tcg_out_jxx(s, JCC_JMP, label_over, 1); + tcg_out_label(s, label_true, (tcg_target_long)s->code_ptr); + + tcg_out_movi(s, TCG_TYPE_I32, args[0], 1); + tcg_out_label(s, label_over, (tcg_target_long)s->code_ptr); + } else { + /* When the destination does not overlap one of the arguments, + clear the destination first, jump if cond false, and emit an + increment in the true case. This results in smaller code. */ + + tcg_out_movi(s, TCG_TYPE_I32, args[0], 0); + + label_over = gen_new_label(); + new_args[4] = tcg_invert_cond(new_args[4]); + new_args[5] = label_over; + tcg_out_brcond2(s, new_args, const_args+1, 1); + + tgen_arithi(s, ARITH_ADD, args[0], 1, 0); + tcg_out_label(s, label_over, (tcg_target_long)s->code_ptr); + } +} +#endif + +static void tcg_out_branch(TCGContext *s, int call, tcg_target_long dest) +{ + tcg_target_long disp = dest - (tcg_target_long)s->code_ptr - 5; + + if (disp == (int32_t)disp) { + tcg_out_opc(s, call ? OPC_CALL_Jz : OPC_JMP_long, 0, 0, 0); + tcg_out32(s, disp); + } else { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R10, dest); + tcg_out_modrm(s, OPC_GRP5, + call ? EXT5_CALLN_Ev : EXT5_JMPN_Ev, TCG_REG_R10); + } +} + +static inline void tcg_out_calli(TCGContext *s, tcg_target_long dest) +{ + tcg_out_branch(s, 1, dest); +} + +static void tcg_out_jmp(TCGContext *s, tcg_target_long dest) +{ + tcg_out_branch(s, 0, dest); +} #if defined(CONFIG_SOFTMMU) @@ -437,11 +973,164 @@ __stl_mmu, __stq_mmu, }; + +/* Perform the TLB load and compare. + + Inputs: + ADDRLO_IDX contains the index into ARGS of the low part of the + address; the high part of the address is at ADDR_LOW_IDX+1. + + MEM_INDEX and S_BITS are the memory context and log2 size of the load. + + WHICH is the offset into the CPUTLBEntry structure of the slot to read. + This should be offsetof addr_read or addr_write. + + Outputs: + LABEL_PTRS is filled with 1 (32-bit addresses) or 2 (64-bit addresses) + positions of the displacements of forward jumps to the TLB miss case. + + First argument register is loaded with the low part of the address. + In the TLB hit case, it has been adjusted as indicated by the TLB + and so is a host address. In the TLB miss case, it continues to + hold a guest address. + + Second argument register is clobbered. */ + +static inline void tcg_out_tlb_load(TCGContext *s, int addrlo_idx, + int mem_index, int s_bits, + const TCGArg *args, + uint8_t **label_ptr, int which) +{ + const int addrlo = args[addrlo_idx]; + const int r0 = tcg_target_call_iarg_regs[0]; + const int r1 = tcg_target_call_iarg_regs[1]; + TCGType type = TCG_TYPE_I32; + int rexw = 0; + + if (TCG_TARGET_REG_BITS == 64 && TARGET_LONG_BITS == 64) { + type = TCG_TYPE_I64; + rexw = P_REXW; + } + + tcg_out_mov(s, type, r1, addrlo); + tcg_out_mov(s, type, r0, addrlo); + + tcg_out_shifti(s, SHIFT_SHR + rexw, r1, + TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS); + + tgen_arithi(s, ARITH_AND + rexw, r0, + TARGET_PAGE_MASK | ((1 << s_bits) - 1), 0); + tgen_arithi(s, ARITH_AND + rexw, r1, + (CPU_TLB_SIZE - 1) << CPU_TLB_ENTRY_BITS, 0); + + tcg_out_modrm_sib_offset(s, OPC_LEA + P_REXW, r1, TCG_AREG0, r1, 0, + offsetof(CPUState, tlb_table[mem_index][0]) + + which); + + /* cmp 0(r1), r0 */ + tcg_out_modrm_offset(s, OPC_CMP_GvEv + rexw, r0, r1, 0); + + tcg_out_mov(s, type, r0, addrlo); + + /* jne label1 */ + tcg_out8(s, OPC_JCC_short + JCC_JNE); + label_ptr[0] = s->code_ptr; + s->code_ptr++; + + if (TARGET_LONG_BITS > TCG_TARGET_REG_BITS) { + /* cmp 4(r1), addrhi */ + tcg_out_modrm_offset(s, OPC_CMP_GvEv, args[addrlo_idx+1], r1, 4); + + /* jne label1 */ + tcg_out8(s, OPC_JCC_short + JCC_JNE); + label_ptr[1] = s->code_ptr; + s->code_ptr++; + } + + /* TLB Hit. */ + + /* add addend(r1), r0 */ + tcg_out_modrm_offset(s, OPC_ADD_GvEv + P_REXW, r0, r1, + offsetof(CPUTLBEntry, addend) - which); +} #endif -#ifndef CONFIG_USER_ONLY -#define GUEST_BASE 0 +static void tcg_out_qemu_ld_direct(TCGContext *s, int datalo, int datahi, + int base, tcg_target_long ofs, int sizeop) +{ +#ifdef TARGET_WORDS_BIGENDIAN + const int bswap = 1; +#else + const int bswap = 0; #endif + switch (sizeop) { + case 0: + tcg_out_modrm_offset(s, OPC_MOVZBL, datalo, base, ofs); + break; + case 0 | 4: + tcg_out_modrm_offset(s, OPC_MOVSBL + P_REXW, datalo, base, ofs); + break; + case 1: + tcg_out_modrm_offset(s, OPC_MOVZWL, datalo, base, ofs); + if (bswap) { + tcg_out_rolw_8(s, datalo); + } + break; + case 1 | 4: + if (bswap) { + tcg_out_modrm_offset(s, OPC_MOVZWL, datalo, base, ofs); + tcg_out_rolw_8(s, datalo); + tcg_out_modrm(s, OPC_MOVSWL + P_REXW, datalo, datalo); + } else { + tcg_out_modrm_offset(s, OPC_MOVSWL + P_REXW, datalo, base, ofs); + } + break; + case 2: + tcg_out_ld(s, TCG_TYPE_I32, datalo, base, ofs); + if (bswap) { + tcg_out_bswap32(s, datalo); + } + break; +#if TCG_TARGET_REG_BITS == 64 + case 2 | 4: + if (bswap) { + tcg_out_ld(s, TCG_TYPE_I32, datalo, base, ofs); + tcg_out_bswap32(s, datalo); + tcg_out_ext32s(s, datalo, datalo); + } else { + tcg_out_modrm_offset(s, OPC_MOVSLQ, datalo, base, ofs); + } + break; +#endif + case 3: + if (TCG_TARGET_REG_BITS == 64) { + tcg_out_ld(s, TCG_TYPE_I64, datalo, base, ofs); + if (bswap) { + tcg_out_bswap64(s, datalo); + } + } else { + if (bswap) { + int t = datalo; + datalo = datahi; + datahi = t; + } + if (base != datalo) { + tcg_out_ld(s, TCG_TYPE_I32, datalo, base, ofs); + tcg_out_ld(s, TCG_TYPE_I32, datahi, base, ofs + 4); + } else { + tcg_out_ld(s, TCG_TYPE_I32, datahi, base, ofs + 4); + tcg_out_ld(s, TCG_TYPE_I32, datalo, base, ofs); + } + if (bswap) { + tcg_out_bswap32(s, datalo); + tcg_out_bswap32(s, datahi); + } + } + break; + default: + tcg_abort(); + } +} /* XXX: qemu_ld and qemu_st could be modified to clobber only EDX and EAX. It will be useful once fixed registers globals are less @@ -449,635 +1138,505 @@ static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, int opc) { - int addr_reg, data_reg, data_reg2, r0, r1, mem_index, s_bits, bswap; -#if defined(CONFIG_SOFTMMU) - uint8_t *label1_ptr, *label2_ptr; -#endif -#if TARGET_LONG_BITS == 64 + int data_reg, data_reg2 = 0; + int addrlo_idx; #if defined(CONFIG_SOFTMMU) - uint8_t *label3_ptr; + int mem_index, s_bits, arg_idx; + uint8_t *label_ptr[3]; #endif - int addr_reg2; -#endif - - data_reg = *args++; - if (opc == 3) - data_reg2 = *args++; - else - data_reg2 = 0; - addr_reg = *args++; -#if TARGET_LONG_BITS == 64 - addr_reg2 = *args++; -#endif - mem_index = *args; - s_bits = opc & 3; - r0 = TCG_REG_EAX; - r1 = TCG_REG_EDX; + data_reg = args[0]; + addrlo_idx = 1; + if (TCG_TARGET_REG_BITS == 32 && opc == 3) { + data_reg2 = args[1]; + addrlo_idx = 2; + } #if defined(CONFIG_SOFTMMU) - tcg_out_mov(s, r1, addr_reg); + mem_index = args[addrlo_idx + 1 + (TARGET_LONG_BITS > TCG_TARGET_REG_BITS)]; + s_bits = opc & 3; - tcg_out_mov(s, r0, addr_reg); - - tcg_out_modrm(s, 0xc1, 5, r1); /* shr $x, r1 */ - tcg_out8(s, TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS); - - tcg_out_modrm(s, 0x81, 4, r0); /* andl $x, r0 */ - tcg_out32(s, TARGET_PAGE_MASK | ((1 << s_bits) - 1)); - - tcg_out_modrm(s, 0x81, 4, r1); /* andl $x, r1 */ - tcg_out32(s, (CPU_TLB_SIZE - 1) << CPU_TLB_ENTRY_BITS); - - tcg_out_opc(s, 0x8d); /* lea offset(r1, %ebp), r1 */ - tcg_out8(s, 0x80 | (r1 << 3) | 0x04); - tcg_out8(s, (5 << 3) | r1); - tcg_out32(s, offsetof(CPUState, tlb_table[mem_index][0].addr_read)); + tcg_out_tlb_load(s, addrlo_idx, mem_index, s_bits, args, + label_ptr, offsetof(CPUTLBEntry, addr_read)); - /* cmp 0(r1), r0 */ - tcg_out_modrm_offset(s, 0x3b, r0, r1, 0); - - tcg_out_mov(s, r0, addr_reg); - -#if TARGET_LONG_BITS == 32 - /* je label1 */ - tcg_out8(s, 0x70 + JCC_JE); - label1_ptr = s->code_ptr; - s->code_ptr++; -#else - /* jne label3 */ - tcg_out8(s, 0x70 + JCC_JNE); - label3_ptr = s->code_ptr; - s->code_ptr++; - - /* cmp 4(r1), addr_reg2 */ - tcg_out_modrm_offset(s, 0x3b, addr_reg2, r1, 4); - - /* je label1 */ - tcg_out8(s, 0x70 + JCC_JE); - label1_ptr = s->code_ptr; + /* TLB Hit. */ + tcg_out_qemu_ld_direct(s, data_reg, data_reg2, + tcg_target_call_iarg_regs[0], 0, opc); + + /* jmp label2 */ + tcg_out8(s, OPC_JMP_short); + label_ptr[2] = s->code_ptr; s->code_ptr++; - - /* label3: */ - *label3_ptr = s->code_ptr - label3_ptr - 1; -#endif + + /* TLB Miss. */ + + /* label1: */ + *label_ptr[0] = s->code_ptr - label_ptr[0] - 1; + if (TARGET_LONG_BITS > TCG_TARGET_REG_BITS) { + *label_ptr[1] = s->code_ptr - label_ptr[1] - 1; + } /* XXX: move that code at the end of the TB */ -#if TARGET_LONG_BITS == 32 - tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_EDX, mem_index); -#else - tcg_out_mov(s, TCG_REG_EDX, addr_reg2); - tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_ECX, mem_index); -#endif - tcg_out8(s, 0xe8); - tcg_out32(s, (tcg_target_long)qemu_ld_helpers[s_bits] - - (tcg_target_long)s->code_ptr - 4); + /* The first argument is already loaded with addrlo. */ + arg_idx = 1; + if (TCG_TARGET_REG_BITS == 32 && TARGET_LONG_BITS == 64) { + tcg_out_mov(s, TCG_TYPE_I32, tcg_target_call_iarg_regs[arg_idx++], + args[addrlo_idx + 1]); + } + tcg_out_movi(s, TCG_TYPE_I32, tcg_target_call_iarg_regs[arg_idx], + mem_index); + tcg_out_calli(s, (tcg_target_long)qemu_ld_helpers[s_bits]); switch(opc) { case 0 | 4: - /* movsbl */ - tcg_out_modrm(s, 0xbe | P_EXT, data_reg, TCG_REG_EAX); + tcg_out_ext8s(s, data_reg, TCG_REG_EAX, P_REXW); break; case 1 | 4: - /* movswl */ - tcg_out_modrm(s, 0xbf | P_EXT, data_reg, TCG_REG_EAX); + tcg_out_ext16s(s, data_reg, TCG_REG_EAX, P_REXW); break; case 0: - /* movzbl */ - tcg_out_modrm(s, 0xb6 | P_EXT, data_reg, TCG_REG_EAX); + tcg_out_ext8u(s, data_reg, TCG_REG_EAX); break; case 1: - /* movzwl */ - tcg_out_modrm(s, 0xb7 | P_EXT, data_reg, TCG_REG_EAX); + tcg_out_ext16u(s, data_reg, TCG_REG_EAX); break; case 2: - default: - tcg_out_mov(s, data_reg, TCG_REG_EAX); + tcg_out_mov(s, TCG_TYPE_I32, data_reg, TCG_REG_EAX); + break; +#if TCG_TARGET_REG_BITS == 64 + case 2 | 4: + tcg_out_ext32s(s, data_reg, TCG_REG_EAX); break; +#endif case 3: - if (data_reg == TCG_REG_EDX) { - tcg_out_opc(s, 0x90 + TCG_REG_EDX); /* xchg %edx, %eax */ - tcg_out_mov(s, data_reg2, TCG_REG_EAX); + if (TCG_TARGET_REG_BITS == 64) { + tcg_out_mov(s, TCG_TYPE_I64, data_reg, TCG_REG_RAX); + } else if (data_reg == TCG_REG_EDX) { + /* xchg %edx, %eax */ + tcg_out_opc(s, OPC_XCHG_ax_r32 + TCG_REG_EDX, 0, 0, 0); + tcg_out_mov(s, TCG_TYPE_I32, data_reg2, TCG_REG_EAX); } else { - tcg_out_mov(s, data_reg, TCG_REG_EAX); - tcg_out_mov(s, data_reg2, TCG_REG_EDX); + tcg_out_mov(s, TCG_TYPE_I32, data_reg, TCG_REG_EAX); + tcg_out_mov(s, TCG_TYPE_I32, data_reg2, TCG_REG_EDX); } break; + default: + tcg_abort(); } - /* jmp label2 */ - tcg_out8(s, 0xeb); - label2_ptr = s->code_ptr; - s->code_ptr++; - - /* label1: */ - *label1_ptr = s->code_ptr - label1_ptr - 1; - - /* add x(r1), r0 */ - tcg_out_modrm_offset(s, 0x03, r0, r1, offsetof(CPUTLBEntry, addend) - - offsetof(CPUTLBEntry, addr_read)); + /* label2: */ + *label_ptr[2] = s->code_ptr - label_ptr[2] - 1; #else - r0 = addr_reg; + { + int32_t offset = GUEST_BASE; + int base = args[addrlo_idx]; + + if (TCG_TARGET_REG_BITS == 64) { + /* ??? We assume all operations have left us with register + contents that are zero extended. So far this appears to + be true. If we want to enforce this, we can either do + an explicit zero-extension here, or (if GUEST_BASE == 0) + use the ADDR32 prefix. For now, do nothing. */ + + if (offset != GUEST_BASE) { + tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_RDI, GUEST_BASE); + tgen_arithr(s, ARITH_ADD + P_REXW, TCG_REG_RDI, base); + base = TCG_REG_RDI, offset = 0; + } + } + + tcg_out_qemu_ld_direct(s, data_reg, data_reg2, base, offset, opc); + } #endif +} +static void tcg_out_qemu_st_direct(TCGContext *s, int datalo, int datahi, + int base, tcg_target_long ofs, int sizeop) +{ #ifdef TARGET_WORDS_BIGENDIAN - bswap = 1; + const int bswap = 1; #else - bswap = 0; + const int bswap = 0; #endif - switch(opc) { + /* ??? Ideally we wouldn't need a scratch register. For user-only, + we could perform the bswap twice to restore the original value + instead of moving to the scratch. But as it is, the L constraint + means that the second argument reg is definitely free here. */ + int scratch = tcg_target_call_iarg_regs[1]; + + switch (sizeop) { case 0: - /* movzbl */ - tcg_out_modrm_offset(s, 0xb6 | P_EXT, data_reg, r0, GUEST_BASE); - break; - case 0 | 4: - /* movsbl */ - tcg_out_modrm_offset(s, 0xbe | P_EXT, data_reg, r0, GUEST_BASE); + tcg_out_modrm_offset(s, OPC_MOVB_EvGv + P_REXB_R, datalo, base, ofs); break; case 1: - /* movzwl */ - tcg_out_modrm_offset(s, 0xb7 | P_EXT, data_reg, r0, GUEST_BASE); - if (bswap) { - /* rolw $8, data_reg */ - tcg_out8(s, 0x66); - tcg_out_modrm(s, 0xc1, 0, data_reg); - tcg_out8(s, 8); - } - break; - case 1 | 4: - /* movswl */ - tcg_out_modrm_offset(s, 0xbf | P_EXT, data_reg, r0, GUEST_BASE); if (bswap) { - /* rolw $8, data_reg */ - tcg_out8(s, 0x66); - tcg_out_modrm(s, 0xc1, 0, data_reg); - tcg_out8(s, 8); - - /* movswl data_reg, data_reg */ - tcg_out_modrm(s, 0xbf | P_EXT, data_reg, data_reg); + tcg_out_mov(s, TCG_TYPE_I32, scratch, datalo); + tcg_out_rolw_8(s, scratch); + datalo = scratch; } + tcg_out_modrm_offset(s, OPC_MOVL_EvGv + P_DATA16, datalo, base, ofs); break; case 2: - /* movl (r0), data_reg */ - tcg_out_modrm_offset(s, 0x8b, data_reg, r0, GUEST_BASE); if (bswap) { - /* bswap */ - tcg_out_opc(s, (0xc8 + data_reg) | P_EXT); + tcg_out_mov(s, TCG_TYPE_I32, scratch, datalo); + tcg_out_bswap32(s, scratch); + datalo = scratch; } + tcg_out_st(s, TCG_TYPE_I32, datalo, base, ofs); break; case 3: - /* XXX: could be nicer */ - if (r0 == data_reg) { - r1 = TCG_REG_EDX; - if (r1 == data_reg) - r1 = TCG_REG_EAX; - tcg_out_mov(s, r1, r0); - r0 = r1; - } - if (!bswap) { - tcg_out_modrm_offset(s, 0x8b, data_reg, r0, GUEST_BASE); - tcg_out_modrm_offset(s, 0x8b, data_reg2, r0, GUEST_BASE + 4); - } else { - tcg_out_modrm_offset(s, 0x8b, data_reg, r0, GUEST_BASE + 4); - tcg_out_opc(s, (0xc8 + data_reg) | P_EXT); - - tcg_out_modrm_offset(s, 0x8b, data_reg2, r0, GUEST_BASE); - /* bswap */ - tcg_out_opc(s, (0xc8 + data_reg2) | P_EXT); + if (TCG_TARGET_REG_BITS == 64) { + if (bswap) { + tcg_out_mov(s, TCG_TYPE_I64, scratch, datalo); + tcg_out_bswap64(s, scratch); + datalo = scratch; + } + tcg_out_st(s, TCG_TYPE_I64, datalo, base, ofs); + } else if (bswap) { + tcg_out_mov(s, TCG_TYPE_I32, scratch, datahi); + tcg_out_bswap32(s, scratch); + tcg_out_st(s, TCG_TYPE_I32, scratch, base, ofs); + tcg_out_mov(s, TCG_TYPE_I32, scratch, datalo); + tcg_out_bswap32(s, scratch); + tcg_out_st(s, TCG_TYPE_I32, scratch, base, ofs + 4); + } else { + tcg_out_st(s, TCG_TYPE_I32, datalo, base, ofs); + tcg_out_st(s, TCG_TYPE_I32, datahi, base, ofs + 4); } break; default: tcg_abort(); } - -#if defined(CONFIG_SOFTMMU) - /* label2: */ - *label2_ptr = s->code_ptr - label2_ptr - 1; -#endif } - static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, int opc) { - int addr_reg, data_reg, data_reg2, r0, r1, mem_index, s_bits, bswap; -#if defined(CONFIG_SOFTMMU) - uint8_t *label1_ptr, *label2_ptr; -#endif -#if TARGET_LONG_BITS == 64 + int data_reg, data_reg2 = 0; + int addrlo_idx; #if defined(CONFIG_SOFTMMU) - uint8_t *label3_ptr; -#endif - int addr_reg2; + int mem_index, s_bits; + int stack_adjust; + uint8_t *label_ptr[3]; #endif - data_reg = *args++; - if (opc == 3) - data_reg2 = *args++; - else - data_reg2 = 0; - addr_reg = *args++; -#if TARGET_LONG_BITS == 64 - addr_reg2 = *args++; -#endif - mem_index = *args; + data_reg = args[0]; + addrlo_idx = 1; + if (TCG_TARGET_REG_BITS == 32 && opc == 3) { + data_reg2 = args[1]; + addrlo_idx = 2; + } +#if defined(CONFIG_SOFTMMU) + mem_index = args[addrlo_idx + 1 + (TARGET_LONG_BITS > TCG_TARGET_REG_BITS)]; s_bits = opc; - r0 = TCG_REG_EAX; - r1 = TCG_REG_EDX; + tcg_out_tlb_load(s, addrlo_idx, mem_index, s_bits, args, + label_ptr, offsetof(CPUTLBEntry, addr_write)); -#if defined(CONFIG_SOFTMMU) - tcg_out_mov(s, r1, addr_reg); - - tcg_out_mov(s, r0, addr_reg); - - tcg_out_modrm(s, 0xc1, 5, r1); /* shr $x, r1 */ - tcg_out8(s, TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS); - - tcg_out_modrm(s, 0x81, 4, r0); /* andl $x, r0 */ - tcg_out32(s, TARGET_PAGE_MASK | ((1 << s_bits) - 1)); - - tcg_out_modrm(s, 0x81, 4, r1); /* andl $x, r1 */ - tcg_out32(s, (CPU_TLB_SIZE - 1) << CPU_TLB_ENTRY_BITS); - - tcg_out_opc(s, 0x8d); /* lea offset(r1, %ebp), r1 */ - tcg_out8(s, 0x80 | (r1 << 3) | 0x04); - tcg_out8(s, (5 << 3) | r1); - tcg_out32(s, offsetof(CPUState, tlb_table[mem_index][0].addr_write)); + /* TLB Hit. */ + tcg_out_qemu_st_direct(s, data_reg, data_reg2, + tcg_target_call_iarg_regs[0], 0, opc); - /* cmp 0(r1), r0 */ - tcg_out_modrm_offset(s, 0x3b, r0, r1, 0); - - tcg_out_mov(s, r0, addr_reg); - -#if TARGET_LONG_BITS == 32 - /* je label1 */ - tcg_out8(s, 0x70 + JCC_JE); - label1_ptr = s->code_ptr; - s->code_ptr++; -#else - /* jne label3 */ - tcg_out8(s, 0x70 + JCC_JNE); - label3_ptr = s->code_ptr; - s->code_ptr++; - - /* cmp 4(r1), addr_reg2 */ - tcg_out_modrm_offset(s, 0x3b, addr_reg2, r1, 4); - - /* je label1 */ - tcg_out8(s, 0x70 + JCC_JE); - label1_ptr = s->code_ptr; + /* jmp label2 */ + tcg_out8(s, OPC_JMP_short); + label_ptr[2] = s->code_ptr; s->code_ptr++; - - /* label3: */ - *label3_ptr = s->code_ptr - label3_ptr - 1; -#endif + + /* TLB Miss. */ + + /* label1: */ + *label_ptr[0] = s->code_ptr - label_ptr[0] - 1; + if (TARGET_LONG_BITS > TCG_TARGET_REG_BITS) { + *label_ptr[1] = s->code_ptr - label_ptr[1] - 1; + } /* XXX: move that code at the end of the TB */ -#if TARGET_LONG_BITS == 32 - if (opc == 3) { - tcg_out_mov(s, TCG_REG_EDX, data_reg); - tcg_out_mov(s, TCG_REG_ECX, data_reg2); - tcg_out8(s, 0x6a); /* push Ib */ - tcg_out8(s, mem_index); - tcg_out8(s, 0xe8); - tcg_out32(s, (tcg_target_long)qemu_st_helpers[s_bits] - - (tcg_target_long)s->code_ptr - 4); - tcg_out_addi(s, TCG_REG_ESP, 4); - } else { - switch(opc) { - case 0: - /* movzbl */ - tcg_out_modrm(s, 0xb6 | P_EXT, TCG_REG_EDX, data_reg); - break; - case 1: - /* movzwl */ - tcg_out_modrm(s, 0xb7 | P_EXT, TCG_REG_EDX, data_reg); - break; - case 2: - tcg_out_mov(s, TCG_REG_EDX, data_reg); - break; + if (TCG_TARGET_REG_BITS == 64) { + tcg_out_mov(s, (opc == 3 ? TCG_TYPE_I64 : TCG_TYPE_I32), + TCG_REG_RSI, data_reg); + tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_RDX, mem_index); + stack_adjust = 0; + } else if (TARGET_LONG_BITS == 32) { + tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_EDX, data_reg); + if (opc == 3) { + tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_ECX, data_reg2); + tcg_out_pushi(s, mem_index); + stack_adjust = 4; + } else { + tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_ECX, mem_index); + stack_adjust = 0; } - tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_ECX, mem_index); - tcg_out8(s, 0xe8); - tcg_out32(s, (tcg_target_long)qemu_st_helpers[s_bits] - - (tcg_target_long)s->code_ptr - 4); - } -#else - if (opc == 3) { - tcg_out_mov(s, TCG_REG_EDX, addr_reg2); - tcg_out8(s, 0x6a); /* push Ib */ - tcg_out8(s, mem_index); - tcg_out_opc(s, 0x50 + data_reg2); /* push */ - tcg_out_opc(s, 0x50 + data_reg); /* push */ - tcg_out8(s, 0xe8); - tcg_out32(s, (tcg_target_long)qemu_st_helpers[s_bits] - - (tcg_target_long)s->code_ptr - 4); - tcg_out_addi(s, TCG_REG_ESP, 12); } else { - tcg_out_mov(s, TCG_REG_EDX, addr_reg2); - switch(opc) { - case 0: - /* movzbl */ - tcg_out_modrm(s, 0xb6 | P_EXT, TCG_REG_ECX, data_reg); - break; - case 1: - /* movzwl */ - tcg_out_modrm(s, 0xb7 | P_EXT, TCG_REG_ECX, data_reg); - break; - case 2: - tcg_out_mov(s, TCG_REG_ECX, data_reg); - break; + if (opc == 3) { + tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_EDX, args[addrlo_idx + 1]); + tcg_out_pushi(s, mem_index); + tcg_out_push(s, data_reg2); + tcg_out_push(s, data_reg); + stack_adjust = 12; + } else { + tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_EDX, args[addrlo_idx + 1]); + switch(opc) { + case 0: + tcg_out_ext8u(s, TCG_REG_ECX, data_reg); + break; + case 1: + tcg_out_ext16u(s, TCG_REG_ECX, data_reg); + break; + case 2: + tcg_out_mov(s, TCG_TYPE_I32, TCG_REG_ECX, data_reg); + break; + } + tcg_out_pushi(s, mem_index); + stack_adjust = 4; } - tcg_out8(s, 0x6a); /* push Ib */ - tcg_out8(s, mem_index); - tcg_out8(s, 0xe8); - tcg_out32(s, (tcg_target_long)qemu_st_helpers[s_bits] - - (tcg_target_long)s->code_ptr - 4); - tcg_out_addi(s, TCG_REG_ESP, 4); } -#endif - - /* jmp label2 */ - tcg_out8(s, 0xeb); - label2_ptr = s->code_ptr; - s->code_ptr++; - - /* label1: */ - *label1_ptr = s->code_ptr - label1_ptr - 1; - /* add x(r1), r0 */ - tcg_out_modrm_offset(s, 0x03, r0, r1, offsetof(CPUTLBEntry, addend) - - offsetof(CPUTLBEntry, addr_write)); -#else - r0 = addr_reg; -#endif + tcg_out_calli(s, (tcg_target_long)qemu_st_helpers[s_bits]); -#ifdef TARGET_WORDS_BIGENDIAN - bswap = 1; -#else - bswap = 0; -#endif - switch(opc) { - case 0: - /* movb */ - tcg_out_modrm_offset(s, 0x88, data_reg, r0, GUEST_BASE); - break; - case 1: - if (bswap) { - tcg_out_mov(s, r1, data_reg); - tcg_out8(s, 0x66); /* rolw $8, %ecx */ - tcg_out_modrm(s, 0xc1, 0, r1); - tcg_out8(s, 8); - data_reg = r1; - } - /* movw */ - tcg_out8(s, 0x66); - tcg_out_modrm_offset(s, 0x89, data_reg, r0, GUEST_BASE); - break; - case 2: - if (bswap) { - tcg_out_mov(s, r1, data_reg); - /* bswap data_reg */ - tcg_out_opc(s, (0xc8 + r1) | P_EXT); - data_reg = r1; - } - /* movl */ - tcg_out_modrm_offset(s, 0x89, data_reg, r0, GUEST_BASE); - break; - case 3: - if (bswap) { - tcg_out_mov(s, r1, data_reg2); - /* bswap data_reg */ - tcg_out_opc(s, (0xc8 + r1) | P_EXT); - tcg_out_modrm_offset(s, 0x89, r1, r0, GUEST_BASE); - tcg_out_mov(s, r1, data_reg); - /* bswap data_reg */ - tcg_out_opc(s, (0xc8 + r1) | P_EXT); - tcg_out_modrm_offset(s, 0x89, r1, r0, GUEST_BASE + 4); - } else { - tcg_out_modrm_offset(s, 0x89, data_reg, r0, GUEST_BASE); - tcg_out_modrm_offset(s, 0x89, data_reg2, r0, GUEST_BASE + 4); - } - break; - default: - tcg_abort(); + if (stack_adjust == (TCG_TARGET_REG_BITS / 8)) { + /* Pop and discard. This is 2 bytes smaller than the add. */ + tcg_out_pop(s, TCG_REG_ECX); + } else if (stack_adjust != 0) { + tcg_out_addi(s, TCG_REG_ESP, stack_adjust); } -#if defined(CONFIG_SOFTMMU) /* label2: */ - *label2_ptr = s->code_ptr - label2_ptr - 1; + *label_ptr[2] = s->code_ptr - label_ptr[2] - 1; +#else + { + int32_t offset = GUEST_BASE; + int base = args[addrlo_idx]; + + if (TCG_TARGET_REG_BITS == 64) { + /* ??? We assume all operations have left us with register + contents that are zero extended. So far this appears to + be true. If we want to enforce this, we can either do + an explicit zero-extension here, or (if GUEST_BASE == 0) + use the ADDR32 prefix. For now, do nothing. */ + + if (offset != GUEST_BASE) { + tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_RDI, GUEST_BASE); + tgen_arithr(s, ARITH_ADD + P_REXW, TCG_REG_RDI, base); + base = TCG_REG_RDI, offset = 0; + } + } + + tcg_out_qemu_st_direct(s, data_reg, data_reg2, base, offset, opc); + } #endif } -static inline void tcg_out_op(TCGContext *s, int opc, +static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg *args, const int *const_args) { - int c; - + int c, rexw = 0; + +#if TCG_TARGET_REG_BITS == 64 +# define OP_32_64(x) \ + case glue(glue(INDEX_op_, x), _i64): \ + rexw = P_REXW; /* FALLTHRU */ \ + case glue(glue(INDEX_op_, x), _i32) +#else +# define OP_32_64(x) \ + case glue(glue(INDEX_op_, x), _i32) +#endif + switch(opc) { case INDEX_op_exit_tb: - tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_EAX, args[0]); - tcg_out8(s, 0xe9); /* jmp tb_ret_addr */ - tcg_out32(s, tb_ret_addr - s->code_ptr - 4); + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_EAX, args[0]); + tcg_out_jmp(s, (tcg_target_long) tb_ret_addr); break; case INDEX_op_goto_tb: if (s->tb_jmp_offset) { /* direct jump method */ - tcg_out8(s, 0xe9); /* jmp im */ + tcg_out8(s, OPC_JMP_long); /* jmp im */ s->tb_jmp_offset[args[0]] = s->code_ptr - s->code_buf; tcg_out32(s, 0); } else { /* indirect jump method */ - /* jmp Ev */ - tcg_out_modrm_offset(s, 0xff, 4, -1, + tcg_out_modrm_offset(s, OPC_GRP5, EXT5_JMPN_Ev, -1, (tcg_target_long)(s->tb_next + args[0])); } s->tb_next_offset[args[0]] = s->code_ptr - s->code_buf; break; case INDEX_op_call: if (const_args[0]) { - tcg_out8(s, 0xe8); - tcg_out32(s, args[0] - (tcg_target_long)s->code_ptr - 4); + tcg_out_calli(s, args[0]); } else { - tcg_out_modrm(s, 0xff, 2, args[0]); + /* call *reg */ + tcg_out_modrm(s, OPC_GRP5, EXT5_CALLN_Ev, args[0]); } break; case INDEX_op_jmp: if (const_args[0]) { - tcg_out8(s, 0xe9); - tcg_out32(s, args[0] - (tcg_target_long)s->code_ptr - 4); + tcg_out_jmp(s, args[0]); } else { - tcg_out_modrm(s, 0xff, 4, args[0]); + /* jmp *reg */ + tcg_out_modrm(s, OPC_GRP5, EXT5_JMPN_Ev, args[0]); } break; case INDEX_op_br: - tcg_out_jxx(s, JCC_JMP, args[0]); + tcg_out_jxx(s, JCC_JMP, args[0], 0); break; case INDEX_op_movi_i32: tcg_out_movi(s, TCG_TYPE_I32, args[0], args[1]); break; - case INDEX_op_ld8u_i32: - /* movzbl */ - tcg_out_modrm_offset(s, 0xb6 | P_EXT, args[0], args[1], args[2]); - break; - case INDEX_op_ld8s_i32: - /* movsbl */ - tcg_out_modrm_offset(s, 0xbe | P_EXT, args[0], args[1], args[2]); - break; - case INDEX_op_ld16u_i32: - /* movzwl */ - tcg_out_modrm_offset(s, 0xb7 | P_EXT, args[0], args[1], args[2]); - break; - case INDEX_op_ld16s_i32: - /* movswl */ - tcg_out_modrm_offset(s, 0xbf | P_EXT, args[0], args[1], args[2]); + OP_32_64(ld8u): + /* Note that we can ignore REXW for the zero-extend to 64-bit. */ + tcg_out_modrm_offset(s, OPC_MOVZBL, args[0], args[1], args[2]); + break; + OP_32_64(ld8s): + tcg_out_modrm_offset(s, OPC_MOVSBL + rexw, args[0], args[1], args[2]); + break; + OP_32_64(ld16u): + /* Note that we can ignore REXW for the zero-extend to 64-bit. */ + tcg_out_modrm_offset(s, OPC_MOVZWL, args[0], args[1], args[2]); break; + OP_32_64(ld16s): + tcg_out_modrm_offset(s, OPC_MOVSWL + rexw, args[0], args[1], args[2]); + break; +#if TCG_TARGET_REG_BITS == 64 + case INDEX_op_ld32u_i64: +#endif case INDEX_op_ld_i32: - /* movl */ - tcg_out_modrm_offset(s, 0x8b, args[0], args[1], args[2]); + tcg_out_ld(s, TCG_TYPE_I32, args[0], args[1], args[2]); break; - case INDEX_op_st8_i32: - /* movb */ - tcg_out_modrm_offset(s, 0x88, args[0], args[1], args[2]); + + OP_32_64(st8): + tcg_out_modrm_offset(s, OPC_MOVB_EvGv | P_REXB_R, + args[0], args[1], args[2]); break; - case INDEX_op_st16_i32: - /* movw */ - tcg_out8(s, 0x66); - tcg_out_modrm_offset(s, 0x89, args[0], args[1], args[2]); + OP_32_64(st16): + tcg_out_modrm_offset(s, OPC_MOVL_EvGv | P_DATA16, + args[0], args[1], args[2]); break; +#if TCG_TARGET_REG_BITS == 64 + case INDEX_op_st32_i64: +#endif case INDEX_op_st_i32: - /* movl */ - tcg_out_modrm_offset(s, 0x89, args[0], args[1], args[2]); + tcg_out_st(s, TCG_TYPE_I32, args[0], args[1], args[2]); break; - case INDEX_op_sub_i32: + + OP_32_64(add): + /* For 3-operand addition, use LEA. */ + if (args[0] != args[1]) { + TCGArg a0 = args[0], a1 = args[1], a2 = args[2], c3 = 0; + + if (const_args[2]) { + c3 = a2, a2 = -1; + } else if (a0 == a2) { + /* Watch out for dest = src + dest, since we've removed + the matching constraint on the add. */ + tgen_arithr(s, ARITH_ADD + rexw, a0, a1); + break; + } + + tcg_out_modrm_sib_offset(s, OPC_LEA + rexw, a0, a1, a2, 0, c3); + break; + } + c = ARITH_ADD; + goto gen_arith; + OP_32_64(sub): c = ARITH_SUB; goto gen_arith; - case INDEX_op_and_i32: + OP_32_64(and): c = ARITH_AND; goto gen_arith; - case INDEX_op_or_i32: + OP_32_64(or): c = ARITH_OR; goto gen_arith; - case INDEX_op_xor_i32: + OP_32_64(xor): c = ARITH_XOR; goto gen_arith; - case INDEX_op_add_i32: - c = ARITH_ADD; gen_arith: if (const_args[2]) { - tgen_arithi(s, c, args[0], args[2], 0); + tgen_arithi(s, c + rexw, args[0], args[2], 0); } else { - tcg_out_modrm(s, 0x01 | (c << 3), args[2], args[0]); + tgen_arithr(s, c + rexw, args[0], args[2]); } break; - case INDEX_op_mul_i32: + + OP_32_64(mul): if (const_args[2]) { int32_t val; val = args[2]; if (val == (int8_t)val) { - tcg_out_modrm(s, 0x6b, args[0], args[0]); + tcg_out_modrm(s, OPC_IMUL_GvEvIb + rexw, args[0], args[0]); tcg_out8(s, val); } else { - tcg_out_modrm(s, 0x69, args[0], args[0]); + tcg_out_modrm(s, OPC_IMUL_GvEvIz + rexw, args[0], args[0]); tcg_out32(s, val); } } else { - tcg_out_modrm(s, 0xaf | P_EXT, args[0], args[2]); + tcg_out_modrm(s, OPC_IMUL_GvEv + rexw, args[0], args[2]); } break; - case INDEX_op_mulu2_i32: - tcg_out_modrm(s, 0xf7, 4, args[3]); - break; - case INDEX_op_div2_i32: - tcg_out_modrm(s, 0xf7, 7, args[4]); + + OP_32_64(div2): + tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_IDIV, args[4]); break; - case INDEX_op_divu2_i32: - tcg_out_modrm(s, 0xf7, 6, args[4]); + OP_32_64(divu2): + tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_DIV, args[4]); break; - case INDEX_op_shl_i32: + + OP_32_64(shl): c = SHIFT_SHL; - gen_shift32: - if (const_args[2]) { - if (args[2] == 1) { - tcg_out_modrm(s, 0xd1, c, args[0]); - } else { - tcg_out_modrm(s, 0xc1, c, args[0]); - tcg_out8(s, args[2]); - } - } else { - tcg_out_modrm(s, 0xd3, c, args[0]); - } - break; - case INDEX_op_shr_i32: + goto gen_shift; + OP_32_64(shr): c = SHIFT_SHR; - goto gen_shift32; - case INDEX_op_sar_i32: + goto gen_shift; + OP_32_64(sar): c = SHIFT_SAR; - goto gen_shift32; - case INDEX_op_rotl_i32: + goto gen_shift; + OP_32_64(rotl): c = SHIFT_ROL; - goto gen_shift32; - case INDEX_op_rotr_i32: + goto gen_shift; + OP_32_64(rotr): c = SHIFT_ROR; - goto gen_shift32; - - case INDEX_op_add2_i32: - if (const_args[4]) - tgen_arithi(s, ARITH_ADD, args[0], args[4], 1); - else - tcg_out_modrm(s, 0x01 | (ARITH_ADD << 3), args[4], args[0]); - if (const_args[5]) - tgen_arithi(s, ARITH_ADC, args[1], args[5], 1); - else - tcg_out_modrm(s, 0x01 | (ARITH_ADC << 3), args[5], args[1]); - break; - case INDEX_op_sub2_i32: - if (const_args[4]) - tgen_arithi(s, ARITH_SUB, args[0], args[4], 1); - else - tcg_out_modrm(s, 0x01 | (ARITH_SUB << 3), args[4], args[0]); - if (const_args[5]) - tgen_arithi(s, ARITH_SBB, args[1], args[5], 1); - else - tcg_out_modrm(s, 0x01 | (ARITH_SBB << 3), args[5], args[1]); + goto gen_shift; + gen_shift: + if (const_args[2]) { + tcg_out_shifti(s, c + rexw, args[0], args[2]); + } else { + tcg_out_modrm(s, OPC_SHIFT_cl + rexw, c, args[0]); + } break; + case INDEX_op_brcond_i32: - tcg_out_brcond(s, args[2], args[0], args[1], const_args[1], args[3]); + tcg_out_brcond32(s, args[2], args[0], args[1], const_args[1], + args[3], 0); break; - case INDEX_op_brcond2_i32: - tcg_out_brcond2(s, args, const_args); + case INDEX_op_setcond_i32: + tcg_out_setcond32(s, args[3], args[0], args[1], + args[2], const_args[2]); break; - case INDEX_op_bswap16_i32: - tcg_out8(s, 0x66); - tcg_out_modrm(s, 0xc1, SHIFT_ROL, args[0]); - tcg_out8(s, 8); + OP_32_64(bswap16): + tcg_out_rolw_8(s, args[0]); break; - case INDEX_op_bswap32_i32: - tcg_out_opc(s, (0xc8 + args[0]) | P_EXT); + OP_32_64(bswap32): + tcg_out_bswap32(s, args[0]); break; - case INDEX_op_neg_i32: - tcg_out_modrm(s, 0xf7, 3, args[0]); + OP_32_64(neg): + tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_NEG, args[0]); break; - - case INDEX_op_not_i32: - tcg_out_modrm(s, 0xf7, 2, args[0]); + OP_32_64(not): + tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_NOT, args[0]); break; - case INDEX_op_ext8s_i32: - tcg_out_modrm(s, 0xbe | P_EXT, args[0], args[1]); + OP_32_64(ext8s): + tcg_out_ext8s(s, args[0], args[1], rexw); break; - case INDEX_op_ext16s_i32: - tcg_out_modrm(s, 0xbf | P_EXT, args[0], args[1]); + OP_32_64(ext16s): + tcg_out_ext16s(s, args[0], args[1], rexw); break; - case INDEX_op_ext8u_i32: - tcg_out_modrm(s, 0xb6 | P_EXT, args[0], args[1]); + OP_32_64(ext8u): + tcg_out_ext8u(s, args[0], args[1]); break; - case INDEX_op_ext16u_i32: - tcg_out_modrm(s, 0xb7 | P_EXT, args[0], args[1]); + OP_32_64(ext16u): + tcg_out_ext16u(s, args[0], args[1]); break; case INDEX_op_qemu_ld8u: @@ -1092,13 +1651,16 @@ case INDEX_op_qemu_ld16s: tcg_out_qemu_ld(s, args, 1 | 4); break; +#if TCG_TARGET_REG_BITS == 64 case INDEX_op_qemu_ld32u: +#endif + case INDEX_op_qemu_ld32: tcg_out_qemu_ld(s, args, 2); break; case INDEX_op_qemu_ld64: tcg_out_qemu_ld(s, args, 3); break; - + case INDEX_op_qemu_st8: tcg_out_qemu_st(s, args, 0); break; @@ -1112,9 +1674,82 @@ tcg_out_qemu_st(s, args, 3); break; +#if TCG_TARGET_REG_BITS == 32 + case INDEX_op_brcond2_i32: + tcg_out_brcond2(s, args, const_args, 0); + break; + case INDEX_op_setcond2_i32: + tcg_out_setcond2(s, args, const_args); + break; + case INDEX_op_mulu2_i32: + tcg_out_modrm(s, OPC_GRP3_Ev, EXT3_MUL, args[3]); + break; + case INDEX_op_add2_i32: + if (const_args[4]) { + tgen_arithi(s, ARITH_ADD, args[0], args[4], 1); + } else { + tgen_arithr(s, ARITH_ADD, args[0], args[4]); + } + if (const_args[5]) { + tgen_arithi(s, ARITH_ADC, args[1], args[5], 1); + } else { + tgen_arithr(s, ARITH_ADC, args[1], args[5]); + } + break; + case INDEX_op_sub2_i32: + if (const_args[4]) { + tgen_arithi(s, ARITH_SUB, args[0], args[4], 1); + } else { + tgen_arithr(s, ARITH_SUB, args[0], args[4]); + } + if (const_args[5]) { + tgen_arithi(s, ARITH_SBB, args[1], args[5], 1); + } else { + tgen_arithr(s, ARITH_SBB, args[1], args[5]); + } + break; +#else /* TCG_TARGET_REG_BITS == 64 */ + case INDEX_op_movi_i64: + tcg_out_movi(s, TCG_TYPE_I64, args[0], args[1]); + break; + case INDEX_op_ld32s_i64: + tcg_out_modrm_offset(s, OPC_MOVSLQ, args[0], args[1], args[2]); + break; + case INDEX_op_ld_i64: + tcg_out_ld(s, TCG_TYPE_I64, args[0], args[1], args[2]); + break; + case INDEX_op_st_i64: + tcg_out_st(s, TCG_TYPE_I64, args[0], args[1], args[2]); + break; + case INDEX_op_qemu_ld32s: + tcg_out_qemu_ld(s, args, 2 | 4); + break; + + case INDEX_op_brcond_i64: + tcg_out_brcond64(s, args[2], args[0], args[1], const_args[1], + args[3], 0); + break; + case INDEX_op_setcond_i64: + tcg_out_setcond64(s, args[3], args[0], args[1], + args[2], const_args[2]); + break; + + case INDEX_op_bswap64_i64: + tcg_out_bswap64(s, args[0]); + break; + case INDEX_op_ext32u_i64: + tcg_out_ext32u(s, args[0], args[1]); + break; + case INDEX_op_ext32s_i64: + tcg_out_ext32s(s, args[0], args[1]); + break; +#endif + default: tcg_abort(); } + +#undef OP_32_64 } static const TCGTargetOpDef x86_op_defs[] = { @@ -1134,10 +1769,9 @@ { INDEX_op_st16_i32, { "r", "r" } }, { INDEX_op_st_i32, { "r", "r" } }, - { INDEX_op_add_i32, { "r", "0", "ri" } }, + { INDEX_op_add_i32, { "r", "r", "ri" } }, { INDEX_op_sub_i32, { "r", "0", "ri" } }, { INDEX_op_mul_i32, { "r", "0", "ri" } }, - { INDEX_op_mulu2_i32, { "a", "d", "a", "r" } }, { INDEX_op_div2_i32, { "a", "d", "0", "1", "r" } }, { INDEX_op_divu2_i32, { "a", "d", "0", "1", "r" } }, { INDEX_op_and_i32, { "r", "0", "ri" } }, @@ -1152,10 +1786,6 @@ { INDEX_op_brcond_i32, { "r", "ri" } }, - { INDEX_op_add2_i32, { "r", "r", "0", "1", "ri", "ri" } }, - { INDEX_op_sub2_i32, { "r", "r", "0", "1", "ri", "ri" } }, - { INDEX_op_brcond2_i32, { "r", "r", "ri", "ri" } }, - { INDEX_op_bswap16_i32, { "r", "0" } }, { INDEX_op_bswap32_i32, { "r", "0" } }, @@ -1165,15 +1795,84 @@ { INDEX_op_ext8s_i32, { "r", "q" } }, { INDEX_op_ext16s_i32, { "r", "r" } }, - { INDEX_op_ext8u_i32, { "r", "q"} }, - { INDEX_op_ext16u_i32, { "r", "r"} }, + { INDEX_op_ext8u_i32, { "r", "q" } }, + { INDEX_op_ext16u_i32, { "r", "r" } }, + + { INDEX_op_setcond_i32, { "q", "r", "ri" } }, + +#if TCG_TARGET_REG_BITS == 32 + { INDEX_op_mulu2_i32, { "a", "d", "a", "r" } }, + { INDEX_op_add2_i32, { "r", "r", "0", "1", "ri", "ri" } }, + { INDEX_op_sub2_i32, { "r", "r", "0", "1", "ri", "ri" } }, + { INDEX_op_brcond2_i32, { "r", "r", "ri", "ri" } }, + { INDEX_op_setcond2_i32, { "r", "r", "r", "ri", "ri" } }, +#else + { INDEX_op_mov_i64, { "r", "r" } }, + { INDEX_op_movi_i64, { "r" } }, + { INDEX_op_ld8u_i64, { "r", "r" } }, + { INDEX_op_ld8s_i64, { "r", "r" } }, + { INDEX_op_ld16u_i64, { "r", "r" } }, + { INDEX_op_ld16s_i64, { "r", "r" } }, + { INDEX_op_ld32u_i64, { "r", "r" } }, + { INDEX_op_ld32s_i64, { "r", "r" } }, + { INDEX_op_ld_i64, { "r", "r" } }, + { INDEX_op_st8_i64, { "r", "r" } }, + { INDEX_op_st16_i64, { "r", "r" } }, + { INDEX_op_st32_i64, { "r", "r" } }, + { INDEX_op_st_i64, { "r", "r" } }, + + { INDEX_op_add_i64, { "r", "0", "re" } }, + { INDEX_op_mul_i64, { "r", "0", "re" } }, + { INDEX_op_div2_i64, { "a", "d", "0", "1", "r" } }, + { INDEX_op_divu2_i64, { "a", "d", "0", "1", "r" } }, + { INDEX_op_sub_i64, { "r", "0", "re" } }, + { INDEX_op_and_i64, { "r", "0", "reZ" } }, + { INDEX_op_or_i64, { "r", "0", "re" } }, + { INDEX_op_xor_i64, { "r", "0", "re" } }, + + { INDEX_op_shl_i64, { "r", "0", "ci" } }, + { INDEX_op_shr_i64, { "r", "0", "ci" } }, + { INDEX_op_sar_i64, { "r", "0", "ci" } }, + { INDEX_op_rotl_i64, { "r", "0", "ci" } }, + { INDEX_op_rotr_i64, { "r", "0", "ci" } }, + + { INDEX_op_brcond_i64, { "r", "re" } }, + { INDEX_op_setcond_i64, { "r", "r", "re" } }, + + { INDEX_op_bswap16_i64, { "r", "0" } }, + { INDEX_op_bswap32_i64, { "r", "0" } }, + { INDEX_op_bswap64_i64, { "r", "0" } }, + { INDEX_op_neg_i64, { "r", "0" } }, + { INDEX_op_not_i64, { "r", "0" } }, + + { INDEX_op_ext8s_i64, { "r", "r" } }, + { INDEX_op_ext16s_i64, { "r", "r" } }, + { INDEX_op_ext32s_i64, { "r", "r" } }, + { INDEX_op_ext8u_i64, { "r", "r" } }, + { INDEX_op_ext16u_i64, { "r", "r" } }, + { INDEX_op_ext32u_i64, { "r", "r" } }, +#endif -#if TARGET_LONG_BITS == 32 +#if TCG_TARGET_REG_BITS == 64 { INDEX_op_qemu_ld8u, { "r", "L" } }, { INDEX_op_qemu_ld8s, { "r", "L" } }, { INDEX_op_qemu_ld16u, { "r", "L" } }, { INDEX_op_qemu_ld16s, { "r", "L" } }, + { INDEX_op_qemu_ld32, { "r", "L" } }, { INDEX_op_qemu_ld32u, { "r", "L" } }, + { INDEX_op_qemu_ld32s, { "r", "L" } }, + { INDEX_op_qemu_ld64, { "r", "L" } }, + + { INDEX_op_qemu_st8, { "L", "L" } }, + { INDEX_op_qemu_st16, { "L", "L" } }, + { INDEX_op_qemu_st32, { "L", "L" } }, + { INDEX_op_qemu_st64, { "L", "L" } }, +#elif TARGET_LONG_BITS <= TCG_TARGET_REG_BITS + { INDEX_op_qemu_ld8u, { "r", "L" } }, + { INDEX_op_qemu_ld8s, { "r", "L" } }, + { INDEX_op_qemu_ld16u, { "r", "L" } }, + { INDEX_op_qemu_ld16s, { "r", "L" } }, + { INDEX_op_qemu_ld32, { "r", "L" } }, { INDEX_op_qemu_ld64, { "r", "r", "L" } }, { INDEX_op_qemu_st8, { "cb", "L" } }, @@ -1185,7 +1884,7 @@ { INDEX_op_qemu_ld8s, { "r", "L", "L" } }, { INDEX_op_qemu_ld16u, { "r", "L", "L" } }, { INDEX_op_qemu_ld16s, { "r", "L", "L" } }, - { INDEX_op_qemu_ld32u, { "r", "L", "L" } }, + { INDEX_op_qemu_ld32, { "r", "L", "L" } }, { INDEX_op_qemu_ld64, { "r", "r", "L", "L" } }, { INDEX_op_qemu_st8, { "cb", "L", "L" } }, @@ -1197,64 +1896,85 @@ }; static int tcg_target_callee_save_regs[] = { - /* TCG_REG_EBP, */ /* currently used for the global env, so no - need to save */ +#if TCG_TARGET_REG_BITS == 64 + TCG_REG_RBP, + TCG_REG_RBX, + TCG_REG_R12, + TCG_REG_R13, + /* TCG_REG_R14, */ /* Currently used for the global env. */ + TCG_REG_R15, +#else + /* TCG_REG_EBP, */ /* Currently used for the global env. */ TCG_REG_EBX, TCG_REG_ESI, TCG_REG_EDI, +#endif }; -static inline void tcg_out_push(TCGContext *s, int reg) -{ - tcg_out_opc(s, 0x50 + reg); -} - -static inline void tcg_out_pop(TCGContext *s, int reg) -{ - tcg_out_opc(s, 0x58 + reg); -} - /* Generate global QEMU prologue and epilogue code */ -void tcg_target_qemu_prologue(TCGContext *s) +static void tcg_target_qemu_prologue(TCGContext *s) { int i, frame_size, push_size, stack_addend; - + /* TB prologue */ - /* save all callee saved registers */ - for(i = 0; i < ARRAY_SIZE(tcg_target_callee_save_regs); i++) { + + /* Save all callee saved registers. */ + for (i = 0; i < ARRAY_SIZE(tcg_target_callee_save_regs); i++) { tcg_out_push(s, tcg_target_callee_save_regs[i]); } - /* reserve some stack space */ - push_size = 4 + ARRAY_SIZE(tcg_target_callee_save_regs) * 4; + + /* Reserve some stack space. */ + push_size = 1 + ARRAY_SIZE(tcg_target_callee_save_regs); + push_size *= TCG_TARGET_REG_BITS / 8; + frame_size = push_size + TCG_STATIC_CALL_ARGS_SIZE; - frame_size = (frame_size + TCG_TARGET_STACK_ALIGN - 1) & + frame_size = (frame_size + TCG_TARGET_STACK_ALIGN - 1) & ~(TCG_TARGET_STACK_ALIGN - 1); stack_addend = frame_size - push_size; tcg_out_addi(s, TCG_REG_ESP, -stack_addend); - tcg_out_modrm(s, 0xff, 4, TCG_REG_EAX); /* jmp *%eax */ - + /* jmp *tb. */ + tcg_out_modrm(s, OPC_GRP5, EXT5_JMPN_Ev, tcg_target_call_iarg_regs[0]); + /* TB epilogue */ tb_ret_addr = s->code_ptr; + tcg_out_addi(s, TCG_REG_ESP, stack_addend); - for(i = ARRAY_SIZE(tcg_target_callee_save_regs) - 1; i >= 0; i--) { + + for (i = ARRAY_SIZE(tcg_target_callee_save_regs) - 1; i >= 0; i--) { tcg_out_pop(s, tcg_target_callee_save_regs[i]); } - tcg_out8(s, 0xc3); /* ret */ + tcg_out_opc(s, OPC_RET, 0, 0, 0); } -void tcg_target_init(TCGContext *s) +static void tcg_target_init(TCGContext *s) { +#if !defined(CONFIG_USER_ONLY) /* fail safe */ if ((1 << CPU_TLB_ENTRY_BITS) != sizeof(CPUTLBEntry)) tcg_abort(); +#endif + + if (TCG_TARGET_REG_BITS == 64) { + tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0, 0xffff); + tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I64], 0, 0xffff); + } else { + tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0, 0xff); + } + + tcg_regset_clear(tcg_target_call_clobber_regs); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_EAX); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_EDX); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_ECX); + if (TCG_TARGET_REG_BITS == 64) { + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_RDI); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_RSI); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R8); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R9); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R10); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R11); + } - tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0, 0xff); - tcg_regset_set32(tcg_target_call_clobber_regs, 0, - (1 << TCG_REG_EAX) | - (1 << TCG_REG_EDX) | - (1 << TCG_REG_ECX)); - tcg_regset_clear(s->reserved_regs); tcg_regset_set_reg(s->reserved_regs, TCG_REG_ESP); diff -Nru qemu-kvm-0.12.5+noroms/tcg/i386/tcg-target.h qemu-kvm-0.14.1/tcg/i386/tcg-target.h --- qemu-kvm-0.12.5+noroms/tcg/i386/tcg-target.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/tcg/i386/tcg-target.h 2011-05-11 13:29:46.000000000 +0000 @@ -23,10 +23,18 @@ */ #define TCG_TARGET_I386 1 -#define TCG_TARGET_REG_BITS 32 +#if defined(__x86_64__) +# define TCG_TARGET_REG_BITS 64 +#else +# define TCG_TARGET_REG_BITS 32 +#endif //#define TCG_TARGET_WORDS_BIGENDIAN -#define TCG_TARGET_NB_REGS 8 +#if TCG_TARGET_REG_BITS == 64 +# define TCG_TARGET_NB_REGS 16 +#else +# define TCG_TARGET_NB_REGS 8 +#endif enum { TCG_REG_EAX = 0, @@ -37,30 +45,81 @@ TCG_REG_EBP, TCG_REG_ESI, TCG_REG_EDI, + + /* 64-bit registers; always define the symbols to avoid + too much if-deffing. */ + TCG_REG_R8, + TCG_REG_R9, + TCG_REG_R10, + TCG_REG_R11, + TCG_REG_R12, + TCG_REG_R13, + TCG_REG_R14, + TCG_REG_R15, + TCG_REG_RAX = TCG_REG_EAX, + TCG_REG_RCX = TCG_REG_ECX, + TCG_REG_RDX = TCG_REG_EDX, + TCG_REG_RBX = TCG_REG_EBX, + TCG_REG_RSP = TCG_REG_ESP, + TCG_REG_RBP = TCG_REG_EBP, + TCG_REG_RSI = TCG_REG_ESI, + TCG_REG_RDI = TCG_REG_EDI, }; +#define TCG_CT_CONST_S32 0x100 +#define TCG_CT_CONST_U32 0x200 + /* used for function call generation */ #define TCG_REG_CALL_STACK TCG_REG_ESP #define TCG_TARGET_STACK_ALIGN 16 #define TCG_TARGET_CALL_STACK_OFFSET 0 /* optional instructions */ -#define TCG_TARGET_HAS_bswap16_i32 -#define TCG_TARGET_HAS_bswap32_i32 -#define TCG_TARGET_HAS_neg_i32 -#define TCG_TARGET_HAS_not_i32 +#define TCG_TARGET_HAS_div2_i32 +#define TCG_TARGET_HAS_rot_i32 #define TCG_TARGET_HAS_ext8s_i32 #define TCG_TARGET_HAS_ext16s_i32 -#define TCG_TARGET_HAS_rot_i32 #define TCG_TARGET_HAS_ext8u_i32 #define TCG_TARGET_HAS_ext16u_i32 +#define TCG_TARGET_HAS_bswap16_i32 +#define TCG_TARGET_HAS_bswap32_i32 +#define TCG_TARGET_HAS_neg_i32 +#define TCG_TARGET_HAS_not_i32 +// #define TCG_TARGET_HAS_andc_i32 +// #define TCG_TARGET_HAS_orc_i32 +// #define TCG_TARGET_HAS_eqv_i32 +// #define TCG_TARGET_HAS_nand_i32 +// #define TCG_TARGET_HAS_nor_i32 + +#if TCG_TARGET_REG_BITS == 64 +#define TCG_TARGET_HAS_div2_i64 +#define TCG_TARGET_HAS_rot_i64 +#define TCG_TARGET_HAS_ext8s_i64 +#define TCG_TARGET_HAS_ext16s_i64 +#define TCG_TARGET_HAS_ext32s_i64 +#define TCG_TARGET_HAS_ext8u_i64 +#define TCG_TARGET_HAS_ext16u_i64 +#define TCG_TARGET_HAS_ext32u_i64 +#define TCG_TARGET_HAS_bswap16_i64 +#define TCG_TARGET_HAS_bswap32_i64 +#define TCG_TARGET_HAS_bswap64_i64 +#define TCG_TARGET_HAS_neg_i64 +#define TCG_TARGET_HAS_not_i64 +// #define TCG_TARGET_HAS_andc_i64 +// #define TCG_TARGET_HAS_orc_i64 +// #define TCG_TARGET_HAS_eqv_i64 +// #define TCG_TARGET_HAS_nand_i64 +// #define TCG_TARGET_HAS_nor_i64 +#endif #define TCG_TARGET_HAS_GUEST_BASE /* Note: must be synced with dyngen-exec.h */ -#define TCG_AREG0 TCG_REG_EBP -#define TCG_AREG1 TCG_REG_EBX -#define TCG_AREG2 TCG_REG_ESI +#if TCG_TARGET_REG_BITS == 64 +# define TCG_AREG0 TCG_REG_R14 +#else +# define TCG_AREG0 TCG_REG_EBP +#endif static inline void flush_icache_range(unsigned long start, unsigned long stop) { diff -Nru qemu-kvm-0.12.5+noroms/tcg/ia64/tcg-target.c qemu-kvm-0.14.1/tcg/ia64/tcg-target.c --- qemu-kvm-0.12.5+noroms/tcg/ia64/tcg-target.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/tcg/ia64/tcg-target.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,2390 @@ +/* + * Tiny Code Generator for QEMU + * + * Copyright (c) 2009-2010 Aurelien Jarno + * Based on i386/tcg-target.c - Copyright (c) 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. + */ + +/* + * Register definitions + */ + +#ifndef NDEBUG +static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", + "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", + "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", + "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31", + "r32", "r33", "r34", "r35", "r36", "r37", "r38", "r39", + "r40", "r41", "r42", "r43", "r44", "r45", "r46", "r47", + "r48", "r49", "r50", "r51", "r52", "r53", "r54", "r55", + "r56", "r57", "r58", "r59", "r60", "r61", "r62", "r63", +}; +#endif + +#ifdef CONFIG_USE_GUEST_BASE +#define TCG_GUEST_BASE_REG TCG_REG_R55 +#else +#define TCG_GUEST_BASE_REG TCG_REG_R0 +#endif +#ifndef GUEST_BASE +#define GUEST_BASE 0 +#endif + +/* Branch registers */ +enum { + TCG_REG_B0 = 0, + TCG_REG_B1, + TCG_REG_B2, + TCG_REG_B3, + TCG_REG_B4, + TCG_REG_B5, + TCG_REG_B6, + TCG_REG_B7, +}; + +/* Floating point registers */ +enum { + TCG_REG_F0 = 0, + TCG_REG_F1, + TCG_REG_F2, + TCG_REG_F3, + TCG_REG_F4, + TCG_REG_F5, + TCG_REG_F6, + TCG_REG_F7, + TCG_REG_F8, + TCG_REG_F9, + TCG_REG_F10, + TCG_REG_F11, + TCG_REG_F12, + TCG_REG_F13, + TCG_REG_F14, + TCG_REG_F15, +}; + +/* Predicate registers */ +enum { + TCG_REG_P0 = 0, + TCG_REG_P1, + TCG_REG_P2, + TCG_REG_P3, + TCG_REG_P4, + TCG_REG_P5, + TCG_REG_P6, + TCG_REG_P7, + TCG_REG_P8, + TCG_REG_P9, + TCG_REG_P10, + TCG_REG_P11, + TCG_REG_P12, + TCG_REG_P13, + TCG_REG_P14, + TCG_REG_P15, +}; + +/* Application registers */ +enum { + TCG_REG_PFS = 64, +}; + +static const int tcg_target_reg_alloc_order[] = { + TCG_REG_R34, + TCG_REG_R35, + TCG_REG_R36, + TCG_REG_R37, + TCG_REG_R38, + TCG_REG_R39, + TCG_REG_R40, + TCG_REG_R41, + TCG_REG_R42, + TCG_REG_R43, + TCG_REG_R44, + TCG_REG_R45, + TCG_REG_R46, + TCG_REG_R47, + TCG_REG_R48, + TCG_REG_R49, + TCG_REG_R50, + TCG_REG_R51, + TCG_REG_R52, + TCG_REG_R53, + TCG_REG_R54, + TCG_REG_R55, + TCG_REG_R14, + TCG_REG_R15, + TCG_REG_R16, + TCG_REG_R17, + TCG_REG_R18, + TCG_REG_R19, + TCG_REG_R20, + TCG_REG_R21, + TCG_REG_R22, + TCG_REG_R23, + TCG_REG_R24, + TCG_REG_R25, + TCG_REG_R26, + TCG_REG_R27, + TCG_REG_R28, + TCG_REG_R29, + TCG_REG_R30, + TCG_REG_R31, + TCG_REG_R56, + TCG_REG_R57, + TCG_REG_R58, + TCG_REG_R59, + TCG_REG_R60, + TCG_REG_R61, + TCG_REG_R62, + TCG_REG_R63, + TCG_REG_R8, + TCG_REG_R9, + TCG_REG_R10, + TCG_REG_R11 +}; + +static const int tcg_target_call_iarg_regs[8] = { + TCG_REG_R56, + TCG_REG_R57, + TCG_REG_R58, + TCG_REG_R59, + TCG_REG_R60, + TCG_REG_R61, + TCG_REG_R62, + TCG_REG_R63, +}; + +static const int tcg_target_call_oarg_regs[2] = { + TCG_REG_R8, + TCG_REG_R9 +}; + +/* maximum number of register used for input function arguments */ +static inline int tcg_target_get_call_iarg_regs_count(int flags) +{ + return 8; +} + +/* + * opcode formation + */ + +/* bundle templates: stops (double bar in the IA64 manual) are marked with + an uppercase letter. */ +enum { + mii = 0x00, + miI = 0x01, + mIi = 0x02, + mII = 0x03, + mlx = 0x04, + mLX = 0x05, + mmi = 0x08, + mmI = 0x09, + Mmi = 0x0a, + MmI = 0x0b, + mfi = 0x0c, + mfI = 0x0d, + mmf = 0x0e, + mmF = 0x0f, + mib = 0x10, + miB = 0x11, + mbb = 0x12, + mbB = 0x13, + bbb = 0x16, + bbB = 0x17, + mmb = 0x18, + mmB = 0x19, + mfb = 0x1c, + mfB = 0x1d, +}; + +enum { + OPC_ADD_A1 = 0x10000000000ull, + OPC_AND_A1 = 0x10060000000ull, + OPC_AND_A3 = 0x10160000000ull, + OPC_ANDCM_A1 = 0x10068000000ull, + OPC_ANDCM_A3 = 0x10168000000ull, + OPC_ADDS_A4 = 0x10800000000ull, + OPC_ADDL_A5 = 0x12000000000ull, + OPC_ALLOC_M34 = 0x02c00000000ull, + OPC_BR_DPTK_FEW_B1 = 0x08400000000ull, + OPC_BR_SPTK_MANY_B1 = 0x08000001000ull, + OPC_BR_SPTK_MANY_B4 = 0x00100001000ull, + OPC_BR_CALL_SPTK_MANY_B5 = 0x02100001000ull, + OPC_BR_RET_SPTK_MANY_B4 = 0x00108001100ull, + OPC_BRL_SPTK_MANY_X3 = 0x18000001000ull, + OPC_CMP_LT_A6 = 0x18000000000ull, + OPC_CMP_LTU_A6 = 0x1a000000000ull, + OPC_CMP_EQ_A6 = 0x1c000000000ull, + OPC_CMP4_LT_A6 = 0x18400000000ull, + OPC_CMP4_LTU_A6 = 0x1a400000000ull, + OPC_CMP4_EQ_A6 = 0x1c400000000ull, + OPC_DEP_Z_I12 = 0x0a600000000ull, + OPC_EXTR_I11 = 0x0a400002000ull, + OPC_EXTR_U_I11 = 0x0a400000000ull, + OPC_FCVT_FX_TRUNC_S1_F10 = 0x004d0000000ull, + OPC_FCVT_FXU_TRUNC_S1_F10 = 0x004d8000000ull, + OPC_FCVT_XF_F11 = 0x000e0000000ull, + OPC_FMA_S1_F1 = 0x10400000000ull, + OPC_FNMA_S1_F1 = 0x18400000000ull, + OPC_FRCPA_S1_F6 = 0x00600000000ull, + OPC_GETF_SIG_M19 = 0x08708000000ull, + OPC_LD1_M1 = 0x08000000000ull, + OPC_LD1_M3 = 0x0a000000000ull, + OPC_LD2_M1 = 0x08040000000ull, + OPC_LD2_M3 = 0x0a040000000ull, + OPC_LD4_M1 = 0x08080000000ull, + OPC_LD4_M3 = 0x0a080000000ull, + OPC_LD8_M1 = 0x080c0000000ull, + OPC_LD8_M3 = 0x0a0c0000000ull, + OPC_MUX1_I3 = 0x0eca0000000ull, + OPC_NOP_B9 = 0x04008000000ull, + OPC_NOP_F16 = 0x00008000000ull, + OPC_NOP_I18 = 0x00008000000ull, + OPC_NOP_M48 = 0x00008000000ull, + OPC_MOV_I21 = 0x00e00100000ull, + OPC_MOV_RET_I21 = 0x00e00500000ull, + OPC_MOV_I22 = 0x00188000000ull, + OPC_MOV_I_I26 = 0x00150000000ull, + OPC_MOVL_X2 = 0x0c000000000ull, + OPC_OR_A1 = 0x10070000000ull, + OPC_SETF_EXP_M18 = 0x0c748000000ull, + OPC_SETF_SIG_M18 = 0x0c708000000ull, + OPC_SHL_I7 = 0x0f240000000ull, + OPC_SHR_I5 = 0x0f220000000ull, + OPC_SHR_U_I5 = 0x0f200000000ull, + OPC_SHRP_I10 = 0x0ac00000000ull, + OPC_SXT1_I29 = 0x000a0000000ull, + OPC_SXT2_I29 = 0x000a8000000ull, + OPC_SXT4_I29 = 0x000b0000000ull, + OPC_ST1_M4 = 0x08c00000000ull, + OPC_ST2_M4 = 0x08c40000000ull, + OPC_ST4_M4 = 0x08c80000000ull, + OPC_ST8_M4 = 0x08cc0000000ull, + OPC_SUB_A1 = 0x10028000000ull, + OPC_SUB_A3 = 0x10128000000ull, + OPC_UNPACK4_L_I2 = 0x0f860000000ull, + OPC_XMA_L_F2 = 0x1d000000000ull, + OPC_XOR_A1 = 0x10078000000ull, + OPC_ZXT1_I29 = 0x00080000000ull, + OPC_ZXT2_I29 = 0x00088000000ull, + OPC_ZXT4_I29 = 0x00090000000ull, +}; + +static inline uint64_t tcg_opc_a1(int qp, uint64_t opc, int r1, + int r2, int r3) +{ + return opc + | ((r3 & 0x7f) << 20) + | ((r2 & 0x7f) << 13) + | ((r1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_a3(int qp, uint64_t opc, int r1, + uint64_t imm, int r3) +{ + return opc + | ((imm & 0x80) << 29) /* s */ + | ((imm & 0x7f) << 13) /* imm7b */ + | ((r3 & 0x7f) << 20) + | ((r1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_a4(int qp, uint64_t opc, int r1, + uint64_t imm, int r3) +{ + return opc + | ((imm & 0x2000) << 23) /* s */ + | ((imm & 0x1f80) << 20) /* imm6d */ + | ((imm & 0x007f) << 13) /* imm7b */ + | ((r3 & 0x7f) << 20) + | ((r1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_a5(int qp, uint64_t opc, int r1, + uint64_t imm, int r3) +{ + return opc + | ((imm & 0x200000) << 15) /* s */ + | ((imm & 0x1f0000) << 6) /* imm5c */ + | ((imm & 0x00ff80) << 20) /* imm9d */ + | ((imm & 0x00007f) << 13) /* imm7b */ + | ((r3 & 0x03) << 20) + | ((r1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_a6(int qp, uint64_t opc, int p1, + int p2, int r2, int r3) +{ + return opc + | ((p2 & 0x3f) << 27) + | ((r3 & 0x7f) << 20) + | ((r2 & 0x7f) << 13) + | ((p1 & 0x3f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_b1(int qp, uint64_t opc, uint64_t imm) +{ + return opc + | ((imm & 0x100000) << 16) /* s */ + | ((imm & 0x0fffff) << 13) /* imm20b */ + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_b4(int qp, uint64_t opc, int b2) +{ + return opc + | ((b2 & 0x7) << 13) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_b5(int qp, uint64_t opc, int b1, int b2) +{ + return opc + | ((b2 & 0x7) << 13) + | ((b1 & 0x7) << 6) + | (qp & 0x3f); +} + + +static inline uint64_t tcg_opc_b9(int qp, uint64_t opc, uint64_t imm) +{ + return opc + | ((imm & 0x100000) << 16) /* i */ + | ((imm & 0x0fffff) << 6) /* imm20a */ + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_f1(int qp, uint64_t opc, int f1, + int f3, int f4, int f2) +{ + return opc + | ((f4 & 0x7f) << 27) + | ((f3 & 0x7f) << 20) + | ((f2 & 0x7f) << 13) + | ((f1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_f2(int qp, uint64_t opc, int f1, + int f3, int f4, int f2) +{ + return opc + | ((f4 & 0x7f) << 27) + | ((f3 & 0x7f) << 20) + | ((f2 & 0x7f) << 13) + | ((f1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_f6(int qp, uint64_t opc, int f1, + int p2, int f2, int f3) +{ + return opc + | ((p2 & 0x3f) << 27) + | ((f3 & 0x7f) << 20) + | ((f2 & 0x7f) << 13) + | ((f1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_f10(int qp, uint64_t opc, int f1, int f2) +{ + return opc + | ((f2 & 0x7f) << 13) + | ((f1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_f11(int qp, uint64_t opc, int f1, int f2) +{ + return opc + | ((f2 & 0x7f) << 13) + | ((f1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_f16(int qp, uint64_t opc, uint64_t imm) +{ + return opc + | ((imm & 0x100000) << 16) /* i */ + | ((imm & 0x0fffff) << 6) /* imm20a */ + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_i2(int qp, uint64_t opc, int r1, + int r2, int r3) +{ + return opc + | ((r3 & 0x7f) << 20) + | ((r2 & 0x7f) << 13) + | ((r1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_i3(int qp, uint64_t opc, int r1, + int r2, int mbtype) +{ + return opc + | ((mbtype & 0x0f) << 20) + | ((r2 & 0x7f) << 13) + | ((r1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_i5(int qp, uint64_t opc, int r1, + int r3, int r2) +{ + return opc + | ((r3 & 0x7f) << 20) + | ((r2 & 0x7f) << 13) + | ((r1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_i7(int qp, uint64_t opc, int r1, + int r2, int r3) +{ + return opc + | ((r3 & 0x7f) << 20) + | ((r2 & 0x7f) << 13) + | ((r1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_i10(int qp, uint64_t opc, int r1, + int r2, int r3, uint64_t count) +{ + return opc + | ((count & 0x3f) << 27) + | ((r3 & 0x7f) << 20) + | ((r2 & 0x7f) << 13) + | ((r1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_i11(int qp, uint64_t opc, int r1, + int r3, uint64_t pos, uint64_t len) +{ + return opc + | ((len & 0x3f) << 27) + | ((r3 & 0x7f) << 20) + | ((pos & 0x3f) << 14) + | ((r1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_i12(int qp, uint64_t opc, int r1, + int r2, uint64_t pos, uint64_t len) +{ + return opc + | ((len & 0x3f) << 27) + | ((pos & 0x3f) << 20) + | ((r2 & 0x7f) << 13) + | ((r1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_i18(int qp, uint64_t opc, uint64_t imm) +{ + return opc + | ((imm & 0x100000) << 16) /* i */ + | ((imm & 0x0fffff) << 6) /* imm20a */ + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_i21(int qp, uint64_t opc, int b1, + int r2, uint64_t imm) +{ + return opc + | ((imm & 0x1ff) << 24) + | ((r2 & 0x7f) << 13) + | ((b1 & 0x7) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_i22(int qp, uint64_t opc, int r1, int b2) +{ + return opc + | ((b2 & 0x7) << 13) + | ((r1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_i26(int qp, uint64_t opc, int ar3, int r2) +{ + return opc + | ((ar3 & 0x7f) << 20) + | ((r2 & 0x7f) << 13) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_i29(int qp, uint64_t opc, int r1, int r3) +{ + return opc + | ((r3 & 0x7f) << 20) + | ((r1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_l2(uint64_t imm) +{ + return (imm & 0x7fffffffffc00000ull) >> 22; +} + +static inline uint64_t tcg_opc_l3(uint64_t imm) +{ + return (imm & 0x07fffffffff00000ull) >> 18; +} + +static inline uint64_t tcg_opc_m1(int qp, uint64_t opc, int r1, int r3) +{ + return opc + | ((r3 & 0x7f) << 20) + | ((r1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_m3(int qp, uint64_t opc, int r1, + int r3, uint64_t imm) +{ + return opc + | ((imm & 0x100) << 28) /* s */ + | ((imm & 0x080) << 20) /* i */ + | ((imm & 0x07f) << 13) /* imm7b */ + | ((r3 & 0x7f) << 20) + | ((r1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_m4(int qp, uint64_t opc, int r2, int r3) +{ + return opc + | ((r3 & 0x7f) << 20) + | ((r2 & 0x7f) << 13) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_m18(int qp, uint64_t opc, int f1, int r2) +{ + return opc + | ((r2 & 0x7f) << 13) + | ((f1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_m19(int qp, uint64_t opc, int r1, int f2) +{ + return opc + | ((f2 & 0x7f) << 13) + | ((r1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_m34(int qp, uint64_t opc, int r1, + int sof, int sol, int sor) +{ + return opc + | ((sor & 0x0f) << 27) + | ((sol & 0x7f) << 20) + | ((sof & 0x7f) << 13) + | ((r1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_m48(int qp, uint64_t opc, uint64_t imm) +{ + return opc + | ((imm & 0x100000) << 16) /* i */ + | ((imm & 0x0fffff) << 6) /* imm20a */ + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_x2(int qp, uint64_t opc, + int r1, uint64_t imm) +{ + return opc + | ((imm & 0x8000000000000000ull) >> 27) /* i */ + | (imm & 0x0000000000200000ull) /* ic */ + | ((imm & 0x00000000001f0000ull) << 6) /* imm5c */ + | ((imm & 0x000000000000ff80ull) << 20) /* imm9d */ + | ((imm & 0x000000000000007full) << 13) /* imm7b */ + | ((r1 & 0x7f) << 6) + | (qp & 0x3f); +} + +static inline uint64_t tcg_opc_x3(int qp, uint64_t opc, uint64_t imm) +{ + return opc + | ((imm & 0x0800000000000000ull) >> 23) /* i */ + | ((imm & 0x00000000000fffffull) << 13) /* imm20b */ + | (qp & 0x3f); +} + + +/* + * Relocations + */ + +static inline void reloc_pcrel21b (void *pc, tcg_target_long target) +{ + uint64_t imm; + int64_t disp; + int slot; + + slot = (tcg_target_long) pc & 3; + pc = (void *)((tcg_target_long) pc & ~3); + + disp = target - (tcg_target_long) pc; + imm = (uint64_t) disp >> 4; + + switch(slot) { + case 0: + *(uint64_t *)(pc + 0) = (*(uint64_t *)(pc + 8) & 0xfffffdc00003ffffull) + | ((imm & 0x100000) << 21) /* s */ + | ((imm & 0x0fffff) << 18); /* imm20b */ + break; + case 1: + *(uint64_t *)(pc + 8) = (*(uint64_t *)(pc + 8) & 0xfffffffffffb8000ull) + | ((imm & 0x100000) >> 2) /* s */ + | ((imm & 0x0fffe0) >> 5); /* imm20b */ + *(uint64_t *)(pc + 0) = (*(uint64_t *)(pc + 0) & 0x07ffffffffffffffull) + | ((imm & 0x00001f) << 59); /* imm20b */ + break; + case 2: + *(uint64_t *)(pc + 8) = (*(uint64_t *)(pc + 8) & 0xf700000fffffffffull) + | ((imm & 0x100000) << 39) /* s */ + | ((imm & 0x0fffff) << 36); /* imm20b */ + break; + } +} + +static inline uint64_t get_reloc_pcrel21b (void *pc) +{ + int64_t low, high; + int slot; + + slot = (tcg_target_long) pc & 3; + pc = (void *)((tcg_target_long) pc & ~3); + + low = (*(uint64_t *)(pc + 0)); + high = (*(uint64_t *)(pc + 8)); + + switch(slot) { + case 0: + return ((low >> 21) & 0x100000) + /* s */ + ((low >> 18) & 0x0fffff); /* imm20b */ + case 1: + return ((high << 2) & 0x100000) + /* s */ + ((high << 5) & 0x0fffe0) + /* imm20b */ + ((low >> 59) & 0x00001f); /* imm20b */ + case 2: + return ((high >> 39) & 0x100000) + /* s */ + ((high >> 36) & 0x0fffff); /* imm20b */ + default: + tcg_abort(); + } +} + +static inline void reloc_pcrel60b (void *pc, tcg_target_long target) +{ + int64_t disp; + uint64_t imm; + + disp = target - (tcg_target_long) pc; + imm = (uint64_t) disp >> 4; + + *(uint64_t *)(pc + 8) = (*(uint64_t *)(pc + 8) & 0xf700000fff800000ull) + | (imm & 0x0800000000000000ull) /* s */ + | ((imm & 0x07fffff000000000ull) >> 36) /* imm39 */ + | ((imm & 0x00000000000fffffull) << 36); /* imm20b */ + *(uint64_t *)(pc + 0) = (*(uint64_t *)(pc + 0) & 0x00003fffffffffffull) + | ((imm & 0x0000000ffff00000ull) << 28); /* imm39 */ +} + +static inline uint64_t get_reloc_pcrel60b (void *pc) +{ + int64_t low, high; + + low = (*(uint64_t *)(pc + 0)); + high = (*(uint64_t *)(pc + 8)); + + return ((high) & 0x0800000000000000ull) + /* s */ + ((high >> 36) & 0x00000000000fffffull) + /* imm20b */ + ((high << 36) & 0x07fffff000000000ull) + /* imm39 */ + ((low >> 28) & 0x0000000ffff00000ull); /* imm39 */ +} + + +static void patch_reloc(uint8_t *code_ptr, int type, + tcg_target_long value, tcg_target_long addend) +{ + value += addend; + switch (type) { + case R_IA64_PCREL21B: + reloc_pcrel21b(code_ptr, value); + break; + case R_IA64_PCREL60B: + reloc_pcrel60b(code_ptr, value); + default: + tcg_abort(); + } +} + +/* + * Constraints + */ + +/* parse target specific constraints */ +static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str) +{ + const char *ct_str; + + ct_str = *pct_str; + switch(ct_str[0]) { + case 'r': + ct->ct |= TCG_CT_REG; + tcg_regset_set(ct->u.regs, 0xffffffffffffffffull); + break; + case 'I': + ct->ct |= TCG_CT_CONST_S22; + break; + case 'S': + ct->ct |= TCG_CT_REG; + tcg_regset_set(ct->u.regs, 0xffffffffffffffffull); +#if defined(CONFIG_SOFTMMU) + tcg_regset_reset_reg(ct->u.regs, TCG_REG_R56); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_R57); +#endif + break; + case 'Z': + /* We are cheating a bit here, using the fact that the register + r0 is also the register number 0. Hence there is no need + to check for const_args in each instruction. */ + ct->ct |= TCG_CT_CONST_ZERO; + break; + default: + return -1; + } + ct_str++; + *pct_str = ct_str; + return 0; +} + +/* test if a constant matches the constraint */ +static inline int tcg_target_const_match(tcg_target_long val, + const TCGArgConstraint *arg_ct) +{ + int ct; + ct = arg_ct->ct; + if (ct & TCG_CT_CONST) + return 1; + else if ((ct & TCG_CT_CONST_ZERO) && val == 0) + return 1; + else if ((ct & TCG_CT_CONST_S22) && val == ((int32_t)val << 10) >> 10) + return 1; + else + return 0; +} + +/* + * Code generation + */ + +static uint8_t *tb_ret_addr; + +static inline void tcg_out_bundle(TCGContext *s, int template, + uint64_t slot0, uint64_t slot1, + uint64_t slot2) +{ + template &= 0x1f; /* 5 bits */ + slot0 &= 0x1ffffffffffull; /* 41 bits */ + slot1 &= 0x1ffffffffffull; /* 41 bits */ + slot2 &= 0x1ffffffffffull; /* 41 bits */ + + *(uint64_t *)(s->code_ptr + 0) = (slot1 << 46) | (slot0 << 5) | template; + *(uint64_t *)(s->code_ptr + 8) = (slot2 << 23) | (slot1 >> 18); + s->code_ptr += 16; +} + +static inline void tcg_out_mov(TCGContext *s, TCGType type, + TCGArg ret, TCGArg arg) +{ + tcg_out_bundle(s, mmI, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_a4(TCG_REG_P0, OPC_ADDS_A4, ret, 0, arg)); +} + +static inline void tcg_out_movi(TCGContext *s, TCGType type, + TCGArg reg, tcg_target_long arg) +{ + tcg_out_bundle(s, mLX, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_l2 (arg), + tcg_opc_x2 (TCG_REG_P0, OPC_MOVL_X2, reg, arg)); +} + +static inline void tcg_out_addi(TCGContext *s, TCGArg reg, tcg_target_long val) +{ + if (val == ((int32_t)val << 10) >> 10) { + tcg_out_bundle(s, MmI, + tcg_opc_a5(TCG_REG_P0, OPC_ADDL_A5, + TCG_REG_R2, val, TCG_REG_R0), + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_a1 (TCG_REG_P0, OPC_ADD_A1, reg, + reg, TCG_REG_R2)); + } else { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R2, val); + tcg_out_bundle(s, mmI, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_a1 (TCG_REG_P0, OPC_ADD_A1, reg, + reg, TCG_REG_R2)); + } +} + +static void tcg_out_br(TCGContext *s, int label_index) +{ + TCGLabel *l = &s->labels[label_index]; + + /* We pay attention here to not modify the branch target by reading + the existing value and using it again. This ensure that caches and + memory are kept coherent during retranslation. */ + tcg_out_bundle(s, mmB, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_b1 (TCG_REG_P0, OPC_BR_SPTK_MANY_B1, + get_reloc_pcrel21b(s->code_ptr + 2))); + + if (l->has_value) { + reloc_pcrel21b((s->code_ptr - 16) + 2, l->u.value); + } else { + tcg_out_reloc(s, (s->code_ptr - 16) + 2, + R_IA64_PCREL21B, label_index, 0); + } +} + +static inline void tcg_out_call(TCGContext *s, TCGArg addr) +{ + tcg_out_bundle(s, MmI, + tcg_opc_m1 (TCG_REG_P0, OPC_LD8_M1, TCG_REG_R2, addr), + tcg_opc_a4 (TCG_REG_P0, OPC_ADDS_A4, TCG_REG_R3, 8, addr), + tcg_opc_i21(TCG_REG_P0, OPC_MOV_I21, + TCG_REG_B6, TCG_REG_R2, 0)); + tcg_out_bundle(s, mmB, + tcg_opc_m1 (TCG_REG_P0, OPC_LD8_M1, TCG_REG_R1, TCG_REG_R3), + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_b5 (TCG_REG_P0, OPC_BR_CALL_SPTK_MANY_B5, + TCG_REG_B0, TCG_REG_B6)); +} + +static void tcg_out_exit_tb(TCGContext *s, tcg_target_long arg) +{ + int64_t disp; + uint64_t imm; + + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R8, arg); + + disp = tb_ret_addr - s->code_ptr; + imm = (uint64_t)disp >> 4; + + tcg_out_bundle(s, mLX, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_l3 (imm), + tcg_opc_x3 (TCG_REG_P0, OPC_BRL_SPTK_MANY_X3, imm)); +} + +static inline void tcg_out_goto_tb(TCGContext *s, TCGArg arg) +{ + if (s->tb_jmp_offset) { + /* direct jump method */ + tcg_abort(); + } else { + /* indirect jump method */ + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R2, + (tcg_target_long)(s->tb_next + arg)); + tcg_out_bundle(s, MmI, + tcg_opc_m1 (TCG_REG_P0, OPC_LD8_M1, + TCG_REG_R2, TCG_REG_R2), + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i21(TCG_REG_P0, OPC_MOV_I21, TCG_REG_B6, + TCG_REG_R2, 0)); + tcg_out_bundle(s, mmB, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_b4 (TCG_REG_P0, OPC_BR_SPTK_MANY_B4, + TCG_REG_B6)); + } + s->tb_next_offset[arg] = s->code_ptr - s->code_buf; +} + +static inline void tcg_out_jmp(TCGContext *s, TCGArg addr) +{ + tcg_out_bundle(s, mmI, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i21(TCG_REG_P0, OPC_MOV_I21, TCG_REG_B6, addr, 0)); + tcg_out_bundle(s, mmB, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_b4(TCG_REG_P0, OPC_BR_SPTK_MANY_B4, TCG_REG_B6)); +} + +static inline void tcg_out_ld_rel(TCGContext *s, uint64_t opc_m4, TCGArg arg, + TCGArg arg1, tcg_target_long arg2) +{ + if (arg2 == ((int16_t)arg2 >> 2) << 2) { + tcg_out_bundle(s, MmI, + tcg_opc_a4(TCG_REG_P0, OPC_ADDS_A4, + TCG_REG_R2, arg2, arg1), + tcg_opc_m1 (TCG_REG_P0, opc_m4, arg, TCG_REG_R2), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0)); + } else { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R2, arg2); + tcg_out_bundle(s, MmI, + tcg_opc_a1 (TCG_REG_P0, OPC_ADD_A1, + TCG_REG_R2, TCG_REG_R2, arg1), + tcg_opc_m1 (TCG_REG_P0, opc_m4, arg, TCG_REG_R2), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0)); + } +} + +static inline void tcg_out_st_rel(TCGContext *s, uint64_t opc_m4, TCGArg arg, + TCGArg arg1, tcg_target_long arg2) +{ + if (arg2 == ((int16_t)arg2 >> 2) << 2) { + tcg_out_bundle(s, MmI, + tcg_opc_a4(TCG_REG_P0, OPC_ADDS_A4, + TCG_REG_R2, arg2, arg1), + tcg_opc_m4 (TCG_REG_P0, opc_m4, arg, TCG_REG_R2), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0)); + } else { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R2, arg2); + tcg_out_bundle(s, MmI, + tcg_opc_a1 (TCG_REG_P0, OPC_ADD_A1, + TCG_REG_R2, TCG_REG_R2, arg1), + tcg_opc_m4 (TCG_REG_P0, opc_m4, arg, TCG_REG_R2), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0)); + } +} + +static inline void tcg_out_ld(TCGContext *s, TCGType type, TCGArg arg, + TCGArg arg1, tcg_target_long arg2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_ld_rel(s, OPC_LD4_M1, arg, arg1, arg2); + } else { + tcg_out_ld_rel(s, OPC_LD8_M1, arg, arg1, arg2); + } +} + +static inline void tcg_out_st(TCGContext *s, TCGType type, TCGArg arg, + TCGArg arg1, tcg_target_long arg2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_st_rel(s, OPC_ST4_M4, arg, arg1, arg2); + } else { + tcg_out_st_rel(s, OPC_ST8_M4, arg, arg1, arg2); + } +} + +static inline void tcg_out_alu(TCGContext *s, uint64_t opc_a1, TCGArg ret, + TCGArg arg1, int const_arg1, + TCGArg arg2, int const_arg2) +{ + uint64_t opc1, opc2; + + if (const_arg1 && arg1 != 0) { + opc1 = tcg_opc_a5(TCG_REG_P0, OPC_ADDL_A5, + TCG_REG_R2, arg1, TCG_REG_R0); + arg1 = TCG_REG_R2; + } else { + opc1 = tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0); + } + + if (const_arg2 && arg2 != 0) { + opc2 = tcg_opc_a5(TCG_REG_P0, OPC_ADDL_A5, + TCG_REG_R3, arg2, TCG_REG_R0); + arg2 = TCG_REG_R3; + } else { + opc2 = tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0); + } + + tcg_out_bundle(s, mII, + opc1, + opc2, + tcg_opc_a1(TCG_REG_P0, opc_a1, ret, arg1, arg2)); +} + +static inline void tcg_out_eqv(TCGContext *s, TCGArg ret, + TCGArg arg1, int const_arg1, + TCGArg arg2, int const_arg2) +{ + tcg_out_bundle(s, mII, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_a1 (TCG_REG_P0, OPC_XOR_A1, ret, arg1, arg2), + tcg_opc_a3 (TCG_REG_P0, OPC_ANDCM_A3, ret, -1, ret)); +} + +static inline void tcg_out_nand(TCGContext *s, TCGArg ret, + TCGArg arg1, int const_arg1, + TCGArg arg2, int const_arg2) +{ + tcg_out_bundle(s, mII, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_a1 (TCG_REG_P0, OPC_AND_A1, ret, arg1, arg2), + tcg_opc_a3 (TCG_REG_P0, OPC_ANDCM_A3, ret, -1, ret)); +} + +static inline void tcg_out_nor(TCGContext *s, TCGArg ret, + TCGArg arg1, int const_arg1, + TCGArg arg2, int const_arg2) +{ + tcg_out_bundle(s, mII, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_a1 (TCG_REG_P0, OPC_OR_A1, ret, arg1, arg2), + tcg_opc_a3 (TCG_REG_P0, OPC_ANDCM_A3, ret, -1, ret)); +} + +static inline void tcg_out_orc(TCGContext *s, TCGArg ret, + TCGArg arg1, int const_arg1, + TCGArg arg2, int const_arg2) +{ + tcg_out_bundle(s, mII, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_a3 (TCG_REG_P0, OPC_ANDCM_A3, TCG_REG_R2, -1, arg2), + tcg_opc_a1 (TCG_REG_P0, OPC_OR_A1, ret, arg1, TCG_REG_R2)); +} + +static inline void tcg_out_mul(TCGContext *s, TCGArg ret, + TCGArg arg1, TCGArg arg2) +{ + tcg_out_bundle(s, mmI, + tcg_opc_m18(TCG_REG_P0, OPC_SETF_SIG_M18, TCG_REG_F6, arg1), + tcg_opc_m18(TCG_REG_P0, OPC_SETF_SIG_M18, TCG_REG_F7, arg2), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0)); + tcg_out_bundle(s, mmF, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_f2 (TCG_REG_P0, OPC_XMA_L_F2, TCG_REG_F6, TCG_REG_F6, + TCG_REG_F7, TCG_REG_F0)); + tcg_out_bundle(s, miI, + tcg_opc_m19(TCG_REG_P0, OPC_GETF_SIG_M19, ret, TCG_REG_F6), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0)); +} + +static inline void tcg_out_sar_i32(TCGContext *s, TCGArg ret, TCGArg arg1, + TCGArg arg2, int const_arg2) +{ + if (const_arg2) { + tcg_out_bundle(s, miI, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_i11(TCG_REG_P0, OPC_EXTR_I11, + ret, arg1, arg2, 31 - arg2)); + } else { + tcg_out_bundle(s, mII, + tcg_opc_a3 (TCG_REG_P0, OPC_AND_A3, + TCG_REG_R3, 0x1f, arg2), + tcg_opc_i29(TCG_REG_P0, OPC_SXT4_I29, TCG_REG_R2, arg1), + tcg_opc_i5 (TCG_REG_P0, OPC_SHR_I5, ret, + TCG_REG_R2, TCG_REG_R3)); + } +} + +static inline void tcg_out_sar_i64(TCGContext *s, TCGArg ret, TCGArg arg1, + TCGArg arg2, int const_arg2) +{ + if (const_arg2) { + tcg_out_bundle(s, miI, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_i11(TCG_REG_P0, OPC_EXTR_I11, + ret, arg1, arg2, 63 - arg2)); + } else { + tcg_out_bundle(s, miI, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_i5 (TCG_REG_P0, OPC_SHR_I5, ret, arg1, arg2)); + } +} + +static inline void tcg_out_shl_i32(TCGContext *s, TCGArg ret, TCGArg arg1, + TCGArg arg2, int const_arg2) +{ + if (const_arg2) { + tcg_out_bundle(s, miI, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, ret, + arg1, 63 - arg2, 31 - arg2)); + } else { + tcg_out_bundle(s, mII, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_a3 (TCG_REG_P0, OPC_AND_A3, TCG_REG_R2, + 0x1f, arg2), + tcg_opc_i7 (TCG_REG_P0, OPC_SHL_I7, ret, + arg1, TCG_REG_R2)); + } +} + +static inline void tcg_out_shl_i64(TCGContext *s, TCGArg ret, TCGArg arg1, + TCGArg arg2, int const_arg2) +{ + if (const_arg2) { + tcg_out_bundle(s, miI, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, ret, + arg1, 63 - arg2, 63 - arg2)); + } else { + tcg_out_bundle(s, miI, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_i7 (TCG_REG_P0, OPC_SHL_I7, ret, + arg1, arg2)); + } +} + +static inline void tcg_out_shr_i32(TCGContext *s, TCGArg ret, TCGArg arg1, + TCGArg arg2, int const_arg2) +{ + if (const_arg2) { + tcg_out_bundle(s, miI, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_i11(TCG_REG_P0, OPC_EXTR_U_I11, ret, + arg1, arg2, 31 - arg2)); + } else { + tcg_out_bundle(s, mII, + tcg_opc_a3 (TCG_REG_P0, OPC_AND_A3, TCG_REG_R3, + 0x1f, arg2), + tcg_opc_i29(TCG_REG_P0, OPC_ZXT4_I29, TCG_REG_R2, arg1), + tcg_opc_i5 (TCG_REG_P0, OPC_SHR_U_I5, ret, + TCG_REG_R2, TCG_REG_R3)); + } +} + +static inline void tcg_out_shr_i64(TCGContext *s, TCGArg ret, TCGArg arg1, + TCGArg arg2, int const_arg2) +{ + if (const_arg2) { + tcg_out_bundle(s, miI, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_i11(TCG_REG_P0, OPC_EXTR_U_I11, ret, + arg1, arg2, 63 - arg2)); + } else { + tcg_out_bundle(s, miI, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_i5 (TCG_REG_P0, OPC_SHR_U_I5, ret, + arg1, arg2)); + } +} + +static inline void tcg_out_rotl_i32(TCGContext *s, TCGArg ret, TCGArg arg1, + TCGArg arg2, int const_arg2) +{ + if (const_arg2) { + tcg_out_bundle(s, mII, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i2 (TCG_REG_P0, OPC_UNPACK4_L_I2, + TCG_REG_R2, arg1, arg1), + tcg_opc_i11(TCG_REG_P0, OPC_EXTR_U_I11, ret, + TCG_REG_R2, 32 - arg2, 31)); + } else { + tcg_out_bundle(s, miI, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i2 (TCG_REG_P0, OPC_UNPACK4_L_I2, + TCG_REG_R2, arg1, arg1), + tcg_opc_a3 (TCG_REG_P0, OPC_AND_A3, TCG_REG_R3, + 0x1f, arg2)); + tcg_out_bundle(s, mII, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_a3 (TCG_REG_P0, OPC_SUB_A3, TCG_REG_R3, + 0x20, TCG_REG_R3), + tcg_opc_i5 (TCG_REG_P0, OPC_SHR_U_I5, ret, + TCG_REG_R2, TCG_REG_R3)); + } +} + +static inline void tcg_out_rotl_i64(TCGContext *s, TCGArg ret, TCGArg arg1, + TCGArg arg2, int const_arg2) +{ + if (const_arg2) { + tcg_out_bundle(s, miI, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_i10(TCG_REG_P0, OPC_SHRP_I10, ret, arg1, + arg1, 0x40 - arg2)); + } else { + tcg_out_bundle(s, mII, + tcg_opc_a3 (TCG_REG_P0, OPC_SUB_A3, TCG_REG_R2, + 0x40, arg2), + tcg_opc_i7 (TCG_REG_P0, OPC_SHL_I7, TCG_REG_R3, + arg1, arg2), + tcg_opc_i5 (TCG_REG_P0, OPC_SHR_U_I5, TCG_REG_R2, + arg1, TCG_REG_R2)); + tcg_out_bundle(s, miI, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_a1 (TCG_REG_P0, OPC_OR_A1, ret, + TCG_REG_R2, TCG_REG_R3)); + } +} + +static inline void tcg_out_rotr_i32(TCGContext *s, TCGArg ret, TCGArg arg1, + TCGArg arg2, int const_arg2) +{ + if (const_arg2) { + tcg_out_bundle(s, mII, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i2 (TCG_REG_P0, OPC_UNPACK4_L_I2, + TCG_REG_R2, arg1, arg1), + tcg_opc_i11(TCG_REG_P0, OPC_EXTR_U_I11, ret, + TCG_REG_R2, arg2, 31)); + } else { + tcg_out_bundle(s, mII, + tcg_opc_a3 (TCG_REG_P0, OPC_AND_A3, TCG_REG_R3, + 0x1f, arg2), + tcg_opc_i2 (TCG_REG_P0, OPC_UNPACK4_L_I2, + TCG_REG_R2, arg1, arg1), + tcg_opc_i5 (TCG_REG_P0, OPC_SHR_U_I5, ret, + TCG_REG_R2, TCG_REG_R3)); + } +} + +static inline void tcg_out_rotr_i64(TCGContext *s, TCGArg ret, TCGArg arg1, + TCGArg arg2, int const_arg2) +{ + if (const_arg2) { + tcg_out_bundle(s, miI, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_i10(TCG_REG_P0, OPC_SHRP_I10, ret, arg1, + arg1, arg2)); + } else { + tcg_out_bundle(s, mII, + tcg_opc_a3 (TCG_REG_P0, OPC_SUB_A3, TCG_REG_R2, + 0x40, arg2), + tcg_opc_i5 (TCG_REG_P0, OPC_SHR_U_I5, TCG_REG_R3, + arg1, arg2), + tcg_opc_i7 (TCG_REG_P0, OPC_SHL_I7, TCG_REG_R2, + arg1, TCG_REG_R2)); + tcg_out_bundle(s, miI, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_a1 (TCG_REG_P0, OPC_OR_A1, ret, + TCG_REG_R2, TCG_REG_R3)); + } +} + +static inline void tcg_out_ext(TCGContext *s, uint64_t opc_i29, + TCGArg ret, TCGArg arg) +{ + tcg_out_bundle(s, miI, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_i29(TCG_REG_P0, opc_i29, ret, arg)); +} + +static inline void tcg_out_bswap16(TCGContext *s, TCGArg ret, TCGArg arg) +{ + tcg_out_bundle(s, mII, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, ret, arg, 15, 15), + tcg_opc_i3 (TCG_REG_P0, OPC_MUX1_I3, ret, ret, 0xb)); +} + +static inline void tcg_out_bswap32(TCGContext *s, TCGArg ret, TCGArg arg) +{ + tcg_out_bundle(s, mII, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, ret, arg, 31, 31), + tcg_opc_i3 (TCG_REG_P0, OPC_MUX1_I3, ret, ret, 0xb)); +} + +static inline void tcg_out_bswap64(TCGContext *s, TCGArg ret, TCGArg arg) +{ + tcg_out_bundle(s, miI, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_i3 (TCG_REG_P0, OPC_MUX1_I3, ret, arg, 0xb)); +} + +static inline uint64_t tcg_opc_cmp_a(int qp, TCGCond cond, TCGArg arg1, + TCGArg arg2, int cmp4) +{ + uint64_t opc_eq_a6, opc_lt_a6, opc_ltu_a6; + + if (cmp4) { + opc_eq_a6 = OPC_CMP4_EQ_A6; + opc_lt_a6 = OPC_CMP4_LT_A6; + opc_ltu_a6 = OPC_CMP4_LTU_A6; + } else { + opc_eq_a6 = OPC_CMP_EQ_A6; + opc_lt_a6 = OPC_CMP_LT_A6; + opc_ltu_a6 = OPC_CMP_LTU_A6; + } + + switch (cond) { + case TCG_COND_EQ: + return tcg_opc_a6 (qp, opc_eq_a6, TCG_REG_P6, TCG_REG_P7, arg1, arg2); + case TCG_COND_NE: + return tcg_opc_a6 (qp, opc_eq_a6, TCG_REG_P7, TCG_REG_P6, arg1, arg2); + case TCG_COND_LT: + return tcg_opc_a6 (qp, opc_lt_a6, TCG_REG_P6, TCG_REG_P7, arg1, arg2); + case TCG_COND_LTU: + return tcg_opc_a6 (qp, opc_ltu_a6, TCG_REG_P6, TCG_REG_P7, arg1, arg2); + case TCG_COND_GE: + return tcg_opc_a6 (qp, opc_lt_a6, TCG_REG_P7, TCG_REG_P6, arg1, arg2); + case TCG_COND_GEU: + return tcg_opc_a6 (qp, opc_ltu_a6, TCG_REG_P7, TCG_REG_P6, arg1, arg2); + case TCG_COND_LE: + return tcg_opc_a6 (qp, opc_lt_a6, TCG_REG_P7, TCG_REG_P6, arg2, arg1); + case TCG_COND_LEU: + return tcg_opc_a6 (qp, opc_ltu_a6, TCG_REG_P7, TCG_REG_P6, arg2, arg1); + case TCG_COND_GT: + return tcg_opc_a6 (qp, opc_lt_a6, TCG_REG_P6, TCG_REG_P7, arg2, arg1); + case TCG_COND_GTU: + return tcg_opc_a6 (qp, opc_ltu_a6, TCG_REG_P6, TCG_REG_P7, arg2, arg1); + default: + tcg_abort(); + break; + } +} + +static inline void tcg_out_brcond(TCGContext *s, TCGCond cond, TCGArg arg1, + int const_arg1, TCGArg arg2, int const_arg2, + int label_index, int cmp4) +{ + TCGLabel *l = &s->labels[label_index]; + uint64_t opc1, opc2; + + if (const_arg1 && arg1 != 0) { + opc1 = tcg_opc_a5(TCG_REG_P0, OPC_ADDL_A5, TCG_REG_R2, + arg1, TCG_REG_R0); + arg1 = TCG_REG_R2; + } else { + opc1 = tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0); + } + + if (const_arg2 && arg2 != 0) { + opc2 = tcg_opc_a5(TCG_REG_P0, OPC_ADDL_A5, TCG_REG_R3, + arg2, TCG_REG_R0); + arg2 = TCG_REG_R3; + } else { + opc2 = tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0); + } + + tcg_out_bundle(s, mII, + opc1, + opc2, + tcg_opc_cmp_a(TCG_REG_P0, cond, arg1, arg2, cmp4)); + tcg_out_bundle(s, mmB, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_b1 (TCG_REG_P6, OPC_BR_DPTK_FEW_B1, + get_reloc_pcrel21b(s->code_ptr + 2))); + + if (l->has_value) { + reloc_pcrel21b((s->code_ptr - 16) + 2, l->u.value); + } else { + tcg_out_reloc(s, (s->code_ptr - 16) + 2, + R_IA64_PCREL21B, label_index, 0); + } +} + +static inline void tcg_out_setcond(TCGContext *s, TCGCond cond, TCGArg ret, + TCGArg arg1, TCGArg arg2, int cmp4) +{ + tcg_out_bundle(s, MmI, + tcg_opc_cmp_a(TCG_REG_P0, cond, arg1, arg2, cmp4), + tcg_opc_a5(TCG_REG_P6, OPC_ADDL_A5, ret, 1, TCG_REG_R0), + tcg_opc_a5(TCG_REG_P7, OPC_ADDL_A5, ret, 0, TCG_REG_R0)); +} + +#if defined(CONFIG_SOFTMMU) + +#include "../../softmmu_defs.h" + +/* Load and compare a TLB entry, and return the result in (p6, p7). + R2 is loaded with the address of the addend TLB entry. + R56 is loaded with the address, zero extented on 32-bit targets. */ +static inline void tcg_out_qemu_tlb(TCGContext *s, TCGArg addr_reg, + int s_bits, uint64_t offset_rw, + uint64_t offset_addend) +{ + tcg_out_bundle(s, mII, + tcg_opc_a5 (TCG_REG_P0, OPC_ADDL_A5, TCG_REG_R3, + TARGET_PAGE_MASK | ((1 << s_bits) - 1), + TCG_REG_R0), + tcg_opc_i11(TCG_REG_P0, OPC_EXTR_U_I11, TCG_REG_R2, + addr_reg, TARGET_PAGE_BITS, CPU_TLB_BITS - 1), + tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, TCG_REG_R2, + TCG_REG_R2, 63 - CPU_TLB_ENTRY_BITS, + 63 - CPU_TLB_ENTRY_BITS)); + tcg_out_bundle(s, mII, + tcg_opc_a5 (TCG_REG_P0, OPC_ADDL_A5, TCG_REG_R2, + offset_rw, TCG_REG_R2), +#if TARGET_LONG_BITS == 32 + tcg_opc_i29(TCG_REG_P0, OPC_ZXT4_I29, TCG_REG_R56, addr_reg), +#else + tcg_opc_a4(TCG_REG_P0, OPC_ADDS_A4, TCG_REG_R56, + 0, addr_reg), +#endif + tcg_opc_a1 (TCG_REG_P0, OPC_ADD_A1, TCG_REG_R2, + TCG_REG_R2, TCG_AREG0)); + tcg_out_bundle(s, mII, + tcg_opc_m3 (TCG_REG_P0, + (TARGET_LONG_BITS == 32 + ? OPC_LD4_M3 : OPC_LD8_M3), TCG_REG_R57, + TCG_REG_R2, offset_addend - offset_rw), + tcg_opc_a1 (TCG_REG_P0, OPC_AND_A1, TCG_REG_R3, + TCG_REG_R3, TCG_REG_R56), + tcg_opc_a6 (TCG_REG_P0, OPC_CMP_EQ_A6, TCG_REG_P6, + TCG_REG_P7, TCG_REG_R3, TCG_REG_R57)); +} + +static void *qemu_ld_helpers[4] = { + __ldb_mmu, + __ldw_mmu, + __ldl_mmu, + __ldq_mmu, +}; + +static inline void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, int opc) +{ + int addr_reg, data_reg, mem_index, s_bits, bswap; + uint64_t opc_ld_m1[4] = { OPC_LD1_M1, OPC_LD2_M1, OPC_LD4_M1, OPC_LD8_M1 }; + uint64_t opc_ext_i29[8] = { OPC_ZXT1_I29, OPC_ZXT2_I29, OPC_ZXT4_I29, 0, + OPC_SXT1_I29, OPC_SXT2_I29, OPC_SXT4_I29, 0 }; + + data_reg = *args++; + addr_reg = *args++; + mem_index = *args; + s_bits = opc & 3; + +#ifdef TARGET_WORDS_BIGENDIAN + bswap = 1; +#else + bswap = 0; +#endif + + /* Read the TLB entry */ + tcg_out_qemu_tlb(s, addr_reg, s_bits, + offsetof(CPUState, tlb_table[mem_index][0].addr_read), + offsetof(CPUState, tlb_table[mem_index][0].addend)); + + /* P6 is the fast path, and P7 the slow path */ + tcg_out_bundle(s, mLX, + tcg_opc_a5 (TCG_REG_P7, OPC_ADDL_A5, TCG_REG_R57, + mem_index, TCG_REG_R0), + tcg_opc_l2 ((tcg_target_long) qemu_ld_helpers[s_bits]), + tcg_opc_x2 (TCG_REG_P7, OPC_MOVL_X2, TCG_REG_R2, + (tcg_target_long) qemu_ld_helpers[s_bits])); + tcg_out_bundle(s, MmI, + tcg_opc_m3 (TCG_REG_P0, OPC_LD8_M3, TCG_REG_R3, + TCG_REG_R2, 8), + tcg_opc_a1 (TCG_REG_P6, OPC_ADD_A1, TCG_REG_R3, + TCG_REG_R3, TCG_REG_R56), + tcg_opc_i21(TCG_REG_P7, OPC_MOV_I21, TCG_REG_B6, + TCG_REG_R3, 0)); + if (bswap && s_bits == 1) { + tcg_out_bundle(s, MmI, + tcg_opc_m1 (TCG_REG_P6, opc_ld_m1[s_bits], + TCG_REG_R8, TCG_REG_R3), + tcg_opc_m1 (TCG_REG_P7, OPC_LD8_M1, TCG_REG_R1, TCG_REG_R2), + tcg_opc_i12(TCG_REG_P6, OPC_DEP_Z_I12, + TCG_REG_R8, TCG_REG_R8, 15, 15)); + } else if (bswap && s_bits == 2) { + tcg_out_bundle(s, MmI, + tcg_opc_m1 (TCG_REG_P6, opc_ld_m1[s_bits], + TCG_REG_R8, TCG_REG_R3), + tcg_opc_m1 (TCG_REG_P7, OPC_LD8_M1, TCG_REG_R1, TCG_REG_R2), + tcg_opc_i12(TCG_REG_P6, OPC_DEP_Z_I12, + TCG_REG_R8, TCG_REG_R8, 31, 31)); + } else { + tcg_out_bundle(s, mmI, + tcg_opc_m1 (TCG_REG_P6, opc_ld_m1[s_bits], + TCG_REG_R8, TCG_REG_R3), + tcg_opc_m1 (TCG_REG_P7, OPC_LD8_M1, TCG_REG_R1, TCG_REG_R2), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0)); + } + if (!bswap || s_bits == 0) { + tcg_out_bundle(s, miB, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_b5 (TCG_REG_P7, OPC_BR_CALL_SPTK_MANY_B5, + TCG_REG_B0, TCG_REG_B6)); + } else { + tcg_out_bundle(s, miB, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i3 (TCG_REG_P6, OPC_MUX1_I3, + TCG_REG_R8, TCG_REG_R8, 0xb), + tcg_opc_b5 (TCG_REG_P7, OPC_BR_CALL_SPTK_MANY_B5, + TCG_REG_B0, TCG_REG_B6)); + } + + if (opc == 3) { + tcg_out_bundle(s, miI, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_a4 (TCG_REG_P0, OPC_ADDS_A4, + data_reg, 0, TCG_REG_R8)); + } else { + tcg_out_bundle(s, miI, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_i29(TCG_REG_P0, opc_ext_i29[opc], + data_reg, TCG_REG_R8)); + } +} + +static void *qemu_st_helpers[4] = { + __stb_mmu, + __stw_mmu, + __stl_mmu, + __stq_mmu, +}; + +static inline void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, int opc) +{ + int addr_reg, data_reg, mem_index, bswap; + uint64_t opc_st_m4[4] = { OPC_ST1_M4, OPC_ST2_M4, OPC_ST4_M4, OPC_ST8_M4 }; + + data_reg = *args++; + addr_reg = *args++; + mem_index = *args; + +#ifdef TARGET_WORDS_BIGENDIAN + bswap = 1; +#else + bswap = 0; +#endif + + tcg_out_qemu_tlb(s, addr_reg, opc, + offsetof(CPUState, tlb_table[mem_index][0].addr_write), + offsetof(CPUState, tlb_table[mem_index][0].addend)); + + /* P6 is the fast path, and P7 the slow path */ + tcg_out_bundle(s, mLX, + tcg_opc_a4(TCG_REG_P7, OPC_ADDS_A4, TCG_REG_R57, + 0, data_reg), + tcg_opc_l2 ((tcg_target_long) qemu_st_helpers[opc]), + tcg_opc_x2 (TCG_REG_P7, OPC_MOVL_X2, TCG_REG_R2, + (tcg_target_long) qemu_st_helpers[opc])); + tcg_out_bundle(s, MmI, + tcg_opc_m3 (TCG_REG_P0, OPC_LD8_M3, TCG_REG_R3, + TCG_REG_R2, 8), + tcg_opc_a1 (TCG_REG_P6, OPC_ADD_A1, TCG_REG_R3, + TCG_REG_R3, TCG_REG_R56), + tcg_opc_i21(TCG_REG_P7, OPC_MOV_I21, TCG_REG_B6, + TCG_REG_R3, 0)); + + if (!bswap || opc == 0) { + tcg_out_bundle(s, mII, + tcg_opc_m1 (TCG_REG_P7, OPC_LD8_M1, + TCG_REG_R1, TCG_REG_R2), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0)); + } else if (opc == 1) { + tcg_out_bundle(s, mII, + tcg_opc_m1 (TCG_REG_P7, OPC_LD8_M1, + TCG_REG_R1, TCG_REG_R2), + tcg_opc_i12(TCG_REG_P6, OPC_DEP_Z_I12, + TCG_REG_R2, data_reg, 15, 15), + tcg_opc_i3 (TCG_REG_P6, OPC_MUX1_I3, + TCG_REG_R2, TCG_REG_R2, 0xb)); + data_reg = TCG_REG_R2; + } else if (opc == 2) { + tcg_out_bundle(s, mII, + tcg_opc_m1 (TCG_REG_P7, OPC_LD8_M1, + TCG_REG_R1, TCG_REG_R2), + tcg_opc_i12(TCG_REG_P6, OPC_DEP_Z_I12, + TCG_REG_R2, data_reg, 31, 31), + tcg_opc_i3 (TCG_REG_P6, OPC_MUX1_I3, + TCG_REG_R2, TCG_REG_R2, 0xb)); + data_reg = TCG_REG_R2; + } else if (opc == 3) { + tcg_out_bundle(s, miI, + tcg_opc_m1 (TCG_REG_P7, OPC_LD8_M1, + TCG_REG_R1, TCG_REG_R2), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_i3 (TCG_REG_P6, OPC_MUX1_I3, + TCG_REG_R2, data_reg, 0xb)); + data_reg = TCG_REG_R2; + } + + tcg_out_bundle(s, miB, + tcg_opc_m4 (TCG_REG_P6, opc_st_m4[opc], + data_reg, TCG_REG_R3), + tcg_opc_a5 (TCG_REG_P7, OPC_ADDL_A5, TCG_REG_R58, + mem_index, TCG_REG_R0), + tcg_opc_b5 (TCG_REG_P7, OPC_BR_CALL_SPTK_MANY_B5, + TCG_REG_B0, TCG_REG_B6)); +} + +#else /* !CONFIG_SOFTMMU */ + +static inline void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, int opc) +{ + static uint64_t const opc_ld_m1[4] = { + OPC_LD1_M1, OPC_LD2_M1, OPC_LD4_M1, OPC_LD8_M1 + }; + static uint64_t const opc_sxt_i29[4] = { + OPC_SXT1_I29, OPC_SXT2_I29, OPC_SXT4_I29, 0 + }; + int addr_reg, data_reg, s_bits, bswap; + + data_reg = *args++; + addr_reg = *args++; + s_bits = opc & 3; + +#ifdef TARGET_WORDS_BIGENDIAN + bswap = 1; +#else + bswap = 0; +#endif + +#if TARGET_LONG_BITS == 32 + if (GUEST_BASE != 0) { + tcg_out_bundle(s, mII, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i29(TCG_REG_P0, OPC_ZXT4_I29, + TCG_REG_R3, addr_reg), + tcg_opc_a1 (TCG_REG_P0, OPC_ADD_A1, TCG_REG_R2, + TCG_GUEST_BASE_REG, TCG_REG_R3)); + } else { + tcg_out_bundle(s, miI, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i29(TCG_REG_P0, OPC_ZXT4_I29, + TCG_REG_R2, addr_reg), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0)); + } + + if (!bswap || s_bits == 0) { + if (s_bits == opc) { + tcg_out_bundle(s, miI, + tcg_opc_m1 (TCG_REG_P0, opc_ld_m1[s_bits], + data_reg, TCG_REG_R2), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0)); + } else { + tcg_out_bundle(s, mII, + tcg_opc_m1 (TCG_REG_P0, opc_ld_m1[s_bits], + data_reg, TCG_REG_R2), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_i29(TCG_REG_P0, opc_sxt_i29[s_bits], + data_reg, data_reg)); + } + } else if (s_bits == 3) { + tcg_out_bundle(s, mII, + tcg_opc_m1 (TCG_REG_P0, opc_ld_m1[s_bits], + data_reg, TCG_REG_R2), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_i3 (TCG_REG_P0, OPC_MUX1_I3, + data_reg, data_reg, 0xb)); + } else { + if (s_bits == 1) { + tcg_out_bundle(s, mII, + tcg_opc_m1 (TCG_REG_P0, opc_ld_m1[s_bits], + data_reg, TCG_REG_R2), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, + data_reg, data_reg, 15, 15)); + } else { + tcg_out_bundle(s, mII, + tcg_opc_m1 (TCG_REG_P0, opc_ld_m1[s_bits], + data_reg, TCG_REG_R2), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, + data_reg, data_reg, 31, 31)); + } + if (opc == s_bits) { + tcg_out_bundle(s, miI, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_i3 (TCG_REG_P0, OPC_MUX1_I3, + data_reg, data_reg, 0xb)); + } else { + tcg_out_bundle(s, mII, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i3 (TCG_REG_P0, OPC_MUX1_I3, + data_reg, data_reg, 0xb), + tcg_opc_i29(TCG_REG_P0, opc_sxt_i29[s_bits], + data_reg, data_reg)); + } + } +#else + if (GUEST_BASE != 0) { + tcg_out_bundle(s, MmI, + tcg_opc_a1 (TCG_REG_P0, OPC_ADD_A1, TCG_REG_R2, + TCG_GUEST_BASE_REG, addr_reg), + tcg_opc_m1 (TCG_REG_P0, opc_ld_m1[s_bits], + data_reg, TCG_REG_R2), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0)); + } else { + tcg_out_bundle(s, mmI, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_m1 (TCG_REG_P0, opc_ld_m1[s_bits], + data_reg, addr_reg), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0)); + } + + if (bswap && s_bits == 1) { + tcg_out_bundle(s, mII, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, + data_reg, data_reg, 15, 15), + tcg_opc_i3 (TCG_REG_P0, OPC_MUX1_I3, + data_reg, data_reg, 0xb)); + } else if (bswap && s_bits == 2) { + tcg_out_bundle(s, mII, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, + data_reg, data_reg, 31, 31), + tcg_opc_i3 (TCG_REG_P0, OPC_MUX1_I3, + data_reg, data_reg, 0xb)); + } else if (bswap && s_bits == 3) { + tcg_out_bundle(s, miI, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_i3 (TCG_REG_P0, OPC_MUX1_I3, + data_reg, data_reg, 0xb)); + } + if (s_bits != opc) { + tcg_out_bundle(s, miI, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_i29(TCG_REG_P0, opc_sxt_i29[s_bits], + data_reg, data_reg)); + } +#endif +} + +static inline void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, int opc) +{ + static uint64_t const opc_st_m4[4] = { + OPC_ST1_M4, OPC_ST2_M4, OPC_ST4_M4, OPC_ST8_M4 + }; + int addr_reg, data_reg, bswap; +#if TARGET_LONG_BITS == 64 + uint64_t add_guest_base; +#endif + + data_reg = *args++; + addr_reg = *args++; + +#ifdef TARGET_WORDS_BIGENDIAN + bswap = 1; +#else + bswap = 0; +#endif + +#if TARGET_LONG_BITS == 32 + if (GUEST_BASE != 0) { + tcg_out_bundle(s, mII, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i29(TCG_REG_P0, OPC_ZXT4_I29, + TCG_REG_R3, addr_reg), + tcg_opc_a1 (TCG_REG_P0, OPC_ADD_A1, TCG_REG_R2, + TCG_GUEST_BASE_REG, TCG_REG_R3)); + } else { + tcg_out_bundle(s, miI, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i29(TCG_REG_P0, OPC_ZXT4_I29, + TCG_REG_R2, addr_reg), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0)); + } + + if (bswap) { + if (opc == 1) { + tcg_out_bundle(s, mII, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, + TCG_REG_R3, data_reg, 15, 15), + tcg_opc_i3 (TCG_REG_P0, OPC_MUX1_I3, + TCG_REG_R3, TCG_REG_R3, 0xb)); + data_reg = TCG_REG_R3; + } else if (opc == 2) { + tcg_out_bundle(s, mII, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, + TCG_REG_R3, data_reg, 31, 31), + tcg_opc_i3 (TCG_REG_P0, OPC_MUX1_I3, + TCG_REG_R3, TCG_REG_R3, 0xb)); + data_reg = TCG_REG_R3; + } else if (opc == 3) { + tcg_out_bundle(s, miI, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_i3 (TCG_REG_P0, OPC_MUX1_I3, + TCG_REG_R3, data_reg, 0xb)); + data_reg = TCG_REG_R3; + } + } + tcg_out_bundle(s, mmI, + tcg_opc_m4 (TCG_REG_P0, opc_st_m4[opc], + data_reg, TCG_REG_R2), + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0)); +#else + if (GUEST_BASE != 0) { + add_guest_base = tcg_opc_a1 (TCG_REG_P0, OPC_ADD_A1, TCG_REG_R2, + TCG_GUEST_BASE_REG, addr_reg); + addr_reg = TCG_REG_R2; + } else { + add_guest_base = tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0); + } + + if (!bswap || opc == 0) { + tcg_out_bundle(s, (GUEST_BASE ? MmI : mmI), + add_guest_base, + tcg_opc_m4 (TCG_REG_P0, opc_st_m4[opc], + data_reg, addr_reg), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0)); + } else { + if (opc == 1) { + tcg_out_bundle(s, mII, + add_guest_base, + tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, + TCG_REG_R3, data_reg, 15, 15), + tcg_opc_i3 (TCG_REG_P0, OPC_MUX1_I3, + TCG_REG_R3, TCG_REG_R3, 0xb)); + data_reg = TCG_REG_R3; + } else if (opc == 2) { + tcg_out_bundle(s, mII, + add_guest_base, + tcg_opc_i12(TCG_REG_P0, OPC_DEP_Z_I12, + TCG_REG_R3, data_reg, 31, 31), + tcg_opc_i3 (TCG_REG_P0, OPC_MUX1_I3, + TCG_REG_R3, TCG_REG_R3, 0xb)); + data_reg = TCG_REG_R3; + } else if (opc == 3) { + tcg_out_bundle(s, miI, + add_guest_base, + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_i3 (TCG_REG_P0, OPC_MUX1_I3, + TCG_REG_R3, data_reg, 0xb)); + data_reg = TCG_REG_R3; + } + tcg_out_bundle(s, miI, + tcg_opc_m4 (TCG_REG_P0, opc_st_m4[opc], + data_reg, addr_reg), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0), + tcg_opc_i18(TCG_REG_P0, OPC_NOP_I18, 0)); + } +#endif +} + +#endif + +static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, + const TCGArg *args, const int *const_args) +{ + switch(opc) { + case INDEX_op_exit_tb: + tcg_out_exit_tb(s, args[0]); + break; + case INDEX_op_br: + tcg_out_br(s, args[0]); + break; + case INDEX_op_call: + tcg_out_call(s, args[0]); + break; + case INDEX_op_goto_tb: + tcg_out_goto_tb(s, args[0]); + break; + case INDEX_op_jmp: + tcg_out_jmp(s, args[0]); + break; + + case INDEX_op_movi_i32: + tcg_out_movi(s, TCG_TYPE_I32, args[0], args[1]); + break; + case INDEX_op_movi_i64: + tcg_out_movi(s, TCG_TYPE_I64, args[0], args[1]); + break; + + case INDEX_op_ld8u_i32: + case INDEX_op_ld8u_i64: + tcg_out_ld_rel(s, OPC_LD1_M1, args[0], args[1], args[2]); + break; + case INDEX_op_ld8s_i32: + case INDEX_op_ld8s_i64: + tcg_out_ld_rel(s, OPC_LD1_M1, args[0], args[1], args[2]); + tcg_out_ext(s, OPC_SXT1_I29, args[0], args[0]); + break; + case INDEX_op_ld16u_i32: + case INDEX_op_ld16u_i64: + tcg_out_ld_rel(s, OPC_LD2_M1, args[0], args[1], args[2]); + break; + case INDEX_op_ld16s_i32: + case INDEX_op_ld16s_i64: + tcg_out_ld_rel(s, OPC_LD2_M1, args[0], args[1], args[2]); + tcg_out_ext(s, OPC_SXT2_I29, args[0], args[0]); + break; + case INDEX_op_ld_i32: + case INDEX_op_ld32u_i64: + tcg_out_ld_rel(s, OPC_LD4_M1, args[0], args[1], args[2]); + break; + case INDEX_op_ld32s_i64: + tcg_out_ld_rel(s, OPC_LD4_M1, args[0], args[1], args[2]); + tcg_out_ext(s, OPC_SXT4_I29, args[0], args[0]); + break; + case INDEX_op_ld_i64: + tcg_out_ld_rel(s, OPC_LD8_M1, args[0], args[1], args[2]); + break; + case INDEX_op_st8_i32: + case INDEX_op_st8_i64: + tcg_out_st_rel(s, OPC_ST1_M4, args[0], args[1], args[2]); + break; + case INDEX_op_st16_i32: + case INDEX_op_st16_i64: + tcg_out_st_rel(s, OPC_ST2_M4, args[0], args[1], args[2]); + break; + case INDEX_op_st_i32: + case INDEX_op_st32_i64: + tcg_out_st_rel(s, OPC_ST4_M4, args[0], args[1], args[2]); + break; + case INDEX_op_st_i64: + tcg_out_st_rel(s, OPC_ST8_M4, args[0], args[1], args[2]); + break; + + case INDEX_op_add_i32: + case INDEX_op_add_i64: + tcg_out_alu(s, OPC_ADD_A1, args[0], args[1], const_args[1], + args[2], const_args[2]); + break; + case INDEX_op_sub_i32: + case INDEX_op_sub_i64: + tcg_out_alu(s, OPC_SUB_A1, args[0], args[1], const_args[1], + args[2], const_args[2]); + break; + + case INDEX_op_and_i32: + case INDEX_op_and_i64: + tcg_out_alu(s, OPC_AND_A1, args[0], args[1], const_args[1], + args[2], const_args[2]); + break; + case INDEX_op_andc_i32: + case INDEX_op_andc_i64: + tcg_out_alu(s, OPC_ANDCM_A1, args[0], args[1], const_args[1], + args[2], const_args[2]); + break; + case INDEX_op_eqv_i32: + case INDEX_op_eqv_i64: + tcg_out_eqv(s, args[0], args[1], const_args[1], + args[2], const_args[2]); + break; + case INDEX_op_nand_i32: + case INDEX_op_nand_i64: + tcg_out_nand(s, args[0], args[1], const_args[1], + args[2], const_args[2]); + break; + case INDEX_op_nor_i32: + case INDEX_op_nor_i64: + tcg_out_nor(s, args[0], args[1], const_args[1], + args[2], const_args[2]); + break; + case INDEX_op_or_i32: + case INDEX_op_or_i64: + tcg_out_alu(s, OPC_OR_A1, args[0], args[1], const_args[1], + args[2], const_args[2]); + break; + case INDEX_op_orc_i32: + case INDEX_op_orc_i64: + tcg_out_orc(s, args[0], args[1], const_args[1], + args[2], const_args[2]); + break; + case INDEX_op_xor_i32: + case INDEX_op_xor_i64: + tcg_out_alu(s, OPC_XOR_A1, args[0], args[1], const_args[1], + args[2], const_args[2]); + break; + + case INDEX_op_mul_i32: + case INDEX_op_mul_i64: + tcg_out_mul(s, args[0], args[1], args[2]); + break; + + case INDEX_op_sar_i32: + tcg_out_sar_i32(s, args[0], args[1], args[2], const_args[2]); + break; + case INDEX_op_sar_i64: + tcg_out_sar_i64(s, args[0], args[1], args[2], const_args[2]); + break; + case INDEX_op_shl_i32: + tcg_out_shl_i32(s, args[0], args[1], args[2], const_args[2]); + break; + case INDEX_op_shl_i64: + tcg_out_shl_i64(s, args[0], args[1], args[2], const_args[2]); + break; + case INDEX_op_shr_i32: + tcg_out_shr_i32(s, args[0], args[1], args[2], const_args[2]); + break; + case INDEX_op_shr_i64: + tcg_out_shr_i64(s, args[0], args[1], args[2], const_args[2]); + break; + case INDEX_op_rotl_i32: + tcg_out_rotl_i32(s, args[0], args[1], args[2], const_args[2]); + break; + case INDEX_op_rotl_i64: + tcg_out_rotl_i64(s, args[0], args[1], args[2], const_args[2]); + break; + case INDEX_op_rotr_i32: + tcg_out_rotr_i32(s, args[0], args[1], args[2], const_args[2]); + break; + case INDEX_op_rotr_i64: + tcg_out_rotr_i64(s, args[0], args[1], args[2], const_args[2]); + break; + + case INDEX_op_ext8s_i32: + case INDEX_op_ext8s_i64: + tcg_out_ext(s, OPC_SXT1_I29, args[0], args[1]); + break; + case INDEX_op_ext8u_i32: + case INDEX_op_ext8u_i64: + tcg_out_ext(s, OPC_ZXT1_I29, args[0], args[1]); + break; + case INDEX_op_ext16s_i32: + case INDEX_op_ext16s_i64: + tcg_out_ext(s, OPC_SXT2_I29, args[0], args[1]); + break; + case INDEX_op_ext16u_i32: + case INDEX_op_ext16u_i64: + tcg_out_ext(s, OPC_ZXT2_I29, args[0], args[1]); + break; + case INDEX_op_ext32s_i64: + tcg_out_ext(s, OPC_SXT4_I29, args[0], args[1]); + break; + case INDEX_op_ext32u_i64: + tcg_out_ext(s, OPC_ZXT4_I29, args[0], args[1]); + break; + + case INDEX_op_bswap16_i32: + case INDEX_op_bswap16_i64: + tcg_out_bswap16(s, args[0], args[1]); + break; + case INDEX_op_bswap32_i32: + case INDEX_op_bswap32_i64: + tcg_out_bswap32(s, args[0], args[1]); + break; + case INDEX_op_bswap64_i64: + tcg_out_bswap64(s, args[0], args[1]); + break; + + case INDEX_op_brcond_i32: + tcg_out_brcond(s, args[2], args[0], const_args[0], + args[1], const_args[1], args[3], 1); + break; + case INDEX_op_brcond_i64: + tcg_out_brcond(s, args[2], args[0], const_args[0], + args[1], const_args[1], args[3], 0); + break; + case INDEX_op_setcond_i32: + tcg_out_setcond(s, args[3], args[0], args[1], args[2], 1); + break; + case INDEX_op_setcond_i64: + tcg_out_setcond(s, args[3], args[0], args[1], args[2], 0); + break; + + case INDEX_op_qemu_ld8u: + tcg_out_qemu_ld(s, args, 0); + break; + case INDEX_op_qemu_ld8s: + tcg_out_qemu_ld(s, args, 0 | 4); + break; + case INDEX_op_qemu_ld16u: + tcg_out_qemu_ld(s, args, 1); + break; + case INDEX_op_qemu_ld16s: + tcg_out_qemu_ld(s, args, 1 | 4); + break; + case INDEX_op_qemu_ld32: + case INDEX_op_qemu_ld32u: + tcg_out_qemu_ld(s, args, 2); + break; + case INDEX_op_qemu_ld32s: + tcg_out_qemu_ld(s, args, 2 | 4); + break; + case INDEX_op_qemu_ld64: + tcg_out_qemu_ld(s, args, 3); + break; + + case INDEX_op_qemu_st8: + tcg_out_qemu_st(s, args, 0); + break; + case INDEX_op_qemu_st16: + tcg_out_qemu_st(s, args, 1); + break; + case INDEX_op_qemu_st32: + tcg_out_qemu_st(s, args, 2); + break; + case INDEX_op_qemu_st64: + tcg_out_qemu_st(s, args, 3); + break; + + default: + tcg_abort(); + } +} + +static const TCGTargetOpDef ia64_op_defs[] = { + { INDEX_op_br, { } }, + { INDEX_op_call, { "r" } }, + { INDEX_op_exit_tb, { } }, + { INDEX_op_goto_tb, { } }, + { INDEX_op_jmp, { "r" } }, + + { INDEX_op_mov_i32, { "r", "r" } }, + { INDEX_op_movi_i32, { "r" } }, + + { INDEX_op_ld8u_i32, { "r", "r" } }, + { INDEX_op_ld8s_i32, { "r", "r" } }, + { INDEX_op_ld16u_i32, { "r", "r" } }, + { INDEX_op_ld16s_i32, { "r", "r" } }, + { INDEX_op_ld_i32, { "r", "r" } }, + { INDEX_op_st8_i32, { "rZ", "r" } }, + { INDEX_op_st16_i32, { "rZ", "r" } }, + { INDEX_op_st_i32, { "rZ", "r" } }, + + { INDEX_op_add_i32, { "r", "rI", "rI" } }, + { INDEX_op_sub_i32, { "r", "rI", "rI" } }, + + { INDEX_op_and_i32, { "r", "rI", "rI" } }, + { INDEX_op_andc_i32, { "r", "rI", "rI" } }, + { INDEX_op_eqv_i32, { "r", "rZ", "rZ" } }, + { INDEX_op_nand_i32, { "r", "rZ", "rZ" } }, + { INDEX_op_nor_i32, { "r", "rZ", "rZ" } }, + { INDEX_op_or_i32, { "r", "rI", "rI" } }, + { INDEX_op_orc_i32, { "r", "rZ", "rZ" } }, + { INDEX_op_xor_i32, { "r", "rI", "rI" } }, + + { INDEX_op_mul_i32, { "r", "rZ", "rZ" } }, + + { INDEX_op_sar_i32, { "r", "rZ", "ri" } }, + { INDEX_op_shl_i32, { "r", "rZ", "ri" } }, + { INDEX_op_shr_i32, { "r", "rZ", "ri" } }, + { INDEX_op_rotl_i32, { "r", "rZ", "ri" } }, + { INDEX_op_rotr_i32, { "r", "rZ", "ri" } }, + + { INDEX_op_ext8s_i32, { "r", "rZ"} }, + { INDEX_op_ext8u_i32, { "r", "rZ"} }, + { INDEX_op_ext16s_i32, { "r", "rZ"} }, + { INDEX_op_ext16u_i32, { "r", "rZ"} }, + + { INDEX_op_bswap16_i32, { "r", "rZ" } }, + { INDEX_op_bswap32_i32, { "r", "rZ" } }, + + { INDEX_op_brcond_i32, { "rI", "rI" } }, + { INDEX_op_setcond_i32, { "r", "rZ", "rZ" } }, + + { INDEX_op_mov_i64, { "r", "r" } }, + { INDEX_op_movi_i64, { "r" } }, + + { INDEX_op_ld8u_i64, { "r", "r" } }, + { INDEX_op_ld8s_i64, { "r", "r" } }, + { INDEX_op_ld16u_i64, { "r", "r" } }, + { INDEX_op_ld16s_i64, { "r", "r" } }, + { INDEX_op_ld32u_i64, { "r", "r" } }, + { INDEX_op_ld32s_i64, { "r", "r" } }, + { INDEX_op_ld_i64, { "r", "r" } }, + { INDEX_op_st8_i64, { "rZ", "r" } }, + { INDEX_op_st16_i64, { "rZ", "r" } }, + { INDEX_op_st32_i64, { "rZ", "r" } }, + { INDEX_op_st_i64, { "rZ", "r" } }, + + { INDEX_op_add_i64, { "r", "rI", "rI" } }, + { INDEX_op_sub_i64, { "r", "rI", "rI" } }, + + { INDEX_op_and_i64, { "r", "rI", "rI" } }, + { INDEX_op_andc_i64, { "r", "rI", "rI" } }, + { INDEX_op_eqv_i64, { "r", "rZ", "rZ" } }, + { INDEX_op_nand_i64, { "r", "rZ", "rZ" } }, + { INDEX_op_nor_i64, { "r", "rZ", "rZ" } }, + { INDEX_op_or_i64, { "r", "rI", "rI" } }, + { INDEX_op_orc_i64, { "r", "rZ", "rZ" } }, + { INDEX_op_xor_i64, { "r", "rI", "rI" } }, + + { INDEX_op_mul_i64, { "r", "rZ", "rZ" } }, + + { INDEX_op_sar_i64, { "r", "rZ", "ri" } }, + { INDEX_op_shl_i64, { "r", "rZ", "ri" } }, + { INDEX_op_shr_i64, { "r", "rZ", "ri" } }, + { INDEX_op_rotl_i64, { "r", "rZ", "ri" } }, + { INDEX_op_rotr_i64, { "r", "rZ", "ri" } }, + + { INDEX_op_ext8s_i64, { "r", "rZ"} }, + { INDEX_op_ext8u_i64, { "r", "rZ"} }, + { INDEX_op_ext16s_i64, { "r", "rZ"} }, + { INDEX_op_ext16u_i64, { "r", "rZ"} }, + { INDEX_op_ext32s_i64, { "r", "rZ"} }, + { INDEX_op_ext32u_i64, { "r", "rZ"} }, + + { INDEX_op_bswap16_i64, { "r", "rZ" } }, + { INDEX_op_bswap32_i64, { "r", "rZ" } }, + { INDEX_op_bswap64_i64, { "r", "rZ" } }, + + { INDEX_op_brcond_i64, { "rI", "rI" } }, + { INDEX_op_setcond_i64, { "r", "rZ", "rZ" } }, + + { INDEX_op_qemu_ld8u, { "r", "r" } }, + { INDEX_op_qemu_ld8s, { "r", "r" } }, + { INDEX_op_qemu_ld16u, { "r", "r" } }, + { INDEX_op_qemu_ld16s, { "r", "r" } }, + { INDEX_op_qemu_ld32, { "r", "r" } }, + { INDEX_op_qemu_ld32u, { "r", "r" } }, + { INDEX_op_qemu_ld32s, { "r", "r" } }, + { INDEX_op_qemu_ld64, { "r", "r" } }, + + { INDEX_op_qemu_st8, { "SZ", "r" } }, + { INDEX_op_qemu_st16, { "SZ", "r" } }, + { INDEX_op_qemu_st32, { "SZ", "r" } }, + { INDEX_op_qemu_st64, { "SZ", "r" } }, + + { -1 }, +}; + +/* Generate global QEMU prologue and epilogue code */ +static void tcg_target_qemu_prologue(TCGContext *s) +{ + int frame_size; + + /* reserve some stack space */ + frame_size = TCG_STATIC_CALL_ARGS_SIZE; + frame_size = (frame_size + TCG_TARGET_STACK_ALIGN - 1) & + ~(TCG_TARGET_STACK_ALIGN - 1); + + /* First emit adhoc function descriptor */ + *(uint64_t *)(s->code_ptr) = (uint64_t)s->code_ptr + 16; /* entry point */ + s->code_ptr += 16; /* skip GP */ + + /* prologue */ + tcg_out_bundle(s, mII, + tcg_opc_m34(TCG_REG_P0, OPC_ALLOC_M34, + TCG_REG_R33, 32, 24, 0), + tcg_opc_i21(TCG_REG_P0, OPC_MOV_I21, + TCG_REG_B6, TCG_REG_R32, 0), + tcg_opc_i22(TCG_REG_P0, OPC_MOV_I22, + TCG_REG_R32, TCG_REG_B0)); + + /* ??? If GUEST_BASE < 0x200000, we could load the register via + an ADDL in the M slot of the next bundle. */ + if (GUEST_BASE != 0) { + tcg_out_bundle(s, mlx, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_l2 (GUEST_BASE), + tcg_opc_x2 (TCG_REG_P0, OPC_MOVL_X2, + TCG_GUEST_BASE_REG, GUEST_BASE)); + tcg_regset_set_reg(s->reserved_regs, TCG_GUEST_BASE_REG); + } + + tcg_out_bundle(s, miB, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_a4 (TCG_REG_P0, OPC_ADDS_A4, + TCG_REG_R12, -frame_size, TCG_REG_R12), + tcg_opc_b4 (TCG_REG_P0, OPC_BR_SPTK_MANY_B4, TCG_REG_B6)); + + /* epilogue */ + tb_ret_addr = s->code_ptr; + tcg_out_bundle(s, miI, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i21(TCG_REG_P0, OPC_MOV_I21, + TCG_REG_B0, TCG_REG_R32, 0), + tcg_opc_a4 (TCG_REG_P0, OPC_ADDS_A4, + TCG_REG_R12, frame_size, TCG_REG_R12)); + tcg_out_bundle(s, miB, + tcg_opc_m48(TCG_REG_P0, OPC_NOP_M48, 0), + tcg_opc_i26(TCG_REG_P0, OPC_MOV_I_I26, + TCG_REG_PFS, TCG_REG_R33), + tcg_opc_b4 (TCG_REG_P0, OPC_BR_RET_SPTK_MANY_B4, + TCG_REG_B0)); +} + +static void tcg_target_init(TCGContext *s) +{ + tcg_regset_set(tcg_target_available_regs[TCG_TYPE_I32], + 0xffffffffffffffffull); + tcg_regset_set(tcg_target_available_regs[TCG_TYPE_I64], + 0xffffffffffffffffull); + + tcg_regset_clear(tcg_target_call_clobber_regs); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R8); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R9); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R10); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R11); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R14); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R15); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R16); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R17); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R18); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R19); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R20); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R21); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R22); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R23); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R24); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R25); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R26); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R27); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R28); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R29); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R30); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R31); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R56); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R57); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R58); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R59); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R60); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R61); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R62); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R63); + + tcg_regset_clear(s->reserved_regs); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R0); /* zero register */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R1); /* global pointer */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R2); /* internal use */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R3); /* internal use */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R12); /* stack pointer */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R13); /* thread pointer */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R32); /* return address */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R33); /* PFS */ + + /* The following 3 are not in use, are call-saved, but *not* saved + by the prologue. Therefore we cannot use them without modifying + the prologue. There doesn't seem to be any good reason to use + these as opposed to the windowed registers. */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R4); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R5); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R6); + + tcg_add_target_add_op_defs(ia64_op_defs); +} diff -Nru qemu-kvm-0.12.5+noroms/tcg/ia64/tcg-target.h qemu-kvm-0.14.1/tcg/ia64/tcg-target.h --- qemu-kvm-0.12.5+noroms/tcg/ia64/tcg-target.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/tcg/ia64/tcg-target.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,156 @@ +/* + * Tiny Code Generator for QEMU + * + * Copyright (c) 2009-2010 Aurelien Jarno + * Based on i386/tcg-target.c - Copyright (c) 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. + */ +#define TCG_TARGET_IA64 1 + +#define TCG_TARGET_REG_BITS 64 + +/* We only map the first 64 registers */ +#define TCG_TARGET_NB_REGS 64 +enum { + TCG_REG_R0 = 0, + TCG_REG_R1, + TCG_REG_R2, + TCG_REG_R3, + TCG_REG_R4, + TCG_REG_R5, + TCG_REG_R6, + TCG_REG_R7, + TCG_REG_R8, + TCG_REG_R9, + TCG_REG_R10, + TCG_REG_R11, + TCG_REG_R12, + TCG_REG_R13, + TCG_REG_R14, + TCG_REG_R15, + TCG_REG_R16, + TCG_REG_R17, + TCG_REG_R18, + TCG_REG_R19, + TCG_REG_R20, + TCG_REG_R21, + TCG_REG_R22, + TCG_REG_R23, + TCG_REG_R24, + TCG_REG_R25, + TCG_REG_R26, + TCG_REG_R27, + TCG_REG_R28, + TCG_REG_R29, + TCG_REG_R30, + TCG_REG_R31, + TCG_REG_R32, + TCG_REG_R33, + TCG_REG_R34, + TCG_REG_R35, + TCG_REG_R36, + TCG_REG_R37, + TCG_REG_R38, + TCG_REG_R39, + TCG_REG_R40, + TCG_REG_R41, + TCG_REG_R42, + TCG_REG_R43, + TCG_REG_R44, + TCG_REG_R45, + TCG_REG_R46, + TCG_REG_R47, + TCG_REG_R48, + TCG_REG_R49, + TCG_REG_R50, + TCG_REG_R51, + TCG_REG_R52, + TCG_REG_R53, + TCG_REG_R54, + TCG_REG_R55, + TCG_REG_R56, + TCG_REG_R57, + TCG_REG_R58, + TCG_REG_R59, + TCG_REG_R60, + TCG_REG_R61, + TCG_REG_R62, + TCG_REG_R63, +}; + +#define TCG_CT_CONST_ZERO 0x100 +#define TCG_CT_CONST_S22 0x200 + +/* used for function call generation */ +#define TCG_REG_CALL_STACK TCG_REG_R12 +#define TCG_TARGET_STACK_ALIGN 16 +#define TCG_TARGET_CALL_STACK_OFFSET 16 + +/* optional instructions */ +#define TCG_TARGET_HAS_andc_i32 +#define TCG_TARGET_HAS_andc_i64 +#define TCG_TARGET_HAS_bswap16_i32 +#define TCG_TARGET_HAS_bswap16_i64 +#define TCG_TARGET_HAS_bswap32_i32 +#define TCG_TARGET_HAS_bswap32_i64 +#define TCG_TARGET_HAS_bswap64_i64 +#define TCG_TARGET_HAS_eqv_i32 +#define TCG_TARGET_HAS_eqv_i64 +#define TCG_TARGET_HAS_ext8s_i32 +#define TCG_TARGET_HAS_ext16s_i32 +#define TCG_TARGET_HAS_ext8s_i64 +#define TCG_TARGET_HAS_ext16s_i64 +#define TCG_TARGET_HAS_ext32s_i64 +#define TCG_TARGET_HAS_ext8u_i32 +#define TCG_TARGET_HAS_ext16u_i32 +#define TCG_TARGET_HAS_ext8u_i64 +#define TCG_TARGET_HAS_ext16u_i64 +#define TCG_TARGET_HAS_ext32u_i64 +#define TCG_TARGET_HAS_nand_i32 +#define TCG_TARGET_HAS_nand_i64 +#define TCG_TARGET_HAS_nor_i32 +#define TCG_TARGET_HAS_nor_i64 +#define TCG_TARGET_HAS_orc_i32 +#define TCG_TARGET_HAS_orc_i64 +#define TCG_TARGET_HAS_rot_i32 +#define TCG_TARGET_HAS_rot_i64 + +/* optional instructions automatically implemented */ +#undef TCG_TARGET_HAS_neg_i32 /* sub r1, r0, r3 */ +#undef TCG_TARGET_HAS_neg_i64 /* sub r1, r0, r3 */ +#undef TCG_TARGET_HAS_not_i32 /* xor r1, -1, r3 */ +#undef TCG_TARGET_HAS_not_i64 /* xor r1, -1, r3 */ + +/* Note: must be synced with dyngen-exec.h */ +#define TCG_AREG0 TCG_REG_R7 + +/* Guest base is supported */ +#define TCG_TARGET_HAS_GUEST_BASE + +static inline void flush_icache_range(unsigned long start, unsigned long stop) +{ + start = start & ~(32UL - 1UL); + stop = (stop + (32UL - 1UL)) & ~(32UL - 1UL); + + for (; start < stop; start += 32UL) { + asm volatile ("fc.i %0" :: "r" (start)); + } + asm volatile (";;sync.i;;srlz.i;;"); +} diff -Nru qemu-kvm-0.12.5+noroms/tcg/mips/tcg-target.c qemu-kvm-0.14.1/tcg/mips/tcg-target.c --- qemu-kvm-0.12.5+noroms/tcg/mips/tcg-target.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/tcg/mips/tcg-target.c 2011-05-11 13:29:46.000000000 +0000 @@ -222,8 +222,8 @@ case 'S': /* qemu_st constraint */ ct->ct |= TCG_CT_REG; tcg_regset_set(ct->u.regs, 0xffffffff); -#if defined(CONFIG_SOFTMMU) tcg_regset_reset_reg(ct->u.regs, TCG_REG_A0); +#if defined(CONFIG_SOFTMMU) # if TARGET_LONG_BITS == 64 tcg_regset_reset_reg(ct->u.regs, TCG_REG_A1); # endif @@ -270,10 +270,11 @@ /* instruction opcodes */ enum { - OPC_SPECIAL = 0x00 << 26, OPC_BEQ = 0x04 << 26, OPC_BNE = 0x05 << 26, OPC_ADDIU = 0x09 << 26, + OPC_SLTI = 0x0A << 26, + OPC_SLTIU = 0x0B << 26, OPC_ANDI = 0x0C << 26, OPC_ORI = 0x0D << 26, OPC_XORI = 0x0E << 26, @@ -287,6 +288,8 @@ OPC_SB = 0x28 << 26, OPC_SH = 0x29 << 26, OPC_SW = 0x2B << 26, + + OPC_SPECIAL = 0x00 << 26, OPC_SLL = OPC_SPECIAL | 0x00, OPC_SRL = OPC_SPECIAL | 0x02, OPC_SRA = OPC_SPECIAL | 0x03, @@ -309,6 +312,10 @@ OPC_NOR = OPC_SPECIAL | 0x27, OPC_SLT = OPC_SPECIAL | 0x2A, OPC_SLTU = OPC_SPECIAL | 0x2B, + + OPC_SPECIAL3 = 0x1f << 26, + OPC_SEB = OPC_SPECIAL3 | 0x420, + OPC_SEH = OPC_SPECIAL3 | 0x620, }; /* @@ -344,8 +351,10 @@ */ static inline void tcg_out_opc_br(TCGContext *s, int opc, int rt, int rs) { - /* We need to keep the offset unchanged for retranslation */ - uint16_t offset = (uint16_t)(*(uint32_t *) &s->code_ptr); + /* We pay attention here to not modify the branch target by reading + the existing value and using it again. This ensure that caches and + memory are kept coherent during retranslation. */ + uint16_t offset = (uint16_t)(*(uint32_t *) s->code_ptr); tcg_out_opc_imm(s, opc, rt, rs, offset); } @@ -370,7 +379,7 @@ tcg_out32(s, 0); } -static inline void tcg_out_mov(TCGContext *s, int ret, int arg) +static inline void tcg_out_mov(TCGContext *s, TCGType type, int ret, int arg) { tcg_out_opc_reg(s, OPC_ADDU, ret, arg, TCG_REG_ZERO); } @@ -439,6 +448,26 @@ tcg_out_opc_reg(s, OPC_OR, ret, ret, TCG_REG_AT); } +static inline void tcg_out_ext8s(TCGContext *s, int ret, int arg) +{ +#ifdef _MIPS_ARCH_MIPS32R2 + tcg_out_opc_reg(s, OPC_SEB, ret, 0, arg); +#else + tcg_out_opc_sa(s, OPC_SLL, ret, arg, 24); + tcg_out_opc_sa(s, OPC_SRA, ret, ret, 24); +#endif +} + +static inline void tcg_out_ext16s(TCGContext *s, int ret, int arg) +{ +#ifdef _MIPS_ARCH_MIPS32R2 + tcg_out_opc_reg(s, OPC_SEH, ret, 0, arg); +#else + tcg_out_opc_sa(s, OPC_SLL, ret, arg, 16); + tcg_out_opc_sa(s, OPC_SRA, ret, ret, 16); +#endif +} + static inline void tcg_out_ldst(TCGContext *s, int opc, int arg, int arg1, tcg_target_long arg2) { @@ -473,7 +502,7 @@ } } -static void tcg_out_brcond(TCGContext *s, int cond, int arg1, +static void tcg_out_brcond(TCGContext *s, TCGCond cond, int arg1, int arg2, int label_index) { TCGLabel *l = &s->labels[label_index]; @@ -531,7 +560,7 @@ /* XXX: we implement it at the target level to avoid having to handle cross basic blocks temporaries */ -static void tcg_out_brcond2(TCGContext *s, int cond, int arg1, +static void tcg_out_brcond2(TCGContext *s, TCGCond cond, int arg1, int arg2, int arg3, int arg4, int label_index) { void *label_ptr; @@ -594,6 +623,128 @@ reloc_pc16(label_ptr, (tcg_target_long) s->code_ptr); } +static void tcg_out_setcond(TCGContext *s, TCGCond cond, int ret, + int arg1, int arg2) +{ + switch (cond) { + case TCG_COND_EQ: + if (arg1 == 0) { + tcg_out_opc_imm(s, OPC_SLTIU, ret, arg2, 1); + } else if (arg2 == 0) { + tcg_out_opc_imm(s, OPC_SLTIU, ret, arg1, 1); + } else { + tcg_out_opc_reg(s, OPC_XOR, ret, arg1, arg2); + tcg_out_opc_imm(s, OPC_SLTIU, ret, ret, 1); + } + break; + case TCG_COND_NE: + if (arg1 == 0) { + tcg_out_opc_reg(s, OPC_SLTU, ret, TCG_REG_ZERO, arg2); + } else if (arg2 == 0) { + tcg_out_opc_reg(s, OPC_SLTU, ret, TCG_REG_ZERO, arg1); + } else { + tcg_out_opc_reg(s, OPC_XOR, ret, arg1, arg2); + tcg_out_opc_reg(s, OPC_SLTU, ret, TCG_REG_ZERO, ret); + } + break; + case TCG_COND_LT: + tcg_out_opc_reg(s, OPC_SLT, ret, arg1, arg2); + break; + case TCG_COND_LTU: + tcg_out_opc_reg(s, OPC_SLTU, ret, arg1, arg2); + break; + case TCG_COND_GE: + tcg_out_opc_reg(s, OPC_SLT, ret, arg1, arg2); + tcg_out_opc_imm(s, OPC_XORI, ret, ret, 1); + break; + case TCG_COND_GEU: + tcg_out_opc_reg(s, OPC_SLTU, ret, arg1, arg2); + tcg_out_opc_imm(s, OPC_XORI, ret, ret, 1); + break; + case TCG_COND_LE: + tcg_out_opc_reg(s, OPC_SLT, ret, arg2, arg1); + tcg_out_opc_imm(s, OPC_XORI, ret, ret, 1); + break; + case TCG_COND_LEU: + tcg_out_opc_reg(s, OPC_SLTU, ret, arg2, arg1); + tcg_out_opc_imm(s, OPC_XORI, ret, ret, 1); + break; + case TCG_COND_GT: + tcg_out_opc_reg(s, OPC_SLT, ret, arg2, arg1); + break; + case TCG_COND_GTU: + tcg_out_opc_reg(s, OPC_SLTU, ret, arg2, arg1); + break; + default: + tcg_abort(); + break; + } +} + +/* XXX: we implement it at the target level to avoid having to + handle cross basic blocks temporaries */ +static void tcg_out_setcond2(TCGContext *s, TCGCond cond, int ret, + int arg1, int arg2, int arg3, int arg4) +{ + switch (cond) { + case TCG_COND_EQ: + tcg_out_setcond(s, TCG_COND_EQ, TCG_REG_AT, arg2, arg4); + tcg_out_setcond(s, TCG_COND_EQ, TCG_REG_T0, arg1, arg3); + tcg_out_opc_reg(s, OPC_AND, ret, TCG_REG_AT, TCG_REG_T0); + return; + case TCG_COND_NE: + tcg_out_setcond(s, TCG_COND_NE, TCG_REG_AT, arg2, arg4); + tcg_out_setcond(s, TCG_COND_NE, TCG_REG_T0, arg1, arg3); + tcg_out_opc_reg(s, OPC_OR, ret, TCG_REG_AT, TCG_REG_T0); + return; + case TCG_COND_LT: + case TCG_COND_LE: + tcg_out_setcond(s, TCG_COND_LT, TCG_REG_AT, arg2, arg4); + break; + case TCG_COND_GT: + case TCG_COND_GE: + tcg_out_setcond(s, TCG_COND_GT, TCG_REG_AT, arg2, arg4); + break; + case TCG_COND_LTU: + case TCG_COND_LEU: + tcg_out_setcond(s, TCG_COND_LTU, TCG_REG_AT, arg2, arg4); + break; + case TCG_COND_GTU: + case TCG_COND_GEU: + tcg_out_setcond(s, TCG_COND_GTU, TCG_REG_AT, arg2, arg4); + break; + default: + tcg_abort(); + break; + } + + tcg_out_setcond(s, TCG_COND_EQ, TCG_REG_T0, arg2, arg4); + + switch(cond) { + case TCG_COND_LT: + case TCG_COND_LTU: + tcg_out_setcond(s, TCG_COND_LTU, ret, arg1, arg3); + break; + case TCG_COND_LE: + case TCG_COND_LEU: + tcg_out_setcond(s, TCG_COND_LEU, ret, arg1, arg3); + break; + case TCG_COND_GT: + case TCG_COND_GTU: + tcg_out_setcond(s, TCG_COND_GTU, ret, arg1, arg3); + break; + case TCG_COND_GE: + case TCG_COND_GEU: + tcg_out_setcond(s, TCG_COND_GEU, ret, arg1, arg3); + break; + default: + tcg_abort(); + } + + tcg_out_opc_reg(s, OPC_AND, ret, ret, TCG_REG_T0); + tcg_out_opc_reg(s, OPC_OR, ret, ret, TCG_REG_AT); +} + #if defined(CONFIG_SOFTMMU) #include "../../softmmu_defs.h" @@ -700,9 +851,9 @@ /* slow path */ sp_args = TCG_REG_A0; - tcg_out_mov(s, sp_args++, addr_reg1); + tcg_out_mov(s, TCG_TYPE_I32, sp_args++, addr_reg1); # if TARGET_LONG_BITS == 64 - tcg_out_mov(s, sp_args++, addr_reg2); + tcg_out_mov(s, TCG_TYPE_I32, sp_args++, addr_reg2); # endif tcg_out_movi(s, TCG_TYPE_I32, sp_args++, mem_index); tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_T9, (tcg_target_long)qemu_ld_helpers[s_bits]); @@ -714,22 +865,20 @@ tcg_out_opc_imm(s, OPC_ANDI, data_reg1, TCG_REG_V0, 0xff); break; case 0 | 4: - tcg_out_opc_sa(s, OPC_SLL, TCG_REG_V0, TCG_REG_V0, 24); - tcg_out_opc_sa(s, OPC_SRA, data_reg1, TCG_REG_V0, 24); + tcg_out_ext8s(s, data_reg1, TCG_REG_V0); break; case 1: tcg_out_opc_imm(s, OPC_ANDI, data_reg1, TCG_REG_V0, 0xffff); break; case 1 | 4: - tcg_out_opc_sa(s, OPC_SLL, TCG_REG_V0, TCG_REG_V0, 16); - tcg_out_opc_sa(s, OPC_SRA, data_reg1, TCG_REG_V0, 16); + tcg_out_ext16s(s, data_reg1, TCG_REG_V0); break; case 2: - tcg_out_mov(s, data_reg1, TCG_REG_V0); + tcg_out_mov(s, TCG_TYPE_I32, data_reg1, TCG_REG_V0); break; case 3: - tcg_out_mov(s, data_reg2, TCG_REG_V1); - tcg_out_mov(s, data_reg1, TCG_REG_V0); + tcg_out_mov(s, TCG_TYPE_I32, data_reg2, TCG_REG_V1); + tcg_out_mov(s, TCG_TYPE_I32, data_reg1, TCG_REG_V0); break; default: tcg_abort(); @@ -743,56 +892,57 @@ reloc_pc16(label1_ptr, (tcg_target_long) s->code_ptr); tcg_out_opc_imm(s, OPC_LW, TCG_REG_A0, TCG_REG_A0, - offsetof(CPUState, tlb_table[mem_index][0].addend) + addr_meml); + offsetof(CPUState, tlb_table[mem_index][0].addend)); tcg_out_opc_reg(s, OPC_ADDU, TCG_REG_V0, TCG_REG_A0, addr_regl); - - addr_reg1 = TCG_REG_V0; +#else + if (GUEST_BASE == (int16_t)GUEST_BASE) { + tcg_out_opc_imm(s, OPC_ADDIU, TCG_REG_V0, addr_regl, GUEST_BASE); + } else { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_V0, GUEST_BASE); + tcg_out_opc_reg(s, OPC_ADDU, TCG_REG_V0, TCG_REG_V0, addr_regl); + } #endif switch(opc) { case 0: - tcg_out_opc_imm(s, OPC_LBU, data_reg1, addr_reg1, 0); + tcg_out_opc_imm(s, OPC_LBU, data_reg1, TCG_REG_V0, 0); break; case 0 | 4: - tcg_out_opc_imm(s, OPC_LB, data_reg1, addr_reg1, 0); + tcg_out_opc_imm(s, OPC_LB, data_reg1, TCG_REG_V0, 0); break; case 1: if (TCG_NEED_BSWAP) { - tcg_out_opc_imm(s, OPC_LHU, TCG_REG_T0, addr_reg1, 0); + tcg_out_opc_imm(s, OPC_LHU, TCG_REG_T0, TCG_REG_V0, 0); tcg_out_bswap16(s, data_reg1, TCG_REG_T0); } else { - tcg_out_opc_imm(s, OPC_LHU, data_reg1, addr_reg1, 0); + tcg_out_opc_imm(s, OPC_LHU, data_reg1, TCG_REG_V0, 0); } break; case 1 | 4: if (TCG_NEED_BSWAP) { - tcg_out_opc_imm(s, OPC_LHU, TCG_REG_T0, addr_reg1, 0); + tcg_out_opc_imm(s, OPC_LHU, TCG_REG_T0, TCG_REG_V0, 0); tcg_out_bswap16s(s, data_reg1, TCG_REG_T0); } else { - tcg_out_opc_imm(s, OPC_LH, data_reg1, addr_reg1, 0); + tcg_out_opc_imm(s, OPC_LH, data_reg1, TCG_REG_V0, 0); } break; case 2: if (TCG_NEED_BSWAP) { - tcg_out_opc_imm(s, OPC_LW, TCG_REG_T0, addr_reg1, 0); + tcg_out_opc_imm(s, OPC_LW, TCG_REG_T0, TCG_REG_V0, 0); tcg_out_bswap32(s, data_reg1, TCG_REG_T0); } else { - tcg_out_opc_imm(s, OPC_LW, data_reg1, addr_reg1, 0); + tcg_out_opc_imm(s, OPC_LW, data_reg1, TCG_REG_V0, 0); } break; case 3: -#if !defined(CONFIG_SOFTMMU) - tcg_out_mov(s, TCG_REG_V0, addr_reg1); - addr_reg1 = TCG_REG_V0; -#endif if (TCG_NEED_BSWAP) { - tcg_out_opc_imm(s, OPC_LW, TCG_REG_T0, addr_reg1, 4); + tcg_out_opc_imm(s, OPC_LW, TCG_REG_T0, TCG_REG_V0, 4); tcg_out_bswap32(s, data_reg1, TCG_REG_T0); - tcg_out_opc_imm(s, OPC_LW, TCG_REG_T0, addr_reg1, 0); + tcg_out_opc_imm(s, OPC_LW, TCG_REG_T0, TCG_REG_V0, 0); tcg_out_bswap32(s, data_reg2, TCG_REG_T0); } else { - tcg_out_opc_imm(s, OPC_LW, data_reg1, addr_reg1, 0); - tcg_out_opc_imm(s, OPC_LW, data_reg2, addr_reg1, 4); + tcg_out_opc_imm(s, OPC_LW, data_reg1, TCG_REG_V0, 0); + tcg_out_opc_imm(s, OPC_LW, data_reg2, TCG_REG_V0, 4); } break; default: @@ -887,9 +1037,9 @@ /* slow path */ sp_args = TCG_REG_A0; - tcg_out_mov(s, sp_args++, addr_reg1); + tcg_out_mov(s, TCG_TYPE_I32, sp_args++, addr_reg1); # if TARGET_LONG_BITS == 64 - tcg_out_mov(s, sp_args++, addr_reg2); + tcg_out_mov(s, TCG_TYPE_I32, sp_args++, addr_reg2); # endif switch(opc) { case 0: @@ -899,12 +1049,12 @@ tcg_out_opc_imm(s, OPC_ANDI, sp_args++, data_reg1, 0xffff); break; case 2: - tcg_out_mov(s, sp_args++, data_reg1); + tcg_out_mov(s, TCG_TYPE_I32, sp_args++, data_reg1); break; case 3: sp_args = (sp_args + 1) & ~1; - tcg_out_mov(s, sp_args++, data_reg1); - tcg_out_mov(s, sp_args++, data_reg2); + tcg_out_mov(s, TCG_TYPE_I32, sp_args++, data_reg1); + tcg_out_mov(s, TCG_TYPE_I32, sp_args++, data_reg2); break; default: tcg_abort(); @@ -929,41 +1079,47 @@ reloc_pc16(label1_ptr, (tcg_target_long) s->code_ptr); tcg_out_opc_imm(s, OPC_LW, TCG_REG_A0, TCG_REG_A0, - offsetof(CPUState, tlb_table[mem_index][0].addend) + addr_meml); + offsetof(CPUState, tlb_table[mem_index][0].addend)); tcg_out_opc_reg(s, OPC_ADDU, TCG_REG_A0, TCG_REG_A0, addr_regl); +#else + if (GUEST_BASE == (int16_t)GUEST_BASE) { + tcg_out_opc_imm(s, OPC_ADDIU, TCG_REG_A0, addr_regl, GUEST_BASE); + } else { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_A0, GUEST_BASE); + tcg_out_opc_reg(s, OPC_ADDU, TCG_REG_A0, TCG_REG_A0, addr_regl); + } - addr_reg1 = TCG_REG_A0; #endif switch(opc) { case 0: - tcg_out_opc_imm(s, OPC_SB, data_reg1, addr_reg1, 0); + tcg_out_opc_imm(s, OPC_SB, data_reg1, TCG_REG_A0, 0); break; case 1: if (TCG_NEED_BSWAP) { tcg_out_bswap16(s, TCG_REG_T0, data_reg1); - tcg_out_opc_imm(s, OPC_SH, TCG_REG_T0, addr_reg1, 0); + tcg_out_opc_imm(s, OPC_SH, TCG_REG_T0, TCG_REG_A0, 0); } else { - tcg_out_opc_imm(s, OPC_SH, data_reg1, addr_reg1, 0); + tcg_out_opc_imm(s, OPC_SH, data_reg1, TCG_REG_A0, 0); } break; case 2: if (TCG_NEED_BSWAP) { tcg_out_bswap32(s, TCG_REG_T0, data_reg1); - tcg_out_opc_imm(s, OPC_SW, TCG_REG_T0, addr_reg1, 0); + tcg_out_opc_imm(s, OPC_SW, TCG_REG_T0, TCG_REG_A0, 0); } else { - tcg_out_opc_imm(s, OPC_SW, data_reg1, addr_reg1, 0); + tcg_out_opc_imm(s, OPC_SW, data_reg1, TCG_REG_A0, 0); } break; case 3: if (TCG_NEED_BSWAP) { tcg_out_bswap32(s, TCG_REG_T0, data_reg2); - tcg_out_opc_imm(s, OPC_SW, TCG_REG_T0, addr_reg1, 0); + tcg_out_opc_imm(s, OPC_SW, TCG_REG_T0, TCG_REG_A0, 0); tcg_out_bswap32(s, TCG_REG_T0, data_reg1); - tcg_out_opc_imm(s, OPC_SW, TCG_REG_T0, addr_reg1, 4); + tcg_out_opc_imm(s, OPC_SW, TCG_REG_T0, TCG_REG_A0, 4); } else { - tcg_out_opc_imm(s, OPC_SW, data_reg1, addr_reg1, 0); - tcg_out_opc_imm(s, OPC_SW, data_reg2, addr_reg1, 4); + tcg_out_opc_imm(s, OPC_SW, data_reg1, TCG_REG_A0, 0); + tcg_out_opc_imm(s, OPC_SW, data_reg2, TCG_REG_A0, 4); } break; default: @@ -975,7 +1131,7 @@ #endif } -static inline void tcg_out_op(TCGContext *s, int opc, +static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg *args, const int *const_args) { switch(opc) { @@ -1011,14 +1167,14 @@ break; case INDEX_op_mov_i32: - tcg_out_mov(s, args[0], args[1]); + tcg_out_mov(s, TCG_TYPE_I32, args[0], args[1]); break; case INDEX_op_movi_i32: tcg_out_movi(s, TCG_TYPE_I32, args[0], args[1]); break; case INDEX_op_ld8u_i32: - tcg_out_ldst(s, OPC_LBU, args[0], args[1], args[2]); + tcg_out_ldst(s, OPC_LBU, args[0], args[1], args[2]); break; case INDEX_op_ld8s_i32: tcg_out_ldst(s, OPC_LB, args[0], args[1], args[2]); @@ -1062,7 +1218,7 @@ tcg_out_opc_reg(s, OPC_ADDU, args[1], args[3], args[5]); } tcg_out_opc_reg(s, OPC_ADDU, args[1], args[1], TCG_REG_T0); - tcg_out_mov(s, args[0], TCG_REG_AT); + tcg_out_mov(s, TCG_TYPE_I32, args[0], TCG_REG_AT); break; case INDEX_op_sub_i32: if (const_args[2]) { @@ -1084,7 +1240,7 @@ tcg_out_opc_reg(s, OPC_SUBU, args[1], args[3], args[5]); } tcg_out_opc_reg(s, OPC_SUBU, args[1], args[1], TCG_REG_T0); - tcg_out_mov(s, args[0], TCG_REG_AT); + tcg_out_mov(s, TCG_TYPE_I32, args[0], TCG_REG_AT); break; case INDEX_op_mul_i32: tcg_out_opc_reg(s, OPC_MULT, 0, args[1], args[2]); @@ -1126,8 +1282,11 @@ tcg_out_opc_reg(s, OPC_OR, args[0], args[1], args[2]); } break; + case INDEX_op_nor_i32: + tcg_out_opc_reg(s, OPC_NOR, args[0], args[1], args[2]); + break; case INDEX_op_not_i32: - tcg_out_opc_reg(s, OPC_NOR, args[0], args[1], args[1]); + tcg_out_opc_reg(s, OPC_NOR, args[0], TCG_REG_ZERO, args[1]); break; case INDEX_op_xor_i32: if (const_args[2]) { @@ -1159,6 +1318,13 @@ } break; + case INDEX_op_ext8s_i32: + tcg_out_ext8s(s, args[0], args[1]); + break; + case INDEX_op_ext16s_i32: + tcg_out_ext16s(s, args[0], args[1]); + break; + case INDEX_op_brcond_i32: tcg_out_brcond(s, args[2], args[0], args[1], args[3]); break; @@ -1166,6 +1332,13 @@ tcg_out_brcond2(s, args[4], args[0], args[1], args[2], args[3], args[5]); break; + case INDEX_op_setcond_i32: + tcg_out_setcond(s, args[3], args[0], args[1], args[2]); + break; + case INDEX_op_setcond2_i32: + tcg_out_setcond2(s, args[5], args[0], args[1], args[2], args[3], args[4]); + break; + case INDEX_op_qemu_ld8u: tcg_out_qemu_ld(s, args, 0); break; @@ -1178,7 +1351,7 @@ case INDEX_op_qemu_ld16s: tcg_out_qemu_ld(s, args, 1 | 4); break; - case INDEX_op_qemu_ld32u: + case INDEX_op_qemu_ld32: tcg_out_qemu_ld(s, args, 2); break; case INDEX_op_qemu_ld64: @@ -1230,6 +1403,7 @@ { INDEX_op_sub_i32, { "r", "rZ", "rJZ" } }, { INDEX_op_and_i32, { "r", "rZ", "rIZ" } }, + { INDEX_op_nor_i32, { "r", "rZ", "rZ" } }, { INDEX_op_not_i32, { "r", "rZ" } }, { INDEX_op_or_i32, { "r", "rZ", "rIZ" } }, { INDEX_op_xor_i32, { "r", "rZ", "rIZ" } }, @@ -1238,7 +1412,12 @@ { INDEX_op_shr_i32, { "r", "rZ", "riZ" } }, { INDEX_op_sar_i32, { "r", "rZ", "riZ" } }, + { INDEX_op_ext8s_i32, { "r", "rZ" } }, + { INDEX_op_ext16s_i32, { "r", "rZ" } }, + { INDEX_op_brcond_i32, { "rZ", "rZ" } }, + { INDEX_op_setcond_i32, { "r", "rZ", "rZ" } }, + { INDEX_op_setcond2_i32, { "r", "rZ", "rZ", "rZ", "rZ" } }, { INDEX_op_add2_i32, { "r", "r", "rZ", "rZ", "rJZ", "rJZ" } }, { INDEX_op_sub2_i32, { "r", "r", "rZ", "rZ", "rJZ", "rJZ" } }, @@ -1249,7 +1428,7 @@ { INDEX_op_qemu_ld8s, { "L", "lZ" } }, { INDEX_op_qemu_ld16u, { "L", "lZ" } }, { INDEX_op_qemu_ld16s, { "L", "lZ" } }, - { INDEX_op_qemu_ld32u, { "L", "lZ" } }, + { INDEX_op_qemu_ld32, { "L", "lZ" } }, { INDEX_op_qemu_ld64, { "L", "L", "lZ" } }, { INDEX_op_qemu_st8, { "SZ", "SZ" } }, @@ -1261,7 +1440,7 @@ { INDEX_op_qemu_ld8s, { "L", "lZ", "lZ" } }, { INDEX_op_qemu_ld16u, { "L", "lZ", "lZ" } }, { INDEX_op_qemu_ld16s, { "L", "lZ", "lZ" } }, - { INDEX_op_qemu_ld32u, { "L", "lZ", "lZ" } }, + { INDEX_op_qemu_ld32, { "L", "lZ", "lZ" } }, { INDEX_op_qemu_ld64, { "L", "L", "lZ", "lZ" } }, { INDEX_op_qemu_st8, { "SZ", "SZ", "SZ" } }, @@ -1289,7 +1468,7 @@ }; /* Generate global QEMU prologue and epilogue code */ -void tcg_target_qemu_prologue(TCGContext *s) +static void tcg_target_qemu_prologue(TCGContext *s) { int i, frame_size; @@ -1321,7 +1500,7 @@ tcg_out_addi(s, TCG_REG_SP, frame_size); } -void tcg_target_init(TCGContext *s) +static void tcg_target_init(TCGContext *s) { tcg_regset_set(tcg_target_available_regs[TCG_TYPE_I32], 0xffffffff); tcg_regset_set(tcg_target_call_clobber_regs, diff -Nru qemu-kvm-0.12.5+noroms/tcg/mips/tcg-target.h qemu-kvm-0.14.1/tcg/mips/tcg-target.h --- qemu-kvm-0.12.5+noroms/tcg/mips/tcg-target.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/tcg/mips/tcg-target.h 2011-05-11 13:29:46.000000000 +0000 @@ -80,11 +80,16 @@ /* optional instructions */ #define TCG_TARGET_HAS_div_i32 #define TCG_TARGET_HAS_not_i32 -#undef TCG_TARGET_HAS_ext8s_i32 -#undef TCG_TARGET_HAS_ext16s_i32 +#define TCG_TARGET_HAS_nor_i32 +#undef TCG_TARGET_HAS_rot_i32 +#define TCG_TARGET_HAS_ext8s_i32 +#define TCG_TARGET_HAS_ext16s_i32 #undef TCG_TARGET_HAS_bswap32_i32 #undef TCG_TARGET_HAS_bswap16_i32 -#undef TCG_TARGET_HAS_rot_i32 +#undef TCG_TARGET_HAS_andc_i32 +#undef TCG_TARGET_HAS_orc_i32 +#undef TCG_TARGET_HAS_eqv_i32 +#undef TCG_TARGET_HAS_nand_i32 /* optional instructions automatically implemented */ #undef TCG_TARGET_HAS_neg_i32 /* sub rd, zero, rt */ @@ -93,8 +98,9 @@ /* Note: must be synced with dyngen-exec.h */ #define TCG_AREG0 TCG_REG_S0 -#define TCG_AREG1 TCG_REG_S1 -#define TCG_AREG2 TCG_REG_FP + +/* guest base is supported */ +#define TCG_TARGET_HAS_GUEST_BASE #include diff -Nru qemu-kvm-0.12.5+noroms/tcg/ppc/tcg-target.c qemu-kvm-0.14.1/tcg/ppc/tcg-target.c --- qemu-kvm-0.12.5+noroms/tcg/ppc/tcg-target.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/tcg/ppc/tcg-target.c 2011-05-11 13:29:46.000000000 +0000 @@ -24,10 +24,10 @@ static uint8_t *tb_ret_addr; -#ifdef __APPLE__ +#ifdef _CALL_DARWIN #define LINKAGE_AREA_SIZE 24 #define LR_OFFSET 8 -#elif defined _AIX +#elif defined _CALL_AIX #define LINKAGE_AREA_SIZE 52 #define LR_OFFSET 8 #else @@ -36,11 +36,6 @@ #endif #define FAST_PATH -#if TARGET_PHYS_ADDR_BITS <= 32 -#define ADDEND_OFFSET 0 -#else -#define ADDEND_OFFSET 4 -#endif #ifndef GUEST_BASE #define GUEST_BASE 0 @@ -56,7 +51,7 @@ static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { "r0", "r1", - "rp", + "r2", "r3", "r4", "r5", @@ -104,7 +99,7 @@ TCG_REG_R29, TCG_REG_R30, TCG_REG_R31, -#ifdef __APPLE__ +#ifdef _CALL_DARWIN TCG_REG_R2, #endif TCG_REG_R3, @@ -115,11 +110,11 @@ TCG_REG_R8, TCG_REG_R9, TCG_REG_R10, -#ifndef __APPLE__ +#ifndef _CALL_DARWIN TCG_REG_R11, #endif TCG_REG_R12, -#ifndef __linux__ +#ifndef _CALL_SYSV TCG_REG_R13, #endif TCG_REG_R24, @@ -145,11 +140,11 @@ }; static const int tcg_target_callee_save_regs[] = { -#ifdef __APPLE__ +#ifdef _CALL_DARWIN TCG_REG_R11, TCG_REG_R13, #endif -#ifdef _AIX +#ifdef _CALL_AIX TCG_REG_R13, #endif TCG_REG_R14, @@ -316,6 +311,7 @@ #define STH OPCD(44) #define STW OPCD(36) +#define ADDIC OPCD(12) #define ADDI OPCD(14) #define ADDIS OPCD(15) #define ORI OPCD(24) @@ -327,11 +323,14 @@ #define MULLI OPCD( 7) #define CMPLI OPCD(10) #define CMPI OPCD(11) +#define SUBFIC OPCD( 8) #define LWZU OPCD(33) #define STWU OPCD(37) +#define RLWIMI OPCD(20) #define RLWINM OPCD(21) +#define RLWNM OPCD(23) #define BCLR XO19( 16) #define BCCTR XO19(528) @@ -339,6 +338,7 @@ #define CRANDC XO19(129) #define CRNAND XO19(225) #define CROR XO19(449) +#define CRNOR XO19( 33) #define EXTSB XO31(954) #define EXTSH XO31(922) @@ -365,6 +365,13 @@ #define MTSPR XO31(467) #define SRAWI XO31(824) #define NEG XO31(104) +#define MFCR XO31( 19) +#define CNTLZW XO31( 26) +#define NOR XO31(124) +#define ANDC XO31( 60) +#define ORC XO31(412) +#define EQV XO31(284) +#define NAND XO31(476) #define LBZX XO31( 87) #define LHZX XO31(279) @@ -430,7 +437,7 @@ [TCG_COND_GTU] = BC | BI (7, CR_GT) | BO_COND_TRUE, }; -static void tcg_out_mov(TCGContext *s, int ret, int arg) +static void tcg_out_mov(TCGContext *s, TCGType type, int ret, int arg) { tcg_out32 (s, OR | SAB (arg, ret, arg)); } @@ -474,7 +481,7 @@ static void tcg_out_call (TCGContext *s, tcg_target_long arg, int const_arg) { -#ifdef _AIX +#ifdef _CALL_AIX int reg; if (const_arg) { @@ -584,11 +591,11 @@ /* slow path */ #if TARGET_LONG_BITS == 32 - tcg_out_mov (s, 3, addr_reg); + tcg_out_mov (s, TCG_TYPE_I32, 3, addr_reg); tcg_out_movi (s, TCG_TYPE_I32, 4, mem_index); #else - tcg_out_mov (s, 3, addr_reg2); - tcg_out_mov (s, 4, addr_reg); + tcg_out_mov (s, TCG_TYPE_I32, 3, addr_reg2); + tcg_out_mov (s, TCG_TYPE_I32, 4, addr_reg); tcg_out_movi (s, TCG_TYPE_I32, 5, mem_index); #endif @@ -604,23 +611,23 @@ case 1: case 2: if (data_reg != 3) - tcg_out_mov (s, data_reg, 3); + tcg_out_mov (s, TCG_TYPE_I32, data_reg, 3); break; case 3: if (data_reg == 3) { if (data_reg2 == 4) { - tcg_out_mov (s, 0, 4); - tcg_out_mov (s, 4, 3); - tcg_out_mov (s, 3, 0); + tcg_out_mov (s, TCG_TYPE_I32, 0, 4); + tcg_out_mov (s, TCG_TYPE_I32, 4, 3); + tcg_out_mov (s, TCG_TYPE_I32, 3, 0); } else { - tcg_out_mov (s, data_reg2, 3); - tcg_out_mov (s, 3, 4); + tcg_out_mov (s, TCG_TYPE_I32, data_reg2, 3); + tcg_out_mov (s, TCG_TYPE_I32, 3, 4); } } else { - if (data_reg != 4) tcg_out_mov (s, data_reg, 4); - if (data_reg2 != 3) tcg_out_mov (s, data_reg2, 3); + if (data_reg != 4) tcg_out_mov (s, TCG_TYPE_I32, data_reg, 4); + if (data_reg2 != 3) tcg_out_mov (s, TCG_TYPE_I32, data_reg2, 3); } break; } @@ -636,7 +643,7 @@ tcg_out32 (s, (LWZ | RT (r0) | RA (r0) - | (ADDEND_OFFSET + offsetof (CPUTLBEntry, addend) + | (offsetof (CPUTLBEntry, addend) - offsetof (CPUTLBEntry, addr_read)) )); /* r0 = env->tlb_table[mem_index][index].addend */ @@ -698,7 +705,7 @@ if (r0 == data_reg2) { tcg_out32 (s, LWZ | RT (0) | RA (r0)); tcg_out32 (s, LWZ | RT (data_reg) | RA (r0) | 4); - tcg_out_mov (s, data_reg2, 0); + tcg_out_mov (s, TCG_TYPE_I32, data_reg2, 0); } else { tcg_out32 (s, LWZ | RT (data_reg2) | RA (r0)); @@ -780,11 +787,11 @@ /* slow path */ #if TARGET_LONG_BITS == 32 - tcg_out_mov (s, 3, addr_reg); + tcg_out_mov (s, TCG_TYPE_I32, 3, addr_reg); ir = 4; #else - tcg_out_mov (s, 3, addr_reg2); - tcg_out_mov (s, 4, addr_reg); + tcg_out_mov (s, TCG_TYPE_I32, 3, addr_reg2); + tcg_out_mov (s, TCG_TYPE_I32, 4, addr_reg); #ifdef TCG_TARGET_CALL_ALIGN_ARGS ir = 5; #else @@ -810,14 +817,14 @@ | ME (31))); break; case 2: - tcg_out_mov (s, ir, data_reg); + tcg_out_mov (s, TCG_TYPE_I32, ir, data_reg); break; case 3: #ifdef TCG_TARGET_CALL_ALIGN_ARGS ir = 5; #endif - tcg_out_mov (s, ir++, data_reg2); - tcg_out_mov (s, ir, data_reg); + tcg_out_mov (s, TCG_TYPE_I32, ir++, data_reg2); + tcg_out_mov (s, TCG_TYPE_I32, ir, data_reg); break; } ir++; @@ -835,7 +842,7 @@ tcg_out32 (s, (LWZ | RT (r0) | RA (r0) - | (ADDEND_OFFSET + offsetof (CPUTLBEntry, addend) + | (offsetof (CPUTLBEntry, addend) - offsetof (CPUTLBEntry, addr_write)) )); /* r0 = env->tlb_table[mem_index][index].addend */ @@ -893,7 +900,7 @@ #endif } -void tcg_target_qemu_prologue (TCGContext *s) +static void tcg_target_qemu_prologue (TCGContext *s) { int i, frame_size; @@ -904,7 +911,7 @@ ; frame_size = (frame_size + 15) & ~15; -#ifdef _AIX +#ifdef _CALL_AIX { uint32_t addr; @@ -926,7 +933,10 @@ tcg_out32 (s, STW | RS (0) | RA (1) | (frame_size + LR_OFFSET)); #ifdef CONFIG_USE_GUEST_BASE - tcg_out_movi (s, TCG_TYPE_I32, TCG_GUEST_BASE_REG, GUEST_BASE); + if (GUEST_BASE) { + tcg_out_movi (s, TCG_TYPE_I32, TCG_GUEST_BASE_REG, GUEST_BASE); + tcg_regset_set_reg(s->reserved_regs, TCG_GUEST_BASE_REG); + } #endif tcg_out32 (s, MTSPR | RS (3) | CTR); @@ -1065,20 +1075,11 @@ } } -static void tcg_out_brcond (TCGContext *s, int cond, - TCGArg arg1, TCGArg arg2, int const_arg2, - int label_index) -{ - tcg_out_cmp (s, cond, arg1, arg2, const_arg2, 7); - tcg_out_bc (s, tcg_to_bc[cond], label_index); -} - -/* XXX: we implement it at the target level to avoid having to - handle cross basic blocks temporaries */ -static void tcg_out_brcond2 (TCGContext *s, const TCGArg *args, - const int *const_args) +static void tcg_out_cr7eq_from_cond (TCGContext *s, const TCGArg *args, + const int *const_args) { - int cond = args[4], label_index = args[5], op; + TCGCond cond = args[4]; + int op; struct { int bit1; int bit2; int cond2; } bits[] = { [TCG_COND_LT ] = { CR_LT, CR_LT, TCG_COND_LT }, [TCG_COND_LE ] = { CR_LT, CR_GT, TCG_COND_LT }, @@ -1108,16 +1109,155 @@ case TCG_COND_GEU: op = (b->bit1 != b->bit2) ? CRANDC : CRAND; tcg_out_cmp (s, b->cond2, args[1], args[3], const_args[3], 5); - tcg_out_cmp (s, TCG_COND_EQ, args[1], args[3], const_args[3], 6); - tcg_out_cmp (s, cond, args[0], args[2], const_args[2], 7); - tcg_out32 (s, op | BT (7, CR_EQ) | BA (6, CR_EQ) | BB (7, b->bit2)); + tcg_out_cmp (s, tcg_unsigned_cond (cond), args[0], args[2], + const_args[2], 7); + tcg_out32 (s, op | BT (7, CR_EQ) | BA (5, CR_EQ) | BB (7, b->bit2)); tcg_out32 (s, CROR | BT (7, CR_EQ) | BA (5, b->bit1) | BB (7, CR_EQ)); break; default: tcg_abort(); } +} + +static void tcg_out_setcond (TCGContext *s, TCGCond cond, TCGArg arg0, + TCGArg arg1, TCGArg arg2, int const_arg2) +{ + int crop, sh, arg; + + switch (cond) { + case TCG_COND_EQ: + if (const_arg2) { + if (!arg2) { + arg = arg1; + } + else { + arg = 0; + if ((uint16_t) arg2 == arg2) { + tcg_out32 (s, XORI | RS (arg1) | RA (0) | arg2); + } + else { + tcg_out_movi (s, TCG_TYPE_I32, 0, arg2); + tcg_out32 (s, XOR | SAB (arg1, 0, 0)); + } + } + } + else { + arg = 0; + tcg_out32 (s, XOR | SAB (arg1, 0, arg2)); + } + tcg_out32 (s, CNTLZW | RS (arg) | RA (0)); + tcg_out32 (s, (RLWINM + | RA (arg0) + | RS (0) + | SH (27) + | MB (5) + | ME (31) + ) + ); + break; + + case TCG_COND_NE: + if (const_arg2) { + if (!arg2) { + arg = arg1; + } + else { + arg = 0; + if ((uint16_t) arg2 == arg2) { + tcg_out32 (s, XORI | RS (arg1) | RA (0) | arg2); + } + else { + tcg_out_movi (s, TCG_TYPE_I32, 0, arg2); + tcg_out32 (s, XOR | SAB (arg1, 0, 0)); + } + } + } + else { + arg = 0; + tcg_out32 (s, XOR | SAB (arg1, 0, arg2)); + } + + if (arg == arg1 && arg1 == arg0) { + tcg_out32 (s, ADDIC | RT (0) | RA (arg) | 0xffff); + tcg_out32 (s, SUBFE | TAB (arg0, 0, arg)); + } + else { + tcg_out32 (s, ADDIC | RT (arg0) | RA (arg) | 0xffff); + tcg_out32 (s, SUBFE | TAB (arg0, arg0, arg)); + } + break; + + case TCG_COND_GT: + case TCG_COND_GTU: + sh = 30; + crop = 0; + goto crtest; + + case TCG_COND_LT: + case TCG_COND_LTU: + sh = 29; + crop = 0; + goto crtest; + + case TCG_COND_GE: + case TCG_COND_GEU: + sh = 31; + crop = CRNOR | BT (7, CR_EQ) | BA (7, CR_LT) | BB (7, CR_LT); + goto crtest; + + case TCG_COND_LE: + case TCG_COND_LEU: + sh = 31; + crop = CRNOR | BT (7, CR_EQ) | BA (7, CR_GT) | BB (7, CR_GT); + crtest: + tcg_out_cmp (s, cond, arg1, arg2, const_arg2, 7); + if (crop) tcg_out32 (s, crop); + tcg_out32 (s, MFCR | RT (0)); + tcg_out32 (s, (RLWINM + | RA (arg0) + | RS (0) + | SH (sh) + | MB (31) + | ME (31) + ) + ); + break; + + default: + tcg_abort (); + } +} + +static void tcg_out_setcond2 (TCGContext *s, const TCGArg *args, + const int *const_args) +{ + tcg_out_cr7eq_from_cond (s, args + 1, const_args + 1); + tcg_out32 (s, MFCR | RT (0)); + tcg_out32 (s, (RLWINM + | RA (args[0]) + | RS (0) + | SH (31) + | MB (31) + | ME (31) + ) + ); +} + +static void tcg_out_brcond (TCGContext *s, TCGCond cond, + TCGArg arg1, TCGArg arg2, int const_arg2, + int label_index) +{ + tcg_out_cmp (s, cond, arg1, arg2, const_arg2, 7); + tcg_out_bc (s, tcg_to_bc[cond], label_index); +} - tcg_out_bc (s, (BC | BI (7, CR_EQ) | BO_COND_TRUE), label_index); +/* XXX: we implement it at the target level to avoid having to + handle cross basic blocks temporaries */ +static void tcg_out_brcond2 (TCGContext *s, const TCGArg *args, + const int *const_args) +{ + tcg_out_cr7eq_from_cond (s, args, const_args); + tcg_out_bc (s, (BC | BI (7, CR_EQ) | BO_COND_TRUE), args[5]); } void ppc_tb_set_jmp_target (unsigned long jmp_addr, unsigned long addr) @@ -1151,7 +1291,7 @@ flush_icache_range(jmp_addr, jmp_addr + patch_size); } -static void tcg_out_op(TCGContext *s, int opc, const TCGArg *args, +static void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg *args, const int *const_args) { switch (opc) { @@ -1335,6 +1475,21 @@ else tcg_out32 (s, XOR | SAB (args[1], args[0], args[2])); break; + case INDEX_op_andc_i32: + tcg_out32 (s, ANDC | SAB (args[1], args[0], args[2])); + break; + case INDEX_op_orc_i32: + tcg_out32 (s, ORC | SAB (args[1], args[0], args[2])); + break; + case INDEX_op_eqv_i32: + tcg_out32 (s, EQV | SAB (args[1], args[0], args[2])); + break; + case INDEX_op_nand_i32: + tcg_out32 (s, NAND | SAB (args[1], args[0], args[2])); + break; + case INDEX_op_nor_i32: + tcg_out32 (s, NOR | SAB (args[1], args[0], args[2])); + break; case INDEX_op_mul_i32: if (const_args[2]) { @@ -1374,7 +1529,7 @@ if (args[0] == args[2] || args[0] == args[3]) { tcg_out32 (s, MULLW | TAB (0, args[2], args[3])); tcg_out32 (s, MULHWU | TAB (args[1], args[2], args[3])); - tcg_out_mov (s, args[0], 0); + tcg_out_mov (s, TCG_TYPE_I32, args[0], 0); } else { tcg_out32 (s, MULLW | TAB (args[0], args[2], args[3])); @@ -1416,12 +1571,51 @@ else tcg_out32 (s, SRAW | SAB (args[1], args[0], args[2])); break; + case INDEX_op_rotl_i32: + { + int op = 0 + | RA (args[0]) + | RS (args[1]) + | MB (0) + | ME (31) + | (const_args[2] ? RLWINM | SH (args[2]) + : RLWNM | RB (args[2])) + ; + tcg_out32 (s, op); + } + break; + case INDEX_op_rotr_i32: + if (const_args[2]) { + if (!args[2]) { + tcg_out_mov (s, TCG_TYPE_I32, args[0], args[1]); + } + else { + tcg_out32 (s, RLWINM + | RA (args[0]) + | RS (args[1]) + | SH (32 - args[2]) + | MB (0) + | ME (31) + ); + } + } + else { + tcg_out32 (s, SUBFIC | RT (0) | RA (args[2]) | 32); + tcg_out32 (s, RLWNM + | RA (args[0]) + | RS (args[1]) + | RB (0) + | MB (0) + | ME (31) + ); + } + break; case INDEX_op_add2_i32: if (args[0] == args[3] || args[0] == args[5]) { tcg_out32 (s, ADDC | TAB (0, args[2], args[4])); tcg_out32 (s, ADDE | TAB (args[1], args[3], args[5])); - tcg_out_mov (s, args[0], 0); + tcg_out_mov (s, TCG_TYPE_I32, args[0], 0); } else { tcg_out32 (s, ADDC | TAB (args[0], args[2], args[4])); @@ -1432,7 +1626,7 @@ if (args[0] == args[3] || args[0] == args[5]) { tcg_out32 (s, SUBFC | TAB (0, args[4], args[2])); tcg_out32 (s, SUBFE | TAB (args[1], args[5], args[3])); - tcg_out_mov (s, args[0], 0); + tcg_out_mov (s, TCG_TYPE_I32, args[0], 0); } else { tcg_out32 (s, SUBFC | TAB (args[0], args[4], args[2])); @@ -1458,6 +1652,10 @@ tcg_out32 (s, NEG | RT (args[0]) | RA (args[1])); break; + case INDEX_op_not_i32: + tcg_out32 (s, NOR | SAB (args[1], args[0], args[1])); + break; + case INDEX_op_qemu_ld8u: tcg_out_qemu_ld(s, args, 0); break; @@ -1470,7 +1668,7 @@ case INDEX_op_qemu_ld16s: tcg_out_qemu_ld(s, args, 1 | 4); break; - case INDEX_op_qemu_ld32u: + case INDEX_op_qemu_ld32: tcg_out_qemu_ld(s, args, 2); break; case INDEX_op_qemu_ld64: @@ -1492,9 +1690,105 @@ case INDEX_op_ext8s_i32: tcg_out32 (s, EXTSB | RS (args[1]) | RA (args[0])); break; + case INDEX_op_ext8u_i32: + tcg_out32 (s, RLWINM + | RA (args[0]) + | RS (args[1]) + | SH (0) + | MB (24) + | ME (31) + ); + break; case INDEX_op_ext16s_i32: tcg_out32 (s, EXTSH | RS (args[1]) | RA (args[0])); break; + case INDEX_op_ext16u_i32: + tcg_out32 (s, RLWINM + | RA (args[0]) + | RS (args[1]) + | SH (0) + | MB (16) + | ME (31) + ); + break; + + case INDEX_op_setcond_i32: + tcg_out_setcond (s, args[3], args[0], args[1], args[2], const_args[2]); + break; + case INDEX_op_setcond2_i32: + tcg_out_setcond2 (s, args, const_args); + break; + + case INDEX_op_bswap16_i32: + /* Stolen from gcc's builtin_bswap16 */ + + /* a1 = abcd */ + + /* r0 = (a1 << 8) & 0xff00 # 00d0 */ + tcg_out32 (s, RLWINM + | RA (0) + | RS (args[1]) + | SH (8) + | MB (16) + | ME (23) + ); + + /* a0 = rotate_left (a1, 24) & 0xff # 000c */ + tcg_out32 (s, RLWINM + | RA (args[0]) + | RS (args[1]) + | SH (24) + | MB (24) + | ME (31) + ); + + /* a0 = a0 | r0 # 00dc */ + tcg_out32 (s, OR | SAB (0, args[0], args[0])); + break; + + case INDEX_op_bswap32_i32: + /* Stolen from gcc's builtin_bswap32 */ + { + int a0 = args[0]; + + /* a1 = args[1] # abcd */ + + if (a0 == args[1]) { + a0 = 0; + } + + /* a0 = rotate_left (a1, 8) # bcda */ + tcg_out32 (s, RLWINM + | RA (a0) + | RS (args[1]) + | SH (8) + | MB (0) + | ME (31) + ); + + /* a0 = (a0 & ~0xff000000) | ((a1 << 24) & 0xff000000) # dcda */ + tcg_out32 (s, RLWIMI + | RA (a0) + | RS (args[1]) + | SH (24) + | MB (0) + | ME (7) + ); + + /* a0 = (a0 & ~0x0000ff00) | ((a1 << 24) & 0x0000ff00) # dcba */ + tcg_out32 (s, RLWIMI + | RA (a0) + | RS (args[1]) + | SH (24) + | MB (16) + | ME (23) + ); + + if (!a0) { + tcg_out_mov (s, TCG_TYPE_I32, args[0], a0); + } + } + break; default: tcg_dump_ops (s, stderr); @@ -1536,6 +1830,9 @@ { INDEX_op_shr_i32, { "r", "r", "ri" } }, { INDEX_op_sar_i32, { "r", "r", "ri" } }, + { INDEX_op_rotl_i32, { "r", "r", "ri" } }, + { INDEX_op_rotr_i32, { "r", "r", "ri" } }, + { INDEX_op_brcond_i32, { "r", "ri" } }, { INDEX_op_add2_i32, { "r", "r", "r", "r", "r", "r" } }, @@ -1543,14 +1840,26 @@ { INDEX_op_brcond2_i32, { "r", "r", "r", "r" } }, { INDEX_op_neg_i32, { "r", "r" } }, + { INDEX_op_not_i32, { "r", "r" } }, + + { INDEX_op_andc_i32, { "r", "r", "r" } }, + { INDEX_op_orc_i32, { "r", "r", "r" } }, + { INDEX_op_eqv_i32, { "r", "r", "r" } }, + { INDEX_op_nand_i32, { "r", "r", "r" } }, + { INDEX_op_nor_i32, { "r", "r", "r" } }, + + { INDEX_op_setcond_i32, { "r", "r", "ri" } }, + { INDEX_op_setcond2_i32, { "r", "r", "r", "ri", "ri" } }, + + { INDEX_op_bswap16_i32, { "r", "r" } }, + { INDEX_op_bswap32_i32, { "r", "r" } }, #if TARGET_LONG_BITS == 32 { INDEX_op_qemu_ld8u, { "r", "L" } }, { INDEX_op_qemu_ld8s, { "r", "L" } }, { INDEX_op_qemu_ld16u, { "r", "L" } }, { INDEX_op_qemu_ld16s, { "r", "L" } }, - { INDEX_op_qemu_ld32u, { "r", "L" } }, - { INDEX_op_qemu_ld32s, { "r", "L" } }, + { INDEX_op_qemu_ld32, { "r", "L" } }, { INDEX_op_qemu_ld64, { "r", "r", "L" } }, { INDEX_op_qemu_st8, { "K", "K" } }, @@ -1562,8 +1871,7 @@ { INDEX_op_qemu_ld8s, { "r", "L", "L" } }, { INDEX_op_qemu_ld16u, { "r", "L", "L" } }, { INDEX_op_qemu_ld16s, { "r", "L", "L" } }, - { INDEX_op_qemu_ld32u, { "r", "L", "L" } }, - { INDEX_op_qemu_ld32s, { "r", "L", "L" } }, + { INDEX_op_qemu_ld32, { "r", "L", "L" } }, { INDEX_op_qemu_ld64, { "r", "L", "L", "L" } }, { INDEX_op_qemu_st8, { "K", "K", "K" } }, @@ -1573,17 +1881,19 @@ #endif { INDEX_op_ext8s_i32, { "r", "r" } }, + { INDEX_op_ext8u_i32, { "r", "r" } }, { INDEX_op_ext16s_i32, { "r", "r" } }, + { INDEX_op_ext16u_i32, { "r", "r" } }, { -1 }, }; -void tcg_target_init(TCGContext *s) +static void tcg_target_init(TCGContext *s) { tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0, 0xffffffff); tcg_regset_set32(tcg_target_call_clobber_regs, 0, (1 << TCG_REG_R0) | -#ifdef __APPLE__ +#ifdef _CALL_DARWIN (1 << TCG_REG_R2) | #endif (1 << TCG_REG_R3) | @@ -1601,15 +1911,12 @@ tcg_regset_clear(s->reserved_regs); tcg_regset_set_reg(s->reserved_regs, TCG_REG_R0); tcg_regset_set_reg(s->reserved_regs, TCG_REG_R1); -#ifndef __APPLE__ +#ifndef _CALL_DARWIN tcg_regset_set_reg(s->reserved_regs, TCG_REG_R2); #endif -#ifdef __linux__ +#ifdef _CALL_SYSV tcg_regset_set_reg(s->reserved_regs, TCG_REG_R13); #endif -#ifdef CONFIG_USE_GUEST_BASE - tcg_regset_set_reg(s->reserved_regs, TCG_GUEST_BASE_REG); -#endif tcg_add_target_add_op_defs(ppc_op_defs); } diff -Nru qemu-kvm-0.12.5+noroms/tcg/ppc/tcg-target.h qemu-kvm-0.14.1/tcg/ppc/tcg-target.h --- qemu-kvm-0.12.5+noroms/tcg/ppc/tcg-target.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/tcg/ppc/tcg-target.h 2011-05-11 13:29:46.000000000 +0000 @@ -65,11 +65,11 @@ /* used for function call generation */ #define TCG_REG_CALL_STACK TCG_REG_R1 #define TCG_TARGET_STACK_ALIGN 16 -#if defined __APPLE__ +#if defined _CALL_DARWIN || defined __APPLE__ #define TCG_TARGET_CALL_STACK_OFFSET 24 -#elif defined _AIX +#elif defined _CALL_AIX #define TCG_TARGET_CALL_STACK_OFFSET 52 -#elif defined __linux__ +#elif defined _CALL_SYSV #define TCG_TARGET_CALL_ALIGN_ARGS 1 #define TCG_TARGET_CALL_STACK_OFFSET 8 #else @@ -77,13 +77,22 @@ #endif /* optional instructions */ -#define TCG_TARGET_HAS_neg_i32 #define TCG_TARGET_HAS_div_i32 +#define TCG_TARGET_HAS_rot_i32 #define TCG_TARGET_HAS_ext8s_i32 #define TCG_TARGET_HAS_ext16s_i32 +#define TCG_TARGET_HAS_ext8u_i32 +#define TCG_TARGET_HAS_ext16u_i32 +#define TCG_TARGET_HAS_bswap16_i32 +#define TCG_TARGET_HAS_bswap32_i32 +#define TCG_TARGET_HAS_not_i32 +#define TCG_TARGET_HAS_neg_i32 +#define TCG_TARGET_HAS_andc_i32 +#define TCG_TARGET_HAS_orc_i32 +#define TCG_TARGET_HAS_eqv_i32 +#define TCG_TARGET_HAS_nand_i32 +#define TCG_TARGET_HAS_nor_i32 #define TCG_AREG0 TCG_REG_R27 -#define TCG_AREG1 TCG_REG_R24 -#define TCG_AREG2 TCG_REG_R25 #define TCG_TARGET_HAS_GUEST_BASE diff -Nru qemu-kvm-0.12.5+noroms/tcg/ppc64/tcg-target.c qemu-kvm-0.14.1/tcg/ppc64/tcg-target.c --- qemu-kvm-0.12.5+noroms/tcg/ppc64/tcg-target.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/tcg/ppc64/tcg-target.c 2011-05-11 13:29:46.000000000 +0000 @@ -28,12 +28,6 @@ #define FAST_PATH -#if TARGET_PHYS_ADDR_BITS == 32 -#define LD_ADDEND LWZ -#else -#define LD_ADDEND LD -#endif - #if TARGET_LONG_BITS == 32 #define LD_ADDR LWZU #define CMP_L 0 @@ -56,7 +50,7 @@ static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { "r0", "r1", - "rp", + "r2", "r3", "r4", "r5", @@ -104,6 +98,9 @@ TCG_REG_R29, TCG_REG_R30, TCG_REG_R31, +#ifdef __APPLE__ + TCG_REG_R2, +#endif TCG_REG_R3, TCG_REG_R4, TCG_REG_R5, @@ -112,7 +109,9 @@ TCG_REG_R8, TCG_REG_R9, TCG_REG_R10, +#ifndef __APPLE__ TCG_REG_R11, +#endif TCG_REG_R12, TCG_REG_R24, TCG_REG_R25, @@ -136,6 +135,9 @@ }; static const int tcg_target_callee_save_regs[] = { +#ifdef __APPLE__ + TCG_REG_R11, +#endif TCG_REG_R14, TCG_REG_R15, TCG_REG_R16, @@ -297,6 +299,7 @@ #define LWA XO58( 2) #define LWAX XO31(341) +#define ADDIC OPCD( 12) #define ADDI OPCD( 14) #define ADDIS OPCD( 15) #define ORI OPCD( 24) @@ -324,6 +327,7 @@ #define CRANDC XO19(129) #define CRNAND XO19(225) #define CROR XO19(449) +#define CRNOR XO19( 33) #define EXTSB XO31(954) #define EXTSH XO31(922) @@ -351,6 +355,9 @@ #define MTSPR XO31(467) #define SRAWI XO31(824) #define NEG XO31(104) +#define MFCR XO31( 19) +#define CNTLZW XO31( 26) +#define CNTLZD XO31( 58) #define MULLD XO31(233) #define MULHD XO31( 73) @@ -428,7 +435,7 @@ [TCG_COND_GTU] = BC | BI (7, CR_GT) | BO_COND_TRUE, }; -static void tcg_out_mov (TCGContext *s, int ret, int arg) +static void tcg_out_mov (TCGContext *s, TCGType type, int ret, int arg) { tcg_out32 (s, OR | SAB (arg, ret, arg)); } @@ -455,8 +462,9 @@ int ret, tcg_target_long arg) { int32_t arg32 = arg; + arg = type == TCG_TYPE_I32 ? arg & 0xffffffff : arg; - if (type == TCG_TYPE_I32 || arg == arg32) { + if (arg == arg32) { tcg_out_movi32 (s, ret, arg32); } else { @@ -477,8 +485,31 @@ } } +static void tcg_out_b (TCGContext *s, int mask, tcg_target_long target) +{ + tcg_target_long disp; + + disp = target - (tcg_target_long) s->code_ptr; + if ((disp << 38) >> 38 == disp) + tcg_out32 (s, B | (disp & 0x3fffffc) | mask); + else { + tcg_out_movi (s, TCG_TYPE_I64, 0, (tcg_target_long) target); + tcg_out32 (s, MTSPR | RS (0) | CTR); + tcg_out32 (s, BCCTR | BO_ALWAYS | mask); + } +} + static void tcg_out_call (TCGContext *s, tcg_target_long arg, int const_arg) { +#ifdef __APPLE__ + if (const_arg) { + tcg_out_b (s, LK, arg); + } + else { + tcg_out32 (s, MTSPR | RS (arg) | LR); + tcg_out32 (s, BCLR | BO_ALWAYS | LK); + } +#else int reg; if (const_arg) { @@ -492,6 +523,7 @@ tcg_out32 (s, LD | RT (11) | RA (reg) | 16); tcg_out32 (s, LD | RT (2) | RA (reg) | 8); tcg_out32 (s, BCCTR | BO_ALWAYS | LK); +#endif } static void tcg_out_ldst (TCGContext *s, int ret, int addr, @@ -516,20 +548,6 @@ } } -static void tcg_out_b (TCGContext *s, int mask, tcg_target_long target) -{ - tcg_target_long disp; - - disp = target - (tcg_target_long) s->code_ptr; - if ((disp << 38) >> 38 == disp) - tcg_out32 (s, B | (disp & 0x3fffffc) | mask); - else { - tcg_out_movi (s, TCG_TYPE_I64, 0, (tcg_target_long) target); - tcg_out32 (s, MTSPR | RS (0) | CTR); - tcg_out32 (s, BCCTR | BO_ALWAYS | mask); - } -} - #if defined (CONFIG_SOFTMMU) #include "../../softmmu_defs.h" @@ -626,7 +644,7 @@ #endif /* slow path */ - tcg_out_mov (s, 3, addr_reg); + tcg_out_mov (s, TCG_TYPE_I64, 3, addr_reg); tcg_out_movi (s, TCG_TYPE_I64, 4, mem_index); tcg_out_call (s, (tcg_target_long) qemu_ld_helpers[s_bits], 1); @@ -646,7 +664,7 @@ case 2: case 3: if (data_reg != 3) - tcg_out_mov (s, data_reg, 3); + tcg_out_mov (s, TCG_TYPE_I64, data_reg, 3); break; } label2_ptr = s->code_ptr; @@ -658,7 +676,7 @@ #endif /* r0 now contains &env->tlb_table[mem_index][index].addr_read */ - tcg_out32 (s, (LD_ADDEND + tcg_out32 (s, (LD | RT (r0) | RA (r0) | (offsetof (CPUTLBEntry, addend) @@ -772,7 +790,7 @@ #endif /* slow path */ - tcg_out_mov (s, 3, addr_reg); + tcg_out_mov (s, TCG_TYPE_I64, 3, addr_reg); tcg_out_rld (s, RLDICL, 4, data_reg, 0, 64 - (1 << (3 + opc))); tcg_out_movi (s, TCG_TYPE_I64, 5, mem_index); @@ -786,7 +804,7 @@ reloc_pc14 (label1_ptr, (tcg_target_long) s->code_ptr); #endif - tcg_out32 (s, (LD_ADDEND + tcg_out32 (s, (LD | RT (r0) | RA (r0) | (offsetof (CPUTLBEntry, addend) @@ -842,10 +860,12 @@ #endif } -void tcg_target_qemu_prologue (TCGContext *s) +static void tcg_target_qemu_prologue (TCGContext *s) { int i, frame_size; +#ifndef __APPLE__ uint64_t addr; +#endif frame_size = 0 + 8 /* back chain */ @@ -859,10 +879,12 @@ ; frame_size = (frame_size + 15) & ~15; +#ifndef __APPLE__ /* First emit adhoc function descriptor */ addr = (uint64_t) s->code_ptr + 24; tcg_out32 (s, addr >> 32); tcg_out32 (s, addr); /* entry point */ s->code_ptr += 16; /* skip TOC and environment pointer */ +#endif /* Prologue */ tcg_out32 (s, MFSPR | RT (0) | LR); @@ -877,7 +899,10 @@ tcg_out32 (s, STD | RS (0) | RA (1) | (frame_size + 16)); #ifdef CONFIG_USE_GUEST_BASE - tcg_out_movi (s, TCG_TYPE_I64, TCG_GUEST_BASE_REG, GUEST_BASE); + if (GUEST_BASE) { + tcg_out_movi (s, TCG_TYPE_I64, TCG_GUEST_BASE_REG, GUEST_BASE); + tcg_regset_set_reg(s->reserved_regs, TCG_GUEST_BASE_REG); + } #endif tcg_out32 (s, MTSPR | RS (3) | CTR); @@ -1021,6 +1046,123 @@ } +static void tcg_out_setcond (TCGContext *s, TCGType type, TCGCond cond, + TCGArg arg0, TCGArg arg1, TCGArg arg2, + int const_arg2) +{ + int crop, sh, arg; + + switch (cond) { + case TCG_COND_EQ: + if (const_arg2) { + if (!arg2) { + arg = arg1; + } + else { + arg = 0; + if ((uint16_t) arg2 == arg2) { + tcg_out32 (s, XORI | RS (arg1) | RA (0) | arg2); + } + else { + tcg_out_movi (s, type, 0, arg2); + tcg_out32 (s, XOR | SAB (arg1, 0, 0)); + } + } + } + else { + arg = 0; + tcg_out32 (s, XOR | SAB (arg1, 0, arg2)); + } + + if (type == TCG_TYPE_I64) { + tcg_out32 (s, CNTLZD | RS (arg) | RA (0)); + tcg_out_rld (s, RLDICL, arg0, 0, 58, 6); + } + else { + tcg_out32 (s, CNTLZW | RS (arg) | RA (0)); + tcg_out32 (s, (RLWINM + | RA (arg0) + | RS (0) + | SH (27) + | MB (5) + | ME (31) + ) + ); + } + break; + + case TCG_COND_NE: + if (const_arg2) { + if (!arg2) { + arg = arg1; + } + else { + arg = 0; + if ((uint16_t) arg2 == arg2) { + tcg_out32 (s, XORI | RS (arg1) | RA (0) | arg2); + } + else { + tcg_out_movi (s, type, 0, arg2); + tcg_out32 (s, XOR | SAB (arg1, 0, 0)); + } + } + } + else { + arg = 0; + tcg_out32 (s, XOR | SAB (arg1, 0, arg2)); + } + + if (arg == arg1 && arg1 == arg0) { + tcg_out32 (s, ADDIC | RT (0) | RA (arg) | 0xffff); + tcg_out32 (s, SUBFE | TAB (arg0, 0, arg)); + } + else { + tcg_out32 (s, ADDIC | RT (arg0) | RA (arg) | 0xffff); + tcg_out32 (s, SUBFE | TAB (arg0, arg0, arg)); + } + break; + + case TCG_COND_GT: + case TCG_COND_GTU: + sh = 30; + crop = 0; + goto crtest; + + case TCG_COND_LT: + case TCG_COND_LTU: + sh = 29; + crop = 0; + goto crtest; + + case TCG_COND_GE: + case TCG_COND_GEU: + sh = 31; + crop = CRNOR | BT (7, CR_EQ) | BA (7, CR_LT) | BB (7, CR_LT); + goto crtest; + + case TCG_COND_LE: + case TCG_COND_LEU: + sh = 31; + crop = CRNOR | BT (7, CR_EQ) | BA (7, CR_GT) | BB (7, CR_GT); + crtest: + tcg_out_cmp (s, cond, arg1, arg2, const_arg2, 7, type == TCG_TYPE_I64); + if (crop) tcg_out32 (s, crop); + tcg_out32 (s, MFCR | RT (0)); + tcg_out32 (s, (RLWINM + | RA (arg0) + | RS (0) + | SH (sh) + | MB (31) + | ME (31) + ) + ); + break; + + default: + tcg_abort (); + } +} + static void tcg_out_bc (TCGContext *s, int bc, int label_index) { TCGLabel *l = &s->labels[label_index]; @@ -1036,7 +1178,7 @@ } } -static void tcg_out_brcond (TCGContext *s, int cond, +static void tcg_out_brcond (TCGContext *s, TCGCond cond, TCGArg arg1, TCGArg arg2, int const_arg2, int label_index, int arch64) { @@ -1055,7 +1197,7 @@ flush_icache_range (jmp_addr, jmp_addr + patch_size); } -static void tcg_out_op (TCGContext *s, int opc, const TCGArg *args, +static void tcg_out_op (TCGContext *s, TCGOpcode opc, const TCGArg *args, const int *const_args) { int c; @@ -1373,6 +1515,7 @@ case INDEX_op_qemu_ld16s: tcg_out_qemu_ld (s, args, 1 | 4); break; + case INDEX_op_qemu_ld32: case INDEX_op_qemu_ld32u: tcg_out_qemu_ld (s, args, 2); break; @@ -1410,6 +1553,15 @@ tcg_out32 (s, c | RS (args[1]) | RA (args[0])); break; + case INDEX_op_setcond_i32: + tcg_out_setcond (s, TCG_TYPE_I32, args[3], args[0], args[1], args[2], + const_args[2]); + break; + case INDEX_op_setcond_i64: + tcg_out_setcond (s, TCG_TYPE_I64, args[3], args[0], args[1], args[2], + const_args[2]); + break; + default: tcg_dump_ops (s, stderr); tcg_abort (); @@ -1492,6 +1644,7 @@ { INDEX_op_qemu_ld8s, { "r", "L" } }, { INDEX_op_qemu_ld16u, { "r", "L" } }, { INDEX_op_qemu_ld16s, { "r", "L" } }, + { INDEX_op_qemu_ld32, { "r", "L" } }, { INDEX_op_qemu_ld32u, { "r", "L" } }, { INDEX_op_qemu_ld32s, { "r", "L" } }, { INDEX_op_qemu_ld64, { "r", "L" } }, @@ -1507,15 +1660,21 @@ { INDEX_op_ext16s_i64, { "r", "r" } }, { INDEX_op_ext32s_i64, { "r", "r" } }, + { INDEX_op_setcond_i32, { "r", "r", "ri" } }, + { INDEX_op_setcond_i64, { "r", "r", "ri" } }, + { -1 }, }; -void tcg_target_init (TCGContext *s) +static void tcg_target_init (TCGContext *s) { tcg_regset_set32 (tcg_target_available_regs[TCG_TYPE_I32], 0, 0xffffffff); tcg_regset_set32 (tcg_target_available_regs[TCG_TYPE_I64], 0, 0xffffffff); tcg_regset_set32 (tcg_target_call_clobber_regs, 0, (1 << TCG_REG_R0) | +#ifdef __APPLE__ + (1 << TCG_REG_R2) | +#endif (1 << TCG_REG_R3) | (1 << TCG_REG_R4) | (1 << TCG_REG_R5) | @@ -1531,12 +1690,10 @@ tcg_regset_clear (s->reserved_regs); tcg_regset_set_reg (s->reserved_regs, TCG_REG_R0); tcg_regset_set_reg (s->reserved_regs, TCG_REG_R1); +#ifndef __APPLE__ tcg_regset_set_reg (s->reserved_regs, TCG_REG_R2); - tcg_regset_set_reg (s->reserved_regs, TCG_REG_R13); - -#ifdef CONFIG_USE_GUEST_BASE - tcg_regset_set_reg(s->reserved_regs, TCG_GUEST_BASE_REG); #endif + tcg_regset_set_reg (s->reserved_regs, TCG_REG_R13); tcg_add_target_add_op_defs (ppc_op_defs); } diff -Nru qemu-kvm-0.12.5+noroms/tcg/ppc64/tcg-target.h qemu-kvm-0.14.1/tcg/ppc64/tcg-target.h --- qemu-kvm-0.12.5+noroms/tcg/ppc64/tcg-target.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/tcg/ppc64/tcg-target.h 2011-05-11 13:29:46.000000000 +0000 @@ -68,18 +68,42 @@ #define TCG_TARGET_CALL_STACK_OFFSET 48 /* optional instructions */ -#define TCG_TARGET_HAS_neg_i32 #define TCG_TARGET_HAS_div_i32 -#define TCG_TARGET_HAS_neg_i64 -#define TCG_TARGET_HAS_div_i64 +/* #define TCG_TARGET_HAS_rot_i32 */ #define TCG_TARGET_HAS_ext8s_i32 #define TCG_TARGET_HAS_ext16s_i32 +/* #define TCG_TARGET_HAS_ext8u_i32 */ +/* #define TCG_TARGET_HAS_ext16u_i32 */ +/* #define TCG_TARGET_HAS_bswap16_i32 */ +/* #define TCG_TARGET_HAS_bswap32_i32 */ +/* #define TCG_TARGET_HAS_not_i32 */ +#define TCG_TARGET_HAS_neg_i32 +/* #define TCG_TARGET_HAS_andc_i32 */ +/* #define TCG_TARGET_HAS_orc_i32 */ +/* #define TCG_TARGET_HAS_eqv_i32 */ +/* #define TCG_TARGET_HAS_nand_i32 */ +/* #define TCG_TARGET_HAS_nor_i32 */ + +#define TCG_TARGET_HAS_div_i64 +/* #define TCG_TARGET_HAS_rot_i64 */ #define TCG_TARGET_HAS_ext8s_i64 #define TCG_TARGET_HAS_ext16s_i64 #define TCG_TARGET_HAS_ext32s_i64 +/* #define TCG_TARGET_HAS_ext8u_i64 */ +/* #define TCG_TARGET_HAS_ext16u_i64 */ +/* #define TCG_TARGET_HAS_ext32u_i64 */ +/* #define TCG_TARGET_HAS_bswap16_i64 */ +/* #define TCG_TARGET_HAS_bswap32_i64 */ +/* #define TCG_TARGET_HAS_bswap64_i64 */ +/* #define TCG_TARGET_HAS_not_i64 */ +#define TCG_TARGET_HAS_neg_i64 +/* #define TCG_TARGET_HAS_andc_i64 */ +/* #define TCG_TARGET_HAS_orc_i64 */ +/* #define TCG_TARGET_HAS_eqv_i64 */ +/* #define TCG_TARGET_HAS_nand_i64 */ +/* #define TCG_TARGET_HAS_nor_i64 */ #define TCG_AREG0 TCG_REG_R27 -#define TCG_AREG1 TCG_REG_R24 -#define TCG_AREG2 TCG_REG_R25 #define TCG_TARGET_HAS_GUEST_BASE +#define TCG_TARGET_EXTEND_ARGS 1 diff -Nru qemu-kvm-0.12.5+noroms/tcg/README qemu-kvm-0.14.1/tcg/README --- qemu-kvm-0.12.5+noroms/tcg/README 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/tcg/README 2011-05-11 13:29:46.000000000 +0000 @@ -75,10 +75,13 @@ * Helpers: Using the tcg_gen_helper_x_y it is possible to call any function -taking i32, i64 or pointer types. Before calling an helper, all -globals are stored at their canonical location and it is assumed that -the function can modify them. In the future, function modifiers will -be allowed to tell that the helper does not read or write some globals. +taking i32, i64 or pointer types. By default, before calling a helper, +all globals are stored at their canonical location and it is assumed +that the function can modify them. This can be overridden by the +TCG_CALL_CONST function modifier. By default, the helper is allowed to +modify the CPU state or raise an exception. This can be overridden by +the TCG_CALL_PURE function modifier, in which case the call to the +function is removed if the return value is not used. On some TCG targets (e.g. x86), several calling conventions are supported. @@ -210,7 +213,7 @@ * eqv_i32/i64 t0, t1, t2 -t0=~(t1^t2) +t0=~(t1^t2), or equivalently, t0=t1^~t2 * nand_i32/i64 t0, t1, t2 @@ -265,13 +268,13 @@ * bswap16_i32/i64 t0, t1 -16 bit byte swap on a 32/64 bit value. The two/six high order bytes must be -set to zero. +16 bit byte swap on a 32/64 bit value. It assumes that the two/six high order +bytes are set to zero. * bswap32_i32/i64 t0, t1 -32 bit byte swap on a 32/64 bit value. With a 64 bit value, the four high -order bytes must be set to zero. +32 bit byte swap on a 32/64 bit value. With a 64 bit value, it assumes that +the four high order bytes are set to zero. * bswap64_i64 t0, t1 @@ -282,6 +285,28 @@ Indicate that the value of t0 won't be used later. It is useful to force dead code elimination. +* deposit_i32/i64 dest, t1, t2, pos, len + +Deposit T2 as a bitfield into T1, placing the result in DEST. +The bitfield is described by POS/LEN, which are immediate values: + + LEN - the length of the bitfield + POS - the position of the first bit, counting from the LSB + +For example, pos=8, len=4 indicates a 4-bit field at bit 8. +This operation would be equivalent to + + dest = (t1 & ~0x0f00) | ((t2 << 8) & 0x0f00) + + +********* Conditional moves + +* setcond_i32/i64 cond, dest, t1, t2 + +dest = (t1 cond t2) + +Set DEST to 1 if (T1 cond T2) is true, otherwise set to 0. + ********* Type conversions * ext_i32_i64 t0, t1 @@ -323,9 +348,37 @@ write(t0, t1 + offset) Write 8, 16, 32 or 64 bits to host memory. +********* 64-bit target on 32-bit host support + +The following opcodes are internal to TCG. Thus they are to be implemented by +32-bit host code generators, but are not to be emitted by guest translators. +They are emitted as needed by inline functions within "tcg-op.h". + +* brcond2_i32 cond, t0_low, t0_high, t1_low, t1_high, label + +Similar to brcond, except that the 64-bit values T0 and T1 +are formed from two 32-bit arguments. + +* add2_i32 t0_low, t0_high, t1_low, t1_high, t2_low, t2_high +* sub2_i32 t0_low, t0_high, t1_low, t1_high, t2_low, t2_high + +Similar to add/sub, except that the 64-bit inputs T1 and T2 are +formed from two 32-bit arguments, and the 64-bit output T0 +is returned in two 32-bit outputs. + +* mulu2_i32 t0_low, t0_high, t1, t2 + +Similar to mul, except two 32-bit (unsigned) inputs T1 and T2 yielding +the full 64-bit product T0. The later is returned in two 32-bit outputs. + +* setcond2_i32 cond, dest, t1_low, t1_high, t2_low, t2_high + +Similar to setcond, except that the 64-bit values T1 and T2 are +formed from two 32-bit arguments. The result is a 32-bit value. + ********* QEMU specific operations -* tb_exit t0 +* exit_tb t0 Exit the current TB and return the value t0 (word type). @@ -339,13 +392,17 @@ qemu_ld8s t0, t1, flags qemu_ld16u t0, t1, flags qemu_ld16s t0, t1, flags +qemu_ld32 t0, t1, flags qemu_ld32u t0, t1, flags qemu_ld32s t0, t1, flags qemu_ld64 t0, t1, flags -Load data at the QEMU CPU address t1 into t0. t1 has the QEMU CPU -address type. 'flags' contains the QEMU memory index (selects user or -kernel access) for example. +Load data at the QEMU CPU address t1 into t0. t1 has the QEMU CPU address +type. 'flags' contains the QEMU memory index (selects user or kernel access) +for example. + +Note that "qemu_ld32" implies a 32-bit result, while "qemu_ld32u" and +"qemu_ld32s" imply a 64-bit result appropriately extended from 32 bits. * qemu_st8 t0, t1, flags qemu_st16 t0, t1, flags @@ -445,7 +502,7 @@ the speed of the translation. - Don't hesitate to use helpers for complicated or seldom used target - intructions. There is little performance advantage in using TCG to + instructions. There is little performance advantage in using TCG to implement target instructions taking more than about twenty TCG instructions. diff -Nru qemu-kvm-0.12.5+noroms/tcg/s390/tcg-target.c qemu-kvm-0.14.1/tcg/s390/tcg-target.c --- qemu-kvm-0.12.5+noroms/tcg/s390/tcg-target.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/tcg/s390/tcg-target.c 2011-05-11 13:29:46.000000000 +0000 @@ -2,6 +2,8 @@ * Tiny Code Generator for QEMU * * Copyright (c) 2009 Ulrich Hecht + * Copyright (c) 2009 Alexander Graf + * Copyright (c) 2010 Richard Henderson * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -22,81 +24,2298 @@ * THE SOFTWARE. */ +/* ??? The translation blocks produced by TCG are generally small enough to + be entirely reachable with a 16-bit displacement. Leaving the option for + a 32-bit displacement here Just In Case. */ +#define USE_LONG_BRANCHES 0 + +#define TCG_CT_CONST_32 0x0100 +#define TCG_CT_CONST_NEG 0x0200 +#define TCG_CT_CONST_ADDI 0x0400 +#define TCG_CT_CONST_MULI 0x0800 +#define TCG_CT_CONST_ANDI 0x1000 +#define TCG_CT_CONST_ORI 0x2000 +#define TCG_CT_CONST_XORI 0x4000 +#define TCG_CT_CONST_CMPI 0x8000 + +/* Several places within the instruction set 0 means "no register" + rather than TCG_REG_R0. */ +#define TCG_REG_NONE 0 + +/* A scratch register that may be be used throughout the backend. */ +#define TCG_TMP0 TCG_REG_R14 + +#ifdef CONFIG_USE_GUEST_BASE +#define TCG_GUEST_BASE_REG TCG_REG_R13 +#else +#define TCG_GUEST_BASE_REG TCG_REG_R0 +#endif + +#ifndef GUEST_BASE +#define GUEST_BASE 0 +#endif + + +/* All of the following instructions are prefixed with their instruction + format, and are defined as 8- or 16-bit quantities, even when the two + halves of the 16-bit quantity may appear 32 bits apart in the insn. + This makes it easy to copy the values from the tables in Appendix B. */ +typedef enum S390Opcode { + RIL_AFI = 0xc209, + RIL_AGFI = 0xc208, + RIL_ALGFI = 0xc20a, + RIL_BRASL = 0xc005, + RIL_BRCL = 0xc004, + RIL_CFI = 0xc20d, + RIL_CGFI = 0xc20c, + RIL_CLFI = 0xc20f, + RIL_CLGFI = 0xc20e, + RIL_IIHF = 0xc008, + RIL_IILF = 0xc009, + RIL_LARL = 0xc000, + RIL_LGFI = 0xc001, + RIL_LGRL = 0xc408, + RIL_LLIHF = 0xc00e, + RIL_LLILF = 0xc00f, + RIL_LRL = 0xc40d, + RIL_MSFI = 0xc201, + RIL_MSGFI = 0xc200, + RIL_NIHF = 0xc00a, + RIL_NILF = 0xc00b, + RIL_OIHF = 0xc00c, + RIL_OILF = 0xc00d, + RIL_XIHF = 0xc006, + RIL_XILF = 0xc007, + + RI_AGHI = 0xa70b, + RI_AHI = 0xa70a, + RI_BRC = 0xa704, + RI_IIHH = 0xa500, + RI_IIHL = 0xa501, + RI_IILH = 0xa502, + RI_IILL = 0xa503, + RI_LGHI = 0xa709, + RI_LLIHH = 0xa50c, + RI_LLIHL = 0xa50d, + RI_LLILH = 0xa50e, + RI_LLILL = 0xa50f, + RI_MGHI = 0xa70d, + RI_MHI = 0xa70c, + RI_NIHH = 0xa504, + RI_NIHL = 0xa505, + RI_NILH = 0xa506, + RI_NILL = 0xa507, + RI_OIHH = 0xa508, + RI_OIHL = 0xa509, + RI_OILH = 0xa50a, + RI_OILL = 0xa50b, + + RIE_CGIJ = 0xec7c, + RIE_CGRJ = 0xec64, + RIE_CIJ = 0xec7e, + RIE_CLGRJ = 0xec65, + RIE_CLIJ = 0xec7f, + RIE_CLGIJ = 0xec7d, + RIE_CLRJ = 0xec77, + RIE_CRJ = 0xec76, + + RRE_AGR = 0xb908, + RRE_CGR = 0xb920, + RRE_CLGR = 0xb921, + RRE_DLGR = 0xb987, + RRE_DLR = 0xb997, + RRE_DSGFR = 0xb91d, + RRE_DSGR = 0xb90d, + RRE_LGBR = 0xb906, + RRE_LCGR = 0xb903, + RRE_LGFR = 0xb914, + RRE_LGHR = 0xb907, + RRE_LGR = 0xb904, + RRE_LLGCR = 0xb984, + RRE_LLGFR = 0xb916, + RRE_LLGHR = 0xb985, + RRE_LRVR = 0xb91f, + RRE_LRVGR = 0xb90f, + RRE_LTGR = 0xb902, + RRE_MSGR = 0xb90c, + RRE_MSR = 0xb252, + RRE_NGR = 0xb980, + RRE_OGR = 0xb981, + RRE_SGR = 0xb909, + RRE_XGR = 0xb982, + + RR_AR = 0x1a, + RR_BASR = 0x0d, + RR_BCR = 0x07, + RR_CLR = 0x15, + RR_CR = 0x19, + RR_DR = 0x1d, + RR_LCR = 0x13, + RR_LR = 0x18, + RR_LTR = 0x12, + RR_NR = 0x14, + RR_OR = 0x16, + RR_SR = 0x1b, + RR_XR = 0x17, + + RSY_RLL = 0xeb1d, + RSY_RLLG = 0xeb1c, + RSY_SLLG = 0xeb0d, + RSY_SRAG = 0xeb0a, + RSY_SRLG = 0xeb0c, + + RS_SLL = 0x89, + RS_SRA = 0x8a, + RS_SRL = 0x88, + + RXY_AG = 0xe308, + RXY_AY = 0xe35a, + RXY_CG = 0xe320, + RXY_CY = 0xe359, + RXY_LB = 0xe376, + RXY_LG = 0xe304, + RXY_LGB = 0xe377, + RXY_LGF = 0xe314, + RXY_LGH = 0xe315, + RXY_LHY = 0xe378, + RXY_LLGC = 0xe390, + RXY_LLGF = 0xe316, + RXY_LLGH = 0xe391, + RXY_LMG = 0xeb04, + RXY_LRV = 0xe31e, + RXY_LRVG = 0xe30f, + RXY_LRVH = 0xe31f, + RXY_LY = 0xe358, + RXY_STCY = 0xe372, + RXY_STG = 0xe324, + RXY_STHY = 0xe370, + RXY_STMG = 0xeb24, + RXY_STRV = 0xe33e, + RXY_STRVG = 0xe32f, + RXY_STRVH = 0xe33f, + RXY_STY = 0xe350, + + RX_A = 0x5a, + RX_C = 0x59, + RX_L = 0x58, + RX_LH = 0x48, + RX_ST = 0x50, + RX_STC = 0x42, + RX_STH = 0x40, +} S390Opcode; + +#define LD_SIGNED 0x04 +#define LD_UINT8 0x00 +#define LD_INT8 (LD_UINT8 | LD_SIGNED) +#define LD_UINT16 0x01 +#define LD_INT16 (LD_UINT16 | LD_SIGNED) +#define LD_UINT32 0x02 +#define LD_INT32 (LD_UINT32 | LD_SIGNED) +#define LD_UINT64 0x03 +#define LD_INT64 (LD_UINT64 | LD_SIGNED) + +#ifndef NDEBUG +static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { + "%r0", "%r1", "%r2", "%r3", "%r4", "%r5", "%r6", "%r7", + "%r8", "%r9", "%r10" "%r11" "%r12" "%r13" "%r14" "%r15" +}; +#endif + +/* Since R6 is a potential argument register, choose it last of the + call-saved registers. Likewise prefer the call-clobbered registers + in reverse order to maximize the chance of avoiding the arguments. */ static const int tcg_target_reg_alloc_order[] = { + TCG_REG_R13, + TCG_REG_R12, + TCG_REG_R11, + TCG_REG_R10, + TCG_REG_R9, + TCG_REG_R8, + TCG_REG_R7, + TCG_REG_R6, + TCG_REG_R14, + TCG_REG_R0, + TCG_REG_R1, + TCG_REG_R5, + TCG_REG_R4, + TCG_REG_R3, + TCG_REG_R2, }; static const int tcg_target_call_iarg_regs[] = { + TCG_REG_R2, + TCG_REG_R3, + TCG_REG_R4, + TCG_REG_R5, + TCG_REG_R6, }; static const int tcg_target_call_oarg_regs[] = { + TCG_REG_R2, + TCG_REG_R3, +}; + +#define S390_CC_EQ 8 +#define S390_CC_LT 4 +#define S390_CC_GT 2 +#define S390_CC_OV 1 +#define S390_CC_NE (S390_CC_LT | S390_CC_GT) +#define S390_CC_LE (S390_CC_LT | S390_CC_EQ) +#define S390_CC_GE (S390_CC_GT | S390_CC_EQ) +#define S390_CC_NEVER 0 +#define S390_CC_ALWAYS 15 + +/* Condition codes that result from a COMPARE and COMPARE LOGICAL. */ +static const uint8_t tcg_cond_to_s390_cond[10] = { + [TCG_COND_EQ] = S390_CC_EQ, + [TCG_COND_NE] = S390_CC_NE, + [TCG_COND_LT] = S390_CC_LT, + [TCG_COND_LE] = S390_CC_LE, + [TCG_COND_GT] = S390_CC_GT, + [TCG_COND_GE] = S390_CC_GE, + [TCG_COND_LTU] = S390_CC_LT, + [TCG_COND_LEU] = S390_CC_LE, + [TCG_COND_GTU] = S390_CC_GT, + [TCG_COND_GEU] = S390_CC_GE, +}; + +/* Condition codes that result from a LOAD AND TEST. Here, we have no + unsigned instruction variation, however since the test is vs zero we + can re-map the outcomes appropriately. */ +static const uint8_t tcg_cond_to_ltr_cond[10] = { + [TCG_COND_EQ] = S390_CC_EQ, + [TCG_COND_NE] = S390_CC_NE, + [TCG_COND_LT] = S390_CC_LT, + [TCG_COND_LE] = S390_CC_LE, + [TCG_COND_GT] = S390_CC_GT, + [TCG_COND_GE] = S390_CC_GE, + [TCG_COND_LTU] = S390_CC_NEVER, + [TCG_COND_LEU] = S390_CC_EQ, + [TCG_COND_GTU] = S390_CC_NE, + [TCG_COND_GEU] = S390_CC_ALWAYS, +}; + +#ifdef CONFIG_SOFTMMU + +#include "../../softmmu_defs.h" + +static void *qemu_ld_helpers[4] = { + __ldb_mmu, + __ldw_mmu, + __ldl_mmu, + __ldq_mmu, +}; + +static void *qemu_st_helpers[4] = { + __stb_mmu, + __stw_mmu, + __stl_mmu, + __stq_mmu, }; +#endif + +static uint8_t *tb_ret_addr; + +/* A list of relevant facilities used by this translator. Some of these + are required for proper operation, and these are checked at startup. */ + +#define FACILITY_ZARCH_ACTIVE (1ULL << (63 - 2)) +#define FACILITY_LONG_DISP (1ULL << (63 - 18)) +#define FACILITY_EXT_IMM (1ULL << (63 - 21)) +#define FACILITY_GEN_INST_EXT (1ULL << (63 - 34)) + +static uint64_t facilities; static void patch_reloc(uint8_t *code_ptr, int type, - tcg_target_long value, tcg_target_long addend) + tcg_target_long value, tcg_target_long addend) { - tcg_abort(); + tcg_target_long code_ptr_tl = (tcg_target_long)code_ptr; + tcg_target_long pcrel2; + + /* ??? Not the usual definition of "addend". */ + pcrel2 = (value - (code_ptr_tl + addend)) >> 1; + + switch (type) { + case R_390_PC16DBL: + assert(pcrel2 == (int16_t)pcrel2); + *(int16_t *)code_ptr = pcrel2; + break; + case R_390_PC32DBL: + assert(pcrel2 == (int32_t)pcrel2); + *(int32_t *)code_ptr = pcrel2; + break; + default: + tcg_abort(); + break; + } } -static inline int tcg_target_get_call_iarg_regs_count(int flags) +static int tcg_target_get_call_iarg_regs_count(int flags) { - tcg_abort(); - return 0; + return sizeof(tcg_target_call_iarg_regs) / sizeof(int); } /* parse target specific constraints */ static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str) { - tcg_abort(); + const char *ct_str = *pct_str; + + switch (ct_str[0]) { + case 'r': /* all registers */ + ct->ct |= TCG_CT_REG; + tcg_regset_set32(ct->u.regs, 0, 0xffff); + break; + case 'R': /* not R0 */ + ct->ct |= TCG_CT_REG; + tcg_regset_set32(ct->u.regs, 0, 0xffff); + tcg_regset_reset_reg(ct->u.regs, TCG_REG_R0); + break; + case 'L': /* qemu_ld/st constraint */ + ct->ct |= TCG_CT_REG; + tcg_regset_set32(ct->u.regs, 0, 0xffff); + tcg_regset_reset_reg (ct->u.regs, TCG_REG_R2); + tcg_regset_reset_reg (ct->u.regs, TCG_REG_R3); + break; + case 'a': /* force R2 for division */ + ct->ct |= TCG_CT_REG; + tcg_regset_clear(ct->u.regs); + tcg_regset_set_reg(ct->u.regs, TCG_REG_R2); + break; + case 'b': /* force R3 for division */ + ct->ct |= TCG_CT_REG; + tcg_regset_clear(ct->u.regs); + tcg_regset_set_reg(ct->u.regs, TCG_REG_R3); + break; + case 'N': /* force immediate negate */ + ct->ct |= TCG_CT_CONST_NEG; + break; + case 'W': /* force 32-bit ("word") immediate */ + ct->ct |= TCG_CT_CONST_32; + break; + case 'I': + ct->ct |= TCG_CT_CONST_ADDI; + break; + case 'K': + ct->ct |= TCG_CT_CONST_MULI; + break; + case 'A': + ct->ct |= TCG_CT_CONST_ANDI; + break; + case 'O': + ct->ct |= TCG_CT_CONST_ORI; + break; + case 'X': + ct->ct |= TCG_CT_CONST_XORI; + break; + case 'C': + ct->ct |= TCG_CT_CONST_CMPI; + break; + default: + return -1; + } + ct_str++; + *pct_str = ct_str; + return 0; } +/* Immediates to be used with logical AND. This is an optimization only, + since a full 64-bit immediate AND can always be performed with 4 sequential + NI[LH][LH] instructions. What we're looking for is immediates that we + can load efficiently, and the immediate load plus the reg-reg AND is + smaller than the sequential NI's. */ + +static int tcg_match_andi(int ct, tcg_target_ulong val) +{ + int i; + + if (facilities & FACILITY_EXT_IMM) { + if (ct & TCG_CT_CONST_32) { + /* All 32-bit ANDs can be performed with 1 48-bit insn. */ + return 1; + } + + /* Zero-extensions. */ + if (val == 0xff || val == 0xffff || val == 0xffffffff) { + return 1; + } + } else { + if (ct & TCG_CT_CONST_32) { + val = (uint32_t)val; + } else if (val == 0xffffffff) { + return 1; + } + } + + /* Try all 32-bit insns that can perform it in one go. */ + for (i = 0; i < 4; i++) { + tcg_target_ulong mask = ~(0xffffull << i*16); + if ((val & mask) == mask) { + return 1; + } + } + + /* Look for 16-bit values performing the mask. These are better + to load with LLI[LH][LH]. */ + for (i = 0; i < 4; i++) { + tcg_target_ulong mask = 0xffffull << i*16; + if ((val & mask) == val) { + return 0; + } + } + + /* Look for 32-bit values performing the 64-bit mask. These + are better to load with LLI[LH]F, or if extended immediates + not available, with a pair of LLI insns. */ + if ((ct & TCG_CT_CONST_32) == 0) { + if (val <= 0xffffffff || (val & 0xffffffff) == 0) { + return 0; + } + } + + return 1; +} + +/* Immediates to be used with logical OR. This is an optimization only, + since a full 64-bit immediate OR can always be performed with 4 sequential + OI[LH][LH] instructions. What we're looking for is immediates that we + can load efficiently, and the immediate load plus the reg-reg OR is + smaller than the sequential OI's. */ + +static int tcg_match_ori(int ct, tcg_target_long val) +{ + if (facilities & FACILITY_EXT_IMM) { + if (ct & TCG_CT_CONST_32) { + /* All 32-bit ORs can be performed with 1 48-bit insn. */ + return 1; + } + } + + /* Look for negative values. These are best to load with LGHI. */ + if (val < 0) { + if (val == (int16_t)val) { + return 0; + } + if (facilities & FACILITY_EXT_IMM) { + if (val == (int32_t)val) { + return 0; + } + } + } + + return 1; +} + +/* Immediates to be used with logical XOR. This is almost, but not quite, + only an optimization. XOR with immediate is only supported with the + extended-immediate facility. That said, there are a few patterns for + which it is better to load the value into a register first. */ + +static int tcg_match_xori(int ct, tcg_target_long val) +{ + if ((facilities & FACILITY_EXT_IMM) == 0) { + return 0; + } + + if (ct & TCG_CT_CONST_32) { + /* All 32-bit XORs can be performed with 1 48-bit insn. */ + return 1; + } + + /* Look for negative values. These are best to load with LGHI. */ + if (val < 0 && val == (int32_t)val) { + return 0; + } + + return 1; +} + +/* Imediates to be used with comparisons. */ + +static int tcg_match_cmpi(int ct, tcg_target_long val) +{ + if (facilities & FACILITY_EXT_IMM) { + /* The COMPARE IMMEDIATE instruction is available. */ + if (ct & TCG_CT_CONST_32) { + /* We have a 32-bit immediate and can compare against anything. */ + return 1; + } else { + /* ??? We have no insight here into whether the comparison is + signed or unsigned. The COMPARE IMMEDIATE insn uses a 32-bit + signed immediate, and the COMPARE LOGICAL IMMEDIATE insn uses + a 32-bit unsigned immediate. If we were to use the (semi) + obvious "val == (int32_t)val" we would be enabling unsigned + comparisons vs very large numbers. The only solution is to + take the intersection of the ranges. */ + /* ??? Another possible solution is to simply lie and allow all + constants here and force the out-of-range values into a temp + register in tgen_cmp when we have knowledge of the actual + comparison code in use. */ + return val >= 0 && val <= 0x7fffffff; + } + } else { + /* Only the LOAD AND TEST instruction is available. */ + return val == 0; + } +} + /* Test if a constant matches the constraint. */ -static inline int tcg_target_const_match(tcg_target_long val, - const TCGArgConstraint *arg_ct) +static int tcg_target_const_match(tcg_target_long val, + const TCGArgConstraint *arg_ct) { - tcg_abort(); + int ct = arg_ct->ct; + + if (ct & TCG_CT_CONST) { + return 1; + } + + /* Handle the modifiers. */ + if (ct & TCG_CT_CONST_NEG) { + val = -val; + } + if (ct & TCG_CT_CONST_32) { + val = (int32_t)val; + } + + /* The following are mutually exclusive. */ + if (ct & TCG_CT_CONST_ADDI) { + /* Immediates that may be used with add. If we have the + extended-immediates facility then we have ADD IMMEDIATE + with signed and unsigned 32-bit, otherwise we have only + ADD HALFWORD IMMEDIATE with a signed 16-bit. */ + if (facilities & FACILITY_EXT_IMM) { + return val == (int32_t)val || val == (uint32_t)val; + } else { + return val == (int16_t)val; + } + } else if (ct & TCG_CT_CONST_MULI) { + /* Immediates that may be used with multiply. If we have the + general-instruction-extensions, then we have MULTIPLY SINGLE + IMMEDIATE with a signed 32-bit, otherwise we have only + MULTIPLY HALFWORD IMMEDIATE, with a signed 16-bit. */ + if (facilities & FACILITY_GEN_INST_EXT) { + return val == (int32_t)val; + } else { + return val == (int16_t)val; + } + } else if (ct & TCG_CT_CONST_ANDI) { + return tcg_match_andi(ct, val); + } else if (ct & TCG_CT_CONST_ORI) { + return tcg_match_ori(ct, val); + } else if (ct & TCG_CT_CONST_XORI) { + return tcg_match_xori(ct, val); + } else if (ct & TCG_CT_CONST_CMPI) { + return tcg_match_cmpi(ct, val); + } + return 0; } +/* Emit instructions according to the given instruction format. */ + +static void tcg_out_insn_RR(TCGContext *s, S390Opcode op, TCGReg r1, TCGReg r2) +{ + tcg_out16(s, (op << 8) | (r1 << 4) | r2); +} + +static void tcg_out_insn_RRE(TCGContext *s, S390Opcode op, + TCGReg r1, TCGReg r2) +{ + tcg_out32(s, (op << 16) | (r1 << 4) | r2); +} + +static void tcg_out_insn_RI(TCGContext *s, S390Opcode op, TCGReg r1, int i2) +{ + tcg_out32(s, (op << 16) | (r1 << 20) | (i2 & 0xffff)); +} + +static void tcg_out_insn_RIL(TCGContext *s, S390Opcode op, TCGReg r1, int i2) +{ + tcg_out16(s, op | (r1 << 4)); + tcg_out32(s, i2); +} + +static void tcg_out_insn_RS(TCGContext *s, S390Opcode op, TCGReg r1, + TCGReg b2, TCGReg r3, int disp) +{ + tcg_out32(s, (op << 24) | (r1 << 20) | (r3 << 16) | (b2 << 12) + | (disp & 0xfff)); +} + +static void tcg_out_insn_RSY(TCGContext *s, S390Opcode op, TCGReg r1, + TCGReg b2, TCGReg r3, int disp) +{ + tcg_out16(s, (op & 0xff00) | (r1 << 4) | r3); + tcg_out32(s, (op & 0xff) | (b2 << 28) + | ((disp & 0xfff) << 16) | ((disp & 0xff000) >> 4)); +} + +#define tcg_out_insn_RX tcg_out_insn_RS +#define tcg_out_insn_RXY tcg_out_insn_RSY + +/* Emit an opcode with "type-checking" of the format. */ +#define tcg_out_insn(S, FMT, OP, ...) \ + glue(tcg_out_insn_,FMT)(S, glue(glue(FMT,_),OP), ## __VA_ARGS__) + + +/* emit 64-bit shifts */ +static void tcg_out_sh64(TCGContext* s, S390Opcode op, TCGReg dest, + TCGReg src, TCGReg sh_reg, int sh_imm) +{ + tcg_out_insn_RSY(s, op, dest, sh_reg, src, sh_imm); +} + +/* emit 32-bit shifts */ +static void tcg_out_sh32(TCGContext* s, S390Opcode op, TCGReg dest, + TCGReg sh_reg, int sh_imm) +{ + tcg_out_insn_RS(s, op, dest, sh_reg, 0, sh_imm); +} + +static void tcg_out_mov(TCGContext *s, TCGType type, TCGReg dst, TCGReg src) +{ + if (src != dst) { + if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RR, LR, dst, src); + } else { + tcg_out_insn(s, RRE, LGR, dst, src); + } + } +} + /* load a register with an immediate value */ -static inline void tcg_out_movi(TCGContext *s, TCGType type, - int ret, tcg_target_long arg) +static void tcg_out_movi(TCGContext *s, TCGType type, + TCGReg ret, tcg_target_long sval) { - tcg_abort(); + static const S390Opcode lli_insns[4] = { + RI_LLILL, RI_LLILH, RI_LLIHL, RI_LLIHH + }; + + tcg_target_ulong uval = sval; + int i; + + if (type == TCG_TYPE_I32) { + uval = (uint32_t)sval; + sval = (int32_t)sval; + } + + /* Try all 32-bit insns that can load it in one go. */ + if (sval >= -0x8000 && sval < 0x8000) { + tcg_out_insn(s, RI, LGHI, ret, sval); + return; + } + + for (i = 0; i < 4; i++) { + tcg_target_long mask = 0xffffull << i*16; + if ((uval & mask) == uval) { + tcg_out_insn_RI(s, lli_insns[i], ret, uval >> i*16); + return; + } + } + + /* Try all 48-bit insns that can load it in one go. */ + if (facilities & FACILITY_EXT_IMM) { + if (sval == (int32_t)sval) { + tcg_out_insn(s, RIL, LGFI, ret, sval); + return; + } + if (uval <= 0xffffffff) { + tcg_out_insn(s, RIL, LLILF, ret, uval); + return; + } + if ((uval & 0xffffffff) == 0) { + tcg_out_insn(s, RIL, LLIHF, ret, uval >> 31 >> 1); + return; + } + } + + /* Try for PC-relative address load. */ + if ((sval & 1) == 0) { + intptr_t off = (sval - (intptr_t)s->code_ptr) >> 1; + if (off == (int32_t)off) { + tcg_out_insn(s, RIL, LARL, ret, off); + return; + } + } + + /* If extended immediates are not present, then we may have to issue + several instructions to load the low 32 bits. */ + if (!(facilities & FACILITY_EXT_IMM)) { + /* A 32-bit unsigned value can be loaded in 2 insns. And given + that the lli_insns loop above did not succeed, we know that + both insns are required. */ + if (uval <= 0xffffffff) { + tcg_out_insn(s, RI, LLILL, ret, uval); + tcg_out_insn(s, RI, IILH, ret, uval >> 16); + return; + } + + /* If all high bits are set, the value can be loaded in 2 or 3 insns. + We first want to make sure that all the high bits get set. With + luck the low 16-bits can be considered negative to perform that for + free, otherwise we load an explicit -1. */ + if (sval >> 31 >> 1 == -1) { + if (uval & 0x8000) { + tcg_out_insn(s, RI, LGHI, ret, uval); + } else { + tcg_out_insn(s, RI, LGHI, ret, -1); + tcg_out_insn(s, RI, IILL, ret, uval); + } + tcg_out_insn(s, RI, IILH, ret, uval >> 16); + return; + } + } + + /* If we get here, both the high and low parts have non-zero bits. */ + + /* Recurse to load the lower 32-bits. */ + tcg_out_movi(s, TCG_TYPE_I32, ret, sval); + + /* Insert data into the high 32-bits. */ + uval = uval >> 31 >> 1; + if (facilities & FACILITY_EXT_IMM) { + if (uval < 0x10000) { + tcg_out_insn(s, RI, IIHL, ret, uval); + } else if ((uval & 0xffff) == 0) { + tcg_out_insn(s, RI, IIHH, ret, uval >> 16); + } else { + tcg_out_insn(s, RIL, IIHF, ret, uval); + } + } else { + if (uval & 0xffff) { + tcg_out_insn(s, RI, IIHL, ret, uval); + } + if (uval & 0xffff0000) { + tcg_out_insn(s, RI, IIHH, ret, uval >> 16); + } + } +} + + +/* Emit a load/store type instruction. Inputs are: + DATA: The register to be loaded or stored. + BASE+OFS: The effective address. + OPC_RX: If the operation has an RX format opcode (e.g. STC), otherwise 0. + OPC_RXY: The RXY format opcode for the operation (e.g. STCY). */ + +static void tcg_out_mem(TCGContext *s, S390Opcode opc_rx, S390Opcode opc_rxy, + TCGReg data, TCGReg base, TCGReg index, + tcg_target_long ofs) +{ + if (ofs < -0x80000 || ofs >= 0x80000) { + /* Combine the low 16 bits of the offset with the actual load insn; + the high 48 bits must come from an immediate load. */ + tcg_out_movi(s, TCG_TYPE_PTR, TCG_TMP0, ofs & ~0xffff); + ofs &= 0xffff; + + /* If we were already given an index register, add it in. */ + if (index != TCG_REG_NONE) { + tcg_out_insn(s, RRE, AGR, TCG_TMP0, index); + } + index = TCG_TMP0; + } + + if (opc_rx && ofs >= 0 && ofs < 0x1000) { + tcg_out_insn_RX(s, opc_rx, data, base, index, ofs); + } else { + tcg_out_insn_RXY(s, opc_rxy, data, base, index, ofs); + } } + /* load data without address translation or endianness conversion */ -static inline void tcg_out_ld(TCGContext *s, TCGType type, int arg, - int arg1, tcg_target_long arg2) +static inline void tcg_out_ld(TCGContext *s, TCGType type, TCGReg data, + TCGReg base, tcg_target_long ofs) { - tcg_abort(); + if (type == TCG_TYPE_I32) { + tcg_out_mem(s, RX_L, RXY_LY, data, base, TCG_REG_NONE, ofs); + } else { + tcg_out_mem(s, 0, RXY_LG, data, base, TCG_REG_NONE, ofs); + } } -static inline void tcg_out_st(TCGContext *s, TCGType type, int arg, - int arg1, tcg_target_long arg2) +static inline void tcg_out_st(TCGContext *s, TCGType type, TCGReg data, + TCGReg base, tcg_target_long ofs) { - tcg_abort(); + if (type == TCG_TYPE_I32) { + tcg_out_mem(s, RX_ST, RXY_STY, data, base, TCG_REG_NONE, ofs); + } else { + tcg_out_mem(s, 0, RXY_STG, data, base, TCG_REG_NONE, ofs); + } +} + +/* load data from an absolute host address */ +static void tcg_out_ld_abs(TCGContext *s, TCGType type, TCGReg dest, void *abs) +{ + tcg_target_long addr = (tcg_target_long)abs; + + if (facilities & FACILITY_GEN_INST_EXT) { + tcg_target_long disp = (addr - (tcg_target_long)s->code_ptr) >> 1; + if (disp == (int32_t)disp) { + if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RIL, LRL, dest, disp); + } else { + tcg_out_insn(s, RIL, LGRL, dest, disp); + } + return; + } + } + + tcg_out_movi(s, TCG_TYPE_PTR, dest, addr & ~0xffff); + tcg_out_ld(s, type, dest, dest, addr & 0xffff); +} + +static void tgen_ext8s(TCGContext *s, TCGType type, TCGReg dest, TCGReg src) +{ + if (facilities & FACILITY_EXT_IMM) { + tcg_out_insn(s, RRE, LGBR, dest, src); + return; + } + + if (type == TCG_TYPE_I32) { + if (dest == src) { + tcg_out_sh32(s, RS_SLL, dest, TCG_REG_NONE, 24); + } else { + tcg_out_sh64(s, RSY_SLLG, dest, src, TCG_REG_NONE, 24); + } + tcg_out_sh32(s, RS_SRA, dest, TCG_REG_NONE, 24); + } else { + tcg_out_sh64(s, RSY_SLLG, dest, src, TCG_REG_NONE, 56); + tcg_out_sh64(s, RSY_SRAG, dest, dest, TCG_REG_NONE, 56); + } +} + +static void tgen_ext8u(TCGContext *s, TCGType type, TCGReg dest, TCGReg src) +{ + if (facilities & FACILITY_EXT_IMM) { + tcg_out_insn(s, RRE, LLGCR, dest, src); + return; + } + + if (dest == src) { + tcg_out_movi(s, type, TCG_TMP0, 0xff); + src = TCG_TMP0; + } else { + tcg_out_movi(s, type, dest, 0xff); + } + if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RR, NR, dest, src); + } else { + tcg_out_insn(s, RRE, NGR, dest, src); + } +} + +static void tgen_ext16s(TCGContext *s, TCGType type, TCGReg dest, TCGReg src) +{ + if (facilities & FACILITY_EXT_IMM) { + tcg_out_insn(s, RRE, LGHR, dest, src); + return; + } + + if (type == TCG_TYPE_I32) { + if (dest == src) { + tcg_out_sh32(s, RS_SLL, dest, TCG_REG_NONE, 16); + } else { + tcg_out_sh64(s, RSY_SLLG, dest, src, TCG_REG_NONE, 16); + } + tcg_out_sh32(s, RS_SRA, dest, TCG_REG_NONE, 16); + } else { + tcg_out_sh64(s, RSY_SLLG, dest, src, TCG_REG_NONE, 48); + tcg_out_sh64(s, RSY_SRAG, dest, dest, TCG_REG_NONE, 48); + } +} + +static void tgen_ext16u(TCGContext *s, TCGType type, TCGReg dest, TCGReg src) +{ + if (facilities & FACILITY_EXT_IMM) { + tcg_out_insn(s, RRE, LLGHR, dest, src); + return; + } + + if (dest == src) { + tcg_out_movi(s, type, TCG_TMP0, 0xffff); + src = TCG_TMP0; + } else { + tcg_out_movi(s, type, dest, 0xffff); + } + if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RR, NR, dest, src); + } else { + tcg_out_insn(s, RRE, NGR, dest, src); + } +} + +static inline void tgen_ext32s(TCGContext *s, TCGReg dest, TCGReg src) +{ + tcg_out_insn(s, RRE, LGFR, dest, src); +} + +static inline void tgen_ext32u(TCGContext *s, TCGReg dest, TCGReg src) +{ + tcg_out_insn(s, RRE, LLGFR, dest, src); +} + +static inline void tgen32_addi(TCGContext *s, TCGReg dest, int32_t val) +{ + if (val == (int16_t)val) { + tcg_out_insn(s, RI, AHI, dest, val); + } else { + tcg_out_insn(s, RIL, AFI, dest, val); + } +} + +static inline void tgen64_addi(TCGContext *s, TCGReg dest, int64_t val) +{ + if (val == (int16_t)val) { + tcg_out_insn(s, RI, AGHI, dest, val); + } else if (val == (int32_t)val) { + tcg_out_insn(s, RIL, AGFI, dest, val); + } else if (val == (uint32_t)val) { + tcg_out_insn(s, RIL, ALGFI, dest, val); + } else { + tcg_abort(); + } + +} + +static void tgen64_andi(TCGContext *s, TCGReg dest, tcg_target_ulong val) +{ + static const S390Opcode ni_insns[4] = { + RI_NILL, RI_NILH, RI_NIHL, RI_NIHH + }; + static const S390Opcode nif_insns[2] = { + RIL_NILF, RIL_NIHF + }; + + int i; + + /* Look for no-op. */ + if (val == -1) { + return; + } + + /* Look for the zero-extensions. */ + if (val == 0xffffffff) { + tgen_ext32u(s, dest, dest); + return; + } + + if (facilities & FACILITY_EXT_IMM) { + if (val == 0xff) { + tgen_ext8u(s, TCG_TYPE_I64, dest, dest); + return; + } + if (val == 0xffff) { + tgen_ext16u(s, TCG_TYPE_I64, dest, dest); + return; + } + + /* Try all 32-bit insns that can perform it in one go. */ + for (i = 0; i < 4; i++) { + tcg_target_ulong mask = ~(0xffffull << i*16); + if ((val & mask) == mask) { + tcg_out_insn_RI(s, ni_insns[i], dest, val >> i*16); + return; + } + } + + /* Try all 48-bit insns that can perform it in one go. */ + if (facilities & FACILITY_EXT_IMM) { + for (i = 0; i < 2; i++) { + tcg_target_ulong mask = ~(0xffffffffull << i*32); + if ((val & mask) == mask) { + tcg_out_insn_RIL(s, nif_insns[i], dest, val >> i*32); + return; + } + } + } + + /* Perform the AND via sequential modifications to the high and low + parts. Do this via recursion to handle 16-bit vs 32-bit masks in + each half. */ + tgen64_andi(s, dest, val | 0xffffffff00000000ull); + tgen64_andi(s, dest, val | 0x00000000ffffffffull); + } else { + /* With no extended-immediate facility, just emit the sequence. */ + for (i = 0; i < 4; i++) { + tcg_target_ulong mask = 0xffffull << i*16; + if ((val & mask) != mask) { + tcg_out_insn_RI(s, ni_insns[i], dest, val >> i*16); + } + } + } +} + +static void tgen64_ori(TCGContext *s, TCGReg dest, tcg_target_ulong val) +{ + static const S390Opcode oi_insns[4] = { + RI_OILL, RI_OILH, RI_OIHL, RI_OIHH + }; + static const S390Opcode nif_insns[2] = { + RIL_OILF, RIL_OIHF + }; + + int i; + + /* Look for no-op. */ + if (val == 0) { + return; + } + + if (facilities & FACILITY_EXT_IMM) { + /* Try all 32-bit insns that can perform it in one go. */ + for (i = 0; i < 4; i++) { + tcg_target_ulong mask = (0xffffull << i*16); + if ((val & mask) != 0 && (val & ~mask) == 0) { + tcg_out_insn_RI(s, oi_insns[i], dest, val >> i*16); + return; + } + } + + /* Try all 48-bit insns that can perform it in one go. */ + for (i = 0; i < 2; i++) { + tcg_target_ulong mask = (0xffffffffull << i*32); + if ((val & mask) != 0 && (val & ~mask) == 0) { + tcg_out_insn_RIL(s, nif_insns[i], dest, val >> i*32); + return; + } + } + + /* Perform the OR via sequential modifications to the high and + low parts. Do this via recursion to handle 16-bit vs 32-bit + masks in each half. */ + tgen64_ori(s, dest, val & 0x00000000ffffffffull); + tgen64_ori(s, dest, val & 0xffffffff00000000ull); + } else { + /* With no extended-immediate facility, we don't need to be so + clever. Just iterate over the insns and mask in the constant. */ + for (i = 0; i < 4; i++) { + tcg_target_ulong mask = (0xffffull << i*16); + if ((val & mask) != 0) { + tcg_out_insn_RI(s, oi_insns[i], dest, val >> i*16); + } + } + } +} + +static void tgen64_xori(TCGContext *s, TCGReg dest, tcg_target_ulong val) +{ + /* Perform the xor by parts. */ + if (val & 0xffffffff) { + tcg_out_insn(s, RIL, XILF, dest, val); + } + if (val > 0xffffffff) { + tcg_out_insn(s, RIL, XIHF, dest, val >> 31 >> 1); + } } -static inline void tcg_out_op(TCGContext *s, int opc, +static int tgen_cmp(TCGContext *s, TCGType type, TCGCond c, TCGReg r1, + TCGArg c2, int c2const) +{ + bool is_unsigned = (c > TCG_COND_GT); + if (c2const) { + if (c2 == 0) { + if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RR, LTR, r1, r1); + } else { + tcg_out_insn(s, RRE, LTGR, r1, r1); + } + return tcg_cond_to_ltr_cond[c]; + } else { + if (is_unsigned) { + if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RIL, CLFI, r1, c2); + } else { + tcg_out_insn(s, RIL, CLGFI, r1, c2); + } + } else { + if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RIL, CFI, r1, c2); + } else { + tcg_out_insn(s, RIL, CGFI, r1, c2); + } + } + } + } else { + if (is_unsigned) { + if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RR, CLR, r1, c2); + } else { + tcg_out_insn(s, RRE, CLGR, r1, c2); + } + } else { + if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RR, CR, r1, c2); + } else { + tcg_out_insn(s, RRE, CGR, r1, c2); + } + } + } + return tcg_cond_to_s390_cond[c]; +} + +static void tgen_setcond(TCGContext *s, TCGType type, TCGCond c, + TCGReg dest, TCGReg r1, TCGArg c2, int c2const) +{ + int cc = tgen_cmp(s, type, c, r1, c2, c2const); + + /* Emit: r1 = 1; if (cc) goto over; r1 = 0; over: */ + tcg_out_movi(s, type, dest, 1); + tcg_out_insn(s, RI, BRC, cc, (4 + 4) >> 1); + tcg_out_movi(s, type, dest, 0); +} + +static void tgen_gotoi(TCGContext *s, int cc, tcg_target_long dest) +{ + tcg_target_long off = (dest - (tcg_target_long)s->code_ptr) >> 1; + if (off > -0x8000 && off < 0x7fff) { + tcg_out_insn(s, RI, BRC, cc, off); + } else if (off == (int32_t)off) { + tcg_out_insn(s, RIL, BRCL, cc, off); + } else { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_TMP0, dest); + tcg_out_insn(s, RR, BCR, cc, TCG_TMP0); + } +} + +static void tgen_branch(TCGContext *s, int cc, int labelno) +{ + TCGLabel* l = &s->labels[labelno]; + if (l->has_value) { + tgen_gotoi(s, cc, l->u.value); + } else if (USE_LONG_BRANCHES) { + tcg_out16(s, RIL_BRCL | (cc << 4)); + tcg_out_reloc(s, s->code_ptr, R_390_PC32DBL, labelno, -2); + s->code_ptr += 4; + } else { + tcg_out16(s, RI_BRC | (cc << 4)); + tcg_out_reloc(s, s->code_ptr, R_390_PC16DBL, labelno, -2); + s->code_ptr += 2; + } +} + +static void tgen_compare_branch(TCGContext *s, S390Opcode opc, int cc, + TCGReg r1, TCGReg r2, int labelno) +{ + TCGLabel* l = &s->labels[labelno]; + tcg_target_long off; + + if (l->has_value) { + off = (l->u.value - (tcg_target_long)s->code_ptr) >> 1; + } else { + /* We need to keep the offset unchanged for retranslation. */ + off = ((int16_t *)s->code_ptr)[1]; + tcg_out_reloc(s, s->code_ptr + 2, R_390_PC16DBL, labelno, -2); + } + + tcg_out16(s, (opc & 0xff00) | (r1 << 4) | r2); + tcg_out16(s, off); + tcg_out16(s, cc << 12 | (opc & 0xff)); +} + +static void tgen_compare_imm_branch(TCGContext *s, S390Opcode opc, int cc, + TCGReg r1, int i2, int labelno) +{ + TCGLabel* l = &s->labels[labelno]; + tcg_target_long off; + + if (l->has_value) { + off = (l->u.value - (tcg_target_long)s->code_ptr) >> 1; + } else { + /* We need to keep the offset unchanged for retranslation. */ + off = ((int16_t *)s->code_ptr)[1]; + tcg_out_reloc(s, s->code_ptr + 2, R_390_PC16DBL, labelno, -2); + } + + tcg_out16(s, (opc & 0xff00) | (r1 << 4) | cc); + tcg_out16(s, off); + tcg_out16(s, (i2 << 8) | (opc & 0xff)); +} + +static void tgen_brcond(TCGContext *s, TCGType type, TCGCond c, + TCGReg r1, TCGArg c2, int c2const, int labelno) +{ + int cc; + + if (facilities & FACILITY_GEN_INST_EXT) { + bool is_unsigned = (c > TCG_COND_GT); + bool in_range; + S390Opcode opc; + + cc = tcg_cond_to_s390_cond[c]; + + if (!c2const) { + opc = (type == TCG_TYPE_I32 + ? (is_unsigned ? RIE_CLRJ : RIE_CRJ) + : (is_unsigned ? RIE_CLGRJ : RIE_CGRJ)); + tgen_compare_branch(s, opc, cc, r1, c2, labelno); + return; + } + + /* COMPARE IMMEDIATE AND BRANCH RELATIVE has an 8-bit immediate field. + If the immediate we've been given does not fit that range, we'll + fall back to separate compare and branch instructions using the + larger comparison range afforded by COMPARE IMMEDIATE. */ + if (type == TCG_TYPE_I32) { + if (is_unsigned) { + opc = RIE_CLIJ; + in_range = (uint32_t)c2 == (uint8_t)c2; + } else { + opc = RIE_CIJ; + in_range = (int32_t)c2 == (int8_t)c2; + } + } else { + if (is_unsigned) { + opc = RIE_CLGIJ; + in_range = (uint64_t)c2 == (uint8_t)c2; + } else { + opc = RIE_CGIJ; + in_range = (int64_t)c2 == (int8_t)c2; + } + } + if (in_range) { + tgen_compare_imm_branch(s, opc, cc, r1, c2, labelno); + return; + } + } + + cc = tgen_cmp(s, type, c, r1, c2, c2const); + tgen_branch(s, cc, labelno); +} + +static void tgen_calli(TCGContext *s, tcg_target_long dest) +{ + tcg_target_long off = (dest - (tcg_target_long)s->code_ptr) >> 1; + if (off == (int32_t)off) { + tcg_out_insn(s, RIL, BRASL, TCG_REG_R14, off); + } else { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_TMP0, dest); + tcg_out_insn(s, RR, BASR, TCG_REG_R14, TCG_TMP0); + } +} + +static void tcg_out_qemu_ld_direct(TCGContext *s, int opc, TCGReg data, + TCGReg base, TCGReg index, int disp) +{ +#ifdef TARGET_WORDS_BIGENDIAN + const int bswap = 0; +#else + const int bswap = 1; +#endif + switch (opc) { + case LD_UINT8: + tcg_out_insn(s, RXY, LLGC, data, base, index, disp); + break; + case LD_INT8: + tcg_out_insn(s, RXY, LGB, data, base, index, disp); + break; + case LD_UINT16: + if (bswap) { + /* swapped unsigned halfword load with upper bits zeroed */ + tcg_out_insn(s, RXY, LRVH, data, base, index, disp); + tgen_ext16u(s, TCG_TYPE_I64, data, data); + } else { + tcg_out_insn(s, RXY, LLGH, data, base, index, disp); + } + break; + case LD_INT16: + if (bswap) { + /* swapped sign-extended halfword load */ + tcg_out_insn(s, RXY, LRVH, data, base, index, disp); + tgen_ext16s(s, TCG_TYPE_I64, data, data); + } else { + tcg_out_insn(s, RXY, LGH, data, base, index, disp); + } + break; + case LD_UINT32: + if (bswap) { + /* swapped unsigned int load with upper bits zeroed */ + tcg_out_insn(s, RXY, LRV, data, base, index, disp); + tgen_ext32u(s, data, data); + } else { + tcg_out_insn(s, RXY, LLGF, data, base, index, disp); + } + break; + case LD_INT32: + if (bswap) { + /* swapped sign-extended int load */ + tcg_out_insn(s, RXY, LRV, data, base, index, disp); + tgen_ext32s(s, data, data); + } else { + tcg_out_insn(s, RXY, LGF, data, base, index, disp); + } + break; + case LD_UINT64: + if (bswap) { + tcg_out_insn(s, RXY, LRVG, data, base, index, disp); + } else { + tcg_out_insn(s, RXY, LG, data, base, index, disp); + } + break; + default: + tcg_abort(); + } +} + +static void tcg_out_qemu_st_direct(TCGContext *s, int opc, TCGReg data, + TCGReg base, TCGReg index, int disp) +{ +#ifdef TARGET_WORDS_BIGENDIAN + const int bswap = 0; +#else + const int bswap = 1; +#endif + switch (opc) { + case LD_UINT8: + if (disp >= 0 && disp < 0x1000) { + tcg_out_insn(s, RX, STC, data, base, index, disp); + } else { + tcg_out_insn(s, RXY, STCY, data, base, index, disp); + } + break; + case LD_UINT16: + if (bswap) { + tcg_out_insn(s, RXY, STRVH, data, base, index, disp); + } else if (disp >= 0 && disp < 0x1000) { + tcg_out_insn(s, RX, STH, data, base, index, disp); + } else { + tcg_out_insn(s, RXY, STHY, data, base, index, disp); + } + break; + case LD_UINT32: + if (bswap) { + tcg_out_insn(s, RXY, STRV, data, base, index, disp); + } else if (disp >= 0 && disp < 0x1000) { + tcg_out_insn(s, RX, ST, data, base, index, disp); + } else { + tcg_out_insn(s, RXY, STY, data, base, index, disp); + } + break; + case LD_UINT64: + if (bswap) { + tcg_out_insn(s, RXY, STRVG, data, base, index, disp); + } else { + tcg_out_insn(s, RXY, STG, data, base, index, disp); + } + break; + default: + tcg_abort(); + } +} + +#if defined(CONFIG_SOFTMMU) +static void tgen64_andi_tmp(TCGContext *s, TCGReg dest, tcg_target_ulong val) +{ + if (tcg_match_andi(0, val)) { + tcg_out_movi(s, TCG_TYPE_I64, TCG_TMP0, val); + tcg_out_insn(s, RRE, NGR, dest, TCG_TMP0); + } else { + tgen64_andi(s, dest, val); + } +} + +static void tcg_prepare_qemu_ldst(TCGContext* s, TCGReg data_reg, + TCGReg addr_reg, int mem_index, int opc, + uint16_t **label2_ptr_p, int is_store) +{ + const TCGReg arg0 = TCG_REG_R2; + const TCGReg arg1 = TCG_REG_R3; + int s_bits = opc & 3; + uint16_t *label1_ptr; + tcg_target_long ofs; + + if (TARGET_LONG_BITS == 32) { + tgen_ext32u(s, arg0, addr_reg); + } else { + tcg_out_mov(s, TCG_TYPE_I64, arg0, addr_reg); + } + + tcg_out_sh64(s, RSY_SRLG, arg1, addr_reg, TCG_REG_NONE, + TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS); + + tgen64_andi_tmp(s, arg0, TARGET_PAGE_MASK | ((1 << s_bits) - 1)); + tgen64_andi_tmp(s, arg1, (CPU_TLB_SIZE - 1) << CPU_TLB_ENTRY_BITS); + + if (is_store) { + ofs = offsetof(CPUState, tlb_table[mem_index][0].addr_write); + } else { + ofs = offsetof(CPUState, tlb_table[mem_index][0].addr_read); + } + assert(ofs < 0x80000); + + if (TARGET_LONG_BITS == 32) { + tcg_out_mem(s, RX_C, RXY_CY, arg0, arg1, TCG_AREG0, ofs); + } else { + tcg_out_mem(s, 0, RXY_CG, arg0, arg1, TCG_AREG0, ofs); + } + + if (TARGET_LONG_BITS == 32) { + tgen_ext32u(s, arg0, addr_reg); + } else { + tcg_out_mov(s, TCG_TYPE_I64, arg0, addr_reg); + } + + label1_ptr = (uint16_t*)s->code_ptr; + + /* je label1 (offset will be patched in later) */ + tcg_out_insn(s, RI, BRC, S390_CC_EQ, 0); + + /* call load/store helper */ + if (is_store) { + /* Make sure to zero-extend the value to the full register + for the calling convention. */ + switch (opc) { + case LD_UINT8: + tgen_ext8u(s, TCG_TYPE_I64, arg1, data_reg); + break; + case LD_UINT16: + tgen_ext16u(s, TCG_TYPE_I64, arg1, data_reg); + break; + case LD_UINT32: + tgen_ext32u(s, arg1, data_reg); + break; + case LD_UINT64: + tcg_out_mov(s, TCG_TYPE_I64, arg1, data_reg); + break; + default: + tcg_abort(); + } + tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_R4, mem_index); + tgen_calli(s, (tcg_target_ulong)qemu_st_helpers[s_bits]); + } else { + tcg_out_movi(s, TCG_TYPE_I32, arg1, mem_index); + tgen_calli(s, (tcg_target_ulong)qemu_ld_helpers[s_bits]); + + /* sign extension */ + switch (opc) { + case LD_INT8: + tgen_ext8s(s, TCG_TYPE_I64, data_reg, arg0); + break; + case LD_INT16: + tgen_ext16s(s, TCG_TYPE_I64, data_reg, arg0); + break; + case LD_INT32: + tgen_ext32s(s, data_reg, arg0); + break; + default: + /* unsigned -> just copy */ + tcg_out_mov(s, TCG_TYPE_I64, data_reg, arg0); + break; + } + } + + /* jump to label2 (end) */ + *label2_ptr_p = (uint16_t*)s->code_ptr; + + tcg_out_insn(s, RI, BRC, S390_CC_ALWAYS, 0); + + /* this is label1, patch branch */ + *(label1_ptr + 1) = ((unsigned long)s->code_ptr - + (unsigned long)label1_ptr) >> 1; + + ofs = offsetof(CPUState, tlb_table[mem_index][0].addend); + assert(ofs < 0x80000); + + tcg_out_mem(s, 0, RXY_AG, arg0, arg1, TCG_AREG0, ofs); +} + +static void tcg_finish_qemu_ldst(TCGContext* s, uint16_t *label2_ptr) +{ + /* patch branch */ + *(label2_ptr + 1) = ((unsigned long)s->code_ptr - + (unsigned long)label2_ptr) >> 1; +} +#else +static void tcg_prepare_user_ldst(TCGContext *s, TCGReg *addr_reg, + TCGReg *index_reg, tcg_target_long *disp) +{ + if (TARGET_LONG_BITS == 32) { + tgen_ext32u(s, TCG_TMP0, *addr_reg); + *addr_reg = TCG_TMP0; + } + if (GUEST_BASE < 0x80000) { + *index_reg = TCG_REG_NONE; + *disp = GUEST_BASE; + } else { + *index_reg = TCG_GUEST_BASE_REG; + *disp = 0; + } +} +#endif /* CONFIG_SOFTMMU */ + +/* load data with address translation (if applicable) + and endianness conversion */ +static void tcg_out_qemu_ld(TCGContext* s, const TCGArg* args, int opc) +{ + TCGReg addr_reg, data_reg; +#if defined(CONFIG_SOFTMMU) + int mem_index; + uint16_t *label2_ptr; +#else + TCGReg index_reg; + tcg_target_long disp; +#endif + + data_reg = *args++; + addr_reg = *args++; + +#if defined(CONFIG_SOFTMMU) + mem_index = *args; + + tcg_prepare_qemu_ldst(s, data_reg, addr_reg, mem_index, + opc, &label2_ptr, 0); + + tcg_out_qemu_ld_direct(s, opc, data_reg, TCG_REG_R2, TCG_REG_NONE, 0); + + tcg_finish_qemu_ldst(s, label2_ptr); +#else + tcg_prepare_user_ldst(s, &addr_reg, &index_reg, &disp); + tcg_out_qemu_ld_direct(s, opc, data_reg, addr_reg, index_reg, disp); +#endif +} + +static void tcg_out_qemu_st(TCGContext* s, const TCGArg* args, int opc) +{ + TCGReg addr_reg, data_reg; +#if defined(CONFIG_SOFTMMU) + int mem_index; + uint16_t *label2_ptr; +#else + TCGReg index_reg; + tcg_target_long disp; +#endif + + data_reg = *args++; + addr_reg = *args++; + +#if defined(CONFIG_SOFTMMU) + mem_index = *args; + + tcg_prepare_qemu_ldst(s, data_reg, addr_reg, mem_index, + opc, &label2_ptr, 1); + + tcg_out_qemu_st_direct(s, opc, data_reg, TCG_REG_R2, TCG_REG_NONE, 0); + + tcg_finish_qemu_ldst(s, label2_ptr); +#else + tcg_prepare_user_ldst(s, &addr_reg, &index_reg, &disp); + tcg_out_qemu_st_direct(s, opc, data_reg, addr_reg, index_reg, disp); +#endif +} + +#if TCG_TARGET_REG_BITS == 64 +# define OP_32_64(x) \ + case glue(glue(INDEX_op_,x),_i32): \ + case glue(glue(INDEX_op_,x),_i64) +#else +# define OP_32_64(x) \ + case glue(glue(INDEX_op_,x),_i32) +#endif + +static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg *args, const int *const_args) { - tcg_abort(); + S390Opcode op; + + switch (opc) { + case INDEX_op_exit_tb: + /* return value */ + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R2, args[0]); + tgen_gotoi(s, S390_CC_ALWAYS, (unsigned long)tb_ret_addr); + break; + + case INDEX_op_goto_tb: + if (s->tb_jmp_offset) { + tcg_abort(); + } else { + /* load address stored at s->tb_next + args[0] */ + tcg_out_ld_abs(s, TCG_TYPE_PTR, TCG_TMP0, s->tb_next + args[0]); + /* and go there */ + tcg_out_insn(s, RR, BCR, S390_CC_ALWAYS, TCG_TMP0); + } + s->tb_next_offset[args[0]] = s->code_ptr - s->code_buf; + break; + + case INDEX_op_call: + if (const_args[0]) { + tgen_calli(s, args[0]); + } else { + tcg_out_insn(s, RR, BASR, TCG_REG_R14, args[0]); + } + break; + + case INDEX_op_mov_i32: + tcg_out_mov(s, TCG_TYPE_I32, args[0], args[1]); + break; + case INDEX_op_movi_i32: + tcg_out_movi(s, TCG_TYPE_I32, args[0], args[1]); + break; + + OP_32_64(ld8u): + /* ??? LLC (RXY format) is only present with the extended-immediate + facility, whereas LLGC is always present. */ + tcg_out_mem(s, 0, RXY_LLGC, args[0], args[1], TCG_REG_NONE, args[2]); + break; + + OP_32_64(ld8s): + /* ??? LB is no smaller than LGB, so no point to using it. */ + tcg_out_mem(s, 0, RXY_LGB, args[0], args[1], TCG_REG_NONE, args[2]); + break; + + OP_32_64(ld16u): + /* ??? LLH (RXY format) is only present with the extended-immediate + facility, whereas LLGH is always present. */ + tcg_out_mem(s, 0, RXY_LLGH, args[0], args[1], TCG_REG_NONE, args[2]); + break; + + case INDEX_op_ld16s_i32: + tcg_out_mem(s, RX_LH, RXY_LHY, args[0], args[1], TCG_REG_NONE, args[2]); + break; + + case INDEX_op_ld_i32: + tcg_out_ld(s, TCG_TYPE_I32, args[0], args[1], args[2]); + break; + + OP_32_64(st8): + tcg_out_mem(s, RX_STC, RXY_STCY, args[0], args[1], + TCG_REG_NONE, args[2]); + break; + + OP_32_64(st16): + tcg_out_mem(s, RX_STH, RXY_STHY, args[0], args[1], + TCG_REG_NONE, args[2]); + break; + + case INDEX_op_st_i32: + tcg_out_st(s, TCG_TYPE_I32, args[0], args[1], args[2]); + break; + + case INDEX_op_add_i32: + if (const_args[2]) { + tgen32_addi(s, args[0], args[2]); + } else { + tcg_out_insn(s, RR, AR, args[0], args[2]); + } + break; + case INDEX_op_sub_i32: + if (const_args[2]) { + tgen32_addi(s, args[0], -args[2]); + } else { + tcg_out_insn(s, RR, SR, args[0], args[2]); + } + break; + + case INDEX_op_and_i32: + if (const_args[2]) { + tgen64_andi(s, args[0], args[2] | 0xffffffff00000000ull); + } else { + tcg_out_insn(s, RR, NR, args[0], args[2]); + } + break; + case INDEX_op_or_i32: + if (const_args[2]) { + tgen64_ori(s, args[0], args[2] & 0xffffffff); + } else { + tcg_out_insn(s, RR, OR, args[0], args[2]); + } + break; + case INDEX_op_xor_i32: + if (const_args[2]) { + tgen64_xori(s, args[0], args[2] & 0xffffffff); + } else { + tcg_out_insn(s, RR, XR, args[0], args[2]); + } + break; + + case INDEX_op_neg_i32: + tcg_out_insn(s, RR, LCR, args[0], args[1]); + break; + + case INDEX_op_mul_i32: + if (const_args[2]) { + if ((int32_t)args[2] == (int16_t)args[2]) { + tcg_out_insn(s, RI, MHI, args[0], args[2]); + } else { + tcg_out_insn(s, RIL, MSFI, args[0], args[2]); + } + } else { + tcg_out_insn(s, RRE, MSR, args[0], args[2]); + } + break; + + case INDEX_op_div2_i32: + tcg_out_insn(s, RR, DR, TCG_REG_R2, args[4]); + break; + case INDEX_op_divu2_i32: + tcg_out_insn(s, RRE, DLR, TCG_REG_R2, args[4]); + break; + + case INDEX_op_shl_i32: + op = RS_SLL; + do_shift32: + if (const_args[2]) { + tcg_out_sh32(s, op, args[0], TCG_REG_NONE, args[2]); + } else { + tcg_out_sh32(s, op, args[0], args[2], 0); + } + break; + case INDEX_op_shr_i32: + op = RS_SRL; + goto do_shift32; + case INDEX_op_sar_i32: + op = RS_SRA; + goto do_shift32; + + case INDEX_op_rotl_i32: + /* ??? Using tcg_out_sh64 here for the format; it is a 32-bit rol. */ + if (const_args[2]) { + tcg_out_sh64(s, RSY_RLL, args[0], args[1], TCG_REG_NONE, args[2]); + } else { + tcg_out_sh64(s, RSY_RLL, args[0], args[1], args[2], 0); + } + break; + case INDEX_op_rotr_i32: + if (const_args[2]) { + tcg_out_sh64(s, RSY_RLL, args[0], args[1], + TCG_REG_NONE, (32 - args[2]) & 31); + } else { + tcg_out_insn(s, RR, LCR, TCG_TMP0, args[2]); + tcg_out_sh64(s, RSY_RLL, args[0], args[1], TCG_TMP0, 0); + } + break; + + case INDEX_op_ext8s_i32: + tgen_ext8s(s, TCG_TYPE_I32, args[0], args[1]); + break; + case INDEX_op_ext16s_i32: + tgen_ext16s(s, TCG_TYPE_I32, args[0], args[1]); + break; + case INDEX_op_ext8u_i32: + tgen_ext8u(s, TCG_TYPE_I32, args[0], args[1]); + break; + case INDEX_op_ext16u_i32: + tgen_ext16u(s, TCG_TYPE_I32, args[0], args[1]); + break; + + OP_32_64(bswap16): + /* The TCG bswap definition requires bits 0-47 already be zero. + Thus we don't need the G-type insns to implement bswap16_i64. */ + tcg_out_insn(s, RRE, LRVR, args[0], args[1]); + tcg_out_sh32(s, RS_SRL, args[0], TCG_REG_NONE, 16); + break; + OP_32_64(bswap32): + tcg_out_insn(s, RRE, LRVR, args[0], args[1]); + break; + + case INDEX_op_br: + tgen_branch(s, S390_CC_ALWAYS, args[0]); + break; + + case INDEX_op_brcond_i32: + tgen_brcond(s, TCG_TYPE_I32, args[2], args[0], + args[1], const_args[1], args[3]); + break; + case INDEX_op_setcond_i32: + tgen_setcond(s, TCG_TYPE_I32, args[3], args[0], args[1], + args[2], const_args[2]); + break; + + case INDEX_op_qemu_ld8u: + tcg_out_qemu_ld(s, args, LD_UINT8); + break; + case INDEX_op_qemu_ld8s: + tcg_out_qemu_ld(s, args, LD_INT8); + break; + case INDEX_op_qemu_ld16u: + tcg_out_qemu_ld(s, args, LD_UINT16); + break; + case INDEX_op_qemu_ld16s: + tcg_out_qemu_ld(s, args, LD_INT16); + break; + case INDEX_op_qemu_ld32: + /* ??? Technically we can use a non-extending instruction. */ + tcg_out_qemu_ld(s, args, LD_UINT32); + break; + case INDEX_op_qemu_ld64: + tcg_out_qemu_ld(s, args, LD_UINT64); + break; + + case INDEX_op_qemu_st8: + tcg_out_qemu_st(s, args, LD_UINT8); + break; + case INDEX_op_qemu_st16: + tcg_out_qemu_st(s, args, LD_UINT16); + break; + case INDEX_op_qemu_st32: + tcg_out_qemu_st(s, args, LD_UINT32); + break; + case INDEX_op_qemu_st64: + tcg_out_qemu_st(s, args, LD_UINT64); + break; + +#if TCG_TARGET_REG_BITS == 64 + case INDEX_op_mov_i64: + tcg_out_mov(s, TCG_TYPE_I64, args[0], args[1]); + break; + case INDEX_op_movi_i64: + tcg_out_movi(s, TCG_TYPE_I64, args[0], args[1]); + break; + + case INDEX_op_ld16s_i64: + tcg_out_mem(s, 0, RXY_LGH, args[0], args[1], TCG_REG_NONE, args[2]); + break; + case INDEX_op_ld32u_i64: + tcg_out_mem(s, 0, RXY_LLGF, args[0], args[1], TCG_REG_NONE, args[2]); + break; + case INDEX_op_ld32s_i64: + tcg_out_mem(s, 0, RXY_LGF, args[0], args[1], TCG_REG_NONE, args[2]); + break; + case INDEX_op_ld_i64: + tcg_out_ld(s, TCG_TYPE_I64, args[0], args[1], args[2]); + break; + + case INDEX_op_st32_i64: + tcg_out_st(s, TCG_TYPE_I32, args[0], args[1], args[2]); + break; + case INDEX_op_st_i64: + tcg_out_st(s, TCG_TYPE_I64, args[0], args[1], args[2]); + break; + + case INDEX_op_add_i64: + if (const_args[2]) { + tgen64_addi(s, args[0], args[2]); + } else { + tcg_out_insn(s, RRE, AGR, args[0], args[2]); + } + break; + case INDEX_op_sub_i64: + if (const_args[2]) { + tgen64_addi(s, args[0], -args[2]); + } else { + tcg_out_insn(s, RRE, SGR, args[0], args[2]); + } + break; + + case INDEX_op_and_i64: + if (const_args[2]) { + tgen64_andi(s, args[0], args[2]); + } else { + tcg_out_insn(s, RRE, NGR, args[0], args[2]); + } + break; + case INDEX_op_or_i64: + if (const_args[2]) { + tgen64_ori(s, args[0], args[2]); + } else { + tcg_out_insn(s, RRE, OGR, args[0], args[2]); + } + break; + case INDEX_op_xor_i64: + if (const_args[2]) { + tgen64_xori(s, args[0], args[2]); + } else { + tcg_out_insn(s, RRE, XGR, args[0], args[2]); + } + break; + + case INDEX_op_neg_i64: + tcg_out_insn(s, RRE, LCGR, args[0], args[1]); + break; + case INDEX_op_bswap64_i64: + tcg_out_insn(s, RRE, LRVGR, args[0], args[1]); + break; + + case INDEX_op_mul_i64: + if (const_args[2]) { + if (args[2] == (int16_t)args[2]) { + tcg_out_insn(s, RI, MGHI, args[0], args[2]); + } else { + tcg_out_insn(s, RIL, MSGFI, args[0], args[2]); + } + } else { + tcg_out_insn(s, RRE, MSGR, args[0], args[2]); + } + break; + + case INDEX_op_div2_i64: + /* ??? We get an unnecessary sign-extension of the dividend + into R3 with this definition, but as we do in fact always + produce both quotient and remainder using INDEX_op_div_i64 + instead requires jumping through even more hoops. */ + tcg_out_insn(s, RRE, DSGR, TCG_REG_R2, args[4]); + break; + case INDEX_op_divu2_i64: + tcg_out_insn(s, RRE, DLGR, TCG_REG_R2, args[4]); + break; + + case INDEX_op_shl_i64: + op = RSY_SLLG; + do_shift64: + if (const_args[2]) { + tcg_out_sh64(s, op, args[0], args[1], TCG_REG_NONE, args[2]); + } else { + tcg_out_sh64(s, op, args[0], args[1], args[2], 0); + } + break; + case INDEX_op_shr_i64: + op = RSY_SRLG; + goto do_shift64; + case INDEX_op_sar_i64: + op = RSY_SRAG; + goto do_shift64; + + case INDEX_op_rotl_i64: + if (const_args[2]) { + tcg_out_sh64(s, RSY_RLLG, args[0], args[1], + TCG_REG_NONE, args[2]); + } else { + tcg_out_sh64(s, RSY_RLLG, args[0], args[1], args[2], 0); + } + break; + case INDEX_op_rotr_i64: + if (const_args[2]) { + tcg_out_sh64(s, RSY_RLLG, args[0], args[1], + TCG_REG_NONE, (64 - args[2]) & 63); + } else { + /* We can use the smaller 32-bit negate because only the + low 6 bits are examined for the rotate. */ + tcg_out_insn(s, RR, LCR, TCG_TMP0, args[2]); + tcg_out_sh64(s, RSY_RLLG, args[0], args[1], TCG_TMP0, 0); + } + break; + + case INDEX_op_ext8s_i64: + tgen_ext8s(s, TCG_TYPE_I64, args[0], args[1]); + break; + case INDEX_op_ext16s_i64: + tgen_ext16s(s, TCG_TYPE_I64, args[0], args[1]); + break; + case INDEX_op_ext32s_i64: + tgen_ext32s(s, args[0], args[1]); + break; + case INDEX_op_ext8u_i64: + tgen_ext8u(s, TCG_TYPE_I64, args[0], args[1]); + break; + case INDEX_op_ext16u_i64: + tgen_ext16u(s, TCG_TYPE_I64, args[0], args[1]); + break; + case INDEX_op_ext32u_i64: + tgen_ext32u(s, args[0], args[1]); + break; + + case INDEX_op_brcond_i64: + tgen_brcond(s, TCG_TYPE_I64, args[2], args[0], + args[1], const_args[1], args[3]); + break; + case INDEX_op_setcond_i64: + tgen_setcond(s, TCG_TYPE_I64, args[3], args[0], args[1], + args[2], const_args[2]); + break; + + case INDEX_op_qemu_ld32u: + tcg_out_qemu_ld(s, args, LD_UINT32); + break; + case INDEX_op_qemu_ld32s: + tcg_out_qemu_ld(s, args, LD_INT32); + break; +#endif /* TCG_TARGET_REG_BITS == 64 */ + + case INDEX_op_jmp: + /* This one is obsolete and never emitted. */ + tcg_abort(); + break; + + default: + fprintf(stderr,"unimplemented opc 0x%x\n",opc); + tcg_abort(); + } } -void tcg_target_init(TCGContext *s) +static const TCGTargetOpDef s390_op_defs[] = { + { INDEX_op_exit_tb, { } }, + { INDEX_op_goto_tb, { } }, + { INDEX_op_call, { "ri" } }, + { INDEX_op_jmp, { "ri" } }, + { INDEX_op_br, { } }, + + { INDEX_op_mov_i32, { "r", "r" } }, + { INDEX_op_movi_i32, { "r" } }, + + { INDEX_op_ld8u_i32, { "r", "r" } }, + { INDEX_op_ld8s_i32, { "r", "r" } }, + { INDEX_op_ld16u_i32, { "r", "r" } }, + { INDEX_op_ld16s_i32, { "r", "r" } }, + { INDEX_op_ld_i32, { "r", "r" } }, + { INDEX_op_st8_i32, { "r", "r" } }, + { INDEX_op_st16_i32, { "r", "r" } }, + { INDEX_op_st_i32, { "r", "r" } }, + + { INDEX_op_add_i32, { "r", "0", "rWI" } }, + { INDEX_op_sub_i32, { "r", "0", "rWNI" } }, + { INDEX_op_mul_i32, { "r", "0", "rK" } }, + + { INDEX_op_div2_i32, { "b", "a", "0", "1", "r" } }, + { INDEX_op_divu2_i32, { "b", "a", "0", "1", "r" } }, + + { INDEX_op_and_i32, { "r", "0", "rWA" } }, + { INDEX_op_or_i32, { "r", "0", "rWO" } }, + { INDEX_op_xor_i32, { "r", "0", "rWX" } }, + + { INDEX_op_neg_i32, { "r", "r" } }, + + { INDEX_op_shl_i32, { "r", "0", "Ri" } }, + { INDEX_op_shr_i32, { "r", "0", "Ri" } }, + { INDEX_op_sar_i32, { "r", "0", "Ri" } }, + + { INDEX_op_rotl_i32, { "r", "r", "Ri" } }, + { INDEX_op_rotr_i32, { "r", "r", "Ri" } }, + + { INDEX_op_ext8s_i32, { "r", "r" } }, + { INDEX_op_ext8u_i32, { "r", "r" } }, + { INDEX_op_ext16s_i32, { "r", "r" } }, + { INDEX_op_ext16u_i32, { "r", "r" } }, + + { INDEX_op_bswap16_i32, { "r", "r" } }, + { INDEX_op_bswap32_i32, { "r", "r" } }, + + { INDEX_op_brcond_i32, { "r", "rWC" } }, + { INDEX_op_setcond_i32, { "r", "r", "rWC" } }, + + { INDEX_op_qemu_ld8u, { "r", "L" } }, + { INDEX_op_qemu_ld8s, { "r", "L" } }, + { INDEX_op_qemu_ld16u, { "r", "L" } }, + { INDEX_op_qemu_ld16s, { "r", "L" } }, + { INDEX_op_qemu_ld32, { "r", "L" } }, + { INDEX_op_qemu_ld64, { "r", "L" } }, + + { INDEX_op_qemu_st8, { "L", "L" } }, + { INDEX_op_qemu_st16, { "L", "L" } }, + { INDEX_op_qemu_st32, { "L", "L" } }, + { INDEX_op_qemu_st64, { "L", "L" } }, + +#if defined(__s390x__) + { INDEX_op_mov_i64, { "r", "r" } }, + { INDEX_op_movi_i64, { "r" } }, + + { INDEX_op_ld8u_i64, { "r", "r" } }, + { INDEX_op_ld8s_i64, { "r", "r" } }, + { INDEX_op_ld16u_i64, { "r", "r" } }, + { INDEX_op_ld16s_i64, { "r", "r" } }, + { INDEX_op_ld32u_i64, { "r", "r" } }, + { INDEX_op_ld32s_i64, { "r", "r" } }, + { INDEX_op_ld_i64, { "r", "r" } }, + + { INDEX_op_st8_i64, { "r", "r" } }, + { INDEX_op_st16_i64, { "r", "r" } }, + { INDEX_op_st32_i64, { "r", "r" } }, + { INDEX_op_st_i64, { "r", "r" } }, + + { INDEX_op_add_i64, { "r", "0", "rI" } }, + { INDEX_op_sub_i64, { "r", "0", "rNI" } }, + { INDEX_op_mul_i64, { "r", "0", "rK" } }, + + { INDEX_op_div2_i64, { "b", "a", "0", "1", "r" } }, + { INDEX_op_divu2_i64, { "b", "a", "0", "1", "r" } }, + + { INDEX_op_and_i64, { "r", "0", "rA" } }, + { INDEX_op_or_i64, { "r", "0", "rO" } }, + { INDEX_op_xor_i64, { "r", "0", "rX" } }, + + { INDEX_op_neg_i64, { "r", "r" } }, + + { INDEX_op_shl_i64, { "r", "r", "Ri" } }, + { INDEX_op_shr_i64, { "r", "r", "Ri" } }, + { INDEX_op_sar_i64, { "r", "r", "Ri" } }, + + { INDEX_op_rotl_i64, { "r", "r", "Ri" } }, + { INDEX_op_rotr_i64, { "r", "r", "Ri" } }, + + { INDEX_op_ext8s_i64, { "r", "r" } }, + { INDEX_op_ext8u_i64, { "r", "r" } }, + { INDEX_op_ext16s_i64, { "r", "r" } }, + { INDEX_op_ext16u_i64, { "r", "r" } }, + { INDEX_op_ext32s_i64, { "r", "r" } }, + { INDEX_op_ext32u_i64, { "r", "r" } }, + + { INDEX_op_bswap16_i64, { "r", "r" } }, + { INDEX_op_bswap32_i64, { "r", "r" } }, + { INDEX_op_bswap64_i64, { "r", "r" } }, + + { INDEX_op_brcond_i64, { "r", "rC" } }, + { INDEX_op_setcond_i64, { "r", "r", "rC" } }, + + { INDEX_op_qemu_ld32u, { "r", "L" } }, + { INDEX_op_qemu_ld32s, { "r", "L" } }, +#endif + + { -1 }, +}; + +/* ??? Linux kernels provide an AUXV entry AT_HWCAP that provides most of + this information. However, getting at that entry is not easy this far + away from main. Our options are: start searching from environ, but + that fails as soon as someone does a setenv in between. Read the data + from /proc/self/auxv. Or do the probing ourselves. The only thing + extra that AT_HWCAP gives us is HWCAP_S390_HIGH_GPRS, which indicates + that the kernel saves all 64-bits of the registers around traps while + in 31-bit mode. But this is true of all "recent" kernels (ought to dig + back and see from when this might not be true). */ + +#include + +static volatile sig_atomic_t got_sigill; + +static void sigill_handler(int sig) { - /* gets called with KVM */ + got_sigill = 1; } -void tcg_target_qemu_prologue(TCGContext *s) +static void query_facilities(void) { - /* gets called with KVM */ + struct sigaction sa_old, sa_new; + register int r0 __asm__("0"); + register void *r1 __asm__("1"); + int fail; + + memset(&sa_new, 0, sizeof(sa_new)); + sa_new.sa_handler = sigill_handler; + sigaction(SIGILL, &sa_new, &sa_old); + + /* First, try STORE FACILITY LIST EXTENDED. If this is present, then + we need not do any more probing. Unfortunately, this itself is an + extension and the original STORE FACILITY LIST instruction is + kernel-only, storing its results at absolute address 200. */ + /* stfle 0(%r1) */ + r1 = &facilities; + asm volatile(".word 0xb2b0,0x1000" + : "=r"(r0) : "0"(0), "r"(r1) : "memory", "cc"); + + if (got_sigill) { + /* STORE FACILITY EXTENDED is not available. Probe for one of each + kind of instruction that we're interested in. */ + /* ??? Possibly some of these are in practice never present unless + the store-facility-extended facility is also present. But since + that isn't documented it's just better to probe for each. */ + + /* Test for z/Architecture. Required even in 31-bit mode. */ + got_sigill = 0; + /* agr %r0,%r0 */ + asm volatile(".word 0xb908,0x0000" : "=r"(r0) : : "cc"); + if (!got_sigill) { + facilities |= FACILITY_ZARCH_ACTIVE; + } + + /* Test for long displacement. */ + got_sigill = 0; + /* ly %r0,0(%r1) */ + r1 = &facilities; + asm volatile(".word 0xe300,0x1000,0x0058" + : "=r"(r0) : "r"(r1) : "cc"); + if (!got_sigill) { + facilities |= FACILITY_LONG_DISP; + } + + /* Test for extended immediates. */ + got_sigill = 0; + /* afi %r0,0 */ + asm volatile(".word 0xc209,0x0000,0x0000" : : : "cc"); + if (!got_sigill) { + facilities |= FACILITY_EXT_IMM; + } + + /* Test for general-instructions-extension. */ + got_sigill = 0; + /* msfi %r0,1 */ + asm volatile(".word 0xc201,0x0000,0x0001"); + if (!got_sigill) { + facilities |= FACILITY_GEN_INST_EXT; + } + } + + sigaction(SIGILL, &sa_old, NULL); + + /* The translator currently uses these extensions unconditionally. + Pruning this back to the base ESA/390 architecture doesn't seem + worthwhile, since even the KVM target requires z/Arch. */ + fail = 0; + if ((facilities & FACILITY_ZARCH_ACTIVE) == 0) { + fprintf(stderr, "TCG: z/Arch facility is required.\n"); + fprintf(stderr, "TCG: Boot with a 64-bit enabled kernel.\n"); + fail = 1; + } + if ((facilities & FACILITY_LONG_DISP) == 0) { + fprintf(stderr, "TCG: long-displacement facility is required.\n"); + fail = 1; + } + + /* So far there's just enough support for 31-bit mode to let the + compile succeed. This is good enough to run QEMU with KVM. */ + if (sizeof(void *) != 8) { + fprintf(stderr, "TCG: 31-bit mode is not supported.\n"); + fail = 1; + } + + if (fail) { + exit(-1); + } } -static inline void tcg_out_mov(TCGContext *s, int ret, int arg) +static void tcg_target_init(TCGContext *s) { - tcg_abort(); +#if !defined(CONFIG_USER_ONLY) + /* fail safe */ + if ((1 << CPU_TLB_ENTRY_BITS) != sizeof(CPUTLBEntry)) { + tcg_abort(); + } +#endif + + query_facilities(); + + tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0, 0xffff); + tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I64], 0, 0xffff); + + tcg_regset_clear(tcg_target_call_clobber_regs); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R0); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R1); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R2); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R3); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R4); + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R5); + /* The return register can be considered call-clobbered. */ + tcg_regset_set_reg(tcg_target_call_clobber_regs, TCG_REG_R14); + + tcg_regset_clear(s->reserved_regs); + tcg_regset_set_reg(s->reserved_regs, TCG_TMP0); + /* XXX many insns can't be used with R0, so we better avoid it for now */ + tcg_regset_set_reg(s->reserved_regs, TCG_REG_R0); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_CALL_STACK); + + tcg_add_target_add_op_defs(s390_op_defs); +} + +static void tcg_target_qemu_prologue(TCGContext *s) +{ + /* stmg %r6,%r15,48(%r15) (save registers) */ + tcg_out_insn(s, RXY, STMG, TCG_REG_R6, TCG_REG_R15, TCG_REG_R15, 48); + + /* aghi %r15,-160 (stack frame) */ + tcg_out_insn(s, RI, AGHI, TCG_REG_R15, -160); + + if (GUEST_BASE >= 0x80000) { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_GUEST_BASE_REG, GUEST_BASE); + tcg_regset_set_reg(s->reserved_regs, TCG_GUEST_BASE_REG); + } + + /* br %r2 (go to TB) */ + tcg_out_insn(s, RR, BCR, S390_CC_ALWAYS, TCG_REG_R2); + + tb_ret_addr = s->code_ptr; + + /* lmg %r6,%r15,208(%r15) (restore registers) */ + tcg_out_insn(s, RXY, LMG, TCG_REG_R6, TCG_REG_R15, TCG_REG_R15, 208); + + /* br %r14 (return) */ + tcg_out_insn(s, RR, BCR, S390_CC_ALWAYS, TCG_REG_R14); } static inline void tcg_out_addi(TCGContext *s, int reg, tcg_target_long val) diff -Nru qemu-kvm-0.12.5+noroms/tcg/s390/tcg-target.h qemu-kvm-0.14.1/tcg/s390/tcg-target.h --- qemu-kvm-0.12.5+noroms/tcg/s390/tcg-target.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/tcg/s390/tcg-target.h 2011-05-11 13:29:46.000000000 +0000 @@ -23,10 +23,15 @@ */ #define TCG_TARGET_S390 1 +#ifdef __s390x__ #define TCG_TARGET_REG_BITS 64 +#else +#define TCG_TARGET_REG_BITS 32 +#endif + #define TCG_TARGET_WORDS_BIGENDIAN -enum { +typedef enum TCGReg { TCG_REG_R0 = 0, TCG_REG_R1, TCG_REG_R2, @@ -43,27 +48,62 @@ TCG_REG_R13, TCG_REG_R14, TCG_REG_R15 -}; +} TCGReg; + #define TCG_TARGET_NB_REGS 16 +/* optional instructions */ +#define TCG_TARGET_HAS_div2_i32 +#define TCG_TARGET_HAS_rot_i32 +#define TCG_TARGET_HAS_ext8s_i32 +#define TCG_TARGET_HAS_ext16s_i32 +#define TCG_TARGET_HAS_ext8u_i32 +#define TCG_TARGET_HAS_ext16u_i32 +#define TCG_TARGET_HAS_bswap16_i32 +#define TCG_TARGET_HAS_bswap32_i32 +// #define TCG_TARGET_HAS_not_i32 +#define TCG_TARGET_HAS_neg_i32 +// #define TCG_TARGET_HAS_andc_i32 +// #define TCG_TARGET_HAS_orc_i32 +// #define TCG_TARGET_HAS_eqv_i32 +// #define TCG_TARGET_HAS_nand_i32 +// #define TCG_TARGET_HAS_nor_i32 + +#if TCG_TARGET_REG_BITS == 64 +#define TCG_TARGET_HAS_div2_i64 +#define TCG_TARGET_HAS_rot_i64 +#define TCG_TARGET_HAS_ext8s_i64 +#define TCG_TARGET_HAS_ext16s_i64 +#define TCG_TARGET_HAS_ext32s_i64 +#define TCG_TARGET_HAS_ext8u_i64 +#define TCG_TARGET_HAS_ext16u_i64 +#define TCG_TARGET_HAS_ext32u_i64 +#define TCG_TARGET_HAS_bswap16_i64 +#define TCG_TARGET_HAS_bswap32_i64 +#define TCG_TARGET_HAS_bswap64_i64 +// #define TCG_TARGET_HAS_not_i64 +#define TCG_TARGET_HAS_neg_i64 +// #define TCG_TARGET_HAS_andc_i64 +// #define TCG_TARGET_HAS_orc_i64 +// #define TCG_TARGET_HAS_eqv_i64 +// #define TCG_TARGET_HAS_nand_i64 +// #define TCG_TARGET_HAS_nor_i64 +#endif + +#define TCG_TARGET_HAS_GUEST_BASE + /* used for function call generation */ #define TCG_REG_CALL_STACK TCG_REG_R15 #define TCG_TARGET_STACK_ALIGN 8 #define TCG_TARGET_CALL_STACK_OFFSET 0 +#define TCG_TARGET_EXTEND_ARGS 1 + enum { /* Note: must be synced with dyngen-exec.h */ TCG_AREG0 = TCG_REG_R10, - TCG_AREG1 = TCG_REG_R7, - TCG_AREG2 = TCG_REG_R8, - TCG_AREG3 = TCG_REG_R9, }; static inline void flush_icache_range(unsigned long start, unsigned long stop) { -#if QEMU_GNUC_PREREQ(4, 1) - __builtin___clear_cache((char *) start, (char *) stop); -#else -#error not implemented -#endif } diff -Nru qemu-kvm-0.12.5+noroms/tcg/sparc/tcg-target.c qemu-kvm-0.14.1/tcg/sparc/tcg-target.c --- qemu-kvm-0.12.5+noroms/tcg/sparc/tcg-target.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/tcg/sparc/tcg-target.c 2011-05-11 13:29:46.000000000 +0000 @@ -143,6 +143,9 @@ ct_str = *pct_str; switch (ct_str[0]) { case 'r': + ct->ct |= TCG_CT_REG; + tcg_regset_set32(ct->u.regs, 0, 0xffffffff); + break; case 'L': /* qemu_ld/st constraint */ ct->ct |= TCG_CT_REG; tcg_regset_set32(ct->u.regs, 0, 0xffffffff); @@ -191,6 +194,7 @@ #define INSN_RS2(x) (x) #define INSN_ASI(x) ((x) << 5) +#define INSN_IMM11(x) ((1 << 13) | ((x) & 0x7ff)) #define INSN_IMM13(x) ((1 << 13) | ((x) & 0x1fff)) #define INSN_OFF19(x) (((x) >> 2) & 0x07ffff) #define INSN_OFF22(x) (((x) >> 2) & 0x3fffff) @@ -214,10 +218,16 @@ #define COND_VC 0xf #define BA (INSN_OP(0) | INSN_COND(COND_A, 0) | INSN_OP2(0x2)) +#define MOVCC_ICC (1 << 18) +#define MOVCC_XCC (1 << 18 | 1 << 12) + #define ARITH_ADD (INSN_OP(2) | INSN_OP3(0x00)) +#define ARITH_ADDCC (INSN_OP(2) | INSN_OP3(0x10)) #define ARITH_AND (INSN_OP(2) | INSN_OP3(0x01)) +#define ARITH_ANDN (INSN_OP(2) | INSN_OP3(0x05)) #define ARITH_OR (INSN_OP(2) | INSN_OP3(0x02)) #define ARITH_ORCC (INSN_OP(2) | INSN_OP3(0x12)) +#define ARITH_ORN (INSN_OP(2) | INSN_OP3(0x06)) #define ARITH_XOR (INSN_OP(2) | INSN_OP3(0x03)) #define ARITH_SUB (INSN_OP(2) | INSN_OP3(0x04)) #define ARITH_SUBCC (INSN_OP(2) | INSN_OP3(0x14)) @@ -229,6 +239,7 @@ #define ARITH_MULX (INSN_OP(2) | INSN_OP3(0x09)) #define ARITH_UDIVX (INSN_OP(2) | INSN_OP3(0x0d)) #define ARITH_SDIVX (INSN_OP(2) | INSN_OP3(0x2d)) +#define ARITH_MOVCC (INSN_OP(2) | INSN_OP3(0x2c)) #define SHIFT_SLL (INSN_OP(2) | INSN_OP3(0x25)) #define SHIFT_SRL (INSN_OP(2) | INSN_OP3(0x26)) @@ -238,7 +249,8 @@ #define SHIFT_SRLX (INSN_OP(2) | INSN_OP3(0x26) | (1 << 12)) #define SHIFT_SRAX (INSN_OP(2) | INSN_OP3(0x27) | (1 << 12)) -#define WRY (INSN_OP(2) | INSN_OP3(0x30)) +#define RDY (INSN_OP(2) | INSN_OP3(0x28) | INSN_RS1(0)) +#define WRY (INSN_OP(2) | INSN_OP3(0x30) | INSN_RD(0)) #define JMPL (INSN_OP(2) | INSN_OP3(0x38)) #define SAVE (INSN_OP(2) | INSN_OP3(0x3c)) #define RESTORE (INSN_OP(2) | INSN_OP3(0x3d)) @@ -285,7 +297,14 @@ INSN_IMM13(offset)); } -static inline void tcg_out_mov(TCGContext *s, int ret, int arg) +static void tcg_out_arithc(TCGContext *s, int rd, int rs1, + int val2, int val2const, int op) +{ + tcg_out32(s, op | INSN_RD(rd) | INSN_RS1(rs1) + | (val2const ? INSN_IMM13(val2) : INSN_RS2(val2))); +} + +static inline void tcg_out_mov(TCGContext *s, TCGType type, int ret, int arg) { tcg_out_arith(s, ret, arg, TCG_REG_G0, ARITH_OR); } @@ -302,7 +321,7 @@ static inline void tcg_out_movi_imm32(TCGContext *s, int ret, uint32_t arg) { - if (check_fit_tl(arg, 12)) + if (check_fit_tl(arg, 13)) tcg_out_movi_imm13(s, ret, arg); else { tcg_out_sethi(s, ret, arg); @@ -314,22 +333,25 @@ static inline void tcg_out_movi(TCGContext *s, TCGType type, int ret, tcg_target_long arg) { -#if defined(__sparc_v9__) && !defined(__sparc_v8plus__) - if (!check_fit_tl(arg, 32) && (arg & ~0xffffffffULL) != 0) { - tcg_out_movi_imm32(s, TCG_REG_I4, arg >> 32); + /* All 32-bit constants, as well as 64-bit constants with + no high bits set go through movi_imm32. */ + if (TCG_TARGET_REG_BITS == 32 + || type == TCG_TYPE_I32 + || (arg & ~(tcg_target_long)0xffffffff) == 0) { + tcg_out_movi_imm32(s, ret, arg); + } else if (check_fit_tl(arg, 13)) { + /* A 13-bit constant sign-extended to 64-bits. */ + tcg_out_movi_imm13(s, ret, arg); + } else if (check_fit_tl(arg, 32)) { + /* A 32-bit constant sign-extended to 64-bits. */ + tcg_out_sethi(s, ret, ~arg); + tcg_out_arithi(s, ret, ret, (arg & 0x3ff) | -0x400, ARITH_XOR); + } else { + tcg_out_movi_imm32(s, TCG_REG_I4, arg >> (TCG_TARGET_REG_BITS / 2)); tcg_out_arithi(s, TCG_REG_I4, TCG_REG_I4, 32, SHIFT_SLLX); tcg_out_movi_imm32(s, ret, arg); tcg_out_arith(s, ret, ret, TCG_REG_I4, ARITH_OR); - } else if (check_fit_tl(arg, 12)) - tcg_out_movi_imm13(s, ret, arg); - else { - tcg_out_sethi(s, ret, arg); - if (arg & 0x3ff) - tcg_out_arithi(s, ret, ret, arg & 0x3ff, ARITH_OR); } -#else - tcg_out_movi_imm32(s, ret, arg); -#endif } static inline void tcg_out_ld_raw(TCGContext *s, int ret, @@ -345,13 +367,13 @@ { if (!check_fit_tl(arg, 10)) tcg_out_movi(s, TCG_TYPE_PTR, ret, arg & ~0x3ffULL); -#if defined(__sparc_v9__) && !defined(__sparc_v8plus__) - tcg_out32(s, LDX | INSN_RD(ret) | INSN_RS1(ret) | - INSN_IMM13(arg & 0x3ff)); -#else - tcg_out32(s, LDUW | INSN_RD(ret) | INSN_RS1(ret) | - INSN_IMM13(arg & 0x3ff)); -#endif + if (TCG_TARGET_REG_BITS == 64) { + tcg_out32(s, LDX | INSN_RD(ret) | INSN_RS1(ret) | + INSN_IMM13(arg & 0x3ff)); + } else { + tcg_out32(s, LDUW | INSN_RD(ret) | INSN_RS1(ret) | + INSN_IMM13(arg & 0x3ff)); + } } static inline void tcg_out_ldst(TCGContext *s, int ret, int addr, int offset, int op) @@ -392,12 +414,14 @@ tcg_out_ldst(s, arg, arg1, arg2, STX); } -static inline void tcg_out_sety(TCGContext *s, tcg_target_long val) +static inline void tcg_out_sety(TCGContext *s, int rs) { - if (val == 0 || val == -1) - tcg_out32(s, WRY | INSN_IMM13(val)); - else - fprintf(stderr, "unimplemented sety %ld\n", (long)val); + tcg_out32(s, WRY | INSN_RS1(TCG_REG_G0) | INSN_RS2(rs)); +} + +static inline void tcg_out_rdy(TCGContext *s, int rd) +{ + tcg_out32(s, RDY | INSN_RD(rd)); } static inline void tcg_out_addi(TCGContext *s, int reg, tcg_target_long val) @@ -424,6 +448,21 @@ } } +static void tcg_out_div32(TCGContext *s, int rd, int rs1, + int val2, int val2const, int uns) +{ + /* Load Y with the sign/zero extension of RS1 to 64-bits. */ + if (uns) { + tcg_out_sety(s, TCG_REG_G0); + } else { + tcg_out_arithi(s, TCG_REG_I5, rs1, 31, SHIFT_SRA); + tcg_out_sety(s, TCG_REG_I5); + } + + tcg_out_arithc(s, rd, rs1, val2, val2const, + uns ? ARITH_UDIV : ARITH_SDIV); +} + static inline void tcg_out_nop(TCGContext *s) { tcg_out_sethi(s, TCG_REG_G0, 0); @@ -444,7 +483,7 @@ } } -#if defined(__sparc_v9__) && !defined(__sparc_v8plus__) +#if TCG_TARGET_REG_BITS == 64 static void tcg_out_branch_i64(TCGContext *s, int opc, int label_index) { int32_t val; @@ -476,38 +515,183 @@ [TCG_COND_GTU] = COND_GU, }; -static void tcg_out_brcond_i32(TCGContext *s, int cond, +static void tcg_out_cmp(TCGContext *s, TCGArg c1, TCGArg c2, int c2const) +{ + tcg_out_arithc(s, TCG_REG_G0, c1, c2, c2const, ARITH_SUBCC); +} + +static void tcg_out_brcond_i32(TCGContext *s, TCGCond cond, TCGArg arg1, TCGArg arg2, int const_arg2, int label_index) { - if (const_arg2 && arg2 == 0) - /* orcc %g0, r, %g0 */ - tcg_out_arith(s, TCG_REG_G0, TCG_REG_G0, arg1, ARITH_ORCC); - else - /* subcc r1, r2, %g0 */ - tcg_out_arith(s, TCG_REG_G0, arg1, arg2, ARITH_SUBCC); + tcg_out_cmp(s, arg1, arg2, const_arg2); tcg_out_branch_i32(s, tcg_cond_to_bcond[cond], label_index); tcg_out_nop(s); } -#if defined(__sparc_v9__) && !defined(__sparc_v8plus__) -static void tcg_out_brcond_i64(TCGContext *s, int cond, +#if TCG_TARGET_REG_BITS == 64 +static void tcg_out_brcond_i64(TCGContext *s, TCGCond cond, TCGArg arg1, TCGArg arg2, int const_arg2, int label_index) { - if (const_arg2 && arg2 == 0) - /* orcc %g0, r, %g0 */ - tcg_out_arith(s, TCG_REG_G0, TCG_REG_G0, arg1, ARITH_ORCC); - else - /* subcc r1, r2, %g0 */ - tcg_out_arith(s, TCG_REG_G0, arg1, arg2, ARITH_SUBCC); + tcg_out_cmp(s, arg1, arg2, const_arg2); tcg_out_branch_i64(s, tcg_cond_to_bcond[cond], label_index); tcg_out_nop(s); } +#else +static void tcg_out_brcond2_i32(TCGContext *s, TCGCond cond, + TCGArg al, TCGArg ah, + TCGArg bl, int blconst, + TCGArg bh, int bhconst, int label_dest) +{ + int cc, label_next = gen_new_label(); + + tcg_out_cmp(s, ah, bh, bhconst); + + /* Note that we fill one of the delay slots with the second compare. */ + switch (cond) { + case TCG_COND_EQ: + cc = INSN_COND(tcg_cond_to_bcond[TCG_COND_NE], 0); + tcg_out_branch_i32(s, cc, label_next); + tcg_out_cmp(s, al, bl, blconst); + cc = INSN_COND(tcg_cond_to_bcond[TCG_COND_EQ], 0); + tcg_out_branch_i32(s, cc, label_dest); + break; + + case TCG_COND_NE: + cc = INSN_COND(tcg_cond_to_bcond[TCG_COND_NE], 0); + tcg_out_branch_i32(s, cc, label_dest); + tcg_out_cmp(s, al, bl, blconst); + tcg_out_branch_i32(s, cc, label_dest); + break; + + default: + /* ??? One could fairly easily special-case 64-bit unsigned + compares against 32-bit zero-extended constants. For instance, + we know that (unsigned)AH < 0 is false and need not emit it. + Similarly, (unsigned)AH > 0 being true implies AH != 0, so the + second branch will never be taken. */ + cc = INSN_COND(tcg_cond_to_bcond[cond], 0); + tcg_out_branch_i32(s, cc, label_dest); + tcg_out_nop(s); + cc = INSN_COND(tcg_cond_to_bcond[TCG_COND_NE], 0); + tcg_out_branch_i32(s, cc, label_next); + tcg_out_cmp(s, al, bl, blconst); + cc = INSN_COND(tcg_cond_to_bcond[tcg_unsigned_cond(cond)], 0); + tcg_out_branch_i32(s, cc, label_dest); + break; + } + tcg_out_nop(s); + + tcg_out_label(s, label_next, (tcg_target_long)s->code_ptr); +} +#endif + +static void tcg_out_setcond_i32(TCGContext *s, TCGCond cond, TCGArg ret, + TCGArg c1, TCGArg c2, int c2const) +{ + TCGArg t; + + /* For 32-bit comparisons, we can play games with ADDX/SUBX. */ + switch (cond) { + case TCG_COND_EQ: + case TCG_COND_NE: + if (c2 != 0) { + tcg_out_arithc(s, ret, c1, c2, c2const, ARITH_XOR); + } + c1 = TCG_REG_G0, c2 = ret, c2const = 0; + cond = (cond == TCG_COND_EQ ? TCG_COND_LEU : TCG_COND_LTU); + break; + + case TCG_COND_GTU: + case TCG_COND_GEU: + if (c2const && c2 != 0) { + tcg_out_movi_imm13(s, TCG_REG_I5, c2); + c2 = TCG_REG_I5; + } + t = c1, c1 = c2, c2 = t, c2const = 0; + cond = tcg_swap_cond(cond); + break; + + case TCG_COND_LTU: + case TCG_COND_LEU: + break; + + default: + tcg_out_cmp(s, c1, c2, c2const); +#if defined(__sparc_v9__) || defined(__sparc_v8plus__) + tcg_out_movi_imm13(s, ret, 0); + tcg_out32 (s, ARITH_MOVCC | INSN_RD(ret) + | INSN_RS1(tcg_cond_to_bcond[cond]) + | MOVCC_ICC | INSN_IMM11(1)); +#else + t = gen_new_label(); + tcg_out_branch_i32(s, INSN_COND(tcg_cond_to_bcond[cond], 1), t); + tcg_out_movi_imm13(s, ret, 1); + tcg_out_movi_imm13(s, ret, 0); + tcg_out_label(s, t, (tcg_target_long)s->code_ptr); +#endif + return; + } + + tcg_out_cmp(s, c1, c2, c2const); + if (cond == TCG_COND_LTU) { + tcg_out_arithi(s, ret, TCG_REG_G0, 0, ARITH_ADDX); + } else { + tcg_out_arithi(s, ret, TCG_REG_G0, -1, ARITH_SUBX); + } +} + +#if TCG_TARGET_REG_BITS == 64 +static void tcg_out_setcond_i64(TCGContext *s, TCGCond cond, TCGArg ret, + TCGArg c1, TCGArg c2, int c2const) +{ + tcg_out_cmp(s, c1, c2, c2const); + tcg_out_movi_imm13(s, ret, 0); + tcg_out32 (s, ARITH_MOVCC | INSN_RD(ret) + | INSN_RS1(tcg_cond_to_bcond[cond]) + | MOVCC_XCC | INSN_IMM11(1)); +} +#else +static void tcg_out_setcond2_i32(TCGContext *s, TCGCond cond, TCGArg ret, + TCGArg al, TCGArg ah, + TCGArg bl, int blconst, + TCGArg bh, int bhconst) +{ + int lab; + + switch (cond) { + case TCG_COND_EQ: + tcg_out_setcond_i32(s, TCG_COND_EQ, TCG_REG_I5, al, bl, blconst); + tcg_out_setcond_i32(s, TCG_COND_EQ, ret, ah, bh, bhconst); + tcg_out_arith(s, ret, ret, TCG_REG_I5, ARITH_AND); + break; + + case TCG_COND_NE: + tcg_out_setcond_i32(s, TCG_COND_NE, TCG_REG_I5, al, al, blconst); + tcg_out_setcond_i32(s, TCG_COND_NE, ret, ah, bh, bhconst); + tcg_out_arith(s, ret, ret, TCG_REG_I5, ARITH_OR); + break; + + default: + lab = gen_new_label(); + + tcg_out_cmp(s, ah, bh, bhconst); + tcg_out_branch_i32(s, INSN_COND(tcg_cond_to_bcond[cond], 1), lab); + tcg_out_movi_imm13(s, ret, 1); + tcg_out_branch_i32(s, INSN_COND(COND_NE, 1), lab); + tcg_out_movi_imm13(s, ret, 0); + + tcg_out_setcond_i32(s, tcg_unsigned_cond(cond), ret, al, bl, blconst); + + tcg_out_label(s, lab, (tcg_target_long)s->code_ptr); + break; + } +} #endif /* Generate global QEMU prologue and epilogue code */ -void tcg_target_qemu_prologue(TCGContext *s) +static void tcg_target_qemu_prologue(TCGContext *s) { tcg_out32(s, SAVE | INSN_RD(TCG_REG_O6) | INSN_RS1(TCG_REG_O6) | INSN_IMM13(-TCG_TARGET_STACK_MINFRAME)); @@ -541,11 +725,13 @@ #define TARGET_LD_OP LDX #endif -#if TARGET_PHYS_ADDR_BITS == 32 +#if defined(CONFIG_SOFTMMU) +#if HOST_LONG_BITS == 32 #define TARGET_ADDEND_LD_OP LDUW #else #define TARGET_ADDEND_LD_OP LDX #endif +#endif #ifdef __arch64__ #define HOST_LD_OP LDX @@ -609,7 +795,7 @@ tcg_out32(s, 0); /* mov (delay slot) */ - tcg_out_mov(s, arg0, addr_reg); + tcg_out_mov(s, TCG_TYPE_PTR, arg0, addr_reg); /* mov */ tcg_out_movi(s, TCG_TYPE_I32, arg1, mem_index); @@ -659,7 +845,7 @@ case 3: default: /* mov */ - tcg_out_mov(s, data_reg, arg0); + tcg_out_mov(s, TCG_TYPE_REG, data_reg, arg0); break; } @@ -821,10 +1007,10 @@ tcg_out32(s, 0); /* mov (delay slot) */ - tcg_out_mov(s, arg0, addr_reg); + tcg_out_mov(s, TCG_TYPE_PTR, arg0, addr_reg); /* mov */ - tcg_out_mov(s, arg1, data_reg); + tcg_out_mov(s, TCG_TYPE_REG, arg1, data_reg); /* mov */ tcg_out_movi(s, TCG_TYPE_I32, arg2, mem_index); @@ -927,7 +1113,7 @@ #endif } -static inline void tcg_out_op(TCGContext *s, int opc, const TCGArg *args, +static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg *args, const int *const_args) { int c; @@ -986,94 +1172,139 @@ tcg_out_movi(s, TCG_TYPE_I32, args[0], (uint32_t)args[1]); break; -#if defined(__sparc_v9__) && !defined(__sparc_v8plus__) +#if TCG_TARGET_REG_BITS == 64 #define OP_32_64(x) \ - glue(glue(case INDEX_op_, x), _i32:) \ - glue(glue(case INDEX_op_, x), _i64:) + glue(glue(case INDEX_op_, x), _i32): \ + glue(glue(case INDEX_op_, x), _i64) #else #define OP_32_64(x) \ - glue(glue(case INDEX_op_, x), _i32:) + glue(glue(case INDEX_op_, x), _i32) #endif - OP_32_64(ld8u); + OP_32_64(ld8u): tcg_out_ldst(s, args[0], args[1], args[2], LDUB); break; - OP_32_64(ld8s); + OP_32_64(ld8s): tcg_out_ldst(s, args[0], args[1], args[2], LDSB); break; - OP_32_64(ld16u); + OP_32_64(ld16u): tcg_out_ldst(s, args[0], args[1], args[2], LDUH); break; - OP_32_64(ld16s); + OP_32_64(ld16s): tcg_out_ldst(s, args[0], args[1], args[2], LDSH); break; case INDEX_op_ld_i32: -#if defined(__sparc_v9__) && !defined(__sparc_v8plus__) +#if TCG_TARGET_REG_BITS == 64 case INDEX_op_ld32u_i64: #endif tcg_out_ldst(s, args[0], args[1], args[2], LDUW); break; - OP_32_64(st8); + OP_32_64(st8): tcg_out_ldst(s, args[0], args[1], args[2], STB); break; - OP_32_64(st16); + OP_32_64(st16): tcg_out_ldst(s, args[0], args[1], args[2], STH); break; case INDEX_op_st_i32: -#if defined(__sparc_v9__) && !defined(__sparc_v8plus__) +#if TCG_TARGET_REG_BITS == 64 case INDEX_op_st32_i64: #endif tcg_out_ldst(s, args[0], args[1], args[2], STW); break; - OP_32_64(add); + OP_32_64(add): c = ARITH_ADD; - goto gen_arith32; - OP_32_64(sub); + goto gen_arith; + OP_32_64(sub): c = ARITH_SUB; - goto gen_arith32; - OP_32_64(and); + goto gen_arith; + OP_32_64(and): c = ARITH_AND; - goto gen_arith32; - OP_32_64(or); + goto gen_arith; + OP_32_64(andc): + c = ARITH_ANDN; + goto gen_arith; + OP_32_64(or): c = ARITH_OR; - goto gen_arith32; - OP_32_64(xor); + goto gen_arith; + OP_32_64(orc): + c = ARITH_ORN; + goto gen_arith; + OP_32_64(xor): c = ARITH_XOR; - goto gen_arith32; + goto gen_arith; case INDEX_op_shl_i32: c = SHIFT_SLL; - goto gen_arith32; + goto gen_arith; case INDEX_op_shr_i32: c = SHIFT_SRL; - goto gen_arith32; + goto gen_arith; case INDEX_op_sar_i32: c = SHIFT_SRA; - goto gen_arith32; + goto gen_arith; case INDEX_op_mul_i32: c = ARITH_UMUL; - goto gen_arith32; - case INDEX_op_div2_i32: -#if defined(__sparc_v9__) || defined(__sparc_v8plus__) - c = ARITH_SDIVX; - goto gen_arith32; -#else - tcg_out_sety(s, 0); - c = ARITH_SDIV; - goto gen_arith32; -#endif - case INDEX_op_divu2_i32: -#if defined(__sparc_v9__) || defined(__sparc_v8plus__) - c = ARITH_UDIVX; - goto gen_arith32; -#else - tcg_out_sety(s, 0); - c = ARITH_UDIV; - goto gen_arith32; -#endif + goto gen_arith; + + OP_32_64(neg): + c = ARITH_SUB; + goto gen_arith1; + OP_32_64(not): + c = ARITH_ORN; + goto gen_arith1; + + case INDEX_op_div_i32: + tcg_out_div32(s, args[0], args[1], args[2], const_args[2], 0); + break; + case INDEX_op_divu_i32: + tcg_out_div32(s, args[0], args[1], args[2], const_args[2], 1); + break; + + case INDEX_op_rem_i32: + case INDEX_op_remu_i32: + tcg_out_div32(s, TCG_REG_I5, args[1], args[2], const_args[2], + opc == INDEX_op_remu_i32); + tcg_out_arithc(s, TCG_REG_I5, TCG_REG_I5, args[2], const_args[2], + ARITH_UMUL); + tcg_out_arith(s, args[0], args[1], TCG_REG_I5, ARITH_SUB); + break; case INDEX_op_brcond_i32: tcg_out_brcond_i32(s, args[2], args[0], args[1], const_args[1], args[3]); break; + case INDEX_op_setcond_i32: + tcg_out_setcond_i32(s, args[3], args[0], args[1], + args[2], const_args[2]); + break; + +#if TCG_TARGET_REG_BITS == 32 + case INDEX_op_brcond2_i32: + tcg_out_brcond2_i32(s, args[4], args[0], args[1], + args[2], const_args[2], + args[3], const_args[3], args[5]); + break; + case INDEX_op_setcond2_i32: + tcg_out_setcond2_i32(s, args[5], args[0], args[1], args[2], + args[3], const_args[3], + args[4], const_args[4]); + break; + case INDEX_op_add2_i32: + tcg_out_arithc(s, args[0], args[2], args[4], const_args[4], + ARITH_ADDCC); + tcg_out_arithc(s, args[1], args[3], args[5], const_args[5], + ARITH_ADDX); + break; + case INDEX_op_sub2_i32: + tcg_out_arithc(s, args[0], args[2], args[4], const_args[4], + ARITH_SUBCC); + tcg_out_arithc(s, args[1], args[3], args[5], const_args[5], + ARITH_SUBX); + break; + case INDEX_op_mulu2_i32: + tcg_out_arithc(s, args[0], args[2], args[3], const_args[3], + ARITH_UMUL); + tcg_out_rdy(s, args[1]); + break; +#endif case INDEX_op_qemu_ld8u: tcg_out_qemu_ld(s, args, 0); @@ -1087,12 +1318,17 @@ case INDEX_op_qemu_ld16s: tcg_out_qemu_ld(s, args, 1 | 4); break; + case INDEX_op_qemu_ld32: +#if TCG_TARGET_REG_BITS == 64 case INDEX_op_qemu_ld32u: +#endif tcg_out_qemu_ld(s, args, 2); break; +#if TCG_TARGET_REG_BITS == 64 case INDEX_op_qemu_ld32s: tcg_out_qemu_ld(s, args, 2 | 4); break; +#endif case INDEX_op_qemu_st8: tcg_out_qemu_st(s, args, 0); break; @@ -1103,7 +1339,7 @@ tcg_out_qemu_st(s, args, 2); break; -#if defined(__sparc_v9__) && !defined(__sparc_v8plus__) +#if TCG_TARGET_REG_BITS == 64 case INDEX_op_movi_i64: tcg_out_movi(s, TCG_TYPE_I64, args[0], args[1]); break; @@ -1118,27 +1354,54 @@ break; case INDEX_op_shl_i64: c = SHIFT_SLLX; - goto gen_arith32; + goto gen_arith; case INDEX_op_shr_i64: c = SHIFT_SRLX; - goto gen_arith32; + goto gen_arith; case INDEX_op_sar_i64: c = SHIFT_SRAX; - goto gen_arith32; + goto gen_arith; case INDEX_op_mul_i64: c = ARITH_MULX; - goto gen_arith32; - case INDEX_op_div2_i64: + goto gen_arith; + case INDEX_op_div_i64: c = ARITH_SDIVX; - goto gen_arith32; - case INDEX_op_divu2_i64: + goto gen_arith; + case INDEX_op_divu_i64: c = ARITH_UDIVX; - goto gen_arith32; + goto gen_arith; + case INDEX_op_rem_i64: + case INDEX_op_remu_i64: + tcg_out_arithc(s, TCG_REG_I5, args[1], args[2], const_args[2], + opc == INDEX_op_rem_i64 ? ARITH_SDIVX : ARITH_UDIVX); + tcg_out_arithc(s, TCG_REG_I5, TCG_REG_I5, args[2], const_args[2], + ARITH_MULX); + tcg_out_arith(s, args[0], args[1], TCG_REG_I5, ARITH_SUB); + break; + case INDEX_op_ext32s_i64: + if (const_args[1]) { + tcg_out_movi(s, TCG_TYPE_I64, args[0], (int32_t)args[1]); + } else { + tcg_out_arithi(s, args[0], args[1], 0, SHIFT_SRA); + } + break; + case INDEX_op_ext32u_i64: + if (const_args[1]) { + tcg_out_movi_imm32(s, args[0], args[1]); + } else { + tcg_out_arithi(s, args[0], args[1], 0, SHIFT_SRL); + } + break; case INDEX_op_brcond_i64: tcg_out_brcond_i64(s, args[2], args[0], args[1], const_args[1], args[3]); break; + case INDEX_op_setcond_i64: + tcg_out_setcond_i64(s, args[3], args[0], args[1], + args[2], const_args[2]); + break; + case INDEX_op_qemu_ld64: tcg_out_qemu_ld(s, args, 3); break; @@ -1147,14 +1410,14 @@ break; #endif - gen_arith32: - if (const_args[2]) { - tcg_out_arithi(s, args[0], args[1], args[2], c); - } else { - tcg_out_arith(s, args[0], args[1], args[2], c); - } + gen_arith: + tcg_out_arithc(s, args[0], args[1], args[2], const_args[2], c); break; + gen_arith1: + tcg_out_arithc(s, args[0], TCG_REG_G0, args[1], const_args[1], c); + break; + default: fprintf(stderr, "unknown opcode 0x%x\n", opc); tcg_abort(); @@ -1181,31 +1444,50 @@ { INDEX_op_add_i32, { "r", "r", "rJ" } }, { INDEX_op_mul_i32, { "r", "r", "rJ" } }, - { INDEX_op_div2_i32, { "r", "r", "0", "1", "r" } }, - { INDEX_op_divu2_i32, { "r", "r", "0", "1", "r" } }, + { INDEX_op_div_i32, { "r", "r", "rJ" } }, + { INDEX_op_divu_i32, { "r", "r", "rJ" } }, + { INDEX_op_rem_i32, { "r", "r", "rJ" } }, + { INDEX_op_remu_i32, { "r", "r", "rJ" } }, { INDEX_op_sub_i32, { "r", "r", "rJ" } }, { INDEX_op_and_i32, { "r", "r", "rJ" } }, + { INDEX_op_andc_i32, { "r", "r", "rJ" } }, { INDEX_op_or_i32, { "r", "r", "rJ" } }, + { INDEX_op_orc_i32, { "r", "r", "rJ" } }, { INDEX_op_xor_i32, { "r", "r", "rJ" } }, { INDEX_op_shl_i32, { "r", "r", "rJ" } }, { INDEX_op_shr_i32, { "r", "r", "rJ" } }, { INDEX_op_sar_i32, { "r", "r", "rJ" } }, - { INDEX_op_brcond_i32, { "r", "ri" } }, + { INDEX_op_neg_i32, { "r", "rJ" } }, + { INDEX_op_not_i32, { "r", "rJ" } }, + + { INDEX_op_brcond_i32, { "r", "rJ" } }, + { INDEX_op_setcond_i32, { "r", "r", "rJ" } }, + +#if TCG_TARGET_REG_BITS == 32 + { INDEX_op_brcond2_i32, { "r", "r", "rJ", "rJ" } }, + { INDEX_op_setcond2_i32, { "r", "r", "r", "rJ", "rJ" } }, + { INDEX_op_add2_i32, { "r", "r", "r", "r", "rJ", "rJ" } }, + { INDEX_op_sub2_i32, { "r", "r", "r", "r", "rJ", "rJ" } }, + { INDEX_op_mulu2_i32, { "r", "r", "r", "rJ" } }, +#endif { INDEX_op_qemu_ld8u, { "r", "L" } }, { INDEX_op_qemu_ld8s, { "r", "L" } }, { INDEX_op_qemu_ld16u, { "r", "L" } }, { INDEX_op_qemu_ld16s, { "r", "L" } }, + { INDEX_op_qemu_ld32, { "r", "L" } }, +#if TCG_TARGET_REG_BITS == 64 { INDEX_op_qemu_ld32u, { "r", "L" } }, { INDEX_op_qemu_ld32s, { "r", "L" } }, +#endif { INDEX_op_qemu_st8, { "L", "L" } }, { INDEX_op_qemu_st16, { "L", "L" } }, { INDEX_op_qemu_st32, { "L", "L" } }, -#if defined(__sparc_v9__) && !defined(__sparc_v8plus__) +#if TCG_TARGET_REG_BITS == 64 { INDEX_op_mov_i64, { "r", "r" } }, { INDEX_op_movi_i64, { "r" } }, { INDEX_op_ld8u_i64, { "r", "r" } }, @@ -1224,26 +1506,37 @@ { INDEX_op_add_i64, { "r", "r", "rJ" } }, { INDEX_op_mul_i64, { "r", "r", "rJ" } }, - { INDEX_op_div2_i64, { "r", "r", "0", "1", "r" } }, - { INDEX_op_divu2_i64, { "r", "r", "0", "1", "r" } }, + { INDEX_op_div_i64, { "r", "r", "rJ" } }, + { INDEX_op_divu_i64, { "r", "r", "rJ" } }, + { INDEX_op_rem_i64, { "r", "r", "rJ" } }, + { INDEX_op_remu_i64, { "r", "r", "rJ" } }, { INDEX_op_sub_i64, { "r", "r", "rJ" } }, { INDEX_op_and_i64, { "r", "r", "rJ" } }, + { INDEX_op_andc_i64, { "r", "r", "rJ" } }, { INDEX_op_or_i64, { "r", "r", "rJ" } }, + { INDEX_op_orc_i64, { "r", "r", "rJ" } }, { INDEX_op_xor_i64, { "r", "r", "rJ" } }, { INDEX_op_shl_i64, { "r", "r", "rJ" } }, { INDEX_op_shr_i64, { "r", "r", "rJ" } }, { INDEX_op_sar_i64, { "r", "r", "rJ" } }, - { INDEX_op_brcond_i64, { "r", "ri" } }, + { INDEX_op_neg_i64, { "r", "rJ" } }, + { INDEX_op_not_i64, { "r", "rJ" } }, + + { INDEX_op_ext32s_i64, { "r", "ri" } }, + { INDEX_op_ext32u_i64, { "r", "ri" } }, + + { INDEX_op_brcond_i64, { "r", "rJ" } }, + { INDEX_op_setcond_i64, { "r", "r", "rJ" } }, #endif { -1 }, }; -void tcg_target_init(TCGContext *s) +static void tcg_target_init(TCGContext *s) { tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0, 0xffffffff); -#if defined(__sparc_v9__) && !defined(__sparc_v8plus__) +#if TCG_TARGET_REG_BITS == 64 tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I64], 0, 0xffffffff); #endif tcg_regset_set32(tcg_target_call_clobber_regs, 0, @@ -1264,7 +1557,7 @@ tcg_regset_clear(s->reserved_regs); tcg_regset_set_reg(s->reserved_regs, TCG_REG_G0); -#if defined(__sparc_v9__) && !defined(__sparc_v8plus__) +#if TCG_TARGET_REG_BITS == 64 tcg_regset_set_reg(s->reserved_regs, TCG_REG_I4); // for internal use #endif tcg_regset_set_reg(s->reserved_regs, TCG_REG_I5); // for internal use diff -Nru qemu-kvm-0.12.5+noroms/tcg/sparc/tcg-target.h qemu-kvm-0.14.1/tcg/sparc/tcg-target.h --- qemu-kvm-0.12.5+noroms/tcg/sparc/tcg-target.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/tcg/sparc/tcg-target.h 2011-05-11 13:29:46.000000000 +0000 @@ -87,26 +87,55 @@ #define TCG_TARGET_STACK_ALIGN 8 #endif -/* optional instructions */ -//#define TCG_TARGET_HAS_bswap32_i32 -//#define TCG_TARGET_HAS_bswap64_i64 -//#define TCG_TARGET_HAS_neg_i32 -//#define TCG_TARGET_HAS_neg_i64 +#ifdef __arch64__ +#define TCG_TARGET_EXTEND_ARGS 1 +#endif +/* optional instructions */ +#define TCG_TARGET_HAS_div_i32 +// #define TCG_TARGET_HAS_rot_i32 +// #define TCG_TARGET_HAS_ext8s_i32 +// #define TCG_TARGET_HAS_ext16s_i32 +// #define TCG_TARGET_HAS_ext8u_i32 +// #define TCG_TARGET_HAS_ext16u_i32 +// #define TCG_TARGET_HAS_bswap16_i32 +// #define TCG_TARGET_HAS_bswap32_i32 +#define TCG_TARGET_HAS_neg_i32 +#define TCG_TARGET_HAS_not_i32 +#define TCG_TARGET_HAS_andc_i32 +#define TCG_TARGET_HAS_orc_i32 +// #define TCG_TARGET_HAS_eqv_i32 +// #define TCG_TARGET_HAS_nand_i32 +// #define TCG_TARGET_HAS_nor_i32 + +#if TCG_TARGET_REG_BITS == 64 +#define TCG_TARGET_HAS_div_i64 +// #define TCG_TARGET_HAS_rot_i64 +// #define TCG_TARGET_HAS_ext8s_i64 +// #define TCG_TARGET_HAS_ext16s_i64 +#define TCG_TARGET_HAS_ext32s_i64 +// #define TCG_TARGET_HAS_ext8u_i64 +// #define TCG_TARGET_HAS_ext16u_i64 +#define TCG_TARGET_HAS_ext32u_i64 +// #define TCG_TARGET_HAS_bswap16_i64 +// #define TCG_TARGET_HAS_bswap32_i64 +// #define TCG_TARGET_HAS_bswap64_i64 +#define TCG_TARGET_HAS_neg_i64 +#define TCG_TARGET_HAS_not_i64 +#define TCG_TARGET_HAS_andc_i64 +#define TCG_TARGET_HAS_orc_i64 +// #define TCG_TARGET_HAS_eqv_i64 +// #define TCG_TARGET_HAS_nand_i64 +// #define TCG_TARGET_HAS_nor_i64 +#endif -/* Note: must be synced with dyngen-exec.h and Makefile.target */ +/* Note: must be synced with dyngen-exec.h */ #ifdef CONFIG_SOLARIS #define TCG_AREG0 TCG_REG_G2 -#define TCG_AREG1 TCG_REG_G3 -#define TCG_AREG2 TCG_REG_G4 #elif defined(__sparc_v9__) #define TCG_AREG0 TCG_REG_G5 -#define TCG_AREG1 TCG_REG_G6 -#define TCG_AREG2 TCG_REG_G7 #else #define TCG_AREG0 TCG_REG_G6 -#define TCG_AREG1 TCG_REG_G1 -#define TCG_AREG2 TCG_REG_G2 #endif static inline void flush_icache_range(unsigned long start, unsigned long stop) diff -Nru qemu-kvm-0.12.5+noroms/tcg/tcg.c qemu-kvm-0.14.1/tcg/tcg.c --- qemu-kvm-0.12.5+noroms/tcg/tcg.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/tcg/tcg.c 2011-05-11 13:29:46.000000000 +0000 @@ -27,7 +27,7 @@ #include "config.h" -#ifndef CONFIG_DEBUG_TCG +#if !defined(CONFIG_DEBUG_TCG) && !defined(NDEBUG) /* define it to suppress various consistency checks (faster) */ #define NDEBUG #endif @@ -47,6 +47,7 @@ #include "qemu-common.h" #include "cache-utils.h" #include "host-utils.h" +#include "qemu-timer.h" /* Note: the long term plan is to reduce the dependancies on the QEMU CPU definitions. Currently they are used for qemu_ld/st @@ -62,15 +63,15 @@ #error GUEST_BASE not supported on this host. #endif +static void tcg_target_init(TCGContext *s); +static void tcg_target_qemu_prologue(TCGContext *s); static void patch_reloc(uint8_t *code_ptr, int type, tcg_target_long value, tcg_target_long addend); static TCGOpDef tcg_op_defs[] = { -#define DEF(s, n, copy_size) { #s, 0, 0, n, n, 0, copy_size }, -#define DEF2(s, oargs, iargs, cargs, flags) { #s, oargs, iargs, cargs, iargs + oargs + cargs, flags, 0 }, +#define DEF(s, oargs, iargs, cargs, flags) { #s, oargs, iargs, cargs, iargs + oargs + cargs, flags }, #include "tcg-opc.h" #undef DEF -#undef DEF2 }; static TCGRegSet tcg_target_available_regs[2]; @@ -99,8 +100,8 @@ /* label relocation processing */ -void tcg_out_reloc(TCGContext *s, uint8_t *code_ptr, int type, - int label_index, long addend) +static void tcg_out_reloc(TCGContext *s, uint8_t *code_ptr, int type, + int label_index, long addend) { TCGLabel *l; TCGRelocation *r; @@ -239,7 +240,10 @@ } tcg_target_init(s); +} +void tcg_prologue_init(TCGContext *s) +{ /* init global prologue and epilogue */ s->code_buf = code_gen_prologue; s->code_ptr = s->code_buf; @@ -549,14 +553,36 @@ void tcg_gen_callN(TCGContext *s, TCGv_ptr func, unsigned int flags, int sizemask, TCGArg ret, int nargs, TCGArg *args) { +#ifdef TCG_TARGET_I386 int call_type; +#endif int i; int real_args; int nb_rets; TCGArg *nparam; + +#if defined(TCG_TARGET_EXTEND_ARGS) && TCG_TARGET_REG_BITS == 64 + for (i = 0; i < nargs; ++i) { + int is_64bit = sizemask & (1 << (i+1)*2); + int is_signed = sizemask & (2 << (i+1)*2); + if (!is_64bit) { + TCGv_i64 temp = tcg_temp_new_i64(); + TCGv_i64 orig = MAKE_TCGV_I64(args[i]); + if (is_signed) { + tcg_gen_ext32s_i64(temp, orig); + } else { + tcg_gen_ext32u_i64(temp, orig); + } + args[i] = GET_TCGV_I64(temp); + } + } +#endif /* TCG_TARGET_EXTEND_ARGS */ + *gen_opc_ptr++ = INDEX_op_call; nparam = gen_opparam_ptr++; +#ifdef TCG_TARGET_I386 call_type = (flags & TCG_CALL_TYPE_MASK); +#endif if (ret != TCG_CALL_DUMMY_ARG) { #if TCG_TARGET_REG_BITS < 64 if (sizemask & 1) { @@ -580,7 +606,8 @@ real_args = 0; for (i = 0; i < nargs; i++) { #if TCG_TARGET_REG_BITS < 64 - if (sizemask & (2 << i)) { + int is_64bit = sizemask & (1 << (i+1)*2); + if (is_64bit) { #ifdef TCG_TARGET_I386 /* REGPARM case: if the third parameter is 64 bit, it is allocated on the stack */ @@ -596,7 +623,17 @@ real_args++; } #endif -#ifdef TCG_TARGET_WORDS_BIGENDIAN + /* If stack grows up, then we will be placing successive + arguments at lower addresses, which means we need to + reverse the order compared to how we would normally + treat either big or little-endian. For those arguments + that will wind up in registers, this still works for + HPPA (the only current STACK_GROWSUP target) since the + argument registers are *also* allocated in decreasing + order. If another such target is added, this logic may + have to get more complicated to differentiate between + stack arguments and register arguments. */ +#if defined(TCG_TARGET_WORDS_BIGENDIAN) != defined(TCG_TARGET_STACK_GROWSUP) *gen_opparam_ptr++ = args[i] + 1; *gen_opparam_ptr++ = args[i]; #else @@ -604,12 +641,12 @@ *gen_opparam_ptr++ = args[i] + 1; #endif real_args += 2; - } else -#endif - { - *gen_opparam_ptr++ = args[i]; - real_args++; + continue; } +#endif /* TCG_TARGET_REG_BITS < 64 */ + + *gen_opparam_ptr++ = args[i]; + real_args++; } *gen_opparam_ptr++ = GET_TCGV_PTR(func); @@ -619,6 +656,16 @@ /* total parameters, needed to go backward in the instruction stream */ *gen_opparam_ptr++ = 1 + nb_rets + real_args + 3; + +#if defined(TCG_TARGET_EXTEND_ARGS) && TCG_TARGET_REG_BITS == 64 + for (i = 0; i < nargs; ++i) { + int is_64bit = sizemask & (1 << (i+1)*2); + if (!is_64bit) { + TCGv_i64 temp = MAKE_TCGV_I64(args[i]); + tcg_temp_free_i64(temp); + } + } +#endif /* TCG_TARGET_EXTEND_ARGS */ } #if TCG_TARGET_REG_BITS == 32 @@ -670,6 +717,7 @@ } #endif + static void tcg_reg_alloc_start(TCGContext *s) { int i; @@ -782,7 +830,8 @@ const uint16_t *opc_ptr; const TCGArg *args; TCGArg arg; - int c, i, k, nb_oargs, nb_iargs, nb_cargs, first_insn; + TCGOpcode c; + int i, k, nb_oargs, nb_iargs, nb_cargs, first_insn; const TCGOpDef *def; char buf[128]; @@ -888,21 +937,29 @@ fprintf(outfile, "%s", tcg_get_arg_str_idx(s, buf, sizeof(buf), args[k++])); } - if (c == INDEX_op_brcond_i32 + switch (c) { + case INDEX_op_brcond_i32: #if TCG_TARGET_REG_BITS == 32 - || c == INDEX_op_brcond2_i32 + case INDEX_op_brcond2_i32: #elif TCG_TARGET_REG_BITS == 64 - || c == INDEX_op_brcond_i64 + case INDEX_op_brcond_i64: +#endif + case INDEX_op_setcond_i32: +#if TCG_TARGET_REG_BITS == 32 + case INDEX_op_setcond2_i32: +#elif TCG_TARGET_REG_BITS == 64 + case INDEX_op_setcond_i64: #endif - ) { if (args[k] < ARRAY_SIZE(cond_name) && cond_name[args[k]]) fprintf(outfile, ",%s", cond_name[args[k++]]); else fprintf(outfile, ",$0x%" TCG_PRIlx, args[k++]); i = 1; - } - else + break; + default: i = 0; + break; + } for(; i < nb_cargs; i++) { if (k != 0) fprintf(outfile, ","); @@ -961,20 +1018,27 @@ void tcg_add_target_add_op_defs(const TCGTargetOpDef *tdefs) { - int op; + TCGOpcode op; TCGOpDef *def; const char *ct_str; int i, nb_args; for(;;) { - if (tdefs->op < 0) + if (tdefs->op == (TCGOpcode)-1) break; op = tdefs->op; - assert(op >= 0 && op < NB_OPS); + assert((unsigned)op < NB_OPS); def = &tcg_op_defs[op]; +#if defined(CONFIG_DEBUG_TCG) + /* Duplicate entry in op definitions? */ + assert(!def->used); + def->used = 1; +#endif nb_args = def->nb_iargs + def->nb_oargs; for(i = 0; i < nb_args; i++) { ct_str = tdefs->args_ct_str[i]; + /* Incomplete TCGTargetOpDef entry? */ + assert(ct_str != NULL); tcg_regset_clear(def->args_ct[i].u.regs); def->args_ct[i].ct = 0; if (ct_str[0] >= '0' && ct_str[0] <= '9') { @@ -1009,6 +1073,9 @@ } } + /* TCGTargetOpDef entry with too much information? */ + assert(i == TCG_MAX_OP_ARGS || tdefs->args_ct_str[i] == NULL); + /* sort the constraints (XXX: this is just an heuristic) */ sort_constraints(def, 0, def->nb_oargs); sort_constraints(def, def->nb_oargs, def->nb_iargs); @@ -1026,6 +1093,29 @@ tdefs++; } +#if defined(CONFIG_DEBUG_TCG) + i = 0; + for (op = 0; op < ARRAY_SIZE(tcg_op_defs); op++) { + if (op < INDEX_op_call || op == INDEX_op_debug_insn_start) { + /* Wrong entry in op definitions? */ + if (tcg_op_defs[op].used) { + fprintf(stderr, "Invalid op definition for %s\n", + tcg_op_defs[op].name); + i = 1; + } + } else { + /* Missing entry in op definitions? */ + if (!tcg_op_defs[op].used) { + fprintf(stderr, "Missing op definition for %s\n", + tcg_op_defs[op].name); + i = 1; + } + } + } + if (i == 1) { + tcg_abort(); + } +#endif } #ifdef USE_LIVENESS_ANALYSIS @@ -1076,7 +1166,8 @@ temporaries are removed. */ static void tcg_liveness_analysis(TCGContext *s) { - int i, op_index, op, nb_args, nb_iargs, nb_oargs, arg, nb_ops; + int i, op_index, nb_args, nb_iargs, nb_oargs, arg, nb_ops; + TCGOpcode op; TCGArg *args; const TCGOpDef *def; uint8_t *dead_temps; @@ -1224,7 +1315,7 @@ } #else /* dummy liveness analysis */ -void tcg_liveness_analysis(TCGContext *s) +static void tcg_liveness_analysis(TCGContext *s) { int nb_ops; nb_ops = gen_opc_ptr - gen_opc_buf; @@ -1485,7 +1576,7 @@ reg = tcg_reg_alloc(s, arg_ct->u.regs, s->reserved_regs); } if (ts->reg != reg) { - tcg_out_mov(s, reg, ts->reg); + tcg_out_mov(s, ots->type, reg, ts->reg); } } } else if (ts->val_type == TEMP_VAL_MEM) { @@ -1517,7 +1608,7 @@ } static void tcg_reg_alloc_op(TCGContext *s, - const TCGOpDef *def, int opc, + const TCGOpDef *def, TCGOpcode opc, const TCGArg *args, unsigned int dead_iargs) { @@ -1590,7 +1681,7 @@ /* allocate a new register matching the constraint and move the temporary register into it */ reg = tcg_reg_alloc(s, arg_ct->u.regs, allocated_regs); - tcg_out_mov(s, reg, ts->reg); + tcg_out_mov(s, ts->type, reg, ts->reg); } new_args[i] = reg; const_args[i] = 0; @@ -1672,7 +1763,7 @@ ts = &s->temps[args[i]]; reg = new_args[i]; if (ts->fixed_reg && ts->reg != reg) { - tcg_out_mov(s, ts->reg, reg); + tcg_out_mov(s, ts->type, ts->reg, reg); } } } @@ -1684,7 +1775,7 @@ #endif static int tcg_reg_alloc_call(TCGContext *s, const TCGOpDef *def, - int opc, const TCGArg *args, + TCGOpcode opc, const TCGArg *args, unsigned int dead_iargs) { int nb_iargs, nb_oargs, flags, nb_regs, i, reg, nb_params; @@ -1758,7 +1849,7 @@ tcg_reg_free(s, reg); if (ts->val_type == TEMP_VAL_REG) { if (ts->reg != reg) { - tcg_out_mov(s, reg, ts->reg); + tcg_out_mov(s, ts->type, reg, ts->reg); } } else if (ts->val_type == TEMP_VAL_MEM) { tcg_out_ld(s, ts->type, reg, ts->mem_reg, ts->mem_offset); @@ -1787,7 +1878,7 @@ reg = ts->reg; if (!tcg_regset_test_reg(arg_ct->u.regs, reg)) { reg = tcg_reg_alloc(s, arg_ct->u.regs, allocated_regs); - tcg_out_mov(s, reg, ts->reg); + tcg_out_mov(s, ts->type, reg, ts->reg); } func_arg = reg; tcg_regset_set_reg(allocated_regs, reg); @@ -1846,7 +1937,7 @@ assert(s->reg_to_temp[reg] == -1); if (ts->fixed_reg) { if (ts->reg != reg) { - tcg_out_mov(s, ts->reg, reg); + tcg_out_mov(s, ts->type, ts->reg, reg); } } else { if (ts->val_type == TEMP_VAL_REG) @@ -1881,7 +1972,8 @@ static inline int tcg_gen_code_common(TCGContext *s, uint8_t *gen_code_buf, long search_pc) { - int opc, op_index; + TCGOpcode opc; + int op_index; const TCGOpDef *def; unsigned int dead_iargs; const TCGArg *args; @@ -2032,8 +2124,7 @@ } #ifdef CONFIG_PROFILER -void tcg_dump_info(FILE *f, - int (*cpu_fprintf)(FILE *f, const char *fmt, ...)) +void tcg_dump_info(FILE *f, fprintf_function cpu_fprintf) { TCGContext *s = &tcg_ctx; int64_t tot; @@ -2077,8 +2168,7 @@ dump_op_count(); } #else -void tcg_dump_info(FILE *f, - int (*cpu_fprintf)(FILE *f, const char *fmt, ...)) +void tcg_dump_info(FILE *f, fprintf_function cpu_fprintf) { cpu_fprintf(f, "[TCG profiler not compiled]\n"); } diff -Nru qemu-kvm-0.12.5+noroms/tcg/tcg.h qemu-kvm-0.14.1/tcg/tcg.h --- qemu-kvm-0.12.5+noroms/tcg/tcg.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/tcg/tcg.h 2011-05-11 13:29:46.000000000 +0000 @@ -47,12 +47,12 @@ #error unsupported #endif -enum { -#define DEF(s, n, copy_size) INDEX_op_ ## s, +typedef enum TCGOpcode { +#define DEF(name, oargs, iargs, cargs, flags) INDEX_op_ ## name, #include "tcg-opc.h" #undef DEF NB_OPS, -}; +} TCGOpcode; #define tcg_regset_clear(d) (d) = 0 #define tcg_regset_set(d, s) (d) = (s) @@ -96,17 +96,29 @@ this value, they are statically allocated in the TB stack frame */ #define TCG_STATIC_CALL_ARGS_SIZE 128 -typedef int TCGType; - -#define TCG_TYPE_I32 0 -#define TCG_TYPE_I64 1 -#define TCG_TYPE_COUNT 2 /* number of different types */ +typedef enum TCGType { + TCG_TYPE_I32, + TCG_TYPE_I64, + TCG_TYPE_COUNT, /* number of different types */ + /* An alias for the size of the host register. */ #if TCG_TARGET_REG_BITS == 32 -#define TCG_TYPE_PTR TCG_TYPE_I32 + TCG_TYPE_REG = TCG_TYPE_I32, +#else + TCG_TYPE_REG = TCG_TYPE_I64, +#endif + + /* An alias for the size of the native pointer. We don't currently + support any hosts with 64-bit registers and 32-bit pointers. */ + TCG_TYPE_PTR = TCG_TYPE_REG, + + /* An alias for the size of the target "long", aka register. */ +#if TARGET_LONG_BITS == 64 + TCG_TYPE_TL = TCG_TYPE_I64, #else -#define TCG_TYPE_PTR TCG_TYPE_I64 + TCG_TYPE_TL = TCG_TYPE_I32, #endif +} TCGType; typedef tcg_target_ulong TCGArg; @@ -205,6 +217,24 @@ TCG_COND_GTU, } TCGCond; +/* Invert the sense of the comparison. */ +static inline TCGCond tcg_invert_cond(TCGCond c) +{ + return (TCGCond)(c ^ 1); +} + +/* Swap the operands in a comparison. */ +static inline TCGCond tcg_swap_cond(TCGCond c) +{ + int mask = (c < TCG_COND_LT ? 0 : c < TCG_COND_LTU ? 7 : 15); + return (TCGCond)(c ^ mask); +} + +static inline TCGCond tcg_unsigned_cond(TCGCond c) +{ + return (c >= TCG_COND_LT && c <= TCG_COND_GT ? c + 4 : c); +} + #define TEMP_VAL_DEAD 0 #define TEMP_VAL_REG 1 #define TEMP_VAL_MEM 2 @@ -323,6 +353,7 @@ } void tcg_context_init(TCGContext *s); +void tcg_prologue_init(TCGContext *s); void tcg_func_start(TCGContext *s); int tcg_gen_code(TCGContext *s, uint8_t *gen_code_buf); @@ -361,8 +392,7 @@ void tcg_temp_free_i64(TCGv_i64 arg); char *tcg_get_arg_str_i64(TCGContext *s, char *buf, int buf_size, TCGv_i64 arg); -void tcg_dump_info(FILE *f, - int (*cpu_fprintf)(FILE *f, const char *fmt, ...)); +void tcg_dump_info(FILE *f, fprintf_function cpu_fprintf); #define TCG_CT_ALIAS 0x80 #define TCG_CT_IALIAS 0x40 @@ -391,19 +421,18 @@ const char *name; uint8_t nb_oargs, nb_iargs, nb_cargs, nb_args; uint8_t flags; - uint16_t copy_size; TCGArgConstraint *args_ct; int *sorted_args; +#if defined(CONFIG_DEBUG_TCG) + int used; +#endif } TCGOpDef; typedef struct TCGTargetOpDef { - int op; + TCGOpcode op; const char *args_ct_str[TCG_MAX_OP_ARGS]; } TCGTargetOpDef; -void tcg_target_init(TCGContext *s); -void tcg_target_qemu_prologue(TCGContext *s); - #define tcg_abort() \ do {\ fprintf(stderr, "%s:%d: tcg fatal error\n", __FILE__, __LINE__);\ @@ -451,9 +480,6 @@ TCGv_i32 tcg_const_local_i32(int32_t val); TCGv_i64 tcg_const_local_i64(int64_t val); -void tcg_out_reloc(TCGContext *s, uint8_t *code_ptr, int type, - int label_index, long addend); - extern uint8_t code_gen_prologue[]; #if defined(_ARCH_PPC) && !defined(_ARCH_PPC64) #define tcg_qemu_tb_exec(tb_ptr) \ diff -Nru qemu-kvm-0.12.5+noroms/tcg/tcg-opc.h qemu-kvm-0.14.1/tcg/tcg-opc.h --- qemu-kvm-0.12.5+noroms/tcg/tcg-opc.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/tcg/tcg-opc.h 2011-05-11 13:29:46.000000000 +0000 @@ -21,252 +21,290 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#ifndef DEF2 -#define DEF2(name, oargs, iargs, cargs, flags) DEF(name, oargs + iargs + cargs, 0) -#endif -/* predefined ops */ -DEF2(end, 0, 0, 0, 0) /* must be kept first */ -DEF2(nop, 0, 0, 0, 0) -DEF2(nop1, 0, 0, 1, 0) -DEF2(nop2, 0, 0, 2, 0) -DEF2(nop3, 0, 0, 3, 0) -DEF2(nopn, 0, 0, 1, 0) /* variable number of parameters */ - -DEF2(discard, 1, 0, 0, 0) - -DEF2(set_label, 0, 0, 1, 0) -DEF2(call, 0, 1, 2, TCG_OPF_SIDE_EFFECTS) /* variable number of parameters */ -DEF2(jmp, 0, 1, 0, TCG_OPF_BB_END | TCG_OPF_SIDE_EFFECTS) -DEF2(br, 0, 0, 1, TCG_OPF_BB_END | TCG_OPF_SIDE_EFFECTS) +/* + * DEF(name, oargs, iargs, cargs, flags) + */ -DEF2(mov_i32, 1, 1, 0, 0) -DEF2(movi_i32, 1, 0, 1, 0) +/* predefined ops */ +DEF(end, 0, 0, 0, 0) /* must be kept first */ +DEF(nop, 0, 0, 0, 0) +DEF(nop1, 0, 0, 1, 0) +DEF(nop2, 0, 0, 2, 0) +DEF(nop3, 0, 0, 3, 0) +DEF(nopn, 0, 0, 1, 0) /* variable number of parameters */ + +DEF(discard, 1, 0, 0, 0) + +DEF(set_label, 0, 0, 1, 0) +DEF(call, 0, 1, 2, TCG_OPF_SIDE_EFFECTS) /* variable number of parameters */ +DEF(jmp, 0, 1, 0, TCG_OPF_BB_END | TCG_OPF_SIDE_EFFECTS) +DEF(br, 0, 0, 1, TCG_OPF_BB_END | TCG_OPF_SIDE_EFFECTS) + +DEF(mov_i32, 1, 1, 0, 0) +DEF(movi_i32, 1, 0, 1, 0) +DEF(setcond_i32, 1, 2, 1, 0) /* load/store */ -DEF2(ld8u_i32, 1, 1, 1, 0) -DEF2(ld8s_i32, 1, 1, 1, 0) -DEF2(ld16u_i32, 1, 1, 1, 0) -DEF2(ld16s_i32, 1, 1, 1, 0) -DEF2(ld_i32, 1, 1, 1, 0) -DEF2(st8_i32, 0, 2, 1, TCG_OPF_SIDE_EFFECTS) -DEF2(st16_i32, 0, 2, 1, TCG_OPF_SIDE_EFFECTS) -DEF2(st_i32, 0, 2, 1, TCG_OPF_SIDE_EFFECTS) +DEF(ld8u_i32, 1, 1, 1, 0) +DEF(ld8s_i32, 1, 1, 1, 0) +DEF(ld16u_i32, 1, 1, 1, 0) +DEF(ld16s_i32, 1, 1, 1, 0) +DEF(ld_i32, 1, 1, 1, 0) +DEF(st8_i32, 0, 2, 1, TCG_OPF_SIDE_EFFECTS) +DEF(st16_i32, 0, 2, 1, TCG_OPF_SIDE_EFFECTS) +DEF(st_i32, 0, 2, 1, TCG_OPF_SIDE_EFFECTS) /* arith */ -DEF2(add_i32, 1, 2, 0, 0) -DEF2(sub_i32, 1, 2, 0, 0) -DEF2(mul_i32, 1, 2, 0, 0) +DEF(add_i32, 1, 2, 0, 0) +DEF(sub_i32, 1, 2, 0, 0) +DEF(mul_i32, 1, 2, 0, 0) #ifdef TCG_TARGET_HAS_div_i32 -DEF2(div_i32, 1, 2, 0, 0) -DEF2(divu_i32, 1, 2, 0, 0) -DEF2(rem_i32, 1, 2, 0, 0) -DEF2(remu_i32, 1, 2, 0, 0) -#else -DEF2(div2_i32, 2, 3, 0, 0) -DEF2(divu2_i32, 2, 3, 0, 0) -#endif -DEF2(and_i32, 1, 2, 0, 0) -DEF2(or_i32, 1, 2, 0, 0) -DEF2(xor_i32, 1, 2, 0, 0) +DEF(div_i32, 1, 2, 0, 0) +DEF(divu_i32, 1, 2, 0, 0) +DEF(rem_i32, 1, 2, 0, 0) +DEF(remu_i32, 1, 2, 0, 0) +#endif +#ifdef TCG_TARGET_HAS_div2_i32 +DEF(div2_i32, 2, 3, 0, 0) +DEF(divu2_i32, 2, 3, 0, 0) +#endif +DEF(and_i32, 1, 2, 0, 0) +DEF(or_i32, 1, 2, 0, 0) +DEF(xor_i32, 1, 2, 0, 0) /* shifts/rotates */ -DEF2(shl_i32, 1, 2, 0, 0) -DEF2(shr_i32, 1, 2, 0, 0) -DEF2(sar_i32, 1, 2, 0, 0) +DEF(shl_i32, 1, 2, 0, 0) +DEF(shr_i32, 1, 2, 0, 0) +DEF(sar_i32, 1, 2, 0, 0) #ifdef TCG_TARGET_HAS_rot_i32 -DEF2(rotl_i32, 1, 2, 0, 0) -DEF2(rotr_i32, 1, 2, 0, 0) +DEF(rotl_i32, 1, 2, 0, 0) +DEF(rotr_i32, 1, 2, 0, 0) +#endif +#ifdef TCG_TARGET_HAS_deposit_i32 +DEF(deposit_i32, 1, 2, 2, 0) #endif -DEF2(brcond_i32, 0, 2, 2, TCG_OPF_BB_END | TCG_OPF_SIDE_EFFECTS) +DEF(brcond_i32, 0, 2, 2, TCG_OPF_BB_END | TCG_OPF_SIDE_EFFECTS) #if TCG_TARGET_REG_BITS == 32 -DEF2(add2_i32, 2, 4, 0, 0) -DEF2(sub2_i32, 2, 4, 0, 0) -DEF2(brcond2_i32, 0, 4, 2, TCG_OPF_BB_END | TCG_OPF_SIDE_EFFECTS) -DEF2(mulu2_i32, 2, 2, 0, 0) +DEF(add2_i32, 2, 4, 0, 0) +DEF(sub2_i32, 2, 4, 0, 0) +DEF(brcond2_i32, 0, 4, 2, TCG_OPF_BB_END | TCG_OPF_SIDE_EFFECTS) +DEF(mulu2_i32, 2, 2, 0, 0) +DEF(setcond2_i32, 1, 4, 1, 0) #endif #ifdef TCG_TARGET_HAS_ext8s_i32 -DEF2(ext8s_i32, 1, 1, 0, 0) +DEF(ext8s_i32, 1, 1, 0, 0) #endif #ifdef TCG_TARGET_HAS_ext16s_i32 -DEF2(ext16s_i32, 1, 1, 0, 0) +DEF(ext16s_i32, 1, 1, 0, 0) #endif #ifdef TCG_TARGET_HAS_ext8u_i32 -DEF2(ext8u_i32, 1, 1, 0, 0) +DEF(ext8u_i32, 1, 1, 0, 0) #endif #ifdef TCG_TARGET_HAS_ext16u_i32 -DEF2(ext16u_i32, 1, 1, 0, 0) +DEF(ext16u_i32, 1, 1, 0, 0) #endif #ifdef TCG_TARGET_HAS_bswap16_i32 -DEF2(bswap16_i32, 1, 1, 0, 0) +DEF(bswap16_i32, 1, 1, 0, 0) #endif #ifdef TCG_TARGET_HAS_bswap32_i32 -DEF2(bswap32_i32, 1, 1, 0, 0) +DEF(bswap32_i32, 1, 1, 0, 0) #endif #ifdef TCG_TARGET_HAS_not_i32 -DEF2(not_i32, 1, 1, 0, 0) +DEF(not_i32, 1, 1, 0, 0) #endif #ifdef TCG_TARGET_HAS_neg_i32 -DEF2(neg_i32, 1, 1, 0, 0) +DEF(neg_i32, 1, 1, 0, 0) +#endif +#ifdef TCG_TARGET_HAS_andc_i32 +DEF(andc_i32, 1, 2, 0, 0) +#endif +#ifdef TCG_TARGET_HAS_orc_i32 +DEF(orc_i32, 1, 2, 0, 0) +#endif +#ifdef TCG_TARGET_HAS_eqv_i32 +DEF(eqv_i32, 1, 2, 0, 0) +#endif +#ifdef TCG_TARGET_HAS_nand_i32 +DEF(nand_i32, 1, 2, 0, 0) +#endif +#ifdef TCG_TARGET_HAS_nor_i32 +DEF(nor_i32, 1, 2, 0, 0) #endif #if TCG_TARGET_REG_BITS == 64 -DEF2(mov_i64, 1, 1, 0, 0) -DEF2(movi_i64, 1, 0, 1, 0) +DEF(mov_i64, 1, 1, 0, 0) +DEF(movi_i64, 1, 0, 1, 0) +DEF(setcond_i64, 1, 2, 1, 0) /* load/store */ -DEF2(ld8u_i64, 1, 1, 1, 0) -DEF2(ld8s_i64, 1, 1, 1, 0) -DEF2(ld16u_i64, 1, 1, 1, 0) -DEF2(ld16s_i64, 1, 1, 1, 0) -DEF2(ld32u_i64, 1, 1, 1, 0) -DEF2(ld32s_i64, 1, 1, 1, 0) -DEF2(ld_i64, 1, 1, 1, 0) -DEF2(st8_i64, 0, 2, 1, TCG_OPF_SIDE_EFFECTS) -DEF2(st16_i64, 0, 2, 1, TCG_OPF_SIDE_EFFECTS) -DEF2(st32_i64, 0, 2, 1, TCG_OPF_SIDE_EFFECTS) -DEF2(st_i64, 0, 2, 1, TCG_OPF_SIDE_EFFECTS) +DEF(ld8u_i64, 1, 1, 1, 0) +DEF(ld8s_i64, 1, 1, 1, 0) +DEF(ld16u_i64, 1, 1, 1, 0) +DEF(ld16s_i64, 1, 1, 1, 0) +DEF(ld32u_i64, 1, 1, 1, 0) +DEF(ld32s_i64, 1, 1, 1, 0) +DEF(ld_i64, 1, 1, 1, 0) +DEF(st8_i64, 0, 2, 1, TCG_OPF_SIDE_EFFECTS) +DEF(st16_i64, 0, 2, 1, TCG_OPF_SIDE_EFFECTS) +DEF(st32_i64, 0, 2, 1, TCG_OPF_SIDE_EFFECTS) +DEF(st_i64, 0, 2, 1, TCG_OPF_SIDE_EFFECTS) /* arith */ -DEF2(add_i64, 1, 2, 0, 0) -DEF2(sub_i64, 1, 2, 0, 0) -DEF2(mul_i64, 1, 2, 0, 0) +DEF(add_i64, 1, 2, 0, 0) +DEF(sub_i64, 1, 2, 0, 0) +DEF(mul_i64, 1, 2, 0, 0) #ifdef TCG_TARGET_HAS_div_i64 -DEF2(div_i64, 1, 2, 0, 0) -DEF2(divu_i64, 1, 2, 0, 0) -DEF2(rem_i64, 1, 2, 0, 0) -DEF2(remu_i64, 1, 2, 0, 0) -#else -DEF2(div2_i64, 2, 3, 0, 0) -DEF2(divu2_i64, 2, 3, 0, 0) -#endif -DEF2(and_i64, 1, 2, 0, 0) -DEF2(or_i64, 1, 2, 0, 0) -DEF2(xor_i64, 1, 2, 0, 0) +DEF(div_i64, 1, 2, 0, 0) +DEF(divu_i64, 1, 2, 0, 0) +DEF(rem_i64, 1, 2, 0, 0) +DEF(remu_i64, 1, 2, 0, 0) +#endif +#ifdef TCG_TARGET_HAS_div2_i64 +DEF(div2_i64, 2, 3, 0, 0) +DEF(divu2_i64, 2, 3, 0, 0) +#endif +DEF(and_i64, 1, 2, 0, 0) +DEF(or_i64, 1, 2, 0, 0) +DEF(xor_i64, 1, 2, 0, 0) /* shifts/rotates */ -DEF2(shl_i64, 1, 2, 0, 0) -DEF2(shr_i64, 1, 2, 0, 0) -DEF2(sar_i64, 1, 2, 0, 0) +DEF(shl_i64, 1, 2, 0, 0) +DEF(shr_i64, 1, 2, 0, 0) +DEF(sar_i64, 1, 2, 0, 0) #ifdef TCG_TARGET_HAS_rot_i64 -DEF2(rotl_i64, 1, 2, 0, 0) -DEF2(rotr_i64, 1, 2, 0, 0) +DEF(rotl_i64, 1, 2, 0, 0) +DEF(rotr_i64, 1, 2, 0, 0) +#endif +#ifdef TCG_TARGET_HAS_deposit_i64 +DEF(deposit_i64, 1, 2, 2, 0) #endif -DEF2(brcond_i64, 0, 2, 2, TCG_OPF_BB_END | TCG_OPF_SIDE_EFFECTS) +DEF(brcond_i64, 0, 2, 2, TCG_OPF_BB_END | TCG_OPF_SIDE_EFFECTS) #ifdef TCG_TARGET_HAS_ext8s_i64 -DEF2(ext8s_i64, 1, 1, 0, 0) +DEF(ext8s_i64, 1, 1, 0, 0) #endif #ifdef TCG_TARGET_HAS_ext16s_i64 -DEF2(ext16s_i64, 1, 1, 0, 0) +DEF(ext16s_i64, 1, 1, 0, 0) #endif #ifdef TCG_TARGET_HAS_ext32s_i64 -DEF2(ext32s_i64, 1, 1, 0, 0) +DEF(ext32s_i64, 1, 1, 0, 0) #endif #ifdef TCG_TARGET_HAS_ext8u_i64 -DEF2(ext8u_i64, 1, 1, 0, 0) +DEF(ext8u_i64, 1, 1, 0, 0) #endif #ifdef TCG_TARGET_HAS_ext16u_i64 -DEF2(ext16u_i64, 1, 1, 0, 0) +DEF(ext16u_i64, 1, 1, 0, 0) #endif #ifdef TCG_TARGET_HAS_ext32u_i64 -DEF2(ext32u_i64, 1, 1, 0, 0) +DEF(ext32u_i64, 1, 1, 0, 0) #endif #ifdef TCG_TARGET_HAS_bswap16_i64 -DEF2(bswap16_i64, 1, 1, 0, 0) +DEF(bswap16_i64, 1, 1, 0, 0) #endif #ifdef TCG_TARGET_HAS_bswap32_i64 -DEF2(bswap32_i64, 1, 1, 0, 0) +DEF(bswap32_i64, 1, 1, 0, 0) #endif #ifdef TCG_TARGET_HAS_bswap64_i64 -DEF2(bswap64_i64, 1, 1, 0, 0) +DEF(bswap64_i64, 1, 1, 0, 0) #endif #ifdef TCG_TARGET_HAS_not_i64 -DEF2(not_i64, 1, 1, 0, 0) +DEF(not_i64, 1, 1, 0, 0) #endif #ifdef TCG_TARGET_HAS_neg_i64 -DEF2(neg_i64, 1, 1, 0, 0) +DEF(neg_i64, 1, 1, 0, 0) +#endif +#ifdef TCG_TARGET_HAS_andc_i64 +DEF(andc_i64, 1, 2, 0, 0) +#endif +#ifdef TCG_TARGET_HAS_orc_i64 +DEF(orc_i64, 1, 2, 0, 0) +#endif +#ifdef TCG_TARGET_HAS_eqv_i64 +DEF(eqv_i64, 1, 2, 0, 0) +#endif +#ifdef TCG_TARGET_HAS_nand_i64 +DEF(nand_i64, 1, 2, 0, 0) +#endif +#ifdef TCG_TARGET_HAS_nor_i64 +DEF(nor_i64, 1, 2, 0, 0) #endif #endif /* QEMU specific */ #if TARGET_LONG_BITS > TCG_TARGET_REG_BITS -DEF2(debug_insn_start, 0, 0, 2, 0) +DEF(debug_insn_start, 0, 0, 2, 0) #else -DEF2(debug_insn_start, 0, 0, 1, 0) +DEF(debug_insn_start, 0, 0, 1, 0) #endif -DEF2(exit_tb, 0, 0, 1, TCG_OPF_BB_END | TCG_OPF_SIDE_EFFECTS) -DEF2(goto_tb, 0, 0, 1, TCG_OPF_BB_END | TCG_OPF_SIDE_EFFECTS) +DEF(exit_tb, 0, 0, 1, TCG_OPF_BB_END | TCG_OPF_SIDE_EFFECTS) +DEF(goto_tb, 0, 0, 1, TCG_OPF_BB_END | TCG_OPF_SIDE_EFFECTS) /* Note: even if TARGET_LONG_BITS is not defined, the INDEX_op constants must be defined */ #if TCG_TARGET_REG_BITS == 32 #if TARGET_LONG_BITS == 32 -DEF2(qemu_ld8u, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) -#else -DEF2(qemu_ld8u, 1, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) -#endif -#if TARGET_LONG_BITS == 32 -DEF2(qemu_ld8s, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_ld8u, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) #else -DEF2(qemu_ld8s, 1, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_ld8u, 1, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) #endif #if TARGET_LONG_BITS == 32 -DEF2(qemu_ld16u, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_ld8s, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) #else -DEF2(qemu_ld16u, 1, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_ld8s, 1, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) #endif #if TARGET_LONG_BITS == 32 -DEF2(qemu_ld16s, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_ld16u, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) #else -DEF2(qemu_ld16s, 1, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_ld16u, 1, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) #endif #if TARGET_LONG_BITS == 32 -DEF2(qemu_ld32u, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_ld16s, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) #else -DEF2(qemu_ld32u, 1, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_ld16s, 1, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) #endif #if TARGET_LONG_BITS == 32 -DEF2(qemu_ld32s, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_ld32, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) #else -DEF2(qemu_ld32s, 1, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_ld32, 1, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) #endif #if TARGET_LONG_BITS == 32 -DEF2(qemu_ld64, 2, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_ld64, 2, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) #else -DEF2(qemu_ld64, 2, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_ld64, 2, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) #endif #if TARGET_LONG_BITS == 32 -DEF2(qemu_st8, 0, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_st8, 0, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) #else -DEF2(qemu_st8, 0, 3, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_st8, 0, 3, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) #endif #if TARGET_LONG_BITS == 32 -DEF2(qemu_st16, 0, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_st16, 0, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) #else -DEF2(qemu_st16, 0, 3, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_st16, 0, 3, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) #endif #if TARGET_LONG_BITS == 32 -DEF2(qemu_st32, 0, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_st32, 0, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) #else -DEF2(qemu_st32, 0, 3, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_st32, 0, 3, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) #endif #if TARGET_LONG_BITS == 32 -DEF2(qemu_st64, 0, 3, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_st64, 0, 3, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) #else -DEF2(qemu_st64, 0, 4, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_st64, 0, 4, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) #endif #else /* TCG_TARGET_REG_BITS == 32 */ -DEF2(qemu_ld8u, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) -DEF2(qemu_ld8s, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) -DEF2(qemu_ld16u, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) -DEF2(qemu_ld16s, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) -DEF2(qemu_ld32u, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) -DEF2(qemu_ld32s, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) -DEF2(qemu_ld64, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) - -DEF2(qemu_st8, 0, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) -DEF2(qemu_st16, 0, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) -DEF2(qemu_st32, 0, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) -DEF2(qemu_st64, 0, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_ld8u, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_ld8s, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_ld16u, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_ld16s, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_ld32, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_ld32u, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_ld32s, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_ld64, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) + +DEF(qemu_st8, 0, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_st16, 0, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_st32, 0, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_st64, 0, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) #endif /* TCG_TARGET_REG_BITS != 32 */ -#undef DEF2 +#undef DEF diff -Nru qemu-kvm-0.12.5+noroms/tcg/tcg-op.h qemu-kvm-0.14.1/tcg/tcg-op.h --- qemu-kvm-0.12.5+noroms/tcg/tcg-op.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/tcg/tcg-op.h 2011-05-11 13:29:46.000000000 +0000 @@ -25,60 +25,60 @@ int gen_new_label(void); -static inline void tcg_gen_op1_i32(int opc, TCGv_i32 arg1) +static inline void tcg_gen_op1_i32(TCGOpcode opc, TCGv_i32 arg1) { *gen_opc_ptr++ = opc; *gen_opparam_ptr++ = GET_TCGV_I32(arg1); } -static inline void tcg_gen_op1_i64(int opc, TCGv_i64 arg1) +static inline void tcg_gen_op1_i64(TCGOpcode opc, TCGv_i64 arg1) { *gen_opc_ptr++ = opc; *gen_opparam_ptr++ = GET_TCGV_I64(arg1); } -static inline void tcg_gen_op1i(int opc, TCGArg arg1) +static inline void tcg_gen_op1i(TCGOpcode opc, TCGArg arg1) { *gen_opc_ptr++ = opc; *gen_opparam_ptr++ = arg1; } -static inline void tcg_gen_op2_i32(int opc, TCGv_i32 arg1, TCGv_i32 arg2) +static inline void tcg_gen_op2_i32(TCGOpcode opc, TCGv_i32 arg1, TCGv_i32 arg2) { *gen_opc_ptr++ = opc; *gen_opparam_ptr++ = GET_TCGV_I32(arg1); *gen_opparam_ptr++ = GET_TCGV_I32(arg2); } -static inline void tcg_gen_op2_i64(int opc, TCGv_i64 arg1, TCGv_i64 arg2) +static inline void tcg_gen_op2_i64(TCGOpcode opc, TCGv_i64 arg1, TCGv_i64 arg2) { *gen_opc_ptr++ = opc; *gen_opparam_ptr++ = GET_TCGV_I64(arg1); *gen_opparam_ptr++ = GET_TCGV_I64(arg2); } -static inline void tcg_gen_op2i_i32(int opc, TCGv_i32 arg1, TCGArg arg2) +static inline void tcg_gen_op2i_i32(TCGOpcode opc, TCGv_i32 arg1, TCGArg arg2) { *gen_opc_ptr++ = opc; *gen_opparam_ptr++ = GET_TCGV_I32(arg1); *gen_opparam_ptr++ = arg2; } -static inline void tcg_gen_op2i_i64(int opc, TCGv_i64 arg1, TCGArg arg2) +static inline void tcg_gen_op2i_i64(TCGOpcode opc, TCGv_i64 arg1, TCGArg arg2) { *gen_opc_ptr++ = opc; *gen_opparam_ptr++ = GET_TCGV_I64(arg1); *gen_opparam_ptr++ = arg2; } -static inline void tcg_gen_op2ii(int opc, TCGArg arg1, TCGArg arg2) +static inline void tcg_gen_op2ii(TCGOpcode opc, TCGArg arg1, TCGArg arg2) { *gen_opc_ptr++ = opc; *gen_opparam_ptr++ = arg1; *gen_opparam_ptr++ = arg2; } -static inline void tcg_gen_op3_i32(int opc, TCGv_i32 arg1, TCGv_i32 arg2, +static inline void tcg_gen_op3_i32(TCGOpcode opc, TCGv_i32 arg1, TCGv_i32 arg2, TCGv_i32 arg3) { *gen_opc_ptr++ = opc; @@ -87,7 +87,7 @@ *gen_opparam_ptr++ = GET_TCGV_I32(arg3); } -static inline void tcg_gen_op3_i64(int opc, TCGv_i64 arg1, TCGv_i64 arg2, +static inline void tcg_gen_op3_i64(TCGOpcode opc, TCGv_i64 arg1, TCGv_i64 arg2, TCGv_i64 arg3) { *gen_opc_ptr++ = opc; @@ -96,8 +96,8 @@ *gen_opparam_ptr++ = GET_TCGV_I64(arg3); } -static inline void tcg_gen_op3i_i32(int opc, TCGv_i32 arg1, TCGv_i32 arg2, - TCGArg arg3) +static inline void tcg_gen_op3i_i32(TCGOpcode opc, TCGv_i32 arg1, + TCGv_i32 arg2, TCGArg arg3) { *gen_opc_ptr++ = opc; *gen_opparam_ptr++ = GET_TCGV_I32(arg1); @@ -105,8 +105,8 @@ *gen_opparam_ptr++ = arg3; } -static inline void tcg_gen_op3i_i64(int opc, TCGv_i64 arg1, TCGv_i64 arg2, - TCGArg arg3) +static inline void tcg_gen_op3i_i64(TCGOpcode opc, TCGv_i64 arg1, + TCGv_i64 arg2, TCGArg arg3) { *gen_opc_ptr++ = opc; *gen_opparam_ptr++ = GET_TCGV_I64(arg1); @@ -114,8 +114,8 @@ *gen_opparam_ptr++ = arg3; } -static inline void tcg_gen_ldst_op_i32(int opc, TCGv_i32 val, TCGv_ptr base, - TCGArg offset) +static inline void tcg_gen_ldst_op_i32(TCGOpcode opc, TCGv_i32 val, + TCGv_ptr base, TCGArg offset) { *gen_opc_ptr++ = opc; *gen_opparam_ptr++ = GET_TCGV_I32(val); @@ -123,8 +123,8 @@ *gen_opparam_ptr++ = offset; } -static inline void tcg_gen_ldst_op_i64(int opc, TCGv_i64 val, TCGv_ptr base, - TCGArg offset) +static inline void tcg_gen_ldst_op_i64(TCGOpcode opc, TCGv_i64 val, + TCGv_ptr base, TCGArg offset) { *gen_opc_ptr++ = opc; *gen_opparam_ptr++ = GET_TCGV_I64(val); @@ -132,8 +132,8 @@ *gen_opparam_ptr++ = offset; } -static inline void tcg_gen_qemu_ldst_op_i64_i32(int opc, TCGv_i64 val, TCGv_i32 addr, - TCGArg mem_index) +static inline void tcg_gen_qemu_ldst_op_i64_i32(TCGOpcode opc, TCGv_i64 val, + TCGv_i32 addr, TCGArg mem_index) { *gen_opc_ptr++ = opc; *gen_opparam_ptr++ = GET_TCGV_I64(val); @@ -141,8 +141,8 @@ *gen_opparam_ptr++ = mem_index; } -static inline void tcg_gen_qemu_ldst_op_i64_i64(int opc, TCGv_i64 val, TCGv_i64 addr, - TCGArg mem_index) +static inline void tcg_gen_qemu_ldst_op_i64_i64(TCGOpcode opc, TCGv_i64 val, + TCGv_i64 addr, TCGArg mem_index) { *gen_opc_ptr++ = opc; *gen_opparam_ptr++ = GET_TCGV_I64(val); @@ -150,7 +150,7 @@ *gen_opparam_ptr++ = mem_index; } -static inline void tcg_gen_op4_i32(int opc, TCGv_i32 arg1, TCGv_i32 arg2, +static inline void tcg_gen_op4_i32(TCGOpcode opc, TCGv_i32 arg1, TCGv_i32 arg2, TCGv_i32 arg3, TCGv_i32 arg4) { *gen_opc_ptr++ = opc; @@ -160,7 +160,7 @@ *gen_opparam_ptr++ = GET_TCGV_I32(arg4); } -static inline void tcg_gen_op4_i64(int opc, TCGv_i64 arg1, TCGv_i64 arg2, +static inline void tcg_gen_op4_i64(TCGOpcode opc, TCGv_i64 arg1, TCGv_i64 arg2, TCGv_i64 arg3, TCGv_i64 arg4) { *gen_opc_ptr++ = opc; @@ -170,7 +170,7 @@ *gen_opparam_ptr++ = GET_TCGV_I64(arg4); } -static inline void tcg_gen_op4i_i32(int opc, TCGv_i32 arg1, TCGv_i32 arg2, +static inline void tcg_gen_op4i_i32(TCGOpcode opc, TCGv_i32 arg1, TCGv_i32 arg2, TCGv_i32 arg3, TCGArg arg4) { *gen_opc_ptr++ = opc; @@ -180,7 +180,7 @@ *gen_opparam_ptr++ = arg4; } -static inline void tcg_gen_op4i_i64(int opc, TCGv_i64 arg1, TCGv_i64 arg2, +static inline void tcg_gen_op4i_i64(TCGOpcode opc, TCGv_i64 arg1, TCGv_i64 arg2, TCGv_i64 arg3, TCGArg arg4) { *gen_opc_ptr++ = opc; @@ -190,7 +190,7 @@ *gen_opparam_ptr++ = arg4; } -static inline void tcg_gen_op4ii_i32(int opc, TCGv_i32 arg1, TCGv_i32 arg2, +static inline void tcg_gen_op4ii_i32(TCGOpcode opc, TCGv_i32 arg1, TCGv_i32 arg2, TCGArg arg3, TCGArg arg4) { *gen_opc_ptr++ = opc; @@ -200,7 +200,7 @@ *gen_opparam_ptr++ = arg4; } -static inline void tcg_gen_op4ii_i64(int opc, TCGv_i64 arg1, TCGv_i64 arg2, +static inline void tcg_gen_op4ii_i64(TCGOpcode opc, TCGv_i64 arg1, TCGv_i64 arg2, TCGArg arg3, TCGArg arg4) { *gen_opc_ptr++ = opc; @@ -210,7 +210,7 @@ *gen_opparam_ptr++ = arg4; } -static inline void tcg_gen_op5_i32(int opc, TCGv_i32 arg1, TCGv_i32 arg2, +static inline void tcg_gen_op5_i32(TCGOpcode opc, TCGv_i32 arg1, TCGv_i32 arg2, TCGv_i32 arg3, TCGv_i32 arg4, TCGv_i32 arg5) { *gen_opc_ptr++ = opc; @@ -221,7 +221,7 @@ *gen_opparam_ptr++ = GET_TCGV_I32(arg5); } -static inline void tcg_gen_op5_i64(int opc, TCGv_i64 arg1, TCGv_i64 arg2, +static inline void tcg_gen_op5_i64(TCGOpcode opc, TCGv_i64 arg1, TCGv_i64 arg2, TCGv_i64 arg3, TCGv_i64 arg4, TCGv_i64 arg5) { *gen_opc_ptr++ = opc; @@ -232,7 +232,7 @@ *gen_opparam_ptr++ = GET_TCGV_I64(arg5); } -static inline void tcg_gen_op5i_i32(int opc, TCGv_i32 arg1, TCGv_i32 arg2, +static inline void tcg_gen_op5i_i32(TCGOpcode opc, TCGv_i32 arg1, TCGv_i32 arg2, TCGv_i32 arg3, TCGv_i32 arg4, TCGArg arg5) { *gen_opc_ptr++ = opc; @@ -243,7 +243,7 @@ *gen_opparam_ptr++ = arg5; } -static inline void tcg_gen_op5i_i64(int opc, TCGv_i64 arg1, TCGv_i64 arg2, +static inline void tcg_gen_op5i_i64(TCGOpcode opc, TCGv_i64 arg1, TCGv_i64 arg2, TCGv_i64 arg3, TCGv_i64 arg4, TCGArg arg5) { *gen_opc_ptr++ = opc; @@ -254,7 +254,31 @@ *gen_opparam_ptr++ = arg5; } -static inline void tcg_gen_op6_i32(int opc, TCGv_i32 arg1, TCGv_i32 arg2, +static inline void tcg_gen_op5ii_i32(TCGOpcode opc, TCGv_i32 arg1, + TCGv_i32 arg2, TCGv_i32 arg3, + TCGArg arg4, TCGArg arg5) +{ + *gen_opc_ptr++ = opc; + *gen_opparam_ptr++ = GET_TCGV_I32(arg1); + *gen_opparam_ptr++ = GET_TCGV_I32(arg2); + *gen_opparam_ptr++ = GET_TCGV_I32(arg3); + *gen_opparam_ptr++ = arg4; + *gen_opparam_ptr++ = arg5; +} + +static inline void tcg_gen_op5ii_i64(TCGOpcode opc, TCGv_i64 arg1, + TCGv_i64 arg2, TCGv_i64 arg3, + TCGArg arg4, TCGArg arg5) +{ + *gen_opc_ptr++ = opc; + *gen_opparam_ptr++ = GET_TCGV_I64(arg1); + *gen_opparam_ptr++ = GET_TCGV_I64(arg2); + *gen_opparam_ptr++ = GET_TCGV_I64(arg3); + *gen_opparam_ptr++ = arg4; + *gen_opparam_ptr++ = arg5; +} + +static inline void tcg_gen_op6_i32(TCGOpcode opc, TCGv_i32 arg1, TCGv_i32 arg2, TCGv_i32 arg3, TCGv_i32 arg4, TCGv_i32 arg5, TCGv_i32 arg6) { @@ -267,7 +291,7 @@ *gen_opparam_ptr++ = GET_TCGV_I32(arg6); } -static inline void tcg_gen_op6_i64(int opc, TCGv_i64 arg1, TCGv_i64 arg2, +static inline void tcg_gen_op6_i64(TCGOpcode opc, TCGv_i64 arg1, TCGv_i64 arg2, TCGv_i64 arg3, TCGv_i64 arg4, TCGv_i64 arg5, TCGv_i64 arg6) { @@ -280,9 +304,35 @@ *gen_opparam_ptr++ = GET_TCGV_I64(arg6); } -static inline void tcg_gen_op6ii_i32(int opc, TCGv_i32 arg1, TCGv_i32 arg2, - TCGv_i32 arg3, TCGv_i32 arg4, TCGArg arg5, - TCGArg arg6) +static inline void tcg_gen_op6i_i32(TCGOpcode opc, TCGv_i32 arg1, TCGv_i32 arg2, + TCGv_i32 arg3, TCGv_i32 arg4, + TCGv_i32 arg5, TCGArg arg6) +{ + *gen_opc_ptr++ = opc; + *gen_opparam_ptr++ = GET_TCGV_I32(arg1); + *gen_opparam_ptr++ = GET_TCGV_I32(arg2); + *gen_opparam_ptr++ = GET_TCGV_I32(arg3); + *gen_opparam_ptr++ = GET_TCGV_I32(arg4); + *gen_opparam_ptr++ = GET_TCGV_I32(arg5); + *gen_opparam_ptr++ = arg6; +} + +static inline void tcg_gen_op6i_i64(TCGOpcode opc, TCGv_i64 arg1, TCGv_i64 arg2, + TCGv_i64 arg3, TCGv_i64 arg4, + TCGv_i64 arg5, TCGArg arg6) +{ + *gen_opc_ptr++ = opc; + *gen_opparam_ptr++ = GET_TCGV_I64(arg1); + *gen_opparam_ptr++ = GET_TCGV_I64(arg2); + *gen_opparam_ptr++ = GET_TCGV_I64(arg3); + *gen_opparam_ptr++ = GET_TCGV_I64(arg4); + *gen_opparam_ptr++ = GET_TCGV_I64(arg5); + *gen_opparam_ptr++ = arg6; +} + +static inline void tcg_gen_op6ii_i32(TCGOpcode opc, TCGv_i32 arg1, + TCGv_i32 arg2, TCGv_i32 arg3, + TCGv_i32 arg4, TCGArg arg5, TCGArg arg6) { *gen_opc_ptr++ = opc; *gen_opparam_ptr++ = GET_TCGV_I32(arg1); @@ -293,9 +343,9 @@ *gen_opparam_ptr++ = arg6; } -static inline void tcg_gen_op6ii_i64(int opc, TCGv_i64 arg1, TCGv_i64 arg2, - TCGv_i64 arg3, TCGv_i64 arg4, TCGArg arg5, - TCGArg arg6) +static inline void tcg_gen_op6ii_i64(TCGOpcode opc, TCGv_i64 arg1, + TCGv_i64 arg2, TCGv_i64 arg3, + TCGv_i64 arg4, TCGArg arg5, TCGArg arg6) { *gen_opc_ptr++ = opc; *gen_opparam_ptr++ = GET_TCGV_I64(arg1); @@ -327,6 +377,13 @@ tcg_gen_op2i_i32(INDEX_op_movi_i32, ret, arg); } +/* A version of dh_sizemask from def-helper.h that doesn't rely on + preprocessor magic. */ +static inline int tcg_gen_sizemask(int n, int is_64bit, int is_signed) +{ + return (is_64bit << n*2) | (is_signed << (n*2 + 1)); +} + /* helper calls */ static inline void tcg_gen_helperN(void *func, int flags, int sizemask, TCGArg ret, int nargs, TCGArg *args) @@ -338,8 +395,25 @@ tcg_temp_free_ptr(fn); } -/* FIXME: Should this be pure? */ -static inline void tcg_gen_helper64(void *func, TCGv_i64 ret, +/* Note: Both tcg_gen_helper32() and tcg_gen_helper64() are currently + reserved for helpers in tcg-runtime.c. These helpers are all const + and pure, hence the call to tcg_gen_callN() with TCG_CALL_CONST | + TCG_CALL_PURE. This may need to be adjusted if these functions + start to be used with other helpers. */ +static inline void tcg_gen_helper32(void *func, int sizemask, TCGv_i32 ret, + TCGv_i32 a, TCGv_i32 b) +{ + TCGv_ptr fn; + TCGArg args[2]; + fn = tcg_const_ptr((tcg_target_long)func); + args[0] = GET_TCGV_I32(a); + args[1] = GET_TCGV_I32(b); + tcg_gen_callN(&tcg_ctx, fn, TCG_CALL_CONST | TCG_CALL_PURE, sizemask, + GET_TCGV_I32(ret), 2, args); + tcg_temp_free_ptr(fn); +} + +static inline void tcg_gen_helper64(void *func, int sizemask, TCGv_i64 ret, TCGv_i64 a, TCGv_i64 b) { TCGv_ptr fn; @@ -347,7 +421,8 @@ fn = tcg_const_ptr((tcg_target_long)func); args[0] = GET_TCGV_I64(a); args[1] = GET_TCGV_I64(b); - tcg_gen_callN(&tcg_ctx, fn, 0, 7, GET_TCGV_I64(ret), 2, args); + tcg_gen_callN(&tcg_ctx, fn, TCG_CALL_CONST | TCG_CALL_PURE, sizemask, + GET_TCGV_I64(ret), 2, args); tcg_temp_free_ptr(fn); } @@ -549,20 +624,34 @@ } } -static inline void tcg_gen_brcond_i32(int cond, TCGv_i32 arg1, TCGv_i32 arg2, - int label_index) +static inline void tcg_gen_brcond_i32(TCGCond cond, TCGv_i32 arg1, + TCGv_i32 arg2, int label_index) { tcg_gen_op4ii_i32(INDEX_op_brcond_i32, arg1, arg2, cond, label_index); } -static inline void tcg_gen_brcondi_i32(int cond, TCGv_i32 arg1, int32_t arg2, - int label_index) +static inline void tcg_gen_brcondi_i32(TCGCond cond, TCGv_i32 arg1, + int32_t arg2, int label_index) { TCGv_i32 t0 = tcg_const_i32(arg2); tcg_gen_brcond_i32(cond, arg1, t0, label_index); tcg_temp_free_i32(t0); } +static inline void tcg_gen_setcond_i32(TCGCond cond, TCGv_i32 ret, + TCGv_i32 arg1, TCGv_i32 arg2) +{ + tcg_gen_op4i_i32(INDEX_op_setcond_i32, ret, arg1, arg2, cond); +} + +static inline void tcg_gen_setcondi_i32(TCGCond cond, TCGv_i32 ret, + TCGv_i32 arg1, int32_t arg2) +{ + TCGv_i32 t0 = tcg_const_i32(arg2); + tcg_gen_setcond_i32(cond, ret, arg1, t0); + tcg_temp_free_i32(t0); +} + static inline void tcg_gen_mul_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { tcg_gen_op3_i32(INDEX_op_mul_i32, ret, arg1, arg2); @@ -595,7 +684,7 @@ { tcg_gen_op3_i32(INDEX_op_remu_i32, ret, arg1, arg2); } -#else +#elif defined(TCG_TARGET_HAS_div2_i32) static inline void tcg_gen_div_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { TCGv_i32 t0; @@ -631,6 +720,50 @@ tcg_gen_op5_i32(INDEX_op_divu2_i32, t0, ret, arg1, t0, arg2); tcg_temp_free_i32(t0); } +#else +static inline void tcg_gen_div_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + int sizemask = 0; + /* Return value and both arguments are 32-bit and signed. */ + sizemask |= tcg_gen_sizemask(0, 0, 1); + sizemask |= tcg_gen_sizemask(1, 0, 1); + sizemask |= tcg_gen_sizemask(2, 0, 1); + + tcg_gen_helper32(tcg_helper_div_i32, sizemask, ret, arg1, arg2); +} + +static inline void tcg_gen_rem_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + int sizemask = 0; + /* Return value and both arguments are 32-bit and signed. */ + sizemask |= tcg_gen_sizemask(0, 0, 1); + sizemask |= tcg_gen_sizemask(1, 0, 1); + sizemask |= tcg_gen_sizemask(2, 0, 1); + + tcg_gen_helper32(tcg_helper_rem_i32, sizemask, ret, arg1, arg2); +} + +static inline void tcg_gen_divu_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + int sizemask = 0; + /* Return value and both arguments are 32-bit and unsigned. */ + sizemask |= tcg_gen_sizemask(0, 0, 0); + sizemask |= tcg_gen_sizemask(1, 0, 0); + sizemask |= tcg_gen_sizemask(2, 0, 0); + + tcg_gen_helper32(tcg_helper_divu_i32, sizemask, ret, arg1, arg2); +} + +static inline void tcg_gen_remu_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) +{ + int sizemask = 0; + /* Return value and both arguments are 32-bit and unsigned. */ + sizemask |= tcg_gen_sizemask(0, 0, 0); + sizemask |= tcg_gen_sizemask(1, 0, 0); + sizemask |= tcg_gen_sizemask(2, 0, 0); + + tcg_gen_helper32(tcg_helper_remu_i32, sizemask, ret, arg1, arg2); +} #endif #if TCG_TARGET_REG_BITS == 32 @@ -789,7 +922,13 @@ specific code (x86) */ static inline void tcg_gen_shl_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { - tcg_gen_helper64(tcg_helper_shl_i64, ret, arg1, arg2); + int sizemask = 0; + /* Return value and both arguments are 64-bit and signed. */ + sizemask |= tcg_gen_sizemask(0, 1, 1); + sizemask |= tcg_gen_sizemask(1, 1, 1); + sizemask |= tcg_gen_sizemask(2, 1, 1); + + tcg_gen_helper64(tcg_helper_shl_i64, sizemask, ret, arg1, arg2); } static inline void tcg_gen_shli_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) @@ -799,7 +938,13 @@ static inline void tcg_gen_shr_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { - tcg_gen_helper64(tcg_helper_shr_i64, ret, arg1, arg2); + int sizemask = 0; + /* Return value and both arguments are 64-bit and signed. */ + sizemask |= tcg_gen_sizemask(0, 1, 1); + sizemask |= tcg_gen_sizemask(1, 1, 1); + sizemask |= tcg_gen_sizemask(2, 1, 1); + + tcg_gen_helper64(tcg_helper_shr_i64, sizemask, ret, arg1, arg2); } static inline void tcg_gen_shri_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) @@ -809,7 +954,13 @@ static inline void tcg_gen_sar_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { - tcg_gen_helper64(tcg_helper_sar_i64, ret, arg1, arg2); + int sizemask = 0; + /* Return value and both arguments are 64-bit and signed. */ + sizemask |= tcg_gen_sizemask(0, 1, 1); + sizemask |= tcg_gen_sizemask(1, 1, 1); + sizemask |= tcg_gen_sizemask(2, 1, 1); + + tcg_gen_helper64(tcg_helper_sar_i64, sizemask, ret, arg1, arg2); } static inline void tcg_gen_sari_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) @@ -817,14 +968,23 @@ tcg_gen_shifti_i64(ret, arg1, arg2, 1, 1); } -static inline void tcg_gen_brcond_i64(int cond, TCGv_i64 arg1, TCGv_i64 arg2, - int label_index) +static inline void tcg_gen_brcond_i64(TCGCond cond, TCGv_i64 arg1, + TCGv_i64 arg2, int label_index) { tcg_gen_op6ii_i32(INDEX_op_brcond2_i32, TCGV_LOW(arg1), TCGV_HIGH(arg1), TCGV_LOW(arg2), TCGV_HIGH(arg2), cond, label_index); } +static inline void tcg_gen_setcond_i64(TCGCond cond, TCGv_i64 ret, + TCGv_i64 arg1, TCGv_i64 arg2) +{ + tcg_gen_op6i_i32(INDEX_op_setcond2_i32, TCGV_LOW(ret), + TCGV_LOW(arg1), TCGV_HIGH(arg1), + TCGV_LOW(arg2), TCGV_HIGH(arg2), cond); + tcg_gen_movi_i32(TCGV_HIGH(ret), 0); +} + static inline void tcg_gen_mul_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { TCGv_i64 t0; @@ -848,22 +1008,46 @@ static inline void tcg_gen_div_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { - tcg_gen_helper64(tcg_helper_div_i64, ret, arg1, arg2); + int sizemask = 0; + /* Return value and both arguments are 64-bit and signed. */ + sizemask |= tcg_gen_sizemask(0, 1, 1); + sizemask |= tcg_gen_sizemask(1, 1, 1); + sizemask |= tcg_gen_sizemask(2, 1, 1); + + tcg_gen_helper64(tcg_helper_div_i64, sizemask, ret, arg1, arg2); } static inline void tcg_gen_rem_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { - tcg_gen_helper64(tcg_helper_rem_i64, ret, arg1, arg2); + int sizemask = 0; + /* Return value and both arguments are 64-bit and signed. */ + sizemask |= tcg_gen_sizemask(0, 1, 1); + sizemask |= tcg_gen_sizemask(1, 1, 1); + sizemask |= tcg_gen_sizemask(2, 1, 1); + + tcg_gen_helper64(tcg_helper_rem_i64, sizemask, ret, arg1, arg2); } static inline void tcg_gen_divu_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { - tcg_gen_helper64(tcg_helper_divu_i64, ret, arg1, arg2); + int sizemask = 0; + /* Return value and both arguments are 64-bit and unsigned. */ + sizemask |= tcg_gen_sizemask(0, 1, 0); + sizemask |= tcg_gen_sizemask(1, 1, 0); + sizemask |= tcg_gen_sizemask(2, 1, 0); + + tcg_gen_helper64(tcg_helper_divu_i64, sizemask, ret, arg1, arg2); } static inline void tcg_gen_remu_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { - tcg_gen_helper64(tcg_helper_remu_i64, ret, arg1, arg2); + int sizemask = 0; + /* Return value and both arguments are 64-bit and unsigned. */ + sizemask |= tcg_gen_sizemask(0, 1, 0); + sizemask |= tcg_gen_sizemask(1, 1, 0); + sizemask |= tcg_gen_sizemask(2, 1, 0); + + tcg_gen_helper64(tcg_helper_remu_i64, sizemask, ret, arg1, arg2); } #else @@ -1049,12 +1233,18 @@ } } -static inline void tcg_gen_brcond_i64(int cond, TCGv_i64 arg1, TCGv_i64 arg2, - int label_index) +static inline void tcg_gen_brcond_i64(TCGCond cond, TCGv_i64 arg1, + TCGv_i64 arg2, int label_index) { tcg_gen_op4ii_i64(INDEX_op_brcond_i64, arg1, arg2, cond, label_index); } +static inline void tcg_gen_setcond_i64(TCGCond cond, TCGv_i64 ret, + TCGv_i64 arg1, TCGv_i64 arg2) +{ + tcg_gen_op4i_i64(INDEX_op_setcond_i64, ret, arg1, arg2, cond); +} + static inline void tcg_gen_mul_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { tcg_gen_op3_i64(INDEX_op_mul_i64, ret, arg1, arg2); @@ -1080,7 +1270,7 @@ { tcg_gen_op3_i64(INDEX_op_remu_i64, ret, arg1, arg2); } -#else +#elif defined(TCG_TARGET_HAS_div2_i64) static inline void tcg_gen_div_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { TCGv_i64 t0; @@ -1116,6 +1306,50 @@ tcg_gen_op5_i64(INDEX_op_divu2_i64, t0, ret, arg1, t0, arg2); tcg_temp_free_i64(t0); } +#else +static inline void tcg_gen_div_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + int sizemask = 0; + /* Return value and both arguments are 64-bit and signed. */ + sizemask |= tcg_gen_sizemask(0, 1, 1); + sizemask |= tcg_gen_sizemask(1, 1, 1); + sizemask |= tcg_gen_sizemask(2, 1, 1); + + tcg_gen_helper64(tcg_helper_div_i64, sizemask, ret, arg1, arg2); +} + +static inline void tcg_gen_rem_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + int sizemask = 0; + /* Return value and both arguments are 64-bit and signed. */ + sizemask |= tcg_gen_sizemask(0, 1, 1); + sizemask |= tcg_gen_sizemask(1, 1, 1); + sizemask |= tcg_gen_sizemask(2, 1, 1); + + tcg_gen_helper64(tcg_helper_rem_i64, sizemask, ret, arg1, arg2); +} + +static inline void tcg_gen_divu_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + int sizemask = 0; + /* Return value and both arguments are 64-bit and unsigned. */ + sizemask |= tcg_gen_sizemask(0, 1, 0); + sizemask |= tcg_gen_sizemask(1, 1, 0); + sizemask |= tcg_gen_sizemask(2, 1, 0); + + tcg_gen_helper64(tcg_helper_divu_i64, sizemask, ret, arg1, arg2); +} + +static inline void tcg_gen_remu_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) +{ + int sizemask = 0; + /* Return value and both arguments are 64-bit and unsigned. */ + sizemask |= tcg_gen_sizemask(0, 1, 0); + sizemask |= tcg_gen_sizemask(1, 1, 0); + sizemask |= tcg_gen_sizemask(2, 1, 0); + + tcg_gen_helper64(tcg_helper_remu_i64, sizemask, ret, arg1, arg2); +} #endif #endif @@ -1150,14 +1384,22 @@ tcg_temp_free_i64(t0); } } -static inline void tcg_gen_brcondi_i64(int cond, TCGv_i64 arg1, int64_t arg2, - int label_index) +static inline void tcg_gen_brcondi_i64(TCGCond cond, TCGv_i64 arg1, + int64_t arg2, int label_index) { TCGv_i64 t0 = tcg_const_i64(arg2); tcg_gen_brcond_i64(cond, arg1, t0, label_index); tcg_temp_free_i64(t0); } +static inline void tcg_gen_setcondi_i64(TCGCond cond, TCGv_i64 ret, + TCGv_i64 arg1, int64_t arg2) +{ + TCGv_i64 t0 = tcg_const_i64(arg2); + tcg_gen_setcond_i64(cond, ret, arg1, t0); + tcg_temp_free_i64(t0); +} + static inline void tcg_gen_muli_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) { TCGv_i64 t0 = tcg_const_i64(arg2); @@ -1532,6 +1774,9 @@ { #ifdef TCG_TARGET_HAS_not_i64 tcg_gen_op2_i64(INDEX_op_not_i64, ret, arg); +#elif defined(TCG_TARGET_HAS_not_i32) && TCG_TARGET_REG_BITS == 32 + tcg_gen_not_i32(TCGV_LOW(ret), TCGV_LOW(arg)); + tcg_gen_not_i32(TCGV_HIGH(ret), TCGV_HIGH(arg)); #else tcg_gen_xori_i64(ret, arg, -1); #endif @@ -1587,74 +1832,129 @@ static inline void tcg_gen_andc_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { +#ifdef TCG_TARGET_HAS_andc_i32 + tcg_gen_op3_i32(INDEX_op_andc_i32, ret, arg1, arg2); +#else TCGv_i32 t0; t0 = tcg_temp_new_i32(); tcg_gen_not_i32(t0, arg2); tcg_gen_and_i32(ret, arg1, t0); tcg_temp_free_i32(t0); +#endif } static inline void tcg_gen_andc_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { +#ifdef TCG_TARGET_HAS_andc_i64 + tcg_gen_op3_i64(INDEX_op_andc_i64, ret, arg1, arg2); +#elif defined(TCG_TARGET_HAS_andc_i32) && TCG_TARGET_REG_BITS == 32 + tcg_gen_andc_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); + tcg_gen_andc_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); +#else TCGv_i64 t0; t0 = tcg_temp_new_i64(); tcg_gen_not_i64(t0, arg2); tcg_gen_and_i64(ret, arg1, t0); tcg_temp_free_i64(t0); +#endif } static inline void tcg_gen_eqv_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { +#ifdef TCG_TARGET_HAS_eqv_i32 + tcg_gen_op3_i32(INDEX_op_eqv_i32, ret, arg1, arg2); +#else tcg_gen_xor_i32(ret, arg1, arg2); tcg_gen_not_i32(ret, ret); +#endif } static inline void tcg_gen_eqv_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { +#ifdef TCG_TARGET_HAS_eqv_i64 + tcg_gen_op3_i64(INDEX_op_eqv_i64, ret, arg1, arg2); +#elif defined(TCG_TARGET_HAS_eqv_i32) && TCG_TARGET_REG_BITS == 32 + tcg_gen_eqv_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); + tcg_gen_eqv_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); +#else tcg_gen_xor_i64(ret, arg1, arg2); tcg_gen_not_i64(ret, ret); +#endif } static inline void tcg_gen_nand_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { +#ifdef TCG_TARGET_HAS_nand_i32 + tcg_gen_op3_i32(INDEX_op_nand_i32, ret, arg1, arg2); +#else tcg_gen_and_i32(ret, arg1, arg2); tcg_gen_not_i32(ret, ret); +#endif } static inline void tcg_gen_nand_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { +#ifdef TCG_TARGET_HAS_nand_i64 + tcg_gen_op3_i64(INDEX_op_nand_i64, ret, arg1, arg2); +#elif defined(TCG_TARGET_HAS_nand_i32) && TCG_TARGET_REG_BITS == 32 + tcg_gen_nand_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); + tcg_gen_nand_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); +#else tcg_gen_and_i64(ret, arg1, arg2); tcg_gen_not_i64(ret, ret); +#endif } static inline void tcg_gen_nor_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { +#ifdef TCG_TARGET_HAS_nor_i32 + tcg_gen_op3_i32(INDEX_op_nor_i32, ret, arg1, arg2); +#else tcg_gen_or_i32(ret, arg1, arg2); tcg_gen_not_i32(ret, ret); +#endif } static inline void tcg_gen_nor_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { +#ifdef TCG_TARGET_HAS_nor_i64 + tcg_gen_op3_i64(INDEX_op_nor_i64, ret, arg1, arg2); +#elif defined(TCG_TARGET_HAS_nor_i32) && TCG_TARGET_REG_BITS == 32 + tcg_gen_nor_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); + tcg_gen_nor_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); +#else tcg_gen_or_i64(ret, arg1, arg2); tcg_gen_not_i64(ret, ret); +#endif } static inline void tcg_gen_orc_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { +#ifdef TCG_TARGET_HAS_orc_i32 + tcg_gen_op3_i32(INDEX_op_orc_i32, ret, arg1, arg2); +#else TCGv_i32 t0; t0 = tcg_temp_new_i32(); tcg_gen_not_i32(t0, arg2); tcg_gen_or_i32(ret, arg1, t0); tcg_temp_free_i32(t0); +#endif } static inline void tcg_gen_orc_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { +#ifdef TCG_TARGET_HAS_orc_i64 + tcg_gen_op3_i64(INDEX_op_orc_i64, ret, arg1, arg2); +#elif defined(TCG_TARGET_HAS_orc_i32) && TCG_TARGET_REG_BITS == 32 + tcg_gen_orc_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); + tcg_gen_orc_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); +#else TCGv_i64 t0; t0 = tcg_temp_new_i64(); tcg_gen_not_i64(t0, arg2); tcg_gen_or_i64(ret, arg1, t0); tcg_temp_free_i64(t0); +#endif } static inline void tcg_gen_rotl_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) @@ -1795,6 +2095,44 @@ } } +static inline void tcg_gen_deposit_i32(TCGv_i32 ret, TCGv_i32 arg1, + TCGv_i32 arg2, unsigned int ofs, + unsigned int len) +{ +#ifdef TCG_TARGET_HAS_deposit_i32 + tcg_gen_op5ii_i32(INDEX_op_deposit_i32, ret, arg1, arg2, ofs, len); +#else + uint32_t mask = (1u << len) - 1; + TCGv_i32 t1 = tcg_temp_new_i32 (); + + tcg_gen_andi_i32(t1, arg2, mask); + tcg_gen_shli_i32(t1, t1, ofs); + tcg_gen_andi_i32(ret, arg1, ~(mask << ofs)); + tcg_gen_or_i32(ret, ret, t1); + + tcg_temp_free_i32(t1); +#endif +} + +static inline void tcg_gen_deposit_i64(TCGv_i64 ret, TCGv_i64 arg1, + TCGv_i64 arg2, unsigned int ofs, + unsigned int len) +{ +#ifdef TCG_TARGET_HAS_deposit_i64 + tcg_gen_op5ii_i64(INDEX_op_deposit_i64, ret, arg1, arg2, ofs, len); +#else + uint64_t mask = (1ull << len) - 1; + TCGv_i64 t1 = tcg_temp_new_i64 (); + + tcg_gen_andi_i64(t1, arg2, mask); + tcg_gen_shli_i64(t1, t1, ofs); + tcg_gen_andi_i64(ret, arg1, ~(mask << ofs)); + tcg_gen_or_i64(ret, ret, t1); + + tcg_temp_free_i64(t1); +#endif +} + /***************************************/ /* QEMU specific operations. Their type depend on the QEMU CPU type. */ @@ -1896,9 +2234,9 @@ static inline void tcg_gen_qemu_ld32u(TCGv ret, TCGv addr, int mem_index) { #if TARGET_LONG_BITS == 32 - tcg_gen_op3i_i32(INDEX_op_qemu_ld32u, ret, addr, mem_index); + tcg_gen_op3i_i32(INDEX_op_qemu_ld32, ret, addr, mem_index); #else - tcg_gen_op4i_i32(INDEX_op_qemu_ld32u, TCGV_LOW(ret), TCGV_LOW(addr), + tcg_gen_op4i_i32(INDEX_op_qemu_ld32, TCGV_LOW(ret), TCGV_LOW(addr), TCGV_HIGH(addr), mem_index); tcg_gen_movi_i32(TCGV_HIGH(ret), 0); #endif @@ -1907,9 +2245,9 @@ static inline void tcg_gen_qemu_ld32s(TCGv ret, TCGv addr, int mem_index) { #if TARGET_LONG_BITS == 32 - tcg_gen_op3i_i32(INDEX_op_qemu_ld32u, ret, addr, mem_index); + tcg_gen_op3i_i32(INDEX_op_qemu_ld32, ret, addr, mem_index); #else - tcg_gen_op4i_i32(INDEX_op_qemu_ld32u, TCGV_LOW(ret), TCGV_LOW(addr), + tcg_gen_op4i_i32(INDEX_op_qemu_ld32, TCGV_LOW(ret), TCGV_LOW(addr), TCGV_HIGH(addr), mem_index); tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_LOW(ret), 31); #endif @@ -1993,12 +2331,20 @@ static inline void tcg_gen_qemu_ld32u(TCGv ret, TCGv addr, int mem_index) { +#if TARGET_LONG_BITS == 32 + tcg_gen_qemu_ldst_op(INDEX_op_qemu_ld32, ret, addr, mem_index); +#else tcg_gen_qemu_ldst_op(INDEX_op_qemu_ld32u, ret, addr, mem_index); +#endif } static inline void tcg_gen_qemu_ld32s(TCGv ret, TCGv addr, int mem_index) { +#if TARGET_LONG_BITS == 32 + tcg_gen_qemu_ldst_op(INDEX_op_qemu_ld32, ret, addr, mem_index); +#else tcg_gen_qemu_ldst_op(INDEX_op_qemu_ld32s, ret, addr, mem_index); +#endif } static inline void tcg_gen_qemu_ld64(TCGv_i64 ret, TCGv addr, int mem_index) @@ -2032,7 +2378,6 @@ #endif /* TCG_TARGET_REG_BITS != 32 */ #if TARGET_LONG_BITS == 64 -#define TCG_TYPE_TL TCG_TYPE_I64 #define tcg_gen_movi_tl tcg_gen_movi_i64 #define tcg_gen_mov_tl tcg_gen_mov_i64 #define tcg_gen_ld8u_tl tcg_gen_ld8u_i64 @@ -2067,6 +2412,8 @@ #define tcg_gen_sari_tl tcg_gen_sari_i64 #define tcg_gen_brcond_tl tcg_gen_brcond_i64 #define tcg_gen_brcondi_tl tcg_gen_brcondi_i64 +#define tcg_gen_setcond_tl tcg_gen_setcond_i64 +#define tcg_gen_setcondi_tl tcg_gen_setcondi_i64 #define tcg_gen_mul_tl tcg_gen_mul_i64 #define tcg_gen_muli_tl tcg_gen_muli_i64 #define tcg_gen_div_tl tcg_gen_div_i64 @@ -2099,10 +2446,10 @@ #define tcg_gen_rotli_tl tcg_gen_rotli_i64 #define tcg_gen_rotr_tl tcg_gen_rotr_i64 #define tcg_gen_rotri_tl tcg_gen_rotri_i64 +#define tcg_gen_deposit_tl tcg_gen_deposit_i64 #define tcg_const_tl tcg_const_i64 #define tcg_const_local_tl tcg_const_local_i64 #else -#define TCG_TYPE_TL TCG_TYPE_I32 #define tcg_gen_movi_tl tcg_gen_movi_i32 #define tcg_gen_mov_tl tcg_gen_mov_i32 #define tcg_gen_ld8u_tl tcg_gen_ld8u_i32 @@ -2137,6 +2484,8 @@ #define tcg_gen_sari_tl tcg_gen_sari_i32 #define tcg_gen_brcond_tl tcg_gen_brcond_i32 #define tcg_gen_brcondi_tl tcg_gen_brcondi_i32 +#define tcg_gen_setcond_tl tcg_gen_setcond_i32 +#define tcg_gen_setcondi_tl tcg_gen_setcondi_i32 #define tcg_gen_mul_tl tcg_gen_mul_i32 #define tcg_gen_muli_tl tcg_gen_muli_i32 #define tcg_gen_div_tl tcg_gen_div_i32 @@ -2168,6 +2517,7 @@ #define tcg_gen_rotli_tl tcg_gen_rotli_i32 #define tcg_gen_rotr_tl tcg_gen_rotr_i32 #define tcg_gen_rotri_tl tcg_gen_rotri_i32 +#define tcg_gen_deposit_tl tcg_gen_deposit_i32 #define tcg_const_tl tcg_const_i32 #define tcg_const_local_tl tcg_const_local_i32 #endif diff -Nru qemu-kvm-0.12.5+noroms/tcg/tcg-runtime.h qemu-kvm-0.14.1/tcg/tcg-runtime.h --- qemu-kvm-0.12.5+noroms/tcg/tcg-runtime.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/tcg/tcg-runtime.h 2011-05-11 13:29:46.000000000 +0000 @@ -2,6 +2,11 @@ #define TCG_RUNTIME_H /* tcg-runtime.c */ +int32_t tcg_helper_div_i32(int32_t arg1, int32_t arg2); +int32_t tcg_helper_rem_i32(int32_t arg1, int32_t arg2); +uint32_t tcg_helper_divu_i32(uint32_t arg1, uint32_t arg2); +uint32_t tcg_helper_remu_i32(uint32_t arg1, uint32_t arg2); + int64_t tcg_helper_shl_i64(int64_t arg1, int64_t arg2); int64_t tcg_helper_shr_i64(int64_t arg1, int64_t arg2); int64_t tcg_helper_sar_i64(int64_t arg1, int64_t arg2); diff -Nru qemu-kvm-0.12.5+noroms/tcg/TODO qemu-kvm-0.14.1/tcg/TODO --- qemu-kvm-0.12.5+noroms/tcg/TODO 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/tcg/TODO 2011-05-11 13:29:46.000000000 +0000 @@ -1,4 +1,4 @@ -- Add new instructions such as: setcond, clz, ctz, popcnt. +- Add new instructions such as: clz, ctz, popcnt. - See if it is worth exporting mul2, mulu2, div2, divu2. diff -Nru qemu-kvm-0.12.5+noroms/tcg/x86_64/tcg-target.c qemu-kvm-0.14.1/tcg/x86_64/tcg-target.c --- qemu-kvm-0.12.5+noroms/tcg/x86_64/tcg-target.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/tcg/x86_64/tcg-target.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,1418 +0,0 @@ -/* - * Tiny Code Generator for QEMU - * - * Copyright (c) 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. - */ - -#ifndef NDEBUG -static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { - "%rax", - "%rcx", - "%rdx", - "%rbx", - "%rsp", - "%rbp", - "%rsi", - "%rdi", - "%r8", - "%r9", - "%r10", - "%r11", - "%r12", - "%r13", - "%r14", - "%r15", -}; -#endif - -static const int tcg_target_reg_alloc_order[] = { - TCG_REG_RBP, - TCG_REG_RBX, - TCG_REG_R12, - TCG_REG_R13, - TCG_REG_R14, - TCG_REG_R15, - TCG_REG_R10, - TCG_REG_R11, - TCG_REG_R9, - TCG_REG_R8, - TCG_REG_RCX, - TCG_REG_RDX, - TCG_REG_RSI, - TCG_REG_RDI, - TCG_REG_RAX, -}; - -static const int tcg_target_call_iarg_regs[6] = { - TCG_REG_RDI, - TCG_REG_RSI, - TCG_REG_RDX, - TCG_REG_RCX, - TCG_REG_R8, - TCG_REG_R9, -}; - -static const int tcg_target_call_oarg_regs[2] = { - TCG_REG_RAX, - TCG_REG_RDX -}; - -static uint8_t *tb_ret_addr; - -static void patch_reloc(uint8_t *code_ptr, int type, - tcg_target_long value, tcg_target_long addend) -{ - value += addend; - switch(type) { - case R_X86_64_32: - if (value != (uint32_t)value) - tcg_abort(); - *(uint32_t *)code_ptr = value; - break; - case R_X86_64_32S: - if (value != (int32_t)value) - tcg_abort(); - *(uint32_t *)code_ptr = value; - break; - case R_386_PC32: - value -= (long)code_ptr; - if (value != (int32_t)value) - tcg_abort(); - *(uint32_t *)code_ptr = value; - break; - default: - tcg_abort(); - } -} - -/* maximum number of register used for input function arguments */ -static inline int tcg_target_get_call_iarg_regs_count(int flags) -{ - return 6; -} - -/* parse target specific constraints */ -static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str) -{ - const char *ct_str; - - ct_str = *pct_str; - switch(ct_str[0]) { - case 'a': - ct->ct |= TCG_CT_REG; - tcg_regset_set_reg(ct->u.regs, TCG_REG_RAX); - break; - case 'b': - ct->ct |= TCG_CT_REG; - tcg_regset_set_reg(ct->u.regs, TCG_REG_RBX); - break; - case 'c': - ct->ct |= TCG_CT_REG; - tcg_regset_set_reg(ct->u.regs, TCG_REG_RCX); - break; - case 'd': - ct->ct |= TCG_CT_REG; - tcg_regset_set_reg(ct->u.regs, TCG_REG_RDX); - break; - case 'S': - ct->ct |= TCG_CT_REG; - tcg_regset_set_reg(ct->u.regs, TCG_REG_RSI); - break; - case 'D': - ct->ct |= TCG_CT_REG; - tcg_regset_set_reg(ct->u.regs, TCG_REG_RDI); - break; - case 'q': - ct->ct |= TCG_CT_REG; - tcg_regset_set32(ct->u.regs, 0, 0xf); - break; - case 'r': - ct->ct |= TCG_CT_REG; - tcg_regset_set32(ct->u.regs, 0, 0xffff); - break; - case 'L': /* qemu_ld/st constraint */ - ct->ct |= TCG_CT_REG; - tcg_regset_set32(ct->u.regs, 0, 0xffff); - tcg_regset_reset_reg(ct->u.regs, TCG_REG_RSI); - tcg_regset_reset_reg(ct->u.regs, TCG_REG_RDI); - break; - case 'e': - ct->ct |= TCG_CT_CONST_S32; - break; - case 'Z': - ct->ct |= TCG_CT_CONST_U32; - break; - default: - return -1; - } - ct_str++; - *pct_str = ct_str; - return 0; -} - -/* test if a constant matches the constraint */ -static inline int tcg_target_const_match(tcg_target_long val, - const TCGArgConstraint *arg_ct) -{ - int ct; - ct = arg_ct->ct; - if (ct & TCG_CT_CONST) - return 1; - else if ((ct & TCG_CT_CONST_S32) && val == (int32_t)val) - return 1; - else if ((ct & TCG_CT_CONST_U32) && val == (uint32_t)val) - return 1; - else - return 0; -} - -#define ARITH_ADD 0 -#define ARITH_OR 1 -#define ARITH_ADC 2 -#define ARITH_SBB 3 -#define ARITH_AND 4 -#define ARITH_SUB 5 -#define ARITH_XOR 6 -#define ARITH_CMP 7 - -#define SHIFT_ROL 0 -#define SHIFT_ROR 1 -#define SHIFT_SHL 4 -#define SHIFT_SHR 5 -#define SHIFT_SAR 7 - -#define JCC_JMP (-1) -#define JCC_JO 0x0 -#define JCC_JNO 0x1 -#define JCC_JB 0x2 -#define JCC_JAE 0x3 -#define JCC_JE 0x4 -#define JCC_JNE 0x5 -#define JCC_JBE 0x6 -#define JCC_JA 0x7 -#define JCC_JS 0x8 -#define JCC_JNS 0x9 -#define JCC_JP 0xa -#define JCC_JNP 0xb -#define JCC_JL 0xc -#define JCC_JGE 0xd -#define JCC_JLE 0xe -#define JCC_JG 0xf - -#define P_EXT 0x100 /* 0x0f opcode prefix */ -#define P_REXW 0x200 /* set rex.w = 1 */ -#define P_REXB 0x400 /* force rex use for byte registers */ - -static const uint8_t tcg_cond_to_jcc[10] = { - [TCG_COND_EQ] = JCC_JE, - [TCG_COND_NE] = JCC_JNE, - [TCG_COND_LT] = JCC_JL, - [TCG_COND_GE] = JCC_JGE, - [TCG_COND_LE] = JCC_JLE, - [TCG_COND_GT] = JCC_JG, - [TCG_COND_LTU] = JCC_JB, - [TCG_COND_GEU] = JCC_JAE, - [TCG_COND_LEU] = JCC_JBE, - [TCG_COND_GTU] = JCC_JA, -}; - -static inline void tcg_out_opc(TCGContext *s, int opc, int r, int rm, int x) -{ - int rex; - rex = ((opc >> 6) & 0x8) | ((r >> 1) & 0x4) | - ((x >> 2) & 2) | ((rm >> 3) & 1); - if (rex || (opc & P_REXB)) { - tcg_out8(s, rex | 0x40); - } - if (opc & P_EXT) - tcg_out8(s, 0x0f); - tcg_out8(s, opc & 0xff); -} - -static inline void tcg_out_modrm(TCGContext *s, int opc, int r, int rm) -{ - tcg_out_opc(s, opc, r, rm, 0); - tcg_out8(s, 0xc0 | ((r & 7) << 3) | (rm & 7)); -} - -/* rm < 0 means no register index plus (-rm - 1 immediate bytes) */ -static inline void tcg_out_modrm_offset(TCGContext *s, int opc, int r, int rm, - tcg_target_long offset) -{ - if (rm < 0) { - tcg_target_long val; - tcg_out_opc(s, opc, r, 0, 0); - val = offset - ((tcg_target_long)s->code_ptr + 5 + (-rm - 1)); - if (val == (int32_t)val) { - /* eip relative */ - tcg_out8(s, 0x05 | ((r & 7) << 3)); - tcg_out32(s, val); - } else if (offset == (int32_t)offset) { - tcg_out8(s, 0x04 | ((r & 7) << 3)); - tcg_out8(s, 0x25); /* sib */ - tcg_out32(s, offset); - } else { - tcg_abort(); - } - } else if (offset == 0 && (rm & 7) != TCG_REG_RBP) { - tcg_out_opc(s, opc, r, rm, 0); - if ((rm & 7) == TCG_REG_RSP) { - tcg_out8(s, 0x04 | ((r & 7) << 3)); - tcg_out8(s, 0x24); - } else { - tcg_out8(s, 0x00 | ((r & 7) << 3) | (rm & 7)); - } - } else if ((int8_t)offset == offset) { - tcg_out_opc(s, opc, r, rm, 0); - if ((rm & 7) == TCG_REG_RSP) { - tcg_out8(s, 0x44 | ((r & 7) << 3)); - tcg_out8(s, 0x24); - } else { - tcg_out8(s, 0x40 | ((r & 7) << 3) | (rm & 7)); - } - tcg_out8(s, offset); - } else { - tcg_out_opc(s, opc, r, rm, 0); - if ((rm & 7) == TCG_REG_RSP) { - tcg_out8(s, 0x84 | ((r & 7) << 3)); - tcg_out8(s, 0x24); - } else { - tcg_out8(s, 0x80 | ((r & 7) << 3) | (rm & 7)); - } - tcg_out32(s, offset); - } -} - -#if defined(CONFIG_SOFTMMU) -/* XXX: incomplete. index must be different from ESP */ -static void tcg_out_modrm_offset2(TCGContext *s, int opc, int r, int rm, - int index, int shift, - tcg_target_long offset) -{ - int mod; - if (rm == -1) - tcg_abort(); - if (offset == 0 && (rm & 7) != TCG_REG_RBP) { - mod = 0; - } else if (offset == (int8_t)offset) { - mod = 0x40; - } else if (offset == (int32_t)offset) { - mod = 0x80; - } else { - tcg_abort(); - } - if (index == -1) { - tcg_out_opc(s, opc, r, rm, 0); - if ((rm & 7) == TCG_REG_RSP) { - tcg_out8(s, mod | ((r & 7) << 3) | 0x04); - tcg_out8(s, 0x04 | (rm & 7)); - } else { - tcg_out8(s, mod | ((r & 7) << 3) | (rm & 7)); - } - } else { - tcg_out_opc(s, opc, r, rm, index); - tcg_out8(s, mod | ((r & 7) << 3) | 0x04); - tcg_out8(s, (shift << 6) | ((index & 7) << 3) | (rm & 7)); - } - if (mod == 0x40) { - tcg_out8(s, offset); - } else if (mod == 0x80) { - tcg_out32(s, offset); - } -} -#endif - -static inline void tcg_out_mov(TCGContext *s, int ret, int arg) -{ - tcg_out_modrm(s, 0x8b | P_REXW, ret, arg); -} - -static inline void tcg_out_movi(TCGContext *s, TCGType type, - int ret, tcg_target_long arg) -{ - if (arg == 0) { - tcg_out_modrm(s, 0x01 | (ARITH_XOR << 3), ret, ret); /* xor r0,r0 */ - } else if (arg == (uint32_t)arg || type == TCG_TYPE_I32) { - tcg_out_opc(s, 0xb8 + (ret & 7), 0, ret, 0); - tcg_out32(s, arg); - } else if (arg == (int32_t)arg) { - tcg_out_modrm(s, 0xc7 | P_REXW, 0, ret); - tcg_out32(s, arg); - } else { - tcg_out_opc(s, (0xb8 + (ret & 7)) | P_REXW, 0, ret, 0); - tcg_out32(s, arg); - tcg_out32(s, arg >> 32); - } -} - -static void tcg_out_goto(TCGContext *s, int call, uint8_t *target) -{ - int32_t disp; - - disp = target - s->code_ptr - 5; - if (disp == (target - s->code_ptr - 5)) { - tcg_out8(s, call ? 0xe8 : 0xe9); - tcg_out32(s, disp); - } else { - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R10, (tcg_target_long) target); - tcg_out_modrm(s, 0xff, call ? 2 : 4, TCG_REG_R10); - } -} - -static inline void tcg_out_ld(TCGContext *s, TCGType type, int ret, - int arg1, tcg_target_long arg2) -{ - if (type == TCG_TYPE_I32) - tcg_out_modrm_offset(s, 0x8b, ret, arg1, arg2); /* movl */ - else - tcg_out_modrm_offset(s, 0x8b | P_REXW, ret, arg1, arg2); /* movq */ -} - -static inline void tcg_out_st(TCGContext *s, TCGType type, int arg, - int arg1, tcg_target_long arg2) -{ - if (type == TCG_TYPE_I32) - tcg_out_modrm_offset(s, 0x89, arg, arg1, arg2); /* movl */ - else - tcg_out_modrm_offset(s, 0x89 | P_REXW, arg, arg1, arg2); /* movq */ -} - -static inline void tgen_arithi32(TCGContext *s, int c, int r0, int32_t val) -{ - if ((c == ARITH_ADD && val == 1) || (c == ARITH_SUB && val == -1)) { - /* inc */ - tcg_out_modrm(s, 0xff, 0, r0); - } else if ((c == ARITH_ADD && val == -1) || (c == ARITH_SUB && val == 1)) { - /* dec */ - tcg_out_modrm(s, 0xff, 1, r0); - } else if (val == (int8_t)val) { - tcg_out_modrm(s, 0x83, c, r0); - tcg_out8(s, val); - } else if (c == ARITH_AND && val == 0xffu) { - /* movzbl */ - tcg_out_modrm(s, 0xb6 | P_EXT | P_REXB, r0, r0); - } else if (c == ARITH_AND && val == 0xffffu) { - /* movzwl */ - tcg_out_modrm(s, 0xb7 | P_EXT, r0, r0); - } else { - tcg_out_modrm(s, 0x81, c, r0); - tcg_out32(s, val); - } -} - -static inline void tgen_arithi64(TCGContext *s, int c, int r0, int64_t val) -{ - if ((c == ARITH_ADD && val == 1) || (c == ARITH_SUB && val == -1)) { - /* inc */ - tcg_out_modrm(s, 0xff | P_REXW, 0, r0); - } else if ((c == ARITH_ADD && val == -1) || (c == ARITH_SUB && val == 1)) { - /* dec */ - tcg_out_modrm(s, 0xff | P_REXW, 1, r0); - } else if (val == (int8_t)val) { - tcg_out_modrm(s, 0x83 | P_REXW, c, r0); - tcg_out8(s, val); - } else if (c == ARITH_AND && val == 0xffu) { - /* movzbl */ - tcg_out_modrm(s, 0xb6 | P_EXT | P_REXW, r0, r0); - } else if (c == ARITH_AND && val == 0xffffu) { - /* movzwl */ - tcg_out_modrm(s, 0xb7 | P_EXT | P_REXW, r0, r0); - } else if (c == ARITH_AND && val == 0xffffffffu) { - /* 32-bit mov zero extends */ - tcg_out_modrm(s, 0x8b, r0, r0); - } else if (val == (int32_t)val) { - tcg_out_modrm(s, 0x81 | P_REXW, c, r0); - tcg_out32(s, val); - } else if (c == ARITH_AND && val == (uint32_t)val) { - tcg_out_modrm(s, 0x81, c, r0); - tcg_out32(s, val); - } else { - tcg_abort(); - } -} - -static void tcg_out_addi(TCGContext *s, int reg, tcg_target_long val) -{ - if (val != 0) - tgen_arithi64(s, ARITH_ADD, reg, val); -} - -static void tcg_out_jxx(TCGContext *s, int opc, int label_index) -{ - int32_t val, val1; - TCGLabel *l = &s->labels[label_index]; - - if (l->has_value) { - val = l->u.value - (tcg_target_long)s->code_ptr; - val1 = val - 2; - if ((int8_t)val1 == val1) { - if (opc == -1) - tcg_out8(s, 0xeb); - else - tcg_out8(s, 0x70 + opc); - tcg_out8(s, val1); - } else { - if (opc == -1) { - tcg_out8(s, 0xe9); - tcg_out32(s, val - 5); - } else { - tcg_out8(s, 0x0f); - tcg_out8(s, 0x80 + opc); - tcg_out32(s, val - 6); - } - } - } else { - if (opc == -1) { - tcg_out8(s, 0xe9); - } else { - tcg_out8(s, 0x0f); - tcg_out8(s, 0x80 + opc); - } - tcg_out_reloc(s, s->code_ptr, R_386_PC32, label_index, -4); - s->code_ptr += 4; - } -} - -static void tcg_out_brcond(TCGContext *s, int cond, - TCGArg arg1, TCGArg arg2, int const_arg2, - int label_index, int rexw) -{ - if (const_arg2) { - if (arg2 == 0) { - /* test r, r */ - tcg_out_modrm(s, 0x85 | rexw, arg1, arg1); - } else { - if (rexw) - tgen_arithi64(s, ARITH_CMP, arg1, arg2); - else - tgen_arithi32(s, ARITH_CMP, arg1, arg2); - } - } else { - tcg_out_modrm(s, 0x01 | (ARITH_CMP << 3) | rexw, arg2, arg1); - } - tcg_out_jxx(s, tcg_cond_to_jcc[cond], label_index); -} - -#if defined(CONFIG_SOFTMMU) - -#include "../../softmmu_defs.h" - -static void *qemu_ld_helpers[4] = { - __ldb_mmu, - __ldw_mmu, - __ldl_mmu, - __ldq_mmu, -}; - -static void *qemu_st_helpers[4] = { - __stb_mmu, - __stw_mmu, - __stl_mmu, - __stq_mmu, -}; -#endif - -static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, - int opc) -{ - int addr_reg, data_reg, r0, r1, mem_index, s_bits, bswap, rexw; - int32_t offset; -#if defined(CONFIG_SOFTMMU) - uint8_t *label1_ptr, *label2_ptr; -#endif - - data_reg = *args++; - addr_reg = *args++; - mem_index = *args; - s_bits = opc & 3; - - r0 = TCG_REG_RDI; - r1 = TCG_REG_RSI; - -#if TARGET_LONG_BITS == 32 - rexw = 0; -#else - rexw = P_REXW; -#endif -#if defined(CONFIG_SOFTMMU) - /* mov */ - tcg_out_modrm(s, 0x8b | rexw, r1, addr_reg); - - /* mov */ - tcg_out_modrm(s, 0x8b | rexw, r0, addr_reg); - - tcg_out_modrm(s, 0xc1 | rexw, 5, r1); /* shr $x, r1 */ - tcg_out8(s, TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS); - - tcg_out_modrm(s, 0x81 | rexw, 4, r0); /* andl $x, r0 */ - tcg_out32(s, TARGET_PAGE_MASK | ((1 << s_bits) - 1)); - - tcg_out_modrm(s, 0x81, 4, r1); /* andl $x, r1 */ - tcg_out32(s, (CPU_TLB_SIZE - 1) << CPU_TLB_ENTRY_BITS); - - /* lea offset(r1, env), r1 */ - tcg_out_modrm_offset2(s, 0x8d | P_REXW, r1, r1, TCG_AREG0, 0, - offsetof(CPUState, tlb_table[mem_index][0].addr_read)); - - /* cmp 0(r1), r0 */ - tcg_out_modrm_offset(s, 0x3b | rexw, r0, r1, 0); - - /* mov */ - tcg_out_modrm(s, 0x8b | rexw, r0, addr_reg); - - /* je label1 */ - tcg_out8(s, 0x70 + JCC_JE); - label1_ptr = s->code_ptr; - s->code_ptr++; - - /* XXX: move that code at the end of the TB */ - tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_RSI, mem_index); - tcg_out_goto(s, 1, qemu_ld_helpers[s_bits]); - - switch(opc) { - case 0 | 4: - /* movsbq */ - tcg_out_modrm(s, 0xbe | P_EXT | P_REXW, data_reg, TCG_REG_RAX); - break; - case 1 | 4: - /* movswq */ - tcg_out_modrm(s, 0xbf | P_EXT | P_REXW, data_reg, TCG_REG_RAX); - break; - case 2 | 4: - /* movslq */ - tcg_out_modrm(s, 0x63 | P_REXW, data_reg, TCG_REG_RAX); - break; - case 0: - /* movzbq */ - tcg_out_modrm(s, 0xb6 | P_EXT | P_REXW, data_reg, TCG_REG_RAX); - break; - case 1: - /* movzwq */ - tcg_out_modrm(s, 0xb7 | P_EXT | P_REXW, data_reg, TCG_REG_RAX); - break; - case 2: - default: - /* movl */ - tcg_out_modrm(s, 0x8b, data_reg, TCG_REG_RAX); - break; - case 3: - tcg_out_mov(s, data_reg, TCG_REG_RAX); - break; - } - - /* jmp label2 */ - tcg_out8(s, 0xeb); - label2_ptr = s->code_ptr; - s->code_ptr++; - - /* label1: */ - *label1_ptr = s->code_ptr - label1_ptr - 1; - - /* add x(r1), r0 */ - tcg_out_modrm_offset(s, 0x03 | P_REXW, r0, r1, offsetof(CPUTLBEntry, addend) - - offsetof(CPUTLBEntry, addr_read)); - offset = 0; -#else - if (GUEST_BASE == (int32_t)GUEST_BASE) { - r0 = addr_reg; - offset = GUEST_BASE; - } else { - offset = 0; - /* movq $GUEST_BASE, r0 */ - tcg_out_opc(s, (0xb8 + (r0 & 7)) | P_REXW, 0, r0, 0); - tcg_out32(s, GUEST_BASE); - tcg_out32(s, GUEST_BASE >> 32); - /* addq addr_reg, r0 */ - tcg_out_modrm(s, 0x01 | P_REXW, addr_reg, r0); - } -#endif - -#ifdef TARGET_WORDS_BIGENDIAN - bswap = 1; -#else - bswap = 0; -#endif - switch(opc) { - case 0: - /* movzbl */ - tcg_out_modrm_offset(s, 0xb6 | P_EXT, data_reg, r0, offset); - break; - case 0 | 4: - /* movsbX */ - tcg_out_modrm_offset(s, 0xbe | P_EXT | rexw, data_reg, r0, offset); - break; - case 1: - /* movzwl */ - tcg_out_modrm_offset(s, 0xb7 | P_EXT, data_reg, r0, offset); - if (bswap) { - /* rolw $8, data_reg */ - tcg_out8(s, 0x66); - tcg_out_modrm(s, 0xc1, 0, data_reg); - tcg_out8(s, 8); - } - break; - case 1 | 4: - if (bswap) { - /* movzwl */ - tcg_out_modrm_offset(s, 0xb7 | P_EXT, data_reg, r0, offset); - /* rolw $8, data_reg */ - tcg_out8(s, 0x66); - tcg_out_modrm(s, 0xc1, 0, data_reg); - tcg_out8(s, 8); - - /* movswX data_reg, data_reg */ - tcg_out_modrm(s, 0xbf | P_EXT | rexw, data_reg, data_reg); - } else { - /* movswX */ - tcg_out_modrm_offset(s, 0xbf | P_EXT | rexw, data_reg, r0, offset); - } - break; - case 2: - /* movl (r0), data_reg */ - tcg_out_modrm_offset(s, 0x8b, data_reg, r0, offset); - if (bswap) { - /* bswap */ - tcg_out_opc(s, (0xc8 + (data_reg & 7)) | P_EXT, 0, data_reg, 0); - } - break; - case 2 | 4: - if (bswap) { - /* movl (r0), data_reg */ - tcg_out_modrm_offset(s, 0x8b, data_reg, r0, offset); - /* bswap */ - tcg_out_opc(s, (0xc8 + (data_reg & 7)) | P_EXT, 0, data_reg, 0); - /* movslq */ - tcg_out_modrm(s, 0x63 | P_REXW, data_reg, data_reg); - } else { - /* movslq */ - tcg_out_modrm_offset(s, 0x63 | P_REXW, data_reg, r0, offset); - } - break; - case 3: - /* movq (r0), data_reg */ - tcg_out_modrm_offset(s, 0x8b | P_REXW, data_reg, r0, offset); - if (bswap) { - /* bswap */ - tcg_out_opc(s, (0xc8 + (data_reg & 7)) | P_EXT | P_REXW, 0, data_reg, 0); - } - break; - default: - tcg_abort(); - } - -#if defined(CONFIG_SOFTMMU) - /* label2: */ - *label2_ptr = s->code_ptr - label2_ptr - 1; -#endif -} - -static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, - int opc) -{ - int addr_reg, data_reg, r0, r1, mem_index, s_bits, bswap, rexw; - int32_t offset; -#if defined(CONFIG_SOFTMMU) - uint8_t *label1_ptr, *label2_ptr; -#endif - - data_reg = *args++; - addr_reg = *args++; - mem_index = *args; - - s_bits = opc; - - r0 = TCG_REG_RDI; - r1 = TCG_REG_RSI; - -#if TARGET_LONG_BITS == 32 - rexw = 0; -#else - rexw = P_REXW; -#endif -#if defined(CONFIG_SOFTMMU) - /* mov */ - tcg_out_modrm(s, 0x8b | rexw, r1, addr_reg); - - /* mov */ - tcg_out_modrm(s, 0x8b | rexw, r0, addr_reg); - - tcg_out_modrm(s, 0xc1 | rexw, 5, r1); /* shr $x, r1 */ - tcg_out8(s, TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS); - - tcg_out_modrm(s, 0x81 | rexw, 4, r0); /* andl $x, r0 */ - tcg_out32(s, TARGET_PAGE_MASK | ((1 << s_bits) - 1)); - - tcg_out_modrm(s, 0x81, 4, r1); /* andl $x, r1 */ - tcg_out32(s, (CPU_TLB_SIZE - 1) << CPU_TLB_ENTRY_BITS); - - /* lea offset(r1, env), r1 */ - tcg_out_modrm_offset2(s, 0x8d | P_REXW, r1, r1, TCG_AREG0, 0, - offsetof(CPUState, tlb_table[mem_index][0].addr_write)); - - /* cmp 0(r1), r0 */ - tcg_out_modrm_offset(s, 0x3b | rexw, r0, r1, 0); - - /* mov */ - tcg_out_modrm(s, 0x8b | rexw, r0, addr_reg); - - /* je label1 */ - tcg_out8(s, 0x70 + JCC_JE); - label1_ptr = s->code_ptr; - s->code_ptr++; - - /* XXX: move that code at the end of the TB */ - switch(opc) { - case 0: - /* movzbl */ - tcg_out_modrm(s, 0xb6 | P_EXT | P_REXB, TCG_REG_RSI, data_reg); - break; - case 1: - /* movzwl */ - tcg_out_modrm(s, 0xb7 | P_EXT, TCG_REG_RSI, data_reg); - break; - case 2: - /* movl */ - tcg_out_modrm(s, 0x8b, TCG_REG_RSI, data_reg); - break; - default: - case 3: - tcg_out_mov(s, TCG_REG_RSI, data_reg); - break; - } - tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_RDX, mem_index); - tcg_out_goto(s, 1, qemu_st_helpers[s_bits]); - - /* jmp label2 */ - tcg_out8(s, 0xeb); - label2_ptr = s->code_ptr; - s->code_ptr++; - - /* label1: */ - *label1_ptr = s->code_ptr - label1_ptr - 1; - - /* add x(r1), r0 */ - tcg_out_modrm_offset(s, 0x03 | P_REXW, r0, r1, offsetof(CPUTLBEntry, addend) - - offsetof(CPUTLBEntry, addr_write)); - offset = 0; -#else - if (GUEST_BASE == (int32_t)GUEST_BASE) { - r0 = addr_reg; - offset = GUEST_BASE; - } else { - offset = 0; - /* movq $GUEST_BASE, r0 */ - tcg_out_opc(s, (0xb8 + (r0 & 7)) | P_REXW, 0, r0, 0); - tcg_out32(s, GUEST_BASE); - tcg_out32(s, GUEST_BASE >> 32); - /* addq addr_reg, r0 */ - tcg_out_modrm(s, 0x01 | P_REXW, addr_reg, r0); - } -#endif - -#ifdef TARGET_WORDS_BIGENDIAN - bswap = 1; -#else - bswap = 0; -#endif - switch(opc) { - case 0: - /* movb */ - tcg_out_modrm_offset(s, 0x88 | P_REXB, data_reg, r0, offset); - break; - case 1: - if (bswap) { - tcg_out_modrm(s, 0x8b, r1, data_reg); /* movl */ - tcg_out8(s, 0x66); /* rolw $8, %ecx */ - tcg_out_modrm(s, 0xc1, 0, r1); - tcg_out8(s, 8); - data_reg = r1; - } - /* movw */ - tcg_out8(s, 0x66); - tcg_out_modrm_offset(s, 0x89, data_reg, r0, offset); - break; - case 2: - if (bswap) { - tcg_out_modrm(s, 0x8b, r1, data_reg); /* movl */ - /* bswap data_reg */ - tcg_out_opc(s, (0xc8 + r1) | P_EXT, 0, r1, 0); - data_reg = r1; - } - /* movl */ - tcg_out_modrm_offset(s, 0x89, data_reg, r0, offset); - break; - case 3: - if (bswap) { - tcg_out_mov(s, r1, data_reg); - /* bswap data_reg */ - tcg_out_opc(s, (0xc8 + r1) | P_EXT | P_REXW, 0, r1, 0); - data_reg = r1; - } - /* movq */ - tcg_out_modrm_offset(s, 0x89 | P_REXW, data_reg, r0, offset); - break; - default: - tcg_abort(); - } - -#if defined(CONFIG_SOFTMMU) - /* label2: */ - *label2_ptr = s->code_ptr - label2_ptr - 1; -#endif -} - -static inline void tcg_out_op(TCGContext *s, int opc, const TCGArg *args, - const int *const_args) -{ - int c; - - switch(opc) { - case INDEX_op_exit_tb: - tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_RAX, args[0]); - tcg_out_goto(s, 0, tb_ret_addr); - break; - case INDEX_op_goto_tb: - if (s->tb_jmp_offset) { - /* direct jump method */ - tcg_out8(s, 0xe9); /* jmp im */ - s->tb_jmp_offset[args[0]] = s->code_ptr - s->code_buf; - tcg_out32(s, 0); - } else { - /* indirect jump method */ - /* jmp Ev */ - tcg_out_modrm_offset(s, 0xff, 4, -1, - (tcg_target_long)(s->tb_next + - args[0])); - } - s->tb_next_offset[args[0]] = s->code_ptr - s->code_buf; - break; - case INDEX_op_call: - if (const_args[0]) { - tcg_out_goto(s, 1, (void *) args[0]); - } else { - tcg_out_modrm(s, 0xff, 2, args[0]); - } - break; - case INDEX_op_jmp: - if (const_args[0]) { - tcg_out_goto(s, 0, (void *) args[0]); - } else { - tcg_out_modrm(s, 0xff, 4, args[0]); - } - break; - case INDEX_op_br: - tcg_out_jxx(s, JCC_JMP, args[0]); - break; - case INDEX_op_movi_i32: - tcg_out_movi(s, TCG_TYPE_I32, args[0], (uint32_t)args[1]); - break; - case INDEX_op_movi_i64: - tcg_out_movi(s, TCG_TYPE_I64, args[0], args[1]); - break; - case INDEX_op_ld8u_i32: - case INDEX_op_ld8u_i64: - /* movzbl */ - tcg_out_modrm_offset(s, 0xb6 | P_EXT, args[0], args[1], args[2]); - break; - case INDEX_op_ld8s_i32: - /* movsbl */ - tcg_out_modrm_offset(s, 0xbe | P_EXT, args[0], args[1], args[2]); - break; - case INDEX_op_ld8s_i64: - /* movsbq */ - tcg_out_modrm_offset(s, 0xbe | P_EXT | P_REXW, args[0], args[1], args[2]); - break; - case INDEX_op_ld16u_i32: - case INDEX_op_ld16u_i64: - /* movzwl */ - tcg_out_modrm_offset(s, 0xb7 | P_EXT, args[0], args[1], args[2]); - break; - case INDEX_op_ld16s_i32: - /* movswl */ - tcg_out_modrm_offset(s, 0xbf | P_EXT, args[0], args[1], args[2]); - break; - case INDEX_op_ld16s_i64: - /* movswq */ - tcg_out_modrm_offset(s, 0xbf | P_EXT | P_REXW, args[0], args[1], args[2]); - break; - case INDEX_op_ld_i32: - case INDEX_op_ld32u_i64: - /* movl */ - tcg_out_modrm_offset(s, 0x8b, args[0], args[1], args[2]); - break; - case INDEX_op_ld32s_i64: - /* movslq */ - tcg_out_modrm_offset(s, 0x63 | P_REXW, args[0], args[1], args[2]); - break; - case INDEX_op_ld_i64: - /* movq */ - tcg_out_modrm_offset(s, 0x8b | P_REXW, args[0], args[1], args[2]); - break; - - case INDEX_op_st8_i32: - case INDEX_op_st8_i64: - /* movb */ - tcg_out_modrm_offset(s, 0x88 | P_REXB, args[0], args[1], args[2]); - break; - case INDEX_op_st16_i32: - case INDEX_op_st16_i64: - /* movw */ - tcg_out8(s, 0x66); - tcg_out_modrm_offset(s, 0x89, args[0], args[1], args[2]); - break; - case INDEX_op_st_i32: - case INDEX_op_st32_i64: - /* movl */ - tcg_out_modrm_offset(s, 0x89, args[0], args[1], args[2]); - break; - case INDEX_op_st_i64: - /* movq */ - tcg_out_modrm_offset(s, 0x89 | P_REXW, args[0], args[1], args[2]); - break; - - case INDEX_op_sub_i32: - c = ARITH_SUB; - goto gen_arith32; - case INDEX_op_and_i32: - c = ARITH_AND; - goto gen_arith32; - case INDEX_op_or_i32: - c = ARITH_OR; - goto gen_arith32; - case INDEX_op_xor_i32: - c = ARITH_XOR; - goto gen_arith32; - case INDEX_op_add_i32: - c = ARITH_ADD; - gen_arith32: - if (const_args[2]) { - tgen_arithi32(s, c, args[0], args[2]); - } else { - tcg_out_modrm(s, 0x01 | (c << 3), args[2], args[0]); - } - break; - - case INDEX_op_sub_i64: - c = ARITH_SUB; - goto gen_arith64; - case INDEX_op_and_i64: - c = ARITH_AND; - goto gen_arith64; - case INDEX_op_or_i64: - c = ARITH_OR; - goto gen_arith64; - case INDEX_op_xor_i64: - c = ARITH_XOR; - goto gen_arith64; - case INDEX_op_add_i64: - c = ARITH_ADD; - gen_arith64: - if (const_args[2]) { - tgen_arithi64(s, c, args[0], args[2]); - } else { - tcg_out_modrm(s, 0x01 | (c << 3) | P_REXW, args[2], args[0]); - } - break; - - case INDEX_op_mul_i32: - if (const_args[2]) { - int32_t val; - val = args[2]; - if (val == (int8_t)val) { - tcg_out_modrm(s, 0x6b, args[0], args[0]); - tcg_out8(s, val); - } else { - tcg_out_modrm(s, 0x69, args[0], args[0]); - tcg_out32(s, val); - } - } else { - tcg_out_modrm(s, 0xaf | P_EXT, args[0], args[2]); - } - break; - case INDEX_op_mul_i64: - if (const_args[2]) { - int32_t val; - val = args[2]; - if (val == (int8_t)val) { - tcg_out_modrm(s, 0x6b | P_REXW, args[0], args[0]); - tcg_out8(s, val); - } else { - tcg_out_modrm(s, 0x69 | P_REXW, args[0], args[0]); - tcg_out32(s, val); - } - } else { - tcg_out_modrm(s, 0xaf | P_EXT | P_REXW, args[0], args[2]); - } - break; - case INDEX_op_div2_i32: - tcg_out_modrm(s, 0xf7, 7, args[4]); - break; - case INDEX_op_divu2_i32: - tcg_out_modrm(s, 0xf7, 6, args[4]); - break; - case INDEX_op_div2_i64: - tcg_out_modrm(s, 0xf7 | P_REXW, 7, args[4]); - break; - case INDEX_op_divu2_i64: - tcg_out_modrm(s, 0xf7 | P_REXW, 6, args[4]); - break; - - case INDEX_op_shl_i32: - c = SHIFT_SHL; - gen_shift32: - if (const_args[2]) { - if (args[2] == 1) { - tcg_out_modrm(s, 0xd1, c, args[0]); - } else { - tcg_out_modrm(s, 0xc1, c, args[0]); - tcg_out8(s, args[2]); - } - } else { - tcg_out_modrm(s, 0xd3, c, args[0]); - } - break; - case INDEX_op_shr_i32: - c = SHIFT_SHR; - goto gen_shift32; - case INDEX_op_sar_i32: - c = SHIFT_SAR; - goto gen_shift32; - case INDEX_op_rotl_i32: - c = SHIFT_ROL; - goto gen_shift32; - case INDEX_op_rotr_i32: - c = SHIFT_ROR; - goto gen_shift32; - - case INDEX_op_shl_i64: - c = SHIFT_SHL; - gen_shift64: - if (const_args[2]) { - if (args[2] == 1) { - tcg_out_modrm(s, 0xd1 | P_REXW, c, args[0]); - } else { - tcg_out_modrm(s, 0xc1 | P_REXW, c, args[0]); - tcg_out8(s, args[2]); - } - } else { - tcg_out_modrm(s, 0xd3 | P_REXW, c, args[0]); - } - break; - case INDEX_op_shr_i64: - c = SHIFT_SHR; - goto gen_shift64; - case INDEX_op_sar_i64: - c = SHIFT_SAR; - goto gen_shift64; - case INDEX_op_rotl_i64: - c = SHIFT_ROL; - goto gen_shift64; - case INDEX_op_rotr_i64: - c = SHIFT_ROR; - goto gen_shift64; - - case INDEX_op_brcond_i32: - tcg_out_brcond(s, args[2], args[0], args[1], const_args[1], - args[3], 0); - break; - case INDEX_op_brcond_i64: - tcg_out_brcond(s, args[2], args[0], args[1], const_args[1], - args[3], P_REXW); - break; - - case INDEX_op_bswap16_i32: - case INDEX_op_bswap16_i64: - tcg_out8(s, 0x66); - tcg_out_modrm(s, 0xc1, SHIFT_ROL, args[0]); - tcg_out8(s, 8); - break; - case INDEX_op_bswap32_i32: - case INDEX_op_bswap32_i64: - tcg_out_opc(s, (0xc8 + (args[0] & 7)) | P_EXT, 0, args[0], 0); - break; - case INDEX_op_bswap64_i64: - tcg_out_opc(s, (0xc8 + (args[0] & 7)) | P_EXT | P_REXW, 0, args[0], 0); - break; - - case INDEX_op_neg_i32: - tcg_out_modrm(s, 0xf7, 3, args[0]); - break; - case INDEX_op_neg_i64: - tcg_out_modrm(s, 0xf7 | P_REXW, 3, args[0]); - break; - - case INDEX_op_not_i32: - tcg_out_modrm(s, 0xf7, 2, args[0]); - break; - case INDEX_op_not_i64: - tcg_out_modrm(s, 0xf7 | P_REXW, 2, args[0]); - break; - - case INDEX_op_ext8s_i32: - tcg_out_modrm(s, 0xbe | P_EXT | P_REXB, args[0], args[1]); - break; - case INDEX_op_ext16s_i32: - tcg_out_modrm(s, 0xbf | P_EXT, args[0], args[1]); - break; - case INDEX_op_ext8s_i64: - tcg_out_modrm(s, 0xbe | P_EXT | P_REXW, args[0], args[1]); - break; - case INDEX_op_ext16s_i64: - tcg_out_modrm(s, 0xbf | P_EXT | P_REXW, args[0], args[1]); - break; - case INDEX_op_ext32s_i64: - tcg_out_modrm(s, 0x63 | P_REXW, args[0], args[1]); - break; - case INDEX_op_ext8u_i32: - tcg_out_modrm(s, 0xb6 | P_EXT | P_REXB, args[0], args[1]); - break; - case INDEX_op_ext16u_i32: - tcg_out_modrm(s, 0xb7 | P_EXT, args[0], args[1]); - break; - case INDEX_op_ext8u_i64: - tcg_out_modrm(s, 0xb6 | P_EXT | P_REXW, args[0], args[1]); - break; - case INDEX_op_ext16u_i64: - tcg_out_modrm(s, 0xb7 | P_EXT | P_REXW, args[0], args[1]); - break; - case INDEX_op_ext32u_i64: - tcg_out_modrm(s, 0x8b, args[0], args[1]); - break; - - case INDEX_op_qemu_ld8u: - tcg_out_qemu_ld(s, args, 0); - break; - case INDEX_op_qemu_ld8s: - tcg_out_qemu_ld(s, args, 0 | 4); - break; - case INDEX_op_qemu_ld16u: - tcg_out_qemu_ld(s, args, 1); - break; - case INDEX_op_qemu_ld16s: - tcg_out_qemu_ld(s, args, 1 | 4); - break; - case INDEX_op_qemu_ld32u: - tcg_out_qemu_ld(s, args, 2); - break; - case INDEX_op_qemu_ld32s: - tcg_out_qemu_ld(s, args, 2 | 4); - break; - case INDEX_op_qemu_ld64: - tcg_out_qemu_ld(s, args, 3); - break; - - case INDEX_op_qemu_st8: - tcg_out_qemu_st(s, args, 0); - break; - case INDEX_op_qemu_st16: - tcg_out_qemu_st(s, args, 1); - break; - case INDEX_op_qemu_st32: - tcg_out_qemu_st(s, args, 2); - break; - case INDEX_op_qemu_st64: - tcg_out_qemu_st(s, args, 3); - break; - - default: - tcg_abort(); - } -} - -static int tcg_target_callee_save_regs[] = { - TCG_REG_RBP, - TCG_REG_RBX, - TCG_REG_R12, - TCG_REG_R13, - /* TCG_REG_R14, */ /* currently used for the global env, so no - need to save */ - TCG_REG_R15, -}; - -static inline void tcg_out_push(TCGContext *s, int reg) -{ - tcg_out_opc(s, (0x50 + (reg & 7)), 0, reg, 0); -} - -static inline void tcg_out_pop(TCGContext *s, int reg) -{ - tcg_out_opc(s, (0x58 + (reg & 7)), 0, reg, 0); -} - -/* Generate global QEMU prologue and epilogue code */ -void tcg_target_qemu_prologue(TCGContext *s) -{ - int i, frame_size, push_size, stack_addend; - - /* TB prologue */ - /* save all callee saved registers */ - for(i = 0; i < ARRAY_SIZE(tcg_target_callee_save_regs); i++) { - tcg_out_push(s, tcg_target_callee_save_regs[i]); - - } - /* reserve some stack space */ - push_size = 8 + ARRAY_SIZE(tcg_target_callee_save_regs) * 8; - frame_size = push_size + TCG_STATIC_CALL_ARGS_SIZE; - frame_size = (frame_size + TCG_TARGET_STACK_ALIGN - 1) & - ~(TCG_TARGET_STACK_ALIGN - 1); - stack_addend = frame_size - push_size; - tcg_out_addi(s, TCG_REG_RSP, -stack_addend); - - tcg_out_modrm(s, 0xff, 4, TCG_REG_RDI); /* jmp *%rdi */ - - /* TB epilogue */ - tb_ret_addr = s->code_ptr; - tcg_out_addi(s, TCG_REG_RSP, stack_addend); - for(i = ARRAY_SIZE(tcg_target_callee_save_regs) - 1; i >= 0; i--) { - tcg_out_pop(s, tcg_target_callee_save_regs[i]); - } - tcg_out8(s, 0xc3); /* ret */ -} - -static const TCGTargetOpDef x86_64_op_defs[] = { - { INDEX_op_exit_tb, { } }, - { INDEX_op_goto_tb, { } }, - { INDEX_op_call, { "ri" } }, /* XXX: might need a specific constant constraint */ - { INDEX_op_jmp, { "ri" } }, /* XXX: might need a specific constant constraint */ - { INDEX_op_br, { } }, - - { INDEX_op_mov_i32, { "r", "r" } }, - { INDEX_op_movi_i32, { "r" } }, - { INDEX_op_ld8u_i32, { "r", "r" } }, - { INDEX_op_ld8s_i32, { "r", "r" } }, - { INDEX_op_ld16u_i32, { "r", "r" } }, - { INDEX_op_ld16s_i32, { "r", "r" } }, - { INDEX_op_ld_i32, { "r", "r" } }, - { INDEX_op_st8_i32, { "r", "r" } }, - { INDEX_op_st16_i32, { "r", "r" } }, - { INDEX_op_st_i32, { "r", "r" } }, - - { INDEX_op_add_i32, { "r", "0", "ri" } }, - { INDEX_op_mul_i32, { "r", "0", "ri" } }, - { INDEX_op_div2_i32, { "a", "d", "0", "1", "r" } }, - { INDEX_op_divu2_i32, { "a", "d", "0", "1", "r" } }, - { INDEX_op_sub_i32, { "r", "0", "ri" } }, - { INDEX_op_and_i32, { "r", "0", "ri" } }, - { INDEX_op_or_i32, { "r", "0", "ri" } }, - { INDEX_op_xor_i32, { "r", "0", "ri" } }, - - { INDEX_op_shl_i32, { "r", "0", "ci" } }, - { INDEX_op_shr_i32, { "r", "0", "ci" } }, - { INDEX_op_sar_i32, { "r", "0", "ci" } }, - { INDEX_op_rotl_i32, { "r", "0", "ci" } }, - { INDEX_op_rotr_i32, { "r", "0", "ci" } }, - - { INDEX_op_brcond_i32, { "r", "ri" } }, - - { INDEX_op_mov_i64, { "r", "r" } }, - { INDEX_op_movi_i64, { "r" } }, - { INDEX_op_ld8u_i64, { "r", "r" } }, - { INDEX_op_ld8s_i64, { "r", "r" } }, - { INDEX_op_ld16u_i64, { "r", "r" } }, - { INDEX_op_ld16s_i64, { "r", "r" } }, - { INDEX_op_ld32u_i64, { "r", "r" } }, - { INDEX_op_ld32s_i64, { "r", "r" } }, - { INDEX_op_ld_i64, { "r", "r" } }, - { INDEX_op_st8_i64, { "r", "r" } }, - { INDEX_op_st16_i64, { "r", "r" } }, - { INDEX_op_st32_i64, { "r", "r" } }, - { INDEX_op_st_i64, { "r", "r" } }, - - { INDEX_op_add_i64, { "r", "0", "re" } }, - { INDEX_op_mul_i64, { "r", "0", "re" } }, - { INDEX_op_div2_i64, { "a", "d", "0", "1", "r" } }, - { INDEX_op_divu2_i64, { "a", "d", "0", "1", "r" } }, - { INDEX_op_sub_i64, { "r", "0", "re" } }, - { INDEX_op_and_i64, { "r", "0", "reZ" } }, - { INDEX_op_or_i64, { "r", "0", "re" } }, - { INDEX_op_xor_i64, { "r", "0", "re" } }, - - { INDEX_op_shl_i64, { "r", "0", "ci" } }, - { INDEX_op_shr_i64, { "r", "0", "ci" } }, - { INDEX_op_sar_i64, { "r", "0", "ci" } }, - { INDEX_op_rotl_i64, { "r", "0", "ci" } }, - { INDEX_op_rotr_i64, { "r", "0", "ci" } }, - - { INDEX_op_brcond_i64, { "r", "re" } }, - - { INDEX_op_bswap16_i32, { "r", "0" } }, - { INDEX_op_bswap16_i64, { "r", "0" } }, - { INDEX_op_bswap32_i32, { "r", "0" } }, - { INDEX_op_bswap32_i64, { "r", "0" } }, - { INDEX_op_bswap64_i64, { "r", "0" } }, - - { INDEX_op_neg_i32, { "r", "0" } }, - { INDEX_op_neg_i64, { "r", "0" } }, - - { INDEX_op_not_i32, { "r", "0" } }, - { INDEX_op_not_i64, { "r", "0" } }, - - { INDEX_op_ext8s_i32, { "r", "r"} }, - { INDEX_op_ext16s_i32, { "r", "r"} }, - { INDEX_op_ext8s_i64, { "r", "r"} }, - { INDEX_op_ext16s_i64, { "r", "r"} }, - { INDEX_op_ext32s_i64, { "r", "r"} }, - { INDEX_op_ext8u_i32, { "r", "r"} }, - { INDEX_op_ext16u_i32, { "r", "r"} }, - { INDEX_op_ext8u_i64, { "r", "r"} }, - { INDEX_op_ext16u_i64, { "r", "r"} }, - { INDEX_op_ext32u_i64, { "r", "r"} }, - - { INDEX_op_qemu_ld8u, { "r", "L" } }, - { INDEX_op_qemu_ld8s, { "r", "L" } }, - { INDEX_op_qemu_ld16u, { "r", "L" } }, - { INDEX_op_qemu_ld16s, { "r", "L" } }, - { INDEX_op_qemu_ld32u, { "r", "L" } }, - { INDEX_op_qemu_ld32s, { "r", "L" } }, - { INDEX_op_qemu_ld64, { "r", "L" } }, - - { INDEX_op_qemu_st8, { "L", "L" } }, - { INDEX_op_qemu_st16, { "L", "L" } }, - { INDEX_op_qemu_st32, { "L", "L" } }, - { INDEX_op_qemu_st64, { "L", "L" } }, - - { -1 }, -}; - -void tcg_target_init(TCGContext *s) -{ - /* fail safe */ - if ((1 << CPU_TLB_ENTRY_BITS) != sizeof(CPUTLBEntry)) - tcg_abort(); - - tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0, 0xffff); - tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I64], 0, 0xffff); - tcg_regset_set32(tcg_target_call_clobber_regs, 0, - (1 << TCG_REG_RDI) | - (1 << TCG_REG_RSI) | - (1 << TCG_REG_RDX) | - (1 << TCG_REG_RCX) | - (1 << TCG_REG_R8) | - (1 << TCG_REG_R9) | - (1 << TCG_REG_RAX) | - (1 << TCG_REG_R10) | - (1 << TCG_REG_R11)); - - tcg_regset_clear(s->reserved_regs); - tcg_regset_set_reg(s->reserved_regs, TCG_REG_RSP); - - tcg_add_target_add_op_defs(x86_64_op_defs); -} diff -Nru qemu-kvm-0.12.5+noroms/tcg/x86_64/tcg-target.h qemu-kvm-0.14.1/tcg/x86_64/tcg-target.h --- qemu-kvm-0.12.5+noroms/tcg/x86_64/tcg-target.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/tcg/x86_64/tcg-target.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,91 +0,0 @@ -/* - * Tiny Code Generator for QEMU - * - * Copyright (c) 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. - */ -#define TCG_TARGET_X86_64 1 - -#define TCG_TARGET_REG_BITS 64 -//#define TCG_TARGET_WORDS_BIGENDIAN - -#define TCG_TARGET_NB_REGS 16 - -enum { - TCG_REG_RAX = 0, - TCG_REG_RCX, - TCG_REG_RDX, - TCG_REG_RBX, - TCG_REG_RSP, - TCG_REG_RBP, - TCG_REG_RSI, - TCG_REG_RDI, - TCG_REG_R8, - TCG_REG_R9, - TCG_REG_R10, - TCG_REG_R11, - TCG_REG_R12, - TCG_REG_R13, - TCG_REG_R14, - TCG_REG_R15, -}; - -#define TCG_CT_CONST_S32 0x100 -#define TCG_CT_CONST_U32 0x200 - -/* used for function call generation */ -#define TCG_REG_CALL_STACK TCG_REG_RSP -#define TCG_TARGET_STACK_ALIGN 16 -#define TCG_TARGET_CALL_STACK_OFFSET 0 - -/* optional instructions */ -#define TCG_TARGET_HAS_bswap16_i32 -#define TCG_TARGET_HAS_bswap16_i64 -#define TCG_TARGET_HAS_bswap32_i32 -#define TCG_TARGET_HAS_bswap32_i64 -#define TCG_TARGET_HAS_bswap64_i64 -#define TCG_TARGET_HAS_neg_i32 -#define TCG_TARGET_HAS_neg_i64 -#define TCG_TARGET_HAS_not_i32 -#define TCG_TARGET_HAS_not_i64 -#define TCG_TARGET_HAS_ext8s_i32 -#define TCG_TARGET_HAS_ext16s_i32 -#define TCG_TARGET_HAS_ext8s_i64 -#define TCG_TARGET_HAS_ext16s_i64 -#define TCG_TARGET_HAS_ext32s_i64 -#define TCG_TARGET_HAS_ext8u_i32 -#define TCG_TARGET_HAS_ext16u_i32 -#define TCG_TARGET_HAS_ext8u_i64 -#define TCG_TARGET_HAS_ext16u_i64 -#define TCG_TARGET_HAS_ext32u_i64 - -#define TCG_TARGET_HAS_rot_i32 -#define TCG_TARGET_HAS_rot_i64 - -#define TCG_TARGET_HAS_GUEST_BASE - -/* Note: must be synced with dyngen-exec.h */ -#define TCG_AREG0 TCG_REG_R14 -#define TCG_AREG1 TCG_REG_R15 -#define TCG_AREG2 TCG_REG_R12 - -static inline void flush_icache_range(unsigned long start, unsigned long stop) -{ -} diff -Nru qemu-kvm-0.12.5+noroms/tcg-runtime.c qemu-kvm-0.14.1/tcg-runtime.c --- qemu-kvm-0.12.5+noroms/tcg-runtime.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/tcg-runtime.c 2011-05-11 13:29:46.000000000 +0000 @@ -25,6 +25,30 @@ #include "tcg/tcg-runtime.h" +/* 32-bit helpers */ + +int32_t tcg_helper_div_i32(int32_t arg1, int32_t arg2) +{ + return arg1 / arg2; +} + +int32_t tcg_helper_rem_i32(int32_t arg1, int32_t arg2) +{ + return arg1 % arg2; +} + +uint32_t tcg_helper_divu_i32(uint32_t arg1, uint32_t arg2) +{ + return arg1 / arg2; +} + +uint32_t tcg_helper_remu_i32(uint32_t arg1, uint32_t arg2) +{ + return arg1 % arg2; +} + +/* 64-bit helpers */ + int64_t tcg_helper_shl_i64(int64_t arg1, int64_t arg2) { return arg1 << arg2; diff -Nru qemu-kvm-0.12.5+noroms/tests/cris/check_abs.c qemu-kvm-0.14.1/tests/cris/check_abs.c --- qemu-kvm-0.12.5+noroms/tests/cris/check_abs.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/tests/cris/check_abs.c 2011-05-11 13:29:46.000000000 +0000 @@ -4,13 +4,14 @@ #include "sys.h" #include "crisutils.h" -extern inline int cris_abs(int n) { +static inline int cris_abs(int n) +{ int r; asm ("abs\t%1, %0\n" : "=r" (r) : "r" (n)); return r; } -extern inline void +static inline void verify_abs(int val, int res, const int n, const int z, const int v, const int c) { diff -Nru qemu-kvm-0.12.5+noroms/tests/cris/check_addc.c qemu-kvm-0.14.1/tests/cris/check_addc.c --- qemu-kvm-0.12.5+noroms/tests/cris/check_addc.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/tests/cris/check_addc.c 2011-05-11 13:29:46.000000000 +0000 @@ -4,7 +4,8 @@ #include "sys.h" #include "crisutils.h" -extern inline int cris_addc(int a, const int b) { +static inline int cris_addc(int a, const int b) +{ asm ("addc\t%1, %0\n" : "+r" (a) : "r" (b)); return a; } diff -Nru qemu-kvm-0.12.5+noroms/tests/cris/check_addcm.c qemu-kvm-0.14.1/tests/cris/check_addcm.c --- qemu-kvm-0.12.5+noroms/tests/cris/check_addcm.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/tests/cris/check_addcm.c 2011-05-11 13:29:46.000000000 +0000 @@ -5,13 +5,15 @@ #include "crisutils.h" /* need to avoid acr as source here. */ -extern inline int cris_addc_m(int a, const int *b) { +static inline int cris_addc_m(int a, const int *b) +{ asm volatile ("addc [%1], %0\n" : "+r" (a) : "r" (b)); return a; } /* 'b' is a crisv32 constrain to avoid postinc with $acr. */ -extern inline int cris_addc_pi_m(int a, int **b) { +static inline int cris_addc_pi_m(int a, int **b) +{ asm volatile ("addc [%1+], %0\n" : "+r" (a), "+b" (*b)); return a; } diff -Nru qemu-kvm-0.12.5+noroms/tests/cris/check_addo.c qemu-kvm-0.14.1/tests/cris/check_addo.c --- qemu-kvm-0.12.5+noroms/tests/cris/check_addo.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/tests/cris/check_addo.c 2011-05-11 13:29:46.000000000 +0000 @@ -85,7 +85,7 @@ cris_tst_cc_init(); asm volatile ("setf\tzvnc\n"); cris_addo_pi_b(p, t); - cris_tst_cc(1, 1, 1, 1); + cris_tst_cc(0, 0, 0, 0); asm volatile ("move.d\t$acr, %0\n" : "=r" (r)); if (*(uint16_t*)r != 0xff22) err(); @@ -114,7 +114,7 @@ cris_tst_cc_init(); asm volatile ("setf\tzvnc\n"); cris_addo_pi_d(p, t); - cris_tst_cc(1, 1, 1, 1); + cris_tst_cc(0, 0, 0, 0); asm volatile ("move.d\t$acr, %0\n" : "=r" (r)); r = (void*)(((char *)r) + 76789885); if (*r != 0x55aa77ff) diff -Nru qemu-kvm-0.12.5+noroms/tests/cris/check_addoq.c qemu-kvm-0.14.1/tests/cris/check_addoq.c --- qemu-kvm-0.12.5+noroms/tests/cris/check_addoq.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/tests/cris/check_addoq.c 2011-05-11 13:29:46.000000000 +0000 @@ -27,7 +27,7 @@ cris_tst_cc_init(); asm volatile ("setf\tzvnc\n"); cris_addoq(4, t); - cris_tst_cc(1, 1, 1, 1); + cris_tst_cc(0, 0, 0, 0); asm volatile ("move.d\t$acr, %0\n" : "=r" (p)); if (*p != 0x88ccee19) err(); diff -Nru qemu-kvm-0.12.5+noroms/tests/cris/check_bound.c qemu-kvm-0.14.1/tests/cris/check_bound.c --- qemu-kvm-0.12.5+noroms/tests/cris/check_bound.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/tests/cris/check_bound.c 2011-05-11 13:29:46.000000000 +0000 @@ -4,19 +4,22 @@ #include "sys.h" #include "crisutils.h" -extern inline int cris_bound_b(int v, int b) { +static inline int cris_bound_b(int v, int b) +{ int r = v; asm ("bound.b\t%1, %0\n" : "+r" (r) : "ri" (b)); return r; } -extern inline int cris_bound_w(int v, int b) { +static inline int cris_bound_w(int v, int b) +{ int r = v; asm ("bound.w\t%1, %0\n" : "+r" (r) : "ri" (b)); return r; } -extern inline int cris_bound_d(int v, int b) { +static inline int cris_bound_d(int v, int b) +{ int r = v; asm ("bound.d\t%1, %0\n" : "+r" (r) : "ri" (b)); return r; diff -Nru qemu-kvm-0.12.5+noroms/tests/cris/check_ftag.c qemu-kvm-0.14.1/tests/cris/check_ftag.c --- qemu-kvm-0.12.5+noroms/tests/cris/check_ftag.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/tests/cris/check_ftag.c 2011-05-11 13:29:46.000000000 +0000 @@ -4,19 +4,23 @@ #include "sys.h" #include "crisutils.h" -extern inline void cris_ftag_i(unsigned int x) { +static inline void cris_ftag_i(unsigned int x) +{ register unsigned int v asm("$r10") = x; asm ("ftagi\t[%0]\n" : : "r" (v) ); } -extern inline void cris_ftag_d(unsigned int x) { +static inline void cris_ftag_d(unsigned int x) +{ register unsigned int v asm("$r10") = x; asm ("ftagd\t[%0]\n" : : "r" (v) ); } -extern inline void cris_fidx_i(unsigned int x) { +static inline void cris_fidx_i(unsigned int x) +{ register unsigned int v asm("$r10") = x; asm ("fidxi\t[%0]\n" : : "r" (v) ); } -extern inline void cris_fidx_d(unsigned int x) { +static inline void cris_fidx_d(unsigned int x) +{ register unsigned int v asm("$r10") = x; asm ("fidxd\t[%0]\n" : : "r" (v) ); } diff -Nru qemu-kvm-0.12.5+noroms/tests/cris/check_int64.c qemu-kvm-0.14.1/tests/cris/check_int64.c --- qemu-kvm-0.12.5+noroms/tests/cris/check_int64.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/tests/cris/check_int64.c 2011-05-11 13:29:46.000000000 +0000 @@ -5,11 +5,13 @@ #include "crisutils.h" -extern inline int64_t add64(const int64_t a, const int64_t b) { +static inline int64_t add64(const int64_t a, const int64_t b) +{ return a + b; } -extern inline int64_t sub64(const int64_t a, const int64_t b) { +static inline int64_t sub64(const int64_t a, const int64_t b) +{ return a - b; } diff -Nru qemu-kvm-0.12.5+noroms/tests/cris/check_lz.c qemu-kvm-0.14.1/tests/cris/check_lz.c --- qemu-kvm-0.12.5+noroms/tests/cris/check_lz.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/tests/cris/check_lz.c 2011-05-11 13:29:46.000000000 +0000 @@ -3,7 +3,7 @@ #include #include "sys.h" -extern inline int cris_lz(int x) +static inline int cris_lz(int x) { int r; asm ("lz\t%1, %0\n" : "=r" (r) : "r" (x)); diff -Nru qemu-kvm-0.12.5+noroms/tests/cris/check_settls1.c qemu-kvm-0.14.1/tests/cris/check_settls1.c --- qemu-kvm-0.12.5+noroms/tests/cris/check_settls1.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/tests/cris/check_settls1.c 2011-05-11 13:29:46.000000000 +0000 @@ -11,11 +11,15 @@ int main (void) { - unsigned long tp; + unsigned long tp, old_tp; int ret; + asm volatile ("move $pid,%0" : "=r" (old_tp)); + old_tp &= ~0xff; + ret = syscall (SYS_set_thread_area, 0xf0); if (ret != -1 || errno != EINVAL) { + syscall (SYS_set_thread_area, old_tp); perror ("Invalid thread area accepted:"); abort(); } @@ -26,10 +30,12 @@ abort (); } - asm ("move $pid,%0" : "=r" (tp)); + asm volatile ("move $pid,%0" : "=r" (tp)); tp &= ~0xff; + syscall (SYS_set_thread_area, old_tp); if (tp != 0xeddeed00) { + * (volatile int *) 0 = 0; perror ("tls2"); abort (); } diff -Nru qemu-kvm-0.12.5+noroms/tests/cris/check_swap.c qemu-kvm-0.14.1/tests/cris/check_swap.c --- qemu-kvm-0.12.5+noroms/tests/cris/check_swap.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/tests/cris/check_swap.c 2011-05-11 13:29:46.000000000 +0000 @@ -9,7 +9,7 @@ #define B 2 #define R 1 -extern inline int cris_swap(const int mode, int x) +static inline int cris_swap(const int mode, int x) { switch (mode) { @@ -41,7 +41,7 @@ cris_tst_mov_cc(n, z); \ if (r != expected) \ err(); \ -} while(0); +} while(0) void check_swap(void) { diff -Nru qemu-kvm-0.12.5+noroms/tests/cris/crisutils.h qemu-kvm-0.14.1/tests/cris/crisutils.h --- qemu-kvm-0.12.5+noroms/tests/cris/crisutils.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/tests/cris/crisutils.h 2011-05-11 13:29:46.000000000 +0000 @@ -10,57 +10,57 @@ _fail(tst_cc_loc); } -extern inline void cris_tst_cc_n1(void) +static inline void cris_tst_cc_n1(void) { asm volatile ("bpl _err\n" "nop\n"); } -extern inline void cris_tst_cc_n0(void) +static inline void cris_tst_cc_n0(void) { asm volatile ("bmi _err\n" "nop\n"); } -extern inline void cris_tst_cc_z1(void) +static inline void cris_tst_cc_z1(void) { asm volatile ("bne _err\n" "nop\n"); } -extern inline void cris_tst_cc_z0(void) +static inline void cris_tst_cc_z0(void) { asm volatile ("beq _err\n" "nop\n"); } -extern inline void cris_tst_cc_v1(void) +static inline void cris_tst_cc_v1(void) { asm volatile ("bvc _err\n" "nop\n"); } -extern inline void cris_tst_cc_v0(void) +static inline void cris_tst_cc_v0(void) { asm volatile ("bvs _err\n" "nop\n"); } -extern inline void cris_tst_cc_c1(void) +static inline void cris_tst_cc_c1(void) { asm volatile ("bcc _err\n" "nop\n"); } -extern inline void cris_tst_cc_c0(void) +static inline void cris_tst_cc_c0(void) { asm volatile ("bcs _err\n" "nop\n"); } -extern inline void cris_tst_mov_cc(int n, int z) +static inline void cris_tst_mov_cc(int n, int z) { if (n) cris_tst_cc_n1(); else cris_tst_cc_n0(); if (z) cris_tst_cc_z1(); else cris_tst_cc_z0(); asm volatile ("" : : "g" (_err)); } -extern inline void cris_tst_cc(const int n, const int z, +static inline void cris_tst_cc(const int n, const int z, const int v, const int c) { if (n) cris_tst_cc_n1(); else cris_tst_cc_n0(); diff -Nru qemu-kvm-0.12.5+noroms/tests/cris/sys.h qemu-kvm-0.14.1/tests/cris/sys.h --- qemu-kvm-0.12.5+noroms/tests/cris/sys.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/tests/cris/sys.h 2011-05-11 13:29:46.000000000 +0000 @@ -12,5 +12,5 @@ #define mb() asm volatile ("" : : : "memory") -extern void pass(void); -extern void _fail(char *reason); +void pass(void); +void _fail(char *reason); diff -Nru qemu-kvm-0.12.5+noroms/tests/hello-i386.c qemu-kvm-0.14.1/tests/hello-i386.c --- qemu-kvm-0.12.5+noroms/tests/hello-i386.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/tests/hello-i386.c 2011-05-11 13:29:46.000000000 +0000 @@ -1,6 +1,6 @@ #include -extern inline volatile void exit(int status) +static inline volatile void exit(int status) { int __res; __asm__ volatile ("movl %%ecx,%%ebx\n"\ @@ -8,7 +8,7 @@ : "=a" (__res) : "0" (__NR_exit),"c" ((long)(status))); } -extern inline int write(int fd, const char * buf, int len) +static inline int write(int fd, const char * buf, int len) { int status; __asm__ volatile ("pushl %%ebx\n"\ diff -Nru qemu-kvm-0.12.5+noroms/tests/Makefile qemu-kvm-0.14.1/tests/Makefile --- qemu-kvm-0.12.5+noroms/tests/Makefile 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/tests/Makefile 2011-05-11 13:29:46.000000000 +0000 @@ -1,82 +1,121 @@ -include ../config-host.mak -VPATH=$(SRC_PATH)/tests +-include $(SRC_PATH)/rules.mak +$(call set-vpath, $(SRC_PATH)/tests) + +QEMU=../i386-linux-user/qemu-i386 +QEMU_X86_64=../x86_64-linux-user/qemu-x86_64 +CC_X86_64=$(CC_I386) -m64 + +QEMU_INCLUDES += -I.. CFLAGS=-Wall -O2 -g -fno-strict-aliasing #CFLAGS+=-msse2 LDFLAGS= -ifeq ($(ARCH),i386) -TESTS=linux-test testthread sha1-i386 test-i386 +# TODO: automatically detect ARM and MIPS compilers, and run those too + +# runcom maps page 0, so it requires root privileges +# also, pi_10.com runs indefinitely + +I386_TESTS=hello-i386 \ + linux-test \ + testthread \ + sha1-i386 \ + test-i386 \ + test-mmap \ + # runcom + +# native i386 compilers sometimes are not biarch. assume cross-compilers are +ifneq ($(ARCH),i386) +I386_TESTS+=run-test-x86_64 endif -ifeq ($(ARCH),x86_64) -TESTS=test-x86_64 + +TESTS = test_path +ifneq ($(call find-in-path, $(CC_I386)),) +TESTS += $(I386_TESTS) endif -TESTS+=sha1# test_path -#TESTS+=test_path -#TESTS+=runcom -QEMU=../i386-linux-user/qemu-i386 +all: $(patsubst %,run-%,$(TESTS)) + +# rules to run tests + +.PHONY: $(patsubst %,run-%,$(TESTS)) + +run-%: % + -$(QEMU) ./$* + +run-hello-i386: hello-i386 +run-linux-test: linux-test +run-testthread: testthread +run-sha1-i386: sha1-i386 + +run-test-i386: test-i386 + ./test-i386 > test-i386.ref + -$(QEMU) test-i386 > test-i386.out + @if diff -u test-i386.ref test-i386.out ; then echo "Auto Test OK"; fi + +run-test-x86_64: test-x86_64 + ./test-x86_64 > test-x86_64.ref + -$(QEMU_X86_64) test-x86_64 > test-x86_64.out + @if diff -u test-x86_64.ref test-x86_64.out ; then echo "Auto Test OK"; fi + +run-test-mmap: test-mmap + -$(QEMU) ./test-mmap + -$(QEMU) -p 8192 ./test-mmap 8192 + -$(QEMU) -p 16384 ./test-mmap 16384 + -$(QEMU) -p 32768 ./test-mmap 32768 + +run-runcom: runcom + -$(QEMU) ./runcom $(SRC_PATH)/tests/pi_10.com + +run-test_path: test_path + ./test_path + +# rules to compile tests -all: $(TESTS) +test_path: test_path.o +test_path.o: test_path.c hello-i386: hello-i386.c - $(CC) -nostdlib $(CFLAGS) -static $(LDFLAGS) -o $@ $< + $(CC_I386) -nostdlib $(CFLAGS) -static $(LDFLAGS) -o $@ $< strip $@ testthread: testthread.c - $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< -lpthread - -test_path: test_path.c - $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< - ./$@ || { rm $@; exit 1; } + $(CC_I386) $(CFLAGS) $(LDFLAGS) -o $@ $< -lpthread # i386/x86_64 emulation test (test various opcodes) */ test-i386: test-i386.c test-i386-code16.S test-i386-vm86.S \ test-i386.h test-i386-shift.h test-i386-muldiv.h - $(CC) -m32 $(CFLAGS) $(LDFLAGS) -static -o $@ \ + $(CC_I386) $(CFLAGS) $(LDFLAGS) -o $@ \ $( test-i386.ref -else -test: -endif - $(QEMU) test-i386 > test-i386.out - @if diff -u test-i386.ref test-i386.out ; then echo "Auto Test OK"; fi - -.PHONY: test-mmap -test-mmap: test-mmap.c - $(CC) $(CFLAGS) -Wall -static -O2 $(LDFLAGS) -o $@ $< - -./test-mmap - -$(QEMU) ./test-mmap - -$(QEMU) -p 8192 ./test-mmap 8192 - -$(QEMU) -p 16384 ./test-mmap 16384 - -$(QEMU) -p 32768 ./test-mmap 32768 + $(CC_X86_64) $(CFLAGS) $(LDFLAGS) -o $@ $( #include -//#define SIGTEST +extern int vm86 (unsigned long int subfunction, + struct vm86plus_struct *info); -#undef __syscall_return -#define __syscall_return(type, res) \ -do { \ - return (type) (res); \ -} while (0) +#define VIF_MASK 0x00080000 -_syscall2(int, vm86, int, func, struct vm86plus_struct *, v86) +//#define SIGTEST #define COM_BASE_ADDR 0x10100 -void usage(void) +static void usage(void) { printf("runcom version 0.1 (c) 2003 Fabrice Bellard\n" "usage: runcom file.com\n" diff -Nru qemu-kvm-0.12.5+noroms/tests/sha1.c qemu-kvm-0.14.1/tests/sha1.c --- qemu-kvm-0.12.5+noroms/tests/sha1.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/tests/sha1.c 2011-05-11 13:29:46.000000000 +0000 @@ -23,7 +23,7 @@ #include #include -#include /* for u_int*_t */ +#include /* ================ sha1.h ================ */ /* @@ -33,14 +33,14 @@ */ typedef struct { - u_int32_t state[5]; - u_int32_t count[2]; + uint32_t state[5]; + uint32_t count[2]; unsigned char buffer[64]; } SHA1_CTX; -void SHA1Transform(u_int32_t state[5], const unsigned char buffer[64]); +void SHA1Transform(uint32_t state[5], const unsigned char buffer[64]); void SHA1Init(SHA1_CTX* context); -void SHA1Update(SHA1_CTX* context, const unsigned char* data, u_int32_t len); +void SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len); void SHA1Final(unsigned char digest[20], SHA1_CTX* context); /* ================ end of sha1.h ================ */ #include @@ -70,12 +70,12 @@ /* Hash a single 512-bit block. This is the core of the algorithm. */ -void SHA1Transform(u_int32_t state[5], const unsigned char buffer[64]) +void SHA1Transform(uint32_t state[5], const unsigned char buffer[64]) { -u_int32_t a, b, c, d, e; +uint32_t a, b, c, d, e; typedef union { unsigned char c[64]; - u_int32_t l[16]; + uint32_t l[16]; } CHAR64LONG16; #ifdef SHA1HANDSOFF CHAR64LONG16 block[1]; /* use array to appear as a pointer */ @@ -145,10 +145,10 @@ /* Run your data through this. */ -void SHA1Update(SHA1_CTX* context, const unsigned char* data, u_int32_t len) +void SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len) { -u_int32_t i; -u_int32_t j; +uint32_t i; +uint32_t j; j = context->count[0]; if ((context->count[0] += len << 3) < j) @@ -186,7 +186,7 @@ for (i = 0; i < 2; i++) { - u_int32_t t = context->count[i]; + uint32_t t = context->count[i]; int j; for (j = 0; j < 4; t >>= 8, j++) diff -Nru qemu-kvm-0.12.5+noroms/tests/test-i386.c qemu-kvm-0.14.1/tests/test-i386.c --- qemu-kvm-0.12.5+noroms/tests/test-i386.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/tests/test-i386.c 2011-05-11 13:29:46.000000000 +0000 @@ -2047,6 +2047,10 @@ #define RBP "%%ebp" #endif +#if !defined(__x86_64__) +/* causes an infinite loop, disable it for now. */ +#define TEST_ENTER(size, stack_type, level) +#else #define TEST_ENTER(size, stack_type, level)\ {\ long esp_save, esp_val, ebp_val, ebp_save, i;\ @@ -2078,6 +2082,7 @@ for(ptr = (stack_type *)esp_val; ptr < stack_end; ptr++)\ printf(FMTLX "\n", (long)ptr[0]);\ } +#endif static void test_enter(void) { diff -Nru qemu-kvm-0.12.5+noroms/tests/test_path.c qemu-kvm-0.14.1/tests/test_path.c --- qemu-kvm-0.12.5+noroms/tests/test_path.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/tests/test_path.c 2011-05-11 13:29:46.000000000 +0000 @@ -1,12 +1,21 @@ /* Test path override code */ -#define _GNU_SOURCE +#include "../config-host.h" +#include "../qemu-malloc.c" +#include "../cutils.c" #include "../path.c" +#include "../trace.c" +#ifdef CONFIG_SIMPLE_TRACE +#include "../simpletrace.c" +#endif + #include #include #include +void qemu_log(const char *fmt, ...); + /* Any log message kills the test. */ -void gemu_log(const char *fmt, ...) +void qemu_log(const char *fmt, ...) { va_list ap; diff -Nru qemu-kvm-0.12.5+noroms/texi2pod.pl qemu-kvm-0.14.1/texi2pod.pl --- qemu-kvm-0.12.5+noroms/texi2pod.pl 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/texi2pod.pl 1970-01-01 00:00:00.000000000 +0000 @@ -1,477 +0,0 @@ -#! /usr/bin/perl -w - -# Copyright (C) 1999, 2000, 2001, 2003 Free Software Foundation, Inc. - -# This file is part of GCC. - -# GCC 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, or (at your option) -# any later version. - -# GCC 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 GCC; see the file COPYING. If not, -# see . - -# This does trivial (and I mean _trivial_) conversion of Texinfo -# markup to Perl POD format. It's intended to be used to extract -# something suitable for a manpage from a Texinfo document. - -$output = 0; -$skipping = 0; -%sects = (); -$section = ""; -@icstack = (); -@endwstack = (); -@skstack = (); -@instack = (); -$shift = ""; -%defs = (); -$fnno = 1; -$inf = ""; -$ibase = ""; -@ipath = (); - -while ($_ = shift) { - if (/^-D(.*)$/) { - if ($1 ne "") { - $flag = $1; - } else { - $flag = shift; - } - $value = ""; - ($flag, $value) = ($flag =~ /^([^=]+)(?:=(.+))?/); - die "no flag specified for -D\n" - unless $flag ne ""; - die "flags may only contain letters, digits, hyphens, dashes and underscores\n" - unless $flag =~ /^[a-zA-Z0-9_-]+$/; - $defs{$flag} = $value; - } elsif (/^-I(.*)$/) { - if ($1 ne "") { - $flag = $1; - } else { - $flag = shift; - } - push (@ipath, $flag); - } elsif (/^-/) { - usage(); - } else { - $in = $_, next unless defined $in; - $out = $_, next unless defined $out; - usage(); - } -} - -if (defined $in) { - $inf = gensym(); - open($inf, "<$in") or die "opening \"$in\": $!\n"; - $ibase = $1 if $in =~ m|^(.+)/[^/]+$|; -} else { - $inf = \*STDIN; -} - -if (defined $out) { - open(STDOUT, ">$out") or die "opening \"$out\": $!\n"; -} - -while(defined $inf) { -while(<$inf>) { - # Certain commands are discarded without further processing. - /^\@(?: - [a-z]+index # @*index: useful only in complete manual - |need # @need: useful only in printed manual - |(?:end\s+)?group # @group .. @end group: ditto - |page # @page: ditto - |node # @node: useful only in .info file - |(?:end\s+)?ifnottex # @ifnottex .. @end ifnottex: use contents - )\b/x and next; - - chomp; - - # Look for filename and title markers. - /^\@setfilename\s+([^.]+)/ and $fn = $1, next; - /^\@settitle\s+([^.]+)/ and $tl = postprocess($1), next; - - # Identify a man title but keep only the one we are interested in. - /^\@c\s+man\s+title\s+([A-Za-z0-9-]+)\s+(.+)/ and do { - if (exists $defs{$1}) { - $fn = $1; - $tl = postprocess($2); - } - next; - }; - - # Look for blocks surrounded by @c man begin SECTION ... @c man end. - # This really oughta be @ifman ... @end ifman and the like, but such - # would require rev'ing all other Texinfo translators. - /^\@c\s+man\s+begin\s+([A-Z]+)\s+([A-Za-z0-9-]+)/ and do { - $output = 1 if exists $defs{$2}; - $sect = $1; - next; - }; - /^\@c\s+man\s+begin\s+([A-Z]+)/ and $sect = $1, $output = 1, next; - /^\@c\s+man\s+end/ and do { - $sects{$sect} = "" unless exists $sects{$sect}; - $sects{$sect} .= postprocess($section); - $section = ""; - $output = 0; - next; - }; - - # handle variables - /^\@set\s+([a-zA-Z0-9_-]+)\s*(.*)$/ and do { - $defs{$1} = $2; - next; - }; - /^\@clear\s+([a-zA-Z0-9_-]+)/ and do { - delete $defs{$1}; - next; - }; - - next unless $output; - - # Discard comments. (Can't do it above, because then we'd never see - # @c man lines.) - /^\@c\b/ and next; - - # End-block handler goes up here because it needs to operate even - # if we are skipping. - /^\@end\s+([a-z]+)/ and do { - # Ignore @end foo, where foo is not an operation which may - # cause us to skip, if we are presently skipping. - my $ended = $1; - next if $skipping && $ended !~ /^(?:ifset|ifclear|ignore|menu|iftex|copying)$/; - - die "\@end $ended without \@$ended at line $.\n" unless defined $endw; - die "\@$endw ended by \@end $ended at line $.\n" unless $ended eq $endw; - - $endw = pop @endwstack; - - if ($ended =~ /^(?:ifset|ifclear|ignore|menu|iftex)$/) { - $skipping = pop @skstack; - next; - } elsif ($ended =~ /^(?:example|smallexample|display)$/) { - $shift = ""; - $_ = ""; # need a paragraph break - } elsif ($ended =~ /^(?:itemize|enumerate|[fv]?table)$/) { - $_ = "\n=back\n"; - $ic = pop @icstack; - } elsif ($ended eq "multitable") { - $_ = "\n=back\n"; - } else { - die "unknown command \@end $ended at line $.\n"; - } - }; - - # We must handle commands which can cause skipping even while we - # are skipping, otherwise we will not process nested conditionals - # correctly. - /^\@ifset\s+([a-zA-Z0-9_-]+)/ and do { - push @endwstack, $endw; - push @skstack, $skipping; - $endw = "ifset"; - $skipping = 1 unless exists $defs{$1}; - next; - }; - - /^\@ifclear\s+([a-zA-Z0-9_-]+)/ and do { - push @endwstack, $endw; - push @skstack, $skipping; - $endw = "ifclear"; - $skipping = 1 if exists $defs{$1}; - next; - }; - - /^\@(ignore|menu|iftex|copying)\b/ and do { - push @endwstack, $endw; - push @skstack, $skipping; - $endw = $1; - $skipping = 1; - next; - }; - - next if $skipping; - - # Character entities. First the ones that can be replaced by raw text - # or discarded outright: - s/\@copyright\{\}/(c)/g; - s/\@dots\{\}/.../g; - s/\@enddots\{\}/..../g; - s/\@([.!? ])/$1/g; - s/\@[:-]//g; - s/\@bullet(?:\{\})?/*/g; - s/\@TeX\{\}/TeX/g; - s/\@pounds\{\}/\#/g; - s/\@minus(?:\{\})?/-/g; - s/\\,/,/g; - - # Now the ones that have to be replaced by special escapes - # (which will be turned back into text by unmunge()) - s/&/&/g; - s/\@\{/{/g; - s/\@\}/}/g; - s/\@\@/&at;/g; - - # Inside a verbatim block, handle @var specially. - if ($shift ne "") { - s/\@var\{([^\}]*)\}/<$1>/g; - } - - # POD doesn't interpret E<> inside a verbatim block. - if ($shift eq "") { - s//>/g; - } else { - s//>/g; - } - - # Single line command handlers. - - /^\@include\s+(.+)$/ and do { - push @instack, $inf; - $inf = gensym(); - $file = postprocess($1); - - # Try cwd and $ibase, then explicit -I paths. - $done = 0; - foreach $path ("", $ibase, @ipath) { - $mypath = $file; - $mypath = $path . "/" . $mypath if ($path ne ""); - open($inf, "<" . $mypath) and ($done = 1, last); - } - die "cannot find $file" if !$done; - next; - }; - - /^\@(?:section|unnumbered|unnumberedsec|center)\s+(.+)$/ - and $_ = "\n=head2 $1\n"; - /^\@subsection\s+(.+)$/ - and $_ = "\n=head3 $1\n"; - /^\@subsubsection\s+(.+)$/ - and $_ = "\n=head4 $1\n"; - - # Block command handlers: - /^\@itemize(?:\s+(\@[a-z]+|\*|-))?/ and do { - push @endwstack, $endw; - push @icstack, $ic; - if (defined $1) { - $ic = $1; - } else { - $ic = '*'; - } - $_ = "\n=over 4\n"; - $endw = "itemize"; - }; - - /^\@enumerate(?:\s+([a-zA-Z0-9]+))?/ and do { - push @endwstack, $endw; - push @icstack, $ic; - if (defined $1) { - $ic = $1 . "."; - } else { - $ic = "1."; - } - $_ = "\n=over 4\n"; - $endw = "enumerate"; - }; - - /^\@multitable\s.*/ and do { - push @endwstack, $endw; - $endw = "multitable"; - $_ = "\n=over 4\n"; - }; - - /^\@([fv]?table)\s+(\@[a-z]+)/ and do { - push @endwstack, $endw; - push @icstack, $ic; - $endw = $1; - $ic = $2; - $ic =~ s/\@(?:samp|strong|key|gcctabopt|option|env)/B/; - $ic =~ s/\@(?:code|kbd)/C/; - $ic =~ s/\@(?:dfn|var|emph|cite|i)/I/; - $ic =~ s/\@(?:file)/F/; - $_ = "\n=over 4\n"; - }; - - /^\@((?:small)?example|display)/ and do { - push @endwstack, $endw; - $endw = $1; - $shift = "\t"; - $_ = ""; # need a paragraph break - }; - - /^\@item\s+(.*\S)\s*$/ and $endw eq "multitable" and do { - @columns = (); - for $column (split (/\s*\@tab\s*/, $1)) { - # @strong{...} is used a @headitem work-alike - $column =~ s/^\@strong{(.*)}$/$1/; - push @columns, $column; - } - $_ = "\n=item ".join (" : ", @columns)."\n"; - }; - - /^\@itemx?\s*(.+)?$/ and do { - if (defined $1) { - # Entity escapes prevent munging by the <> processing below. - $_ = "\n=item $ic\<$1\>\n"; - } else { - $_ = "\n=item $ic\n"; - $ic =~ y/A-Ya-y/B-Zb-z/; - $ic =~ s/(\d+)/$1 + 1/eg; - } - }; - - $section .= $shift.$_."\n"; -} -# End of current file. -close($inf); -$inf = pop @instack; -} - -die "No filename or title\n" unless defined $fn && defined $tl; - -$sects{NAME} = "$fn \- $tl\n"; -$sects{FOOTNOTES} .= "=back\n" if exists $sects{FOOTNOTES}; - -for $sect (qw(NAME SYNOPSIS DESCRIPTION OPTIONS ENVIRONMENT FILES - BUGS NOTES FOOTNOTES SEEALSO AUTHOR COPYRIGHT)) { - if(exists $sects{$sect}) { - $head = $sect; - $head =~ s/SEEALSO/SEE ALSO/; - print "=head1 $head\n\n"; - print scalar unmunge ($sects{$sect}); - print "\n"; - } -} - -sub usage -{ - die "usage: $0 [-D toggle...] [infile [outfile]]\n"; -} - -sub postprocess -{ - local $_ = $_[0]; - - # @value{foo} is replaced by whatever 'foo' is defined as. - while (m/(\@value\{([a-zA-Z0-9_-]+)\})/g) { - if (! exists $defs{$2}) { - print STDERR "Option $2 not defined\n"; - s/\Q$1\E//; - } else { - $value = $defs{$2}; - s/\Q$1\E/$value/; - } - } - - # Formatting commands. - # Temporary escape for @r. - s/\@r\{([^\}]*)\}/R<$1>/g; - s/\@(?:dfn|var|emph|cite|i)\{([^\}]*)\}/I<$1>/g; - s/\@(?:code|kbd)\{([^\}]*)\}/C<$1>/g; - s/\@(?:gccoptlist|samp|strong|key|option|env|command|b)\{([^\}]*)\}/B<$1>/g; - s/\@sc\{([^\}]*)\}/\U$1/g; - s/\@file\{([^\}]*)\}/F<$1>/g; - s/\@w\{([^\}]*)\}/S<$1>/g; - s/\@(?:dmn|math)\{([^\}]*)\}/$1/g; - - # keep references of the form @ref{...}, print them bold - s/\@(?:ref)\{([^\}]*)\}/B<$1>/g; - - # Change double single quotes to double quotes. - s/''/"/g; - s/``/"/g; - - # Cross references are thrown away, as are @noindent and @refill. - # (@noindent is impossible in .pod, and @refill is unnecessary.) - # @* is also impossible in .pod; we discard it and any newline that - # follows it. Similarly, our macro @gol must be discarded. - - s/\(?\@xref\{(?:[^\}]*)\}(?:[^.<]|(?:<[^<>]*>))*\.\)?//g; - s/\s+\(\@pxref\{(?:[^\}]*)\}\)//g; - s/;\s+\@pxref\{(?:[^\}]*)\}//g; - s/\@noindent\s*//g; - s/\@refill//g; - s/\@gol//g; - s/\@\*\s*\n?//g; - - # Anchors are thrown away - s/\@anchor\{(?:[^\}]*)\}//g; - - # @uref can take one, two, or three arguments, with different - # semantics each time. @url and @email are just like @uref with - # one argument, for our purposes. - s/\@(?:uref|url|email)\{([^\},]*)\}/<B<$1>>/g; - s/\@uref\{([^\},]*),([^\},]*)\}/$2 (C<$1>)/g; - s/\@uref\{([^\},]*),([^\},]*),([^\},]*)\}/$3/g; - - # Un-escape <> at this point. - s/<//g; - - # Now un-nest all B<>, I<>, R<>. Theoretically we could have - # indefinitely deep nesting; in practice, one level suffices. - 1 while s/([BIR])<([^<>]*)([BIR])<([^<>]*)>/$1<$2>$3<$4>$1 with bare ...; eliminate empty markup, B<>; - # shift white space at the ends of [BI]<...> expressions outside - # the expression. - s/R<([^<>]*)>/$1/g; - s/[BI]<>//g; - s/([BI])<(\s+)([^>]+)>/$2$1<$3>/g; - s/([BI])<([^>]+?)(\s+)>/$1<$2>$3/g; - - # Extract footnotes. This has to be done after all other - # processing because otherwise the regexp will choke on formatting - # inside @footnote. - while (/\@footnote/g) { - s/\@footnote\{([^\}]+)\}/[$fnno]/; - add_footnote($1, $fnno); - $fnno++; - } - - return $_; -} - -sub unmunge -{ - # Replace escaped symbols with their equivalents. - local $_ = $_[0]; - - s/</E/g; - s/>/E/g; - s/{/\{/g; - s/}/\}/g; - s/&at;/\@/g; - s/&/&/g; - return $_; -} - -sub add_footnote -{ - unless (exists $sects{FOOTNOTES}) { - $sects{FOOTNOTES} = "\n=over 4\n\n"; - } - - $sects{FOOTNOTES} .= "=item $fnno.\n\n"; $fnno++; - $sects{FOOTNOTES} .= $_[0]; - $sects{FOOTNOTES} .= "\n\n"; -} - -# stolen from Symbol.pm -{ - my $genseq = 0; - sub gensym - { - my $name = "GEN" . $genseq++; - my $ref = \*{$name}; - delete $::{$name}; - return $ref; - } -} diff -Nru qemu-kvm-0.12.5+noroms/trace-events qemu-kvm-0.14.1/trace-events --- qemu-kvm-0.12.5+noroms/trace-events 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/trace-events 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,256 @@ +# Trace events for debugging and performance instrumentation +# +# This file is processed by the tracetool script during the build. +# +# To add a new trace event: +# +# 1. Choose a name for the trace event. Declare its arguments and format +# string. +# +# 2. Call the trace event from code using trace_##name, e.g. multiwrite_cb() -> +# trace_multiwrite_cb(). The source file must #include "trace.h". +# +# Format of a trace event: +# +# [disable] ( [, ] ...) "" +# +# Example: qemu_malloc(size_t size) "size %zu" +# +# The "disable" keyword will build without the trace event. +# In case of 'simple' trace backend, it will allow the trace event to be +# compiled, but this would be turned off by default. It can be toggled on via +# the monitor. +# +# The must be a valid as a C function name. +# +# Types should be standard C types. Use void * for pointers because the trace +# system may not have the necessary headers included. +# +# The should be a sprintf()-compatible format string. + +# qemu-malloc.c +disable qemu_malloc(size_t size, void *ptr) "size %zu ptr %p" +disable qemu_realloc(void *ptr, size_t size, void *newptr) "ptr %p size %zu newptr %p" +disable qemu_free(void *ptr) "ptr %p" + +# osdep.c +disable qemu_memalign(size_t alignment, size_t size, void *ptr) "alignment %zu size %zu ptr %p" +disable qemu_vmalloc(size_t size, void *ptr) "size %zu ptr %p" +disable qemu_vfree(void *ptr) "ptr %p" + +# hw/virtio.c +disable virtqueue_fill(void *vq, const void *elem, unsigned int len, unsigned int idx) "vq %p elem %p len %u idx %u" +disable virtqueue_flush(void *vq, unsigned int count) "vq %p count %u" +disable virtqueue_pop(void *vq, void *elem, unsigned int in_num, unsigned int out_num) "vq %p elem %p in_num %u out_num %u" +disable virtio_queue_notify(void *vdev, int n, void *vq) "vdev %p n %d vq %p" +disable virtio_irq(void *vq) "vq %p" +disable virtio_notify(void *vdev, void *vq) "vdev %p vq %p" + +# block.c +disable multiwrite_cb(void *mcb, int ret) "mcb %p ret %d" +disable bdrv_aio_multiwrite(void *mcb, int num_callbacks, int num_reqs) "mcb %p num_callbacks %d num_reqs %d" +disable bdrv_aio_multiwrite_earlyfail(void *mcb) "mcb %p" +disable bdrv_aio_multiwrite_latefail(void *mcb, int i) "mcb %p i %d" +disable bdrv_aio_readv(void *bs, int64_t sector_num, int nb_sectors, void *opaque) "bs %p sector_num %"PRId64" nb_sectors %d opaque %p" +disable bdrv_aio_writev(void *bs, int64_t sector_num, int nb_sectors, void *opaque) "bs %p sector_num %"PRId64" nb_sectors %d opaque %p" + +# hw/virtio-blk.c +disable virtio_blk_req_complete(void *req, int status) "req %p status %d" +disable virtio_blk_rw_complete(void *req, int ret) "req %p ret %d" +disable virtio_blk_handle_write(void *req, uint64_t sector, size_t nsectors) "req %p sector %"PRIu64" nsectors %zu" + +# posix-aio-compat.c +disable paio_submit(void *acb, void *opaque, int64_t sector_num, int nb_sectors, int type) "acb %p opaque %p sector_num %"PRId64" nb_sectors %d type %d" + +# ioport.c +disable cpu_in(unsigned int addr, unsigned int val) "addr %#x value %u" +disable cpu_out(unsigned int addr, unsigned int val) "addr %#x value %u" + +# balloon.c +# Since requests are raised via monitor, not many tracepoints are needed. +disable balloon_event(void *opaque, unsigned long addr) "opaque %p addr %lu" + +# hw/apic.c +disable apic_local_deliver(int vector, uint32_t lvt) "vector %d delivery mode %d" +disable apic_deliver_irq(uint8_t dest, uint8_t dest_mode, uint8_t delivery_mode, uint8_t vector_num, uint8_t polarity, uint8_t trigger_mode) "dest %d dest_mode %d delivery_mode %d vector %d polarity %d trigger_mode %d" +disable cpu_set_apic_base(uint64_t val) "%016"PRIx64"" +disable cpu_get_apic_base(uint64_t val) "%016"PRIx64"" +disable apic_mem_readl(uint64_t addr, uint32_t val) "%"PRIx64" = %08x" +disable apic_mem_writel(uint64_t addr, uint32_t val) "%"PRIx64" = %08x" +# coalescing +disable apic_reset_irq_delivered(int apic_irq_delivered) "old coalescing %d" +disable apic_get_irq_delivered(int apic_irq_delivered) "returning coalescing %d" +disable apic_set_irq(int apic_irq_delivered) "coalescing %d" + +# hw/cs4231.c +disable cs4231_mem_readl_dreg(uint32_t reg, uint32_t ret) "read dreg %d: 0x%02x" +disable cs4231_mem_readl_reg(uint32_t reg, uint32_t ret) "read reg %d: 0x%08x" +disable cs4231_mem_writel_reg(uint32_t reg, uint32_t old, uint32_t val) "write reg %d: 0x%08x -> 0x%08x" +disable cs4231_mem_writel_dreg(uint32_t reg, uint32_t old, uint32_t val) "write dreg %d: 0x%02x -> 0x%02x" + +# hw/eccmemctl.c +disable ecc_mem_writel_mer(uint32_t val) "Write memory enable %08x" +disable ecc_mem_writel_mdr(uint32_t val) "Write memory delay %08x" +disable ecc_mem_writel_mfsr(uint32_t val) "Write memory fault status %08x" +disable ecc_mem_writel_vcr(uint32_t val) "Write slot configuration %08x" +disable ecc_mem_writel_dr(uint32_t val) "Write diagnostic %08x" +disable ecc_mem_writel_ecr0(uint32_t val) "Write event count 1 %08x" +disable ecc_mem_writel_ecr1(uint32_t val) "Write event count 2 %08x" +disable ecc_mem_readl_mer(uint32_t ret) "Read memory enable %08x" +disable ecc_mem_readl_mdr(uint32_t ret) "Read memory delay %08x" +disable ecc_mem_readl_mfsr(uint32_t ret) "Read memory fault status %08x" +disable ecc_mem_readl_vcr(uint32_t ret) "Read slot configuration %08x" +disable ecc_mem_readl_mfar0(uint32_t ret) "Read memory fault address 0 %08x" +disable ecc_mem_readl_mfar1(uint32_t ret) "Read memory fault address 1 %08x" +disable ecc_mem_readl_dr(uint32_t ret) "Read diagnostic %08x" +disable ecc_mem_readl_ecr0(uint32_t ret) "Read event count 1 %08x" +disable ecc_mem_readl_ecr1(uint32_t ret) "Read event count 2 %08x" +disable ecc_diag_mem_writeb(uint64_t addr, uint32_t val) "Write diagnostic %"PRId64" = %02x" +disable ecc_diag_mem_readb(uint64_t addr, uint32_t ret) "Read diagnostic %"PRId64"= %02x" + +# hw/lance.c +disable lance_mem_readw(uint64_t addr, uint32_t ret) "addr=%"PRIx64"val=0x%04x" +disable lance_mem_writew(uint64_t addr, uint32_t val) "addr=%"PRIx64"val=0x%04x" + +# hw/slavio_intctl.c +disable slavio_intctl_mem_readl(uint32_t cpu, uint64_t addr, uint32_t ret) "read cpu %d reg 0x%"PRIx64" = %x" +disable slavio_intctl_mem_writel(uint32_t cpu, uint64_t addr, uint32_t val) "write cpu %d reg 0x%"PRIx64" = %x" +disable slavio_intctl_mem_writel_clear(uint32_t cpu, uint32_t val, uint32_t intreg_pending) "Cleared cpu %d irq mask %x, curmask %x" +disable slavio_intctl_mem_writel_set(uint32_t cpu, uint32_t val, uint32_t intreg_pending) "Set cpu %d irq mask %x, curmask %x" +disable slavio_intctlm_mem_readl(uint64_t addr, uint32_t ret) "read system reg 0x%"PRIx64" = %x" +disable slavio_intctlm_mem_writel(uint64_t addr, uint32_t val) "write system reg 0x%"PRIx64" = %x" +disable slavio_intctlm_mem_writel_enable(uint32_t val, uint32_t intregm_disabled) "Enabled master irq mask %x, curmask %x" +disable slavio_intctlm_mem_writel_disable(uint32_t val, uint32_t intregm_disabled) "Disabled master irq mask %x, curmask %x" +disable slavio_intctlm_mem_writel_target(uint32_t cpu) "Set master irq cpu %d" +disable slavio_check_interrupts(uint32_t pending, uint32_t intregm_disabled) "pending %x disabled %x" +disable slavio_set_irq(uint32_t target_cpu, int irq, uint32_t pil, int level) "Set cpu %d irq %d -> pil %d level %d" +disable slavio_set_timer_irq_cpu(int cpu, int level) "Set cpu %d local timer level %d" + +# hw/slavio_misc.c +disable slavio_misc_update_irq_raise(void) "Raise IRQ" +disable slavio_misc_update_irq_lower(void) "Lower IRQ" +disable slavio_set_power_fail(int power_failing, uint8_t config) "Power fail: %d, config: %d" +disable slavio_cfg_mem_writeb(uint32_t val) "Write config %02x" +disable slavio_cfg_mem_readb(uint32_t ret) "Read config %02x" +disable slavio_diag_mem_writeb(uint32_t val) "Write diag %02x" +disable slavio_diag_mem_readb(uint32_t ret) "Read diag %02x" +disable slavio_mdm_mem_writeb(uint32_t val) "Write modem control %02x" +disable slavio_mdm_mem_readb(uint32_t ret) "Read modem control %02x" +disable slavio_aux1_mem_writeb(uint32_t val) "Write aux1 %02x" +disable slavio_aux1_mem_readb(uint32_t ret) "Read aux1 %02x" +disable slavio_aux2_mem_writeb(uint32_t val) "Write aux2 %02x" +disable slavio_aux2_mem_readb(uint32_t ret) "Read aux2 %02x" +disable apc_mem_writeb(uint32_t val) "Write power management %02x" +disable apc_mem_readb(uint32_t ret) "Read power management %02x" +disable slavio_sysctrl_mem_writel(uint32_t val) "Write system control %08x" +disable slavio_sysctrl_mem_readl(uint32_t ret) "Read system control %08x" +disable slavio_led_mem_writew(uint32_t val) "Write diagnostic LED %04x" +disable slavio_led_mem_readw(uint32_t ret) "Read diagnostic LED %04x" + +# hw/slavio_timer.c +disable slavio_timer_get_out(uint64_t limit, uint32_t counthigh, uint32_t count) "limit %"PRIx64" count %x%08x" +disable slavio_timer_irq(uint32_t counthigh, uint32_t count) "callback: count %x%08x" +disable slavio_timer_mem_readl_invalid(uint64_t addr) "invalid read address %"PRIx64"" +disable slavio_timer_mem_readl(uint64_t addr, uint32_t ret) "read %"PRIx64" = %08x" +disable slavio_timer_mem_writel(uint64_t addr, uint32_t val) "write %"PRIx64" = %08x" +disable slavio_timer_mem_writel_limit(unsigned int timer_index, uint64_t count) "processor %d user timer set to %016"PRIx64"" +disable slavio_timer_mem_writel_counter_invalid(void) "not user timer" +disable slavio_timer_mem_writel_status_start(unsigned int timer_index) "processor %d user timer started" +disable slavio_timer_mem_writel_status_stop(unsigned int timer_index) "processor %d user timer stopped" +disable slavio_timer_mem_writel_mode_user(unsigned int timer_index) "processor %d changed from counter to user timer" +disable slavio_timer_mem_writel_mode_counter(unsigned int timer_index) "processor %d changed from user timer to counter" +disable slavio_timer_mem_writel_mode_invalid(void) "not system timer" +disable slavio_timer_mem_writel_invalid(uint64_t addr) "invalid write address %"PRIx64"" + +# hw/sparc32_dma.c +disable ledma_memory_read(uint64_t addr) "DMA read addr 0x%"PRIx64"" +disable ledma_memory_write(uint64_t addr) "DMA write addr 0x%"PRIx64"" +disable sparc32_dma_set_irq_raise(void) "Raise IRQ" +disable sparc32_dma_set_irq_lower(void) "Lower IRQ" +disable espdma_memory_read(uint32_t addr) "DMA read addr 0x%08x" +disable espdma_memory_write(uint32_t addr) "DMA write addr 0x%08x" +disable sparc32_dma_mem_readl(uint64_t addr, uint32_t ret) "read dmareg %"PRIx64": 0x%08x" +disable sparc32_dma_mem_writel(uint64_t addr, uint32_t old, uint32_t val) "write dmareg %"PRIx64": 0x%08x -> 0x%08x" +disable sparc32_dma_enable_raise(void) "Raise DMA enable" +disable sparc32_dma_enable_lower(void) "Lower DMA enable" + +# hw/sun4m.c +disable sun4m_cpu_interrupt(unsigned int level) "Set CPU IRQ %d" +disable sun4m_cpu_reset_interrupt(unsigned int level) "Reset CPU IRQ %d" +disable sun4m_cpu_set_irq_raise(int level) "Raise CPU IRQ %d" +disable sun4m_cpu_set_irq_lower(int level) "Lower CPU IRQ %d" + +# hw/sun4m_iommu.c +disable sun4m_iommu_mem_readl(uint64_t addr, uint32_t ret) "read reg[%"PRIx64"] = %x" +disable sun4m_iommu_mem_writel(uint64_t addr, uint32_t val) "write reg[%"PRIx64"] = %x" +disable sun4m_iommu_mem_writel_ctrl(uint64_t iostart) "iostart = %"PRIx64"" +disable sun4m_iommu_mem_writel_tlbflush(uint32_t val) "tlb flush %x" +disable sun4m_iommu_mem_writel_pgflush(uint32_t val) "page flush %x" +disable sun4m_iommu_page_get_flags(uint64_t pa, uint64_t iopte, uint32_t ret) "get flags addr %"PRIx64" => pte %"PRIx64", *pte = %x" +disable sun4m_iommu_translate_pa(uint64_t addr, uint64_t pa, uint32_t iopte) "xlate dva %"PRIx64" => pa %"PRIx64" iopte = %x" +disable sun4m_iommu_bad_addr(uint64_t addr) "bad addr %"PRIx64"" + +# hw/usb-desc.c +disable usb_desc_device(int addr, int len, int ret) "dev %d query device, len %d, ret %d" +disable usb_desc_device_qualifier(int addr, int len, int ret) "dev %d query device qualifier, len %d, ret %d" +disable usb_desc_config(int addr, int index, int len, int ret) "dev %d query config %d, len %d, ret %d" +disable usb_desc_other_speed_config(int addr, int index, int len, int ret) "dev %d query config %d, len %d, ret %d" +disable usb_desc_string(int addr, int index, int len, int ret) "dev %d query string %d, len %d, ret %d" +disable usb_set_addr(int addr) "dev %d" +disable usb_set_config(int addr, int config, int ret) "dev %d, config %d, ret %d" +disable usb_clear_device_feature(int addr, int feature, int ret) "dev %d, feature %d, ret %d" +disable usb_set_device_feature(int addr, int feature, int ret) "dev %d, feature %d, ret %d" + +# vl.c +disable vm_state_notify(int running, int reason) "running %d reason %d" + +# block/qed-l2-cache.c +disable qed_alloc_l2_cache_entry(void *l2_cache, void *entry) "l2_cache %p entry %p" +disable qed_unref_l2_cache_entry(void *entry, int ref) "entry %p ref %d" +disable qed_find_l2_cache_entry(void *l2_cache, void *entry, uint64_t offset, int ref) "l2_cache %p entry %p offset %"PRIu64" ref %d" + +# block/qed-table.c +disable qed_read_table(void *s, uint64_t offset, void *table) "s %p offset %"PRIu64" table %p" +disable qed_read_table_cb(void *s, void *table, int ret) "s %p table %p ret %d" +disable qed_write_table(void *s, uint64_t offset, void *table, unsigned int index, unsigned int n) "s %p offset %"PRIu64" table %p index %u n %u" +disable qed_write_table_cb(void *s, void *table, int flush, int ret) "s %p table %p flush %d ret %d" + +# block/qed.c +disable qed_aio_complete(void *s, void *acb, int ret) "s %p acb %p ret %d" +disable qed_aio_setup(void *s, void *acb, int64_t sector_num, int nb_sectors, void *opaque, int is_write) "s %p acb %p sector_num %"PRId64" nb_sectors %d opaque %p is_write %d" +disable qed_aio_next_io(void *s, void *acb, int ret, uint64_t cur_pos) "s %p acb %p ret %d cur_pos %"PRIu64"" +disable qed_aio_read_data(void *s, void *acb, int ret, uint64_t offset, size_t len) "s %p acb %p ret %d offset %"PRIu64" len %zu" +disable qed_aio_write_data(void *s, void *acb, int ret, uint64_t offset, size_t len) "s %p acb %p ret %d offset %"PRIu64" len %zu" +disable qed_aio_write_prefill(void *s, void *acb, uint64_t start, size_t len, uint64_t offset) "s %p acb %p start %"PRIu64" len %zu offset %"PRIu64"" +disable qed_aio_write_postfill(void *s, void *acb, uint64_t start, size_t len, uint64_t offset) "s %p acb %p start %"PRIu64" len %zu offset %"PRIu64"" +disable qed_aio_write_main(void *s, void *acb, int ret, uint64_t offset, size_t len) "s %p acb %p ret %d offset %"PRIu64" len %zu" + +# hw/grlib_gptimer.c +disable grlib_gptimer_enable(int id, uint32_t count) "timer:%d set count 0x%x and run" +disable grlib_gptimer_disabled(int id, uint32_t config) "timer:%d Timer disable config 0x%x" +disable grlib_gptimer_restart(int id, uint32_t reload) "timer:%d reload val: 0x%x" +disable grlib_gptimer_set_scaler(uint32_t scaler, uint32_t freq) "scaler:0x%x freq: 0x%x" +disable grlib_gptimer_hit(int id) "timer:%d HIT" +disable grlib_gptimer_readl(int id, const char *s, uint32_t val) "timer:%d %s 0x%x" +disable grlib_gptimer_writel(int id, const char *s, uint32_t val) "timer:%d %s 0x%x" +disable grlib_gptimer_unknown_register(const char *op, uint64_t val) "%s unknown register 0x%"PRIx64"" + +# hw/grlib_irqmp.c +disable grlib_irqmp_check_irqs(uint32_t pend, uint32_t force, uint32_t mask, uint32_t lvl1, uint32_t lvl2) "pend:0x%04x force:0x%04x mask:0x%04x lvl1:0x%04x lvl0:0x%04x\n" +disable grlib_irqmp_ack(int intno) "interrupt:%d" +disable grlib_irqmp_set_irq(int irq) "Raise CPU IRQ %d" +disable grlib_irqmp_unknown_register(const char *op, uint64_t val) "%s unknown register 0x%"PRIx64"" + +# hw/grlib_apbuart.c +disable grlib_apbuart_event(int event) "event:%d" +disable grlib_apbuart_unknown_register(const char *op, uint64_t val) "%s unknown register 0x%"PRIx64"" + +# hw/leon3.c +disable leon3_set_irq(int intno) "Set CPU IRQ %d" +disable leon3_reset_irq(int intno) "Reset CPU IRQ %d" + +# spice-qemu-char.c +disable spice_vmc_write(ssize_t out, int len) "spice wrottn %lu of requested %zd" +disable spice_vmc_read(int bytes, int len) "spice read %lu of requested %zd" +disable spice_vmc_register_interface(void *scd) "spice vmc registered interface %p" +disable spice_vmc_unregister_interface(void *scd) "spice vmc unregistered interface %p" diff -Nru qemu-kvm-0.12.5+noroms/translate-all.c qemu-kvm-0.14.1/translate-all.c --- qemu-kvm-0.12.5+noroms/translate-all.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/translate-all.c 2011-05-11 13:29:46.000000000 +0000 @@ -29,6 +29,7 @@ #include "exec-all.h" #include "disas.h" #include "tcg.h" +#include "qemu-timer.h" /* code generation context */ TCGContext tcg_ctx; @@ -39,30 +40,6 @@ target_ulong gen_opc_pc[OPC_BUF_SIZE]; uint16_t gen_opc_icount[OPC_BUF_SIZE]; uint8_t gen_opc_instr_start[OPC_BUF_SIZE]; -#if defined(TARGET_I386) -uint8_t gen_opc_cc_op[OPC_BUF_SIZE]; -#elif defined(TARGET_SPARC) -target_ulong gen_opc_npc[OPC_BUF_SIZE]; -target_ulong gen_opc_jump_pc[2]; -#elif defined(TARGET_MIPS) || defined(TARGET_SH4) -uint32_t gen_opc_hflags[OPC_BUF_SIZE]; -#endif - -/* XXX: suppress that */ -unsigned long code_gen_max_block_size(void) -{ - static unsigned long max; - - if (max == 0) { - max = TCG_MAX_OP_SIZE; -#define DEF(s, n, copy_size) max = copy_size > max? copy_size : max; -#include "tcg-opc.h" -#undef DEF - max *= OPC_MAX_SIZE; - } - - return max; -} void cpu_gen_init(void) { @@ -103,10 +80,6 @@ #ifdef USE_DIRECT_JUMP s->tb_jmp_offset = tb->tb_jmp_offset; s->tb_next = NULL; - /* the following two entries are optional (only used for string ops) */ - /* XXX: not used ? */ - tb->tb_jmp_offset[2] = 0xffff; - tb->tb_jmp_offset[3] = 0xffff; #else s->tb_jmp_offset = NULL; s->tb_next = tb->tb_next; diff -Nru qemu-kvm-0.12.5+noroms/ui/cocoa.m qemu-kvm-0.14.1/ui/cocoa.m --- qemu-kvm-0.12.5+noroms/ui/cocoa.m 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/ui/cocoa.m 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,1014 @@ +/* + * QEMU Cocoa CG display driver + * + * Copyright (c) 2008 Mike Kronenberg + * + * 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. + */ + +#import + +#include "qemu-common.h" +#include "console.h" +#include "sysemu.h" + +#ifndef MAC_OS_X_VERSION_10_4 +#define MAC_OS_X_VERSION_10_4 1040 +#endif +#ifndef MAC_OS_X_VERSION_10_5 +#define MAC_OS_X_VERSION_10_5 1050 +#endif + + +//#define DEBUG + +#ifdef DEBUG +#define COCOA_DEBUG(...) { (void) fprintf (stdout, __VA_ARGS__); } +#else +#define COCOA_DEBUG(...) ((void) 0) +#endif + +#define cgrect(nsrect) (*(CGRect *)&(nsrect)) +#define COCOA_MOUSE_EVENT \ + if (isTabletEnabled) { \ + kbd_mouse_event((int)(p.x * 0x7FFF / (screen.width - 1)), (int)((screen.height - p.y) * 0x7FFF / (screen.height - 1)), 0, buttons); \ + } else if (isMouseGrabed) { \ + kbd_mouse_event((int)[event deltaX], (int)[event deltaY], 0, buttons); \ + } else { \ + [NSApp sendEvent:event]; \ + } + +typedef struct { + int width; + int height; + int bitsPerComponent; + int bitsPerPixel; +} QEMUScreen; + +int qemu_main(int argc, char **argv); // main defined in qemu/vl.c +NSWindow *normalWindow; +id cocoaView; +static DisplayChangeListener *dcl; + +int gArgc; +char **gArgv; + +// keymap conversion +int keymap[] = +{ +// SdlI macI macH SdlH 104xtH 104xtC sdl + 30, // 0 0x00 0x1e A QZ_a + 31, // 1 0x01 0x1f S QZ_s + 32, // 2 0x02 0x20 D QZ_d + 33, // 3 0x03 0x21 F QZ_f + 35, // 4 0x04 0x23 H QZ_h + 34, // 5 0x05 0x22 G QZ_g + 44, // 6 0x06 0x2c Z QZ_z + 45, // 7 0x07 0x2d X QZ_x + 46, // 8 0x08 0x2e C QZ_c + 47, // 9 0x09 0x2f V QZ_v + 0, // 10 0x0A Undefined + 48, // 11 0x0B 0x30 B QZ_b + 16, // 12 0x0C 0x10 Q QZ_q + 17, // 13 0x0D 0x11 W QZ_w + 18, // 14 0x0E 0x12 E QZ_e + 19, // 15 0x0F 0x13 R QZ_r + 21, // 16 0x10 0x15 Y QZ_y + 20, // 17 0x11 0x14 T QZ_t + 2, // 18 0x12 0x02 1 QZ_1 + 3, // 19 0x13 0x03 2 QZ_2 + 4, // 20 0x14 0x04 3 QZ_3 + 5, // 21 0x15 0x05 4 QZ_4 + 7, // 22 0x16 0x07 6 QZ_6 + 6, // 23 0x17 0x06 5 QZ_5 + 13, // 24 0x18 0x0d = QZ_EQUALS + 10, // 25 0x19 0x0a 9 QZ_9 + 8, // 26 0x1A 0x08 7 QZ_7 + 12, // 27 0x1B 0x0c - QZ_MINUS + 9, // 28 0x1C 0x09 8 QZ_8 + 11, // 29 0x1D 0x0b 0 QZ_0 + 27, // 30 0x1E 0x1b ] QZ_RIGHTBRACKET + 24, // 31 0x1F 0x18 O QZ_o + 22, // 32 0x20 0x16 U QZ_u + 26, // 33 0x21 0x1a [ QZ_LEFTBRACKET + 23, // 34 0x22 0x17 I QZ_i + 25, // 35 0x23 0x19 P QZ_p + 28, // 36 0x24 0x1c ENTER QZ_RETURN + 38, // 37 0x25 0x26 L QZ_l + 36, // 38 0x26 0x24 J QZ_j + 40, // 39 0x27 0x28 ' QZ_QUOTE + 37, // 40 0x28 0x25 K QZ_k + 39, // 41 0x29 0x27 ; QZ_SEMICOLON + 43, // 42 0x2A 0x2b \ QZ_BACKSLASH + 51, // 43 0x2B 0x33 , QZ_COMMA + 53, // 44 0x2C 0x35 / QZ_SLASH + 49, // 45 0x2D 0x31 N QZ_n + 50, // 46 0x2E 0x32 M QZ_m + 52, // 47 0x2F 0x34 . QZ_PERIOD + 15, // 48 0x30 0x0f TAB QZ_TAB + 57, // 49 0x31 0x39 SPACE QZ_SPACE + 41, // 50 0x32 0x29 ` QZ_BACKQUOTE + 14, // 51 0x33 0x0e BKSP QZ_BACKSPACE + 0, // 52 0x34 Undefined + 1, // 53 0x35 0x01 ESC QZ_ESCAPE + 0, // 54 0x36 QZ_RMETA + 0, // 55 0x37 QZ_LMETA + 42, // 56 0x38 0x2a L SHFT QZ_LSHIFT + 58, // 57 0x39 0x3a CAPS QZ_CAPSLOCK + 56, // 58 0x3A 0x38 L ALT QZ_LALT + 29, // 59 0x3B 0x1d L CTRL QZ_LCTRL + 54, // 60 0x3C 0x36 R SHFT QZ_RSHIFT + 184,// 61 0x3D 0xb8 E0,38 R ALT QZ_RALT + 157,// 62 0x3E 0x9d E0,1D R CTRL QZ_RCTRL + 0, // 63 0x3F Undefined + 0, // 64 0x40 Undefined + 0, // 65 0x41 Undefined + 0, // 66 0x42 Undefined + 55, // 67 0x43 0x37 KP * QZ_KP_MULTIPLY + 0, // 68 0x44 Undefined + 78, // 69 0x45 0x4e KP + QZ_KP_PLUS + 0, // 70 0x46 Undefined + 69, // 71 0x47 0x45 NUM QZ_NUMLOCK + 0, // 72 0x48 Undefined + 0, // 73 0x49 Undefined + 0, // 74 0x4A Undefined + 181,// 75 0x4B 0xb5 E0,35 KP / QZ_KP_DIVIDE + 152,// 76 0x4C 0x9c E0,1C KP EN QZ_KP_ENTER + 0, // 77 0x4D undefined + 74, // 78 0x4E 0x4a KP - QZ_KP_MINUS + 0, // 79 0x4F Undefined + 0, // 80 0x50 Undefined + 0, // 81 0x51 QZ_KP_EQUALS + 82, // 82 0x52 0x52 KP 0 QZ_KP0 + 79, // 83 0x53 0x4f KP 1 QZ_KP1 + 80, // 84 0x54 0x50 KP 2 QZ_KP2 + 81, // 85 0x55 0x51 KP 3 QZ_KP3 + 75, // 86 0x56 0x4b KP 4 QZ_KP4 + 76, // 87 0x57 0x4c KP 5 QZ_KP5 + 77, // 88 0x58 0x4d KP 6 QZ_KP6 + 71, // 89 0x59 0x47 KP 7 QZ_KP7 + 0, // 90 0x5A Undefined + 72, // 91 0x5B 0x48 KP 8 QZ_KP8 + 73, // 92 0x5C 0x49 KP 9 QZ_KP9 + 0, // 93 0x5D Undefined + 0, // 94 0x5E Undefined + 0, // 95 0x5F Undefined + 63, // 96 0x60 0x3f F5 QZ_F5 + 64, // 97 0x61 0x40 F6 QZ_F6 + 65, // 98 0x62 0x41 F7 QZ_F7 + 61, // 99 0x63 0x3d F3 QZ_F3 + 66, // 100 0x64 0x42 F8 QZ_F8 + 67, // 101 0x65 0x43 F9 QZ_F9 + 0, // 102 0x66 Undefined + 87, // 103 0x67 0x57 F11 QZ_F11 + 0, // 104 0x68 Undefined + 183,// 105 0x69 0xb7 QZ_PRINT + 0, // 106 0x6A Undefined + 70, // 107 0x6B 0x46 SCROLL QZ_SCROLLOCK + 0, // 108 0x6C Undefined + 68, // 109 0x6D 0x44 F10 QZ_F10 + 0, // 110 0x6E Undefined + 88, // 111 0x6F 0x58 F12 QZ_F12 + 0, // 112 0x70 Undefined + 110,// 113 0x71 0x0 QZ_PAUSE + 210,// 114 0x72 0xd2 E0,52 INSERT QZ_INSERT + 199,// 115 0x73 0xc7 E0,47 HOME QZ_HOME + 201,// 116 0x74 0xc9 E0,49 PG UP QZ_PAGEUP + 211,// 117 0x75 0xd3 E0,53 DELETE QZ_DELETE + 62, // 118 0x76 0x3e F4 QZ_F4 + 207,// 119 0x77 0xcf E0,4f END QZ_END + 60, // 120 0x78 0x3c F2 QZ_F2 + 209,// 121 0x79 0xd1 E0,51 PG DN QZ_PAGEDOWN + 59, // 122 0x7A 0x3b F1 QZ_F1 + 203,// 123 0x7B 0xcb e0,4B L ARROW QZ_LEFT + 205,// 124 0x7C 0xcd e0,4D R ARROW QZ_RIGHT + 208,// 125 0x7D 0xd0 E0,50 D ARROW QZ_DOWN + 200,// 126 0x7E 0xc8 E0,48 U ARROW QZ_UP +/* completed according to http://www.libsdl.org/cgi/cvsweb.cgi/SDL12/src/video/quartz/SDL_QuartzKeys.h?rev=1.6&content-type=text/x-cvsweb-markup */ + +/* Aditional 104 Key XP-Keyboard Scancodes from http://www.computer-engineering.org/ps2keyboard/scancodes1.html */ +/* + 219 // 0xdb e0,5b L GUI + 220 // 0xdc e0,5c R GUI + 221 // 0xdd e0,5d APPS + // E0,2A,E0,37 PRNT SCRN + // E1,1D,45,E1,9D,C5 PAUSE + 83 // 0x53 0x53 KP . +// ACPI Scan Codes + 222 // 0xde E0, 5E Power + 223 // 0xdf E0, 5F Sleep + 227 // 0xe3 E0, 63 Wake +// Windows Multimedia Scan Codes + 153 // 0x99 E0, 19 Next Track + 144 // 0x90 E0, 10 Previous Track + 164 // 0xa4 E0, 24 Stop + 162 // 0xa2 E0, 22 Play/Pause + 160 // 0xa0 E0, 20 Mute + 176 // 0xb0 E0, 30 Volume Up + 174 // 0xae E0, 2E Volume Down + 237 // 0xed E0, 6D Media Select + 236 // 0xec E0, 6C E-Mail + 161 // 0xa1 E0, 21 Calculator + 235 // 0xeb E0, 6B My Computer + 229 // 0xe5 E0, 65 WWW Search + 178 // 0xb2 E0, 32 WWW Home + 234 // 0xea E0, 6A WWW Back + 233 // 0xe9 E0, 69 WWW Forward + 232 // 0xe8 E0, 68 WWW Stop + 231 // 0xe7 E0, 67 WWW Refresh + 230 // 0xe6 E0, 66 WWW Favorites +*/ +}; + +static int cocoa_keycode_to_qemu(int keycode) +{ + if((sizeof(keymap)/sizeof(int)) <= keycode) + { + printf("(cocoa) warning unknow keycode 0x%x\n", keycode); + return 0; + } + return keymap[keycode]; +} + + + +/* + ------------------------------------------------------ + QemuCocoaView + ------------------------------------------------------ +*/ +@interface QemuCocoaView : NSView +{ + QEMUScreen screen; + NSWindow *fullScreenWindow; + float cx,cy,cw,ch,cdx,cdy; + CGDataProviderRef dataProviderRef; + int modifiers_state[256]; + BOOL isMouseGrabed; + BOOL isFullscreen; + BOOL isAbsoluteEnabled; + BOOL isTabletEnabled; +} +- (void) resizeContentToWidth:(int)w height:(int)h displayState:(DisplayState *)ds; +- (void) grabMouse; +- (void) ungrabMouse; +- (void) toggleFullScreen:(id)sender; +- (void) handleEvent:(NSEvent *)event; +- (void) setAbsoluteEnabled:(BOOL)tIsAbsoluteEnabled; +- (BOOL) isMouseGrabed; +- (BOOL) isAbsoluteEnabled; +- (float) cdx; +- (float) cdy; +- (QEMUScreen) gscreen; +@end + +@implementation QemuCocoaView +- (id)initWithFrame:(NSRect)frameRect +{ + COCOA_DEBUG("QemuCocoaView: initWithFrame\n"); + + self = [super initWithFrame:frameRect]; + if (self) { + + screen.bitsPerComponent = 8; + screen.bitsPerPixel = 32; + screen.width = frameRect.size.width; + screen.height = frameRect.size.height; + + } + return self; +} + +- (void) dealloc +{ + COCOA_DEBUG("QemuCocoaView: dealloc\n"); + + if (dataProviderRef) + CGDataProviderRelease(dataProviderRef); + + [super dealloc]; +} + +- (BOOL) isOpaque +{ + return YES; +} + +- (void) drawRect:(NSRect) rect +{ + COCOA_DEBUG("QemuCocoaView: drawRect\n"); + + // get CoreGraphic context + CGContextRef viewContextRef = [[NSGraphicsContext currentContext] graphicsPort]; + CGContextSetInterpolationQuality (viewContextRef, kCGInterpolationNone); + CGContextSetShouldAntialias (viewContextRef, NO); + + // draw screen bitmap directly to Core Graphics context + if (dataProviderRef) { + CGImageRef imageRef = CGImageCreate( + screen.width, //width + screen.height, //height + screen.bitsPerComponent, //bitsPerComponent + screen.bitsPerPixel, //bitsPerPixel + (screen.width * (screen.bitsPerComponent/2)), //bytesPerRow +#ifdef __LITTLE_ENDIAN__ + CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB), //colorspace for OS X >= 10.4 + kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipFirst, +#else + CGColorSpaceCreateDeviceRGB(), //colorspace for OS X < 10.4 (actually ppc) + kCGImageAlphaNoneSkipFirst, //bitmapInfo +#endif + dataProviderRef, //provider + NULL, //decode + 0, //interpolate + kCGRenderingIntentDefault //intent + ); +// test if host supports "CGImageCreateWithImageInRect" at compile time +#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4) + if (CGImageCreateWithImageInRect == NULL) { // test if "CGImageCreateWithImageInRect" is supported on host at runtime +#endif + // compatibility drawing code (draws everything) (OS X < 10.4) + CGContextDrawImage (viewContextRef, CGRectMake(0, 0, [self bounds].size.width, [self bounds].size.height), imageRef); +#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4) + } else { + // selective drawing code (draws only dirty rectangles) (OS X >= 10.4) + const NSRect *rectList; +#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5) + NSInteger rectCount; +#else + int rectCount; +#endif + int i; + CGImageRef clipImageRef; + CGRect clipRect; + + [self getRectsBeingDrawn:&rectList count:&rectCount]; + for (i = 0; i < rectCount; i++) { + clipRect.origin.x = rectList[i].origin.x / cdx; + clipRect.origin.y = (float)screen.height - (rectList[i].origin.y + rectList[i].size.height) / cdy; + clipRect.size.width = rectList[i].size.width / cdx; + clipRect.size.height = rectList[i].size.height / cdy; + clipImageRef = CGImageCreateWithImageInRect( + imageRef, + clipRect + ); + CGContextDrawImage (viewContextRef, cgrect(rectList[i]), clipImageRef); + CGImageRelease (clipImageRef); + } + } +#endif + CGImageRelease (imageRef); + } +} + +- (void) setContentDimensions +{ + COCOA_DEBUG("QemuCocoaView: setContentDimensions\n"); + + if (isFullscreen) { + cdx = [[NSScreen mainScreen] frame].size.width / (float)screen.width; + cdy = [[NSScreen mainScreen] frame].size.height / (float)screen.height; + cw = screen.width * cdx; + ch = screen.height * cdy; + cx = ([[NSScreen mainScreen] frame].size.width - cw) / 2.0; + cy = ([[NSScreen mainScreen] frame].size.height - ch) / 2.0; + } else { + cx = 0; + cy = 0; + cw = screen.width; + ch = screen.height; + cdx = 1.0; + cdy = 1.0; + } +} + +- (void) resizeContentToWidth:(int)w height:(int)h displayState:(DisplayState *)ds +{ + COCOA_DEBUG("QemuCocoaView: resizeContent\n"); + + // update screenBuffer + if (dataProviderRef) + CGDataProviderRelease(dataProviderRef); + + //sync host window color space with guests + screen.bitsPerPixel = ds_get_bits_per_pixel(ds); + screen.bitsPerComponent = ds_get_bytes_per_pixel(ds) * 2; + + dataProviderRef = CGDataProviderCreateWithData(NULL, ds_get_data(ds), w * 4 * h, NULL); + + // update windows + if (isFullscreen) { + [[fullScreenWindow contentView] setFrame:[[NSScreen mainScreen] frame]]; + [normalWindow setFrame:NSMakeRect([normalWindow frame].origin.x, [normalWindow frame].origin.y - h + screen.height, w, h + [normalWindow frame].size.height - screen.height) display:NO animate:NO]; + } else { + if (qemu_name) + [normalWindow setTitle:[NSString stringWithFormat:@"QEMU %s", qemu_name]]; + [normalWindow setFrame:NSMakeRect([normalWindow frame].origin.x, [normalWindow frame].origin.y - h + screen.height, w, h + [normalWindow frame].size.height - screen.height) display:YES animate:NO]; + } + screen.width = w; + screen.height = h; + [normalWindow center]; + [self setContentDimensions]; + [self setFrame:NSMakeRect(cx, cy, cw, ch)]; +} + +- (void) toggleFullScreen:(id)sender +{ + COCOA_DEBUG("QemuCocoaView: toggleFullScreen\n"); + + if (isFullscreen) { // switch from fullscreen to desktop + isFullscreen = FALSE; + [self ungrabMouse]; + [self setContentDimensions]; +// test if host supports "exitFullScreenModeWithOptions" at compile time +#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5) + if ([NSView respondsToSelector:@selector(exitFullScreenModeWithOptions:)]) { // test if "exitFullScreenModeWithOptions" is supported on host at runtime + [self exitFullScreenModeWithOptions:nil]; + } else { +#endif + [fullScreenWindow close]; + [normalWindow setContentView: self]; + [normalWindow makeKeyAndOrderFront: self]; + [NSMenu setMenuBarVisible:YES]; +#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5) + } +#endif + } else { // switch from desktop to fullscreen + isFullscreen = TRUE; + [self grabMouse]; + [self setContentDimensions]; +// test if host supports "enterFullScreenMode:withOptions" at compile time +#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5) + if ([NSView respondsToSelector:@selector(enterFullScreenMode:withOptions:)]) { // test if "enterFullScreenMode:withOptions" is supported on host at runtime + [self enterFullScreenMode:[NSScreen mainScreen] withOptions:[NSDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithBool:NO], NSFullScreenModeAllScreens, + [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:NO], kCGDisplayModeIsStretched, nil], NSFullScreenModeSetting, + nil]]; + } else { +#endif + [NSMenu setMenuBarVisible:NO]; + fullScreenWindow = [[NSWindow alloc] initWithContentRect:[[NSScreen mainScreen] frame] + styleMask:NSBorderlessWindowMask + backing:NSBackingStoreBuffered + defer:NO]; + [fullScreenWindow setHasShadow:NO]; + [fullScreenWindow setContentView:self]; + [fullScreenWindow makeKeyAndOrderFront:self]; +#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5) + } +#endif + } +} + +- (void) handleEvent:(NSEvent *)event +{ + COCOA_DEBUG("QemuCocoaView: handleEvent\n"); + + int buttons = 0; + int keycode; + NSPoint p = [event locationInWindow]; + + switch ([event type]) { + case NSFlagsChanged: + keycode = cocoa_keycode_to_qemu([event keyCode]); + if (keycode) { + if (keycode == 58 || keycode == 69) { // emulate caps lock and num lock keydown and keyup + kbd_put_keycode(keycode); + kbd_put_keycode(keycode | 0x80); + } else if (is_graphic_console()) { + if (keycode & 0x80) + kbd_put_keycode(0xe0); + if (modifiers_state[keycode] == 0) { // keydown + kbd_put_keycode(keycode & 0x7f); + modifiers_state[keycode] = 1; + } else { // keyup + kbd_put_keycode(keycode | 0x80); + modifiers_state[keycode] = 0; + } + } + } + + // release Mouse grab when pressing ctrl+alt + if (!isFullscreen && ([event modifierFlags] & NSControlKeyMask) && ([event modifierFlags] & NSAlternateKeyMask)) { + [self ungrabMouse]; + } + break; + case NSKeyDown: + + // forward command Key Combos + if ([event modifierFlags] & NSCommandKeyMask) { + [NSApp sendEvent:event]; + return; + } + + // default + keycode = cocoa_keycode_to_qemu([event keyCode]); + + // handle control + alt Key Combos (ctrl+alt is reserved for QEMU) + if (([event modifierFlags] & NSControlKeyMask) && ([event modifierFlags] & NSAlternateKeyMask)) { + switch (keycode) { + + // enable graphic console + case 0x02 ... 0x0a: // '1' to '9' keys + console_select(keycode - 0x02); + break; + } + + // handle keys for graphic console + } else if (is_graphic_console()) { + if (keycode & 0x80) //check bit for e0 in front + kbd_put_keycode(0xe0); + kbd_put_keycode(keycode & 0x7f); //remove e0 bit in front + + // handlekeys for Monitor + } else { + int keysym = 0; + switch([event keyCode]) { + case 115: + keysym = QEMU_KEY_HOME; + break; + case 117: + keysym = QEMU_KEY_DELETE; + break; + case 119: + keysym = QEMU_KEY_END; + break; + case 123: + keysym = QEMU_KEY_LEFT; + break; + case 124: + keysym = QEMU_KEY_RIGHT; + break; + case 125: + keysym = QEMU_KEY_DOWN; + break; + case 126: + keysym = QEMU_KEY_UP; + break; + default: + { + NSString *ks = [event characters]; + if ([ks length] > 0) + keysym = [ks characterAtIndex:0]; + } + } + if (keysym) + kbd_put_keysym(keysym); + } + break; + case NSKeyUp: + keycode = cocoa_keycode_to_qemu([event keyCode]); + if (is_graphic_console()) { + if (keycode & 0x80) + kbd_put_keycode(0xe0); + kbd_put_keycode(keycode | 0x80); //add 128 to signal release of key + } + break; + case NSMouseMoved: + if (isAbsoluteEnabled) { + if (p.x < 0 || p.x > screen.width || p.y < 0 || p.y > screen.height || ![[self window] isKeyWindow]) { + if (isTabletEnabled) { // if we leave the window, deactivate the tablet + [NSCursor unhide]; + isTabletEnabled = FALSE; + } + } else { + if (!isTabletEnabled) { // if we enter the window, activate the tablet + [NSCursor hide]; + isTabletEnabled = TRUE; + } + } + } + COCOA_MOUSE_EVENT + break; + case NSLeftMouseDown: + if ([event modifierFlags] & NSCommandKeyMask) { + buttons |= MOUSE_EVENT_RBUTTON; + } else { + buttons |= MOUSE_EVENT_LBUTTON; + } + COCOA_MOUSE_EVENT + break; + case NSRightMouseDown: + buttons |= MOUSE_EVENT_RBUTTON; + COCOA_MOUSE_EVENT + break; + case NSOtherMouseDown: + buttons |= MOUSE_EVENT_MBUTTON; + COCOA_MOUSE_EVENT + break; + case NSLeftMouseDragged: + if ([event modifierFlags] & NSCommandKeyMask) { + buttons |= MOUSE_EVENT_RBUTTON; + } else { + buttons |= MOUSE_EVENT_LBUTTON; + } + COCOA_MOUSE_EVENT + break; + case NSRightMouseDragged: + buttons |= MOUSE_EVENT_RBUTTON; + COCOA_MOUSE_EVENT + break; + case NSOtherMouseDragged: + buttons |= MOUSE_EVENT_MBUTTON; + COCOA_MOUSE_EVENT + break; + case NSLeftMouseUp: + if (isTabletEnabled) { + COCOA_MOUSE_EVENT + } else if (!isMouseGrabed) { + if (p.x > -1 && p.x < screen.width && p.y > -1 && p.y < screen.height) { + [self grabMouse]; + } else { + [NSApp sendEvent:event]; + } + } else { + COCOA_MOUSE_EVENT + } + break; + case NSRightMouseUp: + COCOA_MOUSE_EVENT + break; + case NSOtherMouseUp: + COCOA_MOUSE_EVENT + break; + case NSScrollWheel: + if (isTabletEnabled || isMouseGrabed) { + kbd_mouse_event(0, 0, -[event deltaY], 0); + } else { + [NSApp sendEvent:event]; + } + break; + default: + [NSApp sendEvent:event]; + } +} + +- (void) grabMouse +{ + COCOA_DEBUG("QemuCocoaView: grabMouse\n"); + + if (!isFullscreen) { + if (qemu_name) + [normalWindow setTitle:[NSString stringWithFormat:@"QEMU %s - (Press ctrl + alt to release Mouse)", qemu_name]]; + else + [normalWindow setTitle:@"QEMU - (Press ctrl + alt to release Mouse)"]; + } + [NSCursor hide]; + CGAssociateMouseAndMouseCursorPosition(FALSE); + isMouseGrabed = TRUE; // while isMouseGrabed = TRUE, QemuCocoaApp sends all events to [cocoaView handleEvent:] +} + +- (void) ungrabMouse +{ + COCOA_DEBUG("QemuCocoaView: ungrabMouse\n"); + + if (!isFullscreen) { + if (qemu_name) + [normalWindow setTitle:[NSString stringWithFormat:@"QEMU %s", qemu_name]]; + else + [normalWindow setTitle:@"QEMU"]; + } + [NSCursor unhide]; + CGAssociateMouseAndMouseCursorPosition(TRUE); + isMouseGrabed = FALSE; +} + +- (void) setAbsoluteEnabled:(BOOL)tIsAbsoluteEnabled {isAbsoluteEnabled = tIsAbsoluteEnabled;} +- (BOOL) isMouseGrabed {return isMouseGrabed;} +- (BOOL) isAbsoluteEnabled {return isAbsoluteEnabled;} +- (float) cdx {return cdx;} +- (float) cdy {return cdy;} +- (QEMUScreen) gscreen {return screen;} +@end + + + +/* + ------------------------------------------------------ + QemuCocoaAppController + ------------------------------------------------------ +*/ +@interface QemuCocoaAppController : NSObject +{ +} +- (void)startEmulationWithArgc:(int)argc argv:(char**)argv; +- (void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo; +- (void)toggleFullScreen:(id)sender; +- (void)showQEMUDoc:(id)sender; +- (void)showQEMUTec:(id)sender; +@end + +@implementation QemuCocoaAppController +- (id) init +{ + COCOA_DEBUG("QemuCocoaAppController: init\n"); + + self = [super init]; + if (self) { + + // create a view and add it to the window + cocoaView = [[QemuCocoaView alloc] initWithFrame:NSMakeRect(0.0, 0.0, 640.0, 480.0)]; + if(!cocoaView) { + fprintf(stderr, "(cocoa) can't create a view\n"); + exit(1); + } + + // create a window + normalWindow = [[NSWindow alloc] initWithContentRect:[cocoaView frame] + styleMask:NSTitledWindowMask|NSMiniaturizableWindowMask|NSClosableWindowMask + backing:NSBackingStoreBuffered defer:NO]; + if(!normalWindow) { + fprintf(stderr, "(cocoa) can't create window\n"); + exit(1); + } + [normalWindow setAcceptsMouseMovedEvents:YES]; + [normalWindow setTitle:[NSString stringWithFormat:@"QEMU"]]; + [normalWindow setContentView:cocoaView]; + [normalWindow useOptimizedDrawing:YES]; + [normalWindow makeKeyAndOrderFront:self]; + [normalWindow center]; + + } + return self; +} + +- (void) dealloc +{ + COCOA_DEBUG("QemuCocoaAppController: dealloc\n"); + + if (cocoaView) + [cocoaView release]; + [super dealloc]; +} + +- (void)applicationDidFinishLaunching: (NSNotification *) note +{ + COCOA_DEBUG("QemuCocoaAppController: applicationDidFinishLaunching\n"); + + // Display an open dialog box if no argument were passed or + // if qemu was launched from the finder ( the Finder passes "-psn" ) + if( gArgc <= 1 || strncmp ((char *)gArgv[1], "-psn", 4) == 0) { + NSOpenPanel *op = [[NSOpenPanel alloc] init]; + [op setPrompt:@"Boot image"]; + [op setMessage:@"Select the disk image you want to boot.\n\nHit the \"Cancel\" button to quit"]; + [op beginSheetForDirectory:nil file:nil types:[NSArray arrayWithObjects:@"img",@"iso",@"dmg",@"qcow",@"cow",@"cloop",@"vmdk",nil] + modalForWindow:normalWindow modalDelegate:self + didEndSelector:@selector(openPanelDidEnd:returnCode:contextInfo:) contextInfo:NULL]; + } else { + // or Launch Qemu, with the global args + [self startEmulationWithArgc:gArgc argv:(char **)gArgv]; + } +} + +- (void)applicationWillTerminate:(NSNotification *)aNotification +{ + COCOA_DEBUG("QemuCocoaAppController: applicationWillTerminate\n"); + + qemu_system_shutdown_request(); + exit(0); +} + +- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication +{ + return YES; +} + +- (void)startEmulationWithArgc:(int)argc argv:(char**)argv +{ + COCOA_DEBUG("QemuCocoaAppController: startEmulationWithArgc\n"); + + int status; + status = qemu_main(argc, argv); + exit(status); +} + +- (void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo +{ + COCOA_DEBUG("QemuCocoaAppController: openPanelDidEnd\n"); + + if(returnCode == NSCancelButton) { + exit(0); + } else if(returnCode == NSOKButton) { + const char *bin = "qemu"; + char *img = (char*)[ [ sheet filename ] cStringUsingEncoding:NSASCIIStringEncoding]; + + char **argv = (char**)malloc( sizeof(char*)*3 ); + + asprintf(&argv[0], "%s", bin); + asprintf(&argv[1], "-hda"); + asprintf(&argv[2], "%s", img); + + printf("Using argc %d argv %s -hda %s\n", 3, bin, img); + + [self startEmulationWithArgc:3 argv:(char**)argv]; + } +} +- (void)toggleFullScreen:(id)sender +{ + COCOA_DEBUG("QemuCocoaAppController: toggleFullScreen\n"); + + [cocoaView toggleFullScreen:sender]; +} + +- (void)showQEMUDoc:(id)sender +{ + COCOA_DEBUG("QemuCocoaAppController: showQEMUDoc\n"); + + [[NSWorkspace sharedWorkspace] openFile:[NSString stringWithFormat:@"%@/../doc/qemu/qemu-doc.html", + [[NSBundle mainBundle] resourcePath]] withApplication:@"Help Viewer"]; +} + +- (void)showQEMUTec:(id)sender +{ + COCOA_DEBUG("QemuCocoaAppController: showQEMUTec\n"); + + [[NSWorkspace sharedWorkspace] openFile:[NSString stringWithFormat:@"%@/../doc/qemu/qemu-tech.html", + [[NSBundle mainBundle] resourcePath]] withApplication:@"Help Viewer"]; +} +@end + + + +// Dock Connection +typedef struct CPSProcessSerNum +{ + UInt32 lo; + UInt32 hi; +} CPSProcessSerNum; + +OSErr CPSGetCurrentProcess( CPSProcessSerNum *psn); +OSErr CPSEnableForegroundOperation( CPSProcessSerNum *psn, UInt32 _arg2, UInt32 _arg3, UInt32 _arg4, UInt32 _arg5); +OSErr CPSSetFrontProcess( CPSProcessSerNum *psn); + +int main (int argc, const char * argv[]) { + + gArgc = argc; + gArgv = (char **)argv; + CPSProcessSerNum PSN; + int i; + + /* In case we don't need to display a window, let's not do that */ + for (i = 1; i < argc; i++) { + if (!strcmp(argv[i], "-vnc") || + !strcmp(argv[i], "-nographic") || + !strcmp(argv[i], "-curses")) { + return qemu_main(gArgc, gArgv); + } + } + + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + [NSApplication sharedApplication]; + + if (!CPSGetCurrentProcess(&PSN)) + if (!CPSEnableForegroundOperation(&PSN,0x03,0x3C,0x2C,0x1103)) + if (!CPSSetFrontProcess(&PSN)) + [NSApplication sharedApplication]; + + // Add menus + NSMenu *menu; + NSMenuItem *menuItem; + + [NSApp setMainMenu:[[NSMenu alloc] init]]; + + // Application menu + menu = [[NSMenu alloc] initWithTitle:@""]; + [menu addItemWithTitle:@"About QEMU" action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""]; // About QEMU + [menu addItem:[NSMenuItem separatorItem]]; //Separator + [menu addItemWithTitle:@"Hide QEMU" action:@selector(hide:) keyEquivalent:@"h"]; //Hide QEMU + menuItem = (NSMenuItem *)[menu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"]; // Hide Others + [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)]; + [menu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""]; // Show All + [menu addItem:[NSMenuItem separatorItem]]; //Separator + [menu addItemWithTitle:@"Quit QEMU" action:@selector(terminate:) keyEquivalent:@"q"]; + menuItem = [[NSMenuItem alloc] initWithTitle:@"Apple" action:nil keyEquivalent:@""]; + [menuItem setSubmenu:menu]; + [[NSApp mainMenu] addItem:menuItem]; + [NSApp performSelector:@selector(setAppleMenu:) withObject:menu]; // Workaround (this method is private since 10.4+) + + // View menu + menu = [[NSMenu alloc] initWithTitle:@"View"]; + [menu addItem: [[[NSMenuItem alloc] initWithTitle:@"Enter Fullscreen" action:@selector(toggleFullScreen:) keyEquivalent:@"f"] autorelease]]; // Fullscreen + menuItem = [[[NSMenuItem alloc] initWithTitle:@"View" action:nil keyEquivalent:@""] autorelease]; + [menuItem setSubmenu:menu]; + [[NSApp mainMenu] addItem:menuItem]; + + // Window menu + menu = [[NSMenu alloc] initWithTitle:@"Window"]; + [menu addItem: [[[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"] autorelease]]; // Miniaturize + menuItem = [[[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""] autorelease]; + [menuItem setSubmenu:menu]; + [[NSApp mainMenu] addItem:menuItem]; + [NSApp setWindowsMenu:menu]; + + // Help menu + menu = [[NSMenu alloc] initWithTitle:@"Help"]; + [menu addItem: [[[NSMenuItem alloc] initWithTitle:@"QEMU Documentation" action:@selector(showQEMUDoc:) keyEquivalent:@"?"] autorelease]]; // QEMU Help + [menu addItem: [[[NSMenuItem alloc] initWithTitle:@"QEMU Technology" action:@selector(showQEMUTec:) keyEquivalent:@""] autorelease]]; // QEMU Help + menuItem = [[[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""] autorelease]; + [menuItem setSubmenu:menu]; + [[NSApp mainMenu] addItem:menuItem]; + + // Create an Application controller + QemuCocoaAppController *appController = [[QemuCocoaAppController alloc] init]; + [NSApp setDelegate:appController]; + + // Start the main event loop + [NSApp run]; + + [appController release]; + [pool release]; + + return 0; +} + + + +#pragma mark qemu +static void cocoa_update(DisplayState *ds, int x, int y, int w, int h) +{ + COCOA_DEBUG("qemu_cocoa: cocoa_update\n"); + + NSRect rect; + if ([cocoaView cdx] == 1.0) { + rect = NSMakeRect(x, [cocoaView gscreen].height - y - h, w, h); + } else { + rect = NSMakeRect( + x * [cocoaView cdx], + ([cocoaView gscreen].height - y - h) * [cocoaView cdy], + w * [cocoaView cdx], + h * [cocoaView cdy]); + } + [cocoaView setNeedsDisplayInRect:rect]; +} + +static void cocoa_resize(DisplayState *ds) +{ + COCOA_DEBUG("qemu_cocoa: cocoa_resize\n"); + + [cocoaView resizeContentToWidth:(int)(ds_get_width(ds)) height:(int)(ds_get_height(ds)) displayState:ds]; +} + +static void cocoa_refresh(DisplayState *ds) +{ + COCOA_DEBUG("qemu_cocoa: cocoa_refresh\n"); + + if (kbd_mouse_is_absolute()) { + if (![cocoaView isAbsoluteEnabled]) { + if ([cocoaView isMouseGrabed]) { + [cocoaView ungrabMouse]; + } + } + [cocoaView setAbsoluteEnabled:YES]; + } + + NSDate *distantPast; + NSEvent *event; + distantPast = [NSDate distantPast]; + do { + event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:distantPast + inMode: NSDefaultRunLoopMode dequeue:YES]; + if (event != nil) { + [cocoaView handleEvent:event]; + } + } while(event != nil); + vga_hw_update(); +} + +static void cocoa_cleanup(void) +{ + COCOA_DEBUG("qemu_cocoa: cocoa_cleanup\n"); + qemu_free(dcl); +} + +void cocoa_display_init(DisplayState *ds, int full_screen) +{ + COCOA_DEBUG("qemu_cocoa: cocoa_display_init\n"); + + dcl = qemu_mallocz(sizeof(DisplayChangeListener)); + + // register vga output callbacks + dcl->dpy_update = cocoa_update; + dcl->dpy_resize = cocoa_resize; + dcl->dpy_refresh = cocoa_refresh; + + register_displaychangelistener(ds, dcl); + + // register cleanup function + atexit(cocoa_cleanup); +} diff -Nru qemu-kvm-0.12.5+noroms/ui/curses.c qemu-kvm-0.14.1/ui/curses.c --- qemu-kvm-0.12.5+noroms/ui/curses.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/ui/curses.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,368 @@ +/* + * QEMU curses/ncurses display driver + * + * Copyright (c) 2005 Andrzej Zaborowski + * + * 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 + +#ifndef _WIN32 +#include +#include +#include +#endif + +#ifdef __OpenBSD__ +#define resize_term resizeterm +#endif + +#include "qemu-common.h" +#include "console.h" +#include "sysemu.h" + +#define FONT_HEIGHT 16 +#define FONT_WIDTH 8 + +static console_ch_t screen[160 * 100]; +static WINDOW *screenpad = NULL; +static int width, height, gwidth, gheight, invalidate; +static int px, py, sminx, sminy, smaxx, smaxy; + +static void curses_update(DisplayState *ds, int x, int y, int w, int h) +{ + chtype *line; + + line = ((chtype *) screen) + y * width; + for (h += y; y < h; y ++, line += width) + mvwaddchnstr(screenpad, y, 0, line, width); + + pnoutrefresh(screenpad, py, px, sminy, sminx, smaxy - 1, smaxx - 1); + refresh(); +} + +static void curses_calc_pad(void) +{ + if (is_fixedsize_console()) { + width = gwidth; + height = gheight; + } else { + width = COLS; + height = LINES; + } + + if (screenpad) + delwin(screenpad); + + clear(); + refresh(); + + screenpad = newpad(height, width); + + if (width > COLS) { + px = (width - COLS) / 2; + sminx = 0; + smaxx = COLS; + } else { + px = 0; + sminx = (COLS - width) / 2; + smaxx = sminx + width; + } + + if (height > LINES) { + py = (height - LINES) / 2; + sminy = 0; + smaxy = LINES; + } else { + py = 0; + sminy = (LINES - height) / 2; + smaxy = sminy + height; + } +} + +static void curses_resize(DisplayState *ds) +{ + if (ds_get_width(ds) == gwidth && ds_get_height(ds) == gheight) + return; + + gwidth = ds_get_width(ds); + gheight = ds_get_height(ds); + + curses_calc_pad(); + ds->surface->width = width * FONT_WIDTH; + ds->surface->height = height * FONT_HEIGHT; +} + +#ifndef _WIN32 +#if defined(SIGWINCH) && defined(KEY_RESIZE) +static void curses_winch_handler(int signum) +{ + struct winsize { + unsigned short ws_row; + unsigned short ws_col; + unsigned short ws_xpixel; /* unused */ + unsigned short ws_ypixel; /* unused */ + } ws; + + /* terminal size changed */ + if (ioctl(1, TIOCGWINSZ, &ws) == -1) + return; + + resize_term(ws.ws_row, ws.ws_col); + curses_calc_pad(); + invalidate = 1; + + /* some systems require this */ + signal(SIGWINCH, curses_winch_handler); +} +#endif +#endif + +static void curses_cursor_position(DisplayState *ds, int x, int y) +{ + if (x >= 0) { + x = sminx + x - px; + y = sminy + y - py; + + if (x >= 0 && y >= 0 && x < COLS && y < LINES) { + move(y, x); + curs_set(1); + /* it seems that curs_set(1) must always be called before + * curs_set(2) for the latter to have effect */ + if (!is_graphic_console()) + curs_set(2); + return; + } + } + + curs_set(0); +} + +/* generic keyboard conversion */ + +#include "curses_keys.h" + +static kbd_layout_t *kbd_layout = NULL; + +static void curses_refresh(DisplayState *ds) +{ + int chr, nextchr, keysym, keycode, keycode_alt; + + if (invalidate) { + clear(); + refresh(); + curses_calc_pad(); + ds->surface->width = FONT_WIDTH * width; + ds->surface->height = FONT_HEIGHT * height; + vga_hw_invalidate(); + invalidate = 0; + } + + vga_hw_text_update(screen); + + nextchr = ERR; + while (1) { + /* while there are any pending key strokes to process */ + if (nextchr == ERR) + chr = getch(); + else { + chr = nextchr; + nextchr = ERR; + } + + if (chr == ERR) + break; + +#ifdef KEY_RESIZE + /* this shouldn't occur when we use a custom SIGWINCH handler */ + if (chr == KEY_RESIZE) { + clear(); + refresh(); + curses_calc_pad(); + curses_update(ds, 0, 0, width, height); + ds->surface->width = FONT_WIDTH * width; + ds->surface->height = FONT_HEIGHT * height; + continue; + } +#endif + + keycode = curses2keycode[chr]; + keycode_alt = 0; + + /* alt key */ + if (keycode == 1) { + nextchr = getch(); + + if (nextchr != ERR) { + chr = nextchr; + keycode_alt = ALT; + keycode = curses2keycode[nextchr]; + nextchr = ERR; + + if (keycode != -1) { + keycode |= ALT; + + /* process keys reserved for qemu */ + if (keycode >= QEMU_KEY_CONSOLE0 && + keycode < QEMU_KEY_CONSOLE0 + 9) { + erase(); + wnoutrefresh(stdscr); + console_select(keycode - QEMU_KEY_CONSOLE0); + + invalidate = 1; + continue; + } + } + } + } + + if (kbd_layout) { + keysym = -1; + if (chr < CURSES_KEYS) + keysym = curses2keysym[chr]; + + if (keysym == -1) { + if (chr < ' ') { + keysym = chr + '@'; + if (keysym >= 'A' && keysym <= 'Z') + keysym += 'a' - 'A'; + keysym |= KEYSYM_CNTRL; + } else + keysym = chr; + } + + keycode = keysym2scancode(kbd_layout, keysym & KEYSYM_MASK); + if (keycode == 0) + continue; + + keycode |= (keysym & ~KEYSYM_MASK) >> 16; + keycode |= keycode_alt; + } + + if (keycode == -1) + continue; + + if (is_graphic_console()) { + /* since terminals don't know about key press and release + * events, we need to emit both for each key received */ + if (keycode & SHIFT) + kbd_put_keycode(SHIFT_CODE); + if (keycode & CNTRL) + kbd_put_keycode(CNTRL_CODE); + if (keycode & ALT) + kbd_put_keycode(ALT_CODE); + if (keycode & ALTGR) { + kbd_put_keycode(SCANCODE_EMUL0); + kbd_put_keycode(ALT_CODE); + } + if (keycode & GREY) + kbd_put_keycode(GREY_CODE); + kbd_put_keycode(keycode & KEY_MASK); + if (keycode & GREY) + kbd_put_keycode(GREY_CODE); + kbd_put_keycode((keycode & KEY_MASK) | KEY_RELEASE); + if (keycode & ALTGR) { + kbd_put_keycode(SCANCODE_EMUL0); + kbd_put_keycode(ALT_CODE | KEY_RELEASE); + } + if (keycode & ALT) + kbd_put_keycode(ALT_CODE | KEY_RELEASE); + if (keycode & CNTRL) + kbd_put_keycode(CNTRL_CODE | KEY_RELEASE); + if (keycode & SHIFT) + kbd_put_keycode(SHIFT_CODE | KEY_RELEASE); + } else { + keysym = curses2qemu[chr]; + if (keysym == -1) + keysym = chr; + + kbd_put_keysym(keysym); + } + } +} + +static void curses_atexit(void) +{ + endwin(); +} + +static void curses_setup(void) +{ + int i, colour_default[8] = { + COLOR_BLACK, COLOR_BLUE, COLOR_GREEN, COLOR_CYAN, + COLOR_RED, COLOR_MAGENTA, COLOR_YELLOW, COLOR_WHITE, + }; + + /* input as raw as possible, let everything be interpreted + * by the guest system */ + initscr(); noecho(); intrflush(stdscr, FALSE); + nodelay(stdscr, TRUE); nonl(); keypad(stdscr, TRUE); + start_color(); raw(); scrollok(stdscr, FALSE); + + for (i = 0; i < 64; i ++) + init_pair(i, colour_default[i & 7], colour_default[i >> 3]); +} + +static void curses_keyboard_setup(void) +{ +#if defined(__APPLE__) + /* always use generic keymaps */ + if (!keyboard_layout) + keyboard_layout = "en-us"; +#endif + if(keyboard_layout) { + kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout); + if (!kbd_layout) + exit(1); + } +} + +void curses_display_init(DisplayState *ds, int full_screen) +{ + DisplayChangeListener *dcl; +#ifndef _WIN32 + if (!isatty(1)) { + fprintf(stderr, "We need a terminal output\n"); + exit(1); + } +#endif + + curses_setup(); + curses_keyboard_setup(); + atexit(curses_atexit); + +#ifndef _WIN32 +#if defined(SIGWINCH) && defined(KEY_RESIZE) + /* some curses implementations provide a handler, but we + * want to be sure this is handled regardless of the library */ + signal(SIGWINCH, curses_winch_handler); +#endif +#endif + + dcl = (DisplayChangeListener *) qemu_mallocz(sizeof(DisplayChangeListener)); + dcl->dpy_update = curses_update; + dcl->dpy_resize = curses_resize; + dcl->dpy_refresh = curses_refresh; + dcl->dpy_text_cursor = curses_cursor_position; + register_displaychangelistener(ds, dcl); + qemu_free_displaysurface(ds); + ds->surface = qemu_create_displaysurface_from(640, 400, 0, 0, (uint8_t*) screen); + + invalidate = 1; +} diff -Nru qemu-kvm-0.12.5+noroms/ui/curses_keys.h qemu-kvm-0.14.1/ui/curses_keys.h --- qemu-kvm-0.12.5+noroms/ui/curses_keys.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/ui/curses_keys.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,509 @@ +/* + * Keycode and keysyms conversion tables for curses + * + * Copyright (c) 2005 Andrzej Zaborowski + * + * 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 +#include "keymaps.h" + + +#define KEY_RELEASE 0x80 +#define KEY_MASK 0x7f +#define GREY_CODE 0xe0 +#define GREY SCANCODE_GREY +#define SHIFT_CODE 0x2a +#define SHIFT SCANCODE_SHIFT +#define CNTRL_CODE 0x1d +#define CNTRL SCANCODE_CTRL +#define ALT_CODE 0x38 +#define ALT SCANCODE_ALT +#define ALTGR SCANCODE_ALTGR + +#define KEYSYM_MASK 0x0ffffff +#define KEYSYM_SHIFT (SCANCODE_SHIFT << 16) +#define KEYSYM_CNTRL (SCANCODE_CTRL << 16) +#define KEYSYM_ALT (SCANCODE_ALT << 16) +#define KEYSYM_ALTGR (SCANCODE_ALTGR << 16) + +/* curses won't detect a Control + Alt + 1, so use Alt + 1 */ +#define QEMU_KEY_CONSOLE0 (2 | ALT) /* (curses2keycode['1'] | ALT) */ + +#define CURSES_KEYS KEY_MAX /* KEY_MAX defined in */ + +static const int curses2keysym[CURSES_KEYS] = { + [0 ... (CURSES_KEYS - 1)] = -1, + + [0x7f] = KEY_BACKSPACE, + ['\r'] = KEY_ENTER, + ['\n'] = KEY_ENTER, + [27] = 27, + [KEY_BTAB] = '\t' | KEYSYM_SHIFT, +}; + +static const int curses2keycode[CURSES_KEYS] = { + [0 ... (CURSES_KEYS - 1)] = -1, + + [0x01b] = 1, /* Escape */ + ['1'] = 2, + ['2'] = 3, + ['3'] = 4, + ['4'] = 5, + ['5'] = 6, + ['6'] = 7, + ['7'] = 8, + ['8'] = 9, + ['9'] = 10, + ['0'] = 11, + ['-'] = 12, + ['='] = 13, + [0x07f] = 14, /* Backspace */ + [KEY_BACKSPACE] = 14, /* Backspace */ + + ['\t'] = 15, /* Tab */ + ['q'] = 16, + ['w'] = 17, + ['e'] = 18, + ['r'] = 19, + ['t'] = 20, + ['y'] = 21, + ['u'] = 22, + ['i'] = 23, + ['o'] = 24, + ['p'] = 25, + ['['] = 26, + [']'] = 27, + ['\n'] = 28, /* Return */ + ['\r'] = 28, /* Return */ + [KEY_ENTER] = 28, /* Return */ + + ['a'] = 30, + ['s'] = 31, + ['d'] = 32, + ['f'] = 33, + ['g'] = 34, + ['h'] = 35, + ['j'] = 36, + ['k'] = 37, + ['l'] = 38, + [';'] = 39, + ['\''] = 40, /* Single quote */ + ['`'] = 41, + ['\\'] = 43, /* Backslash */ + + ['z'] = 44, + ['x'] = 45, + ['c'] = 46, + ['v'] = 47, + ['b'] = 48, + ['n'] = 49, + ['m'] = 50, + [','] = 51, + ['.'] = 52, + ['/'] = 53, + + [' '] = 57, + + [KEY_F(1)] = 59, /* Function Key 1 */ + [KEY_F(2)] = 60, /* Function Key 2 */ + [KEY_F(3)] = 61, /* Function Key 3 */ + [KEY_F(4)] = 62, /* Function Key 4 */ + [KEY_F(5)] = 63, /* Function Key 5 */ + [KEY_F(6)] = 64, /* Function Key 6 */ + [KEY_F(7)] = 65, /* Function Key 7 */ + [KEY_F(8)] = 66, /* Function Key 8 */ + [KEY_F(9)] = 67, /* Function Key 9 */ + [KEY_F(10)] = 68, /* Function Key 10 */ + [KEY_F(11)] = 87, /* Function Key 11 */ + [KEY_F(12)] = 88, /* Function Key 12 */ + + [KEY_HOME] = 71 | GREY, /* Home */ + [KEY_UP] = 72 | GREY, /* Up Arrow */ + [KEY_PPAGE] = 73 | GREY, /* Page Up */ + [KEY_LEFT] = 75 | GREY, /* Left Arrow */ + [KEY_RIGHT] = 77 | GREY, /* Right Arrow */ + [KEY_END] = 79 | GREY, /* End */ + [KEY_DOWN] = 80 | GREY, /* Down Arrow */ + [KEY_NPAGE] = 81 | GREY, /* Page Down */ + [KEY_IC] = 82 | GREY, /* Insert */ + [KEY_DC] = 83 | GREY, /* Delete */ + + ['!'] = 2 | SHIFT, + ['@'] = 3 | SHIFT, + ['#'] = 4 | SHIFT, + ['$'] = 5 | SHIFT, + ['%'] = 6 | SHIFT, + ['^'] = 7 | SHIFT, + ['&'] = 8 | SHIFT, + ['*'] = 9 | SHIFT, + ['('] = 10 | SHIFT, + [')'] = 11 | SHIFT, + ['_'] = 12 | SHIFT, + ['+'] = 13 | SHIFT, + + [KEY_BTAB] = 15 | SHIFT, /* Shift + Tab */ + ['Q'] = 16 | SHIFT, + ['W'] = 17 | SHIFT, + ['E'] = 18 | SHIFT, + ['R'] = 19 | SHIFT, + ['T'] = 20 | SHIFT, + ['Y'] = 21 | SHIFT, + ['U'] = 22 | SHIFT, + ['I'] = 23 | SHIFT, + ['O'] = 24 | SHIFT, + ['P'] = 25 | SHIFT, + ['{'] = 26 | SHIFT, + ['}'] = 27 | SHIFT, + + ['A'] = 30 | SHIFT, + ['S'] = 31 | SHIFT, + ['D'] = 32 | SHIFT, + ['F'] = 33 | SHIFT, + ['G'] = 34 | SHIFT, + ['H'] = 35 | SHIFT, + ['J'] = 36 | SHIFT, + ['K'] = 37 | SHIFT, + ['L'] = 38 | SHIFT, + [':'] = 39 | SHIFT, + ['"'] = 40 | SHIFT, + ['~'] = 41 | SHIFT, + ['|'] = 43 | SHIFT, + + ['Z'] = 44 | SHIFT, + ['X'] = 45 | SHIFT, + ['C'] = 46 | SHIFT, + ['V'] = 47 | SHIFT, + ['B'] = 48 | SHIFT, + ['N'] = 49 | SHIFT, + ['M'] = 50 | SHIFT, + ['<'] = 51 | SHIFT, + ['>'] = 52 | SHIFT, + ['?'] = 53 | SHIFT, + + [KEY_F(13)] = 59 | SHIFT, /* Shift + Function Key 1 */ + [KEY_F(14)] = 60 | SHIFT, /* Shift + Function Key 2 */ + [KEY_F(15)] = 61 | SHIFT, /* Shift + Function Key 3 */ + [KEY_F(16)] = 62 | SHIFT, /* Shift + Function Key 4 */ + [KEY_F(17)] = 63 | SHIFT, /* Shift + Function Key 5 */ + [KEY_F(18)] = 64 | SHIFT, /* Shift + Function Key 6 */ + [KEY_F(19)] = 65 | SHIFT, /* Shift + Function Key 7 */ + [KEY_F(20)] = 66 | SHIFT, /* Shift + Function Key 8 */ + [KEY_F(21)] = 67 | SHIFT, /* Shift + Function Key 9 */ + [KEY_F(22)] = 68 | SHIFT, /* Shift + Function Key 10 */ + [KEY_F(23)] = 69 | SHIFT, /* Shift + Function Key 11 */ + [KEY_F(24)] = 70 | SHIFT, /* Shift + Function Key 12 */ + + ['Q' - '@'] = 16 | CNTRL, /* Control + q */ + ['W' - '@'] = 17 | CNTRL, /* Control + w */ + ['E' - '@'] = 18 | CNTRL, /* Control + e */ + ['R' - '@'] = 19 | CNTRL, /* Control + r */ + ['T' - '@'] = 20 | CNTRL, /* Control + t */ + ['Y' - '@'] = 21 | CNTRL, /* Control + y */ + ['U' - '@'] = 22 | CNTRL, /* Control + u */ + /* Control + i collides with Tab */ + ['O' - '@'] = 24 | CNTRL, /* Control + o */ + ['P' - '@'] = 25 | CNTRL, /* Control + p */ + + ['A' - '@'] = 30 | CNTRL, /* Control + a */ + ['S' - '@'] = 31 | CNTRL, /* Control + s */ + ['D' - '@'] = 32 | CNTRL, /* Control + d */ + ['F' - '@'] = 33 | CNTRL, /* Control + f */ + ['G' - '@'] = 34 | CNTRL, /* Control + g */ + ['H' - '@'] = 35 | CNTRL, /* Control + h */ + /* Control + j collides with Return */ + ['K' - '@'] = 37 | CNTRL, /* Control + k */ + ['L' - '@'] = 38 | CNTRL, /* Control + l */ + + ['Z' - '@'] = 44 | CNTRL, /* Control + z */ + ['X' - '@'] = 45 | CNTRL, /* Control + x */ + ['C' - '@'] = 46 | CNTRL, /* Control + c */ + ['V' - '@'] = 47 | CNTRL, /* Control + v */ + ['B' - '@'] = 48 | CNTRL, /* Control + b */ + ['N' - '@'] = 49 | CNTRL, /* Control + n */ + /* Control + m collides with the keycode for Enter */ + +}; + +static const int curses2qemu[CURSES_KEYS] = { + [0 ... (CURSES_KEYS - 1)] = -1, + + ['\n'] = '\n', + ['\r'] = '\n', + + [0x07f] = QEMU_KEY_BACKSPACE, + + [KEY_DOWN] = QEMU_KEY_DOWN, + [KEY_UP] = QEMU_KEY_UP, + [KEY_LEFT] = QEMU_KEY_LEFT, + [KEY_RIGHT] = QEMU_KEY_RIGHT, + [KEY_HOME] = QEMU_KEY_HOME, + [KEY_BACKSPACE] = QEMU_KEY_BACKSPACE, + + [KEY_DC] = QEMU_KEY_DELETE, + [KEY_NPAGE] = QEMU_KEY_PAGEDOWN, + [KEY_PPAGE] = QEMU_KEY_PAGEUP, + [KEY_ENTER] = '\n', + [KEY_END] = QEMU_KEY_END, + +}; + +static const name2keysym_t name2keysym[] = { + /* Plain ASCII */ + { "space", 0x020 }, + { "exclam", 0x021 }, + { "quotedbl", 0x022 }, + { "numbersign", 0x023 }, + { "dollar", 0x024 }, + { "percent", 0x025 }, + { "ampersand", 0x026 }, + { "apostrophe", 0x027 }, + { "parenleft", 0x028 }, + { "parenright", 0x029 }, + { "asterisk", 0x02a }, + { "plus", 0x02b }, + { "comma", 0x02c }, + { "minus", 0x02d }, + { "period", 0x02e }, + { "slash", 0x02f }, + { "0", 0x030 }, + { "1", 0x031 }, + { "2", 0x032 }, + { "3", 0x033 }, + { "4", 0x034 }, + { "5", 0x035 }, + { "6", 0x036 }, + { "7", 0x037 }, + { "8", 0x038 }, + { "9", 0x039 }, + { "colon", 0x03a }, + { "semicolon", 0x03b }, + { "less", 0x03c }, + { "equal", 0x03d }, + { "greater", 0x03e }, + { "question", 0x03f }, + { "at", 0x040 }, + { "A", 0x041 }, + { "B", 0x042 }, + { "C", 0x043 }, + { "D", 0x044 }, + { "E", 0x045 }, + { "F", 0x046 }, + { "G", 0x047 }, + { "H", 0x048 }, + { "I", 0x049 }, + { "J", 0x04a }, + { "K", 0x04b }, + { "L", 0x04c }, + { "M", 0x04d }, + { "N", 0x04e }, + { "O", 0x04f }, + { "P", 0x050 }, + { "Q", 0x051 }, + { "R", 0x052 }, + { "S", 0x053 }, + { "T", 0x054 }, + { "U", 0x055 }, + { "V", 0x056 }, + { "W", 0x057 }, + { "X", 0x058 }, + { "Y", 0x059 }, + { "Z", 0x05a }, + { "bracketleft", 0x05b }, + { "backslash", 0x05c }, + { "bracketright", 0x05d }, + { "asciicircum", 0x05e }, + { "underscore", 0x05f }, + { "grave", 0x060 }, + { "a", 0x061 }, + { "b", 0x062 }, + { "c", 0x063 }, + { "d", 0x064 }, + { "e", 0x065 }, + { "f", 0x066 }, + { "g", 0x067 }, + { "h", 0x068 }, + { "i", 0x069 }, + { "j", 0x06a }, + { "k", 0x06b }, + { "l", 0x06c }, + { "m", 0x06d }, + { "n", 0x06e }, + { "o", 0x06f }, + { "p", 0x070 }, + { "q", 0x071 }, + { "r", 0x072 }, + { "s", 0x073 }, + { "t", 0x074 }, + { "u", 0x075 }, + { "v", 0x076 }, + { "w", 0x077 }, + { "x", 0x078 }, + { "y", 0x079 }, + { "z", 0x07a }, + { "braceleft", 0x07b }, + { "bar", 0x07c }, + { "braceright", 0x07d }, + { "asciitilde", 0x07e }, + + /* Latin-1 extensions */ + { "nobreakspace", 0x0a0 }, + { "exclamdown", 0x0a1 }, + { "cent", 0x0a2 }, + { "sterling", 0x0a3 }, + { "currency", 0x0a4 }, + { "yen", 0x0a5 }, + { "brokenbar", 0x0a6 }, + { "section", 0x0a7 }, + { "diaeresis", 0x0a8 }, + { "copyright", 0x0a9 }, + { "ordfeminine", 0x0aa }, + { "guillemotleft", 0x0ab }, + { "notsign", 0x0ac }, + { "hyphen", 0x0ad }, + { "registered", 0x0ae }, + { "macron", 0x0af }, + { "degree", 0x0b0 }, + { "plusminus", 0x0b1 }, + { "twosuperior", 0x0b2 }, + { "threesuperior", 0x0b3 }, + { "acute", 0x0b4 }, + { "mu", 0x0b5 }, + { "paragraph", 0x0b6 }, + { "periodcentered", 0x0b7 }, + { "cedilla", 0x0b8 }, + { "onesuperior", 0x0b9 }, + { "masculine", 0x0ba }, + { "guillemotright", 0x0bb }, + { "onequarter", 0x0bc }, + { "onehalf", 0x0bd }, + { "threequarters", 0x0be }, + { "questiondown", 0x0bf }, + { "Agrave", 0x0c0 }, + { "Aacute", 0x0c1 }, + { "Acircumflex", 0x0c2 }, + { "Atilde", 0x0c3 }, + { "Adiaeresis", 0x0c4 }, + { "Aring", 0x0c5 }, + { "AE", 0x0c6 }, + { "Ccedilla", 0x0c7 }, + { "Egrave", 0x0c8 }, + { "Eacute", 0x0c9 }, + { "Ecircumflex", 0x0ca }, + { "Ediaeresis", 0x0cb }, + { "Igrave", 0x0cc }, + { "Iacute", 0x0cd }, + { "Icircumflex", 0x0ce }, + { "Idiaeresis", 0x0cf }, + { "ETH", 0x0d0 }, + { "Eth", 0x0d0 }, + { "Ntilde", 0x0d1 }, + { "Ograve", 0x0d2 }, + { "Oacute", 0x0d3 }, + { "Ocircumflex", 0x0d4 }, + { "Otilde", 0x0d5 }, + { "Odiaeresis", 0x0d6 }, + { "multiply", 0x0d7 }, + { "Ooblique", 0x0d8 }, + { "Oslash", 0x0d8 }, + { "Ugrave", 0x0d9 }, + { "Uacute", 0x0da }, + { "Ucircumflex", 0x0db }, + { "Udiaeresis", 0x0dc }, + { "Yacute", 0x0dd }, + { "THORN", 0x0de }, + { "Thorn", 0x0de }, + { "ssharp", 0x0df }, + { "agrave", 0x0e0 }, + { "aacute", 0x0e1 }, + { "acircumflex", 0x0e2 }, + { "atilde", 0x0e3 }, + { "adiaeresis", 0x0e4 }, + { "aring", 0x0e5 }, + { "ae", 0x0e6 }, + { "ccedilla", 0x0e7 }, + { "egrave", 0x0e8 }, + { "eacute", 0x0e9 }, + { "ecircumflex", 0x0ea }, + { "ediaeresis", 0x0eb }, + { "igrave", 0x0ec }, + { "iacute", 0x0ed }, + { "icircumflex", 0x0ee }, + { "idiaeresis", 0x0ef }, + { "eth", 0x0f0 }, + { "ntilde", 0x0f1 }, + { "ograve", 0x0f2 }, + { "oacute", 0x0f3 }, + { "ocircumflex", 0x0f4 }, + { "otilde", 0x0f5 }, + { "odiaeresis", 0x0f6 }, + { "division", 0x0f7 }, + { "oslash", 0x0f8 }, + { "ooblique", 0x0f8 }, + { "ugrave", 0x0f9 }, + { "uacute", 0x0fa }, + { "ucircumflex", 0x0fb }, + { "udiaeresis", 0x0fc }, + { "yacute", 0x0fd }, + { "thorn", 0x0fe }, + { "ydiaeresis", 0x0ff }, + + /* Special keys */ + { "BackSpace", KEY_BACKSPACE }, + { "Tab", '\t' }, + { "Return", KEY_ENTER }, + { "Right", KEY_RIGHT }, + { "Left", KEY_LEFT }, + { "Up", KEY_UP }, + { "Down", KEY_DOWN }, + { "Page_Down", KEY_NPAGE }, + { "Page_Up", KEY_PPAGE }, + { "Insert", KEY_IC }, + { "Delete", KEY_DC }, + { "Home", KEY_HOME }, + { "End", KEY_END }, + { "F1", KEY_F(1) }, + { "F2", KEY_F(2) }, + { "F3", KEY_F(3) }, + { "F4", KEY_F(4) }, + { "F5", KEY_F(5) }, + { "F6", KEY_F(6) }, + { "F7", KEY_F(7) }, + { "F8", KEY_F(8) }, + { "F9", KEY_F(9) }, + { "F10", KEY_F(10) }, + { "F11", KEY_F(11) }, + { "F12", KEY_F(12) }, + { "F13", KEY_F(13) }, + { "F14", KEY_F(14) }, + { "F15", KEY_F(15) }, + { "F16", KEY_F(16) }, + { "F17", KEY_F(17) }, + { "F18", KEY_F(18) }, + { "F19", KEY_F(19) }, + { "F20", KEY_F(20) }, + { "F21", KEY_F(21) }, + { "F22", KEY_F(22) }, + { "F23", KEY_F(23) }, + { "F24", KEY_F(24) }, + { "Escape", 27 }, + + { NULL, 0 }, +}; diff -Nru qemu-kvm-0.12.5+noroms/ui/d3des.c qemu-kvm-0.14.1/ui/d3des.c --- qemu-kvm-0.12.5+noroms/ui/d3des.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/ui/d3des.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,424 @@ +/* + * This is D3DES (V5.09) by Richard Outerbridge with the double and + * triple-length support removed for use in VNC. Also the bytebit[] array + * has been reversed so that the most significant bit in each byte of the + * key is ignored, not the least significant. + * + * These changes are: + * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. + * + * This software 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. + */ + +/* D3DES (V5.09) - + * + * A portable, public domain, version of the Data Encryption Standard. + * + * Written with Symantec's THINK (Lightspeed) C by Richard Outerbridge. + * Thanks to: Dan Hoey for his excellent Initial and Inverse permutation + * code; Jim Gillogly & Phil Karn for the DES key schedule code; Dennis + * Ferguson, Eric Young and Dana How for comparing notes; and Ray Lau, + * for humouring me on. + * + * Copyright (c) 1988,1989,1990,1991,1992 by Richard Outerbridge. + * (GEnie : OUTER; CIS : [71755,204]) Graven Imagery, 1992. + */ + +#include "d3des.h" + +static void scrunch(unsigned char *, unsigned long *); +static void unscrun(unsigned long *, unsigned char *); +static void desfunc(unsigned long *, unsigned long *); +static void cookey(unsigned long *); + +static unsigned long KnL[32] = { 0L }; + +static const unsigned short bytebit[8] = { + 01, 02, 04, 010, 020, 040, 0100, 0200 }; + +static const unsigned long bigbyte[24] = { + 0x800000L, 0x400000L, 0x200000L, 0x100000L, + 0x80000L, 0x40000L, 0x20000L, 0x10000L, + 0x8000L, 0x4000L, 0x2000L, 0x1000L, + 0x800L, 0x400L, 0x200L, 0x100L, + 0x80L, 0x40L, 0x20L, 0x10L, + 0x8L, 0x4L, 0x2L, 0x1L }; + +/* Use the key schedule specified in the Standard (ANSI X3.92-1981). */ + +static const unsigned char pc1[56] = { + 56, 48, 40, 32, 24, 16, 8, 0, 57, 49, 41, 33, 25, 17, + 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35, + 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, + 13, 5, 60, 52, 44, 36, 28, 20, 12, 4, 27, 19, 11, 3 }; + +static const unsigned char totrot[16] = { + 1,2,4,6,8,10,12,14,15,17,19,21,23,25,27,28 }; + +static const unsigned char pc2[48] = { + 13, 16, 10, 23, 0, 4, 2, 27, 14, 5, 20, 9, + 22, 18, 11, 3, 25, 7, 15, 6, 26, 19, 12, 1, + 40, 51, 30, 36, 46, 54, 29, 39, 50, 44, 32, 47, + 43, 48, 38, 55, 33, 52, 45, 41, 49, 35, 28, 31 }; + +/* Thanks to James Gillogly & Phil Karn! */ +void deskey(unsigned char *key, int edf) +{ + register int i, j, l, m, n; + unsigned char pc1m[56], pcr[56]; + unsigned long kn[32]; + + for ( j = 0; j < 56; j++ ) { + l = pc1[j]; + m = l & 07; + pc1m[j] = (key[l >> 3] & bytebit[m]) ? 1 : 0; + } + for( i = 0; i < 16; i++ ) { + if( edf == DE1 ) m = (15 - i) << 1; + else m = i << 1; + n = m + 1; + kn[m] = kn[n] = 0L; + for( j = 0; j < 28; j++ ) { + l = j + totrot[i]; + if( l < 28 ) pcr[j] = pc1m[l]; + else pcr[j] = pc1m[l - 28]; + } + for( j = 28; j < 56; j++ ) { + l = j + totrot[i]; + if( l < 56 ) pcr[j] = pc1m[l]; + else pcr[j] = pc1m[l - 28]; + } + for( j = 0; j < 24; j++ ) { + if( pcr[pc2[j]] ) kn[m] |= bigbyte[j]; + if( pcr[pc2[j+24]] ) kn[n] |= bigbyte[j]; + } + } + cookey(kn); + return; + } + +static void cookey(register unsigned long *raw1) +{ + register unsigned long *cook, *raw0; + unsigned long dough[32]; + register int i; + + cook = dough; + for( i = 0; i < 16; i++, raw1++ ) { + raw0 = raw1++; + *cook = (*raw0 & 0x00fc0000L) << 6; + *cook |= (*raw0 & 0x00000fc0L) << 10; + *cook |= (*raw1 & 0x00fc0000L) >> 10; + *cook++ |= (*raw1 & 0x00000fc0L) >> 6; + *cook = (*raw0 & 0x0003f000L) << 12; + *cook |= (*raw0 & 0x0000003fL) << 16; + *cook |= (*raw1 & 0x0003f000L) >> 4; + *cook++ |= (*raw1 & 0x0000003fL); + } + usekey(dough); + return; + } + +void cpkey(register unsigned long *into) +{ + register unsigned long *from, *endp; + + from = KnL, endp = &KnL[32]; + while( from < endp ) *into++ = *from++; + return; + } + +void usekey(register unsigned long *from) +{ + register unsigned long *to, *endp; + + to = KnL, endp = &KnL[32]; + while( to < endp ) *to++ = *from++; + return; + } + +void des(unsigned char *inblock, unsigned char *outblock) +{ + unsigned long work[2]; + + scrunch(inblock, work); + desfunc(work, KnL); + unscrun(work, outblock); + return; + } + +static void scrunch(register unsigned char *outof, register unsigned long *into) +{ + *into = (*outof++ & 0xffL) << 24; + *into |= (*outof++ & 0xffL) << 16; + *into |= (*outof++ & 0xffL) << 8; + *into++ |= (*outof++ & 0xffL); + *into = (*outof++ & 0xffL) << 24; + *into |= (*outof++ & 0xffL) << 16; + *into |= (*outof++ & 0xffL) << 8; + *into |= (*outof & 0xffL); + return; + } + +static void unscrun(register unsigned long *outof, register unsigned char *into) +{ + *into++ = (unsigned char)((*outof >> 24) & 0xffL); + *into++ = (unsigned char)((*outof >> 16) & 0xffL); + *into++ = (unsigned char)((*outof >> 8) & 0xffL); + *into++ = (unsigned char)(*outof++ & 0xffL); + *into++ = (unsigned char)((*outof >> 24) & 0xffL); + *into++ = (unsigned char)((*outof >> 16) & 0xffL); + *into++ = (unsigned char)((*outof >> 8) & 0xffL); + *into = (unsigned char)(*outof & 0xffL); + return; + } + +static const unsigned long SP1[64] = { + 0x01010400L, 0x00000000L, 0x00010000L, 0x01010404L, + 0x01010004L, 0x00010404L, 0x00000004L, 0x00010000L, + 0x00000400L, 0x01010400L, 0x01010404L, 0x00000400L, + 0x01000404L, 0x01010004L, 0x01000000L, 0x00000004L, + 0x00000404L, 0x01000400L, 0x01000400L, 0x00010400L, + 0x00010400L, 0x01010000L, 0x01010000L, 0x01000404L, + 0x00010004L, 0x01000004L, 0x01000004L, 0x00010004L, + 0x00000000L, 0x00000404L, 0x00010404L, 0x01000000L, + 0x00010000L, 0x01010404L, 0x00000004L, 0x01010000L, + 0x01010400L, 0x01000000L, 0x01000000L, 0x00000400L, + 0x01010004L, 0x00010000L, 0x00010400L, 0x01000004L, + 0x00000400L, 0x00000004L, 0x01000404L, 0x00010404L, + 0x01010404L, 0x00010004L, 0x01010000L, 0x01000404L, + 0x01000004L, 0x00000404L, 0x00010404L, 0x01010400L, + 0x00000404L, 0x01000400L, 0x01000400L, 0x00000000L, + 0x00010004L, 0x00010400L, 0x00000000L, 0x01010004L }; + +static const unsigned long SP2[64] = { + 0x80108020L, 0x80008000L, 0x00008000L, 0x00108020L, + 0x00100000L, 0x00000020L, 0x80100020L, 0x80008020L, + 0x80000020L, 0x80108020L, 0x80108000L, 0x80000000L, + 0x80008000L, 0x00100000L, 0x00000020L, 0x80100020L, + 0x00108000L, 0x00100020L, 0x80008020L, 0x00000000L, + 0x80000000L, 0x00008000L, 0x00108020L, 0x80100000L, + 0x00100020L, 0x80000020L, 0x00000000L, 0x00108000L, + 0x00008020L, 0x80108000L, 0x80100000L, 0x00008020L, + 0x00000000L, 0x00108020L, 0x80100020L, 0x00100000L, + 0x80008020L, 0x80100000L, 0x80108000L, 0x00008000L, + 0x80100000L, 0x80008000L, 0x00000020L, 0x80108020L, + 0x00108020L, 0x00000020L, 0x00008000L, 0x80000000L, + 0x00008020L, 0x80108000L, 0x00100000L, 0x80000020L, + 0x00100020L, 0x80008020L, 0x80000020L, 0x00100020L, + 0x00108000L, 0x00000000L, 0x80008000L, 0x00008020L, + 0x80000000L, 0x80100020L, 0x80108020L, 0x00108000L }; + +static const unsigned long SP3[64] = { + 0x00000208L, 0x08020200L, 0x00000000L, 0x08020008L, + 0x08000200L, 0x00000000L, 0x00020208L, 0x08000200L, + 0x00020008L, 0x08000008L, 0x08000008L, 0x00020000L, + 0x08020208L, 0x00020008L, 0x08020000L, 0x00000208L, + 0x08000000L, 0x00000008L, 0x08020200L, 0x00000200L, + 0x00020200L, 0x08020000L, 0x08020008L, 0x00020208L, + 0x08000208L, 0x00020200L, 0x00020000L, 0x08000208L, + 0x00000008L, 0x08020208L, 0x00000200L, 0x08000000L, + 0x08020200L, 0x08000000L, 0x00020008L, 0x00000208L, + 0x00020000L, 0x08020200L, 0x08000200L, 0x00000000L, + 0x00000200L, 0x00020008L, 0x08020208L, 0x08000200L, + 0x08000008L, 0x00000200L, 0x00000000L, 0x08020008L, + 0x08000208L, 0x00020000L, 0x08000000L, 0x08020208L, + 0x00000008L, 0x00020208L, 0x00020200L, 0x08000008L, + 0x08020000L, 0x08000208L, 0x00000208L, 0x08020000L, + 0x00020208L, 0x00000008L, 0x08020008L, 0x00020200L }; + +static const unsigned long SP4[64] = { + 0x00802001L, 0x00002081L, 0x00002081L, 0x00000080L, + 0x00802080L, 0x00800081L, 0x00800001L, 0x00002001L, + 0x00000000L, 0x00802000L, 0x00802000L, 0x00802081L, + 0x00000081L, 0x00000000L, 0x00800080L, 0x00800001L, + 0x00000001L, 0x00002000L, 0x00800000L, 0x00802001L, + 0x00000080L, 0x00800000L, 0x00002001L, 0x00002080L, + 0x00800081L, 0x00000001L, 0x00002080L, 0x00800080L, + 0x00002000L, 0x00802080L, 0x00802081L, 0x00000081L, + 0x00800080L, 0x00800001L, 0x00802000L, 0x00802081L, + 0x00000081L, 0x00000000L, 0x00000000L, 0x00802000L, + 0x00002080L, 0x00800080L, 0x00800081L, 0x00000001L, + 0x00802001L, 0x00002081L, 0x00002081L, 0x00000080L, + 0x00802081L, 0x00000081L, 0x00000001L, 0x00002000L, + 0x00800001L, 0x00002001L, 0x00802080L, 0x00800081L, + 0x00002001L, 0x00002080L, 0x00800000L, 0x00802001L, + 0x00000080L, 0x00800000L, 0x00002000L, 0x00802080L }; + +static const unsigned long SP5[64] = { + 0x00000100L, 0x02080100L, 0x02080000L, 0x42000100L, + 0x00080000L, 0x00000100L, 0x40000000L, 0x02080000L, + 0x40080100L, 0x00080000L, 0x02000100L, 0x40080100L, + 0x42000100L, 0x42080000L, 0x00080100L, 0x40000000L, + 0x02000000L, 0x40080000L, 0x40080000L, 0x00000000L, + 0x40000100L, 0x42080100L, 0x42080100L, 0x02000100L, + 0x42080000L, 0x40000100L, 0x00000000L, 0x42000000L, + 0x02080100L, 0x02000000L, 0x42000000L, 0x00080100L, + 0x00080000L, 0x42000100L, 0x00000100L, 0x02000000L, + 0x40000000L, 0x02080000L, 0x42000100L, 0x40080100L, + 0x02000100L, 0x40000000L, 0x42080000L, 0x02080100L, + 0x40080100L, 0x00000100L, 0x02000000L, 0x42080000L, + 0x42080100L, 0x00080100L, 0x42000000L, 0x42080100L, + 0x02080000L, 0x00000000L, 0x40080000L, 0x42000000L, + 0x00080100L, 0x02000100L, 0x40000100L, 0x00080000L, + 0x00000000L, 0x40080000L, 0x02080100L, 0x40000100L }; + +static const unsigned long SP6[64] = { + 0x20000010L, 0x20400000L, 0x00004000L, 0x20404010L, + 0x20400000L, 0x00000010L, 0x20404010L, 0x00400000L, + 0x20004000L, 0x00404010L, 0x00400000L, 0x20000010L, + 0x00400010L, 0x20004000L, 0x20000000L, 0x00004010L, + 0x00000000L, 0x00400010L, 0x20004010L, 0x00004000L, + 0x00404000L, 0x20004010L, 0x00000010L, 0x20400010L, + 0x20400010L, 0x00000000L, 0x00404010L, 0x20404000L, + 0x00004010L, 0x00404000L, 0x20404000L, 0x20000000L, + 0x20004000L, 0x00000010L, 0x20400010L, 0x00404000L, + 0x20404010L, 0x00400000L, 0x00004010L, 0x20000010L, + 0x00400000L, 0x20004000L, 0x20000000L, 0x00004010L, + 0x20000010L, 0x20404010L, 0x00404000L, 0x20400000L, + 0x00404010L, 0x20404000L, 0x00000000L, 0x20400010L, + 0x00000010L, 0x00004000L, 0x20400000L, 0x00404010L, + 0x00004000L, 0x00400010L, 0x20004010L, 0x00000000L, + 0x20404000L, 0x20000000L, 0x00400010L, 0x20004010L }; + +static const unsigned long SP7[64] = { + 0x00200000L, 0x04200002L, 0x04000802L, 0x00000000L, + 0x00000800L, 0x04000802L, 0x00200802L, 0x04200800L, + 0x04200802L, 0x00200000L, 0x00000000L, 0x04000002L, + 0x00000002L, 0x04000000L, 0x04200002L, 0x00000802L, + 0x04000800L, 0x00200802L, 0x00200002L, 0x04000800L, + 0x04000002L, 0x04200000L, 0x04200800L, 0x00200002L, + 0x04200000L, 0x00000800L, 0x00000802L, 0x04200802L, + 0x00200800L, 0x00000002L, 0x04000000L, 0x00200800L, + 0x04000000L, 0x00200800L, 0x00200000L, 0x04000802L, + 0x04000802L, 0x04200002L, 0x04200002L, 0x00000002L, + 0x00200002L, 0x04000000L, 0x04000800L, 0x00200000L, + 0x04200800L, 0x00000802L, 0x00200802L, 0x04200800L, + 0x00000802L, 0x04000002L, 0x04200802L, 0x04200000L, + 0x00200800L, 0x00000000L, 0x00000002L, 0x04200802L, + 0x00000000L, 0x00200802L, 0x04200000L, 0x00000800L, + 0x04000002L, 0x04000800L, 0x00000800L, 0x00200002L }; + +static const unsigned long SP8[64] = { + 0x10001040L, 0x00001000L, 0x00040000L, 0x10041040L, + 0x10000000L, 0x10001040L, 0x00000040L, 0x10000000L, + 0x00040040L, 0x10040000L, 0x10041040L, 0x00041000L, + 0x10041000L, 0x00041040L, 0x00001000L, 0x00000040L, + 0x10040000L, 0x10000040L, 0x10001000L, 0x00001040L, + 0x00041000L, 0x00040040L, 0x10040040L, 0x10041000L, + 0x00001040L, 0x00000000L, 0x00000000L, 0x10040040L, + 0x10000040L, 0x10001000L, 0x00041040L, 0x00040000L, + 0x00041040L, 0x00040000L, 0x10041000L, 0x00001000L, + 0x00000040L, 0x10040040L, 0x00001000L, 0x00041040L, + 0x10001000L, 0x00000040L, 0x10000040L, 0x10040000L, + 0x10040040L, 0x10000000L, 0x00040000L, 0x10001040L, + 0x00000000L, 0x10041040L, 0x00040040L, 0x10000040L, + 0x10040000L, 0x10001000L, 0x10001040L, 0x00000000L, + 0x10041040L, 0x00041000L, 0x00041000L, 0x00001040L, + 0x00001040L, 0x00040040L, 0x10000000L, 0x10041000L }; + +static void desfunc(register unsigned long *block, register unsigned long *keys) +{ + register unsigned long fval, work, right, leftt; + register int round; + + leftt = block[0]; + right = block[1]; + work = ((leftt >> 4) ^ right) & 0x0f0f0f0fL; + right ^= work; + leftt ^= (work << 4); + work = ((leftt >> 16) ^ right) & 0x0000ffffL; + right ^= work; + leftt ^= (work << 16); + work = ((right >> 2) ^ leftt) & 0x33333333L; + leftt ^= work; + right ^= (work << 2); + work = ((right >> 8) ^ leftt) & 0x00ff00ffL; + leftt ^= work; + right ^= (work << 8); + right = ((right << 1) | ((right >> 31) & 1L)) & 0xffffffffL; + work = (leftt ^ right) & 0xaaaaaaaaL; + leftt ^= work; + right ^= work; + leftt = ((leftt << 1) | ((leftt >> 31) & 1L)) & 0xffffffffL; + + for( round = 0; round < 8; round++ ) { + work = (right << 28) | (right >> 4); + work ^= *keys++; + fval = SP7[ work & 0x3fL]; + fval |= SP5[(work >> 8) & 0x3fL]; + fval |= SP3[(work >> 16) & 0x3fL]; + fval |= SP1[(work >> 24) & 0x3fL]; + work = right ^ *keys++; + fval |= SP8[ work & 0x3fL]; + fval |= SP6[(work >> 8) & 0x3fL]; + fval |= SP4[(work >> 16) & 0x3fL]; + fval |= SP2[(work >> 24) & 0x3fL]; + leftt ^= fval; + work = (leftt << 28) | (leftt >> 4); + work ^= *keys++; + fval = SP7[ work & 0x3fL]; + fval |= SP5[(work >> 8) & 0x3fL]; + fval |= SP3[(work >> 16) & 0x3fL]; + fval |= SP1[(work >> 24) & 0x3fL]; + work = leftt ^ *keys++; + fval |= SP8[ work & 0x3fL]; + fval |= SP6[(work >> 8) & 0x3fL]; + fval |= SP4[(work >> 16) & 0x3fL]; + fval |= SP2[(work >> 24) & 0x3fL]; + right ^= fval; + } + + right = (right << 31) | (right >> 1); + work = (leftt ^ right) & 0xaaaaaaaaL; + leftt ^= work; + right ^= work; + leftt = (leftt << 31) | (leftt >> 1); + work = ((leftt >> 8) ^ right) & 0x00ff00ffL; + right ^= work; + leftt ^= (work << 8); + work = ((leftt >> 2) ^ right) & 0x33333333L; + right ^= work; + leftt ^= (work << 2); + work = ((right >> 16) ^ leftt) & 0x0000ffffL; + leftt ^= work; + right ^= (work << 16); + work = ((right >> 4) ^ leftt) & 0x0f0f0f0fL; + leftt ^= work; + right ^= (work << 4); + *block++ = right; + *block = leftt; + return; + } + +/* Validation sets: + * + * Single-length key, single-length plaintext - + * Key : 0123 4567 89ab cdef + * Plain : 0123 4567 89ab cde7 + * Cipher : c957 4425 6a5e d31d + * + * Double-length key, single-length plaintext - + * Key : 0123 4567 89ab cdef fedc ba98 7654 3210 + * Plain : 0123 4567 89ab cde7 + * Cipher : 7f1d 0a77 826b 8aff + * + * Double-length key, double-length plaintext - + * Key : 0123 4567 89ab cdef fedc ba98 7654 3210 + * Plain : 0123 4567 89ab cdef 0123 4567 89ab cdff + * Cipher : 27a0 8440 406a df60 278f 47cf 42d6 15d7 + * + * Triple-length key, single-length plaintext - + * Key : 0123 4567 89ab cdef fedc ba98 7654 3210 89ab cdef 0123 4567 + * Plain : 0123 4567 89ab cde7 + * Cipher : de0b 7c06 ae5e 0ed5 + * + * Triple-length key, double-length plaintext - + * Key : 0123 4567 89ab cdef fedc ba98 7654 3210 89ab cdef 0123 4567 + * Plain : 0123 4567 89ab cdef 0123 4567 89ab cdff + * Cipher : ad0d 1b30 ac17 cf07 0ed1 1c63 81e4 4de5 + * + * d3des V5.0a rwo 9208.07 18:44 Graven Imagery + **********************************************************************/ diff -Nru qemu-kvm-0.12.5+noroms/ui/d3des.h qemu-kvm-0.14.1/ui/d3des.h --- qemu-kvm-0.12.5+noroms/ui/d3des.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/ui/d3des.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,51 @@ +/* + * This is D3DES (V5.09) by Richard Outerbridge with the double and + * triple-length support removed for use in VNC. + * + * These changes are: + * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. + * + * This software 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. + */ + +/* d3des.h - + * + * Headers and defines for d3des.c + * Graven Imagery, 1992. + * + * Copyright (c) 1988,1989,1990,1991,1992 by Richard Outerbridge + * (GEnie : OUTER; CIS : [71755,204]) + */ + +#define EN0 0 /* MODE == encrypt */ +#define DE1 1 /* MODE == decrypt */ + +void deskey(unsigned char *, int); +/* hexkey[8] MODE + * Sets the internal key register according to the hexadecimal + * key contained in the 8 bytes of hexkey, according to the DES, + * for encryption or decryption according to MODE. + */ + +void usekey(unsigned long *); +/* cookedkey[32] + * Loads the internal key register with the data in cookedkey. + */ + +void cpkey(unsigned long *); +/* cookedkey[32] + * Copies the contents of the internal key register into the storage + * located at &cookedkey[0]. + */ + +void des(unsigned char *, unsigned char *); +/* from[8] to[8] + * Encrypts/Decrypts (according to the key currently loaded in the + * internal key register) one block of eight bytes at address 'from' + * into the block at address 'to'. They can be the same. + */ + +/* d3des.h V5.09 rwo 9208.04 15:06 Graven Imagery + ********************************************************************/ diff -Nru qemu-kvm-0.12.5+noroms/ui/keymaps.c qemu-kvm-0.14.1/ui/keymaps.c --- qemu-kvm-0.12.5+noroms/ui/keymaps.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/ui/keymaps.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,210 @@ +/* + * QEMU keysym to keycode conversion using rdesktop keymaps + * + * Copyright (c) 2004 Johannes Schindelin + * + * 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 "keymaps.h" +#include "sysemu.h" + +static int get_keysym(const name2keysym_t *table, + const char *name) +{ + const name2keysym_t *p; + for(p = table; p->name != NULL; p++) { + if (!strcmp(p->name, name)) + return p->keysym; + } + return 0; +} + + +static void add_to_key_range(struct key_range **krp, int code) { + struct key_range *kr; + for (kr = *krp; kr; kr = kr->next) { + if (code >= kr->start && code <= kr->end) + break; + if (code == kr->start - 1) { + kr->start--; + break; + } + if (code == kr->end + 1) { + kr->end++; + break; + } + } + if (kr == NULL) { + kr = qemu_mallocz(sizeof(*kr)); + kr->start = kr->end = code; + kr->next = *krp; + *krp = kr; + } +} + +static void add_keysym(char *line, int keysym, int keycode, kbd_layout_t *k) { + if (keysym < MAX_NORMAL_KEYCODE) { + //fprintf(stderr,"Setting keysym %s (%d) to %d\n",line,keysym,keycode); + k->keysym2keycode[keysym] = keycode; + } else { + if (k->extra_count >= MAX_EXTRA_COUNT) { + fprintf(stderr, + "Warning: Could not assign keysym %s (0x%x) because of memory constraints.\n", + line, keysym); + } else { +#if 0 + fprintf(stderr, "Setting %d: %d,%d\n", + k->extra_count, keysym, keycode); +#endif + k->keysym2keycode_extra[k->extra_count]. + keysym = keysym; + k->keysym2keycode_extra[k->extra_count]. + keycode = keycode; + k->extra_count++; + } + } +} + +static kbd_layout_t *parse_keyboard_layout(const name2keysym_t *table, + const char *language, + kbd_layout_t * k) +{ + FILE *f; + char * filename; + char line[1024]; + int len; + + filename = qemu_find_file(QEMU_FILE_TYPE_KEYMAP, language); + + if (!k) + k = qemu_mallocz(sizeof(kbd_layout_t)); + if (!(filename && (f = fopen(filename, "r")))) { + fprintf(stderr, + "Could not read keymap file: '%s'\n", language); + return NULL; + } + qemu_free(filename); + for(;;) { + if (fgets(line, 1024, f) == NULL) + break; + len = strlen(line); + if (len > 0 && line[len - 1] == '\n') + line[len - 1] = '\0'; + if (line[0] == '#') + continue; + if (!strncmp(line, "map ", 4)) + continue; + if (!strncmp(line, "include ", 8)) { + parse_keyboard_layout(table, line + 8, k); + } else { + char *end_of_keysym = line; + while (*end_of_keysym != 0 && *end_of_keysym != ' ') + end_of_keysym++; + if (*end_of_keysym) { + int keysym; + *end_of_keysym = 0; + keysym = get_keysym(table, line); + if (keysym == 0) { + // fprintf(stderr, "Warning: unknown keysym %s\n", line); + } else { + const char *rest = end_of_keysym + 1; + char *rest2; + int keycode = strtol(rest, &rest2, 0); + + if (rest && strstr(rest, "numlock")) { + add_to_key_range(&k->keypad_range, keycode); + add_to_key_range(&k->numlock_range, keysym); + //fprintf(stderr, "keypad keysym %04x keycode %d\n", keysym, keycode); + } + + if (rest && strstr(rest, "shift")) + keycode |= SCANCODE_SHIFT; + if (rest && strstr(rest, "altgr")) + keycode |= SCANCODE_ALTGR; + if (rest && strstr(rest, "ctrl")) + keycode |= SCANCODE_CTRL; + + add_keysym(line, keysym, keycode, k); + + if (rest && strstr(rest, "addupper")) { + char *c; + for (c = line; *c; c++) + *c = toupper(*c); + keysym = get_keysym(table, line); + if (keysym) + add_keysym(line, keysym, keycode | SCANCODE_SHIFT, k); + } + } + } + } + } + fclose(f); + return k; +} + + +void *init_keyboard_layout(const name2keysym_t *table, const char *language) +{ + return parse_keyboard_layout(table, language, NULL); +} + + +int keysym2scancode(void *kbd_layout, int keysym) +{ + kbd_layout_t *k = kbd_layout; + if (keysym < MAX_NORMAL_KEYCODE) { + if (k->keysym2keycode[keysym] == 0) + fprintf(stderr, "Warning: no scancode found for keysym %d\n", + keysym); + return k->keysym2keycode[keysym]; + } else { + int i; +#ifdef XK_ISO_Left_Tab + if (keysym == XK_ISO_Left_Tab) + keysym = XK_Tab; +#endif + for (i = 0; i < k->extra_count; i++) + if (k->keysym2keycode_extra[i].keysym == keysym) + return k->keysym2keycode_extra[i].keycode; + } + return 0; +} + +int keycode_is_keypad(void *kbd_layout, int keycode) +{ + kbd_layout_t *k = kbd_layout; + struct key_range *kr; + + for (kr = k->keypad_range; kr; kr = kr->next) + if (keycode >= kr->start && keycode <= kr->end) + return 1; + return 0; +} + +int keysym_is_numlock(void *kbd_layout, int keysym) +{ + kbd_layout_t *k = kbd_layout; + struct key_range *kr; + + for (kr = k->numlock_range; kr; kr = kr->next) + if (keysym >= kr->start && keysym <= kr->end) + return 1; + return 0; +} diff -Nru qemu-kvm-0.12.5+noroms/ui/keymaps.h qemu-kvm-0.14.1/ui/keymaps.h --- qemu-kvm-0.12.5+noroms/ui/keymaps.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/ui/keymaps.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,77 @@ +/* + * QEMU keysym to keycode conversion using rdesktop keymaps + * + * Copyright (c) 2004 Johannes Schindelin + * + * 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. + */ + +#ifndef __QEMU_KEYMAPS_H__ +#define __QEMU_KEYMAPS_H__ + +#include "qemu-common.h" + +typedef struct { + const char* name; + int keysym; +} name2keysym_t; + +struct key_range { + int start; + int end; + struct key_range *next; +}; + +#define MAX_NORMAL_KEYCODE 512 +#define MAX_EXTRA_COUNT 256 +typedef struct { + uint16_t keysym2keycode[MAX_NORMAL_KEYCODE]; + struct { + int keysym; + uint16_t keycode; + } keysym2keycode_extra[MAX_EXTRA_COUNT]; + int extra_count; + struct key_range *keypad_range; + struct key_range *numlock_range; +} kbd_layout_t; + +/* scancode without modifiers */ +#define SCANCODE_KEYMASK 0xff +/* scancode without grey or up bit */ +#define SCANCODE_KEYCODEMASK 0x7f + +/* "grey" keys will usually need a 0xe0 prefix */ +#define SCANCODE_GREY 0x80 +#define SCANCODE_EMUL0 0xE0 +/* "up" flag */ +#define SCANCODE_UP 0x80 + +/* Additional modifiers to use if not catched another way. */ +#define SCANCODE_SHIFT 0x100 +#define SCANCODE_CTRL 0x200 +#define SCANCODE_ALT 0x400 +#define SCANCODE_ALTGR 0x800 + + +void *init_keyboard_layout(const name2keysym_t *table, const char *language); +int keysym2scancode(void *kbd_layout, int keysym); +int keycode_is_keypad(void *kbd_layout, int keycode); +int keysym_is_numlock(void *kbd_layout, int keysym); + +#endif /* __QEMU_KEYMAPS_H__ */ diff -Nru qemu-kvm-0.12.5+noroms/ui/qemu-spice.h qemu-kvm-0.14.1/ui/qemu-spice.h --- qemu-kvm-0.12.5+noroms/ui/qemu-spice.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/ui/qemu-spice.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2010 Red Hat, Inc. + * + * 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 or + * (at your option) version 3 of the License. + * + * 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, see . + */ + +#ifndef QEMU_SPICE_H +#define QEMU_SPICE_H + +#ifdef CONFIG_SPICE + +#include + +#include "qemu-option.h" +#include "qemu-config.h" +#include "qemu-char.h" + +extern int using_spice; + +void qemu_spice_init(void); +void qemu_spice_input_init(void); +void qemu_spice_audio_init(void); +void qemu_spice_display_init(DisplayState *ds); +int qemu_spice_add_interface(SpiceBaseInstance *sin); +int qemu_spice_set_passwd(const char *passwd, + bool fail_if_connected, bool disconnect_if_connected); +int qemu_spice_set_pw_expire(time_t expires); +int qemu_spice_migrate_info(const char *hostname, int port, int tls_port, + const char *subject); + +void do_info_spice_print(Monitor *mon, const QObject *data); +void do_info_spice(Monitor *mon, QObject **ret_data); + +CharDriverState *qemu_chr_open_spice(QemuOpts *opts); + +#else /* CONFIG_SPICE */ + +#define using_spice 0 +#define qemu_spice_set_passwd(_p, _f1, _f2) (-1) +#define qemu_spice_set_pw_expire(_e) (-1) +static inline int qemu_spice_migrate_info(const char *h, int p, int t, const char *s) +{ return -1; } + +#endif /* CONFIG_SPICE */ + +#endif /* QEMU_SPICE_H */ diff -Nru qemu-kvm-0.12.5+noroms/ui/sdl.c qemu-kvm-0.14.1/ui/sdl.c --- qemu-kvm-0.12.5+noroms/ui/sdl.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/ui/sdl.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,885 @@ +/* + * QEMU SDL display driver + * + * Copyright (c) 2003 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. + */ + +/* Avoid compiler warning because macro is redefined in SDL_syswm.h. */ +#undef WIN32_LEAN_AND_MEAN + +#include +#include + +#ifndef _WIN32 +#include +#endif + +#include "qemu-common.h" +#include "console.h" +#include "sysemu.h" +#include "x_keymap.h" +#include "sdl_zoom.h" + +static DisplayChangeListener *dcl; +static SDL_Surface *real_screen; +static SDL_Surface *guest_screen = NULL; +static int gui_grab; /* if true, all keyboard/mouse events are grabbed */ +static int last_vm_running; +static int gui_saved_grab; +static int gui_fullscreen; +static int gui_noframe; +static int gui_key_modifier_pressed; +static int gui_keysym; +static int gui_fullscreen_initial_grab; +static int gui_grab_code = KMOD_LALT | KMOD_LCTRL; +static uint8_t modifiers_state[256]; +static int width, height; +static SDL_Cursor *sdl_cursor_normal; +static SDL_Cursor *sdl_cursor_hidden; +static int absolute_enabled = 0; +static int guest_cursor = 0; +static int guest_x, guest_y; +static SDL_Cursor *guest_sprite = NULL; +static uint8_t allocator; +static SDL_PixelFormat host_format; +static int scaling_active = 0; +static Notifier mouse_mode_notifier; + +static void sdl_update(DisplayState *ds, int x, int y, int w, int h) +{ + // printf("updating x=%d y=%d w=%d h=%d\n", x, y, w, h); + SDL_Rect rec; + rec.x = x; + rec.y = y; + rec.w = w; + rec.h = h; + + if (guest_screen) { + if (!scaling_active) { + SDL_BlitSurface(guest_screen, &rec, real_screen, &rec); + } else { + if (sdl_zoom_blit(guest_screen, real_screen, SMOOTHING_ON, &rec) < 0) { + fprintf(stderr, "Zoom blit failed\n"); + exit(1); + } + } + } + SDL_UpdateRect(real_screen, rec.x, rec.y, rec.w, rec.h); +} + +static void sdl_setdata(DisplayState *ds) +{ + if (guest_screen != NULL) SDL_FreeSurface(guest_screen); + + guest_screen = SDL_CreateRGBSurfaceFrom(ds_get_data(ds), ds_get_width(ds), ds_get_height(ds), + ds_get_bits_per_pixel(ds), ds_get_linesize(ds), + ds->surface->pf.rmask, ds->surface->pf.gmask, + ds->surface->pf.bmask, ds->surface->pf.amask); +} + +static void do_sdl_resize(int new_width, int new_height, int bpp) +{ + int flags; + + // printf("resizing to %d %d\n", w, h); + + flags = SDL_HWSURFACE|SDL_ASYNCBLIT|SDL_HWACCEL|SDL_RESIZABLE; + if (gui_fullscreen) + flags |= SDL_FULLSCREEN; + if (gui_noframe) + flags |= SDL_NOFRAME; + + width = new_width; + height = new_height; + real_screen = SDL_SetVideoMode(width, height, bpp, flags); + if (!real_screen) { + fprintf(stderr, "Could not open SDL display (%dx%dx%d): %s\n", width, + height, bpp, SDL_GetError()); + exit(1); + } +} + +static void sdl_resize(DisplayState *ds) +{ + if (!allocator) { + if (!scaling_active) + do_sdl_resize(ds_get_width(ds), ds_get_height(ds), 0); + else if (real_screen->format->BitsPerPixel != ds_get_bits_per_pixel(ds)) + do_sdl_resize(real_screen->w, real_screen->h, ds_get_bits_per_pixel(ds)); + sdl_setdata(ds); + } else { + if (guest_screen != NULL) { + SDL_FreeSurface(guest_screen); + guest_screen = NULL; + } + } +} + +static PixelFormat sdl_to_qemu_pixelformat(SDL_PixelFormat *sdl_pf) +{ + PixelFormat qemu_pf; + + memset(&qemu_pf, 0x00, sizeof(PixelFormat)); + + qemu_pf.bits_per_pixel = sdl_pf->BitsPerPixel; + qemu_pf.bytes_per_pixel = sdl_pf->BytesPerPixel; + qemu_pf.depth = (qemu_pf.bits_per_pixel) == 32 ? 24 : (qemu_pf.bits_per_pixel); + + qemu_pf.rmask = sdl_pf->Rmask; + qemu_pf.gmask = sdl_pf->Gmask; + qemu_pf.bmask = sdl_pf->Bmask; + qemu_pf.amask = sdl_pf->Amask; + + qemu_pf.rshift = sdl_pf->Rshift; + qemu_pf.gshift = sdl_pf->Gshift; + qemu_pf.bshift = sdl_pf->Bshift; + qemu_pf.ashift = sdl_pf->Ashift; + + qemu_pf.rbits = 8 - sdl_pf->Rloss; + qemu_pf.gbits = 8 - sdl_pf->Gloss; + qemu_pf.bbits = 8 - sdl_pf->Bloss; + qemu_pf.abits = 8 - sdl_pf->Aloss; + + qemu_pf.rmax = ((1 << qemu_pf.rbits) - 1); + qemu_pf.gmax = ((1 << qemu_pf.gbits) - 1); + qemu_pf.bmax = ((1 << qemu_pf.bbits) - 1); + qemu_pf.amax = ((1 << qemu_pf.abits) - 1); + + return qemu_pf; +} + +static DisplaySurface* sdl_create_displaysurface(int width, int height) +{ + DisplaySurface *surface = (DisplaySurface*) qemu_mallocz(sizeof(DisplaySurface)); + if (surface == NULL) { + fprintf(stderr, "sdl_create_displaysurface: malloc failed\n"); + exit(1); + } + + surface->width = width; + surface->height = height; + + if (scaling_active) { + if (host_format.BytesPerPixel != 2 && host_format.BytesPerPixel != 4) { + surface->linesize = width * 4; + surface->pf = qemu_default_pixelformat(32); + } else { + surface->linesize = width * host_format.BytesPerPixel; + surface->pf = sdl_to_qemu_pixelformat(&host_format); + } +#ifdef HOST_WORDS_BIGENDIAN + surface->flags = QEMU_ALLOCATED_FLAG | QEMU_BIG_ENDIAN_FLAG; +#else + surface->flags = QEMU_ALLOCATED_FLAG; +#endif + surface->data = (uint8_t*) qemu_mallocz(surface->linesize * surface->height); + + return surface; + } + + if (host_format.BitsPerPixel == 16) + do_sdl_resize(width, height, 16); + else + do_sdl_resize(width, height, 32); + + surface->pf = sdl_to_qemu_pixelformat(real_screen->format); + surface->linesize = real_screen->pitch; + surface->data = real_screen->pixels; + +#ifdef HOST_WORDS_BIGENDIAN + surface->flags = QEMU_REALPIXELS_FLAG | QEMU_BIG_ENDIAN_FLAG; +#else + surface->flags = QEMU_REALPIXELS_FLAG; +#endif + allocator = 1; + + return surface; +} + +static void sdl_free_displaysurface(DisplaySurface *surface) +{ + allocator = 0; + if (surface == NULL) + return; + + if (surface->flags & QEMU_ALLOCATED_FLAG) + qemu_free(surface->data); + qemu_free(surface); +} + +static DisplaySurface* sdl_resize_displaysurface(DisplaySurface *surface, int width, int height) +{ + sdl_free_displaysurface(surface); + return sdl_create_displaysurface(width, height); +} + +/* generic keyboard conversion */ + +#include "sdl_keysym.h" + +static kbd_layout_t *kbd_layout = NULL; + +static uint8_t sdl_keyevent_to_keycode_generic(const SDL_KeyboardEvent *ev) +{ + int keysym; + /* workaround for X11+SDL bug with AltGR */ + keysym = ev->keysym.sym; + if (keysym == 0 && ev->keysym.scancode == 113) + keysym = SDLK_MODE; + /* For Japanese key '\' and '|' */ + if (keysym == 92 && ev->keysym.scancode == 133) { + keysym = 0xa5; + } + return keysym2scancode(kbd_layout, keysym) & SCANCODE_KEYMASK; +} + +/* specific keyboard conversions from scan codes */ + +#if defined(_WIN32) + +static uint8_t sdl_keyevent_to_keycode(const SDL_KeyboardEvent *ev) +{ + return ev->keysym.scancode; +} + +#else + +#if defined(SDL_VIDEO_DRIVER_X11) +#include + +static int check_for_evdev(void) +{ + SDL_SysWMinfo info; + XkbDescPtr desc = NULL; + int has_evdev = 0; + char *keycodes = NULL; + + SDL_VERSION(&info.version); + if (!SDL_GetWMInfo(&info)) { + return 0; + } + desc = XkbGetKeyboard(info.info.x11.display, + XkbGBN_AllComponentsMask, + XkbUseCoreKbd); + if (desc && desc->names) { + keycodes = XGetAtomName(info.info.x11.display, desc->names->keycodes); + if (keycodes == NULL) { + fprintf(stderr, "could not lookup keycode name\n"); + } else if (strstart(keycodes, "evdev", NULL)) { + has_evdev = 1; + } else if (!strstart(keycodes, "xfree86", NULL)) { + fprintf(stderr, "unknown keycodes `%s', please report to " + "qemu-devel@nongnu.org\n", keycodes); + } + } + + if (desc) { + XkbFreeKeyboard(desc, XkbGBN_AllComponentsMask, True); + } + if (keycodes) { + XFree(keycodes); + } + return has_evdev; +} +#else +static int check_for_evdev(void) +{ + return 0; +} +#endif + +static uint8_t sdl_keyevent_to_keycode(const SDL_KeyboardEvent *ev) +{ + int keycode; + static int has_evdev = -1; + + if (has_evdev == -1) + has_evdev = check_for_evdev(); + + keycode = ev->keysym.scancode; + + if (keycode < 9) { + keycode = 0; + } else if (keycode < 97) { + keycode -= 8; /* just an offset */ + } else if (keycode < 158) { + /* use conversion table */ + if (has_evdev) + keycode = translate_evdev_keycode(keycode - 97); + else + keycode = translate_xfree86_keycode(keycode - 97); + } else if (keycode == 208) { /* Hiragana_Katakana */ + keycode = 0x70; + } else if (keycode == 211) { /* backslash */ + keycode = 0x73; + } else { + keycode = 0; + } + return keycode; +} + +#endif + +static void reset_keys(void) +{ + int i; + for(i = 0; i < 256; i++) { + if (modifiers_state[i]) { + if (i & SCANCODE_GREY) + kbd_put_keycode(SCANCODE_EMUL0); + kbd_put_keycode(i | SCANCODE_UP); + modifiers_state[i] = 0; + } + } +} + +static void sdl_process_key(SDL_KeyboardEvent *ev) +{ + int keycode, v; + + if (ev->keysym.sym == SDLK_PAUSE) { + /* specific case */ + v = 0; + if (ev->type == SDL_KEYUP) + v |= SCANCODE_UP; + kbd_put_keycode(0xe1); + kbd_put_keycode(0x1d | v); + kbd_put_keycode(0x45 | v); + return; + } + + if (kbd_layout) { + keycode = sdl_keyevent_to_keycode_generic(ev); + } else { + keycode = sdl_keyevent_to_keycode(ev); + } + + switch(keycode) { + case 0x00: + /* sent when leaving window: reset the modifiers state */ + reset_keys(); + return; + case 0x2a: /* Left Shift */ + case 0x36: /* Right Shift */ + case 0x1d: /* Left CTRL */ + case 0x9d: /* Right CTRL */ + case 0x38: /* Left ALT */ + case 0xb8: /* Right ALT */ + if (ev->type == SDL_KEYUP) + modifiers_state[keycode] = 0; + else + modifiers_state[keycode] = 1; + break; +#define QEMU_SDL_VERSION ((SDL_MAJOR_VERSION << 8) + SDL_MINOR_VERSION) +#if QEMU_SDL_VERSION < 0x102 || QEMU_SDL_VERSION == 0x102 && SDL_PATCHLEVEL < 14 + /* SDL versions before 1.2.14 don't support key up for caps/num lock. */ + case 0x45: /* num lock */ + case 0x3a: /* caps lock */ + /* SDL does not send the key up event, so we generate it */ + kbd_put_keycode(keycode); + kbd_put_keycode(keycode | SCANCODE_UP); + return; +#endif + } + + /* now send the key code */ + if (keycode & SCANCODE_GREY) + kbd_put_keycode(SCANCODE_EMUL0); + if (ev->type == SDL_KEYUP) + kbd_put_keycode(keycode | SCANCODE_UP); + else + kbd_put_keycode(keycode & SCANCODE_KEYCODEMASK); +} + +static void sdl_update_caption(void) +{ + char win_title[1024]; + char icon_title[1024]; + const char *status = ""; + + if (!vm_running) + status = " [Stopped]"; + else if (gui_grab) { + if (alt_grab) + status = " - Press Ctrl-Alt-Shift to exit mouse grab"; + else if (ctrl_grab) + status = " - Press Right-Ctrl to exit mouse grab"; + else + status = " - Press Ctrl-Alt to exit mouse grab"; + } + + if (qemu_name) { + snprintf(win_title, sizeof(win_title), "QEMU (%s)%s", qemu_name, status); + snprintf(icon_title, sizeof(icon_title), "QEMU (%s)", qemu_name); + } else { + snprintf(win_title, sizeof(win_title), "QEMU%s", status); + snprintf(icon_title, sizeof(icon_title), "QEMU"); + } + + SDL_WM_SetCaption(win_title, icon_title); +} + +static void sdl_hide_cursor(void) +{ + if (!cursor_hide) + return; + + if (kbd_mouse_is_absolute()) { + SDL_ShowCursor(1); + SDL_SetCursor(sdl_cursor_hidden); + } else { + SDL_ShowCursor(0); + } +} + +static void sdl_show_cursor(void) +{ + if (!cursor_hide) + return; + + if (!kbd_mouse_is_absolute()) { + SDL_ShowCursor(1); + if (guest_cursor && + (gui_grab || kbd_mouse_is_absolute() || absolute_enabled)) + SDL_SetCursor(guest_sprite); + else + SDL_SetCursor(sdl_cursor_normal); + } +} + +static void sdl_grab_start(void) +{ + if (guest_cursor) { + SDL_SetCursor(guest_sprite); + if (!kbd_mouse_is_absolute() && !absolute_enabled) + SDL_WarpMouse(guest_x, guest_y); + } else + sdl_hide_cursor(); + + if (SDL_WM_GrabInput(SDL_GRAB_ON) == SDL_GRAB_ON) { + gui_grab = 1; + sdl_update_caption(); + } else + sdl_show_cursor(); +} + +static void sdl_grab_end(void) +{ + SDL_WM_GrabInput(SDL_GRAB_OFF); + gui_grab = 0; + sdl_show_cursor(); + sdl_update_caption(); +} + +static void sdl_mouse_mode_change(Notifier *notify) +{ + if (kbd_mouse_is_absolute()) { + if (!absolute_enabled) { + sdl_hide_cursor(); + if (gui_grab) { + sdl_grab_end(); + } + absolute_enabled = 1; + } + } else if (absolute_enabled) { + sdl_show_cursor(); + absolute_enabled = 0; + } +} + +static void sdl_send_mouse_event(int dx, int dy, int dz, int x, int y, int state) +{ + int buttons; + buttons = 0; + if (state & SDL_BUTTON(SDL_BUTTON_LEFT)) + buttons |= MOUSE_EVENT_LBUTTON; + if (state & SDL_BUTTON(SDL_BUTTON_RIGHT)) + buttons |= MOUSE_EVENT_RBUTTON; + if (state & SDL_BUTTON(SDL_BUTTON_MIDDLE)) + buttons |= MOUSE_EVENT_MBUTTON; + + if (kbd_mouse_is_absolute()) { + dx = x * 0x7FFF / (width - 1); + dy = y * 0x7FFF / (height - 1); + } else if (guest_cursor) { + x -= guest_x; + y -= guest_y; + guest_x += x; + guest_y += y; + dx = x; + dy = y; + } + + kbd_mouse_event(dx, dy, dz, buttons); +} + +static void toggle_full_screen(DisplayState *ds) +{ + gui_fullscreen = !gui_fullscreen; + do_sdl_resize(real_screen->w, real_screen->h, real_screen->format->BitsPerPixel); + if (gui_fullscreen) { + scaling_active = 0; + gui_saved_grab = gui_grab; + sdl_grab_start(); + } else { + if (!gui_saved_grab) + sdl_grab_end(); + } + vga_hw_invalidate(); + vga_hw_update(); +} + +static void sdl_refresh(DisplayState *ds) +{ + SDL_Event ev1, *ev = &ev1; + int mod_state; + int buttonstate = SDL_GetMouseState(NULL, NULL); + + if (last_vm_running != vm_running) { + last_vm_running = vm_running; + sdl_update_caption(); + } + + vga_hw_update(); + SDL_EnableUNICODE(!is_graphic_console()); + + while (SDL_PollEvent(ev)) { + switch (ev->type) { + case SDL_VIDEOEXPOSE: + sdl_update(ds, 0, 0, real_screen->w, real_screen->h); + break; + case SDL_KEYDOWN: + case SDL_KEYUP: + if (ev->type == SDL_KEYDOWN) { + if (alt_grab) { + mod_state = (SDL_GetModState() & (gui_grab_code | KMOD_LSHIFT)) == + (gui_grab_code | KMOD_LSHIFT); + } else if (ctrl_grab) { + mod_state = (SDL_GetModState() & KMOD_RCTRL) == KMOD_RCTRL; + } else { + mod_state = (SDL_GetModState() & gui_grab_code) == + gui_grab_code; + } + gui_key_modifier_pressed = mod_state; + if (gui_key_modifier_pressed) { + int keycode; + keycode = sdl_keyevent_to_keycode(&ev->key); + switch(keycode) { + case 0x21: /* 'f' key on US keyboard */ + toggle_full_screen(ds); + gui_keysym = 1; + break; + case 0x16: /* 'u' key on US keyboard */ + scaling_active = 0; + sdl_resize(ds); + vga_hw_invalidate(); + vga_hw_update(); + break; + case 0x02 ... 0x0a: /* '1' to '9' keys */ + /* Reset the modifiers sent to the current console */ + reset_keys(); + console_select(keycode - 0x02); + if (!is_graphic_console()) { + /* display grab if going to a text console */ + if (gui_grab) + sdl_grab_end(); + } + gui_keysym = 1; + break; + default: + break; + } + } else if (!is_graphic_console()) { + int keysym; + keysym = 0; + if (ev->key.keysym.mod & (KMOD_LCTRL | KMOD_RCTRL)) { + switch(ev->key.keysym.sym) { + case SDLK_UP: keysym = QEMU_KEY_CTRL_UP; break; + case SDLK_DOWN: keysym = QEMU_KEY_CTRL_DOWN; break; + case SDLK_LEFT: keysym = QEMU_KEY_CTRL_LEFT; break; + case SDLK_RIGHT: keysym = QEMU_KEY_CTRL_RIGHT; break; + case SDLK_HOME: keysym = QEMU_KEY_CTRL_HOME; break; + case SDLK_END: keysym = QEMU_KEY_CTRL_END; break; + case SDLK_PAGEUP: keysym = QEMU_KEY_CTRL_PAGEUP; break; + case SDLK_PAGEDOWN: keysym = QEMU_KEY_CTRL_PAGEDOWN; break; + default: break; + } + } else { + switch(ev->key.keysym.sym) { + case SDLK_UP: keysym = QEMU_KEY_UP; break; + case SDLK_DOWN: keysym = QEMU_KEY_DOWN; break; + case SDLK_LEFT: keysym = QEMU_KEY_LEFT; break; + case SDLK_RIGHT: keysym = QEMU_KEY_RIGHT; break; + case SDLK_HOME: keysym = QEMU_KEY_HOME; break; + case SDLK_END: keysym = QEMU_KEY_END; break; + case SDLK_PAGEUP: keysym = QEMU_KEY_PAGEUP; break; + case SDLK_PAGEDOWN: keysym = QEMU_KEY_PAGEDOWN; break; + case SDLK_BACKSPACE: keysym = QEMU_KEY_BACKSPACE; break; + case SDLK_DELETE: keysym = QEMU_KEY_DELETE; break; + default: break; + } + } + if (keysym) { + kbd_put_keysym(keysym); + } else if (ev->key.keysym.unicode != 0) { + kbd_put_keysym(ev->key.keysym.unicode); + } + } + } else if (ev->type == SDL_KEYUP) { + if (!alt_grab) { + mod_state = (ev->key.keysym.mod & gui_grab_code); + } else { + mod_state = (ev->key.keysym.mod & + (gui_grab_code | KMOD_LSHIFT)); + } + if (!mod_state) { + if (gui_key_modifier_pressed) { + gui_key_modifier_pressed = 0; + if (gui_keysym == 0) { + /* exit/enter grab if pressing Ctrl-Alt */ + if (!gui_grab) { + /* if the application is not active, + do not try to enter grab state. It + prevents + 'SDL_WM_GrabInput(SDL_GRAB_ON)' + from blocking all the application + (SDL bug). */ + if (SDL_GetAppState() & SDL_APPACTIVE) + sdl_grab_start(); + } else { + sdl_grab_end(); + } + /* SDL does not send back all the + modifiers key, so we must correct it */ + reset_keys(); + break; + } + gui_keysym = 0; + } + } + } + if (is_graphic_console() && !gui_keysym) + sdl_process_key(&ev->key); + break; + case SDL_QUIT: + if (!no_quit) + qemu_system_shutdown_request(); + break; + case SDL_MOUSEMOTION: + if (gui_grab || kbd_mouse_is_absolute() || + absolute_enabled) { + sdl_send_mouse_event(ev->motion.xrel, ev->motion.yrel, 0, + ev->motion.x, ev->motion.y, ev->motion.state); + } + break; + case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEBUTTONUP: + { + SDL_MouseButtonEvent *bev = &ev->button; + if (!gui_grab && !kbd_mouse_is_absolute()) { + if (ev->type == SDL_MOUSEBUTTONDOWN && + (bev->button == SDL_BUTTON_LEFT)) { + /* start grabbing all events */ + sdl_grab_start(); + } + } else { + int dz; + dz = 0; + if (ev->type == SDL_MOUSEBUTTONDOWN) { + buttonstate |= SDL_BUTTON(bev->button); + } else { + buttonstate &= ~SDL_BUTTON(bev->button); + } +#ifdef SDL_BUTTON_WHEELUP + if (bev->button == SDL_BUTTON_WHEELUP && ev->type == SDL_MOUSEBUTTONDOWN) { + dz = -1; + } else if (bev->button == SDL_BUTTON_WHEELDOWN && ev->type == SDL_MOUSEBUTTONDOWN) { + dz = 1; + } +#endif + sdl_send_mouse_event(0, 0, dz, bev->x, bev->y, buttonstate); + } + } + break; + case SDL_ACTIVEEVENT: + if (gui_grab && ev->active.state == SDL_APPINPUTFOCUS && + !ev->active.gain && !gui_fullscreen_initial_grab) { + sdl_grab_end(); + } + if (ev->active.state & SDL_APPACTIVE) { + if (ev->active.gain) { + /* Back to default interval */ + dcl->gui_timer_interval = 0; + dcl->idle = 0; + } else { + /* Sleeping interval */ + dcl->gui_timer_interval = 500; + dcl->idle = 1; + } + } + break; + case SDL_VIDEORESIZE: + { + SDL_ResizeEvent *rev = &ev->resize; + int bpp = real_screen->format->BitsPerPixel; + if (bpp != 16 && bpp != 32) + bpp = 32; + do_sdl_resize(rev->w, rev->h, bpp); + scaling_active = 1; + if (!is_buffer_shared(ds->surface)) { + ds->surface = qemu_resize_displaysurface(ds, ds_get_width(ds), ds_get_height(ds)); + dpy_resize(ds); + } + vga_hw_invalidate(); + vga_hw_update(); + break; + } + default: + break; + } + } +} + +static void sdl_fill(DisplayState *ds, int x, int y, int w, int h, uint32_t c) +{ + SDL_Rect dst = { x, y, w, h }; + SDL_FillRect(real_screen, &dst, c); +} + +static void sdl_mouse_warp(int x, int y, int on) +{ + if (on) { + if (!guest_cursor) + sdl_show_cursor(); + if (gui_grab || kbd_mouse_is_absolute() || absolute_enabled) { + SDL_SetCursor(guest_sprite); + if (!kbd_mouse_is_absolute() && !absolute_enabled) + SDL_WarpMouse(x, y); + } + } else if (gui_grab) + sdl_hide_cursor(); + guest_cursor = on; + guest_x = x, guest_y = y; +} + +static void sdl_mouse_define(QEMUCursor *c) +{ + uint8_t *image, *mask; + int bpl; + + if (guest_sprite) + SDL_FreeCursor(guest_sprite); + + bpl = cursor_get_mono_bpl(c); + image = qemu_mallocz(bpl * c->height); + mask = qemu_mallocz(bpl * c->height); + cursor_get_mono_image(c, 0x000000, image); + cursor_get_mono_mask(c, 0, mask); + guest_sprite = SDL_CreateCursor(image, mask, c->width, c->height, + c->hot_x, c->hot_y); + qemu_free(image); + qemu_free(mask); + + if (guest_cursor && + (gui_grab || kbd_mouse_is_absolute() || absolute_enabled)) + SDL_SetCursor(guest_sprite); +} + +static void sdl_cleanup(void) +{ + if (guest_sprite) + SDL_FreeCursor(guest_sprite); + SDL_QuitSubSystem(SDL_INIT_VIDEO); +} + +void sdl_display_init(DisplayState *ds, int full_screen, int no_frame) +{ + int flags; + uint8_t data = 0; + DisplayAllocator *da; + const SDL_VideoInfo *vi; + +#if defined(__APPLE__) + /* always use generic keymaps */ + if (!keyboard_layout) + keyboard_layout = "en-us"; +#endif + if(keyboard_layout) { + kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout); + if (!kbd_layout) + exit(1); + } + + if (no_frame) + gui_noframe = 1; + + if (!full_screen) { + setenv("SDL_VIDEO_ALLOW_SCREENSAVER", "1", 0); + } + + /* Enable normal up/down events for Caps-Lock and Num-Lock keys. + * This requires SDL >= 1.2.14. */ + setenv("SDL_DISABLE_LOCK_KEYS", "1", 1); + + flags = SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE; + if (SDL_Init (flags)) { + fprintf(stderr, "Could not initialize SDL(%s) - exiting\n", + SDL_GetError()); + exit(1); + } + vi = SDL_GetVideoInfo(); + host_format = *(vi->vfmt); + + dcl = qemu_mallocz(sizeof(DisplayChangeListener)); + dcl->dpy_update = sdl_update; + dcl->dpy_resize = sdl_resize; + dcl->dpy_refresh = sdl_refresh; + dcl->dpy_setdata = sdl_setdata; + dcl->dpy_fill = sdl_fill; + ds->mouse_set = sdl_mouse_warp; + ds->cursor_define = sdl_mouse_define; + register_displaychangelistener(ds, dcl); + + da = qemu_mallocz(sizeof(DisplayAllocator)); + da->create_displaysurface = sdl_create_displaysurface; + da->resize_displaysurface = sdl_resize_displaysurface; + da->free_displaysurface = sdl_free_displaysurface; + if (register_displayallocator(ds, da) == da) { + dpy_resize(ds); + } + + mouse_mode_notifier.notify = sdl_mouse_mode_change; + qemu_add_mouse_mode_change_notifier(&mouse_mode_notifier); + + sdl_update_caption(); + SDL_EnableKeyRepeat(250, 50); + gui_grab = 0; + + sdl_cursor_hidden = SDL_CreateCursor(&data, &data, 8, 1, 0, 0); + sdl_cursor_normal = SDL_GetCursor(); + + atexit(sdl_cleanup); + if (full_screen) { + gui_fullscreen = 1; + gui_fullscreen_initial_grab = 1; + sdl_grab_start(); + } +} diff -Nru qemu-kvm-0.12.5+noroms/ui/sdl_keysym.h qemu-kvm-0.14.1/ui/sdl_keysym.h --- qemu-kvm-0.12.5+noroms/ui/sdl_keysym.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/ui/sdl_keysym.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,277 @@ + +#include "keymaps.h" + +static const name2keysym_t name2keysym[]={ +/* ascii */ + { "space", 0x020}, + { "exclam", 0x021}, + { "quotedbl", 0x022}, + { "numbersign", 0x023}, + { "dollar", 0x024}, + { "percent", 0x025}, + { "ampersand", 0x026}, + { "apostrophe", 0x027}, + { "parenleft", 0x028}, + { "parenright", 0x029}, + { "asterisk", 0x02a}, + { "plus", 0x02b}, + { "comma", 0x02c}, + { "minus", 0x02d}, + { "period", 0x02e}, + { "slash", 0x02f}, + { "0", 0x030}, + { "1", 0x031}, + { "2", 0x032}, + { "3", 0x033}, + { "4", 0x034}, + { "5", 0x035}, + { "6", 0x036}, + { "7", 0x037}, + { "8", 0x038}, + { "9", 0x039}, + { "colon", 0x03a}, + { "semicolon", 0x03b}, + { "less", 0x03c}, + { "equal", 0x03d}, + { "greater", 0x03e}, + { "question", 0x03f}, + { "at", 0x040}, + { "A", 0x041}, + { "B", 0x042}, + { "C", 0x043}, + { "D", 0x044}, + { "E", 0x045}, + { "F", 0x046}, + { "G", 0x047}, + { "H", 0x048}, + { "I", 0x049}, + { "J", 0x04a}, + { "K", 0x04b}, + { "L", 0x04c}, + { "M", 0x04d}, + { "N", 0x04e}, + { "O", 0x04f}, + { "P", 0x050}, + { "Q", 0x051}, + { "R", 0x052}, + { "S", 0x053}, + { "T", 0x054}, + { "U", 0x055}, + { "V", 0x056}, + { "W", 0x057}, + { "X", 0x058}, + { "Y", 0x059}, + { "Z", 0x05a}, + { "bracketleft", 0x05b}, + { "backslash", 0x05c}, + { "bracketright", 0x05d}, + { "asciicircum", 0x05e}, + { "underscore", 0x05f}, + { "grave", 0x060}, + { "a", 0x061}, + { "b", 0x062}, + { "c", 0x063}, + { "d", 0x064}, + { "e", 0x065}, + { "f", 0x066}, + { "g", 0x067}, + { "h", 0x068}, + { "i", 0x069}, + { "j", 0x06a}, + { "k", 0x06b}, + { "l", 0x06c}, + { "m", 0x06d}, + { "n", 0x06e}, + { "o", 0x06f}, + { "p", 0x070}, + { "q", 0x071}, + { "r", 0x072}, + { "s", 0x073}, + { "t", 0x074}, + { "u", 0x075}, + { "v", 0x076}, + { "w", 0x077}, + { "x", 0x078}, + { "y", 0x079}, + { "z", 0x07a}, + { "braceleft", 0x07b}, + { "bar", 0x07c}, + { "braceright", 0x07d}, + { "asciitilde", 0x07e}, + +/* latin 1 extensions */ +{ "nobreakspace", 0x0a0}, +{ "exclamdown", 0x0a1}, +{ "cent", 0x0a2}, +{ "sterling", 0x0a3}, +{ "currency", 0x0a4}, +{ "yen", 0x0a5}, +{ "brokenbar", 0x0a6}, +{ "section", 0x0a7}, +{ "diaeresis", 0x0a8}, +{ "copyright", 0x0a9}, +{ "ordfeminine", 0x0aa}, +{ "guillemotleft", 0x0ab}, +{ "notsign", 0x0ac}, +{ "hyphen", 0x0ad}, +{ "registered", 0x0ae}, +{ "macron", 0x0af}, +{ "degree", 0x0b0}, +{ "plusminus", 0x0b1}, +{ "twosuperior", 0x0b2}, +{ "threesuperior", 0x0b3}, +{ "acute", 0x0b4}, +{ "mu", 0x0b5}, +{ "paragraph", 0x0b6}, +{ "periodcentered", 0x0b7}, +{ "cedilla", 0x0b8}, +{ "onesuperior", 0x0b9}, +{ "masculine", 0x0ba}, +{ "guillemotright", 0x0bb}, +{ "onequarter", 0x0bc}, +{ "onehalf", 0x0bd}, +{ "threequarters", 0x0be}, +{ "questiondown", 0x0bf}, +{ "Agrave", 0x0c0}, +{ "Aacute", 0x0c1}, +{ "Acircumflex", 0x0c2}, +{ "Atilde", 0x0c3}, +{ "Adiaeresis", 0x0c4}, +{ "Aring", 0x0c5}, +{ "AE", 0x0c6}, +{ "Ccedilla", 0x0c7}, +{ "Egrave", 0x0c8}, +{ "Eacute", 0x0c9}, +{ "Ecircumflex", 0x0ca}, +{ "Ediaeresis", 0x0cb}, +{ "Igrave", 0x0cc}, +{ "Iacute", 0x0cd}, +{ "Icircumflex", 0x0ce}, +{ "Idiaeresis", 0x0cf}, +{ "ETH", 0x0d0}, +{ "Eth", 0x0d0}, +{ "Ntilde", 0x0d1}, +{ "Ograve", 0x0d2}, +{ "Oacute", 0x0d3}, +{ "Ocircumflex", 0x0d4}, +{ "Otilde", 0x0d5}, +{ "Odiaeresis", 0x0d6}, +{ "multiply", 0x0d7}, +{ "Ooblique", 0x0d8}, +{ "Oslash", 0x0d8}, +{ "Ugrave", 0x0d9}, +{ "Uacute", 0x0da}, +{ "Ucircumflex", 0x0db}, +{ "Udiaeresis", 0x0dc}, +{ "Yacute", 0x0dd}, +{ "THORN", 0x0de}, +{ "Thorn", 0x0de}, +{ "ssharp", 0x0df}, +{ "agrave", 0x0e0}, +{ "aacute", 0x0e1}, +{ "acircumflex", 0x0e2}, +{ "atilde", 0x0e3}, +{ "adiaeresis", 0x0e4}, +{ "aring", 0x0e5}, +{ "ae", 0x0e6}, +{ "ccedilla", 0x0e7}, +{ "egrave", 0x0e8}, +{ "eacute", 0x0e9}, +{ "ecircumflex", 0x0ea}, +{ "ediaeresis", 0x0eb}, +{ "igrave", 0x0ec}, +{ "iacute", 0x0ed}, +{ "icircumflex", 0x0ee}, +{ "idiaeresis", 0x0ef}, +{ "eth", 0x0f0}, +{ "ntilde", 0x0f1}, +{ "ograve", 0x0f2}, +{ "oacute", 0x0f3}, +{ "ocircumflex", 0x0f4}, +{ "otilde", 0x0f5}, +{ "odiaeresis", 0x0f6}, +{ "division", 0x0f7}, +{ "oslash", 0x0f8}, +{ "ooblique", 0x0f8}, +{ "ugrave", 0x0f9}, +{ "uacute", 0x0fa}, +{ "ucircumflex", 0x0fb}, +{ "udiaeresis", 0x0fc}, +{ "yacute", 0x0fd}, +{ "thorn", 0x0fe}, +{ "ydiaeresis", 0x0ff}, +{"EuroSign", SDLK_EURO}, + + /* modifiers */ +{"Control_L", SDLK_LCTRL}, +{"Control_R", SDLK_RCTRL}, +{"Alt_L", SDLK_LALT}, +{"Alt_R", SDLK_RALT}, +{"Caps_Lock", SDLK_CAPSLOCK}, +{"Meta_L", SDLK_LMETA}, +{"Meta_R", SDLK_RMETA}, +{"Shift_L", SDLK_LSHIFT}, +{"Shift_R", SDLK_RSHIFT}, +{"Super_L", SDLK_LSUPER}, +{"Super_R", SDLK_RSUPER}, + + /* special keys */ +{"BackSpace", SDLK_BACKSPACE}, +{"Tab", SDLK_TAB}, +{"Return", SDLK_RETURN}, +{"Right", SDLK_RIGHT}, +{"Left", SDLK_LEFT}, +{"Up", SDLK_UP}, +{"Down", SDLK_DOWN}, +{"Page_Down", SDLK_PAGEDOWN}, +{"Page_Up", SDLK_PAGEUP}, +{"Insert", SDLK_INSERT}, +{"Delete", SDLK_DELETE}, +{"Home", SDLK_HOME}, +{"End", SDLK_END}, +{"Scroll_Lock", SDLK_SCROLLOCK}, +{"F1", SDLK_F1}, +{"F2", SDLK_F2}, +{"F3", SDLK_F3}, +{"F4", SDLK_F4}, +{"F5", SDLK_F5}, +{"F6", SDLK_F6}, +{"F7", SDLK_F7}, +{"F8", SDLK_F8}, +{"F9", SDLK_F9}, +{"F10", SDLK_F10}, +{"F11", SDLK_F11}, +{"F12", SDLK_F12}, +{"F13", SDLK_F13}, +{"F14", SDLK_F14}, +{"F15", SDLK_F15}, +{"Sys_Req", SDLK_SYSREQ}, +{"KP_0", SDLK_KP0}, +{"KP_1", SDLK_KP1}, +{"KP_2", SDLK_KP2}, +{"KP_3", SDLK_KP3}, +{"KP_4", SDLK_KP4}, +{"KP_5", SDLK_KP5}, +{"KP_6", SDLK_KP6}, +{"KP_7", SDLK_KP7}, +{"KP_8", SDLK_KP8}, +{"KP_9", SDLK_KP9}, +{"KP_Add", SDLK_KP_PLUS}, +{"KP_Decimal", SDLK_KP_PERIOD}, +{"KP_Divide", SDLK_KP_DIVIDE}, +{"KP_Enter", SDLK_KP_ENTER}, +{"KP_Equal", SDLK_KP_EQUALS}, +{"KP_Multiply", SDLK_KP_MULTIPLY}, +{"KP_Subtract", SDLK_KP_MINUS}, +{"help", SDLK_HELP}, +{"Menu", SDLK_MENU}, +{"Power", SDLK_POWER}, +{"Print", SDLK_PRINT}, +{"Mode_switch", SDLK_MODE}, +{"Multi_Key", SDLK_COMPOSE}, +{"Num_Lock", SDLK_NUMLOCK}, +{"Pause", SDLK_PAUSE}, +{"Escape", SDLK_ESCAPE}, + +{NULL, 0}, +}; diff -Nru qemu-kvm-0.12.5+noroms/ui/sdl_zoom.c qemu-kvm-0.14.1/ui/sdl_zoom.c --- qemu-kvm-0.12.5+noroms/ui/sdl_zoom.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/ui/sdl_zoom.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,95 @@ +/* + * SDL_zoom - surface scaling + * + * Copyright (c) 2009 Citrix Systems, Inc. + * + * Derived from: SDL_rotozoom, LGPL (c) A. Schiffler from the SDL_gfx library. + * Modifications by Stefano Stabellini. + * + * This work is licensed under the terms of the GNU GPL version 2. + * See the COPYING file in the top-level directory. + * + */ + +#include "sdl_zoom.h" +#include "osdep.h" +#include +#include + +static int sdl_zoom_rgb16(SDL_Surface *src, SDL_Surface *dst, int smooth, + SDL_Rect *dst_rect); +static int sdl_zoom_rgb32(SDL_Surface *src, SDL_Surface *dst, int smooth, + SDL_Rect *dst_rect); + +#define BPP 32 +#include "sdl_zoom_template.h" +#undef BPP +#define BPP 16 +#include "sdl_zoom_template.h" +#undef BPP + +int sdl_zoom_blit(SDL_Surface *src_sfc, SDL_Surface *dst_sfc, int smooth, + SDL_Rect *in_rect) +{ + SDL_Rect zoom, src_rect; + int extra; + + /* Grow the size of the modified rectangle to avoid edge artefacts */ + src_rect.x = (in_rect->x > 0) ? (in_rect->x - 1) : 0; + src_rect.y = (in_rect->y > 0) ? (in_rect->y - 1) : 0; + + src_rect.w = in_rect->w + 1; + if (src_rect.x + src_rect.w > src_sfc->w) + src_rect.w = src_sfc->w - src_rect.x; + + src_rect.h = in_rect->h + 1; + if (src_rect.y + src_rect.h > src_sfc->h) + src_rect.h = src_sfc->h - src_rect.y; + + /* (x,y) : round down */ + zoom.x = (int)(((float)(src_rect.x * dst_sfc->w)) / (float)(src_sfc->w)); + zoom.y = (int)(((float)(src_rect.y * dst_sfc->h)) / (float)(src_sfc->h)); + + /* (w,h) : round up */ + zoom.w = (int)( ((double)((src_rect.w * dst_sfc->w) + (src_sfc->w - 1))) / + (double)(src_sfc->w)); + + zoom.h = (int)( ((double)((src_rect.h * dst_sfc->h) + (src_sfc->h - 1))) / + (double)(src_sfc->h)); + + /* Account for any (x,y) rounding by adding one-source-pixel's worth + * of destination pixels and then edge checking. + */ + + extra = ((dst_sfc->w-1) / src_sfc->w) + 1; + + if ((zoom.x + zoom.w) < (dst_sfc->w - extra)) + zoom.w += extra; + else + zoom.w = dst_sfc->w - zoom.x; + + extra = ((dst_sfc->h-1) / src_sfc->h) + 1; + + if ((zoom.y + zoom.h) < (dst_sfc->h - extra)) + zoom.h += extra; + else + zoom.h = dst_sfc->h - zoom.y; + + /* The rectangle (zoom.x, zoom.y, zoom.w, zoom.h) is the area on the + * destination surface that needs to be updated. + */ + if (src_sfc->format->BitsPerPixel == 32) + sdl_zoom_rgb32(src_sfc, dst_sfc, smooth, &zoom); + else if (src_sfc->format->BitsPerPixel == 16) + sdl_zoom_rgb16(src_sfc, dst_sfc, smooth, &zoom); + else { + fprintf(stderr, "pixel format not supported\n"); + return -1; + } + + /* Return the rectangle of the update to the caller */ + *in_rect = zoom; + + return 0; +} + diff -Nru qemu-kvm-0.12.5+noroms/ui/sdl_zoom.h qemu-kvm-0.14.1/ui/sdl_zoom.h --- qemu-kvm-0.12.5+noroms/ui/sdl_zoom.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/ui/sdl_zoom.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,25 @@ +/* + * SDL_zoom - surface scaling + * + * Copyright (c) 2009 Citrix Systems, Inc. + * + * Derived from: SDL_rotozoom, LGPL (c) A. Schiffler from the SDL_gfx library. + * Modifications by Stefano Stabellini. + * + * This work is licensed under the terms of the GNU GPL version 2. + * See the COPYING file in the top-level directory. + * + */ + +#ifndef SDL_zoom_h +#define SDL_zoom_h + +#include + +#define SMOOTHING_OFF 0 +#define SMOOTHING_ON 1 + +int sdl_zoom_blit(SDL_Surface *src_sfc, SDL_Surface *dst_sfc, + int smooth, SDL_Rect *src_rect); + +#endif /* SDL_zoom_h */ diff -Nru qemu-kvm-0.12.5+noroms/ui/sdl_zoom_template.h qemu-kvm-0.14.1/ui/sdl_zoom_template.h --- qemu-kvm-0.12.5+noroms/ui/sdl_zoom_template.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/ui/sdl_zoom_template.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,225 @@ +/* + * SDL_zoom_template - surface scaling + * + * Copyright (c) 2009 Citrix Systems, Inc. + * + * Derived from: SDL_rotozoom, LGPL (c) A. Schiffler from the SDL_gfx library. + * Modifications by Stefano Stabellini. + * + * This work is licensed under the terms of the GNU GPL version 2. + * See the COPYING file in the top-level directory. + * + */ + +#if BPP == 16 +#define SDL_TYPE Uint16 +#elif BPP == 32 +#define SDL_TYPE Uint32 +#else +#error unsupport depth +#endif + +/* + * Simple helper functions to make the code looks nicer + * + * Assume spf = source SDL_PixelFormat + * dpf = dest SDL_PixelFormat + * + */ +#define getRed(color) (((color) & spf->Rmask) >> spf->Rshift) +#define getGreen(color) (((color) & spf->Gmask) >> spf->Gshift) +#define getBlue(color) (((color) & spf->Bmask) >> spf->Bshift) +#define getAlpha(color) (((color) & spf->Amask) >> spf->Ashift) + +#define setRed(r, pcolor) do { \ + *pcolor = ((*pcolor) & (~(dpf->Rmask))) + \ + (((r) & (dpf->Rmask >> dpf->Rshift)) << dpf->Rshift); \ +} while (0); + +#define setGreen(g, pcolor) do { \ + *pcolor = ((*pcolor) & (~(dpf->Gmask))) + \ + (((g) & (dpf->Gmask >> dpf->Gshift)) << dpf->Gshift); \ +} while (0); + +#define setBlue(b, pcolor) do { \ + *pcolor = ((*pcolor) & (~(dpf->Bmask))) + \ + (((b) & (dpf->Bmask >> dpf->Bshift)) << dpf->Bshift); \ +} while (0); + +#define setAlpha(a, pcolor) do { \ + *pcolor = ((*pcolor) & (~(dpf->Amask))) + \ + (((a) & (dpf->Amask >> dpf->Ashift)) << dpf->Ashift); \ +} while (0); + +static int glue(sdl_zoom_rgb, BPP)(SDL_Surface *src, SDL_Surface *dst, int smooth, + SDL_Rect *dst_rect) +{ + int x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy, ex, ey, t1, t2, sstep, sstep_jump; + SDL_TYPE *c00, *c01, *c10, *c11, *sp, *csp, *dp; + int d_gap; + SDL_PixelFormat *spf = src->format; + SDL_PixelFormat *dpf = dst->format; + + if (smooth) { + /* For interpolation: assume source dimension is one pixel. + * Smaller here to avoid overflow on right and bottom edge. + */ + sx = (int) (65536.0 * (float) (src->w - 1) / (float) dst->w); + sy = (int) (65536.0 * (float) (src->h - 1) / (float) dst->h); + } else { + sx = (int) (65536.0 * (float) src->w / (float) dst->w); + sy = (int) (65536.0 * (float) src->h / (float) dst->h); + } + + if ((sax = (int *) malloc((dst->w + 1) * sizeof(Uint32))) == NULL) { + return (-1); + } + if ((say = (int *) malloc((dst->h + 1) * sizeof(Uint32))) == NULL) { + free(sax); + return (-1); + } + + sp = csp = (SDL_TYPE *) src->pixels; + dp = (SDL_TYPE *) (dst->pixels + dst_rect->y * dst->pitch + + dst_rect->x * dst->format->BytesPerPixel); + + csx = 0; + csax = sax; + for (x = 0; x <= dst->w; x++) { + *csax = csx; + csax++; + csx &= 0xffff; + csx += sx; + } + csy = 0; + csay = say; + for (y = 0; y <= dst->h; y++) { + *csay = csy; + csay++; + csy &= 0xffff; + csy += sy; + } + + d_gap = dst->pitch - dst_rect->w * dst->format->BytesPerPixel; + + if (smooth) { + csay = say; + for (y = 0; y < dst_rect->y; y++) { + csay++; + sstep = (*csay >> 16) * src->pitch; + csp = (SDL_TYPE *) ((Uint8 *) csp + sstep); + } + + /* Calculate sstep_jump */ + csax = sax; + sstep_jump = 0; + for (x = 0; x < dst_rect->x; x++) { + csax++; + sstep = (*csax >> 16); + sstep_jump += sstep; + } + + for (y = 0; y < dst_rect->h ; y++) { + /* Setup colour source pointers */ + c00 = csp + sstep_jump; + c01 = c00 + 1; + c10 = (SDL_TYPE *) ((Uint8 *) csp + src->pitch) + sstep_jump; + c11 = c10 + 1; + csax = sax + dst_rect->x; + + for (x = 0; x < dst_rect->w; x++) { + + /* Interpolate colours */ + ex = (*csax & 0xffff); + ey = (*csay & 0xffff); + t1 = ((((getRed(*c01) - getRed(*c00)) * ex) >> 16) + + getRed(*c00)) & (dpf->Rmask >> dpf->Rshift); + t2 = ((((getRed(*c11) - getRed(*c10)) * ex) >> 16) + + getRed(*c10)) & (dpf->Rmask >> dpf->Rshift); + setRed((((t2 - t1) * ey) >> 16) + t1, dp); + t1 = ((((getGreen(*c01) - getGreen(*c00)) * ex) >> 16) + + getGreen(*c00)) & (dpf->Gmask >> dpf->Gshift); + t2 = ((((getGreen(*c11) - getGreen(*c10)) * ex) >> 16) + + getGreen(*c10)) & (dpf->Gmask >> dpf->Gshift); + setGreen((((t2 - t1) * ey) >> 16) + t1, dp); + t1 = ((((getBlue(*c01) - getBlue(*c00)) * ex) >> 16) + + getBlue(*c00)) & (dpf->Bmask >> dpf->Bshift); + t2 = ((((getBlue(*c11) - getBlue(*c10)) * ex) >> 16) + + getBlue(*c10)) & (dpf->Bmask >> dpf->Bshift); + setBlue((((t2 - t1) * ey) >> 16) + t1, dp); + t1 = ((((getAlpha(*c01) - getAlpha(*c00)) * ex) >> 16) + + getAlpha(*c00)) & (dpf->Amask >> dpf->Ashift); + t2 = ((((getAlpha(*c11) - getAlpha(*c10)) * ex) >> 16) + + getAlpha(*c10)) & (dpf->Amask >> dpf->Ashift); + setAlpha((((t2 - t1) * ey) >> 16) + t1, dp); + + /* Advance source pointers */ + csax++; + sstep = (*csax >> 16); + c00 += sstep; + c01 += sstep; + c10 += sstep; + c11 += sstep; + /* Advance destination pointer */ + dp++; + } + /* Advance source pointer */ + csay++; + csp = (SDL_TYPE *) ((Uint8 *) csp + (*csay >> 16) * src->pitch); + /* Advance destination pointers */ + dp = (SDL_TYPE *) ((Uint8 *) dp + d_gap); + } + + + } else { + csay = say; + + for (y = 0; y < dst_rect->y; y++) { + csay++; + sstep = (*csay >> 16) * src->pitch; + csp = (SDL_TYPE *) ((Uint8 *) csp + sstep); + } + + /* Calculate sstep_jump */ + csax = sax; + sstep_jump = 0; + for (x = 0; x < dst_rect->x; x++) { + csax++; + sstep = (*csax >> 16); + sstep_jump += sstep; + } + + for (y = 0 ; y < dst_rect->h ; y++) { + sp = csp + sstep_jump; + csax = sax + dst_rect->x; + + for (x = 0; x < dst_rect->w; x++) { + + /* Draw */ + *dp = *sp; + + /* Advance source pointers */ + csax++; + sstep = (*csax >> 16); + sp += sstep; + + /* Advance destination pointer */ + dp++; + } + /* Advance source pointers */ + csay++; + sstep = (*csay >> 16) * src->pitch; + csp = (SDL_TYPE *) ((Uint8 *) csp + sstep); + + /* Advance destination pointer */ + dp = (SDL_TYPE *) ((Uint8 *) dp + d_gap); + } + } + + free(sax); + free(say); + return (0); +} + +#undef SDL_TYPE + diff -Nru qemu-kvm-0.12.5+noroms/ui/spice-core.c qemu-kvm-0.14.1/ui/spice-core.c --- qemu-kvm-0.12.5+noroms/ui/spice-core.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/ui/spice-core.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,672 @@ +/* + * Copyright (C) 2010 Red Hat, Inc. + * + * 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 or + * (at your option) version 3 of the License. + * + * 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, see . + */ + +#include +#include + +#include + +#include "qemu-common.h" +#include "qemu-spice.h" +#include "qemu-timer.h" +#include "qemu-queue.h" +#include "qemu-x509.h" +#include "qemu_socket.h" +#include "qint.h" +#include "qbool.h" +#include "qstring.h" +#include "qjson.h" +#include "notify.h" +#include "migration.h" +#include "monitor.h" +#include "hw/hw.h" + +/* core bits */ + +static SpiceServer *spice_server; +static Notifier migration_state; +static const char *auth = "spice"; +static char *auth_passwd; +static time_t auth_expires = TIME_MAX; +int using_spice = 0; + +struct SpiceTimer { + QEMUTimer *timer; + QTAILQ_ENTRY(SpiceTimer) next; +}; +static QTAILQ_HEAD(, SpiceTimer) timers = QTAILQ_HEAD_INITIALIZER(timers); + +static SpiceTimer *timer_add(SpiceTimerFunc func, void *opaque) +{ + SpiceTimer *timer; + + timer = qemu_mallocz(sizeof(*timer)); + timer->timer = qemu_new_timer(rt_clock, func, opaque); + QTAILQ_INSERT_TAIL(&timers, timer, next); + return timer; +} + +static void timer_start(SpiceTimer *timer, uint32_t ms) +{ + qemu_mod_timer(timer->timer, qemu_get_clock(rt_clock) + ms); +} + +static void timer_cancel(SpiceTimer *timer) +{ + qemu_del_timer(timer->timer); +} + +static void timer_remove(SpiceTimer *timer) +{ + qemu_del_timer(timer->timer); + qemu_free_timer(timer->timer); + QTAILQ_REMOVE(&timers, timer, next); + qemu_free(timer); +} + +struct SpiceWatch { + int fd; + int event_mask; + SpiceWatchFunc func; + void *opaque; + QTAILQ_ENTRY(SpiceWatch) next; +}; +static QTAILQ_HEAD(, SpiceWatch) watches = QTAILQ_HEAD_INITIALIZER(watches); + +static void watch_read(void *opaque) +{ + SpiceWatch *watch = opaque; + watch->func(watch->fd, SPICE_WATCH_EVENT_READ, watch->opaque); +} + +static void watch_write(void *opaque) +{ + SpiceWatch *watch = opaque; + watch->func(watch->fd, SPICE_WATCH_EVENT_WRITE, watch->opaque); +} + +static void watch_update_mask(SpiceWatch *watch, int event_mask) +{ + IOHandler *on_read = NULL; + IOHandler *on_write = NULL; + + watch->event_mask = event_mask; + if (watch->event_mask & SPICE_WATCH_EVENT_READ) { + on_read = watch_read; + } + if (watch->event_mask & SPICE_WATCH_EVENT_WRITE) { + on_write = watch_write; + } + qemu_set_fd_handler(watch->fd, on_read, on_write, watch); +} + +static SpiceWatch *watch_add(int fd, int event_mask, SpiceWatchFunc func, void *opaque) +{ + SpiceWatch *watch; + + watch = qemu_mallocz(sizeof(*watch)); + watch->fd = fd; + watch->func = func; + watch->opaque = opaque; + QTAILQ_INSERT_TAIL(&watches, watch, next); + + watch_update_mask(watch, event_mask); + return watch; +} + +static void watch_remove(SpiceWatch *watch) +{ + watch_update_mask(watch, 0); + QTAILQ_REMOVE(&watches, watch, next); + qemu_free(watch); +} + +#if SPICE_INTERFACE_CORE_MINOR >= 3 + +typedef struct ChannelList ChannelList; +struct ChannelList { + SpiceChannelEventInfo *info; + QTAILQ_ENTRY(ChannelList) link; +}; +static QTAILQ_HEAD(, ChannelList) channel_list = QTAILQ_HEAD_INITIALIZER(channel_list); + +static void channel_list_add(SpiceChannelEventInfo *info) +{ + ChannelList *item; + + item = qemu_mallocz(sizeof(*item)); + item->info = info; + QTAILQ_INSERT_TAIL(&channel_list, item, link); +} + +static void channel_list_del(SpiceChannelEventInfo *info) +{ + ChannelList *item; + + QTAILQ_FOREACH(item, &channel_list, link) { + if (item->info != info) { + continue; + } + QTAILQ_REMOVE(&channel_list, item, link); + qemu_free(item); + return; + } +} + +static void add_addr_info(QDict *dict, struct sockaddr *addr, int len) +{ + char host[NI_MAXHOST], port[NI_MAXSERV]; + const char *family; + + getnameinfo(addr, len, host, sizeof(host), port, sizeof(port), + NI_NUMERICHOST | NI_NUMERICSERV); + family = inet_strfamily(addr->sa_family); + + qdict_put(dict, "host", qstring_from_str(host)); + qdict_put(dict, "port", qstring_from_str(port)); + qdict_put(dict, "family", qstring_from_str(family)); +} + +static void add_channel_info(QDict *dict, SpiceChannelEventInfo *info) +{ + int tls = info->flags & SPICE_CHANNEL_EVENT_FLAG_TLS; + + qdict_put(dict, "connection-id", qint_from_int(info->connection_id)); + qdict_put(dict, "channel-type", qint_from_int(info->type)); + qdict_put(dict, "channel-id", qint_from_int(info->id)); + qdict_put(dict, "tls", qbool_from_int(tls)); +} + +static QList *channel_list_get(void) +{ + ChannelList *item; + QList *list; + QDict *dict; + + list = qlist_new(); + QTAILQ_FOREACH(item, &channel_list, link) { + dict = qdict_new(); + add_addr_info(dict, &item->info->paddr, item->info->plen); + add_channel_info(dict, item->info); + qlist_append(list, dict); + } + return list; +} + +static void channel_event(int event, SpiceChannelEventInfo *info) +{ + static const int qevent[] = { + [ SPICE_CHANNEL_EVENT_CONNECTED ] = QEVENT_SPICE_CONNECTED, + [ SPICE_CHANNEL_EVENT_INITIALIZED ] = QEVENT_SPICE_INITIALIZED, + [ SPICE_CHANNEL_EVENT_DISCONNECTED ] = QEVENT_SPICE_DISCONNECTED, + }; + QDict *server, *client; + QObject *data; + + client = qdict_new(); + add_addr_info(client, &info->paddr, info->plen); + + server = qdict_new(); + add_addr_info(server, &info->laddr, info->llen); + + if (event == SPICE_CHANNEL_EVENT_INITIALIZED) { + qdict_put(server, "auth", qstring_from_str(auth)); + add_channel_info(client, info); + channel_list_add(info); + } + if (event == SPICE_CHANNEL_EVENT_DISCONNECTED) { + channel_list_del(info); + } + + data = qobject_from_jsonf("{ 'client': %p, 'server': %p }", + QOBJECT(client), QOBJECT(server)); + monitor_protocol_event(qevent[event], data); + qobject_decref(data); +} + +#else /* SPICE_INTERFACE_CORE_MINOR >= 3 */ + +static QList *channel_list_get(void) +{ + return NULL; +} + +#endif /* SPICE_INTERFACE_CORE_MINOR >= 3 */ + +static SpiceCoreInterface core_interface = { + .base.type = SPICE_INTERFACE_CORE, + .base.description = "qemu core services", + .base.major_version = SPICE_INTERFACE_CORE_MAJOR, + .base.minor_version = SPICE_INTERFACE_CORE_MINOR, + + .timer_add = timer_add, + .timer_start = timer_start, + .timer_cancel = timer_cancel, + .timer_remove = timer_remove, + + .watch_add = watch_add, + .watch_update_mask = watch_update_mask, + .watch_remove = watch_remove, + +#if SPICE_INTERFACE_CORE_MINOR >= 3 + .channel_event = channel_event, +#endif +}; + +/* config string parsing */ + +static int name2enum(const char *string, const char *table[], int entries) +{ + int i; + + if (string) { + for (i = 0; i < entries; i++) { + if (!table[i]) { + continue; + } + if (strcmp(string, table[i]) != 0) { + continue; + } + return i; + } + } + return -1; +} + +static int parse_name(const char *string, const char *optname, + const char *table[], int entries) +{ + int value = name2enum(string, table, entries); + + if (value != -1) { + return value; + } + fprintf(stderr, "spice: invalid %s: %s\n", optname, string); + exit(1); +} + +#if SPICE_SERVER_VERSION >= 0x000600 /* 0.6.0 */ + +static const char *stream_video_names[] = { + [ SPICE_STREAM_VIDEO_OFF ] = "off", + [ SPICE_STREAM_VIDEO_ALL ] = "all", + [ SPICE_STREAM_VIDEO_FILTER ] = "filter", +}; +#define parse_stream_video(_name) \ + name2enum(_name, stream_video_names, ARRAY_SIZE(stream_video_names)) + +#endif /* >= 0.6.0 */ + +static const char *compression_names[] = { + [ SPICE_IMAGE_COMPRESS_OFF ] = "off", + [ SPICE_IMAGE_COMPRESS_AUTO_GLZ ] = "auto_glz", + [ SPICE_IMAGE_COMPRESS_AUTO_LZ ] = "auto_lz", + [ SPICE_IMAGE_COMPRESS_QUIC ] = "quic", + [ SPICE_IMAGE_COMPRESS_GLZ ] = "glz", + [ SPICE_IMAGE_COMPRESS_LZ ] = "lz", +}; +#define parse_compression(_name) \ + parse_name(_name, "image compression", \ + compression_names, ARRAY_SIZE(compression_names)) + +static const char *wan_compression_names[] = { + [ SPICE_WAN_COMPRESSION_AUTO ] = "auto", + [ SPICE_WAN_COMPRESSION_NEVER ] = "never", + [ SPICE_WAN_COMPRESSION_ALWAYS ] = "always", +}; +#define parse_wan_compression(_name) \ + parse_name(_name, "wan compression", \ + wan_compression_names, ARRAY_SIZE(wan_compression_names)) + +/* functions for the rest of qemu */ + +static void info_spice_iter(QObject *obj, void *opaque) +{ + QDict *client; + Monitor *mon = opaque; + + client = qobject_to_qdict(obj); + monitor_printf(mon, "Channel:\n"); + monitor_printf(mon, " address: %s:%s%s\n", + qdict_get_str(client, "host"), + qdict_get_str(client, "port"), + qdict_get_bool(client, "tls") ? " [tls]" : ""); + monitor_printf(mon, " session: %" PRId64 "\n", + qdict_get_int(client, "connection-id")); + monitor_printf(mon, " channel: %d:%d\n", + (int)qdict_get_int(client, "channel-type"), + (int)qdict_get_int(client, "channel-id")); +} + +void do_info_spice_print(Monitor *mon, const QObject *data) +{ + QDict *server; + QList *channels; + const char *host; + int port; + + server = qobject_to_qdict(data); + if (qdict_get_bool(server, "enabled") == 0) { + monitor_printf(mon, "Server: disabled\n"); + return; + } + + monitor_printf(mon, "Server:\n"); + host = qdict_get_str(server, "host"); + port = qdict_get_try_int(server, "port", -1); + if (port != -1) { + monitor_printf(mon, " address: %s:%d\n", host, port); + } + port = qdict_get_try_int(server, "tls-port", -1); + if (port != -1) { + monitor_printf(mon, " address: %s:%d [tls]\n", host, port); + } + monitor_printf(mon, " auth: %s\n", qdict_get_str(server, "auth")); + + channels = qdict_get_qlist(server, "channels"); + if (qlist_empty(channels)) { + monitor_printf(mon, "Channels: none\n"); + } else { + qlist_iter(channels, info_spice_iter, mon); + } +} + +void do_info_spice(Monitor *mon, QObject **ret_data) +{ + QemuOpts *opts = QTAILQ_FIRST(&qemu_spice_opts.head); + QDict *server; + QList *clist; + const char *addr; + int port, tls_port; + + if (!spice_server) { + *ret_data = qobject_from_jsonf("{ 'enabled': false }"); + return; + } + + addr = qemu_opt_get(opts, "addr"); + port = qemu_opt_get_number(opts, "port", 0); + tls_port = qemu_opt_get_number(opts, "tls-port", 0); + clist = channel_list_get(); + + server = qdict_new(); + qdict_put(server, "enabled", qbool_from_int(true)); + qdict_put(server, "auth", qstring_from_str(auth)); + qdict_put(server, "host", qstring_from_str(addr ? addr : "0.0.0.0")); + if (port) { + qdict_put(server, "port", qint_from_int(port)); + } + if (tls_port) { + qdict_put(server, "tls-port", qint_from_int(tls_port)); + } + if (clist) { + qdict_put(server, "channels", clist); + } + + *ret_data = QOBJECT(server); +} + +static void migration_state_notifier(Notifier *notifier) +{ + int state = get_migration_state(); + + if (state == MIG_STATE_COMPLETED) { +#if SPICE_SERVER_VERSION >= 0x000701 /* 0.7.1 */ + spice_server_migrate_switch(spice_server); +#endif + } +} + +int qemu_spice_migrate_info(const char *hostname, int port, int tls_port, + const char *subject) +{ + return spice_server_migrate_info(spice_server, hostname, + port, tls_port, subject); +} + +static int add_channel(const char *name, const char *value, void *opaque) +{ + int security = 0; + int rc; + + if (strcmp(name, "tls-channel") == 0) { + security = SPICE_CHANNEL_SECURITY_SSL; + } + if (strcmp(name, "plaintext-channel") == 0) { + security = SPICE_CHANNEL_SECURITY_NONE; + } + if (security == 0) { + return 0; + } + if (strcmp(value, "default") == 0) { + rc = spice_server_set_channel_security(spice_server, NULL, security); + } else { + rc = spice_server_set_channel_security(spice_server, value, security); + } + if (rc != 0) { + fprintf(stderr, "spice: failed to set channel security for %s\n", value); + exit(1); + } + return 0; +} + +void qemu_spice_init(void) +{ + QemuOpts *opts = QTAILQ_FIRST(&qemu_spice_opts.head); + const char *password, *str, *x509_dir, *addr, + *x509_key_password = NULL, + *x509_dh_file = NULL, + *tls_ciphers = NULL; + char *x509_key_file = NULL, + *x509_cert_file = NULL, + *x509_cacert_file = NULL; + int port, tls_port, len, addr_flags; + spice_image_compression_t compression; + spice_wan_compression_t wan_compr; + + if (!opts) { + return; + } + port = qemu_opt_get_number(opts, "port", 0); + tls_port = qemu_opt_get_number(opts, "tls-port", 0); + if (!port && !tls_port) { + return; + } + password = qemu_opt_get(opts, "password"); + + if (tls_port) { + x509_dir = qemu_opt_get(opts, "x509-dir"); + if (NULL == x509_dir) { + x509_dir = "."; + } + len = strlen(x509_dir) + 32; + + str = qemu_opt_get(opts, "x509-key-file"); + if (str) { + x509_key_file = qemu_strdup(str); + } else { + x509_key_file = qemu_malloc(len); + snprintf(x509_key_file, len, "%s/%s", x509_dir, X509_SERVER_KEY_FILE); + } + + str = qemu_opt_get(opts, "x509-cert-file"); + if (str) { + x509_cert_file = qemu_strdup(str); + } else { + x509_cert_file = qemu_malloc(len); + snprintf(x509_cert_file, len, "%s/%s", x509_dir, X509_SERVER_CERT_FILE); + } + + str = qemu_opt_get(opts, "x509-cacert-file"); + if (str) { + x509_cacert_file = qemu_strdup(str); + } else { + x509_cacert_file = qemu_malloc(len); + snprintf(x509_cacert_file, len, "%s/%s", x509_dir, X509_CA_CERT_FILE); + } + + x509_key_password = qemu_opt_get(opts, "x509-key-password"); + x509_dh_file = qemu_opt_get(opts, "x509-dh-file"); + tls_ciphers = qemu_opt_get(opts, "tls-ciphers"); + } + + addr = qemu_opt_get(opts, "addr"); + addr_flags = 0; + if (qemu_opt_get_bool(opts, "ipv4", 0)) { + addr_flags |= SPICE_ADDR_FLAG_IPV4_ONLY; + } else if (qemu_opt_get_bool(opts, "ipv6", 0)) { + addr_flags |= SPICE_ADDR_FLAG_IPV6_ONLY; + } + + spice_server = spice_server_new(); + spice_server_set_addr(spice_server, addr ? addr : "", addr_flags); + if (port) { + spice_server_set_port(spice_server, port); + } + if (tls_port) { + spice_server_set_tls(spice_server, tls_port, + x509_cacert_file, + x509_cert_file, + x509_key_file, + x509_key_password, + x509_dh_file, + tls_ciphers); + } + if (password) { + spice_server_set_ticket(spice_server, password, 0, 0, 0); + } + if (qemu_opt_get_bool(opts, "disable-ticketing", 0)) { + auth = "none"; + spice_server_set_noauth(spice_server); + } + + compression = SPICE_IMAGE_COMPRESS_AUTO_GLZ; + str = qemu_opt_get(opts, "image-compression"); + if (str) { + compression = parse_compression(str); + } + spice_server_set_image_compression(spice_server, compression); + + wan_compr = SPICE_WAN_COMPRESSION_AUTO; + str = qemu_opt_get(opts, "jpeg-wan-compression"); + if (str) { + wan_compr = parse_wan_compression(str); + } + spice_server_set_jpeg_compression(spice_server, wan_compr); + + wan_compr = SPICE_WAN_COMPRESSION_AUTO; + str = qemu_opt_get(opts, "zlib-glz-wan-compression"); + if (str) { + wan_compr = parse_wan_compression(str); + } + spice_server_set_zlib_glz_compression(spice_server, wan_compr); + +#if SPICE_SERVER_VERSION >= 0x000600 /* 0.6.0 */ + + str = qemu_opt_get(opts, "streaming-video"); + if (str) { + int streaming_video = parse_stream_video(str); + spice_server_set_streaming_video(spice_server, streaming_video); + } + + spice_server_set_agent_mouse + (spice_server, qemu_opt_get_bool(opts, "agent-mouse", 1)); + spice_server_set_playback_compression + (spice_server, qemu_opt_get_bool(opts, "playback-compression", 1)); + +#endif /* >= 0.6.0 */ + + qemu_opt_foreach(opts, add_channel, NULL, 0); + + spice_server_init(spice_server, &core_interface); + using_spice = 1; + + migration_state.notify = migration_state_notifier; + add_migration_state_change_notifier(&migration_state); + + qemu_spice_input_init(); + qemu_spice_audio_init(); + + qemu_free(x509_key_file); + qemu_free(x509_cert_file); + qemu_free(x509_cacert_file); +} + +int qemu_spice_add_interface(SpiceBaseInstance *sin) +{ + if (!spice_server) { + if (QTAILQ_FIRST(&qemu_spice_opts.head) != NULL) { + fprintf(stderr, "Oops: spice configured but not active\n"); + exit(1); + } + /* + * Create a spice server instance. + * It does *not* listen on the network. + * It handles QXL local rendering only. + * + * With a command line like '-vnc :0 -vga qxl' you'll end up here. + */ + spice_server = spice_server_new(); + spice_server_init(spice_server, &core_interface); + } + return spice_server_add_interface(spice_server, sin); +} + +static int qemu_spice_set_ticket(bool fail_if_conn, bool disconnect_if_conn) +{ + time_t lifetime, now = time(NULL); + char *passwd; + + if (now < auth_expires) { + passwd = auth_passwd; + lifetime = (auth_expires - now); + if (lifetime > INT_MAX) { + lifetime = INT_MAX; + } + } else { + passwd = NULL; + lifetime = 1; + } + return spice_server_set_ticket(spice_server, passwd, lifetime, + fail_if_conn, disconnect_if_conn); +} + +int qemu_spice_set_passwd(const char *passwd, + bool fail_if_conn, bool disconnect_if_conn) +{ + free(auth_passwd); + auth_passwd = strdup(passwd); + return qemu_spice_set_ticket(fail_if_conn, disconnect_if_conn); +} + +int qemu_spice_set_pw_expire(time_t expires) +{ + auth_expires = expires; + return qemu_spice_set_ticket(false, false); +} + +static void spice_register_config(void) +{ + qemu_add_opts(&qemu_spice_opts); +} +machine_init(spice_register_config); + +static void spice_initialize(void) +{ + qemu_spice_init(); +} +device_init(spice_initialize); diff -Nru qemu-kvm-0.12.5+noroms/ui/spice-display.c qemu-kvm-0.14.1/ui/spice-display.c --- qemu-kvm-0.12.5+noroms/ui/spice-display.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/ui/spice-display.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,412 @@ +/* + * Copyright (C) 2010 Red Hat, Inc. + * + * 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 or + * (at your option) version 3 of the License. + * + * 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, see . + */ + +#include + +#include "qemu-common.h" +#include "qemu-spice.h" +#include "qemu-timer.h" +#include "qemu-queue.h" +#include "monitor.h" +#include "console.h" +#include "sysemu.h" + +#include "spice-display.h" + +static int debug = 0; + +static void GCC_FMT_ATTR(2, 3) dprint(int level, const char *fmt, ...) +{ + va_list args; + + if (level <= debug) { + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + } +} + +int qemu_spice_rect_is_empty(const QXLRect* r) +{ + return r->top == r->bottom || r->left == r->right; +} + +void qemu_spice_rect_union(QXLRect *dest, const QXLRect *r) +{ + if (qemu_spice_rect_is_empty(r)) { + return; + } + + if (qemu_spice_rect_is_empty(dest)) { + *dest = *r; + return; + } + + dest->top = MIN(dest->top, r->top); + dest->left = MIN(dest->left, r->left); + dest->bottom = MAX(dest->bottom, r->bottom); + dest->right = MAX(dest->right, r->right); +} + +/* + * Called from spice server thread context (via interface_get_command). + * + * We must aquire the global qemu mutex here to make sure the + * DisplayState (+DisplaySurface) we are accessing doesn't change + * underneath us. + */ +SimpleSpiceUpdate *qemu_spice_create_update(SimpleSpiceDisplay *ssd) +{ + SimpleSpiceUpdate *update; + QXLDrawable *drawable; + QXLImage *image; + QXLCommand *cmd; + uint8_t *src, *dst; + int by, bw, bh; + + qemu_mutex_lock_iothread(); + if (qemu_spice_rect_is_empty(&ssd->dirty)) { + qemu_mutex_unlock_iothread(); + return NULL; + }; + + dprint(2, "%s: lr %d -> %d, tb -> %d -> %d\n", __FUNCTION__, + ssd->dirty.left, ssd->dirty.right, + ssd->dirty.top, ssd->dirty.bottom); + + update = qemu_mallocz(sizeof(*update)); + drawable = &update->drawable; + image = &update->image; + cmd = &update->ext.cmd; + + bw = ssd->dirty.right - ssd->dirty.left; + bh = ssd->dirty.bottom - ssd->dirty.top; + update->bitmap = qemu_malloc(bw * bh * 4); + + drawable->bbox = ssd->dirty; + drawable->clip.type = SPICE_CLIP_TYPE_NONE; + drawable->effect = QXL_EFFECT_OPAQUE; + drawable->release_info.id = (intptr_t)update; + drawable->type = QXL_DRAW_COPY; + drawable->surfaces_dest[0] = -1; + drawable->surfaces_dest[1] = -1; + drawable->surfaces_dest[2] = -1; + + drawable->u.copy.rop_descriptor = SPICE_ROPD_OP_PUT; + drawable->u.copy.src_bitmap = (intptr_t)image; + drawable->u.copy.src_area.right = bw; + drawable->u.copy.src_area.bottom = bh; + + QXL_SET_IMAGE_ID(image, QXL_IMAGE_GROUP_DEVICE, ssd->unique++); + image->descriptor.type = SPICE_IMAGE_TYPE_BITMAP; + image->bitmap.flags = QXL_BITMAP_DIRECT | QXL_BITMAP_TOP_DOWN; + image->bitmap.stride = bw * 4; + image->descriptor.width = image->bitmap.x = bw; + image->descriptor.height = image->bitmap.y = bh; + image->bitmap.data = (intptr_t)(update->bitmap); + image->bitmap.palette = 0; + image->bitmap.format = SPICE_BITMAP_FMT_32BIT; + + if (ssd->conv == NULL) { + PixelFormat dst = qemu_default_pixelformat(32); + ssd->conv = qemu_pf_conv_get(&dst, &ssd->ds->surface->pf); + assert(ssd->conv); + } + + src = ds_get_data(ssd->ds) + + ssd->dirty.top * ds_get_linesize(ssd->ds) + + ssd->dirty.left * ds_get_bytes_per_pixel(ssd->ds); + dst = update->bitmap; + for (by = 0; by < bh; by++) { + qemu_pf_conv_run(ssd->conv, dst, src, bw); + src += ds_get_linesize(ssd->ds); + dst += image->bitmap.stride; + } + + cmd->type = QXL_CMD_DRAW; + cmd->data = (intptr_t)drawable; + + memset(&ssd->dirty, 0, sizeof(ssd->dirty)); + qemu_mutex_unlock_iothread(); + return update; +} + +/* + * Called from spice server thread context (via interface_release_ressource) + * We do *not* hold the global qemu mutex here, so extra care is needed + * when calling qemu functions. Qemu interfaces used: + * - qemu_free (underlying glibc free is re-entrant). + */ +void qemu_spice_destroy_update(SimpleSpiceDisplay *sdpy, SimpleSpiceUpdate *update) +{ + qemu_free(update->bitmap); + qemu_free(update); +} + +void qemu_spice_create_host_memslot(SimpleSpiceDisplay *ssd) +{ + QXLDevMemSlot memslot; + + dprint(1, "%s:\n", __FUNCTION__); + + memset(&memslot, 0, sizeof(memslot)); + memslot.slot_group_id = MEMSLOT_GROUP_HOST; + memslot.virt_end = ~0; + ssd->worker->add_memslot(ssd->worker, &memslot); +} + +void qemu_spice_create_host_primary(SimpleSpiceDisplay *ssd) +{ + QXLDevSurfaceCreate surface; + + dprint(1, "%s: %dx%d\n", __FUNCTION__, + ds_get_width(ssd->ds), ds_get_height(ssd->ds)); + + surface.format = SPICE_SURFACE_FMT_32_xRGB; + surface.width = ds_get_width(ssd->ds); + surface.height = ds_get_height(ssd->ds); + surface.stride = -surface.width * 4; + surface.mouse_mode = true; + surface.flags = 0; + surface.type = 0; + surface.mem = (intptr_t)ssd->buf; + surface.group_id = MEMSLOT_GROUP_HOST; + + qemu_mutex_unlock_iothread(); + ssd->worker->create_primary_surface(ssd->worker, 0, &surface); + qemu_mutex_lock_iothread(); +} + +void qemu_spice_destroy_host_primary(SimpleSpiceDisplay *ssd) +{ + dprint(1, "%s:\n", __FUNCTION__); + + qemu_mutex_unlock_iothread(); + ssd->worker->destroy_primary_surface(ssd->worker, 0); + qemu_mutex_lock_iothread(); +} + +void qemu_spice_vm_change_state_handler(void *opaque, int running, int reason) +{ + SimpleSpiceDisplay *ssd = opaque; + + if (running) { + ssd->worker->start(ssd->worker); + } else { + qemu_mutex_unlock_iothread(); + ssd->worker->stop(ssd->worker); + qemu_mutex_lock_iothread(); + } + ssd->running = running; +} + +/* display listener callbacks */ + +void qemu_spice_display_update(SimpleSpiceDisplay *ssd, + int x, int y, int w, int h) +{ + QXLRect update_area; + + dprint(2, "%s: x %d y %d w %d h %d\n", __FUNCTION__, x, y, w, h); + update_area.left = x, + update_area.right = x + w; + update_area.top = y; + update_area.bottom = y + h; + + if (qemu_spice_rect_is_empty(&ssd->dirty)) { + ssd->notify++; + } + qemu_spice_rect_union(&ssd->dirty, &update_area); +} + +void qemu_spice_display_resize(SimpleSpiceDisplay *ssd) +{ + dprint(1, "%s:\n", __FUNCTION__); + + memset(&ssd->dirty, 0, sizeof(ssd->dirty)); + qemu_pf_conv_put(ssd->conv); + ssd->conv = NULL; + + qemu_spice_destroy_host_primary(ssd); + qemu_spice_create_host_primary(ssd); + + memset(&ssd->dirty, 0, sizeof(ssd->dirty)); + ssd->notify++; +} + +void qemu_spice_display_refresh(SimpleSpiceDisplay *ssd) +{ + dprint(3, "%s:\n", __FUNCTION__); + vga_hw_update(); + if (ssd->notify) { + ssd->notify = 0; + ssd->worker->wakeup(ssd->worker); + dprint(2, "%s: notify\n", __FUNCTION__); + } +} + +/* spice display interface callbacks */ + +static void interface_attach_worker(QXLInstance *sin, QXLWorker *qxl_worker) +{ + SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl); + + dprint(1, "%s:\n", __FUNCTION__); + ssd->worker = qxl_worker; +} + +static void interface_set_compression_level(QXLInstance *sin, int level) +{ + dprint(1, "%s:\n", __FUNCTION__); + /* nothing to do */ +} + +static void interface_set_mm_time(QXLInstance *sin, uint32_t mm_time) +{ + dprint(3, "%s:\n", __FUNCTION__); + /* nothing to do */ +} + +static void interface_get_init_info(QXLInstance *sin, QXLDevInitInfo *info) +{ + SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl); + + info->memslot_gen_bits = MEMSLOT_GENERATION_BITS; + info->memslot_id_bits = MEMSLOT_SLOT_BITS; + info->num_memslots = NUM_MEMSLOTS; + info->num_memslots_groups = NUM_MEMSLOTS_GROUPS; + info->internal_groupslot_id = 0; + info->qxl_ram_size = ssd->bufsize; + info->n_surfaces = NUM_SURFACES; +} + +static int interface_get_command(QXLInstance *sin, struct QXLCommandExt *ext) +{ + SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl); + SimpleSpiceUpdate *update; + + dprint(3, "%s:\n", __FUNCTION__); + update = qemu_spice_create_update(ssd); + if (update == NULL) { + return false; + } + *ext = update->ext; + return true; +} + +static int interface_req_cmd_notification(QXLInstance *sin) +{ + dprint(1, "%s:\n", __FUNCTION__); + return 1; +} + +static void interface_release_resource(QXLInstance *sin, + struct QXLReleaseInfoExt ext) +{ + SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl); + uintptr_t id; + + dprint(2, "%s:\n", __FUNCTION__); + id = ext.info->id; + qemu_spice_destroy_update(ssd, (void*)id); +} + +static int interface_get_cursor_command(QXLInstance *sin, struct QXLCommandExt *ext) +{ + dprint(3, "%s:\n", __FUNCTION__); + return false; +} + +static int interface_req_cursor_notification(QXLInstance *sin) +{ + dprint(1, "%s:\n", __FUNCTION__); + return 1; +} + +static void interface_notify_update(QXLInstance *sin, uint32_t update_id) +{ + fprintf(stderr, "%s: abort()\n", __FUNCTION__); + abort(); +} + +static int interface_flush_resources(QXLInstance *sin) +{ + fprintf(stderr, "%s: abort()\n", __FUNCTION__); + abort(); + return 0; +} + +static const QXLInterface dpy_interface = { + .base.type = SPICE_INTERFACE_QXL, + .base.description = "qemu simple display", + .base.major_version = SPICE_INTERFACE_QXL_MAJOR, + .base.minor_version = SPICE_INTERFACE_QXL_MINOR, + + .attache_worker = interface_attach_worker, + .set_compression_level = interface_set_compression_level, + .set_mm_time = interface_set_mm_time, + .get_init_info = interface_get_init_info, + + /* the callbacks below are called from spice server thread context */ + .get_command = interface_get_command, + .req_cmd_notification = interface_req_cmd_notification, + .release_resource = interface_release_resource, + .get_cursor_command = interface_get_cursor_command, + .req_cursor_notification = interface_req_cursor_notification, + .notify_update = interface_notify_update, + .flush_resources = interface_flush_resources, +}; + +static SimpleSpiceDisplay sdpy; + +static void display_update(struct DisplayState *ds, int x, int y, int w, int h) +{ + qemu_spice_display_update(&sdpy, x, y, w, h); +} + +static void display_resize(struct DisplayState *ds) +{ + qemu_spice_display_resize(&sdpy); +} + +static void display_refresh(struct DisplayState *ds) +{ + qemu_spice_display_refresh(&sdpy); +} + +static DisplayChangeListener display_listener = { + .dpy_update = display_update, + .dpy_resize = display_resize, + .dpy_refresh = display_refresh, +}; + +void qemu_spice_display_init(DisplayState *ds) +{ + assert(sdpy.ds == NULL); + sdpy.ds = ds; + sdpy.bufsize = (16 * 1024 * 1024); + sdpy.buf = qemu_malloc(sdpy.bufsize); + register_displaychangelistener(ds, &display_listener); + + sdpy.qxl.base.sif = &dpy_interface.base; + qemu_spice_add_interface(&sdpy.qxl.base); + assert(sdpy.worker); + + qemu_add_vm_change_state_handler(qemu_spice_vm_change_state_handler, &sdpy); + qemu_spice_create_host_memslot(&sdpy); + qemu_spice_create_host_primary(&sdpy); +} diff -Nru qemu-kvm-0.12.5+noroms/ui/spice-display.h qemu-kvm-0.14.1/ui/spice-display.h --- qemu-kvm-0.12.5+noroms/ui/spice-display.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/ui/spice-display.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2010 Red Hat, Inc. + * + * 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 or + * (at your option) version 3 of the License. + * + * 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, see . + */ + +#include +#include +#include + +#include "pflib.h" + +#define NUM_MEMSLOTS 8 +#define MEMSLOT_GENERATION_BITS 8 +#define MEMSLOT_SLOT_BITS 8 + +#define MEMSLOT_GROUP_HOST 0 +#define MEMSLOT_GROUP_GUEST 1 +#define NUM_MEMSLOTS_GROUPS 2 + +#define NUM_SURFACES 1024 + +typedef struct SimpleSpiceDisplay { + DisplayState *ds; + void *buf; + int bufsize; + QXLWorker *worker; + QXLInstance qxl; + uint32_t unique; + QemuPfConv *conv; + + QXLRect dirty; + int notify; + int running; +} SimpleSpiceDisplay; + +typedef struct SimpleSpiceUpdate { + QXLDrawable drawable; + QXLImage image; + QXLCommandExt ext; + uint8_t *bitmap; +} SimpleSpiceUpdate; + +int qemu_spice_rect_is_empty(const QXLRect* r); +void qemu_spice_rect_union(QXLRect *dest, const QXLRect *r); + +SimpleSpiceUpdate *qemu_spice_create_update(SimpleSpiceDisplay *sdpy); +void qemu_spice_destroy_update(SimpleSpiceDisplay *sdpy, SimpleSpiceUpdate *update); +void qemu_spice_create_host_memslot(SimpleSpiceDisplay *ssd); +void qemu_spice_create_host_primary(SimpleSpiceDisplay *ssd); +void qemu_spice_destroy_host_primary(SimpleSpiceDisplay *ssd); +void qemu_spice_vm_change_state_handler(void *opaque, int running, int reason); + +void qemu_spice_display_update(SimpleSpiceDisplay *ssd, + int x, int y, int w, int h); +void qemu_spice_display_resize(SimpleSpiceDisplay *ssd); +void qemu_spice_display_refresh(SimpleSpiceDisplay *ssd); diff -Nru qemu-kvm-0.12.5+noroms/ui/spice-input.c qemu-kvm-0.14.1/ui/spice-input.c --- qemu-kvm-0.12.5+noroms/ui/spice-input.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/ui/spice-input.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2010 Red Hat, Inc. + * + * 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 or + * (at your option) version 3 of the License. + * + * 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, see . + */ + +#include +#include +#include +#include + +#include +#include + +#include "qemu-common.h" +#include "qemu-spice.h" +#include "console.h" + +/* keyboard bits */ + +typedef struct QemuSpiceKbd { + SpiceKbdInstance sin; + int ledstate; +} QemuSpiceKbd; + +static void kbd_push_key(SpiceKbdInstance *sin, uint8_t frag); +static uint8_t kbd_get_leds(SpiceKbdInstance *sin); +static void kbd_leds(void *opaque, int l); + +static const SpiceKbdInterface kbd_interface = { + .base.type = SPICE_INTERFACE_KEYBOARD, + .base.description = "qemu keyboard", + .base.major_version = SPICE_INTERFACE_KEYBOARD_MAJOR, + .base.minor_version = SPICE_INTERFACE_KEYBOARD_MINOR, + .push_scan_freg = kbd_push_key, + .get_leds = kbd_get_leds, +}; + +static void kbd_push_key(SpiceKbdInstance *sin, uint8_t frag) +{ + kbd_put_keycode(frag); +} + +static uint8_t kbd_get_leds(SpiceKbdInstance *sin) +{ + QemuSpiceKbd *kbd = container_of(sin, QemuSpiceKbd, sin); + return kbd->ledstate; +} + +static void kbd_leds(void *opaque, int ledstate) +{ + QemuSpiceKbd *kbd = opaque; + + kbd->ledstate = 0; + if (ledstate & QEMU_SCROLL_LOCK_LED) { + kbd->ledstate |= SPICE_KEYBOARD_MODIFIER_FLAGS_SCROLL_LOCK; + } + if (ledstate & QEMU_NUM_LOCK_LED) { + kbd->ledstate |= SPICE_KEYBOARD_MODIFIER_FLAGS_NUM_LOCK; + } + if (ledstate & QEMU_CAPS_LOCK_LED) { + kbd->ledstate |= SPICE_KEYBOARD_MODIFIER_FLAGS_CAPS_LOCK; + } + spice_server_kbd_leds(&kbd->sin, ledstate); +} + +/* mouse bits */ + +typedef struct QemuSpicePointer { + SpiceMouseInstance mouse; + SpiceTabletInstance tablet; + int width, height, x, y; + Notifier mouse_mode; + bool absolute; +} QemuSpicePointer; + +static int map_buttons(int spice_buttons) +{ + int qemu_buttons = 0; + + /* + * Note: SPICE_MOUSE_BUTTON_* specifies the wire protocol but this + * isn't what we get passed in via interface callbacks for the + * middle and right button ... + */ + if (spice_buttons & SPICE_MOUSE_BUTTON_MASK_LEFT) { + qemu_buttons |= MOUSE_EVENT_LBUTTON; + } + if (spice_buttons & 0x04 /* SPICE_MOUSE_BUTTON_MASK_MIDDLE */) { + qemu_buttons |= MOUSE_EVENT_MBUTTON; + } + if (spice_buttons & 0x02 /* SPICE_MOUSE_BUTTON_MASK_RIGHT */) { + qemu_buttons |= MOUSE_EVENT_RBUTTON; + } + return qemu_buttons; +} + +static void mouse_motion(SpiceMouseInstance *sin, int dx, int dy, int dz, + uint32_t buttons_state) +{ + kbd_mouse_event(dx, dy, dz, map_buttons(buttons_state)); +} + +static void mouse_buttons(SpiceMouseInstance *sin, uint32_t buttons_state) +{ + kbd_mouse_event(0, 0, 0, map_buttons(buttons_state)); +} + +static const SpiceMouseInterface mouse_interface = { + .base.type = SPICE_INTERFACE_MOUSE, + .base.description = "mouse", + .base.major_version = SPICE_INTERFACE_MOUSE_MAJOR, + .base.minor_version = SPICE_INTERFACE_MOUSE_MINOR, + .motion = mouse_motion, + .buttons = mouse_buttons, +}; + +static void tablet_set_logical_size(SpiceTabletInstance* sin, int width, int height) +{ + QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, tablet); + + if (height < 16) { + height = 16; + } + if (width < 16) { + width = 16; + } + pointer->width = width; + pointer->height = height; +} + +static void tablet_position(SpiceTabletInstance* sin, int x, int y, + uint32_t buttons_state) +{ + QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, tablet); + + pointer->x = x * 0x7FFF / (pointer->width - 1); + pointer->y = y * 0x7FFF / (pointer->height - 1); + kbd_mouse_event(pointer->x, pointer->y, 0, map_buttons(buttons_state)); +} + + +static void tablet_wheel(SpiceTabletInstance* sin, int wheel, + uint32_t buttons_state) +{ + QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, tablet); + + kbd_mouse_event(pointer->x, pointer->y, wheel, map_buttons(buttons_state)); +} + +static void tablet_buttons(SpiceTabletInstance *sin, + uint32_t buttons_state) +{ + QemuSpicePointer *pointer = container_of(sin, QemuSpicePointer, tablet); + + kbd_mouse_event(pointer->x, pointer->y, 0, map_buttons(buttons_state)); +} + +static const SpiceTabletInterface tablet_interface = { + .base.type = SPICE_INTERFACE_TABLET, + .base.description = "tablet", + .base.major_version = SPICE_INTERFACE_TABLET_MAJOR, + .base.minor_version = SPICE_INTERFACE_TABLET_MINOR, + .set_logical_size = tablet_set_logical_size, + .position = tablet_position, + .wheel = tablet_wheel, + .buttons = tablet_buttons, +}; + +static void mouse_mode_notifier(Notifier *notifier) +{ + QemuSpicePointer *pointer = container_of(notifier, QemuSpicePointer, mouse_mode); + bool is_absolute = kbd_mouse_is_absolute(); + + if (pointer->absolute == is_absolute) { + return; + } + + if (is_absolute) { + qemu_spice_add_interface(&pointer->tablet.base); + } else { + spice_server_remove_interface(&pointer->tablet.base); + } + pointer->absolute = is_absolute; +} + +void qemu_spice_input_init(void) +{ + QemuSpiceKbd *kbd; + QemuSpicePointer *pointer; + + kbd = qemu_mallocz(sizeof(*kbd)); + kbd->sin.base.sif = &kbd_interface.base; + qemu_spice_add_interface(&kbd->sin.base); + qemu_add_led_event_handler(kbd_leds, kbd); + + pointer = qemu_mallocz(sizeof(*pointer)); + pointer->mouse.base.sif = &mouse_interface.base; + pointer->tablet.base.sif = &tablet_interface.base; + qemu_spice_add_interface(&pointer->mouse.base); + + pointer->absolute = false; + pointer->mouse_mode.notify = mouse_mode_notifier; + qemu_add_mouse_mode_change_notifier(&pointer->mouse_mode); + mouse_mode_notifier(&pointer->mouse_mode); +} diff -Nru qemu-kvm-0.12.5+noroms/ui/vnc-auth-sasl.c qemu-kvm-0.14.1/ui/vnc-auth-sasl.c --- qemu-kvm-0.12.5+noroms/ui/vnc-auth-sasl.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/ui/vnc-auth-sasl.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,639 @@ +/* + * QEMU VNC display driver: SASL auth protocol + * + * Copyright (C) 2009 Red Hat, Inc + * + * 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 "vnc.h" + +/* Max amount of data we send/recv for SASL steps to prevent DOS */ +#define SASL_DATA_MAX_LEN (1024 * 1024) + + +void vnc_sasl_client_cleanup(VncState *vs) +{ + if (vs->sasl.conn) { + vs->sasl.runSSF = vs->sasl.waitWriteSSF = vs->sasl.wantSSF = 0; + vs->sasl.encodedLength = vs->sasl.encodedOffset = 0; + vs->sasl.encoded = NULL; + free(vs->sasl.username); + free(vs->sasl.mechlist); + vs->sasl.username = vs->sasl.mechlist = NULL; + sasl_dispose(&vs->sasl.conn); + vs->sasl.conn = NULL; + } +} + + +long vnc_client_write_sasl(VncState *vs) +{ + long ret; + + VNC_DEBUG("Write SASL: Pending output %p size %zd offset %zd " + "Encoded: %p size %d offset %d\n", + vs->output.buffer, vs->output.capacity, vs->output.offset, + vs->sasl.encoded, vs->sasl.encodedLength, vs->sasl.encodedOffset); + + if (!vs->sasl.encoded) { + int err; + err = sasl_encode(vs->sasl.conn, + (char *)vs->output.buffer, + vs->output.offset, + (const char **)&vs->sasl.encoded, + &vs->sasl.encodedLength); + if (err != SASL_OK) + return vnc_client_io_error(vs, -1, EIO); + + vs->sasl.encodedOffset = 0; + } + + ret = vnc_client_write_buf(vs, + vs->sasl.encoded + vs->sasl.encodedOffset, + vs->sasl.encodedLength - vs->sasl.encodedOffset); + if (!ret) + return 0; + + vs->sasl.encodedOffset += ret; + if (vs->sasl.encodedOffset == vs->sasl.encodedLength) { + vs->output.offset = 0; + vs->sasl.encoded = NULL; + vs->sasl.encodedOffset = vs->sasl.encodedLength = 0; + } + + /* Can't merge this block with one above, because + * someone might have written more unencrypted + * data in vs->output while we were processing + * SASL encoded output + */ + if (vs->output.offset == 0) { + qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs); + } + + return ret; +} + + +long vnc_client_read_sasl(VncState *vs) +{ + long ret; + uint8_t encoded[4096]; + const char *decoded; + unsigned int decodedLen; + int err; + + ret = vnc_client_read_buf(vs, encoded, sizeof(encoded)); + if (!ret) + return 0; + + err = sasl_decode(vs->sasl.conn, + (char *)encoded, ret, + &decoded, &decodedLen); + + if (err != SASL_OK) + return vnc_client_io_error(vs, -1, -EIO); + VNC_DEBUG("Read SASL Encoded %p size %ld Decoded %p size %d\n", + encoded, ret, decoded, decodedLen); + buffer_reserve(&vs->input, decodedLen); + buffer_append(&vs->input, decoded, decodedLen); + return decodedLen; +} + + +static int vnc_auth_sasl_check_access(VncState *vs) +{ + const void *val; + int err; + int allow; + + err = sasl_getprop(vs->sasl.conn, SASL_USERNAME, &val); + if (err != SASL_OK) { + VNC_DEBUG("cannot query SASL username on connection %d (%s), denying access\n", + err, sasl_errstring(err, NULL, NULL)); + return -1; + } + if (val == NULL) { + VNC_DEBUG("no client username was found, denying access\n"); + return -1; + } + VNC_DEBUG("SASL client username %s\n", (const char *)val); + + vs->sasl.username = qemu_strdup((const char*)val); + + if (vs->vd->sasl.acl == NULL) { + VNC_DEBUG("no ACL activated, allowing access\n"); + return 0; + } + + allow = qemu_acl_party_is_allowed(vs->vd->sasl.acl, vs->sasl.username); + + VNC_DEBUG("SASL client %s %s by ACL\n", vs->sasl.username, + allow ? "allowed" : "denied"); + return allow ? 0 : -1; +} + +static int vnc_auth_sasl_check_ssf(VncState *vs) +{ + const void *val; + int err, ssf; + + if (!vs->sasl.wantSSF) + return 1; + + err = sasl_getprop(vs->sasl.conn, SASL_SSF, &val); + if (err != SASL_OK) + return 0; + + ssf = *(const int *)val; + VNC_DEBUG("negotiated an SSF of %d\n", ssf); + if (ssf < 56) + return 0; /* 56 is good for Kerberos */ + + /* Only setup for read initially, because we're about to send an RPC + * reply which must be in plain text. When the next incoming RPC + * arrives, we'll switch on writes too + * + * cf qemudClientReadSASL in qemud.c + */ + vs->sasl.runSSF = 1; + + /* We have a SSF that's good enough */ + return 1; +} + +/* + * Step Msg + * + * Input from client: + * + * u32 clientin-length + * u8-array clientin-string + * + * Output to client: + * + * u32 serverout-length + * u8-array serverout-strin + * u8 continue + */ + +static int protocol_client_auth_sasl_step_len(VncState *vs, uint8_t *data, size_t len); + +static int protocol_client_auth_sasl_step(VncState *vs, uint8_t *data, size_t len) +{ + uint32_t datalen = len; + const char *serverout; + unsigned int serveroutlen; + int err; + char *clientdata = NULL; + + /* NB, distinction of NULL vs "" is *critical* in SASL */ + if (datalen) { + clientdata = (char*)data; + clientdata[datalen-1] = '\0'; /* Wire includes '\0', but make sure */ + datalen--; /* Don't count NULL byte when passing to _start() */ + } + + VNC_DEBUG("Step using SASL Data %p (%d bytes)\n", + clientdata, datalen); + err = sasl_server_step(vs->sasl.conn, + clientdata, + datalen, + &serverout, + &serveroutlen); + if (err != SASL_OK && + err != SASL_CONTINUE) { + VNC_DEBUG("sasl step failed %d (%s)\n", + err, sasl_errdetail(vs->sasl.conn)); + sasl_dispose(&vs->sasl.conn); + vs->sasl.conn = NULL; + goto authabort; + } + + if (serveroutlen > SASL_DATA_MAX_LEN) { + VNC_DEBUG("sasl step reply data too long %d\n", + serveroutlen); + sasl_dispose(&vs->sasl.conn); + vs->sasl.conn = NULL; + goto authabort; + } + + VNC_DEBUG("SASL return data %d bytes, nil; %d\n", + serveroutlen, serverout ? 0 : 1); + + if (serveroutlen) { + vnc_write_u32(vs, serveroutlen + 1); + vnc_write(vs, serverout, serveroutlen + 1); + } else { + vnc_write_u32(vs, 0); + } + + /* Whether auth is complete */ + vnc_write_u8(vs, err == SASL_CONTINUE ? 0 : 1); + + if (err == SASL_CONTINUE) { + VNC_DEBUG("%s", "Authentication must continue\n"); + /* Wait for step length */ + vnc_read_when(vs, protocol_client_auth_sasl_step_len, 4); + } else { + if (!vnc_auth_sasl_check_ssf(vs)) { + VNC_DEBUG("Authentication rejected for weak SSF %d\n", vs->csock); + goto authreject; + } + + /* Check username whitelist ACL */ + if (vnc_auth_sasl_check_access(vs) < 0) { + VNC_DEBUG("Authentication rejected for ACL %d\n", vs->csock); + goto authreject; + } + + VNC_DEBUG("Authentication successful %d\n", vs->csock); + vnc_write_u32(vs, 0); /* Accept auth */ + /* + * Delay writing in SSF encoded mode until pending output + * buffer is written + */ + if (vs->sasl.runSSF) + vs->sasl.waitWriteSSF = vs->output.offset; + start_client_init(vs); + } + + return 0; + + authreject: + vnc_write_u32(vs, 1); /* Reject auth */ + vnc_write_u32(vs, sizeof("Authentication failed")); + vnc_write(vs, "Authentication failed", sizeof("Authentication failed")); + vnc_flush(vs); + vnc_client_error(vs); + return -1; + + authabort: + vnc_client_error(vs); + return -1; +} + +static int protocol_client_auth_sasl_step_len(VncState *vs, uint8_t *data, size_t len) +{ + uint32_t steplen = read_u32(data, 0); + VNC_DEBUG("Got client step len %d\n", steplen); + if (steplen > SASL_DATA_MAX_LEN) { + VNC_DEBUG("Too much SASL data %d\n", steplen); + vnc_client_error(vs); + return -1; + } + + if (steplen == 0) + return protocol_client_auth_sasl_step(vs, NULL, 0); + else + vnc_read_when(vs, protocol_client_auth_sasl_step, steplen); + return 0; +} + +/* + * Start Msg + * + * Input from client: + * + * u32 clientin-length + * u8-array clientin-string + * + * Output to client: + * + * u32 serverout-length + * u8-array serverout-strin + * u8 continue + */ + +#define SASL_DATA_MAX_LEN (1024 * 1024) + +static int protocol_client_auth_sasl_start(VncState *vs, uint8_t *data, size_t len) +{ + uint32_t datalen = len; + const char *serverout; + unsigned int serveroutlen; + int err; + char *clientdata = NULL; + + /* NB, distinction of NULL vs "" is *critical* in SASL */ + if (datalen) { + clientdata = (char*)data; + clientdata[datalen-1] = '\0'; /* Should be on wire, but make sure */ + datalen--; /* Don't count NULL byte when passing to _start() */ + } + + VNC_DEBUG("Start SASL auth with mechanism %s. Data %p (%d bytes)\n", + vs->sasl.mechlist, clientdata, datalen); + err = sasl_server_start(vs->sasl.conn, + vs->sasl.mechlist, + clientdata, + datalen, + &serverout, + &serveroutlen); + if (err != SASL_OK && + err != SASL_CONTINUE) { + VNC_DEBUG("sasl start failed %d (%s)\n", + err, sasl_errdetail(vs->sasl.conn)); + sasl_dispose(&vs->sasl.conn); + vs->sasl.conn = NULL; + goto authabort; + } + if (serveroutlen > SASL_DATA_MAX_LEN) { + VNC_DEBUG("sasl start reply data too long %d\n", + serveroutlen); + sasl_dispose(&vs->sasl.conn); + vs->sasl.conn = NULL; + goto authabort; + } + + VNC_DEBUG("SASL return data %d bytes, nil; %d\n", + serveroutlen, serverout ? 0 : 1); + + if (serveroutlen) { + vnc_write_u32(vs, serveroutlen + 1); + vnc_write(vs, serverout, serveroutlen + 1); + } else { + vnc_write_u32(vs, 0); + } + + /* Whether auth is complete */ + vnc_write_u8(vs, err == SASL_CONTINUE ? 0 : 1); + + if (err == SASL_CONTINUE) { + VNC_DEBUG("%s", "Authentication must continue\n"); + /* Wait for step length */ + vnc_read_when(vs, protocol_client_auth_sasl_step_len, 4); + } else { + if (!vnc_auth_sasl_check_ssf(vs)) { + VNC_DEBUG("Authentication rejected for weak SSF %d\n", vs->csock); + goto authreject; + } + + /* Check username whitelist ACL */ + if (vnc_auth_sasl_check_access(vs) < 0) { + VNC_DEBUG("Authentication rejected for ACL %d\n", vs->csock); + goto authreject; + } + + VNC_DEBUG("Authentication successful %d\n", vs->csock); + vnc_write_u32(vs, 0); /* Accept auth */ + start_client_init(vs); + } + + return 0; + + authreject: + vnc_write_u32(vs, 1); /* Reject auth */ + vnc_write_u32(vs, sizeof("Authentication failed")); + vnc_write(vs, "Authentication failed", sizeof("Authentication failed")); + vnc_flush(vs); + vnc_client_error(vs); + return -1; + + authabort: + vnc_client_error(vs); + return -1; +} + +static int protocol_client_auth_sasl_start_len(VncState *vs, uint8_t *data, size_t len) +{ + uint32_t startlen = read_u32(data, 0); + VNC_DEBUG("Got client start len %d\n", startlen); + if (startlen > SASL_DATA_MAX_LEN) { + VNC_DEBUG("Too much SASL data %d\n", startlen); + vnc_client_error(vs); + return -1; + } + + if (startlen == 0) + return protocol_client_auth_sasl_start(vs, NULL, 0); + + vnc_read_when(vs, protocol_client_auth_sasl_start, startlen); + return 0; +} + +static int protocol_client_auth_sasl_mechname(VncState *vs, uint8_t *data, size_t len) +{ + char *mechname = malloc(len + 1); + if (!mechname) { + VNC_DEBUG("Out of memory reading mechname\n"); + vnc_client_error(vs); + } + strncpy(mechname, (char*)data, len); + mechname[len] = '\0'; + VNC_DEBUG("Got client mechname '%s' check against '%s'\n", + mechname, vs->sasl.mechlist); + + if (strncmp(vs->sasl.mechlist, mechname, len) == 0) { + if (vs->sasl.mechlist[len] != '\0' && + vs->sasl.mechlist[len] != ',') { + VNC_DEBUG("One %d", vs->sasl.mechlist[len]); + goto fail; + } + } else { + char *offset = strstr(vs->sasl.mechlist, mechname); + VNC_DEBUG("Two %p\n", offset); + if (!offset) { + goto fail; + } + VNC_DEBUG("Two '%s'\n", offset); + if (offset[-1] != ',' || + (offset[len] != '\0'&& + offset[len] != ',')) { + goto fail; + } + } + + free(vs->sasl.mechlist); + vs->sasl.mechlist = mechname; + + VNC_DEBUG("Validated mechname '%s'\n", mechname); + vnc_read_when(vs, protocol_client_auth_sasl_start_len, 4); + return 0; + + fail: + vnc_client_error(vs); + free(mechname); + return -1; +} + +static int protocol_client_auth_sasl_mechname_len(VncState *vs, uint8_t *data, size_t len) +{ + uint32_t mechlen = read_u32(data, 0); + VNC_DEBUG("Got client mechname len %d\n", mechlen); + if (mechlen > 100) { + VNC_DEBUG("Too long SASL mechname data %d\n", mechlen); + vnc_client_error(vs); + return -1; + } + if (mechlen < 1) { + VNC_DEBUG("Too short SASL mechname %d\n", mechlen); + vnc_client_error(vs); + return -1; + } + vnc_read_when(vs, protocol_client_auth_sasl_mechname,mechlen); + return 0; +} + +#define USES_X509_AUTH(vs) \ + ((vs)->subauth == VNC_AUTH_VENCRYPT_X509NONE || \ + (vs)->subauth == VNC_AUTH_VENCRYPT_X509VNC || \ + (vs)->subauth == VNC_AUTH_VENCRYPT_X509PLAIN || \ + (vs)->subauth == VNC_AUTH_VENCRYPT_X509SASL) + + +void start_auth_sasl(VncState *vs) +{ + const char *mechlist = NULL; + sasl_security_properties_t secprops; + int err; + char *localAddr, *remoteAddr; + int mechlistlen; + + VNC_DEBUG("Initialize SASL auth %d\n", vs->csock); + + /* Get local & remote client addresses in form IPADDR;PORT */ + if (!(localAddr = vnc_socket_local_addr("%s;%s", vs->csock))) + goto authabort; + + if (!(remoteAddr = vnc_socket_remote_addr("%s;%s", vs->csock))) { + free(localAddr); + goto authabort; + } + + err = sasl_server_new("vnc", + NULL, /* FQDN - just delegates to gethostname */ + NULL, /* User realm */ + localAddr, + remoteAddr, + NULL, /* Callbacks, not needed */ + SASL_SUCCESS_DATA, + &vs->sasl.conn); + free(localAddr); + free(remoteAddr); + localAddr = remoteAddr = NULL; + + if (err != SASL_OK) { + VNC_DEBUG("sasl context setup failed %d (%s)", + err, sasl_errstring(err, NULL, NULL)); + vs->sasl.conn = NULL; + goto authabort; + } + +#ifdef CONFIG_VNC_TLS + /* Inform SASL that we've got an external SSF layer from TLS/x509 */ + if (vs->vd->auth == VNC_AUTH_VENCRYPT && + vs->vd->subauth == VNC_AUTH_VENCRYPT_X509SASL) { + gnutls_cipher_algorithm_t cipher; + sasl_ssf_t ssf; + + cipher = gnutls_cipher_get(vs->tls.session); + if (!(ssf = (sasl_ssf_t)gnutls_cipher_get_key_size(cipher))) { + VNC_DEBUG("%s", "cannot TLS get cipher size\n"); + sasl_dispose(&vs->sasl.conn); + vs->sasl.conn = NULL; + goto authabort; + } + ssf *= 8; /* tls key size is bytes, sasl wants bits */ + + err = sasl_setprop(vs->sasl.conn, SASL_SSF_EXTERNAL, &ssf); + if (err != SASL_OK) { + VNC_DEBUG("cannot set SASL external SSF %d (%s)\n", + err, sasl_errstring(err, NULL, NULL)); + sasl_dispose(&vs->sasl.conn); + vs->sasl.conn = NULL; + goto authabort; + } + } else +#endif /* CONFIG_VNC_TLS */ + vs->sasl.wantSSF = 1; + + memset (&secprops, 0, sizeof secprops); + /* Inform SASL that we've got an external SSF layer from TLS */ + if (strncmp(vs->vd->display, "unix:", 5) == 0 +#ifdef CONFIG_VNC_TLS + /* Disable SSF, if using TLS+x509+SASL only. TLS without x509 + is not sufficiently strong */ + || (vs->vd->auth == VNC_AUTH_VENCRYPT && + vs->vd->subauth == VNC_AUTH_VENCRYPT_X509SASL) +#endif /* CONFIG_VNC_TLS */ + ) { + /* If we've got TLS or UNIX domain sock, we don't care about SSF */ + secprops.min_ssf = 0; + secprops.max_ssf = 0; + secprops.maxbufsize = 8192; + secprops.security_flags = 0; + } else { + /* Plain TCP, better get an SSF layer */ + secprops.min_ssf = 56; /* Good enough to require kerberos */ + secprops.max_ssf = 100000; /* Arbitrary big number */ + secprops.maxbufsize = 8192; + /* Forbid any anonymous or trivially crackable auth */ + secprops.security_flags = + SASL_SEC_NOANONYMOUS | SASL_SEC_NOPLAINTEXT; + } + + err = sasl_setprop(vs->sasl.conn, SASL_SEC_PROPS, &secprops); + if (err != SASL_OK) { + VNC_DEBUG("cannot set SASL security props %d (%s)\n", + err, sasl_errstring(err, NULL, NULL)); + sasl_dispose(&vs->sasl.conn); + vs->sasl.conn = NULL; + goto authabort; + } + + err = sasl_listmech(vs->sasl.conn, + NULL, /* Don't need to set user */ + "", /* Prefix */ + ",", /* Separator */ + "", /* Suffix */ + &mechlist, + NULL, + NULL); + if (err != SASL_OK) { + VNC_DEBUG("cannot list SASL mechanisms %d (%s)\n", + err, sasl_errdetail(vs->sasl.conn)); + sasl_dispose(&vs->sasl.conn); + vs->sasl.conn = NULL; + goto authabort; + } + VNC_DEBUG("Available mechanisms for client: '%s'\n", mechlist); + + if (!(vs->sasl.mechlist = strdup(mechlist))) { + VNC_DEBUG("Out of memory"); + sasl_dispose(&vs->sasl.conn); + vs->sasl.conn = NULL; + goto authabort; + } + mechlistlen = strlen(mechlist); + vnc_write_u32(vs, mechlistlen); + vnc_write(vs, mechlist, mechlistlen); + vnc_flush(vs); + + VNC_DEBUG("Wait for client mechname length\n"); + vnc_read_when(vs, protocol_client_auth_sasl_mechname_len, 4); + + return; + + authabort: + vnc_client_error(vs); + return; +} + + diff -Nru qemu-kvm-0.12.5+noroms/ui/vnc-auth-sasl.h qemu-kvm-0.14.1/ui/vnc-auth-sasl.h --- qemu-kvm-0.12.5+noroms/ui/vnc-auth-sasl.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/ui/vnc-auth-sasl.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,74 @@ +/* + * QEMU VNC display driver: SASL auth protocol + * + * Copyright (C) 2009 Red Hat, Inc + * + * 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. + */ + + +#ifndef __QEMU_VNC_AUTH_SASL_H__ +#define __QEMU_VNC_AUTH_SASL_H__ + + +#include + +typedef struct VncStateSASL VncStateSASL; +typedef struct VncDisplaySASL VncDisplaySASL; + +#include "acl.h" + +struct VncStateSASL { + sasl_conn_t *conn; + /* If we want to negotiate an SSF layer with client */ + int wantSSF :1; + /* If we are now running the SSF layer */ + int runSSF :1; + /* + * If this is non-zero, then wait for that many bytes + * to be written plain, before switching to SSF encoding + * This allows the VNC auth result to finish being + * written in plain. + */ + unsigned int waitWriteSSF; + + /* + * Buffering encoded data to allow more clear data + * to be stuffed onto the output buffer + */ + const uint8_t *encoded; + unsigned int encodedLength; + unsigned int encodedOffset; + char *username; + char *mechlist; +}; + +struct VncDisplaySASL { + qemu_acl *acl; +}; + +void vnc_sasl_client_cleanup(VncState *vs); + +long vnc_client_read_sasl(VncState *vs); +long vnc_client_write_sasl(VncState *vs); + +void start_auth_sasl(VncState *vs); + +#endif /* __QEMU_VNC_AUTH_SASL_H__ */ + diff -Nru qemu-kvm-0.12.5+noroms/ui/vnc-auth-vencrypt.c qemu-kvm-0.14.1/ui/vnc-auth-vencrypt.c --- qemu-kvm-0.12.5+noroms/ui/vnc-auth-vencrypt.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/ui/vnc-auth-vencrypt.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,175 @@ +/* + * QEMU VNC display driver: VeNCrypt authentication setup + * + * Copyright (C) 2006 Anthony Liguori + * Copyright (C) 2006 Fabrice Bellard + * Copyright (C) 2009 Red Hat, Inc + * + * 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 "vnc.h" + + +static void start_auth_vencrypt_subauth(VncState *vs) +{ + switch (vs->vd->subauth) { + case VNC_AUTH_VENCRYPT_TLSNONE: + case VNC_AUTH_VENCRYPT_X509NONE: + VNC_DEBUG("Accept TLS auth none\n"); + vnc_write_u32(vs, 0); /* Accept auth completion */ + start_client_init(vs); + break; + + case VNC_AUTH_VENCRYPT_TLSVNC: + case VNC_AUTH_VENCRYPT_X509VNC: + VNC_DEBUG("Start TLS auth VNC\n"); + start_auth_vnc(vs); + break; + +#ifdef CONFIG_VNC_SASL + case VNC_AUTH_VENCRYPT_TLSSASL: + case VNC_AUTH_VENCRYPT_X509SASL: + VNC_DEBUG("Start TLS auth SASL\n"); + return start_auth_sasl(vs); +#endif /* CONFIG_VNC_SASL */ + + default: /* Should not be possible, but just in case */ + VNC_DEBUG("Reject subauth %d server bug\n", vs->vd->auth); + vnc_write_u8(vs, 1); + if (vs->minor >= 8) { + static const char err[] = "Unsupported authentication type"; + vnc_write_u32(vs, sizeof(err)); + vnc_write(vs, err, sizeof(err)); + } + vnc_client_error(vs); + } +} + +static void vnc_tls_handshake_io(void *opaque); + +static int vnc_start_vencrypt_handshake(struct VncState *vs) { + int ret; + + if ((ret = gnutls_handshake(vs->tls.session)) < 0) { + if (!gnutls_error_is_fatal(ret)) { + VNC_DEBUG("Handshake interrupted (blocking)\n"); + if (!gnutls_record_get_direction(vs->tls.session)) + qemu_set_fd_handler(vs->csock, vnc_tls_handshake_io, NULL, vs); + else + qemu_set_fd_handler(vs->csock, NULL, vnc_tls_handshake_io, vs); + return 0; + } + VNC_DEBUG("Handshake failed %s\n", gnutls_strerror(ret)); + vnc_client_error(vs); + return -1; + } + + if (vs->vd->tls.x509verify) { + if (vnc_tls_validate_certificate(vs) < 0) { + VNC_DEBUG("Client verification failed\n"); + vnc_client_error(vs); + return -1; + } else { + VNC_DEBUG("Client verification passed\n"); + } + } + + VNC_DEBUG("Handshake done, switching to TLS data mode\n"); + vs->tls.wiremode = VNC_WIREMODE_TLS; + qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, vnc_client_write, vs); + + start_auth_vencrypt_subauth(vs); + + return 0; +} + +static void vnc_tls_handshake_io(void *opaque) { + struct VncState *vs = (struct VncState *)opaque; + + VNC_DEBUG("Handshake IO continue\n"); + vnc_start_vencrypt_handshake(vs); +} + + + +#define NEED_X509_AUTH(vs) \ + ((vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509NONE || \ + (vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509VNC || \ + (vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509PLAIN || \ + (vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509SASL) + + +static int protocol_client_vencrypt_auth(VncState *vs, uint8_t *data, size_t len) +{ + int auth = read_u32(data, 0); + + if (auth != vs->vd->subauth) { + VNC_DEBUG("Rejecting auth %d\n", auth); + vnc_write_u8(vs, 0); /* Reject auth */ + vnc_flush(vs); + vnc_client_error(vs); + } else { + VNC_DEBUG("Accepting auth %d, setting up TLS for handshake\n", auth); + vnc_write_u8(vs, 1); /* Accept auth */ + vnc_flush(vs); + + if (vnc_tls_client_setup(vs, NEED_X509_AUTH(vs)) < 0) { + VNC_DEBUG("Failed to setup TLS\n"); + return 0; + } + + VNC_DEBUG("Start TLS VeNCrypt handshake process\n"); + if (vnc_start_vencrypt_handshake(vs) < 0) { + VNC_DEBUG("Failed to start TLS handshake\n"); + return 0; + } + } + return 0; +} + +static int protocol_client_vencrypt_init(VncState *vs, uint8_t *data, size_t len) +{ + if (data[0] != 0 || + data[1] != 2) { + VNC_DEBUG("Unsupported VeNCrypt protocol %d.%d\n", (int)data[0], (int)data[1]); + vnc_write_u8(vs, 1); /* Reject version */ + vnc_flush(vs); + vnc_client_error(vs); + } else { + VNC_DEBUG("Sending allowed auth %d\n", vs->vd->subauth); + vnc_write_u8(vs, 0); /* Accept version */ + vnc_write_u8(vs, 1); /* Number of sub-auths */ + vnc_write_u32(vs, vs->vd->subauth); /* The supported auth */ + vnc_flush(vs); + vnc_read_when(vs, protocol_client_vencrypt_auth, 4); + } + return 0; +} + + +void start_auth_vencrypt(VncState *vs) +{ + /* Send VeNCrypt version 0.2 */ + vnc_write_u8(vs, 0); + vnc_write_u8(vs, 2); + + vnc_read_when(vs, protocol_client_vencrypt_init, 2); +} + diff -Nru qemu-kvm-0.12.5+noroms/ui/vnc-auth-vencrypt.h qemu-kvm-0.14.1/ui/vnc-auth-vencrypt.h --- qemu-kvm-0.12.5+noroms/ui/vnc-auth-vencrypt.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/ui/vnc-auth-vencrypt.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,33 @@ +/* + * QEMU VNC display driver + * + * Copyright (C) 2006 Anthony Liguori + * Copyright (C) 2006 Fabrice Bellard + * Copyright (C) 2009 Red Hat, Inc + * + * 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. + */ + + +#ifndef __QEMU_VNC_AUTH_VENCRYPT_H__ +#define __QEMU_VNC_AUTH_VENCRYPT_H__ + +void start_auth_vencrypt(VncState *vs); + +#endif /* __QEMU_VNC_AUTH_VENCRYPT_H__ */ diff -Nru qemu-kvm-0.12.5+noroms/ui/vnc.c qemu-kvm-0.14.1/ui/vnc.c --- qemu-kvm-0.12.5+noroms/ui/vnc.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/ui/vnc.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,2768 @@ +/* + * QEMU VNC display driver + * + * Copyright (C) 2006 Anthony Liguori + * Copyright (C) 2006 Fabrice Bellard + * Copyright (C) 2009 Red Hat, Inc + * + * 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 "vnc.h" +#include "vnc-jobs.h" +#include "sysemu.h" +#include "qemu_socket.h" +#include "qemu-timer.h" +#include "acl.h" +#include "qemu-objects.h" + +#define VNC_REFRESH_INTERVAL_BASE 30 +#define VNC_REFRESH_INTERVAL_INC 50 +#define VNC_REFRESH_INTERVAL_MAX 2000 + +#include "vnc_keysym.h" +#include "d3des.h" + +#define count_bits(c, v) { \ + for (c = 0; v; v >>= 1) \ + { \ + c += v & 1; \ + } \ +} + +static VncDisplay *vnc_display; /* needed for info vnc */ +static DisplayChangeListener *dcl; + +static int vnc_cursor_define(VncState *vs); + +static char *addr_to_string(const char *format, + struct sockaddr_storage *sa, + socklen_t salen) { + char *addr; + char host[NI_MAXHOST]; + char serv[NI_MAXSERV]; + int err; + size_t addrlen; + + if ((err = getnameinfo((struct sockaddr *)sa, salen, + host, sizeof(host), + serv, sizeof(serv), + NI_NUMERICHOST | NI_NUMERICSERV)) != 0) { + VNC_DEBUG("Cannot resolve address %d: %s\n", + err, gai_strerror(err)); + return NULL; + } + + /* Enough for the existing format + the 2 vars we're + * substituting in. */ + addrlen = strlen(format) + strlen(host) + strlen(serv); + addr = qemu_malloc(addrlen + 1); + snprintf(addr, addrlen, format, host, serv); + addr[addrlen] = '\0'; + + return addr; +} + + +char *vnc_socket_local_addr(const char *format, int fd) { + struct sockaddr_storage sa; + socklen_t salen; + + salen = sizeof(sa); + if (getsockname(fd, (struct sockaddr*)&sa, &salen) < 0) + return NULL; + + return addr_to_string(format, &sa, salen); +} + +char *vnc_socket_remote_addr(const char *format, int fd) { + struct sockaddr_storage sa; + socklen_t salen; + + salen = sizeof(sa); + if (getpeername(fd, (struct sockaddr*)&sa, &salen) < 0) + return NULL; + + return addr_to_string(format, &sa, salen); +} + +static int put_addr_qdict(QDict *qdict, struct sockaddr_storage *sa, + socklen_t salen) +{ + char host[NI_MAXHOST]; + char serv[NI_MAXSERV]; + int err; + + if ((err = getnameinfo((struct sockaddr *)sa, salen, + host, sizeof(host), + serv, sizeof(serv), + NI_NUMERICHOST | NI_NUMERICSERV)) != 0) { + VNC_DEBUG("Cannot resolve address %d: %s\n", + err, gai_strerror(err)); + return -1; + } + + qdict_put(qdict, "host", qstring_from_str(host)); + qdict_put(qdict, "service", qstring_from_str(serv)); + qdict_put(qdict, "family",qstring_from_str(inet_strfamily(sa->ss_family))); + + return 0; +} + +static int vnc_server_addr_put(QDict *qdict, int fd) +{ + struct sockaddr_storage sa; + socklen_t salen; + + salen = sizeof(sa); + if (getsockname(fd, (struct sockaddr*)&sa, &salen) < 0) { + return -1; + } + + return put_addr_qdict(qdict, &sa, salen); +} + +static int vnc_qdict_remote_addr(QDict *qdict, int fd) +{ + struct sockaddr_storage sa; + socklen_t salen; + + salen = sizeof(sa); + if (getpeername(fd, (struct sockaddr*)&sa, &salen) < 0) { + return -1; + } + + return put_addr_qdict(qdict, &sa, salen); +} + +static const char *vnc_auth_name(VncDisplay *vd) { + switch (vd->auth) { + case VNC_AUTH_INVALID: + return "invalid"; + case VNC_AUTH_NONE: + return "none"; + case VNC_AUTH_VNC: + return "vnc"; + case VNC_AUTH_RA2: + return "ra2"; + case VNC_AUTH_RA2NE: + return "ra2ne"; + case VNC_AUTH_TIGHT: + return "tight"; + case VNC_AUTH_ULTRA: + return "ultra"; + case VNC_AUTH_TLS: + return "tls"; + case VNC_AUTH_VENCRYPT: +#ifdef CONFIG_VNC_TLS + switch (vd->subauth) { + case VNC_AUTH_VENCRYPT_PLAIN: + return "vencrypt+plain"; + case VNC_AUTH_VENCRYPT_TLSNONE: + return "vencrypt+tls+none"; + case VNC_AUTH_VENCRYPT_TLSVNC: + return "vencrypt+tls+vnc"; + case VNC_AUTH_VENCRYPT_TLSPLAIN: + return "vencrypt+tls+plain"; + case VNC_AUTH_VENCRYPT_X509NONE: + return "vencrypt+x509+none"; + case VNC_AUTH_VENCRYPT_X509VNC: + return "vencrypt+x509+vnc"; + case VNC_AUTH_VENCRYPT_X509PLAIN: + return "vencrypt+x509+plain"; + case VNC_AUTH_VENCRYPT_TLSSASL: + return "vencrypt+tls+sasl"; + case VNC_AUTH_VENCRYPT_X509SASL: + return "vencrypt+x509+sasl"; + default: + return "vencrypt"; + } +#else + return "vencrypt"; +#endif + case VNC_AUTH_SASL: + return "sasl"; + } + return "unknown"; +} + +static int vnc_server_info_put(QDict *qdict) +{ + if (vnc_server_addr_put(qdict, vnc_display->lsock) < 0) { + return -1; + } + + qdict_put(qdict, "auth", qstring_from_str(vnc_auth_name(vnc_display))); + return 0; +} + +static void vnc_client_cache_auth(VncState *client) +{ +#if defined(CONFIG_VNC_TLS) || defined(CONFIG_VNC_SASL) + QDict *qdict; +#endif + + if (!client->info) { + return; + } + +#if defined(CONFIG_VNC_TLS) || defined(CONFIG_VNC_SASL) + qdict = qobject_to_qdict(client->info); +#endif + +#ifdef CONFIG_VNC_TLS + if (client->tls.session && + client->tls.dname) { + qdict_put(qdict, "x509_dname", qstring_from_str(client->tls.dname)); + } +#endif +#ifdef CONFIG_VNC_SASL + if (client->sasl.conn && + client->sasl.username) { + qdict_put(qdict, "sasl_username", + qstring_from_str(client->sasl.username)); + } +#endif +} + +static void vnc_client_cache_addr(VncState *client) +{ + QDict *qdict; + + qdict = qdict_new(); + if (vnc_qdict_remote_addr(qdict, client->csock) < 0) { + QDECREF(qdict); + /* XXX: how to report the error? */ + return; + } + + client->info = QOBJECT(qdict); +} + +static void vnc_qmp_event(VncState *vs, MonitorEvent event) +{ + QDict *server; + QObject *data; + + if (!vs->info) { + return; + } + + server = qdict_new(); + if (vnc_server_info_put(server) < 0) { + QDECREF(server); + return; + } + + data = qobject_from_jsonf("{ 'client': %p, 'server': %p }", + vs->info, QOBJECT(server)); + + monitor_protocol_event(event, data); + + qobject_incref(vs->info); + qobject_decref(data); +} + +static void info_vnc_iter(QObject *obj, void *opaque) +{ + QDict *client; + Monitor *mon = opaque; + + client = qobject_to_qdict(obj); + monitor_printf(mon, "Client:\n"); + monitor_printf(mon, " address: %s:%s\n", + qdict_get_str(client, "host"), + qdict_get_str(client, "service")); + +#ifdef CONFIG_VNC_TLS + monitor_printf(mon, " x509_dname: %s\n", + qdict_haskey(client, "x509_dname") ? + qdict_get_str(client, "x509_dname") : "none"); +#endif +#ifdef CONFIG_VNC_SASL + monitor_printf(mon, " username: %s\n", + qdict_haskey(client, "sasl_username") ? + qdict_get_str(client, "sasl_username") : "none"); +#endif +} + +void do_info_vnc_print(Monitor *mon, const QObject *data) +{ + QDict *server; + QList *clients; + + server = qobject_to_qdict(data); + if (qdict_get_bool(server, "enabled") == 0) { + monitor_printf(mon, "Server: disabled\n"); + return; + } + + monitor_printf(mon, "Server:\n"); + monitor_printf(mon, " address: %s:%s\n", + qdict_get_str(server, "host"), + qdict_get_str(server, "service")); + monitor_printf(mon, " auth: %s\n", qdict_get_str(server, "auth")); + + clients = qdict_get_qlist(server, "clients"); + if (qlist_empty(clients)) { + monitor_printf(mon, "Client: none\n"); + } else { + qlist_iter(clients, info_vnc_iter, mon); + } +} + +void do_info_vnc(Monitor *mon, QObject **ret_data) +{ + if (vnc_display == NULL || vnc_display->display == NULL) { + *ret_data = qobject_from_jsonf("{ 'enabled': false }"); + } else { + QList *clist; + VncState *client; + + clist = qlist_new(); + QTAILQ_FOREACH(client, &vnc_display->clients, next) { + if (client->info) { + /* incref so that it's not freed by upper layers */ + qobject_incref(client->info); + qlist_append_obj(clist, client->info); + } + } + + *ret_data = qobject_from_jsonf("{ 'enabled': true, 'clients': %p }", + QOBJECT(clist)); + assert(*ret_data != NULL); + + if (vnc_server_info_put(qobject_to_qdict(*ret_data)) < 0) { + qobject_decref(*ret_data); + *ret_data = NULL; + } + } +} + +/* TODO + 1) Get the queue working for IO. + 2) there is some weirdness when using the -S option (the screen is grey + and not totally invalidated + 3) resolutions > 1024 +*/ + +static int vnc_update_client(VncState *vs, int has_dirty); +static int vnc_update_client_sync(VncState *vs, int has_dirty); +static void vnc_disconnect_start(VncState *vs); +static void vnc_disconnect_finish(VncState *vs); +static void vnc_init_timer(VncDisplay *vd); +static void vnc_remove_timer(VncDisplay *vd); + +static void vnc_colordepth(VncState *vs); +static void framebuffer_update_request(VncState *vs, int incremental, + int x_position, int y_position, + int w, int h); +static void vnc_refresh(void *opaque); +static int vnc_refresh_server_surface(VncDisplay *vd); + +static inline void vnc_set_bit(uint32_t *d, int k) +{ + d[k >> 5] |= 1 << (k & 0x1f); +} + +static inline void vnc_clear_bit(uint32_t *d, int k) +{ + d[k >> 5] &= ~(1 << (k & 0x1f)); +} + +static inline void vnc_set_bits(uint32_t *d, int n, int nb_words) +{ + int j; + + j = 0; + while (n >= 32) { + d[j++] = -1; + n -= 32; + } + if (n > 0) + d[j++] = (1 << n) - 1; + while (j < nb_words) + d[j++] = 0; +} + +static inline int vnc_get_bit(const uint32_t *d, int k) +{ + return (d[k >> 5] >> (k & 0x1f)) & 1; +} + +static inline int vnc_and_bits(const uint32_t *d1, const uint32_t *d2, + int nb_words) +{ + int i; + for(i = 0; i < nb_words; i++) { + if ((d1[i] & d2[i]) != 0) + return 1; + } + return 0; +} + +static void vnc_dpy_update(DisplayState *ds, int x, int y, int w, int h) +{ + int i; + VncDisplay *vd = ds->opaque; + struct VncSurface *s = &vd->guest; + + h += y; + + /* round x down to ensure the loop only spans one 16-pixel block per, + iteration. otherwise, if (x % 16) != 0, the last iteration may span + two 16-pixel blocks but we only mark the first as dirty + */ + w += (x % 16); + x -= (x % 16); + + x = MIN(x, s->ds->width); + y = MIN(y, s->ds->height); + w = MIN(x + w, s->ds->width) - x; + h = MIN(h, s->ds->height); + + for (; y < h; y++) + for (i = 0; i < w; i += 16) + vnc_set_bit(s->dirty[y], (x + i) / 16); +} + +void vnc_framebuffer_update(VncState *vs, int x, int y, int w, int h, + int32_t encoding) +{ + vnc_write_u16(vs, x); + vnc_write_u16(vs, y); + vnc_write_u16(vs, w); + vnc_write_u16(vs, h); + + vnc_write_s32(vs, encoding); +} + +void buffer_reserve(Buffer *buffer, size_t len) +{ + if ((buffer->capacity - buffer->offset) < len) { + buffer->capacity += (len + 1024); + buffer->buffer = qemu_realloc(buffer->buffer, buffer->capacity); + if (buffer->buffer == NULL) { + fprintf(stderr, "vnc: out of memory\n"); + exit(1); + } + } +} + +int buffer_empty(Buffer *buffer) +{ + return buffer->offset == 0; +} + +uint8_t *buffer_end(Buffer *buffer) +{ + return buffer->buffer + buffer->offset; +} + +void buffer_reset(Buffer *buffer) +{ + buffer->offset = 0; +} + +void buffer_free(Buffer *buffer) +{ + qemu_free(buffer->buffer); + buffer->offset = 0; + buffer->capacity = 0; + buffer->buffer = NULL; +} + +void buffer_append(Buffer *buffer, const void *data, size_t len) +{ + memcpy(buffer->buffer + buffer->offset, data, len); + buffer->offset += len; +} + +static void vnc_desktop_resize(VncState *vs) +{ + DisplayState *ds = vs->ds; + + if (vs->csock == -1 || !vnc_has_feature(vs, VNC_FEATURE_RESIZE)) { + return; + } + if (vs->client_width == ds_get_width(ds) && + vs->client_height == ds_get_height(ds)) { + return; + } + vs->client_width = ds_get_width(ds); + vs->client_height = ds_get_height(ds); + vnc_lock_output(vs); + vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE); + vnc_write_u8(vs, 0); + vnc_write_u16(vs, 1); /* number of rects */ + vnc_framebuffer_update(vs, 0, 0, vs->client_width, vs->client_height, + VNC_ENCODING_DESKTOPRESIZE); + vnc_unlock_output(vs); + vnc_flush(vs); +} + +#ifdef CONFIG_VNC_THREAD +static void vnc_abort_display_jobs(VncDisplay *vd) +{ + VncState *vs; + + QTAILQ_FOREACH(vs, &vd->clients, next) { + vnc_lock_output(vs); + vs->abort = true; + vnc_unlock_output(vs); + } + QTAILQ_FOREACH(vs, &vd->clients, next) { + vnc_jobs_join(vs); + } + QTAILQ_FOREACH(vs, &vd->clients, next) { + vnc_lock_output(vs); + vs->abort = false; + vnc_unlock_output(vs); + } +} +#else +static void vnc_abort_display_jobs(VncDisplay *vd) +{ +} +#endif + +static void vnc_dpy_resize(DisplayState *ds) +{ + VncDisplay *vd = ds->opaque; + VncState *vs; + + vnc_abort_display_jobs(vd); + + /* server surface */ + if (!vd->server) + vd->server = qemu_mallocz(sizeof(*vd->server)); + if (vd->server->data) + qemu_free(vd->server->data); + *(vd->server) = *(ds->surface); + vd->server->data = qemu_mallocz(vd->server->linesize * + vd->server->height); + + /* guest surface */ + if (!vd->guest.ds) + vd->guest.ds = qemu_mallocz(sizeof(*vd->guest.ds)); + if (ds_get_bytes_per_pixel(ds) != vd->guest.ds->pf.bytes_per_pixel) + console_color_init(ds); + *(vd->guest.ds) = *(ds->surface); + memset(vd->guest.dirty, 0xFF, sizeof(vd->guest.dirty)); + + QTAILQ_FOREACH(vs, &vd->clients, next) { + vnc_colordepth(vs); + vnc_desktop_resize(vs); + if (vs->vd->cursor) { + vnc_cursor_define(vs); + } + memset(vs->dirty, 0xFF, sizeof(vs->dirty)); + } +} + +/* fastest code */ +static void vnc_write_pixels_copy(VncState *vs, struct PixelFormat *pf, + void *pixels, int size) +{ + vnc_write(vs, pixels, size); +} + +/* slowest but generic code. */ +void vnc_convert_pixel(VncState *vs, uint8_t *buf, uint32_t v) +{ + uint8_t r, g, b; + VncDisplay *vd = vs->vd; + + r = ((((v & vd->server->pf.rmask) >> vd->server->pf.rshift) << vs->clientds.pf.rbits) >> + vd->server->pf.rbits); + g = ((((v & vd->server->pf.gmask) >> vd->server->pf.gshift) << vs->clientds.pf.gbits) >> + vd->server->pf.gbits); + b = ((((v & vd->server->pf.bmask) >> vd->server->pf.bshift) << vs->clientds.pf.bbits) >> + vd->server->pf.bbits); + v = (r << vs->clientds.pf.rshift) | + (g << vs->clientds.pf.gshift) | + (b << vs->clientds.pf.bshift); + switch(vs->clientds.pf.bytes_per_pixel) { + case 1: + buf[0] = v; + break; + case 2: + if (vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) { + buf[0] = v >> 8; + buf[1] = v; + } else { + buf[1] = v >> 8; + buf[0] = v; + } + break; + default: + case 4: + if (vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) { + buf[0] = v >> 24; + buf[1] = v >> 16; + buf[2] = v >> 8; + buf[3] = v; + } else { + buf[3] = v >> 24; + buf[2] = v >> 16; + buf[1] = v >> 8; + buf[0] = v; + } + break; + } +} + +static void vnc_write_pixels_generic(VncState *vs, struct PixelFormat *pf, + void *pixels1, int size) +{ + uint8_t buf[4]; + + if (pf->bytes_per_pixel == 4) { + uint32_t *pixels = pixels1; + int n, i; + n = size >> 2; + for(i = 0; i < n; i++) { + vnc_convert_pixel(vs, buf, pixels[i]); + vnc_write(vs, buf, vs->clientds.pf.bytes_per_pixel); + } + } else if (pf->bytes_per_pixel == 2) { + uint16_t *pixels = pixels1; + int n, i; + n = size >> 1; + for(i = 0; i < n; i++) { + vnc_convert_pixel(vs, buf, pixels[i]); + vnc_write(vs, buf, vs->clientds.pf.bytes_per_pixel); + } + } else if (pf->bytes_per_pixel == 1) { + uint8_t *pixels = pixels1; + int n, i; + n = size; + for(i = 0; i < n; i++) { + vnc_convert_pixel(vs, buf, pixels[i]); + vnc_write(vs, buf, vs->clientds.pf.bytes_per_pixel); + } + } else { + fprintf(stderr, "vnc_write_pixels_generic: VncState color depth not supported\n"); + } +} + +int vnc_raw_send_framebuffer_update(VncState *vs, int x, int y, int w, int h) +{ + int i; + uint8_t *row; + VncDisplay *vd = vs->vd; + + row = vd->server->data + y * ds_get_linesize(vs->ds) + x * ds_get_bytes_per_pixel(vs->ds); + for (i = 0; i < h; i++) { + vs->write_pixels(vs, &vd->server->pf, row, w * ds_get_bytes_per_pixel(vs->ds)); + row += ds_get_linesize(vs->ds); + } + return 1; +} + +int vnc_send_framebuffer_update(VncState *vs, int x, int y, int w, int h) +{ + int n = 0; + + switch(vs->vnc_encoding) { + case VNC_ENCODING_ZLIB: + n = vnc_zlib_send_framebuffer_update(vs, x, y, w, h); + break; + case VNC_ENCODING_HEXTILE: + vnc_framebuffer_update(vs, x, y, w, h, VNC_ENCODING_HEXTILE); + n = vnc_hextile_send_framebuffer_update(vs, x, y, w, h); + break; + case VNC_ENCODING_TIGHT: + n = vnc_tight_send_framebuffer_update(vs, x, y, w, h); + break; + case VNC_ENCODING_TIGHT_PNG: + n = vnc_tight_png_send_framebuffer_update(vs, x, y, w, h); + break; + default: + vnc_framebuffer_update(vs, x, y, w, h, VNC_ENCODING_RAW); + n = vnc_raw_send_framebuffer_update(vs, x, y, w, h); + break; + } + return n; +} + +static void vnc_copy(VncState *vs, int src_x, int src_y, int dst_x, int dst_y, int w, int h) +{ + /* send bitblit op to the vnc client */ + vnc_lock_output(vs); + vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE); + vnc_write_u8(vs, 0); + vnc_write_u16(vs, 1); /* number of rects */ + vnc_framebuffer_update(vs, dst_x, dst_y, w, h, VNC_ENCODING_COPYRECT); + vnc_write_u16(vs, src_x); + vnc_write_u16(vs, src_y); + vnc_unlock_output(vs); + vnc_flush(vs); +} + +static void vnc_dpy_copy(DisplayState *ds, int src_x, int src_y, int dst_x, int dst_y, int w, int h) +{ + VncDisplay *vd = ds->opaque; + VncState *vs, *vn; + uint8_t *src_row; + uint8_t *dst_row; + int i,x,y,pitch,depth,inc,w_lim,s; + int cmp_bytes; + + vnc_refresh_server_surface(vd); + QTAILQ_FOREACH_SAFE(vs, &vd->clients, next, vn) { + if (vnc_has_feature(vs, VNC_FEATURE_COPYRECT)) { + vs->force_update = 1; + vnc_update_client_sync(vs, 1); + /* vs might be free()ed here */ + } + } + + /* do bitblit op on the local surface too */ + pitch = ds_get_linesize(vd->ds); + depth = ds_get_bytes_per_pixel(vd->ds); + src_row = vd->server->data + pitch * src_y + depth * src_x; + dst_row = vd->server->data + pitch * dst_y + depth * dst_x; + y = dst_y; + inc = 1; + if (dst_y > src_y) { + /* copy backwards */ + src_row += pitch * (h-1); + dst_row += pitch * (h-1); + pitch = -pitch; + y = dst_y + h - 1; + inc = -1; + } + w_lim = w - (16 - (dst_x % 16)); + if (w_lim < 0) + w_lim = w; + else + w_lim = w - (w_lim % 16); + for (i = 0; i < h; i++) { + for (x = 0; x <= w_lim; + x += s, src_row += cmp_bytes, dst_row += cmp_bytes) { + if (x == w_lim) { + if ((s = w - w_lim) == 0) + break; + } else if (!x) { + s = (16 - (dst_x % 16)); + s = MIN(s, w_lim); + } else { + s = 16; + } + cmp_bytes = s * depth; + if (memcmp(src_row, dst_row, cmp_bytes) == 0) + continue; + memmove(dst_row, src_row, cmp_bytes); + QTAILQ_FOREACH(vs, &vd->clients, next) { + if (!vnc_has_feature(vs, VNC_FEATURE_COPYRECT)) { + vnc_set_bit(vs->dirty[y], ((x + dst_x) / 16)); + } + } + } + src_row += pitch - w * depth; + dst_row += pitch - w * depth; + y += inc; + } + + QTAILQ_FOREACH(vs, &vd->clients, next) { + if (vnc_has_feature(vs, VNC_FEATURE_COPYRECT)) { + vnc_copy(vs, src_x, src_y, dst_x, dst_y, w, h); + } + } +} + +static void vnc_mouse_set(int x, int y, int visible) +{ + /* can we ask the client(s) to move the pointer ??? */ +} + +static int vnc_cursor_define(VncState *vs) +{ + QEMUCursor *c = vs->vd->cursor; + PixelFormat pf = qemu_default_pixelformat(32); + int isize; + + if (vnc_has_feature(vs, VNC_FEATURE_RICH_CURSOR)) { + vnc_lock_output(vs); + vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE); + vnc_write_u8(vs, 0); /* padding */ + vnc_write_u16(vs, 1); /* # of rects */ + vnc_framebuffer_update(vs, c->hot_x, c->hot_y, c->width, c->height, + VNC_ENCODING_RICH_CURSOR); + isize = c->width * c->height * vs->clientds.pf.bytes_per_pixel; + vnc_write_pixels_generic(vs, &pf, c->data, isize); + vnc_write(vs, vs->vd->cursor_mask, vs->vd->cursor_msize); + vnc_unlock_output(vs); + return 0; + } + return -1; +} + +static void vnc_dpy_cursor_define(QEMUCursor *c) +{ + VncDisplay *vd = vnc_display; + VncState *vs; + + cursor_put(vd->cursor); + qemu_free(vd->cursor_mask); + + vd->cursor = c; + cursor_get(vd->cursor); + vd->cursor_msize = cursor_get_mono_bpl(c) * c->height; + vd->cursor_mask = qemu_mallocz(vd->cursor_msize); + cursor_get_mono_mask(c, 0, vd->cursor_mask); + + QTAILQ_FOREACH(vs, &vd->clients, next) { + vnc_cursor_define(vs); + } +} + +static int find_and_clear_dirty_height(struct VncState *vs, + int y, int last_x, int x) +{ + int h; + VncDisplay *vd = vs->vd; + + for (h = 1; h < (vd->server->height - y); h++) { + int tmp_x; + if (!vnc_get_bit(vs->dirty[y + h], last_x)) + break; + for (tmp_x = last_x; tmp_x < x; tmp_x++) + vnc_clear_bit(vs->dirty[y + h], tmp_x); + } + + return h; +} + +#ifdef CONFIG_VNC_THREAD +static int vnc_update_client_sync(VncState *vs, int has_dirty) +{ + int ret = vnc_update_client(vs, has_dirty); + vnc_jobs_join(vs); + return ret; +} +#else +static int vnc_update_client_sync(VncState *vs, int has_dirty) +{ + return vnc_update_client(vs, has_dirty); +} +#endif + +static int vnc_update_client(VncState *vs, int has_dirty) +{ + if (vs->need_update && vs->csock != -1) { + VncDisplay *vd = vs->vd; + VncJob *job; + int y; + int width, height; + int n = 0; + + + if (vs->output.offset && !vs->audio_cap && !vs->force_update) + /* kernel send buffers are full -> drop frames to throttle */ + return 0; + + if (!has_dirty && !vs->audio_cap && !vs->force_update) + return 0; + + /* + * Send screen updates to the vnc client using the server + * surface and server dirty map. guest surface updates + * happening in parallel don't disturb us, the next pass will + * send them to the client. + */ + job = vnc_job_new(vs); + + width = MIN(vd->server->width, vs->client_width); + height = MIN(vd->server->height, vs->client_height); + + for (y = 0; y < height; y++) { + int x; + int last_x = -1; + for (x = 0; x < width / 16; x++) { + if (vnc_get_bit(vs->dirty[y], x)) { + if (last_x == -1) { + last_x = x; + } + vnc_clear_bit(vs->dirty[y], x); + } else { + if (last_x != -1) { + int h = find_and_clear_dirty_height(vs, y, last_x, x); + + n += vnc_job_add_rect(job, last_x * 16, y, + (x - last_x) * 16, h); + } + last_x = -1; + } + } + if (last_x != -1) { + int h = find_and_clear_dirty_height(vs, y, last_x, x); + n += vnc_job_add_rect(job, last_x * 16, y, + (x - last_x) * 16, h); + } + } + + vnc_job_push(job); + vs->force_update = 0; + return n; + } + + if (vs->csock == -1) + vnc_disconnect_finish(vs); + + return 0; +} + +/* audio */ +static void audio_capture_notify(void *opaque, audcnotification_e cmd) +{ + VncState *vs = opaque; + + switch (cmd) { + case AUD_CNOTIFY_DISABLE: + vnc_lock_output(vs); + vnc_write_u8(vs, VNC_MSG_SERVER_QEMU); + vnc_write_u8(vs, VNC_MSG_SERVER_QEMU_AUDIO); + vnc_write_u16(vs, VNC_MSG_SERVER_QEMU_AUDIO_END); + vnc_unlock_output(vs); + vnc_flush(vs); + break; + + case AUD_CNOTIFY_ENABLE: + vnc_lock_output(vs); + vnc_write_u8(vs, VNC_MSG_SERVER_QEMU); + vnc_write_u8(vs, VNC_MSG_SERVER_QEMU_AUDIO); + vnc_write_u16(vs, VNC_MSG_SERVER_QEMU_AUDIO_BEGIN); + vnc_unlock_output(vs); + vnc_flush(vs); + break; + } +} + +static void audio_capture_destroy(void *opaque) +{ +} + +static void audio_capture(void *opaque, void *buf, int size) +{ + VncState *vs = opaque; + + vnc_lock_output(vs); + vnc_write_u8(vs, VNC_MSG_SERVER_QEMU); + vnc_write_u8(vs, VNC_MSG_SERVER_QEMU_AUDIO); + vnc_write_u16(vs, VNC_MSG_SERVER_QEMU_AUDIO_DATA); + vnc_write_u32(vs, size); + vnc_write(vs, buf, size); + vnc_unlock_output(vs); + vnc_flush(vs); +} + +static void audio_add(VncState *vs) +{ + struct audio_capture_ops ops; + + if (vs->audio_cap) { + monitor_printf(default_mon, "audio already running\n"); + return; + } + + ops.notify = audio_capture_notify; + ops.destroy = audio_capture_destroy; + ops.capture = audio_capture; + + vs->audio_cap = AUD_add_capture(&vs->as, &ops, vs); + if (!vs->audio_cap) { + monitor_printf(default_mon, "Failed to add audio capture\n"); + } +} + +static void audio_del(VncState *vs) +{ + if (vs->audio_cap) { + AUD_del_capture(vs->audio_cap, vs); + vs->audio_cap = NULL; + } +} + +static void vnc_disconnect_start(VncState *vs) +{ + if (vs->csock == -1) + return; + qemu_set_fd_handler2(vs->csock, NULL, NULL, NULL, NULL); + closesocket(vs->csock); + vs->csock = -1; +} + +static void vnc_disconnect_finish(VncState *vs) +{ + vnc_jobs_join(vs); /* Wait encoding jobs */ + + vnc_lock_output(vs); + vnc_qmp_event(vs, QEVENT_VNC_DISCONNECTED); + + buffer_free(&vs->input); + buffer_free(&vs->output); + + qobject_decref(vs->info); + + vnc_zlib_clear(vs); + vnc_tight_clear(vs); + +#ifdef CONFIG_VNC_TLS + vnc_tls_client_cleanup(vs); +#endif /* CONFIG_VNC_TLS */ +#ifdef CONFIG_VNC_SASL + vnc_sasl_client_cleanup(vs); +#endif /* CONFIG_VNC_SASL */ + audio_del(vs); + + QTAILQ_REMOVE(&vs->vd->clients, vs, next); + + if (QTAILQ_EMPTY(&vs->vd->clients)) { + dcl->idle = 1; + } + + qemu_remove_mouse_mode_change_notifier(&vs->mouse_mode_notifier); + vnc_remove_timer(vs->vd); + if (vs->vd->lock_key_sync) + qemu_remove_led_event_handler(vs->led); + vnc_unlock_output(vs); + +#ifdef CONFIG_VNC_THREAD + qemu_mutex_destroy(&vs->output_mutex); +#endif + qemu_free(vs); +} + +int vnc_client_io_error(VncState *vs, int ret, int last_errno) +{ + if (ret == 0 || ret == -1) { + if (ret == -1) { + switch (last_errno) { + case EINTR: + case EAGAIN: +#ifdef _WIN32 + case WSAEWOULDBLOCK: +#endif + return 0; + default: + break; + } + } + + VNC_DEBUG("Closing down client sock: ret %d, errno %d\n", + ret, ret < 0 ? last_errno : 0); + vnc_disconnect_start(vs); + + return 0; + } + return ret; +} + + +void vnc_client_error(VncState *vs) +{ + VNC_DEBUG("Closing down client sock: protocol error\n"); + vnc_disconnect_start(vs); +} + + +/* + * Called to write a chunk of data to the client socket. The data may + * be the raw data, or may have already been encoded by SASL. + * The data will be written either straight onto the socket, or + * written via the GNUTLS wrappers, if TLS/SSL encryption is enabled + * + * NB, it is theoretically possible to have 2 layers of encryption, + * both SASL, and this TLS layer. It is highly unlikely in practice + * though, since SASL encryption will typically be a no-op if TLS + * is active + * + * Returns the number of bytes written, which may be less than + * the requested 'datalen' if the socket would block. Returns + * -1 on error, and disconnects the client socket. + */ +long vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen) +{ + long ret; +#ifdef CONFIG_VNC_TLS + if (vs->tls.session) { + ret = gnutls_write(vs->tls.session, data, datalen); + if (ret < 0) { + if (ret == GNUTLS_E_AGAIN) + errno = EAGAIN; + else + errno = EIO; + ret = -1; + } + } else +#endif /* CONFIG_VNC_TLS */ + ret = send(vs->csock, (const void *)data, datalen, 0); + VNC_DEBUG("Wrote wire %p %zd -> %ld\n", data, datalen, ret); + return vnc_client_io_error(vs, ret, socket_error()); +} + + +/* + * Called to write buffered data to the client socket, when not + * using any SASL SSF encryption layers. Will write as much data + * as possible without blocking. If all buffered data is written, + * will switch the FD poll() handler back to read monitoring. + * + * Returns the number of bytes written, which may be less than + * the buffered output data if the socket would block. Returns + * -1 on error, and disconnects the client socket. + */ +static long vnc_client_write_plain(VncState *vs) +{ + long ret; + +#ifdef CONFIG_VNC_SASL + VNC_DEBUG("Write Plain: Pending output %p size %zd offset %zd. Wait SSF %d\n", + vs->output.buffer, vs->output.capacity, vs->output.offset, + vs->sasl.waitWriteSSF); + + if (vs->sasl.conn && + vs->sasl.runSSF && + vs->sasl.waitWriteSSF) { + ret = vnc_client_write_buf(vs, vs->output.buffer, vs->sasl.waitWriteSSF); + if (ret) + vs->sasl.waitWriteSSF -= ret; + } else +#endif /* CONFIG_VNC_SASL */ + ret = vnc_client_write_buf(vs, vs->output.buffer, vs->output.offset); + if (!ret) + return 0; + + memmove(vs->output.buffer, vs->output.buffer + ret, (vs->output.offset - ret)); + vs->output.offset -= ret; + + if (vs->output.offset == 0) { + qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs); + } + + return ret; +} + + +/* + * First function called whenever there is data to be written to + * the client socket. Will delegate actual work according to whether + * SASL SSF layers are enabled (thus requiring encryption calls) + */ +static void vnc_client_write_locked(void *opaque) +{ + VncState *vs = opaque; + +#ifdef CONFIG_VNC_SASL + if (vs->sasl.conn && + vs->sasl.runSSF && + !vs->sasl.waitWriteSSF) { + vnc_client_write_sasl(vs); + } else +#endif /* CONFIG_VNC_SASL */ + vnc_client_write_plain(vs); +} + +void vnc_client_write(void *opaque) +{ + VncState *vs = opaque; + + vnc_lock_output(vs); + if (vs->output.offset) { + vnc_client_write_locked(opaque); + } else if (vs->csock != -1) { + qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs); + } + vnc_unlock_output(vs); +} + +void vnc_read_when(VncState *vs, VncReadEvent *func, size_t expecting) +{ + vs->read_handler = func; + vs->read_handler_expect = expecting; +} + + +/* + * Called to read a chunk of data from the client socket. The data may + * be the raw data, or may need to be further decoded by SASL. + * The data will be read either straight from to the socket, or + * read via the GNUTLS wrappers, if TLS/SSL encryption is enabled + * + * NB, it is theoretically possible to have 2 layers of encryption, + * both SASL, and this TLS layer. It is highly unlikely in practice + * though, since SASL encryption will typically be a no-op if TLS + * is active + * + * Returns the number of bytes read, which may be less than + * the requested 'datalen' if the socket would block. Returns + * -1 on error, and disconnects the client socket. + */ +long vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen) +{ + long ret; +#ifdef CONFIG_VNC_TLS + if (vs->tls.session) { + ret = gnutls_read(vs->tls.session, data, datalen); + if (ret < 0) { + if (ret == GNUTLS_E_AGAIN) + errno = EAGAIN; + else + errno = EIO; + ret = -1; + } + } else +#endif /* CONFIG_VNC_TLS */ + ret = recv(vs->csock, (void *)data, datalen, 0); + VNC_DEBUG("Read wire %p %zd -> %ld\n", data, datalen, ret); + return vnc_client_io_error(vs, ret, socket_error()); +} + + +/* + * Called to read data from the client socket to the input buffer, + * when not using any SASL SSF encryption layers. Will read as much + * data as possible without blocking. + * + * Returns the number of bytes read. Returns -1 on error, and + * disconnects the client socket. + */ +static long vnc_client_read_plain(VncState *vs) +{ + int ret; + VNC_DEBUG("Read plain %p size %zd offset %zd\n", + vs->input.buffer, vs->input.capacity, vs->input.offset); + buffer_reserve(&vs->input, 4096); + ret = vnc_client_read_buf(vs, buffer_end(&vs->input), 4096); + if (!ret) + return 0; + vs->input.offset += ret; + return ret; +} + + +/* + * First function called whenever there is more data to be read from + * the client socket. Will delegate actual work according to whether + * SASL SSF layers are enabled (thus requiring decryption calls) + */ +void vnc_client_read(void *opaque) +{ + VncState *vs = opaque; + long ret; + +#ifdef CONFIG_VNC_SASL + if (vs->sasl.conn && vs->sasl.runSSF) + ret = vnc_client_read_sasl(vs); + else +#endif /* CONFIG_VNC_SASL */ + ret = vnc_client_read_plain(vs); + if (!ret) { + if (vs->csock == -1) + vnc_disconnect_finish(vs); + return; + } + + while (vs->read_handler && vs->input.offset >= vs->read_handler_expect) { + size_t len = vs->read_handler_expect; + int ret; + + ret = vs->read_handler(vs, vs->input.buffer, len); + if (vs->csock == -1) { + vnc_disconnect_finish(vs); + return; + } + + if (!ret) { + memmove(vs->input.buffer, vs->input.buffer + len, (vs->input.offset - len)); + vs->input.offset -= len; + } else { + vs->read_handler_expect = ret; + } + } +} + +void vnc_write(VncState *vs, const void *data, size_t len) +{ + buffer_reserve(&vs->output, len); + + if (vs->csock != -1 && buffer_empty(&vs->output)) { + qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, vnc_client_write, vs); + } + + buffer_append(&vs->output, data, len); +} + +void vnc_write_s32(VncState *vs, int32_t value) +{ + vnc_write_u32(vs, *(uint32_t *)&value); +} + +void vnc_write_u32(VncState *vs, uint32_t value) +{ + uint8_t buf[4]; + + buf[0] = (value >> 24) & 0xFF; + buf[1] = (value >> 16) & 0xFF; + buf[2] = (value >> 8) & 0xFF; + buf[3] = value & 0xFF; + + vnc_write(vs, buf, 4); +} + +void vnc_write_u16(VncState *vs, uint16_t value) +{ + uint8_t buf[2]; + + buf[0] = (value >> 8) & 0xFF; + buf[1] = value & 0xFF; + + vnc_write(vs, buf, 2); +} + +void vnc_write_u8(VncState *vs, uint8_t value) +{ + vnc_write(vs, (char *)&value, 1); +} + +void vnc_flush(VncState *vs) +{ + vnc_lock_output(vs); + if (vs->csock != -1 && vs->output.offset) { + vnc_client_write_locked(vs); + } + vnc_unlock_output(vs); +} + +uint8_t read_u8(uint8_t *data, size_t offset) +{ + return data[offset]; +} + +uint16_t read_u16(uint8_t *data, size_t offset) +{ + return ((data[offset] & 0xFF) << 8) | (data[offset + 1] & 0xFF); +} + +int32_t read_s32(uint8_t *data, size_t offset) +{ + return (int32_t)((data[offset] << 24) | (data[offset + 1] << 16) | + (data[offset + 2] << 8) | data[offset + 3]); +} + +uint32_t read_u32(uint8_t *data, size_t offset) +{ + return ((data[offset] << 24) | (data[offset + 1] << 16) | + (data[offset + 2] << 8) | data[offset + 3]); +} + +static void client_cut_text(VncState *vs, size_t len, uint8_t *text) +{ +} + +static void check_pointer_type_change(Notifier *notifier) +{ + VncState *vs = container_of(notifier, VncState, mouse_mode_notifier); + int absolute = kbd_mouse_is_absolute(); + + if (vnc_has_feature(vs, VNC_FEATURE_POINTER_TYPE_CHANGE) && vs->absolute != absolute) { + vnc_lock_output(vs); + vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE); + vnc_write_u8(vs, 0); + vnc_write_u16(vs, 1); + vnc_framebuffer_update(vs, absolute, 0, + ds_get_width(vs->ds), ds_get_height(vs->ds), + VNC_ENCODING_POINTER_TYPE_CHANGE); + vnc_unlock_output(vs); + vnc_flush(vs); + } + vs->absolute = absolute; +} + +static void pointer_event(VncState *vs, int button_mask, int x, int y) +{ + int buttons = 0; + int dz = 0; + + if (button_mask & 0x01) + buttons |= MOUSE_EVENT_LBUTTON; + if (button_mask & 0x02) + buttons |= MOUSE_EVENT_MBUTTON; + if (button_mask & 0x04) + buttons |= MOUSE_EVENT_RBUTTON; + if (button_mask & 0x08) + dz = -1; + if (button_mask & 0x10) + dz = 1; + + if (vs->absolute) { + kbd_mouse_event(ds_get_width(vs->ds) > 1 ? + x * 0x7FFF / (ds_get_width(vs->ds) - 1) : 0x4000, + ds_get_height(vs->ds) > 1 ? + y * 0x7FFF / (ds_get_height(vs->ds) - 1) : 0x4000, + dz, buttons); + } else if (vnc_has_feature(vs, VNC_FEATURE_POINTER_TYPE_CHANGE)) { + x -= 0x7FFF; + y -= 0x7FFF; + + kbd_mouse_event(x, y, dz, buttons); + } else { + if (vs->last_x != -1) + kbd_mouse_event(x - vs->last_x, + y - vs->last_y, + dz, buttons); + vs->last_x = x; + vs->last_y = y; + } +} + +static void reset_keys(VncState *vs) +{ + int i; + for(i = 0; i < 256; i++) { + if (vs->modifiers_state[i]) { + if (i & SCANCODE_GREY) + kbd_put_keycode(SCANCODE_EMUL0); + kbd_put_keycode(i | SCANCODE_UP); + vs->modifiers_state[i] = 0; + } + } +} + +static void press_key(VncState *vs, int keysym) +{ + int keycode = keysym2scancode(vs->vd->kbd_layout, keysym) & SCANCODE_KEYMASK; + if (keycode & SCANCODE_GREY) + kbd_put_keycode(SCANCODE_EMUL0); + kbd_put_keycode(keycode & SCANCODE_KEYCODEMASK); + if (keycode & SCANCODE_GREY) + kbd_put_keycode(SCANCODE_EMUL0); + kbd_put_keycode(keycode | SCANCODE_UP); +} + +static void kbd_leds(void *opaque, int ledstate) +{ + VncState *vs = opaque; + int caps, num; + + caps = ledstate & QEMU_CAPS_LOCK_LED ? 1 : 0; + num = ledstate & QEMU_NUM_LOCK_LED ? 1 : 0; + + if (vs->modifiers_state[0x3a] != caps) { + vs->modifiers_state[0x3a] = caps; + } + if (vs->modifiers_state[0x45] != num) { + vs->modifiers_state[0x45] = num; + } +} + +static void do_key_event(VncState *vs, int down, int keycode, int sym) +{ + /* QEMU console switch */ + switch(keycode) { + case 0x2a: /* Left Shift */ + case 0x36: /* Right Shift */ + case 0x1d: /* Left CTRL */ + case 0x9d: /* Right CTRL */ + case 0x38: /* Left ALT */ + case 0xb8: /* Right ALT */ + if (down) + vs->modifiers_state[keycode] = 1; + else + vs->modifiers_state[keycode] = 0; + break; + case 0x02 ... 0x0a: /* '1' to '9' keys */ + if (down && vs->modifiers_state[0x1d] && vs->modifiers_state[0x38]) { + /* Reset the modifiers sent to the current console */ + reset_keys(vs); + console_select(keycode - 0x02); + return; + } + break; + case 0x3a: /* CapsLock */ + case 0x45: /* NumLock */ + if (down) + vs->modifiers_state[keycode] ^= 1; + break; + } + + if (down && vs->vd->lock_key_sync && + keycode_is_keypad(vs->vd->kbd_layout, keycode)) { + /* If the numlock state needs to change then simulate an additional + keypress before sending this one. This will happen if the user + toggles numlock away from the VNC window. + */ + if (keysym_is_numlock(vs->vd->kbd_layout, sym & 0xFFFF)) { + if (!vs->modifiers_state[0x45]) { + vs->modifiers_state[0x45] = 1; + press_key(vs, 0xff7f); + } + } else { + if (vs->modifiers_state[0x45]) { + vs->modifiers_state[0x45] = 0; + press_key(vs, 0xff7f); + } + } + } + + if (down && vs->vd->lock_key_sync && + ((sym >= 'A' && sym <= 'Z') || (sym >= 'a' && sym <= 'z'))) { + /* If the capslock state needs to change then simulate an additional + keypress before sending this one. This will happen if the user + toggles capslock away from the VNC window. + */ + int uppercase = !!(sym >= 'A' && sym <= 'Z'); + int shift = !!(vs->modifiers_state[0x2a] | vs->modifiers_state[0x36]); + int capslock = !!(vs->modifiers_state[0x3a]); + if (capslock) { + if (uppercase == shift) { + vs->modifiers_state[0x3a] = 0; + press_key(vs, 0xffe5); + } + } else { + if (uppercase != shift) { + vs->modifiers_state[0x3a] = 1; + press_key(vs, 0xffe5); + } + } + } + + if (is_graphic_console()) { + if (keycode & SCANCODE_GREY) + kbd_put_keycode(SCANCODE_EMUL0); + if (down) + kbd_put_keycode(keycode & SCANCODE_KEYCODEMASK); + else + kbd_put_keycode(keycode | SCANCODE_UP); + } else { + /* QEMU console emulation */ + if (down) { + int numlock = vs->modifiers_state[0x45]; + switch (keycode) { + case 0x2a: /* Left Shift */ + case 0x36: /* Right Shift */ + case 0x1d: /* Left CTRL */ + case 0x9d: /* Right CTRL */ + case 0x38: /* Left ALT */ + case 0xb8: /* Right ALT */ + break; + case 0xc8: + kbd_put_keysym(QEMU_KEY_UP); + break; + case 0xd0: + kbd_put_keysym(QEMU_KEY_DOWN); + break; + case 0xcb: + kbd_put_keysym(QEMU_KEY_LEFT); + break; + case 0xcd: + kbd_put_keysym(QEMU_KEY_RIGHT); + break; + case 0xd3: + kbd_put_keysym(QEMU_KEY_DELETE); + break; + case 0xc7: + kbd_put_keysym(QEMU_KEY_HOME); + break; + case 0xcf: + kbd_put_keysym(QEMU_KEY_END); + break; + case 0xc9: + kbd_put_keysym(QEMU_KEY_PAGEUP); + break; + case 0xd1: + kbd_put_keysym(QEMU_KEY_PAGEDOWN); + break; + + case 0x47: + kbd_put_keysym(numlock ? '7' : QEMU_KEY_HOME); + break; + case 0x48: + kbd_put_keysym(numlock ? '8' : QEMU_KEY_UP); + break; + case 0x49: + kbd_put_keysym(numlock ? '9' : QEMU_KEY_PAGEUP); + break; + case 0x4b: + kbd_put_keysym(numlock ? '4' : QEMU_KEY_LEFT); + break; + case 0x4c: + kbd_put_keysym('5'); + break; + case 0x4d: + kbd_put_keysym(numlock ? '6' : QEMU_KEY_RIGHT); + break; + case 0x4f: + kbd_put_keysym(numlock ? '1' : QEMU_KEY_END); + break; + case 0x50: + kbd_put_keysym(numlock ? '2' : QEMU_KEY_DOWN); + break; + case 0x51: + kbd_put_keysym(numlock ? '3' : QEMU_KEY_PAGEDOWN); + break; + case 0x52: + kbd_put_keysym('0'); + break; + case 0x53: + kbd_put_keysym(numlock ? '.' : QEMU_KEY_DELETE); + break; + + case 0xb5: + kbd_put_keysym('/'); + break; + case 0x37: + kbd_put_keysym('*'); + break; + case 0x4a: + kbd_put_keysym('-'); + break; + case 0x4e: + kbd_put_keysym('+'); + break; + case 0x9c: + kbd_put_keysym('\n'); + break; + + default: + kbd_put_keysym(sym); + break; + } + } + } +} + +static void key_event(VncState *vs, int down, uint32_t sym) +{ + int keycode; + int lsym = sym; + + if (lsym >= 'A' && lsym <= 'Z' && is_graphic_console()) { + lsym = lsym - 'A' + 'a'; + } + + keycode = keysym2scancode(vs->vd->kbd_layout, lsym & 0xFFFF) & SCANCODE_KEYMASK; + do_key_event(vs, down, keycode, sym); +} + +static void ext_key_event(VncState *vs, int down, + uint32_t sym, uint16_t keycode) +{ + /* if the user specifies a keyboard layout, always use it */ + if (keyboard_layout) + key_event(vs, down, sym); + else + do_key_event(vs, down, keycode, sym); +} + +static void framebuffer_update_request(VncState *vs, int incremental, + int x_position, int y_position, + int w, int h) +{ + if (y_position > ds_get_height(vs->ds)) + y_position = ds_get_height(vs->ds); + if (y_position + h >= ds_get_height(vs->ds)) + h = ds_get_height(vs->ds) - y_position; + + int i; + vs->need_update = 1; + if (!incremental) { + vs->force_update = 1; + for (i = 0; i < h; i++) { + vnc_set_bits(vs->dirty[y_position + i], + (ds_get_width(vs->ds) / 16), VNC_DIRTY_WORDS); + } + } +} + +static void send_ext_key_event_ack(VncState *vs) +{ + vnc_lock_output(vs); + vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE); + vnc_write_u8(vs, 0); + vnc_write_u16(vs, 1); + vnc_framebuffer_update(vs, 0, 0, ds_get_width(vs->ds), ds_get_height(vs->ds), + VNC_ENCODING_EXT_KEY_EVENT); + vnc_unlock_output(vs); + vnc_flush(vs); +} + +static void send_ext_audio_ack(VncState *vs) +{ + vnc_lock_output(vs); + vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE); + vnc_write_u8(vs, 0); + vnc_write_u16(vs, 1); + vnc_framebuffer_update(vs, 0, 0, ds_get_width(vs->ds), ds_get_height(vs->ds), + VNC_ENCODING_AUDIO); + vnc_unlock_output(vs); + vnc_flush(vs); +} + +static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings) +{ + int i; + unsigned int enc = 0; + + vs->features = 0; + vs->vnc_encoding = 0; + vs->tight.compression = 9; + vs->tight.quality = -1; /* Lossless by default */ + vs->absolute = -1; + + /* + * Start from the end because the encodings are sent in order of preference. + * This way the prefered encoding (first encoding defined in the array) + * will be set at the end of the loop. + */ + for (i = n_encodings - 1; i >= 0; i--) { + enc = encodings[i]; + switch (enc) { + case VNC_ENCODING_RAW: + vs->vnc_encoding = enc; + break; + case VNC_ENCODING_COPYRECT: + vs->features |= VNC_FEATURE_COPYRECT_MASK; + break; + case VNC_ENCODING_HEXTILE: + vs->features |= VNC_FEATURE_HEXTILE_MASK; + vs->vnc_encoding = enc; + break; + case VNC_ENCODING_TIGHT: + vs->features |= VNC_FEATURE_TIGHT_MASK; + vs->vnc_encoding = enc; + break; + case VNC_ENCODING_TIGHT_PNG: + vs->features |= VNC_FEATURE_TIGHT_PNG_MASK; + vs->vnc_encoding = enc; + break; + case VNC_ENCODING_ZLIB: + vs->features |= VNC_FEATURE_ZLIB_MASK; + vs->vnc_encoding = enc; + break; + case VNC_ENCODING_DESKTOPRESIZE: + vs->features |= VNC_FEATURE_RESIZE_MASK; + break; + case VNC_ENCODING_POINTER_TYPE_CHANGE: + vs->features |= VNC_FEATURE_POINTER_TYPE_CHANGE_MASK; + break; + case VNC_ENCODING_RICH_CURSOR: + vs->features |= VNC_FEATURE_RICH_CURSOR_MASK; + break; + case VNC_ENCODING_EXT_KEY_EVENT: + send_ext_key_event_ack(vs); + break; + case VNC_ENCODING_AUDIO: + send_ext_audio_ack(vs); + break; + case VNC_ENCODING_WMVi: + vs->features |= VNC_FEATURE_WMVI_MASK; + break; + case VNC_ENCODING_COMPRESSLEVEL0 ... VNC_ENCODING_COMPRESSLEVEL0 + 9: + vs->tight.compression = (enc & 0x0F); + break; + case VNC_ENCODING_QUALITYLEVEL0 ... VNC_ENCODING_QUALITYLEVEL0 + 9: + vs->tight.quality = (enc & 0x0F); + break; + default: + VNC_DEBUG("Unknown encoding: %d (0x%.8x): %d\n", i, enc, enc); + break; + } + } + vnc_desktop_resize(vs); + check_pointer_type_change(&vs->mouse_mode_notifier); +} + +static void set_pixel_conversion(VncState *vs) +{ + if ((vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) == + (vs->ds->surface->flags & QEMU_BIG_ENDIAN_FLAG) && + !memcmp(&(vs->clientds.pf), &(vs->ds->surface->pf), sizeof(PixelFormat))) { + vs->write_pixels = vnc_write_pixels_copy; + vnc_hextile_set_pixel_conversion(vs, 0); + } else { + vs->write_pixels = vnc_write_pixels_generic; + vnc_hextile_set_pixel_conversion(vs, 1); + } +} + +static void set_pixel_format(VncState *vs, + int bits_per_pixel, int depth, + int big_endian_flag, int true_color_flag, + int red_max, int green_max, int blue_max, + int red_shift, int green_shift, int blue_shift) +{ + if (!true_color_flag) { + vnc_client_error(vs); + return; + } + + vs->clientds = *(vs->vd->guest.ds); + vs->clientds.pf.rmax = red_max; + count_bits(vs->clientds.pf.rbits, red_max); + vs->clientds.pf.rshift = red_shift; + vs->clientds.pf.rmask = red_max << red_shift; + vs->clientds.pf.gmax = green_max; + count_bits(vs->clientds.pf.gbits, green_max); + vs->clientds.pf.gshift = green_shift; + vs->clientds.pf.gmask = green_max << green_shift; + vs->clientds.pf.bmax = blue_max; + count_bits(vs->clientds.pf.bbits, blue_max); + vs->clientds.pf.bshift = blue_shift; + vs->clientds.pf.bmask = blue_max << blue_shift; + vs->clientds.pf.bits_per_pixel = bits_per_pixel; + vs->clientds.pf.bytes_per_pixel = bits_per_pixel / 8; + vs->clientds.pf.depth = bits_per_pixel == 32 ? 24 : bits_per_pixel; + vs->clientds.flags = big_endian_flag ? QEMU_BIG_ENDIAN_FLAG : 0x00; + + set_pixel_conversion(vs); + + vga_hw_invalidate(); + vga_hw_update(); +} + +static void pixel_format_message (VncState *vs) { + char pad[3] = { 0, 0, 0 }; + + vnc_write_u8(vs, vs->ds->surface->pf.bits_per_pixel); /* bits-per-pixel */ + vnc_write_u8(vs, vs->ds->surface->pf.depth); /* depth */ + +#ifdef HOST_WORDS_BIGENDIAN + vnc_write_u8(vs, 1); /* big-endian-flag */ +#else + vnc_write_u8(vs, 0); /* big-endian-flag */ +#endif + vnc_write_u8(vs, 1); /* true-color-flag */ + vnc_write_u16(vs, vs->ds->surface->pf.rmax); /* red-max */ + vnc_write_u16(vs, vs->ds->surface->pf.gmax); /* green-max */ + vnc_write_u16(vs, vs->ds->surface->pf.bmax); /* blue-max */ + vnc_write_u8(vs, vs->ds->surface->pf.rshift); /* red-shift */ + vnc_write_u8(vs, vs->ds->surface->pf.gshift); /* green-shift */ + vnc_write_u8(vs, vs->ds->surface->pf.bshift); /* blue-shift */ + + vnc_hextile_set_pixel_conversion(vs, 0); + + vs->clientds = *(vs->ds->surface); + vs->clientds.flags &= ~QEMU_ALLOCATED_FLAG; + vs->write_pixels = vnc_write_pixels_copy; + + vnc_write(vs, pad, 3); /* padding */ +} + +static void vnc_dpy_setdata(DisplayState *ds) +{ + /* We don't have to do anything */ +} + +static void vnc_colordepth(VncState *vs) +{ + if (vnc_has_feature(vs, VNC_FEATURE_WMVI)) { + /* Sending a WMVi message to notify the client*/ + vnc_lock_output(vs); + vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE); + vnc_write_u8(vs, 0); + vnc_write_u16(vs, 1); /* number of rects */ + vnc_framebuffer_update(vs, 0, 0, ds_get_width(vs->ds), + ds_get_height(vs->ds), VNC_ENCODING_WMVi); + pixel_format_message(vs); + vnc_unlock_output(vs); + vnc_flush(vs); + } else { + set_pixel_conversion(vs); + } +} + +static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len) +{ + int i; + uint16_t limit; + VncDisplay *vd = vs->vd; + + if (data[0] > 3) { + vd->timer_interval = VNC_REFRESH_INTERVAL_BASE; + if (!qemu_timer_expired(vd->timer, qemu_get_clock(rt_clock) + vd->timer_interval)) + qemu_mod_timer(vd->timer, qemu_get_clock(rt_clock) + vd->timer_interval); + } + + switch (data[0]) { + case VNC_MSG_CLIENT_SET_PIXEL_FORMAT: + if (len == 1) + return 20; + + set_pixel_format(vs, read_u8(data, 4), read_u8(data, 5), + read_u8(data, 6), read_u8(data, 7), + read_u16(data, 8), read_u16(data, 10), + read_u16(data, 12), read_u8(data, 14), + read_u8(data, 15), read_u8(data, 16)); + break; + case VNC_MSG_CLIENT_SET_ENCODINGS: + if (len == 1) + return 4; + + if (len == 4) { + limit = read_u16(data, 2); + if (limit > 0) + return 4 + (limit * 4); + } else + limit = read_u16(data, 2); + + for (i = 0; i < limit; i++) { + int32_t val = read_s32(data, 4 + (i * 4)); + memcpy(data + 4 + (i * 4), &val, sizeof(val)); + } + + set_encodings(vs, (int32_t *)(data + 4), limit); + break; + case VNC_MSG_CLIENT_FRAMEBUFFER_UPDATE_REQUEST: + if (len == 1) + return 10; + + framebuffer_update_request(vs, + read_u8(data, 1), read_u16(data, 2), read_u16(data, 4), + read_u16(data, 6), read_u16(data, 8)); + break; + case VNC_MSG_CLIENT_KEY_EVENT: + if (len == 1) + return 8; + + key_event(vs, read_u8(data, 1), read_u32(data, 4)); + break; + case VNC_MSG_CLIENT_POINTER_EVENT: + if (len == 1) + return 6; + + pointer_event(vs, read_u8(data, 1), read_u16(data, 2), read_u16(data, 4)); + break; + case VNC_MSG_CLIENT_CUT_TEXT: + if (len == 1) + return 8; + + if (len == 8) { + uint32_t dlen = read_u32(data, 4); + if (dlen > 0) + return 8 + dlen; + } + + client_cut_text(vs, read_u32(data, 4), data + 8); + break; + case VNC_MSG_CLIENT_QEMU: + if (len == 1) + return 2; + + switch (read_u8(data, 1)) { + case VNC_MSG_CLIENT_QEMU_EXT_KEY_EVENT: + if (len == 2) + return 12; + + ext_key_event(vs, read_u16(data, 2), + read_u32(data, 4), read_u32(data, 8)); + break; + case VNC_MSG_CLIENT_QEMU_AUDIO: + if (len == 2) + return 4; + + switch (read_u16 (data, 2)) { + case VNC_MSG_CLIENT_QEMU_AUDIO_ENABLE: + audio_add(vs); + break; + case VNC_MSG_CLIENT_QEMU_AUDIO_DISABLE: + audio_del(vs); + break; + case VNC_MSG_CLIENT_QEMU_AUDIO_SET_FORMAT: + if (len == 4) + return 10; + switch (read_u8(data, 4)) { + case 0: vs->as.fmt = AUD_FMT_U8; break; + case 1: vs->as.fmt = AUD_FMT_S8; break; + case 2: vs->as.fmt = AUD_FMT_U16; break; + case 3: vs->as.fmt = AUD_FMT_S16; break; + case 4: vs->as.fmt = AUD_FMT_U32; break; + case 5: vs->as.fmt = AUD_FMT_S32; break; + default: + printf("Invalid audio format %d\n", read_u8(data, 4)); + vnc_client_error(vs); + break; + } + vs->as.nchannels = read_u8(data, 5); + if (vs->as.nchannels != 1 && vs->as.nchannels != 2) { + printf("Invalid audio channel coount %d\n", + read_u8(data, 5)); + vnc_client_error(vs); + break; + } + vs->as.freq = read_u32(data, 6); + break; + default: + printf ("Invalid audio message %d\n", read_u8(data, 4)); + vnc_client_error(vs); + break; + } + break; + + default: + printf("Msg: %d\n", read_u16(data, 0)); + vnc_client_error(vs); + break; + } + break; + default: + printf("Msg: %d\n", data[0]); + vnc_client_error(vs); + break; + } + + vnc_read_when(vs, protocol_client_msg, 1); + return 0; +} + +static int protocol_client_init(VncState *vs, uint8_t *data, size_t len) +{ + char buf[1024]; + int size; + + vs->client_width = ds_get_width(vs->ds); + vs->client_height = ds_get_height(vs->ds); + vnc_write_u16(vs, vs->client_width); + vnc_write_u16(vs, vs->client_height); + + pixel_format_message(vs); + + if (qemu_name) + size = snprintf(buf, sizeof(buf), "QEMU (%s)", qemu_name); + else + size = snprintf(buf, sizeof(buf), "QEMU"); + + vnc_write_u32(vs, size); + vnc_write(vs, buf, size); + vnc_flush(vs); + + vnc_client_cache_auth(vs); + vnc_qmp_event(vs, QEVENT_VNC_INITIALIZED); + + vnc_read_when(vs, protocol_client_msg, 1); + + return 0; +} + +void start_client_init(VncState *vs) +{ + vnc_read_when(vs, protocol_client_init, 1); +} + +static void make_challenge(VncState *vs) +{ + int i; + + srand(time(NULL)+getpid()+getpid()*987654+rand()); + + for (i = 0 ; i < sizeof(vs->challenge) ; i++) + vs->challenge[i] = (int) (256.0*rand()/(RAND_MAX+1.0)); +} + +static int protocol_client_auth_vnc(VncState *vs, uint8_t *data, size_t len) +{ + unsigned char response[VNC_AUTH_CHALLENGE_SIZE]; + int i, j, pwlen; + unsigned char key[8]; + time_t now = time(NULL); + + if (!vs->vd->password) { + VNC_DEBUG("No password configured on server"); + goto reject; + } + if (vs->vd->expires < now) { + VNC_DEBUG("Password is expired"); + goto reject; + } + + memcpy(response, vs->challenge, VNC_AUTH_CHALLENGE_SIZE); + + /* Calculate the expected challenge response */ + pwlen = strlen(vs->vd->password); + for (i=0; ivd->password[i] : 0; + deskey(key, EN0); + for (j = 0; j < VNC_AUTH_CHALLENGE_SIZE; j += 8) + des(response+j, response+j); + + /* Compare expected vs actual challenge response */ + if (memcmp(response, data, VNC_AUTH_CHALLENGE_SIZE) != 0) { + VNC_DEBUG("Client challenge reponse did not match\n"); + goto reject; + } else { + VNC_DEBUG("Accepting VNC challenge response\n"); + vnc_write_u32(vs, 0); /* Accept auth */ + vnc_flush(vs); + + start_client_init(vs); + } + return 0; + +reject: + vnc_write_u32(vs, 1); /* Reject auth */ + if (vs->minor >= 8) { + static const char err[] = "Authentication failed"; + vnc_write_u32(vs, sizeof(err)); + vnc_write(vs, err, sizeof(err)); + } + vnc_flush(vs); + vnc_client_error(vs); + return 0; +} + +void start_auth_vnc(VncState *vs) +{ + make_challenge(vs); + /* Send client a 'random' challenge */ + vnc_write(vs, vs->challenge, sizeof(vs->challenge)); + vnc_flush(vs); + + vnc_read_when(vs, protocol_client_auth_vnc, sizeof(vs->challenge)); +} + + +static int protocol_client_auth(VncState *vs, uint8_t *data, size_t len) +{ + /* We only advertise 1 auth scheme at a time, so client + * must pick the one we sent. Verify this */ + if (data[0] != vs->vd->auth) { /* Reject auth */ + VNC_DEBUG("Reject auth %d because it didn't match advertized\n", (int)data[0]); + vnc_write_u32(vs, 1); + if (vs->minor >= 8) { + static const char err[] = "Authentication failed"; + vnc_write_u32(vs, sizeof(err)); + vnc_write(vs, err, sizeof(err)); + } + vnc_client_error(vs); + } else { /* Accept requested auth */ + VNC_DEBUG("Client requested auth %d\n", (int)data[0]); + switch (vs->vd->auth) { + case VNC_AUTH_NONE: + VNC_DEBUG("Accept auth none\n"); + if (vs->minor >= 8) { + vnc_write_u32(vs, 0); /* Accept auth completion */ + vnc_flush(vs); + } + start_client_init(vs); + break; + + case VNC_AUTH_VNC: + VNC_DEBUG("Start VNC auth\n"); + start_auth_vnc(vs); + break; + +#ifdef CONFIG_VNC_TLS + case VNC_AUTH_VENCRYPT: + VNC_DEBUG("Accept VeNCrypt auth\n");; + start_auth_vencrypt(vs); + break; +#endif /* CONFIG_VNC_TLS */ + +#ifdef CONFIG_VNC_SASL + case VNC_AUTH_SASL: + VNC_DEBUG("Accept SASL auth\n"); + start_auth_sasl(vs); + break; +#endif /* CONFIG_VNC_SASL */ + + default: /* Should not be possible, but just in case */ + VNC_DEBUG("Reject auth %d server code bug\n", vs->vd->auth); + vnc_write_u8(vs, 1); + if (vs->minor >= 8) { + static const char err[] = "Authentication failed"; + vnc_write_u32(vs, sizeof(err)); + vnc_write(vs, err, sizeof(err)); + } + vnc_client_error(vs); + } + } + return 0; +} + +static int protocol_version(VncState *vs, uint8_t *version, size_t len) +{ + char local[13]; + + memcpy(local, version, 12); + local[12] = 0; + + if (sscanf(local, "RFB %03d.%03d\n", &vs->major, &vs->minor) != 2) { + VNC_DEBUG("Malformed protocol version %s\n", local); + vnc_client_error(vs); + return 0; + } + VNC_DEBUG("Client request protocol version %d.%d\n", vs->major, vs->minor); + if (vs->major != 3 || + (vs->minor != 3 && + vs->minor != 4 && + vs->minor != 5 && + vs->minor != 7 && + vs->minor != 8)) { + VNC_DEBUG("Unsupported client version\n"); + vnc_write_u32(vs, VNC_AUTH_INVALID); + vnc_flush(vs); + vnc_client_error(vs); + return 0; + } + /* Some broken clients report v3.4 or v3.5, which spec requires to be treated + * as equivalent to v3.3 by servers + */ + if (vs->minor == 4 || vs->minor == 5) + vs->minor = 3; + + if (vs->minor == 3) { + if (vs->vd->auth == VNC_AUTH_NONE) { + VNC_DEBUG("Tell client auth none\n"); + vnc_write_u32(vs, vs->vd->auth); + vnc_flush(vs); + start_client_init(vs); + } else if (vs->vd->auth == VNC_AUTH_VNC) { + VNC_DEBUG("Tell client VNC auth\n"); + vnc_write_u32(vs, vs->vd->auth); + vnc_flush(vs); + start_auth_vnc(vs); + } else { + VNC_DEBUG("Unsupported auth %d for protocol 3.3\n", vs->vd->auth); + vnc_write_u32(vs, VNC_AUTH_INVALID); + vnc_flush(vs); + vnc_client_error(vs); + } + } else { + VNC_DEBUG("Telling client we support auth %d\n", vs->vd->auth); + vnc_write_u8(vs, 1); /* num auth */ + vnc_write_u8(vs, vs->vd->auth); + vnc_read_when(vs, protocol_client_auth, 1); + vnc_flush(vs); + } + + return 0; +} + +static int vnc_refresh_server_surface(VncDisplay *vd) +{ + int y; + uint8_t *guest_row; + uint8_t *server_row; + int cmp_bytes; + uint32_t width_mask[VNC_DIRTY_WORDS]; + VncState *vs; + int has_dirty = 0; + + /* + * Walk through the guest dirty map. + * Check and copy modified bits from guest to server surface. + * Update server dirty map. + */ + vnc_set_bits(width_mask, (ds_get_width(vd->ds) / 16), VNC_DIRTY_WORDS); + cmp_bytes = 16 * ds_get_bytes_per_pixel(vd->ds); + guest_row = vd->guest.ds->data; + server_row = vd->server->data; + for (y = 0; y < vd->guest.ds->height; y++) { + if (vnc_and_bits(vd->guest.dirty[y], width_mask, VNC_DIRTY_WORDS)) { + int x; + uint8_t *guest_ptr; + uint8_t *server_ptr; + + guest_ptr = guest_row; + server_ptr = server_row; + + for (x = 0; x < vd->guest.ds->width; + x += 16, guest_ptr += cmp_bytes, server_ptr += cmp_bytes) { + if (!vnc_get_bit(vd->guest.dirty[y], (x / 16))) + continue; + vnc_clear_bit(vd->guest.dirty[y], (x / 16)); + if (memcmp(server_ptr, guest_ptr, cmp_bytes) == 0) + continue; + memcpy(server_ptr, guest_ptr, cmp_bytes); + QTAILQ_FOREACH(vs, &vd->clients, next) { + vnc_set_bit(vs->dirty[y], (x / 16)); + } + has_dirty++; + } + } + guest_row += ds_get_linesize(vd->ds); + server_row += ds_get_linesize(vd->ds); + } + return has_dirty; +} + +static void vnc_refresh(void *opaque) +{ + VncDisplay *vd = opaque; + VncState *vs, *vn; + int has_dirty, rects = 0; + + vga_hw_update(); + + if (vnc_trylock_display(vd)) { + vd->timer_interval = VNC_REFRESH_INTERVAL_BASE; + qemu_mod_timer(vd->timer, qemu_get_clock(rt_clock) + + vd->timer_interval); + return; + } + + has_dirty = vnc_refresh_server_surface(vd); + vnc_unlock_display(vd); + + QTAILQ_FOREACH_SAFE(vs, &vd->clients, next, vn) { + rects += vnc_update_client(vs, has_dirty); + /* vs might be free()ed here */ + } + + /* vd->timer could be NULL now if the last client disconnected, + * in this case don't update the timer */ + if (vd->timer == NULL) + return; + + if (has_dirty && rects) { + vd->timer_interval /= 2; + if (vd->timer_interval < VNC_REFRESH_INTERVAL_BASE) + vd->timer_interval = VNC_REFRESH_INTERVAL_BASE; + } else { + vd->timer_interval += VNC_REFRESH_INTERVAL_INC; + if (vd->timer_interval > VNC_REFRESH_INTERVAL_MAX) + vd->timer_interval = VNC_REFRESH_INTERVAL_MAX; + } + qemu_mod_timer(vd->timer, qemu_get_clock(rt_clock) + vd->timer_interval); +} + +static void vnc_init_timer(VncDisplay *vd) +{ + vd->timer_interval = VNC_REFRESH_INTERVAL_BASE; + if (vd->timer == NULL && !QTAILQ_EMPTY(&vd->clients)) { + vd->timer = qemu_new_timer(rt_clock, vnc_refresh, vd); + vnc_dpy_resize(vd->ds); + vnc_refresh(vd); + } +} + +static void vnc_remove_timer(VncDisplay *vd) +{ + if (vd->timer != NULL && QTAILQ_EMPTY(&vd->clients)) { + qemu_del_timer(vd->timer); + qemu_free_timer(vd->timer); + vd->timer = NULL; + } +} + +static void vnc_connect(VncDisplay *vd, int csock) +{ + VncState *vs = qemu_mallocz(sizeof(VncState)); + vs->csock = csock; + + VNC_DEBUG("New client on socket %d\n", csock); + dcl->idle = 0; + socket_set_nonblock(vs->csock); + qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs); + + vnc_client_cache_addr(vs); + vnc_qmp_event(vs, QEVENT_VNC_CONNECTED); + + vs->vd = vd; + vs->ds = vd->ds; + vs->last_x = -1; + vs->last_y = -1; + + vs->as.freq = 44100; + vs->as.nchannels = 2; + vs->as.fmt = AUD_FMT_S16; + vs->as.endianness = 0; + +#ifdef CONFIG_VNC_THREAD + qemu_mutex_init(&vs->output_mutex); +#endif + + QTAILQ_INSERT_HEAD(&vd->clients, vs, next); + + vga_hw_update(); + + vnc_write(vs, "RFB 003.008\n", 12); + vnc_flush(vs); + vnc_read_when(vs, protocol_version, 12); + reset_keys(vs); + if (vs->vd->lock_key_sync) + vs->led = qemu_add_led_event_handler(kbd_leds, vs); + + vs->mouse_mode_notifier.notify = check_pointer_type_change; + qemu_add_mouse_mode_change_notifier(&vs->mouse_mode_notifier); + + vnc_init_timer(vd); + + /* vs might be free()ed here */ +} + +static void vnc_listen_read(void *opaque) +{ + VncDisplay *vs = opaque; + struct sockaddr_in addr; + socklen_t addrlen = sizeof(addr); + + /* Catch-up */ + vga_hw_update(); + + int csock = qemu_accept(vs->lsock, (struct sockaddr *)&addr, &addrlen); + if (csock != -1) { + vnc_connect(vs, csock); + } +} + +void vnc_display_init(DisplayState *ds) +{ + VncDisplay *vs = qemu_mallocz(sizeof(*vs)); + + dcl = qemu_mallocz(sizeof(DisplayChangeListener)); + + ds->opaque = vs; + dcl->idle = 1; + vnc_display = vs; + + vs->lsock = -1; + + vs->ds = ds; + QTAILQ_INIT(&vs->clients); + vs->expires = TIME_MAX; + + if (keyboard_layout) + vs->kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout); + else + vs->kbd_layout = init_keyboard_layout(name2keysym, "en-us"); + + if (!vs->kbd_layout) + exit(1); + +#ifdef CONFIG_VNC_THREAD + qemu_mutex_init(&vs->mutex); + vnc_start_worker_thread(); +#endif + + dcl->dpy_copy = vnc_dpy_copy; + dcl->dpy_update = vnc_dpy_update; + dcl->dpy_resize = vnc_dpy_resize; + dcl->dpy_setdata = vnc_dpy_setdata; + register_displaychangelistener(ds, dcl); + ds->mouse_set = vnc_mouse_set; + ds->cursor_define = vnc_dpy_cursor_define; +} + + +void vnc_display_close(DisplayState *ds) +{ + VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display; + + if (!vs) + return; + if (vs->display) { + qemu_free(vs->display); + vs->display = NULL; + } + if (vs->lsock != -1) { + qemu_set_fd_handler2(vs->lsock, NULL, NULL, NULL, NULL); + close(vs->lsock); + vs->lsock = -1; + } + vs->auth = VNC_AUTH_INVALID; +#ifdef CONFIG_VNC_TLS + vs->subauth = VNC_AUTH_INVALID; + vs->tls.x509verify = 0; +#endif +} + +int vnc_display_disable_login(DisplayState *ds) +{ + VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display; + + if (!vs) { + return -1; + } + + if (vs->password) { + qemu_free(vs->password); + } + + vs->password = NULL; + vs->auth = VNC_AUTH_VNC; + + return 0; +} + +int vnc_display_password(DisplayState *ds, const char *password) +{ + VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display; + + if (!vs) { + return -1; + } + + if (!password) { + /* This is not the intention of this interface but err on the side + of being safe */ + return vnc_display_disable_login(ds); + } + + if (vs->password) { + qemu_free(vs->password); + vs->password = NULL; + } + vs->password = qemu_strdup(password); + vs->auth = VNC_AUTH_VNC; + + return 0; +} + +int vnc_display_pw_expire(DisplayState *ds, time_t expires) +{ + VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display; + + vs->expires = expires; + return 0; +} + +char *vnc_display_local_addr(DisplayState *ds) +{ + VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display; + + return vnc_socket_local_addr("%s:%s", vs->lsock); +} + +int vnc_display_open(DisplayState *ds, const char *display) +{ + VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display; + const char *options; + int password = 0; + int reverse = 0; +#ifdef CONFIG_VNC_TLS + int tls = 0, x509 = 0; +#endif +#ifdef CONFIG_VNC_SASL + int sasl = 0; + int saslErr; +#endif +#if defined(CONFIG_VNC_TLS) || defined(CONFIG_VNC_SASL) + int acl = 0; +#endif + int lock_key_sync = 1; + + if (!vnc_display) + return -1; + vnc_display_close(ds); + if (strcmp(display, "none") == 0) + return 0; + + if (!(vs->display = strdup(display))) + return -1; + + options = display; + while ((options = strchr(options, ','))) { + options++; + if (strncmp(options, "password", 8) == 0) { + password = 1; /* Require password auth */ + } else if (strncmp(options, "reverse", 7) == 0) { + reverse = 1; + } else if (strncmp(options, "no-lock-key-sync", 9) == 0) { + lock_key_sync = 0; +#ifdef CONFIG_VNC_SASL + } else if (strncmp(options, "sasl", 4) == 0) { + sasl = 1; /* Require SASL auth */ +#endif +#ifdef CONFIG_VNC_TLS + } else if (strncmp(options, "tls", 3) == 0) { + tls = 1; /* Require TLS */ + } else if (strncmp(options, "x509", 4) == 0) { + char *start, *end; + x509 = 1; /* Require x509 certificates */ + if (strncmp(options, "x509verify", 10) == 0) + vs->tls.x509verify = 1; /* ...and verify client certs */ + + /* Now check for 'x509=/some/path' postfix + * and use that to setup x509 certificate/key paths */ + start = strchr(options, '='); + end = strchr(options, ','); + if (start && (!end || (start < end))) { + int len = end ? end-(start+1) : strlen(start+1); + char *path = qemu_strndup(start + 1, len); + + VNC_DEBUG("Trying certificate path '%s'\n", path); + if (vnc_tls_set_x509_creds_dir(vs, path) < 0) { + fprintf(stderr, "Failed to find x509 certificates/keys in %s\n", path); + qemu_free(path); + qemu_free(vs->display); + vs->display = NULL; + return -1; + } + qemu_free(path); + } else { + fprintf(stderr, "No certificate path provided\n"); + qemu_free(vs->display); + vs->display = NULL; + return -1; + } +#endif +#if defined(CONFIG_VNC_TLS) || defined(CONFIG_VNC_SASL) + } else if (strncmp(options, "acl", 3) == 0) { + acl = 1; +#endif + } else if (strncmp(options, "lossy", 5) == 0) { + vs->lossy = true; + } + } + +#ifdef CONFIG_VNC_TLS + if (acl && x509 && vs->tls.x509verify) { + if (!(vs->tls.acl = qemu_acl_init("vnc.x509dname"))) { + fprintf(stderr, "Failed to create x509 dname ACL\n"); + exit(1); + } + } +#endif +#ifdef CONFIG_VNC_SASL + if (acl && sasl) { + if (!(vs->sasl.acl = qemu_acl_init("vnc.username"))) { + fprintf(stderr, "Failed to create username ACL\n"); + exit(1); + } + } +#endif + + /* + * Combinations we support here: + * + * - no-auth (clear text, no auth) + * - password (clear text, weak auth) + * - sasl (encrypt, good auth *IF* using Kerberos via GSSAPI) + * - tls (encrypt, weak anonymous creds, no auth) + * - tls + password (encrypt, weak anonymous creds, weak auth) + * - tls + sasl (encrypt, weak anonymous creds, good auth) + * - tls + x509 (encrypt, good x509 creds, no auth) + * - tls + x509 + password (encrypt, good x509 creds, weak auth) + * - tls + x509 + sasl (encrypt, good x509 creds, good auth) + * + * NB1. TLS is a stackable auth scheme. + * NB2. the x509 schemes have option to validate a client cert dname + */ + if (password) { +#ifdef CONFIG_VNC_TLS + if (tls) { + vs->auth = VNC_AUTH_VENCRYPT; + if (x509) { + VNC_DEBUG("Initializing VNC server with x509 password auth\n"); + vs->subauth = VNC_AUTH_VENCRYPT_X509VNC; + } else { + VNC_DEBUG("Initializing VNC server with TLS password auth\n"); + vs->subauth = VNC_AUTH_VENCRYPT_TLSVNC; + } + } else { +#endif /* CONFIG_VNC_TLS */ + VNC_DEBUG("Initializing VNC server with password auth\n"); + vs->auth = VNC_AUTH_VNC; +#ifdef CONFIG_VNC_TLS + vs->subauth = VNC_AUTH_INVALID; + } +#endif /* CONFIG_VNC_TLS */ +#ifdef CONFIG_VNC_SASL + } else if (sasl) { +#ifdef CONFIG_VNC_TLS + if (tls) { + vs->auth = VNC_AUTH_VENCRYPT; + if (x509) { + VNC_DEBUG("Initializing VNC server with x509 SASL auth\n"); + vs->subauth = VNC_AUTH_VENCRYPT_X509SASL; + } else { + VNC_DEBUG("Initializing VNC server with TLS SASL auth\n"); + vs->subauth = VNC_AUTH_VENCRYPT_TLSSASL; + } + } else { +#endif /* CONFIG_VNC_TLS */ + VNC_DEBUG("Initializing VNC server with SASL auth\n"); + vs->auth = VNC_AUTH_SASL; +#ifdef CONFIG_VNC_TLS + vs->subauth = VNC_AUTH_INVALID; + } +#endif /* CONFIG_VNC_TLS */ +#endif /* CONFIG_VNC_SASL */ + } else { +#ifdef CONFIG_VNC_TLS + if (tls) { + vs->auth = VNC_AUTH_VENCRYPT; + if (x509) { + VNC_DEBUG("Initializing VNC server with x509 no auth\n"); + vs->subauth = VNC_AUTH_VENCRYPT_X509NONE; + } else { + VNC_DEBUG("Initializing VNC server with TLS no auth\n"); + vs->subauth = VNC_AUTH_VENCRYPT_TLSNONE; + } + } else { +#endif + VNC_DEBUG("Initializing VNC server with no auth\n"); + vs->auth = VNC_AUTH_NONE; +#ifdef CONFIG_VNC_TLS + vs->subauth = VNC_AUTH_INVALID; + } +#endif + } + +#ifdef CONFIG_VNC_SASL + if ((saslErr = sasl_server_init(NULL, "qemu")) != SASL_OK) { + fprintf(stderr, "Failed to initialize SASL auth %s", + sasl_errstring(saslErr, NULL, NULL)); + free(vs->display); + vs->display = NULL; + return -1; + } +#endif + vs->lock_key_sync = lock_key_sync; + + if (reverse) { + /* connect to viewer */ + if (strncmp(display, "unix:", 5) == 0) + vs->lsock = unix_connect(display+5); + else + vs->lsock = inet_connect(display, SOCK_STREAM); + if (-1 == vs->lsock) { + free(vs->display); + vs->display = NULL; + return -1; + } else { + int csock = vs->lsock; + vs->lsock = -1; + vnc_connect(vs, csock); + } + return 0; + + } else { + /* listen for connects */ + char *dpy; + dpy = qemu_malloc(256); + if (strncmp(display, "unix:", 5) == 0) { + pstrcpy(dpy, 256, "unix:"); + vs->lsock = unix_listen(display+5, dpy+5, 256-5); + } else { + vs->lsock = inet_listen(display, dpy, 256, SOCK_STREAM, 5900); + } + if (-1 == vs->lsock) { + free(dpy); + return -1; + } else { + free(vs->display); + vs->display = dpy; + } + } + return qemu_set_fd_handler2(vs->lsock, NULL, vnc_listen_read, NULL, vs); +} diff -Nru qemu-kvm-0.12.5+noroms/ui/vnc-enc-hextile.c qemu-kvm-0.14.1/ui/vnc-enc-hextile.c --- qemu-kvm-0.12.5+noroms/ui/vnc-enc-hextile.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/ui/vnc-enc-hextile.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,116 @@ +/* + * QEMU VNC display driver: hextile encoding + * + * Copyright (C) 2006 Anthony Liguori + * Copyright (C) 2006 Fabrice Bellard + * Copyright (C) 2009 Red Hat, Inc + * + * 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 "vnc.h" + +static void hextile_enc_cord(uint8_t *ptr, int x, int y, int w, int h) +{ + ptr[0] = ((x & 0x0F) << 4) | (y & 0x0F); + ptr[1] = (((w - 1) & 0x0F) << 4) | ((h - 1) & 0x0F); +} + +#define BPP 8 +#include "vnc-enc-hextile-template.h" +#undef BPP + +#define BPP 16 +#include "vnc-enc-hextile-template.h" +#undef BPP + +#define BPP 32 +#include "vnc-enc-hextile-template.h" +#undef BPP + +#define GENERIC +#define BPP 8 +#include "vnc-enc-hextile-template.h" +#undef BPP +#undef GENERIC + +#define GENERIC +#define BPP 16 +#include "vnc-enc-hextile-template.h" +#undef BPP +#undef GENERIC + +#define GENERIC +#define BPP 32 +#include "vnc-enc-hextile-template.h" +#undef BPP +#undef GENERIC + +int vnc_hextile_send_framebuffer_update(VncState *vs, int x, + int y, int w, int h) +{ + int i, j; + int has_fg, has_bg; + uint8_t *last_fg, *last_bg; + VncDisplay *vd = vs->vd; + + last_fg = (uint8_t *) qemu_malloc(vd->server->pf.bytes_per_pixel); + last_bg = (uint8_t *) qemu_malloc(vd->server->pf.bytes_per_pixel); + has_fg = has_bg = 0; + for (j = y; j < (y + h); j += 16) { + for (i = x; i < (x + w); i += 16) { + vs->hextile.send_tile(vs, i, j, + MIN(16, x + w - i), MIN(16, y + h - j), + last_bg, last_fg, &has_bg, &has_fg); + } + } + free(last_fg); + free(last_bg); + + return 1; +} + +void vnc_hextile_set_pixel_conversion(VncState *vs, int generic) +{ + if (!generic) { + switch (vs->ds->surface->pf.bits_per_pixel) { + case 8: + vs->hextile.send_tile = send_hextile_tile_8; + break; + case 16: + vs->hextile.send_tile = send_hextile_tile_16; + break; + case 32: + vs->hextile.send_tile = send_hextile_tile_32; + break; + } + } else { + switch (vs->ds->surface->pf.bits_per_pixel) { + case 8: + vs->hextile.send_tile = send_hextile_tile_generic_8; + break; + case 16: + vs->hextile.send_tile = send_hextile_tile_generic_16; + break; + case 32: + vs->hextile.send_tile = send_hextile_tile_generic_32; + break; + } + } +} diff -Nru qemu-kvm-0.12.5+noroms/ui/vnc-enc-hextile-template.h qemu-kvm-0.14.1/ui/vnc-enc-hextile-template.h --- qemu-kvm-0.12.5+noroms/ui/vnc-enc-hextile-template.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/ui/vnc-enc-hextile-template.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,211 @@ +#define CONCAT_I(a, b) a ## b +#define CONCAT(a, b) CONCAT_I(a, b) +#define pixel_t CONCAT(uint, CONCAT(BPP, _t)) +#ifdef GENERIC +#define NAME CONCAT(generic_, BPP) +#else +#define NAME BPP +#endif + +static void CONCAT(send_hextile_tile_, NAME)(VncState *vs, + int x, int y, int w, int h, + void *last_bg_, + void *last_fg_, + int *has_bg, int *has_fg) +{ + VncDisplay *vd = vs->vd; + uint8_t *row = vd->server->data + y * ds_get_linesize(vs->ds) + x * ds_get_bytes_per_pixel(vs->ds); + pixel_t *irow = (pixel_t *)row; + int j, i; + pixel_t *last_bg = (pixel_t *)last_bg_; + pixel_t *last_fg = (pixel_t *)last_fg_; + pixel_t bg = 0; + pixel_t fg = 0; + int n_colors = 0; + int bg_count = 0; + int fg_count = 0; + int flags = 0; + uint8_t data[(vs->clientds.pf.bytes_per_pixel + 2) * 16 * 16]; + int n_data = 0; + int n_subtiles = 0; + + for (j = 0; j < h; j++) { + for (i = 0; i < w; i++) { + switch (n_colors) { + case 0: + bg = irow[i]; + n_colors = 1; + break; + case 1: + if (irow[i] != bg) { + fg = irow[i]; + n_colors = 2; + } + break; + case 2: + if (irow[i] != bg && irow[i] != fg) { + n_colors = 3; + } else { + if (irow[i] == bg) + bg_count++; + else if (irow[i] == fg) + fg_count++; + } + break; + default: + break; + } + } + if (n_colors > 2) + break; + irow += ds_get_linesize(vs->ds) / sizeof(pixel_t); + } + + if (n_colors > 1 && fg_count > bg_count) { + pixel_t tmp = fg; + fg = bg; + bg = tmp; + } + + if (!*has_bg || *last_bg != bg) { + flags |= 0x02; + *has_bg = 1; + *last_bg = bg; + } + + if (n_colors < 3 && (!*has_fg || *last_fg != fg)) { + flags |= 0x04; + *has_fg = 1; + *last_fg = fg; + } + + switch (n_colors) { + case 1: + n_data = 0; + break; + case 2: + flags |= 0x08; + + irow = (pixel_t *)row; + + for (j = 0; j < h; j++) { + int min_x = -1; + for (i = 0; i < w; i++) { + if (irow[i] == fg) { + if (min_x == -1) + min_x = i; + } else if (min_x != -1) { + hextile_enc_cord(data + n_data, min_x, j, i - min_x, 1); + n_data += 2; + n_subtiles++; + min_x = -1; + } + } + if (min_x != -1) { + hextile_enc_cord(data + n_data, min_x, j, i - min_x, 1); + n_data += 2; + n_subtiles++; + } + irow += ds_get_linesize(vs->ds) / sizeof(pixel_t); + } + break; + case 3: + flags |= 0x18; + + irow = (pixel_t *)row; + + if (!*has_bg || *last_bg != bg) + flags |= 0x02; + + for (j = 0; j < h; j++) { + int has_color = 0; + int min_x = -1; + pixel_t color = 0; /* shut up gcc */ + + for (i = 0; i < w; i++) { + if (!has_color) { + if (irow[i] == bg) + continue; + color = irow[i]; + min_x = i; + has_color = 1; + } else if (irow[i] != color) { + has_color = 0; +#ifdef GENERIC + vnc_convert_pixel(vs, data + n_data, color); + n_data += vs->clientds.pf.bytes_per_pixel; +#else + memcpy(data + n_data, &color, sizeof(color)); + n_data += sizeof(pixel_t); +#endif + hextile_enc_cord(data + n_data, min_x, j, i - min_x, 1); + n_data += 2; + n_subtiles++; + + min_x = -1; + if (irow[i] != bg) { + color = irow[i]; + min_x = i; + has_color = 1; + } + } + } + if (has_color) { +#ifdef GENERIC + vnc_convert_pixel(vs, data + n_data, color); + n_data += vs->clientds.pf.bytes_per_pixel; +#else + memcpy(data + n_data, &color, sizeof(color)); + n_data += sizeof(pixel_t); +#endif + hextile_enc_cord(data + n_data, min_x, j, i - min_x, 1); + n_data += 2; + n_subtiles++; + } + irow += ds_get_linesize(vs->ds) / sizeof(pixel_t); + } + + /* A SubrectsColoured subtile invalidates the foreground color */ + *has_fg = 0; + if (n_data > (w * h * sizeof(pixel_t))) { + n_colors = 4; + flags = 0x01; + *has_bg = 0; + + /* we really don't have to invalidate either the bg or fg + but we've lost the old values. oh well. */ + } + default: + break; + } + + if (n_colors > 3) { + flags = 0x01; + *has_fg = 0; + *has_bg = 0; + n_colors = 4; + } + + vnc_write_u8(vs, flags); + if (n_colors < 4) { + if (flags & 0x02) + vs->write_pixels(vs, &vd->server->pf, last_bg, sizeof(pixel_t)); + if (flags & 0x04) + vs->write_pixels(vs, &vd->server->pf, last_fg, sizeof(pixel_t)); + if (n_subtiles) { + vnc_write_u8(vs, n_subtiles); + vnc_write(vs, data, n_data); + } + } else { + for (j = 0; j < h; j++) { + vs->write_pixels(vs, &vd->server->pf, row, + w * ds_get_bytes_per_pixel(vs->ds)); + row += ds_get_linesize(vs->ds); + } + } +} + +#undef NAME +#undef pixel_t +#undef CONCAT_I +#undef CONCAT diff -Nru qemu-kvm-0.12.5+noroms/ui/vnc-enc-tight.c qemu-kvm-0.14.1/ui/vnc-enc-tight.c --- qemu-kvm-0.12.5+noroms/ui/vnc-enc-tight.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/ui/vnc-enc-tight.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,1718 @@ +/* + * QEMU VNC display driver: tight encoding + * + * From libvncserver/libvncserver/tight.c + * Copyright (C) 2000, 2001 Const Kaplinsky. All Rights Reserved. + * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. + * + * Copyright (C) 2010 Corentin Chary + * + * 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 "config-host.h" + +#ifdef CONFIG_VNC_PNG +#include +#endif +#ifdef CONFIG_VNC_JPEG +#include +#include +#endif + +#include "qemu-common.h" + +#include "bswap.h" +#include "qint.h" +#include "vnc.h" +#include "vnc-enc-tight.h" +#include "vnc-palette.h" + +/* Compression level stuff. The following array contains various + encoder parameters for each of 10 compression levels (0..9). + Last three parameters correspond to JPEG quality levels (0..9). */ + +static const struct { + int max_rect_size, max_rect_width; + int mono_min_rect_size, gradient_min_rect_size; + int idx_zlib_level, mono_zlib_level, raw_zlib_level, gradient_zlib_level; + int gradient_threshold, gradient_threshold24; + int idx_max_colors_divisor; + int jpeg_quality, jpeg_threshold, jpeg_threshold24; +} tight_conf[] = { + { 512, 32, 6, 65536, 0, 0, 0, 0, 0, 0, 4, 5, 10000, 23000 }, + { 2048, 128, 6, 65536, 1, 1, 1, 0, 0, 0, 8, 10, 8000, 18000 }, + { 6144, 256, 8, 65536, 3, 3, 2, 0, 0, 0, 24, 15, 6500, 15000 }, + { 10240, 1024, 12, 65536, 5, 5, 3, 0, 0, 0, 32, 25, 5000, 12000 }, + { 16384, 2048, 12, 65536, 6, 6, 4, 0, 0, 0, 32, 37, 4000, 10000 }, + { 32768, 2048, 12, 4096, 7, 7, 5, 4, 150, 380, 32, 50, 3000, 8000 }, + { 65536, 2048, 16, 4096, 7, 7, 6, 4, 170, 420, 48, 60, 2000, 5000 }, + { 65536, 2048, 16, 4096, 8, 8, 7, 5, 180, 450, 64, 70, 1000, 2500 }, + { 65536, 2048, 32, 8192, 9, 9, 8, 6, 190, 475, 64, 75, 500, 1200 }, + { 65536, 2048, 32, 8192, 9, 9, 9, 6, 200, 500, 96, 80, 200, 500 } +}; + + +static int tight_send_framebuffer_update(VncState *vs, int x, int y, + int w, int h); + +#ifdef CONFIG_VNC_PNG +static const struct { + int png_zlib_level, png_filters; +} tight_png_conf[] = { + { 0, PNG_NO_FILTERS }, + { 1, PNG_NO_FILTERS }, + { 2, PNG_NO_FILTERS }, + { 3, PNG_NO_FILTERS }, + { 4, PNG_NO_FILTERS }, + { 5, PNG_ALL_FILTERS }, + { 6, PNG_ALL_FILTERS }, + { 7, PNG_ALL_FILTERS }, + { 8, PNG_ALL_FILTERS }, + { 9, PNG_ALL_FILTERS }, +}; + +static int send_png_rect(VncState *vs, int x, int y, int w, int h, + VncPalette *palette); + +static bool tight_can_send_png_rect(VncState *vs, int w, int h) +{ + if (vs->tight.type != VNC_ENCODING_TIGHT_PNG) { + return false; + } + + if (ds_get_bytes_per_pixel(vs->ds) == 1 || + vs->clientds.pf.bytes_per_pixel == 1) { + return false; + } + + return true; +} +#endif + +/* + * Code to guess if given rectangle is suitable for smooth image + * compression (by applying "gradient" filter or JPEG coder). + */ + +static unsigned int +tight_detect_smooth_image24(VncState *vs, int w, int h) +{ + int off; + int x, y, d, dx; + unsigned int c; + unsigned int stats[256]; + int pixels = 0; + int pix, left[3]; + unsigned int errors; + unsigned char *buf = vs->tight.tight.buffer; + + /* + * If client is big-endian, color samples begin from the second + * byte (offset 1) of a 32-bit pixel value. + */ + off = !!(vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG); + + memset(stats, 0, sizeof (stats)); + + for (y = 0, x = 0; y < h && x < w;) { + for (d = 0; d < h - y && d < w - x - VNC_TIGHT_DETECT_SUBROW_WIDTH; + d++) { + for (c = 0; c < 3; c++) { + left[c] = buf[((y+d)*w+x+d)*4+off+c] & 0xFF; + } + for (dx = 1; dx <= VNC_TIGHT_DETECT_SUBROW_WIDTH; dx++) { + for (c = 0; c < 3; c++) { + pix = buf[((y+d)*w+x+d+dx)*4+off+c] & 0xFF; + stats[abs(pix - left[c])]++; + left[c] = pix; + } + pixels++; + } + } + if (w > h) { + x += h; + y = 0; + } else { + x = 0; + y += w; + } + } + + /* 95% smooth or more ... */ + if (stats[0] * 33 / pixels >= 95) { + return 0; + } + + errors = 0; + for (c = 1; c < 8; c++) { + errors += stats[c] * (c * c); + if (stats[c] == 0 || stats[c] > stats[c-1] * 2) { + return 0; + } + } + for (; c < 256; c++) { + errors += stats[c] * (c * c); + } + errors /= (pixels * 3 - stats[0]); + + return errors; +} + +#define DEFINE_DETECT_FUNCTION(bpp) \ + \ + static unsigned int \ + tight_detect_smooth_image##bpp(VncState *vs, int w, int h) { \ + bool endian; \ + uint##bpp##_t pix; \ + int max[3], shift[3]; \ + int x, y, d, dx; \ + unsigned int c; \ + unsigned int stats[256]; \ + int pixels = 0; \ + int sample, sum, left[3]; \ + unsigned int errors; \ + unsigned char *buf = vs->tight.tight.buffer; \ + \ + endian = ((vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) != \ + (vs->ds->surface->flags & QEMU_BIG_ENDIAN_FLAG)); \ + \ + \ + max[0] = vs->clientds.pf.rmax; \ + max[1] = vs->clientds.pf.gmax; \ + max[2] = vs->clientds.pf.bmax; \ + shift[0] = vs->clientds.pf.rshift; \ + shift[1] = vs->clientds.pf.gshift; \ + shift[2] = vs->clientds.pf.bshift; \ + \ + memset(stats, 0, sizeof(stats)); \ + \ + y = 0, x = 0; \ + while (y < h && x < w) { \ + for (d = 0; d < h - y && \ + d < w - x - VNC_TIGHT_DETECT_SUBROW_WIDTH; d++) { \ + pix = ((uint##bpp##_t *)buf)[(y+d)*w+x+d]; \ + if (endian) { \ + pix = bswap##bpp(pix); \ + } \ + for (c = 0; c < 3; c++) { \ + left[c] = (int)(pix >> shift[c] & max[c]); \ + } \ + for (dx = 1; dx <= VNC_TIGHT_DETECT_SUBROW_WIDTH; \ + dx++) { \ + pix = ((uint##bpp##_t *)buf)[(y+d)*w+x+d+dx]; \ + if (endian) { \ + pix = bswap##bpp(pix); \ + } \ + sum = 0; \ + for (c = 0; c < 3; c++) { \ + sample = (int)(pix >> shift[c] & max[c]); \ + sum += abs(sample - left[c]); \ + left[c] = sample; \ + } \ + if (sum > 255) { \ + sum = 255; \ + } \ + stats[sum]++; \ + pixels++; \ + } \ + } \ + if (w > h) { \ + x += h; \ + y = 0; \ + } else { \ + x = 0; \ + y += w; \ + } \ + } \ + \ + if ((stats[0] + stats[1]) * 100 / pixels >= 90) { \ + return 0; \ + } \ + \ + errors = 0; \ + for (c = 1; c < 8; c++) { \ + errors += stats[c] * (c * c); \ + if (stats[c] == 0 || stats[c] > stats[c-1] * 2) { \ + return 0; \ + } \ + } \ + for (; c < 256; c++) { \ + errors += stats[c] * (c * c); \ + } \ + errors /= (pixels - stats[0]); \ + \ + return errors; \ + } + +DEFINE_DETECT_FUNCTION(16) +DEFINE_DETECT_FUNCTION(32) + +static int +tight_detect_smooth_image(VncState *vs, int w, int h) +{ + unsigned int errors; + int compression = vs->tight.compression; + int quality = vs->tight.quality; + + if (!vs->vd->lossy) { + return 0; + } + + if (ds_get_bytes_per_pixel(vs->ds) == 1 || + vs->clientds.pf.bytes_per_pixel == 1 || + w < VNC_TIGHT_DETECT_MIN_WIDTH || h < VNC_TIGHT_DETECT_MIN_HEIGHT) { + return 0; + } + + if (vs->tight.quality != (uint8_t)-1) { + if (w * h < VNC_TIGHT_JPEG_MIN_RECT_SIZE) { + return 0; + } + } else { + if (w * h < tight_conf[compression].gradient_min_rect_size) { + return 0; + } + } + + if (vs->clientds.pf.bytes_per_pixel == 4) { + if (vs->tight.pixel24) { + errors = tight_detect_smooth_image24(vs, w, h); + if (vs->tight.quality != (uint8_t)-1) { + return (errors < tight_conf[quality].jpeg_threshold24); + } + return (errors < tight_conf[compression].gradient_threshold24); + } else { + errors = tight_detect_smooth_image32(vs, w, h); + } + } else { + errors = tight_detect_smooth_image16(vs, w, h); + } + if (quality != -1) { + return (errors < tight_conf[quality].jpeg_threshold); + } + return (errors < tight_conf[compression].gradient_threshold); +} + +/* + * Code to determine how many different colors used in rectangle. + */ +#define DEFINE_FILL_PALETTE_FUNCTION(bpp) \ + \ + static int \ + tight_fill_palette##bpp(VncState *vs, int x, int y, \ + int max, size_t count, \ + uint32_t *bg, uint32_t *fg, \ + VncPalette **palette) { \ + uint##bpp##_t *data; \ + uint##bpp##_t c0, c1, ci; \ + int i, n0, n1; \ + \ + data = (uint##bpp##_t *)vs->tight.tight.buffer; \ + \ + c0 = data[0]; \ + i = 1; \ + while (i < count && data[i] == c0) \ + i++; \ + if (i >= count) { \ + *bg = *fg = c0; \ + return 1; \ + } \ + \ + if (max < 2) { \ + return 0; \ + } \ + \ + n0 = i; \ + c1 = data[i]; \ + n1 = 0; \ + for (i++; i < count; i++) { \ + ci = data[i]; \ + if (ci == c0) { \ + n0++; \ + } else if (ci == c1) { \ + n1++; \ + } else \ + break; \ + } \ + if (i >= count) { \ + if (n0 > n1) { \ + *bg = (uint32_t)c0; \ + *fg = (uint32_t)c1; \ + } else { \ + *bg = (uint32_t)c1; \ + *fg = (uint32_t)c0; \ + } \ + return 2; \ + } \ + \ + if (max == 2) { \ + return 0; \ + } \ + \ + *palette = palette_new(max, bpp); \ + palette_put(*palette, c0); \ + palette_put(*palette, c1); \ + palette_put(*palette, ci); \ + \ + for (i++; i < count; i++) { \ + if (data[i] == ci) { \ + continue; \ + } else { \ + ci = data[i]; \ + if (!palette_put(*palette, (uint32_t)ci)) { \ + return 0; \ + } \ + } \ + } \ + \ + return palette_size(*palette); \ + } + +DEFINE_FILL_PALETTE_FUNCTION(8) +DEFINE_FILL_PALETTE_FUNCTION(16) +DEFINE_FILL_PALETTE_FUNCTION(32) + +static int tight_fill_palette(VncState *vs, int x, int y, + size_t count, uint32_t *bg, uint32_t *fg, + VncPalette **palette) +{ + int max; + + max = count / tight_conf[vs->tight.compression].idx_max_colors_divisor; + if (max < 2 && + count >= tight_conf[vs->tight.compression].mono_min_rect_size) { + max = 2; + } + if (max >= 256) { + max = 256; + } + + switch(vs->clientds.pf.bytes_per_pixel) { + case 4: + return tight_fill_palette32(vs, x, y, max, count, bg, fg, palette); + case 2: + return tight_fill_palette16(vs, x, y, max, count, bg, fg, palette); + default: + max = 2; + return tight_fill_palette8(vs, x, y, max, count, bg, fg, palette); + } + return 0; +} + +/* + * Converting truecolor samples into palette indices. + */ +#define DEFINE_IDX_ENCODE_FUNCTION(bpp) \ + \ + static void \ + tight_encode_indexed_rect##bpp(uint8_t *buf, int count, \ + VncPalette *palette) { \ + uint##bpp##_t *src; \ + uint##bpp##_t rgb; \ + int i, rep; \ + uint8_t idx; \ + \ + src = (uint##bpp##_t *) buf; \ + \ + for (i = 0; i < count; i++) { \ + \ + rgb = *src++; \ + rep = 0; \ + while (i < count && *src == rgb) { \ + rep++, src++, i++; \ + } \ + idx = palette_idx(palette, rgb); \ + /* \ + * Should never happen, but don't break everything \ + * if it does, use the first color instead \ + */ \ + if (idx == (uint8_t)-1) { \ + idx = 0; \ + } \ + while (rep >= 0) { \ + *buf++ = idx; \ + rep--; \ + } \ + } \ + } + +DEFINE_IDX_ENCODE_FUNCTION(16) +DEFINE_IDX_ENCODE_FUNCTION(32) + +#define DEFINE_MONO_ENCODE_FUNCTION(bpp) \ + \ + static void \ + tight_encode_mono_rect##bpp(uint8_t *buf, int w, int h, \ + uint##bpp##_t bg, uint##bpp##_t fg) { \ + uint##bpp##_t *ptr; \ + unsigned int value, mask; \ + int aligned_width; \ + int x, y, bg_bits; \ + \ + ptr = (uint##bpp##_t *) buf; \ + aligned_width = w - w % 8; \ + \ + for (y = 0; y < h; y++) { \ + for (x = 0; x < aligned_width; x += 8) { \ + for (bg_bits = 0; bg_bits < 8; bg_bits++) { \ + if (*ptr++ != bg) { \ + break; \ + } \ + } \ + if (bg_bits == 8) { \ + *buf++ = 0; \ + continue; \ + } \ + mask = 0x80 >> bg_bits; \ + value = mask; \ + for (bg_bits++; bg_bits < 8; bg_bits++) { \ + mask >>= 1; \ + if (*ptr++ != bg) { \ + value |= mask; \ + } \ + } \ + *buf++ = (uint8_t)value; \ + } \ + \ + mask = 0x80; \ + value = 0; \ + if (x >= w) { \ + continue; \ + } \ + \ + for (; x < w; x++) { \ + if (*ptr++ != bg) { \ + value |= mask; \ + } \ + mask >>= 1; \ + } \ + *buf++ = (uint8_t)value; \ + } \ + } + +DEFINE_MONO_ENCODE_FUNCTION(8) +DEFINE_MONO_ENCODE_FUNCTION(16) +DEFINE_MONO_ENCODE_FUNCTION(32) + +/* + * ``Gradient'' filter for 24-bit color samples. + * Should be called only when redMax, greenMax and blueMax are 255. + * Color components assumed to be byte-aligned. + */ + +static void +tight_filter_gradient24(VncState *vs, uint8_t *buf, int w, int h) +{ + uint32_t *buf32; + uint32_t pix32; + int shift[3]; + int *prev; + int here[3], upper[3], left[3], upperleft[3]; + int prediction; + int x, y, c; + + buf32 = (uint32_t *)buf; + memset(vs->tight.gradient.buffer, 0, w * 3 * sizeof(int)); + + if ((vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) == + (vs->ds->surface->flags & QEMU_BIG_ENDIAN_FLAG)) { + shift[0] = vs->clientds.pf.rshift; + shift[1] = vs->clientds.pf.gshift; + shift[2] = vs->clientds.pf.bshift; + } else { + shift[0] = 24 - vs->clientds.pf.rshift; + shift[1] = 24 - vs->clientds.pf.gshift; + shift[2] = 24 - vs->clientds.pf.bshift; + } + + for (y = 0; y < h; y++) { + for (c = 0; c < 3; c++) { + upper[c] = 0; + here[c] = 0; + } + prev = (int *)vs->tight.gradient.buffer; + for (x = 0; x < w; x++) { + pix32 = *buf32++; + for (c = 0; c < 3; c++) { + upperleft[c] = upper[c]; + left[c] = here[c]; + upper[c] = *prev; + here[c] = (int)(pix32 >> shift[c] & 0xFF); + *prev++ = here[c]; + + prediction = left[c] + upper[c] - upperleft[c]; + if (prediction < 0) { + prediction = 0; + } else if (prediction > 0xFF) { + prediction = 0xFF; + } + *buf++ = (char)(here[c] - prediction); + } + } + } +} + + +/* + * ``Gradient'' filter for other color depths. + */ + +#define DEFINE_GRADIENT_FILTER_FUNCTION(bpp) \ + \ + static void \ + tight_filter_gradient##bpp(VncState *vs, uint##bpp##_t *buf, \ + int w, int h) { \ + uint##bpp##_t pix, diff; \ + bool endian; \ + int *prev; \ + int max[3], shift[3]; \ + int here[3], upper[3], left[3], upperleft[3]; \ + int prediction; \ + int x, y, c; \ + \ + memset (vs->tight.gradient.buffer, 0, w * 3 * sizeof(int)); \ + \ + endian = ((vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) != \ + (vs->ds->surface->flags & QEMU_BIG_ENDIAN_FLAG)); \ + \ + max[0] = vs->clientds.pf.rmax; \ + max[1] = vs->clientds.pf.gmax; \ + max[2] = vs->clientds.pf.bmax; \ + shift[0] = vs->clientds.pf.rshift; \ + shift[1] = vs->clientds.pf.gshift; \ + shift[2] = vs->clientds.pf.bshift; \ + \ + for (y = 0; y < h; y++) { \ + for (c = 0; c < 3; c++) { \ + upper[c] = 0; \ + here[c] = 0; \ + } \ + prev = (int *)vs->tight.gradient.buffer; \ + for (x = 0; x < w; x++) { \ + pix = *buf; \ + if (endian) { \ + pix = bswap##bpp(pix); \ + } \ + diff = 0; \ + for (c = 0; c < 3; c++) { \ + upperleft[c] = upper[c]; \ + left[c] = here[c]; \ + upper[c] = *prev; \ + here[c] = (int)(pix >> shift[c] & max[c]); \ + *prev++ = here[c]; \ + \ + prediction = left[c] + upper[c] - upperleft[c]; \ + if (prediction < 0) { \ + prediction = 0; \ + } else if (prediction > max[c]) { \ + prediction = max[c]; \ + } \ + diff |= ((here[c] - prediction) & max[c]) \ + << shift[c]; \ + } \ + if (endian) { \ + diff = bswap##bpp(diff); \ + } \ + *buf++ = diff; \ + } \ + } \ + } + +DEFINE_GRADIENT_FILTER_FUNCTION(16) +DEFINE_GRADIENT_FILTER_FUNCTION(32) + +/* + * Check if a rectangle is all of the same color. If needSameColor is + * set to non-zero, then also check that its color equals to the + * *colorPtr value. The result is 1 if the test is successful, and in + * that case new color will be stored in *colorPtr. + */ + +#define DEFINE_CHECK_SOLID_FUNCTION(bpp) \ + \ + static bool \ + check_solid_tile##bpp(VncState *vs, int x, int y, int w, int h, \ + uint32_t* color, bool samecolor) \ + { \ + VncDisplay *vd = vs->vd; \ + uint##bpp##_t *fbptr; \ + uint##bpp##_t c; \ + int dx, dy; \ + \ + fbptr = (uint##bpp##_t *) \ + (vd->server->data + y * ds_get_linesize(vs->ds) + \ + x * ds_get_bytes_per_pixel(vs->ds)); \ + \ + c = *fbptr; \ + if (samecolor && (uint32_t)c != *color) { \ + return false; \ + } \ + \ + for (dy = 0; dy < h; dy++) { \ + for (dx = 0; dx < w; dx++) { \ + if (c != fbptr[dx]) { \ + return false; \ + } \ + } \ + fbptr = (uint##bpp##_t *) \ + ((uint8_t *)fbptr + ds_get_linesize(vs->ds)); \ + } \ + \ + *color = (uint32_t)c; \ + return true; \ + } + +DEFINE_CHECK_SOLID_FUNCTION(32) +DEFINE_CHECK_SOLID_FUNCTION(16) +DEFINE_CHECK_SOLID_FUNCTION(8) + +static bool check_solid_tile(VncState *vs, int x, int y, int w, int h, + uint32_t* color, bool samecolor) +{ + VncDisplay *vd = vs->vd; + + switch(vd->server->pf.bytes_per_pixel) { + case 4: + return check_solid_tile32(vs, x, y, w, h, color, samecolor); + case 2: + return check_solid_tile16(vs, x, y, w, h, color, samecolor); + default: + return check_solid_tile8(vs, x, y, w, h, color, samecolor); + } +} + +static void find_best_solid_area(VncState *vs, int x, int y, int w, int h, + uint32_t color, int *w_ptr, int *h_ptr) +{ + int dx, dy, dw, dh; + int w_prev; + int w_best = 0, h_best = 0; + + w_prev = w; + + for (dy = y; dy < y + h; dy += VNC_TIGHT_MAX_SPLIT_TILE_SIZE) { + + dh = MIN(VNC_TIGHT_MAX_SPLIT_TILE_SIZE, y + h - dy); + dw = MIN(VNC_TIGHT_MAX_SPLIT_TILE_SIZE, w_prev); + + if (!check_solid_tile(vs, x, dy, dw, dh, &color, true)) { + break; + } + + for (dx = x + dw; dx < x + w_prev;) { + dw = MIN(VNC_TIGHT_MAX_SPLIT_TILE_SIZE, x + w_prev - dx); + + if (!check_solid_tile(vs, dx, dy, dw, dh, &color, true)) { + break; + } + dx += dw; + } + + w_prev = dx - x; + if (w_prev * (dy + dh - y) > w_best * h_best) { + w_best = w_prev; + h_best = dy + dh - y; + } + } + + *w_ptr = w_best; + *h_ptr = h_best; +} + +static void extend_solid_area(VncState *vs, int x, int y, int w, int h, + uint32_t color, int *x_ptr, int *y_ptr, + int *w_ptr, int *h_ptr) +{ + int cx, cy; + + /* Try to extend the area upwards. */ + for ( cy = *y_ptr - 1; + cy >= y && check_solid_tile(vs, *x_ptr, cy, *w_ptr, 1, &color, true); + cy-- ); + *h_ptr += *y_ptr - (cy + 1); + *y_ptr = cy + 1; + + /* ... downwards. */ + for ( cy = *y_ptr + *h_ptr; + cy < y + h && + check_solid_tile(vs, *x_ptr, cy, *w_ptr, 1, &color, true); + cy++ ); + *h_ptr += cy - (*y_ptr + *h_ptr); + + /* ... to the left. */ + for ( cx = *x_ptr - 1; + cx >= x && check_solid_tile(vs, cx, *y_ptr, 1, *h_ptr, &color, true); + cx-- ); + *w_ptr += *x_ptr - (cx + 1); + *x_ptr = cx + 1; + + /* ... to the right. */ + for ( cx = *x_ptr + *w_ptr; + cx < x + w && + check_solid_tile(vs, cx, *y_ptr, 1, *h_ptr, &color, true); + cx++ ); + *w_ptr += cx - (*x_ptr + *w_ptr); +} + +static int tight_init_stream(VncState *vs, int stream_id, + int level, int strategy) +{ + z_streamp zstream = &vs->tight.stream[stream_id]; + + if (zstream->opaque == NULL) { + int err; + + VNC_DEBUG("VNC: TIGHT: initializing zlib stream %d\n", stream_id); + VNC_DEBUG("VNC: TIGHT: opaque = %p | vs = %p\n", zstream->opaque, vs); + zstream->zalloc = vnc_zlib_zalloc; + zstream->zfree = vnc_zlib_zfree; + + err = deflateInit2(zstream, level, Z_DEFLATED, MAX_WBITS, + MAX_MEM_LEVEL, strategy); + + if (err != Z_OK) { + fprintf(stderr, "VNC: error initializing zlib\n"); + return -1; + } + + vs->tight.levels[stream_id] = level; + zstream->opaque = vs; + } + + if (vs->tight.levels[stream_id] != level) { + if (deflateParams(zstream, level, strategy) != Z_OK) { + return -1; + } + vs->tight.levels[stream_id] = level; + } + return 0; +} + +static void tight_send_compact_size(VncState *vs, size_t len) +{ + int lpc = 0; + int bytes = 0; + char buf[3] = {0, 0, 0}; + + buf[bytes++] = len & 0x7F; + if (len > 0x7F) { + buf[bytes-1] |= 0x80; + buf[bytes++] = (len >> 7) & 0x7F; + if (len > 0x3FFF) { + buf[bytes-1] |= 0x80; + buf[bytes++] = (len >> 14) & 0xFF; + } + } + for (lpc = 0; lpc < bytes; lpc++) { + vnc_write_u8(vs, buf[lpc]); + } +} + +static int tight_compress_data(VncState *vs, int stream_id, size_t bytes, + int level, int strategy) +{ + z_streamp zstream = &vs->tight.stream[stream_id]; + int previous_out; + + if (bytes < VNC_TIGHT_MIN_TO_COMPRESS) { + vnc_write(vs, vs->tight.tight.buffer, vs->tight.tight.offset); + return bytes; + } + + if (tight_init_stream(vs, stream_id, level, strategy)) { + return -1; + } + + /* reserve memory in output buffer */ + buffer_reserve(&vs->tight.zlib, bytes + 64); + + /* set pointers */ + zstream->next_in = vs->tight.tight.buffer; + zstream->avail_in = vs->tight.tight.offset; + zstream->next_out = vs->tight.zlib.buffer + vs->tight.zlib.offset; + zstream->avail_out = vs->tight.zlib.capacity - vs->tight.zlib.offset; + previous_out = zstream->avail_out; + zstream->data_type = Z_BINARY; + + /* start encoding */ + if (deflate(zstream, Z_SYNC_FLUSH) != Z_OK) { + fprintf(stderr, "VNC: error during tight compression\n"); + return -1; + } + + vs->tight.zlib.offset = vs->tight.zlib.capacity - zstream->avail_out; + /* ...how much data has actually been produced by deflate() */ + bytes = previous_out - zstream->avail_out; + + tight_send_compact_size(vs, bytes); + vnc_write(vs, vs->tight.zlib.buffer, bytes); + + buffer_reset(&vs->tight.zlib); + + return bytes; +} + +/* + * Subencoding implementations. + */ +static void tight_pack24(VncState *vs, uint8_t *buf, size_t count, size_t *ret) +{ + uint32_t *buf32; + uint32_t pix; + int rshift, gshift, bshift; + + buf32 = (uint32_t *)buf; + + if ((vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) == + (vs->ds->surface->flags & QEMU_BIG_ENDIAN_FLAG)) { + rshift = vs->clientds.pf.rshift; + gshift = vs->clientds.pf.gshift; + bshift = vs->clientds.pf.bshift; + } else { + rshift = 24 - vs->clientds.pf.rshift; + gshift = 24 - vs->clientds.pf.gshift; + bshift = 24 - vs->clientds.pf.bshift; + } + + if (ret) { + *ret = count * 3; + } + + while (count--) { + pix = *buf32++; + *buf++ = (char)(pix >> rshift); + *buf++ = (char)(pix >> gshift); + *buf++ = (char)(pix >> bshift); + } +} + +static int send_full_color_rect(VncState *vs, int x, int y, int w, int h) +{ + int stream = 0; + ssize_t bytes; + +#ifdef CONFIG_VNC_PNG + if (tight_can_send_png_rect(vs, w, h)) { + return send_png_rect(vs, x, y, w, h, NULL); + } +#endif + + vnc_write_u8(vs, stream << 4); /* no flushing, no filter */ + + if (vs->tight.pixel24) { + tight_pack24(vs, vs->tight.tight.buffer, w * h, &vs->tight.tight.offset); + bytes = 3; + } else { + bytes = vs->clientds.pf.bytes_per_pixel; + } + + bytes = tight_compress_data(vs, stream, w * h * bytes, + tight_conf[vs->tight.compression].raw_zlib_level, + Z_DEFAULT_STRATEGY); + + return (bytes >= 0); +} + +static int send_solid_rect(VncState *vs) +{ + size_t bytes; + + vnc_write_u8(vs, VNC_TIGHT_FILL << 4); /* no flushing, no filter */ + + if (vs->tight.pixel24) { + tight_pack24(vs, vs->tight.tight.buffer, 1, &vs->tight.tight.offset); + bytes = 3; + } else { + bytes = vs->clientds.pf.bytes_per_pixel; + } + + vnc_write(vs, vs->tight.tight.buffer, bytes); + return 1; +} + +static int send_mono_rect(VncState *vs, int x, int y, + int w, int h, uint32_t bg, uint32_t fg) +{ + ssize_t bytes; + int stream = 1; + int level = tight_conf[vs->tight.compression].mono_zlib_level; + +#ifdef CONFIG_VNC_PNG + if (tight_can_send_png_rect(vs, w, h)) { + int ret; + int bpp = vs->clientds.pf.bytes_per_pixel * 8; + VncPalette *palette = palette_new(2, bpp); + + palette_put(palette, bg); + palette_put(palette, fg); + ret = send_png_rect(vs, x, y, w, h, palette); + palette_destroy(palette); + return ret; + } +#endif + + bytes = ((w + 7) / 8) * h; + + vnc_write_u8(vs, (stream | VNC_TIGHT_EXPLICIT_FILTER) << 4); + vnc_write_u8(vs, VNC_TIGHT_FILTER_PALETTE); + vnc_write_u8(vs, 1); + + switch(vs->clientds.pf.bytes_per_pixel) { + case 4: + { + uint32_t buf[2] = {bg, fg}; + size_t ret = sizeof (buf); + + if (vs->tight.pixel24) { + tight_pack24(vs, (unsigned char*)buf, 2, &ret); + } + vnc_write(vs, buf, ret); + + tight_encode_mono_rect32(vs->tight.tight.buffer, w, h, bg, fg); + break; + } + case 2: + vnc_write(vs, &bg, 2); + vnc_write(vs, &fg, 2); + tight_encode_mono_rect16(vs->tight.tight.buffer, w, h, bg, fg); + break; + default: + vnc_write_u8(vs, bg); + vnc_write_u8(vs, fg); + tight_encode_mono_rect8(vs->tight.tight.buffer, w, h, bg, fg); + break; + } + vs->tight.tight.offset = bytes; + + bytes = tight_compress_data(vs, stream, bytes, level, Z_DEFAULT_STRATEGY); + return (bytes >= 0); +} + +struct palette_cb_priv { + VncState *vs; + uint8_t *header; +#ifdef CONFIG_VNC_PNG + png_colorp png_palette; +#endif +}; + +static void write_palette(int idx, uint32_t color, void *opaque) +{ + struct palette_cb_priv *priv = opaque; + VncState *vs = priv->vs; + uint32_t bytes = vs->clientds.pf.bytes_per_pixel; + + if (bytes == 4) { + ((uint32_t*)priv->header)[idx] = color; + } else { + ((uint16_t*)priv->header)[idx] = color; + } +} + +static bool send_gradient_rect(VncState *vs, int x, int y, int w, int h) +{ + int stream = 3; + int level = tight_conf[vs->tight.compression].gradient_zlib_level; + ssize_t bytes; + + if (vs->clientds.pf.bytes_per_pixel == 1) + return send_full_color_rect(vs, x, y, w, h); + + vnc_write_u8(vs, (stream | VNC_TIGHT_EXPLICIT_FILTER) << 4); + vnc_write_u8(vs, VNC_TIGHT_FILTER_GRADIENT); + + buffer_reserve(&vs->tight.gradient, w * 3 * sizeof (int)); + + if (vs->tight.pixel24) { + tight_filter_gradient24(vs, vs->tight.tight.buffer, w, h); + bytes = 3; + } else if (vs->clientds.pf.bytes_per_pixel == 4) { + tight_filter_gradient32(vs, (uint32_t *)vs->tight.tight.buffer, w, h); + bytes = 4; + } else { + tight_filter_gradient16(vs, (uint16_t *)vs->tight.tight.buffer, w, h); + bytes = 2; + } + + buffer_reset(&vs->tight.gradient); + + bytes = w * h * bytes; + vs->tight.tight.offset = bytes; + + bytes = tight_compress_data(vs, stream, bytes, + level, Z_FILTERED); + return (bytes >= 0); +} + +static int send_palette_rect(VncState *vs, int x, int y, + int w, int h, VncPalette *palette) +{ + int stream = 2; + int level = tight_conf[vs->tight.compression].idx_zlib_level; + int colors; + ssize_t bytes; + +#ifdef CONFIG_VNC_PNG + if (tight_can_send_png_rect(vs, w, h)) { + return send_png_rect(vs, x, y, w, h, palette); + } +#endif + + colors = palette_size(palette); + + vnc_write_u8(vs, (stream | VNC_TIGHT_EXPLICIT_FILTER) << 4); + vnc_write_u8(vs, VNC_TIGHT_FILTER_PALETTE); + vnc_write_u8(vs, colors - 1); + + switch(vs->clientds.pf.bytes_per_pixel) { + case 4: + { + size_t old_offset, offset; + uint32_t header[palette_size(palette)]; + struct palette_cb_priv priv = { vs, (uint8_t *)header }; + + old_offset = vs->output.offset; + palette_iter(palette, write_palette, &priv); + vnc_write(vs, header, sizeof(header)); + + if (vs->tight.pixel24) { + tight_pack24(vs, vs->output.buffer + old_offset, colors, &offset); + vs->output.offset = old_offset + offset; + } + + tight_encode_indexed_rect32(vs->tight.tight.buffer, w * h, palette); + break; + } + case 2: + { + uint16_t header[palette_size(palette)]; + struct palette_cb_priv priv = { vs, (uint8_t *)header }; + + palette_iter(palette, write_palette, &priv); + vnc_write(vs, header, sizeof(header)); + tight_encode_indexed_rect16(vs->tight.tight.buffer, w * h, palette); + break; + } + default: + return -1; /* No palette for 8bits colors */ + break; + } + bytes = w * h; + vs->tight.tight.offset = bytes; + + bytes = tight_compress_data(vs, stream, bytes, + level, Z_DEFAULT_STRATEGY); + return (bytes >= 0); +} + +#if defined(CONFIG_VNC_JPEG) || defined(CONFIG_VNC_PNG) +static void rgb_prepare_row24(VncState *vs, uint8_t *dst, int x, int y, + int count) +{ + VncDisplay *vd = vs->vd; + uint32_t *fbptr; + uint32_t pix; + + fbptr = (uint32_t *)(vd->server->data + y * ds_get_linesize(vs->ds) + + x * ds_get_bytes_per_pixel(vs->ds)); + + while (count--) { + pix = *fbptr++; + *dst++ = (uint8_t)(pix >> vs->ds->surface->pf.rshift); + *dst++ = (uint8_t)(pix >> vs->ds->surface->pf.gshift); + *dst++ = (uint8_t)(pix >> vs->ds->surface->pf.bshift); + } +} + +#define DEFINE_RGB_GET_ROW_FUNCTION(bpp) \ + \ + static void \ + rgb_prepare_row##bpp(VncState *vs, uint8_t *dst, \ + int x, int y, int count) \ + { \ + VncDisplay *vd = vs->vd; \ + uint##bpp##_t *fbptr; \ + uint##bpp##_t pix; \ + int r, g, b; \ + \ + fbptr = (uint##bpp##_t *) \ + (vd->server->data + y * ds_get_linesize(vs->ds) + \ + x * ds_get_bytes_per_pixel(vs->ds)); \ + \ + while (count--) { \ + pix = *fbptr++; \ + \ + r = (int)((pix >> vs->ds->surface->pf.rshift) \ + & vs->ds->surface->pf.rmax); \ + g = (int)((pix >> vs->ds->surface->pf.gshift) \ + & vs->ds->surface->pf.gmax); \ + b = (int)((pix >> vs->ds->surface->pf.bshift) \ + & vs->ds->surface->pf.bmax); \ + \ + *dst++ = (uint8_t)((r * 255 + vs->ds->surface->pf.rmax / 2) \ + / vs->ds->surface->pf.rmax); \ + *dst++ = (uint8_t)((g * 255 + vs->ds->surface->pf.gmax / 2) \ + / vs->ds->surface->pf.gmax); \ + *dst++ = (uint8_t)((b * 255 + vs->ds->surface->pf.bmax / 2) \ + / vs->ds->surface->pf.bmax); \ + } \ + } + +DEFINE_RGB_GET_ROW_FUNCTION(16) +DEFINE_RGB_GET_ROW_FUNCTION(32) + +static void rgb_prepare_row(VncState *vs, uint8_t *dst, int x, int y, + int count) +{ + if (ds_get_bytes_per_pixel(vs->ds) == 4) { + if (vs->ds->surface->pf.rmax == 0xFF && + vs->ds->surface->pf.gmax == 0xFF && + vs->ds->surface->pf.bmax == 0xFF) { + rgb_prepare_row24(vs, dst, x, y, count); + } else { + rgb_prepare_row32(vs, dst, x, y, count); + } + } else { + rgb_prepare_row16(vs, dst, x, y, count); + } +} +#endif /* CONFIG_VNC_JPEG or CONFIG_VNC_PNG */ + +/* + * JPEG compression stuff. + */ +#ifdef CONFIG_VNC_JPEG +/* + * Destination manager implementation for JPEG library. + */ + +/* This is called once per encoding */ +static void jpeg_init_destination(j_compress_ptr cinfo) +{ + VncState *vs = cinfo->client_data; + Buffer *buffer = &vs->tight.jpeg; + + cinfo->dest->next_output_byte = (JOCTET *)buffer->buffer + buffer->offset; + cinfo->dest->free_in_buffer = (size_t)(buffer->capacity - buffer->offset); +} + +/* This is called when we ran out of buffer (shouldn't happen!) */ +static boolean jpeg_empty_output_buffer(j_compress_ptr cinfo) +{ + VncState *vs = cinfo->client_data; + Buffer *buffer = &vs->tight.jpeg; + + buffer->offset = buffer->capacity; + buffer_reserve(buffer, 2048); + jpeg_init_destination(cinfo); + return TRUE; +} + +/* This is called when we are done processing data */ +static void jpeg_term_destination(j_compress_ptr cinfo) +{ + VncState *vs = cinfo->client_data; + Buffer *buffer = &vs->tight.jpeg; + + buffer->offset = buffer->capacity - cinfo->dest->free_in_buffer; +} + +static int send_jpeg_rect(VncState *vs, int x, int y, int w, int h, int quality) +{ + struct jpeg_compress_struct cinfo; + struct jpeg_error_mgr jerr; + struct jpeg_destination_mgr manager; + JSAMPROW row[1]; + uint8_t *buf; + int dy; + + if (ds_get_bytes_per_pixel(vs->ds) == 1) + return send_full_color_rect(vs, x, y, w, h); + + buffer_reserve(&vs->tight.jpeg, 2048); + + cinfo.err = jpeg_std_error(&jerr); + jpeg_create_compress(&cinfo); + + cinfo.client_data = vs; + cinfo.image_width = w; + cinfo.image_height = h; + cinfo.input_components = 3; + cinfo.in_color_space = JCS_RGB; + + jpeg_set_defaults(&cinfo); + jpeg_set_quality(&cinfo, quality, true); + + manager.init_destination = jpeg_init_destination; + manager.empty_output_buffer = jpeg_empty_output_buffer; + manager.term_destination = jpeg_term_destination; + cinfo.dest = &manager; + + jpeg_start_compress(&cinfo, true); + + buf = qemu_malloc(w * 3); + row[0] = buf; + for (dy = 0; dy < h; dy++) { + rgb_prepare_row(vs, buf, x, y + dy, w); + jpeg_write_scanlines(&cinfo, row, 1); + } + qemu_free(buf); + + jpeg_finish_compress(&cinfo); + jpeg_destroy_compress(&cinfo); + + vnc_write_u8(vs, VNC_TIGHT_JPEG << 4); + + tight_send_compact_size(vs, vs->tight.jpeg.offset); + vnc_write(vs, vs->tight.jpeg.buffer, vs->tight.jpeg.offset); + buffer_reset(&vs->tight.jpeg); + + return 1; +} +#endif /* CONFIG_VNC_JPEG */ + +/* + * PNG compression stuff. + */ +#ifdef CONFIG_VNC_PNG +static void write_png_palette(int idx, uint32_t pix, void *opaque) +{ + struct palette_cb_priv *priv = opaque; + VncState *vs = priv->vs; + png_colorp color = &priv->png_palette[idx]; + + if (vs->tight.pixel24) + { + color->red = (pix >> vs->clientds.pf.rshift) & vs->clientds.pf.rmax; + color->green = (pix >> vs->clientds.pf.gshift) & vs->clientds.pf.gmax; + color->blue = (pix >> vs->clientds.pf.bshift) & vs->clientds.pf.bmax; + } + else + { + int red, green, blue; + + red = (pix >> vs->clientds.pf.rshift) & vs->clientds.pf.rmax; + green = (pix >> vs->clientds.pf.gshift) & vs->clientds.pf.gmax; + blue = (pix >> vs->clientds.pf.bshift) & vs->clientds.pf.bmax; + color->red = ((red * 255 + vs->clientds.pf.rmax / 2) / + vs->clientds.pf.rmax); + color->green = ((green * 255 + vs->clientds.pf.gmax / 2) / + vs->clientds.pf.gmax); + color->blue = ((blue * 255 + vs->clientds.pf.bmax / 2) / + vs->clientds.pf.bmax); + } +} + +static void png_write_data(png_structp png_ptr, png_bytep data, + png_size_t length) +{ + VncState *vs = png_get_io_ptr(png_ptr); + + buffer_reserve(&vs->tight.png, vs->tight.png.offset + length); + memcpy(vs->tight.png.buffer + vs->tight.png.offset, data, length); + + vs->tight.png.offset += length; +} + +static void png_flush_data(png_structp png_ptr) +{ +} + +static void *vnc_png_malloc(png_structp png_ptr, png_size_t size) +{ + return qemu_malloc(size); +} + +static void vnc_png_free(png_structp png_ptr, png_voidp ptr) +{ + qemu_free(ptr); +} + +static int send_png_rect(VncState *vs, int x, int y, int w, int h, + VncPalette *palette) +{ + png_byte color_type; + png_structp png_ptr; + png_infop info_ptr; + png_colorp png_palette = NULL; + int level = tight_png_conf[vs->tight.compression].png_zlib_level; + int filters = tight_png_conf[vs->tight.compression].png_filters; + uint8_t *buf; + int dy; + + png_ptr = png_create_write_struct_2(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL, + NULL, vnc_png_malloc, vnc_png_free); + + if (png_ptr == NULL) + return -1; + + info_ptr = png_create_info_struct(png_ptr); + + if (info_ptr == NULL) { + png_destroy_write_struct(&png_ptr, NULL); + return -1; + } + + png_set_write_fn(png_ptr, (void *) vs, png_write_data, png_flush_data); + png_set_compression_level(png_ptr, level); + png_set_filter(png_ptr, PNG_FILTER_TYPE_DEFAULT, filters); + + if (palette) { + color_type = PNG_COLOR_TYPE_PALETTE; + } else { + color_type = PNG_COLOR_TYPE_RGB; + } + + png_set_IHDR(png_ptr, info_ptr, w, h, + 8, color_type, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + + if (color_type == PNG_COLOR_TYPE_PALETTE) { + struct palette_cb_priv priv; + + png_palette = png_malloc(png_ptr, sizeof(*png_palette) * + palette_size(palette)); + + priv.vs = vs; + priv.png_palette = png_palette; + palette_iter(palette, write_png_palette, &priv); + + png_set_PLTE(png_ptr, info_ptr, png_palette, palette_size(palette)); + + if (vs->clientds.pf.bytes_per_pixel == 4) { + tight_encode_indexed_rect32(vs->tight.tight.buffer, w * h, palette); + } else { + tight_encode_indexed_rect16(vs->tight.tight.buffer, w * h, palette); + } + } + + png_write_info(png_ptr, info_ptr); + + buffer_reserve(&vs->tight.png, 2048); + buf = qemu_malloc(w * 3); + for (dy = 0; dy < h; dy++) + { + if (color_type == PNG_COLOR_TYPE_PALETTE) { + memcpy(buf, vs->tight.tight.buffer + (dy * w), w); + } else { + rgb_prepare_row(vs, buf, x, y + dy, w); + } + png_write_row(png_ptr, buf); + } + qemu_free(buf); + + png_write_end(png_ptr, NULL); + + if (color_type == PNG_COLOR_TYPE_PALETTE) { + png_free(png_ptr, png_palette); + } + + png_destroy_write_struct(&png_ptr, &info_ptr); + + vnc_write_u8(vs, VNC_TIGHT_PNG << 4); + + tight_send_compact_size(vs, vs->tight.png.offset); + vnc_write(vs, vs->tight.png.buffer, vs->tight.png.offset); + buffer_reset(&vs->tight.png); + return 1; +} +#endif /* CONFIG_VNC_PNG */ + +static void vnc_tight_start(VncState *vs) +{ + buffer_reset(&vs->tight.tight); + + // make the output buffer be the zlib buffer, so we can compress it later + vs->tight.tmp = vs->output; + vs->output = vs->tight.tight; +} + +static void vnc_tight_stop(VncState *vs) +{ + // switch back to normal output/zlib buffers + vs->tight.tight = vs->output; + vs->output = vs->tight.tmp; +} + +static int send_sub_rect_nojpeg(VncState *vs, int x, int y, int w, int h, + int bg, int fg, int colors, VncPalette *palette) +{ + int ret; + + if (colors == 0) { + if (tight_detect_smooth_image(vs, w, h)) { + ret = send_gradient_rect(vs, x, y, w, h); + } else { + ret = send_full_color_rect(vs, x, y, w, h); + } + } else if (colors == 1) { + ret = send_solid_rect(vs); + } else if (colors == 2) { + ret = send_mono_rect(vs, x, y, w, h, bg, fg); + } else if (colors <= 256) { + ret = send_palette_rect(vs, x, y, w, h, palette); + } else { + ret = 0; + } + return ret; +} + +#ifdef CONFIG_VNC_JPEG +static int send_sub_rect_jpeg(VncState *vs, int x, int y, int w, int h, + int bg, int fg, int colors, + VncPalette *palette) +{ + int ret; + + if (colors == 0) { + if (tight_detect_smooth_image(vs, w, h)) { + int quality = tight_conf[vs->tight.quality].jpeg_quality; + + ret = send_jpeg_rect(vs, x, y, w, h, quality); + } else { + ret = send_full_color_rect(vs, x, y, w, h); + } + } else if (colors == 1) { + ret = send_solid_rect(vs); + } else if (colors == 2) { + ret = send_mono_rect(vs, x, y, w, h, bg, fg); + } else if (colors <= 256) { + if (colors > 96 && + tight_detect_smooth_image(vs, w, h)) { + int quality = tight_conf[vs->tight.quality].jpeg_quality; + + ret = send_jpeg_rect(vs, x, y, w, h, quality); + } else { + ret = send_palette_rect(vs, x, y, w, h, palette); + } + } else { + ret = 0; + } + return ret; +} +#endif + +static int send_sub_rect(VncState *vs, int x, int y, int w, int h) +{ + VncPalette *palette = NULL; + uint32_t bg = 0, fg = 0; + int colors; + int ret = 0; + + vnc_framebuffer_update(vs, x, y, w, h, vs->tight.type); + + vnc_tight_start(vs); + vnc_raw_send_framebuffer_update(vs, x, y, w, h); + vnc_tight_stop(vs); + + colors = tight_fill_palette(vs, x, y, w * h, &fg, &bg, &palette); + +#ifdef CONFIG_VNC_JPEG + if (vs->tight.quality != (uint8_t)-1) { + ret = send_sub_rect_jpeg(vs, x, y, w, h, bg, fg, colors, palette); + } else { + ret = send_sub_rect_nojpeg(vs, x, y, w, h, bg, fg, colors, palette); + } +#else + ret = send_sub_rect_nojpeg(vs, x, y, w, h, bg, fg, colors, palette); +#endif + + palette_destroy(palette); + return ret; +} + +static int send_sub_rect_solid(VncState *vs, int x, int y, int w, int h) +{ + vnc_framebuffer_update(vs, x, y, w, h, vs->tight.type); + + vnc_tight_start(vs); + vnc_raw_send_framebuffer_update(vs, x, y, w, h); + vnc_tight_stop(vs); + + return send_solid_rect(vs); +} + +static int send_rect_simple(VncState *vs, int x, int y, int w, int h) +{ + int max_size, max_width; + int max_sub_width, max_sub_height; + int dx, dy; + int rw, rh; + int n = 0; + + max_size = tight_conf[vs->tight.compression].max_rect_size; + max_width = tight_conf[vs->tight.compression].max_rect_width; + + if (w > max_width || w * h > max_size) { + max_sub_width = (w > max_width) ? max_width : w; + max_sub_height = max_size / max_sub_width; + + for (dy = 0; dy < h; dy += max_sub_height) { + for (dx = 0; dx < w; dx += max_width) { + rw = MIN(max_sub_width, w - dx); + rh = MIN(max_sub_height, h - dy); + n += send_sub_rect(vs, x+dx, y+dy, rw, rh); + } + } + } else { + n += send_sub_rect(vs, x, y, w, h); + } + + return n; +} + +static int find_large_solid_color_rect(VncState *vs, int x, int y, + int w, int h, int max_rows) +{ + int dx, dy, dw, dh; + int n = 0; + + /* Try to find large solid-color areas and send them separately. */ + + for (dy = y; dy < y + h; dy += VNC_TIGHT_MAX_SPLIT_TILE_SIZE) { + + /* If a rectangle becomes too large, send its upper part now. */ + + if (dy - y >= max_rows) { + n += send_rect_simple(vs, x, y, w, max_rows); + y += max_rows; + h -= max_rows; + } + + dh = MIN(VNC_TIGHT_MAX_SPLIT_TILE_SIZE, (y + h - dy)); + + for (dx = x; dx < x + w; dx += VNC_TIGHT_MAX_SPLIT_TILE_SIZE) { + uint32_t color_value; + int x_best, y_best, w_best, h_best; + + dw = MIN(VNC_TIGHT_MAX_SPLIT_TILE_SIZE, (x + w - dx)); + + if (!check_solid_tile(vs, dx, dy, dw, dh, &color_value, false)) { + continue ; + } + + /* Get dimensions of solid-color area. */ + + find_best_solid_area(vs, dx, dy, w - (dx - x), h - (dy - y), + color_value, &w_best, &h_best); + + /* Make sure a solid rectangle is large enough + (or the whole rectangle is of the same color). */ + + if (w_best * h_best != w * h && + w_best * h_best < VNC_TIGHT_MIN_SOLID_SUBRECT_SIZE) { + continue; + } + + /* Try to extend solid rectangle to maximum size. */ + + x_best = dx; y_best = dy; + extend_solid_area(vs, x, y, w, h, color_value, + &x_best, &y_best, &w_best, &h_best); + + /* Send rectangles at top and left to solid-color area. */ + + if (y_best != y) { + n += send_rect_simple(vs, x, y, w, y_best-y); + } + if (x_best != x) { + n += tight_send_framebuffer_update(vs, x, y_best, + x_best-x, h_best); + } + + /* Send solid-color rectangle. */ + n += send_sub_rect_solid(vs, x_best, y_best, w_best, h_best); + + /* Send remaining rectangles (at right and bottom). */ + + if (x_best + w_best != x + w) { + n += tight_send_framebuffer_update(vs, x_best+w_best, + y_best, + w-(x_best-x)-w_best, + h_best); + } + if (y_best + h_best != y + h) { + n += tight_send_framebuffer_update(vs, x, y_best+h_best, + w, h-(y_best-y)-h_best); + } + + /* Return after all recursive calls are done. */ + return n; + } + } + return n + send_rect_simple(vs, x, y, w, h); +} + +static int tight_send_framebuffer_update(VncState *vs, int x, int y, + int w, int h) +{ + int max_rows; + + if (vs->clientds.pf.bytes_per_pixel == 4 && vs->clientds.pf.rmax == 0xFF && + vs->clientds.pf.bmax == 0xFF && vs->clientds.pf.gmax == 0xFF) { + vs->tight.pixel24 = true; + } else { + vs->tight.pixel24 = false; + } + + if (w * h < VNC_TIGHT_MIN_SPLIT_RECT_SIZE) + return send_rect_simple(vs, x, y, w, h); + + /* Calculate maximum number of rows in one non-solid rectangle. */ + + max_rows = tight_conf[vs->tight.compression].max_rect_size; + max_rows /= MIN(tight_conf[vs->tight.compression].max_rect_width, w); + + return find_large_solid_color_rect(vs, x, y, w, h, max_rows); +} + +int vnc_tight_send_framebuffer_update(VncState *vs, int x, int y, + int w, int h) +{ + vs->tight.type = VNC_ENCODING_TIGHT; + return tight_send_framebuffer_update(vs, x, y, w, h); +} + +int vnc_tight_png_send_framebuffer_update(VncState *vs, int x, int y, + int w, int h) +{ + vs->tight.type = VNC_ENCODING_TIGHT_PNG; + return tight_send_framebuffer_update(vs, x, y, w, h); +} + +void vnc_tight_clear(VncState *vs) +{ + int i; + for (i=0; itight.stream); i++) { + if (vs->tight.stream[i].opaque) { + deflateEnd(&vs->tight.stream[i]); + } + } + + buffer_free(&vs->tight.tight); + buffer_free(&vs->tight.zlib); + buffer_free(&vs->tight.gradient); +#ifdef CONFIG_VNC_JPEG + buffer_free(&vs->tight.jpeg); +#endif +#ifdef CONFIG_VNC_PNG + buffer_free(&vs->tight.png); +#endif +} diff -Nru qemu-kvm-0.12.5+noroms/ui/vnc-enc-tight.h qemu-kvm-0.14.1/ui/vnc-enc-tight.h --- qemu-kvm-0.12.5+noroms/ui/vnc-enc-tight.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/ui/vnc-enc-tight.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,183 @@ +/* + * QEMU VNC display driver: tight encoding + * + * From libvncserver/rfb/rfbproto.h + * Copyright (C) 2005 Rohit Kumar, Johannes E. Schindelin + * Copyright (C) 2000-2002 Constantin Kaplinsky. All Rights Reserved. + * Copyright (C) 2000 Tridia Corporation. All Rights Reserved. + * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. + * + * + * 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. + */ + +#ifndef VNC_ENCODING_TIGHT_H +#define VNC_ENCODING_TIGHT_H + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * Tight Encoding. + * + *-- The first byte of each Tight-encoded rectangle is a "compression control + * byte". Its format is as follows (bit 0 is the least significant one): + * + * bit 0: if 1, then compression stream 0 should be reset; + * bit 1: if 1, then compression stream 1 should be reset; + * bit 2: if 1, then compression stream 2 should be reset; + * bit 3: if 1, then compression stream 3 should be reset; + * bits 7-4: if 1000 (0x08), then the compression type is "fill", + * if 1001 (0x09), then the compression type is "jpeg", + * if 1010 (0x0A), then the compression type is "png", + * if 0xxx, then the compression type is "basic", + * values greater than 1010 are not valid. + * + * If the compression type is "basic", then bits 6..4 of the + * compression control byte (those xxx in 0xxx) specify the following: + * + * bits 5-4: decimal representation is the index of a particular zlib + * stream which should be used for decompressing the data; + * bit 6: if 1, then a "filter id" byte is following this byte. + * + *-- The data that follows after the compression control byte described + * above depends on the compression type ("fill", "jpeg", "png" or "basic"). + * + *-- If the compression type is "fill", then the only pixel value follows, in + * client pixel format (see NOTE 1). This value applies to all pixels of the + * rectangle. + * + *-- If the compression type is "jpeg" or "png", the following data stream + * looks like this: + * + * 1..3 bytes: data size (N) in compact representation; + * N bytes: JPEG or PNG image. + * + * Data size is compactly represented in one, two or three bytes, according + * to the following scheme: + * + * 0xxxxxxx (for values 0..127) + * 1xxxxxxx 0yyyyyyy (for values 128..16383) + * 1xxxxxxx 1yyyyyyy zzzzzzzz (for values 16384..4194303) + * + * Here each character denotes one bit, xxxxxxx are the least significant 7 + * bits of the value (bits 0-6), yyyyyyy are bits 7-13, and zzzzzzzz are the + * most significant 8 bits (bits 14-21). For example, decimal value 10000 + * should be represented as two bytes: binary 10010000 01001110, or + * hexadecimal 90 4E. + * + *-- If the compression type is "basic" and bit 6 of the compression control + * byte was set to 1, then the next (second) byte specifies "filter id" which + * tells the decoder what filter type was used by the encoder to pre-process + * pixel data before the compression. The "filter id" byte can be one of the + * following: + * + * 0: no filter ("copy" filter); + * 1: "palette" filter; + * 2: "gradient" filter. + * + *-- If bit 6 of the compression control byte is set to 0 (no "filter id" + * byte), or if the filter id is 0, then raw pixel values in the client + * format (see NOTE 1) will be compressed. See below details on the + * compression. + * + *-- The "gradient" filter pre-processes pixel data with a simple algorithm + * which converts each color component to a difference between a "predicted" + * intensity and the actual intensity. Such a technique does not affect + * uncompressed data size, but helps to compress photo-like images better. + * Pseudo-code for converting intensities to differences is the following: + * + * P[i,j] := V[i-1,j] + V[i,j-1] - V[i-1,j-1]; + * if (P[i,j] < 0) then P[i,j] := 0; + * if (P[i,j] > MAX) then P[i,j] := MAX; + * D[i,j] := V[i,j] - P[i,j]; + * + * Here V[i,j] is the intensity of a color component for a pixel at + * coordinates (i,j). MAX is the maximum value of intensity for a color + * component. + * + *-- The "palette" filter converts true-color pixel data to indexed colors + * and a palette which can consist of 2..256 colors. If the number of colors + * is 2, then each pixel is encoded in 1 bit, otherwise 8 bits is used to + * encode one pixel. 1-bit encoding is performed such way that the most + * significant bits correspond to the leftmost pixels, and each raw of pixels + * is aligned to the byte boundary. When "palette" filter is used, the + * palette is sent before the pixel data. The palette begins with an unsigned + * byte which value is the number of colors in the palette minus 1 (i.e. 1 + * means 2 colors, 255 means 256 colors in the palette). Then follows the + * palette itself which consist of pixel values in client pixel format (see + * NOTE 1). + * + *-- The pixel data is compressed using the zlib library. But if the data + * size after applying the filter but before the compression is less then 12, + * then the data is sent as is, uncompressed. Four separate zlib streams + * (0..3) can be used and the decoder should read the actual stream id from + * the compression control byte (see NOTE 2). + * + * If the compression is not used, then the pixel data is sent as is, + * otherwise the data stream looks like this: + * + * 1..3 bytes: data size (N) in compact representation; + * N bytes: zlib-compressed data. + * + * Data size is compactly represented in one, two or three bytes, just like + * in the "jpeg" compression method (see above). + * + *-- NOTE 1. If the color depth is 24, and all three color components are + * 8-bit wide, then one pixel in Tight encoding is always represented by + * three bytes, where the first byte is red component, the second byte is + * green component, and the third byte is blue component of the pixel color + * value. This applies to colors in palettes as well. + * + *-- NOTE 2. The decoder must reset compression streams' states before + * decoding the rectangle, if some of bits 0,1,2,3 in the compression control + * byte are set to 1. Note that the decoder must reset zlib streams even if + * the compression type is "fill", "jpeg" or "png". + * + *-- NOTE 3. The "gradient" filter and "jpeg" compression may be used only + * when bits-per-pixel value is either 16 or 32, not 8. + * + *-- NOTE 4. The width of any Tight-encoded rectangle cannot exceed 2048 + * pixels. If a rectangle is wider, it must be split into several rectangles + * and each one should be encoded separately. + * + */ + +#define VNC_TIGHT_EXPLICIT_FILTER 0x04 +#define VNC_TIGHT_FILL 0x08 +#define VNC_TIGHT_JPEG 0x09 +#define VNC_TIGHT_PNG 0x0A +#define VNC_TIGHT_MAX_SUBENCODING 0x0A + +/* Filters to improve compression efficiency */ +#define VNC_TIGHT_FILTER_COPY 0x00 +#define VNC_TIGHT_FILTER_PALETTE 0x01 +#define VNC_TIGHT_FILTER_GRADIENT 0x02 + +/* Note: The following constant should not be changed. */ +#define VNC_TIGHT_MIN_TO_COMPRESS 12 + +/* The parameters below may be adjusted. */ +#define VNC_TIGHT_MIN_SPLIT_RECT_SIZE 4096 +#define VNC_TIGHT_MIN_SOLID_SUBRECT_SIZE 2048 +#define VNC_TIGHT_MAX_SPLIT_TILE_SIZE 16 + +#define VNC_TIGHT_JPEG_MIN_RECT_SIZE 4096 +#define VNC_TIGHT_DETECT_SUBROW_WIDTH 7 +#define VNC_TIGHT_DETECT_MIN_WIDTH 8 +#define VNC_TIGHT_DETECT_MIN_HEIGHT 8 + +#endif /* VNC_ENCODING_TIGHT_H */ diff -Nru qemu-kvm-0.12.5+noroms/ui/vnc-enc-zlib.c qemu-kvm-0.14.1/ui/vnc-enc-zlib.c --- qemu-kvm-0.12.5+noroms/ui/vnc-enc-zlib.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/ui/vnc-enc-zlib.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,152 @@ +/* + * QEMU VNC display driver: zlib encoding + * + * Copyright (C) 2006 Anthony Liguori + * Copyright (C) 2006 Fabrice Bellard + * Copyright (C) 2009 Red Hat, Inc + * + * 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 "vnc.h" + +#define ZALLOC_ALIGNMENT 16 + +void *vnc_zlib_zalloc(void *x, unsigned items, unsigned size) +{ + void *p; + + size *= items; + size = (size + ZALLOC_ALIGNMENT - 1) & ~(ZALLOC_ALIGNMENT - 1); + + p = qemu_mallocz(size); + + return (p); +} + +void vnc_zlib_zfree(void *x, void *addr) +{ + qemu_free(addr); +} + +static void vnc_zlib_start(VncState *vs) +{ + buffer_reset(&vs->zlib.zlib); + + // make the output buffer be the zlib buffer, so we can compress it later + vs->zlib.tmp = vs->output; + vs->output = vs->zlib.zlib; +} + +static int vnc_zlib_stop(VncState *vs) +{ + z_streamp zstream = &vs->zlib.stream; + int previous_out; + + // switch back to normal output/zlib buffers + vs->zlib.zlib = vs->output; + vs->output = vs->zlib.tmp; + + // compress the zlib buffer + + // initialize the stream + // XXX need one stream per session + if (zstream->opaque != vs) { + int err; + + VNC_DEBUG("VNC: initializing zlib stream\n"); + VNC_DEBUG("VNC: opaque = %p | vs = %p\n", zstream->opaque, vs); + zstream->zalloc = vnc_zlib_zalloc; + zstream->zfree = vnc_zlib_zfree; + + err = deflateInit2(zstream, vs->tight.compression, Z_DEFLATED, MAX_WBITS, + MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY); + + if (err != Z_OK) { + fprintf(stderr, "VNC: error initializing zlib\n"); + return -1; + } + + vs->zlib.level = vs->tight.compression; + zstream->opaque = vs; + } + + if (vs->tight.compression != vs->zlib.level) { + if (deflateParams(zstream, vs->tight.compression, + Z_DEFAULT_STRATEGY) != Z_OK) { + return -1; + } + vs->zlib.level = vs->tight.compression; + } + + // reserve memory in output buffer + buffer_reserve(&vs->output, vs->zlib.zlib.offset + 64); + + // set pointers + zstream->next_in = vs->zlib.zlib.buffer; + zstream->avail_in = vs->zlib.zlib.offset; + zstream->next_out = vs->output.buffer + vs->output.offset; + zstream->avail_out = vs->output.capacity - vs->output.offset; + previous_out = zstream->avail_out; + zstream->data_type = Z_BINARY; + + // start encoding + if (deflate(zstream, Z_SYNC_FLUSH) != Z_OK) { + fprintf(stderr, "VNC: error during zlib compression\n"); + return -1; + } + + vs->output.offset = vs->output.capacity - zstream->avail_out; + return previous_out - zstream->avail_out; +} + +int vnc_zlib_send_framebuffer_update(VncState *vs, int x, int y, int w, int h) +{ + int old_offset, new_offset, bytes_written; + + vnc_framebuffer_update(vs, x, y, w, h, VNC_ENCODING_ZLIB); + + // remember where we put in the follow-up size + old_offset = vs->output.offset; + vnc_write_s32(vs, 0); + + // compress the stream + vnc_zlib_start(vs); + vnc_raw_send_framebuffer_update(vs, x, y, w, h); + bytes_written = vnc_zlib_stop(vs); + + if (bytes_written == -1) + return 0; + + // hack in the size + new_offset = vs->output.offset; + vs->output.offset = old_offset; + vnc_write_u32(vs, bytes_written); + vs->output.offset = new_offset; + + return 1; +} + +void vnc_zlib_clear(VncState *vs) +{ + if (vs->zlib.stream.opaque) { + deflateEnd(&vs->zlib.stream); + } + buffer_free(&vs->zlib.zlib); +} diff -Nru qemu-kvm-0.12.5+noroms/ui/vnc.h qemu-kvm-0.14.1/ui/vnc.h --- qemu-kvm-0.12.5+noroms/ui/vnc.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/ui/vnc.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,502 @@ +/* + * QEMU VNC display driver + * + * Copyright (C) 2006 Anthony Liguori + * Copyright (C) 2006 Fabrice Bellard + * Copyright (C) 2009 Red Hat, Inc + * + * 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. + */ + +#ifndef __QEMU_VNC_H +#define __QEMU_VNC_H + +#include "qemu-common.h" +#include "qemu-queue.h" +#ifdef CONFIG_VNC_THREAD +#include "qemu-thread.h" +#endif +#include "console.h" +#include "monitor.h" +#include "audio/audio.h" +#include +#include + +#include "keymaps.h" + +// #define _VNC_DEBUG 1 + +#ifdef _VNC_DEBUG +#define VNC_DEBUG(fmt, ...) do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0) +#else +#define VNC_DEBUG(fmt, ...) do { } while (0) +#endif + +/***************************************************************************** + * + * Core data structures + * + *****************************************************************************/ + +typedef struct Buffer +{ + size_t capacity; + size_t offset; + uint8_t *buffer; +} Buffer; + +typedef struct VncState VncState; +typedef struct VncJob VncJob; +typedef struct VncRect VncRect; +typedef struct VncRectEntry VncRectEntry; + +typedef int VncReadEvent(VncState *vs, uint8_t *data, size_t len); + +typedef void VncWritePixels(VncState *vs, struct PixelFormat *pf, void *data, int size); + +typedef void VncSendHextileTile(VncState *vs, + int x, int y, int w, int h, + void *last_bg, + void *last_fg, + int *has_bg, int *has_fg); + +#define VNC_MAX_WIDTH 2560 +#define VNC_MAX_HEIGHT 2048 +#define VNC_DIRTY_WORDS (VNC_MAX_WIDTH / (16 * 32)) + +#define VNC_AUTH_CHALLENGE_SIZE 16 + +typedef struct VncDisplay VncDisplay; + +#ifdef CONFIG_VNC_TLS +#include "vnc-tls.h" +#include "vnc-auth-vencrypt.h" +#endif +#ifdef CONFIG_VNC_SASL +#include "vnc-auth-sasl.h" +#endif + +struct VncSurface +{ + uint32_t dirty[VNC_MAX_HEIGHT][VNC_DIRTY_WORDS]; + DisplaySurface *ds; +}; + +struct VncDisplay +{ + QTAILQ_HEAD(, VncState) clients; + QEMUTimer *timer; + int timer_interval; + int lsock; + DisplayState *ds; + kbd_layout_t *kbd_layout; + int lock_key_sync; +#ifdef CONFIG_VNC_THREAD + QemuMutex mutex; +#endif + + QEMUCursor *cursor; + int cursor_msize; + uint8_t *cursor_mask; + + struct VncSurface guest; /* guest visible surface (aka ds->surface) */ + DisplaySurface *server; /* vnc server surface */ + + char *display; + char *password; + time_t expires; + int auth; + bool lossy; +#ifdef CONFIG_VNC_TLS + int subauth; /* Used by VeNCrypt */ + VncDisplayTLS tls; +#endif +#ifdef CONFIG_VNC_SASL + VncDisplaySASL sasl; +#endif +}; + +typedef struct VncTight { + int type; + uint8_t quality; + uint8_t compression; + uint8_t pixel24; + Buffer tight; + Buffer tmp; + Buffer zlib; + Buffer gradient; +#ifdef CONFIG_VNC_JPEG + Buffer jpeg; +#endif +#ifdef CONFIG_VNC_PNG + Buffer png; +#endif + int levels[4]; + z_stream stream[4]; +} VncTight; + +typedef struct VncHextile { + VncSendHextileTile *send_tile; +} VncHextile; + +typedef struct VncZlib { + Buffer zlib; + Buffer tmp; + z_stream stream; + int level; +} VncZlib; + +#ifdef CONFIG_VNC_THREAD +struct VncRect +{ + int x; + int y; + int w; + int h; +}; + +struct VncRectEntry +{ + struct VncRect rect; + QLIST_ENTRY(VncRectEntry) next; +}; + +struct VncJob +{ + VncState *vs; + + QLIST_HEAD(, VncRectEntry) rectangles; + QTAILQ_ENTRY(VncJob) next; +}; +#else +struct VncJob +{ + VncState *vs; + int rectangles; + size_t saved_offset; +}; +#endif + +struct VncState +{ + int csock; + + DisplayState *ds; + uint32_t dirty[VNC_MAX_HEIGHT][VNC_DIRTY_WORDS]; + + VncDisplay *vd; + int need_update; + int force_update; + uint32_t features; + int absolute; + int last_x; + int last_y; + int client_width; + int client_height; + + uint32_t vnc_encoding; + + int major; + int minor; + + char challenge[VNC_AUTH_CHALLENGE_SIZE]; +#ifdef CONFIG_VNC_TLS + VncStateTLS tls; +#endif +#ifdef CONFIG_VNC_SASL + VncStateSASL sasl; +#endif + + QObject *info; + + Buffer output; + Buffer input; + /* current output mode information */ + VncWritePixels *write_pixels; + DisplaySurface clientds; + + CaptureVoiceOut *audio_cap; + struct audsettings as; + + VncReadEvent *read_handler; + size_t read_handler_expect; + /* input */ + uint8_t modifiers_state[256]; + QEMUPutLEDEntry *led; + + bool abort; +#ifndef CONFIG_VNC_THREAD + VncJob job; +#else + QemuMutex output_mutex; +#endif + + /* Encoding specific, if you add something here, don't forget to + * update vnc_async_encoding_start() + */ + VncTight tight; + VncZlib zlib; + VncHextile hextile; + + + Notifier mouse_mode_notifier; + + QTAILQ_ENTRY(VncState) next; +}; + + +/***************************************************************************** + * + * Authentication modes + * + *****************************************************************************/ + +enum { + VNC_AUTH_INVALID = 0, + VNC_AUTH_NONE = 1, + VNC_AUTH_VNC = 2, + VNC_AUTH_RA2 = 5, + VNC_AUTH_RA2NE = 6, + VNC_AUTH_TIGHT = 16, + VNC_AUTH_ULTRA = 17, + VNC_AUTH_TLS = 18, /* Supported in GTK-VNC & VINO */ + VNC_AUTH_VENCRYPT = 19, /* Supported in GTK-VNC & VeNCrypt */ + VNC_AUTH_SASL = 20, /* Supported in GTK-VNC & VINO */ +}; + +enum { + VNC_AUTH_VENCRYPT_PLAIN = 256, + VNC_AUTH_VENCRYPT_TLSNONE = 257, + VNC_AUTH_VENCRYPT_TLSVNC = 258, + VNC_AUTH_VENCRYPT_TLSPLAIN = 259, + VNC_AUTH_VENCRYPT_X509NONE = 260, + VNC_AUTH_VENCRYPT_X509VNC = 261, + VNC_AUTH_VENCRYPT_X509PLAIN = 262, + VNC_AUTH_VENCRYPT_X509SASL = 263, + VNC_AUTH_VENCRYPT_TLSSASL = 264, +}; + + +/***************************************************************************** + * + * Encoding types + * + *****************************************************************************/ + +#define VNC_ENCODING_RAW 0x00000000 +#define VNC_ENCODING_COPYRECT 0x00000001 +#define VNC_ENCODING_RRE 0x00000002 +#define VNC_ENCODING_CORRE 0x00000004 +#define VNC_ENCODING_HEXTILE 0x00000005 +#define VNC_ENCODING_ZLIB 0x00000006 +#define VNC_ENCODING_TIGHT 0x00000007 +#define VNC_ENCODING_ZLIBHEX 0x00000008 +#define VNC_ENCODING_TRLE 0x0000000f +#define VNC_ENCODING_ZRLE 0x00000010 +#define VNC_ENCODING_ZYWRLE 0x00000011 +#define VNC_ENCODING_COMPRESSLEVEL0 0xFFFFFF00 /* -256 */ +#define VNC_ENCODING_QUALITYLEVEL0 0xFFFFFFE0 /* -32 */ +#define VNC_ENCODING_XCURSOR 0xFFFFFF10 /* -240 */ +#define VNC_ENCODING_RICH_CURSOR 0xFFFFFF11 /* -239 */ +#define VNC_ENCODING_POINTER_POS 0xFFFFFF18 /* -232 */ +#define VNC_ENCODING_LASTRECT 0xFFFFFF20 /* -224 */ +#define VNC_ENCODING_DESKTOPRESIZE 0xFFFFFF21 /* -223 */ +#define VNC_ENCODING_POINTER_TYPE_CHANGE 0XFFFFFEFF /* -257 */ +#define VNC_ENCODING_EXT_KEY_EVENT 0XFFFFFEFE /* -258 */ +#define VNC_ENCODING_AUDIO 0XFFFFFEFD /* -259 */ +#define VNC_ENCODING_TIGHT_PNG 0xFFFFFEFC /* -260 */ +#define VNC_ENCODING_WMVi 0x574D5669 + +/***************************************************************************** + * + * Other tight constants + * + *****************************************************************************/ + +/* + * Vendors known by TightVNC: standard VNC/RealVNC, TridiaVNC, and TightVNC. + */ + +#define VNC_TIGHT_CCB_RESET_MASK (0x0f) +#define VNC_TIGHT_CCB_TYPE_MASK (0x0f << 4) +#define VNC_TIGHT_CCB_TYPE_FILL (0x08 << 4) +#define VNC_TIGHT_CCB_TYPE_JPEG (0x09 << 4) +#define VNC_TIGHT_CCB_TYPE_PNG (0x0A << 4) +#define VNC_TIGHT_CCB_BASIC_MAX (0x07 << 4) +#define VNC_TIGHT_CCB_BASIC_ZLIB (0x03 << 4) +#define VNC_TIGHT_CCB_BASIC_FILTER (0x04 << 4) + +/***************************************************************************** + * + * Features + * + *****************************************************************************/ + +#define VNC_FEATURE_RESIZE 0 +#define VNC_FEATURE_HEXTILE 1 +#define VNC_FEATURE_POINTER_TYPE_CHANGE 2 +#define VNC_FEATURE_WMVI 3 +#define VNC_FEATURE_TIGHT 4 +#define VNC_FEATURE_ZLIB 5 +#define VNC_FEATURE_COPYRECT 6 +#define VNC_FEATURE_RICH_CURSOR 7 +#define VNC_FEATURE_TIGHT_PNG 8 + +#define VNC_FEATURE_RESIZE_MASK (1 << VNC_FEATURE_RESIZE) +#define VNC_FEATURE_HEXTILE_MASK (1 << VNC_FEATURE_HEXTILE) +#define VNC_FEATURE_POINTER_TYPE_CHANGE_MASK (1 << VNC_FEATURE_POINTER_TYPE_CHANGE) +#define VNC_FEATURE_WMVI_MASK (1 << VNC_FEATURE_WMVI) +#define VNC_FEATURE_TIGHT_MASK (1 << VNC_FEATURE_TIGHT) +#define VNC_FEATURE_ZLIB_MASK (1 << VNC_FEATURE_ZLIB) +#define VNC_FEATURE_COPYRECT_MASK (1 << VNC_FEATURE_COPYRECT) +#define VNC_FEATURE_RICH_CURSOR_MASK (1 << VNC_FEATURE_RICH_CURSOR) +#define VNC_FEATURE_TIGHT_PNG_MASK (1 << VNC_FEATURE_TIGHT_PNG) + + +/* Client -> Server message IDs */ +#define VNC_MSG_CLIENT_SET_PIXEL_FORMAT 0 +#define VNC_MSG_CLIENT_SET_ENCODINGS 2 +#define VNC_MSG_CLIENT_FRAMEBUFFER_UPDATE_REQUEST 3 +#define VNC_MSG_CLIENT_KEY_EVENT 4 +#define VNC_MSG_CLIENT_POINTER_EVENT 5 +#define VNC_MSG_CLIENT_CUT_TEXT 6 +#define VNC_MSG_CLIENT_VMWARE_0 127 +#define VNC_MSG_CLIENT_CALL_CONTROL 249 +#define VNC_MSG_CLIENT_XVP 250 +#define VNC_MSG_CLIENT_SET_DESKTOP_SIZE 251 +#define VNC_MSG_CLIENT_TIGHT 252 +#define VNC_MSG_CLIENT_GII 253 +#define VNC_MSG_CLIENT_VMWARE_1 254 +#define VNC_MSG_CLIENT_QEMU 255 + +/* Server -> Client message IDs */ +#define VNC_MSG_SERVER_FRAMEBUFFER_UPDATE 0 +#define VNC_MSG_SERVER_SET_COLOUR_MAP_ENTRIES 1 +#define VNC_MSG_SERVER_BELL 2 +#define VNC_MSG_SERVER_CUT_TEXT 3 +#define VNC_MSG_SERVER_VMWARE_0 127 +#define VNC_MSG_SERVER_CALL_CONTROL 249 +#define VNC_MSG_SERVER_XVP 250 +#define VNC_MSG_SERVER_TIGHT 252 +#define VNC_MSG_SERVER_GII 253 +#define VNC_MSG_SERVER_VMWARE_1 254 +#define VNC_MSG_SERVER_QEMU 255 + + + +/* QEMU client -> server message IDs */ +#define VNC_MSG_CLIENT_QEMU_EXT_KEY_EVENT 0 +#define VNC_MSG_CLIENT_QEMU_AUDIO 1 + +/* QEMU server -> client message IDs */ +#define VNC_MSG_SERVER_QEMU_AUDIO 1 + + + +/* QEMU client -> server audio message IDs */ +#define VNC_MSG_CLIENT_QEMU_AUDIO_ENABLE 0 +#define VNC_MSG_CLIENT_QEMU_AUDIO_DISABLE 1 +#define VNC_MSG_CLIENT_QEMU_AUDIO_SET_FORMAT 2 + +/* QEMU server -> client audio message IDs */ +#define VNC_MSG_SERVER_QEMU_AUDIO_END 0 +#define VNC_MSG_SERVER_QEMU_AUDIO_BEGIN 1 +#define VNC_MSG_SERVER_QEMU_AUDIO_DATA 2 + + +/***************************************************************************** + * + * Internal APIs + * + *****************************************************************************/ + +/* Event loop functions */ +void vnc_client_read(void *opaque); +void vnc_client_write(void *opaque); + +long vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen); +long vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen); + +/* Protocol I/O functions */ +void vnc_write(VncState *vs, const void *data, size_t len); +void vnc_write_u32(VncState *vs, uint32_t value); +void vnc_write_s32(VncState *vs, int32_t value); +void vnc_write_u16(VncState *vs, uint16_t value); +void vnc_write_u8(VncState *vs, uint8_t value); +void vnc_flush(VncState *vs); +void vnc_read_when(VncState *vs, VncReadEvent *func, size_t expecting); + + +/* Buffer I/O functions */ +uint8_t read_u8(uint8_t *data, size_t offset); +uint16_t read_u16(uint8_t *data, size_t offset); +int32_t read_s32(uint8_t *data, size_t offset); +uint32_t read_u32(uint8_t *data, size_t offset); + +/* Protocol stage functions */ +void vnc_client_error(VncState *vs); +int vnc_client_io_error(VncState *vs, int ret, int last_errno); + +void start_client_init(VncState *vs); +void start_auth_vnc(VncState *vs); + +/* Buffer management */ +void buffer_reserve(Buffer *buffer, size_t len); +int buffer_empty(Buffer *buffer); +uint8_t *buffer_end(Buffer *buffer); +void buffer_reset(Buffer *buffer); +void buffer_free(Buffer *buffer); +void buffer_append(Buffer *buffer, const void *data, size_t len); + + +/* Misc helpers */ + +char *vnc_socket_local_addr(const char *format, int fd); +char *vnc_socket_remote_addr(const char *format, int fd); + +static inline uint32_t vnc_has_feature(VncState *vs, int feature) { + return (vs->features & (1 << feature)); +} + +/* Framebuffer */ +void vnc_framebuffer_update(VncState *vs, int x, int y, int w, int h, + int32_t encoding); + +void vnc_convert_pixel(VncState *vs, uint8_t *buf, uint32_t v); + +/* Encodings */ +int vnc_send_framebuffer_update(VncState *vs, int x, int y, int w, int h); + +int vnc_raw_send_framebuffer_update(VncState *vs, int x, int y, int w, int h); + +int vnc_hextile_send_framebuffer_update(VncState *vs, int x, + int y, int w, int h); +void vnc_hextile_set_pixel_conversion(VncState *vs, int generic); + +void *vnc_zlib_zalloc(void *x, unsigned items, unsigned size); +void vnc_zlib_zfree(void *x, void *addr); +int vnc_zlib_send_framebuffer_update(VncState *vs, int x, int y, int w, int h); +void vnc_zlib_clear(VncState *vs); + +int vnc_tight_send_framebuffer_update(VncState *vs, int x, int y, int w, int h); +int vnc_tight_png_send_framebuffer_update(VncState *vs, int x, int y, + int w, int h); +void vnc_tight_clear(VncState *vs); + +#endif /* __QEMU_VNC_H */ diff -Nru qemu-kvm-0.12.5+noroms/ui/vnc-jobs-async.c qemu-kvm-0.14.1/ui/vnc-jobs-async.c --- qemu-kvm-0.12.5+noroms/ui/vnc-jobs-async.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/ui/vnc-jobs-async.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,335 @@ +/* + * QEMU VNC display driver + * + * Copyright (C) 2006 Anthony Liguori + * Copyright (C) 2006 Fabrice Bellard + * Copyright (C) 2009 Red Hat, Inc + * Copyright (C) 2010 Corentin Chary + * + * 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 "vnc.h" +#include "vnc-jobs.h" + +/* + * Locking: + * + * There is three levels of locking: + * - jobs queue lock: for each operation on the queue (push, pop, isEmpty?) + * - VncDisplay global lock: mainly used for framebuffer updates to avoid + * screen corruption if the framebuffer is updated + * while the worker is doing something. + * - VncState::output lock: used to make sure the output buffer is not corrupted + * if two threads try to write on it at the same time + * + * While the VNC worker thread is working, the VncDisplay global lock is hold + * to avoid screen corruptions (this does not block vnc_refresh() because it + * uses trylock()) but the output lock is not hold because the thread work on + * its own output buffer. + * When the encoding job is done, the worker thread will hold the output lock + * and copy its output buffer in vs->output. +*/ + +struct VncJobQueue { + QemuCond cond; + QemuMutex mutex; + QemuThread thread; + Buffer buffer; + bool exit; + QTAILQ_HEAD(, VncJob) jobs; +}; + +typedef struct VncJobQueue VncJobQueue; + +/* + * We use a single global queue, but most of the functions are + * already reetrant, so we can easilly add more than one encoding thread + */ +static VncJobQueue *queue; + +static void vnc_lock_queue(VncJobQueue *queue) +{ + qemu_mutex_lock(&queue->mutex); +} + +static void vnc_unlock_queue(VncJobQueue *queue) +{ + qemu_mutex_unlock(&queue->mutex); +} + +VncJob *vnc_job_new(VncState *vs) +{ + VncJob *job = qemu_mallocz(sizeof(VncJob)); + + job->vs = vs; + vnc_lock_queue(queue); + QLIST_INIT(&job->rectangles); + vnc_unlock_queue(queue); + return job; +} + +int vnc_job_add_rect(VncJob *job, int x, int y, int w, int h) +{ + VncRectEntry *entry = qemu_mallocz(sizeof(VncRectEntry)); + + entry->rect.x = x; + entry->rect.y = y; + entry->rect.w = w; + entry->rect.h = h; + + vnc_lock_queue(queue); + QLIST_INSERT_HEAD(&job->rectangles, entry, next); + vnc_unlock_queue(queue); + return 1; +} + +void vnc_job_push(VncJob *job) +{ + vnc_lock_queue(queue); + if (queue->exit || QLIST_EMPTY(&job->rectangles)) { + qemu_free(job); + } else { + QTAILQ_INSERT_TAIL(&queue->jobs, job, next); + qemu_cond_broadcast(&queue->cond); + } + vnc_unlock_queue(queue); +} + +static bool vnc_has_job_locked(VncState *vs) +{ + VncJob *job; + + QTAILQ_FOREACH(job, &queue->jobs, next) { + if (job->vs == vs || !vs) { + return true; + } + } + return false; +} + +bool vnc_has_job(VncState *vs) +{ + bool ret; + + vnc_lock_queue(queue); + ret = vnc_has_job_locked(vs); + vnc_unlock_queue(queue); + return ret; +} + +void vnc_jobs_clear(VncState *vs) +{ + VncJob *job, *tmp; + + vnc_lock_queue(queue); + QTAILQ_FOREACH_SAFE(job, &queue->jobs, next, tmp) { + if (job->vs == vs || !vs) { + QTAILQ_REMOVE(&queue->jobs, job, next); + } + } + vnc_unlock_queue(queue); +} + +void vnc_jobs_join(VncState *vs) +{ + vnc_lock_queue(queue); + while (vnc_has_job_locked(vs)) { + qemu_cond_wait(&queue->cond, &queue->mutex); + } + vnc_unlock_queue(queue); +} + +/* + * Copy data for local use + */ +static void vnc_async_encoding_start(VncState *orig, VncState *local) +{ + local->vnc_encoding = orig->vnc_encoding; + local->features = orig->features; + local->ds = orig->ds; + local->vd = orig->vd; + local->write_pixels = orig->write_pixels; + local->clientds = orig->clientds; + local->tight = orig->tight; + local->zlib = orig->zlib; + local->hextile = orig->hextile; + local->output = queue->buffer; + local->csock = -1; /* Don't do any network work on this thread */ + + buffer_reset(&local->output); +} + +static void vnc_async_encoding_end(VncState *orig, VncState *local) +{ + orig->tight = local->tight; + orig->zlib = local->zlib; + orig->hextile = local->hextile; +} + +static int vnc_worker_thread_loop(VncJobQueue *queue) +{ + VncJob *job; + VncRectEntry *entry, *tmp; + VncState vs; + int n_rectangles; + int saved_offset; + bool flush; + + vnc_lock_queue(queue); + while (QTAILQ_EMPTY(&queue->jobs) && !queue->exit) { + qemu_cond_wait(&queue->cond, &queue->mutex); + } + /* Here job can only be NULL if queue->exit is true */ + job = QTAILQ_FIRST(&queue->jobs); + vnc_unlock_queue(queue); + + if (queue->exit) { + return -1; + } + + vnc_lock_output(job->vs); + if (job->vs->csock == -1 || job->vs->abort == true) { + goto disconnected; + } + vnc_unlock_output(job->vs); + + /* Make a local copy of vs and switch output buffers */ + vnc_async_encoding_start(job->vs, &vs); + + /* Start sending rectangles */ + n_rectangles = 0; + vnc_write_u8(&vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE); + vnc_write_u8(&vs, 0); + saved_offset = vs.output.offset; + vnc_write_u16(&vs, 0); + + vnc_lock_display(job->vs->vd); + QLIST_FOREACH_SAFE(entry, &job->rectangles, next, tmp) { + int n; + + if (job->vs->csock == -1) { + vnc_unlock_display(job->vs->vd); + /* output mutex must be locked before going to + * disconnected: + */ + vnc_lock_output(job->vs); + goto disconnected; + } + + n = vnc_send_framebuffer_update(&vs, entry->rect.x, entry->rect.y, + entry->rect.w, entry->rect.h); + + if (n >= 0) { + n_rectangles += n; + } + qemu_free(entry); + } + vnc_unlock_display(job->vs->vd); + + /* Put n_rectangles at the beginning of the message */ + vs.output.buffer[saved_offset] = (n_rectangles >> 8) & 0xFF; + vs.output.buffer[saved_offset + 1] = n_rectangles & 0xFF; + + /* Switch back buffers */ + vnc_lock_output(job->vs); + if (job->vs->csock == -1) { + goto disconnected; + } + + vnc_write(job->vs, vs.output.buffer, vs.output.offset); + +disconnected: + /* Copy persistent encoding data */ + vnc_async_encoding_end(job->vs, &vs); + flush = (job->vs->csock != -1 && job->vs->abort != true); + vnc_unlock_output(job->vs); + + if (flush) { + vnc_flush(job->vs); + } + + vnc_lock_queue(queue); + QTAILQ_REMOVE(&queue->jobs, job, next); + vnc_unlock_queue(queue); + qemu_cond_broadcast(&queue->cond); + qemu_free(job); + return 0; +} + +static VncJobQueue *vnc_queue_init(void) +{ + VncJobQueue *queue = qemu_mallocz(sizeof(VncJobQueue)); + + qemu_cond_init(&queue->cond); + qemu_mutex_init(&queue->mutex); + QTAILQ_INIT(&queue->jobs); + return queue; +} + +static void vnc_queue_clear(VncJobQueue *q) +{ + qemu_cond_destroy(&queue->cond); + qemu_mutex_destroy(&queue->mutex); + buffer_free(&queue->buffer); + qemu_free(q); + queue = NULL; /* Unset global queue */ +} + +static void *vnc_worker_thread(void *arg) +{ + VncJobQueue *queue = arg; + + qemu_thread_self(&queue->thread); + + while (!vnc_worker_thread_loop(queue)) ; + vnc_queue_clear(queue); + return NULL; +} + +void vnc_start_worker_thread(void) +{ + VncJobQueue *q; + + if (vnc_worker_thread_running()) + return ; + + q = vnc_queue_init(); + qemu_thread_create(&q->thread, vnc_worker_thread, q); + queue = q; /* Set global queue */ +} + +bool vnc_worker_thread_running(void) +{ + return queue; /* Check global queue */ +} + +void vnc_stop_worker_thread(void) +{ + if (!vnc_worker_thread_running()) + return ; + + /* Remove all jobs and wake up the thread */ + vnc_lock_queue(queue); + queue->exit = true; + vnc_unlock_queue(queue); + vnc_jobs_clear(NULL); + qemu_cond_broadcast(&queue->cond); +} diff -Nru qemu-kvm-0.12.5+noroms/ui/vnc-jobs.h qemu-kvm-0.14.1/ui/vnc-jobs.h --- qemu-kvm-0.12.5+noroms/ui/vnc-jobs.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/ui/vnc-jobs.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,87 @@ +/* + * QEMU VNC display driver + * + * From libvncserver/rfb/rfbproto.h + * Copyright (C) 2005 Rohit Kumar, Johannes E. Schindelin + * Copyright (C) 2000-2002 Constantin Kaplinsky. All Rights Reserved. + * Copyright (C) 2000 Tridia Corporation. All Rights Reserved. + * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. + * + * + * 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. + */ + +#ifndef VNC_JOBS_H +#define VNC_JOBS_H + +/* Jobs */ +VncJob *vnc_job_new(VncState *vs); +int vnc_job_add_rect(VncJob *job, int x, int y, int w, int h); +void vnc_job_push(VncJob *job); +bool vnc_has_job(VncState *vs); +void vnc_jobs_clear(VncState *vs); +void vnc_jobs_join(VncState *vs); + +#ifdef CONFIG_VNC_THREAD + +void vnc_start_worker_thread(void); +bool vnc_worker_thread_running(void); +void vnc_stop_worker_thread(void); + +#endif /* CONFIG_VNC_THREAD */ + +/* Locks */ +static inline int vnc_trylock_display(VncDisplay *vd) +{ +#ifdef CONFIG_VNC_THREAD + return qemu_mutex_trylock(&vd->mutex); +#else + return 0; +#endif +} + +static inline void vnc_lock_display(VncDisplay *vd) +{ +#ifdef CONFIG_VNC_THREAD + qemu_mutex_lock(&vd->mutex); +#endif +} + +static inline void vnc_unlock_display(VncDisplay *vd) +{ +#ifdef CONFIG_VNC_THREAD + qemu_mutex_unlock(&vd->mutex); +#endif +} + +static inline void vnc_lock_output(VncState *vs) +{ +#ifdef CONFIG_VNC_THREAD + qemu_mutex_lock(&vs->output_mutex); +#endif +} + +static inline void vnc_unlock_output(VncState *vs) +{ +#ifdef CONFIG_VNC_THREAD + qemu_mutex_unlock(&vs->output_mutex); +#endif +} + +#endif /* VNC_JOBS_H */ diff -Nru qemu-kvm-0.12.5+noroms/ui/vnc-jobs-sync.c qemu-kvm-0.14.1/ui/vnc-jobs-sync.c --- qemu-kvm-0.12.5+noroms/ui/vnc-jobs-sync.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/ui/vnc-jobs-sync.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,73 @@ +/* + * QEMU VNC display driver + * + * Copyright (C) 2006 Anthony Liguori + * Copyright (C) 2006 Fabrice Bellard + * Copyright (C) 2009 Red Hat, Inc + * Copyright (C) 2010 Corentin Chary + * + * 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 "vnc.h" +#include "vnc-jobs.h" + +void vnc_jobs_clear(VncState *vs) +{ +} + +void vnc_jobs_join(VncState *vs) +{ +} + +VncJob *vnc_job_new(VncState *vs) +{ + vs->job.vs = vs; + vs->job.rectangles = 0; + + vnc_write_u8(vs, VNC_MSG_SERVER_FRAMEBUFFER_UPDATE); + vnc_write_u8(vs, 0); + vs->job.saved_offset = vs->output.offset; + vnc_write_u16(vs, 0); + return &vs->job; +} + +void vnc_job_push(VncJob *job) +{ + VncState *vs = job->vs; + + vs->output.buffer[job->saved_offset] = (job->rectangles >> 8) & 0xFF; + vs->output.buffer[job->saved_offset + 1] = job->rectangles & 0xFF; + vnc_flush(job->vs); +} + +int vnc_job_add_rect(VncJob *job, int x, int y, int w, int h) +{ + int n; + + n = vnc_send_framebuffer_update(job->vs, x, y, w, h); + if (n >= 0) + job->rectangles += n; + return n; +} + +bool vnc_has_job(VncState *vs) +{ + return false; +} diff -Nru qemu-kvm-0.12.5+noroms/ui/vnc_keysym.h qemu-kvm-0.14.1/ui/vnc_keysym.h --- qemu-kvm-0.12.5+noroms/ui/vnc_keysym.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/ui/vnc_keysym.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,324 @@ + +#include "keymaps.h" + +static const name2keysym_t name2keysym[]={ +/* ascii */ + { "space", 0x020}, + { "exclam", 0x021}, + { "quotedbl", 0x022}, + { "numbersign", 0x023}, + { "dollar", 0x024}, + { "percent", 0x025}, + { "ampersand", 0x026}, + { "apostrophe", 0x027}, + { "parenleft", 0x028}, + { "parenright", 0x029}, + { "asterisk", 0x02a}, + { "plus", 0x02b}, + { "comma", 0x02c}, + { "minus", 0x02d}, + { "period", 0x02e}, + { "slash", 0x02f}, + { "0", 0x030}, + { "1", 0x031}, + { "2", 0x032}, + { "3", 0x033}, + { "4", 0x034}, + { "5", 0x035}, + { "6", 0x036}, + { "7", 0x037}, + { "8", 0x038}, + { "9", 0x039}, + { "colon", 0x03a}, + { "semicolon", 0x03b}, + { "less", 0x03c}, + { "equal", 0x03d}, + { "greater", 0x03e}, + { "question", 0x03f}, + { "at", 0x040}, + { "A", 0x041}, + { "B", 0x042}, + { "C", 0x043}, + { "D", 0x044}, + { "E", 0x045}, + { "F", 0x046}, + { "G", 0x047}, + { "H", 0x048}, + { "I", 0x049}, + { "J", 0x04a}, + { "K", 0x04b}, + { "L", 0x04c}, + { "M", 0x04d}, + { "N", 0x04e}, + { "O", 0x04f}, + { "P", 0x050}, + { "Q", 0x051}, + { "R", 0x052}, + { "S", 0x053}, + { "T", 0x054}, + { "U", 0x055}, + { "V", 0x056}, + { "W", 0x057}, + { "X", 0x058}, + { "Y", 0x059}, + { "Z", 0x05a}, + { "bracketleft", 0x05b}, + { "backslash", 0x05c}, + { "bracketright", 0x05d}, + { "asciicircum", 0x05e}, + { "underscore", 0x05f}, + { "grave", 0x060}, + { "a", 0x061}, + { "b", 0x062}, + { "c", 0x063}, + { "d", 0x064}, + { "e", 0x065}, + { "f", 0x066}, + { "g", 0x067}, + { "h", 0x068}, + { "i", 0x069}, + { "j", 0x06a}, + { "k", 0x06b}, + { "l", 0x06c}, + { "m", 0x06d}, + { "n", 0x06e}, + { "o", 0x06f}, + { "p", 0x070}, + { "q", 0x071}, + { "r", 0x072}, + { "s", 0x073}, + { "t", 0x074}, + { "u", 0x075}, + { "v", 0x076}, + { "w", 0x077}, + { "x", 0x078}, + { "y", 0x079}, + { "z", 0x07a}, + { "braceleft", 0x07b}, + { "bar", 0x07c}, + { "braceright", 0x07d}, + { "asciitilde", 0x07e}, + +/* latin 1 extensions */ +{ "nobreakspace", 0x0a0}, +{ "exclamdown", 0x0a1}, +{ "cent", 0x0a2}, +{ "sterling", 0x0a3}, +{ "currency", 0x0a4}, +{ "yen", 0x0a5}, +{ "brokenbar", 0x0a6}, +{ "section", 0x0a7}, +{ "diaeresis", 0x0a8}, +{ "copyright", 0x0a9}, +{ "ordfeminine", 0x0aa}, +{ "guillemotleft", 0x0ab}, +{ "notsign", 0x0ac}, +{ "hyphen", 0x0ad}, +{ "registered", 0x0ae}, +{ "macron", 0x0af}, +{ "degree", 0x0b0}, +{ "plusminus", 0x0b1}, +{ "twosuperior", 0x0b2}, +{ "threesuperior", 0x0b3}, +{ "acute", 0x0b4}, +{ "mu", 0x0b5}, +{ "paragraph", 0x0b6}, +{ "periodcentered", 0x0b7}, +{ "cedilla", 0x0b8}, +{ "onesuperior", 0x0b9}, +{ "masculine", 0x0ba}, +{ "guillemotright", 0x0bb}, +{ "onequarter", 0x0bc}, +{ "onehalf", 0x0bd}, +{ "threequarters", 0x0be}, +{ "questiondown", 0x0bf}, +{ "Agrave", 0x0c0}, +{ "Aacute", 0x0c1}, +{ "Acircumflex", 0x0c2}, +{ "Atilde", 0x0c3}, +{ "Adiaeresis", 0x0c4}, +{ "Aring", 0x0c5}, +{ "AE", 0x0c6}, +{ "Ccedilla", 0x0c7}, +{ "Egrave", 0x0c8}, +{ "Eacute", 0x0c9}, +{ "Ecircumflex", 0x0ca}, +{ "Ediaeresis", 0x0cb}, +{ "Igrave", 0x0cc}, +{ "Iacute", 0x0cd}, +{ "Icircumflex", 0x0ce}, +{ "Idiaeresis", 0x0cf}, +{ "ETH", 0x0d0}, +{ "Eth", 0x0d0}, +{ "Ntilde", 0x0d1}, +{ "Ograve", 0x0d2}, +{ "Oacute", 0x0d3}, +{ "Ocircumflex", 0x0d4}, +{ "Otilde", 0x0d5}, +{ "Odiaeresis", 0x0d6}, +{ "multiply", 0x0d7}, +{ "Ooblique", 0x0d8}, +{ "Oslash", 0x0d8}, +{ "Ugrave", 0x0d9}, +{ "Uacute", 0x0da}, +{ "Ucircumflex", 0x0db}, +{ "Udiaeresis", 0x0dc}, +{ "Yacute", 0x0dd}, +{ "THORN", 0x0de}, +{ "Thorn", 0x0de}, +{ "ssharp", 0x0df}, +{ "agrave", 0x0e0}, +{ "aacute", 0x0e1}, +{ "acircumflex", 0x0e2}, +{ "atilde", 0x0e3}, +{ "adiaeresis", 0x0e4}, +{ "aring", 0x0e5}, +{ "ae", 0x0e6}, +{ "ccedilla", 0x0e7}, +{ "egrave", 0x0e8}, +{ "eacute", 0x0e9}, +{ "ecircumflex", 0x0ea}, +{ "ediaeresis", 0x0eb}, +{ "igrave", 0x0ec}, +{ "iacute", 0x0ed}, +{ "icircumflex", 0x0ee}, +{ "idiaeresis", 0x0ef}, +{ "eth", 0x0f0}, +{ "ntilde", 0x0f1}, +{ "ograve", 0x0f2}, +{ "oacute", 0x0f3}, +{ "ocircumflex", 0x0f4}, +{ "otilde", 0x0f5}, +{ "odiaeresis", 0x0f6}, +{ "division", 0x0f7}, +{ "oslash", 0x0f8}, +{ "ooblique", 0x0f8}, +{ "ugrave", 0x0f9}, +{ "uacute", 0x0fa}, +{ "ucircumflex", 0x0fb}, +{ "udiaeresis", 0x0fc}, +{ "yacute", 0x0fd}, +{ "thorn", 0x0fe}, +{ "ydiaeresis", 0x0ff}, +{"EuroSign", 0x20ac}, /* XK_EuroSign */ + + /* modifiers */ +{"ISO_Level3_Shift", 0xfe03}, /* XK_ISO_Level3_Shift */ +{"Control_L", 0xffe3}, /* XK_Control_L */ +{"Control_R", 0xffe4}, /* XK_Control_R */ +{"Alt_L", 0xffe9}, /* XK_Alt_L */ +{"Alt_R", 0xffea}, /* XK_Alt_R */ +{"Caps_Lock", 0xffe5}, /* XK_Caps_Lock */ +{"Meta_L", 0xffe7}, /* XK_Meta_L */ +{"Meta_R", 0xffe8}, /* XK_Meta_R */ +{"Shift_L", 0xffe1}, /* XK_Shift_L */ +{"Shift_R", 0xffe2}, /* XK_Shift_R */ +{"Super_L", 0xffeb}, /* XK_Super_L */ +{"Super_R", 0xffec}, /* XK_Super_R */ + + /* special keys */ +{"BackSpace", 0xff08}, /* XK_BackSpace */ +{"Tab", 0xff09}, /* XK_Tab */ +{"Return", 0xff0d}, /* XK_Return */ +{"Right", 0xff53}, /* XK_Right */ +{"Left", 0xff51}, /* XK_Left */ +{"Up", 0xff52}, /* XK_Up */ +{"Down", 0xff54}, /* XK_Down */ +{"Page_Down", 0xff56}, /* XK_Page_Down */ +{"Page_Up", 0xff55}, /* XK_Page_Up */ +{"Insert", 0xff63}, /* XK_Insert */ +{"Delete", 0xffff}, /* XK_Delete */ +{"Home", 0xff50}, /* XK_Home */ +{"End", 0xff57}, /* XK_End */ +{"Scroll_Lock", 0xff14}, /* XK_Scroll_Lock */ +{"KP_Home", 0xff95}, +{"KP_Left", 0xff96}, +{"KP_Up", 0xff97}, +{"KP_Right", 0xff98}, +{"KP_Down", 0xff99}, +{"KP_Prior", 0xff9a}, +{"KP_Page_Up", 0xff9a}, +{"KP_Next", 0xff9b}, +{"KP_Page_Down", 0xff9b}, +{"KP_End", 0xff9c}, +{"KP_Begin", 0xff9d}, +{"KP_Insert", 0xff9e}, +{"KP_Delete", 0xff9f}, +{"F1", 0xffbe}, /* XK_F1 */ +{"F2", 0xffbf}, /* XK_F2 */ +{"F3", 0xffc0}, /* XK_F3 */ +{"F4", 0xffc1}, /* XK_F4 */ +{"F5", 0xffc2}, /* XK_F5 */ +{"F6", 0xffc3}, /* XK_F6 */ +{"F7", 0xffc4}, /* XK_F7 */ +{"F8", 0xffc5}, /* XK_F8 */ +{"F9", 0xffc6}, /* XK_F9 */ +{"F10", 0xffc7}, /* XK_F10 */ +{"F11", 0xffc8}, /* XK_F11 */ +{"F12", 0xffc9}, /* XK_F12 */ +{"F13", 0xffca}, /* XK_F13 */ +{"F14", 0xffcb}, /* XK_F14 */ +{"F15", 0xffcc}, /* XK_F15 */ +{"Sys_Req", 0xff15}, /* XK_Sys_Req */ +{"KP_0", 0xffb0}, /* XK_KP_0 */ +{"KP_1", 0xffb1}, /* XK_KP_1 */ +{"KP_2", 0xffb2}, /* XK_KP_2 */ +{"KP_3", 0xffb3}, /* XK_KP_3 */ +{"KP_4", 0xffb4}, /* XK_KP_4 */ +{"KP_5", 0xffb5}, /* XK_KP_5 */ +{"KP_6", 0xffb6}, /* XK_KP_6 */ +{"KP_7", 0xffb7}, /* XK_KP_7 */ +{"KP_8", 0xffb8}, /* XK_KP_8 */ +{"KP_9", 0xffb9}, /* XK_KP_9 */ +{"KP_Add", 0xffab}, /* XK_KP_Add */ +{"KP_Separator", 0xffac},/* XK_KP_Separator */ +{"KP_Decimal", 0xffae}, /* XK_KP_Decimal */ +{"KP_Divide", 0xffaf}, /* XK_KP_Divide */ +{"KP_Enter", 0xff8d}, /* XK_KP_Enter */ +{"KP_Equal", 0xffbd}, /* XK_KP_Equal */ +{"KP_Multiply", 0xffaa}, /* XK_KP_Multiply */ +{"KP_Subtract", 0xffad}, /* XK_KP_Subtract */ +{"help", 0xff6a}, /* XK_Help */ +{"Menu", 0xff67}, /* XK_Menu */ +{"Print", 0xff61}, /* XK_Print */ +{"Mode_switch", 0xff7e}, /* XK_Mode_switch */ +{"Num_Lock", 0xff7f}, /* XK_Num_Lock */ +{"Pause", 0xff13}, /* XK_Pause */ +{"Escape", 0xff1b}, /* XK_Escape */ + +/* dead keys */ +{"dead_grave", 0xfe50}, /* XK_dead_grave */ +{"dead_acute", 0xfe51}, /* XK_dead_acute */ +{"dead_circumflex", 0xfe52}, /* XK_dead_circumflex */ +{"dead_tilde", 0xfe53}, /* XK_dead_tilde */ +{"dead_macron", 0xfe54}, /* XK_dead_macron */ +{"dead_breve", 0xfe55}, /* XK_dead_breve */ +{"dead_abovedot", 0xfe56}, /* XK_dead_abovedot */ +{"dead_diaeresis", 0xfe57}, /* XK_dead_diaeresis */ +{"dead_abovering", 0xfe58}, /* XK_dead_abovering */ +{"dead_doubleacute", 0xfe59}, /* XK_dead_doubleacute */ +{"dead_caron", 0xfe5a}, /* XK_dead_caron */ +{"dead_cedilla", 0xfe5b}, /* XK_dead_cedilla */ +{"dead_ogonek", 0xfe5c}, /* XK_dead_ogonek */ +{"dead_iota", 0xfe5d}, /* XK_dead_iota */ +{"dead_voiced_sound", 0xfe5e}, /* XK_dead_voiced_sound */ +{"dead_semivoiced_sound", 0xfe5f}, /* XK_dead_semivoiced_sound */ +{"dead_belowdot", 0xfe60}, /* XK_dead_belowdot */ +{"dead_hook", 0xfe61}, /* XK_dead_hook */ +{"dead_horn", 0xfe62}, /* XK_dead_horn */ + + + /* localized keys */ +{"BackApostrophe", 0xff21}, +{"Muhenkan", 0xff22}, +{"Katakana", 0xff27}, +{"Hankaku", 0xff29}, +{"Zenkaku_Hankaku", 0xff2a}, +{"Henkan_Mode_Real", 0xff23}, +{"Henkan_Mode_Ultra", 0xff3e}, +{"backslash_ja", 0xffa5}, +{"Katakana_Real", 0xff25}, +{"Eisu_toggle", 0xff30}, + +{NULL,0}, +}; diff -Nru qemu-kvm-0.12.5+noroms/ui/vnc-palette.c qemu-kvm-0.14.1/ui/vnc-palette.c --- qemu-kvm-0.12.5+noroms/ui/vnc-palette.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/ui/vnc-palette.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,136 @@ +/* + * QEMU VNC display driver: palette hash table + * + * From libvncserver/libvncserver/tight.c + * Copyright (C) 2000, 2001 Const Kaplinsky. All Rights Reserved. + * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. + * + * Copyright (C) 2010 Corentin Chary + * + * 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 "vnc-palette.h" + +static VncPaletteEntry *palette_find(const VncPalette *palette, + uint32_t color, unsigned int hash) +{ + VncPaletteEntry *entry; + + QLIST_FOREACH(entry, &palette->table[hash], next) { + if (entry->color == color) { + return entry; + } + } + + return NULL; +} + +static unsigned int palette_hash(uint32_t rgb, int bpp) +{ + if (bpp == 16) { + return ((unsigned int)(((rgb >> 8) + rgb) & 0xFF)); + } else { + return ((unsigned int)(((rgb >> 16) + (rgb >> 8)) & 0xFF)); + } +} + +VncPalette *palette_new(size_t max, int bpp) +{ + VncPalette *palette; + + palette = qemu_mallocz(sizeof(*palette)); + palette->max = max; + palette->bpp = bpp; + return palette; +} + +void palette_destroy(VncPalette *palette) +{ + int i; + + if (palette == NULL) { + return ; + } + + for (i = 0; i < VNC_PALETTE_HASH_SIZE; i++) { + VncPaletteEntry *entry = QLIST_FIRST(&palette->table[i]); + while (entry) { + VncPaletteEntry *tmp = QLIST_NEXT(entry, next); + QLIST_REMOVE(entry, next); + qemu_free(entry); + entry = tmp; + } + } + + qemu_free(palette); +} + +int palette_put(VncPalette *palette, uint32_t color) +{ + unsigned int hash; + unsigned int idx = palette->size; + VncPaletteEntry *entry; + + hash = palette_hash(color, palette->bpp) % VNC_PALETTE_HASH_SIZE; + entry = palette_find(palette, color, hash); + + if (!entry && palette->size >= palette->max) { + return 0; + } + if (!entry) { + VncPaletteEntry *entry; + + entry = qemu_mallocz(sizeof(*entry)); + entry->color = color; + entry->idx = idx; + QLIST_INSERT_HEAD(&palette->table[hash], entry, next); + palette->size++; + } + return palette->size; +} + +int palette_idx(const VncPalette *palette, uint32_t color) +{ + VncPaletteEntry *entry; + unsigned int hash; + + hash = palette_hash(color, palette->bpp) % VNC_PALETTE_HASH_SIZE; + entry = palette_find(palette, color, hash); + return (entry == NULL ? -1 : entry->idx); +} + +size_t palette_size(const VncPalette *palette) +{ + return palette->size; +} + +void palette_iter(const VncPalette *palette, + void (*iter)(int idx, uint32_t color, void *opaque), + void *opaque) +{ + int i; + VncPaletteEntry *entry; + + for (i = 0; i < VNC_PALETTE_HASH_SIZE; i++) { + QLIST_FOREACH(entry, &palette->table[i], next) { + iter(entry->idx, entry->color, opaque); + } + } +} diff -Nru qemu-kvm-0.12.5+noroms/ui/vnc-palette.h qemu-kvm-0.14.1/ui/vnc-palette.h --- qemu-kvm-0.12.5+noroms/ui/vnc-palette.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/ui/vnc-palette.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,63 @@ +/* + * QEMU VNC display driver: palette hash table + * + * From libvncserver/libvncserver/tight.c + * Copyright (C) 2000, 2001 Const Kaplinsky. All Rights Reserved. + * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. + * + * Copyright (C) 2010 Corentin Chary + * + * 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. + */ + +#ifndef VNC_PALETTE_H +#define VNC_PALETTE_H + +#include "qlist.h" +#include "qemu-queue.h" +#include + +#define VNC_PALETTE_HASH_SIZE 256 + +typedef struct VncPaletteEntry { + int idx; + uint32_t color; + QLIST_ENTRY(VncPaletteEntry) next; +} VncPaletteEntry; + +typedef struct VncPalette { + QObject_HEAD; + size_t size; + size_t max; + int bpp; + QLIST_HEAD(,VncPaletteEntry) table[VNC_PALETTE_HASH_SIZE]; +} VncPalette; + +VncPalette *palette_new(size_t max, int bpp); +void palette_destroy(VncPalette *palette); + +int palette_put(VncPalette *palette, uint32_t color); +int palette_idx(const VncPalette *palette, uint32_t color); +size_t palette_size(const VncPalette *palette); + +void palette_iter(const VncPalette *palette, + void (*iter)(int idx, uint32_t color, void *opaque), + void *opaque); + +#endif /* VNC_PALETTE_H */ diff -Nru qemu-kvm-0.12.5+noroms/ui/vnc-tls.c qemu-kvm-0.14.1/ui/vnc-tls.c --- qemu-kvm-0.12.5+noroms/ui/vnc-tls.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/ui/vnc-tls.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,445 @@ +/* + * QEMU VNC display driver: TLS helpers + * + * Copyright (C) 2006 Anthony Liguori + * Copyright (C) 2006 Fabrice Bellard + * Copyright (C) 2009 Red Hat, Inc + * + * 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 "qemu-x509.h" +#include "vnc.h" +#include "qemu_socket.h" + +#if defined(_VNC_DEBUG) && _VNC_DEBUG >= 2 +/* Very verbose, so only enabled for _VNC_DEBUG >= 2 */ +static void vnc_debug_gnutls_log(int level, const char* str) { + VNC_DEBUG("%d %s", level, str); +} +#endif /* defined(_VNC_DEBUG) && _VNC_DEBUG >= 2 */ + + +#define DH_BITS 1024 +static gnutls_dh_params_t dh_params; + +static int vnc_tls_initialize(void) +{ + static int tlsinitialized = 0; + + if (tlsinitialized) + return 1; + + if (gnutls_global_init () < 0) + return 0; + + /* XXX ought to re-generate diffie-hellmen params periodically */ + if (gnutls_dh_params_init (&dh_params) < 0) + return 0; + if (gnutls_dh_params_generate2 (dh_params, DH_BITS) < 0) + return 0; + +#if defined(_VNC_DEBUG) && _VNC_DEBUG >= 2 + gnutls_global_set_log_level(10); + gnutls_global_set_log_function(vnc_debug_gnutls_log); +#endif + + tlsinitialized = 1; + + return 1; +} + +static ssize_t vnc_tls_push(gnutls_transport_ptr_t transport, + const void *data, + size_t len) { + struct VncState *vs = (struct VncState *)transport; + int ret; + + retry: + ret = send(vs->csock, data, len, 0); + if (ret < 0) { + if (errno == EINTR) + goto retry; + return -1; + } + return ret; +} + + +static ssize_t vnc_tls_pull(gnutls_transport_ptr_t transport, + void *data, + size_t len) { + struct VncState *vs = (struct VncState *)transport; + int ret; + + retry: + ret = recv(vs->csock, data, len, 0); + if (ret < 0) { + if (errno == EINTR) + goto retry; + return -1; + } + return ret; +} + + +static gnutls_anon_server_credentials vnc_tls_initialize_anon_cred(void) +{ + gnutls_anon_server_credentials anon_cred; + int ret; + + if ((ret = gnutls_anon_allocate_server_credentials(&anon_cred)) < 0) { + VNC_DEBUG("Cannot allocate credentials %s\n", gnutls_strerror(ret)); + return NULL; + } + + gnutls_anon_set_server_dh_params(anon_cred, dh_params); + + return anon_cred; +} + + +static gnutls_certificate_credentials_t vnc_tls_initialize_x509_cred(VncDisplay *vd) +{ + gnutls_certificate_credentials_t x509_cred; + int ret; + + if (!vd->tls.x509cacert) { + VNC_DEBUG("No CA x509 certificate specified\n"); + return NULL; + } + if (!vd->tls.x509cert) { + VNC_DEBUG("No server x509 certificate specified\n"); + return NULL; + } + if (!vd->tls.x509key) { + VNC_DEBUG("No server private key specified\n"); + return NULL; + } + + if ((ret = gnutls_certificate_allocate_credentials(&x509_cred)) < 0) { + VNC_DEBUG("Cannot allocate credentials %s\n", gnutls_strerror(ret)); + return NULL; + } + if ((ret = gnutls_certificate_set_x509_trust_file(x509_cred, + vd->tls.x509cacert, + GNUTLS_X509_FMT_PEM)) < 0) { + VNC_DEBUG("Cannot load CA certificate %s\n", gnutls_strerror(ret)); + gnutls_certificate_free_credentials(x509_cred); + return NULL; + } + + if ((ret = gnutls_certificate_set_x509_key_file (x509_cred, + vd->tls.x509cert, + vd->tls.x509key, + GNUTLS_X509_FMT_PEM)) < 0) { + VNC_DEBUG("Cannot load certificate & key %s\n", gnutls_strerror(ret)); + gnutls_certificate_free_credentials(x509_cred); + return NULL; + } + + if (vd->tls.x509cacrl) { + if ((ret = gnutls_certificate_set_x509_crl_file(x509_cred, + vd->tls.x509cacrl, + GNUTLS_X509_FMT_PEM)) < 0) { + VNC_DEBUG("Cannot load CRL %s\n", gnutls_strerror(ret)); + gnutls_certificate_free_credentials(x509_cred); + return NULL; + } + } + + gnutls_certificate_set_dh_params (x509_cred, dh_params); + + return x509_cred; +} + + +int vnc_tls_validate_certificate(struct VncState *vs) +{ + int ret; + unsigned int status; + const gnutls_datum_t *certs; + unsigned int nCerts, i; + time_t now; + + VNC_DEBUG("Validating client certificate\n"); + if ((ret = gnutls_certificate_verify_peers2 (vs->tls.session, &status)) < 0) { + VNC_DEBUG("Verify failed %s\n", gnutls_strerror(ret)); + return -1; + } + + if ((now = time(NULL)) == ((time_t)-1)) { + return -1; + } + + if (status != 0) { + if (status & GNUTLS_CERT_INVALID) + VNC_DEBUG("The certificate is not trusted.\n"); + + if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) + VNC_DEBUG("The certificate hasn't got a known issuer.\n"); + + if (status & GNUTLS_CERT_REVOKED) + VNC_DEBUG("The certificate has been revoked.\n"); + + if (status & GNUTLS_CERT_INSECURE_ALGORITHM) + VNC_DEBUG("The certificate uses an insecure algorithm\n"); + + return -1; + } else { + VNC_DEBUG("Certificate is valid!\n"); + } + + /* Only support x509 for now */ + if (gnutls_certificate_type_get(vs->tls.session) != GNUTLS_CRT_X509) + return -1; + + if (!(certs = gnutls_certificate_get_peers(vs->tls.session, &nCerts))) + return -1; + + for (i = 0 ; i < nCerts ; i++) { + gnutls_x509_crt_t cert; + VNC_DEBUG ("Checking certificate chain %d\n", i); + if (gnutls_x509_crt_init (&cert) < 0) + return -1; + + if (gnutls_x509_crt_import(cert, &certs[i], GNUTLS_X509_FMT_DER) < 0) { + gnutls_x509_crt_deinit (cert); + return -1; + } + + if (gnutls_x509_crt_get_expiration_time (cert) < now) { + VNC_DEBUG("The certificate has expired\n"); + gnutls_x509_crt_deinit (cert); + return -1; + } + + if (gnutls_x509_crt_get_activation_time (cert) > now) { + VNC_DEBUG("The certificate is not yet activated\n"); + gnutls_x509_crt_deinit (cert); + return -1; + } + + if (gnutls_x509_crt_get_activation_time (cert) > now) { + VNC_DEBUG("The certificate is not yet activated\n"); + gnutls_x509_crt_deinit (cert); + return -1; + } + + if (i == 0) { + size_t dnameSize = 1024; + vs->tls.dname = qemu_malloc(dnameSize); + requery: + if ((ret = gnutls_x509_crt_get_dn (cert, vs->tls.dname, &dnameSize)) != 0) { + if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) { + vs->tls.dname = qemu_realloc(vs->tls.dname, dnameSize); + goto requery; + } + gnutls_x509_crt_deinit (cert); + VNC_DEBUG("Cannot get client distinguished name: %s", + gnutls_strerror (ret)); + return -1; + } + + if (vs->vd->tls.x509verify) { + int allow; + if (!vs->vd->tls.acl) { + VNC_DEBUG("no ACL activated, allowing access"); + gnutls_x509_crt_deinit (cert); + continue; + } + + allow = qemu_acl_party_is_allowed(vs->vd->tls.acl, + vs->tls.dname); + + VNC_DEBUG("TLS x509 ACL check for %s is %s\n", + vs->tls.dname, allow ? "allowed" : "denied"); + if (!allow) { + gnutls_x509_crt_deinit (cert); + return -1; + } + } + } + + gnutls_x509_crt_deinit (cert); + } + + return 0; +} + + +int vnc_tls_client_setup(struct VncState *vs, + int needX509Creds) { + static const int cert_type_priority[] = { GNUTLS_CRT_X509, 0 }; + static const int protocol_priority[]= { GNUTLS_TLS1_1, GNUTLS_TLS1_0, GNUTLS_SSL3, 0 }; + static const int kx_anon[] = {GNUTLS_KX_ANON_DH, 0}; + static const int kx_x509[] = {GNUTLS_KX_DHE_DSS, GNUTLS_KX_RSA, GNUTLS_KX_DHE_RSA, GNUTLS_KX_SRP, 0}; + + VNC_DEBUG("Do TLS setup\n"); + if (vnc_tls_initialize() < 0) { + VNC_DEBUG("Failed to init TLS\n"); + vnc_client_error(vs); + return -1; + } + if (vs->tls.session == NULL) { + if (gnutls_init(&vs->tls.session, GNUTLS_SERVER) < 0) { + vnc_client_error(vs); + return -1; + } + + if (gnutls_set_default_priority(vs->tls.session) < 0) { + gnutls_deinit(vs->tls.session); + vs->tls.session = NULL; + vnc_client_error(vs); + return -1; + } + + if (gnutls_kx_set_priority(vs->tls.session, needX509Creds ? kx_x509 : kx_anon) < 0) { + gnutls_deinit(vs->tls.session); + vs->tls.session = NULL; + vnc_client_error(vs); + return -1; + } + + if (gnutls_certificate_type_set_priority(vs->tls.session, cert_type_priority) < 0) { + gnutls_deinit(vs->tls.session); + vs->tls.session = NULL; + vnc_client_error(vs); + return -1; + } + + if (gnutls_protocol_set_priority(vs->tls.session, protocol_priority) < 0) { + gnutls_deinit(vs->tls.session); + vs->tls.session = NULL; + vnc_client_error(vs); + return -1; + } + + if (needX509Creds) { + gnutls_certificate_server_credentials x509_cred = vnc_tls_initialize_x509_cred(vs->vd); + if (!x509_cred) { + gnutls_deinit(vs->tls.session); + vs->tls.session = NULL; + vnc_client_error(vs); + return -1; + } + if (gnutls_credentials_set(vs->tls.session, GNUTLS_CRD_CERTIFICATE, x509_cred) < 0) { + gnutls_deinit(vs->tls.session); + vs->tls.session = NULL; + gnutls_certificate_free_credentials(x509_cred); + vnc_client_error(vs); + return -1; + } + if (vs->vd->tls.x509verify) { + VNC_DEBUG("Requesting a client certificate\n"); + gnutls_certificate_server_set_request (vs->tls.session, GNUTLS_CERT_REQUEST); + } + + } else { + gnutls_anon_server_credentials anon_cred = vnc_tls_initialize_anon_cred(); + if (!anon_cred) { + gnutls_deinit(vs->tls.session); + vs->tls.session = NULL; + vnc_client_error(vs); + return -1; + } + if (gnutls_credentials_set(vs->tls.session, GNUTLS_CRD_ANON, anon_cred) < 0) { + gnutls_deinit(vs->tls.session); + vs->tls.session = NULL; + gnutls_anon_free_server_credentials(anon_cred); + vnc_client_error(vs); + return -1; + } + } + + gnutls_transport_set_ptr(vs->tls.session, (gnutls_transport_ptr_t)vs); + gnutls_transport_set_push_function(vs->tls.session, vnc_tls_push); + gnutls_transport_set_pull_function(vs->tls.session, vnc_tls_pull); + } + return 0; +} + + +void vnc_tls_client_cleanup(struct VncState *vs) +{ + if (vs->tls.session) { + gnutls_deinit(vs->tls.session); + vs->tls.session = NULL; + } + vs->tls.wiremode = VNC_WIREMODE_CLEAR; + free(vs->tls.dname); +} + + + +static int vnc_set_x509_credential(VncDisplay *vd, + const char *certdir, + const char *filename, + char **cred, + int ignoreMissing) +{ + struct stat sb; + + if (*cred) { + qemu_free(*cred); + *cred = NULL; + } + + *cred = qemu_malloc(strlen(certdir) + strlen(filename) + 2); + + strcpy(*cred, certdir); + strcat(*cred, "/"); + strcat(*cred, filename); + + VNC_DEBUG("Check %s\n", *cred); + if (stat(*cred, &sb) < 0) { + qemu_free(*cred); + *cred = NULL; + if (ignoreMissing && errno == ENOENT) + return 0; + return -1; + } + + return 0; +} + + +int vnc_tls_set_x509_creds_dir(VncDisplay *vd, + const char *certdir) +{ + if (vnc_set_x509_credential(vd, certdir, X509_CA_CERT_FILE, &vd->tls.x509cacert, 0) < 0) + goto cleanup; + if (vnc_set_x509_credential(vd, certdir, X509_CA_CRL_FILE, &vd->tls.x509cacrl, 1) < 0) + goto cleanup; + if (vnc_set_x509_credential(vd, certdir, X509_SERVER_CERT_FILE, &vd->tls.x509cert, 0) < 0) + goto cleanup; + if (vnc_set_x509_credential(vd, certdir, X509_SERVER_KEY_FILE, &vd->tls.x509key, 0) < 0) + goto cleanup; + + return 0; + + cleanup: + qemu_free(vd->tls.x509cacert); + qemu_free(vd->tls.x509cacrl); + qemu_free(vd->tls.x509cert); + qemu_free(vd->tls.x509key); + vd->tls.x509cacert = vd->tls.x509cacrl = vd->tls.x509cert = vd->tls.x509key = NULL; + return -1; +} + diff -Nru qemu-kvm-0.12.5+noroms/ui/vnc-tls.h qemu-kvm-0.14.1/ui/vnc-tls.h --- qemu-kvm-0.12.5+noroms/ui/vnc-tls.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/ui/vnc-tls.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,76 @@ +/* + * QEMU VNC display driver. TLS helpers + * + * Copyright (C) 2006 Anthony Liguori + * Copyright (C) 2006 Fabrice Bellard + * Copyright (C) 2009 Red Hat, Inc + * + * 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. + */ + + +#ifndef __QEMU_VNC_TLS_H__ +#define __QEMU_VNC_TLS_H__ + +#include +#include + +#include "acl.h" + +enum { + VNC_WIREMODE_CLEAR, + VNC_WIREMODE_TLS, +}; + +typedef struct VncDisplayTLS VncDisplayTLS; +typedef struct VncStateTLS VncStateTLS; + +/* Server state */ +struct VncDisplayTLS { + int x509verify; /* Non-zero if server requests & validates client cert */ + qemu_acl *acl; + + /* Paths to x509 certs/keys */ + char *x509cacert; + char *x509cacrl; + char *x509cert; + char *x509key; +}; + +/* Per client state */ +struct VncStateTLS { + /* Whether data is being TLS encrypted yet */ + int wiremode; + gnutls_session_t session; + + /* Client's Distinguished Name from the x509 cert */ + char *dname; +}; + +int vnc_tls_client_setup(VncState *vs, int x509Creds); +void vnc_tls_client_cleanup(VncState *vs); + +int vnc_tls_validate_certificate(VncState *vs); + +int vnc_tls_set_x509_creds_dir(VncDisplay *vd, + const char *path); + + +#endif /* __QEMU_VNC_TLS_H__ */ + diff -Nru qemu-kvm-0.12.5+noroms/ui/x_keymap.c qemu-kvm-0.14.1/ui/x_keymap.c --- qemu-kvm-0.12.5+noroms/ui/x_keymap.c 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/ui/x_keymap.c 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,168 @@ +/* + * QEMU SDL display driver + * + * Copyright (c) 2003 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 "qemu-common.h" +#include "x_keymap.h" + +static const uint8_t x_keycode_to_pc_keycode[115] = { + 0xc7, /* 97 Home */ + 0xc8, /* 98 Up */ + 0xc9, /* 99 PgUp */ + 0xcb, /* 100 Left */ + 0x4c, /* 101 KP-5 */ + 0xcd, /* 102 Right */ + 0xcf, /* 103 End */ + 0xd0, /* 104 Down */ + 0xd1, /* 105 PgDn */ + 0xd2, /* 106 Ins */ + 0xd3, /* 107 Del */ + 0x9c, /* 108 Enter */ + 0x9d, /* 109 Ctrl-R */ + 0x0, /* 110 Pause */ + 0xb7, /* 111 Print */ + 0xb5, /* 112 Divide */ + 0xb8, /* 113 Alt-R */ + 0xc6, /* 114 Break */ + 0x0, /* 115 */ + 0x0, /* 116 */ + 0x0, /* 117 */ + 0x0, /* 118 */ + 0x0, /* 119 */ + 0x0, /* 120 */ + 0x0, /* 121 */ + 0x0, /* 122 */ + 0x0, /* 123 */ + 0x0, /* 124 */ + 0x0, /* 125 */ + 0x0, /* 126 */ + 0x0, /* 127 */ + 0x0, /* 128 */ + 0x79, /* 129 Henkan */ + 0x0, /* 130 */ + 0x7b, /* 131 Muhenkan */ + 0x0, /* 132 */ + 0x7d, /* 133 Yen */ + 0x0, /* 134 */ + 0x0, /* 135 */ + 0x47, /* 136 KP_7 */ + 0x48, /* 137 KP_8 */ + 0x49, /* 138 KP_9 */ + 0x4b, /* 139 KP_4 */ + 0x4c, /* 140 KP_5 */ + 0x4d, /* 141 KP_6 */ + 0x4f, /* 142 KP_1 */ + 0x50, /* 143 KP_2 */ + 0x51, /* 144 KP_3 */ + 0x52, /* 145 KP_0 */ + 0x53, /* 146 KP_. */ + 0x47, /* 147 KP_HOME */ + 0x48, /* 148 KP_UP */ + 0x49, /* 149 KP_PgUp */ + 0x4b, /* 150 KP_Left */ + 0x4c, /* 151 KP_ */ + 0x4d, /* 152 KP_Right */ + 0x4f, /* 153 KP_End */ + 0x50, /* 154 KP_Down */ + 0x51, /* 155 KP_PgDn */ + 0x52, /* 156 KP_Ins */ + 0x53, /* 157 KP_Del */ +}; + +/* This table is generated based off the xfree86 -> scancode mapping above + * and the keycode mappings in /usr/share/X11/xkb/keycodes/evdev + * and /usr/share/X11/xkb/keycodes/xfree86 + */ + +static const uint8_t evdev_keycode_to_pc_keycode[61] = { + 0, /* 97 EVDEV - RO ("Internet" Keyboards) */ + 0, /* 98 EVDEV - KATA (Katakana) */ + 0, /* 99 EVDEV - HIRA (Hiragana) */ + 0x79, /* 100 EVDEV - HENK (Henkan) */ + 0x70, /* 101 EVDEV - HKTG (Hiragana/Katakana toggle) */ + 0x7b, /* 102 EVDEV - MUHE (Muhenkan) */ + 0, /* 103 EVDEV - JPCM (KPJPComma) */ + 0x9c, /* 104 KPEN */ + 0x9d, /* 105 RCTL */ + 0xb5, /* 106 KPDV */ + 0xb7, /* 107 PRSC */ + 0xb8, /* 108 RALT */ + 0, /* 109 EVDEV - LNFD ("Internet" Keyboards) */ + 0xc7, /* 110 HOME */ + 0xc8, /* 111 UP */ + 0xc9, /* 112 PGUP */ + 0xcb, /* 113 LEFT */ + 0xcd, /* 114 RGHT */ + 0xcf, /* 115 END */ + 0xd0, /* 116 DOWN */ + 0xd1, /* 117 PGDN */ + 0xd2, /* 118 INS */ + 0xd3, /* 119 DELE */ + 0, /* 120 EVDEV - I120 ("Internet" Keyboards) */ + 0, /* 121 EVDEV - MUTE */ + 0, /* 122 EVDEV - VOL- */ + 0, /* 123 EVDEV - VOL+ */ + 0, /* 124 EVDEV - POWR */ + 0, /* 125 EVDEV - KPEQ */ + 0, /* 126 EVDEV - I126 ("Internet" Keyboards) */ + 0, /* 127 EVDEV - PAUS */ + 0, /* 128 EVDEV - ???? */ + 0, /* 129 EVDEV - I129 ("Internet" Keyboards) */ + 0xf1, /* 130 EVDEV - HNGL (Korean Hangul Latin toggle) */ + 0xf2, /* 131 EVDEV - HJCV (Korean Hangul Hanja toggle) */ + 0x7d, /* 132 AE13 (Yen)*/ + 0xdb, /* 133 EVDEV - LWIN */ + 0xdc, /* 134 EVDEV - RWIN */ + 0xdd, /* 135 EVDEV - MENU */ + 0, /* 136 EVDEV - STOP */ + 0, /* 137 EVDEV - AGAI */ + 0, /* 138 EVDEV - PROP */ + 0, /* 139 EVDEV - UNDO */ + 0, /* 140 EVDEV - FRNT */ + 0, /* 141 EVDEV - COPY */ + 0, /* 142 EVDEV - OPEN */ + 0, /* 143 EVDEV - PAST */ + 0, /* 144 EVDEV - FIND */ + 0, /* 145 EVDEV - CUT */ + 0, /* 146 EVDEV - HELP */ + 0, /* 147 EVDEV - I147 */ + 0, /* 148 EVDEV - I148 */ + 0, /* 149 EVDEV - I149 */ + 0, /* 150 EVDEV - I150 */ + 0, /* 151 EVDEV - I151 */ + 0, /* 152 EVDEV - I152 */ + 0, /* 153 EVDEV - I153 */ + 0, /* 154 EVDEV - I154 */ + 0, /* 155 EVDEV - I156 */ + 0, /* 156 EVDEV - I157 */ + 0, /* 157 EVDEV - I158 */ +}; + +uint8_t translate_xfree86_keycode(const int key) +{ + return x_keycode_to_pc_keycode[key]; +} + +uint8_t translate_evdev_keycode(const int key) +{ + return evdev_keycode_to_pc_keycode[key]; +} diff -Nru qemu-kvm-0.12.5+noroms/ui/x_keymap.h qemu-kvm-0.14.1/ui/x_keymap.h --- qemu-kvm-0.12.5+noroms/ui/x_keymap.h 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/ui/x_keymap.h 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,32 @@ +/* + * QEMU SDL display driver + * + * Copyright (c) 2003 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. + */ + +#ifndef QEMU_X_KEYMAP_H +#define QEMU_X_KEYMAP_H + +uint8_t translate_xfree86_keycode(const int key); + +uint8_t translate_evdev_keycode(const int key); + +#endif diff -Nru qemu-kvm-0.12.5+noroms/usb-bsd.c qemu-kvm-0.14.1/usb-bsd.c --- qemu-kvm-0.12.5+noroms/usb-bsd.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/usb-bsd.c 2011-05-11 13:29:46.000000000 +0000 @@ -306,14 +306,15 @@ { struct usb_device_info bus_info, dev_info; USBDevice *d = NULL; - USBHostDevice *dev; + USBHostDevice *dev, *ret = NULL; char ctlpath[PATH_MAX + 1]; char buspath[PATH_MAX + 1]; int bfd, dfd, bus, address, i; int ugendebug = UGEN_DEBUG_LEVEL; - if (usb_host_find_device(&bus, &address, devname) < 0) - return NULL; + if (usb_host_find_device(&bus, &address, devname) < 0) { + goto fail; + } snprintf(buspath, PATH_MAX, "/dev/usb%d", bus); @@ -323,7 +324,7 @@ printf("usb_host_device_open: failed to open usb bus - %s\n", strerror(errno)); #endif - return NULL; + goto fail; } bus_info.udi_addr = address; @@ -332,7 +333,7 @@ printf("usb_host_device_open: failed to grab bus information - %s\n", strerror(errno)); #endif - return NULL; + goto fail_bfd; } #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) @@ -350,46 +351,52 @@ ctlpath, strerror(errno)); #endif } + goto fail_dfd; } - if (dfd >= 0) { - if (ioctl(dfd, USB_GET_DEVICEINFO, &dev_info) < 0) { + if (ioctl(dfd, USB_GET_DEVICEINFO, &dev_info) < 0) { #ifdef DEBUG - printf("usb_host_device_open: failed to grab device info - %s\n", - strerror(errno)); + printf("usb_host_device_open: failed to grab device info - %s\n", + strerror(errno)); #endif - goto fail; - } + goto fail_dfd; + } + + d = usb_create(NULL /* FIXME */, "usb-host"); + dev = DO_UPCAST(USBHostDevice, dev, d); - d = usb_create(NULL /* FIXME */, "usb-host"); - dev = DO_UPCAST(USBHostDevice, dev, d); + if (dev_info.udi_speed == 1) { + dev->dev.speed = USB_SPEED_LOW - 1; + } else { + dev->dev.speed = USB_SPEED_FULL - 1; + } - if (dev_info.udi_speed == 1) - dev->dev.speed = USB_SPEED_LOW - 1; - else - dev->dev.speed = USB_SPEED_FULL - 1; - - if (strncmp(dev_info.udi_product, "product", 7) != 0) - pstrcpy(dev->dev.product_desc, sizeof(dev->dev.product_desc), - dev_info.udi_product); - else - snprintf(dev->dev.product_desc, sizeof(dev->dev.product_desc), - "host:%s", devname); - - pstrcpy(dev->devpath, sizeof(dev->devpath), "/dev/"); - pstrcat(dev->devpath, sizeof(dev->devpath), dev_info.udi_devnames[0]); - - /* Mark the endpoints as not yet open */ - for (i = 0; i < USB_MAX_ENDPOINTS; i++) - dev->ep_fd[i] = -1; + if (strncmp(dev_info.udi_product, "product", 7) != 0) { + pstrcpy(dev->dev.product_desc, sizeof(dev->dev.product_desc), + dev_info.udi_product); + } else { + snprintf(dev->dev.product_desc, sizeof(dev->dev.product_desc), + "host:%s", devname); + } - ioctl(dfd, USB_SETDEBUG, &ugendebug); + pstrcpy(dev->devpath, sizeof(dev->devpath), "/dev/"); + pstrcat(dev->devpath, sizeof(dev->devpath), dev_info.udi_devnames[0]); - return (USBDevice *)dev; + /* Mark the endpoints as not yet open */ + for (i = 0; i < USB_MAX_ENDPOINTS; i++) { + dev->ep_fd[i] = -1; } + ioctl(dfd, USB_SETDEBUG, &ugendebug); + + ret = (USBDevice *)dev; + +fail_dfd: + close(dfd); +fail_bfd: + close(bfd); fail: - return NULL; + return ret; } static struct USBDeviceInfo usb_host_dev_info = { diff -Nru qemu-kvm-0.12.5+noroms/usb-linux.c qemu-kvm-0.14.1/usb-linux.c --- qemu-kvm-0.12.5+noroms/usb-linux.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/usb-linux.c 2011-05-11 13:29:46.000000000 +0000 @@ -33,6 +33,7 @@ #include "qemu-common.h" #include "qemu-timer.h" #include "monitor.h" +#include "sysemu.h" #include #include @@ -61,16 +62,16 @@ uint16_t wLength; }; -typedef int USBScanFunc(void *opaque, int bus_num, int addr, int class_id, - int vendor_id, int product_id, +typedef int USBScanFunc(void *opaque, int bus_num, int addr, int devpath, + int class_id, int vendor_id, int product_id, const char *product_name, int speed); //#define DEBUG #ifdef DEBUG -#define dprintf printf +#define DPRINTF printf #else -#define dprintf(...) +#define DPRINTF(...) #endif #define USBDBG_DEVOPENED "husb: opened %s/devices\n" @@ -105,15 +106,15 @@ /* * Control transfer state. - * Note that 'buffer' _must_ follow 'req' field because + * Note that 'buffer' _must_ follow 'req' field because * we need contigious buffer when we submit control URB. - */ + */ struct ctrl_struct { uint16_t len; uint16_t offset; uint8_t state; struct usb_ctrlrequest req; - uint8_t buffer[2048]; + uint8_t buffer[8192]; }; struct USBAutoFilter { @@ -132,6 +133,7 @@ int configuration; int ninterfaces; int closing; + Notifier exit; struct ctrl_struct ctrl; struct endp_data endp_table[MAX_ENDPOINTS]; @@ -139,6 +141,7 @@ /* Host side address */ int bus_num; int addr; + int devpath; struct USBAutoFilter match; QTAILQ_ENTRY(USBHostDevice) next; @@ -149,6 +152,8 @@ static int usb_host_close(USBHostDevice *dev); static int parse_filter(const char *spec, struct USBAutoFilter *f); static void usb_host_auto_check(void *unused); +static int usb_host_read_file(char *line, size_t line_size, + const char *device_file, const char *device_name); static int is_isoc(USBHostDevice *s, int ep) { @@ -170,10 +175,10 @@ s->endp_table[ep - 1].halted = 1; } -/* +/* * Async URB state. * We always allocate one isoc descriptor even for bulk transfers - * to simplify allocation and casts. + * to simplify allocation and casts. */ typedef struct AsyncURB { @@ -220,41 +225,43 @@ AsyncURB *aurb; while (1) { - USBPacket *p; + USBPacket *p; - int r = ioctl(s->fd, USBDEVFS_REAPURBNDELAY, &aurb); + int r = ioctl(s->fd, USBDEVFS_REAPURBNDELAY, &aurb); if (r < 0) { - if (errno == EAGAIN) + if (errno == EAGAIN) { return; - + } if (errno == ENODEV && !s->closing) { - printf("husb: device %d.%d disconnected\n", s->bus_num, s->addr); + printf("husb: device %d.%d disconnected\n", + s->bus_num, s->addr); usb_host_close(s); usb_host_auto_check(NULL); return; } - dprintf("husb: async. reap urb failed errno %d\n", errno); + DPRINTF("husb: async. reap urb failed errno %d\n", errno); return; } p = aurb->packet; - dprintf("husb: async completed. aurb %p status %d alen %d\n", + DPRINTF("husb: async completed. aurb %p status %d alen %d\n", aurb, aurb->urb.status, aurb->urb.actual_length); - if (p) { + if (p) { switch (aurb->urb.status) { case 0: p->len = aurb->urb.actual_length; - if (aurb->urb.type == USBDEVFS_URB_TYPE_CONTROL) + if (aurb->urb.type == USBDEVFS_URB_TYPE_CONTROL) { async_complete_ctrl(s, p); + } break; case -EPIPE: set_halt(s, p->devep); - p->len = USB_RET_STALL; - break; + p->len = USB_RET_STALL; + break; default: p->len = USB_RET_NAK; @@ -262,7 +269,7 @@ } usb_packet_complete(p); - } + } async_free(aurb); } @@ -273,37 +280,38 @@ AsyncURB *aurb = opaque; USBHostDevice *s = aurb->hdev; - dprintf("husb: async cancel. aurb %p\n", aurb); + DPRINTF("husb: async cancel. aurb %p\n", aurb); /* Mark it as dead (see async_complete above) */ aurb->packet = NULL; int r = ioctl(s->fd, USBDEVFS_DISCARDURB, aurb); if (r < 0) { - dprintf("husb: async. discard urb failed errno %d\n", errno); + DPRINTF("husb: async. discard urb failed errno %d\n", errno); } } static int usb_host_claim_interfaces(USBHostDevice *dev, int configuration) { int dev_descr_len, config_descr_len; - int interface, nb_interfaces, nb_configurations; + int interface, nb_interfaces; int ret, i; if (configuration == 0) /* address state - ignore */ return 1; - dprintf("husb: claiming interfaces. config %d\n", configuration); + DPRINTF("husb: claiming interfaces. config %d\n", configuration); i = 0; dev_descr_len = dev->descr[0]; - if (dev_descr_len > dev->descr_len) + if (dev_descr_len > dev->descr_len) { goto fail; - nb_configurations = dev->descr[17]; + } i += dev_descr_len; while (i < dev->descr_len) { - dprintf("husb: i is %d, descr_len is %d, dl %d, dt %d\n", i, dev->descr_len, + DPRINTF("husb: i is %d, descr_len is %d, dl %d, dt %d\n", + i, dev->descr_len, dev->descr[i], dev->descr[i+1]); if (dev->descr[i+1] != USB_DT_CONFIG) { @@ -312,7 +320,7 @@ } config_descr_len = dev->descr[i]; - printf("husb: config #%d need %d\n", dev->descr[i + 5], configuration); + printf("husb: config #%d need %d\n", dev->descr[i + 5], configuration); if (configuration < 0 || configuration == dev->descr[i + 5]) { configuration = dev->descr[i + 5]; @@ -323,7 +331,8 @@ } if (i >= dev->descr_len) { - fprintf(stderr, "husb: update iface failed. no matching configuration\n"); + fprintf(stderr, + "husb: update iface failed. no matching configuration\n"); goto fail; } nb_interfaces = dev->descr[i + 4]; @@ -370,7 +379,7 @@ { int ret, i; - dprintf("husb: releasing interfaces\n"); + DPRINTF("husb: releasing interfaces\n"); for (i = 0; i < s->ninterfaces; i++) { ret = ioctl(s->fd, USBDEVFS_RELEASEINTERFACE, &i); @@ -387,7 +396,7 @@ { USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev); - dprintf("husb: reset device %u.%u\n", s->bus_num, s->addr); + DPRINTF("husb: reset device %u.%u\n", s->bus_num, s->addr); ioctl(s->fd, USBDEVFS_RESET); @@ -400,6 +409,7 @@ usb_host_close(s); QTAILQ_REMOVE(&hostdevs, s, next); + qemu_remove_exit_notifier(&s->exit); } static int usb_linux_update_endp_table(USBHostDevice *s); @@ -416,15 +426,16 @@ urb = &aurb->urb; - if (p->pid == USB_TOKEN_IN) - urb->endpoint = p->devep | 0x80; - else - urb->endpoint = p->devep; + if (p->pid == USB_TOKEN_IN) { + urb->endpoint = p->devep | 0x80; + } else { + urb->endpoint = p->devep; + } if (is_halted(s, p->devep)) { - ret = ioctl(s->fd, USBDEVFS_CLEAR_HALT, &urb->endpoint); + ret = ioctl(s->fd, USBDEVFS_CLEAR_HALT, &urb->endpoint); if (ret < 0) { - dprintf("husb: failed to clear halt. ep 0x%x errno %d\n", + DPRINTF("husb: failed to clear halt. ep 0x%x errno %d\n", urb->endpoint, errno); return USB_RET_NAK; } @@ -449,10 +460,11 @@ ret = ioctl(s->fd, USBDEVFS_SUBMITURB, urb); - dprintf("husb: data submit. ep 0x%x len %u aurb %p\n", urb->endpoint, p->len, aurb); + DPRINTF("husb: data submit. ep 0x%x len %u aurb %p\n", + urb->endpoint, p->len, aurb); if (ret < 0) { - dprintf("husb: submit failed. errno %d\n", errno); + DPRINTF("husb: submit failed. errno %d\n", errno); async_free(aurb); switch(errno) { @@ -470,15 +482,16 @@ static int ctrl_error(void) { - if (errno == ETIMEDOUT) + if (errno == ETIMEDOUT) { return USB_RET_NAK; - else + } else { return USB_RET_STALL; + } } static int usb_host_set_address(USBHostDevice *s, int addr) { - dprintf("husb: ctrl set addr %u\n", addr); + DPRINTF("husb: ctrl set addr %u\n", addr); s->dev.addr = addr; return 0; } @@ -488,12 +501,12 @@ usb_host_release_interfaces(s); int ret = ioctl(s->fd, USBDEVFS_SETCONFIGURATION, &config); - - dprintf("husb: ctrl set config %d ret %d errno %d\n", config, ret, errno); - - if (ret < 0) + + DPRINTF("husb: ctrl set config %d ret %d errno %d\n", config, ret, errno); + + if (ret < 0) { return ctrl_error(); - + } usb_host_claim_interfaces(s, config); return 0; } @@ -506,13 +519,13 @@ si.interface = iface; si.altsetting = alt; ret = ioctl(s->fd, USBDEVFS_SETINTERFACE, &si); - - dprintf("husb: ctrl set iface %d altset %d ret %d errno %d\n", - iface, alt, ret, errno); - - if (ret < 0) - return ctrl_error(); + DPRINTF("husb: ctrl set iface %d altset %d ret %d errno %d\n", + iface, alt, ret, errno); + + if (ret < 0) { + return ctrl_error(); + } usb_linux_update_endp_table(s); return 0; } @@ -524,16 +537,16 @@ int ret, value, index; int buffer_len; - /* + /* * Process certain standard device requests. * These are infrequent and are processed synchronously. */ value = le16_to_cpu(s->ctrl.req.wValue); index = le16_to_cpu(s->ctrl.req.wIndex); - dprintf("husb: ctrl type 0x%x req 0x%x val 0x%x index %u len %u\n", - s->ctrl.req.bRequestType, s->ctrl.req.bRequest, value, index, - s->ctrl.len); + DPRINTF("husb: ctrl type 0x%x req 0x%x val 0x%x index %u len %u\n", + s->ctrl.req.bRequestType, s->ctrl.req.bRequest, value, index, + s->ctrl.len); if (s->ctrl.req.bRequestType == 0) { switch (s->ctrl.req.bRequest) { @@ -546,8 +559,9 @@ } if (s->ctrl.req.bRequestType == 1 && - s->ctrl.req.bRequest == USB_REQ_SET_INTERFACE) + s->ctrl.req.bRequest == USB_REQ_SET_INTERFACE) { return usb_host_set_interface(s, index, value); + } /* The rest are asynchronous */ @@ -562,12 +576,12 @@ aurb->hdev = s; aurb->packet = p; - /* + /* * Setup ctrl transfer. * * s->ctrl is layed out such that data buffer immediately follows * 'req' struct which is exactly what usbdevfs expects. - */ + */ urb = &aurb->urb; urb->type = USBDEVFS_URB_TYPE_CONTROL; @@ -580,10 +594,10 @@ ret = ioctl(s->fd, USBDEVFS_SUBMITURB, urb); - dprintf("husb: submit ctrl. len %u aurb %p\n", urb->buffer_length, aurb); + DPRINTF("husb: submit ctrl. len %u aurb %p\n", urb->buffer_length, aurb); if (ret < 0) { - dprintf("husb: submit failed. errno %d\n", errno); + DPRINTF("husb: submit failed. errno %d\n", errno); async_free(aurb); switch(errno) { @@ -604,9 +618,10 @@ USBHostDevice *s = (USBHostDevice *) dev; int ret = 0; - if (p->len != 8) + if (p->len != 8) { return USB_RET_STALL; - + } + memcpy(&s->ctrl.req, p->data, 8); s->ctrl.len = le16_to_cpu(s->ctrl.req.wLength); s->ctrl.offset = 0; @@ -614,17 +629,20 @@ if (s->ctrl.req.bRequestType & USB_DIR_IN) { ret = usb_host_handle_control(s, p); - if (ret < 0) + if (ret < 0) { return ret; + } - if (ret < s->ctrl.len) + if (ret < s->ctrl.len) { s->ctrl.len = ret; + } s->ctrl.state = CTRL_STATE_DATA; } else { - if (s->ctrl.len == 0) + if (s->ctrl.len == 0) { s->ctrl.state = CTRL_STATE_ACK; - else + } else { s->ctrl.state = CTRL_STATE_DATA; + } } return ret; @@ -635,16 +653,17 @@ USBHostDevice *s = (USBHostDevice *) dev; int ret = 0; - if (p->devep != 0) + if (p->devep != 0) { return usb_host_handle_data(s, p); + } switch(s->ctrl.state) { case CTRL_STATE_ACK: if (!(s->ctrl.req.bRequestType & USB_DIR_IN)) { ret = usb_host_handle_control(s, p); - if (ret == USB_RET_ASYNC) + if (ret == USB_RET_ASYNC) { return USB_RET_ASYNC; - + } s->ctrl.state = CTRL_STATE_IDLE; return ret > 0 ? 0 : ret; } @@ -654,12 +673,14 @@ case CTRL_STATE_DATA: if (s->ctrl.req.bRequestType & USB_DIR_IN) { int len = s->ctrl.len - s->ctrl.offset; - if (len > p->len) + if (len > p->len) { len = p->len; + } memcpy(p->data, s->ctrl.buffer + s->ctrl.offset, len); s->ctrl.offset += len; - if (s->ctrl.offset >= s->ctrl.len) + if (s->ctrl.offset >= s->ctrl.len) { s->ctrl.state = CTRL_STATE_ACK; + } return len; } @@ -675,8 +696,9 @@ { USBHostDevice *s = (USBHostDevice *) dev; - if (p->devep != 0) + if (p->devep != 0) { return usb_host_handle_data(s, p); + } switch(s->ctrl.state) { case CTRL_STATE_ACK: @@ -691,12 +713,14 @@ case CTRL_STATE_DATA: if (!(s->ctrl.req.bRequestType & USB_DIR_IN)) { int len = s->ctrl.len - s->ctrl.offset; - if (len > p->len) + if (len > p->len) { len = p->len; + } memcpy(s->ctrl.buffer + s->ctrl.offset, p->data, len); s->ctrl.offset += len; - if (s->ctrl.offset >= s->ctrl.len) + if (s->ctrl.offset >= s->ctrl.len) { s->ctrl.state = CTRL_STATE_ACK; + } return len; } @@ -734,8 +758,9 @@ } /* Rest of the PIDs must match our address */ - if (s->state < USB_STATE_DEFAULT || p->devaddr != s->addr) + if (s->state < USB_STATE_DEFAULT || p->devaddr != s->addr) { return USB_RET_NODEV; + } switch (p->pid) { case USB_TOKEN_SETUP: @@ -746,20 +771,35 @@ case USB_TOKEN_OUT: return do_token_out(s, p); - + default: return USB_RET_STALL; } } -/* returns 1 on problem encountered or 0 for success */ -static int usb_linux_update_endp_table(USBHostDevice *s) +static int usb_linux_get_configuration(USBHostDevice *s) { - uint8_t *descriptors; - uint8_t devep, type, configuration, alt_interface; + uint8_t configuration; struct usb_ctrltransfer ct; - int interface, ret, length, i; + int ret; + + if (usb_fs_type == USB_FS_SYS) { + char device_name[32], line[1024]; + int configuration; + + sprintf(device_name, "%d-%d", s->bus_num, s->devpath); + if (!usb_host_read_file(line, sizeof(line), "bConfigurationValue", + device_name)) { + goto usbdevfs; + } + if (sscanf(line, "%d", &configuration) != 1) { + goto usbdevfs; + } + return configuration; + } + +usbdevfs: ct.bRequestType = USB_DIR_IN; ct.bRequest = USB_REQ_GET_CONFIGURATION; ct.wValue = 0; @@ -770,13 +810,30 @@ ret = ioctl(s->fd, USBDEVFS_CONTROL, &ct); if (ret < 0) { - perror("usb_linux_update_endp_table"); - return 1; + perror("usb_linux_get_configuration"); + return -1; } /* in address state */ - if (configuration == 0) + if (configuration == 0) { + return -1; + } + + return configuration; +} + +/* returns 1 on problem encountered or 0 for success */ +static int usb_linux_update_endp_table(USBHostDevice *s) +{ + uint8_t *descriptors; + uint8_t devep, type, configuration, alt_interface; + struct usb_ctrltransfer ct; + int interface, ret, length, i; + + i = usb_linux_get_configuration(s); + if (i < 0) return 1; + configuration = i; /* get the desired configuration, interface, and endpoint descriptors * from device description */ @@ -786,7 +843,7 @@ if (descriptors[i + 1] != USB_DT_CONFIG || descriptors[i + 5] != configuration) { - dprintf("invalid descriptor data - configuration\n"); + DPRINTF("invalid descriptor data - configuration\n"); return 1; } i += descriptors[i]; @@ -822,15 +879,17 @@ } /* advance to the endpoints */ - while (i < length && descriptors[i +1] != USB_DT_ENDPOINT) + while (i < length && descriptors[i +1] != USB_DT_ENDPOINT) { i += descriptors[i]; + } if (i >= length) break; while (i < length) { - if (descriptors[i + 1] != USB_DT_ENDPOINT) + if (descriptors[i + 1] != USB_DT_ENDPOINT) { break; + } devep = descriptors[i + 2]; switch (descriptors[i + 3] & 0x3) { @@ -847,7 +906,7 @@ type = USBDEVFS_URB_TYPE_INTERRUPT; break; default: - dprintf("usb_host: malformed endpoint type\n"); + DPRINTF("usb_host: malformed endpoint type\n"); type = USBDEVFS_URB_TYPE_BULK; } s->endp_table[(devep & 0xf) - 1].type = type; @@ -860,15 +919,15 @@ } static int usb_host_open(USBHostDevice *dev, int bus_num, - int addr, const char *prod_name) + int addr, int devpath, const char *prod_name) { int fd = -1, ret; struct usbdevfs_connectinfo ci; char buf[1024]; - if (dev->fd != -1) + if (dev->fd != -1) { goto fail; - + } printf("husb: open device %d.%d\n", bus_num, addr); if (!usb_host_device_path) { @@ -882,10 +941,11 @@ perror(buf); goto fail; } - dprintf("husb: opened %s\n", buf); + DPRINTF("husb: opened %s\n", buf); dev->bus_num = bus_num; dev->addr = addr; + dev->devpath = devpath; dev->fd = fd; /* read the device description */ @@ -899,21 +959,23 @@ { int x; printf("=== begin dumping device descriptor data ===\n"); - for (x = 0; x < dev->descr_len; x++) + for (x = 0; x < dev->descr_len; x++) { printf("%02x ", dev->descr[x]); + } printf("\n=== end dumping device descriptor data ===\n"); } #endif - /* - * Initial configuration is -1 which makes us claim first + /* + * Initial configuration is -1 which makes us claim first * available config. We used to start with 1, which does not - * always work. I've seen devices where first config starts + * always work. I've seen devices where first config starts * with 2. */ - if (!usb_host_claim_interfaces(dev, -1)) + if (!usb_host_claim_interfaces(dev, -1)) { goto fail; + } ret = ioctl(fd, USBDEVFS_CONNECTINFO, &ci); if (ret < 0) { @@ -924,20 +986,23 @@ printf("husb: grabbed usb device %d.%d\n", bus_num, addr); ret = usb_linux_update_endp_table(dev); - if (ret) + if (ret) { goto fail; + } - if (ci.slow) + if (ci.slow) { dev->dev.speed = USB_SPEED_LOW; - else + } else { dev->dev.speed = USB_SPEED_HIGH; + } - if (!prod_name || prod_name[0] == '\0') + if (!prod_name || prod_name[0] == '\0') { snprintf(dev->dev.product_desc, sizeof(dev->dev.product_desc), "host:%d.%d", bus_num, addr); - else + } else { pstrcpy(dev->dev.product_desc, sizeof(dev->dev.product_desc), prod_name); + } /* USB devio uses 'write' flag to check for async completions */ qemu_set_fd_handler(dev->fd, NULL, async_complete, dev); @@ -947,26 +1012,38 @@ fail: dev->fd = -1; - if (fd != -1) + if (fd != -1) { close(fd); + } return -1; } static int usb_host_close(USBHostDevice *dev) { - if (dev->fd == -1) + if (dev->fd == -1) { return -1; + } qemu_set_fd_handler(dev->fd, NULL, NULL, NULL); dev->closing = 1; async_complete(dev); dev->closing = 0; usb_device_detach(&dev->dev); + ioctl(dev->fd, USBDEVFS_RESET); close(dev->fd); dev->fd = -1; return 0; } +static void usb_host_exit_notifier(struct Notifier* n) +{ + USBHostDevice *s = container_of(n, USBHostDevice, exit); + + if (s->fd != -1) { + ioctl(s->fd, USBDEVFS_RESET); + } +} + static int usb_host_initfn(USBDevice *dev) { USBHostDevice *s = DO_UPCAST(USBHostDevice, dev, dev); @@ -974,6 +1051,8 @@ dev->auto_attach = 0; s->fd = -1; QTAILQ_INSERT_TAIL(&hostdevs, s, next); + s->exit.notify = usb_host_exit_notifier; + qemu_add_exit_notifier(&s->exit); usb_host_auto_check(NULL); return 0; } @@ -1007,15 +1086,14 @@ { struct USBAutoFilter filter; USBDevice *dev; - USBHostDevice *s; char *p; dev = usb_create(NULL /* FIXME */, "usb-host"); - s = DO_UPCAST(USBHostDevice, dev, dev); if (strstr(devname, "auto:")) { - if (parse_filter(devname, &filter) < 0) + if (parse_filter(devname, &filter) < 0) { goto fail; + } } else { if ((p = strchr(devname, '.'))) { filter.bus_num = strtoul(devname, NULL, 0); @@ -1051,13 +1129,13 @@ int bus_num, addr; USBHostDevice *s; - if (strstr(devname, "auto:")) + if (strstr(devname, "auto:")) { return usb_host_auto_del(devname); - - if (usb_host_find_device(&bus_num, &addr, product_name, sizeof(product_name), - devname) < 0) + } + if (usb_host_find_device(&bus_num, &addr, product_name, + sizeof(product_name), devname) < 0) { return -1; - + } s = hostdev_find(bus_num, addr); if (s) { usb_device_delete_addr(s->bus_num, s->dev.addr); @@ -1075,15 +1153,18 @@ const char *p; char *q; p = strstr(str, tag); - if (!p) + if (!p) { return -1; + } p += strlen(tag); - while (qemu_isspace(*p)) + while (qemu_isspace(*p)) { p++; + } q = buf; while (*p != '\0' && !strchr(stopchars, *p)) { - if ((q - buf) < (buf_size - 1)) + if ((q - buf) < (buf_size - 1)) { *q++ = *p; + } p++; } *q = '\0'; @@ -1118,63 +1199,75 @@ device_count = 0; bus_num = addr = speed = class_id = product_id = vendor_id = 0; for(;;) { - if (fgets(line, sizeof(line), f) == NULL) + if (fgets(line, sizeof(line), f) == NULL) { break; - if (strlen(line) > 0) + } + if (strlen(line) > 0) { line[strlen(line) - 1] = '\0'; + } if (line[0] == 'T' && line[1] == ':') { if (device_count && (vendor_id || product_id)) { /* New device. Add the previously discovered device. */ - ret = func(opaque, bus_num, addr, class_id, vendor_id, + ret = func(opaque, bus_num, addr, 0, class_id, vendor_id, product_id, product_name, speed); - if (ret) + if (ret) { goto the_end; + } } - if (get_tag_value(buf, sizeof(buf), line, "Bus=", " ") < 0) + if (get_tag_value(buf, sizeof(buf), line, "Bus=", " ") < 0) { goto fail; + } bus_num = atoi(buf); - if (get_tag_value(buf, sizeof(buf), line, "Dev#=", " ") < 0) + if (get_tag_value(buf, sizeof(buf), line, "Dev#=", " ") < 0) { goto fail; + } addr = atoi(buf); - if (get_tag_value(buf, sizeof(buf), line, "Spd=", " ") < 0) + if (get_tag_value(buf, sizeof(buf), line, "Spd=", " ") < 0) { goto fail; - if (!strcmp(buf, "480")) + } + if (!strcmp(buf, "480")) { speed = USB_SPEED_HIGH; - else if (!strcmp(buf, "1.5")) + } else if (!strcmp(buf, "1.5")) { speed = USB_SPEED_LOW; - else + } else { speed = USB_SPEED_FULL; + } product_name[0] = '\0'; class_id = 0xff; device_count++; product_id = 0; vendor_id = 0; } else if (line[0] == 'P' && line[1] == ':') { - if (get_tag_value(buf, sizeof(buf), line, "Vendor=", " ") < 0) + if (get_tag_value(buf, sizeof(buf), line, "Vendor=", " ") < 0) { goto fail; + } vendor_id = strtoul(buf, NULL, 16); - if (get_tag_value(buf, sizeof(buf), line, "ProdID=", " ") < 0) + if (get_tag_value(buf, sizeof(buf), line, "ProdID=", " ") < 0) { goto fail; + } product_id = strtoul(buf, NULL, 16); } else if (line[0] == 'S' && line[1] == ':') { - if (get_tag_value(buf, sizeof(buf), line, "Product=", "") < 0) + if (get_tag_value(buf, sizeof(buf), line, "Product=", "") < 0) { goto fail; + } pstrcpy(product_name, sizeof(product_name), buf); } else if (line[0] == 'D' && line[1] == ':') { - if (get_tag_value(buf, sizeof(buf), line, "Cls=", " (") < 0) + if (get_tag_value(buf, sizeof(buf), line, "Cls=", " (") < 0) { goto fail; + } class_id = strtoul(buf, NULL, 16); } fail: ; } if (device_count && (vendor_id || product_id)) { /* Add the last device. */ - ret = func(opaque, bus_num, addr, class_id, vendor_id, + ret = func(opaque, bus_num, addr, 0, class_id, vendor_id, product_id, product_name, speed); } the_end: - if (f) + if (f) { fclose(f); + } return ret; } @@ -1188,11 +1281,9 @@ * * @return 0 failed, 1 succeeded ('line' contains data) */ -static int usb_host_read_file(char *line, size_t line_size, const char *device_file, const char *device_name) +static int usb_host_read_file(char *line, size_t line_size, + const char *device_file, const char *device_name) { -#if 0 - Monitor *mon = cur_mon; -#endif FILE *f; int ret = 0; char filename[PATH_MAX]; @@ -1201,14 +1292,8 @@ device_file); f = fopen(filename, "r"); if (f) { - fgets(line, line_size, f); + ret = fgets(line, line_size, f) != NULL; fclose(f); - ret = 1; -#if 0 - } else { - if (mon) - monitor_printf(mon, "husb: could not open %s\n", filename); -#endif } return ret; @@ -1225,7 +1310,7 @@ { DIR *dir = NULL; char line[1024]; - int bus_num, addr, speed, class_id, product_id, vendor_id; + int bus_num, addr, devpath, speed, class_id, product_id, vendor_id; int ret = 0; char product_name[512]; struct dirent *de; @@ -1239,59 +1324,73 @@ while ((de = readdir(dir))) { if (de->d_name[0] != '.' && !strchr(de->d_name, ':')) { char *tmpstr = de->d_name; - if (!strncmp(de->d_name, "usb", 3)) + if (!strncmp(de->d_name, "usb", 3)) { tmpstr += 3; - bus_num = atoi(tmpstr); + } + if (sscanf(tmpstr, "%d-%d", &bus_num, &devpath) < 1) { + goto the_end; + } - if (!usb_host_read_file(line, sizeof(line), "devnum", de->d_name)) + if (!usb_host_read_file(line, sizeof(line), "devnum", de->d_name)) { goto the_end; - if (sscanf(line, "%d", &addr) != 1) + } + if (sscanf(line, "%d", &addr) != 1) { goto the_end; - + } if (!usb_host_read_file(line, sizeof(line), "bDeviceClass", - de->d_name)) + de->d_name)) { goto the_end; - if (sscanf(line, "%x", &class_id) != 1) + } + if (sscanf(line, "%x", &class_id) != 1) { goto the_end; + } - if (!usb_host_read_file(line, sizeof(line), "idVendor", de->d_name)) + if (!usb_host_read_file(line, sizeof(line), "idVendor", + de->d_name)) { goto the_end; - if (sscanf(line, "%x", &vendor_id) != 1) + } + if (sscanf(line, "%x", &vendor_id) != 1) { goto the_end; - + } if (!usb_host_read_file(line, sizeof(line), "idProduct", - de->d_name)) + de->d_name)) { goto the_end; - if (sscanf(line, "%x", &product_id) != 1) + } + if (sscanf(line, "%x", &product_id) != 1) { goto the_end; - + } if (!usb_host_read_file(line, sizeof(line), "product", de->d_name)) { *product_name = 0; } else { - if (strlen(line) > 0) + if (strlen(line) > 0) { line[strlen(line) - 1] = '\0'; + } pstrcpy(product_name, sizeof(product_name), line); } - if (!usb_host_read_file(line, sizeof(line), "speed", de->d_name)) + if (!usb_host_read_file(line, sizeof(line), "speed", de->d_name)) { goto the_end; - if (!strcmp(line, "480\n")) + } + if (!strcmp(line, "480\n")) { speed = USB_SPEED_HIGH; - else if (!strcmp(line, "1.5\n")) + } else if (!strcmp(line, "1.5\n")) { speed = USB_SPEED_LOW; - else + } else { speed = USB_SPEED_FULL; + } - ret = func(opaque, bus_num, addr, class_id, vendor_id, + ret = func(opaque, bus_num, addr, devpath, class_id, vendor_id, product_id, product_name, speed); - if (ret) + if (ret) { goto the_end; + } } } the_end: - if (dir) + if (dir) { closedir(dir); + } return ret; } @@ -1316,7 +1415,7 @@ strcpy(devpath, USBDEVBUS_PATH); usb_fs_type = USB_FS_SYS; closedir(dir); - dprintf(USBDBG_DEVOPENED, USBSYSBUS_PATH); + DPRINTF(USBDBG_DEVOPENED, USBSYSBUS_PATH); goto found_devices; } f = fopen(USBPROCBUS_PATH "/devices", "r"); @@ -1325,7 +1424,7 @@ strcpy(devpath, USBPROCBUS_PATH); usb_fs_type = USB_FS_PROC; fclose(f); - dprintf(USBDBG_DEVOPENED, USBPROCBUS_PATH); + DPRINTF(USBDBG_DEVOPENED, USBPROCBUS_PATH); goto found_devices; } /* try additional methods if an access method hasn't been found yet */ @@ -1335,22 +1434,24 @@ strcpy(devpath, USBDEVBUS_PATH); usb_fs_type = USB_FS_DEV; fclose(f); - dprintf(USBDBG_DEVOPENED, USBDEVBUS_PATH); + DPRINTF(USBDBG_DEVOPENED, USBDEVBUS_PATH); goto found_devices; } found_devices: if (!usb_fs_type) { - if (mon) + if (mon) { monitor_printf(mon, "husb: unable to access USB devices\n"); + } return -ENOENT; } /* the module setting (used later for opening devices) */ usb_host_device_path = qemu_mallocz(strlen(devpath)+1); strcpy(usb_host_device_path, devpath); - if (mon) + if (mon) { monitor_printf(mon, "husb: using %s file-system with %s\n", fs_type[usb_fs_type], usb_host_device_path); + } } switch (usb_fs_type) { @@ -1370,7 +1471,7 @@ static QEMUTimer *usb_auto_timer; -static int usb_host_auto_scan(void *opaque, int bus_num, int addr, +static int usb_host_auto_scan(void *opaque, int bus_num, int addr, int devpath, int class_id, int vendor_id, int product_id, const char *product_name, int speed) { @@ -1384,27 +1485,29 @@ QTAILQ_FOREACH(s, &hostdevs, next) { f = &s->match; - if (f->bus_num > 0 && f->bus_num != bus_num) + if (f->bus_num > 0 && f->bus_num != bus_num) { continue; - - if (f->addr > 0 && f->addr != addr) + } + if (f->addr > 0 && f->addr != addr) { continue; + } - if (f->vendor_id > 0 && f->vendor_id != vendor_id) + if (f->vendor_id > 0 && f->vendor_id != vendor_id) { continue; + } - if (f->product_id > 0 && f->product_id != product_id) + if (f->product_id > 0 && f->product_id != product_id) { continue; - + } /* We got a match */ /* Already attached ? */ - if (s->fd != -1) + if (s->fd != -1) { return 0; + } + DPRINTF("husb: auto open: bus_num %d addr %d\n", bus_num, addr); - dprintf("husb: auto open: bus_num %d addr %d\n", bus_num, addr); - - usb_host_open(s, bus_num, addr, product_name); + usb_host_open(s, bus_num, addr, devpath, product_name); } return 0; @@ -1418,21 +1521,24 @@ usb_host_scan(NULL, usb_host_auto_scan); QTAILQ_FOREACH(s, &hostdevs, next) { - if (s->fd == -1) + if (s->fd == -1) { unconnected++; + } } if (unconnected == 0) { /* nothing to watch */ - if (usb_auto_timer) + if (usb_auto_timer) { qemu_del_timer(usb_auto_timer); + } return; } if (!usb_auto_timer) { usb_auto_timer = qemu_new_timer(rt_clock, usb_host_auto_check, NULL); - if (!usb_auto_timer) + if (!usb_auto_timer) { return; + } } qemu_mod_timer(usb_auto_timer, qemu_get_clock(rt_clock) + 2000); } @@ -1462,13 +1568,15 @@ f->product_id = 0; for (i = BUS; i < DONE; i++) { - p = strpbrk(p, ":."); - if (!p) break; + p = strpbrk(p, ":."); + if (!p) { + break; + } p++; - - if (*p == '*') - continue; + if (*p == '*') { + continue; + } switch(i) { case BUS: f->bus_num = strtol(p, NULL, 10); break; case DEV: f->addr = strtol(p, NULL, 10); break; @@ -1514,8 +1622,9 @@ { const struct usb_class_info *p; for(p = usb_class_info; p->class != -1; p++) { - if (p->class == class) + if (p->class == class) { break; + } } return p->class_name; } @@ -1545,18 +1654,20 @@ monitor_printf(mon, " Device %d.%d, speed %s Mb/s\n", bus_num, addr, speed_str); class_str = usb_class_str(class_id); - if (class_str) + if (class_str) { monitor_printf(mon, " %s:", class_str); - else + } else { monitor_printf(mon, " Class %02x:", class_id); + } monitor_printf(mon, " USB device %04x:%04x", vendor_id, product_id); - if (product_name[0] != '\0') + if (product_name[0] != '\0') { monitor_printf(mon, ", %s", product_name); + } monitor_printf(mon, "\n"); } static int usb_host_info_device(void *opaque, int bus_num, int addr, - int class_id, + int devpath, int class_id, int vendor_id, int product_id, const char *product_name, int speed) @@ -1570,18 +1681,20 @@ static void dec2str(int val, char *str, size_t size) { - if (val == 0) + if (val == 0) { snprintf(str, size, "*"); - else - snprintf(str, size, "%d", val); + } else { + snprintf(str, size, "%d", val); + } } static void hex2str(int val, char *str, size_t size) { - if (val == 0) + if (val == 0) { snprintf(str, size, "*"); - else + } else { snprintf(str, size, "%04x", val); + } } void usb_host_info(Monitor *mon) @@ -1591,8 +1704,10 @@ usb_host_scan(mon, usb_host_info_device); - if (QTAILQ_EMPTY(&hostdevs)) + if (QTAILQ_EMPTY(&hostdevs)) { return; + } + monitor_printf(mon, " Auto filters:\n"); QTAILQ_FOREACH(s, &hostdevs, next) { char bus[10], addr[10], vid[10], pid[10]; diff -Nru qemu-kvm-0.12.5+noroms/VERSION qemu-kvm-0.14.1/VERSION --- qemu-kvm-0.12.5+noroms/VERSION 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/VERSION 2011-05-11 13:29:46.000000000 +0000 @@ -1 +1 @@ -0.12.5 +0.14.1 diff -Nru qemu-kvm-0.12.5+noroms/version.rc qemu-kvm-0.14.1/version.rc --- qemu-kvm-0.12.5+noroms/version.rc 1970-01-01 00:00:00.000000000 +0000 +++ qemu-kvm-0.14.1/version.rc 2011-05-11 13:29:46.000000000 +0000 @@ -0,0 +1,28 @@ +#include +#include "config-host.h" + +VS_VERSION_INFO VERSIONINFO +FILEVERSION CONFIG_FILEVERSION +PRODUCTVERSION CONFIG_PRODUCTVERSION +FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +FILEOS VOS_NT_WINDOWS32 +FILETYPE VFT_APP +FILESUBTYPE VFT2_UNKNOWN +{ + BLOCK "StringFileInfo" + { + BLOCK "040904E4" + { + VALUE "CompanyName", "http://www.qemu.org" + VALUE "FileDescription", "QEMU machine emulators and tools" + VALUE "FileVersion", QEMU_VERSION + VALUE "LegalCopyright", "Copyright various authors. Released under the GNU General Public License." + VALUE "LegalTrademarks", "QEMU is a trademark of Fabrice Bellard." + VALUE "ProductName", "QEMU" + } + } + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0409, 1252 + } +} diff -Nru qemu-kvm-0.12.5+noroms/vl.c qemu-kvm-0.14.1/vl.c --- qemu-kvm-0.12.5+noroms/vl.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/vl.c 2011-05-11 13:29:46.000000000 +0000 @@ -34,7 +34,6 @@ #ifndef _WIN32 #include -#include #include #include #include @@ -48,10 +47,15 @@ #include #include #include +#ifdef CONFIG_SIMPLE_TRACE +#include "trace.h" +#endif + #ifdef CONFIG_BSD #include #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) #include +#include #else #include #endif @@ -59,13 +63,6 @@ #ifdef __linux__ #include #include -#include -#include - -/* For the benefit of older linux systems which don't supply it, - we use a local copy of hpet.h. */ -/* #include */ -#include "hpet.h" #include #include @@ -75,7 +72,6 @@ #include #include #include -#include #include #include #include // must come after ip.h @@ -84,9 +80,6 @@ #include #include #include -/* See MySQL bug #7156 (http://bugs.mysql.com/bug.php?id=7156) for - discussion about Solaris header problems */ -extern int madvise(caddr_t, size_t, int); #endif #endif #endif @@ -101,7 +94,6 @@ #ifdef _WIN32 #include -#include #endif #ifdef CONFIG_SDL @@ -127,7 +119,6 @@ #include "hw/usb.h" #include "hw/pcmcia.h" #include "hw/pc.h" -#include "hw/audiodev.h" #include "hw/isa.h" #include "hw/baum.h" #include "hw/bt.h" @@ -147,63 +138,61 @@ #include "qemu-char.h" #include "cache-utils.h" #include "block.h" -#include "block_int.h" +#include "blockdev.h" #include "block-migration.h" #include "dma.h" #include "audio/audio.h" #include "migration.h" #include "kvm.h" -#include "balloon.h" #include "qemu-option.h" #include "qemu-config.h" #include "qemu-objects.h" -#include "qemu-kvm.h" +#include "qemu-options.h" #include "hw/device-assignment.h" +#ifdef CONFIG_VIRTFS +#include "fsdev/qemu-fsdev.h" +#endif #include "disas.h" -#include "exec-all.h" - #include "qemu_socket.h" #include "slirp/libslirp.h" +#include "trace.h" #include "qemu-queue.h" +#include "cpus.h" +#include "arch_init.h" + +#include "ui/qemu-spice.h" //#define DEBUG_NET //#define DEBUG_SLIRP #define DEFAULT_RAM_SIZE 128 +#define MAX_VIRTIO_CONSOLES 1 + static const char *data_dir; const char *bios_name = NULL; -/* Note: drives_table[MAX_DRIVES] is a dummy block driver if none available - to store the VM snapshots */ -struct drivelist drives = QTAILQ_HEAD_INITIALIZER(drives); -struct driveoptlist driveopts = QTAILQ_HEAD_INITIALIZER(driveopts); -DriveInfo *extboot_drive = NULL; enum vga_retrace_method vga_retrace_method = VGA_RETRACE_DUMB; -static DisplayState *display_state; DisplayType display_type = DT_DEFAULT; +int display_remote = 0; const char* keyboard_layout = NULL; ram_addr_t ram_size; +const char *mem_path = NULL; +#ifdef MAP_POPULATE +int mem_prealloc = 0; /* force preallocation of physical target memory */ +#endif int nb_nics; NICInfo nd_table[MAX_NICS]; int vm_running; int autostart; +int incoming_expected; /* Started with -incoming and waiting for incoming */ static int rtc_utc = 1; static int rtc_date_offset = -1; /* -1 means no change */ QEMUClock *rtc_clock; int vga_interface_type = VGA_NONE; -#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; @@ -212,70 +201,53 @@ CharDriverState *serial_hds[MAX_SERIAL_PORTS]; CharDriverState *parallel_hds[MAX_PARALLEL_PORTS]; CharDriverState *virtcon_hds[MAX_VIRTIO_CONSOLES]; -#ifdef TARGET_I386 int win2k_install_hack = 0; int rtc_td_hack = 0; -#endif int usb_enabled = 0; int singlestep = 0; -const char *assigned_devices[MAX_DEV_ASSIGN_CMDLINE]; -int assigned_devices_index; int smp_cpus = 1; int max_cpus = 0; int smp_cores = 1; int smp_threads = 1; const char *vnc_display; int acpi_enabled = 1; -#ifdef TARGET_I386 int no_hpet = 0; -#endif int fd_bootchk = 1; int no_reboot = 0; int no_shutdown = 0; int cursor_hide = 1; int graphic_rotate = 0; uint8_t irq0override = 1; -#ifndef _WIN32 -int daemonize = 0; -#endif const char *watchdog; -const char *option_rom[MAX_OPTION_ROMS]; +QEMUOptionRom option_rom[MAX_OPTION_ROMS]; int nb_option_roms; int semihosting_enabled = 0; int time_drift_fix = 0; unsigned int kvm_shadow_memory = 0; -const char *mem_path = NULL; -#ifdef MAP_POPULATE -int mem_prealloc = 1; /* force preallocation of physical target memory */ -#endif -#ifdef TARGET_ARM int old_param = 0; -#endif const char *qemu_name; int alt_grab = 0; int ctrl_grab = 0; -#if defined(TARGET_SPARC) || defined(TARGET_PPC) unsigned int nb_prom_envs = 0; const char *prom_envs[MAX_PROM_ENVS]; -#endif const char *nvram = NULL; int boot_menu; +typedef struct FWBootEntry FWBootEntry; + +struct FWBootEntry { + QTAILQ_ENTRY(FWBootEntry) link; + int32_t bootindex; + DeviceState *dev; + char *suffix; +}; + +QTAILQ_HEAD(, FWBootEntry) fw_boot_order = QTAILQ_HEAD_INITIALIZER(fw_boot_order); + int nb_numa_nodes; uint64_t node_mem[MAX_NODES]; uint64_t node_cpumask[MAX_NODES]; -static CPUState *cur_cpu; -static CPUState *next_cpu; -static int timer_alarm_pending = 1; -/* Conversion factor from emulated instructions to virtual clock ticks. */ -static int icount_time_shift; -/* Arbitrarily pick 1MIPS as the minimum allowable speed. */ -#define MAX_ICOUNT_SHIFT 10 -/* Compensate for varying guest execution speed. */ -static int64_t qemu_icount_bias; -static QEMUTimer *icount_rt_timer; -static QEMUTimer *icount_vm_timer; static QEMUTimer *nographic_timer; uint8_t qemu_uuid[16]; @@ -283,6 +255,16 @@ static QEMUBootSetHandler *boot_set_handler; static void *boot_set_opaque; +static NotifierList exit_notifiers = + NOTIFIER_LIST_INITIALIZER(exit_notifiers); + +static NotifierList machine_init_done_notifiers = + NOTIFIER_LIST_INITIALIZER(machine_init_done_notifiers); + +int kvm_allowed = -1; +uint32_t xen_domid; +enum xen_mode xen_mode = XEN_EMULATE; + static int default_serial = 1; static int default_parallel = 1; static int default_virtcon = 1; @@ -300,8 +282,9 @@ { .driver = "isa-parallel", .flag = &default_parallel }, { .driver = "isa-fdc", .flag = &default_floppy }, { .driver = "ide-drive", .flag = &default_cdrom }, - { .driver = "virtio-console-pci", .flag = &default_virtcon }, - { .driver = "virtio-console-s390", .flag = &default_virtcon }, + { .driver = "virtio-serial-pci", .flag = &default_virtcon }, + { .driver = "virtio-serial-s390", .flag = &default_virtcon }, + { .driver = "virtio-serial", .flag = &default_virtcon }, { .driver = "VGA", .flag = &default_vga }, { .driver = "cirrus-vga", .flag = &default_vga }, { .driver = "vmware-svga", .flag = &default_vga }, @@ -323,3644 +306,1043 @@ } /***********************************************************/ -/* x86 ISA bus support */ - -target_phys_addr_t isa_mem_base = 0; -PicState2 *isa_pic; +/* real time host monotonic timer */ /***********************************************************/ -void hw_error(const char *fmt, ...) +/* host time/date access */ +void qemu_get_timedate(struct tm *tm, int offset) { - va_list ap; - CPUState *env; + time_t ti; + struct tm *ret; - va_start(ap, fmt); - fprintf(stderr, "qemu: hardware error: "); - vfprintf(stderr, fmt, ap); - fprintf(stderr, "\n"); - for(env = first_cpu; env != NULL; env = env->next_cpu) { - fprintf(stderr, "CPU #%d:\n", env->cpu_index); -#ifdef TARGET_I386 - cpu_dump_state(env, stderr, fprintf, X86_DUMP_FPU); -#else - cpu_dump_state(env, stderr, fprintf, 0); -#endif + time(&ti); + ti += offset; + if (rtc_date_offset == -1) { + if (rtc_utc) + ret = gmtime(&ti); + else + ret = localtime(&ti); + } else { + ti -= rtc_date_offset; + ret = gmtime(&ti); } - va_end(ap); - abort(); -} -static void set_proc_name(const char *s) -{ -#if defined(__linux__) && defined(PR_SET_NAME) - char name[16]; - if (!s) - return; - name[sizeof(name) - 1] = 0; - strncpy(name, s, sizeof(name)); - /* Could rewrite argv[0] too, but that's a bit more complicated. - This simple way is enough for `top'. */ - prctl(PR_SET_NAME, name); -#endif + memcpy(tm, ret, sizeof(struct tm)); } - -/***************/ -/* ballooning */ - -static QEMUBalloonEvent *qemu_balloon_event; -void *qemu_balloon_event_opaque; -void qemu_add_balloon_handler(QEMUBalloonEvent *func, void *opaque) +int qemu_timedate_diff(struct tm *tm) { - qemu_balloon_event = func; - qemu_balloon_event_opaque = opaque; -} + time_t seconds; -void qemu_balloon(ram_addr_t target) -{ - if (qemu_balloon_event) - qemu_balloon_event(qemu_balloon_event_opaque, target); -} + if (rtc_date_offset == -1) + if (rtc_utc) + seconds = mktimegm(tm); + else + seconds = mktime(tm); + else + seconds = mktimegm(tm) + rtc_date_offset; -ram_addr_t qemu_balloon_status(void) -{ - if (qemu_balloon_event) - return qemu_balloon_event(qemu_balloon_event_opaque, 0); - return 0; + return seconds - time(NULL); } -/***********************************************************/ -/* keyboard/mouse */ - -static QEMUPutKBDEvent *qemu_put_kbd_event; -static void *qemu_put_kbd_event_opaque; -static QEMUPutMouseEntry *qemu_put_mouse_event_head; -static QEMUPutMouseEntry *qemu_put_mouse_event_current; - -void qemu_add_kbd_event_handler(QEMUPutKBDEvent *func, void *opaque) +void rtc_change_mon_event(struct tm *tm) { - qemu_put_kbd_event_opaque = opaque; - qemu_put_kbd_event = func; + QObject *data; + + data = qobject_from_jsonf("{ 'offset': %d }", qemu_timedate_diff(tm)); + monitor_protocol_event(QEVENT_RTC_CHANGE, data); + qobject_decref(data); } -QEMUPutMouseEntry *qemu_add_mouse_event_handler(QEMUPutMouseEvent *func, - void *opaque, int absolute, - const char *name) +static void configure_rtc_date_offset(const char *startdate, int legacy) { - QEMUPutMouseEntry *s, *cursor; - - s = qemu_mallocz(sizeof(QEMUPutMouseEntry)); - - s->qemu_put_mouse_event = func; - s->qemu_put_mouse_event_opaque = opaque; - s->qemu_put_mouse_event_absolute = absolute; - s->qemu_put_mouse_event_name = qemu_strdup(name); - s->next = NULL; + time_t rtc_start_date; + struct tm tm; - if (!qemu_put_mouse_event_head) { - qemu_put_mouse_event_head = qemu_put_mouse_event_current = s; - return s; + if (!strcmp(startdate, "now") && legacy) { + rtc_date_offset = -1; + } else { + if (sscanf(startdate, "%d-%d-%dT%d:%d:%d", + &tm.tm_year, + &tm.tm_mon, + &tm.tm_mday, + &tm.tm_hour, + &tm.tm_min, + &tm.tm_sec) == 6) { + /* OK */ + } else if (sscanf(startdate, "%d-%d-%d", + &tm.tm_year, + &tm.tm_mon, + &tm.tm_mday) == 3) { + tm.tm_hour = 0; + tm.tm_min = 0; + tm.tm_sec = 0; + } else { + goto date_fail; + } + tm.tm_year -= 1900; + tm.tm_mon--; + rtc_start_date = mktimegm(&tm); + if (rtc_start_date == -1) { + date_fail: + fprintf(stderr, "Invalid date format. Valid formats are:\n" + "'2006-06-17T16:01:21' or '2006-06-17'\n"); + exit(1); + } + rtc_date_offset = time(NULL) - rtc_start_date; } - - cursor = qemu_put_mouse_event_head; - while (cursor->next != NULL) - cursor = cursor->next; - - cursor->next = s; - qemu_put_mouse_event_current = s; - - return s; } -void qemu_remove_mouse_event_handler(QEMUPutMouseEntry *entry) +static void configure_rtc(QemuOpts *opts) { - QEMUPutMouseEntry *prev = NULL, *cursor; - - if (!qemu_put_mouse_event_head || entry == NULL) - return; + const char *value; - cursor = qemu_put_mouse_event_head; - while (cursor != NULL && cursor != entry) { - prev = cursor; - cursor = cursor->next; + value = qemu_opt_get(opts, "base"); + if (value) { + if (!strcmp(value, "utc")) { + rtc_utc = 1; + } else if (!strcmp(value, "localtime")) { + rtc_utc = 0; + } else { + configure_rtc_date_offset(value, 0); + } } - - if (cursor == NULL) // does not exist or list empty - return; - else if (prev == NULL) { // entry is head - qemu_put_mouse_event_head = cursor->next; - if (qemu_put_mouse_event_current == entry) - qemu_put_mouse_event_current = cursor->next; - qemu_free(entry->qemu_put_mouse_event_name); - qemu_free(entry); - return; + value = qemu_opt_get(opts, "clock"); + if (value) { + if (!strcmp(value, "host")) { + rtc_clock = host_clock; + } else if (!strcmp(value, "vm")) { + rtc_clock = vm_clock; + } else { + fprintf(stderr, "qemu: invalid option value '%s'\n", value); + exit(1); + } } - - prev->next = entry->next; - - if (qemu_put_mouse_event_current == entry) - qemu_put_mouse_event_current = prev; - - qemu_free(entry->qemu_put_mouse_event_name); - qemu_free(entry); -} - -void kbd_put_keycode(int keycode) -{ - if (qemu_put_kbd_event) { - qemu_put_kbd_event(qemu_put_kbd_event_opaque, keycode); + value = qemu_opt_get(opts, "driftfix"); + if (value) { + if (!strcmp(value, "slew")) { + rtc_td_hack = 1; + } else if (!strcmp(value, "none")) { + rtc_td_hack = 0; + } else { + fprintf(stderr, "qemu: invalid option value '%s'\n", value); + exit(1); + } } } -void kbd_mouse_event(int dx, int dy, int dz, int buttons_state) -{ - QEMUPutMouseEvent *mouse_event; - void *mouse_event_opaque; - int width; +/***********************************************************/ +/* Bluetooth support */ +static int nb_hcis; +static int cur_hci; +static struct HCIInfo *hci_table[MAX_NICS]; - if (!qemu_put_mouse_event_current) { - return; - } +static struct bt_vlan_s { + struct bt_scatternet_s net; + int id; + struct bt_vlan_s *next; +} *first_bt_vlan; - mouse_event = - qemu_put_mouse_event_current->qemu_put_mouse_event; - mouse_event_opaque = - qemu_put_mouse_event_current->qemu_put_mouse_event_opaque; - - if (mouse_event) { - if (graphic_rotate) { - if (qemu_put_mouse_event_current->qemu_put_mouse_event_absolute) - width = 0x7fff; - else - width = graphic_width - 1; - mouse_event(mouse_event_opaque, - width - dy, dx, dz, buttons_state); - } else - mouse_event(mouse_event_opaque, - dx, dy, dz, buttons_state); +/* find or alloc a new bluetooth "VLAN" */ +static struct bt_scatternet_s *qemu_find_bt_vlan(int id) +{ + struct bt_vlan_s **pvlan, *vlan; + for (vlan = first_bt_vlan; vlan != NULL; vlan = vlan->next) { + if (vlan->id == id) + return &vlan->net; } + vlan = qemu_mallocz(sizeof(struct bt_vlan_s)); + vlan->id = id; + pvlan = &first_bt_vlan; + while (*pvlan != NULL) + pvlan = &(*pvlan)->next; + *pvlan = vlan; + return &vlan->net; } -int kbd_mouse_is_absolute(void) +static void null_hci_send(struct HCIInfo *hci, const uint8_t *data, int len) { - if (!qemu_put_mouse_event_current) - return 0; - - return qemu_put_mouse_event_current->qemu_put_mouse_event_absolute; } -static void info_mice_iter(QObject *data, void *opaque) +static int null_hci_addr_set(struct HCIInfo *hci, const uint8_t *bd_addr) { - QDict *mouse; - Monitor *mon = opaque; - - mouse = qobject_to_qdict(data); - monitor_printf(mon, "%c Mouse #%" PRId64 ": %s\n", - (qdict_get_bool(mouse, "current") ? '*' : ' '), - qdict_get_int(mouse, "index"), qdict_get_str(mouse, "name")); + return -ENOTSUP; } -void do_info_mice_print(Monitor *mon, const QObject *data) -{ - QList *mice_list; +static struct HCIInfo null_hci = { + .cmd_send = null_hci_send, + .sco_send = null_hci_send, + .acl_send = null_hci_send, + .bdaddr_set = null_hci_addr_set, +}; - mice_list = qobject_to_qlist(data); - if (qlist_empty(mice_list)) { - monitor_printf(mon, "No mouse devices connected\n"); - return; - } +struct HCIInfo *qemu_next_hci(void) +{ + if (cur_hci == nb_hcis) + return &null_hci; - qlist_iter(mice_list, info_mice_iter, mon); + return hci_table[cur_hci++]; } -/** - * do_info_mice(): Show VM mice information - * - * Each mouse is represented by a QDict, the returned QObject is a QList of - * all mice. - * - * The mouse QDict contains the following: - * - * - "name": mouse's name - * - "index": mouse's index - * - "current": true if this mouse is receiving events, false otherwise - * - * Example: - * - * [ { "name": "QEMU Microsoft Mouse", "index": 0, "current": false }, - * { "name": "QEMU PS/2 Mouse", "index": 1, "current": true } ] - */ -void do_info_mice(Monitor *mon, QObject **ret_data) +static struct HCIInfo *hci_init(const char *str) { - QEMUPutMouseEntry *cursor; - QList *mice_list; - int index = 0; - - mice_list = qlist_new(); + char *endp; + struct bt_scatternet_s *vlan = 0; - if (!qemu_put_mouse_event_head) { - goto out; + if (!strcmp(str, "null")) + /* null */ + return &null_hci; + else if (!strncmp(str, "host", 4) && (str[4] == '\0' || str[4] == ':')) + /* host[:hciN] */ + return bt_host_hci(str[4] ? str + 5 : "hci0"); + else if (!strncmp(str, "hci", 3)) { + /* hci[,vlan=n] */ + if (str[3]) { + if (!strncmp(str + 3, ",vlan=", 6)) { + vlan = qemu_find_bt_vlan(strtol(str + 9, &endp, 0)); + if (*endp) + vlan = 0; + } + } else + vlan = qemu_find_bt_vlan(0); + if (vlan) + return bt_new_hci(vlan); } - cursor = qemu_put_mouse_event_head; - while (cursor != NULL) { - QObject *obj; - obj = qobject_from_jsonf("{ 'name': %s, 'index': %d, 'current': %i }", - cursor->qemu_put_mouse_event_name, - index, cursor == qemu_put_mouse_event_current); - qlist_append_obj(mice_list, obj); - index++; - cursor = cursor->next; - } + fprintf(stderr, "qemu: Unknown bluetooth HCI `%s'.\n", str); -out: - *ret_data = QOBJECT(mice_list); + return 0; } -void do_mouse_set(Monitor *mon, const QDict *qdict) +static int bt_hci_parse(const char *str) { - QEMUPutMouseEntry *cursor; - int i = 0; - int index = qdict_get_int(qdict, "index"); + struct HCIInfo *hci; + bdaddr_t bdaddr; - if (!qemu_put_mouse_event_head) { - monitor_printf(mon, "No mouse devices connected\n"); - return; + if (nb_hcis >= MAX_NICS) { + fprintf(stderr, "qemu: Too many bluetooth HCIs (max %i).\n", MAX_NICS); + return -1; } - cursor = qemu_put_mouse_event_head; - while (cursor != NULL && index != i) { - i++; - cursor = cursor->next; - } + hci = hci_init(str); + if (!hci) + return -1; - if (cursor != NULL) - qemu_put_mouse_event_current = cursor; - else - monitor_printf(mon, "Mouse at given index not found\n"); + bdaddr.b[0] = 0x52; + bdaddr.b[1] = 0x54; + bdaddr.b[2] = 0x00; + bdaddr.b[3] = 0x12; + bdaddr.b[4] = 0x34; + bdaddr.b[5] = 0x56 + nb_hcis; + hci->bdaddr_set(hci, bdaddr.b); + + hci_table[nb_hcis++] = hci; + + return 0; } -/* compute with 96 bit intermediate result: (a*b)/c */ -uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c) +static void bt_vhci_add(int vlan_id) { - 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; -} - -/***********************************************************/ -/* real time host monotonic timer */ + struct bt_scatternet_s *vlan = qemu_find_bt_vlan(vlan_id); -static int64_t get_clock_realtime(void) -{ - struct timeval tv; + if (!vlan->slave) + fprintf(stderr, "qemu: warning: adding a VHCI to " + "an empty scatternet %i\n", vlan_id); - gettimeofday(&tv, NULL); - return tv.tv_sec * 1000000000LL + (tv.tv_usec * 1000); + bt_vhci_init(bt_new_hci(vlan)); } -#ifdef WIN32 +static struct bt_device_s *bt_device_add(const char *opt) +{ + struct bt_scatternet_s *vlan; + int vlan_id = 0; + char *endp = strstr(opt, ",vlan="); + int len = (endp ? endp - opt : strlen(opt)) + 1; + char devname[10]; -static int64_t clock_freq; + pstrcpy(devname, MIN(sizeof(devname), len), opt); -static void init_get_clock(void) -{ - LARGE_INTEGER freq; - int ret; - ret = QueryPerformanceFrequency(&freq); - if (ret == 0) { - fprintf(stderr, "Could not calibrate ticks\n"); - exit(1); + if (endp) { + vlan_id = strtol(endp + 6, &endp, 0); + if (*endp) { + fprintf(stderr, "qemu: unrecognised bluetooth vlan Id\n"); + return 0; + } } - clock_freq = freq.QuadPart; -} - -static int64_t get_clock(void) -{ - LARGE_INTEGER ti; - QueryPerformanceCounter(&ti); - return muldiv64(ti.QuadPart, get_ticks_per_sec(), clock_freq); -} -#else + vlan = qemu_find_bt_vlan(vlan_id); -static int use_rt_clock; + if (!vlan->slave) + fprintf(stderr, "qemu: warning: adding a slave device to " + "an empty scatternet %i\n", vlan_id); -static void init_get_clock(void) -{ - use_rt_clock = 0; -#if defined(__linux__) || (defined(__FreeBSD__) && __FreeBSD_version >= 500000) \ - || defined(__DragonFly__) || defined(__FreeBSD_kernel__) - { - struct timespec ts; - if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) { - use_rt_clock = 1; - } - } -#endif -} + if (!strcmp(devname, "keyboard")) + return bt_keyboard_init(vlan); -static int64_t get_clock(void) -{ -#if defined(__linux__) || (defined(__FreeBSD__) && __FreeBSD_version >= 500000) \ - || defined(__DragonFly__) || defined(__FreeBSD_kernel__) - if (use_rt_clock) { - struct timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - return ts.tv_sec * 1000000000LL + ts.tv_nsec; - } else -#endif - { - /* XXX: using gettimeofday leads to problems if the date - changes, so it should be avoided. */ - return get_clock_realtime(); - } + fprintf(stderr, "qemu: unsupported bluetooth device `%s'\n", devname); + return 0; } -#endif -/* Return the virtual CPU time, based on the instruction counter. */ -static int64_t cpu_get_icount(void) +static int bt_parse(const char *opt) { - int64_t icount; - CPUState *env = cpu_single_env;; - icount = qemu_icount; - if (env) { - if (!can_do_io(env)) - fprintf(stderr, "Bad clock read\n"); - icount -= (env->icount_decr.u16.low + env->icount_extra); - } - return qemu_icount_bias + (icount << icount_time_shift); -} + const char *endp, *p; + int vlan; -/***********************************************************/ -/* guest cycle counter */ + if (strstart(opt, "hci", &endp)) { + if (!*endp || *endp == ',') { + if (*endp) + if (!strstart(endp, ",vlan=", 0)) + opt = endp + 1; -typedef struct TimersState { - int64_t cpu_ticks_prev; - int64_t cpu_ticks_offset; - int64_t cpu_clock_offset; - int32_t cpu_ticks_enabled; - int64_t dummy; -} TimersState; - -TimersState timers_state; + return bt_hci_parse(opt); + } + } else if (strstart(opt, "vhci", &endp)) { + if (!*endp || *endp == ',') { + if (*endp) { + if (strstart(endp, ",vlan=", &p)) { + vlan = strtol(p, (char **) &endp, 0); + if (*endp) { + fprintf(stderr, "qemu: bad scatternet '%s'\n", p); + return 1; + } + } else { + fprintf(stderr, "qemu: bad parameter '%s'\n", endp + 1); + return 1; + } + } else + vlan = 0; -/* return the host CPU cycle counter and handle stop/restart */ -int64_t cpu_get_ticks(void) -{ - if (use_icount) { - return cpu_get_icount(); - } - if (!timers_state.cpu_ticks_enabled) { - return timers_state.cpu_ticks_offset; - } else { - int64_t ticks; - ticks = cpu_get_real_ticks(); - if (timers_state.cpu_ticks_prev > ticks) { - /* Note: non increasing ticks may happen if the host uses - software suspend */ - timers_state.cpu_ticks_offset += timers_state.cpu_ticks_prev - ticks; + bt_vhci_add(vlan); + return 0; } - timers_state.cpu_ticks_prev = ticks; - return ticks + timers_state.cpu_ticks_offset; - } -} - -/* return the host CPU monotonic timer and handle stop/restart */ -static int64_t cpu_get_clock(void) -{ - int64_t ti; - if (!timers_state.cpu_ticks_enabled) { - return timers_state.cpu_clock_offset; - } else { - ti = get_clock(); - return ti + timers_state.cpu_clock_offset; - } -} - -/* enable cpu_get_ticks() */ -void cpu_enable_ticks(void) -{ - if (!timers_state.cpu_ticks_enabled) { - timers_state.cpu_ticks_offset -= cpu_get_real_ticks(); - timers_state.cpu_clock_offset -= get_clock(); - timers_state.cpu_ticks_enabled = 1; - } -} + } else if (strstart(opt, "device:", &endp)) + return !bt_device_add(endp); -/* disable cpu_get_ticks() : the clock is stopped. You must not call - cpu_get_ticks() after that. */ -void cpu_disable_ticks(void) -{ - if (timers_state.cpu_ticks_enabled) { - timers_state.cpu_ticks_offset = cpu_get_ticks(); - timers_state.cpu_clock_offset = cpu_get_clock(); - timers_state.cpu_ticks_enabled = 0; - } + fprintf(stderr, "qemu: bad bluetooth parameter '%s'\n", opt); + return 1; } /***********************************************************/ -/* timers */ - -#define QEMU_CLOCK_REALTIME 0 -#define QEMU_CLOCK_VIRTUAL 1 -#define QEMU_CLOCK_HOST 2 - -struct QEMUClock { - int type; - /* XXX: add frequency */ -}; - -struct QEMUTimer { - QEMUClock *clock; - int64_t expire_time; - QEMUTimerCB *cb; - void *opaque; - struct QEMUTimer *next; -}; - -struct qemu_alarm_timer { - char const *name; - unsigned int flags; - - int (*start)(struct qemu_alarm_timer *t); - void (*stop)(struct qemu_alarm_timer *t); - void (*rearm)(struct qemu_alarm_timer *t); - void *priv; -}; +/* QEMU Block devices */ -#define ALARM_FLAG_DYNTICKS 0x1 -#define ALARM_FLAG_EXPIRED 0x2 +#define HD_OPTS "media=disk" +#define CDROM_OPTS "media=cdrom" +#define FD_OPTS "" +#define PFLASH_OPTS "" +#define MTD_OPTS "" +#define SD_OPTS "" -static inline int alarm_has_dynticks(struct qemu_alarm_timer *t) +static int drive_init_func(QemuOpts *opts, void *opaque) { - return t && (t->flags & ALARM_FLAG_DYNTICKS); + int *use_scsi = opaque; + + return drive_init(opts, *use_scsi) == NULL; } -static void qemu_rearm_alarm_timer(struct qemu_alarm_timer *t) +static int drive_enable_snapshot(QemuOpts *opts, void *opaque) { - if (!alarm_has_dynticks(t)) - return; - - t->rearm(t); + if (NULL == qemu_opt_get(opts, "snapshot")) { + qemu_opt_set(opts, "snapshot", "on"); + } + return 0; } -/* TODO: MIN_TIMER_REARM_US should be optimized */ -#define MIN_TIMER_REARM_US 250 - -static struct qemu_alarm_timer *alarm_timer; - -#ifdef _WIN32 - -struct qemu_alarm_win32 { - MMRESULT timerId; - unsigned int period; -} alarm_win32_data = {0, -1}; - -static int win32_start_timer(struct qemu_alarm_timer *t); -static void win32_stop_timer(struct qemu_alarm_timer *t); -static void win32_rearm_timer(struct qemu_alarm_timer *t); - -#else - -static int unix_start_timer(struct qemu_alarm_timer *t); -static void unix_stop_timer(struct qemu_alarm_timer *t); +static void default_drive(int enable, int snapshot, int use_scsi, + BlockInterfaceType type, int index, + const char *optstr) +{ + QemuOpts *opts; -#ifdef __linux__ + if (type == IF_DEFAULT) { + type = use_scsi ? IF_SCSI : IF_IDE; + } -static int dynticks_start_timer(struct qemu_alarm_timer *t); -static void dynticks_stop_timer(struct qemu_alarm_timer *t); -static void dynticks_rearm_timer(struct qemu_alarm_timer *t); - -static int hpet_start_timer(struct qemu_alarm_timer *t); -static void hpet_stop_timer(struct qemu_alarm_timer *t); - -static int rtc_start_timer(struct qemu_alarm_timer *t); -static void rtc_stop_timer(struct qemu_alarm_timer *t); - -#endif /* __linux__ */ - -#endif /* _WIN32 */ - -/* Correlation between real and virtual time is always going to be - fairly approximate, so ignore small variation. - When the guest is idle real and virtual time will be aligned in - the IO wait loop. */ -#define ICOUNT_WOBBLE (get_ticks_per_sec() / 10) - -static void icount_adjust(void) -{ - int64_t cur_time; - int64_t cur_icount; - int64_t delta; - static int64_t last_delta; - /* If the VM is not running, then do nothing. */ - if (!vm_running) + if (!enable || drive_get_by_index(type, index)) { return; + } - cur_time = cpu_get_clock(); - cur_icount = qemu_get_clock(vm_clock); - delta = cur_icount - cur_time; - /* FIXME: This is a very crude algorithm, somewhat prone to oscillation. */ - if (delta > 0 - && last_delta + ICOUNT_WOBBLE < delta * 2 - && icount_time_shift > 0) { - /* The guest is getting too far ahead. Slow time down. */ - icount_time_shift--; + opts = drive_add(type, index, NULL, optstr); + if (snapshot) { + drive_enable_snapshot(opts, NULL); } - if (delta < 0 - && last_delta - ICOUNT_WOBBLE > delta * 2 - && icount_time_shift < MAX_ICOUNT_SHIFT) { - /* The guest is getting too far behind. Speed time up. */ - icount_time_shift++; + if (!drive_init(opts, use_scsi)) { + exit(1); } - last_delta = delta; - qemu_icount_bias = cur_icount - (qemu_icount << icount_time_shift); -} - -static void icount_adjust_rt(void * opaque) -{ - qemu_mod_timer(icount_rt_timer, - qemu_get_clock(rt_clock) + 1000); - icount_adjust(); -} - -static void icount_adjust_vm(void * opaque) -{ - qemu_mod_timer(icount_vm_timer, - qemu_get_clock(vm_clock) + get_ticks_per_sec() / 10); - icount_adjust(); } -static void init_icount_adjust(void) +void qemu_register_boot_set(QEMUBootSetHandler *func, void *opaque) { - /* Have both realtime and virtual time triggers for speed adjustment. - The realtime trigger catches emulated time passing too slowly, - the virtual time trigger catches emulated time passing too fast. - Realtime triggers occur even when idle, so use them less frequently - than VM triggers. */ - icount_rt_timer = qemu_new_timer(rt_clock, icount_adjust_rt, NULL); - qemu_mod_timer(icount_rt_timer, - qemu_get_clock(rt_clock) + 1000); - icount_vm_timer = qemu_new_timer(vm_clock, icount_adjust_vm, NULL); - qemu_mod_timer(icount_vm_timer, - qemu_get_clock(vm_clock) + get_ticks_per_sec() / 10); + boot_set_handler = func; + boot_set_opaque = opaque; } -static struct qemu_alarm_timer alarm_timers[] = { -#ifndef _WIN32 -#ifdef __linux__ - {"dynticks", ALARM_FLAG_DYNTICKS, dynticks_start_timer, - dynticks_stop_timer, dynticks_rearm_timer, NULL}, - /* HPET - if available - is preferred */ - {"hpet", 0, hpet_start_timer, hpet_stop_timer, NULL, NULL}, - /* ...otherwise try RTC */ - {"rtc", 0, rtc_start_timer, rtc_stop_timer, NULL, NULL}, -#endif - {"unix", 0, unix_start_timer, unix_stop_timer, NULL, NULL}, -#else - {"dynticks", ALARM_FLAG_DYNTICKS, win32_start_timer, - win32_stop_timer, win32_rearm_timer, &alarm_win32_data}, - {"win32", 0, win32_start_timer, - win32_stop_timer, NULL, &alarm_win32_data}, -#endif - {NULL, } -}; - -static void show_available_alarms(void) +int qemu_boot_set(const char *boot_devices) { - int i; - - printf("Available alarm timers, in order of precedence:\n"); - for (i = 0; alarm_timers[i].name; i++) - printf("%s\n", alarm_timers[i].name); + if (!boot_set_handler) { + return -EINVAL; + } + return boot_set_handler(boot_set_opaque, boot_devices); } -static void configure_alarms(char const *opt) +static void validate_bootdevices(char *devices) { - int i; - int cur = 0; - int count = ARRAY_SIZE(alarm_timers) - 1; - char *arg; - char *name; - struct qemu_alarm_timer tmp; - - if (!strcmp(opt, "?")) { - show_available_alarms(); - exit(0); - } - - arg = qemu_strdup(opt); + /* We just do some generic consistency checks */ + const char *p; + int bitmap = 0; - /* Reorder the array */ - name = strtok(arg, ","); - while (name) { - for (i = 0; i < count && alarm_timers[i].name; i++) { - if (!strcmp(alarm_timers[i].name, name)) - break; + for (p = devices; *p != '\0'; p++) { + /* Allowed boot devices are: + * a-b: floppy disk drives + * c-f: IDE disk drives + * g-m: machine implementation dependant drives + * n-p: network devices + * It's up to each machine implementation to check if the given boot + * devices match the actual hardware implementation and firmware + * features. + */ + if (*p < 'a' || *p > 'p') { + fprintf(stderr, "Invalid boot device '%c'\n", *p); + exit(1); } - - if (i == count) { - fprintf(stderr, "Unknown clock %s\n", name); - goto next; + if (bitmap & (1 << (*p - 'a'))) { + fprintf(stderr, "Boot device '%c' was given twice\n", *p); + exit(1); } - - if (i < cur) - /* Ignore */ - goto next; - - /* Swap */ - tmp = alarm_timers[i]; - alarm_timers[i] = alarm_timers[cur]; - alarm_timers[cur] = tmp; - - cur++; -next: - name = strtok(NULL, ","); - } - - qemu_free(arg); - - if (cur) { - /* Disable remaining timers */ - for (i = cur; i < count; i++) - alarm_timers[i].name = NULL; - } else { - show_available_alarms(); - exit(1); + bitmap |= 1 << (*p - 'a'); } } -#define QEMU_NUM_CLOCKS 3 - -QEMUClock *rt_clock; -QEMUClock *vm_clock; -QEMUClock *host_clock; - -static QEMUTimer *active_timers[QEMU_NUM_CLOCKS]; - -static QEMUClock *qemu_new_clock(int type) +static void restore_boot_devices(void *opaque) { - QEMUClock *clock; - clock = qemu_mallocz(sizeof(QEMUClock)); - clock->type = type; - return clock; -} + char *standard_boot_devices = opaque; + static int first = 1; -QEMUTimer *qemu_new_timer(QEMUClock *clock, QEMUTimerCB *cb, void *opaque) -{ - QEMUTimer *ts; + /* Restore boot order and remove ourselves after the first boot */ + if (first) { + first = 0; + return; + } - ts = qemu_mallocz(sizeof(QEMUTimer)); - ts->clock = clock; - ts->cb = cb; - ts->opaque = opaque; - return ts; -} + qemu_boot_set(standard_boot_devices); -void qemu_free_timer(QEMUTimer *ts) -{ - qemu_free(ts); + qemu_unregister_reset(restore_boot_devices, standard_boot_devices); + qemu_free(standard_boot_devices); } -/* stop a timer, but do not dealloc it */ -void qemu_del_timer(QEMUTimer *ts) +void add_boot_device_path(int32_t bootindex, DeviceState *dev, + const char *suffix) { - QEMUTimer **pt, *t; + FWBootEntry *node, *i; - /* NOTE: this code must be signal safe because - qemu_timer_expired() can be called from a signal. */ - pt = &active_timers[ts->clock->type]; - for(;;) { - t = *pt; - if (!t) - break; - if (t == ts) { - *pt = t->next; - break; - } - pt = &t->next; + if (bootindex < 0) { + return; } -} - -/* modify the current timer so that it will be fired when current_time - >= expire_time. The corresponding callback will be called. */ -void qemu_mod_timer(QEMUTimer *ts, int64_t expire_time) -{ - QEMUTimer **pt, *t; - qemu_del_timer(ts); - - /* add the timer in the sorted list */ - /* NOTE: this code must be signal safe because - qemu_timer_expired() can be called from a signal. */ - pt = &active_timers[ts->clock->type]; - for(;;) { - t = *pt; - if (!t) - break; - if (t->expire_time > expire_time) - break; - pt = &t->next; - } - ts->expire_time = expire_time; - ts->next = *pt; - *pt = ts; + assert(dev != NULL || suffix != NULL); - /* Rearm if necessary */ - if (pt == &active_timers[ts->clock->type]) { - if ((alarm_timer->flags & ALARM_FLAG_EXPIRED) == 0) { - qemu_rearm_alarm_timer(alarm_timer); + node = qemu_mallocz(sizeof(FWBootEntry)); + node->bootindex = bootindex; + node->suffix = suffix ? qemu_strdup(suffix) : NULL; + node->dev = dev; + + QTAILQ_FOREACH(i, &fw_boot_order, link) { + if (i->bootindex == bootindex) { + fprintf(stderr, "Two devices with same boot index %d\n", bootindex); + exit(1); + } else if (i->bootindex < bootindex) { + continue; } - /* Interrupt execution to force deadline recalculation. */ - if (use_icount) - qemu_notify_event(); - } -} - -int qemu_timer_pending(QEMUTimer *ts) -{ - QEMUTimer *t; - for(t = active_timers[ts->clock->type]; t != NULL; t = t->next) { - if (t == ts) - return 1; + QTAILQ_INSERT_BEFORE(i, node, link); + return; } - return 0; -} - -int qemu_timer_expired(QEMUTimer *timer_head, int64_t current_time) -{ - if (!timer_head) - return 0; - return (timer_head->expire_time <= current_time); + QTAILQ_INSERT_TAIL(&fw_boot_order, node, link); } -static void qemu_run_timers(QEMUTimer **ptimer_head, int64_t current_time) +/* + * This function returns null terminated string that consist of new line + * separated device pathes. + * + * memory pointed by "size" is assigned total length of the array in bytes + * + */ +char *get_boot_devices_list(uint32_t *size) { - QEMUTimer *ts; + FWBootEntry *i; + uint32_t total = 0; + char *list = NULL; - for(;;) { - ts = *ptimer_head; - if (!ts || ts->expire_time > current_time) - break; - /* remove timer from the list before calling the callback */ - *ptimer_head = ts->next; - ts->next = NULL; + QTAILQ_FOREACH(i, &fw_boot_order, link) { + char *devpath = NULL, *bootpath; + int len; - /* run the callback (the timer list can be modified) */ - ts->cb(ts->opaque); - } -} + if (i->dev) { + devpath = qdev_get_fw_dev_path(i->dev); + assert(devpath); + } -int64_t qemu_get_clock(QEMUClock *clock) -{ - switch(clock->type) { - case QEMU_CLOCK_REALTIME: - return get_clock() / 1000000; - default: - case QEMU_CLOCK_VIRTUAL: - if (use_icount) { - return cpu_get_icount(); + if (i->suffix && devpath) { + size_t bootpathlen = strlen(devpath) + strlen(i->suffix) + 1; + + bootpath = qemu_malloc(bootpathlen); + snprintf(bootpath, bootpathlen, "%s%s", devpath, i->suffix); + qemu_free(devpath); + } else if (devpath) { + bootpath = devpath; } else { - return cpu_get_clock(); + bootpath = qemu_strdup(i->suffix); + assert(bootpath); } - case QEMU_CLOCK_HOST: - return get_clock_realtime(); - } -} - -static void init_clocks(void) -{ - init_get_clock(); - rt_clock = qemu_new_clock(QEMU_CLOCK_REALTIME); - vm_clock = qemu_new_clock(QEMU_CLOCK_VIRTUAL); - host_clock = qemu_new_clock(QEMU_CLOCK_HOST); - rtc_clock = host_clock; -} + if (total) { + list[total-1] = '\n'; + } + len = strlen(bootpath) + 1; + list = qemu_realloc(list, total + len); + memcpy(&list[total], bootpath, len); + total += len; + qemu_free(bootpath); + } -/* save a timer */ -void qemu_put_timer(QEMUFile *f, QEMUTimer *ts) -{ - uint64_t expire_time; + *size = total; - if (qemu_timer_pending(ts)) { - expire_time = ts->expire_time; - } else { - expire_time = -1; - } - qemu_put_be64(f, expire_time); + return list; } -void qemu_get_timer(QEMUFile *f, QEMUTimer *ts) +static void numa_add(const char *optarg) { - uint64_t expire_time; + char option[128]; + char *endptr; + unsigned long long value, endvalue; + int nodenr; - expire_time = qemu_get_be64(f); - if (expire_time != -1) { - qemu_mod_timer(ts, expire_time); - } else { - qemu_del_timer(ts); - } -} - -static const VMStateDescription vmstate_timers = { - .name = "timer", - .version_id = 2, - .minimum_version_id = 1, - .minimum_version_id_old = 1, - .fields = (VMStateField []) { - VMSTATE_INT64(cpu_ticks_offset, TimersState), - VMSTATE_INT64(dummy, TimersState), - VMSTATE_INT64_V(cpu_clock_offset, TimersState, 2), - VMSTATE_END_OF_LIST() - } -}; - -static void qemu_event_increment(void); - -#ifdef _WIN32 -static void CALLBACK host_alarm_handler(UINT uTimerID, UINT uMsg, - DWORD_PTR dwUser, DWORD_PTR dw1, - DWORD_PTR dw2) -#else -static void host_alarm_handler(int host_signum) -#endif -{ -#if 0 -#define DISP_FREQ 1000 - { - static int64_t delta_min = INT64_MAX; - static int64_t delta_max, delta_cum, last_clock, delta, ti; - static int count; - ti = qemu_get_clock(vm_clock); - if (last_clock != 0) { - delta = ti - last_clock; - if (delta < delta_min) - delta_min = delta; - if (delta > delta_max) - delta_max = delta; - delta_cum += delta; - if (++count == DISP_FREQ) { - printf("timer: min=%" PRId64 " us max=%" PRId64 " us avg=%" PRId64 " us avg_freq=%0.3f Hz\n", - muldiv64(delta_min, 1000000, get_ticks_per_sec()), - muldiv64(delta_max, 1000000, get_ticks_per_sec()), - muldiv64(delta_cum, 1000000 / DISP_FREQ, get_ticks_per_sec()), - (double)get_ticks_per_sec() / ((double)delta_cum / DISP_FREQ)); - count = 0; - delta_min = INT64_MAX; - delta_max = 0; - delta_cum = 0; - } - } - last_clock = ti; - } -#endif - if (alarm_has_dynticks(alarm_timer) || - (!use_icount && - qemu_timer_expired(active_timers[QEMU_CLOCK_VIRTUAL], - qemu_get_clock(vm_clock))) || - qemu_timer_expired(active_timers[QEMU_CLOCK_REALTIME], - qemu_get_clock(rt_clock)) || - qemu_timer_expired(active_timers[QEMU_CLOCK_HOST], - qemu_get_clock(host_clock))) { - qemu_event_increment(); - if (alarm_timer) alarm_timer->flags |= ALARM_FLAG_EXPIRED; - -#ifndef CONFIG_IOTHREAD - if (next_cpu) { - /* stop the currently executing cpu because a timer occured */ - cpu_exit(next_cpu); - } -#endif - timer_alarm_pending = 1; - qemu_notify_event(); - } -} - -static int64_t qemu_next_deadline(void) -{ - /* To avoid problems with overflow limit this to 2^32. */ - int64_t delta = INT32_MAX; - - if (active_timers[QEMU_CLOCK_VIRTUAL]) { - delta = active_timers[QEMU_CLOCK_VIRTUAL]->expire_time - - qemu_get_clock(vm_clock); - } - if (active_timers[QEMU_CLOCK_HOST]) { - int64_t hdelta = active_timers[QEMU_CLOCK_HOST]->expire_time - - qemu_get_clock(host_clock); - if (hdelta < delta) - delta = hdelta; - } - - if (delta < 0) - delta = 0; - - return delta; -} - -#if defined(__linux__) -static uint64_t qemu_next_deadline_dyntick(void) -{ - int64_t delta; - int64_t rtdelta; - - if (use_icount) - delta = INT32_MAX; - else - delta = (qemu_next_deadline() + 999) / 1000; - - if (active_timers[QEMU_CLOCK_REALTIME]) { - rtdelta = (active_timers[QEMU_CLOCK_REALTIME]->expire_time - - qemu_get_clock(rt_clock))*1000; - if (rtdelta < delta) - delta = rtdelta; - } - - if (delta < MIN_TIMER_REARM_US) - delta = MIN_TIMER_REARM_US; - - return delta; -} -#endif - -#ifndef _WIN32 - -/* Sets a specific flag */ -static int fcntl_setfl(int fd, int flag) -{ - int flags; - - flags = fcntl(fd, F_GETFL); - if (flags == -1) - return -errno; - - if (fcntl(fd, F_SETFL, flags | flag) == -1) - return -errno; - - return 0; -} - -#if defined(__linux__) - -#define RTC_FREQ 1024 - -static void enable_sigio_timer(int fd) -{ - struct sigaction act; - - /* timer signal */ - sigfillset(&act.sa_mask); - act.sa_flags = 0; - act.sa_handler = host_alarm_handler; - - sigaction(SIGIO, &act, NULL); - fcntl_setfl(fd, O_ASYNC); - fcntl(fd, F_SETOWN, getpid()); -} - -static int hpet_start_timer(struct qemu_alarm_timer *t) -{ - struct hpet_info info; - int r, fd; - - fd = qemu_open("/dev/hpet", O_RDONLY); - if (fd < 0) - return -1; - - /* Set frequency */ - r = ioctl(fd, HPET_IRQFREQ, RTC_FREQ); - if (r < 0) { - fprintf(stderr, "Could not configure '/dev/hpet' to have a 1024Hz timer. This is not a fatal\n" - "error, but for better emulation accuracy type:\n" - "'echo 1024 > /proc/sys/dev/hpet/max-user-freq' as root.\n"); - goto fail; - } - - /* Check capabilities */ - r = ioctl(fd, HPET_INFO, &info); - if (r < 0) - goto fail; - - /* Enable periodic mode */ - r = ioctl(fd, HPET_EPI, 0); - if (info.hi_flags && (r < 0)) - goto fail; - - /* Enable interrupt */ - r = ioctl(fd, HPET_IE_ON, 0); - if (r < 0) - goto fail; - - enable_sigio_timer(fd); - t->priv = (void *)(long)fd; - - return 0; -fail: - close(fd); - return -1; -} - -static void hpet_stop_timer(struct qemu_alarm_timer *t) -{ - int fd = (long)t->priv; - - close(fd); -} - -static int rtc_start_timer(struct qemu_alarm_timer *t) -{ - int rtc_fd; - unsigned long current_rtc_freq = 0; - - TFR(rtc_fd = qemu_open("/dev/rtc", O_RDONLY)); - if (rtc_fd < 0) - return -1; - ioctl(rtc_fd, RTC_IRQP_READ, ¤t_rtc_freq); - if (current_rtc_freq != RTC_FREQ && - ioctl(rtc_fd, RTC_IRQP_SET, RTC_FREQ) < 0) { - fprintf(stderr, "Could not configure '/dev/rtc' to have a 1024 Hz timer. This is not a fatal\n" - "error, but for better emulation accuracy either use a 2.6 host Linux kernel or\n" - "type 'echo 1024 > /proc/sys/dev/rtc/max-user-freq' as root.\n"); - goto fail; - } - if (ioctl(rtc_fd, RTC_PIE_ON, 0) < 0) { - fail: - close(rtc_fd); - return -1; - } - - enable_sigio_timer(rtc_fd); - - t->priv = (void *)(long)rtc_fd; - - return 0; -} - -static void rtc_stop_timer(struct qemu_alarm_timer *t) -{ - int rtc_fd = (long)t->priv; - - close(rtc_fd); -} - -static int dynticks_start_timer(struct qemu_alarm_timer *t) -{ - struct sigevent ev; - timer_t host_timer; - struct sigaction act; - - sigfillset(&act.sa_mask); - act.sa_flags = 0; - act.sa_handler = host_alarm_handler; - - sigaction(SIGALRM, &act, NULL); - - /* - * Initialize ev struct to 0 to avoid valgrind complaining - * about uninitialized data in timer_create call - */ - memset(&ev, 0, sizeof(ev)); - ev.sigev_value.sival_int = 0; - ev.sigev_notify = SIGEV_SIGNAL; - ev.sigev_signo = SIGALRM; - - if (timer_create(CLOCK_REALTIME, &ev, &host_timer)) { - perror("timer_create"); - - /* disable dynticks */ - fprintf(stderr, "Dynamic Ticks disabled\n"); - - return -1; - } - - t->priv = (void *)(long)host_timer; - - return 0; -} - -static void dynticks_stop_timer(struct qemu_alarm_timer *t) -{ - timer_t host_timer = (timer_t)(long)t->priv; - - timer_delete(host_timer); -} - -static void dynticks_rearm_timer(struct qemu_alarm_timer *t) -{ - timer_t host_timer = (timer_t)(long)t->priv; - struct itimerspec timeout; - int64_t nearest_delta_us = INT64_MAX; - int64_t current_us; - - if (!active_timers[QEMU_CLOCK_REALTIME] && - !active_timers[QEMU_CLOCK_VIRTUAL] && - !active_timers[QEMU_CLOCK_HOST]) - return; - - nearest_delta_us = qemu_next_deadline_dyntick(); - - /* check whether a timer is already running */ - if (timer_gettime(host_timer, &timeout)) { - perror("gettime"); - fprintf(stderr, "Internal timer error: aborting\n"); - exit(1); - } - current_us = timeout.it_value.tv_sec * 1000000 + timeout.it_value.tv_nsec/1000; - if (current_us && current_us <= nearest_delta_us) - return; - - timeout.it_interval.tv_sec = 0; - timeout.it_interval.tv_nsec = 0; /* 0 for one-shot timer */ - timeout.it_value.tv_sec = nearest_delta_us / 1000000; - timeout.it_value.tv_nsec = (nearest_delta_us % 1000000) * 1000; - if (timer_settime(host_timer, 0 /* RELATIVE */, &timeout, NULL)) { - perror("settime"); - fprintf(stderr, "Internal timer error: aborting\n"); - exit(1); - } -} - -#endif /* defined(__linux__) */ - -static int unix_start_timer(struct qemu_alarm_timer *t) -{ - struct sigaction act; - struct itimerval itv; - int err; - - /* timer signal */ - sigfillset(&act.sa_mask); - act.sa_flags = 0; - act.sa_handler = host_alarm_handler; - - sigaction(SIGALRM, &act, NULL); - - itv.it_interval.tv_sec = 0; - /* for i386 kernel 2.6 to get 1 ms */ - itv.it_interval.tv_usec = 999; - itv.it_value.tv_sec = 0; - itv.it_value.tv_usec = 10 * 1000; - - err = setitimer(ITIMER_REAL, &itv, NULL); - if (err) - return -1; - - return 0; -} - -static void unix_stop_timer(struct qemu_alarm_timer *t) -{ - struct itimerval itv; - - memset(&itv, 0, sizeof(itv)); - setitimer(ITIMER_REAL, &itv, NULL); -} - -#endif /* !defined(_WIN32) */ - - -#ifdef _WIN32 - -static int win32_start_timer(struct qemu_alarm_timer *t) -{ - TIMECAPS tc; - struct qemu_alarm_win32 *data = t->priv; - UINT flags; - - memset(&tc, 0, sizeof(tc)); - timeGetDevCaps(&tc, sizeof(tc)); - - if (data->period < tc.wPeriodMin) - data->period = tc.wPeriodMin; - - timeBeginPeriod(data->period); - - flags = TIME_CALLBACK_FUNCTION; - if (alarm_has_dynticks(t)) - flags |= TIME_ONESHOT; - else - flags |= TIME_PERIODIC; - - data->timerId = timeSetEvent(1, // interval (ms) - data->period, // resolution - host_alarm_handler, // function - (DWORD)t, // parameter - flags); - - if (!data->timerId) { - fprintf(stderr, "Failed to initialize win32 alarm timer: %ld\n", - GetLastError()); - timeEndPeriod(data->period); - return -1; - } - - return 0; -} - -static void win32_stop_timer(struct qemu_alarm_timer *t) -{ - struct qemu_alarm_win32 *data = t->priv; - - timeKillEvent(data->timerId); - timeEndPeriod(data->period); -} - -static void win32_rearm_timer(struct qemu_alarm_timer *t) -{ - struct qemu_alarm_win32 *data = t->priv; - - if (!active_timers[QEMU_CLOCK_REALTIME] && - !active_timers[QEMU_CLOCK_VIRTUAL] && - !active_timers[QEMU_CLOCK_HOST]) - return; - - timeKillEvent(data->timerId); - - data->timerId = timeSetEvent(1, - data->period, - host_alarm_handler, - (DWORD)t, - TIME_ONESHOT | TIME_PERIODIC); - - if (!data->timerId) { - fprintf(stderr, "Failed to re-arm win32 alarm timer %ld\n", - GetLastError()); - - timeEndPeriod(data->period); - exit(1); - } -} - -#endif /* _WIN32 */ - -static int init_timer_alarm(void) -{ - struct qemu_alarm_timer *t = NULL; - int i, err = -1; - - for (i = 0; alarm_timers[i].name; i++) { - t = &alarm_timers[i]; - - err = t->start(t); - if (!err) - break; - } - - if (err) { - err = -ENOENT; - goto fail; - } - - alarm_timer = t; - - return 0; - -fail: - return err; -} - -static void quit_timers(void) -{ - alarm_timer->stop(alarm_timer); - alarm_timer = NULL; -} - -/***********************************************************/ -/* host time/date access */ -void qemu_get_timedate(struct tm *tm, int offset) -{ - time_t ti; - struct tm *ret; - - time(&ti); - ti += offset; - if (rtc_date_offset == -1) { - if (rtc_utc) - ret = gmtime(&ti); - else - ret = localtime(&ti); - } else { - ti -= rtc_date_offset; - ret = gmtime(&ti); - } - - memcpy(tm, ret, sizeof(struct tm)); -} - -int qemu_timedate_diff(struct tm *tm) -{ - time_t seconds; - - if (rtc_date_offset == -1) - if (rtc_utc) - seconds = mktimegm(tm); - else - seconds = mktime(tm); - else - seconds = mktimegm(tm) + rtc_date_offset; - - return seconds - time(NULL); -} - -static void configure_rtc_date_offset(const char *startdate, int legacy) -{ - time_t rtc_start_date; - struct tm tm; - - if (!strcmp(startdate, "now") && legacy) { - rtc_date_offset = -1; - } else { - if (sscanf(startdate, "%d-%d-%dT%d:%d:%d", - &tm.tm_year, - &tm.tm_mon, - &tm.tm_mday, - &tm.tm_hour, - &tm.tm_min, - &tm.tm_sec) == 6) { - /* OK */ - } else if (sscanf(startdate, "%d-%d-%d", - &tm.tm_year, - &tm.tm_mon, - &tm.tm_mday) == 3) { - tm.tm_hour = 0; - tm.tm_min = 0; - tm.tm_sec = 0; - } else { - goto date_fail; - } - tm.tm_year -= 1900; - tm.tm_mon--; - rtc_start_date = mktimegm(&tm); - if (rtc_start_date == -1) { - date_fail: - fprintf(stderr, "Invalid date format. Valid formats are:\n" - "'2006-06-17T16:01:21' or '2006-06-17'\n"); - exit(1); - } - rtc_date_offset = time(NULL) - rtc_start_date; - } -} - -static void configure_rtc(QemuOpts *opts) -{ - const char *value; - - value = qemu_opt_get(opts, "base"); - if (value) { - if (!strcmp(value, "utc")) { - rtc_utc = 1; - } else if (!strcmp(value, "localtime")) { - rtc_utc = 0; - } else { - configure_rtc_date_offset(value, 0); - } - } - value = qemu_opt_get(opts, "clock"); - if (value) { - if (!strcmp(value, "host")) { - rtc_clock = host_clock; - } else if (!strcmp(value, "vm")) { - rtc_clock = vm_clock; - } else { - fprintf(stderr, "qemu: invalid option value '%s'\n", value); - exit(1); - } - } -#ifdef CONFIG_TARGET_I386 - value = qemu_opt_get(opts, "driftfix"); - if (value) { - if (!strcmp(buf, "slew")) { - rtc_td_hack = 1; - } else if (!strcmp(buf, "none")) { - rtc_td_hack = 0; - } else { - fprintf(stderr, "qemu: invalid option value '%s'\n", value); - exit(1); - } - } -#endif -} - -#ifdef _WIN32 -static void socket_cleanup(void) -{ - WSACleanup(); -} - -static int socket_init(void) -{ - WSADATA Data; - int ret, err; - - ret = WSAStartup(MAKEWORD(2,2), &Data); - if (ret != 0) { - err = WSAGetLastError(); - fprintf(stderr, "WSAStartup: %d\n", err); - return -1; - } - atexit(socket_cleanup); - return 0; -} -#endif - -/***********************************************************/ -/* Bluetooth support */ -static int nb_hcis; -static int cur_hci; -static struct HCIInfo *hci_table[MAX_NICS]; - -static struct bt_vlan_s { - struct bt_scatternet_s net; - int id; - struct bt_vlan_s *next; -} *first_bt_vlan; - -/* find or alloc a new bluetooth "VLAN" */ -static struct bt_scatternet_s *qemu_find_bt_vlan(int id) -{ - struct bt_vlan_s **pvlan, *vlan; - for (vlan = first_bt_vlan; vlan != NULL; vlan = vlan->next) { - if (vlan->id == id) - return &vlan->net; - } - vlan = qemu_mallocz(sizeof(struct bt_vlan_s)); - vlan->id = id; - pvlan = &first_bt_vlan; - while (*pvlan != NULL) - pvlan = &(*pvlan)->next; - *pvlan = vlan; - return &vlan->net; -} - -static void null_hci_send(struct HCIInfo *hci, const uint8_t *data, int len) -{ -} - -static int null_hci_addr_set(struct HCIInfo *hci, const uint8_t *bd_addr) -{ - return -ENOTSUP; -} - -static struct HCIInfo null_hci = { - .cmd_send = null_hci_send, - .sco_send = null_hci_send, - .acl_send = null_hci_send, - .bdaddr_set = null_hci_addr_set, -}; - -struct HCIInfo *qemu_next_hci(void) -{ - if (cur_hci == nb_hcis) - return &null_hci; - - return hci_table[cur_hci++]; -} - -static struct HCIInfo *hci_init(const char *str) -{ - char *endp; - struct bt_scatternet_s *vlan = 0; - - if (!strcmp(str, "null")) - /* null */ - return &null_hci; - else if (!strncmp(str, "host", 4) && (str[4] == '\0' || str[4] == ':')) - /* host[:hciN] */ - return bt_host_hci(str[4] ? str + 5 : "hci0"); - else if (!strncmp(str, "hci", 3)) { - /* hci[,vlan=n] */ - if (str[3]) { - if (!strncmp(str + 3, ",vlan=", 6)) { - vlan = qemu_find_bt_vlan(strtol(str + 9, &endp, 0)); - if (*endp) - vlan = 0; - } - } else - vlan = qemu_find_bt_vlan(0); - if (vlan) - return bt_new_hci(vlan); - } - - fprintf(stderr, "qemu: Unknown bluetooth HCI `%s'.\n", str); - - return 0; -} - -static int bt_hci_parse(const char *str) -{ - struct HCIInfo *hci; - bdaddr_t bdaddr; - - if (nb_hcis >= MAX_NICS) { - fprintf(stderr, "qemu: Too many bluetooth HCIs (max %i).\n", MAX_NICS); - return -1; - } - - hci = hci_init(str); - if (!hci) - return -1; - - bdaddr.b[0] = 0x52; - bdaddr.b[1] = 0x54; - bdaddr.b[2] = 0x00; - bdaddr.b[3] = 0x12; - bdaddr.b[4] = 0x34; - bdaddr.b[5] = 0x56 + nb_hcis; - hci->bdaddr_set(hci, bdaddr.b); - - hci_table[nb_hcis++] = hci; - - return 0; -} - -static void bt_vhci_add(int vlan_id) -{ - struct bt_scatternet_s *vlan = qemu_find_bt_vlan(vlan_id); - - if (!vlan->slave) - fprintf(stderr, "qemu: warning: adding a VHCI to " - "an empty scatternet %i\n", vlan_id); - - bt_vhci_init(bt_new_hci(vlan)); -} - -static struct bt_device_s *bt_device_add(const char *opt) -{ - struct bt_scatternet_s *vlan; - int vlan_id = 0; - char *endp = strstr(opt, ",vlan="); - int len = (endp ? endp - opt : strlen(opt)) + 1; - char devname[10]; - - pstrcpy(devname, MIN(sizeof(devname), len), opt); - - if (endp) { - vlan_id = strtol(endp + 6, &endp, 0); - if (*endp) { - fprintf(stderr, "qemu: unrecognised bluetooth vlan Id\n"); - return 0; - } - } - - vlan = qemu_find_bt_vlan(vlan_id); - - if (!vlan->slave) - fprintf(stderr, "qemu: warning: adding a slave device to " - "an empty scatternet %i\n", vlan_id); - - if (!strcmp(devname, "keyboard")) - return bt_keyboard_init(vlan); - - fprintf(stderr, "qemu: unsupported bluetooth device `%s'\n", devname); - return 0; -} - -static int bt_parse(const char *opt) -{ - const char *endp, *p; - int vlan; - - if (strstart(opt, "hci", &endp)) { - if (!*endp || *endp == ',') { - if (*endp) - if (!strstart(endp, ",vlan=", 0)) - opt = endp + 1; - - return bt_hci_parse(opt); - } - } else if (strstart(opt, "vhci", &endp)) { - if (!*endp || *endp == ',') { - if (*endp) { - if (strstart(endp, ",vlan=", &p)) { - vlan = strtol(p, (char **) &endp, 0); - if (*endp) { - fprintf(stderr, "qemu: bad scatternet '%s'\n", p); - return 1; - } - } else { - fprintf(stderr, "qemu: bad parameter '%s'\n", endp + 1); - return 1; - } - } else - vlan = 0; - - bt_vhci_add(vlan); - return 0; - } - } else if (strstart(opt, "device:", &endp)) - return !bt_device_add(endp); - - fprintf(stderr, "qemu: bad bluetooth parameter '%s'\n", opt); - return 1; -} - -/***********************************************************/ -/* QEMU Block devices */ - -#define HD_ALIAS "index=%d,media=disk" -#define CDROM_ALIAS "index=2,media=cdrom" -#define FD_ALIAS "index=%d,if=floppy" -#define PFLASH_ALIAS "if=pflash" -#define MTD_ALIAS "if=mtd" -#define SD_ALIAS "index=0,if=sd" - -QemuOpts *drive_add(const char *file, const char *fmt, ...) -{ - va_list ap; - char optstr[1024]; - QemuOpts *opts; - - va_start(ap, fmt); - vsnprintf(optstr, sizeof(optstr), fmt, ap); - va_end(ap); - - opts = qemu_opts_parse(&qemu_drive_opts, optstr, NULL); - if (!opts) { - fprintf(stderr, "%s: huh? duplicate? (%s)\n", - __FUNCTION__, optstr); - return NULL; - } - if (file) - qemu_opt_set(opts, "file", file); - return opts; -} - -DriveInfo *drive_get(BlockInterfaceType type, int bus, int unit) -{ - DriveInfo *dinfo; - - /* seek interface, bus and unit */ - - QTAILQ_FOREACH(dinfo, &drives, next) { - if (dinfo->type == type && - dinfo->bus == bus && - dinfo->unit == unit) - return dinfo; - } - - return NULL; -} - -DriveInfo *drive_get_by_id(const char *id) -{ - DriveInfo *dinfo; - - QTAILQ_FOREACH(dinfo, &drives, next) { - if (strcmp(id, dinfo->id)) - continue; - return dinfo; - } - return NULL; -} - -int drive_get_max_bus(BlockInterfaceType type) -{ - int max_bus; - DriveInfo *dinfo; - - max_bus = -1; - QTAILQ_FOREACH(dinfo, &drives, next) { - if(dinfo->type == type && - dinfo->bus > max_bus) - max_bus = dinfo->bus; - } - return max_bus; -} - -const char *drive_get_serial(BlockDriverState *bdrv) -{ - DriveInfo *dinfo; - - QTAILQ_FOREACH(dinfo, &drives, next) { - if (dinfo->bdrv == bdrv) - return dinfo->serial; - } - - return "\0"; -} - -BlockInterfaceErrorAction drive_get_on_error( - BlockDriverState *bdrv, int is_read) -{ - DriveInfo *dinfo; - - QTAILQ_FOREACH(dinfo, &drives, next) { - if (dinfo->bdrv == bdrv) - return is_read ? dinfo->on_read_error : dinfo->on_write_error; - } - - return is_read ? BLOCK_ERR_REPORT : BLOCK_ERR_STOP_ENOSPC; -} - -static void bdrv_format_print(void *opaque, const char *name) -{ - fprintf(stderr, " %s", name); -} - -void drive_uninit(DriveInfo *dinfo) -{ - qemu_opts_del(dinfo->opts); - bdrv_delete(dinfo->bdrv); - QTAILQ_REMOVE(&drives, dinfo, next); - qemu_free(dinfo); -} - -static int parse_block_error_action(const char *buf, int is_read) -{ - if (!strcmp(buf, "ignore")) { - return BLOCK_ERR_IGNORE; - } else if (!is_read && !strcmp(buf, "enospc")) { - return BLOCK_ERR_STOP_ENOSPC; - } else if (!strcmp(buf, "stop")) { - return BLOCK_ERR_STOP_ANY; - } else if (!strcmp(buf, "report")) { - return BLOCK_ERR_REPORT; - } else { - fprintf(stderr, "qemu: '%s' invalid %s error action\n", - buf, is_read ? "read" : "write"); - return -1; - } -} - -DriveInfo *drive_init(QemuOpts *opts, void *opaque, - int *fatal_error) -{ - const char *buf; - const char *file = NULL; - char devname[128]; - const char *serial; - const char *mediastr = ""; - BlockInterfaceType type; - enum { MEDIA_DISK, MEDIA_CDROM } media; - int bus_id, unit_id; - int cyls, heads, secs, translation; - BlockDriver *drv = NULL; - QEMUMachine *machine = opaque; - int max_devs; - int index; - int cache; - int aio = 0; - int ro = 0; - int bdrv_flags; - int on_read_error, on_write_error; - const char *devaddr; - DriveInfo *dinfo; - int is_extboot = 0; - int snapshot = 0; - - *fatal_error = 1; - - translation = BIOS_ATA_TRANSLATION_AUTO; - cache = 1; - - if (machine && machine->use_scsi) { - type = IF_SCSI; - max_devs = MAX_SCSI_DEVS; - pstrcpy(devname, sizeof(devname), "scsi"); - } else { - type = IF_IDE; - max_devs = MAX_IDE_DEVS; - pstrcpy(devname, sizeof(devname), "ide"); - } - media = MEDIA_DISK; - - /* extract parameters */ - bus_id = qemu_opt_get_number(opts, "bus", 0); - unit_id = qemu_opt_get_number(opts, "unit", -1); - index = qemu_opt_get_number(opts, "index", -1); - - cyls = qemu_opt_get_number(opts, "cyls", 0); - heads = qemu_opt_get_number(opts, "heads", 0); - secs = qemu_opt_get_number(opts, "secs", 0); - - snapshot = qemu_opt_get_bool(opts, "snapshot", 0); - ro = qemu_opt_get_bool(opts, "readonly", 0); - - file = qemu_opt_get(opts, "file"); - serial = qemu_opt_get(opts, "serial"); - - if ((buf = qemu_opt_get(opts, "if")) != NULL) { - pstrcpy(devname, sizeof(devname), buf); - if (!strcmp(buf, "ide")) { - type = IF_IDE; - max_devs = MAX_IDE_DEVS; - } else if (!strcmp(buf, "scsi")) { - type = IF_SCSI; - max_devs = MAX_SCSI_DEVS; - } else if (!strcmp(buf, "floppy")) { - type = IF_FLOPPY; - max_devs = 0; - } else if (!strcmp(buf, "pflash")) { - type = IF_PFLASH; - max_devs = 0; - } else if (!strcmp(buf, "mtd")) { - type = IF_MTD; - max_devs = 0; - } else if (!strcmp(buf, "sd")) { - type = IF_SD; - max_devs = 0; - } else if (!strcmp(buf, "virtio")) { - type = IF_VIRTIO; - max_devs = 0; - } else if (!strcmp(buf, "xen")) { - type = IF_XEN; - max_devs = 0; - } else if (!strcmp(buf, "none")) { - type = IF_NONE; - max_devs = 0; - } else { - fprintf(stderr, "qemu: unsupported bus type '%s'\n", buf); - return NULL; - } - } - - if (cyls || heads || secs) { - if (cyls < 1 || (type == IF_IDE && cyls > 16383)) { - fprintf(stderr, "qemu: '%s' invalid physical cyls number\n", buf); - return NULL; - } - if (heads < 1 || (type == IF_IDE && heads > 16)) { - fprintf(stderr, "qemu: '%s' invalid physical heads number\n", buf); - return NULL; - } - if (secs < 1 || (type == IF_IDE && secs > 63)) { - fprintf(stderr, "qemu: '%s' invalid physical secs number\n", buf); - return NULL; - } - } - - if ((buf = qemu_opt_get(opts, "trans")) != NULL) { - if (!cyls) { - fprintf(stderr, - "qemu: '%s' trans must be used with cyls,heads and secs\n", - buf); - return NULL; - } - if (!strcmp(buf, "none")) - translation = BIOS_ATA_TRANSLATION_NONE; - else if (!strcmp(buf, "lba")) - translation = BIOS_ATA_TRANSLATION_LBA; - else if (!strcmp(buf, "auto")) - translation = BIOS_ATA_TRANSLATION_AUTO; - else { - fprintf(stderr, "qemu: '%s' invalid translation type\n", buf); - return NULL; - } - } - - if ((buf = qemu_opt_get(opts, "media")) != NULL) { - if (!strcmp(buf, "disk")) { - media = MEDIA_DISK; - } else if (!strcmp(buf, "cdrom")) { - if (cyls || secs || heads) { - fprintf(stderr, - "qemu: '%s' invalid physical CHS format\n", buf); - return NULL; - } - media = MEDIA_CDROM; - } else { - fprintf(stderr, "qemu: '%s' invalid media\n", buf); - return NULL; - } - } - - if ((buf = qemu_opt_get(opts, "cache")) != NULL) { - if (!strcmp(buf, "off") || !strcmp(buf, "none")) - cache = 0; - else if (!strcmp(buf, "writethrough")) - cache = 1; - else if (!strcmp(buf, "writeback")) - cache = 2; - else { - fprintf(stderr, "qemu: invalid cache option\n"); - return NULL; - } - } - -#ifdef CONFIG_LINUX_AIO - if ((buf = qemu_opt_get(opts, "aio")) != NULL) { - if (!strcmp(buf, "threads")) - aio = 0; - else if (!strcmp(buf, "native")) - aio = 1; - else { - fprintf(stderr, "qemu: invalid aio option\n"); - return NULL; - } - } -#endif - - if ((buf = qemu_opt_get(opts, "format")) != NULL) { - if (strcmp(buf, "?") == 0) { - fprintf(stderr, "qemu: Supported formats:"); - bdrv_iterate_format(bdrv_format_print, NULL); - fprintf(stderr, "\n"); - return NULL; - } - drv = bdrv_find_whitelisted_format(buf); - if (!drv) { - fprintf(stderr, "qemu: '%s' invalid format\n", buf); - return NULL; - } - } - - is_extboot = qemu_opt_get_bool(opts, "boot", 0); - if (is_extboot && extboot_drive) { - fprintf(stderr, "qemu: two bootable drives specified\n"); - return NULL; - } - - on_write_error = BLOCK_ERR_STOP_ENOSPC; - if ((buf = qemu_opt_get(opts, "werror")) != NULL) { - if (type != IF_IDE && type != IF_SCSI && type != IF_VIRTIO) { - fprintf(stderr, "werror is no supported by this format\n"); - return NULL; - } - - on_write_error = parse_block_error_action(buf, 0); - if (on_write_error < 0) { - return NULL; - } - } - - on_read_error = BLOCK_ERR_REPORT; - if ((buf = qemu_opt_get(opts, "rerror")) != NULL) { - if (type != IF_IDE && type != IF_VIRTIO) { - fprintf(stderr, "rerror is no supported by this format\n"); - return NULL; - } - - on_read_error = parse_block_error_action(buf, 1); - if (on_read_error < 0) { - return NULL; - } - } - - if ((devaddr = qemu_opt_get(opts, "addr")) != NULL) { - if (type != IF_VIRTIO) { - fprintf(stderr, "addr is not supported\n"); - return NULL; - } - } - - /* compute bus and unit according index */ - - if (index != -1) { - if (bus_id != 0 || unit_id != -1) { - fprintf(stderr, - "qemu: index cannot be used with bus and unit\n"); - return NULL; - } - if (max_devs == 0) - { - unit_id = index; - bus_id = 0; - } else { - unit_id = index % max_devs; - bus_id = index / max_devs; - } - } - - /* if user doesn't specify a unit_id, - * try to find the first free - */ - - if (unit_id == -1) { - unit_id = 0; - while (drive_get(type, bus_id, unit_id) != NULL) { - unit_id++; - if (max_devs && unit_id >= max_devs) { - unit_id -= max_devs; - bus_id++; - } - } - } - - /* check unit id */ - - if (max_devs && unit_id >= max_devs) { - fprintf(stderr, "qemu: unit %d too big (max is %d)\n", - unit_id, max_devs - 1); - return NULL; - } - - /* - * ignore multiple definitions - */ - - if (drive_get(type, bus_id, unit_id) != NULL) { - *fatal_error = 0; - return NULL; - } - - /* init */ - - dinfo = qemu_mallocz(sizeof(*dinfo)); - if ((buf = qemu_opts_id(opts)) != NULL) { - dinfo->id = qemu_strdup(buf); - } else { - /* no id supplied -> create one */ - dinfo->id = qemu_mallocz(32); - if (type == IF_IDE || type == IF_SCSI) - mediastr = (media == MEDIA_CDROM) ? "-cd" : "-hd"; - if (max_devs) - snprintf(dinfo->id, 32, "%s%i%s%i", - devname, bus_id, mediastr, unit_id); - else - snprintf(dinfo->id, 32, "%s%s%i", - devname, mediastr, unit_id); - } - dinfo->bdrv = bdrv_new(dinfo->id); - dinfo->devaddr = devaddr; - dinfo->type = type; - dinfo->bus = bus_id; - dinfo->unit = unit_id; - dinfo->on_read_error = on_read_error; - dinfo->on_write_error = on_write_error; - dinfo->opts = opts; - if (serial) - strncpy(dinfo->serial, serial, sizeof(serial)); - QTAILQ_INSERT_TAIL(&drives, dinfo, next); - if (is_extboot) { - extboot_drive = dinfo; - } - - switch(type) { - case IF_IDE: - case IF_SCSI: - case IF_XEN: - case IF_NONE: - switch(media) { - case MEDIA_DISK: - if (cyls != 0) { - bdrv_set_geometry_hint(dinfo->bdrv, cyls, heads, secs); - bdrv_set_translation_hint(dinfo->bdrv, translation); - } - break; - case MEDIA_CDROM: - bdrv_set_type_hint(dinfo->bdrv, BDRV_TYPE_CDROM); - break; - } - break; - case IF_SD: - /* FIXME: This isn't really a floppy, but it's a reasonable - approximation. */ - case IF_FLOPPY: - bdrv_set_type_hint(dinfo->bdrv, BDRV_TYPE_FLOPPY); - break; - case IF_PFLASH: - case IF_MTD: - break; - case IF_VIRTIO: - /* add virtio block device */ - opts = qemu_opts_create(&qemu_device_opts, NULL, 0); - qemu_opt_set(opts, "driver", "virtio-blk-pci"); - qemu_opt_set(opts, "drive", dinfo->id); - if (devaddr) - qemu_opt_set(opts, "addr", devaddr); - break; - case IF_COUNT: - abort(); - } - if (!file) { - *fatal_error = 0; - return NULL; - } - bdrv_flags = 0; - if (snapshot) { - bdrv_flags |= BDRV_O_SNAPSHOT; - cache = 2; /* always use write-back with snapshot */ - } - if (cache == 0) /* no caching */ - bdrv_flags |= BDRV_O_NOCACHE; - else if (cache == 2) /* write-back */ - bdrv_flags |= BDRV_O_CACHE_WB; - - if (aio == 1) { - bdrv_flags |= BDRV_O_NATIVE_AIO; - } else { - bdrv_flags &= ~BDRV_O_NATIVE_AIO; - } - - if (ro == 1) { - if (type == IF_IDE) { - fprintf(stderr, "qemu: readonly flag not supported for drive with ide interface\n"); - return NULL; - } - (void)bdrv_set_read_only(dinfo->bdrv, 1); - } - - if (bdrv_open2(dinfo->bdrv, file, bdrv_flags, drv) < 0) { - fprintf(stderr, "qemu: could not open disk image %s: %s\n", - file, strerror(errno)); - return NULL; - } - - if (bdrv_key_required(dinfo->bdrv)) - autostart = 0; - *fatal_error = 0; - return dinfo; -} - -static int drive_init_func(QemuOpts *opts, void *opaque) -{ - QEMUMachine *machine = opaque; - int fatal_error = 0; - - if (drive_init(opts, machine, &fatal_error) == NULL) { - if (fatal_error) - return 1; - } - return 0; -} - -static int drive_enable_snapshot(QemuOpts *opts, void *opaque) -{ - if (NULL == qemu_opt_get(opts, "snapshot")) { - qemu_opt_set(opts, "snapshot", "on"); - } - return 0; -} - -void qemu_register_boot_set(QEMUBootSetHandler *func, void *opaque) -{ - boot_set_handler = func; - boot_set_opaque = opaque; -} - -int qemu_boot_set(const char *boot_devices) -{ - if (!boot_set_handler) { - return -EINVAL; - } - return boot_set_handler(boot_set_opaque, boot_devices); -} - -static int parse_bootdevices(char *devices) -{ - /* We just do some generic consistency checks */ - const char *p; - int bitmap = 0; - - for (p = devices; *p != '\0'; p++) { - /* Allowed boot devices are: - * a-b: floppy disk drives - * c-f: IDE disk drives - * g-m: machine implementation dependant drives - * n-p: network devices - * It's up to each machine implementation to check if the given boot - * devices match the actual hardware implementation and firmware - * features. - */ - if (*p < 'a' || *p > 'p') { - fprintf(stderr, "Invalid boot device '%c'\n", *p); - exit(1); - } - if (bitmap & (1 << (*p - 'a'))) { - fprintf(stderr, "Boot device '%c' was given twice\n", *p); - exit(1); - } - bitmap |= 1 << (*p - 'a'); - } - return bitmap; -} - -static void restore_boot_devices(void *opaque) -{ - char *standard_boot_devices = opaque; - - qemu_boot_set(standard_boot_devices); - - qemu_unregister_reset(restore_boot_devices, standard_boot_devices); - qemu_free(standard_boot_devices); -} - -static void numa_add(const char *optarg) -{ - char option[128]; - char *endptr; - unsigned long long value, endvalue; - int nodenr; - - optarg = get_opt_name(option, 128, optarg, ',') + 1; - if (!strcmp(option, "node")) { - if (get_param_value(option, 128, "nodeid", optarg) == 0) { - nodenr = nb_numa_nodes; - } else { - nodenr = strtoull(option, NULL, 10); - } - - if (get_param_value(option, 128, "mem", optarg) == 0) { - node_mem[nodenr] = 0; - } else { - value = strtoull(option, &endptr, 0); - switch (*endptr) { - case 0: case 'M': case 'm': - value <<= 20; - break; - case 'G': case 'g': - value <<= 30; - break; - } - node_mem[nodenr] = value; - } - if (get_param_value(option, 128, "cpus", optarg) == 0) { - node_cpumask[nodenr] = 0; - } else { - value = strtoull(option, &endptr, 10); - if (value >= 64) { - value = 63; - fprintf(stderr, "only 64 CPUs in NUMA mode supported.\n"); - } else { - if (*endptr == '-') { - endvalue = strtoull(endptr+1, &endptr, 10); - if (endvalue >= 63) { - endvalue = 62; - fprintf(stderr, - "only 63 CPUs in NUMA mode supported.\n"); - } - value = (2ULL << endvalue) - (1ULL << value); - } else { - value = 1ULL << value; - } - } - node_cpumask[nodenr] = value; - } - nb_numa_nodes++; - } - return; -} - -static void smp_parse(const char *optarg) -{ - int smp, sockets = 0, threads = 0, cores = 0; - char *endptr; - char option[128]; - - smp = strtoul(optarg, &endptr, 10); - if (endptr != optarg) { - if (*endptr == ',') { - endptr++; - } - } - if (get_param_value(option, 128, "sockets", endptr) != 0) - sockets = strtoull(option, NULL, 10); - if (get_param_value(option, 128, "cores", endptr) != 0) - cores = strtoull(option, NULL, 10); - if (get_param_value(option, 128, "threads", endptr) != 0) - threads = strtoull(option, NULL, 10); - if (get_param_value(option, 128, "maxcpus", endptr) != 0) - max_cpus = strtoull(option, NULL, 10); - - /* compute missing values, prefer sockets over cores over threads */ - if (smp == 0 || sockets == 0) { - sockets = sockets > 0 ? sockets : 1; - cores = cores > 0 ? cores : 1; - threads = threads > 0 ? threads : 1; - if (smp == 0) { - smp = cores * threads * sockets; - } else { - sockets = smp / (cores * threads); - } - } else { - if (cores == 0) { - threads = threads > 0 ? threads : 1; - cores = smp / (sockets * threads); - } else { - if (sockets == 0) { - sockets = smp / (cores * threads); - } else { - threads = smp / (cores * sockets); - } - } - } - smp_cpus = smp; - smp_cores = cores > 0 ? cores : 1; - smp_threads = threads > 0 ? threads : 1; - if (max_cpus == 0) - max_cpus = smp_cpus; -} - -/***********************************************************/ -/* USB devices */ - -static int usb_device_add(const char *devname, int is_hotplug) -{ - const char *p; - USBDevice *dev = NULL; - - if (!usb_enabled) - return -1; - - /* drivers with .usbdevice_name entry in USBDeviceInfo */ - dev = usbdevice_create(devname); - if (dev) - goto done; - - /* the other ones */ - if (strstart(devname, "host:", &p)) { - dev = usb_host_device_open(p); - } else if (!strcmp(devname, "bt") || strstart(devname, "bt:", &p)) { - dev = usb_bt_init(devname[2] ? hci_init(p) : - bt_new_hci(qemu_find_bt_vlan(0))); - } else { - return -1; - } - if (!dev) - return -1; - -done: - return 0; -} - -static int usb_device_del(const char *devname) -{ - int bus_num, addr; - const char *p; - - if (strstart(devname, "host:", &p)) - return usb_host_device_close(p); - - if (!usb_enabled) - return -1; - - p = strchr(devname, '.'); - if (!p) - return -1; - bus_num = strtoul(devname, NULL, 0); - addr = strtoul(p + 1, NULL, 0); - - return usb_device_delete_addr(bus_num, addr); -} - -static int usb_parse(const char *cmdline) -{ - int r; - r = usb_device_add(cmdline, 0); - if (r < 0) { - fprintf(stderr, "qemu: could not add USB device '%s'\n", cmdline); - } - return r; -} - -void do_usb_add(Monitor *mon, const QDict *qdict) -{ - const char *devname = qdict_get_str(qdict, "devname"); - if (usb_device_add(devname, 1) < 0) { - qemu_error("could not add USB device '%s'\n", devname); - } -} - -void do_usb_del(Monitor *mon, const QDict *qdict) -{ - const char *devname = qdict_get_str(qdict, "devname"); - if (usb_device_del(devname) < 0) { - qemu_error("could not delete USB device '%s'\n", devname); - } -} - -/***********************************************************/ -/* PCMCIA/Cardbus */ - -static struct pcmcia_socket_entry_s { - PCMCIASocket *socket; - struct pcmcia_socket_entry_s *next; -} *pcmcia_sockets = 0; - -void pcmcia_socket_register(PCMCIASocket *socket) -{ - struct pcmcia_socket_entry_s *entry; - - entry = qemu_malloc(sizeof(struct pcmcia_socket_entry_s)); - entry->socket = socket; - entry->next = pcmcia_sockets; - pcmcia_sockets = entry; -} - -void pcmcia_socket_unregister(PCMCIASocket *socket) -{ - struct pcmcia_socket_entry_s *entry, **ptr; - - ptr = &pcmcia_sockets; - for (entry = *ptr; entry; ptr = &entry->next, entry = *ptr) - if (entry->socket == socket) { - *ptr = entry->next; - qemu_free(entry); - } -} - -void pcmcia_info(Monitor *mon) -{ - struct pcmcia_socket_entry_s *iter; - - if (!pcmcia_sockets) - monitor_printf(mon, "No PCMCIA sockets\n"); - - for (iter = pcmcia_sockets; iter; iter = iter->next) - monitor_printf(mon, "%s: %s\n", iter->socket->slot_string, - iter->socket->attached ? iter->socket->card_string : - "Empty"); -} - -/***********************************************************/ -/* register display */ - -struct DisplayAllocator default_allocator = { - defaultallocator_create_displaysurface, - defaultallocator_resize_displaysurface, - defaultallocator_free_displaysurface -}; - -void register_displaystate(DisplayState *ds) -{ - DisplayState **s; - s = &display_state; - while (*s != NULL) - s = &(*s)->next; - ds->next = NULL; - *s = ds; -} - -DisplayState *get_displaystate(void) -{ - return display_state; -} - -DisplayAllocator *register_displayallocator(DisplayState *ds, DisplayAllocator *da) -{ - if(ds->allocator == &default_allocator) ds->allocator = da; - return ds->allocator; -} - -/* dumb display */ - -static void dumb_display_init(void) -{ - DisplayState *ds = qemu_mallocz(sizeof(DisplayState)); - ds->allocator = &default_allocator; - ds->surface = qemu_create_displaysurface(ds, 640, 480); - register_displaystate(ds); -} - -/***********************************************************/ -/* I/O handling */ - -typedef struct IOHandlerRecord { - int fd; - IOCanRWHandler *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, - IOCanRWHandler *fd_read_poll, - IOHandler *fd_read, - IOHandler *fd_write, - void *opaque) -{ - IOHandlerRecord **pioh, *ioh; - - if (!fd_read && !fd_write) { - pioh = &first_io_handler; - for(;;) { - ioh = *pioh; - if (ioh == NULL) - break; - if (ioh->fd == fd) { - ioh->deleted = 1; - break; - } - pioh = &ioh->next; - } - } else { - for(ioh = first_io_handler; ioh != NULL; ioh = ioh->next) { - if (ioh->fd == fd) - goto found; - } - ioh = qemu_mallocz(sizeof(IOHandlerRecord)); - 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; - } - qemu_notify_event(); - 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); -} - -#ifdef _WIN32 -/***********************************************************/ -/* 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)); - 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; - } - } -} - -/***********************************************************/ -/* Wait objects support */ -typedef struct WaitObjects { - int num; - HANDLE events[MAXIMUM_WAIT_OBJECTS + 1]; - WaitObjectFunc *func[MAXIMUM_WAIT_OBJECTS + 1]; - void *opaque[MAXIMUM_WAIT_OBJECTS + 1]; -} WaitObjects; - -static WaitObjects wait_objects = {0}; - -int qemu_add_wait_object(HANDLE handle, WaitObjectFunc *func, void *opaque) -{ - WaitObjects *w = &wait_objects; - - if (w->num >= MAXIMUM_WAIT_OBJECTS) - return -1; - w->events[w->num] = handle; - w->func[w->num] = func; - w->opaque[w->num] = opaque; - w->num++; - return 0; -} - -void qemu_del_wait_object(HANDLE handle, WaitObjectFunc *func, void *opaque) -{ - int i, found; - WaitObjects *w = &wait_objects; - - found = 0; - for (i = 0; i < w->num; i++) { - if (w->events[i] == handle) - found = 1; - if (found) { - w->events[i] = w->events[i + 1]; - w->func[i] = w->func[i + 1]; - w->opaque[i] = w->opaque[i + 1]; - } - } - if (found) - w->num--; -} -#endif - -/***********************************************************/ -/* 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 (kvm_enabled() && current_addr == 0) { - int r; - r = kvm_update_dirty_pages_log(); - if (r) { - fprintf(stderr, "%s: update dirty pages log failed %d\n", __FUNCTION__, r); - qemu_file_set_error(f); - return 0; - } - } - 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; -} - -static 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 = get_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 = get_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()); -} - -static 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; + optarg = get_opt_name(option, 128, optarg, ',') + 1; + if (!strcmp(option, "node")) { + if (get_param_value(option, 128, "nodeid", optarg) == 0) { + nodenr = nb_numa_nodes; + } else { + nodenr = strtoull(option, NULL, 10); } - 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); + if (get_param_value(option, 128, "mem", optarg) == 0) { + node_mem[nodenr] = 0; + } else { + int64_t sval; + sval = strtosz(option, NULL); + if (sval < 0) { + fprintf(stderr, "qemu: invalid numa mem size: %s\n", optarg); + exit(1); } -#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(); -} - -/***********************************************************/ -/* machine registration */ - -static QEMUMachine *first_machine = NULL; -QEMUMachine *current_machine = NULL; - -int qemu_register_machine(QEMUMachine *m) -{ - QEMUMachine **pm; - pm = &first_machine; - while (*pm != NULL) - pm = &(*pm)->next; - m->next = NULL; - *pm = m; - return 0; -} - -static QEMUMachine *find_machine(const char *name) -{ - QEMUMachine *m; - - for(m = first_machine; m != NULL; m = m->next) { - if (!strcmp(m->name, name)) - return m; - if (m->alias && !strcmp(m->alias, name)) - return m; - } - return NULL; -} - -static QEMUMachine *find_default_machine(void) -{ - QEMUMachine *m; - - for(m = first_machine; m != NULL; m = m->next) { - if (m->is_default) { - return m; + node_mem[nodenr] = sval; } - } - return NULL; -} - -/***********************************************************/ -/* 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)); -} - -struct vm_change_state_entry { - VMChangeStateHandler *cb; - void *opaque; - QLIST_ENTRY (vm_change_state_entry) entries; -}; - -static QLIST_HEAD(vm_change_state_head, vm_change_state_entry) vm_change_state_head; - -VMChangeStateEntry *qemu_add_vm_change_state_handler(VMChangeStateHandler *cb, - void *opaque) -{ - VMChangeStateEntry *e; - - e = qemu_mallocz(sizeof (*e)); - - e->cb = cb; - e->opaque = opaque; - QLIST_INSERT_HEAD(&vm_change_state_head, e, entries); - return e; -} - -void qemu_del_vm_change_state_handler(VMChangeStateEntry *e) -{ - QLIST_REMOVE (e, entries); - qemu_free (e); -} - -static void vm_state_notify(int running, int reason) -{ - VMChangeStateEntry *e; - - for (e = vm_change_state_head.lh_first; e; e = e->entries.le_next) { - e->cb(e->opaque, running, reason); - } -} - -static void resume_all_vcpus(void); -static void pause_all_vcpus(void); - -void vm_start(void) -{ - if (!vm_running) { - cpu_enable_ticks(); - vm_running = 1; - vm_state_notify(1, 0); - qemu_rearm_alarm_timer(alarm_timer); - resume_all_vcpus(); - } -} - -/* reset/shutdown handler */ - -typedef struct QEMUResetEntry { - QTAILQ_ENTRY(QEMUResetEntry) entry; - QEMUResetHandler *func; - void *opaque; -} QEMUResetEntry; - -static QTAILQ_HEAD(reset_handlers, QEMUResetEntry) reset_handlers = - QTAILQ_HEAD_INITIALIZER(reset_handlers); -static int reset_requested; -static int shutdown_requested; -static int powerdown_requested; -static int debug_requested; -static int vmstop_requested; - -int qemu_no_shutdown(void) -{ - int r = no_shutdown; - no_shutdown = 0; - return r; -} - -int qemu_shutdown_requested(void) -{ - int r = shutdown_requested; - shutdown_requested = 0; - return r; -} - -int qemu_reset_requested(void) -{ - int r = reset_requested; - reset_requested = 0; - return r; -} - -int qemu_powerdown_requested(void) -{ - int r = powerdown_requested; - powerdown_requested = 0; - return r; -} - -static int qemu_debug_requested(void) -{ - int r = debug_requested; - debug_requested = 0; - return r; -} - -static int qemu_vmstop_requested(void) -{ - int r = vmstop_requested; - vmstop_requested = 0; - return r; -} - -static void do_vm_stop(int reason) -{ - if (vm_running) { - cpu_disable_ticks(); - vm_running = 0; - pause_all_vcpus(); - vm_state_notify(0, reason); - } -} - -void qemu_register_reset(QEMUResetHandler *func, void *opaque) -{ - QEMUResetEntry *re = qemu_mallocz(sizeof(QEMUResetEntry)); - - re->func = func; - re->opaque = opaque; - QTAILQ_INSERT_TAIL(&reset_handlers, re, entry); -} - -void qemu_unregister_reset(QEMUResetHandler *func, void *opaque) -{ - QEMUResetEntry *re; - - QTAILQ_FOREACH(re, &reset_handlers, entry) { - if (re->func == func && re->opaque == opaque) { - QTAILQ_REMOVE(&reset_handlers, re, entry); - qemu_free(re); - return; + if (get_param_value(option, 128, "cpus", optarg) == 0) { + node_cpumask[nodenr] = 0; + } else { + value = strtoull(option, &endptr, 10); + if (value >= 64) { + value = 63; + fprintf(stderr, "only 64 CPUs in NUMA mode supported.\n"); + } else { + if (*endptr == '-') { + endvalue = strtoull(endptr+1, &endptr, 10); + if (endvalue >= 63) { + endvalue = 62; + fprintf(stderr, + "only 63 CPUs in NUMA mode supported.\n"); + } + value = (2ULL << endvalue) - (1ULL << value); + } else { + value = 1ULL << value; + } + } + node_cpumask[nodenr] = value; } + nb_numa_nodes++; } + return; } -void qemu_system_reset(void) +static void smp_parse(const char *optarg) { - QEMUResetEntry *re, *nre; + int smp, sockets = 0, threads = 0, cores = 0; + char *endptr; + char option[128]; - /* reset all devices */ - QTAILQ_FOREACH_SAFE(re, &reset_handlers, entry, nre) { - re->func(re->opaque); + smp = strtoul(optarg, &endptr, 10); + if (endptr != optarg) { + if (*endptr == ',') { + endptr++; + } } -} + if (get_param_value(option, 128, "sockets", endptr) != 0) + sockets = strtoull(option, NULL, 10); + if (get_param_value(option, 128, "cores", endptr) != 0) + cores = strtoull(option, NULL, 10); + if (get_param_value(option, 128, "threads", endptr) != 0) + threads = strtoull(option, NULL, 10); + if (get_param_value(option, 128, "maxcpus", endptr) != 0) + max_cpus = strtoull(option, NULL, 10); -void qemu_system_reset_request(void) -{ - if (no_reboot) { - shutdown_requested = 1; + /* compute missing values, prefer sockets over cores over threads */ + if (smp == 0 || sockets == 0) { + sockets = sockets > 0 ? sockets : 1; + cores = cores > 0 ? cores : 1; + threads = threads > 0 ? threads : 1; + if (smp == 0) { + smp = cores * threads * sockets; + } } else { - reset_requested = 1; - } - if (cpu_single_env) { - cpu_single_env->stopped = 1; + if (cores == 0) { + threads = threads > 0 ? threads : 1; + cores = smp / (sockets * threads); + } else { + threads = smp / (cores * sockets); + } } - qemu_notify_event(); -} - -void qemu_system_shutdown_request(void) -{ - shutdown_requested = 1; - qemu_notify_event(); -} - -void qemu_system_powerdown_request(void) -{ - powerdown_requested = 1; - qemu_notify_event(); -} - -#ifdef CONFIG_IOTHREAD -static void qemu_system_vmstop_request(int reason) -{ - vmstop_requested = reason; - qemu_notify_event(); + smp_cpus = smp; + smp_cores = cores > 0 ? cores : 1; + smp_threads = threads > 0 ? threads : 1; + if (max_cpus == 0) + max_cpus = smp_cpus; } -#endif -#ifndef _WIN32 -static int io_thread_fd = -1; +/***********************************************************/ +/* USB devices */ -static void qemu_event_increment(void) +static int usb_device_add(const char *devname) { - static const char byte = 0; + const char *p; + USBDevice *dev = NULL; - if (io_thread_fd == -1) - return; + if (!usb_enabled) + return -1; - write(io_thread_fd, &byte, sizeof(byte)); -} + /* drivers with .usbdevice_name entry in USBDeviceInfo */ + dev = usbdevice_create(devname); + if (dev) + goto done; -static void qemu_event_read(void *opaque) -{ - int fd = (unsigned long)opaque; - ssize_t len; + /* the other ones */ + if (strstart(devname, "host:", &p)) { + dev = usb_host_device_open(p); + } else if (!strcmp(devname, "bt") || strstart(devname, "bt:", &p)) { + dev = usb_bt_init(devname[2] ? hci_init(p) : + bt_new_hci(qemu_find_bt_vlan(0))); + } else { + return -1; + } + if (!dev) + return -1; - /* Drain the notify pipe */ - do { - char buffer[512]; - len = read(fd, buffer, sizeof(buffer)); - } while ((len == -1 && errno == EINTR) || len > 0); +done: + return 0; } -static int qemu_event_init(void) +static int usb_device_del(const char *devname) { - int err; - int fds[2]; - - err = qemu_pipe(fds); - if (err == -1) - return -errno; - - err = fcntl_setfl(fds[0], O_NONBLOCK); - if (err < 0) - goto fail; - - err = fcntl_setfl(fds[1], O_NONBLOCK); - if (err < 0) - goto fail; + int bus_num, addr; + const char *p; - qemu_set_fd_handler2(fds[0], NULL, qemu_event_read, NULL, - (void *)(unsigned long)fds[0]); + if (strstart(devname, "host:", &p)) + return usb_host_device_close(p); - io_thread_fd = fds[1]; - return 0; + if (!usb_enabled) + return -1; -fail: - close(fds[0]); - close(fds[1]); - return err; -} -#else -HANDLE qemu_event_handle; + p = strchr(devname, '.'); + if (!p) + return -1; + bus_num = strtoul(devname, NULL, 0); + addr = strtoul(p + 1, NULL, 0); -static void dummy_event_handler(void *opaque) -{ + return usb_device_delete_addr(bus_num, addr); } -static int qemu_event_init(void) +static int usb_parse(const char *cmdline) { - qemu_event_handle = CreateEvent(NULL, FALSE, FALSE, NULL); - if (!qemu_event_handle) { - fprintf(stderr, "Failed CreateEvent: %ld\n", GetLastError()); - return -1; + int r; + r = usb_device_add(cmdline); + if (r < 0) { + fprintf(stderr, "qemu: could not add USB device '%s'\n", cmdline); } - qemu_add_wait_object(qemu_event_handle, dummy_event_handler, NULL); - return 0; + return r; } -static void qemu_event_increment(void) +void do_usb_add(Monitor *mon, const QDict *qdict) { - if (!SetEvent(qemu_event_handle)) { - fprintf(stderr, "qemu_event_increment: SetEvent failed: %ld\n", - GetLastError()); - exit (1); + const char *devname = qdict_get_str(qdict, "devname"); + if (usb_device_add(devname) < 0) { + error_report("could not add USB device '%s'", devname); } } -#endif - -static int cpu_can_run(CPUState *env) -{ - if (env->stop) - return 0; - if (env->stopped) - return 0; - if (!vm_running) - return 0; - return 1; -} -#ifndef CONFIG_IOTHREAD -static int qemu_init_main_loop(void) +void do_usb_del(Monitor *mon, const QDict *qdict) { - return qemu_event_init(); + const char *devname = qdict_get_str(qdict, "devname"); + if (usb_device_del(devname) < 0) { + error_report("could not delete USB device '%s'", devname); + } } -void qemu_init_vcpu(void *_env) -{ - CPUState *env = _env; +/***********************************************************/ +/* PCMCIA/Cardbus */ - env->nr_cores = smp_cores; - env->nr_threads = smp_threads; - if (kvm_enabled()) - kvm_init_vcpu(env); - return; -} +static struct pcmcia_socket_entry_s { + PCMCIASocket *socket; + struct pcmcia_socket_entry_s *next; +} *pcmcia_sockets = 0; -int qemu_cpu_self(void *env) +void pcmcia_socket_register(PCMCIASocket *socket) { - return 1; -} + struct pcmcia_socket_entry_s *entry; -static void resume_all_vcpus(void) -{ + entry = qemu_malloc(sizeof(struct pcmcia_socket_entry_s)); + entry->socket = socket; + entry->next = pcmcia_sockets; + pcmcia_sockets = entry; } -static void pause_all_vcpus(void) +void pcmcia_socket_unregister(PCMCIASocket *socket) { -} + struct pcmcia_socket_entry_s *entry, **ptr; -void qemu_cpu_kick(void *env) -{ - return; + ptr = &pcmcia_sockets; + for (entry = *ptr; entry; ptr = &entry->next, entry = *ptr) + if (entry->socket == socket) { + *ptr = entry->next; + qemu_free(entry); + } } -void qemu_notify_event(void) +void pcmcia_info(Monitor *mon) { - CPUState *env = cpu_single_env; - - if (kvm_enabled()) { - qemu_kvm_notify_work(); - return; - } - if (env) { - cpu_exit(env); - } -} + struct pcmcia_socket_entry_s *iter; -#if defined(KVM_UPSTREAM) || !defined(CONFIG_KVM) -void qemu_mutex_lock_iothread(void) {} -void qemu_mutex_unlock_iothread(void) {} -#endif + if (!pcmcia_sockets) + monitor_printf(mon, "No PCMCIA sockets\n"); -void vm_stop(int reason) -{ - do_vm_stop(reason); + for (iter = pcmcia_sockets; iter; iter = iter->next) + monitor_printf(mon, "%s: %s\n", iter->socket->slot_string, + iter->socket->attached ? iter->socket->card_string : + "Empty"); } -#else /* CONFIG_IOTHREAD */ - -#include "qemu-thread.h" +/***********************************************************/ +/* I/O handling */ -QemuMutex qemu_global_mutex; -static QemuMutex qemu_fair_mutex; +typedef struct IOHandlerRecord { + int fd; + IOCanReadHandler *fd_read_poll; + IOHandler *fd_read; + IOHandler *fd_write; + int deleted; + void *opaque; + /* temporary data */ + struct pollfd *ufd; + QLIST_ENTRY(IOHandlerRecord) next; +} IOHandlerRecord; -static QemuThread io_thread; +static QLIST_HEAD(, IOHandlerRecord) io_handlers = + QLIST_HEAD_INITIALIZER(io_handlers); -static QemuThread *tcg_cpu_thread; -static QemuCond *tcg_halt_cond; -static int qemu_system_ready; -/* cpu creation */ -static QemuCond qemu_cpu_cond; -/* system init */ -static QemuCond qemu_system_cond; -static QemuCond qemu_pause_cond; +/* 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 *ioh; -static void block_io_signals(void); -static void unblock_io_signals(void); -static int tcg_has_work(void); + if (!fd_read && !fd_write) { + QLIST_FOREACH(ioh, &io_handlers, next) { + if (ioh->fd == fd) { + ioh->deleted = 1; + break; + } + } + } else { + QLIST_FOREACH(ioh, &io_handlers, next) { + if (ioh->fd == fd) + goto found; + } + ioh = qemu_mallocz(sizeof(IOHandlerRecord)); + QLIST_INSERT_HEAD(&io_handlers, ioh, next); + 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; + } + qemu_notify_event(); + return 0; +} -static int qemu_init_main_loop(void) +int qemu_set_fd_handler(int fd, + IOHandler *fd_read, + IOHandler *fd_write, + void *opaque) { - int ret; + return qemu_set_fd_handler2(fd, NULL, fd_read, fd_write, opaque); +} - ret = qemu_event_init(); - if (ret) - return ret; - - qemu_cond_init(&qemu_pause_cond); - qemu_mutex_init(&qemu_fair_mutex); - qemu_mutex_init(&qemu_global_mutex); - qemu_mutex_lock(&qemu_global_mutex); +/***********************************************************/ +/* machine registration */ - unblock_io_signals(); - qemu_thread_self(&io_thread); +static QEMUMachine *first_machine = NULL; +QEMUMachine *current_machine = NULL; +int qemu_register_machine(QEMUMachine *m) +{ + QEMUMachine **pm; + pm = &first_machine; + while (*pm != NULL) + pm = &(*pm)->next; + m->next = NULL; + *pm = m; return 0; } -static void qemu_wait_io_event(CPUState *env) +static QEMUMachine *find_machine(const char *name) { - while (!tcg_has_work()) - qemu_cond_timedwait(env->halt_cond, &qemu_global_mutex, 1000); - - qemu_mutex_unlock(&qemu_global_mutex); - - /* - * Users of qemu_global_mutex can be starved, having no chance - * to acquire it since this path will get to it first. - * So use another lock to provide fairness. - */ - qemu_mutex_lock(&qemu_fair_mutex); - qemu_mutex_unlock(&qemu_fair_mutex); + QEMUMachine *m; - qemu_mutex_lock(&qemu_global_mutex); - if (env->stop) { - env->stop = 0; - env->stopped = 1; - qemu_cond_signal(&qemu_pause_cond); + for(m = first_machine; m != NULL; m = m->next) { + if (!strcmp(m->name, name)) + return m; + if (m->alias && !strcmp(m->alias, name)) + return m; } + return NULL; } -static int qemu_cpu_exec(CPUState *env); - -static void *kvm_cpu_thread_fn(void *arg) +static QEMUMachine *find_default_machine(void) { - CPUState *env = arg; - - block_io_signals(); - qemu_thread_self(env->thread); - if (kvm_enabled()) - kvm_init_vcpu(env); - - /* signal CPU creation */ - qemu_mutex_lock(&qemu_global_mutex); - env->created = 1; - qemu_cond_signal(&qemu_cpu_cond); - - /* and wait for machine initialization */ - while (!qemu_system_ready) - qemu_cond_timedwait(&qemu_system_cond, &qemu_global_mutex, 100); + QEMUMachine *m; - while (1) { - if (cpu_can_run(env)) - qemu_cpu_exec(env); - qemu_wait_io_event(env); + for(m = first_machine; m != NULL; m = m->next) { + if (m->is_default) { + return m; + } } - return NULL; } -static void tcg_cpu_exec(void); +/***********************************************************/ +/* main execution loop */ -static void *tcg_cpu_thread_fn(void *arg) +static void gui_update(void *opaque) { - CPUState *env = arg; - - block_io_signals(); - qemu_thread_self(env->thread); - - /* signal CPU creation */ - qemu_mutex_lock(&qemu_global_mutex); - for (env = first_cpu; env != NULL; env = env->next_cpu) - env->created = 1; - qemu_cond_signal(&qemu_cpu_cond); + uint64_t interval = GUI_REFRESH_INTERVAL; + DisplayState *ds = opaque; + DisplayChangeListener *dcl = ds->listeners; - /* and wait for machine initialization */ - while (!qemu_system_ready) - qemu_cond_timedwait(&qemu_system_cond, &qemu_global_mutex, 100); + qemu_flush_coalesced_mmio_buffer(); + dpy_refresh(ds); - while (1) { - tcg_cpu_exec(); - qemu_wait_io_event(cur_cpu); + while (dcl != NULL) { + if (dcl->gui_timer_interval && + dcl->gui_timer_interval < interval) + interval = dcl->gui_timer_interval; + dcl = dcl->next; } - - return NULL; + qemu_mod_timer(ds->gui_timer, interval + qemu_get_clock(rt_clock)); } -void qemu_cpu_kick(void *_env) +static void nographic_update(void *opaque) { - CPUState *env = _env; - qemu_cond_broadcast(env->halt_cond); - if (kvm_enabled()) - qemu_thread_signal(env->thread, SIGUSR1); -} + uint64_t interval = GUI_REFRESH_INTERVAL; -int qemu_cpu_self(void *_env) -{ - CPUState *env = _env; - QemuThread this; - - qemu_thread_self(&this); - - return qemu_thread_equal(&this, env->thread); + qemu_flush_coalesced_mmio_buffer(); + qemu_mod_timer(nographic_timer, interval + qemu_get_clock(rt_clock)); } -static void cpu_signal(int sig) -{ - if (cpu_single_env) - cpu_exit(cpu_single_env); -} +struct vm_change_state_entry { + VMChangeStateHandler *cb; + void *opaque; + QLIST_ENTRY (vm_change_state_entry) entries; +}; -static void block_io_signals(void) -{ - sigset_t set; - struct sigaction sigact; +static QLIST_HEAD(vm_change_state_head, vm_change_state_entry) vm_change_state_head; - sigemptyset(&set); - sigaddset(&set, SIGUSR2); - sigaddset(&set, SIGIO); - sigaddset(&set, SIGALRM); - pthread_sigmask(SIG_BLOCK, &set, NULL); +VMChangeStateEntry *qemu_add_vm_change_state_handler(VMChangeStateHandler *cb, + void *opaque) +{ + VMChangeStateEntry *e; - sigemptyset(&set); - sigaddset(&set, SIGUSR1); - pthread_sigmask(SIG_UNBLOCK, &set, NULL); + e = qemu_mallocz(sizeof (*e)); - memset(&sigact, 0, sizeof(sigact)); - sigact.sa_handler = cpu_signal; - sigaction(SIGUSR1, &sigact, NULL); + e->cb = cb; + e->opaque = opaque; + QLIST_INSERT_HEAD(&vm_change_state_head, e, entries); + return e; } -static void unblock_io_signals(void) +void qemu_del_vm_change_state_handler(VMChangeStateEntry *e) { - sigset_t set; - - sigemptyset(&set); - sigaddset(&set, SIGUSR2); - sigaddset(&set, SIGIO); - sigaddset(&set, SIGALRM); - pthread_sigmask(SIG_UNBLOCK, &set, NULL); - - sigemptyset(&set); - sigaddset(&set, SIGUSR1); - pthread_sigmask(SIG_BLOCK, &set, NULL); + QLIST_REMOVE (e, entries); + qemu_free (e); } -static void qemu_signal_lock(unsigned int msecs) +void vm_state_notify(int running, int reason) { - qemu_mutex_lock(&qemu_fair_mutex); + VMChangeStateEntry *e; - while (qemu_mutex_trylock(&qemu_global_mutex)) { - qemu_thread_signal(tcg_cpu_thread, SIGUSR1); - if (!qemu_mutex_timedlock(&qemu_global_mutex, msecs)) - break; + trace_vm_state_notify(running, reason); + + for (e = vm_change_state_head.lh_first; e; e = e->entries.le_next) { + e->cb(e->opaque, running, reason); } - qemu_mutex_unlock(&qemu_fair_mutex); } -void qemu_mutex_lock_iothread(void) +void vm_start(void) { - if (kvm_enabled()) { - qemu_mutex_lock(&qemu_fair_mutex); - qemu_mutex_lock(&qemu_global_mutex); - qemu_mutex_unlock(&qemu_fair_mutex); - } else - qemu_signal_lock(100); + if (!vm_running) { + cpu_enable_ticks(); + vm_running = 1; + vm_state_notify(1, 0); + resume_all_vcpus(); + monitor_protocol_event(QEVENT_RESUME, NULL); + } } -void qemu_mutex_unlock_iothread(void) -{ - qemu_mutex_unlock(&qemu_global_mutex); -} +/* reset/shutdown handler */ -static int all_vcpus_paused(void) -{ - CPUState *penv = first_cpu; +typedef struct QEMUResetEntry { + QTAILQ_ENTRY(QEMUResetEntry) entry; + QEMUResetHandler *func; + void *opaque; +} QEMUResetEntry; - while (penv) { - if (!penv->stopped) - return 0; - penv = (CPUState *)penv->next_cpu; - } +static QTAILQ_HEAD(reset_handlers, QEMUResetEntry) reset_handlers = + QTAILQ_HEAD_INITIALIZER(reset_handlers); +static int reset_requested; +static int shutdown_requested; +static int powerdown_requested; +int debug_requested; +int vmstop_requested; - return 1; +int qemu_no_shutdown(void) +{ + int r = no_shutdown; + no_shutdown = 0; + return r; } -static void pause_all_vcpus(void) +int qemu_shutdown_requested(void) { - CPUState *penv = first_cpu; - - while (penv) { - penv->stop = 1; - qemu_thread_signal(penv->thread, SIGUSR1); - qemu_cpu_kick(penv); - penv = (CPUState *)penv->next_cpu; - } - - while (!all_vcpus_paused()) { - qemu_cond_timedwait(&qemu_pause_cond, &qemu_global_mutex, 100); - penv = first_cpu; - while (penv) { - qemu_thread_signal(penv->thread, SIGUSR1); - penv = (CPUState *)penv->next_cpu; - } - } + int r = shutdown_requested; + shutdown_requested = 0; + return r; } -static void resume_all_vcpus(void) +int qemu_reset_requested(void) { - CPUState *penv = first_cpu; - - while (penv) { - penv->stop = 0; - penv->stopped = 0; - qemu_thread_signal(penv->thread, SIGUSR1); - qemu_cpu_kick(penv); - penv = (CPUState *)penv->next_cpu; - } + int r = reset_requested; + reset_requested = 0; + return r; } -static void tcg_init_vcpu(void *_env) +int qemu_powerdown_requested(void) { - CPUState *env = _env; - /* share a single thread for all cpus with TCG */ - if (!tcg_cpu_thread) { - env->thread = qemu_mallocz(sizeof(QemuThread)); - env->halt_cond = qemu_mallocz(sizeof(QemuCond)); - qemu_cond_init(env->halt_cond); - qemu_thread_create(env->thread, tcg_cpu_thread_fn, env); - while (env->created == 0) - qemu_cond_timedwait(&qemu_cpu_cond, &qemu_global_mutex, 100); - tcg_cpu_thread = env->thread; - tcg_halt_cond = env->halt_cond; - } else { - env->thread = tcg_cpu_thread; - env->halt_cond = tcg_halt_cond; - } + int r = powerdown_requested; + powerdown_requested = 0; + return r; } -static void kvm_start_vcpu(CPUState *env) +static int qemu_debug_requested(void) { - env->thread = qemu_mallocz(sizeof(QemuThread)); - env->halt_cond = qemu_mallocz(sizeof(QemuCond)); - qemu_cond_init(env->halt_cond); - qemu_thread_create(env->thread, kvm_cpu_thread_fn, env); - while (env->created == 0) - qemu_cond_timedwait(&qemu_cpu_cond, &qemu_global_mutex, 100); + int r = debug_requested; + debug_requested = 0; + return r; } -void qemu_init_vcpu(void *_env) +static int qemu_vmstop_requested(void) { - CPUState *env = _env; - - env->nr_cores = smp_cores; - env->nr_threads = smp_threads; - if (kvm_enabled()) - kvm_start_vcpu(env); - else - tcg_init_vcpu(env); + int r = vmstop_requested; + vmstop_requested = 0; + return r; } -void qemu_notify_event(void) +void qemu_register_reset(QEMUResetHandler *func, void *opaque) { - qemu_event_increment(); + QEMUResetEntry *re = qemu_mallocz(sizeof(QEMUResetEntry)); + + re->func = func; + re->opaque = opaque; + QTAILQ_INSERT_TAIL(&reset_handlers, re, entry); } -void vm_stop(int reason) +void qemu_unregister_reset(QEMUResetHandler *func, void *opaque) { - QemuThread me; - qemu_thread_self(&me); + QEMUResetEntry *re; - if (!qemu_thread_equal(&me, &io_thread)) { - qemu_system_vmstop_request(reason); - /* - * FIXME: should not return to device code in case - * vm_stop() has been requested. - */ - if (cpu_single_env) { - cpu_exit(cpu_single_env); - cpu_single_env->stop = 1; + QTAILQ_FOREACH(re, &reset_handlers, entry) { + if (re->func == func && re->opaque == opaque) { + QTAILQ_REMOVE(&reset_handlers, re, entry); + qemu_free(re); + return; } - return; } - do_vm_stop(reason); } -#endif - - -#ifdef _WIN32 -static void host_main_loop_wait(int *timeout) +void qemu_system_reset(void) { - int ret, ret2, i; - PollingEntry *pe; + QEMUResetEntry *re, *nre; + /* reset all devices */ + QTAILQ_FOREACH_SAFE(re, &reset_handlers, entry, nre) { + re->func(re->opaque); + } + monitor_protocol_event(QEVENT_RESET, NULL); + cpu_synchronize_all_post_reset(); +} - /* 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); - } - if (ret == 0) { - int err; - WaitObjects *w = &wait_objects; - - ret = WaitForMultipleObjects(w->num, w->events, FALSE, *timeout); - if (WAIT_OBJECT_0 + 0 <= ret && ret <= WAIT_OBJECT_0 + w->num - 1) { - if (w->func[ret - WAIT_OBJECT_0]) - w->func[ret - WAIT_OBJECT_0](w->opaque[ret - WAIT_OBJECT_0]); - - /* Check for additional signaled events */ - for(i = (ret - WAIT_OBJECT_0 + 1); i < w->num; i++) { - - /* Check if event is signaled */ - ret2 = WaitForSingleObject(w->events[i], 0); - if(ret2 == WAIT_OBJECT_0) { - if (w->func[i]) - w->func[i](w->opaque[i]); - } else if (ret2 == WAIT_TIMEOUT) { - } else { - err = GetLastError(); - fprintf(stderr, "WaitForSingleObject error %d %d\n", i, err); - } - } - } else if (ret == WAIT_TIMEOUT) { - } else { - err = GetLastError(); - fprintf(stderr, "WaitForMultipleObjects error %d %d\n", ret, err); - } +void qemu_system_reset_request(void) +{ + if (no_reboot) { + shutdown_requested = 1; + } else { + reset_requested = 1; + } + if (cpu_single_env) { + cpu_single_env->stopped = 1; + cpu_exit(cpu_single_env); } + qemu_notify_event(); +} - *timeout = 0; +void qemu_system_shutdown_request(void) +{ + shutdown_requested = 1; + qemu_notify_event(); } -#else -static void host_main_loop_wait(int *timeout) + +void qemu_system_powerdown_request(void) { + powerdown_requested = 1; + qemu_notify_event(); } -#endif -void main_loop_wait(int timeout) +void main_loop_wait(int nonblocking) { IOHandlerRecord *ioh; fd_set rfds, wfds, xfds; int ret, nfds; struct timeval tv; + int timeout; - qemu_bh_update_timeout(&timeout); + if (nonblocking) + timeout = 0; + else { + timeout = qemu_calculate_timeout(); + qemu_bh_update_timeout(&timeout); + } - host_main_loop_wait(&timeout); + os_host_main_loop_wait(&timeout); /* poll any events */ /* XXX: separate device handlers from system ones */ @@ -3968,225 +1350,60 @@ 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; - - slirp_select_fill(&nfds, &rfds, &wfds, &xfds); - - qemu_mutex_unlock_iothread(); - ret = select(nfds + 1, &rfds, &wfds, &xfds, &tv); - qemu_mutex_lock_iothread(); - 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->fd_read_poll && ioh->fd_read_poll(ioh->opaque))) - FD_CLR(ioh->fd, &rfds); - } - 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; - qemu_free(ioh); - } else - pioh = &ioh->next; - } - } - - slirp_select_poll(&rfds, &wfds, &xfds, (ret < 0)); - - /* rearm timer, if not periodic */ - if (alarm_timer->flags & ALARM_FLAG_EXPIRED) { - alarm_timer->flags &= ~ALARM_FLAG_EXPIRED; - qemu_rearm_alarm_timer(alarm_timer); - } - - /* vm time timers */ - if (vm_running) { - if (!cur_cpu || likely(!(cur_cpu->singlestep_enabled & SSTEP_NOTIMER))) - qemu_run_timers(&active_timers[QEMU_CLOCK_VIRTUAL], - qemu_get_clock(vm_clock)); - } - - /* real time timers */ - qemu_run_timers(&active_timers[QEMU_CLOCK_REALTIME], - qemu_get_clock(rt_clock)); - - qemu_run_timers(&active_timers[QEMU_CLOCK_HOST], - qemu_get_clock(host_clock)); - - /* Check bottom-halves last in case any of the earlier events triggered - them. */ - qemu_bh_poll(); - -} - -static int qemu_cpu_exec(CPUState *env) -{ - int ret; -#ifdef CONFIG_PROFILER - int64_t ti; -#endif - -#ifdef CONFIG_PROFILER - ti = profile_getclock(); -#endif - if (use_icount) { - int64_t count; - int decr; - qemu_icount -= (env->icount_decr.u16.low + env->icount_extra); - env->icount_decr.u16.low = 0; - env->icount_extra = 0; - count = qemu_next_deadline(); - count = (count + (1 << icount_time_shift) - 1) - >> icount_time_shift; - qemu_icount += count; - decr = (count > 0xffff) ? 0xffff : count; - count -= decr; - env->icount_decr.u16.low = decr; - env->icount_extra = count; - } - ret = cpu_exec(env); -#ifdef CONFIG_PROFILER - qemu_time += profile_getclock() - ti; -#endif - if (use_icount) { - /* Fold pending instructions back into the - instruction counter, and clear the interrupt flag. */ - qemu_icount -= (env->icount_decr.u16.low - + env->icount_extra); - env->icount_decr.u32 = 0; - env->icount_extra = 0; - } - return ret; -} - -static void tcg_cpu_exec(void) -{ - int ret = 0; - - if (next_cpu == NULL) - next_cpu = first_cpu; - for (; next_cpu != NULL; next_cpu = next_cpu->next_cpu) { - CPUState *env = cur_cpu = next_cpu; - - if (timer_alarm_pending) { - timer_alarm_pending = 0; - break; + QLIST_FOREACH(ioh, &io_handlers, 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 (cpu_can_run(env)) - ret = qemu_cpu_exec(env); - else if (env->stop) - break; - - if (ret == EXCP_DEBUG) { - gdb_set_stop_cpu(env); - debug_requested = 1; - break; + if (ioh->fd_write) { + FD_SET(ioh->fd, &wfds); + if (ioh->fd > nfds) + nfds = ioh->fd; } } -} -static int cpu_has_work(CPUState *env) -{ - if (env->stop) - return 1; - if (env->stopped) - return 0; - if (!env->halted) - return 1; - if (qemu_cpu_has_work(env)) - return 1; - return 0; -} + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; -static int tcg_has_work(void) -{ - CPUState *env; + slirp_select_fill(&nfds, &rfds, &wfds, &xfds); - for (env = first_cpu; env != NULL; env = env->next_cpu) - if (cpu_has_work(env)) - return 1; - return 0; -} + qemu_mutex_unlock_iothread(); + ret = select(nfds + 1, &rfds, &wfds, &xfds, &tv); + qemu_mutex_lock_iothread(); + if (ret > 0) { + IOHandlerRecord *pioh; -static int qemu_calculate_timeout(void) -{ -#ifndef CONFIG_IOTHREAD - int timeout; + QLIST_FOREACH_SAFE(ioh, &io_handlers, next, pioh) { + if (!ioh->deleted && ioh->fd_read && FD_ISSET(ioh->fd, &rfds)) { + ioh->fd_read(ioh->opaque); + if (!(ioh->fd_read_poll && ioh->fd_read_poll(ioh->opaque))) + FD_CLR(ioh->fd, &rfds); + } + if (!ioh->deleted && ioh->fd_write && FD_ISSET(ioh->fd, &wfds)) { + ioh->fd_write(ioh->opaque); + } - if (!vm_running) - timeout = 5000; - else if (tcg_has_work()) - timeout = 0; - else if (!use_icount) - timeout = 5000; - else { - /* XXX: use timeout computed from timers */ - int64_t add; - int64_t delta; - /* Advance virtual time to the next event. */ - if (use_icount == 1) { - /* When not using an adaptive execution frequency - we tend to get badly out of sync with real time, - so just delay for a reasonable amount of time. */ - delta = 0; - } else { - delta = cpu_get_icount() - cpu_get_clock(); - } - if (delta > 0) { - /* If virtual time is ahead of real time then just - wait for IO. */ - timeout = (delta / 1000000) + 1; - } else { - /* Wait for either IO to occur or the next - timer event. */ - add = qemu_next_deadline(); - /* We advance the timer before checking for IO. - Limit the amount we advance so that early IO - activity won't get the guest too far ahead. */ - if (add > 10000000) - add = 10000000; - delta += add; - add = (add + (1 << icount_time_shift) - 1) - >> icount_time_shift; - qemu_icount += add; - timeout = delta / 1000000; - if (timeout < 0) - timeout = 0; + /* Do this last in case read/write handlers marked it for deletion */ + if (ioh->deleted) { + QLIST_REMOVE(ioh, next); + qemu_free(ioh); + } } } - return timeout; -#else /* CONFIG_IOTHREAD */ - return 1000; -#endif + slirp_select_poll(&rfds, &wfds, &xfds, (ret < 0)); + + qemu_run_all_timers(); + + /* Check bottom-halves last in case any of the earlier events triggered + them. */ + qemu_bh_poll(); + } static int vm_can_run(void) @@ -4214,31 +1431,28 @@ return; } -#ifdef CONFIG_IOTHREAD - qemu_system_ready = 1; - qemu_cond_broadcast(&qemu_system_cond); -#endif + qemu_main_loop_start(); for (;;) { do { + bool nonblocking = false; #ifdef CONFIG_PROFILER int64_t ti; #endif #ifndef CONFIG_IOTHREAD - tcg_cpu_exec(); + nonblocking = cpu_exec_all(); #endif #ifdef CONFIG_PROFILER ti = profile_getclock(); #endif - main_loop_wait(qemu_calculate_timeout()); + main_loop_wait(nonblocking); #ifdef CONFIG_PROFILER dev_time += profile_getclock() - ti; #endif } while (vm_can_run()); - if (qemu_debug_requested()) { - monitor_protocol_event(QEVENT_DEBUG, NULL); - vm_stop(EXCP_DEBUG); + if ((r = qemu_debug_requested())) { + vm_stop(r); } if (qemu_shutdown_requested()) { monitor_protocol_event(QEVENT_SHUTDOWN, NULL); @@ -4249,7 +1463,6 @@ break; } if (qemu_reset_requested()) { - monitor_protocol_event(QEVENT_RESET, NULL); pause_all_vcpus(); qemu_system_reset(); resume_all_vcpus(); @@ -4259,222 +1472,66 @@ qemu_irq_raise(qemu_system_powerdown); } if ((r = qemu_vmstop_requested())) { - monitor_protocol_event(QEVENT_STOP, NULL); vm_stop(r); } } + bdrv_close_all(); pause_all_vcpus(); } static void version(void) { - printf("QEMU PC emulator version " QEMU_VERSION QEMU_PKGVERSION ", Copyright (c) 2003-2008 Fabrice Bellard\n"); + printf("QEMU emulator version " QEMU_VERSION QEMU_PKGVERSION ", Copyright (c) 2003-2008 Fabrice Bellard\n"); } static void help(int exitcode) { - version(); - printf("usage: %s [options] [disk_image]\n" - "\n" - "'disk_image' is a raw hard image image for IDE hard disk 0\n" - "\n" -#define DEF(option, opt_arg, opt_enum, opt_help) \ - opt_help + const char *options_help = +#define DEF(option, opt_arg, opt_enum, opt_help, arch_mask) \ + opt_help #define DEFHEADING(text) stringify(text) "\n" -#include "qemu-options.h" +#include "qemu-options.def" #undef DEF #undef DEFHEADING #undef GEN_DOCS + ; + version(); + printf("usage: %s [options] [disk_image]\n" "\n" + "'disk_image' is a raw hard disk image for IDE hard disk 0\n" + "\n" + "%s\n" "During emulation, the following keys are useful:\n" "ctrl-alt-f toggle full screen\n" "ctrl-alt-n switch to virtual console 'n'\n" "ctrl-alt toggle mouse and keyboard grab\n" "\n" - "When using -nographic, press 'ctrl-a h' to get some help.\n" - , + "When using -nographic, press 'ctrl-a h' to get some help.\n", "qemu", - DEFAULT_RAM_SIZE, -#ifndef _WIN32 - DEFAULT_NETWORK_SCRIPT, - DEFAULT_NETWORK_DOWN_SCRIPT, -#endif - DEFAULT_GDBSTUB_PORT, - "/tmp/qemu.log"); + options_help); exit(exitcode); } #define HAS_ARG 0x0001 -enum { -#define DEF(option, opt_arg, opt_enum, opt_help) \ - opt_enum, -#define DEFHEADING(text) -#include "qemu-options.h" -#undef DEF -#undef DEFHEADING -#undef GEN_DOCS -}; - typedef struct QEMUOption { const char *name; int flags; int index; + uint32_t arch_mask; } QEMUOption; static const QEMUOption qemu_options[] = { - { "h", 0, QEMU_OPTION_h }, -#define DEF(option, opt_arg, opt_enum, opt_help) \ - { option, opt_arg, opt_enum }, + { "h", 0, QEMU_OPTION_h, QEMU_ARCH_ALL }, +#define DEF(option, opt_arg, opt_enum, opt_help, arch_mask) \ + { option, opt_arg, opt_enum, arch_mask }, #define DEFHEADING(text) -#include "qemu-options.h" +#include "qemu-options.def" #undef DEF #undef DEFHEADING #undef GEN_DOCS { NULL }, }; - -#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 } } -}; - -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 { - 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; - } -} -#endif - static void select_vgahw (const char *p) { const char *opts; @@ -4489,6 +1546,8 @@ vga_interface_type = VGA_VMWARE; } else if (strstart(p, "xenfb", &opts)) { vga_interface_type = VGA_XENFB; + } else if (strstart(p, "qxl", &opts)) { + vga_interface_type = VGA_QXL; } else if (!strstart(p, "none", &opts)) { invalid_vga: fprintf(stderr, "Unknown vga type: %s\n", p); @@ -4509,7 +1568,6 @@ } } -#ifdef TARGET_I386 static int balloon_parse(const char *arg) { QemuOpts *opts; @@ -4521,12 +1579,12 @@ if (!strncmp(arg, "virtio", 6)) { if (arg[6] == ',') { /* have params -> parse them */ - opts = qemu_opts_parse(&qemu_device_opts, arg+7, NULL); + opts = qemu_opts_parse(qemu_find_opts("device"), arg+7, 0); if (!opts) return -1; } else { /* create empty opts */ - opts = qemu_opts_create(&qemu_device_opts, NULL, 0); + opts = qemu_opts_create(qemu_find_opts("device"), NULL, 0); } qemu_opt_set(opts, "driver", "virtio-balloon-pci"); return 0; @@ -4534,151 +1592,6 @@ return -1; } -#endif - -#ifdef _WIN32 -static BOOL WINAPI qemu_ctrl_handler(DWORD type) -{ - exit(STATUS_CONTROL_C_EXIT); - return TRUE; -} -#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; -} - -#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 - -#ifdef _WIN32 -/* Look for support files in the same directory as the executable. */ -static char *find_datadir(const char *argv0) -{ - char *p; - char buf[MAX_PATH]; - DWORD len; - - len = GetModuleFileName(NULL, buf, sizeof(buf) - 1); - if (len == 0) { - return NULL; - } - - buf[len] = 0; - p = buf + len - 1; - while (p != buf && *p != '\\') - p--; - *p = 0; - if (access(buf, R_OK) == 0) { - return qemu_strdup(buf); - } - return NULL; -} -#else /* !_WIN32 */ - -/* Find a likely location for support files using the location of the binary. - For installed binaries this will be "$bindir/../share/qemu". When - running from the build tree this will be "$bindir/../pc-bios". */ -#define SHARE_SUFFIX "/share/qemu" -#define BUILD_SUFFIX "/pc-bios" -static char *find_datadir(const char *argv0) -{ - char *dir; - char *p = NULL; - char *res; - char buf[PATH_MAX]; - size_t max_len; - -#if defined(__linux__) - { - int len; - len = readlink("/proc/self/exe", buf, sizeof(buf) - 1); - if (len > 0) { - buf[len] = 0; - p = buf; - } - } -#elif defined(__FreeBSD__) - { - int len; - len = readlink("/proc/curproc/file", buf, sizeof(buf) - 1); - if (len > 0) { - buf[len] = 0; - p = buf; - } - } -#endif - /* If we don't have any way of figuring out the actual executable - location then try argv[0]. */ - if (!p) { - p = realpath(argv0, buf); - if (!p) { - return NULL; - } - } - dir = dirname(p); - dir = dirname(dir); - - max_len = strlen(dir) + - MAX(strlen(SHARE_SUFFIX), strlen(BUILD_SUFFIX)) + 1; - res = qemu_mallocz(max_len); - snprintf(res, max_len, "%s%s", dir, SHARE_SUFFIX); - if (access(res, R_OK)) { - snprintf(res, max_len, "%s%s", dir, BUILD_SUFFIX); - if (access(res, R_OK)) { - qemu_free(res); - res = NULL; - } - } - - return res; -} -#undef SHARE_SUFFIX -#undef BUILD_SUFFIX -#endif char *qemu_find_file(int type, const char *name) { @@ -4711,6 +1624,11 @@ return buf; } +static int device_help_func(QemuOpts *opts, void *opaque) +{ + return qdev_device_help(opts); +} + static int device_init_func(QemuOpts *opts, void *opaque) { DeviceState *dev; @@ -4731,6 +1649,16 @@ return 0; } +#ifdef CONFIG_VIRTFS +static int fsdev_init_func(QemuOpts *opts, void *opaque) +{ + int ret; + ret = qemu_fsdev_add(opts); + + return ret; +} +#endif + static int mon_init_func(QemuOpts *opts, void *opaque) { CharDriverState *chr; @@ -4751,6 +1679,9 @@ exit(1); } + if (qemu_opt_get_bool(opts, "pretty", 0)) + flags |= MONITOR_USE_PRETTY; + if (qemu_opt_get_bool(opts, "default", 0)) flags |= MONITOR_IS_DEFAULT; @@ -4776,11 +1707,9 @@ if (strstart(optarg, "chardev:", &p)) { snprintf(label, sizeof(label), "%s", p); } else { - if (monitor_device_index) { - snprintf(label, sizeof(label), "monitor%d", - monitor_device_index); - } else { - snprintf(label, sizeof(label), "monitor"); + snprintf(label, sizeof(label), "compat_monitor%d", + monitor_device_index); + if (monitor_device_index == 0) { def = 1; } opts = qemu_chr_parse_compat(label, optarg); @@ -4790,7 +1719,7 @@ } } - opts = qemu_opts_create(&qemu_mon_opts, label, 1); + opts = qemu_opts_create(qemu_find_opts("mon"), label, 1); if (!opts) { fprintf(stderr, "duplicate chardev: %s\n", label); exit(1); @@ -4809,6 +1738,7 @@ DEV_SERIAL, /* -serial */ DEV_PARALLEL, /* -parallel */ DEV_VIRTCON, /* -virtioconsole */ + DEV_DEBUGCON, /* -debugcon */ } type; const char *cmdline; QTAILQ_ENTRY(device_config) next; @@ -4886,8 +1816,10 @@ static int virtcon_parse(const char *devname) { + QemuOptsList *device = qemu_find_opts("device"); static int index = 0; char label[32]; + QemuOpts *bus_opts, *dev_opts; if (strcmp(devname, "none") == 0) return 0; @@ -4895,6 +1827,13 @@ fprintf(stderr, "qemu: too many virtio consoles\n"); exit(1); } + + bus_opts = qemu_opts_create(device, NULL, 0); + qemu_opt_set(bus_opts, "driver", "virtio-serial"); + + dev_opts = qemu_opts_create(device, NULL, 0); + qemu_opt_set(dev_opts, "driver", "virtconsole"); + snprintf(label, sizeof(label), "virtcon%d", index); virtcon_hds[index] = qemu_chr_open(label, devname, NULL); if (!virtcon_hds[index]) { @@ -4902,16 +1841,100 @@ devname, strerror(errno)); return -1; } + qemu_opt_set(dev_opts, "chardev", label); + index++; return 0; } +static int debugcon_parse(const char *devname) +{ + QemuOpts *opts; + + if (!qemu_chr_open("debugcon", devname, NULL)) { + exit(1); + } + opts = qemu_opts_create(qemu_find_opts("device"), "debugcon", 1); + if (!opts) { + fprintf(stderr, "qemu: already have a debugcon device\n"); + exit(1); + } + qemu_opt_set(opts, "driver", "isa-debugcon"); + qemu_opt_set(opts, "chardev", "debugcon"); + return 0; +} + +void qemu_add_exit_notifier(Notifier *notify) +{ + notifier_list_add(&exit_notifiers, notify); +} + +void qemu_remove_exit_notifier(Notifier *notify) +{ + notifier_list_remove(&exit_notifiers, notify); +} + +static void qemu_run_exit_notifiers(void) +{ + notifier_list_notify(&exit_notifiers); +} + +void qemu_add_machine_init_done_notifier(Notifier *notify) +{ + notifier_list_add(&machine_init_done_notifiers, notify); +} + +static void qemu_run_machine_init_done_notifiers(void) +{ + notifier_list_notify(&machine_init_done_notifiers); +} + +static const QEMUOption *lookup_opt(int argc, char **argv, + const char **poptarg, int *poptind) +{ + const QEMUOption *popt; + int optind = *poptind; + char *r = argv[optind]; + const char *optarg; + + loc_set_cmdline(argv, optind, 1); + optind++; + /* Treat --foo the same as -foo. */ + if (r[1] == '-') + r++; + popt = qemu_options; + for(;;) { + if (!popt->name) { + error_report("invalid option"); + exit(1); + } + if (!strcmp(popt->name, r + 1)) + break; + popt++; + } + if (popt->flags & HAS_ARG) { + if (optind >= argc) { + error_report("requires an argument"); + exit(1); + } + optarg = argv[optind++]; + loc_set_cmdline(argv, optind - 2, 2); + } else { + optarg = NULL; + } + + *poptarg = optarg; + *poptind = optind; + + return popt; +} + int main(int argc, char **argv, char **envp) { const char *gdbstub_dev = NULL; - uint32_t boot_devices_bitmap = 0; int i; int snapshot, linux_boot; + const char *icount_option = NULL; const char *initrd_filename; const char *kernel_filename, *kernel_cmdline; char boot_devices[33] = "cad"; /* default to HD->floppy->CD-ROM */ @@ -4919,61 +1942,30 @@ DisplayChangeListener *dcl; int cyls, heads, secs, translation; QemuOpts *hda_opts = NULL, *opts; + QemuOptsList *olist; int optind; - const char *r, *optarg; + const char *optarg; const char *loadvm = NULL; QEMUMachine *machine; const char *cpu_model; -#ifndef _WIN32 - int fds[2]; -#endif int tb_size; const char *pid_file = NULL; const char *incoming = NULL; -#ifndef _WIN32 - int fd = 0; - struct passwd *pwd = NULL; - const char *chroot_dir = NULL; - const char *run_as = NULL; -#endif - CPUState *env; - int show_vnc_port = 0; - - init_clocks(); - - qemu_errors_to_file(stderr); - qemu_cache_utils_init(envp); - - QLIST_INIT (&vm_change_state_head); -#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); - } - } - } + int show_vnc_port = 0; + int defconfig = 1; + +#ifdef CONFIG_SIMPLE_TRACE + const char *trace_file = NULL; #endif + atexit(qemu_run_exit_notifiers); + error_set_progname(argv[0]); + + init_clocks(); + + qemu_cache_utils_init(envp); + + QLIST_INIT (&vm_change_state_head); + os_setup_early_signal_handling(); module_call_init(MODULE_INIT_MACHINE); machine = find_default_machine(); @@ -4991,50 +1983,61 @@ node_cpumask[i] = 0; } - assigned_devices_index = 0; - nb_numa_nodes = 0; nb_nics = 0; tb_size = 0; autostart= 1; + /* first pass of option parsing */ + optind = 1; + while (optind < argc) { + if (argv[optind][0] != '-') { + /* disk image */ + optind++; + continue; + } else { + const QEMUOption *popt; + + popt = lookup_opt(argc, argv, &optarg, &optind); + switch (popt->index) { + case QEMU_OPTION_nodefconfig: + defconfig=0; + break; + } + } + } + + if (defconfig) { + int ret; + + ret = qemu_read_config_file(CONFIG_QEMU_CONFDIR "/qemu.conf"); + if (ret < 0 && ret != -ENOENT) { + exit(1); + } + + ret = qemu_read_config_file(arch_config_name); + if (ret < 0 && ret != -ENOENT) { + exit(1); + } + } + cpudef_init(); + + /* second pass of option parsing */ optind = 1; for(;;) { if (optind >= argc) break; - r = argv[optind]; - if (r[0] != '-') { - hda_opts = drive_add(argv[optind++], HD_ALIAS, 0); + if (argv[optind][0] != '-') { + hda_opts = drive_add(IF_DEFAULT, 0, argv[optind++], HD_OPTS); } else { const QEMUOption *popt; - optind++; - /* Treat --foo the same as -foo. */ - if (r[1] == '-') - r++; - popt = qemu_options; - for(;;) { - if (!popt->name) { - fprintf(stderr, "%s: invalid option -- '%s'\n", - argv[0], r); - exit(1); - } - if (!strcmp(popt->name, r + 1)) - break; - popt++; - } - if (popt->flags & HAS_ARG) { - if (optind >= argc) { - fprintf(stderr, "%s: option '%s' requires an argument\n", - argv[0], r); - exit(1); - } - optarg = argv[optind++]; - } else { - optarg = NULL; + popt = lookup_opt(argc, argv, &optarg, &optind); + if (!(popt->arch_mask & arch_type)) { + printf("Option %s not supported for this target\n", popt->name); + exit(1); } - switch(popt->index) { case QEMU_OPTION_M: machine = find_machine(optarg); @@ -5055,10 +2058,7 @@ case QEMU_OPTION_cpu: /* hw initialization will check this */ if (*optarg == '?') { -/* XXX: implement xxx_cpu_list for targets that still miss it */ -#if defined(cpu_list) - cpu_list(stdout, &fprintf); -#endif + list_cpus(stdout, &fprintf, optarg); exit(0); } else { cpu_model = optarg; @@ -5068,24 +2068,31 @@ initrd_filename = optarg; break; case QEMU_OPTION_hda: - if (cyls == 0) - hda_opts = drive_add(optarg, HD_ALIAS, 0); - else - hda_opts = drive_add(optarg, HD_ALIAS - ",cyls=%d,heads=%d,secs=%d%s", - 0, cyls, heads, secs, - translation == BIOS_ATA_TRANSLATION_LBA ? + { + char buf[256]; + if (cyls == 0) + snprintf(buf, sizeof(buf), "%s", HD_OPTS); + else + snprintf(buf, sizeof(buf), + "%s,cyls=%d,heads=%d,secs=%d%s", + HD_OPTS , cyls, heads, secs, + translation == BIOS_ATA_TRANSLATION_LBA ? ",trans=lba" : - translation == BIOS_ATA_TRANSLATION_NONE ? + translation == BIOS_ATA_TRANSLATION_NONE ? ",trans=none" : ""); - break; + drive_add(IF_DEFAULT, 0, optarg, buf); + break; + } case QEMU_OPTION_hdb: case QEMU_OPTION_hdc: case QEMU_OPTION_hdd: - drive_add(optarg, HD_ALIAS, popt->index - QEMU_OPTION_hda); + drive_add(IF_DEFAULT, popt->index - QEMU_OPTION_hda, optarg, + HD_OPTS); break; case QEMU_OPTION_drive: - drive_add(NULL, "%s", optarg); + if (drive_def(optarg) == NULL) { + exit(1); + } break; case QEMU_OPTION_set: if (qemu_set_option(optarg) != 0) @@ -5096,13 +2103,13 @@ exit(1); break; case QEMU_OPTION_mtdblock: - drive_add(optarg, MTD_ALIAS); + drive_add(IF_MTD, -1, optarg, MTD_OPTS); break; case QEMU_OPTION_sd: - drive_add(optarg, SD_ALIAS); + drive_add(IF_SD, 0, optarg, SD_OPTS); break; case QEMU_OPTION_pflash: - drive_add(optarg, PFLASH_ALIAS); + drive_add(IF_PFLASH, -1, optarg, PFLASH_OPTS); break; case QEMU_OPTION_snapshot: snapshot = 1; @@ -5181,7 +2188,7 @@ kernel_cmdline = optarg; break; case QEMU_OPTION_cdrom: - drive_add(optarg, CDROM_ALIAS); + drive_add(IF_DEFAULT, 2, optarg, CDROM_OPTS); break; case QEMU_OPTION_boot: { @@ -5204,13 +2211,13 @@ if (legacy || get_param_value(buf, sizeof(buf), "order", optarg)) { - boot_devices_bitmap = parse_bootdevices(buf); + validate_bootdevices(buf); pstrcpy(boot_devices, sizeof(boot_devices), buf); } if (!legacy) { if (get_param_value(buf, sizeof(buf), "once", optarg)) { - boot_devices_bitmap |= parse_bootdevices(buf); + validate_bootdevices(buf); standard_boot_devices = qemu_strdup(boot_devices); pstrcpy(boot_devices, sizeof(boot_devices), buf); qemu_register_reset(restore_boot_devices, @@ -5234,20 +2241,19 @@ break; case QEMU_OPTION_fda: case QEMU_OPTION_fdb: - drive_add(optarg, FD_ALIAS, popt->index - QEMU_OPTION_fda); + drive_add(IF_FLOPPY, popt->index - QEMU_OPTION_fda, + optarg, FD_OPTS); break; -#ifdef TARGET_I386 case QEMU_OPTION_no_fd_bootchk: fd_bootchk = 0; break; -#endif case QEMU_OPTION_netdev: - if (net_client_parse(&qemu_netdev_opts, optarg) == -1) { + if (net_client_parse(qemu_find_opts("netdev"), optarg) == -1) { exit(1); } break; case QEMU_OPTION_net: - if (net_client_parse(&qemu_net_opts, optarg) == -1) { + if (net_client_parse(qemu_find_opts("net"), optarg) == -1) { exit(1); } break; @@ -5258,12 +2264,6 @@ case QEMU_OPTION_bootp: legacy_bootp_filename = optarg; break; -#ifndef _WIN32 - case QEMU_OPTION_smb: - if (net_slirp_smb(optarg) < 0) - exit(1); - break; -#endif case QEMU_OPTION_redir: if (net_slirp_redir(optarg) < 0) exit(1); @@ -5272,15 +2272,21 @@ case QEMU_OPTION_bt: add_device_config(DEV_BT, optarg); break; -#ifdef HAS_AUDIO case QEMU_OPTION_audio_help: + if (!(audio_available())) { + printf("Option %s not supported for this target\n", popt->name); + exit(1); + } AUD_help (); exit (0); break; case QEMU_OPTION_soundhw: + if (!(audio_available())) { + printf("Option %s not supported for this target\n", popt->name); + exit(1); + } select_soundhw (optarg); break; -#endif case QEMU_OPTION_h: help(0); break; @@ -5289,18 +2295,10 @@ exit(0); break; case QEMU_OPTION_m: { - uint64_t value; - char *ptr; + int64_t value; - value = strtoul(optarg, &ptr, 10); - switch (*ptr) { - case 0: case 'M': case 'm': - value <<= 20; - break; - case 'G': case 'g': - value <<= 30; - break; - default: + value = strtosz(optarg, NULL); + if (value < 0) { fprintf(stderr, "qemu: invalid ram size: %s\n", optarg); exit(1); } @@ -5317,21 +2315,16 @@ ram_size = value; break; } + case QEMU_OPTION_mempath: + mem_path = optarg; + break; +#ifdef MAP_POPULATE + case QEMU_OPTION_mem_prealloc: + mem_prealloc = 1; + break; +#endif case QEMU_OPTION_d: - { - int mask; - const CPULogItem *item; - - mask = cpu_str_to_log_mask(optarg); - if (!mask) { - printf("Log items (comma separated):\n"); - for(item = cpu_log_items; item->mask != 0; item++) { - printf("%-10s %s\n", item->name, item->help); - } - exit(1); - } - cpu_set_log(mask); - } + set_cpu_log(optarg); break; case QEMU_OPTION_s: gdbstub_dev = "tcp::" DEFAULT_GDBSTUB_PORT; @@ -5360,7 +2353,6 @@ case QEMU_OPTION_vga: select_vgahw (optarg); break; -#if defined(TARGET_PPC) || defined(TARGET_SPARC) case QEMU_OPTION_g: { const char *p; @@ -5395,7 +2387,6 @@ graphic_depth = depth; } break; -#endif case QEMU_OPTION_echr: { char *r; @@ -5413,20 +2404,93 @@ default_monitor = 0; break; case QEMU_OPTION_mon: - opts = qemu_opts_parse(&qemu_mon_opts, optarg, "chardev"); + opts = qemu_opts_parse(qemu_find_opts("mon"), optarg, 1); if (!opts) { - fprintf(stderr, "parse error: %s\n", optarg); exit(1); } default_monitor = 0; break; case QEMU_OPTION_chardev: - opts = qemu_opts_parse(&qemu_chardev_opts, optarg, "backend"); + opts = qemu_opts_parse(qemu_find_opts("chardev"), optarg, 1); + if (!opts) { + exit(1); + } + break; + case QEMU_OPTION_fsdev: + olist = qemu_find_opts("fsdev"); + if (!olist) { + fprintf(stderr, "fsdev is not supported by this qemu build.\n"); + exit(1); + } + opts = qemu_opts_parse(olist, optarg, 1); + if (!opts) { + fprintf(stderr, "parse error: %s\n", optarg); + exit(1); + } + break; + case QEMU_OPTION_virtfs: { + char *arg_fsdev = NULL; + char *arg_9p = NULL; + int len = 0; + + olist = qemu_find_opts("virtfs"); + if (!olist) { + fprintf(stderr, "virtfs is not supported by this qemu build.\n"); + exit(1); + } + opts = qemu_opts_parse(olist, optarg, 1); if (!opts) { fprintf(stderr, "parse error: %s\n", optarg); exit(1); } + + if (qemu_opt_get(opts, "fstype") == NULL || + qemu_opt_get(opts, "mount_tag") == NULL || + qemu_opt_get(opts, "path") == NULL || + qemu_opt_get(opts, "security_model") == NULL) { + fprintf(stderr, "Usage: -virtfs fstype,path=/share_path/," + "security_model=[mapped|passthrough|none]," + "mnt_tag=tag.\n"); + exit(1); + } + + len = strlen(",id=,path=,security_model="); + len += strlen(qemu_opt_get(opts, "fstype")); + len += strlen(qemu_opt_get(opts, "mount_tag")); + len += strlen(qemu_opt_get(opts, "path")); + len += strlen(qemu_opt_get(opts, "security_model")); + arg_fsdev = qemu_malloc((len + 1) * sizeof(*arg_fsdev)); + + snprintf(arg_fsdev, (len + 1) * sizeof(*arg_fsdev), + "%s,id=%s,path=%s,security_model=%s", + qemu_opt_get(opts, "fstype"), + qemu_opt_get(opts, "mount_tag"), + qemu_opt_get(opts, "path"), + qemu_opt_get(opts, "security_model")); + + len = strlen("virtio-9p-pci,fsdev=,mount_tag="); + len += 2*strlen(qemu_opt_get(opts, "mount_tag")); + arg_9p = qemu_malloc((len + 1) * sizeof(*arg_9p)); + + snprintf(arg_9p, (len + 1) * sizeof(*arg_9p), + "virtio-9p-pci,fsdev=%s,mount_tag=%s", + qemu_opt_get(opts, "mount_tag"), + qemu_opt_get(opts, "mount_tag")); + + if (!qemu_opts_parse(qemu_find_opts("fsdev"), arg_fsdev, 1)) { + fprintf(stderr, "parse error [fsdev]: %s\n", optarg); + exit(1); + } + + if (!qemu_opts_parse(qemu_find_opts("device"), arg_9p, 1)) { + fprintf(stderr, "parse error [device]: %s\n", optarg); + exit(1); + } + + qemu_free(arg_fsdev); + qemu_free(arg_9p); break; + } case QEMU_OPTION_serial: add_device_config(DEV_SERIAL, optarg); default_serial = 0; @@ -5462,6 +2526,9 @@ default_monitor = 0; } break; + case QEMU_OPTION_debugcon: + add_device_config(DEV_DEBUGCON, optarg); + break; case QEMU_OPTION_loadvm: loadvm = optarg; break; @@ -5488,7 +2555,6 @@ case QEMU_OPTION_pidfile: pid_file = optarg; break; -#ifdef TARGET_I386 case QEMU_OPTION_win2k_hack: win2k_install_hack = 1; break; @@ -5496,27 +2562,22 @@ rtc_td_hack = 1; break; case QEMU_OPTION_acpitable: - if(acpi_table_add(optarg) < 0) { - fprintf(stderr, "Wrong acpi table provided\n"); - exit(1); - } + do_acpitable_option(optarg); break; case QEMU_OPTION_smbios: - if(smbios_entry_add(optarg) < 0) { - fprintf(stderr, "Wrong smbios provided\n"); - exit(1); - } + do_smbios_option(optarg); break; -#endif -#ifdef CONFIG_KVM -#ifdef KVM_UPSTREAM case QEMU_OPTION_enable_kvm: kvm_allowed = 1; -#endif break; case QEMU_OPTION_no_kvm: kvm_allowed = 0; +#ifdef CONFIG_NO_CPU_EMULATION + fprintf(stderr, "cpu emulation not configured\n"); + exit(1); +#endif break; +#ifdef CONFIG_KVM case QEMU_OPTION_no_kvm_irqchip: { kvm_irqchip = 0; kvm_pit = 0; @@ -5534,16 +2595,6 @@ kvm_nested = 1; break; } -#if defined(TARGET_I386) || defined(TARGET_X86_64) || defined(TARGET_IA64) || defined(__linux__) - case QEMU_OPTION_pcidevice: - if (assigned_devices_index >= MAX_DEV_ASSIGN_CMDLINE) { - fprintf(stderr, "Too many assigned devices\n"); - exit(1); - } - assigned_devices[assigned_devices_index] = optarg; - assigned_devices_index++; - break; -#endif #endif case QEMU_OPTION_usb: usb_enabled = 1; @@ -5553,7 +2604,7 @@ add_device_config(DEV_USB, optarg); break; case QEMU_OPTION_device: - if (!qemu_opts_parse(&qemu_device_opts, optarg, "driver")) { + if (!qemu_opts_parse(qemu_find_opts("device"), optarg, 1)) { exit(1); } break; @@ -5574,10 +2625,9 @@ } break; case QEMU_OPTION_vnc: - display_type = DT_VNC; + display_remote++; vnc_display = optarg; break; -#ifdef TARGET_I386 case QEMU_OPTION_no_acpi: acpi_enabled = 0; break; @@ -5590,7 +2640,6 @@ exit(1); } break; -#endif case QEMU_OPTION_no_reboot: no_reboot = 1; break; @@ -5607,38 +2656,30 @@ exit(1); } break; -#ifndef _WIN32 - case QEMU_OPTION_daemonize: - daemonize = 1; - break; -#endif case QEMU_OPTION_option_rom: if (nb_option_roms >= MAX_OPTION_ROMS) { fprintf(stderr, "Too many option ROMs\n"); exit(1); } - option_rom[nb_option_roms] = optarg; + opts = qemu_opts_parse(qemu_find_opts("option-rom"), optarg, 1); + option_rom[nb_option_roms].name = qemu_opt_get(opts, "romfile"); + option_rom[nb_option_roms].bootindex = + qemu_opt_get_number(opts, "bootindex", -1); + if (!option_rom[nb_option_roms].name) { + fprintf(stderr, "Option ROM file is not specified\n"); + exit(1); + } nb_option_roms++; break; -#if defined(TARGET_ARM) || defined(TARGET_M68K) case QEMU_OPTION_semihosting: semihosting_enabled = 1; break; -#endif case QEMU_OPTION_tdf: time_drift_fix = 1; break; case QEMU_OPTION_kvm_shadow_memory: kvm_shadow_memory = (int64_t)atoi(optarg) * 1024 * 1024 / 4096; break; - case QEMU_OPTION_mempath: - mem_path = optarg; - break; -#ifdef MAP_POPULATE - case QEMU_OPTION_mem_prealloc: - mem_prealloc = !mem_prealloc; - break; -#endif case QEMU_OPTION_name: qemu_name = qemu_strdup(optarg); { @@ -5646,15 +2687,14 @@ if (p != NULL) { *p++ = 0; if (strncmp(p, "process=", 8)) { - fprintf(stderr, "Unknown subargument %s to -name", p); + fprintf(stderr, "Unknown subargument %s to -name\n", p); exit(1); } p += 8; - set_proc_name(p); + os_set_proc_name(p); } } break; -#if defined(TARGET_SPARC) || defined(TARGET_PPC) case QEMU_OPTION_prom_env: if (nb_prom_envs >= MAX_PROM_ENVS) { fprintf(stderr, "Too many prom variables\n"); @@ -5663,12 +2703,9 @@ prom_envs[nb_prom_envs] = optarg; nb_prom_envs++; break; -#endif -#ifdef TARGET_ARM case QEMU_OPTION_old_param: old_param = 1; break; -#endif case QEMU_OPTION_clock: configure_alarms(optarg); break; @@ -5676,9 +2713,8 @@ configure_rtc_date_offset(optarg, 1); break; case QEMU_OPTION_rtc: - opts = qemu_opts_parse(&qemu_rtc_opts, optarg, NULL); + opts = qemu_opts_parse(qemu_find_opts("rtc"), optarg, 0); if (!opts) { - fprintf(stderr, "parse error: %s\n", optarg); exit(1); } configure_rtc(opts); @@ -5689,15 +2725,11 @@ tb_size = 0; break; case QEMU_OPTION_icount: - use_icount = 1; - if (strcmp(optarg, "auto") == 0) { - icount_time_shift = -1; - } else { - icount_time_shift = strtol(optarg, NULL, 0); - } + icount_option = optarg; break; case QEMU_OPTION_incoming: incoming = optarg; + incoming_expected = true; break; case QEMU_OPTION_nodefaults: default_serial = 0; @@ -5711,41 +2743,61 @@ default_sdcard = 0; break; #ifndef _WIN32 - case QEMU_OPTION_chroot: - chroot_dir = optarg; - break; - case QEMU_OPTION_runas: - run_as = optarg; - break; case QEMU_OPTION_nvram: nvram = optarg; break; #endif -#ifdef CONFIG_XEN case QEMU_OPTION_xen_domid: + if (!(xen_available())) { + printf("Option %s not supported for this target\n", popt->name); + exit(1); + } xen_domid = atoi(optarg); break; case QEMU_OPTION_xen_create: + if (!(xen_available())) { + printf("Option %s not supported for this target\n", popt->name); + exit(1); + } xen_mode = XEN_CREATE; break; case QEMU_OPTION_xen_attach: + if (!(xen_available())) { + printf("Option %s not supported for this target\n", popt->name); + exit(1); + } xen_mode = XEN_ATTACH; break; +#ifdef CONFIG_SIMPLE_TRACE + case QEMU_OPTION_trace: + opts = qemu_opts_parse(qemu_find_opts("trace"), optarg, 0); + if (opts) { + trace_file = qemu_opt_get(opts, "file"); + } + break; #endif case QEMU_OPTION_readconfig: { - FILE *fp; - fp = fopen(optarg, "r"); - if (fp == NULL) { - fprintf(stderr, "open %s: %s\n", optarg, strerror(errno)); + int ret = qemu_read_config_file(optarg); + if (ret < 0) { + fprintf(stderr, "read config %s: %s\n", optarg, + strerror(-ret)); exit(1); } - if (qemu_config_parse(fp) != 0) { - exit(1); - } - fclose(fp); break; } + case QEMU_OPTION_spice: + olist = qemu_find_opts("spice"); + if (!olist) { + fprintf(stderr, "spice is not supported by this qemu build.\n"); + exit(1); + } + opts = qemu_opts_parse(olist, optarg, 0); + if (!opts) { + fprintf(stderr, "parse error: %s\n", optarg); + exit(1); + } + break; case QEMU_OPTION_writeconfig: { FILE *fp; @@ -5762,20 +2814,29 @@ fclose(fp); break; } + default: + os_parse_cmd_args(popt->index, optarg); } } } + loc_set_none(); /* If no data_dir is specified then try to find it relative to the executable path. */ if (!data_dir) { - data_dir = find_datadir(argv[0]); + data_dir = os_find_datadir(argv[0]); } /* If all else fails use the install patch specified when building. */ if (!data_dir) { - data_dir = CONFIG_QEMU_SHAREDIR; + data_dir = CONFIG_QEMU_DATADIR; } +#ifdef CONFIG_SIMPLE_TRACE + /* + * Set the trace file name, if specified. + */ + st_set_trace_file(trace_file); +#endif /* * Default to max_cpus = smp_cpus, in case the user doesn't * specify a max_cpus value. @@ -5791,8 +2852,8 @@ exit(1); } - qemu_opts_foreach(&qemu_device_opts, default_driver_check, NULL, 0); - qemu_opts_foreach(&qemu_global_opts, default_driver_check, NULL, 0); + qemu_opts_foreach(qemu_find_opts("device"), default_driver_check, NULL, 0); + qemu_opts_foreach(qemu_find_opts("global"), default_driver_check, NULL, 0); if (machine->no_serial) { default_serial = 0; @@ -5844,84 +2905,37 @@ if (default_vga) vga_interface_type = VGA_CIRRUS; - if (qemu_opts_foreach(&qemu_chardev_opts, chardev_init_func, NULL, 1) != 0) - exit(1); - -#ifndef _WIN32 - if (daemonize) { - pid_t pid; - - if (pipe(fds) == -1) - exit(1); - - pid = fork(); - if (pid > 0) { - uint8_t status; - ssize_t len; - - close(fds[1]); - - again: - len = read(fds[0], &status, 1); - if (len == -1 && (errno == EINTR)) - goto again; - - if (len != 1) - exit(1); - else if (status == 1) { - fprintf(stderr, "Could not acquire pidfile: %s\n", strerror(errno)); - exit(1); - } else - exit(0); - } else if (pid < 0) - exit(1); - - close(fds[0]); - qemu_set_cloexec(fds[1]); - - setsid(); + socket_init(); - pid = fork(); - if (pid > 0) - exit(0); - else if (pid < 0) - exit(1); - - umask(027); - - signal(SIGTSTP, SIG_IGN); - signal(SIGTTOU, SIG_IGN); - signal(SIGTTIN, SIG_IGN); + if (qemu_opts_foreach(qemu_find_opts("chardev"), chardev_init_func, NULL, 1) != 0) + exit(1); +#ifdef CONFIG_VIRTFS + if (qemu_opts_foreach(qemu_find_opts("fsdev"), fsdev_init_func, NULL, 1) != 0) { + exit(1); } +#endif + + os_daemonize(); if (pid_file && qemu_create_pidfile(pid_file) != 0) { - if (daemonize) { - uint8_t status = 1; - write(fds[1], &status, 1); - } else - fprintf(stderr, "Could not acquire pid file: %s\n", strerror(errno)); + os_pidfile_error(); exit(1); } -#endif - - if (kvm_enabled()) { - int ret; - ret = kvm_init(smp_cpus); + if (kvm_allowed) { + int ret = kvm_init(); if (ret < 0) { -#if defined(KVM_UPSTREAM) || defined(CONFIG_NO_CPU_EMULATION) - fprintf(stderr, "failed to initialize KVM\n"); - exit(1); -#endif + if (kvm_allowed > 0) { + if (!kvm_available()) { + printf("KVM not supported for this target\n"); + } else { + fprintf(stderr, "failed to initialize KVM: %s\n", strerror(-ret)); + } + exit(1); + } fprintf(stderr, "Could not initialize KVM, will disable KVM support\n"); - kvm_allowed = 0; - } - } else { - /* without kvm enabled, we can only support 4095 MB RAM */ - if (ram_size > (4095UL << 20)) { - fprintf(stderr, "qemu: without kvm support at most 4095 MB RAM can be simulated\n"); - exit(1); } + kvm_allowed = ret >= 0; } if (qemu_init_main_loop()) { @@ -5940,26 +2954,13 @@ exit(1); } -#ifndef _WIN32 - /* Win32 doesn't support line-buffering and requires size >= 2 */ - setvbuf(stdout, NULL, _IOLBF, 0); -#endif + os_set_line_buffering(); if (init_timer_alarm() < 0) { fprintf(stderr, "could not initialize alarm timer\n"); exit(1); } - if (use_icount && icount_time_shift < 0) { - use_icount = 2; - /* 125MIPS seems a reasonable initial guess at the guest speed. - It will be corrected fairly quickly anyway. */ - icount_time_shift = 3; - init_icount_adjust(); - } - -#ifdef _WIN32 - socket_init(); -#endif + configure_icount(icount_option); if (net_init_clients() < 0) { exit(1); @@ -5980,29 +2981,20 @@ blk_mig_init(); - if (default_cdrom) { - /* we always create the cdrom drive, even if no disk is there */ - drive_add(NULL, CDROM_ALIAS); - } - - if (default_floppy) { - /* we always create at least one floppy */ - drive_add(NULL, FD_ALIAS, 0); - } - - if (default_sdcard) { - /* we always create one sd slot, even if no card is in it */ - drive_add(NULL, SD_ALIAS); - } - /* open the virtual block devices */ if (snapshot) - qemu_opts_foreach(&qemu_drive_opts, drive_enable_snapshot, NULL, 0); - if (qemu_opts_foreach(&qemu_drive_opts, drive_init_func, machine, 1) != 0) + qemu_opts_foreach(qemu_find_opts("drive"), drive_enable_snapshot, NULL, 0); + if (qemu_opts_foreach(qemu_find_opts("drive"), drive_init_func, &machine->use_scsi, 1) != 0) exit(1); - vmstate_register(0, &vmstate_timers ,&timers_state); - register_savevm_live("ram", 0, 3, NULL, ram_save_live, NULL, + default_drive(default_cdrom, snapshot, machine->use_scsi, + IF_DEFAULT, 2, CDROM_OPTS); + default_drive(default_floppy, snapshot, machine->use_scsi, + IF_FLOPPY, 0, FD_OPTS); + default_drive(default_sdcard, snapshot, machine->use_scsi, + IF_SD, 0, SD_OPTS); + + register_savevm_live(NULL, "ram", 0, 4, NULL, ram_save_live, NULL, ram_load, NULL); if (nb_numa_nodes > 0) { @@ -6047,15 +3039,24 @@ } } + if (qemu_opts_foreach(qemu_find_opts("mon"), mon_init_func, NULL, 1) != 0) { + exit(1); + } + if (foreach_device_config(DEV_SERIAL, serial_parse) < 0) exit(1); if (foreach_device_config(DEV_PARALLEL, parallel_parse) < 0) exit(1); if (foreach_device_config(DEV_VIRTCON, virtcon_parse) < 0) exit(1); + if (foreach_device_config(DEV_DEBUGCON, debugcon_parse) < 0) + exit(1); module_call_init(MODULE_INIT_DEVICE); + if (qemu_opts_foreach(qemu_find_opts("device"), device_help_func, NULL, 0) != 0) + exit(0); + if (watchdog) { i = select_watchdog(watchdog); if (i > 0) @@ -6070,19 +3071,12 @@ machine->init(ram_size, boot_devices, kernel_filename, kernel_cmdline, initrd_filename, cpu_model); + cpu_synchronize_all_post_init(); -#ifndef _WIN32 /* must be after terminal init, SDL library changes signal handlers */ - sighandler_setup(); -#endif + os_setup_signal_handling(); - for (env = first_cpu; env != NULL; env = env->next_cpu) { - for (i = 0; i < nb_numa_nodes; i++) { - if (node_cpumask[i] & (1 << env->cpu_index)) { - env->numa_node = i; - } - } - } + set_numa_modes(); current_machine = machine; @@ -6093,27 +3087,27 @@ } /* init generic devices */ - if (qemu_opts_foreach(&qemu_device_opts, device_init_func, NULL, 1) != 0) + if (qemu_opts_foreach(qemu_find_opts("device"), device_init_func, NULL, 1) != 0) exit(1); net_check_clients(); - if (!display_state) - dumb_display_init(); /* just use the first displaystate for the moment */ - ds = display_state; + ds = get_displaystate(); - if (display_type == DT_DEFAULT) { + if (using_spice) + display_remote++; + if (display_type == DT_DEFAULT && !display_remote) { #if defined(CONFIG_SDL) || defined(CONFIG_COCOA) display_type = DT_SDL; #else - display_type = DT_VNC; vnc_display = "localhost:0,to=99"; show_vnc_port = 1; #endif } + /* init local displays */ switch (display_type) { case DT_NOGRAPHIC: break; @@ -6131,7 +3125,12 @@ cocoa_display_init(ds, full_screen); break; #endif - case DT_VNC: + default: + break; + } + + /* init remote displays */ + if (vnc_display) { vnc_display_init(ds); if (vnc_display_open(ds, vnc_display) < 0) exit(1); @@ -6139,30 +3138,29 @@ if (show_vnc_port) { printf("VNC server running on `%s'\n", vnc_display_local_addr(ds)); } - break; - default: - break; } - dpy_resize(ds); +#ifdef CONFIG_SPICE + if (using_spice && !qxl_enabled) { + qemu_spice_display_init(ds); + } +#endif + /* display setup */ + 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)); + break; } dcl = dcl->next; } - - if (display_type == DT_NOGRAPHIC || display_type == DT_VNC) { + if (ds->gui_timer == NULL) { nographic_timer = qemu_new_timer(rt_clock, nographic_update, NULL); qemu_mod_timer(nographic_timer, qemu_get_clock(rt_clock)); } - - text_consoles_set_display(display_state); - - if (qemu_opts_foreach(&qemu_mon_opts, mon_init_func, NULL, 1) != 0) - exit(1); + text_consoles_set_display(ds); if (gdbstub_dev && gdbserver_start(gdbstub_dev) < 0) { fprintf(stderr, "qemu: could not open gdbserver on device '%s'\n", @@ -6177,77 +3175,30 @@ exit(1); } + /* TODO: once all bus devices are qdevified, this should be done + * when bus is created by qdev.c */ + qemu_register_reset(qbus_reset_all_fn, sysbus_get_default()); + qemu_run_machine_init_done_notifiers(); + qemu_system_reset(); if (loadvm) { - if (load_vmstate(cur_mon, loadvm) < 0) { + if (load_vmstate(loadvm) < 0) { autostart = 0; } } if (incoming) { - qemu_start_incoming_migration(incoming); + int ret = qemu_start_incoming_migration(incoming); + if (ret < 0) { + fprintf(stderr, "Migration failed. Exit code %s(%d), exiting.\n", + incoming, ret); + exit(ret); + } } else if (autostart) { vm_start(); } -#ifndef _WIN32 - if (daemonize) { - uint8_t status = 0; - ssize_t len; - - again1: - len = write(fds[1], &status, 1); - if (len == -1 && (errno == EINTR)) - goto again1; - - if (len != 1) - exit(1); - - chdir("/"); - TFR(fd = qemu_open("/dev/null", O_RDWR)); - if (fd == -1) - exit(1); - } - - if (run_as) { - pwd = getpwnam(run_as); - if (!pwd) { - fprintf(stderr, "User \"%s\" doesn't exist\n", run_as); - exit(1); - } - } - - if (chroot_dir) { - if (chroot(chroot_dir) < 0) { - fprintf(stderr, "chroot failed\n"); - exit(1); - } - chdir("/"); - } - - if (run_as) { - if (setgid(pwd->pw_gid) < 0) { - fprintf(stderr, "Failed to setgid(%d)\n", pwd->pw_gid); - exit(1); - } - if (setuid(pwd->pw_uid) < 0) { - fprintf(stderr, "Failed to setuid(%d)\n", pwd->pw_uid); - exit(1); - } - if (setuid(0) != -1) { - fprintf(stderr, "Dropping privileges failed\n"); - exit(1); - } - } - - if (daemonize) { - dup2(fd, 0); - dup2(fd, 1); - dup2(fd, 2); - - close(fd); - } -#endif + os_setup_post(); main_loop(); quit_timers(); diff -Nru qemu-kvm-0.12.5+noroms/vnc-auth-sasl.c qemu-kvm-0.14.1/vnc-auth-sasl.c --- qemu-kvm-0.12.5+noroms/vnc-auth-sasl.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/vnc-auth-sasl.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,636 +0,0 @@ -/* - * QEMU VNC display driver: SASL auth protocol - * - * Copyright (C) 2009 Red Hat, Inc - * - * 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 "vnc.h" - -/* Max amount of data we send/recv for SASL steps to prevent DOS */ -#define SASL_DATA_MAX_LEN (1024 * 1024) - - -void vnc_sasl_client_cleanup(VncState *vs) -{ - if (vs->sasl.conn) { - vs->sasl.runSSF = vs->sasl.waitWriteSSF = vs->sasl.wantSSF = 0; - vs->sasl.encodedLength = vs->sasl.encodedOffset = 0; - vs->sasl.encoded = NULL; - free(vs->sasl.username); - free(vs->sasl.mechlist); - vs->sasl.username = vs->sasl.mechlist = NULL; - sasl_dispose(&vs->sasl.conn); - vs->sasl.conn = NULL; - } -} - - -long vnc_client_write_sasl(VncState *vs) -{ - long ret; - - VNC_DEBUG("Write SASL: Pending output %p size %d offset %d Encoded: %p size %d offset %d\n", - vs->output.buffer, vs->output.capacity, vs->output.offset, - vs->sasl.encoded, vs->sasl.encodedLength, vs->sasl.encodedOffset); - - if (!vs->sasl.encoded) { - int err; - err = sasl_encode(vs->sasl.conn, - (char *)vs->output.buffer, - vs->output.offset, - (const char **)&vs->sasl.encoded, - &vs->sasl.encodedLength); - if (err != SASL_OK) - return vnc_client_io_error(vs, -1, EIO); - - vs->sasl.encodedOffset = 0; - } - - ret = vnc_client_write_buf(vs, - vs->sasl.encoded + vs->sasl.encodedOffset, - vs->sasl.encodedLength - vs->sasl.encodedOffset); - if (!ret) - return 0; - - vs->sasl.encodedOffset += ret; - if (vs->sasl.encodedOffset == vs->sasl.encodedLength) { - vs->output.offset = 0; - vs->sasl.encoded = NULL; - vs->sasl.encodedOffset = vs->sasl.encodedLength = 0; - } - - /* Can't merge this block with one above, because - * someone might have written more unencrypted - * data in vs->output while we were processing - * SASL encoded output - */ - if (vs->output.offset == 0) { - qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs); - } - - return ret; -} - - -long vnc_client_read_sasl(VncState *vs) -{ - long ret; - uint8_t encoded[4096]; - const char *decoded; - unsigned int decodedLen; - int err; - - ret = vnc_client_read_buf(vs, encoded, sizeof(encoded)); - if (!ret) - return 0; - - err = sasl_decode(vs->sasl.conn, - (char *)encoded, ret, - &decoded, &decodedLen); - - if (err != SASL_OK) - return vnc_client_io_error(vs, -1, -EIO); - VNC_DEBUG("Read SASL Encoded %p size %ld Decoded %p size %d\n", - encoded, ret, decoded, decodedLen); - buffer_reserve(&vs->input, decodedLen); - buffer_append(&vs->input, decoded, decodedLen); - return decodedLen; -} - - -static int vnc_auth_sasl_check_access(VncState *vs) -{ - const void *val; - int err; - int allow; - - err = sasl_getprop(vs->sasl.conn, SASL_USERNAME, &val); - if (err != SASL_OK) { - VNC_DEBUG("cannot query SASL username on connection %d (%s), denying access\n", - err, sasl_errstring(err, NULL, NULL)); - return -1; - } - if (val == NULL) { - VNC_DEBUG("no client username was found, denying access\n"); - return -1; - } - VNC_DEBUG("SASL client username %s\n", (const char *)val); - - vs->sasl.username = qemu_strdup((const char*)val); - - if (vs->vd->sasl.acl == NULL) { - VNC_DEBUG("no ACL activated, allowing access\n"); - return 0; - } - - allow = qemu_acl_party_is_allowed(vs->vd->sasl.acl, vs->sasl.username); - - VNC_DEBUG("SASL client %s %s by ACL\n", vs->sasl.username, - allow ? "allowed" : "denied"); - return allow ? 0 : -1; -} - -static int vnc_auth_sasl_check_ssf(VncState *vs) -{ - const void *val; - int err, ssf; - - if (!vs->sasl.wantSSF) - return 1; - - err = sasl_getprop(vs->sasl.conn, SASL_SSF, &val); - if (err != SASL_OK) - return 0; - - ssf = *(const int *)val; - VNC_DEBUG("negotiated an SSF of %d\n", ssf); - if (ssf < 56) - return 0; /* 56 is good for Kerberos */ - - /* Only setup for read initially, because we're about to send an RPC - * reply which must be in plain text. When the next incoming RPC - * arrives, we'll switch on writes too - * - * cf qemudClientReadSASL in qemud.c - */ - vs->sasl.runSSF = 1; - - /* We have a SSF that's good enough */ - return 1; -} - -/* - * Step Msg - * - * Input from client: - * - * u32 clientin-length - * u8-array clientin-string - * - * Output to client: - * - * u32 serverout-length - * u8-array serverout-strin - * u8 continue - */ - -static int protocol_client_auth_sasl_step_len(VncState *vs, uint8_t *data, size_t len); - -static int protocol_client_auth_sasl_step(VncState *vs, uint8_t *data, size_t len) -{ - uint32_t datalen = len; - const char *serverout; - unsigned int serveroutlen; - int err; - char *clientdata = NULL; - - /* NB, distinction of NULL vs "" is *critical* in SASL */ - if (datalen) { - clientdata = (char*)data; - clientdata[datalen-1] = '\0'; /* Wire includes '\0', but make sure */ - datalen--; /* Don't count NULL byte when passing to _start() */ - } - - VNC_DEBUG("Step using SASL Data %p (%d bytes)\n", - clientdata, datalen); - err = sasl_server_step(vs->sasl.conn, - clientdata, - datalen, - &serverout, - &serveroutlen); - if (err != SASL_OK && - err != SASL_CONTINUE) { - VNC_DEBUG("sasl step failed %d (%s)\n", - err, sasl_errdetail(vs->sasl.conn)); - sasl_dispose(&vs->sasl.conn); - vs->sasl.conn = NULL; - goto authabort; - } - - if (serveroutlen > SASL_DATA_MAX_LEN) { - VNC_DEBUG("sasl step reply data too long %d\n", - serveroutlen); - sasl_dispose(&vs->sasl.conn); - vs->sasl.conn = NULL; - goto authabort; - } - - VNC_DEBUG("SASL return data %d bytes, nil; %d\n", - serveroutlen, serverout ? 0 : 1); - - if (serveroutlen) { - vnc_write_u32(vs, serveroutlen + 1); - vnc_write(vs, serverout, serveroutlen + 1); - } else { - vnc_write_u32(vs, 0); - } - - /* Whether auth is complete */ - vnc_write_u8(vs, err == SASL_CONTINUE ? 0 : 1); - - if (err == SASL_CONTINUE) { - VNC_DEBUG("%s", "Authentication must continue\n"); - /* Wait for step length */ - vnc_read_when(vs, protocol_client_auth_sasl_step_len, 4); - } else { - if (!vnc_auth_sasl_check_ssf(vs)) { - VNC_DEBUG("Authentication rejected for weak SSF %d\n", vs->csock); - goto authreject; - } - - /* Check username whitelist ACL */ - if (vnc_auth_sasl_check_access(vs) < 0) { - VNC_DEBUG("Authentication rejected for ACL %d\n", vs->csock); - goto authreject; - } - - VNC_DEBUG("Authentication successful %d\n", vs->csock); - vnc_write_u32(vs, 0); /* Accept auth */ - /* - * Delay writing in SSF encoded mode until pending output - * buffer is written - */ - if (vs->sasl.runSSF) - vs->sasl.waitWriteSSF = vs->output.offset; - start_client_init(vs); - } - - return 0; - - authreject: - vnc_write_u32(vs, 1); /* Reject auth */ - vnc_write_u32(vs, sizeof("Authentication failed")); - vnc_write(vs, "Authentication failed", sizeof("Authentication failed")); - vnc_flush(vs); - vnc_client_error(vs); - return -1; - - authabort: - vnc_client_error(vs); - return -1; -} - -static int protocol_client_auth_sasl_step_len(VncState *vs, uint8_t *data, size_t len) -{ - uint32_t steplen = read_u32(data, 0); - VNC_DEBUG("Got client step len %d\n", steplen); - if (steplen > SASL_DATA_MAX_LEN) { - VNC_DEBUG("Too much SASL data %d\n", steplen); - vnc_client_error(vs); - return -1; - } - - if (steplen == 0) - return protocol_client_auth_sasl_step(vs, NULL, 0); - else - vnc_read_when(vs, protocol_client_auth_sasl_step, steplen); - return 0; -} - -/* - * Start Msg - * - * Input from client: - * - * u32 clientin-length - * u8-array clientin-string - * - * Output to client: - * - * u32 serverout-length - * u8-array serverout-strin - * u8 continue - */ - -#define SASL_DATA_MAX_LEN (1024 * 1024) - -static int protocol_client_auth_sasl_start(VncState *vs, uint8_t *data, size_t len) -{ - uint32_t datalen = len; - const char *serverout; - unsigned int serveroutlen; - int err; - char *clientdata = NULL; - - /* NB, distinction of NULL vs "" is *critical* in SASL */ - if (datalen) { - clientdata = (char*)data; - clientdata[datalen-1] = '\0'; /* Should be on wire, but make sure */ - datalen--; /* Don't count NULL byte when passing to _start() */ - } - - VNC_DEBUG("Start SASL auth with mechanism %s. Data %p (%d bytes)\n", - vs->sasl.mechlist, clientdata, datalen); - err = sasl_server_start(vs->sasl.conn, - vs->sasl.mechlist, - clientdata, - datalen, - &serverout, - &serveroutlen); - if (err != SASL_OK && - err != SASL_CONTINUE) { - VNC_DEBUG("sasl start failed %d (%s)\n", - err, sasl_errdetail(vs->sasl.conn)); - sasl_dispose(&vs->sasl.conn); - vs->sasl.conn = NULL; - goto authabort; - } - if (serveroutlen > SASL_DATA_MAX_LEN) { - VNC_DEBUG("sasl start reply data too long %d\n", - serveroutlen); - sasl_dispose(&vs->sasl.conn); - vs->sasl.conn = NULL; - goto authabort; - } - - VNC_DEBUG("SASL return data %d bytes, nil; %d\n", - serveroutlen, serverout ? 0 : 1); - - if (serveroutlen) { - vnc_write_u32(vs, serveroutlen + 1); - vnc_write(vs, serverout, serveroutlen + 1); - } else { - vnc_write_u32(vs, 0); - } - - /* Whether auth is complete */ - vnc_write_u8(vs, err == SASL_CONTINUE ? 0 : 1); - - if (err == SASL_CONTINUE) { - VNC_DEBUG("%s", "Authentication must continue\n"); - /* Wait for step length */ - vnc_read_when(vs, protocol_client_auth_sasl_step_len, 4); - } else { - if (!vnc_auth_sasl_check_ssf(vs)) { - VNC_DEBUG("Authentication rejected for weak SSF %d\n", vs->csock); - goto authreject; - } - - /* Check username whitelist ACL */ - if (vnc_auth_sasl_check_access(vs) < 0) { - VNC_DEBUG("Authentication rejected for ACL %d\n", vs->csock); - goto authreject; - } - - VNC_DEBUG("Authentication successful %d\n", vs->csock); - vnc_write_u32(vs, 0); /* Accept auth */ - start_client_init(vs); - } - - return 0; - - authreject: - vnc_write_u32(vs, 1); /* Reject auth */ - vnc_write_u32(vs, sizeof("Authentication failed")); - vnc_write(vs, "Authentication failed", sizeof("Authentication failed")); - vnc_flush(vs); - vnc_client_error(vs); - return -1; - - authabort: - vnc_client_error(vs); - return -1; -} - -static int protocol_client_auth_sasl_start_len(VncState *vs, uint8_t *data, size_t len) -{ - uint32_t startlen = read_u32(data, 0); - VNC_DEBUG("Got client start len %d\n", startlen); - if (startlen > SASL_DATA_MAX_LEN) { - VNC_DEBUG("Too much SASL data %d\n", startlen); - vnc_client_error(vs); - return -1; - } - - if (startlen == 0) - return protocol_client_auth_sasl_start(vs, NULL, 0); - - vnc_read_when(vs, protocol_client_auth_sasl_start, startlen); - return 0; -} - -static int protocol_client_auth_sasl_mechname(VncState *vs, uint8_t *data, size_t len) -{ - char *mechname = malloc(len + 1); - if (!mechname) { - VNC_DEBUG("Out of memory reading mechname\n"); - vnc_client_error(vs); - } - strncpy(mechname, (char*)data, len); - mechname[len] = '\0'; - VNC_DEBUG("Got client mechname '%s' check against '%s'\n", - mechname, vs->sasl.mechlist); - - if (strncmp(vs->sasl.mechlist, mechname, len) == 0) { - if (vs->sasl.mechlist[len] != '\0' && - vs->sasl.mechlist[len] != ',') { - VNC_DEBUG("One %d", vs->sasl.mechlist[len]); - vnc_client_error(vs); - return -1; - } - } else { - char *offset = strstr(vs->sasl.mechlist, mechname); - VNC_DEBUG("Two %p\n", offset); - if (!offset) { - vnc_client_error(vs); - return -1; - } - VNC_DEBUG("Two '%s'\n", offset); - if (offset[-1] != ',' || - (offset[len] != '\0'&& - offset[len] != ',')) { - vnc_client_error(vs); - return -1; - } - } - - free(vs->sasl.mechlist); - vs->sasl.mechlist = mechname; - - VNC_DEBUG("Validated mechname '%s'\n", mechname); - vnc_read_when(vs, protocol_client_auth_sasl_start_len, 4); - return 0; -} - -static int protocol_client_auth_sasl_mechname_len(VncState *vs, uint8_t *data, size_t len) -{ - uint32_t mechlen = read_u32(data, 0); - VNC_DEBUG("Got client mechname len %d\n", mechlen); - if (mechlen > 100) { - VNC_DEBUG("Too long SASL mechname data %d\n", mechlen); - vnc_client_error(vs); - return -1; - } - if (mechlen < 1) { - VNC_DEBUG("Too short SASL mechname %d\n", mechlen); - vnc_client_error(vs); - return -1; - } - vnc_read_when(vs, protocol_client_auth_sasl_mechname,mechlen); - return 0; -} - -#define USES_X509_AUTH(vs) \ - ((vs)->subauth == VNC_AUTH_VENCRYPT_X509NONE || \ - (vs)->subauth == VNC_AUTH_VENCRYPT_X509VNC || \ - (vs)->subauth == VNC_AUTH_VENCRYPT_X509PLAIN || \ - (vs)->subauth == VNC_AUTH_VENCRYPT_X509SASL) - - -void start_auth_sasl(VncState *vs) -{ - const char *mechlist = NULL; - sasl_security_properties_t secprops; - int err; - char *localAddr, *remoteAddr; - int mechlistlen; - - VNC_DEBUG("Initialize SASL auth %d\n", vs->csock); - - /* Get local & remote client addresses in form IPADDR;PORT */ - if (!(localAddr = vnc_socket_local_addr("%s;%s", vs->csock))) - goto authabort; - - if (!(remoteAddr = vnc_socket_remote_addr("%s;%s", vs->csock))) { - free(localAddr); - goto authabort; - } - - err = sasl_server_new("vnc", - NULL, /* FQDN - just delegates to gethostname */ - NULL, /* User realm */ - localAddr, - remoteAddr, - NULL, /* Callbacks, not needed */ - SASL_SUCCESS_DATA, - &vs->sasl.conn); - free(localAddr); - free(remoteAddr); - localAddr = remoteAddr = NULL; - - if (err != SASL_OK) { - VNC_DEBUG("sasl context setup failed %d (%s)", - err, sasl_errstring(err, NULL, NULL)); - vs->sasl.conn = NULL; - goto authabort; - } - -#ifdef CONFIG_VNC_TLS - /* Inform SASL that we've got an external SSF layer from TLS/x509 */ - if (vs->vd->auth == VNC_AUTH_VENCRYPT && - vs->vd->subauth == VNC_AUTH_VENCRYPT_X509SASL) { - gnutls_cipher_algorithm_t cipher; - sasl_ssf_t ssf; - - cipher = gnutls_cipher_get(vs->tls.session); - if (!(ssf = (sasl_ssf_t)gnutls_cipher_get_key_size(cipher))) { - VNC_DEBUG("%s", "cannot TLS get cipher size\n"); - sasl_dispose(&vs->sasl.conn); - vs->sasl.conn = NULL; - goto authabort; - } - ssf *= 8; /* tls key size is bytes, sasl wants bits */ - - err = sasl_setprop(vs->sasl.conn, SASL_SSF_EXTERNAL, &ssf); - if (err != SASL_OK) { - VNC_DEBUG("cannot set SASL external SSF %d (%s)\n", - err, sasl_errstring(err, NULL, NULL)); - sasl_dispose(&vs->sasl.conn); - vs->sasl.conn = NULL; - goto authabort; - } - } else -#endif /* CONFIG_VNC_TLS */ - vs->sasl.wantSSF = 1; - - memset (&secprops, 0, sizeof secprops); - /* Inform SASL that we've got an external SSF layer from TLS */ - if (strncmp(vs->vd->display, "unix:", 5) == 0 -#ifdef CONFIG_VNC_TLS - /* Disable SSF, if using TLS+x509+SASL only. TLS without x509 - is not sufficiently strong */ - || (vs->vd->auth == VNC_AUTH_VENCRYPT && - vs->vd->subauth == VNC_AUTH_VENCRYPT_X509SASL) -#endif /* CONFIG_VNC_TLS */ - ) { - /* If we've got TLS or UNIX domain sock, we don't care about SSF */ - secprops.min_ssf = 0; - secprops.max_ssf = 0; - secprops.maxbufsize = 8192; - secprops.security_flags = 0; - } else { - /* Plain TCP, better get an SSF layer */ - secprops.min_ssf = 56; /* Good enough to require kerberos */ - secprops.max_ssf = 100000; /* Arbitrary big number */ - secprops.maxbufsize = 8192; - /* Forbid any anonymous or trivially crackable auth */ - secprops.security_flags = - SASL_SEC_NOANONYMOUS | SASL_SEC_NOPLAINTEXT; - } - - err = sasl_setprop(vs->sasl.conn, SASL_SEC_PROPS, &secprops); - if (err != SASL_OK) { - VNC_DEBUG("cannot set SASL security props %d (%s)\n", - err, sasl_errstring(err, NULL, NULL)); - sasl_dispose(&vs->sasl.conn); - vs->sasl.conn = NULL; - goto authabort; - } - - err = sasl_listmech(vs->sasl.conn, - NULL, /* Don't need to set user */ - "", /* Prefix */ - ",", /* Separator */ - "", /* Suffix */ - &mechlist, - NULL, - NULL); - if (err != SASL_OK) { - VNC_DEBUG("cannot list SASL mechanisms %d (%s)\n", - err, sasl_errdetail(vs->sasl.conn)); - sasl_dispose(&vs->sasl.conn); - vs->sasl.conn = NULL; - goto authabort; - } - VNC_DEBUG("Available mechanisms for client: '%s'\n", mechlist); - - if (!(vs->sasl.mechlist = strdup(mechlist))) { - VNC_DEBUG("Out of memory"); - sasl_dispose(&vs->sasl.conn); - vs->sasl.conn = NULL; - goto authabort; - } - mechlistlen = strlen(mechlist); - vnc_write_u32(vs, mechlistlen); - vnc_write(vs, mechlist, mechlistlen); - vnc_flush(vs); - - VNC_DEBUG("Wait for client mechname length\n"); - vnc_read_when(vs, protocol_client_auth_sasl_mechname_len, 4); - - return; - - authabort: - vnc_client_error(vs); - return; -} - - diff -Nru qemu-kvm-0.12.5+noroms/vnc-auth-sasl.h qemu-kvm-0.14.1/vnc-auth-sasl.h --- qemu-kvm-0.12.5+noroms/vnc-auth-sasl.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/vnc-auth-sasl.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,74 +0,0 @@ -/* - * QEMU VNC display driver: SASL auth protocol - * - * Copyright (C) 2009 Red Hat, Inc - * - * 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. - */ - - -#ifndef __QEMU_VNC_AUTH_SASL_H__ -#define __QEMU_VNC_AUTH_SASL_H__ - - -#include - -typedef struct VncStateSASL VncStateSASL; -typedef struct VncDisplaySASL VncDisplaySASL; - -#include "acl.h" - -struct VncStateSASL { - sasl_conn_t *conn; - /* If we want to negotiate an SSF layer with client */ - int wantSSF :1; - /* If we are now running the SSF layer */ - int runSSF :1; - /* - * If this is non-zero, then wait for that many bytes - * to be written plain, before switching to SSF encoding - * This allows the VNC auth result to finish being - * written in plain. - */ - unsigned int waitWriteSSF; - - /* - * Buffering encoded data to allow more clear data - * to be stuffed onto the output buffer - */ - const uint8_t *encoded; - unsigned int encodedLength; - unsigned int encodedOffset; - char *username; - char *mechlist; -}; - -struct VncDisplaySASL { - qemu_acl *acl; -}; - -void vnc_sasl_client_cleanup(VncState *vs); - -long vnc_client_read_sasl(VncState *vs); -long vnc_client_write_sasl(VncState *vs); - -void start_auth_sasl(VncState *vs); - -#endif /* __QEMU_VNC_AUTH_SASL_H__ */ - diff -Nru qemu-kvm-0.12.5+noroms/vnc-auth-vencrypt.c qemu-kvm-0.14.1/vnc-auth-vencrypt.c --- qemu-kvm-0.12.5+noroms/vnc-auth-vencrypt.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/vnc-auth-vencrypt.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,175 +0,0 @@ -/* - * QEMU VNC display driver: VeNCrypt authentication setup - * - * Copyright (C) 2006 Anthony Liguori - * Copyright (C) 2006 Fabrice Bellard - * Copyright (C) 2009 Red Hat, Inc - * - * 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 "vnc.h" - - -static void start_auth_vencrypt_subauth(VncState *vs) -{ - switch (vs->vd->subauth) { - case VNC_AUTH_VENCRYPT_TLSNONE: - case VNC_AUTH_VENCRYPT_X509NONE: - VNC_DEBUG("Accept TLS auth none\n"); - vnc_write_u32(vs, 0); /* Accept auth completion */ - start_client_init(vs); - break; - - case VNC_AUTH_VENCRYPT_TLSVNC: - case VNC_AUTH_VENCRYPT_X509VNC: - VNC_DEBUG("Start TLS auth VNC\n"); - start_auth_vnc(vs); - break; - -#ifdef CONFIG_VNC_SASL - case VNC_AUTH_VENCRYPT_TLSSASL: - case VNC_AUTH_VENCRYPT_X509SASL: - VNC_DEBUG("Start TLS auth SASL\n"); - return start_auth_sasl(vs); -#endif /* CONFIG_VNC_SASL */ - - default: /* Should not be possible, but just in case */ - VNC_DEBUG("Reject subauth %d server bug\n", vs->vd->auth); - vnc_write_u8(vs, 1); - if (vs->minor >= 8) { - static const char err[] = "Unsupported authentication type"; - vnc_write_u32(vs, sizeof(err)); - vnc_write(vs, err, sizeof(err)); - } - vnc_client_error(vs); - } -} - -static void vnc_tls_handshake_io(void *opaque); - -static int vnc_start_vencrypt_handshake(struct VncState *vs) { - int ret; - - if ((ret = gnutls_handshake(vs->tls.session)) < 0) { - if (!gnutls_error_is_fatal(ret)) { - VNC_DEBUG("Handshake interrupted (blocking)\n"); - if (!gnutls_record_get_direction(vs->tls.session)) - qemu_set_fd_handler(vs->csock, vnc_tls_handshake_io, NULL, vs); - else - qemu_set_fd_handler(vs->csock, NULL, vnc_tls_handshake_io, vs); - return 0; - } - VNC_DEBUG("Handshake failed %s\n", gnutls_strerror(ret)); - vnc_client_error(vs); - return -1; - } - - if (vs->vd->tls.x509verify) { - if (vnc_tls_validate_certificate(vs) < 0) { - VNC_DEBUG("Client verification failed\n"); - vnc_client_error(vs); - return -1; - } else { - VNC_DEBUG("Client verification passed\n"); - } - } - - VNC_DEBUG("Handshake done, switching to TLS data mode\n"); - vs->tls.wiremode = VNC_WIREMODE_TLS; - qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, vnc_client_write, vs); - - start_auth_vencrypt_subauth(vs); - - return 0; -} - -static void vnc_tls_handshake_io(void *opaque) { - struct VncState *vs = (struct VncState *)opaque; - - VNC_DEBUG("Handshake IO continue\n"); - vnc_start_vencrypt_handshake(vs); -} - - - -#define NEED_X509_AUTH(vs) \ - ((vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509NONE || \ - (vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509VNC || \ - (vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509PLAIN || \ - (vs)->vd->subauth == VNC_AUTH_VENCRYPT_X509SASL) - - -static int protocol_client_vencrypt_auth(VncState *vs, uint8_t *data, size_t len) -{ - int auth = read_u32(data, 0); - - if (auth != vs->vd->subauth) { - VNC_DEBUG("Rejecting auth %d\n", auth); - vnc_write_u8(vs, 0); /* Reject auth */ - vnc_flush(vs); - vnc_client_error(vs); - } else { - VNC_DEBUG("Accepting auth %d, setting up TLS for handshake\n", auth); - vnc_write_u8(vs, 1); /* Accept auth */ - vnc_flush(vs); - - if (vnc_tls_client_setup(vs, NEED_X509_AUTH(vs)) < 0) { - VNC_DEBUG("Failed to setup TLS\n"); - return 0; - } - - VNC_DEBUG("Start TLS VeNCrypt handshake process\n"); - if (vnc_start_vencrypt_handshake(vs) < 0) { - VNC_DEBUG("Failed to start TLS handshake\n"); - return 0; - } - } - return 0; -} - -static int protocol_client_vencrypt_init(VncState *vs, uint8_t *data, size_t len) -{ - if (data[0] != 0 || - data[1] != 2) { - VNC_DEBUG("Unsupported VeNCrypt protocol %d.%d\n", (int)data[0], (int)data[1]); - vnc_write_u8(vs, 1); /* Reject version */ - vnc_flush(vs); - vnc_client_error(vs); - } else { - VNC_DEBUG("Sending allowed auth %d\n", vs->vd->subauth); - vnc_write_u8(vs, 0); /* Accept version */ - vnc_write_u8(vs, 1); /* Number of sub-auths */ - vnc_write_u32(vs, vs->vd->subauth); /* The supported auth */ - vnc_flush(vs); - vnc_read_when(vs, protocol_client_vencrypt_auth, 4); - } - return 0; -} - - -void start_auth_vencrypt(VncState *vs) -{ - /* Send VeNCrypt version 0.2 */ - vnc_write_u8(vs, 0); - vnc_write_u8(vs, 2); - - vnc_read_when(vs, protocol_client_vencrypt_init, 2); -} - diff -Nru qemu-kvm-0.12.5+noroms/vnc-auth-vencrypt.h qemu-kvm-0.14.1/vnc-auth-vencrypt.h --- qemu-kvm-0.12.5+noroms/vnc-auth-vencrypt.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/vnc-auth-vencrypt.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,33 +0,0 @@ -/* - * QEMU VNC display driver - * - * Copyright (C) 2006 Anthony Liguori - * Copyright (C) 2006 Fabrice Bellard - * Copyright (C) 2009 Red Hat, Inc - * - * 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. - */ - - -#ifndef __QEMU_VNC_AUTH_VENCRYPT_H__ -#define __QEMU_VNC_AUTH_VENCRYPT_H__ - -void start_auth_vencrypt(VncState *vs); - -#endif /* __QEMU_VNC_AUTH_VENCRYPT_H__ */ diff -Nru qemu-kvm-0.12.5+noroms/vnc.c qemu-kvm-0.14.1/vnc.c --- qemu-kvm-0.12.5+noroms/vnc.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/vnc.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,2697 +0,0 @@ -/* - * QEMU VNC display driver - * - * Copyright (C) 2006 Anthony Liguori - * Copyright (C) 2006 Fabrice Bellard - * Copyright (C) 2009 Red Hat, Inc - * - * 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 "vnc.h" -#include "sysemu.h" -#include "qemu_socket.h" -#include "qemu-timer.h" -#include "acl.h" -#include "qemu-objects.h" - -#define VNC_REFRESH_INTERVAL_BASE 30 -#define VNC_REFRESH_INTERVAL_INC 50 -#define VNC_REFRESH_INTERVAL_MAX 2000 - -#include "vnc_keysym.h" -#include "d3des.h" - -#define count_bits(c, v) { \ - for (c = 0; v; v >>= 1) \ - { \ - c += v & 1; \ - } \ -} - - -static VncDisplay *vnc_display; /* needed for info vnc */ -static DisplayChangeListener *dcl; - -static char *addr_to_string(const char *format, - struct sockaddr_storage *sa, - socklen_t salen) { - char *addr; - char host[NI_MAXHOST]; - char serv[NI_MAXSERV]; - int err; - size_t addrlen; - - if ((err = getnameinfo((struct sockaddr *)sa, salen, - host, sizeof(host), - serv, sizeof(serv), - NI_NUMERICHOST | NI_NUMERICSERV)) != 0) { - VNC_DEBUG("Cannot resolve address %d: %s\n", - err, gai_strerror(err)); - return NULL; - } - - /* Enough for the existing format + the 2 vars we're - * substituting in. */ - addrlen = strlen(format) + strlen(host) + strlen(serv); - addr = qemu_malloc(addrlen + 1); - snprintf(addr, addrlen, format, host, serv); - addr[addrlen] = '\0'; - - return addr; -} - - -char *vnc_socket_local_addr(const char *format, int fd) { - struct sockaddr_storage sa; - socklen_t salen; - - salen = sizeof(sa); - if (getsockname(fd, (struct sockaddr*)&sa, &salen) < 0) - return NULL; - - return addr_to_string(format, &sa, salen); -} - -char *vnc_socket_remote_addr(const char *format, int fd) { - struct sockaddr_storage sa; - socklen_t salen; - - salen = sizeof(sa); - if (getpeername(fd, (struct sockaddr*)&sa, &salen) < 0) - return NULL; - - return addr_to_string(format, &sa, salen); -} - -static int put_addr_qdict(QDict *qdict, struct sockaddr_storage *sa, - socklen_t salen) -{ - char host[NI_MAXHOST]; - char serv[NI_MAXSERV]; - int err; - - if ((err = getnameinfo((struct sockaddr *)sa, salen, - host, sizeof(host), - serv, sizeof(serv), - NI_NUMERICHOST | NI_NUMERICSERV)) != 0) { - VNC_DEBUG("Cannot resolve address %d: %s\n", - err, gai_strerror(err)); - return -1; - } - - qdict_put(qdict, "host", qstring_from_str(host)); - qdict_put(qdict, "service", qstring_from_str(serv)); - - return 0; -} - -static int vnc_qdict_local_addr(QDict *qdict, int fd) -{ - struct sockaddr_storage sa; - socklen_t salen; - - salen = sizeof(sa); - if (getsockname(fd, (struct sockaddr*)&sa, &salen) < 0) { - return -1; - } - - return put_addr_qdict(qdict, &sa, salen); -} - -static int vnc_qdict_remote_addr(QDict *qdict, int fd) -{ - struct sockaddr_storage sa; - socklen_t salen; - - salen = sizeof(sa); - if (getpeername(fd, (struct sockaddr*)&sa, &salen) < 0) { - return -1; - } - - return put_addr_qdict(qdict, &sa, salen); -} - -static const char *vnc_auth_name(VncDisplay *vd) { - switch (vd->auth) { - case VNC_AUTH_INVALID: - return "invalid"; - case VNC_AUTH_NONE: - return "none"; - case VNC_AUTH_VNC: - return "vnc"; - case VNC_AUTH_RA2: - return "ra2"; - case VNC_AUTH_RA2NE: - return "ra2ne"; - case VNC_AUTH_TIGHT: - return "tight"; - case VNC_AUTH_ULTRA: - return "ultra"; - case VNC_AUTH_TLS: - return "tls"; - case VNC_AUTH_VENCRYPT: -#ifdef CONFIG_VNC_TLS - switch (vd->subauth) { - case VNC_AUTH_VENCRYPT_PLAIN: - return "vencrypt+plain"; - case VNC_AUTH_VENCRYPT_TLSNONE: - return "vencrypt+tls+none"; - case VNC_AUTH_VENCRYPT_TLSVNC: - return "vencrypt+tls+vnc"; - case VNC_AUTH_VENCRYPT_TLSPLAIN: - return "vencrypt+tls+plain"; - case VNC_AUTH_VENCRYPT_X509NONE: - return "vencrypt+x509+none"; - case VNC_AUTH_VENCRYPT_X509VNC: - return "vencrypt+x509+vnc"; - case VNC_AUTH_VENCRYPT_X509PLAIN: - return "vencrypt+x509+plain"; - case VNC_AUTH_VENCRYPT_TLSSASL: - return "vencrypt+tls+sasl"; - case VNC_AUTH_VENCRYPT_X509SASL: - return "vencrypt+x509+sasl"; - default: - return "vencrypt"; - } -#else - return "vencrypt"; -#endif - case VNC_AUTH_SASL: - return "sasl"; - } - return "unknown"; -} - -static QDict *do_info_vnc_client(Monitor *mon, VncState *client) -{ - QDict *qdict; - - qdict = qdict_new(); - if (vnc_qdict_remote_addr(qdict, client->csock) < 0) { - QDECREF(qdict); - return NULL; - } - -#ifdef CONFIG_VNC_TLS - if (client->tls.session && - client->tls.dname) { - qdict_put(qdict, "x509_dname", qstring_from_str(client->tls.dname)); - } -#endif -#ifdef CONFIG_VNC_SASL - if (client->sasl.conn && - client->sasl.username) { - qdict_put(qdict, "username", qstring_from_str(client->sasl.username)); - } -#endif - - return qdict; -} - -static void info_vnc_iter(QObject *obj, void *opaque) -{ - QDict *client; - Monitor *mon = opaque; - - client = qobject_to_qdict(obj); - monitor_printf(mon, "Client:\n"); - monitor_printf(mon, " address: %s:%s\n", - qdict_get_str(client, "host"), - qdict_get_str(client, "service")); - -#ifdef CONFIG_VNC_TLS - monitor_printf(mon, " x509_dname: %s\n", - qdict_haskey(client, "x509_dname") ? - qdict_get_str(client, "x509_dname") : "none"); -#endif -#ifdef CONFIG_VNC_SASL - monitor_printf(mon, " username: %s\n", - qdict_haskey(client, "username") ? - qdict_get_str(client, "username") : "none"); -#endif -} - -void do_info_vnc_print(Monitor *mon, const QObject *data) -{ - QDict *server; - QList *clients; - - server = qobject_to_qdict(data); - if (strcmp(qdict_get_str(server, "status"), "disabled") == 0) { - monitor_printf(mon, "Server: disabled\n"); - return; - } - - monitor_printf(mon, "Server:\n"); - monitor_printf(mon, " address: %s:%s\n", - qdict_get_str(server, "host"), - qdict_get_str(server, "service")); - monitor_printf(mon, " auth: %s\n", - qdict_haskey(server, "auth") ? qdict_get_str(server, "auth") : "none"); - - clients = qdict_get_qlist(server, "clients"); - if (qlist_empty(clients)) { - monitor_printf(mon, "Client: none\n"); - } else { - qlist_iter(clients, info_vnc_iter, mon); - } -} - -/** - * do_info_vnc(): Show VNC server information - * - * Return a QDict with server information. Connected clients are returned - * as a QList of QDicts. - * - * The main QDict contains the following: - * - * - "status": "disabled" or "enabled" - * - "host": server's IP address - * - "service": server's port number - * - "auth": authentication method (optional) - * - "clients": a QList of all connected clients - * - * Clients are described by a QDict, with the following information: - * - * - "host": client's IP address - * - "service": client's port number - * - "x509_dname": TLS dname (optional) - * - "username": SASL username (optional) - * - * Example: - * - * { "status": "enabled", "host": "0.0.0.0", "service": "50402", "auth": "vnc", - * "clients": [ { "host": "127.0.0.1", "service": "50401" } ] } - */ -void do_info_vnc(Monitor *mon, QObject **ret_data) -{ - if (vnc_display == NULL || vnc_display->display == NULL) { - *ret_data = qobject_from_jsonf("{ 'status': 'disabled' }"); - } else { - QDict *qdict; - QList *clist; - - clist = qlist_new(); - if (vnc_display->clients) { - VncState *client = vnc_display->clients; - while (client) { - qdict = do_info_vnc_client(mon, client); - if (qdict) - qlist_append(clist, qdict); - client = client->next; - } - } - - *ret_data = qobject_from_jsonf("{ 'status': 'enabled', 'clients': %p }", - QOBJECT(clist)); - assert(*ret_data != NULL); - - qdict = qobject_to_qdict(*ret_data); - - if (vnc_display->auth != VNC_AUTH_NONE) { - qdict_put(qdict, "auth", - qstring_from_str(vnc_auth_name(vnc_display))); - } - - if (vnc_qdict_local_addr(qdict, vnc_display->lsock) < 0) { - qobject_decref(*ret_data); - *ret_data = NULL; - } - } -} - -static inline uint32_t vnc_has_feature(VncState *vs, int feature) { - return (vs->features & (1 << feature)); -} - -/* TODO - 1) Get the queue working for IO. - 2) there is some weirdness when using the -S option (the screen is grey - and not totally invalidated - 3) resolutions > 1024 -*/ - -static int vnc_update_client(VncState *vs, int has_dirty); -static void vnc_disconnect_start(VncState *vs); -static void vnc_disconnect_finish(VncState *vs); -static void vnc_init_timer(VncDisplay *vd); -static void vnc_remove_timer(VncDisplay *vd); - -static void vnc_colordepth(VncState *vs); -static void framebuffer_update_request(VncState *vs, int incremental, - int x_position, int y_position, - int w, int h); -static void vnc_refresh(void *opaque); -static int vnc_refresh_server_surface(VncDisplay *vd); - -static inline void vnc_set_bit(uint32_t *d, int k) -{ - d[k >> 5] |= 1 << (k & 0x1f); -} - -static inline void vnc_clear_bit(uint32_t *d, int k) -{ - d[k >> 5] &= ~(1 << (k & 0x1f)); -} - -static inline void vnc_set_bits(uint32_t *d, int n, int nb_words) -{ - int j; - - j = 0; - while (n >= 32) { - d[j++] = -1; - n -= 32; - } - if (n > 0) - d[j++] = (1 << n) - 1; - while (j < nb_words) - d[j++] = 0; -} - -static inline int vnc_get_bit(const uint32_t *d, int k) -{ - return (d[k >> 5] >> (k & 0x1f)) & 1; -} - -static inline int vnc_and_bits(const uint32_t *d1, const uint32_t *d2, - int nb_words) -{ - int i; - for(i = 0; i < nb_words; i++) { - if ((d1[i] & d2[i]) != 0) - return 1; - } - return 0; -} - -static void vnc_dpy_update(DisplayState *ds, int x, int y, int w, int h) -{ - int i; - VncDisplay *vd = ds->opaque; - struct VncSurface *s = &vd->guest; - - h += y; - - /* round x down to ensure the loop only spans one 16-pixel block per, - iteration. otherwise, if (x % 16) != 0, the last iteration may span - two 16-pixel blocks but we only mark the first as dirty - */ - w += (x % 16); - x -= (x % 16); - - x = MIN(x, s->ds->width); - y = MIN(y, s->ds->height); - w = MIN(x + w, s->ds->width) - x; - h = MIN(h, s->ds->height); - - for (; y < h; y++) - for (i = 0; i < w; i += 16) - vnc_set_bit(s->dirty[y], (x + i) / 16); -} - -static void vnc_framebuffer_update(VncState *vs, int x, int y, int w, int h, - int32_t encoding) -{ - vnc_write_u16(vs, x); - vnc_write_u16(vs, y); - vnc_write_u16(vs, w); - vnc_write_u16(vs, h); - - vnc_write_s32(vs, encoding); -} - -void buffer_reserve(Buffer *buffer, size_t len) -{ - if ((buffer->capacity - buffer->offset) < len) { - buffer->capacity += (len + 1024); - buffer->buffer = qemu_realloc(buffer->buffer, buffer->capacity); - if (buffer->buffer == NULL) { - fprintf(stderr, "vnc: out of memory\n"); - exit(1); - } - } -} - -int buffer_empty(Buffer *buffer) -{ - return buffer->offset == 0; -} - -uint8_t *buffer_end(Buffer *buffer) -{ - return buffer->buffer + buffer->offset; -} - -void buffer_reset(Buffer *buffer) -{ - buffer->offset = 0; -} - -void buffer_append(Buffer *buffer, const void *data, size_t len) -{ - memcpy(buffer->buffer + buffer->offset, data, len); - buffer->offset += len; -} - -static void vnc_dpy_resize(DisplayState *ds) -{ - int size_changed; - VncDisplay *vd = ds->opaque; - VncState *vs = vd->clients; - - /* server surface */ - if (!vd->server) - vd->server = qemu_mallocz(sizeof(*vd->server)); - if (vd->server->data) - qemu_free(vd->server->data); - *(vd->server) = *(ds->surface); - vd->server->data = qemu_mallocz(vd->server->linesize * - vd->server->height); - - /* guest surface */ - if (!vd->guest.ds) - vd->guest.ds = qemu_mallocz(sizeof(*vd->guest.ds)); - if (ds_get_bytes_per_pixel(ds) != vd->guest.ds->pf.bytes_per_pixel) - console_color_init(ds); - size_changed = ds_get_width(ds) != vd->guest.ds->width || - ds_get_height(ds) != vd->guest.ds->height; - *(vd->guest.ds) = *(ds->surface); - memset(vd->guest.dirty, 0xFF, sizeof(vd->guest.dirty)); - - while (vs != NULL) { - vnc_colordepth(vs); - if (size_changed) { - if (vs->csock != -1 && vnc_has_feature(vs, VNC_FEATURE_RESIZE)) { - vnc_write_u8(vs, 0); /* msg id */ - vnc_write_u8(vs, 0); - vnc_write_u16(vs, 1); /* number of rects */ - vnc_framebuffer_update(vs, 0, 0, ds_get_width(ds), ds_get_height(ds), - VNC_ENCODING_DESKTOPRESIZE); - vnc_flush(vs); - } - } - memset(vs->dirty, 0xFF, sizeof(vs->dirty)); - vs = vs->next; - } -} - -/* fastest code */ -static void vnc_write_pixels_copy(VncState *vs, void *pixels, int size) -{ - vnc_write(vs, pixels, size); -} - -/* slowest but generic code. */ -static void vnc_convert_pixel(VncState *vs, uint8_t *buf, uint32_t v) -{ - uint8_t r, g, b; - VncDisplay *vd = vs->vd; - - r = ((((v & vd->server->pf.rmask) >> vd->server->pf.rshift) << vs->clientds.pf.rbits) >> - vd->server->pf.rbits); - g = ((((v & vd->server->pf.gmask) >> vd->server->pf.gshift) << vs->clientds.pf.gbits) >> - vd->server->pf.gbits); - b = ((((v & vd->server->pf.bmask) >> vd->server->pf.bshift) << vs->clientds.pf.bbits) >> - vd->server->pf.bbits); - v = (r << vs->clientds.pf.rshift) | - (g << vs->clientds.pf.gshift) | - (b << vs->clientds.pf.bshift); - switch(vs->clientds.pf.bytes_per_pixel) { - case 1: - buf[0] = v; - break; - case 2: - if (vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) { - buf[0] = v >> 8; - buf[1] = v; - } else { - buf[1] = v >> 8; - buf[0] = v; - } - break; - default: - case 4: - if (vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) { - buf[0] = v >> 24; - buf[1] = v >> 16; - buf[2] = v >> 8; - buf[3] = v; - } else { - buf[3] = v >> 24; - buf[2] = v >> 16; - buf[1] = v >> 8; - buf[0] = v; - } - break; - } -} - -static void vnc_write_pixels_generic(VncState *vs, void *pixels1, int size) -{ - uint8_t buf[4]; - VncDisplay *vd = vs->vd; - - if (vd->server->pf.bytes_per_pixel == 4) { - uint32_t *pixels = pixels1; - int n, i; - n = size >> 2; - for(i = 0; i < n; i++) { - vnc_convert_pixel(vs, buf, pixels[i]); - vnc_write(vs, buf, vs->clientds.pf.bytes_per_pixel); - } - } else if (vd->server->pf.bytes_per_pixel == 2) { - uint16_t *pixels = pixels1; - int n, i; - n = size >> 1; - for(i = 0; i < n; i++) { - vnc_convert_pixel(vs, buf, pixels[i]); - vnc_write(vs, buf, vs->clientds.pf.bytes_per_pixel); - } - } else if (vd->server->pf.bytes_per_pixel == 1) { - uint8_t *pixels = pixels1; - int n, i; - n = size; - for(i = 0; i < n; i++) { - vnc_convert_pixel(vs, buf, pixels[i]); - vnc_write(vs, buf, vs->clientds.pf.bytes_per_pixel); - } - } else { - fprintf(stderr, "vnc_write_pixels_generic: VncState color depth not supported\n"); - } -} - -static void send_framebuffer_update_raw(VncState *vs, int x, int y, int w, int h) -{ - int i; - uint8_t *row; - VncDisplay *vd = vs->vd; - - row = vd->server->data + y * ds_get_linesize(vs->ds) + x * ds_get_bytes_per_pixel(vs->ds); - for (i = 0; i < h; i++) { - vs->write_pixels(vs, row, w * ds_get_bytes_per_pixel(vs->ds)); - row += ds_get_linesize(vs->ds); - } -} - -static void hextile_enc_cord(uint8_t *ptr, int x, int y, int w, int h) -{ - ptr[0] = ((x & 0x0F) << 4) | (y & 0x0F); - ptr[1] = (((w - 1) & 0x0F) << 4) | ((h - 1) & 0x0F); -} - -#define BPP 8 -#include "vnchextile.h" -#undef BPP - -#define BPP 16 -#include "vnchextile.h" -#undef BPP - -#define BPP 32 -#include "vnchextile.h" -#undef BPP - -#define GENERIC -#define BPP 8 -#include "vnchextile.h" -#undef BPP -#undef GENERIC - -#define GENERIC -#define BPP 16 -#include "vnchextile.h" -#undef BPP -#undef GENERIC - -#define GENERIC -#define BPP 32 -#include "vnchextile.h" -#undef BPP -#undef GENERIC - -static void send_framebuffer_update_hextile(VncState *vs, int x, int y, int w, int h) -{ - int i, j; - int has_fg, has_bg; - uint8_t *last_fg, *last_bg; - VncDisplay *vd = vs->vd; - - last_fg = (uint8_t *) qemu_malloc(vd->server->pf.bytes_per_pixel); - last_bg = (uint8_t *) qemu_malloc(vd->server->pf.bytes_per_pixel); - has_fg = has_bg = 0; - for (j = y; j < (y + h); j += 16) { - for (i = x; i < (x + w); i += 16) { - vs->send_hextile_tile(vs, i, j, - MIN(16, x + w - i), MIN(16, y + h - j), - last_bg, last_fg, &has_bg, &has_fg); - } - } - free(last_fg); - free(last_bg); - -} - -#define ZALLOC_ALIGNMENT 16 - -static void *zalloc(void *x, unsigned items, unsigned size) -{ - void *p; - - size *= items; - size = (size + ZALLOC_ALIGNMENT - 1) & ~(ZALLOC_ALIGNMENT - 1); - - p = qemu_mallocz(size); - - return (p); -} - -static void zfree(void *x, void *addr) -{ - qemu_free(addr); -} - -static void vnc_zlib_init(VncState *vs) -{ - int i; - for (i=0; i<(sizeof(vs->zlib_stream) / sizeof(z_stream)); i++) - vs->zlib_stream[i].opaque = NULL; -} - -static void vnc_zlib_start(VncState *vs) -{ - buffer_reset(&vs->zlib); - - // make the output buffer be the zlib buffer, so we can compress it later - vs->zlib_tmp = vs->output; - vs->output = vs->zlib; -} - -static int vnc_zlib_stop(VncState *vs, int stream_id) -{ - z_streamp zstream = &vs->zlib_stream[stream_id]; - int previous_out; - - // switch back to normal output/zlib buffers - vs->zlib = vs->output; - vs->output = vs->zlib_tmp; - - // compress the zlib buffer - - // initialize the stream - // XXX need one stream per session - if (zstream->opaque != vs) { - int err; - - VNC_DEBUG("VNC: initializing zlib stream %d\n", stream_id); - VNC_DEBUG("VNC: opaque = %p | vs = %p\n", zstream->opaque, vs); - zstream->zalloc = zalloc; - zstream->zfree = zfree; - - err = deflateInit2(zstream, vs->tight_compression, Z_DEFLATED, MAX_WBITS, - MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY); - - if (err != Z_OK) { - fprintf(stderr, "VNC: error initializing zlib\n"); - return -1; - } - - zstream->opaque = vs; - } - - // XXX what to do if tight_compression changed in between? - - // reserve memory in output buffer - buffer_reserve(&vs->output, vs->zlib.offset + 64); - - // set pointers - zstream->next_in = vs->zlib.buffer; - zstream->avail_in = vs->zlib.offset; - zstream->next_out = vs->output.buffer + vs->output.offset; - zstream->avail_out = vs->output.capacity - vs->output.offset; - zstream->data_type = Z_BINARY; - previous_out = zstream->total_out; - - // start encoding - if (deflate(zstream, Z_SYNC_FLUSH) != Z_OK) { - fprintf(stderr, "VNC: error during zlib compression\n"); - return -1; - } - - vs->output.offset = vs->output.capacity - zstream->avail_out; - return zstream->total_out - previous_out; -} - -static void send_framebuffer_update_zlib(VncState *vs, int x, int y, int w, int h) -{ - int old_offset, new_offset, bytes_written; - - vnc_framebuffer_update(vs, x, y, w, h, VNC_ENCODING_ZLIB); - - // remember where we put in the follow-up size - old_offset = vs->output.offset; - vnc_write_s32(vs, 0); - - // compress the stream - vnc_zlib_start(vs); - send_framebuffer_update_raw(vs, x, y, w, h); - bytes_written = vnc_zlib_stop(vs, 0); - - if (bytes_written == -1) - return; - - // hack in the size - new_offset = vs->output.offset; - vs->output.offset = old_offset; - vnc_write_u32(vs, bytes_written); - vs->output.offset = new_offset; -} - -static void send_framebuffer_update(VncState *vs, int x, int y, int w, int h) -{ - switch(vs->vnc_encoding) { - case VNC_ENCODING_ZLIB: - send_framebuffer_update_zlib(vs, x, y, w, h); - break; - case VNC_ENCODING_HEXTILE: - vnc_framebuffer_update(vs, x, y, w, h, VNC_ENCODING_HEXTILE); - send_framebuffer_update_hextile(vs, x, y, w, h); - break; - default: - vnc_framebuffer_update(vs, x, y, w, h, VNC_ENCODING_RAW); - send_framebuffer_update_raw(vs, x, y, w, h); - break; - } -} - -static void vnc_copy(VncState *vs, int src_x, int src_y, int dst_x, int dst_y, int w, int h) -{ - /* send bitblit op to the vnc client */ - vnc_write_u8(vs, 0); /* msg id */ - vnc_write_u8(vs, 0); - vnc_write_u16(vs, 1); /* number of rects */ - vnc_framebuffer_update(vs, dst_x, dst_y, w, h, VNC_ENCODING_COPYRECT); - vnc_write_u16(vs, src_x); - vnc_write_u16(vs, src_y); - vnc_flush(vs); -} - -static void vnc_dpy_copy(DisplayState *ds, int src_x, int src_y, int dst_x, int dst_y, int w, int h) -{ - VncDisplay *vd = ds->opaque; - VncState *vs, *vn; - uint8_t *src_row; - uint8_t *dst_row; - int i,x,y,pitch,depth,inc,w_lim,s; - int cmp_bytes; - - vnc_refresh_server_surface(vd); - for (vs = vd->clients; vs != NULL; vs = vn) { - vn = vs->next; - if (vnc_has_feature(vs, VNC_FEATURE_COPYRECT)) { - vs->force_update = 1; - vnc_update_client(vs, 1); - /* vs might be free()ed here */ - } - } - - /* do bitblit op on the local surface too */ - pitch = ds_get_linesize(vd->ds); - depth = ds_get_bytes_per_pixel(vd->ds); - src_row = vd->server->data + pitch * src_y + depth * src_x; - dst_row = vd->server->data + pitch * dst_y + depth * dst_x; - y = dst_y; - inc = 1; - if (dst_y > src_y) { - /* copy backwards */ - src_row += pitch * (h-1); - dst_row += pitch * (h-1); - pitch = -pitch; - y = dst_y + h - 1; - inc = -1; - } - w_lim = w - (16 - (dst_x % 16)); - if (w_lim < 0) - w_lim = w; - else - w_lim = w - (w_lim % 16); - for (i = 0; i < h; i++) { - for (x = 0; x <= w_lim; - x += s, src_row += cmp_bytes, dst_row += cmp_bytes) { - if (x == w_lim) { - if ((s = w - w_lim) == 0) - break; - } else if (!x) { - s = (16 - (dst_x % 16)); - s = MIN(s, w_lim); - } else { - s = 16; - } - cmp_bytes = s * depth; - if (memcmp(src_row, dst_row, cmp_bytes) == 0) - continue; - memmove(dst_row, src_row, cmp_bytes); - vs = vd->clients; - while (vs != NULL) { - if (!vnc_has_feature(vs, VNC_FEATURE_COPYRECT)) - vnc_set_bit(vs->dirty[y], ((x + dst_x) / 16)); - vs = vs->next; - } - } - src_row += pitch - w * depth; - dst_row += pitch - w * depth; - y += inc; - } - - for (vs = vd->clients; vs != NULL; vs = vs->next) { - if (vnc_has_feature(vs, VNC_FEATURE_COPYRECT)) - vnc_copy(vs, src_x, src_y, dst_x, dst_y, w, h); - } -} - -static int find_and_clear_dirty_height(struct VncState *vs, - int y, int last_x, int x) -{ - int h; - VncDisplay *vd = vs->vd; - - for (h = 1; h < (vd->server->height - y); h++) { - int tmp_x; - if (!vnc_get_bit(vs->dirty[y + h], last_x)) - break; - for (tmp_x = last_x; tmp_x < x; tmp_x++) - vnc_clear_bit(vs->dirty[y + h], tmp_x); - } - - return h; -} - -static int vnc_update_client(VncState *vs, int has_dirty) -{ - if (vs->need_update && vs->csock != -1) { - VncDisplay *vd = vs->vd; - int y; - int n_rectangles; - int saved_offset; - - if (vs->output.offset && !vs->audio_cap && !vs->force_update) - /* kernel send buffers are full -> drop frames to throttle */ - return 0; - - if (!has_dirty && !vs->audio_cap && !vs->force_update) - return 0; - - /* - * Send screen updates to the vnc client using the server - * surface and server dirty map. guest surface updates - * happening in parallel don't disturb us, the next pass will - * send them to the client. - */ - n_rectangles = 0; - vnc_write_u8(vs, 0); /* msg id */ - vnc_write_u8(vs, 0); - saved_offset = vs->output.offset; - vnc_write_u16(vs, 0); - - for (y = 0; y < vd->server->height; y++) { - int x; - int last_x = -1; - for (x = 0; x < vd->server->width / 16; x++) { - if (vnc_get_bit(vs->dirty[y], x)) { - if (last_x == -1) { - last_x = x; - } - vnc_clear_bit(vs->dirty[y], x); - } else { - if (last_x != -1) { - int h = find_and_clear_dirty_height(vs, y, last_x, x); - send_framebuffer_update(vs, last_x * 16, y, (x - last_x) * 16, h); - n_rectangles++; - } - last_x = -1; - } - } - if (last_x != -1) { - int h = find_and_clear_dirty_height(vs, y, last_x, x); - send_framebuffer_update(vs, last_x * 16, y, (x - last_x) * 16, h); - n_rectangles++; - } - } - vs->output.buffer[saved_offset] = (n_rectangles >> 8) & 0xFF; - vs->output.buffer[saved_offset + 1] = n_rectangles & 0xFF; - vnc_flush(vs); - vs->force_update = 0; - return n_rectangles; - } - - if (vs->csock == -1) - vnc_disconnect_finish(vs); - - return 0; -} - -/* audio */ -static void audio_capture_notify(void *opaque, audcnotification_e cmd) -{ - VncState *vs = opaque; - - switch (cmd) { - case AUD_CNOTIFY_DISABLE: - vnc_write_u8(vs, 255); - vnc_write_u8(vs, 1); - vnc_write_u16(vs, 0); - vnc_flush(vs); - break; - - case AUD_CNOTIFY_ENABLE: - vnc_write_u8(vs, 255); - vnc_write_u8(vs, 1); - vnc_write_u16(vs, 1); - vnc_flush(vs); - break; - } -} - -static void audio_capture_destroy(void *opaque) -{ -} - -static void audio_capture(void *opaque, void *buf, int size) -{ - VncState *vs = opaque; - - vnc_write_u8(vs, 255); - vnc_write_u8(vs, 1); - vnc_write_u16(vs, 2); - vnc_write_u32(vs, size); - vnc_write(vs, buf, size); - vnc_flush(vs); -} - -static void audio_add(VncState *vs) -{ - Monitor *mon = cur_mon; - struct audio_capture_ops ops; - - if (vs->audio_cap) { - monitor_printf(mon, "audio already running\n"); - return; - } - - ops.notify = audio_capture_notify; - ops.destroy = audio_capture_destroy; - ops.capture = audio_capture; - - vs->audio_cap = AUD_add_capture(&vs->as, &ops, vs); - if (!vs->audio_cap) { - monitor_printf(mon, "Failed to add audio capture\n"); - } -} - -static void audio_del(VncState *vs) -{ - if (vs->audio_cap) { - AUD_del_capture(vs->audio_cap, vs); - vs->audio_cap = NULL; - } -} - -static void vnc_disconnect_start(VncState *vs) -{ - if (vs->csock == -1) - return; - qemu_set_fd_handler2(vs->csock, NULL, NULL, NULL, NULL); - closesocket(vs->csock); - vs->csock = -1; -} - -static void vnc_disconnect_finish(VncState *vs) -{ - if (vs->input.buffer) { - qemu_free(vs->input.buffer); - vs->input.buffer = NULL; - } - if (vs->output.buffer) { - qemu_free(vs->output.buffer); - vs->output.buffer = NULL; - } -#ifdef CONFIG_VNC_TLS - vnc_tls_client_cleanup(vs); -#endif /* CONFIG_VNC_TLS */ -#ifdef CONFIG_VNC_SASL - vnc_sasl_client_cleanup(vs); -#endif /* CONFIG_VNC_SASL */ - audio_del(vs); - - VncState *p, *parent = NULL; - for (p = vs->vd->clients; p != NULL; p = p->next) { - if (p == vs) { - if (parent) - parent->next = p->next; - else - vs->vd->clients = p->next; - break; - } - parent = p; - } - if (!vs->vd->clients) - dcl->idle = 1; - - vnc_remove_timer(vs->vd); - qemu_free(vs); -} - -int vnc_client_io_error(VncState *vs, int ret, int last_errno) -{ - if (ret == 0 || ret == -1) { - if (ret == -1) { - switch (last_errno) { - case EINTR: - case EAGAIN: -#ifdef _WIN32 - case WSAEWOULDBLOCK: -#endif - return 0; - default: - break; - } - } - - VNC_DEBUG("Closing down client sock: ret %d, errno %d\n", - ret, ret < 0 ? last_errno : 0); - vnc_disconnect_start(vs); - - return 0; - } - return ret; -} - - -void vnc_client_error(VncState *vs) -{ - VNC_DEBUG("Closing down client sock: protocol error\n"); - vnc_disconnect_start(vs); -} - - -/* - * Called to write a chunk of data to the client socket. The data may - * be the raw data, or may have already been encoded by SASL. - * The data will be written either straight onto the socket, or - * written via the GNUTLS wrappers, if TLS/SSL encryption is enabled - * - * NB, it is theoretically possible to have 2 layers of encryption, - * both SASL, and this TLS layer. It is highly unlikely in practice - * though, since SASL encryption will typically be a no-op if TLS - * is active - * - * Returns the number of bytes written, which may be less than - * the requested 'datalen' if the socket would block. Returns - * -1 on error, and disconnects the client socket. - */ -long vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen) -{ - long ret; -#ifdef CONFIG_VNC_TLS - if (vs->tls.session) { - ret = gnutls_write(vs->tls.session, data, datalen); - if (ret < 0) { - if (ret == GNUTLS_E_AGAIN) - errno = EAGAIN; - else - errno = EIO; - ret = -1; - } - } else -#endif /* CONFIG_VNC_TLS */ - ret = send(vs->csock, (const void *)data, datalen, 0); - VNC_DEBUG("Wrote wire %p %zd -> %ld\n", data, datalen, ret); - return vnc_client_io_error(vs, ret, socket_error()); -} - - -/* - * Called to write buffered data to the client socket, when not - * using any SASL SSF encryption layers. Will write as much data - * as possible without blocking. If all buffered data is written, - * will switch the FD poll() handler back to read monitoring. - * - * Returns the number of bytes written, which may be less than - * the buffered output data if the socket would block. Returns - * -1 on error, and disconnects the client socket. - */ -static long vnc_client_write_plain(VncState *vs) -{ - long ret; - -#ifdef CONFIG_VNC_SASL - VNC_DEBUG("Write Plain: Pending output %p size %zd offset %zd. Wait SSF %d\n", - vs->output.buffer, vs->output.capacity, vs->output.offset, - vs->sasl.waitWriteSSF); - - if (vs->sasl.conn && - vs->sasl.runSSF && - vs->sasl.waitWriteSSF) { - ret = vnc_client_write_buf(vs, vs->output.buffer, vs->sasl.waitWriteSSF); - if (ret) - vs->sasl.waitWriteSSF -= ret; - } else -#endif /* CONFIG_VNC_SASL */ - ret = vnc_client_write_buf(vs, vs->output.buffer, vs->output.offset); - if (!ret) - return 0; - - memmove(vs->output.buffer, vs->output.buffer + ret, (vs->output.offset - ret)); - vs->output.offset -= ret; - - if (vs->output.offset == 0) { - qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs); - } - - return ret; -} - - -/* - * First function called whenever there is data to be written to - * the client socket. Will delegate actual work according to whether - * SASL SSF layers are enabled (thus requiring encryption calls) - */ -void vnc_client_write(void *opaque) -{ - long ret; - VncState *vs = opaque; - -#ifdef CONFIG_VNC_SASL - if (vs->sasl.conn && - vs->sasl.runSSF && - !vs->sasl.waitWriteSSF) - ret = vnc_client_write_sasl(vs); - else -#endif /* CONFIG_VNC_SASL */ - ret = vnc_client_write_plain(vs); -} - -void vnc_read_when(VncState *vs, VncReadEvent *func, size_t expecting) -{ - vs->read_handler = func; - vs->read_handler_expect = expecting; -} - - -/* - * Called to read a chunk of data from the client socket. The data may - * be the raw data, or may need to be further decoded by SASL. - * The data will be read either straight from to the socket, or - * read via the GNUTLS wrappers, if TLS/SSL encryption is enabled - * - * NB, it is theoretically possible to have 2 layers of encryption, - * both SASL, and this TLS layer. It is highly unlikely in practice - * though, since SASL encryption will typically be a no-op if TLS - * is active - * - * Returns the number of bytes read, which may be less than - * the requested 'datalen' if the socket would block. Returns - * -1 on error, and disconnects the client socket. - */ -long vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen) -{ - long ret; -#ifdef CONFIG_VNC_TLS - if (vs->tls.session) { - ret = gnutls_read(vs->tls.session, data, datalen); - if (ret < 0) { - if (ret == GNUTLS_E_AGAIN) - errno = EAGAIN; - else - errno = EIO; - ret = -1; - } - } else -#endif /* CONFIG_VNC_TLS */ - ret = recv(vs->csock, (void *)data, datalen, 0); - VNC_DEBUG("Read wire %p %zd -> %ld\n", data, datalen, ret); - return vnc_client_io_error(vs, ret, socket_error()); -} - - -/* - * Called to read data from the client socket to the input buffer, - * when not using any SASL SSF encryption layers. Will read as much - * data as possible without blocking. - * - * Returns the number of bytes read. Returns -1 on error, and - * disconnects the client socket. - */ -static long vnc_client_read_plain(VncState *vs) -{ - int ret; - VNC_DEBUG("Read plain %p size %zd offset %zd\n", - vs->input.buffer, vs->input.capacity, vs->input.offset); - buffer_reserve(&vs->input, 4096); - ret = vnc_client_read_buf(vs, buffer_end(&vs->input), 4096); - if (!ret) - return 0; - vs->input.offset += ret; - return ret; -} - - -/* - * First function called whenever there is more data to be read from - * the client socket. Will delegate actual work according to whether - * SASL SSF layers are enabled (thus requiring decryption calls) - */ -void vnc_client_read(void *opaque) -{ - VncState *vs = opaque; - long ret; - -#ifdef CONFIG_VNC_SASL - if (vs->sasl.conn && vs->sasl.runSSF) - ret = vnc_client_read_sasl(vs); - else -#endif /* CONFIG_VNC_SASL */ - ret = vnc_client_read_plain(vs); - if (!ret) { - if (vs->csock == -1) - vnc_disconnect_finish(vs); - return; - } - - while (vs->read_handler && vs->input.offset >= vs->read_handler_expect) { - size_t len = vs->read_handler_expect; - int ret; - - ret = vs->read_handler(vs, vs->input.buffer, len); - if (vs->csock == -1) { - vnc_disconnect_finish(vs); - return; - } - - if (!ret) { - memmove(vs->input.buffer, vs->input.buffer + len, (vs->input.offset - len)); - vs->input.offset -= len; - } else { - vs->read_handler_expect = ret; - } - } -} - -void vnc_write(VncState *vs, const void *data, size_t len) -{ - buffer_reserve(&vs->output, len); - - if (vs->csock != -1 && buffer_empty(&vs->output)) { - qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, vnc_client_write, vs); - } - - buffer_append(&vs->output, data, len); -} - -void vnc_write_s32(VncState *vs, int32_t value) -{ - vnc_write_u32(vs, *(uint32_t *)&value); -} - -void vnc_write_u32(VncState *vs, uint32_t value) -{ - uint8_t buf[4]; - - buf[0] = (value >> 24) & 0xFF; - buf[1] = (value >> 16) & 0xFF; - buf[2] = (value >> 8) & 0xFF; - buf[3] = value & 0xFF; - - vnc_write(vs, buf, 4); -} - -void vnc_write_u16(VncState *vs, uint16_t value) -{ - uint8_t buf[2]; - - buf[0] = (value >> 8) & 0xFF; - buf[1] = value & 0xFF; - - vnc_write(vs, buf, 2); -} - -void vnc_write_u8(VncState *vs, uint8_t value) -{ - vnc_write(vs, (char *)&value, 1); -} - -void vnc_flush(VncState *vs) -{ - if (vs->csock != -1 && vs->output.offset) - vnc_client_write(vs); -} - -uint8_t read_u8(uint8_t *data, size_t offset) -{ - return data[offset]; -} - -uint16_t read_u16(uint8_t *data, size_t offset) -{ - return ((data[offset] & 0xFF) << 8) | (data[offset + 1] & 0xFF); -} - -int32_t read_s32(uint8_t *data, size_t offset) -{ - return (int32_t)((data[offset] << 24) | (data[offset + 1] << 16) | - (data[offset + 2] << 8) | data[offset + 3]); -} - -uint32_t read_u32(uint8_t *data, size_t offset) -{ - return ((data[offset] << 24) | (data[offset + 1] << 16) | - (data[offset + 2] << 8) | data[offset + 3]); -} - -static void client_cut_text(VncState *vs, size_t len, uint8_t *text) -{ -} - -static void check_pointer_type_change(VncState *vs, int absolute) -{ - if (vnc_has_feature(vs, VNC_FEATURE_POINTER_TYPE_CHANGE) && vs->absolute != absolute) { - vnc_write_u8(vs, 0); - vnc_write_u8(vs, 0); - vnc_write_u16(vs, 1); - vnc_framebuffer_update(vs, absolute, 0, - ds_get_width(vs->ds), ds_get_height(vs->ds), - VNC_ENCODING_POINTER_TYPE_CHANGE); - vnc_flush(vs); - } - vs->absolute = absolute; -} - -static void pointer_event(VncState *vs, int button_mask, int x, int y) -{ - int buttons = 0; - int dz = 0; - - if (button_mask & 0x01) - buttons |= MOUSE_EVENT_LBUTTON; - if (button_mask & 0x02) - buttons |= MOUSE_EVENT_MBUTTON; - if (button_mask & 0x04) - buttons |= MOUSE_EVENT_RBUTTON; - if (button_mask & 0x08) - dz = -1; - if (button_mask & 0x10) - dz = 1; - - if (vs->absolute) { - kbd_mouse_event(ds_get_width(vs->ds) > 1 ? - x * 0x7FFF / (ds_get_width(vs->ds) - 1) : 0x4000, - ds_get_height(vs->ds) > 1 ? - y * 0x7FFF / (ds_get_height(vs->ds) - 1) : 0x4000, - dz, buttons); - } else if (vnc_has_feature(vs, VNC_FEATURE_POINTER_TYPE_CHANGE)) { - x -= 0x7FFF; - y -= 0x7FFF; - - kbd_mouse_event(x, y, dz, buttons); - } else { - if (vs->last_x != -1) - kbd_mouse_event(x - vs->last_x, - y - vs->last_y, - dz, buttons); - vs->last_x = x; - vs->last_y = y; - } - - check_pointer_type_change(vs, kbd_mouse_is_absolute()); -} - -static void reset_keys(VncState *vs) -{ - int i; - for(i = 0; i < 256; i++) { - if (vs->modifiers_state[i]) { - if (i & 0x80) - kbd_put_keycode(0xe0); - kbd_put_keycode(i | 0x80); - vs->modifiers_state[i] = 0; - } - } -} - -static void press_key(VncState *vs, int keysym) -{ - kbd_put_keycode(keysym2scancode(vs->vd->kbd_layout, keysym) & 0x7f); - kbd_put_keycode(keysym2scancode(vs->vd->kbd_layout, keysym) | 0x80); -} - -static void do_key_event(VncState *vs, int down, int keycode, int sym) -{ - /* QEMU console switch */ - switch(keycode) { - case 0x2a: /* Left Shift */ - case 0x36: /* Right Shift */ - case 0x1d: /* Left CTRL */ - case 0x9d: /* Right CTRL */ - case 0x38: /* Left ALT */ - case 0xb8: /* Right ALT */ - if (down) - vs->modifiers_state[keycode] = 1; - else - vs->modifiers_state[keycode] = 0; - break; - case 0x02 ... 0x0a: /* '1' to '9' keys */ - if (down && vs->modifiers_state[0x1d] && vs->modifiers_state[0x38]) { - /* Reset the modifiers sent to the current console */ - reset_keys(vs); - console_select(keycode - 0x02); - return; - } - break; - case 0x3a: /* CapsLock */ - case 0x45: /* NumLock */ - if (!down) - vs->modifiers_state[keycode] ^= 1; - break; - } - - if (keycode_is_keypad(vs->vd->kbd_layout, keycode)) { - /* If the numlock state needs to change then simulate an additional - keypress before sending this one. This will happen if the user - toggles numlock away from the VNC window. - */ - if (keysym_is_numlock(vs->vd->kbd_layout, sym & 0xFFFF)) { - if (!vs->modifiers_state[0x45]) { - vs->modifiers_state[0x45] = 1; - press_key(vs, 0xff7f); - } - } else { - if (vs->modifiers_state[0x45]) { - vs->modifiers_state[0x45] = 0; - press_key(vs, 0xff7f); - } - } - } - - if ((sym >= 'A' && sym <= 'Z') || (sym >= 'a' && sym <= 'z')) { - /* If the capslock state needs to change then simulate an additional - keypress before sending this one. This will happen if the user - toggles capslock away from the VNC window. - */ - int uppercase = !!(sym >= 'A' && sym <= 'Z'); - int shift = !!(vs->modifiers_state[0x2a] | vs->modifiers_state[0x36]); - int capslock = !!(vs->modifiers_state[0x3a]); - if (capslock) { - if (uppercase == shift) { - vs->modifiers_state[0x3a] = 0; - press_key(vs, 0xffe5); - } - } else { - if (uppercase != shift) { - vs->modifiers_state[0x3a] = 1; - press_key(vs, 0xffe5); - } - } - } - - if (is_graphic_console()) { - if (keycode & 0x80) - kbd_put_keycode(0xe0); - if (down) - kbd_put_keycode(keycode & 0x7f); - else - kbd_put_keycode(keycode | 0x80); - } else { - /* QEMU console emulation */ - if (down) { - int numlock = vs->modifiers_state[0x45]; - switch (keycode) { - case 0x2a: /* Left Shift */ - case 0x36: /* Right Shift */ - case 0x1d: /* Left CTRL */ - case 0x9d: /* Right CTRL */ - case 0x38: /* Left ALT */ - case 0xb8: /* Right ALT */ - break; - case 0xc8: - kbd_put_keysym(QEMU_KEY_UP); - break; - case 0xd0: - kbd_put_keysym(QEMU_KEY_DOWN); - break; - case 0xcb: - kbd_put_keysym(QEMU_KEY_LEFT); - break; - case 0xcd: - kbd_put_keysym(QEMU_KEY_RIGHT); - break; - case 0xd3: - kbd_put_keysym(QEMU_KEY_DELETE); - break; - case 0xc7: - kbd_put_keysym(QEMU_KEY_HOME); - break; - case 0xcf: - kbd_put_keysym(QEMU_KEY_END); - break; - case 0xc9: - kbd_put_keysym(QEMU_KEY_PAGEUP); - break; - case 0xd1: - kbd_put_keysym(QEMU_KEY_PAGEDOWN); - break; - - case 0x47: - kbd_put_keysym(numlock ? '7' : QEMU_KEY_HOME); - break; - case 0x48: - kbd_put_keysym(numlock ? '8' : QEMU_KEY_UP); - break; - case 0x49: - kbd_put_keysym(numlock ? '9' : QEMU_KEY_PAGEUP); - break; - case 0x4b: - kbd_put_keysym(numlock ? '4' : QEMU_KEY_LEFT); - break; - case 0x4c: - kbd_put_keysym('5'); - break; - case 0x4d: - kbd_put_keysym(numlock ? '6' : QEMU_KEY_RIGHT); - break; - case 0x4f: - kbd_put_keysym(numlock ? '1' : QEMU_KEY_END); - break; - case 0x50: - kbd_put_keysym(numlock ? '2' : QEMU_KEY_DOWN); - break; - case 0x51: - kbd_put_keysym(numlock ? '3' : QEMU_KEY_PAGEDOWN); - break; - case 0x52: - kbd_put_keysym('0'); - break; - case 0x53: - kbd_put_keysym(numlock ? '.' : QEMU_KEY_DELETE); - break; - - case 0xb5: - kbd_put_keysym('/'); - break; - case 0x37: - kbd_put_keysym('*'); - break; - case 0x4a: - kbd_put_keysym('-'); - break; - case 0x4e: - kbd_put_keysym('+'); - break; - case 0x9c: - kbd_put_keysym('\n'); - break; - - default: - kbd_put_keysym(sym); - break; - } - } - } -} - -static void key_event(VncState *vs, int down, uint32_t sym) -{ - int keycode; - int lsym = sym; - - if (lsym >= 'A' && lsym <= 'Z' && is_graphic_console()) { - lsym = lsym - 'A' + 'a'; - } - - keycode = keysym2scancode(vs->vd->kbd_layout, lsym & 0xFFFF); - do_key_event(vs, down, keycode, sym); -} - -static void ext_key_event(VncState *vs, int down, - uint32_t sym, uint16_t keycode) -{ - /* if the user specifies a keyboard layout, always use it */ - if (keyboard_layout) - key_event(vs, down, sym); - else - do_key_event(vs, down, keycode, sym); -} - -static void framebuffer_update_request(VncState *vs, int incremental, - int x_position, int y_position, - int w, int h) -{ - if (x_position > ds_get_width(vs->ds)) - x_position = ds_get_width(vs->ds); - if (y_position > ds_get_height(vs->ds)) - y_position = ds_get_height(vs->ds); - if (x_position + w >= ds_get_width(vs->ds)) - w = ds_get_width(vs->ds) - x_position; - if (y_position + h >= ds_get_height(vs->ds)) - h = ds_get_height(vs->ds) - y_position; - - int i; - vs->need_update = 1; - if (!incremental) { - vs->force_update = 1; - for (i = 0; i < h; i++) { - vnc_set_bits(vs->dirty[y_position + i], - (ds_get_width(vs->ds) / 16), VNC_DIRTY_WORDS); - } - } -} - -static void send_ext_key_event_ack(VncState *vs) -{ - vnc_write_u8(vs, 0); - vnc_write_u8(vs, 0); - vnc_write_u16(vs, 1); - vnc_framebuffer_update(vs, 0, 0, ds_get_width(vs->ds), ds_get_height(vs->ds), - VNC_ENCODING_EXT_KEY_EVENT); - vnc_flush(vs); -} - -static void send_ext_audio_ack(VncState *vs) -{ - vnc_write_u8(vs, 0); - vnc_write_u8(vs, 0); - vnc_write_u16(vs, 1); - vnc_framebuffer_update(vs, 0, 0, ds_get_width(vs->ds), ds_get_height(vs->ds), - VNC_ENCODING_AUDIO); - vnc_flush(vs); -} - -static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings) -{ - int i; - unsigned int enc = 0; - - vnc_zlib_init(vs); - vs->features = 0; - vs->vnc_encoding = 0; - vs->tight_compression = 9; - vs->tight_quality = 9; - vs->absolute = -1; - - for (i = n_encodings - 1; i >= 0; i--) { - enc = encodings[i]; - switch (enc) { - case VNC_ENCODING_RAW: - vs->vnc_encoding = enc; - break; - case VNC_ENCODING_COPYRECT: - vs->features |= VNC_FEATURE_COPYRECT_MASK; - break; - case VNC_ENCODING_HEXTILE: - vs->features |= VNC_FEATURE_HEXTILE_MASK; - vs->vnc_encoding = enc; - break; - case VNC_ENCODING_ZLIB: - vs->features |= VNC_FEATURE_ZLIB_MASK; - vs->vnc_encoding = enc; - break; - case VNC_ENCODING_DESKTOPRESIZE: - vs->features |= VNC_FEATURE_RESIZE_MASK; - break; - case VNC_ENCODING_POINTER_TYPE_CHANGE: - vs->features |= VNC_FEATURE_POINTER_TYPE_CHANGE_MASK; - break; - case VNC_ENCODING_EXT_KEY_EVENT: - send_ext_key_event_ack(vs); - break; - case VNC_ENCODING_AUDIO: - send_ext_audio_ack(vs); - break; - case VNC_ENCODING_WMVi: - vs->features |= VNC_FEATURE_WMVI_MASK; - break; - case VNC_ENCODING_COMPRESSLEVEL0 ... VNC_ENCODING_COMPRESSLEVEL0 + 9: - vs->tight_compression = (enc & 0x0F); - break; - case VNC_ENCODING_QUALITYLEVEL0 ... VNC_ENCODING_QUALITYLEVEL0 + 9: - vs->tight_quality = (enc & 0x0F); - break; - default: - VNC_DEBUG("Unknown encoding: %d (0x%.8x): %d\n", i, enc, enc); - break; - } - } - - check_pointer_type_change(vs, kbd_mouse_is_absolute()); -} - -static void set_pixel_conversion(VncState *vs) -{ - if ((vs->clientds.flags & QEMU_BIG_ENDIAN_FLAG) == - (vs->ds->surface->flags & QEMU_BIG_ENDIAN_FLAG) && - !memcmp(&(vs->clientds.pf), &(vs->ds->surface->pf), sizeof(PixelFormat))) { - vs->write_pixels = vnc_write_pixels_copy; - switch (vs->ds->surface->pf.bits_per_pixel) { - case 8: - vs->send_hextile_tile = send_hextile_tile_8; - break; - case 16: - vs->send_hextile_tile = send_hextile_tile_16; - break; - case 32: - vs->send_hextile_tile = send_hextile_tile_32; - break; - } - } else { - vs->write_pixels = vnc_write_pixels_generic; - switch (vs->ds->surface->pf.bits_per_pixel) { - case 8: - vs->send_hextile_tile = send_hextile_tile_generic_8; - break; - case 16: - vs->send_hextile_tile = send_hextile_tile_generic_16; - break; - case 32: - vs->send_hextile_tile = send_hextile_tile_generic_32; - break; - } - } -} - -static void set_pixel_format(VncState *vs, - int bits_per_pixel, int depth, - int big_endian_flag, int true_color_flag, - int red_max, int green_max, int blue_max, - int red_shift, int green_shift, int blue_shift) -{ - if (!true_color_flag) { - vnc_client_error(vs); - return; - } - - vs->clientds = *(vs->vd->guest.ds); - vs->clientds.pf.rmax = red_max; - count_bits(vs->clientds.pf.rbits, red_max); - vs->clientds.pf.rshift = red_shift; - vs->clientds.pf.rmask = red_max << red_shift; - vs->clientds.pf.gmax = green_max; - count_bits(vs->clientds.pf.gbits, green_max); - vs->clientds.pf.gshift = green_shift; - vs->clientds.pf.gmask = green_max << green_shift; - vs->clientds.pf.bmax = blue_max; - count_bits(vs->clientds.pf.bbits, blue_max); - vs->clientds.pf.bshift = blue_shift; - vs->clientds.pf.bmask = blue_max << blue_shift; - vs->clientds.pf.bits_per_pixel = bits_per_pixel; - vs->clientds.pf.bytes_per_pixel = bits_per_pixel / 8; - vs->clientds.pf.depth = bits_per_pixel == 32 ? 24 : bits_per_pixel; - vs->clientds.flags = big_endian_flag ? QEMU_BIG_ENDIAN_FLAG : 0x00; - - set_pixel_conversion(vs); - - vga_hw_invalidate(); - vga_hw_update(); -} - -static void pixel_format_message (VncState *vs) { - char pad[3] = { 0, 0, 0 }; - - vnc_write_u8(vs, vs->ds->surface->pf.bits_per_pixel); /* bits-per-pixel */ - vnc_write_u8(vs, vs->ds->surface->pf.depth); /* depth */ - -#ifdef HOST_WORDS_BIGENDIAN - vnc_write_u8(vs, 1); /* big-endian-flag */ -#else - vnc_write_u8(vs, 0); /* big-endian-flag */ -#endif - vnc_write_u8(vs, 1); /* true-color-flag */ - vnc_write_u16(vs, vs->ds->surface->pf.rmax); /* red-max */ - vnc_write_u16(vs, vs->ds->surface->pf.gmax); /* green-max */ - vnc_write_u16(vs, vs->ds->surface->pf.bmax); /* blue-max */ - vnc_write_u8(vs, vs->ds->surface->pf.rshift); /* red-shift */ - vnc_write_u8(vs, vs->ds->surface->pf.gshift); /* green-shift */ - vnc_write_u8(vs, vs->ds->surface->pf.bshift); /* blue-shift */ - if (vs->ds->surface->pf.bits_per_pixel == 32) - vs->send_hextile_tile = send_hextile_tile_32; - else if (vs->ds->surface->pf.bits_per_pixel == 16) - vs->send_hextile_tile = send_hextile_tile_16; - else if (vs->ds->surface->pf.bits_per_pixel == 8) - vs->send_hextile_tile = send_hextile_tile_8; - vs->clientds = *(vs->ds->surface); - vs->clientds.flags &= ~QEMU_ALLOCATED_FLAG; - vs->write_pixels = vnc_write_pixels_copy; - - vnc_write(vs, pad, 3); /* padding */ -} - -static void vnc_dpy_setdata(DisplayState *ds) -{ - /* We don't have to do anything */ -} - -static void vnc_colordepth(VncState *vs) -{ - if (vnc_has_feature(vs, VNC_FEATURE_WMVI)) { - /* Sending a WMVi message to notify the client*/ - vnc_write_u8(vs, 0); /* msg id */ - vnc_write_u8(vs, 0); - vnc_write_u16(vs, 1); /* number of rects */ - vnc_framebuffer_update(vs, 0, 0, ds_get_width(vs->ds), - ds_get_height(vs->ds), VNC_ENCODING_WMVi); - pixel_format_message(vs); - vnc_flush(vs); - } else { - set_pixel_conversion(vs); - } -} - -static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len) -{ - int i; - uint16_t limit; - VncDisplay *vd = vs->vd; - - if (data[0] > 3) { - vd->timer_interval = VNC_REFRESH_INTERVAL_BASE; - if (!qemu_timer_expired(vd->timer, qemu_get_clock(rt_clock) + vd->timer_interval)) - qemu_mod_timer(vd->timer, qemu_get_clock(rt_clock) + vd->timer_interval); - } - - switch (data[0]) { - case 0: - if (len == 1) - return 20; - - set_pixel_format(vs, read_u8(data, 4), read_u8(data, 5), - read_u8(data, 6), read_u8(data, 7), - read_u16(data, 8), read_u16(data, 10), - read_u16(data, 12), read_u8(data, 14), - read_u8(data, 15), read_u8(data, 16)); - break; - case 2: - if (len == 1) - return 4; - - if (len == 4) { - limit = read_u16(data, 2); - if (limit > 0) - return 4 + (limit * 4); - } else - limit = read_u16(data, 2); - - for (i = 0; i < limit; i++) { - int32_t val = read_s32(data, 4 + (i * 4)); - memcpy(data + 4 + (i * 4), &val, sizeof(val)); - } - - set_encodings(vs, (int32_t *)(data + 4), limit); - break; - case 3: - if (len == 1) - return 10; - - framebuffer_update_request(vs, - read_u8(data, 1), read_u16(data, 2), read_u16(data, 4), - read_u16(data, 6), read_u16(data, 8)); - break; - case 4: - if (len == 1) - return 8; - - key_event(vs, read_u8(data, 1), read_u32(data, 4)); - break; - case 5: - if (len == 1) - return 6; - - pointer_event(vs, read_u8(data, 1), read_u16(data, 2), read_u16(data, 4)); - break; - case 6: - if (len == 1) - return 8; - - if (len == 8) { - uint32_t dlen = read_u32(data, 4); - if (dlen > 0) - return 8 + dlen; - } - - client_cut_text(vs, read_u32(data, 4), data + 8); - break; - case 255: - if (len == 1) - return 2; - - switch (read_u8(data, 1)) { - case 0: - if (len == 2) - return 12; - - ext_key_event(vs, read_u16(data, 2), - read_u32(data, 4), read_u32(data, 8)); - break; - case 1: - if (len == 2) - return 4; - - switch (read_u16 (data, 2)) { - case 0: - audio_add(vs); - break; - case 1: - audio_del(vs); - break; - case 2: - if (len == 4) - return 10; - switch (read_u8(data, 4)) { - case 0: vs->as.fmt = AUD_FMT_U8; break; - case 1: vs->as.fmt = AUD_FMT_S8; break; - case 2: vs->as.fmt = AUD_FMT_U16; break; - case 3: vs->as.fmt = AUD_FMT_S16; break; - case 4: vs->as.fmt = AUD_FMT_U32; break; - case 5: vs->as.fmt = AUD_FMT_S32; break; - default: - printf("Invalid audio format %d\n", read_u8(data, 4)); - vnc_client_error(vs); - break; - } - vs->as.nchannels = read_u8(data, 5); - if (vs->as.nchannels != 1 && vs->as.nchannels != 2) { - printf("Invalid audio channel coount %d\n", - read_u8(data, 5)); - vnc_client_error(vs); - break; - } - vs->as.freq = read_u32(data, 6); - break; - default: - printf ("Invalid audio message %d\n", read_u8(data, 4)); - vnc_client_error(vs); - break; - } - break; - - default: - printf("Msg: %d\n", read_u16(data, 0)); - vnc_client_error(vs); - break; - } - break; - default: - printf("Msg: %d\n", data[0]); - vnc_client_error(vs); - break; - } - - vnc_read_when(vs, protocol_client_msg, 1); - return 0; -} - -static int protocol_client_init(VncState *vs, uint8_t *data, size_t len) -{ - char buf[1024]; - int size; - - vnc_write_u16(vs, ds_get_width(vs->ds)); - vnc_write_u16(vs, ds_get_height(vs->ds)); - - pixel_format_message(vs); - - if (qemu_name) - size = snprintf(buf, sizeof(buf), "QEMU (%s)", qemu_name); - else - size = snprintf(buf, sizeof(buf), "QEMU"); - - vnc_write_u32(vs, size); - vnc_write(vs, buf, size); - vnc_flush(vs); - - vnc_read_when(vs, protocol_client_msg, 1); - - return 0; -} - -void start_client_init(VncState *vs) -{ - vnc_read_when(vs, protocol_client_init, 1); -} - -static void make_challenge(VncState *vs) -{ - int i; - - srand(time(NULL)+getpid()+getpid()*987654+rand()); - - for (i = 0 ; i < sizeof(vs->challenge) ; i++) - vs->challenge[i] = (int) (256.0*rand()/(RAND_MAX+1.0)); -} - -static int protocol_client_auth_vnc(VncState *vs, uint8_t *data, size_t len) -{ - unsigned char response[VNC_AUTH_CHALLENGE_SIZE]; - int i, j, pwlen; - unsigned char key[8]; - - if (!vs->vd->password || !vs->vd->password[0]) { - VNC_DEBUG("No password configured on server"); - vnc_write_u32(vs, 1); /* Reject auth */ - if (vs->minor >= 8) { - static const char err[] = "Authentication failed"; - vnc_write_u32(vs, sizeof(err)); - vnc_write(vs, err, sizeof(err)); - } - vnc_flush(vs); - vnc_client_error(vs); - return 0; - } - - memcpy(response, vs->challenge, VNC_AUTH_CHALLENGE_SIZE); - - /* Calculate the expected challenge response */ - pwlen = strlen(vs->vd->password); - for (i=0; ivd->password[i] : 0; - deskey(key, EN0); - for (j = 0; j < VNC_AUTH_CHALLENGE_SIZE; j += 8) - des(response+j, response+j); - - /* Compare expected vs actual challenge response */ - if (memcmp(response, data, VNC_AUTH_CHALLENGE_SIZE) != 0) { - VNC_DEBUG("Client challenge reponse did not match\n"); - vnc_write_u32(vs, 1); /* Reject auth */ - if (vs->minor >= 8) { - static const char err[] = "Authentication failed"; - vnc_write_u32(vs, sizeof(err)); - vnc_write(vs, err, sizeof(err)); - } - vnc_flush(vs); - vnc_client_error(vs); - } else { - VNC_DEBUG("Accepting VNC challenge response\n"); - vnc_write_u32(vs, 0); /* Accept auth */ - vnc_flush(vs); - - start_client_init(vs); - } - return 0; -} - -void start_auth_vnc(VncState *vs) -{ - make_challenge(vs); - /* Send client a 'random' challenge */ - vnc_write(vs, vs->challenge, sizeof(vs->challenge)); - vnc_flush(vs); - - vnc_read_when(vs, protocol_client_auth_vnc, sizeof(vs->challenge)); -} - - -static int protocol_client_auth(VncState *vs, uint8_t *data, size_t len) -{ - /* We only advertise 1 auth scheme at a time, so client - * must pick the one we sent. Verify this */ - if (data[0] != vs->vd->auth) { /* Reject auth */ - VNC_DEBUG("Reject auth %d because it didn't match advertized\n", (int)data[0]); - vnc_write_u32(vs, 1); - if (vs->minor >= 8) { - static const char err[] = "Authentication failed"; - vnc_write_u32(vs, sizeof(err)); - vnc_write(vs, err, sizeof(err)); - } - vnc_client_error(vs); - } else { /* Accept requested auth */ - VNC_DEBUG("Client requested auth %d\n", (int)data[0]); - switch (vs->vd->auth) { - case VNC_AUTH_NONE: - VNC_DEBUG("Accept auth none\n"); - if (vs->minor >= 8) { - vnc_write_u32(vs, 0); /* Accept auth completion */ - vnc_flush(vs); - } - start_client_init(vs); - break; - - case VNC_AUTH_VNC: - VNC_DEBUG("Start VNC auth\n"); - start_auth_vnc(vs); - break; - -#ifdef CONFIG_VNC_TLS - case VNC_AUTH_VENCRYPT: - VNC_DEBUG("Accept VeNCrypt auth\n");; - start_auth_vencrypt(vs); - break; -#endif /* CONFIG_VNC_TLS */ - -#ifdef CONFIG_VNC_SASL - case VNC_AUTH_SASL: - VNC_DEBUG("Accept SASL auth\n"); - start_auth_sasl(vs); - break; -#endif /* CONFIG_VNC_SASL */ - - default: /* Should not be possible, but just in case */ - VNC_DEBUG("Reject auth %d server code bug\n", vs->vd->auth); - vnc_write_u8(vs, 1); - if (vs->minor >= 8) { - static const char err[] = "Authentication failed"; - vnc_write_u32(vs, sizeof(err)); - vnc_write(vs, err, sizeof(err)); - } - vnc_client_error(vs); - } - } - return 0; -} - -static int protocol_version(VncState *vs, uint8_t *version, size_t len) -{ - char local[13]; - - memcpy(local, version, 12); - local[12] = 0; - - if (sscanf(local, "RFB %03d.%03d\n", &vs->major, &vs->minor) != 2) { - VNC_DEBUG("Malformed protocol version %s\n", local); - vnc_client_error(vs); - return 0; - } - VNC_DEBUG("Client request protocol version %d.%d\n", vs->major, vs->minor); - if (vs->major != 3 || - (vs->minor != 3 && - vs->minor != 4 && - vs->minor != 5 && - vs->minor != 7 && - vs->minor != 8)) { - VNC_DEBUG("Unsupported client version\n"); - vnc_write_u32(vs, VNC_AUTH_INVALID); - vnc_flush(vs); - vnc_client_error(vs); - return 0; - } - /* Some broken clients report v3.4 or v3.5, which spec requires to be treated - * as equivalent to v3.3 by servers - */ - if (vs->minor == 4 || vs->minor == 5) - vs->minor = 3; - - if (vs->minor == 3) { - if (vs->vd->auth == VNC_AUTH_NONE) { - VNC_DEBUG("Tell client auth none\n"); - vnc_write_u32(vs, vs->vd->auth); - vnc_flush(vs); - start_client_init(vs); - } else if (vs->vd->auth == VNC_AUTH_VNC) { - VNC_DEBUG("Tell client VNC auth\n"); - vnc_write_u32(vs, vs->vd->auth); - vnc_flush(vs); - start_auth_vnc(vs); - } else { - VNC_DEBUG("Unsupported auth %d for protocol 3.3\n", vs->vd->auth); - vnc_write_u32(vs, VNC_AUTH_INVALID); - vnc_flush(vs); - vnc_client_error(vs); - } - } else { - VNC_DEBUG("Telling client we support auth %d\n", vs->vd->auth); - vnc_write_u8(vs, 1); /* num auth */ - vnc_write_u8(vs, vs->vd->auth); - vnc_read_when(vs, protocol_client_auth, 1); - vnc_flush(vs); - } - - return 0; -} - -static int vnc_refresh_server_surface(VncDisplay *vd) -{ - int y; - uint8_t *guest_row; - uint8_t *server_row; - int cmp_bytes; - uint32_t width_mask[VNC_DIRTY_WORDS]; - VncState *vs = NULL; - int has_dirty = 0; - - /* - * Walk through the guest dirty map. - * Check and copy modified bits from guest to server surface. - * Update server dirty map. - */ - vnc_set_bits(width_mask, (ds_get_width(vd->ds) / 16), VNC_DIRTY_WORDS); - cmp_bytes = 16 * ds_get_bytes_per_pixel(vd->ds); - guest_row = vd->guest.ds->data; - server_row = vd->server->data; - for (y = 0; y < vd->guest.ds->height; y++) { - if (vnc_and_bits(vd->guest.dirty[y], width_mask, VNC_DIRTY_WORDS)) { - int x; - uint8_t *guest_ptr; - uint8_t *server_ptr; - - guest_ptr = guest_row; - server_ptr = server_row; - - for (x = 0; x < vd->guest.ds->width; - x += 16, guest_ptr += cmp_bytes, server_ptr += cmp_bytes) { - if (!vnc_get_bit(vd->guest.dirty[y], (x / 16))) - continue; - vnc_clear_bit(vd->guest.dirty[y], (x / 16)); - if (memcmp(server_ptr, guest_ptr, cmp_bytes) == 0) - continue; - memcpy(server_ptr, guest_ptr, cmp_bytes); - vs = vd->clients; - while (vs != NULL) { - vnc_set_bit(vs->dirty[y], (x / 16)); - vs = vs->next; - } - has_dirty++; - } - } - guest_row += ds_get_linesize(vd->ds); - server_row += ds_get_linesize(vd->ds); - } - return has_dirty; -} - -static void vnc_refresh(void *opaque) -{ - VncDisplay *vd = opaque; - VncState *vs = NULL, *vn = NULL; - int has_dirty = 0, rects = 0; - - vga_hw_update(); - - has_dirty = vnc_refresh_server_surface(vd); - - vs = vd->clients; - while (vs != NULL) { - vn = vs->next; - rects += vnc_update_client(vs, has_dirty); - /* vs might be free()ed here */ - vs = vn; - } - /* vd->timer could be NULL now if the last client disconnected, - * in this case don't update the timer */ - if (vd->timer == NULL) - return; - - if (has_dirty && rects) { - vd->timer_interval /= 2; - if (vd->timer_interval < VNC_REFRESH_INTERVAL_BASE) - vd->timer_interval = VNC_REFRESH_INTERVAL_BASE; - } else { - vd->timer_interval += VNC_REFRESH_INTERVAL_INC; - if (vd->timer_interval > VNC_REFRESH_INTERVAL_MAX) - vd->timer_interval = VNC_REFRESH_INTERVAL_MAX; - } - qemu_mod_timer(vd->timer, qemu_get_clock(rt_clock) + vd->timer_interval); -} - -static void vnc_init_timer(VncDisplay *vd) -{ - vd->timer_interval = VNC_REFRESH_INTERVAL_BASE; - if (vd->timer == NULL && vd->clients != NULL) { - vd->timer = qemu_new_timer(rt_clock, vnc_refresh, vd); - vnc_refresh(vd); - } -} - -static void vnc_remove_timer(VncDisplay *vd) -{ - if (vd->timer != NULL && vd->clients == NULL) { - qemu_del_timer(vd->timer); - qemu_free_timer(vd->timer); - vd->timer = NULL; - } -} - -static void vnc_connect(VncDisplay *vd, int csock) -{ - VncState *vs = qemu_mallocz(sizeof(VncState)); - vs->csock = csock; - - VNC_DEBUG("New client on socket %d\n", csock); - dcl->idle = 0; - socket_set_nonblock(vs->csock); - qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs); - - vs->vd = vd; - vs->ds = vd->ds; - vs->last_x = -1; - vs->last_y = -1; - - vs->as.freq = 44100; - vs->as.nchannels = 2; - vs->as.fmt = AUD_FMT_S16; - vs->as.endianness = 0; - - vs->next = vd->clients; - vd->clients = vs; - - vga_hw_update(); - - vnc_write(vs, "RFB 003.008\n", 12); - vnc_flush(vs); - vnc_read_when(vs, protocol_version, 12); - reset_keys(vs); - - vnc_init_timer(vd); - - /* vs might be free()ed here */ -} - -static void vnc_listen_read(void *opaque) -{ - VncDisplay *vs = opaque; - struct sockaddr_in addr; - socklen_t addrlen = sizeof(addr); - - /* Catch-up */ - vga_hw_update(); - - int csock = qemu_accept(vs->lsock, (struct sockaddr *)&addr, &addrlen); - if (csock != -1) { - vnc_connect(vs, csock); - } -} - -void vnc_display_init(DisplayState *ds) -{ - VncDisplay *vs = qemu_mallocz(sizeof(*vs)); - - dcl = qemu_mallocz(sizeof(DisplayChangeListener)); - - ds->opaque = vs; - dcl->idle = 1; - vnc_display = vs; - - vs->lsock = -1; - - vs->ds = ds; - - if (keyboard_layout) - vs->kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout); - else - vs->kbd_layout = init_keyboard_layout(name2keysym, "en-us"); - - if (!vs->kbd_layout) - exit(1); - - dcl->dpy_copy = vnc_dpy_copy; - dcl->dpy_update = vnc_dpy_update; - dcl->dpy_resize = vnc_dpy_resize; - dcl->dpy_setdata = vnc_dpy_setdata; - register_displaychangelistener(ds, dcl); -} - - -void vnc_display_close(DisplayState *ds) -{ - VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display; - - if (!vs) - return; - if (vs->display) { - qemu_free(vs->display); - vs->display = NULL; - } - if (vs->lsock != -1) { - qemu_set_fd_handler2(vs->lsock, NULL, NULL, NULL, NULL); - close(vs->lsock); - vs->lsock = -1; - } - vs->auth = VNC_AUTH_INVALID; -#ifdef CONFIG_VNC_TLS - vs->subauth = VNC_AUTH_INVALID; - vs->tls.x509verify = 0; -#endif -} - -int vnc_display_password(DisplayState *ds, const char *password) -{ - VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display; - - if (!vs) { - return -1; - } - - if (vs->password) { - qemu_free(vs->password); - vs->password = NULL; - } - if (password && password[0]) { - if (!(vs->password = qemu_strdup(password))) - return -1; - if (vs->auth == VNC_AUTH_NONE) { - vs->auth = VNC_AUTH_VNC; - } - } else { - vs->auth = VNC_AUTH_NONE; - } - - return 0; -} - -char *vnc_display_local_addr(DisplayState *ds) -{ - VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display; - - return vnc_socket_local_addr("%s:%s", vs->lsock); -} - -int vnc_display_open(DisplayState *ds, const char *display) -{ - VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display; - const char *options; - int password = 0; - int reverse = 0; - int to_port = 0; -#ifdef CONFIG_VNC_TLS - int tls = 0, x509 = 0; -#endif -#ifdef CONFIG_VNC_SASL - int sasl = 0; - int saslErr; -#endif - int acl = 0; - - if (!vnc_display) - return -1; - vnc_display_close(ds); - if (strcmp(display, "none") == 0) - return 0; - - if (!(vs->display = strdup(display))) - return -1; - - options = display; - while ((options = strchr(options, ','))) { - options++; - if (strncmp(options, "password", 8) == 0) { - password = 1; /* Require password auth */ - } else if (strncmp(options, "reverse", 7) == 0) { - reverse = 1; - } else if (strncmp(options, "to=", 3) == 0) { - to_port = atoi(options+3) + 5900; -#ifdef CONFIG_VNC_SASL - } else if (strncmp(options, "sasl", 4) == 0) { - sasl = 1; /* Require SASL auth */ -#endif -#ifdef CONFIG_VNC_TLS - } else if (strncmp(options, "tls", 3) == 0) { - tls = 1; /* Require TLS */ - } else if (strncmp(options, "x509", 4) == 0) { - char *start, *end; - x509 = 1; /* Require x509 certificates */ - if (strncmp(options, "x509verify", 10) == 0) - vs->tls.x509verify = 1; /* ...and verify client certs */ - - /* Now check for 'x509=/some/path' postfix - * and use that to setup x509 certificate/key paths */ - start = strchr(options, '='); - end = strchr(options, ','); - if (start && (!end || (start < end))) { - int len = end ? end-(start+1) : strlen(start+1); - char *path = qemu_strndup(start + 1, len); - - VNC_DEBUG("Trying certificate path '%s'\n", path); - if (vnc_tls_set_x509_creds_dir(vs, path) < 0) { - fprintf(stderr, "Failed to find x509 certificates/keys in %s\n", path); - qemu_free(path); - qemu_free(vs->display); - vs->display = NULL; - return -1; - } - qemu_free(path); - } else { - fprintf(stderr, "No certificate path provided\n"); - qemu_free(vs->display); - vs->display = NULL; - return -1; - } -#endif - } else if (strncmp(options, "acl", 3) == 0) { - acl = 1; - } - } - -#ifdef CONFIG_VNC_TLS - if (acl && x509 && vs->tls.x509verify) { - if (!(vs->tls.acl = qemu_acl_init("vnc.x509dname"))) { - fprintf(stderr, "Failed to create x509 dname ACL\n"); - exit(1); - } - } -#endif -#ifdef CONFIG_VNC_SASL - if (acl && sasl) { - if (!(vs->sasl.acl = qemu_acl_init("vnc.username"))) { - fprintf(stderr, "Failed to create username ACL\n"); - exit(1); - } - } -#endif - - /* - * Combinations we support here: - * - * - no-auth (clear text, no auth) - * - password (clear text, weak auth) - * - sasl (encrypt, good auth *IF* using Kerberos via GSSAPI) - * - tls (encrypt, weak anonymous creds, no auth) - * - tls + password (encrypt, weak anonymous creds, weak auth) - * - tls + sasl (encrypt, weak anonymous creds, good auth) - * - tls + x509 (encrypt, good x509 creds, no auth) - * - tls + x509 + password (encrypt, good x509 creds, weak auth) - * - tls + x509 + sasl (encrypt, good x509 creds, good auth) - * - * NB1. TLS is a stackable auth scheme. - * NB2. the x509 schemes have option to validate a client cert dname - */ - if (password) { -#ifdef CONFIG_VNC_TLS - if (tls) { - vs->auth = VNC_AUTH_VENCRYPT; - if (x509) { - VNC_DEBUG("Initializing VNC server with x509 password auth\n"); - vs->subauth = VNC_AUTH_VENCRYPT_X509VNC; - } else { - VNC_DEBUG("Initializing VNC server with TLS password auth\n"); - vs->subauth = VNC_AUTH_VENCRYPT_TLSVNC; - } - } else { -#endif /* CONFIG_VNC_TLS */ - VNC_DEBUG("Initializing VNC server with password auth\n"); - vs->auth = VNC_AUTH_VNC; -#ifdef CONFIG_VNC_TLS - vs->subauth = VNC_AUTH_INVALID; - } -#endif /* CONFIG_VNC_TLS */ -#ifdef CONFIG_VNC_SASL - } else if (sasl) { -#ifdef CONFIG_VNC_TLS - if (tls) { - vs->auth = VNC_AUTH_VENCRYPT; - if (x509) { - VNC_DEBUG("Initializing VNC server with x509 SASL auth\n"); - vs->subauth = VNC_AUTH_VENCRYPT_X509SASL; - } else { - VNC_DEBUG("Initializing VNC server with TLS SASL auth\n"); - vs->subauth = VNC_AUTH_VENCRYPT_TLSSASL; - } - } else { -#endif /* CONFIG_VNC_TLS */ - VNC_DEBUG("Initializing VNC server with SASL auth\n"); - vs->auth = VNC_AUTH_SASL; -#ifdef CONFIG_VNC_TLS - vs->subauth = VNC_AUTH_INVALID; - } -#endif /* CONFIG_VNC_TLS */ -#endif /* CONFIG_VNC_SASL */ - } else { -#ifdef CONFIG_VNC_TLS - if (tls) { - vs->auth = VNC_AUTH_VENCRYPT; - if (x509) { - VNC_DEBUG("Initializing VNC server with x509 no auth\n"); - vs->subauth = VNC_AUTH_VENCRYPT_X509NONE; - } else { - VNC_DEBUG("Initializing VNC server with TLS no auth\n"); - vs->subauth = VNC_AUTH_VENCRYPT_TLSNONE; - } - } else { -#endif - VNC_DEBUG("Initializing VNC server with no auth\n"); - vs->auth = VNC_AUTH_NONE; -#ifdef CONFIG_VNC_TLS - vs->subauth = VNC_AUTH_INVALID; - } -#endif - } - -#ifdef CONFIG_VNC_SASL - if ((saslErr = sasl_server_init(NULL, "qemu")) != SASL_OK) { - fprintf(stderr, "Failed to initialize SASL auth %s", - sasl_errstring(saslErr, NULL, NULL)); - free(vs->display); - vs->display = NULL; - return -1; - } -#endif - - if (reverse) { - /* connect to viewer */ - if (strncmp(display, "unix:", 5) == 0) - vs->lsock = unix_connect(display+5); - else - vs->lsock = inet_connect(display, SOCK_STREAM); - if (-1 == vs->lsock) { - free(vs->display); - vs->display = NULL; - return -1; - } else { - int csock = vs->lsock; - vs->lsock = -1; - vnc_connect(vs, csock); - } - return 0; - - } else { - /* listen for connects */ - char *dpy; - dpy = qemu_malloc(256); - if (strncmp(display, "unix:", 5) == 0) { - pstrcpy(dpy, 256, "unix:"); - vs->lsock = unix_listen(display+5, dpy+5, 256-5); - } else { - vs->lsock = inet_listen(display, dpy, 256, SOCK_STREAM, 5900); - } - if (-1 == vs->lsock) { - free(dpy); - return -1; - } else { - free(vs->display); - vs->display = dpy; - } - } - return qemu_set_fd_handler2(vs->lsock, NULL, vnc_listen_read, NULL, vs); -} diff -Nru qemu-kvm-0.12.5+noroms/vnc.h qemu-kvm-0.14.1/vnc.h --- qemu-kvm-0.12.5+noroms/vnc.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/vnc.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,321 +0,0 @@ -/* - * QEMU VNC display driver - * - * Copyright (C) 2006 Anthony Liguori - * Copyright (C) 2006 Fabrice Bellard - * Copyright (C) 2009 Red Hat, Inc - * - * 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. - */ - -#ifndef __QEMU_VNC_H -#define __QEMU_VNC_H - -#include "qemu-common.h" -#include "console.h" -#include "monitor.h" -#include "audio/audio.h" -#include - -#include "keymaps.h" - -// #define _VNC_DEBUG 1 - -#ifdef _VNC_DEBUG -#define VNC_DEBUG(fmt, ...) do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0) -#else -#define VNC_DEBUG(fmt, ...) do { } while (0) -#endif - -/***************************************************************************** - * - * Core data structures - * - *****************************************************************************/ - -typedef struct Buffer -{ - size_t capacity; - size_t offset; - uint8_t *buffer; -} Buffer; - -typedef struct VncState VncState; - -typedef int VncReadEvent(VncState *vs, uint8_t *data, size_t len); - -typedef void VncWritePixels(VncState *vs, void *data, int size); - -typedef void VncSendHextileTile(VncState *vs, - int x, int y, int w, int h, - void *last_bg, - void *last_fg, - int *has_bg, int *has_fg); - -#define VNC_MAX_WIDTH 2560 -#define VNC_MAX_HEIGHT 2048 -#define VNC_DIRTY_WORDS (VNC_MAX_WIDTH / (16 * 32)) - -#define VNC_AUTH_CHALLENGE_SIZE 16 - -typedef struct VncDisplay VncDisplay; - -#ifdef CONFIG_VNC_TLS -#include "vnc-tls.h" -#include "vnc-auth-vencrypt.h" -#endif -#ifdef CONFIG_VNC_SASL -#include "vnc-auth-sasl.h" -#endif - -struct VncSurface -{ - uint32_t dirty[VNC_MAX_HEIGHT][VNC_DIRTY_WORDS]; - DisplaySurface *ds; -}; - -struct VncDisplay -{ - QEMUTimer *timer; - int timer_interval; - int lsock; - DisplayState *ds; - VncState *clients; - kbd_layout_t *kbd_layout; - - struct VncSurface guest; /* guest visible surface (aka ds->surface) */ - DisplaySurface *server; /* vnc server surface */ - - char *display; - char *password; - int auth; -#ifdef CONFIG_VNC_TLS - int subauth; /* Used by VeNCrypt */ - VncDisplayTLS tls; -#endif -#ifdef CONFIG_VNC_SASL - VncDisplaySASL sasl; -#endif -}; - -struct VncState -{ - int csock; - - DisplayState *ds; - uint32_t dirty[VNC_MAX_HEIGHT][VNC_DIRTY_WORDS]; - - VncDisplay *vd; - int need_update; - int force_update; - uint32_t features; - int absolute; - int last_x; - int last_y; - - uint32_t vnc_encoding; - uint8_t tight_quality; - uint8_t tight_compression; - - int major; - int minor; - - char challenge[VNC_AUTH_CHALLENGE_SIZE]; -#ifdef CONFIG_VNC_TLS - VncStateTLS tls; -#endif -#ifdef CONFIG_VNC_SASL - VncStateSASL sasl; -#endif - - Buffer output; - Buffer input; - /* current output mode information */ - VncWritePixels *write_pixels; - VncSendHextileTile *send_hextile_tile; - DisplaySurface clientds; - - CaptureVoiceOut *audio_cap; - struct audsettings as; - - VncReadEvent *read_handler; - size_t read_handler_expect; - /* input */ - uint8_t modifiers_state[256]; - - Buffer zlib; - Buffer zlib_tmp; - z_stream zlib_stream[4]; - - VncState *next; -}; - - -/***************************************************************************** - * - * Authentication modes - * - *****************************************************************************/ - -enum { - VNC_AUTH_INVALID = 0, - VNC_AUTH_NONE = 1, - VNC_AUTH_VNC = 2, - VNC_AUTH_RA2 = 5, - VNC_AUTH_RA2NE = 6, - VNC_AUTH_TIGHT = 16, - VNC_AUTH_ULTRA = 17, - VNC_AUTH_TLS = 18, /* Supported in GTK-VNC & VINO */ - VNC_AUTH_VENCRYPT = 19, /* Supported in GTK-VNC & VeNCrypt */ - VNC_AUTH_SASL = 20, /* Supported in GTK-VNC & VINO */ -}; - -enum { - VNC_AUTH_VENCRYPT_PLAIN = 256, - VNC_AUTH_VENCRYPT_TLSNONE = 257, - VNC_AUTH_VENCRYPT_TLSVNC = 258, - VNC_AUTH_VENCRYPT_TLSPLAIN = 259, - VNC_AUTH_VENCRYPT_X509NONE = 260, - VNC_AUTH_VENCRYPT_X509VNC = 261, - VNC_AUTH_VENCRYPT_X509PLAIN = 262, - VNC_AUTH_VENCRYPT_X509SASL = 263, - VNC_AUTH_VENCRYPT_TLSSASL = 264, -}; - - -/***************************************************************************** - * - * Encoding types - * - *****************************************************************************/ - -#define VNC_ENCODING_RAW 0x00000000 -#define VNC_ENCODING_COPYRECT 0x00000001 -#define VNC_ENCODING_RRE 0x00000002 -#define VNC_ENCODING_CORRE 0x00000004 -#define VNC_ENCODING_HEXTILE 0x00000005 -#define VNC_ENCODING_ZLIB 0x00000006 -#define VNC_ENCODING_TIGHT 0x00000007 -#define VNC_ENCODING_ZLIBHEX 0x00000008 -#define VNC_ENCODING_TRLE 0x0000000f -#define VNC_ENCODING_ZRLE 0x00000010 -#define VNC_ENCODING_ZYWRLE 0x00000011 -#define VNC_ENCODING_COMPRESSLEVEL0 0xFFFFFF00 /* -256 */ -#define VNC_ENCODING_QUALITYLEVEL0 0xFFFFFFE0 /* -32 */ -#define VNC_ENCODING_XCURSOR 0xFFFFFF10 /* -240 */ -#define VNC_ENCODING_RICH_CURSOR 0xFFFFFF11 /* -239 */ -#define VNC_ENCODING_POINTER_POS 0xFFFFFF18 /* -232 */ -#define VNC_ENCODING_LASTRECT 0xFFFFFF20 /* -224 */ -#define VNC_ENCODING_DESKTOPRESIZE 0xFFFFFF21 /* -223 */ -#define VNC_ENCODING_POINTER_TYPE_CHANGE 0XFFFFFEFF /* -257 */ -#define VNC_ENCODING_EXT_KEY_EVENT 0XFFFFFEFE /* -258 */ -#define VNC_ENCODING_AUDIO 0XFFFFFEFD /* -259 */ -#define VNC_ENCODING_WMVi 0x574D5669 - -/***************************************************************************** - * - * Other tight constants - * - *****************************************************************************/ - -/* - * Vendors known by TightVNC: standard VNC/RealVNC, TridiaVNC, and TightVNC. - */ - -#define VNC_TIGHT_CCB_RESET_MASK (0x0f) -#define VNC_TIGHT_CCB_TYPE_MASK (0x0f << 4) -#define VNC_TIGHT_CCB_TYPE_FILL (0x08 << 4) -#define VNC_TIGHT_CCB_TYPE_JPEG (0x09 << 4) -#define VNC_TIGHT_CCB_BASIC_MAX (0x07 << 4) -#define VNC_TIGHT_CCB_BASIC_ZLIB (0x03 << 4) -#define VNC_TIGHT_CCB_BASIC_FILTER (0x04 << 4) - -/***************************************************************************** - * - * Features - * - *****************************************************************************/ - -#define VNC_FEATURE_RESIZE 0 -#define VNC_FEATURE_HEXTILE 1 -#define VNC_FEATURE_POINTER_TYPE_CHANGE 2 -#define VNC_FEATURE_WMVI 3 -#define VNC_FEATURE_TIGHT 4 -#define VNC_FEATURE_ZLIB 5 -#define VNC_FEATURE_COPYRECT 6 - -#define VNC_FEATURE_RESIZE_MASK (1 << VNC_FEATURE_RESIZE) -#define VNC_FEATURE_HEXTILE_MASK (1 << VNC_FEATURE_HEXTILE) -#define VNC_FEATURE_POINTER_TYPE_CHANGE_MASK (1 << VNC_FEATURE_POINTER_TYPE_CHANGE) -#define VNC_FEATURE_WMVI_MASK (1 << VNC_FEATURE_WMVI) -#define VNC_FEATURE_TIGHT_MASK (1 << VNC_FEATURE_TIGHT) -#define VNC_FEATURE_ZLIB_MASK (1 << VNC_FEATURE_ZLIB) -#define VNC_FEATURE_COPYRECT_MASK (1 << VNC_FEATURE_COPYRECT) - - -/***************************************************************************** - * - * Internal APIs - * - *****************************************************************************/ - -/* Event loop functions */ -void vnc_client_read(void *opaque); -void vnc_client_write(void *opaque); - -long vnc_client_read_buf(VncState *vs, uint8_t *data, size_t datalen); -long vnc_client_write_buf(VncState *vs, const uint8_t *data, size_t datalen); - -/* Protocol I/O functions */ -void vnc_write(VncState *vs, const void *data, size_t len); -void vnc_write_u32(VncState *vs, uint32_t value); -void vnc_write_s32(VncState *vs, int32_t value); -void vnc_write_u16(VncState *vs, uint16_t value); -void vnc_write_u8(VncState *vs, uint8_t value); -void vnc_flush(VncState *vs); -void vnc_read_when(VncState *vs, VncReadEvent *func, size_t expecting); - - -/* Buffer I/O functions */ -uint8_t read_u8(uint8_t *data, size_t offset); -uint16_t read_u16(uint8_t *data, size_t offset); -int32_t read_s32(uint8_t *data, size_t offset); -uint32_t read_u32(uint8_t *data, size_t offset); - -/* Protocol stage functions */ -void vnc_client_error(VncState *vs); -int vnc_client_io_error(VncState *vs, int ret, int last_errno); - -void start_client_init(VncState *vs); -void start_auth_vnc(VncState *vs); - -/* Buffer management */ -void buffer_reserve(Buffer *buffer, size_t len); -int buffer_empty(Buffer *buffer); -uint8_t *buffer_end(Buffer *buffer); -void buffer_reset(Buffer *buffer); -void buffer_append(Buffer *buffer, const void *data, size_t len); - - -/* Misc helpers */ - -char *vnc_socket_local_addr(const char *format, int fd); -char *vnc_socket_remote_addr(const char *format, int fd); - -#endif /* __QEMU_VNC_H */ diff -Nru qemu-kvm-0.12.5+noroms/vnchextile.h qemu-kvm-0.14.1/vnchextile.h --- qemu-kvm-0.12.5+noroms/vnchextile.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/vnchextile.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,210 +0,0 @@ -#define CONCAT_I(a, b) a ## b -#define CONCAT(a, b) CONCAT_I(a, b) -#define pixel_t CONCAT(uint, CONCAT(BPP, _t)) -#ifdef GENERIC -#define NAME CONCAT(generic_, BPP) -#else -#define NAME BPP -#endif - -static void CONCAT(send_hextile_tile_, NAME)(VncState *vs, - int x, int y, int w, int h, - void *last_bg_, - void *last_fg_, - int *has_bg, int *has_fg) -{ - VncDisplay *vd = vs->vd; - uint8_t *row = vd->server->data + y * ds_get_linesize(vs->ds) + x * ds_get_bytes_per_pixel(vs->ds); - pixel_t *irow = (pixel_t *)row; - int j, i; - pixel_t *last_bg = (pixel_t *)last_bg_; - pixel_t *last_fg = (pixel_t *)last_fg_; - pixel_t bg = 0; - pixel_t fg = 0; - int n_colors = 0; - int bg_count = 0; - int fg_count = 0; - int flags = 0; - uint8_t data[(vs->clientds.pf.bytes_per_pixel + 2) * 16 * 16]; - int n_data = 0; - int n_subtiles = 0; - - for (j = 0; j < h; j++) { - for (i = 0; i < w; i++) { - switch (n_colors) { - case 0: - bg = irow[i]; - n_colors = 1; - break; - case 1: - if (irow[i] != bg) { - fg = irow[i]; - n_colors = 2; - } - break; - case 2: - if (irow[i] != bg && irow[i] != fg) { - n_colors = 3; - } else { - if (irow[i] == bg) - bg_count++; - else if (irow[i] == fg) - fg_count++; - } - break; - default: - break; - } - } - if (n_colors > 2) - break; - irow += ds_get_linesize(vs->ds) / sizeof(pixel_t); - } - - if (n_colors > 1 && fg_count > bg_count) { - pixel_t tmp = fg; - fg = bg; - bg = tmp; - } - - if (!*has_bg || *last_bg != bg) { - flags |= 0x02; - *has_bg = 1; - *last_bg = bg; - } - - if (n_colors < 3 && (!*has_fg || *last_fg != fg)) { - flags |= 0x04; - *has_fg = 1; - *last_fg = fg; - } - - switch (n_colors) { - case 1: - n_data = 0; - break; - case 2: - flags |= 0x08; - - irow = (pixel_t *)row; - - for (j = 0; j < h; j++) { - int min_x = -1; - for (i = 0; i < w; i++) { - if (irow[i] == fg) { - if (min_x == -1) - min_x = i; - } else if (min_x != -1) { - hextile_enc_cord(data + n_data, min_x, j, i - min_x, 1); - n_data += 2; - n_subtiles++; - min_x = -1; - } - } - if (min_x != -1) { - hextile_enc_cord(data + n_data, min_x, j, i - min_x, 1); - n_data += 2; - n_subtiles++; - } - irow += ds_get_linesize(vs->ds) / sizeof(pixel_t); - } - break; - case 3: - flags |= 0x18; - - irow = (pixel_t *)row; - - if (!*has_bg || *last_bg != bg) - flags |= 0x02; - - for (j = 0; j < h; j++) { - int has_color = 0; - int min_x = -1; - pixel_t color = 0; /* shut up gcc */ - - for (i = 0; i < w; i++) { - if (!has_color) { - if (irow[i] == bg) - continue; - color = irow[i]; - min_x = i; - has_color = 1; - } else if (irow[i] != color) { - has_color = 0; -#ifdef GENERIC - vnc_convert_pixel(vs, data + n_data, color); - n_data += vs->clientds.pf.bytes_per_pixel; -#else - memcpy(data + n_data, &color, sizeof(color)); - n_data += sizeof(pixel_t); -#endif - hextile_enc_cord(data + n_data, min_x, j, i - min_x, 1); - n_data += 2; - n_subtiles++; - - min_x = -1; - if (irow[i] != bg) { - color = irow[i]; - min_x = i; - has_color = 1; - } - } - } - if (has_color) { -#ifdef GENERIC - vnc_convert_pixel(vs, data + n_data, color); - n_data += vs->clientds.pf.bytes_per_pixel; -#else - memcpy(data + n_data, &color, sizeof(color)); - n_data += sizeof(pixel_t); -#endif - hextile_enc_cord(data + n_data, min_x, j, i - min_x, 1); - n_data += 2; - n_subtiles++; - } - irow += ds_get_linesize(vs->ds) / sizeof(pixel_t); - } - - /* A SubrectsColoured subtile invalidates the foreground color */ - *has_fg = 0; - if (n_data > (w * h * sizeof(pixel_t))) { - n_colors = 4; - flags = 0x01; - *has_bg = 0; - - /* we really don't have to invalidate either the bg or fg - but we've lost the old values. oh well. */ - } - default: - break; - } - - if (n_colors > 3) { - flags = 0x01; - *has_fg = 0; - *has_bg = 0; - n_colors = 4; - } - - vnc_write_u8(vs, flags); - if (n_colors < 4) { - if (flags & 0x02) - vs->write_pixels(vs, last_bg, sizeof(pixel_t)); - if (flags & 0x04) - vs->write_pixels(vs, last_fg, sizeof(pixel_t)); - if (n_subtiles) { - vnc_write_u8(vs, n_subtiles); - vnc_write(vs, data, n_data); - } - } else { - for (j = 0; j < h; j++) { - vs->write_pixels(vs, row, w * ds_get_bytes_per_pixel(vs->ds)); - row += ds_get_linesize(vs->ds); - } - } -} - -#undef NAME -#undef pixel_t -#undef CONCAT_I -#undef CONCAT diff -Nru qemu-kvm-0.12.5+noroms/vnc_keysym.h qemu-kvm-0.14.1/vnc_keysym.h --- qemu-kvm-0.12.5+noroms/vnc_keysym.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/vnc_keysym.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,324 +0,0 @@ - -#include "keymaps.h" - -static const name2keysym_t name2keysym[]={ -/* ascii */ - { "space", 0x020}, - { "exclam", 0x021}, - { "quotedbl", 0x022}, - { "numbersign", 0x023}, - { "dollar", 0x024}, - { "percent", 0x025}, - { "ampersand", 0x026}, - { "apostrophe", 0x027}, - { "parenleft", 0x028}, - { "parenright", 0x029}, - { "asterisk", 0x02a}, - { "plus", 0x02b}, - { "comma", 0x02c}, - { "minus", 0x02d}, - { "period", 0x02e}, - { "slash", 0x02f}, - { "0", 0x030}, - { "1", 0x031}, - { "2", 0x032}, - { "3", 0x033}, - { "4", 0x034}, - { "5", 0x035}, - { "6", 0x036}, - { "7", 0x037}, - { "8", 0x038}, - { "9", 0x039}, - { "colon", 0x03a}, - { "semicolon", 0x03b}, - { "less", 0x03c}, - { "equal", 0x03d}, - { "greater", 0x03e}, - { "question", 0x03f}, - { "at", 0x040}, - { "A", 0x041}, - { "B", 0x042}, - { "C", 0x043}, - { "D", 0x044}, - { "E", 0x045}, - { "F", 0x046}, - { "G", 0x047}, - { "H", 0x048}, - { "I", 0x049}, - { "J", 0x04a}, - { "K", 0x04b}, - { "L", 0x04c}, - { "M", 0x04d}, - { "N", 0x04e}, - { "O", 0x04f}, - { "P", 0x050}, - { "Q", 0x051}, - { "R", 0x052}, - { "S", 0x053}, - { "T", 0x054}, - { "U", 0x055}, - { "V", 0x056}, - { "W", 0x057}, - { "X", 0x058}, - { "Y", 0x059}, - { "Z", 0x05a}, - { "bracketleft", 0x05b}, - { "backslash", 0x05c}, - { "bracketright", 0x05d}, - { "asciicircum", 0x05e}, - { "underscore", 0x05f}, - { "grave", 0x060}, - { "a", 0x061}, - { "b", 0x062}, - { "c", 0x063}, - { "d", 0x064}, - { "e", 0x065}, - { "f", 0x066}, - { "g", 0x067}, - { "h", 0x068}, - { "i", 0x069}, - { "j", 0x06a}, - { "k", 0x06b}, - { "l", 0x06c}, - { "m", 0x06d}, - { "n", 0x06e}, - { "o", 0x06f}, - { "p", 0x070}, - { "q", 0x071}, - { "r", 0x072}, - { "s", 0x073}, - { "t", 0x074}, - { "u", 0x075}, - { "v", 0x076}, - { "w", 0x077}, - { "x", 0x078}, - { "y", 0x079}, - { "z", 0x07a}, - { "braceleft", 0x07b}, - { "bar", 0x07c}, - { "braceright", 0x07d}, - { "asciitilde", 0x07e}, - -/* latin 1 extensions */ -{ "nobreakspace", 0x0a0}, -{ "exclamdown", 0x0a1}, -{ "cent", 0x0a2}, -{ "sterling", 0x0a3}, -{ "currency", 0x0a4}, -{ "yen", 0x0a5}, -{ "brokenbar", 0x0a6}, -{ "section", 0x0a7}, -{ "diaeresis", 0x0a8}, -{ "copyright", 0x0a9}, -{ "ordfeminine", 0x0aa}, -{ "guillemotleft", 0x0ab}, -{ "notsign", 0x0ac}, -{ "hyphen", 0x0ad}, -{ "registered", 0x0ae}, -{ "macron", 0x0af}, -{ "degree", 0x0b0}, -{ "plusminus", 0x0b1}, -{ "twosuperior", 0x0b2}, -{ "threesuperior", 0x0b3}, -{ "acute", 0x0b4}, -{ "mu", 0x0b5}, -{ "paragraph", 0x0b6}, -{ "periodcentered", 0x0b7}, -{ "cedilla", 0x0b8}, -{ "onesuperior", 0x0b9}, -{ "masculine", 0x0ba}, -{ "guillemotright", 0x0bb}, -{ "onequarter", 0x0bc}, -{ "onehalf", 0x0bd}, -{ "threequarters", 0x0be}, -{ "questiondown", 0x0bf}, -{ "Agrave", 0x0c0}, -{ "Aacute", 0x0c1}, -{ "Acircumflex", 0x0c2}, -{ "Atilde", 0x0c3}, -{ "Adiaeresis", 0x0c4}, -{ "Aring", 0x0c5}, -{ "AE", 0x0c6}, -{ "Ccedilla", 0x0c7}, -{ "Egrave", 0x0c8}, -{ "Eacute", 0x0c9}, -{ "Ecircumflex", 0x0ca}, -{ "Ediaeresis", 0x0cb}, -{ "Igrave", 0x0cc}, -{ "Iacute", 0x0cd}, -{ "Icircumflex", 0x0ce}, -{ "Idiaeresis", 0x0cf}, -{ "ETH", 0x0d0}, -{ "Eth", 0x0d0}, -{ "Ntilde", 0x0d1}, -{ "Ograve", 0x0d2}, -{ "Oacute", 0x0d3}, -{ "Ocircumflex", 0x0d4}, -{ "Otilde", 0x0d5}, -{ "Odiaeresis", 0x0d6}, -{ "multiply", 0x0d7}, -{ "Ooblique", 0x0d8}, -{ "Oslash", 0x0d8}, -{ "Ugrave", 0x0d9}, -{ "Uacute", 0x0da}, -{ "Ucircumflex", 0x0db}, -{ "Udiaeresis", 0x0dc}, -{ "Yacute", 0x0dd}, -{ "THORN", 0x0de}, -{ "Thorn", 0x0de}, -{ "ssharp", 0x0df}, -{ "agrave", 0x0e0}, -{ "aacute", 0x0e1}, -{ "acircumflex", 0x0e2}, -{ "atilde", 0x0e3}, -{ "adiaeresis", 0x0e4}, -{ "aring", 0x0e5}, -{ "ae", 0x0e6}, -{ "ccedilla", 0x0e7}, -{ "egrave", 0x0e8}, -{ "eacute", 0x0e9}, -{ "ecircumflex", 0x0ea}, -{ "ediaeresis", 0x0eb}, -{ "igrave", 0x0ec}, -{ "iacute", 0x0ed}, -{ "icircumflex", 0x0ee}, -{ "idiaeresis", 0x0ef}, -{ "eth", 0x0f0}, -{ "ntilde", 0x0f1}, -{ "ograve", 0x0f2}, -{ "oacute", 0x0f3}, -{ "ocircumflex", 0x0f4}, -{ "otilde", 0x0f5}, -{ "odiaeresis", 0x0f6}, -{ "division", 0x0f7}, -{ "oslash", 0x0f8}, -{ "ooblique", 0x0f8}, -{ "ugrave", 0x0f9}, -{ "uacute", 0x0fa}, -{ "ucircumflex", 0x0fb}, -{ "udiaeresis", 0x0fc}, -{ "yacute", 0x0fd}, -{ "thorn", 0x0fe}, -{ "ydiaeresis", 0x0ff}, -{"EuroSign", 0x20ac}, /* XK_EuroSign */ - - /* modifiers */ -{"ISO_Level3_Shift", 0xfe03}, /* XK_ISO_Level3_Shift */ -{"Control_L", 0xffe3}, /* XK_Control_L */ -{"Control_R", 0xffe4}, /* XK_Control_R */ -{"Alt_L", 0xffe9}, /* XK_Alt_L */ -{"Alt_R", 0xffea}, /* XK_Alt_R */ -{"Caps_Lock", 0xffe5}, /* XK_Caps_Lock */ -{"Meta_L", 0xffe7}, /* XK_Meta_L */ -{"Meta_R", 0xffe8}, /* XK_Meta_R */ -{"Shift_L", 0xffe1}, /* XK_Shift_L */ -{"Shift_R", 0xffe2}, /* XK_Shift_R */ -{"Super_L", 0xffeb}, /* XK_Super_L */ -{"Super_R", 0xffec}, /* XK_Super_R */ - - /* special keys */ -{"BackSpace", 0xff08}, /* XK_BackSpace */ -{"Tab", 0xff09}, /* XK_Tab */ -{"Return", 0xff0d}, /* XK_Return */ -{"Right", 0xff53}, /* XK_Right */ -{"Left", 0xff51}, /* XK_Left */ -{"Up", 0xff52}, /* XK_Up */ -{"Down", 0xff54}, /* XK_Down */ -{"Page_Down", 0xff56}, /* XK_Page_Down */ -{"Page_Up", 0xff55}, /* XK_Page_Up */ -{"Insert", 0xff63}, /* XK_Insert */ -{"Delete", 0xffff}, /* XK_Delete */ -{"Home", 0xff50}, /* XK_Home */ -{"End", 0xff57}, /* XK_End */ -{"Scroll_Lock", 0xff14}, /* XK_Scroll_Lock */ -{"KP_Home", 0xff95}, -{"KP_Left", 0xff96}, -{"KP_Up", 0xff97}, -{"KP_Right", 0xff98}, -{"KP_Down", 0xff99}, -{"KP_Prior", 0xff9a}, -{"KP_Page_Up", 0xff9a}, -{"KP_Next", 0xff9b}, -{"KP_Page_Down", 0xff9b}, -{"KP_End", 0xff9c}, -{"KP_Begin", 0xff9d}, -{"KP_Insert", 0xff9e}, -{"KP_Delete", 0xff9f}, -{"F1", 0xffbe}, /* XK_F1 */ -{"F2", 0xffbf}, /* XK_F2 */ -{"F3", 0xffc0}, /* XK_F3 */ -{"F4", 0xffc1}, /* XK_F4 */ -{"F5", 0xffc2}, /* XK_F5 */ -{"F6", 0xffc3}, /* XK_F6 */ -{"F7", 0xffc4}, /* XK_F7 */ -{"F8", 0xffc5}, /* XK_F8 */ -{"F9", 0xffc6}, /* XK_F9 */ -{"F10", 0xffc7}, /* XK_F10 */ -{"F11", 0xffc8}, /* XK_F11 */ -{"F12", 0xffc9}, /* XK_F12 */ -{"F13", 0xffca}, /* XK_F13 */ -{"F14", 0xffcb}, /* XK_F14 */ -{"F15", 0xffcc}, /* XK_F15 */ -{"Sys_Req", 0xff15}, /* XK_Sys_Req */ -{"KP_0", 0xffb0}, /* XK_KP_0 */ -{"KP_1", 0xffb1}, /* XK_KP_1 */ -{"KP_2", 0xffb2}, /* XK_KP_2 */ -{"KP_3", 0xffb3}, /* XK_KP_3 */ -{"KP_4", 0xffb4}, /* XK_KP_4 */ -{"KP_5", 0xffb5}, /* XK_KP_5 */ -{"KP_6", 0xffb6}, /* XK_KP_6 */ -{"KP_7", 0xffb7}, /* XK_KP_7 */ -{"KP_8", 0xffb8}, /* XK_KP_8 */ -{"KP_9", 0xffb9}, /* XK_KP_9 */ -{"KP_Add", 0xffab}, /* XK_KP_Add */ -{"KP_Separator", 0xffac},/* XK_KP_Separator */ -{"KP_Decimal", 0xffae}, /* XK_KP_Decimal */ -{"KP_Divide", 0xffaf}, /* XK_KP_Divide */ -{"KP_Enter", 0xff8d}, /* XK_KP_Enter */ -{"KP_Equal", 0xffbd}, /* XK_KP_Equal */ -{"KP_Multiply", 0xffaa}, /* XK_KP_Multiply */ -{"KP_Subtract", 0xffad}, /* XK_KP_Subtract */ -{"help", 0xff6a}, /* XK_Help */ -{"Menu", 0xff67}, /* XK_Menu */ -{"Print", 0xff61}, /* XK_Print */ -{"Mode_switch", 0xff7e}, /* XK_Mode_switch */ -{"Num_Lock", 0xff7f}, /* XK_Num_Lock */ -{"Pause", 0xff13}, /* XK_Pause */ -{"Escape", 0xff1b}, /* XK_Escape */ - -/* dead keys */ -{"dead_grave", 0xfe50}, /* XK_dead_grave */ -{"dead_acute", 0xfe51}, /* XK_dead_acute */ -{"dead_circumflex", 0xfe52}, /* XK_dead_circumflex */ -{"dead_tilde", 0xfe53}, /* XK_dead_tilde */ -{"dead_macron", 0xfe54}, /* XK_dead_macron */ -{"dead_breve", 0xfe55}, /* XK_dead_breve */ -{"dead_abovedot", 0xfe56}, /* XK_dead_abovedot */ -{"dead_diaeresis", 0xfe57}, /* XK_dead_diaeresis */ -{"dead_abovering", 0xfe58}, /* XK_dead_abovering */ -{"dead_doubleacute", 0xfe59}, /* XK_dead_doubleacute */ -{"dead_caron", 0xfe5a}, /* XK_dead_caron */ -{"dead_cedilla", 0xfe5b}, /* XK_dead_cedilla */ -{"dead_ogonek", 0xfe5c}, /* XK_dead_ogonek */ -{"dead_iota", 0xfe5d}, /* XK_dead_iota */ -{"dead_voiced_sound", 0xfe5e}, /* XK_dead_voiced_sound */ -{"dead_semivoiced_sound", 0xfe5f}, /* XK_dead_semivoiced_sound */ -{"dead_belowdot", 0xfe60}, /* XK_dead_belowdot */ -{"dead_hook", 0xfe61}, /* XK_dead_hook */ -{"dead_horn", 0xfe62}, /* XK_dead_horn */ - - - /* localized keys */ -{"BackApostrophe", 0xff21}, -{"Muhenkan", 0xff22}, -{"Katakana", 0xff27}, -{"Hankaku", 0xff29}, -{"Zenkaku_Hankaku", 0xff2a}, -{"Henkan_Mode_Real", 0xff23}, -{"Henkan_Mode_Ultra", 0xff3e}, -{"backslash_ja", 0xffa5}, -{"Katakana_Real", 0xff25}, -{"Eisu_toggle", 0xff30}, - -{NULL,0}, -}; diff -Nru qemu-kvm-0.12.5+noroms/vnc-tls.c qemu-kvm-0.14.1/vnc-tls.c --- qemu-kvm-0.12.5+noroms/vnc-tls.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/vnc-tls.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,450 +0,0 @@ -/* - * QEMU VNC display driver: TLS helpers - * - * Copyright (C) 2006 Anthony Liguori - * Copyright (C) 2006 Fabrice Bellard - * Copyright (C) 2009 Red Hat, Inc - * - * 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 "vnc.h" -#include "qemu_socket.h" - -#if defined(_VNC_DEBUG) && _VNC_DEBUG >= 2 -/* Very verbose, so only enabled for _VNC_DEBUG >= 2 */ -static void vnc_debug_gnutls_log(int level, const char* str) { - VNC_DEBUG("%d %s", level, str); -} -#endif /* defined(_VNC_DEBUG) && _VNC_DEBUG >= 2 */ - - -#define DH_BITS 1024 -static gnutls_dh_params_t dh_params; - -static int vnc_tls_initialize(void) -{ - static int tlsinitialized = 0; - - if (tlsinitialized) - return 1; - - if (gnutls_global_init () < 0) - return 0; - - /* XXX ought to re-generate diffie-hellmen params periodically */ - if (gnutls_dh_params_init (&dh_params) < 0) - return 0; - if (gnutls_dh_params_generate2 (dh_params, DH_BITS) < 0) - return 0; - -#if defined(_VNC_DEBUG) && _VNC_DEBUG >= 2 - gnutls_global_set_log_level(10); - gnutls_global_set_log_function(vnc_debug_gnutls_log); -#endif - - tlsinitialized = 1; - - return 1; -} - -static ssize_t vnc_tls_push(gnutls_transport_ptr_t transport, - const void *data, - size_t len) { - struct VncState *vs = (struct VncState *)transport; - int ret; - - retry: - ret = send(vs->csock, data, len, 0); - if (ret < 0) { - if (errno == EINTR) - goto retry; - return -1; - } - return ret; -} - - -static ssize_t vnc_tls_pull(gnutls_transport_ptr_t transport, - void *data, - size_t len) { - struct VncState *vs = (struct VncState *)transport; - int ret; - - retry: - ret = recv(vs->csock, data, len, 0); - if (ret < 0) { - if (errno == EINTR) - goto retry; - return -1; - } - return ret; -} - - -static gnutls_anon_server_credentials vnc_tls_initialize_anon_cred(void) -{ - gnutls_anon_server_credentials anon_cred; - int ret; - - if ((ret = gnutls_anon_allocate_server_credentials(&anon_cred)) < 0) { - VNC_DEBUG("Cannot allocate credentials %s\n", gnutls_strerror(ret)); - return NULL; - } - - gnutls_anon_set_server_dh_params(anon_cred, dh_params); - - return anon_cred; -} - - -static gnutls_certificate_credentials_t vnc_tls_initialize_x509_cred(VncDisplay *vd) -{ - gnutls_certificate_credentials_t x509_cred; - int ret; - - if (!vd->tls.x509cacert) { - VNC_DEBUG("No CA x509 certificate specified\n"); - return NULL; - } - if (!vd->tls.x509cert) { - VNC_DEBUG("No server x509 certificate specified\n"); - return NULL; - } - if (!vd->tls.x509key) { - VNC_DEBUG("No server private key specified\n"); - return NULL; - } - - if ((ret = gnutls_certificate_allocate_credentials(&x509_cred)) < 0) { - VNC_DEBUG("Cannot allocate credentials %s\n", gnutls_strerror(ret)); - return NULL; - } - if ((ret = gnutls_certificate_set_x509_trust_file(x509_cred, - vd->tls.x509cacert, - GNUTLS_X509_FMT_PEM)) < 0) { - VNC_DEBUG("Cannot load CA certificate %s\n", gnutls_strerror(ret)); - gnutls_certificate_free_credentials(x509_cred); - return NULL; - } - - if ((ret = gnutls_certificate_set_x509_key_file (x509_cred, - vd->tls.x509cert, - vd->tls.x509key, - GNUTLS_X509_FMT_PEM)) < 0) { - VNC_DEBUG("Cannot load certificate & key %s\n", gnutls_strerror(ret)); - gnutls_certificate_free_credentials(x509_cred); - return NULL; - } - - if (vd->tls.x509cacrl) { - if ((ret = gnutls_certificate_set_x509_crl_file(x509_cred, - vd->tls.x509cacrl, - GNUTLS_X509_FMT_PEM)) < 0) { - VNC_DEBUG("Cannot load CRL %s\n", gnutls_strerror(ret)); - gnutls_certificate_free_credentials(x509_cred); - return NULL; - } - } - - gnutls_certificate_set_dh_params (x509_cred, dh_params); - - return x509_cred; -} - - -int vnc_tls_validate_certificate(struct VncState *vs) -{ - int ret; - unsigned int status; - const gnutls_datum_t *certs; - unsigned int nCerts, i; - time_t now; - - VNC_DEBUG("Validating client certificate\n"); - if ((ret = gnutls_certificate_verify_peers2 (vs->tls.session, &status)) < 0) { - VNC_DEBUG("Verify failed %s\n", gnutls_strerror(ret)); - return -1; - } - - if ((now = time(NULL)) == ((time_t)-1)) { - return -1; - } - - if (status != 0) { - if (status & GNUTLS_CERT_INVALID) - VNC_DEBUG("The certificate is not trusted.\n"); - - if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) - VNC_DEBUG("The certificate hasn't got a known issuer.\n"); - - if (status & GNUTLS_CERT_REVOKED) - VNC_DEBUG("The certificate has been revoked.\n"); - - if (status & GNUTLS_CERT_INSECURE_ALGORITHM) - VNC_DEBUG("The certificate uses an insecure algorithm\n"); - - return -1; - } else { - VNC_DEBUG("Certificate is valid!\n"); - } - - /* Only support x509 for now */ - if (gnutls_certificate_type_get(vs->tls.session) != GNUTLS_CRT_X509) - return -1; - - if (!(certs = gnutls_certificate_get_peers(vs->tls.session, &nCerts))) - return -1; - - for (i = 0 ; i < nCerts ; i++) { - gnutls_x509_crt_t cert; - VNC_DEBUG ("Checking certificate chain %d\n", i); - if (gnutls_x509_crt_init (&cert) < 0) - return -1; - - if (gnutls_x509_crt_import(cert, &certs[i], GNUTLS_X509_FMT_DER) < 0) { - gnutls_x509_crt_deinit (cert); - return -1; - } - - if (gnutls_x509_crt_get_expiration_time (cert) < now) { - VNC_DEBUG("The certificate has expired\n"); - gnutls_x509_crt_deinit (cert); - return -1; - } - - if (gnutls_x509_crt_get_activation_time (cert) > now) { - VNC_DEBUG("The certificate is not yet activated\n"); - gnutls_x509_crt_deinit (cert); - return -1; - } - - if (gnutls_x509_crt_get_activation_time (cert) > now) { - VNC_DEBUG("The certificate is not yet activated\n"); - gnutls_x509_crt_deinit (cert); - return -1; - } - - if (i == 0) { - size_t dnameSize = 1024; - vs->tls.dname = qemu_malloc(dnameSize); - requery: - if ((ret = gnutls_x509_crt_get_dn (cert, vs->tls.dname, &dnameSize)) != 0) { - if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) { - vs->tls.dname = qemu_realloc(vs->tls.dname, dnameSize); - goto requery; - } - gnutls_x509_crt_deinit (cert); - VNC_DEBUG("Cannot get client distinguished name: %s", - gnutls_strerror (ret)); - return -1; - } - - if (vs->vd->tls.x509verify) { - int allow; - if (!vs->vd->tls.acl) { - VNC_DEBUG("no ACL activated, allowing access"); - gnutls_x509_crt_deinit (cert); - continue; - } - - allow = qemu_acl_party_is_allowed(vs->vd->tls.acl, - vs->tls.dname); - - VNC_DEBUG("TLS x509 ACL check for %s is %s\n", - vs->tls.dname, allow ? "allowed" : "denied"); - if (!allow) { - gnutls_x509_crt_deinit (cert); - return -1; - } - } - } - - gnutls_x509_crt_deinit (cert); - } - - return 0; -} - - -int vnc_tls_client_setup(struct VncState *vs, - int needX509Creds) { - static const int cert_type_priority[] = { GNUTLS_CRT_X509, 0 }; - static const int protocol_priority[]= { GNUTLS_TLS1_1, GNUTLS_TLS1_0, GNUTLS_SSL3, 0 }; - static const int kx_anon[] = {GNUTLS_KX_ANON_DH, 0}; - static const int kx_x509[] = {GNUTLS_KX_DHE_DSS, GNUTLS_KX_RSA, GNUTLS_KX_DHE_RSA, GNUTLS_KX_SRP, 0}; - - VNC_DEBUG("Do TLS setup\n"); - if (vnc_tls_initialize() < 0) { - VNC_DEBUG("Failed to init TLS\n"); - vnc_client_error(vs); - return -1; - } - if (vs->tls.session == NULL) { - if (gnutls_init(&vs->tls.session, GNUTLS_SERVER) < 0) { - vnc_client_error(vs); - return -1; - } - - if (gnutls_set_default_priority(vs->tls.session) < 0) { - gnutls_deinit(vs->tls.session); - vs->tls.session = NULL; - vnc_client_error(vs); - return -1; - } - - if (gnutls_kx_set_priority(vs->tls.session, needX509Creds ? kx_x509 : kx_anon) < 0) { - gnutls_deinit(vs->tls.session); - vs->tls.session = NULL; - vnc_client_error(vs); - return -1; - } - - if (gnutls_certificate_type_set_priority(vs->tls.session, cert_type_priority) < 0) { - gnutls_deinit(vs->tls.session); - vs->tls.session = NULL; - vnc_client_error(vs); - return -1; - } - - if (gnutls_protocol_set_priority(vs->tls.session, protocol_priority) < 0) { - gnutls_deinit(vs->tls.session); - vs->tls.session = NULL; - vnc_client_error(vs); - return -1; - } - - if (needX509Creds) { - gnutls_certificate_server_credentials x509_cred = vnc_tls_initialize_x509_cred(vs->vd); - if (!x509_cred) { - gnutls_deinit(vs->tls.session); - vs->tls.session = NULL; - vnc_client_error(vs); - return -1; - } - if (gnutls_credentials_set(vs->tls.session, GNUTLS_CRD_CERTIFICATE, x509_cred) < 0) { - gnutls_deinit(vs->tls.session); - vs->tls.session = NULL; - gnutls_certificate_free_credentials(x509_cred); - vnc_client_error(vs); - return -1; - } - if (vs->vd->tls.x509verify) { - VNC_DEBUG("Requesting a client certificate\n"); - gnutls_certificate_server_set_request (vs->tls.session, GNUTLS_CERT_REQUEST); - } - - } else { - gnutls_anon_server_credentials anon_cred = vnc_tls_initialize_anon_cred(); - if (!anon_cred) { - gnutls_deinit(vs->tls.session); - vs->tls.session = NULL; - vnc_client_error(vs); - return -1; - } - if (gnutls_credentials_set(vs->tls.session, GNUTLS_CRD_ANON, anon_cred) < 0) { - gnutls_deinit(vs->tls.session); - vs->tls.session = NULL; - gnutls_anon_free_server_credentials(anon_cred); - vnc_client_error(vs); - return -1; - } - } - - gnutls_transport_set_ptr(vs->tls.session, (gnutls_transport_ptr_t)vs); - gnutls_transport_set_push_function(vs->tls.session, vnc_tls_push); - gnutls_transport_set_pull_function(vs->tls.session, vnc_tls_pull); - } - return 0; -} - - -void vnc_tls_client_cleanup(struct VncState *vs) -{ - if (vs->tls.session) { - gnutls_deinit(vs->tls.session); - vs->tls.session = NULL; - } - vs->tls.wiremode = VNC_WIREMODE_CLEAR; - free(vs->tls.dname); -} - - - -static int vnc_set_x509_credential(VncDisplay *vd, - const char *certdir, - const char *filename, - char **cred, - int ignoreMissing) -{ - struct stat sb; - - if (*cred) { - qemu_free(*cred); - *cred = NULL; - } - - *cred = qemu_malloc(strlen(certdir) + strlen(filename) + 2); - - strcpy(*cred, certdir); - strcat(*cred, "/"); - strcat(*cred, filename); - - VNC_DEBUG("Check %s\n", *cred); - if (stat(*cred, &sb) < 0) { - qemu_free(*cred); - *cred = NULL; - if (ignoreMissing && errno == ENOENT) - return 0; - return -1; - } - - return 0; -} - - -#define X509_CA_CERT_FILE "ca-cert.pem" -#define X509_CA_CRL_FILE "ca-crl.pem" -#define X509_SERVER_KEY_FILE "server-key.pem" -#define X509_SERVER_CERT_FILE "server-cert.pem" - - -int vnc_tls_set_x509_creds_dir(VncDisplay *vd, - const char *certdir) -{ - if (vnc_set_x509_credential(vd, certdir, X509_CA_CERT_FILE, &vd->tls.x509cacert, 0) < 0) - goto cleanup; - if (vnc_set_x509_credential(vd, certdir, X509_CA_CRL_FILE, &vd->tls.x509cacrl, 1) < 0) - goto cleanup; - if (vnc_set_x509_credential(vd, certdir, X509_SERVER_CERT_FILE, &vd->tls.x509cert, 0) < 0) - goto cleanup; - if (vnc_set_x509_credential(vd, certdir, X509_SERVER_KEY_FILE, &vd->tls.x509key, 0) < 0) - goto cleanup; - - return 0; - - cleanup: - qemu_free(vd->tls.x509cacert); - qemu_free(vd->tls.x509cacrl); - qemu_free(vd->tls.x509cert); - qemu_free(vd->tls.x509key); - vd->tls.x509cacert = vd->tls.x509cacrl = vd->tls.x509cert = vd->tls.x509key = NULL; - return -1; -} - diff -Nru qemu-kvm-0.12.5+noroms/vnc-tls.h qemu-kvm-0.14.1/vnc-tls.h --- qemu-kvm-0.12.5+noroms/vnc-tls.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/vnc-tls.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,76 +0,0 @@ -/* - * QEMU VNC display driver. TLS helpers - * - * Copyright (C) 2006 Anthony Liguori - * Copyright (C) 2006 Fabrice Bellard - * Copyright (C) 2009 Red Hat, Inc - * - * 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. - */ - - -#ifndef __QEMU_VNC_TLS_H__ -#define __QEMU_VNC_TLS_H__ - -#include -#include - -#include "acl.h" - -enum { - VNC_WIREMODE_CLEAR, - VNC_WIREMODE_TLS, -}; - -typedef struct VncDisplayTLS VncDisplayTLS; -typedef struct VncStateTLS VncStateTLS; - -/* Server state */ -struct VncDisplayTLS { - int x509verify; /* Non-zero if server requests & validates client cert */ - qemu_acl *acl; - - /* Paths to x509 certs/keys */ - char *x509cacert; - char *x509cacrl; - char *x509cert; - char *x509key; -}; - -/* Per client state */ -struct VncStateTLS { - /* Whether data is being TLS encrypted yet */ - int wiremode; - gnutls_session_t session; - - /* Client's Distinguished Name from the x509 cert */ - char *dname; -}; - -int vnc_tls_client_setup(VncState *vs, int x509Creds); -void vnc_tls_client_cleanup(VncState *vs); - -int vnc_tls_validate_certificate(VncState *vs); - -int vnc_tls_set_x509_creds_dir(VncDisplay *vd, - const char *path); - - -#endif /* __QEMU_VNC_TLS_H__ */ - diff -Nru qemu-kvm-0.12.5+noroms/x_keymap.c qemu-kvm-0.14.1/x_keymap.c --- qemu-kvm-0.12.5+noroms/x_keymap.c 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/x_keymap.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,168 +0,0 @@ -/* - * QEMU SDL display driver - * - * Copyright (c) 2003 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 "qemu-common.h" -#include "x_keymap.h" - -static const uint8_t x_keycode_to_pc_keycode[115] = { - 0xc7, /* 97 Home */ - 0xc8, /* 98 Up */ - 0xc9, /* 99 PgUp */ - 0xcb, /* 100 Left */ - 0x4c, /* 101 KP-5 */ - 0xcd, /* 102 Right */ - 0xcf, /* 103 End */ - 0xd0, /* 104 Down */ - 0xd1, /* 105 PgDn */ - 0xd2, /* 106 Ins */ - 0xd3, /* 107 Del */ - 0x9c, /* 108 Enter */ - 0x9d, /* 109 Ctrl-R */ - 0x0, /* 110 Pause */ - 0xb7, /* 111 Print */ - 0xb5, /* 112 Divide */ - 0xb8, /* 113 Alt-R */ - 0xc6, /* 114 Break */ - 0x0, /* 115 */ - 0x0, /* 116 */ - 0x0, /* 117 */ - 0x0, /* 118 */ - 0x0, /* 119 */ - 0x0, /* 120 */ - 0x0, /* 121 */ - 0x0, /* 122 */ - 0x0, /* 123 */ - 0x0, /* 124 */ - 0x0, /* 125 */ - 0x0, /* 126 */ - 0x0, /* 127 */ - 0x0, /* 128 */ - 0x79, /* 129 Henkan */ - 0x0, /* 130 */ - 0x7b, /* 131 Muhenkan */ - 0x0, /* 132 */ - 0x7d, /* 133 Yen */ - 0x0, /* 134 */ - 0x0, /* 135 */ - 0x47, /* 136 KP_7 */ - 0x48, /* 137 KP_8 */ - 0x49, /* 138 KP_9 */ - 0x4b, /* 139 KP_4 */ - 0x4c, /* 140 KP_5 */ - 0x4d, /* 141 KP_6 */ - 0x4f, /* 142 KP_1 */ - 0x50, /* 143 KP_2 */ - 0x51, /* 144 KP_3 */ - 0x52, /* 145 KP_0 */ - 0x53, /* 146 KP_. */ - 0x47, /* 147 KP_HOME */ - 0x48, /* 148 KP_UP */ - 0x49, /* 149 KP_PgUp */ - 0x4b, /* 150 KP_Left */ - 0x4c, /* 151 KP_ */ - 0x4d, /* 152 KP_Right */ - 0x4f, /* 153 KP_End */ - 0x50, /* 154 KP_Down */ - 0x51, /* 155 KP_PgDn */ - 0x52, /* 156 KP_Ins */ - 0x53, /* 157 KP_Del */ -}; - -/* This table is generated based off the xfree86 -> scancode mapping above - * and the keycode mappings in /usr/share/X11/xkb/keycodes/evdev - * and /usr/share/X11/xkb/keycodes/xfree86 - */ - -static const uint8_t evdev_keycode_to_pc_keycode[61] = { - 0, /* 97 EVDEV - RO ("Internet" Keyboards) */ - 0, /* 98 EVDEV - KATA (Katakana) */ - 0, /* 99 EVDEV - HIRA (Hiragana) */ - 0x79, /* 100 EVDEV - HENK (Henkan) */ - 0x70, /* 101 EVDEV - HKTG (Hiragana/Katakana toggle) */ - 0x7b, /* 102 EVDEV - MUHE (Muhenkan) */ - 0, /* 103 EVDEV - JPCM (KPJPComma) */ - 0x9c, /* 104 KPEN */ - 0x9d, /* 105 RCTL */ - 0xb5, /* 106 KPDV */ - 0xb7, /* 107 PRSC */ - 0xb8, /* 108 RALT */ - 0, /* 109 EVDEV - LNFD ("Internet" Keyboards) */ - 0xc7, /* 110 HOME */ - 0xc8, /* 111 UP */ - 0xc9, /* 112 PGUP */ - 0xcb, /* 113 LEFT */ - 0xcd, /* 114 RGHT */ - 0xcf, /* 115 END */ - 0xd0, /* 116 DOWN */ - 0xd1, /* 117 PGDN */ - 0xd2, /* 118 INS */ - 0xd3, /* 119 DELE */ - 0, /* 120 EVDEV - I120 ("Internet" Keyboards) */ - 0, /* 121 EVDEV - MUTE */ - 0, /* 122 EVDEV - VOL- */ - 0, /* 123 EVDEV - VOL+ */ - 0, /* 124 EVDEV - POWR */ - 0, /* 125 EVDEV - KPEQ */ - 0, /* 126 EVDEV - I126 ("Internet" Keyboards) */ - 0, /* 127 EVDEV - PAUS */ - 0, /* 128 EVDEV - ???? */ - 0, /* 129 EVDEV - I129 ("Internet" Keyboards) */ - 0xf1, /* 130 EVDEV - HNGL (Korean Hangul Latin toggle) */ - 0xf2, /* 131 EVDEV - HJCV (Korean Hangul Hanja toggle) */ - 0x7d, /* 132 AE13 (Yen)*/ - 0xdb, /* 133 EVDEV - LWIN */ - 0xdc, /* 134 EVDEV - RWIN */ - 0xdd, /* 135 EVDEV - MENU */ - 0, /* 136 EVDEV - STOP */ - 0, /* 137 EVDEV - AGAI */ - 0, /* 138 EVDEV - PROP */ - 0, /* 139 EVDEV - UNDO */ - 0, /* 140 EVDEV - FRNT */ - 0, /* 141 EVDEV - COPY */ - 0, /* 142 EVDEV - OPEN */ - 0, /* 143 EVDEV - PAST */ - 0, /* 144 EVDEV - FIND */ - 0, /* 145 EVDEV - CUT */ - 0, /* 146 EVDEV - HELP */ - 0, /* 147 EVDEV - I147 */ - 0, /* 148 EVDEV - I148 */ - 0, /* 149 EVDEV - I149 */ - 0, /* 150 EVDEV - I150 */ - 0, /* 151 EVDEV - I151 */ - 0, /* 152 EVDEV - I152 */ - 0, /* 153 EVDEV - I153 */ - 0, /* 154 EVDEV - I154 */ - 0, /* 155 EVDEV - I156 */ - 0, /* 156 EVDEV - I157 */ - 0, /* 157 EVDEV - I158 */ -}; - -uint8_t translate_xfree86_keycode(const int key) -{ - return x_keycode_to_pc_keycode[key]; -} - -uint8_t translate_evdev_keycode(const int key) -{ - return evdev_keycode_to_pc_keycode[key]; -} diff -Nru qemu-kvm-0.12.5+noroms/x_keymap.h qemu-kvm-0.14.1/x_keymap.h --- qemu-kvm-0.12.5+noroms/x_keymap.h 2010-07-27 00:43:53.000000000 +0000 +++ qemu-kvm-0.14.1/x_keymap.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,32 +0,0 @@ -/* - * QEMU SDL display driver - * - * Copyright (c) 2003 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. - */ - -#ifndef QEMU_X_KEYMAP_H -#define QEMU_X_KEYMAP_H - -extern uint8_t translate_xfree86_keycode(const int key); - -extern uint8_t translate_evdev_keycode(const int key); - -#endif